From: Jean-Philippe Brucker jean-philippe.brucker@arm.com
ascend inclusion category: feature bugzilla: 14369 CVE: NA
--------------
IOMMU drivers need a way to bind Linux processes to devices. This is used for Shared Virtual Memory (SVM), where devices support paging. In that mode, DMA can directly target virtual addresses of a process.
Introduce boilerplate code for allocating process structures and binding them to devices. Four operations are added to IOMMU drivers:
When a process exits, we need to ensure that devices attached to it stop issuing transactions with its PASID. Let device drivers register a callback to be notified on process exit.
At the moment the callback is set on the domain like the fault handler, because we don't have a structure available for IOMMU masters. This can become problematic if different devices in a domain are managed by distinct device drivers (for example multiple devices in the same group). The problem is the same for the fault handler, so we'll probably fix them all at once.
Signed-off-by: Jean-Philippe Brucker jean-philippe.brucker@arm.com Signed-off-by: Fang Lijun fanglijun3@huawei.com Reviewed-by: Hanjun Guo guohanjun@huawei.com Signed-off-by: Yang Yingliang yangyingliang@huawei.com --- drivers/iommu/Kconfig | 11 ++++++ drivers/iommu/Makefile | 1 + drivers/iommu/iommu-process.c | 68 +++++++++++++++++++++++++++++++++++ include/linux/iommu.h | 32 +++++++++++++++++ 4 files changed, 112 insertions(+) create mode 100644 drivers/iommu/iommu-process.c
diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig index 4f4132d0fca4..80aeff486e0c 100644 --- a/drivers/iommu/Kconfig +++ b/drivers/iommu/Kconfig @@ -112,6 +112,17 @@ config IOMMU_DMA select IOMMU_IOVA select NEED_SG_DMA_LENGTH
+config IOMMU_PROCESS + bool "Process management API for the IOMMU" + depends on MMU_NOTIFIER + select IOMMU_API + help + Enable process management for the IOMMU API. In systems that support + it, device drivers can bind processes to devices and share their page + tables using this API. + + If unsure, say N here. + config IOMMU_SVA bool select IOMMU_API diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile index a6f94cc89f92..1533a9ff4777 100644 --- a/drivers/iommu/Makefile +++ b/drivers/iommu/Makefile @@ -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_DEBUGFS) += iommu-debugfs.o +obj-$(CONFIG_IOMMU_PROCESS) += iommu-process.o obj-$(CONFIG_IOMMU_DMA) += dma-iommu.o obj-$(CONFIG_IOMMU_SVA) += iommu-sva.o obj-$(CONFIG_IOMMU_PAGE_FAULT) += io-pgfault.o diff --git a/drivers/iommu/iommu-process.c b/drivers/iommu/iommu-process.c new file mode 100644 index 000000000000..66ee91c14094 --- /dev/null +++ b/drivers/iommu/iommu-process.c @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Track processes bound to devices + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + * + * Copyright (C) 2017 ARM Ltd. + * + * Author: Jean-Philippe Brucker jean-philippe.brucker@arm.com + */ + +#include <linux/idr.h> +#include <linux/iommu.h> +#include <linux/slab.h> +#include <linux/spinlock.h> + +/* Link between a domain and a process */ +struct iommu_context { + struct iommu_process *process; + struct iommu_domain *domain; + + struct list_head process_head; + struct list_head domain_head; + + /* Number of devices that use this context */ + refcount_t ref; +}; + +/** + * iommu_set_process_exit_handler() - set a callback for stopping the use of + * PASID in a device. + * @dev: the device + * @handler: exit handler + * @token: user data, will be passed back to the exit handler + * + * Users of the bind/unbind API should call this function to set a + * device-specific callback telling them when a process is exiting. + * + * After the callback returns, the device must not issue any more transaction + * with the PASIDs given as argument to the handler. It can be a single PASID + * value or the special IOMMU_PROCESS_EXIT_ALL. + * + * The handler itself should return 0 on success, and an appropriate error code + * otherwise. + */ +void iommu_set_process_exit_handler(struct device *dev, + iommu_process_exit_handler_t handler, + void *token) +{ + struct iommu_domain *domain = iommu_get_domain_for_dev(dev); + + if (WARN_ON(!domain)) + return; + + domain->process_exit = handler; + domain->process_exit_token = token; +} +EXPORT_SYMBOL_GPL(iommu_set_process_exit_handler); diff --git a/include/linux/iommu.h b/include/linux/iommu.h index e6ffd426e267..8bec6a66e065 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -63,6 +63,11 @@ 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 *);
+/* All process are being detached from this device */ +#define IOMMU_PROCESS_EXIT_ALL (-1) +typedef int (*iommu_process_exit_handler_t)(struct iommu_domain *, struct device *dev, + int pasid, void *); + #define IOMMU_SVA_FEAT_IOPF (1 << 0)
struct iommu_domain_geometry { @@ -101,12 +106,27 @@ struct iommu_domain { unsigned long pgsize_bitmap; /* Bitmap of page sizes in use */ iommu_fault_handler_t handler; void *handler_token; + iommu_process_exit_handler_t process_exit; + void *process_exit_token; struct iommu_domain_geometry geometry; void *iova_cookie;
+ unsigned int min_pasid, max_pasid; + struct list_head processes; + struct list_head mm_list; };
+struct iommu_process { + struct pid *pid; + int pasid; + struct list_head domains; + struct kref kref; + + /* Release callback for this process */ + void (*release)(struct iommu_process *process); +}; + struct io_mm { int pasid; /* IOMMU_SVA_FEAT_* */ @@ -1125,4 +1145,16 @@ void iommu_debugfs_setup(void); static inline void iommu_debugfs_setup(void) {} #endif
+#ifdef CONFIG_IOMMU_PROCESS +extern void iommu_set_process_exit_handler(struct device *dev, + iommu_process_exit_handler_t cb, + void *token); +#else /* CONFIG_IOMMU_PROCESS */ +static inline void iommu_set_process_exit_handler(struct device *dev, + iommu_process_exit_handler_t cb, + void *token) +{ +} +#endif /* CONFIG_IOMMU_PROCESS */ + #endif /* __LINUX_IOMMU_H */