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