Skip to content

Commit

Permalink
iommu/vt-d: Shared virtual address in scalable mode
Browse files Browse the repository at this point in the history
This patch enables the current SVA (Shared Virtual Address)
implementation to work in the scalable mode.

Cc: Ashok Raj <[email protected]>
Cc: Jacob Pan <[email protected]>
Cc: Kevin Tian <[email protected]>
Signed-off-by: Sanjay Kumar <[email protected]>
Signed-off-by: Liu Yi L <[email protected]>
Signed-off-by: Lu Baolu <[email protected]>
Reviewed-by: Ashok Raj <[email protected]>
Signed-off-by: Joerg Roedel <[email protected]>
  • Loading branch information
LuBaolu authored and joergroedel committed Dec 11, 2018
1 parent 437f35e commit 1c4f88b
Show file tree
Hide file tree
Showing 5 changed files with 20 additions and 88 deletions.
38 changes: 0 additions & 38 deletions drivers/iommu/intel-iommu.c
Original file line number Diff line number Diff line change
Expand Up @@ -5257,18 +5257,6 @@ static void intel_iommu_put_resv_regions(struct device *dev,
}

#ifdef CONFIG_INTEL_IOMMU_SVM
static inline unsigned long intel_iommu_get_pts(struct device *dev)
{
int pts, max_pasid;

max_pasid = intel_pasid_get_dev_max_id(dev);
pts = find_first_bit((unsigned long *)&max_pasid, MAX_NR_PASID_BITS);
if (pts < 5)
return 0;

return pts - 5;
}

int intel_iommu_enable_pasid(struct intel_iommu *iommu, struct intel_svm_dev *sdev)
{
struct device_domain_info *info;
Expand Down Expand Up @@ -5300,33 +5288,7 @@ int intel_iommu_enable_pasid(struct intel_iommu *iommu, struct intel_svm_dev *sd
sdev->sid = PCI_DEVID(info->bus, info->devfn);

if (!(ctx_lo & CONTEXT_PASIDE)) {
if (iommu->pasid_state_table)
context[1].hi = (u64)virt_to_phys(iommu->pasid_state_table);
context[1].lo = (u64)virt_to_phys(info->pasid_table->table) |
intel_iommu_get_pts(sdev->dev);

wmb();
/* CONTEXT_TT_MULTI_LEVEL and CONTEXT_TT_DEV_IOTLB are both
* extended to permit requests-with-PASID if the PASIDE bit
* is set. which makes sense. For CONTEXT_TT_PASS_THROUGH,
* however, the PASIDE bit is ignored and requests-with-PASID
* are unconditionally blocked. Which makes less sense.
* So convert from CONTEXT_TT_PASS_THROUGH to one of the new
* "guest mode" translation types depending on whether ATS
* is available or not. Annoyingly, we can't use the new
* modes *unless* PASIDE is set. */
if ((ctx_lo & CONTEXT_TT_MASK) == (CONTEXT_TT_PASS_THROUGH << 2)) {
ctx_lo &= ~CONTEXT_TT_MASK;
if (info->ats_supported)
ctx_lo |= CONTEXT_TT_PT_PASID_DEV_IOTLB << 2;
else
ctx_lo |= CONTEXT_TT_PT_PASID << 2;
}
ctx_lo |= CONTEXT_PASIDE;
if (iommu->pasid_state_table)
ctx_lo |= CONTEXT_DINVE;
if (info->pri_supported)
ctx_lo |= CONTEXT_PRS;
context[0].lo = ctx_lo;
wmb();
iommu->flush.flush_context(iommu, sdev->did, sdev->sid,
Expand Down
2 changes: 1 addition & 1 deletion drivers/iommu/intel-pasid.c
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ static inline void pasid_clear_entry(struct pasid_entry *pe)
WRITE_ONCE(pe->val[7], 0);
}

void intel_pasid_clear_entry(struct device *dev, int pasid)
static void intel_pasid_clear_entry(struct device *dev, int pasid)
{
struct pasid_entry *pe;

Expand Down
1 change: 0 additions & 1 deletion drivers/iommu/intel-pasid.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ void intel_pasid_free_table(struct device *dev);
struct pasid_table *intel_pasid_get_table(struct device *dev);
int intel_pasid_get_dev_max_id(struct device *dev);
struct pasid_entry *intel_pasid_get_entry(struct device *dev, int pasid);
void intel_pasid_clear_entry(struct device *dev, int pasid);
int intel_pasid_setup_first_level(struct intel_iommu *iommu,
struct device *dev, pgd_t *pgd,
int pasid, u16 did, int flags);
Expand Down
58 changes: 18 additions & 40 deletions drivers/iommu/intel-svm.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,6 @@

#include "intel-pasid.h"

#define PASID_ENTRY_P BIT_ULL(0)
#define PASID_ENTRY_FLPM_5LP BIT_ULL(9)
#define PASID_ENTRY_SRE BIT_ULL(11)

static irqreturn_t prq_event_thread(int irq, void *d);

struct pasid_state_entry {
Expand Down Expand Up @@ -248,20 +244,6 @@ static void intel_invalidate_range(struct mmu_notifier *mn,
(end - start + PAGE_SIZE - 1) >> VTD_PAGE_SHIFT, 0, 0);
}


static void intel_flush_pasid_dev(struct intel_svm *svm, struct intel_svm_dev *sdev, int pasid)
{
struct qi_desc desc;

desc.qw0 = QI_PC_TYPE | QI_PC_DID(sdev->did) |
QI_PC_PASID_SEL | QI_PC_PASID(pasid);
desc.qw1 = 0;
desc.qw2 = 0;
desc.qw3 = 0;

qi_submit_sync(&desc, svm->iommu);
}

static void intel_mm_release(struct mmu_notifier *mn, struct mm_struct *mm)
{
struct intel_svm *svm = container_of(mn, struct intel_svm, notifier);
Expand All @@ -281,8 +263,7 @@ static void intel_mm_release(struct mmu_notifier *mn, struct mm_struct *mm)
*/
rcu_read_lock();
list_for_each_entry_rcu(sdev, &svm->devs, list) {
intel_pasid_clear_entry(sdev->dev, svm->pasid);
intel_flush_pasid_dev(svm, sdev, svm->pasid);
intel_pasid_tear_down_entry(svm->iommu, sdev->dev, svm->pasid);
intel_flush_svm_range_dev(svm, sdev, 0, -1, 0, !svm->mm);
}
rcu_read_unlock();
Expand All @@ -301,11 +282,9 @@ static LIST_HEAD(global_svm_list);
int intel_svm_bind_mm(struct device *dev, int *pasid, int flags, struct svm_dev_ops *ops)
{
struct intel_iommu *iommu = intel_svm_device_to_iommu(dev);
struct pasid_entry *entry;
struct intel_svm_dev *sdev;
struct intel_svm *svm = NULL;
struct mm_struct *mm = NULL;
u64 pasid_entry_val;
int pasid_max;
int ret;

Expand Down Expand Up @@ -414,22 +393,22 @@ int intel_svm_bind_mm(struct device *dev, int *pasid, int flags, struct svm_dev_
kfree(sdev);
goto out;
}
pasid_entry_val = (u64)__pa(mm->pgd) | PASID_ENTRY_P;
} else
pasid_entry_val = (u64)__pa(init_mm.pgd) |
PASID_ENTRY_P | PASID_ENTRY_SRE;
if (cpu_feature_enabled(X86_FEATURE_LA57))
pasid_entry_val |= PASID_ENTRY_FLPM_5LP;

entry = intel_pasid_get_entry(dev, svm->pasid);
WRITE_ONCE(entry->val[0], pasid_entry_val);

/*
* Flush PASID cache when a PASID table entry becomes
* present.
*/
if (cap_caching_mode(iommu->cap))
intel_flush_pasid_dev(svm, sdev, svm->pasid);
}

spin_lock(&iommu->lock);
ret = intel_pasid_setup_first_level(iommu, dev,
mm ? mm->pgd : init_mm.pgd,
svm->pasid, FLPT_DEFAULT_DID,
mm ? 0 : PASID_FLAG_SUPERVISOR_MODE);
spin_unlock(&iommu->lock);
if (ret) {
if (mm)
mmu_notifier_unregister(&svm->notifier, mm);
intel_pasid_free_id(svm->pasid);
kfree(svm);
kfree(sdev);
goto out;
}

list_add_tail(&svm->list, &global_svm_list);
}
Expand Down Expand Up @@ -475,10 +454,9 @@ int intel_svm_unbind_mm(struct device *dev, int pasid)
* to use. We have a *shared* PASID table, because it's
* large and has to be physically contiguous. So it's
* hard to be as defensive as we might like. */
intel_flush_pasid_dev(svm, sdev, svm->pasid);
intel_pasid_tear_down_entry(iommu, dev, svm->pasid);
intel_flush_svm_range_dev(svm, sdev, 0, -1, 0, !svm->mm);
kfree_rcu(sdev, rcu);
intel_pasid_clear_entry(dev, svm->pasid);

if (list_empty(&svm->devs)) {
intel_pasid_free_id(svm->pasid);
Expand Down
9 changes: 1 addition & 8 deletions include/linux/intel-iommu.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,7 @@
#define CONTEXT_TT_MULTI_LEVEL 0
#define CONTEXT_TT_DEV_IOTLB 1
#define CONTEXT_TT_PASS_THROUGH 2
/* Extended context entry types */
#define CONTEXT_TT_PT_PASID 4
#define CONTEXT_TT_PT_PASID_DEV_IOTLB 5
#define CONTEXT_TT_MASK (7ULL << 2)

#define CONTEXT_DINVE (1ULL << 8)
#define CONTEXT_PRS (1ULL << 9)
#define CONTEXT_PASIDE (1ULL << 11)
#define CONTEXT_PASIDE BIT_ULL(3)

/*
* Intel IOMMU register specification per version 1.0 public spec.
Expand Down

0 comments on commit 1c4f88b

Please sign in to comment.