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
9struct mod_hdr_key {
10 int num_actions;
11 void *actions;
12};
13
14struct 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
27static 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
33static 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
42void mlx5e_mod_hdr_tbl_init(struct mod_hdr_tbl *tbl)
43{
44 mutex_init(&tbl->lock);
45 hash_init(tbl->hlist);
46}
47
48void 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
54static 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
71struct mlx5e_mod_hdr_handle *
72mlx5e_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
129attach_header:
130 return mh;
131
132alloc_header_err:
133 complete_all(&mh->res_ready);
134attach_header_err:
135 mlx5e_mod_hdr_detach(mdev, tbl, mh);
136 return ERR_PTR(error: err);
137}
138
139void 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
154struct mlx5_modify_hdr *mlx5e_mod_hdr_get(struct mlx5e_mod_hdr_handle *mh)
155{
156 return mh->modify_hdr;
157}
158
159char *
160mlx5e_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
197out:
198 return mod_hdr_acts->actions + (mod_hdr_acts->num_actions * MLX5_MH_ACT_SZ);
199}
200
201void
202mlx5e_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
212char *
213mlx5e_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

source code of linux/drivers/net/ethernet/mellanox/mlx5/core/en/mod_hdr.c