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 
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 
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 
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 
48b2fdf3d0SPaul Blakey void mlx5e_mod_hdr_tbl_destroy(struct mod_hdr_tbl *tbl)
49b2fdf3d0SPaul Blakey {
50b2fdf3d0SPaul Blakey 	mutex_destroy(&tbl->lock);
51b2fdf3d0SPaul Blakey }
52b2fdf3d0SPaul Blakey 
53b2fdf3d0SPaul Blakey static struct mlx5e_mod_hdr_handle *mod_hdr_get(struct mod_hdr_tbl *tbl,
54b2fdf3d0SPaul Blakey 						struct mod_hdr_key *key,
55b2fdf3d0SPaul Blakey 						u32 hash_key)
56b2fdf3d0SPaul Blakey {
57b2fdf3d0SPaul Blakey 	struct mlx5e_mod_hdr_handle *mh, *found = NULL;
58b2fdf3d0SPaul Blakey 
59b2fdf3d0SPaul Blakey 	hash_for_each_possible(tbl->hlist, mh, mod_hdr_hlist, hash_key) {
60b2fdf3d0SPaul Blakey 		if (!cmp_mod_hdr_info(&mh->key, key)) {
61b2fdf3d0SPaul Blakey 			refcount_inc(&mh->refcnt);
62b2fdf3d0SPaul Blakey 			found = mh;
63b2fdf3d0SPaul Blakey 			break;
64b2fdf3d0SPaul Blakey 		}
65b2fdf3d0SPaul Blakey 	}
66b2fdf3d0SPaul Blakey 
67b2fdf3d0SPaul Blakey 	return found;
68b2fdf3d0SPaul Blakey }
69b2fdf3d0SPaul Blakey 
70b2fdf3d0SPaul Blakey struct mlx5e_mod_hdr_handle *
71b2fdf3d0SPaul Blakey mlx5e_mod_hdr_attach(struct mlx5_core_dev *mdev,
72b2fdf3d0SPaul Blakey 		     struct mod_hdr_tbl *tbl,
73b2fdf3d0SPaul Blakey 		     enum mlx5_flow_namespace_type namespace,
74b2fdf3d0SPaul Blakey 		     struct mlx5e_tc_mod_hdr_acts *mod_hdr_acts)
75b2fdf3d0SPaul Blakey {
76b2fdf3d0SPaul Blakey 	int num_actions, actions_size, err;
77b2fdf3d0SPaul Blakey 	struct mlx5e_mod_hdr_handle *mh;
78b2fdf3d0SPaul Blakey 	struct mod_hdr_key key;
79b2fdf3d0SPaul Blakey 	u32 hash_key;
80b2fdf3d0SPaul Blakey 
81b2fdf3d0SPaul Blakey 	num_actions  = mod_hdr_acts->num_actions;
82b2fdf3d0SPaul Blakey 	actions_size = MLX5_MH_ACT_SZ * num_actions;
83b2fdf3d0SPaul Blakey 
84b2fdf3d0SPaul Blakey 	key.actions = mod_hdr_acts->actions;
85b2fdf3d0SPaul Blakey 	key.num_actions = num_actions;
86b2fdf3d0SPaul Blakey 
87b2fdf3d0SPaul Blakey 	hash_key = hash_mod_hdr_info(&key);
88b2fdf3d0SPaul Blakey 
89b2fdf3d0SPaul Blakey 	mutex_lock(&tbl->lock);
90b2fdf3d0SPaul Blakey 	mh = mod_hdr_get(tbl, &key, hash_key);
91b2fdf3d0SPaul Blakey 	if (mh) {
92b2fdf3d0SPaul Blakey 		mutex_unlock(&tbl->lock);
93b2fdf3d0SPaul Blakey 		wait_for_completion(&mh->res_ready);
94b2fdf3d0SPaul Blakey 
95b2fdf3d0SPaul Blakey 		if (mh->compl_result < 0) {
96b2fdf3d0SPaul Blakey 			err = -EREMOTEIO;
97b2fdf3d0SPaul Blakey 			goto attach_header_err;
98b2fdf3d0SPaul Blakey 		}
99b2fdf3d0SPaul Blakey 		goto attach_header;
100b2fdf3d0SPaul Blakey 	}
101b2fdf3d0SPaul Blakey 
102b2fdf3d0SPaul Blakey 	mh = kzalloc(sizeof(*mh) + actions_size, GFP_KERNEL);
103b2fdf3d0SPaul Blakey 	if (!mh) {
104b2fdf3d0SPaul Blakey 		mutex_unlock(&tbl->lock);
105b2fdf3d0SPaul Blakey 		return ERR_PTR(-ENOMEM);
106b2fdf3d0SPaul Blakey 	}
107b2fdf3d0SPaul Blakey 
108b2fdf3d0SPaul Blakey 	mh->key.actions = (void *)mh + sizeof(*mh);
109b2fdf3d0SPaul Blakey 	memcpy(mh->key.actions, key.actions, actions_size);
110b2fdf3d0SPaul Blakey 	mh->key.num_actions = num_actions;
111b2fdf3d0SPaul Blakey 	refcount_set(&mh->refcnt, 1);
112b2fdf3d0SPaul Blakey 	init_completion(&mh->res_ready);
113b2fdf3d0SPaul Blakey 
114b2fdf3d0SPaul Blakey 	hash_add(tbl->hlist, &mh->mod_hdr_hlist, hash_key);
115b2fdf3d0SPaul Blakey 	mutex_unlock(&tbl->lock);
116b2fdf3d0SPaul Blakey 
117b2fdf3d0SPaul Blakey 	mh->modify_hdr = mlx5_modify_header_alloc(mdev, namespace,
118b2fdf3d0SPaul Blakey 						  mh->key.num_actions,
119b2fdf3d0SPaul Blakey 						  mh->key.actions);
120b2fdf3d0SPaul Blakey 	if (IS_ERR(mh->modify_hdr)) {
121b2fdf3d0SPaul Blakey 		err = PTR_ERR(mh->modify_hdr);
122b2fdf3d0SPaul Blakey 		mh->compl_result = err;
123b2fdf3d0SPaul Blakey 		goto alloc_header_err;
124b2fdf3d0SPaul Blakey 	}
125b2fdf3d0SPaul Blakey 	mh->compl_result = 1;
126b2fdf3d0SPaul Blakey 	complete_all(&mh->res_ready);
127b2fdf3d0SPaul Blakey 
128b2fdf3d0SPaul Blakey attach_header:
129b2fdf3d0SPaul Blakey 	return mh;
130b2fdf3d0SPaul Blakey 
131b2fdf3d0SPaul Blakey alloc_header_err:
132b2fdf3d0SPaul Blakey 	complete_all(&mh->res_ready);
133b2fdf3d0SPaul Blakey attach_header_err:
134b2fdf3d0SPaul Blakey 	mlx5e_mod_hdr_detach(mdev, tbl, mh);
135b2fdf3d0SPaul Blakey 	return ERR_PTR(err);
136b2fdf3d0SPaul Blakey }
137b2fdf3d0SPaul Blakey 
138b2fdf3d0SPaul Blakey void mlx5e_mod_hdr_detach(struct mlx5_core_dev *mdev,
139b2fdf3d0SPaul Blakey 			  struct mod_hdr_tbl *tbl,
140b2fdf3d0SPaul Blakey 			  struct mlx5e_mod_hdr_handle *mh)
141b2fdf3d0SPaul Blakey {
142b2fdf3d0SPaul Blakey 	if (!refcount_dec_and_mutex_lock(&mh->refcnt, &tbl->lock))
143b2fdf3d0SPaul Blakey 		return;
144b2fdf3d0SPaul Blakey 	hash_del(&mh->mod_hdr_hlist);
145b2fdf3d0SPaul Blakey 	mutex_unlock(&tbl->lock);
146b2fdf3d0SPaul Blakey 
147b2fdf3d0SPaul Blakey 	if (mh->compl_result > 0)
148b2fdf3d0SPaul Blakey 		mlx5_modify_header_dealloc(mdev, mh->modify_hdr);
149b2fdf3d0SPaul Blakey 
150b2fdf3d0SPaul Blakey 	kfree(mh);
151b2fdf3d0SPaul Blakey }
152b2fdf3d0SPaul Blakey 
153b2fdf3d0SPaul Blakey struct mlx5_modify_hdr *mlx5e_mod_hdr_get(struct mlx5e_mod_hdr_handle *mh)
154b2fdf3d0SPaul Blakey {
155b2fdf3d0SPaul Blakey 	return mh->modify_hdr;
156b2fdf3d0SPaul Blakey }
157b2fdf3d0SPaul Blakey 
158*2c0e5cf5SPaul Blakey char *
159*2c0e5cf5SPaul Blakey mlx5e_mod_hdr_alloc(struct mlx5_core_dev *mdev, int namespace,
160*2c0e5cf5SPaul Blakey 		    struct mlx5e_tc_mod_hdr_acts *mod_hdr_acts)
161*2c0e5cf5SPaul Blakey {
162*2c0e5cf5SPaul Blakey 	int new_num_actions, max_hw_actions;
163*2c0e5cf5SPaul Blakey 	size_t new_sz, old_sz;
164*2c0e5cf5SPaul Blakey 	void *ret;
165*2c0e5cf5SPaul Blakey 
166*2c0e5cf5SPaul Blakey 	if (mod_hdr_acts->num_actions < mod_hdr_acts->max_actions)
167*2c0e5cf5SPaul Blakey 		goto out;
168*2c0e5cf5SPaul Blakey 
169*2c0e5cf5SPaul Blakey 	max_hw_actions = mlx5e_mod_hdr_max_actions(mdev, namespace);
170*2c0e5cf5SPaul Blakey 	new_num_actions = min(max_hw_actions,
171*2c0e5cf5SPaul Blakey 			      mod_hdr_acts->actions ?
172*2c0e5cf5SPaul Blakey 			      mod_hdr_acts->max_actions * 2 : 1);
173*2c0e5cf5SPaul Blakey 	if (mod_hdr_acts->max_actions == new_num_actions)
174*2c0e5cf5SPaul Blakey 		return ERR_PTR(-ENOSPC);
175*2c0e5cf5SPaul Blakey 
176*2c0e5cf5SPaul Blakey 	new_sz = MLX5_MH_ACT_SZ * new_num_actions;
177*2c0e5cf5SPaul Blakey 	old_sz = mod_hdr_acts->max_actions * MLX5_MH_ACT_SZ;
178*2c0e5cf5SPaul Blakey 
179*2c0e5cf5SPaul Blakey 	ret = krealloc(mod_hdr_acts->actions, new_sz, GFP_KERNEL);
180*2c0e5cf5SPaul Blakey 	if (!ret)
181*2c0e5cf5SPaul Blakey 		return ERR_PTR(-ENOMEM);
182*2c0e5cf5SPaul Blakey 
183*2c0e5cf5SPaul Blakey 	memset(ret + old_sz, 0, new_sz - old_sz);
184*2c0e5cf5SPaul Blakey 	mod_hdr_acts->actions = ret;
185*2c0e5cf5SPaul Blakey 	mod_hdr_acts->max_actions = new_num_actions;
186*2c0e5cf5SPaul Blakey 
187*2c0e5cf5SPaul Blakey out:
188*2c0e5cf5SPaul Blakey 	return mod_hdr_acts->actions + (mod_hdr_acts->num_actions * MLX5_MH_ACT_SZ);
189*2c0e5cf5SPaul Blakey }
190*2c0e5cf5SPaul Blakey 
191*2c0e5cf5SPaul Blakey void
192*2c0e5cf5SPaul Blakey mlx5e_mod_hdr_dealloc(struct mlx5e_tc_mod_hdr_acts *mod_hdr_acts)
193*2c0e5cf5SPaul Blakey {
194*2c0e5cf5SPaul Blakey 	kfree(mod_hdr_acts->actions);
195*2c0e5cf5SPaul Blakey 	mod_hdr_acts->actions = NULL;
196*2c0e5cf5SPaul Blakey 	mod_hdr_acts->num_actions = 0;
197*2c0e5cf5SPaul Blakey 	mod_hdr_acts->max_actions = 0;
198*2c0e5cf5SPaul Blakey }
199*2c0e5cf5SPaul Blakey 
200*2c0e5cf5SPaul Blakey char *
201*2c0e5cf5SPaul Blakey mlx5e_mod_hdr_get_item(struct mlx5e_tc_mod_hdr_acts *mod_hdr_acts, int pos)
202*2c0e5cf5SPaul Blakey {
203*2c0e5cf5SPaul Blakey 	return mod_hdr_acts->actions + (pos * MLX5_MH_ACT_SZ);
204*2c0e5cf5SPaul Blakey }
205