Shared Virtual Addressing for the IOMMU
Subsystems (13)
v2 cover Shared Virtual Addressing for the IOMMU
This is version 2 of the Shared Virtual Addressing (SVA) series, which adds process address space management (1-6) and I/O page faults (7-9) to the IOMMU core. It also includes two example users of the API: VFIO as device driver (10-13), and Arm SMMUv3 as IOMMU driver (14-40).
The series is getting bulky and although it will remove lots of context, I'll probably split next version into IOMMU, VFIO and SMMU changes. This time around I also Cc'd the mm list even though get_maintainers doesn't pick it up, because I'd like to export a few symbols in patches 10-12 and because of the recent interest in sharing mm with devices [1], which seems somewhat related.
Major changes since v1 [2]:
* Removed bind_group(). Only bind_device() is supported. Supporting multi-device groups is difficult and unnecessary at the moment.
* Reworked the I/O Page Fault code to support multiple queues. IOMMU driver now registers IOPF queues as needed (for example one per IOMMU device), then attach devices that require IOPF (for example, in sva_device_init)
* mm_exit callback is allowed to sleep. It is now registered with init_sva_device_init() instead of a separate function.
* Remove IOMMU_SVA_FEAT_PASID, making PASID support mandatory for now. If a future series implements !PASID, it will have to introduce flag IOMMU_SVA_FEAT_NO_PASID.
* Removed the atomic/blocking handler distinction for now. It is a bit irrelevant here and can be added back later.
* Tried to address all comments. Please let me know if I missed something.
The series relies on Jacob Pan's fault reporting work, currently under discussion [3].
You can pull everything from:
git://linux-arm.org/linux-jpb.git sva/v2
Branch sva/debug contains tracepoints and other tools that I found useful for validation.
[1] https://lwn.net/Articles/753481/
[2] https://www.mail-archive.com/iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA@public.gmane.org/msg21813.html
[3] IOMMU and VT-d driver support for Shared Virtual Address (SVA)
https://www.mail-archive.com/iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA@public.gmane.org/msg22640.html ---
If you're unfamiliar with SVM/SVA, a wordy description copied from previous version follows.
Shared Virtual Addressing (SVA) is the ability to share process address spaces with devices. It is called "SVM" (Shared Virtual Memory) by OpenCL and some IOMMU architectures, but since that abbreviation is already used for AMD virtualisation in Linux (Secure Virtual Machine), we prefer the less ambiguous "SVA".
Sharing process address spaces with devices allows to rely on core kernel memory management for DMA, removing some complexity from application and device drivers. After binding to a device, applications can instruct it to perform DMA on buffers obtained with malloc.
The device, bus and IOMMU must support the following features:
* Multiple address spaces per device, for example using the PCI PASID (Process Address Space ID) extension. The IOMMU driver allocates a PASID and the device uses it in DMA transactions.
* I/O Page Faults (IOPF), for example PCI PRI (Page Request Interface) or Arm SMMU stall. The core mm handles translation faults from the IOMMU.
* MMU and IOMMU implement compatible page table formats.
This series requires to support all three features. Upcoming patches will enable private PASID management, which doesn't share page tables and augments the map/unmap API with PASIDs.
Although we don't have any performance measurement at the moment, SVA will likely be slower than classical DMA since it relies on page faults, whereas classical DMA pins all pages in memory. SVA mostly aims at simplifying DMA management, but also improves security by isolating address spaces in devices.
Jean-Philippe Brucker (40):
iommu: Introduce Shared Virtual Addressing API
iommu/sva: Bind process address spaces to devices iommu/sva: Manage process address spaces
iommu/sva: Add a mm_exit callback for device drivers iommu/sva: Track mm changes with an MMU notifier iommu/sva: Search mm by PASID
iommu: Add a page fault handler
iommu/iopf: Handle mm faults
iommu/sva: Register page fault handler
mm: export symbol mm_access
mm: export symbol find_get_task_by_vpid
mm: export symbol mmput_async
vfio: Add support for Shared Virtual Addressing
dt-bindings: document stall and PASID properties for IOMMU masters iommu/of: Add stall and pasid properties to iommu_fwspec
arm64: mm: Pin down ASIDs for sharing mm with devices iommu/arm-smmu-v3: Link domains and devices iommu/io-pgtable-arm: Factor out ARM LPAE register defines
iommu: Add generic PASID table library
iommu/arm-smmu-v3: Move context descriptor code iommu/arm-smmu-v3: Add support for Substream IDs iommu/arm-smmu-v3: Add second level of context descriptor table iommu/arm-smmu-v3: Share process page tables iommu/arm-smmu-v3: Seize private ASID
iommu/arm-smmu-v3: Add support for VHE
iommu/arm-smmu-v3: Enable broadcast TLB maintenance iommu/arm-smmu-v3: Add SVA feature checking iommu/arm-smmu-v3: Implement mm operations iommu/arm-smmu-v3: Add support for Hardware Translation Table Update iommu/arm-smmu-v3: Register I/O Page Fault queue iommu/arm-smmu-v3: Improve add_device error handling iommu/arm-smmu-v3: Maintain a SID->device structure iommu/arm-smmu-v3: Add stall support for platform devices ACPI/IORT: Check ATS capability in root complex nodes iommu/arm-smmu-v3: Add support for PCI ATS iommu/arm-smmu-v3: Hook up ATC invalidation to mm ops iommu/arm-smmu-v3: Disable tagged pointers
PCI: Make "PRG Response PASID Required" handling common iommu/arm-smmu-v3: Add support for PRI
iommu/arm-smmu-v3: Add support for PCI PASID
.../devicetree/bindings/iommu/iommu.txt | 24 + MAINTAINERS | 6 +- arch/arm64/include/asm/mmu.h | 1 + arch/arm64/include/asm/mmu_context.h | 11 +- arch/arm64/mm/context.c | 92 +- drivers/acpi/arm64/iort.c | 11 + drivers/iommu/Kconfig | 29 + drivers/iommu/Makefile | 4 + drivers/iommu/amd_iommu.c | 19 +- drivers/iommu/arm-smmu-v3-context.c | 700 ++++++++ drivers/iommu/arm-smmu-v3.c | 1438 ++++++++++++++--- drivers/iommu/io-pgfault.c | 445 +++++ drivers/iommu/io-pgtable-arm.c | 49 +- drivers/iommu/io-pgtable-arm.h | 54 + drivers/iommu/iommu-pasid-table.c | 52 + drivers/iommu/iommu-pasid-table.h | 177 ++ drivers/iommu/iommu-sva.c | 792 +++++++++ drivers/iommu/iommu.c | 84 + drivers/iommu/of_iommu.c | 12 + drivers/pci/ats.c | 17 + drivers/vfio/vfio_iommu_type1.c | 449 ++++- include/linux/iommu.h | 184 +++ include/linux/pci-ats.h | 8 + include/uapi/linux/pci_regs.h | 1 + include/uapi/linux/vfio.h | 76 + kernel/fork.c | 15 + kernel/pid.c | 1 + 27 files changed, 4469 insertions(+), 282 deletions(-) create mode 100644 drivers/iommu/arm-smmu-v3-context.c create mode 100644 drivers/iommu/io-pgfault.c create mode 100644 drivers/iommu/io-pgtable-arm.h create mode 100644 drivers/iommu/iommu-pasid-table.c create mode 100644 drivers/iommu/iommu-pasid-table.h create mode 100644 drivers/iommu/iommu-sva.c
Signature · 1 line
2.17.0
PATCH v2 1/40 iommu: Introduce Shared Virtual Addressing API
Shared Virtual Addressing (SVA) provides a way for device drivers to bind process address spaces to devices. This requires the IOMMU to support page table format and features compatible with the CPUs, and usually requires the system to support I/O Page Faults (IOPF) and Process Address Space ID (PASID). When all of these are available, DMA can access virtual addresses of a process. A PASID is allocated for each process, and the device driver programs it into the device in an implementation-specific way.
Add a new API for sharing process page tables with devices. Introduce two IOMMU operations, sva_device_init() and sva_device_shutdown(), that prepare the IOMMU driver for SVA. For example allocate PASID tables and fault queues. Subsequent patches will implement the bind() and unbind() operations.
Support for I/O Page Faults will be added in a later patch using a new feature bit (IOMMU_SVA_FEAT_IOPF). With the current API users must pin down all shared mappings. Other feature bits that may be added in the future are IOMMU_SVA_FEAT_PRIVATE, to support private PASID address spaces, and IOMMU_SVA_FEAT_NO_PASID, to bind the whole device address space to a process.
---v1->v2:
* Add sva_param structure to iommu_param
* CONFIG option is only selectable by IOMMU drivers
--- drivers/iommu/Kconfig | 4 ++ drivers/iommu/Makefile | 1 + drivers/iommu/iommu-sva.c | 110 ++++++++++++++++++++++++++++++++++++++ include/linux/iommu.h | 32 +++++++++++ 4 files changed, 147 insertions(+) create mode 100644 drivers/iommu/iommu-sva.c
drivers/iommu/Kconfig +4 -0
@@ -74,6 +74,10 @@ config IOMMU_DMA select IOMMU_IOVA select NEED_SG_DMA_LENGTH +config IOMMU_SVA + bool + select IOMMU_API + config FSL_PAMU bool "Freescale IOMMU support" depends on PCI
drivers/iommu/Makefile +1 -0
@@ -3,6 +3,7 @@ obj-$(CONFIG_IOMMU_API) += iommu.o obj-$(CONFIG_IOMMU_API) += iommu-traces.o obj-$(CONFIG_IOMMU_API) += iommu-sysfs.o obj-$(CONFIG_IOMMU_DMA) += dma-iommu.o +obj-$(CONFIG_IOMMU_SVA) += iommu-sva.o obj-$(CONFIG_IOMMU_IO_PGTABLE) += io-pgtable.o obj-$(CONFIG_IOMMU_IO_PGTABLE_ARMV7S) += io-pgtable-arm-v7s.o obj-$(CONFIG_IOMMU_IO_PGTABLE_LPAE) += io-pgtable-arm.o
drivers/iommu/iommu-sva.c new file +110 -0
110 lines not on this page · raw message
include/linux/iommu.h +32 -0
@@ -212,6 +212,12 @@ struct page_response_msg {u64 private_data; }; +struct iommu_sva_param { +unsigned long features; +unsigned int min_pasid; +unsigned int max_pasid; +}; +/** * struct iommu_ops - iommu ops and capabilities * @capable: check capability
@@ -219,6 +225,8 @@ struct page_response_msg {* @domain_free: free iommu domain * @attach_dev: attach device to an iommu domain * @detach_dev: detach device from an iommu domain +* @sva_device_init: initialize Shared Virtual Adressing for a device +* @sva_device_shutdown: shutdown Shared Virtual Adressing for a device * @map: map a physically contiguous memory region to an iommu domain * @unmap: unmap a physically contiguous memory region from an iommu domain * @map_sg: map a scatter-gather list of physically contiguous memory chunks
@@ -256,6 +264,10 @@ struct iommu_ops {int (*attach_dev)(struct iommu_domain *domain,struct device *dev);void (*detach_dev)(struct iommu_domain *domain,struct device *dev); +int (*sva_device_init)(struct device *dev, +struct iommu_sva_param *param); +void (*sva_device_shutdown)(struct device *dev, +struct iommu_sva_param *param);int (*map)(struct iommu_domain *domain,unsigned long iova,phys_addr_t paddr,size_t size,int prot); size_t (*unmap)(struct iommu_domain *domain, unsigned long iova,
@@ -413,6 +425,7 @@ struct iommu_fault_param {* struct iommu_param - collection of per-device IOMMU data * * @fault_param: IOMMU detected device fault reporting data +* @sva_param: SVA parameters * * TODO: migrate other per device data pointers under iommu_dev_data, e.g. * struct iommu_group *iommu_group;
@@ -421,6 +434,7 @@ struct iommu_fault_param {struct iommu_param {struct mutex lock;struct iommu_fault_param *fault_param; +struct iommu_sva_param *sva_param; };int iommu_device_register (struct iommu_device *iommu);
@@ -920,4 +934,22 @@ static inline int iommu_sva_invalidate(struct iommu_domain *domain,#endif /* CONFIG_IOMMU_API */ +#ifdef CONFIG_IOMMU_SVA +extern int iommu_sva_device_init (struct device *dev,unsigned long features, +unsigned int max_pasid); +extern int iommu_sva_device_shutdown (struct device *dev); +#else /* CONFIG_IOMMU_SVA */ +static inline int iommu_sva_device_init (struct device *dev, +unsigned long features, +unsigned int max_pasid) +{ +return -ENODEV ; +} + +static inline int iommu_sva_device_shutdown (struct device *dev) +{ +return -ENODEV ; +} +#endif /* CONFIG_IOMMU_SVA */ +#endif /* __LINUX_IOMMU_H */
Signature · 1 line
2.17.0
Re: [PATCH v2 01/40] iommu: Introduce Shared Virtual Addressing API
Quoted 92 lines · On Fri, 11 May 2018 20:06:02 +0100 Jean-Philippe Brucker…
Shared Virtual Addressing (SVA) provides a way for device drivers to bind process address spaces to devices. This requires the IOMMU to support page table format and features compatible with the CPUs, and usually requires the system to support I/O Page Faults (IOPF) and Process Address Space ID (PASID). When all of these are available, DMA can access virtual addresses of a process. A PASID is allocated for each process, and the device driver programs it into the device in an implementation-specific way.
Add a new API for sharing process page tables with devices. Introduce two IOMMU operations,
sva_device_init()andsva_device_shutdown(), that prepare the IOMMU driver for SVA. For example allocate PASID tables and fault queues. Subsequent patches will implement thebind()andunbind()operations.Support for I/O Page Faults will be added in a later patch using a new feature bit (
IOMMU_SVA_FEAT_IOPF). With the current API users must pin down all shared mappings. Other feature bits that may be added in the future areIOMMU_SVA_FEAT_PRIVATE, to support private PASID address spaces, andIOMMU_SVA_FEAT_NO_PASID, to bind the whole device address space to a process.Signed-off-by Jean-Philippe Brucker <[email protected]>---
v1->v2:
* Addsva_paramstructure toiommu_param
* CONFIG option is only selectable by IOMMU drivers--- drivers/iommu/Kconfig | 4 ++ drivers/iommu/Makefile | 1 + drivers/iommu/iommu-sva.c | 110 ++++++++++++++++++++++++++++++++++++++ include/linux/iommu.h | 32 +++++++++++ 4 files changed, 147 insertions(+) create mode 100644 drivers/iommu/iommu-sva.cdrivers/iommu/Kconfig +4
-0@@ -74,6 +74,10 @@ config IOMMU_DMA select IOMMU_IOVA select NEED_SG_DMA_LENGTH +config IOMMU_SVA + bool + select IOMMU_API + config FSL_PAMU bool "Freescale IOMMU support" depends on PCIdrivers/iommu/Makefile +1
-0@@ -3,6 +3,7 @@ obj-$(CONFIG_IOMMU_API) += iommu.o obj-$(CONFIG_IOMMU_API) += iommu-traces.o obj-$(CONFIG_IOMMU_API) += iommu-sysfs.o obj-$(CONFIG_IOMMU_DMA) += dma-iommu.o +obj-$(CONFIG_IOMMU_SVA) += iommu-sva.o obj-$(CONFIG_IOMMU_IO_PGTABLE) += io-pgtable.o obj-$(CONFIG_IOMMU_IO_PGTABLE_ARMV7S) += io-pgtable-arm-v7s.o obj-$(CONFIG_IOMMU_IO_PGTABLE_LPAE) += io-pgtable-arm.odrivers/iommu/iommu-sva.c new file +12
-0@@ -0,0 +1,110 @@ +// SPDX-License-Identifier: GPL-2.0 +/* +* Manage PASIDs and bind process address spaces to devices. +* +* Copyright (C) 2018 ARM Ltd. +*/ + +#include <linux/iommu.h> +#include <linux/slab.h> + +/** +* iommu_sva_device_init() - Initialize Shared Virtual Addressing for a device
+ * @dev: the device
+ * @features: bitmask of features that need to be initialized
+ * @max_pasid: max PASID value supported by the device
+ *
+ * Users of the bind()/unbind() API must call this function to initialize all
+ * features required for SVA.
+ *
+ * The device must support multiple address spaces (e.g. PCI PASID). By default
Quoted 8 lines
+ * the PASID allocated during
bind()is limited by the IOMMU capacity, and by
+ * the device PASID width defined in the PCI capability or in the firmware
+ * description. Setting @max_pasid to a non-zero value smaller than this limit
+ * overrides it.
+ *
seems the min_pasid never gets used. do you really need it?
Quoted 8 lines
+ * The device should not be performing any DMA while this function is running,
+ * otherwise the behavior is undefined.
+ *
+ * Return 0 if initialization succeeded, or an error.
+ */drivers/iommu/iommu-sva.c new file +2
-0+int iommu_sva_device_init (struct device *dev,unsigned long features, +unsigned int max_pasid)
drivers/iommu/iommu-sva.c line 42
Quoted 10 lines
drivers/iommu/iommu-sva.c new file +10
-0+{ +int ret; +struct iommu_sva_param *param; +struct iommu_domain *domain =iommu_get_domain_for_dev (dev); + +if (!domain || !domain->ops->sva_device_init) +return -ENODEV ; + +if (features) +return -EINVAL ;
should it be !features?
Quoted 55 lines
drivers/iommu/iommu-sva.c new file +10
-0+ + param =kzalloc (sizeof (*param),GFP_KERNEL ); +if (!param) +return -ENOMEM ; + + param->features = features; + param->max_pasid = max_pasid; + +/* +* IOMMU driver updates the limits depending on the IOMMU and device
drivers/iommu/iommu-sva.c new file +30
-0+* capabilities. +*/ + ret = domain->ops->sva_device_init (dev, param); +if (ret) + goto err_free_param; + +mutex_lock (&dev->iommu_param->lock); +if (dev->iommu_param->sva_param) + ret = -EEXIST ; +else + dev->iommu_param->sva_param = param; +mutex_unlock (&dev->iommu_param->lock); +if (ret) + goto err_device_shutdown; + +return 0 ; + +err_device_shutdown: +if (domain->ops->sva_device_shutdown) + domain->ops->sva_device_shutdown (dev, param); + +err_free_param: +kfree (param); + +return ret; +} +EXPORT_SYMBOL_GPL (iommu_sva_device_init); + +/** +* iommu_sva_device_shutdown() - Shutdown Shared Virtual Addressing for a device
+ * @dev: the device
+ *
+ * Disable SVA. Device driver should ensure that the device isn't performing any
+ * DMA while this function is running.
+ */drivers/iommu/iommu-sva.c new file +7
-0+int iommu_sva_device_shutdown (struct device *dev) +{ +struct iommu_sva_param *param; +struct iommu_domain *domain =iommu_get_domain_for_dev (dev); + +if (!domain) +return -ENODEV ;
drivers/iommu/iommu-sva.c line 104
Quoted 10 lines
drivers/iommu/iommu-sva.c new file +10
-0+ +mutex_lock (&dev->iommu_param->lock); + param = dev->iommu_param->sva_param; + dev->iommu_param->sva_param =NULL ; +mutex_unlock (&dev->iommu_param->lock); +if (!param) +return -ENODEV ; + +if (domain->ops->sva_device_shutdown) + domain->ops->sva_device_shutdown (dev, param);
seems a little mismatch here, do you need pass the param. I don't think there is anything else model specific iommu driver need to do for the param.
Quoted 86 lines
drivers/iommu/iommu-sva.c new file +6
-0+ +kfree (param); + +return 0 ; +} +EXPORT_SYMBOL_GPL (iommu_sva_device_shutdown);include/linux/iommu.h +8
-0@@ -212,6 +212,12 @@ struct page_response_msg {u64 private_data; }; +struct iommu_sva_param { +unsigned long features; +unsigned int min_pasid; +unsigned int max_pasid; +}; +/** * struct iommu_ops - iommu ops and capabilities * @capable: check capability @@ -219,6 +225,8 @@ struct page_response_msg {* @domain_free: free iommu domain * @attach_dev: attach device to an iommu domain * @detach_dev: detach device from an iommu domain +* @sva_device_init: initialize Shared Virtual Adressing for a device +* @sva_device_shutdown: shutdown Shared Virtual Adressing for a device
* @map: map a physically contiguous memory region to an iommu domain
* @unmap: unmap a physically contiguous memory region from an iommu
domain
* @map_sg: map a scatter-gather list of physically contiguous
memory chunks @@ -256,6 +264,10 @@ structiommu_ops{int (*attach_dev)(struct
iommu_domain*domain, struct device
*dev); void (*detach_dev)(structiommu_domain*domain, struct device *dev);include/linux/iommu.h +4
-0+int (*sva_device_init)(struct device *dev, +struct iommu_sva_param *param); +void (*sva_device_shutdown)(struct device *dev, +struct iommu_sva_param *param);int (*map)(struct iommu_domain *domain,unsigned long iova,phys_addr_t paddr,size_t size,int prot); size_t (*unmap)(struct iommu_domain *domain, unsigned longiova, @@ -413,6 +425,7 @@ struct
iommu_fault_param{
* structiommu_param- collection of per-device IOMMU data *
* @fault_param: IOMMU detected device fault reporting data
+ * @sva_param: SVA parameters
*
* TODO: migrate other per device data pointers underiommu_dev_data, e.g.
* structiommu_group*iommu_group;include/linux/iommu.h +19
-0@@ -421,6 +434,7 @@ struct iommu_fault_param {struct iommu_param {struct mutex lock;struct iommu_fault_param *fault_param; +struct iommu_sva_param *sva_param; };int iommu_device_register (struct iommu_device *iommu);@@ -920,4 +934,22 @@ static inline int iommu_sva_invalidate(struct iommu_domain *domain,#endif /* CONFIG_IOMMU_API */ +#ifdef CONFIG_IOMMU_SVA +extern int iommu_sva_device_init (struct device *dev,unsigned long features, +unsigned int max_pasid); +extern int iommu_sva_device_shutdown (struct device *dev); +#else /* CONFIG_IOMMU_SVA */ +static inline int iommu_sva_device_init (struct device *dev, +unsigned long features, +unsigned int max_pasid) +{ +return -ENODEV ; +} + +static inline int iommu_sva_device_shutdown (struct device *dev) +{ +return -ENODEV ; +} +#endif /* CONFIG_IOMMU_SVA */ +
Quoted 1 line
#endif /*
__LINUX_IOMMU_H*/
[Jacob Pan]
Re: [PATCH v2 01/40] iommu: Introduce Shared Virtual Addressing API
Hi Jacob,
Thanks for reviewing this
Quoted 11 lines · Jacob Pan
Quoted 10 lines
+ * The device must support multiple address spaces (e.g. PCI PASID). By default
+ * the PASID allocated duringbind()is limited by the IOMMU capacity, and by
+ * the device PASID width defined in the PCI capability or in the firmware
+ * description. Setting @max_pasid to a non-zero value smaller than this limit
+ * overrides it.
+ *seems the
min_pasidnever gets used. do you really need it?
Yes, the SMMU sets it to 1 in patch 28/40, because it needs to reserve PASID 0
Quoted 11 lines
Quoted 11 lines
+ * The device should not be performing any DMA while this function is running,
+ * otherwise the behavior is undefined.
+ *
+ * Return 0 if initialization succeeded, or an error.
+ */drivers/iommu/iommu-sva.c new file +5
-0+int iommu_sva_device_init (struct device *dev,unsigned long features, +unsigned int max_pasid) +{ +int ret; +struct iommu_sva_param *param;
Quoted 8 lines
drivers/iommu/iommu-sva.c line 42
Quoted 7 lines
drivers/iommu/iommu-sva.c new file +7
-0+struct iommu_domain *domain =iommu_get_domain_for_dev (dev); + +if (!domain || !domain->ops->sva_device_init) +return -ENODEV ; + +if (features) +return -EINVAL ;should it be !features?
This checks if the user sets any unsupported bit in features. No feature is supported right now, but patch 09/40 adds IOMMU_SVA_FEAT_IOPF, and changes this line to "features & ~IOMMU_SVA_FEAT_IOPF"
Quoted 4 lines
Quoted 4 lines
drivers/iommu/iommu-sva.c new file +4
-0+mutex_lock (&dev->iommu_param->lock); + param = dev->iommu_param->sva_param; + dev->iommu_param->sva_param =NULL ; +mutex_unlock (&dev->iommu_param->lock);
Quoted 8 lines
drivers/iommu/iommu-sva.c line 104
Quoted 5 lines
drivers/iommu/iommu-sva.c new file +5
-0+if (!param) +return -ENODEV ; + +if (domain->ops->sva_device_shutdown) + domain->ops->sva_device_shutdown (dev, param);seems a little mismatch here, do you need pass the param. I don't think there is anything else model specific iommu driver need to do for the param.
SMMU doesn't use it, but maybe it would remind other IOMMU driver which features were enabled, so they don't have to keep track of that themselves? I can remove it if it isn't useful
Thanks,
Jean
Re: [PATCH v2 01/40] iommu: Introduce Shared Virtual Addressing API
Quoted 32 lines · On Thu, 17 May 2018 11:02:02 +0100 Jean-Philippe Brucker…
Hi Jacob,
Thanks for reviewing this
On 16/05/18 21:41, Jacob Pan wrote:
[...]Quoted 1 line
seems the
min_pasidnever gets used. do you really need it?Yes, the SMMU sets it to 1 in patch 28/40, because it needs to reserve PASID 0
[...]
Quoted 1 line
should it be !features?
This checks if the user sets any unsupported bit in features. No feature is supported right now, but patch 09/40 adds
IOMMU_SVA_FEAT_IOPF, and changes this line to "features & ~IOMMU_SVA_FEAT_IOPF"Quoted 4 lines
Quoted 4 lines
drivers/iommu/iommu-sva.c new file +4
-0+mutex_lock (&dev->iommu_param->lock); + param = dev->iommu_param->sva_param; + dev->iommu_param->sva_param =NULL ; +mutex_unlock (&dev->iommu_param->lock);Quoted 8 lines
drivers/iommu/iommu-sva.c line 104
Quoted 5 lines
drivers/iommu/iommu-sva.c new file +5
-0+if (!param) +return -ENODEV ; + +if (domain->ops->sva_device_shutdown) + domain->ops->sva_device_shutdown (dev, param);seems a little mismatch here, do you need pass the param. I don't think there is anything else model specific iommu driver need to do for the param.
Quoted 4 lines
SMMU doesn't use it, but maybe it would remind other IOMMU driver which features were enabled, so they don't have to keep track of that themselves? I can remove it if it isn't useful
If there is a use case, I guess iommu driver can always retrieve the param from struct device.
Quoted 2 lines
Thanks,
Jean
[Jacob Pan]
Re: [PATCH v2 01/40] iommu: Introduce Shared Virtual Addressing API
In reply to Jean-Philippe BruckerHi Jean-Philippe,
Quoted 90 lines · Jean-Philippe Brucker
Shared Virtual Addressing (SVA) provides a way for device drivers to bind process address spaces to devices. This requires the IOMMU to support page table format and features compatible with the CPUs, and usually requires the system to support I/O Page Faults (IOPF) and Process Address Space ID (PASID). When all of these are available, DMA can access virtual addresses of a process. A PASID is allocated for each process, and the device driver programs it into the device in an implementation-specific way.
Add a new API for sharing process page tables with devices. Introduce two IOMMU operations,
sva_device_init()andsva_device_shutdown(), that prepare the IOMMU driver for SVA. For example allocate PASID tables and fault queues. Subsequent patches will implement thebind()andunbind()operations.Support for I/O Page Faults will be added in a later patch using a new feature bit (
IOMMU_SVA_FEAT_IOPF). With the current API users must pin down all shared mappings. Other feature bits that may be added in the future areIOMMU_SVA_FEAT_PRIVATE, to support private PASID address spaces, andIOMMU_SVA_FEAT_NO_PASID, to bind the whole device address space to a process.Signed-off-by Jean-Philippe Brucker <[email protected]>---
v1->v2:
* Addsva_paramstructure toiommu_param
* CONFIG option is only selectable by IOMMU drivers--- drivers/iommu/Kconfig | 4 ++ drivers/iommu/Makefile | 1 + drivers/iommu/iommu-sva.c | 110 ++++++++++++++++++++++++++++++++++++++ include/linux/iommu.h | 32 +++++++++++ 4 files changed, 147 insertions(+) create mode 100644 drivers/iommu/iommu-sva.cdrivers/iommu/Kconfig +4
-0@@ -74,6 +74,10 @@ config IOMMU_DMA select IOMMU_IOVA select NEED_SG_DMA_LENGTH +config IOMMU_SVA + bool + select IOMMU_API + config FSL_PAMU bool "Freescale IOMMU support" depends on PCIdrivers/iommu/Makefile +1
-0@@ -3,6 +3,7 @@ obj-$(CONFIG_IOMMU_API) += iommu.o obj-$(CONFIG_IOMMU_API) += iommu-traces.o obj-$(CONFIG_IOMMU_API) += iommu-sysfs.o obj-$(CONFIG_IOMMU_DMA) += dma-iommu.o +obj-$(CONFIG_IOMMU_SVA) += iommu-sva.o obj-$(CONFIG_IOMMU_IO_PGTABLE) += io-pgtable.o obj-$(CONFIG_IOMMU_IO_PGTABLE_ARMV7S) += io-pgtable-arm-v7s.o obj-$(CONFIG_IOMMU_IO_PGTABLE_LPAE) += io-pgtable-arm.odrivers/iommu/iommu-sva.c new file +22
-0@@ -0,0 +1,110 @@ +// SPDX-License-Identifier: GPL-2.0 +/* +* Manage PASIDs and bind process address spaces to devices. +* +* Copyright (C) 2018 ARM Ltd. +*/ + +#include <linux/iommu.h> +#include <linux/slab.h> + +/** +* iommu_sva_device_init() - Initialize Shared Virtual Addressing for a device +* @dev: the device +* @features: bitmask of features that need to be initialized +* @max_pasid: max PASID value supported by the device +* +* Users of the bind()/unbind() API must call this function to initialize all +* features required for SVA. +* +* The device must support multiple address spaces (e.g. PCI PASID). By default +* the PASID allocated during bind() is limited by the IOMMU capacity, and by +* the device PASID width defined in the PCI capability or in the firmware
drivers/iommu/iommu-sva.c line 32
Quoted 10 lines
drivers/iommu/iommu-sva.c new file +10
-0+* description. Setting @max_pasid to a non-zero value smaller than this limit +* overrides it. +* +* The device should not be performing any DMA while this function is running, +* otherwise the behavior is undefined. +* +* Return 0 if initialization succeeded, or an error. +*/ +int iommu_sva_device_init (struct device *dev,unsigned long features, +unsigned int max_pasid)
what about min_pasid?
Quoted 15 lines
drivers/iommu/iommu-sva.c new file +15
-0+{ +int ret; +struct iommu_sva_param *param; +struct iommu_domain *domain =iommu_get_domain_for_dev (dev); + +if (!domain || !domain->ops->sva_device_init) +return -ENODEV ; + +if (features) +return -EINVAL ; + + param =kzalloc (sizeof (*param),GFP_KERNEL ); +if (!param) +return -ENOMEM ; +
drivers/iommu/iommu-sva.c line 57
Quoted 10 lines
drivers/iommu/iommu-sva.c new file +10
-0+ param->features = features; + param->max_pasid = max_pasid; + +/* +* IOMMU driver updates the limits depending on the IOMMU and device +* capabilities. +*/ + ret = domain->ops->sva_device_init (dev, param); +if (ret) + goto err_free_param;
So you are likely to call sva_device_init even if it was already called (ie. dev->iommu_param->sva_param is already set). Can't you test whether dev->iommu_param->sva_param is already set first?
Quoted 21 lines
drivers/iommu/iommu-sva.c new file +21
-0+ +mutex_lock (&dev->iommu_param->lock); +if (dev->iommu_param->sva_param) + ret = -EEXIST ; +else + dev->iommu_param->sva_param = param; +mutex_unlock (&dev->iommu_param->lock); +if (ret) + goto err_device_shutdown; + +return 0 ; + +err_device_shutdown: +if (domain->ops->sva_device_shutdown) + domain->ops->sva_device_shutdown (dev, param); + +err_free_param: +kfree (param); + +return ret; +}
drivers/iommu/iommu-sva.c line 88
Quoted 10 lines
drivers/iommu/iommu-sva.c new file +10
-0+EXPORT_SYMBOL_GPL (iommu_sva_device_init); + +/** +* iommu_sva_device_shutdown() - Shutdown Shared Virtual Addressing for a device +* @dev: the device +* +* Disable SVA. Device driver should ensure that the device isn't performing any +* DMA while this function is running. +*/ +int iommu_sva_device_shutdown (struct device *dev)
Not sure the returned value is required for a shutdown operation.
Quoted 27 lines
drivers/iommu/iommu-sva.c new file +22
-0+{ +struct iommu_sva_param *param; +struct iommu_domain *domain =iommu_get_domain_for_dev (dev); + +if (!domain) +return -ENODEV ; + +mutex_lock (&dev->iommu_param->lock); + param = dev->iommu_param->sva_param; + dev->iommu_param->sva_param =NULL ; +mutex_unlock (&dev->iommu_param->lock); +if (!param) +return -ENODEV ; + +if (domain->ops->sva_device_shutdown) + domain->ops->sva_device_shutdown (dev, param); + +kfree (param); + +return 0 ; +} +EXPORT_SYMBOL_GPL (iommu_sva_device_shutdown);include/linux/iommu.h +0
-0
include/linux/iommu.h struct page_response_msg { · line 215
Quoted 4 lines
include/linux/iommu.h +1
-0@@ -212,6 +212,12 @@ struct page_response_msg {u64 private_data; }; +struct iommu_sva_param {
What are the feature values?
include/linux/iommu.h struct page_response_msg { · line 228
Quoted 13 lines
include/linux/iommu.h +6
-0+unsigned long features; +unsigned int min_pasid; +unsigned int max_pasid; +}; +/** * struct iommu_ops - iommu ops and capabilities * @capable: check capability @@ -219,6 +225,8 @@ struct page_response_msg {* @domain_free: free iommu domain * @attach_dev: attach device to an iommu domain * @detach_dev: detach device from an iommu domain +* @sva_device_init: initialize Shared Virtual Adressing for a device
Addressing
include/linux/iommu.h struct page_response_msg { · line 229
Quoted 1 line
include/linux/iommu.h +1
-0+* @sva_device_shutdown: shutdown Shared Virtual Adressing for a device
Addressing
Quoted 44 lines
include/linux/iommu.h +15
-0* @map: map a physically contiguous memory region to an iommu domain * @unmap: unmap a physically contiguous memory region from an iommu domain * @map_sg: map a scatter-gather list of physically contiguous memory chunks @@ -256,6 +264,10 @@ struct iommu_ops {int (*attach_dev)(struct iommu_domain *domain,struct device *dev);void (*detach_dev)(struct iommu_domain *domain,struct device *dev); +int (*sva_device_init)(struct device *dev, +struct iommu_sva_param *param); +void (*sva_device_shutdown)(struct device *dev, +struct iommu_sva_param *param);int (*map)(struct iommu_domain *domain,unsigned long iova,phys_addr_t paddr,size_t size,int prot); size_t (*unmap)(struct iommu_domain *domain, unsigned long iova,@@ -413,6 +425,7 @@ struct iommu_fault_param {* struct iommu_param - collection of per-device IOMMU data * * @fault_param: IOMMU detected device fault reporting data +* @sva_param: SVA parameters * * TODO: migrate other per device data pointers under iommu_dev_data, e.g. * struct iommu_group *iommu_group; @@ -421,6 +434,7 @@ struct iommu_fault_param {struct iommu_param {struct mutex lock;struct iommu_fault_param *fault_param; +struct iommu_sva_param *sva_param; };int iommu_device_register (struct iommu_device *iommu);@@ -920,4 +934,22 @@ static inline int iommu_sva_invalidate(struct iommu_domain *domain,#endif /* CONFIG_IOMMU_API */ +#ifdef CONFIG_IOMMU_SVA +extern int iommu_sva_device_init (struct device *dev,unsigned long features, +unsigned int max_pasid); +extern int iommu_sva_device_shutdown (struct device *dev); +#else /* CONFIG_IOMMU_SVA */ +static inline int iommu_sva_device_init (struct device *dev, +unsigned long features, +unsigned int max_pasid) +{
include/linux/iommu.h static inline int iommu_sva_invalidate(struct iommu_domain *domain, · line 955
Quoted 10 lines
include/linux/iommu.h +9
-0+return -ENODEV ; +} + +static inline int iommu_sva_device_shutdown (struct device *dev) +{ +return -ENODEV ; +} +#endif /* CONFIG_IOMMU_SVA */ +#endif /* __LINUX_IOMMU_H */
Thanks
Eric
Re: [PATCH v2 01/40] iommu: Introduce Shared Virtual Addressing API
Hi Eric,
Thanks for reviewing
Quoted 3 lines · Auger Eric
drivers/iommu/iommu-sva.c line 32
Quoted 2 lines
drivers/iommu/iommu-sva.c new file +2
-0+int iommu_sva_device_init (struct device *dev,unsigned long features, +unsigned int max_pasid)what about
min_pasid?
No one asked for it... The max_pasid parameter is here for drivers that have vendor-specific PASID size limits, such as AMD KFD (see kfd_iommu_device_init and
https://patchwork.kernel.org/patch/9989307/#21389571). But in most cases the PASID size will only depend on the PCI PASID capability and the IOMMU limits, both known by the IOMMU driver, so device drivers won't have to set max_pasid.
IOMMU drivers need to set min_pasid in the sva_device_init callback because it may be either 1 (e.g. Arm where PASID #0 is reserved) or 0 (Intel Vt-d rev2), but at the moment I can't see a reason for device drivers to override min_pasid
Quoted 10 lines
drivers/iommu/iommu-sva.c line 57
Quoted 7 lines
drivers/iommu/iommu-sva.c new file +7
-0+/* +* IOMMU driver updates the limits depending on the IOMMU and device +* capabilities. +*/ + ret = domain->ops->sva_device_init (dev, param); +if (ret) + goto err_free_param;So you are likely to call
sva_device_initeven if it was already called (ie.dev->iommu_param->sva_paramis already set). Can't you test whetherdev->iommu_param->sva_paramis already set first?
Right, that's probably better
Quoted 9 lines
drivers/iommu/iommu-sva.c line 88
Quoted 8 lines
drivers/iommu/iommu-sva.c new file +8
-0+/** +* iommu_sva_device_shutdown() - Shutdown Shared Virtual Addressing for a device +* @dev: the device +* +* Disable SVA. Device driver should ensure that the device isn't performing any +* DMA while this function is running. +*/ +int iommu_sva_device_shutdown (struct device *dev)Not sure the returned value is required for a shutdown operation.
I don't know either. I like them because they help me debug, but are otherwise rather useless if we don't describe precise semantics. The caller cannot do anything with it. Given that the corresponding IOMMU op is already void, I can change this function to void as well
Quoted 2 lines
Quoted 1 line
+struct
iommu_sva_param{What are the feature values?
At the moment only IOMMU_SVA_FEAT_IOPF, introduced by patch 09
Quoted 14 lines
include/linux/iommu.h struct page_response_msg { · line 228
Quoted 13 lines
include/linux/iommu.h +6
-0+unsigned long features; +unsigned int min_pasid; +unsigned int max_pasid; +}; +/** * struct iommu_ops - iommu ops and capabilities * @capable: check capability @@ -219,6 +225,8 @@ struct page_response_msg {* @domain_free: free iommu domain * @attach_dev: attach device to an iommu domain * @detach_dev: detach device from an iommu domain +* @sva_device_init: initialize Shared Virtual Adressing for a device Addressing
Quoted 2 lines
include/linux/iommu.h struct page_response_msg { · line 229
Quoted 1 line
include/linux/iommu.h +1
-0+* @sva_device_shutdown: shutdown Shared Virtual Adressing for a device Addressing
Nice catch
Thanks,
Jean
Re: [PATCH v2 01/40] iommu: Introduce Shared Virtual Addressing API
Am 06.09.2018 um 13:09 schrieb Jean-Philippe Brucker:
Quoted 16 lines
Hi Eric,
Thanks for reviewing
Quoted 3 lines · Auger Eric
drivers/iommu/iommu-sva.c line 32
Quoted 2 lines
drivers/iommu/iommu-sva.c new file +2
-0+int iommu_sva_device_init (struct device *dev,unsigned long features, +unsigned int max_pasid)what about
min_pasid?No one asked for it... The
max_pasidparameter is here for drivers that have vendor-specific PASID size limits, such as AMD KFD (seekfd_iommu_device_initand
https://patchwork.kernel.org/patch/9989307/#21389571). But in most cases the PASID size will only depend on the PCI PASID capability and the IOMMU limits, both known by the IOMMU driver, so device drivers won't have to setmax_pasid.
Quoted 4 lines
IOMMU drivers need to set
min_pasidin thesva_device_initcallback because it may be either 1 (e.g. Arm where PASID #0 is reserved) or 0 (Intel Vt-d rev2), but at the moment I can't see a reason for device drivers to overridemin_pasid
Sorry to ruin your day, but if I'm not completely mistaken PASID zero is reserved in the AMD KFD as well.
Regards,
Christian.
Quoted 51 lines
Quoted 11 lines
drivers/iommu/iommu-sva.c line 57
Quoted 8 lines
drivers/iommu/iommu-sva.c new file +7
-0+/* +* IOMMU driver updates the limits depending on the IOMMU and device +* capabilities. +*/ + ret = domain->ops->sva_device_init (dev, param); +if (ret) + goto err_free_param;So you are likely to call
sva_device_initeven if it was already called (ie.dev->iommu_param->sva_paramis already set). Can't you test whetherdev->iommu_param->sva_paramis already set first?Right, that's probably better
Quoted 9 lines
drivers/iommu/iommu-sva.c line 88
Quoted 8 lines
drivers/iommu/iommu-sva.c new file +8
-0+/** +* iommu_sva_device_shutdown() - Shutdown Shared Virtual Addressing for a device +* @dev: the device +* +* Disable SVA. Device driver should ensure that the device isn't performing any +* DMA while this function is running. +*/ +int iommu_sva_device_shutdown (struct device *dev)Not sure the returned value is required for a shutdown operation.
I don't know either. I like them because they help me debug, but are otherwise rather useless if we don't describe precise semantics. The caller cannot do anything with it. Given that the corresponding IOMMU op is already void, I can change this function to void as well
Quoted 2 lines
Quoted 1 line
+struct
iommu_sva_param{What are the feature values?
At the moment only
IOMMU_SVA_FEAT_IOPF, introduced by patch 09Quoted 14 lines
include/linux/iommu.h struct page_response_msg { · line 228
Quoted 13 lines
include/linux/iommu.h +6
-0+unsigned long features; +unsigned int min_pasid; +unsigned int max_pasid; +}; +/** * struct iommu_ops - iommu ops and capabilities * @capable: check capability @@ -219,6 +225,8 @@ struct page_response_msg {* @domain_free: free iommu domain * @attach_dev: attach device to an iommu domain * @detach_dev: detach device from an iommu domain +* @sva_device_init: initialize Shared Virtual Adressing for a device Addressing
Quoted 2 lines
include/linux/iommu.h struct page_response_msg { · line 229
Quoted 1 line
include/linux/iommu.h +1
-0+* @sva_device_shutdown: shutdown Shared Virtual Adressing for a device Addressing
Nice catch
Thanks,
Jean
Re: [PATCH v2 01/40] iommu: Introduce Shared Virtual Addressing API
Quoted 17 lines · Christian König
Am 06.09.2018 um 13:09 schrieb Jean-Philippe Brucker:
Quoted 16 lines
Hi Eric,
Thanks for reviewing
Quoted 3 lines · Auger Eric
Quoted 2 lines
+int
iommu_sva_device_init(struct device *dev, unsigned long features, + unsigned int max_pasid)what about
min_pasid?No one asked for it... The
max_pasidparameter is here for drivers that have vendor-specific PASID size limits, such as AMD KFD (seekfd_iommu_device_initand
https://patchwork.kernel.org/patch/9989307/#21389571). But in most cases the PASID size will only depend on the PCI PASID capability and the IOMMU limits, both known by the IOMMU driver, so device drivers won't have to setmax_pasid.
Quoted 7 lines
Quoted 4 lines
IOMMU drivers need to set
min_pasidin thesva_device_initcallback because it may be either 1 (e.g. Arm where PASID #0 is reserved) or 0 (Intel Vt-d rev2), but at the moment I can't see a reason for device drivers to overridemin_pasidSorry to ruin your day, but if I'm not completely mistaken PASID zero is reserved in the AMD KFD as well.
Heh, fair enough. I'll add the min_pasid parameter
Thanks,
Jean
_______________________________________________
iommu mailing list
[email protected]
https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [PATCH v2 01/40] iommu: Introduce Shared Virtual Addressing API
Am 06.09.2018 um 14:45 schrieb Jean-Philippe Brucker:
Quoted 18 lines · Christian König
Quoted 17 lines
Am 06.09.2018 um 13:09 schrieb Jean-Philippe Brucker:
Quoted 16 lines
Hi Eric,
Thanks for reviewing
Quoted 3 lines · Auger Eric
Quoted 2 lines
+int
iommu_sva_device_init(struct device *dev, unsigned long features, + unsigned int max_pasid)what about
min_pasid?No one asked for it... The
max_pasidparameter is here for drivers that have vendor-specific PASID size limits, such as AMD KFD (seekfd_iommu_device_initand
https://patchwork.kernel.org/patch/9989307/#21389571). But in most cases the PASID size will only depend on the PCI PASID capability and the IOMMU limits, both known by the IOMMU driver, so device drivers won't have to setmax_pasid.
Quoted 7 lines
Quoted 6 lines
Quoted 4 lines
IOMMU drivers need to set
min_pasidin thesva_device_initcallback because it may be either 1 (e.g. Arm where PASID #0 is reserved) or 0 (Intel Vt-d rev2), but at the moment I can't see a reason for device drivers to overridemin_pasidSorry to ruin your day, but if I'm not completely mistaken PASID zero is reserved in the AMD KFD as well.
Heh, fair enough. I'll add the
min_pasidparameter
I will take this as an opportunity to summarize some of the requirements we have for PASID management from the amdgpu driver point of view:
1. We need to be able to allocate PASID between 1 and some maximum. Zero is reserved as far as I know, but we don't necessary need a minimum.
2. We need to be able to allocate PASIDs without a process address space backing it. E.g. our hardware uses PASIDs even without Shared Virtual Addressing enabled to distinct clients from each other. Would be a pity if we need to still have a separate PASID handling because the system wide is only available when IOMMU is turned on.
3. Even after destruction of a process address space we need some grace period before a PASID is reused because it can be that the specific PASID is still in some hardware queues etc... At bare minimum all device drivers using process binding need to explicitly note to the core when they are done with a PASID.
4. It would be nice to have to be able to set a "void *" for each PASID/device combination while binding to a process which then can be queried later on based on the PASID. E.g. when you have a per PASID/device structure around anyway, just add an extra field.
5. It would be nice to have to allocate multiple PASIDs for the same process address space.
E.g. some teams at AMD want to use a separate GPU address space for their userspace client library. I'm still trying to avoid that, but it is perfectly possible that we are going to need that. Additional to that it is sometimes quite useful for debugging to isolate where exactly an incorrect access (segfault) is coming from.
Let me know if there are some problems with that, especially I want to know if there is pushback on #5 so that I can forward that :)
Thanks,
Christian.
Quoted 3 lines
Thanks,
Jean
_______________________________________________
iommu mailing list
[email protected]
https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [PATCH v2 01/40] iommu: Introduce Shared Virtual Addressing API
Quoted 2 lines · Christian König
I will take this as an opportunity to summarize some of the requirements we have for PASID management from the amdgpu driver point of view:
That's incredibly useful, thanks :)
Quoted 2 lines
1. We need to be able to allocate PASID between 1 and some maximum. Zero is reserved as far as I know, but we don't necessary need a minimum.
Should be fine. The PASID range is restricted by the PCI PASID capability, firmware description (for non-PCI devices), the IOMMU capacity, and what the device driver passes to iommu_sva_device_init. Not all IOMMUs reserve PASID 0 (AMD IOMMU without GIoSup doesn't, if I'm not mistaken), so the KFD driver will need to pass min_pasid=1 to make sure that 0 isn't allocated.
Quoted 5 lines
2. We need to be able to allocate PASIDs without a process address space backing it. E.g. our hardware uses PASIDs even without Shared Virtual Addressing enabled to distinct clients from each other.
Would be a pity if we need to still have a separate PASID
handling because the system wide is only available when IOMMU is turned on.
I'm still not sure about this one. From my point of view we shouldn't add to the IOMMU subsystem helpers for devices without an IOMMU. iommu-sva expects everywhere that the device has an iommu_domain, it's the first thing we check on entry. Bypassing all of this would call idr_alloc() directly, and wouldn't have any code in common with the current iommu-sva. So it seems like you need a layer on top of iommu-sva calling idr_alloc() when an IOMMU isn't present, but I don't think it should be in drivers/iommu/
Quoted 5 lines
3. Even after destruction of a process address space we need some grace period before a PASID is reused because it can be that the specific PASID is still in some hardware queues etc...
At bare minimum all device drivers using process binding need
to explicitly note to the core when they are done with a PASID.
Right, much of the horribleness in iommu-sva deals with this:
The process dies, iommu-sva is notified and calls the mm_exit() function passed by the device driver to iommu_sva_device_init(). In mm_exit() the device driver needs to clear any reference to the PASID in hardware and in its own structures. When the device driver returns from mm_exit(), it effectively tells the core that it has finished using the PASID, and iommu-sva can reuse the PASID for another process. mm_exit() is allowed to block, so the device driver has time to clean up and flush the queues.
If the device driver finishes using the PASID before the process exits, it just calls unbind().
Quoted 5 lines
4. It would be nice to have to be able to set a "void *" for each PASID/device combination while binding to a process which then can be queried later on based on the PASID.
E.g. when you have a per PASID/device structure around anyway,
just add an extra field.
iommu_sva_bind_device() takes a "drvdata" pointer that is stored internally for the PASID/device combination (iommu_bond). It is passed to mm_exit(), but I haven't added anything for the device driver to query it back.
Quoted 5 lines
5. It would be nice to have to allocate multiple PASIDs for the same process address space.
E.g. some teams at AMD want to use a separate GPU address space
for their userspace client library. I'm still trying to avoid that, but it is perfectly possible that we are going to need that.
Two PASIDs pointing to the same process pgd? At first glance it seems feasible, maybe with a flag passed to bind() and a few changes to internal structures. It will duplicate ATC invalidation commands for each process address space change (munmap etc) so you might take a performance hit.
Intel's SVM code has the SVM_FLAG_PRIVATE_PASID which seems similar to what you describe, but I don't plan to support it in this series (the io_mm model is already pretty complicated). I think it can be added without too much effort in a future series, though with a different flag name since we'd like to use "private PASID" for something else
(https://www.spinics.net/lists/dri-devel/msg177007.html).
Thanks,
Jean
Quoted 13 lines
Additional to that it is sometimes quite useful for debugging
to isolate where exactly an incorrect access (segfault) is coming from.Let me know if there are some problems with that, especially I want to know if there is pushback on #5 so that I can forward that :)
Thanks,
Christian.Quoted 3 lines
Thanks,
Jean
_______________________________________________
iommu mailing list
[email protected]
https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [PATCH v2 01/40] iommu: Introduce Shared Virtual Addressing API
Am 07.09.2018 um 17:45 schrieb Jean-Philippe Brucker:
Quoted 14 lines · Christian König
Quoted 2 lines
I will take this as an opportunity to summarize some of the requirements we have for PASID management from the amdgpu driver point of view:
That's incredibly useful, thanks :)
Quoted 2 lines
1. We need to be able to allocate PASID between 1 and some maximum. Zero is reserved as far as I know, but we don't necessary need a minimum.
Should be fine. The PASID range is restricted by the PCI PASID capability, firmware description (for non-PCI devices), the IOMMU capacity, and what the device driver passes to
iommu_sva_device_init. Not all IOMMUs reserve PASID 0 (AMD IOMMU without GIoSup doesn't, if I'm not mistaken), so the KFD driver will need to pass min_pasid=1 to make sure that 0 isn't allocated.
Quoted 7 lines
Quoted 5 lines
2. We need to be able to allocate PASIDs without a process address space backing it. E.g. our hardware uses PASIDs even without Shared Virtual Addressing enabled to distinct clients from each other.
Would be a pity if we need to still have a separate PASID
handling because the system wide is only available when IOMMU is turned on.I'm still not sure about this one. From my point of view we shouldn't add to the IOMMU subsystem helpers for devices without an IOMMU.
I agree on that.
Quoted 6 lines
iommu-sva expects everywhere that the device has an
iommu_domain, it's the first thing we check on entry. Bypassing all of this would callidr_alloc()directly, and wouldn't have any code in common with the current iommu-sva. So it seems like you need a layer on top of iommu-sva callingidr_alloc()when an IOMMU isn't present, but I don't think it should be indrivers/iommu/
In this case I question if the PASID handling should be under drivers/iommu at all.
See I can have a mix of VM context which are bound to processes (some few) and VM contexts which are standalone and doesn't care for a process address space. But for each VM context I need a distinct PASID for the hardware to work.
I can live if we say if IOMMU is completely disabled we use a simple ida to allocate them, but when IOMMU is enabled I certainly need a way to reserve a PASID without an associated process.
Quoted 15 lines
Quoted 5 lines
3. Even after destruction of a process address space we need some grace period before a PASID is reused because it can be that the specific PASID is still in some hardware queues etc...
At bare minimum all device drivers using process binding need
to explicitly note to the core when they are done with a PASID.Right, much of the horribleness in iommu-sva deals with this:
The process dies, iommu-sva is notified and calls the
mm_exit()function passed by the device driver toiommu_sva_device_init(). Inmm_exit()the device driver needs to clear any reference to the PASID in hardware and in its own structures. When the device driver returns frommm_exit(), it effectively tells the core that it has finished using the PASID, and iommu-sva can reuse the PASID for another process.mm_exit()is allowed to block, so the device driver has time to clean up and flush the queues.
Quoted 2 lines
If the device driver finishes using the PASID before the process exits, it just calls
unbind().
Exactly that's what Michal Hocko is probably going to not like at all.
Can we have a different approach where each driver is informed by the mm_exit(), but needs to explicitly call unbind() before a PASID is reused?
During that teardown transition it would be ideal if that PASID only points to a dummy root page directory with only invalid entries.
Quoted 10 lines
Quoted 6 lines
4. It would be nice to have to be able to set a "void *" for each PASID/device combination while binding to a process which then can be queried later on based on the PASID.
E.g. when you have a per PASID/device structure around anyway,
just add an extra field.
iommu_sva_bind_device()takes a "drvdata" pointer that is stored internally for the PASID/device combination (iommu_bond). It is passed tomm_exit(), but I haven't added anything for the device driver to query it back.
Nice! Looks like all we need additionally is a function to retrieve that based on the PASID.
Quoted 11 lines
Quoted 5 lines
5. It would be nice to have to allocate multiple PASIDs for the same process address space.
E.g. some teams at AMD want to use a separate GPU address space
for their userspace client library. I'm still trying to avoid that, but it is perfectly possible that we are going to need that.Two PASIDs pointing to the same process pgd? At first glance it seems feasible, maybe with a flag passed to
bind()and a few changes to internal structures. It will duplicate ATC invalidation commands for each process address space change (munmap etc) so you might take a performance hit.
Quoted 6 lines
Intel's SVM code has the
SVM_FLAG_PRIVATE_PASIDwhich seems similar to what you describe, but I don't plan to support it in this series (theio_mmmodel is already pretty complicated). I think it can be added without too much effort in a future series, though with a different flag name since we'd like to use "private PASID" for something else
(https://www.spinics.net/lists/dri-devel/msg177007.html).
To be honest I hoped that you would say: No never! So that I have a good argument to pushback on such requirements :)
But if it's doable it would be at least nice to have for debugging.
Thanks a lot for working on that,
Christian.
Quoted 15 lines
Thanks,
JeanQuoted 11 lines
Additional to that it is sometimes quite useful for debugging
to isolate where exactly an incorrect access (segfault) is coming from.Let me know if there are some problems with that, especially I want to know if there is pushback on #5 so that I can forward that :)
Thanks,
Christian.Quoted 2 lines
Thanks,
Jean
_______________________________________________
iommu mailing list
[email protected]
https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [PATCH v2 01/40] iommu: Introduce Shared Virtual Addressing API
Quoted 31 lines · On Fri, 7 Sep 2018 20:02:54 +0200 Christian König <[email protected]>…
Am 07.09.2018 um 17:45 schrieb Jean-Philippe Brucker:
Quoted 9 lines · Christian König
Quoted 3 lines
I will take this as an opportunity to summarize some of the requirements we have for PASID management from the amdgpu driver point of view:
That's incredibly useful, thanks :)
Quoted 3 lines
1. We need to be able to allocate PASID between 1 and some maximum. Zero is reserved as far as I know, but we don't necessary need a minimum.
[...]
Quoted 6 lines
Quoted 6 lines
2. We need to be able to allocate PASIDs without a process address space backing it. E.g. our hardware uses PASIDs even without Shared Virtual Addressing enabled to distinct clients from each other. Would be a pity if we need to still have a separate PASID handling because the system wide is only available when IOMMU is turned on.
[...]
I agree on that.
Quoted 6 lines
iommu-sva expects everywhere that the device has an
iommu_domain, it's the first thing we check on entry. Bypassing all of this would callidr_alloc()directly, and wouldn't have any code in common with the current iommu-sva. So it seems like you need a layer on top of iommu-sva callingidr_alloc()when an IOMMU isn't present, but I don't think it should be indrivers/iommu/In this case I question if the PASID handling should be under
drivers/iommuat all.
Quoted 9 lines
See I can have a mix of VM context which are bound to processes (some few) and VM contexts which are standalone and doesn't care for a process address space. But for each VM context I need a distinct PASID for the hardware to work.
I can live if we say if IOMMU is completely disabled we use a simple ida to allocate them, but when IOMMU is enabled I certainly need a way to reserve a PASID without an associated process.
VT-d would also have such requirement. There is a virtual command register for allocate and free PASID for VM use. When that PASID allocation request gets propagated to the host IOMMU driver, we need to allocate PASID w/o mm.
If the PASID allocation is done via VFIO, can we have FD to track PASID life cycle instead of mm_exit()? i.e. all FDs get closed before mm_exit, I assume?
Quoted 20 lines
Quoted 17 lines
Quoted 6 lines
3. Even after destruction of a process address space we need some grace period before a PASID is reused because it can be that the specific PASID is still in some hardware queues etc... At bare minimum all device drivers using process binding
need to explicitly note to the core when they are done with a PASID.Right, much of the horribleness in iommu-sva deals with this:
The process dies, iommu-sva is notified and calls the
mm_exit()function passed by the device driver toiommu_sva_device_init(). Inmm_exit()the device driver needs to clear any reference to the PASID in hardware and in its own structures. When the device driver returns frommm_exit(), it effectively tells the core that it has finished using the PASID, and iommu-sva can reuse the PASID for another process.mm_exit()is allowed to block, so the device driver has time to clean up and flush the queues.Quoted 2 lines
If the device driver finishes using the PASID before the process exits, it just calls
unbind().
Quoted 9 lines
Exactly that's what Michal Hocko is probably going to not like at all.
Can we have a different approach where each driver is informed by the
mm_exit(), but needs to explicitly callunbind()before a PASID is reused?During that teardown transition it would be ideal if that PASID only points to a dummy root page directory with only invalid entries.
I guess this can be vendor specific, In VT-d I plan to mark PASID entry not present and disable fault reporting while draining remaining activities.
Quoted 34 lines
Quoted 10 lines
Quoted 6 lines
4. It would be nice to have to be able to set a "void *" for each PASID/device combination while binding to a process which then can be queried later on based on the PASID.
E.g. when you have a per PASID/device structure around
anyway, just add an extra field.
iommu_sva_bind_device()takes a "drvdata" pointer that is stored internally for the PASID/device combination (iommu_bond). It is passed tomm_exit(), but I haven't added anything for the device driver to query it back.Nice! Looks like all we need additionally is a function to retrieve that based on the PASID.
Quoted 12 lines
Quoted 6 lines
5. It would be nice to have to allocate multiple PASIDs for the same process address space.
E.g. some teams at AMD want to use a separate GPU
address space for their userspace client library. I'm still trying to avoid that, but it is perfectly possible that we are going to need that.Two PASIDs pointing to the same process pgd? At first glance it seems feasible, maybe with a flag passed to
bind()and a few changes to internal structures. It will duplicate ATC invalidation commands for each process address space change (munmap etc) so you might take a performance hit.Quoted 7 lines
Intel's SVM code has the
SVM_FLAG_PRIVATE_PASIDwhich seems similar to what you describe, but I don't plan to support it in this series (theio_mmmodel is already pretty complicated). I think it can be added without too much effort in a future series, though with a different flag name since we'd like to use "private PASID" for something else
(https://www.spinics.net/lists/dri-devel/msg177007.html).
Quoted 26 lines
To be honest I hoped that you would say: No never! So that I have a good argument to pushback on such requirements :)
But if it's doable it would be at least nice to have for debugging.
Thanks a lot for working on that,
Christian.Quoted 17 lines
Thanks,
JeanQuoted 13 lines
Additional to that it is sometimes quite useful for
debugging to isolate where exactly an incorrect access (segfault) is coming from.Let me know if there are some problems with that, especially I want to know if there is pushback on #5 so that I can forward that :)
Thanks,
Christian.Quoted 2 lines
Thanks,
Jean
[Jacob Pan]
Re: [PATCH v2 01/40] iommu: Introduce Shared Virtual Addressing API
Am 07.09.2018 um 23:25 schrieb Jacob Pan:
Quoted 21 lines
Quoted 10 lines · On Fri, 7 Sep 2018 20:02:54 +0200 Christian König <[email protected]>…
[SNIP]
Quoted 6 lines
iommu-sva expects everywhere that the device has an
iommu_domain, it's the first thing we check on entry. Bypassing all of this would callidr_alloc()directly, and wouldn't have any code in common with the current iommu-sva. So it seems like you need a layer on top of iommu-sva callingidr_alloc()when an IOMMU isn't present, but I don't think it should be indrivers/iommu/In this case I question if the PASID handling should be under
drivers/iommuat all.Quoted 9 lines
See I can have a mix of VM context which are bound to processes (some few) and VM contexts which are standalone and doesn't care for a process address space. But for each VM context I need a distinct PASID for the hardware to work.
I can live if we say if IOMMU is completely disabled we use a simple ida to allocate them, but when IOMMU is enabled I certainly need a way to reserve a PASID without an associated process.
Quoted 8 lines
VT-d would also have such requirement. There is a virtual command register for allocate and free PASID for VM use. When that PASID allocation request gets propagated to the host IOMMU driver, we need to allocate PASID w/o mm.
If the PASID allocation is done via VFIO, can we have FD to track PASID life cycle instead of
mm_exit()? i.e. all FDs get closed beforemm_exit, I assume?
Yes, exactly. I just need a PASID which is never used by the OS for a process and we can easily give that back when the last FD reference is closed.
Quoted 25 lines
Quoted 25 lines
Quoted 17 lines
Quoted 6 lines
3. Even after destruction of a process address space we need some grace period before a PASID is reused because it can be that the specific PASID is still in some hardware queues etc... At bare minimum all device drivers using process binding
need to explicitly note to the core when they are done with a PASID.Right, much of the horribleness in iommu-sva deals with this:
The process dies, iommu-sva is notified and calls the
mm_exit()function passed by the device driver toiommu_sva_device_init(). Inmm_exit()the device driver needs to clear any reference to the PASID in hardware and in its own structures. When the device driver returns frommm_exit(), it effectively tells the core that it has finished using the PASID, and iommu-sva can reuse the PASID for another process.mm_exit()is allowed to block, so the device driver has time to clean up and flush the queues.Quoted 2 lines
If the device driver finishes using the PASID before the process exits, it just calls
unbind().Exactly that's what Michal Hocko is probably going to not like at all.
Can we have a different approach where each driver is informed by the
mm_exit(), but needs to explicitly callunbind()before a PASID is reused?
Quoted 6 lines
Quoted 3 lines
During that teardown transition it would be ideal if that PASID only points to a dummy root page directory with only invalid entries.
I guess this can be vendor specific, In VT-d I plan to mark PASID entry not present and disable fault reporting while draining remaining activities.
Sounds good to me.
Point is at least in the case where the process was killed by the OOM killer we should not block in mm_exit().
Instead operations issued by the process to a device driver which uses SVA needs to be terminated as soon as possible to make sure that the OOM killer can advance.
Thanks,
Christian.
_______________________________________________
iommu mailing list
[email protected]
https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [PATCH v2 01/40] iommu: Introduce Shared Virtual Addressing API
Quoted 3 lines · Christian König
Yes, exactly. I just need a PASID which is never used by the OS for a process and we can easily give that back when the last FD reference is closed.
Alright, iommu-sva can get its PASID from this external allocator as well, as long as it has an interface similar to idr. Where would it go, drivers/base/, mm/, kernel/...?
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
The process dies, iommu-sva is notified and calls the
mm_exit()function passed by the device driver toiommu_sva_device_init(). Inmm_exit()the device driver needs to clear any reference to the PASID in hardware and in its own structures. When the device driver returns frommm_exit(), it effectively tells the core that it has finished using the PASID, and iommu-sva can reuse the PASID for another process.mm_exit()is allowed to block, so the device driver has time to clean up and flush the queues.
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 2 lines
If the device driver finishes using the PASID before the process exits, it just calls
unbind().Exactly that's what Michal Hocko is probably going to not like at all.
Can we have a different approach where each driver is informed by the
mm_exit(), but needs to explicitly callunbind()before a PASID is reused?
It's awful from the IOMMU driver perspective. In addition to "enabled" and "disabled" PASID states, you add "disabled but DMA still running normally". Between that new state and "disabled", the IOMMU will be flooded by translation faults (non-recoverable ones), which it needs to ignore instead of reporting to the kernel. Not all IOMMUs can deal with this in hardware (SMMU and VT-d can quiesce translation faults per-PASID, but I don't think AMD IOMMU can.) Some drivers will have to filter fault events themselves, depending on the PASID state.
Quoted 7 lines
Quoted 6 lines
Quoted 3 lines
During that teardown transition it would be ideal if that PASID only points to a dummy root page directory with only invalid entries.
I guess this can be vendor specific, In VT-d I plan to mark PASID entry not present and disable fault reporting while draining remaining activities.
Quoted 8 lines
Sounds good to me.
Point is at least in the case where the process was killed by the OOM killer we should not block in
mm_exit().Instead operations issued by the process to a device driver which uses SVA needs to be terminated as soon as possible to make sure that the OOM killer can advance.
I don't see how we're preventing the OOM killer from advancing, so I'm looking for a stronger argument that justifies adding this complexity to IOMMU drivers. Time limit of the release MMU notifier, locking requirement, a concrete example where things break, even a comment somewhere in mm/ would do...
In my tests I can't manage to disturb the OOM killer, but I could be missing some special case. Even if the mm_exit() callback (unrealistically) sleeps for 60 seconds, the OOM killer isn't affected because oom_reap_task_mm() wipes the victim's address space in another thread, either before or while the release notifier is running.
Thanks,
Jean
_______________________________________________
iommu mailing list
[email protected]
https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [PATCH v2 01/40] iommu: Introduce Shared Virtual Addressing API
Am 12.09.2018 um 14:40 schrieb Jean-Philippe Brucker:
Quoted 7 lines · Christian König
Quoted 3 lines
Yes, exactly. I just need a PASID which is never used by the OS for a process and we can easily give that back when the last FD reference is closed.
Alright, iommu-sva can get its PASID from this external allocator as well, as long as it has an interface similar to idr. Where would it go,
drivers/base/,mm/,kernel/...?
Good question, my initial instinct was to put it under drivers/pci.
But AFAIKS now you are supporting SVA implementations which are not based on PCI.
So drivers/base sounds like a good place to me.
Quoted 17 lines
Quoted 10 lines
Quoted 10 lines
Quoted 10 lines
Quoted 10 lines
The process dies, iommu-sva is notified and calls the
mm_exit()function passed by the device driver toiommu_sva_device_init(). Inmm_exit()the device driver needs to clear any reference to the PASID in hardware and in its own structures. When the device driver returns frommm_exit(), it effectively tells the core that it has finished using the PASID, and iommu-sva can reuse the PASID for another process.mm_exit()is allowed to block, so the device driver has time to clean up and flush the queues.Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 2 lines
If the device driver finishes using the PASID before the process exits, it just calls
unbind().Exactly that's what Michal Hocko is probably going to not like at all.
Can we have a different approach where each driver is informed by the
mm_exit(), but needs to explicitly callunbind()before a PASID is reused?
Quoted 8 lines
It's awful from the IOMMU driver perspective. In addition to "enabled" and "disabled" PASID states, you add "disabled but DMA still running normally". Between that new state and "disabled", the IOMMU will be flooded by translation faults (non-recoverable ones), which it needs to ignore instead of reporting to the kernel. Not all IOMMUs can deal with this in hardware (SMMU and VT-d can quiesce translation faults per-PASID, but I don't think AMD IOMMU can.) Some drivers will have to filter fault events themselves, depending on the PASID state.
Puh, yeah that is probably true.
Ok let us skip that for a moment, we just need to invest more work in killing DMA operations quickly when the process address space is teared down.
Quoted 20 lines
Quoted 6 lines
Quoted 6 lines
Quoted 3 lines
During that teardown transition it would be ideal if that PASID only points to a dummy root page directory with only invalid entries.
I guess this can be vendor specific, In VT-d I plan to mark PASID entry not present and disable fault reporting while draining remaining activities.
Quoted 8 lines
Sounds good to me.
Point is at least in the case where the process was killed by the OOM killer we should not block in
mm_exit().Instead operations issued by the process to a device driver which uses SVA needs to be terminated as soon as possible to make sure that the OOM killer can advance.
I don't see how we're preventing the OOM killer from advancing, so I'm looking for a stronger argument that justifies adding this complexity to IOMMU drivers. Time limit of the release MMU notifier, locking requirement, a concrete example where things break, even a comment somewhere in
mm/would do...
Quoted 3 lines
In my tests I can't manage to disturb the OOM killer, but I could be missing some special case. Even if the
mm_exit()callback (unrealistically) sleeps for 60 seconds,
Well you are *COMPLETELY* under estimating this. A compute task with a huge wave launch can take multiple minutes to tear down.
That's why I'm so concerned about that, but to be honest I think that just the hardware needs to become better and we need to be able to block dead tasks from spawning threads again.
Regards,
Christian.
Quoted 6 lines
the OOM killer isn't affected
becauseoom_reap_task_mm()wipes the victim's address space in another thread, either before or while the release notifier is running.Thanks,
Jean
_______________________________________________
iommu mailing list
[email protected]
https://lists.linuxfoundation.org/mailman/listinfo/iommu
RE: [PATCH v2 01/40] iommu: Introduce Shared Virtual Addressing API
In reply to Jacob PanQuoted 16 lines
From: Jacob Pan [mailto:jacob.jun.pan-VuQAYsv1563Yd54FQh9/[email protected]]
Sent: Saturday, September 8, 2018 5:25 AMQuoted 14 lines
Quoted 6 lines
iommu-sva expects everywhere that the device has an
iommu_domain, it's the first thing we check on entry. Bypassing all of this would callidr_alloc()directly, and wouldn't have any code in common with the current iommu-sva. So it seems like you need a layer on top of iommu-sva callingidr_alloc()when an IOMMU isn't present, but I don't think it should be indrivers/iommu/In this case I question if the PASID handling should be under
drivers/iommuat all.See I can have a mix of VM context which are bound to processes (some few) and VM contexts which are standalone and doesn't care for a process address space. But for each VM context I need a distinct PASID for the hardware to work.
I'm confused about VM context vs. process. Is VM referring to Virtual Machine or something else? If yes, I don't understand the binding part
- what VM context is bound to (host?) process?
Quoted 10 lines
Quoted 5 lines
I can live if we say if IOMMU is completely disabled we use a simple ida to allocate them, but when IOMMU is enabled I certainly need a way to reserve a PASID without an associated process.
VT-d would also have such requirement. There is a virtual command register for allocate and free PASID for VM use. When that PASID allocation request gets propagated to the host IOMMU driver, we need to allocate PASID w/o mm.
VT-d is a bit different. In host side, PASID allocation always happens in Qemu's context, so those PASIDs are recorded with Qemu process, though the entries may point to guest page tables instead of host mm of Qemu.
Thanks
Kevin
RE: [PATCH v2 01/40] iommu: Introduce Shared Virtual Addressing API
In reply to Christian KönigQuoted 11 lines
From: Christian König
Sent: Friday, September 7, 2018 4:56 PM5. It would be nice to have to allocate multiple PASIDs for the same process address space.
E.g. some teams at AMD want to use a separate GPU address space
for their userspace client library. I'm still trying to avoid that, but it is perfectly possible that we are going to need that.
Additional to that it is sometimes quite useful for debugging
to isolate where exactly an incorrect access (segfault) is coming from.
Quoted 3 lines
Let me know if there are some problems with that, especially I want to know if there is pushback on #5 so that I can forward that :)
We have similar requirement, except that it is "multiple PASIDs for same process" instead of "for same process address space".
Intel VT-d goes to a 'true' system-wide PASID allocation policy, cross both host processes and guest processes. As Jacob explains, there will be a virtual cmd register on virtual vtd, through which guest IOMMU driver requests to get system-wide PASIDs allocated by host IOMMU driver.
with that design, Qemu represents all guest processes in host side, thus will get "multiple PASIDs allocated for same process". However instead of binding all PASIDs to same host address space of Qemu, each of PASID entry points to guest address space if used by guest process.
Thanks
Kevin
_______________________________________________
iommu mailing list
[email protected]
https://lists.linuxfoundation.org/mailman/listinfo/iommu
PATCH v2 2/40 iommu/sva: Bind process address spaces to devices
In reply to Jean-Philippe BruckerAdd bind() and unbind() operations to the IOMMU API. Bind() returns a PASID that drivers can program in hardware, to let their devices access an mm. This patch only adds skeletons for the device driver API, most of the implementation is still missing.
IOMMU groups with more than one device aren't supported for SVA at the moment. There may be P2P traffic between devices within a group, which cannot be seen by an IOMMU (note that supporting PASID doesn't add any form of isolation with regard to P2P). Supporting groups would require calling bind() for all bound processes every time a device is added to a group, to perform sanity checks (e.g. ensure that new devices support PASIDs at least as big as those already allocated in the group). It also means making sure that reserved ranges (IOMMU_RESV_*) of all devices are carved out of processes. This is already tricky with single devices, but becomes very difficult with groups. Since SVA-capable devices are expected to be cleanly isolated, and since we don't have any way to test groups or hot-plug, we only allow singular groups for now.
---v1->v2: remove iommu_sva_bind/unbind_group
--- drivers/iommu/iommu-sva.c | 27 +++++++++++++ drivers/iommu/iommu.c | 83 +++++++++++++++++++++++++++++++++++++++ include/linux/iommu.h | 37 +++++++++++++++++ 3 files changed, 147 insertions(+)
drivers/iommu/iommu-sva.c +27 -0
@@ -93,6 +93,8 @@ int iommu_sva_device_shutdown(struct device *dev)if (!domain)return -ENODEV ; +__iommu_sva_unbind_dev_all (dev); +mutex_lock (&dev->iommu_param->lock); param = dev->iommu_param->sva_param; dev->iommu_param->sva_param =NULL ;
@@ -108,3 +110,28 @@ int iommu_sva_device_shutdown(struct device *dev)return 0 ; }EXPORT_SYMBOL_GPL (iommu_sva_device_shutdown); + +int __iommu_sva_bind_device (struct device *dev,struct mm_struct *mm, +int *pasid,unsigned long flags,void *drvdata) +{ +return -ENOSYS ;/* TODO */ +} +EXPORT_SYMBOL_GPL (__iommu_sva_bind_device); + +int __iommu_sva_unbind_device (struct device *dev,int pasid) +{ +return -ENOSYS ;/* TODO */ +} +EXPORT_SYMBOL_GPL (__iommu_sva_unbind_device); + +/** +* __iommu_sva_unbind_dev_all() - Detach all address spaces from this device +* @dev: the device +* +* When detaching @device from a domain, IOMMU drivers should use this helper. +*/ +void __iommu_sva_unbind_dev_all (struct device *dev) +{ +/* TODO */ +} +EXPORT_SYMBOL_GPL (__iommu_sva_unbind_dev_all);
drivers/iommu/iommu.c +83 -0
86 lines not on this page · raw message
include/linux/iommu.h +37 -0
@@ -613,6 +613,10 @@ void iommu_fwspec_free(struct device *dev);int iommu_fwspec_add_ids (struct device *dev,u32 *ids,int num_ids);const struct iommu_ops *iommu_ops_from_fwnode (struct fwnode_handle *fwnode); +extern int iommu_sva_bind_device (struct device *dev,struct mm_struct *mm, +int *pasid,unsigned long flags,void *drvdata); +extern int iommu_sva_unbind_device (struct device *dev,int pasid); +#else /* CONFIG_IOMMU_API */ struct iommu_ops {};
@@ -932,12 +936,29 @@ static inline int iommu_sva_invalidate(struct iommu_domain *domain,return -EINVAL ; } +static inline int iommu_sva_bind_device (struct device *dev, +struct mm_struct *mm,int *pasid, +unsigned long flags,void *drvdata) +{ +return -ENODEV ; +} + +static inline int iommu_sva_unbind_device (struct device *dev,int pasid) +{ +return -ENODEV ; +} +#endif /* CONFIG_IOMMU_API */ #ifdef CONFIG_IOMMU_SVA extern int iommu_sva_device_init (struct device *dev,unsigned long features,unsigned int max_pasid);extern int iommu_sva_device_shutdown (struct device *dev); +extern int __iommu_sva_bind_device (struct device *dev,struct mm_struct *mm, +int *pasid,unsigned long flags, +void *drvdata); +extern int __iommu_sva_unbind_device (struct device *dev,int pasid); +extern void __iommu_sva_unbind_dev_all (struct device *dev);#else /* CONFIG_IOMMU_SVA */ static inline int iommu_sva_device_init(struct device *dev, unsigned long features,
@@ -950,6 +971,22 @@ static inline int iommu_sva_device_shutdown(struct device *dev) {return -ENODEV ; } + +static inline int __iommu_sva_bind_device (struct device *dev, +struct mm_struct *mm,int *pasid, +unsigned long flags,void *drvdata) +{ +return -ENODEV ; +} + +static inline int __iommu_sva_unbind_device (struct device *dev,int pasid) +{ +return -ENODEV ; +} + +static inline void __iommu_sva_unbind_dev_all (struct device *dev) +{ +}#endif /* CONFIG_IOMMU_SVA */ #endif /* __LINUX_IOMMU_H */
Signature · 1 line
2.17.0
Re: [PATCH v2 02/40] iommu/sva: Bind process address spaces to devices
Quoted 5 lines · On Fri, 11 May 2018 20:06:03 +0100 Jean-Philippe Brucker…
Add
bind()andunbind()operations to the IOMMU API.Bind()returns a PASID that drivers can program in hardware, to let their devices access an mm. This patch only adds skeletons for the device driver API, most of the implementation is still missing.
Quoted 7 lines
IOMMU groups with more than one device aren't supported for SVA at the moment. There may be P2P traffic between devices within a group, which cannot be seen by an IOMMU (note that supporting PASID doesn't add any form of isolation with regard to P2P). Supporting groups would require calling
bind()for all bound processes every time a device is added to a group, to perform sanity checks (e.g. ensure that new devices support PASIDs at least as big as those already allocated in the group).
Is it worth adding an explicit comment on this reasoning (or a minimal subset of it) at the check for the number of devices in the group?
It's well laid out here, but might not be so obvious if someone is reading the code in the future.
Quoted 8 lines
It also
means making sure that reserved ranges (IOMMU_RESV_*) of all devices are carved out of processes. This is already tricky with single devices, but becomes very difficult with groups. Since SVA-capable devices are expected to be cleanly isolated, and since we don't have any way to test groups or hot-plug, we only allow singular groups for now.Signed-off-by Jean-Philippe Brucker <[email protected]>
Otherwise, looks good to me.
Quoted 210 lines
---
v1->v2: remove iommu_sva_bind/unbind_group--- drivers/iommu/iommu-sva.c | 27 +++++++++++++ drivers/iommu/iommu.c | 83 +++++++++++++++++++++++++++++++++++++++ include/linux/iommu.h | 37 +++++++++++++++++ 3 files changed, 147 insertions(+)drivers/iommu/iommu-sva.c +27
-0@@ -93,6 +93,8 @@ int iommu_sva_device_shutdown(struct device *dev)if (!domain)return -ENODEV ; +__iommu_sva_unbind_dev_all (dev); +mutex_lock (&dev->iommu_param->lock); param = dev->iommu_param->sva_param; dev->iommu_param->sva_param =NULL ;@@ -108,3 +110,28 @@ int iommu_sva_device_shutdown(struct device *dev)return 0 ; }EXPORT_SYMBOL_GPL (iommu_sva_device_shutdown); + +int __iommu_sva_bind_device (struct device *dev,struct mm_struct *mm, +int *pasid,unsigned long flags,void *drvdata) +{ +return -ENOSYS ;/* TODO */ +} +EXPORT_SYMBOL_GPL (__iommu_sva_bind_device); + +int __iommu_sva_unbind_device (struct device *dev,int pasid) +{ +return -ENOSYS ;/* TODO */ +} +EXPORT_SYMBOL_GPL (__iommu_sva_unbind_device); + +/** +* __iommu_sva_unbind_dev_all() - Detach all address spaces from this device +* @dev: the device +* +* When detaching @device from a domain, IOMMU drivers should use this helper. +*/ +void __iommu_sva_unbind_dev_all (struct device *dev) +{ +/* TODO */ +} +EXPORT_SYMBOL_GPL (__iommu_sva_unbind_dev_all);drivers/iommu/iommu.c +83
-0@@ -2261,3 +2261,86 @@ int iommu_fwspec_add_ids(struct device *dev, u32 *ids, int num_ids)return 0 ; }EXPORT_SYMBOL_GPL (iommu_fwspec_add_ids); + +/** +* iommu_sva_bind_device() - Bind a process address space to a device +* @dev: the device +* @mm: the mm to bind, caller must hold a reference to it +* @pasid: valid address where the PASID will be stored +* @flags: bond properties +* @drvdata: private data passed to the mm exit handler +* +* Create a bond between device and task, allowing the device to access the mm +* using the returned PASID. If unbind() isn't called first, a subsequent bind() +* for the same device and mm fails with -EEXIST. +* +* iommu_sva_device_init() must be called first, to initialize the required SVA +* features. @flags is a subset of these features. +* +* The caller must pin down using get_user_pages*() all mappings shared with the +* device. mlock() isn't sufficient, as it doesn't prevent minor page faults +* (e.g. copy-on-write). +* +* On success, 0 is returned and @pasid contains a valid ID. Otherwise, an error +* is returned. +*/ +int iommu_sva_bind_device (struct device *dev,struct mm_struct *mm,int *pasid, +unsigned long flags,void *drvdata) +{ +int ret = -EINVAL ; +struct iommu_group *group; + +if (!pasid) +return -EINVAL ; + + group =iommu_group_get (dev); +if (!group) +return -ENODEV ; + +/* Ensure device count and domain don't change while we're binding */ +mutex_lock (&group->mutex); +if (iommu_group_device_count (group) !=1 ) + goto out_unlock; + + ret =__iommu_sva_bind_device (dev, mm, pasid, flags, drvdata); + +out_unlock: +mutex_unlock (&group->mutex); +iommu_group_put (group); + +return ret; +} +EXPORT_SYMBOL_GPL (iommu_sva_bind_device); + +/** +* iommu_sva_unbind_device() - Remove a bond created with iommu_sva_bind_device +* @dev: the device +* @pasid: the pasid returned by bind() +* +* Remove bond between device and address space identified by @pasid. Users +* should not call unbind() if the corresponding mm exited (as the PASID might +* have been reallocated for another process). +* +* The device must not be issuing any more transaction for this PASID. All +* outstanding page requests for this PASID must have been flushed to the IOMMU. +* +* Returns 0 on success, or an error value +*/ +int iommu_sva_unbind_device (struct device *dev,int pasid) +{ +int ret = -EINVAL ; +struct iommu_group *group; + + group =iommu_group_get (dev); +if (!group) +return -ENODEV ; + +mutex_lock (&group->mutex); + ret =__iommu_sva_unbind_device (dev, pasid); +mutex_unlock (&group->mutex); + +iommu_group_put (group); + +return ret; +} +EXPORT_SYMBOL_GPL (iommu_sva_unbind_device);include/linux/iommu.h +37
-0@@ -613,6 +613,10 @@ void iommu_fwspec_free(struct device *dev);int iommu_fwspec_add_ids (struct device *dev,u32 *ids,int num_ids);const struct iommu_ops *iommu_ops_from_fwnode (struct fwnode_handle *fwnode); +extern int iommu_sva_bind_device (struct device *dev,struct mm_struct *mm, +int *pasid,unsigned long flags,void *drvdata); +extern int iommu_sva_unbind_device (struct device *dev,int pasid); +#else /* CONFIG_IOMMU_API */ struct iommu_ops {};@@ -932,12 +936,29 @@ static inline int iommu_sva_invalidate(struct iommu_domain *domain,return -EINVAL ; } +static inline int iommu_sva_bind_device (struct device *dev, +struct mm_struct *mm,int *pasid, +unsigned long flags,void *drvdata) +{ +return -ENODEV ; +} + +static inline int iommu_sva_unbind_device (struct device *dev,int pasid) +{ +return -ENODEV ; +} +#endif /* CONFIG_IOMMU_API */ #ifdef CONFIG_IOMMU_SVA extern int iommu_sva_device_init (struct device *dev,unsigned long features,unsigned int max_pasid);extern int iommu_sva_device_shutdown (struct device *dev); +extern int __iommu_sva_bind_device (struct device *dev,struct mm_struct *mm, +int *pasid,unsigned long flags, +void *drvdata); +extern int __iommu_sva_unbind_device (struct device *dev,int pasid); +extern void __iommu_sva_unbind_dev_all (struct device *dev);#else /* CONFIG_IOMMU_SVA */ static inline int iommu_sva_device_init(struct device *dev, unsigned long features,@@ -950,6 +971,22 @@ static inline int iommu_sva_device_shutdown(struct device *dev) {return -ENODEV ; } + +static inline int __iommu_sva_bind_device (struct device *dev, +struct mm_struct *mm,int *pasid, +unsigned long flags,void *drvdata) +{ +return -ENODEV ; +} + +static inline int __iommu_sva_unbind_device (struct device *dev,int pasid) +{ +return -ENODEV ; +} + +static inline void __iommu_sva_unbind_dev_all (struct device *dev) +{ +}#endif /* CONFIG_IOMMU_SVA */ #endif /* __LINUX_IOMMU_H */
Re: [PATCH v2 02/40] iommu/sva: Bind process address spaces to devices
Quoted 16 lines · Jonathan Cameron
Quoted 5 lines · On Fri, 11 May 2018 20:06:03 +0100 Jean-Philippe Brucker…
Add
bind()andunbind()operations to the IOMMU API.Bind()returns a PASID that drivers can program in hardware, to let their devices access an mm. This patch only adds skeletons for the device driver API, most of the implementation is still missing.Quoted 7 lines
IOMMU groups with more than one device aren't supported for SVA at the moment. There may be P2P traffic between devices within a group, which cannot be seen by an IOMMU (note that supporting PASID doesn't add any form of isolation with regard to P2P). Supporting groups would require calling
bind()for all bound processes every time a device is added to a group, to perform sanity checks (e.g. ensure that new devices support PASIDs at least as big as those already allocated in the group).
Quoted 4 lines
Is it worth adding an explicit comment on this reasoning (or a minimal subset of it) at the check for the number of devices in the group?
It's well laid out here, but might not be so obvious if someone is reading the code in the future.
Sure, I'll add something
Thanks,
Jean
Re: [PATCH v2 02/40] iommu/sva: Bind process address spaces to devices
In reply to Jean-Philippe BruckerHi Jean-Philippe,
Quoted 83 lines · Jean-Philippe Brucker
Add
bind()andunbind()operations to the IOMMU API.Bind()returns a PASID that drivers can program in hardware, to let their devices access an mm. This patch only adds skeletons for the device driver API, most of the implementation is still missing.IOMMU groups with more than one device aren't supported for SVA at the moment. There may be P2P traffic between devices within a group, which cannot be seen by an IOMMU (note that supporting PASID doesn't add any form of isolation with regard to P2P). Supporting groups would require calling
bind()for all bound processes every time a device is added to a group, to perform sanity checks (e.g. ensure that new devices support PASIDs at least as big as those already allocated in the group). It also means making sure that reserved ranges (IOMMU_RESV_*) of all devices are carved out of processes. This is already tricky with single devices, but becomes very difficult with groups. Since SVA-capable devices are expected to be cleanly isolated, and since we don't have any way to test groups or hot-plug, we only allow singular groups for now.Signed-off-by Jean-Philippe Brucker <[email protected]>---
v1->v2: remove iommu_sva_bind/unbind_group--- drivers/iommu/iommu-sva.c | 27 +++++++++++++ drivers/iommu/iommu.c | 83 +++++++++++++++++++++++++++++++++++++++ include/linux/iommu.h | 37 +++++++++++++++++ 3 files changed, 147 insertions(+)drivers/iommu/iommu-sva.c +27
-0@@ -93,6 +93,8 @@ int iommu_sva_device_shutdown(struct device *dev)if (!domain)return -ENODEV ; +__iommu_sva_unbind_dev_all (dev); +mutex_lock (&dev->iommu_param->lock); param = dev->iommu_param->sva_param; dev->iommu_param->sva_param =NULL ;@@ -108,3 +110,28 @@ int iommu_sva_device_shutdown(struct device *dev)return 0 ; }EXPORT_SYMBOL_GPL (iommu_sva_device_shutdown); + +int __iommu_sva_bind_device (struct device *dev,struct mm_struct *mm, +int *pasid,unsigned long flags,void *drvdata) +{ +return -ENOSYS ;/* TODO */ +} +EXPORT_SYMBOL_GPL (__iommu_sva_bind_device); + +int __iommu_sva_unbind_device (struct device *dev,int pasid) +{ +return -ENOSYS ;/* TODO */ +} +EXPORT_SYMBOL_GPL (__iommu_sva_unbind_device); + +/** +* __iommu_sva_unbind_dev_all() - Detach all address spaces from this device +* @dev: the device +* +* When detaching @device from a domain, IOMMU drivers should use this helper. +*/ +void __iommu_sva_unbind_dev_all (struct device *dev) +{ +/* TODO */ +} +EXPORT_SYMBOL_GPL (__iommu_sva_unbind_dev_all);drivers/iommu/iommu.c +5
-0@@ -2261,3 +2261,86 @@ int iommu_fwspec_add_ids(struct device *dev, u32 *ids, int num_ids)return 0 ; }EXPORT_SYMBOL_GPL (iommu_fwspec_add_ids); + +/** +* iommu_sva_bind_device() - Bind a process address space to a device +* @dev: the device +* @mm: the mm to bind, caller must hold a reference to it
drivers/iommu/iommu.c int iommu_fwspec_add_ids(struct device *dev, u32 *ids, int num_ids) · line 2278
Quoted 10 lines
drivers/iommu/iommu.c +10
-0+* @pasid: valid address where the PASID will be stored +* @flags: bond properties +* @drvdata: private data passed to the mm exit handler +* +* Create a bond between device and task, allowing the device to access the mm +* using the returned PASID. If unbind() isn't called first, a subsequent bind() +* for the same device and mm fails with -EEXIST. +* +* iommu_sva_device_init() must be called first, to initialize the required SVA +* features. @flags is a subset of these features.
@flags must be a subset of these features?
Quoted 15 lines
drivers/iommu/iommu.c +15
-0+* +* The caller must pin down using get_user_pages*() all mappings shared with the +* device. mlock() isn't sufficient, as it doesn't prevent minor page faults +* (e.g. copy-on-write). +* +* On success, 0 is returned and @pasid contains a valid ID. Otherwise, an error +* is returned. +*/ +int iommu_sva_bind_device (struct device *dev,struct mm_struct *mm,int *pasid, +unsigned long flags,void *drvdata) +{ +int ret = -EINVAL ; +struct iommu_group *group; + +if (!pasid)
drivers/iommu/iommu.c int iommu_fwspec_add_ids(struct device *dev, u32 *ids, int num_ids) · line 2303
Quoted 10 lines
drivers/iommu/iommu.c +10
-0+return -EINVAL ; + + group =iommu_group_get (dev); +if (!group) +return -ENODEV ; + +/* Ensure device count and domain don't change while we're binding */ +mutex_lock (&group->mutex); +if (iommu_group_device_count (group) !=1 ) + goto out_unlock;
don't you want to check flags is a subset ofdev->iommu_param->sva_param->features?
Quoted 16 lines
drivers/iommu/iommu.c +16
-0+ + ret =__iommu_sva_bind_device (dev, mm, pasid, flags, drvdata); + +out_unlock: +mutex_unlock (&group->mutex); +iommu_group_put (group); + +return ret; +} +EXPORT_SYMBOL_GPL (iommu_sva_bind_device); + +/** +* iommu_sva_unbind_device() - Remove a bond created with iommu_sva_bind_device +* @dev: the device +* @pasid: the pasid returned by bind() +*
drivers/iommu/iommu.c int iommu_fwspec_add_ids(struct device *dev, u32 *ids, int num_ids) · line 2329
Quoted 10 lines
drivers/iommu/iommu.c +10
-0+* Remove bond between device and address space identified by @pasid. Users +* should not call unbind() if the corresponding mm exited (as the PASID might +* have been reallocated for another process). +* +* The device must not be issuing any more transaction for this PASID. All +* outstanding page requests for this PASID must have been flushed to the IOMMU. +* +* Returns 0 on success, or an error value +*/ +int iommu_sva_unbind_device (struct device *dev,int pasid)
returned value needed?
Quoted 76 lines
drivers/iommu/iommu.c +17
-0+{ +int ret = -EINVAL ; +struct iommu_group *group; + + group =iommu_group_get (dev); +if (!group) +return -ENODEV ; + +mutex_lock (&group->mutex); + ret =__iommu_sva_unbind_device (dev, pasid); +mutex_unlock (&group->mutex); + +iommu_group_put (group); + +return ret; +} +EXPORT_SYMBOL_GPL (iommu_sva_unbind_device);include/linux/iommu.h +30
-0@@ -613,6 +613,10 @@ void iommu_fwspec_free(struct device *dev);int iommu_fwspec_add_ids (struct device *dev,u32 *ids,int num_ids);const struct iommu_ops *iommu_ops_from_fwnode (struct fwnode_handle *fwnode); +extern int iommu_sva_bind_device (struct device *dev,struct mm_struct *mm, +int *pasid,unsigned long flags,void *drvdata); +extern int iommu_sva_unbind_device (struct device *dev,int pasid); +#else /* CONFIG_IOMMU_API */ struct iommu_ops {};@@ -932,12 +936,29 @@ static inline int iommu_sva_invalidate(struct iommu_domain *domain,return -EINVAL ; } +static inline int iommu_sva_bind_device (struct device *dev, +struct mm_struct *mm,int *pasid, +unsigned long flags,void *drvdata) +{ +return -ENODEV ; +} + +static inline int iommu_sva_unbind_device (struct device *dev,int pasid) +{ +return -ENODEV ; +} +#endif /* CONFIG_IOMMU_API */ #ifdef CONFIG_IOMMU_SVA extern int iommu_sva_device_init (struct device *dev,unsigned long features,unsigned int max_pasid);extern int iommu_sva_device_shutdown (struct device *dev); +extern int __iommu_sva_bind_device (struct device *dev,struct mm_struct *mm, +int *pasid,unsigned long flags, +void *drvdata); +extern int __iommu_sva_unbind_device (struct device *dev,int pasid); +extern void __iommu_sva_unbind_dev_all (struct device *dev);#else /* CONFIG_IOMMU_SVA */ static inline int iommu_sva_device_init(struct device *dev, unsigned long features,@@ -950,6 +971,22 @@ static inline int iommu_sva_device_shutdown(struct device *dev) {return -ENODEV ; } + +static inline int __iommu_sva_bind_device (struct device *dev, +struct mm_struct *mm,int *pasid, +unsigned long flags,void *drvdata) +{ +return -ENODEV ; +} + +static inline int __iommu_sva_unbind_device (struct device *dev,int pasid)
include/linux/iommu.h static inline int iommu_sva_device_shutdown(struct device *dev) · line 992
Quoted 10 lines
include/linux/iommu.h +7
-0+{ +return -ENODEV ; +} + +static inline void __iommu_sva_unbind_dev_all (struct device *dev) +{ +}#endif /* CONFIG_IOMMU_SVA */ #endif /* __LINUX_IOMMU_H */
Thanks
Eric
Re: [PATCH v2 02/40] iommu/sva: Bind process address spaces to devices
Quoted 14 lines · Auger Eric
Quoted 6 lines
+/**
+ *iommu_sva_bind_device()- Bind a process address space to a device
+ * @dev: the device
+ * @mm: the mm to bind, caller must hold a reference to it
+ * @pasid: valid address where the PASID will be stored
+ * @flags: bond propertiesQuoted 8 lines
+ * @drvdata: private data passed to the mm exit handler
+ *
+ * Create a bond between device and task, allowing the device to access the mm
+ * using the returned PASID. Ifunbind()isn't called first, a subsequentbind()
+ * for the same device and mm fails with-EEXIST.
+ *
+ *iommu_sva_device_init()must be called first, to initialize the required SVA
+ * features. @flags is a subset of these features.
Quoted 1 line
@flags must be a subset of these features?
Ok
Quoted 2 lines
don't you want to check flags is a subset of
dev->iommu_param->sva_param->features?
Yes, that will be in next version
Quoted 8 lines
Quoted 8 lines
+/**
+ *iommu_sva_unbind_device()- Remove a bond created withiommu_sva_bind_device
+ * @dev: the device
+ * @pasid: the pasid returned bybind()
+ *
+ * Remove bond between device and address space identified by @pasid. Users
+ * should not callunbind()if the corresponding mm exited (as the PASID might
+ * have been reallocated for another process).
Quoted 8 lines
Quoted 7 lines
+ *
+ * The device must not be issuing any more transaction for this PASID. All
+ * outstanding page requests for this PASID must have been flushed to the IOMMU.
+ *
+ * Returns 0 on success, or an error value
+ */
+intiommu_sva_unbind_device(struct device *dev, int pasid)returned value needed?
I'd rather keep this one, since it already pointed out some of my bugs during regression testing.
Thanks,
Jean
PATCH v2 3/40 iommu/sva: Manage process address spaces
In reply to Jean-Philippe BruckerAllocate IOMMU mm structures and binding them to devices. Four operations are added to IOMMU drivers:
* mm_alloc(): to create an io_mm structure and perform architecture- specific operations required to grab the process (for instance on ARM, pin down the CPU ASID so that the process doesn't get assigned a new ASID on rollover).
There is a single valid io_mm structure per Linux mm. Future extensions may also use io_mm for kernel-managed address spaces, populated with map()/unmap() calls instead of bound to process address spaces. This patch focuses on "shared" io_mm.
* mm_attach(): attach an mm to a device. The IOMMU driver checks that the device is capable of sharing an address space, and writes the PASID table entry to install the pgd.
Some IOMMU drivers will have a single PASID table per domain, for convenience. Other can implement it differently but to help these drivers, mm_attach and mm_detach take 'attach_domain' and 'detach_domain' parameters, that tell whether they need to set and clear the PASID entry or only send the required TLB invalidations.
* mm_detach(): detach an mm from a device. The IOMMU driver removes the PASID table entry and invalidates the IOTLBs.
* mm_free(): free a structure allocated by mm_alloc(), and let arch release the process.
mm_attach and mm_detach operations are serialized with a spinlock. When trying to optimize this code, we should at least prevent concurrent attach()/detach() on the same domain (so multi-level PASID table code can allocate tables lazily). mm_alloc() can sleep, but mm_free must not (because we'll have to call it from call_srcu later on).
At the moment we use an IDR for allocating PASIDs and retrieving contexts. We also use a single spinlock. These can be refined and optimized later (a custom allocator will be needed for top-down PASID allocation).
Keeping track of address spaces requires the use of MMU notifiers. Handling process exit with regard to unbind() is tricky, so it is left for another patch and we explicitly fail mm_alloc() for the moment.
---v1->v2: sanity-check of flags
--- drivers/iommu/iommu-sva.c | 380 +++++++++++++++++++++++++++++++++++++- drivers/iommu/iommu.c | 1 + include/linux/iommu.h | 28 +++ 3 files changed, 406 insertions(+), 3 deletions(-)
drivers/iommu/iommu-sva.c +377 -3
@@ -5,8 +5,298 @@* Copyright (C) 2018 ARM Ltd. */ +#include <linux/idr.h> #include <linux/iommu.h> +#include <linux/sched/mm.h> #include <linux/slab.h> +#include <linux/spinlock.h> + +/** +* DOC: io_mm model +* +* The io_mm keeps track of process address spaces shared between CPU and IOMMU. +* The following example illustrates the relation between structures +* iommu_domain, io_mm and iommu_bond. An iommu_bond is a link between io_mm and +* device. A device can have multiple io_mm and an io_mm may be bound to +* multiple devices. +* ___________________________ +* | IOMMU domain A | +* | ________________ | +* | | IOMMU group | +------- io_pgtables +* | | | | +* | | dev 00:00.0 ----+------- bond --- io_mm X +* | |________________| \ | +* | '----- bond ---. +* |___________________________| \ +* ___________________________ \ +* | IOMMU domain B | io_mm Y +* | ________________ | / / +* | | IOMMU group | | / / +* | | | | / / +* | | dev 00:01.0 ------------ bond -' / +* | | dev 00:01.1 ------------ bond --' +* | |________________| | +* | +------- io_pgtables +* |___________________________| +* +* In this example, device 00:00.0 is in domain A, devices 00:01.* are in domain +* B. All devices within the same domain access the same address spaces. Device +* 00:00.0 accesses address spaces X and Y, each corresponding to an mm_struct. +* Devices 00:01.* only access address space Y. In addition each +* IOMMU_DOMAIN_DMA domain has a private address space, io_pgtable, that is +* managed with iommu_map()/iommu_unmap(), and isn't shared with the CPU MMU. +* +* To obtain the above configuration, users would for instance issue the +* following calls: +* +* iommu_sva_bind_device(dev 00:00.0, mm X, ...) -> PASID 1 +* iommu_sva_bind_device(dev 00:00.0, mm Y, ...) -> PASID 2 +* iommu_sva_bind_device(dev 00:01.0, mm Y, ...) -> PASID 2 +* iommu_sva_bind_device(dev 00:01.1, mm Y, ...) -> PASID 2 +* +* A single Process Address Space ID (PASID) is allocated for each mm. In the +* example, devices use PASID 1 to read/write into address space X and PASID 2 +* to read/write into address space Y. +* +* Hardware tables describing this configuration in the IOMMU would typically +* look like this: +* +* PASID tables +* of domain A +* .->+--------+ +* / 0 | |-------> io_pgtable +* / +--------+ +* Device tables / 1 | |-------> pgd X +* +--------+ / +--------+ +* 00:00.0 | A |-' 2 | |--. +* +--------+ +--------+ \ +* : : 3 | | \ +* +--------+ +--------+ --> pgd Y +* 00:01.0 | B |--. / +* +--------+ \ | +* 00:01.1 | B |----+ PASID tables | +* +--------+ \ of domain B | +* '->+--------+ | +* 0 | |-- | --> io_pgtable +* +--------+ | +* 1 | | | +* +--------+ | +* 2 | |---' +* +--------+ +* 3 | | +* +--------+ +* +* With this model, a single call binds all devices in a given domain to an +* address space. Other devices in the domain will get the same bond implicitly. +* However, users must issue one bind() for each device, because IOMMUs may +* implement SVA differently. Furthermore, mandating one bind() per device +* allows the driver to perform sanity-checks on device capabilities. +* +* On Arm and AMD IOMMUs, entry 0 of the PASID table can be used to hold +* non-PASID translations. In this case PASID 0 is reserved and entry 0 points +* to the io_pgtable base. On Intel IOMMU, the io_pgtable base would be held in +* the device table and PASID 0 would be available to the allocator. +*/ + +struct iommu_bond { +struct io_mm *io_mm; +struct device *dev; +struct iommu_domain *domain; + +struct list_head mm_head; +struct list_head dev_head; +struct list_head domain_head; + +void *drvdata; +}; + +/* +* Because we're using an IDR, PASIDs are limited to 31 bits (the sign bit is +* used for returning errors). In practice implementations will use at most 20 +* bits, which is the PCI limit. +*/ +static DEFINE_IDR (iommu_pasid_idr); + +/* +* For the moment this is an all-purpose lock. It serializes +* access/modifications to bonds, access/modifications to the PASID IDR, and +* changes to io_mm refcount as well. +*/ +static DEFINE_SPINLOCK (iommu_sva_lock); + +static struct io_mm * +io_mm_alloc (struct iommu_domain *domain,struct device *dev, +struct mm_struct *mm,unsigned long flags) +{ +int ret; +int pasid; +struct io_mm *io_mm; +struct iommu_sva_param *param = dev->iommu_param->sva_param; + +if (!domain->ops->mm_alloc || !domain->ops->mm_free) +return ERR_PTR (-ENODEV ); + + io_mm = domain->ops->mm_alloc (domain, mm, flags); +if (IS_ERR (io_mm)) +return io_mm; +if (!io_mm) +return ERR_PTR (-ENOMEM ); + +/* +* The mm must not be freed until after the driver frees the io_mm +* (which may involve unpinning the CPU ASID for instance, requiring a +* valid mm struct.) +*/ +mmgrab (mm); + + io_mm->flags = flags; + io_mm->mm = mm; + io_mm->release = domain->ops->mm_free; +INIT_LIST_HEAD (&io_mm->devices); + +idr_preload (GFP_KERNEL ); +spin_lock (&iommu_sva_lock); + pasid =idr_alloc (&iommu_pasid_idr, io_mm, param->min_pasid, + param->max_pasid +1 ,GFP_ATOMIC ); + io_mm->pasid = pasid; +spin_unlock (&iommu_sva_lock); +idr_preload_end (); + +if (pasid <0 ) { + ret = pasid; + goto err_free_mm; + } + +/* TODO: keep track of mm. For the moment, abort. */ + ret = -ENOSYS ; +spin_lock (&iommu_sva_lock); +idr_remove (&iommu_pasid_idr, io_mm->pasid); +spin_unlock (&iommu_sva_lock); + +err_free_mm: + domain->ops->mm_free (io_mm); +mmdrop (mm); + +return ERR_PTR (ret); +} + +static void io_mm_free (struct io_mm *io_mm) +{ +struct mm_struct *mm = io_mm->mm; + + io_mm->release (io_mm); +mmdrop (mm); +} + +static void io_mm_release (struct kref *kref) +{ +struct io_mm *io_mm; + + io_mm = container_of(kref, struct io_mm, kref); +WARN_ON (!list_empty (&io_mm->devices)); + +idr_remove (&iommu_pasid_idr, io_mm->pasid); + +io_mm_free (io_mm); +} + +/* +* Returns non-zero if a reference to the io_mm was successfully taken. +* Returns zero if the io_mm is being freed and should not be used. +*/ +static int io_mm_get_locked (struct io_mm *io_mm) +{ +if (io_mm) +return kref_get_unless_zero (&io_mm->kref); + +return 0 ; +} + +static void io_mm_put_locked (struct io_mm *io_mm) +{ +kref_put (&io_mm->kref, io_mm_release); +} + +static void io_mm_put (struct io_mm *io_mm) +{ +spin_lock (&iommu_sva_lock); +io_mm_put_locked (io_mm); +spin_unlock (&iommu_sva_lock); +} + +static int io_mm_attach (struct iommu_domain *domain,struct device *dev, +struct io_mm *io_mm,void *drvdata) +{ +int ret; +bool attach_domain = true; +int pasid = io_mm->pasid; +struct iommu_bond *bond, *tmp; +struct iommu_sva_param *param = dev->iommu_param->sva_param; + +if (!domain->ops->mm_attach || !domain->ops->mm_detach) +return -ENODEV ; + +if (pasid > param->max_pasid || pasid < param->min_pasid) +return -ERANGE ; + + bond =kzalloc (sizeof (*bond),GFP_KERNEL ); +if (!bond) +return -ENOMEM ; + + bond->domain = domain; + bond->io_mm = io_mm; + bond->dev = dev; + bond->drvdata = drvdata; + +spin_lock (&iommu_sva_lock); +/* +* Check if this io_mm is already bound to the domain. In which case the +* IOMMU driver doesn't have to install the PASID table entry. +*/ +list_for_each_entry (tmp, &domain->mm_list, domain_head) { +if (tmp->io_mm == io_mm) { + attach_domain = false; +break ; + } + } + + ret = domain->ops->mm_attach (domain, dev, io_mm, attach_domain); +if (ret) { +kfree (bond); +spin_unlock (&iommu_sva_lock); +return ret; + } + +list_add (&bond->mm_head, &io_mm->devices); +list_add (&bond->domain_head, &domain->mm_list); +list_add (&bond->dev_head, ¶m->mm_list); +spin_unlock (&iommu_sva_lock); + +return 0 ; +} + +static void io_mm_detach_locked (struct iommu_bond *bond) +{ +struct iommu_bond *tmp; +bool detach_domain = true; +struct iommu_domain *domain = bond->domain; + +list_for_each_entry (tmp, &domain->mm_list, domain_head) { +if (tmp->io_mm == bond->io_mm && tmp->dev != bond->dev) { + detach_domain = false; +break ; + } + } + + domain->ops->mm_detach (domain, bond->dev, bond->io_mm, detach_domain); + +list_del (&bond->mm_head); +list_del (&bond->domain_head); +list_del (&bond->dev_head); +io_mm_put_locked (bond->io_mm); + +kfree (bond); +}/** * iommu_sva_device_init() - Initialize Shared Virtual Addressing for a device
@@ -47,6 +337,7 @@ int iommu_sva_device_init(struct device *dev, unsigned long features, param->features = features; param->max_pasid = max_pasid; +INIT_LIST_HEAD (¶m->mm_list);/* * IOMMU driver updates the limits depending on the IOMMU and device
@@ -114,13 +405,87 @@ EXPORT_SYMBOL_GPL(iommu_sva_device_shutdown);int __iommu_sva_bind_device (struct device *dev,struct mm_struct *mm,int *pasid,unsigned long flags,void *drvdata) { -return -ENOSYS ;/* TODO */ +int i, ret =0 ; +struct io_mm *io_mm =NULL ; +struct iommu_domain *domain; +struct iommu_bond *bond =NULL , *tmp; +struct iommu_sva_param *param = dev->iommu_param->sva_param; + + domain =iommu_get_domain_for_dev (dev); +if (!domain) +return -EINVAL ; + +/* +* The device driver does not call sva_device_init/shutdown and +* bind/unbind concurrently, so no need to take the param lock. +*/ +if (WARN_ON_ONCE (!param) || (flags & ~param->features)) +return -EINVAL ; + +/* If an io_mm already exists, use it */ +spin_lock (&iommu_sva_lock); +idr_for_each_entry (&iommu_pasid_idr, io_mm, i) { +if (io_mm->mm == mm &&io_mm_get_locked (io_mm)) { +/* ... Unless it's already bound to this device */ +list_for_each_entry (tmp, &io_mm->devices, mm_head) { +if (tmp->dev == dev) { + bond = tmp; +io_mm_put_locked (io_mm); +break ; + } + } +break ; + } + } +spin_unlock (&iommu_sva_lock); + +if (bond) +return -EEXIST ; + +/* Require identical features within an io_mm for now */ +if (io_mm && (flags != io_mm->flags)) { +io_mm_put (io_mm); +return -EDOM ; + } + +if (!io_mm) { + io_mm =io_mm_alloc (domain, dev, mm, flags); +if (IS_ERR (io_mm)) +return PTR_ERR (io_mm); + } + + ret =io_mm_attach (domain, dev, io_mm, drvdata); +if (ret) +io_mm_put (io_mm); +else + *pasid = io_mm->pasid; + +return ret; }EXPORT_SYMBOL_GPL (__iommu_sva_bind_device);int __iommu_sva_unbind_device (struct device *dev,int pasid) { -return -ENOSYS ;/* TODO */ +int ret = -ESRCH ; +struct iommu_domain *domain; +struct iommu_bond *bond =NULL ; +struct iommu_sva_param *param = dev->iommu_param->sva_param; + + domain =iommu_get_domain_for_dev (dev); +if (!param ||WARN_ON (!domain)) +return -EINVAL ; + +spin_lock (&iommu_sva_lock); +list_for_each_entry (bond, ¶m->mm_list, dev_head) { +if (bond->io_mm->pasid == pasid) { +io_mm_detach_locked (bond); + ret =0 ; +break ; + } + } +spin_unlock (&iommu_sva_lock); + +return ret; }EXPORT_SYMBOL_GPL (__iommu_sva_unbind_device);
@@ -132,6 +497,15 @@ EXPORT_SYMBOL_GPL(__iommu_sva_unbind_device);*/ void __iommu_sva_unbind_dev_all (struct device *dev) { -/* TODO */ +struct iommu_sva_param *param; +struct iommu_bond *bond, *next; + + param = dev->iommu_param->sva_param; +if (param) { +spin_lock (&iommu_sva_lock); +list_for_each_entry_safe (bond, next, ¶m->mm_list, dev_head) +io_mm_detach_locked (bond); +spin_unlock (&iommu_sva_lock); + } }EXPORT_SYMBOL_GPL (__iommu_sva_unbind_dev_all);
drivers/iommu/iommu.c +1 -0
@@ -1463,6 +1463,7 @@ static struct iommu_domain *__iommu_domain_alloc(struct bus_type *bus, domain->type = type;/* Assume all sizes by default; the driver may override this later */ domain->pgsize_bitmap = bus->iommu_ops->pgsize_bitmap; +INIT_LIST_HEAD (&domain->mm_list);return domain; }
include/linux/iommu.h +28 -0
@@ -100,6 +100,20 @@ struct iommu_domain {void *handler_token;struct iommu_domain_geometry geometry;void *iova_cookie; + +struct list_head mm_list; +}; + +struct io_mm { +int pasid; +/* IOMMU_SVA_FEAT_* */ +unsigned long flags; +struct list_head devices; +struct kref kref; +struct mm_struct *mm; + +/* Release callback for this mm */ +void (*release)(struct io_mm *io_mm); };enum iommu_cap {
@@ -216,6 +230,7 @@ struct iommu_sva_param {unsigned long features;unsigned int min_pasid;unsigned int max_pasid; +struct list_head mm_list; };/**
@@ -227,6 +242,11 @@ struct iommu_sva_param {* @detach_dev: detach device from an iommu domain * @sva_device_init: initialize Shared Virtual Adressing for a device * @sva_device_shutdown: shutdown Shared Virtual Adressing for a device +* @mm_alloc: allocate io_mm +* @mm_free: free io_mm +* @mm_attach: attach io_mm to a device. Install PASID entry if necessary +* @mm_detach: detach io_mm from a device. Remove PASID entry and +* flush associated TLB entries. * @map: map a physically contiguous memory region to an iommu domain * @unmap: unmap a physically contiguous memory region from an iommu domain * @map_sg: map a scatter-gather list of physically contiguous memory chunks
@@ -268,6 +288,14 @@ struct iommu_ops { struct iommu_sva_param *param);void (*sva_device_shutdown)(struct device *dev,struct iommu_sva_param *param); +struct io_mm *(*mm_alloc)(struct iommu_domain *domain, +struct mm_struct *mm, +unsigned long flags); +void (*mm_free)(struct io_mm *io_mm); +int (*mm_attach)(struct iommu_domain *domain,struct device *dev, +struct io_mm *io_mm,bool attach_domain); +void (*mm_detach)(struct iommu_domain *domain,struct device *dev, +struct io_mm *io_mm,bool detach_domain);int (*map)(struct iommu_domain *domain,unsigned long iova,phys_addr_t paddr,size_t size,int prot); size_t (*unmap)(struct iommu_domain *domain, unsigned long iova,
Signature · 1 line
2.17.0
Re: [PATCH v2 03/40] iommu/sva: Manage process address spaces
Quoted 156 lines · On Fri, 11 May 2018 20:06:04 +0100 Jean-Philippe Brucker…
Allocate IOMMU mm structures and binding them to devices. Four operations are added to IOMMU drivers:
*
mm_alloc(): to create anio_mmstructure and perform architecture- specific operations required to grab the process (for instance on
ARM, pin down the CPU ASID so that the process doesn't get assigned a new ASID on rollover).There is a single valid
io_mmstructure per Linux mm. Future
extensions may also useio_mmfor kernel-managed address spaces, populated with map()/unmap() calls instead of bound to process address spaces. This patch focuses on "shared"io_mm.*
mm_attach(): attach an mm to a device. The IOMMU driver checks that the device is capable of sharing an address space, and writes the PASID table entry to install the pgd.Some IOMMU drivers will have a single PASID table per domain, for convenience. Other can implement it differently but to help these drivers,
mm_attachandmm_detachtake 'attach_domain' and 'detach_domain' parameters, that tell whether they need to set and
clear the PASID entry or only send the required TLB invalidations.*
mm_detach(): detach an mm from a device. The IOMMU driver removes the PASID table entry and invalidates the IOTLBs.*
mm_free(): free a structure allocated bymm_alloc(), and let arch release the process.
mm_attachandmm_detachoperations are serialized with a spinlock. When trying to optimize this code, we should at least prevent concurrent attach()/detach() on the same domain (so multi-level PASID table code can allocate tables lazily).mm_alloc()can sleep, butmm_freemust not (because we'll have to call it fromcall_srculater on).At the moment we use an IDR for allocating PASIDs and retrieving contexts. We also use a single spinlock. These can be refined and optimized later (a custom allocator will be needed for top-down PASID allocation).
Keeping track of address spaces requires the use of MMU notifiers. Handling process exit with regard to
unbind()is tricky, so it is left for another patch and we explicitly failmm_alloc()for the moment.Signed-off-by Jean-Philippe Brucker <[email protected]>---
v1->v2: sanity-check of flags--- drivers/iommu/iommu-sva.c | 380 +++++++++++++++++++++++++++++++++++++- drivers/iommu/iommu.c | 1 + include/linux/iommu.h | 28 +++ 3 files changed, 406 insertions(+), 3 deletions(-)drivers/iommu/iommu-sva.c +8
-0@@ -5,8 +5,298 @@* Copyright (C) 2018 ARM Ltd. */ +#include <linux/idr.h> #include <linux/iommu.h> +#include <linux/sched/mm.h> #include <linux/slab.h> +#include <linux/spinlock.h> + +/** +* DOC: io_mm model +* +* The io_mm keeps track of process address spaces shared between CPU and IOMMU.
+ * The following example illustrates the relation between structures
+ *iommu_domain,io_mmandiommu_bond. Aniommu_bondis a link betweenio_mmand
+ * device. A device can have multipleio_mmand anio_mmmay be bound to
+ * multiple devices.
+ * ___________________________+ * | IOMMU domain A | + * | ________________ | + * | | IOMMU group | +------- io_pgtables + * | | | | + * | | dev 00:00.0 ----+------- bond --- io_mm X + * | |________________| \ | + * | '----- bond ---. + * |___________________________| \ + * ___________________________ \ + * | IOMMU domain B | io_mm Y + * | ________________ | / / + * | | IOMMU group | | / / + * | | | | / / + * | | dev 00:01.0 ------------ bond -' / + * | | dev 00:01.1 ------------ bond --' + * | |________________| | + * | +------- io_pgtables + * |___________________________|+ *
+ * In this example, device 00:00.0 is in domain A, devices 00:01.* are in domain
+ * B. All devices within the same domain access the same address spaces. Device
+ * 00:00.0 accesses address spaces X and Y, each corresponding to anmm_struct.
+ * Devices 00:01.* only access address space Y. In addition each
+ *IOMMU_DOMAIN_DMAdomain has a private address space,io_pgtable, that is
+ * managed with iommu_map()/iommu_unmap(), and isn't shared with the CPU MMU.
+ *
+ * To obtain the above configuration, users would for instance issue the
+ * following calls:
+ *+ * iommu_sva_bind_device(dev 00:00.0, mm X, ...) -> PASID 1 + * iommu_sva_bind_device(dev 00:00.0, mm Y, ...) -> PASID 2 + * iommu_sva_bind_device(dev 00:01.0, mm Y, ...) -> PASID 2 + * iommu_sva_bind_device(dev 00:01.1, mm Y, ...) -> PASID 2+ *
+ * A single Process Address Space ID (PASID) is allocated for each mm. In the
+ * example, devices use PASID 1 to read/write into address space X and PASID 2
+ * to read/write into address space Y.
+ *
+ * Hardware tables describing this configuration in the IOMMU would typically
+ * look like this:
+ *
+ * PASID tables
+ * of domain A
+ * .->+--------++ * / 0 | |-------> io_pgtable + * / +--------+ + * Device tables / 1 | |-------> pgd X + * +--------+ / +--------+ + * 00:00.0 | A |-' 2 | |--. + * +--------+ +--------+ \ + * : : 3 | | \ + * +--------+ +--------+ --> pgd Y + * 00:01.0 | B |--. / + * +--------+ \ | + * 00:01.1 | B |----+ PASID tables | + * +--------+ \ of domain B | + * '->+--------+ | + * 0 | |-- | --> io_pgtable + * +--------+ | + * 1 | | | + * +--------+ | + * 2 | |---' + * +--------+ + * 3 | | + * +--------+
Quoted 1 line
+ *
I am a little confused about domain vs. pasid relationship. If each domain represents a address space, should there be a domain for each pasid?
Quoted 429 lines
+ * With this model, a single call binds all devices in a given domain to an
+ * address space. Other devices in the domain will get the same bond implicitly.
+ * However, users must issue onebind()for each device, because IOMMUs may
+ * implement SVA differently. Furthermore, mandating onebind()per device
+ * allows the driver to perform sanity-checks on device capabilities.
+ *
+ * On Arm and AMD IOMMUs, entry 0 of the PASID table can be used to hold
+ * non-PASID translations. In this case PASID 0 is reserved and entry 0 points
+ * to theio_pgtablebase. On Intel IOMMU, theio_pgtablebase would be held in
+ * the device table and PASID 0 would be available to the allocator.
+ */drivers/iommu/iommu-sva.c +15
-0+ +struct iommu_bond { +struct io_mm *io_mm; +struct device *dev; +struct iommu_domain *domain; + +struct list_head mm_head; +struct list_head dev_head; +struct list_head domain_head; + +void *drvdata; +}; + +/* +* Because we're using an IDR, PASIDs are limited to 31 bits (the sign bit is
+ * used for returning errors). In practice implementations will use at most 20
+ * bits, which is the PCI limit.
+ */drivers/iommu/iommu-sva.c +5
-0+static DEFINE_IDR (iommu_pasid_idr); + +/* +* For the moment this is an all-purpose lock. It serializes +* access/modifications to bonds, access/modifications to the PASID IDR, and
+ * changes toio_mmrefcount as well.
+ */drivers/iommu/iommu-sva.c +22
-0+static DEFINE_SPINLOCK (iommu_sva_lock); + +static struct io_mm * +io_mm_alloc (struct iommu_domain *domain,struct device *dev, +struct mm_struct *mm,unsigned long flags) +{ +int ret; +int pasid; +struct io_mm *io_mm; +struct iommu_sva_param *param = dev->iommu_param->sva_param; + +if (!domain->ops->mm_alloc || !domain->ops->mm_free) +return ERR_PTR (-ENODEV ); + + io_mm = domain->ops->mm_alloc (domain, mm, flags); +if (IS_ERR (io_mm)) +return io_mm; +if (!io_mm) +return ERR_PTR (-ENOMEM ); + +/* +* The mm must not be freed until after the driver frees the
io_mmdrivers/iommu/iommu-sva.c +1
-0+* (which may involve unpinning the CPU ASID for instance, requiring a
drivers/iommu/iommu-sva.c +57
-0+* valid mm struct.) +*/ +mmgrab (mm); + + io_mm->flags = flags; + io_mm->mm = mm; + io_mm->release = domain->ops->mm_free; +INIT_LIST_HEAD (&io_mm->devices); + +idr_preload (GFP_KERNEL ); +spin_lock (&iommu_sva_lock); + pasid =idr_alloc (&iommu_pasid_idr, io_mm, param->min_pasid, + param->max_pasid +1 ,GFP_ATOMIC ); + io_mm->pasid = pasid; +spin_unlock (&iommu_sva_lock); +idr_preload_end (); + +if (pasid <0 ) { + ret = pasid; + goto err_free_mm; + } + +/* TODO: keep track of mm. For the moment, abort. */ + ret = -ENOSYS ; +spin_lock (&iommu_sva_lock); +idr_remove (&iommu_pasid_idr, io_mm->pasid); +spin_unlock (&iommu_sva_lock); + +err_free_mm: + domain->ops->mm_free (io_mm); +mmdrop (mm); + +return ERR_PTR (ret); +} + +static void io_mm_free (struct io_mm *io_mm) +{ +struct mm_struct *mm = io_mm->mm; + + io_mm->release (io_mm); +mmdrop (mm); +} + +static void io_mm_release (struct kref *kref) +{ +struct io_mm *io_mm; + + io_mm = container_of(kref, struct io_mm, kref); +WARN_ON (!list_empty (&io_mm->devices)); + +idr_remove (&iommu_pasid_idr, io_mm->pasid); + +io_mm_free (io_mm); +} + +/* +* Returns non-zero if a reference to the io_mm was successfully taken.
+ * Returns zero if theio_mmis being freed and should not be used.
+ */drivers/iommu/iommu-sva.c +21
-0+static int io_mm_get_locked (struct io_mm *io_mm) +{ +if (io_mm) +return kref_get_unless_zero (&io_mm->kref); + +return 0 ; +} + +static void io_mm_put_locked (struct io_mm *io_mm) +{ +kref_put (&io_mm->kref, io_mm_release); +} + +static void io_mm_put (struct io_mm *io_mm) +{ +spin_lock (&iommu_sva_lock); +io_mm_put_locked (io_mm); +spin_unlock (&iommu_sva_lock); +} + +static int io_mm_attach(struct iommu_domain *domain, struct device*dev,
drivers/iommu/iommu-sva.c +26
-0+ struct io_mm *io_mm, void *drvdata) +{ + int ret; +bool attach_domain = true; +int pasid = io_mm->pasid; +struct iommu_bond *bond, *tmp; +struct iommu_sva_param *param = dev->iommu_param->sva_param; + +if (!domain->ops->mm_attach || !domain->ops->mm_detach) +return -ENODEV ; + +if (pasid > param->max_pasid || pasid < param->min_pasid) +return -ERANGE ; + + bond =kzalloc (sizeof (*bond),GFP_KERNEL ); +if (!bond) +return -ENOMEM ; + + bond->domain = domain; + bond->io_mm = io_mm; + bond->dev = dev; + bond->drvdata = drvdata; + +spin_lock (&iommu_sva_lock); +/* +* Check if this io_mm is already bound to the domain. In which case the
drivers/iommu/iommu-sva.c +1
-0+* IOMMU driver doesn't have to install the PASID table entry.
drivers/iommu/iommu-sva.c +9
-0+*/ +list_for_each_entry (tmp, &domain->mm_list, domain_head) { +if (tmp->io_mm == io_mm) { + attach_domain = false; +break ; + } + } + + ret = domain->ops->mm_attach(domain, dev, io_mm,
attach_domain);drivers/iommu/iommu-sva.c +22
-0+if (ret) { +kfree (bond); +spin_unlock (&iommu_sva_lock); +return ret; + } + +list_add (&bond->mm_head, &io_mm->devices); +list_add (&bond->domain_head, &domain->mm_list); +list_add (&bond->dev_head, ¶m->mm_list); +spin_unlock (&iommu_sva_lock); + +return 0 ; +} + +static void io_mm_detach_locked(struct iommu_bond *bond) +{ + struct iommu_bond *tmp; + bool detach_domain = true; + struct iommu_domain *domain = bond->domain; + + list_for_each_entry(tmp, &domain->mm_list, domain_head) { + if (tmp->io_mm == bond->io_mm && tmp->dev !=
bond->dev) {drivers/iommu/iommu-sva.c +6
-0+ detach_domain = false; +break ; + } + } + + domain->ops->mm_detach(domain, bond->dev, bond->io_mm,
detach_domain); +drivers/iommu/iommu-sva.c +7
-0+list_del (&bond->mm_head); +list_del (&bond->domain_head); +list_del (&bond->dev_head); +io_mm_put_locked (bond->io_mm); + +kfree (bond); +}/** * iommu_sva_device_init() - Initialize Shared Virtual Addressing for a device @@ -47,6 +337,7 @@ int iommu_sva_device_init(struct device *dev, unsigned long features,
drivers/iommu/iommu-sva.c +1
-0param->features = features; param->max_pasid = max_pasid; +INIT_LIST_HEAD (¶m->mm_list);/* * IOMMU driver updates the limits depending on the IOMMU and device @@ -114,13 +405,87 @@
EXPORT_SYMBOL_GPL(iommu_sva_device_shutdown); int__iommu_sva_bind_device(struct device *dev, struct mm_struct *mm, int *pasid, unsigned long flags, void *drvdata){drivers/iommu/iommu-sva.c +12
-1-return -ENOSYS ;/* TODO */ +int i, ret =0 ; +struct io_mm *io_mm =NULL ; +struct iommu_domain *domain; +struct iommu_bond *bond =NULL , *tmp; +struct iommu_sva_param *param = dev->iommu_param->sva_param; + + domain =iommu_get_domain_for_dev (dev); +if (!domain) +return -EINVAL ; + +/* +* The device driver does not call sva_device_init/shutdown and
drivers/iommu/iommu-sva.c +1
-0+* bind/unbind concurrently, so no need to take the param lock.
drivers/iommu/iommu-sva.c +9
-0+*/ +if (WARN_ON_ONCE (!param) || (flags & ~param->features)) +return -EINVAL ; + +/* If an io_mm already exists, use it */ +spin_lock (&iommu_sva_lock); +idr_for_each_entry (&iommu_pasid_idr, io_mm, i) { +if (io_mm->mm == mm &&io_mm_get_locked (io_mm)) { +/* ... Unless it's already bound to this device */
drivers/iommu/iommu-sva.c +1
-0+ list_for_each_entry(tmp, &io_mm->devices,
mm_head) {drivers/iommu/iommu-sva.c +63
-2+if (tmp->dev == dev) { + bond = tmp; +io_mm_put_locked (io_mm); +break ; + } + } +break ; + } + } +spin_unlock (&iommu_sva_lock); + +if (bond) +return -EEXIST ; + +/* Require identical features within an io_mm for now */ +if (io_mm && (flags != io_mm->flags)) { +io_mm_put (io_mm); +return -EDOM ; + } + +if (!io_mm) { + io_mm =io_mm_alloc (domain, dev, mm, flags); +if (IS_ERR (io_mm)) +return PTR_ERR (io_mm); + } + + ret =io_mm_attach (domain, dev, io_mm, drvdata); +if (ret) +io_mm_put (io_mm); +else + *pasid = io_mm->pasid; + +return ret; }EXPORT_SYMBOL_GPL (__iommu_sva_bind_device);int __iommu_sva_unbind_device (struct device *dev,int pasid) { -return -ENOSYS ;/* TODO */ +int ret = -ESRCH ; +struct iommu_domain *domain; +struct iommu_bond *bond =NULL ; +struct iommu_sva_param *param = dev->iommu_param->sva_param; + + domain =iommu_get_domain_for_dev (dev); +if (!param ||WARN_ON (!domain)) +return -EINVAL ; + +spin_lock (&iommu_sva_lock); +list_for_each_entry (bond, ¶m->mm_list, dev_head) { +if (bond->io_mm->pasid == pasid) { +io_mm_detach_locked (bond); + ret =0 ; +break ; + } + } +spin_unlock (&iommu_sva_lock); + +return ret; }EXPORT_SYMBOL_GPL (__iommu_sva_unbind_device);@@ -132,6 +497,15 @@ EXPORT_SYMBOL_GPL(__iommu_sva_unbind_device);*/ void __iommu_sva_unbind_dev_all (struct device *dev) { -/* TODO */ +struct iommu_sva_param *param; +struct iommu_bond *bond, *next; + + param = dev->iommu_param->sva_param; +if (param) { +spin_lock (&iommu_sva_lock); +list_for_each_entry_safe (bond, next, ¶m->mm_list, dev_head) +io_mm_detach_locked (bond); +spin_unlock (&iommu_sva_lock); + } }
EXPORT_SYMBOL_GPL(__iommu_sva_unbind_dev_all);drivers/iommu/iommu.c +1
-0@@ -1463,6 +1463,7 @@ static struct iommu_domain *__iommu_domain_alloc(struct bus_type *bus, domain->type = type;/* Assume all sizes by default; the driver may override this later */ domain->pgsize_bitmap = bus->iommu_ops->pgsize_bitmap; +INIT_LIST_HEAD (&domain->mm_list);return domain; }include/linux/iommu.h +15
-0@@ -100,6 +100,20 @@ struct iommu_domain {void *handler_token;struct iommu_domain_geometry geometry;void *iova_cookie; + +struct list_head mm_list; +}; + +struct io_mm { +int pasid; +/* IOMMU_SVA_FEAT_* */ +unsigned long flags; +struct list_head devices; +struct kref kref; +struct mm_struct *mm; + +/* Release callback for this mm */ +void (*release)(struct io_mm *io_mm); };enum iommu_cap {@@ -216,6 +230,7 @@ struct iommu_sva_param {unsigned long features;unsigned int min_pasid;unsigned int max_pasid; +struct list_head mm_list; };/** @@ -227,6 +242,11 @@ struct iommu_sva_param {* @detach_dev: detach device from an iommu domain * @sva_device_init: initialize Shared Virtual Adressing for a device * @sva_device_shutdown: shutdown Shared Virtual Adressing for a device
+ * @mm_alloc: allocateio_mm
+ * @mm_free: freeio_mm
+ * @mm_attach: attachio_mmto a device. Install PASID entry if necessary
+ * @mm_detach: detachio_mmfrom a device. Remove PASID entry and
+ * flush associated TLB entries.
* @map: map a physically contiguous memory region to an iommu domain
* @unmap: unmap a physically contiguous memory region from an iommu
domain
* @map_sg: map a scatter-gather list of physically contiguous
memory chunks @@ -268,6 +288,14 @@ structiommu_ops{include/linux/iommu.h +5
-0struct iommu_sva_param *param);void (*sva_device_shutdown)(struct device *dev,struct iommu_sva_param *param); +struct io_mm *(*mm_alloc)(struct iommu_domain *domain, +struct mm_struct *mm, +unsigned long flags); +void (*mm_free)(struct io_mm *io_mm); + int (*mm_attach)(struct iommu_domain *domain, struct device*dev,
include/linux/iommu.h +2
-0+ struct io_mm *io_mm, bool attach_domain); + void (*mm_detach)(struct iommu_domain *domain, struct device*dev,
include/linux/iommu.h +1
-0+ struct io_mm *io_mm, bool detach_domain);int (*map)(struct iommu_domain *domain,unsigned long iova,phys_addr_t paddr,size_t size,int prot); size_t (*unmap)(struct iommu_domain *domain, unsigned long
Quoted 1 line
iova,
[Jacob Pan]
Re: [PATCH v2 03/40] iommu/sva: Manage process address spaces
Quoted 4 lines · Jacob Pan
On Fri, 11 May 2018 20:06:04 +0100
I am a little confused about domain vs. pasid relationship. If each domain represents a address space, should there be a domain for each pasid?
I don't think there is a formal definition, but from previous discussion the consensus seems to be: domains are a collection of devices that have the same virtual address spaces (one or many).
Keeping that definition makes things easier, in my opinion. Some time ago, I did try to represent PASIDs using "subdomains" (introducing a hierarchy of struct iommu_domain), but it required invasive changes in the IOMMU subsystem and probably all over the tree.
You do need some kind of "root domain" for each device, so that "iommu_get_domain_for_dev()" still makes sense. That root domain doesn't have a single address space but a collection of subdomains. If you need this anyway, representing a PASID with an iommu_domain doesn't seem preferable than using a different structure (io_mm), because they don't have anything in common.
Thanks,
Jean
Re: [PATCH v2 03/40] iommu/sva: Manage process address spaces
Quoted 15 lines · On Thu, 17 May 2018 11:02:42 +0100 Jean-Philippe Brucker…
Quoted 4 lines · Jacob Pan
On Fri, 11 May 2018 20:06:04 +0100
I am a little confused about domain vs. pasid relationship. If each domain represents a address space, should there be a domain for each pasid?I don't think there is a formal definition, but from previous discussion the consensus seems to be: domains are a collection of devices that have the same virtual address spaces (one or many).
Keeping that definition makes things easier, in my opinion. Some time ago, I did try to represent PASIDs using "subdomains" (introducing a hierarchy of struct
iommu_domain), but it required invasive changes in the IOMMU subsystem and probably all over the tree.
Quoted 7 lines
You do need some kind of "root domain" for each device, so that "
iommu_get_domain_for_dev()" still makes sense. That root domain doesn't have a single address space but a collection of subdomains. If you need this anyway, representing a PASID with aniommu_domaindoesn't seem preferable than using a different structure (io_mm), because they don't have anything in common.
My main concern is the PASID table storage. If PASID table storage is tied to domain, it is ok to scale up, i.e. multiple devices in a domain share a single PASID table. But to scale down, e.g. further partition device with VFIO mdev for assignment, each mdev may get its own domain via vfio. But there is no IOMMU storage for PASID table at mdev device level. Perhaps we do need root domain or some parent-child relationship to locate PASID table.
Quoted 2 lines
Thanks,
Jean
[Jacob Pan]
Re: [PATCH v2 03/40] iommu/sva: Manage process address spaces
Quoted 25 lines · Jacob Pan
Quoted 15 lines · On Thu, 17 May 2018 11:02:42 +0100 Jean-Philippe Brucker…
Quoted 4 lines · Jacob Pan
On Fri, 11 May 2018 20:06:04 +0100
I am a little confused about domain vs. pasid relationship. If each domain represents a address space, should there be a domain for each pasid?I don't think there is a formal definition, but from previous discussion the consensus seems to be: domains are a collection of devices that have the same virtual address spaces (one or many).
Keeping that definition makes things easier, in my opinion. Some time ago, I did try to represent PASIDs using "subdomains" (introducing a hierarchy of struct
iommu_domain), but it required invasive changes in the IOMMU subsystem and probably all over the tree.Quoted 7 lines
You do need some kind of "root domain" for each device, so that "
iommu_get_domain_for_dev()" still makes sense. That root domain doesn't have a single address space but a collection of subdomains. If you need this anyway, representing a PASID with aniommu_domaindoesn't seem preferable than using a different structure (io_mm), because they don't have anything in common.
Quoted 7 lines
My main concern is the PASID table storage. If PASID table storage is tied to domain, it is ok to scale up, i.e. multiple devices in a domain share a single PASID table. But to scale down, e.g. further partition device with VFIO mdev for assignment, each mdev may get its own domain via vfio. But there is no IOMMU storage for PASID table at mdev device level. Perhaps we do need root domain or some parent-child relationship to locate PASID table.
Interesting, I hadn't thought about this use-case before. At first I thought you were talking about mdev devices assigned to VMs, but I think you're referring to mdevs assigned to userspace drivers instead? Out of curiosity, is it only theoretical or does someone actually need this?
I don't think mdev for VM assignment are compatible with PASID, at least not when the IOMMU is involved. I usually ignore mdev in my reasoning because, as far as I know, currently they only affect devices that have their own MMU, and IOMMU domains don't come into play. However, if a device was backed by the IOMMU, and the device driver wanted to partition it into mdevs, then users would be tempted to assign mdev1 to VM1 and mdev2 to VM2.
It doesn't work with PASID, because the PASID spaces of VM1 and VM2 will conflict. If both VM1 and VM2 allocate PASID #1, then the host has to somehow arbitrate device accesses, for example scheduling first mdev1 then mdev2. That's possible if the device driver is in charge of the MMU, but not really compatible with the IOMMU.
So in the IOMMU subsystem, for assigning devices to VMs the only model that makes sense is SR-IOV, where each VF/mdev has its own RID and its own PASID table. In that case you'd get one IOMMU domain per VF.
But considering userspace drivers in the host alone, it might make sense to partition a device into mdevs and assign them to multiple processes. Interestingly this scheme still doesn't work with the classic MAP/UNMAP
ioctl: since there is a single device context entry for all mdevs, the mediating driver would have to catch all MAP/UNMAP ioctls and reject those with IOVAs that overlap those of another mdev. It's doesn't seem viable. But when using PASID then each mdev has its own address space, and since PASIDs are allocated by the kernel there is no such conflict.
Anyway, I think this use-case can work with the current structures, if mediating driver does the bind() instead of VFIO. That's necessary because you can't let userspace program the PASID into the device, or they would be able to access address spaces owned by other mdevs. Then the mediating driver does the bind(), and keeps internal structures to associate the process to the given mdev.
Thanks,
Jean
Re: [PATCH v2 03/40] iommu/sva: Manage process address spaces
Quoted 4 lines
Interesting, I hadn't thought about this use-case before. At first I thought you were talking about mdev devices assigned to VMs, but I think you're referring to mdevs assigned to userspace drivers instead? Out of curiosity, is it only theoretical or does someone actually need this?
There has been some non upstreamed efforts to have mdev and produce userspace drivers. Huawei is using it on what they call "wrapdrive" for crypto devices and we did a proof of concept for ethernet interfaces. At the time we choose not to involve the IOMMU for the reason you mentioned, but having it there would be good.
Thanks
Ilias
Re: [PATCH v2 03/40] iommu/sva: Manage process address spaces
Quoted 10 lines · Ilias Apalodimas
Quoted 4 lines
Interesting, I hadn't thought about this use-case before. At first I thought you were talking about mdev devices assigned to VMs, but I think you're referring to mdevs assigned to userspace drivers instead? Out of curiosity, is it only theoretical or does someone actually need this?
There has been some non upstreamed efforts to have mdev and produce userspace drivers. Huawei is using it on what they call "wrapdrive" for crypto devices and we did a proof of concept for ethernet interfaces. At the time we choose not to involve the IOMMU for the reason you mentioned, but having it there would be good.
I'm guessing there were good reasons to do it that way but I wonder, is it not simpler to just have the kernel driver create a /dev/foo, with a standard ioctl/mmap/poll interface? Here VFIO adds a layer of indirection, and since the mediating driver has to implement these operations already, what is gained?
Thanks,
Jean
Re: [PATCH v2 03/40] iommu/sva: Manage process address spaces
Quoted 12 lines · Jean-Philippe Brucker
Quoted 10 lines · Ilias Apalodimas
Quoted 4 lines
Interesting, I hadn't thought about this use-case before. At first I thought you were talking about mdev devices assigned to VMs, but I think you're referring to mdevs assigned to userspace drivers instead? Out of curiosity, is it only theoretical or does someone actually need this?
There has been some non upstreamed efforts to have mdev and produce userspace drivers. Huawei is using it on what they call "wrapdrive" for crypto devices and we did a proof of concept for ethernet interfaces. At the time we choose not to involve the IOMMU for the reason you mentioned, but having it there would be good.
Quoted 5 lines
I'm guessing there were good reasons to do it that way but I wonder, is it not simpler to just have the kernel driver create a
/dev/foo, with a standard ioctl/mmap/poll interface? Here VFIO adds a layer of indirection, and since the mediating driver has to implement these operations already, what is gained?
The best reason i can come up with is "common code". You already have one API doing that for you so we replicate it in a /dev file?
The mdev approach still needs extentions to support what we tried to do (i.e mdev bus might need yo have access on iommu_ops), but as far as i undestand it's a possible case.
Quoted 3 lines
Thanks,
Jean
Re: [PATCH v2 03/40] iommu/sva: Manage process address spaces
+CC Kenneth Lee
Quoted 26 lines · On Fri, 25 May 2018 09:33:11 +0300 Ilias Apalodimas…
Quoted 12 lines · Jean-Philippe Brucker
Quoted 10 lines · Ilias Apalodimas
Quoted 4 lines
Interesting, I hadn't thought about this use-case before. At first I thought you were talking about mdev devices assigned to VMs, but I think you're referring to mdevs assigned to userspace drivers instead? Out of curiosity, is it only theoretical or does someone actually need this?
There has been some non upstreamed efforts to have mdev and produce userspace drivers. Huawei is using it on what they call "wrapdrive" for crypto devices and we did a proof of concept for ethernet interfaces. At the time we choose not to involve the IOMMU for the reason you mentioned, but having it there would be good.
Quoted 5 lines
I'm guessing there were good reasons to do it that way but I wonder, is it not simpler to just have the kernel driver create a
/dev/foo, with a standard ioctl/mmap/poll interface? Here VFIO adds a layer of indirection, and since the mediating driver has to implement these operations already, what is gained?The best reason i can come up with is "common code". You already have one API doing that for you so we replicate it in a /dev file?
The mdev approach still needs extentions to support what we tried to do (i.e mdev bus might need yo have access oniommu_ops), but as far as i undestand it's a possible case.Quoted 3 lines
Thanks,
Jean
A message that is not in the archive (sent off-list or not yet received): [email protected]
Re: [PATCH v2 03/40] iommu/sva: Manage process address spaces
Quoted 34 lines · Jonathan Cameron
Date: Fri, 25 May 2018 09:39:59 +0100
From: Jonathan Cameron <[email protected]>
To: Ilias Apalodimas <[email protected]>
CC: Jean-Philippe Brucker <[email protected]>,
"[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, Will Deacon <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "tn-nYOzD4b6Jr9Wk0Htik3J/[email protected]" <tn-nYOzD4b6Jr9Wk0Htik3J/[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "rfranz-YGCgFSpz5w/[email protected]" <rfranz-YGCgFSpz5w/[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, Jacob Pan <jacob.jun.pan-VuQAYsv1563Yd54FQh9/[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org" <linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA@public.gmane.org" <iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA@public.gmane.org>, Robin Murphy <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "baolu.lu-VuQAYsv1563Yd54FQh9/[email protected]" <baolu.lu-VuQAYsv1563Yd54FQh9/[email protected]>, liguozhu-C8/M+/[email protected], [email protected]
Subject: Re: [PATCH v2 03/40] iommu/sva: Manage process address spaces
Message-ID: <[email protected]>
Quoted 28 lines
+CC Kenneth Lee
Quoted 23 lines · On Fri, 25 May 2018 09:33:11 +0300 Ilias Apalodimas…
Quoted 12 lines · Jean-Philippe Brucker
Quoted 10 lines · Ilias Apalodimas
Quoted 4 lines
Interesting, I hadn't thought about this use-case before. At first I thought you were talking about mdev devices assigned to VMs, but I think you're referring to mdevs assigned to userspace drivers instead? Out of curiosity, is it only theoretical or does someone actually need this?
There has been some non upstreamed efforts to have mdev and produce userspace drivers. Huawei is using it on what they call "wrapdrive" for crypto devices and we did a proof of concept for ethernet interfaces. At the time we choose not to involve the IOMMU for the reason you mentioned, but having it there would be good.
Quoted 5 lines
I'm guessing there were good reasons to do it that way but I wonder, is it not simpler to just have the kernel driver create a
/dev/foo, with a standard ioctl/mmap/poll interface? Here VFIO adds a layer of indirection, and since the mediating driver has to implement these operations already, what is gained?The best reason i can come up with is "common code". You already have one API doing that for you so we replicate it in a /dev file?
The mdev approach still needs extentions to support what we tried to do (i.e mdev bus might need yo have access oniommu_ops), but as far as i undestand it's a possible case.
Hi, Jean, Please allow me to share my understanding here:
https://zhuanlan.zhihu.com/p/35489035
The reason we do not use the /dev/foo scheme is that the devices to be shared are programmable accelerators. We cannot fix up the kernel driver for them.
Quoted 5 lines
Quoted 3 lines
Quoted 3 lines
Thanks,
Jean
Signature · 1 line
-Kenneth Lee (Hisilicon)
Re: [PATCH v2 03/40] iommu/sva: Manage process address spaces
In reply to Jonathan CameronQuoted 34 lines · Jonathan Cameron
Date: Fri, 25 May 2018 09:39:59 +0100
From: Jonathan Cameron <[email protected]>
To: Ilias Apalodimas <[email protected]>
CC: Jean-Philippe Brucker <[email protected]>,
"[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, Will Deacon <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, Jacob Pan <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, Robin Murphy <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, [email protected], [email protected]
Subject: Re: [PATCH v2 03/40] iommu/sva: Manage process address spaces
Message-ID: <[email protected]>
Quoted 28 lines
+CC Kenneth Lee
Quoted 23 lines · On Fri, 25 May 2018 09:33:11 +0300 Ilias Apalodimas…
Quoted 12 lines · Jean-Philippe Brucker
Quoted 10 lines · Ilias Apalodimas
Quoted 4 lines
Interesting, I hadn't thought about this use-case before. At first I thought you were talking about mdev devices assigned to VMs, but I think you're referring to mdevs assigned to userspace drivers instead? Out of curiosity, is it only theoretical or does someone actually need this?
There has been some non upstreamed efforts to have mdev and produce userspace drivers. Huawei is using it on what they call "wrapdrive" for crypto devices and we did a proof of concept for ethernet interfaces. At the time we choose not to involve the IOMMU for the reason you mentioned, but having it there would be good.
Quoted 5 lines
I'm guessing there were good reasons to do it that way but I wonder, is it not simpler to just have the kernel driver create a
/dev/foo, with a standard ioctl/mmap/poll interface? Here VFIO adds a layer of indirection, and since the mediating driver has to implement these operations already, what is gained?The best reason i can come up with is "common code". You already have one API doing that for you so we replicate it in a /dev file?
The mdev approach still needs extentions to support what we tried to do (i.e mdev bus might need yo have access oniommu_ops), but as far as i undestand it's a possible case.
Hi, Jean, Please allow me to share my understanding here:
https://zhuanlan.zhihu.com/p/35489035
The reason we do not use the /dev/foo scheme is that the devices to be shared are programmable accelerators. We cannot fix up the kernel driver for them.
Quoted 5 lines
Quoted 3 lines
Quoted 3 lines
Thanks,
Jean
Signature · 1 line
-Kenneth Lee (Hisilicon)
PATCH v2 3/40 iommu/sva: Manage process address spaces
In reply to Jonathan CameronQuoted 34 lines · Jonathan Cameron
Date: Fri, 25 May 2018 09:39:59 +0100
From: Jonathan Cameron <[email protected]>
To: Ilias Apalodimas <[email protected]>
CC: Jean-Philippe Brucker <[email protected]>,
"xieyisheng1 at huawei.com" <[email protected]>, "kvm at vger.kernel.org" <[email protected]>, "linux-pci at vger.kernel.org" <[email protected]>, "xuzaibo at huawei.com" <[email protected]>, Will Deacon <[email protected]>, "okaya at codeaurora.org" <[email protected]>, "linux-mm at kvack.org" <[email protected]>, "yi.l.liu at intel.com" <[email protected]>, "ashok.raj at intel.com" <[email protected]>, "tn at semihalf.com" <[email protected]>, "joro at 8bytes.org" <[email protected]>, "robdclark at gmail.com" <[email protected]>, "bharatku at xilinx.com" <[email protected]>, "linux-acpi at vger.kernel.org" <[email protected]>, "liudongdong3 at huawei.com" <[email protected]>, "rfranz at cavium.com" <[email protected]>, "devicetree at vger.kernel.org" <[email protected]>, "kevin.tian at intel.com" <[email protected]>, Jacob Pan <[email protected]>, "alex.williamson at redhat.com" <[email protected]>, "rgummal at xilinx.com" <[email protected]>, "thunder.leizhen at huawei.com" <[email protected]>, "linux-arm-kernel at lists.infradead.org" <[email protected]>, "shunyong.yang at hxt-semitech.com" <[email protected]>, "dwmw2 at infradead.org" <[email protected]>, "liubo95 at huawei.com" <[email protected]>, "jcrouse at codeaurora.org" <[email protected]>,
"iommu at lists.linux-foundation.org" <[email protected]>, Robin Murphy <[email protected]>, "christian.koenig at amd.com" <[email protected]>, "nwatters at codeaurora.org" <[email protected]>, "baolu.lu at linux.intel.com" <[email protected]>, liguozhu at hisilicon.com, kenneth-lee-2012 at foxmail.com
Subject: Re: [PATCH v2 03/40] iommu/sva: Manage process address spaces
Message-ID: <[email protected]>
Quoted 28 lines
+CC Kenneth Lee
Quoted 23 lines · On Fri, 25 May 2018 09:33:11 +0300 Ilias Apalodimas…
Quoted 12 lines · Jean-Philippe Brucker
Quoted 10 lines · Ilias Apalodimas
Quoted 4 lines
Interesting, I hadn't thought about this use-case before. At first I thought you were talking about mdev devices assigned to VMs, but I think you're referring to mdevs assigned to userspace drivers instead? Out of curiosity, is it only theoretical or does someone actually need this?
There has been some non upstreamed efforts to have mdev and produce userspace drivers. Huawei is using it on what they call "wrapdrive" for crypto devices and we did a proof of concept for ethernet interfaces. At the time we choose not to involve the IOMMU for the reason you mentioned, but having it there would be good.
Quoted 5 lines
I'm guessing there were good reasons to do it that way but I wonder, is it not simpler to just have the kernel driver create a
/dev/foo, with a standard ioctl/mmap/poll interface? Here VFIO adds a layer of indirection, and since the mediating driver has to implement these operations already, what is gained?The best reason i can come up with is "common code". You already have one API doing that for you so we replicate it in a /dev file?
The mdev approach still needs extentions to support what we tried to do (i.e mdev bus might need yo have access oniommu_ops), but as far as i undestand it's a possible case.
Hi, Jean, Please allow me to share my understanding here:
https://zhuanlan.zhihu.com/p/35489035
The reason we do not use the /dev/foo scheme is that the devices to be shared are programmable accelerators. We cannot fix up the kernel driver for them.
Quoted 5 lines
Quoted 3 lines
Quoted 3 lines
Thanks,
Jean
Signature · 1 line
-Kenneth Lee (Hisilicon)
Re: [PATCH v2 03/40] iommu/sva: Manage process address spaces
Quoted 101 lines · Kenneth Lee
Date: Sat, 26 May 2018 10:24:45 +0800
From: Kenneth Lee <[email protected]>
To: Jonathan Cameron <[email protected]>
Cc: Ilias Apalodimas <[email protected]>, Jean-Philippe Brucker
<[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>,
"[email protected]" <[email protected]>, Will Deacon <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "tn-nYOzD4b6Jr9Wk0Htik3J/[email protected]" <tn-nYOzD4b6Jr9Wk0Htik3J/[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "rfranz-YGCgFSpz5w/[email protected]" <rfranz-YGCgFSpz5w/[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, Jacob Pan <jacob.jun.pan-VuQAYsv1563Yd54FQh9/[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org"
<linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org>, "[email protected]" <[email protected]>, "[email protected]"
<[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA@public.gmane.org" <iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA@public.gmane.org>,
Robin Murphy <[email protected]>, "[email protected]"
<[email protected]>, "[email protected]"
<[email protected]>, "baolu.lu-VuQAYsv1563Yd54FQh9/[email protected]"
<baolu.lu-VuQAYsv1563Yd54FQh9/[email protected]>, liguozhu-C8/M+/[email protected]
Subject: Re: [PATCH v2 03/40] iommu/sva: Manage process address spaces
Message-ID: <20180526022445.GA6069@kllp05>Quoted 34 lines · Jonathan Cameron
Date: Fri, 25 May 2018 09:39:59 +0100
From: Jonathan Cameron <[email protected]>
To: Ilias Apalodimas <[email protected]>
CC: Jean-Philippe Brucker <[email protected]>,
"[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, Will Deacon <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "tn-nYOzD4b6Jr9Wk0Htik3J/[email protected]" <tn-nYOzD4b6Jr9Wk0Htik3J/[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "rfranz-YGCgFSpz5w/[email protected]" <rfranz-YGCgFSpz5w/[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, Jacob Pan <jacob.jun.pan-VuQAYsv1563Yd54FQh9/[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org" <linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA@public.gmane.org" <iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA@public.gmane.org>, Robin Murphy <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "baolu.lu-VuQAYsv1563Yd54FQh9/[email protected]" <baolu.lu-VuQAYsv1563Yd54FQh9/[email protected]>, liguozhu-C8/M+/[email protected], [email protected]
Subject: Re: [PATCH v2 03/40] iommu/sva: Manage process address spaces
Message-ID: <[email protected]>Quoted 28 lines
+CC Kenneth Lee
Quoted 23 lines · On Fri, 25 May 2018 09:33:11 +0300 Ilias Apalodimas…
Quoted 12 lines · Jean-Philippe Brucker
Quoted 10 lines · Ilias Apalodimas
Quoted 4 lines
Interesting, I hadn't thought about this use-case before. At first I thought you were talking about mdev devices assigned to VMs, but I think you're referring to mdevs assigned to userspace drivers instead? Out of curiosity, is it only theoretical or does someone actually need this?
There has been some non upstreamed efforts to have mdev and produce userspace drivers. Huawei is using it on what they call "wrapdrive" for crypto devices and we did a proof of concept for ethernet interfaces. At the time we choose not to involve the IOMMU for the reason you mentioned, but having it there would be good.
Quoted 5 lines
I'm guessing there were good reasons to do it that way but I wonder, is it not simpler to just have the kernel driver create a
/dev/foo, with a standard ioctl/mmap/poll interface? Here VFIO adds a layer of indirection, and since the mediating driver has to implement these operations already, what is gained?The best reason i can come up with is "common code". You already have one API doing that for you so we replicate it in a /dev file?
The mdev approach still needs extentions to support what we tried to do (i.e mdev bus might need yo have access oniommu_ops), but as far as i undestand it's a possible case.Hi, Jean, Please allow me to share my understanding here:
https://zhuanlan.zhihu.com/p/35489035
Quoted 10 lines
The reason we do not use the
/dev/fooscheme is that the devices to be shared are programmable accelerators. We cannot fix up the kernel driver for them.Quoted 5 lines
Quoted 3 lines
Quoted 3 lines
Thanks,
JeanSignature · 1 line
-Kenneth Lee (Hisilicon)
I just found this mail was missed in the mailing list. I tried it once again.
Signature · 1 line
-Kenneth Lee (Hisilicon)
Re: [PATCH v2 03/40] iommu/sva: Manage process address spaces
In reply to Kenneth LeeQuoted 101 lines · Kenneth Lee
Date: Sat, 26 May 2018 10:24:45 +0800
From: Kenneth Lee <[email protected]>
To: Jonathan Cameron <[email protected]>
Cc: Ilias Apalodimas <[email protected]>, Jean-Philippe Brucker
<[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, Will Deacon <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, Jacob Pan <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, Robin Murphy <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, [email protected]
Subject: Re: [PATCH v2 03/40] iommu/sva: Manage process address spaces
Message-ID: <20180526022445.GA6069@kllp05>Quoted 34 lines · Jonathan Cameron
Date: Fri, 25 May 2018 09:39:59 +0100
From: Jonathan Cameron <[email protected]>
To: Ilias Apalodimas <[email protected]>
CC: Jean-Philippe Brucker <[email protected]>,
"[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, Will Deacon <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, Jacob Pan <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, Robin Murphy <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, [email protected], [email protected]
Subject: Re: [PATCH v2 03/40] iommu/sva: Manage process address spaces
Message-ID: <[email protected]>Quoted 28 lines
+CC Kenneth Lee
Quoted 23 lines · On Fri, 25 May 2018 09:33:11 +0300 Ilias Apalodimas…
Quoted 12 lines · Jean-Philippe Brucker
Quoted 10 lines · Ilias Apalodimas
Quoted 4 lines
Interesting, I hadn't thought about this use-case before. At first I thought you were talking about mdev devices assigned to VMs, but I think you're referring to mdevs assigned to userspace drivers instead? Out of curiosity, is it only theoretical or does someone actually need this?
There has been some non upstreamed efforts to have mdev and produce userspace drivers. Huawei is using it on what they call "wrapdrive" for crypto devices and we did a proof of concept for ethernet interfaces. At the time we choose not to involve the IOMMU for the reason you mentioned, but having it there would be good.
Quoted 5 lines
I'm guessing there were good reasons to do it that way but I wonder, is it not simpler to just have the kernel driver create a
/dev/foo, with a standard ioctl/mmap/poll interface? Here VFIO adds a layer of indirection, and since the mediating driver has to implement these operations already, what is gained?The best reason i can come up with is "common code". You already have one API doing that for you so we replicate it in a /dev file?
The mdev approach still needs extentions to support what we tried to do (i.e mdev bus might need yo have access oniommu_ops), but as far as i undestand it's a possible case.Hi, Jean, Please allow me to share my understanding here:
https://zhuanlan.zhihu.com/p/35489035
Quoted 10 lines
The reason we do not use the
/dev/fooscheme is that the devices to be shared are programmable accelerators. We cannot fix up the kernel driver for them.Quoted 5 lines
Quoted 3 lines
Quoted 3 lines
Thanks,
JeanSignature · 1 line
-Kenneth Lee (Hisilicon)
I just found this mail was missed in the mailing list. I tried it once again.
Signature · 1 line
-Kenneth Lee (Hisilicon)
PATCH v2 3/40 iommu/sva: Manage process address spaces
In reply to Kenneth LeeQuoted 101 lines · Kenneth Lee
Date: Sat, 26 May 2018 10:24:45 +0800
From: Kenneth Lee <[email protected]>
To: Jonathan Cameron <[email protected]>
Cc: Ilias Apalodimas <[email protected]>, Jean-Philippe Brucker
<[email protected]>, "xieyisheng1 at huawei.com" <[email protected]>, "kvm at vger.kernel.org" <[email protected]>, "linux-pci at vger.kernel.org" <[email protected]>, "xuzaibo at huawei.com" <[email protected]>, Will Deacon <[email protected]>, "okaya at codeaurora.org" <[email protected]>, "linux-mm at kvack.org" <[email protected]>, "yi.l.liu at intel.com" <[email protected]>, "ashok.raj at intel.com" <[email protected]>, "tn at semihalf.com" <[email protected]>, "joro at 8bytes.org" <[email protected]>, "robdclark at gmail.com" <[email protected]>, "bharatku at xilinx.com" <[email protected]>, "linux-acpi at vger.kernel.org" <[email protected]>, "liudongdong3 at huawei.com" <[email protected]>, "rfranz at cavium.com" <[email protected]>, "devicetree at vger.kernel.org" <[email protected]>, "kevin.tian at intel.com" <[email protected]>, Jacob Pan <[email protected]>, "alex.williamson at redhat.com" <[email protected]>, "rgummal at xilinx.com" <[email protected]>, "thunder.leizhen at huawei.com" <[email protected]>, "linux-arm-kernel at lists.infradead.org" <[email protected]>, "shunyong.yang at hxt-semitech.com" <[email protected]>, "dwmw2 at infradead.org" <[email protected]>, "liubo95 at huawei.com" <[email protected]>, "jcrouse at codeaurora.org" <[email protected]>,
"iommu at lists.linux-foundation.org" <[email protected]>, Robin Murphy <[email protected]>, "christian.koenig at amd.com" <[email protected]>, "nwatters at codeaurora.org" <[email protected]>, "baolu.lu at linux.intel.com" <[email protected]>, liguozhu at hisilicon.com
Subject: Re: [PATCH v2 03/40] iommu/sva: Manage process address spaces
Message-ID: <20180526022445.GA6069@kllp05>Quoted 34 lines · Jonathan Cameron
Date: Fri, 25 May 2018 09:39:59 +0100
From: Jonathan Cameron <[email protected]>
To: Ilias Apalodimas <[email protected]>
CC: Jean-Philippe Brucker <[email protected]>,
"xieyisheng1 at huawei.com" <[email protected]>, "kvm at vger.kernel.org" <[email protected]>, "linux-pci at vger.kernel.org" <[email protected]>, "xuzaibo at huawei.com" <[email protected]>, Will Deacon <[email protected]>, "okaya at codeaurora.org" <[email protected]>, "linux-mm at kvack.org" <[email protected]>, "yi.l.liu at intel.com" <[email protected]>, "ashok.raj at intel.com" <[email protected]>, "tn at semihalf.com" <[email protected]>, "joro at 8bytes.org" <[email protected]>, "robdclark at gmail.com" <[email protected]>, "bharatku at xilinx.com" <[email protected]>, "linux-acpi at vger.kernel.org" <[email protected]>, "liudongdong3 at huawei.com" <[email protected]>, "rfranz at cavium.com" <[email protected]>, "devicetree at vger.kernel.org" <[email protected]>, "kevin.tian at intel.com" <[email protected]>, Jacob Pan <[email protected]>, "alex.williamson at redhat.com" <[email protected]>, "rgummal at xilinx.com" <[email protected]>, "thunder.leizhen at huawei.com" <[email protected]>, "linux-arm-kernel at lists.infradead.org" <[email protected]>, "shunyong.yang at hxt-semitech.com" <[email protected]>, "dwmw2 at infradead.org" <[email protected]>, "liubo95 at huawei.com" <[email protected]>, "jcrouse at codeaurora.org" <[email protected]>,
"iommu at lists.linux-foundation.org" <[email protected]>, Robin Murphy <[email protected]>, "christian.koenig at amd.com" <[email protected]>, "nwatters at codeaurora.org" <[email protected]>, "baolu.lu at linux.intel.com" <[email protected]>, liguozhu at hisilicon.com, kenneth-lee-2012 at foxmail.com
Subject: Re: [PATCH v2 03/40] iommu/sva: Manage process address spaces
Message-ID: <[email protected]>Quoted 28 lines
+CC Kenneth Lee
Quoted 23 lines · On Fri, 25 May 2018 09:33:11 +0300 Ilias Apalodimas…
Quoted 12 lines · Jean-Philippe Brucker
Quoted 10 lines · Ilias Apalodimas
Quoted 4 lines
Interesting, I hadn't thought about this use-case before. At first I thought you were talking about mdev devices assigned to VMs, but I think you're referring to mdevs assigned to userspace drivers instead? Out of curiosity, is it only theoretical or does someone actually need this?
There has been some non upstreamed efforts to have mdev and produce userspace drivers. Huawei is using it on what they call "wrapdrive" for crypto devices and we did a proof of concept for ethernet interfaces. At the time we choose not to involve the IOMMU for the reason you mentioned, but having it there would be good.
Quoted 5 lines
I'm guessing there were good reasons to do it that way but I wonder, is it not simpler to just have the kernel driver create a
/dev/foo, with a standard ioctl/mmap/poll interface? Here VFIO adds a layer of indirection, and since the mediating driver has to implement these operations already, what is gained?The best reason i can come up with is "common code". You already have one API doing that for you so we replicate it in a /dev file?
The mdev approach still needs extentions to support what we tried to do (i.e mdev bus might need yo have access oniommu_ops), but as far as i undestand it's a possible case.Hi, Jean, Please allow me to share my understanding here:
https://zhuanlan.zhihu.com/p/35489035
Quoted 10 lines
The reason we do not use the
/dev/fooscheme is that the devices to be shared are programmable accelerators. We cannot fix up the kernel driver for them.Quoted 5 lines
Quoted 3 lines
Quoted 3 lines
Thanks,
JeanSignature · 1 line
-Kenneth Lee (Hisilicon)
I just found this mail was missed in the mailing list. I tried it once again.
Signature · 1 line
-Kenneth Lee (Hisilicon)
Re: [PATCH v2 03/40] iommu/sva: Manage process address spaces
In reply to Kenneth LeeQuoted 101 lines · Kenneth Lee
Date: Sat, 26 May 2018 10:24:45 +0800
From: Kenneth Lee <[email protected]>
To: Jonathan Cameron <[email protected]>
Cc: Ilias Apalodimas <[email protected]>, Jean-Philippe Brucker
<[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, Will Deacon <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, Jacob Pan <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, Robin Murphy <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, [email protected]
Subject: Re: [PATCH v2 03/40] iommu/sva: Manage process address spaces
Message-ID: <20180526022445.GA6069@kllp05>Quoted 34 lines · Jonathan Cameron
Date: Fri, 25 May 2018 09:39:59 +0100
From: Jonathan Cameron <[email protected]>
To: Ilias Apalodimas <[email protected]>
CC: Jean-Philippe Brucker <[email protected]>,
"[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, Will Deacon <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, Jacob Pan <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, Robin Murphy <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, [email protected], [email protected]
Subject: Re: [PATCH v2 03/40] iommu/sva: Manage process address spaces
Message-ID: <[email protected]>Quoted 28 lines
+CC Kenneth Lee
Quoted 23 lines · On Fri, 25 May 2018 09:33:11 +0300 Ilias Apalodimas…
Quoted 12 lines · Jean-Philippe Brucker
Quoted 10 lines · Ilias Apalodimas
Quoted 4 lines
Interesting, I hadn't thought about this use-case before. At first I thought you were talking about mdev devices assigned to VMs, but I think you're referring to mdevs assigned to userspace drivers instead? Out of curiosity, is it only theoretical or does someone actually need this?
There has been some non upstreamed efforts to have mdev and produce userspace drivers. Huawei is using it on what they call "wrapdrive" for crypto devices and we did a proof of concept for ethernet interfaces. At the time we choose not to involve the IOMMU for the reason you mentioned, but having it there would be good.
Quoted 5 lines
I'm guessing there were good reasons to do it that way but I wonder, is it not simpler to just have the kernel driver create a
/dev/foo, with a standard ioctl/mmap/poll interface? Here VFIO adds a layer of indirection, and since the mediating driver has to implement these operations already, what is gained?The best reason i can come up with is "common code". You already have one API doing that for you so we replicate it in a /dev file?
The mdev approach still needs extentions to support what we tried to do (i.e mdev bus might need yo have access oniommu_ops), but as far as i undestand it's a possible case.Hi, Jean, Please allow me to share my understanding here:
https://zhuanlan.zhihu.com/p/35489035
Quoted 10 lines
The reason we do not use the
/dev/fooscheme is that the devices to be shared are programmable accelerators. We cannot fix up the kernel driver for them.Quoted 5 lines
Quoted 3 lines
Quoted 3 lines
Thanks,
JeanSignature · 1 line
-Kenneth Lee (Hisilicon)
I just found this mail was missed in the mailing list. I tried it once again.
Signature · 8 lines
-Kenneth Lee (Hisilicon) _______________________________________________ linux-arm-kernel mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
Re: [PATCH v2 03/40] iommu/sva: Manage process address spaces
In reply to Kenneth LeeQuoted 101 lines · Kenneth Lee
Date: Sat, 26 May 2018 10:24:45 +0800
From: Kenneth Lee <[email protected]>
To: Jonathan Cameron <[email protected]>
Cc: Ilias Apalodimas <[email protected]>, Jean-Philippe Brucker
<[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, Will Deacon <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, Jacob Pan <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, Robin Murphy <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, [email protected]
Subject: Re: [PATCH v2 03/40] iommu/sva: Manage process address spaces
Message-ID: <20180526022445.GA6069@kllp05>Quoted 34 lines · Jonathan Cameron
Date: Fri, 25 May 2018 09:39:59 +0100
From: Jonathan Cameron <[email protected]>
To: Ilias Apalodimas <[email protected]>
CC: Jean-Philippe Brucker <[email protected]>,
"[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, Will Deacon <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, Jacob Pan <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, Robin Murphy <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, [email protected], [email protected]
Subject: Re: [PATCH v2 03/40] iommu/sva: Manage process address spaces
Message-ID: <[email protected]>Quoted 28 lines
+CC Kenneth Lee
Quoted 23 lines · On Fri, 25 May 2018 09:33:11 +0300 Ilias Apalodimas…
Quoted 12 lines · Jean-Philippe Brucker
Quoted 10 lines · Ilias Apalodimas
Quoted 4 lines
Interesting, I hadn't thought about this use-case before. At first I thought you were talking about mdev devices assigned to VMs, but I think you're referring to mdevs assigned to userspace drivers instead? Out of curiosity, is it only theoretical or does someone actually need this?
There has been some non upstreamed efforts to have mdev and produce userspace drivers. Huawei is using it on what they call "wrapdrive" for crypto devices and we did a proof of concept for ethernet interfaces. At the time we choose not to involve the IOMMU for the reason you mentioned, but having it there would be good.
Quoted 5 lines
I'm guessing there were good reasons to do it that way but I wonder, is it not simpler to just have the kernel driver create a
/dev/foo, with a standard ioctl/mmap/poll interface? Here VFIO adds a layer of indirection, and since the mediating driver has to implement these operations already, what is gained?The best reason i can come up with is "common code". You already have one API doing that for you so we replicate it in a /dev file?
The mdev approach still needs extentions to support what we tried to do (i.e mdev bus might need yo have access oniommu_ops), but as far as i undestand it's a possible case.Hi, Jean, Please allow me to share my understanding here:
https://zhuanlan.zhihu.com/p/35489035
Quoted 10 lines
The reason we do not use the
/dev/fooscheme is that the devices to be shared are programmable accelerators. We cannot fix up the kernel driver for them.Quoted 5 lines
Quoted 3 lines
Quoted 3 lines
Thanks,
JeanSignature · 1 line
-Kenneth Lee (Hisilicon)
I just found this mail was missed in the mailing list. I tried it once again.
Signature · 8 lines
-Kenneth Lee (Hisilicon) _______________________________________________ linux-arm-kernel mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
Re: [PATCH v2 03/40] iommu/sva: Manage process address spaces
In reply to Kenneth LeeQuoted 101 lines · Kenneth Lee
Date: Sat, 26 May 2018 10:24:45 +0800
From: Kenneth Lee <[email protected]>
To: Jonathan Cameron <[email protected]>
Cc: Ilias Apalodimas <[email protected]>, Jean-Philippe Brucker
<[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, Will Deacon <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, Jacob Pan <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, Robin Murphy <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, [email protected]
Subject: Re: [PATCH v2 03/40] iommu/sva: Manage process address spaces
Message-ID: <20180526022445.GA6069@kllp05>Quoted 34 lines · Jonathan Cameron
Date: Fri, 25 May 2018 09:39:59 +0100
From: Jonathan Cameron <[email protected]>
To: Ilias Apalodimas <[email protected]>
CC: Jean-Philippe Brucker <[email protected]>,
"[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, Will Deacon <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, Jacob Pan <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, Robin Murphy <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, [email protected], [email protected]
Subject: Re: [PATCH v2 03/40] iommu/sva: Manage process address spaces
Message-ID: <[email protected]>Quoted 28 lines
+CC Kenneth Lee
Quoted 23 lines · On Fri, 25 May 2018 09:33:11 +0300 Ilias Apalodimas…
Quoted 12 lines · Jean-Philippe Brucker
Quoted 10 lines · Ilias Apalodimas
Quoted 4 lines
Interesting, I hadn't thought about this use-case before. At first I thought you were talking about mdev devices assigned to VMs, but I think you're referring to mdevs assigned to userspace drivers instead? Out of curiosity, is it only theoretical or does someone actually need this?
There has been some non upstreamed efforts to have mdev and produce userspace drivers. Huawei is using it on what they call "wrapdrive" for crypto devices and we did a proof of concept for ethernet interfaces. At the time we choose not to involve the IOMMU for the reason you mentioned, but having it there would be good.
Quoted 5 lines
I'm guessing there were good reasons to do it that way but I wonder, is it not simpler to just have the kernel driver create a
/dev/foo, with a standard ioctl/mmap/poll interface? Here VFIO adds a layer of indirection, and since the mediating driver has to implement these operations already, what is gained?The best reason i can come up with is "common code". You already have one API doing that for you so we replicate it in a /dev file?
The mdev approach still needs extentions to support what we tried to do (i.e mdev bus might need yo have access oniommu_ops), but as far as i undestand it's a possible case.Hi, Jean, Please allow me to share my understanding here:
https://zhuanlan.zhihu.com/p/35489035
Quoted 10 lines
The reason we do not use the
/dev/fooscheme is that the devices to be shared are programmable accelerators. We cannot fix up the kernel driver for them.Quoted 5 lines
Quoted 3 lines
Quoted 3 lines
Thanks,
JeanSignature · 1 line
-Kenneth Lee (Hisilicon)
I just found this mail was missed in the mailing list. I tried it once again.
Signature · 1 line
-Kenneth Lee (Hisilicon)
Re: [PATCH v2 03/40] iommu/sva: Manage process address spaces
In reply to Jonathan CameronQuoted 34 lines · Jonathan Cameron
Date: Fri, 25 May 2018 09:39:59 +0100
From: Jonathan Cameron <[email protected]>
To: Ilias Apalodimas <[email protected]>
CC: Jean-Philippe Brucker <[email protected]>,
"[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, Will Deacon <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, Jacob Pan <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, Robin Murphy <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, [email protected], [email protected]
Subject: Re: [PATCH v2 03/40] iommu/sva: Manage process address spaces
Message-ID: <[email protected]>
Quoted 28 lines
+CC Kenneth Lee
Quoted 23 lines · On Fri, 25 May 2018 09:33:11 +0300 Ilias Apalodimas…
Quoted 12 lines · Jean-Philippe Brucker
Quoted 10 lines · Ilias Apalodimas
Quoted 4 lines
Interesting, I hadn't thought about this use-case before. At first I thought you were talking about mdev devices assigned to VMs, but I think you're referring to mdevs assigned to userspace drivers instead? Out of curiosity, is it only theoretical or does someone actually need this?
There has been some non upstreamed efforts to have mdev and produce userspace drivers. Huawei is using it on what they call "wrapdrive" for crypto devices and we did a proof of concept for ethernet interfaces. At the time we choose not to involve the IOMMU for the reason you mentioned, but having it there would be good.
Quoted 5 lines
I'm guessing there were good reasons to do it that way but I wonder, is it not simpler to just have the kernel driver create a
/dev/foo, with a standard ioctl/mmap/poll interface? Here VFIO adds a layer of indirection, and since the mediating driver has to implement these operations already, what is gained?The best reason i can come up with is "common code". You already have one API doing that for you so we replicate it in a /dev file?
The mdev approach still needs extentions to support what we tried to do (i.e mdev bus might need yo have access oniommu_ops), but as far as i undestand it's a possible case.
Hi, Jean, Please allow me to share my understanding here:
https://zhuanlan.zhihu.com/p/35489035
The reason we do not use the /dev/foo scheme is that the devices to be shared are programmable accelerators. We cannot fix up the kernel driver for them.
Quoted 5 lines
Quoted 3 lines
Quoted 3 lines
Thanks,
Jean
Signature · 8 lines
-Kenneth Lee (Hisilicon) _______________________________________________ linux-arm-kernel mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
Re: [PATCH v2 03/40] iommu/sva: Manage process address spaces
In reply to Jonathan CameronQuoted 34 lines · Jonathan Cameron
Date: Fri, 25 May 2018 09:39:59 +0100
From: Jonathan Cameron <[email protected]>
To: Ilias Apalodimas <[email protected]>
CC: Jean-Philippe Brucker <[email protected]>,
"[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, Will Deacon <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, Jacob Pan <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, Robin Murphy <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, [email protected], [email protected]
Subject: Re: [PATCH v2 03/40] iommu/sva: Manage process address spaces
Message-ID: <[email protected]>
Quoted 28 lines
+CC Kenneth Lee
Quoted 23 lines · On Fri, 25 May 2018 09:33:11 +0300 Ilias Apalodimas…
Quoted 12 lines · Jean-Philippe Brucker
Quoted 10 lines · Ilias Apalodimas
Quoted 4 lines
Interesting, I hadn't thought about this use-case before. At first I thought you were talking about mdev devices assigned to VMs, but I think you're referring to mdevs assigned to userspace drivers instead? Out of curiosity, is it only theoretical or does someone actually need this?
There has been some non upstreamed efforts to have mdev and produce userspace drivers. Huawei is using it on what they call "wrapdrive" for crypto devices and we did a proof of concept for ethernet interfaces. At the time we choose not to involve the IOMMU for the reason you mentioned, but having it there would be good.
Quoted 5 lines
I'm guessing there were good reasons to do it that way but I wonder, is it not simpler to just have the kernel driver create a
/dev/foo, with a standard ioctl/mmap/poll interface? Here VFIO adds a layer of indirection, and since the mediating driver has to implement these operations already, what is gained?The best reason i can come up with is "common code". You already have one API doing that for you so we replicate it in a /dev file?
The mdev approach still needs extentions to support what we tried to do (i.e mdev bus might need yo have access oniommu_ops), but as far as i undestand it's a possible case.
Hi, Jean, Please allow me to share my understanding here:
https://zhuanlan.zhihu.com/p/35489035
The reason we do not use the /dev/foo scheme is that the devices to be shared are programmable accelerators. We cannot fix up the kernel driver for them.
Quoted 5 lines
Quoted 3 lines
Quoted 3 lines
Thanks,
Jean
Signature · 8 lines
-Kenneth Lee (Hisilicon) _______________________________________________ linux-arm-kernel mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
Re: [PATCH v2 03/40] iommu/sva: Manage process address spaces
In reply to Jonathan CameronQuoted 34 lines · Jonathan Cameron
Date: Fri, 25 May 2018 09:39:59 +0100
From: Jonathan Cameron <[email protected]>
To: Ilias Apalodimas <[email protected]>
CC: Jean-Philippe Brucker <[email protected]>,
"[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, Will Deacon <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, Jacob Pan <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, Robin Murphy <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, [email protected], [email protected]
Subject: Re: [PATCH v2 03/40] iommu/sva: Manage process address spaces
Message-ID: <[email protected]>
Quoted 28 lines
+CC Kenneth Lee
Quoted 23 lines · On Fri, 25 May 2018 09:33:11 +0300 Ilias Apalodimas…
Quoted 12 lines · Jean-Philippe Brucker
Quoted 10 lines · Ilias Apalodimas
Quoted 4 lines
Interesting, I hadn't thought about this use-case before. At first I thought you were talking about mdev devices assigned to VMs, but I think you're referring to mdevs assigned to userspace drivers instead? Out of curiosity, is it only theoretical or does someone actually need this?
There has been some non upstreamed efforts to have mdev and produce userspace drivers. Huawei is using it on what they call "wrapdrive" for crypto devices and we did a proof of concept for ethernet interfaces. At the time we choose not to involve the IOMMU for the reason you mentioned, but having it there would be good.
Quoted 5 lines
I'm guessing there were good reasons to do it that way but I wonder, is it not simpler to just have the kernel driver create a
/dev/foo, with a standard ioctl/mmap/poll interface? Here VFIO adds a layer of indirection, and since the mediating driver has to implement these operations already, what is gained?The best reason i can come up with is "common code". You already have one API doing that for you so we replicate it in a /dev file?
The mdev approach still needs extentions to support what we tried to do (i.e mdev bus might need yo have access oniommu_ops), but as far as i undestand it's a possible case.
Hi, Jean, Please allow me to share my understanding here:
https://zhuanlan.zhihu.com/p/35489035
The reason we do not use the /dev/foo scheme is that the devices to be shared are programmable accelerators. We cannot fix up the kernel driver for them.
Quoted 5 lines
Quoted 3 lines
Quoted 3 lines
Thanks,
Jean
Signature · 1 line
-Kenneth Lee (Hisilicon)
Re: [PATCH v2 03/40] iommu/sva: Manage process address spaces
In reply to Jonathan CameronQuoted 36 lines · Jonathan Cameron
Date: Fri, 25 May 2018 09:39:59 +0100
From: Jonathan Cameron <[email protected]>
To: Ilias Apalodimas <[email protected]>
CC: Jean-Philippe Brucker <[email protected]>,
"[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, Will Deacon <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, Jacob Pan <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, Robin Murphy <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, "[email protected]" <[email protected]>, [email protected], [email protected]
Subject: Re: [PATCH v2 03/40] iommu/sva: Manage process address spaces
Message-ID: <[email protected]>
Organization: Huawei
X-Mailer: Claws Mail 3.15.0 (GTK+ 2.24.31; x86_64-w64-mingw32)
Quoted 28 lines
+CC Kenneth Lee
Quoted 23 lines · On Fri, 25 May 2018 09:33:11 +0300 Ilias Apalodimas…
Quoted 12 lines · Jean-Philippe Brucker
Quoted 10 lines · Ilias Apalodimas
Quoted 4 lines
Interesting, I hadn't thought about this use-case before. At first I thought you were talking about mdev devices assigned to VMs, but I think you're referring to mdevs assigned to userspace drivers instead? Out of curiosity, is it only theoretical or does someone actually need this?
There has been some non upstreamed efforts to have mdev and produce userspace drivers. Huawei is using it on what they call "wrapdrive" for crypto devices and we did a proof of concept for ethernet interfaces. At the time we choose not to involve the IOMMU for the reason you mentioned, but having it there would be good.
Quoted 5 lines
I'm guessing there were good reasons to do it that way but I wonder, is it not simpler to just have the kernel driver create a
/dev/foo, with a standard ioctl/mmap/poll interface? Here VFIO adds a layer of indirection, and since the mediating driver has to implement these operations already, what is gained?The best reason i can come up with is "common code". You already have one API doing that for you so we replicate it in a /dev file?
The mdev approach still needs extentions to support what we tried to do (i.e mdev bus might need yo have access oniommu_ops), but as far as i undestand it's a possible case.
Hi, Jean, Please allow me to share my understanding here:
https://zhuanlan.zhihu.com/p/35489035
The reason we do not use the /dev/foo scheme is that the devices to be shared are programmable accelerators. We cannot fix up the kernel driver for them.
Quoted 5 lines
Quoted 3 lines
Quoted 3 lines
Thanks,
Jean
(p.s. I sent this mail on May 26 from my public email count. But it seems the email server is blocked. I resent it from my company count until my colleague told me just now. Sorry for inconvenience)
Signature · 1 line
-Kenneth(Hisilicon)
Re: [PATCH v2 03/40] iommu/sva: Manage process address spaces
In reply to Jean-Philippe BruckerQuoted 39 lines · On Fri, 11 May 2018 20:06:04 +0100 Jean-Philippe Brucker…
Allocate IOMMU mm structures and binding them to devices. Four operations are added to IOMMU drivers:
*
mm_alloc(): to create anio_mmstructure and perform architecture- specific operations required to grab the process (for instance on ARM, pin down the CPU ASID so that the process doesn't get assigned a new ASID on rollover).There is a single valid
io_mmstructure per Linux mm. Future extensions may also useio_mmfor kernel-managed address spaces, populated with map()/unmap() calls instead of bound to process address spaces. This patch focuses on "shared"io_mm.*
mm_attach(): attach an mm to a device. The IOMMU driver checks that the device is capable of sharing an address space, and writes the PASID table entry to install the pgd.Some IOMMU drivers will have a single PASID table per domain, for convenience. Other can implement it differently but to help these drivers,
mm_attachandmm_detachtake 'attach_domain' and 'detach_domain' parameters, that tell whether they need to set and clear the PASID entry or only send the required TLB invalidations.*
mm_detach(): detach an mm from a device. The IOMMU driver removes the PASID table entry and invalidates the IOTLBs.*
mm_free(): free a structure allocated bymm_alloc(), and let arch release the process.
mm_attachandmm_detachoperations are serialized with a spinlock. When trying to optimize this code, we should at least prevent concurrent attach()/detach() on the same domain (so multi-level PASID table code can allocate tables lazily).mm_alloc()can sleep, butmm_freemust not (because we'll have to call it fromcall_srculater on).At the moment we use an IDR for allocating PASIDs and retrieving contexts. We also use a single spinlock. These can be refined and optimized later (a custom allocator will be needed for top-down PASID allocation).
Quoted 5 lines
Keeping track of address spaces requires the use of MMU notifiers. Handling process exit with regard to
unbind()is tricky, so it is left for another patch and we explicitly failmm_alloc()for the moment.Signed-off-by Jean-Philippe Brucker <[email protected]>
A few minor bits and bobs inline. Looks good in general + nice diags!
Thanks,
Jonathan
Quoted 160 lines
---
v1->v2: sanity-check of flags--- drivers/iommu/iommu-sva.c | 380 +++++++++++++++++++++++++++++++++++++- drivers/iommu/iommu.c | 1 + include/linux/iommu.h | 28 +++ 3 files changed, 406 insertions(+), 3 deletions(-)drivers/iommu/iommu-sva.c +141
-0@@ -5,8 +5,298 @@* Copyright (C) 2018 ARM Ltd. */ +#include <linux/idr.h> #include <linux/iommu.h> +#include <linux/sched/mm.h> #include <linux/slab.h> +#include <linux/spinlock.h> + +/** +* DOC: io_mm model +* +* The io_mm keeps track of process address spaces shared between CPU and IOMMU. +* The following example illustrates the relation between structures +* iommu_domain, io_mm and iommu_bond. An iommu_bond is a link between io_mm and +* device. A device can have multiple io_mm and an io_mm may be bound to +* multiple devices. +* ___________________________ +* | IOMMU domain A | +* | ________________ | +* | | IOMMU group | +------- io_pgtables +* | | | | +* | | dev 00:00.0 ----+------- bond --- io_mm X +* | |________________| \ | +* | '----- bond ---. +* |___________________________| \ +* ___________________________ \ +* | IOMMU domain B | io_mm Y +* | ________________ | / / +* | | IOMMU group | | / / +* | | | | / / +* | | dev 00:01.0 ------------ bond -' / +* | | dev 00:01.1 ------------ bond --' +* | |________________| | +* | +------- io_pgtables +* |___________________________| +* +* In this example, device 00:00.0 is in domain A, devices 00:01.* are in domain +* B. All devices within the same domain access the same address spaces. Device +* 00:00.0 accesses address spaces X and Y, each corresponding to an mm_struct. +* Devices 00:01.* only access address space Y. In addition each +* IOMMU_DOMAIN_DMA domain has a private address space, io_pgtable, that is +* managed with iommu_map()/iommu_unmap(), and isn't shared with the CPU MMU. +* +* To obtain the above configuration, users would for instance issue the +* following calls: +* +* iommu_sva_bind_device(dev 00:00.0, mm X, ...) -> PASID 1 +* iommu_sva_bind_device(dev 00:00.0, mm Y, ...) -> PASID 2 +* iommu_sva_bind_device(dev 00:01.0, mm Y, ...) -> PASID 2 +* iommu_sva_bind_device(dev 00:01.1, mm Y, ...) -> PASID 2 +* +* A single Process Address Space ID (PASID) is allocated for each mm. In the +* example, devices use PASID 1 to read/write into address space X and PASID 2 +* to read/write into address space Y. +* +* Hardware tables describing this configuration in the IOMMU would typically +* look like this: +* +* PASID tables +* of domain A +* .->+--------+ +* / 0 | |-------> io_pgtable +* / +--------+ +* Device tables / 1 | |-------> pgd X +* +--------+ / +--------+ +* 00:00.0 | A |-' 2 | |--. +* +--------+ +--------+ \ +* : : 3 | | \ +* +--------+ +--------+ --> pgd Y +* 00:01.0 | B |--. / +* +--------+ \ | +* 00:01.1 | B |----+ PASID tables | +* +--------+ \ of domain B | +* '->+--------+ | +* 0 | |-- | --> io_pgtable +* +--------+ | +* 1 | | | +* +--------+ | +* 2 | |---' +* +--------+ +* 3 | | +* +--------+ +* +* With this model, a single call binds all devices in a given domain to an +* address space. Other devices in the domain will get the same bond implicitly. +* However, users must issue one bind() for each device, because IOMMUs may +* implement SVA differently. Furthermore, mandating one bind() per device +* allows the driver to perform sanity-checks on device capabilities. +* +* On Arm and AMD IOMMUs, entry 0 of the PASID table can be used to hold +* non-PASID translations. In this case PASID 0 is reserved and entry 0 points +* to the io_pgtable base. On Intel IOMMU, the io_pgtable base would be held in +* the device table and PASID 0 would be available to the allocator. +*/ + +struct iommu_bond { +struct io_mm *io_mm; +struct device *dev; +struct iommu_domain *domain; + +struct list_head mm_head; +struct list_head dev_head; +struct list_head domain_head; + +void *drvdata; +}; + +/* +* Because we're using an IDR, PASIDs are limited to 31 bits (the sign bit is +* used for returning errors). In practice implementations will use at most 20 +* bits, which is the PCI limit. +*/ +static DEFINE_IDR (iommu_pasid_idr); + +/* +* For the moment this is an all-purpose lock. It serializes +* access/modifications to bonds, access/modifications to the PASID IDR, and +* changes to io_mm refcount as well. +*/ +static DEFINE_SPINLOCK (iommu_sva_lock); + +static struct io_mm * +io_mm_alloc (struct iommu_domain *domain,struct device *dev, +struct mm_struct *mm,unsigned long flags) +{ +int ret; +int pasid; +struct io_mm *io_mm; +struct iommu_sva_param *param = dev->iommu_param->sva_param; + +if (!domain->ops->mm_alloc || !domain->ops->mm_free) +return ERR_PTR (-ENODEV ); + + io_mm = domain->ops->mm_alloc (domain, mm, flags); +if (IS_ERR (io_mm)) +return io_mm; +if (!io_mm) +return ERR_PTR (-ENOMEM ); + +/* +* The mm must not be freed until after the driver frees the io_mm +* (which may involve unpinning the CPU ASID for instance, requiring a +* valid mm struct.) +*/ +mmgrab (mm);
drivers/iommu/iommu-sva.c line 160
Quoted 10 lines
drivers/iommu/iommu-sva.c +10
-0+ + io_mm->flags = flags; + io_mm->mm = mm; + io_mm->release = domain->ops->mm_free; +INIT_LIST_HEAD (&io_mm->devices); + +idr_preload (GFP_KERNEL ); +spin_lock (&iommu_sva_lock); + pasid =idr_alloc (&iommu_pasid_idr, io_mm, param->min_pasid, + param->max_pasid +1 ,GFP_ATOMIC );
I'd expect the IDR cleanup to be in io_mm_free as that would 'match' against io_mm_alloc but it's in io_mm_release just before the io_mm_free call, perhaps move it or am I missing something?
Hmm. This is reworked in patch 5 to use call rcu to do the free. I suppose we may be burning an idr entry if we take a while to get round to the free.. If so a comment to explain this would be great.
Quoted 11 lines
drivers/iommu/iommu-sva.c +10
-0+ io_mm->pasid = pasid; +spin_unlock (&iommu_sva_lock); +idr_preload_end (); + +if (pasid <0 ) { + ret = pasid; + goto err_free_mm; + } + +/* TODO: keep track of mm. For the moment, abort. */
Quoted 1 line
From later patches, I can now see why we didn't init the kref
here, but perhaps a comment would make that clear rather than people checking it is correctly used throughout? Actually just grab the comment from patch 5 and put it in this one and that will do the job nicely.
drivers/iommu/iommu-sva.c line 177
Quoted 7 lines
drivers/iommu/iommu-sva.c +7
-0+ ret = -ENOSYS ; +spin_lock (&iommu_sva_lock); +idr_remove (&iommu_pasid_idr, io_mm->pasid); +spin_unlock (&iommu_sva_lock); + +err_free_mm: + domain->ops->mm_free (io_mm);
Really minor, but you now have io_mm->release set so to keep this obviously the same as the io_mm_free path, perhaps call that rather than mm_free directly.
Quoted 105 lines
drivers/iommu/iommu-sva.c +105
-0+mmdrop (mm); + +return ERR_PTR (ret); +} + +static void io_mm_free (struct io_mm *io_mm) +{ +struct mm_struct *mm = io_mm->mm; + + io_mm->release (io_mm); +mmdrop (mm); +} + +static void io_mm_release (struct kref *kref) +{ +struct io_mm *io_mm; + + io_mm = container_of(kref, struct io_mm, kref); +WARN_ON (!list_empty (&io_mm->devices)); + +idr_remove (&iommu_pasid_idr, io_mm->pasid); + +io_mm_free (io_mm); +} + +/* +* Returns non-zero if a reference to the io_mm was successfully taken. +* Returns zero if the io_mm is being freed and should not be used. +*/ +static int io_mm_get_locked (struct io_mm *io_mm) +{ +if (io_mm) +return kref_get_unless_zero (&io_mm->kref); + +return 0 ; +} + +static void io_mm_put_locked (struct io_mm *io_mm) +{ +kref_put (&io_mm->kref, io_mm_release); +} + +static void io_mm_put (struct io_mm *io_mm) +{ +spin_lock (&iommu_sva_lock); +io_mm_put_locked (io_mm); +spin_unlock (&iommu_sva_lock); +} + +static int io_mm_attach (struct iommu_domain *domain,struct device *dev, +struct io_mm *io_mm,void *drvdata) +{ +int ret; +bool attach_domain = true; +int pasid = io_mm->pasid; +struct iommu_bond *bond, *tmp; +struct iommu_sva_param *param = dev->iommu_param->sva_param; + +if (!domain->ops->mm_attach || !domain->ops->mm_detach) +return -ENODEV ; + +if (pasid > param->max_pasid || pasid < param->min_pasid) +return -ERANGE ; + + bond =kzalloc (sizeof (*bond),GFP_KERNEL ); +if (!bond) +return -ENOMEM ; + + bond->domain = domain; + bond->io_mm = io_mm; + bond->dev = dev; + bond->drvdata = drvdata; + +spin_lock (&iommu_sva_lock); +/* +* Check if this io_mm is already bound to the domain. In which case the +* IOMMU driver doesn't have to install the PASID table entry. +*/ +list_for_each_entry (tmp, &domain->mm_list, domain_head) { +if (tmp->io_mm == io_mm) { + attach_domain = false; +break ; + } + } + + ret = domain->ops->mm_attach (domain, dev, io_mm, attach_domain); +if (ret) { +kfree (bond); +spin_unlock (&iommu_sva_lock); +return ret; + } + +list_add (&bond->mm_head, &io_mm->devices); +list_add (&bond->domain_head, &domain->mm_list); +list_add (&bond->dev_head, ¶m->mm_list); +spin_unlock (&iommu_sva_lock); + +return 0 ; +} + +static void io_mm_detach_locked (struct iommu_bond *bond) +{ +struct iommu_bond *tmp; +bool detach_domain = true; +struct iommu_domain *domain = bond->domain;
drivers/iommu/iommu-sva.c line 292
Quoted 10 lines
drivers/iommu/iommu-sva.c +10
-0+ +list_for_each_entry (tmp, &domain->mm_list, domain_head) { +if (tmp->io_mm == bond->io_mm && tmp->dev != bond->dev) { + detach_domain = false; +break ; + } + } + + domain->ops->mm_detach (domain, bond->dev, bond->io_mm, detach_domain); +
I can't see an immediate reason to have a different order in her to the reverse of
the attach above. So I think you should be detaching after the list_del calls.
If there is a reason, can we have a comment so I don't ask on v10.
Quoted 38 lines
drivers/iommu/iommu-sva.c +23
-1+list_del (&bond->mm_head); +list_del (&bond->domain_head); +list_del (&bond->dev_head); +io_mm_put_locked (bond->io_mm); + +kfree (bond); +}/** * iommu_sva_device_init() - Initialize Shared Virtual Addressing for a device @@ -47,6 +337,7 @@ int iommu_sva_device_init(struct device *dev, unsigned long features, param->features = features; param->max_pasid = max_pasid; +INIT_LIST_HEAD (¶m->mm_list);/* * IOMMU driver updates the limits depending on the IOMMU and device @@ -114,13 +405,87 @@ EXPORT_SYMBOL_GPL(iommu_sva_device_shutdown);int __iommu_sva_bind_device (struct device *dev,struct mm_struct *mm,int *pasid,unsigned long flags,void *drvdata) { -return -ENOSYS ;/* TODO */ +int i, ret =0 ; +struct io_mm *io_mm =NULL ; +struct iommu_domain *domain; +struct iommu_bond *bond =NULL , *tmp; +struct iommu_sva_param *param = dev->iommu_param->sva_param; + + domain =iommu_get_domain_for_dev (dev); +if (!domain) +return -EINVAL ; + +/* +* The device driver does not call sva_device_init/shutdown and +* bind/unbind concurrently, so no need to take the param lock. +*/ +if (WARN_ON_ONCE (!param) || (flags & ~param->features))
drivers/iommu/iommu-sva.c EXPORT_SYMBOL_GPL(iommu_sva_device_shutdown); · line 432
Quoted 10 lines
drivers/iommu/iommu-sva.c +10
-0+return -EINVAL ; + +/* If an io_mm already exists, use it */ +spin_lock (&iommu_sva_lock); +idr_for_each_entry (&iommu_pasid_idr, io_mm, i) { +if (io_mm->mm == mm &&io_mm_get_locked (io_mm)) { +/* ... Unless it's already bound to this device */ +list_for_each_entry (tmp, &io_mm->devices, mm_head) { +if (tmp->dev == dev) { + bond = tmp;
Using bond for this is clear in a sense, but can we not just use ret so it is obvious here that we are going to return -EEXIST? At first glance I thought you were going to carry on with this bond and couldn't work out why it would ever make sense to have two bonds between a device an an io_mm (which it doesn't!)
Quoted 149 lines
drivers/iommu/iommu-sva.c +61
-2+io_mm_put_locked (io_mm); +break ; + } + } +break ; + } + } +spin_unlock (&iommu_sva_lock); + +if (bond) +return -EEXIST ; + +/* Require identical features within an io_mm for now */ +if (io_mm && (flags != io_mm->flags)) { +io_mm_put (io_mm); +return -EDOM ; + } + +if (!io_mm) { + io_mm =io_mm_alloc (domain, dev, mm, flags); +if (IS_ERR (io_mm)) +return PTR_ERR (io_mm); + } + + ret =io_mm_attach (domain, dev, io_mm, drvdata); +if (ret) +io_mm_put (io_mm); +else + *pasid = io_mm->pasid; + +return ret; }EXPORT_SYMBOL_GPL (__iommu_sva_bind_device);int __iommu_sva_unbind_device (struct device *dev,int pasid) { -return -ENOSYS ;/* TODO */ +int ret = -ESRCH ; +struct iommu_domain *domain; +struct iommu_bond *bond =NULL ; +struct iommu_sva_param *param = dev->iommu_param->sva_param; + + domain =iommu_get_domain_for_dev (dev); +if (!param ||WARN_ON (!domain)) +return -EINVAL ; + +spin_lock (&iommu_sva_lock); +list_for_each_entry (bond, ¶m->mm_list, dev_head) { +if (bond->io_mm->pasid == pasid) { +io_mm_detach_locked (bond); + ret =0 ; +break ; + } + } +spin_unlock (&iommu_sva_lock); + +return ret; }EXPORT_SYMBOL_GPL (__iommu_sva_unbind_device);@@ -132,6 +497,15 @@ EXPORT_SYMBOL_GPL(__iommu_sva_unbind_device);*/ void __iommu_sva_unbind_dev_all (struct device *dev) { -/* TODO */ +struct iommu_sva_param *param; +struct iommu_bond *bond, *next; + + param = dev->iommu_param->sva_param; +if (param) { +spin_lock (&iommu_sva_lock); +list_for_each_entry_safe (bond, next, ¶m->mm_list, dev_head) +io_mm_detach_locked (bond); +spin_unlock (&iommu_sva_lock); + } }EXPORT_SYMBOL_GPL (__iommu_sva_unbind_dev_all);drivers/iommu/iommu.c +1
-0@@ -1463,6 +1463,7 @@ static struct iommu_domain *__iommu_domain_alloc(struct bus_type *bus, domain->type = type;/* Assume all sizes by default; the driver may override this later */ domain->pgsize_bitmap = bus->iommu_ops->pgsize_bitmap; +INIT_LIST_HEAD (&domain->mm_list);return domain; }include/linux/iommu.h +28
-0@@ -100,6 +100,20 @@ struct iommu_domain {void *handler_token;struct iommu_domain_geometry geometry;void *iova_cookie; + +struct list_head mm_list; +}; + +struct io_mm { +int pasid; +/* IOMMU_SVA_FEAT_* */ +unsigned long flags; +struct list_head devices; +struct kref kref; +struct mm_struct *mm; + +/* Release callback for this mm */ +void (*release)(struct io_mm *io_mm); };enum iommu_cap {@@ -216,6 +230,7 @@ struct iommu_sva_param {unsigned long features;unsigned int min_pasid;unsigned int max_pasid; +struct list_head mm_list; };/** @@ -227,6 +242,11 @@ struct iommu_sva_param {* @detach_dev: detach device from an iommu domain * @sva_device_init: initialize Shared Virtual Adressing for a device * @sva_device_shutdown: shutdown Shared Virtual Adressing for a device +* @mm_alloc: allocate io_mm +* @mm_free: free io_mm +* @mm_attach: attach io_mm to a device. Install PASID entry if necessary +* @mm_detach: detach io_mm from a device. Remove PASID entry and +* flush associated TLB entries. * @map: map a physically contiguous memory region to an iommu domain * @unmap: unmap a physically contiguous memory region from an iommu domain * @map_sg: map a scatter-gather list of physically contiguous memory chunks @@ -268,6 +288,14 @@ struct iommu_ops { struct iommu_sva_param *param);void (*sva_device_shutdown)(struct device *dev,struct iommu_sva_param *param); +struct io_mm *(*mm_alloc)(struct iommu_domain *domain, +struct mm_struct *mm, +unsigned long flags); +void (*mm_free)(struct io_mm *io_mm); +int (*mm_attach)(struct iommu_domain *domain,struct device *dev, +struct io_mm *io_mm,bool attach_domain); +void (*mm_detach)(struct iommu_domain *domain,struct device *dev, +struct io_mm *io_mm,bool detach_domain);int (*map)(struct iommu_domain *domain,unsigned long iova,phys_addr_t paddr,size_t size,int prot); size_t (*unmap)(struct iommu_domain *domain, unsigned long iova,
Re: [PATCH v2 03/40] iommu/sva: Manage process address spaces
Quoted 39 lines · Jonathan Cameron
Quoted 24 lines
drivers/iommu/iommu-sva.c +24
-0+static struct io_mm * +io_mm_alloc (struct iommu_domain *domain,struct device *dev, +struct mm_struct *mm,unsigned long flags) +{ +int ret; +int pasid; +struct io_mm *io_mm; +struct iommu_sva_param *param = dev->iommu_param->sva_param; + +if (!domain->ops->mm_alloc || !domain->ops->mm_free) +return ERR_PTR (-ENODEV ); + + io_mm = domain->ops->mm_alloc (domain, mm, flags); +if (IS_ERR (io_mm)) +return io_mm; +if (!io_mm) +return ERR_PTR (-ENOMEM ); + +/* +* The mm must not be freed until after the driver frees the io_mm +* (which may involve unpinning the CPU ASID for instance, requiring a +* valid mm struct.) +*/ +mmgrab (mm);drivers/iommu/iommu-sva.c line 160
Quoted 10 lines
drivers/iommu/iommu-sva.c +10
-0+ + io_mm->flags = flags; + io_mm->mm = mm; + io_mm->release = domain->ops->mm_free; +INIT_LIST_HEAD (&io_mm->devices); + +idr_preload (GFP_KERNEL ); +spin_lock (&iommu_sva_lock); + pasid =idr_alloc (&iommu_pasid_idr, io_mm, param->min_pasid, + param->max_pasid +1 ,GFP_ATOMIC );I'd expect the IDR cleanup to be in
io_mm_freeas that would 'match' againstio_mm_allocbut it's inio_mm_releasejust before theio_mm_freecall, perhaps move it or am I missing something?
Quoted 3 lines
Hmm. This is reworked in patch 5 to use call rcu to do the free. I suppose we may be burning an idr entry if we take a while to get round to the free.. If so a comment to explain this would be great.
Ok, I'll see if I can come up with some comments for both patch 3 and 5.
Quoted 8 lines
Quoted 8 lines
drivers/iommu/iommu-sva.c +8
-0+ io_mm->pasid = pasid; +spin_unlock (&iommu_sva_lock); +idr_preload_end (); + +if (pasid <0 ) { + ret = pasid; + goto err_free_mm; + }
Quoted 8 lines
Review
Quoted 2 lines
(quoted diff) +2
-0+ + /* TODO: keep track of mm. For the moment, abort. */From later patches, I can now see why we didn't init the kref here, but perhaps a comment would make that clear rather than people checking it is correctly used throughout? Actually just grab the comment from patch 5 and put it in this one and that will do the job nicely.
Ok
Quoted 11 lines
drivers/iommu/iommu-sva.c line 177
Quoted 7 lines
drivers/iommu/iommu-sva.c +7
-0+ ret = -ENOSYS ; +spin_lock (&iommu_sva_lock); +idr_remove (&iommu_pasid_idr, io_mm->pasid); +spin_unlock (&iommu_sva_lock); + +err_free_mm: + domain->ops->mm_free (io_mm);Really minor, but you now have
io_mm->releaseset so to keep this obviously the same as theio_mm_freepath, perhaps call that rather thanmm_freedirectly.
Yes, makes sense
Quoted 11 lines
Quoted 11 lines
drivers/iommu/iommu-sva.c +11
-0+static void io_mm_detach_locked (struct iommu_bond *bond) +{ +struct iommu_bond *tmp; +bool detach_domain = true; +struct iommu_domain *domain = bond->domain; + +list_for_each_entry (tmp, &domain->mm_list, domain_head) { +if (tmp->io_mm == bond->io_mm && tmp->dev != bond->dev) { + detach_domain = false; +break ; + }
Quoted 8 lines
drivers/iommu/iommu-sva.c line 292
Quoted 4 lines
drivers/iommu/iommu-sva.c +4
-0+ } + + domain->ops->mm_detach (domain, bond->dev, bond->io_mm, detach_domain); +I can't see an immediate reason to have a different order in her to the reverse of
the attach above. So I think you should be detaching after thelist_delcalls.
If there is a reason, can we have a comment so I don't ask on v10.
I don't see a reason either right now, I'll see if it can be moved
Quoted 7 lines
Quoted 5 lines
drivers/iommu/iommu-sva.c +4
-0+list_del (&bond->mm_head); +list_del (&bond->domain_head); +list_del (&bond->dev_head); +io_mm_put_locked (bond->io_mm);
Quoted 14 lines
drivers/iommu/iommu-sva.c EXPORT_SYMBOL_GPL(iommu_sva_device_shutdown); · line 432
Quoted 8 lines
drivers/iommu/iommu-sva.c +8
-0+/* If an io_mm already exists, use it */ +spin_lock (&iommu_sva_lock); +idr_for_each_entry (&iommu_pasid_idr, io_mm, i) { +if (io_mm->mm == mm &&io_mm_get_locked (io_mm)) { +/* ... Unless it's already bound to this device */ +list_for_each_entry (tmp, &io_mm->devices, mm_head) { +if (tmp->dev == dev) { + bond = tmp;Using bond for this is clear in a sense, but can we not just use ret so it is obvious here that we are going to return
-EEXIST? At first glance I thought you were going to carry on with this bond and couldn't work out why it would ever make sense to have two bonds between a device an anio_mm(which it doesn't!)
Yes, using ret is nicer
Thanks,
Jean
Re: [PATCH v2 03/40] iommu/sva: Manage process address spaces
In reply to Jean-Philippe BruckerHi Jean-Philippe,
Quoted 1 line · Jean-Philippe Brucker
Allocate IOMMU mm structures and binding them to devices. Four operations
s/binding/bind
Quoted 142 lines
are added to IOMMU drivers:
*
mm_alloc(): to create anio_mmstructure and perform architecture- specific operations required to grab the process (for instance on ARM, pin down the CPU ASID so that the process doesn't get assigned a new ASID on rollover).There is a single valid
io_mmstructure per Linux mm. Future extensions may also useio_mmfor kernel-managed address spaces, populated with map()/unmap() calls instead of bound to process address spaces. This patch focuses on "shared"io_mm.*
mm_attach(): attach an mm to a device. The IOMMU driver checks that the device is capable of sharing an address space, and writes the PASID table entry to install the pgd.Some IOMMU drivers will have a single PASID table per domain, for convenience. Other can implement it differently but to help these drivers,
mm_attachandmm_detachtake 'attach_domain' and 'detach_domain' parameters, that tell whether they need to set and clear the PASID entry or only send the required TLB invalidations.*
mm_detach(): detach an mm from a device. The IOMMU driver removes the PASID table entry and invalidates the IOTLBs.*
mm_free(): free a structure allocated bymm_alloc(), and let arch release the process.
mm_attachandmm_detachoperations are serialized with a spinlock. When trying to optimize this code, we should at least prevent concurrent attach()/detach() on the same domain (so multi-level PASID table code can allocate tables lazily).mm_alloc()can sleep, butmm_freemust not (because we'll have to call it fromcall_srculater on).At the moment we use an IDR for allocating PASIDs and retrieving contexts. We also use a single spinlock. These can be refined and optimized later (a custom allocator will be needed for top-down PASID allocation).
Keeping track of address spaces requires the use of MMU notifiers. Handling process exit with regard to
unbind()is tricky, so it is left for another patch and we explicitly failmm_alloc()for the moment.Signed-off-by Jean-Philippe Brucker <[email protected]>---
v1->v2: sanity-check of flags--- drivers/iommu/iommu-sva.c | 380 +++++++++++++++++++++++++++++++++++++- drivers/iommu/iommu.c | 1 + include/linux/iommu.h | 28 +++ 3 files changed, 406 insertions(+), 3 deletions(-)drivers/iommu/iommu-sva.c +80
-0@@ -5,8 +5,298 @@* Copyright (C) 2018 ARM Ltd. */ +#include <linux/idr.h> #include <linux/iommu.h> +#include <linux/sched/mm.h> #include <linux/slab.h> +#include <linux/spinlock.h> + +/** +* DOC: io_mm model +* +* The io_mm keeps track of process address spaces shared between CPU and IOMMU. +* The following example illustrates the relation between structures +* iommu_domain, io_mm and iommu_bond. An iommu_bond is a link between io_mm and +* device. A device can have multiple io_mm and an io_mm may be bound to +* multiple devices. +* ___________________________ +* | IOMMU domain A | +* | ________________ | +* | | IOMMU group | +------- io_pgtables +* | | | | +* | | dev 00:00.0 ----+------- bond --- io_mm X +* | |________________| \ | +* | '----- bond ---. +* |___________________________| \ +* ___________________________ \ +* | IOMMU domain B | io_mm Y +* | ________________ | / / +* | | IOMMU group | | / / +* | | | | / / +* | | dev 00:01.0 ------------ bond -' / +* | | dev 00:01.1 ------------ bond --' +* | |________________| | +* | +------- io_pgtables +* |___________________________| +* +* In this example, device 00:00.0 is in domain A, devices 00:01.* are in domain +* B. All devices within the same domain access the same address spaces. Device +* 00:00.0 accesses address spaces X and Y, each corresponding to an mm_struct. +* Devices 00:01.* only access address space Y. In addition each +* IOMMU_DOMAIN_DMA domain has a private address space, io_pgtable, that is +* managed with iommu_map()/iommu_unmap(), and isn't shared with the CPU MMU. +* +* To obtain the above configuration, users would for instance issue the +* following calls: +* +* iommu_sva_bind_device(dev 00:00.0, mm X, ...) -> PASID 1 +* iommu_sva_bind_device(dev 00:00.0, mm Y, ...) -> PASID 2 +* iommu_sva_bind_device(dev 00:01.0, mm Y, ...) -> PASID 2 +* iommu_sva_bind_device(dev 00:01.1, mm Y, ...) -> PASID 2 +* +* A single Process Address Space ID (PASID) is allocated for each mm. In the +* example, devices use PASID 1 to read/write into address space X and PASID 2 +* to read/write into address space Y. +* +* Hardware tables describing this configuration in the IOMMU would typically +* look like this: +* +* PASID tables +* of domain A +* .->+--------+ +* / 0 | |-------> io_pgtable +* / +--------+ +* Device tables / 1 | |-------> pgd X +* +--------+ / +--------+ +* 00:00.0 | A |-' 2 | |--. +* +--------+ +--------+ \ +* : : 3 | | \ +* +--------+ +--------+ --> pgd Y +* 00:01.0 | B |--. / +* +--------+ \ | +* 00:01.1 | B |----+ PASID tables | +* +--------+ \ of domain B | +* '->+--------+ | +* 0 | |-- | --> io_pgtable +* +--------+ | +* 1 | | | +* +--------+ | +* 2 | |---' +* +--------+ +* 3 | | +* +--------+ +* +* With this model, a single call binds all devices in a given domain to an
drivers/iommu/iommu-sva.c line 99
Quoted 10 lines
drivers/iommu/iommu-sva.c +10
-0+* address space. Other devices in the domain will get the same bond implicitly. +* However, users must issue one bind() for each device, because IOMMUs may +* implement SVA differently. Furthermore, mandating one bind() per device +* allows the driver to perform sanity-checks on device capabilities. +* +* On Arm and AMD IOMMUs, entry 0 of the PASID table can be used to hold +* non-PASID translations. In this case PASID 0 is reserved and entry 0 points +* to the io_pgtable base. On Intel IOMMU, the io_pgtable base would be held in +* the device table and PASID 0 would be available to the allocator. +*/
very nice explanation
Quoted 26 lines
drivers/iommu/iommu-sva.c +26
-0+ +struct iommu_bond { +struct io_mm *io_mm; +struct device *dev; +struct iommu_domain *domain; + +struct list_head mm_head; +struct list_head dev_head; +struct list_head domain_head; + +void *drvdata; +}; + +/* +* Because we're using an IDR, PASIDs are limited to 31 bits (the sign bit is +* used for returning errors). In practice implementations will use at most 20 +* bits, which is the PCI limit. +*/ +static DEFINE_IDR (iommu_pasid_idr); + +/* +* For the moment this is an all-purpose lock. It serializes +* access/modifications to bonds, access/modifications to the PASID IDR, and +* changes to io_mm refcount as well. +*/ +static DEFINE_SPINLOCK (iommu_sva_lock);
drivers/iommu/iommu-sva.c line 135
Quoted 10 lines
drivers/iommu/iommu-sva.c +10
-0+ +static struct io_mm * +io_mm_alloc (struct iommu_domain *domain,struct device *dev, +struct mm_struct *mm,unsigned long flags) +{ +int ret; +int pasid; +struct io_mm *io_mm; +struct iommu_sva_param *param = dev->iommu_param->sva_param; +
don't you need to check param != NULL and flags are compatible with those set at init?
Quoted 15 lines
drivers/iommu/iommu-sva.c +15
-0+if (!domain->ops->mm_alloc || !domain->ops->mm_free) +return ERR_PTR (-ENODEV ); + + io_mm = domain->ops->mm_alloc (domain, mm, flags); +if (IS_ERR (io_mm)) +return io_mm; +if (!io_mm) +return ERR_PTR (-ENOMEM ); + +/* +* The mm must not be freed until after the driver frees the io_mm +* (which may involve unpinning the CPU ASID for instance, requiring a +* valid mm struct.) +*/ +mmgrab (mm);
drivers/iommu/iommu-sva.c line 160
Quoted 10 lines
drivers/iommu/iommu-sva.c +10
-0+ + io_mm->flags = flags; + io_mm->mm = mm; + io_mm->release = domain->ops->mm_free; +INIT_LIST_HEAD (&io_mm->devices); + +idr_preload (GFP_KERNEL ); +spin_lock (&iommu_sva_lock); + pasid =idr_alloc (&iommu_pasid_idr, io_mm, param->min_pasid, + param->max_pasid +1 ,GFP_ATOMIC );
isn't it param->max_pasid - 1?
Quoted 67 lines
drivers/iommu/iommu-sva.c +67
-0+ io_mm->pasid = pasid; +spin_unlock (&iommu_sva_lock); +idr_preload_end (); + +if (pasid <0 ) { + ret = pasid; + goto err_free_mm; + } + + /* TODO: keep track of mm. For the moment, abort. */I don't get where you return with success? + ret = -ENOSYS ; +spin_lock (&iommu_sva_lock); +idr_remove (&iommu_pasid_idr, io_mm->pasid); +spin_unlock (&iommu_sva_lock); + +err_free_mm: + domain->ops->mm_free (io_mm); +mmdrop (mm); + + return ERR_PTR(ret) +} + +static void io_mm_free (struct io_mm *io_mm) +{ +struct mm_struct *mm = io_mm->mm; + + io_mm->release (io_mm); +mmdrop (mm); +} + +static void io_mm_release (struct kref *kref) +{ +struct io_mm *io_mm; + + io_mm = container_of(kref, struct io_mm, kref); +WARN_ON (!list_empty (&io_mm->devices)); + +idr_remove (&iommu_pasid_idr, io_mm->pasid); + +io_mm_free (io_mm); +} + +/* +* Returns non-zero if a reference to the io_mm was successfully taken. +* Returns zero if the io_mm is being freed and should not be used. +*/ +static int io_mm_get_locked (struct io_mm *io_mm) +{ +if (io_mm) +return kref_get_unless_zero (&io_mm->kref); + +return 0 ; +} + +static void io_mm_put_locked (struct io_mm *io_mm) +{ +kref_put (&io_mm->kref, io_mm_release); +} + +static void io_mm_put (struct io_mm *io_mm) +{ +spin_lock (&iommu_sva_lock); +io_mm_put_locked (io_mm); +spin_unlock (&iommu_sva_lock); +} + +static int io_mm_attach(struct iommu_domain *domain, struct device *dev,
drivers/iommu/iommu-sva.c line 237
Quoted 10 lines
drivers/iommu/iommu-sva.c +10
-0+ struct io_mm *io_mm, void *drvdata) +{ + int ret; +bool attach_domain = true; +int pasid = io_mm->pasid; +struct iommu_bond *bond, *tmp; +struct iommu_sva_param *param = dev->iommu_param->sva_param; + +if (!domain->ops->mm_attach || !domain->ops->mm_detach) +return -ENODEV ;
don't you need to check param is not NULL?
drivers/iommu/iommu-sva.c line 239
Quoted 2 lines
drivers/iommu/iommu-sva.c +2
-0+ +if (pasid > param->max_pasid || pasid < param->min_pasid)
pasid >= param->max_pasid ?
Quoted 14 lines
drivers/iommu/iommu-sva.c +14
-0+return -ERANGE ; + + bond =kzalloc (sizeof (*bond),GFP_KERNEL ); +if (!bond) +return -ENOMEM ; + + bond->domain = domain; + bond->io_mm = io_mm; + bond->dev = dev; + bond->drvdata = drvdata; + +spin_lock (&iommu_sva_lock); +/* +* Check if this io_mm is already bound to the domain. In which case the
drivers/iommu/iommu-sva.c line 263
Quoted 10 lines
drivers/iommu/iommu-sva.c +10
-0+* IOMMU driver doesn't have to install the PASID table entry. +*/ +list_for_each_entry (tmp, &domain->mm_list, domain_head) { +if (tmp->io_mm == io_mm) { + attach_domain = false; +break ; + } + } + + ret = domain->ops->mm_attach (domain, dev, io_mm, attach_domain);
the fact the mm_attach/detach() must not sleep may be documented in the API doc.
drivers/iommu/iommu-sva.c line 267
Quoted 4 lines
drivers/iommu/iommu-sva.c +4
-0+if (ret) { +kfree (bond); +spin_unlock (&iommu_sva_lock); +return ret;
nit: goto unlock simplification
Quoted 52 lines
drivers/iommu/iommu-sva.c +37
-1+ } + +list_add (&bond->mm_head, &io_mm->devices); +list_add (&bond->domain_head, &domain->mm_list); +list_add (&bond->dev_head, ¶m->mm_list); +spin_unlock (&iommu_sva_lock); + +return 0 ; +} + +static void io_mm_detach_locked (struct iommu_bond *bond) +{ +struct iommu_bond *tmp; +bool detach_domain = true; +struct iommu_domain *domain = bond->domain; + +list_for_each_entry (tmp, &domain->mm_list, domain_head) { +if (tmp->io_mm == bond->io_mm && tmp->dev != bond->dev) { + detach_domain = false; +break ; + } + } + + domain->ops->mm_detach (domain, bond->dev, bond->io_mm, detach_domain); + +list_del (&bond->mm_head); +list_del (&bond->domain_head); +list_del (&bond->dev_head); +io_mm_put_locked (bond->io_mm); + +kfree (bond); +}/** * iommu_sva_device_init() - Initialize Shared Virtual Addressing for a device @@ -47,6 +337,7 @@ int iommu_sva_device_init(struct device *dev, unsigned long features, param->features = features; param->max_pasid = max_pasid; +INIT_LIST_HEAD (¶m->mm_list);/* * IOMMU driver updates the limits depending on the IOMMU and device @@ -114,13 +405,87 @@ EXPORT_SYMBOL_GPL(iommu_sva_device_shutdown);int __iommu_sva_bind_device (struct device *dev,struct mm_struct *mm,int *pasid,unsigned long flags,void *drvdata) { -return -ENOSYS ;/* TODO */ +int i, ret =0 ; +struct io_mm *io_mm =NULL ; +struct iommu_domain *domain; +struct iommu_bond *bond =NULL , *tmp;
drivers/iommu/iommu-sva.c EXPORT_SYMBOL_GPL(iommu_sva_device_shutdown); · line 421
Quoted 10 lines
drivers/iommu/iommu-sva.c +10
-0+struct iommu_sva_param *param = dev->iommu_param->sva_param; + + domain =iommu_get_domain_for_dev (dev); +if (!domain) +return -EINVAL ; + +/* +* The device driver does not call sva_device_init/shutdown and +* bind/unbind concurrently, so no need to take the param lock. +*/
what does guarantee that?
Quoted 28 lines
drivers/iommu/iommu-sva.c +28
-0+if (WARN_ON_ONCE (!param) || (flags & ~param->features)) +return -EINVAL ; + +/* If an io_mm already exists, use it */ +spin_lock (&iommu_sva_lock); +idr_for_each_entry (&iommu_pasid_idr, io_mm, i) { +if (io_mm->mm == mm &&io_mm_get_locked (io_mm)) { +/* ... Unless it's already bound to this device */ +list_for_each_entry (tmp, &io_mm->devices, mm_head) { +if (tmp->dev == dev) { + bond = tmp; +io_mm_put_locked (io_mm); +break ; + } + } +break ; + } + } +spin_unlock (&iommu_sva_lock); + +if (bond) +return -EEXIST ; + +/* Require identical features within an io_mm for now */ +if (io_mm && (flags != io_mm->flags)) { +io_mm_put (io_mm); +return -EDOM ; + }
drivers/iommu/iommu-sva.c EXPORT_SYMBOL_GPL(iommu_sva_device_shutdown); · line 459
Quoted 10 lines
drivers/iommu/iommu-sva.c +10
-0+ +if (!io_mm) { + io_mm =io_mm_alloc (domain, dev, mm, flags); +if (IS_ERR (io_mm)) +return PTR_ERR (io_mm); + } + + ret =io_mm_attach (domain, dev, io_mm, drvdata); +if (ret) +io_mm_put (io_mm);
dont't you want to free the io_mm if just allocated?
Quoted 96 lines
drivers/iommu/iommu-sva.c +34
-2+else + *pasid = io_mm->pasid; + +return ret; }EXPORT_SYMBOL_GPL (__iommu_sva_bind_device);int __iommu_sva_unbind_device (struct device *dev,int pasid) { -return -ENOSYS ;/* TODO */ +int ret = -ESRCH ; +struct iommu_domain *domain; +struct iommu_bond *bond =NULL ; +struct iommu_sva_param *param = dev->iommu_param->sva_param; + + domain =iommu_get_domain_for_dev (dev); +if (!param ||WARN_ON (!domain)) +return -EINVAL ; + +spin_lock (&iommu_sva_lock); +list_for_each_entry (bond, ¶m->mm_list, dev_head) { +if (bond->io_mm->pasid == pasid) { +io_mm_detach_locked (bond); + ret =0 ; +break ; + } + } +spin_unlock (&iommu_sva_lock); + +return ret; }EXPORT_SYMBOL_GPL (__iommu_sva_unbind_device);@@ -132,6 +497,15 @@ EXPORT_SYMBOL_GPL(__iommu_sva_unbind_device);*/ void __iommu_sva_unbind_dev_all (struct device *dev) { -/* TODO */ +struct iommu_sva_param *param; +struct iommu_bond *bond, *next; + + param = dev->iommu_param->sva_param; +if (param) { +spin_lock (&iommu_sva_lock); +list_for_each_entry_safe (bond, next, ¶m->mm_list, dev_head) +io_mm_detach_locked (bond); +spin_unlock (&iommu_sva_lock); + } }EXPORT_SYMBOL_GPL (__iommu_sva_unbind_dev_all);drivers/iommu/iommu.c +1
-0@@ -1463,6 +1463,7 @@ static struct iommu_domain *__iommu_domain_alloc(struct bus_type *bus, domain->type = type;/* Assume all sizes by default; the driver may override this later */ domain->pgsize_bitmap = bus->iommu_ops->pgsize_bitmap; +INIT_LIST_HEAD (&domain->mm_list);return domain; }include/linux/iommu.h +15
-0@@ -100,6 +100,20 @@ struct iommu_domain {void *handler_token;struct iommu_domain_geometry geometry;void *iova_cookie; + +struct list_head mm_list; +}; + +struct io_mm { +int pasid; +/* IOMMU_SVA_FEAT_* */ +unsigned long flags; +struct list_head devices; +struct kref kref; +struct mm_struct *mm; + +/* Release callback for this mm */ +void (*release)(struct io_mm *io_mm); };enum iommu_cap {@@ -216,6 +230,7 @@ struct iommu_sva_param {unsigned long features;unsigned int min_pasid;unsigned int max_pasid; +struct list_head mm_list; };/**
include/linux/iommu.h struct iommu_sva_param { · line 249
Quoted 8 lines
include/linux/iommu.h +5
-0@@ -227,6 +242,11 @@ struct iommu_sva_param {* @detach_dev: detach device from an iommu domain * @sva_device_init: initialize Shared Virtual Adressing for a device * @sva_device_shutdown: shutdown Shared Virtual Adressing for a device +* @mm_alloc: allocate io_mm +* @mm_free: free io_mm +* @mm_attach: attach io_mm to a device. Install PASID entry if necessary +* @mm_detach: detach io_mm from a device. Remove PASID entry and +* flush associated TLB entries.
if necessary too?
Quoted 9 lines
include/linux/iommu.h +1
-0* @map: map a physically contiguous memory region to an iommu domain * @unmap: unmap a physically contiguous memory region from an iommu domain * @map_sg: map a scatter-gather list of physically contiguous memory chunks @@ -268,6 +288,14 @@ struct iommu_ops { struct iommu_sva_param *param);void (*sva_device_shutdown)(struct device *dev,struct iommu_sva_param *param); + struct io_mm *(*mm_alloc)(struct iommu_domain *domain,
include/linux/iommu.h struct iommu_ops { · line 301
Quoted 10 lines
include/linux/iommu.h +7
-0+struct mm_struct *mm, + unsigned long flags); +void (*mm_free)(struct io_mm *io_mm); +int (*mm_attach)(struct iommu_domain *domain,struct device *dev, +struct io_mm *io_mm,bool attach_domain); +void (*mm_detach)(struct iommu_domain *domain,struct device *dev, +struct io_mm *io_mm,bool detach_domain);int (*map)(struct iommu_domain *domain,unsigned long iova,phys_addr_t paddr,size_t size,int prot); size_t (*unmap)(struct iommu_domain *domain, unsigned long iova,
Thanks
Eric
Re: [PATCH v2 03/40] iommu/sva: Manage process address spaces
Quoted 11 lines · On Wed, 5 Sep 2018 14:14:12 +0200 Auger Eric <[email protected]> wrote:
Quoted 10 lines
+ *
+ * On Arm and AMD IOMMUs, entry 0 of the PASID table can be used to hold
+ * non-PASID translations. In this case PASID 0 is reserved and entry 0 points
+ * to theio_pgtablebase. On Intel IOMMU, theio_pgtablebase would be held in
+ * the device table and PASID 0 would be available to the allocator.
+ */very nice explanation
With the new Vt-d 3.0 spec., 2nd level IO page table base is no longer held in the device context table. Instead it is held in the PASID table entry pointed by the RID_PASID field in the device context entry. If RID_PASID = 0, then it is the same as ARM and AMD IOMMUs. You can refer to ch3.4.3 of the VT-d spec.
Re: [PATCH v2 03/40] iommu/sva: Manage process address spaces
Quoted 13 lines · Jacob Pan
Quoted 10 lines · On Wed, 5 Sep 2018 14:14:12 +0200 Auger Eric <[email protected]> wrote:
Quoted 10 lines
+ *
+ * On Arm and AMD IOMMUs, entry 0 of the PASID table can be used to hold
+ * non-PASID translations. In this case PASID 0 is reserved and entry 0 points
+ * to theio_pgtablebase. On Intel IOMMU, theio_pgtablebase would be held in
+ * the device table and PASID 0 would be available to the allocator.
+ */
Quoted 6 lines
Quoted 1 line
very nice explanation
With the new Vt-d 3.0 spec., 2nd level IO page table base is no longer held in the device context table. Instead it is held in the PASID table entry pointed by the
RID_PASIDfield in the device context entry. IfRID_PASID= 0, then it is the same as ARM and AMD IOMMUs. You can refer to ch3.4.3 of the VT-d spec.
I could simplify that paragraph by removing the specific implementations:
"In some IOMMUs, entry 0 of the PASID table can be used to hold non-PASID translations. In this case PASID 0 is reserved and entry 0 points to the io_pgtable base. In other IOMMUs the io_pgtable base is held in the device table and PASID 0 is available to the allocator."
I guess in Linux there isn't any reason to set RID_PASID to a non-zero value? Otherwise the iommu-sva allocator will need minor changes.
Thanks,
Jean
Re: [PATCH v2 03/40] iommu/sva: Manage process address spaces
In reply to Auger EricQuoted 11 lines · Auger Eric
drivers/iommu/iommu-sva.c line 135
Quoted 9 lines
drivers/iommu/iommu-sva.c +9
-0+static struct io_mm * +io_mm_alloc (struct iommu_domain *domain,struct device *dev, +struct mm_struct *mm,unsigned long flags) +{ +int ret; +int pasid; +struct io_mm *io_mm; +struct iommu_sva_param *param = dev->iommu_param->sva_param; +don't you need to check param != NULL and flags are compatible with those set at init?
It would be redundant, parameters are already checked by bind(). Following your comment below I think this function should also be called under iommu_param->lock
Quoted 5 lines
drivers/iommu/iommu-sva.c line 160
Quoted 4 lines
drivers/iommu/iommu-sva.c +4
-0+idr_preload (GFP_KERNEL ); +spin_lock (&iommu_sva_lock); + pasid =idr_alloc (&iommu_pasid_idr, io_mm, param->min_pasid, + param->max_pasid +1 ,GFP_ATOMIC );isn't it
param->max_pasid- 1?
max_pasid is the last allocatable PASID, and the 'end' parameter of idr_alloc is exclusive, so this needs to be max_pasid + 1.
Quoted 4 lines
Quoted 4 lines
drivers/iommu/iommu-sva.c +4
-0+static int io_mm_attach (struct iommu_domain *domain,struct device *dev, +struct io_mm *io_mm,void *drvdata) +{ +int ret;
Quoted 8 lines
drivers/iommu/iommu-sva.c line 237
Quoted 7 lines
drivers/iommu/iommu-sva.c +7
-0+bool attach_domain = true; +int pasid = io_mm->pasid; +struct iommu_bond *bond, *tmp; +struct iommu_sva_param *param = dev->iommu_param->sva_param; + +if (!domain->ops->mm_attach || !domain->ops->mm_detach) +return -ENODEV ;don't you need to check param is not NULL?
As mm_alloc, this is called by bind() which already performs argument checks
Quoted 3 lines
Review
Quoted 2 lines
(quoted diff) +2
-0+ + if (pasid > param->max_pasid || pasid < param->min_pasid)pasid >=
param->max_pasid?
max_pasid is inclusive
Quoted 3 lines
drivers/iommu/iommu-sva.c line 263
Quoted 1 line
drivers/iommu/iommu-sva.c +1
-0+ ret = domain->ops->mm_attach (domain, dev, io_mm, attach_domain);the fact the mm_attach/detach() must not sleep may be documented in the API doc.
Ok
Quoted 11 lines
Quoted 11 lines
int __iommu_sva_bind_device(struct device *dev, struct
mm_struct*mm,drivers/iommu/iommu-sva.c +7
-1int *pasid, unsigned long flags, void *drvdata) { - return -ENOSYS; /* TODO */ + int i, ret = 0; +struct io_mm *io_mm =NULL ; +struct iommu_domain *domain; +struct iommu_bond *bond =NULL , *tmp; +struct iommu_sva_param *param = dev->iommu_param->sva_param; + + domain =iommu_get_domain_for_dev (dev);
Quoted 8 lines
drivers/iommu/iommu-sva.c EXPORT_SYMBOL_GPL(iommu_sva_device_shutdown); · line 421
Quoted 7 lines
drivers/iommu/iommu-sva.c +7
-0+if (!domain) +return -EINVAL ; + +/* +* The device driver does not call sva_device_init/shutdown and +* bind/unbind concurrently, so no need to take the param lock. +*/ what does guarantee that?
The doc for iommu_sva_bind_device mandates that iommu_sva_device_init() is called before bind(), but nothing is said about unbind and shutdown. I think that was just based on the assumption that the device driver doesn't have any reason to call unbind and shutdown concurrently, but taking the lock here and in unbind is probably safer.
Quoted 4 lines
drivers/iommu/iommu-sva.c EXPORT_SYMBOL_GPL(iommu_sva_device_shutdown); · line 459
Quoted 3 lines
drivers/iommu/iommu-sva.c +3
-0+ ret =io_mm_attach (domain, dev, io_mm, drvdata); +if (ret) +io_mm_put (io_mm);dont't you want to free the
io_mmif just allocated?
We do: if the io_mm has just been allocated, it has a single reference so io_mm_put frees it.
Quoted 4 lines
Quoted 3 lines
+ * @mm_attach: attach
io_mmto a device. Install PASID entry if necessary
+ * @mm_detach: detachio_mmfrom a device. Remove PASID entry and
+ * flush associated TLB entries.if necessary too?
Right
Thanks,
Jean
PATCH v2 4/40 iommu/sva: Add a mm_exit callback for device drivers
In reply to Jean-Philippe BruckerWhen an mm exits, devices that were bound to it must stop performing DMA on its PASID. Let device drivers register a callback to be notified on mm exit. Add the callback to the sva_param structure attached to struct device.
---v1->v2: use iommu_sva_device_init instead of a separate function
--- drivers/iommu/iommu-sva.c | 11 ++++++++++- include/linux/iommu.h | 9 +++++++-- 2 files changed, 17 insertions(+), 3 deletions(-)
drivers/iommu/iommu-sva.c +10 -1
@@ -303,6 +303,7 @@ static void io_mm_detach_locked(struct iommu_bond *bond)* @dev: the device * @features: bitmask of features that need to be initialized * @max_pasid: max PASID value supported by the device +* @mm_exit: callback to notify the device driver of an mm exiting * * Users of the bind()/unbind() API must call this function to initialize all * features required for SVA.
@@ -313,13 +314,20 @@ static void io_mm_detach_locked(struct iommu_bond *bond)* description. Setting @max_pasid to a non-zero value smaller than this limit * overrides it. * +* If the driver intends to share process address spaces, it should pass a valid +* @mm_exit handler. Otherwise @mm_exit can be NULL. After @mm_exit returns, the +* device must not issue any more transaction with the PASID given as argument. +* The handler gets an opaque pointer corresponding to the drvdata passed as +* argument of bind(). +* * The device should not be performing any DMA while this function is running, * otherwise the behavior is undefined. * * Return 0 if initialization succeeded, or an error. */ int iommu_sva_device_init (struct device *dev,unsigned long features, -unsigned int max_pasid) +unsigned int max_pasid, +iommu_mm_exit_handler_t mm_exit) {int ret;struct iommu_sva_param *param;
@@ -337,6 +345,7 @@ int iommu_sva_device_init(struct device *dev, unsigned long features, param->features = features; param->max_pasid = max_pasid; + param->mm_exit = mm_exit;INIT_LIST_HEAD (¶m->mm_list);/*
include/linux/iommu.h +7 -2
@@ -61,6 +61,7 @@ struct iommu_fault_event;typedef int (*iommu_fault_handler_t )(struct iommu_domain *,struct device *,unsigned long ,int ,void *);typedef int (*iommu_dev_fault_handler_t )(struct iommu_fault_event *,void *); +typedef int (*iommu_mm_exit_handler_t )(struct device *dev,int pasid,void *);struct iommu_domain_geometry {dma_addr_t aperture_start;/* First address that can be mapped */
@@ -231,6 +232,7 @@ struct iommu_sva_param {unsigned int min_pasid;unsigned int max_pasid;struct list_head mm_list; +iommu_mm_exit_handler_t mm_exit; };/**
@@ -980,17 +982,20 @@ static inline int iommu_sva_unbind_device(struct device *dev, int pasid)#ifdef CONFIG_IOMMU_SVA extern int iommu_sva_device_init (struct device *dev,unsigned long features, -unsigned int max_pasid); +unsigned int max_pasid, +iommu_mm_exit_handler_t mm_exit);extern int iommu_sva_device_shutdown (struct device *dev);extern int __iommu_sva_bind_device (struct device *dev,struct mm_struct *mm,int *pasid,unsigned long flags,void *drvdata);extern int __iommu_sva_unbind_device (struct device *dev,int pasid);extern void __iommu_sva_unbind_dev_all (struct device *dev); +#else /* CONFIG_IOMMU_SVA */ static inline int iommu_sva_device_init (struct device *dev,unsigned long features, -unsigned int max_pasid) +unsigned int max_pasid, +iommu_mm_exit_handler_t mm_exit) {return -ENODEV ; }
Signature · 1 line
2.17.0
Re: [PATCH v2 04/40] iommu/sva: Add a mm_exit callback for device drivers
Hi Jean-Philippe,
Quoted 27 lines · Jean-Philippe Brucker
When an mm exits, devices that were bound to it must stop performing DMA on its PASID. Let device drivers register a callback to be notified on mm exit. Add the callback to the
sva_paramstructure attached to struct device.Signed-off-by Jean-Philippe Brucker <[email protected]>---
v1->v2: useiommu_sva_device_initinstead of a separate function--- drivers/iommu/iommu-sva.c | 11 ++++++++++- include/linux/iommu.h | 9 +++++++-- 2 files changed, 17 insertions(+), 3 deletions(-)drivers/iommu/iommu-sva.c +1
-0@@ -303,6 +303,7 @@ static void io_mm_detach_locked(struct iommu_bond *bond)* @dev: the device * @features: bitmask of features that need to be initialized * @max_pasid: max PASID value supported by the device +* @mm_exit: callback to notify the device driver of an mm exiting * * Users of the bind()/unbind() API must call this function to initialize all * features required for SVA.
drivers/iommu/iommu-sva.c static void io_mm_detach_locked(struct iommu_bond *bond) · line 318
Quoted 5 lines
drivers/iommu/iommu-sva.c +2
-0@@ -313,13 +314,20 @@ static void io_mm_detach_locked(struct iommu_bond *bond)* description. Setting @max_pasid to a non-zero value smaller than this limit * overrides it. * +* If the driver intends to share process address spaces, it should pass a valid +* @mm_exit handler. Otherwise @mm_exit can be NULL.
I don't get case where mm_exit is allowed to be NULL.
Thanks
Eric
After @mm_exit returns, the
Quoted 68 lines
drivers/iommu/iommu-sva.c +7
-1+* device must not issue any more transaction with the PASID given as argument. +* The handler gets an opaque pointer corresponding to the drvdata passed as +* argument of bind(). +* * The device should not be performing any DMA while this function is running, * otherwise the behavior is undefined. * * Return 0 if initialization succeeded, or an error. */ int iommu_sva_device_init (struct device *dev,unsigned long features, -unsigned int max_pasid) +unsigned int max_pasid, +iommu_mm_exit_handler_t mm_exit) {int ret;struct iommu_sva_param *param;@@ -337,6 +345,7 @@ int iommu_sva_device_init(struct device *dev, unsigned long features, param->features = features; param->max_pasid = max_pasid; + param->mm_exit = mm_exit;INIT_LIST_HEAD (¶m->mm_list);/* include/linux/iommu.h +7
-2@@ -61,6 +61,7 @@ struct iommu_fault_event;typedef int (*iommu_fault_handler_t )(struct iommu_domain *,struct device *,unsigned long ,int ,void *);typedef int (*iommu_dev_fault_handler_t )(struct iommu_fault_event *,void *); +typedef int (*iommu_mm_exit_handler_t )(struct device *dev,int pasid,void *);struct iommu_domain_geometry {dma_addr_t aperture_start;/* First address that can be mapped */ @@ -231,6 +232,7 @@ struct iommu_sva_param {unsigned int min_pasid;unsigned int max_pasid;struct list_head mm_list; +iommu_mm_exit_handler_t mm_exit; };/** @@ -980,17 +982,20 @@ static inline int iommu_sva_unbind_device(struct device *dev, int pasid)#ifdef CONFIG_IOMMU_SVA extern int iommu_sva_device_init (struct device *dev,unsigned long features, -unsigned int max_pasid); +unsigned int max_pasid, +iommu_mm_exit_handler_t mm_exit);extern int iommu_sva_device_shutdown (struct device *dev);extern int __iommu_sva_bind_device (struct device *dev,struct mm_struct *mm,int *pasid,unsigned long flags,void *drvdata);extern int __iommu_sva_unbind_device (struct device *dev,int pasid);extern void __iommu_sva_unbind_dev_all (struct device *dev); +#else /* CONFIG_IOMMU_SVA */ static inline int iommu_sva_device_init (struct device *dev,unsigned long features, -unsigned int max_pasid) +unsigned int max_pasid, +iommu_mm_exit_handler_t mm_exit) {return -ENODEV ; }
Re: [PATCH v2 04/40] iommu/sva: Add a mm_exit callback for device drivers
Quoted 3 lines · Auger Eric
Quoted 2 lines
+ * If the driver intends to share process address spaces, it should pass a valid
+ * @mm_exit handler. Otherwise @mm_exit can be NULL.I don't get case where
mm_exitis allowed to be NULL.
Right, this comment is a bit premature. Next version adds a "private PASID" patch to allocate private address spaces per PASID (modifiable with map/unmap). That mode doesn't require mm_exit, and I can move the comment there
Thanks,
Jean
PATCH v2 5/40 iommu/sva: Track mm changes with an MMU notifier
In reply to Jean-Philippe BruckerWhen creating an io_mm structure, register an MMU notifier that informs us when the virtual address space changes and disappears.
Add a new operation to the IOMMU driver, mm_invalidate, called when a range of addresses is unmapped to let the IOMMU driver send ATC invalidations. mm_invalidate cannot sleep.
Adding the notifier complicates io_mm release. In one case device drivers free the io_mm explicitly by calling unbind (or detaching the device from its domain). In the other case the process could crash before unbind, in which case the release notifier has to do all the work.
Allowing the device driver's mm_exit() handler to sleep adds another complication, but it will greatly simplify things for users. For example VFIO can take the IOMMU mutex and remove any trace of io_mm, instead of introducing complex synchronization to delicatly handle this race. But relaxing the user side does force unbind() to sleep and wait for all pending mm_exit() calls to finish.
---v1->v2:
* Unbind() waits for mm_exit to finish
* mm_exit can sleep
--- drivers/iommu/Kconfig | 1 + drivers/iommu/iommu-sva.c | 248 +++++++++++++++++++++++++++++++++++--- include/linux/iommu.h | 10 ++ 3 files changed, 244 insertions(+), 15 deletions(-)
drivers/iommu/Kconfig +1 -0
@@ -77,6 +77,7 @@ config IOMMU_DMA config IOMMU_SVA bool select IOMMU_API + select MMU_NOTIFIER config FSL_PAMU bool "Freescale IOMMU support"
drivers/iommu/iommu-sva.c +233 -15
359 lines not on this page · raw message
include/linux/iommu.h +10 -0
@@ -24,6 +24,7 @@#include <linux/types.h> #include <linux/errno.h> #include <linux/err.h> +#include <linux/mmu_notifier.h> #include <linux/of.h> #include <uapi/linux/iommu.h>
@@ -111,10 +112,15 @@ struct io_mm {unsigned long flags;struct list_head devices;struct kref kref; +#if defined(CONFIG_MMU_NOTIFIER ) +struct mmu_notifier notifier; +#endif struct mm_struct *mm;/* Release callback for this mm */ void (*release)(struct io_mm *io_mm); +/* For postponed release */ +struct rcu_head rcu; };enum iommu_cap {
@@ -249,6 +255,7 @@ struct iommu_sva_param {* @mm_attach: attach io_mm to a device. Install PASID entry if necessary * @mm_detach: detach io_mm from a device. Remove PASID entry and * flush associated TLB entries. +* @mm_invalidate: Invalidate a range of mappings for an mm * @map: map a physically contiguous memory region to an iommu domain * @unmap: unmap a physically contiguous memory region from an iommu domain * @map_sg: map a scatter-gather list of physically contiguous memory chunks
@@ -298,6 +305,9 @@ struct iommu_ops { struct io_mm *io_mm, bool attach_domain);void (*mm_detach)(struct iommu_domain *domain,struct device *dev,struct io_mm *io_mm,bool detach_domain); +void (*mm_invalidate)(struct iommu_domain *domain,struct device *dev, +struct io_mm *io_mm,unsigned long vaddr, +size_t size);int (*map)(struct iommu_domain *domain,unsigned long iova,phys_addr_t paddr,size_t size,int prot); size_t (*unmap)(struct iommu_domain *domain, unsigned long iova,
Signature · 1 line
2.17.0
Re: [PATCH v2 05/40] iommu/sva: Track mm changes with an MMU notifier
Quoted 201 lines · On Fri, 11 May 2018 20:06:06 +0100 Jean-Philippe Brucker…
When creating an
io_mmstructure, register an MMU notifier that informs us when the virtual address space changes and disappears.Add a new operation to the IOMMU driver,
mm_invalidate, called when a range of addresses is unmapped to let the IOMMU driver send ATC invalidations.mm_invalidatecannot sleep.Adding the notifier complicates
io_mmrelease. In one case device drivers free theio_mmexplicitly by calling unbind (or detaching the device from its domain). In the other case the process could crash before unbind, in which case the release notifier has to do all the work.Allowing the device driver's
mm_exit()handler to sleep adds another complication, but it will greatly simplify things for users. For example VFIO can take the IOMMU mutex and remove any trace ofio_mm, instead of introducing complex synchronization to delicatly handle this race. But relaxing the user side does forceunbind()to sleep and wait for all pendingmm_exit()calls to finish.Signed-off-by Jean-Philippe Brucker <[email protected]>---
v1->v2:
*Unbind()waits formm_exitto finish
*mm_exitcan sleep--- drivers/iommu/Kconfig | 1 + drivers/iommu/iommu-sva.c | 248 +++++++++++++++++++++++++++++++++++--- include/linux/iommu.h | 10 ++ 3 files changed, 244 insertions(+), 15 deletions(-)drivers/iommu/Kconfig +1
-0@@ -77,6 +77,7 @@ config IOMMU_DMA config IOMMU_SVA bool select IOMMU_API + select MMU_NOTIFIER config FSL_PAMU bool "Freescale IOMMU support"drivers/iommu/iommu-sva.c +65
-9@@ -7,6 +7,7 @@#include <linux/idr.h> #include <linux/iommu.h> +#include <linux/mmu_notifier.h> #include <linux/sched/mm.h> #include <linux/slab.h> #include <linux/spinlock.h> @@ -106,6 +107,9 @@ struct iommu_bond {struct list_head mm_head;struct list_head dev_head;struct list_head domain_head; +refcount_t refs; +struct wait_queue_head mm_exit_wq; +bool mm_exit_active;void *drvdata; };@@ -124,6 +128,8 @@ static DEFINE_IDR(iommu_pasid_idr);*/ static DEFINE_SPINLOCK (iommu_sva_lock); +static struct mmu_notifier_ops iommu_mmu_notifier; +static struct io_mm *io_mm_alloc (struct iommu_domain *domain,struct device *dev,struct mm_struct *mm,unsigned long flags)@@ -151,6 +157,7 @@ io_mm_alloc(struct iommu_domain *domain, struct device *dev, io_mm->flags = flags; io_mm->mm = mm; + io_mm->notifier.ops = &iommu_mmu_notifier; io_mm->release = domain->ops->mm_free;INIT_LIST_HEAD (&io_mm->devices);@@ -167,8 +174,29 @@ io_mm_alloc(struct iommu_domain *domain, struct device *dev, goto err_free_mm; } -/* TODO: keep track of mm. For the moment, abort. */ - ret = -ENOSYS ; + ret =mmu_notifier_register (&io_mm->notifier, mm); +if (ret) + goto err_free_pasid; + +/* +* Now that the MMU notifier is valid, we can allow users to grab this +* io_mm by setting a valid refcount. Before that it was accessible in +* the IDR but invalid. +* +* The following barrier ensures that users, who obtain the io_mm with +* kref_get_unless_zero, don't read uninitialized fields in the +* structure. +*/ +smp_wmb (); +kref_init (&io_mm->kref); + +return io_mm; + +err_free_pasid: +/* +* Even if the io_mm is accessible from the IDR at this point, kref is +* 0 so no user could get a reference to it. Free it manually. +*/ spin_lock (&iommu_sva_lock);idr_remove (&iommu_pasid_idr, io_mm->pasid);spin_unlock (&iommu_sva_lock);@@ -180,9 +208,13 @@ io_mm_alloc(struct iommu_domain *domain, struct device *dev,return ERR_PTR (ret); } -static void io_mm_free (struct io_mm *io_mm) +static void io_mm_free (struct rcu_head *rcu) { -struct mm_struct *mm = io_mm->mm; +struct io_mm *io_mm; +struct mm_struct *mm; + + io_mm = container_of(rcu, struct io_mm, rcu); + mm = io_mm->mm; io_mm->release (io_mm);mmdrop (mm);@@ -197,7 +229,22 @@ static void io_mm_release(struct kref *kref)idr_remove (&iommu_pasid_idr, io_mm->pasid); -io_mm_free (io_mm); +/* +* If we're being released from mm exit, the notifier callback ->release +* has already been called. Otherwise we don't need ->release, the io_mm +* isn't attached to anything anymore. Hence no_release. +*/ +mmu_notifier_unregister_no_release (&io_mm->notifier, io_mm->mm); + +/* +* We can't free the structure here, because if mm exits during +* unbind(), then ->release might be attempting to grab the io_mm +* concurrently. And in the other case, if ->release is calling +* io_mm_release, then __mmu_notifier_release expects to still have a +* valid mn when returning. So free the structure when it's safe, after +* the RCU grace period elapsed. +*/ +mmu_notifier_call_srcu (&io_mm->rcu, io_mm_free); }/* @@ -206,8 +253,14 @@ static void io_mm_release(struct kref *kref)*/ static int io_mm_get_locked (struct io_mm *io_mm) { -if (io_mm) -return kref_get_unless_zero (&io_mm->kref); +if (io_mm &&kref_get_unless_zero (&io_mm->kref)) { +/* +* kref_get_unless_zero doesn't provide ordering for reads. This +* barrier pairs with the one in io_mm_alloc. +*/ +smp_rmb (); +return 1 ; + }return 0 ; }@@ -233,7 +286,8 @@ static int io_mm_attach(struct iommu_domain *domain, struct device *dev,struct iommu_bond *bond, *tmp;struct iommu_sva_param *param = dev->iommu_param->sva_param; -if (!domain->ops->mm_attach || !domain->ops->mm_detach) +if (!domain->ops->mm_attach || !domain->ops->mm_detach || + !domain->ops->mm_invalidate)return -ENODEV ;if (pasid > param->max_pasid || pasid < param->min_pasid)@@ -247,6 +301,8 @@ static int io_mm_attach(struct iommu_domain *domain, struct device *dev, bond->io_mm = io_mm; bond->dev = dev; bond->drvdata = drvdata; +refcount_set (&bond->refs,1 ); +init_waitqueue_head (&bond->mm_exit_wq);spin_lock (&iommu_sva_lock);/* @@ -275,12 +331,37 @@ static int io_mm_attach(struct iommu_domain *domain, struct device *dev,return 0 ; } -static void io_mm_detach_locked (struct iommu_bond *bond) +static void io_mm_detach_locked (struct iommu_bond *bond,bool wait) {struct iommu_bond *tmp;bool detach_domain = true;struct iommu_domain *domain = bond->domain;
drivers/iommu/iommu-sva.c static int io_mm_attach(struct iommu_domain *domain, struct device *dev, · line 348
Quoted 10 lines
drivers/iommu/iommu-sva.c +9
-0+if (wait) { +bool do_detach = true; +/* +* If we're unbind() then we're deleting the bond no matter +* what. Tell the mm_exit thread that we're cleaning up, and +* wait until it finishes using the bond. +* +* refs is guaranteed to be one or more, otherwise it would +* already have been removed from the list. Check is someone is
Check if someone...
Quoted 257 lines
drivers/iommu/iommu-sva.c +159
-6+* already waiting, in which case we wait but do not free. +*/ +if (refcount_read (&bond->refs) >1 ) + do_detach = false; + +refcount_inc (&bond->refs); +wait_event_lock_irq (bond->mm_exit_wq, !bond->mm_exit_active, + iommu_sva_lock); +if (!do_detach) +return ; + + }else if (!refcount_dec_and_test (&bond->refs)) { +/* unbind() is waiting to free the bond */ +return ; + } +list_for_each_entry (tmp, &domain->mm_list, domain_head) {if (tmp->io_mm == bond->io_mm && tmp->dev != bond->dev) { detach_domain = false;@@ -298,6 +379,129 @@ static void io_mm_detach_locked(struct iommu_bond *bond)kfree (bond); } +static int iommu_signal_mm_exit (struct iommu_bond *bond) +{ +struct device *dev = bond->dev; +struct io_mm *io_mm = bond->io_mm; +struct iommu_sva_param *param = dev->iommu_param->sva_param; + +/* +* We can't hold the device's param_lock. If we did and the device +* driver used a global lock around io_mm, we would risk getting the +* following deadlock: +* +* exit_mm() | Shutdown SVA +* mutex_lock(param->lock) | mutex_lock(glob lock) +* param->mm_exit() | sva_device_shutdown() +* mutex_lock(glob lock) | mutex_lock(param->lock) +* +* Fortunately unbind() waits for us to finish, and sva_device_shutdown +* requires that any bond is removed, so we can safely access mm_exit +* and drvdata without taking any lock. +*/ +if (!param || !param->mm_exit) +return 0 ; + +return param->mm_exit (dev, io_mm->pasid, bond->drvdata); +} + +/* Called when the mm exits. Can race with unbind(). */ +static void iommu_notifier_release (struct mmu_notifier *mn,struct mm_struct *mm) +{ +struct iommu_bond *bond, *next; + struct io_mm *io_mm = container_of(mn, struct io_mm, notifier); + +/* +* If the mm is exiting then devices are still bound to the io_mm. +* A few things need to be done before it is safe to release: +* +* - As the mmu notifier doesn't hold any reference to the io_mm when +* calling ->release(), try to take a reference. +* - Tell the device driver to stop using this PASID. +* - Clear the PASID table and invalidate TLBs. +* - Drop all references to this io_mm by freeing the bonds. +*/ +spin_lock (&iommu_sva_lock); +if (!io_mm_get_locked (io_mm)) { +/* Someone's already taking care of it. */ +spin_unlock (&iommu_sva_lock); +return ; + } + +list_for_each_entry_safe (bond, next, &io_mm->devices, mm_head) { +/* +* Release the lock to let the handler sleep. We need to be +* careful about concurrent modifications to the list and to the +* bond. Tell unbind() not to free the bond until we're done. +*/ + bond->mm_exit_active = true; +spin_unlock (&iommu_sva_lock); + +if (iommu_signal_mm_exit (bond)) +dev_WARN (bond->dev,"possible leak of PASID %u" , + io_mm->pasid); + +spin_lock (&iommu_sva_lock); + next =list_next_entry (bond, mm_head); + +/* If someone is waiting, let them delete the bond now */ + bond->mm_exit_active = false; +wake_up_all (&bond->mm_exit_wq); + +/* Otherwise, do it ourselves */ +io_mm_detach_locked (bond, false); + } +spin_unlock (&iommu_sva_lock); + +/* +* We're now reasonably certain that no more fault is being handled for +* this io_mm, since we just flushed them all out of the fault queue. +* Release the last reference to free the io_mm. +*/ +io_mm_put (io_mm); +} + +static void iommu_notifier_invalidate_range (struct mmu_notifier *mn, +struct mm_struct *mm, +unsigned long start, +unsigned long end) +{ +struct iommu_bond *bond; + struct io_mm *io_mm = container_of(mn, struct io_mm, notifier); + +spin_lock (&iommu_sva_lock); +list_for_each_entry (bond, &io_mm->devices, mm_head) { +struct iommu_domain *domain = bond->domain; + + domain->ops->mm_invalidate (domain, bond->dev, io_mm, start, + end - start); + } +spin_unlock (&iommu_sva_lock); +} + +static int iommu_notifier_clear_flush_young (struct mmu_notifier *mn, +struct mm_struct *mm, +unsigned long start, +unsigned long end) +{ +iommu_notifier_invalidate_range (mn, mm, start, end); +return 0 ; +} + +static void iommu_notifier_change_pte (struct mmu_notifier *mn, +struct mm_struct *mm, +unsigned long address,pte_t pte) +{ +iommu_notifier_invalidate_range (mn, mm, address, address +PAGE_SIZE ); +} + +static struct mmu_notifier_ops iommu_mmu_notifier = { + .release = iommu_notifier_release, + .clear_flush_young = iommu_notifier_clear_flush_young, + .change_pte = iommu_notifier_change_pte, + .invalidate_range = iommu_notifier_invalidate_range, +}; +/** * iommu_sva_device_init() - Initialize Shared Virtual Addressing for a device * @dev: the device @@ -320,6 +524,12 @@ static void io_mm_detach_locked(struct iommu_bond *bond)* The handler gets an opaque pointer corresponding to the drvdata passed as * argument of bind(). * +* The @mm_exit handler is allowed to sleep. Be careful about the locks taken in +* @mm_exit, because they might lead to deadlocks if they are also held when +* dropping references to the mm. Consider the following call chain: +* mutex_lock(A); mmput(mm) -> exit_mm() -> @mm_exit() -> mutex_lock(A) +* Using mmput_async() prevents this scenario. +* * The device should not be performing any DMA while this function is running, * otherwise the behavior is undefined. * @@ -484,15 +694,16 @@ int __iommu_sva_unbind_device(struct device *dev, int pasid)if (!param ||WARN_ON (!domain))return -EINVAL ; -spin_lock (&iommu_sva_lock); +/* spin_lock_irq matches the one in wait_event_lock_irq */ +spin_lock_irq (&iommu_sva_lock);list_for_each_entry (bond, ¶m->mm_list, dev_head) {if (bond->io_mm->pasid == pasid) { -io_mm_detach_locked (bond); +io_mm_detach_locked (bond, true); ret =0 ;break ; } } -spin_unlock (&iommu_sva_lock); +spin_unlock_irq (&iommu_sva_lock);return ret; }@@ -503,18 +714,25 @@ EXPORT_SYMBOL_GPL(__iommu_sva_unbind_device);* @dev: the device * * When detaching @device from a domain, IOMMU drivers should use this helper. +* This function may sleep while waiting for bonds to be released. */ void __iommu_sva_unbind_dev_all (struct device *dev) {struct iommu_sva_param *param;struct iommu_bond *bond, *next; +/* +* io_mm_detach_locked might wait, so we shouldn't call it with the dev +* param lock held. It's fine to read sva_param outside the lock because +* it can only be freed by iommu_sva_device_shutdown when there are no +* more bonds in the list. +*/ param = dev->iommu_param->sva_param;if (param) { -spin_lock (&iommu_sva_lock); +spin_lock_irq (&iommu_sva_lock);list_for_each_entry_safe (bond, next, ¶m->mm_list, dev_head) -io_mm_detach_locked (bond); -spin_unlock (&iommu_sva_lock); +io_mm_detach_locked (bond, true); +spin_unlock_irq (&iommu_sva_lock); } }EXPORT_SYMBOL_GPL (__iommu_sva_unbind_dev_all);include/linux/iommu.h +10
-0@@ -24,6 +24,7 @@#include <linux/types.h> #include <linux/errno.h> #include <linux/err.h> +#include <linux/mmu_notifier.h> #include <linux/of.h> #include <uapi/linux/iommu.h> @@ -111,10 +112,15 @@ struct io_mm {unsigned long flags;struct list_head devices;struct kref kref; +#if defined(CONFIG_MMU_NOTIFIER ) +struct mmu_notifier notifier; +#endif struct mm_struct *mm;/* Release callback for this mm */ void (*release)(struct io_mm *io_mm); +/* For postponed release */ +struct rcu_head rcu; };enum iommu_cap {@@ -249,6 +255,7 @@ struct iommu_sva_param {* @mm_attach: attach io_mm to a device. Install PASID entry if necessary * @mm_detach: detach io_mm from a device. Remove PASID entry and * flush associated TLB entries. +* @mm_invalidate: Invalidate a range of mappings for an mm * @map: map a physically contiguous memory region to an iommu domain * @unmap: unmap a physically contiguous memory region from an iommu domain * @map_sg: map a scatter-gather list of physically contiguous memory chunks @@ -298,6 +305,9 @@ struct iommu_ops { struct io_mm *io_mm, bool attach_domain);void (*mm_detach)(struct iommu_domain *domain,struct device *dev,struct io_mm *io_mm,bool detach_domain); +void (*mm_invalidate)(struct iommu_domain *domain,struct device *dev, +struct io_mm *io_mm,unsigned long vaddr, +size_t size);int (*map)(struct iommu_domain *domain,unsigned long iova,phys_addr_t paddr,size_t size,int prot); size_t (*unmap)(struct iommu_domain *domain, unsigned long iova,
Re: [PATCH v2 05/40] iommu/sva: Track mm changes with an MMU notifier
Quoted 3 lines · Jonathan Cameron
drivers/iommu/iommu-sva.c static int io_mm_attach(struct iommu_domain *domain, struct device *dev, · line 348
Quoted 1 line
drivers/iommu/iommu-sva.c +1
-0+* already have been removed from the list. Check is someone is Check if someone...
Ok
Thanks,
Jean
PATCH v2 6/40 iommu/sva: Search mm by PASID
In reply to Jean-Philippe BruckerThe fault handler will need to find an mm given its PASID. This is the reason we have an IDR for storing address spaces, so hook it up.
--- drivers/iommu/iommu-sva.c | 26 ++++++++++++++++++++++++++ include/linux/iommu.h | 6 ++++++ 2 files changed, 32 insertions(+)
drivers/iommu/iommu-sva.c +26 -0
@@ -736,3 +736,29 @@ void __iommu_sva_unbind_dev_all(struct device *dev) } }EXPORT_SYMBOL_GPL (__iommu_sva_unbind_dev_all); + +/** +* iommu_sva_find() - Find mm associated to the given PASID +* @pasid: Process Address Space ID assigned to the mm +* +* Returns the mm corresponding to this PASID, or NULL if not found. A reference +* to the mm is taken, and must be released with mmput(). +*/ +struct mm_struct *iommu_sva_find (int pasid) +{ +struct io_mm *io_mm; +struct mm_struct *mm =NULL ; + +spin_lock (&iommu_sva_lock); + io_mm =idr_find (&iommu_pasid_idr, pasid); +if (io_mm &&io_mm_get_locked (io_mm)) { +if (mmget_not_zero (io_mm->mm)) + mm = io_mm->mm; + +io_mm_put_locked (io_mm); + } +spin_unlock (&iommu_sva_lock); + +return mm; +} +EXPORT_SYMBOL_GPL (iommu_sva_find);
include/linux/iommu.h +6 -0
@@ -1001,6 +1001,7 @@ extern int __iommu_sva_bind_device(struct device *dev, struct mm_struct *mm,extern int __iommu_sva_unbind_device (struct device *dev,int pasid);extern void __iommu_sva_unbind_dev_all (struct device *dev); +extern struct mm_struct *iommu_sva_find (int pasid);#else /* CONFIG_IOMMU_SVA */ static inline int iommu_sva_device_init(struct device *dev, unsigned long features,
@@ -1030,6 +1031,11 @@ static inline int __iommu_sva_unbind_device(struct device *dev, int pasid)static inline void __iommu_sva_unbind_dev_all (struct device *dev) { } + +static inline struct mm_struct *iommu_sva_find (int pasid) +{ +return NULL ; +}#endif /* CONFIG_IOMMU_SVA */ #endif /* __LINUX_IOMMU_H */
Signature · 1 line
2.17.0
PATCH v2 7/40 iommu: Add a page fault handler
In reply to Jean-Philippe BruckerSome systems allow devices to handle I/O Page Faults in the core mm. For example systems implementing the PCI PRI extension or Arm SMMU stall model. Infrastructure for reporting these recoverable page faults was recently added to the IOMMU core for SVA virtualisation. Add a page fault handler for host SVA.
IOMMU driver can now instantiate several fault workqueues and link them to IOPF-capable devices. Drivers can choose between a single global workqueue, one per IOMMU device, one per low-level fault queue, one per domain, etc.
When it receives a fault event, supposedly in an IRQ handler, the IOMMU driver reports the fault using iommu_report_device_fault(), which calls the registered handler. The page fault handler then calls the mm fault handler, and reports either success or failure with iommu_page_response(). When the handler succeeded, the IOMMU retries the access.
The iopf_param pointer could be embedded into iommu_fault_param. But putting iopf_param into the iommu_param structure allows us not to care about ordering between calls to iopf_queue_add_device() and iommu_register_device_fault_handler().
---v1->v2: multiple queues registered by IOMMU driver
--- drivers/iommu/Kconfig | 4 + drivers/iommu/Makefile | 1 + drivers/iommu/io-pgfault.c | 363 +++++++++++++++++++++++++++++++++++++ include/linux/iommu.h | 58 ++++++ 4 files changed, 426 insertions(+) create mode 100644 drivers/iommu/io-pgfault.c
drivers/iommu/Kconfig +4 -0
@@ -79,6 +79,10 @@ config IOMMU_SVA select IOMMU_API select MMU_NOTIFIER +config IOMMU_PAGE_FAULT + bool + select IOMMU_API + config FSL_PAMU bool "Freescale IOMMU support" depends on PCI
drivers/iommu/Makefile +1 -0
@@ -4,6 +4,7 @@ obj-$(CONFIG_IOMMU_API) += iommu-traces.o obj-$(CONFIG_IOMMU_API) += iommu-sysfs.o obj-$(CONFIG_IOMMU_DMA) += dma-iommu.o obj-$(CONFIG_IOMMU_SVA) += iommu-sva.o +obj-$(CONFIG_IOMMU_PAGE_FAULT) += io-pgfault.o obj-$(CONFIG_IOMMU_IO_PGTABLE) += io-pgtable.o obj-$(CONFIG_IOMMU_IO_PGTABLE_ARMV7S) += io-pgtable-arm-v7s.o obj-$(CONFIG_IOMMU_IO_PGTABLE_LPAE) += io-pgtable-arm.o
drivers/iommu/io-pgfault.c new file +363 -0
363 lines not on this page · raw message
include/linux/iommu.h +58 -0
85 lines not on this page · raw message
Signature · 1 line
2.17.0
Re: [PATCH v2 07/40] iommu: Add a page fault handler
Quoted 17 lines · On Fri, 11 May 2018 20:06:08 +0100 Jean-Philippe Brucker…
Some systems allow devices to handle I/O Page Faults in the core mm. For example systems implementing the PCI PRI extension or Arm SMMU stall model. Infrastructure for reporting these recoverable page faults was recently added to the IOMMU core for SVA virtualisation. Add a page fault handler for host SVA.
IOMMU driver can now instantiate several fault workqueues and link them to IOPF-capable devices. Drivers can choose between a single global workqueue, one per IOMMU device, one per low-level fault queue, one per domain, etc.
When it receives a fault event, supposedly in an IRQ handler, the IOMMU driver reports the fault using
iommu_report_device_fault(), which calls the registered handler. The page fault handler then calls the mm fault handler, and reports either success or failure withiommu_page_response(). When the handler succeeded, the IOMMU retries the access.
Quoted 6 lines
The
iopf_parampointer could be embedded intoiommu_fault_param. But puttingiopf_paraminto theiommu_paramstructure allows us not to care about ordering between calls toiopf_queue_add_device()andiommu_register_device_fault_handler().Signed-off-by Jean-Philippe Brucker <[email protected]>
Hi Jean-Phillipe,
One question below on how we would end up with partial faults left when doing the queue remove. Code looks fine, but I'm not seeing how that would happen without buggy hardware... + we presumably have to rely on the hardware timing out on that request or it's dead anyway...
Jonathan
Quoted 326 lines
---
v1->v2: multiple queues registered by IOMMU driver--- drivers/iommu/Kconfig | 4 + drivers/iommu/Makefile | 1 + drivers/iommu/io-pgfault.c | 363 +++++++++++++++++++++++++++++++++++++ include/linux/iommu.h | 58 ++++++ 4 files changed, 426 insertions(+) create mode 100644 drivers/iommu/io-pgfault.cdrivers/iommu/Kconfig +4
-0@@ -79,6 +79,10 @@ config IOMMU_SVA select IOMMU_API select MMU_NOTIFIER +config IOMMU_PAGE_FAULT + bool + select IOMMU_API + config FSL_PAMU bool "Freescale IOMMU support" depends on PCIdrivers/iommu/Makefile +1
-0@@ -4,6 +4,7 @@ obj-$(CONFIG_IOMMU_API) += iommu-traces.o obj-$(CONFIG_IOMMU_API) += iommu-sysfs.o obj-$(CONFIG_IOMMU_DMA) += dma-iommu.o obj-$(CONFIG_IOMMU_SVA) += iommu-sva.o +obj-$(CONFIG_IOMMU_PAGE_FAULT) += io-pgfault.o obj-$(CONFIG_IOMMU_IO_PGTABLE) += io-pgtable.o obj-$(CONFIG_IOMMU_IO_PGTABLE_ARMV7S) += io-pgtable-arm-v7s.o obj-$(CONFIG_IOMMU_IO_PGTABLE_LPAE) += io-pgtable-arm.odrivers/iommu/io-pgfault.c new file +282
-0@@ -0,0 +1,363 @@ +// SPDX-License-Identifier: GPL-2.0 +/* +* Handle device page faults +* +* Copyright (C) 2018 ARM Ltd. +*/ + +#include <linux/iommu.h> +#include <linux/list.h> +#include <linux/slab.h> +#include <linux/workqueue.h> + +/** +* struct iopf_queue - IO Page Fault queue +* @wq: the fault workqueue +* @flush: low-level flush callback +* @flush_arg: flush() argument +* @refs: references to this structure taken by producers +*/ +struct iopf_queue { +struct workqueue_struct *wq; +iopf_queue_flush_t flush; +void *flush_arg; +refcount_t refs; +}; + +/** +* struct iopf_device_param - IO Page Fault data attached to a device +* @queue: IOPF queue +* @partial: faults that are part of a Page Request Group for which the last +* request hasn't been submitted yet. +*/ +struct iopf_device_param { +struct iopf_queue *queue; +struct list_head partial; +}; + +struct iopf_context { +struct device *dev; +struct iommu_fault_event evt; +struct list_head head; +}; + +struct iopf_group { +struct iopf_context last_fault; +struct list_head faults; +struct work_struct work; +}; + +static int iopf_complete (struct device *dev,struct iommu_fault_event *evt, +enum page_response_code status) +{ +struct page_response_msg resp = { + .addr = evt->addr, + .pasid = evt->pasid, + .pasid_present = evt->pasid_valid, + .page_req_group_id = evt->page_req_group_id, + .private_data = evt->iommu_private, + .resp_code = status, + }; + +return iommu_page_response (dev, &resp); +} + +static enum page_response_code +iopf_handle_single (struct iopf_context *fault) +{ +/* TODO */ +return -ENODEV ; +} + +static void iopf_handle_group (struct work_struct *work) +{ +struct iopf_group *group; +struct iopf_context *fault, *next; +enum page_response_code status =IOMMU_PAGE_RESP_SUCCESS ; + + group = container_of(work, struct iopf_group, work); + +list_for_each_entry_safe (fault, next, &group->faults, head) { +struct iommu_fault_event *evt = &fault->evt; +/* +* Errors are sticky: don't handle subsequent faults in the +* group if there is an error. +*/ +if (status ==IOMMU_PAGE_RESP_SUCCESS ) + status =iopf_handle_single (fault); + +if (!evt->last_req) +kfree (fault); + } + +iopf_complete (group->last_fault.dev, &group->last_fault.evt, status); +kfree (group); +} + +/** +* iommu_queue_iopf - IO Page Fault handler +* @evt: fault event +* @cookie: struct device, passed to iommu_register_device_fault_handler. +* +* Add a fault to the device workqueue, to be handled by mm. +*/ +int iommu_queue_iopf (struct iommu_fault_event *evt,void *cookie) +{ +struct iopf_group *group; +struct iopf_context *fault, *next; +struct iopf_device_param *iopf_param; + +struct device *dev = cookie; +struct iommu_param *param = dev->iommu_param; + +if (WARN_ON (!mutex_is_locked (¶m->lock))) +return -EINVAL ; + +if (evt->type !=IOMMU_FAULT_PAGE_REQ ) +/* Not a recoverable page fault */ +return IOMMU_PAGE_RESP_CONTINUE ; + +/* +* As long as we're holding param->lock, the queue can't be unlinked +* from the device and therefore cannot disappear. +*/ + iopf_param = param->iopf_param; +if (!iopf_param) +return -ENODEV ; + +if (!evt->last_req) { + fault =kzalloc (sizeof (*fault),GFP_KERNEL ); +if (!fault) +return -ENOMEM ; + + fault->evt = *evt; + fault->dev = dev; + +/* Non-last request of a group. Postpone until the last one */ +list_add (&fault->head, &iopf_param->partial); + +return IOMMU_PAGE_RESP_HANDLED ; + } + + group =kzalloc (sizeof (*group),GFP_KERNEL ); +if (!group) +return -ENOMEM ; + + group->last_fault.evt = *evt; + group->last_fault.dev = dev; +INIT_LIST_HEAD (&group->faults); +list_add (&group->last_fault.head, &group->faults); +INIT_WORK (&group->work, iopf_handle_group); + +/* See if we have partial faults for this group */ +list_for_each_entry_safe (fault, next, &iopf_param->partial, head) { +if (fault->evt.page_req_group_id == evt->page_req_group_id) +/* Insert *before* the last fault */ +list_move (&fault->head, &group->faults); + } + +queue_work (iopf_param->queue->wq, &group->work); + +/* Postpone the fault completion */ +return IOMMU_PAGE_RESP_HANDLED ; +} +EXPORT_SYMBOL_GPL (iommu_queue_iopf); + +/** +* iopf_queue_flush_dev - Ensure that all queued faults have been processed +* @dev: the endpoint whose faults need to be flushed. +* +* Users must call this function when releasing a PASID, to ensure that all +* pending faults for this PASID have been handled, and won't hit the address +* space of the next process that uses this PASID. +* +* Return 0 on success. +*/ +int iopf_queue_flush_dev (struct device *dev) +{ +int ret =0 ; +struct iopf_queue *queue; +struct iommu_param *param = dev->iommu_param; + +if (!param) +return -ENODEV ; + +/* +* It is incredibly easy to find ourselves in a deadlock situation if +* we're not careful, because we're taking the opposite path as +* iommu_queue_iopf: +* +* iopf_queue_flush_dev() | PRI queue handler +* lock(mutex) | iommu_queue_iopf() +* queue->flush() | lock(mutex) +* wait PRI queue empty | +* +* So we can't hold the device param lock while flushing. We don't have +* to, because the queue or the device won't disappear until all flush +* are finished. +*/ +mutex_lock (¶m->lock); +if (param->iopf_param) { + queue = param->iopf_param->queue; + }else { + ret = -ENODEV ; + } +mutex_unlock (¶m->lock); +if (ret) +return ret; + + queue->flush (queue->flush_arg, dev); + +/* +* No need to clear the partial list. All PRGs containing the PASID that +* needs to be decommissioned are whole (the device driver made sure of +* it before this function was called). They have been submitted to the +* queue by the above flush(). +*/ +flush_workqueue (queue->wq); + +return 0 ; +} +EXPORT_SYMBOL_GPL (iopf_queue_flush_dev); + +/** +* iopf_queue_add_device - Add producer to the fault queue +* @queue: IOPF queue +* @dev: device to add +*/ +int iopf_queue_add_device (struct iopf_queue *queue,struct device *dev) +{ +int ret = -EINVAL ; +struct iopf_device_param *iopf_param; +struct iommu_param *param = dev->iommu_param; + +if (!param) +return -ENODEV ; + + iopf_param =kzalloc (sizeof (*iopf_param),GFP_KERNEL ); +if (!iopf_param) +return -ENOMEM ; + +INIT_LIST_HEAD (&iopf_param->partial); + iopf_param->queue = queue; + +mutex_lock (¶m->lock); +if (!param->iopf_param) { +refcount_inc (&queue->refs); + param->iopf_param = iopf_param; + ret =0 ; + } +mutex_unlock (¶m->lock); + +if (ret) +kfree (iopf_param); + +return ret; +} +EXPORT_SYMBOL_GPL (iopf_queue_add_device); + +/** +* iopf_queue_remove_device - Remove producer from fault queue +* @dev: device to remove +* +* Caller makes sure that no more fault is reported for this device, and no more +* flush is scheduled for this device. +* +* Note: safe to call unconditionally on a cleanup path, even if the device +* isn't registered to any IOPF queue. +* +* Return 0 if the device was attached to the IOPF queue +*/ +int iopf_queue_remove_device (struct device *dev) +{ +struct iopf_context *fault, *next; +struct iopf_device_param *iopf_param; +struct iommu_param *param = dev->iommu_param; + +if (!param) +return -EINVAL ; + +mutex_lock (¶m->lock); + iopf_param = param->iopf_param; +if (iopf_param) {
drivers/iommu/io-pgfault.c line 292
Quoted 10 lines
drivers/iommu/io-pgfault.c new file +10
-0+refcount_dec (&iopf_param->queue->refs); + param->iopf_param =NULL ; + } +mutex_unlock (¶m->lock); +if (!iopf_param) +return -EINVAL ; + +list_for_each_entry_safe (fault, next, &iopf_param->partial, head) +kfree (fault); +
Why would we end up here with partials still in the list? Buggy hardware?
Quoted 53 lines
drivers/iommu/io-pgfault.c new file +53
-0+/* +* No more flush is scheduled, and the caller removed all bonds from +* this device. unbind() waited until any concurrent mm_exit() finished, +* therefore there is no flush() running anymore and we can free the +* param. +*/ +kfree (iopf_param); + +return 0 ; +} +EXPORT_SYMBOL_GPL (iopf_queue_remove_device); + +/** +* iopf_queue_alloc - Allocate and initialize a fault queue +* @name: a unique string identifying the queue (for workqueue) +* @flush: a callback that flushes the low-level queue +* @cookie: driver-private data passed to the flush callback +* +* The callback is called before the workqueue is flushed. The IOMMU driver must +* commit all faults that are pending in its low-level queues at the time of the +* call, into the IOPF queue (with iommu_report_device_fault). The callback +* takes a device pointer as argument, hinting what endpoint is causing the +* flush. When the device is NULL, all faults should be committed. +*/ +struct iopf_queue * +iopf_queue_alloc (const char *name,iopf_queue_flush_t flush,void *cookie) +{ +struct iopf_queue *queue; + + queue =kzalloc (sizeof (*queue),GFP_KERNEL ); +if (!queue) +return NULL ; + +/* +* The WQ is unordered because the low-level handler enqueues faults by +* group. PRI requests within a group have to be ordered, but once +* that's dealt with, the high-level function can handle groups out of +* order. +*/ + queue->wq =alloc_workqueue ("iopf_queue/%s" ,WQ_UNBOUND ,0 , name); +if (!queue->wq) { +kfree (queue); +return NULL ; + } + + queue->flush = flush; + queue->flush_arg = cookie; +refcount_set (&queue->refs,1 ); + +return queue; +} +EXPORT_SYMBOL_GPL (iopf_queue_alloc); +
drivers/iommu/io-pgfault.c line 355
Quoted 10 lines
drivers/iommu/io-pgfault.c new file +10
-0+/** +* iopf_queue_free - Free IOPF queue +* @queue: queue to free +* +* Counterpart to iopf_queue_alloc(). Caller must make sure that all producers +* have been removed. +*/ +void iopf_queue_free (struct iopf_queue *queue) +{ +
Nitpick : Blank line here doesn't add anything.
Quoted 101 lines
drivers/iommu/io-pgfault.c new file +8
-0+/* Caller should have removed all producers first */ +if (WARN_ON (!refcount_dec_and_test (&queue->refs))) +return ; + +destroy_workqueue (queue->wq); +kfree (queue); +} +EXPORT_SYMBOL_GPL (iopf_queue_free);include/linux/iommu.h +58
-0@@ -461,11 +461,20 @@ struct iommu_fault_param {void *data; }; +/** +* iopf_queue_flush_t - Flush low-level page fault queue +* +* Report all faults currently pending in the low-level page fault queue +*/ +struct iopf_queue ; +typedef int (*iopf_queue_flush_t )(void *cookie,struct device *dev); +/** * struct iommu_param - collection of per-device IOMMU data * * @fault_param: IOMMU detected device fault reporting data * @sva_param: SVA parameters +* @iopf_param: I/O Page Fault queue and data * * TODO: migrate other per device data pointers under iommu_dev_data, e.g. * struct iommu_group *iommu_group; @@ -475,6 +484,7 @@ struct iommu_param {struct mutex lock;struct iommu_fault_param *fault_param;struct iommu_sva_param *sva_param; +struct iopf_device_param *iopf_param; };int iommu_device_register (struct iommu_device *iommu);@@ -874,6 +884,12 @@ static inline int iommu_report_device_fault(struct device *dev, struct iommu_faureturn 0 ; } +static inline int iommu_page_response (struct device *dev, +struct page_response_msg *msg) +{ +return -ENODEV ; +} +static inline int iommu_group_id (struct iommu_group *group) {return -ENODEV ;@@ -1038,4 +1054,46 @@ static inline struct mm_struct *iommu_sva_find(int pasid) }#endif /* CONFIG_IOMMU_SVA */ +#ifdef CONFIG_IOMMU_PAGE_FAULT +extern int iommu_queue_iopf (struct iommu_fault_event *evt,void *cookie); + +extern int iopf_queue_add_device (struct iopf_queue *queue,struct device *dev); +extern int iopf_queue_remove_device (struct device *dev); +extern int iopf_queue_flush_dev (struct device *dev); +extern struct iopf_queue * +iopf_queue_alloc (const char *name,iopf_queue_flush_t flush,void *cookie); +extern void iopf_queue_free (struct iopf_queue *queue); +#else /* CONFIG_IOMMU_PAGE_FAULT */ +static inline int iommu_queue_iopf (struct iommu_fault_event *evt,void *cookie) +{ +return -ENODEV ; +} + +static inline int iopf_queue_add_device (struct iopf_queue *queue, +struct device *dev) +{ +return -ENODEV ; +} + +static inline int iopf_queue_remove_device (struct device *dev) +{ +return -ENODEV ; +} + +static inline int iopf_queue_flush_dev (struct device *dev) +{ +return -ENODEV ; +} + +static inline struct iopf_queue * +iopf_queue_alloc (const char *name,iopf_queue_flush_t flush,void *cookie) +{ +return NULL ; +} + +static inline void iopf_queue_free (struct iopf_queue *queue) +{ +} +#endif /* CONFIG_IOMMU_PAGE_FAULT */ +#endif /* __LINUX_IOMMU_H */
Re: [PATCH v2 07/40] iommu: Add a page fault handler
Quoted 62 lines · Jonathan Cameron
Quoted 17 lines · On Fri, 11 May 2018 20:06:08 +0100 Jean-Philippe Brucker…
Some systems allow devices to handle I/O Page Faults in the core mm. For example systems implementing the PCI PRI extension or Arm SMMU stall model. Infrastructure for reporting these recoverable page faults was recently added to the IOMMU core for SVA virtualisation. Add a page fault handler for host SVA.
IOMMU driver can now instantiate several fault workqueues and link them to IOPF-capable devices. Drivers can choose between a single global workqueue, one per IOMMU device, one per low-level fault queue, one per domain, etc.
When it receives a fault event, supposedly in an IRQ handler, the IOMMU driver reports the fault using
iommu_report_device_fault(), which calls the registered handler. The page fault handler then calls the mm fault handler, and reports either success or failure withiommu_page_response(). When the handler succeeded, the IOMMU retries the access.Quoted 6 lines
The
iopf_parampointer could be embedded intoiommu_fault_param. But puttingiopf_paraminto theiommu_paramstructure allows us not to care about ordering between calls toiopf_queue_add_device()andiommu_register_device_fault_handler().Signed-off-by Jean-Philippe Brucker <[email protected]>Hi Jean-Phillipe,
One question below on how we would end up with partial faults left when doing the queue remove. Code looks fine, but I'm not seeing how that would happen without buggy hardware... + we presumably have to rely on the hardware timing out on that request or it's dead anyway...
Quoted 28 lines
drivers/iommu/io-pgfault.c new file +28
-0+/** +* iopf_queue_remove_device - Remove producer from fault queue +* @dev: device to remove +* +* Caller makes sure that no more fault is reported for this device, and no more +* flush is scheduled for this device. +* +* Note: safe to call unconditionally on a cleanup path, even if the device +* isn't registered to any IOPF queue. +* +* Return 0 if the device was attached to the IOPF queue +*/ +int iopf_queue_remove_device (struct device *dev) +{ +struct iopf_context *fault, *next; +struct iopf_device_param *iopf_param; +struct iommu_param *param = dev->iommu_param; + +if (!param) +return -EINVAL ; + +mutex_lock (¶m->lock); + iopf_param = param->iopf_param; +if (iopf_param) { +refcount_dec (&iopf_param->queue->refs); + param->iopf_param =NULL ; + } +mutex_unlock (¶m->lock);
Quoted 8 lines
drivers/iommu/io-pgfault.c line 292
Quoted 6 lines
drivers/iommu/io-pgfault.c new file +6
-0+if (!iopf_param) +return -EINVAL ; + +list_for_each_entry_safe (fault, next, &iopf_param->partial, head) +kfree (fault); +Why would we end up here with partials still in the list? Buggy hardware?
Buggy hardware is one possibility. There also is the nasty case of PRI queue overflow. If the PRI queue is full, then the SMMU discards new Page Requests from the device. It may discard a 'last' PR of a group that is already in iopf_param->partial. This group will never be freed until this cleanup.
The spec dismisses PRIq overflows because the OS is supposed to allocate PRI credits to devices such that they can't send more than the PRI queue size. This isn't possible in Linux because we don't know exactly how many PRI-capable devices will share a queue (the upper limit is 2**32, and devices may be hotplugged well after we allocated the PRI queue). So PRIq overflow is possible.
Ideally when detecting a PRIq overflow we need to immediately remove all partial faults of all devices sharing this queue. Since I can't easily test that case (my device doesn't do PRGs) and it's complicated code, I left it as TODO in patch 39, and this is our only chance to free orphaned page requests.
Quoted 5 lines
drivers/iommu/io-pgfault.c line 355
Quoted 3 lines
drivers/iommu/io-pgfault.c new file +3
-0+void iopf_queue_free (struct iopf_queue *queue) +{ +Nitpick : Blank line here doesn't add anything.
Ok
Thanks,
Jean
Re: [PATCH v2 07/40] iommu: Add a page fault handler
In reply to Jean-Philippe BruckerQuoted 108 lines · On Fri, 11 May 2018 20:06:08 +0100 Jean-Philippe Brucker…
Some systems allow devices to handle I/O Page Faults in the core mm. For example systems implementing the PCI PRI extension or Arm SMMU stall model. Infrastructure for reporting these recoverable page faults was recently added to the IOMMU core for SVA virtualisation. Add a page fault handler for host SVA.
IOMMU driver can now instantiate several fault workqueues and link them to IOPF-capable devices. Drivers can choose between a single global workqueue, one per IOMMU device, one per low-level fault queue, one per domain, etc.
When it receives a fault event, supposedly in an IRQ handler, the IOMMU driver reports the fault using
iommu_report_device_fault(), which calls the registered handler. The page fault handler then calls the mm fault handler, and reports either success or failure withiommu_page_response(). When the handler succeeded, the IOMMU retries the access.The
iopf_parampointer could be embedded intoiommu_fault_param. But puttingiopf_paraminto theiommu_paramstructure allows us not to care about ordering between calls toiopf_queue_add_device()andiommu_register_device_fault_handler().Signed-off-by Jean-Philippe Brucker <[email protected]>---
v1->v2: multiple queues registered by IOMMU driver--- drivers/iommu/Kconfig | 4 + drivers/iommu/Makefile | 1 + drivers/iommu/io-pgfault.c | 363 +++++++++++++++++++++++++++++++++++++ include/linux/iommu.h | 58 ++++++ 4 files changed, 426 insertions(+) create mode 100644 drivers/iommu/io-pgfault.cdrivers/iommu/Kconfig +4
-0@@ -79,6 +79,10 @@ config IOMMU_SVA select IOMMU_API select MMU_NOTIFIER +config IOMMU_PAGE_FAULT + bool + select IOMMU_API + config FSL_PAMU bool "Freescale IOMMU support" depends on PCIdrivers/iommu/Makefile +1
-0@@ -4,6 +4,7 @@ obj-$(CONFIG_IOMMU_API) += iommu-traces.o obj-$(CONFIG_IOMMU_API) += iommu-sysfs.o obj-$(CONFIG_IOMMU_DMA) += dma-iommu.o obj-$(CONFIG_IOMMU_SVA) += iommu-sva.o +obj-$(CONFIG_IOMMU_PAGE_FAULT) += io-pgfault.o obj-$(CONFIG_IOMMU_IO_PGTABLE) += io-pgtable.o obj-$(CONFIG_IOMMU_IO_PGTABLE_ARMV7S) += io-pgtable-arm-v7s.o obj-$(CONFIG_IOMMU_IO_PGTABLE_LPAE) += io-pgtable-arm.odrivers/iommu/io-pgfault.c new file +30
-0@@ -0,0 +1,363 @@ +// SPDX-License-Identifier: GPL-2.0 +/* +* Handle device page faults +* +* Copyright (C) 2018 ARM Ltd. +*/ + +#include <linux/iommu.h> +#include <linux/list.h> +#include <linux/slab.h> +#include <linux/workqueue.h> + +/** +* struct iopf_queue - IO Page Fault queue +* @wq: the fault workqueue +* @flush: low-level flush callback +* @flush_arg: flush() argument +* @refs: references to this structure taken by producers +*/ +struct iopf_queue { +struct workqueue_struct *wq; +iopf_queue_flush_t flush; +void *flush_arg; +refcount_t refs; +}; + +/** +* struct iopf_device_param - IO Page Fault data attached to a device +* @queue: IOPF queue +* @partial: faults that are part of a Page Request Group for which the last
+ * request hasn't been submitted yet.
+ */drivers/iommu/io-pgfault.c new file +7
-0+struct iopf_device_param { +struct iopf_queue *queue; +struct list_head partial; +}; + +struct iopf_context { +struct device *dev;
drivers/iommu/io-pgfault.c line 49
Quoted 10 lines
drivers/iommu/io-pgfault.c new file +10
-0+struct iommu_fault_event evt; +struct list_head head; +}; + +struct iopf_group { +struct iopf_context last_fault; +struct list_head faults; +struct work_struct work; +}; +
All the page requests in the group should belong to the same device, perhaps struct device tracking should be in iopf_group instead of iopf_context?
Quoted 36 lines
+static int iopf_complete(struct device *dev, struct
iommu_fault_event*evt,drivers/iommu/io-pgfault.c new file +33
-0+ enum page_response_code status) +{ +struct page_response_msg resp = { + .addr = evt->addr, + .pasid = evt->pasid, + .pasid_present = evt->pasid_valid, + .page_req_group_id = evt->page_req_group_id, + .private_data = evt->iommu_private, + .resp_code = status, + }; + +return iommu_page_response (dev, &resp); +} + +static enum page_response_code +iopf_handle_single (struct iopf_context *fault) +{ +/* TODO */ +return -ENODEV ; +} + +static void iopf_handle_group (struct work_struct *work) +{ +struct iopf_group *group; +struct iopf_context *fault, *next; +enum page_response_code status =IOMMU_PAGE_RESP_SUCCESS ; + + group = container_of(work, struct iopf_group, work); + +list_for_each_entry_safe (fault, next, &group->faults, head) { +struct iommu_fault_event *evt = &fault->evt; +/* +* Errors are sticky: don't handle subsequent faults in the
drivers/iommu/io-pgfault.c
Quoted 2 lines
drivers/iommu/io-pgfault.c new file +2
-0+* group if there is an error. +*/
There might be performance benefit for certain device to continue in spite of error in that the ATS retry may have less fault. Perhaps a policy decision for later enhancement.
Quoted 74 lines
drivers/iommu/io-pgfault.c new file +8
-0+if (status ==IOMMU_PAGE_RESP_SUCCESS ) + status =iopf_handle_single (fault); + +if (!evt->last_req) +kfree (fault); + } + + iopf_complete(group->last_fault.dev, &group->last_fault.evt,status);
drivers/iommu/io-pgfault.c new file +7
-0+kfree (group); +} + +/** +* iommu_queue_iopf - IO Page Fault handler +* @evt: fault event +* @cookie: struct device, passed to
iommu_register_device_fault_handler.
+ *
+ * Add a fault to the device workqueue, to be handled by mm.
+ */drivers/iommu/io-pgfault.c new file +18
-0+int iommu_queue_iopf (struct iommu_fault_event *evt,void *cookie) +{ +struct iopf_group *group; +struct iopf_context *fault, *next; +struct iopf_device_param *iopf_param; + +struct device *dev = cookie; +struct iommu_param *param = dev->iommu_param; + +if (WARN_ON (!mutex_is_locked (¶m->lock))) +return -EINVAL ; + +if (evt->type !=IOMMU_FAULT_PAGE_REQ ) +/* Not a recoverable page fault */ +return IOMMU_PAGE_RESP_CONTINUE ; + +/* +* As long as we're holding param->lock, the queue can't be unlinked
drivers/iommu/io-pgfault.c new file +15
-0+* from the device and therefore cannot disappear. +*/ + iopf_param = param->iopf_param; +if (!iopf_param) +return -ENODEV ; + +if (!evt->last_req) { + fault =kzalloc (sizeof (*fault),GFP_KERNEL ); +if (!fault) +return -ENOMEM ; + + fault->evt = *evt; + fault->dev = dev; + +/* Non-last request of a group. Postpone until the last one */
drivers/iommu/io-pgfault.c new file +17
-0+list_add (&fault->head, &iopf_param->partial); + +return IOMMU_PAGE_RESP_HANDLED ; + } + + group =kzalloc (sizeof (*group),GFP_KERNEL ); +if (!group) +return -ENOMEM ; + + group->last_fault.evt = *evt; + group->last_fault.dev = dev; +INIT_LIST_HEAD (&group->faults); +list_add (&group->last_fault.head, &group->faults); +INIT_WORK (&group->work, iopf_handle_group); + +/* See if we have partial faults for this group */ + list_for_each_entry_safe(fault, next, &iopf_param->partial,head) {
drivers/iommu/io-pgfault.c new file +1
-0+ if (fault->evt.page_req_group_id ==
Quoted 1 line
evt->page_req_group_id)
If all 9 bit group index are used, there can be lots of PRGs. For future enhancement, maybe we can have per group partial list ready to go when LPIG comes in? I will be less searching.
drivers/iommu/io-pgfault.c line 158
Quoted 4 lines
drivers/iommu/io-pgfault.c new file +4
-0+/* Insert *before* the last fault */ +list_move (&fault->head, &group->faults); + } +
If you already sorted the group list with last fault at the end, why do you need a separate entry to track the last fault?
Quoted 66 lines
drivers/iommu/io-pgfault.c new file +9
-0+queue_work (iopf_param->queue->wq, &group->work); + +/* Postpone the fault completion */ +return IOMMU_PAGE_RESP_HANDLED ; +} +EXPORT_SYMBOL_GPL (iommu_queue_iopf); + +/** +* iopf_queue_flush_dev - Ensure that all queued faults have been processed
+ * @dev: the endpoint whose faults need to be flushed.
+ *
+ * Users must call this function when releasing a PASID, to ensure that all
+ * pending faults for this PASID have been handled, and won't hit the address
+ * space of the next process that uses this PASID.
+ *
+ * Return 0 on success.
+ */drivers/iommu/io-pgfault.c new file +11
-0+int iopf_queue_flush_dev (struct device *dev) +{ +int ret =0 ; +struct iopf_queue *queue; +struct iommu_param *param = dev->iommu_param; + +if (!param) +return -ENODEV ; + +/* +* It is incredibly easy to find ourselves in a deadlock situation if
drivers/iommu/io-pgfault.c new file +1
-0+* we're not careful, because we're taking the opposite path as
drivers/iommu/io-pgfault.c new file +8
-0+* iommu_queue_iopf: +* +* iopf_queue_flush_dev() | PRI queue handler +* lock(mutex) | iommu_queue_iopf() +* queue->flush() | lock(mutex) +* wait PRI queue empty | +* +* So we can't hold the device param lock while flushing. We don't have
drivers/iommu/io-pgfault.c new file +1
-0+* to, because the queue or the device won't disappear until all flush
drivers/iommu/io-pgfault.c new file +16
-0+* are finished. +*/ +mutex_lock (¶m->lock); +if (param->iopf_param) { + queue = param->iopf_param->queue; + }else { + ret = -ENODEV ; + } +mutex_unlock (¶m->lock); +if (ret) +return ret; + + queue->flush (queue->flush_arg, dev); + +/* +* No need to clear the partial list. All PRGs containing the PASID that
drivers/iommu/io-pgfault.c new file +1
-0+* needs to be decommissioned are whole (the device driver made sure of
drivers/iommu/io-pgfault.c new file +1
-0+* it before this function was called). They have been submitted to the
drivers/iommu/io-pgfault.c
Quoted 2 lines
drivers/iommu/io-pgfault.c new file +2
-0+* queue by the above flush(). +*/
So you are saying device driver need to make sure LPIG PRQ is submitted in the flush call above such that partial list is cleared? what if there are device failures where device needs to reset (either whole function level or PASID level), should there still be a need to clear the partial list for all associated PASID/group?
Quoted 257 lines
drivers/iommu/io-pgfault.c new file +12
-0+flush_workqueue (queue->wq); + +return 0 ; +} +EXPORT_SYMBOL_GPL (iopf_queue_flush_dev); + +/** +* iopf_queue_add_device - Add producer to the fault queue +* @queue: IOPF queue +* @dev: device to add +*/ +int iopf_queue_add_device(struct iopf_queue *queue, struct device*dev) +{
drivers/iommu/io-pgfault.c new file +34
-0+int ret = -EINVAL ; +struct iopf_device_param *iopf_param; +struct iommu_param *param = dev->iommu_param; + +if (!param) +return -ENODEV ; + + iopf_param =kzalloc (sizeof (*iopf_param),GFP_KERNEL ); +if (!iopf_param) +return -ENOMEM ; + +INIT_LIST_HEAD (&iopf_param->partial); + iopf_param->queue = queue; + +mutex_lock (¶m->lock); +if (!param->iopf_param) { +refcount_inc (&queue->refs); + param->iopf_param = iopf_param; + ret =0 ; + } +mutex_unlock (¶m->lock); + +if (ret) +kfree (iopf_param); + +return ret; +} +EXPORT_SYMBOL_GPL (iopf_queue_add_device); + +/** +* iopf_queue_remove_device - Remove producer from fault queue +* @dev: device to remove +* +* Caller makes sure that no more fault is reported for this device, and no more
+ * flush is scheduled for this device.
+ *
+ * Note: safe to call unconditionally on a cleanup path, even if the device
+ * isn't registered to any IOPF queue.
+ *
+ * Return 0 if the device was attached to the IOPF queue
+ */drivers/iommu/io-pgfault.c new file +20
-0+int iopf_queue_remove_device (struct device *dev) +{ +struct iopf_context *fault, *next; +struct iopf_device_param *iopf_param; +struct iommu_param *param = dev->iommu_param; + +if (!param) +return -EINVAL ; + +mutex_lock (¶m->lock); + iopf_param = param->iopf_param; +if (iopf_param) { +refcount_dec (&iopf_param->queue->refs); + param->iopf_param =NULL ; + } +mutex_unlock (¶m->lock); +if (!iopf_param) +return -EINVAL ; + + list_for_each_entry_safe(fault, next, &iopf_param->partial,head)
drivers/iommu/io-pgfault.c new file +4
-0+kfree (fault); + +/* +* No more flush is scheduled, and the caller removed all bonds from
drivers/iommu/io-pgfault.c new file +1
-0+* this device. unbind() waited until any concurrent
mm_exit()finished,drivers/iommu/io-pgfault.c new file +1
-0+* therefore there is no flush() running anymore and we can free the
drivers/iommu/io-pgfault.c new file +15
-0+* param. +*/ +kfree (iopf_param); + +return 0 ; +} +EXPORT_SYMBOL_GPL (iopf_queue_remove_device); + +/** +* iopf_queue_alloc - Allocate and initialize a fault queue +* @name: a unique string identifying the queue (for workqueue) +* @flush: a callback that flushes the low-level queue +* @cookie: driver-private data passed to the flush callback +* +* The callback is called before the workqueue is flushed. The IOMMU driver must
+ * commit all faults that are pending in its low-level queues at the time of the
+ * call, into the IOPF queue (withiommu_report_device_fault). The callback
+ * takes a device pointer as argument, hinting what endpoint is causing the
+ * flush. When the device is NULL, all faults should be committed.
+ */drivers/iommu/io-pgfault.c new file +2
-0+struct iopf_queue * +iopf_queue_alloc(const char *name, iopf_queue_flush_t flush, void*cookie) +{
drivers/iommu/io-pgfault.c new file +8
-0+struct iopf_queue *queue; + + queue =kzalloc (sizeof (*queue),GFP_KERNEL ); +if (!queue) +return NULL ; + +/* +* The WQ is unordered because the low-level handler enqueues faults by
drivers/iommu/io-pgfault.c new file +1
-0+* group. PRI requests within a group have to be ordered, but once
drivers/iommu/io-pgfault.c new file +1
-0+* that's dealt with, the high-level function can handle groups out of
drivers/iommu/io-pgfault.c new file +3
-0+* order. +*/ + queue->wq = alloc_workqueue("iopf_queue/%s", WQ_UNBOUND, 0,name);
drivers/iommu/io-pgfault.c new file +18
-0+if (!queue->wq) { +kfree (queue); +return NULL ; + } + + queue->flush = flush; + queue->flush_arg = cookie; +refcount_set (&queue->refs,1 ); + +return queue; +} +EXPORT_SYMBOL_GPL (iopf_queue_alloc); + +/** +* iopf_queue_free - Free IOPF queue +* @queue: queue to free +* +* Counterpart to iopf_queue_alloc(). Caller must make sure that all producers
+ * have been removed.
+ */drivers/iommu/io-pgfault.c new file +11
-0+void iopf_queue_free (struct iopf_queue *queue) +{ + +/* Caller should have removed all producers first */ +if (WARN_ON (!refcount_dec_and_test (&queue->refs))) +return ; + +destroy_workqueue (queue->wq); +kfree (queue); +} +EXPORT_SYMBOL_GPL (iopf_queue_free);include/linux/iommu.h +4
-0@@ -461,11 +461,20 @@ struct iommu_fault_param {void *data; }; +/** +* iopf_queue_flush_t - Flush low-level page fault queue +* +* Report all faults currently pending in the low-level page fault queue
+ */include/linux/iommu.h +4
-0+struct iopf_queue ; +typedef int (*iopf_queue_flush_t )(void *cookie,struct device *dev); +/** * struct iommu_param - collection of per-device IOMMU data * * @fault_param: IOMMU detected device fault reporting data * @sva_param: SVA parameters +* @iopf_param: I/O Page Fault queue and data * * TODO: migrate other per device data pointers under
iommu_dev_data, e.g.
* structiommu_group*iommu_group;include/linux/iommu.h +1
-0@@ -475,6 +484,7 @@ struct iommu_param {struct mutex lock;struct iommu_fault_param *fault_param;struct iommu_sva_param *sva_param; +struct iopf_device_param *iopf_param; };int iommu_device_register (struct iommu_device *iommu);@@ -874,6 +884,12 @@ static inline intiommu_report_device_fault(struct device *dev, struct
iommu_faureturn 0; }include/linux/iommu.h +9
-0+static inline int iommu_page_response (struct device *dev, +struct page_response_msg *msg) +{ +return -ENODEV ; +} +static inline int iommu_group_id (struct iommu_group *group) {return -ENODEV ;@@ -1038,4 +1054,46 @@ static inline struct mm_struct *iommu_sva_find(int pasid) }#endif /* CONFIG_IOMMU_SVA */ +#ifdef CONFIG_IOMMU_PAGE_FAULT +extern int iommu_queue_iopf (struct iommu_fault_event *evt,void *cookie); + +extern int iopf_queue_add_device(struct iopf_queue *queue, structdevice *dev); +extern int
iopf_queue_remove_device(struct device *dev); +extern intiopf_queue_flush_dev(struct device *dev);include/linux/iommu.h +2
-0+extern struct iopf_queue * +iopf_queue_alloc(const char *name, iopf_queue_flush_t flush, void*cookie); +extern void
iopf_queue_free(struct iopf_queue *queue);include/linux/iommu.h +2
-0+#else /* CONFIG_IOMMU_PAGE_FAULT */ +static inline int iommu_queue_iopf(struct iommu_fault_event *evt,void *cookie) +{
include/linux/iommu.h +21
-0+return -ENODEV ; +} + +static inline int iopf_queue_add_device (struct iopf_queue *queue, +struct device *dev) +{ +return -ENODEV ; +} + +static inline int iopf_queue_remove_device (struct device *dev) +{ +return -ENODEV ; +} + +static inline int iopf_queue_flush_dev (struct device *dev) +{ +return -ENODEV ; +} + +static inline struct iopf_queue * +iopf_queue_alloc(const char *name, iopf_queue_flush_t flush, void*cookie) +{
include/linux/iommu.h +8
-0+return NULL ; +} + +static inline void iopf_queue_free (struct iopf_queue *queue) +{ +} +#endif /* CONFIG_IOMMU_PAGE_FAULT */ +#endif /* __LINUX_IOMMU_H */
Re: [PATCH v2 07/40] iommu: Add a page fault handler
Quoted 7 lines · Jacob Pan
Quoted 7 lines
drivers/iommu/io-pgfault.c new file +7
-0+struct iopf_context { +struct device *dev; +struct iommu_fault_event evt; +struct list_head head; +}; + +struct iopf_group {
Quoted 8 lines
drivers/iommu/io-pgfault.c line 49
Quoted 5 lines
drivers/iommu/io-pgfault.c new file +5
-0+struct iopf_context last_fault; +struct list_head faults; +struct work_struct work; +}; +All the page requests in the group should belong to the same device, perhaps struct device tracking should be in
iopf_groupinstead ofiopf_context?
Right, this is a leftover from when we kept a global list of partial faults. Since the list is now per-device, I can move the dev pointer (I think I should also rename iopf_context to iopf_fault, if that's all right)
Quoted 33 lines
Quoted 33 lines
+static int iopf_complete(struct device *dev, struct
iommu_fault_event*evt,drivers/iommu/io-pgfault.c new file +31
-0+ enum page_response_code status) +{ +struct page_response_msg resp = { + .addr = evt->addr, + .pasid = evt->pasid, + .pasid_present = evt->pasid_valid, + .page_req_group_id = evt->page_req_group_id, + .private_data = evt->iommu_private, + .resp_code = status, + }; + +return iommu_page_response (dev, &resp); +} + +static enum page_response_code +iopf_handle_single (struct iopf_context *fault) +{ +/* TODO */ +return -ENODEV ; +} + +static void iopf_handle_group (struct work_struct *work) +{ +struct iopf_group *group; +struct iopf_context *fault, *next; +enum page_response_code status =IOMMU_PAGE_RESP_SUCCESS ; + + group = container_of(work, struct iopf_group, work); + +list_for_each_entry_safe (fault, next, &group->faults, head) { +struct iommu_fault_event *evt = &fault->evt;
Quoted 8 lines
Review
Quoted 5 lines
(quoted diff) +2
-0+ /* + * Errors are sticky: don't handle subsequent faultsin the
(quoted diff) +2
-0+ * group if there is an error. + */There might be performance benefit for certain device to continue in spite of error in that the ATS retry may have less fault. Perhaps a policy decision for later enhancement.
Yes I think we should leave it to future work. My current reasoning is that subsequent requests are more likely to fail as well and reporting the error is more urgent, but we need real workloads to confirm this.
Quoted 70 lines
Quoted 70 lines
drivers/iommu/io-pgfault.c new file +8
-0+if (status ==IOMMU_PAGE_RESP_SUCCESS ) + status =iopf_handle_single (fault); + +if (!evt->last_req) +kfree (fault); + } + + iopf_complete(group->last_fault.dev, &group->last_fault.evt,status);
drivers/iommu/io-pgfault.c new file +7
-0+kfree (group); +} + +/** +* iommu_queue_iopf - IO Page Fault handler +* @evt: fault event +* @cookie: struct device, passed to
iommu_register_device_fault_handler.
+ *
+ * Add a fault to the device workqueue, to be handled by mm.
+ */drivers/iommu/io-pgfault.c new file +18
-0+int iommu_queue_iopf (struct iommu_fault_event *evt,void *cookie) +{ +struct iopf_group *group; +struct iopf_context *fault, *next; +struct iopf_device_param *iopf_param; + +struct device *dev = cookie; +struct iommu_param *param = dev->iommu_param; + +if (WARN_ON (!mutex_is_locked (¶m->lock))) +return -EINVAL ; + +if (evt->type !=IOMMU_FAULT_PAGE_REQ ) +/* Not a recoverable page fault */ +return IOMMU_PAGE_RESP_CONTINUE ; + +/* +* As long as we're holding param->lock, the queue can't be unlinked
drivers/iommu/io-pgfault.c new file +15
-0+* from the device and therefore cannot disappear. +*/ + iopf_param = param->iopf_param; +if (!iopf_param) +return -ENODEV ; + +if (!evt->last_req) { + fault =kzalloc (sizeof (*fault),GFP_KERNEL ); +if (!fault) +return -ENOMEM ; + + fault->evt = *evt; + fault->dev = dev; + +/* Non-last request of a group. Postpone until the last one */
drivers/iommu/io-pgfault.c new file +15
-0+list_add (&fault->head, &iopf_param->partial); + +return IOMMU_PAGE_RESP_HANDLED ; + } + + group =kzalloc (sizeof (*group),GFP_KERNEL ); +if (!group) +return -ENOMEM ; + + group->last_fault.evt = *evt; + group->last_fault.dev = dev; +INIT_LIST_HEAD (&group->faults); +list_add (&group->last_fault.head, &group->faults); +INIT_WORK (&group->work, iopf_handle_group); +
Quoted 8 lines
Quoted 5 lines
(quoted diff) +2
-0+ /* See if we have partial faults for this group */ + list_for_each_entry_safe(fault, next, &iopf_param->partial,head) {
(quoted diff) +1
-0+ if (fault->evt.page_req_group_id ==
evt->page_req_group_id)If all 9 bit group index are used, there can be lots of PRGs. For future enhancement, maybe we can have per group partial list ready to go when LPIG comes in? I will be less searching.
Yes, allocating the PRG from the start could be more efficient. I think it's slightly more complicated code so I'd rather see performance numbers before implementing it
Quoted 6 lines
drivers/iommu/io-pgfault.c line 158
Quoted 4 lines
drivers/iommu/io-pgfault.c new file +4
-0+/* Insert *before* the last fault */ +list_move (&fault->head, &group->faults); + } +If you already sorted the group list with last fault at the end, why do you need a separate entry to track the last fault?
Not sure I understand your question, sorry. Do you mean why the iopf_group.last_fault? Just to avoid one more kzalloc.
Quoted 6 lines
Quoted 6 lines
drivers/iommu/io-pgfault.c new file +5
-0+ + queue->flush (queue->flush_arg, dev); + +/* +* No need to clear the partial list. All PRGs containing the PASID that
Quoted 8 lines
Review
Quoted 6 lines
(quoted diff) +1
-0+ * needs to be decommissioned are whole (the device drivermade sure of
(quoted diff) +1
-0+ * it before this function was called). They have beensubmitted to the
(quoted diff) +2
-0+ * queue by the above flush(). + */So you are saying device driver need to make sure LPIG PRQ is submitted in the flush call above such that partial list is cleared?
Not exactly, it's the IOMMU driver that makes sure all LPIG in its queues are submitted by the above flush call. In more details the flow is:
* Either device driver calls unbind()/sva_device_shutdown(), or the process exits.
* If the device driver called, then it already told the device to stop using the PASID. Otherwise we use the mm_exit() callback to tell the device driver to stop using the PASID.
* In either case, when receiving a stop request from the driver, the device sends the LPIGs to the IOMMU queue.
* Then, the flush call above ensures that the IOMMU reports the LPIG with iommu_report_device_fault.
* While submitting all LPIGs for this PASID to the work queue, ipof_queue_fault also picked up all partial faults, so the partial list is clean.
Maybe I should improve this comment?
Quoted 4 lines
what if
there are device failures where device needs to reset (either whole function level or PASID level), should there still be a need to clear the partial list for all associated PASID/group?
I guess the simplest way out, when resetting the device, is for the device driver to call iommu_sva_device_shutdown(). Both the IOMMU driver's sva_device_shutdown() and remove_device() ops should call iopf_queue_remove_device(), which clears the partial list.
Thanks,
Jean
Re: [PATCH v2 07/40] iommu: Add a page fault handler
Quoted 160 lines · On Mon, 21 May 2018 15:49:40 +0100 Jean-Philippe Brucker…
Quoted 7 lines · Jacob Pan
Quoted 7 lines
drivers/iommu/io-pgfault.c new file +7
-0+struct iopf_context { +struct device *dev; +struct iommu_fault_event evt; +struct list_head head; +}; + +struct iopf_group {Quoted 8 lines
drivers/iommu/io-pgfault.c line 49
Quoted 5 lines
drivers/iommu/io-pgfault.c new file +5
-0+struct iopf_context last_fault; +struct list_head faults; +struct work_struct work; +}; +All the page requests in the group should belong to the same device, perhaps struct device tracking should be in
iopf_groupinstead ofiopf_context?Right, this is a leftover from when we kept a global list of partial faults. Since the list is now per-device, I can move the dev pointer (I think I should also rename
iopf_contexttoiopf_fault, if that's all right)Quoted 35 lines
Quoted 35 lines
+static int iopf_complete(struct device *dev, struct
iommu_fault_event*evt,drivers/iommu/io-pgfault.c new file +7
-0+ enum page_response_code status) +{ + struct page_response_msg resp = { + .addr = evt->addr, + .pasid = evt->pasid, + .pasid_present = evt->pasid_valid, + .page_req_group_id =
evt->page_req_group_id,drivers/iommu/io-pgfault.c new file +23
-0+ .private_data = evt->iommu_private, + .resp_code = status, + }; + +return iommu_page_response (dev, &resp); +} + +static enum page_response_code +iopf_handle_single (struct iopf_context *fault) +{ +/* TODO */ +return -ENODEV ; +} + +static void iopf_handle_group (struct work_struct *work) +{ +struct iopf_group *group; +struct iopf_context *fault, *next; +enum page_response_code status =IOMMU_PAGE_RESP_SUCCESS ; + + group = container_of(work, struct iopf_group, work); + + list_for_each_entry_safe(fault, next, &group->faults,head) {
drivers/iommu/io-pgfault.c new file +1
-0+struct iommu_fault_event *evt = &fault->evt;Quoted 8 lines
Review
Quoted 5 lines
(quoted diff) +2
-0+ /* + * Errors are sticky: don't handle subsequentfaults in the
(quoted diff) +2
-0+ * group if there is an error. + */There might be performance benefit for certain device to continue in spite of error in that the ATS retry may have less fault. Perhaps a policy decision for later enhancement.
Yes I think we should leave it to future work. My current reasoning is that subsequent requests are more likely to fail as well and reporting the error is more urgent, but we need real workloads to confirm this.
Quoted 70 lines
Quoted 70 lines
drivers/iommu/io-pgfault.c new file +8
-0+if (status ==IOMMU_PAGE_RESP_SUCCESS ) + status =iopf_handle_single (fault); + +if (!evt->last_req) +kfree (fault); + } + + iopf_complete(group->last_fault.dev,
&group->last_fault.evt, status);drivers/iommu/io-pgfault.c new file +7
-0+kfree (group); +} + +/** +* iommu_queue_iopf - IO Page Fault handler +* @evt: fault event +* @cookie: struct device, passed to
iommu_register_device_fault_handler.
+ *
+ * Add a fault to the device workqueue, to be handled by mm.
+ */drivers/iommu/io-pgfault.c new file +18
-0+int iommu_queue_iopf (struct iommu_fault_event *evt,void *cookie) +{ +struct iopf_group *group; +struct iopf_context *fault, *next; +struct iopf_device_param *iopf_param; + +struct device *dev = cookie; +struct iommu_param *param = dev->iommu_param; + +if (WARN_ON (!mutex_is_locked (¶m->lock))) +return -EINVAL ; + +if (evt->type !=IOMMU_FAULT_PAGE_REQ ) +/* Not a recoverable page fault */ +return IOMMU_PAGE_RESP_CONTINUE ; + +/* +* As long as we're holding param->lock, the queue can't be unlinked
drivers/iommu/io-pgfault.c new file +15
-0+* from the device and therefore cannot disappear. +*/ + iopf_param = param->iopf_param; +if (!iopf_param) +return -ENODEV ; + +if (!evt->last_req) { + fault =kzalloc (sizeof (*fault),GFP_KERNEL ); +if (!fault) +return -ENOMEM ; + + fault->evt = *evt; + fault->dev = dev; + +/* Non-last request of a group. Postpone until the last one */
drivers/iommu/io-pgfault.c new file +15
-0+list_add (&fault->head, &iopf_param->partial); + +return IOMMU_PAGE_RESP_HANDLED ; + } + + group =kzalloc (sizeof (*group),GFP_KERNEL ); +if (!group) +return -ENOMEM ; + + group->last_fault.evt = *evt; + group->last_fault.dev = dev; +INIT_LIST_HEAD (&group->faults); +list_add (&group->last_fault.head, &group->faults); +INIT_WORK (&group->work, iopf_handle_group); +Quoted 8 lines
Quoted 5 lines
(quoted diff) +2
-0+ /* See if we have partial faults for this group */ + list_for_each_entry_safe(fault, next,
&iopf_param->partial, head) {(quoted diff) +1
-0+ if (fault->evt.page_req_group_id ==
evt->page_req_group_id)If all 9 bit group index are used, there can be lots of PRGs. For future enhancement, maybe we can have per group partial list ready to go when LPIG comes in? I will be less searching.
Yes, allocating the PRG from the start could be more efficient. I think it's slightly more complicated code so I'd rather see performance numbers before implementing it
Quoted 6 lines
drivers/iommu/io-pgfault.c line 158
Quoted 4 lines
drivers/iommu/io-pgfault.c new file +4
-0+/* Insert *before* the last fault */ +list_move (&fault->head, &group->faults); + } +If you already sorted the group list with last fault at the end, why do you need a separate entry to track the last fault?
Quoted 3 lines
Not sure I understand your question, sorry. Do you mean why the iopf_group.last_fault? Just to avoid one more kzalloc.
kind of :) what i thought was that why can't the last_fault naturally be the last entry in your group fault list? then there is no need for special treatment in terms of allocation of the last fault. just my preference.
Quoted 33 lines
Quoted 7 lines
Quoted 7 lines
drivers/iommu/io-pgfault.c new file +5
-0+ + queue->flush (queue->flush_arg, dev); + +/* +* No need to clear the partial list. All PRGs containing the PASID that
(quoted diff) +1
-0+ * needs to be decommissioned are whole (the device driverQuoted 8 lines
Review
Quoted 5 lines
made sure of
(quoted diff) +1
-0+ * it before this function was called). They have beensubmitted to the
(quoted diff) +2
-0+ * queue by the above flush(). + */So you are saying device driver need to make sure LPIG PRQ is submitted in the flush call above such that partial list is cleared?
Not exactly, it's the IOMMU driver that makes sure all LPIG in its queues are submitted by the above flush call. In more details the flow is:
* Either device driver calls unbind()/sva_device_shutdown(), or the process exits.
* If the device driver called, then it already told the device to stop using the PASID. Otherwise we use themm_exit()callback to tell the device driver to stop using the PASID.
* In either case, when receiving a stop request from the driver, the device sends the LPIGs to the IOMMU queue.
* Then, the flush call above ensures that the IOMMU reports the LPIG withiommu_report_device_fault.
* While submitting all LPIGs for this PASID to the work queue,ipof_queue_faultalso picked up all partial faults, so the partial list is clean.
Quoted 2 lines
Maybe I should improve this comment?
thanks for explaining. LPIG submission is done by device asynchronously w.r.t. driver stopping/decommission PASID. so if we were to use this flow on vt-d, which does not stall page request queue, then we should use the iommu model specific flush() callback to ensure LPIG is received? There could be queue full condition and retry. I am just trying to understand how and where we can make sure LPIG is received and all groups are whole.
Quoted 10 lines
Quoted 4 lines
what if
there are device failures where device needs to reset (either whole function level or PASID level), should there still be a need to clear the partial list for all associated PASID/group?I guess the simplest way out, when resetting the device, is for the device driver to call
iommu_sva_device_shutdown(). Both the IOMMU driver'ssva_device_shutdown()andremove_device()ops should calliopf_queue_remove_device(), which clears the partial list.
yes. I think that should work for functional reset.
Quoted 2 lines
Thanks,
Jean
[Jacob Pan]
Re: [PATCH v2 07/40] iommu: Add a page fault handler
Quoted 7 lines · Jacob Pan
Quoted 7 lines
Quoted 6 lines
drivers/iommu/io-pgfault.c line 158
Quoted 4 lines
drivers/iommu/io-pgfault.c new file +4
-0+/* Insert *before* the last fault */ +list_move (&fault->head, &group->faults); + } +If you already sorted the group list with last fault at the end, why do you need a separate entry to track the last fault?
Quoted 7 lines
Quoted 3 lines
Not sure I understand your question, sorry. Do you mean why the iopf_group.last_fault? Just to avoid one more kzalloc.
kind of :) what i thought was that why can't the
last_faultnaturally be the last entry in your group fault list? then there is no need for special treatment in terms of allocation of the last fault. just my preference.
But we need a kzalloc for the last fault anyway, so I thought I'd just piggy-back on the group allocation. And if I don't add the last fault at the end of group->faults, then it's iopf_handle_group that requires special treatment. I'm still not sure I understood your question though, could you send me a patch that does it?
Quoted 33 lines
Quoted 33 lines
Quoted 7 lines
Quoted 7 lines
drivers/iommu/io-pgfault.c new file +5
-0+ + queue->flush (queue->flush_arg, dev); + +/* +* No need to clear the partial list. All PRGs containing the PASID that
(quoted diff) +1
-0+ * needs to be decommissioned are whole (the device driverQuoted 8 lines
Review
Quoted 5 lines
made sure of
(quoted diff) +1
-0+ * it before this function was called). They have beensubmitted to the
(quoted diff) +2
-0+ * queue by the above flush(). + */So you are saying device driver need to make sure LPIG PRQ is submitted in the flush call above such that partial list is cleared?
Not exactly, it's the IOMMU driver that makes sure all LPIG in its queues are submitted by the above flush call. In more details the flow is:
* Either device driver calls unbind()/sva_device_shutdown(), or the process exits.
* If the device driver called, then it already told the device to stop using the PASID. Otherwise we use themm_exit()callback to tell the device driver to stop using the PASID.
* In either case, when receiving a stop request from the driver, the device sends the LPIGs to the IOMMU queue.
* Then, the flush call above ensures that the IOMMU reports the LPIG withiommu_report_device_fault.
* While submitting all LPIGs for this PASID to the work queue,ipof_queue_faultalso picked up all partial faults, so the partial list is clean.
Quoted 4 lines
Quoted 2 lines
Maybe I should improve this comment?
thanks for explaining. LPIG submission is done by device asynchronously w.r.t. driver stopping/decommission PASID.
Hmm, it should really be synchronous, otherwise there is no way to know when the PASID can be decommissioned. We need a guarantee such as the one in 6.20.1 of the PCIe spec, "Managing PASID TLP Prefix Usage":
"When the stop request mechanism indicates completion, the Function has:
* Completed all Non-Posted Requests associated with this PASID.
* Flushed to the host all Posted Requests addressing host memory in all TCs that were used by the PASID."
That's in combination with "The function shall [...] finish transmitting any multi-page Page Request Messages for this PASID (i.e. send the Page Request Message with the L bit Set)." from the ATS spec.
(If I remember correctly a PRI Page Request is a Posted Request.) Only after this stop request completes can the driver call unbind(), or return from exit_mm(). Then we know that if there was a LPIG for that PASID, it is in the IOMMU's PRI queue (or already completed) once we call flush().
Quoted 6 lines
so if we were to use this
flow on vt-d, which does not stall page request queue, then we should use the iommu model specificflush()callback to ensure LPIG is received? There could be queue full condition and retry. I am just trying to understand how and where we can make sure LPIG is received and all groups are whole.
For SMMU in patch 30, the flush() callback waits until the PRI queue is empty or, when the PRI thread is running in parallel, until the thread has done a full circle (handled as many faults as the queue size). It's really unpleasant to implement because the flush() callback takes a lock to inspect the hardware state, but I don't think we have a choice.
Thanks,
Jean
Re: [PATCH v2 07/40] iommu: Add a page fault handler
Quoted 16 lines · On Thu, 24 May 2018 12:44:38 +0100 Jean-Philippe Brucker…
Quoted 7 lines · Jacob Pan
Quoted 7 lines
Quoted 6 lines
drivers/iommu/io-pgfault.c line 158
Quoted 4 lines
drivers/iommu/io-pgfault.c new file +4
-0+/* Insert *before* the last fault */ +list_move (&fault->head, &group->faults); + } +If you already sorted the group list with last fault at the end, why do you need a separate entry to track the last fault?
Quoted 7 lines
Quoted 3 lines
Not sure I understand your question, sorry. Do you mean why the iopf_group.last_fault? Just to avoid one more kzalloc.
kind of :) what i thought was that why can't the
last_faultnaturally be the last entry in your group fault list? then there is no need for special treatment in terms of allocation of the last fault. just my preference.
Quoted 31 lines
But we need a kzalloc for the last fault anyway, so I thought I'd just piggy-back on the group allocation. And if I don't add the last fault at the end of
group->faults, then it'siopf_handle_groupthat requires special treatment. I'm still not sure I understood your question though, could you send me a patch that does it?Quoted 25 lines
Quoted 25 lines
Quoted 7 lines
Quoted 7 lines
drivers/iommu/io-pgfault.c new file +5
-0+ + queue->flush (queue->flush_arg, dev); + +/* +* No need to clear the partial list. All PRGs containing the PASID that
(quoted diff) +1
-0+ * needs to be decommissioned are whole (the deviceQuoted 8 lines
Review
Quoted 5 lines
driver made sure of
(quoted diff) +1
-0+ * it before this function was called). They have beensubmitted to the
(quoted diff) +2
-0+ * queue by the above flush(). + */So you are saying device driver need to make sure LPIG PRQ is submitted in the flush call above such that partial list is cleared?
Not exactly, it's the IOMMU driver that makes sure all LPIG in its queues are submitted by the above flush call. In more details the flow is:
* Either device driver calls unbind()/sva_device_shutdown(), or the process exits.
* If the device driver called, then it already told the device to stop using the PASID. Otherwise we use themm_exit()callback to tell the device driver to stop using the PASID.
Sorry I still need more clarification. For the PASID termination initiated by vfio unbind, I don't see device driver given a chance to stop PASID. Seems just call __iommu_sva_unbind_device() which already assume device stopped issuing DMA with the PASID.
So it is the vfio unbind caller responsible for doing driver callback to stop DMA on a given PASID?
Quoted 24 lines
Quoted 8 lines
Quoted 8 lines
* In either case, when receiving a stop request from the driver, the device sends the LPIGs to the IOMMU queue.
* Then, the flush call above ensures that the IOMMU reports the LPIG withiommu_report_device_fault.
* While submitting all LPIGs for this PASID to the work queue,ipof_queue_faultalso picked up all partial faults, so the partial list is clean.Quoted 4 lines
Quoted 2 lines
Maybe I should improve this comment?
thanks for explaining. LPIG submission is done by device asynchronously w.r.t. driver stopping/decommission PASID.
Hmm, it should really be synchronous, otherwise there is no way to know when the PASID can be decommissioned. We need a guarantee such as the one in 6.20.1 of the PCIe spec, "Managing PASID TLP Prefix Usage":
"When the stop request mechanism indicates completion, the Function has:
* Completed all Non-Posted Requests associated with this PASID.
* Flushed to the host all Posted Requests addressing host memory in all TCs that were used by the PASID."
Quoted 5 lines
That's in combination with "The function shall [...] finish transmitting any multi-page Page Request Messages for this PASID (i.e. send the Page Request Message with the L bit Set)." from the ATS spec.
I am not contesting on the device side, what I meant was from the host IOMMU driver perspective, LPIG is received via IOMMU host queue, therefore asynchronous. Not sure about ARM, but on VT-d LPIG submission could meet queue full condition. So per VT-d spec, iommu will generate a successful auto response to the device. At this point, assume we already stopped the given PASID on the device, there might not be another LPIG sent for the device. Therefore, you could have a partial list. I think we can just drop the requests in the partial list for that PASID until the PASID gets re-allocated.
Quoted 6 lines
(If I remember correctly a PRI Page Request is a Posted Request.) Only after this stop request completes can the driver call
unbind(), or return fromexit_mm(). Then we know that if there was a LPIG for that PASID, it is in the IOMMU's PRI queue (or already completed) once we callflush().
agreed.
Quoted 7 lines
Quoted 6 lines
so if we were to use this
flow on vt-d, which does not stall page request queue, then we should use the iommu model specificflush()callback to ensure LPIG is received? There could be queue full condition and retry. I am just trying to understand how and where we can make sure LPIG is received and all groups are whole.
Quoted 7 lines
For SMMU in patch 30, the
flush()callback waits until the PRI queue is empty or, when the PRI thread is running in parallel, until the thread has done a full circle (handled as many faults as the queue size). It's really unpleasant to implement because theflush()callback takes a lock to inspect the hardware state, but I don't think we have a choice.
yes, vt-d has similar situation in page request queue. one option is to track queue head (SW update) to make sure one complete cycle when queue tail(HW update) crosses. Or we(suggested by Ashok Raj) can take a snapshot of the entire queue and process (drops PRQs belong to the terminated PASID) without holding the queue.
Thanks,
Jacob
Re: [PATCH v2 07/40] iommu: Add a page fault handler
Quoted 9 lines · Jacob Pan
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Not exactly, it's the IOMMU driver that makes sure all LPIG in its queues are submitted by the above flush call. In more details the flow is:
* Either device driver calls unbind()/sva_device_shutdown(), or the process exits.
* If the device driver called, then it already told the device to stop using the PASID. Otherwise we use themm_exit()callback to tell the device driver to stop using the PASID.
Quoted 6 lines
Sorry I still need more clarification. For the PASID termination initiated by vfio unbind, I don't see device driver given a chance to stop PASID. Seems just call
__iommu_sva_unbind_device()which already assume device stopped issuing DMA with the PASID.
So it is the vfio unbind caller responsible for doing driver callback to stop DMA on a given PASID?
Yes, but I don't know how to implement this. Since PCI doesn't formalize the PASID stop mechanism and the device doesn't have a kernel driver, VFIO would need help from the userspace driver for stopping PASID (notify the userspace driver when an other process exits).
Quoted 29 lines
Quoted 24 lines
Quoted 8 lines
Quoted 8 lines
* In either case, when receiving a stop request from the driver, the device sends the LPIGs to the IOMMU queue.
* Then, the flush call above ensures that the IOMMU reports the LPIG withiommu_report_device_fault.
* While submitting all LPIGs for this PASID to the work queue,ipof_queue_faultalso picked up all partial faults, so the partial list is clean.Quoted 4 lines
Quoted 2 lines
Maybe I should improve this comment?
thanks for explaining. LPIG submission is done by device asynchronously w.r.t. driver stopping/decommission PASID.
Hmm, it should really be synchronous, otherwise there is no way to know when the PASID can be decommissioned. We need a guarantee such as the one in 6.20.1 of the PCIe spec, "Managing PASID TLP Prefix Usage":
"When the stop request mechanism indicates completion, the Function has:
* Completed all Non-Posted Requests associated with this PASID.
* Flushed to the host all Posted Requests addressing host memory in all TCs that were used by the PASID."Quoted 5 lines
That's in combination with "The function shall [...] finish transmitting any multi-page Page Request Messages for this PASID (i.e. send the Page Request Message with the L bit Set)." from the ATS spec.
Quoted 9 lines
I am not contesting on the device side, what I meant was from the host IOMMU driver perspective, LPIG is received via IOMMU host queue, therefore asynchronous. Not sure about ARM, but on VT-d LPIG submission could meet queue full condition. So per VT-d spec, iommu will generate a successful auto response to the device. At this point, assume we already stopped the given PASID on the device, there might not be another LPIG sent for the device. Therefore, you could have a partial list. I think we can just drop the requests in the partial list for that PASID until the PASID gets re-allocated.
Indeed, I'll add this in next version. For a complete solution to the queue-full condition (which seems to behave the same way on ARM) I was thinking the IOMMU driver should also have a method for removing all partial faults when detecting a queue overflow. Since it doesn't know which PRGs did receive an auto-response, all it can do is remove all partial faults, for all devices using this queue. But freeing the stuck partial faults in flush() and remove_device() should be good enough
Thanks,
Jean
PATCH v2 8/40 iommu/iopf: Handle mm faults
In reply to Jean-Philippe BruckerWhen a recoverable page fault is handled by the fault workqueue, find the associated mm and call handle_mm_fault.
---v1->v2: let IOMMU drivers deal with Stop PASID
--- drivers/iommu/io-pgfault.c | 86 +++++++++++++++++++++++++++++++++++++- 1 file changed, 84 insertions(+), 2 deletions(-)
drivers/iommu/io-pgfault.c +84 -2
@@ -7,6 +7,7 @@#include <linux/iommu.h> #include <linux/list.h> +#include <linux/sched/mm.h> #include <linux/slab.h> #include <linux/workqueue.h>
@@ -65,8 +66,65 @@ static int iopf_complete(struct device *dev, struct iommu_fault_event *evt,static enum page_response_code iopf_handle_single (struct iopf_context *fault) { -/* TODO */ -return -ENODEV ; +int ret; +struct mm_struct *mm; +struct vm_area_struct *vma; +unsigned int access_flags =0 ; +unsigned int fault_flags =FAULT_FLAG_REMOTE ; +struct iommu_fault_event *evt = &fault->evt; +enum page_response_code status =IOMMU_PAGE_RESP_INVALID ; + +if (!evt->pasid_valid) +return status; + + mm =iommu_sva_find (evt->pasid); +if (!mm) +return status; + +down_read (&mm->mmap_sem); + + vma =find_extend_vma (mm, evt->addr); +if (!vma) +/* Unmapped area */ + goto out_put_mm; + +if (evt->prot &IOMMU_FAULT_READ ) + access_flags |=VM_READ ; + +if (evt->prot &IOMMU_FAULT_WRITE ) { + access_flags |=VM_WRITE ; + fault_flags |=FAULT_FLAG_WRITE ; + } + +if (evt->prot &IOMMU_FAULT_EXEC ) { + access_flags |=VM_EXEC ; + fault_flags |=FAULT_FLAG_INSTRUCTION ; + } + +if (!(evt->prot &IOMMU_FAULT_PRIV )) + fault_flags |=FAULT_FLAG_USER ; + +if (access_flags & ~vma->vm_flags) +/* Access fault */ + goto out_put_mm; + + ret =handle_mm_fault (vma, evt->addr, fault_flags); + status = ret &VM_FAULT_ERROR ?IOMMU_PAGE_RESP_INVALID : +IOMMU_PAGE_RESP_SUCCESS ; + +out_put_mm: +up_read (&mm->mmap_sem); + +/* +* If the process exits while we're handling the fault on its mm, we +* can't do mmput(). exit_mmap() would release the MMU notifier, calling +* iommu_notifier_release(), which has to flush the fault queue that +* we're executing on... So mmput_async() moves the release of the mm to +* another thread, if we're the last user. +*/ +mmput_async (mm); + +return status; }static void iopf_handle_group (struct work_struct *work)
@@ -100,6 +158,30 @@ static void iopf_handle_group(struct work_struct *work)* @cookie: struct device, passed to iommu_register_device_fault_handler. * * Add a fault to the device workqueue, to be handled by mm. +* +* This module doesn't handle PCI PASID Stop Marker; IOMMU drivers must discard +* them before reporting faults. A PASID Stop Marker (LRW = 0b100) doesn't +* expect a response. It may be generated when disabling a PASID (issuing a +* PASID stop request) by some PCI devices. +* +* The PASID stop request is triggered by the mm_exit() callback. When the +* callback returns from the device driver, no page request is generated for +* this PASID anymore and outstanding ones have been pushed to the IOMMU (as per +* PCIe 4.0r1.0 - 6.20.1 and 10.4.1.2 - Managing PASID TLP Prefix Usage). Some +* PCI devices will wait for all outstanding page requests to come back with a +* response before completing the PASID stop request. Others do not wait for +* page responses, and instead issue this Stop Marker that tells us when the +* PASID can be reallocated. +* +* It is safe to discard the Stop Marker because it is an optimization. +* a. Page requests, which are posted requests, have been flushed to the IOMMU +* when mm_exit() returns, +* b. We flush all fault queues after mm_exit() returns and before freeing the +* PASID. +* +* So even though the Stop Marker might be issued by the device *after* the stop +* request completes, outstanding faults will have been dealt with by the time +* we free the PASID. */ int iommu_queue_iopf (struct iommu_fault_event *evt,void *cookie) {
Signature · 1 line
2.17.0
PATCH v2 9/40 iommu/sva: Register page fault handler
In reply to Jean-Philippe BruckerLet users call iommu_sva_device_init() with the IOMMU_SVA_FEAT_IOPF flag, that enables the I/O Page Fault queue. The IOMMU driver checks is the device supports a form of page fault, in which case they add the device to a fault queue. If the device doesn't support page faults, the IOMMU driver aborts iommu_sva_device_init().
The fault queue must be flushed before any io_mm is freed, to make sure that its PASID isn't used in any fault queue, and can be reallocated. Add iopf_queue_flush() calls in a few strategic locations.
---v1->v2: new
--- drivers/iommu/iommu-sva.c | 36 ++++++++++++++++++++++++++++++++---- drivers/iommu/iommu.c | 6 +++--- include/linux/iommu.h | 2 ++ 3 files changed, 37 insertions(+), 7 deletions(-)
drivers/iommu/iommu-sva.c +32 -4
@@ -441,6 +441,8 @@ static void iommu_notifier_release(struct mmu_notifier *mn, struct mm_struct *mmdev_WARN (bond->dev,"possible leak of PASID %u" , io_mm->pasid); +iopf_queue_flush_dev (bond->dev); +spin_lock (&iommu_sva_lock); next =list_next_entry (bond, mm_head);
@@ -518,6 +520,9 @@ static struct mmu_notifier_ops iommu_mmu_notifier = {* description. Setting @max_pasid to a non-zero value smaller than this limit * overrides it. * +* If the device should support recoverable I/O Page Faults (e.g. PCI PRI), the +* IOMMU_SVA_FEAT_IOPF feature must be requested. +* * If the driver intends to share process address spaces, it should pass a valid * @mm_exit handler. Otherwise @mm_exit can be NULL. After @mm_exit returns, the * device must not issue any more transaction with the PASID given as argument.
@@ -546,12 +551,21 @@ int iommu_sva_device_init(struct device *dev, unsigned long features,if (!domain || !domain->ops->sva_device_init)return -ENODEV ; -if (features) +if (features & ~IOMMU_SVA_FEAT_IOPF )return -EINVAL ; +if (features &IOMMU_SVA_FEAT_IOPF ) { + ret =iommu_register_device_fault_handler (dev, iommu_queue_iopf, + dev); +if (ret) +return ret; + } + param =kzalloc (sizeof (*param),GFP_KERNEL ); -if (!param) -return -ENOMEM ; +if (!param) { + ret = -ENOMEM ; + goto err_remove_handler; + } param->features = features; param->max_pasid = max_pasid;
@@ -584,6 +598,9 @@ int iommu_sva_device_init(struct device *dev, unsigned long features, err_free_param:kfree (param); +err_remove_handler: +iommu_unregister_device_fault_handler (dev); +return ret; }EXPORT_SYMBOL_GPL (iommu_sva_device_init);
@@ -593,7 +610,8 @@ EXPORT_SYMBOL_GPL(iommu_sva_device_init);* @dev: the device * * Disable SVA. Device driver should ensure that the device isn't performing any -* DMA while this function is running. +* DMA while this function is running. In addition all faults should have been +* flushed to the IOMMU. */ int iommu_sva_device_shutdown (struct device *dev) {
@@ -617,6 +635,8 @@ int iommu_sva_device_shutdown(struct device *dev)kfree (param); +iommu_unregister_device_fault_handler (dev); +return 0 ; }EXPORT_SYMBOL_GPL (iommu_sva_device_shutdown);
@@ -694,6 +714,12 @@ int __iommu_sva_unbind_device(struct device *dev, int pasid)if (!param ||WARN_ON (!domain))return -EINVAL ; +/* +* Caller stopped the device from issuing PASIDs, now make sure they are +* out of the fault queue. +*/ +iopf_queue_flush_dev (dev); +/* spin_lock_irq matches the one in wait_event_lock_irq */ spin_lock_irq (&iommu_sva_lock);list_for_each_entry (bond, ¶m->mm_list, dev_head) {
@@ -721,6 +747,8 @@ void __iommu_sva_unbind_dev_all(struct device *dev)struct iommu_sva_param *param;struct iommu_bond *bond, *next; +iopf_queue_flush_dev (dev); +/* * io_mm_detach_locked might wait, so we shouldn't call it with the dev * param lock held. It's fine to read sva_param outside the lock because
drivers/iommu/iommu.c +3 -3
@@ -2278,9 +2278,9 @@ EXPORT_SYMBOL_GPL(iommu_fwspec_add_ids);* iommu_sva_device_init() must be called first, to initialize the required SVA * features. @flags is a subset of these features. * -* The caller must pin down using get_user_pages*() all mappings shared with the -* device. mlock() isn't sufficient, as it doesn't prevent minor page faults -* (e.g. copy-on-write). +* If IOMMU_SVA_FEAT_IOPF isn't requested, the caller must pin down using +* get_user_pages*() all mappings shared with the device. mlock() isn't +* sufficient, as it doesn't prevent minor page faults (e.g. copy-on-write). * * On success, 0 is returned and @pasid contains a valid ID. Otherwise, an error * is returned.
include/linux/iommu.h +2 -0
@@ -64,6 +64,8 @@ typedef int (*iommu_fault_handler_t)(struct iommu_domain *,typedef int (*iommu_dev_fault_handler_t )(struct iommu_fault_event *,void *);typedef int (*iommu_mm_exit_handler_t )(struct device *dev,int pasid,void *); +#define IOMMU_SVA_FEAT_IOPF (1 << 0) +struct iommu_domain_geometry {dma_addr_t aperture_start;/* First address that can be mapped */ dma_addr_t aperture_end;/* Last address that can be mapped */
Signature · 1 line
2.17.0
PATCH v2 10/40 mm: export symbol mm_access
In reply to Jean-Philippe BruckerSome devices can access process address spaces directly. When creating such bond, to check that a process controlling the device is allowed to access the target address space, the device driver uses mm_access(). Since the drivers (in this case VFIO) can be built as a module, export the mm_access symbol.
---
This patch was already sent last year for AMD KFD. I'm resending it for VFIO, trying to address Andrew Morton's request to comment the exported
function: http://lkml.iu.edu/hypermail/linux/kernel/1705.2/06774.html
--- kernel/fork.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+)
kernel/fork.c +14 -0
@@ -1098,6 +1098,19 @@ struct mm_struct *get_task_mm(struct task_struct *task) }EXPORT_SYMBOL_GPL (get_task_mm); +/** +* mm_access - check access permission to a task and and acquire a reference to +* its mm. +* @task: target task +* @mode: selects type of access and caller credentials +* +* Return the task's mm on success, or %NULL if it cannot be accessed. +* +* Check if the caller is allowed to read or write the target task's pages. +* @mode describes the access mode and credentials using ptrace access flags. +* See ptrace_may_access() for more details. On success, a reference to the mm +* is taken. +*/ struct mm_struct *mm_access (struct task_struct *task,unsigned int mode) {struct mm_struct *mm;
@@ -1117,6 +1130,7 @@ struct mm_struct *mm_access(struct task_struct *task, unsigned int mode)return mm; } +EXPORT_SYMBOL_GPL (mm_access);static void complete_vfork_done (struct task_struct *tsk) {
Signature · 1 line
2.17.0
PATCH v2 11/40 mm: export symbol find_get_task_by_vpid
In reply to Jean-Philippe BruckerUserspace drivers implemented with VFIO might want to bind sub-processes to their devices. In a VFIO ioctl, they provide a pid that is used to find a task and its mm. Since VFIO can be built as a module, export the find_get_task_by_vpid symbol.
---
Here I didn't add a comment because neighbouring functions that get exported don't have a comment either, and the name seems fairly clear.
--- kernel/pid.c | 1 + 1 file changed, 1 insertion(+)
kernel/pid.c +1 -0
@@ -355,6 +355,7 @@ struct task_struct *find_get_task_by_vpid(pid_t nr)return task; } +EXPORT_SYMBOL_GPL (find_get_task_by_vpid);struct pid *get_task_pid (struct task_struct *task,enum pid_type type) {
Signature · 1 line
2.17.0
PATCH v2 12/40 mm: export symbol mmput_async
In reply to Jean-Philippe BruckerIn some cases releasing a mm bound to a device might invoke an exit handler, that takes a lock already held by the function calling mmput(). This is the case for VFIO, which needs to call mmput_async to avoid a deadlock. Other drivers using SVA might follow. Since they can be built as modules, export the mmput_async symbol.
--- kernel/fork.c | 1 + 1 file changed, 1 insertion(+)
kernel/fork.c +1 -0
@@ -999,6 +999,7 @@ void mmput_async(struct mm_struct *mm) schedule_work(&mm->async_put_work); } } +EXPORT_SYMBOL_GPL (mmput_async);#endif /**
Signature · 1 line
2.17.0
PATCH v2 13/40 vfio: Add support for Shared Virtual Addressing
In reply to Jean-Philippe BruckerAdd two new ioctls for VFIO containers. VFIO_IOMMU_BIND_PROCESS creates a bond between a container and a process address space, identified by a Process Address Space ID (PASID). Devices in the container append this PASID to DMA transactions in order to access the process' address space. The process page tables are shared with the IOMMU, and mechanisms such as PCI ATS/PRI are used to handle faults. VFIO_IOMMU_UNBIND_PROCESS removes a bond created with VFIO_IOMMU_BIND_PROCESS.
---v1->v2:
* Simplify mm_exit
* Can be built as module again
--- drivers/vfio/vfio_iommu_type1.c | 449 ++++++++++++++++++++++++++++++-- include/uapi/linux/vfio.h | 76 ++++++ 2 files changed, 504 insertions(+), 21 deletions(-)
drivers/vfio/vfio_iommu_type1.c +428 -21
@@ -30,6 +30,7 @@#include <linux/iommu.h> #include <linux/module.h> #include <linux/mm.h> +#include <linux/ptrace.h> #include <linux/rbtree.h> #include <linux/sched/signal.h> #include <linux/sched/mm.h>
@@ -60,6 +61,7 @@ MODULE_PARM_DESC(disable_hugepages,struct vfio_iommu {struct list_head domain_list; +struct list_head mm_list;struct vfio_domain *external_domain;/* domain for external user */ struct mutex lock;struct rb_root dma_list;
@@ -90,6 +92,14 @@ struct vfio_dma {struct vfio_group {struct iommu_group *iommu_group;struct list_head next; +bool sva_enabled; +}; + +struct vfio_mm { +#define VFIO_PASID_INVALID (-1) +int pasid; +struct mm_struct *mm; +struct list_head next; };/*
@@ -1238,6 +1248,164 @@ static int vfio_iommu_replay(struct vfio_iommu *iommu,return 0 ; } +static int vfio_iommu_mm_exit (struct device *dev,int pasid,void *data) +{ +struct vfio_mm *vfio_mm; +struct vfio_iommu *iommu = data; + +mutex_lock (&iommu->lock); +list_for_each_entry (vfio_mm, &iommu->mm_list, next) { +if (vfio_mm->pasid == pasid) { +list_del (&vfio_mm->next); +kfree (vfio_mm); +break ; + } + } +mutex_unlock (&iommu->lock); + +return 0 ; +} + +static int vfio_iommu_sva_init (struct device *dev,void *data) +{ +return iommu_sva_device_init (dev,IOMMU_SVA_FEAT_IOPF ,0 , + vfio_iommu_mm_exit); +} + +static int vfio_iommu_sva_shutdown (struct device *dev,void *data) +{ +iommu_sva_device_shutdown (dev); + +return 0 ; +} + +struct vfio_iommu_sva_bind_data { +struct vfio_mm *vfio_mm; +struct vfio_iommu *iommu; +int count; +}; + +static int vfio_iommu_sva_bind_dev (struct device *dev,void *data) +{ +struct vfio_iommu_sva_bind_data *bind_data = data; + +/* Multi-device groups aren't support for SVA */ +if (bind_data->count++) +return -EINVAL ; + +return __iommu_sva_bind_device (dev, bind_data->vfio_mm->mm, + &bind_data->vfio_mm->pasid, +IOMMU_SVA_FEAT_IOPF , bind_data->iommu); +} + +static int vfio_iommu_sva_unbind_dev (struct device *dev,void *data) +{ +struct vfio_mm *vfio_mm = data; + +return __iommu_sva_unbind_device (dev, vfio_mm->pasid); +} + +static int vfio_iommu_bind_group (struct vfio_iommu *iommu, +struct vfio_group *group, +struct vfio_mm *vfio_mm) +{ +int ret; +bool enabled_sva = false; +struct vfio_iommu_sva_bind_data data = { + .vfio_mm = vfio_mm, + .iommu = iommu, + .count =0 , + }; + +if (!group->sva_enabled) { + ret =iommu_group_for_each_dev (group->iommu_group,NULL , + vfio_iommu_sva_init); +if (ret) +return ret; + + group->sva_enabled = enabled_sva = true; + } + + ret =iommu_group_for_each_dev (group->iommu_group, &data, + vfio_iommu_sva_bind_dev); +if (ret && data.count >1 ) +iommu_group_for_each_dev (group->iommu_group, vfio_mm, + vfio_iommu_sva_unbind_dev); +if (ret && enabled_sva) { +iommu_group_for_each_dev (group->iommu_group,NULL , + vfio_iommu_sva_shutdown); + group->sva_enabled = false; + } + +return ret; +} + +static void vfio_iommu_unbind_group (struct vfio_group *group, +struct vfio_mm *vfio_mm) +{ +iommu_group_for_each_dev (group->iommu_group, vfio_mm, + vfio_iommu_sva_unbind_dev); +} + +static void vfio_iommu_unbind (struct vfio_iommu *iommu, +struct vfio_mm *vfio_mm) +{ +struct vfio_group *group; +struct vfio_domain *domain; + +list_for_each_entry (domain, &iommu->domain_list, next) +list_for_each_entry (group, &domain->group_list, next) +vfio_iommu_unbind_group (group, vfio_mm); +} + +static int vfio_iommu_replay_bind (struct vfio_iommu *iommu, +struct vfio_group *group) +{ +int ret =0 ; +struct vfio_mm *vfio_mm; + +list_for_each_entry (vfio_mm, &iommu->mm_list, next) { +/* +* Ensure that mm doesn't exit while we're binding it to the new +* group. It may already have exited, and the mm_exit notifier +* might be waiting on the IOMMU mutex to remove this vfio_mm +* from the list. +*/ +if (!mmget_not_zero (vfio_mm->mm)) +continue ; + ret =vfio_iommu_bind_group (iommu, group, vfio_mm); +/* +* Use async to avoid triggering an mm_exit callback right away, +* which would block on the mutex that we're holding. +*/ +mmput_async (vfio_mm->mm); + +if (ret) + goto out_unbind; + } + +return 0 ; + +out_unbind: +list_for_each_entry_continue_reverse (vfio_mm, &iommu->mm_list, next) +vfio_iommu_unbind_group (group, vfio_mm); + +return ret; +} + +static void vfio_iommu_free_all_mm (struct vfio_iommu *iommu) +{ +struct vfio_mm *vfio_mm, *next; + +/* +* No need for unbind() here. Since all groups are detached from this +* iommu, bonds have been removed. +*/ +list_for_each_entry_safe (vfio_mm, next, &iommu->mm_list, next) +kfree (vfio_mm); +INIT_LIST_HEAD (&iommu->mm_list); +} +/* * We change our unmap behavior slightly depending on whether the IOMMU * supports fine-grained superpages. IOMMUs like AMD-Vi will use a superpage
@@ -1313,6 +1481,44 @@ static bool vfio_iommu_has_sw_msi(struct iommu_group *group, phys_addr_t *base)return ret; } +static int vfio_iommu_try_attach_group (struct vfio_iommu *iommu, +struct vfio_group *group, +struct vfio_domain *cur_domain, +struct vfio_domain *new_domain) +{ +struct iommu_group *iommu_group = group->iommu_group; + +/* +* Try to match an existing compatible domain. We don't want to +* preclude an IOMMU driver supporting multiple bus_types and being +* able to include different bus_types in the same IOMMU domain, so +* we test whether the domains use the same iommu_ops rather than +* testing if they're on the same bus_type. +*/ +if (new_domain->domain->ops != cur_domain->domain->ops || + new_domain->prot != cur_domain->prot) +return 1 ; + +iommu_detach_group (cur_domain->domain, iommu_group); + +if (iommu_attach_group (new_domain->domain, iommu_group)) + goto out_reattach; + +if (vfio_iommu_replay_bind (iommu, group)) + goto out_detach; + +return 0 ; + +out_detach: +iommu_detach_group (new_domain->domain, iommu_group); + +out_reattach: +if (iommu_attach_group (cur_domain->domain, iommu_group)) +return -EINVAL ; + +return 1 ; +} +static int vfio_iommu_type1_attach_group (void *iommu_data,struct iommu_group *iommu_group) {
@@ -1410,28 +1616,16 @@ static int vfio_iommu_type1_attach_group(void *iommu_data,if (iommu_capable (bus,IOMMU_CAP_CACHE_COHERENCY )) domain->prot |=IOMMU_CACHE ; -/* -* Try to match an existing compatible domain. We don't want to -* preclude an IOMMU driver supporting multiple bus_types and being -* able to include different bus_types in the same IOMMU domain, so -* we test whether the domains use the same iommu_ops rather than -* testing if they're on the same bus_type. -*/ list_for_each_entry (d, &iommu->domain_list, next) { -if (d->domain->ops == domain->domain->ops && - d->prot == domain->prot) { -iommu_detach_group (domain->domain, iommu_group); -if (!iommu_attach_group (d->domain, iommu_group)) { -list_add (&group->next, &d->group_list); -iommu_domain_free (domain->domain); -kfree (domain); -mutex_unlock (&iommu->lock); -return 0 ; - } - - ret =iommu_attach_group (domain->domain, iommu_group); -if (ret) - goto out_domain; + ret =vfio_iommu_try_attach_group (iommu, group, domain, d); +if (ret <0 ) { + goto out_domain; + }else if (!ret) { +list_add (&group->next, &d->group_list); +iommu_domain_free (domain->domain); +kfree (domain); +mutex_unlock (&iommu->lock); +return 0 ; } }
@@ -1442,6 +1636,10 @@ static int vfio_iommu_type1_attach_group(void *iommu_data,if (ret) goto out_detach; + ret =vfio_iommu_replay_bind (iommu, group); +if (ret) + goto out_detach; +if (resv_msi) { ret =iommu_get_msi_cookie (domain->domain, resv_msi_base);if (ret)
@@ -1547,6 +1745,11 @@ static void vfio_iommu_type1_detach_group(void *iommu_data,continue ;iommu_detach_group (domain->domain, iommu_group); +if (group->sva_enabled) { +iommu_group_for_each_dev (iommu_group,NULL , + vfio_iommu_sva_shutdown); + group->sva_enabled = false; + }list_del (&group->next);kfree (group);/*
@@ -1562,6 +1765,7 @@ static void vfio_iommu_type1_detach_group(void *iommu_data,vfio_iommu_unmap_unpin_all (iommu); elsevfio_iommu_unmap_unpin_reaccount (iommu ); +vfio_iommu_free_all_mm (iommu); }iommu_domain_free (domain->domain);list_del (&domain->next);
@@ -1596,6 +1800,7 @@ static void *vfio_iommu_type1_open(unsigned long arg) }INIT_LIST_HEAD (&iommu->domain_list); +INIT_LIST_HEAD (&iommu->mm_list); iommu->dma_list =RB_ROOT ;mutex_init (&iommu->lock);BLOCKING_INIT_NOTIFIER_HEAD (&iommu->notifier);
@@ -1630,6 +1835,7 @@ static void vfio_iommu_type1_release(void *iommu_data)kfree (iommu->external_domain); } +vfio_iommu_free_all_mm (iommu);vfio_iommu_unmap_unpin_all (iommu); list_for_each_entry_safe(domain, domain_tmp,
@@ -1658,6 +1864,169 @@ static int vfio_domains_have_iommu_cache(struct vfio_iommu *iommu)return ret; } +static struct mm_struct *vfio_iommu_get_mm_by_vpid (pid_t vpid) +{ +struct mm_struct *mm; +struct task_struct *task; + + task =find_get_task_by_vpid (vpid); +if (!task) +return ERR_PTR (-ESRCH ); + +/* Ensure that current has RW access on the mm */ + mm =mm_access (task,PTRACE_MODE_ATTACH_REALCREDS ); +put_task_struct (task); + +if (!mm) +return ERR_PTR (-ESRCH ); + +return mm; +} + +static long vfio_iommu_type1_bind_process (struct vfio_iommu *iommu, +void __user *arg, +struct vfio_iommu_type1_bind *bind) +{ +struct vfio_iommu_type1_bind_process params; +struct vfio_domain *domain; +struct vfio_group *group; +struct vfio_mm *vfio_mm; +struct mm_struct *mm; +unsigned long minsz; +int ret =0 ; + + minsz =sizeof (*bind) +sizeof (params); +if (bind->argsz < minsz) +return -EINVAL ; + + arg +=sizeof (*bind); +if (copy_from_user (¶ms, arg,sizeof (params))) +return -EFAULT ; + +if (params.flags & ~VFIO_IOMMU_BIND_PID ) +return -EINVAL ; + +if (params.flags &VFIO_IOMMU_BIND_PID ) { + mm =vfio_iommu_get_mm_by_vpid (params.pid); +if (IS_ERR (mm)) +return PTR_ERR (mm); + }else { + mm =get_task_mm (current); +if (!mm) +return -EINVAL ; + } + +mutex_lock (&iommu->lock); +if (!IS_IOMMU_CAP_DOMAIN_IN_CONTAINER (iommu)) { + ret = -EINVAL ; + goto out_unlock; + } + +list_for_each_entry (vfio_mm, &iommu->mm_list, next) { +if (vfio_mm->mm == mm) { + params.pasid = vfio_mm->pasid; + ret =copy_to_user (arg, ¶ms,sizeof (params)) ? + -EFAULT :0 ; + goto out_unlock; + } + } + + vfio_mm =kzalloc (sizeof (*vfio_mm),GFP_KERNEL ); +if (!vfio_mm) { + ret = -ENOMEM ; + goto out_unlock; + } + + vfio_mm->mm = mm; + vfio_mm->pasid =VFIO_PASID_INVALID ; + +list_for_each_entry (domain, &iommu->domain_list, next) { +list_for_each_entry (group, &domain->group_list, next) { + ret =vfio_iommu_bind_group (iommu, group, vfio_mm); +if (ret) + goto out_unbind; + } + } + +list_add (&vfio_mm->next, &iommu->mm_list); + + params.pasid = vfio_mm->pasid; + ret =copy_to_user (arg, ¶ms,sizeof (params)) ? -EFAULT :0 ; +if (ret) + goto out_delete; + +mutex_unlock (&iommu->lock); +mmput (mm); +return 0 ; + +out_delete: +list_del (&vfio_mm->next); + +out_unbind: +/* Undo all binds that already succeeded */ +vfio_iommu_unbind (iommu, vfio_mm); +kfree (vfio_mm); + +out_unlock: +mutex_unlock (&iommu->lock); +mmput (mm); +return ret; +} + +static long vfio_iommu_type1_unbind_process (struct vfio_iommu *iommu, +void __user *arg, +struct vfio_iommu_type1_bind *bind) +{ +int ret = -EINVAL ; +unsigned long minsz; +struct mm_struct *mm; +struct vfio_mm *vfio_mm; +struct vfio_iommu_type1_bind_process params; + + minsz =sizeof (*bind) +sizeof (params); +if (bind->argsz < minsz) +return -EINVAL ; + + arg +=sizeof (*bind); +if (copy_from_user (¶ms, arg,sizeof (params))) +return -EFAULT ; + +if (params.flags & ~VFIO_IOMMU_BIND_PID ) +return -EINVAL ; + +/* +* We can't simply call unbind with the PASID, because the process might +* have died and the PASID might have been reallocated to another +* process. Instead we need to fetch that process mm by PID again to +* make sure we remove the right vfio_mm. +*/ +if (params.flags &VFIO_IOMMU_BIND_PID ) { + mm =vfio_iommu_get_mm_by_vpid (params.pid); +if (IS_ERR (mm)) +return PTR_ERR (mm); + }else { + mm =get_task_mm (current); +if (!mm) +return -EINVAL ; + } + + ret = -ESRCH ; +mutex_lock (&iommu->lock); +list_for_each_entry (vfio_mm, &iommu->mm_list, next) { +if (vfio_mm->mm == mm) { +vfio_iommu_unbind (iommu, vfio_mm); +list_del (&vfio_mm->next); +kfree (vfio_mm); + ret =0 ; +break ; + } + } +mutex_unlock (&iommu->lock); +mmput (mm); + +return ret; +} +static long vfio_iommu_type1_ioctl (void *iommu_data,unsigned int cmd,unsigned long arg) {
@@ -1728,6 +2097,44 @@ static long vfio_iommu_type1_ioctl(void *iommu_data,return copy_to_user ((void __user *)arg, &unmap, minsz) ? -EFAULT :0 ; + + }else if (cmd ==VFIO_IOMMU_BIND ) { +struct vfio_iommu_type1_bind bind; + + minsz = offsetofend(struct vfio_iommu_type1_bind, flags); + +if (copy_from_user (&bind, (void __user *)arg, minsz)) +return -EFAULT ; + +if (bind.argsz < minsz) +return -EINVAL ; + +switch (bind.flags) { +case VFIO_IOMMU_BIND_PROCESS : +return vfio_iommu_type1_bind_process (iommu, (void *)arg, + &bind); +default : +return -EINVAL ; + } + + }else if (cmd ==VFIO_IOMMU_UNBIND ) { +struct vfio_iommu_type1_bind bind; + + minsz = offsetofend(struct vfio_iommu_type1_bind, flags); + +if (copy_from_user (&bind, (void __user *)arg, minsz)) +return -EFAULT ; + +if (bind.argsz < minsz) +return -EINVAL ; + +switch (bind.flags) { +case VFIO_IOMMU_BIND_PROCESS : +return vfio_iommu_type1_unbind_process (iommu, (void *)arg, + &bind); +default : +return -EINVAL ; + } }return -ENOTTY ;
include/uapi/linux/vfio.h +76 -0
82 lines not on this page · raw message
Signature · 1 line
2.17.0
Re: [PATCH v2 13/40] vfio: Add support for Shared Virtual Addressing
Quoted 9 lines · On Fri, 11 May 2018 20:06:14 +0100 Jean-Philippe Brucker…
Add two new ioctls for VFIO containers.
VFIO_IOMMU_BIND_PROCESScreates a bond between a container and a process address space, identified by a Process Address Space ID (PASID). Devices in the container append this PASID to DMA transactions in order to access the process' address space. The process page tables are shared with the IOMMU, and mechanisms such as PCI ATS/PRI are used to handle faults.VFIO_IOMMU_UNBIND_PROCESSremoves a bond created withVFIO_IOMMU_BIND_PROCESS.Signed-off-by Jean-Philippe Brucker <[email protected]>
A couple of small comments inline..
Jonathan
Quoted 120 lines
---
v1->v2:
* Simplifymm_exit
* Can be built as module again--- drivers/vfio/vfio_iommu_type1.c | 449 ++++++++++++++++++++++++++++++-- include/uapi/linux/vfio.h | 76 ++++++ 2 files changed, 504 insertions(+), 21 deletions(-)drivers/vfio/vfio_iommu_type1.c +81
-0@@ -30,6 +30,7 @@#include <linux/iommu.h> #include <linux/module.h> #include <linux/mm.h> +#include <linux/ptrace.h> #include <linux/rbtree.h> #include <linux/sched/signal.h> #include <linux/sched/mm.h> @@ -60,6 +61,7 @@ MODULE_PARM_DESC(disable_hugepages,struct vfio_iommu {struct list_head domain_list; +struct list_head mm_list;struct vfio_domain *external_domain;/* domain for external user */ struct mutex lock;struct rb_root dma_list;@@ -90,6 +92,14 @@ struct vfio_dma {struct vfio_group {struct iommu_group *iommu_group;struct list_head next; +bool sva_enabled; +}; + +struct vfio_mm { +#define VFIO_PASID_INVALID (-1) +int pasid; +struct mm_struct *mm; +struct list_head next; };/* @@ -1238,6 +1248,164 @@ static int vfio_iommu_replay(struct vfio_iommu *iommu,return 0 ; } +static int vfio_iommu_mm_exit (struct device *dev,int pasid,void *data) +{ +struct vfio_mm *vfio_mm; +struct vfio_iommu *iommu = data; + +mutex_lock (&iommu->lock); +list_for_each_entry (vfio_mm, &iommu->mm_list, next) { +if (vfio_mm->pasid == pasid) { +list_del (&vfio_mm->next); +kfree (vfio_mm); +break ; + } + } +mutex_unlock (&iommu->lock); + +return 0 ; +} + +static int vfio_iommu_sva_init (struct device *dev,void *data) +{ +return iommu_sva_device_init (dev,IOMMU_SVA_FEAT_IOPF ,0 , + vfio_iommu_mm_exit); +} + +static int vfio_iommu_sva_shutdown (struct device *dev,void *data) +{ +iommu_sva_device_shutdown (dev); + +return 0 ; +} + +struct vfio_iommu_sva_bind_data { +struct vfio_mm *vfio_mm; +struct vfio_iommu *iommu; +int count; +}; + +static int vfio_iommu_sva_bind_dev (struct device *dev,void *data) +{ +struct vfio_iommu_sva_bind_data *bind_data = data; + +/* Multi-device groups aren't support for SVA */ +if (bind_data->count++) +return -EINVAL ; + +return __iommu_sva_bind_device (dev, bind_data->vfio_mm->mm, + &bind_data->vfio_mm->pasid, +IOMMU_SVA_FEAT_IOPF , bind_data->iommu); +} + +static int vfio_iommu_sva_unbind_dev (struct device *dev,void *data) +{ +struct vfio_mm *vfio_mm = data; + +return __iommu_sva_unbind_device (dev, vfio_mm->pasid); +} + +static int vfio_iommu_bind_group (struct vfio_iommu *iommu, +struct vfio_group *group, +struct vfio_mm *vfio_mm) +{ +int ret; +bool enabled_sva = false; +struct vfio_iommu_sva_bind_data data = { + .vfio_mm = vfio_mm, + .iommu = iommu, + .count =0 , + }; + +if (!group->sva_enabled) { + ret = iommu_group_for_each_dev(group->iommu_group, NULL,
drivers/vfio/vfio_iommu_type1.c static int vfio_iommu_replay(struct vfio_iommu *iommu, · line 1331
Quoted 10 lines
drivers/vfio/vfio_iommu_type1.c +10
-0+ vfio_iommu_sva_init); +if (ret) +return ret; + + group->sva_enabled = enabled_sva = true; + } + + ret =iommu_group_for_each_dev (group->iommu_group, &data, + vfio_iommu_sva_bind_dev); +if (ret && data.count >1 )
Are we safe to run this even if data.count == 1? I assume that at that point we would always not have an iommu domain associated with the device so the initial check would error out.
Just be nice to get rid of the special casing in this error path as then could just do it all under if (ret) and make it visually clearer these are different aspects of the same error path.
Quoted 390 lines
drivers/vfio/vfio_iommu_type1.c +305
-21+iommu_group_for_each_dev (group->iommu_group, vfio_mm, + vfio_iommu_sva_unbind_dev); +if (ret && enabled_sva) { +iommu_group_for_each_dev (group->iommu_group,NULL , + vfio_iommu_sva_shutdown); + group->sva_enabled = false; + } + +return ret; +} + +static void vfio_iommu_unbind_group (struct vfio_group *group, +struct vfio_mm *vfio_mm) +{ +iommu_group_for_each_dev (group->iommu_group, vfio_mm, + vfio_iommu_sva_unbind_dev); +} + +static void vfio_iommu_unbind (struct vfio_iommu *iommu, +struct vfio_mm *vfio_mm) +{ +struct vfio_group *group; +struct vfio_domain *domain; + +list_for_each_entry (domain, &iommu->domain_list, next) +list_for_each_entry (group, &domain->group_list, next) +vfio_iommu_unbind_group (group, vfio_mm); +} + +static int vfio_iommu_replay_bind (struct vfio_iommu *iommu, +struct vfio_group *group) +{ +int ret =0 ; +struct vfio_mm *vfio_mm; + +list_for_each_entry (vfio_mm, &iommu->mm_list, next) { +/* +* Ensure that mm doesn't exit while we're binding it to the new +* group. It may already have exited, and the mm_exit notifier +* might be waiting on the IOMMU mutex to remove this vfio_mm +* from the list. +*/ +if (!mmget_not_zero (vfio_mm->mm)) +continue ; + ret =vfio_iommu_bind_group (iommu, group, vfio_mm); +/* +* Use async to avoid triggering an mm_exit callback right away, +* which would block on the mutex that we're holding. +*/ +mmput_async (vfio_mm->mm); + +if (ret) + goto out_unbind; + } + +return 0 ; + +out_unbind: +list_for_each_entry_continue_reverse (vfio_mm, &iommu->mm_list, next) +vfio_iommu_unbind_group (group, vfio_mm); + +return ret; +} + +static void vfio_iommu_free_all_mm (struct vfio_iommu *iommu) +{ +struct vfio_mm *vfio_mm, *next; + +/* +* No need for unbind() here. Since all groups are detached from this +* iommu, bonds have been removed. +*/ +list_for_each_entry_safe (vfio_mm, next, &iommu->mm_list, next) +kfree (vfio_mm); +INIT_LIST_HEAD (&iommu->mm_list); +} +/* * We change our unmap behavior slightly depending on whether the IOMMU * supports fine-grained superpages. IOMMUs like AMD-Vi will use a superpage @@ -1313,6 +1481,44 @@ static bool vfio_iommu_has_sw_msi(struct iommu_group *group, phys_addr_t *base)return ret; } +static int vfio_iommu_try_attach_group (struct vfio_iommu *iommu, +struct vfio_group *group, +struct vfio_domain *cur_domain, +struct vfio_domain *new_domain) +{ +struct iommu_group *iommu_group = group->iommu_group; + +/* +* Try to match an existing compatible domain. We don't want to +* preclude an IOMMU driver supporting multiple bus_types and being +* able to include different bus_types in the same IOMMU domain, so +* we test whether the domains use the same iommu_ops rather than +* testing if they're on the same bus_type. +*/ +if (new_domain->domain->ops != cur_domain->domain->ops || + new_domain->prot != cur_domain->prot) +return 1 ; + +iommu_detach_group (cur_domain->domain, iommu_group); + +if (iommu_attach_group (new_domain->domain, iommu_group)) + goto out_reattach; + +if (vfio_iommu_replay_bind (iommu, group)) + goto out_detach; + +return 0 ; + +out_detach: +iommu_detach_group (new_domain->domain, iommu_group); + +out_reattach: +if (iommu_attach_group (cur_domain->domain, iommu_group)) +return -EINVAL ; + +return 1 ; +} +static int vfio_iommu_type1_attach_group (void *iommu_data,struct iommu_group *iommu_group) {@@ -1410,28 +1616,16 @@ static int vfio_iommu_type1_attach_group(void *iommu_data,if (iommu_capable (bus,IOMMU_CAP_CACHE_COHERENCY )) domain->prot |=IOMMU_CACHE ; -/* -* Try to match an existing compatible domain. We don't want to -* preclude an IOMMU driver supporting multiple bus_types and being -* able to include different bus_types in the same IOMMU domain, so -* we test whether the domains use the same iommu_ops rather than -* testing if they're on the same bus_type. -*/ list_for_each_entry (d, &iommu->domain_list, next) { -if (d->domain->ops == domain->domain->ops && - d->prot == domain->prot) { -iommu_detach_group (domain->domain, iommu_group); -if (!iommu_attach_group (d->domain, iommu_group)) { -list_add (&group->next, &d->group_list); -iommu_domain_free (domain->domain); -kfree (domain); -mutex_unlock (&iommu->lock); -return 0 ; - } - - ret =iommu_attach_group (domain->domain, iommu_group); -if (ret) - goto out_domain; + ret =vfio_iommu_try_attach_group (iommu, group, domain, d); +if (ret <0 ) { + goto out_domain; + }else if (!ret) { +list_add (&group->next, &d->group_list); +iommu_domain_free (domain->domain); +kfree (domain); +mutex_unlock (&iommu->lock); +return 0 ; } }@@ -1442,6 +1636,10 @@ static int vfio_iommu_type1_attach_group(void *iommu_data,if (ret) goto out_detach; + ret =vfio_iommu_replay_bind (iommu, group); +if (ret) + goto out_detach; +if (resv_msi) { ret =iommu_get_msi_cookie (domain->domain, resv_msi_base);if (ret)@@ -1547,6 +1745,11 @@ static void vfio_iommu_type1_detach_group(void *iommu_data,continue ;iommu_detach_group (domain->domain, iommu_group); +if (group->sva_enabled) { +iommu_group_for_each_dev (iommu_group,NULL , + vfio_iommu_sva_shutdown); + group->sva_enabled = false; + }list_del (&group->next);kfree (group);/* @@ -1562,6 +1765,7 @@ static void vfio_iommu_type1_detach_group(void *iommu_data,vfio_iommu_unmap_unpin_all (iommu); elsevfio_iommu_unmap_unpin_reaccount (iommu ); +vfio_iommu_free_all_mm (iommu); }iommu_domain_free (domain->domain);list_del (&domain->next);@@ -1596,6 +1800,7 @@ static void *vfio_iommu_type1_open(unsigned long arg) }INIT_LIST_HEAD (&iommu->domain_list); +INIT_LIST_HEAD (&iommu->mm_list); iommu->dma_list =RB_ROOT ;mutex_init (&iommu->lock);BLOCKING_INIT_NOTIFIER_HEAD (&iommu->notifier);@@ -1630,6 +1835,7 @@ static void vfio_iommu_type1_release(void *iommu_data)kfree (iommu->external_domain); } +vfio_iommu_free_all_mm (iommu);vfio_iommu_unmap_unpin_all (iommu); list_for_each_entry_safe(domain, domain_tmp,@@ -1658,6 +1864,169 @@ static int vfio_domains_have_iommu_cache(struct vfio_iommu *iommu)return ret; } +static struct mm_struct *vfio_iommu_get_mm_by_vpid (pid_t vpid) +{ +struct mm_struct *mm; +struct task_struct *task; + + task =find_get_task_by_vpid (vpid); +if (!task) +return ERR_PTR (-ESRCH ); + +/* Ensure that current has RW access on the mm */ + mm =mm_access (task,PTRACE_MODE_ATTACH_REALCREDS ); +put_task_struct (task); + +if (!mm) +return ERR_PTR (-ESRCH ); + +return mm; +} + +static long vfio_iommu_type1_bind_process (struct vfio_iommu *iommu, +void __user *arg, +struct vfio_iommu_type1_bind *bind) +{ +struct vfio_iommu_type1_bind_process params; +struct vfio_domain *domain; +struct vfio_group *group; +struct vfio_mm *vfio_mm; +struct mm_struct *mm; +unsigned long minsz; +int ret =0 ; + + minsz =sizeof (*bind) +sizeof (params); +if (bind->argsz < minsz) +return -EINVAL ; + + arg +=sizeof (*bind); +if (copy_from_user (¶ms, arg,sizeof (params))) +return -EFAULT ; + +if (params.flags & ~VFIO_IOMMU_BIND_PID ) +return -EINVAL ; + +if (params.flags &VFIO_IOMMU_BIND_PID ) { + mm =vfio_iommu_get_mm_by_vpid (params.pid); +if (IS_ERR (mm)) +return PTR_ERR (mm); + }else { + mm =get_task_mm (current); +if (!mm) +return -EINVAL ; + } + +mutex_lock (&iommu->lock); +if (!IS_IOMMU_CAP_DOMAIN_IN_CONTAINER (iommu)) { + ret = -EINVAL ; + goto out_unlock; + } + +list_for_each_entry (vfio_mm, &iommu->mm_list, next) { +if (vfio_mm->mm == mm) { + params.pasid = vfio_mm->pasid; + ret =copy_to_user (arg, ¶ms,sizeof (params)) ? + -EFAULT :0 ; + goto out_unlock; + } + } + + vfio_mm =kzalloc (sizeof (*vfio_mm),GFP_KERNEL ); +if (!vfio_mm) { + ret = -ENOMEM ; + goto out_unlock; + } + + vfio_mm->mm = mm; + vfio_mm->pasid =VFIO_PASID_INVALID ; + +list_for_each_entry (domain, &iommu->domain_list, next) { +list_for_each_entry (group, &domain->group_list, next) { + ret =vfio_iommu_bind_group (iommu, group, vfio_mm); +if (ret) + goto out_unbind; + } + } + +list_add (&vfio_mm->next, &iommu->mm_list); + + params.pasid = vfio_mm->pasid; + ret =copy_to_user (arg, ¶ms,sizeof (params)) ? -EFAULT :0 ; +if (ret) + goto out_delete; + +mutex_unlock (&iommu->lock); +mmput (mm); +return 0 ; + +out_delete: +list_del (&vfio_mm->next); + +out_unbind: +/* Undo all binds that already succeeded */ +vfio_iommu_unbind (iommu, vfio_mm); +kfree (vfio_mm); + +out_unlock: +mutex_unlock (&iommu->lock); +mmput (mm); +return ret; +} + +static long vfio_iommu_type1_unbind_process (struct vfio_iommu *iommu, +void __user *arg, +struct vfio_iommu_type1_bind *bind) +{ +int ret = -EINVAL ; +unsigned long minsz; +struct mm_struct *mm; +struct vfio_mm *vfio_mm; +struct vfio_iommu_type1_bind_process params; + + minsz =sizeof (*bind) +sizeof (params); +if (bind->argsz < minsz) +return -EINVAL ; + + arg +=sizeof (*bind); +if (copy_from_user (¶ms, arg,sizeof (params))) +return -EFAULT ; + +if (params.flags & ~VFIO_IOMMU_BIND_PID ) +return -EINVAL ; + +/* +* We can't simply call unbind with the PASID, because the process might +* have died and the PASID might have been reallocated to another +* process. Instead we need to fetch that process mm by PID again to +* make sure we remove the right vfio_mm. +*/ +if (params.flags &VFIO_IOMMU_BIND_PID ) { + mm =vfio_iommu_get_mm_by_vpid (params.pid); +if (IS_ERR (mm)) +return PTR_ERR (mm); + }else { + mm =get_task_mm (current); +if (!mm) +return -EINVAL ; + } + + ret = -ESRCH ; +mutex_lock (&iommu->lock); +list_for_each_entry (vfio_mm, &iommu->mm_list, next) { +if (vfio_mm->mm == mm) { +vfio_iommu_unbind (iommu, vfio_mm); +list_del (&vfio_mm->next); +kfree (vfio_mm); + ret =0 ; +break ; + } + } +mutex_unlock (&iommu->lock); +mmput (mm); + +return ret; +} +static long vfio_iommu_type1_ioctl (void *iommu_data,unsigned int cmd,unsigned long arg) {@@ -1728,6 +2097,44 @@ static long vfio_iommu_type1_ioctl(void *iommu_data,return copy_to_user ((void __user *)arg, &unmap, minsz) ? -EFAULT :0 ; + + }else if (cmd ==VFIO_IOMMU_BIND ) { +struct vfio_iommu_type1_bind bind; + + minsz = offsetofend(struct vfio_iommu_type1_bind, flags); +
drivers/vfio/vfio_iommu_type1.c static long vfio_iommu_type1_ioctl(void *iommu_data, · line 2115
Quoted 10 lines
drivers/vfio/vfio_iommu_type1.c +10
-0+if (copy_from_user (&bind, (void __user *)arg, minsz)) +return -EFAULT ; + +if (bind.argsz < minsz) +return -EINVAL ; + +switch (bind.flags) { +case VFIO_IOMMU_BIND_PROCESS : +return vfio_iommu_type1_bind_process (iommu, (void *)arg, + &bind);
Can we combine these two blocks given it is only this case statement that is different?
Quoted 112 lines
drivers/vfio/vfio_iommu_type1.c +22
-0+default : +return -EINVAL ; + } + + }else if (cmd ==VFIO_IOMMU_UNBIND ) { +struct vfio_iommu_type1_bind bind; + + minsz = offsetofend(struct vfio_iommu_type1_bind, flags); + +if (copy_from_user (&bind, (void __user *)arg, minsz)) +return -EFAULT ; + +if (bind.argsz < minsz) +return -EINVAL ; + +switch (bind.flags) { +case VFIO_IOMMU_BIND_PROCESS : +return vfio_iommu_type1_unbind_process (iommu, (void *)arg, + &bind); +default : +return -EINVAL ; + } }return -ENOTTY ;include/uapi/linux/vfio.h +76
-0@@ -665,6 +665,82 @@ struct vfio_iommu_type1_dma_unmap {#define VFIO_IOMMU_ENABLE _IO(VFIO_TYPE, VFIO_BASE + 15)#define VFIO_IOMMU_DISABLE _IO(VFIO_TYPE, VFIO_BASE + 16) +/* +* VFIO_IOMMU_BIND_PROCESS +* +* Allocate a PASID for a process address space, and use it to attach this +* process to all devices in the container. Devices can then tag their DMA +* traffic with the returned @pasid to perform transactions on the associated +* virtual address space. Mapping and unmapping buffers is performed by standard +* functions such as mmap and malloc. +* +* If flag is VFIO_IOMMU_BIND_PID, @pid contains the pid of a foreign process to +* bind. Otherwise the current task is bound. Given that the caller owns the +* device, setting this flag grants the caller read and write permissions on the +* entire address space of foreign process described by @pid. Therefore, +* permission to perform the bind operation on a foreign process is governed by +* the ptrace access mode PTRACE_MODE_ATTACH_REALCREDS check. See man ptrace(2) +* for more information. +* +* On success, VFIO writes a Process Address Space ID (PASID) into @pasid. This +* ID is unique to a process and can be used on all devices in the container. +* +* On fork, the child inherits the device fd and can use the bonds setup by its +* parent. Consequently, the child has R/W access on the address spaces bound by +* its parent. After an execv, the device fd is closed and the child doesn't +* have access to the address space anymore. +* +* To remove a bond between process and container, VFIO_IOMMU_UNBIND ioctl is +* issued with the same parameters. If a pid was specified in VFIO_IOMMU_BIND, +* it should also be present for VFIO_IOMMU_UNBIND. Otherwise unbind the current +* task from the container. +*/ +struct vfio_iommu_type1_bind_process { +__u32 flags; +#define VFIO_IOMMU_BIND_PID (1 << 0) +__u32 pasid; +__s32 pid; +}; + +/* +* Only mode supported at the moment is VFIO_IOMMU_BIND_PROCESS, which takes +* vfio_iommu_type1_bind_process in data. +*/ +struct vfio_iommu_type1_bind { +__u32 argsz; +__u32 flags; +#define VFIO_IOMMU_BIND_PROCESS (1 << 0) +__u8 data[]; +}; + +/* +* VFIO_IOMMU_BIND - _IOWR(VFIO_TYPE, VFIO_BASE + 22, struct vfio_iommu_bind) +* +* Manage address spaces of devices in this container. Initially a TYPE1 +* container can only have one address space, managed with +* VFIO_IOMMU_MAP/UNMAP_DMA. +* +* An IOMMU of type VFIO_TYPE1_NESTING_IOMMU can be managed by both MAP/UNMAP +* and BIND ioctls at the same time. MAP/UNMAP acts on the stage-2 (host) page +* tables, and BIND manages the stage-1 (guest) page tables. Other types of +* IOMMU may allow MAP/UNMAP and BIND to coexist, where MAP/UNMAP controls +* non-PASID traffic and BIND controls PASID traffic. But this depends on the +* underlying IOMMU architecture and isn't guaranteed. +* +* Availability of this feature depends on the device, its bus, the underlying +* IOMMU and the CPU architecture. +* +* returns: 0 on success, -errno on failure. +*/ +#define VFIO_IOMMU_BIND _IO(VFIO_TYPE, VFIO_BASE + 22) + +/* +* VFIO_IOMMU_UNBIND - _IOWR(VFIO_TYPE, VFIO_BASE + 23, struct vfio_iommu_bind) +* +* Undo what was done by the corresponding VFIO_IOMMU_BIND ioctl. +*/ +#define VFIO_IOMMU_UNBIND _IO(VFIO_TYPE, VFIO_BASE + 23) +/* -------- Additional API for SPAPR TCE (Server POWERPC) IOMMU -------- */ /*
Re: [PATCH v2 13/40] vfio: Add support for Shared Virtual Addressing
Quoted 20 lines · Jonathan Cameron
Quoted 20 lines
drivers/vfio/vfio_iommu_type1.c +20
-0+static int vfio_iommu_bind_group (struct vfio_iommu *iommu, +struct vfio_group *group, +struct vfio_mm *vfio_mm) +{ +int ret; +bool enabled_sva = false; +struct vfio_iommu_sva_bind_data data = { + .vfio_mm = vfio_mm, + .iommu = iommu, + .count =0 , + }; + +if (!group->sva_enabled) { + ret =iommu_group_for_each_dev (group->iommu_group,NULL , + vfio_iommu_sva_init); +if (ret) +return ret; + + group->sva_enabled = enabled_sva = true; + }
Quoted 8 lines
drivers/vfio/vfio_iommu_type1.c static int vfio_iommu_replay(struct vfio_iommu *iommu, · line 1331
Quoted 4 lines
drivers/vfio/vfio_iommu_type1.c +4
-0+ + ret =iommu_group_for_each_dev (group->iommu_group, &data, + vfio_iommu_sva_bind_dev); +if (ret && data.count >1 )Are we safe to run this even if data.count == 1? I assume that at that point we would always not have an iommu domain associated with the device so the initial check would error out.
If data.count == 1, then the first bind didn't succeed. But it's not necessarily a domain missing, failure to bind may come from various places. If this vfio_mm was already bound to another device then it contains a valid PASID and it's safe to call unbind(). Otherwise we call it with PASID -1 (VFIO_INVALID_PASID) and that's a bit of a grey area. -1 is currently invalid everywhere, but in the future someone might implement 32 bits of PASIDs, in which case a bond between this dev and PASID -1 might exist. But I think it's safe for now, and whoever redefines VFIO_INVALID_PASID when such implementation appears will also fix this case.
Quoted 3 lines
Just be nice to get rid of the special casing in this error path as then could just do it all under if (ret) and make it visually clearer these are different aspects of the same error path.
[...]
Quoted 17 lines
Quoted 17 lines
static long vfio_iommu_type1_ioctl(void
*iommu_data,drivers/vfio/vfio_iommu_type1.c +10
-0unsigned int cmd, unsigned long arg) {@@ -1728,6 +2097,44 @@ static long vfio_iommu_type1_ioctl(void *iommu_data,return copy_to_user ((void __user *)arg, &unmap, minsz) ? -EFAULT :0 ; + + }else if (cmd ==VFIO_IOMMU_BIND ) { +struct vfio_iommu_type1_bind bind; + + minsz = offsetofend(struct vfio_iommu_type1_bind, flags); + +if (copy_from_user (&bind, (void __user *)arg, minsz)) +return -EFAULT ; + +if (bind.argsz < minsz)
Quoted 8 lines
drivers/vfio/vfio_iommu_type1.c static long vfio_iommu_type1_ioctl(void *iommu_data, · line 2115
Quoted 6 lines
drivers/vfio/vfio_iommu_type1.c +6
-0+return -EINVAL ; + +switch (bind.flags) { +case VFIO_IOMMU_BIND_PROCESS : +return vfio_iommu_type1_bind_process (iommu, (void *)arg, + &bind);Can we combine these two blocks given it is only this case statement that is different?
That would be nicer, though I don't know yet what's needed for vSVA (by Yi Liu on Cc), which will add statements to the switches.
Thanks,
Jean
Quoted 24 lines
Quoted 24 lines
drivers/vfio/vfio_iommu_type1.c +22
-0+default : +return -EINVAL ; + } + + }else if (cmd ==VFIO_IOMMU_UNBIND ) { +struct vfio_iommu_type1_bind bind; + + minsz = offsetofend(struct vfio_iommu_type1_bind, flags); + +if (copy_from_user (&bind, (void __user *)arg, minsz)) +return -EFAULT ; + +if (bind.argsz < minsz) +return -EINVAL ; + +switch (bind.flags) { +case VFIO_IOMMU_BIND_PROCESS : +return vfio_iommu_type1_unbind_process (iommu, (void *)arg, + &bind); +default : +return -EINVAL ; + } }
Re: [PATCH v2 13/40] vfio: Add support for Shared Virtual Addressing
In reply to Jean-Philippe BruckerHi,
Quoted 20 lines · Jean-Philippe Brucker
Add two new ioctls for VFIO containers.
VFIO_IOMMU_BIND_PROCESScreates a bond between a container and a process address space, identified by a Process Address Space ID (PASID). Devices in the container append this PASID to DMA transactions in order to access the process' address space. The process page tables are shared with the IOMMU, and mechanisms such as PCI ATS/PRI are used to handle faults.VFIO_IOMMU_UNBIND_PROCESSremoves a bond created withVFIO_IOMMU_BIND_PROCESS.Signed-off-by Jean-Philippe Brucker <[email protected]>drivers/vfio/vfio_iommu_type1.c +5
-0+static int vfio_iommu_bind_group (struct vfio_iommu *iommu, +struct vfio_group *group, +struct vfio_mm *vfio_mm) +{ +int ret;
drivers/vfio/vfio_iommu_type1.c static int vfio_iommu_replay(struct vfio_iommu *iommu, · line 1322
Quoted 10 lines
drivers/vfio/vfio_iommu_type1.c +10
-0+bool enabled_sva = false; +struct vfio_iommu_sva_bind_data data = { + .vfio_mm = vfio_mm, + .iommu = iommu, + .count =0 , + }; + +if (!group->sva_enabled) { + ret =iommu_group_for_each_dev (group->iommu_group,NULL , + vfio_iommu_sva_init);
Do we need to do *sva_init here or do anything to avoid repeated initiation?
while another process already did initiation at this device, I think that current process will get an EEXIST.
Thanks.
Quoted 34 lines
drivers/vfio/vfio_iommu_type1.c +23
-0+if (ret) +return ret; + + group->sva_enabled = enabled_sva = true; + } + + ret =iommu_group_for_each_dev (group->iommu_group, &data, + vfio_iommu_sva_bind_dev); +if (ret && data.count >1 ) +iommu_group_for_each_dev (group->iommu_group, vfio_mm, + vfio_iommu_sva_unbind_dev); +if (ret && enabled_sva) { +iommu_group_for_each_dev (group->iommu_group,NULL , + vfio_iommu_sva_shutdown); + group->sva_enabled = false; + } + +return ret; +}@@ -1442,6 +1636,10 @@ static int vfio_iommu_type1_attach_group(void *iommu_data,if (ret) goto out_detach; + ret =vfio_iommu_replay_bind (iommu, group); +if (ret) + goto out_detach; +if (resv_msi) { ret =iommu_get_msi_cookie (domain->domain, resv_msi_base);if (ret)
drivers/vfio/vfio_iommu_type1.c static void vfio_iommu_type1_detach_group(void *iommu_data, · line 1752
Quoted 8 lines
drivers/vfio/vfio_iommu_type1.c +5
-0@@ -1547,6 +1745,11 @@ static void vfio_iommu_type1_detach_group(void *iommu_data,continue ;iommu_detach_group (domain->domain, iommu_group); +if (group->sva_enabled) { +iommu_group_for_each_dev (iommu_group,NULL , + vfio_iommu_sva_shutdown); + group->sva_enabled = false; + }
Here, why shut down here? If another process is working on the device, there may be a crash?
Thanks.
Quoted 5 lines
drivers/vfio/vfio_iommu_type1.c +0
-0list_del (&group->next);kfree (group);/* @@ -1562,6 +1765,7 @@ static void vfio_iommu_type1_detach_group(void *iommu_data,
Re: [PATCH v2 13/40] vfio: Add support for Shared Virtual Addressing
Hi,
Quoted 11 lines · Xu Zaibo
Quoted 11 lines
drivers/vfio/vfio_iommu_type1.c +11
-0+static int vfio_iommu_bind_group (struct vfio_iommu *iommu, + struct vfio_group *group, + struct vfio_mm *vfio_mm) +{ + int ret; + bool enabled_sva = false; + struct vfio_iommu_sva_bind_data data = { + .vfio_mm = vfio_mm, + .iommu = iommu, + .count = 0, + };
Quoted 8 lines
Quoted 4 lines
+
+ if (!group->sva_enabled) {
+ ret = iommu_group_for_each_dev(group->iommu_group, NULL, +vfio_iommu_sva_init);Do we need to do
*sva_inithere or do anything to avoid repeated initiation?
while another process already did initiation at this device, I think that current process will get an EEXIST.
Right, sva_init() must be called once for any device that intends to use bind(). For the second process though, group->sva_enabled will be true so we won't call sva_init() again, only bind().
Thanks,
Jean
_______________________________________________
iommu mailing list
[email protected]
https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [PATCH v2 13/40] vfio: Add support for Shared Virtual Addressing
Quoted 22 lines · Jean-Philippe Brucker
Hi,
Quoted 11 lines · Xu Zaibo
Quoted 11 lines
drivers/vfio/vfio_iommu_type1.c +11
-0+static int vfio_iommu_bind_group (struct vfio_iommu *iommu, +struct vfio_group *group, +struct vfio_mm *vfio_mm) +{ +int ret; +bool enabled_sva = false; +struct vfio_iommu_sva_bind_data data = { + .vfio_mm = vfio_mm, + .iommu = iommu, + .count =0 , + };Quoted 8 lines
drivers/vfio/vfio_iommu_type1.c static int vfio_iommu_replay(struct vfio_iommu *iommu, · line 1322
Quoted 4 lines
drivers/vfio/vfio_iommu_type1.c +4
-0+ +if (!group->sva_enabled) { + ret =iommu_group_for_each_dev (group->iommu_group,NULL , + vfio_iommu_sva_init);Do we need to do
*sva_inithere or do anything to avoid repeated initiation?
while another process already did initiation at this device, I think that current process will get an EEXIST.
Quoted 4 lines
Right,
sva_init()must be called once for any device that intends to usebind(). For the second process though,group->sva_enabledwill be true so we won't callsva_init()again, onlybind().
Well, while I create mediated devices based on one parent device to support multiple processes(a new process will create a new 'vfio_group' for the corresponding mediated device, and 'sva_enabled' cannot work any more), in fact, *sva_init and *sva_shutdown are basically working on parent device, so, as a result, I just only need sva initiation and shutdown on the parent device only once. So I change the two as following:
/@@ -551,8 +565,18 @@ int iommu_sva_device_init(struct device *dev, unsigned long features,/
if (features & ~IOMMU_SVA_FEAT_IOPF)
return -EINVAL;
/+ /* If already exists, do nothing *///
//+ mutex_lock(&dev->iommu_param->lock);//
//+ if (dev->iommu_param->sva_param) {//
//+ mutex_unlock(&dev->iommu_param->lock);//
//+ return 0;//
//+ }//
//+ mutex_unlock(&dev->iommu_param->lock);//
////
// if (features & IOMMU_SVA_FEAT_IOPF) {//
// ret = iommu_register_device_fault_handler(dev, iommu_queue_iopf,///// /
//
//
//@@ -621,6 +646,14 @@ int iommu_sva_device_shutdown(struct device *dev)//
// if (!domain)//
// return -ENODEV;//
////
//+ /* If any other process is working on the device, shut down does nothing. *///
//+ mutex_lock(&dev->iommu_param->lock);//
//+ if (!list_empty(&dev->iommu_param->sva_param->mm_list)) {////+ mutex_unlock(&dev->iommu_param->lock);//
//+ return 0;//
//+ }//
//+ mutex_unlock(&dev->iommu_param->lock);//
//+//
// __iommu_sva_unbind_dev_all(dev);//
////
// mutex_lock(&dev->iommu_param->lock);/
I add the above two checkings in both *sva_init and *sva_shutdown, it is working now, but i don't know if it will cause any new problems. What's more, i doubt if it is reasonable to check this to avoid repeating operation in VFIO, maybe it is better to check in IOMMU. :)
Thanks
Zaibo
Quoted 3 lines
.
Re: [PATCH v2 13/40] vfio: Add support for Shared Virtual Addressing
Quoted 27 lines · Xu Zaibo
Quoted 3 lines
Right,
sva_init()must be called once for any device that intends to usebind(). For the second process though,group->sva_enabledwill be true so we won't callsva_init()again, onlybind().Well, while I create mediated devices based on one parent device to support multiple processes(a new process will create a new '
vfio_group' for the corresponding mediated device, and 'sva_enabled' cannot work any more), in fact,*sva_initand*sva_shutdownare basically working on parent device, so, as a result, I just only need sva initiation and shutdown on the parent device only once. So I change the two as following:(quoted diff) +7
-0@@ -551,8 +565,18 @@ int iommu_sva_device_init(struct device *dev, unsigned long features, if (features & ~IOMMU_SVA_FEAT_IOPF) return -EINVAL; + /* If already exists, do nothing */ + mutex_lock(&dev->iommu_param->lock); + if (dev->iommu_param->sva_param) { + mutex_unlock(&dev->iommu_param->lock); + return 0; + } + mutex_unlock(&dev->iommu_param->lock); if (features & IOMMU_SVA_FEAT_IOPF) { ret = iommu_register_device_fault_handler(dev, iommu_queue_iopf,
Review int iommu_sva_device_shutdown(struct device *dev) · line 655
Quoted 10 lines
(quoted diff) +7
-0@@ -621,6 +646,14 @@ int iommu_sva_device_shutdown(struct device *dev) if (!domain) return -ENODEV; + /* If any other process is working on the device, shut down does nothing. */ + mutex_lock(&dev->iommu_param->lock); + if (!list_empty(&dev->iommu_param->sva_param->mm_list)) { + mutex_unlock(&dev->iommu_param->lock); + return 0; + } + mutex_unlock(&dev->iommu_param->lock);
I don't think iommu-sva.c is the best place for this, it's probably better to implement an intermediate layer (the mediating driver), that calls iommu_sva_device_init() and iommu_sva_device_shutdown() once. Then vfio-pci would still call these functions itself, but for mdev the mediating driver keeps a refcount of groups, and calls device_shutdown() only when freeing the last mdev.
A device driver (non mdev in this example) expects to be able to free all its resources after sva_device_shutdown() returns. Imagine the mm_list isn't empty (mm_exit() is running late), and instead of waiting in unbind_dev_all() below, we return 0 immediately. Then the calling driver frees its resources, and the mm_exit callback along with private data passed to bind() disappear. If a mm_exit() is still running in parallel, then it will try to access freed data and corrupt memory. So in this function if mm_list isn't empty, the only thing we can do is wait.
Thanks,
Jean
Quoted 9 lines
drivers/vfio/vfio_iommu_type1.c +1
-0+__iommu_sva_unbind_dev_all (dev);mutex_lock (&dev->iommu_param->lock);I add the above two checkings in both
*sva_initand*sva_shutdown, it is working now, but i don't know if it will cause any new problems. What's more, i doubt if it is reasonable to check this to avoid repeating operation in VFIO, maybe it is better to check in IOMMU. :)
Re: [PATCH v2 13/40] vfio: Add support for Shared Virtual Addressing
Hi,
Quoted 44 lines · Jean-Philippe Brucker
Quoted 26 lines · Xu Zaibo
Quoted 3 lines
Right,
sva_init()must be called once for any device that intends to usebind(). For the second process though,group->sva_enabledwill be true so we won't callsva_init()again, onlybind().Well, while I create mediated devices based on one parent device to support multiple processes(a new process will create a new '
vfio_group' for the corresponding mediated device, and 'sva_enabled' cannot work any more), in fact,*sva_initand*sva_shutdownare basically working on parent device, so, as a result, I just only need sva initiation and shutdown on the parent device only once. So I change the two as following:(quoted diff) +7
-0@@ -551,8 +565,18 @@ int iommu_sva_device_init(struct device *dev, unsigned long features, if (features & ~IOMMU_SVA_FEAT_IOPF) return -EINVAL; + /* If already exists, do nothing */ + mutex_lock(&dev->iommu_param->lock); + if (dev->iommu_param->sva_param) { + mutex_unlock(&dev->iommu_param->lock); + return 0; + } + mutex_unlock(&dev->iommu_param->lock); if (features & IOMMU_SVA_FEAT_IOPF) { ret = iommu_register_device_fault_handler(dev, iommu_queue_iopf,Review int iommu_sva_device_shutdown(struct device *dev) · line 655
Quoted 10 lines
(quoted diff) +7
-0@@ -621,6 +646,14 @@ int iommu_sva_device_shutdown(struct device *dev) if (!domain) return -ENODEV; + /* If any other process is working on the device, shut down does nothing. */ + mutex_lock(&dev->iommu_param->lock); + if (!list_empty(&dev->iommu_param->sva_param->mm_list)) { + mutex_unlock(&dev->iommu_param->lock); + return 0; + } + mutex_unlock(&dev->iommu_param->lock);I don't think
iommu-sva.cis the best place for this, it's probably better to implement an intermediate layer (the mediating driver), that callsiommu_sva_device_init()andiommu_sva_device_shutdown()once. Then vfio-pci would still call these functions itself, but for mdev the mediating driver keeps a refcount of groups, and callsdevice_shutdown()only when freeing the last mdev.
Quoted 9 lines
A device driver (non mdev in this example) expects to be able to free all its resources after
sva_device_shutdown()returns. Imagine themm_listisn't empty (mm_exit()is running late), and instead of waiting inunbind_dev_all()below, we return 0 immediately. Then the calling driver frees its resources, and themm_exitcallback along with private data passed tobind()disappear. If amm_exit()is still running in parallel, then it will try to access freed data and corrupt memory. So in this function ifmm_listisn't empty, the only thing we can do is wait.
I still don't understand why we should 'unbind_dev_all', is it possible to do a 'unbind_dev_pasid'? Then we can do other things instead of waiting that user may not like. :)
Thanks
Zaibo
Quoted 15 lines
Quoted 10 lines
drivers/vfio/vfio_iommu_type1.c +1
-0+__iommu_sva_unbind_dev_all (dev);mutex_lock (&dev->iommu_param->lock);I add the above two checkings in both
*sva_initand*sva_shutdown, it is working now, but i don't know if it will cause any new problems. What's more, i doubt if it is reasonable to check this to avoid repeating operation in VFIO, maybe it is better to check in IOMMU. :).
Re: [PATCH v2 13/40] vfio: Add support for Shared Virtual Addressing
Quoted 56 lines · Xu Zaibo
Hi,
Quoted 44 lines · Jean-Philippe Brucker
Quoted 26 lines · Xu Zaibo
Quoted 3 lines
Right,
sva_init()must be called once for any device that intends to usebind(). For the second process though,group->sva_enabledwill be true so we won't callsva_init()again, onlybind().Well, while I create mediated devices based on one parent device to support multiple processes(a new process will create a new '
vfio_group' for the corresponding mediated device, and 'sva_enabled' cannot work any more), in fact,*sva_initand*sva_shutdownare basically working on parent device, so, as a result, I just only need sva initiation and shutdown on the parent device only once. So I change the two as following:(quoted diff) +7
-0@@ -551,8 +565,18 @@ int iommu_sva_device_init(struct device *dev, unsigned long features, if (features & ~IOMMU_SVA_FEAT_IOPF) return -EINVAL; + /* If already exists, do nothing */ + mutex_lock(&dev->iommu_param->lock); + if (dev->iommu_param->sva_param) { + mutex_unlock(&dev->iommu_param->lock); + return 0; + } + mutex_unlock(&dev->iommu_param->lock); if (features & IOMMU_SVA_FEAT_IOPF) { ret = iommu_register_device_fault_handler(dev, iommu_queue_iopf,Review int iommu_sva_device_shutdown(struct device *dev) · line 655
Quoted 10 lines
(quoted diff) +7
-0@@ -621,6 +646,14 @@ int iommu_sva_device_shutdown(struct device *dev) if (!domain) return -ENODEV; + /* If any other process is working on the device, shut down does nothing. */ + mutex_lock(&dev->iommu_param->lock); + if (!list_empty(&dev->iommu_param->sva_param->mm_list)) { + mutex_unlock(&dev->iommu_param->lock); + return 0; + } + mutex_unlock(&dev->iommu_param->lock);I don't think
iommu-sva.cis the best place for this, it's probably better to implement an intermediate layer (the mediating driver), that callsiommu_sva_device_init()andiommu_sva_device_shutdown()once. Then vfio-pci would still call these functions itself, but for mdev the mediating driver keeps a refcount of groups, and callsdevice_shutdown()only when freeing the last mdev.Quoted 9 lines
A device driver (non mdev in this example) expects to be able to free all its resources after
sva_device_shutdown()returns. Imagine themm_listisn't empty (mm_exit()is running late), and instead of waiting inunbind_dev_all()below, we return 0 immediately. Then the calling driver frees its resources, and themm_exitcallback along with private data passed tobind()disappear. If amm_exit()is still running in parallel, then it will try to access freed data and corrupt memory. So in this function ifmm_listisn't empty, the only thing we can do is wait.
Quoted 2 lines
I still don't understand why we should '
unbind_dev_all', is it possible to do a 'unbind_dev_pasid'?
Not in sva_device_shutdown(), it needs to clean up everything. For example you want to physically unplug the device, or assign it to a VM. To prevent any leak sva_device_shutdown() needs to remove all bonds. In theory there shouldn't be any, since either the driver did unbind_dev(), or all process exited. This is a safety net.
Quoted 1 line
Then we can do other things instead of waiting that user may not like. :)
They may not like it, but it's for their own good :) At the moment we're waiting that:
* All exit_mm() callback for this device have finished. If we don't wait then the caller will free the private data passed to bind and the mm_exit() callback while they are still being used.
* All page requests targeting this device are dealt with. If we don't wait then some requests, that are lingering in the IOMMU PRI queue, may hit the next contexts bound to this device, possibly in a different VM. It may not be too risky (though probably exploitable in some way), but is incredibly messy.
All of this is bounded in time, and normally should be over pretty fast unless the device driver's exit_mm() does something strange. If the driver did the right thing, there shouldn't be any wait here (although there may be one in unbind_dev() for the same reasons - prevent use after free).
Thanks,
Jean
Re: [PATCH v2 13/40] vfio: Add support for Shared Virtual Addressing
Hi,
Quoted 79 lines · Jean-Philippe Brucker
Quoted 56 lines · Xu Zaibo
Hi,
Quoted 44 lines · Jean-Philippe Brucker
Quoted 26 lines · Xu Zaibo
Quoted 3 lines
Right,
sva_init()must be called once for any device that intends to usebind(). For the second process though,group->sva_enabledwill be true so we won't callsva_init()again, onlybind().Well, while I create mediated devices based on one parent device to support multiple processes(a new process will create a new '
vfio_group' for the corresponding mediated device, and 'sva_enabled' cannot work any more), in fact,*sva_initand*sva_shutdownare basically working on parent device, so, as a result, I just only need sva initiation and shutdown on the parent device only once. So I change the two as following:(quoted diff) +7
-0@@ -551,8 +565,18 @@ int iommu_sva_device_init(struct device *dev, unsigned long features, if (features & ~IOMMU_SVA_FEAT_IOPF) return -EINVAL; + /* If already exists, do nothing */ + mutex_lock(&dev->iommu_param->lock); + if (dev->iommu_param->sva_param) { + mutex_unlock(&dev->iommu_param->lock); + return 0; + } + mutex_unlock(&dev->iommu_param->lock); if (features & IOMMU_SVA_FEAT_IOPF) { ret = iommu_register_device_fault_handler(dev, iommu_queue_iopf,Review int iommu_sva_device_shutdown(struct device *dev) · line 655
Quoted 10 lines
(quoted diff) +7
-0@@ -621,6 +646,14 @@ int iommu_sva_device_shutdown(struct device *dev) if (!domain) return -ENODEV; + /* If any other process is working on the device, shut down does nothing. */ + mutex_lock(&dev->iommu_param->lock); + if (!list_empty(&dev->iommu_param->sva_param->mm_list)) { + mutex_unlock(&dev->iommu_param->lock); + return 0; + } + mutex_unlock(&dev->iommu_param->lock);I don't think
iommu-sva.cis the best place for this, it's probably better to implement an intermediate layer (the mediating driver), that callsiommu_sva_device_init()andiommu_sva_device_shutdown()once. Then vfio-pci would still call these functions itself, but for mdev the mediating driver keeps a refcount of groups, and callsdevice_shutdown()only when freeing the last mdev.Quoted 9 lines
A device driver (non mdev in this example) expects to be able to free all its resources after
sva_device_shutdown()returns. Imagine themm_listisn't empty (mm_exit()is running late), and instead of waiting inunbind_dev_all()below, we return 0 immediately. Then the calling driver frees its resources, and themm_exitcallback along with private data passed tobind()disappear. If amm_exit()is still running in parallel, then it will try to access freed data and corrupt memory. So in this function ifmm_listisn't empty, the only thing we can do is wait.Quoted 2 lines
I still don't understand why we should '
unbind_dev_all', is it possible to do a 'unbind_dev_pasid'?Not in
sva_device_shutdown(), it needs to clean up everything. For example you want to physically unplug the device, or assign it to a VM. To prevent any leaksva_device_shutdown()needs to remove all bonds. In theory there shouldn't be any, since either the driver didunbind_dev(), or all process exited. This is a safety net.Quoted 1 line
Then we can do other things instead of waiting that user may not like. :)
They may not like it, but it's for their own good :) At the moment we're waiting that:
* All
exit_mm()callback for this device have finished. If we don't wait then the caller will free the private data passed to bind and themm_exit()callback while they are still being used.* All page requests targeting this device are dealt with. If we don't wait then some requests, that are lingering in the IOMMU PRI queue, may hit the next contexts bound to this device, possibly in a different VM. It may not be too risky (though probably exploitable in some way), but is incredibly messy.
Quoted 6 lines
All of this is bounded in time, and normally should be over pretty fast unless the device driver's
exit_mm()does something strange. If the driver did the right thing, there shouldn't be any wait here (although there may be one inunbind_dev()for the same reasons - prevent use after free).
I guess there may be some misunderstandings :).
From the current patches, '/iommu_sva_device_shutdown/' is called by '/vfio_iommu_sva_shutdown/', which is mainly used by '/vfio_iommu_type1_detach_group/' that is finally called by processes' release of vfio facilitiy automatically or called by 'VFIO_GROUP_UNSET_CONTAINER' manually in the processes.
So, just image that 2 processes is working on the device with IOPF feature, and the 2 do the following to enable SVA:
/ 1.open("/dev/vfio/vfio") ;//
//
// 2.open the group of the devcie by calling open("/dev/vfio/x"), but now, //
// I think the second processes will fail to open the group because current VFIO thinks that one group only can be used by one process/vm,
at present, I use mediated device to create more groups on the
parent device to support multiple processes;//
/ //
/ 3.VFIO_GROUP_SET_CONTAINER;/
/ 4.VFIO_SET_IOMMU;
/
/// 5.VFIO_IOMMU_BIND;
6.Do some works with the hardware working unit filled by PASID on the device;
7.//VFIO_IOMMU_UNBIND;
//***8.VFIO_GROUP_UNSET_CONTAINER;---here, have to sleep to wait another process to finish works of the step 6;
* 9. close(group); close(containner);
/So, my idea is: If it is possible to just release the params or facilities that only belong to the current process while the process shutdown the device, and while the last process releases them all. Then, as in the above step 8, we
don't need to wait, or maybe wait for a very long time if the other process is doing lots of work on the device.
Thanks
Zaibo
Quoted 1 line
Re: [PATCH v2 13/40] vfio: Add support for Shared Virtual Addressing
(If possible, please reply in plain text to the list. Reading this in a text-only reader is confusing, because all quotes have the same level)
Quoted 10 lines · Xu Zaibo
I guess there may be some misunderstandings :).
From the current patches, '
iommu_sva_device_shutdown' is called by 'vfio_iommu_sva_shutdown', which is mainly used by 'vfio_iommu_type1_detach_group' that is finally called by processes' release of vfio facilitiy automatically or called by 'VFIO_GROUP_UNSET_CONTAINER' manually in the processes.So, just image that 2 processes is working on the device with IOPF feature, and the 2 do the following to enable SVA:
1.open("/dev/vfio/vfio") ;
Quoted 9 lines
2.open the group of the devcie by calling open("/dev/vfio/x"), but now,
I think the second processes will fail to open the group because current VFIO thinks that one group only can be used by one process/vm,
at present, I use mediated device to create more groups on the parent device to support multiple processes;
3.VFIO_GROUP_SET_CONTAINER;
4.VFIO_SET_IOMMU;
5.VFIO_IOMMU_BIND;I have a concern regarding your driver. With mdev you can't allow processes to program the PASID themselves, since the parent device has a single PASID table. You lose all isolation since processes could write any value in the PASID field and access address spaces of other processes bound to this parent device (even if the BIND call was for other mdevs).
The wrap driver has to mediate calls to bind(), and either program the PASID into the device itself, or at least check that, when receiving a SET_PASID ioctl from a process, the given PASID was actually allocated to the process.
Quoted 9 lines
6.Do some works with the hardware working unit filled by PASID on the device;7.VFIO_IOMMU_UNBIND; 8.VFIO_GROUP_UNSET_CONTAINER;---here, have to sleep to wait another process to finish works of the step 6; 9. close(group); close(containner);
Quoted 3 lines
So, my idea is: If it is possible to just release the params or facilities that only belong to the current process while the process shutdown the device, and while the last process releases them all. Then, as in the above step 8, we
don't need to wait, or maybe wait for a very long time if the other process is doing lots of work on the device.
Given that you need to notify the mediating driver of IOMMU_BIND calls as explained above, you could do something similar for shutdown: from the mdev driver, call iommu_sva_shutdown_device() only for the last mdev.
Thanks,
Jean
Re: [PATCH v2 13/40] vfio: Add support for Shared Virtual Addressing
Hi,
Quoted 2 lines · Jean-Philippe Brucker
(If possible, please reply in plain text to the list. Reading this in a text-only reader is confusing, because all quotes have the same level)
Sorry for that, I have reset the thunderbird, :) thanks.
Quoted 19 lines · Xu Zaibo
Quoted 18 lines
I guess there may be some misunderstandings :).
From the current patches, '
iommu_sva_device_shutdown' is called by 'vfio_iommu_sva_shutdown', which
is mainly used by 'vfio_iommu_type1_detach_group' that is finally called by processes' release of vfio facilitiy automatically or called by 'VFIO_GROUP_UNSET_CONTAINER' manually in the processes.So, just image that 2 processes is working on the device with IOPF feature, and the 2 do the following to enable SVA:
1.open("/dev/vfio/vfio") ; 2.open the group of the devcie by calling open("/dev/vfio/x"), but now, I think the second processes will fail to open the group because current VFIO thinks that one group only can be used by one process/vm, at present, I use mediated device to create more groups on the parent device to support multiple processes; 3.VFIO_GROUP_SET_CONTAINER; 4.VFIO_SET_IOMMU;
Quoted 7 lines
Quoted 1 line
5.VFIO_IOMMU_BIND;I have a concern regarding your driver. With mdev you can't allow processes to program the PASID themselves, since the parent device has a single PASID table. You lose all isolation since processes could write any value in the PASID field and access address spaces of other processes bound to this parent device (even if the BIND call was for other mdevs).
Yes, if wrapdrive do nothing on this PASID setting procedure in kernel space, I think there definitely exists this security risk.
Quoted 4 lines
The wrap driver has to mediate calls to
bind(), and either program the PASID into the device itself, or at least check that, when receiving aSET_PASIDioctl from a process, the given PASID was actually allocated to the process.
Yes, good advice, thanks.
Quoted 10 lines
Quoted 10 lines
6.Do some works with the hardware working unit filled by PASID on the device; 7.VFIO_IOMMU_UNBIND; 8.VFIO_GROUP_UNSET_CONTAINER;---here, have to sleep to wait another process to finish works of the step 6; 9. close(group); close(containner);
Quoted 6 lines
Quoted 3 lines
So, my idea is: If it is possible to just release the params or facilities that only belong to the current process while the process shutdown the device, and while the last process releases them all. Then, as in the above step 8, we
don't need to wait, or maybe wait for a very long time if the other process is doing lots of work on the device.Given that you need to notify the mediating driver of
IOMMU_BINDcalls as explained above, you could do something similar for shutdown: from the mdev driver, calliommu_sva_shutdown_device()only for the last mdev.
Currently, I add an API to check if it is the last mdev in wrapdrive, as vfio shutdowns the device, it call the API to do the check at first.
Thanks
Zaibo
_______________________________________________
iommu mailing list
[email protected]
https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [PATCH v2 13/40] vfio: Add support for Shared Virtual Addressing
In reply to Jean-Philippe BruckerHi Jean,
Quoted 32 lines · Jean-Philippe Brucker
include/uapi/linux/vfio.h +24
-0@@ -665,6 +665,82 @@ struct vfio_iommu_type1_dma_unmap {#define VFIO_IOMMU_ENABLE _IO(VFIO_TYPE, VFIO_BASE + 15)#define VFIO_IOMMU_DISABLE _IO(VFIO_TYPE, VFIO_BASE + 16) +/* +* VFIO_IOMMU_BIND_PROCESS +* +* Allocate a PASID for a process address space, and use it to attach this +* process to all devices in the container. Devices can then tag their DMA +* traffic with the returned @pasid to perform transactions on the associated +* virtual address space. Mapping and unmapping buffers is performed by standard +* functions such as mmap and malloc. +* +* If flag is VFIO_IOMMU_BIND_PID, @pid contains the pid of a foreign process to +* bind. Otherwise the current task is bound. Given that the caller owns the +* device, setting this flag grants the caller read and write permissions on the +* entire address space of foreign process described by @pid. Therefore, +* permission to perform the bind operation on a foreign process is governed by +* the ptrace access mode PTRACE_MODE_ATTACH_REALCREDS check. See man ptrace(2) +* for more information. +* +* On success, VFIO writes a Process Address Space ID (PASID) into @pasid. This +* ID is unique to a process and can be used on all devices in the container. +* +* On fork, the child inherits the device fd and can use the bonds setup by its +* parent. Consequently, the child has R/W access on the address spaces bound by +* its parent. After an execv, the device fd is closed and the child doesn't +* have access to the address space anymore.
include/uapi/linux/vfio.h struct vfio_iommu_type1_dma_unmap { · line 701
Quoted 10 lines
include/uapi/linux/vfio.h +10
-0+* +* To remove a bond between process and container, VFIO_IOMMU_UNBIND ioctl is +* issued with the same parameters. If a pid was specified in VFIO_IOMMU_BIND, +* it should also be present for VFIO_IOMMU_UNBIND. Otherwise unbind the current +* task from the container. +*/ +struct vfio_iommu_type1_bind_process { +__u32 flags; +#define VFIO_IOMMU_BIND_PID (1 << 0) +__u32 pasid;
As I am doing some works on the SVA patch set. I just consider why the user space need this pasid. Maybe, is it much more reasonable to set the pasid into all devices under the vfio container by a call back function from 'vfio_devices' while 'VFIO_IOMMU_BIND_PROCESS' CMD is executed in kernel land? I am not sure because there exists no suitable call back in 'vfio_device' at present.
Thanks,
Zaibo
Quoted 45 lines
include/uapi/linux/vfio.h +42
-0+__s32 pid; +}; + +/* +* Only mode supported at the moment is VFIO_IOMMU_BIND_PROCESS, which takes +* vfio_iommu_type1_bind_process in data. +*/ +struct vfio_iommu_type1_bind { +__u32 argsz; +__u32 flags; +#define VFIO_IOMMU_BIND_PROCESS (1 << 0) +__u8 data[]; +}; + +/* +* VFIO_IOMMU_BIND - _IOWR(VFIO_TYPE, VFIO_BASE + 22, struct vfio_iommu_bind) +* +* Manage address spaces of devices in this container. Initially a TYPE1 +* container can only have one address space, managed with +* VFIO_IOMMU_MAP/UNMAP_DMA. +* +* An IOMMU of type VFIO_TYPE1_NESTING_IOMMU can be managed by both MAP/UNMAP +* and BIND ioctls at the same time. MAP/UNMAP acts on the stage-2 (host) page +* tables, and BIND manages the stage-1 (guest) page tables. Other types of +* IOMMU may allow MAP/UNMAP and BIND to coexist, where MAP/UNMAP controls +* non-PASID traffic and BIND controls PASID traffic. But this depends on the +* underlying IOMMU architecture and isn't guaranteed. +* +* Availability of this feature depends on the device, its bus, the underlying +* IOMMU and the CPU architecture. +* +* returns: 0 on success, -errno on failure. +*/ +#define VFIO_IOMMU_BIND _IO(VFIO_TYPE, VFIO_BASE + 22) + +/* +* VFIO_IOMMU_UNBIND - _IOWR(VFIO_TYPE, VFIO_BASE + 23, struct vfio_iommu_bind) +* +* Undo what was done by the corresponding VFIO_IOMMU_BIND ioctl. +*/ +#define VFIO_IOMMU_UNBIND _IO(VFIO_TYPE, VFIO_BASE + 23) +/* -------- Additional API for SPAPR TCE (Server POWERPC) IOMMU -------- */ /*
Re: [PATCH v2 13/40] vfio: Add support for Shared Virtual Addressing
Hi Zaibo,
Quoted 4 lines · Xu Zaibo
include/uapi/linux/vfio.h struct vfio_iommu_type1_dma_unmap { · line 701
Quoted 4 lines
include/uapi/linux/vfio.h +4
-0+struct vfio_iommu_type1_bind_process { + __u32 flags; +#define VFIO_IOMMU_BIND_PID (1 << 0) + __u32 pasid;
Quoted 8 lines
As I am doing some works on the SVA patch set. I just consider why the user space need this pasid.
Maybe, is it much more reasonable to set the pasid into all devices under the vfio container by
a call back function from 'vfio_devices' while 'VFIO_IOMMU_BIND_PROCESS' CMD is executed
in kernel land? I am not sure because there exists no suitable call back in 'vfio_device' at present.
When using vfio-pci, the kernel doesn't know how to program the PASID into the device because the only kernel driver for the device is the generic vfio-pci module. The PCI specification doesn't describe a way of setting up the PASID, it's vendor-specific. Only the userspace application owning the device (and calling VFIO_IOMMU_BIND) knows how to do it, so we return the allocated PASID.
Note that unlike vfio-mdev where applications share slices of a function, with vfio-pci one application owns the whole function so it's safe to let userspace set the PASID in hardware. With vfio-mdev it's the kernel driver that should be in charge of setting the PASID as you described, and we wouldn't have a reason to return the PASID in the vfio_iommu_type1_bind_process structure.
Thanks,
Jean
Re: [PATCH v2 13/40] vfio: Add support for Shared Virtual Addressing
Hi Jean,
Quoted 20 lines · Jean-Philippe Brucker
Quoted 4 lines · Xu Zaibo
include/uapi/linux/vfio.h struct vfio_iommu_type1_dma_unmap { · line 701
Quoted 4 lines
include/uapi/linux/vfio.h +4
-0+struct vfio_iommu_type1_bind_process { +__u32 flags; +#define VFIO_IOMMU_BIND_PID (1 << 0) +__u32 pasid;Quoted 8 lines
As I am doing some works on the SVA patch set. I just consider why the user space need this pasid.
Maybe, is it much more reasonable to set the pasid into all devices under the vfio container by
a call back function from 'vfio_devices' while 'VFIO_IOMMU_BIND_PROCESS' CMD is executed
in kernel land? I am not sure because there exists no suitable call back in 'vfio_device' at present.When using vfio-pci, the kernel doesn't know how to program the PASID into the device because the only kernel driver for the device is the generic vfio-pci module. The PCI specification doesn't describe a way of setting up the PASID, it's vendor-specific. Only the userspace application owning the device (and calling
VFIO_IOMMU_BIND) knows how to do it, so we return the allocated PASID.
Quoted 6 lines
Note that unlike vfio-mdev where applications share slices of a function, with vfio-pci one application owns the whole function so it's safe to let userspace set the PASID in hardware. With vfio-mdev it's the kernel driver that should be in charge of setting the PASID as you described, and we wouldn't have a reason to return the PASID in the
vfio_iommu_type1_bind_processstructure.
Understood. But I still get the following confusion:
As one application takes a whole function while using VFIO-PCI, why do the application and the function need to enable PASID capability? (Since just one I/O page table is enough for them.)
Maybe I misunderstood, hope you can help me clear it. Thank you very much.
Thanks,
Zaibo
.
Re: [PATCH v2 13/40] vfio: Add support for Shared Virtual Addressing
Quoted 4 lines · Xu Zaibo
As one application takes a whole function while using VFIO-PCI, why do the application and the
function need to enable PASID capability? (Since just one I/O page table is enough for them.)
At the moment the series doesn't provide support for SVA without PASID (on the I/O page fault path, 08/40). In addition the BIND ioctl could be used by the owner application to bind other processes (slaves) and perform sub-assignment. But that feature is incomplete because we don't send stop_pasid notification to the owner when a slave dies.
Thanks,
Jean
Re: [PATCH v2 13/40] vfio: Add support for Shared Virtual Addressing
Quoted 11 lines · Jean-Philippe Brucker
Quoted 4 lines · Xu Zaibo
As one application takes a whole function while using VFIO-PCI, why do the application and the
function need to enable PASID capability? (Since just one I/O page table is enough for them.)At the moment the series doesn't provide support for SVA without PASID (on the I/O page fault path, 08/40). In addition the BIND ioctl could be used by the owner application to bind other processes (slaves) and perform sub-assignment. But that feature is incomplete because we don't send
stop_pasidnotification to the owner when a slave dies.
So, Could I understand like this?
1. While the series are finished well, VFIO-PCI device can be held
by only one process
through binding IOCTL command without PASID (without PASID
being exposed user space).
2. While using VFIO-PCI device to support multiple processes with
SVA series, a primary
process with multiple secondary processes must be deployed just
like DPDK(https://www.dpdk.org/).
And, the PASID still has to be exposed to user land.
Thanks,
Zaibo
.
Re: [PATCH v2 13/40] vfio: Add support for Shared Virtual Addressing
Quoted 12 lines · Xu Zaibo
Quoted 11 lines · Jean-Philippe Brucker
Quoted 4 lines · Xu Zaibo
As one application takes a whole function while using VFIO-PCI, why do the application and the
function need to enable PASID capability? (Since just one I/O page table is enough for them.)At the moment the series doesn't provide support for SVA without PASID (on the I/O page fault path, 08/40). In addition the BIND ioctl could be used by the owner application to bind other processes (slaves) and perform sub-assignment. But that feature is incomplete because we don't send
stop_pasidnotification to the owner when a slave dies.
Quoted 6 lines
So, Could I understand like this?
1. While the series are finished well, VFIO-PCI device can be held
by only one process
through binding IOCTL command without PASID (without PASID
being exposed user space).
It could, but isn't supported at the moment. In addition to adding support in the I/O page fault code, we'd also need to update the VFIO API. Currently a VFIO_TYPE1 domain always supports the MAP/UNMAP ioctl. The case you describe isn't compatible with MAP/UNMAP, since the process manages the shared address space with mmap or malloc. We'd probably need to introduce a new VFIO IOMMU type, in which case the bind could be performed implicitly when the process does VFIO_SET_IOMMU. Then the process wouldn't need to send an additional BIND IOCTL.
Quoted 5 lines
2. While using VFIO-PCI device to support multiple processes with
SVA series, a primary
process with multiple secondary processes must be deployed just
like DPDK(https://www.dpdk.org/).
And, the PASID still has to be exposed to user land.
Right. A third case, also implemented by this patch (and complete), is the primary process simply doing a BIND for itself, and using the returned PASID to share its own address space with the device.
Thanks,
Jean
Re: [PATCH v2 13/40] vfio: Add support for Shared Virtual Addressing
Hi,
Quoted 19 lines · Jean-Philippe Brucker
Quoted 12 lines · Xu Zaibo
Quoted 11 lines · Jean-Philippe Brucker
Quoted 4 lines · Xu Zaibo
As one application takes a whole function while using VFIO-PCI, why do the application and the
function need to enable PASID capability? (Since just one I/O page table is enough for them.)At the moment the series doesn't provide support for SVA without PASID (on the I/O page fault path, 08/40). In addition the BIND ioctl could be used by the owner application to bind other processes (slaves) and perform sub-assignment. But that feature is incomplete because we don't send
stop_pasidnotification to the owner when a slave dies.Quoted 6 lines
So, Could I understand like this?
1. While the series are finished well, VFIO-PCI device can be held
by only one process
through binding IOCTL command without PASID (without PASID
being exposed user space).
Quoted 8 lines
It could, but isn't supported at the moment. In addition to adding support in the I/O page fault code, we'd also need to update the VFIO API. Currently a
VFIO_TYPE1domain always supports the MAP/UNMAP ioctl. The case you describe isn't compatible with MAP/UNMAP, since the process manages the shared address space with mmap or malloc. We'd probably need to introduce a new VFIO IOMMU type, in which case the bind could be performed implicitly when the process doesVFIO_SET_IOMMU. Then the process wouldn't need to send an additional BIND IOCTL.
ok. got it. This is the legacy mode, so all the VFIO APIs are kept unchanged?
Quoted 9 lines
Quoted 5 lines
2. While using VFIO-PCI device to support multiple processes with
SVA series, a primary
process with multiple secondary processes must be deployed just
like DPDK(https://www.dpdk.org/).
And, the PASID still has to be exposed to user land.Right. A third case, also implemented by this patch (and complete), is the primary process simply doing a BIND for itself, and using the returned PASID to share its own address space with the device.
ok. But I am worried that the sulotion of one primary processes with several secondary ones
is a little bit limited. Maybe, users don't want to depend on the primary process. :)
Thanks,
Zaibo
.
Re: [PATCH v2 13/40] vfio: Add support for Shared Virtual Addressing
Quoted 12 lines · Xu Zaibo
Quoted 4 lines
Quoted 4 lines
1. While the series are finished well, VFIO-PCI device can be held
by only one process
through binding IOCTL command without PASID (without PASID
being exposed user space).Quoted 8 lines
It could, but isn't supported at the moment. In addition to adding support in the I/O page fault code, we'd also need to update the VFIO API. Currently a
VFIO_TYPE1domain always supports the MAP/UNMAP ioctl. The case you describe isn't compatible with MAP/UNMAP, since the process manages the shared address space with mmap or malloc. We'd probably need to introduce a new VFIO IOMMU type, in which case the bind could be performed implicitly when the process doesVFIO_SET_IOMMU. Then the process wouldn't need to send an additional BIND IOCTL.
Quoted 2 lines
ok. got it. This is the legacy mode, so all the VFIO APIs are kept unchanged?
Yes, existing VFIO semantics are preserved
Quoted 9 lines
Quoted 9 lines
Quoted 5 lines
2. While using VFIO-PCI device to support multiple processes with
SVA series, a primary
process with multiple secondary processes must be deployed just
like DPDK(https://www.dpdk.org/).
And, the PASID still has to be exposed to user land.Right. A third case, also implemented by this patch (and complete), is the primary process simply doing a BIND for itself, and using the returned PASID to share its own address space with the device.
Quoted 5 lines
ok. But I am worried that the sulotion of one primary processes with several secondary ones
is a little bit limited. Maybe, users don't want to depend on the primary process. :)
I don't see a better way for vfio-pci, though. But more importantly, I don't know of any users :) While the feature is great for testing new hardware, and I've been using it for all kinds of stress testing, I haven't received feedback from possible users in production settings (DPDK etc) and can't speculate about what they'd prefer.
Thanks,
Jean
Re: [PATCH v2 13/40] vfio: Add support for Shared Virtual Addressing
Quoted 29 lines · Jean-Philippe Brucker
Quoted 12 lines · Xu Zaibo
Quoted 4 lines
Quoted 4 lines
1. While the series are finished well, VFIO-PCI device can be held
by only one process
through binding IOCTL command without PASID (without PASID
being exposed user space).Quoted 8 lines
It could, but isn't supported at the moment. In addition to adding support in the I/O page fault code, we'd also need to update the VFIO API. Currently a
VFIO_TYPE1domain always supports the MAP/UNMAP ioctl. The case you describe isn't compatible with MAP/UNMAP, since the process manages the shared address space with mmap or malloc. We'd probably need to introduce a new VFIO IOMMU type, in which case the bind could be performed implicitly when the process doesVFIO_SET_IOMMU. Then the process wouldn't need to send an additional BIND IOCTL.Quoted 2 lines
ok. got it. This is the legacy mode, so all the VFIO APIs are kept unchanged?
Yes, existing VFIO semantics are preserved
Quoted 12 lines
Quoted 9 lines
Quoted 5 lines
2. While using VFIO-PCI device to support multiple processes with
SVA series, a primary
process with multiple secondary processes must be deployed just
like DPDK(https://www.dpdk.org/).
And, the PASID still has to be exposed to user land.Right. A third case, also implemented by this patch (and complete), is the primary process simply doing a BIND for itself, and using the returned PASID to share its own address space with the device.
ok. But I am worried that the sulotion of one primary processes with several secondary ones
Quoted 8 lines
Quoted 2 lines
is a little bit limited. Maybe, users don't want to depend on the primary process. :)
I don't see a better way for vfio-pci, though. But more importantly, I don't know of any users :) While the feature is great for testing new hardware, and I've been using it for all kinds of stress testing, I haven't received feedback from possible users in production settings (DPDK etc) and can't speculate about what they'd prefer.
At present, It seems no other way existing while being compatible with current logic. Thank you.
Thanks,
Zaibo
PATCH v2 14/40 dt-bindings: document stall and PASID properties for IOMMU masters
In reply to Jean-Philippe BruckerOn ARM systems, some platform devices behind an IOMMU may support stall and PASID features. Stall is the ability to recover from page faults and PASID offers multiple process address spaces to the device. Together they allow to do paging with a device. Let the firmware tell us when a device supports stall and PASID.
--- .../devicetree/bindings/iommu/iommu.txt | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+)
Documentation/devicetree/bindings/iommu/iommu.txt +24 -0
@@ -86,6 +86,30 @@ have a means to turn off translation. But it is invalid in such cases to disable the IOMMU's device tree node in the first place because it would prevent any driver from properly setting up the translations. +Optional properties: +-------------------- +- dma-can-stall: When present, the master can wait for a transaction to + complete for an indefinite amount of time. Upon translation fault some + IOMMUs, instead of aborting the translation immediately, may first + notify the driver and keep the transaction in flight. This allows the OS + to inspect the fault and, for example, make physical pages resident + before updating the mappings and completing the transaction. Such IOMMU + accepts a limited number of simultaneous stalled transactions before + having to either put back-pressure on the master, or abort new faulting + transactions. + + Firmware has to opt-in stalling, because most buses and masters don't + support it. In particular it isn't compatible with PCI, where + transactions have to complete before a time limit. More generally it + won't work in systems and masters that haven't been designed for + stalling. For example the OS, in order to handle a stalled transaction, + may attempt to retrieve pages from secondary storage in a stalled + domain, leading to a deadlock. + +- pasid-num-bits: Some masters support multiple address spaces for DMA, by + tagging DMA transactions with an address space identifier. By default, + this is 0, which means that the device only has one address space. + Notes: ======
Signature · 1 line
2.17.0
PATCH v2 15/40 iommu/of: Add stall and pasid properties to iommu_fwspec
In reply to Jean-Philippe BruckerAdd stall and pasid properties to iommu_fwspec, and fill them when dma-can-stall and pasid-bits properties are present in the device tree.
--- drivers/iommu/of_iommu.c | 12 ++++++++++++ include/linux/iommu.h | 2 ++ 2 files changed, 14 insertions(+)
drivers/iommu/of_iommu.c +12 -0
@@ -204,6 +204,18 @@ const struct iommu_ops *of_iommu_configure(struct device *dev,if (err)break ; } + + fwspec = dev->iommu_fwspec; +if (!err && fwspec) { +const __be32 *prop; + +if (of_get_property (master_np,"dma-can-stall" ,NULL )) + fwspec->can_stall = true; + + prop =of_get_property (master_np,"pasid-num-bits" ,NULL ); +if (prop) + fwspec->num_pasid_bits =be32_to_cpu (*prop); + } }/*
include/linux/iommu.h +2 -0
@@ -656,6 +656,8 @@ struct iommu_fwspec {struct fwnode_handle *iommu_fwnode;void *iommu_priv;unsigned int num_ids; +unsigned int num_pasid_bits; +bool can_stall;u32 ids[1 ]; };
Signature · 1 line
2.17.0
PATCH v2 16/40 arm64: mm: Pin down ASIDs for sharing mm with devices
In reply to Jean-Philippe BruckerTo enable address space sharing with the IOMMU, introduce mm_context_get() and mm_context_put(), that pin down a context and ensure that it will keep its ASID after a rollover.
Pinning is necessary because a device constantly needs a valid ASID, unlike tasks that only require one when running. Without pinning, we would need to notify the IOMMU when we're about to use a new ASID for a task, and it would get complicated when a new task is assigned a shared ASID. Consider the following scenario with no ASID pinned:
1. Task t1 is running on CPUx with shared ASID (gen=1, asid=1)
2. Task t2 is scheduled on CPUx, gets ASID (1, 2)
3. Task tn is scheduled on CPUy, a rollover occurs, tn gets ASID (2, 1) We would now have to immediately generate a new ASID for t1, notify the IOMMU, and finally enable task tn. We are holding the lock during all that time, since we can't afford having another CPU trigger a rollover. The IOMMU issues invalidation commands that can take tens of milliseconds.
It gets needlessly complicated. All we wanted to do was schedule task tn, that has no business with the IOMMU. By letting the IOMMU pin tasks when needed, we avoid stalling the slow path, and let the pinning fail when we're out of shareable ASIDs.
After a rollover, the allocator expects at least one ASID to be available in addition to the reserved ones (one per CPU). So (NR_ASIDS - NR_CPUS -
1) is the maximum number of ASIDs that can be shared with the IOMMU.
---v1->v2: TLC found a bug in my code :) It was a bit silly.
When updating an mm's context after a rollover, we check if the ASID is pinned. If it is, then we can reuse it. But even then we do need to update the generation in the reserved_asid map. V1 didn't do that, so what happened was:
1. A task t1 is running with ASID (gen=1, asid=1) on CPU1 all along.
2. CPU2 triggers a rollover, but since t1 is running it keeps its ASID.
3. ASID 1 is pinned. t1 is scheduled on CPU2. The ASID allocator sees the ASID pinned, and skips the update of reserved_asids. t1 now has ASID (2, 1)
4. ASID 1 is unpinned. Another rollover. t1 is scheduled on CPU2. Since it is still running on CPU1, the allocator should keep reuse its ASID, but as it looks for ASID (2, 1) in reserved_asid, it finds (1, 1), and concludes that the task needs a new ASID. Woops.
The fix is simple: check and update reserved_asids *before* checking for pinned ASIDs. The bug was found this afternoon (after a 4h run), and there probably will be more. I restarted the validation but it might take a while or never finish -- I had to stop the penultimate run after 2 weeks, the parameters were too large. The last successful run was with only two generations and took 4:30 hours (on 4 Xeon E5-2660v4). This bug was found with 3 generations and a single pinned task.
You can find the asidalloc changes for kernel-tla here, temporarily:
http://jpbrucker.net/git/kernel-tla/commit/?id=b70361
http://jpbrucker.net/git/kernel-tla/commit/?id=f5413d
--- arch/arm64/include/asm/mmu.h | 1 + arch/arm64/include/asm/mmu_context.h | 11 +++- arch/arm64/mm/context.c | 92 ++++++++++++++++++++++++++-- 3 files changed, 99 insertions(+), 5 deletions(-)
arch/arm64/include/asm/mmu.h +1 -0
@@ -27,6 +27,7 @@typedef struct {atomic64_t id; +unsigned long pinned;void *vdso;unsigned long flags; }mm_context_t ;
arch/arm64/include/asm/mmu_context.h +10 -1
@@ -168,7 +168,13 @@ static inline void cpu_replace_ttbr1(pgd_t *pgdp)#define destroy_context (mm) do { } while(0)void check_and_switch_context (struct mm_struct *mm,unsigned int cpu); -#define init_new_context (tsk,mm) ({ atomic64_set(&(mm)->context.id, 0); 0; }) +static inline int +init_new_context (struct task_struct *tsk,struct mm_struct *mm) +{ +atomic64_set (&mm->context.id,0 ); + mm->context.pinned =0 ; +return 0 ; +}#ifdef CONFIG_ARM64_SW_TTBR0_PAN static inline void update_saved_ttbr0(struct task_struct *tsk,
@@ -241,6 +247,9 @@ switch_mm(struct mm_struct *prev, struct mm_struct *next,void verify_cpu_asid_bits (void );void post_ttbr_update_workaround (void ); +unsigned long mm_context_get (struct mm_struct *mm); +void mm_context_put (struct mm_struct *mm); +#endif /* !__ASSEMBLY__ */ #endif /* !__ASM_MMU_CONTEXT_H */
arch/arm64/mm/context.c +88 -4
140 lines not on this page · raw message
Signature · 1 line
2.17.0
Catalin Marinas
maintainerRe: [PATCH v2 16/40] arm64: mm: Pin down ASIDs for sharing mm with devices
Hi Jean-Philippe,
Quoted 31 lines · Jean-Philippe Brucker
arch/arm64/mm/context.c +31
-0+unsigned long mm_context_get (struct mm_struct *mm) +{ +unsigned long flags; +u64 asid; + +raw_spin_lock_irqsave (&cpu_asid_lock, flags); + + asid =atomic64_read (&mm->context.id); + +if (mm->context.pinned) { + mm->context.pinned++; + asid &= ~ASID_MASK ; + goto out_unlock; + } + +if (nr_pinned_asids >= max_pinned_asids) { + asid =0 ; + goto out_unlock; + } + +if (!asid_gen_match (asid)) { +/* +* We went through one or more rollover since that ASID was +* used. Ensure that it is still valid, or generate a new one. +* The cpu argument isn't used by new_context. +*/ + asid =new_context (mm,0 ); +atomic64_set (&mm->context.id, asid); + } + + asid &= ~ASID_MASK ;
arch/arm64/mm/context.c void check_and_switch_context(struct mm_struct *mm, unsigned int cpu) · line 302
Quoted 10 lines
arch/arm64/mm/context.c +10
-0+ + nr_pinned_asids++; +__set_bit (asid2idx (asid), pinned_asid_map); + mm->context.pinned++; + +out_unlock: +raw_spin_unlock_irqrestore (&cpu_asid_lock, flags); + +return asid; +}
With CONFIG_UNMAP_KERNEL_AT_EL0 (a.k.a. KPTI), the hardware ASID has bit 0 set automatically when entering user space (and cleared when getting back to the kernel). If the returned asid value here is going to be used as is in the calling code, you should probably set bit 0 when KPTI is enabled.
Signature · 1 line
Catalin
Re: [PATCH v2 16/40] arm64: mm: Pin down ASIDs for sharing mm with devices
Quoted 42 lines · Catalin Marinas
Hi Jean-Philippe,
Quoted 39 lines · Jean-Philippe Brucker
arch/arm64/mm/context.c +39
-0+unsigned long mm_context_get (struct mm_struct *mm) +{ +unsigned long flags; +u64 asid; + +raw_spin_lock_irqsave (&cpu_asid_lock, flags); + + asid =atomic64_read (&mm->context.id); + +if (mm->context.pinned) { + mm->context.pinned++; + asid &= ~ASID_MASK ; + goto out_unlock; + } + +if (nr_pinned_asids >= max_pinned_asids) { + asid =0 ; + goto out_unlock; + } + +if (!asid_gen_match (asid)) { +/* +* We went through one or more rollover since that ASID was +* used. Ensure that it is still valid, or generate a new one. +* The cpu argument isn't used by new_context. +*/ + asid =new_context (mm,0 ); +atomic64_set (&mm->context.id, asid); + } + + asid &= ~ASID_MASK ; + + nr_pinned_asids++; +__set_bit (asid2idx (asid), pinned_asid_map); + mm->context.pinned++; + +out_unlock: +raw_spin_unlock_irqrestore (&cpu_asid_lock, flags); +
Quoted 9 lines
Review
Quoted 2 lines
(quoted diff) +2
-0+ return asid; +}With
CONFIG_UNMAP_KERNEL_AT_EL0(a.k.a. KPTI), the hardware ASID has bit 0 set automatically when entering user space (and cleared when getting back to the kernel). If the returned asid value here is going to be used as is in the calling code, you should probably set bit 0 when KPTI is enabled.
Oh right, I'll change this
Thanks,
Jean
PATCH v2 17/40 iommu/arm-smmu-v3: Link domains and devices
In reply to Jean-Philippe BruckerWhen removing a mapping from a domain, we need to send an invalidation to all devices that might have stored it in their Address Translation Cache (ATC). In addition when updating the context descriptor of a live domain, we'll need to send invalidations for all devices attached to it.
Maintain a list of devices in each domain, protected by a spinlock. It is updated every time we attach or detach devices to and from domains.
It needs to be a spinlock because we'll invalidate ATC entries from within hardirq-safe contexts, but it may be possible to relax the read side with RCU later.
--- drivers/iommu/arm-smmu-v3.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+)
drivers/iommu/arm-smmu-v3.c +28 -0
@@ -595,6 +595,11 @@ struct arm_smmu_device {struct arm_smmu_master_data {struct arm_smmu_device *smmu;struct arm_smmu_strtab_ent ste; + +struct arm_smmu_domain *domain; +struct list_head list;/* domain->devices */ + +struct device *dev; };/* SMMU private data for an IOMMU domain */
@@ -618,6 +623,9 @@ struct arm_smmu_domain { };struct iommu_domain domain; + +struct list_head devices; +spinlock_t devices_lock; };struct arm_smmu_option_prop {
@@ -1470,6 +1478,9 @@ static struct iommu_domain *arm_smmu_domain_alloc(unsigned type) }mutex_init (&smmu_domain->init_mutex); +INIT_LIST_HEAD (&smmu_domain->devices); +spin_lock_init (&smmu_domain->devices_lock); +return &smmu_domain->domain; }
@@ -1685,7 +1696,17 @@ static void arm_smmu_install_ste_for_dev(struct iommu_fwspec *fwspec)static void arm_smmu_detach_dev (struct device *dev) { +unsigned long flags;struct arm_smmu_master_data *master = dev->iommu_fwspec->iommu_priv; +struct arm_smmu_domain *smmu_domain = master->domain; + +if (smmu_domain) { +spin_lock_irqsave (&smmu_domain->devices_lock, flags); +list_del (&master->list); +spin_unlock_irqrestore (&smmu_domain->devices_lock, flags); + + master->domain =NULL ; + } master->ste.assigned = false;arm_smmu_install_ste_for_dev (dev->iommu_fwspec);
@@ -1694,6 +1715,7 @@ static void arm_smmu_detach_dev(struct device *dev)static int arm_smmu_attach_dev (struct iommu_domain *domain,struct device *dev) {int ret =0 ; +unsigned long flags;struct arm_smmu_device *smmu;struct arm_smmu_domain *smmu_domain =to_smmu_domain (domain);struct arm_smmu_master_data *master;
@@ -1729,6 +1751,11 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev) } ste->assigned = true; + master->domain = smmu_domain; + +spin_lock_irqsave (&smmu_domain->devices_lock, flags); +list_add (&master->list, &smmu_domain->devices); +spin_unlock_irqrestore (&smmu_domain->devices_lock, flags);if (smmu_domain->stage ==ARM_SMMU_DOMAIN_BYPASS ) { ste->s1_cfg =NULL ;
@@ -1847,6 +1874,7 @@ static int arm_smmu_add_device(struct device *dev)return -ENOMEM ; master->smmu = smmu; + master->dev = dev; fwspec->iommu_priv = master; }
Signature · 1 line
2.17.0
Re: [PATCH v2 17/40] iommu/arm-smmu-v3: Link domains and devices
Quoted 5 lines · On Fri, 11 May 2018 20:06:18 +0100 Jean-Philippe Brucker…
When removing a mapping from a domain, we need to send an invalidation to all devices that might have stored it in their Address Translation Cache (ATC). In addition when updating the context descriptor of a live domain, we'll need to send invalidations for all devices attached to it.
Quoted 8 lines
Maintain a list of devices in each domain, protected by a spinlock. It is updated every time we attach or detach devices to and from domains.
It needs to be a spinlock because we'll invalidate ATC entries from within hardirq-safe contexts, but it may be possible to relax the read side with RCU later.
Signed-off-by Jean-Philippe Brucker <[email protected]>
Trivial naming suggestion...
Quoted 9 lines
--- drivers/iommu/arm-smmu-v3.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+)drivers/iommu/arm-smmu-v3.c +0
-0
drivers/iommu/arm-smmu-v3.c struct arm_smmu_device { · line 600
Quoted 6 lines
drivers/iommu/arm-smmu-v3.c +3
-0@@ -595,6 +595,11 @@ struct arm_smmu_device {struct arm_smmu_master_data {struct arm_smmu_device *smmu;struct arm_smmu_strtab_ent ste; + +struct arm_smmu_domain *domain; +struct list_head list;/* domain->devices */
More meaningful name perhaps to avoid the need for the comment?
Quoted 71 lines
drivers/iommu/arm-smmu-v3.c +25
-0+ +struct device *dev; };/* SMMU private data for an IOMMU domain */ @@ -618,6 +623,9 @@ struct arm_smmu_domain { };struct iommu_domain domain; + +struct list_head devices; +spinlock_t devices_lock; };struct arm_smmu_option_prop {@@ -1470,6 +1478,9 @@ static struct iommu_domain *arm_smmu_domain_alloc(unsigned type) }mutex_init (&smmu_domain->init_mutex); +INIT_LIST_HEAD (&smmu_domain->devices); +spin_lock_init (&smmu_domain->devices_lock); +return &smmu_domain->domain; }@@ -1685,7 +1696,17 @@ static void arm_smmu_install_ste_for_dev(struct iommu_fwspec *fwspec)static void arm_smmu_detach_dev (struct device *dev) { +unsigned long flags;struct arm_smmu_master_data *master = dev->iommu_fwspec->iommu_priv; +struct arm_smmu_domain *smmu_domain = master->domain; + +if (smmu_domain) { +spin_lock_irqsave (&smmu_domain->devices_lock, flags); +list_del (&master->list); +spin_unlock_irqrestore (&smmu_domain->devices_lock, flags); + + master->domain =NULL ; + } master->ste.assigned = false;arm_smmu_install_ste_for_dev (dev->iommu_fwspec);@@ -1694,6 +1715,7 @@ static void arm_smmu_detach_dev(struct device *dev)static int arm_smmu_attach_dev (struct iommu_domain *domain,struct device *dev) {int ret =0 ; +unsigned long flags;struct arm_smmu_device *smmu;struct arm_smmu_domain *smmu_domain =to_smmu_domain (domain);struct arm_smmu_master_data *master;@@ -1729,6 +1751,11 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev) } ste->assigned = true; + master->domain = smmu_domain; + +spin_lock_irqsave (&smmu_domain->devices_lock, flags); +list_add (&master->list, &smmu_domain->devices); +spin_unlock_irqrestore (&smmu_domain->devices_lock, flags);if (smmu_domain->stage ==ARM_SMMU_DOMAIN_BYPASS ) { ste->s1_cfg =NULL ;@@ -1847,6 +1874,7 @@ static int arm_smmu_add_device(struct device *dev)return -ENOMEM ; master->smmu = smmu; + master->dev = dev; fwspec->iommu_priv = master; }
Re: [PATCH v2 17/40] iommu/arm-smmu-v3: Link domains and devices
Quoted 10 lines · Jonathan Cameron
drivers/iommu/arm-smmu-v3.c struct arm_smmu_device { · line 600
Quoted 8 lines
+++
b/drivers/iommu/arm-smmu-v3.cdrivers/iommu/arm-smmu-v3.c +3
-0@@ -595,6 +595,11 @@ struct arm_smmu_device {struct arm_smmu_master_data {struct arm_smmu_device *smmu;struct arm_smmu_strtab_ent ste; + +struct arm_smmu_domain *domain; +struct list_head list;/* domain->devices */ More meaningful name perhaps to avoid the need for the comment?
Sure
Thanks,
Jean
Re: [PATCH v2 17/40] iommu/arm-smmu-v3: Link domains and devices
In reply to Jean-Philippe BruckerHi Jean-Philippe,
Quoted 22 lines · Jean-Philippe Brucker
When removing a mapping from a domain, we need to send an invalidation to all devices that might have stored it in their Address Translation Cache (ATC). In addition when updating the context descriptor of a live domain, we'll need to send invalidations for all devices attached to it.
Maintain a list of devices in each domain, protected by a spinlock. It is updated every time we attach or detach devices to and from domains.
It needs to be a spinlock because we'll invalidate ATC entries from within hardirq-safe contexts, but it may be possible to relax the read side with RCU later.
Signed-off-by Jean-Philippe Brucker <[email protected]>--- drivers/iommu/arm-smmu-v3.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+)drivers/iommu/arm-smmu-v3.c +0
-0
drivers/iommu/arm-smmu-v3.c struct arm_smmu_device { · line 602
Quoted 8 lines
drivers/iommu/arm-smmu-v3.c +5
-0@@ -595,6 +595,11 @@ struct arm_smmu_device {struct arm_smmu_master_data {struct arm_smmu_device *smmu;struct arm_smmu_strtab_ent ste; + +struct arm_smmu_domain *domain; +struct list_head list;/* domain->devices */ + +struct device *dev;
This field addition and associated assignment in arm_smmu_attach_dev() is not really documented in the commit message.
Quoted 50 lines
drivers/iommu/arm-smmu-v3.c +17
-0};/* SMMU private data for an IOMMU domain */ @@ -618,6 +623,9 @@ struct arm_smmu_domain { };struct iommu_domain domain; + +struct list_head devices; +spinlock_t devices_lock; };struct arm_smmu_option_prop {@@ -1470,6 +1478,9 @@ static struct iommu_domain *arm_smmu_domain_alloc(unsigned type) }mutex_init (&smmu_domain->init_mutex); +INIT_LIST_HEAD (&smmu_domain->devices); +spin_lock_init (&smmu_domain->devices_lock); +return &smmu_domain->domain; }@@ -1685,7 +1696,17 @@ static void arm_smmu_install_ste_for_dev(struct iommu_fwspec *fwspec)static void arm_smmu_detach_dev (struct device *dev) { +unsigned long flags;struct arm_smmu_master_data *master = dev->iommu_fwspec->iommu_priv; +struct arm_smmu_domain *smmu_domain = master->domain; + +if (smmu_domain) { +spin_lock_irqsave (&smmu_domain->devices_lock, flags); +list_del (&master->list); +spin_unlock_irqrestore (&smmu_domain->devices_lock, flags); + + master->domain =NULL ; + } master->ste.assigned = false;arm_smmu_install_ste_for_dev (dev->iommu_fwspec);@@ -1694,6 +1715,7 @@ static void arm_smmu_detach_dev(struct device *dev)static int arm_smmu_attach_dev (struct iommu_domain *domain,struct device *dev) {int ret =0 ; +unsigned long flags;struct arm_smmu_device *smmu;struct arm_smmu_domain *smmu_domain =to_smmu_domain (domain);struct arm_smmu_master_data *master;
drivers/iommu/arm-smmu-v3.c static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev) · line 1757
Quoted 7 lines
drivers/iommu/arm-smmu-v3.c +4
-0@@ -1729,6 +1751,11 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev) } ste->assigned = true; + master->domain = smmu_domain; + +spin_lock_irqsave (&smmu_domain->devices_lock, flags); +list_add (&master->list, &smmu_domain->devices);
it is not totally obvious to me why master->domain = smmu_domain isn't within the lock either for consistency. Same when deleting the node.
Thanks
Eric
Quoted 13 lines
drivers/iommu/arm-smmu-v3.c +2
-0+spin_unlock_irqrestore (&smmu_domain->devices_lock, flags);if (smmu_domain->stage ==ARM_SMMU_DOMAIN_BYPASS ) { ste->s1_cfg =NULL ;@@ -1847,6 +1874,7 @@ static int arm_smmu_add_device(struct device *dev)return -ENOMEM ; master->smmu = smmu; + master->dev = dev; fwspec->iommu_priv = master; }
PATCH v2 18/40 iommu/io-pgtable-arm: Factor out ARM LPAE register defines
In reply to Jean-Philippe BruckerFor SVA, we'll need to extract CPU page table information and mirror it in the substream setup. Move relevant defines to a common header.
Fix TCR_SZ_MASK while we're at it.
--- MAINTAINERS | 3 +- drivers/iommu/io-pgtable-arm.c | 49 +----------------------------- drivers/iommu/io-pgtable-arm.h | 54 ++++++++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+), 50 deletions(-) create mode 100644 drivers/iommu/io-pgtable-arm.h
MAINTAINERS +1 -2
@@ -1114,8 +1114,7 @@ L: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org (moderated for non-subscribers) S: Maintained F: drivers/iommu/arm-smmu.c F: drivers/iommu/arm-smmu-v3.c -F: drivers/iommu/io-pgtable-arm.c -F: drivers/iommu/io-pgtable-arm-v7s.c +F: drivers/iommu/io-pgtable-arm* ARM SUB-ARCHITECTURES L: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org (moderated for non-subscribers)
drivers/iommu/io-pgtable-arm.c +1 -48
61 lines not on this page · raw message
drivers/iommu/io-pgtable-arm.h new file +54 -0
54 lines not on this page · raw message
Signature · 1 line
2.17.0
PATCH v2 19/40 iommu: Add generic PASID table library
In reply to Jean-Philippe BruckerAdd a small API within the IOMMU subsystem to handle different formats of PASID tables. It uses the same principle as io-pgtable:
* The IOMMU driver registers a PASID table with some invalidation callbacks.
* The pasid-table lib allocates a set of tables of the right format, and returns an iommu_pasid_table_ops structure.
* The IOMMU driver allocates entries and writes them using the provided ops.
* The pasid-table lib calls the IOMMU driver back for invalidation when necessary.
* The IOMMU driver unregisters the ops which frees the tables when finished.
An example user will be Arm SMMU in a subsequent patch. Other IOMMU drivers (e.g. paravirtualized ones) will be able to use the same PASID table code.
---v1->v2: remove free_entry from the ops. The table driver now registers a standalone release callback to each entry, because it may be freed after the tables.
--- drivers/iommu/Kconfig | 7 ++ drivers/iommu/Makefile | 1 + drivers/iommu/iommu-pasid-table.c | 51 +++++++++++ drivers/iommu/iommu-pasid-table.h | 146 ++++++++++++++++++++++++++++++ 4 files changed, 205 insertions(+) create mode 100644 drivers/iommu/iommu-pasid-table.c create mode 100644 drivers/iommu/iommu-pasid-table.h
drivers/iommu/Kconfig +7 -0
@@ -60,6 +60,13 @@ config IOMMU_IO_PGTABLE_ARMV7S_SELFTEST endmenu +menu "Generic PASID table support" + +config IOMMU_PASID_TABLE + bool + +endmenu + config IOMMU_IOVA tristate
drivers/iommu/Makefile +1 -0
@@ -8,6 +8,7 @@ obj-$(CONFIG_IOMMU_PAGE_FAULT) += io-pgfault.o obj-$(CONFIG_IOMMU_IO_PGTABLE) += io-pgtable.o obj-$(CONFIG_IOMMU_IO_PGTABLE_ARMV7S) += io-pgtable-arm-v7s.o obj-$(CONFIG_IOMMU_IO_PGTABLE_LPAE) += io-pgtable-arm.o +obj-$(CONFIG_IOMMU_PASID_TABLE) += iommu-pasid-table.o obj-$(CONFIG_IOMMU_IOVA) += iova.o obj-$(CONFIG_OF_IOMMU) += of_iommu.o obj-$(CONFIG_MSM_IOMMU) += msm_iommu.o
drivers/iommu/iommu-pasid-table.c new file +51 -0
51 lines not on this page · raw message
drivers/iommu/iommu-pasid-table.h new file +146 -0
146 lines not on this page · raw message
Signature · 1 line
2.17.0
PATCH v2 20/40 iommu/arm-smmu-v3: Move context descriptor code
In reply to Jean-Philippe BruckerIn preparation for substream ID support, move the context descriptor code into a separate module. At the moment it only manages context descriptor zero, which is used for non-PASID translations.
One important behavior change is the ASID allocator, which is now global instead of per-SMMU. If we end up needing per-SMMU ASIDs after all, it would be relatively simple to move back to per-device allocator instead of a global one. Sharing ASIDs will require an IDR, so implement the ASID allocator with an IDA instead of porting the bitmap, to ease the transition.
---v1->v2: try to simplify
--- MAINTAINERS | 3 +- drivers/iommu/Kconfig | 11 ++ drivers/iommu/Makefile | 1 + drivers/iommu/arm-smmu-v3-context.c | 257 ++++++++++++++++++++++++++++ drivers/iommu/arm-smmu-v3.c | 191 +++++++-------------- drivers/iommu/iommu-pasid-table.c | 1 + drivers/iommu/iommu-pasid-table.h | 20 +++ 7 files changed, 355 insertions(+), 129 deletions(-) create mode 100644 drivers/iommu/arm-smmu-v3-context.c
MAINTAINERS +1 -2
@@ -1112,8 +1112,7 @@ M: Will Deacon <[email protected]> R: Robin Murphy <[email protected]> L: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org (moderated for non-subscribers) S: Maintained -F: drivers/iommu/arm-smmu.c -F: drivers/iommu/arm-smmu-v3.c +F: drivers/iommu/arm-smmu* F: drivers/iommu/io-pgtable-arm* ARM SUB-ARCHITECTURES
drivers/iommu/Kconfig +11 -0
@@ -65,6 +65,16 @@ menu "Generic PASID table support" config IOMMU_PASID_TABLE bool +config ARM_SMMU_V3_CONTEXT + bool "ARM SMMU v3 Context Descriptor tables" + select IOMMU_PASID_TABLE + depends on ARM64 + help + Enable support for ARM SMMU v3 Context Descriptor tables, used for + DMA and PASID support. + + If unsure, say N here. + endmenu config IOMMU_IOVA
@@ -334,6 +344,7 @@ config ARM_SMMU_V3 depends on ARM64 select IOMMU_API select IOMMU_IO_PGTABLE_LPAE + select ARM_SMMU_V3_CONTEXT select GENERIC_MSI_IRQ_DOMAIN help Support for implementations of the ARM System MMU architecture
drivers/iommu/Makefile +1 -0
@@ -9,6 +9,7 @@ obj-$(CONFIG_IOMMU_IO_PGTABLE) += io-pgtable.o obj-$(CONFIG_IOMMU_IO_PGTABLE_ARMV7S) += io-pgtable-arm-v7s.o obj-$(CONFIG_IOMMU_IO_PGTABLE_LPAE) += io-pgtable-arm.o obj-$(CONFIG_IOMMU_PASID_TABLE) += iommu-pasid-table.o +obj-$(CONFIG_ARM_SMMU_V3_CONTEXT) += arm-smmu-v3-context.o obj-$(CONFIG_IOMMU_IOVA) += iova.o obj-$(CONFIG_OF_IOMMU) += of_iommu.o obj-$(CONFIG_MSM_IOMMU) += msm_iommu.o
drivers/iommu/arm-smmu-v3-context.c new file +257 -0
257 lines not on this page · raw message
drivers/iommu/arm-smmu-v3.c +64 -127
274 lines not on this page · raw message
drivers/iommu/iommu-pasid-table.c +1 -0
@@ -11,6 +11,7 @@static const struct iommu_pasid_init_fns * pasid_table_init_fns[PASID_TABLE_NUM_FMTS ] = { + [PASID_TABLE_ARM_SMMU_V3 ] = &arm_smmu_v3_pasid_init_fns, }; struct iommu_pasid_table_ops *
drivers/iommu/iommu-pasid-table.h +20 -0
@@ -14,6 +14,7 @@struct mm_struct ;enum iommu_pasid_table_fmt { +PASID_TABLE_ARM_SMMU_V3 ,PASID_TABLE_NUM_FMTS , };
@@ -71,6 +72,18 @@ struct iommu_pasid_sync_ops { struct iommu_pasid_entry *entry); }; +/** +* arm_smmu_context_cfg - PASID table configuration for ARM SMMU v3 +* +* SMMU properties: +* @stall: devices attached to the domain are allowed to stall. +* @asid_bits: number of ASID bits supported by the SMMU +*/ +struct arm_smmu_context_cfg { +u8 stall:1 ; +u8 asid_bits; +}; +/** * struct iommu_pasid_table_cfg - Configuration data for a set of PASID tables. *
@@ -85,6 +98,11 @@ struct iommu_pasid_table_cfg {size_t order;const struct iommu_pasid_sync_ops *sync;dma_addr_t base; + +/* Low-level data specific to the IOMMU */ +union { +struct arm_smmu_context_cfg arm_smmu; + }; }; struct iommu_pasid_table_ops *
@@ -143,4 +161,6 @@ static inline void iommu_pasid_flush_tlbs(struct iommu_pasid_table *table, table->cfg.sync->tlb_flush(table->cookie, pasid, entry); } +extern struct iommu_pasid_init_fns arm_smmu_v3_pasid_init_fns; +#endif /* __IOMMU_PASID_TABLE_H */
Signature · 1 line
2.17.0
PATCH v2 21/40 iommu/arm-smmu-v3: Add support for Substream IDs
In reply to Jean-Philippe BruckerAt the moment, the SMMUv3 driver offers only one stage-1 or stage-2 address space to each device. SMMUv3 allows to associate multiple address spaces per device. In addition to the Stream ID (SID), that identifies a device, we can now have Substream IDs (SSID) identifying an address space. In PCIe lingo, SID is called Requester ID (RID) and SSID is called Process Address-Space ID (PASID).
Prepare the driver for SSID support, by adding context descriptor tables in STEs (previously a single static context descriptor). A complete stage-1 walk is now performed like this by the SMMU:
Stream tables Ctx. tables Page tables
+--------+ ,------->+-------+ ,------->+-------+
: : | : : | : :
+--------+ | +-------+ | +-------+
SID->| STE |---' SSID->| CD |---' IOVA->| PTE |--> IPA
+--------+ +-------+ +-------+
: : : : : :
+--------+ +-------+ +-------+We only implement one level of context descriptor table for now, but as with stream and page tables, an SSID can be split to target multiple levels of tables.
In all stream table entries, we set S1DSS=SSID0 mode, making translations without an ssid use context descriptor 0.
---v1->v2: use GENMASK throughout SMMU patches
--- drivers/iommu/arm-smmu-v3-context.c | 141 +++++++++++++++++++++------- drivers/iommu/arm-smmu-v3.c | 82 +++++++++++++++- drivers/iommu/iommu-pasid-table.h | 7 ++ 3 files changed, 190 insertions(+), 40 deletions(-)
drivers/iommu/arm-smmu-v3-context.c +106 -35
@@ -62,11 +62,14 @@ struct arm_smmu_cd {#define pasid_entry_to_cd (entry) \ container_of((entry), struct arm_smmu_cd, entry) +struct arm_smmu_cd_table { +__le64 *ptr; +dma_addr_t ptr_dma; +}; +struct arm_smmu_cd_tables {struct iommu_pasid_table pasid; - -void *ptr; -dma_addr_t ptr_dma; +struct arm_smmu_cd_table table; };#define pasid_to_cd_tables (pasid_table) \
@@ -77,6 +80,36 @@ struct arm_smmu_cd_tables {static DEFINE_IDA (asid_ida); +static int arm_smmu_alloc_cd_leaf_table (struct device *dev, +struct arm_smmu_cd_table *desc, +size_t num_entries) +{ +size_t size = num_entries * (CTXDESC_CD_DWORDS <<3 ); + + desc->ptr =dmam_alloc_coherent (dev, size, &desc->ptr_dma, +GFP_ATOMIC | __GFP_ZERO); +if (!desc->ptr) { +dev_warn (dev,"failed to allocate context descriptor table\n" ); +return -ENOMEM ; + } + +return 0 ; +} + +static void arm_smmu_free_cd_leaf_table (struct device *dev, +struct arm_smmu_cd_table *desc, +size_t num_entries) +{ +size_t size = num_entries * (CTXDESC_CD_DWORDS <<3 ); + +dmam_free_coherent (dev, size, desc->ptr, desc->ptr_dma); +} + +static __le64 *arm_smmu_get_cd_ptr (struct arm_smmu_cd_tables *tbl,u32 ssid) +{ +return tbl->table.ptr + ssid *CTXDESC_CD_DWORDS ; +} +static u64 arm_smmu_cpu_tcr_to_cd (u64 tcr) {u64 val =0 ;
@@ -95,34 +128,74 @@ static u64 arm_smmu_cpu_tcr_to_cd(u64 tcr)return val; } -static void arm_smmu_write_ctx_desc (struct arm_smmu_cd_tables *tbl, -struct arm_smmu_cd *cd) +static int arm_smmu_write_ctx_desc (struct arm_smmu_cd_tables *tbl,int ssid, +struct arm_smmu_cd *cd) {u64 val; -__u64 *cdptr = tbl->ptr; +bool cd_live; +__le64 *cdptr =arm_smmu_get_cd_ptr (tbl, ssid);struct arm_smmu_context_cfg *cfg = &tbl->pasid.cfg.arm_smmu;/* -* We don't need to issue any invalidation here, as we'll invalidate -* the STE when installing the new entry anyway. +* This function handles the following cases: +* +* (1) Install primary CD, for normal DMA traffic (SSID = 0). +* (2) Install a secondary CD, for SID+SSID traffic, followed by an +* invalidation. +* (3) Update ASID of primary CD. This is allowed by atomically writing +* the first 64 bits of the CD, followed by invalidation of the old +* entry and mappings. +* (4) Remove a secondary CD and invalidate it. */ - val = arm_smmu_cpu_tcr_to_cd(cd->tcr) | + +if (!cdptr) +return -ENOMEM ; + + val =le64_to_cpu (cdptr[0 ]); + cd_live = !!(val &CTXDESC_CD_0_V ); + +if (!cd) {/* (4) */ + cdptr[0 ] =0 ; + }else if (cd_live) {/* (3) */ + val &= ~CTXDESC_CD_0_ASID ; + val |=FIELD_PREP (CTXDESC_CD_0_ASID , cd->entry.tag); + + cdptr[0 ] =cpu_to_le64 (val); +/* +* Until CD+TLB invalidation, both ASIDs may be used for tagging +* this substream's traffic +*/ + }else {/* (1) and (2) */ + cdptr[1 ] =cpu_to_le64 (cd->ttbr &CTXDESC_CD_1_TTB0_MASK ); + cdptr[2 ] =0 ; + cdptr[3 ] =cpu_to_le64 (cd->mair); + +/* +* STE is live, and the SMMU might fetch this CD at any +* time. Ensure it observes the rest of the CD before we +* enable it. +*/ +iommu_pasid_flush (&tbl->pasid, ssid, true); + + + val = arm_smmu_cpu_tcr_to_cd(cd->tcr) |#ifdef __BIG_ENDIAN -CTXDESC_CD_0_ENDI | +CTXDESC_CD_0_ENDI | #endif -CTXDESC_CD_0_R |CTXDESC_CD_0_A |CTXDESC_CD_0_ASET | -CTXDESC_CD_0_AA64 |FIELD_PREP (CTXDESC_CD_0_ASID , cd->entry.tag) | -CTXDESC_CD_0_V ; +CTXDESC_CD_0_R |CTXDESC_CD_0_A |CTXDESC_CD_0_ASET | +CTXDESC_CD_0_AA64 | +FIELD_PREP (CTXDESC_CD_0_ASID , cd->entry.tag) | +CTXDESC_CD_0_V ; -if (cfg->stall) - val |=CTXDESC_CD_0_S ; +if (cfg->stall) + val |=CTXDESC_CD_0_S ; - cdptr[0 ] =cpu_to_le64 (val); + cdptr[0] = cpu_to_le64(val); + } - val = cd->ttbr &CTXDESC_CD_1_TTB0_MASK ; - cdptr[1 ] =cpu_to_le64 (val); +iommu_pasid_flush (&tbl->pasid, ssid, true); - cdptr[3] = cpu_to_le64(cd->mair); +return 0 ; }static void arm_smmu_free_cd (struct iommu_pasid_entry *entry)
@@ -190,8 +263,10 @@ static int arm_smmu_set_cd(struct iommu_pasid_table_ops *ops, int pasid,struct arm_smmu_cd_tables *tbl =pasid_ops_to_tables (ops);struct arm_smmu_cd *cd =pasid_entry_to_cd (entry); -arm_smmu_write_ctx_desc (tbl, cd); -return 0 ; +if (WARN_ON (pasid > (1 << tbl->pasid.cfg.order))) +return -EINVAL ; + +return arm_smmu_write_ctx_desc (tbl, pasid, cd); } static void arm_smmu_clear_cd(struct iommu_pasid_table_ops *ops, int pasid,
@@ -199,30 +274,26 @@ static void arm_smmu_clear_cd(struct iommu_pasid_table_ops *ops, int pasid, {struct arm_smmu_cd_tables *tbl =pasid_ops_to_tables (ops); -arm_smmu_write_ctx_desc (tbl,NULL ); +if (WARN_ON (pasid > (1 << tbl->pasid.cfg.order))) +return ; + +arm_smmu_write_ctx_desc (tbl, pasid,NULL ); }static struct iommu_pasid_table *arm_smmu_alloc_cd_tables (struct iommu_pasid_table_cfg *cfg,void *cookie) { +int ret;struct arm_smmu_cd_tables *tbl;struct device *dev = cfg->iommu_dev; -if (cfg->order) { -/* TODO: support SSID */ -return NULL ; - } - tbl =devm_kzalloc (dev,sizeof (*tbl),GFP_KERNEL );if (!tbl)return NULL ; - tbl->ptr =dmam_alloc_coherent (dev,CTXDESC_CD_DWORDS <<3 , - &tbl->ptr_dma,GFP_KERNEL | __GFP_ZERO); -if (!tbl->ptr) { -dev_warn (dev,"failed to allocate context descriptor\n" ); + ret =arm_smmu_alloc_cd_leaf_table (dev, &tbl->table,1 << cfg->order); +if (ret) goto err_free_tbl; - } tbl->pasid.ops = (struct iommu_pasid_table_ops ) { .alloc_priv_entry = arm_smmu_alloc_priv_cd,
@@ -230,7 +301,8 @@ arm_smmu_alloc_cd_tables(struct iommu_pasid_table_cfg *cfg, void *cookie) .set_entry = arm_smmu_set_cd, .clear_entry = arm_smmu_clear_cd, }; - cfg->base = tbl->ptr_dma; + cfg->base = tbl->table.ptr_dma; + cfg->arm_smmu.s1fmt =ARM_SMMU_S1FMT_LINEAR ;return &tbl->pasid;
@@ -246,8 +318,7 @@ static void arm_smmu_free_cd_tables(struct iommu_pasid_table *pasid_table)struct device *dev = cfg->iommu_dev;struct arm_smmu_cd_tables *tbl =pasid_to_cd_tables (pasid_table); -dmam_free_coherent (dev,CTXDESC_CD_DWORDS <<3 , - tbl->ptr, tbl->ptr_dma); +arm_smmu_free_cd_leaf_table (dev, &tbl->table,1 << cfg->order);devm_kfree (dev, tbl); }
drivers/iommu/arm-smmu-v3.c +77 -5
200 lines not on this page · raw message
drivers/iommu/iommu-pasid-table.h +7 -0
@@ -78,10 +78,17 @@ struct iommu_pasid_sync_ops {* SMMU properties: * @stall: devices attached to the domain are allowed to stall. * @asid_bits: number of ASID bits supported by the SMMU +* +* @s1fmt: PASID table format, chosen by the allocator. */ struct arm_smmu_context_cfg {u8 stall:1 ;u8 asid_bits; + +#define ARM_SMMU_S1FMT_LINEAR 0x0 +#define ARM_SMMU_S1FMT_4K_L2 0x1 +#define ARM_SMMU_S1FMT_64K_L2 0x2 +u8 s1fmt; };/**
Signature · 1 line
2.17.0
RE: [PATCH v2 21/40] iommu/arm-smmu-v3: Add support for Substream IDs
Quoted 391 lines
At the moment, the SMMUv3 driver offers only one stage-1 or stage-2 address space to each device. SMMUv3 allows to associate multiple address spaces per device. In addition to the Stream ID (SID), that identifies a device, we can now have Substream IDs (SSID) identifying an address space. In PCIe lingo, SID is called Requester ID (RID) and SSID is called Process Address-Space ID (PASID).
Prepare the driver for SSID support, by adding context descriptor tables in STEs (previously a single static context descriptor). A complete stage-1 walk is now performed like this by the SMMU:
Stream tables Ctx. tables Page tables
+--------+ ,------->+-------+ ,------->+-------+
: : | : : | : :
+--------+ | +-------+ | +-------+SID->| STE |---' SSID->| CD |---' IOVA->| PTE |--> IPA +--------+ +-------+ +-------+ : : : : : : +--------+ +-------+ +-------+We only implement one level of context descriptor table for now, but as with stream and page tables, an SSID can be split to target multiple levels of tables.
In all stream table entries, we set S1DSS=SSID0 mode, making translations without an ssid use context descriptor 0.
Signed-off-by Jean-Philippe Brucker <[email protected]>---
v1->v2: use GENMASK throughout SMMU patches--- drivers/iommu/arm-smmu-v3-context.c | 141 +++++++++++++++++++++------(quoted diff) +0
-1- drivers/iommu/arm-smmu-v3.c | 82 +++++++++++++++- drivers/iommu/iommu-pasid-table.h | 7 ++ 3 files changed, 190 insertions(+), 40 deletions(-)drivers/iommu/arm- renamed from drivers/iommu/arm-smmu-v3-context.c +0
-0
smmu-v3-context.c
index15d3d02c59b2..0969a3626110100644drivers/iommu/arm-smmu-v3-context.c +6
-3@@ -62,11 +62,14 @@ struct arm_smmu_cd { #definepasid_entry_to_cd (entry) \ container_of((entry), struct arm_smmu_cd, entry) +struct arm_smmu_cd_table { +__le64 *ptr; +dma_addr_t ptr_dma; +}; +struct arm_smmu_cd_tables {struct iommu_pasid_table pasid; - -void *ptr; -dma_addr_t ptr_dma; +struct arm_smmu_cd_table table; };#define pasid_to_cd_tables (pasid_table) \ @@ -77,6 +80,36 @@ struct
arm_smmu_cd_tables{static
DEFINE_IDA(asid_ida);drivers/iommu/arm-smmu-v3-context.c +10
-0+static int arm_smmu_alloc_cd_leaf_table (struct device *dev, +struct arm_smmu_cd_table *desc, +size_t num_entries) +{ +size_t size = num_entries * (CTXDESC_CD_DWORDS <<3 ); + + desc->ptr =dmam_alloc_coherent (dev, size, &desc->ptr_dma, +GFP_ATOMIC | __GFP_ZERO); +if (!desc->ptr) { + dev_warn(dev, "failed to allocate context descriptortable\n");
drivers/iommu/arm-smmu-v3-context.c +15
-0+return -ENOMEM ; + } + +return 0 ; +} + +static void arm_smmu_free_cd_leaf_table (struct device *dev, +struct arm_smmu_cd_table *desc, +size_t num_entries) +{ +size_t size = num_entries * (CTXDESC_CD_DWORDS <<3 ); + +dmam_free_coherent (dev, size, desc->ptr, desc->ptr_dma); } + +static __le64 *arm_smmu_get_cd_ptr(struct arm_smmu_cd_tables *tbl,u32
drivers/iommu/arm-smmu-v3-context.c +13
-5+ssid) { +return tbl->table.ptr + ssid *CTXDESC_CD_DWORDS ; } +static u64 arm_smmu_cpu_tcr_to_cd (u64 tcr) {u64 val =0 ;@@ -95,34 +128,74 @@ static u64 arm_smmu_cpu_tcr_to_cd(u64 tcr)return val; } -static void arm_smmu_write_ctx_desc (struct arm_smmu_cd_tables *tbl, -struct arm_smmu_cd *cd) +static int arm_smmu_write_ctx_desc (struct arm_smmu_cd_tables *tbl,int ssid, +struct arm_smmu_cd *cd) {u64 val; -__u64 *cdptr = tbl->ptr; +bool cd_live; +__le64 *cdptr =arm_smmu_get_cd_ptr (tbl, ssid);struct arm_smmu_context_cfg *cfg = &tbl->pasid.cfg.arm_smmu;/* -* We don't need to issue any invalidation here, as we'll invalidate -* the STE when installing the new entry anyway. +* This function handles the following cases: +* +* (1) Install primary CD, for normal DMA traffic (SSID = 0). +* (2) Install a secondary CD, for SID+SSID traffic, followed by an +* invalidation. +* (3) Update ASID of primary CD. This is allowed by atomically writing
drivers/iommu/arm-smmu-v3-context.c +19
-1+* the first 64 bits of the CD, followed by invalidation of the old +* entry and mappings. +* (4) Remove a secondary CD and invalidate it. */ - val = arm_smmu_cpu_tcr_to_cd(cd->tcr) | + +if (!cdptr) +return -ENOMEM ; + + val =le64_to_cpu (cdptr[0 ]); + cd_live = !!(val &CTXDESC_CD_0_V ); + +if (!cd) {/* (4) */ + cdptr[0 ] =0 ; + }else if (cd_live) {/* (3) */ + val &= ~CTXDESC_CD_0_ASID ; + val |=FIELD_PREP (CTXDESC_CD_0_ASID , cd->entry.tag); + + cdptr[0 ] =cpu_to_le64 (val); +/* +* Until CD+TLB invalidation, both ASIDs may be used for tagging
drivers/iommu/arm-smmu-v3-context.c +4
-0+* this substream's traffic +*/ + }else {/* (1) and (2) */ + cdptr[1] = cpu_to_le64(cd->ttbr &
CTXDESC_CD_1_TTB0_MASK);drivers/iommu/arm-smmu-v3-context.c +13
-3+ cdptr[2 ] =0 ; + cdptr[3 ] =cpu_to_le64 (cd->mair); + +/* +* STE is live, and the SMMU might fetch this CD at any +* time. Ensure it observes the rest of the CD before we +* enable it. +*/ +iommu_pasid_flush (&tbl->pasid, ssid, true); + + + val = arm_smmu_cpu_tcr_to_cd(cd->tcr) |#ifdef __BIG_ENDIAN -CTXDESC_CD_0_ENDI | + CTXDESC_CD_0_ENDI |#endif -CTXDESC_CD_0_R |CTXDESC_CD_0_A |CTXDESC_CD_0_ASET | - CTXDESC_CD_0_AA64 | FIELD_PREP(CTXDESC_CD_0_ASID, cd-Quoted 1 line
entry.tag) |
drivers/iommu/arm-smmu-v3-context.c +1
-1-CTXDESC_CD_0_V ; + CTXDESC_CD_0_R | CTXDESC_CD_0_A |
CTXDESC_CD_0_ASET|drivers/iommu/arm-smmu-v3-context.c +9
-6+CTXDESC_CD_0_AA64 | +FIELD_PREP (CTXDESC_CD_0_ASID , cd->entry.tag) | +CTXDESC_CD_0_V ; -if (cfg->stall) - val |=CTXDESC_CD_0_S ; +if (cfg->stall) + val |=CTXDESC_CD_0_S ; - cdptr[0 ] =cpu_to_le64 (val); + cdptr[0 ] =cpu_to_le64 (val); + } - val = cd->ttbr &CTXDESC_CD_1_TTB0_MASK ; - cdptr[1 ] =cpu_to_le64 (val); +iommu_pasid_flush (&tbl->pasid, ssid, true); - cdptr[3 ] =cpu_to_le64 (cd->mair); +return 0 ; } static void arm_smmu_free_cd(struct iommu_pasid_entry *entry) @@ -190,8 +263,10 @@ static int arm_smmu_set_cd(struct
iommu_pasid_table_ops*ops, int pasid,drivers/iommu/arm-smmu-v3-context.c +4
-2struct arm_smmu_cd_tables *tbl =pasid_ops_to_tables (ops);struct arm_smmu_cd *cd =pasid_entry_to_cd (entry); -arm_smmu_write_ctx_desc (tbl, cd); -return 0 ; +if (WARN_ON (pasid > (1 << tbl->pasid.cfg.order))) +return -EINVAL ; + +return arm_smmu_write_ctx_desc (tbl, pasid, cd); } static void arm_smmu_clear_cd(struct iommu_pasid_table_ops *ops, intpasid, @@ -199,30 +274,26 @@ static void arm_smmu_clear_cd(struct
iommu_pasid_table_ops*ops, int pasid, {drivers/iommu/arm-smmu-v3-context.c +4
-1struct arm_smmu_cd_tables *tbl =pasid_ops_to_tables (ops); -arm_smmu_write_ctx_desc (tbl,NULL ); +if (WARN_ON (pasid > (1 << tbl->pasid.cfg.order))) +return ; + +arm_smmu_write_ctx_desc (tbl, pasid,NULL ); }static struct iommu_pasid_table * arm_smmu_alloc_cd_tables(struct iommu_pasid_table_cfg *cfg, void*cookie) {
drivers/iommu/arm-smmu-v3-context.c +1
-7+int ret;struct arm_smmu_cd_tables *tbl;struct device *dev = cfg->iommu_dev; -if (cfg->order) { -/* TODO: support SSID */ -return NULL ; - } - tbl =devm_kzalloc (dev,sizeof (*tbl),GFP_KERNEL );if (!tbl)return NULL ; - tbl->ptr = dmam_alloc_coherent(dev, CTXDESC_CD_DWORDS << 3, - &tbl->ptr_dma, GFP_KERNEL |
__GFP_ZERO);drivers/iommu/arm-smmu-v3-context.c +1
-2-if (!tbl->ptr) { -dev_warn (dev,"failed to allocate context descriptor\n" ); + ret = arm_smmu_alloc_cd_leaf_table(dev, &tbl->table, 1 << cfg-Quoted 1 line
order);
drivers/iommu/arm-smmu-v3-context.c +4
-4+if (ret) goto err_free_tbl; - } tbl->pasid.ops = (struct iommu_pasid_table_ops ) { .alloc_priv_entry = arm_smmu_alloc_priv_cd,@@ -230,7 +301,8 @@ arm_smmu_alloc_cd_tables(struct iommu_pasid_table_cfg *cfg, void *cookie) .set_entry = arm_smmu_set_cd, .clear_entry = arm_smmu_clear_cd, }; - cfg->base = tbl->ptr_dma; + cfg->base = tbl->table.ptr_dma; + cfg->arm_smmu.s1fmt =ARM_SMMU_S1FMT_LINEAR ;return &tbl->pasid;@@ -246,8 +318,7 @@ static void arm_smmu_free_cd_tables(struct iommu_pasid_table *pasid_table)struct device *dev = cfg->iommu_dev;struct arm_smmu_cd_tables *tbl =pasid_to_cd_tables (pasid_table); -dmam_free_coherent (dev,CTXDESC_CD_DWORDS <<3 , - tbl->ptr, tbl->ptr_dma); +arm_smmu_free_cd_leaf_table (dev, &tbl->table,1 << cfg->order);devm_kfree (dev, tbl); }drivers/iommu/arm-smmu-v3.c +10
-1@@ -224,10 +224,14 @@#define STRTAB_STE_0_CFG_S2_TRANS 6#define STRTAB_STE_0_S1FMT GENMASK_ULL(5, 4) -#define STRTAB_STE_0_S1FMT_LINEAR 0#define STRTAB_STE_0_S1CTXPTR_MASK GENMASK_ULL(51, 6)#define STRTAB_STE_0_S1CDMAX GENMASK_ULL(63, 59) +#define STRTAB_STE_1_S1DSS GENMASK_ULL(1, 0) +#define STRTAB_STE_1_S1DSS_TERMINATE 0x0 +#define STRTAB_STE_1_S1DSS_BYPASS 0x1 +#define STRTAB_STE_1_S1DSS_SSID0 0x2 +#define STRTAB_STE_1_S1C_CACHE_NC 0UL#define STRTAB_STE_1_S1C_CACHE_WBRA 1UL#define STRTAB_STE_1_S1C_CACHE_WT 2UL@@ -275,6 +279,7 @@#define CMDQ_PREFETCH_1_SIZE GENMASK_ULL(4, 0)#define CMDQ_PREFETCH_1_ADDR_MASK GENMASK_ULL(63, 12) +#define CMDQ_CFGI_0_SSID GENMASK_ULL(31, 12)#define CMDQ_CFGI_0_SID GENMASK_ULL(63, 32)#define CMDQ_CFGI_1_LEAF (1UL << 0)#define CMDQ_CFGI_1_RANGE GENMASK_ULL(4, 0)@@ -381,8 +386,11 @@ struct arm_smmu_cmdq_ent {#define CMDQ_OP_CFGI_STE 0x3#define CMDQ_OP_CFGI_ALL 0x4 +#define CMDQ_OP_CFGI_CD 0x5 +#define CMDQ_OP_CFGI_CD_ALL 0x6struct {u32 sid; +u32 ssid;union {bool leaf;u8 span;@@ -555,6 +563,7 @@ struct arm_smmu_master_data {struct list_head list;/* domain->devices */ struct device *dev; +size_t ssid_bits; }; /* SMMU private data for an IOMMU domain */ @@ -753,10 +762,16 @@static int
arm_smmu_cmdq_build_cmd(u64 *cmd, struct arm_smmu_cmdq_ent *ent)
cmd[1] |= FIELD_PREP(CMDQ_PREFETCH_1_SIZE, ent-Quoted 1 line
prefetch.size);
cmd[1] |=
ent->prefetch.addr&CMDQ_PREFETCH_1_ADDR_MASK;drivers/iommu/arm-smmu-v3.c +6
-0break ; +case CMDQ_OP_CFGI_CD : + cmd[0 ] |=FIELD_PREP (CMDQ_CFGI_0_SSID , ent->cfgi.ssid); +/* Fallthrough */ case CMDQ_OP_CFGI_STE : cmd[0 ] |=FIELD_PREP (CMDQ_CFGI_0_SID , ent->cfgi.sid); cmd[1 ] |=FIELD_PREP (CMDQ_CFGI_1_LEAF , ent->cfgi.leaf);break ; +case CMDQ_OP_CFGI_CD_ALL : + cmd[0 ] |=FIELD_PREP (CMDQ_CFGI_0_SID , ent->cfgi.sid); +break ;case CMDQ_OP_CFGI_ALL :/* Cover the entire SID range */ cmd[1] |= FIELD_PREP(CMDQ_CFGI_1_RANGE, 31); @@ -1048,8 +1063,11 @@ static void arm_smmu_write_strtab_ent(struct
arm_smmu_device*smmu, u32 sid,drivers/iommu/arm-smmu-v3.c +3
-0}if (ste->s1_cfg) { +struct iommu_pasid_table_cfg *cfg = &ste->s1_cfg->tables; +BUG_ON (ste_live); dst[1] = cpu_to_le64( + FIELD_PREP(STRTAB_STE_1_S1DSS,
STRTAB_STE_1_S1DSS_SSID0) |
FIELD_PREP(STRTAB_STE_1_S1CIR,STRTAB_STE_1_S1C_CACHE_WBRA) |
FIELD_PREP(STRTAB_STE_1_S1COR,STRTAB_STE_1_S1C_CACHE_WBRA) |
FIELD_PREP(STRTAB_STE_1_S1CSH,ARM_SMMU_SH_ISH) | @@ -1063,7 +1081,9 @@ static void arm_smmu_write_strtab_ent(structarm_smmu_device*smmu, u32 sid,drivers/iommu/arm-smmu-v3.c +0
-0dst[1 ] |=cpu_to_le64 (STRTAB_STE_1_S1STALLD ); val |= (ste->s1_cfg->tables.base &
STRTAB_STE_0_S1CTXPTR_MASK) |drivers/iommu/arm-smmu-v3.c +0
-1- FIELD_PREP(STRTAB_STE_0_CFG,
STRTAB_STE_0_CFG_S1_TRANS);drivers/iommu/arm-smmu-v3.c +1
-0+ FIELD_PREP(STRTAB_STE_0_CFG,
STRTAB_STE_0_CFG_S1_TRANS) |drivers/iommu/arm-smmu-v3.c +2
-0+FIELD_PREP (STRTAB_STE_0_S1CDMAX , cfg->order) | + FIELD_PREP(STRTAB_STE_0_S1FMT, cfg-Quoted 1 line
arm_smmu.s1fmt);
drivers/iommu/arm-smmu-v3.c +14
-0}if (ste->s2_cfg) {@@ -1352,17 +1372,62 @@ static const struct iommu_gather_ops arm_smmu_gather_ops = { };/* PASID TABLE API */ +static void __arm_smmu_sync_cd (struct arm_smmu_domain *smmu_domain, +struct arm_smmu_cmdq_ent *cmd) { +size_t i; +unsigned long flags; +struct arm_smmu_master_data *master; +struct arm_smmu_device *smmu = smmu_domain->smmu; + +spin_lock_irqsave (&smmu_domain->devices_lock, flags); +list_for_each_entry (master, &smmu_domain->devices, list) { +struct iommu_fwspec *fwspec = master->dev- >iommu_fwspec; + +for (i =0 ; i < fwspec->num_ids; i++) { + cmd->cfgi.sid = fwspec->ids[i]; +arm_smmu_cmdq_issue_cmd (smmu, cmd);
drivers/iommu/arm-smmu-v3.c static const struct iommu_gather_ops · line 1400
Quoted 10 lines
drivers/iommu/arm-smmu-v3.c +9
-0+ } + } +spin_unlock_irqrestore (&smmu_domain->devices_lock, flags); + +__arm_smmu_tlb_sync (smmu); +} +static void arm_smmu_sync_cd (void *cookie,int ssid,bool leaf) { +struct arm_smmu_cmdq_ent cmd = { + .opcode =CMDQ_OP_CFGI_CD_ALL ,
Hi Jean, here CMDQ_OP_CFGI_CD opcode 0x5.
drivers/iommu/arm-smmu-v3.c static const struct iommu_gather_ops arm_smmu_gather_ops = { · line 1409
Quoted 9 lines
drivers/iommu/arm-smmu-v3.c +7
-0+ .cfgi = { + .ssid = ssid, + .leaf = leaf, + }, + }; + +__arm_smmu_sync_cd (cookie, &cmd); }
Regards,
Bharat
Re: [PATCH v2 21/40] iommu/arm-smmu-v3: Add support for Substream IDs
Quoted 5 lines · Bharat Kumar Gogada
Review
Quoted 3 lines
static void
arm_smmu_sync_cd(void *cookie, int ssid, bool leaf){(quoted diff) +2
-0+ struct arm_smmu_cmdq_ent cmd = { + .opcode = CMDQ_OP_CFGI_CD_ALL,Hi Jean, here
CMDQ_OP_CFGI_CDopcode0x5.
Woops, nice catch!
I pushed fixes for all comments so far to branch sva/current
Thanks,
Jean