mirror of
https://github.com/torvalds/linux.git
synced 2026-05-27 16:44:58 +02:00
Merge branch 'eth-fbnic-support-rss-contexts-and-ntuple-filters'
Jakub Kicinski says: ==================== eth: fbnic: support RSS contexts and ntuple filters Add support for RSS contexts and ntuple filters in fbnic. The device has only one context, intended for use by TCP zero-copy Rx. First two patches add a check we seem to be missing in the core, to avoid having to copy it to all drivers. $ ./drivers/net/hw/rss_ctx.py KTAP version 1 1..16 ok 1 rss_ctx.test_rss_key_indir ok 2 rss_ctx.test_rss_queue_reconfigure ok 3 rss_ctx.test_rss_resize ok 4 rss_ctx.test_hitless_key_update ok 5 rss_ctx.test_rss_context # Failed to create context 2, trying to test what we got ok 6 rss_ctx.test_rss_context4 # SKIP Tested only 1 contexts, wanted 4 # Increasing queue count 44 -> 66 # Failed to create context 2, trying to test what we got ok 7 rss_ctx.test_rss_context32 # SKIP Tested only 1 contexts, wanted 32 # Added only 1 out of 3 contexts ok 8 rss_ctx.test_rss_context_dump # Driver does not support rss + queue offset ok 9 rss_ctx.test_rss_context_queue_reconfigure ok 10 rss_ctx.test_rss_context_overlap ok 11 rss_ctx.test_rss_context_overlap2 # SKIP Test requires at least 2 contexts, but device only has 1 ok 12 rss_ctx.test_rss_context_out_of_order # SKIP Test requires at least 4 contexts, but device only has 1 # Failed to create context 2, trying to test what we got ok 13 rss_ctx.test_rss_context4_create_with_cfg # SKIP Tested only 1 contexts, wanted 4 ok 14 rss_ctx.test_flow_add_context_missing ok 15 rss_ctx.test_delete_rss_context_busy ok 16 rss_ctx.test_rss_ntuple_addition # SKIP Ntuple filter with RSS and nonzero action not supported # Totals: pass:10 fail:0 xfail:0 xpass:0 skip:6 error:0 ==================== Link: https://patch.msgid.link/20250206235334.1425329-1-kuba@kernel.org Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
commit
39f54262ba
|
|
@ -60,6 +60,12 @@ struct fbnic_dev {
|
|||
u8 mac_addr_boundary;
|
||||
u8 tce_tcam_last;
|
||||
|
||||
/* IP TCAM */
|
||||
struct fbnic_ip_addr ip_src[FBNIC_RPC_TCAM_IP_ADDR_NUM_ENTRIES];
|
||||
struct fbnic_ip_addr ip_dst[FBNIC_RPC_TCAM_IP_ADDR_NUM_ENTRIES];
|
||||
struct fbnic_ip_addr ipo_src[FBNIC_RPC_TCAM_IP_ADDR_NUM_ENTRIES];
|
||||
struct fbnic_ip_addr ipo_dst[FBNIC_RPC_TCAM_IP_ADDR_NUM_ENTRIES];
|
||||
|
||||
/* Number of TCQs/RCQs available on hardware */
|
||||
u16 max_num_queues;
|
||||
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
@ -677,6 +680,9 @@ enum {
|
|||
|
||||
#define FBNIC_RPC_TCAM_OUTER_IPSRC(m, n)\
|
||||
(0x08c00 + 0x08 * (n) + (m)) /* 0x023000 + 32*n + 4*m */
|
||||
#define FBNIC_RPC_TCAM_IP_ADDR_VALUE CSR_GENMASK(15, 0)
|
||||
#define FBNIC_RPC_TCAM_IP_ADDR_MASK CSR_GENMASK(31, 16)
|
||||
|
||||
#define FBNIC_RPC_TCAM_OUTER_IPDST(m, n)\
|
||||
(0x08c48 + 0x08 * (n) + (m)) /* 0x023120 + 32*n + 4*m */
|
||||
#define FBNIC_RPC_TCAM_IPSRC(m, n)\
|
||||
|
|
|
|||
|
|
@ -44,6 +44,132 @@ static int fbnic_dbg_mac_addr_show(struct seq_file *s, void *v)
|
|||
}
|
||||
DEFINE_SHOW_ATTRIBUTE(fbnic_dbg_mac_addr);
|
||||
|
||||
static int fbnic_dbg_tce_tcam_show(struct seq_file *s, void *v)
|
||||
{
|
||||
struct fbnic_dev *fbd = s->private;
|
||||
int i, tcam_idx = 0;
|
||||
char hdr[80];
|
||||
|
||||
/* Generate Header */
|
||||
snprintf(hdr, sizeof(hdr), "%3s %s %-17s %s\n",
|
||||
"Idx", "S", "TCAM Bitmap", "Addr/Mask");
|
||||
seq_puts(s, hdr);
|
||||
fbnic_dbg_desc_break(s, strnlen(hdr, sizeof(hdr)));
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(fbd->mac_addr); i++) {
|
||||
struct fbnic_mac_addr *mac_addr = &fbd->mac_addr[i];
|
||||
|
||||
/* Verify BMC bit is set */
|
||||
if (!test_bit(FBNIC_MAC_ADDR_T_BMC, mac_addr->act_tcam))
|
||||
continue;
|
||||
|
||||
if (tcam_idx == FBNIC_TCE_TCAM_NUM_ENTRIES)
|
||||
break;
|
||||
|
||||
seq_printf(s, "%02d %d %64pb %pm\n",
|
||||
tcam_idx, mac_addr->state, mac_addr->act_tcam,
|
||||
mac_addr->value.addr8);
|
||||
seq_printf(s, " %pm\n",
|
||||
mac_addr->mask.addr8);
|
||||
tcam_idx++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
DEFINE_SHOW_ATTRIBUTE(fbnic_dbg_tce_tcam);
|
||||
|
||||
static int fbnic_dbg_act_tcam_show(struct seq_file *s, void *v)
|
||||
{
|
||||
struct fbnic_dev *fbd = s->private;
|
||||
char hdr[80];
|
||||
int i;
|
||||
|
||||
/* Generate Header */
|
||||
snprintf(hdr, sizeof(hdr), "%3s %s %-55s %-4s %s\n",
|
||||
"Idx", "S", "Value/Mask", "RSS", "Dest");
|
||||
seq_puts(s, hdr);
|
||||
fbnic_dbg_desc_break(s, strnlen(hdr, sizeof(hdr)));
|
||||
|
||||
for (i = 0; i < FBNIC_RPC_TCAM_ACT_NUM_ENTRIES; i++) {
|
||||
struct fbnic_act_tcam *act_tcam = &fbd->act_tcam[i];
|
||||
|
||||
seq_printf(s, "%02d %d %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %08x\n",
|
||||
i, act_tcam->state,
|
||||
act_tcam->value.tcam[10], act_tcam->value.tcam[9],
|
||||
act_tcam->value.tcam[8], act_tcam->value.tcam[7],
|
||||
act_tcam->value.tcam[6], act_tcam->value.tcam[5],
|
||||
act_tcam->value.tcam[4], act_tcam->value.tcam[3],
|
||||
act_tcam->value.tcam[2], act_tcam->value.tcam[1],
|
||||
act_tcam->value.tcam[0], act_tcam->rss_en_mask,
|
||||
act_tcam->dest);
|
||||
seq_printf(s, " %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x\n",
|
||||
act_tcam->mask.tcam[10], act_tcam->mask.tcam[9],
|
||||
act_tcam->mask.tcam[8], act_tcam->mask.tcam[7],
|
||||
act_tcam->mask.tcam[6], act_tcam->mask.tcam[5],
|
||||
act_tcam->mask.tcam[4], act_tcam->mask.tcam[3],
|
||||
act_tcam->mask.tcam[2], act_tcam->mask.tcam[1],
|
||||
act_tcam->mask.tcam[0]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
DEFINE_SHOW_ATTRIBUTE(fbnic_dbg_act_tcam);
|
||||
|
||||
static int fbnic_dbg_ip_addr_show(struct seq_file *s,
|
||||
struct fbnic_ip_addr *ip_addr)
|
||||
{
|
||||
char hdr[80];
|
||||
int i;
|
||||
|
||||
/* Generate Header */
|
||||
snprintf(hdr, sizeof(hdr), "%3s %s %-17s %s %s\n",
|
||||
"Idx", "S", "TCAM Bitmap", "V", "Addr/Mask");
|
||||
seq_puts(s, hdr);
|
||||
fbnic_dbg_desc_break(s, strnlen(hdr, sizeof(hdr)));
|
||||
|
||||
for (i = 0; i < FBNIC_RPC_TCAM_IP_ADDR_NUM_ENTRIES; i++, ip_addr++) {
|
||||
seq_printf(s, "%02d %d %64pb %d %pi6\n",
|
||||
i, ip_addr->state, ip_addr->act_tcam,
|
||||
ip_addr->version, &ip_addr->value);
|
||||
seq_printf(s, " %pi6\n",
|
||||
&ip_addr->mask);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fbnic_dbg_ip_src_show(struct seq_file *s, void *v)
|
||||
{
|
||||
struct fbnic_dev *fbd = s->private;
|
||||
|
||||
return fbnic_dbg_ip_addr_show(s, fbd->ip_src);
|
||||
}
|
||||
DEFINE_SHOW_ATTRIBUTE(fbnic_dbg_ip_src);
|
||||
|
||||
static int fbnic_dbg_ip_dst_show(struct seq_file *s, void *v)
|
||||
{
|
||||
struct fbnic_dev *fbd = s->private;
|
||||
|
||||
return fbnic_dbg_ip_addr_show(s, fbd->ip_dst);
|
||||
}
|
||||
DEFINE_SHOW_ATTRIBUTE(fbnic_dbg_ip_dst);
|
||||
|
||||
static int fbnic_dbg_ipo_src_show(struct seq_file *s, void *v)
|
||||
{
|
||||
struct fbnic_dev *fbd = s->private;
|
||||
|
||||
return fbnic_dbg_ip_addr_show(s, fbd->ipo_src);
|
||||
}
|
||||
DEFINE_SHOW_ATTRIBUTE(fbnic_dbg_ipo_src);
|
||||
|
||||
static int fbnic_dbg_ipo_dst_show(struct seq_file *s, void *v)
|
||||
{
|
||||
struct fbnic_dev *fbd = s->private;
|
||||
|
||||
return fbnic_dbg_ip_addr_show(s, fbd->ipo_dst);
|
||||
}
|
||||
DEFINE_SHOW_ATTRIBUTE(fbnic_dbg_ipo_dst);
|
||||
|
||||
static int fbnic_dbg_pcie_stats_show(struct seq_file *s, void *v)
|
||||
{
|
||||
struct fbnic_dev *fbd = s->private;
|
||||
|
|
@ -84,6 +210,18 @@ void fbnic_dbg_fbd_init(struct fbnic_dev *fbd)
|
|||
&fbnic_dbg_pcie_stats_fops);
|
||||
debugfs_create_file("mac_addr", 0400, fbd->dbg_fbd, fbd,
|
||||
&fbnic_dbg_mac_addr_fops);
|
||||
debugfs_create_file("tce_tcam", 0400, fbd->dbg_fbd, fbd,
|
||||
&fbnic_dbg_tce_tcam_fops);
|
||||
debugfs_create_file("act_tcam", 0400, fbd->dbg_fbd, fbd,
|
||||
&fbnic_dbg_act_tcam_fops);
|
||||
debugfs_create_file("ip_src", 0400, fbd->dbg_fbd, fbd,
|
||||
&fbnic_dbg_ip_src_fops);
|
||||
debugfs_create_file("ip_dst", 0400, fbd->dbg_fbd, fbd,
|
||||
&fbnic_dbg_ip_dst_fops);
|
||||
debugfs_create_file("ipo_src", 0400, fbd->dbg_fbd, fbd,
|
||||
&fbnic_dbg_ipo_src_fops);
|
||||
debugfs_create_file("ipo_dst", 0400, fbd->dbg_fbd, fbd,
|
||||
&fbnic_dbg_ipo_dst_fops);
|
||||
}
|
||||
|
||||
void fbnic_dbg_fbd_exit(struct fbnic_dev *fbd)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -374,6 +1020,61 @@ fbnic_set_rxfh(struct net_device *netdev, struct ethtool_rxfh_param *rxfh,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
fbnic_modify_rxfh_context(struct net_device *netdev,
|
||||
struct ethtool_rxfh_context *ctx,
|
||||
const struct ethtool_rxfh_param *rxfh,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct fbnic_net *fbn = netdev_priv(netdev);
|
||||
const u32 *indir = rxfh->indir;
|
||||
unsigned int changes;
|
||||
|
||||
if (!indir)
|
||||
indir = ethtool_rxfh_context_indir(ctx);
|
||||
|
||||
changes = fbnic_set_indir(fbn, rxfh->rss_context, indir);
|
||||
if (changes && netif_running(netdev))
|
||||
fbnic_rss_reinit_hw(fbn->fbd, fbn);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
fbnic_create_rxfh_context(struct net_device *netdev,
|
||||
struct ethtool_rxfh_context *ctx,
|
||||
const struct ethtool_rxfh_param *rxfh,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct fbnic_net *fbn = netdev_priv(netdev);
|
||||
|
||||
if (rxfh->hfunc && rxfh->hfunc != ETH_RSS_HASH_TOP) {
|
||||
NL_SET_ERR_MSG_MOD(extack, "RSS hash function not supported");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
ctx->hfunc = ETH_RSS_HASH_TOP;
|
||||
|
||||
if (!rxfh->indir) {
|
||||
u32 *indir = ethtool_rxfh_context_indir(ctx);
|
||||
unsigned int num_rx = fbn->num_rx_queues;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < FBNIC_RPC_RSS_TBL_SIZE; i++)
|
||||
indir[i] = ethtool_rxfh_indir_default(i, num_rx);
|
||||
}
|
||||
|
||||
return fbnic_modify_rxfh_context(netdev, ctx, rxfh, extack);
|
||||
}
|
||||
|
||||
static int
|
||||
fbnic_remove_rxfh_context(struct net_device *netdev,
|
||||
struct ethtool_rxfh_context *ctx, u32 rss_context,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
/* Nothing to do, contexts are allocated statically */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fbnic_get_channels(struct net_device *netdev,
|
||||
struct ethtool_channels *ch)
|
||||
{
|
||||
|
|
@ -586,6 +1287,7 @@ fbnic_get_eth_mac_stats(struct net_device *netdev,
|
|||
}
|
||||
|
||||
static const struct ethtool_ops fbnic_ethtool_ops = {
|
||||
.rxfh_max_num_contexts = FBNIC_RPC_RSS_TBL_COUNT,
|
||||
.get_drvinfo = fbnic_get_drvinfo,
|
||||
.get_regs_len = fbnic_get_regs_len,
|
||||
.get_regs = fbnic_get_regs,
|
||||
|
|
@ -598,6 +1300,9 @@ static const struct ethtool_ops fbnic_ethtool_ops = {
|
|||
.get_rxfh_indir_size = fbnic_get_rxfh_indir_size,
|
||||
.get_rxfh = fbnic_get_rxfh,
|
||||
.set_rxfh = fbnic_set_rxfh,
|
||||
.create_rxfh_context = fbnic_create_rxfh_context,
|
||||
.modify_rxfh_context = fbnic_modify_rxfh_context,
|
||||
.remove_rxfh_context = fbnic_remove_rxfh_context,
|
||||
.get_channels = fbnic_get_channels,
|
||||
.set_channels = fbnic_set_channels,
|
||||
.get_ts_info = fbnic_get_ts_info,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/ethtool.h>
|
||||
#include <net/ipv6.h>
|
||||
|
||||
#include "fbnic.h"
|
||||
#include "fbnic_netdev.h"
|
||||
|
|
@ -60,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;
|
||||
|
|
@ -698,6 +699,359 @@ void fbnic_write_tce_tcam(struct fbnic_dev *fbd)
|
|||
__fbnic_write_tce_tcam(fbd);
|
||||
}
|
||||
|
||||
struct fbnic_ip_addr *__fbnic_ip4_sync(struct fbnic_dev *fbd,
|
||||
struct fbnic_ip_addr *ip_addr,
|
||||
const struct in_addr *addr,
|
||||
const struct in_addr *mask)
|
||||
{
|
||||
struct fbnic_ip_addr *avail_addr = NULL;
|
||||
unsigned int i;
|
||||
|
||||
/* Scan from top of list to bottom, filling bottom up. */
|
||||
for (i = 0; i < FBNIC_RPC_TCAM_IP_ADDR_NUM_ENTRIES; i++, ip_addr++) {
|
||||
struct in6_addr *m = &ip_addr->mask;
|
||||
|
||||
if (ip_addr->state == FBNIC_TCAM_S_DISABLED) {
|
||||
avail_addr = ip_addr;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ip_addr->version != 4)
|
||||
continue;
|
||||
|
||||
/* Drop avail_addr if mask is a subset of our current mask,
|
||||
* This prevents us from inserting a longer prefix behind a
|
||||
* shorter one.
|
||||
*
|
||||
* The mask is stored inverted value so as an example:
|
||||
* m ffff ffff ffff ffff ffff ffff ffff 0000 0000
|
||||
* mask 0000 0000 0000 0000 0000 0000 0000 ffff ffff
|
||||
*
|
||||
* "m" and "mask" represent typical IPv4 mask stored in
|
||||
* the TCAM and those provided by the stack. The code below
|
||||
* should return a non-zero result if there is a 0 stored
|
||||
* anywhere in "m" where "mask" has a 0.
|
||||
*/
|
||||
if (~m->s6_addr32[3] & ~mask->s_addr) {
|
||||
avail_addr = NULL;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Check to see if the mask actually contains fewer bits than
|
||||
* our new mask "m". The XOR below should only result in 0 if
|
||||
* "m" is masking a bit that we are looking for in our new
|
||||
* "mask", we eliminated the 0^0 case with the check above.
|
||||
*
|
||||
* If it contains fewer bits we need to stop here, otherwise
|
||||
* we might be adding an unreachable rule.
|
||||
*/
|
||||
if (~(m->s6_addr32[3] ^ mask->s_addr))
|
||||
break;
|
||||
|
||||
if (ip_addr->value.s6_addr32[3] == addr->s_addr) {
|
||||
avail_addr = ip_addr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (avail_addr && avail_addr->state == FBNIC_TCAM_S_DISABLED) {
|
||||
ipv6_addr_set(&avail_addr->value, 0, 0, 0, addr->s_addr);
|
||||
ipv6_addr_set(&avail_addr->mask, htonl(~0), htonl(~0),
|
||||
htonl(~0), ~mask->s_addr);
|
||||
avail_addr->version = 4;
|
||||
|
||||
avail_addr->state = FBNIC_TCAM_S_ADD;
|
||||
}
|
||||
|
||||
return avail_addr;
|
||||
}
|
||||
|
||||
struct fbnic_ip_addr *__fbnic_ip6_sync(struct fbnic_dev *fbd,
|
||||
struct fbnic_ip_addr *ip_addr,
|
||||
const struct in6_addr *addr,
|
||||
const struct in6_addr *mask)
|
||||
{
|
||||
struct fbnic_ip_addr *avail_addr = NULL;
|
||||
unsigned int i;
|
||||
|
||||
ip_addr = &ip_addr[FBNIC_RPC_TCAM_IP_ADDR_NUM_ENTRIES - 1];
|
||||
|
||||
/* Scan from bottom of list to top, filling top down. */
|
||||
for (i = FBNIC_RPC_TCAM_IP_ADDR_NUM_ENTRIES; i--; ip_addr--) {
|
||||
struct in6_addr *m = &ip_addr->mask;
|
||||
|
||||
if (ip_addr->state == FBNIC_TCAM_S_DISABLED) {
|
||||
avail_addr = ip_addr;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ip_addr->version != 6)
|
||||
continue;
|
||||
|
||||
/* Drop avail_addr if mask is a superset of our current mask.
|
||||
* This prevents us from inserting a longer prefix behind a
|
||||
* shorter one.
|
||||
*
|
||||
* The mask is stored inverted value so as an example:
|
||||
* m 0000 0000 0000 0000 0000 0000 0000 0000 0000
|
||||
* mask ffff ffff ffff ffff ffff ffff ffff ffff ffff
|
||||
*
|
||||
* "m" and "mask" represent typical IPv6 mask stored in
|
||||
* the TCAM and those provided by the stack. The code below
|
||||
* should return a non-zero result which will cause us
|
||||
* to drop the avail_addr value that might be cached
|
||||
* to prevent us from dropping a v6 address behind it.
|
||||
*/
|
||||
if ((m->s6_addr32[0] & mask->s6_addr32[0]) |
|
||||
(m->s6_addr32[1] & mask->s6_addr32[1]) |
|
||||
(m->s6_addr32[2] & mask->s6_addr32[2]) |
|
||||
(m->s6_addr32[3] & mask->s6_addr32[3])) {
|
||||
avail_addr = NULL;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* The previous test eliminated any overlap between the
|
||||
* two values so now we need to check for gaps.
|
||||
*
|
||||
* If the mask is equal to our current mask then it should
|
||||
* result with m ^ mask = ffff ffff, if however the value
|
||||
* stored in m is bigger then we should see a 0 appear
|
||||
* somewhere in the mask.
|
||||
*/
|
||||
if (~(m->s6_addr32[0] ^ mask->s6_addr32[0]) |
|
||||
~(m->s6_addr32[1] ^ mask->s6_addr32[1]) |
|
||||
~(m->s6_addr32[2] ^ mask->s6_addr32[2]) |
|
||||
~(m->s6_addr32[3] ^ mask->s6_addr32[3]))
|
||||
break;
|
||||
|
||||
if (ipv6_addr_cmp(&ip_addr->value, addr))
|
||||
continue;
|
||||
|
||||
avail_addr = ip_addr;
|
||||
break;
|
||||
}
|
||||
|
||||
if (avail_addr && avail_addr->state == FBNIC_TCAM_S_DISABLED) {
|
||||
memcpy(&avail_addr->value, addr, sizeof(*addr));
|
||||
ipv6_addr_set(&avail_addr->mask,
|
||||
~mask->s6_addr32[0], ~mask->s6_addr32[1],
|
||||
~mask->s6_addr32[2], ~mask->s6_addr32[3]);
|
||||
avail_addr->version = 6;
|
||||
|
||||
avail_addr->state = FBNIC_TCAM_S_ADD;
|
||||
}
|
||||
|
||||
return avail_addr;
|
||||
}
|
||||
|
||||
int __fbnic_ip_unsync(struct fbnic_ip_addr *ip_addr, unsigned int tcam_idx)
|
||||
{
|
||||
if (!test_and_clear_bit(tcam_idx, ip_addr->act_tcam))
|
||||
return -ENOENT;
|
||||
|
||||
if (bitmap_empty(ip_addr->act_tcam, FBNIC_RPC_TCAM_ACT_NUM_ENTRIES))
|
||||
ip_addr->state = FBNIC_TCAM_S_DELETE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fbnic_clear_ip_src_entry(struct fbnic_dev *fbd, unsigned int idx)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Invalidate entry and clear addr state info */
|
||||
for (i = 0; i <= FBNIC_RPC_TCAM_IP_ADDR_WORD_LEN; i++)
|
||||
wr32(fbd, FBNIC_RPC_TCAM_IPSRC(idx, i), 0);
|
||||
}
|
||||
|
||||
static void fbnic_clear_ip_dst_entry(struct fbnic_dev *fbd, unsigned int idx)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Invalidate entry and clear addr state info */
|
||||
for (i = 0; i <= FBNIC_RPC_TCAM_IP_ADDR_WORD_LEN; i++)
|
||||
wr32(fbd, FBNIC_RPC_TCAM_IPDST(idx, i), 0);
|
||||
}
|
||||
|
||||
static void fbnic_clear_ip_outer_src_entry(struct fbnic_dev *fbd,
|
||||
unsigned int idx)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Invalidate entry and clear addr state info */
|
||||
for (i = 0; i <= FBNIC_RPC_TCAM_IP_ADDR_WORD_LEN; i++)
|
||||
wr32(fbd, FBNIC_RPC_TCAM_OUTER_IPSRC(idx, i), 0);
|
||||
}
|
||||
|
||||
static void fbnic_clear_ip_outer_dst_entry(struct fbnic_dev *fbd,
|
||||
unsigned int idx)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Invalidate entry and clear addr state info */
|
||||
for (i = 0; i <= FBNIC_RPC_TCAM_IP_ADDR_WORD_LEN; i++)
|
||||
wr32(fbd, FBNIC_RPC_TCAM_OUTER_IPDST(idx, i), 0);
|
||||
}
|
||||
|
||||
static void fbnic_write_ip_src_entry(struct fbnic_dev *fbd, unsigned int idx,
|
||||
struct fbnic_ip_addr *ip_addr)
|
||||
{
|
||||
__be16 *mask, *value;
|
||||
int i;
|
||||
|
||||
mask = &ip_addr->mask.s6_addr16[FBNIC_RPC_TCAM_IP_ADDR_WORD_LEN - 1];
|
||||
value = &ip_addr->value.s6_addr16[FBNIC_RPC_TCAM_IP_ADDR_WORD_LEN - 1];
|
||||
|
||||
for (i = 0; i < FBNIC_RPC_TCAM_IP_ADDR_WORD_LEN; i++)
|
||||
wr32(fbd, FBNIC_RPC_TCAM_IPSRC(idx, i),
|
||||
FIELD_PREP(FBNIC_RPC_TCAM_IP_ADDR_MASK, ntohs(*mask--)) |
|
||||
FIELD_PREP(FBNIC_RPC_TCAM_IP_ADDR_VALUE, ntohs(*value--)));
|
||||
wrfl(fbd);
|
||||
|
||||
/* Bit 129 is used to flag for v4/v6 */
|
||||
wr32(fbd, FBNIC_RPC_TCAM_IPSRC(idx, i),
|
||||
(ip_addr->version == 6) | FBNIC_RPC_TCAM_VALIDATE);
|
||||
}
|
||||
|
||||
static void fbnic_write_ip_dst_entry(struct fbnic_dev *fbd, unsigned int idx,
|
||||
struct fbnic_ip_addr *ip_addr)
|
||||
{
|
||||
__be16 *mask, *value;
|
||||
int i;
|
||||
|
||||
mask = &ip_addr->mask.s6_addr16[FBNIC_RPC_TCAM_IP_ADDR_WORD_LEN - 1];
|
||||
value = &ip_addr->value.s6_addr16[FBNIC_RPC_TCAM_IP_ADDR_WORD_LEN - 1];
|
||||
|
||||
for (i = 0; i < FBNIC_RPC_TCAM_IP_ADDR_WORD_LEN; i++)
|
||||
wr32(fbd, FBNIC_RPC_TCAM_IPDST(idx, i),
|
||||
FIELD_PREP(FBNIC_RPC_TCAM_IP_ADDR_MASK, ntohs(*mask--)) |
|
||||
FIELD_PREP(FBNIC_RPC_TCAM_IP_ADDR_VALUE, ntohs(*value--)));
|
||||
wrfl(fbd);
|
||||
|
||||
/* Bit 129 is used to flag for v4/v6 */
|
||||
wr32(fbd, FBNIC_RPC_TCAM_IPDST(idx, i),
|
||||
(ip_addr->version == 6) | FBNIC_RPC_TCAM_VALIDATE);
|
||||
}
|
||||
|
||||
static void fbnic_write_ip_outer_src_entry(struct fbnic_dev *fbd,
|
||||
unsigned int idx,
|
||||
struct fbnic_ip_addr *ip_addr)
|
||||
{
|
||||
__be16 *mask, *value;
|
||||
int i;
|
||||
|
||||
mask = &ip_addr->mask.s6_addr16[FBNIC_RPC_TCAM_IP_ADDR_WORD_LEN - 1];
|
||||
value = &ip_addr->value.s6_addr16[FBNIC_RPC_TCAM_IP_ADDR_WORD_LEN - 1];
|
||||
|
||||
for (i = 0; i < FBNIC_RPC_TCAM_IP_ADDR_WORD_LEN; i++)
|
||||
wr32(fbd, FBNIC_RPC_TCAM_OUTER_IPSRC(idx, i),
|
||||
FIELD_PREP(FBNIC_RPC_TCAM_IP_ADDR_MASK, ntohs(*mask--)) |
|
||||
FIELD_PREP(FBNIC_RPC_TCAM_IP_ADDR_VALUE, ntohs(*value--)));
|
||||
wrfl(fbd);
|
||||
|
||||
wr32(fbd, FBNIC_RPC_TCAM_OUTER_IPSRC(idx, i), FBNIC_RPC_TCAM_VALIDATE);
|
||||
}
|
||||
|
||||
static void fbnic_write_ip_outer_dst_entry(struct fbnic_dev *fbd,
|
||||
unsigned int idx,
|
||||
struct fbnic_ip_addr *ip_addr)
|
||||
{
|
||||
__be16 *mask, *value;
|
||||
int i;
|
||||
|
||||
mask = &ip_addr->mask.s6_addr16[FBNIC_RPC_TCAM_IP_ADDR_WORD_LEN - 1];
|
||||
value = &ip_addr->value.s6_addr16[FBNIC_RPC_TCAM_IP_ADDR_WORD_LEN - 1];
|
||||
|
||||
for (i = 0; i < FBNIC_RPC_TCAM_IP_ADDR_WORD_LEN; i++)
|
||||
wr32(fbd, FBNIC_RPC_TCAM_OUTER_IPDST(idx, i),
|
||||
FIELD_PREP(FBNIC_RPC_TCAM_IP_ADDR_MASK, ntohs(*mask--)) |
|
||||
FIELD_PREP(FBNIC_RPC_TCAM_IP_ADDR_VALUE, ntohs(*value--)));
|
||||
wrfl(fbd);
|
||||
|
||||
wr32(fbd, FBNIC_RPC_TCAM_OUTER_IPDST(idx, i), FBNIC_RPC_TCAM_VALIDATE);
|
||||
}
|
||||
|
||||
void fbnic_write_ip_addr(struct fbnic_dev *fbd)
|
||||
{
|
||||
int idx;
|
||||
|
||||
for (idx = ARRAY_SIZE(fbd->ip_src); idx--;) {
|
||||
struct fbnic_ip_addr *ip_addr = &fbd->ip_src[idx];
|
||||
|
||||
/* Check if update flag is set else skip. */
|
||||
if (!(ip_addr->state & FBNIC_TCAM_S_UPDATE))
|
||||
continue;
|
||||
|
||||
/* Clear by writing 0s. */
|
||||
if (ip_addr->state == FBNIC_TCAM_S_DELETE) {
|
||||
/* Invalidate entry and clear addr state info */
|
||||
fbnic_clear_ip_src_entry(fbd, idx);
|
||||
memset(ip_addr, 0, sizeof(*ip_addr));
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
fbnic_write_ip_src_entry(fbd, idx, ip_addr);
|
||||
|
||||
ip_addr->state = FBNIC_TCAM_S_VALID;
|
||||
}
|
||||
|
||||
/* Repeat process for other IP TCAMs */
|
||||
for (idx = ARRAY_SIZE(fbd->ip_dst); idx--;) {
|
||||
struct fbnic_ip_addr *ip_addr = &fbd->ip_dst[idx];
|
||||
|
||||
if (!(ip_addr->state & FBNIC_TCAM_S_UPDATE))
|
||||
continue;
|
||||
|
||||
if (ip_addr->state == FBNIC_TCAM_S_DELETE) {
|
||||
fbnic_clear_ip_dst_entry(fbd, idx);
|
||||
memset(ip_addr, 0, sizeof(*ip_addr));
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
fbnic_write_ip_dst_entry(fbd, idx, ip_addr);
|
||||
|
||||
ip_addr->state = FBNIC_TCAM_S_VALID;
|
||||
}
|
||||
|
||||
for (idx = ARRAY_SIZE(fbd->ipo_src); idx--;) {
|
||||
struct fbnic_ip_addr *ip_addr = &fbd->ipo_src[idx];
|
||||
|
||||
if (!(ip_addr->state & FBNIC_TCAM_S_UPDATE))
|
||||
continue;
|
||||
|
||||
if (ip_addr->state == FBNIC_TCAM_S_DELETE) {
|
||||
fbnic_clear_ip_outer_src_entry(fbd, idx);
|
||||
memset(ip_addr, 0, sizeof(*ip_addr));
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
fbnic_write_ip_outer_src_entry(fbd, idx, ip_addr);
|
||||
|
||||
ip_addr->state = FBNIC_TCAM_S_VALID;
|
||||
}
|
||||
|
||||
for (idx = ARRAY_SIZE(fbd->ipo_dst); idx--;) {
|
||||
struct fbnic_ip_addr *ip_addr = &fbd->ipo_dst[idx];
|
||||
|
||||
if (!(ip_addr->state & FBNIC_TCAM_S_UPDATE))
|
||||
continue;
|
||||
|
||||
if (ip_addr->state == FBNIC_TCAM_S_DELETE) {
|
||||
fbnic_clear_ip_outer_dst_entry(fbd, idx);
|
||||
memset(ip_addr, 0, sizeof(*ip_addr));
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
fbnic_write_ip_outer_dst_entry(fbd, idx, ip_addr);
|
||||
|
||||
ip_addr->state = FBNIC_TCAM_S_VALID;
|
||||
}
|
||||
}
|
||||
|
||||
void fbnic_clear_rules(struct fbnic_dev *fbd)
|
||||
{
|
||||
u32 dest = FIELD_PREP(FBNIC_RPC_ACT_TBL0_DEST_MASK,
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@
|
|||
#include <uapi/linux/in6.h>
|
||||
#include <linux/bitfield.h>
|
||||
|
||||
struct in_addr;
|
||||
|
||||
/* The TCAM state definitions follow an expected ordering.
|
||||
* They start out disabled, then move through the following states:
|
||||
* Disabled 0 -> Add 2
|
||||
|
|
@ -32,6 +34,12 @@ enum {
|
|||
#define FBNIC_RPC_TCAM_MACDA_WORD_LEN 3
|
||||
#define FBNIC_RPC_TCAM_MACDA_NUM_ENTRIES 32
|
||||
|
||||
/* 8 IPSRC and IPDST TCAM Entries each
|
||||
* 8 registers, Validate each
|
||||
*/
|
||||
#define FBNIC_RPC_TCAM_IP_ADDR_WORD_LEN 8
|
||||
#define FBNIC_RPC_TCAM_IP_ADDR_NUM_ENTRIES 8
|
||||
|
||||
#define FBNIC_RPC_TCAM_ACT_WORD_LEN 11
|
||||
#define FBNIC_RPC_TCAM_ACT_NUM_ENTRIES 64
|
||||
|
||||
|
|
@ -47,6 +55,13 @@ struct fbnic_mac_addr {
|
|||
DECLARE_BITMAP(act_tcam, FBNIC_RPC_TCAM_ACT_NUM_ENTRIES);
|
||||
};
|
||||
|
||||
struct fbnic_ip_addr {
|
||||
struct in6_addr mask, value;
|
||||
unsigned char version;
|
||||
unsigned char state;
|
||||
DECLARE_BITMAP(act_tcam, FBNIC_RPC_TCAM_ACT_NUM_ENTRIES);
|
||||
};
|
||||
|
||||
struct fbnic_act_tcam {
|
||||
struct {
|
||||
u16 tcam[FBNIC_RPC_TCAM_ACT_WORD_LEN];
|
||||
|
|
@ -81,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.
|
||||
|
|
@ -88,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.
|
||||
|
|
@ -168,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,
|
||||
|
|
@ -177,6 +201,17 @@ struct fbnic_mac_addr *__fbnic_mc_sync(struct fbnic_dev *fbd,
|
|||
void fbnic_sift_macda(struct fbnic_dev *fbd);
|
||||
void fbnic_write_macda(struct fbnic_dev *fbd);
|
||||
|
||||
struct fbnic_ip_addr *__fbnic_ip4_sync(struct fbnic_dev *fbd,
|
||||
struct fbnic_ip_addr *ip_addr,
|
||||
const struct in_addr *addr,
|
||||
const struct in_addr *mask);
|
||||
struct fbnic_ip_addr *__fbnic_ip6_sync(struct fbnic_dev *fbd,
|
||||
struct fbnic_ip_addr *ip_addr,
|
||||
const struct in6_addr *addr,
|
||||
const struct in6_addr *mask);
|
||||
int __fbnic_ip_unsync(struct fbnic_ip_addr *ip_addr, unsigned int tcam_idx);
|
||||
void fbnic_write_ip_addr(struct fbnic_dev *fbd);
|
||||
|
||||
static inline int __fbnic_uc_unsync(struct fbnic_mac_addr *mac_addr)
|
||||
{
|
||||
return __fbnic_xc_unsync(mac_addr, FBNIC_MAC_ADDR_T_UNICAST);
|
||||
|
|
|
|||
|
|
@ -993,10 +993,14 @@ static noinline_for_stack int ethtool_set_rxnfc(struct net_device *dev,
|
|||
return rc;
|
||||
|
||||
/* Nonzero ring with RSS only makes sense if NIC adds them together */
|
||||
if (cmd == ETHTOOL_SRXCLSRLINS && info.fs.flow_type & FLOW_RSS &&
|
||||
!ops->cap_rss_rxnfc_adds &&
|
||||
ethtool_get_flow_spec_ring(info.fs.ring_cookie))
|
||||
return -EINVAL;
|
||||
if (cmd == ETHTOOL_SRXCLSRLINS && info.fs.flow_type & FLOW_RSS) {
|
||||
if (!ops->cap_rss_rxnfc_adds &&
|
||||
ethtool_get_flow_spec_ring(info.fs.ring_cookie))
|
||||
return -EINVAL;
|
||||
|
||||
if (!xa_load(&dev->ethtool->rss_ctx, info.rss_context))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (cmd == ETHTOOL_SRXFH && ops->get_rxfh) {
|
||||
struct ethtool_rxfh_param rxfh = {};
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@
|
|||
import datetime
|
||||
import random
|
||||
import re
|
||||
from lib.py import ksft_run, ksft_pr, ksft_exit, ksft_eq, ksft_ne, ksft_ge, ksft_lt, ksft_true
|
||||
from lib.py import ksft_run, ksft_pr, ksft_exit
|
||||
from lib.py import ksft_eq, ksft_ne, ksft_ge, ksft_in, ksft_lt, ksft_true, ksft_raises
|
||||
from lib.py import NetDrvEpEnv
|
||||
from lib.py import EthtoolFamily, NetdevFamily
|
||||
from lib.py import KsftSkipEx, KsftFailEx
|
||||
|
|
@ -58,6 +59,14 @@ def require_ntuple(cfg):
|
|||
raise KsftSkipEx("Ntuple filters not enabled on the device: " + str(features["ntuple-filters"]))
|
||||
|
||||
|
||||
def require_context_cnt(cfg, need_cnt):
|
||||
# There's no good API to get the context count, so the tests
|
||||
# which try to add a lot opportunisitically set the count they
|
||||
# discovered. Careful with test ordering!
|
||||
if need_cnt and cfg.context_cnt and cfg.context_cnt < need_cnt:
|
||||
raise KsftSkipEx(f"Test requires at least {need_cnt} contexts, but device only has {cfg.context_cnt}")
|
||||
|
||||
|
||||
# Get Rx packet counts for all queues, as a simple list of integers
|
||||
# if @prev is specified the prev counts will be subtracted
|
||||
def _get_rx_cnts(cfg, prev=None):
|
||||
|
|
@ -456,6 +465,8 @@ def test_rss_context(cfg, ctx_cnt=1, create_with_cfg=None):
|
|||
raise
|
||||
ksft_pr(f"Failed to create context {i + 1}, trying to test what we got")
|
||||
ctx_cnt = i
|
||||
if cfg.context_cnt is None:
|
||||
cfg.context_cnt = ctx_cnt
|
||||
break
|
||||
|
||||
_rss_key_check(cfg, context=ctx_id)
|
||||
|
|
@ -511,8 +522,7 @@ def test_rss_context_out_of_order(cfg, ctx_cnt=4):
|
|||
"""
|
||||
|
||||
require_ntuple(cfg)
|
||||
|
||||
requested_ctx_cnt = ctx_cnt
|
||||
require_context_cnt(cfg, 4)
|
||||
|
||||
# Try to allocate more queues when necessary
|
||||
qcnt = len(_get_rx_cnts(cfg))
|
||||
|
|
@ -577,9 +587,6 @@ def test_rss_context_out_of_order(cfg, ctx_cnt=4):
|
|||
remove_ctx(-1)
|
||||
check_traffic()
|
||||
|
||||
if requested_ctx_cnt != ctx_cnt:
|
||||
raise KsftSkipEx(f"Tested only {ctx_cnt} contexts, wanted {requested_ctx_cnt}")
|
||||
|
||||
|
||||
def test_rss_context_overlap(cfg, other_ctx=0):
|
||||
"""
|
||||
|
|
@ -588,6 +595,8 @@ def test_rss_context_overlap(cfg, other_ctx=0):
|
|||
"""
|
||||
|
||||
require_ntuple(cfg)
|
||||
if other_ctx:
|
||||
require_context_cnt(cfg, 2)
|
||||
|
||||
queue_cnt = len(_get_rx_cnts(cfg))
|
||||
if queue_cnt < 4:
|
||||
|
|
@ -649,6 +658,29 @@ def test_rss_context_overlap2(cfg):
|
|||
test_rss_context_overlap(cfg, True)
|
||||
|
||||
|
||||
def test_flow_add_context_missing(cfg):
|
||||
"""
|
||||
Test that we are not allowed to add a rule pointing to an RSS context
|
||||
which was never created.
|
||||
"""
|
||||
|
||||
require_ntuple(cfg)
|
||||
|
||||
# Find a context which doesn't exist
|
||||
for ctx_id in range(1, 100):
|
||||
try:
|
||||
get_rss(cfg, context=ctx_id)
|
||||
except CmdExitFailure:
|
||||
break
|
||||
|
||||
with ksft_raises(CmdExitFailure) as cm:
|
||||
flow = f"flow-type tcp{cfg.addr_ipver} dst-ip {cfg.addr} dst-port 1234 context {ctx_id}"
|
||||
ntuple_id = ethtool_create(cfg, "-N", flow)
|
||||
ethtool(f"-N {cfg.ifname} delete {ntuple_id}")
|
||||
if cm.exception:
|
||||
ksft_in('Invalid argument', cm.exception.cmd.stderr)
|
||||
|
||||
|
||||
def test_delete_rss_context_busy(cfg):
|
||||
"""
|
||||
Test that deletion returns -EBUSY when an rss context is being used
|
||||
|
|
@ -717,6 +749,7 @@ def test_rss_ntuple_addition(cfg):
|
|||
|
||||
def main() -> None:
|
||||
with NetDrvEpEnv(__file__, nsim_test=False) as cfg:
|
||||
cfg.context_cnt = None
|
||||
cfg.ethnl = EthtoolFamily()
|
||||
cfg.netdevnl = NetdevFamily()
|
||||
|
||||
|
|
@ -726,6 +759,7 @@ def main() -> None:
|
|||
test_rss_context_dump, test_rss_context_queue_reconfigure,
|
||||
test_rss_context_overlap, test_rss_context_overlap2,
|
||||
test_rss_context_out_of_order, test_rss_context4_create_with_cfg,
|
||||
test_flow_add_context_missing,
|
||||
test_delete_rss_context_busy, test_rss_ntuple_addition],
|
||||
args=(cfg, ))
|
||||
ksft_exit()
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user