Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net/mlx5: Move the vhca event notifier outside of the devlink lock

The vhca event notifier consists of an atomic notifier for vhca state
changes (used for SF events), multiple workqueues and a blocking
notifier chain for delivering the vhca state change events for further
processing.

This patch moves the vhca notifier head outside of mlx5_init_one() /
mlx5_uninit_one() and into the mlx5_mdev_init() / mlx5_mdev_uninit()
functions.

This allows called notifiers to grab the PF devlink lock which was
previously impossible because it would create a circular lock
dependency.

mlx5_vhca_event_stop() is now called earlier in the cleanup phase and
flushes the workqueues to ensure that after the call, there are no
pending events. This simplifies the cleanup flow for vhca event
consumers.

Signed-off-by: Cosmin Ratiu <cratiu@nvidia.com>
Reviewed-by: Carolina Jubran <cjubran@nvidia.com>
Signed-off-by: Tariq Toukan <tariqt@nvidia.com>
Link: https://patch.msgid.link/1763325940-1231508-4-git-send-email-tariqt@nvidia.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Cosmin Ratiu and committed by
Jakub Kicinski
d3a356db 3fee8287

+35 -48
+2 -1
drivers/net/ethernet/mellanox/mlx5/core/main.c
··· 1438 1438 { 1439 1439 mlx5_eswitch_disable(dev->priv.eswitch); 1440 1440 mlx5_devlink_traps_unregister(priv_to_devlink(dev)); 1441 + mlx5_vhca_event_stop(dev); 1441 1442 mlx5_sf_dev_table_destroy(dev); 1442 1443 mlx5_sriov_detach(dev); 1443 1444 mlx5_lag_remove_mdev(dev); 1444 1445 mlx5_ec_cleanup(dev); 1445 1446 mlx5_sf_hw_table_destroy(dev); 1446 - mlx5_vhca_event_stop(dev); 1447 1447 mlx5_fs_core_cleanup(dev); 1448 1448 mlx5_fpga_device_stop(dev); 1449 1449 mlx5_rsc_dump_cleanup(dev); ··· 1835 1835 } 1836 1836 1837 1837 BLOCKING_INIT_NOTIFIER_HEAD(&dev->priv.esw_n_head); 1838 + mlx5_vhca_state_notifier_init(dev); 1838 1839 1839 1840 return 0; 1840 1841 }
-1
drivers/net/ethernet/mellanox/mlx5/core/sf/dev/dev.c
··· 381 381 382 382 mlx5_sf_dev_destroy_active_works(table); 383 383 mlx5_vhca_event_notifier_unregister(dev, &table->nb); 384 - mlx5_vhca_event_work_queues_flush(dev); 385 384 386 385 /* Now that event handler is not running, it is safe to destroy 387 386 * the sf device without race.
+1
drivers/net/ethernet/mellanox/mlx5/core/sf/hw_table.c
··· 389 389 return; 390 390 391 391 mlx5_vhca_event_notifier_unregister(dev, &table->vhca_nb); 392 + 392 393 /* Dealloc SFs whose firmware event has been missed. */ 393 394 mlx5_sf_hw_table_dealloc_all(table); 394 395 }
+25 -44
drivers/net/ethernet/mellanox/mlx5/core/sf/vhca_event.c
··· 9 9 #define CREATE_TRACE_POINTS 10 10 #include "diag/vhca_tracepoint.h" 11 11 12 - struct mlx5_vhca_state_notifier { 13 - struct mlx5_core_dev *dev; 14 - struct mlx5_nb nb; 15 - struct blocking_notifier_head n_head; 16 - }; 17 - 18 12 struct mlx5_vhca_event_work { 19 13 struct work_struct work; 20 - struct mlx5_vhca_state_notifier *notifier; 14 + struct mlx5_core_dev *dev; 21 15 struct mlx5_vhca_state_event event; 22 16 }; 23 17 ··· 89 95 mlx5_vhca_event_arm(dev, event->function_id); 90 96 trace_mlx5_sf_vhca_event(dev, event); 91 97 92 - blocking_notifier_call_chain(&dev->priv.vhca_state_notifier->n_head, 0, event); 98 + blocking_notifier_call_chain(&dev->priv.vhca_state_n_head, 0, event); 93 99 } 94 100 95 101 static void mlx5_vhca_state_work_handler(struct work_struct *_work) 96 102 { 97 103 struct mlx5_vhca_event_work *work = container_of(_work, struct mlx5_vhca_event_work, work); 98 - struct mlx5_vhca_state_notifier *notifier = work->notifier; 99 - struct mlx5_core_dev *dev = notifier->dev; 100 104 101 - mlx5_vhca_event_notify(dev, &work->event); 105 + mlx5_vhca_event_notify(work->dev, &work->event); 102 106 kfree(work); 103 107 } 104 108 ··· 108 116 static int 109 117 mlx5_vhca_state_change_notifier(struct notifier_block *nb, unsigned long type, void *data) 110 118 { 111 - struct mlx5_vhca_state_notifier *notifier = 112 - mlx5_nb_cof(nb, struct mlx5_vhca_state_notifier, nb); 119 + struct mlx5_core_dev *dev = mlx5_nb_cof(nb, struct mlx5_core_dev, 120 + priv.vhca_state_nb); 113 121 struct mlx5_vhca_event_work *work; 114 122 struct mlx5_eqe *eqe = data; 115 123 int wq_idx; ··· 118 126 if (!work) 119 127 return NOTIFY_DONE; 120 128 INIT_WORK(&work->work, &mlx5_vhca_state_work_handler); 121 - work->notifier = notifier; 129 + work->dev = dev; 122 130 work->event.function_id = be16_to_cpu(eqe->data.vhca_state.function_id); 123 131 wq_idx = work->event.function_id % MLX5_DEV_MAX_WQS; 124 - mlx5_vhca_events_work_enqueue(notifier->dev, wq_idx, &work->work); 132 + mlx5_vhca_events_work_enqueue(dev, wq_idx, &work->work); 125 133 return NOTIFY_OK; 126 134 } 127 135 ··· 137 145 MLX5_SET(cmd_hca_cap, set_hca_cap, event_on_vhca_state_teardown_request, 1); 138 146 } 139 147 148 + void mlx5_vhca_state_notifier_init(struct mlx5_core_dev *dev) 149 + { 150 + BLOCKING_INIT_NOTIFIER_HEAD(&dev->priv.vhca_state_n_head); 151 + MLX5_NB_INIT(&dev->priv.vhca_state_nb, mlx5_vhca_state_change_notifier, 152 + VHCA_STATE_CHANGE); 153 + } 154 + 140 155 int mlx5_vhca_event_init(struct mlx5_core_dev *dev) 141 156 { 142 - struct mlx5_vhca_state_notifier *notifier; 143 157 char wq_name[MLX5_CMD_WQ_MAX_NAME]; 144 158 struct mlx5_vhca_events *events; 145 159 int err, i; ··· 158 160 return -ENOMEM; 159 161 160 162 events->dev = dev; 161 - dev->priv.vhca_events = events; 162 163 for (i = 0; i < MLX5_DEV_MAX_WQS; i++) { 163 164 snprintf(wq_name, MLX5_CMD_WQ_MAX_NAME, "mlx5_vhca_event%d", i); 164 165 events->handler[i].wq = create_singlethread_workqueue(wq_name); ··· 166 169 goto err_create_wq; 167 170 } 168 171 } 172 + dev->priv.vhca_events = events; 169 173 170 - notifier = kzalloc(sizeof(*notifier), GFP_KERNEL); 171 - if (!notifier) { 172 - err = -ENOMEM; 173 - goto err_notifier; 174 - } 175 - 176 - dev->priv.vhca_state_notifier = notifier; 177 - notifier->dev = dev; 178 - BLOCKING_INIT_NOTIFIER_HEAD(&notifier->n_head); 179 - MLX5_NB_INIT(&notifier->nb, mlx5_vhca_state_change_notifier, VHCA_STATE_CHANGE); 180 174 return 0; 181 175 182 - err_notifier: 183 176 err_create_wq: 184 177 for (--i; i >= 0; i--) 185 178 destroy_workqueue(events->handler[i].wq); ··· 198 211 if (!mlx5_vhca_event_supported(dev)) 199 212 return; 200 213 201 - kfree(dev->priv.vhca_state_notifier); 202 - dev->priv.vhca_state_notifier = NULL; 203 214 vhca_events = dev->priv.vhca_events; 204 215 for (i = 0; i < MLX5_DEV_MAX_WQS; i++) 205 216 destroy_workqueue(vhca_events->handler[i].wq); ··· 206 221 207 222 void mlx5_vhca_event_start(struct mlx5_core_dev *dev) 208 223 { 209 - struct mlx5_vhca_state_notifier *notifier; 210 - 211 - if (!dev->priv.vhca_state_notifier) 224 + if (!mlx5_vhca_event_supported(dev)) 212 225 return; 213 226 214 - notifier = dev->priv.vhca_state_notifier; 215 - mlx5_eq_notifier_register(dev, &notifier->nb); 227 + mlx5_eq_notifier_register(dev, &dev->priv.vhca_state_nb); 216 228 } 217 229 218 230 void mlx5_vhca_event_stop(struct mlx5_core_dev *dev) 219 231 { 220 - struct mlx5_vhca_state_notifier *notifier; 221 - 222 - if (!dev->priv.vhca_state_notifier) 232 + if (!mlx5_vhca_event_supported(dev)) 223 233 return; 224 234 225 - notifier = dev->priv.vhca_state_notifier; 226 - mlx5_eq_notifier_unregister(dev, &notifier->nb); 235 + mlx5_eq_notifier_unregister(dev, &dev->priv.vhca_state_nb); 236 + 237 + /* Flush workqueues of all pending events. */ 238 + mlx5_vhca_event_work_queues_flush(dev); 227 239 } 228 240 229 241 int mlx5_vhca_event_notifier_register(struct mlx5_core_dev *dev, struct notifier_block *nb) 230 242 { 231 - if (!dev->priv.vhca_state_notifier) 232 - return -EOPNOTSUPP; 233 - return blocking_notifier_chain_register(&dev->priv.vhca_state_notifier->n_head, nb); 243 + return blocking_notifier_chain_register(&dev->priv.vhca_state_n_head, 244 + nb); 234 245 } 235 246 236 247 void mlx5_vhca_event_notifier_unregister(struct mlx5_core_dev *dev, struct notifier_block *nb) 237 248 { 238 - blocking_notifier_chain_unregister(&dev->priv.vhca_state_notifier->n_head, nb); 249 + blocking_notifier_chain_unregister(&dev->priv.vhca_state_n_head, nb); 239 250 }
+5
drivers/net/ethernet/mellanox/mlx5/core/sf/vhca_event.h
··· 18 18 } 19 19 20 20 void mlx5_vhca_state_cap_handle(struct mlx5_core_dev *dev, void *set_hca_cap); 21 + void mlx5_vhca_state_notifier_init(struct mlx5_core_dev *dev); 21 22 int mlx5_vhca_event_init(struct mlx5_core_dev *dev); 22 23 void mlx5_vhca_event_cleanup(struct mlx5_core_dev *dev); 23 24 void mlx5_vhca_event_start(struct mlx5_core_dev *dev); ··· 35 34 #else 36 35 37 36 static inline void mlx5_vhca_state_cap_handle(struct mlx5_core_dev *dev, void *set_hca_cap) 37 + { 38 + } 39 + 40 + static inline void mlx5_vhca_state_notifier_init(struct mlx5_core_dev *dev) 38 41 { 39 42 } 40 43
+2 -2
include/linux/mlx5/driver.h
··· 488 488 struct mlx5_fw_reset; 489 489 struct mlx5_eq_table; 490 490 struct mlx5_irq_table; 491 - struct mlx5_vhca_state_notifier; 492 491 struct mlx5_sf_dev_table; 493 492 struct mlx5_sf_hw_table; 494 493 struct mlx5_sf_table; ··· 614 615 struct mlx5_bfreg_data bfregs; 615 616 struct mlx5_sq_bfreg bfreg; 616 617 #ifdef CONFIG_MLX5_SF 617 - struct mlx5_vhca_state_notifier *vhca_state_notifier; 618 + struct mlx5_nb vhca_state_nb; 619 + struct blocking_notifier_head vhca_state_n_head; 618 620 struct mlx5_sf_dev_table *sf_dev_table; 619 621 struct mlx5_core_dev *parent_mdev; 620 622 #endif