mirror of
https://github.com/torvalds/linux.git
synced 2026-07-28 10:09:10 +02:00
Replace the stack-allocated amdgpu_lockdep mutex with a heap allocation via kmalloc to fix a stack overflow caused by the large struct size. Signed-off-by: Prike Liang <Prike.Liang@amd.com> Reviewed-by: Vitaly Prosyak <vitaly.prosyak@amd.com> Signed-off-by: Alex Deucher <alexander.deucher@amd.com> (cherry picked from commit dbae980eefb2f46f31cee12f1f8540d0d79f61ae)
199 lines
6.5 KiB
C
199 lines
6.5 KiB
C
// SPDX-License-Identifier: MIT
|
|
/*
|
|
* Copyright 2024 Advanced Micro Devices, Inc.
|
|
*
|
|
* Lockdep annotation for AMDGPU lock ordering
|
|
*
|
|
* This module teaches lockdep the correct lock ordering to catch
|
|
* potential deadlocks at development time rather than runtime.
|
|
*
|
|
* Based on dma-resv lockdep approach from:
|
|
* drivers/dma-buf/dma-resv.c:dma_resv_lockdep()
|
|
*/
|
|
|
|
#include "amdgpu.h"
|
|
#include "amdgpu_reset.h"
|
|
|
|
#ifdef CONFIG_LOCKDEP
|
|
|
|
struct amdgpu_lockdep_dummy_locks {
|
|
struct mutex reset_lock;
|
|
struct mutex userq_sch_mutex;
|
|
struct mutex userq_mutex;
|
|
struct mutex notifier_lock;
|
|
struct mutex vram_lock;
|
|
struct mutex srbm_mutex;
|
|
struct mutex grbm_idx_mutex;
|
|
spinlock_t mmio_idx_lock;
|
|
};
|
|
|
|
/* Lock class keys for associating with real driver locks */
|
|
static struct lock_class_key amdgpu_userq_sch_mutex_key;
|
|
static struct lock_class_key amdgpu_userq_mutex_key;
|
|
static struct lock_class_key amdgpu_notifier_lock_key;
|
|
static struct lock_class_key amdgpu_vram_lock_key;
|
|
static struct lock_class_key amdgpu_reset_sem_key;
|
|
static struct lock_class_key amdgpu_reset_lock_key;
|
|
static struct lock_class_key amdgpu_srbm_lock_key;
|
|
static struct lock_class_key amdgpu_grbm_lock_key;
|
|
static struct lock_class_key amdgpu_mmio_lock_key;
|
|
|
|
/**
|
|
* amdgpu_lockdep_set_class - Associate lock class keys with real locks
|
|
* @adev: AMDGPU device
|
|
*
|
|
* Call during device init to associate lock classes with actual locks
|
|
* so lockdep can track them properly.
|
|
*/
|
|
void amdgpu_lockdep_set_class(struct amdgpu_device *adev)
|
|
{
|
|
lockdep_set_class(&adev->gfx.userq_sch_mutex,
|
|
&amdgpu_userq_sch_mutex_key);
|
|
lockdep_set_class(&adev->notifier_lock, &amdgpu_notifier_lock_key);
|
|
lockdep_set_class(&adev->srbm_mutex, &amdgpu_srbm_lock_key);
|
|
lockdep_set_class(&adev->grbm_idx_mutex, &amdgpu_grbm_lock_key);
|
|
lockdep_set_class(&adev->mmio_idx_lock, &amdgpu_mmio_lock_key);
|
|
|
|
if (adev->reset_domain)
|
|
lockdep_set_class(&adev->reset_domain->sem,
|
|
&amdgpu_reset_sem_key);
|
|
}
|
|
|
|
/**
|
|
* amdgpu_lockdep_init - Teach lockdep the correct lock ordering
|
|
*
|
|
* Instantiates dummy objects and takes locks in the correct order to
|
|
* train lockdep. This helps catch lock ordering violations during
|
|
* development.
|
|
*
|
|
* Lock ordering hierarchy (outermost to innermost):
|
|
*
|
|
* 1. userq_sch_mutex - Global userq scheduler (enforce_isolation)
|
|
* 2. userq_mutex - Per-context userq (held across queue create/destroy)
|
|
* 3. notifier_lock - MMU notifier lock
|
|
* 4. vram_lock - VRAM allocator lock
|
|
* 5. reset_domain->sem - GPU reset synchronization
|
|
* 6. reset_lock - Reset control lock
|
|
* 7. srbm_mutex - SRBM register access
|
|
* 8. grbm_idx_mutex - GRBM index access
|
|
* 9. mmio_idx_lock - MMIO index access (spinlock)
|
|
*
|
|
* Evidence:
|
|
* - userq_sch_mutex -> userq_mutex: amdgpu_gfx_kfd_sch_ctrl() calls
|
|
* amdgpu_userq_stop_sched_for_enforce_isolation() which takes userq_mutex
|
|
* - userq_mutex -> notifier_lock: userq paths may trigger MMU notifier
|
|
* invalidation which acquires notifier_lock
|
|
* - notifier_lock -> reset_domain->sem: HMM invalidation callback holds
|
|
* notifier_lock and can wait for GPU reset completion, so notifier_lock
|
|
* must be outer to reset_domain->sem
|
|
* - vram_lock -> reset_domain->sem: VRAM management paths may need to
|
|
* wait for ongoing reset to complete
|
|
*
|
|
* Note: mmap_lock ordering relative to GPU locks is already taught
|
|
* by dma-resv (drivers/dma-buf/dma-resv.c).
|
|
*/
|
|
int amdgpu_lockdep_init(void)
|
|
{
|
|
struct amdgpu_reset_domain *reset_domain = NULL;
|
|
struct amdgpu_lockdep_dummy_locks *locks;
|
|
unsigned long flags;
|
|
|
|
locks = kzalloc(sizeof(*locks), GFP_KERNEL);
|
|
if (!locks)
|
|
return -ENOMEM;
|
|
|
|
/*
|
|
* Initialize dummy reset domain
|
|
*/
|
|
reset_domain = amdgpu_reset_create_reset_domain(SINGLE_DEVICE,
|
|
"lockdep_test");
|
|
if (!reset_domain) {
|
|
kfree(locks);
|
|
return -ENOMEM;
|
|
}
|
|
/* Initialize dummy locks */
|
|
mutex_init(&locks->userq_sch_mutex);
|
|
mutex_init(&locks->userq_mutex);
|
|
mutex_init(&locks->notifier_lock);
|
|
mutex_init(&locks->vram_lock);
|
|
mutex_init(&locks->reset_lock);
|
|
mutex_init(&locks->srbm_mutex);
|
|
mutex_init(&locks->grbm_idx_mutex);
|
|
spin_lock_init(&locks->mmio_idx_lock);
|
|
|
|
/*
|
|
* Associate dummy locks with the same class keys used for real
|
|
* driver locks. This ensures lockdep connects the ordering learned
|
|
* here with the actual locks used at runtime.
|
|
*/
|
|
lockdep_set_class(&locks->userq_sch_mutex, &amdgpu_userq_sch_mutex_key);
|
|
lockdep_set_class(&locks->userq_mutex, &amdgpu_userq_mutex_key);
|
|
lockdep_set_class(&locks->notifier_lock, &amdgpu_notifier_lock_key);
|
|
lockdep_set_class(&locks->vram_lock, &amdgpu_vram_lock_key);
|
|
lockdep_set_class(&reset_domain->sem, &amdgpu_reset_sem_key);
|
|
lockdep_set_class(&locks->reset_lock, &amdgpu_reset_lock_key);
|
|
lockdep_set_class(&locks->srbm_mutex, &amdgpu_srbm_lock_key);
|
|
lockdep_set_class(&locks->grbm_idx_mutex, &amdgpu_grbm_lock_key);
|
|
lockdep_set_class(&locks->mmio_idx_lock, &amdgpu_mmio_lock_key);
|
|
/*
|
|
* Take locks in the correct order to train lockdep.
|
|
* This establishes the dependency chain.
|
|
*/
|
|
|
|
/* Level 1: Global userq scheduler mutex (outermost) */
|
|
mutex_lock(&locks->userq_sch_mutex);
|
|
|
|
/* Level 2: Per-context userq mutex */
|
|
mutex_lock(&locks->userq_mutex);
|
|
/* Level 3: MMU notifier lock */
|
|
mutex_lock(&locks->notifier_lock);
|
|
/* Level 4: VRAM allocator lock */
|
|
mutex_lock(&locks->vram_lock);
|
|
/* Level 5: Reset domain semaphore */
|
|
down_read(&reset_domain->sem);
|
|
|
|
/* Level 6: Reset control lock */
|
|
mutex_lock(&locks->reset_lock);
|
|
/*
|
|
* Mark potential memory reclaim boundary.
|
|
* GPU operations might trigger memory allocation/reclaim.
|
|
*/
|
|
fs_reclaim_acquire(GFP_KERNEL);
|
|
|
|
/* Level 7: SRBM register access */
|
|
mutex_lock(&locks->srbm_mutex);
|
|
/* Level 8: GRBM index access */
|
|
mutex_lock(&locks->grbm_idx_mutex);
|
|
|
|
/* Level 9: MMIO index access (innermost lock, spinlock) */
|
|
spin_lock_irqsave(&locks->mmio_idx_lock, flags);
|
|
/*
|
|
* All locks acquired in order.
|
|
* Lockdep has now learned the valid dependency chain.
|
|
*/
|
|
|
|
/* Release in reverse order */
|
|
spin_unlock_irqrestore(&locks->mmio_idx_lock, flags);
|
|
mutex_unlock(&locks->grbm_idx_mutex);
|
|
mutex_unlock(&locks->srbm_mutex);
|
|
fs_reclaim_release(GFP_KERNEL);
|
|
|
|
mutex_unlock(&locks->reset_lock);
|
|
up_read(&reset_domain->sem);
|
|
|
|
mutex_unlock(&locks->vram_lock);
|
|
mutex_unlock(&locks->notifier_lock);
|
|
mutex_unlock(&locks->userq_mutex);
|
|
mutex_unlock(&locks->userq_sch_mutex);
|
|
|
|
/* Cleanup */
|
|
amdgpu_reset_put_reset_domain(reset_domain);
|
|
|
|
kfree(locks);
|
|
pr_info("AMDGPU: Lockdep annotations initialized (9 lock levels)\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
#endif /* CONFIG_LOCKDEP */
|