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