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 <[email protected]>
Cc: Alex Deucher <[email protected]>
Cc: Christian König <[email protected]>
---
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 b97ceabe6173..5613dc9903fe 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 909ee71d6d59..13a18e1ed576 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:
*
--
2.43.0