Skip to content

Commit 37e91bd

Browse files
LuBaolujoergroedel
authored andcommitted
iommu/vt-d: Disable non-recoverable fault processing before unbind
When a PASID is used for SVA by the device, it's possible that the PASID entry is cleared before the device flushes all ongoing DMA requests. The IOMMU should tolerate and ignore the non-recoverable faults caused by the untranslated requests from this device. For example, when an exception happens, the process terminates before the device driver stops DMA and call IOMMU driver to unbind PASID. The flow of process exist is as follows: do_exit() { exit_mm() { mm_put(); exit_mmap() { intel_invalidate_range() //mmu notifier tlb_finish_mmu() mmu_notifier_release(mm) { intel_iommu_release() { [2] intel_iommu_teardown_pasid(); intel_iommu_flush_tlbs(); } } unmap_vmas(); free_pgtables(); }; } exit_files(tsk) { close_files() { dsa_close(); [1] dsa_stop_dma(); intel_svm_unbind_pasid(); } } } Care must be taken on VT-d to avoid unrecoverable faults between the time window of [1] and [2]. [Process exist flow was contributed by Jacob Pan.] Intel VT-d provides such function through the FPD bit of the PASID entry. This sets FPD bit when PASID entry is changing from present to nonpresent in the mm notifier and will clear it when the pasid is unbound. Signed-off-by: Lu Baolu <[email protected]> Reviewed-by: Jacob Pan <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Joerg Roedel <[email protected]>
1 parent 4c0fa5b commit 37e91bd

File tree

4 files changed

+32
-11
lines changed

4 files changed

+32
-11
lines changed

drivers/iommu/intel-iommu.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5005,7 +5005,7 @@ static void __dmar_remove_one_dev_info(struct device_domain_info *info)
50055005
if (info->dev) {
50065006
if (dev_is_pci(info->dev) && sm_supported(iommu))
50075007
intel_pasid_tear_down_entry(iommu, info->dev,
5008-
PASID_RID2PASID);
5008+
PASID_RID2PASID, false);
50095009

50105010
iommu_disable_dev_iotlb(info);
50115011
domain_context_clear(iommu, info->dev);
@@ -5234,7 +5234,7 @@ static void aux_domain_remove_dev(struct dmar_domain *domain,
52345234
auxiliary_unlink_device(domain, dev);
52355235

52365236
spin_lock(&iommu->lock);
5237-
intel_pasid_tear_down_entry(iommu, dev, domain->default_pasid);
5237+
intel_pasid_tear_down_entry(iommu, dev, domain->default_pasid, false);
52385238
domain_detach_iommu(domain, iommu);
52395239
spin_unlock(&iommu->lock);
52405240

drivers/iommu/intel-pasid.c

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -292,15 +292,31 @@ static inline void pasid_clear_entry(struct pasid_entry *pe)
292292
WRITE_ONCE(pe->val[7], 0);
293293
}
294294

295-
static void intel_pasid_clear_entry(struct device *dev, int pasid)
295+
static inline void pasid_clear_entry_with_fpd(struct pasid_entry *pe)
296+
{
297+
WRITE_ONCE(pe->val[0], PASID_PTE_FPD);
298+
WRITE_ONCE(pe->val[1], 0);
299+
WRITE_ONCE(pe->val[2], 0);
300+
WRITE_ONCE(pe->val[3], 0);
301+
WRITE_ONCE(pe->val[4], 0);
302+
WRITE_ONCE(pe->val[5], 0);
303+
WRITE_ONCE(pe->val[6], 0);
304+
WRITE_ONCE(pe->val[7], 0);
305+
}
306+
307+
static void
308+
intel_pasid_clear_entry(struct device *dev, int pasid, bool fault_ignore)
296309
{
297310
struct pasid_entry *pe;
298311

299312
pe = intel_pasid_get_entry(dev, pasid);
300313
if (WARN_ON(!pe))
301314
return;
302315

303-
pasid_clear_entry(pe);
316+
if (fault_ignore && pasid_pte_is_present(pe))
317+
pasid_clear_entry_with_fpd(pe);
318+
else
319+
pasid_clear_entry(pe);
304320
}
305321

306322
static inline void pasid_set_bits(u64 *ptr, u64 mask, u64 bits)
@@ -473,8 +489,8 @@ devtlb_invalidation_with_pasid(struct intel_iommu *iommu,
473489
qi_flush_dev_iotlb(iommu, sid, pfsid, qdep, 0, 64 - VTD_PAGE_SHIFT);
474490
}
475491

476-
void intel_pasid_tear_down_entry(struct intel_iommu *iommu,
477-
struct device *dev, int pasid)
492+
void intel_pasid_tear_down_entry(struct intel_iommu *iommu, struct device *dev,
493+
int pasid, bool fault_ignore)
478494
{
479495
struct pasid_entry *pte;
480496
u16 did;
@@ -484,7 +500,7 @@ void intel_pasid_tear_down_entry(struct intel_iommu *iommu,
484500
return;
485501

486502
did = pasid_get_domain_id(pte);
487-
intel_pasid_clear_entry(dev, pasid);
503+
intel_pasid_clear_entry(dev, pasid, fault_ignore);
488504

489505
if (!ecap_coherent(iommu->ecap))
490506
clflush_cache_range(pte, sizeof(*pte));

drivers/iommu/intel-pasid.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#define PASID_MAX 0x100000
1616
#define PASID_PTE_MASK 0x3F
1717
#define PASID_PTE_PRESENT 1
18+
#define PASID_PTE_FPD 2
1819
#define PDE_PFN_MASK PAGE_MASK
1920
#define PASID_PDE_SHIFT 6
2021
#define MAX_NR_PASID_BITS 20
@@ -120,7 +121,8 @@ int intel_pasid_setup_nested(struct intel_iommu *iommu,
120121
struct iommu_gpasid_bind_data_vtd *pasid_data,
121122
struct dmar_domain *domain, int addr_width);
122123
void intel_pasid_tear_down_entry(struct intel_iommu *iommu,
123-
struct device *dev, int pasid);
124+
struct device *dev, int pasid,
125+
bool fault_ignore);
124126
int vcmd_alloc_pasid(struct intel_iommu *iommu, unsigned int *pasid);
125127
void vcmd_free_pasid(struct intel_iommu *iommu, unsigned int pasid);
126128
#endif /* __INTEL_PASID_H */

drivers/iommu/intel-svm.c

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,8 @@ static void intel_mm_release(struct mmu_notifier *mn, struct mm_struct *mm)
207207
*/
208208
rcu_read_lock();
209209
list_for_each_entry_rcu(sdev, &svm->devs, list) {
210-
intel_pasid_tear_down_entry(svm->iommu, sdev->dev, svm->pasid);
210+
intel_pasid_tear_down_entry(svm->iommu, sdev->dev,
211+
svm->pasid, true);
211212
intel_flush_svm_range_dev(svm, sdev, 0, -1, 0);
212213
}
213214
rcu_read_unlock();
@@ -396,7 +397,8 @@ int intel_svm_unbind_gpasid(struct device *dev, int pasid)
396397
sdev->users--;
397398
if (!sdev->users) {
398399
list_del_rcu(&sdev->list);
399-
intel_pasid_tear_down_entry(iommu, dev, svm->pasid);
400+
intel_pasid_tear_down_entry(iommu, dev,
401+
svm->pasid, false);
400402
intel_flush_svm_range_dev(svm, sdev, 0, -1, 0);
401403
/* TODO: Drain in flight PRQ for the PASID since it
402404
* may get reused soon, we don't want to
@@ -639,7 +641,8 @@ int intel_svm_unbind_mm(struct device *dev, int pasid)
639641
* to use. We have a *shared* PASID table, because it's
640642
* large and has to be physically contiguous. So it's
641643
* hard to be as defensive as we might like. */
642-
intel_pasid_tear_down_entry(iommu, dev, svm->pasid);
644+
intel_pasid_tear_down_entry(iommu, dev,
645+
svm->pasid, false);
643646
intel_flush_svm_range_dev(svm, sdev, 0, -1, 0);
644647
kfree_rcu(sdev, rcu);
645648

0 commit comments

Comments
 (0)