All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/12] Generic DT bindings for PCI and ARM SMMU
@ 2016-02-29 13:46 ` Robin Murphy
  0 siblings, 0 replies; 38+ messages in thread
From: Robin Murphy @ 2016-02-29 13:46 UTC (permalink / raw)
  To: iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA
  Cc: Mark Rutland, Thomas.Lendacky-5C7GfCeVMHo, David Daney,
	Marc Zyngier, Catalin Marinas,
	thunder.leizhen-hv44wF8Li93QT0dZR+AlfA, will.deacon-5wv7dgnIgG8,
	stuart.yoder-3arQi8VN3Tc, Rob Herring,
	tchalamarla-M3mlKVOIwJVv6pq1l3V1OdBPR1lH4CV8, Frank Rowand

Hi all,

Here's an initial dump of what I've been doing for full generic binding
support on SMMUv2. I'm posting it as one big series because it all fits
together, but the OF and arm64 patches should be free to go through
their respective trees without horribly breaking anything (I think...)

Currently this just brings platform and PCI generic binding support to
the level of the old binding, and adds proper group creation - I have
more ongoing work on top of this to remove the stream ID limits and start
filling in SMR support, which will probably show up added/folded in to
subsequent revisions.

I've put a branch together (based on the SMMU changes in -next) here:

  git://linux-arm.org/linux-rm iommu/generic

Tested on Juno (MMU-401) and LS2085 (MMU-500) for both PCI and platform
devices, plus various combinations of stream ID aliasing between the
two. Since we don't yet have a robust solution for dealing with MSI
doorbells downstream of translation, there are some temporary hacks that
I've been running with on the iommu/msi branch.

Robin.
---

Mark Rutland (1):
  Docs: dt: add PCI IOMMU map bindings

Robin Murphy (11):
  of/irq: Break out msi-map lookup (again)
  iommu/of: Handle iommu-map property for PCI
  arm64/dma-mapping: Extend DMA ops workaround to PCI devices
  arm64/dma-mapping: Remove default domain workaround
  iommu/arm-smmu: Streamline SMMU data lookup
  iommu/arm-smmu: Factor out mmu-masters handling
  iommu/arm-smmu: Refactor master/group config handling
  iommu/arm-smmu: Simplify mmu-masters handling
  Docs: dt: document ARM SMMU generic binding usage
  iommu/arm-smmu: Generic IOMMU DT bindings support
  iommu/arm-smmu: Group platform devices appropriately

 .../devicetree/bindings/iommu/arm,smmu.txt         |  40 +-
 .../devicetree/bindings/pci/pci-iommu.txt          | 171 +++++
 arch/arm64/mm/dma-mapping.c                        |  56 +-
 drivers/iommu/arm-smmu.c                           | 733 ++++++++++++---------
 drivers/iommu/of_iommu.c                           |  43 +-
 drivers/of/irq.c                                   |  70 +-
 drivers/of/of_pci.c                                | 102 +++
 include/linux/of_pci.h                             |   8 +
 8 files changed, 765 insertions(+), 458 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/pci/pci-iommu.txt

-- 
2.7.2.333.g70bd996.dirty

^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH 00/12] Generic DT bindings for PCI and ARM SMMU
@ 2016-02-29 13:46 ` Robin Murphy
  0 siblings, 0 replies; 38+ messages in thread
From: Robin Murphy @ 2016-02-29 13:46 UTC (permalink / raw)
  To: linux-arm-kernel

Hi all,

Here's an initial dump of what I've been doing for full generic binding
support on SMMUv2. I'm posting it as one big series because it all fits
together, but the OF and arm64 patches should be free to go through
their respective trees without horribly breaking anything (I think...)

Currently this just brings platform and PCI generic binding support to
the level of the old binding, and adds proper group creation - I have
more ongoing work on top of this to remove the stream ID limits and start
filling in SMR support, which will probably show up added/folded in to
subsequent revisions.

I've put a branch together (based on the SMMU changes in -next) here:

  git://linux-arm.org/linux-rm iommu/generic

Tested on Juno (MMU-401) and LS2085 (MMU-500) for both PCI and platform
devices, plus various combinations of stream ID aliasing between the
two. Since we don't yet have a robust solution for dealing with MSI
doorbells downstream of translation, there are some temporary hacks that
I've been running with on the iommu/msi branch.

Robin.
---

Mark Rutland (1):
  Docs: dt: add PCI IOMMU map bindings

Robin Murphy (11):
  of/irq: Break out msi-map lookup (again)
  iommu/of: Handle iommu-map property for PCI
  arm64/dma-mapping: Extend DMA ops workaround to PCI devices
  arm64/dma-mapping: Remove default domain workaround
  iommu/arm-smmu: Streamline SMMU data lookup
  iommu/arm-smmu: Factor out mmu-masters handling
  iommu/arm-smmu: Refactor master/group config handling
  iommu/arm-smmu: Simplify mmu-masters handling
  Docs: dt: document ARM SMMU generic binding usage
  iommu/arm-smmu: Generic IOMMU DT bindings support
  iommu/arm-smmu: Group platform devices appropriately

 .../devicetree/bindings/iommu/arm,smmu.txt         |  40 +-
 .../devicetree/bindings/pci/pci-iommu.txt          | 171 +++++
 arch/arm64/mm/dma-mapping.c                        |  56 +-
 drivers/iommu/arm-smmu.c                           | 733 ++++++++++++---------
 drivers/iommu/of_iommu.c                           |  43 +-
 drivers/of/irq.c                                   |  70 +-
 drivers/of/of_pci.c                                | 102 +++
 include/linux/of_pci.h                             |   8 +
 8 files changed, 765 insertions(+), 458 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/pci/pci-iommu.txt

-- 
2.7.2.333.g70bd996.dirty

^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH 01/12] Docs: dt: add PCI IOMMU map bindings
  2016-02-29 13:46 ` Robin Murphy
@ 2016-02-29 13:46     ` Robin Murphy
  -1 siblings, 0 replies; 38+ messages in thread
From: Robin Murphy @ 2016-02-29 13:46 UTC (permalink / raw)
  To: iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA
  Cc: Mark Rutland, Thomas.Lendacky-5C7GfCeVMHo,
	thunder.leizhen-hv44wF8Li93QT0dZR+AlfA, will.deacon-5wv7dgnIgG8,
	stuart.yoder-3arQi8VN3Tc,
	tchalamarla-M3mlKVOIwJVv6pq1l3V1OdBPR1lH4CV8

From: Mark Rutland <mark.rutland-5wv7dgnIgG8@public.gmane.org>

The existing IOMMU bindings are able to specify the relationship between
masters and IOMMUs, but they are insufficient for describing the general
case of hotpluggable busses such as PCI where the set of masters is not
known until runtime, and the relationship between masters and IOMMUs is
a property of the integration of the system.

This patch adds a generic binding for mapping PCI devices to IOMMUs,
using a new iommu-map property (specific to PCI*) which may be used to
map devices (identified by their Requester ID) to sideband data for the
IOMMU which they master through.

Signed-off-by: Mark Rutland <mark.rutland-5wv7dgnIgG8@public.gmane.org>
---
 .../devicetree/bindings/pci/pci-iommu.txt          | 171 +++++++++++++++++++++
 1 file changed, 171 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/pci-iommu.txt

diff --git a/Documentation/devicetree/bindings/pci/pci-iommu.txt b/Documentation/devicetree/bindings/pci/pci-iommu.txt
new file mode 100644
index 0000000..56c8296
--- /dev/null
+++ b/Documentation/devicetree/bindings/pci/pci-iommu.txt
@@ -0,0 +1,171 @@
+This document describes the generic device tree binding for describing the
+relationship between PCI(e) devices and IOMMU(s).
+
+Each PCI(e) device under a root complex is uniquely identified by its Requester
+ID (AKA RID). A Requester ID is a triplet of a Bus number, Device number, and
+Function number.
+
+For the purpose of this document, when treated as a numeric value, a RID is
+formatted such that:
+
+* Bits [15:8] are the Bus number.
+* Bits [7:3] are the Device number.
+* Bits [2:0] are the Function number.
+* Any other bits required for padding must be zero.
+
+IOMMUs may distinguish PCI devices through sideband data derived from the
+Requester ID. While a given PCI device can only master through one IOMMU, a
+root complex may split masters across a set of IOMMUs (e.g. with one IOMMU per
+bus).
+
+The generic 'iommus' property is insufficient to describe this relationship,
+and a mechanism is required to map from a PCI device to its IOMMU and sideband
+data.
+
+For generic IOMMU bindings, see
+Documentation/devicetree/bindings/iommu/iommu.txt.
+
+
+PCI root complex
+================
+
+Optional properties
+-------------------
+
+- iommu-map: Maps a Requester ID to an IOMMU and associated iommu-specifier
+  data.
+
+  The property is an arbitrary number of tuples of
+  (rid-base,iommu,iommu-base,length).
+
+  Any RID r in the interval [rid-base, rid-base + length) is associated with
+  the listed IOMMU, with the iommu-specifier (r - rid-base + iommu-base).
+
+- iommu-map-mask: A mask to be applied to each Requester ID prior to being
+  mapped to an iommu-specifier per the iommu-map property.
+
+
+Example (1)
+===========
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	iommu: iommu@a {
+		reg = <0xa 0x1>;
+		compatible = "vendor,some-iommu";
+		#iommu-cells = <1>;
+	};
+
+	pci: pci@f {
+		reg = <0xf 0x1>;
+		compatible = "vendor,pcie-root-complex";
+		device_type = "pci";
+
+		/*
+		 * The sideband data provided to the IOMMU is the RID,
+		 * identity-mapped.
+		 */
+		iommu-map = <0x0 &iommu 0x0 0x10000>;
+	};
+};
+
+
+Example (2)
+===========
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	iommu: iommu@a {
+		reg = <0xa 0x1>;
+		compatible = "vendor,some-iommu";
+		#iommu-cells = <1>;
+	};
+
+	pci: pci@f {
+		reg = <0xf 0x1>;
+		compatible = "vendor,pcie-root-complex";
+		device_type = "pci";
+
+		/*
+		 * The sideband data provided to the IOMMU is the RID with the
+		 * function bits masked out.
+		 */
+		iommu-map = <0x0 &iommu 0x0 0x10000>;
+		iommu-map-mask = <0xfff8>;
+	};
+};
+
+
+Example (3)
+===========
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	iommu: iommu@a {
+		reg = <0xa 0x1>;
+		compatible = "vendor,some-iommu";
+		#iommu-cells = <1>;
+	};
+
+	pci: pci@f {
+		reg = <0xf 0x1>;
+		compatible = "vendor,pcie-root-complex";
+		device_type = "pci";
+
+		/*
+		 * The sideband data provided to the IOMMU is the RID,
+		 * but the high bits of the bus number are flipped.
+		 */
+		iommu-map = <0x0000 &iommu 0x8000 0x8000>,
+			    <0x8000 &iommu 0x0000 0x8000>;
+	};
+};
+
+
+Example (4)
+===========
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	iommu_a: iommu@a {
+		reg = <0xa 0x1>;
+		compatible = "vendor,some-iommu";
+		#iommu-cells = <1>;
+	};
+
+	iommu_b: iommu@b {
+		reg = <0xb 0x1>;
+		compatible = "vendor,some-iommu";
+		#iommu-cells = <1>;
+	};
+
+	iommu_c: iommu@c {
+		reg = <0xc 0x1>;
+		compatible = "vendor,some-iommu";
+		#iommu-cells = <1>;
+	};
+
+	pci: pci@f {
+		reg = <0xf 0x1>;
+		compatible = "vendor,pcie-root-complex";
+		device_type = "pci";
+
+		/*
+		 * Devices with bus number 0-127 are mastered via IOMMU
+		 * a, with sideband data being RID[14:0].
+		 * Devices with bus number 128-255 are mastered via
+		 * IOMMU b, with sideband data being RID[14:0].
+		 * No devices master via IOMMU c.
+		 */
+		iommu-map = <0x0000 &iommu_a 0x0000 0x8000>,
+			    <0x8000 &iommu_b 0x0000 0x8000>;
+	};
+};
-- 
2.7.2.333.g70bd996.dirty

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 01/12] Docs: dt: add PCI IOMMU map bindings
@ 2016-02-29 13:46     ` Robin Murphy
  0 siblings, 0 replies; 38+ messages in thread
From: Robin Murphy @ 2016-02-29 13:46 UTC (permalink / raw)
  To: linux-arm-kernel

From: Mark Rutland <mark.rutland@arm.com>

The existing IOMMU bindings are able to specify the relationship between
masters and IOMMUs, but they are insufficient for describing the general
case of hotpluggable busses such as PCI where the set of masters is not
known until runtime, and the relationship between masters and IOMMUs is
a property of the integration of the system.

This patch adds a generic binding for mapping PCI devices to IOMMUs,
using a new iommu-map property (specific to PCI*) which may be used to
map devices (identified by their Requester ID) to sideband data for the
IOMMU which they master through.

Signed-off-by: Mark Rutland <mark.rutland@arm.com>
---
 .../devicetree/bindings/pci/pci-iommu.txt          | 171 +++++++++++++++++++++
 1 file changed, 171 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/pci-iommu.txt

diff --git a/Documentation/devicetree/bindings/pci/pci-iommu.txt b/Documentation/devicetree/bindings/pci/pci-iommu.txt
new file mode 100644
index 0000000..56c8296
--- /dev/null
+++ b/Documentation/devicetree/bindings/pci/pci-iommu.txt
@@ -0,0 +1,171 @@
+This document describes the generic device tree binding for describing the
+relationship between PCI(e) devices and IOMMU(s).
+
+Each PCI(e) device under a root complex is uniquely identified by its Requester
+ID (AKA RID). A Requester ID is a triplet of a Bus number, Device number, and
+Function number.
+
+For the purpose of this document, when treated as a numeric value, a RID is
+formatted such that:
+
+* Bits [15:8] are the Bus number.
+* Bits [7:3] are the Device number.
+* Bits [2:0] are the Function number.
+* Any other bits required for padding must be zero.
+
+IOMMUs may distinguish PCI devices through sideband data derived from the
+Requester ID. While a given PCI device can only master through one IOMMU, a
+root complex may split masters across a set of IOMMUs (e.g. with one IOMMU per
+bus).
+
+The generic 'iommus' property is insufficient to describe this relationship,
+and a mechanism is required to map from a PCI device to its IOMMU and sideband
+data.
+
+For generic IOMMU bindings, see
+Documentation/devicetree/bindings/iommu/iommu.txt.
+
+
+PCI root complex
+================
+
+Optional properties
+-------------------
+
+- iommu-map: Maps a Requester ID to an IOMMU and associated iommu-specifier
+  data.
+
+  The property is an arbitrary number of tuples of
+  (rid-base,iommu,iommu-base,length).
+
+  Any RID r in the interval [rid-base, rid-base + length) is associated with
+  the listed IOMMU, with the iommu-specifier (r - rid-base + iommu-base).
+
+- iommu-map-mask: A mask to be applied to each Requester ID prior to being
+  mapped to an iommu-specifier per the iommu-map property.
+
+
+Example (1)
+===========
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	iommu: iommu at a {
+		reg = <0xa 0x1>;
+		compatible = "vendor,some-iommu";
+		#iommu-cells = <1>;
+	};
+
+	pci: pci at f {
+		reg = <0xf 0x1>;
+		compatible = "vendor,pcie-root-complex";
+		device_type = "pci";
+
+		/*
+		 * The sideband data provided to the IOMMU is the RID,
+		 * identity-mapped.
+		 */
+		iommu-map = <0x0 &iommu 0x0 0x10000>;
+	};
+};
+
+
+Example (2)
+===========
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	iommu: iommu at a {
+		reg = <0xa 0x1>;
+		compatible = "vendor,some-iommu";
+		#iommu-cells = <1>;
+	};
+
+	pci: pci at f {
+		reg = <0xf 0x1>;
+		compatible = "vendor,pcie-root-complex";
+		device_type = "pci";
+
+		/*
+		 * The sideband data provided to the IOMMU is the RID with the
+		 * function bits masked out.
+		 */
+		iommu-map = <0x0 &iommu 0x0 0x10000>;
+		iommu-map-mask = <0xfff8>;
+	};
+};
+
+
+Example (3)
+===========
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	iommu: iommu at a {
+		reg = <0xa 0x1>;
+		compatible = "vendor,some-iommu";
+		#iommu-cells = <1>;
+	};
+
+	pci: pci at f {
+		reg = <0xf 0x1>;
+		compatible = "vendor,pcie-root-complex";
+		device_type = "pci";
+
+		/*
+		 * The sideband data provided to the IOMMU is the RID,
+		 * but the high bits of the bus number are flipped.
+		 */
+		iommu-map = <0x0000 &iommu 0x8000 0x8000>,
+			    <0x8000 &iommu 0x0000 0x8000>;
+	};
+};
+
+
+Example (4)
+===========
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	iommu_a: iommu at a {
+		reg = <0xa 0x1>;
+		compatible = "vendor,some-iommu";
+		#iommu-cells = <1>;
+	};
+
+	iommu_b: iommu at b {
+		reg = <0xb 0x1>;
+		compatible = "vendor,some-iommu";
+		#iommu-cells = <1>;
+	};
+
+	iommu_c: iommu at c {
+		reg = <0xc 0x1>;
+		compatible = "vendor,some-iommu";
+		#iommu-cells = <1>;
+	};
+
+	pci: pci at f {
+		reg = <0xf 0x1>;
+		compatible = "vendor,pcie-root-complex";
+		device_type = "pci";
+
+		/*
+		 * Devices with bus number 0-127 are mastered via IOMMU
+		 * a, with sideband data being RID[14:0].
+		 * Devices with bus number 128-255 are mastered via
+		 * IOMMU b, with sideband data being RID[14:0].
+		 * No devices master via IOMMU c.
+		 */
+		iommu-map = <0x0000 &iommu_a 0x0000 0x8000>,
+			    <0x8000 &iommu_b 0x0000 0x8000>;
+	};
+};
-- 
2.7.2.333.g70bd996.dirty

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 02/12] of/irq: Break out msi-map lookup (again)
  2016-02-29 13:46 ` Robin Murphy
@ 2016-02-29 13:46     ` Robin Murphy
  -1 siblings, 0 replies; 38+ messages in thread
From: Robin Murphy @ 2016-02-29 13:46 UTC (permalink / raw)
  To: iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA
  Cc: Thomas.Lendacky-5C7GfCeVMHo, David Daney, Marc Zyngier,
	thunder.leizhen-hv44wF8Li93QT0dZR+AlfA, will.deacon-5wv7dgnIgG8,
	stuart.yoder-3arQi8VN3Tc, Rob Herring,
	tchalamarla-M3mlKVOIwJVv6pq1l3V1OdBPR1lH4CV8, Frank Rowand

The PCI msi-map code is already doing double-duty translating IDs and
retrieving MSI parents, which unsurprisingly is the same functionality
we need for the identically-formatted PCI iommu-map property. Drag the
core parsing routine up yet another layer into the general OF-PCI code,
and further generalise it for either kind of lookup in either flavour
of map property.

CC: Rob Herring <robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
CC: Frank Rowand <frowand.list-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
CC: Marc Zyngier <marc.zyngier-5wv7dgnIgG8@public.gmane.org>
CC: David Daney <david.daney-YGCgFSpz5w/QT0dZR+AlfA@public.gmane.org>
Signed-off-by: Robin Murphy <robin.murphy-5wv7dgnIgG8@public.gmane.org>
---
 drivers/of/irq.c       |  70 ++-------------------------------
 drivers/of/of_pci.c    | 102 +++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/of_pci.h |   8 ++++
 3 files changed, 114 insertions(+), 66 deletions(-)

diff --git a/drivers/of/irq.c b/drivers/of/irq.c
index e7bfc17..0c9118d 100644
--- a/drivers/of/irq.c
+++ b/drivers/of/irq.c
@@ -24,6 +24,7 @@
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/of_irq.h>
+#include <linux/of_pci.h>
 #include <linux/string.h>
 #include <linux/slab.h>
 
@@ -586,13 +587,7 @@ static u32 __of_msi_map_rid(struct device *dev, struct device_node **np,
 			    u32 rid_in)
 {
 	struct device *parent_dev;
-	struct device_node *msi_controller_node;
-	struct device_node *msi_np = *np;
-	u32 map_mask, masked_rid, rid_base, msi_base, rid_len, phandle;
-	int msi_map_len;
-	bool matched;
 	u32 rid_out = rid_in;
-	const __be32 *msi_map = NULL;
 
 	/*
 	 * Walk up the device parent links looking for one with a
@@ -602,71 +597,14 @@ static u32 __of_msi_map_rid(struct device *dev, struct device_node **np,
 		if (!parent_dev->of_node)
 			continue;
 
-		msi_map = of_get_property(parent_dev->of_node,
-					  "msi-map", &msi_map_len);
-		if (!msi_map)
+		if (!of_property_read_bool(parent_dev->of_node, "msi-map"))
 			continue;
 
-		if (msi_map_len % (4 * sizeof(__be32))) {
-			dev_err(parent_dev, "Error: Bad msi-map length: %d\n",
-				msi_map_len);
-			return rid_out;
-		}
 		/* We have a good parent_dev and msi_map, let's use them. */
+		of_pci_map_rid(parent_dev->of_node, "msi-map", rid_in, np,
+			       &rid_out);
 		break;
 	}
-	if (!msi_map)
-		return rid_out;
-
-	/* The default is to select all bits. */
-	map_mask = 0xffffffff;
-
-	/*
-	 * Can be overridden by "msi-map-mask" property.  If
-	 * of_property_read_u32() fails, the default is used.
-	 */
-	of_property_read_u32(parent_dev->of_node, "msi-map-mask", &map_mask);
-
-	masked_rid = map_mask & rid_in;
-	matched = false;
-	while (!matched && msi_map_len >= 4 * sizeof(__be32)) {
-		rid_base = be32_to_cpup(msi_map + 0);
-		phandle = be32_to_cpup(msi_map + 1);
-		msi_base = be32_to_cpup(msi_map + 2);
-		rid_len = be32_to_cpup(msi_map + 3);
-
-		if (rid_base & ~map_mask) {
-			dev_err(parent_dev,
-				"Invalid msi-map translation - msi-map-mask (0x%x) ignores rid-base (0x%x)\n",
-				map_mask, rid_base);
-			return rid_out;
-		}
-
-		msi_controller_node = of_find_node_by_phandle(phandle);
-
-		matched = (masked_rid >= rid_base &&
-			   masked_rid < rid_base + rid_len);
-		if (msi_np)
-			matched &= msi_np == msi_controller_node;
-
-		if (matched && !msi_np) {
-			*np = msi_np = msi_controller_node;
-			break;
-		}
-
-		of_node_put(msi_controller_node);
-		msi_map_len -= 4 * sizeof(__be32);
-		msi_map += 4;
-	}
-	if (!matched)
-		return rid_out;
-
-	rid_out = masked_rid - rid_base + msi_base;
-	dev_dbg(dev,
-		"msi-map at: %s, using mask %08x, rid-base: %08x, msi-base: %08x, length: %08x, rid: %08x -> %08x\n",
-		dev_name(parent_dev), map_mask, rid_base, msi_base,
-		rid_len, rid_in, rid_out);
-
 	return rid_out;
 }
 
diff --git a/drivers/of/of_pci.c b/drivers/of/of_pci.c
index b1449f7..15c4a64b 100644
--- a/drivers/of/of_pci.c
+++ b/drivers/of/of_pci.c
@@ -307,3 +307,105 @@ struct msi_controller *of_pci_find_msi_chip_by_node(struct device_node *of_node)
 EXPORT_SYMBOL_GPL(of_pci_find_msi_chip_by_node);
 
 #endif /* CONFIG_PCI_MSI */
+
+#define MASK_NAME_LEN	32	/* Safely longer than "iommu-map-mask" */
+
+/**
+ * of_pci_map_rid - Translate a requester ID through a downstream mapping.
+ * @np: root complex device node.
+ * @map_name: property name of the map to use.
+ * @target: optional pointer to a target device node.
+ * @id_out: optional pointer to receive the translated ID.
+ *
+ * Given a PCI requester ID, look up the appropriate implementation-defined
+ * platform ID and/or the target device which receives transactions on that
+ * ID, as per the "iommu-map" and "msi-map" bindings. @target or @id_out may
+ * be NULL if not required. If @target points to a device node pointer, only
+ * entries targeting that node will be matched; if it points to a NULL
+ * value, it will receive the device node for the first matching target entry,
+ * with a reference held.
+ *
+ * Return: 0 on success or a standard error code on failure.
+ */
+int of_pci_map_rid(struct device_node *np, const char *map_name, u32 rid_in,
+		   struct device_node **target, u32 *rid_out)
+{
+	u32 map_mask, masked_rid;
+	int map_len;
+	const __be32 *map = NULL;
+	char mask_name[MASK_NAME_LEN];
+
+	if (!np || !map_name || (!target && !rid_out))
+		return -EINVAL;
+
+	map = of_get_property(np, map_name, &map_len);
+	if (!map) {
+		if (target)
+			return -ENODEV;
+		/* Otherwise, no map implies no translation */
+		*rid_out = rid_in;
+		return 0;
+	}
+
+	if (!map_len || map_len % (4 * sizeof(*map))) {
+		pr_err("%s: Error: Bad %s length: %d\n", np->full_name,
+			map_name, map_len);
+		return -EINVAL;
+	}
+
+	/* The default is to select all bits. */
+	map_mask = 0xffffffff;
+
+	/*
+	 * Can be overridden by "{iommu,msi}-map-mask" property.
+	 * If of_property_read_u32() fails, the default is used.
+	 */
+	snprintf(mask_name, MASK_NAME_LEN, "%s-mask", map_name);
+	of_property_read_u32(np, mask_name, &map_mask);
+
+	masked_rid = map_mask & rid_in;
+	for ( ; map_len > 0; map_len -= 4 * sizeof(*map), map += 4) {
+		struct device_node *phandle_node;
+		u32 rid_base = be32_to_cpup(map + 0);
+		u32 phandle = be32_to_cpup(map + 1);
+		u32 out_base = be32_to_cpup(map + 2);
+		u32 rid_len = be32_to_cpup(map + 3);
+
+		if (rid_base & ~map_mask) {
+			pr_err("%s: Invalid %s translation - %s-mask (0x%x) ignores rid-base (0x%x)\n",
+				np->full_name, map_name, map_name,
+				map_mask, rid_base);
+			return -EINVAL;
+		}
+
+		if (masked_rid < rid_base || masked_rid >= rid_base + rid_len)
+			continue;
+
+		phandle_node = of_find_node_by_phandle(phandle);
+		if (!phandle_node)
+			return -ENODEV;
+
+		if (target) {
+			if (*target)
+				of_node_put(phandle_node);
+			else
+				*target = phandle_node;
+
+			if (*target != phandle_node)
+				continue;
+		}
+
+		if (rid_out)
+			*rid_out = masked_rid - rid_base + out_base;
+
+		pr_debug("%s: %s, using mask %08x, rid-base: %08x, out-base: %08x, length: %08x, rid: %08x -> %08x\n",
+			np->full_name, map_name, map_mask, rid_base, out_base,
+			rid_len, rid_in, *rid_out);
+		return 0;
+	}
+
+	pr_err("%s: Invalid %s translation - no match for rid 0x%x on %s\n",
+		np->full_name, map_name, rid_in,
+		target && *target ? (*target)->full_name : "any target");
+	return -EINVAL;
+}
diff --git a/include/linux/of_pci.h b/include/linux/of_pci.h
index f6e9e85..070f883 100644
--- a/include/linux/of_pci.h
+++ b/include/linux/of_pci.h
@@ -17,6 +17,8 @@ int of_irq_parse_and_map_pci(const struct pci_dev *dev, u8 slot, u8 pin);
 int of_pci_parse_bus_range(struct device_node *node, struct resource *res);
 int of_get_pci_domain_nr(struct device_node *node);
 void of_pci_check_probe_only(void);
+int of_pci_map_rid(struct device_node *np, const char *map_name, u32 rid_in,
+		   struct device_node **target, u32 *rid_out);
 #else
 static inline int of_irq_parse_pci(const struct pci_dev *pdev, struct of_phandle_args *out_irq)
 {
@@ -52,6 +54,12 @@ of_get_pci_domain_nr(struct device_node *node)
 	return -1;
 }
 
+static inline int of_pci_map_rid(struct device_node *np, const char *map_name,
+			u32 rid_in, struct device_node **target, u32 *rid_out)
+{
+	return -EINVAL;
+}
+
 static inline void of_pci_check_probe_only(void) { }
 #endif
 
-- 
2.7.2.333.g70bd996.dirty

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 02/12] of/irq: Break out msi-map lookup (again)
@ 2016-02-29 13:46     ` Robin Murphy
  0 siblings, 0 replies; 38+ messages in thread
From: Robin Murphy @ 2016-02-29 13:46 UTC (permalink / raw)
  To: linux-arm-kernel

The PCI msi-map code is already doing double-duty translating IDs and
retrieving MSI parents, which unsurprisingly is the same functionality
we need for the identically-formatted PCI iommu-map property. Drag the
core parsing routine up yet another layer into the general OF-PCI code,
and further generalise it for either kind of lookup in either flavour
of map property.

CC: Rob Herring <robh+dt@kernel.org>
CC: Frank Rowand <frowand.list@gmail.com>
CC: Marc Zyngier <marc.zyngier@arm.com>
CC: David Daney <david.daney@cavium.com>
Signed-off-by: Robin Murphy <robin.murphy@arm.com>
---
 drivers/of/irq.c       |  70 ++-------------------------------
 drivers/of/of_pci.c    | 102 +++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/of_pci.h |   8 ++++
 3 files changed, 114 insertions(+), 66 deletions(-)

diff --git a/drivers/of/irq.c b/drivers/of/irq.c
index e7bfc17..0c9118d 100644
--- a/drivers/of/irq.c
+++ b/drivers/of/irq.c
@@ -24,6 +24,7 @@
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/of_irq.h>
+#include <linux/of_pci.h>
 #include <linux/string.h>
 #include <linux/slab.h>
 
@@ -586,13 +587,7 @@ static u32 __of_msi_map_rid(struct device *dev, struct device_node **np,
 			    u32 rid_in)
 {
 	struct device *parent_dev;
-	struct device_node *msi_controller_node;
-	struct device_node *msi_np = *np;
-	u32 map_mask, masked_rid, rid_base, msi_base, rid_len, phandle;
-	int msi_map_len;
-	bool matched;
 	u32 rid_out = rid_in;
-	const __be32 *msi_map = NULL;
 
 	/*
 	 * Walk up the device parent links looking for one with a
@@ -602,71 +597,14 @@ static u32 __of_msi_map_rid(struct device *dev, struct device_node **np,
 		if (!parent_dev->of_node)
 			continue;
 
-		msi_map = of_get_property(parent_dev->of_node,
-					  "msi-map", &msi_map_len);
-		if (!msi_map)
+		if (!of_property_read_bool(parent_dev->of_node, "msi-map"))
 			continue;
 
-		if (msi_map_len % (4 * sizeof(__be32))) {
-			dev_err(parent_dev, "Error: Bad msi-map length: %d\n",
-				msi_map_len);
-			return rid_out;
-		}
 		/* We have a good parent_dev and msi_map, let's use them. */
+		of_pci_map_rid(parent_dev->of_node, "msi-map", rid_in, np,
+			       &rid_out);
 		break;
 	}
-	if (!msi_map)
-		return rid_out;
-
-	/* The default is to select all bits. */
-	map_mask = 0xffffffff;
-
-	/*
-	 * Can be overridden by "msi-map-mask" property.  If
-	 * of_property_read_u32() fails, the default is used.
-	 */
-	of_property_read_u32(parent_dev->of_node, "msi-map-mask", &map_mask);
-
-	masked_rid = map_mask & rid_in;
-	matched = false;
-	while (!matched && msi_map_len >= 4 * sizeof(__be32)) {
-		rid_base = be32_to_cpup(msi_map + 0);
-		phandle = be32_to_cpup(msi_map + 1);
-		msi_base = be32_to_cpup(msi_map + 2);
-		rid_len = be32_to_cpup(msi_map + 3);
-
-		if (rid_base & ~map_mask) {
-			dev_err(parent_dev,
-				"Invalid msi-map translation - msi-map-mask (0x%x) ignores rid-base (0x%x)\n",
-				map_mask, rid_base);
-			return rid_out;
-		}
-
-		msi_controller_node = of_find_node_by_phandle(phandle);
-
-		matched = (masked_rid >= rid_base &&
-			   masked_rid < rid_base + rid_len);
-		if (msi_np)
-			matched &= msi_np == msi_controller_node;
-
-		if (matched && !msi_np) {
-			*np = msi_np = msi_controller_node;
-			break;
-		}
-
-		of_node_put(msi_controller_node);
-		msi_map_len -= 4 * sizeof(__be32);
-		msi_map += 4;
-	}
-	if (!matched)
-		return rid_out;
-
-	rid_out = masked_rid - rid_base + msi_base;
-	dev_dbg(dev,
-		"msi-map at: %s, using mask %08x, rid-base: %08x, msi-base: %08x, length: %08x, rid: %08x -> %08x\n",
-		dev_name(parent_dev), map_mask, rid_base, msi_base,
-		rid_len, rid_in, rid_out);
-
 	return rid_out;
 }
 
diff --git a/drivers/of/of_pci.c b/drivers/of/of_pci.c
index b1449f7..15c4a64b 100644
--- a/drivers/of/of_pci.c
+++ b/drivers/of/of_pci.c
@@ -307,3 +307,105 @@ struct msi_controller *of_pci_find_msi_chip_by_node(struct device_node *of_node)
 EXPORT_SYMBOL_GPL(of_pci_find_msi_chip_by_node);
 
 #endif /* CONFIG_PCI_MSI */
+
+#define MASK_NAME_LEN	32	/* Safely longer than "iommu-map-mask" */
+
+/**
+ * of_pci_map_rid - Translate a requester ID through a downstream mapping.
+ * @np: root complex device node.
+ * @map_name: property name of the map to use.
+ * @target: optional pointer to a target device node.
+ * @id_out: optional pointer to receive the translated ID.
+ *
+ * Given a PCI requester ID, look up the appropriate implementation-defined
+ * platform ID and/or the target device which receives transactions on that
+ * ID, as per the "iommu-map" and "msi-map" bindings. @target or @id_out may
+ * be NULL if not required. If @target points to a device node pointer, only
+ * entries targeting that node will be matched; if it points to a NULL
+ * value, it will receive the device node for the first matching target entry,
+ * with a reference held.
+ *
+ * Return: 0 on success or a standard error code on failure.
+ */
+int of_pci_map_rid(struct device_node *np, const char *map_name, u32 rid_in,
+		   struct device_node **target, u32 *rid_out)
+{
+	u32 map_mask, masked_rid;
+	int map_len;
+	const __be32 *map = NULL;
+	char mask_name[MASK_NAME_LEN];
+
+	if (!np || !map_name || (!target && !rid_out))
+		return -EINVAL;
+
+	map = of_get_property(np, map_name, &map_len);
+	if (!map) {
+		if (target)
+			return -ENODEV;
+		/* Otherwise, no map implies no translation */
+		*rid_out = rid_in;
+		return 0;
+	}
+
+	if (!map_len || map_len % (4 * sizeof(*map))) {
+		pr_err("%s: Error: Bad %s length: %d\n", np->full_name,
+			map_name, map_len);
+		return -EINVAL;
+	}
+
+	/* The default is to select all bits. */
+	map_mask = 0xffffffff;
+
+	/*
+	 * Can be overridden by "{iommu,msi}-map-mask" property.
+	 * If of_property_read_u32() fails, the default is used.
+	 */
+	snprintf(mask_name, MASK_NAME_LEN, "%s-mask", map_name);
+	of_property_read_u32(np, mask_name, &map_mask);
+
+	masked_rid = map_mask & rid_in;
+	for ( ; map_len > 0; map_len -= 4 * sizeof(*map), map += 4) {
+		struct device_node *phandle_node;
+		u32 rid_base = be32_to_cpup(map + 0);
+		u32 phandle = be32_to_cpup(map + 1);
+		u32 out_base = be32_to_cpup(map + 2);
+		u32 rid_len = be32_to_cpup(map + 3);
+
+		if (rid_base & ~map_mask) {
+			pr_err("%s: Invalid %s translation - %s-mask (0x%x) ignores rid-base (0x%x)\n",
+				np->full_name, map_name, map_name,
+				map_mask, rid_base);
+			return -EINVAL;
+		}
+
+		if (masked_rid < rid_base || masked_rid >= rid_base + rid_len)
+			continue;
+
+		phandle_node = of_find_node_by_phandle(phandle);
+		if (!phandle_node)
+			return -ENODEV;
+
+		if (target) {
+			if (*target)
+				of_node_put(phandle_node);
+			else
+				*target = phandle_node;
+
+			if (*target != phandle_node)
+				continue;
+		}
+
+		if (rid_out)
+			*rid_out = masked_rid - rid_base + out_base;
+
+		pr_debug("%s: %s, using mask %08x, rid-base: %08x, out-base: %08x, length: %08x, rid: %08x -> %08x\n",
+			np->full_name, map_name, map_mask, rid_base, out_base,
+			rid_len, rid_in, *rid_out);
+		return 0;
+	}
+
+	pr_err("%s: Invalid %s translation - no match for rid 0x%x on %s\n",
+		np->full_name, map_name, rid_in,
+		target && *target ? (*target)->full_name : "any target");
+	return -EINVAL;
+}
diff --git a/include/linux/of_pci.h b/include/linux/of_pci.h
index f6e9e85..070f883 100644
--- a/include/linux/of_pci.h
+++ b/include/linux/of_pci.h
@@ -17,6 +17,8 @@ int of_irq_parse_and_map_pci(const struct pci_dev *dev, u8 slot, u8 pin);
 int of_pci_parse_bus_range(struct device_node *node, struct resource *res);
 int of_get_pci_domain_nr(struct device_node *node);
 void of_pci_check_probe_only(void);
+int of_pci_map_rid(struct device_node *np, const char *map_name, u32 rid_in,
+		   struct device_node **target, u32 *rid_out);
 #else
 static inline int of_irq_parse_pci(const struct pci_dev *pdev, struct of_phandle_args *out_irq)
 {
@@ -52,6 +54,12 @@ of_get_pci_domain_nr(struct device_node *node)
 	return -1;
 }
 
+static inline int of_pci_map_rid(struct device_node *np, const char *map_name,
+			u32 rid_in, struct device_node **target, u32 *rid_out)
+{
+	return -EINVAL;
+}
+
 static inline void of_pci_check_probe_only(void) { }
 #endif
 
-- 
2.7.2.333.g70bd996.dirty

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 03/12] iommu/of: Handle iommu-map property for PCI
  2016-02-29 13:46 ` Robin Murphy
@ 2016-02-29 13:46     ` Robin Murphy
  -1 siblings, 0 replies; 38+ messages in thread
From: Robin Murphy @ 2016-02-29 13:46 UTC (permalink / raw)
  To: iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA
  Cc: Thomas.Lendacky-5C7GfCeVMHo,
	thunder.leizhen-hv44wF8Li93QT0dZR+AlfA, will.deacon-5wv7dgnIgG8,
	stuart.yoder-3arQi8VN3Tc, Rob Herring,
	tchalamarla-M3mlKVOIwJVv6pq1l3V1OdBPR1lH4CV8, Frank Rowand

Now that we have a way to pick up the RID translation and target IOMMU,
hook up of_iommu_configure() to bring PCI devices into the of_xlate
mechanism and allow them IOMMU-backed DMA ops without the need for
driver-specific handling.

CC: Rob Herring <robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
CC: Frank Rowand <frowand.list-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Signed-off-by: Robin Murphy <robin.murphy-5wv7dgnIgG8@public.gmane.org>
---
 drivers/iommu/of_iommu.c | 43 ++++++++++++++++++++++++++++++++++++-------
 1 file changed, 36 insertions(+), 7 deletions(-)

diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c
index 5fea665..910826c 100644
--- a/drivers/iommu/of_iommu.c
+++ b/drivers/iommu/of_iommu.c
@@ -22,6 +22,7 @@
 #include <linux/limits.h>
 #include <linux/of.h>
 #include <linux/of_iommu.h>
+#include <linux/of_pci.h>
 #include <linux/slab.h>
 
 static const struct of_device_id __iommu_of_table_sentinel
@@ -134,20 +135,48 @@ struct iommu_ops *of_iommu_get_ops(struct device_node *np)
 	return ops;
 }
 
+static int __get_pci_rid(struct pci_dev *pdev, u16 alias, void *data)
+{
+	struct of_phandle_args *iommu_spec = data;
+	struct device_node *np = pdev->bus->dev.of_node;
+
+	iommu_spec->args[0] = alias;
+	return np == iommu_spec->np;
+}
+
 struct iommu_ops *of_iommu_configure(struct device *dev,
 				     struct device_node *master_np)
 {
 	struct of_phandle_args iommu_spec;
-	struct device_node *np;
+	struct device_node *np = NULL;
 	struct iommu_ops *ops = NULL;
 	int idx = 0;
 
-	/*
-	 * We can't do much for PCI devices without knowing how
-	 * device IDs are wired up from the PCI bus to the IOMMU.
-	 */
-	if (dev_is_pci(dev))
-		return NULL;
+	if (dev_is_pci(dev)) {
+		/*
+		 * Start by tracing the RID alias down the PCI topology as
+		 * far as the host bridge whose OF node we have...
+		 */
+		iommu_spec.np = master_np;
+		pci_for_each_dma_alias(to_pci_dev(dev), __get_pci_rid,
+				       &iommu_spec);
+		/*
+		 * ...then find out what that becomes once it escapes the PCI
+		 * bus into the system beyond, and which IOMMU it ends up at.
+		 */
+		if (of_pci_map_rid(master_np, "iommu-map", iommu_spec.args[0],
+				   &np, iommu_spec.args))
+			return NULL;
+
+		iommu_spec.np = np;
+		iommu_spec.args_count = 1;
+		ops = of_iommu_get_ops(np);
+		if (!ops || !ops->of_xlate || ops->of_xlate(dev, &iommu_spec))
+			goto err_put_node;
+
+		of_node_put(np);
+		return ops;
+	}
 
 	/*
 	 * We don't currently walk up the tree looking for a parent IOMMU.
-- 
2.7.2.333.g70bd996.dirty

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 03/12] iommu/of: Handle iommu-map property for PCI
@ 2016-02-29 13:46     ` Robin Murphy
  0 siblings, 0 replies; 38+ messages in thread
From: Robin Murphy @ 2016-02-29 13:46 UTC (permalink / raw)
  To: linux-arm-kernel

Now that we have a way to pick up the RID translation and target IOMMU,
hook up of_iommu_configure() to bring PCI devices into the of_xlate
mechanism and allow them IOMMU-backed DMA ops without the need for
driver-specific handling.

CC: Rob Herring <robh+dt@kernel.org>
CC: Frank Rowand <frowand.list@gmail.com>
Signed-off-by: Robin Murphy <robin.murphy@arm.com>
---
 drivers/iommu/of_iommu.c | 43 ++++++++++++++++++++++++++++++++++++-------
 1 file changed, 36 insertions(+), 7 deletions(-)

diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c
index 5fea665..910826c 100644
--- a/drivers/iommu/of_iommu.c
+++ b/drivers/iommu/of_iommu.c
@@ -22,6 +22,7 @@
 #include <linux/limits.h>
 #include <linux/of.h>
 #include <linux/of_iommu.h>
+#include <linux/of_pci.h>
 #include <linux/slab.h>
 
 static const struct of_device_id __iommu_of_table_sentinel
@@ -134,20 +135,48 @@ struct iommu_ops *of_iommu_get_ops(struct device_node *np)
 	return ops;
 }
 
+static int __get_pci_rid(struct pci_dev *pdev, u16 alias, void *data)
+{
+	struct of_phandle_args *iommu_spec = data;
+	struct device_node *np = pdev->bus->dev.of_node;
+
+	iommu_spec->args[0] = alias;
+	return np == iommu_spec->np;
+}
+
 struct iommu_ops *of_iommu_configure(struct device *dev,
 				     struct device_node *master_np)
 {
 	struct of_phandle_args iommu_spec;
-	struct device_node *np;
+	struct device_node *np = NULL;
 	struct iommu_ops *ops = NULL;
 	int idx = 0;
 
-	/*
-	 * We can't do much for PCI devices without knowing how
-	 * device IDs are wired up from the PCI bus to the IOMMU.
-	 */
-	if (dev_is_pci(dev))
-		return NULL;
+	if (dev_is_pci(dev)) {
+		/*
+		 * Start by tracing the RID alias down the PCI topology as
+		 * far as the host bridge whose OF node we have...
+		 */
+		iommu_spec.np = master_np;
+		pci_for_each_dma_alias(to_pci_dev(dev), __get_pci_rid,
+				       &iommu_spec);
+		/*
+		 * ...then find out what that becomes once it escapes the PCI
+		 * bus into the system beyond, and which IOMMU it ends up at.
+		 */
+		if (of_pci_map_rid(master_np, "iommu-map", iommu_spec.args[0],
+				   &np, iommu_spec.args))
+			return NULL;
+
+		iommu_spec.np = np;
+		iommu_spec.args_count = 1;
+		ops = of_iommu_get_ops(np);
+		if (!ops || !ops->of_xlate || ops->of_xlate(dev, &iommu_spec))
+			goto err_put_node;
+
+		of_node_put(np);
+		return ops;
+	}
 
 	/*
 	 * We don't currently walk up the tree looking for a parent IOMMU.
-- 
2.7.2.333.g70bd996.dirty

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 04/12] arm64/dma-mapping: Extend DMA ops workaround to PCI devices
  2016-02-29 13:46 ` Robin Murphy
@ 2016-02-29 13:46     ` Robin Murphy
  -1 siblings, 0 replies; 38+ messages in thread
From: Robin Murphy @ 2016-02-29 13:46 UTC (permalink / raw)
  To: iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA
  Cc: will.deacon-5wv7dgnIgG8, Suravee.Suthikulpanit-5C7GfCeVMHo,
	Thomas.Lendacky-5C7GfCeVMHo, stuart.yoder-3arQi8VN3Tc,
	tchalamarla-M3mlKVOIwJVv6pq1l3V1OdBPR1lH4CV8,
	anup.patel-dY08KVG/lbpWk0Htik3J/w,
	thunder.leizhen-hv44wF8Li93QT0dZR+AlfA, Catalin Marinas

PCI devices now suffer the same hiccup as platform devices, in that they
get their DMA ops configured before they have been added to their bus,
and thus before we know whether they have successfully registered with
an IOMMU or not. Until the necessary driver core changes to reorder
calls during device creation have been worked out, extend our delayed
notifier trick onto the PCI bus so as to avoid broken DMA ops for IOMMUs
there.

CC: Catalin Marinas <catalin.marinas-5wv7dgnIgG8@public.gmane.org>
CC: Will Deacon <will.deacon-5wv7dgnIgG8@public.gmane.org>
Signed-off-by: Robin Murphy <robin.murphy-5wv7dgnIgG8@public.gmane.org>
---
 arch/arm64/mm/dma-mapping.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/arch/arm64/mm/dma-mapping.c b/arch/arm64/mm/dma-mapping.c
index a6e757c..607e709 100644
--- a/arch/arm64/mm/dma-mapping.c
+++ b/arch/arm64/mm/dma-mapping.c
@@ -933,6 +933,10 @@ static int __init __iommu_dma_init(void)
 		ret = register_iommu_dma_ops_notifier(&platform_bus_type);
 	if (!ret)
 		ret = register_iommu_dma_ops_notifier(&amba_bustype);
+#ifdef CONFIG_PCI
+	if (!ret)
+		ret = register_iommu_dma_ops_notifier(&pci_bus_type);
+#endif
 
 	/* handle devices queued before this arch_initcall */
 	if (!ret)
-- 
2.7.2.333.g70bd996.dirty

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

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 04/12] arm64/dma-mapping: Extend DMA ops workaround to PCI devices
@ 2016-02-29 13:46     ` Robin Murphy
  0 siblings, 0 replies; 38+ messages in thread
From: Robin Murphy @ 2016-02-29 13:46 UTC (permalink / raw)
  To: linux-arm-kernel

PCI devices now suffer the same hiccup as platform devices, in that they
get their DMA ops configured before they have been added to their bus,
and thus before we know whether they have successfully registered with
an IOMMU or not. Until the necessary driver core changes to reorder
calls during device creation have been worked out, extend our delayed
notifier trick onto the PCI bus so as to avoid broken DMA ops for IOMMUs
there.

CC: Catalin Marinas <catalin.marinas@arm.com>
CC: Will Deacon <will.deacon@arm.com>
Signed-off-by: Robin Murphy <robin.murphy@arm.com>
---
 arch/arm64/mm/dma-mapping.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/arch/arm64/mm/dma-mapping.c b/arch/arm64/mm/dma-mapping.c
index a6e757c..607e709 100644
--- a/arch/arm64/mm/dma-mapping.c
+++ b/arch/arm64/mm/dma-mapping.c
@@ -933,6 +933,10 @@ static int __init __iommu_dma_init(void)
 		ret = register_iommu_dma_ops_notifier(&platform_bus_type);
 	if (!ret)
 		ret = register_iommu_dma_ops_notifier(&amba_bustype);
+#ifdef CONFIG_PCI
+	if (!ret)
+		ret = register_iommu_dma_ops_notifier(&pci_bus_type);
+#endif
 
 	/* handle devices queued before this arch_initcall */
 	if (!ret)
-- 
2.7.2.333.g70bd996.dirty

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 05/12] arm64/dma-mapping: Remove default domain workaround
  2016-02-29 13:46 ` Robin Murphy
@ 2016-02-29 13:46     ` Robin Murphy
  -1 siblings, 0 replies; 38+ messages in thread
From: Robin Murphy @ 2016-02-29 13:46 UTC (permalink / raw)
  To: iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA
  Cc: Thomas.Lendacky-5C7GfCeVMHo, Catalin Marinas,
	thunder.leizhen-hv44wF8Li93QT0dZR+AlfA, will.deacon-5wv7dgnIgG8,
	stuart.yoder-3arQi8VN3Tc,
	tchalamarla-M3mlKVOIwJVv6pq1l3V1OdBPR1lH4CV8

With the IOMMU core now taking care of default domains for groups
regardless of bus type, we can gleefully rip out this stop-gap, as
slight recompense for having to expand the other one.

CC: Catalin Marinas <catalin.marinas-5wv7dgnIgG8@public.gmane.org>
CC: Will Deacon <will.deacon-5wv7dgnIgG8@public.gmane.org>
Signed-off-by: Robin Murphy <robin.murphy-5wv7dgnIgG8@public.gmane.org>
---
 arch/arm64/mm/dma-mapping.c | 52 +++++++--------------------------------------
 1 file changed, 8 insertions(+), 44 deletions(-)

diff --git a/arch/arm64/mm/dma-mapping.c b/arch/arm64/mm/dma-mapping.c
index 607e709..fd8b942 100644
--- a/arch/arm64/mm/dma-mapping.c
+++ b/arch/arm64/mm/dma-mapping.c
@@ -804,57 +804,24 @@ struct iommu_dma_notifier_data {
 static LIST_HEAD(iommu_dma_masters);
 static DEFINE_MUTEX(iommu_dma_notifier_lock);
 
-/*
- * Temporarily "borrow" a domain feature flag to to tell if we had to resort
- * to creating our own domain here, in case we need to clean it up again.
- */
-#define __IOMMU_DOMAIN_FAKE_DEFAULT		(1U << 31)
-
 static bool do_iommu_attach(struct device *dev, const struct iommu_ops *ops,
 			   u64 dma_base, u64 size)
 {
 	struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
 
 	/*
-	 * Best case: The device is either part of a group which was
-	 * already attached to a domain in a previous call, or it's
-	 * been put in a default DMA domain by the IOMMU core.
+	 * If the IOMMU driver has the DMA domain support that we require,
+	 * then the IOMMU core will have already configured a group for this
+	 * device, and allocated the default domain for that group.
 	 */
-	if (!domain) {
-		/*
-		 * Urgh. The IOMMU core isn't going to do default domains
-		 * for non-PCI devices anyway, until it has some means of
-		 * abstracting the entirely implementation-specific
-		 * sideband data/SoC topology/unicorn dust that may or
-		 * may not differentiate upstream masters.
-		 * So until then, HORRIBLE HACKS!
-		 */
-		domain = ops->domain_alloc(IOMMU_DOMAIN_DMA);
-		if (!domain)
-			goto out_no_domain;
-
-		domain->ops = ops;
-		domain->type = IOMMU_DOMAIN_DMA | __IOMMU_DOMAIN_FAKE_DEFAULT;
-
-		if (iommu_attach_device(domain, dev))
-			goto out_put_domain;
+	if (!domain || iommu_dma_init_domain(domain, dma_base, size)) {
+		pr_warn("Failed to set up IOMMU for device %s; retaining platform DMA ops\n",
+			dev_name(dev));
+		return false;
 	}
 
-	if (iommu_dma_init_domain(domain, dma_base, size))
-		goto out_detach;
-
 	dev->archdata.dma_ops = &iommu_dma_ops;
 	return true;
-
-out_detach:
-	iommu_detach_device(domain, dev);
-out_put_domain:
-	if (domain->type & __IOMMU_DOMAIN_FAKE_DEFAULT)
-		iommu_domain_free(domain);
-out_no_domain:
-	pr_warn("Failed to set up IOMMU for device %s; retaining platform DMA ops\n",
-		dev_name(dev));
-	return false;
 }
 
 static void queue_iommu_attach(struct device *dev, const struct iommu_ops *ops,
@@ -971,11 +938,8 @@ void arch_teardown_dma_ops(struct device *dev)
 {
 	struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
 
-	if (domain) {
+	if (WARN_ON(domain))
 		iommu_detach_device(domain, dev);
-		if (domain->type & __IOMMU_DOMAIN_FAKE_DEFAULT)
-			iommu_domain_free(domain);
-	}
 
 	dev->archdata.dma_ops = NULL;
 }
-- 
2.7.2.333.g70bd996.dirty

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 05/12] arm64/dma-mapping: Remove default domain workaround
@ 2016-02-29 13:46     ` Robin Murphy
  0 siblings, 0 replies; 38+ messages in thread
From: Robin Murphy @ 2016-02-29 13:46 UTC (permalink / raw)
  To: linux-arm-kernel

With the IOMMU core now taking care of default domains for groups
regardless of bus type, we can gleefully rip out this stop-gap, as
slight recompense for having to expand the other one.

CC: Catalin Marinas <catalin.marinas@arm.com>
CC: Will Deacon <will.deacon@arm.com>
Signed-off-by: Robin Murphy <robin.murphy@arm.com>
---
 arch/arm64/mm/dma-mapping.c | 52 +++++++--------------------------------------
 1 file changed, 8 insertions(+), 44 deletions(-)

diff --git a/arch/arm64/mm/dma-mapping.c b/arch/arm64/mm/dma-mapping.c
index 607e709..fd8b942 100644
--- a/arch/arm64/mm/dma-mapping.c
+++ b/arch/arm64/mm/dma-mapping.c
@@ -804,57 +804,24 @@ struct iommu_dma_notifier_data {
 static LIST_HEAD(iommu_dma_masters);
 static DEFINE_MUTEX(iommu_dma_notifier_lock);
 
-/*
- * Temporarily "borrow" a domain feature flag to to tell if we had to resort
- * to creating our own domain here, in case we need to clean it up again.
- */
-#define __IOMMU_DOMAIN_FAKE_DEFAULT		(1U << 31)
-
 static bool do_iommu_attach(struct device *dev, const struct iommu_ops *ops,
 			   u64 dma_base, u64 size)
 {
 	struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
 
 	/*
-	 * Best case: The device is either part of a group which was
-	 * already attached to a domain in a previous call, or it's
-	 * been put in a default DMA domain by the IOMMU core.
+	 * If the IOMMU driver has the DMA domain support that we require,
+	 * then the IOMMU core will have already configured a group for this
+	 * device, and allocated the default domain for that group.
 	 */
-	if (!domain) {
-		/*
-		 * Urgh. The IOMMU core isn't going to do default domains
-		 * for non-PCI devices anyway, until it has some means of
-		 * abstracting the entirely implementation-specific
-		 * sideband data/SoC topology/unicorn dust that may or
-		 * may not differentiate upstream masters.
-		 * So until then, HORRIBLE HACKS!
-		 */
-		domain = ops->domain_alloc(IOMMU_DOMAIN_DMA);
-		if (!domain)
-			goto out_no_domain;
-
-		domain->ops = ops;
-		domain->type = IOMMU_DOMAIN_DMA | __IOMMU_DOMAIN_FAKE_DEFAULT;
-
-		if (iommu_attach_device(domain, dev))
-			goto out_put_domain;
+	if (!domain || iommu_dma_init_domain(domain, dma_base, size)) {
+		pr_warn("Failed to set up IOMMU for device %s; retaining platform DMA ops\n",
+			dev_name(dev));
+		return false;
 	}
 
-	if (iommu_dma_init_domain(domain, dma_base, size))
-		goto out_detach;
-
 	dev->archdata.dma_ops = &iommu_dma_ops;
 	return true;
-
-out_detach:
-	iommu_detach_device(domain, dev);
-out_put_domain:
-	if (domain->type & __IOMMU_DOMAIN_FAKE_DEFAULT)
-		iommu_domain_free(domain);
-out_no_domain:
-	pr_warn("Failed to set up IOMMU for device %s; retaining platform DMA ops\n",
-		dev_name(dev));
-	return false;
 }
 
 static void queue_iommu_attach(struct device *dev, const struct iommu_ops *ops,
@@ -971,11 +938,8 @@ void arch_teardown_dma_ops(struct device *dev)
 {
 	struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
 
-	if (domain) {
+	if (WARN_ON(domain))
 		iommu_detach_device(domain, dev);
-		if (domain->type & __IOMMU_DOMAIN_FAKE_DEFAULT)
-			iommu_domain_free(domain);
-	}
 
 	dev->archdata.dma_ops = NULL;
 }
-- 
2.7.2.333.g70bd996.dirty

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 06/12] iommu/arm-smmu: Streamline SMMU data lookup
  2016-02-29 13:46 ` Robin Murphy
@ 2016-02-29 13:46     ` Robin Murphy
  -1 siblings, 0 replies; 38+ messages in thread
From: Robin Murphy @ 2016-02-29 13:46 UTC (permalink / raw)
  To: iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA
  Cc: will.deacon-5wv7dgnIgG8, Suravee.Suthikulpanit-5C7GfCeVMHo,
	Thomas.Lendacky-5C7GfCeVMHo, stuart.yoder-3arQi8VN3Tc,
	tchalamarla-M3mlKVOIwJVv6pq1l3V1OdBPR1lH4CV8,
	anup.patel-dY08KVG/lbpWk0Htik3J/w,
	thunder.leizhen-hv44wF8Li93QT0dZR+AlfA

Simplify things by stashing our SMMU instance data in drvdata, so that
we don't need to go off hunting for it in the driver model callbacks.
Watch out, arm_smmu_devices list, your days are numbered!

Signed-off-by: Robin Murphy <robin.murphy-5wv7dgnIgG8@public.gmane.org>
---
 drivers/iommu/arm-smmu.c | 20 +++++++-------------
 1 file changed, 7 insertions(+), 13 deletions(-)

diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index 2409e3b..514ae2d 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -1836,6 +1836,7 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
 		}
 	}
 
+	platform_set_drvdata(pdev, smmu);
 	INIT_LIST_HEAD(&smmu->list);
 	spin_lock(&arm_smmu_devices_lock);
 	list_add(&smmu->list, &arm_smmu_devices);
@@ -1860,24 +1861,17 @@ out_put_masters:
 
 static int arm_smmu_device_remove(struct platform_device *pdev)
 {
+	struct arm_smmu_device *smmu = platform_get_drvdata(pdev);
 	int i;
-	struct device *dev = &pdev->dev;
-	struct arm_smmu_device *curr, *smmu = NULL;
 	struct rb_node *node;
 
-	spin_lock(&arm_smmu_devices_lock);
-	list_for_each_entry(curr, &arm_smmu_devices, list) {
-		if (curr->dev == dev) {
-			smmu = curr;
-			list_del(&smmu->list);
-			break;
-		}
-	}
-	spin_unlock(&arm_smmu_devices_lock);
-
 	if (!smmu)
 		return -ENODEV;
 
+	spin_lock(&arm_smmu_devices_lock);
+	list_del(&smmu->list);
+	spin_unlock(&arm_smmu_devices_lock);
+
 	for (node = rb_first(&smmu->masters); node; node = rb_next(node)) {
 		struct arm_smmu_master *master
 			= container_of(node, struct arm_smmu_master, node);
@@ -1885,7 +1879,7 @@ static int arm_smmu_device_remove(struct platform_device *pdev)
 	}
 
 	if (!bitmap_empty(smmu->context_map, ARM_SMMU_MAX_CBS))
-		dev_err(dev, "removing device with active domains!\n");
+		dev_err(&pdev->dev, "removing device with active domains!\n");
 
 	for (i = 0; i < smmu->num_global_irqs; ++i)
 		free_irq(smmu->irqs[i], smmu);
-- 
2.7.2.333.g70bd996.dirty

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

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 06/12] iommu/arm-smmu: Streamline SMMU data lookup
@ 2016-02-29 13:46     ` Robin Murphy
  0 siblings, 0 replies; 38+ messages in thread
From: Robin Murphy @ 2016-02-29 13:46 UTC (permalink / raw)
  To: linux-arm-kernel

Simplify things by stashing our SMMU instance data in drvdata, so that
we don't need to go off hunting for it in the driver model callbacks.
Watch out, arm_smmu_devices list, your days are numbered!

Signed-off-by: Robin Murphy <robin.murphy@arm.com>
---
 drivers/iommu/arm-smmu.c | 20 +++++++-------------
 1 file changed, 7 insertions(+), 13 deletions(-)

diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index 2409e3b..514ae2d 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -1836,6 +1836,7 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
 		}
 	}
 
+	platform_set_drvdata(pdev, smmu);
 	INIT_LIST_HEAD(&smmu->list);
 	spin_lock(&arm_smmu_devices_lock);
 	list_add(&smmu->list, &arm_smmu_devices);
@@ -1860,24 +1861,17 @@ out_put_masters:
 
 static int arm_smmu_device_remove(struct platform_device *pdev)
 {
+	struct arm_smmu_device *smmu = platform_get_drvdata(pdev);
 	int i;
-	struct device *dev = &pdev->dev;
-	struct arm_smmu_device *curr, *smmu = NULL;
 	struct rb_node *node;
 
-	spin_lock(&arm_smmu_devices_lock);
-	list_for_each_entry(curr, &arm_smmu_devices, list) {
-		if (curr->dev == dev) {
-			smmu = curr;
-			list_del(&smmu->list);
-			break;
-		}
-	}
-	spin_unlock(&arm_smmu_devices_lock);
-
 	if (!smmu)
 		return -ENODEV;
 
+	spin_lock(&arm_smmu_devices_lock);
+	list_del(&smmu->list);
+	spin_unlock(&arm_smmu_devices_lock);
+
 	for (node = rb_first(&smmu->masters); node; node = rb_next(node)) {
 		struct arm_smmu_master *master
 			= container_of(node, struct arm_smmu_master, node);
@@ -1885,7 +1879,7 @@ static int arm_smmu_device_remove(struct platform_device *pdev)
 	}
 
 	if (!bitmap_empty(smmu->context_map, ARM_SMMU_MAX_CBS))
-		dev_err(dev, "removing device with active domains!\n");
+		dev_err(&pdev->dev, "removing device with active domains!\n");
 
 	for (i = 0; i < smmu->num_global_irqs; ++i)
 		free_irq(smmu->irqs[i], smmu);
-- 
2.7.2.333.g70bd996.dirty

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 07/12] iommu/arm-smmu: Factor out mmu-masters handling
  2016-02-29 13:46 ` Robin Murphy
@ 2016-02-29 13:46     ` Robin Murphy
  -1 siblings, 0 replies; 38+ messages in thread
From: Robin Murphy @ 2016-02-29 13:46 UTC (permalink / raw)
  To: iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA
  Cc: will.deacon-5wv7dgnIgG8, Suravee.Suthikulpanit-5C7GfCeVMHo,
	Thomas.Lendacky-5C7GfCeVMHo, stuart.yoder-3arQi8VN3Tc,
	tchalamarla-M3mlKVOIwJVv6pq1l3V1OdBPR1lH4CV8,
	anup.patel-dY08KVG/lbpWk0Htik3J/w,
	thunder.leizhen-hv44wF8Li93QT0dZR+AlfA

Reorder the SMMU probe routine slightly to separate probing and
configuring the SMMU itself from processing the mmu-masters property,
and break the handling of the latter out into self-contained functions.

In preparation for changes to come, the failure mode is changed slightly
such that instead of a malformed mmu-masters property aborting the whole
SMMU device probe, we instead roll back any association with those
master devices and let the SMMU itself continue without them.

Signed-off-by: Robin Murphy <robin.murphy-5wv7dgnIgG8@public.gmane.org>
---
 drivers/iommu/arm-smmu.c | 88 ++++++++++++++++++++++++++----------------------
 1 file changed, 48 insertions(+), 40 deletions(-)

diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index 514ae2d..6f7c531 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -465,7 +465,6 @@ static int insert_smmu_master(struct arm_smmu_device *smmu,
 }
 
 static int register_smmu_master(struct arm_smmu_device *smmu,
-				struct device *dev,
 				struct of_phandle_args *masterspec)
 {
 	int i;
@@ -473,20 +472,20 @@ static int register_smmu_master(struct arm_smmu_device *smmu,
 
 	master = find_smmu_master(smmu, masterspec->np);
 	if (master) {
-		dev_err(dev,
+		dev_err(smmu->dev,
 			"rejecting multiple registrations for master device %s\n",
 			masterspec->np->name);
 		return -EBUSY;
 	}
 
 	if (masterspec->args_count > MAX_MASTER_STREAMIDS) {
-		dev_err(dev,
+		dev_err(smmu->dev,
 			"reached maximum number (%d) of stream IDs for master device %s\n",
 			MAX_MASTER_STREAMIDS, masterspec->np->name);
 		return -ENOSPC;
 	}
 
-	master = devm_kzalloc(dev, sizeof(*master), GFP_KERNEL);
+	master = devm_kzalloc(smmu->dev, sizeof(*master), GFP_KERNEL);
 	if (!master)
 		return -ENOMEM;
 
@@ -498,7 +497,7 @@ static int register_smmu_master(struct arm_smmu_device *smmu,
 
 		if (!(smmu->features & ARM_SMMU_FEAT_STREAM_MATCH) &&
 		     (streamid >= smmu->num_mapping_groups)) {
-			dev_err(dev,
+			dev_err(smmu->dev,
 				"stream ID for master device %s greater than maximum allowed (%d)\n",
 				masterspec->np->name, smmu->num_mapping_groups);
 			return -ERANGE;
@@ -1730,14 +1729,53 @@ static const struct of_device_id arm_smmu_of_match[] = {
 };
 MODULE_DEVICE_TABLE(of, arm_smmu_of_match);
 
+static void arm_smmu_remove_mmu_masters(struct arm_smmu_device *smmu)
+{
+	struct arm_smmu_master *master;
+	struct rb_node *node, *tmp;
+
+	node = rb_first(&smmu->masters);
+	while (node) {
+		tmp = node;
+		node = rb_next(node);
+		master = container_of(tmp, struct arm_smmu_master, node);
+
+		rb_erase(tmp, &smmu->masters);
+		of_node_put(master->of_node);
+		devm_kfree(smmu->dev, master);
+	}
+}
+
+static int arm_smmu_probe_mmu_masters(struct arm_smmu_device *smmu)
+{
+	struct of_phandle_args masterspec;
+	int err, i = 0;
+
+	while (!of_parse_phandle_with_args(smmu->dev->of_node, "mmu-masters",
+			"#stream-id-cells", i,
+			&masterspec)) {
+		err = register_smmu_master(smmu, &masterspec);
+		if (err)
+			break;
+		i++;
+	}
+
+	if (err) {
+		dev_err(smmu->dev, "failed to register mmu-masters\n");
+		arm_smmu_remove_mmu_masters(smmu);
+	} else if (i) {
+		dev_info(smmu->dev, "registered %d mmu-masters\n", i);
+	}
+
+	return err;
+}
+
 static int arm_smmu_device_dt_probe(struct platform_device *pdev)
 {
 	const struct of_device_id *of_id;
 	struct resource *res;
 	struct arm_smmu_device *smmu;
 	struct device *dev = &pdev->dev;
-	struct rb_node *node;
-	struct of_phandle_args masterspec;
 	int num_irqs, i, err;
 
 	smmu = devm_kzalloc(dev, sizeof(*smmu), GFP_KERNEL);
@@ -1796,22 +1834,6 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
 	if (err)
 		return err;
 
-	i = 0;
-	smmu->masters = RB_ROOT;
-	while (!of_parse_phandle_with_args(dev->of_node, "mmu-masters",
-					   "#stream-id-cells", i,
-					   &masterspec)) {
-		err = register_smmu_master(smmu, dev, &masterspec);
-		if (err) {
-			dev_err(dev, "failed to add master %s\n",
-				masterspec.np->name);
-			goto out_put_masters;
-		}
-
-		i++;
-	}
-	dev_notice(dev, "registered %d master devices\n", i);
-
 	parse_driver_options(smmu);
 
 	if (smmu->version > ARM_SMMU_V1 &&
@@ -1819,8 +1841,7 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
 		dev_err(dev,
 			"found only %d context interrupt(s) but %d required\n",
 			smmu->num_context_irqs, smmu->num_context_banks);
-		err = -ENODEV;
-		goto out_put_masters;
+		return -ENODEV;
 	}
 
 	for (i = 0; i < smmu->num_global_irqs; ++i) {
@@ -1837,6 +1858,7 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
 	}
 
 	platform_set_drvdata(pdev, smmu);
+	arm_smmu_probe_mmu_masters(smmu);
 	INIT_LIST_HEAD(&smmu->list);
 	spin_lock(&arm_smmu_devices_lock);
 	list_add(&smmu->list, &arm_smmu_devices);
@@ -1848,14 +1870,6 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
 out_free_irqs:
 	while (i--)
 		free_irq(smmu->irqs[i], smmu);
-
-out_put_masters:
-	for (node = rb_first(&smmu->masters); node; node = rb_next(node)) {
-		struct arm_smmu_master *master
-			= container_of(node, struct arm_smmu_master, node);
-		of_node_put(master->of_node);
-	}
-
 	return err;
 }
 
@@ -1863,21 +1877,15 @@ static int arm_smmu_device_remove(struct platform_device *pdev)
 {
 	struct arm_smmu_device *smmu = platform_get_drvdata(pdev);
 	int i;
-	struct rb_node *node;
 
 	if (!smmu)
 		return -ENODEV;
 
+	arm_smmu_remove_mmu_masters(smmu);
 	spin_lock(&arm_smmu_devices_lock);
 	list_del(&smmu->list);
 	spin_unlock(&arm_smmu_devices_lock);
 
-	for (node = rb_first(&smmu->masters); node; node = rb_next(node)) {
-		struct arm_smmu_master *master
-			= container_of(node, struct arm_smmu_master, node);
-		of_node_put(master->of_node);
-	}
-
 	if (!bitmap_empty(smmu->context_map, ARM_SMMU_MAX_CBS))
 		dev_err(&pdev->dev, "removing device with active domains!\n");
 
-- 
2.7.2.333.g70bd996.dirty

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

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 07/12] iommu/arm-smmu: Factor out mmu-masters handling
@ 2016-02-29 13:46     ` Robin Murphy
  0 siblings, 0 replies; 38+ messages in thread
From: Robin Murphy @ 2016-02-29 13:46 UTC (permalink / raw)
  To: linux-arm-kernel

Reorder the SMMU probe routine slightly to separate probing and
configuring the SMMU itself from processing the mmu-masters property,
and break the handling of the latter out into self-contained functions.

In preparation for changes to come, the failure mode is changed slightly
such that instead of a malformed mmu-masters property aborting the whole
SMMU device probe, we instead roll back any association with those
master devices and let the SMMU itself continue without them.

Signed-off-by: Robin Murphy <robin.murphy@arm.com>
---
 drivers/iommu/arm-smmu.c | 88 ++++++++++++++++++++++++++----------------------
 1 file changed, 48 insertions(+), 40 deletions(-)

diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index 514ae2d..6f7c531 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -465,7 +465,6 @@ static int insert_smmu_master(struct arm_smmu_device *smmu,
 }
 
 static int register_smmu_master(struct arm_smmu_device *smmu,
-				struct device *dev,
 				struct of_phandle_args *masterspec)
 {
 	int i;
@@ -473,20 +472,20 @@ static int register_smmu_master(struct arm_smmu_device *smmu,
 
 	master = find_smmu_master(smmu, masterspec->np);
 	if (master) {
-		dev_err(dev,
+		dev_err(smmu->dev,
 			"rejecting multiple registrations for master device %s\n",
 			masterspec->np->name);
 		return -EBUSY;
 	}
 
 	if (masterspec->args_count > MAX_MASTER_STREAMIDS) {
-		dev_err(dev,
+		dev_err(smmu->dev,
 			"reached maximum number (%d) of stream IDs for master device %s\n",
 			MAX_MASTER_STREAMIDS, masterspec->np->name);
 		return -ENOSPC;
 	}
 
-	master = devm_kzalloc(dev, sizeof(*master), GFP_KERNEL);
+	master = devm_kzalloc(smmu->dev, sizeof(*master), GFP_KERNEL);
 	if (!master)
 		return -ENOMEM;
 
@@ -498,7 +497,7 @@ static int register_smmu_master(struct arm_smmu_device *smmu,
 
 		if (!(smmu->features & ARM_SMMU_FEAT_STREAM_MATCH) &&
 		     (streamid >= smmu->num_mapping_groups)) {
-			dev_err(dev,
+			dev_err(smmu->dev,
 				"stream ID for master device %s greater than maximum allowed (%d)\n",
 				masterspec->np->name, smmu->num_mapping_groups);
 			return -ERANGE;
@@ -1730,14 +1729,53 @@ static const struct of_device_id arm_smmu_of_match[] = {
 };
 MODULE_DEVICE_TABLE(of, arm_smmu_of_match);
 
+static void arm_smmu_remove_mmu_masters(struct arm_smmu_device *smmu)
+{
+	struct arm_smmu_master *master;
+	struct rb_node *node, *tmp;
+
+	node = rb_first(&smmu->masters);
+	while (node) {
+		tmp = node;
+		node = rb_next(node);
+		master = container_of(tmp, struct arm_smmu_master, node);
+
+		rb_erase(tmp, &smmu->masters);
+		of_node_put(master->of_node);
+		devm_kfree(smmu->dev, master);
+	}
+}
+
+static int arm_smmu_probe_mmu_masters(struct arm_smmu_device *smmu)
+{
+	struct of_phandle_args masterspec;
+	int err, i = 0;
+
+	while (!of_parse_phandle_with_args(smmu->dev->of_node, "mmu-masters",
+			"#stream-id-cells", i,
+			&masterspec)) {
+		err = register_smmu_master(smmu, &masterspec);
+		if (err)
+			break;
+		i++;
+	}
+
+	if (err) {
+		dev_err(smmu->dev, "failed to register mmu-masters\n");
+		arm_smmu_remove_mmu_masters(smmu);
+	} else if (i) {
+		dev_info(smmu->dev, "registered %d mmu-masters\n", i);
+	}
+
+	return err;
+}
+
 static int arm_smmu_device_dt_probe(struct platform_device *pdev)
 {
 	const struct of_device_id *of_id;
 	struct resource *res;
 	struct arm_smmu_device *smmu;
 	struct device *dev = &pdev->dev;
-	struct rb_node *node;
-	struct of_phandle_args masterspec;
 	int num_irqs, i, err;
 
 	smmu = devm_kzalloc(dev, sizeof(*smmu), GFP_KERNEL);
@@ -1796,22 +1834,6 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
 	if (err)
 		return err;
 
-	i = 0;
-	smmu->masters = RB_ROOT;
-	while (!of_parse_phandle_with_args(dev->of_node, "mmu-masters",
-					   "#stream-id-cells", i,
-					   &masterspec)) {
-		err = register_smmu_master(smmu, dev, &masterspec);
-		if (err) {
-			dev_err(dev, "failed to add master %s\n",
-				masterspec.np->name);
-			goto out_put_masters;
-		}
-
-		i++;
-	}
-	dev_notice(dev, "registered %d master devices\n", i);
-
 	parse_driver_options(smmu);
 
 	if (smmu->version > ARM_SMMU_V1 &&
@@ -1819,8 +1841,7 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
 		dev_err(dev,
 			"found only %d context interrupt(s) but %d required\n",
 			smmu->num_context_irqs, smmu->num_context_banks);
-		err = -ENODEV;
-		goto out_put_masters;
+		return -ENODEV;
 	}
 
 	for (i = 0; i < smmu->num_global_irqs; ++i) {
@@ -1837,6 +1858,7 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
 	}
 
 	platform_set_drvdata(pdev, smmu);
+	arm_smmu_probe_mmu_masters(smmu);
 	INIT_LIST_HEAD(&smmu->list);
 	spin_lock(&arm_smmu_devices_lock);
 	list_add(&smmu->list, &arm_smmu_devices);
@@ -1848,14 +1870,6 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
 out_free_irqs:
 	while (i--)
 		free_irq(smmu->irqs[i], smmu);
-
-out_put_masters:
-	for (node = rb_first(&smmu->masters); node; node = rb_next(node)) {
-		struct arm_smmu_master *master
-			= container_of(node, struct arm_smmu_master, node);
-		of_node_put(master->of_node);
-	}
-
 	return err;
 }
 
@@ -1863,21 +1877,15 @@ static int arm_smmu_device_remove(struct platform_device *pdev)
 {
 	struct arm_smmu_device *smmu = platform_get_drvdata(pdev);
 	int i;
-	struct rb_node *node;
 
 	if (!smmu)
 		return -ENODEV;
 
+	arm_smmu_remove_mmu_masters(smmu);
 	spin_lock(&arm_smmu_devices_lock);
 	list_del(&smmu->list);
 	spin_unlock(&arm_smmu_devices_lock);
 
-	for (node = rb_first(&smmu->masters); node; node = rb_next(node)) {
-		struct arm_smmu_master *master
-			= container_of(node, struct arm_smmu_master, node);
-		of_node_put(master->of_node);
-	}
-
 	if (!bitmap_empty(smmu->context_map, ARM_SMMU_MAX_CBS))
 		dev_err(&pdev->dev, "removing device with active domains!\n");
 
-- 
2.7.2.333.g70bd996.dirty

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 08/12] iommu/arm-smmu: Refactor master/group config handling
  2016-02-29 13:46 ` Robin Murphy
@ 2016-02-29 13:46     ` Robin Murphy
  -1 siblings, 0 replies; 38+ messages in thread
From: Robin Murphy @ 2016-02-29 13:46 UTC (permalink / raw)
  To: iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA
  Cc: will.deacon-5wv7dgnIgG8, Suravee.Suthikulpanit-5C7GfCeVMHo,
	Thomas.Lendacky-5C7GfCeVMHo, stuart.yoder-3arQi8VN3Tc,
	tchalamarla-M3mlKVOIwJVv6pq1l3V1OdBPR1lH4CV8,
	anup.patel-dY08KVG/lbpWk0Htik3J/w,
	thunder.leizhen-hv44wF8Li93QT0dZR+AlfA

We currently have some very vague and blurred boundaries around devices,
groups, and what exactly an arm_smmu_master_cfg represents. Before we
can properly deal with multi-master groups, this needs sorting out.

Ratify arm_smmu_master_cfg to strictly represent the set of stream IDs
used by an individual device, plus the necessary per-device information
for efficient lookup. Complement this by introducing arm_smmu_group_cfg
to represent the union of all IDs owned by members of the group and
arbitrate the actual allocation of stream mapping table resources.

Signed-off-by: Robin Murphy <robin.murphy-5wv7dgnIgG8@public.gmane.org>
---
 drivers/iommu/arm-smmu.c | 321 +++++++++++++++++++++++++----------------------
 1 file changed, 171 insertions(+), 150 deletions(-)

diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index 6f7c531..5eaa8cf 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -274,16 +274,29 @@ enum arm_smmu_arch_version {
 	ARM_SMMU_V2,
 };
 
-struct arm_smmu_smr {
-	u8				idx;
+#define STREAM_UNASSIGNED		0
+#define STREAM_MULTIPLE			-1
+struct arm_smmu_streamid {
+	int				s2cr_idx;
+	u16				mask;
+	u16				id;
+};
+
+struct arm_smmu_stream_map_entry {
 	u16				mask;
 	u16				id;
 };
 
 struct arm_smmu_master_cfg {
+	struct arm_smmu_device		*smmu;
+	struct arm_smmu_domain		*smmu_domain;
 	int				num_streamids;
-	u16				streamids[MAX_MASTER_STREAMIDS];
-	struct arm_smmu_smr		*smrs;
+	struct arm_smmu_streamid	streamids[MAX_MASTER_STREAMIDS];
+};
+
+struct arm_smmu_group_cfg {
+	int				num_smes;
+	struct arm_smmu_stream_map_entry smes[MAX_MASTER_STREAMIDS];
 };
 
 struct arm_smmu_master {
@@ -425,20 +438,6 @@ static struct arm_smmu_master *find_smmu_master(struct arm_smmu_device *smmu,
 	return NULL;
 }
 
-static struct arm_smmu_master_cfg *
-find_smmu_master_cfg(struct device *dev)
-{
-	struct arm_smmu_master_cfg *cfg = NULL;
-	struct iommu_group *group = iommu_group_get(dev);
-
-	if (group) {
-		cfg = iommu_group_get_iommudata(group);
-		iommu_group_put(group);
-	}
-
-	return cfg;
-}
-
 static int insert_smmu_master(struct arm_smmu_device *smmu,
 			      struct arm_smmu_master *master)
 {
@@ -502,7 +501,8 @@ static int register_smmu_master(struct arm_smmu_device *smmu,
 				masterspec->np->name, smmu->num_mapping_groups);
 			return -ERANGE;
 		}
-		master->cfg.streamids[i] = streamid;
+		master->cfg.streamids[i].id = streamid;
+		/* leave .mask 0; we don't currently share SMRs */
 	}
 	return insert_smmu_master(smmu, master);
 }
@@ -1006,92 +1006,101 @@ static void arm_smmu_domain_free(struct iommu_domain *domain)
 	kfree(smmu_domain);
 }
 
-static int arm_smmu_master_configure_smrs(struct arm_smmu_device *smmu,
-					  struct arm_smmu_master_cfg *cfg)
+static int arm_smmu_master_configure_smrs(struct arm_smmu_master_cfg *cfg)
 {
-	int i;
-	struct arm_smmu_smr *smrs;
+	int i, err;
+	struct arm_smmu_device *smmu = cfg->smmu;
 	void __iomem *gr0_base = ARM_SMMU_GR0(smmu);
-
-	if (!(smmu->features & ARM_SMMU_FEAT_STREAM_MATCH))
-		return 0;
-
-	if (cfg->smrs)
-		return -EEXIST;
-
-	smrs = kmalloc_array(cfg->num_streamids, sizeof(*smrs), GFP_KERNEL);
-	if (!smrs) {
-		dev_err(smmu->dev, "failed to allocate %d SMRs\n",
-			cfg->num_streamids);
-		return -ENOMEM;
-	}
+	bool stream_match = smmu->features & ARM_SMMU_FEAT_STREAM_MATCH;
 
 	/* Allocate the SMRs on the SMMU */
-	for (i = 0; i < cfg->num_streamids; ++i) {
-		int idx = __arm_smmu_alloc_bitmap(smmu->smr_map, 0,
-						  smmu->num_mapping_groups);
-		if (IS_ERR_VALUE(idx)) {
-			dev_err(smmu->dev, "failed to allocate free SMR\n");
+	for (i = 0; i < cfg->num_streamids; i++) {
+		int idx;
+
+		if (cfg->streamids[i].s2cr_idx == STREAM_MULTIPLE)
+			continue;
+
+		if (cfg->streamids[i].s2cr_idx > STREAM_UNASSIGNED) {
+			err = -EEXIST;
 			goto err_free_smrs;
 		}
 
-		smrs[i] = (struct arm_smmu_smr) {
-			.idx	= idx,
-			.mask	= 0, /* We don't currently share SMRs */
-			.id	= cfg->streamids[i],
-		};
+		if (stream_match)
+			idx = __arm_smmu_alloc_bitmap(smmu->smr_map, 0,
+						smmu->num_mapping_groups);
+		else
+			idx = cfg->streamids[i].id;
+
+		if (IS_ERR_VALUE(idx)) {
+			dev_err(smmu->dev, "failed to allocate free SMR\n");
+			err = -ENOSPC;
+			goto err_free_smrs;
+		}
+
+		cfg->streamids[i].s2cr_idx = idx + 1;
 	}
 
+	/* For stream indexing, we're only here for the bookkeeping... */
+	if (!stream_match)
+		return 0;
+
 	/* It worked! Now, poke the actual hardware */
 	for (i = 0; i < cfg->num_streamids; ++i) {
-		u32 reg = SMR_VALID | smrs[i].id << SMR_ID_SHIFT |
-			  smrs[i].mask << SMR_MASK_SHIFT;
-		writel_relaxed(reg, gr0_base + ARM_SMMU_GR0_SMR(smrs[i].idx));
+		u32 idx = cfg->streamids[i].s2cr_idx - 1;
+		u32 reg = SMR_VALID | cfg->streamids[i].id << SMR_ID_SHIFT |
+			  cfg->streamids[i].mask << SMR_MASK_SHIFT;
+
+		if (idx != STREAM_MULTIPLE)
+			writel_relaxed(reg, gr0_base + ARM_SMMU_GR0_SMR(idx));
 	}
 
-	cfg->smrs = smrs;
 	return 0;
 
 err_free_smrs:
-	while (--i >= 0)
-		__arm_smmu_free_bitmap(smmu->smr_map, smrs[i].idx);
-	kfree(smrs);
-	return -ENOSPC;
+	while (--i >= 0) {
+		int idx = cfg->streamids[i].s2cr_idx - 1;
+
+		if (idx != STREAM_MULTIPLE) {
+			if (stream_match)
+				__arm_smmu_free_bitmap(smmu->smr_map, idx);
+			cfg->streamids[i].s2cr_idx = STREAM_UNASSIGNED;
+		}
+	}
+	return err;
 }
 
-static void arm_smmu_master_free_smrs(struct arm_smmu_device *smmu,
-				      struct arm_smmu_master_cfg *cfg)
+static void arm_smmu_master_free_smrs(struct arm_smmu_master_cfg *cfg)
 {
 	int i;
+	struct arm_smmu_device *smmu = cfg->smmu;
 	void __iomem *gr0_base = ARM_SMMU_GR0(smmu);
-	struct arm_smmu_smr *smrs = cfg->smrs;
-
-	if (!smrs)
-		return;
+	bool stream_match = smmu->features & ARM_SMMU_FEAT_STREAM_MATCH;
 
 	/* Invalidate the SMRs before freeing back to the allocator */
 	for (i = 0; i < cfg->num_streamids; ++i) {
-		u8 idx = smrs[i].idx;
+		u8 idx = cfg->streamids[i].s2cr_idx - 1;
+
+		if (cfg->streamids[i].s2cr_idx == STREAM_MULTIPLE)
+			continue;
+
+		cfg->streamids[i].s2cr_idx = STREAM_UNASSIGNED;
+		if (!stream_match)
+			continue;
 
 		writel_relaxed(~SMR_VALID, gr0_base + ARM_SMMU_GR0_SMR(idx));
 		__arm_smmu_free_bitmap(smmu->smr_map, idx);
 	}
-
-	cfg->smrs = NULL;
-	kfree(smrs);
 }
 
 static int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain,
 				      struct arm_smmu_master_cfg *cfg)
 {
 	int i, ret;
-	struct arm_smmu_device *smmu = smmu_domain->smmu;
-	void __iomem *gr0_base = ARM_SMMU_GR0(smmu);
+	void __iomem *gr0_base = ARM_SMMU_GR0(cfg->smmu);
 
-	/* Devices in an IOMMU group may already be configured */
-	ret = arm_smmu_master_configure_smrs(smmu, cfg);
+	ret = arm_smmu_master_configure_smrs(cfg);
 	if (ret)
-		return ret == -EEXIST ? 0 : ret;
+		return ret;
 
 	/*
 	 * FIXME: This won't be needed once we have IOMMU-backed DMA ops
@@ -1101,50 +1110,34 @@ static int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain,
 		return 0;
 
 	for (i = 0; i < cfg->num_streamids; ++i) {
-		u32 idx, s2cr;
+		u32 idx = cfg->streamids[i].s2cr_idx - 1;
+		u32 s2cr = S2CR_TYPE_TRANS | S2CR_PRIVCFG_UNPRIV |
+			   (smmu_domain->cfg.cbndx << S2CR_CBNDX_SHIFT);
 
-		idx = cfg->smrs ? cfg->smrs[i].idx : cfg->streamids[i];
-		s2cr = S2CR_TYPE_TRANS | S2CR_PRIVCFG_UNPRIV |
-		       (smmu_domain->cfg.cbndx << S2CR_CBNDX_SHIFT);
 		writel_relaxed(s2cr, gr0_base + ARM_SMMU_GR0_S2CR(idx));
 	}
 
 	return 0;
 }
 
-static void arm_smmu_domain_remove_master(struct arm_smmu_domain *smmu_domain,
-					  struct arm_smmu_master_cfg *cfg)
+static void arm_smmu_domain_remove_master(struct arm_smmu_master_cfg *cfg)
 {
 	int i;
-	struct arm_smmu_device *smmu = smmu_domain->smmu;
-	void __iomem *gr0_base = ARM_SMMU_GR0(smmu);
-
-	/* An IOMMU group is torn down by the first device to be removed */
-	if ((smmu->features & ARM_SMMU_FEAT_STREAM_MATCH) && !cfg->smrs)
-		return;
+	void __iomem *gr0_base = ARM_SMMU_GR0(cfg->smmu);
 
 	/*
 	 * We *must* clear the S2CR first, because freeing the SMR means
 	 * that it can be re-allocated immediately.
 	 */
 	for (i = 0; i < cfg->num_streamids; ++i) {
-		u32 idx = cfg->smrs ? cfg->smrs[i].idx : cfg->streamids[i];
+		u32 idx = cfg->streamids[i].s2cr_idx - 1;
 		u32 reg = disable_bypass ? S2CR_TYPE_FAULT : S2CR_TYPE_BYPASS;
 
 		writel_relaxed(reg, gr0_base + ARM_SMMU_GR0_S2CR(idx));
 	}
 
-	arm_smmu_master_free_smrs(smmu, cfg);
-}
-
-static void arm_smmu_detach_dev(struct device *dev,
-				struct arm_smmu_master_cfg *cfg)
-{
-	struct iommu_domain *domain = dev->archdata.iommu;
-	struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
-
-	dev->archdata.iommu = NULL;
-	arm_smmu_domain_remove_master(smmu_domain, cfg);
+	cfg->smmu_domain = NULL;
+	arm_smmu_master_free_smrs(cfg);
 }
 
 static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
@@ -1152,14 +1145,14 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
 	int ret;
 	struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
 	struct arm_smmu_device *smmu;
-	struct arm_smmu_master_cfg *cfg;
+	struct arm_smmu_master_cfg *cfg = dev->archdata.iommu;
 
-	smmu = find_smmu_for_device(dev);
-	if (!smmu) {
+	if (!cfg) {
 		dev_err(dev, "cannot attach to SMMU, is it on the same bus?\n");
 		return -ENXIO;
 	}
 
+	smmu = cfg->smmu;
 	/* Ensure that the domain is finalised */
 	ret = arm_smmu_init_domain_context(domain, smmu);
 	if (IS_ERR_VALUE(ret))
@@ -1176,18 +1169,13 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
 		return -EINVAL;
 	}
 
-	/* Looks ok, so add the device to the domain */
-	cfg = find_smmu_master_cfg(dev);
-	if (!cfg)
-		return -ENODEV;
-
 	/* Detach the dev from its current domain */
-	if (dev->archdata.iommu)
-		arm_smmu_detach_dev(dev, cfg);
+	if (cfg->smmu_domain)
+		arm_smmu_domain_remove_master(cfg);
 
 	ret = arm_smmu_domain_add_master(smmu_domain, cfg);
 	if (!ret)
-		dev->archdata.iommu = domain;
+		cfg->smmu_domain = smmu_domain;
 	return ret;
 }
 
@@ -1315,61 +1303,37 @@ static int __arm_smmu_get_pci_sid(struct pci_dev *pdev, u16 alias, void *data)
 	return 0; /* Continue walking */
 }
 
-static void __arm_smmu_release_pci_iommudata(void *data)
-{
-	kfree(data);
-}
-
-static int arm_smmu_init_pci_device(struct pci_dev *pdev,
-				    struct iommu_group *group)
+static int arm_smmu_init_pci_device(struct arm_smmu_device *smmu,
+				    struct pci_dev *pdev)
 {
 	struct arm_smmu_master_cfg *cfg;
 	u16 sid;
-	int i;
-
-	cfg = iommu_group_get_iommudata(group);
-	if (!cfg) {
-		cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
-		if (!cfg)
-			return -ENOMEM;
-
-		iommu_group_set_iommudata(group, cfg,
-					  __arm_smmu_release_pci_iommudata);
-	}
-
-	if (cfg->num_streamids >= MAX_MASTER_STREAMIDS)
-		return -ENOSPC;
 
+	cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
+	if (!cfg)
+		return -ENOMEM;
 	/*
 	 * Assume Stream ID == Requester ID for now.
 	 * We need a way to describe the ID mappings in FDT.
 	 */
 	pci_for_each_dma_alias(pdev, __arm_smmu_get_pci_sid, &sid);
-	for (i = 0; i < cfg->num_streamids; ++i)
-		if (cfg->streamids[i] == sid)
-			break;
 
-	/* Avoid duplicate SIDs, as this can lead to SMR conflicts */
-	if (i == cfg->num_streamids)
-		cfg->streamids[cfg->num_streamids++] = sid;
+	cfg->streamids[0].id = sid;
+	cfg->num_streamids = 1;
+
+	cfg->smmu = smmu;
+	pdev->dev.archdata.iommu = cfg;
 
 	return 0;
 }
 
-static int arm_smmu_init_platform_device(struct device *dev,
-					 struct iommu_group *group)
+static int arm_smmu_init_platform_device(struct arm_smmu_device *smmu,
+					 struct device *dev)
 {
-	struct arm_smmu_device *smmu = find_smmu_for_device(dev);
 	struct arm_smmu_master *master;
 
-	if (!smmu)
-		return -ENODEV;
-
-	master = find_smmu_master(smmu, dev->of_node);
-	if (!master)
-		return -ENODEV;
-
-	iommu_group_set_iommudata(group, &master->cfg, NULL);
+	master = find_smmu_master(smmu, dev_get_dev_node(dev));
+	dev->archdata.iommu = &master->cfg;
 
 	return 0;
 }
@@ -1377,6 +1341,22 @@ static int arm_smmu_init_platform_device(struct device *dev,
 static int arm_smmu_add_device(struct device *dev)
 {
 	struct iommu_group *group;
+	struct arm_smmu_device *smmu;
+	int ret;
+
+	if (dev->archdata.iommu)
+		return -EEXIST;
+
+	smmu = find_smmu_for_device(dev);
+	if (!smmu)
+		return -ENODEV;
+
+	if (dev_is_pci(dev))
+		ret = arm_smmu_init_pci_device(smmu, to_pci_dev(dev));
+	else
+		ret = arm_smmu_init_platform_device(smmu, dev);
+	if (ret)
+		return ret;
 
 	group = iommu_group_get_for_dev(dev);
 	if (IS_ERR(group))
@@ -1389,12 +1369,30 @@ static int arm_smmu_add_device(struct device *dev)
 static void arm_smmu_remove_device(struct device *dev)
 {
 	iommu_group_remove_device(dev);
+	if (dev_is_pci(dev))
+		kfree(dev->archdata.iommu);
+}
+
+static void __arm_smmu_release_iommudata(void *data)
+{
+	kfree(data);
+}
+
+static inline bool __streamid_match_sme(struct arm_smmu_streamid *sid,
+					struct arm_smmu_stream_map_entry *sme)
+{
+	/* This will get rather more complicated with masking... */
+	return sid->id == sme->id;
 }
 
 static struct iommu_group *arm_smmu_device_group(struct device *dev)
 {
+	struct arm_smmu_master_cfg *master_cfg;
+	struct arm_smmu_group_cfg *group_cfg;
+	struct arm_smmu_streamid *sids;
+	struct arm_smmu_stream_map_entry *smes;
 	struct iommu_group *group;
-	int ret;
+	int i, j;
 
 	if (dev_is_pci(dev))
 		group = pci_device_group(dev);
@@ -1404,14 +1402,37 @@ static struct iommu_group *arm_smmu_device_group(struct device *dev)
 	if (IS_ERR(group))
 		return group;
 
-	if (dev_is_pci(dev))
-		ret = arm_smmu_init_pci_device(to_pci_dev(dev), group);
-	else
-		ret = arm_smmu_init_platform_device(dev, group);
+	master_cfg = dev->archdata.iommu;
+	group_cfg = iommu_group_get_iommudata(group);
+	if (!group_cfg) {
+		group_cfg = kzalloc(sizeof(*group_cfg), GFP_KERNEL);
+		if (!group_cfg)
+			return ERR_PTR(-ENOMEM);
 
-	if (ret) {
-		iommu_group_put(group);
-		group = ERR_PTR(ret);
+		iommu_group_set_iommudata(group, group_cfg,
+					  __arm_smmu_release_iommudata);
+	}
+
+	/* Propagate device's IDs to the group, avoiding duplicate entries */
+	sids = master_cfg->streamids;
+	smes = group_cfg->smes;
+	for (i = 0; i < master_cfg->num_streamids; i++) {
+		for (j = 0; j < group_cfg->num_smes; j++) {
+			if (__streamid_match_sme(&sids[i], &smes[j])) {
+				sids[i].s2cr_idx = STREAM_MULTIPLE;
+				break;
+			}
+		}
+
+		if (j < group_cfg->num_smes)
+			continue;
+
+		if (group_cfg->num_smes == MAX_MASTER_STREAMIDS) {
+			iommu_group_put(group);
+			return ERR_PTR(-ENOSPC);
+		}
+
+		smes[group_cfg->num_smes++].id = sids[i].id;
 	}
 
 	return group;
-- 
2.7.2.333.g70bd996.dirty

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

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 08/12] iommu/arm-smmu: Refactor master/group config handling
@ 2016-02-29 13:46     ` Robin Murphy
  0 siblings, 0 replies; 38+ messages in thread
From: Robin Murphy @ 2016-02-29 13:46 UTC (permalink / raw)
  To: linux-arm-kernel

We currently have some very vague and blurred boundaries around devices,
groups, and what exactly an arm_smmu_master_cfg represents. Before we
can properly deal with multi-master groups, this needs sorting out.

Ratify arm_smmu_master_cfg to strictly represent the set of stream IDs
used by an individual device, plus the necessary per-device information
for efficient lookup. Complement this by introducing arm_smmu_group_cfg
to represent the union of all IDs owned by members of the group and
arbitrate the actual allocation of stream mapping table resources.

Signed-off-by: Robin Murphy <robin.murphy@arm.com>
---
 drivers/iommu/arm-smmu.c | 321 +++++++++++++++++++++++++----------------------
 1 file changed, 171 insertions(+), 150 deletions(-)

diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index 6f7c531..5eaa8cf 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -274,16 +274,29 @@ enum arm_smmu_arch_version {
 	ARM_SMMU_V2,
 };
 
-struct arm_smmu_smr {
-	u8				idx;
+#define STREAM_UNASSIGNED		0
+#define STREAM_MULTIPLE			-1
+struct arm_smmu_streamid {
+	int				s2cr_idx;
+	u16				mask;
+	u16				id;
+};
+
+struct arm_smmu_stream_map_entry {
 	u16				mask;
 	u16				id;
 };
 
 struct arm_smmu_master_cfg {
+	struct arm_smmu_device		*smmu;
+	struct arm_smmu_domain		*smmu_domain;
 	int				num_streamids;
-	u16				streamids[MAX_MASTER_STREAMIDS];
-	struct arm_smmu_smr		*smrs;
+	struct arm_smmu_streamid	streamids[MAX_MASTER_STREAMIDS];
+};
+
+struct arm_smmu_group_cfg {
+	int				num_smes;
+	struct arm_smmu_stream_map_entry smes[MAX_MASTER_STREAMIDS];
 };
 
 struct arm_smmu_master {
@@ -425,20 +438,6 @@ static struct arm_smmu_master *find_smmu_master(struct arm_smmu_device *smmu,
 	return NULL;
 }
 
-static struct arm_smmu_master_cfg *
-find_smmu_master_cfg(struct device *dev)
-{
-	struct arm_smmu_master_cfg *cfg = NULL;
-	struct iommu_group *group = iommu_group_get(dev);
-
-	if (group) {
-		cfg = iommu_group_get_iommudata(group);
-		iommu_group_put(group);
-	}
-
-	return cfg;
-}
-
 static int insert_smmu_master(struct arm_smmu_device *smmu,
 			      struct arm_smmu_master *master)
 {
@@ -502,7 +501,8 @@ static int register_smmu_master(struct arm_smmu_device *smmu,
 				masterspec->np->name, smmu->num_mapping_groups);
 			return -ERANGE;
 		}
-		master->cfg.streamids[i] = streamid;
+		master->cfg.streamids[i].id = streamid;
+		/* leave .mask 0; we don't currently share SMRs */
 	}
 	return insert_smmu_master(smmu, master);
 }
@@ -1006,92 +1006,101 @@ static void arm_smmu_domain_free(struct iommu_domain *domain)
 	kfree(smmu_domain);
 }
 
-static int arm_smmu_master_configure_smrs(struct arm_smmu_device *smmu,
-					  struct arm_smmu_master_cfg *cfg)
+static int arm_smmu_master_configure_smrs(struct arm_smmu_master_cfg *cfg)
 {
-	int i;
-	struct arm_smmu_smr *smrs;
+	int i, err;
+	struct arm_smmu_device *smmu = cfg->smmu;
 	void __iomem *gr0_base = ARM_SMMU_GR0(smmu);
-
-	if (!(smmu->features & ARM_SMMU_FEAT_STREAM_MATCH))
-		return 0;
-
-	if (cfg->smrs)
-		return -EEXIST;
-
-	smrs = kmalloc_array(cfg->num_streamids, sizeof(*smrs), GFP_KERNEL);
-	if (!smrs) {
-		dev_err(smmu->dev, "failed to allocate %d SMRs\n",
-			cfg->num_streamids);
-		return -ENOMEM;
-	}
+	bool stream_match = smmu->features & ARM_SMMU_FEAT_STREAM_MATCH;
 
 	/* Allocate the SMRs on the SMMU */
-	for (i = 0; i < cfg->num_streamids; ++i) {
-		int idx = __arm_smmu_alloc_bitmap(smmu->smr_map, 0,
-						  smmu->num_mapping_groups);
-		if (IS_ERR_VALUE(idx)) {
-			dev_err(smmu->dev, "failed to allocate free SMR\n");
+	for (i = 0; i < cfg->num_streamids; i++) {
+		int idx;
+
+		if (cfg->streamids[i].s2cr_idx == STREAM_MULTIPLE)
+			continue;
+
+		if (cfg->streamids[i].s2cr_idx > STREAM_UNASSIGNED) {
+			err = -EEXIST;
 			goto err_free_smrs;
 		}
 
-		smrs[i] = (struct arm_smmu_smr) {
-			.idx	= idx,
-			.mask	= 0, /* We don't currently share SMRs */
-			.id	= cfg->streamids[i],
-		};
+		if (stream_match)
+			idx = __arm_smmu_alloc_bitmap(smmu->smr_map, 0,
+						smmu->num_mapping_groups);
+		else
+			idx = cfg->streamids[i].id;
+
+		if (IS_ERR_VALUE(idx)) {
+			dev_err(smmu->dev, "failed to allocate free SMR\n");
+			err = -ENOSPC;
+			goto err_free_smrs;
+		}
+
+		cfg->streamids[i].s2cr_idx = idx + 1;
 	}
 
+	/* For stream indexing, we're only here for the bookkeeping... */
+	if (!stream_match)
+		return 0;
+
 	/* It worked! Now, poke the actual hardware */
 	for (i = 0; i < cfg->num_streamids; ++i) {
-		u32 reg = SMR_VALID | smrs[i].id << SMR_ID_SHIFT |
-			  smrs[i].mask << SMR_MASK_SHIFT;
-		writel_relaxed(reg, gr0_base + ARM_SMMU_GR0_SMR(smrs[i].idx));
+		u32 idx = cfg->streamids[i].s2cr_idx - 1;
+		u32 reg = SMR_VALID | cfg->streamids[i].id << SMR_ID_SHIFT |
+			  cfg->streamids[i].mask << SMR_MASK_SHIFT;
+
+		if (idx != STREAM_MULTIPLE)
+			writel_relaxed(reg, gr0_base + ARM_SMMU_GR0_SMR(idx));
 	}
 
-	cfg->smrs = smrs;
 	return 0;
 
 err_free_smrs:
-	while (--i >= 0)
-		__arm_smmu_free_bitmap(smmu->smr_map, smrs[i].idx);
-	kfree(smrs);
-	return -ENOSPC;
+	while (--i >= 0) {
+		int idx = cfg->streamids[i].s2cr_idx - 1;
+
+		if (idx != STREAM_MULTIPLE) {
+			if (stream_match)
+				__arm_smmu_free_bitmap(smmu->smr_map, idx);
+			cfg->streamids[i].s2cr_idx = STREAM_UNASSIGNED;
+		}
+	}
+	return err;
 }
 
-static void arm_smmu_master_free_smrs(struct arm_smmu_device *smmu,
-				      struct arm_smmu_master_cfg *cfg)
+static void arm_smmu_master_free_smrs(struct arm_smmu_master_cfg *cfg)
 {
 	int i;
+	struct arm_smmu_device *smmu = cfg->smmu;
 	void __iomem *gr0_base = ARM_SMMU_GR0(smmu);
-	struct arm_smmu_smr *smrs = cfg->smrs;
-
-	if (!smrs)
-		return;
+	bool stream_match = smmu->features & ARM_SMMU_FEAT_STREAM_MATCH;
 
 	/* Invalidate the SMRs before freeing back to the allocator */
 	for (i = 0; i < cfg->num_streamids; ++i) {
-		u8 idx = smrs[i].idx;
+		u8 idx = cfg->streamids[i].s2cr_idx - 1;
+
+		if (cfg->streamids[i].s2cr_idx == STREAM_MULTIPLE)
+			continue;
+
+		cfg->streamids[i].s2cr_idx = STREAM_UNASSIGNED;
+		if (!stream_match)
+			continue;
 
 		writel_relaxed(~SMR_VALID, gr0_base + ARM_SMMU_GR0_SMR(idx));
 		__arm_smmu_free_bitmap(smmu->smr_map, idx);
 	}
-
-	cfg->smrs = NULL;
-	kfree(smrs);
 }
 
 static int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain,
 				      struct arm_smmu_master_cfg *cfg)
 {
 	int i, ret;
-	struct arm_smmu_device *smmu = smmu_domain->smmu;
-	void __iomem *gr0_base = ARM_SMMU_GR0(smmu);
+	void __iomem *gr0_base = ARM_SMMU_GR0(cfg->smmu);
 
-	/* Devices in an IOMMU group may already be configured */
-	ret = arm_smmu_master_configure_smrs(smmu, cfg);
+	ret = arm_smmu_master_configure_smrs(cfg);
 	if (ret)
-		return ret == -EEXIST ? 0 : ret;
+		return ret;
 
 	/*
 	 * FIXME: This won't be needed once we have IOMMU-backed DMA ops
@@ -1101,50 +1110,34 @@ static int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain,
 		return 0;
 
 	for (i = 0; i < cfg->num_streamids; ++i) {
-		u32 idx, s2cr;
+		u32 idx = cfg->streamids[i].s2cr_idx - 1;
+		u32 s2cr = S2CR_TYPE_TRANS | S2CR_PRIVCFG_UNPRIV |
+			   (smmu_domain->cfg.cbndx << S2CR_CBNDX_SHIFT);
 
-		idx = cfg->smrs ? cfg->smrs[i].idx : cfg->streamids[i];
-		s2cr = S2CR_TYPE_TRANS | S2CR_PRIVCFG_UNPRIV |
-		       (smmu_domain->cfg.cbndx << S2CR_CBNDX_SHIFT);
 		writel_relaxed(s2cr, gr0_base + ARM_SMMU_GR0_S2CR(idx));
 	}
 
 	return 0;
 }
 
-static void arm_smmu_domain_remove_master(struct arm_smmu_domain *smmu_domain,
-					  struct arm_smmu_master_cfg *cfg)
+static void arm_smmu_domain_remove_master(struct arm_smmu_master_cfg *cfg)
 {
 	int i;
-	struct arm_smmu_device *smmu = smmu_domain->smmu;
-	void __iomem *gr0_base = ARM_SMMU_GR0(smmu);
-
-	/* An IOMMU group is torn down by the first device to be removed */
-	if ((smmu->features & ARM_SMMU_FEAT_STREAM_MATCH) && !cfg->smrs)
-		return;
+	void __iomem *gr0_base = ARM_SMMU_GR0(cfg->smmu);
 
 	/*
 	 * We *must* clear the S2CR first, because freeing the SMR means
 	 * that it can be re-allocated immediately.
 	 */
 	for (i = 0; i < cfg->num_streamids; ++i) {
-		u32 idx = cfg->smrs ? cfg->smrs[i].idx : cfg->streamids[i];
+		u32 idx = cfg->streamids[i].s2cr_idx - 1;
 		u32 reg = disable_bypass ? S2CR_TYPE_FAULT : S2CR_TYPE_BYPASS;
 
 		writel_relaxed(reg, gr0_base + ARM_SMMU_GR0_S2CR(idx));
 	}
 
-	arm_smmu_master_free_smrs(smmu, cfg);
-}
-
-static void arm_smmu_detach_dev(struct device *dev,
-				struct arm_smmu_master_cfg *cfg)
-{
-	struct iommu_domain *domain = dev->archdata.iommu;
-	struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
-
-	dev->archdata.iommu = NULL;
-	arm_smmu_domain_remove_master(smmu_domain, cfg);
+	cfg->smmu_domain = NULL;
+	arm_smmu_master_free_smrs(cfg);
 }
 
 static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
@@ -1152,14 +1145,14 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
 	int ret;
 	struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
 	struct arm_smmu_device *smmu;
-	struct arm_smmu_master_cfg *cfg;
+	struct arm_smmu_master_cfg *cfg = dev->archdata.iommu;
 
-	smmu = find_smmu_for_device(dev);
-	if (!smmu) {
+	if (!cfg) {
 		dev_err(dev, "cannot attach to SMMU, is it on the same bus?\n");
 		return -ENXIO;
 	}
 
+	smmu = cfg->smmu;
 	/* Ensure that the domain is finalised */
 	ret = arm_smmu_init_domain_context(domain, smmu);
 	if (IS_ERR_VALUE(ret))
@@ -1176,18 +1169,13 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
 		return -EINVAL;
 	}
 
-	/* Looks ok, so add the device to the domain */
-	cfg = find_smmu_master_cfg(dev);
-	if (!cfg)
-		return -ENODEV;
-
 	/* Detach the dev from its current domain */
-	if (dev->archdata.iommu)
-		arm_smmu_detach_dev(dev, cfg);
+	if (cfg->smmu_domain)
+		arm_smmu_domain_remove_master(cfg);
 
 	ret = arm_smmu_domain_add_master(smmu_domain, cfg);
 	if (!ret)
-		dev->archdata.iommu = domain;
+		cfg->smmu_domain = smmu_domain;
 	return ret;
 }
 
@@ -1315,61 +1303,37 @@ static int __arm_smmu_get_pci_sid(struct pci_dev *pdev, u16 alias, void *data)
 	return 0; /* Continue walking */
 }
 
-static void __arm_smmu_release_pci_iommudata(void *data)
-{
-	kfree(data);
-}
-
-static int arm_smmu_init_pci_device(struct pci_dev *pdev,
-				    struct iommu_group *group)
+static int arm_smmu_init_pci_device(struct arm_smmu_device *smmu,
+				    struct pci_dev *pdev)
 {
 	struct arm_smmu_master_cfg *cfg;
 	u16 sid;
-	int i;
-
-	cfg = iommu_group_get_iommudata(group);
-	if (!cfg) {
-		cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
-		if (!cfg)
-			return -ENOMEM;
-
-		iommu_group_set_iommudata(group, cfg,
-					  __arm_smmu_release_pci_iommudata);
-	}
-
-	if (cfg->num_streamids >= MAX_MASTER_STREAMIDS)
-		return -ENOSPC;
 
+	cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
+	if (!cfg)
+		return -ENOMEM;
 	/*
 	 * Assume Stream ID == Requester ID for now.
 	 * We need a way to describe the ID mappings in FDT.
 	 */
 	pci_for_each_dma_alias(pdev, __arm_smmu_get_pci_sid, &sid);
-	for (i = 0; i < cfg->num_streamids; ++i)
-		if (cfg->streamids[i] == sid)
-			break;
 
-	/* Avoid duplicate SIDs, as this can lead to SMR conflicts */
-	if (i == cfg->num_streamids)
-		cfg->streamids[cfg->num_streamids++] = sid;
+	cfg->streamids[0].id = sid;
+	cfg->num_streamids = 1;
+
+	cfg->smmu = smmu;
+	pdev->dev.archdata.iommu = cfg;
 
 	return 0;
 }
 
-static int arm_smmu_init_platform_device(struct device *dev,
-					 struct iommu_group *group)
+static int arm_smmu_init_platform_device(struct arm_smmu_device *smmu,
+					 struct device *dev)
 {
-	struct arm_smmu_device *smmu = find_smmu_for_device(dev);
 	struct arm_smmu_master *master;
 
-	if (!smmu)
-		return -ENODEV;
-
-	master = find_smmu_master(smmu, dev->of_node);
-	if (!master)
-		return -ENODEV;
-
-	iommu_group_set_iommudata(group, &master->cfg, NULL);
+	master = find_smmu_master(smmu, dev_get_dev_node(dev));
+	dev->archdata.iommu = &master->cfg;
 
 	return 0;
 }
@@ -1377,6 +1341,22 @@ static int arm_smmu_init_platform_device(struct device *dev,
 static int arm_smmu_add_device(struct device *dev)
 {
 	struct iommu_group *group;
+	struct arm_smmu_device *smmu;
+	int ret;
+
+	if (dev->archdata.iommu)
+		return -EEXIST;
+
+	smmu = find_smmu_for_device(dev);
+	if (!smmu)
+		return -ENODEV;
+
+	if (dev_is_pci(dev))
+		ret = arm_smmu_init_pci_device(smmu, to_pci_dev(dev));
+	else
+		ret = arm_smmu_init_platform_device(smmu, dev);
+	if (ret)
+		return ret;
 
 	group = iommu_group_get_for_dev(dev);
 	if (IS_ERR(group))
@@ -1389,12 +1369,30 @@ static int arm_smmu_add_device(struct device *dev)
 static void arm_smmu_remove_device(struct device *dev)
 {
 	iommu_group_remove_device(dev);
+	if (dev_is_pci(dev))
+		kfree(dev->archdata.iommu);
+}
+
+static void __arm_smmu_release_iommudata(void *data)
+{
+	kfree(data);
+}
+
+static inline bool __streamid_match_sme(struct arm_smmu_streamid *sid,
+					struct arm_smmu_stream_map_entry *sme)
+{
+	/* This will get rather more complicated with masking... */
+	return sid->id == sme->id;
 }
 
 static struct iommu_group *arm_smmu_device_group(struct device *dev)
 {
+	struct arm_smmu_master_cfg *master_cfg;
+	struct arm_smmu_group_cfg *group_cfg;
+	struct arm_smmu_streamid *sids;
+	struct arm_smmu_stream_map_entry *smes;
 	struct iommu_group *group;
-	int ret;
+	int i, j;
 
 	if (dev_is_pci(dev))
 		group = pci_device_group(dev);
@@ -1404,14 +1402,37 @@ static struct iommu_group *arm_smmu_device_group(struct device *dev)
 	if (IS_ERR(group))
 		return group;
 
-	if (dev_is_pci(dev))
-		ret = arm_smmu_init_pci_device(to_pci_dev(dev), group);
-	else
-		ret = arm_smmu_init_platform_device(dev, group);
+	master_cfg = dev->archdata.iommu;
+	group_cfg = iommu_group_get_iommudata(group);
+	if (!group_cfg) {
+		group_cfg = kzalloc(sizeof(*group_cfg), GFP_KERNEL);
+		if (!group_cfg)
+			return ERR_PTR(-ENOMEM);
 
-	if (ret) {
-		iommu_group_put(group);
-		group = ERR_PTR(ret);
+		iommu_group_set_iommudata(group, group_cfg,
+					  __arm_smmu_release_iommudata);
+	}
+
+	/* Propagate device's IDs to the group, avoiding duplicate entries */
+	sids = master_cfg->streamids;
+	smes = group_cfg->smes;
+	for (i = 0; i < master_cfg->num_streamids; i++) {
+		for (j = 0; j < group_cfg->num_smes; j++) {
+			if (__streamid_match_sme(&sids[i], &smes[j])) {
+				sids[i].s2cr_idx = STREAM_MULTIPLE;
+				break;
+			}
+		}
+
+		if (j < group_cfg->num_smes)
+			continue;
+
+		if (group_cfg->num_smes == MAX_MASTER_STREAMIDS) {
+			iommu_group_put(group);
+			return ERR_PTR(-ENOSPC);
+		}
+
+		smes[group_cfg->num_smes++].id = sids[i].id;
 	}
 
 	return group;
-- 
2.7.2.333.g70bd996.dirty

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 09/12] iommu/arm-smmu: Simplify mmu-masters handling
  2016-02-29 13:46 ` Robin Murphy
@ 2016-02-29 13:46     ` Robin Murphy
  -1 siblings, 0 replies; 38+ messages in thread
From: Robin Murphy @ 2016-02-29 13:46 UTC (permalink / raw)
  To: iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA
  Cc: will.deacon-5wv7dgnIgG8, Suravee.Suthikulpanit-5C7GfCeVMHo,
	Thomas.Lendacky-5C7GfCeVMHo, stuart.yoder-3arQi8VN3Tc,
	tchalamarla-M3mlKVOIwJVv6pq1l3V1OdBPR1lH4CV8,
	anup.patel-dY08KVG/lbpWk0Htik3J/w,
	thunder.leizhen-hv44wF8Li93QT0dZR+AlfA

With the preceding data reshuffles allowing SMMU instances to be looked
up directly wherever needed, we can now wipe out the last vestiges of
the global SMMU list. With mmu-masters lookup now a one-time thing
outside of any real hotpath, condense our stash of those out of the SMMU
instance data down to a single shared list, where they may await their
eventual deprecation.

This also seems like a good opportunity to move the parts only relevant
to the probe/remove routines down to live in that area of the code.

Signed-off-by: Robin Murphy <robin.murphy-5wv7dgnIgG8@public.gmane.org>
---
 drivers/iommu/arm-smmu.c | 231 +++++++++++++++++++----------------------------
 1 file changed, 91 insertions(+), 140 deletions(-)

diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index 5eaa8cf..91b0a1b 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -301,7 +301,7 @@ struct arm_smmu_group_cfg {
 
 struct arm_smmu_master {
 	struct device_node		*of_node;
-	struct rb_node			node;
+	struct list_head		list;
 	struct arm_smmu_master_cfg	cfg;
 };
 
@@ -372,8 +372,8 @@ struct arm_smmu_domain {
 
 static struct iommu_ops arm_smmu_ops;
 
-static DEFINE_SPINLOCK(arm_smmu_devices_lock);
-static LIST_HEAD(arm_smmu_devices);
+static DEFINE_RWLOCK(arm_smmu_masters_lock);
+static LIST_HEAD(arm_smmu_masters);
 
 struct arm_smmu_option_prop {
 	u32 opt;
@@ -417,111 +417,20 @@ static struct device_node *dev_get_dev_node(struct device *dev)
 	return dev->of_node;
 }
 
-static struct arm_smmu_master *find_smmu_master(struct arm_smmu_device *smmu,
-						struct device_node *dev_node)
+static struct arm_smmu_master *find_smmu_master(struct device_node *dev_node)
 {
-	struct rb_node *node = smmu->masters.rb_node;
-
-	while (node) {
-		struct arm_smmu_master *master;
-
-		master = container_of(node, struct arm_smmu_master, node);
-
-		if (dev_node < master->of_node)
-			node = node->rb_left;
-		else if (dev_node > master->of_node)
-			node = node->rb_right;
-		else
-			return master;
-	}
-
-	return NULL;
-}
-
-static int insert_smmu_master(struct arm_smmu_device *smmu,
-			      struct arm_smmu_master *master)
-{
-	struct rb_node **new, *parent;
-
-	new = &smmu->masters.rb_node;
-	parent = NULL;
-	while (*new) {
-		struct arm_smmu_master *this
-			= container_of(*new, struct arm_smmu_master, node);
-
-		parent = *new;
-		if (master->of_node < this->of_node)
-			new = &((*new)->rb_left);
-		else if (master->of_node > this->of_node)
-			new = &((*new)->rb_right);
-		else
-			return -EEXIST;
-	}
-
-	rb_link_node(&master->node, parent, new);
-	rb_insert_color(&master->node, &smmu->masters);
-	return 0;
-}
-
-static int register_smmu_master(struct arm_smmu_device *smmu,
-				struct of_phandle_args *masterspec)
-{
-	int i;
 	struct arm_smmu_master *master;
 
-	master = find_smmu_master(smmu, masterspec->np);
-	if (master) {
-		dev_err(smmu->dev,
-			"rejecting multiple registrations for master device %s\n",
-			masterspec->np->name);
-		return -EBUSY;
-	}
+	read_lock(&arm_smmu_masters_lock);
+	list_for_each_entry(master, &arm_smmu_masters, list)
+		if (master->of_node == dev_node)
+			goto out_unlock;
 
-	if (masterspec->args_count > MAX_MASTER_STREAMIDS) {
-		dev_err(smmu->dev,
-			"reached maximum number (%d) of stream IDs for master device %s\n",
-			MAX_MASTER_STREAMIDS, masterspec->np->name);
-		return -ENOSPC;
-	}
+	master = NULL;
 
-	master = devm_kzalloc(smmu->dev, sizeof(*master), GFP_KERNEL);
-	if (!master)
-		return -ENOMEM;
-
-	master->of_node			= masterspec->np;
-	master->cfg.num_streamids	= masterspec->args_count;
-
-	for (i = 0; i < master->cfg.num_streamids; ++i) {
-		u16 streamid = masterspec->args[i];
-
-		if (!(smmu->features & ARM_SMMU_FEAT_STREAM_MATCH) &&
-		     (streamid >= smmu->num_mapping_groups)) {
-			dev_err(smmu->dev,
-				"stream ID for master device %s greater than maximum allowed (%d)\n",
-				masterspec->np->name, smmu->num_mapping_groups);
-			return -ERANGE;
-		}
-		master->cfg.streamids[i].id = streamid;
-		/* leave .mask 0; we don't currently share SMRs */
-	}
-	return insert_smmu_master(smmu, master);
-}
-
-static struct arm_smmu_device *find_smmu_for_device(struct device *dev)
-{
-	struct arm_smmu_device *smmu;
-	struct arm_smmu_master *master = NULL;
-	struct device_node *dev_node = dev_get_dev_node(dev);
-
-	spin_lock(&arm_smmu_devices_lock);
-	list_for_each_entry(smmu, &arm_smmu_devices, list) {
-		master = find_smmu_master(smmu, dev_node);
-		if (master)
-			break;
-	}
-	spin_unlock(&arm_smmu_devices_lock);
-
-	return master ? smmu : NULL;
+out_unlock:
+	read_unlock(&arm_smmu_masters_lock);
+	return master;
 }
 
 static int __arm_smmu_alloc_bitmap(unsigned long *map, int start, int end)
@@ -1327,36 +1236,26 @@ static int arm_smmu_init_pci_device(struct arm_smmu_device *smmu,
 	return 0;
 }
 
-static int arm_smmu_init_platform_device(struct arm_smmu_device *smmu,
-					 struct device *dev)
-{
-	struct arm_smmu_master *master;
-
-	master = find_smmu_master(smmu, dev_get_dev_node(dev));
-	dev->archdata.iommu = &master->cfg;
-
-	return 0;
-}
-
 static int arm_smmu_add_device(struct device *dev)
 {
 	struct iommu_group *group;
-	struct arm_smmu_device *smmu;
-	int ret;
+	struct arm_smmu_master *master;
 
 	if (dev->archdata.iommu)
 		return -EEXIST;
 
-	smmu = find_smmu_for_device(dev);
-	if (!smmu)
+	master = find_smmu_master(dev_get_dev_node(dev));
+	if (!master)
 		return -ENODEV;
 
-	if (dev_is_pci(dev))
-		ret = arm_smmu_init_pci_device(smmu, to_pci_dev(dev));
-	else
-		ret = arm_smmu_init_platform_device(smmu, dev);
-	if (ret)
-		return ret;
+	if (dev_is_pci(dev)) {
+		int ret = arm_smmu_init_pci_device(master->cfg.smmu,
+						   to_pci_dev(dev));
+		if (ret)
+			return ret;
+	} else {
+		dev->archdata.iommu = &master->cfg;
+	}
 
 	group = iommu_group_get_for_dev(dev);
 	if (IS_ERR(group))
@@ -1752,19 +1651,79 @@ MODULE_DEVICE_TABLE(of, arm_smmu_of_match);
 
 static void arm_smmu_remove_mmu_masters(struct arm_smmu_device *smmu)
 {
-	struct arm_smmu_master *master;
-	struct rb_node *node, *tmp;
+	struct arm_smmu_master *master, *tmp;
 
-	node = rb_first(&smmu->masters);
-	while (node) {
-		tmp = node;
-		node = rb_next(node);
-		master = container_of(tmp, struct arm_smmu_master, node);
+	write_lock(&arm_smmu_masters_lock);
+	list_for_each_entry_safe(master, tmp, &arm_smmu_masters, list) {
+		if (master->cfg.smmu != smmu)
+			continue;
 
-		rb_erase(tmp, &smmu->masters);
+		list_del(&master->list);
 		of_node_put(master->of_node);
 		devm_kfree(smmu->dev, master);
 	}
+	write_unlock(&arm_smmu_masters_lock);
+}
+
+static int insert_smmu_master(struct arm_smmu_master *master)
+{
+	struct arm_smmu_master *tmp;
+	int ret = -EEXIST;
+
+	write_lock(&arm_smmu_masters_lock);
+	list_for_each_entry(tmp, &arm_smmu_masters, list)
+		if (tmp->of_node == master->of_node)
+			goto out_unlock;
+
+	ret = 0;
+	list_add(&master->list, &arm_smmu_masters);
+out_unlock:
+	write_unlock(&arm_smmu_masters_lock);
+	return ret;
+}
+
+static int register_smmu_master(struct arm_smmu_device *smmu,
+				struct of_phandle_args *masterspec)
+{
+	int i;
+	struct arm_smmu_master *master;
+
+	master = find_smmu_master(masterspec->np);
+	if (master) {
+		dev_err(smmu->dev,
+			"rejecting multiple registrations for master device %s\n",
+			masterspec->np->name);
+		return -EBUSY;
+	}
+
+	if (masterspec->args_count > MAX_MASTER_STREAMIDS) {
+		dev_err(smmu->dev,
+			"reached maximum number (%d) of stream IDs for master device %s\n",
+			MAX_MASTER_STREAMIDS, masterspec->np->name);
+		return -ENOSPC;
+	}
+
+	master = devm_kzalloc(smmu->dev, sizeof(*master), GFP_KERNEL);
+	if (!master)
+		return -ENOMEM;
+
+	master->of_node			= masterspec->np;
+	master->cfg.num_streamids	= masterspec->args_count;
+
+	for (i = 0; i < master->cfg.num_streamids; ++i) {
+		u16 streamid = masterspec->args[i];
+
+		if (!(smmu->features & ARM_SMMU_FEAT_STREAM_MATCH) &&
+		     (streamid >= smmu->num_mapping_groups)) {
+			dev_err(smmu->dev,
+				"stream ID for master device %s greater than maximum allowed (%d)\n",
+				masterspec->np->name, smmu->num_mapping_groups);
+			return -ERANGE;
+		}
+		master->cfg.streamids[i].id = streamid;
+		/* leave .mask 0; we don't currently share SMRs */
+	}
+	return insert_smmu_master(master);
 }
 
 static int arm_smmu_probe_mmu_masters(struct arm_smmu_device *smmu)
@@ -1880,11 +1839,6 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
 
 	platform_set_drvdata(pdev, smmu);
 	arm_smmu_probe_mmu_masters(smmu);
-	INIT_LIST_HEAD(&smmu->list);
-	spin_lock(&arm_smmu_devices_lock);
-	list_add(&smmu->list, &arm_smmu_devices);
-	spin_unlock(&arm_smmu_devices_lock);
-
 	arm_smmu_device_reset(smmu);
 	return 0;
 
@@ -1903,9 +1857,6 @@ static int arm_smmu_device_remove(struct platform_device *pdev)
 		return -ENODEV;
 
 	arm_smmu_remove_mmu_masters(smmu);
-	spin_lock(&arm_smmu_devices_lock);
-	list_del(&smmu->list);
-	spin_unlock(&arm_smmu_devices_lock);
 
 	if (!bitmap_empty(smmu->context_map, ARM_SMMU_MAX_CBS))
 		dev_err(&pdev->dev, "removing device with active domains!\n");
-- 
2.7.2.333.g70bd996.dirty

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

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 09/12] iommu/arm-smmu: Simplify mmu-masters handling
@ 2016-02-29 13:46     ` Robin Murphy
  0 siblings, 0 replies; 38+ messages in thread
From: Robin Murphy @ 2016-02-29 13:46 UTC (permalink / raw)
  To: linux-arm-kernel

With the preceding data reshuffles allowing SMMU instances to be looked
up directly wherever needed, we can now wipe out the last vestiges of
the global SMMU list. With mmu-masters lookup now a one-time thing
outside of any real hotpath, condense our stash of those out of the SMMU
instance data down to a single shared list, where they may await their
eventual deprecation.

This also seems like a good opportunity to move the parts only relevant
to the probe/remove routines down to live in that area of the code.

Signed-off-by: Robin Murphy <robin.murphy@arm.com>
---
 drivers/iommu/arm-smmu.c | 231 +++++++++++++++++++----------------------------
 1 file changed, 91 insertions(+), 140 deletions(-)

diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index 5eaa8cf..91b0a1b 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -301,7 +301,7 @@ struct arm_smmu_group_cfg {
 
 struct arm_smmu_master {
 	struct device_node		*of_node;
-	struct rb_node			node;
+	struct list_head		list;
 	struct arm_smmu_master_cfg	cfg;
 };
 
@@ -372,8 +372,8 @@ struct arm_smmu_domain {
 
 static struct iommu_ops arm_smmu_ops;
 
-static DEFINE_SPINLOCK(arm_smmu_devices_lock);
-static LIST_HEAD(arm_smmu_devices);
+static DEFINE_RWLOCK(arm_smmu_masters_lock);
+static LIST_HEAD(arm_smmu_masters);
 
 struct arm_smmu_option_prop {
 	u32 opt;
@@ -417,111 +417,20 @@ static struct device_node *dev_get_dev_node(struct device *dev)
 	return dev->of_node;
 }
 
-static struct arm_smmu_master *find_smmu_master(struct arm_smmu_device *smmu,
-						struct device_node *dev_node)
+static struct arm_smmu_master *find_smmu_master(struct device_node *dev_node)
 {
-	struct rb_node *node = smmu->masters.rb_node;
-
-	while (node) {
-		struct arm_smmu_master *master;
-
-		master = container_of(node, struct arm_smmu_master, node);
-
-		if (dev_node < master->of_node)
-			node = node->rb_left;
-		else if (dev_node > master->of_node)
-			node = node->rb_right;
-		else
-			return master;
-	}
-
-	return NULL;
-}
-
-static int insert_smmu_master(struct arm_smmu_device *smmu,
-			      struct arm_smmu_master *master)
-{
-	struct rb_node **new, *parent;
-
-	new = &smmu->masters.rb_node;
-	parent = NULL;
-	while (*new) {
-		struct arm_smmu_master *this
-			= container_of(*new, struct arm_smmu_master, node);
-
-		parent = *new;
-		if (master->of_node < this->of_node)
-			new = &((*new)->rb_left);
-		else if (master->of_node > this->of_node)
-			new = &((*new)->rb_right);
-		else
-			return -EEXIST;
-	}
-
-	rb_link_node(&master->node, parent, new);
-	rb_insert_color(&master->node, &smmu->masters);
-	return 0;
-}
-
-static int register_smmu_master(struct arm_smmu_device *smmu,
-				struct of_phandle_args *masterspec)
-{
-	int i;
 	struct arm_smmu_master *master;
 
-	master = find_smmu_master(smmu, masterspec->np);
-	if (master) {
-		dev_err(smmu->dev,
-			"rejecting multiple registrations for master device %s\n",
-			masterspec->np->name);
-		return -EBUSY;
-	}
+	read_lock(&arm_smmu_masters_lock);
+	list_for_each_entry(master, &arm_smmu_masters, list)
+		if (master->of_node == dev_node)
+			goto out_unlock;
 
-	if (masterspec->args_count > MAX_MASTER_STREAMIDS) {
-		dev_err(smmu->dev,
-			"reached maximum number (%d) of stream IDs for master device %s\n",
-			MAX_MASTER_STREAMIDS, masterspec->np->name);
-		return -ENOSPC;
-	}
+	master = NULL;
 
-	master = devm_kzalloc(smmu->dev, sizeof(*master), GFP_KERNEL);
-	if (!master)
-		return -ENOMEM;
-
-	master->of_node			= masterspec->np;
-	master->cfg.num_streamids	= masterspec->args_count;
-
-	for (i = 0; i < master->cfg.num_streamids; ++i) {
-		u16 streamid = masterspec->args[i];
-
-		if (!(smmu->features & ARM_SMMU_FEAT_STREAM_MATCH) &&
-		     (streamid >= smmu->num_mapping_groups)) {
-			dev_err(smmu->dev,
-				"stream ID for master device %s greater than maximum allowed (%d)\n",
-				masterspec->np->name, smmu->num_mapping_groups);
-			return -ERANGE;
-		}
-		master->cfg.streamids[i].id = streamid;
-		/* leave .mask 0; we don't currently share SMRs */
-	}
-	return insert_smmu_master(smmu, master);
-}
-
-static struct arm_smmu_device *find_smmu_for_device(struct device *dev)
-{
-	struct arm_smmu_device *smmu;
-	struct arm_smmu_master *master = NULL;
-	struct device_node *dev_node = dev_get_dev_node(dev);
-
-	spin_lock(&arm_smmu_devices_lock);
-	list_for_each_entry(smmu, &arm_smmu_devices, list) {
-		master = find_smmu_master(smmu, dev_node);
-		if (master)
-			break;
-	}
-	spin_unlock(&arm_smmu_devices_lock);
-
-	return master ? smmu : NULL;
+out_unlock:
+	read_unlock(&arm_smmu_masters_lock);
+	return master;
 }
 
 static int __arm_smmu_alloc_bitmap(unsigned long *map, int start, int end)
@@ -1327,36 +1236,26 @@ static int arm_smmu_init_pci_device(struct arm_smmu_device *smmu,
 	return 0;
 }
 
-static int arm_smmu_init_platform_device(struct arm_smmu_device *smmu,
-					 struct device *dev)
-{
-	struct arm_smmu_master *master;
-
-	master = find_smmu_master(smmu, dev_get_dev_node(dev));
-	dev->archdata.iommu = &master->cfg;
-
-	return 0;
-}
-
 static int arm_smmu_add_device(struct device *dev)
 {
 	struct iommu_group *group;
-	struct arm_smmu_device *smmu;
-	int ret;
+	struct arm_smmu_master *master;
 
 	if (dev->archdata.iommu)
 		return -EEXIST;
 
-	smmu = find_smmu_for_device(dev);
-	if (!smmu)
+	master = find_smmu_master(dev_get_dev_node(dev));
+	if (!master)
 		return -ENODEV;
 
-	if (dev_is_pci(dev))
-		ret = arm_smmu_init_pci_device(smmu, to_pci_dev(dev));
-	else
-		ret = arm_smmu_init_platform_device(smmu, dev);
-	if (ret)
-		return ret;
+	if (dev_is_pci(dev)) {
+		int ret = arm_smmu_init_pci_device(master->cfg.smmu,
+						   to_pci_dev(dev));
+		if (ret)
+			return ret;
+	} else {
+		dev->archdata.iommu = &master->cfg;
+	}
 
 	group = iommu_group_get_for_dev(dev);
 	if (IS_ERR(group))
@@ -1752,19 +1651,79 @@ MODULE_DEVICE_TABLE(of, arm_smmu_of_match);
 
 static void arm_smmu_remove_mmu_masters(struct arm_smmu_device *smmu)
 {
-	struct arm_smmu_master *master;
-	struct rb_node *node, *tmp;
+	struct arm_smmu_master *master, *tmp;
 
-	node = rb_first(&smmu->masters);
-	while (node) {
-		tmp = node;
-		node = rb_next(node);
-		master = container_of(tmp, struct arm_smmu_master, node);
+	write_lock(&arm_smmu_masters_lock);
+	list_for_each_entry_safe(master, tmp, &arm_smmu_masters, list) {
+		if (master->cfg.smmu != smmu)
+			continue;
 
-		rb_erase(tmp, &smmu->masters);
+		list_del(&master->list);
 		of_node_put(master->of_node);
 		devm_kfree(smmu->dev, master);
 	}
+	write_unlock(&arm_smmu_masters_lock);
+}
+
+static int insert_smmu_master(struct arm_smmu_master *master)
+{
+	struct arm_smmu_master *tmp;
+	int ret = -EEXIST;
+
+	write_lock(&arm_smmu_masters_lock);
+	list_for_each_entry(tmp, &arm_smmu_masters, list)
+		if (tmp->of_node == master->of_node)
+			goto out_unlock;
+
+	ret = 0;
+	list_add(&master->list, &arm_smmu_masters);
+out_unlock:
+	write_unlock(&arm_smmu_masters_lock);
+	return ret;
+}
+
+static int register_smmu_master(struct arm_smmu_device *smmu,
+				struct of_phandle_args *masterspec)
+{
+	int i;
+	struct arm_smmu_master *master;
+
+	master = find_smmu_master(masterspec->np);
+	if (master) {
+		dev_err(smmu->dev,
+			"rejecting multiple registrations for master device %s\n",
+			masterspec->np->name);
+		return -EBUSY;
+	}
+
+	if (masterspec->args_count > MAX_MASTER_STREAMIDS) {
+		dev_err(smmu->dev,
+			"reached maximum number (%d) of stream IDs for master device %s\n",
+			MAX_MASTER_STREAMIDS, masterspec->np->name);
+		return -ENOSPC;
+	}
+
+	master = devm_kzalloc(smmu->dev, sizeof(*master), GFP_KERNEL);
+	if (!master)
+		return -ENOMEM;
+
+	master->of_node			= masterspec->np;
+	master->cfg.num_streamids	= masterspec->args_count;
+
+	for (i = 0; i < master->cfg.num_streamids; ++i) {
+		u16 streamid = masterspec->args[i];
+
+		if (!(smmu->features & ARM_SMMU_FEAT_STREAM_MATCH) &&
+		     (streamid >= smmu->num_mapping_groups)) {
+			dev_err(smmu->dev,
+				"stream ID for master device %s greater than maximum allowed (%d)\n",
+				masterspec->np->name, smmu->num_mapping_groups);
+			return -ERANGE;
+		}
+		master->cfg.streamids[i].id = streamid;
+		/* leave .mask 0; we don't currently share SMRs */
+	}
+	return insert_smmu_master(master);
 }
 
 static int arm_smmu_probe_mmu_masters(struct arm_smmu_device *smmu)
@@ -1880,11 +1839,6 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
 
 	platform_set_drvdata(pdev, smmu);
 	arm_smmu_probe_mmu_masters(smmu);
-	INIT_LIST_HEAD(&smmu->list);
-	spin_lock(&arm_smmu_devices_lock);
-	list_add(&smmu->list, &arm_smmu_devices);
-	spin_unlock(&arm_smmu_devices_lock);
-
 	arm_smmu_device_reset(smmu);
 	return 0;
 
@@ -1903,9 +1857,6 @@ static int arm_smmu_device_remove(struct platform_device *pdev)
 		return -ENODEV;
 
 	arm_smmu_remove_mmu_masters(smmu);
-	spin_lock(&arm_smmu_devices_lock);
-	list_del(&smmu->list);
-	spin_unlock(&arm_smmu_devices_lock);
 
 	if (!bitmap_empty(smmu->context_map, ARM_SMMU_MAX_CBS))
 		dev_err(&pdev->dev, "removing device with active domains!\n");
-- 
2.7.2.333.g70bd996.dirty

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 10/12] Docs: dt: document ARM SMMU generic binding usage
  2016-02-29 13:46 ` Robin Murphy
@ 2016-02-29 13:46     ` Robin Murphy
  -1 siblings, 0 replies; 38+ messages in thread
From: Robin Murphy @ 2016-02-29 13:46 UTC (permalink / raw)
  To: iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA
  Cc: will.deacon-5wv7dgnIgG8, Suravee.Suthikulpanit-5C7GfCeVMHo,
	Thomas.Lendacky-5C7GfCeVMHo, stuart.yoder-3arQi8VN3Tc,
	tchalamarla-M3mlKVOIwJVv6pq1l3V1OdBPR1lH4CV8,
	anup.patel-dY08KVG/lbpWk0Htik3J/w,
	thunder.leizhen-hv44wF8Li93QT0dZR+AlfA

Document how the generic "iommus" binding should be used to describe ARM
SMMU stream IDs instead of the old "mmu-masters" binding.

Signed-off-by: Robin Murphy <robin.murphy-5wv7dgnIgG8@public.gmane.org>
---
 .../devicetree/bindings/iommu/arm,smmu.txt         | 40 ++++++++++++++--------
 1 file changed, 25 insertions(+), 15 deletions(-)

diff --git a/Documentation/devicetree/bindings/iommu/arm,smmu.txt b/Documentation/devicetree/bindings/iommu/arm,smmu.txt
index 7180745..a213f74 100644
--- a/Documentation/devicetree/bindings/iommu/arm,smmu.txt
+++ b/Documentation/devicetree/bindings/iommu/arm,smmu.txt
@@ -34,12 +34,11 @@ conditions.
                   interrupt per context bank. In the case of a single,
                   combined interrupt, it must be listed multiple times.
 
-- mmu-masters   : A list of phandles to device nodes representing bus
-                  masters for which the SMMU can provide a translation
-                  and their corresponding StreamIDs (see example below).
-                  Each device node linked from this list must have a
-                  "#stream-id-cells" property, indicating the number of
-                  StreamIDs associated with it.
+- #iommu-cells  : See Documentation/devicetree/bindings/iommu/iommu.txt
+                  for details. Should be 1, where each "iommus" entry on
+                  the device represents a distinct stream ID emitted by
+                  that device into the relevant SMMU. Cells beyond 1 are
+                  reserved for future use.
 
 ** System MMU optional properties:
 
@@ -55,9 +54,19 @@ conditions.
                   aliases of secure registers have to be used during
                   SMMU configuration.
 
-Example:
+** Deprecated properties:
 
-        smmu {
+- mmu-masters (deprecated in favour of the generic "iommus" binding) :
+                  A list of phandles to device nodes representing bus
+                  masters for which the SMMU can provide a translation
+                  and their corresponding StreamIDs (see example below).
+                  Each device node linked from this list must have a
+                  "#stream-id-cells" property, indicating the number of
+                  StreamIDs associated with it.
+
+** Example:
+
+        smmu1: iommu {
                 compatible = "arm,smmu-v1";
                 reg = <0xba5e0000 0x10000>;
                 #global-interrupts = <2>;
@@ -67,11 +76,12 @@ Example:
                              <0 35 4>,
                              <0 36 4>,
                              <0 37 4>;
-
-                /*
-                 * Two DMA controllers, the first with two StreamIDs (0xd01d
-                 * and 0xd01e) and the second with only one (0xd11c).
-                 */
-                mmu-masters = <&dma0 0xd01d 0xd01e>,
-                              <&dma1 0xd11c>;
+                #iommu-cells = <1>;
         };
+
+        /* device with two stream IDs, 0 and 7 */
+        master {
+                iommus = <&smmu1 0>,
+                         <&smmu1 7>;
+        };
+
-- 
2.7.2.333.g70bd996.dirty

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

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 10/12] Docs: dt: document ARM SMMU generic binding usage
@ 2016-02-29 13:46     ` Robin Murphy
  0 siblings, 0 replies; 38+ messages in thread
From: Robin Murphy @ 2016-02-29 13:46 UTC (permalink / raw)
  To: linux-arm-kernel

Document how the generic "iommus" binding should be used to describe ARM
SMMU stream IDs instead of the old "mmu-masters" binding.

Signed-off-by: Robin Murphy <robin.murphy@arm.com>
---
 .../devicetree/bindings/iommu/arm,smmu.txt         | 40 ++++++++++++++--------
 1 file changed, 25 insertions(+), 15 deletions(-)

diff --git a/Documentation/devicetree/bindings/iommu/arm,smmu.txt b/Documentation/devicetree/bindings/iommu/arm,smmu.txt
index 7180745..a213f74 100644
--- a/Documentation/devicetree/bindings/iommu/arm,smmu.txt
+++ b/Documentation/devicetree/bindings/iommu/arm,smmu.txt
@@ -34,12 +34,11 @@ conditions.
                   interrupt per context bank. In the case of a single,
                   combined interrupt, it must be listed multiple times.
 
-- mmu-masters   : A list of phandles to device nodes representing bus
-                  masters for which the SMMU can provide a translation
-                  and their corresponding StreamIDs (see example below).
-                  Each device node linked from this list must have a
-                  "#stream-id-cells" property, indicating the number of
-                  StreamIDs associated with it.
+- #iommu-cells  : See Documentation/devicetree/bindings/iommu/iommu.txt
+                  for details. Should be 1, where each "iommus" entry on
+                  the device represents a distinct stream ID emitted by
+                  that device into the relevant SMMU. Cells beyond 1 are
+                  reserved for future use.
 
 ** System MMU optional properties:
 
@@ -55,9 +54,19 @@ conditions.
                   aliases of secure registers have to be used during
                   SMMU configuration.
 
-Example:
+** Deprecated properties:
 
-        smmu {
+- mmu-masters (deprecated in favour of the generic "iommus" binding) :
+                  A list of phandles to device nodes representing bus
+                  masters for which the SMMU can provide a translation
+                  and their corresponding StreamIDs (see example below).
+                  Each device node linked from this list must have a
+                  "#stream-id-cells" property, indicating the number of
+                  StreamIDs associated with it.
+
+** Example:
+
+        smmu1: iommu {
                 compatible = "arm,smmu-v1";
                 reg = <0xba5e0000 0x10000>;
                 #global-interrupts = <2>;
@@ -67,11 +76,12 @@ Example:
                              <0 35 4>,
                              <0 36 4>,
                              <0 37 4>;
-
-                /*
-                 * Two DMA controllers, the first with two StreamIDs (0xd01d
-                 * and 0xd01e) and the second with only one (0xd11c).
-                 */
-                mmu-masters = <&dma0 0xd01d 0xd01e>,
-                              <&dma1 0xd11c>;
+                #iommu-cells = <1>;
         };
+
+        /* device with two stream IDs, 0 and 7 */
+        master {
+                iommus = <&smmu1 0>,
+                         <&smmu1 7>;
+        };
+
-- 
2.7.2.333.g70bd996.dirty

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 11/12] iommu/arm-smmu: Generic IOMMU DT bindings support
  2016-02-29 13:46 ` Robin Murphy
@ 2016-02-29 13:46     ` Robin Murphy
  -1 siblings, 0 replies; 38+ messages in thread
From: Robin Murphy @ 2016-02-29 13:46 UTC (permalink / raw)
  To: iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA
  Cc: will.deacon-5wv7dgnIgG8, Suravee.Suthikulpanit-5C7GfCeVMHo,
	Thomas.Lendacky-5C7GfCeVMHo, stuart.yoder-3arQi8VN3Tc,
	tchalamarla-M3mlKVOIwJVv6pq1l3V1OdBPR1lH4CV8,
	anup.patel-dY08KVG/lbpWk0Htik3J/w,
	thunder.leizhen-hv44wF8Li93QT0dZR+AlfA

Implement an of_xlate callback and the appropriate registration so that
we can configure masters via generic DT bindings. Initially, we have the
equivalent level of functionality with respect to groups, stream ID
limits, etc. as for the old bindings, but the door is now open for
further improvements.

Since of_iommmu_configure() is not yet clever enough to enforce the
necessary probe ordering dependencies, employ the same explicit device
creation tactic as the Exynos IOMMU driver to ensure our actual SMMU
instance is up and running in time to handle of_xlate calls.

Signed-off-by: Robin Murphy <robin.murphy-5wv7dgnIgG8@public.gmane.org>
---
 drivers/iommu/arm-smmu.c | 144 ++++++++++++++++++++++++++++++++++-------------
 1 file changed, 106 insertions(+), 38 deletions(-)

diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index 91b0a1b..8fcf27a 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -39,6 +39,8 @@
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/of_address.h>
+#include <linux/of_iommu.h>
+#include <linux/of_platform.h>
 #include <linux/pci.h>
 #include <linux/platform_device.h>
 #include <linux/slab.h>
@@ -421,6 +423,9 @@ static struct arm_smmu_master *find_smmu_master(struct device_node *dev_node)
 {
 	struct arm_smmu_master *master;
 
+	if (!dev_node)
+		return NULL;
+
 	read_lock(&arm_smmu_masters_lock);
 	list_for_each_entry(master, &arm_smmu_masters, list)
 		if (master->of_node == dev_node)
@@ -1011,13 +1016,6 @@ static int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain,
 	if (ret)
 		return ret;
 
-	/*
-	 * FIXME: This won't be needed once we have IOMMU-backed DMA ops
-	 * for all devices behind the SMMU.
-	 */
-	if (smmu_domain->domain.type == IOMMU_DOMAIN_DMA)
-		return 0;
-
 	for (i = 0; i < cfg->num_streamids; ++i) {
 		u32 idx = cfg->streamids[i].s2cr_idx - 1;
 		u32 s2cr = S2CR_TYPE_TRANS | S2CR_PRIVCFG_UNPRIV |
@@ -1206,57 +1204,79 @@ static bool arm_smmu_capable(enum iommu_cap cap)
 	}
 }
 
+static int arm_smmu_add_dev_streamid(struct arm_smmu_device *smmu,
+				     struct device *dev, u16 sid)
+{
+	struct arm_smmu_master_cfg *cfg = dev->archdata.iommu;
+	int i;
+
+	if (!cfg) {
+		cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
+		if (!cfg)
+			return -ENOMEM;
+
+		cfg->smmu = smmu;
+		dev->archdata.iommu = cfg;
+	}
+
+	if (cfg->num_streamids >= MAX_MASTER_STREAMIDS)
+		return -ENOSPC;
+
+	/* Avoid duplicate SIDs, as this can lead to SMR conflicts */
+	for (i = 0; i < cfg->num_streamids; ++i)
+		if (cfg->streamids[i].id == sid) {
+			dev_warn(dev, "Stream ID 0x%hx repeated; ignoring\n",
+				 sid);
+			return 0;
+		}
+
+	cfg->streamids[cfg->num_streamids++].id = sid;
+
+	return 0;
+}
+
 static int __arm_smmu_get_pci_sid(struct pci_dev *pdev, u16 alias, void *data)
 {
 	*((u16 *)data) = alias;
 	return 0; /* Continue walking */
 }
 
-static int arm_smmu_init_pci_device(struct arm_smmu_device *smmu,
-				    struct pci_dev *pdev)
+static int arm_smmu_init_legacy_master(struct device *dev)
 {
-	struct arm_smmu_master_cfg *cfg;
+	struct arm_smmu_master *master;
+	struct device_node *np = dev_get_dev_node(dev);
 	u16 sid;
 
-	cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
-	if (!cfg)
-		return -ENOMEM;
-	/*
-	 * Assume Stream ID == Requester ID for now.
-	 * We need a way to describe the ID mappings in FDT.
-	 */
-	pci_for_each_dma_alias(pdev, __arm_smmu_get_pci_sid, &sid);
+	master = find_smmu_master(np);
+	if (!master)
+		return -ENODEV;
 
-	cfg->streamids[0].id = sid;
-	cfg->num_streamids = 1;
+	if (!dev_is_pci(dev)) {
+		dev->archdata.iommu = &master->cfg;
+		return 0;
+	}
 
-	cfg->smmu = smmu;
-	pdev->dev.archdata.iommu = cfg;
-
-	return 0;
+	/* Legacy bindings assume Stream ID == Requester ID */
+	pci_for_each_dma_alias(to_pci_dev(dev), __arm_smmu_get_pci_sid, &sid);
+	return arm_smmu_add_dev_streamid(master->cfg.smmu, dev, sid);
 }
 
 static int arm_smmu_add_device(struct device *dev)
 {
 	struct iommu_group *group;
-	struct arm_smmu_master *master;
 
-	if (dev->archdata.iommu)
-		return -EEXIST;
+	if (!dev->archdata.iommu) {
+		int ret = arm_smmu_init_legacy_master(dev);
 
-	master = find_smmu_master(dev_get_dev_node(dev));
-	if (!master)
-		return -ENODEV;
-
-	if (dev_is_pci(dev)) {
-		int ret = arm_smmu_init_pci_device(master->cfg.smmu,
-						   to_pci_dev(dev));
 		if (ret)
 			return ret;
-	} else {
-		dev->archdata.iommu = &master->cfg;
 	}
 
+	/*
+	 * For now, assume that the default group allocators suffice.
+	 * We might have to do some preparatory work here to properly
+	 * handle multiple devices sharing stream IDs.
+	 */
 	group = iommu_group_get_for_dev(dev);
 	if (IS_ERR(group))
 		return PTR_ERR(group);
@@ -1268,7 +1288,7 @@ static int arm_smmu_add_device(struct device *dev)
 static void arm_smmu_remove_device(struct device *dev)
 {
 	iommu_group_remove_device(dev);
-	if (dev_is_pci(dev))
+	if (!find_smmu_master(dev->of_node))
 		kfree(dev->archdata.iommu);
 }
 
@@ -1381,6 +1401,20 @@ out_unlock:
 	return ret;
 }
 
+static int arm_smmu_of_xlate(struct device *dev, struct of_phandle_args *args)
+{
+	struct arm_smmu_device *smmu;
+	struct platform_device *smmu_pdev;
+
+	smmu_pdev = of_find_device_by_node(args->np);
+	if (!smmu_pdev)
+		return -ENODEV;
+
+	smmu = platform_get_drvdata(smmu_pdev);
+
+	return arm_smmu_add_dev_streamid(smmu, dev, args->args[0]);
+}
+
 static struct iommu_ops arm_smmu_ops = {
 	.capable		= arm_smmu_capable,
 	.domain_alloc		= arm_smmu_domain_alloc,
@@ -1395,6 +1429,7 @@ static struct iommu_ops arm_smmu_ops = {
 	.device_group		= arm_smmu_device_group,
 	.domain_get_attr	= arm_smmu_domain_get_attr,
 	.domain_set_attr	= arm_smmu_domain_set_attr,
+	.of_xlate		= arm_smmu_of_xlate,
 	.pgsize_bitmap		= -1UL, /* Restricted during device attach */
 };
 
@@ -1731,6 +1766,9 @@ static int arm_smmu_probe_mmu_masters(struct arm_smmu_device *smmu)
 	struct of_phandle_args masterspec;
 	int err, i = 0;
 
+	dev_notice(smmu->dev,
+		   "Deprecated \"mmu-masters\" property found; update DT to \"iommus\" property if possible\n");
+
 	while (!of_parse_phandle_with_args(smmu->dev->of_node, "mmu-masters",
 			"#stream-id-cells", i,
 			&masterspec)) {
@@ -1838,7 +1876,9 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
 	}
 
 	platform_set_drvdata(pdev, smmu);
-	arm_smmu_probe_mmu_masters(smmu);
+	/* Check first to avoid of_parse_phandle_with_args complaining */
+	if (of_property_read_bool(dev->of_node, "mmu-masters"))
+		arm_smmu_probe_mmu_masters(smmu);
 	arm_smmu_device_reset(smmu);
 	return 0;
 
@@ -1881,8 +1921,11 @@ static struct platform_driver arm_smmu_driver = {
 static int __init arm_smmu_init(void)
 {
 	struct device_node *np;
+	static bool done;
 	int ret;
 
+	if (done)
+		return 0;
 	/*
 	 * Play nice with systems that don't have an ARM SMMU by checking that
 	 * an ARM SMMU exists in the system before proceeding with the driver
@@ -1912,6 +1955,7 @@ static int __init arm_smmu_init(void)
 		bus_set_iommu(&pci_bus_type, &arm_smmu_ops);
 #endif
 
+	done = true;
 	return 0;
 }
 
@@ -1923,6 +1967,30 @@ static void __exit arm_smmu_exit(void)
 subsys_initcall(arm_smmu_init);
 module_exit(arm_smmu_exit);
 
+static int __init arm_smmu_of_init(struct device_node *np)
+{
+	struct arm_smmu_device *smmu;
+	struct platform_device *pdev;
+	int ret = arm_smmu_init();
+
+	if (ret)
+		return ret;
+
+	pdev = of_platform_device_create(np, NULL, platform_bus_type.dev_root);
+	if (!pdev)
+		return -ENODEV;
+
+	smmu = platform_get_drvdata(pdev);
+	of_iommu_set_ops(np, &arm_smmu_ops);
+
+	return 0;
+}
+IOMMU_OF_DECLARE(arm_smmuv1, "arm,smmu-v1", arm_smmu_of_init);
+IOMMU_OF_DECLARE(arm_smmuv2, "arm,smmu-v2", arm_smmu_of_init);
+IOMMU_OF_DECLARE(arm_mmu400, "arm,mmu-400", arm_smmu_of_init);
+IOMMU_OF_DECLARE(arm_mmu401, "arm,mmu-401", arm_smmu_of_init);
+IOMMU_OF_DECLARE(arm_mmu500, "arm,mmu-500", arm_smmu_of_init);
+
 MODULE_DESCRIPTION("IOMMU API for ARM architected SMMU implementations");
 MODULE_AUTHOR("Will Deacon <will.deacon-5wv7dgnIgG8@public.gmane.org>");
 MODULE_LICENSE("GPL v2");
-- 
2.7.2.333.g70bd996.dirty

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

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 11/12] iommu/arm-smmu: Generic IOMMU DT bindings support
@ 2016-02-29 13:46     ` Robin Murphy
  0 siblings, 0 replies; 38+ messages in thread
From: Robin Murphy @ 2016-02-29 13:46 UTC (permalink / raw)
  To: linux-arm-kernel

Implement an of_xlate callback and the appropriate registration so that
we can configure masters via generic DT bindings. Initially, we have the
equivalent level of functionality with respect to groups, stream ID
limits, etc. as for the old bindings, but the door is now open for
further improvements.

Since of_iommmu_configure() is not yet clever enough to enforce the
necessary probe ordering dependencies, employ the same explicit device
creation tactic as the Exynos IOMMU driver to ensure our actual SMMU
instance is up and running in time to handle of_xlate calls.

Signed-off-by: Robin Murphy <robin.murphy@arm.com>
---
 drivers/iommu/arm-smmu.c | 144 ++++++++++++++++++++++++++++++++++-------------
 1 file changed, 106 insertions(+), 38 deletions(-)

diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index 91b0a1b..8fcf27a 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -39,6 +39,8 @@
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/of_address.h>
+#include <linux/of_iommu.h>
+#include <linux/of_platform.h>
 #include <linux/pci.h>
 #include <linux/platform_device.h>
 #include <linux/slab.h>
@@ -421,6 +423,9 @@ static struct arm_smmu_master *find_smmu_master(struct device_node *dev_node)
 {
 	struct arm_smmu_master *master;
 
+	if (!dev_node)
+		return NULL;
+
 	read_lock(&arm_smmu_masters_lock);
 	list_for_each_entry(master, &arm_smmu_masters, list)
 		if (master->of_node == dev_node)
@@ -1011,13 +1016,6 @@ static int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain,
 	if (ret)
 		return ret;
 
-	/*
-	 * FIXME: This won't be needed once we have IOMMU-backed DMA ops
-	 * for all devices behind the SMMU.
-	 */
-	if (smmu_domain->domain.type == IOMMU_DOMAIN_DMA)
-		return 0;
-
 	for (i = 0; i < cfg->num_streamids; ++i) {
 		u32 idx = cfg->streamids[i].s2cr_idx - 1;
 		u32 s2cr = S2CR_TYPE_TRANS | S2CR_PRIVCFG_UNPRIV |
@@ -1206,57 +1204,79 @@ static bool arm_smmu_capable(enum iommu_cap cap)
 	}
 }
 
+static int arm_smmu_add_dev_streamid(struct arm_smmu_device *smmu,
+				     struct device *dev, u16 sid)
+{
+	struct arm_smmu_master_cfg *cfg = dev->archdata.iommu;
+	int i;
+
+	if (!cfg) {
+		cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
+		if (!cfg)
+			return -ENOMEM;
+
+		cfg->smmu = smmu;
+		dev->archdata.iommu = cfg;
+	}
+
+	if (cfg->num_streamids >= MAX_MASTER_STREAMIDS)
+		return -ENOSPC;
+
+	/* Avoid duplicate SIDs, as this can lead to SMR conflicts */
+	for (i = 0; i < cfg->num_streamids; ++i)
+		if (cfg->streamids[i].id == sid) {
+			dev_warn(dev, "Stream ID 0x%hx repeated; ignoring\n",
+				 sid);
+			return 0;
+		}
+
+	cfg->streamids[cfg->num_streamids++].id = sid;
+
+	return 0;
+}
+
 static int __arm_smmu_get_pci_sid(struct pci_dev *pdev, u16 alias, void *data)
 {
 	*((u16 *)data) = alias;
 	return 0; /* Continue walking */
 }
 
-static int arm_smmu_init_pci_device(struct arm_smmu_device *smmu,
-				    struct pci_dev *pdev)
+static int arm_smmu_init_legacy_master(struct device *dev)
 {
-	struct arm_smmu_master_cfg *cfg;
+	struct arm_smmu_master *master;
+	struct device_node *np = dev_get_dev_node(dev);
 	u16 sid;
 
-	cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
-	if (!cfg)
-		return -ENOMEM;
-	/*
-	 * Assume Stream ID == Requester ID for now.
-	 * We need a way to describe the ID mappings in FDT.
-	 */
-	pci_for_each_dma_alias(pdev, __arm_smmu_get_pci_sid, &sid);
+	master = find_smmu_master(np);
+	if (!master)
+		return -ENODEV;
 
-	cfg->streamids[0].id = sid;
-	cfg->num_streamids = 1;
+	if (!dev_is_pci(dev)) {
+		dev->archdata.iommu = &master->cfg;
+		return 0;
+	}
 
-	cfg->smmu = smmu;
-	pdev->dev.archdata.iommu = cfg;
-
-	return 0;
+	/* Legacy bindings assume Stream ID == Requester ID */
+	pci_for_each_dma_alias(to_pci_dev(dev), __arm_smmu_get_pci_sid, &sid);
+	return arm_smmu_add_dev_streamid(master->cfg.smmu, dev, sid);
 }
 
 static int arm_smmu_add_device(struct device *dev)
 {
 	struct iommu_group *group;
-	struct arm_smmu_master *master;
 
-	if (dev->archdata.iommu)
-		return -EEXIST;
+	if (!dev->archdata.iommu) {
+		int ret = arm_smmu_init_legacy_master(dev);
 
-	master = find_smmu_master(dev_get_dev_node(dev));
-	if (!master)
-		return -ENODEV;
-
-	if (dev_is_pci(dev)) {
-		int ret = arm_smmu_init_pci_device(master->cfg.smmu,
-						   to_pci_dev(dev));
 		if (ret)
 			return ret;
-	} else {
-		dev->archdata.iommu = &master->cfg;
 	}
 
+	/*
+	 * For now, assume that the default group allocators suffice.
+	 * We might have to do some preparatory work here to properly
+	 * handle multiple devices sharing stream IDs.
+	 */
 	group = iommu_group_get_for_dev(dev);
 	if (IS_ERR(group))
 		return PTR_ERR(group);
@@ -1268,7 +1288,7 @@ static int arm_smmu_add_device(struct device *dev)
 static void arm_smmu_remove_device(struct device *dev)
 {
 	iommu_group_remove_device(dev);
-	if (dev_is_pci(dev))
+	if (!find_smmu_master(dev->of_node))
 		kfree(dev->archdata.iommu);
 }
 
@@ -1381,6 +1401,20 @@ out_unlock:
 	return ret;
 }
 
+static int arm_smmu_of_xlate(struct device *dev, struct of_phandle_args *args)
+{
+	struct arm_smmu_device *smmu;
+	struct platform_device *smmu_pdev;
+
+	smmu_pdev = of_find_device_by_node(args->np);
+	if (!smmu_pdev)
+		return -ENODEV;
+
+	smmu = platform_get_drvdata(smmu_pdev);
+
+	return arm_smmu_add_dev_streamid(smmu, dev, args->args[0]);
+}
+
 static struct iommu_ops arm_smmu_ops = {
 	.capable		= arm_smmu_capable,
 	.domain_alloc		= arm_smmu_domain_alloc,
@@ -1395,6 +1429,7 @@ static struct iommu_ops arm_smmu_ops = {
 	.device_group		= arm_smmu_device_group,
 	.domain_get_attr	= arm_smmu_domain_get_attr,
 	.domain_set_attr	= arm_smmu_domain_set_attr,
+	.of_xlate		= arm_smmu_of_xlate,
 	.pgsize_bitmap		= -1UL, /* Restricted during device attach */
 };
 
@@ -1731,6 +1766,9 @@ static int arm_smmu_probe_mmu_masters(struct arm_smmu_device *smmu)
 	struct of_phandle_args masterspec;
 	int err, i = 0;
 
+	dev_notice(smmu->dev,
+		   "Deprecated \"mmu-masters\" property found; update DT to \"iommus\" property if possible\n");
+
 	while (!of_parse_phandle_with_args(smmu->dev->of_node, "mmu-masters",
 			"#stream-id-cells", i,
 			&masterspec)) {
@@ -1838,7 +1876,9 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
 	}
 
 	platform_set_drvdata(pdev, smmu);
-	arm_smmu_probe_mmu_masters(smmu);
+	/* Check first to avoid of_parse_phandle_with_args complaining */
+	if (of_property_read_bool(dev->of_node, "mmu-masters"))
+		arm_smmu_probe_mmu_masters(smmu);
 	arm_smmu_device_reset(smmu);
 	return 0;
 
@@ -1881,8 +1921,11 @@ static struct platform_driver arm_smmu_driver = {
 static int __init arm_smmu_init(void)
 {
 	struct device_node *np;
+	static bool done;
 	int ret;
 
+	if (done)
+		return 0;
 	/*
 	 * Play nice with systems that don't have an ARM SMMU by checking that
 	 * an ARM SMMU exists in the system before proceeding with the driver
@@ -1912,6 +1955,7 @@ static int __init arm_smmu_init(void)
 		bus_set_iommu(&pci_bus_type, &arm_smmu_ops);
 #endif
 
+	done = true;
 	return 0;
 }
 
@@ -1923,6 +1967,30 @@ static void __exit arm_smmu_exit(void)
 subsys_initcall(arm_smmu_init);
 module_exit(arm_smmu_exit);
 
+static int __init arm_smmu_of_init(struct device_node *np)
+{
+	struct arm_smmu_device *smmu;
+	struct platform_device *pdev;
+	int ret = arm_smmu_init();
+
+	if (ret)
+		return ret;
+
+	pdev = of_platform_device_create(np, NULL, platform_bus_type.dev_root);
+	if (!pdev)
+		return -ENODEV;
+
+	smmu = platform_get_drvdata(pdev);
+	of_iommu_set_ops(np, &arm_smmu_ops);
+
+	return 0;
+}
+IOMMU_OF_DECLARE(arm_smmuv1, "arm,smmu-v1", arm_smmu_of_init);
+IOMMU_OF_DECLARE(arm_smmuv2, "arm,smmu-v2", arm_smmu_of_init);
+IOMMU_OF_DECLARE(arm_mmu400, "arm,mmu-400", arm_smmu_of_init);
+IOMMU_OF_DECLARE(arm_mmu401, "arm,mmu-401", arm_smmu_of_init);
+IOMMU_OF_DECLARE(arm_mmu500, "arm,mmu-500", arm_smmu_of_init);
+
 MODULE_DESCRIPTION("IOMMU API for ARM architected SMMU implementations");
 MODULE_AUTHOR("Will Deacon <will.deacon@arm.com>");
 MODULE_LICENSE("GPL v2");
-- 
2.7.2.333.g70bd996.dirty

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 12/12] iommu/arm-smmu: Group platform devices appropriately
  2016-02-29 13:46 ` Robin Murphy
@ 2016-02-29 13:46     ` Robin Murphy
  -1 siblings, 0 replies; 38+ messages in thread
From: Robin Murphy @ 2016-02-29 13:46 UTC (permalink / raw)
  To: iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA
  Cc: will.deacon-5wv7dgnIgG8, Suravee.Suthikulpanit-5C7GfCeVMHo,
	Thomas.Lendacky-5C7GfCeVMHo, stuart.yoder-3arQi8VN3Tc,
	tchalamarla-M3mlKVOIwJVv6pq1l3V1OdBPR1lH4CV8,
	anup.patel-dY08KVG/lbpWk0Htik3J/w,
	thunder.leizhen-hv44wF8Li93QT0dZR+AlfA

IOMMU groups are necessary to represent multiple devices which the IOMMU
cannot tell apart; if two platform devices are wired up with the same
stream ID, then the current approach of blindly placing every device in
its own group makes it possible for each device to end up attached to a
different domain. This is a Very Bad Thing under the SMMU architecture,
where mapping a stream ID to more than one context leads to various
unpleasant behaviours.

In the absence of a suitable common abstraction (which is difficult due
to the implementation-specific nature of platform device IDs), implement
a simple lookup table within the SMMU driver to ensure that any devices
with overlapping stream IDs end up in the same group as they should.

Signed-off-by: Robin Murphy <robin.murphy-5wv7dgnIgG8@public.gmane.org>
---
 drivers/iommu/arm-smmu.c | 61 +++++++++++++++++++++++++++++++++++++++---------
 1 file changed, 50 insertions(+), 11 deletions(-)

diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index 8fcf27a..2d65de4 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -333,6 +333,7 @@ struct arm_smmu_device {
 
 	u32				num_mapping_groups;
 	DECLARE_BITMAP(smr_map, ARM_SMMU_MAX_SMRS);
+	struct iommu_group		**group_lut;
 
 	unsigned long			va_size;
 	unsigned long			ipa_size;
@@ -1272,11 +1273,6 @@ static int arm_smmu_add_device(struct device *dev)
 			return ret;
 	}
 
-	/*
-	 * For now, assume that the default group allocators suffice.
-	 * We might have to do some preparatory work here to properly
-	 * handle multiple devices sharing stream IDs.
-	 */
 	group = iommu_group_get_for_dev(dev);
 	if (IS_ERR(group))
 		return PTR_ERR(group);
@@ -1297,6 +1293,45 @@ static void __arm_smmu_release_iommudata(void *data)
 	kfree(data);
 }
 
+static struct iommu_group *arm_smmu_group_lookup(struct device *dev)
+{
+	struct arm_smmu_master_cfg *cfg = dev->archdata.iommu;
+	struct arm_smmu_device *smmu = cfg->smmu;
+	struct iommu_group **lut = smmu->group_lut;
+	struct iommu_group *group = NULL;
+	int i;
+
+	if (!lut) {
+		/*
+		 * Unfortunately this has to be sized for the worst-case until
+		 * we get even cleverer with stream ID management.
+		 */
+		lut = devm_kcalloc(smmu->dev, SMR_ID_MASK + 1,
+				   sizeof(*lut), GFP_KERNEL);
+		if (lut)
+			smmu->group_lut = lut;
+		else
+			group = ERR_PTR(-ENOMEM);
+	} else {
+		/* Check for platform or cross-bus aliases */
+		for (i = 0; i < cfg->num_streamids; i++) {
+			struct iommu_group *tmp = lut[cfg->streamids[i].id];
+
+			if (!tmp)
+				continue;
+
+			if (group && tmp != group) {
+				dev_err(smmu->dev,
+					"Cannot handle master %s aliasing multiple groups\n",
+					dev_name(dev));
+				return ERR_PTR(-EBUSY);
+			}
+			group = tmp;
+		}
+	}
+	return group;
+}
+
 static inline bool __streamid_match_sme(struct arm_smmu_streamid *sid,
 					struct arm_smmu_stream_map_entry *sme)
 {
@@ -1306,22 +1341,24 @@ static inline bool __streamid_match_sme(struct arm_smmu_streamid *sid,
 
 static struct iommu_group *arm_smmu_device_group(struct device *dev)
 {
-	struct arm_smmu_master_cfg *master_cfg;
+	struct arm_smmu_master_cfg *master_cfg = dev->archdata.iommu;
 	struct arm_smmu_group_cfg *group_cfg;
 	struct arm_smmu_streamid *sids;
 	struct arm_smmu_stream_map_entry *smes;
 	struct iommu_group *group;
 	int i, j;
 
-	if (dev_is_pci(dev))
-		group = pci_device_group(dev);
-	else
-		group = generic_device_group(dev);
+	group = arm_smmu_group_lookup(dev);
+	if (!group) {
+		if (dev_is_pci(dev))
+			group = pci_device_group(dev);
+		else
+			group = generic_device_group(dev);
+	}
 
 	if (IS_ERR(group))
 		return group;
 
-	master_cfg = dev->archdata.iommu;
 	group_cfg = iommu_group_get_iommudata(group);
 	if (!group_cfg) {
 		group_cfg = kzalloc(sizeof(*group_cfg), GFP_KERNEL);
@@ -1336,6 +1373,8 @@ static struct iommu_group *arm_smmu_device_group(struct device *dev)
 	sids = master_cfg->streamids;
 	smes = group_cfg->smes;
 	for (i = 0; i < master_cfg->num_streamids; i++) {
+		master_cfg->smmu->group_lut[sids[i].id] = group;
+
 		for (j = 0; j < group_cfg->num_smes; j++) {
 			if (__streamid_match_sme(&sids[i], &smes[j])) {
 				sids[i].s2cr_idx = STREAM_MULTIPLE;
-- 
2.7.2.333.g70bd996.dirty

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

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 12/12] iommu/arm-smmu: Group platform devices appropriately
@ 2016-02-29 13:46     ` Robin Murphy
  0 siblings, 0 replies; 38+ messages in thread
From: Robin Murphy @ 2016-02-29 13:46 UTC (permalink / raw)
  To: linux-arm-kernel

IOMMU groups are necessary to represent multiple devices which the IOMMU
cannot tell apart; if two platform devices are wired up with the same
stream ID, then the current approach of blindly placing every device in
its own group makes it possible for each device to end up attached to a
different domain. This is a Very Bad Thing under the SMMU architecture,
where mapping a stream ID to more than one context leads to various
unpleasant behaviours.

In the absence of a suitable common abstraction (which is difficult due
to the implementation-specific nature of platform device IDs), implement
a simple lookup table within the SMMU driver to ensure that any devices
with overlapping stream IDs end up in the same group as they should.

Signed-off-by: Robin Murphy <robin.murphy@arm.com>
---
 drivers/iommu/arm-smmu.c | 61 +++++++++++++++++++++++++++++++++++++++---------
 1 file changed, 50 insertions(+), 11 deletions(-)

diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index 8fcf27a..2d65de4 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -333,6 +333,7 @@ struct arm_smmu_device {
 
 	u32				num_mapping_groups;
 	DECLARE_BITMAP(smr_map, ARM_SMMU_MAX_SMRS);
+	struct iommu_group		**group_lut;
 
 	unsigned long			va_size;
 	unsigned long			ipa_size;
@@ -1272,11 +1273,6 @@ static int arm_smmu_add_device(struct device *dev)
 			return ret;
 	}
 
-	/*
-	 * For now, assume that the default group allocators suffice.
-	 * We might have to do some preparatory work here to properly
-	 * handle multiple devices sharing stream IDs.
-	 */
 	group = iommu_group_get_for_dev(dev);
 	if (IS_ERR(group))
 		return PTR_ERR(group);
@@ -1297,6 +1293,45 @@ static void __arm_smmu_release_iommudata(void *data)
 	kfree(data);
 }
 
+static struct iommu_group *arm_smmu_group_lookup(struct device *dev)
+{
+	struct arm_smmu_master_cfg *cfg = dev->archdata.iommu;
+	struct arm_smmu_device *smmu = cfg->smmu;
+	struct iommu_group **lut = smmu->group_lut;
+	struct iommu_group *group = NULL;
+	int i;
+
+	if (!lut) {
+		/*
+		 * Unfortunately this has to be sized for the worst-case until
+		 * we get even cleverer with stream ID management.
+		 */
+		lut = devm_kcalloc(smmu->dev, SMR_ID_MASK + 1,
+				   sizeof(*lut), GFP_KERNEL);
+		if (lut)
+			smmu->group_lut = lut;
+		else
+			group = ERR_PTR(-ENOMEM);
+	} else {
+		/* Check for platform or cross-bus aliases */
+		for (i = 0; i < cfg->num_streamids; i++) {
+			struct iommu_group *tmp = lut[cfg->streamids[i].id];
+
+			if (!tmp)
+				continue;
+
+			if (group && tmp != group) {
+				dev_err(smmu->dev,
+					"Cannot handle master %s aliasing multiple groups\n",
+					dev_name(dev));
+				return ERR_PTR(-EBUSY);
+			}
+			group = tmp;
+		}
+	}
+	return group;
+}
+
 static inline bool __streamid_match_sme(struct arm_smmu_streamid *sid,
 					struct arm_smmu_stream_map_entry *sme)
 {
@@ -1306,22 +1341,24 @@ static inline bool __streamid_match_sme(struct arm_smmu_streamid *sid,
 
 static struct iommu_group *arm_smmu_device_group(struct device *dev)
 {
-	struct arm_smmu_master_cfg *master_cfg;
+	struct arm_smmu_master_cfg *master_cfg = dev->archdata.iommu;
 	struct arm_smmu_group_cfg *group_cfg;
 	struct arm_smmu_streamid *sids;
 	struct arm_smmu_stream_map_entry *smes;
 	struct iommu_group *group;
 	int i, j;
 
-	if (dev_is_pci(dev))
-		group = pci_device_group(dev);
-	else
-		group = generic_device_group(dev);
+	group = arm_smmu_group_lookup(dev);
+	if (!group) {
+		if (dev_is_pci(dev))
+			group = pci_device_group(dev);
+		else
+			group = generic_device_group(dev);
+	}
 
 	if (IS_ERR(group))
 		return group;
 
-	master_cfg = dev->archdata.iommu;
 	group_cfg = iommu_group_get_iommudata(group);
 	if (!group_cfg) {
 		group_cfg = kzalloc(sizeof(*group_cfg), GFP_KERNEL);
@@ -1336,6 +1373,8 @@ static struct iommu_group *arm_smmu_device_group(struct device *dev)
 	sids = master_cfg->streamids;
 	smes = group_cfg->smes;
 	for (i = 0; i < master_cfg->num_streamids; i++) {
+		master_cfg->smmu->group_lut[sids[i].id] = group;
+
 		for (j = 0; j < group_cfg->num_smes; j++) {
 			if (__streamid_match_sme(&sids[i], &smes[j])) {
 				sids[i].s2cr_idx = STREAM_MULTIPLE;
-- 
2.7.2.333.g70bd996.dirty

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* Re: [PATCH 04/12] arm64/dma-mapping: Extend DMA ops workaround to PCI devices
  2016-02-29 13:46     ` Robin Murphy
@ 2016-02-29 16:47         ` Catalin Marinas
  -1 siblings, 0 replies; 38+ messages in thread
From: Catalin Marinas @ 2016-02-29 16:47 UTC (permalink / raw)
  To: Robin Murphy
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, Thomas.Lendacky-5C7GfCeVMHo,
	tchalamarla-M3mlKVOIwJVv6pq1l3V1OdBPR1lH4CV8,
	will.deacon-5wv7dgnIgG8, stuart.yoder-3arQi8VN3Tc,
	iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	thunder.leizhen-hv44wF8Li93QT0dZR+AlfA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

On Mon, Feb 29, 2016 at 01:46:13PM +0000, Robin Murphy wrote:
> PCI devices now suffer the same hiccup as platform devices, in that they
> get their DMA ops configured before they have been added to their bus,
> and thus before we know whether they have successfully registered with
> an IOMMU or not. Until the necessary driver core changes to reorder
> calls during device creation have been worked out, extend our delayed
> notifier trick onto the PCI bus so as to avoid broken DMA ops for IOMMUs
> there.
> 
> CC: Catalin Marinas <catalin.marinas-5wv7dgnIgG8@public.gmane.org>
> CC: Will Deacon <will.deacon-5wv7dgnIgG8@public.gmane.org>
> Signed-off-by: Robin Murphy <robin.murphy-5wv7dgnIgG8@public.gmane.org>

Acked-by: Catalin Marinas <catalin.marinas-5wv7dgnIgG8@public.gmane.org>

-- 
Catalin

^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH 04/12] arm64/dma-mapping: Extend DMA ops workaround to PCI devices
@ 2016-02-29 16:47         ` Catalin Marinas
  0 siblings, 0 replies; 38+ messages in thread
From: Catalin Marinas @ 2016-02-29 16:47 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Feb 29, 2016 at 01:46:13PM +0000, Robin Murphy wrote:
> PCI devices now suffer the same hiccup as platform devices, in that they
> get their DMA ops configured before they have been added to their bus,
> and thus before we know whether they have successfully registered with
> an IOMMU or not. Until the necessary driver core changes to reorder
> calls during device creation have been worked out, extend our delayed
> notifier trick onto the PCI bus so as to avoid broken DMA ops for IOMMUs
> there.
> 
> CC: Catalin Marinas <catalin.marinas@arm.com>
> CC: Will Deacon <will.deacon@arm.com>
> Signed-off-by: Robin Murphy <robin.murphy@arm.com>

Acked-by: Catalin Marinas <catalin.marinas@arm.com>

-- 
Catalin

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 05/12] arm64/dma-mapping: Remove default domain workaround
  2016-02-29 13:46     ` Robin Murphy
@ 2016-02-29 16:51         ` Catalin Marinas
  -1 siblings, 0 replies; 38+ messages in thread
From: Catalin Marinas @ 2016-02-29 16:51 UTC (permalink / raw)
  To: Robin Murphy
  Cc: iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Thomas.Lendacky-5C7GfCeVMHo,
	anup.patel-dY08KVG/lbpWk0Htik3J/w,
	thunder.leizhen-hv44wF8Li93QT0dZR+AlfA, will.deacon-5wv7dgnIgG8,
	stuart.yoder-3arQi8VN3Tc, Suravee.Suthikulpanit-5C7GfCeVMHo,
	tchalamarla-M3mlKVOIwJVv6pq1l3V1OdBPR1lH4CV8

On Mon, Feb 29, 2016 at 01:46:14PM +0000, Robin Murphy wrote:
> With the IOMMU core now taking care of default domains for groups
> regardless of bus type, we can gleefully rip out this stop-gap, as
> slight recompense for having to expand the other one.
> 
> CC: Catalin Marinas <catalin.marinas-5wv7dgnIgG8@public.gmane.org>
> CC: Will Deacon <will.deacon-5wv7dgnIgG8@public.gmane.org>
> Signed-off-by: Robin Murphy <robin.murphy-5wv7dgnIgG8@public.gmane.org>

Acked-by: Catalin Marinas <catalin.marinas-5wv7dgnIgG8@public.gmane.org>

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

^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH 05/12] arm64/dma-mapping: Remove default domain workaround
@ 2016-02-29 16:51         ` Catalin Marinas
  0 siblings, 0 replies; 38+ messages in thread
From: Catalin Marinas @ 2016-02-29 16:51 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Feb 29, 2016 at 01:46:14PM +0000, Robin Murphy wrote:
> With the IOMMU core now taking care of default domains for groups
> regardless of bus type, we can gleefully rip out this stop-gap, as
> slight recompense for having to expand the other one.
> 
> CC: Catalin Marinas <catalin.marinas@arm.com>
> CC: Will Deacon <will.deacon@arm.com>
> Signed-off-by: Robin Murphy <robin.murphy@arm.com>

Acked-by: Catalin Marinas <catalin.marinas@arm.com>

-- 
Catalin

^ permalink raw reply	[flat|nested] 38+ messages in thread

* RE: [PATCH 11/12] iommu/arm-smmu: Generic IOMMU DT bindings support
  2016-02-29 13:46     ` Robin Murphy
@ 2016-02-29 18:09         ` Sricharan
  -1 siblings, 0 replies; 38+ messages in thread
From: Sricharan @ 2016-02-29 18:09 UTC (permalink / raw)
  To: 'Robin Murphy',
	iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA
  Cc: Thomas.Lendacky-5C7GfCeVMHo,
	tchalamarla-M3mlKVOIwJVv6pq1l3V1OdBPR1lH4CV8,
	will.deacon-5wv7dgnIgG8, stuart.yoder-3arQi8VN3Tc,
	thunder.leizhen-hv44wF8Li93QT0dZR+AlfA

Hi Robin,

> -----Original Message-----
> From: linux-arm-kernel [mailto:linux-arm-kernel-
> bounces-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org] On Behalf Of Robin Murphy
> Sent: Monday, February 29, 2016 7:16 PM
> To: iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA@public.gmane.org;
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org;
> devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> Cc: Thomas.Lendacky-5C7GfCeVMHo@public.gmane.org; anup.patel-dY08KVG/lbpWk0Htik3J/w@public.gmane.org;
> thunder.leizhen-hv44wF8Li93QT0dZR+AlfA@public.gmane.org; will.deacon-5wv7dgnIgG8@public.gmane.org;
> stuart.yoder-3arQi8VN3Tc@public.gmane.org; Suravee.Suthikulpanit-5C7GfCeVMHo@public.gmane.org;
> tchalamarla-M3mlKVOIwJVv6pq1l3V1OdBPR1lH4CV8@public.gmane.org
> Subject: [PATCH 11/12] iommu/arm-smmu: Generic IOMMU DT bindings
> support
> 
> Implement an of_xlate callback and the appropriate registration so that we
> can configure masters via generic DT bindings. Initially, we have the
> equivalent level of functionality with respect to groups, stream ID
limits, etc.
> as for the old bindings, but the door is now open for further
improvements.
> 
> Since of_iommmu_configure() is not yet clever enough to enforce the
> necessary probe ordering dependencies, employ the same explicit device
> creation tactic as the Exynos IOMMU driver to ensure our actual SMMU
> instance is up and running in time to handle of_xlate calls.
> 
> Signed-off-by: Robin Murphy <robin.murphy-5wv7dgnIgG8@public.gmane.org>
> ---
>  drivers/iommu/arm-smmu.c | 144
> ++++++++++++++++++++++++++++++++++-------------
>  1 file changed, 106 insertions(+), 38 deletions(-)
> 
> diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index
> 91b0a1b..8fcf27a 100644
> --- a/drivers/iommu/arm-smmu.c
> +++ b/drivers/iommu/arm-smmu.c
> @@ -39,6 +39,8 @@
>  #include <linux/module.h>
>  #include <linux/of.h>
>  #include <linux/of_address.h>
> +#include <linux/of_iommu.h>
> +#include <linux/of_platform.h>
>  #include <linux/pci.h>
>  #include <linux/platform_device.h>
>  #include <linux/slab.h>
> @@ -421,6 +423,9 @@ static struct arm_smmu_master
> *find_smmu_master(struct device_node *dev_node)  {
>  	struct arm_smmu_master *master;
> 
> +	if (!dev_node)
> +		return NULL;
> +
>  	read_lock(&arm_smmu_masters_lock);
>  	list_for_each_entry(master, &arm_smmu_masters, list)
>  		if (master->of_node == dev_node)
> @@ -1011,13 +1016,6 @@ static int arm_smmu_domain_add_master(struct
> arm_smmu_domain *smmu_domain,
>  	if (ret)
>  		return ret;
> 
> -	/*
> -	 * FIXME: This won't be needed once we have IOMMU-backed DMA
> ops
> -	 * for all devices behind the SMMU.
> -	 */
> -	if (smmu_domain->domain.type == IOMMU_DOMAIN_DMA)
> -		return 0;
> -
>  	for (i = 0; i < cfg->num_streamids; ++i) {
>  		u32 idx = cfg->streamids[i].s2cr_idx - 1;
>  		u32 s2cr = S2CR_TYPE_TRANS | S2CR_PRIVCFG_UNPRIV |
> @@ -1206,57 +1204,79 @@ static bool arm_smmu_capable(enum
> iommu_cap cap)
>  	}
>  }
> 
> +static int arm_smmu_add_dev_streamid(struct arm_smmu_device *smmu,
> +				     struct device *dev, u16 sid)
> +{
> +	struct arm_smmu_master_cfg *cfg = dev->archdata.iommu;
> +	int i;
> +
> +	if (!cfg) {
> +		cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
> +		if (!cfg)
> +			return -ENOMEM;
> +
> +		cfg->smmu = smmu;
> +		dev->archdata.iommu = cfg;
> +	}
> +
> +	if (cfg->num_streamids >= MAX_MASTER_STREAMIDS)
> +		return -ENOSPC;
> +
> +	/* Avoid duplicate SIDs, as this can lead to SMR conflicts */
> +	for (i = 0; i < cfg->num_streamids; ++i)
> +		if (cfg->streamids[i].id == sid) {
> +			dev_warn(dev, "Stream ID 0x%hx repeated;
> ignoring\n",
> +				 sid);
> +			return 0;
> +		}
> +
> +	cfg->streamids[cfg->num_streamids++].id = sid;
> +
> +	return 0;
> +}
> +
>  static int __arm_smmu_get_pci_sid(struct pci_dev *pdev, u16 alias, void
> *data)  {
>  	*((u16 *)data) = alias;
>  	return 0; /* Continue walking */
>  }
> 
> -static int arm_smmu_init_pci_device(struct arm_smmu_device *smmu,
> -				    struct pci_dev *pdev)
> +static int arm_smmu_init_legacy_master(struct device *dev)
>  {
> -	struct arm_smmu_master_cfg *cfg;
> +	struct arm_smmu_master *master;
> +	struct device_node *np = dev_get_dev_node(dev);
>  	u16 sid;
> 
> -	cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
> -	if (!cfg)
> -		return -ENOMEM;
> -	/*
> -	 * Assume Stream ID == Requester ID for now.
> -	 * We need a way to describe the ID mappings in FDT.
> -	 */
> -	pci_for_each_dma_alias(pdev, __arm_smmu_get_pci_sid, &sid);
> +	master = find_smmu_master(np);
> +	if (!master)
> +		return -ENODEV;
> 
> -	cfg->streamids[0].id = sid;
> -	cfg->num_streamids = 1;
> +	if (!dev_is_pci(dev)) {
> +		dev->archdata.iommu = &master->cfg;
> +		return 0;
> +	}
> 
> -	cfg->smmu = smmu;
> -	pdev->dev.archdata.iommu = cfg;
> -
> -	return 0;
> +	/* Legacy bindings assume Stream ID == Requester ID */
> +	pci_for_each_dma_alias(to_pci_dev(dev),
> __arm_smmu_get_pci_sid, &sid);
> +	return arm_smmu_add_dev_streamid(master->cfg.smmu, dev, sid);
>  }
> 
>  static int arm_smmu_add_device(struct device *dev)  {
>  	struct iommu_group *group;
> -	struct arm_smmu_master *master;
> 
> -	if (dev->archdata.iommu)
> -		return -EEXIST;
> +	if (!dev->archdata.iommu) {
> +		int ret = arm_smmu_init_legacy_master(dev);
> 
> -	master = find_smmu_master(dev_get_dev_node(dev));
> -	if (!master)
> -		return -ENODEV;
> -
> -	if (dev_is_pci(dev)) {
> -		int ret = arm_smmu_init_pci_device(master->cfg.smmu,
> -						   to_pci_dev(dev));
>  		if (ret)
>  			return ret;
> -	} else {
> -		dev->archdata.iommu = &master->cfg;
>  	}
> 
> +	/*
> +	 * For now, assume that the default group allocators suffice.
> +	 * We might have to do some preparatory work here to properly
> +	 * handle multiple devices sharing stream IDs.
> +	 */
>  	group = iommu_group_get_for_dev(dev);
>  	if (IS_ERR(group))
>  		return PTR_ERR(group);
> @@ -1268,7 +1288,7 @@ static int arm_smmu_add_device(struct device
> *dev)  static void arm_smmu_remove_device(struct device *dev)  {
>  	iommu_group_remove_device(dev);
> -	if (dev_is_pci(dev))
> +	if (!find_smmu_master(dev->of_node))
>  		kfree(dev->archdata.iommu);
>  }
> 
> @@ -1381,6 +1401,20 @@ out_unlock:
>  	return ret;
>  }
> 
> +static int arm_smmu_of_xlate(struct device *dev, struct of_phandle_args
> +*args) {
> +	struct arm_smmu_device *smmu;
> +	struct platform_device *smmu_pdev;
> +
> +	smmu_pdev = of_find_device_by_node(args->np);
> +	if (!smmu_pdev)
> +		return -ENODEV;
> +
> +	smmu = platform_get_drvdata(smmu_pdev);
> +
> +	return arm_smmu_add_dev_streamid(smmu, dev, args->args[0]); }
> +
>  static struct iommu_ops arm_smmu_ops = {
>  	.capable		= arm_smmu_capable,
>  	.domain_alloc		= arm_smmu_domain_alloc,
> @@ -1395,6 +1429,7 @@ static struct iommu_ops arm_smmu_ops = {
>  	.device_group		= arm_smmu_device_group,
>  	.domain_get_attr	= arm_smmu_domain_get_attr,
>  	.domain_set_attr	= arm_smmu_domain_set_attr,
> +	.of_xlate		= arm_smmu_of_xlate,
>  	.pgsize_bitmap		= -1UL, /* Restricted during device attach
*/
>  };
> 
> @@ -1731,6 +1766,9 @@ static int arm_smmu_probe_mmu_masters(struct
> arm_smmu_device *smmu)
>  	struct of_phandle_args masterspec;
>  	int err, i = 0;
> 
> +	dev_notice(smmu->dev,
> +		   "Deprecated \"mmu-masters\" property found; update DT
> to
> +\"iommus\" property if possible\n");
> +
>  	while (!of_parse_phandle_with_args(smmu->dev->of_node, "mmu-
> masters",
>  			"#stream-id-cells", i,
>  			&masterspec)) {
> @@ -1838,7 +1876,9 @@ static int arm_smmu_device_dt_probe(struct
> platform_device *pdev)
>  	}
> 
>  	platform_set_drvdata(pdev, smmu);
> -	arm_smmu_probe_mmu_masters(smmu);
> +	/* Check first to avoid of_parse_phandle_with_args complaining */
> +	if (of_property_read_bool(dev->of_node, "mmu-masters"))
> +		arm_smmu_probe_mmu_masters(smmu);
>  	arm_smmu_device_reset(smmu);
>  	return 0;
> 
> @@ -1881,8 +1921,11 @@ static struct platform_driver arm_smmu_driver = {
> static int __init arm_smmu_init(void)  {
>  	struct device_node *np;
> +	static bool done;
>  	int ret;
> 
> +	if (done)
> +		return 0;
>  	/*
>  	 * Play nice with systems that don't have an ARM SMMU by checking
> that
>  	 * an ARM SMMU exists in the system before proceeding with the
> driver @@ -1912,6 +1955,7 @@ static int __init arm_smmu_init(void)
>  		bus_set_iommu(&pci_bus_type, &arm_smmu_ops);  #endif
> 
> +	done = true;
>  	return 0;
>  }
> 
> @@ -1923,6 +1967,30 @@ static void __exit arm_smmu_exit(void)
> subsys_initcall(arm_smmu_init);  module_exit(arm_smmu_exit);
> 
> +static int __init arm_smmu_of_init(struct device_node *np) {
> +	struct arm_smmu_device *smmu;
> +	struct platform_device *pdev;
> +	int ret = arm_smmu_init();
> +
> +	if (ret)
> +		return ret;
> +
> +	pdev = of_platform_device_create(np, NULL,
> platform_bus_type.dev_root);
> +	if (!pdev)
> +		return -ENODEV;
> +
> +	smmu = platform_get_drvdata(pdev);
> +	of_iommu_set_ops(np, &arm_smmu_ops);
> +
> +	return 0;
> +}
> +IOMMU_OF_DECLARE(arm_smmuv1, "arm,smmu-v1",
> arm_smmu_of_init);
> +IOMMU_OF_DECLARE(arm_smmuv2, "arm,smmu-v2",
> arm_smmu_of_init);
> +IOMMU_OF_DECLARE(arm_mmu400, "arm,mmu-400",
> arm_smmu_of_init);
> +IOMMU_OF_DECLARE(arm_mmu401, "arm,mmu-401",
> arm_smmu_of_init);
> +IOMMU_OF_DECLARE(arm_mmu500, "arm,mmu-500",
> arm_smmu_of_init);
> +
 Thanks for this series. I am going to use and test this. Also I wanted to
 ask about the iommu probe deferral series [1] to avoid early device
registration and wanted know the direction on that ?

[1] http://lkml.iu.edu/hypermail/linux/kernel/1505.3/03280.html

Regards,
 Sricharan

^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH 11/12] iommu/arm-smmu: Generic IOMMU DT bindings support
@ 2016-02-29 18:09         ` Sricharan
  0 siblings, 0 replies; 38+ messages in thread
From: Sricharan @ 2016-02-29 18:09 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Robin,

> -----Original Message-----
> From: linux-arm-kernel [mailto:linux-arm-kernel-
> bounces at lists.infradead.org] On Behalf Of Robin Murphy
> Sent: Monday, February 29, 2016 7:16 PM
> To: iommu at lists.linux-foundation.org;
linux-arm-kernel at lists.infradead.org;
> devicetree at vger.kernel.org
> Cc: Thomas.Lendacky at amd.com; anup.patel at broadcom.com;
> thunder.leizhen at huawei.com; will.deacon at arm.com;
> stuart.yoder at nxp.com; Suravee.Suthikulpanit at amd.com;
> tchalamarla at caviumnetworks.com
> Subject: [PATCH 11/12] iommu/arm-smmu: Generic IOMMU DT bindings
> support
> 
> Implement an of_xlate callback and the appropriate registration so that we
> can configure masters via generic DT bindings. Initially, we have the
> equivalent level of functionality with respect to groups, stream ID
limits, etc.
> as for the old bindings, but the door is now open for further
improvements.
> 
> Since of_iommmu_configure() is not yet clever enough to enforce the
> necessary probe ordering dependencies, employ the same explicit device
> creation tactic as the Exynos IOMMU driver to ensure our actual SMMU
> instance is up and running in time to handle of_xlate calls.
> 
> Signed-off-by: Robin Murphy <robin.murphy@arm.com>
> ---
>  drivers/iommu/arm-smmu.c | 144
> ++++++++++++++++++++++++++++++++++-------------
>  1 file changed, 106 insertions(+), 38 deletions(-)
> 
> diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index
> 91b0a1b..8fcf27a 100644
> --- a/drivers/iommu/arm-smmu.c
> +++ b/drivers/iommu/arm-smmu.c
> @@ -39,6 +39,8 @@
>  #include <linux/module.h>
>  #include <linux/of.h>
>  #include <linux/of_address.h>
> +#include <linux/of_iommu.h>
> +#include <linux/of_platform.h>
>  #include <linux/pci.h>
>  #include <linux/platform_device.h>
>  #include <linux/slab.h>
> @@ -421,6 +423,9 @@ static struct arm_smmu_master
> *find_smmu_master(struct device_node *dev_node)  {
>  	struct arm_smmu_master *master;
> 
> +	if (!dev_node)
> +		return NULL;
> +
>  	read_lock(&arm_smmu_masters_lock);
>  	list_for_each_entry(master, &arm_smmu_masters, list)
>  		if (master->of_node == dev_node)
> @@ -1011,13 +1016,6 @@ static int arm_smmu_domain_add_master(struct
> arm_smmu_domain *smmu_domain,
>  	if (ret)
>  		return ret;
> 
> -	/*
> -	 * FIXME: This won't be needed once we have IOMMU-backed DMA
> ops
> -	 * for all devices behind the SMMU.
> -	 */
> -	if (smmu_domain->domain.type == IOMMU_DOMAIN_DMA)
> -		return 0;
> -
>  	for (i = 0; i < cfg->num_streamids; ++i) {
>  		u32 idx = cfg->streamids[i].s2cr_idx - 1;
>  		u32 s2cr = S2CR_TYPE_TRANS | S2CR_PRIVCFG_UNPRIV |
> @@ -1206,57 +1204,79 @@ static bool arm_smmu_capable(enum
> iommu_cap cap)
>  	}
>  }
> 
> +static int arm_smmu_add_dev_streamid(struct arm_smmu_device *smmu,
> +				     struct device *dev, u16 sid)
> +{
> +	struct arm_smmu_master_cfg *cfg = dev->archdata.iommu;
> +	int i;
> +
> +	if (!cfg) {
> +		cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
> +		if (!cfg)
> +			return -ENOMEM;
> +
> +		cfg->smmu = smmu;
> +		dev->archdata.iommu = cfg;
> +	}
> +
> +	if (cfg->num_streamids >= MAX_MASTER_STREAMIDS)
> +		return -ENOSPC;
> +
> +	/* Avoid duplicate SIDs, as this can lead to SMR conflicts */
> +	for (i = 0; i < cfg->num_streamids; ++i)
> +		if (cfg->streamids[i].id == sid) {
> +			dev_warn(dev, "Stream ID 0x%hx repeated;
> ignoring\n",
> +				 sid);
> +			return 0;
> +		}
> +
> +	cfg->streamids[cfg->num_streamids++].id = sid;
> +
> +	return 0;
> +}
> +
>  static int __arm_smmu_get_pci_sid(struct pci_dev *pdev, u16 alias, void
> *data)  {
>  	*((u16 *)data) = alias;
>  	return 0; /* Continue walking */
>  }
> 
> -static int arm_smmu_init_pci_device(struct arm_smmu_device *smmu,
> -				    struct pci_dev *pdev)
> +static int arm_smmu_init_legacy_master(struct device *dev)
>  {
> -	struct arm_smmu_master_cfg *cfg;
> +	struct arm_smmu_master *master;
> +	struct device_node *np = dev_get_dev_node(dev);
>  	u16 sid;
> 
> -	cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
> -	if (!cfg)
> -		return -ENOMEM;
> -	/*
> -	 * Assume Stream ID == Requester ID for now.
> -	 * We need a way to describe the ID mappings in FDT.
> -	 */
> -	pci_for_each_dma_alias(pdev, __arm_smmu_get_pci_sid, &sid);
> +	master = find_smmu_master(np);
> +	if (!master)
> +		return -ENODEV;
> 
> -	cfg->streamids[0].id = sid;
> -	cfg->num_streamids = 1;
> +	if (!dev_is_pci(dev)) {
> +		dev->archdata.iommu = &master->cfg;
> +		return 0;
> +	}
> 
> -	cfg->smmu = smmu;
> -	pdev->dev.archdata.iommu = cfg;
> -
> -	return 0;
> +	/* Legacy bindings assume Stream ID == Requester ID */
> +	pci_for_each_dma_alias(to_pci_dev(dev),
> __arm_smmu_get_pci_sid, &sid);
> +	return arm_smmu_add_dev_streamid(master->cfg.smmu, dev, sid);
>  }
> 
>  static int arm_smmu_add_device(struct device *dev)  {
>  	struct iommu_group *group;
> -	struct arm_smmu_master *master;
> 
> -	if (dev->archdata.iommu)
> -		return -EEXIST;
> +	if (!dev->archdata.iommu) {
> +		int ret = arm_smmu_init_legacy_master(dev);
> 
> -	master = find_smmu_master(dev_get_dev_node(dev));
> -	if (!master)
> -		return -ENODEV;
> -
> -	if (dev_is_pci(dev)) {
> -		int ret = arm_smmu_init_pci_device(master->cfg.smmu,
> -						   to_pci_dev(dev));
>  		if (ret)
>  			return ret;
> -	} else {
> -		dev->archdata.iommu = &master->cfg;
>  	}
> 
> +	/*
> +	 * For now, assume that the default group allocators suffice.
> +	 * We might have to do some preparatory work here to properly
> +	 * handle multiple devices sharing stream IDs.
> +	 */
>  	group = iommu_group_get_for_dev(dev);
>  	if (IS_ERR(group))
>  		return PTR_ERR(group);
> @@ -1268,7 +1288,7 @@ static int arm_smmu_add_device(struct device
> *dev)  static void arm_smmu_remove_device(struct device *dev)  {
>  	iommu_group_remove_device(dev);
> -	if (dev_is_pci(dev))
> +	if (!find_smmu_master(dev->of_node))
>  		kfree(dev->archdata.iommu);
>  }
> 
> @@ -1381,6 +1401,20 @@ out_unlock:
>  	return ret;
>  }
> 
> +static int arm_smmu_of_xlate(struct device *dev, struct of_phandle_args
> +*args) {
> +	struct arm_smmu_device *smmu;
> +	struct platform_device *smmu_pdev;
> +
> +	smmu_pdev = of_find_device_by_node(args->np);
> +	if (!smmu_pdev)
> +		return -ENODEV;
> +
> +	smmu = platform_get_drvdata(smmu_pdev);
> +
> +	return arm_smmu_add_dev_streamid(smmu, dev, args->args[0]); }
> +
>  static struct iommu_ops arm_smmu_ops = {
>  	.capable		= arm_smmu_capable,
>  	.domain_alloc		= arm_smmu_domain_alloc,
> @@ -1395,6 +1429,7 @@ static struct iommu_ops arm_smmu_ops = {
>  	.device_group		= arm_smmu_device_group,
>  	.domain_get_attr	= arm_smmu_domain_get_attr,
>  	.domain_set_attr	= arm_smmu_domain_set_attr,
> +	.of_xlate		= arm_smmu_of_xlate,
>  	.pgsize_bitmap		= -1UL, /* Restricted during device attach
*/
>  };
> 
> @@ -1731,6 +1766,9 @@ static int arm_smmu_probe_mmu_masters(struct
> arm_smmu_device *smmu)
>  	struct of_phandle_args masterspec;
>  	int err, i = 0;
> 
> +	dev_notice(smmu->dev,
> +		   "Deprecated \"mmu-masters\" property found; update DT
> to
> +\"iommus\" property if possible\n");
> +
>  	while (!of_parse_phandle_with_args(smmu->dev->of_node, "mmu-
> masters",
>  			"#stream-id-cells", i,
>  			&masterspec)) {
> @@ -1838,7 +1876,9 @@ static int arm_smmu_device_dt_probe(struct
> platform_device *pdev)
>  	}
> 
>  	platform_set_drvdata(pdev, smmu);
> -	arm_smmu_probe_mmu_masters(smmu);
> +	/* Check first to avoid of_parse_phandle_with_args complaining */
> +	if (of_property_read_bool(dev->of_node, "mmu-masters"))
> +		arm_smmu_probe_mmu_masters(smmu);
>  	arm_smmu_device_reset(smmu);
>  	return 0;
> 
> @@ -1881,8 +1921,11 @@ static struct platform_driver arm_smmu_driver = {
> static int __init arm_smmu_init(void)  {
>  	struct device_node *np;
> +	static bool done;
>  	int ret;
> 
> +	if (done)
> +		return 0;
>  	/*
>  	 * Play nice with systems that don't have an ARM SMMU by checking
> that
>  	 * an ARM SMMU exists in the system before proceeding with the
> driver @@ -1912,6 +1955,7 @@ static int __init arm_smmu_init(void)
>  		bus_set_iommu(&pci_bus_type, &arm_smmu_ops);  #endif
> 
> +	done = true;
>  	return 0;
>  }
> 
> @@ -1923,6 +1967,30 @@ static void __exit arm_smmu_exit(void)
> subsys_initcall(arm_smmu_init);  module_exit(arm_smmu_exit);
> 
> +static int __init arm_smmu_of_init(struct device_node *np) {
> +	struct arm_smmu_device *smmu;
> +	struct platform_device *pdev;
> +	int ret = arm_smmu_init();
> +
> +	if (ret)
> +		return ret;
> +
> +	pdev = of_platform_device_create(np, NULL,
> platform_bus_type.dev_root);
> +	if (!pdev)
> +		return -ENODEV;
> +
> +	smmu = platform_get_drvdata(pdev);
> +	of_iommu_set_ops(np, &arm_smmu_ops);
> +
> +	return 0;
> +}
> +IOMMU_OF_DECLARE(arm_smmuv1, "arm,smmu-v1",
> arm_smmu_of_init);
> +IOMMU_OF_DECLARE(arm_smmuv2, "arm,smmu-v2",
> arm_smmu_of_init);
> +IOMMU_OF_DECLARE(arm_mmu400, "arm,mmu-400",
> arm_smmu_of_init);
> +IOMMU_OF_DECLARE(arm_mmu401, "arm,mmu-401",
> arm_smmu_of_init);
> +IOMMU_OF_DECLARE(arm_mmu500, "arm,mmu-500",
> arm_smmu_of_init);
> +
 Thanks for this series. I am going to use and test this. Also I wanted to
 ask about the iommu probe deferral series [1] to avoid early device
registration and wanted know the direction on that ?

[1] http://lkml.iu.edu/hypermail/linux/kernel/1505.3/03280.html

Regards,
 Sricharan

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 11/12] iommu/arm-smmu: Generic IOMMU DT bindings support
  2016-02-29 18:09         ` Sricharan
@ 2016-03-02 13:30           ` Robin Murphy
  -1 siblings, 0 replies; 38+ messages in thread
From: Robin Murphy @ 2016-03-02 13:30 UTC (permalink / raw)
  To: Sricharan, iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA
  Cc: Thomas.Lendacky-5C7GfCeVMHo, Marc Zyngier,
	thunder.leizhen-hv44wF8Li93QT0dZR+AlfA, will.deacon-5wv7dgnIgG8,
	stuart.yoder-3arQi8VN3Tc,
	tchalamarla-M3mlKVOIwJVv6pq1l3V1OdBPR1lH4CV8

On 29/02/16 18:09, Sricharan wrote:
> Hi Robin,
>
>> -----Original Message-----

[...]

>> +static int __init arm_smmu_of_init(struct device_node *np) {
>> +	struct arm_smmu_device *smmu;
>> +	struct platform_device *pdev;
>> +	int ret = arm_smmu_init();
>> +
>> +	if (ret)
>> +		return ret;
>> +
>> +	pdev = of_platform_device_create(np, NULL,
>> platform_bus_type.dev_root);
>> +	if (!pdev)
>> +		return -ENODEV;
>> +
>> +	smmu = platform_get_drvdata(pdev);
>> +	of_iommu_set_ops(np, &arm_smmu_ops);
>> +
>> +	return 0;
>> +}
>> +IOMMU_OF_DECLARE(arm_smmuv1, "arm,smmu-v1",
>> arm_smmu_of_init);
>> +IOMMU_OF_DECLARE(arm_smmuv2, "arm,smmu-v2",
>> arm_smmu_of_init);
>> +IOMMU_OF_DECLARE(arm_mmu400, "arm,mmu-400",
>> arm_smmu_of_init);
>> +IOMMU_OF_DECLARE(arm_mmu401, "arm,mmu-401",
>> arm_smmu_of_init);
>> +IOMMU_OF_DECLARE(arm_mmu500, "arm,mmu-500",
>> arm_smmu_of_init);
>> +
>   Thanks for this series. I am going to use and test this. Also I wanted to
>   ask about the iommu probe deferral series [1] to avoid early device
> registration and wanted know the direction on that ?

It's certainly on my near-term to-do list to revisit. I recall running 
into problems with that series if the IOMMU was ready but the device 
itself then requested probe deferral, and I have vague memories of 
thinking more needed to be done generally around the failure/device 
teardown path too. I also had high hopes for the on-demand device 
probing series from around the same time[2], which would have helped 
simplify things quite a bit, but that also seems to have died after a 
brief stint breaking things in -next.

Anyway, Marc reckons that we also have the exact same probe-dependency 
problem for things like IRQ-MSI bridges, so I'll be looking into a more 
general solution at some point unless anyone wants to beat me to it ;)

Thanks,
Robin.

[2]:http://thread.gmane.org/gmane.linux.acpi.devel/78833

>
> [1] http://lkml.iu.edu/hypermail/linux/kernel/1505.3/03280.html
>
> Regards,
>   Sricharan
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>

^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH 11/12] iommu/arm-smmu: Generic IOMMU DT bindings support
@ 2016-03-02 13:30           ` Robin Murphy
  0 siblings, 0 replies; 38+ messages in thread
From: Robin Murphy @ 2016-03-02 13:30 UTC (permalink / raw)
  To: linux-arm-kernel

On 29/02/16 18:09, Sricharan wrote:
> Hi Robin,
>
>> -----Original Message-----

[...]

>> +static int __init arm_smmu_of_init(struct device_node *np) {
>> +	struct arm_smmu_device *smmu;
>> +	struct platform_device *pdev;
>> +	int ret = arm_smmu_init();
>> +
>> +	if (ret)
>> +		return ret;
>> +
>> +	pdev = of_platform_device_create(np, NULL,
>> platform_bus_type.dev_root);
>> +	if (!pdev)
>> +		return -ENODEV;
>> +
>> +	smmu = platform_get_drvdata(pdev);
>> +	of_iommu_set_ops(np, &arm_smmu_ops);
>> +
>> +	return 0;
>> +}
>> +IOMMU_OF_DECLARE(arm_smmuv1, "arm,smmu-v1",
>> arm_smmu_of_init);
>> +IOMMU_OF_DECLARE(arm_smmuv2, "arm,smmu-v2",
>> arm_smmu_of_init);
>> +IOMMU_OF_DECLARE(arm_mmu400, "arm,mmu-400",
>> arm_smmu_of_init);
>> +IOMMU_OF_DECLARE(arm_mmu401, "arm,mmu-401",
>> arm_smmu_of_init);
>> +IOMMU_OF_DECLARE(arm_mmu500, "arm,mmu-500",
>> arm_smmu_of_init);
>> +
>   Thanks for this series. I am going to use and test this. Also I wanted to
>   ask about the iommu probe deferral series [1] to avoid early device
> registration and wanted know the direction on that ?

It's certainly on my near-term to-do list to revisit. I recall running 
into problems with that series if the IOMMU was ready but the device 
itself then requested probe deferral, and I have vague memories of 
thinking more needed to be done generally around the failure/device 
teardown path too. I also had high hopes for the on-demand device 
probing series from around the same time[2], which would have helped 
simplify things quite a bit, but that also seems to have died after a 
brief stint breaking things in -next.

Anyway, Marc reckons that we also have the exact same probe-dependency 
problem for things like IRQ-MSI bridges, so I'll be looking into a more 
general solution at some point unless anyone wants to beat me to it ;)

Thanks,
Robin.

[2]:http://thread.gmane.org/gmane.linux.acpi.devel/78833

>
> [1] http://lkml.iu.edu/hypermail/linux/kernel/1505.3/03280.html
>
> Regards,
>   Sricharan
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 01/12] Docs: dt: add PCI IOMMU map bindings
  2016-02-29 13:46     ` Robin Murphy
@ 2016-03-03 22:27         ` Rob Herring
  -1 siblings, 0 replies; 38+ messages in thread
From: Rob Herring @ 2016-03-03 22:27 UTC (permalink / raw)
  To: Robin Murphy
  Cc: Mark Rutland, devicetree-u79uwXL29TY76Z2rM5mHXA,
	Thomas.Lendacky-5C7GfCeVMHo,
	thunder.leizhen-hv44wF8Li93QT0dZR+AlfA, will.deacon-5wv7dgnIgG8,
	stuart.yoder-3arQi8VN3Tc,
	iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	tchalamarla-M3mlKVOIwJVv6pq1l3V1OdBPR1lH4CV8,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

On Mon, Feb 29, 2016 at 01:46:10PM +0000, Robin Murphy wrote:
> From: Mark Rutland <mark.rutland-5wv7dgnIgG8@public.gmane.org>
> 
> The existing IOMMU bindings are able to specify the relationship between
> masters and IOMMUs, but they are insufficient for describing the general
> case of hotpluggable busses such as PCI where the set of masters is not
> known until runtime, and the relationship between masters and IOMMUs is
> a property of the integration of the system.
> 
> This patch adds a generic binding for mapping PCI devices to IOMMUs,
> using a new iommu-map property (specific to PCI*) which may be used to
> map devices (identified by their Requester ID) to sideband data for the
> IOMMU which they master through.
> 
> Signed-off-by: Mark Rutland <mark.rutland-5wv7dgnIgG8@public.gmane.org>
> ---
>  .../devicetree/bindings/pci/pci-iommu.txt          | 171 +++++++++++++++++++++
>  1 file changed, 171 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/pci/pci-iommu.txt

Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>

^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH 01/12] Docs: dt: add PCI IOMMU map bindings
@ 2016-03-03 22:27         ` Rob Herring
  0 siblings, 0 replies; 38+ messages in thread
From: Rob Herring @ 2016-03-03 22:27 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Feb 29, 2016 at 01:46:10PM +0000, Robin Murphy wrote:
> From: Mark Rutland <mark.rutland@arm.com>
> 
> The existing IOMMU bindings are able to specify the relationship between
> masters and IOMMUs, but they are insufficient for describing the general
> case of hotpluggable busses such as PCI where the set of masters is not
> known until runtime, and the relationship between masters and IOMMUs is
> a property of the integration of the system.
> 
> This patch adds a generic binding for mapping PCI devices to IOMMUs,
> using a new iommu-map property (specific to PCI*) which may be used to
> map devices (identified by their Requester ID) to sideband data for the
> IOMMU which they master through.
> 
> Signed-off-by: Mark Rutland <mark.rutland@arm.com>
> ---
>  .../devicetree/bindings/pci/pci-iommu.txt          | 171 +++++++++++++++++++++
>  1 file changed, 171 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/pci/pci-iommu.txt

Acked-by: Rob Herring <robh@kernel.org>

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 10/12] Docs: dt: document ARM SMMU generic binding usage
  2016-02-29 13:46     ` Robin Murphy
@ 2016-03-03 22:29         ` Rob Herring
  -1 siblings, 0 replies; 38+ messages in thread
From: Rob Herring @ 2016-03-03 22:29 UTC (permalink / raw)
  To: Robin Murphy
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, Thomas.Lendacky-5C7GfCeVMHo,
	thunder.leizhen-hv44wF8Li93QT0dZR+AlfA, will.deacon-5wv7dgnIgG8,
	stuart.yoder-3arQi8VN3Tc,
	iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	tchalamarla-M3mlKVOIwJVv6pq1l3V1OdBPR1lH4CV8,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

On Mon, Feb 29, 2016 at 01:46:19PM +0000, Robin Murphy wrote:
> Document how the generic "iommus" binding should be used to describe ARM
> SMMU stream IDs instead of the old "mmu-masters" binding.
> 
> Signed-off-by: Robin Murphy <robin.murphy-5wv7dgnIgG8@public.gmane.org>
> ---
>  .../devicetree/bindings/iommu/arm,smmu.txt         | 40 ++++++++++++++--------
>  1 file changed, 25 insertions(+), 15 deletions(-)

Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>

^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH 10/12] Docs: dt: document ARM SMMU generic binding usage
@ 2016-03-03 22:29         ` Rob Herring
  0 siblings, 0 replies; 38+ messages in thread
From: Rob Herring @ 2016-03-03 22:29 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Feb 29, 2016 at 01:46:19PM +0000, Robin Murphy wrote:
> Document how the generic "iommus" binding should be used to describe ARM
> SMMU stream IDs instead of the old "mmu-masters" binding.
> 
> Signed-off-by: Robin Murphy <robin.murphy@arm.com>
> ---
>  .../devicetree/bindings/iommu/arm,smmu.txt         | 40 ++++++++++++++--------
>  1 file changed, 25 insertions(+), 15 deletions(-)

Acked-by: Rob Herring <robh@kernel.org>

^ permalink raw reply	[flat|nested] 38+ messages in thread

end of thread, other threads:[~2016-03-03 22:29 UTC | newest]

Thread overview: 38+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-02-29 13:46 [PATCH 00/12] Generic DT bindings for PCI and ARM SMMU Robin Murphy
2016-02-29 13:46 ` Robin Murphy
     [not found] ` <cover.1456514380.git.robin.murphy-5wv7dgnIgG8@public.gmane.org>
2016-02-29 13:46   ` [PATCH 01/12] Docs: dt: add PCI IOMMU map bindings Robin Murphy
2016-02-29 13:46     ` Robin Murphy
     [not found]     ` <5bcbc7c0c8825cb0a2ac216bac0ab99722a84618.1456514380.git.robin.murphy-5wv7dgnIgG8@public.gmane.org>
2016-03-03 22:27       ` Rob Herring
2016-03-03 22:27         ` Rob Herring
2016-02-29 13:46   ` [PATCH 02/12] of/irq: Break out msi-map lookup (again) Robin Murphy
2016-02-29 13:46     ` Robin Murphy
2016-02-29 13:46   ` [PATCH 03/12] iommu/of: Handle iommu-map property for PCI Robin Murphy
2016-02-29 13:46     ` Robin Murphy
2016-02-29 13:46   ` [PATCH 04/12] arm64/dma-mapping: Extend DMA ops workaround to PCI devices Robin Murphy
2016-02-29 13:46     ` Robin Murphy
     [not found]     ` <ffcca878210c916962c562d3d1341aae93d593d0.1456514380.git.robin.murphy-5wv7dgnIgG8@public.gmane.org>
2016-02-29 16:47       ` Catalin Marinas
2016-02-29 16:47         ` Catalin Marinas
2016-02-29 13:46   ` [PATCH 05/12] arm64/dma-mapping: Remove default domain workaround Robin Murphy
2016-02-29 13:46     ` Robin Murphy
     [not found]     ` <698ecf1450da8b56a8f297b83815d2fe866ca0e1.1456514380.git.robin.murphy-5wv7dgnIgG8@public.gmane.org>
2016-02-29 16:51       ` Catalin Marinas
2016-02-29 16:51         ` Catalin Marinas
2016-02-29 13:46   ` [PATCH 06/12] iommu/arm-smmu: Streamline SMMU data lookup Robin Murphy
2016-02-29 13:46     ` Robin Murphy
2016-02-29 13:46   ` [PATCH 07/12] iommu/arm-smmu: Factor out mmu-masters handling Robin Murphy
2016-02-29 13:46     ` Robin Murphy
2016-02-29 13:46   ` [PATCH 08/12] iommu/arm-smmu: Refactor master/group config handling Robin Murphy
2016-02-29 13:46     ` Robin Murphy
2016-02-29 13:46   ` [PATCH 09/12] iommu/arm-smmu: Simplify mmu-masters handling Robin Murphy
2016-02-29 13:46     ` Robin Murphy
2016-02-29 13:46   ` [PATCH 10/12] Docs: dt: document ARM SMMU generic binding usage Robin Murphy
2016-02-29 13:46     ` Robin Murphy
     [not found]     ` <af1ce6d7d69e340ac839c69b475728f7aa50ff23.1456514380.git.robin.murphy-5wv7dgnIgG8@public.gmane.org>
2016-03-03 22:29       ` Rob Herring
2016-03-03 22:29         ` Rob Herring
2016-02-29 13:46   ` [PATCH 11/12] iommu/arm-smmu: Generic IOMMU DT bindings support Robin Murphy
2016-02-29 13:46     ` Robin Murphy
     [not found]     ` <a3edd4225fe8b39440fb9b6a70a70883d3cb103b.1456514380.git.robin.murphy-5wv7dgnIgG8@public.gmane.org>
2016-02-29 18:09       ` Sricharan
2016-02-29 18:09         ` Sricharan
2016-03-02 13:30         ` Robin Murphy
2016-03-02 13:30           ` Robin Murphy
2016-02-29 13:46   ` [PATCH 12/12] iommu/arm-smmu: Group platform devices appropriately Robin Murphy
2016-02-29 13:46     ` Robin Murphy

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.