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 mutex_destroy(&tbl->lock);
51 }
52
mod_hdr_get(struct mod_hdr_tbl * tbl,struct mod_hdr_key * key,u32 hash_key)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 *
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)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
mlx5e_mod_hdr_detach(struct mlx5_core_dev * mdev,struct mod_hdr_tbl * tbl,struct mlx5e_mod_hdr_handle * mh)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
mlx5e_mod_hdr_get(struct mlx5e_mod_hdr_handle * mh)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 *
mlx5e_mod_hdr_alloc(struct mlx5_core_dev * mdev,int namespace,struct mlx5e_tc_mod_hdr_acts * mod_hdr_acts)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
mlx5e_mod_hdr_dealloc(struct mlx5e_tc_mod_hdr_acts * mod_hdr_acts)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 *
mlx5e_mod_hdr_get_item(struct mlx5e_tc_mod_hdr_acts * mod_hdr_acts,int pos)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