207 lines · 6.8 KB · C
RawDownload
// 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 */