Skip to contentSkip to conversation

Introduce automatic DMA configuration for IOMMU masters

RFC PATCH Superseded Will Deacon 5 participants 24 messages Sep 2, 2014 – Sep 10, 2014

Subsystems (5)

v2 of 6 Superseded Latest is v6 →

lore
Share & tools
lore
#0 lore raw

v2 cover Introduce automatic DMA configuration for IOMMU masters

Hello again,

Hot on the heels of my initial RFC, here's a v2 of the posting from here:

RFCv1: http://lists.infradead.org/pipermail/linux-arm-kernel/2014-August/283023.html

The main difference since v1 is that I've introduced some generic structures to represent IOMMUs and their mappings (struct iommu_data and struct iommu_dma_mapping). This allows us to propagate the per-instance data all the way down to the DMA-mapping code, which can then manage a per-instance domain. Note that the ARM dma-mapping implementation does not currently make use of this information.

Since there was a bit of confusion about how this is supposed to work, the way I envisage it hanging together is:

(1) An IOMMU driver uses IOMMU_OF_DECLARE to register an early initialisation callback.

(2) The architecture code calls iommu_init() before kicking off the usual device probing (e.g. of_platform_populate), which executes the IOMMU initialisation callback for each IOMMU node in the device-tree. At this point, the IOMMU driver is expected to allocate iommu_data structure and initialise the priv field before calling of_iommu_set_data on its device_node.

(3) For each master, of_iommu_configure will call of_xlate on the corresponding IOMMU(s), passing of_phandle_args. This allows the IOMMU driver to retrieve the iommu_data with of_iommu_get_data and update its internal data structures with the IDs for the new master. At this point, the per-iommu-instance iommu_data is inserted into a per-device iommu_dma_mapping struct. This allows multiple IOMMU domains to be linked together for devices that master through multiple IOMMUs (but note that iommu_configure currently doesn't not allow this because I'm lazy).

(4) The newly allocated iommu_dma_mapping is passed to arch_setup_dma_ops, which can use it to set the correct dma_ops for the device. If IOMMU-capable ops are in-use, the domain and iova allocator for each iommu_data entry should be initialised (if they haven't been already) and used for managing the per-iommu-instance address space.

There are a bunch of things that could be done on top of this series:

- Port IOMMU driver(s) to it
- Remove the add_device call from iommu_ops
- Add support for devices that master through multiple IOMMUs
- Do something more intelligent with the DMA mask
- Add generic support for device isolation (ie. one domain per device)
- Wire up a different bus (e.g. PCI)
- Move ARM's IOMMU dma-mapping code over to using iommu_dma_mapping instead of the arch-specific dma_iommu_mapping

Anyway, feedback welcome. There are certainly a few different ways of doing this.

Will

--->8

Will Deacon (7):
iommu: provide early initialisation hook for IOMMU drivers
dma-mapping: replace set_arch_dma_coherent_ops with arch_setup_dma_ops
iommu: add new iommu_ops callback for adding an OF device
iommu: provide helper function to configure an IOMMU for an of master
dma-mapping: detect and configure IOMMU in of_dma_configure
arm: call iommu_init before of_platform_populate
arm: dma-mapping: plumb our iommu mapping ops into arch_setup_dma_ops

 arch/arm/include/asm/dma-mapping.h | 11 +++---
 arch/arm/kernel/setup.c            |  5 ++-
 arch/arm/mm/dma-mapping.c          | 72 +++++++++++++++++++++++++++++++++-----
 drivers/iommu/Kconfig              |  2 +-
 drivers/iommu/of_iommu.c           | 66 ++++++++++++++++++++++++++++++++++
 drivers/of/platform.c              | 54 +++++++++++-----------------
 include/asm-generic/vmlinux.lds.h  |  2 ++
 include/linux/dma-mapping.h        | 18 +++++++---
 include/linux/iommu.h              | 11 ++++++
 include/linux/of_iommu.h           | 31 ++++++++++++++++
 10 files changed, 218 insertions(+), 54 deletions(-)
Signature · 1 line
2.1.0
#1 lore raw

RFC PATCH v2 1/7 iommu: provide early initialisation hook for IOMMU drivers

IOMMU drivers must be initialised before any of their upstream devices, otherwise the relevant iommu_ops won't be configured for the bus in question. To solve this, a number of IOMMU drivers use initcalls to initialise the driver before anything has a chance to be probed.

Whilst this solves the immediate problem, it leaves the job of probing the IOMMU completely separate from the iommu_ops to configure the IOMMU, which are called on a per-bus basis and require the driver to figure out exactly which instance of the IOMMU is being requested. In particular, the add_device callback simply passes a struct device to the driver, which then has to parse firmware tables or probe buses to identify the relevant IOMMU instance.

This patch takes the first step in addressing this problem by adding an early initialisation pass for IOMMU drivers, giving them the ability to set some per-instance data on their of_node in the form of a new iommu_data structure. This can later be used when parsing OF masters to identify the IOMMU instance in question.

---
 drivers/iommu/of_iommu.c          | 14 ++++++++++++++
 include/asm-generic/vmlinux.lds.h |  2 ++
 include/linux/iommu.h             |  6 ++++++
 include/linux/of_iommu.h          | 23 +++++++++++++++++++++++
 4 files changed, 45 insertions(+)
drivers/iommu/of_iommu.c +14 -0
index e550ccb7634e..f9209666157c 100644
@@ -89,3 +89,17 @@ int of_get_dma_window(struct device_node *dn, const char *prefix, int index,
 	return 0;
 }
 EXPORT_SYMBOL_GPL(of_get_dma_window);
+
+void __init of_iommu_init(void)
+{
+	struct device_node *np;
+	const struct of_device_id *match, *matches = &__iommu_of_table;
+
+	for_each_matching_node_and_match(np, matches, &match) {
+		const int (*init_fn)(struct device_node *) = match->data;
+
+		if (init_fn(np))
+			pr_err("Failed to initialise IOMMU %s\n",
+				of_node_full_name(np));
+	}
+}
include/asm-generic/vmlinux.lds.h +2 -0
index 5ba0360663a7..b75ede8f99ae 100644
@@ -162,6 +162,7 @@
 #define CLKSRC_OF_TABLES()	OF_TABLE(CONFIG_CLKSRC_OF, clksrc)
 #define IRQCHIP_OF_MATCH_TABLE() OF_TABLE(CONFIG_IRQCHIP, irqchip)
 #define CLK_OF_TABLES()		OF_TABLE(CONFIG_COMMON_CLK, clk)
+#define IOMMU_OF_TABLES()	OF_TABLE(CONFIG_OF_IOMMU, iommu)
 #define RESERVEDMEM_OF_TABLES()	OF_TABLE(CONFIG_OF_RESERVED_MEM, reservedmem)
 #define CPU_METHOD_OF_TABLES()	OF_TABLE(CONFIG_SMP, cpu_method)
 #define EARLYCON_OF_TABLES()	OF_TABLE(CONFIG_SERIAL_EARLYCON, earlycon)
@@ -495,6 +496,7 @@
 	CLK_OF_TABLES()							\
 	RESERVEDMEM_OF_TABLES()						\
 	CLKSRC_OF_TABLES()						\
+	IOMMU_OF_TABLES()						\
 	CPU_METHOD_OF_TABLES()						\
 	KERNEL_DTB()							\
 	IRQCHIP_OF_MATCH_TABLE()					\
include/linux/iommu.h +6 -0
index 20f9a527922a..fdddb14cd8f5 100644
@@ -57,6 +57,12 @@ struct iommu_domain {
 	struct iommu_domain_geometry geometry;
 };
 
+struct iommu_data {
+	struct iommu_domain *domain;
+	struct iova_domain *iovad;
+	void *priv;
+};
+
 #define IOMMU_CAP_CACHE_COHERENCY	0x1
 #define IOMMU_CAP_INTR_REMAP		0x2	/* isolates device intrs */
 
include/linux/of_iommu.h +23 -0
index 51a560f34bca..a39e2d78f735 100644
@@ -1,12 +1,17 @@
 #ifndef __OF_IOMMU_H
 #define __OF_IOMMU_H
 
+#include <linux/iommu.h>
+#include <linux/of.h>
+
 #ifdef CONFIG_OF_IOMMU
 
 extern int of_get_dma_window(struct device_node *dn, const char *prefix,
 			     int index, unsigned long *busno, dma_addr_t *addr,
 			     size_t *size);
 
+extern void of_iommu_init(void);
+
 #else
 
 static inline int of_get_dma_window(struct device_node *dn, const char *prefix,
@@ -16,6 +21,24 @@ static inline int of_get_dma_window(struct device_node *dn, const char *prefix,
 	return -EINVAL;
 }
 
+static inline void of_iommu_init(void) { }
+
 #endif	/* CONFIG_OF_IOMMU */
 
+static inline void of_iommu_set_data(struct device_node *np,
+				     struct iommu_data *data)
+{
+	np->data = data;
+}
+
+static inline struct iommu_data *of_iommu_get_data(struct device_node *np)
+{
+	return np->data;
+}
+
+extern struct of_device_id __iommu_of_table;
+
+#define IOMMU_OF_DECLARE(name, compat, fn) \
+	OF_DECLARE_1(iommu, name, compat, fn)
+
 #endif /* __OF_IOMMU_H */
Signature · 1 line
2.1.0
maintainer
#2 lore raw

RFC PATCH v2 1/7 iommu: provide early initialisation hook for IOMMU drivers

Hello,

Quoted 121 lines · Will Deacon

IOMMU drivers must be initialised before any of their upstream devices, otherwise the relevant iommu_ops won't be configured for the bus in question. To solve this, a number of IOMMU drivers use initcalls to initialise the driver before anything has a chance to be probed.

Whilst this solves the immediate problem, it leaves the job of probing the IOMMU completely separate from the iommu_ops to configure the IOMMU, which are called on a per-bus basis and require the driver to figure out exactly which instance of the IOMMU is being requested. In particular, the add_device callback simply passes a struct device to the driver, which then has to parse firmware tables or probe buses to identify the relevant IOMMU instance.

This patch takes the first step in addressing this problem by adding an early initialisation pass for IOMMU drivers, giving them the ability to set some per-instance data on their of_node in the form of a new iommu_data structure. This can later be used when parsing OF masters to identify the IOMMU instance in question.

---
  drivers/iommu/of_iommu.c          | 14 ++++++++++++++
  include/asm-generic/vmlinux.lds.h |  2 ++
  include/linux/iommu.h             |  6 ++++++
  include/linux/of_iommu.h          | 23 +++++++++++++++++++++++
  4 files changed, 45 insertions(+)
drivers/iommu/of_iommu.c +14 -0
index e550ccb7634e..f9209666157c 100644
@@ -89,3 +89,17 @@ int of_get_dma_window(struct device_node *dn, const char *prefix, int index,
  	return 0;
  }
  EXPORT_SYMBOL_GPL(of_get_dma_window);
+
+void __init of_iommu_init(void)
+{
+	struct device_node *np;
+	const struct of_device_id *match, *matches = &__iommu_of_table;
+
+	for_each_matching_node_and_match(np, matches, &match) {
+		const int (*init_fn)(struct device_node *) = match->data;
+
+		if (init_fn(np))
+			pr_err("Failed to initialise IOMMU %s\n",
+				of_node_full_name(np));
+	}
+}
include/asm-generic/vmlinux.lds.h +2 -0
index 5ba0360663a7..b75ede8f99ae 100644
@@ -162,6 +162,7 @@
  #define CLKSRC_OF_TABLES()	OF_TABLE(CONFIG_CLKSRC_OF, clksrc)
  #define IRQCHIP_OF_MATCH_TABLE() OF_TABLE(CONFIG_IRQCHIP, irqchip)
  #define CLK_OF_TABLES()		OF_TABLE(CONFIG_COMMON_CLK, clk)
+#define IOMMU_OF_TABLES()	OF_TABLE(CONFIG_OF_IOMMU, iommu)
  #define RESERVEDMEM_OF_TABLES()	OF_TABLE(CONFIG_OF_RESERVED_MEM, reservedmem)
  #define CPU_METHOD_OF_TABLES()	OF_TABLE(CONFIG_SMP, cpu_method)
  #define EARLYCON_OF_TABLES()	OF_TABLE(CONFIG_SERIAL_EARLYCON, earlycon)
@@ -495,6 +496,7 @@
  	CLK_OF_TABLES()							\
  	RESERVEDMEM_OF_TABLES()						\
  	CLKSRC_OF_TABLES()						\
+	IOMMU_OF_TABLES()						\
  	CPU_METHOD_OF_TABLES()						\
  	KERNEL_DTB()							\
  	IRQCHIP_OF_MATCH_TABLE()					\
include/linux/iommu.h +6 -0
index 20f9a527922a..fdddb14cd8f5 100644
@@ -57,6 +57,12 @@ struct iommu_domain {
  	struct iommu_domain_geometry geometry;
  };
 
+struct iommu_data {
+	struct iommu_domain *domain;
+	struct iova_domain *iovad;
+	void *priv;
+};
+
  #define IOMMU_CAP_CACHE_COHERENCY	0x1
  #define IOMMU_CAP_INTR_REMAP		0x2	/* isolates device intrs */
 
include/linux/of_iommu.h +12 -0
index 51a560f34bca..a39e2d78f735 100644
@@ -1,12 +1,17 @@
  #ifndef __OF_IOMMU_H
  #define __OF_IOMMU_H
 
+#include <linux/iommu.h>
+#include <linux/of.h>
+
  #ifdef CONFIG_OF_IOMMU
 
  extern int of_get_dma_window(struct device_node *dn, const char *prefix,
  			     int index, unsigned long *busno, dma_addr_t *addr,
  			     size_t *size);
 
+extern void of_iommu_init(void);
+
  #else
 
  static inline int of_get_dma_window(struct device_node *dn, const char *prefix,
@@ -16,6 +21,24 @@ static inline int of_get_dma_window(struct device_node *dn, const char *prefix,
  	return -EINVAL;
  }
 
+static inline void of_iommu_init(void) { }
+
  #endif	/* CONFIG_OF_IOMMU */
 
+static inline void of_iommu_set_data(struct device_node *np,
+				     struct iommu_data *data)
+{
+	np->data = data;
+}

include/linux/of_iommu.h static inline int of_get_dma_window(struct device_node *dn, const char *prefix, · line 42

Quoted 10 lines
include/linux/of_iommu.h +10 -0
+
+static inline struct iommu_data *of_iommu_get_data(struct device_node *np)
+{
+	return np->data;
+}
+
+extern struct of_device_id __iommu_of_table;
+
+#define IOMMU_OF_DECLARE(name, compat, fn) \
+	OF_DECLARE_1(iommu, name, compat, fn)

OF_DECLARE_1 assumes that init_fn doesn't return anything, however in of_iommu_init() you expect the init function to return an error code, so the above lines should be changed to:

typedef int (*of_iommu_init_fn)(struct device_node *);

#define IOMMU_OF_DECLARE(name, compat, fn) \
_OF_DECLARE(iommu, name, compat, fn, of_iommu_init_fn)

include/linux/of_iommu.h static inline int of_get_dma_window(struct device_node *dn, const char *prefix, · line 44

Quoted 2 lines
include/linux/of_iommu.h +1 -0
+
  #endif /* __OF_IOMMU_H */

Best regards

Signature · 2 lines
Marek Szyprowski, PhD
Samsung R&D Institute Poland
#3 lore raw

RFC PATCH v2 2/7 dma-mapping: replace set_arch_dma_coherent_ops with arch_setup_dma_ops

In reply to Will Deacon

set_arch_dma_coherent_ops is called from of_dma_configure in order to swizzle the architectural dma-mapping functions over to a cache-coherent implementation. This is currently implemented only for ARM.

In anticipation of re-using this mechanism for IOMMU-backed dma-mapping ops too, this patch replaces the function with a broader arch_setup_dma_ops callback which is also responsible for setting the DMA mask and offset as well as selecting the correct mapping functions.

A further advantage of this split is that it nicely isolates the of-specific code from the dma-mapping code, allowing potential reuse by other buses (e.g. PCI) in the future.

---
 arch/arm/include/asm/dma-mapping.h | 13 ++++++++----
 drivers/of/platform.c              | 42 ++++++++++----------------------------
 include/linux/dma-mapping.h        |  8 +++-----
 3 files changed, 23 insertions(+), 40 deletions(-)
arch/arm/include/asm/dma-mapping.h +9 -4
index c45b61a4b4a5..dad006dabbe6 100644
@@ -121,12 +121,17 @@ static inline unsigned long dma_max_pfn(struct device *dev)
 }
 #define dma_max_pfn(dev) dma_max_pfn(dev)
 
-static inline int set_arch_dma_coherent_ops(struct device *dev)
+static inline void arch_setup_dma_ops(struct device *dev, u64 mask,
+				      unsigned long offset, bool coherent)
 {
-	set_dma_ops(dev, &arm_coherent_dma_ops);
-	return 0;
+	dev->coherent_dma_mask	= mask;
+	dev->dma_mask		= &dev->coherent_dma_mask;
+	dev->dma_pfn_offset	= offset;
+
+	if (coherent)
+		set_dma_ops(dev, &arm_coherent_dma_ops);
 }
-#define set_arch_dma_coherent_ops(dev)	set_arch_dma_coherent_ops(dev)
+#define arch_setup_dma_ops arch_setup_dma_ops
 
 static inline dma_addr_t phys_to_dma(struct device *dev, phys_addr_t paddr)
 {
drivers/of/platform.c +11 -31

54 lines not on this page · raw message

include/linux/dma-mapping.h +3 -5
index 931b70986272..0f7f7b68b0db 100644
@@ -129,11 +129,9 @@ static inline int dma_coerce_mask_and_coherent(struct device *dev, u64 mask)
 
 extern u64 dma_get_required_mask(struct device *dev);
 
-#ifndef set_arch_dma_coherent_ops
-static inline int set_arch_dma_coherent_ops(struct device *dev)
-{
-	return 0;
-}
+#ifndef arch_setup_dma_ops
+static inline void arch_setup_dma_ops(struct device *dev, u64 mask,
+				      unsigned long offset, bool coherent) { }
 #endif
 
 static inline unsigned int dma_get_max_seg_size(struct device *dev)
Signature · 1 line
2.1.0
#4 lore raw

RFC PATCH v2 2/7 dma-mapping: replace set_arch_dma_coherent_ops with arch_setup_dma_ops

Hi Will,

Quoted 4 lines · Will Deacon

set_arch_dma_coherent_ops is called from of_dma_configure in order to swizzle the architectural dma-mapping functions over to a cache-coherent implementation. This is currently implemented only for ARM.

Quoted 8 lines

In anticipation of re-using this mechanism for IOMMU-backed dma-mapping ops too, this patch replaces the function with a broader arch_setup_dma_ops callback which is also responsible for setting the DMA mask and offset as well as selecting the correct mapping functions.

A further advantage of this split is that it nicely isolates the of-specific code from the dma-mapping code, allowing potential reuse by other buses (e.g. PCI) in the future.

I think this patch can introduce a regression if it will be used as is :(

When this code was initially created there ware a lot of discussion about and finally it was decided to configure all common (for all arches) DMA specific parameters for devices in common code, while strictly arch specific things using arch specific APIs/callbacks.

The following parameters are common now:
dev->coherent_dma_mask = mask;
dev->dma_mask = &dev->coherent_dma_mask;
dev->dma_pfn_offset = offset;

and they need to be set always, otherwise it will affect on other DT-based arches.

Links:
- [PATCH v2 0/7] ARM: dma: Support dma-ranges and dma-coherent
http://www.spinics.net/lists/arm-kernel/msg311678.html

- [PATCH 0/7] of: setup dma parameters using dma-ranges and dma-coherent
https://lkml.org/lkml/2014/3/6/186

- [PATCH v2 0/7] of: setup dma parameters using dma-ranges and dma-coherent
https://lkml.org/lkml/2014/4/19/80

- [PATCH v3 0/7] of: setup dma parameters using dma-ranges and dma-coherent
http://thread.gmane.org/gmane.linux.kernel/1690224/focus=319246

Quoted 8 lines
---
  arch/arm/include/asm/dma-mapping.h | 13 ++++++++----
  drivers/of/platform.c              | 42 ++++++++++----------------------------
  include/linux/dma-mapping.h        |  8 +++-----
  3 files changed, 23 insertions(+), 40 deletions(-)

[...]

Quoted 14 lines
drivers/of/platform.c +1 -0
+	arch_setup_dma_ops(dev, DMA_BIT_MASK(32), offset, coherent);
  }
 
  /**
include/linux/dma-mapping.h +0 -1
index 931b70986272..0f7f7b68b0db 100644
@@ -129,11 +129,9 @@ static inline int dma_coerce_mask_and_coherent(struct device *dev, u64 mask)
 
  extern u64 dma_get_required_mask(struct device *dev);
 
-#ifndef set_arch_dma_coherent_ops

include/linux/dma-mapping.h static inline int dma_coerce_mask_and_coherent(struct device *dev, u64 mask) · line 137

Quoted 10 lines
include/linux/dma-mapping.h +3 -4
-static inline int set_arch_dma_coherent_ops(struct device *dev)
-{
-	return 0;
-}
+#ifndef arch_setup_dma_ops
+static inline void arch_setup_dma_ops(struct device *dev, u64 mask,
+				      unsigned long offset, bool coherent) { }
  #endif
 
  static inline unsigned int dma_get_max_seg_size(struct device *dev)

Regards,
-grygorii

#5 lore raw

RFC PATCH v2 2/7 dma-mapping: replace set_arch_dma_coherent_ops with arch_setup_dma_ops

Quoted 1 line · Grygorii Strashko

Hi Will,

Hi Grygorii,

Quoted 21 lines · Will Deacon
Quoted 4 lines

set_arch_dma_coherent_ops is called from of_dma_configure in order to swizzle the architectural dma-mapping functions over to a cache-coherent implementation. This is currently implemented only for ARM.

Quoted 8 lines

In anticipation of re-using this mechanism for IOMMU-backed dma-mapping ops too, this patch replaces the function with a broader arch_setup_dma_ops callback which is also responsible for setting the DMA mask and offset as well as selecting the correct mapping functions.

A further advantage of this split is that it nicely isolates the of-specific code from the dma-mapping code, allowing potential reuse by other buses (e.g. PCI) in the future.

I think this patch can introduce a regression if it will be used as is :(

When this code was initially created there ware a lot of discussion about and finally it was decided to configure all common (for all arches) DMA specific parameters for devices in common code, while strictly arch specific things using arch specific APIs/callbacks.

Quoted 7 lines

The following parameters are common now:
dev->coherent_dma_mask = mask;
dev->dma_mask = &dev->coherent_dma_mask;
dev->dma_pfn_offset = offset;

and they need to be set always, otherwise it will affect on other DT-based arches.

Ok, in which case we can either configure the struct device in of_dma_configure before calling arch_setup_dma_ops, or we can do the work in a generic version of arch_setup_dma_ops (that I haven't added yet).

The former is how it's done in mainline atm, so I'll stick with that.

Thanks,

Will

#6 lore raw

RFC PATCH v2 2/7 dma-mapping: replace set_arch_dma_coherent_ops with arch_setup_dma_ops

Hi Will,

Quoted 28 lines · Will Deacon
Quoted 26 lines · Grygorii Strashko
Quoted 4 lines · Will Deacon

set_arch_dma_coherent_ops is called from of_dma_configure in order to swizzle the architectural dma-mapping functions over to a cache-coherent implementation. This is currently implemented only for ARM.

Quoted 8 lines

In anticipation of re-using this mechanism for IOMMU-backed dma-mapping ops too, this patch replaces the function with a broader arch_setup_dma_ops callback which is also responsible for setting the DMA mask and offset as well as selecting the correct mapping functions.

A further advantage of this split is that it nicely isolates the of-specific code from the dma-mapping code, allowing potential reuse by other buses (e.g. PCI) in the future.

I think this patch can introduce a regression if it will be used as is :(

When this code was initially created there ware a lot of discussion about and finally it was decided to configure all common (for all arches) DMA specific parameters for devices in common code, while strictly arch specific things using arch specific APIs/callbacks.

The following parameters are common now:
dev->coherent_dma_mask = mask;
dev->dma_mask = &dev->coherent_dma_mask;
dev->dma_pfn_offset = offset;

Quoted 6 lines
Quoted 2 lines

and they need to be set always, otherwise it will affect on other DT-based arches.

Ok, in which case we can either configure the struct device in of_dma_configure before calling arch_setup_dma_ops, or we can do the work in a generic version of arch_setup_dma_ops (that I haven't added yet).

Just think that it would more simpler to add arch-API:
arch_setup_iommu(struct device *dev, struct iommu_dma_mapping *iommu)

and call it at the end of of_dma_configure() as following:

  arch_setup_iommu(dev, of_iommu_configure(dev));

so it will setup IOMMU parameters for device and overwrite DMA parameters respectively.

if IOMMU is not present/disabled arch_setup_iommu() will be a NOP and system will roll back to default (old) behavior.

Also it will simplify ARM dma-mapping changes -
only IOMMU code will need to be added.

Best regards,
-grygorii

#7 lore raw

RFC PATCH v2 3/7 iommu: add new iommu_ops callback for adding an OF device

In reply to Will Deacon

This patch adds a new function to the iommu_ops structure to allow an OF device to be added to a specific IOMMU instance using the recently merged generic devicetree binding for IOMMUs. The callback (of_xlate) takes a struct device representing the master and an of_phandle_args representing the IOMMU and the correspondong IDs for the new master.

---
 include/linux/iommu.h | 5 +++++
 1 file changed, 5 insertions(+)
include/linux/iommu.h +5 -0
index fdddb14cd8f5..3e766b85daa3 100644
@@ -21,6 +21,7 @@
 
 #include <linux/errno.h>
 #include <linux/err.h>
+#include <linux/of.h>
 #include <linux/types.h>
 #include <trace/events/iommu.h>
 
@@ -136,6 +137,10 @@ struct iommu_ops {
 	/* Get the numer of window per domain */
 	u32 (*domain_get_windows)(struct iommu_domain *domain);
 
+#ifdef CONFIG_OF_IOMMU
+	int (*of_xlate)(struct device *dev, struct of_phandle_args *args);
+#endif
+
 	unsigned long pgsize_bitmap;
 };
 
Signature · 1 line
2.1.0
maintainer
#8 lore raw

RFC PATCH v2 3/7 iommu: add new iommu_ops callback for adding an OF device

Hello,

Quoted 24 lines · Will Deacon

This patch adds a new function to the iommu_ops structure to allow an OF device to be added to a specific IOMMU instance using the recently merged generic devicetree binding for IOMMUs. The callback (of_xlate) takes a struct device representing the master and an of_phandle_args representing the IOMMU and the correspondong IDs for the new master.

---
  include/linux/iommu.h | 5 +++++
  1 file changed, 5 insertions(+)
include/linux/iommu.h +1 -0
index fdddb14cd8f5..3e766b85daa3 100644
@@ -21,6 +21,7 @@
 
  #include <linux/errno.h>
  #include <linux/err.h>
+#include <linux/of.h>
  #include <linux/types.h>
  #include <trace/events/iommu.h>
 

include/linux/iommu.h struct iommu_ops { · line 141

Quoted 5 lines
include/linux/iommu.h +2 -0
@@ -136,6 +137,10 @@ struct iommu_ops {
  	/* Get the numer of window per domain */
  	u32 (*domain_get_windows)(struct iommu_domain *domain);
 
+#ifdef CONFIG_OF_IOMMU
+	int (*of_xlate)(struct device *dev, struct of_phandle_args *args);

If I understand correctly how it is designed to work, then it should be:

struct iommu_data *(*of_xlate)(struct device *dev, struct of_phandle_args *args);

include/linux/iommu.h struct iommu_ops { · line 146

Quoted 5 lines
include/linux/iommu.h +2 -0
+#endif
+
  	unsigned long pgsize_bitmap;
  };
 

Best regards

Signature · 2 lines
Marek Szyprowski, PhD
Samsung R&D Institute Poland
#9 lore raw

RFC PATCH v2 3/7 iommu: add new iommu_ops callback for adding an OF device

Quoted 28 lines · Marek Szyprowski
Quoted 27 lines · Will Deacon

This patch adds a new function to the iommu_ops structure to allow an OF device to be added to a specific IOMMU instance using the recently merged generic devicetree binding for IOMMUs. The callback (of_xlate) takes a struct device representing the master and an of_phandle_args representing the IOMMU and the correspondong IDs for the new master.

---
  include/linux/iommu.h | 5 +++++
  1 file changed, 5 insertions(+)
include/linux/iommu.h +1 -0
index fdddb14cd8f5..3e766b85daa3 100644
@@ -21,6 +21,7 @@
 
  #include <linux/errno.h>
  #include <linux/err.h>
+#include <linux/of.h>
  #include <linux/types.h>
  #include <trace/events/iommu.h>
 
@@ -136,6 +137,10 @@ struct iommu_ops {
  	/* Get the numer of window per domain */
  	u32 (*domain_get_windows)(struct iommu_domain *domain);
 
Quoted 7 lines

include/linux/iommu.h struct iommu_ops { · line 141

Quoted 2 lines
include/linux/iommu.h +2 -0
+#ifdef CONFIG_OF_IOMMU
+	int (*of_xlate)(struct device *dev, struct of_phandle_args *args);

If I understand correctly how it is designed to work, then it should be:

struct iommu_data *(*of_xlate)(struct device *dev, struct of_phandle_args *args);

Yeah, that might be cleaner than the of_iommu_get_data call in of_dma_configure. I'm currently cooking a v3, so I'll include this change.

Will

#10 lore raw

RFC PATCH v2 3/7 iommu: add new iommu_ops callback for adding an OF device

Quoted 7 lines · Will Deacon
Quoted 6 lines · Marek Szyprowski

include/linux/iommu.h struct iommu_ops { · line 141

Quoted 2 lines · Will Deacon
include/linux/iommu.h +2 -0
+#ifdef CONFIG_OF_IOMMU
+	int (*of_xlate)(struct device *dev, struct of_phandle_args *args);

If I understand correctly how it is designed to work, then it should be:

Quoted 5 lines
Quoted 2 lines

struct iommu_data *(*of_xlate)(struct device *dev, struct of_phandle_args *args);

Yeah, that might be cleaner than the of_iommu_get_data call in of_dma_configure. I'm currently cooking a v3, so I'll include this change.

Actually, I spoke too soon. If we make of_xlate return the iommu_data, then we have to continue getting the iommu_ops from the bus_type, otherwise we have no way to call the right of_xlate.

So I'd rather leave of_dma_configure using of_iommu_get_data (I'm currently ripping out the bus code in there) so that we can embed the iommu_ops in iommu_data instead.

Will

#11 lore raw

RFC PATCH v2 4/7 iommu: provide helper function to configure an IOMMU for an of master

In reply to Will Deacon

The generic IOMMU device-tree bindings can be used to add arbitrary OF masters to an IOMMU with a compliant binding.

This patch introduces of_iommu_configure, which does exactly that.

---
 drivers/iommu/Kconfig       |  2 +-
 drivers/iommu/of_iommu.c    | 52 +++++++++++++++++++++++++++++++++++++++++++++
 include/linux/dma-mapping.h |  7 ++++++
 include/linux/of_iommu.h    |  8 +++++++
 4 files changed, 68 insertions(+), 1 deletion(-)
drivers/iommu/Kconfig +1 -1
index dd5112265cc9..6d13f962f156 100644
@@ -15,7 +15,7 @@ if IOMMU_SUPPORT
 
 config OF_IOMMU
        def_bool y
-       depends on OF
+       depends on OF && IOMMU_API
 
 config FSL_PAMU
 	bool "Freescale IOMMU support"
drivers/iommu/of_iommu.c +52 -0

64 lines not on this page · raw message

include/linux/dma-mapping.h +7 -0
index 0f7f7b68b0db..16bf92f71c3e 100644
@@ -62,6 +62,13 @@ struct dma_map_ops {
 	int is_phys;
 };
 
+struct iommu_data;
+
+struct iommu_dma_mapping {
+	struct iommu_data *iommu;
+	struct list_head node;
+};
+
 #define DMA_BIT_MASK(n)	(((n) == 64) ? ~0ULL : ((1ULL<<(n))-1))
 
 #define DMA_MASK_NONE	0x0ULL
include/linux/of_iommu.h +8 -0
index a39e2d78f735..f2d3b1e780d7 100644
@@ -1,9 +1,12 @@
 #ifndef __OF_IOMMU_H
 #define __OF_IOMMU_H
 
+#include <linux/device.h>
 #include <linux/iommu.h>
 #include <linux/of.h>
 
+struct iommu_dma_mapping;
+
 #ifdef CONFIG_OF_IOMMU
 
 extern int of_get_dma_window(struct device_node *dn, const char *prefix,
@@ -11,6 +14,7 @@ extern int of_get_dma_window(struct device_node *dn, const char *prefix,
 			     size_t *size);
 
 extern void of_iommu_init(void);
+extern struct iommu_dma_mapping *of_iommu_configure(struct device *dev);
 
 #else
 
@@ -22,6 +26,10 @@ static inline int of_get_dma_window(struct device_node *dn, const char *prefix,
 }
 
 static inline void of_iommu_init(void) { }
+static inline struct iommu_dma_mapping *of_iommu_configure(struct device *dev)
+{
+	return NULL;
+}
 
 #endif	/* CONFIG_OF_IOMMU */
 
Signature · 1 line
2.1.0
maintainer
#12 lore raw

RFC PATCH v2 4/7 iommu: provide helper function to configure an IOMMU for an of master

drivers/iommu/of_iommu.c int of_get_dma_window(struct device_node *dn, const char *prefix, int index, · line 99

Quoted 6 lines · On Tuesday 02 September 2014 18:56:24 Will Deacon wrote:
drivers/iommu/of_iommu.c +6 -0
+struct iommu_dma_mapping *of_iommu_configure(struct device *dev)
+{
+       struct of_phandle_args iommu_spec;
+       struct iommu_dma_mapping *mapping;
+       struct bus_type *bus = dev->bus;
+       const struct iommu_ops *ops = bus->iommu_ops;

I think it would be best to not even introduce the tight coupling between bus_type and iommu_ops here, one of the main reasons we are doing this is to break that connection.

Better put the iommu_ops into the iommu_data pointer that gets looked up per iommu device.

Quoted 11 lines
drivers/iommu/of_iommu.c +11 -0
+       struct iommu_data *iommu = NULL;
+       int idx = 0;
+
+       if (!iommu_present(bus) || !ops->of_xlate)
+               return NULL;
+
+       /*
+        * We don't currently walk up the tree looking for a parent IOMMU.
+        * See the `Notes:' section of
+        * Documentation/devicetree/bindings/iommu/iommu.txt
+        */

drivers/iommu/of_iommu.c int of_get_dma_window(struct device_node *dn, const char *prefix, int index, · line 119

Quoted 10 lines
drivers/iommu/of_iommu.c +10 -0
+        */
+       while (!of_parse_phandle_with_args(dev->of_node, "iommus",
+                                          "#iommu-cells", idx,
+                                          &iommu_spec)) {
+               struct device_node *np = iommu_spec.np;
+               struct iommu_data *data = of_iommu_get_data(np);
+
+               if (!iommu) {
+                       if (!ops->of_xlate(dev, &iommu_spec))
+                               iommu = data;

I would make the first argument of the of_xlate function the iommu_data, so the code can find the right instance.

Maybe also add an extra argument at the end that can be used by the PCI code and potentially other drivers with multiple master IDs behind one "iommus" property to pass some value identifying which of the masters is meant.

The format of that is unfortunately bus specific and our platform_bus really covers a number of different hardware buses (AXI, AHB, OPB, ...) but the caller should be able to provide the data in the right form that the iommu understands. I would try using a single u64 argument as a start, hoping that this covers all the buses we need. At least it's enough for PCI (bus/device/function) and for AXI (requester-id?).

Quoted 11 lines
drivers/iommu/of_iommu.c +10 -0
+               } else if (iommu != data) {
+                       pr_warn("Rejecting device %s with multiple IOMMU instances\n",
+                               dev_name(dev));
+                       iommu = NULL;
+               }
+
+               of_node_put(np);
+
+               if (!iommu)
+                       break;

drivers/iommu/of_iommu.c int of_get_dma_window(struct device_node *dn, const char *prefix, int index, · line 139

Quoted 10 lines
drivers/iommu/of_iommu.c +10 -0
+
+               idx++;
+       }
+
+       if (!iommu)
+               return NULL;
+
+       mapping = devm_kzalloc(dev, sizeof(*mapping), GFP_KERNEL);
+       if (!mapping)
+               return NULL;

I don't think it's safe to use devm_* functions here: this is during device discovery, and this data will be freed if probe() fails or the device gets removed from a driver.

Arnd

#13 lore raw

RFC PATCH v2 4/7 iommu: provide helper function to configure an IOMMU for an of master

Hi Arnd,

Thanks for the review.

Quoted 12 lines · Arnd Bergmann

drivers/iommu/of_iommu.c int of_get_dma_window(struct device_node *dn, const char *prefix, int index, · line 99

Quoted 6 lines · On Tuesday 02 September 2014 18:56:24 Will Deacon wrote:
drivers/iommu/of_iommu.c +6 -0
+struct iommu_dma_mapping *of_iommu_configure(struct device *dev)
+{
+       struct of_phandle_args iommu_spec;
+       struct iommu_dma_mapping *mapping;
+       struct bus_type *bus = dev->bus;
+       const struct iommu_ops *ops = bus->iommu_ops;

I think it would be best to not even introduce the tight coupling between bus_type and iommu_ops here, one of the main reasons we are doing this is to break that connection.

Quoted 2 lines

Better put the iommu_ops into the iommu_data pointer that gets looked up per iommu device.

Yes, I'll add that. It's a bit weird, because those same ops will later be duplicated in iommu_data->domain->ops, but that's an artifact of how the domain is currently constructed by iommu_domain_alloc.

Quoted 16 lines
Quoted 16 lines
drivers/iommu/of_iommu.c +16 -0
+       struct iommu_data *iommu = NULL;
+       int idx = 0;
+
+       if (!iommu_present(bus) || !ops->of_xlate)
+               return NULL;
+
+       /*
+        * We don't currently walk up the tree looking for a parent IOMMU.
+        * See the `Notes:' section of
+        * Documentation/devicetree/bindings/iommu/iommu.txt
+        */
+        */
+       while (!of_parse_phandle_with_args(dev->of_node, "iommus",
+                                          "#iommu-cells", idx,
+                                          &iommu_spec)) {
+               struct device_node *np = iommu_spec.np;
Quoted 8 lines

drivers/iommu/of_iommu.c int of_get_dma_window(struct device_node *dn, const char *prefix, int index, · line 119

Quoted 5 lines
drivers/iommu/of_iommu.c +5 -0
+               struct iommu_data *data = of_iommu_get_data(np);
+
+               if (!iommu) {
+                       if (!ops->of_xlate(dev, &iommu_spec))
+                               iommu = data;

I would make the first argument of the of_xlate function the iommu_data, so the code can find the right instance.

Oops, that's what I intended. Well spotted.

Quoted 11 lines

Maybe also add an extra argument at the end that can be used by the PCI code and potentially other drivers with multiple master IDs behind one "iommus" property to pass some value identifying which of the masters is meant.

The format of that is unfortunately bus specific and our platform_bus really covers a number of different hardware buses (AXI, AHB, OPB, ...) but the caller should be able to provide the data in the right form that the iommu understands. I would try using a single u64 argument as a start, hoping that this covers all the buses we need. At least it's enough for PCI (bus/device/function) and for AXI (requester-id?).

Yeah, I think that will work. It's kind of like a device `index' for a device that sits behind a bridge (trying to avoid yet another ID parameter :).

Quoted 18 lines
Quoted 18 lines
drivers/iommu/of_iommu.c +17 -0
+               } else if (iommu != data) {
+                       pr_warn("Rejecting device %s with multiple IOMMU instances\n",
+                               dev_name(dev));
+                       iommu = NULL;
+               }
+
+               of_node_put(np);
+
+               if (!iommu)
+                       break;
+
+               idx++;
+       }
+
+       if (!iommu)
+               return NULL;
+
Quoted 8 lines

drivers/iommu/of_iommu.c int of_get_dma_window(struct device_node *dn, const char *prefix, int index, · line 139

Quoted 4 lines
drivers/iommu/of_iommu.c +3 -0
+       mapping = devm_kzalloc(dev, sizeof(*mapping), GFP_KERNEL);
+       if (!mapping)
+               return NULL;

I don't think it's safe to use devm_* functions here: this is during device discovery, and this data will be freed if probe() fails or the device gets removed from a driver.

So I can make this a standard kzalloc, but I have no idea where the corresponding kfree should live. Is there something equivalent to of_dma_unconfigure, or is this data that is expected to persist?

Will

maintainer
#14 lore raw

RFC PATCH v2 4/7 iommu: provide helper function to configure an IOMMU for an of master

Quoted 27 lines · On Thursday 04 September 2014 12:26:25 Will Deacon wrote:
Quoted 18 lines
Quoted 18 lines
drivers/iommu/of_iommu.c +17 -0
+               } else if (iommu != data) {
+                       pr_warn("Rejecting device %s with multiple IOMMU instances\n",
+                               dev_name(dev));
+                       iommu = NULL;
+               }
+
+               of_node_put(np);
+
+               if (!iommu)
+                       break;
+
+               idx++;
+       }
+
+       if (!iommu)
+               return NULL;
+
Quoted 8 lines

drivers/iommu/of_iommu.c int of_get_dma_window(struct device_node *dn, const char *prefix, int index, · line 139

Quoted 4 lines
drivers/iommu/of_iommu.c +3 -0
+       mapping = devm_kzalloc(dev, sizeof(*mapping), GFP_KERNEL);
+       if (!mapping)
+               return NULL;

I don't think it's safe to use devm_* functions here: this is during device discovery, and this data will be freed if probe() fails or the device gets removed from a driver.

Quoted 4 lines

So I can make this a standard kzalloc, but I have no idea where the corresponding kfree should live. Is there something equivalent to of_dma_unconfigure, or is this data that is expected to persist?

Can this be a simple kfree in of_platform_device_create_pdata?

Arnd

#15 lore raw

RFC PATCH v2 4/7 iommu: provide helper function to configure an IOMMU for an of master

Quoted 28 lines · Arnd Bergmann
Quoted 27 lines · On Thursday 04 September 2014 12:26:25 Will Deacon wrote:
Quoted 26 lines
Quoted 12 lines
drivers/iommu/of_iommu.c +10 -0
+               } else if (iommu != data) {
+                       pr_warn("Rejecting device %s with multiple IOMMU instances\n",
+                               dev_name(dev));
+                       iommu = NULL;
+               }
+
+               of_node_put(np);
+
+               if (!iommu)
+                       break;

drivers/iommu/of_iommu.c int of_get_dma_window(struct device_node *dn, const char *prefix, int index, · line 139

Quoted 10 lines
drivers/iommu/of_iommu.c +10 -0
+
+               idx++;
+       }
+
+       if (!iommu)
+               return NULL;
+
+       mapping = devm_kzalloc(dev, sizeof(*mapping), GFP_KERNEL);
+       if (!mapping)
+               return NULL;

I don't think it's safe to use devm_* functions here: this is during device discovery, and this data will be freed if probe() fails or the device gets removed from a driver.

Quoted 6 lines
Quoted 4 lines

So I can make this a standard kzalloc, but I have no idea where the corresponding kfree should live. Is there something equivalent to of_dma_unconfigure, or is this data that is expected to persist?

Can this be a simple kfree in of_platform_device_create_pdata?

We could certainly hook into the error path there (when of_device_add fails), yes, but I was also thinking about device removal and whether we need to care about that. I guess not for the OF case.

Will

#16 lore raw

RFC PATCH v2 4/7 iommu: provide helper function to configure an IOMMU for an of master

In reply to Will Deacon

Hi Will,

Thank you for the patch.

Quoted 58 lines · On Tuesday 02 September 2014 18:56:24 Will Deacon wrote:

The generic IOMMU device-tree bindings can be used to add arbitrary OF masters to an IOMMU with a compliant binding.

This patch introduces of_iommu_configure, which does exactly that.

---
 drivers/iommu/Kconfig       |  2 +-
 drivers/iommu/of_iommu.c    | 52 ++++++++++++++++++++++++++++++++++++++++++
 include/linux/dma-mapping.h |  7 ++++++
 include/linux/of_iommu.h    |  8 +++++++
 4 files changed, 68 insertions(+), 1 deletion(-)
drivers/iommu/Kconfig +1 -1
index dd5112265cc9..6d13f962f156 100644
@@ -15,7 +15,7 @@ if IOMMU_SUPPORT
 
 config OF_IOMMU
        def_bool y
-       depends on OF
+       depends on OF && IOMMU_API
 
 config FSL_PAMU
 	bool "Freescale IOMMU support"
drivers/iommu/of_iommu.c +17 -0
index f9209666157c..1188c929ffa7 100644
@@ -18,6 +18,7 @@
  */
 
 #include <linux/export.h>
+#include <linux/iommu.h>
 #include <linux/limits.h>
 #include <linux/of.h>
 #include <linux/of_iommu.h>
@@ -90,6 +91,57 @@ int of_get_dma_window(struct device_node *dn, const char
 *prefix, int index, }
 EXPORT_SYMBOL_GPL(of_get_dma_window);
 
+struct iommu_dma_mapping *of_iommu_configure(struct device *dev)
+{
+	struct of_phandle_args iommu_spec;
+	struct iommu_dma_mapping *mapping;
+	struct bus_type *bus = dev->bus;
+	const struct iommu_ops *ops = bus->iommu_ops;
+	struct iommu_data *iommu = NULL;
+	int idx = 0;
+
+	if (!iommu_present(bus) || !ops->of_xlate)
+		return NULL;
+
+	/*
+	 * We don't currently walk up the tree looking for a parent IOMMU.
+	 * See the `Notes:' section of
+	 * Documentation/devicetree/bindings/iommu/iommu.txt

drivers/iommu/of_iommu.c int of_get_dma_window(struct device_node *dn, const char · line 119

Quoted 10 lines
drivers/iommu/of_iommu.c +10 -0
+	 */
+	while (!of_parse_phandle_with_args(dev->of_node, "iommus",
+					   "#iommu-cells", idx,
+					   &iommu_spec)) {
+		struct device_node *np = iommu_spec.np;
+		struct iommu_data *data = of_iommu_get_data(np);
+
+		if (!iommu) {
+			if (!ops->of_xlate(dev, &iommu_spec))
+				iommu = data;

If I understand the code correctly, this will call of_xlate for the first IOMMU reference only and silently ignore all subsequent references if they point to the same IOMMU device but with different stream/requester IDs. Is that the desired behaviour ?

Quoted 81 lines
drivers/iommu/of_iommu.c +25 -0
+		} else if (iommu != data) {
+			pr_warn("Rejecting device %s with multiple IOMMU instances\n",
+				dev_name(dev));
+			iommu = NULL;
+		}
+
+		of_node_put(np);
+
+		if (!iommu)
+			break;
+
+		idx++;
+	}
+
+	if (!iommu)
+		return NULL;
+
+	mapping = devm_kzalloc(dev, sizeof(*mapping), GFP_KERNEL);
+	if (!mapping)
+		return NULL;
+
+	mapping->iommu = iommu;
+	return mapping;
+}
+
 void __init of_iommu_init(void)
 {
 	struct device_node *np;
include/linux/dma-mapping.h +7 -0
index 0f7f7b68b0db..16bf92f71c3e 100644
@@ -62,6 +62,13 @@ struct dma_map_ops {
 	int is_phys;
 };
 
+struct iommu_data;
+
+struct iommu_dma_mapping {
+	struct iommu_data *iommu;
+	struct list_head node;
+};
+
 #define DMA_BIT_MASK(n)	(((n) == 64) ? ~0ULL : ((1ULL<<(n))-1))
 
 #define DMA_MASK_NONE	0x0ULL
include/linux/of_iommu.h +7 -0
index a39e2d78f735..f2d3b1e780d7 100644
@@ -1,9 +1,12 @@
 #ifndef __OF_IOMMU_H
 #define __OF_IOMMU_H
 
+#include <linux/device.h>
 #include <linux/iommu.h>
 #include <linux/of.h>
 
+struct iommu_dma_mapping;
+
 #ifdef CONFIG_OF_IOMMU
 
 extern int of_get_dma_window(struct device_node *dn, const char *prefix,
@@ -11,6 +14,7 @@ extern int of_get_dma_window(struct device_node *dn, const
 char *prefix, size_t *size);
 
 extern void of_iommu_init(void);
+extern struct iommu_dma_mapping *of_iommu_configure(struct device *dev);
 
 #else
 
@@ -22,6 +26,10 @@ static inline int of_get_dma_window(struct device_node
 *dn, const char *prefix, }
 
 static inline void of_iommu_init(void) { }
+static inline struct iommu_dma_mapping *of_iommu_configure(struct device
 *dev) +{
+	return NULL;
+}
 
 #endif	/* CONFIG_OF_IOMMU */
Signature · 3 lines
Regards,

Laurent Pinchart
#17 lore raw

RFC PATCH v2 4/7 iommu: provide helper function to configure an IOMMU for an of master

Hi Laurent,

Cheers for the review.

Quoted 24 lines · Laurent Pinchart
Quoted 23 lines · On Tuesday 02 September 2014 18:56:24 Will Deacon wrote:
drivers/iommu/of_iommu.c +23 -0
+struct iommu_dma_mapping *of_iommu_configure(struct device *dev)
+{
+	struct of_phandle_args iommu_spec;
+	struct iommu_dma_mapping *mapping;
+	struct bus_type *bus = dev->bus;
+	const struct iommu_ops *ops = bus->iommu_ops;
+	struct iommu_data *iommu = NULL;
+	int idx = 0;
+
+	if (!iommu_present(bus) || !ops->of_xlate)
+		return NULL;
+
+	/*
+	 * We don't currently walk up the tree looking for a parent IOMMU.
+	 * See the `Notes:' section of
+	 * Documentation/devicetree/bindings/iommu/iommu.txt
+	 */
+	while (!of_parse_phandle_with_args(dev->of_node, "iommus",
+					   "#iommu-cells", idx,
+					   &iommu_spec)) {
+		struct device_node *np = iommu_spec.np;
+		struct iommu_data *data = of_iommu_get_data(np);
+
Quoted 8 lines

drivers/iommu/of_iommu.c int of_get_dma_window(struct device_node *dn, const char *prefix, int index, · line 119

Quoted 3 lines
drivers/iommu/of_iommu.c +3 -0
+		if (!iommu) {
+			if (!ops->of_xlate(dev, &iommu_spec))
+				iommu = data;

If I understand the code correctly, this will call of_xlate for the first IOMMU reference only and silently ignore all subsequent references if they point to the same IOMMU device but with different stream/requester IDs. Is that the desired behaviour ?

No; I just fixed that actually. I'll send a v3 soon which has a bunch of fixes like this.

Will

#18 lore raw

RFC PATCH v2 5/7 dma-mapping: detect and configure IOMMU in of_dma_configure

In reply to Will Deacon

This patch extends of_dma_configure so that it sets up the IOMMU for a device, as well as the coherent/non-coherent DMA mapping ops.

---
 arch/arm/include/asm/dma-mapping.h |  5 ++++-
 drivers/of/platform.c              | 36 ++++++++++++++++++++++--------------
 include/linux/dma-mapping.h        |  5 ++++-
 3 files changed, 30 insertions(+), 16 deletions(-)
arch/arm/include/asm/dma-mapping.h +4 -1
index dad006dabbe6..788b8eef40e8 100644
@@ -122,7 +122,10 @@ static inline unsigned long dma_max_pfn(struct device *dev)
 #define dma_max_pfn(dev) dma_max_pfn(dev)
 
 static inline void arch_setup_dma_ops(struct device *dev, u64 mask,
-				      unsigned long offset, bool coherent)
+				      u64 dma_base, u64 size,
+				      unsigned long offset,
+				      struct iommu_dma_mapping *iommu,
+				      bool coherent)
 {
 	dev->coherent_dma_mask	= mask;
 	dev->dma_mask		= &dev->coherent_dma_mask;
drivers/of/platform.c +22 -14
index 484c558c63a6..6067f6423674 100644
@@ -19,6 +19,7 @@
 #include <linux/slab.h>
 #include <linux/of_address.h>
 #include <linux/of_device.h>
+#include <linux/of_iommu.h>
 #include <linux/of_irq.h>
 #include <linux/of_platform.h>
 #include <linux/platform_device.h>
@@ -162,25 +163,37 @@ EXPORT_SYMBOL(of_device_alloc);
  */
 static void of_dma_configure(struct platform_device *pdev)
 {
-	u64 dma_addr, paddr, size;
+	u64 dma_addr, paddr, size, mask;
 	int ret;
 	bool coherent;
 	unsigned long offset;
+	struct iommu_dma_mapping *iommu;
 	struct device *dev = &pdev->dev;
 
+	/*
+	 * Set default dma-mask to 32 bit. Drivers are expected to setup
+	 * the correct supported dma_mask.
+	 */
+	mask = DMA_BIT_MASK(32);
+
 	ret = of_dma_get_range(dev->of_node, &dma_addr, &paddr, &size);
-	offset = ret < 0 ? 0 : PFN_DOWN(paddr - dma_addr);
-	dev_dbg(dev, "dma_pfn_offset(%#08lx)\n", dev->dma_pfn_offset);
+	if (ret < 0) {
+		dma_addr = offset = 0;
+		size = mask;
+	} else {
+		offset = PFN_DOWN(paddr - dma_addr);
+		dev_dbg(dev, "dma_pfn_offset(%#08lx)\n", dev->dma_pfn_offset);
+	}
 
 	coherent = of_dma_is_coherent(dev->of_node);
 	dev_dbg(dev, "device is%sdma coherent\n",
 		coherent ? " " : " not ");
 
-	/*
-	 * Set default dma-mask to 32 bit. Drivers are expected to setup
-	 * the correct supported dma_mask.
-	 */
-	arch_setup_dma_ops(dev, DMA_BIT_MASK(32), offset, coherent);
+	iommu = of_iommu_configure(dev);
+	dev_dbg(dev, "device is%sbehind an iommu\n",
+		iommu ? " " : " not ");
+
+	arch_setup_dma_ops(dev, mask, dma_addr, size, offset, iommu, coherent);
 }
 
 /**
@@ -209,14 +222,9 @@ static struct platform_device *of_platform_device_create_pdata(
 	if (!dev)
 		goto err_clear_flag;
 
-	of_dma_configure(dev);
 	dev->dev.bus = &platform_bus_type;
 	dev->dev.platform_data = platform_data;
-
-	/* We do not fill the DMA ops for platform devices by default.
-	 * This is currently the responsibility of the platform code
-	 * to do such, possibly using a device notifier
-	 */
+	of_dma_configure(dev);
 
 	if (of_device_add(dev) != 0) {
 		platform_device_put(dev);
include/linux/dma-mapping.h +4 -1
index 16bf92f71c3e..19a31f55a5d0 100644
@@ -138,7 +138,10 @@ extern u64 dma_get_required_mask(struct device *dev);
 
 #ifndef arch_setup_dma_ops
 static inline void arch_setup_dma_ops(struct device *dev, u64 mask,
-				      unsigned long offset, bool coherent) { }
+				      u64 dma_base, u64 size,
+				      unsigned long offset,
+				      struct iommu_dma_mapping *iommu,
+				      bool coherent) { }
 #endif
 
 static inline unsigned int dma_get_max_seg_size(struct device *dev)
Signature · 1 line
2.1.0
#19 lore raw

RFC PATCH v2 6/7 arm: call iommu_init before of_platform_populate

In reply to Will Deacon

We need to ensure that the IOMMUs in the system have a chance to perform some basic initialisation before we start adding masters to them.

This patch adds a call to of_iommu_init before of_platform_populate.

---
 arch/arm/kernel/setup.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)
arch/arm/kernel/setup.c +4 -1
index 84db893dedc2..38a0203523af 100644
@@ -18,6 +18,7 @@
 #include <linux/bootmem.h>
 #include <linux/seq_file.h>
 #include <linux/screen_info.h>
+#include <linux/of_iommu.h>
 #include <linux/of_platform.h>
 #include <linux/init.h>
 #include <linux/kexec.h>
@@ -803,9 +804,11 @@ static int __init customize_machine(void)
 	if (machine_desc->init_machine)
 		machine_desc->init_machine();
 #ifdef CONFIG_OF
-	else
+	else {
+		of_iommu_init();
 		of_platform_populate(NULL, of_default_bus_match_table,
 					NULL, NULL);
+	}
 #endif
 	return 0;
 }
Signature · 1 line
2.1.0
maintainer
#20 lore raw

RFC PATCH v2 6/7 arm: call iommu_init before of_platform_populate

arch/arm/kernel/setup.c static int __init customize_machine(void) · line 814

Quoted 13 lines · On Tuesday 02 September 2014 18:56:26 Will Deacon wrote:
arch/arm/kernel/setup.c +3 -1
@@ -803,9 +804,11 @@ static int __init customize_machine(void)
        if (machine_desc->init_machine)
                machine_desc->init_machine();
 #ifdef CONFIG_OF
-       else
+       else {
+               of_iommu_init();
                of_platform_populate(NULL, of_default_bus_match_table,
                                        NULL, NULL);
+       }
 #endif
        return 0;
 }

I think it would be better to not have this depend on the presence of an init_machine callback. Why not move of_iommu_init() in front of the if()? It shouldn't do anything unless we have an iommu driver that we should be using.

Arnd

#21 lore raw

RFC PATCH v2 7/7 arm: dma-mapping: plumb our iommu mapping ops into arch_setup_dma_ops

In reply to Will Deacon

This patch plumbs the existing ARM IOMMU DMA infrastructure (which isn't actually called outside of a few drivers) into arch_setup_dma_ops, so that we can use IOMMUs for DMA transfers in a more generic fashion.

Since this significantly complicates the arch_setup_dma_ops function, it is moved out of line into dma-mapping.c. If CONFIG_ARM_DMA_USE_IOMMU is not set, the iommu paramater is ignored and the normal ops are used instead.

---
 arch/arm/include/asm/dma-mapping.h | 17 +++------
 arch/arm/mm/dma-mapping.c          | 72 +++++++++++++++++++++++++++++++++-----
 2 files changed, 68 insertions(+), 21 deletions(-)
arch/arm/include/asm/dma-mapping.h +4 -13
index 788b8eef40e8..2f648b9b000d 100644
@@ -121,20 +121,11 @@ static inline unsigned long dma_max_pfn(struct device *dev)
 }
 #define dma_max_pfn(dev) dma_max_pfn(dev)
 
-static inline void arch_setup_dma_ops(struct device *dev, u64 mask,
-				      u64 dma_base, u64 size,
-				      unsigned long offset,
-				      struct iommu_dma_mapping *iommu,
-				      bool coherent)
-{
-	dev->coherent_dma_mask	= mask;
-	dev->dma_mask		= &dev->coherent_dma_mask;
-	dev->dma_pfn_offset	= offset;
-
-	if (coherent)
-		set_dma_ops(dev, &arm_coherent_dma_ops);
-}
 #define arch_setup_dma_ops arch_setup_dma_ops
+extern void arch_setup_dma_ops(struct device *dev, u64 mask, u64 dma_base,
+			       u64 size, unsigned long offset,
+			       struct iommu_dma_mapping *iommu,
+			       bool coherent);
 
 static inline dma_addr_t phys_to_dma(struct device *dev, phys_addr_t paddr)
 {
arch/arm/mm/dma-mapping.c +64 -8

98 lines not on this page · raw message

Signature · 1 line
2.1.0
maintainer
#22 lore raw

RFC PATCH v2 7/7 arm: dma-mapping: plumb our iommu mapping ops into arch_setup_dma_ops

Quoted 11 lines · On Tuesday 02 September 2014 18:56:27 Will Deacon wrote:

This patch plumbs the existing ARM IOMMU DMA infrastructure (which isn't actually called outside of a few drivers) into arch_setup_dma_ops, so that we can use IOMMUs for DMA transfers in a more generic fashion.

Since this significantly complicates the arch_setup_dma_ops function, it is moved out of line into dma-mapping.c. If CONFIG_ARM_DMA_USE_IOMMU is not set, the iommu paramater is ignored and the normal ops are used instead.

Looks great to me.

Arnd

maintainer
#23 lore raw

v2 cover Introduce automatic DMA configuration for IOMMU masters

In reply to Will Deacon
Quoted 7 lines · On Tuesday 02 September 2014 18:56:20 Will Deacon wrote:

The main difference since v1 is that I've introduced some generic structures to represent IOMMUs and their mappings (struct iommu_data and struct iommu_dma_mapping). This allows us to propagate the per-instance data all the way down to the DMA-mapping code, which can then manage a per-instance domain. Note that the ARM dma-mapping implementation does not currently make use of this information.

Overall I'm pretty happy with how this is turning out so far. I have commented on a few patches, everything else looks good to me.

Arnd