]> git.hungrycats.org Git - linux/commitdiff
drm/amd/display: Fix DM I2C teardown race
authorGeoffrey McRae <geoffrey.mcrae@amd.com>
Sun, 28 Jun 2026 16:04:13 +0000 (02:04 +1000)
committerAlex Deucher <alexander.deucher@amd.com>
Wed, 15 Jul 2026 13:15:37 +0000 (09:15 -0400)
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 <geoffrey.mcrae@amd.com>
Acked-by: Alex Deucher <alexander.deucher@amd.com>
Reviewed-by: Leo Li <sunpeng.li@amd.com>
Cc: Alex Deucher <alexander.deucher@amd.com>
Cc: Christian König <christian.koenig@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h

index 74bb5845b6be6c35db4218a90818feedeac3b64c..fb19a0781e592ca2d35c97507790f9e705967b61 100644 (file)
@@ -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);
index 91affbdb2d6cd0d618db593a942d63b88f27f700..a04c7f26672b9606cee7a2e1da44436bffb87b41 100644 (file)
@@ -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:
         *