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