mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2025-01-01 19:34:35 +08:00
iommu/vt-d: Fix lockdep splat in intel_pasid_get_entry()
The pasid_lock is used to synchronize different threads from modifying a same pasid directory entry at the same time. It causes below lockdep splat. [ 83.296538] ======================================================== [ 83.296538] WARNING: possible irq lock inversion dependency detected [ 83.296539] 5.12.0-rc3+ #25 Tainted: G W [ 83.296539] -------------------------------------------------------- [ 83.296540] bash/780 just changed the state of lock: [ 83.296540] ffffffff82b29c98 (device_domain_lock){..-.}-{2:2}, at: iommu_flush_dev_iotlb.part.0+0x32/0x110 [ 83.296547] but this lock took another, SOFTIRQ-unsafe lock in the past: [ 83.296547] (pasid_lock){+.+.}-{2:2} [ 83.296548] and interrupts could create inverse lock ordering between them. [ 83.296549] other info that might help us debug this: [ 83.296549] Chain exists of: device_domain_lock --> &iommu->lock --> pasid_lock [ 83.296551] Possible interrupt unsafe locking scenario: [ 83.296551] CPU0 CPU1 [ 83.296552] ---- ---- [ 83.296552] lock(pasid_lock); [ 83.296553] local_irq_disable(); [ 83.296553] lock(device_domain_lock); [ 83.296554] lock(&iommu->lock); [ 83.296554] <Interrupt> [ 83.296554] lock(device_domain_lock); [ 83.296555] *** DEADLOCK *** Fix it by replacing the pasid_lock with an atomic exchange operation. Reported-and-tested-by: Dave Jiang <dave.jiang@intel.com> Signed-off-by: Lu Baolu <baolu.lu@linux.intel.com> Link: https://lore.kernel.org/r/20210320020916.640115-1-baolu.lu@linux.intel.com Signed-off-by: Joerg Roedel <jroedel@suse.de>
This commit is contained in:
parent
396bd6f3d9
commit
803766cbf8
@ -24,7 +24,6 @@
|
|||||||
/*
|
/*
|
||||||
* Intel IOMMU system wide PASID name space:
|
* Intel IOMMU system wide PASID name space:
|
||||||
*/
|
*/
|
||||||
static DEFINE_SPINLOCK(pasid_lock);
|
|
||||||
u32 intel_pasid_max_id = PASID_MAX;
|
u32 intel_pasid_max_id = PASID_MAX;
|
||||||
|
|
||||||
int vcmd_alloc_pasid(struct intel_iommu *iommu, u32 *pasid)
|
int vcmd_alloc_pasid(struct intel_iommu *iommu, u32 *pasid)
|
||||||
@ -259,19 +258,25 @@ struct pasid_entry *intel_pasid_get_entry(struct device *dev, u32 pasid)
|
|||||||
dir_index = pasid >> PASID_PDE_SHIFT;
|
dir_index = pasid >> PASID_PDE_SHIFT;
|
||||||
index = pasid & PASID_PTE_MASK;
|
index = pasid & PASID_PTE_MASK;
|
||||||
|
|
||||||
spin_lock(&pasid_lock);
|
retry:
|
||||||
entries = get_pasid_table_from_pde(&dir[dir_index]);
|
entries = get_pasid_table_from_pde(&dir[dir_index]);
|
||||||
if (!entries) {
|
if (!entries) {
|
||||||
entries = alloc_pgtable_page(info->iommu->node);
|
entries = alloc_pgtable_page(info->iommu->node);
|
||||||
if (!entries) {
|
if (!entries)
|
||||||
spin_unlock(&pasid_lock);
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
|
||||||
|
|
||||||
WRITE_ONCE(dir[dir_index].val,
|
/*
|
||||||
(u64)virt_to_phys(entries) | PASID_PTE_PRESENT);
|
* The pasid directory table entry won't be freed after
|
||||||
|
* allocation. No worry about the race with free and
|
||||||
|
* clear. However, this entry might be populated by others
|
||||||
|
* while we are preparing it. Use theirs with a retry.
|
||||||
|
*/
|
||||||
|
if (cmpxchg64(&dir[dir_index].val, 0ULL,
|
||||||
|
(u64)virt_to_phys(entries) | PASID_PTE_PRESENT)) {
|
||||||
|
free_pgtable_page(entries);
|
||||||
|
goto retry;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
spin_unlock(&pasid_lock);
|
|
||||||
|
|
||||||
return &entries[index];
|
return &entries[index];
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user