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: key->actions, |
30 | length: key->num_actions * MLX5_MH_ACT_SZ, initval: 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(p: a->actions, q: b->actions, |
39 | size: 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 | WARN_ON(!hash_empty(tbl->hlist)); |
51 | mutex_destroy(lock: &tbl->lock); |
52 | } |
53 | |
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(a: &mh->key, b: key)) { |
62 | refcount_inc(r: &mh->refcnt); |
63 | found = mh; |
64 | break; |
65 | } |
66 | } |
67 | |
68 | return found; |
69 | } |
70 | |
71 | struct mlx5e_mod_hdr_handle * |
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: &key); |
89 | |
90 | mutex_lock(&tbl->lock); |
91 | mh = mod_hdr_get(tbl, key: &key, hash_key); |
92 | if (mh) { |
93 | mutex_unlock(lock: &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(size: sizeof(*mh) + actions_size, GFP_KERNEL); |
104 | if (!mh) { |
105 | mutex_unlock(lock: &tbl->lock); |
106 | return ERR_PTR(error: -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(r: &mh->refcnt, n: 1); |
113 | init_completion(x: &mh->res_ready); |
114 | |
115 | hash_add(tbl->hlist, &mh->mod_hdr_hlist, hash_key); |
116 | mutex_unlock(lock: &tbl->lock); |
117 | |
118 | mh->modify_hdr = mlx5_modify_header_alloc(dev: mdev, ns_type: namespace, |
119 | num_actions: mh->key.num_actions, |
120 | modify_actions: mh->key.actions); |
121 | if (IS_ERR(ptr: mh->modify_hdr)) { |
122 | err = PTR_ERR(ptr: 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 | : |
130 | return mh; |
131 | |
132 | : |
133 | complete_all(&mh->res_ready); |
134 | : |
135 | mlx5e_mod_hdr_detach(mdev, tbl, mh); |
136 | return ERR_PTR(error: err); |
137 | } |
138 | |
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(r: &mh->refcnt, lock: &tbl->lock)) |
144 | return; |
145 | hash_del(node: &mh->mod_hdr_hlist); |
146 | mutex_unlock(lock: &tbl->lock); |
147 | |
148 | if (mh->compl_result > 0) |
149 | mlx5_modify_header_dealloc(dev: mdev, modify_hdr: mh->modify_hdr); |
150 | |
151 | kfree(objp: mh); |
152 | } |
153 | |
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 * |
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(error: -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(size: 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(objp: mod_hdr_acts->actions, new_size: 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(error: -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 |
202 | mlx5e_mod_hdr_dealloc(struct mlx5e_tc_mod_hdr_acts *mod_hdr_acts) |
203 | { |
204 | if (!mod_hdr_acts->is_static) |
205 | kfree(objp: 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 * |
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 | |