On PCIe surprise removal (e.g. Thunderbolt eGPU link drop),
amdgpu_device_fini_hw() calls amdgpu_amdkfd_device_fini_sw() when
pci_dev_is_disconnected() is true, which frees adev->kfd.dev and sets
it to NULL. amdgpu_device_ip_fini_early() then runs a few calls later
in the same function and reaches kgd2kfd_teardown_processes(), which
unconditionally dereferences adev->kfd.dev->kfd_processes_count.

Observed on real hardware (MI50 eGPU behind TB3): NULL deref at +0x1b4
(kfd_processes_count) inside kgd2kfd_teardown_processes, called from
the pciehp removal thread; panic via panic_on_oops (pstore capture).

Additionally, the wait-for-termination loop is unbounded: on a
physically gone device, processes can be unable to make forward
progress, wedging the pciehp removal thread in cond_resched() forever
and cascading into hung-task/softlockup panics.

Guard the wait on adev->kfd.dev and bound it to 30s with a dev_warn so
removal can complete and the kernel survives.

Fixes: d12d05c4bc4c ("drm/amdkfd: Check if there are kfd porcesses using adev 
by kfd_processes_count")
Cc: Xiaogang Chen <[email protected]>
Signed-off-by: Olivier Kaloudoff <[email protected]>
---
 drivers/gpu/drm/amd/amdkfd/kfd_device.c | 22 ++++++++++++++++++++--
 1 file changed, 20 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device.c 
b/drivers/gpu/drm/amd/amdkfd/kfd_device.c
index 586e640..9326447 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_device.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_device.c
@@ -1780,6 +1780,7 @@ bool kgd2kfd_vmfault_fast_path(struct amdgpu_device 
*adev, struct amdgpu_iv_entr
 void kgd2kfd_teardown_processes(struct amdgpu_device *adev)
 {
        struct hlist_node *p_temp;
+       unsigned long timeout;
        struct kfd_process *p;
        struct kfd_node *dev;
        unsigned int temp;
@@ -1801,9 +1802,26 @@ void kgd2kfd_teardown_processes(struct amdgpu_device 
*adev)
 
        mutex_unlock(&kfd_processes_mutex);
 
-       /* wait all kfd processes use adev terminate */
-       while (!!atomic_read(&adev->kfd.dev->kfd_processes_count))
+       /* On surprise removal amdgpu_amdkfd_device_fini_sw() has already
+        * freed kfd.dev before we get here.
+        */
+       if (!adev->kfd.dev)
+               return;
+
+       /* Wait for all kfd processes using adev to terminate. Bound the
+        * wait: on a physically gone device processes may never finish,
+        * and spinning here forever wedges the pciehp removal thread.
+        */
+       timeout = jiffies + msecs_to_jiffies(30000);
+       while (atomic_read(&adev->kfd.dev->kfd_processes_count)) {
+               if (time_after(jiffies, timeout)) {
+                       dev_warn(adev->dev,
+                                "kfd: %d processes still attached to removed 
device\n",
+                                
atomic_read(&adev->kfd.dev->kfd_processes_count));
+                       break;
+               }
                cond_resched();
+       }
 }
 
 int kgd2kfd_reset_mes_queue(struct kfd_dev *kfd, uint32_t node_id,
-- 
2.54.0 (Apple Git-157)

Reply via email to