// 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_obj(*locks); 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); /* * Register fs_reclaim lock class FIRST, before taking any locks. * * This acquire/release pair does NOT create a static lockdep edge * (no locks are held between acquire and release). It only registers * the fs_reclaim lock class with lockdep. * * The actual fs_reclaim -> notifier_lock dependency is established at * RUNTIME when memory reclaim invokes MMU notifiers: * fs_reclaim (held by reclaim) -> notifier_lock (acquired in callback) */ fs_reclaim_acquire(GFP_KERNEL); fs_reclaim_release(GFP_KERNEL); /* * 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); /* 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); 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 */
// 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_obj(*locks);
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);
/*
* Register fs_reclaim lock class FIRST, before taking any locks.
*
* This acquire/release pair does NOT create a static lockdep edge
* (no locks are held between acquire and release). It only registers
* the fs_reclaim lock class with lockdep.
*
* The actual fs_reclaim -> notifier_lock dependency is established at
* RUNTIME when memory reclaim invokes MMU notifiers:
* fs_reclaim (held by reclaim) -> notifier_lock (acquired in callback)
*/
fs_reclaim_acquire(GFP_KERNEL);
fs_reclaim_release(GFP_KERNEL);
/*
* 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);
/* 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);
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 */