1 // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
2 /* Copyright (c) 2020 Mellanox Technologies Ltd */
4 #include <linux/mlx5/driver.h>
7 #include "sf/dev/dev.h"
8 #include "mlx5_ifc_vhca_event.h"
9 #include "vhca_event.h"
13 struct devlink_port dl_port;
14 unsigned int port_index;
20 struct mlx5_sf_table {
21 struct mlx5_core_dev *dev; /* To refer from notifier context. */
22 struct xarray port_indices; /* port index based lookup. */
24 struct completion disable_complete;
25 struct mutex sf_state_lock; /* Serializes sf state among user cmds & vhca event handler. */
26 struct notifier_block esw_nb;
27 struct notifier_block vhca_nb;
31 static struct mlx5_sf *
32 mlx5_sf_lookup_by_index(struct mlx5_sf_table *table, unsigned int port_index)
34 return xa_load(&table->port_indices, port_index);
37 static struct mlx5_sf *
38 mlx5_sf_lookup_by_function_id(struct mlx5_sf_table *table, unsigned int fn_id)
43 xa_for_each(&table->port_indices, index, sf) {
44 if (sf->hw_fn_id == fn_id)
50 static int mlx5_sf_id_insert(struct mlx5_sf_table *table, struct mlx5_sf *sf)
52 return xa_insert(&table->port_indices, sf->port_index, sf, GFP_KERNEL);
55 static void mlx5_sf_id_erase(struct mlx5_sf_table *table, struct mlx5_sf *sf)
57 xa_erase(&table->port_indices, sf->port_index);
60 static struct mlx5_sf *
61 mlx5_sf_alloc(struct mlx5_sf_table *table, u32 sfnum, struct netlink_ext_ack *extack)
63 unsigned int dl_port_index;
69 id_err = mlx5_sf_hw_table_sf_alloc(table->dev, sfnum);
75 sf = kzalloc(sizeof(*sf), GFP_KERNEL);
81 hw_fn_id = mlx5_sf_sw_to_hw_id(table->dev, sf->id);
82 dl_port_index = mlx5_esw_vport_to_devlink_port_index(table->dev, hw_fn_id);
83 sf->port_index = dl_port_index;
84 sf->hw_fn_id = hw_fn_id;
85 sf->hw_state = MLX5_VHCA_STATE_ALLOCATED;
87 err = mlx5_sf_id_insert(table, sf);
96 mlx5_sf_hw_table_sf_free(table->dev, id_err);
99 NL_SET_ERR_MSG_MOD(extack, "SF already exist. Choose different sfnum");
103 static void mlx5_sf_free(struct mlx5_sf_table *table, struct mlx5_sf *sf)
105 mlx5_sf_id_erase(table, sf);
106 mlx5_sf_hw_table_sf_free(table->dev, sf->id);
110 static struct mlx5_sf_table *mlx5_sf_table_try_get(struct mlx5_core_dev *dev)
112 struct mlx5_sf_table *table = dev->priv.sf_table;
117 return refcount_inc_not_zero(&table->refcount) ? table : NULL;
120 static void mlx5_sf_table_put(struct mlx5_sf_table *table)
122 if (refcount_dec_and_test(&table->refcount))
123 complete(&table->disable_complete);
126 static enum devlink_port_fn_state mlx5_sf_to_devlink_state(u8 hw_state)
129 case MLX5_VHCA_STATE_ACTIVE:
130 case MLX5_VHCA_STATE_IN_USE:
131 case MLX5_VHCA_STATE_TEARDOWN_REQUEST:
132 return DEVLINK_PORT_FN_STATE_ACTIVE;
133 case MLX5_VHCA_STATE_INVALID:
134 case MLX5_VHCA_STATE_ALLOCATED:
136 return DEVLINK_PORT_FN_STATE_INACTIVE;
140 static enum devlink_port_fn_opstate mlx5_sf_to_devlink_opstate(u8 hw_state)
143 case MLX5_VHCA_STATE_IN_USE:
144 case MLX5_VHCA_STATE_TEARDOWN_REQUEST:
145 return DEVLINK_PORT_FN_OPSTATE_ATTACHED;
146 case MLX5_VHCA_STATE_INVALID:
147 case MLX5_VHCA_STATE_ALLOCATED:
148 case MLX5_VHCA_STATE_ACTIVE:
150 return DEVLINK_PORT_FN_OPSTATE_DETACHED;
154 static bool mlx5_sf_is_active(const struct mlx5_sf *sf)
156 return sf->hw_state == MLX5_VHCA_STATE_ACTIVE || sf->hw_state == MLX5_VHCA_STATE_IN_USE;
159 int mlx5_devlink_sf_port_fn_state_get(struct devlink *devlink, struct devlink_port *dl_port,
160 enum devlink_port_fn_state *state,
161 enum devlink_port_fn_opstate *opstate,
162 struct netlink_ext_ack *extack)
164 struct mlx5_core_dev *dev = devlink_priv(devlink);
165 struct mlx5_sf_table *table;
169 table = mlx5_sf_table_try_get(dev);
173 sf = mlx5_sf_lookup_by_index(table, dl_port->index);
178 mutex_lock(&table->sf_state_lock);
179 *state = mlx5_sf_to_devlink_state(sf->hw_state);
180 *opstate = mlx5_sf_to_devlink_opstate(sf->hw_state);
181 mutex_unlock(&table->sf_state_lock);
183 mlx5_sf_table_put(table);
187 static int mlx5_sf_activate(struct mlx5_core_dev *dev, struct mlx5_sf *sf)
191 if (mlx5_sf_is_active(sf))
193 if (sf->hw_state != MLX5_VHCA_STATE_ALLOCATED)
196 err = mlx5_cmd_sf_enable_hca(dev, sf->hw_fn_id);
200 sf->hw_state = MLX5_VHCA_STATE_ACTIVE;
204 static int mlx5_sf_deactivate(struct mlx5_core_dev *dev, struct mlx5_sf *sf)
208 if (!mlx5_sf_is_active(sf))
211 err = mlx5_cmd_sf_disable_hca(dev, sf->hw_fn_id);
215 sf->hw_state = MLX5_VHCA_STATE_TEARDOWN_REQUEST;
219 static int mlx5_sf_state_set(struct mlx5_core_dev *dev, struct mlx5_sf_table *table,
221 enum devlink_port_fn_state state)
225 mutex_lock(&table->sf_state_lock);
226 if (state == mlx5_sf_to_devlink_state(sf->hw_state))
228 if (state == DEVLINK_PORT_FN_STATE_ACTIVE)
229 err = mlx5_sf_activate(dev, sf);
230 else if (state == DEVLINK_PORT_FN_STATE_INACTIVE)
231 err = mlx5_sf_deactivate(dev, sf);
235 mutex_unlock(&table->sf_state_lock);
239 int mlx5_devlink_sf_port_fn_state_set(struct devlink *devlink, struct devlink_port *dl_port,
240 enum devlink_port_fn_state state,
241 struct netlink_ext_ack *extack)
243 struct mlx5_core_dev *dev = devlink_priv(devlink);
244 struct mlx5_sf_table *table;
248 table = mlx5_sf_table_try_get(dev);
250 NL_SET_ERR_MSG_MOD(extack,
251 "Port state set is only supported in eswitch switchdev mode or SF ports are disabled.");
254 sf = mlx5_sf_lookup_by_index(table, dl_port->index);
260 err = mlx5_sf_state_set(dev, table, sf, state);
262 mlx5_sf_table_put(table);
266 static int mlx5_sf_add(struct mlx5_core_dev *dev, struct mlx5_sf_table *table,
267 const struct devlink_port_new_attrs *new_attr,
268 struct netlink_ext_ack *extack,
269 unsigned int *new_port_index)
271 struct mlx5_eswitch *esw = dev->priv.eswitch;
276 sf = mlx5_sf_alloc(table, new_attr->sfnum, extack);
280 hw_fn_id = mlx5_sf_sw_to_hw_id(dev, sf->id);
281 err = mlx5_esw_offloads_sf_vport_enable(esw, &sf->dl_port, hw_fn_id, new_attr->sfnum);
284 *new_port_index = sf->port_index;
288 mlx5_sf_free(table, sf);
293 mlx5_sf_new_check_attr(struct mlx5_core_dev *dev, const struct devlink_port_new_attrs *new_attr,
294 struct netlink_ext_ack *extack)
296 if (new_attr->flavour != DEVLINK_PORT_FLAVOUR_PCI_SF) {
297 NL_SET_ERR_MSG_MOD(extack, "Driver supports only SF port addition");
300 if (new_attr->port_index_valid) {
301 NL_SET_ERR_MSG_MOD(extack,
302 "Driver does not support user defined port index assignment");
305 if (!new_attr->sfnum_valid) {
306 NL_SET_ERR_MSG_MOD(extack,
307 "User must provide unique sfnum. Driver does not support auto assignment");
310 if (new_attr->controller_valid && new_attr->controller) {
311 NL_SET_ERR_MSG_MOD(extack, "External controller is unsupported");
314 if (new_attr->pfnum != PCI_FUNC(dev->pdev->devfn)) {
315 NL_SET_ERR_MSG_MOD(extack, "Invalid pfnum supplied");
321 int mlx5_devlink_sf_port_new(struct devlink *devlink,
322 const struct devlink_port_new_attrs *new_attr,
323 struct netlink_ext_ack *extack,
324 unsigned int *new_port_index)
326 struct mlx5_core_dev *dev = devlink_priv(devlink);
327 struct mlx5_sf_table *table;
330 err = mlx5_sf_new_check_attr(dev, new_attr, extack);
334 table = mlx5_sf_table_try_get(dev);
336 NL_SET_ERR_MSG_MOD(extack,
337 "Port add is only supported in eswitch switchdev mode or SF ports are disabled.");
340 err = mlx5_sf_add(dev, table, new_attr, extack, new_port_index);
341 mlx5_sf_table_put(table);
345 static void mlx5_sf_dealloc(struct mlx5_sf_table *table, struct mlx5_sf *sf)
347 if (sf->hw_state == MLX5_VHCA_STATE_ALLOCATED) {
348 mlx5_sf_free(table, sf);
349 } else if (mlx5_sf_is_active(sf)) {
350 /* Even if its active, it is treated as in_use because by the time,
351 * it is disabled here, it may getting used. So it is safe to
352 * always look for the event to ensure that it is recycled only after
353 * firmware gives confirmation that it is detached by the driver.
355 mlx5_cmd_sf_disable_hca(table->dev, sf->hw_fn_id);
356 mlx5_sf_hw_table_sf_deferred_free(table->dev, sf->id);
359 mlx5_sf_hw_table_sf_deferred_free(table->dev, sf->id);
364 int mlx5_devlink_sf_port_del(struct devlink *devlink, unsigned int port_index,
365 struct netlink_ext_ack *extack)
367 struct mlx5_core_dev *dev = devlink_priv(devlink);
368 struct mlx5_eswitch *esw = dev->priv.eswitch;
369 struct mlx5_sf_table *table;
373 table = mlx5_sf_table_try_get(dev);
375 NL_SET_ERR_MSG_MOD(extack,
376 "Port del is only supported in eswitch switchdev mode or SF ports are disabled.");
379 sf = mlx5_sf_lookup_by_index(table, port_index);
385 mlx5_esw_offloads_sf_vport_disable(esw, sf->hw_fn_id);
386 mlx5_sf_id_erase(table, sf);
388 mutex_lock(&table->sf_state_lock);
389 mlx5_sf_dealloc(table, sf);
390 mutex_unlock(&table->sf_state_lock);
392 mlx5_sf_table_put(table);
396 static bool mlx5_sf_state_update_check(const struct mlx5_sf *sf, u8 new_state)
398 if (sf->hw_state == MLX5_VHCA_STATE_ACTIVE && new_state == MLX5_VHCA_STATE_IN_USE)
401 if (sf->hw_state == MLX5_VHCA_STATE_IN_USE && new_state == MLX5_VHCA_STATE_ACTIVE)
404 if (sf->hw_state == MLX5_VHCA_STATE_TEARDOWN_REQUEST &&
405 new_state == MLX5_VHCA_STATE_ALLOCATED)
411 static int mlx5_sf_vhca_event(struct notifier_block *nb, unsigned long opcode, void *data)
413 struct mlx5_sf_table *table = container_of(nb, struct mlx5_sf_table, vhca_nb);
414 const struct mlx5_vhca_state_event *event = data;
418 table = mlx5_sf_table_try_get(table->dev);
422 mutex_lock(&table->sf_state_lock);
423 sf = mlx5_sf_lookup_by_function_id(table, event->function_id);
427 /* When driver is attached or detached to a function, an event
428 * notifies such state change.
430 update = mlx5_sf_state_update_check(sf, event->new_vhca_state);
432 sf->hw_state = event->new_vhca_state;
434 mutex_unlock(&table->sf_state_lock);
435 mlx5_sf_table_put(table);
439 static void mlx5_sf_table_enable(struct mlx5_sf_table *table)
441 if (!mlx5_sf_max_functions(table->dev))
444 init_completion(&table->disable_complete);
445 refcount_set(&table->refcount, 1);
448 static void mlx5_sf_deactivate_all(struct mlx5_sf_table *table)
450 struct mlx5_eswitch *esw = table->dev->priv.eswitch;
454 /* At this point, no new user commands can start and no vhca event can
455 * arrive. It is safe to destroy all user created SFs.
457 xa_for_each(&table->port_indices, index, sf) {
458 mlx5_esw_offloads_sf_vport_disable(esw, sf->hw_fn_id);
459 mlx5_sf_id_erase(table, sf);
460 mlx5_sf_dealloc(table, sf);
464 static void mlx5_sf_table_disable(struct mlx5_sf_table *table)
466 if (!mlx5_sf_max_functions(table->dev))
469 if (!refcount_read(&table->refcount))
472 /* Balances with refcount_set; drop the reference so that new user cmd cannot start
473 * and new vhca event handler cannnot run.
475 mlx5_sf_table_put(table);
476 wait_for_completion(&table->disable_complete);
478 mlx5_sf_deactivate_all(table);
481 static int mlx5_sf_esw_event(struct notifier_block *nb, unsigned long event, void *data)
483 struct mlx5_sf_table *table = container_of(nb, struct mlx5_sf_table, esw_nb);
484 const struct mlx5_esw_event_info *mode = data;
486 switch (mode->new_mode) {
487 case MLX5_ESWITCH_OFFLOADS:
488 mlx5_sf_table_enable(table);
490 case MLX5_ESWITCH_NONE:
491 mlx5_sf_table_disable(table);
500 static bool mlx5_sf_table_supported(const struct mlx5_core_dev *dev)
502 return dev->priv.eswitch && MLX5_ESWITCH_MANAGER(dev) && mlx5_sf_supported(dev);
505 int mlx5_sf_table_init(struct mlx5_core_dev *dev)
507 struct mlx5_sf_table *table;
510 if (!mlx5_sf_table_supported(dev) || !mlx5_vhca_event_supported(dev))
513 table = kzalloc(sizeof(*table), GFP_KERNEL);
517 mutex_init(&table->sf_state_lock);
519 xa_init(&table->port_indices);
520 dev->priv.sf_table = table;
521 refcount_set(&table->refcount, 0);
522 table->esw_nb.notifier_call = mlx5_sf_esw_event;
523 err = mlx5_esw_event_notifier_register(dev->priv.eswitch, &table->esw_nb);
527 table->vhca_nb.notifier_call = mlx5_sf_vhca_event;
528 err = mlx5_vhca_event_notifier_register(table->dev, &table->vhca_nb);
535 mlx5_esw_event_notifier_unregister(dev->priv.eswitch, &table->esw_nb);
537 mutex_destroy(&table->sf_state_lock);
539 dev->priv.sf_table = NULL;
543 void mlx5_sf_table_cleanup(struct mlx5_core_dev *dev)
545 struct mlx5_sf_table *table = dev->priv.sf_table;
550 mlx5_vhca_event_notifier_unregister(table->dev, &table->vhca_nb);
551 mlx5_esw_event_notifier_unregister(dev->priv.eswitch, &table->esw_nb);
552 WARN_ON(refcount_read(&table->refcount));
553 mutex_destroy(&table->sf_state_lock);
554 WARN_ON(!xa_empty(&table->port_indices));