Commit 4bfbe53f authored by David S. Miller's avatar David S. Miller

Merge branch 'cxgb4-add-hash-filter-support-to-tc-flower-offload'

Rahul Lakkireddy says:

====================
cxgb4: add hash-filter support to tc-flower offload

This series of patches add support to create hash-filters; a.k.a
exact-match filters, to tc-flower offload.  T6 supports creating
~500K hash-filters in hw and can theoretically be expanded up to
~1 million.

Patch 1 fetches and saves the configured hw filter tuple field shifts
and filter mask.

Patch 2 initializes the driver to use hash-filter configuration.

Patch 3 adds support to create hash filters in hw.

Patch 4 adds support to delete hash filters in hw.

Patch 5 adds support to retrieve filter stats for hash filters.

Patch 6 converts the flower table to use rhashtable instead of
static hlist.

Patch 7 finally adds support to create hash filters via tc-flower
offload.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 1f2c897c 3eb8b62d
......@@ -287,10 +287,18 @@ struct tp_params {
* places we store their offsets here, or a -1 if the field isn't
* present.
*/
int vlan_shift;
int vnic_shift;
int fcoe_shift;
int port_shift;
int vnic_shift;
int vlan_shift;
int tos_shift;
int protocol_shift;
int ethertype_shift;
int macmatch_shift;
int matchtype_shift;
int frag_shift;
u64 hash_filter_mask;
};
struct vpd_params {
......@@ -358,6 +366,7 @@ struct adapter_params {
unsigned char crypto; /* HW capability for crypto */
unsigned char bypass;
unsigned char hash_filter;
unsigned int ofldq_wr_cred;
bool ulptx_memwrite_dsgl; /* use of T5 DSGL allowed */
......@@ -909,8 +918,10 @@ struct adapter {
struct chcr_stats_debug chcr_stats;
/* TC flower offload */
DECLARE_HASHTABLE(flower_anymatch_tbl, 9);
struct rhashtable flower_tbl;
struct rhashtable_params flower_ht_params;
struct timer_list flower_stats_timer;
struct work_struct flower_stats_work;
/* Ethtool Dump */
struct ethtool_dump eth_dump;
......@@ -1041,6 +1052,7 @@ struct ch_filter_specification {
* matching that doesn't exist as a (value, mask) tuple.
*/
uint32_t type:1; /* 0 => IPv4, 1 => IPv6 */
u32 hash:1; /* 0 => wild-card, 1 => exact-match */
/* Packet dispatch information. Ingress packets which match the
* filter rules will be dropped, passed to the host or switched back
......@@ -1098,7 +1110,14 @@ enum {
};
enum {
NAT_MODE_ALL = 7, /* NAT on entire 4-tuple */
NAT_MODE_NONE = 0, /* No NAT performed */
NAT_MODE_DIP, /* NAT on Dst IP */
NAT_MODE_DIP_DP, /* NAT on Dst IP, Dst Port */
NAT_MODE_DIP_DP_SIP, /* NAT on Dst IP, Dst Port and Src IP */
NAT_MODE_DIP_DP_SP, /* NAT on Dst IP, Dst Port and Src Port */
NAT_MODE_SIP_SP, /* NAT on Src IP and Src Port */
NAT_MODE_DIP_SIP_SP, /* NAT on Dst IP, Src IP and Src Port */
NAT_MODE_ALL /* NAT on entire 4-tuple */
};
/* Host shadow copy of ingress filter entry. This is in host native format
......@@ -1132,6 +1151,11 @@ static inline int is_offload(const struct adapter *adap)
return adap->params.offload;
}
static inline int is_hashfilter(const struct adapter *adap)
{
return adap->params.hash_filter;
}
static inline int is_pci_uld(const struct adapter *adap)
{
return adap->params.crypto;
......
......@@ -37,7 +37,12 @@
#include "t4_msg.h"
#define WORD_MASK 0xffffffff
void filter_rpl(struct adapter *adap, const struct cpl_set_tcb_rpl *rpl);
void hash_filter_rpl(struct adapter *adap, const struct cpl_act_open_rpl *rpl);
void hash_del_filter_rpl(struct adapter *adap,
const struct cpl_abort_rpl_rss *rpl);
void clear_filter(struct adapter *adap, struct filter_entry *f);
int set_filter_wr(struct adapter *adapter, int fidx);
......@@ -45,4 +50,7 @@ int delete_filter(struct adapter *adapter, unsigned int fidx);
int writable_filter(struct filter_entry *f);
void clear_all_filters(struct adapter *adapter);
int init_hash_filter(struct adapter *adap);
bool is_filter_exact_match(struct adapter *adap,
struct ch_filter_specification *fs);
#endif /* __CXGB4_FILTER_H */
......@@ -572,6 +572,14 @@ static int fwevtq_handler(struct sge_rspq *q, const __be64 *rsp,
const struct cpl_set_tcb_rpl *p = (void *)rsp;
filter_rpl(q->adap, p);
} else if (opcode == CPL_ACT_OPEN_RPL) {
const struct cpl_act_open_rpl *p = (void *)rsp;
hash_filter_rpl(q->adap, p);
} else if (opcode == CPL_ABORT_RPL_RSS) {
const struct cpl_abort_rpl_rss *p = (void *)rsp;
hash_del_filter_rpl(q->adap, p);
} else
dev_err(q->adap->pdev_dev,
"unexpected CPL %#x on FW event queue\n", opcode);
......@@ -3963,7 +3971,8 @@ static int adap_init0(struct adapter *adap)
if (ret < 0)
goto bye;
if (caps_cmd.ofldcaps) {
if (caps_cmd.ofldcaps ||
(caps_cmd.niccaps & htons(FW_CAPS_CONFIG_NIC_HASHFILTER))) {
/* query offload-related parameters */
params[0] = FW_PARAM_DEV(NTID);
params[1] = FW_PARAM_PFVF(SERVER_START);
......@@ -4000,8 +4009,13 @@ static int adap_init0(struct adapter *adap)
adap->vres.ddp.size = val[4] - val[3] + 1;
adap->params.ofldq_wr_cred = val[5];
adap->params.offload = 1;
adap->num_ofld_uld += 1;
if (caps_cmd.niccaps & htons(FW_CAPS_CONFIG_NIC_HASHFILTER)) {
if (init_hash_filter(adap) < 0)
goto bye;
} else {
adap->params.offload = 1;
adap->num_ofld_uld += 1;
}
}
if (caps_cmd.rdmacaps) {
params[0] = FW_PARAM_PFVF(STAG_START);
......@@ -5168,10 +5182,12 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
dev_warn(&pdev->dev,
"could not offload tc u32, continuing\n");
cxgb4_init_tc_flower(adapter);
if (cxgb4_init_tc_flower(adapter))
dev_warn(&pdev->dev,
"could not offload tc flower, continuing\n");
}
if (is_offload(adapter)) {
if (is_offload(adapter) || is_hashfilter(adapter)) {
if (t4_read_reg(adapter, LE_DB_CONFIG_A) & HASHEN_F) {
u32 hash_base, hash_reg;
......
......@@ -38,6 +38,7 @@
#include <net/tc_act/tc_vlan.h>
#include "cxgb4.h"
#include "cxgb4_filter.h"
#include "cxgb4_tc_flower.h"
#define STATS_CHECK_PERIOD (HZ / 2)
......@@ -74,13 +75,8 @@ static struct ch_tc_flower_entry *allocate_flower_entry(void)
static struct ch_tc_flower_entry *ch_flower_lookup(struct adapter *adap,
unsigned long flower_cookie)
{
struct ch_tc_flower_entry *flower_entry;
hash_for_each_possible_rcu(adap->flower_anymatch_tbl, flower_entry,
link, flower_cookie)
if (flower_entry->tc_flower_cookie == flower_cookie)
return flower_entry;
return NULL;
return rhashtable_lookup_fast(&adap->flower_tbl, &flower_cookie,
adap->flower_ht_params);
}
static void cxgb4_process_flow_match(struct net_device *dev,
......@@ -677,11 +673,16 @@ int cxgb4_tc_flower_replace(struct net_device *dev,
cxgb4_process_flow_match(dev, cls, fs);
cxgb4_process_flow_actions(dev, cls, fs);
fidx = cxgb4_get_free_ftid(dev, fs->type ? PF_INET6 : PF_INET);
if (fidx < 0) {
netdev_err(dev, "%s: No fidx for offload.\n", __func__);
ret = -ENOMEM;
goto free_entry;
fs->hash = is_filter_exact_match(adap, fs);
if (fs->hash) {
fidx = 0;
} else {
fidx = cxgb4_get_free_ftid(dev, fs->type ? PF_INET6 : PF_INET);
if (fidx < 0) {
netdev_err(dev, "%s: No fidx for offload.\n", __func__);
ret = -ENOMEM;
goto free_entry;
}
}
init_completion(&ctx.completion);
......@@ -707,12 +708,17 @@ int cxgb4_tc_flower_replace(struct net_device *dev,
goto free_entry;
}
INIT_HLIST_NODE(&ch_flower->link);
ch_flower->tc_flower_cookie = cls->cookie;
ch_flower->filter_id = ctx.tid;
hash_add_rcu(adap->flower_anymatch_tbl, &ch_flower->link, cls->cookie);
ret = rhashtable_insert_fast(&adap->flower_tbl, &ch_flower->node,
adap->flower_ht_params);
if (ret)
goto del_filter;
return ret;
return 0;
del_filter:
cxgb4_del_filter(dev, ch_flower->filter_id, &ch_flower->fs);
free_entry:
kfree(ch_flower);
......@@ -730,47 +736,70 @@ int cxgb4_tc_flower_destroy(struct net_device *dev,
if (!ch_flower)
return -ENOENT;
ret = cxgb4_del_filter(dev, ch_flower->filter_id);
ret = cxgb4_del_filter(dev, ch_flower->filter_id, &ch_flower->fs);
if (ret)
goto err;
hash_del_rcu(&ch_flower->link);
ret = rhashtable_remove_fast(&adap->flower_tbl, &ch_flower->node,
adap->flower_ht_params);
if (ret) {
netdev_err(dev, "Flow remove from rhashtable failed");
goto err;
}
kfree_rcu(ch_flower, rcu);
err:
return ret;
}
static void ch_flower_stats_cb(struct timer_list *t)
static void ch_flower_stats_handler(struct work_struct *work)
{
struct adapter *adap = from_timer(adap, t, flower_stats_timer);
struct adapter *adap = container_of(work, struct adapter,
flower_stats_work);
struct ch_tc_flower_entry *flower_entry;
struct ch_tc_flower_stats *ofld_stats;
unsigned int i;
struct rhashtable_iter iter;
u64 packets;
u64 bytes;
int ret;
rcu_read_lock();
hash_for_each_rcu(adap->flower_anymatch_tbl, i, flower_entry, link) {
ret = cxgb4_get_filter_counters(adap->port[0],
flower_entry->filter_id,
&packets, &bytes);
if (!ret) {
spin_lock(&flower_entry->lock);
ofld_stats = &flower_entry->stats;
if (ofld_stats->prev_packet_count != packets) {
ofld_stats->prev_packet_count = packets;
ofld_stats->last_used = jiffies;
rhashtable_walk_enter(&adap->flower_tbl, &iter);
do {
flower_entry = ERR_PTR(rhashtable_walk_start(&iter));
if (IS_ERR(flower_entry))
goto walk_stop;
while ((flower_entry = rhashtable_walk_next(&iter)) &&
!IS_ERR(flower_entry)) {
ret = cxgb4_get_filter_counters(adap->port[0],
flower_entry->filter_id,
&packets, &bytes,
flower_entry->fs.hash);
if (!ret) {
spin_lock(&flower_entry->lock);
ofld_stats = &flower_entry->stats;
if (ofld_stats->prev_packet_count != packets) {
ofld_stats->prev_packet_count = packets;
ofld_stats->last_used = jiffies;
}
spin_unlock(&flower_entry->lock);
}
spin_unlock(&flower_entry->lock);
}
}
rcu_read_unlock();
walk_stop:
rhashtable_walk_stop(&iter);
} while (flower_entry == ERR_PTR(-EAGAIN));
rhashtable_walk_exit(&iter);
mod_timer(&adap->flower_stats_timer, jiffies + STATS_CHECK_PERIOD);
}
static void ch_flower_stats_cb(struct timer_list *t)
{
struct adapter *adap = from_timer(adap, t, flower_stats_timer);
schedule_work(&adap->flower_stats_work);
}
int cxgb4_tc_flower_stats(struct net_device *dev,
struct tc_cls_flower_offload *cls)
{
......@@ -788,7 +817,8 @@ int cxgb4_tc_flower_stats(struct net_device *dev,
}
ret = cxgb4_get_filter_counters(dev, ch_flower->filter_id,
&packets, &bytes);
&packets, &bytes,
ch_flower->fs.hash);
if (ret < 0)
goto err;
......@@ -812,15 +842,35 @@ int cxgb4_tc_flower_stats(struct net_device *dev,
return ret;
}
void cxgb4_init_tc_flower(struct adapter *adap)
static const struct rhashtable_params cxgb4_tc_flower_ht_params = {
.nelem_hint = 384,
.head_offset = offsetof(struct ch_tc_flower_entry, node),
.key_offset = offsetof(struct ch_tc_flower_entry, tc_flower_cookie),
.key_len = sizeof(((struct ch_tc_flower_entry *)0)->tc_flower_cookie),
.max_size = 524288,
.min_size = 512,
.automatic_shrinking = true
};
int cxgb4_init_tc_flower(struct adapter *adap)
{
hash_init(adap->flower_anymatch_tbl);
int ret;
adap->flower_ht_params = cxgb4_tc_flower_ht_params;
ret = rhashtable_init(&adap->flower_tbl, &adap->flower_ht_params);
if (ret)
return ret;
INIT_WORK(&adap->flower_stats_work, ch_flower_stats_handler);
timer_setup(&adap->flower_stats_timer, ch_flower_stats_cb, 0);
mod_timer(&adap->flower_stats_timer, jiffies + STATS_CHECK_PERIOD);
return 0;
}
void cxgb4_cleanup_tc_flower(struct adapter *adap)
{
if (adap->flower_stats_timer.function)
del_timer_sync(&adap->flower_stats_timer);
cancel_work_sync(&adap->flower_stats_work);
rhashtable_destroy(&adap->flower_tbl);
}
......@@ -48,7 +48,7 @@ struct ch_tc_flower_entry {
struct ch_filter_specification fs;
struct ch_tc_flower_stats stats;
unsigned long tc_flower_cookie;
struct hlist_node link;
struct rhash_head node;
struct rcu_head rcu;
spinlock_t lock; /* lock for stats */
u32 filter_id;
......@@ -115,6 +115,6 @@ int cxgb4_tc_flower_destroy(struct net_device *dev,
int cxgb4_tc_flower_stats(struct net_device *dev,
struct tc_cls_flower_offload *cls);
void cxgb4_init_tc_flower(struct adapter *adap);
int cxgb4_init_tc_flower(struct adapter *adap);
void cxgb4_cleanup_tc_flower(struct adapter *adap);
#endif /* __CXGB4_TC_FLOWER_H */
......@@ -380,7 +380,7 @@ int cxgb4_delete_knode(struct net_device *dev, struct tc_cls_u32_offload *cls)
return -EINVAL;
}
ret = cxgb4_del_filter(dev, filter_id);
ret = cxgb4_del_filter(dev, filter_id, NULL);
if (ret)
goto out;
......@@ -399,7 +399,7 @@ int cxgb4_delete_knode(struct net_device *dev, struct tc_cls_u32_offload *cls)
if (!test_bit(j, link->tid_map))
continue;
ret = __cxgb4_del_filter(dev, j, NULL);
ret = __cxgb4_del_filter(dev, j, NULL, NULL);
if (ret)
goto out;
......
......@@ -217,12 +217,14 @@ int __cxgb4_set_filter(struct net_device *dev, int filter_id,
struct ch_filter_specification *fs,
struct filter_ctx *ctx);
int __cxgb4_del_filter(struct net_device *dev, int filter_id,
struct ch_filter_specification *fs,
struct filter_ctx *ctx);
int cxgb4_set_filter(struct net_device *dev, int filter_id,
struct ch_filter_specification *fs);
int cxgb4_del_filter(struct net_device *dev, int filter_id);
int cxgb4_del_filter(struct net_device *dev, int filter_id,
struct ch_filter_specification *fs);
int cxgb4_get_filter_counters(struct net_device *dev, unsigned int fidx,
u64 *hitcnt, u64 *bytecnt);
u64 *hitcnt, u64 *bytecnt, bool hash);
static inline void set_wr_txq(struct sk_buff *skb, int prio, int queue)
{
......
......@@ -8816,11 +8816,21 @@ int t4_init_tp_params(struct adapter *adap, bool sleep_ok)
* shift positions of several elements of the Compressed Filter Tuple
* for this adapter which we need frequently ...
*/
adap->params.tp.vlan_shift = t4_filter_field_shift(adap, VLAN_F);
adap->params.tp.vnic_shift = t4_filter_field_shift(adap, VNIC_ID_F);
adap->params.tp.fcoe_shift = t4_filter_field_shift(adap, FCOE_F);
adap->params.tp.port_shift = t4_filter_field_shift(adap, PORT_F);
adap->params.tp.vnic_shift = t4_filter_field_shift(adap, VNIC_ID_F);
adap->params.tp.vlan_shift = t4_filter_field_shift(adap, VLAN_F);
adap->params.tp.tos_shift = t4_filter_field_shift(adap, TOS_F);
adap->params.tp.protocol_shift = t4_filter_field_shift(adap,
PROTOCOL_F);
adap->params.tp.ethertype_shift = t4_filter_field_shift(adap,
ETHERTYPE_F);
adap->params.tp.macmatch_shift = t4_filter_field_shift(adap,
MACMATCH_F);
adap->params.tp.matchtype_shift = t4_filter_field_shift(adap,
MPSHITTYPE_F);
adap->params.tp.frag_shift = t4_filter_field_shift(adap,
FRAGMENTATION_F);
/* If TP_INGRESS_CONFIG.VNID == 0, then TP_VLAN_PRI_MAP.VNIC_ID
* represents the presence of an Outer VLAN instead of a VNIC ID.
......@@ -8828,6 +8838,10 @@ int t4_init_tp_params(struct adapter *adap, bool sleep_ok)
if ((adap->params.tp.ingress_config & VNIC_F) == 0)
adap->params.tp.vnic_shift = -1;
v = t4_read_reg(adap, LE_3_DB_HASH_MASK_GEN_IPV4_T6_A);
adap->params.tp.hash_filter_mask = v;
v = t4_read_reg(adap, LE_4_DB_HASH_MASK_GEN_IPV4_T6_A);
adap->params.tp.hash_filter_mask |= ((u64)v << 32);
return 0;
}
......
......@@ -286,6 +286,7 @@ struct work_request_hdr {
#define RX_CHANNEL_S 26
#define RX_CHANNEL_V(x) ((x) << RX_CHANNEL_S)
#define RX_CHANNEL_F RX_CHANNEL_V(1U)
#define WND_SCALE_EN_S 28
#define WND_SCALE_EN_V(x) ((x) << WND_SCALE_EN_S)
......@@ -315,6 +316,10 @@ struct cpl_pass_open_req {
#define DELACK_V(x) ((x) << DELACK_S)
#define DELACK_F DELACK_V(1U)
#define NON_OFFLOAD_S 7
#define NON_OFFLOAD_V(x) ((x) << NON_OFFLOAD_S)
#define NON_OFFLOAD_F NON_OFFLOAD_V(1U)
#define DSCP_S 22
#define DSCP_M 0x3F
#define DSCP_V(x) ((x) << DSCP_S)
......
......@@ -2933,6 +2933,23 @@
#define SSRAMINTPERR_V(x) ((x) << SSRAMINTPERR_S)
#define SSRAMINTPERR_F SSRAMINTPERR_V(1U)
#define LE_DB_RSP_CODE_0_A 0x19c74
#define TCAM_ACTV_HIT_S 0
#define TCAM_ACTV_HIT_M 0x1fU
#define TCAM_ACTV_HIT_V(x) ((x) << TCAM_ACTV_HIT_S)
#define TCAM_ACTV_HIT_G(x) (((x) >> TCAM_ACTV_HIT_S) & TCAM_ACTV_HIT_M)
#define LE_DB_RSP_CODE_1_A 0x19c78
#define HASH_ACTV_HIT_S 25
#define HASH_ACTV_HIT_M 0x1fU
#define HASH_ACTV_HIT_V(x) ((x) << HASH_ACTV_HIT_S)
#define HASH_ACTV_HIT_G(x) (((x) >> HASH_ACTV_HIT_S) & HASH_ACTV_HIT_M)
#define LE_3_DB_HASH_MASK_GEN_IPV4_T6_A 0x19eac
#define LE_4_DB_HASH_MASK_GEN_IPV4_T6_A 0x19eb0
#define NCSI_INT_CAUSE_A 0x1a0d8
#define CIM_DM_PRTY_ERR_S 8
......
......@@ -42,6 +42,28 @@
#define TCB_T_FLAGS_W 1
#define TF_CCTRL_ECE_S 60
#define TF_CCTRL_CWR_S 61
#define TF_CCTRL_RFR_S 62
#define TCB_RSS_INFO_W 3
#define TCB_RSS_INFO_S 0
#define TCB_RSS_INFO_M 0x3ffULL
#define TCB_RSS_INFO_V(x) ((x) << TCB_RSS_INFO_S)
#define TCB_TIMESTAMP_W 5
#define TCB_TIMESTAMP_S 0
#define TCB_TIMESTAMP_M 0xffffffffULL
#define TCB_TIMESTAMP_V(x) ((x) << TCB_TIMESTAMP_S)
#define TCB_RTT_TS_RECENT_AGE_W 6
#define TCB_RTT_TS_RECENT_AGE_S 0
#define TCB_RTT_TS_RECENT_AGE_M 0xffffffffULL
#define TCB_RTT_TS_RECENT_AGE_V(x) ((x) << TCB_RTT_TS_RECENT_AGE_S)
#define TCB_SND_UNA_RAW_W 10
#define TCB_RX_FRAG2_PTR_RAW_W 27
#define TCB_RX_FRAG3_LEN_RAW_W 29
#define TCB_RX_FRAG3_START_IDX_OFFSET_RAW_W 30
#define TCB_PDU_HDR_LEN_W 31
#endif /* __T4_TCB_H */
......@@ -1092,6 +1092,7 @@ enum fw_caps_config_switch {
enum fw_caps_config_nic {
FW_CAPS_CONFIG_NIC = 0x00000001,
FW_CAPS_CONFIG_NIC_VM = 0x00000002,
FW_CAPS_CONFIG_NIC_HASHFILTER = 0x00000020,
};
enum fw_caps_config_ofld {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment