[RFCv2 PATCH 09/36] iommu/fault: Allow blocking fault handlers

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 




Allow device driver to register their fault handler at various stages of
the handling path, by adding flags to iommu_set_ext_fault_handler. Since
we now have a fault workqueue, it is quite easy to call their handler from
thread context instead of IRQ handler.

A driver can request to be called both in blocking and non-blocking
context, so it can filter faults early and only execute the blocking code
for some of them. Add the IOMMU_FAULT_ATOMIC fault flag to tell the driver
where we're calling it from.

Signed-off-by: Jean-Philippe Brucker <jean-philippe.brucker@xxxxxxx>
---

Rob, would this do what you want? The MSM driver can register its handler
with ATOMIC | BLOCKING flags. When called in IRQ context, it can ignore
the fault by returning IOMMU_FAULT_STATUS_NONE, or drop it by returning
IOMMU_FAULT_STATUS_INVALID. When called in thread context, it can sleep
and then return IOMMU_FAULT_STATUS_INVALID to terminate the fault.
---
 drivers/iommu/io-pgfault.c | 16 ++++++++++++++--
 drivers/iommu/iommu.c      | 12 +++++++++---
 include/linux/iommu.h      | 20 +++++++++++++++++++-
 3 files changed, 42 insertions(+), 6 deletions(-)

diff --git a/drivers/iommu/io-pgfault.c b/drivers/iommu/io-pgfault.c
index 532bdb9ce519..3ec8179f58b5 100644
--- a/drivers/iommu/io-pgfault.c
+++ b/drivers/iommu/io-pgfault.c
@@ -91,6 +91,14 @@ static int iommu_fault_handle_single(struct iommu_fault_context *fault)
 	unsigned int access_flags = 0;
 	unsigned int fault_flags = FAULT_FLAG_REMOTE;
 	struct iommu_fault *params = &fault->params;
+	struct iommu_domain *domain = fault->domain;
+
+	if (domain->handler_flags & IOMMU_FAULT_HANDLER_BLOCKING) {
+		ret = domain->ext_handler(domain, fault->dev, &fault->params,
+					  domain->handler_token);
+		if (ret != IOMMU_FAULT_STATUS_NONE)
+			return ret;
+	}
 
 	if (!(params->flags & IOMMU_FAULT_PASID))
 		return ret;
@@ -274,7 +282,8 @@ int handle_iommu_fault(struct iommu_domain *domain, struct device *dev,
 	 * if upper layers showed interest and installed a fault handler,
 	 * invoke it.
 	 */
-	if (domain->ext_handler) {
+	if (domain->handler_flags & IOMMU_FAULT_HANDLER_ATOMIC) {
+		fault->flags |= IOMMU_FAULT_ATOMIC;
 		ret = domain->ext_handler(domain, dev, fault,
 					  domain->handler_token);
 
@@ -290,8 +299,11 @@ int handle_iommu_fault(struct iommu_domain *domain, struct device *dev,
 	}
 
 	/* If the handler is blocking, handle fault in the workqueue */
-	if (fault->flags & IOMMU_FAULT_RECOVERABLE)
+	if ((fault->flags & IOMMU_FAULT_RECOVERABLE) ||
+	    (domain->handler_flags & IOMMU_FAULT_HANDLER_BLOCKING)) {
+		fault->flags &= ~IOMMU_FAULT_ATOMIC;
 		ret = iommu_queue_fault(domain, dev, fault);
+	}
 
 	return iommu_fault_finish(domain, dev, fault, ret);
 }
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index ee956b5fc301..c189648ab7b4 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -1258,7 +1258,9 @@ EXPORT_SYMBOL_GPL(iommu_set_fault_handler);
  * @dev: the device
  * @handler: fault handler
  * @token: user data, will be passed back to the fault handler
- * @flags: IOMMU_FAULT_HANDLER_* parameters.
+ * @flags: IOMMU_FAULT_HANDLER_* parameters. Allows the driver to tell when it
+ * wants to be notified. By default the handler will only be called from atomic
+ * context.
  *
  * This function should be used by IOMMU users which want to be notified
  * whenever an IOMMU fault happens.
@@ -1275,11 +1277,15 @@ void iommu_set_ext_fault_handler(struct device *dev,
 	if (WARN_ON(!domain))
 		return;
 
+	if (!flags)
+		flags |= IOMMU_FAULT_HANDLER_ATOMIC;
+
 	if (WARN_ON(domain->handler || domain->ext_handler))
 		return;
 
 	domain->ext_handler = handler;
 	domain->handler_token = token;
+	domain->handler_flags = flags;
 }
 EXPORT_SYMBOL_GPL(iommu_set_ext_fault_handler);
 
@@ -1824,7 +1830,7 @@ int report_iommu_fault(struct iommu_domain *domain, struct device *dev,
 	int ret = -ENOSYS;
 	struct iommu_fault fault = {
 		.address	= iova,
-		.flags		= flags,
+		.flags		= flags | IOMMU_FAULT_ATOMIC,
 	};
 
 	/*
@@ -1834,7 +1840,7 @@ int report_iommu_fault(struct iommu_domain *domain, struct device *dev,
 	if (domain->handler)
 		ret = domain->handler(domain, dev, iova, flags,
 						domain->handler_token);
-	else if (domain->ext_handler)
+	else if (domain->handler_flags & IOMMU_FAULT_HANDLER_ATOMIC)
 		ret = domain->ext_handler(domain, dev, &fault,
 					  domain->handler_token);
 
diff --git a/include/linux/iommu.h b/include/linux/iommu.h
index 37fafaf07ee2..a6d417785c7b 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -66,6 +66,8 @@ struct notifier_block;
 #define IOMMU_FAULT_GROUP		(1 << 6)
 /* Fault is last of its group */
 #define IOMMU_FAULT_LAST		(1 << 7)
+/* The fault handler is being called from atomic context */
+#define IOMMU_FAULT_ATOMIC		(1 << 8)
 
 /**
  * enum iommu_fault_status - Return status of fault handlers, telling the IOMMU
@@ -97,6 +99,21 @@ enum iommu_fault_status {
 typedef int (*iommu_fault_handler_t)(struct iommu_domain *,
 			struct device *, unsigned long, int, void *);
 
+/*
+ * IOMMU_FAULT_HANDLER_ATOMIC: Notify device driver from within atomic context
+ * (IRQ handler). The callback is not allowed to sleep. If the fault is
+ * recoverable, the driver must either return a fault status telling the IOMMU
+ * driver how to complete the fault (FAILURE, INVALID, HANDLED) or complete the
+ * fault later with iommu_fault_response.
+ */
+#define IOMMU_FAULT_HANDLER_ATOMIC	(1 << 0)
+/*
+ * IOMMU_FAULT_HANDLER_BLOCKING: Notify device driver from a thread. If the fault
+ * is recoverable, the driver must return a fault status telling the IOMMU
+ * driver how to complete the fault (FAILURE, INVALID, HANDLED)
+ */
+#define IOMMU_FAULT_HANDLER_BLOCKING	(1 << 1)
+
 struct iommu_fault {
 	/* Faulting address */
 	unsigned long		address;
@@ -161,6 +178,7 @@ struct iommu_domain {
 	iommu_fault_handler_t handler;
 	iommu_ext_fault_handler_t ext_handler;
 	void *handler_token;
+	int handler_flags;
 	iommu_process_exit_handler_t process_exit;
 	void *process_exit_token;
 	struct iommu_domain_geometry geometry;
@@ -633,7 +651,7 @@ static inline phys_addr_t iommu_iova_to_phys(struct iommu_domain *domain, dma_ad
 }
 
 static inline void iommu_set_fault_handler(struct iommu_domain *domain,
-				iommu_fault_handler_t handler, void *token)
+				iommu_fault_handler_t handler, void *token, int flags)
 {
 }
 
-- 
2.13.3

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Device Tree Compilter]     [Device Tree Spec]     [Linux Driver Backports]     [Video for Linux]     [Linux USB Devel]     [Linux PCI Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Yosemite Backpacking]


  Powered by Linux