IOMMU driver changes for shared virtual memory virtualization
cover IOMMU driver changes for shared virtual memory virtualization
This patchset provides IOMMU driver support of shared virtual memory (SVM) virtualization. Generic APIs are introduced in addition to Intel VT-d specific changes, the goal is to have common interfaces across IOMMU and device types for both VFIO and other in-kernel users.
This is the IOMMU portion follow up of the more complete series of the kernel changes to support SVM. Please refer to the link below for more details.
https://www.spinics.net/lists/kvm/msg148819.html
New in this series are the IOMMU fault notification APIs.
At the top level, three new IOMMU interfaces are introduced:
- bind PASID table
- passdown invalidation
- per device IOMMU fault notification
The additional patches are Intel VT-d specific, which either implements or replaces existing private interfaces with the generic ones.
Thanks,
Jacob
Jacob Pan (8):
iommu: Introduce bind_pasid_table API function iommu/vt-d: add bind_pasid_table function iommu/vt-d: Add iommu do invalidate function
iommu: Introduce fault notifier API
iommu/vt-d: track device with pasid table bond to a guest iommu/dmar: notify unrecoverable faults iommu/intel-svm: notify page request to guest iommu/intel-svm: replace dev ops with generic fault notifier
Liu, Yi L (1):
iommu: Introduce iommu do invalidate API function
drivers/iommu/dmar.c | 37 ++++++++- drivers/iommu/intel-iommu.c | 169 +++++++++++++++++++++++++++++++++++++----- drivers/iommu/intel-svm.c | 94 ++++++++++++++++++++--- drivers/iommu/iommu.c | 95 ++++++++++++++++++++++++ include/linux/dma_remapping.h | 1 + include/linux/intel-iommu.h | 30 +++++++- include/linux/intel-svm.h | 20 +---- include/linux/iommu.h | 88 ++++++++++++++++++++++ include/uapi/linux/iommu.h | 37 +++++++++ 9 files changed, 522 insertions(+), 49 deletions(-) create mode 100644 include/uapi/linux/iommu.h
Signature · 1 line
2.7.4
RFC 1/9 iommu: Introduce bind_pasid_table API function
Virtual IOMMU was proposed to support Shared Virtual Memory (SVM) use case in the guest:
https://lists.gnu.org/archive/html/qemu-devel/2016-11/msg05311.html
As part of the proposed architecture, when a SVM capable PCI device is assigned to a guest, nested mode is turned on. Guest owns the first level page tables (request with PASID) and performs GVA->GPA translation. Second level page tables are owned by the host for GPA->HPA translation for both request with and without PASID.
A new IOMMU driver interface is therefore needed to perform tasks as follows:
* Enable nested translation and appropriate translation type
* Assign guest PASID table pointer (in GPA) and size to host IOMMU
This patch introduces new functions called iommu_(un)bind_pasid_table() to IOMMU APIs. Architecture specific IOMMU function can be added later to perform the specific steps for binding pasid table of assigned devices.
This patch also adds model definition in iommu.h. It would be used to check if the bind request is from a compatible entity. e.g. a bind request from an intel_iommu emulator may not be supported by an ARM SMMU driver.
--- drivers/iommu/iommu.c | 19 +++++++++++++++++++ include/linux/iommu.h | 23 +++++++++++++++++++++++ include/uapi/linux/iommu.h | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+) create mode 100644 include/uapi/linux/iommu.h
drivers/iommu/iommu.c +19 -0
@@ -1328,6 +1328,25 @@ int iommu_attach_device(struct iommu_domain *domain, struct device *dev) }EXPORT_SYMBOL_GPL (iommu_attach_device); +int iommu_bind_pasid_table (struct iommu_domain *domain,struct device *dev, +struct pasid_table_info *pasidt_binfo) +{ +if (unlikely (!domain->ops->bind_pasid_table)) +return -EINVAL ; + +return domain->ops->bind_pasid_table (domain, dev, pasidt_binfo); +} +EXPORT_SYMBOL_GPL (iommu_bind_pasid_table); + +int iommu_unbind_pasid_table (struct iommu_domain *domain,struct device *dev) +{ +if (unlikely (!domain->ops->unbind_pasid_table)) +return -EINVAL ; + +return domain->ops->unbind_pasid_table (domain, dev); +} +EXPORT_SYMBOL_GPL (iommu_unbind_pasid_table); +static void __iommu_detach_device (struct iommu_domain *domain,struct device *dev) {
include/linux/iommu.h +23 -0
@@ -25,6 +25,7 @@#include <linux/errno.h> #include <linux/err.h> #include <linux/of.h> +#include <uapi/linux/iommu.h> #define IOMMU_READ (1 << 0)#define IOMMU_WRITE (1 << 1)
@@ -183,6 +184,8 @@ struct iommu_resv_region {* @domain_get_windows: Return the number of windows for a domain * @of_xlate: add OF master IDs to iommu grouping * @pgsize_bitmap: bitmap of all possible supported page sizes +* @bind_pasid_table: bind pasid table pointer for guest SVM +* @unbind_pasid_table: unbind pasid table pointer and restore defaults */ struct iommu_ops {bool (*capable)(enum iommu_cap );
@@ -225,6 +228,10 @@ struct iommu_ops {u32 (*domain_get_windows)(struct iommu_domain *domain);int (*of_xlate)(struct device *dev,struct of_phandle_args *args); +int (*bind_pasid_table)(struct iommu_domain *domain,struct device *dev, +struct pasid_table_info *pasidt_binfo); +int (*unbind_pasid_table)(struct iommu_domain *domain, +struct device *dev);unsigned long pgsize_bitmap; };
@@ -282,6 +289,10 @@ extern int iommu_attach_device(struct iommu_domain *domain, struct device *dev);extern void iommu_detach_device (struct iommu_domain *domain,struct device *dev); +extern int iommu_bind_pasid_table (struct iommu_domain *domain, +struct device *dev,struct pasid_table_info *pasidt_binfo); +extern int iommu_unbind_pasid_table (struct iommu_domain *domain, +struct device *dev);extern struct iommu_domain *iommu_get_domain_for_dev (struct device *dev);extern int iommu_map (struct iommu_domain *domain,unsigned long iova,phys_addr_t paddr,size_t size,int prot);
@@ -637,6 +648,18 @@ const struct iommu_ops *iommu_ops_from_fwnode(struct fwnode_handle *fwnode)return NULL ; } +static inline +int iommu_bind_pasid_table (struct iommu_domain *domain,struct device *dev, +struct pasid_table_info *pasidt_binfo) +{ +return -EINVAL ; +} +static inline +int iommu_unbind_pasid_table (struct iommu_domain *domain,struct device *dev) +{ +return -EINVAL ; +} +#endif /* CONFIG_IOMMU_API */ #endif /* __LINUX_IOMMU_H */
include/uapi/linux/iommu.h new file +33 -0
@@ -0,0 +1,33 @@ +/* +* IOMMU user API definitions +* +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +*/ + +#ifndef _UAPI_IOMMU_H +#define _UAPI_IOMMU_H + +/** +* PASID table data used to bind guest PASID table to the host IOMMU. This will +* enable guest managed first level page tables. +* @ptr PASID table pointer in GPA +* @size size of the guest PASID table, must be <= host table size +* @model magic number tells vendor apart +* @length length of the opaque data +* @opaque architecture specific IOMMU data +*/ +struct pasid_table_info { +__u64 ptr; +__u64 size; +__u32 model; +#define INTEL_IOMMU (1 << 0) +#define ARM_SMMU (1 << 1) +__u32 length; +__u8 opaque[]; +}; + + +#endif /* _UAPI_IOMMU_H */
Signature · 1 line
2.7.4
Re: [RFC 1/9] iommu: Introduce bind_pasid_table API function
Quoted 138 lines · On Wed, 14 Jun 2017 15:22:55 -0700 Jacob Pan…
Virtual IOMMU was proposed to support Shared Virtual Memory (SVM) use case in the guest:
https://lists.gnu.org/archive/html/qemu-devel/2016-11/msg05311.htmlAs part of the proposed architecture, when a SVM capable PCI device is assigned to a guest, nested mode is turned on. Guest owns the first level page tables (request with PASID) and performs
GVA->GPAtranslation. Second level page tables are owned by the host forGPA->HPAtranslation for both request with and without PASID.A new IOMMU driver interface is therefore needed to perform tasks as follows:
* Enable nested translation and appropriate translation type
* Assign guest PASID table pointer (in GPA) and size to host IOMMUThis patch introduces new functions called iommu_(un)bind_pasid_table() to IOMMU APIs. Architecture specific IOMMU function can be added later to perform the specific steps for binding pasid table of assigned devices.
This patch also adds model definition in
iommu.h. It would be used to check if the bind request is from a compatible entity. e.g. a bind request from anintel_iommuemulator may not be supported by an ARM SMMU driver.Signed-off-by Ashok Raj <[email protected]>--- drivers/iommu/iommu.c | 19 +++++++++++++++++++ include/linux/iommu.h | 23 +++++++++++++++++++++++ include/uapi/linux/iommu.h | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+) create mode 100644 include/uapi/linux/iommu.hdrivers/iommu/iommu.c +19
-0@@ -1328,6 +1328,25 @@ int iommu_attach_device(struct iommu_domain *domain, struct device *dev) }EXPORT_SYMBOL_GPL (iommu_attach_device); +int iommu_bind_pasid_table (struct iommu_domain *domain,struct device *dev, +struct pasid_table_info *pasidt_binfo) +{ +if (unlikely (!domain->ops->bind_pasid_table)) +return -EINVAL ; + +return domain->ops->bind_pasid_table (domain, dev, pasidt_binfo); +} +EXPORT_SYMBOL_GPL (iommu_bind_pasid_table); + +int iommu_unbind_pasid_table (struct iommu_domain *domain,struct device *dev) +{ +if (unlikely (!domain->ops->unbind_pasid_table)) +return -EINVAL ; + +return domain->ops->unbind_pasid_table (domain, dev); +} +EXPORT_SYMBOL_GPL (iommu_unbind_pasid_table); +static void __iommu_detach_device (struct iommu_domain *domain,struct device *dev) {include/linux/iommu.h +23
-0@@ -25,6 +25,7 @@#include <linux/errno.h> #include <linux/err.h> #include <linux/of.h> +#include <uapi/linux/iommu.h> #define IOMMU_READ (1 << 0)#define IOMMU_WRITE (1 << 1)@@ -183,6 +184,8 @@ struct iommu_resv_region {* @domain_get_windows: Return the number of windows for a domain * @of_xlate: add OF master IDs to iommu grouping * @pgsize_bitmap: bitmap of all possible supported page sizes +* @bind_pasid_table: bind pasid table pointer for guest SVM +* @unbind_pasid_table: unbind pasid table pointer and restore defaults */ struct iommu_ops {bool (*capable)(enum iommu_cap );@@ -225,6 +228,10 @@ struct iommu_ops {u32 (*domain_get_windows)(struct iommu_domain *domain);int (*of_xlate)(struct device *dev,struct of_phandle_args *args); +int (*bind_pasid_table)(struct iommu_domain *domain,struct device *dev, +struct pasid_table_info *pasidt_binfo); +int (*unbind_pasid_table)(struct iommu_domain *domain, +struct device *dev);unsigned long pgsize_bitmap; };@@ -282,6 +289,10 @@ extern int iommu_attach_device(struct iommu_domain *domain, struct device *dev);extern void iommu_detach_device (struct iommu_domain *domain,struct device *dev); +extern int iommu_bind_pasid_table (struct iommu_domain *domain, +struct device *dev,struct pasid_table_info *pasidt_binfo); +extern int iommu_unbind_pasid_table (struct iommu_domain *domain, +struct device *dev);extern struct iommu_domain *iommu_get_domain_for_dev (struct device *dev);extern int iommu_map (struct iommu_domain *domain,unsigned long iova,phys_addr_t paddr,size_t size,int prot);@@ -637,6 +648,18 @@ const struct iommu_ops *iommu_ops_from_fwnode(struct fwnode_handle *fwnode)return NULL ; } +static inline +int iommu_bind_pasid_table (struct iommu_domain *domain,struct device *dev, +struct pasid_table_info *pasidt_binfo) +{ +return -EINVAL ; +} +static inline +int iommu_unbind_pasid_table (struct iommu_domain *domain,struct device *dev) +{ +return -EINVAL ; +} +#endif /* CONFIG_IOMMU_API */ #endif /* __LINUX_IOMMU_H */ include/uapi/linux/iommu.h new file +6
-0@@ -0,0 +1,33 @@ +/* +* IOMMU user API definitions +* +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as
include/uapi/linux/iommu.h line 16
Quoted 10 lines
include/uapi/linux/iommu.h new file +10
-0+* published by the Free Software Foundation. +*/ + +#ifndef _UAPI_IOMMU_H +#define _UAPI_IOMMU_H + +/** +* PASID table data used to bind guest PASID table to the host IOMMU. This will +* enable guest managed first level page tables. +* @ptr PASID table pointer in GPA
GPA? I'm confused how the host physical IOMMU needs the guest physical address of the PASID table here. The first level translation does GVA to GPA lookup, but doesn't that translation still come from a table that the IOMMU references via a host physical address? If not, is the address space of this pointer implementation/architecture specific? In general I think it's good policy to not make the interface specific to the VM use case. After all, vfio is a userspace driver interface and device assignment is just one use case.
include/uapi/linux/iommu.h line 17
Quoted 1 line
include/uapi/linux/iommu.h new file +1
-0+* @size size of the guest PASID table, must be <= host table size
Presumably in bytes, best to say so.
include/uapi/linux/iommu.h line 19
Quoted 2 lines
include/uapi/linux/iommu.h new file +2
-0+* @model magic number tells vendor apart +* @length length of the opaque data
Also in bytes.
include/uapi/linux/iommu.h line 20
Quoted 1 line
include/uapi/linux/iommu.h new file +1
-0+* @opaque architecture specific IOMMU data
s/architecture/model/?
include/uapi/linux/iommu.h line 27
Quoted 7 lines
include/uapi/linux/iommu.h new file +7
-0+*/ +struct pasid_table_info { +__u64 ptr; +__u64 size; +__u32 model; +#define INTEL_IOMMU (1 << 0) +#define ARM_SMMU (1 << 1)
Why are we using this as a bit field rather than an enum? Does it make sense for model to be (INTEL_IOMMU|ARM_SMMU)?
Quoted 6 lines
include/uapi/linux/iommu.h new file +6
-0+__u32 length; + __u8 opaque[]; +}; + + +#endif /* _UAPI_IOMMU_H */
Re: [RFC 1/9] iommu: Introduce bind_pasid_table API function
Quoted 164 lines · On Thu, 22 Jun 2017 16:52:01 -0600 Alex Williamson…
Quoted 156 lines · On Wed, 14 Jun 2017 15:22:55 -0700 Jacob Pan <[email protected]>…
Virtual IOMMU was proposed to support Shared Virtual Memory (SVM) use case in the guest:
https://lists.gnu.org/archive/html/qemu-devel/2016-11/msg05311.htmlAs part of the proposed architecture, when a SVM capable PCI device is assigned to a guest, nested mode is turned on. Guest owns the first level page tables (request with PASID) and performs
GVA->GPAtranslation. Second level page tables are owned by the host forGPA->HPAtranslation for both request with and without PASID.A new IOMMU driver interface is therefore needed to perform tasks as follows:
* Enable nested translation and appropriate translation type
* Assign guest PASID table pointer (in GPA) and size to host IOMMUThis patch introduces new functions called iommu_(un)bind_pasid_table() to IOMMU APIs. Architecture specific IOMMU function can be added later to perform the specific steps for binding pasid table of assigned devices.
This patch also adds model definition in
iommu.h. It would be used to check if the bind request is from a compatible entity. e.g. a bind request from anintel_iommuemulator may not be supported by an ARM SMMU driver.Signed-off-by Jacob Pan <[email protected]>Signed-off-by Liu, Yi L <[email protected]>Signed-off-by Ashok Raj <[email protected]>--- drivers/iommu/iommu.c | 19 +++++++++++++++++++ include/linux/iommu.h | 23 +++++++++++++++++++++++ include/uapi/linux/iommu.h | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+) create mode 100644 include/uapi/linux/iommu.hdrivers/iommu/iommu.c +17
-0@@ -1328,6 +1328,25 @@ int iommu_attach_device(struct iommu_domain *domain, struct device *dev) }EXPORT_SYMBOL_GPL (iommu_attach_device); +int iommu_bind_pasid_table (struct iommu_domain *domain,struct device *dev, +struct pasid_table_info *pasidt_binfo) +{ +if (unlikely (!domain->ops->bind_pasid_table)) +return -EINVAL ; + +return domain->ops->bind_pasid_table (domain, dev, pasidt_binfo); +} +EXPORT_SYMBOL_GPL (iommu_bind_pasid_table); + +int iommu_unbind_pasid_table (struct iommu_domain *domain,struct device *dev) +{ +if (unlikely (!domain->ops->unbind_pasid_table)) +return -EINVAL ; + +return domain->ops->unbind_pasid_table (domain, dev); +} +EXPORT_SYMBOL_GPL (iommu_unbind_pasid_table); +static void __iommu_detach_device (struct iommu_domain *domain,struct device *dev) {include/linux/iommu.h +3
-0@@ -25,6 +25,7 @@#include <linux/errno.h> #include <linux/err.h> #include <linux/of.h> +#include <uapi/linux/iommu.h> #define IOMMU_READ (1 << 0)#define IOMMU_WRITE (1 << 1)@@ -183,6 +184,8 @@ struct iommu_resv_region {* @domain_get_windows: Return the number of windows for a domain * @of_xlate: add OF master IDs to iommu grouping * @pgsize_bitmap: bitmap of all possible supported page sizes +* @bind_pasid_table: bind pasid table pointer for guest SVM +* @unbind_pasid_table: unbind pasid table pointer and restore defaults */
structiommu_ops{include/linux/iommu.h +4
-0bool (*capable)(enum iommu_cap );@@ -225,6 +228,10 @@ struct iommu_ops {u32 (*domain_get_windows)(struct iommu_domain *domain);int (*of_xlate)(struct device *dev,struct of_phandle_args *args); +int (*bind_pasid_table)(struct iommu_domain *domain,struct device *dev, +struct pasid_table_info *pasidt_binfo); +int (*unbind_pasid_table)(struct iommu_domain *domain, +struct device *dev);include/linux/iommu.h +3
-0unsigned long pgsize_bitmap; };@@ -282,6 +289,10 @@ extern int iommu_attach_device(struct iommu_domain *domain, struct device *dev);extern void iommu_detach_device (struct iommu_domain *domain,struct device *dev); +extern int iommu_bind_pasid_table (struct iommu_domain *domain, +struct device *dev,struct pasid_table_info *pasidt_binfo); +extern int iommu_unbind_pasid_table(structiommu_domain *domain, +struct device *dev); extern struct iommu_domain *iommu_get_domain_for_dev(struct device*dev); extern int
iommu_map(struct iommu_domain *domain, unsigned long iova, phys_addr_t paddr, size_t size, int prot);include/linux/iommu.h +11
-0@@ -637,6 +648,18 @@ const struct iommu_ops *iommu_ops_from_fwnode(struct fwnode_handle *fwnode) return NULL; } +static inline +int iommu_bind_pasid_table (struct iommu_domain *domain,struct device *dev, +struct pasid_table_info *pasidt_binfo) +{ +return -EINVAL ; +} +static inline +int iommu_unbind_pasid_table (struct iommu_domain *domain,struct device *dev) +{ +return -EINVAL ; +} +#endif /* CONFIG_IOMMU_API */ #endif /* __LINUX_IOMMU_H */ include/uapi/linux/iommu.h new file +5
-0@@ -0,0 +1,33 @@ +/* +* IOMMU user API definitions +* +* +* This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */include/uapi/linux/iommu.h new file +6
-0+ +#ifndef _UAPI_IOMMU_H +#define _UAPI_IOMMU_H + +/** +* PASID table data used to bind guest PASID table to the host Quoted 3 lines
IOMMU. This will
+ * enable guest managed first level page tables.
+ * @ptr PASID table pointer in GPAGPA? I'm confused how the host physical IOMMU needs the guest
Quoted 9 lines
physical address of the PASID table here. The first level translation does GVA to GPA lookup, but doesn't that translation still come from a table that the IOMMU references via a host physical address? If not, is the address space of this pointer implementation/architecture specific? In general I think it's good policy to not make the interface specific to the VM use case. After all, vfio is a userspace driver interface and device assignment is just one use case.
This is the case we have nested translation turned on. 2nd level does GPA-HPA translation therefore PASID table pointer is GPA. But I agree we don't have to restrict this to specific architecture. Perhaps just leave it as pointer, w/o mentioning GPA.
At IOMMU driver level, binding PASID table and nested translation should be generic. Perhaps at VFIO level, this can be further abstracted to encompass both VM and non-VM use cases?
Quoted 14 lines
Quoted 2 lines
+ * @size size of the guest PASID table, must be <= host
table sizePresumably in bytes, best to say so.
Quoted 2 lines
+ * @model magic number tells vendor apart
+ * @length length of the opaque dataAlso in bytes.
Quoted 1 line
+ * @opaque architecture specific IOMMU data
s/architecture/model/?
Quoted 11 lines
include/uapi/linux/iommu.h line 27
Quoted 7 lines
+ */
include/uapi/linux/iommu.h new file +6
-0+struct pasid_table_info { +__u64 ptr; +__u64 size; +__u32 model; +#define INTEL_IOMMU (1 << 0) +#define ARM_SMMU (1 << 1)Why are we using this as a bit field rather than an enum? Does it make sense for model to be (INTEL_IOMMU|ARM_SMMU)?
make sense.
Quoted 7 lines
include/uapi/linux/iommu.h line 33
Quoted 6 lines
include/uapi/linux/iommu.h new file +6
-0+__u32 length; + __u8 opaque[]; +}; + + +#endif /* _UAPI_IOMMU_H */
[Jacob Pan]
RFC 2/9 iommu/vt-d: add bind_pasid_table function
In reply to Jacob PanAdd Intel VT-d ops to the generic iommu_bind_pasid_table API functions.
The primary use case is for direct assignment of SVM capable device. Originated from emulated IOMMU in the guest, the request goes through many layers (e.g. VFIO). Upon calling host IOMMU driver, caller passes guest PASID table pointer (GPA) and size.
Device context table entry is modified by Intel IOMMU specific bind_pasid_table function. This will turn on nesting mode and matching translation type.
The unbind operation restores default context mapping.
--- drivers/iommu/intel-iommu.c | 109 ++++++++++++++++++++++++++++++++++++++++++ include/linux/dma_remapping.h | 1 + 2 files changed, 110 insertions(+)
drivers/iommu/intel-iommu.c +109 -0
@@ -5430,6 +5430,111 @@ struct intel_iommu *intel_svm_device_to_iommu(struct device *dev)return iommu; } + +static int intel_iommu_bind_pasid_table (struct iommu_domain *domain, +struct device *dev,struct pasid_table_info *pasidt_binfo) +{ +struct intel_iommu *iommu; +struct context_entry *context; +struct dmar_domain *dmar_domain =to_dmar_domain (domain); +struct device_domain_info *info; +struct pci_dev *pdev; +u8 bus, devfn; +u16 did, *sid; +int ret =0 ; +unsigned long flags; +u64 ctx_lo; + +if (pasidt_binfo ==NULL || pasidt_binfo->model !=INTEL_IOMMU ) { +pr_warn ("%s: Invalid bind request!\n" , __func__); +return -EINVAL ; + } + + iommu =device_to_iommu (dev, &bus, &devfn); +if (!iommu) +return -ENODEV ; + + sid = (u16 *)&pasidt_binfo->opaque; +/* +* check SID, if it is not correct, return success to allow looping +* through all devices within a group +*/ +if (PCI_DEVID (bus, devfn) != *sid) +return 0 ; + + pdev =to_pci_dev (dev); + info = dev->archdata.iommu; +if (!info || !info->pasid_supported) { +pr_err ("PCI %04x:%02x:%02x.%d: has no PASID support\n" , +pci_domain_nr (pdev->bus), bus,PCI_SLOT (devfn), +PCI_FUNC (devfn)); + ret = -EINVAL ; + goto out; + } + +if (pasidt_binfo->size >intel_iommu_get_pts (iommu)) { +pr_err ("Invalid gPASID table size %llu, host size %lu\n" , + pasidt_binfo->size, +intel_iommu_get_pts (iommu)); + ret = -EINVAL ; + goto out; + } +spin_lock_irqsave (&iommu->lock, flags); + context =iommu_context_addr (iommu, bus, devfn,0 ); +if (!context || !context_present (context)) { +pr_warn ("%s: ctx not present for bus devfn %x:%x\n" , + __func__, bus, devfn); +spin_unlock_irqrestore (&iommu->lock, flags); + goto out; + } +/* Anticipate guest to use SVM and owns the first level */ + ctx_lo = context[0 ].lo; + ctx_lo |=CONTEXT_NESTE ; + ctx_lo |=CONTEXT_PRS ; + ctx_lo |=CONTEXT_PASIDE ; + ctx_lo &= ~CONTEXT_TT_MASK ; + ctx_lo |=CONTEXT_TT_DEV_IOTLB <<2 ; + context[0 ].lo = ctx_lo; + +/* Assign guest PASID table pointer and size */ + ctx_lo = (pasidt_binfo->ptr &VTD_PAGE_MASK ) | pasidt_binfo->size; + context[1 ].lo = ctx_lo; +/* make sure context entry is updated before flushing */ +wmb (); + did = dmar_domain->iommu_did[iommu->seq_id]; + iommu->flush.flush_context (iommu, did, + (((u16 )bus) <<8 ) | devfn, +DMA_CCMD_MASK_NOBIT , +DMA_CCMD_DEVICE_INVL ); + iommu->flush.flush_iotlb (iommu, did,0 ,0 ,DMA_TLB_DSI_FLUSH ); +spin_unlock_irqrestore (&iommu->lock, flags); + + +out: +return ret; +} + +static int intel_iommu_unbind_pasid_table (struct iommu_domain *domain, +struct device *dev) +{ +struct intel_iommu *iommu; +struct dmar_domain *dmar_domain =to_dmar_domain (domain); +u8 bus, devfn; + + iommu =device_to_iommu (dev, &bus, &devfn); +if (!iommu) +return -ENODEV ; +/* +* REVISIT: we might want to clear the PASID table pointer +* as part of context clear operation. Currently, it leaves +* stale data but should be ignored by hardware since PASIDE +* is clear. +*/ +/* ATS will be reenabled when remapping is restored */ +pci_disable_ats (to_pci_dev (dev)); +domain_context_clear (iommu, dev); +return domain_context_mapping_one (dmar_domain, iommu, bus, devfn); +}#endif /* CONFIG_INTEL_IOMMU_SVM */ const struct iommu_ops intel_iommu_ops = {
@@ -5438,6 +5543,10 @@ const struct iommu_ops intel_iommu_ops = { .domain_free = intel_iommu_domain_free, .attach_dev = intel_iommu_attach_device, .detach_dev = intel_iommu_detach_device, +#ifdef CONFIG_INTEL_IOMMU_SVM + .bind_pasid_table = intel_iommu_bind_pasid_table, + .unbind_pasid_table = intel_iommu_unbind_pasid_table, +#endif .map = intel_iommu_map, .unmap = intel_iommu_unmap, .map_sg = default_iommu_map_sg,
include/linux/dma_remapping.h +1 -0
@@ -27,6 +27,7 @@#define CONTEXT_DINVE (1ULL << 8)#define CONTEXT_PRS (1ULL << 9) +#define CONTEXT_NESTE (1ULL << 10)#define CONTEXT_PASIDE (1ULL << 11)struct intel_iommu ;
Signature · 1 line
2.7.4
Re: [RFC 2/9] iommu/vt-d: add bind_pasid_table function
Quoted 36 lines · On Wed, 14 Jun 2017 15:22:56 -0700 Jacob Pan…
Add Intel VT-d ops to the generic
iommu_bind_pasid_tableAPI functions.The primary use case is for direct assignment of SVM capable device. Originated from emulated IOMMU in the guest, the request goes through many layers (e.g. VFIO). Upon calling host IOMMU driver, caller passes guest PASID table pointer (GPA) and size.
Device context table entry is modified by Intel IOMMU specific
bind_pasid_tablefunction. This will turn on nesting mode and matching translation type.The unbind operation restores default context mapping.
Signed-off-by Ashok Raj <[email protected]>--- drivers/iommu/intel-iommu.c | 109 ++++++++++++++++++++++++++++++++++++++++++ include/linux/dma_remapping.h | 1 + 2 files changed, 110 insertions(+)drivers/iommu/intel-iommu.c +6
-0@@ -5430,6 +5430,111 @@ struct intel_iommu *intel_svm_device_to_iommu(struct device *dev)return iommu; } + +static int intel_iommu_bind_pasid_table (struct iommu_domain *domain, +struct device *dev,struct pasid_table_info *pasidt_binfo) +{ +struct intel_iommu *iommu; +struct context_entry *context;
drivers/iommu/intel-iommu.c struct intel_iommu *intel_svm_device_to_iommu(struct device *dev) · line 5448
Quoted 10 lines
drivers/iommu/intel-iommu.c +10
-0+struct dmar_domain *dmar_domain =to_dmar_domain (domain); +struct device_domain_info *info; +struct pci_dev *pdev; +u8 bus, devfn; +u16 did, *sid; +int ret =0 ; +unsigned long flags; +u64 ctx_lo; + +if (pasidt_binfo ==NULL || pasidt_binfo->model !=INTEL_IOMMU ) {
Clearly model cannot be used as a bit field so it would appear wrong to limit ourselves to 32 possible models by using it that way.
drivers/iommu/intel-iommu.c struct intel_iommu *intel_svm_device_to_iommu(struct device *dev) · line 5449
Quoted 1 line
drivers/iommu/intel-iommu.c +1
-0+pr_warn ("%s: Invalid bind request!\n" , __func__);
Let the callers deal with error reporting.
drivers/iommu/intel-iommu.c struct intel_iommu *intel_svm_device_to_iommu(struct device *dev) · line 5457
Quoted 8 lines
drivers/iommu/intel-iommu.c +8
-0+return -EINVAL ; + } + + iommu =device_to_iommu (dev, &bus, &devfn); +if (!iommu) +return -ENODEV ; + + sid = (u16 *)&pasidt_binfo->opaque;
Failed to check length.
drivers/iommu/intel-iommu.c struct intel_iommu *intel_svm_device_to_iommu(struct device *dev) · line 5465
Quoted 8 lines
drivers/iommu/intel-iommu.c +8
-0+/* +* check SID, if it is not correct, return success to allow looping +* through all devices within a group +*/ +if (PCI_DEVID (bus, devfn) != *sid) +return 0 ; + + pdev =to_pci_dev (dev);
Better test dev_is_pci() first!
Quoted 6 lines
drivers/iommu/intel-iommu.c +6
-0+ info = dev->archdata.iommu; +if (!info || !info->pasid_supported) { +pr_err ("PCI %04x:%02x:%02x.%d: has no PASID support\n" , +pci_domain_nr (pdev->bus), bus,PCI_SLOT (devfn), +PCI_FUNC (devfn)); + ret = -EINVAL ;
drivers/iommu/intel-iommu.c struct intel_iommu *intel_svm_device_to_iommu(struct device *dev) · line 5481
Quoted 10 lines
drivers/iommu/intel-iommu.c +10
-0+ goto out; + } + +if (pasidt_binfo->size >intel_iommu_get_pts (iommu)) { +pr_err ("Invalid gPASID table size %llu, host size %lu\n" , + pasidt_binfo->size, +intel_iommu_get_pts (iommu)); + ret = -EINVAL ; + goto out; + }
Different errnos here would be more useful to code that handles the return than these pr_err()s.
drivers/iommu/intel-iommu.c struct intel_iommu *intel_svm_device_to_iommu(struct device *dev) · line 5488
Quoted 7 lines
drivers/iommu/intel-iommu.c +7
-0+spin_lock_irqsave (&iommu->lock, flags); + context =iommu_context_addr (iommu, bus, devfn,0 ); +if (!context || !context_present (context)) { +pr_warn ("%s: ctx not present for bus devfn %x:%x\n" , + __func__, bus, devfn); +spin_unlock_irqrestore (&iommu->lock, flags); + goto out;
Return success?!
Quoted 36 lines
drivers/iommu/intel-iommu.c +36
-0+ } +/* Anticipate guest to use SVM and owns the first level */ + ctx_lo = context[0 ].lo; + ctx_lo |=CONTEXT_NESTE ; + ctx_lo |=CONTEXT_PRS ; + ctx_lo |=CONTEXT_PASIDE ; + ctx_lo &= ~CONTEXT_TT_MASK ; + ctx_lo |=CONTEXT_TT_DEV_IOTLB <<2 ; + context[0 ].lo = ctx_lo; + +/* Assign guest PASID table pointer and size */ + ctx_lo = (pasidt_binfo->ptr &VTD_PAGE_MASK ) | pasidt_binfo->size; + context[1 ].lo = ctx_lo; +/* make sure context entry is updated before flushing */ +wmb (); + did = dmar_domain->iommu_did[iommu->seq_id]; + iommu->flush.flush_context (iommu, did, + (((u16 )bus) <<8 ) | devfn, +DMA_CCMD_MASK_NOBIT , +DMA_CCMD_DEVICE_INVL ); + iommu->flush.flush_iotlb (iommu, did,0 ,0 ,DMA_TLB_DSI_FLUSH ); +spin_unlock_irqrestore (&iommu->lock, flags); + + +out: +return ret; +} + +static int intel_iommu_unbind_pasid_table (struct iommu_domain *domain, +struct device *dev) +{ +struct intel_iommu *iommu; +struct dmar_domain *dmar_domain =to_dmar_domain (domain); +u8 bus, devfn; + + iommu =device_to_iommu (dev, &bus, &devfn);
drivers/iommu/intel-iommu.c struct intel_iommu *intel_svm_device_to_iommu(struct device *dev) · line 5534
Quoted 10 lines
drivers/iommu/intel-iommu.c +10
-0+if (!iommu) +return -ENODEV ; +/* +* REVISIT: we might want to clear the PASID table pointer +* as part of context clear operation. Currently, it leaves +* stale data but should be ignored by hardware since PASIDE +* is clear. +*/ +/* ATS will be reenabled when remapping is restored */ +pci_disable_ats (to_pci_dev (dev));
dev_is_pci()?
Quoted 29 lines
drivers/iommu/intel-iommu.c +7
-0+domain_context_clear (iommu, dev); +return domain_context_mapping_one (dmar_domain, iommu, bus, devfn); +}#endif /* CONFIG_INTEL_IOMMU_SVM */ const struct iommu_ops intel_iommu_ops = {@@ -5438,6 +5543,10 @@ const struct iommu_ops intel_iommu_ops = { .domain_free = intel_iommu_domain_free, .attach_dev = intel_iommu_attach_device, .detach_dev = intel_iommu_detach_device, +#ifdef CONFIG_INTEL_IOMMU_SVM + .bind_pasid_table = intel_iommu_bind_pasid_table, + .unbind_pasid_table = intel_iommu_unbind_pasid_table, +#endif .map = intel_iommu_map, .unmap = intel_iommu_unmap, .map_sg = default_iommu_map_sg,include/linux/dma_remapping.h +1
-0@@ -27,6 +27,7 @@#define CONTEXT_DINVE (1ULL << 8)#define CONTEXT_PRS (1ULL << 9) +#define CONTEXT_NESTE (1ULL << 10)#define CONTEXT_PASIDE (1ULL << 11)struct intel_iommu ;
Re: [RFC 2/9] iommu/vt-d: add bind_pasid_table function
Quoted 47 lines · On Thu, 22 Jun 2017 16:52:15 -0600 Alex Williamson…
Quoted 44 lines · On Wed, 14 Jun 2017 15:22:56 -0700 Jacob Pan <[email protected]>…
Add Intel VT-d ops to the generic
iommu_bind_pasid_tableAPI functions.The primary use case is for direct assignment of SVM capable device. Originated from emulated IOMMU in the guest, the request goes through many layers (e.g. VFIO). Upon calling host IOMMU driver, caller passes guest PASID table pointer (GPA) and size.
Device context table entry is modified by Intel IOMMU specific
bind_pasid_tablefunction. This will turn on nesting mode and matching translation type.The unbind operation restores default context mapping.
Signed-off-by Jacob Pan <[email protected]>Signed-off-by Liu, Yi L <[email protected]>Signed-off-by Ashok Raj <[email protected]>--- drivers/iommu/intel-iommu.c | 109++++++++++++++++++++++++++++++++++++++++++
include/linux/dma_remapping.h| 1 + 2 files changed, 110
insertions(+)(quoted diff) +0
-0
b/drivers/iommu/intel-iommu.cindex fc2765c..1d5d9ab9 100644drivers/iommu/intel-iommu.c +11
-0@@ -5430,6 +5430,111 @@ struct intel_iommu *intel_svm_device_to_iommu(struct device *dev)return iommu; } + +static int intel_iommu_bind_pasid_table (struct iommu_domain *domain, +struct device *dev,struct pasid_table_info *pasidt_binfo) +{ +struct intel_iommu *iommu; +struct context_entry *context; +struct dmar_domain *dmar_domain =to_dmar_domain (domain); +struct device_domain_info *info; +struct pci_dev *pdev; +u8 bus, devfn; +u16 did, *sid; +int ret =0 ;
Quoted 9 lines
drivers/iommu/intel-iommu.c struct intel_iommu *intel_svm_device_to_iommu(struct device *dev)
Quoted 5 lines
drivers/iommu/intel-iommu.c +4
-0+unsigned long flags; +u64 ctx_lo; + +if (pasidt_binfo ==NULL || pasidt_binfo->model !=INTEL_IOMMU ) {Clearly model cannot be used as a bit field so it would appear wrong to limit ourselves to 32 possible models by using it that way.
agreed.
Quoted 4 lines
drivers/iommu/intel-iommu.c struct intel_iommu *intel_svm_device_to_iommu(struct device *dev) · line 5449
Quoted 1 line
drivers/iommu/intel-iommu.c +1
-0+pr_warn ("%s: Invalid bind request!\n" , __func__);Let the callers deal with error reporting.
ditto.
Quoted 11 lines
drivers/iommu/intel-iommu.c struct intel_iommu *intel_svm_device_to_iommu(struct device *dev) · line 5457
Quoted 8 lines
drivers/iommu/intel-iommu.c +8
-0+return -EINVAL ; + } + + iommu =device_to_iommu (dev, &bus, &devfn); +if (!iommu) +return -ENODEV ; + + sid = (u16 *)&pasidt_binfo->opaque;Failed to check length.
ditto.
Quoted 13 lines
drivers/iommu/intel-iommu.c struct intel_iommu *intel_svm_device_to_iommu(struct device *dev) · line 5465
Quoted 10 lines
drivers/iommu/intel-iommu.c +2
-0+/* +* check SID, if it is not correct, return success to allow looping
drivers/iommu/intel-iommu.c +6
-0+* through all devices within a group +*/ +if (PCI_DEVID (bus, devfn) != *sid) +return 0 ; + + pdev =to_pci_dev (dev);Better test
dev_is_pci()first!
good point.
Quoted 14 lines
Quoted 14 lines
drivers/iommu/intel-iommu.c +3
-0+ info = dev->archdata.iommu; +if (!info || !info->pasid_supported) { + pr_err("PCI %04x:%02x:%02x.%d: has no PASIDsupport\n",
drivers/iommu/intel-iommu.c +1
-0+ pci_domain_nr(pdev->bus), bus,
PCI_SLOT(devfn),drivers/iommu/intel-iommu.c +7
-0+ PCI_FUNC(devfn)); + ret = -EINVAL ; + goto out; + } + +if (pasidt_binfo->size >intel_iommu_get_pts (iommu)) { + pr_err("Invalid gPASID table size %llu, host size%lu\n",
Quoted 9 lines
drivers/iommu/intel-iommu.c struct intel_iommu *intel_svm_device_to_iommu(struct device *dev) · line 5481
Quoted 5 lines
drivers/iommu/intel-iommu.c +5
-0+ pasidt_binfo->size, + intel_iommu_get_pts(iommu)); + ret = -EINVAL ; + goto out; + }Different errnos here would be more useful to code that handles the return than these pr_err()s.
OK.
Quoted 11 lines
drivers/iommu/intel-iommu.c struct intel_iommu *intel_svm_device_to_iommu(struct device *dev) · line 5488
Quoted 8 lines
drivers/iommu/intel-iommu.c +4
-0+spin_lock_irqsave (&iommu->lock, flags); + context =iommu_context_addr (iommu, bus, devfn,0 ); +if (!context || !context_present (context)) { + pr_warn("%s: ctx not present for bus devfn%x:%x\n",
drivers/iommu/intel-iommu.c +3
-0+ __func__, bus, devfn); +spin_unlock_irqrestore (&iommu->lock, flags); + goto out;Return success?!
good catch, should fail.
Quoted 44 lines
Quoted 44 lines
drivers/iommu/intel-iommu.c +12
-0+ } +/* Anticipate guest to use SVM and owns the first level */ + ctx_lo = context[0 ].lo; + ctx_lo |=CONTEXT_NESTE ; + ctx_lo |=CONTEXT_PRS ; + ctx_lo |=CONTEXT_PASIDE ; + ctx_lo &= ~CONTEXT_TT_MASK ; + ctx_lo |=CONTEXT_TT_DEV_IOTLB <<2 ; + context[0 ].lo = ctx_lo; + +/* Assign guest PASID table pointer and size */ + ctx_lo = (pasidt_binfo->ptr & VTD_PAGE_MASK) |
pasidt_binfo->size;drivers/iommu/intel-iommu.c +9
-0+ context[1 ].lo = ctx_lo; +/* make sure context entry is updated before flushing */ +wmb (); + did = dmar_domain->iommu_did[iommu->seq_id]; + iommu->flush.flush_context (iommu, did, + (((u16 )bus) <<8 ) | devfn, +DMA_CCMD_MASK_NOBIT , +DMA_CCMD_DEVICE_INVL ); + iommu->flush.flush_iotlb(iommu, did, 0, 0,
DMA_TLB_DSI_FLUSH);drivers/iommu/intel-iommu.c +8
-0+spin_unlock_irqrestore (&iommu->lock, flags); + + +out: +return ret; +} + +static int intel_iommu_unbind_pasid_table(struct iommu_domain*domain,
drivers/iommu/intel-iommu.c +12
-0+ struct device *dev) +{ +struct intel_iommu *iommu; +struct dmar_domain *dmar_domain =to_dmar_domain (domain); +u8 bus, devfn; + + iommu =device_to_iommu (dev, &bus, &devfn); +if (!iommu) +return -ENODEV ; +/* +* REVISIT: we might want to clear the PASID table pointer +* as part of context clear operation. Currently, it leaves
Quoted 9 lines
drivers/iommu/intel-iommu.c struct intel_iommu *intel_svm_device_to_iommu(struct device *dev) · line 5534
Quoted 6 lines
drivers/iommu/intel-iommu.c +1
-0+* stale data but should be ignored by hardware since PASIDE
drivers/iommu/intel-iommu.c +4
-0+* is clear. +*/ +/* ATS will be reenabled when remapping is restored */ +pci_disable_ats (to_pci_dev (dev));
dev_is_pci()?
good to check, even thought intel iommu supports PCI only.
Quoted 29 lines
Quoted 29 lines
drivers/iommu/intel-iommu.c +2
-0+domain_context_clear (iommu, dev); + return domain_context_mapping_one(dmar_domain, iommu, bus,devfn); +}
#endif /*CONFIG_INTEL_IOMMU_SVM*/const struct
iommu_opsintel_iommu_ops= {drivers/iommu/intel-iommu.c +3
-0@@ -5438,6 +5543,10 @@ const struct iommu_ops intel_iommu_ops = { .domain_free = intel_iommu_domain_free, .attach_dev = intel_iommu_attach_device, .detach_dev = intel_iommu_detach_device, +#ifdef CONFIG_INTEL_IOMMU_SVM + .bind_pasid_table = intel_iommu_bind_pasid_table, + .unbind_pasid_table =
intel_iommu_unbind_pasid_table, +#endifdrivers/iommu/intel-iommu.c +0
-0.map = intel_iommu_map, .unmap = intel_iommu_unmap, .map_sg = default_iommu_map_sg,(quoted diff) +0
-0
b/include/linux/dma_remapping.hindex 9088407..85367b7 100644include/linux/dma_remapping.h +1
-0@@ -27,6 +27,7 @@#define CONTEXT_DINVE (1ULL << 8)#define CONTEXT_PRS (1ULL << 9) +#define CONTEXT_NESTE (1ULL << 10)#define CONTEXT_PASIDE (1ULL << 11)struct intel_iommu ;
Re: [RFC 2/9] iommu/vt-d: add bind_pasid_table function
Quoted 28 lines · On Fri, 23 Jun 2017 11:19:52 -0700 Jacob Pan…
Quoted 16 lines · On Thu, 22 Jun 2017 16:52:15 -0600 Alex Williamson…
Quoted 14 lines · On Wed, 14 Jun 2017 15:22:56 -0700 Jacob Pan…
+static int intel_iommu_unbind_pasid_table(struct
iommu_domain*domain,drivers/iommu/intel-iommu.c +12
-0+ struct device *dev) +{ +struct intel_iommu *iommu; +struct dmar_domain *dmar_domain =to_dmar_domain (domain); +u8 bus, devfn; + + iommu =device_to_iommu (dev, &bus, &devfn); +if (!iommu) +return -ENODEV ; +/* +* REVISIT: we might want to clear the PASID table pointer +* as part of context clear operation. Currently, it leaves Quoted 9 lines
drivers/iommu/intel-iommu.c struct intel_iommu *intel_svm_device_to_iommu(struct device *dev) · line 5534
Quoted 6 lines
(quoted diff) +1
-0+ * stale data but should be ignored by hardware sincePASIDE
drivers/iommu/intel-iommu.c +4
-0+* is clear. +*/ +/* ATS will be reenabled when remapping is restored */ +pci_disable_ats (to_pci_dev (dev));
dev_is_pci()?
Quoted 1 line
good to check, even thought intel iommu supports PCI only.
That's not true, intel-iommu supports non-PCI devices defined in ACPI as well. Thanks,
Alex
Re: [RFC 2/9] iommu/vt-d: add bind_pasid_table function
Quoted 34 lines · On Fri, 23 Jun 2017 12:59:00 -0600 Alex Williamson…
Quoted 31 lines · On Fri, 23 Jun 2017 11:19:52 -0700 Jacob Pan <[email protected]>…
Quoted 28 lines · On Thu, 22 Jun 2017 16:52:15 -0600 Alex Williamson…
Quoted 19 lines · On Wed, 14 Jun 2017 15:22:56 -0700 Jacob Pan <[email protected]>…
+static int intel_iommu_unbind_pasid_table(struct
iommu_domain*domain,drivers/iommu/intel-iommu.c +4
-0+ struct device *dev) +{ +struct intel_iommu *iommu; + struct dmar_domain *dmar_domain =
to_dmar_domain(domain);drivers/iommu/intel-iommu.c +7
-0+u8 bus, devfn; + + iommu =device_to_iommu (dev, &bus, &devfn); +if (!iommu) +return -ENODEV ; +/* +* REVISIT: we might want to clear the PASID table pointer
(quoted diff) +1
-0+ * as part of context clear operation. Currently, itleaves
(quoted diff) +1
-0+ * stale data but should be ignored by hardware sincePASIDE
drivers/iommu/intel-iommu.c struct intel_iommu *intel_svm_device_to_iommu(struct device *dev) · line 5534
Quoted 4 lines
drivers/iommu/intel-iommu.c +4
-0+* is clear. +*/ +/* ATS will be reenabled when remapping is restored */ +pci_disable_ats (to_pci_dev (dev));
dev_is_pci()?
Quoted 5 lines
Quoted 1 line
good to check, even thought intel iommu supports PCI only.
That's not true, intel-iommu supports non-PCI devices defined in ACPI as well. Thanks,
For non-pci device, there is still a pci BDF allocated for it (shown in ACPI) such that it can have its own IOMMU context, right? e.g. HPET
Quoted 1 line
Alex
[Jacob Pan]
RFC 3/9 iommu: Introduce iommu do invalidate API function
In reply to Jacob PanFrom: "Liu, Yi L" <[email protected]>
When a SVM capable device is assigned to a guest, the first level page tables are owned by the guest and the guest PASID table pointer is linked to the device context entry of the physical IOMMU.
Host IOMMU driver has no knowledge of caching structure updates unless the guest invalidation activities are passed down to the host. The primary usage is derived from emulated IOMMU in the guest, where QEMU can trap invalidation activities before pass them down the host/physical IOMMU. There are IOMMU architectural specific actions need to be taken which requires the generic APIs introduced in this patch to have opaque data in the tlb_invalidate_info argument.
--- drivers/iommu/iommu.c | 13 +++++++++++++ include/linux/iommu.h | 11 +++++++++++ include/uapi/linux/iommu.h | 4 ++++ 3 files changed, 28 insertions(+)
drivers/iommu/iommu.c +13 -0
@@ -1347,6 +1347,19 @@ int iommu_unbind_pasid_table(struct iommu_domain *domain, struct device *dev) }EXPORT_SYMBOL_GPL (iommu_unbind_pasid_table); +int iommu_do_invalidate (struct iommu_domain *domain, +struct device *dev,struct tlb_invalidate_info *inv_info) +{ +int ret =0 ; + +if (unlikely (domain->ops->do_invalidate ==NULL )) +return -ENODEV ; + + ret = domain->ops->do_invalidate (domain, dev, inv_info); +return ret; +} +EXPORT_SYMBOL_GPL (iommu_do_invalidate); +static void __iommu_detach_device (struct iommu_domain *domain,struct device *dev) {
include/linux/iommu.h +11 -0
@@ -232,6 +232,8 @@ struct iommu_ops { struct pasid_table_info *pasidt_binfo);int (*unbind_pasid_table)(struct iommu_domain *domain,struct device *dev); +int (*do_invalidate)(struct iommu_domain *domain, +struct device *dev,struct tlb_invalidate_info *inv_info);unsigned long pgsize_bitmap; };
@@ -293,6 +295,9 @@ extern int iommu_bind_pasid_table(struct iommu_domain *domain, struct device *dev, struct pasid_table_info *pasidt_binfo);extern int iommu_unbind_pasid_table (struct iommu_domain *domain,struct device *dev); +extern int iommu_do_invalidate (struct iommu_domain *domain, +struct device *dev,struct tlb_invalidate_info *inv_info); +extern struct iommu_domain *iommu_get_domain_for_dev (struct device *dev);extern int iommu_map (struct iommu_domain *domain,unsigned long iova,phys_addr_t paddr,size_t size,int prot);
@@ -660,6 +665,12 @@ int iommu_unbind_pasid_table(struct iommu_domain *domain, struct device *dev)return -EINVAL ; } +static inline int iommu_do_invalidate (struct iommu_domain *domain, +struct device *dev,struct tlb_invalidate_info *inv_info) +{ +return -EINVAL ; +} +#endif /* CONFIG_IOMMU_API */ #endif /* __LINUX_IOMMU_H */
include/uapi/linux/iommu.h +4 -0
@@ -29,5 +29,9 @@ struct pasid_table_info { __u8 opaque[]; }; +struct tlb_invalidate_info { +__u32 model; +__u8 opaque[]; +};#endif /* _UAPI_IOMMU_H */
Signature · 1 line
2.7.4
Re: [RFC 3/9] iommu: Introduce iommu do invalidate API function
Quoted 33 lines · On Wed, 14 Jun 2017 15:22:57 -0700 Jacob Pan…
From: "Liu, Yi L" <yi.l.liu-VuQAYsv1563Yd54FQh9/[email protected]>
When a SVM capable device is assigned to a guest, the first level page tables are owned by the guest and the guest PASID table pointer is linked to the device context entry of the physical IOMMU.
Host IOMMU driver has no knowledge of caching structure updates unless the guest invalidation activities are passed down to the host. The primary usage is derived from emulated IOMMU in the guest, where QEMU can trap invalidation activities before pass them down the host/physical IOMMU. There are IOMMU architectural specific actions need to be taken which requires the generic APIs introduced in this patch to have opaque data in the
tlb_invalidate_infoargument.Signed-off-by Ashok Raj <[email protected]>--- drivers/iommu/iommu.c | 13 +++++++++++++ include/linux/iommu.h | 11 +++++++++++ include/uapi/linux/iommu.h | 4 ++++ 3 files changed, 28 insertions(+)drivers/iommu/iommu.c +2
-0@@ -1347,6 +1347,19 @@ int iommu_unbind_pasid_table(struct iommu_domain *domain, struct device *dev) }EXPORT_SYMBOL_GPL (iommu_unbind_pasid_table); +int iommu_do_invalidate (struct iommu_domain *domain, +struct device *dev,struct tlb_invalidate_info *inv_info)
drivers/iommu/iommu.c int iommu_unbind_pasid_table(struct iommu_domain *domain, struct device *dev) · line 1361
Quoted 10 lines
drivers/iommu/iommu.c +10
-0+{ +int ret =0 ; + +if (unlikely (domain->ops->do_invalidate ==NULL )) +return -ENODEV ; + + ret = domain->ops->do_invalidate (domain, dev, inv_info); +return ret; +} +EXPORT_SYMBOL_GPL (iommu_do_invalidate);
s/do_invalidate/invalidate/? 'do_invalidate' seems like something 'invalidate' might call, not the exported interface. Invalidate what? Is that entirely buried in the opaque data? It seems like there are plenty of things to be invalidated that could be common.
Quoted 45 lines
drivers/iommu/iommu.c +1
-0+static void __iommu_detach_device (struct iommu_domain *domain,struct device *dev) {include/linux/iommu.h +11
-0@@ -232,6 +232,8 @@ struct iommu_ops { struct pasid_table_info *pasidt_binfo);int (*unbind_pasid_table)(struct iommu_domain *domain,struct device *dev); +int (*do_invalidate)(struct iommu_domain *domain, +struct device *dev,struct tlb_invalidate_info *inv_info);unsigned long pgsize_bitmap; };@@ -293,6 +295,9 @@ extern int iommu_bind_pasid_table(struct iommu_domain *domain, struct device *dev, struct pasid_table_info *pasidt_binfo);extern int iommu_unbind_pasid_table (struct iommu_domain *domain,struct device *dev); +extern int iommu_do_invalidate (struct iommu_domain *domain, +struct device *dev,struct tlb_invalidate_info *inv_info); +extern struct iommu_domain *iommu_get_domain_for_dev (struct device *dev);extern int iommu_map (struct iommu_domain *domain,unsigned long iova,phys_addr_t paddr,size_t size,int prot);@@ -660,6 +665,12 @@ int iommu_unbind_pasid_table(struct iommu_domain *domain, struct device *dev)return -EINVAL ; } +static inline int iommu_do_invalidate (struct iommu_domain *domain, +struct device *dev,struct tlb_invalidate_info *inv_info) +{ +return -EINVAL ; +} +#endif /* CONFIG_IOMMU_API */ #endif /* __LINUX_IOMMU_H */ include/uapi/linux/iommu.h +0
-0
include/uapi/linux/iommu.h struct pasid_table_info { · line 34
Quoted 6 lines
include/uapi/linux/iommu.h +3
-0@@ -29,5 +29,9 @@ struct pasid_table_info {__u8 opaque[]; }; +struct tlb_invalidate_info { +__u32 model; +__u8 opaque[];
Why did we specify a length (and then not use it anyway) of opaque data field in pasid_table_info but not here?
Quoted 3 lines
include/uapi/linux/iommu.h +1
-0+};#endif /* _UAPI_IOMMU_H */
RFC 4/9 iommu/vt-d: Add iommu do invalidate function
In reply to Jacob PanThis patch adds Intel VT-d specific function to implement iommu_do_invalidate API.
The use case is for supporting caching structure invalidation of assigned SVM capable devices. Emulated IOMMU exposes queue invalidation capability and passes down all descriptors from the guest to the physical IOMMU.
The assumption is that guest to host device ID mapping should be resolved prior to calling IOMMU driver. Based on the device handle, host IOMMU driver can replace certain fields before submit to the invalidation queue.
--- drivers/iommu/intel-iommu.c | 41 +++++++++++++++++++++++++++++++++++++++++ include/linux/intel-iommu.h | 11 ++++++++++- 2 files changed, 51 insertions(+), 1 deletion(-)
drivers/iommu/intel-iommu.c +41 -0
@@ -5127,6 +5127,46 @@ static void intel_iommu_detach_device(struct iommu_domain *domain,dmar_remove_one_dev_info (to_dmar_domain (domain), dev); } +static int intel_iommu_do_invalidate (struct iommu_domain *domain, +struct device *dev,struct tlb_invalidate_info *inv_info) +{ +struct intel_iommu *iommu; +struct dmar_domain *dmar_domain =to_dmar_domain (domain); +struct intel_invalidate_data *inv_data; +struct qi_desc *qi; +u16 did; +u8 bus, devfn; + +if (!inv_info || !dmar_domain || (inv_info->model !=INTEL_IOMMU )) +return -EINVAL ; + + iommu =device_to_iommu (dev, &bus, &devfn); +if (!iommu) +return -ENODEV ; + + inv_data = (struct intel_invalidate_data *)&inv_info->opaque; + +/* check SID */ +if (PCI_DEVID (bus, devfn) != inv_data->sid) +return 0 ; + + qi = &inv_data->inv_desc; + +switch (qi->low &QI_TYPE_MASK ) { +case QI_DIOTLB_TYPE : +case QI_DEIOTLB_TYPE : +/* for device IOTLB, we just let it pass through */ +break ; +default : + did = dmar_domain->iommu_did[iommu->seq_id]; + qi->low &= ~QI_DID_MASK ; + qi->low |=QI_DID (did); +break ; + } + +return qi_submit_sync (qi, iommu); +} +static int intel_iommu_map (struct iommu_domain *domain,unsigned long iova,phys_addr_t hpa,size_t size,int iommu_prot)
@@ -5546,6 +5586,7 @@ const struct iommu_ops intel_iommu_ops = {#ifdef CONFIG_INTEL_IOMMU_SVM .bind_pasid_table = intel_iommu_bind_pasid_table, .unbind_pasid_table = intel_iommu_unbind_pasid_table, + .do_invalidate = intel_iommu_do_invalidate, #endif .map = intel_iommu_map, .unmap = intel_iommu_unmap,
include/linux/intel-iommu.h +10 -1
@@ -31,7 +31,6 @@#include <linux/list.h> #include <linux/iommu.h> #include <linux/io-64-nonatomic-lo-hi.h> -#include <asm/cacheflush.h> #include <asm/iommu.h>
@@ -258,6 +257,10 @@ enum {#define QI_PGRP_RESP_TYPE 0x9#define QI_PSTRM_RESP_TYPE 0xa +#define QI_DID (did) (((u64)did & 0xffff) << 16) +#define QI_DID_MASK GENMASK(31, 16) +#define QI_TYPE_MASK GENMASK(3, 0) +#define QI_IEC_SELECTIVE (((u64)1) << 4)#define QI_IEC_IIDEX (idx) (((u64)(idx & 0xffff) << 32))#define QI_IEC_IM (m) (((u64)(m & 0x1f) << 27))
@@ -489,6 +492,12 @@ extern int intel_iommu_enable_pasid(struct intel_iommu *iommu, struct intel_svm_extern struct intel_iommu *intel_svm_device_to_iommu (struct device *dev);#endif +struct intel_invalidate_data { +u16 sid; +u32 pasid; +struct qi_desc inv_desc; +}; +extern const struct attribute_group *intel_iommu_groups[];#endif
Signature · 1 line
2.7.4
Re: [RFC 4/9] iommu/vt-d: Add iommu do invalidate function
Quoted 39 lines · On Wed, 14 Jun 2017 15:22:58 -0700 Jacob Pan…
This patch adds Intel VT-d specific function to implement
iommu_do_invalidateAPI.The use case is for supporting caching structure invalidation of assigned SVM capable devices. Emulated IOMMU exposes queue invalidation capability and passes down all descriptors from the guest to the physical IOMMU.
The assumption is that guest to host device ID mapping should be resolved prior to calling IOMMU driver. Based on the device handle, host IOMMU driver can replace certain fields before submit to the invalidation queue.
Signed-off-by Ashok Raj <[email protected]>--- drivers/iommu/intel-iommu.c | 41 +++++++++++++++++++++++++++++++++++++++++ include/linux/intel-iommu.h | 11 ++++++++++- 2 files changed, 51 insertions(+), 1 deletion(-)drivers/iommu/intel-iommu.c +10
-0@@ -5127,6 +5127,46 @@ static void intel_iommu_detach_device(struct iommu_domain *domain,dmar_remove_one_dev_info (to_dmar_domain (domain), dev); } +static int intel_iommu_do_invalidate (struct iommu_domain *domain, +struct device *dev,struct tlb_invalidate_info *inv_info) +{ +struct intel_iommu *iommu; +struct dmar_domain *dmar_domain =to_dmar_domain (domain); +struct intel_invalidate_data *inv_data; +struct qi_desc *qi; +u16 did; +u8 bus, devfn; +
drivers/iommu/intel-iommu.c static void intel_iommu_detach_device(struct iommu_domain *domain, · line 5149
Quoted 10 lines
drivers/iommu/intel-iommu.c +10
-0+if (!inv_info || !dmar_domain || (inv_info->model !=INTEL_IOMMU )) +return -EINVAL ; + + iommu =device_to_iommu (dev, &bus, &devfn); +if (!iommu) +return -ENODEV ; + + inv_data = (struct intel_invalidate_data *)&inv_info->opaque; + +/* check SID */
dev_is_pci()!
Quoted 55 lines
drivers/iommu/intel-iommu.c +21
-0+if (PCI_DEVID (bus, devfn) != inv_data->sid) +return 0 ; + + qi = &inv_data->inv_desc; + +switch (qi->low &QI_TYPE_MASK ) { +case QI_DIOTLB_TYPE : +case QI_DEIOTLB_TYPE : +/* for device IOTLB, we just let it pass through */ +break ; +default : + did = dmar_domain->iommu_did[iommu->seq_id]; + qi->low &= ~QI_DID_MASK ; + qi->low |=QI_DID (did); +break ; + } + +return qi_submit_sync (qi, iommu); +} +static int intel_iommu_map (struct iommu_domain *domain,unsigned long iova,phys_addr_t hpa,size_t size,int iommu_prot)@@ -5546,6 +5586,7 @@ const struct iommu_ops intel_iommu_ops = {#ifdef CONFIG_INTEL_IOMMU_SVM .bind_pasid_table = intel_iommu_bind_pasid_table, .unbind_pasid_table = intel_iommu_unbind_pasid_table, + .do_invalidate = intel_iommu_do_invalidate, #endif .map = intel_iommu_map, .unmap = intel_iommu_unmap,include/linux/intel-iommu.h +4
-1@@ -31,7 +31,6 @@#include <linux/list.h> #include <linux/iommu.h> #include <linux/io-64-nonatomic-lo-hi.h> -#include <asm/cacheflush.h> #include <asm/iommu.h> @@ -258,6 +257,10 @@ enum {#define QI_PGRP_RESP_TYPE 0x9#define QI_PSTRM_RESP_TYPE 0xa +#define QI_DID (did) (((u64)did & 0xffff) << 16) +#define QI_DID_MASK GENMASK(31, 16) +#define QI_TYPE_MASK GENMASK(3, 0) +#define QI_IEC_SELECTIVE (((u64)1) << 4)#define QI_IEC_IIDEX (idx) (((u64)(idx & 0xffff) << 32))#define QI_IEC_IM (m) (((u64)(m & 0x1f) << 27))
include/linux/intel-iommu.h extern int intel_iommu_enable_pasid(struct intel_iommu *iommu, struct intel_svm_ · line 500
Quoted 9 lines
include/linux/intel-iommu.h +6
-0@@ -489,6 +492,12 @@ extern int intel_iommu_enable_pasid(struct intel_iommu *iommu, struct intel_svm_extern struct intel_iommu *intel_svm_device_to_iommu (struct device *dev);#endif +struct intel_invalidate_data { +u16 sid; +u32 pasid; +struct qi_desc inv_desc; +}; +
If userspace is ever going to construct this to pass it through vfio, it'll need to be defined in UAPI.
Quoted 3 lines
include/linux/intel-iommu.h +0
-0extern const struct attribute_group *intel_iommu_groups[];#endif
RFC 5/9 iommu: Introduce fault notifier API
In reply to Jacob PanTraditionally, device specific faults are detected and handled within their own device drivers. When IOMMU is enabled, faults such as DMA related transactions are detected by IOMMU. There is no generic reporting mechanism to report faults back to the in-kernel device driver or the guest OS in case of assigned devices.
Faults detected by IOMMU is based on the transaction's source ID which can be reported at per device basis, regardless of the device type is a PCI device or not.
The fault types includes recoverable (e.g. page request) and unrecoverable faults(e.g. invalid context). In most cases, faults can be handled by IOMMU drivers. However, there are use cases that require fault processing outside IOMMU driver, e.g.
1. page request fault originated from an SVM capable device that is assigned to guest via vIOMMU. In this case, the first level page tables are owned by the guest. Page request must be propagated to the guest to let guest OS fault in the pages then send page response. In this mechanism, the direct receiver of IOMMU fault notification is VFIO, which can relay notification events to QEMU or other user space software.
2. faults need more subtle handling by device drivers. Other than simply invoke reset function, there are needs to let device driver handle the fault with a smaller impact.
This patchset is intended to create a generic fault notification API such that it can scale as follows:
- all IOMMU types
- PCI and non-PCI devices
- recoverable and unrecoverable faults
- VFIO and other other in kernel users
- DMA & IRQ remapping (TBD)
The event data contains both generic and raw architectural data such that performance is not compromised as the data propagation may involve many layers.
--- drivers/iommu/iommu.c | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/iommu.h | 54 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 117 insertions(+)
drivers/iommu/iommu.c +63 -0
@@ -48,6 +48,7 @@ struct iommu_group {struct list_head devices;struct mutex mutex;struct blocking_notifier_head notifier; +struct blocking_notifier_head fault_notifier;void *iommu_data;void (*iommu_data_release)(void *iommu_data);char *name;
@@ -345,6 +346,7 @@ struct iommu_group *iommu_group_alloc(void)mutex_init (&group->mutex);INIT_LIST_HEAD (&group->devices);BLOCKING_INIT_NOTIFIER_HEAD (&group->notifier); +BLOCKING_INIT_NOTIFIER_HEAD (&group->fault_notifier); ret =ida_simple_get (&iommu_group_ida,0 ,0 ,GFP_KERNEL );if (ret <0 ) {
@@ -790,6 +792,67 @@ int iommu_group_unregister_notifier(struct iommu_group *group,EXPORT_SYMBOL_GPL (iommu_group_unregister_notifier);/** +* iommu_register_fault_notifier - Register a notifier for fault reporting +* @dev: device to notify fault events +* @nb: notifier block to signal +* +*/ +int iommu_register_fault_notifier (struct device *dev, +struct notifier_block *nb) +{ +int ret; +struct iommu_group *group =iommu_group_get (dev); + +if (!group) +return -EINVAL ; + + ret =blocking_notifier_chain_register (&group->fault_notifier, nb); +iommu_group_put (group); + +return ret; + +} +EXPORT_SYMBOL_GPL (iommu_register_fault_notifier); + +/** +* iommu_unregister_fault_notifier - Unregister a notifier for fault reporting +* @domain: the domain to watch +* @nb: notifier block to signal +* +*/ +int iommu_unregister_fault_notifier (struct device *dev, +struct notifier_block *nb) +{ +int ret; +struct iommu_group *group =iommu_group_get (dev); + +if (!group) +return -EINVAL ; + + ret =blocking_notifier_chain_unregister (&group->fault_notifier, nb); +iommu_group_put (group); + +return ret; +} +EXPORT_SYMBOL_GPL (iommu_unregister_fault_notifier); + +int iommu_fault_notifier_call_chain (struct iommu_fault_event *event) +{ +int ret; +struct iommu_group *group =iommu_group_get (event->dev); + +if (!group) +return -EINVAL ; +/* caller provide generic data related to the event, TBD */ + ret = (blocking_notifier_call_chain (&group->fault_notifier,0 , (void *)event) + ==NOTIFY_BAD ) ? -EINVAL :0 ; +iommu_group_put (group); + +return ret; +} +EXPORT_SYMBOL (iommu_fault_notifier_call_chain); + +/** * iommu_group_id - Return ID for a group * @group: the group to ID *
include/linux/iommu.h +54 -0
78 lines not on this page · raw message
Signature · 1 line
2.7.4
Re: [RFC 5/9] iommu: Introduce fault notifier API
Quoted 103 lines · On Wed, 14 Jun 2017 15:22:59 -0700 Jacob Pan…
Traditionally, device specific faults are detected and handled within their own device drivers. When IOMMU is enabled, faults such as DMA related transactions are detected by IOMMU. There is no generic reporting mechanism to report faults back to the in-kernel device driver or the guest OS in case of assigned devices.
Faults detected by IOMMU is based on the transaction's source ID which can be reported at per device basis, regardless of the device type is a PCI device or not.
The fault types includes recoverable (e.g. page request) and unrecoverable faults(e.g. invalid context). In most cases, faults can be handled by IOMMU drivers. However, there are use cases that require fault processing outside IOMMU driver, e.g.
1. page request fault originated from an SVM capable device that is assigned to guest via vIOMMU. In this case, the first level page tables are owned by the guest. Page request must be propagated to the guest to let guest OS fault in the pages then send page response. In this mechanism, the direct receiver of IOMMU fault notification is VFIO, which can relay notification events to QEMU or other user space software.
2. faults need more subtle handling by device drivers. Other than simply invoke reset function, there are needs to let device driver handle the fault with a smaller impact.
This patchset is intended to create a generic fault notification API such that it can scale as follows:
- all IOMMU types
- PCI and non-PCI devices
- recoverable and unrecoverable faults
- VFIO and other other in kernel users
- DMA & IRQ remapping (TBD)The event data contains both generic and raw architectural data such that performance is not compromised as the data propagation may involve many layers.
Signed-off-by Ashok Raj <[email protected]>--- drivers/iommu/iommu.c | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/iommu.h | 54 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 117 insertions(+)drivers/iommu/iommu.c +35
-0@@ -48,6 +48,7 @@ struct iommu_group {struct list_head devices;struct mutex mutex;struct blocking_notifier_head notifier; +struct blocking_notifier_head fault_notifier;void *iommu_data;void (*iommu_data_release)(void *iommu_data);char *name;@@ -345,6 +346,7 @@ struct iommu_group *iommu_group_alloc(void)mutex_init (&group->mutex);INIT_LIST_HEAD (&group->devices);BLOCKING_INIT_NOTIFIER_HEAD (&group->notifier); +BLOCKING_INIT_NOTIFIER_HEAD (&group->fault_notifier); ret =ida_simple_get (&iommu_group_ida,0 ,0 ,GFP_KERNEL );if (ret <0 ) {@@ -790,6 +792,67 @@ int iommu_group_unregister_notifier(struct iommu_group *group,EXPORT_SYMBOL_GPL (iommu_group_unregister_notifier);/** +* iommu_register_fault_notifier - Register a notifier for fault reporting +* @dev: device to notify fault events +* @nb: notifier block to signal +* +*/ +int iommu_register_fault_notifier (struct device *dev, +struct notifier_block *nb) +{ +int ret; +struct iommu_group *group =iommu_group_get (dev); + +if (!group) +return -EINVAL ; + + ret =blocking_notifier_chain_register (&group->fault_notifier, nb); +iommu_group_put (group); + +return ret; + +} +EXPORT_SYMBOL_GPL (iommu_register_fault_notifier); + +/** +* iommu_unregister_fault_notifier - Unregister a notifier for fault reporting +* @domain: the domain to watch +* @nb: notifier block to signal +* +*/ +int iommu_unregister_fault_notifier (struct device *dev, +struct notifier_block *nb) +{ +int ret; +struct iommu_group *group =iommu_group_get (dev);
drivers/iommu/iommu.c int iommu_group_unregister_notifier(struct iommu_group *group, · line 837
Quoted 10 lines
drivers/iommu/iommu.c +10
-0+ +if (!group) +return -EINVAL ; + + ret =blocking_notifier_chain_unregister (&group->fault_notifier, nb); +iommu_group_put (group); + +return ret; +} +EXPORT_SYMBOL_GPL (iommu_unregister_fault_notifier);
If the call chains are on the group, why do we register with a device? If I registered with a group, I'd know that I only need to register once per group, that's not clear here and may lead to callers of this getting multiple notifications, one for each device in a group.
Quoted 26 lines
drivers/iommu/iommu.c +18
-0+ +int iommu_fault_notifier_call_chain (struct iommu_fault_event *event) +{ +int ret; +struct iommu_group *group =iommu_group_get (event->dev); + +if (!group) +return -EINVAL ; +/* caller provide generic data related to the event, TBD */ + ret = (blocking_notifier_call_chain (&group->fault_notifier,0 , (void *)event) + ==NOTIFY_BAD ) ? -EINVAL :0 ; +iommu_group_put (group); + +return ret; +} +EXPORT_SYMBOL (iommu_fault_notifier_call_chain); + +/** * iommu_group_id - Return ID for a group * @group: the group to ID * include/linux/iommu.h +0
-0
include/linux/iommu.h line 45
Quoted 4 lines
include/linux/iommu.h +1
-0@@ -42,6 +42,7 @@* if the IOMMU page table format is equivalent. */ #define IOMMU_PRIV (1 << 5) +#define IOMMU_EXEC (1 << 6)
Irrelevant change?
Quoted 23 lines
include/linux/iommu.h +16
-0struct iommu_ops ;struct iommu_group ;@@ -97,6 +98,36 @@ struct iommu_domain {void *iova_cookie; }; +/* +* Generic fault event notification data, used by all IOMMU architectures. +* +* - PCI and non-PCI devices +* - Recoverable faults (e.g. page request) & un-recoverable faults +* - DMA remapping and IRQ remapping faults +* +* @dev The device which faults are reported by IOMMU +* @addr tells the offending address +* @pasid contains process address space ID, used in shared virtual memory (SVM) +* @prot page access protection flag, e.g. IOMMU_READ, IOMMU_WRITE +* @flags contains fault type, etc. +* @length tells the size of the buf +* @buf contains any raw or arch specific data +* +*/
include/linux/iommu.h struct iommu_domain { · line 126
Quoted 10 lines
include/linux/iommu.h +10
-0+struct iommu_fault_event { +struct device *dev; +__u64 addr; +__u32 pasid; +__u32 prot; +__u32 flags; +#define IOMMU_FAULT_PAGE_REQ BIT(0) +#define IOMMU_FAULT_UNRECOV BIT(1) +#define IOMMU_FAULT_IRQ_REMAP BIT(2) +#define IOMMU_FAULT_INVAL BIT(3)
Details of each of these are defined where?
include/linux/iommu.h struct iommu_domain { · line 129
Quoted 3 lines
include/linux/iommu.h +3
-0+__u32 length; +__u8 buf[]; +};
This is not UAPI, so I'll be curious how vfio is supposed to expose this to userspace.
Quoted 41 lines
include/linux/iommu.h +24
-0+enum iommu_cap {IOMMU_CAP_CACHE_COHERENCY ,/* IOMMU can enforce cache coherent DMA transactions */ @@ -341,6 +372,12 @@ extern int iommu_group_register_notifier(struct iommu_group *group, struct notifier_block *nb);extern int iommu_group_unregister_notifier (struct iommu_group *group,struct notifier_block *nb); +extern int iommu_register_fault_notifier (struct device *dev, +struct notifier_block *nb); +extern int iommu_unregister_fault_notifier (struct device *dev, +struct notifier_block *nb); +extern int iommu_fault_notifier_call_chain (struct iommu_fault_event *event); +extern int iommu_group_id (struct iommu_group *group);extern struct iommu_group *iommu_group_get_for_dev (struct device *dev);extern struct iommu_domain *iommu_group_default_domain (struct iommu_group *);@@ -573,6 +610,23 @@ static inline int iommu_group_unregister_notifier(struct iommu_group *group,return 0 ; } +static inline int iommu_register_fault_notifier (struct device *dev, +struct notifier_block *nb) +{ +return 0 ; +} + +static inline int iommu_unregister_fault_notifier (struct device *dev, +struct notifier_block *nb) +{ +return 0 ; +} + +static inline int iommu_fault_notifier_call_chain (struct iommu_fault_event *event) +{ +return 0 ; +} +static inline int iommu_group_id (struct iommu_group *group) {return -ENODEV ;
Re: [RFC 5/9] iommu: Introduce fault notifier API
Quoted 120 lines · On Thu, 22 Jun 2017 16:53:17 -0600 Alex Williamson…
Quoted 117 lines · On Wed, 14 Jun 2017 15:22:59 -0700 Jacob Pan <[email protected]>…
Traditionally, device specific faults are detected and handled within their own device drivers. When IOMMU is enabled, faults such as DMA related transactions are detected by IOMMU. There is no generic reporting mechanism to report faults back to the in-kernel device driver or the guest OS in case of assigned devices.
Faults detected by IOMMU is based on the transaction's source ID which can be reported at per device basis, regardless of the device type is a PCI device or not.
The fault types includes recoverable (e.g. page request) and unrecoverable faults(e.g. invalid context). In most cases, faults can be handled by IOMMU drivers. However, there are use cases that require fault processing outside IOMMU driver, e.g.
1. page request fault originated from an SVM capable device that is assigned to guest via vIOMMU. In this case, the first level page tables are owned by the guest. Page request must be propagated to the guest to let guest OS fault in the pages then send page response. In this mechanism, the direct receiver of IOMMU fault notification is VFIO, which can relay notification events to QEMU or other user space software.
2. faults need more subtle handling by device drivers. Other than simply invoke reset function, there are needs to let device driver handle the fault with a smaller impact.
This patchset is intended to create a generic fault notification API such that it can scale as follows:
- all IOMMU types
- PCI and non-PCI devices
- recoverable and unrecoverable faults
- VFIO and other other in kernel users
- DMA & IRQ remapping (TBD)The event data contains both generic and raw architectural data such that performance is not compromised as the data propagation may involve many layers.
Signed-off-by Jacob Pan <[email protected]>Signed-off-by Ashok Raj <[email protected]>--- drivers/iommu/iommu.c | 63+++++++++++++++++++++++++++++++++++++++++++++++++++
include/linux/iommu.h| 54 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 117 insertions(+)drivers/iommu/iommu.c +3
-0@@ -48,6 +48,7 @@ struct iommu_group {struct list_head devices;struct mutex mutex;struct blocking_notifier_head notifier; +struct blocking_notifier_head fault_notifier;void *iommu_data;void (*iommu_data_release)(void *iommu_data);char *name;@@ -345,6 +346,7 @@ struct iommu_group *iommu_group_alloc(void)mutex_init (&group->mutex);INIT_LIST_HEAD (&group->devices);BLOCKING_INIT_NOTIFIER_HEAD (&group->notifier); +BLOCKING_INIT_NOTIFIER_HEAD (&group->fault_notifier); ret =ida_simple_get (&iommu_group_ida,0 ,0 ,GFP_KERNEL );if (ret <0 ) {@@ -790,6 +792,67 @@ int iommu_group_unregister_notifier(structiommu_group *group,EXPORT_SYMBOL_GPL (iommu_group_unregister_notifier );/** +* iommu_register_fault_notifier - Register a notifier for fault reporting
+ * @dev: device to notify fault events
+ * @nb: notifier block to signal
+ *
+ */drivers/iommu/iommu.c +10
-0+int iommu_register_fault_notifier (struct device *dev, +struct notifier_block *nb) +{ +int ret; +struct iommu_group *group =iommu_group_get (dev); + +if (!group) +return -EINVAL ; + + ret =
blocking_notifier_chain_register(&group->fault_notifier, nb);drivers/iommu/iommu.c +9
-0+iommu_group_put (group); + +return ret; + +} +EXPORT_SYMBOL_GPL (iommu_register_fault_notifier); + +/** +* iommu_unregister_fault_notifier - Unregister a notifier for fault reporting
+ * @domain: the domain to watch
+ * @nb: notifier block to signal
+ *
+ */drivers/iommu/iommu.c +10
-0+int iommu_unregister_fault_notifier (struct device *dev, +struct notifier_block *nb) +{ +int ret; +struct iommu_group *group =iommu_group_get (dev); + +if (!group) +return -EINVAL ; + + ret =
blocking_notifier_chain_unregister(&group->fault_notifier, nb);drivers/iommu/iommu.c +3
-0+iommu_group_put (group); + +return ret;
Quoted 9 lines
drivers/iommu/iommu.c
Quoted 2 lines
drivers/iommu/iommu.c +2
-0+} +EXPORT_SYMBOL_GPL (iommu_unregister_fault_notifier);If the call chains are on the group, why do we register with a device? If I registered with a group, I'd know that I only need to register once per group, that's not clear here and may lead to callers of this getting multiple notifications, one for each device in a group.
My thought was that since iommu_group is already part of struct device, it is more efficient to share group level notifier than introducing per device fault callbacks or notifiers.
So you do need to register per device not just once for the group. In most cases device with SVM has ACS, it will be 1:1 between device and group.
When the fault event occurs, IOMMU can identify the offending device and struct device is embedded in the event data sent to the device driver or VFIO, so the receiver can filter out unwanted events.
Quoted 26 lines
Quoted 26 lines
drivers/iommu/iommu.c +2
-0+ +int iommu_fault_notifier_call_chain(struct iommu_fault_event*event) +{
drivers/iommu/iommu.c +7
-0+int ret; +struct iommu_group *group =iommu_group_get (event->dev); + +if (!group) +return -EINVAL ; +/* caller provide generic data related to the event, TBD */ + ret =(
blocking_notifier_call_chain(&group->fault_notifier, 0, (void *)event)drivers/iommu/iommu.c +8
-0+ == NOTIFY_BAD) ? -EINVAL : 0; +iommu_group_put (group); + +return ret; +} +EXPORT_SYMBOL (iommu_fault_notifier_call_chain); + +/** * iommu_group_id - Return ID for a group * @group: the group to ID * include/linux/iommu.h +0
-1--- a/include/linux/iommu.h
Quoted 9 lines
include/linux/iommu.h line 45
Quoted 6 lines
+++
b/include/linux/iommu.hinclude/linux/iommu.h +1
-0@@ -42,6 +42,7 @@* if the IOMMU page table format is equivalent. */ #define IOMMU_PRIV (1 << 5) +#define IOMMU_EXEC (1 << 6)Irrelevant change?
yes, it should be separate. used later in svm patch.
Quoted 29 lines
Quoted 29 lines
include/linux/iommu.h +2
-0struct iommu_ops ;struct iommu_group ;@@ -97,6 +98,36 @@ struct iommu_domain {void *iova_cookie; }; +/* +* Generic fault event notification data, used by all IOMMU architectures.
+ *
+ * - PCI and non-PCI devices
+ * - Recoverable faults (e.g. page request) & un-recoverable faults
+ * - DMA remapping and IRQ remapping faults
+ *
+ * @dev The device which faults are reported by IOMMU
+ * @addr tells the offending address
+ * @pasid contains process address space ID, used in shared virtual memory (SVM)
+ * @prot page access protection flag, e.g.IOMMU_READ,IOMMU_WRITE
+ * @flags contains fault type, etc.
+ * @length tells the size of the buf
+ * @buf contains any raw or arch specific data
+ *
+ */include/linux/iommu.h +4
-0+struct iommu_fault_event { +struct device *dev; +__u64 addr; +__u32 pasid;
Quoted 9 lines
include/linux/iommu.h struct iommu_domain { · line 126
Quoted 6 lines
include/linux/iommu.h +6
-0+__u32 prot; +__u32 flags; +#define IOMMU_FAULT_PAGE_REQ BIT(0) +#define IOMMU_FAULT_UNRECOV BIT(1) +#define IOMMU_FAULT_IRQ_REMAP BIT(2) +#define IOMMU_FAULT_INVAL BIT(3)Details of each of these are defined where?
good point, will add description.
Quoted 7 lines
include/linux/iommu.h struct iommu_domain { · line 129
Quoted 3 lines
include/linux/iommu.h +3
-0+__u32 length; +__u8 buf[]; +};This is not UAPI, so I'll be curious how vfio is supposed to expose this to userspace.
But this also has other in-kernel users.
Quoted 36 lines
Quoted 36 lines
include/linux/iommu.h +1
-0+enum iommu_cap {IOMMU_CAP_CACHE_COHERENCY ,/* IOMMU can enforce cache coherent DMA transactions */
include/linux/iommu.h +0
-0@@ -341,6 +372,12 @@ extern int
iommu_group_register_notifier(struct iommu_group *group, struct notifier_block *nb); extern intiommu_group_unregister_notifier(struct iommu_group *group, struct notifier_block *nb); +extern int iommu_register_fault_notifier(struct device *dev,include/linux/iommu.h +1
-0+struct notifier_block *nb); +extern int iommu_unregister_fault_notifier(struct device *dev,
include/linux/iommu.h +1
-0+struct notifier_block *nb); +extern int
iommu_fault_notifier_call_chain(struct iommu_fault_event *event); +
extern intiommu_group_id(struct iommu_group *group); extern structiommu_group*iommu_group_get_for_dev(struct device
*dev); extern structiommu_domain*iommu_group_default_domain(structiommu_group*); @@ -573,6 +610,23 @@ static inline int iommu_group_unregister_notifier(structiommu_group*group, return 0; }include/linux/iommu.h +2
-0+static inline int iommu_register_fault_notifier(struct device *dev, + struct
notifier_block*nb) +{include/linux/iommu.h +4
-0+return 0 ; +} + +static inline int iommu_unregister_fault_notifier(struct device*dev,
include/linux/iommu.h +1
-0+ struct
notifier_block*nb) +{include/linux/iommu.h +3
-0+return 0 ; +} +
Quoted 9 lines
include/linux/iommu.h static inline int iommu_group_unregister_notifier(struct iommu_group *group, · line 632
Quoted 8 lines
+static inline int
iommu_fault_notifier_call_chain(struct iommu_fault_event *event)+{include/linux/iommu.h +3
-0+return 0 ; +} +static inline int iommu_group_id (struct iommu_group *group) {return -ENODEV ;
[Jacob Pan]
Re: [RFC 5/9] iommu: Introduce fault notifier API
Quoted 132 lines · On Fri, 23 Jun 2017 11:59:28 -0700 Jacob Pan <[email protected]>…
Quoted 120 lines · On Thu, 22 Jun 2017 16:53:17 -0600 Alex Williamson…
Quoted 117 lines · On Wed, 14 Jun 2017 15:22:59 -0700 Jacob Pan <[email protected]>…
Traditionally, device specific faults are detected and handled within their own device drivers. When IOMMU is enabled, faults such as DMA related transactions are detected by IOMMU. There is no generic reporting mechanism to report faults back to the in-kernel device driver or the guest OS in case of assigned devices.
Faults detected by IOMMU is based on the transaction's source ID which can be reported at per device basis, regardless of the device type is a PCI device or not.
The fault types includes recoverable (e.g. page request) and unrecoverable faults(e.g. invalid context). In most cases, faults can be handled by IOMMU drivers. However, there are use cases that require fault processing outside IOMMU driver, e.g.
1. page request fault originated from an SVM capable device that is assigned to guest via vIOMMU. In this case, the first level page tables are owned by the guest. Page request must be propagated to the guest to let guest OS fault in the pages then send page response. In this mechanism, the direct receiver of IOMMU fault notification is VFIO, which can relay notification events to QEMU or other user space software.
2. faults need more subtle handling by device drivers. Other than simply invoke reset function, there are needs to let device driver handle the fault with a smaller impact.
This patchset is intended to create a generic fault notification API such that it can scale as follows:
- all IOMMU types
- PCI and non-PCI devices
- recoverable and unrecoverable faults
- VFIO and other other in kernel users
- DMA & IRQ remapping (TBD)The event data contains both generic and raw architectural data such that performance is not compromised as the data propagation may involve many layers.
Signed-off-by Jacob Pan <[email protected]>Signed-off-by Ashok Raj <[email protected]>--- drivers/iommu/iommu.c | 63+++++++++++++++++++++++++++++++++++++++++++++++++++
include/linux/iommu.h| 54 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 117 insertions(+)drivers/iommu/iommu.c +3
-0@@ -48,6 +48,7 @@ struct iommu_group {struct list_head devices;struct mutex mutex;struct blocking_notifier_head notifier; +struct blocking_notifier_head fault_notifier;void *iommu_data;void (*iommu_data_release)(void *iommu_data);char *name;@@ -345,6 +346,7 @@ struct iommu_group *iommu_group_alloc(void)mutex_init (&group->mutex);INIT_LIST_HEAD (&group->devices);BLOCKING_INIT_NOTIFIER_HEAD (&group->notifier); +BLOCKING_INIT_NOTIFIER_HEAD (&group->fault_notifier); ret =ida_simple_get (&iommu_group_ida,0 ,0 ,GFP_KERNEL );if (ret <0 ) {@@ -790,6 +792,67 @@ int iommu_group_unregister_notifier(structiommu_group *group,EXPORT_SYMBOL_GPL (iommu_group_unregister_notifier );/** +* iommu_register_fault_notifier - Register a notifier for fault reporting
+ * @dev: device to notify fault events
+ * @nb: notifier block to signal
+ *
+ */drivers/iommu/iommu.c +10
-0+int iommu_register_fault_notifier (struct device *dev, +struct notifier_block *nb) +{ +int ret; +struct iommu_group *group =iommu_group_get (dev); + +if (!group) +return -EINVAL ; + + ret =
blocking_notifier_chain_register(&group->fault_notifier, nb);drivers/iommu/iommu.c +9
-0+iommu_group_put (group); + +return ret; + +} +EXPORT_SYMBOL_GPL (iommu_register_fault_notifier); + +/** +* iommu_unregister_fault_notifier - Unregister a notifier for fault reporting
+ * @domain: the domain to watch
+ * @nb: notifier block to signal
+ *
+ */drivers/iommu/iommu.c +10
-0+int iommu_unregister_fault_notifier (struct device *dev, +struct notifier_block *nb) +{ +int ret; +struct iommu_group *group =iommu_group_get (dev); + +if (!group) +return -EINVAL ; + + ret =
blocking_notifier_chain_unregister(&group->fault_notifier, nb);drivers/iommu/iommu.c +3
-0+iommu_group_put (group); + +return ret;Quoted 9 lines
drivers/iommu/iommu.c
Quoted 2 lines
drivers/iommu/iommu.c +2
-0+} +EXPORT_SYMBOL_GPL (iommu_unregister_fault_notifier);If the call chains are on the group, why do we register with a device? If I registered with a group, I'd know that I only need to register once per group, that's not clear here and may lead to callers of this getting multiple notifications, one for each device in a group.
Quoted 3 lines
My thought was that since
iommu_groupis already part of struct device, it is more efficient to share group level notifier than introducing per device fault callbacks or notifiers.
That's fine, but the interface should reflect the same.
Quoted 3 lines
So you do need to register per device not just once for the group. In most cases device with SVM has ACS, it will be 1:1 between device and group.
ACS at the endpoint is only one factor in determining grouping, we need only look at Intel Core processors to find root ports lacking ACS which will result in multiple devices per group as a very common case.
Quoted 3 lines
When the fault event occurs, IOMMU can identify the offending device and struct device is embedded in the event data sent to the device driver or VFIO, so the receiver can filter out unwanted events.
I think this is a poor design choice. If the notification list is on the group then make the registration be on the group. Otherwise the number of notifies seen by the caller is automatically multiplied by the number of devices they're using in the group. The caller not only needs to disregard unintended devices, but they need to make sure they only act on a notification for the device on the correct notifier. This is designing in unnecessary complexity and overhead.
Quoted 75 lines
Quoted 26 lines
Quoted 26 lines
drivers/iommu/iommu.c +2
-0+ +int iommu_fault_notifier_call_chain(struct iommu_fault_event*event) +{
drivers/iommu/iommu.c +7
-0+int ret; +struct iommu_group *group =iommu_group_get (event->dev); + +if (!group) +return -EINVAL ; +/* caller provide generic data related to the event, TBD */ + ret =(
blocking_notifier_call_chain(&group->fault_notifier, 0, (void *)event)drivers/iommu/iommu.c +8
-0+ == NOTIFY_BAD) ? -EINVAL : 0; +iommu_group_put (group); + +return ret; +} +EXPORT_SYMBOL (iommu_fault_notifier_call_chain); + +/** * iommu_group_id - Return ID for a group * @group: the group to ID * include/linux/iommu.h +0
-1--- a/include/linux/iommu.hQuoted 9 lines
include/linux/iommu.h line 45
Quoted 6 lines
+++
b/include/linux/iommu.hinclude/linux/iommu.h +1
-0@@ -42,6 +42,7 @@* if the IOMMU page table format is equivalent. */ #define IOMMU_PRIV (1 << 5) +#define IOMMU_EXEC (1 << 6)Irrelevant change?
yes, it should be separate. used later in svm patch.
Quoted 29 lines
Quoted 29 lines
include/linux/iommu.h +2
-0struct iommu_ops ;struct iommu_group ;@@ -97,6 +98,36 @@ struct iommu_domain {void *iova_cookie; }; +/* +* Generic fault event notification data, used by all IOMMU architectures.
+ *
+ * - PCI and non-PCI devices
+ * - Recoverable faults (e.g. page request) & un-recoverable faults
+ * - DMA remapping and IRQ remapping faults
+ *
+ * @dev The device which faults are reported by IOMMU
+ * @addr tells the offending address
+ * @pasid contains process address space ID, used in shared virtual memory (SVM)
+ * @prot page access protection flag, e.g.IOMMU_READ,IOMMU_WRITE
+ * @flags contains fault type, etc.
+ * @length tells the size of the buf
+ * @buf contains any raw or arch specific data
+ *
+ */include/linux/iommu.h +4
-0+struct iommu_fault_event { +struct device *dev; +__u64 addr; +__u32 pasid;Quoted 9 lines
include/linux/iommu.h struct iommu_domain { · line 126
Quoted 6 lines
include/linux/iommu.h +6
-0+__u32 prot; +__u32 flags; +#define IOMMU_FAULT_PAGE_REQ BIT(0) +#define IOMMU_FAULT_UNRECOV BIT(1) +#define IOMMU_FAULT_IRQ_REMAP BIT(2) +#define IOMMU_FAULT_INVAL BIT(3)Details of each of these are defined where?
good point, will add description.
Quoted 8 lines
Quoted 7 lines
include/linux/iommu.h struct iommu_domain { · line 129
Quoted 3 lines
include/linux/iommu.h +3
-0+__u32 length; +__u8 buf[]; +};This is not UAPI, so I'll be curious how vfio is supposed to expose this to userspace.
But this also has other in-kernel users.
Any point where you expect vfio to pass through one of these model specific data structures needs to be fully specified in a uapi header. No magic opaque data please. Thanks,
Alex
Re: [RFC 5/9] iommu: Introduce fault notifier API
Quoted 154 lines · On Fri, 23 Jun 2017 13:15:51 -0600 Alex Williamson…
Quoted 133 lines · On Fri, 23 Jun 2017 11:59:28 -0700 Jacob Pan <[email protected]>…
Quoted 122 lines · On Thu, 22 Jun 2017 16:53:17 -0600 Alex Williamson…
Quoted 119 lines · On Wed, 14 Jun 2017 15:22:59 -0700 Jacob Pan <[email protected]>…
Traditionally, device specific faults are detected and handled within their own device drivers. When IOMMU is enabled, faults such as DMA related transactions are detected by IOMMU. There is no generic reporting mechanism to report faults back to the in-kernel device driver or the guest OS in case of assigned devices.
Faults detected by IOMMU is based on the transaction's source ID which can be reported at per device basis, regardless of the device type is a PCI device or not.
The fault types includes recoverable (e.g. page request) and unrecoverable faults(e.g. invalid context). In most cases, faults can be handled by IOMMU drivers. However, there are use cases that require fault processing outside IOMMU driver, e.g.
1. page request fault originated from an SVM capable device that is assigned to guest via vIOMMU. In this case, the first level page tables are owned by the guest. Page request must be propagated to the guest to let guest OS fault in the pages then send page response. In this mechanism, the direct receiver of IOMMU fault notification is VFIO, which can relay notification events to QEMU or other user space software.
2. faults need more subtle handling by device drivers. Other than simply invoke reset function, there are needs to let device driver handle the fault with a smaller impact.
This patchset is intended to create a generic fault notification API such that it can scale as follows:
- all IOMMU types
- PCI and non-PCI devices
- recoverable and unrecoverable faults
- VFIO and other other in kernel users
- DMA & IRQ remapping (TBD)The event data contains both generic and raw architectural data such that performance is not compromised as the data propagation may involve many layers.
Signed-off-by Jacob Pan <[email protected]>Signed-off-by Ashok Raj <[email protected]>--- drivers/iommu/iommu.c | 63+++++++++++++++++++++++++++++++++++++++++++++++++++
include/linux/iommu.h| 54 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 117 insertions(+)drivers/iommu/iommu.c +3
-0@@ -48,6 +48,7 @@ struct iommu_group {struct list_head devices;struct mutex mutex;struct blocking_notifier_head notifier; +struct blocking_notifier_head fault_notifier;void *iommu_data;void (*iommu_data_release)(void *iommu_data);char *name;@@ -345,6 +346,7 @@ struct iommu_group *iommu_group_alloc(void)mutex_init (&group->mutex);INIT_LIST_HEAD (&group->devices);BLOCKING_INIT_NOTIFIER_HEAD (&group->notifier); +BLOCKING_INIT_NOTIFIER_HEAD (&group->fault_notifier); ret =ida_simple_get (&iommu_group_ida,0 ,0 ,GFP_KERNEL );if (ret <0 ) {@@ -790,6 +792,67 @@ int iommu_group_unregister_notifier(structiommu_group *group,EXPORT_SYMBOL_GPL (iommu_group_unregister_notifier );/** +* iommu_register_fault_notifier - Register a notifier for fault reporting
+ * @dev: device to notify fault events
+ * @nb: notifier block to signal
+ *
+ */drivers/iommu/iommu.c +10
-0+int iommu_register_fault_notifier (struct device *dev, +struct notifier_block *nb) +{ +int ret; +struct iommu_group *group =iommu_group_get (dev); + +if (!group) +return -EINVAL ; + + ret =
blocking_notifier_chain_register(&group->fault_notifier, nb);drivers/iommu/iommu.c +9
-0+iommu_group_put (group); + +return ret; + +} +EXPORT_SYMBOL_GPL (iommu_register_fault_notifier); + +/** +* iommu_unregister_fault_notifier - Unregister a notifier for fault reporting
+ * @domain: the domain to watch
+ * @nb: notifier block to signal
+ *
+ */drivers/iommu/iommu.c +10
-0+int iommu_unregister_fault_notifier (struct device *dev, +struct notifier_block *nb) +{ +int ret; +struct iommu_group *group =iommu_group_get (dev); + +if (!group) +return -EINVAL ; + + ret =
blocking_notifier_chain_unregister(&group->fault_notifier, nb);drivers/iommu/iommu.c +4
-0+iommu_group_put (group); + +return ret; +}Quoted 8 lines
Quoted 1 line
+EXPORT_SYMBOL_GPL(iommu_unregister_fault_notifier);
If the call chains are on the group, why do we register with a device? If I registered with a group, I'd know that I only need to register once per group, that's not clear here and may lead to callers of this getting multiple notifications, one for each device in a group.
Quoted 3 lines
My thought was that since
iommu_groupis already part of struct device, it is more efficient to share group level notifier than introducing per device fault callbacks or notifiers.That's fine, but the interface should reflect the same.
Quoted 3 lines
So you do need to register per device not just once for the group. In most cases device with SVM has ACS, it will be 1:1 between device and group.
ACS at the endpoint is only one factor in determining grouping, we need only look at Intel Core processors to find root ports lacking ACS which will result in multiple devices per group as a very common case.
Quoted 3 lines
When the fault event occurs, IOMMU can identify the offending device and struct device is embedded in the event data sent to the device driver or VFIO, so the receiver can filter out unwanted events.
Quoted 8 lines
I think this is a poor design choice. If the notification list is on the group then make the registration be on the group. Otherwise the number of notifies seen by the caller is automatically multiplied by the number of devices they're using in the group. The caller not only needs to disregard unintended devices, but they need to make sure they only act on a notification for the device on the correct notifier. This is designing in unnecessary complexity and overhead.
If the design is for high frequency events, I would totally agree with you that having notifier on the group does not scale. But since this is for fault notification, which implies infrequent use, is it still worthwhile to tax strict device to have a per device notifier? Even with recoverable fault, i.e. page request, a properly configured use case would pre-fault pages to minimize PRQ.
I was referring to this previous discussion by David for the need of per device fault handler(https://lwn.net/Articles/608914/). Perhaps I misinterpreted your suggestion for an alternative solution.
Thanks,
Jacob
Quoted 85 lines
Quoted 85 lines
Quoted 27 lines
Quoted 27 lines
drivers/iommu/iommu.c +2
-0+ +int iommu_fault_notifier_call_chain(struct iommu_fault_event*event) +{
drivers/iommu/iommu.c +2
-0+int ret; + struct iommu_group *group =
iommu_group_get(event->dev); +drivers/iommu/iommu.c +3
-0+if (!group) +return -EINVAL ; +/* caller provide generic data related to the event, TBD */
drivers/iommu/iommu.c +1
-0+ ret =(
blocking_notifier_call_chain(&group->fault_notifier, 0, (void *)event)drivers/iommu/iommu.c +8
-0+ == NOTIFY_BAD) ? -EINVAL : 0; +iommu_group_put (group); + +return ret; +} +EXPORT_SYMBOL (iommu_fault_notifier_call_chain); + +/** * iommu_group_id - Return ID for a group * @group: the group to ID * include/linux/iommu.h +0
-1--- a/include/linux/iommu.hQuoted 9 lines
include/linux/iommu.h line 45
Quoted 6 lines
+++
b/include/linux/iommu.hinclude/linux/iommu.h +1
-0@@ -42,6 +42,7 @@* if the IOMMU page table format is equivalent. */ #define IOMMU_PRIV (1 << 5) +#define IOMMU_EXEC (1 << 6)Irrelevant change?
yes, it should be separate. used later in svm patch.
Quoted 31 lines
Quoted 31 lines
include/linux/iommu.h +2
-0struct iommu_ops ;struct iommu_group ;@@ -97,6 +98,36 @@ struct iommu_domain {void *iova_cookie; }; +/* +* Generic fault event notification data, used by all IOMMU architectures.
+ *
+ * - PCI and non-PCI devices
+ * - Recoverable faults (e.g. page request) & un-recoverable faults
+ * - DMA remapping and IRQ remapping faults
+ *
+ * @dev The device which faults are reported by IOMMU
+ * @addr tells the offending address
+ * @pasid contains process address space ID, used in shared virtual memory (SVM)
+ * @prot page access protection flag, e.g.IOMMU_READ,IOMMU_WRITE
+ * @flags contains fault type, etc.
+ * @length tells the size of the buf
+ * @buf contains any raw or arch specific data
+ *
+ */include/linux/iommu.h +4
-0+struct iommu_fault_event { +struct device *dev; +__u64 addr; +__u32 pasid;Quoted 9 lines
include/linux/iommu.h struct iommu_domain { · line 126
Quoted 6 lines
include/linux/iommu.h +6
-0+__u32 prot; +__u32 flags; +#define IOMMU_FAULT_PAGE_REQ BIT(0) +#define IOMMU_FAULT_UNRECOV BIT(1) +#define IOMMU_FAULT_IRQ_REMAP BIT(2) +#define IOMMU_FAULT_INVAL BIT(3)Details of each of these are defined where?
good point, will add description.
Quoted 7 lines
include/linux/iommu.h struct iommu_domain { · line 129
Quoted 3 lines
include/linux/iommu.h +3
-0+__u32 length; +__u8 buf[]; +};This is not UAPI, so I'll be curious how vfio is supposed to expose this to userspace.
Quoted 8 lines
Quoted 1 line
But this also has other in-kernel users.
Any point where you expect vfio to pass through one of these model specific data structures needs to be fully specified in a uapi header. No magic opaque data please. Thanks,
Alex
[Jacob Pan]
Re: [RFC 5/9] iommu: Introduce fault notifier API
Quoted 172 lines · On Mon, 26 Jun 2017 08:27:52 -0700 Jacob Pan…
Quoted 154 lines · On Fri, 23 Jun 2017 13:15:51 -0600 Alex Williamson…
Quoted 133 lines · On Fri, 23 Jun 2017 11:59:28 -0700 Jacob Pan…
Quoted 122 lines · On Thu, 22 Jun 2017 16:53:17 -0600 Alex Williamson…
Quoted 119 lines · On Wed, 14 Jun 2017 15:22:59 -0700 Jacob Pan…
Traditionally, device specific faults are detected and handled within their own device drivers. When IOMMU is enabled, faults such as DMA related transactions are detected by IOMMU. There is no generic reporting mechanism to report faults back to the in-kernel device driver or the guest OS in case of assigned devices.
Faults detected by IOMMU is based on the transaction's source ID which can be reported at per device basis, regardless of the device type is a PCI device or not.
The fault types includes recoverable (e.g. page request) and unrecoverable faults(e.g. invalid context). In most cases, faults can be handled by IOMMU drivers. However, there are use cases that require fault processing outside IOMMU driver, e.g.
1. page request fault originated from an SVM capable device that is assigned to guest via vIOMMU. In this case, the first level page tables are owned by the guest. Page request must be propagated to the guest to let guest OS fault in the pages then send page response. In this mechanism, the direct receiver of IOMMU fault notification is VFIO, which can relay notification events to QEMU or other user space software.
2. faults need more subtle handling by device drivers. Other than simply invoke reset function, there are needs to let device driver handle the fault with a smaller impact.
This patchset is intended to create a generic fault notification API such that it can scale as follows:
- all IOMMU types
- PCI and non-PCI devices
- recoverable and unrecoverable faults
- VFIO and other other in kernel users
- DMA & IRQ remapping (TBD)The event data contains both generic and raw architectural data such that performance is not compromised as the data propagation may involve many layers.
Signed-off-by Ashok Raj <[email protected]>--- drivers/iommu/iommu.c | 63+++++++++++++++++++++++++++++++++++++++++++++++++++
include/linux/iommu.h| 54 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 117 insertions(+)drivers/iommu/iommu.c +3
-0@@ -48,6 +48,7 @@ struct iommu_group {struct list_head devices;struct mutex mutex;struct blocking_notifier_head notifier; +struct blocking_notifier_head fault_notifier;void *iommu_data;void (*iommu_data_release)(void *iommu_data);char *name;@@ -345,6 +346,7 @@ struct iommu_group *iommu_group_alloc(void)mutex_init (&group->mutex);INIT_LIST_HEAD (&group->devices);BLOCKING_INIT_NOTIFIER_HEAD (&group->notifier); +BLOCKING_INIT_NOTIFIER_HEAD (&group->fault_notifier); ret =ida_simple_get (&iommu_group_ida,0 ,0 ,GFP_KERNEL );if (ret <0 ) {@@ -790,6 +792,67 @@ int iommu_group_unregister_notifier(structiommu_group *group,EXPORT_SYMBOL_GPL (iommu_group_unregister_notifier );/** +* iommu_register_fault_notifier - Register a notifier for fault reporting
+ * @dev: device to notify fault events
+ * @nb: notifier block to signal
+ *
+ */drivers/iommu/iommu.c +10
-0+int iommu_register_fault_notifier (struct device *dev, +struct notifier_block *nb) +{ +int ret; +struct iommu_group *group =iommu_group_get (dev); + +if (!group) +return -EINVAL ; + + ret =
blocking_notifier_chain_register(&group->fault_notifier, nb);drivers/iommu/iommu.c +9
-0+iommu_group_put (group); + +return ret; + +} +EXPORT_SYMBOL_GPL (iommu_register_fault_notifier); + +/** +* iommu_unregister_fault_notifier - Unregister a notifier for fault reporting
+ * @domain: the domain to watch
+ * @nb: notifier block to signal
+ *
+ */drivers/iommu/iommu.c +10
-0+int iommu_unregister_fault_notifier (struct device *dev, +struct notifier_block *nb) +{ +int ret; +struct iommu_group *group =iommu_group_get (dev); + +if (!group) +return -EINVAL ; + + ret =
blocking_notifier_chain_unregister(&group->fault_notifier, nb);drivers/iommu/iommu.c +4
-0+iommu_group_put (group); + +return ret; +}Quoted 8 lines
Quoted 1 line
+EXPORT_SYMBOL_GPL(iommu_unregister_fault_notifier);
If the call chains are on the group, why do we register with a device? If I registered with a group, I'd know that I only need to register once per group, that's not clear here and may lead to callers of this getting multiple notifications, one for each device in a group.
Quoted 3 lines
My thought was that since
iommu_groupis already part of struct device, it is more efficient to share group level notifier than introducing per device fault callbacks or notifiers.That's fine, but the interface should reflect the same.
Quoted 3 lines
So you do need to register per device not just once for the group. In most cases device with SVM has ACS, it will be 1:1 between device and group.
ACS at the endpoint is only one factor in determining grouping, we need only look at Intel Core processors to find root ports lacking ACS which will result in multiple devices per group as a very common case.
Quoted 3 lines
When the fault event occurs, IOMMU can identify the offending device and struct device is embedded in the event data sent to the device driver or VFIO, so the receiver can filter out unwanted events.
Quoted 8 lines
I think this is a poor design choice. If the notification list is on the group then make the registration be on the group. Otherwise the number of notifies seen by the caller is automatically multiplied by the number of devices they're using in the group. The caller not only needs to disregard unintended devices, but they need to make sure they only act on a notification for the device on the correct notifier. This is designing in unnecessary complexity and overhead.
If the design is for high frequency events, I would totally agree with you that having notifier on the group does not scale. But since this is for fault notification, which implies infrequent use, is it still worthwhile to tax strict device to have a per device notifier? Even with recoverable fault, i.e. page request, a properly configured use case would pre-fault pages to minimize PRQ.
Quoted 3 lines
I was referring to this previous discussion by David for the need of per device fault handler(https://lwn.net/Articles/608914/). Perhaps I misinterpreted your suggestion for an alternative solution.
I'm not suggesting per device notifiers, I'm suggesting that if the notifier is per group then make the registration on the group, not the device. The interface is too complicated to use with too many apparently duplicate notifications otherwise. Thanks,
Alex
Quoted 95 lines
Quoted 85 lines
Quoted 85 lines
Quoted 27 lines
Quoted 27 lines
drivers/iommu/iommu.c +2
-0+ +int iommu_fault_notifier_call_chain(struct iommu_fault_event*event) +{
drivers/iommu/iommu.c +2
-0+int ret; + struct iommu_group *group =
iommu_group_get(event->dev); +drivers/iommu/iommu.c +3
-0+if (!group) +return -EINVAL ; +/* caller provide generic data related to the event, TBD */
drivers/iommu/iommu.c +1
-0+ ret =(
blocking_notifier_call_chain(&group->fault_notifier, 0, (void *)event)drivers/iommu/iommu.c +8
-0+ == NOTIFY_BAD) ? -EINVAL : 0; +iommu_group_put (group); + +return ret; +} +EXPORT_SYMBOL (iommu_fault_notifier_call_chain); + +/** * iommu_group_id - Return ID for a group * @group: the group to ID * include/linux/iommu.h +0
-1--- a/include/linux/iommu.hQuoted 9 lines
include/linux/iommu.h line 45
Quoted 6 lines
+++
b/include/linux/iommu.hinclude/linux/iommu.h +1
-0@@ -42,6 +42,7 @@* if the IOMMU page table format is equivalent. */ #define IOMMU_PRIV (1 << 5) +#define IOMMU_EXEC (1 << 6)Irrelevant change?
yes, it should be separate. used later in svm patch.
Quoted 31 lines
Quoted 31 lines
include/linux/iommu.h +2
-0struct iommu_ops ;struct iommu_group ;@@ -97,6 +98,36 @@ struct iommu_domain {void *iova_cookie; }; +/* +* Generic fault event notification data, used by all IOMMU architectures.
+ *
+ * - PCI and non-PCI devices
+ * - Recoverable faults (e.g. page request) & un-recoverable faults
+ * - DMA remapping and IRQ remapping faults
+ *
+ * @dev The device which faults are reported by IOMMU
+ * @addr tells the offending address
+ * @pasid contains process address space ID, used in shared virtual memory (SVM)
+ * @prot page access protection flag, e.g.IOMMU_READ,IOMMU_WRITE
+ * @flags contains fault type, etc.
+ * @length tells the size of the buf
+ * @buf contains any raw or arch specific data
+ *
+ */include/linux/iommu.h +4
-0+struct iommu_fault_event { +struct device *dev; +__u64 addr; +__u32 pasid;Quoted 9 lines
include/linux/iommu.h struct iommu_domain { · line 126
Quoted 6 lines
include/linux/iommu.h +6
-0+__u32 prot; +__u32 flags; +#define IOMMU_FAULT_PAGE_REQ BIT(0) +#define IOMMU_FAULT_UNRECOV BIT(1) +#define IOMMU_FAULT_IRQ_REMAP BIT(2) +#define IOMMU_FAULT_INVAL BIT(3)Details of each of these are defined where?
good point, will add description.
Quoted 7 lines
include/linux/iommu.h struct iommu_domain { · line 129
Quoted 3 lines
include/linux/iommu.h +3
-0+__u32 length; +__u8 buf[]; +};This is not UAPI, so I'll be curious how vfio is supposed to expose this to userspace.
Quoted 8 lines
Quoted 1 line
But this also has other in-kernel users.
Any point where you expect vfio to pass through one of these model specific data structures needs to be fully specified in a uapi header. No magic opaque data please. Thanks,
Alex
[Jacob Pan]
RFC 6/9 iommu/vt-d: track device with pasid table bond to a guest
In reply to Jacob PanWhen PASID table pointer of an assigned device is bond to a guest, the first level page tables are managed by the guest. However, only host/physical IOMMU can detect fault events, e.g. page requests. Therefore, we need to keep track of which device has its PASID table pointer bond to a guest such that page request and other events can be propagated to the guest as needed.
--- drivers/iommu/intel-iommu.c | 19 +------------------ include/linux/intel-iommu.h | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 18 deletions(-)
drivers/iommu/intel-iommu.c +1 -18
@@ -416,24 +416,6 @@ struct dmar_domain {iommu core */ }; -/* PCI domain-device relationship */ -struct device_domain_info { -struct list_head link;/* link to domain siblings */ -struct list_head global;/* link to global list */ -u8 bus;/* PCI bus number */ -u8 devfn;/* PCI devfn number */ -u8 pasid_supported:3 ; -u8 pasid_enabled:1 ; -u8 pri_supported:1 ; -u8 pri_enabled:1 ; -u8 ats_supported:1 ; -u8 ats_enabled:1 ; -u8 ats_qdep; -struct device *dev;/* it's NULL for PCIe-to-PCI bridge */ -struct intel_iommu *iommu;/* IOMMU used by this device */ -struct dmar_domain *domain;/* pointer to domain */ -}; -struct dmar_rmrr_unit {struct list_head list;/* list of rmrr units */ struct acpi_dmar_header *hdr;/* ACPI header */
@@ -5547,6 +5529,7 @@ static int intel_iommu_bind_pasid_table(struct iommu_domain *domain,DMA_CCMD_MASK_NOBIT , DMA_CCMD_DEVICE_INVL); iommu->flush.flush_iotlb (iommu, did,0 ,0 ,DMA_TLB_DSI_FLUSH ); + info->pasid_tbl_bond =1 ;spin_unlock_irqrestore (&iommu->lock, flags);
include/linux/intel-iommu.h +19 -0
@@ -434,6 +434,25 @@ struct intel_iommu {u32 flags;/* Software defined flags */ }; +/* PCI domain-device relationship */ +struct device_domain_info { +struct list_head link;/* link to domain siblings */ +struct list_head global;/* link to global list */ +u8 bus;/* PCI bus number */ +u8 devfn;/* PCI devfn number */ +u8 pasid_supported:3 ; +u8 pasid_enabled:1 ; +u8 pasid_tbl_bond:1 ;/* bond to guest PASID table */ +u8 pri_supported:1 ; +u8 pri_enabled:1 ; +u8 ats_supported:1 ; +u8 ats_enabled:1 ; +u8 ats_qdep; +struct device *dev;/* it's NULL for PCIe-to-PCI bridge */ +struct intel_iommu *iommu;/* IOMMU used by this device */ +struct dmar_domain *domain;/* pointer to domain */ +}; +static inline void __iommu_flush_cache (struct intel_iommu *iommu,void *addr,int size) {
Signature · 1 line
2.7.4
Re: [RFC 6/9] iommu/vt-d: track device with pasid table bond to a guest
Quoted 1 line · On Wed, 14 Jun 2017 15:23:00 -0700 Jacob Pan…
When PASID table pointer of an assigned device is bond to a guest,
s/bond/bound/
Quoted 4 lines
the first level page tables are managed by the guest. However, only host/physical IOMMU can detect fault events, e.g. page requests. Therefore, we need to keep track of which device has its PASID table pointer bond to a guest such that page request and other events can
same and throughout...
Quoted 76 lines
be propagated to the guest as needed.
Signed-off-by Ashok Raj <[email protected]>--- drivers/iommu/intel-iommu.c | 19 +------------------ include/linux/intel-iommu.h | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 18 deletions(-)drivers/iommu/intel-iommu.c +1
-18@@ -416,24 +416,6 @@ struct dmar_domain {iommu core */ }; -/* PCI domain-device relationship */ -struct device_domain_info { -struct list_head link;/* link to domain siblings */ -struct list_head global;/* link to global list */ -u8 bus;/* PCI bus number */ -u8 devfn;/* PCI devfn number */ -u8 pasid_supported:3 ; -u8 pasid_enabled:1 ; -u8 pri_supported:1 ; -u8 pri_enabled:1 ; -u8 ats_supported:1 ; -u8 ats_enabled:1 ; -u8 ats_qdep; -struct device *dev;/* it's NULL for PCIe-to-PCI bridge */ -struct intel_iommu *iommu;/* IOMMU used by this device */ -struct dmar_domain *domain;/* pointer to domain */ -}; -struct dmar_rmrr_unit {struct list_head list;/* list of rmrr units */ struct acpi_dmar_header *hdr;/* ACPI header */ @@ -5547,6 +5529,7 @@ static int intel_iommu_bind_pasid_table(struct iommu_domain *domain,DMA_CCMD_MASK_NOBIT , DMA_CCMD_DEVICE_INVL); iommu->flush.flush_iotlb (iommu, did,0 ,0 ,DMA_TLB_DSI_FLUSH ); + info->pasid_tbl_bond =1 ;spin_unlock_irqrestore (&iommu->lock, flags);include/linux/intel-iommu.h +19
-0@@ -434,6 +434,25 @@ struct intel_iommu {u32 flags;/* Software defined flags */ }; +/* PCI domain-device relationship */ +struct device_domain_info { +struct list_head link;/* link to domain siblings */ +struct list_head global;/* link to global list */ +u8 bus;/* PCI bus number */ +u8 devfn;/* PCI devfn number */ +u8 pasid_supported:3 ; +u8 pasid_enabled:1 ; +u8 pasid_tbl_bond:1 ;/* bond to guest PASID table */ +u8 pri_supported:1 ; +u8 pri_enabled:1 ; +u8 ats_supported:1 ; +u8 ats_enabled:1 ; +u8 ats_qdep; +struct device *dev;/* it's NULL for PCIe-to-PCI bridge */ +struct intel_iommu *iommu;/* IOMMU used by this device */ +struct dmar_domain *domain;/* pointer to domain */ +}; +static inline void __iommu_flush_cache (struct intel_iommu *iommu,void *addr,int size) {
RFC 7/9 iommu/dmar: notify unrecoverable faults
In reply to Jacob PanCurrently, when device DMA faults are detected by IOMMU the fault reasons are printed but the offending device is not notified. This patch allows device drivers to be optionally notified for fault conditions when device specific handling is needed for more subtle processing, e.g. request with PASID transactions.
--- drivers/iommu/dmar.c | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-)
drivers/iommu/dmar.c +36 -1
@@ -1587,11 +1587,43 @@ void dmar_msi_read(int irq, struct msi_msg *msg)raw_spin_unlock_irqrestore (&iommu->register_lock, flag); } +static int dmar_unrecov_fault_notify (u8 fault_reason,u16 source_id, +unsigned long long addr) +{ +int ret; +struct pci_dev *pdev; +struct iommu_fault_event *event; + + pdev =pci_get_bus_and_slot (source_id >>8 , source_id &0xFF ); +if (!pdev) +return -ENODEV ; +pr_debug ("Notify PCI device fault [%02x:%02x.%d]\n" , + source_id >>8 ,PCI_SLOT (source_id &0xff ), +PCI_FUNC (source_id &0xff )); + event =kzalloc (sizeof (*event) +sizeof (fault_reason),GFP_KERNEL ); +if (!event) +return -ENOMEM ; + +pci_dev_get (pdev); + event->dev = &pdev->dev; + event->buf[0 ] = fault_reason; + event->addr = addr; + event->length =sizeof (fault_reason); + event->flags =IOMMU_FAULT_UNRECOV ; + ret =iommu_fault_notifier_call_chain (event); + +pci_dev_put (pdev); +kfree (event); + +return ret; +} +static int dmar_fault_do_one (struct intel_iommu *iommu,int type,u8 fault_reason,u16 source_id,unsigned long long addr) {const char *reason;int fault_type; +int ret =0 ; reason =dmar_get_fault_reason (fault_reason, &fault_type);
@@ -1600,11 +1632,14 @@ static int dmar_fault_do_one(struct intel_iommu *iommu, int type, source_id >>8 ,PCI_SLOT (source_id &0xFF ),PCI_FUNC (source_id &0xFF ), addr >>48 , fault_reason, reason); - else + else {pr_err ("[%s] Request device [%02x:%02x.%d] fault addr %llx [fault reason %02d] %s\n" , type ?"DMA Read" :"DMA Write" , source_id >>8 ,PCI_SLOT (source_id &0xFF ),PCI_FUNC (source_id &0xFF ), addr, fault_reason, reason); + ret =dmar_unrecov_fault_notify (fault_reason, source_id, addr); + } +return 0 ; }
Signature · 1 line
2.7.4
Re: [RFC 7/9] iommu/dmar: notify unrecoverable faults
Quoted 26 lines · On Wed, 14 Jun 2017 15:23:01 -0700 Jacob Pan…
Currently, when device DMA faults are detected by IOMMU the fault reasons are printed but the offending device is not notified. This patch allows device drivers to be optionally notified for fault conditions when device specific handling is needed for more subtle processing, e.g. request with PASID transactions.
Signed-off-by Ashok Raj <[email protected]>--- drivers/iommu/dmar.c | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-)drivers/iommu/dmar.c +6
-0@@ -1587,11 +1587,43 @@ void dmar_msi_read(int irq, struct msi_msg *msg)raw_spin_unlock_irqrestore (&iommu->register_lock, flag); } +static int dmar_unrecov_fault_notify (u8 fault_reason,u16 source_id, +unsigned long long addr) +{ +int ret; +struct pci_dev *pdev; +struct iommu_fault_event *event;
drivers/iommu/dmar.c void dmar_msi_read(int irq, struct msi_msg *msg) · line 1605
Quoted 10 lines
drivers/iommu/dmar.c +10
-0+ + pdev =pci_get_bus_and_slot (source_id >>8 , source_id &0xFF ); +if (!pdev) +return -ENODEV ; +pr_debug ("Notify PCI device fault [%02x:%02x.%d]\n" , + source_id >>8 ,PCI_SLOT (source_id &0xff ), +PCI_FUNC (source_id &0xff )); + event =kzalloc (sizeof (*event) +sizeof (fault_reason),GFP_KERNEL ); +if (!event) +return -ENOMEM ;
Leaks pdev reference.
Quoted 25 lines
drivers/iommu/dmar.c +16
-0+ +pci_dev_get (pdev); + event->dev = &pdev->dev; + event->buf[0 ] = fault_reason; + event->addr = addr; + event->length =sizeof (fault_reason); + event->flags =IOMMU_FAULT_UNRECOV ; + ret =iommu_fault_notifier_call_chain (event); + +pci_dev_put (pdev); +kfree (event); + +return ret; +} +static int dmar_fault_do_one (struct intel_iommu *iommu,int type,u8 fault_reason,u16 source_id,unsigned long long addr) {const char *reason;int fault_type; +int ret =0 ; reason =dmar_get_fault_reason (fault_reason, &fault_type);
drivers/iommu/dmar.c static int dmar_fault_do_one(struct intel_iommu *iommu, int type, · line 1640
Quoted 10 lines
drivers/iommu/dmar.c +2
-1@@ -1600,11 +1632,14 @@ static int dmar_fault_do_one(struct intel_iommu *iommu, int type, source_id >>8 ,PCI_SLOT (source_id &0xFF ),PCI_FUNC (source_id &0xFF ), addr >>48 , fault_reason, reason); - else + else {pr_err ("[%s] Request device [%02x:%02x.%d] fault addr %llx [fault reason %02d] %s\n" , type ?"DMA Read" :"DMA Write" , source_id >>8 ,PCI_SLOT (source_id &0xFF ),PCI_FUNC (source_id &0xFF ), addr, fault_reason, reason); + ret =dmar_unrecov_fault_notify (fault_reason, source_id, addr);
For what purpose are we recording this return code?
Quoted 5 lines
drivers/iommu/dmar.c +2
-0+ } +return 0 ; }
Re: [RFC 7/9] iommu/dmar: notify unrecoverable faults
Quoted 36 lines · On Thu, 22 Jun 2017 16:54:16 -0600 Alex Williamson…
Quoted 33 lines · On Wed, 14 Jun 2017 15:23:01 -0700 Jacob Pan <[email protected]>…
Currently, when device DMA faults are detected by IOMMU the fault reasons are printed but the offending device is not notified. This patch allows device drivers to be optionally notified for fault conditions when device specific handling is needed for more subtle processing, e.g. request with PASID transactions.
Signed-off-by Jacob Pan <[email protected]>Signed-off-by Ashok Raj <[email protected]>--- drivers/iommu/dmar.c | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-)drivers/iommu/dmar.c +11
-0@@ -1587,11 +1587,43 @@ void dmar_msi_read(int irq, struct msi_msg *msg) raw_spin_unlock_irqrestore(&iommu->register_lock, flag); } +static int dmar_unrecov_fault_notify (u8 fault_reason,u16 source_id, +unsigned long long addr) +{ +int ret; +struct pci_dev *pdev; +struct iommu_fault_event *event; + + pdev =pci_get_bus_and_slot (source_id >>8 , source_id &0xFF ); +if (!pdev) +return -ENODEV ; + pr_debug("Notify PCI device fault [%02x:%02x.%d]\n",
Quoted 9 lines
drivers/iommu/dmar.c void dmar_msi_read(int irq, struct msi_msg *msg) · line 1605
Quoted 6 lines
drivers/iommu/dmar.c +5
-0+ source_id >>8 ,PCI_SLOT (source_id &0xff ), + PCI_FUNC(source_id & 0xff)); + event =kzalloc (sizeof (*event) +sizeof (fault_reason),GFP_KERNEL ); +if (!event) +return -ENOMEM ;Leaks pdev reference.
same as before. pci_get_bus_and_slot() does not do ref counting.
Quoted 24 lines
Quoted 24 lines
drivers/iommu/dmar.c +16
-0+ +pci_dev_get (pdev); + event->dev = &pdev->dev; + event->buf[0 ] = fault_reason; + event->addr = addr; + event->length =sizeof (fault_reason); + event->flags =IOMMU_FAULT_UNRECOV ; + ret =iommu_fault_notifier_call_chain (event); + +pci_dev_put (pdev); +kfree (event); + +return ret; +} +static int dmar_fault_do_one (struct intel_iommu *iommu,int type,u8 fault_reason,u16 source_id,unsigned long long addr) {const char *reason;int fault_type; +int ret =0 ;reason =
dmar_get_fault_reason(fault_reason, &fault_type);
Quoted 16 lines
Quoted 12 lines
drivers/iommu/dmar.c +0
-0@@ -1600,11 +1632,14 @@ static int dmar_fault_do_one(struct
intel_iommu*iommu, int type,source_id>> 8,PCI_SLOT(source_id & 0xFF),PCI_FUNC(source_id & 0xFF), addr >> 48,drivers/iommu/dmar.c +1
-1fault_reason, reason); - else + else { pr_err("[%s] Request device [%02x:%02x.%d] faultaddr %llx [fault reason %02d] %s\n", type ? "DMA Read" : "DMA Write",
source_id>> 8,PCI_SLOT(source_id & 0xFF),
PCI_FUNC(source_id & 0xFF), addr,fault_reason, reason);drivers/iommu/dmar.c +1
-0+ ret = dmar_unrecov_fault_notify(fault_reason,Quoted 1 line
source_id, addr);For what purpose are we recording this return code?
good catch. I will drop the return code.
Quoted 6 lines
drivers/iommu/dmar.c static int dmar_fault_do_one(struct intel_iommu *iommu, int type, · line 1644
Quoted 5 lines
drivers/iommu/dmar.c +2
-0+ } +return 0 ; }
[Jacob Pan]
RFC 8/9 iommu/intel-svm: notify page request to guest
In reply to Jacob PanIf the source device of a page request has its PASID table pointer bond to a guest, the first level page tables are owned by the guest. In this case, we shall let guest OS to manage page fault.
This patch uses the IOMMU fault notification API to send notifications, possibly via VFIO, to the guest OS. Once guest pages are fault in, guest will issue page response which will be passed down via the invalidation passdown APIs.
--- drivers/iommu/intel-svm.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 80 insertions(+), 1 deletion(-)
drivers/iommu/intel-svm.c +80 -1
@@ -525,6 +525,80 @@ static bool access_error(struct vm_area_struct *vma, struct page_req_dsc *req)return (requested & ~vma->vm_flags) !=0 ; } +static int prq_to_iommu_prot (struct page_req_dsc *req) +{ +int prot =0 ; + +if (req->rd_req) + prot |=IOMMU_READ ; +if (req->wr_req) + prot |=IOMMU_WRITE ; +if (req->exe_req) + prot |=IOMMU_EXEC ; +if (req->priv_req) + prot |=IOMMU_PRIV ; + +return prot; +} + +static int intel_svm_prq_notify (struct device *dev,struct page_req_dsc *desc) +{ +int ret =0 ; +struct iommu_fault_event *event; +struct pci_dev *pdev; +struct device_domain_info *info; +unsigned long buf_offset; + +/** +* If caller does not provide struct device, this is the case where +* guest PASID table is bond to the device. So we need to retrieve +* struct device from the page request deescriptor then proceed. +*/ +if (!dev) { + pdev =pci_get_bus_and_slot (desc->bus, desc->devfn); +if (!pdev) { +pr_err ("No PCI device found for PRQ [%02x:%02x.%d]\n" , + desc->bus,PCI_SLOT (desc->devfn), +PCI_FUNC (desc->devfn)); +return -ENODEV ; + } +/** +* Make sure PASID table pointer is bond to guest, if yes notify +* handler in the guest, e.g. via VFIO. +*/ + info = pdev->dev.archdata.iommu; +if (!info || !info->pasid_tbl_bond) { +pr_debug ("PRQ device pasid table not bond.\n" ); +return -EINVAL ; + } + dev = &pdev->dev; + } + +pr_debug ("Notify PRQ device [%02x:%02x.%d]\n" , + desc->bus,PCI_SLOT (desc->devfn), +PCI_FUNC (desc->devfn)); + event =kzalloc (sizeof (*event) +sizeof (*desc),GFP_KERNEL ); +if (!event) +return -ENOMEM ; + +get_device (dev); +/* Fill in event data for device specific processing */ + event->dev = dev; + buf_offset = offsetofend(struct iommu_fault_event, length); +memcpy (buf_offset + event, desc,sizeof (*desc)); + event->addr = desc->addr; + event->pasid = desc->pasid; + event->prot =prq_to_iommu_prot (desc); + event->length =sizeof (*desc); + event->flags =IOMMU_FAULT_PAGE_REQ ; + + ret =iommu_fault_notifier_call_chain (event); +put_device (dev); +kfree (event); + +return ret; +} +static irqreturn_t prq_event_thread (int irq,void *d) {struct intel_iommu *iommu = d;
@@ -548,7 +622,12 @@ static irqreturn_t prq_event_thread(int irq, void *d) handled =1 ; req = &iommu->prq[head /sizeof (*req)]; - +/** +* If prq is to be handled outside iommu driver via receiver of +* the fault notifiers, we skip the page response here. +*/ +if (!intel_svm_prq_notify (NULL , req)) +continue ; result =QI_RESP_FAILURE ; address = (u64 )req->addr <<VTD_PAGE_SHIFT ;if (!req->pasid_present) {
Signature · 1 line
2.7.4
Re: [RFC 8/9] iommu/intel-svm: notify page request to guest
Quoted 57 lines · On Wed, 14 Jun 2017 15:23:02 -0700 Jacob Pan <[email protected]>…
If the source device of a page request has its PASID table pointer bond to a guest, the first level page tables are owned by the guest. In this case, we shall let guest OS to manage page fault.
This patch uses the IOMMU fault notification API to send notifications, possibly via VFIO, to the guest OS. Once guest pages are fault in, guest will issue page response which will be passed down via the invalidation passdown APIs.
Signed-off-by Jacob Pan <[email protected]>Signed-off-by Ashok Raj <[email protected]>--- drivers/iommu/intel-svm.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 80 insertions(+), 1 deletion(-)drivers/iommu/intel-svm.c +34
-0@@ -525,6 +525,80 @@ static bool access_error(struct vm_area_struct *vma, struct page_req_dsc *req)return (requested & ~vma->vm_flags) !=0 ; } +static int prq_to_iommu_prot (struct page_req_dsc *req) +{ +int prot =0 ; + +if (req->rd_req) + prot |=IOMMU_READ ; +if (req->wr_req) + prot |=IOMMU_WRITE ; +if (req->exe_req) + prot |=IOMMU_EXEC ; +if (req->priv_req) + prot |=IOMMU_PRIV ; + +return prot; +} + +static int intel_svm_prq_notify (struct device *dev,struct page_req_dsc *desc) +{ +int ret =0 ; +struct iommu_fault_event *event; +struct pci_dev *pdev; +struct device_domain_info *info; +unsigned long buf_offset; + +/** +* If caller does not provide struct device, this is the case where +* guest PASID table is bond to the device. So we need to retrieve +* struct device from the page request deescriptor then proceed. +*/ +if (!dev) { + pdev =pci_get_bus_and_slot (desc->bus, desc->devfn); +if (!pdev) { + pr_err("No PCI device found for PRQ [%02x:%02x.%d]\n", + desc->bus, PCI_SLOT(desc->devfn),
drivers/iommu/intel-svm.c static bool access_error(struct vm_area_struct *vma, struct page_req_dsc *req) · line 571
Quoted 10 lines
drivers/iommu/intel-svm.c +10
-0+ PCI_FUNC(desc->devfn)); +return -ENODEV ; + } +/** +* Make sure PASID table pointer is bond to guest, if yes notify +* handler in the guest, e.g. via VFIO. +*/ + info = pdev->dev.archdata.iommu; +if (!info || !info->pasid_tbl_bond) { +pr_debug ("PRQ device pasid table not bond.\n" );
I can "bond" two things together, they are then "bound".
drivers/iommu/intel-svm.c static bool access_error(struct vm_area_struct *vma, struct page_req_dsc *req) · line 574
Quoted 3 lines
drivers/iommu/intel-svm.c +3
-0+return -EINVAL ; + } + dev = &pdev->dev;
Leaks pdev reference. Both normal and error path.
Quoted 44 lines
drivers/iommu/intel-svm.c +33
-1+ } + +pr_debug ("Notify PRQ device [%02x:%02x.%d]\n" , + desc->bus,PCI_SLOT (desc->devfn), +PCI_FUNC (desc->devfn)); + event =kzalloc (sizeof (*event) +sizeof (*desc),GFP_KERNEL ); +if (!event) +return -ENOMEM ; + +get_device (dev); +/* Fill in event data for device specific processing */ + event->dev = dev; + buf_offset = offsetofend(struct iommu_fault_event, length); +memcpy (buf_offset + event, desc,sizeof (*desc)); + event->addr = desc->addr; + event->pasid = desc->pasid; + event->prot =prq_to_iommu_prot (desc); + event->length =sizeof (*desc); + event->flags =IOMMU_FAULT_PAGE_REQ ; + + ret =iommu_fault_notifier_call_chain (event); +put_device (dev); +kfree (event); + +return ret; +} +static irqreturn_t prq_event_thread (int irq,void *d) {struct intel_iommu *iommu = d;@@ -548,7 +622,12 @@ static irqreturn_t prq_event_thread(int irq, void *d) handled =1 ; req = &iommu->prq[head /sizeof (*req)]; - +/** +* If prq is to be handled outside iommu driver via receiver of +* the fault notifiers, we skip the page response here. +*/ +if (!intel_svm_prq_notify (NULL , req)) +continue ; result =QI_RESP_FAILURE ; address = (u64 )req->addr <<VTD_PAGE_SHIFT ;if (!req->pasid_present) {
Re: [RFC 8/9] iommu/intel-svm: notify page request to guest
Quoted 72 lines · On Thu, 22 Jun 2017 16:53:58 -0600 Alex Williamson…
Quoted 69 lines · On Wed, 14 Jun 2017 15:23:02 -0700 Jacob Pan <[email protected]>…
If the source device of a page request has its PASID table pointer bond to a guest, the first level page tables are owned by the guest. In this case, we shall let guest OS to manage page fault.
This patch uses the IOMMU fault notification API to send notifications, possibly via VFIO, to the guest OS. Once guest pages are fault in, guest will issue page response which will be passed down via the invalidation passdown APIs.
Signed-off-by Jacob Pan <[email protected]>Signed-off-by Ashok Raj <[email protected]>--- drivers/iommu/intel-svm.c | 81++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 80 insertions(+), 1 deletion(-)
drivers/iommu/intel-svm.c +25
-0@@ -525,6 +525,80 @@ static bool access_error(struct vm_area_struct *vma, struct page_req_dsc *req) return (requested & ~vma->vm_flags) !=0 ; } +static int prq_to_iommu_prot (struct page_req_dsc *req) +{ +int prot =0 ; + +if (req->rd_req) + prot |=IOMMU_READ ; +if (req->wr_req) + prot |=IOMMU_WRITE ; +if (req->exe_req) + prot |=IOMMU_EXEC ; +if (req->priv_req) + prot |=IOMMU_PRIV ; + +return prot; +} + +static int intel_svm_prq_notify (struct device *dev,struct page_req_dsc *desc) +{ +int ret =0 ; +struct iommu_fault_event *event; +struct pci_dev *pdev; +struct device_domain_info *info; +unsigned long buf_offset; + +/** +* If caller does not provide struct device, this is the case where
drivers/iommu/intel-svm.c +1
-0+* guest PASID table is bond to the device. So we need to retrieve
drivers/iommu/intel-svm.c +1
-0+* struct device from the page request deescriptor then proceed.
drivers/iommu/intel-svm.c +3
-0+*/ +if (!dev) { + pdev = pci_get_bus_and_slot(desc->bus,
desc->devfn);drivers/iommu/intel-svm.c +2
-0+if (!pdev) { + pr_err("No PCI device found for PRQ[%02x:%02x.%d]\n",
drivers/iommu/intel-svm.c +6
-0+ desc->bus,PCI_SLOT (desc->devfn), + PCI_FUNC(desc->devfn)); +return -ENODEV ; + } +/** +* Make sure PASID table pointer is bond to guest, if yes notify
Quoted 9 lines
Quoted 6 lines
drivers/iommu/intel-svm.c +5
-0+* handler in the guest, e.g. via VFIO. +*/ + info = pdev->dev.archdata.iommu; +if (!info || !info->pasid_tbl_bond) { + pr_debug("PRQ device pasid table notbond.\n");
I can "bond" two things together, they are then "bound".
will fix that :)
Quoted 6 lines
drivers/iommu/intel-svm.c static bool access_error(struct vm_area_struct *vma, struct page_req_dsc *req) · line 574
Quoted 3 lines
drivers/iommu/intel-svm.c +3
-0+return -EINVAL ; + } + dev = &pdev->dev;Leaks pdev reference. Both normal and error path.
I guess you are referring to ref count in pci_get_bus_and_slot()? I did look at the code, it does not seem to do the count increment. Perhaps the comment is stale?
* pointer to its data structure. The caller must decrement the
* reference count by calling pci_dev_put(). If no device is found,
* %NULL is returned.
*/
struct pci_dev *pci_get_domain_bus_and_slot(int domain, unsigned int bus,
unsigned int devfn)
{
struct pci_dev *dev = NULL;
for_each_pci_dev(dev) {
if (pci_domain_nr(dev->bus) == domain &&
(dev->bus->number == bus && dev->devfn == devfn))
return dev;
}
return NULL;
}EXPORT_SYMBOL(pci_get_domain_bus_and_slot);
Quoted 34 lines
Quoted 34 lines
drivers/iommu/intel-svm.c +6
-0+ } + +pr_debug ("Notify PRQ device [%02x:%02x.%d]\n" , + desc->bus,PCI_SLOT (desc->devfn), +PCI_FUNC (desc->devfn)); + event = kzalloc(sizeof(*event) + sizeof(*desc),
GFP_KERNEL);drivers/iommu/intel-svm.c +21
-0+if (!event) +return -ENOMEM ; + +get_device (dev); +/* Fill in event data for device specific processing */ + event->dev = dev; + buf_offset = offsetofend(struct iommu_fault_event, length); +memcpy (buf_offset + event, desc,sizeof (*desc)); + event->addr = desc->addr; + event->pasid = desc->pasid; + event->prot =prq_to_iommu_prot (desc); + event->length =sizeof (*desc); + event->flags =IOMMU_FAULT_PAGE_REQ ; + + ret =iommu_fault_notifier_call_chain (event); +put_device (dev); +kfree (event); + +return ret; +} +static irqreturn_t prq_event_thread (int irq,void *d) {struct intel_iommu *iommu = d;@@ -548,7 +622,12 @@ static irqreturn_t prq_event_thread(int irq, void *d) handled = 1;
Quoted 14 lines
drivers/iommu/intel-svm.c static irqreturn_t prq_event_thread(int irq, void *d) · line 633
Quoted 13 lines
drivers/iommu/intel-svm.c +2
-1req = &iommu->prq[head /sizeof (*req)]; - +/** +* If prq is to be handled outside iommu driver via receiver of
drivers/iommu/intel-svm.c +1
-0+* the fault notifiers, we skip the page response here.
drivers/iommu/intel-svm.c +3
-0+*/ +if (!intel_svm_prq_notify (NULL , req)) +continue ; result =QI_RESP_FAILURE ; address = (u64 )req->addr <<VTD_PAGE_SHIFT ;if (!req->pasid_present) {
[Jacob Pan]
Re: [RFC 8/9] iommu/intel-svm: notify page request to guest
Quoted 101 lines · On Fri, 23 Jun 2017 13:16:29 -0700 Jacob Pan <[email protected]>…
Quoted 72 lines · On Thu, 22 Jun 2017 16:53:58 -0600 Alex Williamson…
Quoted 69 lines · On Wed, 14 Jun 2017 15:23:02 -0700 Jacob Pan <[email protected]>…
If the source device of a page request has its PASID table pointer bond to a guest, the first level page tables are owned by the guest. In this case, we shall let guest OS to manage page fault.
This patch uses the IOMMU fault notification API to send notifications, possibly via VFIO, to the guest OS. Once guest pages are fault in, guest will issue page response which will be passed down via the invalidation passdown APIs.
Signed-off-by Jacob Pan <[email protected]>Signed-off-by Ashok Raj <[email protected]>--- drivers/iommu/intel-svm.c | 81++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 80 insertions(+), 1 deletion(-)
drivers/iommu/intel-svm.c +25
-0@@ -525,6 +525,80 @@ static bool access_error(struct vm_area_struct *vma, struct page_req_dsc *req) return (requested & ~vma->vm_flags) !=0 ; } +static int prq_to_iommu_prot (struct page_req_dsc *req) +{ +int prot =0 ; + +if (req->rd_req) + prot |=IOMMU_READ ; +if (req->wr_req) + prot |=IOMMU_WRITE ; +if (req->exe_req) + prot |=IOMMU_EXEC ; +if (req->priv_req) + prot |=IOMMU_PRIV ; + +return prot; +} + +static int intel_svm_prq_notify (struct device *dev,struct page_req_dsc *desc) +{ +int ret =0 ; +struct iommu_fault_event *event; +struct pci_dev *pdev; +struct device_domain_info *info; +unsigned long buf_offset; + +/** +* If caller does not provide struct device, this is the case where
drivers/iommu/intel-svm.c +1
-0+* guest PASID table is bond to the device. So we need to retrieve
drivers/iommu/intel-svm.c +1
-0+* struct device from the page request deescriptor then proceed.
drivers/iommu/intel-svm.c +3
-0+*/ +if (!dev) { + pdev = pci_get_bus_and_slot(desc->bus,
desc->devfn);drivers/iommu/intel-svm.c +2
-0+if (!pdev) { + pr_err("No PCI device found for PRQ[%02x:%02x.%d]\n",
drivers/iommu/intel-svm.c +6
-0+ desc->bus,PCI_SLOT (desc->devfn), + PCI_FUNC(desc->devfn)); +return -ENODEV ; + } +/** +* Make sure PASID table pointer is bond to guest, if yes notify
Quoted 9 lines
Quoted 6 lines
drivers/iommu/intel-svm.c +5
-0+* handler in the guest, e.g. via VFIO. +*/ + info = pdev->dev.archdata.iommu; +if (!info || !info->pasid_tbl_bond) { + pr_debug("PRQ device pasid table notbond.\n");
I can "bond" two things together, they are then "bound".
will fix that :)
Quoted 6 lines
drivers/iommu/intel-svm.c static bool access_error(struct vm_area_struct *vma, struct page_req_dsc *req) · line 574
Quoted 3 lines
drivers/iommu/intel-svm.c +3
-0+return -EINVAL ; + } + dev = &pdev->dev;Leaks pdev reference. Both normal and error path.
I guess you are referring to ref count in
pci_get_bus_and_slot()? I did look at the code, it does not seem to do the count increment. Perhaps the comment is stale?* pointer to its data structure. The caller must decrement the
* reference count by callingpci_dev_put(). If no device is found,
* %NULL is returned.
*/
structpci_dev*pci_get_domain_bus_and_slot(int domain, unsigned int bus,
unsigned int devfn)
Quoted 4 lines
{
struct pci_dev *dev = NULL;
for_each_pci_dev(dev) {^^^^^^^^^^^^^^^^ <-- look in here, it's trickier than it appears
Quoted 60 lines
if (pci_domain_nr(dev->bus) == domain && (dev->bus->number == bus && dev->devfn == devfn)) return dev; } return NULL; }
EXPORT_SYMBOL(pci_get_domain_bus_and_slot);Quoted 34 lines
Quoted 34 lines
drivers/iommu/intel-svm.c +6
-0+ } + +pr_debug ("Notify PRQ device [%02x:%02x.%d]\n" , + desc->bus,PCI_SLOT (desc->devfn), +PCI_FUNC (desc->devfn)); + event = kzalloc(sizeof(*event) + sizeof(*desc),
GFP_KERNEL);drivers/iommu/intel-svm.c +21
-0+if (!event) +return -ENOMEM ; + +get_device (dev); +/* Fill in event data for device specific processing */ + event->dev = dev; + buf_offset = offsetofend(struct iommu_fault_event, length); +memcpy (buf_offset + event, desc,sizeof (*desc)); + event->addr = desc->addr; + event->pasid = desc->pasid; + event->prot =prq_to_iommu_prot (desc); + event->length =sizeof (*desc); + event->flags =IOMMU_FAULT_PAGE_REQ ; + + ret =iommu_fault_notifier_call_chain (event); +put_device (dev); +kfree (event); + +return ret; +} +static irqreturn_t prq_event_thread (int irq,void *d) {struct intel_iommu *iommu = d;@@ -548,7 +622,12 @@ static irqreturn_t prq_event_thread(int irq, void *d) handled = 1;Quoted 14 lines
drivers/iommu/intel-svm.c static irqreturn_t prq_event_thread(int irq, void *d) · line 633
Quoted 13 lines
drivers/iommu/intel-svm.c +2
-1req = &iommu->prq[head /sizeof (*req)]; - +/** +* If prq is to be handled outside iommu driver via receiver of
drivers/iommu/intel-svm.c +1
-0+* the fault notifiers, we skip the page response here.
drivers/iommu/intel-svm.c +3
-0+*/ +if (!intel_svm_prq_notify (NULL , req)) +continue ; result =QI_RESP_FAILURE ; address = (u64 )req->addr <<VTD_PAGE_SHIFT ;if (!req->pasid_present) {[Jacob Pan]
Re: [RFC 8/9] iommu/intel-svm: notify page request to guest
Quoted 3 lines · On Fri, 23 Jun 2017 14:34:34 -0600 Alex Williamson…
Quoted 1 line
for_each_pci_dev(dev) {^^^^^^^^^^^^^^^^ <-- look in here, it's trickier than itappears
you are right, thanks.
RFC 9/9 iommu/intel-svm: replace dev ops with generic fault notifier
In reply to Jacob PanIntel SVM devices register callbacks when bind task and PASID. These fault callbacks are optional which can be replaced by the generic IOMMU fault notifier APIs.
Currently, there is no main line kernel user of these callback functions. Fault event data delivered by the IOMMU notification API is both generic and contains raw data for architectural specific uses.
--- drivers/iommu/intel-svm.c | 13 ++----------- include/linux/intel-svm.h | 20 +++----------------- 2 files changed, 5 insertions(+), 28 deletions(-)
drivers/iommu/intel-svm.c +2 -11
@@ -291,7 +291,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 flags,struct svm_dev_ops *ops) +int intel_svm_bind_mm (struct device *dev,int *pasid,int flags) {struct intel_iommu *iommu =intel_svm_device_to_iommu (dev);struct intel_svm_dev *sdev;
@@ -337,10 +337,6 @@ int intel_svm_bind_mm(struct device *dev, int *pasid, int flags, struct svm_dev_list_for_each_entry (sdev, &svm->devs, list) {if (dev == sdev->dev) { -if (sdev->ops != ops) { - ret = -EBUSY ; - goto out; - } sdev->users++; goto success; }
@@ -366,7 +362,6 @@ int intel_svm_bind_mm(struct device *dev, int *pasid, int flags, struct svm_dev_ }/* Finish the setup now we know we're keeping it */ sdev->users =1 ; - sdev->ops = ops;init_rcu_head (&sdev->rcu);if (!svm) {
@@ -693,11 +688,7 @@ static irqreturn_t prq_event_thread(int irq, void *d)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->exe_req <<1 ) | (req->priv_req); - sdev->ops->fault_cb (sdev->dev, req->pasid, req->addr, req->private, rwxp, result); - } +intel_svm_prq_notify (sdev->dev, req);/* We get here in the error case where the PASID lookup failed, and these can be NULL. Do not use them below this point! */ sdev =NULL ;
include/linux/intel-svm.h +3 -17
@@ -18,18 +18,6 @@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) - -/* * The SVM_FLAG_PRIVATE_PASID flag requests a PASID which is *not* the "main" * PASID for the current process. Even if a PASID already exists, a new one
@@ -60,7 +48,6 @@ struct svm_dev_ops {* @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
@@ -82,8 +69,7 @@ struct svm_dev_ops {* 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,int flags, -struct svm_dev_ops *ops); +extern int intel_svm_bind_mm (struct device *dev,int *pasid,int flags);/** * intel_svm_unbind_mm() - Unbind a specified PASID
@@ -105,7 +91,7 @@ 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, -int flags,struct svm_dev_ops *ops) +int flags) {return -ENOSYS ; }
@@ -116,6 +102,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, 0, NULL)) +#define intel_svm_available (dev) (!intel_svm_bind_mm((dev), NULL, 0))#endif /* __INTEL_SVM_H__ */
Signature · 1 line
2.7.4