1b2fdf3d0SPaul Blakey // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
2b2fdf3d0SPaul Blakey // Copyright (c) 2020 Mellanox Technologies
3b2fdf3d0SPaul Blakey 
4b2fdf3d0SPaul Blakey #include <linux/jhash.h>
5b2fdf3d0SPaul Blakey #include "mod_hdr.h"
6b2fdf3d0SPaul Blakey 
7b2fdf3d0SPaul Blakey #define MLX5_MH_ACT_SZ MLX5_UN_SZ_BYTES(set_add_copy_action_in_auto)
8b2fdf3d0SPaul Blakey 
9b2fdf3d0SPaul Blakey struct mod_hdr_key {
10b2fdf3d0SPaul Blakey 	int num_actions;
11b2fdf3d0SPaul Blakey 	void *actions;
12b2fdf3d0SPaul Blakey };
13b2fdf3d0SPaul Blakey 
14b2fdf3d0SPaul Blakey struct mlx5e_mod_hdr_handle {
15b2fdf3d0SPaul Blakey 	/* a node of a hash table which keeps all the mod_hdr entries */
16b2fdf3d0SPaul Blakey 	struct hlist_node mod_hdr_hlist;
17b2fdf3d0SPaul Blakey 
18b2fdf3d0SPaul Blakey 	struct mod_hdr_key key;
19b2fdf3d0SPaul Blakey 
20b2fdf3d0SPaul Blakey 	struct mlx5_modify_hdr *modify_hdr;
21b2fdf3d0SPaul Blakey 
22b2fdf3d0SPaul Blakey 	refcount_t refcnt;
23b2fdf3d0SPaul Blakey 	struct completion res_ready;
24b2fdf3d0SPaul Blakey 	int compl_result;
25b2fdf3d0SPaul Blakey };
26b2fdf3d0SPaul Blakey 
hash_mod_hdr_info(struct mod_hdr_key * key)27b2fdf3d0SPaul Blakey static u32 hash_mod_hdr_info(struct mod_hdr_key *key)
28b2fdf3d0SPaul Blakey {
29b2fdf3d0SPaul Blakey 	return jhash(key->actions,
30b2fdf3d0SPaul Blakey 		     key->num_actions * MLX5_MH_ACT_SZ, 0);
31b2fdf3d0SPaul Blakey }
32b2fdf3d0SPaul Blakey 
cmp_mod_hdr_info(struct mod_hdr_key * a,struct mod_hdr_key * b)33b2fdf3d0SPaul Blakey static int cmp_mod_hdr_info(struct mod_hdr_key *a, struct mod_hdr_key *b)
34b2fdf3d0SPaul Blakey {
35b2fdf3d0SPaul Blakey 	if (a->num_actions != b->num_actions)
36b2fdf3d0SPaul Blakey 		return 1;
37b2fdf3d0SPaul Blakey 
38b2fdf3d0SPaul Blakey 	return memcmp(a->actions, b->actions,
39b2fdf3d0SPaul Blakey 		      a->num_actions * MLX5_MH_ACT_SZ);
40b2fdf3d0SPaul Blakey }
41b2fdf3d0SPaul Blakey 
mlx5e_mod_hdr_tbl_init(struct mod_hdr_tbl * tbl)42b2fdf3d0SPaul Blakey void mlx5e_mod_hdr_tbl_init(struct mod_hdr_tbl *tbl)
43b2fdf3d0SPaul Blakey {
44b2fdf3d0SPaul Blakey 	mutex_init(&tbl->lock);
45b2fdf3d0SPaul Blakey 	hash_init(tbl->hlist);
46b2fdf3d0SPaul Blakey }
47b2fdf3d0SPaul Blakey 
mlx5e_mod_hdr_tbl_destroy(struct mod_hdr_tbl * tbl)48b2fdf3d0SPaul Blakey void mlx5e_mod_hdr_tbl_destroy(struct mod_hdr_tbl *tbl)
49b2fdf3d0SPaul Blakey {
50*2a1f4fedSRoi Dayan 	WARN_ON(!hash_empty(tbl->hlist));
51b2fdf3d0SPaul Blakey 	mutex_destroy(&tbl->lock);
52b2fdf3d0SPaul Blakey }
53b2fdf3d0SPaul Blakey 
mod_hdr_get(struct mod_hdr_tbl * tbl,struct mod_hdr_key * key,u32 hash_key)54b2fdf3d0SPaul Blakey static struct mlx5e_mod_hdr_handle *mod_hdr_get(struct mod_hdr_tbl *tbl,
55b2fdf3d0SPaul Blakey 						struct mod_hdr_key *key,
56b2fdf3d0SPaul Blakey 						u32 hash_key)
57b2fdf3d0SPaul Blakey {
58b2fdf3d0SPaul Blakey 	struct mlx5e_mod_hdr_handle *mh, *found = NULL;
59b2fdf3d0SPaul Blakey 
60b2fdf3d0SPaul Blakey 	hash_for_each_possible(tbl->hlist, mh, mod_hdr_hlist, hash_key) {
61b2fdf3d0SPaul Blakey 		if (!cmp_mod_hdr_info(&mh->key, key)) {
62b2fdf3d0SPaul Blakey 			refcount_inc(&mh->refcnt);
63b2fdf3d0SPaul Blakey 			found = mh;
64b2fdf3d0SPaul Blakey 			break;
65b2fdf3d0SPaul Blakey 		}
66b2fdf3d0SPaul Blakey 	}
67b2fdf3d0SPaul Blakey 
68b2fdf3d0SPaul Blakey 	return found;
69b2fdf3d0SPaul Blakey }
70b2fdf3d0SPaul Blakey 
71b2fdf3d0SPaul Blakey struct mlx5e_mod_hdr_handle *
mlx5e_mod_hdr_attach(struct mlx5_core_dev * mdev,struct mod_hdr_tbl * tbl,enum mlx5_flow_namespace_type namespace,struct mlx5e_tc_mod_hdr_acts * mod_hdr_acts)72b2fdf3d0SPaul Blakey mlx5e_mod_hdr_attach(struct mlx5_core_dev *mdev,
73b2fdf3d0SPaul Blakey 		     struct mod_hdr_tbl *tbl,
74b2fdf3d0SPaul Blakey 		     enum mlx5_flow_namespace_type namespace,
75b2fdf3d0SPaul Blakey 		     struct mlx5e_tc_mod_hdr_acts *mod_hdr_acts)
76b2fdf3d0SPaul Blakey {
77b2fdf3d0SPaul Blakey 	int num_actions, actions_size, err;
78b2fdf3d0SPaul Blakey 	struct mlx5e_mod_hdr_handle *mh;
79b2fdf3d0SPaul Blakey 	struct mod_hdr_key key;
80b2fdf3d0SPaul Blakey 	u32 hash_key;
81b2fdf3d0SPaul Blakey 
82b2fdf3d0SPaul Blakey 	num_actions  = mod_hdr_acts->num_actions;
83b2fdf3d0SPaul Blakey 	actions_size = MLX5_MH_ACT_SZ * num_actions;
84b2fdf3d0SPaul Blakey 
85b2fdf3d0SPaul Blakey 	key.actions = mod_hdr_acts->actions;
86b2fdf3d0SPaul Blakey 	key.num_actions = num_actions;
87b2fdf3d0SPaul Blakey 
88b2fdf3d0SPaul Blakey 	hash_key = hash_mod_hdr_info(&key);
89b2fdf3d0SPaul Blakey 
90b2fdf3d0SPaul Blakey 	mutex_lock(&tbl->lock);
91b2fdf3d0SPaul Blakey 	mh = mod_hdr_get(tbl, &key, hash_key);
92b2fdf3d0SPaul Blakey 	if (mh) {
93b2fdf3d0SPaul Blakey 		mutex_unlock(&tbl->lock);
94b2fdf3d0SPaul Blakey 		wait_for_completion(&mh->res_ready);
95b2fdf3d0SPaul Blakey 
96b2fdf3d0SPaul Blakey 		if (mh->compl_result < 0) {
97b2fdf3d0SPaul Blakey 			err = -EREMOTEIO;
98b2fdf3d0SPaul Blakey 			goto attach_header_err;
99b2fdf3d0SPaul Blakey 		}
100b2fdf3d0SPaul Blakey 		goto attach_header;
101b2fdf3d0SPaul Blakey 	}
102b2fdf3d0SPaul Blakey 
103b2fdf3d0SPaul Blakey 	mh = kzalloc(sizeof(*mh) + actions_size, GFP_KERNEL);
104b2fdf3d0SPaul Blakey 	if (!mh) {
105b2fdf3d0SPaul Blakey 		mutex_unlock(&tbl->lock);
106b2fdf3d0SPaul Blakey 		return ERR_PTR(-ENOMEM);
107b2fdf3d0SPaul Blakey 	}
108b2fdf3d0SPaul Blakey 
109b2fdf3d0SPaul Blakey 	mh->key.actions = (void *)mh + sizeof(*mh);
110b2fdf3d0SPaul Blakey 	memcpy(mh->key.actions, key.actions, actions_size);
111b2fdf3d0SPaul Blakey 	mh->key.num_actions = num_actions;
112b2fdf3d0SPaul Blakey 	refcount_set(&mh->refcnt, 1);
113b2fdf3d0SPaul Blakey 	init_completion(&mh->res_ready);
114b2fdf3d0SPaul Blakey 
115b2fdf3d0SPaul Blakey 	hash_add(tbl->hlist, &mh->mod_hdr_hlist, hash_key);
116b2fdf3d0SPaul Blakey 	mutex_unlock(&tbl->lock);
117b2fdf3d0SPaul Blakey 
118b2fdf3d0SPaul Blakey 	mh->modify_hdr = mlx5_modify_header_alloc(mdev, namespace,
119b2fdf3d0SPaul Blakey 						  mh->key.num_actions,
120b2fdf3d0SPaul Blakey 						  mh->key.actions);
121b2fdf3d0SPaul Blakey 	if (IS_ERR(mh->modify_hdr)) {
122b2fdf3d0SPaul Blakey 		err = PTR_ERR(mh->modify_hdr);
123b2fdf3d0SPaul Blakey 		mh->compl_result = err;
124b2fdf3d0SPaul Blakey 		goto alloc_header_err;
125b2fdf3d0SPaul Blakey 	}
126b2fdf3d0SPaul Blakey 	mh->compl_result = 1;
127b2fdf3d0SPaul Blakey 	complete_all(&mh->res_ready);
128b2fdf3d0SPaul Blakey 
129b2fdf3d0SPaul Blakey attach_header:
130b2fdf3d0SPaul Blakey 	return mh;
131b2fdf3d0SPaul Blakey 
132b2fdf3d0SPaul Blakey alloc_header_err:
133b2fdf3d0SPaul Blakey 	complete_all(&mh->res_ready);
134b2fdf3d0SPaul Blakey attach_header_err:
135b2fdf3d0SPaul Blakey 	mlx5e_mod_hdr_detach(mdev, tbl, mh);
136b2fdf3d0SPaul Blakey 	return ERR_PTR(err);
137b2fdf3d0SPaul Blakey }
138b2fdf3d0SPaul Blakey 
mlx5e_mod_hdr_detach(struct mlx5_core_dev * mdev,struct mod_hdr_tbl * tbl,struct mlx5e_mod_hdr_handle * mh)139b2fdf3d0SPaul Blakey void mlx5e_mod_hdr_detach(struct mlx5_core_dev *mdev,
140b2fdf3d0SPaul Blakey 			  struct mod_hdr_tbl *tbl,
141b2fdf3d0SPaul Blakey 			  struct mlx5e_mod_hdr_handle *mh)
142b2fdf3d0SPaul Blakey {
143b2fdf3d0SPaul Blakey 	if (!refcount_dec_and_mutex_lock(&mh->refcnt, &tbl->lock))
144b2fdf3d0SPaul Blakey 		return;
145b2fdf3d0SPaul Blakey 	hash_del(&mh->mod_hdr_hlist);
146b2fdf3d0SPaul Blakey 	mutex_unlock(&tbl->lock);
147b2fdf3d0SPaul Blakey 
148b2fdf3d0SPaul Blakey 	if (mh->compl_result > 0)
149b2fdf3d0SPaul Blakey 		mlx5_modify_header_dealloc(mdev, mh->modify_hdr);
150b2fdf3d0SPaul Blakey 
151b2fdf3d0SPaul Blakey 	kfree(mh);
152b2fdf3d0SPaul Blakey }
153b2fdf3d0SPaul Blakey 
mlx5e_mod_hdr_get(struct mlx5e_mod_hdr_handle * mh)154b2fdf3d0SPaul Blakey struct mlx5_modify_hdr *mlx5e_mod_hdr_get(struct mlx5e_mod_hdr_handle *mh)
155b2fdf3d0SPaul Blakey {
156b2fdf3d0SPaul Blakey 	return mh->modify_hdr;
157b2fdf3d0SPaul Blakey }
158b2fdf3d0SPaul Blakey 
1592c0e5cf5SPaul Blakey char *
mlx5e_mod_hdr_alloc(struct mlx5_core_dev * mdev,int namespace,struct mlx5e_tc_mod_hdr_acts * mod_hdr_acts)1602c0e5cf5SPaul Blakey mlx5e_mod_hdr_alloc(struct mlx5_core_dev *mdev, int namespace,
1612c0e5cf5SPaul Blakey 		    struct mlx5e_tc_mod_hdr_acts *mod_hdr_acts)
1622c0e5cf5SPaul Blakey {
1632c0e5cf5SPaul Blakey 	int new_num_actions, max_hw_actions;
1642c0e5cf5SPaul Blakey 	size_t new_sz, old_sz;
1652c0e5cf5SPaul Blakey 	void *ret;
1662c0e5cf5SPaul Blakey 
1672c0e5cf5SPaul Blakey 	if (mod_hdr_acts->num_actions < mod_hdr_acts->max_actions)
1682c0e5cf5SPaul Blakey 		goto out;
1692c0e5cf5SPaul Blakey 
1702c0e5cf5SPaul Blakey 	max_hw_actions = mlx5e_mod_hdr_max_actions(mdev, namespace);
1712c0e5cf5SPaul Blakey 	new_num_actions = min(max_hw_actions,
1722c0e5cf5SPaul Blakey 			      mod_hdr_acts->actions ?
1732c0e5cf5SPaul Blakey 			      mod_hdr_acts->max_actions * 2 : 1);
1742c0e5cf5SPaul Blakey 	if (mod_hdr_acts->max_actions == new_num_actions)
1752c0e5cf5SPaul Blakey 		return ERR_PTR(-ENOSPC);
1762c0e5cf5SPaul Blakey 
1772c0e5cf5SPaul Blakey 	new_sz = MLX5_MH_ACT_SZ * new_num_actions;
1782c0e5cf5SPaul Blakey 	old_sz = mod_hdr_acts->max_actions * MLX5_MH_ACT_SZ;
1792c0e5cf5SPaul Blakey 
1801cfd3490SPaul Blakey 	if (mod_hdr_acts->is_static) {
1811cfd3490SPaul Blakey 		ret = kzalloc(new_sz, GFP_KERNEL);
1821cfd3490SPaul Blakey 		if (ret) {
1831cfd3490SPaul Blakey 			memcpy(ret, mod_hdr_acts->actions, old_sz);
1841cfd3490SPaul Blakey 			mod_hdr_acts->is_static = false;
1851cfd3490SPaul Blakey 		}
1861cfd3490SPaul Blakey 	} else {
1872c0e5cf5SPaul Blakey 		ret = krealloc(mod_hdr_acts->actions, new_sz, GFP_KERNEL);
1881cfd3490SPaul Blakey 		if (ret)
1891cfd3490SPaul Blakey 			memset(ret + old_sz, 0, new_sz - old_sz);
1901cfd3490SPaul Blakey 	}
1912c0e5cf5SPaul Blakey 	if (!ret)
1922c0e5cf5SPaul Blakey 		return ERR_PTR(-ENOMEM);
1932c0e5cf5SPaul Blakey 
1942c0e5cf5SPaul Blakey 	mod_hdr_acts->actions = ret;
1952c0e5cf5SPaul Blakey 	mod_hdr_acts->max_actions = new_num_actions;
1962c0e5cf5SPaul Blakey 
1972c0e5cf5SPaul Blakey out:
1982c0e5cf5SPaul Blakey 	return mod_hdr_acts->actions + (mod_hdr_acts->num_actions * MLX5_MH_ACT_SZ);
1992c0e5cf5SPaul Blakey }
2002c0e5cf5SPaul Blakey 
2012c0e5cf5SPaul Blakey void
mlx5e_mod_hdr_dealloc(struct mlx5e_tc_mod_hdr_acts * mod_hdr_acts)2022c0e5cf5SPaul Blakey mlx5e_mod_hdr_dealloc(struct mlx5e_tc_mod_hdr_acts *mod_hdr_acts)
2032c0e5cf5SPaul Blakey {
2041cfd3490SPaul Blakey 	if (!mod_hdr_acts->is_static)
2052c0e5cf5SPaul Blakey 		kfree(mod_hdr_acts->actions);
2061cfd3490SPaul Blakey 
2072c0e5cf5SPaul Blakey 	mod_hdr_acts->actions = NULL;
2082c0e5cf5SPaul Blakey 	mod_hdr_acts->num_actions = 0;
2092c0e5cf5SPaul Blakey 	mod_hdr_acts->max_actions = 0;
2102c0e5cf5SPaul Blakey }
2112c0e5cf5SPaul Blakey 
2122c0e5cf5SPaul Blakey char *
mlx5e_mod_hdr_get_item(struct mlx5e_tc_mod_hdr_acts * mod_hdr_acts,int pos)2132c0e5cf5SPaul Blakey mlx5e_mod_hdr_get_item(struct mlx5e_tc_mod_hdr_acts *mod_hdr_acts, int pos)
2142c0e5cf5SPaul Blakey {
2152c0e5cf5SPaul Blakey 	return mod_hdr_acts->actions + (pos * MLX5_MH_ACT_SZ);
2162c0e5cf5SPaul Blakey }
217