1 // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB 2 /* 3 * Copyright (c) 2013-2021, Mellanox Technologies inc. All rights reserved. 4 */ 5 6 #include <linux/interrupt.h> 7 #include <linux/notifier.h> 8 #include <linux/mlx5/driver.h> 9 #include <linux/mlx5/vport.h> 10 #include <linux/mlx5/eq.h> 11 #ifdef CONFIG_RFS_ACCEL 12 #include <linux/cpu_rmap.h> 13 #endif 14 #include "mlx5_core.h" 15 #include "lib/eq.h" 16 #include "fpga/core.h" 17 #include "eswitch.h" 18 #include "lib/clock.h" 19 #include "diag/fw_tracer.h" 20 #include "mlx5_irq.h" 21 #include "pci_irq.h" 22 #include "devlink.h" 23 #include "en_accel/ipsec.h" 24 25 enum { 26 MLX5_EQE_OWNER_INIT_VAL = 0x1, 27 }; 28 29 enum { 30 MLX5_EQ_STATE_ARMED = 0x9, 31 MLX5_EQ_STATE_FIRED = 0xa, 32 MLX5_EQ_STATE_ALWAYS_ARMED = 0xb, 33 }; 34 35 #define MLX5_EQ_DOORBELL_OFFSET 0x40 36 37 /* budget must be smaller than MLX5_NUM_SPARE_EQE to guarantee that we update 38 * the ci before we polled all the entries in the EQ. MLX5_NUM_SPARE_EQE is 39 * used to set the EQ size, budget must be smaller than the EQ size. 40 */ 41 enum { 42 MLX5_EQ_POLLING_BUDGET = 128, 43 }; 44 45 static_assert(MLX5_EQ_POLLING_BUDGET <= MLX5_NUM_SPARE_EQE); 46 47 struct mlx5_eq_table { 48 struct xarray comp_eqs; 49 struct mlx5_eq_async pages_eq; 50 struct mlx5_eq_async cmd_eq; 51 struct mlx5_eq_async async_eq; 52 53 struct atomic_notifier_head nh[MLX5_EVENT_TYPE_MAX]; 54 55 /* Since CQ DB is stored in async_eq */ 56 struct mlx5_nb cq_err_nb; 57 58 struct mutex lock; /* sync async eqs creations */ 59 struct mutex comp_lock; /* sync comp eqs creations */ 60 int curr_comp_eqs; 61 int max_comp_eqs; 62 struct mlx5_irq_table *irq_table; 63 struct xarray comp_irqs; 64 struct mlx5_irq *ctrl_irq; 65 struct cpu_rmap *rmap; 66 struct cpumask used_cpus; 67 }; 68 69 #define MLX5_ASYNC_EVENT_MASK ((1ull << MLX5_EVENT_TYPE_PATH_MIG) | \ 70 (1ull << MLX5_EVENT_TYPE_COMM_EST) | \ 71 (1ull << MLX5_EVENT_TYPE_SQ_DRAINED) | \ 72 (1ull << MLX5_EVENT_TYPE_CQ_ERROR) | \ 73 (1ull << MLX5_EVENT_TYPE_WQ_CATAS_ERROR) | \ 74 (1ull << MLX5_EVENT_TYPE_PATH_MIG_FAILED) | \ 75 (1ull << MLX5_EVENT_TYPE_WQ_INVAL_REQ_ERROR) | \ 76 (1ull << MLX5_EVENT_TYPE_WQ_ACCESS_ERROR) | \ 77 (1ull << MLX5_EVENT_TYPE_PORT_CHANGE) | \ 78 (1ull << MLX5_EVENT_TYPE_SRQ_CATAS_ERROR) | \ 79 (1ull << MLX5_EVENT_TYPE_SRQ_LAST_WQE) | \ 80 (1ull << MLX5_EVENT_TYPE_SRQ_RQ_LIMIT)) 81 82 static int mlx5_cmd_destroy_eq(struct mlx5_core_dev *dev, u8 eqn) 83 { 84 u32 in[MLX5_ST_SZ_DW(destroy_eq_in)] = {}; 85 86 MLX5_SET(destroy_eq_in, in, opcode, MLX5_CMD_OP_DESTROY_EQ); 87 MLX5_SET(destroy_eq_in, in, eq_number, eqn); 88 return mlx5_cmd_exec_in(dev, destroy_eq, in); 89 } 90 91 /* caller must eventually call mlx5_cq_put on the returned cq */ 92 static struct mlx5_core_cq *mlx5_eq_cq_get(struct mlx5_eq *eq, u32 cqn) 93 { 94 struct mlx5_cq_table *table = &eq->cq_table; 95 struct mlx5_core_cq *cq = NULL; 96 97 rcu_read_lock(); 98 cq = radix_tree_lookup(&table->tree, cqn); 99 if (likely(cq)) 100 mlx5_cq_hold(cq); 101 rcu_read_unlock(); 102 103 return cq; 104 } 105 106 static int mlx5_eq_comp_int(struct notifier_block *nb, 107 __always_unused unsigned long action, 108 __always_unused void *data) 109 { 110 struct mlx5_eq_comp *eq_comp = 111 container_of(nb, struct mlx5_eq_comp, irq_nb); 112 struct mlx5_eq *eq = &eq_comp->core; 113 struct mlx5_eqe *eqe; 114 int num_eqes = 0; 115 116 while ((eqe = next_eqe_sw(eq))) { 117 struct mlx5_core_cq *cq; 118 u32 cqn; 119 120 /* Make sure we read EQ entry contents after we've 121 * checked the ownership bit. 122 */ 123 dma_rmb(); 124 /* Assume (eqe->type) is always MLX5_EVENT_TYPE_COMP */ 125 cqn = be32_to_cpu(eqe->data.comp.cqn) & 0xffffff; 126 127 cq = mlx5_eq_cq_get(eq, cqn); 128 if (likely(cq)) { 129 ++cq->arm_sn; 130 cq->comp(cq, eqe); 131 mlx5_cq_put(cq); 132 } else { 133 dev_dbg_ratelimited(eq->dev->device, 134 "Completion event for bogus CQ 0x%x\n", cqn); 135 } 136 137 ++eq->cons_index; 138 139 if (++num_eqes >= MLX5_EQ_POLLING_BUDGET) 140 break; 141 } 142 143 eq_update_ci(eq, 1); 144 145 return 0; 146 } 147 148 /* Some architectures don't latch interrupts when they are disabled, so using 149 * mlx5_eq_poll_irq_disabled could end up losing interrupts while trying to 150 * avoid losing them. It is not recommended to use it, unless this is the last 151 * resort. 152 */ 153 u32 mlx5_eq_poll_irq_disabled(struct mlx5_eq_comp *eq) 154 { 155 u32 count_eqe; 156 157 disable_irq(eq->core.irqn); 158 count_eqe = eq->core.cons_index; 159 mlx5_eq_comp_int(&eq->irq_nb, 0, NULL); 160 count_eqe = eq->core.cons_index - count_eqe; 161 enable_irq(eq->core.irqn); 162 163 return count_eqe; 164 } 165 166 static void mlx5_eq_async_int_lock(struct mlx5_eq_async *eq, bool recovery, 167 unsigned long *flags) 168 __acquires(&eq->lock) 169 { 170 if (!recovery) 171 spin_lock(&eq->lock); 172 else 173 spin_lock_irqsave(&eq->lock, *flags); 174 } 175 176 static void mlx5_eq_async_int_unlock(struct mlx5_eq_async *eq, bool recovery, 177 unsigned long *flags) 178 __releases(&eq->lock) 179 { 180 if (!recovery) 181 spin_unlock(&eq->lock); 182 else 183 spin_unlock_irqrestore(&eq->lock, *flags); 184 } 185 186 enum async_eq_nb_action { 187 ASYNC_EQ_IRQ_HANDLER = 0, 188 ASYNC_EQ_RECOVER = 1, 189 }; 190 191 static int mlx5_eq_async_int(struct notifier_block *nb, 192 unsigned long action, void *data) 193 { 194 struct mlx5_eq_async *eq_async = 195 container_of(nb, struct mlx5_eq_async, irq_nb); 196 struct mlx5_eq *eq = &eq_async->core; 197 struct mlx5_eq_table *eqt; 198 struct mlx5_core_dev *dev; 199 struct mlx5_eqe *eqe; 200 unsigned long flags; 201 int num_eqes = 0; 202 bool recovery; 203 204 dev = eq->dev; 205 eqt = dev->priv.eq_table; 206 207 recovery = action == ASYNC_EQ_RECOVER; 208 mlx5_eq_async_int_lock(eq_async, recovery, &flags); 209 210 while ((eqe = next_eqe_sw(eq))) { 211 /* 212 * Make sure we read EQ entry contents after we've 213 * checked the ownership bit. 214 */ 215 dma_rmb(); 216 217 atomic_notifier_call_chain(&eqt->nh[eqe->type], eqe->type, eqe); 218 atomic_notifier_call_chain(&eqt->nh[MLX5_EVENT_TYPE_NOTIFY_ANY], eqe->type, eqe); 219 220 ++eq->cons_index; 221 222 if (++num_eqes >= MLX5_EQ_POLLING_BUDGET) 223 break; 224 } 225 226 eq_update_ci(eq, 1); 227 mlx5_eq_async_int_unlock(eq_async, recovery, &flags); 228 229 return unlikely(recovery) ? num_eqes : 0; 230 } 231 232 void mlx5_cmd_eq_recover(struct mlx5_core_dev *dev) 233 { 234 struct mlx5_eq_async *eq = &dev->priv.eq_table->cmd_eq; 235 int eqes; 236 237 eqes = mlx5_eq_async_int(&eq->irq_nb, ASYNC_EQ_RECOVER, NULL); 238 if (eqes) 239 mlx5_core_warn(dev, "Recovered %d EQEs on cmd_eq\n", eqes); 240 } 241 242 static void init_eq_buf(struct mlx5_eq *eq) 243 { 244 struct mlx5_eqe *eqe; 245 int i; 246 247 for (i = 0; i < eq_get_size(eq); i++) { 248 eqe = get_eqe(eq, i); 249 eqe->owner = MLX5_EQE_OWNER_INIT_VAL; 250 } 251 } 252 253 static int 254 create_map_eq(struct mlx5_core_dev *dev, struct mlx5_eq *eq, 255 struct mlx5_eq_param *param) 256 { 257 u8 log_eq_size = order_base_2(param->nent + MLX5_NUM_SPARE_EQE); 258 struct mlx5_cq_table *cq_table = &eq->cq_table; 259 u32 out[MLX5_ST_SZ_DW(create_eq_out)] = {0}; 260 u8 log_eq_stride = ilog2(MLX5_EQE_SIZE); 261 struct mlx5_priv *priv = &dev->priv; 262 __be64 *pas; 263 u16 vecidx; 264 void *eqc; 265 int inlen; 266 u32 *in; 267 int err; 268 int i; 269 270 /* Init CQ table */ 271 memset(cq_table, 0, sizeof(*cq_table)); 272 spin_lock_init(&cq_table->lock); 273 INIT_RADIX_TREE(&cq_table->tree, GFP_ATOMIC); 274 275 eq->cons_index = 0; 276 277 err = mlx5_frag_buf_alloc_node(dev, wq_get_byte_sz(log_eq_size, log_eq_stride), 278 &eq->frag_buf, dev->priv.numa_node); 279 if (err) 280 return err; 281 282 mlx5_init_fbc(eq->frag_buf.frags, log_eq_stride, log_eq_size, &eq->fbc); 283 init_eq_buf(eq); 284 285 eq->irq = param->irq; 286 vecidx = mlx5_irq_get_index(eq->irq); 287 288 inlen = MLX5_ST_SZ_BYTES(create_eq_in) + 289 MLX5_FLD_SZ_BYTES(create_eq_in, pas[0]) * eq->frag_buf.npages; 290 291 in = kvzalloc(inlen, GFP_KERNEL); 292 if (!in) { 293 err = -ENOMEM; 294 goto err_buf; 295 } 296 297 pas = (__be64 *)MLX5_ADDR_OF(create_eq_in, in, pas); 298 mlx5_fill_page_frag_array(&eq->frag_buf, pas); 299 300 MLX5_SET(create_eq_in, in, opcode, MLX5_CMD_OP_CREATE_EQ); 301 if (!param->mask[0] && MLX5_CAP_GEN(dev, log_max_uctx)) 302 MLX5_SET(create_eq_in, in, uid, MLX5_SHARED_RESOURCE_UID); 303 304 for (i = 0; i < 4; i++) 305 MLX5_ARRAY_SET64(create_eq_in, in, event_bitmask, i, 306 param->mask[i]); 307 308 eqc = MLX5_ADDR_OF(create_eq_in, in, eq_context_entry); 309 MLX5_SET(eqc, eqc, log_eq_size, eq->fbc.log_sz); 310 MLX5_SET(eqc, eqc, uar_page, priv->bfreg.up->index); 311 MLX5_SET(eqc, eqc, intr, vecidx); 312 MLX5_SET(eqc, eqc, log_page_size, 313 eq->frag_buf.page_shift - MLX5_ADAPTER_PAGE_SHIFT); 314 315 err = mlx5_cmd_exec(dev, in, inlen, out, sizeof(out)); 316 if (err) 317 goto err_in; 318 319 eq->vecidx = vecidx; 320 eq->eqn = MLX5_GET(create_eq_out, out, eq_number); 321 eq->irqn = pci_irq_vector(dev->pdev, vecidx); 322 eq->dev = dev; 323 eq->doorbell = priv->bfreg.up->map + MLX5_EQ_DOORBELL_OFFSET; 324 325 err = mlx5_debug_eq_add(dev, eq); 326 if (err) 327 goto err_eq; 328 329 kvfree(in); 330 return 0; 331 332 err_eq: 333 mlx5_cmd_destroy_eq(dev, eq->eqn); 334 335 err_in: 336 kvfree(in); 337 338 err_buf: 339 mlx5_frag_buf_free(dev, &eq->frag_buf); 340 return err; 341 } 342 343 /** 344 * mlx5_eq_enable - Enable EQ for receiving EQEs 345 * @dev : Device which owns the eq 346 * @eq : EQ to enable 347 * @nb : Notifier call block 348 * 349 * Must be called after EQ is created in device. 350 * 351 * @return: 0 if no error 352 */ 353 int mlx5_eq_enable(struct mlx5_core_dev *dev, struct mlx5_eq *eq, 354 struct notifier_block *nb) 355 { 356 int err; 357 358 err = mlx5_irq_attach_nb(eq->irq, nb); 359 if (!err) 360 eq_update_ci(eq, 1); 361 362 return err; 363 } 364 EXPORT_SYMBOL(mlx5_eq_enable); 365 366 /** 367 * mlx5_eq_disable - Disable EQ for receiving EQEs 368 * @dev : Device which owns the eq 369 * @eq : EQ to disable 370 * @nb : Notifier call block 371 * 372 * Must be called before EQ is destroyed. 373 */ 374 void mlx5_eq_disable(struct mlx5_core_dev *dev, struct mlx5_eq *eq, 375 struct notifier_block *nb) 376 { 377 mlx5_irq_detach_nb(eq->irq, nb); 378 } 379 EXPORT_SYMBOL(mlx5_eq_disable); 380 381 static int destroy_unmap_eq(struct mlx5_core_dev *dev, struct mlx5_eq *eq) 382 { 383 int err; 384 385 mlx5_debug_eq_remove(dev, eq); 386 387 err = mlx5_cmd_destroy_eq(dev, eq->eqn); 388 if (err) 389 mlx5_core_warn(dev, "failed to destroy a previously created eq: eqn %d\n", 390 eq->eqn); 391 392 mlx5_frag_buf_free(dev, &eq->frag_buf); 393 return err; 394 } 395 396 int mlx5_eq_add_cq(struct mlx5_eq *eq, struct mlx5_core_cq *cq) 397 { 398 struct mlx5_cq_table *table = &eq->cq_table; 399 int err; 400 401 spin_lock(&table->lock); 402 err = radix_tree_insert(&table->tree, cq->cqn, cq); 403 spin_unlock(&table->lock); 404 405 return err; 406 } 407 408 void mlx5_eq_del_cq(struct mlx5_eq *eq, struct mlx5_core_cq *cq) 409 { 410 struct mlx5_cq_table *table = &eq->cq_table; 411 struct mlx5_core_cq *tmp; 412 413 spin_lock(&table->lock); 414 tmp = radix_tree_delete(&table->tree, cq->cqn); 415 spin_unlock(&table->lock); 416 417 if (!tmp) { 418 mlx5_core_dbg(eq->dev, "cq 0x%x not found in eq 0x%x tree\n", 419 eq->eqn, cq->cqn); 420 return; 421 } 422 423 if (tmp != cq) 424 mlx5_core_dbg(eq->dev, "corruption on cqn 0x%x in eq 0x%x\n", 425 eq->eqn, cq->cqn); 426 } 427 428 int mlx5_eq_table_init(struct mlx5_core_dev *dev) 429 { 430 struct mlx5_eq_table *eq_table; 431 int i; 432 433 eq_table = kvzalloc_node(sizeof(*eq_table), GFP_KERNEL, 434 dev->priv.numa_node); 435 if (!eq_table) 436 return -ENOMEM; 437 438 dev->priv.eq_table = eq_table; 439 440 mlx5_eq_debugfs_init(dev); 441 442 mutex_init(&eq_table->lock); 443 for (i = 0; i < MLX5_EVENT_TYPE_MAX; i++) 444 ATOMIC_INIT_NOTIFIER_HEAD(&eq_table->nh[i]); 445 446 eq_table->irq_table = mlx5_irq_table_get(dev); 447 cpumask_clear(&eq_table->used_cpus); 448 xa_init(&eq_table->comp_eqs); 449 xa_init(&eq_table->comp_irqs); 450 mutex_init(&eq_table->comp_lock); 451 eq_table->curr_comp_eqs = 0; 452 return 0; 453 } 454 455 void mlx5_eq_table_cleanup(struct mlx5_core_dev *dev) 456 { 457 struct mlx5_eq_table *table = dev->priv.eq_table; 458 459 mlx5_eq_debugfs_cleanup(dev); 460 xa_destroy(&table->comp_irqs); 461 xa_destroy(&table->comp_eqs); 462 kvfree(table); 463 } 464 465 /* Async EQs */ 466 467 static int create_async_eq(struct mlx5_core_dev *dev, 468 struct mlx5_eq *eq, struct mlx5_eq_param *param) 469 { 470 struct mlx5_eq_table *eq_table = dev->priv.eq_table; 471 int err; 472 473 mutex_lock(&eq_table->lock); 474 err = create_map_eq(dev, eq, param); 475 mutex_unlock(&eq_table->lock); 476 return err; 477 } 478 479 static int destroy_async_eq(struct mlx5_core_dev *dev, struct mlx5_eq *eq) 480 { 481 struct mlx5_eq_table *eq_table = dev->priv.eq_table; 482 int err; 483 484 mutex_lock(&eq_table->lock); 485 err = destroy_unmap_eq(dev, eq); 486 mutex_unlock(&eq_table->lock); 487 return err; 488 } 489 490 static int cq_err_event_notifier(struct notifier_block *nb, 491 unsigned long type, void *data) 492 { 493 struct mlx5_eq_table *eqt; 494 struct mlx5_core_cq *cq; 495 struct mlx5_eqe *eqe; 496 struct mlx5_eq *eq; 497 u32 cqn; 498 499 /* type == MLX5_EVENT_TYPE_CQ_ERROR */ 500 501 eqt = mlx5_nb_cof(nb, struct mlx5_eq_table, cq_err_nb); 502 eq = &eqt->async_eq.core; 503 eqe = data; 504 505 cqn = be32_to_cpu(eqe->data.cq_err.cqn) & 0xffffff; 506 mlx5_core_warn(eq->dev, "CQ error on CQN 0x%x, syndrome 0x%x\n", 507 cqn, eqe->data.cq_err.syndrome); 508 509 cq = mlx5_eq_cq_get(eq, cqn); 510 if (unlikely(!cq)) { 511 mlx5_core_warn(eq->dev, "Async event for bogus CQ 0x%x\n", cqn); 512 return NOTIFY_OK; 513 } 514 515 if (cq->event) 516 cq->event(cq, type); 517 518 mlx5_cq_put(cq); 519 520 return NOTIFY_OK; 521 } 522 523 static void gather_user_async_events(struct mlx5_core_dev *dev, u64 mask[4]) 524 { 525 __be64 *user_unaffiliated_events; 526 __be64 *user_affiliated_events; 527 int i; 528 529 user_affiliated_events = 530 MLX5_CAP_DEV_EVENT(dev, user_affiliated_events); 531 user_unaffiliated_events = 532 MLX5_CAP_DEV_EVENT(dev, user_unaffiliated_events); 533 534 for (i = 0; i < 4; i++) 535 mask[i] |= be64_to_cpu(user_affiliated_events[i] | 536 user_unaffiliated_events[i]); 537 } 538 539 static void gather_async_events_mask(struct mlx5_core_dev *dev, u64 mask[4]) 540 { 541 u64 async_event_mask = MLX5_ASYNC_EVENT_MASK; 542 543 if (MLX5_VPORT_MANAGER(dev)) 544 async_event_mask |= (1ull << MLX5_EVENT_TYPE_NIC_VPORT_CHANGE); 545 546 if (MLX5_CAP_GEN(dev, general_notification_event)) 547 async_event_mask |= (1ull << MLX5_EVENT_TYPE_GENERAL_EVENT); 548 549 if (MLX5_CAP_GEN(dev, port_module_event)) 550 async_event_mask |= (1ull << MLX5_EVENT_TYPE_PORT_MODULE_EVENT); 551 else 552 mlx5_core_dbg(dev, "port_module_event is not set\n"); 553 554 if (MLX5_PPS_CAP(dev)) 555 async_event_mask |= (1ull << MLX5_EVENT_TYPE_PPS_EVENT); 556 557 if (MLX5_CAP_GEN(dev, fpga)) 558 async_event_mask |= (1ull << MLX5_EVENT_TYPE_FPGA_ERROR) | 559 (1ull << MLX5_EVENT_TYPE_FPGA_QP_ERROR); 560 if (MLX5_CAP_GEN_MAX(dev, dct)) 561 async_event_mask |= (1ull << MLX5_EVENT_TYPE_DCT_DRAINED); 562 563 if (MLX5_CAP_GEN(dev, temp_warn_event)) 564 async_event_mask |= (1ull << MLX5_EVENT_TYPE_TEMP_WARN_EVENT); 565 566 if (MLX5_CAP_MCAM_REG(dev, tracer_registers)) 567 async_event_mask |= (1ull << MLX5_EVENT_TYPE_DEVICE_TRACER); 568 569 if (MLX5_CAP_GEN(dev, max_num_of_monitor_counters)) 570 async_event_mask |= (1ull << MLX5_EVENT_TYPE_MONITOR_COUNTER); 571 572 if (mlx5_eswitch_is_funcs_handler(dev)) 573 async_event_mask |= 574 (1ull << MLX5_EVENT_TYPE_ESW_FUNCTIONS_CHANGED); 575 576 if (MLX5_CAP_GEN_MAX(dev, vhca_state)) 577 async_event_mask |= (1ull << MLX5_EVENT_TYPE_VHCA_STATE_CHANGE); 578 579 if (MLX5_CAP_MACSEC(dev, log_max_macsec_offload)) 580 async_event_mask |= (1ull << MLX5_EVENT_TYPE_OBJECT_CHANGE); 581 582 if (mlx5_ipsec_device_caps(dev) & MLX5_IPSEC_CAP_PACKET_OFFLOAD) 583 async_event_mask |= 584 (1ull << MLX5_EVENT_TYPE_OBJECT_CHANGE); 585 586 if (mlx5_pcie_cong_event_supported(dev)) 587 async_event_mask |= (1ull << MLX5_EVENT_TYPE_OBJECT_CHANGE); 588 589 mask[0] = async_event_mask; 590 591 if (MLX5_CAP_GEN(dev, event_cap)) 592 gather_user_async_events(dev, mask); 593 } 594 595 static int 596 setup_async_eq(struct mlx5_core_dev *dev, struct mlx5_eq_async *eq, 597 struct mlx5_eq_param *param, const char *name) 598 { 599 int err; 600 601 eq->irq_nb.notifier_call = mlx5_eq_async_int; 602 spin_lock_init(&eq->lock); 603 604 err = create_async_eq(dev, &eq->core, param); 605 if (err) { 606 mlx5_core_warn(dev, "failed to create %s EQ %d\n", name, err); 607 return err; 608 } 609 err = mlx5_eq_enable(dev, &eq->core, &eq->irq_nb); 610 if (err) { 611 mlx5_core_warn(dev, "failed to enable %s EQ %d\n", name, err); 612 destroy_async_eq(dev, &eq->core); 613 } 614 return err; 615 } 616 617 static void cleanup_async_eq(struct mlx5_core_dev *dev, 618 struct mlx5_eq_async *eq, const char *name) 619 { 620 int err; 621 622 mlx5_eq_disable(dev, &eq->core, &eq->irq_nb); 623 err = destroy_async_eq(dev, &eq->core); 624 if (err) 625 mlx5_core_err(dev, "failed to destroy %s eq, err(%d)\n", 626 name, err); 627 } 628 629 static u16 async_eq_depth_devlink_param_get(struct mlx5_core_dev *dev) 630 { 631 struct devlink *devlink = priv_to_devlink(dev); 632 union devlink_param_value val; 633 int err; 634 635 err = devl_param_driverinit_value_get(devlink, 636 DEVLINK_PARAM_GENERIC_ID_EVENT_EQ_SIZE, 637 &val); 638 if (!err) 639 return val.vu32; 640 mlx5_core_dbg(dev, "Failed to get param. using default. err = %d\n", err); 641 return MLX5_NUM_ASYNC_EQE; 642 } 643 644 static int create_async_eqs(struct mlx5_core_dev *dev) 645 { 646 struct mlx5_eq_table *table = dev->priv.eq_table; 647 struct mlx5_eq_param param = {}; 648 int err; 649 650 /* All the async_eqs are using single IRQ, request one IRQ and share its 651 * index among all the async_eqs of this device. 652 */ 653 table->ctrl_irq = mlx5_ctrl_irq_request(dev); 654 if (IS_ERR(table->ctrl_irq)) 655 return PTR_ERR(table->ctrl_irq); 656 657 MLX5_NB_INIT(&table->cq_err_nb, cq_err_event_notifier, CQ_ERROR); 658 mlx5_eq_notifier_register(dev, &table->cq_err_nb); 659 660 param = (struct mlx5_eq_param) { 661 .irq = table->ctrl_irq, 662 .nent = MLX5_NUM_CMD_EQE, 663 .mask[0] = 1ull << MLX5_EVENT_TYPE_CMD, 664 }; 665 mlx5_cmd_allowed_opcode(dev, MLX5_CMD_OP_CREATE_EQ); 666 err = setup_async_eq(dev, &table->cmd_eq, ¶m, "cmd"); 667 if (err) 668 goto err1; 669 670 mlx5_cmd_use_events(dev); 671 mlx5_cmd_allowed_opcode(dev, CMD_ALLOWED_OPCODE_ALL); 672 673 param = (struct mlx5_eq_param) { 674 .irq = table->ctrl_irq, 675 .nent = async_eq_depth_devlink_param_get(dev), 676 }; 677 678 gather_async_events_mask(dev, param.mask); 679 err = setup_async_eq(dev, &table->async_eq, ¶m, "async"); 680 if (err) 681 goto err2; 682 683 /* Skip page eq creation when the device does not request for page requests */ 684 if (MLX5_CAP_GEN(dev, page_request_disable)) { 685 mlx5_core_dbg(dev, "Skip page EQ creation\n"); 686 return 0; 687 } 688 689 param = (struct mlx5_eq_param) { 690 .irq = table->ctrl_irq, 691 .nent = /* TODO: sriov max_vf + */ 1, 692 .mask[0] = 1ull << MLX5_EVENT_TYPE_PAGE_REQUEST, 693 }; 694 695 err = setup_async_eq(dev, &table->pages_eq, ¶m, "pages"); 696 if (err) 697 goto err3; 698 699 return 0; 700 701 err3: 702 cleanup_async_eq(dev, &table->async_eq, "async"); 703 err2: 704 mlx5_cmd_use_polling(dev); 705 cleanup_async_eq(dev, &table->cmd_eq, "cmd"); 706 err1: 707 mlx5_cmd_allowed_opcode(dev, CMD_ALLOWED_OPCODE_ALL); 708 mlx5_eq_notifier_unregister(dev, &table->cq_err_nb); 709 mlx5_ctrl_irq_release(dev, table->ctrl_irq); 710 return err; 711 } 712 713 static void destroy_async_eqs(struct mlx5_core_dev *dev) 714 { 715 struct mlx5_eq_table *table = dev->priv.eq_table; 716 717 if (!MLX5_CAP_GEN(dev, page_request_disable)) 718 cleanup_async_eq(dev, &table->pages_eq, "pages"); 719 cleanup_async_eq(dev, &table->async_eq, "async"); 720 mlx5_cmd_allowed_opcode(dev, MLX5_CMD_OP_DESTROY_EQ); 721 mlx5_cmd_use_polling(dev); 722 cleanup_async_eq(dev, &table->cmd_eq, "cmd"); 723 mlx5_cmd_allowed_opcode(dev, CMD_ALLOWED_OPCODE_ALL); 724 mlx5_eq_notifier_unregister(dev, &table->cq_err_nb); 725 mlx5_ctrl_irq_release(dev, table->ctrl_irq); 726 } 727 728 struct mlx5_eq *mlx5_get_async_eq(struct mlx5_core_dev *dev) 729 { 730 return &dev->priv.eq_table->async_eq.core; 731 } 732 733 void mlx5_eq_synchronize_async_irq(struct mlx5_core_dev *dev) 734 { 735 synchronize_irq(dev->priv.eq_table->async_eq.core.irqn); 736 } 737 738 void mlx5_eq_synchronize_cmd_irq(struct mlx5_core_dev *dev) 739 { 740 synchronize_irq(dev->priv.eq_table->cmd_eq.core.irqn); 741 } 742 743 /* Generic EQ API for mlx5_core consumers 744 * Needed For RDMA ODP EQ for now 745 */ 746 struct mlx5_eq * 747 mlx5_eq_create_generic(struct mlx5_core_dev *dev, 748 struct mlx5_eq_param *param) 749 { 750 struct mlx5_eq *eq = kvzalloc_node(sizeof(*eq), GFP_KERNEL, 751 dev->priv.numa_node); 752 int err; 753 754 if (!eq) 755 return ERR_PTR(-ENOMEM); 756 757 param->irq = dev->priv.eq_table->ctrl_irq; 758 err = create_async_eq(dev, eq, param); 759 if (err) { 760 kvfree(eq); 761 eq = ERR_PTR(err); 762 } 763 764 return eq; 765 } 766 EXPORT_SYMBOL(mlx5_eq_create_generic); 767 768 int mlx5_eq_destroy_generic(struct mlx5_core_dev *dev, struct mlx5_eq *eq) 769 { 770 int err; 771 772 if (IS_ERR(eq)) 773 return -EINVAL; 774 775 err = destroy_async_eq(dev, eq); 776 if (err) 777 goto out; 778 779 kvfree(eq); 780 out: 781 return err; 782 } 783 EXPORT_SYMBOL(mlx5_eq_destroy_generic); 784 785 struct mlx5_eqe *mlx5_eq_get_eqe(struct mlx5_eq *eq, u32 cc) 786 { 787 u32 ci = eq->cons_index + cc; 788 u32 nent = eq_get_size(eq); 789 struct mlx5_eqe *eqe; 790 791 eqe = get_eqe(eq, ci & (nent - 1)); 792 eqe = ((eqe->owner & 1) ^ !!(ci & nent)) ? NULL : eqe; 793 /* Make sure we read EQ entry contents after we've 794 * checked the ownership bit. 795 */ 796 if (eqe) 797 dma_rmb(); 798 799 return eqe; 800 } 801 EXPORT_SYMBOL(mlx5_eq_get_eqe); 802 803 void mlx5_eq_update_ci(struct mlx5_eq *eq, u32 cc, bool arm) 804 { 805 eq->cons_index += cc; 806 eq_update_ci(eq, arm); 807 } 808 EXPORT_SYMBOL(mlx5_eq_update_ci); 809 810 static void comp_irq_release_pci(struct mlx5_core_dev *dev, u16 vecidx) 811 { 812 struct mlx5_eq_table *table = dev->priv.eq_table; 813 struct mlx5_irq *irq; 814 815 irq = xa_load(&table->comp_irqs, vecidx); 816 if (!irq) 817 return; 818 819 xa_erase(&table->comp_irqs, vecidx); 820 mlx5_irq_release_vector(irq); 821 } 822 823 static int mlx5_cpumask_default_spread(struct mlx5_core_dev *dev, int index) 824 { 825 return cpumask_local_spread(index, dev->priv.numa_node); 826 } 827 828 static struct cpu_rmap *mlx5_eq_table_get_pci_rmap(struct mlx5_core_dev *dev) 829 { 830 #ifdef CONFIG_RFS_ACCEL 831 #ifdef CONFIG_MLX5_SF 832 if (mlx5_core_is_sf(dev)) 833 return dev->priv.parent_mdev->priv.eq_table->rmap; 834 #endif 835 return dev->priv.eq_table->rmap; 836 #else 837 return NULL; 838 #endif 839 } 840 841 static int comp_irq_request_pci(struct mlx5_core_dev *dev, u16 vecidx) 842 { 843 struct mlx5_eq_table *table = dev->priv.eq_table; 844 struct cpu_rmap *rmap; 845 struct mlx5_irq *irq; 846 int cpu; 847 848 rmap = mlx5_eq_table_get_pci_rmap(dev); 849 cpu = mlx5_cpumask_default_spread(dev, vecidx); 850 irq = mlx5_irq_request_vector(dev, cpu, vecidx, &rmap); 851 if (IS_ERR(irq)) 852 return PTR_ERR(irq); 853 854 return xa_err(xa_store(&table->comp_irqs, vecidx, irq, GFP_KERNEL)); 855 } 856 857 static void comp_irq_release_sf(struct mlx5_core_dev *dev, u16 vecidx) 858 { 859 struct mlx5_eq_table *table = dev->priv.eq_table; 860 struct mlx5_irq *irq; 861 int cpu; 862 863 irq = xa_load(&table->comp_irqs, vecidx); 864 if (!irq) 865 return; 866 867 cpu = cpumask_first(mlx5_irq_get_affinity_mask(irq)); 868 cpumask_clear_cpu(cpu, &table->used_cpus); 869 xa_erase(&table->comp_irqs, vecidx); 870 mlx5_irq_affinity_irq_release(dev, irq); 871 } 872 873 static int comp_irq_request_sf(struct mlx5_core_dev *dev, u16 vecidx) 874 { 875 struct mlx5_irq_pool *pool = mlx5_irq_table_get_comp_irq_pool(dev); 876 struct mlx5_eq_table *table = dev->priv.eq_table; 877 struct irq_affinity_desc *af_desc; 878 struct mlx5_irq *irq; 879 880 /* In case SF irq pool does not exist, fallback to the PF irqs */ 881 if (!mlx5_irq_pool_is_sf_pool(pool)) 882 return comp_irq_request_pci(dev, vecidx); 883 884 af_desc = kvzalloc_obj(*af_desc); 885 if (!af_desc) 886 return -ENOMEM; 887 888 af_desc->is_managed = false; 889 cpumask_andnot(&af_desc->mask, cpu_online_mask, &table->used_cpus); 890 irq = mlx5_irq_affinity_request(dev, pool, af_desc); 891 if (IS_ERR(irq)) { 892 kvfree(af_desc); 893 return PTR_ERR(irq); 894 } 895 896 cpumask_or(&table->used_cpus, &table->used_cpus, mlx5_irq_get_affinity_mask(irq)); 897 mlx5_core_dbg(pool->dev, "IRQ %u mapped to cpu %*pbl, %u EQs on this irq\n", 898 pci_irq_vector(dev->pdev, mlx5_irq_get_index(irq)), 899 cpumask_pr_args(mlx5_irq_get_affinity_mask(irq)), 900 mlx5_irq_read_locked(irq) / MLX5_EQ_REFS_PER_IRQ); 901 902 kvfree(af_desc); 903 904 return xa_err(xa_store(&table->comp_irqs, vecidx, irq, GFP_KERNEL)); 905 } 906 907 static void comp_irq_release(struct mlx5_core_dev *dev, u16 vecidx) 908 { 909 mlx5_core_is_sf(dev) ? comp_irq_release_sf(dev, vecidx) : 910 comp_irq_release_pci(dev, vecidx); 911 } 912 913 static int comp_irq_request(struct mlx5_core_dev *dev, u16 vecidx) 914 { 915 return mlx5_core_is_sf(dev) ? comp_irq_request_sf(dev, vecidx) : 916 comp_irq_request_pci(dev, vecidx); 917 } 918 919 #ifdef CONFIG_RFS_ACCEL 920 static int alloc_rmap(struct mlx5_core_dev *mdev) 921 { 922 struct mlx5_eq_table *eq_table = mdev->priv.eq_table; 923 924 /* rmap is a mapping between irq number and queue number. 925 * Each irq can be assigned only to a single rmap. 926 * Since SFs share IRQs, rmap mapping cannot function correctly 927 * for irqs that are shared between different core/netdev RX rings. 928 * Hence we don't allow netdev rmap for SFs. 929 */ 930 if (mlx5_core_is_sf(mdev)) 931 return 0; 932 933 eq_table->rmap = alloc_irq_cpu_rmap(eq_table->max_comp_eqs); 934 if (!eq_table->rmap) 935 return -ENOMEM; 936 return 0; 937 } 938 939 static void free_rmap(struct mlx5_core_dev *mdev) 940 { 941 struct mlx5_eq_table *eq_table = mdev->priv.eq_table; 942 943 if (eq_table->rmap) { 944 free_irq_cpu_rmap(eq_table->rmap); 945 eq_table->rmap = NULL; 946 } 947 } 948 #else 949 static int alloc_rmap(struct mlx5_core_dev *mdev) { return 0; } 950 static void free_rmap(struct mlx5_core_dev *mdev) {} 951 #endif 952 953 static void destroy_comp_eq(struct mlx5_core_dev *dev, struct mlx5_eq_comp *eq, u16 vecidx) 954 { 955 struct mlx5_eq_table *table = dev->priv.eq_table; 956 957 xa_erase(&table->comp_eqs, vecidx); 958 mlx5_eq_disable(dev, &eq->core, &eq->irq_nb); 959 if (destroy_unmap_eq(dev, &eq->core)) 960 mlx5_core_warn(dev, "failed to destroy comp EQ 0x%x\n", 961 eq->core.eqn); 962 tasklet_disable(&eq->tasklet_ctx.task); 963 kfree(eq); 964 comp_irq_release(dev, vecidx); 965 table->curr_comp_eqs--; 966 } 967 968 static u16 comp_eq_depth_devlink_param_get(struct mlx5_core_dev *dev) 969 { 970 struct devlink *devlink = priv_to_devlink(dev); 971 union devlink_param_value val; 972 int err; 973 974 err = devl_param_driverinit_value_get(devlink, 975 DEVLINK_PARAM_GENERIC_ID_IO_EQ_SIZE, 976 &val); 977 if (!err) 978 return val.vu32; 979 mlx5_core_dbg(dev, "Failed to get param. using default. err = %d\n", err); 980 return MLX5_COMP_EQ_SIZE; 981 } 982 983 /* Must be called with EQ table comp_lock held */ 984 static int create_comp_eq(struct mlx5_core_dev *dev, u16 vecidx) 985 { 986 struct mlx5_eq_table *table = dev->priv.eq_table; 987 struct mlx5_eq_param param = {}; 988 struct mlx5_eq_comp *eq; 989 struct mlx5_irq *irq; 990 int nent; 991 int err; 992 993 lockdep_assert_held(&table->comp_lock); 994 if (table->curr_comp_eqs == table->max_comp_eqs) { 995 mlx5_core_err(dev, "maximum number of vectors is allocated, %d\n", 996 table->max_comp_eqs); 997 return -ENOMEM; 998 } 999 1000 err = comp_irq_request(dev, vecidx); 1001 if (err) 1002 return err; 1003 1004 nent = comp_eq_depth_devlink_param_get(dev); 1005 1006 eq = kzalloc_node(sizeof(*eq), GFP_KERNEL, dev->priv.numa_node); 1007 if (!eq) { 1008 err = -ENOMEM; 1009 goto clean_irq; 1010 } 1011 1012 INIT_LIST_HEAD(&eq->tasklet_ctx.list); 1013 INIT_LIST_HEAD(&eq->tasklet_ctx.process_list); 1014 spin_lock_init(&eq->tasklet_ctx.lock); 1015 tasklet_setup(&eq->tasklet_ctx.task, mlx5_cq_tasklet_cb); 1016 1017 irq = xa_load(&table->comp_irqs, vecidx); 1018 eq->irq_nb.notifier_call = mlx5_eq_comp_int; 1019 param = (struct mlx5_eq_param) { 1020 .irq = irq, 1021 .nent = nent, 1022 }; 1023 1024 err = create_map_eq(dev, &eq->core, ¶m); 1025 if (err) 1026 goto clean_eq; 1027 err = mlx5_eq_enable(dev, &eq->core, &eq->irq_nb); 1028 if (err) { 1029 destroy_unmap_eq(dev, &eq->core); 1030 goto clean_eq; 1031 } 1032 1033 mlx5_core_dbg(dev, "allocated completion EQN %d\n", eq->core.eqn); 1034 err = xa_err(xa_store(&table->comp_eqs, vecidx, eq, GFP_KERNEL)); 1035 if (err) 1036 goto disable_eq; 1037 1038 table->curr_comp_eqs++; 1039 return eq->core.eqn; 1040 1041 disable_eq: 1042 mlx5_eq_disable(dev, &eq->core, &eq->irq_nb); 1043 clean_eq: 1044 kfree(eq); 1045 clean_irq: 1046 comp_irq_release(dev, vecidx); 1047 return err; 1048 } 1049 1050 int mlx5_comp_eqn_get(struct mlx5_core_dev *dev, u16 vecidx, int *eqn) 1051 { 1052 struct mlx5_eq_table *table = dev->priv.eq_table; 1053 struct mlx5_eq_comp *eq; 1054 int ret = 0; 1055 1056 if (vecidx >= table->max_comp_eqs) { 1057 mlx5_core_dbg(dev, "Requested vector index %u should be less than %u", 1058 vecidx, table->max_comp_eqs); 1059 return -EINVAL; 1060 } 1061 1062 mutex_lock(&table->comp_lock); 1063 eq = xa_load(&table->comp_eqs, vecidx); 1064 if (eq) { 1065 *eqn = eq->core.eqn; 1066 goto out; 1067 } 1068 1069 ret = create_comp_eq(dev, vecidx); 1070 if (ret < 0) { 1071 mutex_unlock(&table->comp_lock); 1072 return ret; 1073 } 1074 1075 *eqn = ret; 1076 out: 1077 mutex_unlock(&table->comp_lock); 1078 return 0; 1079 } 1080 EXPORT_SYMBOL(mlx5_comp_eqn_get); 1081 1082 int mlx5_comp_irqn_get(struct mlx5_core_dev *dev, int vector, unsigned int *irqn) 1083 { 1084 struct mlx5_eq_table *table = dev->priv.eq_table; 1085 struct mlx5_eq_comp *eq; 1086 int eqn; 1087 int err; 1088 1089 /* Allocate the EQ if not allocated yet */ 1090 err = mlx5_comp_eqn_get(dev, vector, &eqn); 1091 if (err) 1092 return err; 1093 1094 eq = xa_load(&table->comp_eqs, vector); 1095 *irqn = eq->core.irqn; 1096 return 0; 1097 } 1098 1099 unsigned int mlx5_comp_vectors_max(struct mlx5_core_dev *dev) 1100 { 1101 return dev->priv.eq_table->max_comp_eqs; 1102 } 1103 EXPORT_SYMBOL(mlx5_comp_vectors_max); 1104 1105 static struct cpumask * 1106 mlx5_comp_irq_get_affinity_mask(struct mlx5_core_dev *dev, int vector) 1107 { 1108 struct mlx5_eq_table *table = dev->priv.eq_table; 1109 struct mlx5_eq_comp *eq; 1110 1111 eq = xa_load(&table->comp_eqs, vector); 1112 if (eq) 1113 return mlx5_irq_get_affinity_mask(eq->core.irq); 1114 1115 return NULL; 1116 } 1117 1118 int mlx5_comp_vector_get_cpu(struct mlx5_core_dev *dev, int vector) 1119 { 1120 struct cpumask *mask; 1121 int cpu; 1122 1123 mask = mlx5_comp_irq_get_affinity_mask(dev, vector); 1124 if (mask) 1125 cpu = cpumask_first(mask); 1126 else 1127 cpu = mlx5_cpumask_default_spread(dev, vector); 1128 1129 return cpu; 1130 } 1131 EXPORT_SYMBOL(mlx5_comp_vector_get_cpu); 1132 1133 #ifdef CONFIG_RFS_ACCEL 1134 struct cpu_rmap *mlx5_eq_table_get_rmap(struct mlx5_core_dev *dev) 1135 { 1136 return dev->priv.eq_table->rmap; 1137 } 1138 #endif 1139 1140 struct mlx5_eq_comp *mlx5_eqn2comp_eq(struct mlx5_core_dev *dev, int eqn) 1141 { 1142 struct mlx5_eq_table *table = dev->priv.eq_table; 1143 struct mlx5_eq_comp *eq; 1144 unsigned long index; 1145 1146 xa_for_each(&table->comp_eqs, index, eq) 1147 if (eq->core.eqn == eqn) 1148 return eq; 1149 1150 return ERR_PTR(-ENOENT); 1151 } 1152 1153 /* This function should only be called after mlx5_cmd_force_teardown_hca */ 1154 void mlx5_core_eq_free_irqs(struct mlx5_core_dev *dev) 1155 { 1156 mlx5_irq_table_free_irqs(dev); 1157 } 1158 1159 #ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING 1160 #define MLX5_MAX_ASYNC_EQS 4 1161 #else 1162 #define MLX5_MAX_ASYNC_EQS 3 1163 #endif 1164 1165 static int get_num_eqs(struct mlx5_core_dev *dev) 1166 { 1167 struct mlx5_eq_table *eq_table = dev->priv.eq_table; 1168 int max_dev_eqs; 1169 int num_eqs; 1170 1171 /* If ethernet is disabled we use just a single completion vector to 1172 * have the other vectors available for other drivers using mlx5_core. For 1173 * example, mlx5_vdpa 1174 */ 1175 if (!mlx5_core_is_eth_enabled(dev) && mlx5_eth_supported(dev)) 1176 return 1; 1177 1178 max_dev_eqs = mlx5_max_eq_cap_get(dev); 1179 1180 num_eqs = min_t(int, mlx5_irq_table_get_num_comp(eq_table->irq_table), 1181 max_dev_eqs - MLX5_MAX_ASYNC_EQS); 1182 if (mlx5_core_is_sf(dev)) { 1183 int max_eqs_sf = MLX5_CAP_GEN_2(dev, sf_eq_usage) ? 1184 MLX5_CAP_GEN_2(dev, max_num_eqs_24b) : 1185 MLX5_COMP_EQS_PER_SF; 1186 1187 max_eqs_sf = min_t(int, max_eqs_sf, 1188 mlx5_irq_table_get_sfs_vec(eq_table->irq_table)); 1189 num_eqs = min_t(int, num_eqs, max_eqs_sf); 1190 } 1191 1192 return num_eqs; 1193 } 1194 1195 int mlx5_eq_table_create(struct mlx5_core_dev *dev) 1196 { 1197 struct mlx5_eq_table *eq_table = dev->priv.eq_table; 1198 int err; 1199 1200 eq_table->max_comp_eqs = get_num_eqs(dev); 1201 err = create_async_eqs(dev); 1202 if (err) { 1203 mlx5_core_err(dev, "Failed to create async EQs\n"); 1204 goto err_async_eqs; 1205 } 1206 1207 err = alloc_rmap(dev); 1208 if (err) { 1209 mlx5_core_err(dev, "Failed to allocate rmap\n"); 1210 goto err_rmap; 1211 } 1212 1213 return 0; 1214 1215 err_rmap: 1216 destroy_async_eqs(dev); 1217 err_async_eqs: 1218 return err; 1219 } 1220 1221 void mlx5_eq_table_destroy(struct mlx5_core_dev *dev) 1222 { 1223 struct mlx5_eq_table *table = dev->priv.eq_table; 1224 struct mlx5_eq_comp *eq; 1225 unsigned long index; 1226 1227 xa_for_each(&table->comp_eqs, index, eq) 1228 destroy_comp_eq(dev, eq, index); 1229 1230 free_rmap(dev); 1231 destroy_async_eqs(dev); 1232 } 1233 1234 int mlx5_eq_notifier_register(struct mlx5_core_dev *dev, struct mlx5_nb *nb) 1235 { 1236 struct mlx5_eq_table *eqt = dev->priv.eq_table; 1237 1238 return atomic_notifier_chain_register(&eqt->nh[nb->event_type], &nb->nb); 1239 } 1240 EXPORT_SYMBOL(mlx5_eq_notifier_register); 1241 1242 int mlx5_eq_notifier_unregister(struct mlx5_core_dev *dev, struct mlx5_nb *nb) 1243 { 1244 struct mlx5_eq_table *eqt = dev->priv.eq_table; 1245 1246 return atomic_notifier_chain_unregister(&eqt->nh[nb->event_type], &nb->nb); 1247 } 1248 EXPORT_SYMBOL(mlx5_eq_notifier_unregister); 1249