[PATCH] drm/amdkfd: Fixed kfd_process cleanup on module exit.
Christian König
ckoenig.leichtzumerken at gmail.com
Wed Mar 8 16:20:36 UTC 2023
Am 08.03.23 um 17:17 schrieb Felix Kuehling:
> On 2023-03-08 04:07, Christian König wrote:
>> Am 07.03.23 um 16:28 schrieb Belanger, David:
>>> [AMD Official Use Only - General]
>>>
>>>
>>> The test case is a python program that will load the driver, do some
>>> operations, then unload the driver.
>>
>> What do you mean with unloading the driver? Removing the module? Or
>> destroying the device?
>>
>>> When the driver exists, there is still the python process space
>>> around holding on the address space.
>>> When the python process space exits, the mmu_notifier gets called
>>> but the driver has already been unloaded.
>>>
>>> The goal of the fix is to address case where there could be
>>> outstanding address space / worker threads for process
>>> cleanup that needs to be cleared/completed at exit time.
>>
>> Yeah and when the module is unloaded this is a completely futile effort.
>>
>> The general upstream approach is to take references on the struct
>> device and module and prevent unloading as long as those references
>> exists.
>
> That's not how it always works. In case of RCU callbacks, the
> documented strategy is to use rcu_barrier in the module exit function
> to ensure the grace period and all callbacks have completed
> (https://www.kernel.org/doc/html/latest/RCU/rcubarrier.html).
> mmu_notifier_synchronize is meant to do something similar for pending
> mmu_notifier_put work
> (https://elixir.bootlin.com/linux/v6.2.2/source/mm/mmu_notifier.c#L1116).
>
> But this implies that we need to call mmu_notifier_put for all the MMU
> notifiers registered by the module first. I think closing /dev/kfd
> drops the module reference count, but the MMU notifiers we register
> for process cleanup persist until the address space is destroyed. We
> need to trigger that cleanup for any processes that still exist in
> that state when the module is unloaded. Or we need to find a way to
> increment the module refcount for every process that registers a KFD
> cleanup MMU notifier.
The later is what I've meant. Cleaning up when the module unloads is
certainly possible as well, but harder to get right.
And I don't really see an use case that we should do the cleanup way.
Regards,
Christian.
>
> Regards,
> Felix
>
>
>>
>>
>> The device might be non-functional any more (because for example of
>> hot plug), but the driver should never be unloaded before the python
>> program exits.
>>
>> Regards,
>> Christian.
>>
>>>
>>> Regards,
>>> David B.
>>>
>>>> -----Original Message-----
>>>> From: Koenig, Christian <Christian.Koenig at amd.com>
>>>> Sent: Tuesday, March 7, 2023 2:05 AM
>>>> To: Belanger, David <David.Belanger at amd.com>; amd-
>>>> gfx at lists.freedesktop.org
>>>> Subject: Re: [PATCH] drm/amdkfd: Fixed kfd_process cleanup on module
>>>> exit.
>>>>
>>>> Am 06.03.23 um 22:58 schrieb David Belanger:
>>>>> Handle case when module is unloaded (kfd_exit) before a process space
>>>>> (mm_struct) is released.
>>>> Well that should never ever happen in the first place. It sounds
>>>> like we are
>>>> missing grabbing module references.
>>>>
>>>> Regards,
>>>> Christian.
>>>>
>>>>> Signed-off-by: David Belanger <david.belanger at amd.com>
>>>>> ---
>>>>> drivers/gpu/drm/amd/amdkfd/kfd_module.c | 4 ++
>>>>> drivers/gpu/drm/amd/amdkfd/kfd_process.c | 57
>>>> ++++++++++++++++++++++++
>>>>> 2 files changed, 61 insertions(+)
>>>>>
>>>>> diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_module.c
>>>>> b/drivers/gpu/drm/amd/amdkfd/kfd_module.c
>>>>> index 09b966dc3768..8ef4bd9e4f7d 100644
>>>>> --- a/drivers/gpu/drm/amd/amdkfd/kfd_module.c
>>>>> +++ b/drivers/gpu/drm/amd/amdkfd/kfd_module.c
>>>>> @@ -26,6 +26,9 @@
>>>>> #include "kfd_priv.h"
>>>>> #include "amdgpu_amdkfd.h"
>>>>>
>>>>> +void kfd_cleanup_processes(void);
>>>>> +
>>>>> +
>>>>> static int kfd_init(void)
>>>>> {
>>>>> int err;
>>>>> @@ -77,6 +80,7 @@ static int kfd_init(void)
>>>>>
>>>>> static void kfd_exit(void)
>>>>> {
>>>>> + kfd_cleanup_processes();
>>>>> kfd_debugfs_fini();
>>>>> kfd_process_destroy_wq();
>>>>> kfd_procfs_shutdown();
>>>>> diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_process.c
>>>>> b/drivers/gpu/drm/amd/amdkfd/kfd_process.c
>>>>> index ebabe92f7edb..b5b28a32639d 100644
>>>>> --- a/drivers/gpu/drm/amd/amdkfd/kfd_process.c
>>>>> +++ b/drivers/gpu/drm/amd/amdkfd/kfd_process.c
>>>>> @@ -1181,6 +1181,17 @@ static void
>>>>> kfd_process_notifier_release(struct
>>>> mmu_notifier *mn,
>>>>> return;
>>>>>
>>>>> mutex_lock(&kfd_processes_mutex);
>>>>> + /*
>>>>> + * Do early return if p is not in the table.
>>>>> + *
>>>>> + * This could potentially happen if this function is called
>>>>> concurrently
>>>>> + * by mmu_notifier and by kfd_cleanup_pocesses.
>>>>> + *
>>>>> + */
>>>>> + if (!hash_hashed(&p->kfd_processes)) {
>>>>> + mutex_unlock(&kfd_processes_mutex);
>>>>> + return;
>>>>> + }
>>>>> hash_del_rcu(&p->kfd_processes);
>>>>> mutex_unlock(&kfd_processes_mutex);
>>>>> synchronize_srcu(&kfd_processes_srcu);
>>>>> @@ -1200,6 +1211,52 @@ static const struct mmu_notifier_ops
>>>> kfd_process_mmu_notifier_ops = {
>>>>> .free_notifier = kfd_process_free_notifier,
>>>>> };
>>>>>
>>>>> +
>>>>> +void kfd_cleanup_processes(void)
>>>>> +{
>>>>> + struct kfd_process *p;
>>>>> + unsigned int temp;
>>>>> +
>>>>> + /*
>>>>> + * Iterate over remaining processes in table, calling
>>>>> notifier release
>>>>> + * to free up notifier and process resources.
>>>>> + *
>>>>> + * This code handles the case when driver is unloaded before all
>>>> mm_struct
>>>>> + * are released.
>>>>> + */
>>>>> + int idx = srcu_read_lock(&kfd_processes_srcu);
>>>>> +
>>>>> + hash_for_each_rcu(kfd_processes_table, temp, p, kfd_processes) {
>>>>> + if (p) {
>>>>> + /*
>>>>> + * Obtain a reference on p to avoid a late
>>>> mmu_notifier release
>>>>> + * call triggering freeing the process.
>>>>> + */
>>>>> +
>>>>> + kref_get(&p->ref);
>>>>> +
>>>>> + srcu_read_unlock(&kfd_processes_srcu, idx);
>>>>> +
>>>>> + kfd_process_notifier_release(&p->mmu_notifier, p-
>>>>> mm);
>>>>> +
>>>>> + kfd_unref_process(p);
>>>>> +
>>>>> + idx = srcu_read_lock(&kfd_processes_srcu);
>>>>> + }
>>>>> + }
>>>>> + srcu_read_unlock(&kfd_processes_srcu, idx);
>>>>> +
>>>>> + /*
>>>>> + * Must be called after all mmu_notifier_put are done and before
>>>>> + * kfd_process_wq is released.
>>>>> + *
>>>>> + * Ensures that all outstanding free_notifier gets called,
>>>>> triggering the
>>>> release
>>>>> + * of the process.
>>>>> + */
>>>>> + mmu_notifier_synchronize();
>>>>> +}
>>>>> +
>>>>> +
>>>>> static int kfd_process_init_cwsr_apu(struct kfd_process *p,
>>>>> struct file
>>>> *filep)
>>>>> {
>>>>> unsigned long offset;
>>
More information about the amd-gfx
mailing list