162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
262306a36Sopenharmony_ci/* Copyright (C) 2017-2018 Netronome Systems, Inc. */
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include <linux/hash.h>
562306a36Sopenharmony_ci#include <linux/hashtable.h>
662306a36Sopenharmony_ci#include <linux/jhash.h>
762306a36Sopenharmony_ci#include <linux/math64.h>
862306a36Sopenharmony_ci#include <linux/vmalloc.h>
962306a36Sopenharmony_ci#include <net/pkt_cls.h>
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include "cmsg.h"
1262306a36Sopenharmony_ci#include "conntrack.h"
1362306a36Sopenharmony_ci#include "main.h"
1462306a36Sopenharmony_ci#include "../nfp_app.h"
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_cistruct nfp_mask_id_table {
1762306a36Sopenharmony_ci	struct hlist_node link;
1862306a36Sopenharmony_ci	u32 hash_key;
1962306a36Sopenharmony_ci	u32 ref_cnt;
2062306a36Sopenharmony_ci	u8 mask_id;
2162306a36Sopenharmony_ci};
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cistruct nfp_fl_flow_table_cmp_arg {
2462306a36Sopenharmony_ci	struct net_device *netdev;
2562306a36Sopenharmony_ci	unsigned long cookie;
2662306a36Sopenharmony_ci};
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistruct nfp_fl_stats_ctx_to_flow {
2962306a36Sopenharmony_ci	struct rhash_head ht_node;
3062306a36Sopenharmony_ci	u32 stats_cxt;
3162306a36Sopenharmony_ci	struct nfp_fl_payload *flow;
3262306a36Sopenharmony_ci};
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cistatic const struct rhashtable_params stats_ctx_table_params = {
3562306a36Sopenharmony_ci	.key_offset	= offsetof(struct nfp_fl_stats_ctx_to_flow, stats_cxt),
3662306a36Sopenharmony_ci	.head_offset	= offsetof(struct nfp_fl_stats_ctx_to_flow, ht_node),
3762306a36Sopenharmony_ci	.key_len	= sizeof(u32),
3862306a36Sopenharmony_ci};
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cistatic int nfp_release_stats_entry(struct nfp_app *app, u32 stats_context_id)
4162306a36Sopenharmony_ci{
4262306a36Sopenharmony_ci	struct nfp_flower_priv *priv = app->priv;
4362306a36Sopenharmony_ci	struct circ_buf *ring;
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	ring = &priv->stats_ids.free_list;
4662306a36Sopenharmony_ci	/* Check if buffer is full, stats_ring_size must be power of 2 */
4762306a36Sopenharmony_ci	if (!CIRC_SPACE(ring->head, ring->tail, priv->stats_ring_size))
4862306a36Sopenharmony_ci		return -ENOBUFS;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	/* Each increment of head represents size of NFP_FL_STATS_ELEM_RS */
5162306a36Sopenharmony_ci	memcpy(&ring->buf[ring->head * NFP_FL_STATS_ELEM_RS],
5262306a36Sopenharmony_ci	       &stats_context_id, NFP_FL_STATS_ELEM_RS);
5362306a36Sopenharmony_ci	ring->head = (ring->head + 1) & (priv->stats_ring_size - 1);
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	return 0;
5662306a36Sopenharmony_ci}
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_cistatic int nfp_get_stats_entry(struct nfp_app *app, u32 *stats_context_id)
5962306a36Sopenharmony_ci{
6062306a36Sopenharmony_ci	struct nfp_flower_priv *priv = app->priv;
6162306a36Sopenharmony_ci	u32 freed_stats_id, temp_stats_id;
6262306a36Sopenharmony_ci	struct circ_buf *ring;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	ring = &priv->stats_ids.free_list;
6562306a36Sopenharmony_ci	freed_stats_id = priv->stats_ring_size;
6662306a36Sopenharmony_ci	/* Check for unallocated entries first. */
6762306a36Sopenharmony_ci	if (priv->stats_ids.init_unalloc > 0) {
6862306a36Sopenharmony_ci		*stats_context_id =
6962306a36Sopenharmony_ci			FIELD_PREP(NFP_FL_STAT_ID_STAT,
7062306a36Sopenharmony_ci				   priv->stats_ids.init_unalloc - 1) |
7162306a36Sopenharmony_ci			FIELD_PREP(NFP_FL_STAT_ID_MU_NUM,
7262306a36Sopenharmony_ci				   priv->active_mem_unit);
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci		if (++priv->active_mem_unit == priv->total_mem_units) {
7562306a36Sopenharmony_ci			priv->stats_ids.init_unalloc--;
7662306a36Sopenharmony_ci			priv->active_mem_unit = 0;
7762306a36Sopenharmony_ci		}
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci		return 0;
8062306a36Sopenharmony_ci	}
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	/* Check if buffer is empty. */
8362306a36Sopenharmony_ci	if (ring->head == ring->tail) {
8462306a36Sopenharmony_ci		*stats_context_id = freed_stats_id;
8562306a36Sopenharmony_ci		return -ENOENT;
8662306a36Sopenharmony_ci	}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	/* Each increment of tail represents size of NFP_FL_STATS_ELEM_RS */
8962306a36Sopenharmony_ci	memcpy(&temp_stats_id, &ring->buf[ring->tail * NFP_FL_STATS_ELEM_RS],
9062306a36Sopenharmony_ci	       NFP_FL_STATS_ELEM_RS);
9162306a36Sopenharmony_ci	*stats_context_id = temp_stats_id;
9262306a36Sopenharmony_ci	memcpy(&ring->buf[ring->tail * NFP_FL_STATS_ELEM_RS], &freed_stats_id,
9362306a36Sopenharmony_ci	       NFP_FL_STATS_ELEM_RS);
9462306a36Sopenharmony_ci	/* stats_ring_size must be power of 2 */
9562306a36Sopenharmony_ci	ring->tail = (ring->tail + 1) & (priv->stats_ring_size - 1);
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	return 0;
9862306a36Sopenharmony_ci}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci/* Must be called with either RTNL or rcu_read_lock */
10162306a36Sopenharmony_cistruct nfp_fl_payload *
10262306a36Sopenharmony_cinfp_flower_search_fl_table(struct nfp_app *app, unsigned long tc_flower_cookie,
10362306a36Sopenharmony_ci			   struct net_device *netdev)
10462306a36Sopenharmony_ci{
10562306a36Sopenharmony_ci	struct nfp_fl_flow_table_cmp_arg flower_cmp_arg;
10662306a36Sopenharmony_ci	struct nfp_flower_priv *priv = app->priv;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	flower_cmp_arg.netdev = netdev;
10962306a36Sopenharmony_ci	flower_cmp_arg.cookie = tc_flower_cookie;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	return rhashtable_lookup_fast(&priv->flow_table, &flower_cmp_arg,
11262306a36Sopenharmony_ci				      nfp_flower_table_params);
11362306a36Sopenharmony_ci}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_civoid nfp_flower_rx_flow_stats(struct nfp_app *app, struct sk_buff *skb)
11662306a36Sopenharmony_ci{
11762306a36Sopenharmony_ci	unsigned int msg_len = nfp_flower_cmsg_get_data_len(skb);
11862306a36Sopenharmony_ci	struct nfp_flower_priv *priv = app->priv;
11962306a36Sopenharmony_ci	struct nfp_fl_stats_frame *stats;
12062306a36Sopenharmony_ci	unsigned char *msg;
12162306a36Sopenharmony_ci	u32 ctx_id;
12262306a36Sopenharmony_ci	int i;
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	msg = nfp_flower_cmsg_get_data(skb);
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	spin_lock(&priv->stats_lock);
12762306a36Sopenharmony_ci	for (i = 0; i < msg_len / sizeof(*stats); i++) {
12862306a36Sopenharmony_ci		stats = (struct nfp_fl_stats_frame *)msg + i;
12962306a36Sopenharmony_ci		ctx_id = be32_to_cpu(stats->stats_con_id);
13062306a36Sopenharmony_ci		priv->stats[ctx_id].pkts += be32_to_cpu(stats->pkt_count);
13162306a36Sopenharmony_ci		priv->stats[ctx_id].bytes += be64_to_cpu(stats->byte_count);
13262306a36Sopenharmony_ci		priv->stats[ctx_id].used = jiffies;
13362306a36Sopenharmony_ci	}
13462306a36Sopenharmony_ci	spin_unlock(&priv->stats_lock);
13562306a36Sopenharmony_ci}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_cistatic int nfp_release_mask_id(struct nfp_app *app, u8 mask_id)
13862306a36Sopenharmony_ci{
13962306a36Sopenharmony_ci	struct nfp_flower_priv *priv = app->priv;
14062306a36Sopenharmony_ci	struct circ_buf *ring;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	ring = &priv->mask_ids.mask_id_free_list;
14362306a36Sopenharmony_ci	/* Checking if buffer is full,
14462306a36Sopenharmony_ci	 * NFP_FLOWER_MASK_ENTRY_RS must be power of 2
14562306a36Sopenharmony_ci	 */
14662306a36Sopenharmony_ci	if (CIRC_SPACE(ring->head, ring->tail, NFP_FLOWER_MASK_ENTRY_RS) == 0)
14762306a36Sopenharmony_ci		return -ENOBUFS;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	/* Each increment of head represents size of
15062306a36Sopenharmony_ci	 * NFP_FLOWER_MASK_ELEMENT_RS
15162306a36Sopenharmony_ci	 */
15262306a36Sopenharmony_ci	memcpy(&ring->buf[ring->head * NFP_FLOWER_MASK_ELEMENT_RS], &mask_id,
15362306a36Sopenharmony_ci	       NFP_FLOWER_MASK_ELEMENT_RS);
15462306a36Sopenharmony_ci	ring->head = (ring->head + 1) & (NFP_FLOWER_MASK_ENTRY_RS - 1);
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	priv->mask_ids.last_used[mask_id] = ktime_get();
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	return 0;
15962306a36Sopenharmony_ci}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_cistatic int nfp_mask_alloc(struct nfp_app *app, u8 *mask_id)
16262306a36Sopenharmony_ci{
16362306a36Sopenharmony_ci	struct nfp_flower_priv *priv = app->priv;
16462306a36Sopenharmony_ci	ktime_t reuse_timeout;
16562306a36Sopenharmony_ci	struct circ_buf *ring;
16662306a36Sopenharmony_ci	u8 temp_id, freed_id;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	ring = &priv->mask_ids.mask_id_free_list;
16962306a36Sopenharmony_ci	freed_id = NFP_FLOWER_MASK_ENTRY_RS - 1;
17062306a36Sopenharmony_ci	/* Checking for unallocated entries first. */
17162306a36Sopenharmony_ci	if (priv->mask_ids.init_unallocated > 0) {
17262306a36Sopenharmony_ci		*mask_id = priv->mask_ids.init_unallocated;
17362306a36Sopenharmony_ci		priv->mask_ids.init_unallocated--;
17462306a36Sopenharmony_ci		return 0;
17562306a36Sopenharmony_ci	}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	/* Checking if buffer is empty. */
17862306a36Sopenharmony_ci	if (ring->head == ring->tail)
17962306a36Sopenharmony_ci		goto err_not_found;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	/* Each increment of tail represents size of
18262306a36Sopenharmony_ci	 * NFP_FLOWER_MASK_ELEMENT_RS
18362306a36Sopenharmony_ci	 */
18462306a36Sopenharmony_ci	memcpy(&temp_id, &ring->buf[ring->tail * NFP_FLOWER_MASK_ELEMENT_RS],
18562306a36Sopenharmony_ci	       NFP_FLOWER_MASK_ELEMENT_RS);
18662306a36Sopenharmony_ci	*mask_id = temp_id;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	reuse_timeout = ktime_add_ns(priv->mask_ids.last_used[*mask_id],
18962306a36Sopenharmony_ci				     NFP_FL_MASK_REUSE_TIME_NS);
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	if (ktime_before(ktime_get(), reuse_timeout))
19262306a36Sopenharmony_ci		goto err_not_found;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	memcpy(&ring->buf[ring->tail * NFP_FLOWER_MASK_ELEMENT_RS], &freed_id,
19562306a36Sopenharmony_ci	       NFP_FLOWER_MASK_ELEMENT_RS);
19662306a36Sopenharmony_ci	/* NFP_FLOWER_MASK_ENTRY_RS must be power of 2 */
19762306a36Sopenharmony_ci	ring->tail = (ring->tail + 1) & (NFP_FLOWER_MASK_ENTRY_RS - 1);
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	return 0;
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_cierr_not_found:
20262306a36Sopenharmony_ci	*mask_id = freed_id;
20362306a36Sopenharmony_ci	return -ENOENT;
20462306a36Sopenharmony_ci}
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_cistatic int
20762306a36Sopenharmony_cinfp_add_mask_table(struct nfp_app *app, char *mask_data, u32 mask_len)
20862306a36Sopenharmony_ci{
20962306a36Sopenharmony_ci	struct nfp_flower_priv *priv = app->priv;
21062306a36Sopenharmony_ci	struct nfp_mask_id_table *mask_entry;
21162306a36Sopenharmony_ci	unsigned long hash_key;
21262306a36Sopenharmony_ci	u8 mask_id;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	if (nfp_mask_alloc(app, &mask_id))
21562306a36Sopenharmony_ci		return -ENOENT;
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	mask_entry = kmalloc(sizeof(*mask_entry), GFP_KERNEL);
21862306a36Sopenharmony_ci	if (!mask_entry) {
21962306a36Sopenharmony_ci		nfp_release_mask_id(app, mask_id);
22062306a36Sopenharmony_ci		return -ENOMEM;
22162306a36Sopenharmony_ci	}
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	INIT_HLIST_NODE(&mask_entry->link);
22462306a36Sopenharmony_ci	mask_entry->mask_id = mask_id;
22562306a36Sopenharmony_ci	hash_key = jhash(mask_data, mask_len, priv->mask_id_seed);
22662306a36Sopenharmony_ci	mask_entry->hash_key = hash_key;
22762306a36Sopenharmony_ci	mask_entry->ref_cnt = 1;
22862306a36Sopenharmony_ci	hash_add(priv->mask_table, &mask_entry->link, hash_key);
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	return mask_id;
23162306a36Sopenharmony_ci}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_cistatic struct nfp_mask_id_table *
23462306a36Sopenharmony_cinfp_search_mask_table(struct nfp_app *app, char *mask_data, u32 mask_len)
23562306a36Sopenharmony_ci{
23662306a36Sopenharmony_ci	struct nfp_flower_priv *priv = app->priv;
23762306a36Sopenharmony_ci	struct nfp_mask_id_table *mask_entry;
23862306a36Sopenharmony_ci	unsigned long hash_key;
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	hash_key = jhash(mask_data, mask_len, priv->mask_id_seed);
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	hash_for_each_possible(priv->mask_table, mask_entry, link, hash_key)
24362306a36Sopenharmony_ci		if (mask_entry->hash_key == hash_key)
24462306a36Sopenharmony_ci			return mask_entry;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	return NULL;
24762306a36Sopenharmony_ci}
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_cistatic int
25062306a36Sopenharmony_cinfp_find_in_mask_table(struct nfp_app *app, char *mask_data, u32 mask_len)
25162306a36Sopenharmony_ci{
25262306a36Sopenharmony_ci	struct nfp_mask_id_table *mask_entry;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	mask_entry = nfp_search_mask_table(app, mask_data, mask_len);
25562306a36Sopenharmony_ci	if (!mask_entry)
25662306a36Sopenharmony_ci		return -ENOENT;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	mask_entry->ref_cnt++;
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	/* Casting u8 to int for later use. */
26162306a36Sopenharmony_ci	return mask_entry->mask_id;
26262306a36Sopenharmony_ci}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_cistatic bool
26562306a36Sopenharmony_cinfp_check_mask_add(struct nfp_app *app, char *mask_data, u32 mask_len,
26662306a36Sopenharmony_ci		   u8 *meta_flags, u8 *mask_id)
26762306a36Sopenharmony_ci{
26862306a36Sopenharmony_ci	int id;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	id = nfp_find_in_mask_table(app, mask_data, mask_len);
27162306a36Sopenharmony_ci	if (id < 0) {
27262306a36Sopenharmony_ci		id = nfp_add_mask_table(app, mask_data, mask_len);
27362306a36Sopenharmony_ci		if (id < 0)
27462306a36Sopenharmony_ci			return false;
27562306a36Sopenharmony_ci		*meta_flags |= NFP_FL_META_FLAG_MANAGE_MASK;
27662306a36Sopenharmony_ci	}
27762306a36Sopenharmony_ci	*mask_id = id;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	return true;
28062306a36Sopenharmony_ci}
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_cistatic bool
28362306a36Sopenharmony_cinfp_check_mask_remove(struct nfp_app *app, char *mask_data, u32 mask_len,
28462306a36Sopenharmony_ci		      u8 *meta_flags, u8 *mask_id)
28562306a36Sopenharmony_ci{
28662306a36Sopenharmony_ci	struct nfp_mask_id_table *mask_entry;
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	mask_entry = nfp_search_mask_table(app, mask_data, mask_len);
28962306a36Sopenharmony_ci	if (!mask_entry)
29062306a36Sopenharmony_ci		return false;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	*mask_id = mask_entry->mask_id;
29362306a36Sopenharmony_ci	mask_entry->ref_cnt--;
29462306a36Sopenharmony_ci	if (!mask_entry->ref_cnt) {
29562306a36Sopenharmony_ci		hash_del(&mask_entry->link);
29662306a36Sopenharmony_ci		nfp_release_mask_id(app, *mask_id);
29762306a36Sopenharmony_ci		kfree(mask_entry);
29862306a36Sopenharmony_ci		if (meta_flags)
29962306a36Sopenharmony_ci			*meta_flags |= NFP_FL_META_FLAG_MANAGE_MASK;
30062306a36Sopenharmony_ci	}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	return true;
30362306a36Sopenharmony_ci}
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ciint nfp_compile_flow_metadata(struct nfp_app *app, u32 cookie,
30662306a36Sopenharmony_ci			      struct nfp_fl_payload *nfp_flow,
30762306a36Sopenharmony_ci			      struct net_device *netdev,
30862306a36Sopenharmony_ci			      struct netlink_ext_ack *extack)
30962306a36Sopenharmony_ci{
31062306a36Sopenharmony_ci	struct nfp_fl_stats_ctx_to_flow *ctx_entry;
31162306a36Sopenharmony_ci	struct nfp_flower_priv *priv = app->priv;
31262306a36Sopenharmony_ci	struct nfp_fl_payload *check_entry;
31362306a36Sopenharmony_ci	u8 new_mask_id;
31462306a36Sopenharmony_ci	u32 stats_cxt;
31562306a36Sopenharmony_ci	int err;
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	err = nfp_get_stats_entry(app, &stats_cxt);
31862306a36Sopenharmony_ci	if (err) {
31962306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "invalid entry: cannot allocate new stats context");
32062306a36Sopenharmony_ci		return err;
32162306a36Sopenharmony_ci	}
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	nfp_flow->meta.host_ctx_id = cpu_to_be32(stats_cxt);
32462306a36Sopenharmony_ci	nfp_flow->meta.host_cookie = cpu_to_be64(cookie);
32562306a36Sopenharmony_ci	nfp_flow->ingress_dev = netdev;
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	ctx_entry = kzalloc(sizeof(*ctx_entry), GFP_KERNEL);
32862306a36Sopenharmony_ci	if (!ctx_entry) {
32962306a36Sopenharmony_ci		err = -ENOMEM;
33062306a36Sopenharmony_ci		goto err_release_stats;
33162306a36Sopenharmony_ci	}
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	ctx_entry->stats_cxt = stats_cxt;
33462306a36Sopenharmony_ci	ctx_entry->flow = nfp_flow;
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	if (rhashtable_insert_fast(&priv->stats_ctx_table, &ctx_entry->ht_node,
33762306a36Sopenharmony_ci				   stats_ctx_table_params)) {
33862306a36Sopenharmony_ci		err = -ENOMEM;
33962306a36Sopenharmony_ci		goto err_free_ctx_entry;
34062306a36Sopenharmony_ci	}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	/* Do not allocate a mask-id for pre_tun_rules. These flows are used to
34362306a36Sopenharmony_ci	 * configure the pre_tun table and are never actually send to the
34462306a36Sopenharmony_ci	 * firmware as an add-flow message. This causes the mask-id allocation
34562306a36Sopenharmony_ci	 * on the firmware to get out of sync if allocated here.
34662306a36Sopenharmony_ci	 */
34762306a36Sopenharmony_ci	new_mask_id = 0;
34862306a36Sopenharmony_ci	if (!nfp_flow->pre_tun_rule.dev &&
34962306a36Sopenharmony_ci	    !nfp_check_mask_add(app, nfp_flow->mask_data,
35062306a36Sopenharmony_ci				nfp_flow->meta.mask_len,
35162306a36Sopenharmony_ci				&nfp_flow->meta.flags, &new_mask_id)) {
35262306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "invalid entry: cannot allocate a new mask id");
35362306a36Sopenharmony_ci		err = -ENOENT;
35462306a36Sopenharmony_ci		goto err_remove_rhash;
35562306a36Sopenharmony_ci	}
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	nfp_flow->meta.flow_version = cpu_to_be64(priv->flower_version);
35862306a36Sopenharmony_ci	priv->flower_version++;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	/* Update flow payload with mask ids. */
36162306a36Sopenharmony_ci	nfp_flow->unmasked_data[NFP_FL_MASK_ID_LOCATION] = new_mask_id;
36262306a36Sopenharmony_ci	priv->stats[stats_cxt].pkts = 0;
36362306a36Sopenharmony_ci	priv->stats[stats_cxt].bytes = 0;
36462306a36Sopenharmony_ci	priv->stats[stats_cxt].used = jiffies;
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	check_entry = nfp_flower_search_fl_table(app, cookie, netdev);
36762306a36Sopenharmony_ci	if (check_entry) {
36862306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "invalid entry: cannot offload duplicate flow entry");
36962306a36Sopenharmony_ci		err = -EEXIST;
37062306a36Sopenharmony_ci		goto err_remove_mask;
37162306a36Sopenharmony_ci	}
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	return 0;
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_cierr_remove_mask:
37662306a36Sopenharmony_ci	if (!nfp_flow->pre_tun_rule.dev)
37762306a36Sopenharmony_ci		nfp_check_mask_remove(app, nfp_flow->mask_data,
37862306a36Sopenharmony_ci				      nfp_flow->meta.mask_len,
37962306a36Sopenharmony_ci				      NULL, &new_mask_id);
38062306a36Sopenharmony_cierr_remove_rhash:
38162306a36Sopenharmony_ci	WARN_ON_ONCE(rhashtable_remove_fast(&priv->stats_ctx_table,
38262306a36Sopenharmony_ci					    &ctx_entry->ht_node,
38362306a36Sopenharmony_ci					    stats_ctx_table_params));
38462306a36Sopenharmony_cierr_free_ctx_entry:
38562306a36Sopenharmony_ci	kfree(ctx_entry);
38662306a36Sopenharmony_cierr_release_stats:
38762306a36Sopenharmony_ci	nfp_release_stats_entry(app, stats_cxt);
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	return err;
39062306a36Sopenharmony_ci}
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_civoid __nfp_modify_flow_metadata(struct nfp_flower_priv *priv,
39362306a36Sopenharmony_ci				struct nfp_fl_payload *nfp_flow)
39462306a36Sopenharmony_ci{
39562306a36Sopenharmony_ci	nfp_flow->meta.flags &= ~NFP_FL_META_FLAG_MANAGE_MASK;
39662306a36Sopenharmony_ci	nfp_flow->meta.flow_version = cpu_to_be64(priv->flower_version);
39762306a36Sopenharmony_ci	priv->flower_version++;
39862306a36Sopenharmony_ci}
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ciint nfp_modify_flow_metadata(struct nfp_app *app,
40162306a36Sopenharmony_ci			     struct nfp_fl_payload *nfp_flow)
40262306a36Sopenharmony_ci{
40362306a36Sopenharmony_ci	struct nfp_fl_stats_ctx_to_flow *ctx_entry;
40462306a36Sopenharmony_ci	struct nfp_flower_priv *priv = app->priv;
40562306a36Sopenharmony_ci	u8 new_mask_id = 0;
40662306a36Sopenharmony_ci	u32 temp_ctx_id;
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	__nfp_modify_flow_metadata(priv, nfp_flow);
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	if (!nfp_flow->pre_tun_rule.dev)
41162306a36Sopenharmony_ci		nfp_check_mask_remove(app, nfp_flow->mask_data,
41262306a36Sopenharmony_ci				      nfp_flow->meta.mask_len, &nfp_flow->meta.flags,
41362306a36Sopenharmony_ci				      &new_mask_id);
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	/* Update flow payload with mask ids. */
41662306a36Sopenharmony_ci	nfp_flow->unmasked_data[NFP_FL_MASK_ID_LOCATION] = new_mask_id;
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	/* Release the stats ctx id and ctx to flow table entry. */
41962306a36Sopenharmony_ci	temp_ctx_id = be32_to_cpu(nfp_flow->meta.host_ctx_id);
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	ctx_entry = rhashtable_lookup_fast(&priv->stats_ctx_table, &temp_ctx_id,
42262306a36Sopenharmony_ci					   stats_ctx_table_params);
42362306a36Sopenharmony_ci	if (!ctx_entry)
42462306a36Sopenharmony_ci		return -ENOENT;
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	WARN_ON_ONCE(rhashtable_remove_fast(&priv->stats_ctx_table,
42762306a36Sopenharmony_ci					    &ctx_entry->ht_node,
42862306a36Sopenharmony_ci					    stats_ctx_table_params));
42962306a36Sopenharmony_ci	kfree(ctx_entry);
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	return nfp_release_stats_entry(app, temp_ctx_id);
43262306a36Sopenharmony_ci}
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_cistruct nfp_fl_payload *
43562306a36Sopenharmony_cinfp_flower_get_fl_payload_from_ctx(struct nfp_app *app, u32 ctx_id)
43662306a36Sopenharmony_ci{
43762306a36Sopenharmony_ci	struct nfp_fl_stats_ctx_to_flow *ctx_entry;
43862306a36Sopenharmony_ci	struct nfp_flower_priv *priv = app->priv;
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	ctx_entry = rhashtable_lookup_fast(&priv->stats_ctx_table, &ctx_id,
44162306a36Sopenharmony_ci					   stats_ctx_table_params);
44262306a36Sopenharmony_ci	if (!ctx_entry)
44362306a36Sopenharmony_ci		return NULL;
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	return ctx_entry->flow;
44662306a36Sopenharmony_ci}
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_cistatic int nfp_fl_obj_cmpfn(struct rhashtable_compare_arg *arg,
44962306a36Sopenharmony_ci			    const void *obj)
45062306a36Sopenharmony_ci{
45162306a36Sopenharmony_ci	const struct nfp_fl_flow_table_cmp_arg *cmp_arg = arg->key;
45262306a36Sopenharmony_ci	const struct nfp_fl_payload *flow_entry = obj;
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	if (flow_entry->ingress_dev == cmp_arg->netdev)
45562306a36Sopenharmony_ci		return flow_entry->tc_flower_cookie != cmp_arg->cookie;
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	return 1;
45862306a36Sopenharmony_ci}
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_cistatic u32 nfp_fl_obj_hashfn(const void *data, u32 len, u32 seed)
46162306a36Sopenharmony_ci{
46262306a36Sopenharmony_ci	const struct nfp_fl_payload *flower_entry = data;
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	return jhash2((u32 *)&flower_entry->tc_flower_cookie,
46562306a36Sopenharmony_ci		      sizeof(flower_entry->tc_flower_cookie) / sizeof(u32),
46662306a36Sopenharmony_ci		      seed);
46762306a36Sopenharmony_ci}
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_cistatic u32 nfp_fl_key_hashfn(const void *data, u32 len, u32 seed)
47062306a36Sopenharmony_ci{
47162306a36Sopenharmony_ci	const struct nfp_fl_flow_table_cmp_arg *cmp_arg = data;
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	return jhash2((u32 *)&cmp_arg->cookie,
47462306a36Sopenharmony_ci		      sizeof(cmp_arg->cookie) / sizeof(u32), seed);
47562306a36Sopenharmony_ci}
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ciconst struct rhashtable_params nfp_flower_table_params = {
47862306a36Sopenharmony_ci	.head_offset		= offsetof(struct nfp_fl_payload, fl_node),
47962306a36Sopenharmony_ci	.hashfn			= nfp_fl_key_hashfn,
48062306a36Sopenharmony_ci	.obj_cmpfn		= nfp_fl_obj_cmpfn,
48162306a36Sopenharmony_ci	.obj_hashfn		= nfp_fl_obj_hashfn,
48262306a36Sopenharmony_ci	.automatic_shrinking	= true,
48362306a36Sopenharmony_ci};
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ciconst struct rhashtable_params merge_table_params = {
48662306a36Sopenharmony_ci	.key_offset	= offsetof(struct nfp_merge_info, parent_ctx),
48762306a36Sopenharmony_ci	.head_offset	= offsetof(struct nfp_merge_info, ht_node),
48862306a36Sopenharmony_ci	.key_len	= sizeof(u64),
48962306a36Sopenharmony_ci};
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ciconst struct rhashtable_params nfp_zone_table_params = {
49262306a36Sopenharmony_ci	.head_offset		= offsetof(struct nfp_fl_ct_zone_entry, hash_node),
49362306a36Sopenharmony_ci	.key_len		= sizeof(u16),
49462306a36Sopenharmony_ci	.key_offset		= offsetof(struct nfp_fl_ct_zone_entry, zone),
49562306a36Sopenharmony_ci	.automatic_shrinking	= false,
49662306a36Sopenharmony_ci};
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ciconst struct rhashtable_params nfp_ct_map_params = {
49962306a36Sopenharmony_ci	.head_offset		= offsetof(struct nfp_fl_ct_map_entry, hash_node),
50062306a36Sopenharmony_ci	.key_len		= sizeof(unsigned long),
50162306a36Sopenharmony_ci	.key_offset		= offsetof(struct nfp_fl_ct_map_entry, cookie),
50262306a36Sopenharmony_ci	.automatic_shrinking	= true,
50362306a36Sopenharmony_ci};
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ciconst struct rhashtable_params neigh_table_params = {
50662306a36Sopenharmony_ci	.key_offset	= offsetof(struct nfp_neigh_entry, neigh_cookie),
50762306a36Sopenharmony_ci	.head_offset	= offsetof(struct nfp_neigh_entry, ht_node),
50862306a36Sopenharmony_ci	.key_len	= sizeof(unsigned long),
50962306a36Sopenharmony_ci};
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ciint nfp_flower_metadata_init(struct nfp_app *app, u64 host_ctx_count,
51262306a36Sopenharmony_ci			     unsigned int host_num_mems)
51362306a36Sopenharmony_ci{
51462306a36Sopenharmony_ci	struct nfp_flower_priv *priv = app->priv;
51562306a36Sopenharmony_ci	int err, stats_size;
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	hash_init(priv->mask_table);
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	err = rhashtable_init(&priv->flow_table, &nfp_flower_table_params);
52062306a36Sopenharmony_ci	if (err)
52162306a36Sopenharmony_ci		return err;
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	err = rhashtable_init(&priv->stats_ctx_table, &stats_ctx_table_params);
52462306a36Sopenharmony_ci	if (err)
52562306a36Sopenharmony_ci		goto err_free_flow_table;
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	err = rhashtable_init(&priv->merge_table, &merge_table_params);
52862306a36Sopenharmony_ci	if (err)
52962306a36Sopenharmony_ci		goto err_free_stats_ctx_table;
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	mutex_init(&priv->nfp_fl_lock);
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	err = rhashtable_init(&priv->ct_zone_table, &nfp_zone_table_params);
53462306a36Sopenharmony_ci	if (err)
53562306a36Sopenharmony_ci		goto err_free_merge_table;
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	err = rhashtable_init(&priv->ct_map_table, &nfp_ct_map_params);
53862306a36Sopenharmony_ci	if (err)
53962306a36Sopenharmony_ci		goto err_free_ct_zone_table;
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	err = rhashtable_init(&priv->neigh_table, &neigh_table_params);
54262306a36Sopenharmony_ci	if (err)
54362306a36Sopenharmony_ci		goto err_free_ct_map_table;
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	INIT_LIST_HEAD(&priv->predt_list);
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	get_random_bytes(&priv->mask_id_seed, sizeof(priv->mask_id_seed));
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	/* Init ring buffer and unallocated mask_ids. */
55062306a36Sopenharmony_ci	priv->mask_ids.mask_id_free_list.buf =
55162306a36Sopenharmony_ci		kmalloc_array(NFP_FLOWER_MASK_ENTRY_RS,
55262306a36Sopenharmony_ci			      NFP_FLOWER_MASK_ELEMENT_RS, GFP_KERNEL);
55362306a36Sopenharmony_ci	if (!priv->mask_ids.mask_id_free_list.buf)
55462306a36Sopenharmony_ci		goto err_free_neigh_table;
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	priv->mask_ids.init_unallocated = NFP_FLOWER_MASK_ENTRY_RS - 1;
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	/* Init timestamps for mask id*/
55962306a36Sopenharmony_ci	priv->mask_ids.last_used =
56062306a36Sopenharmony_ci		kmalloc_array(NFP_FLOWER_MASK_ENTRY_RS,
56162306a36Sopenharmony_ci			      sizeof(*priv->mask_ids.last_used), GFP_KERNEL);
56262306a36Sopenharmony_ci	if (!priv->mask_ids.last_used)
56362306a36Sopenharmony_ci		goto err_free_mask_id;
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	/* Init ring buffer and unallocated stats_ids. */
56662306a36Sopenharmony_ci	priv->stats_ids.free_list.buf =
56762306a36Sopenharmony_ci		vmalloc(array_size(NFP_FL_STATS_ELEM_RS,
56862306a36Sopenharmony_ci				   priv->stats_ring_size));
56962306a36Sopenharmony_ci	if (!priv->stats_ids.free_list.buf)
57062306a36Sopenharmony_ci		goto err_free_last_used;
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	priv->stats_ids.init_unalloc = div_u64(host_ctx_count, host_num_mems);
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	stats_size = FIELD_PREP(NFP_FL_STAT_ID_STAT, host_ctx_count) |
57562306a36Sopenharmony_ci		     FIELD_PREP(NFP_FL_STAT_ID_MU_NUM, host_num_mems - 1);
57662306a36Sopenharmony_ci	priv->stats = kvmalloc_array(stats_size, sizeof(struct nfp_fl_stats),
57762306a36Sopenharmony_ci				     GFP_KERNEL);
57862306a36Sopenharmony_ci	if (!priv->stats)
57962306a36Sopenharmony_ci		goto err_free_ring_buf;
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	spin_lock_init(&priv->stats_lock);
58262306a36Sopenharmony_ci	spin_lock_init(&priv->predt_lock);
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci	return 0;
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_cierr_free_ring_buf:
58762306a36Sopenharmony_ci	vfree(priv->stats_ids.free_list.buf);
58862306a36Sopenharmony_cierr_free_last_used:
58962306a36Sopenharmony_ci	kfree(priv->mask_ids.last_used);
59062306a36Sopenharmony_cierr_free_mask_id:
59162306a36Sopenharmony_ci	kfree(priv->mask_ids.mask_id_free_list.buf);
59262306a36Sopenharmony_cierr_free_neigh_table:
59362306a36Sopenharmony_ci	rhashtable_destroy(&priv->neigh_table);
59462306a36Sopenharmony_cierr_free_ct_map_table:
59562306a36Sopenharmony_ci	rhashtable_destroy(&priv->ct_map_table);
59662306a36Sopenharmony_cierr_free_ct_zone_table:
59762306a36Sopenharmony_ci	rhashtable_destroy(&priv->ct_zone_table);
59862306a36Sopenharmony_cierr_free_merge_table:
59962306a36Sopenharmony_ci	rhashtable_destroy(&priv->merge_table);
60062306a36Sopenharmony_cierr_free_stats_ctx_table:
60162306a36Sopenharmony_ci	rhashtable_destroy(&priv->stats_ctx_table);
60262306a36Sopenharmony_cierr_free_flow_table:
60362306a36Sopenharmony_ci	rhashtable_destroy(&priv->flow_table);
60462306a36Sopenharmony_ci	return -ENOMEM;
60562306a36Sopenharmony_ci}
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_cistatic void nfp_zone_table_entry_destroy(struct nfp_fl_ct_zone_entry *zt)
60862306a36Sopenharmony_ci{
60962306a36Sopenharmony_ci	if (!zt)
61062306a36Sopenharmony_ci		return;
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	if (!list_empty(&zt->pre_ct_list)) {
61362306a36Sopenharmony_ci		struct rhashtable *m_table = &zt->priv->ct_map_table;
61462306a36Sopenharmony_ci		struct nfp_fl_ct_flow_entry *entry, *tmp;
61562306a36Sopenharmony_ci		struct nfp_fl_ct_map_entry *map;
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci		WARN_ONCE(1, "pre_ct_list not empty as expected, cleaning up\n");
61862306a36Sopenharmony_ci		list_for_each_entry_safe(entry, tmp, &zt->pre_ct_list,
61962306a36Sopenharmony_ci					 list_node) {
62062306a36Sopenharmony_ci			map = rhashtable_lookup_fast(m_table,
62162306a36Sopenharmony_ci						     &entry->cookie,
62262306a36Sopenharmony_ci						     nfp_ct_map_params);
62362306a36Sopenharmony_ci			WARN_ON_ONCE(rhashtable_remove_fast(m_table,
62462306a36Sopenharmony_ci							    &map->hash_node,
62562306a36Sopenharmony_ci							    nfp_ct_map_params));
62662306a36Sopenharmony_ci			nfp_fl_ct_clean_flow_entry(entry);
62762306a36Sopenharmony_ci			kfree(map);
62862306a36Sopenharmony_ci		}
62962306a36Sopenharmony_ci	}
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	if (!list_empty(&zt->post_ct_list)) {
63262306a36Sopenharmony_ci		struct rhashtable *m_table = &zt->priv->ct_map_table;
63362306a36Sopenharmony_ci		struct nfp_fl_ct_flow_entry *entry, *tmp;
63462306a36Sopenharmony_ci		struct nfp_fl_ct_map_entry *map;
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci		WARN_ONCE(1, "post_ct_list not empty as expected, cleaning up\n");
63762306a36Sopenharmony_ci		list_for_each_entry_safe(entry, tmp, &zt->post_ct_list,
63862306a36Sopenharmony_ci					 list_node) {
63962306a36Sopenharmony_ci			map = rhashtable_lookup_fast(m_table,
64062306a36Sopenharmony_ci						     &entry->cookie,
64162306a36Sopenharmony_ci						     nfp_ct_map_params);
64262306a36Sopenharmony_ci			WARN_ON_ONCE(rhashtable_remove_fast(m_table,
64362306a36Sopenharmony_ci							    &map->hash_node,
64462306a36Sopenharmony_ci							    nfp_ct_map_params));
64562306a36Sopenharmony_ci			nfp_fl_ct_clean_flow_entry(entry);
64662306a36Sopenharmony_ci			kfree(map);
64762306a36Sopenharmony_ci		}
64862306a36Sopenharmony_ci	}
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	if (zt->nft) {
65162306a36Sopenharmony_ci		nf_flow_table_offload_del_cb(zt->nft,
65262306a36Sopenharmony_ci					     nfp_fl_ct_handle_nft_flow,
65362306a36Sopenharmony_ci					     zt);
65462306a36Sopenharmony_ci		zt->nft = NULL;
65562306a36Sopenharmony_ci	}
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	if (!list_empty(&zt->nft_flows_list)) {
65862306a36Sopenharmony_ci		struct rhashtable *m_table = &zt->priv->ct_map_table;
65962306a36Sopenharmony_ci		struct nfp_fl_ct_flow_entry *entry, *tmp;
66062306a36Sopenharmony_ci		struct nfp_fl_ct_map_entry *map;
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci		WARN_ONCE(1, "nft_flows_list not empty as expected, cleaning up\n");
66362306a36Sopenharmony_ci		list_for_each_entry_safe(entry, tmp, &zt->nft_flows_list,
66462306a36Sopenharmony_ci					 list_node) {
66562306a36Sopenharmony_ci			map = rhashtable_lookup_fast(m_table,
66662306a36Sopenharmony_ci						     &entry->cookie,
66762306a36Sopenharmony_ci						     nfp_ct_map_params);
66862306a36Sopenharmony_ci			WARN_ON_ONCE(rhashtable_remove_fast(m_table,
66962306a36Sopenharmony_ci							    &map->hash_node,
67062306a36Sopenharmony_ci							    nfp_ct_map_params));
67162306a36Sopenharmony_ci			nfp_fl_ct_clean_flow_entry(entry);
67262306a36Sopenharmony_ci			kfree(map);
67362306a36Sopenharmony_ci		}
67462306a36Sopenharmony_ci	}
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	rhashtable_free_and_destroy(&zt->tc_merge_tb,
67762306a36Sopenharmony_ci				    nfp_check_rhashtable_empty, NULL);
67862306a36Sopenharmony_ci	rhashtable_free_and_destroy(&zt->nft_merge_tb,
67962306a36Sopenharmony_ci				    nfp_check_rhashtable_empty, NULL);
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	kfree(zt);
68262306a36Sopenharmony_ci}
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_cistatic void nfp_free_zone_table_entry(void *ptr, void *arg)
68562306a36Sopenharmony_ci{
68662306a36Sopenharmony_ci	struct nfp_fl_ct_zone_entry *zt = ptr;
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci	nfp_zone_table_entry_destroy(zt);
68962306a36Sopenharmony_ci}
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_cistatic void nfp_free_map_table_entry(void *ptr, void *arg)
69262306a36Sopenharmony_ci{
69362306a36Sopenharmony_ci	struct nfp_fl_ct_map_entry *map = ptr;
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci	if (!map)
69662306a36Sopenharmony_ci		return;
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	kfree(map);
69962306a36Sopenharmony_ci}
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_civoid nfp_flower_metadata_cleanup(struct nfp_app *app)
70262306a36Sopenharmony_ci{
70362306a36Sopenharmony_ci	struct nfp_flower_priv *priv = app->priv;
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci	if (!priv)
70662306a36Sopenharmony_ci		return;
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci	rhashtable_free_and_destroy(&priv->flow_table,
70962306a36Sopenharmony_ci				    nfp_check_rhashtable_empty, NULL);
71062306a36Sopenharmony_ci	rhashtable_free_and_destroy(&priv->stats_ctx_table,
71162306a36Sopenharmony_ci				    nfp_check_rhashtable_empty, NULL);
71262306a36Sopenharmony_ci	rhashtable_free_and_destroy(&priv->merge_table,
71362306a36Sopenharmony_ci				    nfp_check_rhashtable_empty, NULL);
71462306a36Sopenharmony_ci	rhashtable_free_and_destroy(&priv->ct_zone_table,
71562306a36Sopenharmony_ci				    nfp_free_zone_table_entry, NULL);
71662306a36Sopenharmony_ci	nfp_zone_table_entry_destroy(priv->ct_zone_wc);
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	rhashtable_free_and_destroy(&priv->ct_map_table,
71962306a36Sopenharmony_ci				    nfp_free_map_table_entry, NULL);
72062306a36Sopenharmony_ci	rhashtable_free_and_destroy(&priv->neigh_table,
72162306a36Sopenharmony_ci				    nfp_check_rhashtable_empty, NULL);
72262306a36Sopenharmony_ci	kvfree(priv->stats);
72362306a36Sopenharmony_ci	kfree(priv->mask_ids.mask_id_free_list.buf);
72462306a36Sopenharmony_ci	kfree(priv->mask_ids.last_used);
72562306a36Sopenharmony_ci	vfree(priv->stats_ids.free_list.buf);
72662306a36Sopenharmony_ci}
727