1 // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
2 // Copyright (c) 2020 Mellanox Technologies
3 
4 #include <linux/jhash.h>
5 #include "mod_hdr.h"
6 
7 #define MLX5_MH_ACT_SZ MLX5_UN_SZ_BYTES(set_add_copy_action_in_auto)
8 
9 struct mod_hdr_key {
10 	int num_actions;
11 	void *actions;
12 };
13 
14 struct mlx5e_mod_hdr_handle {
15 	/* a node of a hash table which keeps all the mod_hdr entries */
16 	struct hlist_node mod_hdr_hlist;
17 
18 	struct mod_hdr_key key;
19 
20 	struct mlx5_modify_hdr *modify_hdr;
21 
22 	refcount_t refcnt;
23 	struct completion res_ready;
24 	int compl_result;
25 };
26 
hash_mod_hdr_info(struct mod_hdr_key * key)27 static u32 hash_mod_hdr_info(struct mod_hdr_key *key)
28 {
29 	return jhash(key->actions,
30 		     key->num_actions * MLX5_MH_ACT_SZ, 0);
31 }
32 
cmp_mod_hdr_info(struct mod_hdr_key * a,struct mod_hdr_key * b)33 static int cmp_mod_hdr_info(struct mod_hdr_key *a, struct mod_hdr_key *b)
34 {
35 	if (a->num_actions != b->num_actions)
36 		return 1;
37 
38 	return memcmp(a->actions, b->actions,
39 		      a->num_actions * MLX5_MH_ACT_SZ);
40 }
41 
mlx5e_mod_hdr_tbl_init(struct mod_hdr_tbl * tbl)42 void mlx5e_mod_hdr_tbl_init(struct mod_hdr_tbl *tbl)
43 {
44 	mutex_init(&tbl->lock);
45 	hash_init(tbl->hlist);
46 }
47 
mlx5e_mod_hdr_tbl_destroy(struct mod_hdr_tbl * tbl)48 void mlx5e_mod_hdr_tbl_destroy(struct mod_hdr_tbl *tbl)
49 {
50 	WARN_ON(!hash_empty(tbl->hlist));
51 	mutex_destroy(&tbl->lock);
52 }
53 
mod_hdr_get(struct mod_hdr_tbl * tbl,struct mod_hdr_key * key,u32 hash_key)54 static struct mlx5e_mod_hdr_handle *mod_hdr_get(struct mod_hdr_tbl *tbl,
55 						struct mod_hdr_key *key,
56 						u32 hash_key)
57 {
58 	struct mlx5e_mod_hdr_handle *mh, *found = NULL;
59 
60 	hash_for_each_possible(tbl->hlist, mh, mod_hdr_hlist, hash_key) {
61 		if (!cmp_mod_hdr_info(&mh->key, key)) {
62 			refcount_inc(&mh->refcnt);
63 			found = mh;
64 			break;
65 		}
66 	}
67 
68 	return found;
69 }
70 
71 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)72 mlx5e_mod_hdr_attach(struct mlx5_core_dev *mdev,
73 		     struct mod_hdr_tbl *tbl,
74 		     enum mlx5_flow_namespace_type namespace,
75 		     struct mlx5e_tc_mod_hdr_acts *mod_hdr_acts)
76 {
77 	int num_actions, actions_size, err;
78 	struct mlx5e_mod_hdr_handle *mh;
79 	struct mod_hdr_key key;
80 	u32 hash_key;
81 
82 	num_actions  = mod_hdr_acts->num_actions;
83 	actions_size = MLX5_MH_ACT_SZ * num_actions;
84 
85 	key.actions = mod_hdr_acts->actions;
86 	key.num_actions = num_actions;
87 
88 	hash_key = hash_mod_hdr_info(&key);
89 
90 	mutex_lock(&tbl->lock);
91 	mh = mod_hdr_get(tbl, &key, hash_key);
92 	if (mh) {
93 		mutex_unlock(&tbl->lock);
94 		wait_for_completion(&mh->res_ready);
95 
96 		if (mh->compl_result < 0) {
97 			err = -EREMOTEIO;
98 			goto attach_header_err;
99 		}
100 		goto attach_header;
101 	}
102 
103 	mh = kzalloc(sizeof(*mh) + actions_size, GFP_KERNEL);
104 	if (!mh) {
105 		mutex_unlock(&tbl->lock);
106 		return ERR_PTR(-ENOMEM);
107 	}
108 
109 	mh->key.actions = (void *)mh + sizeof(*mh);
110 	memcpy(mh->key.actions, key.actions, actions_size);
111 	mh->key.num_actions = num_actions;
112 	refcount_set(&mh->refcnt, 1);
113 	init_completion(&mh->res_ready);
114 
115 	hash_add(tbl->hlist, &mh->mod_hdr_hlist, hash_key);
116 	mutex_unlock(&tbl->lock);
117 
118 	mh->modify_hdr = mlx5_modify_header_alloc(mdev, namespace,
119 						  mh->key.num_actions,
120 						  mh->key.actions);
121 	if (IS_ERR(mh->modify_hdr)) {
122 		err = PTR_ERR(mh->modify_hdr);
123 		mh->compl_result = err;
124 		goto alloc_header_err;
125 	}
126 	mh->compl_result = 1;
127 	complete_all(&mh->res_ready);
128 
129 attach_header:
130 	return mh;
131 
132 alloc_header_err:
133 	complete_all(&mh->res_ready);
134 attach_header_err:
135 	mlx5e_mod_hdr_detach(mdev, tbl, mh);
136 	return ERR_PTR(err);
137 }
138 
mlx5e_mod_hdr_detach(struct mlx5_core_dev * mdev,struct mod_hdr_tbl * tbl,struct mlx5e_mod_hdr_handle * mh)139 void mlx5e_mod_hdr_detach(struct mlx5_core_dev *mdev,
140 			  struct mod_hdr_tbl *tbl,
141 			  struct mlx5e_mod_hdr_handle *mh)
142 {
143 	if (!refcount_dec_and_mutex_lock(&mh->refcnt, &tbl->lock))
144 		return;
145 	hash_del(&mh->mod_hdr_hlist);
146 	mutex_unlock(&tbl->lock);
147 
148 	if (mh->compl_result > 0)
149 		mlx5_modify_header_dealloc(mdev, mh->modify_hdr);
150 
151 	kfree(mh);
152 }
153 
mlx5e_mod_hdr_get(struct mlx5e_mod_hdr_handle * mh)154 struct mlx5_modify_hdr *mlx5e_mod_hdr_get(struct mlx5e_mod_hdr_handle *mh)
155 {
156 	return mh->modify_hdr;
157 }
158 
159 char *
mlx5e_mod_hdr_alloc(struct mlx5_core_dev * mdev,int namespace,struct mlx5e_tc_mod_hdr_acts * mod_hdr_acts)160 mlx5e_mod_hdr_alloc(struct mlx5_core_dev *mdev, int namespace,
161 		    struct mlx5e_tc_mod_hdr_acts *mod_hdr_acts)
162 {
163 	int new_num_actions, max_hw_actions;
164 	size_t new_sz, old_sz;
165 	void *ret;
166 
167 	if (mod_hdr_acts->num_actions < mod_hdr_acts->max_actions)
168 		goto out;
169 
170 	max_hw_actions = mlx5e_mod_hdr_max_actions(mdev, namespace);
171 	new_num_actions = min(max_hw_actions,
172 			      mod_hdr_acts->actions ?
173 			      mod_hdr_acts->max_actions * 2 : 1);
174 	if (mod_hdr_acts->max_actions == new_num_actions)
175 		return ERR_PTR(-ENOSPC);
176 
177 	new_sz = MLX5_MH_ACT_SZ * new_num_actions;
178 	old_sz = mod_hdr_acts->max_actions * MLX5_MH_ACT_SZ;
179 
180 	if (mod_hdr_acts->is_static) {
181 		ret = kzalloc(new_sz, GFP_KERNEL);
182 		if (ret) {
183 			memcpy(ret, mod_hdr_acts->actions, old_sz);
184 			mod_hdr_acts->is_static = false;
185 		}
186 	} else {
187 		ret = krealloc(mod_hdr_acts->actions, new_sz, GFP_KERNEL);
188 		if (ret)
189 			memset(ret + old_sz, 0, new_sz - old_sz);
190 	}
191 	if (!ret)
192 		return ERR_PTR(-ENOMEM);
193 
194 	mod_hdr_acts->actions = ret;
195 	mod_hdr_acts->max_actions = new_num_actions;
196 
197 out:
198 	return mod_hdr_acts->actions + (mod_hdr_acts->num_actions * MLX5_MH_ACT_SZ);
199 }
200 
201 void
mlx5e_mod_hdr_dealloc(struct mlx5e_tc_mod_hdr_acts * mod_hdr_acts)202 mlx5e_mod_hdr_dealloc(struct mlx5e_tc_mod_hdr_acts *mod_hdr_acts)
203 {
204 	if (!mod_hdr_acts->is_static)
205 		kfree(mod_hdr_acts->actions);
206 
207 	mod_hdr_acts->actions = NULL;
208 	mod_hdr_acts->num_actions = 0;
209 	mod_hdr_acts->max_actions = 0;
210 }
211 
212 char *
mlx5e_mod_hdr_get_item(struct mlx5e_tc_mod_hdr_acts * mod_hdr_acts,int pos)213 mlx5e_mod_hdr_get_item(struct mlx5e_tc_mod_hdr_acts *mod_hdr_acts, int pos)
214 {
215 	return mod_hdr_acts->actions + (pos * MLX5_MH_ACT_SZ);
216 }
217