Enable SVM for Intel VT-d
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.
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): 1444348223.92154.22.camel-wEGCiKHe2LqWVfeAwA7xHQ@public.gmane.org
PATCH 1/7 iommu/vt-d: Introduce intel_iommu=pasid28, and pasid_enabled() macro
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
@@ -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,"," );
@@ -2399,11 +2412,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
@@ -123,7 +123,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
PATCH 5/7 iommu/vt-d: Assume BIOS lies about ATSR for integrated gfx
In reply to a message not in the archiveIf 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
@@ -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
PATCH 2/7 iommu/vt-d: Add initial support for PASID tables
In reply to David WoodhouseAdd 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
@@ -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
@@ -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
@@ -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)
@@ -3103,6 +3108,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)
@@ -4118,6 +4127,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
@@ -327,6 +327,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 */
@@ -350,6 +353,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.*/
@@ -389,6 +401,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
_______________________________________________
Intel-gfx mailing list
[email protected]
http://lists.freedesktop.org/mailman/listinfo/intel-gfx
PATCH 3/7 iommu/vt-d: Add intel_svm_{un, }bind_mm() functions
In reply to David WoodhouseThis provides basic PASID support for endpoint devices, tested with a version of the i915 driver.
A given process can bind to only one device per IOMMU for now. Making that more generic isn't particularly difficult but isn't needed yet, and can come later once the basic functionality is stable.
Eventually we'll also want the PASID space to be system-wide, not just per-IOMMU. But when we have that requirement we'll also have a way to achieve it.
--- drivers/iommu/intel-iommu.c | 100 ++++++++++++++++++ drivers/iommu/intel-svm.c | 229 ++++++++++++++++++++++++++++++++++++++++++ include/linux/dma_remapping.h | 7 ++ include/linux/intel-iommu.h | 59 ++++++++++- include/linux/intel-svm.h | 68 +++++++++++++ 5 files changed, 458 insertions(+), 5 deletions(-) create mode 100644 include/linux/intel-svm.h
drivers/iommu/intel-iommu.c +100 -0
@@ -4882,6 +4882,106 @@ static void intel_iommu_remove_device(struct device *dev)iommu_device_unlink (iommu->iommu_dev, dev); } +#ifdef CONFIG_INTEL_IOMMU_SVM +int intel_iommu_enable_pasid (struct intel_svm *svm) +{ +struct device_domain_info *info =NULL ; +struct context_entry *context; +struct dmar_domain *domain; +unsigned long flags; +u8 bus, devfn; +u64 ctx_lo; + +if (iommu_dummy (svm->dev)) { +dev_warn (svm->dev, +"No IOMMU translation for device; cannot enable SVM\n" ); +return -EINVAL ; + } + + domain =get_valid_domain_for_dev (svm->dev); +if (!domain) { +dev_warn (svm->dev,"Cannot get IOMMU domain to enable SVM\n" ); +return -EINVAL ; + } + + svm->iommu =device_to_iommu (svm->dev, &bus, &devfn); +if (!ecs_enabled (svm->iommu)) { +dev_dbg (svm->dev,"No ECS support on IOMMU; cannot enable SVM\n" ); +return -EINVAL ; + } + svm->did = domain->iommu_did[svm->iommu->seq_id]; + +spin_lock_irqsave (&device_domain_lock, flags); +spin_lock (&svm->iommu->lock); + context =iommu_context_addr (svm->iommu, bus, devfn,0 ); +if (WARN_ON (!context)) { +spin_unlock (&svm->iommu->lock); +spin_unlock_irqrestore (&device_domain_lock, flags); +return -EINVAL ; + } + + ctx_lo = context[0 ].lo; +/* Modes in which the device IOTLB is enabled are 1 and 5. Modes +* 3 and 7 are invalid. so we only need to test the low bit of TT */ + svm->dev_iotlb = (ctx_lo >>2 ) &1 ; + +if (!(ctx_lo &CONTEXT_PASIDE )) { + context[1 ].hi = (u64 )virt_to_phys (svm->iommu->pasid_state_table); + context[1 ].lo = (u64 )virt_to_phys (svm->iommu->pasid_table) |ecap_pss (svm->iommu->ecap); +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. */ +if ((ctx_lo &CONTEXT_TT_MASK ) == (CONTEXT_TT_PASS_THROUGH <<2 )) { + ctx_lo &= ~CONTEXT_TT_MASK ; + info =iommu_support_dev_iotlb (domain, svm->iommu, bus, devfn); +if (info) { + ctx_lo |=CONTEXT_TT_PT_PASID_DEV_IOTLB <<2 ; + svm->dev_iotlb =1 ; + }else + ctx_lo |=CONTEXT_TT_PT_PASID <<2 ; + } + ctx_lo |=CONTEXT_PASIDE ; + context[0 ].lo = ctx_lo; +wmb (); + svm->iommu->flush.flush_context (svm->iommu, svm->did, + (((u16 )bus) <<8 ) | devfn, +DMA_CCMD_MASK_NOBIT , +DMA_CCMD_DEVICE_INVL ); + } +spin_unlock (&svm->iommu->lock); +spin_unlock_irqrestore (&device_domain_lock, flags); + +/* This only happens if we just switched from CONTEXT_TT_PASS_THROUGH */ +if (info) +iommu_enable_dev_iotlb (info); + +/* This can also happen when we were already in a dev-iotlb mode */ +if (svm->dev_iotlb) { + svm->qdep =pci_ats_queue_depth (to_pci_dev (svm->dev)); +if (svm->qdep >=QI_DEV_EIOTLB_MAX_INVS ) + svm->qdep =0 ; + svm->sid = (((u16 )bus) <<8 ) | devfn; + } + +return 0 ; +} + +/* Helper function for SVM code, so that we can look up a given PASID +* in its IOMMU's pasid_idr for unbinding */ +struct intel_iommu *intel_iommu_device_to_iommu (struct device *dev) +{ +u8 bus, devfn; + +return device_to_iommu (dev, &bus, &devfn); +} +#endif /* CONFIG_INTEL_IOMMU_SVM */ +static const struct iommu_ops intel_iommu_ops = { .capable = intel_iommu_capable, .domain_alloc = intel_iommu_domain_alloc,
drivers/iommu/intel-svm.c +229 -0
246 lines not on this page · raw message
include/linux/dma_remapping.h +7 -0
@@ -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 +54 -5
100 lines not on this page · raw message
include/linux/intel-svm.h new file +68 -0
68 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
PATCH 4/7 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
@@ -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
@@ -59,6 +59,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)/*
@@ -374,7 +382,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
PATCH 6/7 iommu/vt-d: Enable page request interrupt
In reply to David Woodhouse--- drivers/iommu/intel-iommu.c | 22 +++++++++++++- drivers/iommu/intel-svm.c | 71 +++++++++++++++++++++++++++++++++++++++++++++ include/linux/intel-iommu.h | 5 ++++ 3 files changed, 97 insertions(+), 1 deletion(-)
drivers/iommu/intel-iommu.c +21 -1
@@ -1687,8 +1687,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 }
@@ -3204,6 +3207,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;
@@ -4148,6 +4158,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;
@@ -4952,6 +4970,8 @@ int intel_iommu_enable_pasid(struct intel_svm *svm) ctx_lo |=CONTEXT_TT_PT_PASID <<2 ; } ctx_lo |=CONTEXT_PASIDE ; +if (svm->dev_iotlb &&ecap_prs (svm->iommu->ecap)) + ctx_lo |=CONTEXT_PRS ; context[0 ].lo = ctx_lo;wmb (); svm->iommu->flush.flush_context(svm->iommu, svm->did,
drivers/iommu/intel-svm.c +71 -0
86 lines not on this page · raw message
include/linux/intel-iommu.h +5 -0
@@ -370,6 +370,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 */
@@ -402,6 +403,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 +448,8 @@ 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 int intel_svm_enable_prq (struct intel_iommu *iommu); +extern int intel_svm_finish_prq (struct intel_iommu *iommu);struct intel_svm {struct kref kref;
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
PATCH 7/7 iommu/vt-d: Implement page request handling
In reply to David Woodhouse--- drivers/iommu/intel-svm.c | 115 +++++++++++++++++++++++++++++++++++++++++++- include/linux/intel-iommu.h | 21 ++++++++ 2 files changed, 134 insertions(+), 2 deletions(-)
drivers/iommu/intel-svm.c +113 -2
@@ -356,10 +356,121 @@ int intel_svm_unbind_mm(struct device *dev, int pasid) }EXPORT_SYMBOL_GPL (intel_svm_unbind_mm); +/* Page request queue descriptor */ +struct page_req_dsc { +u64 srr:1 ; +u64 bof:1 ; +u64 pasid_present:1 ; +u64 lpig:1 ; +u64 pasid:20 ; +u64 bus:8 ; +u64 private:23 ; +u64 prg_index:9 ; +u64 rd_req:1 ; +u64 wr_req:1 ; +u64 exe_req:1 ; +u64 priv_req:1 ; +u64 devfn:8 ; +u64 addr:52 ; +}; +#define PRQ_RING_MASK ((0x1000 << PRQ_ORDER) - 0x10)static irqreturn_t prq_event_thread (int irq,void *d) {struct intel_iommu *iommu = d; -printk ("PRQ event on %s\n" , iommu->name); -return IRQ_HANDLED ; +struct intel_svm *svm =NULL ; +int head, tail, handled =0 ; + + 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 vm_area_struct *vma; +struct page_req_dsc *req; +struct qi_desc resp; +int ret, result; +u64 address; + + handled =1 ; + + req = &iommu->prq[head /sizeof (*req)]; + + result =QI_RESP_INVALID ; + address = req->addr <<PAGE_SHIFT ; +if (!req->pasid_present) { +pr_err ("%s: Page request without PASID: %08llx %08llx\n" , + iommu->name, ((unsigned long long *)req)[0 ], + ((unsigned long long *)req)[1 ]); + goto inval_req; + } + +if (!svm || svm->pasid != req->pasid) { +mutex_lock (&pasid_mutex); +if (svm) +kref_put (&svm->kref, &intel_mm_free); + + svm =idr_find (&iommu->pasid_idr, req->pasid); +if (!svm) { +pr_err ("%s: Page request for invalid PASID %d: %08llx %08llx\n" , + iommu->name, req->pasid, ((unsigned long long *)req)[0 ], + ((unsigned long long *)req)[1 ]); +mutex_unlock (&pasid_mutex); + goto inval_req; + } +/* Strictly speaking, we shouldn't need this. It is forbidden +to unbind the PASID while there may still be requests in +flight. But let's do it anyway. */ +kref_get (&svm->kref); +mutex_unlock (&pasid_mutex); + } + + result =QI_RESP_FAILURE ; +down_read (&svm->mm->mmap_sem); + vma =find_extend_vma (svm->mm, address); +if (!vma || address < vma->vm_start) + goto hard_fault; + + ret =handle_mm_fault (svm->mm, vma, address, + req->wr_req ?FAULT_FLAG_WRITE :0 ); +if (ret &VM_FAULT_ERROR ) + goto hard_fault; + + result =QI_RESP_SUCCESS ; + hard_fault: +up_read (&svm->mm->mmap_sem); + inval_req: +/* Accounting for major/minor faults? */ + +if (req->lpig) { +/* Page Group Response */ + resp.low =QI_PGRP_PASID (req->pasid) | +QI_PGRP_DID ((req->bus <<8 ) | req->devfn) | +QI_PGRP_PASID_P (req->pasid_present) | +QI_PGRP_RESP_TYPE ; + resp.high =QI_PGRP_IDX (req->prg_index) | +QI_PGRP_PRIV (req->private) |QI_PGRP_RESP_CODE (result); + +qi_submit_sync (&resp, svm->iommu); + }else if (req->srr) { +/* Page Stream Response */ + resp.low =QI_PSTRM_IDX (req->prg_index) | +QI_PSTRM_PRIV (req->private) |QI_PSTRM_BUS (req->bus) | +QI_PSTRM_PASID (req->pasid) |QI_PSTRM_RESP_TYPE ; + resp.high =QI_PSTRM_ADDR (address) |QI_PSTRM_DEVFN (req->devfn) | +QI_PSTRM_RESP_CODE (result); + +qi_submit_sync (&resp, svm->iommu); + } + + head = (head +sizeof (*req)) &PRQ_RING_MASK ; + } + +if (svm) { +mutex_lock (&pasid_mutex); +kref_put (&svm->kref, &intel_mm_free); +mutex_unlock (&pasid_mutex); + } + +dmar_writeq (iommu->reg +DMAR_PQH_REG , tail); + +return IRQ_RETVAL (handled); }
include/linux/intel-iommu.h +21 -0
@@ -266,6 +266,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))
@@ -316,6 +318,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
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
Re: [PATCH 7/7] iommu/vt-d: Implement page request handling
Quoted 10 lines · David Woodhouse
Signed-off-by David Woodhouse <[email protected]>--- drivers/iommu/intel-svm.c | 115 +++++++++++++++++++++++++++++++++++++++++++- include/linux/intel-iommu.h | 21 ++++++++ 2 files changed, 134 insertions(+), 2 deletions(-)drivers/iommu/intel-svm.c +0
-0
drivers/iommu/intel-svm.c int intel_svm_unbind_mm(struct device *dev, int pasid) · line 416
Quoted 7 lines
drivers/iommu/intel-svm.c +4
-0static irqreturn_t prq_event_thread (int irq,void *d) { +if (svm) { +mutex_lock (&pasid_mutex); +kref_put (&svm->kref, &intel_mm_free); +mutex_unlock (&pasid_mutex);
kref_put_mutex(&svm->kref, intel_mm_free, &pasid_mutex); ? -Chris
Signature · 5 lines
Chris Wilson, Intel Open Source Technology Centre _______________________________________________ Intel-gfx mailing list [email protected] http://lists.freedesktop.org/mailman/listinfo/intel-gfx
Quoted 14 lines · David Woodhouse
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.
Quoted 7 lines
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 noticeable difference is the lifetime management of the mm. My code takes a reference on it, and will only do the mmput() when the driver unbinds the PASID. So the mmu_notifier's .release() method won't get called before that.
The AMD version doesn't take that refcount, and its .release() method therefore needs to actually call back into the device driver and ensure that all access to the mm, including pending page faults, is flushed. The locking issues there scare me a little, especially if page faults are currently outstanding.
In the i915 case we have an open file descriptor associated with the gfx context. When the process dies, the fd is closed and the driver can go and clean up after it.
The amdkfd driver, on the other hand, keeps the device-side job running even after the process has closed its file descriptor. So it *needs* the .release() call to happen when the process exits, as it otherwise doesn't know when to clean up.
I am somewhat dubious about that as a design decision. If we're moving to a more explicit management of off-cpu tasks with mm access, as is to be discussed at the Kernel Summit, then hopefully we can fix that. It's a *lot* simpler if we just pin the mm while the device context has access to it.
Signature · 1 line
dwmw2
_______________________________________________
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): 1444483075.92154.98.camel-wEGCiKHe2LqWVfeAwA7xHQ@public.gmane.org
Quoted 44 lines · David Woodhouse
Quoted 14 lines · David Woodhouse
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.
Quoted 7 lines
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 noticeable difference is the lifetime management of the mm. My code takes a reference on it, and will only do the
mmput()when the driver unbinds the PASID. So the mmu_notifier's .release() method won't get called before that.The AMD version doesn't take that refcount, and its .release() method therefore needs to actually call back into the device driver and ensure that all access to the mm, including pending page faults, is flushed. The locking issues there scare me a little, especially if page faults are currently outstanding.
In the i915 case we have an open file descriptor associated with the gfx context. When the process dies, the fd is closed and the driver can go and clean up after it.
The amdkfd driver, on the other hand, keeps the device-side job running even after the process has closed its file descriptor. So it *needs* the .release() call to happen when the process exits, as it otherwise doesn't know when to clean up.
Quoted 9 lines
I am somewhat dubious about that as a design decision. If we're moving to a more explicit management of off-cpu tasks with mm access, as is to be discussed at the Kernel Summit, then hopefully we can fix that. It's a *lot* simpler if we just pin the mm while the device context has access to it.
--
dwmw2
Hi David,
There was a whole debate about this issue (amdkfd binding to mm struct lifespan instead of to fd) when we upstreamed amdkfd, with good arguments for and against. If you want to understand the reasons, I suggest reading the following email thread:
https://lists.linuxfoundation.org/pipermail/iommu/2014-July/009005.html
TL;DR, IIRC, the bottom line was that (over-simplified):
1. HSA/amdkfd is not a "classic" device driver, is it performs operations in context of a process working on multiple devices and doesn't contain an "instance per device". It's conceptually more like a subsystem/system call interface then a device driver.
2. It is not a one-of-a-kind in the kernel, as there are other drivers which use this method.
Oded
Quoted 43 lines · David Woodhouse
Quoted 14 lines · David Woodhouse
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.
Quoted 7 lines
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 noticeable difference is the lifetime management of the mm. My code takes a reference on it, and will only do the
mmput()when the driver unbinds the PASID. So the mmu_notifier's .release() method won't get called before that.The AMD version doesn't take that refcount, and its .release() method therefore needs to actually call back into the device driver and ensure that all access to the mm, including pending page faults, is flushed. The locking issues there scare me a little, especially if page faults are currently outstanding.
In the i915 case we have an open file descriptor associated with the gfx context. When the process dies, the fd is closed and the driver can go and clean up after it.
The amdkfd driver, on the other hand, keeps the device-side job running even after the process has closed its file descriptor. So it *needs* the .release() call to happen when the process exits, as it otherwise doesn't know when to clean up.
Quoted 5 lines
I am somewhat dubious about that as a design decision. If we're moving to a more explicit management of off-cpu tasks with mm access, as is to be discussed at the Kernel Summit, then hopefully we can fix that. It's a *lot* simpler if we just pin the mm while the device context has access to it.
I think acquiring a full reference on the mm makes sense. Conceptually an svm context is just another compute thread, just not running on the cpu. The other way round would mean that at mm exit we tear down these additional threads, which seems a bit backwards.
Of course that special thread is attached to an fd, which has a completely separate lifetime from threads. There's also the fun that a different mm could submit commands to a foreign svm context. So no perfect fit, but on a hunch still feels like grabbing a full reference is the cleaner design. And it matches the refcounting we do for traditional gpu contexts on the ppgtt address space they're using.
-Daniel
Signature · 7 lines
Daniel Vetter Software Engineer, Intel Corporation http://blog.ffwll.ch _______________________________________________ Intel-gfx mailing list [email protected] http://lists.freedesktop.org/mailman/listinfo/intel-gfx