Skip to contentSkip to conversation

Enable SVM for Intel VT-d

PATCH In review David Woodhouse 2 participants 12 messages Oct 13, 2015 – Oct 14, 2015 1 message not in the archive

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

v2 cover Enable SVM for Intel VT-d

This patch set enables PASID support for the Intel IOMMU, along with page request support.

Like its AMD counterpart, it exposes an IOMMU-specific API. I believe we'll have a session at the Kernel Summit later this month in which we can work out a generic API which will cover the two (now) existing implementations as well as upcoming ARM (and other?) versions.

For the time being, however, exposing an Intel-specific API is good enough, especially as we don't have the required TLP prefix support on our PCIe root ports and we *can't* support discrete PCIe devices with PASID support. It's purely on-chip stuff right now, which is basically only Intel graphics.

The AMD implementation allows a per-device PASID space, and managing the PASID space is left entirely to the device driver. In contrast, this implementation maintains a per-IOMMU PASID space, and drivers calling intel_svm_bind_mm() will be *given* the PASID that they are to use. In general we seem to be converging on using a single PASID space across *all* IOMMUs in the system, and this will support that mode of operation.

The other main different to note is the lifetime management of the linked mm. This implementation keeps a refcount on the mm and will only release it when the device driver unbinds the PASID. The AMD implementation doesn't, and relies on mmu_notifier_release().

v2: Various cleanups
Support multiple devices per PASID
Support deferred invalidation
Add callback to device driver on fault
Fix fault response codes (swap INVALID vs. FAILURE) Fix PASID/PRI capability handling

Signature · 2 lines
David Woodhouse                            Open Source Technology Centre
[email protected]                              Intel Corporation

_______________________________________________
Intel-gfx mailing list
[email protected]
http://lists.freedesktop.org/mailman/listinfo/intel-gfx

A message that is not in the archive (sent off-list or not yet received): 1444771787.125804.50.camel-wEGCiKHe2LqWVfeAwA7xHQ@public.gmane.org

#2 lore raw

PATCH v2 1/10 iommu/vt-d: Use plain writeq() for dmar_writeq() where available

---
 include/linux/intel-iommu.h | 14 ++++++--------
 1 file changed, 6 insertions(+), 8 deletions(-)
include/linux/intel-iommu.h +6 -8
index 6240063..08802b9 100644
@@ -59,14 +59,11 @@
 #define DMAR_IRTA_REG	0xb8    /* Interrupt remapping table addr register */
 
 #define OFFSET_STRIDE		(9)
-/*
-#define dmar_readl(dmar, reg) readl(dmar + reg)
-#define dmar_readq(dmar, reg) ({ \
-		u32 lo, hi; \
-		lo = readl(dmar + reg); \
-		hi = readl(dmar + reg + 4); \
-		(((u64) hi) << 32) + lo; })
-*/
+
+#ifdef CONFIG_64BIT
+#define dmar_readq(a) readq(a)
+#define dmar_writeq(a,v) writeq(v,a)
+#else
 static inline u64 dmar_readq(void __iomem *addr)
 {
 	u32 lo, hi;
@@ -80,6 +77,7 @@ static inline void dmar_writeq(void __iomem *addr, u64 val)
 	writel((u32)val, addr);
 	writel((u32)(val >> 32), addr + 4);
 }
+#endif
 
 #define DMAR_VER_MAJOR(v)		(((v) & 0xf0) >> 4)
 #define DMAR_VER_MINOR(v)		((v) & 0x0f)
Signature · 5 lines
2.4.3

-- 
David Woodhouse                            Open Source Technology Centre
[email protected]                              Intel Corporation
#3 lore raw

PATCH v2 2/10 iommu/vt-d: Introduce intel_iommu=pasid28, and pasid_enabled() macro

In reply to a message not in the archive

As long as we use an identity mapping to work around the worst of the hardware bugs which caused us to defeature it and change the definition of the capability bit, we *can* use PASID support on the devices which advertised it in bit 28 of the Extended Capability Register.

Allow people to do so with 'intel_iommu=pasid28' on the command line.

---
 drivers/iommu/intel-iommu.c | 20 ++++++++++++++------
 include/linux/intel-iommu.h |  2 +-
 2 files changed, 15 insertions(+), 7 deletions(-)
drivers/iommu/intel-iommu.c +14 -6
index df53855..1448751 100644
@@ -497,13 +497,21 @@ static int dmar_forcedac;
 static int intel_iommu_strict;
 static int intel_iommu_superpage = 1;
 static int intel_iommu_ecs = 1;
+static int intel_iommu_pasid28;
+static int iommu_identity_mapping;
+
+#define IDENTMAP_ALL		1
+#define IDENTMAP_GFX		2
+#define IDENTMAP_AZALIA		4
 
 /* We only actually use ECS when PASID support (on the new bit 40)
  * is also advertised. Some early implementations — the ones with
  * PASID support on bit 28 — have issues even when we *only* use
  * extended root/context tables. */
+#define pasid_enabled(iommu) (ecap_pasid(iommu->ecap) || \
+			      (intel_iommu_pasid28 && ecap_broken_pasid(iommu->ecap)))
 #define ecs_enabled(iommu) (intel_iommu_ecs && ecap_ecs(iommu->ecap) && \
-			    ecap_pasid(iommu->ecap))
+			    pasid_enabled(iommu))
 
 int intel_iommu_gfx_mapped;
 EXPORT_SYMBOL_GPL(intel_iommu_gfx_mapped);
@@ -566,6 +574,11 @@ static int __init intel_iommu_setup(char *str)
 			printk(KERN_INFO
 				"Intel-IOMMU: disable extended context table support\n");
 			intel_iommu_ecs = 0;
+		} else if (!strncmp(str, "pasid28", 7)) {
+			printk(KERN_INFO
+				"Intel-IOMMU: enable pre-production PASID support\n");
+			intel_iommu_pasid28 = 1;
+			iommu_identity_mapping |= IDENTMAP_GFX;
 		}
 
 		str += strcspn(str, ",");
@@ -2403,11 +2416,6 @@ found_domain:
 	return domain;
 }
 
-static int iommu_identity_mapping;
-#define IDENTMAP_ALL		1
-#define IDENTMAP_GFX		2
-#define IDENTMAP_AZALIA		4
-
 static int iommu_domain_identity_map(struct dmar_domain *domain,
 				     unsigned long long start,
 				     unsigned long long end)
include/linux/intel-iommu.h +1 -1
index 08802b9..1d69c1d 100644
@@ -121,7 +121,7 @@ static inline void dmar_writeq(void __iomem *addr, u64 val)
 #define ecap_srs(e)		((e >> 31) & 0x1)
 #define ecap_ers(e)		((e >> 30) & 0x1)
 #define ecap_prs(e)		((e >> 29) & 0x1)
-/* PASID support used to be on bit 28 */
+#define ecap_broken_pasid(e)	((e >> 28) & 0x1)
 #define ecap_dis(e)		((e >> 27) & 0x1)
 #define ecap_nest(e)		((e >> 26) & 0x1)
 #define ecap_mts(e)		((e >> 25) & 0x1)
Signature · 5 lines
2.4.3

-- 
David Woodhouse                            Open Source Technology Centre
[email protected]                              Intel Corporation
#4 lore raw

PATCH v2 3/10 iommu/vt-d: Add initial support for PASID tables

In reply to a message not in the archive

Add CONFIG_INTEL_IOMMU_SVM, and allocate PASID tables on supported hardware.

---
 drivers/iommu/Kconfig       |  8 ++++++
 drivers/iommu/Makefile      |  1 +
 drivers/iommu/intel-iommu.c | 14 ++++++++++
 drivers/iommu/intel-svm.c   | 65 +++++++++++++++++++++++++++++++++++++++++++++
 include/linux/intel-iommu.h | 15 +++++++++++
 5 files changed, 103 insertions(+)
 create mode 100644 drivers/iommu/intel-svm.c
drivers/iommu/Kconfig +8 -0
index d9da766..e3b2c2e 100644
@@ -135,6 +135,14 @@ config INTEL_IOMMU
 	  and include PCI device scope covered by these DMA
 	  remapping devices.
 
+config INTEL_IOMMU_SVM
+	bool "Support for Shared Virtual Memory with Intel IOMMU"
+	depends on INTEL_IOMMU && X86
+	help
+	  Shared Virtual Memory (SVM) provides a facility for devices
+	  to access DMA resources through process address space by
+	  means of a Process Address Space ID (PASID).
+
 config INTEL_IOMMU_DEFAULT_ON
 	def_bool y
 	prompt "Enable Intel DMA Remapping Devices by default"
drivers/iommu/Makefile +1 -0
index c6dcc51..dc6f511 100644
@@ -12,6 +12,7 @@ obj-$(CONFIG_ARM_SMMU) += arm-smmu.o
 obj-$(CONFIG_ARM_SMMU_V3) += arm-smmu-v3.o
 obj-$(CONFIG_DMAR_TABLE) += dmar.o
 obj-$(CONFIG_INTEL_IOMMU) += intel-iommu.o
+obj-$(CONFIG_INTEL_IOMMU_SVM) += intel-svm.o
 obj-$(CONFIG_IPMMU_VMSA) += ipmmu-vmsa.o
 obj-$(CONFIG_IRQ_REMAP) += intel_irq_remapping.o irq_remapping.o
 obj-$(CONFIG_OMAP_IOMMU) += omap-iommu.o
drivers/iommu/intel-iommu.c +14 -0
index 1448751..7136dc4 100644
@@ -1680,6 +1680,11 @@ static void free_dmar_iommu(struct intel_iommu *iommu)
 
 	/* free context mapping */
 	free_context_table(iommu);
+
+#ifdef CONFIG_INTEL_IOMMU_SVM
+	if (pasid_enabled(iommu))
+		intel_svm_free_pasid_tables(iommu);
+#endif
 }
 
 static struct dmar_domain *alloc_domain(int flags)
@@ -3107,6 +3112,10 @@ static int __init init_dmars(void)
 
 		if (!ecap_pass_through(iommu->ecap))
 			hw_pass_through = 0;
+#ifdef CONFIG_INTEL_IOMMU_SVM
+		if (pasid_enabled(iommu))
+			intel_svm_alloc_pasid_tables(iommu);
+#endif
 	}
 
 	if (iommu_pass_through)
@@ -4122,6 +4131,11 @@ static int intel_iommu_add(struct dmar_drhd_unit *dmaru)
 	if (ret)
 		goto out;
 
+#ifdef CONFIG_INTEL_IOMMU_SVM
+	if (pasid_enabled(iommu))
+		intel_svm_alloc_pasid_tables(iommu);
+#endif
+
 	if (dmaru->ignored) {
 		/*
 		 * we always have to disable PMRs or DMA may fail on this device
drivers/iommu/intel-svm.c new file +65 -0

65 lines not on this page · raw message

include/linux/intel-iommu.h +15 -0
index 1d69c1d..0f38e60 100644
@@ -325,6 +325,9 @@ enum {
 #define VTD_FLAG_TRANS_PRE_ENABLED	(1 << 0)
 #define VTD_FLAG_IRQ_REMAP_PRE_ENABLED	(1 << 1)
 
+struct pasid_entry;
+struct pasid_state_entry;
+
 struct intel_iommu {
 	void __iomem	*reg; /* Pointer to hardware regs, virtual addr */
 	u64 		reg_phys; /* physical address of hw register set */
@@ -348,6 +351,15 @@ struct intel_iommu {
 
 	struct iommu_flush flush;
 #endif
+#ifdef CONFIG_INTEL_IOMMU_SVM
+	/* These are large and need to be contiguous, so we allocate just
+	 * one for now. We'll maybe want to rethink that if we truly give
+	 * devices away to userspace processes (e.g. for DPDK) and don't
+	 * want to trust that userspace will use *only* the PASID it was
+	 * told to. But while it's all driver-arbitrated, we're fine. */
+	struct pasid_entry *pasid_table;
+	struct pasid_state_entry *pasid_state_table;
+#endif
 	struct q_inval  *qi;            /* Queued invalidation info */
 	u32 *iommu_state; /* Store iommu states between suspend and resume.*/
 
@@ -387,6 +399,9 @@ extern int qi_submit_sync(struct qi_desc *desc, struct intel_iommu *iommu);
 
 extern int dmar_ir_support(void);
 
+extern int intel_svm_alloc_pasid_tables(struct intel_iommu *iommu);
+extern int intel_svm_free_pasid_tables(struct intel_iommu *iommu);
+
 extern const struct attribute_group *intel_iommu_groups[];
 
 #endif
Signature · 5 lines
2.4.3

-- 
David Woodhouse                            Open Source Technology Centre
[email protected]                              Intel Corporation
#5 lore raw

PATCH v2 5/10 iommu/vt-d: Always enable PASID/PRI PCI capabilities before ATS

In reply to a message not in the archive

The behaviour if you enable PASID support after ATS is undefined. So we have to enable it first, even if we don't know whether we'll need it.

This is safe enough; unless we set up a context that permits it, the device can't actually *do* anything with it.

---
 drivers/iommu/Kconfig       |  1 +
 drivers/iommu/intel-iommu.c | 96 ++++++++++++++++++++++++++++++++-------------
 2 files changed, 70 insertions(+), 27 deletions(-)
drivers/iommu/Kconfig +1 -0
index e3b2c2e..8d23f5e 100644
@@ -138,6 +138,7 @@ config INTEL_IOMMU
 config INTEL_IOMMU_SVM
 	bool "Support for Shared Virtual Memory with Intel IOMMU"
 	depends on INTEL_IOMMU && X86
+	select PCI_PASID
 	help
 	  Shared Virtual Memory (SVM) provides a facility for devices
 	  to access DMA resources through process address space by
drivers/iommu/intel-iommu.c +69 -27

151 lines not on this page · raw message

Signature · 5 lines
2.4.3

-- 
David Woodhouse                            Open Source Technology Centre
[email protected]                              Intel Corporation
#6 lore raw

PATCH v2 9/10 iommu/vt-d: Implement page request handling

In reply to a message not in the archive

Largely based on the driver-mode implementation by Jesse Barnes.

---
 drivers/iommu/intel-iommu.c |  22 +++++-
 drivers/iommu/intel-svm.c   | 173 ++++++++++++++++++++++++++++++++++++++++++++
 include/linux/intel-iommu.h |  26 +++++++
 3 files changed, 220 insertions(+), 1 deletion(-)
drivers/iommu/intel-iommu.c +21 -1
index 787cd6e..548f6e5 100644
@@ -1726,8 +1726,11 @@ static void free_dmar_iommu(struct intel_iommu *iommu)
 	free_context_table(iommu);
 
 #ifdef CONFIG_INTEL_IOMMU_SVM
-	if (pasid_enabled(iommu))
+	if (pasid_enabled(iommu)) {
+		if (ecap_prs(iommu->ecap))
+			intel_svm_finish_prq(iommu);
 		intel_svm_free_pasid_tables(iommu);
+	}
 #endif
 }
 
@@ -3250,6 +3253,13 @@ domains_done:
 
 		iommu_flush_write_buffer(iommu);
 
+#ifdef CONFIG_INTEL_IOMMU_SVM
+		if (pasid_enabled(iommu) && ecap_prs(iommu->ecap)) {
+			ret = intel_svm_enable_prq(iommu);
+			if (ret)
+				goto free_iommu;
+		}
+#endif
 		ret = dmar_set_interrupt(iommu);
 		if (ret)
 			goto free_iommu;
@@ -4194,6 +4204,14 @@ static int intel_iommu_add(struct dmar_drhd_unit *dmaru)
 
 	intel_iommu_init_qi(iommu);
 	iommu_flush_write_buffer(iommu);
+
+#ifdef CONFIG_INTEL_IOMMU_SVM
+	if (pasid_enabled(iommu) && ecap_prs(iommu->ecap)) {
+		ret = intel_svm_enable_prq(iommu);
+		if (ret)
+			goto disable_iommu;
+	}
+#endif
 	ret = dmar_set_interrupt(iommu);
 	if (ret)
 		goto disable_iommu;
@@ -4995,6 +5013,8 @@ int intel_iommu_enable_pasid(struct intel_iommu *iommu, struct intel_svm_dev *sd
 		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,
drivers/iommu/intel-svm.c +173 -0

188 lines not on this page · raw message

include/linux/intel-iommu.h +26 -0
index f16a2b9..e5b80d3 100644
@@ -265,6 +265,8 @@ enum {
 #define QI_EIOTLB_TYPE		0x6
 #define QI_PC_TYPE		0x7
 #define QI_DEIOTLB_TYPE		0x8
+#define QI_PGRP_RESP_TYPE	0x9
+#define QI_PSTRM_RESP_TYPE	0xa
 
 #define QI_IEC_SELECTIVE	(((u64)1) << 4)
 #define QI_IEC_IIDEX(idx)	(((u64)(idx & 0xffff) << 32))
@@ -315,6 +317,25 @@ enum {
 #define QI_DEV_EIOTLB_QDEP(qd)	(((qd) & 0x1f) << 16)
 #define QI_DEV_EIOTLB_MAX_INVS	32
 
+#define QI_PGRP_IDX(idx)	(((u64)(idx)) << 55)
+#define QI_PGRP_PRIV(priv)	(((u64)(priv)) << 32)
+#define QI_PGRP_RESP_CODE(res)	((u64)(res))
+#define QI_PGRP_PASID(pasid)	(((u64)(pasid)) << 32)
+#define QI_PGRP_DID(did)	(((u64)(did)) << 16)
+#define QI_PGRP_PASID_P(p)	(((u64)(p)) << 4)
+
+#define QI_PSTRM_ADDR(addr)	(((u64)(addr)) & VTD_PAGE_MASK)
+#define QI_PSTRM_DEVFN(devfn)	(((u64)(devfn)) << 4)
+#define QI_PSTRM_RESP_CODE(res)	((u64)(res))
+#define QI_PSTRM_IDX(idx)	(((u64)(idx)) << 55)
+#define QI_PSTRM_PRIV(priv)	(((u64)(priv)) << 32)
+#define QI_PSTRM_BUS(bus)	(((u64)(bus)) << 24)
+#define QI_PSTRM_PASID(pasid)	(((u64)(pasid)) << 4)
+
+#define QI_RESP_SUCCESS		0x0
+#define QI_RESP_INVALID		0x1
+#define QI_RESP_FAILURE		0xf
+
 #define QI_GRAN_ALL_ALL			0
 #define QI_GRAN_NONG_ALL		1
 #define QI_GRAN_NONG_PASID		2
@@ -369,6 +390,7 @@ enum {
 
 struct pasid_entry;
 struct pasid_state_entry;
+struct page_req_dsc;
 
 struct intel_iommu {
 	void __iomem	*reg; /* Pointer to hardware regs, virtual addr */
@@ -401,6 +423,8 @@ struct intel_iommu {
 	 * told to. But while it's all driver-arbitrated, we're fine. */
 	struct pasid_entry *pasid_table;
 	struct pasid_state_entry *pasid_state_table;
+	struct page_req_dsc *prq;
+	unsigned char prq_name[16];    /* Name for PRQ interrupt */
 	struct idr pasid_idr;
 #endif
 	struct q_inval  *qi;            /* Queued invalidation info */
@@ -445,6 +469,8 @@ extern int dmar_ir_support(void);
 #ifdef CONFIG_INTEL_IOMMU_SVM
 extern int intel_svm_alloc_pasid_tables(struct intel_iommu *iommu);
 extern int intel_svm_free_pasid_tables(struct intel_iommu *iommu);
+extern int intel_svm_enable_prq(struct intel_iommu *iommu);
+extern int intel_svm_finish_prq(struct intel_iommu *iommu);
 
 struct intel_svm_dev {
 	struct list_head list;
Signature · 5 lines
2.4.3

-- 
David Woodhouse                            Open Source Technology Centre
[email protected]                              Intel Corporation
#7 lore raw

PATCH v2 10/10 iommu/vt-d: Add callback to device driver on page faults

In reply to a message not in the archive
---
 drivers/iommu/intel-iommu.c |  3 ++-
 drivers/iommu/intel-svm.c   | 26 +++++++++++++++++++++++++-
 include/linux/intel-iommu.h |  3 +++
 include/linux/intel-svm.h   | 21 ++++++++++++++++++---
 4 files changed, 48 insertions(+), 5 deletions(-)
drivers/iommu/intel-iommu.c +2 -1
index 548f6e5..4f2eab6 100644
@@ -5029,12 +5029,13 @@ int intel_iommu_enable_pasid(struct intel_iommu *iommu, struct intel_svm_dev *sd
 	if (!info->pasid_enabled)
 		iommu_enable_dev_iotlb(info);
 
+	sdev->sid = (((u16)bus) << 8) | devfn;
+
 	if (info->ats_enabled) {
 		sdev->dev_iotlb = 1;
 		sdev->qdep = info->ats_qdep;
 		if (sdev->qdep >= QI_DEV_EIOTLB_MAX_INVS)
 			sdev->qdep = 0;
-		sdev->sid = (((u16)bus) << 8) | devfn;
 	}
 
 	return 0;
drivers/iommu/intel-svm.c +25 -1
index 0e86542..0a31350 100644
@@ -264,7 +264,7 @@ static const struct mmu_notifier_ops intel_mmuops = {
 
 static DEFINE_MUTEX(pasid_mutex);
 
-int intel_svm_bind_mm(struct device *dev, int *pasid)
+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 intel_svm_dev *sdev;
@@ -302,6 +302,10 @@ int intel_svm_bind_mm(struct device *dev, int *pasid)
 
 			list_for_each_entry(sdev, &svm->devs, list) {
 				if (dev == sdev->dev) {
+					if (sdev->ops != ops) {
+						ret = -EBUSY;
+						goto out;
+					}
 					sdev->users++;
 					goto success;
 				}
@@ -327,6 +331,7 @@ int intel_svm_bind_mm(struct device *dev, int *pasid)
 	}
 	/* Finish the setup now we know we're keeping it */
 	sdev->users = 1;
+	sdev->ops = ops;
 	init_rcu_head(&sdev->rcu);
 
 	if (!svm) {
@@ -456,6 +461,7 @@ static irqreturn_t prq_event_thread(int irq, void *d)
 	tail = dmar_readq(iommu->reg + DMAR_PQT_REG) & PRQ_RING_MASK;
 	head = dmar_readq(iommu->reg + DMAR_PQH_REG) & PRQ_RING_MASK;
 	while (head != tail) {
+		struct intel_svm_dev *sdev;
 		struct vm_area_struct *vma;
 		struct page_req_dsc *req;
 		struct qi_desc resp;
@@ -507,6 +513,24 @@ static irqreturn_t prq_event_thread(int irq, void *d)
 		up_read(&svm->mm->mmap_sem);
 	bad_req:
 		/* Accounting for major/minor faults? */
+		rcu_read_lock();
+		list_for_each_entry_rcu(sdev, &svm->devs, list) {
+			if (sdev->sid == ((req->bus << 8) | req->devfn))
+				break;
+		}
+		/* Other devices can go away, but the drivers are not permitted
+		 * to unbind while any page faults might be in flight. So it's
+		 * OK to drop the 'lock' here now we have it. */
+		rcu_read_unlock();
+
+		if (WARN_ON(&sdev->list == &svm->devs))
+			sdev = NULL;
+
+		if (sdev && sdev->ops && sdev->ops->fault_cb) {
+			int rwxp = (req->rd_req << 3) | (req->wr_req << 2) |
+				(req->wr_req << 1) | (req->exe_req);
+			sdev->ops->fault_cb(sdev->dev, req->pasid, req->addr, req->private, rwxp, result);
+		}
 
 		if (req->lpig) {
 			/* Page Group Response */
include/linux/intel-iommu.h +3 -0
index e5b80d3..57be14f 100644
@@ -472,10 +472,13 @@ extern int intel_svm_free_pasid_tables(struct intel_iommu *iommu);
 extern int intel_svm_enable_prq(struct intel_iommu *iommu);
 extern int intel_svm_finish_prq(struct intel_iommu *iommu);
 
+struct svm_dev_ops;
+
 struct intel_svm_dev {
 	struct list_head list;
 	struct rcu_head rcu;
 	struct device *dev;
+	struct svm_dev_ops *ops;
 	int users;
 	u16 did;
 	u16 dev_iotlb:1;
include/linux/intel-svm.h +18 -3
index 42f80ad..239f8a1 100644
@@ -20,10 +20,23 @@
 
 struct device;
 
+struct svm_dev_ops {
+	void (*fault_cb)(struct device *dev, int pasid, u64 address,
+			 u32 private, int rwxp, int response);
+};
+
+/* Values for rxwp in fault_cb callback */
+#define SVM_REQ_READ	(1<<3)
+#define SVM_REQ_WRITE	(1<<2)
+#define SVM_REQ_EXEC	(1<<1)
+#define SVM_REQ_PRIV	(1<<0)
+
 /**
  * intel_svm_bind_mm() - Bind the current process to a PASID
  * @dev:	Device to be granted acccess
  * @pasid:	Address for allocated PASID
+ * @flags:	Flags. Later for requesting supervisor mode, etc.
+ * @ops:	Callbacks to device driver
  *
  * This function attempts to enable PASID support for the given device.
  * If the @pasid argument is non-%NULL, a PASID is allocated for access
@@ -45,7 +58,8 @@ struct device;
  * Multiple calls from the same process may result in the same PASID
  * being re-used. A reference count is kept.
  */
-extern int intel_svm_bind_mm(struct device *dev, int *pasid);
+extern int intel_svm_bind_mm(struct device *dev, int *pasid, int flags,
+			     struct svm_dev_ops *ops);
 
 /**
  * intel_svm_unbind_mm() - Unbind a specified PASID
@@ -66,7 +80,8 @@ extern int intel_svm_unbind_mm(struct device *dev, int pasid);
 
 #else /* CONFIG_INTEL_IOMMU_SVM */
 
-static inline int intel_svm_bind_mm(struct device *dev, int *pasid)
+static inline int intel_svm_bind_mm(struct device *dev, int *pasid,
+				    int flags, struct svm_dev_ops *ops)
 {
 	return -ENOSYS;
 }
@@ -77,6 +92,6 @@ static inline int intel_svm_unbind_mm(struct device *dev, int pasid)
 }
 #endif /* CONFIG_INTEL_IOMMU_SVM */
 
-#define intel_svm_available(dev) (!intel_svm_bind_mm((dev), NULL))
+#define intel_svm_available(dev) (!intel_svm_bind_mm((dev), NULL, 0, NULL))
 
 #endif /* __INTEL_SVM_H__ */
Signature · 5 lines
2.4.3

-- 
David Woodhouse                            Open Source Technology Centre
[email protected]                              Intel Corporation
#8 lore raw

PATCH v2 4/10 iommu/vt-d: Assume BIOS lies about ATSR for integrated gfx

In reply to David Woodhouse

If the device itself reports ATS in its PCIe capabilities, but the BIOS neglects to provide an ATSR structure to indicate that the root port can also cope, then assume the latter is lying.

---
 drivers/iommu/intel-iommu.c | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)
drivers/iommu/intel-iommu.c +8 -3
index 7136dc4..4a81523b 100644
@@ -1447,9 +1447,14 @@ iommu_support_dev_iotlb (struct dmar_domain *domain, struct intel_iommu *iommu,
 	if (!pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ATS))
 		return NULL;
 
-	if (!dmar_find_matched_atsr_unit(pdev))
-		return NULL;
-
+	if (!dmar_find_matched_atsr_unit(pdev)) {
+		if (intel_iommu_pasid28 && IS_GFX_DEVICE(pdev) &&
+		    pdev->vendor == 0x8086) {
+			pr_warn("BIOS denies ATSR capability for %s; assuming it lies\n",
+				dev_name(info->dev));
+		} else
+			return NULL;
+	}
 	return info;
 }
 
Signature · 5 lines
2.4.3

-- 
David Woodhouse                            Open Source Technology Centre
[email protected]                              Intel Corporation

_______________________________________________
Intel-gfx mailing list
[email protected]
http://lists.freedesktop.org/mailman/listinfo/intel-gfx

#9 lore raw

Re: [PATCH v2 04/10] iommu/vt-d: Assume BIOS lies about ATSR for integrated gfx

Quoted 3 lines · David Woodhouse

If the device itself reports ATS in its PCIe capabilities, but the BIOS neglects to provide an ATSR structure to indicate that the root port can also cope, then assume the latter is lying.

Except, of course, that integrated devices aren't behind a root port. I think this should fix it the correct way?

drivers/iommu/intel-iommu.c +6 -3
index 4f2eab6..70a031d 100644
@@ -4280,14 +4280,17 @@ int dmar_find_matched_atsr_unit(struct pci_dev *dev)
 	dev = pci_physfn(dev);
 	for (bus = dev->bus; bus; bus = bus->parent) {
 		bridge = bus->self;
-		if (!bridge || !pci_is_pcie(bridge) ||
+		/* If it's an integrated device, allow ATS */
+		if (!bridge)
+			return 1;
+		/* Connected via non-PCIe: no ATS */
+		if (!pci_is_pcie(bridge) ||
 		    pci_pcie_type(bridge) == PCI_EXP_TYPE_PCI_BRIDGE)
 			return 0;
+		/* If we found the root port, look it up in the ATSR */
 		if (pci_pcie_type(bridge) == PCI_EXP_TYPE_ROOT_PORT)
 			break;
 	}
-	if (!bridge)
-		return 0;
 
 	rcu_read_lock();
 	list_for_each_entry_rcu(atsru, &dmar_atsr_units, list) {
Signature · 2 lines
David Woodhouse                            Open Source Technology Centre
[email protected]                              Intel Corporation
#10 lore raw

PATCH v2 6/10 iommu/vt-d: Add basic SVM PASID support

In reply to David Woodhouse

This provides basic PASID support for endpoint devices, tested with a version of the i915 driver.

---
 drivers/iommu/Kconfig         |   1 +
 drivers/iommu/intel-iommu.c   | 111 ++++++++++++++++
 drivers/iommu/intel-svm.c     | 291 ++++++++++++++++++++++++++++++++++++++++++
 include/linux/dma_remapping.h |   7 +
 include/linux/intel-iommu.h   |  68 +++++++++-
 include/linux/intel-svm.h     |  82 ++++++++++++
 6 files changed, 555 insertions(+), 5 deletions(-)
 create mode 100644 include/linux/intel-svm.h
drivers/iommu/Kconfig +1 -0
index 8d23f5e..be18b21 100644
@@ -139,6 +139,7 @@ config INTEL_IOMMU_SVM
 	bool "Support for Shared Virtual Memory with Intel IOMMU"
 	depends on INTEL_IOMMU && X86
 	select PCI_PASID
+	select MMU_NOTIFIER
 	help
 	  Shared Virtual Memory (SVM) provides a facility for devices
 	  to access DMA resources through process address space by
drivers/iommu/intel-iommu.c +111 -0

117 lines not on this page · raw message

drivers/iommu/intel-svm.c +291 -0

308 lines not on this page · raw message

include/linux/dma_remapping.h +7 -0
index 7ac17f5..0e114bf 100644
@@ -20,6 +20,13 @@
 #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_PRS		(1ULL << 9)
+#define CONTEXT_PASIDE		(1ULL << 11)
 
 struct intel_iommu;
 struct dmar_domain;
include/linux/intel-iommu.h +63 -5

112 lines not on this page · raw message

include/linux/intel-svm.h new file +82 -0

82 lines not on this page · raw message

Signature · 5 lines
2.4.3

-- 
David Woodhouse                            Open Source Technology Centre
[email protected]                              Intel Corporation

_______________________________________________
Intel-gfx mailing list
[email protected]
http://lists.freedesktop.org/mailman/listinfo/intel-gfx

#11 lore raw

PATCH v2 7/10 iommu/vt-d: Implement deferred invalidate for SVM

In reply to David Woodhouse
---
 drivers/iommu/intel-iommu.c   | 2 ++
 drivers/iommu/intel-svm.c     | 9 +++++++++
 include/linux/dma_remapping.h | 1 +
 3 files changed, 12 insertions(+)
drivers/iommu/intel-iommu.c +2 -0
index 4c462ab..787cd6e 100644
@@ -4993,6 +4993,8 @@ int intel_iommu_enable_pasid(struct intel_iommu *iommu, struct intel_svm_dev *sd
 				ctx_lo |= CONTEXT_TT_PT_PASID << 2;
 		}
 		ctx_lo |= CONTEXT_PASIDE;
+		if (iommu->pasid_state_table)
+			ctx_lo |= CONTEXT_DINVE;
 		context[0].lo = ctx_lo;
 		wmb();
 		iommu->flush.flush_context(iommu, sdev->did,
drivers/iommu/intel-svm.c +9 -0
index 82d53e1..a64720b 100644
@@ -26,6 +26,10 @@ struct pasid_entry {
 	u64 val;
 };
 
+struct pasid_state_entry {
+	u64 val;
+};
+
 int intel_svm_alloc_pasid_tables(struct intel_iommu *iommu)
 {
 	struct page *pages;
@@ -127,6 +131,11 @@ static void intel_flush_svm_range(struct intel_svm *svm, unsigned long address,
 {
 	struct intel_svm_dev *sdev;
 
+	/* Try deferred invalidate if available */
+	if (svm->iommu->pasid_state_table &&
+	    !cmpxchg64(&svm->iommu->pasid_state_table[svm->pasid].val, 0, 1ULL << 63))
+		return;
+
 	rcu_read_lock();
 	list_for_each_entry_rcu(sdev, &svm->devs, list)
 		intel_flush_svm_range_dev(svm, sdev, address, pages, ih);
include/linux/dma_remapping.h +1 -0
index 0e114bf..187c102 100644
@@ -25,6 +25,7 @@
 #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)
 
Signature · 5 lines
2.4.3

-- 
David Woodhouse                            Open Source Technology Centre
[email protected]                              Intel Corporation

_______________________________________________
Intel-gfx mailing list
[email protected]
http://lists.freedesktop.org/mailman/listinfo/intel-gfx

#12 lore raw

PATCH v2 8/10 iommu/vt-d: Generalise DMAR MSI setup to allow for page request events

In reply to David Woodhouse
---
 drivers/iommu/dmar.c        | 42 +++++++++++++++++++++++++++++++-----------
 include/linux/intel-iommu.h | 10 +++++++++-
 2 files changed, 40 insertions(+), 12 deletions(-)
drivers/iommu/dmar.c +31 -11
index 8757f8d..80e3c17 100644
@@ -1086,6 +1086,11 @@ static void free_iommu(struct intel_iommu *iommu)
 	iommu_device_destroy(iommu->iommu_dev);
 
 	if (iommu->irq) {
+		if (iommu->pr_irq) {
+			free_irq(iommu->pr_irq, iommu);
+			dmar_free_hwirq(iommu->pr_irq);
+			iommu->pr_irq = 0;
+		}
 		free_irq(iommu->irq, iommu);
 		dmar_free_hwirq(iommu->irq);
 		iommu->irq = 0;
@@ -1493,53 +1498,68 @@ static const char *dmar_get_fault_reason(u8 fault_reason, int *fault_type)
 	}
 }
 
+
+static inline int dmar_msi_reg(struct intel_iommu *iommu, int irq)
+{
+	if (iommu->irq == irq)
+		return DMAR_FECTL_REG;
+	else if (iommu->pr_irq == irq)
+		return DMAR_PECTL_REG;
+	else
+		BUG();
+}
+
 void dmar_msi_unmask(struct irq_data *data)
 {
 	struct intel_iommu *iommu = irq_data_get_irq_handler_data(data);
+	int reg = dmar_msi_reg(iommu, data->irq);
 	unsigned long flag;
 
 	/* unmask it */
 	raw_spin_lock_irqsave(&iommu->register_lock, flag);
-	writel(0, iommu->reg + DMAR_FECTL_REG);
+	writel(0, iommu->reg + reg);
 	/* Read a reg to force flush the post write */
-	readl(iommu->reg + DMAR_FECTL_REG);
+	readl(iommu->reg + reg);
 	raw_spin_unlock_irqrestore(&iommu->register_lock, flag);
 }
 
 void dmar_msi_mask(struct irq_data *data)
 {
-	unsigned long flag;
 	struct intel_iommu *iommu = irq_data_get_irq_handler_data(data);
+	int reg = dmar_msi_reg(iommu, data->irq);
+	unsigned long flag;
 
 	/* mask it */
 	raw_spin_lock_irqsave(&iommu->register_lock, flag);
-	writel(DMA_FECTL_IM, iommu->reg + DMAR_FECTL_REG);
+	writel(DMA_FECTL_IM, iommu->reg + reg);
 	/* Read a reg to force flush the post write */
-	readl(iommu->reg + DMAR_FECTL_REG);
+	readl(iommu->reg + reg);
 	raw_spin_unlock_irqrestore(&iommu->register_lock, flag);
 }
 
 void dmar_msi_write(int irq, struct msi_msg *msg)
 {
 	struct intel_iommu *iommu = irq_get_handler_data(irq);
+	int reg = dmar_msi_reg(iommu, irq);
 	unsigned long flag;
 
 	raw_spin_lock_irqsave(&iommu->register_lock, flag);
-	writel(msg->data, iommu->reg + DMAR_FEDATA_REG);
-	writel(msg->address_lo, iommu->reg + DMAR_FEADDR_REG);
-	writel(msg->address_hi, iommu->reg + DMAR_FEUADDR_REG);
+	writel(msg->data, iommu->reg + reg + 4);
+	writel(msg->address_lo, iommu->reg + reg + 8);
+	writel(msg->address_hi, iommu->reg + reg + 12);
 	raw_spin_unlock_irqrestore(&iommu->register_lock, flag);
 }
 
 void dmar_msi_read(int irq, struct msi_msg *msg)
 {
 	struct intel_iommu *iommu = irq_get_handler_data(irq);
+	int reg = dmar_msi_reg(iommu, irq);
 	unsigned long flag;
 
 	raw_spin_lock_irqsave(&iommu->register_lock, flag);
-	msg->data = readl(iommu->reg + DMAR_FEDATA_REG);
-	msg->address_lo = readl(iommu->reg + DMAR_FEADDR_REG);
-	msg->address_hi = readl(iommu->reg + DMAR_FEUADDR_REG);
+	msg->data = readl(iommu->reg + reg + 4);
+	msg->address_lo = readl(iommu->reg + reg + 8);
+	msg->address_hi = readl(iommu->reg + reg + 12);
 	raw_spin_unlock_irqrestore(&iommu->register_lock, flag);
 }
 
include/linux/intel-iommu.h +9 -1
index 46add60..f16a2b9 100644
@@ -60,6 +60,14 @@
 #define DMAR_IQA_REG	0x90	/* Invalidation queue addr register */
 #define DMAR_ICS_REG	0x9c	/* Invalidation complete status register */
 #define DMAR_IRTA_REG	0xb8    /* Interrupt remapping table addr register */
+#define DMAR_PQH_REG	0xc0	/* Page request queue head register */
+#define DMAR_PQT_REG	0xc8	/* Page request queue tail register */
+#define DMAR_PQA_REG	0xd0	/* Page request queue address register */
+#define DMAR_PRS_REG	0xdc	/* Page request status register */
+#define DMAR_PECTL_REG	0xe0	/* Page request event control register */
+#define	DMAR_PEDATA_REG	0xe4	/* Page request event interrupt data register */
+#define	DMAR_PEADDR_REG	0xe8	/* Page request event interrupt addr register */
+#define	DMAR_PEUADDR_REG 0xec	/* Page request event Upper address register */
 
 #define OFFSET_STRIDE		(9)
 
@@ -373,7 +381,7 @@ struct intel_iommu {
 	int		seq_id;	/* sequence id of the iommu */
 	int		agaw; /* agaw of this iommu */
 	int		msagaw; /* max sagaw of this iommu */
-	unsigned int 	irq;
+	unsigned int 	irq, pr_irq;
 	u16		segment;     /* PCI segment# */
 	unsigned char 	name[13];    /* Device Name */
 
Signature · 5 lines
2.4.3

-- 
David Woodhouse                            Open Source Technology Centre
[email protected]                              Intel Corporation

_______________________________________________
Intel-gfx mailing list
[email protected]
http://lists.freedesktop.org/mailman/listinfo/intel-gfx