mirror of
https://github.com/torvalds/linux.git
synced 2026-05-24 23:22:31 +02:00
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:
parent
3a265bd6a3
commit
2230035439
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user