On 10/3/26 08:38, Olivier Kaloudoff wrote:
> 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.

That is clearly a really bad idea. Waiting for the termination *must* be 
unbounded.

The real question is why those KFD processes doesn't terminate?

Regards,
Christian.

> 
> 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,

Reply via email to