Merge tag 'clk-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/clk/linux
[sfrench/cifs-2.6.git] / drivers / net / ethernet / mellanox / mlx5 / core / steering / dr_arg.c
1 // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
2 // Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3
4 #include "dr_types.h"
5
6 #define DR_ICM_MODIFY_HDR_GRANULARITY_4K 12
7
8 /* modify-header arg pool */
9 enum dr_arg_chunk_size {
10         DR_ARG_CHUNK_SIZE_1,
11         DR_ARG_CHUNK_SIZE_MIN = DR_ARG_CHUNK_SIZE_1, /* keep updated when changing */
12         DR_ARG_CHUNK_SIZE_2,
13         DR_ARG_CHUNK_SIZE_3,
14         DR_ARG_CHUNK_SIZE_4,
15         DR_ARG_CHUNK_SIZE_MAX,
16 };
17
18 /* argument pool area */
19 struct dr_arg_pool {
20         enum dr_arg_chunk_size log_chunk_size;
21         struct mlx5dr_domain *dmn;
22         struct list_head free_list;
23         struct mutex mutex; /* protect arg pool */
24 };
25
26 struct mlx5dr_arg_mgr {
27         struct mlx5dr_domain *dmn;
28         struct dr_arg_pool *pools[DR_ARG_CHUNK_SIZE_MAX];
29 };
30
31 static int dr_arg_pool_alloc_objs(struct dr_arg_pool *pool)
32 {
33         struct mlx5dr_arg_obj *arg_obj, *tmp_arg;
34         struct list_head cur_list;
35         u16 object_range;
36         int num_of_objects;
37         u32 obj_id = 0;
38         int i, ret;
39
40         INIT_LIST_HEAD(&cur_list);
41
42         object_range =
43                 pool->dmn->info.caps.log_header_modify_argument_granularity;
44
45         object_range =
46                 max_t(u32, pool->dmn->info.caps.log_header_modify_argument_granularity,
47                       DR_ICM_MODIFY_HDR_GRANULARITY_4K);
48         object_range =
49                 min_t(u32, pool->dmn->info.caps.log_header_modify_argument_max_alloc,
50                       object_range);
51
52         if (pool->log_chunk_size > object_range) {
53                 mlx5dr_err(pool->dmn, "Required chunk size (%d) is not supported\n",
54                            pool->log_chunk_size);
55                 return -ENOMEM;
56         }
57
58         num_of_objects = (1 << (object_range - pool->log_chunk_size));
59         /* Only one devx object per range */
60         ret = mlx5dr_cmd_create_modify_header_arg(pool->dmn->mdev,
61                                                   object_range,
62                                                   pool->dmn->pdn,
63                                                   &obj_id);
64         if (ret) {
65                 mlx5dr_err(pool->dmn, "failed allocating object with range: %d:\n",
66                            object_range);
67                 return -EAGAIN;
68         }
69
70         for (i = 0; i < num_of_objects; i++) {
71                 arg_obj = kzalloc(sizeof(*arg_obj), GFP_KERNEL);
72                 if (!arg_obj) {
73                         ret = -ENOMEM;
74                         goto clean_arg_obj;
75                 }
76
77                 arg_obj->log_chunk_size = pool->log_chunk_size;
78
79                 list_add_tail(&arg_obj->list_node, &cur_list);
80
81                 arg_obj->obj_id = obj_id;
82                 arg_obj->obj_offset = i * (1 << pool->log_chunk_size);
83         }
84         list_splice_tail_init(&cur_list, &pool->free_list);
85
86         return 0;
87
88 clean_arg_obj:
89         mlx5dr_cmd_destroy_modify_header_arg(pool->dmn->mdev, obj_id);
90         list_for_each_entry_safe(arg_obj, tmp_arg, &cur_list, list_node) {
91                 list_del(&arg_obj->list_node);
92                 kfree(arg_obj);
93         }
94         return ret;
95 }
96
97 static struct mlx5dr_arg_obj *dr_arg_pool_get_arg_obj(struct dr_arg_pool *pool)
98 {
99         struct mlx5dr_arg_obj *arg_obj = NULL;
100         int ret;
101
102         mutex_lock(&pool->mutex);
103         if (list_empty(&pool->free_list)) {
104                 ret = dr_arg_pool_alloc_objs(pool);
105                 if (ret)
106                         goto out;
107         }
108
109         arg_obj = list_first_entry_or_null(&pool->free_list,
110                                            struct mlx5dr_arg_obj,
111                                            list_node);
112         WARN(!arg_obj, "couldn't get dr arg obj from pool");
113
114         if (arg_obj)
115                 list_del_init(&arg_obj->list_node);
116
117 out:
118         mutex_unlock(&pool->mutex);
119         return arg_obj;
120 }
121
122 static void dr_arg_pool_put_arg_obj(struct dr_arg_pool *pool,
123                                     struct mlx5dr_arg_obj *arg_obj)
124 {
125         mutex_lock(&pool->mutex);
126         list_add(&arg_obj->list_node, &pool->free_list);
127         mutex_unlock(&pool->mutex);
128 }
129
130 static struct dr_arg_pool *dr_arg_pool_create(struct mlx5dr_domain *dmn,
131                                               enum dr_arg_chunk_size chunk_size)
132 {
133         struct dr_arg_pool *pool;
134
135         pool = kzalloc(sizeof(*pool), GFP_KERNEL);
136         if (!pool)
137                 return NULL;
138
139         pool->dmn = dmn;
140
141         INIT_LIST_HEAD(&pool->free_list);
142         mutex_init(&pool->mutex);
143
144         pool->log_chunk_size = chunk_size;
145         if (dr_arg_pool_alloc_objs(pool))
146                 goto free_pool;
147
148         return pool;
149
150 free_pool:
151         kfree(pool);
152
153         return NULL;
154 }
155
156 static void dr_arg_pool_destroy(struct dr_arg_pool *pool)
157 {
158         struct mlx5dr_arg_obj *arg_obj, *tmp_arg;
159
160         list_for_each_entry_safe(arg_obj, tmp_arg, &pool->free_list, list_node) {
161                 list_del(&arg_obj->list_node);
162                 if (!arg_obj->obj_offset) /* the first in range */
163                         mlx5dr_cmd_destroy_modify_header_arg(pool->dmn->mdev, arg_obj->obj_id);
164                 kfree(arg_obj);
165         }
166
167         mutex_destroy(&pool->mutex);
168         kfree(pool);
169 }
170
171 static enum dr_arg_chunk_size dr_arg_get_chunk_size(u16 num_of_actions)
172 {
173         if (num_of_actions <= 8)
174                 return DR_ARG_CHUNK_SIZE_1;
175         if (num_of_actions <= 16)
176                 return DR_ARG_CHUNK_SIZE_2;
177         if (num_of_actions <= 32)
178                 return DR_ARG_CHUNK_SIZE_3;
179         if (num_of_actions <= 64)
180                 return DR_ARG_CHUNK_SIZE_4;
181
182         return DR_ARG_CHUNK_SIZE_MAX;
183 }
184
185 u32 mlx5dr_arg_get_obj_id(struct mlx5dr_arg_obj *arg_obj)
186 {
187         return (arg_obj->obj_id + arg_obj->obj_offset);
188 }
189
190 struct mlx5dr_arg_obj *mlx5dr_arg_get_obj(struct mlx5dr_arg_mgr *mgr,
191                                           u16 num_of_actions,
192                                           u8 *data)
193 {
194         u32 size = dr_arg_get_chunk_size(num_of_actions);
195         struct mlx5dr_arg_obj *arg_obj;
196         int ret;
197
198         if (size >= DR_ARG_CHUNK_SIZE_MAX)
199                 return NULL;
200
201         arg_obj = dr_arg_pool_get_arg_obj(mgr->pools[size]);
202         if (!arg_obj) {
203                 mlx5dr_err(mgr->dmn, "Failed allocating args object for modify header\n");
204                 return NULL;
205         }
206
207         /* write it into the hw */
208         ret = mlx5dr_send_postsend_args(mgr->dmn,
209                                         mlx5dr_arg_get_obj_id(arg_obj),
210                                         num_of_actions, data);
211         if (ret) {
212                 mlx5dr_err(mgr->dmn, "Failed writing args object\n");
213                 goto put_obj;
214         }
215
216         return arg_obj;
217
218 put_obj:
219         mlx5dr_arg_put_obj(mgr, arg_obj);
220         return NULL;
221 }
222
223 void mlx5dr_arg_put_obj(struct mlx5dr_arg_mgr *mgr,
224                         struct mlx5dr_arg_obj *arg_obj)
225 {
226         dr_arg_pool_put_arg_obj(mgr->pools[arg_obj->log_chunk_size], arg_obj);
227 }
228
229 struct mlx5dr_arg_mgr*
230 mlx5dr_arg_mgr_create(struct mlx5dr_domain *dmn)
231 {
232         struct mlx5dr_arg_mgr *pool_mgr;
233         int i;
234
235         if (!mlx5dr_domain_is_support_ptrn_arg(dmn))
236                 return NULL;
237
238         pool_mgr = kzalloc(sizeof(*pool_mgr), GFP_KERNEL);
239         if (!pool_mgr)
240                 return NULL;
241
242         pool_mgr->dmn = dmn;
243
244         for (i = 0; i < DR_ARG_CHUNK_SIZE_MAX; i++) {
245                 pool_mgr->pools[i] = dr_arg_pool_create(dmn, i);
246                 if (!pool_mgr->pools[i])
247                         goto clean_pools;
248         }
249
250         return pool_mgr;
251
252 clean_pools:
253         for (i--; i >= 0; i--)
254                 dr_arg_pool_destroy(pool_mgr->pools[i]);
255
256         kfree(pool_mgr);
257         return NULL;
258 }
259
260 void mlx5dr_arg_mgr_destroy(struct mlx5dr_arg_mgr *mgr)
261 {
262         struct dr_arg_pool **pools;
263         int i;
264
265         if (!mgr)
266                 return;
267
268         pools = mgr->pools;
269         for (i = 0; i < DR_ARG_CHUNK_SIZE_MAX; i++)
270                 dr_arg_pool_destroy(pools[i]);
271
272         kfree(mgr);
273 }