VYPR
Unrated severityNVD Advisory· Published May 27, 2026· Updated May 27, 2026

CVE-2026-45973

CVE-2026-45973

Description

In the Linux kernel, the following vulnerability has been resolved:

RDMA/mlx5: Fix UMR hang in LAG error state unload

During firmware reset in LAG mode, a race condition causes the driver to hang indefinitely while waiting for UMR completion during device unload. See [1].

In LAG mode the bond device is only registered on the master, so it never sees sys_error events from the slave. During firmware reset this causes UMR waits to hang forever on unload as the slave is dead but the master hasn't entered error state yet, so UMR posts succeed but completions never arrive.

Fix this by adding a sys_error notifier that gets registered before MLX5_IB_STAGE_IB_REG and stays alive until after ib_unregister_device(). This ensures error events reach the bond device throughout teardown.

[1] Call Trace: __schedule+0x2bd/0x760 schedule+0x37/0xa0 schedule_preempt_disabled+0xa/0x10 __mutex_lock.isra.6+0x2b5/0x4a0 __mlx5_ib_dereg_mr+0x606/0x870 [mlx5_ib] ? __xa_erase+0x4a/0xa0 ? _cond_resched+0x15/0x30 ? wait_for_completion+0x31/0x100 ib_dereg_mr_user+0x48/0xc0 [ib_core] ? rdmacg_uncharge_hierarchy+0xa0/0x100 destroy_hw_idr_uobject+0x20/0x50 [ib_uverbs] uverbs_destroy_uobject+0x37/0x150 [ib_uverbs] __uverbs_cleanup_ufile+0xda/0x140 [ib_uverbs] uverbs_destroy_ufile_hw+0x3a/0xf0 [ib_uverbs] ib_uverbs_remove_one+0xc3/0x140 [ib_uverbs] remove_client_context+0x8b/0xd0 [ib_core] disable_device+0x8c/0x130 [ib_core] __ib_unregister_device+0x10d/0x180 [ib_core] ib_unregister_device+0x21/0x30 [ib_core] __mlx5_ib_remove+0x1e4/0x1f0 [mlx5_ib] auxiliary_bus_remove+0x1e/0x30 device_release_driver_internal+0x103/0x1f0 bus_remove_device+0xf7/0x170 device_del+0x181/0x410 mlx5_rescan_drivers_locked.part.10+0xa9/0x1d0 [mlx5_core] mlx5_disable_lag+0x253/0x260 [mlx5_core] mlx5_lag_disable_change+0x89/0xc0 [mlx5_core] mlx5_eswitch_disable+0x67/0xa0 [mlx5_core] mlx5_unload+0x15/0xd0 [mlx5_core] mlx5_unload_one+0x71/0xc0 [mlx5_core] mlx5_sync_reset_reload_work+0x83/0x100 [mlx5_core] process_one_work+0x1a7/0x360 worker_thread+0x30/0x390 ? create_worker+0x1a0/0x1a0 kthread+0x116/0x130 ? kthread_flush_work_fn+0x10/0x10 ret_from_fork+0x22/0x40

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

A race condition in Linux kernel RDMA/mlx5 driver causes UMR hang during LAG error state unload, potentially leading to indefinite driver stall.

Vulnerability

The Linux kernel RDMA/mlx5 driver contains a race condition triggered during firmware reset in LAG (Link Aggregation) mode. The bond device is only registered on the master, so it never receives sys_error events from the slave. During device unload when a firmware reset occurs, this causes UMR (User Memory Registration) waits to hang forever because the slave is dead but the master has not yet entered the error state. UMR posts succeed but completions never arrive. Affected versions include those prior to the fix commit 613f5d4139b6 in the mlx5_ib module. [1]

Exploitation

An attacker with the ability to trigger a firmware reset on a system configured with LAG where an RDMA device is in use may exploit this race. No special local privileges beyond the ability to cause the reset condition are required. The attack sequence involves initiating a firmware reset, which causes the slave device to fail; the master remains active, allowing UMR posts to succeed while the completions are never delivered due to the slave's death. This leads to a hang in the __mlx5_ib_dereg_mr function waiting on UMR completion during device unload. [1]

Impact

A successful exploitation results in indefinite driver hang (denial of service) during device unload, making the system unresponsive for that device. The hang is observed in the kernel call trace, specifically at __mlx5_ib_dereg_mr waiting for a completion. This impacts system availability when the device is removed or rebooted, potentially causing disruption. There is no risk of data corruption or unauthorized access. [1]

Mitigation

The fix was implemented in the Linux kernel commit 613f5d4139b6 by adding a sys_error notifier that gets registered before MLX5_IB_STAGE_IB_REG and stays alive until after ib_unregister_device(). This ensures error events reach the bond device throughout teardown. Users should update to a kernel version containing this commit. No workarounds are mentioned in the available references. The vulnerability is not listed as being exploited in the wild in any known CISA KEV. [1]

AI Insight generated on May 27, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected products

1

Patches

8
613f5d4139b6

RDMA/mlx5: Fix UMR hang in LAG error state unload

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitChiara MeiohasJan 13, 2026Fixed in 6.19.4via kernel-cna
2 files changed · +68 10
  • drivers/infiniband/hw/mlx5/main.c+66 9 modified
    diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c
    index 8d515d266125e6..3485a9a3d75e0d 100644
    --- a/drivers/infiniband/hw/mlx5/main.c
    +++ b/drivers/infiniband/hw/mlx5/main.c
    @@ -2878,7 +2878,6 @@ static void mlx5_ib_handle_event(struct work_struct *_work)
     		container_of(_work, struct mlx5_ib_event_work, work);
     	struct mlx5_ib_dev *ibdev;
     	struct ib_event ibev;
    -	bool fatal = false;
     
     	if (work->is_slave) {
     		ibdev = mlx5_ib_get_ibdev_from_mpi(work->mpi);
    @@ -2889,12 +2888,6 @@ static void mlx5_ib_handle_event(struct work_struct *_work)
     	}
     
     	switch (work->event) {
    -	case MLX5_DEV_EVENT_SYS_ERROR:
    -		ibev.event = IB_EVENT_DEVICE_FATAL;
    -		mlx5_ib_handle_internal_error(ibdev);
    -		ibev.element.port_num  = (u8)(unsigned long)work->param;
    -		fatal = true;
    -		break;
     	case MLX5_EVENT_TYPE_PORT_CHANGE:
     		if (handle_port_change(ibdev, work->param, &ibev))
     			goto out;
    @@ -2916,8 +2909,6 @@ static void mlx5_ib_handle_event(struct work_struct *_work)
     	if (ibdev->ib_active)
     		ib_dispatch_event(&ibev);
     
    -	if (fatal)
    -		ibdev->ib_active = false;
     out:
     	kfree(work);
     }
    @@ -2961,6 +2952,66 @@ static int mlx5_ib_event_slave_port(struct notifier_block *nb,
     	return NOTIFY_OK;
     }
     
    +static void mlx5_ib_handle_sys_error_event(struct work_struct *_work)
    +{
    +	struct mlx5_ib_event_work *work =
    +		container_of(_work, struct mlx5_ib_event_work, work);
    +	struct mlx5_ib_dev *ibdev = work->dev;
    +	struct ib_event ibev;
    +
    +	ibev.event = IB_EVENT_DEVICE_FATAL;
    +	mlx5_ib_handle_internal_error(ibdev);
    +	ibev.element.port_num = (u8)(unsigned long)work->param;
    +	ibev.device = &ibdev->ib_dev;
    +
    +	if (!rdma_is_port_valid(&ibdev->ib_dev, ibev.element.port_num)) {
    +		mlx5_ib_warn(ibdev, "warning: event on port %d\n",  ibev.element.port_num);
    +		goto out;
    +	}
    +
    +	if (ibdev->ib_active)
    +		ib_dispatch_event(&ibev);
    +
    +	ibdev->ib_active = false;
    +out:
    +	kfree(work);
    +}
    +
    +static int mlx5_ib_sys_error_event(struct notifier_block *nb,
    +				   unsigned long event, void *param)
    +{
    +	struct mlx5_ib_event_work *work;
    +
    +	if (event != MLX5_DEV_EVENT_SYS_ERROR)
    +		return NOTIFY_DONE;
    +
    +	work = kmalloc(sizeof(*work), GFP_ATOMIC);
    +	if (!work)
    +		return NOTIFY_DONE;
    +
    +	INIT_WORK(&work->work, mlx5_ib_handle_sys_error_event);
    +	work->dev = container_of(nb, struct mlx5_ib_dev, sys_error_events);
    +	work->is_slave = false;
    +	work->param = param;
    +	work->event = event;
    +
    +	queue_work(mlx5_ib_event_wq, &work->work);
    +
    +	return NOTIFY_OK;
    +}
    +
    +static int mlx5_ib_stage_sys_error_notifier_init(struct mlx5_ib_dev *dev)
    +{
    +	dev->sys_error_events.notifier_call = mlx5_ib_sys_error_event;
    +	mlx5_notifier_register(dev->mdev, &dev->sys_error_events);
    +	return 0;
    +}
    +
    +static void mlx5_ib_stage_sys_error_notifier_cleanup(struct mlx5_ib_dev *dev)
    +{
    +	mlx5_notifier_unregister(dev->mdev, &dev->sys_error_events);
    +}
    +
     static int mlx5_ib_get_plane_num(struct mlx5_core_dev *mdev, u8 *num_plane)
     {
     	struct mlx5_hca_vport_context vport_ctx;
    @@ -4811,6 +4862,9 @@ static const struct mlx5_ib_profile pf_profile = {
     	STAGE_CREATE(MLX5_IB_STAGE_WHITELIST_UID,
     		     mlx5_ib_devx_init,
     		     mlx5_ib_devx_cleanup),
    +	STAGE_CREATE(MLX5_IB_STAGE_SYS_ERROR_NOTIFIER,
    +		     mlx5_ib_stage_sys_error_notifier_init,
    +		     mlx5_ib_stage_sys_error_notifier_cleanup),
     	STAGE_CREATE(MLX5_IB_STAGE_IB_REG,
     		     mlx5_ib_stage_ib_reg_init,
     		     mlx5_ib_stage_ib_reg_cleanup),
    @@ -4868,6 +4922,9 @@ const struct mlx5_ib_profile raw_eth_profile = {
     	STAGE_CREATE(MLX5_IB_STAGE_WHITELIST_UID,
     		     mlx5_ib_devx_init,
     		     mlx5_ib_devx_cleanup),
    +	STAGE_CREATE(MLX5_IB_STAGE_SYS_ERROR_NOTIFIER,
    +		     mlx5_ib_stage_sys_error_notifier_init,
    +		     mlx5_ib_stage_sys_error_notifier_cleanup),
     	STAGE_CREATE(MLX5_IB_STAGE_IB_REG,
     		     mlx5_ib_stage_ib_reg_init,
     		     mlx5_ib_stage_ib_reg_cleanup),
    
  • drivers/infiniband/hw/mlx5/mlx5_ib.h+2 1 modified
    diff --git a/drivers/infiniband/hw/mlx5/mlx5_ib.h b/drivers/infiniband/hw/mlx5/mlx5_ib.h
    index 09d82d5f95e354..fbccb0362590bb 100644
    --- a/drivers/infiniband/hw/mlx5/mlx5_ib.h
    +++ b/drivers/infiniband/hw/mlx5/mlx5_ib.h
    @@ -1007,6 +1007,7 @@ enum mlx5_ib_stages {
     	MLX5_IB_STAGE_BFREG,
     	MLX5_IB_STAGE_PRE_IB_REG_UMR,
     	MLX5_IB_STAGE_WHITELIST_UID,
    +	MLX5_IB_STAGE_SYS_ERROR_NOTIFIER,
     	MLX5_IB_STAGE_IB_REG,
     	MLX5_IB_STAGE_DEVICE_NOTIFIER,
     	MLX5_IB_STAGE_POST_IB_REG_UMR,
    @@ -1165,6 +1166,7 @@ struct mlx5_ib_dev {
     	/* protect accessing data_direct_dev */
     	struct mutex			data_direct_lock;
     	struct notifier_block		mdev_events;
    +	struct notifier_block		sys_error_events;
     	struct notifier_block           lag_events;
     	int				num_ports;
     	/* serialize update of capability mask
    -- 
    cgit 1.3-korg
    
    
    
6d838873da9c

RDMA/mlx5: Fix UMR hang in LAG error state unload

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitChiara MeiohasJan 13, 2026Fixed in 6.18.14via kernel-cna
2 files changed · +68 10
  • drivers/infiniband/hw/mlx5/main.c+66 9 modified
    diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c
    index 8f69c8c1ba54d6..b6096f9126858a 100644
    --- a/drivers/infiniband/hw/mlx5/main.c
    +++ b/drivers/infiniband/hw/mlx5/main.c
    @@ -2874,7 +2874,6 @@ static void mlx5_ib_handle_event(struct work_struct *_work)
     		container_of(_work, struct mlx5_ib_event_work, work);
     	struct mlx5_ib_dev *ibdev;
     	struct ib_event ibev;
    -	bool fatal = false;
     
     	if (work->is_slave) {
     		ibdev = mlx5_ib_get_ibdev_from_mpi(work->mpi);
    @@ -2885,12 +2884,6 @@ static void mlx5_ib_handle_event(struct work_struct *_work)
     	}
     
     	switch (work->event) {
    -	case MLX5_DEV_EVENT_SYS_ERROR:
    -		ibev.event = IB_EVENT_DEVICE_FATAL;
    -		mlx5_ib_handle_internal_error(ibdev);
    -		ibev.element.port_num  = (u8)(unsigned long)work->param;
    -		fatal = true;
    -		break;
     	case MLX5_EVENT_TYPE_PORT_CHANGE:
     		if (handle_port_change(ibdev, work->param, &ibev))
     			goto out;
    @@ -2912,8 +2905,6 @@ static void mlx5_ib_handle_event(struct work_struct *_work)
     	if (ibdev->ib_active)
     		ib_dispatch_event(&ibev);
     
    -	if (fatal)
    -		ibdev->ib_active = false;
     out:
     	kfree(work);
     }
    @@ -2957,6 +2948,66 @@ static int mlx5_ib_event_slave_port(struct notifier_block *nb,
     	return NOTIFY_OK;
     }
     
    +static void mlx5_ib_handle_sys_error_event(struct work_struct *_work)
    +{
    +	struct mlx5_ib_event_work *work =
    +		container_of(_work, struct mlx5_ib_event_work, work);
    +	struct mlx5_ib_dev *ibdev = work->dev;
    +	struct ib_event ibev;
    +
    +	ibev.event = IB_EVENT_DEVICE_FATAL;
    +	mlx5_ib_handle_internal_error(ibdev);
    +	ibev.element.port_num = (u8)(unsigned long)work->param;
    +	ibev.device = &ibdev->ib_dev;
    +
    +	if (!rdma_is_port_valid(&ibdev->ib_dev, ibev.element.port_num)) {
    +		mlx5_ib_warn(ibdev, "warning: event on port %d\n",  ibev.element.port_num);
    +		goto out;
    +	}
    +
    +	if (ibdev->ib_active)
    +		ib_dispatch_event(&ibev);
    +
    +	ibdev->ib_active = false;
    +out:
    +	kfree(work);
    +}
    +
    +static int mlx5_ib_sys_error_event(struct notifier_block *nb,
    +				   unsigned long event, void *param)
    +{
    +	struct mlx5_ib_event_work *work;
    +
    +	if (event != MLX5_DEV_EVENT_SYS_ERROR)
    +		return NOTIFY_DONE;
    +
    +	work = kmalloc(sizeof(*work), GFP_ATOMIC);
    +	if (!work)
    +		return NOTIFY_DONE;
    +
    +	INIT_WORK(&work->work, mlx5_ib_handle_sys_error_event);
    +	work->dev = container_of(nb, struct mlx5_ib_dev, sys_error_events);
    +	work->is_slave = false;
    +	work->param = param;
    +	work->event = event;
    +
    +	queue_work(mlx5_ib_event_wq, &work->work);
    +
    +	return NOTIFY_OK;
    +}
    +
    +static int mlx5_ib_stage_sys_error_notifier_init(struct mlx5_ib_dev *dev)
    +{
    +	dev->sys_error_events.notifier_call = mlx5_ib_sys_error_event;
    +	mlx5_notifier_register(dev->mdev, &dev->sys_error_events);
    +	return 0;
    +}
    +
    +static void mlx5_ib_stage_sys_error_notifier_cleanup(struct mlx5_ib_dev *dev)
    +{
    +	mlx5_notifier_unregister(dev->mdev, &dev->sys_error_events);
    +}
    +
     static int mlx5_ib_get_plane_num(struct mlx5_core_dev *mdev, u8 *num_plane)
     {
     	struct mlx5_hca_vport_context vport_ctx;
    @@ -4807,6 +4858,9 @@ static const struct mlx5_ib_profile pf_profile = {
     	STAGE_CREATE(MLX5_IB_STAGE_WHITELIST_UID,
     		     mlx5_ib_devx_init,
     		     mlx5_ib_devx_cleanup),
    +	STAGE_CREATE(MLX5_IB_STAGE_SYS_ERROR_NOTIFIER,
    +		     mlx5_ib_stage_sys_error_notifier_init,
    +		     mlx5_ib_stage_sys_error_notifier_cleanup),
     	STAGE_CREATE(MLX5_IB_STAGE_IB_REG,
     		     mlx5_ib_stage_ib_reg_init,
     		     mlx5_ib_stage_ib_reg_cleanup),
    @@ -4864,6 +4918,9 @@ const struct mlx5_ib_profile raw_eth_profile = {
     	STAGE_CREATE(MLX5_IB_STAGE_WHITELIST_UID,
     		     mlx5_ib_devx_init,
     		     mlx5_ib_devx_cleanup),
    +	STAGE_CREATE(MLX5_IB_STAGE_SYS_ERROR_NOTIFIER,
    +		     mlx5_ib_stage_sys_error_notifier_init,
    +		     mlx5_ib_stage_sys_error_notifier_cleanup),
     	STAGE_CREATE(MLX5_IB_STAGE_IB_REG,
     		     mlx5_ib_stage_ib_reg_init,
     		     mlx5_ib_stage_ib_reg_cleanup),
    
  • drivers/infiniband/hw/mlx5/mlx5_ib.h+2 1 modified
    diff --git a/drivers/infiniband/hw/mlx5/mlx5_ib.h b/drivers/infiniband/hw/mlx5/mlx5_ib.h
    index 09d82d5f95e354..fbccb0362590bb 100644
    --- a/drivers/infiniband/hw/mlx5/mlx5_ib.h
    +++ b/drivers/infiniband/hw/mlx5/mlx5_ib.h
    @@ -1007,6 +1007,7 @@ enum mlx5_ib_stages {
     	MLX5_IB_STAGE_BFREG,
     	MLX5_IB_STAGE_PRE_IB_REG_UMR,
     	MLX5_IB_STAGE_WHITELIST_UID,
    +	MLX5_IB_STAGE_SYS_ERROR_NOTIFIER,
     	MLX5_IB_STAGE_IB_REG,
     	MLX5_IB_STAGE_DEVICE_NOTIFIER,
     	MLX5_IB_STAGE_POST_IB_REG_UMR,
    @@ -1165,6 +1166,7 @@ struct mlx5_ib_dev {
     	/* protect accessing data_direct_dev */
     	struct mutex			data_direct_lock;
     	struct notifier_block		mdev_events;
    +	struct notifier_block		sys_error_events;
     	struct notifier_block           lag_events;
     	int				num_ports;
     	/* serialize update of capability mask
    -- 
    cgit 1.3-korg
    
    
    
c8fb5c965ac7

RDMA/mlx5: Fix UMR hang in LAG error state unload

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitChiara MeiohasJan 13, 2026Fixed in 6.12.75via kernel-cna
2 files changed · +68 10
  • drivers/infiniband/hw/mlx5/main.c+66 9 modified
    diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c
    index f3e58797705d72..10bda03eb3388b 100644
    --- a/drivers/infiniband/hw/mlx5/main.c
    +++ b/drivers/infiniband/hw/mlx5/main.c
    @@ -2826,7 +2826,6 @@ static void mlx5_ib_handle_event(struct work_struct *_work)
     		container_of(_work, struct mlx5_ib_event_work, work);
     	struct mlx5_ib_dev *ibdev;
     	struct ib_event ibev;
    -	bool fatal = false;
     
     	if (work->is_slave) {
     		ibdev = mlx5_ib_get_ibdev_from_mpi(work->mpi);
    @@ -2837,12 +2836,6 @@ static void mlx5_ib_handle_event(struct work_struct *_work)
     	}
     
     	switch (work->event) {
    -	case MLX5_DEV_EVENT_SYS_ERROR:
    -		ibev.event = IB_EVENT_DEVICE_FATAL;
    -		mlx5_ib_handle_internal_error(ibdev);
    -		ibev.element.port_num  = (u8)(unsigned long)work->param;
    -		fatal = true;
    -		break;
     	case MLX5_EVENT_TYPE_PORT_CHANGE:
     		if (handle_port_change(ibdev, work->param, &ibev))
     			goto out;
    @@ -2864,8 +2857,6 @@ static void mlx5_ib_handle_event(struct work_struct *_work)
     	if (ibdev->ib_active)
     		ib_dispatch_event(&ibev);
     
    -	if (fatal)
    -		ibdev->ib_active = false;
     out:
     	kfree(work);
     }
    @@ -2909,6 +2900,66 @@ static int mlx5_ib_event_slave_port(struct notifier_block *nb,
     	return NOTIFY_OK;
     }
     
    +static void mlx5_ib_handle_sys_error_event(struct work_struct *_work)
    +{
    +	struct mlx5_ib_event_work *work =
    +		container_of(_work, struct mlx5_ib_event_work, work);
    +	struct mlx5_ib_dev *ibdev = work->dev;
    +	struct ib_event ibev;
    +
    +	ibev.event = IB_EVENT_DEVICE_FATAL;
    +	mlx5_ib_handle_internal_error(ibdev);
    +	ibev.element.port_num = (u8)(unsigned long)work->param;
    +	ibev.device = &ibdev->ib_dev;
    +
    +	if (!rdma_is_port_valid(&ibdev->ib_dev, ibev.element.port_num)) {
    +		mlx5_ib_warn(ibdev, "warning: event on port %d\n",  ibev.element.port_num);
    +		goto out;
    +	}
    +
    +	if (ibdev->ib_active)
    +		ib_dispatch_event(&ibev);
    +
    +	ibdev->ib_active = false;
    +out:
    +	kfree(work);
    +}
    +
    +static int mlx5_ib_sys_error_event(struct notifier_block *nb,
    +				   unsigned long event, void *param)
    +{
    +	struct mlx5_ib_event_work *work;
    +
    +	if (event != MLX5_DEV_EVENT_SYS_ERROR)
    +		return NOTIFY_DONE;
    +
    +	work = kmalloc(sizeof(*work), GFP_ATOMIC);
    +	if (!work)
    +		return NOTIFY_DONE;
    +
    +	INIT_WORK(&work->work, mlx5_ib_handle_sys_error_event);
    +	work->dev = container_of(nb, struct mlx5_ib_dev, sys_error_events);
    +	work->is_slave = false;
    +	work->param = param;
    +	work->event = event;
    +
    +	queue_work(mlx5_ib_event_wq, &work->work);
    +
    +	return NOTIFY_OK;
    +}
    +
    +static int mlx5_ib_stage_sys_error_notifier_init(struct mlx5_ib_dev *dev)
    +{
    +	dev->sys_error_events.notifier_call = mlx5_ib_sys_error_event;
    +	mlx5_notifier_register(dev->mdev, &dev->sys_error_events);
    +	return 0;
    +}
    +
    +static void mlx5_ib_stage_sys_error_notifier_cleanup(struct mlx5_ib_dev *dev)
    +{
    +	mlx5_notifier_unregister(dev->mdev, &dev->sys_error_events);
    +}
    +
     static int mlx5_ib_get_plane_num(struct mlx5_core_dev *mdev, u8 *num_plane)
     {
     	struct mlx5_hca_vport_context vport_ctx;
    @@ -4682,6 +4733,9 @@ static const struct mlx5_ib_profile pf_profile = {
     	STAGE_CREATE(MLX5_IB_STAGE_WHITELIST_UID,
     		     mlx5_ib_devx_init,
     		     mlx5_ib_devx_cleanup),
    +	STAGE_CREATE(MLX5_IB_STAGE_SYS_ERROR_NOTIFIER,
    +		     mlx5_ib_stage_sys_error_notifier_init,
    +		     mlx5_ib_stage_sys_error_notifier_cleanup),
     	STAGE_CREATE(MLX5_IB_STAGE_IB_REG,
     		     mlx5_ib_stage_ib_reg_init,
     		     mlx5_ib_stage_ib_reg_cleanup),
    @@ -4742,6 +4796,9 @@ const struct mlx5_ib_profile raw_eth_profile = {
     	STAGE_CREATE(MLX5_IB_STAGE_WHITELIST_UID,
     		     mlx5_ib_devx_init,
     		     mlx5_ib_devx_cleanup),
    +	STAGE_CREATE(MLX5_IB_STAGE_SYS_ERROR_NOTIFIER,
    +		     mlx5_ib_stage_sys_error_notifier_init,
    +		     mlx5_ib_stage_sys_error_notifier_cleanup),
     	STAGE_CREATE(MLX5_IB_STAGE_IB_REG,
     		     mlx5_ib_stage_ib_reg_init,
     		     mlx5_ib_stage_ib_reg_cleanup),
    
  • drivers/infiniband/hw/mlx5/mlx5_ib.h+2 1 modified
    diff --git a/drivers/infiniband/hw/mlx5/mlx5_ib.h b/drivers/infiniband/hw/mlx5/mlx5_ib.h
    index f49cb588a856d5..3135519f1cfdfd 100644
    --- a/drivers/infiniband/hw/mlx5/mlx5_ib.h
    +++ b/drivers/infiniband/hw/mlx5/mlx5_ib.h
    @@ -979,6 +979,7 @@ enum mlx5_ib_stages {
     	MLX5_IB_STAGE_BFREG,
     	MLX5_IB_STAGE_PRE_IB_REG_UMR,
     	MLX5_IB_STAGE_WHITELIST_UID,
    +	MLX5_IB_STAGE_SYS_ERROR_NOTIFIER,
     	MLX5_IB_STAGE_IB_REG,
     	MLX5_IB_STAGE_DEVICE_NOTIFIER,
     	MLX5_IB_STAGE_POST_IB_REG_UMR,
    @@ -1137,6 +1138,7 @@ struct mlx5_ib_dev {
     	/* protect accessing data_direct_dev */
     	struct mutex			data_direct_lock;
     	struct notifier_block		mdev_events;
    +	struct notifier_block		sys_error_events;
     	struct notifier_block           lag_events;
     	int				num_ports;
     	/* serialize update of capability mask
    -- 
    cgit 1.3-korg
    
    
    
ebc2164a4cd4

RDMA/mlx5: Fix UMR hang in LAG error state unload

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitChiara MeiohasJan 13, 2026Fixed in 7.0via kernel-cna
2 files changed · +68 10
  • drivers/infiniband/hw/mlx5/main.c+66 9 modified
    diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c
    index e81080622283c1..e83a5f12e6bcd6 100644
    --- a/drivers/infiniband/hw/mlx5/main.c
    +++ b/drivers/infiniband/hw/mlx5/main.c
    @@ -3009,7 +3009,6 @@ static void mlx5_ib_handle_event(struct work_struct *_work)
     		container_of(_work, struct mlx5_ib_event_work, work);
     	struct mlx5_ib_dev *ibdev;
     	struct ib_event ibev;
    -	bool fatal = false;
     
     	if (work->is_slave) {
     		ibdev = mlx5_ib_get_ibdev_from_mpi(work->mpi);
    @@ -3020,12 +3019,6 @@ static void mlx5_ib_handle_event(struct work_struct *_work)
     	}
     
     	switch (work->event) {
    -	case MLX5_DEV_EVENT_SYS_ERROR:
    -		ibev.event = IB_EVENT_DEVICE_FATAL;
    -		mlx5_ib_handle_internal_error(ibdev);
    -		ibev.element.port_num  = (u8)(unsigned long)work->param;
    -		fatal = true;
    -		break;
     	case MLX5_EVENT_TYPE_PORT_CHANGE:
     		if (handle_port_change(ibdev, work->param, &ibev))
     			goto out;
    @@ -3047,8 +3040,6 @@ static void mlx5_ib_handle_event(struct work_struct *_work)
     	if (ibdev->ib_active)
     		ib_dispatch_event(&ibev);
     
    -	if (fatal)
    -		ibdev->ib_active = false;
     out:
     	kfree(work);
     }
    @@ -3092,6 +3083,66 @@ static int mlx5_ib_event_slave_port(struct notifier_block *nb,
     	return NOTIFY_OK;
     }
     
    +static void mlx5_ib_handle_sys_error_event(struct work_struct *_work)
    +{
    +	struct mlx5_ib_event_work *work =
    +		container_of(_work, struct mlx5_ib_event_work, work);
    +	struct mlx5_ib_dev *ibdev = work->dev;
    +	struct ib_event ibev;
    +
    +	ibev.event = IB_EVENT_DEVICE_FATAL;
    +	mlx5_ib_handle_internal_error(ibdev);
    +	ibev.element.port_num = (u8)(unsigned long)work->param;
    +	ibev.device = &ibdev->ib_dev;
    +
    +	if (!rdma_is_port_valid(&ibdev->ib_dev, ibev.element.port_num)) {
    +		mlx5_ib_warn(ibdev, "warning: event on port %d\n",  ibev.element.port_num);
    +		goto out;
    +	}
    +
    +	if (ibdev->ib_active)
    +		ib_dispatch_event(&ibev);
    +
    +	ibdev->ib_active = false;
    +out:
    +	kfree(work);
    +}
    +
    +static int mlx5_ib_sys_error_event(struct notifier_block *nb,
    +				   unsigned long event, void *param)
    +{
    +	struct mlx5_ib_event_work *work;
    +
    +	if (event != MLX5_DEV_EVENT_SYS_ERROR)
    +		return NOTIFY_DONE;
    +
    +	work = kmalloc(sizeof(*work), GFP_ATOMIC);
    +	if (!work)
    +		return NOTIFY_DONE;
    +
    +	INIT_WORK(&work->work, mlx5_ib_handle_sys_error_event);
    +	work->dev = container_of(nb, struct mlx5_ib_dev, sys_error_events);
    +	work->is_slave = false;
    +	work->param = param;
    +	work->event = event;
    +
    +	queue_work(mlx5_ib_event_wq, &work->work);
    +
    +	return NOTIFY_OK;
    +}
    +
    +static int mlx5_ib_stage_sys_error_notifier_init(struct mlx5_ib_dev *dev)
    +{
    +	dev->sys_error_events.notifier_call = mlx5_ib_sys_error_event;
    +	mlx5_notifier_register(dev->mdev, &dev->sys_error_events);
    +	return 0;
    +}
    +
    +static void mlx5_ib_stage_sys_error_notifier_cleanup(struct mlx5_ib_dev *dev)
    +{
    +	mlx5_notifier_unregister(dev->mdev, &dev->sys_error_events);
    +}
    +
     static int mlx5_ib_get_plane_num(struct mlx5_core_dev *mdev, u8 *num_plane)
     {
     	struct mlx5_hca_vport_context vport_ctx;
    @@ -4943,6 +4994,9 @@ static const struct mlx5_ib_profile pf_profile = {
     	STAGE_CREATE(MLX5_IB_STAGE_WHITELIST_UID,
     		     mlx5_ib_devx_init,
     		     mlx5_ib_devx_cleanup),
    +	STAGE_CREATE(MLX5_IB_STAGE_SYS_ERROR_NOTIFIER,
    +		     mlx5_ib_stage_sys_error_notifier_init,
    +		     mlx5_ib_stage_sys_error_notifier_cleanup),
     	STAGE_CREATE(MLX5_IB_STAGE_IB_REG,
     		     mlx5_ib_stage_ib_reg_init,
     		     mlx5_ib_stage_ib_reg_cleanup),
    @@ -5000,6 +5054,9 @@ const struct mlx5_ib_profile raw_eth_profile = {
     	STAGE_CREATE(MLX5_IB_STAGE_WHITELIST_UID,
     		     mlx5_ib_devx_init,
     		     mlx5_ib_devx_cleanup),
    +	STAGE_CREATE(MLX5_IB_STAGE_SYS_ERROR_NOTIFIER,
    +		     mlx5_ib_stage_sys_error_notifier_init,
    +		     mlx5_ib_stage_sys_error_notifier_cleanup),
     	STAGE_CREATE(MLX5_IB_STAGE_IB_REG,
     		     mlx5_ib_stage_ib_reg_init,
     		     mlx5_ib_stage_ib_reg_cleanup),
    
  • drivers/infiniband/hw/mlx5/mlx5_ib.h+2 1 modified
    diff --git a/drivers/infiniband/hw/mlx5/mlx5_ib.h b/drivers/infiniband/hw/mlx5/mlx5_ib.h
    index cc6b3b6c713c03..4f4114d9513000 100644
    --- a/drivers/infiniband/hw/mlx5/mlx5_ib.h
    +++ b/drivers/infiniband/hw/mlx5/mlx5_ib.h
    @@ -1007,6 +1007,7 @@ enum mlx5_ib_stages {
     	MLX5_IB_STAGE_BFREG,
     	MLX5_IB_STAGE_PRE_IB_REG_UMR,
     	MLX5_IB_STAGE_WHITELIST_UID,
    +	MLX5_IB_STAGE_SYS_ERROR_NOTIFIER,
     	MLX5_IB_STAGE_IB_REG,
     	MLX5_IB_STAGE_DEVICE_NOTIFIER,
     	MLX5_IB_STAGE_POST_IB_REG_UMR,
    @@ -1165,6 +1166,7 @@ struct mlx5_ib_dev {
     	/* protect accessing data_direct_dev */
     	struct mutex			data_direct_lock;
     	struct notifier_block		mdev_events;
    +	struct notifier_block		sys_error_events;
     	struct notifier_block           lag_events;
     	int				num_ports;
     	/* serialize update of capability mask
    -- 
    cgit 1.3-korg
    
    
    
6d838873da9c

RDMA/mlx5: Fix UMR hang in LAG error state unload

2 files changed · +68 10
  • drivers/infiniband/hw/mlx5/main.c+66 9 modified
    diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c
    index 8f69c8c1ba54d6..b6096f9126858a 100644
    --- a/drivers/infiniband/hw/mlx5/main.c
    +++ b/drivers/infiniband/hw/mlx5/main.c
    @@ -2874,7 +2874,6 @@ static void mlx5_ib_handle_event(struct work_struct *_work)
     		container_of(_work, struct mlx5_ib_event_work, work);
     	struct mlx5_ib_dev *ibdev;
     	struct ib_event ibev;
    -	bool fatal = false;
     
     	if (work->is_slave) {
     		ibdev = mlx5_ib_get_ibdev_from_mpi(work->mpi);
    @@ -2885,12 +2884,6 @@ static void mlx5_ib_handle_event(struct work_struct *_work)
     	}
     
     	switch (work->event) {
    -	case MLX5_DEV_EVENT_SYS_ERROR:
    -		ibev.event = IB_EVENT_DEVICE_FATAL;
    -		mlx5_ib_handle_internal_error(ibdev);
    -		ibev.element.port_num  = (u8)(unsigned long)work->param;
    -		fatal = true;
    -		break;
     	case MLX5_EVENT_TYPE_PORT_CHANGE:
     		if (handle_port_change(ibdev, work->param, &ibev))
     			goto out;
    @@ -2912,8 +2905,6 @@ static void mlx5_ib_handle_event(struct work_struct *_work)
     	if (ibdev->ib_active)
     		ib_dispatch_event(&ibev);
     
    -	if (fatal)
    -		ibdev->ib_active = false;
     out:
     	kfree(work);
     }
    @@ -2957,6 +2948,66 @@ static int mlx5_ib_event_slave_port(struct notifier_block *nb,
     	return NOTIFY_OK;
     }
     
    +static void mlx5_ib_handle_sys_error_event(struct work_struct *_work)
    +{
    +	struct mlx5_ib_event_work *work =
    +		container_of(_work, struct mlx5_ib_event_work, work);
    +	struct mlx5_ib_dev *ibdev = work->dev;
    +	struct ib_event ibev;
    +
    +	ibev.event = IB_EVENT_DEVICE_FATAL;
    +	mlx5_ib_handle_internal_error(ibdev);
    +	ibev.element.port_num = (u8)(unsigned long)work->param;
    +	ibev.device = &ibdev->ib_dev;
    +
    +	if (!rdma_is_port_valid(&ibdev->ib_dev, ibev.element.port_num)) {
    +		mlx5_ib_warn(ibdev, "warning: event on port %d\n",  ibev.element.port_num);
    +		goto out;
    +	}
    +
    +	if (ibdev->ib_active)
    +		ib_dispatch_event(&ibev);
    +
    +	ibdev->ib_active = false;
    +out:
    +	kfree(work);
    +}
    +
    +static int mlx5_ib_sys_error_event(struct notifier_block *nb,
    +				   unsigned long event, void *param)
    +{
    +	struct mlx5_ib_event_work *work;
    +
    +	if (event != MLX5_DEV_EVENT_SYS_ERROR)
    +		return NOTIFY_DONE;
    +
    +	work = kmalloc(sizeof(*work), GFP_ATOMIC);
    +	if (!work)
    +		return NOTIFY_DONE;
    +
    +	INIT_WORK(&work->work, mlx5_ib_handle_sys_error_event);
    +	work->dev = container_of(nb, struct mlx5_ib_dev, sys_error_events);
    +	work->is_slave = false;
    +	work->param = param;
    +	work->event = event;
    +
    +	queue_work(mlx5_ib_event_wq, &work->work);
    +
    +	return NOTIFY_OK;
    +}
    +
    +static int mlx5_ib_stage_sys_error_notifier_init(struct mlx5_ib_dev *dev)
    +{
    +	dev->sys_error_events.notifier_call = mlx5_ib_sys_error_event;
    +	mlx5_notifier_register(dev->mdev, &dev->sys_error_events);
    +	return 0;
    +}
    +
    +static void mlx5_ib_stage_sys_error_notifier_cleanup(struct mlx5_ib_dev *dev)
    +{
    +	mlx5_notifier_unregister(dev->mdev, &dev->sys_error_events);
    +}
    +
     static int mlx5_ib_get_plane_num(struct mlx5_core_dev *mdev, u8 *num_plane)
     {
     	struct mlx5_hca_vport_context vport_ctx;
    @@ -4807,6 +4858,9 @@ static const struct mlx5_ib_profile pf_profile = {
     	STAGE_CREATE(MLX5_IB_STAGE_WHITELIST_UID,
     		     mlx5_ib_devx_init,
     		     mlx5_ib_devx_cleanup),
    +	STAGE_CREATE(MLX5_IB_STAGE_SYS_ERROR_NOTIFIER,
    +		     mlx5_ib_stage_sys_error_notifier_init,
    +		     mlx5_ib_stage_sys_error_notifier_cleanup),
     	STAGE_CREATE(MLX5_IB_STAGE_IB_REG,
     		     mlx5_ib_stage_ib_reg_init,
     		     mlx5_ib_stage_ib_reg_cleanup),
    @@ -4864,6 +4918,9 @@ const struct mlx5_ib_profile raw_eth_profile = {
     	STAGE_CREATE(MLX5_IB_STAGE_WHITELIST_UID,
     		     mlx5_ib_devx_init,
     		     mlx5_ib_devx_cleanup),
    +	STAGE_CREATE(MLX5_IB_STAGE_SYS_ERROR_NOTIFIER,
    +		     mlx5_ib_stage_sys_error_notifier_init,
    +		     mlx5_ib_stage_sys_error_notifier_cleanup),
     	STAGE_CREATE(MLX5_IB_STAGE_IB_REG,
     		     mlx5_ib_stage_ib_reg_init,
     		     mlx5_ib_stage_ib_reg_cleanup),
    
  • drivers/infiniband/hw/mlx5/mlx5_ib.h+2 1 modified
    diff --git a/drivers/infiniband/hw/mlx5/mlx5_ib.h b/drivers/infiniband/hw/mlx5/mlx5_ib.h
    index 09d82d5f95e354..fbccb0362590bb 100644
    --- a/drivers/infiniband/hw/mlx5/mlx5_ib.h
    +++ b/drivers/infiniband/hw/mlx5/mlx5_ib.h
    @@ -1007,6 +1007,7 @@ enum mlx5_ib_stages {
     	MLX5_IB_STAGE_BFREG,
     	MLX5_IB_STAGE_PRE_IB_REG_UMR,
     	MLX5_IB_STAGE_WHITELIST_UID,
    +	MLX5_IB_STAGE_SYS_ERROR_NOTIFIER,
     	MLX5_IB_STAGE_IB_REG,
     	MLX5_IB_STAGE_DEVICE_NOTIFIER,
     	MLX5_IB_STAGE_POST_IB_REG_UMR,
    @@ -1165,6 +1166,7 @@ struct mlx5_ib_dev {
     	/* protect accessing data_direct_dev */
     	struct mutex			data_direct_lock;
     	struct notifier_block		mdev_events;
    +	struct notifier_block		sys_error_events;
     	struct notifier_block           lag_events;
     	int				num_ports;
     	/* serialize update of capability mask
    -- 
    cgit 1.3-korg
    
    
    
c8fb5c965ac7

RDMA/mlx5: Fix UMR hang in LAG error state unload

2 files changed · +68 10
  • drivers/infiniband/hw/mlx5/main.c+66 9 modified
    diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c
    index f3e58797705d72..10bda03eb3388b 100644
    --- a/drivers/infiniband/hw/mlx5/main.c
    +++ b/drivers/infiniband/hw/mlx5/main.c
    @@ -2826,7 +2826,6 @@ static void mlx5_ib_handle_event(struct work_struct *_work)
     		container_of(_work, struct mlx5_ib_event_work, work);
     	struct mlx5_ib_dev *ibdev;
     	struct ib_event ibev;
    -	bool fatal = false;
     
     	if (work->is_slave) {
     		ibdev = mlx5_ib_get_ibdev_from_mpi(work->mpi);
    @@ -2837,12 +2836,6 @@ static void mlx5_ib_handle_event(struct work_struct *_work)
     	}
     
     	switch (work->event) {
    -	case MLX5_DEV_EVENT_SYS_ERROR:
    -		ibev.event = IB_EVENT_DEVICE_FATAL;
    -		mlx5_ib_handle_internal_error(ibdev);
    -		ibev.element.port_num  = (u8)(unsigned long)work->param;
    -		fatal = true;
    -		break;
     	case MLX5_EVENT_TYPE_PORT_CHANGE:
     		if (handle_port_change(ibdev, work->param, &ibev))
     			goto out;
    @@ -2864,8 +2857,6 @@ static void mlx5_ib_handle_event(struct work_struct *_work)
     	if (ibdev->ib_active)
     		ib_dispatch_event(&ibev);
     
    -	if (fatal)
    -		ibdev->ib_active = false;
     out:
     	kfree(work);
     }
    @@ -2909,6 +2900,66 @@ static int mlx5_ib_event_slave_port(struct notifier_block *nb,
     	return NOTIFY_OK;
     }
     
    +static void mlx5_ib_handle_sys_error_event(struct work_struct *_work)
    +{
    +	struct mlx5_ib_event_work *work =
    +		container_of(_work, struct mlx5_ib_event_work, work);
    +	struct mlx5_ib_dev *ibdev = work->dev;
    +	struct ib_event ibev;
    +
    +	ibev.event = IB_EVENT_DEVICE_FATAL;
    +	mlx5_ib_handle_internal_error(ibdev);
    +	ibev.element.port_num = (u8)(unsigned long)work->param;
    +	ibev.device = &ibdev->ib_dev;
    +
    +	if (!rdma_is_port_valid(&ibdev->ib_dev, ibev.element.port_num)) {
    +		mlx5_ib_warn(ibdev, "warning: event on port %d\n",  ibev.element.port_num);
    +		goto out;
    +	}
    +
    +	if (ibdev->ib_active)
    +		ib_dispatch_event(&ibev);
    +
    +	ibdev->ib_active = false;
    +out:
    +	kfree(work);
    +}
    +
    +static int mlx5_ib_sys_error_event(struct notifier_block *nb,
    +				   unsigned long event, void *param)
    +{
    +	struct mlx5_ib_event_work *work;
    +
    +	if (event != MLX5_DEV_EVENT_SYS_ERROR)
    +		return NOTIFY_DONE;
    +
    +	work = kmalloc(sizeof(*work), GFP_ATOMIC);
    +	if (!work)
    +		return NOTIFY_DONE;
    +
    +	INIT_WORK(&work->work, mlx5_ib_handle_sys_error_event);
    +	work->dev = container_of(nb, struct mlx5_ib_dev, sys_error_events);
    +	work->is_slave = false;
    +	work->param = param;
    +	work->event = event;
    +
    +	queue_work(mlx5_ib_event_wq, &work->work);
    +
    +	return NOTIFY_OK;
    +}
    +
    +static int mlx5_ib_stage_sys_error_notifier_init(struct mlx5_ib_dev *dev)
    +{
    +	dev->sys_error_events.notifier_call = mlx5_ib_sys_error_event;
    +	mlx5_notifier_register(dev->mdev, &dev->sys_error_events);
    +	return 0;
    +}
    +
    +static void mlx5_ib_stage_sys_error_notifier_cleanup(struct mlx5_ib_dev *dev)
    +{
    +	mlx5_notifier_unregister(dev->mdev, &dev->sys_error_events);
    +}
    +
     static int mlx5_ib_get_plane_num(struct mlx5_core_dev *mdev, u8 *num_plane)
     {
     	struct mlx5_hca_vport_context vport_ctx;
    @@ -4682,6 +4733,9 @@ static const struct mlx5_ib_profile pf_profile = {
     	STAGE_CREATE(MLX5_IB_STAGE_WHITELIST_UID,
     		     mlx5_ib_devx_init,
     		     mlx5_ib_devx_cleanup),
    +	STAGE_CREATE(MLX5_IB_STAGE_SYS_ERROR_NOTIFIER,
    +		     mlx5_ib_stage_sys_error_notifier_init,
    +		     mlx5_ib_stage_sys_error_notifier_cleanup),
     	STAGE_CREATE(MLX5_IB_STAGE_IB_REG,
     		     mlx5_ib_stage_ib_reg_init,
     		     mlx5_ib_stage_ib_reg_cleanup),
    @@ -4742,6 +4796,9 @@ const struct mlx5_ib_profile raw_eth_profile = {
     	STAGE_CREATE(MLX5_IB_STAGE_WHITELIST_UID,
     		     mlx5_ib_devx_init,
     		     mlx5_ib_devx_cleanup),
    +	STAGE_CREATE(MLX5_IB_STAGE_SYS_ERROR_NOTIFIER,
    +		     mlx5_ib_stage_sys_error_notifier_init,
    +		     mlx5_ib_stage_sys_error_notifier_cleanup),
     	STAGE_CREATE(MLX5_IB_STAGE_IB_REG,
     		     mlx5_ib_stage_ib_reg_init,
     		     mlx5_ib_stage_ib_reg_cleanup),
    
  • drivers/infiniband/hw/mlx5/mlx5_ib.h+2 1 modified
    diff --git a/drivers/infiniband/hw/mlx5/mlx5_ib.h b/drivers/infiniband/hw/mlx5/mlx5_ib.h
    index f49cb588a856d5..3135519f1cfdfd 100644
    --- a/drivers/infiniband/hw/mlx5/mlx5_ib.h
    +++ b/drivers/infiniband/hw/mlx5/mlx5_ib.h
    @@ -979,6 +979,7 @@ enum mlx5_ib_stages {
     	MLX5_IB_STAGE_BFREG,
     	MLX5_IB_STAGE_PRE_IB_REG_UMR,
     	MLX5_IB_STAGE_WHITELIST_UID,
    +	MLX5_IB_STAGE_SYS_ERROR_NOTIFIER,
     	MLX5_IB_STAGE_IB_REG,
     	MLX5_IB_STAGE_DEVICE_NOTIFIER,
     	MLX5_IB_STAGE_POST_IB_REG_UMR,
    @@ -1137,6 +1138,7 @@ struct mlx5_ib_dev {
     	/* protect accessing data_direct_dev */
     	struct mutex			data_direct_lock;
     	struct notifier_block		mdev_events;
    +	struct notifier_block		sys_error_events;
     	struct notifier_block           lag_events;
     	int				num_ports;
     	/* serialize update of capability mask
    -- 
    cgit 1.3-korg
    
    
    
ebc2164a4cd4

RDMA/mlx5: Fix UMR hang in LAG error state unload

2 files changed · +68 10
  • drivers/infiniband/hw/mlx5/main.c+66 9 modified
    diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c
    index e81080622283c1..e83a5f12e6bcd6 100644
    --- a/drivers/infiniband/hw/mlx5/main.c
    +++ b/drivers/infiniband/hw/mlx5/main.c
    @@ -3009,7 +3009,6 @@ static void mlx5_ib_handle_event(struct work_struct *_work)
     		container_of(_work, struct mlx5_ib_event_work, work);
     	struct mlx5_ib_dev *ibdev;
     	struct ib_event ibev;
    -	bool fatal = false;
     
     	if (work->is_slave) {
     		ibdev = mlx5_ib_get_ibdev_from_mpi(work->mpi);
    @@ -3020,12 +3019,6 @@ static void mlx5_ib_handle_event(struct work_struct *_work)
     	}
     
     	switch (work->event) {
    -	case MLX5_DEV_EVENT_SYS_ERROR:
    -		ibev.event = IB_EVENT_DEVICE_FATAL;
    -		mlx5_ib_handle_internal_error(ibdev);
    -		ibev.element.port_num  = (u8)(unsigned long)work->param;
    -		fatal = true;
    -		break;
     	case MLX5_EVENT_TYPE_PORT_CHANGE:
     		if (handle_port_change(ibdev, work->param, &ibev))
     			goto out;
    @@ -3047,8 +3040,6 @@ static void mlx5_ib_handle_event(struct work_struct *_work)
     	if (ibdev->ib_active)
     		ib_dispatch_event(&ibev);
     
    -	if (fatal)
    -		ibdev->ib_active = false;
     out:
     	kfree(work);
     }
    @@ -3092,6 +3083,66 @@ static int mlx5_ib_event_slave_port(struct notifier_block *nb,
     	return NOTIFY_OK;
     }
     
    +static void mlx5_ib_handle_sys_error_event(struct work_struct *_work)
    +{
    +	struct mlx5_ib_event_work *work =
    +		container_of(_work, struct mlx5_ib_event_work, work);
    +	struct mlx5_ib_dev *ibdev = work->dev;
    +	struct ib_event ibev;
    +
    +	ibev.event = IB_EVENT_DEVICE_FATAL;
    +	mlx5_ib_handle_internal_error(ibdev);
    +	ibev.element.port_num = (u8)(unsigned long)work->param;
    +	ibev.device = &ibdev->ib_dev;
    +
    +	if (!rdma_is_port_valid(&ibdev->ib_dev, ibev.element.port_num)) {
    +		mlx5_ib_warn(ibdev, "warning: event on port %d\n",  ibev.element.port_num);
    +		goto out;
    +	}
    +
    +	if (ibdev->ib_active)
    +		ib_dispatch_event(&ibev);
    +
    +	ibdev->ib_active = false;
    +out:
    +	kfree(work);
    +}
    +
    +static int mlx5_ib_sys_error_event(struct notifier_block *nb,
    +				   unsigned long event, void *param)
    +{
    +	struct mlx5_ib_event_work *work;
    +
    +	if (event != MLX5_DEV_EVENT_SYS_ERROR)
    +		return NOTIFY_DONE;
    +
    +	work = kmalloc(sizeof(*work), GFP_ATOMIC);
    +	if (!work)
    +		return NOTIFY_DONE;
    +
    +	INIT_WORK(&work->work, mlx5_ib_handle_sys_error_event);
    +	work->dev = container_of(nb, struct mlx5_ib_dev, sys_error_events);
    +	work->is_slave = false;
    +	work->param = param;
    +	work->event = event;
    +
    +	queue_work(mlx5_ib_event_wq, &work->work);
    +
    +	return NOTIFY_OK;
    +}
    +
    +static int mlx5_ib_stage_sys_error_notifier_init(struct mlx5_ib_dev *dev)
    +{
    +	dev->sys_error_events.notifier_call = mlx5_ib_sys_error_event;
    +	mlx5_notifier_register(dev->mdev, &dev->sys_error_events);
    +	return 0;
    +}
    +
    +static void mlx5_ib_stage_sys_error_notifier_cleanup(struct mlx5_ib_dev *dev)
    +{
    +	mlx5_notifier_unregister(dev->mdev, &dev->sys_error_events);
    +}
    +
     static int mlx5_ib_get_plane_num(struct mlx5_core_dev *mdev, u8 *num_plane)
     {
     	struct mlx5_hca_vport_context vport_ctx;
    @@ -4943,6 +4994,9 @@ static const struct mlx5_ib_profile pf_profile = {
     	STAGE_CREATE(MLX5_IB_STAGE_WHITELIST_UID,
     		     mlx5_ib_devx_init,
     		     mlx5_ib_devx_cleanup),
    +	STAGE_CREATE(MLX5_IB_STAGE_SYS_ERROR_NOTIFIER,
    +		     mlx5_ib_stage_sys_error_notifier_init,
    +		     mlx5_ib_stage_sys_error_notifier_cleanup),
     	STAGE_CREATE(MLX5_IB_STAGE_IB_REG,
     		     mlx5_ib_stage_ib_reg_init,
     		     mlx5_ib_stage_ib_reg_cleanup),
    @@ -5000,6 +5054,9 @@ const struct mlx5_ib_profile raw_eth_profile = {
     	STAGE_CREATE(MLX5_IB_STAGE_WHITELIST_UID,
     		     mlx5_ib_devx_init,
     		     mlx5_ib_devx_cleanup),
    +	STAGE_CREATE(MLX5_IB_STAGE_SYS_ERROR_NOTIFIER,
    +		     mlx5_ib_stage_sys_error_notifier_init,
    +		     mlx5_ib_stage_sys_error_notifier_cleanup),
     	STAGE_CREATE(MLX5_IB_STAGE_IB_REG,
     		     mlx5_ib_stage_ib_reg_init,
     		     mlx5_ib_stage_ib_reg_cleanup),
    
  • drivers/infiniband/hw/mlx5/mlx5_ib.h+2 1 modified
    diff --git a/drivers/infiniband/hw/mlx5/mlx5_ib.h b/drivers/infiniband/hw/mlx5/mlx5_ib.h
    index cc6b3b6c713c03..4f4114d9513000 100644
    --- a/drivers/infiniband/hw/mlx5/mlx5_ib.h
    +++ b/drivers/infiniband/hw/mlx5/mlx5_ib.h
    @@ -1007,6 +1007,7 @@ enum mlx5_ib_stages {
     	MLX5_IB_STAGE_BFREG,
     	MLX5_IB_STAGE_PRE_IB_REG_UMR,
     	MLX5_IB_STAGE_WHITELIST_UID,
    +	MLX5_IB_STAGE_SYS_ERROR_NOTIFIER,
     	MLX5_IB_STAGE_IB_REG,
     	MLX5_IB_STAGE_DEVICE_NOTIFIER,
     	MLX5_IB_STAGE_POST_IB_REG_UMR,
    @@ -1165,6 +1166,7 @@ struct mlx5_ib_dev {
     	/* protect accessing data_direct_dev */
     	struct mutex			data_direct_lock;
     	struct notifier_block		mdev_events;
    +	struct notifier_block		sys_error_events;
     	struct notifier_block           lag_events;
     	int				num_ports;
     	/* serialize update of capability mask
    -- 
    cgit 1.3-korg
    
    
    
613f5d4139b6

RDMA/mlx5: Fix UMR hang in LAG error state unload

2 files changed · +68 10
  • drivers/infiniband/hw/mlx5/main.c+66 9 modified
    diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c
    index 8d515d266125e6..3485a9a3d75e0d 100644
    --- a/drivers/infiniband/hw/mlx5/main.c
    +++ b/drivers/infiniband/hw/mlx5/main.c
    @@ -2878,7 +2878,6 @@ static void mlx5_ib_handle_event(struct work_struct *_work)
     		container_of(_work, struct mlx5_ib_event_work, work);
     	struct mlx5_ib_dev *ibdev;
     	struct ib_event ibev;
    -	bool fatal = false;
     
     	if (work->is_slave) {
     		ibdev = mlx5_ib_get_ibdev_from_mpi(work->mpi);
    @@ -2889,12 +2888,6 @@ static void mlx5_ib_handle_event(struct work_struct *_work)
     	}
     
     	switch (work->event) {
    -	case MLX5_DEV_EVENT_SYS_ERROR:
    -		ibev.event = IB_EVENT_DEVICE_FATAL;
    -		mlx5_ib_handle_internal_error(ibdev);
    -		ibev.element.port_num  = (u8)(unsigned long)work->param;
    -		fatal = true;
    -		break;
     	case MLX5_EVENT_TYPE_PORT_CHANGE:
     		if (handle_port_change(ibdev, work->param, &ibev))
     			goto out;
    @@ -2916,8 +2909,6 @@ static void mlx5_ib_handle_event(struct work_struct *_work)
     	if (ibdev->ib_active)
     		ib_dispatch_event(&ibev);
     
    -	if (fatal)
    -		ibdev->ib_active = false;
     out:
     	kfree(work);
     }
    @@ -2961,6 +2952,66 @@ static int mlx5_ib_event_slave_port(struct notifier_block *nb,
     	return NOTIFY_OK;
     }
     
    +static void mlx5_ib_handle_sys_error_event(struct work_struct *_work)
    +{
    +	struct mlx5_ib_event_work *work =
    +		container_of(_work, struct mlx5_ib_event_work, work);
    +	struct mlx5_ib_dev *ibdev = work->dev;
    +	struct ib_event ibev;
    +
    +	ibev.event = IB_EVENT_DEVICE_FATAL;
    +	mlx5_ib_handle_internal_error(ibdev);
    +	ibev.element.port_num = (u8)(unsigned long)work->param;
    +	ibev.device = &ibdev->ib_dev;
    +
    +	if (!rdma_is_port_valid(&ibdev->ib_dev, ibev.element.port_num)) {
    +		mlx5_ib_warn(ibdev, "warning: event on port %d\n",  ibev.element.port_num);
    +		goto out;
    +	}
    +
    +	if (ibdev->ib_active)
    +		ib_dispatch_event(&ibev);
    +
    +	ibdev->ib_active = false;
    +out:
    +	kfree(work);
    +}
    +
    +static int mlx5_ib_sys_error_event(struct notifier_block *nb,
    +				   unsigned long event, void *param)
    +{
    +	struct mlx5_ib_event_work *work;
    +
    +	if (event != MLX5_DEV_EVENT_SYS_ERROR)
    +		return NOTIFY_DONE;
    +
    +	work = kmalloc(sizeof(*work), GFP_ATOMIC);
    +	if (!work)
    +		return NOTIFY_DONE;
    +
    +	INIT_WORK(&work->work, mlx5_ib_handle_sys_error_event);
    +	work->dev = container_of(nb, struct mlx5_ib_dev, sys_error_events);
    +	work->is_slave = false;
    +	work->param = param;
    +	work->event = event;
    +
    +	queue_work(mlx5_ib_event_wq, &work->work);
    +
    +	return NOTIFY_OK;
    +}
    +
    +static int mlx5_ib_stage_sys_error_notifier_init(struct mlx5_ib_dev *dev)
    +{
    +	dev->sys_error_events.notifier_call = mlx5_ib_sys_error_event;
    +	mlx5_notifier_register(dev->mdev, &dev->sys_error_events);
    +	return 0;
    +}
    +
    +static void mlx5_ib_stage_sys_error_notifier_cleanup(struct mlx5_ib_dev *dev)
    +{
    +	mlx5_notifier_unregister(dev->mdev, &dev->sys_error_events);
    +}
    +
     static int mlx5_ib_get_plane_num(struct mlx5_core_dev *mdev, u8 *num_plane)
     {
     	struct mlx5_hca_vport_context vport_ctx;
    @@ -4811,6 +4862,9 @@ static const struct mlx5_ib_profile pf_profile = {
     	STAGE_CREATE(MLX5_IB_STAGE_WHITELIST_UID,
     		     mlx5_ib_devx_init,
     		     mlx5_ib_devx_cleanup),
    +	STAGE_CREATE(MLX5_IB_STAGE_SYS_ERROR_NOTIFIER,
    +		     mlx5_ib_stage_sys_error_notifier_init,
    +		     mlx5_ib_stage_sys_error_notifier_cleanup),
     	STAGE_CREATE(MLX5_IB_STAGE_IB_REG,
     		     mlx5_ib_stage_ib_reg_init,
     		     mlx5_ib_stage_ib_reg_cleanup),
    @@ -4868,6 +4922,9 @@ const struct mlx5_ib_profile raw_eth_profile = {
     	STAGE_CREATE(MLX5_IB_STAGE_WHITELIST_UID,
     		     mlx5_ib_devx_init,
     		     mlx5_ib_devx_cleanup),
    +	STAGE_CREATE(MLX5_IB_STAGE_SYS_ERROR_NOTIFIER,
    +		     mlx5_ib_stage_sys_error_notifier_init,
    +		     mlx5_ib_stage_sys_error_notifier_cleanup),
     	STAGE_CREATE(MLX5_IB_STAGE_IB_REG,
     		     mlx5_ib_stage_ib_reg_init,
     		     mlx5_ib_stage_ib_reg_cleanup),
    
  • drivers/infiniband/hw/mlx5/mlx5_ib.h+2 1 modified
    diff --git a/drivers/infiniband/hw/mlx5/mlx5_ib.h b/drivers/infiniband/hw/mlx5/mlx5_ib.h
    index 09d82d5f95e354..fbccb0362590bb 100644
    --- a/drivers/infiniband/hw/mlx5/mlx5_ib.h
    +++ b/drivers/infiniband/hw/mlx5/mlx5_ib.h
    @@ -1007,6 +1007,7 @@ enum mlx5_ib_stages {
     	MLX5_IB_STAGE_BFREG,
     	MLX5_IB_STAGE_PRE_IB_REG_UMR,
     	MLX5_IB_STAGE_WHITELIST_UID,
    +	MLX5_IB_STAGE_SYS_ERROR_NOTIFIER,
     	MLX5_IB_STAGE_IB_REG,
     	MLX5_IB_STAGE_DEVICE_NOTIFIER,
     	MLX5_IB_STAGE_POST_IB_REG_UMR,
    @@ -1165,6 +1166,7 @@ struct mlx5_ib_dev {
     	/* protect accessing data_direct_dev */
     	struct mutex			data_direct_lock;
     	struct notifier_block		mdev_events;
    +	struct notifier_block		sys_error_events;
     	struct notifier_block           lag_events;
     	int				num_ports;
     	/* serialize update of capability mask
    -- 
    cgit 1.3-korg
    
    
    

Vulnerability mechanics

Root cause

"Missing sys_error event delivery to the bond device in LAG mode during firmware reset causes UMR completions to never arrive, leading to an indefinite hang on device unload."

Attack vector

During a firmware reset on a system configured with LAG (bonding), the slave device enters an error state but the master does not immediately see the sys_error event because the bond device is only registered on the master and the slave's sys_error events are not forwarded. UMR (User-Mode Memory Registration) work requests posted to the dead slave succeed on the wire but their completions never arrive, causing the driver to block forever in __mlx5_ib_dereg_mr while waiting for those completions during teardown. An attacker who can trigger a firmware reset (e.g. via physical access or a management interface) can cause a denial-of-service hang on the host.

Affected code

The bug is in drivers/infiniband/hw/mlx5/main.c, where the original mlx5_ib_handle_event() function handled MLX5_DEV_EVENT_SYS_ERROR inline but only for the device that received the event. In LAG mode the slave's sys_error never reached the master's bond device. The patch also modifies drivers/infiniband/hw/mlx5/mlx5_ib.h to add a new sys_error_events notifier_block field and a new MLX5_IB_STAGE_SYS_ERROR_NOTIFIER stage.

What the fix does

The patch removes the MLX5_DEV_EVENT_SYS_ERROR case from the old mlx5_ib_handle_event() function and creates a dedicated notifier chain: mlx5_ib_sys_error_event() is the notifier callback that queues work to mlx5_ib_handle_sys_error_event(), which calls mlx5_ib_handle_internal_error(), dispatches IB_EVENT_DEVICE_FATAL, and sets ibdev->ib_active = false. This notifier is registered via mlx5_ib_stage_sys_error_notifier_init() at a new stage (MLX5_IB_STAGE_SYS_ERROR_NOTIFIER) that runs before MLX5_IB_STAGE_IB_REG and is torn down after ib_unregister_device(), ensuring the bond device receives sys_error events from any port throughout the entire teardown sequence.

Preconditions

  • configSystem must be configured with LAG (bonding) mode on mlx5 devices
  • inputA firmware reset must be triggered on the LAG bond, causing the slave to enter an error state while the master has not yet entered error state

Generated on May 27, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

4

News mentions

0

No linked articles in our index yet.