aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2018-10-11 01:32:45 -0400
committerDavid S. Miller <davem@davemloft.net>2018-10-11 01:32:45 -0400
commit1a21cc507bb75954bd8618f193179e2a3f8d9069 (patch)
tree217eb7856b602cbd597a960cf376f34d2c62ae2b
parent7579d84be12c4645672deeac5bb0eace7dbd6cb5 (diff)
parent12ecf61529dc51b4dd03a15bed34c7d317c137ae (diff)
Merge branch 'nfp-flower-speed-up-stats-update-loop'
Jakub Kicinski says: ==================== nfp: flower: speed up stats update loop This set from Pieter improves performance of processing FW stats update notifications. The FW seems to send those at relatively high rate (roughly ten per second per flow), therefore if we want to approach the million flows mark we have to be very careful about our data structures. We tried rhashtable for stat updates, but according to our experiments rhashtable lookup on a u32 takes roughly 60ns on an Xeon E5-2670 v3. Which translate to a hard limit of 16M lookups per second on this CPU, and, according to perf record jhash and memcmp account for 60% of CPU usage on the core handling the updates. Given that our statistic IDs are already array indices, and considering each statistic is only 24B in size, we decided to forego the use of hashtables and use a directly indexed array. The CPU savings are considerable. With the recent improvements in TC core and with our own bottlenecks out of the way Pieter removes the artificial limit of 128 flows, and allows the driver to install as many flows as FW supports. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/net/ethernet/netronome/nfp/bpf/main.c5
-rw-r--r--drivers/net/ethernet/netronome/nfp/flower/main.c15
-rw-r--r--drivers/net/ethernet/netronome/nfp/flower/main.h23
-rw-r--r--drivers/net/ethernet/netronome/nfp/flower/metadata.c145
-rw-r--r--drivers/net/ethernet/netronome/nfp/flower/offload.c31
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfp_app.c5
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfp_app.h1
7 files changed, 148 insertions, 77 deletions
diff --git a/drivers/net/ethernet/netronome/nfp/bpf/main.c b/drivers/net/ethernet/netronome/nfp/bpf/main.c
index d9d37aa860e0..28af263d4577 100644
--- a/drivers/net/ethernet/netronome/nfp/bpf/main.c
+++ b/drivers/net/ethernet/netronome/nfp/bpf/main.c
@@ -509,11 +509,6 @@ err_free_bpf:
509 return err; 509 return err;
510} 510}
511 511
512static void nfp_check_rhashtable_empty(void *ptr, void *arg)
513{
514 WARN_ON_ONCE(1);
515}
516
517static void nfp_bpf_clean(struct nfp_app *app) 512static void nfp_bpf_clean(struct nfp_app *app)
518{ 513{
519 struct nfp_app_bpf *bpf = app->priv; 514 struct nfp_app_bpf *bpf = app->priv;
diff --git a/drivers/net/ethernet/netronome/nfp/flower/main.c b/drivers/net/ethernet/netronome/nfp/flower/main.c
index e57d23746585..3c54487186bf 100644
--- a/drivers/net/ethernet/netronome/nfp/flower/main.c
+++ b/drivers/net/ethernet/netronome/nfp/flower/main.c
@@ -518,8 +518,8 @@ err_clear_nn:
518static int nfp_flower_init(struct nfp_app *app) 518static int nfp_flower_init(struct nfp_app *app)
519{ 519{
520 const struct nfp_pf *pf = app->pf; 520 const struct nfp_pf *pf = app->pf;
521 u64 version, features, ctx_count;
521 struct nfp_flower_priv *app_priv; 522 struct nfp_flower_priv *app_priv;
522 u64 version, features;
523 int err; 523 int err;
524 524
525 if (!pf->eth_tbl) { 525 if (!pf->eth_tbl) {
@@ -543,6 +543,16 @@ static int nfp_flower_init(struct nfp_app *app)
543 return err; 543 return err;
544 } 544 }
545 545
546 ctx_count = nfp_rtsym_read_le(app->pf->rtbl, "CONFIG_FC_HOST_CTX_COUNT",
547 &err);
548 if (err) {
549 nfp_warn(app->cpp,
550 "FlowerNIC: unsupported host context count: %d\n",
551 err);
552 err = 0;
553 ctx_count = BIT(17);
554 }
555
546 /* We need to ensure hardware has enough flower capabilities. */ 556 /* We need to ensure hardware has enough flower capabilities. */
547 if (version != NFP_FLOWER_ALLOWED_VER) { 557 if (version != NFP_FLOWER_ALLOWED_VER) {
548 nfp_warn(app->cpp, "FlowerNIC: unsupported firmware version\n"); 558 nfp_warn(app->cpp, "FlowerNIC: unsupported firmware version\n");
@@ -553,6 +563,7 @@ static int nfp_flower_init(struct nfp_app *app)
553 if (!app_priv) 563 if (!app_priv)
554 return -ENOMEM; 564 return -ENOMEM;
555 565
566 app_priv->stats_ring_size = roundup_pow_of_two(ctx_count);
556 app->priv = app_priv; 567 app->priv = app_priv;
557 app_priv->app = app; 568 app_priv->app = app;
558 skb_queue_head_init(&app_priv->cmsg_skbs_high); 569 skb_queue_head_init(&app_priv->cmsg_skbs_high);
@@ -563,7 +574,7 @@ static int nfp_flower_init(struct nfp_app *app)
563 init_waitqueue_head(&app_priv->mtu_conf.wait_q); 574 init_waitqueue_head(&app_priv->mtu_conf.wait_q);
564 spin_lock_init(&app_priv->mtu_conf.lock); 575 spin_lock_init(&app_priv->mtu_conf.lock);
565 576
566 err = nfp_flower_metadata_init(app); 577 err = nfp_flower_metadata_init(app, ctx_count);
567 if (err) 578 if (err)
568 goto err_free_app_priv; 579 goto err_free_app_priv;
569 580
diff --git a/drivers/net/ethernet/netronome/nfp/flower/main.h b/drivers/net/ethernet/netronome/nfp/flower/main.h
index 81d941ab895c..fd92bda1c0fa 100644
--- a/drivers/net/ethernet/netronome/nfp/flower/main.h
+++ b/drivers/net/ethernet/netronome/nfp/flower/main.h
@@ -38,6 +38,7 @@
38 38
39#include <linux/circ_buf.h> 39#include <linux/circ_buf.h>
40#include <linux/hashtable.h> 40#include <linux/hashtable.h>
41#include <linux/rhashtable.h>
41#include <linux/time64.h> 42#include <linux/time64.h>
42#include <linux/types.h> 43#include <linux/types.h>
43#include <net/pkt_cls.h> 44#include <net/pkt_cls.h>
@@ -50,10 +51,8 @@ struct net_device;
50struct nfp_app; 51struct nfp_app;
51 52
52#define NFP_FL_STATS_CTX_DONT_CARE cpu_to_be32(0xffffffff) 53#define NFP_FL_STATS_CTX_DONT_CARE cpu_to_be32(0xffffffff)
53#define NFP_FL_STATS_ENTRY_RS BIT(20) 54#define NFP_FL_STATS_ELEM_RS FIELD_SIZEOF(struct nfp_fl_stats_id, \
54#define NFP_FL_STATS_ELEM_RS 4 55 init_unalloc)
55#define NFP_FL_REPEATED_HASH_MAX BIT(17)
56#define NFP_FLOWER_HASH_BITS 19
57#define NFP_FLOWER_MASK_ENTRY_RS 256 56#define NFP_FLOWER_MASK_ENTRY_RS 256
58#define NFP_FLOWER_MASK_ELEMENT_RS 1 57#define NFP_FLOWER_MASK_ELEMENT_RS 1
59#define NFP_FLOWER_MASK_HASH_BITS 10 58#define NFP_FLOWER_MASK_HASH_BITS 10
@@ -138,7 +137,10 @@ struct nfp_fl_lag {
138 * @stats_ids: List of free stats ids 137 * @stats_ids: List of free stats ids
139 * @mask_ids: List of free mask ids 138 * @mask_ids: List of free mask ids
140 * @mask_table: Hash table used to store masks 139 * @mask_table: Hash table used to store masks
140 * @stats_ring_size: Maximum number of allowed stats ids
141 * @flow_table: Hash table used to store flower rules 141 * @flow_table: Hash table used to store flower rules
142 * @stats: Stored stats updates for flower rules
143 * @stats_lock: Lock for flower rule stats updates
142 * @cmsg_work: Workqueue for control messages processing 144 * @cmsg_work: Workqueue for control messages processing
143 * @cmsg_skbs_high: List of higher priority skbs for control message 145 * @cmsg_skbs_high: List of higher priority skbs for control message
144 * processing 146 * processing
@@ -171,7 +173,10 @@ struct nfp_flower_priv {
171 struct nfp_fl_stats_id stats_ids; 173 struct nfp_fl_stats_id stats_ids;
172 struct nfp_fl_mask_id mask_ids; 174 struct nfp_fl_mask_id mask_ids;
173 DECLARE_HASHTABLE(mask_table, NFP_FLOWER_MASK_HASH_BITS); 175 DECLARE_HASHTABLE(mask_table, NFP_FLOWER_MASK_HASH_BITS);
174 DECLARE_HASHTABLE(flow_table, NFP_FLOWER_HASH_BITS); 176 u32 stats_ring_size;
177 struct rhashtable flow_table;
178 struct nfp_fl_stats *stats;
179 spinlock_t stats_lock; /* lock stats */
175 struct work_struct cmsg_work; 180 struct work_struct cmsg_work;
176 struct sk_buff_head cmsg_skbs_high; 181 struct sk_buff_head cmsg_skbs_high;
177 struct sk_buff_head cmsg_skbs_low; 182 struct sk_buff_head cmsg_skbs_low;
@@ -227,10 +232,8 @@ struct nfp_fl_stats {
227struct nfp_fl_payload { 232struct nfp_fl_payload {
228 struct nfp_fl_rule_metadata meta; 233 struct nfp_fl_rule_metadata meta;
229 unsigned long tc_flower_cookie; 234 unsigned long tc_flower_cookie;
230 struct hlist_node link; 235 struct rhash_head fl_node;
231 struct rcu_head rcu; 236 struct rcu_head rcu;
232 spinlock_t lock; /* lock stats */
233 struct nfp_fl_stats stats;
234 __be32 nfp_tun_ipv4_addr; 237 __be32 nfp_tun_ipv4_addr;
235 struct net_device *ingress_dev; 238 struct net_device *ingress_dev;
236 char *unmasked_data; 239 char *unmasked_data;
@@ -239,6 +242,8 @@ struct nfp_fl_payload {
239 bool ingress_offload; 242 bool ingress_offload;
240}; 243};
241 244
245extern const struct rhashtable_params nfp_flower_table_params;
246
242struct nfp_fl_stats_frame { 247struct nfp_fl_stats_frame {
243 __be32 stats_con_id; 248 __be32 stats_con_id;
244 __be32 pkt_count; 249 __be32 pkt_count;
@@ -246,7 +251,7 @@ struct nfp_fl_stats_frame {
246 __be64 stats_cookie; 251 __be64 stats_cookie;
247}; 252};
248 253
249int nfp_flower_metadata_init(struct nfp_app *app); 254int nfp_flower_metadata_init(struct nfp_app *app, u64 host_ctx_count);
250void nfp_flower_metadata_cleanup(struct nfp_app *app); 255void nfp_flower_metadata_cleanup(struct nfp_app *app);
251 256
252int nfp_flower_setup_tc(struct nfp_app *app, struct net_device *netdev, 257int nfp_flower_setup_tc(struct nfp_app *app, struct net_device *netdev,
diff --git a/drivers/net/ethernet/netronome/nfp/flower/metadata.c b/drivers/net/ethernet/netronome/nfp/flower/metadata.c
index c098730544b7..a4cce9a30830 100644
--- a/drivers/net/ethernet/netronome/nfp/flower/metadata.c
+++ b/drivers/net/ethernet/netronome/nfp/flower/metadata.c
@@ -48,6 +48,12 @@ struct nfp_mask_id_table {
48 u8 mask_id; 48 u8 mask_id;
49}; 49};
50 50
51struct nfp_fl_flow_table_cmp_arg {
52 struct net_device *netdev;
53 unsigned long cookie;
54 __be32 host_ctx;
55};
56
51static int nfp_release_stats_entry(struct nfp_app *app, u32 stats_context_id) 57static int nfp_release_stats_entry(struct nfp_app *app, u32 stats_context_id)
52{ 58{
53 struct nfp_flower_priv *priv = app->priv; 59 struct nfp_flower_priv *priv = app->priv;
@@ -55,14 +61,14 @@ static int nfp_release_stats_entry(struct nfp_app *app, u32 stats_context_id)
55 61
56 ring = &priv->stats_ids.free_list; 62 ring = &priv->stats_ids.free_list;
57 /* Check if buffer is full. */ 63 /* Check if buffer is full. */
58 if (!CIRC_SPACE(ring->head, ring->tail, NFP_FL_STATS_ENTRY_RS * 64 if (!CIRC_SPACE(ring->head, ring->tail,
59 NFP_FL_STATS_ELEM_RS - 65 priv->stats_ring_size * NFP_FL_STATS_ELEM_RS -
60 NFP_FL_STATS_ELEM_RS + 1)) 66 NFP_FL_STATS_ELEM_RS + 1))
61 return -ENOBUFS; 67 return -ENOBUFS;
62 68
63 memcpy(&ring->buf[ring->head], &stats_context_id, NFP_FL_STATS_ELEM_RS); 69 memcpy(&ring->buf[ring->head], &stats_context_id, NFP_FL_STATS_ELEM_RS);
64 ring->head = (ring->head + NFP_FL_STATS_ELEM_RS) % 70 ring->head = (ring->head + NFP_FL_STATS_ELEM_RS) %
65 (NFP_FL_STATS_ENTRY_RS * NFP_FL_STATS_ELEM_RS); 71 (priv->stats_ring_size * NFP_FL_STATS_ELEM_RS);
66 72
67 return 0; 73 return 0;
68} 74}
@@ -74,7 +80,7 @@ static int nfp_get_stats_entry(struct nfp_app *app, u32 *stats_context_id)
74 struct circ_buf *ring; 80 struct circ_buf *ring;
75 81
76 ring = &priv->stats_ids.free_list; 82 ring = &priv->stats_ids.free_list;
77 freed_stats_id = NFP_FL_STATS_ENTRY_RS; 83 freed_stats_id = priv->stats_ring_size;
78 /* Check for unallocated entries first. */ 84 /* Check for unallocated entries first. */
79 if (priv->stats_ids.init_unalloc > 0) { 85 if (priv->stats_ids.init_unalloc > 0) {
80 *stats_context_id = priv->stats_ids.init_unalloc - 1; 86 *stats_context_id = priv->stats_ids.init_unalloc - 1;
@@ -92,7 +98,7 @@ static int nfp_get_stats_entry(struct nfp_app *app, u32 *stats_context_id)
92 *stats_context_id = temp_stats_id; 98 *stats_context_id = temp_stats_id;
93 memcpy(&ring->buf[ring->tail], &freed_stats_id, NFP_FL_STATS_ELEM_RS); 99 memcpy(&ring->buf[ring->tail], &freed_stats_id, NFP_FL_STATS_ELEM_RS);
94 ring->tail = (ring->tail + NFP_FL_STATS_ELEM_RS) % 100 ring->tail = (ring->tail + NFP_FL_STATS_ELEM_RS) %
95 (NFP_FL_STATS_ENTRY_RS * NFP_FL_STATS_ELEM_RS); 101 (priv->stats_ring_size * NFP_FL_STATS_ELEM_RS);
96 102
97 return 0; 103 return 0;
98} 104}
@@ -102,56 +108,37 @@ struct nfp_fl_payload *
102nfp_flower_search_fl_table(struct nfp_app *app, unsigned long tc_flower_cookie, 108nfp_flower_search_fl_table(struct nfp_app *app, unsigned long tc_flower_cookie,
103 struct net_device *netdev, __be32 host_ctx) 109 struct net_device *netdev, __be32 host_ctx)
104{ 110{
111 struct nfp_fl_flow_table_cmp_arg flower_cmp_arg;
105 struct nfp_flower_priv *priv = app->priv; 112 struct nfp_flower_priv *priv = app->priv;
106 struct nfp_fl_payload *flower_entry;
107 113
108 hash_for_each_possible_rcu(priv->flow_table, flower_entry, link, 114 flower_cmp_arg.netdev = netdev;
109 tc_flower_cookie) 115 flower_cmp_arg.cookie = tc_flower_cookie;
110 if (flower_entry->tc_flower_cookie == tc_flower_cookie && 116 flower_cmp_arg.host_ctx = host_ctx;
111 (!netdev || flower_entry->ingress_dev == netdev) &&
112 (host_ctx == NFP_FL_STATS_CTX_DONT_CARE ||
113 flower_entry->meta.host_ctx_id == host_ctx))
114 return flower_entry;
115 117
116 return NULL; 118 return rhashtable_lookup_fast(&priv->flow_table, &flower_cmp_arg,
117} 119 nfp_flower_table_params);
118
119static void
120nfp_flower_update_stats(struct nfp_app *app, struct nfp_fl_stats_frame *stats)
121{
122 struct nfp_fl_payload *nfp_flow;
123 unsigned long flower_cookie;
124
125 flower_cookie = be64_to_cpu(stats->stats_cookie);
126
127 rcu_read_lock();
128 nfp_flow = nfp_flower_search_fl_table(app, flower_cookie, NULL,
129 stats->stats_con_id);
130 if (!nfp_flow)
131 goto exit_rcu_unlock;
132
133 spin_lock(&nfp_flow->lock);
134 nfp_flow->stats.pkts += be32_to_cpu(stats->pkt_count);
135 nfp_flow->stats.bytes += be64_to_cpu(stats->byte_count);
136 nfp_flow->stats.used = jiffies;
137 spin_unlock(&nfp_flow->lock);
138
139exit_rcu_unlock:
140 rcu_read_unlock();
141} 120}
142 121
143void nfp_flower_rx_flow_stats(struct nfp_app *app, struct sk_buff *skb) 122void nfp_flower_rx_flow_stats(struct nfp_app *app, struct sk_buff *skb)
144{ 123{
145 unsigned int msg_len = nfp_flower_cmsg_get_data_len(skb); 124 unsigned int msg_len = nfp_flower_cmsg_get_data_len(skb);
146 struct nfp_fl_stats_frame *stats_frame; 125 struct nfp_flower_priv *priv = app->priv;
126 struct nfp_fl_stats_frame *stats;
147 unsigned char *msg; 127 unsigned char *msg;
128 u32 ctx_id;
148 int i; 129 int i;
149 130
150 msg = nfp_flower_cmsg_get_data(skb); 131 msg = nfp_flower_cmsg_get_data(skb);
151 132
152 stats_frame = (struct nfp_fl_stats_frame *)msg; 133 spin_lock(&priv->stats_lock);
153 for (i = 0; i < msg_len / sizeof(*stats_frame); i++) 134 for (i = 0; i < msg_len / sizeof(*stats); i++) {
154 nfp_flower_update_stats(app, stats_frame + i); 135 stats = (struct nfp_fl_stats_frame *)msg + i;
136 ctx_id = be32_to_cpu(stats->stats_con_id);
137 priv->stats[ctx_id].pkts += be32_to_cpu(stats->pkt_count);
138 priv->stats[ctx_id].bytes += be64_to_cpu(stats->byte_count);
139 priv->stats[ctx_id].used = jiffies;
140 }
141 spin_unlock(&priv->stats_lock);
155} 142}
156 143
157static int nfp_release_mask_id(struct nfp_app *app, u8 mask_id) 144static int nfp_release_mask_id(struct nfp_app *app, u8 mask_id)
@@ -345,9 +332,9 @@ int nfp_compile_flow_metadata(struct nfp_app *app,
345 332
346 /* Update flow payload with mask ids. */ 333 /* Update flow payload with mask ids. */
347 nfp_flow->unmasked_data[NFP_FL_MASK_ID_LOCATION] = new_mask_id; 334 nfp_flow->unmasked_data[NFP_FL_MASK_ID_LOCATION] = new_mask_id;
348 nfp_flow->stats.pkts = 0; 335 priv->stats[stats_cxt].pkts = 0;
349 nfp_flow->stats.bytes = 0; 336 priv->stats[stats_cxt].bytes = 0;
350 nfp_flow->stats.used = jiffies; 337 priv->stats[stats_cxt].used = jiffies;
351 338
352 check_entry = nfp_flower_search_fl_table(app, flow->cookie, netdev, 339 check_entry = nfp_flower_search_fl_table(app, flow->cookie, netdev,
353 NFP_FL_STATS_CTX_DONT_CARE); 340 NFP_FL_STATS_CTX_DONT_CARE);
@@ -389,12 +376,56 @@ int nfp_modify_flow_metadata(struct nfp_app *app,
389 return nfp_release_stats_entry(app, temp_ctx_id); 376 return nfp_release_stats_entry(app, temp_ctx_id);
390} 377}
391 378
392int nfp_flower_metadata_init(struct nfp_app *app) 379static int nfp_fl_obj_cmpfn(struct rhashtable_compare_arg *arg,
380 const void *obj)
381{
382 const struct nfp_fl_flow_table_cmp_arg *cmp_arg = arg->key;
383 const struct nfp_fl_payload *flow_entry = obj;
384
385 if ((!cmp_arg->netdev || flow_entry->ingress_dev == cmp_arg->netdev) &&
386 (cmp_arg->host_ctx == NFP_FL_STATS_CTX_DONT_CARE ||
387 flow_entry->meta.host_ctx_id == cmp_arg->host_ctx))
388 return flow_entry->tc_flower_cookie != cmp_arg->cookie;
389
390 return 1;
391}
392
393static u32 nfp_fl_obj_hashfn(const void *data, u32 len, u32 seed)
394{
395 const struct nfp_fl_payload *flower_entry = data;
396
397 return jhash2((u32 *)&flower_entry->tc_flower_cookie,
398 sizeof(flower_entry->tc_flower_cookie) / sizeof(u32),
399 seed);
400}
401
402static u32 nfp_fl_key_hashfn(const void *data, u32 len, u32 seed)
403{
404 const struct nfp_fl_flow_table_cmp_arg *cmp_arg = data;
405
406 return jhash2((u32 *)&cmp_arg->cookie,
407 sizeof(cmp_arg->cookie) / sizeof(u32), seed);
408}
409
410const struct rhashtable_params nfp_flower_table_params = {
411 .head_offset = offsetof(struct nfp_fl_payload, fl_node),
412 .hashfn = nfp_fl_key_hashfn,
413 .obj_cmpfn = nfp_fl_obj_cmpfn,
414 .obj_hashfn = nfp_fl_obj_hashfn,
415 .automatic_shrinking = true,
416};
417
418int nfp_flower_metadata_init(struct nfp_app *app, u64 host_ctx_count)
393{ 419{
394 struct nfp_flower_priv *priv = app->priv; 420 struct nfp_flower_priv *priv = app->priv;
421 int err;
395 422
396 hash_init(priv->mask_table); 423 hash_init(priv->mask_table);
397 hash_init(priv->flow_table); 424
425 err = rhashtable_init(&priv->flow_table, &nfp_flower_table_params);
426 if (err)
427 return err;
428
398 get_random_bytes(&priv->mask_id_seed, sizeof(priv->mask_id_seed)); 429 get_random_bytes(&priv->mask_id_seed, sizeof(priv->mask_id_seed));
399 430
400 /* Init ring buffer and unallocated mask_ids. */ 431 /* Init ring buffer and unallocated mask_ids. */
@@ -402,7 +433,7 @@ int nfp_flower_metadata_init(struct nfp_app *app)
402 kmalloc_array(NFP_FLOWER_MASK_ENTRY_RS, 433 kmalloc_array(NFP_FLOWER_MASK_ENTRY_RS,
403 NFP_FLOWER_MASK_ELEMENT_RS, GFP_KERNEL); 434 NFP_FLOWER_MASK_ELEMENT_RS, GFP_KERNEL);
404 if (!priv->mask_ids.mask_id_free_list.buf) 435 if (!priv->mask_ids.mask_id_free_list.buf)
405 return -ENOMEM; 436 goto err_free_flow_table;
406 437
407 priv->mask_ids.init_unallocated = NFP_FLOWER_MASK_ENTRY_RS - 1; 438 priv->mask_ids.init_unallocated = NFP_FLOWER_MASK_ENTRY_RS - 1;
408 439
@@ -416,18 +447,29 @@ int nfp_flower_metadata_init(struct nfp_app *app)
416 /* Init ring buffer and unallocated stats_ids. */ 447 /* Init ring buffer and unallocated stats_ids. */
417 priv->stats_ids.free_list.buf = 448 priv->stats_ids.free_list.buf =
418 vmalloc(array_size(NFP_FL_STATS_ELEM_RS, 449 vmalloc(array_size(NFP_FL_STATS_ELEM_RS,
419 NFP_FL_STATS_ENTRY_RS)); 450 priv->stats_ring_size));
420 if (!priv->stats_ids.free_list.buf) 451 if (!priv->stats_ids.free_list.buf)
421 goto err_free_last_used; 452 goto err_free_last_used;
422 453
423 priv->stats_ids.init_unalloc = NFP_FL_REPEATED_HASH_MAX; 454 priv->stats_ids.init_unalloc = host_ctx_count;
455
456 priv->stats = kvmalloc_array(priv->stats_ring_size,
457 sizeof(struct nfp_fl_stats), GFP_KERNEL);
458 if (!priv->stats)
459 goto err_free_ring_buf;
460
461 spin_lock_init(&priv->stats_lock);
424 462
425 return 0; 463 return 0;
426 464
465err_free_ring_buf:
466 vfree(priv->stats_ids.free_list.buf);
427err_free_last_used: 467err_free_last_used:
428 kfree(priv->mask_ids.last_used); 468 kfree(priv->mask_ids.last_used);
429err_free_mask_id: 469err_free_mask_id:
430 kfree(priv->mask_ids.mask_id_free_list.buf); 470 kfree(priv->mask_ids.mask_id_free_list.buf);
471err_free_flow_table:
472 rhashtable_destroy(&priv->flow_table);
431 return -ENOMEM; 473 return -ENOMEM;
432} 474}
433 475
@@ -438,6 +480,9 @@ void nfp_flower_metadata_cleanup(struct nfp_app *app)
438 if (!priv) 480 if (!priv)
439 return; 481 return;
440 482
483 rhashtable_free_and_destroy(&priv->flow_table,
484 nfp_check_rhashtable_empty, NULL);
485 kvfree(priv->stats);
441 kfree(priv->mask_ids.mask_id_free_list.buf); 486 kfree(priv->mask_ids.mask_id_free_list.buf);
442 kfree(priv->mask_ids.last_used); 487 kfree(priv->mask_ids.last_used);
443 vfree(priv->stats_ids.free_list.buf); 488 vfree(priv->stats_ids.free_list.buf);
diff --git a/drivers/net/ethernet/netronome/nfp/flower/offload.c b/drivers/net/ethernet/netronome/nfp/flower/offload.c
index bd19624f10cf..efe7a41e1a3e 100644
--- a/drivers/net/ethernet/netronome/nfp/flower/offload.c
+++ b/drivers/net/ethernet/netronome/nfp/flower/offload.c
@@ -428,8 +428,6 @@ nfp_flower_allocate_new(struct nfp_fl_key_ls *key_layer, bool egress)
428 428
429 flow_pay->nfp_tun_ipv4_addr = 0; 429 flow_pay->nfp_tun_ipv4_addr = 0;
430 flow_pay->meta.flags = 0; 430 flow_pay->meta.flags = 0;
431 spin_lock_init(&flow_pay->lock);
432
433 flow_pay->ingress_offload = !egress; 431 flow_pay->ingress_offload = !egress;
434 432
435 return flow_pay; 433 return flow_pay;
@@ -513,9 +511,12 @@ nfp_flower_add_offload(struct nfp_app *app, struct net_device *netdev,
513 if (err) 511 if (err)
514 goto err_destroy_flow; 512 goto err_destroy_flow;
515 513
516 INIT_HLIST_NODE(&flow_pay->link);
517 flow_pay->tc_flower_cookie = flow->cookie; 514 flow_pay->tc_flower_cookie = flow->cookie;
518 hash_add_rcu(priv->flow_table, &flow_pay->link, flow->cookie); 515 err = rhashtable_insert_fast(&priv->flow_table, &flow_pay->fl_node,
516 nfp_flower_table_params);
517 if (err)
518 goto err_destroy_flow;
519
519 port->tc_offload_cnt++; 520 port->tc_offload_cnt++;
520 521
521 /* Deallocate flow payload when flower rule has been destroyed. */ 522 /* Deallocate flow payload when flower rule has been destroyed. */
@@ -550,6 +551,7 @@ nfp_flower_del_offload(struct nfp_app *app, struct net_device *netdev,
550 struct tc_cls_flower_offload *flow, bool egress) 551 struct tc_cls_flower_offload *flow, bool egress)
551{ 552{
552 struct nfp_port *port = nfp_port_from_netdev(netdev); 553 struct nfp_port *port = nfp_port_from_netdev(netdev);
554 struct nfp_flower_priv *priv = app->priv;
553 struct nfp_fl_payload *nfp_flow; 555 struct nfp_fl_payload *nfp_flow;
554 struct net_device *ingr_dev; 556 struct net_device *ingr_dev;
555 int err; 557 int err;
@@ -573,11 +575,13 @@ nfp_flower_del_offload(struct nfp_app *app, struct net_device *netdev,
573 goto err_free_flow; 575 goto err_free_flow;
574 576
575err_free_flow: 577err_free_flow:
576 hash_del_rcu(&nfp_flow->link);
577 port->tc_offload_cnt--; 578 port->tc_offload_cnt--;
578 kfree(nfp_flow->action_data); 579 kfree(nfp_flow->action_data);
579 kfree(nfp_flow->mask_data); 580 kfree(nfp_flow->mask_data);
580 kfree(nfp_flow->unmasked_data); 581 kfree(nfp_flow->unmasked_data);
582 WARN_ON_ONCE(rhashtable_remove_fast(&priv->flow_table,
583 &nfp_flow->fl_node,
584 nfp_flower_table_params));
581 kfree_rcu(nfp_flow, rcu); 585 kfree_rcu(nfp_flow, rcu);
582 return err; 586 return err;
583} 587}
@@ -598,8 +602,10 @@ static int
598nfp_flower_get_stats(struct nfp_app *app, struct net_device *netdev, 602nfp_flower_get_stats(struct nfp_app *app, struct net_device *netdev,
599 struct tc_cls_flower_offload *flow, bool egress) 603 struct tc_cls_flower_offload *flow, bool egress)
600{ 604{
605 struct nfp_flower_priv *priv = app->priv;
601 struct nfp_fl_payload *nfp_flow; 606 struct nfp_fl_payload *nfp_flow;
602 struct net_device *ingr_dev; 607 struct net_device *ingr_dev;
608 u32 ctx_id;
603 609
604 ingr_dev = egress ? NULL : netdev; 610 ingr_dev = egress ? NULL : netdev;
605 nfp_flow = nfp_flower_search_fl_table(app, flow->cookie, ingr_dev, 611 nfp_flow = nfp_flower_search_fl_table(app, flow->cookie, ingr_dev,
@@ -610,13 +616,16 @@ nfp_flower_get_stats(struct nfp_app *app, struct net_device *netdev,
610 if (nfp_flow->ingress_offload && egress) 616 if (nfp_flow->ingress_offload && egress)
611 return 0; 617 return 0;
612 618
613 spin_lock_bh(&nfp_flow->lock); 619 ctx_id = be32_to_cpu(nfp_flow->meta.host_ctx_id);
614 tcf_exts_stats_update(flow->exts, nfp_flow->stats.bytes, 620
615 nfp_flow->stats.pkts, nfp_flow->stats.used); 621 spin_lock_bh(&priv->stats_lock);
622 tcf_exts_stats_update(flow->exts, priv->stats[ctx_id].bytes,
623 priv->stats[ctx_id].pkts,
624 priv->stats[ctx_id].used);
616 625
617 nfp_flow->stats.pkts = 0; 626 priv->stats[ctx_id].pkts = 0;
618 nfp_flow->stats.bytes = 0; 627 priv->stats[ctx_id].bytes = 0;
619 spin_unlock_bh(&nfp_flow->lock); 628 spin_unlock_bh(&priv->stats_lock);
620 629
621 return 0; 630 return 0;
622} 631}
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_app.c b/drivers/net/ethernet/netronome/nfp/nfp_app.c
index 8607d09ab732..01116822ddf7 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_app.c
+++ b/drivers/net/ethernet/netronome/nfp/nfp_app.c
@@ -60,6 +60,11 @@ static const struct nfp_app_type *apps[] = {
60#endif 60#endif
61}; 61};
62 62
63void nfp_check_rhashtable_empty(void *ptr, void *arg)
64{
65 WARN_ON_ONCE(1);
66}
67
63struct nfp_app *nfp_app_from_netdev(struct net_device *netdev) 68struct nfp_app *nfp_app_from_netdev(struct net_device *netdev)
64{ 69{
65 if (nfp_netdev_is_nfp_net(netdev)) { 70 if (nfp_netdev_is_nfp_net(netdev)) {
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_app.h b/drivers/net/ethernet/netronome/nfp/nfp_app.h
index c896eb8f87a1..19f82f24c6ad 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_app.h
+++ b/drivers/net/ethernet/netronome/nfp/nfp_app.h
@@ -196,6 +196,7 @@ struct nfp_app {
196 void *priv; 196 void *priv;
197}; 197};
198 198
199void nfp_check_rhashtable_empty(void *ptr, void *arg);
199bool __nfp_ctrl_tx(struct nfp_net *nn, struct sk_buff *skb); 200bool __nfp_ctrl_tx(struct nfp_net *nn, struct sk_buff *skb);
200bool nfp_ctrl_tx(struct nfp_net *nn, struct sk_buff *skb); 201bool nfp_ctrl_tx(struct nfp_net *nn, struct sk_buff *skb);
201 202