1
2
3
4#include <linux/mlx5/driver.h>
5#include "eswitch.h"
6#include "priv.h"
7#include "sf/dev/dev.h"
8#include "mlx5_ifc_vhca_event.h"
9#include "vhca_event.h"
10#include "ecpf.h"
11
12struct mlx5_sf {
13 struct devlink_port dl_port;
14 unsigned int port_index;
15 u32 controller;
16 u16 id;
17 u16 hw_fn_id;
18 u16 hw_state;
19};
20
21struct mlx5_sf_table {
22 struct mlx5_core_dev *dev;
23 struct xarray port_indices;
24 refcount_t refcount;
25 struct completion disable_complete;
26 struct mutex sf_state_lock;
27 struct notifier_block esw_nb;
28 struct notifier_block vhca_nb;
29 u8 ecpu: 1;
30};
31
32static struct mlx5_sf *
33mlx5_sf_lookup_by_index(struct mlx5_sf_table *table, unsigned int port_index)
34{
35 return xa_load(&table->port_indices, port_index);
36}
37
38static struct mlx5_sf *
39mlx5_sf_lookup_by_function_id(struct mlx5_sf_table *table, unsigned int fn_id)
40{
41 unsigned long index;
42 struct mlx5_sf *sf;
43
44 xa_for_each(&table->port_indices, index, sf) {
45 if (sf->hw_fn_id == fn_id)
46 return sf;
47 }
48 return NULL;
49}
50
51static int mlx5_sf_id_insert(struct mlx5_sf_table *table, struct mlx5_sf *sf)
52{
53 return xa_insert(&table->port_indices, sf->port_index, sf, GFP_KERNEL);
54}
55
56static void mlx5_sf_id_erase(struct mlx5_sf_table *table, struct mlx5_sf *sf)
57{
58 xa_erase(&table->port_indices, sf->port_index);
59}
60
61static struct mlx5_sf *
62mlx5_sf_alloc(struct mlx5_sf_table *table, struct mlx5_eswitch *esw,
63 u32 controller, u32 sfnum, struct netlink_ext_ack *extack)
64{
65 unsigned int dl_port_index;
66 struct mlx5_sf *sf;
67 u16 hw_fn_id;
68 int id_err;
69 int err;
70
71 if (!mlx5_esw_offloads_controller_valid(esw, controller)) {
72 NL_SET_ERR_MSG_MOD(extack, "Invalid controller number");
73 return ERR_PTR(-EINVAL);
74 }
75
76 id_err = mlx5_sf_hw_table_sf_alloc(table->dev, controller, sfnum);
77 if (id_err < 0) {
78 err = id_err;
79 goto id_err;
80 }
81
82 sf = kzalloc(sizeof(*sf), GFP_KERNEL);
83 if (!sf) {
84 err = -ENOMEM;
85 goto alloc_err;
86 }
87 sf->id = id_err;
88 hw_fn_id = mlx5_sf_sw_to_hw_id(table->dev, controller, sf->id);
89 dl_port_index = mlx5_esw_vport_to_devlink_port_index(table->dev, hw_fn_id);
90 sf->port_index = dl_port_index;
91 sf->hw_fn_id = hw_fn_id;
92 sf->hw_state = MLX5_VHCA_STATE_ALLOCATED;
93 sf->controller = controller;
94
95 err = mlx5_sf_id_insert(table, sf);
96 if (err)
97 goto insert_err;
98
99 return sf;
100
101insert_err:
102 kfree(sf);
103alloc_err:
104 mlx5_sf_hw_table_sf_free(table->dev, controller, id_err);
105id_err:
106 if (err == -EEXIST)
107 NL_SET_ERR_MSG_MOD(extack, "SF already exist. Choose different sfnum");
108 return ERR_PTR(err);
109}
110
111static void mlx5_sf_free(struct mlx5_sf_table *table, struct mlx5_sf *sf)
112{
113 mlx5_sf_id_erase(table, sf);
114 mlx5_sf_hw_table_sf_free(table->dev, sf->controller, sf->id);
115 kfree(sf);
116}
117
118static struct mlx5_sf_table *mlx5_sf_table_try_get(struct mlx5_core_dev *dev)
119{
120 struct mlx5_sf_table *table = dev->priv.sf_table;
121
122 if (!table)
123 return NULL;
124
125 return refcount_inc_not_zero(&table->refcount) ? table : NULL;
126}
127
128static void mlx5_sf_table_put(struct mlx5_sf_table *table)
129{
130 if (refcount_dec_and_test(&table->refcount))
131 complete(&table->disable_complete);
132}
133
134static enum devlink_port_fn_state mlx5_sf_to_devlink_state(u8 hw_state)
135{
136 switch (hw_state) {
137 case MLX5_VHCA_STATE_ACTIVE:
138 case MLX5_VHCA_STATE_IN_USE:
139 return DEVLINK_PORT_FN_STATE_ACTIVE;
140 case MLX5_VHCA_STATE_INVALID:
141 case MLX5_VHCA_STATE_ALLOCATED:
142 case MLX5_VHCA_STATE_TEARDOWN_REQUEST:
143 default:
144 return DEVLINK_PORT_FN_STATE_INACTIVE;
145 }
146}
147
148static enum devlink_port_fn_opstate mlx5_sf_to_devlink_opstate(u8 hw_state)
149{
150 switch (hw_state) {
151 case MLX5_VHCA_STATE_IN_USE:
152 case MLX5_VHCA_STATE_TEARDOWN_REQUEST:
153 return DEVLINK_PORT_FN_OPSTATE_ATTACHED;
154 case MLX5_VHCA_STATE_INVALID:
155 case MLX5_VHCA_STATE_ALLOCATED:
156 case MLX5_VHCA_STATE_ACTIVE:
157 default:
158 return DEVLINK_PORT_FN_OPSTATE_DETACHED;
159 }
160}
161
162static bool mlx5_sf_is_active(const struct mlx5_sf *sf)
163{
164 return sf->hw_state == MLX5_VHCA_STATE_ACTIVE || sf->hw_state == MLX5_VHCA_STATE_IN_USE;
165}
166
167int mlx5_devlink_sf_port_fn_state_get(struct devlink *devlink, struct devlink_port *dl_port,
168 enum devlink_port_fn_state *state,
169 enum devlink_port_fn_opstate *opstate,
170 struct netlink_ext_ack *extack)
171{
172 struct mlx5_core_dev *dev = devlink_priv(devlink);
173 struct mlx5_sf_table *table;
174 struct mlx5_sf *sf;
175 int err = 0;
176
177 table = mlx5_sf_table_try_get(dev);
178 if (!table)
179 return -EOPNOTSUPP;
180
181 sf = mlx5_sf_lookup_by_index(table, dl_port->index);
182 if (!sf) {
183 err = -EOPNOTSUPP;
184 goto sf_err;
185 }
186 mutex_lock(&table->sf_state_lock);
187 *state = mlx5_sf_to_devlink_state(sf->hw_state);
188 *opstate = mlx5_sf_to_devlink_opstate(sf->hw_state);
189 mutex_unlock(&table->sf_state_lock);
190sf_err:
191 mlx5_sf_table_put(table);
192 return err;
193}
194
195static int mlx5_sf_activate(struct mlx5_core_dev *dev, struct mlx5_sf *sf,
196 struct netlink_ext_ack *extack)
197{
198 int err;
199
200 if (mlx5_sf_is_active(sf))
201 return 0;
202 if (sf->hw_state != MLX5_VHCA_STATE_ALLOCATED) {
203 NL_SET_ERR_MSG_MOD(extack, "SF is inactivated but it is still attached");
204 return -EBUSY;
205 }
206
207 err = mlx5_cmd_sf_enable_hca(dev, sf->hw_fn_id);
208 if (err)
209 return err;
210
211 sf->hw_state = MLX5_VHCA_STATE_ACTIVE;
212 return 0;
213}
214
215static int mlx5_sf_deactivate(struct mlx5_core_dev *dev, struct mlx5_sf *sf)
216{
217 int err;
218
219 if (!mlx5_sf_is_active(sf))
220 return 0;
221
222 err = mlx5_cmd_sf_disable_hca(dev, sf->hw_fn_id);
223 if (err)
224 return err;
225
226 sf->hw_state = MLX5_VHCA_STATE_TEARDOWN_REQUEST;
227 return 0;
228}
229
230static int mlx5_sf_state_set(struct mlx5_core_dev *dev, struct mlx5_sf_table *table,
231 struct mlx5_sf *sf,
232 enum devlink_port_fn_state state,
233 struct netlink_ext_ack *extack)
234{
235 int err = 0;
236
237 mutex_lock(&table->sf_state_lock);
238 if (state == mlx5_sf_to_devlink_state(sf->hw_state))
239 goto out;
240 if (state == DEVLINK_PORT_FN_STATE_ACTIVE)
241 err = mlx5_sf_activate(dev, sf, extack);
242 else if (state == DEVLINK_PORT_FN_STATE_INACTIVE)
243 err = mlx5_sf_deactivate(dev, sf);
244 else
245 err = -EINVAL;
246out:
247 mutex_unlock(&table->sf_state_lock);
248 return err;
249}
250
251int mlx5_devlink_sf_port_fn_state_set(struct devlink *devlink, struct devlink_port *dl_port,
252 enum devlink_port_fn_state state,
253 struct netlink_ext_ack *extack)
254{
255 struct mlx5_core_dev *dev = devlink_priv(devlink);
256 struct mlx5_sf_table *table;
257 struct mlx5_sf *sf;
258 int err;
259
260 table = mlx5_sf_table_try_get(dev);
261 if (!table) {
262 NL_SET_ERR_MSG_MOD(extack,
263 "Port state set is only supported in eswitch switchdev mode or SF ports are disabled.");
264 return -EOPNOTSUPP;
265 }
266 sf = mlx5_sf_lookup_by_index(table, dl_port->index);
267 if (!sf) {
268 err = -ENODEV;
269 goto out;
270 }
271
272 err = mlx5_sf_state_set(dev, table, sf, state, extack);
273out:
274 mlx5_sf_table_put(table);
275 return err;
276}
277
278static int mlx5_sf_add(struct mlx5_core_dev *dev, struct mlx5_sf_table *table,
279 const struct devlink_port_new_attrs *new_attr,
280 struct netlink_ext_ack *extack,
281 unsigned int *new_port_index)
282{
283 struct mlx5_eswitch *esw = dev->priv.eswitch;
284 struct mlx5_sf *sf;
285 int err;
286
287 sf = mlx5_sf_alloc(table, esw, new_attr->controller, new_attr->sfnum, extack);
288 if (IS_ERR(sf))
289 return PTR_ERR(sf);
290
291 err = mlx5_esw_offloads_sf_vport_enable(esw, &sf->dl_port, sf->hw_fn_id,
292 new_attr->controller, new_attr->sfnum);
293 if (err)
294 goto esw_err;
295 *new_port_index = sf->port_index;
296 return 0;
297
298esw_err:
299 mlx5_sf_free(table, sf);
300 return err;
301}
302
303static int
304mlx5_sf_new_check_attr(struct mlx5_core_dev *dev, const struct devlink_port_new_attrs *new_attr,
305 struct netlink_ext_ack *extack)
306{
307 if (new_attr->flavour != DEVLINK_PORT_FLAVOUR_PCI_SF) {
308 NL_SET_ERR_MSG_MOD(extack, "Driver supports only SF port addition");
309 return -EOPNOTSUPP;
310 }
311 if (new_attr->port_index_valid) {
312 NL_SET_ERR_MSG_MOD(extack,
313 "Driver does not support user defined port index assignment");
314 return -EOPNOTSUPP;
315 }
316 if (!new_attr->sfnum_valid) {
317 NL_SET_ERR_MSG_MOD(extack,
318 "User must provide unique sfnum. Driver does not support auto assignment");
319 return -EOPNOTSUPP;
320 }
321 if (new_attr->controller_valid && new_attr->controller &&
322 !mlx5_core_is_ecpf_esw_manager(dev)) {
323 NL_SET_ERR_MSG_MOD(extack, "External controller is unsupported");
324 return -EOPNOTSUPP;
325 }
326 if (new_attr->pfnum != PCI_FUNC(dev->pdev->devfn)) {
327 NL_SET_ERR_MSG_MOD(extack, "Invalid pfnum supplied");
328 return -EOPNOTSUPP;
329 }
330 return 0;
331}
332
333int mlx5_devlink_sf_port_new(struct devlink *devlink,
334 const struct devlink_port_new_attrs *new_attr,
335 struct netlink_ext_ack *extack,
336 unsigned int *new_port_index)
337{
338 struct mlx5_core_dev *dev = devlink_priv(devlink);
339 struct mlx5_sf_table *table;
340 int err;
341
342 err = mlx5_sf_new_check_attr(dev, new_attr, extack);
343 if (err)
344 return err;
345
346 table = mlx5_sf_table_try_get(dev);
347 if (!table) {
348 NL_SET_ERR_MSG_MOD(extack,
349 "Port add is only supported in eswitch switchdev mode or SF ports are disabled.");
350 return -EOPNOTSUPP;
351 }
352 err = mlx5_sf_add(dev, table, new_attr, extack, new_port_index);
353 mlx5_sf_table_put(table);
354 return err;
355}
356
357static void mlx5_sf_dealloc(struct mlx5_sf_table *table, struct mlx5_sf *sf)
358{
359 if (sf->hw_state == MLX5_VHCA_STATE_ALLOCATED) {
360 mlx5_sf_free(table, sf);
361 } else if (mlx5_sf_is_active(sf)) {
362
363
364
365
366
367 mlx5_cmd_sf_disable_hca(table->dev, sf->hw_fn_id);
368 mlx5_sf_hw_table_sf_deferred_free(table->dev, sf->controller, sf->id);
369 kfree(sf);
370 } else {
371 mlx5_sf_hw_table_sf_deferred_free(table->dev, sf->controller, sf->id);
372 kfree(sf);
373 }
374}
375
376int mlx5_devlink_sf_port_del(struct devlink *devlink, unsigned int port_index,
377 struct netlink_ext_ack *extack)
378{
379 struct mlx5_core_dev *dev = devlink_priv(devlink);
380 struct mlx5_eswitch *esw = dev->priv.eswitch;
381 struct mlx5_sf_table *table;
382 struct mlx5_sf *sf;
383 int err = 0;
384
385 table = mlx5_sf_table_try_get(dev);
386 if (!table) {
387 NL_SET_ERR_MSG_MOD(extack,
388 "Port del is only supported in eswitch switchdev mode or SF ports are disabled.");
389 return -EOPNOTSUPP;
390 }
391 sf = mlx5_sf_lookup_by_index(table, port_index);
392 if (!sf) {
393 err = -ENODEV;
394 goto sf_err;
395 }
396
397 mlx5_esw_offloads_sf_vport_disable(esw, sf->hw_fn_id);
398 mlx5_sf_id_erase(table, sf);
399
400 mutex_lock(&table->sf_state_lock);
401 mlx5_sf_dealloc(table, sf);
402 mutex_unlock(&table->sf_state_lock);
403sf_err:
404 mlx5_sf_table_put(table);
405 return err;
406}
407
408static bool mlx5_sf_state_update_check(const struct mlx5_sf *sf, u8 new_state)
409{
410 if (sf->hw_state == MLX5_VHCA_STATE_ACTIVE && new_state == MLX5_VHCA_STATE_IN_USE)
411 return true;
412
413 if (sf->hw_state == MLX5_VHCA_STATE_IN_USE && new_state == MLX5_VHCA_STATE_ACTIVE)
414 return true;
415
416 if (sf->hw_state == MLX5_VHCA_STATE_TEARDOWN_REQUEST &&
417 new_state == MLX5_VHCA_STATE_ALLOCATED)
418 return true;
419
420 return false;
421}
422
423static int mlx5_sf_vhca_event(struct notifier_block *nb, unsigned long opcode, void *data)
424{
425 struct mlx5_sf_table *table = container_of(nb, struct mlx5_sf_table, vhca_nb);
426 const struct mlx5_vhca_state_event *event = data;
427 bool update = false;
428 struct mlx5_sf *sf;
429
430 table = mlx5_sf_table_try_get(table->dev);
431 if (!table)
432 return 0;
433
434 mutex_lock(&table->sf_state_lock);
435 sf = mlx5_sf_lookup_by_function_id(table, event->function_id);
436 if (!sf)
437 goto sf_err;
438
439
440
441
442 update = mlx5_sf_state_update_check(sf, event->new_vhca_state);
443 if (update)
444 sf->hw_state = event->new_vhca_state;
445sf_err:
446 mutex_unlock(&table->sf_state_lock);
447 mlx5_sf_table_put(table);
448 return 0;
449}
450
451static void mlx5_sf_table_enable(struct mlx5_sf_table *table)
452{
453 init_completion(&table->disable_complete);
454 refcount_set(&table->refcount, 1);
455}
456
457static void mlx5_sf_deactivate_all(struct mlx5_sf_table *table)
458{
459 struct mlx5_eswitch *esw = table->dev->priv.eswitch;
460 unsigned long index;
461 struct mlx5_sf *sf;
462
463
464
465
466 xa_for_each(&table->port_indices, index, sf) {
467 mlx5_esw_offloads_sf_vport_disable(esw, sf->hw_fn_id);
468 mlx5_sf_id_erase(table, sf);
469 mlx5_sf_dealloc(table, sf);
470 }
471}
472
473static void mlx5_sf_table_disable(struct mlx5_sf_table *table)
474{
475 if (!refcount_read(&table->refcount))
476 return;
477
478
479
480
481 mlx5_sf_table_put(table);
482 wait_for_completion(&table->disable_complete);
483
484 mlx5_sf_deactivate_all(table);
485}
486
487static int mlx5_sf_esw_event(struct notifier_block *nb, unsigned long event, void *data)
488{
489 struct mlx5_sf_table *table = container_of(nb, struct mlx5_sf_table, esw_nb);
490 const struct mlx5_esw_event_info *mode = data;
491
492 switch (mode->new_mode) {
493 case MLX5_ESWITCH_OFFLOADS:
494 mlx5_sf_table_enable(table);
495 break;
496 case MLX5_ESWITCH_NONE:
497 mlx5_sf_table_disable(table);
498 break;
499 default:
500 break;
501 }
502
503 return 0;
504}
505
506static bool mlx5_sf_table_supported(const struct mlx5_core_dev *dev)
507{
508 return dev->priv.eswitch && MLX5_ESWITCH_MANAGER(dev) &&
509 mlx5_sf_hw_table_supported(dev);
510}
511
512int mlx5_sf_table_init(struct mlx5_core_dev *dev)
513{
514 struct mlx5_sf_table *table;
515 int err;
516
517 if (!mlx5_sf_table_supported(dev) || !mlx5_vhca_event_supported(dev))
518 return 0;
519
520 table = kzalloc(sizeof(*table), GFP_KERNEL);
521 if (!table)
522 return -ENOMEM;
523
524 mutex_init(&table->sf_state_lock);
525 table->dev = dev;
526 xa_init(&table->port_indices);
527 dev->priv.sf_table = table;
528 refcount_set(&table->refcount, 0);
529 table->esw_nb.notifier_call = mlx5_sf_esw_event;
530 err = mlx5_esw_event_notifier_register(dev->priv.eswitch, &table->esw_nb);
531 if (err)
532 goto reg_err;
533
534 table->vhca_nb.notifier_call = mlx5_sf_vhca_event;
535 err = mlx5_vhca_event_notifier_register(table->dev, &table->vhca_nb);
536 if (err)
537 goto vhca_err;
538
539 return 0;
540
541vhca_err:
542 mlx5_esw_event_notifier_unregister(dev->priv.eswitch, &table->esw_nb);
543reg_err:
544 mutex_destroy(&table->sf_state_lock);
545 kfree(table);
546 dev->priv.sf_table = NULL;
547 return err;
548}
549
550void mlx5_sf_table_cleanup(struct mlx5_core_dev *dev)
551{
552 struct mlx5_sf_table *table = dev->priv.sf_table;
553
554 if (!table)
555 return;
556
557 mlx5_vhca_event_notifier_unregister(table->dev, &table->vhca_nb);
558 mlx5_esw_event_notifier_unregister(dev->priv.eswitch, &table->esw_nb);
559 WARN_ON(refcount_read(&table->refcount));
560 mutex_destroy(&table->sf_state_lock);
561 WARN_ON(!xa_empty(&table->port_indices));
562 kfree(table);
563}
564