eth: fbnic: support n-tuple filters

Add ethtool -n / -N support. Support only "un-ordered" rule sets
(RX_CLS_LOC_ANY), just for simplicity of the code. It's unclear
anyone actually cares about the rule ordering.

Signed-off-by: Alexander Duyck <alexanderduyck@fb.com>
Link: https://patch.msgid.link/20250206235334.1425329-6-kuba@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Alexander Duyck 2025-02-06 15:53:32 -08:00 committed by Jakub Kicinski
parent 3a265bd6a3
commit 2230035439
5 changed files with 660 additions and 1 deletions

View File

@ -605,8 +605,11 @@ enum {
FBNIC_RPC_ACT_TBL0_DEST_EI = 4,
};
#define FBNIC_RPC_ACT_TBL0_Q_SEL CSR_BIT(4)
#define FBNIC_RPC_ACT_TBL0_Q_ID CSR_GENMASK(15, 8)
#define FBNIC_RPC_ACT_TBL0_DMA_HINT CSR_GENMASK(24, 16)
#define FBNIC_RPC_ACT_TBL0_TS_ENA CSR_BIT(28)
#define FBNIC_RPC_ACT_TBL0_ACT_TBL_IDX CSR_BIT(29)
#define FBNIC_RPC_ACT_TBL0_RSS_CTXT_ID CSR_BIT(30)
#define FBNIC_RPC_ACT_TBL1_DEFAULT 0x0840b /* 0x2102c */

View File

@ -4,6 +4,7 @@
#include <linux/ethtool.h>
#include <linux/netdevice.h>
#include <linux/pci.h>
#include <net/ipv6.h>
#include "fbnic.h"
#include "fbnic_netdev.h"
@ -218,11 +219,234 @@ fbnic_get_rss_hash_opts(struct fbnic_net *fbn, struct ethtool_rxnfc *cmd)
return 0;
}
static int fbnic_get_cls_rule_all(struct fbnic_net *fbn,
struct ethtool_rxnfc *cmd,
u32 *rule_locs)
{
struct fbnic_dev *fbd = fbn->fbd;
int i, cnt = 0;
/* Report maximum rule count */
cmd->data = FBNIC_RPC_ACT_TBL_NFC_ENTRIES;
for (i = 0; i < FBNIC_RPC_ACT_TBL_NFC_ENTRIES; i++) {
int idx = i + FBNIC_RPC_ACT_TBL_NFC_OFFSET;
struct fbnic_act_tcam *act_tcam;
act_tcam = &fbd->act_tcam[idx];
if (act_tcam->state != FBNIC_TCAM_S_VALID)
continue;
if (rule_locs) {
if (cnt == cmd->rule_cnt)
return -EMSGSIZE;
rule_locs[cnt] = i;
}
cnt++;
}
return cnt;
}
static int fbnic_get_cls_rule(struct fbnic_net *fbn, struct ethtool_rxnfc *cmd)
{
struct ethtool_rx_flow_spec *fsp;
struct fbnic_dev *fbd = fbn->fbd;
struct fbnic_act_tcam *act_tcam;
int idx;
fsp = (struct ethtool_rx_flow_spec *)&cmd->fs;
if (fsp->location >= FBNIC_RPC_ACT_TBL_NFC_ENTRIES)
return -EINVAL;
idx = fsp->location + FBNIC_RPC_ACT_TBL_NFC_OFFSET;
act_tcam = &fbd->act_tcam[idx];
if (act_tcam->state != FBNIC_TCAM_S_VALID)
return -EINVAL;
/* Report maximum rule count */
cmd->data = FBNIC_RPC_ACT_TBL_NFC_ENTRIES;
/* Set flow type field */
if (!(act_tcam->value.tcam[1] & FBNIC_RPC_TCAM_ACT1_IP_VALID)) {
fsp->flow_type = ETHER_FLOW;
if (!FIELD_GET(FBNIC_RPC_TCAM_ACT1_L2_MACDA_IDX,
act_tcam->mask.tcam[1])) {
struct fbnic_mac_addr *mac_addr;
idx = FIELD_GET(FBNIC_RPC_TCAM_ACT1_L2_MACDA_IDX,
act_tcam->value.tcam[1]);
mac_addr = &fbd->mac_addr[idx];
ether_addr_copy(fsp->h_u.ether_spec.h_dest,
mac_addr->value.addr8);
eth_broadcast_addr(fsp->m_u.ether_spec.h_dest);
}
} else if (act_tcam->value.tcam[1] &
FBNIC_RPC_TCAM_ACT1_OUTER_IP_VALID) {
fsp->flow_type = IPV6_USER_FLOW;
fsp->h_u.usr_ip6_spec.l4_proto = IPPROTO_IPV6;
fsp->m_u.usr_ip6_spec.l4_proto = 0xff;
if (!FIELD_GET(FBNIC_RPC_TCAM_ACT0_OUTER_IPSRC_IDX,
act_tcam->mask.tcam[0])) {
struct fbnic_ip_addr *ip_addr;
int i;
idx = FIELD_GET(FBNIC_RPC_TCAM_ACT0_OUTER_IPSRC_IDX,
act_tcam->value.tcam[0]);
ip_addr = &fbd->ipo_src[idx];
for (i = 0; i < 4; i++) {
fsp->h_u.usr_ip6_spec.ip6src[i] =
ip_addr->value.s6_addr32[i];
fsp->m_u.usr_ip6_spec.ip6src[i] =
~ip_addr->mask.s6_addr32[i];
}
}
if (!FIELD_GET(FBNIC_RPC_TCAM_ACT0_OUTER_IPDST_IDX,
act_tcam->mask.tcam[0])) {
struct fbnic_ip_addr *ip_addr;
int i;
idx = FIELD_GET(FBNIC_RPC_TCAM_ACT0_OUTER_IPDST_IDX,
act_tcam->value.tcam[0]);
ip_addr = &fbd->ipo_dst[idx];
for (i = 0; i < 4; i++) {
fsp->h_u.usr_ip6_spec.ip6dst[i] =
ip_addr->value.s6_addr32[i];
fsp->m_u.usr_ip6_spec.ip6dst[i] =
~ip_addr->mask.s6_addr32[i];
}
}
} else if ((act_tcam->value.tcam[1] & FBNIC_RPC_TCAM_ACT1_IP_IS_V6)) {
if (act_tcam->value.tcam[1] & FBNIC_RPC_TCAM_ACT1_L4_VALID) {
if (act_tcam->value.tcam[1] &
FBNIC_RPC_TCAM_ACT1_L4_IS_UDP)
fsp->flow_type = UDP_V6_FLOW;
else
fsp->flow_type = TCP_V6_FLOW;
fsp->h_u.tcp_ip6_spec.psrc =
cpu_to_be16(act_tcam->value.tcam[3]);
fsp->m_u.tcp_ip6_spec.psrc =
cpu_to_be16(~act_tcam->mask.tcam[3]);
fsp->h_u.tcp_ip6_spec.pdst =
cpu_to_be16(act_tcam->value.tcam[4]);
fsp->m_u.tcp_ip6_spec.pdst =
cpu_to_be16(~act_tcam->mask.tcam[4]);
} else {
fsp->flow_type = IPV6_USER_FLOW;
}
if (!FIELD_GET(FBNIC_RPC_TCAM_ACT0_IPSRC_IDX,
act_tcam->mask.tcam[0])) {
struct fbnic_ip_addr *ip_addr;
int i;
idx = FIELD_GET(FBNIC_RPC_TCAM_ACT0_IPSRC_IDX,
act_tcam->value.tcam[0]);
ip_addr = &fbd->ip_src[idx];
for (i = 0; i < 4; i++) {
fsp->h_u.usr_ip6_spec.ip6src[i] =
ip_addr->value.s6_addr32[i];
fsp->m_u.usr_ip6_spec.ip6src[i] =
~ip_addr->mask.s6_addr32[i];
}
}
if (!FIELD_GET(FBNIC_RPC_TCAM_ACT0_IPDST_IDX,
act_tcam->mask.tcam[0])) {
struct fbnic_ip_addr *ip_addr;
int i;
idx = FIELD_GET(FBNIC_RPC_TCAM_ACT0_IPDST_IDX,
act_tcam->value.tcam[0]);
ip_addr = &fbd->ip_dst[idx];
for (i = 0; i < 4; i++) {
fsp->h_u.usr_ip6_spec.ip6dst[i] =
ip_addr->value.s6_addr32[i];
fsp->m_u.usr_ip6_spec.ip6dst[i] =
~ip_addr->mask.s6_addr32[i];
}
}
} else {
if (act_tcam->value.tcam[1] & FBNIC_RPC_TCAM_ACT1_L4_VALID) {
if (act_tcam->value.tcam[1] &
FBNIC_RPC_TCAM_ACT1_L4_IS_UDP)
fsp->flow_type = UDP_V4_FLOW;
else
fsp->flow_type = TCP_V4_FLOW;
fsp->h_u.tcp_ip4_spec.psrc =
cpu_to_be16(act_tcam->value.tcam[3]);
fsp->m_u.tcp_ip4_spec.psrc =
cpu_to_be16(~act_tcam->mask.tcam[3]);
fsp->h_u.tcp_ip4_spec.pdst =
cpu_to_be16(act_tcam->value.tcam[4]);
fsp->m_u.tcp_ip4_spec.pdst =
cpu_to_be16(~act_tcam->mask.tcam[4]);
} else {
fsp->flow_type = IPV4_USER_FLOW;
fsp->h_u.usr_ip4_spec.ip_ver = ETH_RX_NFC_IP4;
}
if (!FIELD_GET(FBNIC_RPC_TCAM_ACT0_IPSRC_IDX,
act_tcam->mask.tcam[0])) {
struct fbnic_ip_addr *ip_addr;
idx = FIELD_GET(FBNIC_RPC_TCAM_ACT0_IPSRC_IDX,
act_tcam->value.tcam[0]);
ip_addr = &fbd->ip_src[idx];
fsp->h_u.usr_ip4_spec.ip4src =
ip_addr->value.s6_addr32[3];
fsp->m_u.usr_ip4_spec.ip4src =
~ip_addr->mask.s6_addr32[3];
}
if (!FIELD_GET(FBNIC_RPC_TCAM_ACT0_IPDST_IDX,
act_tcam->mask.tcam[0])) {
struct fbnic_ip_addr *ip_addr;
idx = FIELD_GET(FBNIC_RPC_TCAM_ACT0_IPDST_IDX,
act_tcam->value.tcam[0]);
ip_addr = &fbd->ip_dst[idx];
fsp->h_u.usr_ip4_spec.ip4dst =
ip_addr->value.s6_addr32[3];
fsp->m_u.usr_ip4_spec.ip4dst =
~ip_addr->mask.s6_addr32[3];
}
}
/* Record action */
if (act_tcam->dest & FBNIC_RPC_ACT_TBL0_DROP)
fsp->ring_cookie = RX_CLS_FLOW_DISC;
else if (act_tcam->dest & FBNIC_RPC_ACT_TBL0_Q_SEL)
fsp->ring_cookie = FIELD_GET(FBNIC_RPC_ACT_TBL0_Q_ID,
act_tcam->dest);
else
fsp->flow_type |= FLOW_RSS;
cmd->rss_context = FIELD_GET(FBNIC_RPC_ACT_TBL0_RSS_CTXT_ID,
act_tcam->dest);
return 0;
}
static int fbnic_get_rxnfc(struct net_device *netdev,
struct ethtool_rxnfc *cmd, u32 *rule_locs)
{
struct fbnic_net *fbn = netdev_priv(netdev);
int ret = -EOPNOTSUPP;
u32 special = 0;
switch (cmd->cmd) {
case ETHTOOL_GRXRINGS:
@ -232,6 +456,22 @@ static int fbnic_get_rxnfc(struct net_device *netdev,
case ETHTOOL_GRXFH:
ret = fbnic_get_rss_hash_opts(fbn, cmd);
break;
case ETHTOOL_GRXCLSRULE:
ret = fbnic_get_cls_rule(fbn, cmd);
break;
case ETHTOOL_GRXCLSRLCNT:
rule_locs = NULL;
special = RX_CLS_LOC_SPECIAL;
fallthrough;
case ETHTOOL_GRXCLSRLALL:
ret = fbnic_get_cls_rule_all(fbn, cmd, rule_locs);
if (ret < 0)
break;
cmd->data |= special;
cmd->rule_cnt = ret;
ret = 0;
break;
}
return ret;
@ -272,6 +512,406 @@ fbnic_set_rss_hash_opts(struct fbnic_net *fbn, const struct ethtool_rxnfc *cmd)
return 0;
}
static int fbnic_cls_rule_any_loc(struct fbnic_dev *fbd)
{
int i;
for (i = FBNIC_RPC_ACT_TBL_NFC_ENTRIES; i--;) {
int idx = i + FBNIC_RPC_ACT_TBL_NFC_OFFSET;
if (fbd->act_tcam[idx].state != FBNIC_TCAM_S_VALID)
return i;
}
return -ENOSPC;
}
static int fbnic_set_cls_rule_ins(struct fbnic_net *fbn,
const struct ethtool_rxnfc *cmd)
{
u16 flow_value = 0, flow_mask = 0xffff, ip_value = 0, ip_mask = 0xffff;
u16 sport = 0, sport_mask = ~0, dport = 0, dport_mask = ~0;
u16 misc = 0, misc_mask = ~0;
u32 dest = FIELD_PREP(FBNIC_RPC_ACT_TBL0_DEST_MASK,
FBNIC_RPC_ACT_TBL0_DEST_HOST);
struct fbnic_ip_addr *ip_src = NULL, *ip_dst = NULL;
struct fbnic_mac_addr *mac_addr = NULL;
struct ethtool_rx_flow_spec *fsp;
struct fbnic_dev *fbd = fbn->fbd;
struct fbnic_act_tcam *act_tcam;
struct in6_addr *addr6, *mask6;
struct in_addr *addr4, *mask4;
int hash_idx, location;
u32 flow_type;
int idx, j;
fsp = (struct ethtool_rx_flow_spec *)&cmd->fs;
if (fsp->location != RX_CLS_LOC_ANY)
return -EINVAL;
location = fbnic_cls_rule_any_loc(fbd);
if (location < 0)
return location;
if (fsp->ring_cookie == RX_CLS_FLOW_DISC) {
dest = FBNIC_RPC_ACT_TBL0_DROP;
} else if (fsp->flow_type & FLOW_RSS) {
if (cmd->rss_context == 1)
dest |= FBNIC_RPC_ACT_TBL0_RSS_CTXT_ID;
} else {
u32 ring_idx = ethtool_get_flow_spec_ring(fsp->ring_cookie);
if (ring_idx >= fbn->num_rx_queues)
return -EINVAL;
dest |= FBNIC_RPC_ACT_TBL0_Q_SEL |
FIELD_PREP(FBNIC_RPC_ACT_TBL0_Q_ID, ring_idx);
}
idx = location + FBNIC_RPC_ACT_TBL_NFC_OFFSET;
act_tcam = &fbd->act_tcam[idx];
/* Do not allow overwriting for now.
* To support overwriting rules we will need to add logic to free
* any IP or MACDA TCAMs that may be associated with the old rule.
*/
if (act_tcam->state != FBNIC_TCAM_S_DISABLED)
return -EBUSY;
flow_type = fsp->flow_type & ~(FLOW_EXT | FLOW_RSS);
hash_idx = fbnic_get_rss_hash_idx(flow_type);
switch (flow_type) {
case UDP_V4_FLOW:
udp4_flow:
flow_value |= FBNIC_RPC_TCAM_ACT1_L4_IS_UDP;
fallthrough;
case TCP_V4_FLOW:
tcp4_flow:
flow_value |= FBNIC_RPC_TCAM_ACT1_L4_VALID;
flow_mask &= ~(FBNIC_RPC_TCAM_ACT1_L4_IS_UDP |
FBNIC_RPC_TCAM_ACT1_L4_VALID);
sport = be16_to_cpu(fsp->h_u.tcp_ip4_spec.psrc);
sport_mask = ~be16_to_cpu(fsp->m_u.tcp_ip4_spec.psrc);
dport = be16_to_cpu(fsp->h_u.tcp_ip4_spec.pdst);
dport_mask = ~be16_to_cpu(fsp->m_u.tcp_ip4_spec.pdst);
goto ip4_flow;
case IP_USER_FLOW:
if (!fsp->m_u.usr_ip4_spec.proto)
goto ip4_flow;
if (fsp->m_u.usr_ip4_spec.proto != 0xff)
return -EINVAL;
if (fsp->h_u.usr_ip4_spec.proto == IPPROTO_UDP)
goto udp4_flow;
if (fsp->h_u.usr_ip4_spec.proto == IPPROTO_TCP)
goto tcp4_flow;
return -EINVAL;
ip4_flow:
addr4 = (struct in_addr *)&fsp->h_u.usr_ip4_spec.ip4src;
mask4 = (struct in_addr *)&fsp->m_u.usr_ip4_spec.ip4src;
if (mask4->s_addr) {
ip_src = __fbnic_ip4_sync(fbd, fbd->ip_src,
addr4, mask4);
if (!ip_src)
return -ENOSPC;
set_bit(idx, ip_src->act_tcam);
ip_value |= FBNIC_RPC_TCAM_ACT0_IPSRC_VALID |
FIELD_PREP(FBNIC_RPC_TCAM_ACT0_IPSRC_IDX,
ip_src - fbd->ip_src);
ip_mask &= ~(FBNIC_RPC_TCAM_ACT0_IPSRC_VALID |
FBNIC_RPC_TCAM_ACT0_IPSRC_IDX);
}
addr4 = (struct in_addr *)&fsp->h_u.usr_ip4_spec.ip4dst;
mask4 = (struct in_addr *)&fsp->m_u.usr_ip4_spec.ip4dst;
if (mask4->s_addr) {
ip_dst = __fbnic_ip4_sync(fbd, fbd->ip_dst,
addr4, mask4);
if (!ip_dst) {
if (ip_src && ip_src->state == FBNIC_TCAM_S_ADD)
memset(ip_src, 0, sizeof(*ip_src));
return -ENOSPC;
}
set_bit(idx, ip_dst->act_tcam);
ip_value |= FBNIC_RPC_TCAM_ACT0_IPDST_VALID |
FIELD_PREP(FBNIC_RPC_TCAM_ACT0_IPDST_IDX,
ip_dst - fbd->ip_dst);
ip_mask &= ~(FBNIC_RPC_TCAM_ACT0_IPDST_VALID |
FBNIC_RPC_TCAM_ACT0_IPDST_IDX);
}
flow_value |= FBNIC_RPC_TCAM_ACT1_IP_VALID |
FBNIC_RPC_TCAM_ACT1_L2_MACDA_VALID;
flow_mask &= ~(FBNIC_RPC_TCAM_ACT1_IP_IS_V6 |
FBNIC_RPC_TCAM_ACT1_IP_VALID |
FBNIC_RPC_TCAM_ACT1_L2_MACDA_VALID);
break;
case UDP_V6_FLOW:
udp6_flow:
flow_value |= FBNIC_RPC_TCAM_ACT1_L4_IS_UDP;
fallthrough;
case TCP_V6_FLOW:
tcp6_flow:
flow_value |= FBNIC_RPC_TCAM_ACT1_L4_VALID;
flow_mask &= ~(FBNIC_RPC_TCAM_ACT1_L4_IS_UDP |
FBNIC_RPC_TCAM_ACT1_L4_VALID);
sport = be16_to_cpu(fsp->h_u.tcp_ip6_spec.psrc);
sport_mask = ~be16_to_cpu(fsp->m_u.tcp_ip6_spec.psrc);
dport = be16_to_cpu(fsp->h_u.tcp_ip6_spec.pdst);
dport_mask = ~be16_to_cpu(fsp->m_u.tcp_ip6_spec.pdst);
goto ipv6_flow;
case IPV6_USER_FLOW:
if (!fsp->m_u.usr_ip6_spec.l4_proto)
goto ipv6_flow;
if (fsp->m_u.usr_ip6_spec.l4_proto != 0xff)
return -EINVAL;
if (fsp->h_u.usr_ip6_spec.l4_proto == IPPROTO_UDP)
goto udp6_flow;
if (fsp->h_u.usr_ip6_spec.l4_proto == IPPROTO_TCP)
goto tcp6_flow;
if (fsp->h_u.usr_ip6_spec.l4_proto != IPPROTO_IPV6)
return -EINVAL;
addr6 = (struct in6_addr *)fsp->h_u.usr_ip6_spec.ip6src;
mask6 = (struct in6_addr *)fsp->m_u.usr_ip6_spec.ip6src;
if (!ipv6_addr_any(mask6)) {
ip_src = __fbnic_ip6_sync(fbd, fbd->ipo_src,
addr6, mask6);
if (!ip_src)
return -ENOSPC;
set_bit(idx, ip_src->act_tcam);
ip_value |=
FBNIC_RPC_TCAM_ACT0_OUTER_IPSRC_VALID |
FIELD_PREP(FBNIC_RPC_TCAM_ACT0_OUTER_IPSRC_IDX,
ip_src - fbd->ipo_src);
ip_mask &=
~(FBNIC_RPC_TCAM_ACT0_OUTER_IPSRC_VALID |
FBNIC_RPC_TCAM_ACT0_OUTER_IPSRC_IDX);
}
addr6 = (struct in6_addr *)fsp->h_u.usr_ip6_spec.ip6dst;
mask6 = (struct in6_addr *)fsp->m_u.usr_ip6_spec.ip6dst;
if (!ipv6_addr_any(mask6)) {
ip_dst = __fbnic_ip6_sync(fbd, fbd->ipo_dst,
addr6, mask6);
if (!ip_dst) {
if (ip_src && ip_src->state == FBNIC_TCAM_S_ADD)
memset(ip_src, 0, sizeof(*ip_src));
return -ENOSPC;
}
set_bit(idx, ip_dst->act_tcam);
ip_value |=
FBNIC_RPC_TCAM_ACT0_OUTER_IPDST_VALID |
FIELD_PREP(FBNIC_RPC_TCAM_ACT0_OUTER_IPDST_IDX,
ip_dst - fbd->ipo_dst);
ip_mask &= ~(FBNIC_RPC_TCAM_ACT0_OUTER_IPDST_VALID |
FBNIC_RPC_TCAM_ACT0_OUTER_IPDST_IDX);
}
flow_value |= FBNIC_RPC_TCAM_ACT1_OUTER_IP_VALID;
flow_mask &= FBNIC_RPC_TCAM_ACT1_OUTER_IP_VALID;
ipv6_flow:
addr6 = (struct in6_addr *)fsp->h_u.usr_ip6_spec.ip6src;
mask6 = (struct in6_addr *)fsp->m_u.usr_ip6_spec.ip6src;
if (!ip_src && !ipv6_addr_any(mask6)) {
ip_src = __fbnic_ip6_sync(fbd, fbd->ip_src,
addr6, mask6);
if (!ip_src)
return -ENOSPC;
set_bit(idx, ip_src->act_tcam);
ip_value |= FBNIC_RPC_TCAM_ACT0_IPSRC_VALID |
FIELD_PREP(FBNIC_RPC_TCAM_ACT0_IPSRC_IDX,
ip_src - fbd->ip_src);
ip_mask &= ~(FBNIC_RPC_TCAM_ACT0_IPSRC_VALID |
FBNIC_RPC_TCAM_ACT0_IPSRC_IDX);
}
addr6 = (struct in6_addr *)fsp->h_u.usr_ip6_spec.ip6dst;
mask6 = (struct in6_addr *)fsp->m_u.usr_ip6_spec.ip6dst;
if (!ip_dst && !ipv6_addr_any(mask6)) {
ip_dst = __fbnic_ip6_sync(fbd, fbd->ip_dst,
addr6, mask6);
if (!ip_dst) {
if (ip_src && ip_src->state == FBNIC_TCAM_S_ADD)
memset(ip_src, 0, sizeof(*ip_src));
return -ENOSPC;
}
set_bit(idx, ip_dst->act_tcam);
ip_value |= FBNIC_RPC_TCAM_ACT0_IPDST_VALID |
FIELD_PREP(FBNIC_RPC_TCAM_ACT0_IPDST_IDX,
ip_dst - fbd->ip_dst);
ip_mask &= ~(FBNIC_RPC_TCAM_ACT0_IPDST_VALID |
FBNIC_RPC_TCAM_ACT0_IPDST_IDX);
}
flow_value |= FBNIC_RPC_TCAM_ACT1_IP_IS_V6 |
FBNIC_RPC_TCAM_ACT1_IP_VALID |
FBNIC_RPC_TCAM_ACT1_L2_MACDA_VALID;
flow_mask &= ~(FBNIC_RPC_TCAM_ACT1_IP_IS_V6 |
FBNIC_RPC_TCAM_ACT1_IP_VALID |
FBNIC_RPC_TCAM_ACT1_L2_MACDA_VALID);
break;
case ETHER_FLOW:
if (!is_zero_ether_addr(fsp->m_u.ether_spec.h_dest)) {
u8 *addr = fsp->h_u.ether_spec.h_dest;
u8 *mask = fsp->m_u.ether_spec.h_dest;
/* Do not allow MAC addr of 0 */
if (is_zero_ether_addr(addr))
return -EINVAL;
/* Only support full MAC address to avoid
* conflicts with other MAC addresses.
*/
if (!is_broadcast_ether_addr(mask))
return -EINVAL;
if (is_multicast_ether_addr(addr))
mac_addr = __fbnic_mc_sync(fbd, addr);
else
mac_addr = __fbnic_uc_sync(fbd, addr);
if (!mac_addr)
return -ENOSPC;
set_bit(idx, mac_addr->act_tcam);
flow_value |=
FIELD_PREP(FBNIC_RPC_TCAM_ACT1_L2_MACDA_IDX,
mac_addr - fbd->mac_addr);
flow_mask &= ~FBNIC_RPC_TCAM_ACT1_L2_MACDA_IDX;
}
flow_value |= FBNIC_RPC_TCAM_ACT1_L2_MACDA_VALID;
flow_mask &= ~FBNIC_RPC_TCAM_ACT1_L2_MACDA_VALID;
break;
default:
return -EINVAL;
}
/* Write action table values */
act_tcam->dest = dest;
act_tcam->rss_en_mask = fbnic_flow_hash_2_rss_en_mask(fbn, hash_idx);
/* Write IP Match value/mask to action_tcam[0] */
act_tcam->value.tcam[0] = ip_value;
act_tcam->mask.tcam[0] = ip_mask;
/* Write flow type value/mask to action_tcam[1] */
act_tcam->value.tcam[1] = flow_value;
act_tcam->mask.tcam[1] = flow_mask;
/* Write error, DSCP, extra L4 matches to action_tcam[2] */
act_tcam->value.tcam[2] = misc;
act_tcam->mask.tcam[2] = misc_mask;
/* Write source/destination port values */
act_tcam->value.tcam[3] = sport;
act_tcam->mask.tcam[3] = sport_mask;
act_tcam->value.tcam[4] = dport;
act_tcam->mask.tcam[4] = dport_mask;
for (j = 5; j < FBNIC_RPC_TCAM_ACT_WORD_LEN; j++)
act_tcam->mask.tcam[j] = 0xffff;
act_tcam->state = FBNIC_TCAM_S_UPDATE;
fsp->location = location;
if (netif_running(fbn->netdev)) {
fbnic_write_rules(fbd);
if (ip_src || ip_dst)
fbnic_write_ip_addr(fbd);
if (mac_addr)
fbnic_write_macda(fbd);
}
return 0;
}
static void fbnic_clear_nfc_macda(struct fbnic_net *fbn,
unsigned int tcam_idx)
{
struct fbnic_dev *fbd = fbn->fbd;
int idx;
for (idx = ARRAY_SIZE(fbd->mac_addr); idx--;)
__fbnic_xc_unsync(&fbd->mac_addr[idx], tcam_idx);
/* Write updates to hardware */
if (netif_running(fbn->netdev))
fbnic_write_macda(fbd);
}
static void fbnic_clear_nfc_ip_addr(struct fbnic_net *fbn,
unsigned int tcam_idx)
{
struct fbnic_dev *fbd = fbn->fbd;
int idx;
for (idx = ARRAY_SIZE(fbd->ip_src); idx--;)
__fbnic_ip_unsync(&fbd->ip_src[idx], tcam_idx);
for (idx = ARRAY_SIZE(fbd->ip_dst); idx--;)
__fbnic_ip_unsync(&fbd->ip_dst[idx], tcam_idx);
for (idx = ARRAY_SIZE(fbd->ipo_src); idx--;)
__fbnic_ip_unsync(&fbd->ipo_src[idx], tcam_idx);
for (idx = ARRAY_SIZE(fbd->ipo_dst); idx--;)
__fbnic_ip_unsync(&fbd->ipo_dst[idx], tcam_idx);
/* Write updates to hardware */
if (netif_running(fbn->netdev))
fbnic_write_ip_addr(fbd);
}
static int fbnic_set_cls_rule_del(struct fbnic_net *fbn,
const struct ethtool_rxnfc *cmd)
{
struct ethtool_rx_flow_spec *fsp;
struct fbnic_dev *fbd = fbn->fbd;
struct fbnic_act_tcam *act_tcam;
int idx;
fsp = (struct ethtool_rx_flow_spec *)&cmd->fs;
if (fsp->location >= FBNIC_RPC_ACT_TBL_NFC_ENTRIES)
return -EINVAL;
idx = fsp->location + FBNIC_RPC_ACT_TBL_NFC_OFFSET;
act_tcam = &fbd->act_tcam[idx];
if (act_tcam->state != FBNIC_TCAM_S_VALID)
return -EINVAL;
act_tcam->state = FBNIC_TCAM_S_DELETE;
if ((act_tcam->value.tcam[1] & FBNIC_RPC_TCAM_ACT1_L2_MACDA_VALID) &&
(~act_tcam->mask.tcam[1] & FBNIC_RPC_TCAM_ACT1_L2_MACDA_IDX))
fbnic_clear_nfc_macda(fbn, idx);
if ((act_tcam->value.tcam[0] &
(FBNIC_RPC_TCAM_ACT0_IPSRC_VALID |
FBNIC_RPC_TCAM_ACT0_IPDST_VALID |
FBNIC_RPC_TCAM_ACT0_OUTER_IPSRC_VALID |
FBNIC_RPC_TCAM_ACT0_OUTER_IPDST_VALID)) &&
(~act_tcam->mask.tcam[0] &
(FBNIC_RPC_TCAM_ACT0_IPSRC_IDX |
FBNIC_RPC_TCAM_ACT0_IPDST_IDX |
FBNIC_RPC_TCAM_ACT0_OUTER_IPSRC_IDX |
FBNIC_RPC_TCAM_ACT0_OUTER_IPDST_IDX)))
fbnic_clear_nfc_ip_addr(fbn, idx);
if (netif_running(fbn->netdev))
fbnic_write_rules(fbd);
return 0;
}
static int fbnic_set_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd)
{
struct fbnic_net *fbn = netdev_priv(netdev);
@ -281,6 +921,12 @@ static int fbnic_set_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd)
case ETHTOOL_SRXFH:
ret = fbnic_set_rss_hash_opts(fbn, cmd);
break;
case ETHTOOL_SRXCLSRLINS:
ret = fbnic_set_cls_rule_ins(fbn, cmd);
break;
case ETHTOOL_SRXCLSRLDEL:
ret = fbnic_set_cls_rule_del(fbn, cmd);
break;
}
return ret;

View File

@ -639,6 +639,7 @@ struct net_device *fbnic_netdev_alloc(struct fbnic_dev *fbd)
netdev->hw_features |= netdev->features;
netdev->vlan_features |= netdev->features;
netdev->hw_enc_features |= netdev->features;
netdev->features |= NETIF_F_NTUPLE;
netdev->min_mtu = IPV6_MIN_MTU;
netdev->max_mtu = FBNIC_MAX_JUMBO_FRAME_SIZE - ETH_HLEN;

View File

@ -61,7 +61,7 @@ void fbnic_rss_disable_hw(struct fbnic_dev *fbd)
#define FBNIC_FH_2_RSSEM_BIT(_fh, _rssem, _val) \
FIELD_PREP(FBNIC_RPC_ACT_TBL1_RSS_ENA_##_rssem, \
FIELD_GET(RXH_##_fh, _val))
static u16 fbnic_flow_hash_2_rss_en_mask(struct fbnic_net *fbn, int flow_type)
u16 fbnic_flow_hash_2_rss_en_mask(struct fbnic_net *fbn, int flow_type)
{
u32 flow_hash = fbn->rss_flow_hash[flow_type];
u32 rss_en_mask = 0;

View File

@ -96,6 +96,11 @@ enum {
#define FBNIC_RPC_ACT_TBL_BMC_OFFSET 0
#define FBNIC_RPC_ACT_TBL_BMC_ALL_MULTI_OFFSET 1
/* This should leave us with 48 total entries in the TCAM that can be used
* for NFC after also deducting the 14 needed for RSS table programming.
*/
#define FBNIC_RPC_ACT_TBL_NFC_OFFSET 2
/* We reserve the last 14 entries for RSS rules on the host. The BMC
* unicast rule will need to be populated above these and is expected to
* use MACDA TCAM entry 23 to store the BMC MAC address.
@ -103,6 +108,9 @@ enum {
#define FBNIC_RPC_ACT_TBL_RSS_OFFSET \
(FBNIC_RPC_ACT_TBL_NUM_ENTRIES - FBNIC_RSS_EN_NUM_ENTRIES)
#define FBNIC_RPC_ACT_TBL_NFC_ENTRIES \
(FBNIC_RPC_ACT_TBL_RSS_OFFSET - FBNIC_RPC_ACT_TBL_NFC_OFFSET)
/* Flags used to identify the owner for this MAC filter. Note that any
* flags set for Broadcast thru Promisc indicate that the rule belongs
* to the RSS filters for the host.
@ -183,6 +191,7 @@ void fbnic_rss_init_en_mask(struct fbnic_net *fbn);
void fbnic_rss_disable_hw(struct fbnic_dev *fbd);
void fbnic_rss_reinit_hw(struct fbnic_dev *fbd, struct fbnic_net *fbn);
void fbnic_rss_reinit(struct fbnic_dev *fbd, struct fbnic_net *fbn);
u16 fbnic_flow_hash_2_rss_en_mask(struct fbnic_net *fbn, int flow_type);
int __fbnic_xc_unsync(struct fbnic_mac_addr *mac_addr, unsigned int tcam_idx);
struct fbnic_mac_addr *__fbnic_uc_sync(struct fbnic_dev *fbd,