From e4ae30a12aa95942814957d8bc1ce7366a7107d7 Mon Sep 17 00:00:00 2001 From: Geoffrey McRae Date: Mon, 29 Jun 2026 02:04:13 +1000 Subject: [PATCH] drm/amd/display: Fix DM I2C teardown race MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DM I2C adapters can remain visible to userspace while DM teardown is already in progress. A concurrent i2c-dev transfer may then enter amdgpu_dm_i2c_xfer() after the backing DM state has been torn down, leading to a NULL pointer dereference. Create a devres group around the DM I2C adapter lifetime and release it at the start of dm_hw_fini(), before HPD, IRQ, and DM state are torn down. This removes the I2C adapters first and waits for in-flight users to drain before the structures used by amdgpu_dm_i2c_xfer() disappear. This fixes a teardown ordering race seen during device removal: BUG: kernel NULL pointer dereference RIP: amdgpu_dm_i2c_xfer+0x122/0x1c0 [amdgpu] Call Trace: __i2c_transfer i2c_transfer i2cdev_ioctl_rdwr Fixes: 5b3eca05cfb0 ("drm/amd/display: Use devm_i2c_add_adapter to simplify i2c cleanup logic") Signed-off-by: Geoffrey McRae Acked-by: Alex Deucher Reviewed-by: Leo Li Cc: Alex Deucher Cc: Christian König Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 14 +++++++++++++- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h | 7 +++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 74bb5845b6be..fb19a0781e59 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -1496,17 +1496,26 @@ static int dm_hw_init(struct amdgpu_ip_block *ip_block) struct amdgpu_device *adev = ip_block->adev; int r; + adev->dm.i2c_devres_group = devres_open_group(adev->dev, NULL, GFP_KERNEL); + if (!adev->dm.i2c_devres_group) + return -ENOMEM; + /* Create DAL display manager */ r = amdgpu_dm_init(adev); if (r) - return r; + goto err_release_i2c; amdgpu_dm_hpd_init(adev); r = dm_oem_i2c_hw_init(adev); if (r) drm_info(adev_to_drm(adev), "Failed to add OEM i2c bus\n"); + devres_close_group(adev->dev, adev->dm.i2c_devres_group); return 0; + +err_release_i2c: + devres_release_group(adev->dev, adev->dm.i2c_devres_group); + return r; } /** @@ -1521,6 +1530,9 @@ static int dm_hw_fini(struct amdgpu_ip_block *ip_block) { struct amdgpu_device *adev = ip_block->adev; + if (adev->dm.i2c_devres_group) + devres_release_group(adev->dev, adev->dm.i2c_devres_group); + amdgpu_dm_hpd_fini(adev); amdgpu_dm_irq_fini(adev); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h index 91affbdb2d6c..a04c7f26672b 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h @@ -688,6 +688,13 @@ struct amdgpu_display_manager { */ void *bb_from_dmub; + /** + * @i2c_devres_group: + * + * Devres group for DM i2c adapter lifetime management. + */ + void *i2c_devres_group; + /** * @oem_i2c: *