All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v7 00/22] Generic DT bindings for PCI IOMMUs and ARM SMMU
@ 2016-09-12 16:13 ` Robin Murphy
  0 siblings, 0 replies; 104+ messages in thread
From: Robin Murphy @ 2016-09-12 16:13 UTC (permalink / raw)
  To: will.deacon-5wv7dgnIgG8, joro-zLv9SwRftAIdnm+yROfE0A,
	iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, lorenzo.pieralisi-5wv7dgnIgG8,
	jean-philippe.brucker-5wv7dgnIgG8, punit.agrawal-5wv7dgnIgG8,
	thunder.leizhen-hv44wF8Li93QT0dZR+AlfA,
	eric.auger-H+wXaHxf7aLQT0dZR+AlfA

Hi all,

To any more confusing fixups and crazily numbered extra patches, here's
a quick v7 with everything rebased into the right order. The significant
change this time is to implement iommu_fwspec properly from the start,
which ends up being far simpler and more robust than faffing about
introducing it somewhere 'less intrusive' to move toward core code later.

New branch in the logical place:

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

Robin.

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

Robin Murphy (21):
  of/irq: Break out msi-map lookup (again)
  iommu/of: Handle iommu-map property for PCI
  iommu: Introduce iommu_fwspec
  Docs: dt: document ARM SMMUv3 generic binding usage
  iommu/arm-smmu: Fall back to global bypass
  iommu/arm-smmu: Implement of_xlate() for SMMUv3
  iommu/arm-smmu: Support non-PCI devices with SMMUv3
  iommu/arm-smmu: Set PRIVCFG in stage 1 STEs
  iommu/arm-smmu: Handle stream IDs more dynamically
  iommu/arm-smmu: Consolidate stream map entry state
  iommu/arm-smmu: Keep track of S2CR state
  iommu/arm-smmu: Refactor mmu-masters handling
  iommu/arm-smmu: Streamline SMMU data lookups
  iommu/arm-smmu: Add a stream map entry iterator
  iommu/arm-smmu: Intelligent SMR allocation
  iommu/arm-smmu: Convert to iommu_fwspec
  Docs: dt: document ARM SMMU generic binding usage
  iommu/arm-smmu: Wire up generic configuration support
  iommu/arm-smmu: Set domain geometry
  iommu/dma: Add support for mapping MSIs
  iommu/dma: Avoid PCI host bridge windows

 .../devicetree/bindings/iommu/arm,smmu-v3.txt      |   8 +-
 .../devicetree/bindings/iommu/arm,smmu.txt         |  63 +-
 .../devicetree/bindings/pci/pci-iommu.txt          | 171 ++++
 arch/arm64/mm/dma-mapping.c                        |   2 +-
 drivers/gpu/drm/exynos/exynos_drm_iommu.h          |   2 +-
 drivers/iommu/Kconfig                              |   2 +-
 drivers/iommu/arm-smmu-v3.c                        | 386 +++++----
 drivers/iommu/arm-smmu.c                           | 962 ++++++++++-----------
 drivers/iommu/dma-iommu.c                          | 161 +++-
 drivers/iommu/iommu.c                              |  56 ++
 drivers/iommu/of_iommu.c                           |  52 +-
 drivers/irqchip/irq-gic-v2m.c                      |   3 +
 drivers/irqchip/irq-gic-v3-its.c                   |   3 +
 drivers/of/irq.c                                   |  78 +-
 drivers/of/of_pci.c                                | 102 +++
 include/linux/device.h                             |   3 +
 include/linux/dma-iommu.h                          |  12 +-
 include/linux/iommu.h                              |  38 +
 include/linux/of_pci.h                             |  10 +
 19 files changed, 1323 insertions(+), 791 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/pci/pci-iommu.txt

-- 
2.8.1.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	[flat|nested] 104+ messages in thread

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

Hi all,

To any more confusing fixups and crazily numbered extra patches, here's
a quick v7 with everything rebased into the right order. The significant
change this time is to implement iommu_fwspec properly from the start,
which ends up being far simpler and more robust than faffing about
introducing it somewhere 'less intrusive' to move toward core code later.

New branch in the logical place:

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

Robin.

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

Robin Murphy (21):
  of/irq: Break out msi-map lookup (again)
  iommu/of: Handle iommu-map property for PCI
  iommu: Introduce iommu_fwspec
  Docs: dt: document ARM SMMUv3 generic binding usage
  iommu/arm-smmu: Fall back to global bypass
  iommu/arm-smmu: Implement of_xlate() for SMMUv3
  iommu/arm-smmu: Support non-PCI devices with SMMUv3
  iommu/arm-smmu: Set PRIVCFG in stage 1 STEs
  iommu/arm-smmu: Handle stream IDs more dynamically
  iommu/arm-smmu: Consolidate stream map entry state
  iommu/arm-smmu: Keep track of S2CR state
  iommu/arm-smmu: Refactor mmu-masters handling
  iommu/arm-smmu: Streamline SMMU data lookups
  iommu/arm-smmu: Add a stream map entry iterator
  iommu/arm-smmu: Intelligent SMR allocation
  iommu/arm-smmu: Convert to iommu_fwspec
  Docs: dt: document ARM SMMU generic binding usage
  iommu/arm-smmu: Wire up generic configuration support
  iommu/arm-smmu: Set domain geometry
  iommu/dma: Add support for mapping MSIs
  iommu/dma: Avoid PCI host bridge windows

 .../devicetree/bindings/iommu/arm,smmu-v3.txt      |   8 +-
 .../devicetree/bindings/iommu/arm,smmu.txt         |  63 +-
 .../devicetree/bindings/pci/pci-iommu.txt          | 171 ++++
 arch/arm64/mm/dma-mapping.c                        |   2 +-
 drivers/gpu/drm/exynos/exynos_drm_iommu.h          |   2 +-
 drivers/iommu/Kconfig                              |   2 +-
 drivers/iommu/arm-smmu-v3.c                        | 386 +++++----
 drivers/iommu/arm-smmu.c                           | 962 ++++++++++-----------
 drivers/iommu/dma-iommu.c                          | 161 +++-
 drivers/iommu/iommu.c                              |  56 ++
 drivers/iommu/of_iommu.c                           |  52 +-
 drivers/irqchip/irq-gic-v2m.c                      |   3 +
 drivers/irqchip/irq-gic-v3-its.c                   |   3 +
 drivers/of/irq.c                                   |  78 +-
 drivers/of/of_pci.c                                | 102 +++
 include/linux/device.h                             |   3 +
 include/linux/dma-iommu.h                          |  12 +-
 include/linux/iommu.h                              |  38 +
 include/linux/of_pci.h                             |  10 +
 19 files changed, 1323 insertions(+), 791 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/pci/pci-iommu.txt

-- 
2.8.1.dirty

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

* [PATCH v7 01/22] Docs: dt: add PCI IOMMU map bindings
  2016-09-12 16:13 ` Robin Murphy
@ 2016-09-12 16:13     ` Robin Murphy
  -1 siblings, 0 replies; 104+ messages in thread
From: Robin Murphy @ 2016-09-12 16:13 UTC (permalink / raw)
  To: will.deacon-5wv7dgnIgG8, joro-zLv9SwRftAIdnm+yROfE0A,
	iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: Mark Rutland, devicetree-u79uwXL29TY76Z2rM5mHXA,
	punit.agrawal-5wv7dgnIgG8,
	thunder.leizhen-hv44wF8Li93QT0dZR+AlfA

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.

Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
Acked-by: Will Deacon <will.deacon-5wv7dgnIgG8@public.gmane.org>
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 000000000000..56c829621b9a
--- /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.8.1.dirty

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

* [PATCH v7 01/22] Docs: dt: add PCI IOMMU map bindings
@ 2016-09-12 16:13     ` Robin Murphy
  0 siblings, 0 replies; 104+ messages in thread
From: Robin Murphy @ 2016-09-12 16:13 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.

Acked-by: Rob Herring <robh@kernel.org>
Acked-by: Will Deacon <will.deacon@arm.com>
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 000000000000..56c829621b9a
--- /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.8.1.dirty

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

* [PATCH v7 02/22] of/irq: Break out msi-map lookup (again)
  2016-09-12 16:13 ` Robin Murphy
@ 2016-09-12 16:13     ` Robin Murphy
  -1 siblings, 0 replies; 104+ messages in thread
From: Robin Murphy @ 2016-09-12 16:13 UTC (permalink / raw)
  To: will.deacon-5wv7dgnIgG8, joro-zLv9SwRftAIdnm+yROfE0A,
	iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, lorenzo.pieralisi-5wv7dgnIgG8,
	jean-philippe.brucker-5wv7dgnIgG8, punit.agrawal-5wv7dgnIgG8,
	thunder.leizhen-hv44wF8Li93QT0dZR+AlfA,
	eric.auger-H+wXaHxf7aLQT0dZR+AlfA

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.

Acked-by: Rob Herring <robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
Acked-by: Marc Zyngier <marc.zyngier-5wv7dgnIgG8@public.gmane.org>
Tested-by: Lorenzo Pieralisi <lorenzo.pieralisi-5wv7dgnIgG8@public.gmane.org>
Signed-off-by: Robin Murphy <robin.murphy-5wv7dgnIgG8@public.gmane.org>
---
 drivers/of/irq.c       |  78 ++-----------------------------------
 drivers/of/of_pci.c    | 102 +++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/of_pci.h |  10 +++++
 3 files changed, 116 insertions(+), 74 deletions(-)

diff --git a/drivers/of/irq.c b/drivers/of/irq.c
index a2e68f740eda..393fea85eb4e 100644
--- a/drivers/of/irq.c
+++ b/drivers/of/irq.c
@@ -26,6 +26,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>
 
@@ -592,87 +593,16 @@ 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
 	 * "msi-map" property.
 	 */
-	for (parent_dev = dev; parent_dev; parent_dev = parent_dev->parent) {
-		if (!parent_dev->of_node)
-			continue;
-
-		msi_map = of_get_property(parent_dev->of_node,
-					  "msi-map", &msi_map_len);
-		if (!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. */
-		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;
+	for (parent_dev = dev; parent_dev; parent_dev = parent_dev->parent)
+		if (!of_pci_map_rid(parent_dev->of_node, rid_in, "msi-map",
+				    "msi-map-mask", np, &rid_out))
 			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 589b30c68e14..b58be12ab277 100644
--- a/drivers/of/of_pci.c
+++ b/drivers/of/of_pci.c
@@ -308,3 +308,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 */
+
+/**
+ * of_pci_map_rid - Translate a requester ID through a downstream mapping.
+ * @np: root complex device node.
+ * @rid: PCI requester ID to map.
+ * @map_name: property name of the map to use.
+ * @map_mask_name: optional property name of the mask 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. Either of @target or
+ * @id_out may be NULL if only the other is required. If @target points to
+ * a non-NULL device node pointer, only entries targeting that node will be
+ * matched; if it points to a NULL value, it will receive the device node of
+ * the first matching target phandle, with a reference held.
+ *
+ * Return: 0 on success or a standard error code on failure.
+ */
+int of_pci_map_rid(struct device_node *np, u32 rid,
+		   const char *map_name, const char *map_mask_name,
+		   struct device_node **target, u32 *id_out)
+{
+	u32 map_mask, masked_rid;
+	int map_len;
+	const __be32 *map = NULL;
+
+	if (!np || !map_name || (!target && !id_out))
+		return -EINVAL;
+
+	map = of_get_property(np, map_name, &map_len);
+	if (!map) {
+		if (target)
+			return -ENODEV;
+		/* Otherwise, no map implies no translation */
+		*id_out = rid;
+		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.
+	 */
+	if (map_mask_name)
+		of_property_read_u32(np, map_mask_name, &map_mask);
+
+	masked_rid = map_mask & rid;
+	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 -EFAULT;
+		}
+
+		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 (id_out)
+			*id_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, *id_out);
+		return 0;
+	}
+
+	pr_err("%s: Invalid %s translation - no match for rid 0x%x on %s\n",
+		np->full_name, map_name, rid,
+		target && *target ? (*target)->full_name : "any target");
+	return -EFAULT;
+}
diff --git a/include/linux/of_pci.h b/include/linux/of_pci.h
index b969e9443962..7fd5cfce9140 100644
--- a/include/linux/of_pci.h
+++ b/include/linux/of_pci.h
@@ -17,6 +17,9 @@ 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, u32 rid,
+		   const char *map_name, const char *map_mask_name,
+		   struct device_node **target, u32 *id_out);
 #else
 static inline int of_irq_parse_pci(const struct pci_dev *pdev, struct of_phandle_args *out_irq)
 {
@@ -52,6 +55,13 @@ of_get_pci_domain_nr(struct device_node *node)
 	return -1;
 }
 
+static inline int of_pci_map_rid(struct device_node *np, u32 rid,
+			const char *map_name, const char *map_mask_name,
+			struct device_node **target, u32 *id_out)
+{
+	return -EINVAL;
+}
+
 static inline void of_pci_check_probe_only(void) { }
 #endif
 
-- 
2.8.1.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] 104+ messages in thread

* [PATCH v7 02/22] of/irq: Break out msi-map lookup (again)
@ 2016-09-12 16:13     ` Robin Murphy
  0 siblings, 0 replies; 104+ messages in thread
From: Robin Murphy @ 2016-09-12 16:13 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.

Acked-by: Rob Herring <robh+dt@kernel.org>
Acked-by: Marc Zyngier <marc.zyngier@arm.com>
Tested-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Signed-off-by: Robin Murphy <robin.murphy@arm.com>
---
 drivers/of/irq.c       |  78 ++-----------------------------------
 drivers/of/of_pci.c    | 102 +++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/of_pci.h |  10 +++++
 3 files changed, 116 insertions(+), 74 deletions(-)

diff --git a/drivers/of/irq.c b/drivers/of/irq.c
index a2e68f740eda..393fea85eb4e 100644
--- a/drivers/of/irq.c
+++ b/drivers/of/irq.c
@@ -26,6 +26,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>
 
@@ -592,87 +593,16 @@ 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
 	 * "msi-map" property.
 	 */
-	for (parent_dev = dev; parent_dev; parent_dev = parent_dev->parent) {
-		if (!parent_dev->of_node)
-			continue;
-
-		msi_map = of_get_property(parent_dev->of_node,
-					  "msi-map", &msi_map_len);
-		if (!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. */
-		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;
+	for (parent_dev = dev; parent_dev; parent_dev = parent_dev->parent)
+		if (!of_pci_map_rid(parent_dev->of_node, rid_in, "msi-map",
+				    "msi-map-mask", np, &rid_out))
 			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 589b30c68e14..b58be12ab277 100644
--- a/drivers/of/of_pci.c
+++ b/drivers/of/of_pci.c
@@ -308,3 +308,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 */
+
+/**
+ * of_pci_map_rid - Translate a requester ID through a downstream mapping.
+ * @np: root complex device node.
+ * @rid: PCI requester ID to map.
+ * @map_name: property name of the map to use.
+ * @map_mask_name: optional property name of the mask 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. Either of @target or
+ * @id_out may be NULL if only the other is required. If @target points to
+ * a non-NULL device node pointer, only entries targeting that node will be
+ * matched; if it points to a NULL value, it will receive the device node of
+ * the first matching target phandle, with a reference held.
+ *
+ * Return: 0 on success or a standard error code on failure.
+ */
+int of_pci_map_rid(struct device_node *np, u32 rid,
+		   const char *map_name, const char *map_mask_name,
+		   struct device_node **target, u32 *id_out)
+{
+	u32 map_mask, masked_rid;
+	int map_len;
+	const __be32 *map = NULL;
+
+	if (!np || !map_name || (!target && !id_out))
+		return -EINVAL;
+
+	map = of_get_property(np, map_name, &map_len);
+	if (!map) {
+		if (target)
+			return -ENODEV;
+		/* Otherwise, no map implies no translation */
+		*id_out = rid;
+		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.
+	 */
+	if (map_mask_name)
+		of_property_read_u32(np, map_mask_name, &map_mask);
+
+	masked_rid = map_mask & rid;
+	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 -EFAULT;
+		}
+
+		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 (id_out)
+			*id_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, *id_out);
+		return 0;
+	}
+
+	pr_err("%s: Invalid %s translation - no match for rid 0x%x on %s\n",
+		np->full_name, map_name, rid,
+		target && *target ? (*target)->full_name : "any target");
+	return -EFAULT;
+}
diff --git a/include/linux/of_pci.h b/include/linux/of_pci.h
index b969e9443962..7fd5cfce9140 100644
--- a/include/linux/of_pci.h
+++ b/include/linux/of_pci.h
@@ -17,6 +17,9 @@ 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, u32 rid,
+		   const char *map_name, const char *map_mask_name,
+		   struct device_node **target, u32 *id_out);
 #else
 static inline int of_irq_parse_pci(const struct pci_dev *pdev, struct of_phandle_args *out_irq)
 {
@@ -52,6 +55,13 @@ of_get_pci_domain_nr(struct device_node *node)
 	return -1;
 }
 
+static inline int of_pci_map_rid(struct device_node *np, u32 rid,
+			const char *map_name, const char *map_mask_name,
+			struct device_node **target, u32 *id_out)
+{
+	return -EINVAL;
+}
+
 static inline void of_pci_check_probe_only(void) { }
 #endif
 
-- 
2.8.1.dirty

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

* [PATCH v7 03/22] iommu/of: Handle iommu-map property for PCI
  2016-09-12 16:13 ` Robin Murphy
@ 2016-09-12 16:13     ` Robin Murphy
  -1 siblings, 0 replies; 104+ messages in thread
From: Robin Murphy @ 2016-09-12 16:13 UTC (permalink / raw)
  To: will.deacon-5wv7dgnIgG8, joro-zLv9SwRftAIdnm+yROfE0A,
	iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, lorenzo.pieralisi-5wv7dgnIgG8,
	jean-philippe.brucker-5wv7dgnIgG8, punit.agrawal-5wv7dgnIgG8,
	thunder.leizhen-hv44wF8Li93QT0dZR+AlfA,
	eric.auger-H+wXaHxf7aLQT0dZR+AlfA

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.

Reviewed-by: Will Deacon <will.deacon-5wv7dgnIgG8@public.gmane.org>
Signed-off-by: Robin Murphy <robin.murphy-5wv7dgnIgG8@public.gmane.org>

---

- Split PCI handling into a separate function
---
 drivers/iommu/of_iommu.c | 46 +++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 41 insertions(+), 5 deletions(-)

diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c
index 57f23eaaa2f9..19e1e8f2f871 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,6 +135,45 @@ const 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;
+
+	iommu_spec->args[0] = alias;
+	return iommu_spec->np == pdev->bus->dev.of_node;
+}
+
+static const struct iommu_ops
+*of_pci_iommu_configure(struct pci_dev *pdev, struct device_node *bridge_np)
+{
+	const struct iommu_ops *ops;
+	struct of_phandle_args iommu_spec;
+
+	/*
+	 * Start by tracing the RID alias down the PCI topology as
+	 * far as the host bridge whose OF node we have...
+	 * (we're not even attempting to handle multi-alias devices yet)
+	 */
+	iommu_spec.args_count = 1;
+	iommu_spec.np = bridge_np;
+	pci_for_each_dma_alias(pdev, __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.
+	 */
+	iommu_spec.np = NULL;
+	if (of_pci_map_rid(bridge_np, iommu_spec.args[0], "iommu-map",
+			   "iommu-map-mask", &iommu_spec.np, iommu_spec.args))
+		return NULL;
+
+	ops = of_iommu_get_ops(iommu_spec.np);
+	if (!ops || !ops->of_xlate || ops->of_xlate(&pdev->dev, &iommu_spec))
+		ops = NULL;
+
+	of_node_put(iommu_spec.np);
+	return ops;
+}
+
 const struct iommu_ops *of_iommu_configure(struct device *dev,
 					   struct device_node *master_np)
 {
@@ -142,12 +182,8 @@ const struct iommu_ops *of_iommu_configure(struct device *dev,
 	const 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;
+		return of_pci_iommu_configure(to_pci_dev(dev), master_np);
 
 	/*
 	 * We don't currently walk up the tree looking for a parent IOMMU.
-- 
2.8.1.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] 104+ messages in thread

* [PATCH v7 03/22] iommu/of: Handle iommu-map property for PCI
@ 2016-09-12 16:13     ` Robin Murphy
  0 siblings, 0 replies; 104+ messages in thread
From: Robin Murphy @ 2016-09-12 16:13 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.

Reviewed-by: Will Deacon <will.deacon@arm.com>
Signed-off-by: Robin Murphy <robin.murphy@arm.com>

---

- Split PCI handling into a separate function
---
 drivers/iommu/of_iommu.c | 46 +++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 41 insertions(+), 5 deletions(-)

diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c
index 57f23eaaa2f9..19e1e8f2f871 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,6 +135,45 @@ const 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;
+
+	iommu_spec->args[0] = alias;
+	return iommu_spec->np == pdev->bus->dev.of_node;
+}
+
+static const struct iommu_ops
+*of_pci_iommu_configure(struct pci_dev *pdev, struct device_node *bridge_np)
+{
+	const struct iommu_ops *ops;
+	struct of_phandle_args iommu_spec;
+
+	/*
+	 * Start by tracing the RID alias down the PCI topology as
+	 * far as the host bridge whose OF node we have...
+	 * (we're not even attempting to handle multi-alias devices yet)
+	 */
+	iommu_spec.args_count = 1;
+	iommu_spec.np = bridge_np;
+	pci_for_each_dma_alias(pdev, __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.
+	 */
+	iommu_spec.np = NULL;
+	if (of_pci_map_rid(bridge_np, iommu_spec.args[0], "iommu-map",
+			   "iommu-map-mask", &iommu_spec.np, iommu_spec.args))
+		return NULL;
+
+	ops = of_iommu_get_ops(iommu_spec.np);
+	if (!ops || !ops->of_xlate || ops->of_xlate(&pdev->dev, &iommu_spec))
+		ops = NULL;
+
+	of_node_put(iommu_spec.np);
+	return ops;
+}
+
 const struct iommu_ops *of_iommu_configure(struct device *dev,
 					   struct device_node *master_np)
 {
@@ -142,12 +182,8 @@ const struct iommu_ops *of_iommu_configure(struct device *dev,
 	const 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;
+		return of_pci_iommu_configure(to_pci_dev(dev), master_np);
 
 	/*
 	 * We don't currently walk up the tree looking for a parent IOMMU.
-- 
2.8.1.dirty

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

* [PATCH v7 04/22] iommu: Introduce iommu_fwspec
  2016-09-12 16:13 ` Robin Murphy
@ 2016-09-12 16:13     ` Robin Murphy
  -1 siblings, 0 replies; 104+ messages in thread
From: Robin Murphy @ 2016-09-12 16:13 UTC (permalink / raw)
  To: will.deacon-5wv7dgnIgG8, joro-zLv9SwRftAIdnm+yROfE0A,
	iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, lorenzo.pieralisi-5wv7dgnIgG8,
	jean-philippe.brucker-5wv7dgnIgG8, punit.agrawal-5wv7dgnIgG8,
	thunder.leizhen-hv44wF8Li93QT0dZR+AlfA,
	eric.auger-H+wXaHxf7aLQT0dZR+AlfA, Greg Kroah-Hartman

Introduce a common structure to hold the per-device firmware data that
most IOMMU drivers need to keep track of. This enables us to configure
much of that data from common firmware code, and consolidate a lot of
the equivalent implementations, device look-up tables, etc. which are
currently strewn across IOMMU drivers.

This will also be enable us to address the outstanding "multiple IOMMUs
on the platform bus" problem by tweaking IOMMU API calls to prefer
dev->fwspec->ops before falling back to dev->bus->iommu_ops, and thus
gracefully handle those troublesome systems which we currently cannot.

As the first user, hook up the OF IOMMU configuration mechanism. The
driver-defined nature of DT cells means that we still need the drivers
to translate and add the IDs themselves, but future users such as the
much less free-form ACPI IORT will be much simpler and self-contained.

CC: Greg Kroah-Hartman <gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r@public.gmane.org>
Suggested-by: Will Deacon <will.deacon-5wv7dgnIgG8@public.gmane.org>
Signed-off-by: Robin Murphy <robin.murphy-5wv7dgnIgG8@public.gmane.org>

---

- Drop the 'gradual introduction' fiddling and go straight to struct
  device and common code, as it prevents all the silly build issues
  and ultimately makes life simpler for everyone
---
 drivers/iommu/iommu.c    | 56 ++++++++++++++++++++++++++++++++++++++++++++++++
 drivers/iommu/of_iommu.c |  8 +++++--
 include/linux/device.h   |  3 +++
 include/linux/iommu.h    | 38 ++++++++++++++++++++++++++++++++
 4 files changed, 103 insertions(+), 2 deletions(-)

diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index b06d93594436..816e320d3ad8 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -31,6 +31,7 @@
 #include <linux/err.h>
 #include <linux/pci.h>
 #include <linux/bitops.h>
+#include <linux/property.h>
 #include <trace/events/iommu.h>
 
 static struct kset *iommu_group_kset;
@@ -1613,3 +1614,58 @@ out:
 
 	return ret;
 }
+
+int iommu_fwspec_init(struct device *dev, struct fwnode_handle *iommu_fwnode,
+		      const struct iommu_ops *ops)
+{
+	struct iommu_fwspec *fwspec = dev->iommu_fwspec;
+
+	if (fwspec)
+		return ops == fwspec->ops ? 0 : -EINVAL;
+
+	fwspec = kzalloc(sizeof(*fwspec), GFP_KERNEL);
+	if (!fwspec)
+		return -ENOMEM;
+
+	of_node_get(to_of_node(iommu_fwnode));
+	fwspec->iommu_fwnode = iommu_fwnode;
+	fwspec->ops = ops;
+	dev->iommu_fwspec = fwspec;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(iommu_fwspec_init);
+
+void iommu_fwspec_free(struct device *dev)
+{
+	struct iommu_fwspec *fwspec = dev->iommu_fwspec;
+
+	if (fwspec) {
+		fwnode_handle_put(fwspec->iommu_fwnode);
+		kfree(fwspec);
+		dev->iommu_fwspec = NULL;
+	}
+}
+EXPORT_SYMBOL_GPL(iommu_fwspec_free);
+
+int iommu_fwspec_add_ids(struct device *dev, u32 *ids, int num_ids)
+{
+	struct iommu_fwspec *fwspec = dev->iommu_fwspec;
+	size_t size;
+	int i;
+
+	if (!fwspec)
+		return -EINVAL;
+
+	size = offsetof(struct iommu_fwspec, ids[fwspec->num_ids + num_ids]);
+	fwspec = krealloc(dev->iommu_fwspec, size, GFP_KERNEL);
+	if (!fwspec)
+		return -ENOMEM;
+
+	for (i = 0; i < num_ids; i++)
+		fwspec->ids[fwspec->num_ids + i] = ids[i];
+
+	fwspec->num_ids += num_ids;
+	dev->iommu_fwspec = fwspec;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(iommu_fwspec_add_ids);
diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c
index 19e1e8f2f871..5b82862f571f 100644
--- a/drivers/iommu/of_iommu.c
+++ b/drivers/iommu/of_iommu.c
@@ -167,7 +167,9 @@ static const struct iommu_ops
 		return NULL;
 
 	ops = of_iommu_get_ops(iommu_spec.np);
-	if (!ops || !ops->of_xlate || ops->of_xlate(&pdev->dev, &iommu_spec))
+	if (!ops || !ops->of_xlate ||
+	    iommu_fwspec_init(&pdev->dev, &iommu_spec.np->fwnode, ops) ||
+	    ops->of_xlate(&pdev->dev, &iommu_spec))
 		ops = NULL;
 
 	of_node_put(iommu_spec.np);
@@ -196,7 +198,9 @@ const struct iommu_ops *of_iommu_configure(struct device *dev,
 		np = iommu_spec.np;
 		ops = of_iommu_get_ops(np);
 
-		if (!ops || !ops->of_xlate || ops->of_xlate(dev, &iommu_spec))
+		if (!ops || !ops->of_xlate ||
+		    iommu_fwspec_init(dev, &np->fwnode, ops) ||
+		    ops->of_xlate(dev, &iommu_spec))
 			goto err_put_node;
 
 		of_node_put(np);
diff --git a/include/linux/device.h b/include/linux/device.h
index 38f02814d53a..bc41e87a969b 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -41,6 +41,7 @@ struct device_node;
 struct fwnode_handle;
 struct iommu_ops;
 struct iommu_group;
+struct iommu_fwspec;
 
 struct bus_attribute {
 	struct attribute	attr;
@@ -765,6 +766,7 @@ struct device_dma_parameters {
  * 		gone away. This should be set by the allocator of the
  * 		device (i.e. the bus driver that discovered the device).
  * @iommu_group: IOMMU group the device belongs to.
+ * @iommu_fwspec: IOMMU-specific properties supplied by firmware.
  *
  * @offline_disabled: If set, the device is permanently online.
  * @offline:	Set after successful invocation of bus type's .offline().
@@ -849,6 +851,7 @@ struct device {
 
 	void	(*release)(struct device *dev);
 	struct iommu_group	*iommu_group;
+	struct iommu_fwspec	*iommu_fwspec;
 
 	bool			offline_disabled:1;
 	bool			offline:1;
diff --git a/include/linux/iommu.h b/include/linux/iommu.h
index a35fb8b42e1a..2ea15842e9f6 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -331,10 +331,32 @@ extern struct iommu_group *pci_device_group(struct device *dev);
 /* Generic device grouping function */
 extern struct iommu_group *generic_device_group(struct device *dev);
 
+/**
+ * struct iommu_fwspec - per-device IOMMU instance data
+ * @ops: ops for this device's IOMMU
+ * @iommu_fwnode: firmware handle for this device's IOMMU
+ * @iommu_priv: IOMMU driver private data for this device
+ * @num_ids: number of associated device IDs
+ * @ids: IDs which this device may present to the IOMMU
+ */
+struct iommu_fwspec {
+	const struct iommu_ops	*ops;
+	struct fwnode_handle	*iommu_fwnode;
+	void			*iommu_priv;
+	unsigned int		num_ids;
+	u32			ids[];
+};
+
+int iommu_fwspec_init(struct device *dev, struct fwnode_handle *iommu_fwnode,
+		      const struct iommu_ops *ops);
+void iommu_fwspec_free(struct device *dev);
+int iommu_fwspec_add_ids(struct device *dev, u32 *ids, int num_ids);
+
 #else /* CONFIG_IOMMU_API */
 
 struct iommu_ops {};
 struct iommu_group {};
+struct iommu_fwspec {};
 
 static inline bool iommu_present(struct bus_type *bus)
 {
@@ -541,6 +563,22 @@ static inline void iommu_device_unlink(struct device *dev, struct device *link)
 {
 }
 
+static inline int iommu_fwspec_init(struct device *dev,
+				    struct fwnode_handle *iommu_fwnode,
+				    const struct iommu_ops *ops)
+{
+	return -ENODEV;
+}
+
+void iommu_fwspec_free(struct device *dev)
+{
+}
+
+int iommu_fwspec_add_ids(struct device *dev, u32 *ids, int num_ids)
+{
+	return -ENODEV;
+}
+
 #endif /* CONFIG_IOMMU_API */
 
 #endif /* __LINUX_IOMMU_H */
-- 
2.8.1.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] 104+ messages in thread

* [PATCH v7 04/22] iommu: Introduce iommu_fwspec
@ 2016-09-12 16:13     ` Robin Murphy
  0 siblings, 0 replies; 104+ messages in thread
From: Robin Murphy @ 2016-09-12 16:13 UTC (permalink / raw)
  To: linux-arm-kernel

Introduce a common structure to hold the per-device firmware data that
most IOMMU drivers need to keep track of. This enables us to configure
much of that data from common firmware code, and consolidate a lot of
the equivalent implementations, device look-up tables, etc. which are
currently strewn across IOMMU drivers.

This will also be enable us to address the outstanding "multiple IOMMUs
on the platform bus" problem by tweaking IOMMU API calls to prefer
dev->fwspec->ops before falling back to dev->bus->iommu_ops, and thus
gracefully handle those troublesome systems which we currently cannot.

As the first user, hook up the OF IOMMU configuration mechanism. The
driver-defined nature of DT cells means that we still need the drivers
to translate and add the IDs themselves, but future users such as the
much less free-form ACPI IORT will be much simpler and self-contained.

CC: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Suggested-by: Will Deacon <will.deacon@arm.com>
Signed-off-by: Robin Murphy <robin.murphy@arm.com>

---

- Drop the 'gradual introduction' fiddling and go straight to struct
  device and common code, as it prevents all the silly build issues
  and ultimately makes life simpler for everyone
---
 drivers/iommu/iommu.c    | 56 ++++++++++++++++++++++++++++++++++++++++++++++++
 drivers/iommu/of_iommu.c |  8 +++++--
 include/linux/device.h   |  3 +++
 include/linux/iommu.h    | 38 ++++++++++++++++++++++++++++++++
 4 files changed, 103 insertions(+), 2 deletions(-)

diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index b06d93594436..816e320d3ad8 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -31,6 +31,7 @@
 #include <linux/err.h>
 #include <linux/pci.h>
 #include <linux/bitops.h>
+#include <linux/property.h>
 #include <trace/events/iommu.h>
 
 static struct kset *iommu_group_kset;
@@ -1613,3 +1614,58 @@ out:
 
 	return ret;
 }
+
+int iommu_fwspec_init(struct device *dev, struct fwnode_handle *iommu_fwnode,
+		      const struct iommu_ops *ops)
+{
+	struct iommu_fwspec *fwspec = dev->iommu_fwspec;
+
+	if (fwspec)
+		return ops == fwspec->ops ? 0 : -EINVAL;
+
+	fwspec = kzalloc(sizeof(*fwspec), GFP_KERNEL);
+	if (!fwspec)
+		return -ENOMEM;
+
+	of_node_get(to_of_node(iommu_fwnode));
+	fwspec->iommu_fwnode = iommu_fwnode;
+	fwspec->ops = ops;
+	dev->iommu_fwspec = fwspec;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(iommu_fwspec_init);
+
+void iommu_fwspec_free(struct device *dev)
+{
+	struct iommu_fwspec *fwspec = dev->iommu_fwspec;
+
+	if (fwspec) {
+		fwnode_handle_put(fwspec->iommu_fwnode);
+		kfree(fwspec);
+		dev->iommu_fwspec = NULL;
+	}
+}
+EXPORT_SYMBOL_GPL(iommu_fwspec_free);
+
+int iommu_fwspec_add_ids(struct device *dev, u32 *ids, int num_ids)
+{
+	struct iommu_fwspec *fwspec = dev->iommu_fwspec;
+	size_t size;
+	int i;
+
+	if (!fwspec)
+		return -EINVAL;
+
+	size = offsetof(struct iommu_fwspec, ids[fwspec->num_ids + num_ids]);
+	fwspec = krealloc(dev->iommu_fwspec, size, GFP_KERNEL);
+	if (!fwspec)
+		return -ENOMEM;
+
+	for (i = 0; i < num_ids; i++)
+		fwspec->ids[fwspec->num_ids + i] = ids[i];
+
+	fwspec->num_ids += num_ids;
+	dev->iommu_fwspec = fwspec;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(iommu_fwspec_add_ids);
diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c
index 19e1e8f2f871..5b82862f571f 100644
--- a/drivers/iommu/of_iommu.c
+++ b/drivers/iommu/of_iommu.c
@@ -167,7 +167,9 @@ static const struct iommu_ops
 		return NULL;
 
 	ops = of_iommu_get_ops(iommu_spec.np);
-	if (!ops || !ops->of_xlate || ops->of_xlate(&pdev->dev, &iommu_spec))
+	if (!ops || !ops->of_xlate ||
+	    iommu_fwspec_init(&pdev->dev, &iommu_spec.np->fwnode, ops) ||
+	    ops->of_xlate(&pdev->dev, &iommu_spec))
 		ops = NULL;
 
 	of_node_put(iommu_spec.np);
@@ -196,7 +198,9 @@ const struct iommu_ops *of_iommu_configure(struct device *dev,
 		np = iommu_spec.np;
 		ops = of_iommu_get_ops(np);
 
-		if (!ops || !ops->of_xlate || ops->of_xlate(dev, &iommu_spec))
+		if (!ops || !ops->of_xlate ||
+		    iommu_fwspec_init(dev, &np->fwnode, ops) ||
+		    ops->of_xlate(dev, &iommu_spec))
 			goto err_put_node;
 
 		of_node_put(np);
diff --git a/include/linux/device.h b/include/linux/device.h
index 38f02814d53a..bc41e87a969b 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -41,6 +41,7 @@ struct device_node;
 struct fwnode_handle;
 struct iommu_ops;
 struct iommu_group;
+struct iommu_fwspec;
 
 struct bus_attribute {
 	struct attribute	attr;
@@ -765,6 +766,7 @@ struct device_dma_parameters {
  * 		gone away. This should be set by the allocator of the
  * 		device (i.e. the bus driver that discovered the device).
  * @iommu_group: IOMMU group the device belongs to.
+ * @iommu_fwspec: IOMMU-specific properties supplied by firmware.
  *
  * @offline_disabled: If set, the device is permanently online.
  * @offline:	Set after successful invocation of bus type's .offline().
@@ -849,6 +851,7 @@ struct device {
 
 	void	(*release)(struct device *dev);
 	struct iommu_group	*iommu_group;
+	struct iommu_fwspec	*iommu_fwspec;
 
 	bool			offline_disabled:1;
 	bool			offline:1;
diff --git a/include/linux/iommu.h b/include/linux/iommu.h
index a35fb8b42e1a..2ea15842e9f6 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -331,10 +331,32 @@ extern struct iommu_group *pci_device_group(struct device *dev);
 /* Generic device grouping function */
 extern struct iommu_group *generic_device_group(struct device *dev);
 
+/**
+ * struct iommu_fwspec - per-device IOMMU instance data
+ * @ops: ops for this device's IOMMU
+ * @iommu_fwnode: firmware handle for this device's IOMMU
+ * @iommu_priv: IOMMU driver private data for this device
+ * @num_ids: number of associated device IDs
+ * @ids: IDs which this device may present to the IOMMU
+ */
+struct iommu_fwspec {
+	const struct iommu_ops	*ops;
+	struct fwnode_handle	*iommu_fwnode;
+	void			*iommu_priv;
+	unsigned int		num_ids;
+	u32			ids[];
+};
+
+int iommu_fwspec_init(struct device *dev, struct fwnode_handle *iommu_fwnode,
+		      const struct iommu_ops *ops);
+void iommu_fwspec_free(struct device *dev);
+int iommu_fwspec_add_ids(struct device *dev, u32 *ids, int num_ids);
+
 #else /* CONFIG_IOMMU_API */
 
 struct iommu_ops {};
 struct iommu_group {};
+struct iommu_fwspec {};
 
 static inline bool iommu_present(struct bus_type *bus)
 {
@@ -541,6 +563,22 @@ static inline void iommu_device_unlink(struct device *dev, struct device *link)
 {
 }
 
+static inline int iommu_fwspec_init(struct device *dev,
+				    struct fwnode_handle *iommu_fwnode,
+				    const struct iommu_ops *ops)
+{
+	return -ENODEV;
+}
+
+void iommu_fwspec_free(struct device *dev)
+{
+}
+
+int iommu_fwspec_add_ids(struct device *dev, u32 *ids, int num_ids)
+{
+	return -ENODEV;
+}
+
 #endif /* CONFIG_IOMMU_API */
 
 #endif /* __LINUX_IOMMU_H */
-- 
2.8.1.dirty

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

* [PATCH v7 05/22] Docs: dt: document ARM SMMUv3 generic binding usage
  2016-09-12 16:13 ` Robin Murphy
@ 2016-09-12 16:13     ` Robin Murphy
  -1 siblings, 0 replies; 104+ messages in thread
From: Robin Murphy @ 2016-09-12 16:13 UTC (permalink / raw)
  To: will.deacon-5wv7dgnIgG8, joro-zLv9SwRftAIdnm+yROfE0A,
	iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, lorenzo.pieralisi-5wv7dgnIgG8,
	jean-philippe.brucker-5wv7dgnIgG8, punit.agrawal-5wv7dgnIgG8,
	thunder.leizhen-hv44wF8Li93QT0dZR+AlfA,
	eric.auger-H+wXaHxf7aLQT0dZR+AlfA, Rob Herring, Mark Rutland

We're about to ratify our use of the generic binding, so document it.

CC: Rob Herring <robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
CC: Mark Rutland <mark.rutland-5wv7dgnIgG8@public.gmane.org>
Signed-off-by: Robin Murphy <robin.murphy-5wv7dgnIgG8@public.gmane.org>

---

- Reference PCI "iommu-map" binding instead, as that's our main concern
- Fix "IDs" typo
---
 Documentation/devicetree/bindings/iommu/arm,smmu-v3.txt | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/iommu/arm,smmu-v3.txt b/Documentation/devicetree/bindings/iommu/arm,smmu-v3.txt
index 7b94c88cf2ee..be57550e14e4 100644
--- a/Documentation/devicetree/bindings/iommu/arm,smmu-v3.txt
+++ b/Documentation/devicetree/bindings/iommu/arm,smmu-v3.txt
@@ -27,6 +27,12 @@ the PCIe specification.
                       * "cmdq-sync" - CMD_SYNC complete
                       * "gerror"    - Global Error activated
 
+- #iommu-cells      : See the generic IOMMU binding described in
+                        devicetree/bindings/pci/pci-iommu.txt
+                      for details. For SMMUv3, must be 1, with each cell
+                      describing a single stream ID. All possible stream
+                      IDs which a device may emit must be described.
+
 ** SMMUv3 optional properties:
 
 - dma-coherent      : Present if DMA operations made by the SMMU (page
@@ -54,6 +60,6 @@ the PCIe specification.
                              <GIC_SPI 79 IRQ_TYPE_EDGE_RISING>;
                 interrupt-names = "eventq", "priq", "cmdq-sync", "gerror";
                 dma-coherent;
-                #iommu-cells = <0>;
+                #iommu-cells = <1>;
                 msi-parent = <&its 0xff0000>;
         };
-- 
2.8.1.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] 104+ messages in thread

* [PATCH v7 05/22] Docs: dt: document ARM SMMUv3 generic binding usage
@ 2016-09-12 16:13     ` Robin Murphy
  0 siblings, 0 replies; 104+ messages in thread
From: Robin Murphy @ 2016-09-12 16:13 UTC (permalink / raw)
  To: linux-arm-kernel

We're about to ratify our use of the generic binding, so document it.

CC: Rob Herring <robh+dt@kernel.org>
CC: Mark Rutland <mark.rutland@arm.com>
Signed-off-by: Robin Murphy <robin.murphy@arm.com>

---

- Reference PCI "iommu-map" binding instead, as that's our main concern
- Fix "IDs" typo
---
 Documentation/devicetree/bindings/iommu/arm,smmu-v3.txt | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/iommu/arm,smmu-v3.txt b/Documentation/devicetree/bindings/iommu/arm,smmu-v3.txt
index 7b94c88cf2ee..be57550e14e4 100644
--- a/Documentation/devicetree/bindings/iommu/arm,smmu-v3.txt
+++ b/Documentation/devicetree/bindings/iommu/arm,smmu-v3.txt
@@ -27,6 +27,12 @@ the PCIe specification.
                       * "cmdq-sync" - CMD_SYNC complete
                       * "gerror"    - Global Error activated
 
+- #iommu-cells      : See the generic IOMMU binding described in
+                        devicetree/bindings/pci/pci-iommu.txt
+                      for details. For SMMUv3, must be 1, with each cell
+                      describing a single stream ID. All possible stream
+                      IDs which a device may emit must be described.
+
 ** SMMUv3 optional properties:
 
 - dma-coherent      : Present if DMA operations made by the SMMU (page
@@ -54,6 +60,6 @@ the PCIe specification.
                              <GIC_SPI 79 IRQ_TYPE_EDGE_RISING>;
                 interrupt-names = "eventq", "priq", "cmdq-sync", "gerror";
                 dma-coherent;
-                #iommu-cells = <0>;
+                #iommu-cells = <1>;
                 msi-parent = <&its 0xff0000>;
         };
-- 
2.8.1.dirty

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

* [PATCH v7 06/22] iommu/arm-smmu: Fall back to global bypass
  2016-09-12 16:13 ` Robin Murphy
@ 2016-09-12 16:13     ` Robin Murphy
  -1 siblings, 0 replies; 104+ messages in thread
From: Robin Murphy @ 2016-09-12 16:13 UTC (permalink / raw)
  To: will.deacon-5wv7dgnIgG8, joro-zLv9SwRftAIdnm+yROfE0A,
	iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, punit.agrawal-5wv7dgnIgG8,
	thunder.leizhen-hv44wF8Li93QT0dZR+AlfA

Unlike SMMUv2, SMMUv3 has no easy way to bypass unknown stream IDs,
other than allocating and filling in the entire stream table with bypass
entries, which for some configurations would waste *gigabytes* of RAM.
Otherwise, all transactions on unknown stream IDs will simply be aborted
with a C_BAD_STREAMID event.

Rather than render the system unusable in the case of an invalid DT,
avoid enabling the SMMU altogether such that everything bypasses
(though letting the explicit disable_bypass option take precedence).

Signed-off-by: Robin Murphy <robin.murphy-5wv7dgnIgG8@public.gmane.org>

---

- Implement proper GBPA update procedure per the spec
---
 drivers/iommu/arm-smmu-v3.c | 48 +++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 44 insertions(+), 4 deletions(-)

diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c
index 5db6931c715c..d7fef5f99bfc 100644
--- a/drivers/iommu/arm-smmu-v3.c
+++ b/drivers/iommu/arm-smmu-v3.c
@@ -123,6 +123,10 @@
 #define CR2_RECINVSID			(1 << 1)
 #define CR2_E2H				(1 << 0)
 
+#define ARM_SMMU_GBPA			0x44
+#define GBPA_ABORT			(1 << 20)
+#define GBPA_UPDATE			(1 << 31)
+
 #define ARM_SMMU_IRQ_CTRL		0x50
 #define IRQ_CTRL_EVTQ_IRQEN		(1 << 2)
 #define IRQ_CTRL_PRIQ_IRQEN		(1 << 1)
@@ -2124,6 +2128,24 @@ static int arm_smmu_write_reg_sync(struct arm_smmu_device *smmu, u32 val,
 					  1, ARM_SMMU_POLL_TIMEOUT_US);
 }
 
+/* GBPA is "special" */
+static int arm_smmu_update_gbpa(struct arm_smmu_device *smmu, u32 set, u32 clr)
+{
+	int ret;
+	u32 reg, __iomem *gbpa = smmu->base + ARM_SMMU_GBPA;
+
+	ret = readl_relaxed_poll_timeout(gbpa, reg, !(reg & GBPA_UPDATE),
+					 1, ARM_SMMU_POLL_TIMEOUT_US);
+	if (ret)
+		return ret;
+
+	reg &= ~clr;
+	reg |= set;
+	writel_relaxed(reg | GBPA_UPDATE, gbpa);
+	return readl_relaxed_poll_timeout(gbpa, reg, !(reg & GBPA_UPDATE),
+					  1, ARM_SMMU_POLL_TIMEOUT_US);
+}
+
 static void arm_smmu_free_msis(void *data)
 {
 	struct device *dev = data;
@@ -2269,7 +2291,7 @@ static int arm_smmu_device_disable(struct arm_smmu_device *smmu)
 	return ret;
 }
 
-static int arm_smmu_device_reset(struct arm_smmu_device *smmu)
+static int arm_smmu_device_reset(struct arm_smmu_device *smmu, bool bypass)
 {
 	int ret;
 	u32 reg, enables;
@@ -2370,8 +2392,17 @@ static int arm_smmu_device_reset(struct arm_smmu_device *smmu)
 		return ret;
 	}
 
-	/* Enable the SMMU interface */
-	enables |= CR0_SMMUEN;
+
+	/* Enable the SMMU interface, or ensure bypass */
+	if (!bypass || disable_bypass) {
+		enables |= CR0_SMMUEN;
+	} else {
+		ret = arm_smmu_update_gbpa(smmu, 0, GBPA_ABORT);
+		if (ret) {
+			dev_err(smmu->dev, "GBPA not responding to update\n");
+			return ret;
+		}
+	}
 	ret = arm_smmu_write_reg_sync(smmu, enables, ARM_SMMU_CR0,
 				      ARM_SMMU_CR0ACK);
 	if (ret) {
@@ -2570,6 +2601,15 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
 	struct resource *res;
 	struct arm_smmu_device *smmu;
 	struct device *dev = &pdev->dev;
+	bool bypass = true;
+	u32 cells;
+
+	if (of_property_read_u32(dev->of_node, "#iommu-cells", &cells))
+		dev_err(dev, "missing #iommu-cells property\n");
+	else if (cells != 1)
+		dev_err(dev, "invalid #iommu-cells value (%d)\n", cells);
+	else
+		bypass = false;
 
 	smmu = devm_kzalloc(dev, sizeof(*smmu), GFP_KERNEL);
 	if (!smmu) {
@@ -2622,7 +2662,7 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
 	platform_set_drvdata(pdev, smmu);
 
 	/* Reset the device */
-	return arm_smmu_device_reset(smmu);
+	return arm_smmu_device_reset(smmu, bypass);
 }
 
 static int arm_smmu_device_remove(struct platform_device *pdev)
-- 
2.8.1.dirty

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

* [PATCH v7 06/22] iommu/arm-smmu: Fall back to global bypass
@ 2016-09-12 16:13     ` Robin Murphy
  0 siblings, 0 replies; 104+ messages in thread
From: Robin Murphy @ 2016-09-12 16:13 UTC (permalink / raw)
  To: linux-arm-kernel

Unlike SMMUv2, SMMUv3 has no easy way to bypass unknown stream IDs,
other than allocating and filling in the entire stream table with bypass
entries, which for some configurations would waste *gigabytes* of RAM.
Otherwise, all transactions on unknown stream IDs will simply be aborted
with a C_BAD_STREAMID event.

Rather than render the system unusable in the case of an invalid DT,
avoid enabling the SMMU altogether such that everything bypasses
(though letting the explicit disable_bypass option take precedence).

Signed-off-by: Robin Murphy <robin.murphy@arm.com>

---

- Implement proper GBPA update procedure per the spec
---
 drivers/iommu/arm-smmu-v3.c | 48 +++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 44 insertions(+), 4 deletions(-)

diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c
index 5db6931c715c..d7fef5f99bfc 100644
--- a/drivers/iommu/arm-smmu-v3.c
+++ b/drivers/iommu/arm-smmu-v3.c
@@ -123,6 +123,10 @@
 #define CR2_RECINVSID			(1 << 1)
 #define CR2_E2H				(1 << 0)
 
+#define ARM_SMMU_GBPA			0x44
+#define GBPA_ABORT			(1 << 20)
+#define GBPA_UPDATE			(1 << 31)
+
 #define ARM_SMMU_IRQ_CTRL		0x50
 #define IRQ_CTRL_EVTQ_IRQEN		(1 << 2)
 #define IRQ_CTRL_PRIQ_IRQEN		(1 << 1)
@@ -2124,6 +2128,24 @@ static int arm_smmu_write_reg_sync(struct arm_smmu_device *smmu, u32 val,
 					  1, ARM_SMMU_POLL_TIMEOUT_US);
 }
 
+/* GBPA is "special" */
+static int arm_smmu_update_gbpa(struct arm_smmu_device *smmu, u32 set, u32 clr)
+{
+	int ret;
+	u32 reg, __iomem *gbpa = smmu->base + ARM_SMMU_GBPA;
+
+	ret = readl_relaxed_poll_timeout(gbpa, reg, !(reg & GBPA_UPDATE),
+					 1, ARM_SMMU_POLL_TIMEOUT_US);
+	if (ret)
+		return ret;
+
+	reg &= ~clr;
+	reg |= set;
+	writel_relaxed(reg | GBPA_UPDATE, gbpa);
+	return readl_relaxed_poll_timeout(gbpa, reg, !(reg & GBPA_UPDATE),
+					  1, ARM_SMMU_POLL_TIMEOUT_US);
+}
+
 static void arm_smmu_free_msis(void *data)
 {
 	struct device *dev = data;
@@ -2269,7 +2291,7 @@ static int arm_smmu_device_disable(struct arm_smmu_device *smmu)
 	return ret;
 }
 
-static int arm_smmu_device_reset(struct arm_smmu_device *smmu)
+static int arm_smmu_device_reset(struct arm_smmu_device *smmu, bool bypass)
 {
 	int ret;
 	u32 reg, enables;
@@ -2370,8 +2392,17 @@ static int arm_smmu_device_reset(struct arm_smmu_device *smmu)
 		return ret;
 	}
 
-	/* Enable the SMMU interface */
-	enables |= CR0_SMMUEN;
+
+	/* Enable the SMMU interface, or ensure bypass */
+	if (!bypass || disable_bypass) {
+		enables |= CR0_SMMUEN;
+	} else {
+		ret = arm_smmu_update_gbpa(smmu, 0, GBPA_ABORT);
+		if (ret) {
+			dev_err(smmu->dev, "GBPA not responding to update\n");
+			return ret;
+		}
+	}
 	ret = arm_smmu_write_reg_sync(smmu, enables, ARM_SMMU_CR0,
 				      ARM_SMMU_CR0ACK);
 	if (ret) {
@@ -2570,6 +2601,15 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
 	struct resource *res;
 	struct arm_smmu_device *smmu;
 	struct device *dev = &pdev->dev;
+	bool bypass = true;
+	u32 cells;
+
+	if (of_property_read_u32(dev->of_node, "#iommu-cells", &cells))
+		dev_err(dev, "missing #iommu-cells property\n");
+	else if (cells != 1)
+		dev_err(dev, "invalid #iommu-cells value (%d)\n", cells);
+	else
+		bypass = false;
 
 	smmu = devm_kzalloc(dev, sizeof(*smmu), GFP_KERNEL);
 	if (!smmu) {
@@ -2622,7 +2662,7 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
 	platform_set_drvdata(pdev, smmu);
 
 	/* Reset the device */
-	return arm_smmu_device_reset(smmu);
+	return arm_smmu_device_reset(smmu, bypass);
 }
 
 static int arm_smmu_device_remove(struct platform_device *pdev)
-- 
2.8.1.dirty

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

* [PATCH v7 07/22] iommu/arm-smmu: Implement of_xlate() for SMMUv3
  2016-09-12 16:13 ` Robin Murphy
@ 2016-09-12 16:13     ` Robin Murphy
  -1 siblings, 0 replies; 104+ messages in thread
From: Robin Murphy @ 2016-09-12 16:13 UTC (permalink / raw)
  To: will.deacon-5wv7dgnIgG8, joro-zLv9SwRftAIdnm+yROfE0A,
	iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, punit.agrawal-5wv7dgnIgG8,
	thunder.leizhen-hv44wF8Li93QT0dZR+AlfA

Now that we can properly describe the mapping between PCI RIDs and
stream IDs via "iommu-map", and have it fed it to the driver
automatically via of_xlate(), rework the SMMUv3 driver to benefit from
that, and get rid of the current misuse of the "iommus" binding.

Since having of_xlate wired up means that masters will now be given the
appropriate DMA ops, we also need to make sure that default domains work
properly. This necessitates dispensing with the "whole group at a time"
notion for attaching to a domain, as devices which share a group get
attached to the group's default domain one by one as they are initially
probed.

Signed-off-by: Robin Murphy <robin.murphy-5wv7dgnIgG8@public.gmane.org>

---

- Convert to updated fwspec mechanism
---
 drivers/iommu/arm-smmu-v3.c | 304 +++++++++++++++++++-------------------------
 1 file changed, 131 insertions(+), 173 deletions(-)

diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c
index d7fef5f99bfc..15ba80db6465 100644
--- a/drivers/iommu/arm-smmu-v3.c
+++ b/drivers/iommu/arm-smmu-v3.c
@@ -30,6 +30,7 @@
 #include <linux/msi.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>
@@ -610,12 +611,9 @@ struct arm_smmu_device {
 	struct arm_smmu_strtab_cfg	strtab_cfg;
 };
 
-/* SMMU private data for an IOMMU group */
-struct arm_smmu_group {
+/* SMMU private data for each master */
+struct arm_smmu_master_data {
 	struct arm_smmu_device		*smmu;
-	struct arm_smmu_domain		*domain;
-	int				num_sids;
-	u32				*sids;
 	struct arm_smmu_strtab_ent	ste;
 };
 
@@ -1555,20 +1553,6 @@ static int arm_smmu_domain_finalise(struct iommu_domain *domain)
 	return ret;
 }
 
-static struct arm_smmu_group *arm_smmu_group_get(struct device *dev)
-{
-	struct iommu_group *group;
-	struct arm_smmu_group *smmu_group;
-
-	group = iommu_group_get(dev);
-	if (!group)
-		return NULL;
-
-	smmu_group = iommu_group_get_iommudata(group);
-	iommu_group_put(group);
-	return smmu_group;
-}
-
 static __le64 *arm_smmu_get_step_for_sid(struct arm_smmu_device *smmu, u32 sid)
 {
 	__le64 *step;
@@ -1591,27 +1575,17 @@ static __le64 *arm_smmu_get_step_for_sid(struct arm_smmu_device *smmu, u32 sid)
 	return step;
 }
 
-static int arm_smmu_install_ste_for_group(struct arm_smmu_group *smmu_group)
+static int arm_smmu_install_ste_for_dev(struct iommu_fwspec *fwspec)
 {
 	int i;
-	struct arm_smmu_domain *smmu_domain = smmu_group->domain;
-	struct arm_smmu_strtab_ent *ste = &smmu_group->ste;
-	struct arm_smmu_device *smmu = smmu_group->smmu;
+	struct arm_smmu_master_data *master = fwspec->iommu_priv;
+	struct arm_smmu_device *smmu = master->smmu;
 
-	if (smmu_domain->stage == ARM_SMMU_DOMAIN_S1) {
-		ste->s1_cfg = &smmu_domain->s1_cfg;
-		ste->s2_cfg = NULL;
-		arm_smmu_write_ctx_desc(smmu, ste->s1_cfg);
-	} else {
-		ste->s1_cfg = NULL;
-		ste->s2_cfg = &smmu_domain->s2_cfg;
-	}
-
-	for (i = 0; i < smmu_group->num_sids; ++i) {
-		u32 sid = smmu_group->sids[i];
+	for (i = 0; i < fwspec->num_ids; ++i) {
+		u32 sid = fwspec->ids[i];
 		__le64 *step = arm_smmu_get_step_for_sid(smmu, sid);
 
-		arm_smmu_write_strtab_ent(smmu, sid, step, ste);
+		arm_smmu_write_strtab_ent(smmu, sid, step, &master->ste);
 	}
 
 	return 0;
@@ -1619,13 +1593,11 @@ static int arm_smmu_install_ste_for_group(struct arm_smmu_group *smmu_group)
 
 static void arm_smmu_detach_dev(struct device *dev)
 {
-	struct arm_smmu_group *smmu_group = arm_smmu_group_get(dev);
+	struct arm_smmu_master_data *master = dev->iommu_fwspec->iommu_priv;
 
-	smmu_group->ste.bypass = true;
-	if (arm_smmu_install_ste_for_group(smmu_group) < 0)
+	master->ste.bypass = true;
+	if (arm_smmu_install_ste_for_dev(dev->iommu_fwspec) < 0)
 		dev_warn(dev, "failed to install bypass STE\n");
-
-	smmu_group->domain = NULL;
 }
 
 static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
@@ -1633,16 +1605,20 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
 	int ret = 0;
 	struct arm_smmu_device *smmu;
 	struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
-	struct arm_smmu_group *smmu_group = arm_smmu_group_get(dev);
+	struct arm_smmu_master_data *master;
+	struct arm_smmu_strtab_ent *ste;
 
-	if (!smmu_group)
+	if (!dev->iommu_fwspec)
 		return -ENOENT;
 
+	master = dev->iommu_fwspec->iommu_priv;
+	smmu = master->smmu;
+	ste = &master->ste;
+
 	/* Already attached to a different domain? */
-	if (smmu_group->domain && smmu_group->domain != smmu_domain)
+	if (!ste->bypass)
 		arm_smmu_detach_dev(dev);
 
-	smmu = smmu_group->smmu;
 	mutex_lock(&smmu_domain->init_mutex);
 
 	if (!smmu_domain->smmu) {
@@ -1661,21 +1637,21 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
 		goto out_unlock;
 	}
 
-	/* Group already attached to this domain? */
-	if (smmu_group->domain)
-		goto out_unlock;
+	ste->bypass = false;
+	ste->valid = true;
 
-	smmu_group->domain	= smmu_domain;
+	if (smmu_domain->stage == ARM_SMMU_DOMAIN_S1) {
+		ste->s1_cfg = &smmu_domain->s1_cfg;
+		ste->s2_cfg = NULL;
+		arm_smmu_write_ctx_desc(smmu, ste->s1_cfg);
+	} else {
+		ste->s1_cfg = NULL;
+		ste->s2_cfg = &smmu_domain->s2_cfg;
+	}
 
-	/*
-	 * FIXME: This should always be "false" once we have IOMMU-backed
-	 * DMA ops for all devices behind the SMMU.
-	 */
-	smmu_group->ste.bypass	= domain->type == IOMMU_DOMAIN_DMA;
-
-	ret = arm_smmu_install_ste_for_group(smmu_group);
+	ret = arm_smmu_install_ste_for_dev(dev->iommu_fwspec);
 	if (ret < 0)
-		smmu_group->domain = NULL;
+		ste->valid = false;
 
 out_unlock:
 	mutex_unlock(&smmu_domain->init_mutex);
@@ -1734,40 +1710,19 @@ arm_smmu_iova_to_phys(struct iommu_domain *domain, dma_addr_t iova)
 	return ret;
 }
 
-static int __arm_smmu_get_pci_sid(struct pci_dev *pdev, u16 alias, void *sidp)
+static struct platform_driver arm_smmu_driver;
+
+static int arm_smmu_match_node(struct device *dev, void *data)
 {
-	*(u32 *)sidp = alias;
-	return 0; /* Continue walking */
+	return dev->of_node == data;
 }
 
-static void __arm_smmu_release_pci_iommudata(void *data)
+static struct arm_smmu_device *arm_smmu_get_by_node(struct device_node *np)
 {
-	kfree(data);
-}
-
-static struct arm_smmu_device *arm_smmu_get_for_pci_dev(struct pci_dev *pdev)
-{
-	struct device_node *of_node;
-	struct platform_device *smmu_pdev;
-	struct arm_smmu_device *smmu = NULL;
-	struct pci_bus *bus = pdev->bus;
-
-	/* Walk up to the root bus */
-	while (!pci_is_root_bus(bus))
-		bus = bus->parent;
-
-	/* Follow the "iommus" phandle from the host controller */
-	of_node = of_parse_phandle(bus->bridge->parent->of_node, "iommus", 0);
-	if (!of_node)
-		return NULL;
-
-	/* See if we can find an SMMU corresponding to the phandle */
-	smmu_pdev = of_find_device_by_node(of_node);
-	if (smmu_pdev)
-		smmu = platform_get_drvdata(smmu_pdev);
-
-	of_node_put(of_node);
-	return smmu;
+	struct device *dev = driver_find_device(&arm_smmu_driver.driver, NULL,
+						np, arm_smmu_match_node);
+	put_device(dev);
+	return dev ? dev_get_drvdata(dev) : NULL;
 }
 
 static bool arm_smmu_sid_in_range(struct arm_smmu_device *smmu, u32 sid)
@@ -1780,94 +1735,74 @@ static bool arm_smmu_sid_in_range(struct arm_smmu_device *smmu, u32 sid)
 	return sid < limit;
 }
 
+static struct iommu_ops arm_smmu_ops;
+
 static int arm_smmu_add_device(struct device *dev)
 {
 	int i, ret;
-	u32 sid, *sids;
-	struct pci_dev *pdev;
-	struct iommu_group *group;
-	struct arm_smmu_group *smmu_group;
 	struct arm_smmu_device *smmu;
+	struct arm_smmu_master_data *master;
+	struct iommu_fwspec *fwspec = dev->iommu_fwspec;
+	struct iommu_group *group;
 
-	/* We only support PCI, for now */
-	if (!dev_is_pci(dev))
+	if (!fwspec || fwspec->ops != &arm_smmu_ops)
 		return -ENODEV;
-
-	pdev = to_pci_dev(dev);
-	group = iommu_group_get_for_dev(dev);
-	if (IS_ERR(group))
-		return PTR_ERR(group);
-
-	smmu_group = iommu_group_get_iommudata(group);
-	if (!smmu_group) {
-		smmu = arm_smmu_get_for_pci_dev(pdev);
-		if (!smmu) {
-			ret = -ENOENT;
-			goto out_remove_dev;
-		}
-
-		smmu_group = kzalloc(sizeof(*smmu_group), GFP_KERNEL);
-		if (!smmu_group) {
-			ret = -ENOMEM;
-			goto out_remove_dev;
-		}
-
-		smmu_group->ste.valid	= true;
-		smmu_group->smmu	= smmu;
-		iommu_group_set_iommudata(group, smmu_group,
-					  __arm_smmu_release_pci_iommudata);
+	/*
+	 * We _can_ actually withstand dodgy bus code re-calling add_device()
+	 * without an intervening remove_device()/of_xlate() sequence, but
+	 * we're not going to do so quietly...
+	 */
+	if (WARN_ON_ONCE(fwspec->iommu_priv)) {
+		master = fwspec->iommu_priv;
+		smmu = master->smmu;
 	} else {
-		smmu = smmu_group->smmu;
+		smmu = arm_smmu_get_by_node(to_of_node(fwspec->iommu_fwnode));
+		if (!smmu)
+			return -ENODEV;
+		master = kzalloc(sizeof(*master), GFP_KERNEL);
+		if (!master)
+			return -ENOMEM;
+
+		master->smmu = smmu;
+		fwspec->iommu_priv = master;
 	}
 
-	/* Assume SID == RID until firmware tells us otherwise */
-	pci_for_each_dma_alias(pdev, __arm_smmu_get_pci_sid, &sid);
-	for (i = 0; i < smmu_group->num_sids; ++i) {
-		/* If we already know about this SID, then we're done */
-		if (smmu_group->sids[i] == sid)
-			goto out_put_group;
+	/* Check the SIDs are in range of the SMMU and our stream table */
+	for (i = 0; i < fwspec->num_ids; i++) {
+		u32 sid = fwspec->ids[i];
+
+		if (!arm_smmu_sid_in_range(smmu, sid))
+			return -ERANGE;
+
+		/* Ensure l2 strtab is initialised */
+		if (smmu->features & ARM_SMMU_FEAT_2_LVL_STRTAB) {
+			ret = arm_smmu_init_l2_strtab(smmu, sid);
+			if (ret)
+				return ret;
+		}
 	}
 
-	/* Check the SID is in range of the SMMU and our stream table */
-	if (!arm_smmu_sid_in_range(smmu, sid)) {
-		ret = -ERANGE;
-		goto out_remove_dev;
-	}
+	group = iommu_group_get_for_dev(dev);
+	if (!IS_ERR(group))
+		iommu_group_put(group);
 
-	/* Ensure l2 strtab is initialised */
-	if (smmu->features & ARM_SMMU_FEAT_2_LVL_STRTAB) {
-		ret = arm_smmu_init_l2_strtab(smmu, sid);
-		if (ret)
-			goto out_remove_dev;
-	}
-
-	/* Resize the SID array for the group */
-	smmu_group->num_sids++;
-	sids = krealloc(smmu_group->sids, smmu_group->num_sids * sizeof(*sids),
-			GFP_KERNEL);
-	if (!sids) {
-		smmu_group->num_sids--;
-		ret = -ENOMEM;
-		goto out_remove_dev;
-	}
-
-	/* Add the new SID */
-	sids[smmu_group->num_sids - 1] = sid;
-	smmu_group->sids = sids;
-
-out_put_group:
-	iommu_group_put(group);
-	return 0;
-
-out_remove_dev:
-	iommu_group_remove_device(dev);
-	iommu_group_put(group);
-	return ret;
+	return PTR_ERR_OR_ZERO(group);
 }
 
 static void arm_smmu_remove_device(struct device *dev)
 {
+	struct iommu_fwspec *fwspec = dev->iommu_fwspec;
+	struct arm_smmu_master_data *master;
+
+	if (!fwspec || fwspec->ops != &arm_smmu_ops)
+		return;
+
+	master = fwspec->iommu_priv;
+	if (master && master->ste.valid)
+		arm_smmu_detach_dev(dev);
 	iommu_group_remove_device(dev);
+	kfree(master);
+	iommu_fwspec_free(dev);
 }
 
 static int arm_smmu_domain_get_attr(struct iommu_domain *domain,
@@ -1914,6 +1849,15 @@ out_unlock:
 	return ret;
 }
 
+static int arm_smmu_of_xlate(struct device *dev, struct of_phandle_args *args)
+{
+	/* We only support PCI, for now */
+	if (!dev_is_pci(dev))
+		return -ENODEV;
+
+	return iommu_fwspec_add_ids(dev, args->args, 1);
+}
+
 static struct iommu_ops arm_smmu_ops = {
 	.capable		= arm_smmu_capable,
 	.domain_alloc		= arm_smmu_domain_alloc,
@@ -1928,6 +1872,7 @@ static struct iommu_ops arm_smmu_ops = {
 	.device_group		= pci_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 */
 };
 
@@ -2662,7 +2607,14 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
 	platform_set_drvdata(pdev, smmu);
 
 	/* Reset the device */
-	return arm_smmu_device_reset(smmu, bypass);
+	ret = arm_smmu_device_reset(smmu, bypass);
+	if (ret)
+		return ret;
+
+	/* And we're up. Go go go! */
+	of_iommu_set_ops(dev->of_node, &arm_smmu_ops);
+	pci_request_acs();
+	return bus_set_iommu(&pci_bus_type, &arm_smmu_ops);
 }
 
 static int arm_smmu_device_remove(struct platform_device *pdev)
@@ -2690,22 +2642,14 @@ static struct platform_driver arm_smmu_driver = {
 
 static int __init arm_smmu_init(void)
 {
-	struct device_node *np;
-	int ret;
+	static bool registered;
+	int ret = 0;
 
-	np = of_find_matching_node(NULL, arm_smmu_of_match);
-	if (!np)
-		return 0;
-
-	of_node_put(np);
-
-	ret = platform_driver_register(&arm_smmu_driver);
-	if (ret)
-		return ret;
-
-	pci_request_acs();
-
-	return bus_set_iommu(&pci_bus_type, &arm_smmu_ops);
+	if (!registered) {
+		ret = platform_driver_register(&arm_smmu_driver);
+		registered = !ret;
+	}
+	return ret;
 }
 
 static void __exit arm_smmu_exit(void)
@@ -2716,6 +2660,20 @@ 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)
+{
+	int ret = arm_smmu_init();
+
+	if (ret)
+		return ret;
+
+	if (!of_platform_device_create(np, NULL, platform_bus_type.dev_root))
+		return -ENODEV;
+
+	return 0;
+}
+IOMMU_OF_DECLARE(arm_smmuv3, "arm,smmu-v3", arm_smmu_of_init);
+
 MODULE_DESCRIPTION("IOMMU API for ARM architected SMMUv3 implementations");
 MODULE_AUTHOR("Will Deacon <will.deacon-5wv7dgnIgG8@public.gmane.org>");
 MODULE_LICENSE("GPL v2");
-- 
2.8.1.dirty

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

* [PATCH v7 07/22] iommu/arm-smmu: Implement of_xlate() for SMMUv3
@ 2016-09-12 16:13     ` Robin Murphy
  0 siblings, 0 replies; 104+ messages in thread
From: Robin Murphy @ 2016-09-12 16:13 UTC (permalink / raw)
  To: linux-arm-kernel

Now that we can properly describe the mapping between PCI RIDs and
stream IDs via "iommu-map", and have it fed it to the driver
automatically via of_xlate(), rework the SMMUv3 driver to benefit from
that, and get rid of the current misuse of the "iommus" binding.

Since having of_xlate wired up means that masters will now be given the
appropriate DMA ops, we also need to make sure that default domains work
properly. This necessitates dispensing with the "whole group at a time"
notion for attaching to a domain, as devices which share a group get
attached to the group's default domain one by one as they are initially
probed.

Signed-off-by: Robin Murphy <robin.murphy@arm.com>

---

- Convert to updated fwspec mechanism
---
 drivers/iommu/arm-smmu-v3.c | 304 +++++++++++++++++++-------------------------
 1 file changed, 131 insertions(+), 173 deletions(-)

diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c
index d7fef5f99bfc..15ba80db6465 100644
--- a/drivers/iommu/arm-smmu-v3.c
+++ b/drivers/iommu/arm-smmu-v3.c
@@ -30,6 +30,7 @@
 #include <linux/msi.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>
@@ -610,12 +611,9 @@ struct arm_smmu_device {
 	struct arm_smmu_strtab_cfg	strtab_cfg;
 };
 
-/* SMMU private data for an IOMMU group */
-struct arm_smmu_group {
+/* SMMU private data for each master */
+struct arm_smmu_master_data {
 	struct arm_smmu_device		*smmu;
-	struct arm_smmu_domain		*domain;
-	int				num_sids;
-	u32				*sids;
 	struct arm_smmu_strtab_ent	ste;
 };
 
@@ -1555,20 +1553,6 @@ static int arm_smmu_domain_finalise(struct iommu_domain *domain)
 	return ret;
 }
 
-static struct arm_smmu_group *arm_smmu_group_get(struct device *dev)
-{
-	struct iommu_group *group;
-	struct arm_smmu_group *smmu_group;
-
-	group = iommu_group_get(dev);
-	if (!group)
-		return NULL;
-
-	smmu_group = iommu_group_get_iommudata(group);
-	iommu_group_put(group);
-	return smmu_group;
-}
-
 static __le64 *arm_smmu_get_step_for_sid(struct arm_smmu_device *smmu, u32 sid)
 {
 	__le64 *step;
@@ -1591,27 +1575,17 @@ static __le64 *arm_smmu_get_step_for_sid(struct arm_smmu_device *smmu, u32 sid)
 	return step;
 }
 
-static int arm_smmu_install_ste_for_group(struct arm_smmu_group *smmu_group)
+static int arm_smmu_install_ste_for_dev(struct iommu_fwspec *fwspec)
 {
 	int i;
-	struct arm_smmu_domain *smmu_domain = smmu_group->domain;
-	struct arm_smmu_strtab_ent *ste = &smmu_group->ste;
-	struct arm_smmu_device *smmu = smmu_group->smmu;
+	struct arm_smmu_master_data *master = fwspec->iommu_priv;
+	struct arm_smmu_device *smmu = master->smmu;
 
-	if (smmu_domain->stage == ARM_SMMU_DOMAIN_S1) {
-		ste->s1_cfg = &smmu_domain->s1_cfg;
-		ste->s2_cfg = NULL;
-		arm_smmu_write_ctx_desc(smmu, ste->s1_cfg);
-	} else {
-		ste->s1_cfg = NULL;
-		ste->s2_cfg = &smmu_domain->s2_cfg;
-	}
-
-	for (i = 0; i < smmu_group->num_sids; ++i) {
-		u32 sid = smmu_group->sids[i];
+	for (i = 0; i < fwspec->num_ids; ++i) {
+		u32 sid = fwspec->ids[i];
 		__le64 *step = arm_smmu_get_step_for_sid(smmu, sid);
 
-		arm_smmu_write_strtab_ent(smmu, sid, step, ste);
+		arm_smmu_write_strtab_ent(smmu, sid, step, &master->ste);
 	}
 
 	return 0;
@@ -1619,13 +1593,11 @@ static int arm_smmu_install_ste_for_group(struct arm_smmu_group *smmu_group)
 
 static void arm_smmu_detach_dev(struct device *dev)
 {
-	struct arm_smmu_group *smmu_group = arm_smmu_group_get(dev);
+	struct arm_smmu_master_data *master = dev->iommu_fwspec->iommu_priv;
 
-	smmu_group->ste.bypass = true;
-	if (arm_smmu_install_ste_for_group(smmu_group) < 0)
+	master->ste.bypass = true;
+	if (arm_smmu_install_ste_for_dev(dev->iommu_fwspec) < 0)
 		dev_warn(dev, "failed to install bypass STE\n");
-
-	smmu_group->domain = NULL;
 }
 
 static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
@@ -1633,16 +1605,20 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
 	int ret = 0;
 	struct arm_smmu_device *smmu;
 	struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
-	struct arm_smmu_group *smmu_group = arm_smmu_group_get(dev);
+	struct arm_smmu_master_data *master;
+	struct arm_smmu_strtab_ent *ste;
 
-	if (!smmu_group)
+	if (!dev->iommu_fwspec)
 		return -ENOENT;
 
+	master = dev->iommu_fwspec->iommu_priv;
+	smmu = master->smmu;
+	ste = &master->ste;
+
 	/* Already attached to a different domain? */
-	if (smmu_group->domain && smmu_group->domain != smmu_domain)
+	if (!ste->bypass)
 		arm_smmu_detach_dev(dev);
 
-	smmu = smmu_group->smmu;
 	mutex_lock(&smmu_domain->init_mutex);
 
 	if (!smmu_domain->smmu) {
@@ -1661,21 +1637,21 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
 		goto out_unlock;
 	}
 
-	/* Group already attached to this domain? */
-	if (smmu_group->domain)
-		goto out_unlock;
+	ste->bypass = false;
+	ste->valid = true;
 
-	smmu_group->domain	= smmu_domain;
+	if (smmu_domain->stage == ARM_SMMU_DOMAIN_S1) {
+		ste->s1_cfg = &smmu_domain->s1_cfg;
+		ste->s2_cfg = NULL;
+		arm_smmu_write_ctx_desc(smmu, ste->s1_cfg);
+	} else {
+		ste->s1_cfg = NULL;
+		ste->s2_cfg = &smmu_domain->s2_cfg;
+	}
 
-	/*
-	 * FIXME: This should always be "false" once we have IOMMU-backed
-	 * DMA ops for all devices behind the SMMU.
-	 */
-	smmu_group->ste.bypass	= domain->type == IOMMU_DOMAIN_DMA;
-
-	ret = arm_smmu_install_ste_for_group(smmu_group);
+	ret = arm_smmu_install_ste_for_dev(dev->iommu_fwspec);
 	if (ret < 0)
-		smmu_group->domain = NULL;
+		ste->valid = false;
 
 out_unlock:
 	mutex_unlock(&smmu_domain->init_mutex);
@@ -1734,40 +1710,19 @@ arm_smmu_iova_to_phys(struct iommu_domain *domain, dma_addr_t iova)
 	return ret;
 }
 
-static int __arm_smmu_get_pci_sid(struct pci_dev *pdev, u16 alias, void *sidp)
+static struct platform_driver arm_smmu_driver;
+
+static int arm_smmu_match_node(struct device *dev, void *data)
 {
-	*(u32 *)sidp = alias;
-	return 0; /* Continue walking */
+	return dev->of_node == data;
 }
 
-static void __arm_smmu_release_pci_iommudata(void *data)
+static struct arm_smmu_device *arm_smmu_get_by_node(struct device_node *np)
 {
-	kfree(data);
-}
-
-static struct arm_smmu_device *arm_smmu_get_for_pci_dev(struct pci_dev *pdev)
-{
-	struct device_node *of_node;
-	struct platform_device *smmu_pdev;
-	struct arm_smmu_device *smmu = NULL;
-	struct pci_bus *bus = pdev->bus;
-
-	/* Walk up to the root bus */
-	while (!pci_is_root_bus(bus))
-		bus = bus->parent;
-
-	/* Follow the "iommus" phandle from the host controller */
-	of_node = of_parse_phandle(bus->bridge->parent->of_node, "iommus", 0);
-	if (!of_node)
-		return NULL;
-
-	/* See if we can find an SMMU corresponding to the phandle */
-	smmu_pdev = of_find_device_by_node(of_node);
-	if (smmu_pdev)
-		smmu = platform_get_drvdata(smmu_pdev);
-
-	of_node_put(of_node);
-	return smmu;
+	struct device *dev = driver_find_device(&arm_smmu_driver.driver, NULL,
+						np, arm_smmu_match_node);
+	put_device(dev);
+	return dev ? dev_get_drvdata(dev) : NULL;
 }
 
 static bool arm_smmu_sid_in_range(struct arm_smmu_device *smmu, u32 sid)
@@ -1780,94 +1735,74 @@ static bool arm_smmu_sid_in_range(struct arm_smmu_device *smmu, u32 sid)
 	return sid < limit;
 }
 
+static struct iommu_ops arm_smmu_ops;
+
 static int arm_smmu_add_device(struct device *dev)
 {
 	int i, ret;
-	u32 sid, *sids;
-	struct pci_dev *pdev;
-	struct iommu_group *group;
-	struct arm_smmu_group *smmu_group;
 	struct arm_smmu_device *smmu;
+	struct arm_smmu_master_data *master;
+	struct iommu_fwspec *fwspec = dev->iommu_fwspec;
+	struct iommu_group *group;
 
-	/* We only support PCI, for now */
-	if (!dev_is_pci(dev))
+	if (!fwspec || fwspec->ops != &arm_smmu_ops)
 		return -ENODEV;
-
-	pdev = to_pci_dev(dev);
-	group = iommu_group_get_for_dev(dev);
-	if (IS_ERR(group))
-		return PTR_ERR(group);
-
-	smmu_group = iommu_group_get_iommudata(group);
-	if (!smmu_group) {
-		smmu = arm_smmu_get_for_pci_dev(pdev);
-		if (!smmu) {
-			ret = -ENOENT;
-			goto out_remove_dev;
-		}
-
-		smmu_group = kzalloc(sizeof(*smmu_group), GFP_KERNEL);
-		if (!smmu_group) {
-			ret = -ENOMEM;
-			goto out_remove_dev;
-		}
-
-		smmu_group->ste.valid	= true;
-		smmu_group->smmu	= smmu;
-		iommu_group_set_iommudata(group, smmu_group,
-					  __arm_smmu_release_pci_iommudata);
+	/*
+	 * We _can_ actually withstand dodgy bus code re-calling add_device()
+	 * without an intervening remove_device()/of_xlate() sequence, but
+	 * we're not going to do so quietly...
+	 */
+	if (WARN_ON_ONCE(fwspec->iommu_priv)) {
+		master = fwspec->iommu_priv;
+		smmu = master->smmu;
 	} else {
-		smmu = smmu_group->smmu;
+		smmu = arm_smmu_get_by_node(to_of_node(fwspec->iommu_fwnode));
+		if (!smmu)
+			return -ENODEV;
+		master = kzalloc(sizeof(*master), GFP_KERNEL);
+		if (!master)
+			return -ENOMEM;
+
+		master->smmu = smmu;
+		fwspec->iommu_priv = master;
 	}
 
-	/* Assume SID == RID until firmware tells us otherwise */
-	pci_for_each_dma_alias(pdev, __arm_smmu_get_pci_sid, &sid);
-	for (i = 0; i < smmu_group->num_sids; ++i) {
-		/* If we already know about this SID, then we're done */
-		if (smmu_group->sids[i] == sid)
-			goto out_put_group;
+	/* Check the SIDs are in range of the SMMU and our stream table */
+	for (i = 0; i < fwspec->num_ids; i++) {
+		u32 sid = fwspec->ids[i];
+
+		if (!arm_smmu_sid_in_range(smmu, sid))
+			return -ERANGE;
+
+		/* Ensure l2 strtab is initialised */
+		if (smmu->features & ARM_SMMU_FEAT_2_LVL_STRTAB) {
+			ret = arm_smmu_init_l2_strtab(smmu, sid);
+			if (ret)
+				return ret;
+		}
 	}
 
-	/* Check the SID is in range of the SMMU and our stream table */
-	if (!arm_smmu_sid_in_range(smmu, sid)) {
-		ret = -ERANGE;
-		goto out_remove_dev;
-	}
+	group = iommu_group_get_for_dev(dev);
+	if (!IS_ERR(group))
+		iommu_group_put(group);
 
-	/* Ensure l2 strtab is initialised */
-	if (smmu->features & ARM_SMMU_FEAT_2_LVL_STRTAB) {
-		ret = arm_smmu_init_l2_strtab(smmu, sid);
-		if (ret)
-			goto out_remove_dev;
-	}
-
-	/* Resize the SID array for the group */
-	smmu_group->num_sids++;
-	sids = krealloc(smmu_group->sids, smmu_group->num_sids * sizeof(*sids),
-			GFP_KERNEL);
-	if (!sids) {
-		smmu_group->num_sids--;
-		ret = -ENOMEM;
-		goto out_remove_dev;
-	}
-
-	/* Add the new SID */
-	sids[smmu_group->num_sids - 1] = sid;
-	smmu_group->sids = sids;
-
-out_put_group:
-	iommu_group_put(group);
-	return 0;
-
-out_remove_dev:
-	iommu_group_remove_device(dev);
-	iommu_group_put(group);
-	return ret;
+	return PTR_ERR_OR_ZERO(group);
 }
 
 static void arm_smmu_remove_device(struct device *dev)
 {
+	struct iommu_fwspec *fwspec = dev->iommu_fwspec;
+	struct arm_smmu_master_data *master;
+
+	if (!fwspec || fwspec->ops != &arm_smmu_ops)
+		return;
+
+	master = fwspec->iommu_priv;
+	if (master && master->ste.valid)
+		arm_smmu_detach_dev(dev);
 	iommu_group_remove_device(dev);
+	kfree(master);
+	iommu_fwspec_free(dev);
 }
 
 static int arm_smmu_domain_get_attr(struct iommu_domain *domain,
@@ -1914,6 +1849,15 @@ out_unlock:
 	return ret;
 }
 
+static int arm_smmu_of_xlate(struct device *dev, struct of_phandle_args *args)
+{
+	/* We only support PCI, for now */
+	if (!dev_is_pci(dev))
+		return -ENODEV;
+
+	return iommu_fwspec_add_ids(dev, args->args, 1);
+}
+
 static struct iommu_ops arm_smmu_ops = {
 	.capable		= arm_smmu_capable,
 	.domain_alloc		= arm_smmu_domain_alloc,
@@ -1928,6 +1872,7 @@ static struct iommu_ops arm_smmu_ops = {
 	.device_group		= pci_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 */
 };
 
@@ -2662,7 +2607,14 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
 	platform_set_drvdata(pdev, smmu);
 
 	/* Reset the device */
-	return arm_smmu_device_reset(smmu, bypass);
+	ret = arm_smmu_device_reset(smmu, bypass);
+	if (ret)
+		return ret;
+
+	/* And we're up. Go go go! */
+	of_iommu_set_ops(dev->of_node, &arm_smmu_ops);
+	pci_request_acs();
+	return bus_set_iommu(&pci_bus_type, &arm_smmu_ops);
 }
 
 static int arm_smmu_device_remove(struct platform_device *pdev)
@@ -2690,22 +2642,14 @@ static struct platform_driver arm_smmu_driver = {
 
 static int __init arm_smmu_init(void)
 {
-	struct device_node *np;
-	int ret;
+	static bool registered;
+	int ret = 0;
 
-	np = of_find_matching_node(NULL, arm_smmu_of_match);
-	if (!np)
-		return 0;
-
-	of_node_put(np);
-
-	ret = platform_driver_register(&arm_smmu_driver);
-	if (ret)
-		return ret;
-
-	pci_request_acs();
-
-	return bus_set_iommu(&pci_bus_type, &arm_smmu_ops);
+	if (!registered) {
+		ret = platform_driver_register(&arm_smmu_driver);
+		registered = !ret;
+	}
+	return ret;
 }
 
 static void __exit arm_smmu_exit(void)
@@ -2716,6 +2660,20 @@ 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)
+{
+	int ret = arm_smmu_init();
+
+	if (ret)
+		return ret;
+
+	if (!of_platform_device_create(np, NULL, platform_bus_type.dev_root))
+		return -ENODEV;
+
+	return 0;
+}
+IOMMU_OF_DECLARE(arm_smmuv3, "arm,smmu-v3", arm_smmu_of_init);
+
 MODULE_DESCRIPTION("IOMMU API for ARM architected SMMUv3 implementations");
 MODULE_AUTHOR("Will Deacon <will.deacon@arm.com>");
 MODULE_LICENSE("GPL v2");
-- 
2.8.1.dirty

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

* [PATCH v7 08/22] iommu/arm-smmu: Support non-PCI devices with SMMUv3
  2016-09-12 16:13 ` Robin Murphy
@ 2016-09-12 16:13     ` Robin Murphy
  -1 siblings, 0 replies; 104+ messages in thread
From: Robin Murphy @ 2016-09-12 16:13 UTC (permalink / raw)
  To: will.deacon-5wv7dgnIgG8, joro-zLv9SwRftAIdnm+yROfE0A,
	iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, lorenzo.pieralisi-5wv7dgnIgG8,
	jean-philippe.brucker-5wv7dgnIgG8, punit.agrawal-5wv7dgnIgG8,
	thunder.leizhen-hv44wF8Li93QT0dZR+AlfA,
	eric.auger-H+wXaHxf7aLQT0dZR+AlfA

With the device <-> stream ID relationship suitably abstracted and
of_xlate() hooked up, the PCI dependency now looks, and is, entirely
arbitrary. Any bus using the of_dma_configure() mechanism will work,
so extend support to the platform and AMBA buses which do just that.

Acked-by: Will Deacon <will.deacon-5wv7dgnIgG8@public.gmane.org>
Tested-by: Lorenzo Pieralisi <lorenzo.pieralisi-5wv7dgnIgG8@public.gmane.org>
Signed-off-by: Robin Murphy <robin.murphy-5wv7dgnIgG8@public.gmane.org>
---
 drivers/iommu/Kconfig       |  2 +-
 drivers/iommu/arm-smmu-v3.c | 37 +++++++++++++++++++++++++++++++------
 2 files changed, 32 insertions(+), 7 deletions(-)

diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
index d432ca828472..8ee54d71c7eb 100644
--- a/drivers/iommu/Kconfig
+++ b/drivers/iommu/Kconfig
@@ -309,7 +309,7 @@ config ARM_SMMU
 
 config ARM_SMMU_V3
 	bool "ARM Ltd. System MMU Version 3 (SMMUv3) Support"
-	depends on ARM64 && PCI
+	depends on ARM64
 	select IOMMU_API
 	select IOMMU_IO_PGTABLE_LPAE
 	select GENERIC_MSI_IRQ_DOMAIN
diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c
index 15ba80db6465..52860bcf80f2 100644
--- a/drivers/iommu/arm-smmu-v3.c
+++ b/drivers/iommu/arm-smmu-v3.c
@@ -35,6 +35,8 @@
 #include <linux/pci.h>
 #include <linux/platform_device.h>
 
+#include <linux/amba/bus.h>
+
 #include "io-pgtable.h"
 
 /* MMIO registers */
@@ -1805,6 +1807,23 @@ static void arm_smmu_remove_device(struct device *dev)
 	iommu_fwspec_free(dev);
 }
 
+static struct iommu_group *arm_smmu_device_group(struct device *dev)
+{
+	struct iommu_group *group;
+
+	/*
+	 * We don't support devices sharing stream IDs other than PCI RID
+	 * aliases, since the necessary ID-to-device lookup becomes rather
+	 * impractical given a potential sparse 32-bit stream ID space.
+	 */
+	if (dev_is_pci(dev))
+		group = pci_device_group(dev);
+	else
+		group = generic_device_group(dev);
+
+	return group;
+}
+
 static int arm_smmu_domain_get_attr(struct iommu_domain *domain,
 				    enum iommu_attr attr, void *data)
 {
@@ -1851,10 +1870,6 @@ out_unlock:
 
 static int arm_smmu_of_xlate(struct device *dev, struct of_phandle_args *args)
 {
-	/* We only support PCI, for now */
-	if (!dev_is_pci(dev))
-		return -ENODEV;
-
 	return iommu_fwspec_add_ids(dev, args->args, 1);
 }
 
@@ -1869,7 +1884,7 @@ static struct iommu_ops arm_smmu_ops = {
 	.iova_to_phys		= arm_smmu_iova_to_phys,
 	.add_device		= arm_smmu_add_device,
 	.remove_device		= arm_smmu_remove_device,
-	.device_group		= pci_device_group,
+	.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,
@@ -2613,8 +2628,18 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
 
 	/* And we're up. Go go go! */
 	of_iommu_set_ops(dev->of_node, &arm_smmu_ops);
+#ifdef CONFIG_PCI
 	pci_request_acs();
-	return bus_set_iommu(&pci_bus_type, &arm_smmu_ops);
+	ret = bus_set_iommu(&pci_bus_type, &arm_smmu_ops);
+	if (ret)
+		return ret;
+#endif
+#ifdef CONFIG_ARM_AMBA
+	ret = bus_set_iommu(&amba_bustype, &arm_smmu_ops);
+	if (ret)
+		return ret;
+#endif
+	return bus_set_iommu(&platform_bus_type, &arm_smmu_ops);
 }
 
 static int arm_smmu_device_remove(struct platform_device *pdev)
-- 
2.8.1.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] 104+ messages in thread

* [PATCH v7 08/22] iommu/arm-smmu: Support non-PCI devices with SMMUv3
@ 2016-09-12 16:13     ` Robin Murphy
  0 siblings, 0 replies; 104+ messages in thread
From: Robin Murphy @ 2016-09-12 16:13 UTC (permalink / raw)
  To: linux-arm-kernel

With the device <-> stream ID relationship suitably abstracted and
of_xlate() hooked up, the PCI dependency now looks, and is, entirely
arbitrary. Any bus using the of_dma_configure() mechanism will work,
so extend support to the platform and AMBA buses which do just that.

Acked-by: Will Deacon <will.deacon@arm.com>
Tested-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Signed-off-by: Robin Murphy <robin.murphy@arm.com>
---
 drivers/iommu/Kconfig       |  2 +-
 drivers/iommu/arm-smmu-v3.c | 37 +++++++++++++++++++++++++++++++------
 2 files changed, 32 insertions(+), 7 deletions(-)

diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
index d432ca828472..8ee54d71c7eb 100644
--- a/drivers/iommu/Kconfig
+++ b/drivers/iommu/Kconfig
@@ -309,7 +309,7 @@ config ARM_SMMU
 
 config ARM_SMMU_V3
 	bool "ARM Ltd. System MMU Version 3 (SMMUv3) Support"
-	depends on ARM64 && PCI
+	depends on ARM64
 	select IOMMU_API
 	select IOMMU_IO_PGTABLE_LPAE
 	select GENERIC_MSI_IRQ_DOMAIN
diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c
index 15ba80db6465..52860bcf80f2 100644
--- a/drivers/iommu/arm-smmu-v3.c
+++ b/drivers/iommu/arm-smmu-v3.c
@@ -35,6 +35,8 @@
 #include <linux/pci.h>
 #include <linux/platform_device.h>
 
+#include <linux/amba/bus.h>
+
 #include "io-pgtable.h"
 
 /* MMIO registers */
@@ -1805,6 +1807,23 @@ static void arm_smmu_remove_device(struct device *dev)
 	iommu_fwspec_free(dev);
 }
 
+static struct iommu_group *arm_smmu_device_group(struct device *dev)
+{
+	struct iommu_group *group;
+
+	/*
+	 * We don't support devices sharing stream IDs other than PCI RID
+	 * aliases, since the necessary ID-to-device lookup becomes rather
+	 * impractical given a potential sparse 32-bit stream ID space.
+	 */
+	if (dev_is_pci(dev))
+		group = pci_device_group(dev);
+	else
+		group = generic_device_group(dev);
+
+	return group;
+}
+
 static int arm_smmu_domain_get_attr(struct iommu_domain *domain,
 				    enum iommu_attr attr, void *data)
 {
@@ -1851,10 +1870,6 @@ out_unlock:
 
 static int arm_smmu_of_xlate(struct device *dev, struct of_phandle_args *args)
 {
-	/* We only support PCI, for now */
-	if (!dev_is_pci(dev))
-		return -ENODEV;
-
 	return iommu_fwspec_add_ids(dev, args->args, 1);
 }
 
@@ -1869,7 +1884,7 @@ static struct iommu_ops arm_smmu_ops = {
 	.iova_to_phys		= arm_smmu_iova_to_phys,
 	.add_device		= arm_smmu_add_device,
 	.remove_device		= arm_smmu_remove_device,
-	.device_group		= pci_device_group,
+	.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,
@@ -2613,8 +2628,18 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
 
 	/* And we're up. Go go go! */
 	of_iommu_set_ops(dev->of_node, &arm_smmu_ops);
+#ifdef CONFIG_PCI
 	pci_request_acs();
-	return bus_set_iommu(&pci_bus_type, &arm_smmu_ops);
+	ret = bus_set_iommu(&pci_bus_type, &arm_smmu_ops);
+	if (ret)
+		return ret;
+#endif
+#ifdef CONFIG_ARM_AMBA
+	ret = bus_set_iommu(&amba_bustype, &arm_smmu_ops);
+	if (ret)
+		return ret;
+#endif
+	return bus_set_iommu(&platform_bus_type, &arm_smmu_ops);
 }
 
 static int arm_smmu_device_remove(struct platform_device *pdev)
-- 
2.8.1.dirty

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

* [PATCH v7 09/22] iommu/arm-smmu: Set PRIVCFG in stage 1 STEs
  2016-09-12 16:13 ` Robin Murphy
@ 2016-09-12 16:13     ` Robin Murphy
  -1 siblings, 0 replies; 104+ messages in thread
From: Robin Murphy @ 2016-09-12 16:13 UTC (permalink / raw)
  To: will.deacon-5wv7dgnIgG8, joro-zLv9SwRftAIdnm+yROfE0A,
	iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, lorenzo.pieralisi-5wv7dgnIgG8,
	jean-philippe.brucker-5wv7dgnIgG8, punit.agrawal-5wv7dgnIgG8,
	thunder.leizhen-hv44wF8Li93QT0dZR+AlfA,
	eric.auger-H+wXaHxf7aLQT0dZR+AlfA

Implement the SMMUv3 equivalent of d346180e70b9 ("iommu/arm-smmu: Treat
all device transactions as unprivileged"), so that once again those
pesky DMA controllers with their privileged instruction fetches don't
unexpectedly fault in stage 1 domains due to VMSAv8 rules.

Acked-by: Will Deacon <will.deacon-5wv7dgnIgG8@public.gmane.org>
Tested-by: Lorenzo Pieralisi <lorenzo.pieralisi-5wv7dgnIgG8@public.gmane.org>
Signed-off-by: Robin Murphy <robin.murphy-5wv7dgnIgG8@public.gmane.org>
---
 drivers/iommu/arm-smmu-v3.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c
index 52860bcf80f2..0c45c1e02e04 100644
--- a/drivers/iommu/arm-smmu-v3.c
+++ b/drivers/iommu/arm-smmu-v3.c
@@ -267,6 +267,9 @@
 #define STRTAB_STE_1_SHCFG_INCOMING	1UL
 #define STRTAB_STE_1_SHCFG_SHIFT	44
 
+#define STRTAB_STE_1_PRIVCFG_UNPRIV	2UL
+#define STRTAB_STE_1_PRIVCFG_SHIFT	48
+
 #define STRTAB_STE_2_S2VMID_SHIFT	0
 #define STRTAB_STE_2_S2VMID_MASK	0xffffUL
 #define STRTAB_STE_2_VTCR_SHIFT		32
@@ -1068,7 +1071,9 @@ static void arm_smmu_write_strtab_ent(struct arm_smmu_device *smmu, u32 sid,
 #ifdef CONFIG_PCI_ATS
 			 STRTAB_STE_1_EATS_TRANS << STRTAB_STE_1_EATS_SHIFT |
 #endif
-			 STRTAB_STE_1_STRW_NSEL1 << STRTAB_STE_1_STRW_SHIFT);
+			 STRTAB_STE_1_STRW_NSEL1 << STRTAB_STE_1_STRW_SHIFT |
+			 STRTAB_STE_1_PRIVCFG_UNPRIV <<
+			 STRTAB_STE_1_PRIVCFG_SHIFT);
 
 		if (smmu->features & ARM_SMMU_FEAT_STALLS)
 			dst[1] |= cpu_to_le64(STRTAB_STE_1_S1STALLD);
-- 
2.8.1.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] 104+ messages in thread

* [PATCH v7 09/22] iommu/arm-smmu: Set PRIVCFG in stage 1 STEs
@ 2016-09-12 16:13     ` Robin Murphy
  0 siblings, 0 replies; 104+ messages in thread
From: Robin Murphy @ 2016-09-12 16:13 UTC (permalink / raw)
  To: linux-arm-kernel

Implement the SMMUv3 equivalent of d346180e70b9 ("iommu/arm-smmu: Treat
all device transactions as unprivileged"), so that once again those
pesky DMA controllers with their privileged instruction fetches don't
unexpectedly fault in stage 1 domains due to VMSAv8 rules.

Acked-by: Will Deacon <will.deacon@arm.com>
Tested-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Signed-off-by: Robin Murphy <robin.murphy@arm.com>
---
 drivers/iommu/arm-smmu-v3.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c
index 52860bcf80f2..0c45c1e02e04 100644
--- a/drivers/iommu/arm-smmu-v3.c
+++ b/drivers/iommu/arm-smmu-v3.c
@@ -267,6 +267,9 @@
 #define STRTAB_STE_1_SHCFG_INCOMING	1UL
 #define STRTAB_STE_1_SHCFG_SHIFT	44
 
+#define STRTAB_STE_1_PRIVCFG_UNPRIV	2UL
+#define STRTAB_STE_1_PRIVCFG_SHIFT	48
+
 #define STRTAB_STE_2_S2VMID_SHIFT	0
 #define STRTAB_STE_2_S2VMID_MASK	0xffffUL
 #define STRTAB_STE_2_VTCR_SHIFT		32
@@ -1068,7 +1071,9 @@ static void arm_smmu_write_strtab_ent(struct arm_smmu_device *smmu, u32 sid,
 #ifdef CONFIG_PCI_ATS
 			 STRTAB_STE_1_EATS_TRANS << STRTAB_STE_1_EATS_SHIFT |
 #endif
-			 STRTAB_STE_1_STRW_NSEL1 << STRTAB_STE_1_STRW_SHIFT);
+			 STRTAB_STE_1_STRW_NSEL1 << STRTAB_STE_1_STRW_SHIFT |
+			 STRTAB_STE_1_PRIVCFG_UNPRIV <<
+			 STRTAB_STE_1_PRIVCFG_SHIFT);
 
 		if (smmu->features & ARM_SMMU_FEAT_STALLS)
 			dst[1] |= cpu_to_le64(STRTAB_STE_1_S1STALLD);
-- 
2.8.1.dirty

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

* [PATCH v7 10/22] iommu/arm-smmu: Handle stream IDs more dynamically
  2016-09-12 16:13 ` Robin Murphy
@ 2016-09-12 16:13     ` Robin Murphy
  -1 siblings, 0 replies; 104+ messages in thread
From: Robin Murphy @ 2016-09-12 16:13 UTC (permalink / raw)
  To: will.deacon-5wv7dgnIgG8, joro-zLv9SwRftAIdnm+yROfE0A,
	iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, punit.agrawal-5wv7dgnIgG8,
	thunder.leizhen-hv44wF8Li93QT0dZR+AlfA

Rather than assuming fixed worst-case values for stream IDs and SMR
masks, keep track of whatever implemented bits the hardware actually
reports. This also obviates the slightly questionable validation of SMR
fields in isolation - rather than aborting the whole SMMU probe for a
hardware configuration which is still architecturally valid, we can
simply refuse masters later if they try to claim an unrepresentable ID
or mask (which almost certainly implies a DT error anyway).

Acked-by: Will Deacon <will.deacon-5wv7dgnIgG8@public.gmane.org>
Tested-by: Lorenzo Pieralisi <lorenzo.pieralisi-5wv7dgnIgG8@public.gmane.org>
Signed-off-by: Robin Murphy <robin.murphy-5wv7dgnIgG8@public.gmane.org>
---
 drivers/iommu/arm-smmu.c | 43 ++++++++++++++++++++++---------------------
 1 file changed, 22 insertions(+), 21 deletions(-)

diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index 4b1c87e947fd..f86d7887f69a 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -165,9 +165,7 @@
 #define ARM_SMMU_GR0_SMR(n)		(0x800 + ((n) << 2))
 #define SMR_VALID			(1 << 31)
 #define SMR_MASK_SHIFT			16
-#define SMR_MASK_MASK			0x7fff
 #define SMR_ID_SHIFT			0
-#define SMR_ID_MASK			0x7fff
 
 #define ARM_SMMU_GR0_S2CR(n)		(0xc00 + ((n) << 2))
 #define S2CR_CBNDX_SHIFT		0
@@ -346,6 +344,8 @@ struct arm_smmu_device {
 	atomic_t			irptndx;
 
 	u32				num_mapping_groups;
+	u16				streamid_mask;
+	u16				smr_mask_mask;
 	DECLARE_BITMAP(smr_map, ARM_SMMU_MAX_SMRS);
 
 	unsigned long			va_size;
@@ -1715,39 +1715,40 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu)
 		dev_notice(smmu->dev,
 			   "\t(IDR0.CTTW overridden by dma-coherent property)\n");
 
+	/* Max. number of entries we have for stream matching/indexing */
+	size = 1 << ((id >> ID0_NUMSIDB_SHIFT) & ID0_NUMSIDB_MASK);
+	smmu->streamid_mask = size - 1;
 	if (id & ID0_SMS) {
-		u32 smr, sid, mask;
+		u32 smr;
 
 		smmu->features |= ARM_SMMU_FEAT_STREAM_MATCH;
-		smmu->num_mapping_groups = (id >> ID0_NUMSMRG_SHIFT) &
-					   ID0_NUMSMRG_MASK;
-		if (smmu->num_mapping_groups == 0) {
+		size = (id >> ID0_NUMSMRG_SHIFT) & ID0_NUMSMRG_MASK;
+		if (size == 0) {
 			dev_err(smmu->dev,
 				"stream-matching supported, but no SMRs present!\n");
 			return -ENODEV;
 		}
 
-		smr = SMR_MASK_MASK << SMR_MASK_SHIFT;
-		smr |= (SMR_ID_MASK << SMR_ID_SHIFT);
+		/*
+		 * SMR.ID bits may not be preserved if the corresponding MASK
+		 * bits are set, so check each one separately. We can reject
+		 * masters later if they try to claim IDs outside these masks.
+		 */
+		smr = smmu->streamid_mask << SMR_ID_SHIFT;
 		writel_relaxed(smr, gr0_base + ARM_SMMU_GR0_SMR(0));
 		smr = readl_relaxed(gr0_base + ARM_SMMU_GR0_SMR(0));
+		smmu->streamid_mask = smr >> SMR_ID_SHIFT;
 
-		mask = (smr >> SMR_MASK_SHIFT) & SMR_MASK_MASK;
-		sid = (smr >> SMR_ID_SHIFT) & SMR_ID_MASK;
-		if ((mask & sid) != sid) {
-			dev_err(smmu->dev,
-				"SMR mask bits (0x%x) insufficient for ID field (0x%x)\n",
-				mask, sid);
-			return -ENODEV;
-		}
+		smr = smmu->streamid_mask << SMR_MASK_SHIFT;
+		writel_relaxed(smr, gr0_base + ARM_SMMU_GR0_SMR(0));
+		smr = readl_relaxed(gr0_base + ARM_SMMU_GR0_SMR(0));
+		smmu->smr_mask_mask = smr >> SMR_MASK_SHIFT;
 
 		dev_notice(smmu->dev,
-			   "\tstream matching with %u register groups, mask 0x%x",
-			   smmu->num_mapping_groups, mask);
-	} else {
-		smmu->num_mapping_groups = (id >> ID0_NUMSIDB_SHIFT) &
-					   ID0_NUMSIDB_MASK;
+			   "\tstream matching with %lu register groups, mask 0x%x",
+			   size, smmu->smr_mask_mask);
 	}
+	smmu->num_mapping_groups = size;
 
 	if (smmu->version < ARM_SMMU_V2 || !(id & ID0_PTFS_NO_AARCH32)) {
 		smmu->features |= ARM_SMMU_FEAT_FMT_AARCH32_L;
-- 
2.8.1.dirty

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

* [PATCH v7 10/22] iommu/arm-smmu: Handle stream IDs more dynamically
@ 2016-09-12 16:13     ` Robin Murphy
  0 siblings, 0 replies; 104+ messages in thread
From: Robin Murphy @ 2016-09-12 16:13 UTC (permalink / raw)
  To: linux-arm-kernel

Rather than assuming fixed worst-case values for stream IDs and SMR
masks, keep track of whatever implemented bits the hardware actually
reports. This also obviates the slightly questionable validation of SMR
fields in isolation - rather than aborting the whole SMMU probe for a
hardware configuration which is still architecturally valid, we can
simply refuse masters later if they try to claim an unrepresentable ID
or mask (which almost certainly implies a DT error anyway).

Acked-by: Will Deacon <will.deacon@arm.com>
Tested-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Signed-off-by: Robin Murphy <robin.murphy@arm.com>
---
 drivers/iommu/arm-smmu.c | 43 ++++++++++++++++++++++---------------------
 1 file changed, 22 insertions(+), 21 deletions(-)

diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index 4b1c87e947fd..f86d7887f69a 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -165,9 +165,7 @@
 #define ARM_SMMU_GR0_SMR(n)		(0x800 + ((n) << 2))
 #define SMR_VALID			(1 << 31)
 #define SMR_MASK_SHIFT			16
-#define SMR_MASK_MASK			0x7fff
 #define SMR_ID_SHIFT			0
-#define SMR_ID_MASK			0x7fff
 
 #define ARM_SMMU_GR0_S2CR(n)		(0xc00 + ((n) << 2))
 #define S2CR_CBNDX_SHIFT		0
@@ -346,6 +344,8 @@ struct arm_smmu_device {
 	atomic_t			irptndx;
 
 	u32				num_mapping_groups;
+	u16				streamid_mask;
+	u16				smr_mask_mask;
 	DECLARE_BITMAP(smr_map, ARM_SMMU_MAX_SMRS);
 
 	unsigned long			va_size;
@@ -1715,39 +1715,40 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu)
 		dev_notice(smmu->dev,
 			   "\t(IDR0.CTTW overridden by dma-coherent property)\n");
 
+	/* Max. number of entries we have for stream matching/indexing */
+	size = 1 << ((id >> ID0_NUMSIDB_SHIFT) & ID0_NUMSIDB_MASK);
+	smmu->streamid_mask = size - 1;
 	if (id & ID0_SMS) {
-		u32 smr, sid, mask;
+		u32 smr;
 
 		smmu->features |= ARM_SMMU_FEAT_STREAM_MATCH;
-		smmu->num_mapping_groups = (id >> ID0_NUMSMRG_SHIFT) &
-					   ID0_NUMSMRG_MASK;
-		if (smmu->num_mapping_groups == 0) {
+		size = (id >> ID0_NUMSMRG_SHIFT) & ID0_NUMSMRG_MASK;
+		if (size == 0) {
 			dev_err(smmu->dev,
 				"stream-matching supported, but no SMRs present!\n");
 			return -ENODEV;
 		}
 
-		smr = SMR_MASK_MASK << SMR_MASK_SHIFT;
-		smr |= (SMR_ID_MASK << SMR_ID_SHIFT);
+		/*
+		 * SMR.ID bits may not be preserved if the corresponding MASK
+		 * bits are set, so check each one separately. We can reject
+		 * masters later if they try to claim IDs outside these masks.
+		 */
+		smr = smmu->streamid_mask << SMR_ID_SHIFT;
 		writel_relaxed(smr, gr0_base + ARM_SMMU_GR0_SMR(0));
 		smr = readl_relaxed(gr0_base + ARM_SMMU_GR0_SMR(0));
+		smmu->streamid_mask = smr >> SMR_ID_SHIFT;
 
-		mask = (smr >> SMR_MASK_SHIFT) & SMR_MASK_MASK;
-		sid = (smr >> SMR_ID_SHIFT) & SMR_ID_MASK;
-		if ((mask & sid) != sid) {
-			dev_err(smmu->dev,
-				"SMR mask bits (0x%x) insufficient for ID field (0x%x)\n",
-				mask, sid);
-			return -ENODEV;
-		}
+		smr = smmu->streamid_mask << SMR_MASK_SHIFT;
+		writel_relaxed(smr, gr0_base + ARM_SMMU_GR0_SMR(0));
+		smr = readl_relaxed(gr0_base + ARM_SMMU_GR0_SMR(0));
+		smmu->smr_mask_mask = smr >> SMR_MASK_SHIFT;
 
 		dev_notice(smmu->dev,
-			   "\tstream matching with %u register groups, mask 0x%x",
-			   smmu->num_mapping_groups, mask);
-	} else {
-		smmu->num_mapping_groups = (id >> ID0_NUMSIDB_SHIFT) &
-					   ID0_NUMSIDB_MASK;
+			   "\tstream matching with %lu register groups, mask 0x%x",
+			   size, smmu->smr_mask_mask);
 	}
+	smmu->num_mapping_groups = size;
 
 	if (smmu->version < ARM_SMMU_V2 || !(id & ID0_PTFS_NO_AARCH32)) {
 		smmu->features |= ARM_SMMU_FEAT_FMT_AARCH32_L;
-- 
2.8.1.dirty

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

* [PATCH v7 11/22] iommu/arm-smmu: Consolidate stream map entry state
  2016-09-12 16:13 ` Robin Murphy
@ 2016-09-12 16:13     ` Robin Murphy
  -1 siblings, 0 replies; 104+ messages in thread
From: Robin Murphy @ 2016-09-12 16:13 UTC (permalink / raw)
  To: will.deacon-5wv7dgnIgG8, joro-zLv9SwRftAIdnm+yROfE0A,
	iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, punit.agrawal-5wv7dgnIgG8,
	thunder.leizhen-hv44wF8Li93QT0dZR+AlfA

In order to consider SMR masking, we really want to be able to validate
ID/mask pairs against existing SMR contents to prevent stream match
conflicts, which at best would cause transactions to fault unexpectedly,
and at worst lead to silent unpredictable behaviour. With our SMMU
instance data holding only an allocator bitmap, and the SMR values
themselves scattered across master configs hanging off devices which we
may have no way of finding, there's essentially no way short of digging
everything back out of the hardware. Similarly, the thought of power
management ops to support suspend/resume faces the exact same problem.

By massaging the software state into a closer shape to the underlying
hardware, everything comes together quite nicely; the allocator and the
high-level view of the data become a single centralised state which we
can easily keep track of, and to which any updates can be validated in
full before being synchronised to the hardware itself.

Signed-off-by: Robin Murphy <robin.murphy-5wv7dgnIgG8@public.gmane.org>

---

- Make INVALID_SMENDX checks explicit
---
 drivers/iommu/arm-smmu.c | 147 +++++++++++++++++++++++++++--------------------
 1 file changed, 86 insertions(+), 61 deletions(-)

diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index f86d7887f69a..dfe13780ba54 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -28,6 +28,7 @@
 
 #define pr_fmt(fmt) "arm-smmu: " fmt
 
+#include <linux/atomic.h>
 #include <linux/delay.h>
 #include <linux/dma-iommu.h>
 #include <linux/dma-mapping.h>
@@ -55,9 +56,6 @@
 /* Maximum number of context banks per SMMU */
 #define ARM_SMMU_MAX_CBS		128
 
-/* Maximum number of mapping groups per SMMU */
-#define ARM_SMMU_MAX_SMRS		128
-
 /* SMMU global address space */
 #define ARM_SMMU_GR0(smmu)		((smmu)->base)
 #define ARM_SMMU_GR1(smmu)		((smmu)->base + (1 << (smmu)->pgshift))
@@ -295,16 +293,17 @@ enum arm_smmu_implementation {
 };
 
 struct arm_smmu_smr {
-	u8				idx;
 	u16				mask;
 	u16				id;
+	bool				valid;
 };
 
 struct arm_smmu_master_cfg {
 	int				num_streamids;
 	u16				streamids[MAX_MASTER_STREAMIDS];
-	struct arm_smmu_smr		*smrs;
+	s16				smendx[MAX_MASTER_STREAMIDS];
 };
+#define INVALID_SMENDX			-1
 
 struct arm_smmu_master {
 	struct device_node		*of_node;
@@ -346,7 +345,7 @@ struct arm_smmu_device {
 	u32				num_mapping_groups;
 	u16				streamid_mask;
 	u16				smr_mask_mask;
-	DECLARE_BITMAP(smr_map, ARM_SMMU_MAX_SMRS);
+	struct arm_smmu_smr		*smrs;
 
 	unsigned long			va_size;
 	unsigned long			ipa_size;
@@ -550,6 +549,7 @@ static int register_smmu_master(struct arm_smmu_device *smmu,
 			return -ERANGE;
 		}
 		master->cfg.streamids[i] = streamid;
+		master->cfg.smendx[i] = INVALID_SMENDX;
 	}
 	return insert_smmu_master(smmu, master);
 }
@@ -1080,79 +1080,91 @@ 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_alloc_smr(struct arm_smmu_device *smmu)
 {
 	int i;
-	struct arm_smmu_smr *smrs;
-	void __iomem *gr0_base = ARM_SMMU_GR0(smmu);
 
-	if (!(smmu->features & ARM_SMMU_FEAT_STREAM_MATCH))
-		return 0;
+	for (i = 0; i < smmu->num_mapping_groups; i++)
+		if (!cmpxchg(&smmu->smrs[i].valid, false, true))
+			return i;
 
-	if (cfg->smrs)
-		return -EEXIST;
+	return INVALID_SMENDX;
+}
 
-	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;
-	}
+static void arm_smmu_free_smr(struct arm_smmu_device *smmu, int idx)
+{
+	writel_relaxed(~SMR_VALID, ARM_SMMU_GR0(smmu) + ARM_SMMU_GR0_SMR(idx));
+	WRITE_ONCE(smmu->smrs[idx].valid, false);
+}
+
+static void arm_smmu_write_smr(struct arm_smmu_device *smmu, int idx)
+{
+	struct arm_smmu_smr *smr = smmu->smrs + idx;
+	u32 reg = (smr->id & smmu->streamid_mask) << SMR_ID_SHIFT |
+		  (smr->mask & smmu->smr_mask_mask) << SMR_MASK_SHIFT;
+
+	if (smr->valid)
+		reg |= SMR_VALID;
+	writel_relaxed(reg, ARM_SMMU_GR0(smmu) + ARM_SMMU_GR0_SMR(idx));
+}
+
+static int arm_smmu_master_alloc_smes(struct arm_smmu_device *smmu,
+				      struct arm_smmu_master_cfg *cfg)
+{
+	struct arm_smmu_smr *smrs = smmu->smrs;
+	int i, idx;
 
 	/* 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 (cfg->smendx[i] != INVALID_SMENDX)
+			return -EEXIST;
+
+		/* ...except on stream indexing hardware, of course */
+		if (!smrs) {
+			cfg->smendx[i] = cfg->streamids[i];
+			continue;
+		}
+
+		idx = arm_smmu_alloc_smr(smmu);
 		if (idx < 0) {
 			dev_err(smmu->dev, "failed to allocate free SMR\n");
 			goto err_free_smrs;
 		}
+		cfg->smendx[i] = idx;
 
-		smrs[i] = (struct arm_smmu_smr) {
-			.idx	= idx,
-			.mask	= 0, /* We don't currently share SMRs */
-			.id	= cfg->streamids[i],
-		};
+		smrs[idx].id = cfg->streamids[i];
+		smrs[idx].mask = 0; /* We don't currently share SMRs */
 	}
 
+	if (!smrs)
+		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));
-	}
+	for (i = 0; i < cfg->num_streamids; ++i)
+		arm_smmu_write_smr(smmu, cfg->smendx[i]);
 
-	cfg->smrs = smrs;
 	return 0;
 
 err_free_smrs:
-	while (--i >= 0)
-		__arm_smmu_free_bitmap(smmu->smr_map, smrs[i].idx);
-	kfree(smrs);
+	while (i--) {
+		arm_smmu_free_smr(smmu, cfg->smendx[i]);
+		cfg->smendx[i] = INVALID_SMENDX;
+	}
 	return -ENOSPC;
 }
 
-static void arm_smmu_master_free_smrs(struct arm_smmu_device *smmu,
+static void arm_smmu_master_free_smes(struct arm_smmu_device *smmu,
 				      struct arm_smmu_master_cfg *cfg)
 {
 	int i;
-	void __iomem *gr0_base = ARM_SMMU_GR0(smmu);
-	struct arm_smmu_smr *smrs = cfg->smrs;
-
-	if (!smrs)
-		return;
 
 	/* Invalidate the SMRs before freeing back to the allocator */
 	for (i = 0; i < cfg->num_streamids; ++i) {
-		u8 idx = smrs[i].idx;
+		if (smmu->smrs)
+			arm_smmu_free_smr(smmu, cfg->smendx[i]);
 
-		writel_relaxed(~SMR_VALID, gr0_base + ARM_SMMU_GR0_SMR(idx));
-		__arm_smmu_free_bitmap(smmu->smr_map, idx);
+		cfg->smendx[i] = INVALID_SMENDX;
 	}
-
-	cfg->smrs = NULL;
-	kfree(smrs);
 }
 
 static int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain,
@@ -1172,14 +1184,14 @@ static int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain,
 		return 0;
 
 	/* Devices in an IOMMU group may already be configured */
-	ret = arm_smmu_master_configure_smrs(smmu, cfg);
+	ret = arm_smmu_master_alloc_smes(smmu, cfg);
 	if (ret)
 		return ret == -EEXIST ? 0 : ret;
 
 	for (i = 0; i < cfg->num_streamids; ++i) {
 		u32 idx, s2cr;
 
-		idx = cfg->smrs ? cfg->smrs[i].idx : cfg->streamids[i];
+		idx = cfg->smendx[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));
@@ -1195,22 +1207,22 @@ static void arm_smmu_domain_remove_master(struct arm_smmu_domain *smmu_domain,
 	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;
-
 	/*
 	 * 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];
+		int idx = cfg->smendx[i];
 		u32 reg = disable_bypass ? S2CR_TYPE_FAULT : S2CR_TYPE_BYPASS;
 
+		/* An IOMMU group is torn down by the first device to be removed */
+		if (idx == INVALID_SMENDX)
+			return;
+
 		writel_relaxed(reg, gr0_base + ARM_SMMU_GR0_S2CR(idx));
 	}
 
-	arm_smmu_master_free_smrs(smmu, cfg);
+	arm_smmu_master_free_smes(smmu, cfg);
 }
 
 static void arm_smmu_detach_dev(struct device *dev,
@@ -1424,8 +1436,11 @@ static int arm_smmu_init_pci_device(struct pci_dev *pdev,
 			break;
 
 	/* Avoid duplicate SIDs, as this can lead to SMR conflicts */
-	if (i == cfg->num_streamids)
-		cfg->streamids[cfg->num_streamids++] = sid;
+	if (i == cfg->num_streamids) {
+		cfg->streamids[i] = sid;
+		cfg->smendx[i] = INVALID_SMENDX;
+		cfg->num_streamids++;
+	}
 
 	return 0;
 }
@@ -1556,17 +1571,21 @@ static void arm_smmu_device_reset(struct arm_smmu_device *smmu)
 {
 	void __iomem *gr0_base = ARM_SMMU_GR0(smmu);
 	void __iomem *cb_base;
-	int i = 0;
+	int i;
 	u32 reg, major;
 
 	/* clear global FSR */
 	reg = readl_relaxed(ARM_SMMU_GR0_NS(smmu) + ARM_SMMU_GR0_sGFSR);
 	writel(reg, ARM_SMMU_GR0_NS(smmu) + ARM_SMMU_GR0_sGFSR);
 
-	/* Mark all SMRn as invalid and all S2CRn as bypass unless overridden */
+	/*
+	 * Reset stream mapping groups: Initial values mark all SMRn as
+	 * invalid and all S2CRn as bypass unless overridden.
+	 */
 	reg = disable_bypass ? S2CR_TYPE_FAULT : S2CR_TYPE_BYPASS;
 	for (i = 0; i < smmu->num_mapping_groups; ++i) {
-		writel_relaxed(0, gr0_base + ARM_SMMU_GR0_SMR(i));
+		if (smmu->smrs)
+			arm_smmu_write_smr(smmu, i);
 		writel_relaxed(reg, gr0_base + ARM_SMMU_GR0_S2CR(i));
 	}
 
@@ -1744,6 +1763,12 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu)
 		smr = readl_relaxed(gr0_base + ARM_SMMU_GR0_SMR(0));
 		smmu->smr_mask_mask = smr >> SMR_MASK_SHIFT;
 
+		/* Zero-initialised to mark as invalid */
+		smmu->smrs = devm_kcalloc(smmu->dev, size, sizeof(*smmu->smrs),
+					  GFP_KERNEL);
+		if (!smmu->smrs)
+			return -ENOMEM;
+
 		dev_notice(smmu->dev,
 			   "\tstream matching with %lu register groups, mask 0x%x",
 			   size, smmu->smr_mask_mask);
-- 
2.8.1.dirty

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

* [PATCH v7 11/22] iommu/arm-smmu: Consolidate stream map entry state
@ 2016-09-12 16:13     ` Robin Murphy
  0 siblings, 0 replies; 104+ messages in thread
From: Robin Murphy @ 2016-09-12 16:13 UTC (permalink / raw)
  To: linux-arm-kernel

In order to consider SMR masking, we really want to be able to validate
ID/mask pairs against existing SMR contents to prevent stream match
conflicts, which at best would cause transactions to fault unexpectedly,
and at worst lead to silent unpredictable behaviour. With our SMMU
instance data holding only an allocator bitmap, and the SMR values
themselves scattered across master configs hanging off devices which we
may have no way of finding, there's essentially no way short of digging
everything back out of the hardware. Similarly, the thought of power
management ops to support suspend/resume faces the exact same problem.

By massaging the software state into a closer shape to the underlying
hardware, everything comes together quite nicely; the allocator and the
high-level view of the data become a single centralised state which we
can easily keep track of, and to which any updates can be validated in
full before being synchronised to the hardware itself.

Signed-off-by: Robin Murphy <robin.murphy@arm.com>

---

- Make INVALID_SMENDX checks explicit
---
 drivers/iommu/arm-smmu.c | 147 +++++++++++++++++++++++++++--------------------
 1 file changed, 86 insertions(+), 61 deletions(-)

diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index f86d7887f69a..dfe13780ba54 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -28,6 +28,7 @@
 
 #define pr_fmt(fmt) "arm-smmu: " fmt
 
+#include <linux/atomic.h>
 #include <linux/delay.h>
 #include <linux/dma-iommu.h>
 #include <linux/dma-mapping.h>
@@ -55,9 +56,6 @@
 /* Maximum number of context banks per SMMU */
 #define ARM_SMMU_MAX_CBS		128
 
-/* Maximum number of mapping groups per SMMU */
-#define ARM_SMMU_MAX_SMRS		128
-
 /* SMMU global address space */
 #define ARM_SMMU_GR0(smmu)		((smmu)->base)
 #define ARM_SMMU_GR1(smmu)		((smmu)->base + (1 << (smmu)->pgshift))
@@ -295,16 +293,17 @@ enum arm_smmu_implementation {
 };
 
 struct arm_smmu_smr {
-	u8				idx;
 	u16				mask;
 	u16				id;
+	bool				valid;
 };
 
 struct arm_smmu_master_cfg {
 	int				num_streamids;
 	u16				streamids[MAX_MASTER_STREAMIDS];
-	struct arm_smmu_smr		*smrs;
+	s16				smendx[MAX_MASTER_STREAMIDS];
 };
+#define INVALID_SMENDX			-1
 
 struct arm_smmu_master {
 	struct device_node		*of_node;
@@ -346,7 +345,7 @@ struct arm_smmu_device {
 	u32				num_mapping_groups;
 	u16				streamid_mask;
 	u16				smr_mask_mask;
-	DECLARE_BITMAP(smr_map, ARM_SMMU_MAX_SMRS);
+	struct arm_smmu_smr		*smrs;
 
 	unsigned long			va_size;
 	unsigned long			ipa_size;
@@ -550,6 +549,7 @@ static int register_smmu_master(struct arm_smmu_device *smmu,
 			return -ERANGE;
 		}
 		master->cfg.streamids[i] = streamid;
+		master->cfg.smendx[i] = INVALID_SMENDX;
 	}
 	return insert_smmu_master(smmu, master);
 }
@@ -1080,79 +1080,91 @@ 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_alloc_smr(struct arm_smmu_device *smmu)
 {
 	int i;
-	struct arm_smmu_smr *smrs;
-	void __iomem *gr0_base = ARM_SMMU_GR0(smmu);
 
-	if (!(smmu->features & ARM_SMMU_FEAT_STREAM_MATCH))
-		return 0;
+	for (i = 0; i < smmu->num_mapping_groups; i++)
+		if (!cmpxchg(&smmu->smrs[i].valid, false, true))
+			return i;
 
-	if (cfg->smrs)
-		return -EEXIST;
+	return INVALID_SMENDX;
+}
 
-	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;
-	}
+static void arm_smmu_free_smr(struct arm_smmu_device *smmu, int idx)
+{
+	writel_relaxed(~SMR_VALID, ARM_SMMU_GR0(smmu) + ARM_SMMU_GR0_SMR(idx));
+	WRITE_ONCE(smmu->smrs[idx].valid, false);
+}
+
+static void arm_smmu_write_smr(struct arm_smmu_device *smmu, int idx)
+{
+	struct arm_smmu_smr *smr = smmu->smrs + idx;
+	u32 reg = (smr->id & smmu->streamid_mask) << SMR_ID_SHIFT |
+		  (smr->mask & smmu->smr_mask_mask) << SMR_MASK_SHIFT;
+
+	if (smr->valid)
+		reg |= SMR_VALID;
+	writel_relaxed(reg, ARM_SMMU_GR0(smmu) + ARM_SMMU_GR0_SMR(idx));
+}
+
+static int arm_smmu_master_alloc_smes(struct arm_smmu_device *smmu,
+				      struct arm_smmu_master_cfg *cfg)
+{
+	struct arm_smmu_smr *smrs = smmu->smrs;
+	int i, idx;
 
 	/* 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 (cfg->smendx[i] != INVALID_SMENDX)
+			return -EEXIST;
+
+		/* ...except on stream indexing hardware, of course */
+		if (!smrs) {
+			cfg->smendx[i] = cfg->streamids[i];
+			continue;
+		}
+
+		idx = arm_smmu_alloc_smr(smmu);
 		if (idx < 0) {
 			dev_err(smmu->dev, "failed to allocate free SMR\n");
 			goto err_free_smrs;
 		}
+		cfg->smendx[i] = idx;
 
-		smrs[i] = (struct arm_smmu_smr) {
-			.idx	= idx,
-			.mask	= 0, /* We don't currently share SMRs */
-			.id	= cfg->streamids[i],
-		};
+		smrs[idx].id = cfg->streamids[i];
+		smrs[idx].mask = 0; /* We don't currently share SMRs */
 	}
 
+	if (!smrs)
+		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));
-	}
+	for (i = 0; i < cfg->num_streamids; ++i)
+		arm_smmu_write_smr(smmu, cfg->smendx[i]);
 
-	cfg->smrs = smrs;
 	return 0;
 
 err_free_smrs:
-	while (--i >= 0)
-		__arm_smmu_free_bitmap(smmu->smr_map, smrs[i].idx);
-	kfree(smrs);
+	while (i--) {
+		arm_smmu_free_smr(smmu, cfg->smendx[i]);
+		cfg->smendx[i] = INVALID_SMENDX;
+	}
 	return -ENOSPC;
 }
 
-static void arm_smmu_master_free_smrs(struct arm_smmu_device *smmu,
+static void arm_smmu_master_free_smes(struct arm_smmu_device *smmu,
 				      struct arm_smmu_master_cfg *cfg)
 {
 	int i;
-	void __iomem *gr0_base = ARM_SMMU_GR0(smmu);
-	struct arm_smmu_smr *smrs = cfg->smrs;
-
-	if (!smrs)
-		return;
 
 	/* Invalidate the SMRs before freeing back to the allocator */
 	for (i = 0; i < cfg->num_streamids; ++i) {
-		u8 idx = smrs[i].idx;
+		if (smmu->smrs)
+			arm_smmu_free_smr(smmu, cfg->smendx[i]);
 
-		writel_relaxed(~SMR_VALID, gr0_base + ARM_SMMU_GR0_SMR(idx));
-		__arm_smmu_free_bitmap(smmu->smr_map, idx);
+		cfg->smendx[i] = INVALID_SMENDX;
 	}
-
-	cfg->smrs = NULL;
-	kfree(smrs);
 }
 
 static int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain,
@@ -1172,14 +1184,14 @@ static int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain,
 		return 0;
 
 	/* Devices in an IOMMU group may already be configured */
-	ret = arm_smmu_master_configure_smrs(smmu, cfg);
+	ret = arm_smmu_master_alloc_smes(smmu, cfg);
 	if (ret)
 		return ret == -EEXIST ? 0 : ret;
 
 	for (i = 0; i < cfg->num_streamids; ++i) {
 		u32 idx, s2cr;
 
-		idx = cfg->smrs ? cfg->smrs[i].idx : cfg->streamids[i];
+		idx = cfg->smendx[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));
@@ -1195,22 +1207,22 @@ static void arm_smmu_domain_remove_master(struct arm_smmu_domain *smmu_domain,
 	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;
-
 	/*
 	 * 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];
+		int idx = cfg->smendx[i];
 		u32 reg = disable_bypass ? S2CR_TYPE_FAULT : S2CR_TYPE_BYPASS;
 
+		/* An IOMMU group is torn down by the first device to be removed */
+		if (idx == INVALID_SMENDX)
+			return;
+
 		writel_relaxed(reg, gr0_base + ARM_SMMU_GR0_S2CR(idx));
 	}
 
-	arm_smmu_master_free_smrs(smmu, cfg);
+	arm_smmu_master_free_smes(smmu, cfg);
 }
 
 static void arm_smmu_detach_dev(struct device *dev,
@@ -1424,8 +1436,11 @@ static int arm_smmu_init_pci_device(struct pci_dev *pdev,
 			break;
 
 	/* Avoid duplicate SIDs, as this can lead to SMR conflicts */
-	if (i == cfg->num_streamids)
-		cfg->streamids[cfg->num_streamids++] = sid;
+	if (i == cfg->num_streamids) {
+		cfg->streamids[i] = sid;
+		cfg->smendx[i] = INVALID_SMENDX;
+		cfg->num_streamids++;
+	}
 
 	return 0;
 }
@@ -1556,17 +1571,21 @@ static void arm_smmu_device_reset(struct arm_smmu_device *smmu)
 {
 	void __iomem *gr0_base = ARM_SMMU_GR0(smmu);
 	void __iomem *cb_base;
-	int i = 0;
+	int i;
 	u32 reg, major;
 
 	/* clear global FSR */
 	reg = readl_relaxed(ARM_SMMU_GR0_NS(smmu) + ARM_SMMU_GR0_sGFSR);
 	writel(reg, ARM_SMMU_GR0_NS(smmu) + ARM_SMMU_GR0_sGFSR);
 
-	/* Mark all SMRn as invalid and all S2CRn as bypass unless overridden */
+	/*
+	 * Reset stream mapping groups: Initial values mark all SMRn as
+	 * invalid and all S2CRn as bypass unless overridden.
+	 */
 	reg = disable_bypass ? S2CR_TYPE_FAULT : S2CR_TYPE_BYPASS;
 	for (i = 0; i < smmu->num_mapping_groups; ++i) {
-		writel_relaxed(0, gr0_base + ARM_SMMU_GR0_SMR(i));
+		if (smmu->smrs)
+			arm_smmu_write_smr(smmu, i);
 		writel_relaxed(reg, gr0_base + ARM_SMMU_GR0_S2CR(i));
 	}
 
@@ -1744,6 +1763,12 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu)
 		smr = readl_relaxed(gr0_base + ARM_SMMU_GR0_SMR(0));
 		smmu->smr_mask_mask = smr >> SMR_MASK_SHIFT;
 
+		/* Zero-initialised to mark as invalid */
+		smmu->smrs = devm_kcalloc(smmu->dev, size, sizeof(*smmu->smrs),
+					  GFP_KERNEL);
+		if (!smmu->smrs)
+			return -ENOMEM;
+
 		dev_notice(smmu->dev,
 			   "\tstream matching with %lu register groups, mask 0x%x",
 			   size, smmu->smr_mask_mask);
-- 
2.8.1.dirty

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

* [PATCH v7 12/22] iommu/arm-smmu: Keep track of S2CR state
  2016-09-12 16:13 ` Robin Murphy
@ 2016-09-12 16:13     ` Robin Murphy
  -1 siblings, 0 replies; 104+ messages in thread
From: Robin Murphy @ 2016-09-12 16:13 UTC (permalink / raw)
  To: will.deacon-5wv7dgnIgG8, joro-zLv9SwRftAIdnm+yROfE0A,
	iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, lorenzo.pieralisi-5wv7dgnIgG8,
	jean-philippe.brucker-5wv7dgnIgG8, punit.agrawal-5wv7dgnIgG8,
	thunder.leizhen-hv44wF8Li93QT0dZR+AlfA,
	eric.auger-H+wXaHxf7aLQT0dZR+AlfA

Making S2CRs first-class citizens within the driver with a high-level
representation of their state offers a neat solution to a few problems:

Firstly, the information about which context a device's stream IDs are
associated with is already present by necessity in the S2CR. With that
state easily accessible we can refer directly to it and obviate the need
to track an IOMMU domain in each device's archdata (its earlier purpose
of enforcing correct attachment of multi-device groups now being handled
by the IOMMU core itself).

Secondly, the core API now deprecates explicit domain detach and expects
domain attach to move devices smoothly from one domain to another; for
SMMUv2, this notion maps directly to simply rewriting the S2CRs assigned
to the device. By giving the driver a suitable abstraction of those
S2CRs to work with, we can massively reduce the overhead of the current
heavy-handed "detach, free resources, reallocate resources, attach"
approach.

Thirdly, making the software state hardware-shaped and attached to the
SMMU instance once again makes suspend/resume of this register group
that much simpler to implement in future.

Signed-off-by: Robin Murphy <robin.murphy-5wv7dgnIgG8@public.gmane.org>

---

- Make INVALID_SMENDX checks explicit
---
 drivers/iommu/arm-smmu.c | 159 +++++++++++++++++++++++++++--------------------
 1 file changed, 93 insertions(+), 66 deletions(-)

diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index dfe13780ba54..69b6cab65421 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -170,12 +170,20 @@
 #define S2CR_CBNDX_MASK			0xff
 #define S2CR_TYPE_SHIFT			16
 #define S2CR_TYPE_MASK			0x3
-#define S2CR_TYPE_TRANS			(0 << S2CR_TYPE_SHIFT)
-#define S2CR_TYPE_BYPASS		(1 << S2CR_TYPE_SHIFT)
-#define S2CR_TYPE_FAULT			(2 << S2CR_TYPE_SHIFT)
+enum arm_smmu_s2cr_type {
+	S2CR_TYPE_TRANS,
+	S2CR_TYPE_BYPASS,
+	S2CR_TYPE_FAULT,
+};
 
 #define S2CR_PRIVCFG_SHIFT		24
-#define S2CR_PRIVCFG_UNPRIV		(2 << S2CR_PRIVCFG_SHIFT)
+#define S2CR_PRIVCFG_MASK		0x3
+enum arm_smmu_s2cr_privcfg {
+	S2CR_PRIVCFG_DEFAULT,
+	S2CR_PRIVCFG_DIPAN,
+	S2CR_PRIVCFG_UNPRIV,
+	S2CR_PRIVCFG_PRIV,
+};
 
 /* Context bank attribute registers */
 #define ARM_SMMU_GR1_CBAR(n)		(0x0 + ((n) << 2))
@@ -292,6 +300,16 @@ enum arm_smmu_implementation {
 	CAVIUM_SMMUV2,
 };
 
+struct arm_smmu_s2cr {
+	enum arm_smmu_s2cr_type		type;
+	enum arm_smmu_s2cr_privcfg	privcfg;
+	u8				cbndx;
+};
+
+#define s2cr_init_val (struct arm_smmu_s2cr){				\
+	.type = disable_bypass ? S2CR_TYPE_FAULT : S2CR_TYPE_BYPASS,	\
+}
+
 struct arm_smmu_smr {
 	u16				mask;
 	u16				id;
@@ -346,6 +364,7 @@ struct arm_smmu_device {
 	u16				streamid_mask;
 	u16				smr_mask_mask;
 	struct arm_smmu_smr		*smrs;
+	struct arm_smmu_s2cr		*s2crs;
 
 	unsigned long			va_size;
 	unsigned long			ipa_size;
@@ -1108,6 +1127,23 @@ static void arm_smmu_write_smr(struct arm_smmu_device *smmu, int idx)
 	writel_relaxed(reg, ARM_SMMU_GR0(smmu) + ARM_SMMU_GR0_SMR(idx));
 }
 
+static void arm_smmu_write_s2cr(struct arm_smmu_device *smmu, int idx)
+{
+	struct arm_smmu_s2cr *s2cr = smmu->s2crs + idx;
+	u32 reg = (s2cr->type & S2CR_TYPE_MASK) << S2CR_TYPE_SHIFT |
+		  (s2cr->cbndx & S2CR_CBNDX_MASK) << S2CR_CBNDX_SHIFT |
+		  (s2cr->privcfg & S2CR_PRIVCFG_MASK) << S2CR_PRIVCFG_SHIFT;
+
+	writel_relaxed(reg, ARM_SMMU_GR0(smmu) + ARM_SMMU_GR0_S2CR(idx));
+}
+
+static void arm_smmu_write_sme(struct arm_smmu_device *smmu, int idx)
+{
+	arm_smmu_write_s2cr(smmu, idx);
+	if (smmu->smrs)
+		arm_smmu_write_smr(smmu, idx);
+}
+
 static int arm_smmu_master_alloc_smes(struct arm_smmu_device *smmu,
 				      struct arm_smmu_master_cfg *cfg)
 {
@@ -1158,6 +1194,23 @@ static void arm_smmu_master_free_smes(struct arm_smmu_device *smmu,
 {
 	int i;
 
+	/*
+	 * 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) {
+		int idx = cfg->smendx[i];
+
+		/* An IOMMU group is torn down by the first device to be removed */
+		if (idx == INVALID_SMENDX)
+			return;
+
+		smmu->s2crs[idx] = s2cr_init_val;
+		arm_smmu_write_s2cr(smmu, idx);
+	}
+	/* Sync S2CR updates before touching anything else */
+	__iowmb();
+
 	/* Invalidate the SMRs before freeing back to the allocator */
 	for (i = 0; i < cfg->num_streamids; ++i) {
 		if (smmu->smrs)
@@ -1170,9 +1223,16 @@ static void arm_smmu_master_free_smes(struct arm_smmu_device *smmu,
 static int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain,
 				      struct arm_smmu_master_cfg *cfg)
 {
-	int i, ret;
+	int i, ret = 0;
 	struct arm_smmu_device *smmu = smmu_domain->smmu;
-	void __iomem *gr0_base = ARM_SMMU_GR0(smmu);
+	struct arm_smmu_s2cr *s2cr = smmu->s2crs;
+	enum arm_smmu_s2cr_type type = S2CR_TYPE_TRANS;
+	u8 cbndx = smmu_domain->cfg.cbndx;
+
+	if (cfg->smendx[0] == INVALID_SMENDX)
+		ret = arm_smmu_master_alloc_smes(smmu, cfg);
+	if (ret)
+		return ret;
 
 	/*
 	 * FIXME: This won't be needed once we have IOMMU-backed DMA ops
@@ -1181,58 +1241,21 @@ static int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain,
 	 * and a PCI device (i.e. a PCI host controller)
 	 */
 	if (smmu_domain->domain.type == IOMMU_DOMAIN_DMA)
-		return 0;
+		type = S2CR_TYPE_BYPASS;
 
-	/* Devices in an IOMMU group may already be configured */
-	ret = arm_smmu_master_alloc_smes(smmu, cfg);
-	if (ret)
-		return ret == -EEXIST ? 0 : ret;
-
-	for (i = 0; i < cfg->num_streamids; ++i) {
-		u32 idx, s2cr;
-
-		idx = cfg->smendx[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)
-{
-	int i;
-	struct arm_smmu_device *smmu = smmu_domain->smmu;
-	void __iomem *gr0_base = ARM_SMMU_GR0(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) {
 		int idx = cfg->smendx[i];
-		u32 reg = disable_bypass ? S2CR_TYPE_FAULT : S2CR_TYPE_BYPASS;
 
-		/* An IOMMU group is torn down by the first device to be removed */
-		if (idx == INVALID_SMENDX)
-			return;
+		/* Devices in an IOMMU group may already be configured */
+		if (type == s2cr[idx].type && cbndx == s2cr[idx].cbndx)
+			break;
 
-		writel_relaxed(reg, gr0_base + ARM_SMMU_GR0_S2CR(idx));
+		s2cr[idx].type = type;
+		s2cr[idx].privcfg = S2CR_PRIVCFG_UNPRIV;
+		s2cr[idx].cbndx = cbndx;
+		arm_smmu_write_s2cr(smmu, idx);
 	}
-
-	arm_smmu_master_free_smes(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);
+	return 0;
 }
 
 static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
@@ -1269,14 +1292,7 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
 	if (!cfg)
 		return -ENODEV;
 
-	/* Detach the dev from its current domain */
-	if (dev->archdata.iommu)
-		arm_smmu_detach_dev(dev, cfg);
-
-	ret = arm_smmu_domain_add_master(smmu_domain, cfg);
-	if (!ret)
-		dev->archdata.iommu = domain;
-	return ret;
+	return arm_smmu_domain_add_master(smmu_domain, cfg);
 }
 
 static int arm_smmu_map(struct iommu_domain *domain, unsigned long iova,
@@ -1477,6 +1493,12 @@ static int arm_smmu_add_device(struct device *dev)
 
 static void arm_smmu_remove_device(struct device *dev)
 {
+	struct arm_smmu_device *smmu = find_smmu_for_device(dev);
+	struct arm_smmu_master_cfg *cfg = find_smmu_master_cfg(dev);
+
+	if (smmu && cfg)
+		arm_smmu_master_free_smes(smmu, cfg);
+
 	iommu_group_remove_device(dev);
 }
 
@@ -1582,12 +1604,8 @@ static void arm_smmu_device_reset(struct arm_smmu_device *smmu)
 	 * Reset stream mapping groups: Initial values mark all SMRn as
 	 * invalid and all S2CRn as bypass unless overridden.
 	 */
-	reg = disable_bypass ? S2CR_TYPE_FAULT : S2CR_TYPE_BYPASS;
-	for (i = 0; i < smmu->num_mapping_groups; ++i) {
-		if (smmu->smrs)
-			arm_smmu_write_smr(smmu, i);
-		writel_relaxed(reg, gr0_base + ARM_SMMU_GR0_S2CR(i));
-	}
+	for (i = 0; i < smmu->num_mapping_groups; ++i)
+		arm_smmu_write_sme(smmu, i);
 
 	/*
 	 * Before clearing ARM_MMU500_ACTLR_CPRE, need to
@@ -1676,6 +1694,7 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu)
 	void __iomem *gr0_base = ARM_SMMU_GR0(smmu);
 	u32 id;
 	bool cttw_dt, cttw_reg;
+	int i;
 
 	dev_notice(smmu->dev, "probing hardware configuration...\n");
 	dev_notice(smmu->dev, "SMMUv%d with:\n",
@@ -1773,6 +1792,14 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu)
 			   "\tstream matching with %lu register groups, mask 0x%x",
 			   size, smmu->smr_mask_mask);
 	}
+	/* s2cr->type == 0 means translation, so initialise explicitly */
+	smmu->s2crs = devm_kmalloc_array(smmu->dev, size, sizeof(*smmu->s2crs),
+					 GFP_KERNEL);
+	if (!smmu->s2crs)
+		return -ENOMEM;
+	for (i = 0; i < size; i++)
+		smmu->s2crs[i] = s2cr_init_val;
+
 	smmu->num_mapping_groups = size;
 
 	if (smmu->version < ARM_SMMU_V2 || !(id & ID0_PTFS_NO_AARCH32)) {
-- 
2.8.1.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] 104+ messages in thread

* [PATCH v7 12/22] iommu/arm-smmu: Keep track of S2CR state
@ 2016-09-12 16:13     ` Robin Murphy
  0 siblings, 0 replies; 104+ messages in thread
From: Robin Murphy @ 2016-09-12 16:13 UTC (permalink / raw)
  To: linux-arm-kernel

Making S2CRs first-class citizens within the driver with a high-level
representation of their state offers a neat solution to a few problems:

Firstly, the information about which context a device's stream IDs are
associated with is already present by necessity in the S2CR. With that
state easily accessible we can refer directly to it and obviate the need
to track an IOMMU domain in each device's archdata (its earlier purpose
of enforcing correct attachment of multi-device groups now being handled
by the IOMMU core itself).

Secondly, the core API now deprecates explicit domain detach and expects
domain attach to move devices smoothly from one domain to another; for
SMMUv2, this notion maps directly to simply rewriting the S2CRs assigned
to the device. By giving the driver a suitable abstraction of those
S2CRs to work with, we can massively reduce the overhead of the current
heavy-handed "detach, free resources, reallocate resources, attach"
approach.

Thirdly, making the software state hardware-shaped and attached to the
SMMU instance once again makes suspend/resume of this register group
that much simpler to implement in future.

Signed-off-by: Robin Murphy <robin.murphy@arm.com>

---

- Make INVALID_SMENDX checks explicit
---
 drivers/iommu/arm-smmu.c | 159 +++++++++++++++++++++++++++--------------------
 1 file changed, 93 insertions(+), 66 deletions(-)

diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index dfe13780ba54..69b6cab65421 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -170,12 +170,20 @@
 #define S2CR_CBNDX_MASK			0xff
 #define S2CR_TYPE_SHIFT			16
 #define S2CR_TYPE_MASK			0x3
-#define S2CR_TYPE_TRANS			(0 << S2CR_TYPE_SHIFT)
-#define S2CR_TYPE_BYPASS		(1 << S2CR_TYPE_SHIFT)
-#define S2CR_TYPE_FAULT			(2 << S2CR_TYPE_SHIFT)
+enum arm_smmu_s2cr_type {
+	S2CR_TYPE_TRANS,
+	S2CR_TYPE_BYPASS,
+	S2CR_TYPE_FAULT,
+};
 
 #define S2CR_PRIVCFG_SHIFT		24
-#define S2CR_PRIVCFG_UNPRIV		(2 << S2CR_PRIVCFG_SHIFT)
+#define S2CR_PRIVCFG_MASK		0x3
+enum arm_smmu_s2cr_privcfg {
+	S2CR_PRIVCFG_DEFAULT,
+	S2CR_PRIVCFG_DIPAN,
+	S2CR_PRIVCFG_UNPRIV,
+	S2CR_PRIVCFG_PRIV,
+};
 
 /* Context bank attribute registers */
 #define ARM_SMMU_GR1_CBAR(n)		(0x0 + ((n) << 2))
@@ -292,6 +300,16 @@ enum arm_smmu_implementation {
 	CAVIUM_SMMUV2,
 };
 
+struct arm_smmu_s2cr {
+	enum arm_smmu_s2cr_type		type;
+	enum arm_smmu_s2cr_privcfg	privcfg;
+	u8				cbndx;
+};
+
+#define s2cr_init_val (struct arm_smmu_s2cr){				\
+	.type = disable_bypass ? S2CR_TYPE_FAULT : S2CR_TYPE_BYPASS,	\
+}
+
 struct arm_smmu_smr {
 	u16				mask;
 	u16				id;
@@ -346,6 +364,7 @@ struct arm_smmu_device {
 	u16				streamid_mask;
 	u16				smr_mask_mask;
 	struct arm_smmu_smr		*smrs;
+	struct arm_smmu_s2cr		*s2crs;
 
 	unsigned long			va_size;
 	unsigned long			ipa_size;
@@ -1108,6 +1127,23 @@ static void arm_smmu_write_smr(struct arm_smmu_device *smmu, int idx)
 	writel_relaxed(reg, ARM_SMMU_GR0(smmu) + ARM_SMMU_GR0_SMR(idx));
 }
 
+static void arm_smmu_write_s2cr(struct arm_smmu_device *smmu, int idx)
+{
+	struct arm_smmu_s2cr *s2cr = smmu->s2crs + idx;
+	u32 reg = (s2cr->type & S2CR_TYPE_MASK) << S2CR_TYPE_SHIFT |
+		  (s2cr->cbndx & S2CR_CBNDX_MASK) << S2CR_CBNDX_SHIFT |
+		  (s2cr->privcfg & S2CR_PRIVCFG_MASK) << S2CR_PRIVCFG_SHIFT;
+
+	writel_relaxed(reg, ARM_SMMU_GR0(smmu) + ARM_SMMU_GR0_S2CR(idx));
+}
+
+static void arm_smmu_write_sme(struct arm_smmu_device *smmu, int idx)
+{
+	arm_smmu_write_s2cr(smmu, idx);
+	if (smmu->smrs)
+		arm_smmu_write_smr(smmu, idx);
+}
+
 static int arm_smmu_master_alloc_smes(struct arm_smmu_device *smmu,
 				      struct arm_smmu_master_cfg *cfg)
 {
@@ -1158,6 +1194,23 @@ static void arm_smmu_master_free_smes(struct arm_smmu_device *smmu,
 {
 	int i;
 
+	/*
+	 * 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) {
+		int idx = cfg->smendx[i];
+
+		/* An IOMMU group is torn down by the first device to be removed */
+		if (idx == INVALID_SMENDX)
+			return;
+
+		smmu->s2crs[idx] = s2cr_init_val;
+		arm_smmu_write_s2cr(smmu, idx);
+	}
+	/* Sync S2CR updates before touching anything else */
+	__iowmb();
+
 	/* Invalidate the SMRs before freeing back to the allocator */
 	for (i = 0; i < cfg->num_streamids; ++i) {
 		if (smmu->smrs)
@@ -1170,9 +1223,16 @@ static void arm_smmu_master_free_smes(struct arm_smmu_device *smmu,
 static int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain,
 				      struct arm_smmu_master_cfg *cfg)
 {
-	int i, ret;
+	int i, ret = 0;
 	struct arm_smmu_device *smmu = smmu_domain->smmu;
-	void __iomem *gr0_base = ARM_SMMU_GR0(smmu);
+	struct arm_smmu_s2cr *s2cr = smmu->s2crs;
+	enum arm_smmu_s2cr_type type = S2CR_TYPE_TRANS;
+	u8 cbndx = smmu_domain->cfg.cbndx;
+
+	if (cfg->smendx[0] == INVALID_SMENDX)
+		ret = arm_smmu_master_alloc_smes(smmu, cfg);
+	if (ret)
+		return ret;
 
 	/*
 	 * FIXME: This won't be needed once we have IOMMU-backed DMA ops
@@ -1181,58 +1241,21 @@ static int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain,
 	 * and a PCI device (i.e. a PCI host controller)
 	 */
 	if (smmu_domain->domain.type == IOMMU_DOMAIN_DMA)
-		return 0;
+		type = S2CR_TYPE_BYPASS;
 
-	/* Devices in an IOMMU group may already be configured */
-	ret = arm_smmu_master_alloc_smes(smmu, cfg);
-	if (ret)
-		return ret == -EEXIST ? 0 : ret;
-
-	for (i = 0; i < cfg->num_streamids; ++i) {
-		u32 idx, s2cr;
-
-		idx = cfg->smendx[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)
-{
-	int i;
-	struct arm_smmu_device *smmu = smmu_domain->smmu;
-	void __iomem *gr0_base = ARM_SMMU_GR0(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) {
 		int idx = cfg->smendx[i];
-		u32 reg = disable_bypass ? S2CR_TYPE_FAULT : S2CR_TYPE_BYPASS;
 
-		/* An IOMMU group is torn down by the first device to be removed */
-		if (idx == INVALID_SMENDX)
-			return;
+		/* Devices in an IOMMU group may already be configured */
+		if (type == s2cr[idx].type && cbndx == s2cr[idx].cbndx)
+			break;
 
-		writel_relaxed(reg, gr0_base + ARM_SMMU_GR0_S2CR(idx));
+		s2cr[idx].type = type;
+		s2cr[idx].privcfg = S2CR_PRIVCFG_UNPRIV;
+		s2cr[idx].cbndx = cbndx;
+		arm_smmu_write_s2cr(smmu, idx);
 	}
-
-	arm_smmu_master_free_smes(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);
+	return 0;
 }
 
 static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
@@ -1269,14 +1292,7 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
 	if (!cfg)
 		return -ENODEV;
 
-	/* Detach the dev from its current domain */
-	if (dev->archdata.iommu)
-		arm_smmu_detach_dev(dev, cfg);
-
-	ret = arm_smmu_domain_add_master(smmu_domain, cfg);
-	if (!ret)
-		dev->archdata.iommu = domain;
-	return ret;
+	return arm_smmu_domain_add_master(smmu_domain, cfg);
 }
 
 static int arm_smmu_map(struct iommu_domain *domain, unsigned long iova,
@@ -1477,6 +1493,12 @@ static int arm_smmu_add_device(struct device *dev)
 
 static void arm_smmu_remove_device(struct device *dev)
 {
+	struct arm_smmu_device *smmu = find_smmu_for_device(dev);
+	struct arm_smmu_master_cfg *cfg = find_smmu_master_cfg(dev);
+
+	if (smmu && cfg)
+		arm_smmu_master_free_smes(smmu, cfg);
+
 	iommu_group_remove_device(dev);
 }
 
@@ -1582,12 +1604,8 @@ static void arm_smmu_device_reset(struct arm_smmu_device *smmu)
 	 * Reset stream mapping groups: Initial values mark all SMRn as
 	 * invalid and all S2CRn as bypass unless overridden.
 	 */
-	reg = disable_bypass ? S2CR_TYPE_FAULT : S2CR_TYPE_BYPASS;
-	for (i = 0; i < smmu->num_mapping_groups; ++i) {
-		if (smmu->smrs)
-			arm_smmu_write_smr(smmu, i);
-		writel_relaxed(reg, gr0_base + ARM_SMMU_GR0_S2CR(i));
-	}
+	for (i = 0; i < smmu->num_mapping_groups; ++i)
+		arm_smmu_write_sme(smmu, i);
 
 	/*
 	 * Before clearing ARM_MMU500_ACTLR_CPRE, need to
@@ -1676,6 +1694,7 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu)
 	void __iomem *gr0_base = ARM_SMMU_GR0(smmu);
 	u32 id;
 	bool cttw_dt, cttw_reg;
+	int i;
 
 	dev_notice(smmu->dev, "probing hardware configuration...\n");
 	dev_notice(smmu->dev, "SMMUv%d with:\n",
@@ -1773,6 +1792,14 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu)
 			   "\tstream matching with %lu register groups, mask 0x%x",
 			   size, smmu->smr_mask_mask);
 	}
+	/* s2cr->type == 0 means translation, so initialise explicitly */
+	smmu->s2crs = devm_kmalloc_array(smmu->dev, size, sizeof(*smmu->s2crs),
+					 GFP_KERNEL);
+	if (!smmu->s2crs)
+		return -ENOMEM;
+	for (i = 0; i < size; i++)
+		smmu->s2crs[i] = s2cr_init_val;
+
 	smmu->num_mapping_groups = size;
 
 	if (smmu->version < ARM_SMMU_V2 || !(id & ID0_PTFS_NO_AARCH32)) {
-- 
2.8.1.dirty

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

* [PATCH v7 13/22] iommu/arm-smmu: Refactor mmu-masters handling
  2016-09-12 16:13 ` Robin Murphy
@ 2016-09-12 16:13     ` Robin Murphy
  -1 siblings, 0 replies; 104+ messages in thread
From: Robin Murphy @ 2016-09-12 16:13 UTC (permalink / raw)
  To: will.deacon-5wv7dgnIgG8, joro-zLv9SwRftAIdnm+yROfE0A,
	iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, lorenzo.pieralisi-5wv7dgnIgG8,
	jean-philippe.brucker-5wv7dgnIgG8, punit.agrawal-5wv7dgnIgG8,
	thunder.leizhen-hv44wF8Li93QT0dZR+AlfA,
	eric.auger-H+wXaHxf7aLQT0dZR+AlfA

To be able to support the generic bindings and handle of_xlate() calls,
we need to be able to associate SMMUs and stream IDs directly with
devices *before* allocating IOMMU groups. Furthermore, to support real
default domains with multi-device groups we also have to handle domain
attach on a per-device basis, as the "whole group at a time" assumption
fails to properly handle subsequent devices added to a group after the
first has already triggered default domain creation and attachment.

To that end, use the now-vacant dev->archdata.iommu field for easy
config and SMMU instance lookup, and unify config management by chopping
down the platform-device-specific tree and probing the "mmu-masters"
property on-demand instead. This may add a bit of one-off overhead to
initially adding a new device, but we're about to deprecate that binding
in favour of the inherently-more-efficient generic ones anyway.

For the sake of simplicity, this patch does temporarily regress the case
of aliasing PCI devices by losing the duplicate stream ID detection that
the previous per-group config had. Stay tuned, because we'll be back to
fix that in a better and more general way momentarily...

Tested-by: Lorenzo Pieralisi <lorenzo.pieralisi-5wv7dgnIgG8@public.gmane.org>
Signed-off-by: Robin Murphy <robin.murphy-5wv7dgnIgG8@public.gmane.org>
---
 drivers/iommu/arm-smmu.c | 382 +++++++++++++----------------------------------
 1 file changed, 107 insertions(+), 275 deletions(-)

diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index 69b6cab65421..0a628f2c9297 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -317,18 +317,13 @@ struct arm_smmu_smr {
 };
 
 struct arm_smmu_master_cfg {
+	struct arm_smmu_device		*smmu;
 	int				num_streamids;
 	u16				streamids[MAX_MASTER_STREAMIDS];
 	s16				smendx[MAX_MASTER_STREAMIDS];
 };
 #define INVALID_SMENDX			-1
 
-struct arm_smmu_master {
-	struct device_node		*of_node;
-	struct rb_node			node;
-	struct arm_smmu_master_cfg	cfg;
-};
-
 struct arm_smmu_device {
 	struct device			*dev;
 
@@ -376,7 +371,6 @@ struct arm_smmu_device {
 	unsigned int			*irqs;
 
 	struct list_head		list;
-	struct rb_root			masters;
 
 	u32				cavium_id_base; /* Specific to Cavium */
 };
@@ -415,12 +409,6 @@ struct arm_smmu_domain {
 	struct iommu_domain		domain;
 };
 
-struct arm_smmu_phandle_args {
-	struct device_node *np;
-	int args_count;
-	uint32_t args[MAX_MASTER_STREAMIDS];
-};
-
 static DEFINE_SPINLOCK(arm_smmu_devices_lock);
 static LIST_HEAD(arm_smmu_devices);
 
@@ -462,132 +450,89 @@ static struct device_node *dev_get_dev_node(struct device *dev)
 
 		while (!pci_is_root_bus(bus))
 			bus = bus->parent;
-		return bus->bridge->parent->of_node;
+		return of_node_get(bus->bridge->parent->of_node);
 	}
 
-	return dev->of_node;
+	return of_node_get(dev->of_node);
 }
 
-static struct arm_smmu_master *find_smmu_master(struct arm_smmu_device *smmu,
-						struct device_node *dev_node)
+static int __arm_smmu_get_pci_sid(struct pci_dev *pdev, u16 alias, void *data)
 {
-	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;
+	*((__be32 *)data) = cpu_to_be32(alias);
+	return 0; /* Continue walking */
 }
 
-static struct arm_smmu_master_cfg *
-find_smmu_master_cfg(struct device *dev)
+static int __find_legacy_master_phandle(struct device *dev, void *data)
 {
-	struct arm_smmu_master_cfg *cfg = NULL;
-	struct iommu_group *group = iommu_group_get(dev);
+	struct of_phandle_iterator *it = *(void **)data;
+	struct device_node *np = it->node;
+	int err;
 
-	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)
-{
-	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 device *dev,
-				struct arm_smmu_phandle_args *masterspec)
-{
-	int i;
-	struct arm_smmu_master *master;
-
-	master = find_smmu_master(smmu, masterspec->np);
-	if (master) {
-		dev_err(dev,
-			"rejecting multiple registrations for master device %s\n",
-			masterspec->np->name);
-		return -EBUSY;
-	}
-
-	if (masterspec->args_count > MAX_MASTER_STREAMIDS) {
-		dev_err(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);
-	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(dev,
-				"stream ID for master device %s greater than maximum allowed (%d)\n",
-				masterspec->np->name, smmu->num_mapping_groups);
-			return -ERANGE;
+	of_for_each_phandle(it, err, dev->of_node, "mmu-masters",
+			    "#stream-id-cells", 0)
+		if (it->node == np) {
+			*(void **)data = dev;
+			return 1;
 		}
-		master->cfg.streamids[i] = streamid;
-		master->cfg.smendx[i] = INVALID_SMENDX;
-	}
-	return insert_smmu_master(smmu, master);
+	it->node = np;
+	return err;
 }
 
-static struct arm_smmu_device *find_smmu_for_device(struct device *dev)
+static int arm_smmu_register_legacy_master(struct device *dev)
 {
 	struct arm_smmu_device *smmu;
-	struct arm_smmu_master *master = NULL;
-	struct device_node *dev_node = dev_get_dev_node(dev);
+	struct arm_smmu_master_cfg *cfg;
+	struct device_node *np;
+	struct of_phandle_iterator it;
+	void *data = &it;
+	__be32 pci_sid;
+	int err;
 
+	np = dev_get_dev_node(dev);
+	if (!np || !of_find_property(np, "#stream-id-cells", NULL)) {
+		of_node_put(np);
+		return -ENODEV;
+	}
+
+	it.node = np;
 	spin_lock(&arm_smmu_devices_lock);
 	list_for_each_entry(smmu, &arm_smmu_devices, list) {
-		master = find_smmu_master(smmu, dev_node);
-		if (master)
+		err = __find_legacy_master_phandle(smmu->dev, &data);
+		if (err)
 			break;
 	}
 	spin_unlock(&arm_smmu_devices_lock);
+	of_node_put(np);
+	if (err == 0)
+		return -ENODEV;
+	if (err < 0)
+		return err;
 
-	return master ? smmu : NULL;
+	if (it.cur_count > MAX_MASTER_STREAMIDS) {
+		dev_err(smmu->dev,
+			"reached maximum number (%d) of stream IDs for master device %s\n",
+			MAX_MASTER_STREAMIDS, dev_name(dev));
+		return -ENOSPC;
+	}
+	if (dev_is_pci(dev)) {
+		/* "mmu-masters" assumes Stream ID == Requester ID */
+		pci_for_each_dma_alias(to_pci_dev(dev), __arm_smmu_get_pci_sid,
+				       &pci_sid);
+		it.cur = &pci_sid;
+		it.cur_count = 1;
+	}
+
+	cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
+	if (!cfg)
+		return -ENOMEM;
+
+	cfg->smmu = smmu;
+	dev->archdata.iommu = cfg;
+
+	while (it.cur_count--)
+		cfg->streamids[cfg->num_streamids++] = be32_to_cpup(it.cur++);
+
+	return 0;
 }
 
 static int __arm_smmu_alloc_bitmap(unsigned long *map, int start, int end)
@@ -1119,8 +1064,7 @@ static void arm_smmu_free_smr(struct arm_smmu_device *smmu, int idx)
 static void arm_smmu_write_smr(struct arm_smmu_device *smmu, int idx)
 {
 	struct arm_smmu_smr *smr = smmu->smrs + idx;
-	u32 reg = (smr->id & smmu->streamid_mask) << SMR_ID_SHIFT |
-		  (smr->mask & smmu->smr_mask_mask) << SMR_MASK_SHIFT;
+	u32 reg = smr->id << SMR_ID_SHIFT | smr->mask << SMR_MASK_SHIFT;
 
 	if (smr->valid)
 		reg |= SMR_VALID;
@@ -1189,9 +1133,9 @@ err_free_smrs:
 	return -ENOSPC;
 }
 
-static void arm_smmu_master_free_smes(struct arm_smmu_device *smmu,
-				      struct arm_smmu_master_cfg *cfg)
+static void arm_smmu_master_free_smes(struct arm_smmu_master_cfg *cfg)
 {
+	struct arm_smmu_device *smmu = cfg->smmu;
 	int i;
 
 	/*
@@ -1262,17 +1206,15 @@ 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;
 	}
 
 	/* Ensure that the domain is finalised */
-	ret = arm_smmu_init_domain_context(domain, smmu);
+	ret = arm_smmu_init_domain_context(domain, cfg->smmu);
 	if (ret < 0)
 		return ret;
 
@@ -1280,18 +1222,14 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
 	 * Sanity check the domain. We don't support domains across
 	 * different SMMUs.
 	 */
-	if (smmu_domain->smmu != smmu) {
+	if (smmu_domain->smmu != cfg->smmu) {
 		dev_err(dev,
 			"cannot attach to SMMU %s whilst already attached to domain on SMMU %s\n",
-			dev_name(smmu_domain->smmu->dev), dev_name(smmu->dev));
+			dev_name(smmu_domain->smmu->dev), dev_name(cfg->smmu->dev));
 		return -EINVAL;
 	}
 
 	/* Looks ok, so add the device to the domain */
-	cfg = find_smmu_master_cfg(dev);
-	if (!cfg)
-		return -ENODEV;
-
 	return arm_smmu_domain_add_master(smmu_domain, cfg);
 }
 
@@ -1411,120 +1349,65 @@ static bool arm_smmu_capable(enum iommu_cap cap)
 	}
 }
 
-static int __arm_smmu_get_pci_sid(struct pci_dev *pdev, u16 alias, void *data)
-{
-	*((u16 *)data) = alias;
-	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)
-{
-	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;
-
-	/*
-	 * 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[i] = sid;
-		cfg->smendx[i] = INVALID_SMENDX;
-		cfg->num_streamids++;
-	}
-
-	return 0;
-}
-
-static int arm_smmu_init_platform_device(struct device *dev,
-					 struct iommu_group *group)
-{
-	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);
-
-	return 0;
-}
-
 static int arm_smmu_add_device(struct device *dev)
 {
+	struct arm_smmu_master_cfg *cfg;
 	struct iommu_group *group;
+	int i, ret;
+
+	ret = arm_smmu_register_legacy_master(dev);
+	cfg = dev->archdata.iommu;
+	if (ret)
+		goto out_free;
+
+	ret = -EINVAL;
+	for (i = 0; i < cfg->num_streamids; i++) {
+		u16 sid = cfg->streamids[i];
+
+		if (sid & ~cfg->smmu->streamid_mask) {
+			dev_err(dev, "stream ID 0x%x out of range for SMMU (0x%x)\n",
+				sid, cfg->smmu->streamid_mask);
+			goto out_free;
+		}
+		cfg->smendx[i] = INVALID_SMENDX;
+	}
 
 	group = iommu_group_get_for_dev(dev);
-	if (IS_ERR(group))
-		return PTR_ERR(group);
-
+	if (IS_ERR(group)) {
+		ret = PTR_ERR(group);
+		goto out_free;
+	}
 	iommu_group_put(group);
 	return 0;
+
+out_free:
+	kfree(cfg);
+	dev->archdata.iommu = NULL;
+	return ret;
 }
 
 static void arm_smmu_remove_device(struct device *dev)
 {
-	struct arm_smmu_device *smmu = find_smmu_for_device(dev);
-	struct arm_smmu_master_cfg *cfg = find_smmu_master_cfg(dev);
+	struct arm_smmu_master_cfg *cfg = dev->archdata.iommu;
 
-	if (smmu && cfg)
-		arm_smmu_master_free_smes(smmu, cfg);
+	if (!cfg)
+		return;
 
+	arm_smmu_master_free_smes(cfg);
 	iommu_group_remove_device(dev);
+	kfree(cfg);
+	dev->archdata.iommu = NULL;
 }
 
 static struct iommu_group *arm_smmu_device_group(struct device *dev)
 {
 	struct iommu_group *group;
-	int ret;
 
 	if (dev_is_pci(dev))
 		group = pci_device_group(dev);
 	else
 		group = generic_device_group(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);
-
-	if (ret) {
-		iommu_group_put(group);
-		group = ERR_PTR(ret);
-	}
-
 	return group;
 }
 
@@ -1938,9 +1821,6 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
 	struct resource *res;
 	struct arm_smmu_device *smmu;
 	struct device *dev = &pdev->dev;
-	struct rb_node *node;
-	struct of_phandle_iterator it;
-	struct arm_smmu_phandle_args *masterspec;
 	int num_irqs, i, err;
 
 	smmu = devm_kzalloc(dev, sizeof(*smmu), GFP_KERNEL);
@@ -2001,37 +1881,6 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
 	if (err)
 		return err;
 
-	i = 0;
-	smmu->masters = RB_ROOT;
-
-	err = -ENOMEM;
-	/* No need to zero the memory for masterspec */
-	masterspec = kmalloc(sizeof(*masterspec), GFP_KERNEL);
-	if (!masterspec)
-		goto out_put_masters;
-
-	of_for_each_phandle(&it, err, dev->of_node,
-			    "mmu-masters", "#stream-id-cells", 0) {
-		int count = of_phandle_iterator_args(&it, masterspec->args,
-						     MAX_MASTER_STREAMIDS);
-		masterspec->np		= of_node_get(it.node);
-		masterspec->args_count	= count;
-
-		err = register_smmu_master(smmu, dev, masterspec);
-		if (err) {
-			dev_err(dev, "failed to add master %s\n",
-				masterspec->np->name);
-			kfree(masterspec);
-			goto out_put_masters;
-		}
-
-		i++;
-	}
-
-	dev_notice(dev, "registered %d master devices\n", i);
-
-	kfree(masterspec);
-
 	parse_driver_options(smmu);
 
 	if (smmu->version == ARM_SMMU_V2 &&
@@ -2039,8 +1888,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) {
@@ -2052,7 +1900,7 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
 		if (err) {
 			dev_err(dev, "failed to request global IRQ %d (%u)\n",
 				i, smmu->irqs[i]);
-			goto out_put_masters;
+			return err;
 		}
 	}
 
@@ -2063,22 +1911,12 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
 
 	arm_smmu_device_reset(smmu);
 	return 0;
-
-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;
 }
 
 static int arm_smmu_device_remove(struct platform_device *pdev)
 {
 	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) {
@@ -2093,12 +1931,6 @@ static int arm_smmu_device_remove(struct platform_device *pdev)
 	if (!smmu)
 		return -ENODEV;
 
-	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(dev, "removing device with active domains!\n");
 
-- 
2.8.1.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] 104+ messages in thread

* [PATCH v7 13/22] iommu/arm-smmu: Refactor mmu-masters handling
@ 2016-09-12 16:13     ` Robin Murphy
  0 siblings, 0 replies; 104+ messages in thread
From: Robin Murphy @ 2016-09-12 16:13 UTC (permalink / raw)
  To: linux-arm-kernel

To be able to support the generic bindings and handle of_xlate() calls,
we need to be able to associate SMMUs and stream IDs directly with
devices *before* allocating IOMMU groups. Furthermore, to support real
default domains with multi-device groups we also have to handle domain
attach on a per-device basis, as the "whole group at a time" assumption
fails to properly handle subsequent devices added to a group after the
first has already triggered default domain creation and attachment.

To that end, use the now-vacant dev->archdata.iommu field for easy
config and SMMU instance lookup, and unify config management by chopping
down the platform-device-specific tree and probing the "mmu-masters"
property on-demand instead. This may add a bit of one-off overhead to
initially adding a new device, but we're about to deprecate that binding
in favour of the inherently-more-efficient generic ones anyway.

For the sake of simplicity, this patch does temporarily regress the case
of aliasing PCI devices by losing the duplicate stream ID detection that
the previous per-group config had. Stay tuned, because we'll be back to
fix that in a better and more general way momentarily...

Tested-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Signed-off-by: Robin Murphy <robin.murphy@arm.com>
---
 drivers/iommu/arm-smmu.c | 382 +++++++++++++----------------------------------
 1 file changed, 107 insertions(+), 275 deletions(-)

diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index 69b6cab65421..0a628f2c9297 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -317,18 +317,13 @@ struct arm_smmu_smr {
 };
 
 struct arm_smmu_master_cfg {
+	struct arm_smmu_device		*smmu;
 	int				num_streamids;
 	u16				streamids[MAX_MASTER_STREAMIDS];
 	s16				smendx[MAX_MASTER_STREAMIDS];
 };
 #define INVALID_SMENDX			-1
 
-struct arm_smmu_master {
-	struct device_node		*of_node;
-	struct rb_node			node;
-	struct arm_smmu_master_cfg	cfg;
-};
-
 struct arm_smmu_device {
 	struct device			*dev;
 
@@ -376,7 +371,6 @@ struct arm_smmu_device {
 	unsigned int			*irqs;
 
 	struct list_head		list;
-	struct rb_root			masters;
 
 	u32				cavium_id_base; /* Specific to Cavium */
 };
@@ -415,12 +409,6 @@ struct arm_smmu_domain {
 	struct iommu_domain		domain;
 };
 
-struct arm_smmu_phandle_args {
-	struct device_node *np;
-	int args_count;
-	uint32_t args[MAX_MASTER_STREAMIDS];
-};
-
 static DEFINE_SPINLOCK(arm_smmu_devices_lock);
 static LIST_HEAD(arm_smmu_devices);
 
@@ -462,132 +450,89 @@ static struct device_node *dev_get_dev_node(struct device *dev)
 
 		while (!pci_is_root_bus(bus))
 			bus = bus->parent;
-		return bus->bridge->parent->of_node;
+		return of_node_get(bus->bridge->parent->of_node);
 	}
 
-	return dev->of_node;
+	return of_node_get(dev->of_node);
 }
 
-static struct arm_smmu_master *find_smmu_master(struct arm_smmu_device *smmu,
-						struct device_node *dev_node)
+static int __arm_smmu_get_pci_sid(struct pci_dev *pdev, u16 alias, void *data)
 {
-	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;
+	*((__be32 *)data) = cpu_to_be32(alias);
+	return 0; /* Continue walking */
 }
 
-static struct arm_smmu_master_cfg *
-find_smmu_master_cfg(struct device *dev)
+static int __find_legacy_master_phandle(struct device *dev, void *data)
 {
-	struct arm_smmu_master_cfg *cfg = NULL;
-	struct iommu_group *group = iommu_group_get(dev);
+	struct of_phandle_iterator *it = *(void **)data;
+	struct device_node *np = it->node;
+	int err;
 
-	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)
-{
-	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 device *dev,
-				struct arm_smmu_phandle_args *masterspec)
-{
-	int i;
-	struct arm_smmu_master *master;
-
-	master = find_smmu_master(smmu, masterspec->np);
-	if (master) {
-		dev_err(dev,
-			"rejecting multiple registrations for master device %s\n",
-			masterspec->np->name);
-		return -EBUSY;
-	}
-
-	if (masterspec->args_count > MAX_MASTER_STREAMIDS) {
-		dev_err(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);
-	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(dev,
-				"stream ID for master device %s greater than maximum allowed (%d)\n",
-				masterspec->np->name, smmu->num_mapping_groups);
-			return -ERANGE;
+	of_for_each_phandle(it, err, dev->of_node, "mmu-masters",
+			    "#stream-id-cells", 0)
+		if (it->node == np) {
+			*(void **)data = dev;
+			return 1;
 		}
-		master->cfg.streamids[i] = streamid;
-		master->cfg.smendx[i] = INVALID_SMENDX;
-	}
-	return insert_smmu_master(smmu, master);
+	it->node = np;
+	return err;
 }
 
-static struct arm_smmu_device *find_smmu_for_device(struct device *dev)
+static int arm_smmu_register_legacy_master(struct device *dev)
 {
 	struct arm_smmu_device *smmu;
-	struct arm_smmu_master *master = NULL;
-	struct device_node *dev_node = dev_get_dev_node(dev);
+	struct arm_smmu_master_cfg *cfg;
+	struct device_node *np;
+	struct of_phandle_iterator it;
+	void *data = &it;
+	__be32 pci_sid;
+	int err;
 
+	np = dev_get_dev_node(dev);
+	if (!np || !of_find_property(np, "#stream-id-cells", NULL)) {
+		of_node_put(np);
+		return -ENODEV;
+	}
+
+	it.node = np;
 	spin_lock(&arm_smmu_devices_lock);
 	list_for_each_entry(smmu, &arm_smmu_devices, list) {
-		master = find_smmu_master(smmu, dev_node);
-		if (master)
+		err = __find_legacy_master_phandle(smmu->dev, &data);
+		if (err)
 			break;
 	}
 	spin_unlock(&arm_smmu_devices_lock);
+	of_node_put(np);
+	if (err == 0)
+		return -ENODEV;
+	if (err < 0)
+		return err;
 
-	return master ? smmu : NULL;
+	if (it.cur_count > MAX_MASTER_STREAMIDS) {
+		dev_err(smmu->dev,
+			"reached maximum number (%d) of stream IDs for master device %s\n",
+			MAX_MASTER_STREAMIDS, dev_name(dev));
+		return -ENOSPC;
+	}
+	if (dev_is_pci(dev)) {
+		/* "mmu-masters" assumes Stream ID == Requester ID */
+		pci_for_each_dma_alias(to_pci_dev(dev), __arm_smmu_get_pci_sid,
+				       &pci_sid);
+		it.cur = &pci_sid;
+		it.cur_count = 1;
+	}
+
+	cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
+	if (!cfg)
+		return -ENOMEM;
+
+	cfg->smmu = smmu;
+	dev->archdata.iommu = cfg;
+
+	while (it.cur_count--)
+		cfg->streamids[cfg->num_streamids++] = be32_to_cpup(it.cur++);
+
+	return 0;
 }
 
 static int __arm_smmu_alloc_bitmap(unsigned long *map, int start, int end)
@@ -1119,8 +1064,7 @@ static void arm_smmu_free_smr(struct arm_smmu_device *smmu, int idx)
 static void arm_smmu_write_smr(struct arm_smmu_device *smmu, int idx)
 {
 	struct arm_smmu_smr *smr = smmu->smrs + idx;
-	u32 reg = (smr->id & smmu->streamid_mask) << SMR_ID_SHIFT |
-		  (smr->mask & smmu->smr_mask_mask) << SMR_MASK_SHIFT;
+	u32 reg = smr->id << SMR_ID_SHIFT | smr->mask << SMR_MASK_SHIFT;
 
 	if (smr->valid)
 		reg |= SMR_VALID;
@@ -1189,9 +1133,9 @@ err_free_smrs:
 	return -ENOSPC;
 }
 
-static void arm_smmu_master_free_smes(struct arm_smmu_device *smmu,
-				      struct arm_smmu_master_cfg *cfg)
+static void arm_smmu_master_free_smes(struct arm_smmu_master_cfg *cfg)
 {
+	struct arm_smmu_device *smmu = cfg->smmu;
 	int i;
 
 	/*
@@ -1262,17 +1206,15 @@ 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;
 	}
 
 	/* Ensure that the domain is finalised */
-	ret = arm_smmu_init_domain_context(domain, smmu);
+	ret = arm_smmu_init_domain_context(domain, cfg->smmu);
 	if (ret < 0)
 		return ret;
 
@@ -1280,18 +1222,14 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
 	 * Sanity check the domain. We don't support domains across
 	 * different SMMUs.
 	 */
-	if (smmu_domain->smmu != smmu) {
+	if (smmu_domain->smmu != cfg->smmu) {
 		dev_err(dev,
 			"cannot attach to SMMU %s whilst already attached to domain on SMMU %s\n",
-			dev_name(smmu_domain->smmu->dev), dev_name(smmu->dev));
+			dev_name(smmu_domain->smmu->dev), dev_name(cfg->smmu->dev));
 		return -EINVAL;
 	}
 
 	/* Looks ok, so add the device to the domain */
-	cfg = find_smmu_master_cfg(dev);
-	if (!cfg)
-		return -ENODEV;
-
 	return arm_smmu_domain_add_master(smmu_domain, cfg);
 }
 
@@ -1411,120 +1349,65 @@ static bool arm_smmu_capable(enum iommu_cap cap)
 	}
 }
 
-static int __arm_smmu_get_pci_sid(struct pci_dev *pdev, u16 alias, void *data)
-{
-	*((u16 *)data) = alias;
-	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)
-{
-	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;
-
-	/*
-	 * 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[i] = sid;
-		cfg->smendx[i] = INVALID_SMENDX;
-		cfg->num_streamids++;
-	}
-
-	return 0;
-}
-
-static int arm_smmu_init_platform_device(struct device *dev,
-					 struct iommu_group *group)
-{
-	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);
-
-	return 0;
-}
-
 static int arm_smmu_add_device(struct device *dev)
 {
+	struct arm_smmu_master_cfg *cfg;
 	struct iommu_group *group;
+	int i, ret;
+
+	ret = arm_smmu_register_legacy_master(dev);
+	cfg = dev->archdata.iommu;
+	if (ret)
+		goto out_free;
+
+	ret = -EINVAL;
+	for (i = 0; i < cfg->num_streamids; i++) {
+		u16 sid = cfg->streamids[i];
+
+		if (sid & ~cfg->smmu->streamid_mask) {
+			dev_err(dev, "stream ID 0x%x out of range for SMMU (0x%x)\n",
+				sid, cfg->smmu->streamid_mask);
+			goto out_free;
+		}
+		cfg->smendx[i] = INVALID_SMENDX;
+	}
 
 	group = iommu_group_get_for_dev(dev);
-	if (IS_ERR(group))
-		return PTR_ERR(group);
-
+	if (IS_ERR(group)) {
+		ret = PTR_ERR(group);
+		goto out_free;
+	}
 	iommu_group_put(group);
 	return 0;
+
+out_free:
+	kfree(cfg);
+	dev->archdata.iommu = NULL;
+	return ret;
 }
 
 static void arm_smmu_remove_device(struct device *dev)
 {
-	struct arm_smmu_device *smmu = find_smmu_for_device(dev);
-	struct arm_smmu_master_cfg *cfg = find_smmu_master_cfg(dev);
+	struct arm_smmu_master_cfg *cfg = dev->archdata.iommu;
 
-	if (smmu && cfg)
-		arm_smmu_master_free_smes(smmu, cfg);
+	if (!cfg)
+		return;
 
+	arm_smmu_master_free_smes(cfg);
 	iommu_group_remove_device(dev);
+	kfree(cfg);
+	dev->archdata.iommu = NULL;
 }
 
 static struct iommu_group *arm_smmu_device_group(struct device *dev)
 {
 	struct iommu_group *group;
-	int ret;
 
 	if (dev_is_pci(dev))
 		group = pci_device_group(dev);
 	else
 		group = generic_device_group(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);
-
-	if (ret) {
-		iommu_group_put(group);
-		group = ERR_PTR(ret);
-	}
-
 	return group;
 }
 
@@ -1938,9 +1821,6 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
 	struct resource *res;
 	struct arm_smmu_device *smmu;
 	struct device *dev = &pdev->dev;
-	struct rb_node *node;
-	struct of_phandle_iterator it;
-	struct arm_smmu_phandle_args *masterspec;
 	int num_irqs, i, err;
 
 	smmu = devm_kzalloc(dev, sizeof(*smmu), GFP_KERNEL);
@@ -2001,37 +1881,6 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
 	if (err)
 		return err;
 
-	i = 0;
-	smmu->masters = RB_ROOT;
-
-	err = -ENOMEM;
-	/* No need to zero the memory for masterspec */
-	masterspec = kmalloc(sizeof(*masterspec), GFP_KERNEL);
-	if (!masterspec)
-		goto out_put_masters;
-
-	of_for_each_phandle(&it, err, dev->of_node,
-			    "mmu-masters", "#stream-id-cells", 0) {
-		int count = of_phandle_iterator_args(&it, masterspec->args,
-						     MAX_MASTER_STREAMIDS);
-		masterspec->np		= of_node_get(it.node);
-		masterspec->args_count	= count;
-
-		err = register_smmu_master(smmu, dev, masterspec);
-		if (err) {
-			dev_err(dev, "failed to add master %s\n",
-				masterspec->np->name);
-			kfree(masterspec);
-			goto out_put_masters;
-		}
-
-		i++;
-	}
-
-	dev_notice(dev, "registered %d master devices\n", i);
-
-	kfree(masterspec);
-
 	parse_driver_options(smmu);
 
 	if (smmu->version == ARM_SMMU_V2 &&
@@ -2039,8 +1888,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) {
@@ -2052,7 +1900,7 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
 		if (err) {
 			dev_err(dev, "failed to request global IRQ %d (%u)\n",
 				i, smmu->irqs[i]);
-			goto out_put_masters;
+			return err;
 		}
 	}
 
@@ -2063,22 +1911,12 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
 
 	arm_smmu_device_reset(smmu);
 	return 0;
-
-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;
 }
 
 static int arm_smmu_device_remove(struct platform_device *pdev)
 {
 	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) {
@@ -2093,12 +1931,6 @@ static int arm_smmu_device_remove(struct platform_device *pdev)
 	if (!smmu)
 		return -ENODEV;
 
-	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(dev, "removing device with active domains!\n");
 
-- 
2.8.1.dirty

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

* [PATCH v7 14/22] iommu/arm-smmu: Streamline SMMU data lookups
  2016-09-12 16:13 ` Robin Murphy
@ 2016-09-12 16:13     ` Robin Murphy
  -1 siblings, 0 replies; 104+ messages in thread
From: Robin Murphy @ 2016-09-12 16:13 UTC (permalink / raw)
  To: will.deacon-5wv7dgnIgG8, joro-zLv9SwRftAIdnm+yROfE0A,
	iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, lorenzo.pieralisi-5wv7dgnIgG8,
	jean-philippe.brucker-5wv7dgnIgG8, punit.agrawal-5wv7dgnIgG8,
	thunder.leizhen-hv44wF8Li93QT0dZR+AlfA,
	eric.auger-H+wXaHxf7aLQT0dZR+AlfA

Simplify things somewhat by stashing our arm_smmu_device instance in
drvdata, so that it's readily available to our driver model callbacks.
Then we can excise the private list entirely, since the driver core
already has a perfectly good list of SMMU devices we can use in the one
instance we actually need to. Finally, make a further modest code saving
with the relatively new of_device_get_match_data() helper.

Tested-by: Lorenzo Pieralisi <lorenzo.pieralisi-5wv7dgnIgG8@public.gmane.org>
Signed-off-by: Robin Murphy <robin.murphy-5wv7dgnIgG8@public.gmane.org>
---
 drivers/iommu/arm-smmu.c | 44 +++++++++++---------------------------------
 1 file changed, 11 insertions(+), 33 deletions(-)

diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index 0a628f2c9297..3962add0c33b 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -41,6 +41,7 @@
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/of_address.h>
+#include <linux/of_device.h>
 #include <linux/pci.h>
 #include <linux/platform_device.h>
 #include <linux/slab.h>
@@ -370,8 +371,6 @@ struct arm_smmu_device {
 	u32				num_context_irqs;
 	unsigned int			*irqs;
 
-	struct list_head		list;
-
 	u32				cavium_id_base; /* Specific to Cavium */
 };
 
@@ -409,9 +408,6 @@ struct arm_smmu_domain {
 	struct iommu_domain		domain;
 };
 
-static DEFINE_SPINLOCK(arm_smmu_devices_lock);
-static LIST_HEAD(arm_smmu_devices);
-
 struct arm_smmu_option_prop {
 	u32 opt;
 	const char *prop;
@@ -478,6 +474,8 @@ static int __find_legacy_master_phandle(struct device *dev, void *data)
 	return err;
 }
 
+static struct platform_driver arm_smmu_driver;
+
 static int arm_smmu_register_legacy_master(struct device *dev)
 {
 	struct arm_smmu_device *smmu;
@@ -495,19 +493,16 @@ static int arm_smmu_register_legacy_master(struct device *dev)
 	}
 
 	it.node = np;
-	spin_lock(&arm_smmu_devices_lock);
-	list_for_each_entry(smmu, &arm_smmu_devices, list) {
-		err = __find_legacy_master_phandle(smmu->dev, &data);
-		if (err)
-			break;
-	}
-	spin_unlock(&arm_smmu_devices_lock);
+	err = driver_for_each_device(&arm_smmu_driver.driver, NULL, &data,
+				     __find_legacy_master_phandle);
 	of_node_put(np);
 	if (err == 0)
 		return -ENODEV;
 	if (err < 0)
 		return err;
 
+	smmu = dev_get_drvdata(data);
+
 	if (it.cur_count > MAX_MASTER_STREAMIDS) {
 		dev_err(smmu->dev,
 			"reached maximum number (%d) of stream IDs for master device %s\n",
@@ -1816,7 +1811,6 @@ MODULE_DEVICE_TABLE(of, arm_smmu_of_match);
 
 static int arm_smmu_device_dt_probe(struct platform_device *pdev)
 {
-	const struct of_device_id *of_id;
 	const struct arm_smmu_match_data *data;
 	struct resource *res;
 	struct arm_smmu_device *smmu;
@@ -1830,8 +1824,7 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
 	}
 	smmu->dev = dev;
 
-	of_id = of_match_node(arm_smmu_of_match, dev->of_node);
-	data = of_id->data;
+	data = of_device_get_match_data(dev);
 	smmu->version = data->version;
 	smmu->model = data->model;
 
@@ -1904,35 +1897,20 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
 		}
 	}
 
-	INIT_LIST_HEAD(&smmu->list);
-	spin_lock(&arm_smmu_devices_lock);
-	list_add(&smmu->list, &arm_smmu_devices);
-	spin_unlock(&arm_smmu_devices_lock);
-
+	platform_set_drvdata(pdev, smmu);
 	arm_smmu_device_reset(smmu);
 	return 0;
 }
 
 static int arm_smmu_device_remove(struct platform_device *pdev)
 {
-	struct device *dev = &pdev->dev;
-	struct arm_smmu_device *curr, *smmu = NULL;
-
-	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);
+	struct arm_smmu_device *smmu = platform_get_drvdata(pdev);
 
 	if (!smmu)
 		return -ENODEV;
 
 	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");
 
 	/* Turn the thing off */
 	writel(sCR0_CLIENTPD, ARM_SMMU_GR0_NS(smmu) + ARM_SMMU_GR0_sCR0);
-- 
2.8.1.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] 104+ messages in thread

* [PATCH v7 14/22] iommu/arm-smmu: Streamline SMMU data lookups
@ 2016-09-12 16:13     ` Robin Murphy
  0 siblings, 0 replies; 104+ messages in thread
From: Robin Murphy @ 2016-09-12 16:13 UTC (permalink / raw)
  To: linux-arm-kernel

Simplify things somewhat by stashing our arm_smmu_device instance in
drvdata, so that it's readily available to our driver model callbacks.
Then we can excise the private list entirely, since the driver core
already has a perfectly good list of SMMU devices we can use in the one
instance we actually need to. Finally, make a further modest code saving
with the relatively new of_device_get_match_data() helper.

Tested-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Signed-off-by: Robin Murphy <robin.murphy@arm.com>
---
 drivers/iommu/arm-smmu.c | 44 +++++++++++---------------------------------
 1 file changed, 11 insertions(+), 33 deletions(-)

diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index 0a628f2c9297..3962add0c33b 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -41,6 +41,7 @@
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/of_address.h>
+#include <linux/of_device.h>
 #include <linux/pci.h>
 #include <linux/platform_device.h>
 #include <linux/slab.h>
@@ -370,8 +371,6 @@ struct arm_smmu_device {
 	u32				num_context_irqs;
 	unsigned int			*irqs;
 
-	struct list_head		list;
-
 	u32				cavium_id_base; /* Specific to Cavium */
 };
 
@@ -409,9 +408,6 @@ struct arm_smmu_domain {
 	struct iommu_domain		domain;
 };
 
-static DEFINE_SPINLOCK(arm_smmu_devices_lock);
-static LIST_HEAD(arm_smmu_devices);
-
 struct arm_smmu_option_prop {
 	u32 opt;
 	const char *prop;
@@ -478,6 +474,8 @@ static int __find_legacy_master_phandle(struct device *dev, void *data)
 	return err;
 }
 
+static struct platform_driver arm_smmu_driver;
+
 static int arm_smmu_register_legacy_master(struct device *dev)
 {
 	struct arm_smmu_device *smmu;
@@ -495,19 +493,16 @@ static int arm_smmu_register_legacy_master(struct device *dev)
 	}
 
 	it.node = np;
-	spin_lock(&arm_smmu_devices_lock);
-	list_for_each_entry(smmu, &arm_smmu_devices, list) {
-		err = __find_legacy_master_phandle(smmu->dev, &data);
-		if (err)
-			break;
-	}
-	spin_unlock(&arm_smmu_devices_lock);
+	err = driver_for_each_device(&arm_smmu_driver.driver, NULL, &data,
+				     __find_legacy_master_phandle);
 	of_node_put(np);
 	if (err == 0)
 		return -ENODEV;
 	if (err < 0)
 		return err;
 
+	smmu = dev_get_drvdata(data);
+
 	if (it.cur_count > MAX_MASTER_STREAMIDS) {
 		dev_err(smmu->dev,
 			"reached maximum number (%d) of stream IDs for master device %s\n",
@@ -1816,7 +1811,6 @@ MODULE_DEVICE_TABLE(of, arm_smmu_of_match);
 
 static int arm_smmu_device_dt_probe(struct platform_device *pdev)
 {
-	const struct of_device_id *of_id;
 	const struct arm_smmu_match_data *data;
 	struct resource *res;
 	struct arm_smmu_device *smmu;
@@ -1830,8 +1824,7 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
 	}
 	smmu->dev = dev;
 
-	of_id = of_match_node(arm_smmu_of_match, dev->of_node);
-	data = of_id->data;
+	data = of_device_get_match_data(dev);
 	smmu->version = data->version;
 	smmu->model = data->model;
 
@@ -1904,35 +1897,20 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
 		}
 	}
 
-	INIT_LIST_HEAD(&smmu->list);
-	spin_lock(&arm_smmu_devices_lock);
-	list_add(&smmu->list, &arm_smmu_devices);
-	spin_unlock(&arm_smmu_devices_lock);
-
+	platform_set_drvdata(pdev, smmu);
 	arm_smmu_device_reset(smmu);
 	return 0;
 }
 
 static int arm_smmu_device_remove(struct platform_device *pdev)
 {
-	struct device *dev = &pdev->dev;
-	struct arm_smmu_device *curr, *smmu = NULL;
-
-	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);
+	struct arm_smmu_device *smmu = platform_get_drvdata(pdev);
 
 	if (!smmu)
 		return -ENODEV;
 
 	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");
 
 	/* Turn the thing off */
 	writel(sCR0_CLIENTPD, ARM_SMMU_GR0_NS(smmu) + ARM_SMMU_GR0_sCR0);
-- 
2.8.1.dirty

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

* [PATCH v7 15/22] iommu/arm-smmu: Add a stream map entry iterator
  2016-09-12 16:13 ` Robin Murphy
@ 2016-09-12 16:13     ` Robin Murphy
  -1 siblings, 0 replies; 104+ messages in thread
From: Robin Murphy @ 2016-09-12 16:13 UTC (permalink / raw)
  To: will.deacon-5wv7dgnIgG8, joro-zLv9SwRftAIdnm+yROfE0A,
	iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, lorenzo.pieralisi-5wv7dgnIgG8,
	jean-philippe.brucker-5wv7dgnIgG8, punit.agrawal-5wv7dgnIgG8,
	thunder.leizhen-hv44wF8Li93QT0dZR+AlfA,
	eric.auger-H+wXaHxf7aLQT0dZR+AlfA

We iterate over the SMEs associated with a master config quite a lot in
various places, and are about to do so even more. Let's wrap the idiom
in a handy iterator macro before the repetition gets out of hand.

Tested-by: Lorenzo Pieralisi <lorenzo.pieralisi-5wv7dgnIgG8@public.gmane.org>
Signed-off-by: Robin Murphy <robin.murphy-5wv7dgnIgG8@public.gmane.org>
---
 drivers/iommu/arm-smmu.c | 26 ++++++++++++--------------
 1 file changed, 12 insertions(+), 14 deletions(-)

diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index 3962add0c33b..7a0edef34a22 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -324,6 +324,8 @@ struct arm_smmu_master_cfg {
 	s16				smendx[MAX_MASTER_STREAMIDS];
 };
 #define INVALID_SMENDX			-1
+#define for_each_cfg_sme(cfg, i, idx) \
+	for (i = 0; idx = cfg->smendx[i], i < cfg->num_streamids; ++i)
 
 struct arm_smmu_device {
 	struct device			*dev;
@@ -1090,8 +1092,8 @@ static int arm_smmu_master_alloc_smes(struct arm_smmu_device *smmu,
 	int i, idx;
 
 	/* Allocate the SMRs on the SMMU */
-	for (i = 0; i < cfg->num_streamids; ++i) {
-		if (cfg->smendx[i] != INVALID_SMENDX)
+	for_each_cfg_sme(cfg, i, idx) {
+		if (idx != INVALID_SMENDX)
 			return -EEXIST;
 
 		/* ...except on stream indexing hardware, of course */
@@ -1115,8 +1117,8 @@ static int arm_smmu_master_alloc_smes(struct arm_smmu_device *smmu,
 		return 0;
 
 	/* It worked! Now, poke the actual hardware */
-	for (i = 0; i < cfg->num_streamids; ++i)
-		arm_smmu_write_smr(smmu, cfg->smendx[i]);
+	for_each_cfg_sme(cfg, i, idx)
+		arm_smmu_write_smr(smmu, idx);
 
 	return 0;
 
@@ -1131,15 +1133,13 @@ err_free_smrs:
 static void arm_smmu_master_free_smes(struct arm_smmu_master_cfg *cfg)
 {
 	struct arm_smmu_device *smmu = cfg->smmu;
-	int i;
+	int i, idx;
 
 	/*
 	 * 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) {
-		int idx = cfg->smendx[i];
-
+	for_each_cfg_sme(cfg, i, idx) {
 		/* An IOMMU group is torn down by the first device to be removed */
 		if (idx == INVALID_SMENDX)
 			return;
@@ -1151,9 +1151,9 @@ static void arm_smmu_master_free_smes(struct arm_smmu_master_cfg *cfg)
 	__iowmb();
 
 	/* Invalidate the SMRs before freeing back to the allocator */
-	for (i = 0; i < cfg->num_streamids; ++i) {
+	for_each_cfg_sme(cfg, i, idx) {
 		if (smmu->smrs)
-			arm_smmu_free_smr(smmu, cfg->smendx[i]);
+			arm_smmu_free_smr(smmu, idx);
 
 		cfg->smendx[i] = INVALID_SMENDX;
 	}
@@ -1162,7 +1162,7 @@ static void arm_smmu_master_free_smes(struct arm_smmu_master_cfg *cfg)
 static int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain,
 				      struct arm_smmu_master_cfg *cfg)
 {
-	int i, ret = 0;
+	int i, idx, ret = 0;
 	struct arm_smmu_device *smmu = smmu_domain->smmu;
 	struct arm_smmu_s2cr *s2cr = smmu->s2crs;
 	enum arm_smmu_s2cr_type type = S2CR_TYPE_TRANS;
@@ -1182,9 +1182,7 @@ static int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain,
 	if (smmu_domain->domain.type == IOMMU_DOMAIN_DMA)
 		type = S2CR_TYPE_BYPASS;
 
-	for (i = 0; i < cfg->num_streamids; ++i) {
-		int idx = cfg->smendx[i];
-
+	for_each_cfg_sme(cfg, i, idx) {
 		/* Devices in an IOMMU group may already be configured */
 		if (type == s2cr[idx].type && cbndx == s2cr[idx].cbndx)
 			break;
-- 
2.8.1.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] 104+ messages in thread

* [PATCH v7 15/22] iommu/arm-smmu: Add a stream map entry iterator
@ 2016-09-12 16:13     ` Robin Murphy
  0 siblings, 0 replies; 104+ messages in thread
From: Robin Murphy @ 2016-09-12 16:13 UTC (permalink / raw)
  To: linux-arm-kernel

We iterate over the SMEs associated with a master config quite a lot in
various places, and are about to do so even more. Let's wrap the idiom
in a handy iterator macro before the repetition gets out of hand.

Tested-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Signed-off-by: Robin Murphy <robin.murphy@arm.com>
---
 drivers/iommu/arm-smmu.c | 26 ++++++++++++--------------
 1 file changed, 12 insertions(+), 14 deletions(-)

diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index 3962add0c33b..7a0edef34a22 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -324,6 +324,8 @@ struct arm_smmu_master_cfg {
 	s16				smendx[MAX_MASTER_STREAMIDS];
 };
 #define INVALID_SMENDX			-1
+#define for_each_cfg_sme(cfg, i, idx) \
+	for (i = 0; idx = cfg->smendx[i], i < cfg->num_streamids; ++i)
 
 struct arm_smmu_device {
 	struct device			*dev;
@@ -1090,8 +1092,8 @@ static int arm_smmu_master_alloc_smes(struct arm_smmu_device *smmu,
 	int i, idx;
 
 	/* Allocate the SMRs on the SMMU */
-	for (i = 0; i < cfg->num_streamids; ++i) {
-		if (cfg->smendx[i] != INVALID_SMENDX)
+	for_each_cfg_sme(cfg, i, idx) {
+		if (idx != INVALID_SMENDX)
 			return -EEXIST;
 
 		/* ...except on stream indexing hardware, of course */
@@ -1115,8 +1117,8 @@ static int arm_smmu_master_alloc_smes(struct arm_smmu_device *smmu,
 		return 0;
 
 	/* It worked! Now, poke the actual hardware */
-	for (i = 0; i < cfg->num_streamids; ++i)
-		arm_smmu_write_smr(smmu, cfg->smendx[i]);
+	for_each_cfg_sme(cfg, i, idx)
+		arm_smmu_write_smr(smmu, idx);
 
 	return 0;
 
@@ -1131,15 +1133,13 @@ err_free_smrs:
 static void arm_smmu_master_free_smes(struct arm_smmu_master_cfg *cfg)
 {
 	struct arm_smmu_device *smmu = cfg->smmu;
-	int i;
+	int i, idx;
 
 	/*
 	 * 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) {
-		int idx = cfg->smendx[i];
-
+	for_each_cfg_sme(cfg, i, idx) {
 		/* An IOMMU group is torn down by the first device to be removed */
 		if (idx == INVALID_SMENDX)
 			return;
@@ -1151,9 +1151,9 @@ static void arm_smmu_master_free_smes(struct arm_smmu_master_cfg *cfg)
 	__iowmb();
 
 	/* Invalidate the SMRs before freeing back to the allocator */
-	for (i = 0; i < cfg->num_streamids; ++i) {
+	for_each_cfg_sme(cfg, i, idx) {
 		if (smmu->smrs)
-			arm_smmu_free_smr(smmu, cfg->smendx[i]);
+			arm_smmu_free_smr(smmu, idx);
 
 		cfg->smendx[i] = INVALID_SMENDX;
 	}
@@ -1162,7 +1162,7 @@ static void arm_smmu_master_free_smes(struct arm_smmu_master_cfg *cfg)
 static int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain,
 				      struct arm_smmu_master_cfg *cfg)
 {
-	int i, ret = 0;
+	int i, idx, ret = 0;
 	struct arm_smmu_device *smmu = smmu_domain->smmu;
 	struct arm_smmu_s2cr *s2cr = smmu->s2crs;
 	enum arm_smmu_s2cr_type type = S2CR_TYPE_TRANS;
@@ -1182,9 +1182,7 @@ static int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain,
 	if (smmu_domain->domain.type == IOMMU_DOMAIN_DMA)
 		type = S2CR_TYPE_BYPASS;
 
-	for (i = 0; i < cfg->num_streamids; ++i) {
-		int idx = cfg->smendx[i];
-
+	for_each_cfg_sme(cfg, i, idx) {
 		/* Devices in an IOMMU group may already be configured */
 		if (type == s2cr[idx].type && cbndx == s2cr[idx].cbndx)
 			break;
-- 
2.8.1.dirty

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

* [PATCH v7 16/22] iommu/arm-smmu: Intelligent SMR allocation
  2016-09-12 16:13 ` Robin Murphy
@ 2016-09-12 16:13     ` Robin Murphy
  -1 siblings, 0 replies; 104+ messages in thread
From: Robin Murphy @ 2016-09-12 16:13 UTC (permalink / raw)
  To: will.deacon-5wv7dgnIgG8, joro-zLv9SwRftAIdnm+yROfE0A,
	iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, lorenzo.pieralisi-5wv7dgnIgG8,
	jean-philippe.brucker-5wv7dgnIgG8, punit.agrawal-5wv7dgnIgG8,
	thunder.leizhen-hv44wF8Li93QT0dZR+AlfA,
	eric.auger-H+wXaHxf7aLQT0dZR+AlfA

Stream Match Registers are one of the more awkward parts of the SMMUv2
architecture; there are typically never enough to assign one to each
stream ID in the system, and configuring them such that a single ID
matches multiple entries is catastrophically bad - at best, every
transaction raises a global fault; at worst, they go *somewhere*.

To address the former issue, we can mask ID bits such that a single
register may be used to match multiple IDs belonging to the same device
or group, but doing so also heightens the risk of the latter problem
(which can be nasty to debug).

Tackle both problems at once by replacing the simple bitmap allocator
with something much cleverer. Now that we have convenient in-memory
representations of the stream mapping table, it becomes straightforward
to properly validate new SMR entries against the current state, opening
the door to arbitrary masking and SMR sharing.

Another feature which falls out of this is that with IDs shared by
separate devices being automatically accounted for, simply associating a
group pointer with the S2CR offers appropriate group allocation almost
for free, so hook that up in the process.

Signed-off-by: Robin Murphy <robin.murphy-5wv7dgnIgG8@public.gmane.org>

---

- Simplify SMR matching logic, and document it properly
- Explicify INVALID_SMENDX check
---
 drivers/iommu/arm-smmu.c | 201 ++++++++++++++++++++++++++++-------------------
 1 file changed, 121 insertions(+), 80 deletions(-)

diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index 7a0edef34a22..0e7dbf59335d 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -302,6 +302,8 @@ enum arm_smmu_implementation {
 };
 
 struct arm_smmu_s2cr {
+	struct iommu_group		*group;
+	int				count;
 	enum arm_smmu_s2cr_type		type;
 	enum arm_smmu_s2cr_privcfg	privcfg;
 	u8				cbndx;
@@ -363,6 +365,7 @@ struct arm_smmu_device {
 	u16				smr_mask_mask;
 	struct arm_smmu_smr		*smrs;
 	struct arm_smmu_s2cr		*s2crs;
+	struct mutex			stream_map_mutex;
 
 	unsigned long			va_size;
 	unsigned long			ipa_size;
@@ -1041,23 +1044,6 @@ static void arm_smmu_domain_free(struct iommu_domain *domain)
 	kfree(smmu_domain);
 }
 
-static int arm_smmu_alloc_smr(struct arm_smmu_device *smmu)
-{
-	int i;
-
-	for (i = 0; i < smmu->num_mapping_groups; i++)
-		if (!cmpxchg(&smmu->smrs[i].valid, false, true))
-			return i;
-
-	return INVALID_SMENDX;
-}
-
-static void arm_smmu_free_smr(struct arm_smmu_device *smmu, int idx)
-{
-	writel_relaxed(~SMR_VALID, ARM_SMMU_GR0(smmu) + ARM_SMMU_GR0_SMR(idx));
-	WRITE_ONCE(smmu->smrs[idx].valid, false);
-}
-
 static void arm_smmu_write_smr(struct arm_smmu_device *smmu, int idx)
 {
 	struct arm_smmu_smr *smr = smmu->smrs + idx;
@@ -1085,49 +1071,115 @@ static void arm_smmu_write_sme(struct arm_smmu_device *smmu, int idx)
 		arm_smmu_write_smr(smmu, idx);
 }
 
-static int arm_smmu_master_alloc_smes(struct arm_smmu_device *smmu,
-				      struct arm_smmu_master_cfg *cfg)
+static int arm_smmu_find_sme(struct arm_smmu_device *smmu, u16 id, u16 mask)
 {
 	struct arm_smmu_smr *smrs = smmu->smrs;
-	int i, idx;
+	int i, free_idx = -ENOSPC;
 
-	/* Allocate the SMRs on the SMMU */
-	for_each_cfg_sme(cfg, i, idx) {
-		if (idx != INVALID_SMENDX)
-			return -EEXIST;
+	/* Stream indexing is blissfully easy */
+	if (!smrs)
+		return id;
 
-		/* ...except on stream indexing hardware, of course */
-		if (!smrs) {
-			cfg->smendx[i] = cfg->streamids[i];
+	/* Validating SMRs is... less so */
+	for (i = 0; i < smmu->num_mapping_groups; ++i) {
+		if (!smrs[i].valid) {
+			/*
+			 * Note the first free entry we come across, which
+			 * we'll claim in the end if nothing else matches.
+			 */
+			if (free_idx < 0)
+				free_idx = i;
 			continue;
 		}
-
-		idx = arm_smmu_alloc_smr(smmu);
-		if (idx < 0) {
-			dev_err(smmu->dev, "failed to allocate free SMR\n");
-			goto err_free_smrs;
-		}
-		cfg->smendx[i] = idx;
-
-		smrs[idx].id = cfg->streamids[i];
-		smrs[idx].mask = 0; /* We don't currently share SMRs */
+		/*
+		 * If the new entry is _entirely_ matched by an existing entry,
+		 * then reuse that, with the guarantee that there also cannot
+		 * be any subsequent conflicting entries. In normal use we'd
+		 * expect simply identical entries for this case, but there's
+		 * no harm in accommodating the generalisation.
+		 */
+		if ((mask & smrs[i].mask) == mask &&
+		    !((id ^ smrs[i].id) & ~smrs[i].mask))
+			return i;
+		/*
+		 * If the new entry has any other overlap with an existing one,
+		 * though, then there always exists at least one stream ID
+		 * which would cause a conflict, and we can't allow that risk.
+		 */
+		if (!((id ^ smrs[i].id) & ~(smrs[i].mask | mask)))
+			return -EINVAL;
 	}
 
-	if (!smrs)
-		return 0;
+	return free_idx;
+}
+
+static bool arm_smmu_free_sme(struct arm_smmu_device *smmu, int idx)
+{
+	if (--smmu->s2crs[idx].count)
+		return false;
+
+	smmu->s2crs[idx] = s2cr_init_val;
+	if (smmu->smrs)
+		smmu->smrs[idx].valid = false;
+
+	return true;
+}
+
+static int arm_smmu_master_alloc_smes(struct device *dev)
+{
+	struct arm_smmu_master_cfg *cfg = dev->archdata.iommu;
+	struct arm_smmu_device *smmu = cfg->smmu;
+	struct arm_smmu_smr *smrs = smmu->smrs;
+	struct iommu_group *group;
+	int i, idx, ret;
+
+	mutex_lock(&smmu->stream_map_mutex);
+	/* Figure out a viable stream map entry allocation */
+	for_each_cfg_sme(cfg, i, idx) {
+		if (idx != INVALID_SMENDX) {
+			ret = -EEXIST;
+			goto out_err;
+		}
+
+		ret = arm_smmu_find_sme(smmu, cfg->streamids[i], 0);
+		if (ret < 0)
+			goto out_err;
+
+		idx = ret;
+		if (smrs && smmu->s2crs[idx].count == 0) {
+			smrs[idx].id = cfg->streamids[i];
+			smrs[idx].mask = 0; /* We don't currently share SMRs */
+			smrs[idx].valid = true;
+		}
+		smmu->s2crs[idx].count++;
+		cfg->smendx[i] = (s16)idx;
+	}
+
+	group = iommu_group_get_for_dev(dev);
+	if (!group)
+		group = ERR_PTR(-ENOMEM);
+	if (IS_ERR(group)) {
+		ret = PTR_ERR(group);
+		goto out_err;
+	}
+	iommu_group_put(group);
 
 	/* It worked! Now, poke the actual hardware */
-	for_each_cfg_sme(cfg, i, idx)
-		arm_smmu_write_smr(smmu, idx);
+	for_each_cfg_sme(cfg, i, idx) {
+		arm_smmu_write_sme(smmu, idx);
+		smmu->s2crs[idx].group = group;
+	}
 
+	mutex_unlock(&smmu->stream_map_mutex);
 	return 0;
 
-err_free_smrs:
+out_err:
 	while (i--) {
-		arm_smmu_free_smr(smmu, cfg->smendx[i]);
+		arm_smmu_free_sme(smmu, cfg->smendx[i]);
 		cfg->smendx[i] = INVALID_SMENDX;
 	}
-	return -ENOSPC;
+	mutex_unlock(&smmu->stream_map_mutex);
+	return ret;
 }
 
 static void arm_smmu_master_free_smes(struct arm_smmu_master_cfg *cfg)
@@ -1135,43 +1187,23 @@ static void arm_smmu_master_free_smes(struct arm_smmu_master_cfg *cfg)
 	struct arm_smmu_device *smmu = cfg->smmu;
 	int i, idx;
 
-	/*
-	 * We *must* clear the S2CR first, because freeing the SMR means
-	 * that it can be re-allocated immediately.
-	 */
+	mutex_lock(&smmu->stream_map_mutex);
 	for_each_cfg_sme(cfg, i, idx) {
-		/* An IOMMU group is torn down by the first device to be removed */
-		if (idx == INVALID_SMENDX)
-			return;
-
-		smmu->s2crs[idx] = s2cr_init_val;
-		arm_smmu_write_s2cr(smmu, idx);
-	}
-	/* Sync S2CR updates before touching anything else */
-	__iowmb();
-
-	/* Invalidate the SMRs before freeing back to the allocator */
-	for_each_cfg_sme(cfg, i, idx) {
-		if (smmu->smrs)
-			arm_smmu_free_smr(smmu, idx);
-
+		if (arm_smmu_free_sme(smmu, idx))
+			arm_smmu_write_sme(smmu, idx);
 		cfg->smendx[i] = INVALID_SMENDX;
 	}
+	mutex_unlock(&smmu->stream_map_mutex);
 }
 
 static int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain,
 				      struct arm_smmu_master_cfg *cfg)
 {
-	int i, idx, ret = 0;
 	struct arm_smmu_device *smmu = smmu_domain->smmu;
 	struct arm_smmu_s2cr *s2cr = smmu->s2crs;
 	enum arm_smmu_s2cr_type type = S2CR_TYPE_TRANS;
 	u8 cbndx = smmu_domain->cfg.cbndx;
-
-	if (cfg->smendx[0] == INVALID_SMENDX)
-		ret = arm_smmu_master_alloc_smes(smmu, cfg);
-	if (ret)
-		return ret;
+	int i, idx;
 
 	/*
 	 * FIXME: This won't be needed once we have IOMMU-backed DMA ops
@@ -1183,9 +1215,8 @@ static int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain,
 		type = S2CR_TYPE_BYPASS;
 
 	for_each_cfg_sme(cfg, i, idx) {
-		/* Devices in an IOMMU group may already be configured */
 		if (type == s2cr[idx].type && cbndx == s2cr[idx].cbndx)
-			break;
+			continue;
 
 		s2cr[idx].type = type;
 		s2cr[idx].privcfg = S2CR_PRIVCFG_UNPRIV;
@@ -1345,7 +1376,6 @@ static bool arm_smmu_capable(enum iommu_cap cap)
 static int arm_smmu_add_device(struct device *dev)
 {
 	struct arm_smmu_master_cfg *cfg;
-	struct iommu_group *group;
 	int i, ret;
 
 	ret = arm_smmu_register_legacy_master(dev);
@@ -1365,13 +1395,9 @@ static int arm_smmu_add_device(struct device *dev)
 		cfg->smendx[i] = INVALID_SMENDX;
 	}
 
-	group = iommu_group_get_for_dev(dev);
-	if (IS_ERR(group)) {
-		ret = PTR_ERR(group);
-		goto out_free;
-	}
-	iommu_group_put(group);
-	return 0;
+	ret = arm_smmu_master_alloc_smes(dev);
+	if (!ret)
+		return ret;
 
 out_free:
 	kfree(cfg);
@@ -1394,7 +1420,21 @@ static void arm_smmu_remove_device(struct device *dev)
 
 static struct iommu_group *arm_smmu_device_group(struct device *dev)
 {
-	struct iommu_group *group;
+	struct arm_smmu_master_cfg *cfg = dev->archdata.iommu;
+	struct arm_smmu_device *smmu = cfg->smmu;
+	struct iommu_group *group = NULL;
+	int i, idx;
+
+	for_each_cfg_sme(cfg, i, idx) {
+		if (group && smmu->s2crs[idx].group &&
+		    group != smmu->s2crs[idx].group)
+			return ERR_PTR(-EINVAL);
+
+		group = smmu->s2crs[idx].group;
+	}
+
+	if (group)
+		return group;
 
 	if (dev_is_pci(dev))
 		group = pci_device_group(dev);
@@ -1677,6 +1717,7 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu)
 		smmu->s2crs[i] = s2cr_init_val;
 
 	smmu->num_mapping_groups = size;
+	mutex_init(&smmu->stream_map_mutex);
 
 	if (smmu->version < ARM_SMMU_V2 || !(id & ID0_PTFS_NO_AARCH32)) {
 		smmu->features |= ARM_SMMU_FEAT_FMT_AARCH32_L;
-- 
2.8.1.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] 104+ messages in thread

* [PATCH v7 16/22] iommu/arm-smmu: Intelligent SMR allocation
@ 2016-09-12 16:13     ` Robin Murphy
  0 siblings, 0 replies; 104+ messages in thread
From: Robin Murphy @ 2016-09-12 16:13 UTC (permalink / raw)
  To: linux-arm-kernel

Stream Match Registers are one of the more awkward parts of the SMMUv2
architecture; there are typically never enough to assign one to each
stream ID in the system, and configuring them such that a single ID
matches multiple entries is catastrophically bad - at best, every
transaction raises a global fault; at worst, they go *somewhere*.

To address the former issue, we can mask ID bits such that a single
register may be used to match multiple IDs belonging to the same device
or group, but doing so also heightens the risk of the latter problem
(which can be nasty to debug).

Tackle both problems at once by replacing the simple bitmap allocator
with something much cleverer. Now that we have convenient in-memory
representations of the stream mapping table, it becomes straightforward
to properly validate new SMR entries against the current state, opening
the door to arbitrary masking and SMR sharing.

Another feature which falls out of this is that with IDs shared by
separate devices being automatically accounted for, simply associating a
group pointer with the S2CR offers appropriate group allocation almost
for free, so hook that up in the process.

Signed-off-by: Robin Murphy <robin.murphy@arm.com>

---

- Simplify SMR matching logic, and document it properly
- Explicify INVALID_SMENDX check
---
 drivers/iommu/arm-smmu.c | 201 ++++++++++++++++++++++++++++-------------------
 1 file changed, 121 insertions(+), 80 deletions(-)

diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index 7a0edef34a22..0e7dbf59335d 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -302,6 +302,8 @@ enum arm_smmu_implementation {
 };
 
 struct arm_smmu_s2cr {
+	struct iommu_group		*group;
+	int				count;
 	enum arm_smmu_s2cr_type		type;
 	enum arm_smmu_s2cr_privcfg	privcfg;
 	u8				cbndx;
@@ -363,6 +365,7 @@ struct arm_smmu_device {
 	u16				smr_mask_mask;
 	struct arm_smmu_smr		*smrs;
 	struct arm_smmu_s2cr		*s2crs;
+	struct mutex			stream_map_mutex;
 
 	unsigned long			va_size;
 	unsigned long			ipa_size;
@@ -1041,23 +1044,6 @@ static void arm_smmu_domain_free(struct iommu_domain *domain)
 	kfree(smmu_domain);
 }
 
-static int arm_smmu_alloc_smr(struct arm_smmu_device *smmu)
-{
-	int i;
-
-	for (i = 0; i < smmu->num_mapping_groups; i++)
-		if (!cmpxchg(&smmu->smrs[i].valid, false, true))
-			return i;
-
-	return INVALID_SMENDX;
-}
-
-static void arm_smmu_free_smr(struct arm_smmu_device *smmu, int idx)
-{
-	writel_relaxed(~SMR_VALID, ARM_SMMU_GR0(smmu) + ARM_SMMU_GR0_SMR(idx));
-	WRITE_ONCE(smmu->smrs[idx].valid, false);
-}
-
 static void arm_smmu_write_smr(struct arm_smmu_device *smmu, int idx)
 {
 	struct arm_smmu_smr *smr = smmu->smrs + idx;
@@ -1085,49 +1071,115 @@ static void arm_smmu_write_sme(struct arm_smmu_device *smmu, int idx)
 		arm_smmu_write_smr(smmu, idx);
 }
 
-static int arm_smmu_master_alloc_smes(struct arm_smmu_device *smmu,
-				      struct arm_smmu_master_cfg *cfg)
+static int arm_smmu_find_sme(struct arm_smmu_device *smmu, u16 id, u16 mask)
 {
 	struct arm_smmu_smr *smrs = smmu->smrs;
-	int i, idx;
+	int i, free_idx = -ENOSPC;
 
-	/* Allocate the SMRs on the SMMU */
-	for_each_cfg_sme(cfg, i, idx) {
-		if (idx != INVALID_SMENDX)
-			return -EEXIST;
+	/* Stream indexing is blissfully easy */
+	if (!smrs)
+		return id;
 
-		/* ...except on stream indexing hardware, of course */
-		if (!smrs) {
-			cfg->smendx[i] = cfg->streamids[i];
+	/* Validating SMRs is... less so */
+	for (i = 0; i < smmu->num_mapping_groups; ++i) {
+		if (!smrs[i].valid) {
+			/*
+			 * Note the first free entry we come across, which
+			 * we'll claim in the end if nothing else matches.
+			 */
+			if (free_idx < 0)
+				free_idx = i;
 			continue;
 		}
-
-		idx = arm_smmu_alloc_smr(smmu);
-		if (idx < 0) {
-			dev_err(smmu->dev, "failed to allocate free SMR\n");
-			goto err_free_smrs;
-		}
-		cfg->smendx[i] = idx;
-
-		smrs[idx].id = cfg->streamids[i];
-		smrs[idx].mask = 0; /* We don't currently share SMRs */
+		/*
+		 * If the new entry is _entirely_ matched by an existing entry,
+		 * then reuse that, with the guarantee that there also cannot
+		 * be any subsequent conflicting entries. In normal use we'd
+		 * expect simply identical entries for this case, but there's
+		 * no harm in accommodating the generalisation.
+		 */
+		if ((mask & smrs[i].mask) == mask &&
+		    !((id ^ smrs[i].id) & ~smrs[i].mask))
+			return i;
+		/*
+		 * If the new entry has any other overlap with an existing one,
+		 * though, then there always exists at least one stream ID
+		 * which would cause a conflict, and we can't allow that risk.
+		 */
+		if (!((id ^ smrs[i].id) & ~(smrs[i].mask | mask)))
+			return -EINVAL;
 	}
 
-	if (!smrs)
-		return 0;
+	return free_idx;
+}
+
+static bool arm_smmu_free_sme(struct arm_smmu_device *smmu, int idx)
+{
+	if (--smmu->s2crs[idx].count)
+		return false;
+
+	smmu->s2crs[idx] = s2cr_init_val;
+	if (smmu->smrs)
+		smmu->smrs[idx].valid = false;
+
+	return true;
+}
+
+static int arm_smmu_master_alloc_smes(struct device *dev)
+{
+	struct arm_smmu_master_cfg *cfg = dev->archdata.iommu;
+	struct arm_smmu_device *smmu = cfg->smmu;
+	struct arm_smmu_smr *smrs = smmu->smrs;
+	struct iommu_group *group;
+	int i, idx, ret;
+
+	mutex_lock(&smmu->stream_map_mutex);
+	/* Figure out a viable stream map entry allocation */
+	for_each_cfg_sme(cfg, i, idx) {
+		if (idx != INVALID_SMENDX) {
+			ret = -EEXIST;
+			goto out_err;
+		}
+
+		ret = arm_smmu_find_sme(smmu, cfg->streamids[i], 0);
+		if (ret < 0)
+			goto out_err;
+
+		idx = ret;
+		if (smrs && smmu->s2crs[idx].count == 0) {
+			smrs[idx].id = cfg->streamids[i];
+			smrs[idx].mask = 0; /* We don't currently share SMRs */
+			smrs[idx].valid = true;
+		}
+		smmu->s2crs[idx].count++;
+		cfg->smendx[i] = (s16)idx;
+	}
+
+	group = iommu_group_get_for_dev(dev);
+	if (!group)
+		group = ERR_PTR(-ENOMEM);
+	if (IS_ERR(group)) {
+		ret = PTR_ERR(group);
+		goto out_err;
+	}
+	iommu_group_put(group);
 
 	/* It worked! Now, poke the actual hardware */
-	for_each_cfg_sme(cfg, i, idx)
-		arm_smmu_write_smr(smmu, idx);
+	for_each_cfg_sme(cfg, i, idx) {
+		arm_smmu_write_sme(smmu, idx);
+		smmu->s2crs[idx].group = group;
+	}
 
+	mutex_unlock(&smmu->stream_map_mutex);
 	return 0;
 
-err_free_smrs:
+out_err:
 	while (i--) {
-		arm_smmu_free_smr(smmu, cfg->smendx[i]);
+		arm_smmu_free_sme(smmu, cfg->smendx[i]);
 		cfg->smendx[i] = INVALID_SMENDX;
 	}
-	return -ENOSPC;
+	mutex_unlock(&smmu->stream_map_mutex);
+	return ret;
 }
 
 static void arm_smmu_master_free_smes(struct arm_smmu_master_cfg *cfg)
@@ -1135,43 +1187,23 @@ static void arm_smmu_master_free_smes(struct arm_smmu_master_cfg *cfg)
 	struct arm_smmu_device *smmu = cfg->smmu;
 	int i, idx;
 
-	/*
-	 * We *must* clear the S2CR first, because freeing the SMR means
-	 * that it can be re-allocated immediately.
-	 */
+	mutex_lock(&smmu->stream_map_mutex);
 	for_each_cfg_sme(cfg, i, idx) {
-		/* An IOMMU group is torn down by the first device to be removed */
-		if (idx == INVALID_SMENDX)
-			return;
-
-		smmu->s2crs[idx] = s2cr_init_val;
-		arm_smmu_write_s2cr(smmu, idx);
-	}
-	/* Sync S2CR updates before touching anything else */
-	__iowmb();
-
-	/* Invalidate the SMRs before freeing back to the allocator */
-	for_each_cfg_sme(cfg, i, idx) {
-		if (smmu->smrs)
-			arm_smmu_free_smr(smmu, idx);
-
+		if (arm_smmu_free_sme(smmu, idx))
+			arm_smmu_write_sme(smmu, idx);
 		cfg->smendx[i] = INVALID_SMENDX;
 	}
+	mutex_unlock(&smmu->stream_map_mutex);
 }
 
 static int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain,
 				      struct arm_smmu_master_cfg *cfg)
 {
-	int i, idx, ret = 0;
 	struct arm_smmu_device *smmu = smmu_domain->smmu;
 	struct arm_smmu_s2cr *s2cr = smmu->s2crs;
 	enum arm_smmu_s2cr_type type = S2CR_TYPE_TRANS;
 	u8 cbndx = smmu_domain->cfg.cbndx;
-
-	if (cfg->smendx[0] == INVALID_SMENDX)
-		ret = arm_smmu_master_alloc_smes(smmu, cfg);
-	if (ret)
-		return ret;
+	int i, idx;
 
 	/*
 	 * FIXME: This won't be needed once we have IOMMU-backed DMA ops
@@ -1183,9 +1215,8 @@ static int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain,
 		type = S2CR_TYPE_BYPASS;
 
 	for_each_cfg_sme(cfg, i, idx) {
-		/* Devices in an IOMMU group may already be configured */
 		if (type == s2cr[idx].type && cbndx == s2cr[idx].cbndx)
-			break;
+			continue;
 
 		s2cr[idx].type = type;
 		s2cr[idx].privcfg = S2CR_PRIVCFG_UNPRIV;
@@ -1345,7 +1376,6 @@ static bool arm_smmu_capable(enum iommu_cap cap)
 static int arm_smmu_add_device(struct device *dev)
 {
 	struct arm_smmu_master_cfg *cfg;
-	struct iommu_group *group;
 	int i, ret;
 
 	ret = arm_smmu_register_legacy_master(dev);
@@ -1365,13 +1395,9 @@ static int arm_smmu_add_device(struct device *dev)
 		cfg->smendx[i] = INVALID_SMENDX;
 	}
 
-	group = iommu_group_get_for_dev(dev);
-	if (IS_ERR(group)) {
-		ret = PTR_ERR(group);
-		goto out_free;
-	}
-	iommu_group_put(group);
-	return 0;
+	ret = arm_smmu_master_alloc_smes(dev);
+	if (!ret)
+		return ret;
 
 out_free:
 	kfree(cfg);
@@ -1394,7 +1420,21 @@ static void arm_smmu_remove_device(struct device *dev)
 
 static struct iommu_group *arm_smmu_device_group(struct device *dev)
 {
-	struct iommu_group *group;
+	struct arm_smmu_master_cfg *cfg = dev->archdata.iommu;
+	struct arm_smmu_device *smmu = cfg->smmu;
+	struct iommu_group *group = NULL;
+	int i, idx;
+
+	for_each_cfg_sme(cfg, i, idx) {
+		if (group && smmu->s2crs[idx].group &&
+		    group != smmu->s2crs[idx].group)
+			return ERR_PTR(-EINVAL);
+
+		group = smmu->s2crs[idx].group;
+	}
+
+	if (group)
+		return group;
 
 	if (dev_is_pci(dev))
 		group = pci_device_group(dev);
@@ -1677,6 +1717,7 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu)
 		smmu->s2crs[i] = s2cr_init_val;
 
 	smmu->num_mapping_groups = size;
+	mutex_init(&smmu->stream_map_mutex);
 
 	if (smmu->version < ARM_SMMU_V2 || !(id & ID0_PTFS_NO_AARCH32)) {
 		smmu->features |= ARM_SMMU_FEAT_FMT_AARCH32_L;
-- 
2.8.1.dirty

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

* [PATCH v7 17/22] iommu/arm-smmu: Convert to iommu_fwspec
  2016-09-12 16:13 ` Robin Murphy
@ 2016-09-12 16:13     ` Robin Murphy
  -1 siblings, 0 replies; 104+ messages in thread
From: Robin Murphy @ 2016-09-12 16:13 UTC (permalink / raw)
  To: will.deacon-5wv7dgnIgG8, joro-zLv9SwRftAIdnm+yROfE0A,
	iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, lorenzo.pieralisi-5wv7dgnIgG8,
	jean-philippe.brucker-5wv7dgnIgG8, punit.agrawal-5wv7dgnIgG8,
	thunder.leizhen-hv44wF8Li93QT0dZR+AlfA,
	eric.auger-H+wXaHxf7aLQT0dZR+AlfA

In the final step of preparation for full generic configuration support,
swap our fixed-size master_cfg for the generic iommu_fwspec. For the
legacy DT bindings, the driver simply gets to act as its own 'firmware'.
Farewell, arbitrary MAX_MASTER_STREAMIDS!

Signed-off-by: Robin Murphy <robin.murphy-5wv7dgnIgG8@public.gmane.org>

---

- Convert to updated fwspec mechanism
---
 drivers/iommu/arm-smmu.c | 140 ++++++++++++++++++++++++++---------------------
 1 file changed, 78 insertions(+), 62 deletions(-)

diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index 0e7dbf59335d..c5ee5a51c7ec 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -42,6 +42,7 @@
 #include <linux/of.h>
 #include <linux/of_address.h>
 #include <linux/of_device.h>
+#include <linux/of_iommu.h>
 #include <linux/pci.h>
 #include <linux/platform_device.h>
 #include <linux/slab.h>
@@ -51,9 +52,6 @@
 
 #include "io-pgtable.h"
 
-/* Maximum number of stream IDs assigned to a single device */
-#define MAX_MASTER_STREAMIDS		128
-
 /* Maximum number of context banks per SMMU */
 #define ARM_SMMU_MAX_CBS		128
 
@@ -321,13 +319,13 @@ struct arm_smmu_smr {
 
 struct arm_smmu_master_cfg {
 	struct arm_smmu_device		*smmu;
-	int				num_streamids;
-	u16				streamids[MAX_MASTER_STREAMIDS];
-	s16				smendx[MAX_MASTER_STREAMIDS];
+	s16				smendx[];
 };
 #define INVALID_SMENDX			-1
-#define for_each_cfg_sme(cfg, i, idx) \
-	for (i = 0; idx = cfg->smendx[i], i < cfg->num_streamids; ++i)
+#define __fwspec_cfg(fw) ((struct arm_smmu_master_cfg *)fw->iommu_priv)
+#define fwspec_smmu(fw)  (__fwspec_cfg(fw)->smmu)
+#define for_each_cfg_sme(fw, i, idx) \
+	for (i = 0; idx = __fwspec_cfg(fw)->smendx[i], i < fw->num_ids; ++i)
 
 struct arm_smmu_device {
 	struct device			*dev;
@@ -480,14 +478,16 @@ static int __find_legacy_master_phandle(struct device *dev, void *data)
 }
 
 static struct platform_driver arm_smmu_driver;
+static struct iommu_ops arm_smmu_ops;
 
-static int arm_smmu_register_legacy_master(struct device *dev)
+static int arm_smmu_register_legacy_master(struct device *dev,
+					   struct arm_smmu_device **smmu)
 {
-	struct arm_smmu_device *smmu;
-	struct arm_smmu_master_cfg *cfg;
+	struct device *smmu_dev;
 	struct device_node *np;
 	struct of_phandle_iterator it;
 	void *data = &it;
+	u32 *sids;
 	__be32 pci_sid;
 	int err;
 
@@ -500,20 +500,13 @@ static int arm_smmu_register_legacy_master(struct device *dev)
 	it.node = np;
 	err = driver_for_each_device(&arm_smmu_driver.driver, NULL, &data,
 				     __find_legacy_master_phandle);
+	smmu_dev = data;
 	of_node_put(np);
 	if (err == 0)
 		return -ENODEV;
 	if (err < 0)
 		return err;
 
-	smmu = dev_get_drvdata(data);
-
-	if (it.cur_count > MAX_MASTER_STREAMIDS) {
-		dev_err(smmu->dev,
-			"reached maximum number (%d) of stream IDs for master device %s\n",
-			MAX_MASTER_STREAMIDS, dev_name(dev));
-		return -ENOSPC;
-	}
 	if (dev_is_pci(dev)) {
 		/* "mmu-masters" assumes Stream ID == Requester ID */
 		pci_for_each_dma_alias(to_pci_dev(dev), __arm_smmu_get_pci_sid,
@@ -522,17 +515,20 @@ static int arm_smmu_register_legacy_master(struct device *dev)
 		it.cur_count = 1;
 	}
 
-	cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
-	if (!cfg)
+	err = iommu_fwspec_init(dev, &smmu_dev->of_node->fwnode,
+				&arm_smmu_ops);
+	if (err)
+		return err;
+
+	sids = kcalloc(it.cur_count, sizeof(*sids), GFP_KERNEL);
+	if (!sids)
 		return -ENOMEM;
 
-	cfg->smmu = smmu;
-	dev->archdata.iommu = cfg;
-
-	while (it.cur_count--)
-		cfg->streamids[cfg->num_streamids++] = be32_to_cpup(it.cur++);
-
-	return 0;
+	*smmu = dev_get_drvdata(smmu_dev);
+	of_phandle_iterator_args(&it, sids, it.cur_count);
+	err = iommu_fwspec_add_ids(dev, sids, it.cur_count);
+	kfree(sids);
+	return err;
 }
 
 static int __arm_smmu_alloc_bitmap(unsigned long *map, int start, int end)
@@ -1127,7 +1123,8 @@ static bool arm_smmu_free_sme(struct arm_smmu_device *smmu, int idx)
 
 static int arm_smmu_master_alloc_smes(struct device *dev)
 {
-	struct arm_smmu_master_cfg *cfg = dev->archdata.iommu;
+	struct iommu_fwspec *fwspec = dev->iommu_fwspec;
+	struct arm_smmu_master_cfg *cfg = fwspec->iommu_priv;
 	struct arm_smmu_device *smmu = cfg->smmu;
 	struct arm_smmu_smr *smrs = smmu->smrs;
 	struct iommu_group *group;
@@ -1135,19 +1132,19 @@ static int arm_smmu_master_alloc_smes(struct device *dev)
 
 	mutex_lock(&smmu->stream_map_mutex);
 	/* Figure out a viable stream map entry allocation */
-	for_each_cfg_sme(cfg, i, idx) {
+	for_each_cfg_sme(fwspec, i, idx) {
 		if (idx != INVALID_SMENDX) {
 			ret = -EEXIST;
 			goto out_err;
 		}
 
-		ret = arm_smmu_find_sme(smmu, cfg->streamids[i], 0);
+		ret = arm_smmu_find_sme(smmu, fwspec->ids[i], 0);
 		if (ret < 0)
 			goto out_err;
 
 		idx = ret;
 		if (smrs && smmu->s2crs[idx].count == 0) {
-			smrs[idx].id = cfg->streamids[i];
+			smrs[idx].id = fwspec->ids[i];
 			smrs[idx].mask = 0; /* We don't currently share SMRs */
 			smrs[idx].valid = true;
 		}
@@ -1165,7 +1162,7 @@ static int arm_smmu_master_alloc_smes(struct device *dev)
 	iommu_group_put(group);
 
 	/* It worked! Now, poke the actual hardware */
-	for_each_cfg_sme(cfg, i, idx) {
+	for_each_cfg_sme(fwspec, i, idx) {
 		arm_smmu_write_sme(smmu, idx);
 		smmu->s2crs[idx].group = group;
 	}
@@ -1182,13 +1179,14 @@ out_err:
 	return ret;
 }
 
-static void arm_smmu_master_free_smes(struct arm_smmu_master_cfg *cfg)
+static void arm_smmu_master_free_smes(struct iommu_fwspec *fwspec)
 {
-	struct arm_smmu_device *smmu = cfg->smmu;
+	struct arm_smmu_device *smmu = fwspec_smmu(fwspec);
+	struct arm_smmu_master_cfg *cfg = fwspec->iommu_priv;
 	int i, idx;
 
 	mutex_lock(&smmu->stream_map_mutex);
-	for_each_cfg_sme(cfg, i, idx) {
+	for_each_cfg_sme(fwspec, i, idx) {
 		if (arm_smmu_free_sme(smmu, idx))
 			arm_smmu_write_sme(smmu, idx);
 		cfg->smendx[i] = INVALID_SMENDX;
@@ -1197,7 +1195,7 @@ static void arm_smmu_master_free_smes(struct arm_smmu_master_cfg *cfg)
 }
 
 static int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain,
-				      struct arm_smmu_master_cfg *cfg)
+				      struct iommu_fwspec *fwspec)
 {
 	struct arm_smmu_device *smmu = smmu_domain->smmu;
 	struct arm_smmu_s2cr *s2cr = smmu->s2crs;
@@ -1214,7 +1212,7 @@ static int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain,
 	if (smmu_domain->domain.type == IOMMU_DOMAIN_DMA)
 		type = S2CR_TYPE_BYPASS;
 
-	for_each_cfg_sme(cfg, i, idx) {
+	for_each_cfg_sme(fwspec, i, idx) {
 		if (type == s2cr[idx].type && cbndx == s2cr[idx].cbndx)
 			continue;
 
@@ -1229,16 +1227,18 @@ static int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain,
 static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
 {
 	int ret;
+	struct iommu_fwspec *fwspec = dev->iommu_fwspec;
+	struct arm_smmu_device *smmu;
 	struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
-	struct arm_smmu_master_cfg *cfg = dev->archdata.iommu;
 
-	if (!cfg) {
+	if (!fwspec || fwspec->ops != &arm_smmu_ops) {
 		dev_err(dev, "cannot attach to SMMU, is it on the same bus?\n");
 		return -ENXIO;
 	}
 
+	smmu = fwspec_smmu(fwspec);
 	/* Ensure that the domain is finalised */
-	ret = arm_smmu_init_domain_context(domain, cfg->smmu);
+	ret = arm_smmu_init_domain_context(domain, smmu);
 	if (ret < 0)
 		return ret;
 
@@ -1246,15 +1246,15 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
 	 * Sanity check the domain. We don't support domains across
 	 * different SMMUs.
 	 */
-	if (smmu_domain->smmu != cfg->smmu) {
+	if (smmu_domain->smmu != smmu) {
 		dev_err(dev,
 			"cannot attach to SMMU %s whilst already attached to domain on SMMU %s\n",
-			dev_name(smmu_domain->smmu->dev), dev_name(cfg->smmu->dev));
+			dev_name(smmu_domain->smmu->dev), dev_name(smmu->dev));
 		return -EINVAL;
 	}
 
 	/* Looks ok, so add the device to the domain */
-	return arm_smmu_domain_add_master(smmu_domain, cfg);
+	return arm_smmu_domain_add_master(smmu_domain, fwspec);
 }
 
 static int arm_smmu_map(struct iommu_domain *domain, unsigned long iova,
@@ -1375,57 +1375,72 @@ static bool arm_smmu_capable(enum iommu_cap cap)
 
 static int arm_smmu_add_device(struct device *dev)
 {
+	struct arm_smmu_device *smmu;
 	struct arm_smmu_master_cfg *cfg;
+	struct iommu_fwspec *fwspec;
 	int i, ret;
 
-	ret = arm_smmu_register_legacy_master(dev);
-	cfg = dev->archdata.iommu;
+	ret = arm_smmu_register_legacy_master(dev, &smmu);
+	fwspec = dev->iommu_fwspec;
 	if (ret)
 		goto out_free;
 
 	ret = -EINVAL;
-	for (i = 0; i < cfg->num_streamids; i++) {
-		u16 sid = cfg->streamids[i];
+	for (i = 0; i < fwspec->num_ids; i++) {
+		u16 sid = fwspec->ids[i];
 
-		if (sid & ~cfg->smmu->streamid_mask) {
+		if (sid & ~smmu->streamid_mask) {
 			dev_err(dev, "stream ID 0x%x out of range for SMMU (0x%x)\n",
 				sid, cfg->smmu->streamid_mask);
 			goto out_free;
 		}
-		cfg->smendx[i] = INVALID_SMENDX;
 	}
 
+	ret = -ENOMEM;
+	cfg = kzalloc(offsetof(struct arm_smmu_master_cfg, smendx[i]),
+		      GFP_KERNEL);
+	if (!cfg)
+		goto out_free;
+
+	cfg->smmu = smmu;
+	fwspec->iommu_priv = cfg;
+	while (i--)
+		cfg->smendx[i] = INVALID_SMENDX;
+
 	ret = arm_smmu_master_alloc_smes(dev);
-	if (!ret)
-		return ret;
+	if (ret)
+		goto out_free;
+
+	return 0;
 
 out_free:
-	kfree(cfg);
-	dev->archdata.iommu = NULL;
+	if (fwspec)
+		kfree(fwspec->iommu_priv);
+	iommu_fwspec_free(dev);
 	return ret;
 }
 
 static void arm_smmu_remove_device(struct device *dev)
 {
-	struct arm_smmu_master_cfg *cfg = dev->archdata.iommu;
+	struct iommu_fwspec *fwspec = dev->iommu_fwspec;
 
-	if (!cfg)
+	if (!fwspec || fwspec->ops != &arm_smmu_ops)
 		return;
 
-	arm_smmu_master_free_smes(cfg);
+	arm_smmu_master_free_smes(fwspec);
 	iommu_group_remove_device(dev);
-	kfree(cfg);
-	dev->archdata.iommu = NULL;
+	kfree(fwspec->iommu_priv);
+	iommu_fwspec_free(dev);
 }
 
 static struct iommu_group *arm_smmu_device_group(struct device *dev)
 {
-	struct arm_smmu_master_cfg *cfg = dev->archdata.iommu;
-	struct arm_smmu_device *smmu = cfg->smmu;
+	struct iommu_fwspec *fwspec = dev->iommu_fwspec;
+	struct arm_smmu_device *smmu = fwspec_smmu(fwspec);
 	struct iommu_group *group = NULL;
 	int i, idx;
 
-	for_each_cfg_sme(cfg, i, idx) {
+	for_each_cfg_sme(fwspec, i, idx) {
 		if (group && smmu->s2crs[idx].group &&
 		    group != smmu->s2crs[idx].group)
 			return ERR_PTR(-EINVAL);
@@ -1936,6 +1951,7 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
 		}
 	}
 
+	of_iommu_set_ops(dev->of_node, &arm_smmu_ops);
 	platform_set_drvdata(pdev, smmu);
 	arm_smmu_device_reset(smmu);
 	return 0;
-- 
2.8.1.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] 104+ messages in thread

* [PATCH v7 17/22] iommu/arm-smmu: Convert to iommu_fwspec
@ 2016-09-12 16:13     ` Robin Murphy
  0 siblings, 0 replies; 104+ messages in thread
From: Robin Murphy @ 2016-09-12 16:13 UTC (permalink / raw)
  To: linux-arm-kernel

In the final step of preparation for full generic configuration support,
swap our fixed-size master_cfg for the generic iommu_fwspec. For the
legacy DT bindings, the driver simply gets to act as its own 'firmware'.
Farewell, arbitrary MAX_MASTER_STREAMIDS!

Signed-off-by: Robin Murphy <robin.murphy@arm.com>

---

- Convert to updated fwspec mechanism
---
 drivers/iommu/arm-smmu.c | 140 ++++++++++++++++++++++++++---------------------
 1 file changed, 78 insertions(+), 62 deletions(-)

diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index 0e7dbf59335d..c5ee5a51c7ec 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -42,6 +42,7 @@
 #include <linux/of.h>
 #include <linux/of_address.h>
 #include <linux/of_device.h>
+#include <linux/of_iommu.h>
 #include <linux/pci.h>
 #include <linux/platform_device.h>
 #include <linux/slab.h>
@@ -51,9 +52,6 @@
 
 #include "io-pgtable.h"
 
-/* Maximum number of stream IDs assigned to a single device */
-#define MAX_MASTER_STREAMIDS		128
-
 /* Maximum number of context banks per SMMU */
 #define ARM_SMMU_MAX_CBS		128
 
@@ -321,13 +319,13 @@ struct arm_smmu_smr {
 
 struct arm_smmu_master_cfg {
 	struct arm_smmu_device		*smmu;
-	int				num_streamids;
-	u16				streamids[MAX_MASTER_STREAMIDS];
-	s16				smendx[MAX_MASTER_STREAMIDS];
+	s16				smendx[];
 };
 #define INVALID_SMENDX			-1
-#define for_each_cfg_sme(cfg, i, idx) \
-	for (i = 0; idx = cfg->smendx[i], i < cfg->num_streamids; ++i)
+#define __fwspec_cfg(fw) ((struct arm_smmu_master_cfg *)fw->iommu_priv)
+#define fwspec_smmu(fw)  (__fwspec_cfg(fw)->smmu)
+#define for_each_cfg_sme(fw, i, idx) \
+	for (i = 0; idx = __fwspec_cfg(fw)->smendx[i], i < fw->num_ids; ++i)
 
 struct arm_smmu_device {
 	struct device			*dev;
@@ -480,14 +478,16 @@ static int __find_legacy_master_phandle(struct device *dev, void *data)
 }
 
 static struct platform_driver arm_smmu_driver;
+static struct iommu_ops arm_smmu_ops;
 
-static int arm_smmu_register_legacy_master(struct device *dev)
+static int arm_smmu_register_legacy_master(struct device *dev,
+					   struct arm_smmu_device **smmu)
 {
-	struct arm_smmu_device *smmu;
-	struct arm_smmu_master_cfg *cfg;
+	struct device *smmu_dev;
 	struct device_node *np;
 	struct of_phandle_iterator it;
 	void *data = &it;
+	u32 *sids;
 	__be32 pci_sid;
 	int err;
 
@@ -500,20 +500,13 @@ static int arm_smmu_register_legacy_master(struct device *dev)
 	it.node = np;
 	err = driver_for_each_device(&arm_smmu_driver.driver, NULL, &data,
 				     __find_legacy_master_phandle);
+	smmu_dev = data;
 	of_node_put(np);
 	if (err == 0)
 		return -ENODEV;
 	if (err < 0)
 		return err;
 
-	smmu = dev_get_drvdata(data);
-
-	if (it.cur_count > MAX_MASTER_STREAMIDS) {
-		dev_err(smmu->dev,
-			"reached maximum number (%d) of stream IDs for master device %s\n",
-			MAX_MASTER_STREAMIDS, dev_name(dev));
-		return -ENOSPC;
-	}
 	if (dev_is_pci(dev)) {
 		/* "mmu-masters" assumes Stream ID == Requester ID */
 		pci_for_each_dma_alias(to_pci_dev(dev), __arm_smmu_get_pci_sid,
@@ -522,17 +515,20 @@ static int arm_smmu_register_legacy_master(struct device *dev)
 		it.cur_count = 1;
 	}
 
-	cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
-	if (!cfg)
+	err = iommu_fwspec_init(dev, &smmu_dev->of_node->fwnode,
+				&arm_smmu_ops);
+	if (err)
+		return err;
+
+	sids = kcalloc(it.cur_count, sizeof(*sids), GFP_KERNEL);
+	if (!sids)
 		return -ENOMEM;
 
-	cfg->smmu = smmu;
-	dev->archdata.iommu = cfg;
-
-	while (it.cur_count--)
-		cfg->streamids[cfg->num_streamids++] = be32_to_cpup(it.cur++);
-
-	return 0;
+	*smmu = dev_get_drvdata(smmu_dev);
+	of_phandle_iterator_args(&it, sids, it.cur_count);
+	err = iommu_fwspec_add_ids(dev, sids, it.cur_count);
+	kfree(sids);
+	return err;
 }
 
 static int __arm_smmu_alloc_bitmap(unsigned long *map, int start, int end)
@@ -1127,7 +1123,8 @@ static bool arm_smmu_free_sme(struct arm_smmu_device *smmu, int idx)
 
 static int arm_smmu_master_alloc_smes(struct device *dev)
 {
-	struct arm_smmu_master_cfg *cfg = dev->archdata.iommu;
+	struct iommu_fwspec *fwspec = dev->iommu_fwspec;
+	struct arm_smmu_master_cfg *cfg = fwspec->iommu_priv;
 	struct arm_smmu_device *smmu = cfg->smmu;
 	struct arm_smmu_smr *smrs = smmu->smrs;
 	struct iommu_group *group;
@@ -1135,19 +1132,19 @@ static int arm_smmu_master_alloc_smes(struct device *dev)
 
 	mutex_lock(&smmu->stream_map_mutex);
 	/* Figure out a viable stream map entry allocation */
-	for_each_cfg_sme(cfg, i, idx) {
+	for_each_cfg_sme(fwspec, i, idx) {
 		if (idx != INVALID_SMENDX) {
 			ret = -EEXIST;
 			goto out_err;
 		}
 
-		ret = arm_smmu_find_sme(smmu, cfg->streamids[i], 0);
+		ret = arm_smmu_find_sme(smmu, fwspec->ids[i], 0);
 		if (ret < 0)
 			goto out_err;
 
 		idx = ret;
 		if (smrs && smmu->s2crs[idx].count == 0) {
-			smrs[idx].id = cfg->streamids[i];
+			smrs[idx].id = fwspec->ids[i];
 			smrs[idx].mask = 0; /* We don't currently share SMRs */
 			smrs[idx].valid = true;
 		}
@@ -1165,7 +1162,7 @@ static int arm_smmu_master_alloc_smes(struct device *dev)
 	iommu_group_put(group);
 
 	/* It worked! Now, poke the actual hardware */
-	for_each_cfg_sme(cfg, i, idx) {
+	for_each_cfg_sme(fwspec, i, idx) {
 		arm_smmu_write_sme(smmu, idx);
 		smmu->s2crs[idx].group = group;
 	}
@@ -1182,13 +1179,14 @@ out_err:
 	return ret;
 }
 
-static void arm_smmu_master_free_smes(struct arm_smmu_master_cfg *cfg)
+static void arm_smmu_master_free_smes(struct iommu_fwspec *fwspec)
 {
-	struct arm_smmu_device *smmu = cfg->smmu;
+	struct arm_smmu_device *smmu = fwspec_smmu(fwspec);
+	struct arm_smmu_master_cfg *cfg = fwspec->iommu_priv;
 	int i, idx;
 
 	mutex_lock(&smmu->stream_map_mutex);
-	for_each_cfg_sme(cfg, i, idx) {
+	for_each_cfg_sme(fwspec, i, idx) {
 		if (arm_smmu_free_sme(smmu, idx))
 			arm_smmu_write_sme(smmu, idx);
 		cfg->smendx[i] = INVALID_SMENDX;
@@ -1197,7 +1195,7 @@ static void arm_smmu_master_free_smes(struct arm_smmu_master_cfg *cfg)
 }
 
 static int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain,
-				      struct arm_smmu_master_cfg *cfg)
+				      struct iommu_fwspec *fwspec)
 {
 	struct arm_smmu_device *smmu = smmu_domain->smmu;
 	struct arm_smmu_s2cr *s2cr = smmu->s2crs;
@@ -1214,7 +1212,7 @@ static int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain,
 	if (smmu_domain->domain.type == IOMMU_DOMAIN_DMA)
 		type = S2CR_TYPE_BYPASS;
 
-	for_each_cfg_sme(cfg, i, idx) {
+	for_each_cfg_sme(fwspec, i, idx) {
 		if (type == s2cr[idx].type && cbndx == s2cr[idx].cbndx)
 			continue;
 
@@ -1229,16 +1227,18 @@ static int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain,
 static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
 {
 	int ret;
+	struct iommu_fwspec *fwspec = dev->iommu_fwspec;
+	struct arm_smmu_device *smmu;
 	struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
-	struct arm_smmu_master_cfg *cfg = dev->archdata.iommu;
 
-	if (!cfg) {
+	if (!fwspec || fwspec->ops != &arm_smmu_ops) {
 		dev_err(dev, "cannot attach to SMMU, is it on the same bus?\n");
 		return -ENXIO;
 	}
 
+	smmu = fwspec_smmu(fwspec);
 	/* Ensure that the domain is finalised */
-	ret = arm_smmu_init_domain_context(domain, cfg->smmu);
+	ret = arm_smmu_init_domain_context(domain, smmu);
 	if (ret < 0)
 		return ret;
 
@@ -1246,15 +1246,15 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
 	 * Sanity check the domain. We don't support domains across
 	 * different SMMUs.
 	 */
-	if (smmu_domain->smmu != cfg->smmu) {
+	if (smmu_domain->smmu != smmu) {
 		dev_err(dev,
 			"cannot attach to SMMU %s whilst already attached to domain on SMMU %s\n",
-			dev_name(smmu_domain->smmu->dev), dev_name(cfg->smmu->dev));
+			dev_name(smmu_domain->smmu->dev), dev_name(smmu->dev));
 		return -EINVAL;
 	}
 
 	/* Looks ok, so add the device to the domain */
-	return arm_smmu_domain_add_master(smmu_domain, cfg);
+	return arm_smmu_domain_add_master(smmu_domain, fwspec);
 }
 
 static int arm_smmu_map(struct iommu_domain *domain, unsigned long iova,
@@ -1375,57 +1375,72 @@ static bool arm_smmu_capable(enum iommu_cap cap)
 
 static int arm_smmu_add_device(struct device *dev)
 {
+	struct arm_smmu_device *smmu;
 	struct arm_smmu_master_cfg *cfg;
+	struct iommu_fwspec *fwspec;
 	int i, ret;
 
-	ret = arm_smmu_register_legacy_master(dev);
-	cfg = dev->archdata.iommu;
+	ret = arm_smmu_register_legacy_master(dev, &smmu);
+	fwspec = dev->iommu_fwspec;
 	if (ret)
 		goto out_free;
 
 	ret = -EINVAL;
-	for (i = 0; i < cfg->num_streamids; i++) {
-		u16 sid = cfg->streamids[i];
+	for (i = 0; i < fwspec->num_ids; i++) {
+		u16 sid = fwspec->ids[i];
 
-		if (sid & ~cfg->smmu->streamid_mask) {
+		if (sid & ~smmu->streamid_mask) {
 			dev_err(dev, "stream ID 0x%x out of range for SMMU (0x%x)\n",
 				sid, cfg->smmu->streamid_mask);
 			goto out_free;
 		}
-		cfg->smendx[i] = INVALID_SMENDX;
 	}
 
+	ret = -ENOMEM;
+	cfg = kzalloc(offsetof(struct arm_smmu_master_cfg, smendx[i]),
+		      GFP_KERNEL);
+	if (!cfg)
+		goto out_free;
+
+	cfg->smmu = smmu;
+	fwspec->iommu_priv = cfg;
+	while (i--)
+		cfg->smendx[i] = INVALID_SMENDX;
+
 	ret = arm_smmu_master_alloc_smes(dev);
-	if (!ret)
-		return ret;
+	if (ret)
+		goto out_free;
+
+	return 0;
 
 out_free:
-	kfree(cfg);
-	dev->archdata.iommu = NULL;
+	if (fwspec)
+		kfree(fwspec->iommu_priv);
+	iommu_fwspec_free(dev);
 	return ret;
 }
 
 static void arm_smmu_remove_device(struct device *dev)
 {
-	struct arm_smmu_master_cfg *cfg = dev->archdata.iommu;
+	struct iommu_fwspec *fwspec = dev->iommu_fwspec;
 
-	if (!cfg)
+	if (!fwspec || fwspec->ops != &arm_smmu_ops)
 		return;
 
-	arm_smmu_master_free_smes(cfg);
+	arm_smmu_master_free_smes(fwspec);
 	iommu_group_remove_device(dev);
-	kfree(cfg);
-	dev->archdata.iommu = NULL;
+	kfree(fwspec->iommu_priv);
+	iommu_fwspec_free(dev);
 }
 
 static struct iommu_group *arm_smmu_device_group(struct device *dev)
 {
-	struct arm_smmu_master_cfg *cfg = dev->archdata.iommu;
-	struct arm_smmu_device *smmu = cfg->smmu;
+	struct iommu_fwspec *fwspec = dev->iommu_fwspec;
+	struct arm_smmu_device *smmu = fwspec_smmu(fwspec);
 	struct iommu_group *group = NULL;
 	int i, idx;
 
-	for_each_cfg_sme(cfg, i, idx) {
+	for_each_cfg_sme(fwspec, i, idx) {
 		if (group && smmu->s2crs[idx].group &&
 		    group != smmu->s2crs[idx].group)
 			return ERR_PTR(-EINVAL);
@@ -1936,6 +1951,7 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
 		}
 	}
 
+	of_iommu_set_ops(dev->of_node, &arm_smmu_ops);
 	platform_set_drvdata(pdev, smmu);
 	arm_smmu_device_reset(smmu);
 	return 0;
-- 
2.8.1.dirty

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

* [PATCH v7 18/22] Docs: dt: document ARM SMMU generic binding usage
  2016-09-12 16:13 ` Robin Murphy
@ 2016-09-12 16:13     ` Robin Murphy
  -1 siblings, 0 replies; 104+ messages in thread
From: Robin Murphy @ 2016-09-12 16:13 UTC (permalink / raw)
  To: will.deacon-5wv7dgnIgG8, joro-zLv9SwRftAIdnm+yROfE0A,
	iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, lorenzo.pieralisi-5wv7dgnIgG8,
	jean-philippe.brucker-5wv7dgnIgG8, punit.agrawal-5wv7dgnIgG8,
	thunder.leizhen-hv44wF8Li93QT0dZR+AlfA,
	eric.auger-H+wXaHxf7aLQT0dZR+AlfA

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

Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
Signed-off-by: Robin Murphy <robin.murphy-5wv7dgnIgG8@public.gmane.org>

---

- Remove missed reference to old "mmu-masters" example
---
 .../devicetree/bindings/iommu/arm,smmu.txt         | 63 ++++++++++++++++------
 1 file changed, 48 insertions(+), 15 deletions(-)

diff --git a/Documentation/devicetree/bindings/iommu/arm,smmu.txt b/Documentation/devicetree/bindings/iommu/arm,smmu.txt
index 19fe6f2c83f6..e862d1485205 100644
--- a/Documentation/devicetree/bindings/iommu/arm,smmu.txt
+++ b/Documentation/devicetree/bindings/iommu/arm,smmu.txt
@@ -35,12 +35,16 @@ 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. With a value of 1, each "iommus" entry
+                  represents a distinct stream ID emitted by that device
+                  into the relevant SMMU.
+
+                  SMMUs with stream matching support and complex masters
+                  may use a value of 2, where the second cell represents
+                  an SMR mask to combine with the ID in the first cell.
+                  Care must be taken to ensure the set of matched IDs
+                  does not result in conflicts.
 
 ** System MMU optional properties:
 
@@ -56,9 +60,20 @@ 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 Stream IDs. Each device node
+                  linked from this list must have a "#stream-id-cells"
+                  property, indicating the number of Stream ID
+                  arguments associated with its phandle.
+
+** Examples:
+
+        /* SMMU with stream matching or stream indexing */
+        smmu1: iommu {
                 compatible = "arm,smmu-v1";
                 reg = <0xba5e0000 0x10000>;
                 #global-interrupts = <2>;
@@ -68,11 +83,29 @@ 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 */
+        master1 {
+                iommus = <&smmu1 0>,
+                         <&smmu1 7>;
+        };
+
+
+        /* SMMU with stream matching */
+        smmu2: iommu {
+                ...
+                #iommu-cells = <2>;
+        };
+
+        /* device with stream IDs 0 and 7 */
+        master2 {
+                iommus = <&smmu2 0 0>,
+                         <&smmu2 7 0>;
+        };
+
+        /* device with stream IDs 1, 17, 33 and 49 */
+        master3 {
+                iommus = <&smmu2 1 0x30>;
         };
-- 
2.8.1.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] 104+ messages in thread

* [PATCH v7 18/22] Docs: dt: document ARM SMMU generic binding usage
@ 2016-09-12 16:13     ` Robin Murphy
  0 siblings, 0 replies; 104+ messages in thread
From: Robin Murphy @ 2016-09-12 16:13 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.

Acked-by: Rob Herring <robh@kernel.org>
Signed-off-by: Robin Murphy <robin.murphy@arm.com>

---

- Remove missed reference to old "mmu-masters" example
---
 .../devicetree/bindings/iommu/arm,smmu.txt         | 63 ++++++++++++++++------
 1 file changed, 48 insertions(+), 15 deletions(-)

diff --git a/Documentation/devicetree/bindings/iommu/arm,smmu.txt b/Documentation/devicetree/bindings/iommu/arm,smmu.txt
index 19fe6f2c83f6..e862d1485205 100644
--- a/Documentation/devicetree/bindings/iommu/arm,smmu.txt
+++ b/Documentation/devicetree/bindings/iommu/arm,smmu.txt
@@ -35,12 +35,16 @@ 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. With a value of 1, each "iommus" entry
+                  represents a distinct stream ID emitted by that device
+                  into the relevant SMMU.
+
+                  SMMUs with stream matching support and complex masters
+                  may use a value of 2, where the second cell represents
+                  an SMR mask to combine with the ID in the first cell.
+                  Care must be taken to ensure the set of matched IDs
+                  does not result in conflicts.
 
 ** System MMU optional properties:
 
@@ -56,9 +60,20 @@ 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 Stream IDs. Each device node
+                  linked from this list must have a "#stream-id-cells"
+                  property, indicating the number of Stream ID
+                  arguments associated with its phandle.
+
+** Examples:
+
+        /* SMMU with stream matching or stream indexing */
+        smmu1: iommu {
                 compatible = "arm,smmu-v1";
                 reg = <0xba5e0000 0x10000>;
                 #global-interrupts = <2>;
@@ -68,11 +83,29 @@ 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 */
+        master1 {
+                iommus = <&smmu1 0>,
+                         <&smmu1 7>;
+        };
+
+
+        /* SMMU with stream matching */
+        smmu2: iommu {
+                ...
+                #iommu-cells = <2>;
+        };
+
+        /* device with stream IDs 0 and 7 */
+        master2 {
+                iommus = <&smmu2 0 0>,
+                         <&smmu2 7 0>;
+        };
+
+        /* device with stream IDs 1, 17, 33 and 49 */
+        master3 {
+                iommus = <&smmu2 1 0x30>;
         };
-- 
2.8.1.dirty

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

* [PATCH v7 19/22] iommu/arm-smmu: Wire up generic configuration support
  2016-09-12 16:13 ` Robin Murphy
@ 2016-09-12 16:13     ` Robin Murphy
  -1 siblings, 0 replies; 104+ messages in thread
From: Robin Murphy @ 2016-09-12 16:13 UTC (permalink / raw)
  To: will.deacon-5wv7dgnIgG8, joro-zLv9SwRftAIdnm+yROfE0A,
	iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, lorenzo.pieralisi-5wv7dgnIgG8,
	jean-philippe.brucker-5wv7dgnIgG8, punit.agrawal-5wv7dgnIgG8,
	thunder.leizhen-hv44wF8Li93QT0dZR+AlfA,
	eric.auger-H+wXaHxf7aLQT0dZR+AlfA

With everything else now in place, fill in an of_xlate callback and the
appropriate registration to plumb into the generic configuration
machinery, and watch everything just work.

Signed-off-by: Robin Murphy <robin.murphy-5wv7dgnIgG8@public.gmane.org>

---

- Convert to updated fwspec mechanism
---
 drivers/iommu/arm-smmu.c | 168 ++++++++++++++++++++++++++++++-----------------
 1 file changed, 108 insertions(+), 60 deletions(-)

diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index c5ee5a51c7ec..f143dbc549b9 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -43,6 +43,7 @@
 #include <linux/of_address.h>
 #include <linux/of_device.h>
 #include <linux/of_iommu.h>
+#include <linux/of_platform.h>
 #include <linux/pci.h>
 #include <linux/platform_device.h>
 #include <linux/slab.h>
@@ -418,6 +419,8 @@ struct arm_smmu_option_prop {
 
 static atomic_t cavium_smmu_context_count = ATOMIC_INIT(0);
 
+static bool using_legacy_binding, using_generic_binding;
+
 static struct arm_smmu_option_prop arm_smmu_options[] = {
 	{ ARM_SMMU_OPT_SECURE_CFG_ACCESS, "calxeda,smmu-secure-config-access" },
 	{ 0, NULL},
@@ -817,12 +820,6 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain,
 	if (smmu_domain->smmu)
 		goto out_unlock;
 
-	/* We're bypassing these SIDs, so don't allocate an actual context */
-	if (domain->type == IOMMU_DOMAIN_DMA) {
-		smmu_domain->smmu = smmu;
-		goto out_unlock;
-	}
-
 	/*
 	 * Mapping the requested stage onto what we support is surprisingly
 	 * complicated, mainly because the spec allows S1+S2 SMMUs without
@@ -981,7 +978,7 @@ static void arm_smmu_destroy_domain_context(struct iommu_domain *domain)
 	void __iomem *cb_base;
 	int irq;
 
-	if (!smmu || domain->type == IOMMU_DOMAIN_DMA)
+	if (!smmu)
 		return;
 
 	/*
@@ -1015,8 +1012,8 @@ static struct iommu_domain *arm_smmu_domain_alloc(unsigned type)
 	if (!smmu_domain)
 		return NULL;
 
-	if (type == IOMMU_DOMAIN_DMA &&
-	    iommu_get_dma_cookie(&smmu_domain->domain)) {
+	if (type == IOMMU_DOMAIN_DMA && (using_legacy_binding ||
+	    iommu_get_dma_cookie(&smmu_domain->domain))) {
 		kfree(smmu_domain);
 		return NULL;
 	}
@@ -1133,19 +1130,22 @@ static int arm_smmu_master_alloc_smes(struct device *dev)
 	mutex_lock(&smmu->stream_map_mutex);
 	/* Figure out a viable stream map entry allocation */
 	for_each_cfg_sme(fwspec, i, idx) {
+		u16 sid = fwspec->ids[i];
+		u16 mask = fwspec->ids[i] >> SMR_MASK_SHIFT;
+
 		if (idx != INVALID_SMENDX) {
 			ret = -EEXIST;
 			goto out_err;
 		}
 
-		ret = arm_smmu_find_sme(smmu, fwspec->ids[i], 0);
+		ret = arm_smmu_find_sme(smmu, sid, mask);
 		if (ret < 0)
 			goto out_err;
 
 		idx = ret;
 		if (smrs && smmu->s2crs[idx].count == 0) {
-			smrs[idx].id = fwspec->ids[i];
-			smrs[idx].mask = 0; /* We don't currently share SMRs */
+			smrs[idx].id = sid;
+			smrs[idx].mask = mask;
 			smrs[idx].valid = true;
 		}
 		smmu->s2crs[idx].count++;
@@ -1203,15 +1203,6 @@ static int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain,
 	u8 cbndx = smmu_domain->cfg.cbndx;
 	int i, idx;
 
-	/*
-	 * FIXME: This won't be needed once we have IOMMU-backed DMA ops
-	 * for all devices behind the SMMU. Note that we need to take
-	 * care configuring SMRs for devices both a platform_device and
-	 * and a PCI device (i.e. a PCI host controller)
-	 */
-	if (smmu_domain->domain.type == IOMMU_DOMAIN_DMA)
-		type = S2CR_TYPE_BYPASS;
-
 	for_each_cfg_sme(fwspec, i, idx) {
 		if (type == s2cr[idx].type && cbndx == s2cr[idx].cbndx)
 			continue;
@@ -1373,25 +1364,50 @@ static bool arm_smmu_capable(enum iommu_cap cap)
 	}
 }
 
+static int arm_smmu_match_node(struct device *dev, void *data)
+{
+	return dev->of_node == data;
+}
+
+static struct arm_smmu_device *arm_smmu_get_by_node(struct device_node *np)
+{
+	struct device *dev = driver_find_device(&arm_smmu_driver.driver, NULL,
+						np, arm_smmu_match_node);
+	put_device(dev);
+	return dev ? dev_get_drvdata(dev) : NULL;
+}
+
 static int arm_smmu_add_device(struct device *dev)
 {
 	struct arm_smmu_device *smmu;
 	struct arm_smmu_master_cfg *cfg;
-	struct iommu_fwspec *fwspec;
+	struct iommu_fwspec *fwspec = dev->iommu_fwspec;
 	int i, ret;
 
-	ret = arm_smmu_register_legacy_master(dev, &smmu);
-	fwspec = dev->iommu_fwspec;
-	if (ret)
-		goto out_free;
+	if (using_legacy_binding) {
+		ret = arm_smmu_register_legacy_master(dev, &smmu);
+		fwspec = dev->iommu_fwspec;
+		if (ret)
+			goto out_free;
+	} else if (fwspec) {
+		smmu = arm_smmu_get_by_node(to_of_node(fwspec->iommu_fwnode));
+	} else {
+		return -ENODEV;
+	}
 
 	ret = -EINVAL;
 	for (i = 0; i < fwspec->num_ids; i++) {
 		u16 sid = fwspec->ids[i];
+		u16 mask = fwspec->ids[i] >> SMR_MASK_SHIFT;
 
 		if (sid & ~smmu->streamid_mask) {
 			dev_err(dev, "stream ID 0x%x out of range for SMMU (0x%x)\n",
-				sid, cfg->smmu->streamid_mask);
+				sid, smmu->streamid_mask);
+			goto out_free;
+		}
+		if (mask & ~smmu->smr_mask_mask) {
+			dev_err(dev, "SMR mask 0x%x out of range for SMMU (0x%x)\n",
+				sid, smmu->smr_mask_mask);
 			goto out_free;
 		}
 	}
@@ -1503,6 +1519,19 @@ out_unlock:
 	return ret;
 }
 
+static int arm_smmu_of_xlate(struct device *dev, struct of_phandle_args *args)
+{
+	u32 fwid = 0;
+
+	if (args->args_count > 0)
+		fwid |= (u16)args->args[0];
+
+	if (args->args_count > 1)
+		fwid |= (u16)args->args[1] << SMR_MASK_SHIFT;
+
+	return iommu_fwspec_add_ids(dev, &fwid, 1);
+}
+
 static struct iommu_ops arm_smmu_ops = {
 	.capable		= arm_smmu_capable,
 	.domain_alloc		= arm_smmu_domain_alloc,
@@ -1517,6 +1546,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 */
 };
 
@@ -1870,6 +1900,18 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
 	struct arm_smmu_device *smmu;
 	struct device *dev = &pdev->dev;
 	int num_irqs, i, err;
+	bool legacy_binding;
+
+	legacy_binding = of_find_property(dev->of_node, "mmu-masters", NULL);
+	if (legacy_binding && !using_generic_binding) {
+		pr_notice("deprecated \"mmu-masters\" DT property in use; DMA API support unavailable\n");
+		using_legacy_binding = true;
+	} else if (!legacy_binding && !using_legacy_binding) {
+		using_generic_binding = true;
+	} else {
+		dev_err(dev, "not probing due to mismatched DT properties\n");
+		return -ENODEV;
+	}
 
 	smmu = devm_kzalloc(dev, sizeof(*smmu), GFP_KERNEL);
 	if (!smmu) {
@@ -1954,6 +1996,20 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
 	of_iommu_set_ops(dev->of_node, &arm_smmu_ops);
 	platform_set_drvdata(pdev, smmu);
 	arm_smmu_device_reset(smmu);
+
+	/* Oh, for a proper bus abstraction */
+	if (!iommu_present(&platform_bus_type))
+		bus_set_iommu(&platform_bus_type, &arm_smmu_ops);
+#ifdef CONFIG_ARM_AMBA
+	if (!iommu_present(&amba_bustype))
+		bus_set_iommu(&amba_bustype, &arm_smmu_ops);
+#endif
+#ifdef CONFIG_PCI
+	if (!iommu_present(&pci_bus_type)) {
+		pci_request_acs();
+		bus_set_iommu(&pci_bus_type, &arm_smmu_ops);
+	}
+#endif
 	return 0;
 }
 
@@ -1983,41 +2039,14 @@ static struct platform_driver arm_smmu_driver = {
 
 static int __init arm_smmu_init(void)
 {
-	struct device_node *np;
-	int ret;
+	static bool registered;
+	int ret = 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
-	 * and IOMMU bus operation registration.
-	 */
-	np = of_find_matching_node(NULL, arm_smmu_of_match);
-	if (!np)
-		return 0;
-
-	of_node_put(np);
-
-	ret = platform_driver_register(&arm_smmu_driver);
-	if (ret)
-		return ret;
-
-	/* Oh, for a proper bus abstraction */
-	if (!iommu_present(&platform_bus_type))
-		bus_set_iommu(&platform_bus_type, &arm_smmu_ops);
-
-#ifdef CONFIG_ARM_AMBA
-	if (!iommu_present(&amba_bustype))
-		bus_set_iommu(&amba_bustype, &arm_smmu_ops);
-#endif
-
-#ifdef CONFIG_PCI
-	if (!iommu_present(&pci_bus_type)) {
-		pci_request_acs();
-		bus_set_iommu(&pci_bus_type, &arm_smmu_ops);
+	if (!registered) {
+		ret = platform_driver_register(&arm_smmu_driver);
+		registered = !ret;
 	}
-#endif
-
-	return 0;
+	return ret;
 }
 
 static void __exit arm_smmu_exit(void)
@@ -2028,6 +2057,25 @@ 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)
+{
+	int ret = arm_smmu_init();
+
+	if (ret)
+		return ret;
+
+	if (!of_platform_device_create(np, NULL, platform_bus_type.dev_root))
+		return -ENODEV;
+
+	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);
+IOMMU_OF_DECLARE(cavium_smmuv2, "cavium,smmu-v2", 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.8.1.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] 104+ messages in thread

* [PATCH v7 19/22] iommu/arm-smmu: Wire up generic configuration support
@ 2016-09-12 16:13     ` Robin Murphy
  0 siblings, 0 replies; 104+ messages in thread
From: Robin Murphy @ 2016-09-12 16:13 UTC (permalink / raw)
  To: linux-arm-kernel

With everything else now in place, fill in an of_xlate callback and the
appropriate registration to plumb into the generic configuration
machinery, and watch everything just work.

Signed-off-by: Robin Murphy <robin.murphy@arm.com>

---

- Convert to updated fwspec mechanism
---
 drivers/iommu/arm-smmu.c | 168 ++++++++++++++++++++++++++++++-----------------
 1 file changed, 108 insertions(+), 60 deletions(-)

diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index c5ee5a51c7ec..f143dbc549b9 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -43,6 +43,7 @@
 #include <linux/of_address.h>
 #include <linux/of_device.h>
 #include <linux/of_iommu.h>
+#include <linux/of_platform.h>
 #include <linux/pci.h>
 #include <linux/platform_device.h>
 #include <linux/slab.h>
@@ -418,6 +419,8 @@ struct arm_smmu_option_prop {
 
 static atomic_t cavium_smmu_context_count = ATOMIC_INIT(0);
 
+static bool using_legacy_binding, using_generic_binding;
+
 static struct arm_smmu_option_prop arm_smmu_options[] = {
 	{ ARM_SMMU_OPT_SECURE_CFG_ACCESS, "calxeda,smmu-secure-config-access" },
 	{ 0, NULL},
@@ -817,12 +820,6 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain,
 	if (smmu_domain->smmu)
 		goto out_unlock;
 
-	/* We're bypassing these SIDs, so don't allocate an actual context */
-	if (domain->type == IOMMU_DOMAIN_DMA) {
-		smmu_domain->smmu = smmu;
-		goto out_unlock;
-	}
-
 	/*
 	 * Mapping the requested stage onto what we support is surprisingly
 	 * complicated, mainly because the spec allows S1+S2 SMMUs without
@@ -981,7 +978,7 @@ static void arm_smmu_destroy_domain_context(struct iommu_domain *domain)
 	void __iomem *cb_base;
 	int irq;
 
-	if (!smmu || domain->type == IOMMU_DOMAIN_DMA)
+	if (!smmu)
 		return;
 
 	/*
@@ -1015,8 +1012,8 @@ static struct iommu_domain *arm_smmu_domain_alloc(unsigned type)
 	if (!smmu_domain)
 		return NULL;
 
-	if (type == IOMMU_DOMAIN_DMA &&
-	    iommu_get_dma_cookie(&smmu_domain->domain)) {
+	if (type == IOMMU_DOMAIN_DMA && (using_legacy_binding ||
+	    iommu_get_dma_cookie(&smmu_domain->domain))) {
 		kfree(smmu_domain);
 		return NULL;
 	}
@@ -1133,19 +1130,22 @@ static int arm_smmu_master_alloc_smes(struct device *dev)
 	mutex_lock(&smmu->stream_map_mutex);
 	/* Figure out a viable stream map entry allocation */
 	for_each_cfg_sme(fwspec, i, idx) {
+		u16 sid = fwspec->ids[i];
+		u16 mask = fwspec->ids[i] >> SMR_MASK_SHIFT;
+
 		if (idx != INVALID_SMENDX) {
 			ret = -EEXIST;
 			goto out_err;
 		}
 
-		ret = arm_smmu_find_sme(smmu, fwspec->ids[i], 0);
+		ret = arm_smmu_find_sme(smmu, sid, mask);
 		if (ret < 0)
 			goto out_err;
 
 		idx = ret;
 		if (smrs && smmu->s2crs[idx].count == 0) {
-			smrs[idx].id = fwspec->ids[i];
-			smrs[idx].mask = 0; /* We don't currently share SMRs */
+			smrs[idx].id = sid;
+			smrs[idx].mask = mask;
 			smrs[idx].valid = true;
 		}
 		smmu->s2crs[idx].count++;
@@ -1203,15 +1203,6 @@ static int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain,
 	u8 cbndx = smmu_domain->cfg.cbndx;
 	int i, idx;
 
-	/*
-	 * FIXME: This won't be needed once we have IOMMU-backed DMA ops
-	 * for all devices behind the SMMU. Note that we need to take
-	 * care configuring SMRs for devices both a platform_device and
-	 * and a PCI device (i.e. a PCI host controller)
-	 */
-	if (smmu_domain->domain.type == IOMMU_DOMAIN_DMA)
-		type = S2CR_TYPE_BYPASS;
-
 	for_each_cfg_sme(fwspec, i, idx) {
 		if (type == s2cr[idx].type && cbndx == s2cr[idx].cbndx)
 			continue;
@@ -1373,25 +1364,50 @@ static bool arm_smmu_capable(enum iommu_cap cap)
 	}
 }
 
+static int arm_smmu_match_node(struct device *dev, void *data)
+{
+	return dev->of_node == data;
+}
+
+static struct arm_smmu_device *arm_smmu_get_by_node(struct device_node *np)
+{
+	struct device *dev = driver_find_device(&arm_smmu_driver.driver, NULL,
+						np, arm_smmu_match_node);
+	put_device(dev);
+	return dev ? dev_get_drvdata(dev) : NULL;
+}
+
 static int arm_smmu_add_device(struct device *dev)
 {
 	struct arm_smmu_device *smmu;
 	struct arm_smmu_master_cfg *cfg;
-	struct iommu_fwspec *fwspec;
+	struct iommu_fwspec *fwspec = dev->iommu_fwspec;
 	int i, ret;
 
-	ret = arm_smmu_register_legacy_master(dev, &smmu);
-	fwspec = dev->iommu_fwspec;
-	if (ret)
-		goto out_free;
+	if (using_legacy_binding) {
+		ret = arm_smmu_register_legacy_master(dev, &smmu);
+		fwspec = dev->iommu_fwspec;
+		if (ret)
+			goto out_free;
+	} else if (fwspec) {
+		smmu = arm_smmu_get_by_node(to_of_node(fwspec->iommu_fwnode));
+	} else {
+		return -ENODEV;
+	}
 
 	ret = -EINVAL;
 	for (i = 0; i < fwspec->num_ids; i++) {
 		u16 sid = fwspec->ids[i];
+		u16 mask = fwspec->ids[i] >> SMR_MASK_SHIFT;
 
 		if (sid & ~smmu->streamid_mask) {
 			dev_err(dev, "stream ID 0x%x out of range for SMMU (0x%x)\n",
-				sid, cfg->smmu->streamid_mask);
+				sid, smmu->streamid_mask);
+			goto out_free;
+		}
+		if (mask & ~smmu->smr_mask_mask) {
+			dev_err(dev, "SMR mask 0x%x out of range for SMMU (0x%x)\n",
+				sid, smmu->smr_mask_mask);
 			goto out_free;
 		}
 	}
@@ -1503,6 +1519,19 @@ out_unlock:
 	return ret;
 }
 
+static int arm_smmu_of_xlate(struct device *dev, struct of_phandle_args *args)
+{
+	u32 fwid = 0;
+
+	if (args->args_count > 0)
+		fwid |= (u16)args->args[0];
+
+	if (args->args_count > 1)
+		fwid |= (u16)args->args[1] << SMR_MASK_SHIFT;
+
+	return iommu_fwspec_add_ids(dev, &fwid, 1);
+}
+
 static struct iommu_ops arm_smmu_ops = {
 	.capable		= arm_smmu_capable,
 	.domain_alloc		= arm_smmu_domain_alloc,
@@ -1517,6 +1546,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 */
 };
 
@@ -1870,6 +1900,18 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
 	struct arm_smmu_device *smmu;
 	struct device *dev = &pdev->dev;
 	int num_irqs, i, err;
+	bool legacy_binding;
+
+	legacy_binding = of_find_property(dev->of_node, "mmu-masters", NULL);
+	if (legacy_binding && !using_generic_binding) {
+		pr_notice("deprecated \"mmu-masters\" DT property in use; DMA API support unavailable\n");
+		using_legacy_binding = true;
+	} else if (!legacy_binding && !using_legacy_binding) {
+		using_generic_binding = true;
+	} else {
+		dev_err(dev, "not probing due to mismatched DT properties\n");
+		return -ENODEV;
+	}
 
 	smmu = devm_kzalloc(dev, sizeof(*smmu), GFP_KERNEL);
 	if (!smmu) {
@@ -1954,6 +1996,20 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
 	of_iommu_set_ops(dev->of_node, &arm_smmu_ops);
 	platform_set_drvdata(pdev, smmu);
 	arm_smmu_device_reset(smmu);
+
+	/* Oh, for a proper bus abstraction */
+	if (!iommu_present(&platform_bus_type))
+		bus_set_iommu(&platform_bus_type, &arm_smmu_ops);
+#ifdef CONFIG_ARM_AMBA
+	if (!iommu_present(&amba_bustype))
+		bus_set_iommu(&amba_bustype, &arm_smmu_ops);
+#endif
+#ifdef CONFIG_PCI
+	if (!iommu_present(&pci_bus_type)) {
+		pci_request_acs();
+		bus_set_iommu(&pci_bus_type, &arm_smmu_ops);
+	}
+#endif
 	return 0;
 }
 
@@ -1983,41 +2039,14 @@ static struct platform_driver arm_smmu_driver = {
 
 static int __init arm_smmu_init(void)
 {
-	struct device_node *np;
-	int ret;
+	static bool registered;
+	int ret = 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
-	 * and IOMMU bus operation registration.
-	 */
-	np = of_find_matching_node(NULL, arm_smmu_of_match);
-	if (!np)
-		return 0;
-
-	of_node_put(np);
-
-	ret = platform_driver_register(&arm_smmu_driver);
-	if (ret)
-		return ret;
-
-	/* Oh, for a proper bus abstraction */
-	if (!iommu_present(&platform_bus_type))
-		bus_set_iommu(&platform_bus_type, &arm_smmu_ops);
-
-#ifdef CONFIG_ARM_AMBA
-	if (!iommu_present(&amba_bustype))
-		bus_set_iommu(&amba_bustype, &arm_smmu_ops);
-#endif
-
-#ifdef CONFIG_PCI
-	if (!iommu_present(&pci_bus_type)) {
-		pci_request_acs();
-		bus_set_iommu(&pci_bus_type, &arm_smmu_ops);
+	if (!registered) {
+		ret = platform_driver_register(&arm_smmu_driver);
+		registered = !ret;
 	}
-#endif
-
-	return 0;
+	return ret;
 }
 
 static void __exit arm_smmu_exit(void)
@@ -2028,6 +2057,25 @@ 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)
+{
+	int ret = arm_smmu_init();
+
+	if (ret)
+		return ret;
+
+	if (!of_platform_device_create(np, NULL, platform_bus_type.dev_root))
+		return -ENODEV;
+
+	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);
+IOMMU_OF_DECLARE(cavium_smmuv2, "cavium,smmu-v2", 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.8.1.dirty

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

* [PATCH v7 20/22] iommu/arm-smmu: Set domain geometry
  2016-09-12 16:13 ` Robin Murphy
@ 2016-09-12 16:13     ` Robin Murphy
  -1 siblings, 0 replies; 104+ messages in thread
From: Robin Murphy @ 2016-09-12 16:13 UTC (permalink / raw)
  To: will.deacon-5wv7dgnIgG8, joro-zLv9SwRftAIdnm+yROfE0A,
	iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, lorenzo.pieralisi-5wv7dgnIgG8,
	jean-philippe.brucker-5wv7dgnIgG8, punit.agrawal-5wv7dgnIgG8,
	thunder.leizhen-hv44wF8Li93QT0dZR+AlfA,
	eric.auger-H+wXaHxf7aLQT0dZR+AlfA

For non-aperture-based IOMMUs, the domain geometry seems to have become
the de-facto way of indicating the input address space size. That is
quite a useful thing from the users' perspective, so let's do the same.

Reviewed-by: Eric Auger <eric.auger-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
Tested-by: Lorenzo Pieralisi <lorenzo.pieralisi-5wv7dgnIgG8@public.gmane.org>
Signed-off-by: Robin Murphy <robin.murphy-5wv7dgnIgG8@public.gmane.org>
---
 drivers/iommu/arm-smmu-v3.c | 2 ++
 drivers/iommu/arm-smmu.c    | 2 ++
 2 files changed, 4 insertions(+)

diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c
index 0c45c1e02e04..15c01c3cd540 100644
--- a/drivers/iommu/arm-smmu-v3.c
+++ b/drivers/iommu/arm-smmu-v3.c
@@ -1551,6 +1551,8 @@ static int arm_smmu_domain_finalise(struct iommu_domain *domain)
 		return -ENOMEM;
 
 	domain->pgsize_bitmap = pgtbl_cfg.pgsize_bitmap;
+	domain->geometry.aperture_end = (1UL << ias) - 1;
+	domain->geometry.force_aperture = true;
 	smmu_domain->pgtbl_ops = pgtbl_ops;
 
 	ret = finalise_stage_fn(smmu_domain, &pgtbl_cfg);
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index f143dbc549b9..fa892d25004d 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -940,6 +940,8 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain,
 
 	/* Update the domain's page sizes to reflect the page table format */
 	domain->pgsize_bitmap = pgtbl_cfg.pgsize_bitmap;
+	domain->geometry.aperture_end = (1UL << ias) - 1;
+	domain->geometry.force_aperture = true;
 
 	/* Initialise the context bank with our page table cfg */
 	arm_smmu_init_context_bank(smmu_domain, &pgtbl_cfg);
-- 
2.8.1.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] 104+ messages in thread

* [PATCH v7 20/22] iommu/arm-smmu: Set domain geometry
@ 2016-09-12 16:13     ` Robin Murphy
  0 siblings, 0 replies; 104+ messages in thread
From: Robin Murphy @ 2016-09-12 16:13 UTC (permalink / raw)
  To: linux-arm-kernel

For non-aperture-based IOMMUs, the domain geometry seems to have become
the de-facto way of indicating the input address space size. That is
quite a useful thing from the users' perspective, so let's do the same.

Reviewed-by: Eric Auger <eric.auger@redhat.com>
Tested-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Signed-off-by: Robin Murphy <robin.murphy@arm.com>
---
 drivers/iommu/arm-smmu-v3.c | 2 ++
 drivers/iommu/arm-smmu.c    | 2 ++
 2 files changed, 4 insertions(+)

diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c
index 0c45c1e02e04..15c01c3cd540 100644
--- a/drivers/iommu/arm-smmu-v3.c
+++ b/drivers/iommu/arm-smmu-v3.c
@@ -1551,6 +1551,8 @@ static int arm_smmu_domain_finalise(struct iommu_domain *domain)
 		return -ENOMEM;
 
 	domain->pgsize_bitmap = pgtbl_cfg.pgsize_bitmap;
+	domain->geometry.aperture_end = (1UL << ias) - 1;
+	domain->geometry.force_aperture = true;
 	smmu_domain->pgtbl_ops = pgtbl_ops;
 
 	ret = finalise_stage_fn(smmu_domain, &pgtbl_cfg);
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index f143dbc549b9..fa892d25004d 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -940,6 +940,8 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain,
 
 	/* Update the domain's page sizes to reflect the page table format */
 	domain->pgsize_bitmap = pgtbl_cfg.pgsize_bitmap;
+	domain->geometry.aperture_end = (1UL << ias) - 1;
+	domain->geometry.force_aperture = true;
 
 	/* Initialise the context bank with our page table cfg */
 	arm_smmu_init_context_bank(smmu_domain, &pgtbl_cfg);
-- 
2.8.1.dirty

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

* [PATCH v7 21/22] iommu/dma: Add support for mapping MSIs
  2016-09-12 16:13 ` Robin Murphy
@ 2016-09-12 16:13     ` Robin Murphy
  -1 siblings, 0 replies; 104+ messages in thread
From: Robin Murphy @ 2016-09-12 16:13 UTC (permalink / raw)
  To: will.deacon-5wv7dgnIgG8, joro-zLv9SwRftAIdnm+yROfE0A,
	iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, lorenzo.pieralisi-5wv7dgnIgG8,
	jean-philippe.brucker-5wv7dgnIgG8, punit.agrawal-5wv7dgnIgG8,
	thunder.leizhen-hv44wF8Li93QT0dZR+AlfA,
	eric.auger-H+wXaHxf7aLQT0dZR+AlfA

When an MSI doorbell is located downstream of an IOMMU, attaching
devices to a DMA ops domain and switching on translation leads to a rude
shock when their attempt to write to the physical address returned by
the irqchip driver faults (or worse, writes into some already-mapped
buffer) and no interrupt is forthcoming.

Address this by adding a hook for relevant irqchip drivers to call from
their compose_msi_msg() callback, to swizzle the physical address with
an appropriatly-mapped IOVA for any device attached to one of our DMA
ops domains.

Acked-by: Thomas Gleixner <tglx-hfZtesqFncYOwBW4kG4KsQ@public.gmane.org>
Acked-by: Marc Zyngier <marc.zyngier-5wv7dgnIgG8@public.gmane.org>
Signed-off-by: Robin Murphy <robin.murphy-5wv7dgnIgG8@public.gmane.org>
---
 drivers/iommu/dma-iommu.c        | 136 ++++++++++++++++++++++++++++++++++-----
 drivers/irqchip/irq-gic-v2m.c    |   3 +
 drivers/irqchip/irq-gic-v3-its.c |   3 +
 include/linux/dma-iommu.h        |   9 +++
 4 files changed, 136 insertions(+), 15 deletions(-)

diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index 00c8a08d56e7..4329d18080cf 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -25,10 +25,28 @@
 #include <linux/huge_mm.h>
 #include <linux/iommu.h>
 #include <linux/iova.h>
+#include <linux/irq.h>
 #include <linux/mm.h>
 #include <linux/scatterlist.h>
 #include <linux/vmalloc.h>
 
+struct iommu_dma_msi_page {
+	struct list_head	list;
+	dma_addr_t		iova;
+	phys_addr_t		phys;
+};
+
+struct iommu_dma_cookie {
+	struct iova_domain	iovad;
+	struct list_head	msi_page_list;
+	spinlock_t		msi_lock;
+};
+
+static inline struct iova_domain *cookie_iovad(struct iommu_domain *domain)
+{
+	return &((struct iommu_dma_cookie *)domain->iova_cookie)->iovad;
+}
+
 int iommu_dma_init(void)
 {
 	return iova_cache_get();
@@ -43,15 +61,19 @@ int iommu_dma_init(void)
  */
 int iommu_get_dma_cookie(struct iommu_domain *domain)
 {
-	struct iova_domain *iovad;
+	struct iommu_dma_cookie *cookie;
 
 	if (domain->iova_cookie)
 		return -EEXIST;
 
-	iovad = kzalloc(sizeof(*iovad), GFP_KERNEL);
-	domain->iova_cookie = iovad;
+	cookie = kzalloc(sizeof(*cookie), GFP_KERNEL);
+	if (!cookie)
+		return -ENOMEM;
 
-	return iovad ? 0 : -ENOMEM;
+	spin_lock_init(&cookie->msi_lock);
+	INIT_LIST_HEAD(&cookie->msi_page_list);
+	domain->iova_cookie = cookie;
+	return 0;
 }
 EXPORT_SYMBOL(iommu_get_dma_cookie);
 
@@ -63,14 +85,20 @@ EXPORT_SYMBOL(iommu_get_dma_cookie);
  */
 void iommu_put_dma_cookie(struct iommu_domain *domain)
 {
-	struct iova_domain *iovad = domain->iova_cookie;
+	struct iommu_dma_cookie *cookie = domain->iova_cookie;
+	struct iommu_dma_msi_page *msi, *tmp;
 
-	if (!iovad)
+	if (!cookie)
 		return;
 
-	if (iovad->granule)
-		put_iova_domain(iovad);
-	kfree(iovad);
+	if (cookie->iovad.granule)
+		put_iova_domain(&cookie->iovad);
+
+	list_for_each_entry_safe(msi, tmp, &cookie->msi_page_list, list) {
+		list_del(&msi->list);
+		kfree(msi);
+	}
+	kfree(cookie);
 	domain->iova_cookie = NULL;
 }
 EXPORT_SYMBOL(iommu_put_dma_cookie);
@@ -88,7 +116,7 @@ EXPORT_SYMBOL(iommu_put_dma_cookie);
  */
 int iommu_dma_init_domain(struct iommu_domain *domain, dma_addr_t base, u64 size)
 {
-	struct iova_domain *iovad = domain->iova_cookie;
+	struct iova_domain *iovad = cookie_iovad(domain);
 	unsigned long order, base_pfn, end_pfn;
 
 	if (!iovad)
@@ -155,7 +183,7 @@ int dma_direction_to_prot(enum dma_data_direction dir, bool coherent)
 static struct iova *__alloc_iova(struct iommu_domain *domain, size_t size,
 		dma_addr_t dma_limit)
 {
-	struct iova_domain *iovad = domain->iova_cookie;
+	struct iova_domain *iovad = cookie_iovad(domain);
 	unsigned long shift = iova_shift(iovad);
 	unsigned long length = iova_align(iovad, size) >> shift;
 
@@ -171,7 +199,7 @@ static struct iova *__alloc_iova(struct iommu_domain *domain, size_t size,
 /* The IOVA allocator knows what we mapped, so just unmap whatever that was */
 static void __iommu_dma_unmap(struct iommu_domain *domain, dma_addr_t dma_addr)
 {
-	struct iova_domain *iovad = domain->iova_cookie;
+	struct iova_domain *iovad = cookie_iovad(domain);
 	unsigned long shift = iova_shift(iovad);
 	unsigned long pfn = dma_addr >> shift;
 	struct iova *iova = find_iova(iovad, pfn);
@@ -294,7 +322,7 @@ struct page **iommu_dma_alloc(struct device *dev, size_t size, gfp_t gfp,
 		void (*flush_page)(struct device *, const void *, phys_addr_t))
 {
 	struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
-	struct iova_domain *iovad = domain->iova_cookie;
+	struct iova_domain *iovad = cookie_iovad(domain);
 	struct iova *iova;
 	struct page **pages;
 	struct sg_table sgt;
@@ -386,7 +414,7 @@ dma_addr_t iommu_dma_map_page(struct device *dev, struct page *page,
 {
 	dma_addr_t dma_addr;
 	struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
-	struct iova_domain *iovad = domain->iova_cookie;
+	struct iova_domain *iovad = cookie_iovad(domain);
 	phys_addr_t phys = page_to_phys(page) + offset;
 	size_t iova_off = iova_offset(iovad, phys);
 	size_t len = iova_align(iovad, size + iova_off);
@@ -495,7 +523,7 @@ int iommu_dma_map_sg(struct device *dev, struct scatterlist *sg,
 		int nents, int prot)
 {
 	struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
-	struct iova_domain *iovad = domain->iova_cookie;
+	struct iova_domain *iovad = cookie_iovad(domain);
 	struct iova *iova;
 	struct scatterlist *s, *prev = NULL;
 	dma_addr_t dma_addr;
@@ -587,3 +615,81 @@ int iommu_dma_mapping_error(struct device *dev, dma_addr_t dma_addr)
 {
 	return dma_addr == DMA_ERROR_CODE;
 }
+
+static struct iommu_dma_msi_page *iommu_dma_get_msi_page(struct device *dev,
+		phys_addr_t msi_addr, struct iommu_domain *domain)
+{
+	struct iommu_dma_cookie *cookie = domain->iova_cookie;
+	struct iommu_dma_msi_page *msi_page;
+	struct iova_domain *iovad = &cookie->iovad;
+	struct iova *iova;
+	int prot = IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_MMIO;
+
+	msi_addr &= ~(phys_addr_t)iova_mask(iovad);
+	list_for_each_entry(msi_page, &cookie->msi_page_list, list)
+		if (msi_page->phys == msi_addr)
+			return msi_page;
+
+	msi_page = kzalloc(sizeof(*msi_page), GFP_ATOMIC);
+	if (!msi_page)
+		return NULL;
+
+	iova = __alloc_iova(domain, iovad->granule, dma_get_mask(dev));
+	if (!iova)
+		goto out_free_page;
+
+	msi_page->phys = msi_addr;
+	msi_page->iova = iova_dma_addr(iovad, iova);
+	if (iommu_map(domain, msi_page->iova, msi_addr, iovad->granule, prot))
+		goto out_free_iova;
+
+	INIT_LIST_HEAD(&msi_page->list);
+	list_add(&msi_page->list, &cookie->msi_page_list);
+	return msi_page;
+
+out_free_iova:
+	__free_iova(iovad, iova);
+out_free_page:
+	kfree(msi_page);
+	return NULL;
+}
+
+void iommu_dma_map_msi_msg(int irq, struct msi_msg *msg)
+{
+	struct device *dev = msi_desc_to_dev(irq_get_msi_desc(irq));
+	struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
+	struct iommu_dma_cookie *cookie;
+	struct iommu_dma_msi_page *msi_page;
+	phys_addr_t msi_addr = (u64)msg->address_hi << 32 | msg->address_lo;
+	unsigned long flags;
+
+	if (!domain || !domain->iova_cookie)
+		return;
+
+	cookie = domain->iova_cookie;
+
+	/*
+	 * We disable IRQs to rule out a possible inversion against
+	 * irq_desc_lock if, say, someone tries to retarget the affinity
+	 * of an MSI from within an IPI handler.
+	 */
+	spin_lock_irqsave(&cookie->msi_lock, flags);
+	msi_page = iommu_dma_get_msi_page(dev, msi_addr, domain);
+	spin_unlock_irqrestore(&cookie->msi_lock, flags);
+
+	if (WARN_ON(!msi_page)) {
+		/*
+		 * We're called from a void callback, so the best we can do is
+		 * 'fail' by filling the message with obviously bogus values.
+		 * Since we got this far due to an IOMMU being present, it's
+		 * not like the existing address would have worked anyway...
+		 */
+		msg->address_hi = ~0U;
+		msg->address_lo = ~0U;
+		msg->data = ~0U;
+	} else {
+		msg->address_hi = upper_32_bits(msi_page->iova);
+		msg->address_lo &= iova_mask(&cookie->iovad);
+		msg->address_lo += lower_32_bits(msi_page->iova);
+	}
+}
diff --git a/drivers/irqchip/irq-gic-v2m.c b/drivers/irqchip/irq-gic-v2m.c
index 35eb7ac5d21f..863e073c6f7f 100644
--- a/drivers/irqchip/irq-gic-v2m.c
+++ b/drivers/irqchip/irq-gic-v2m.c
@@ -16,6 +16,7 @@
 #define pr_fmt(fmt) "GICv2m: " fmt
 
 #include <linux/acpi.h>
+#include <linux/dma-iommu.h>
 #include <linux/irq.h>
 #include <linux/irqdomain.h>
 #include <linux/kernel.h>
@@ -108,6 +109,8 @@ static void gicv2m_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
 
 	if (v2m->flags & GICV2M_NEEDS_SPI_OFFSET)
 		msg->data -= v2m->spi_offset;
+
+	iommu_dma_map_msi_msg(data->irq, msg);
 }
 
 static struct irq_chip gicv2m_irq_chip = {
diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c
index 36b9c28a5c91..98ff669d5962 100644
--- a/drivers/irqchip/irq-gic-v3-its.c
+++ b/drivers/irqchip/irq-gic-v3-its.c
@@ -18,6 +18,7 @@
 #include <linux/bitmap.h>
 #include <linux/cpu.h>
 #include <linux/delay.h>
+#include <linux/dma-iommu.h>
 #include <linux/interrupt.h>
 #include <linux/log2.h>
 #include <linux/mm.h>
@@ -655,6 +656,8 @@ static void its_irq_compose_msi_msg(struct irq_data *d, struct msi_msg *msg)
 	msg->address_lo		= addr & ((1UL << 32) - 1);
 	msg->address_hi		= addr >> 32;
 	msg->data		= its_get_event_id(d);
+
+	iommu_dma_map_msi_msg(d->irq, msg);
 }
 
 static struct irq_chip its_irq_chip = {
diff --git a/include/linux/dma-iommu.h b/include/linux/dma-iommu.h
index 81c5c8d167ad..5ee806e41b5c 100644
--- a/include/linux/dma-iommu.h
+++ b/include/linux/dma-iommu.h
@@ -21,6 +21,7 @@
 
 #ifdef CONFIG_IOMMU_DMA
 #include <linux/iommu.h>
+#include <linux/msi.h>
 
 int iommu_dma_init(void);
 
@@ -62,9 +63,13 @@ void iommu_dma_unmap_sg(struct device *dev, struct scatterlist *sg, int nents,
 int iommu_dma_supported(struct device *dev, u64 mask);
 int iommu_dma_mapping_error(struct device *dev, dma_addr_t dma_addr);
 
+/* The DMA API isn't _quite_ the whole story, though... */
+void iommu_dma_map_msi_msg(int irq, struct msi_msg *msg);
+
 #else
 
 struct iommu_domain;
+struct msi_msg;
 
 static inline int iommu_dma_init(void)
 {
@@ -80,6 +85,10 @@ static inline void iommu_put_dma_cookie(struct iommu_domain *domain)
 {
 }
 
+static inline void iommu_dma_map_msi_msg(int irq, struct msi_msg *msg)
+{
+}
+
 #endif	/* CONFIG_IOMMU_DMA */
 #endif	/* __KERNEL__ */
 #endif	/* __DMA_IOMMU_H */
-- 
2.8.1.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] 104+ messages in thread

* [PATCH v7 21/22] iommu/dma: Add support for mapping MSIs
@ 2016-09-12 16:13     ` Robin Murphy
  0 siblings, 0 replies; 104+ messages in thread
From: Robin Murphy @ 2016-09-12 16:13 UTC (permalink / raw)
  To: linux-arm-kernel

When an MSI doorbell is located downstream of an IOMMU, attaching
devices to a DMA ops domain and switching on translation leads to a rude
shock when their attempt to write to the physical address returned by
the irqchip driver faults (or worse, writes into some already-mapped
buffer) and no interrupt is forthcoming.

Address this by adding a hook for relevant irqchip drivers to call from
their compose_msi_msg() callback, to swizzle the physical address with
an appropriatly-mapped IOVA for any device attached to one of our DMA
ops domains.

Acked-by: Thomas Gleixner <tglx@linutronix.de>
Acked-by: Marc Zyngier <marc.zyngier@arm.com>
Signed-off-by: Robin Murphy <robin.murphy@arm.com>
---
 drivers/iommu/dma-iommu.c        | 136 ++++++++++++++++++++++++++++++++++-----
 drivers/irqchip/irq-gic-v2m.c    |   3 +
 drivers/irqchip/irq-gic-v3-its.c |   3 +
 include/linux/dma-iommu.h        |   9 +++
 4 files changed, 136 insertions(+), 15 deletions(-)

diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index 00c8a08d56e7..4329d18080cf 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -25,10 +25,28 @@
 #include <linux/huge_mm.h>
 #include <linux/iommu.h>
 #include <linux/iova.h>
+#include <linux/irq.h>
 #include <linux/mm.h>
 #include <linux/scatterlist.h>
 #include <linux/vmalloc.h>
 
+struct iommu_dma_msi_page {
+	struct list_head	list;
+	dma_addr_t		iova;
+	phys_addr_t		phys;
+};
+
+struct iommu_dma_cookie {
+	struct iova_domain	iovad;
+	struct list_head	msi_page_list;
+	spinlock_t		msi_lock;
+};
+
+static inline struct iova_domain *cookie_iovad(struct iommu_domain *domain)
+{
+	return &((struct iommu_dma_cookie *)domain->iova_cookie)->iovad;
+}
+
 int iommu_dma_init(void)
 {
 	return iova_cache_get();
@@ -43,15 +61,19 @@ int iommu_dma_init(void)
  */
 int iommu_get_dma_cookie(struct iommu_domain *domain)
 {
-	struct iova_domain *iovad;
+	struct iommu_dma_cookie *cookie;
 
 	if (domain->iova_cookie)
 		return -EEXIST;
 
-	iovad = kzalloc(sizeof(*iovad), GFP_KERNEL);
-	domain->iova_cookie = iovad;
+	cookie = kzalloc(sizeof(*cookie), GFP_KERNEL);
+	if (!cookie)
+		return -ENOMEM;
 
-	return iovad ? 0 : -ENOMEM;
+	spin_lock_init(&cookie->msi_lock);
+	INIT_LIST_HEAD(&cookie->msi_page_list);
+	domain->iova_cookie = cookie;
+	return 0;
 }
 EXPORT_SYMBOL(iommu_get_dma_cookie);
 
@@ -63,14 +85,20 @@ EXPORT_SYMBOL(iommu_get_dma_cookie);
  */
 void iommu_put_dma_cookie(struct iommu_domain *domain)
 {
-	struct iova_domain *iovad = domain->iova_cookie;
+	struct iommu_dma_cookie *cookie = domain->iova_cookie;
+	struct iommu_dma_msi_page *msi, *tmp;
 
-	if (!iovad)
+	if (!cookie)
 		return;
 
-	if (iovad->granule)
-		put_iova_domain(iovad);
-	kfree(iovad);
+	if (cookie->iovad.granule)
+		put_iova_domain(&cookie->iovad);
+
+	list_for_each_entry_safe(msi, tmp, &cookie->msi_page_list, list) {
+		list_del(&msi->list);
+		kfree(msi);
+	}
+	kfree(cookie);
 	domain->iova_cookie = NULL;
 }
 EXPORT_SYMBOL(iommu_put_dma_cookie);
@@ -88,7 +116,7 @@ EXPORT_SYMBOL(iommu_put_dma_cookie);
  */
 int iommu_dma_init_domain(struct iommu_domain *domain, dma_addr_t base, u64 size)
 {
-	struct iova_domain *iovad = domain->iova_cookie;
+	struct iova_domain *iovad = cookie_iovad(domain);
 	unsigned long order, base_pfn, end_pfn;
 
 	if (!iovad)
@@ -155,7 +183,7 @@ int dma_direction_to_prot(enum dma_data_direction dir, bool coherent)
 static struct iova *__alloc_iova(struct iommu_domain *domain, size_t size,
 		dma_addr_t dma_limit)
 {
-	struct iova_domain *iovad = domain->iova_cookie;
+	struct iova_domain *iovad = cookie_iovad(domain);
 	unsigned long shift = iova_shift(iovad);
 	unsigned long length = iova_align(iovad, size) >> shift;
 
@@ -171,7 +199,7 @@ static struct iova *__alloc_iova(struct iommu_domain *domain, size_t size,
 /* The IOVA allocator knows what we mapped, so just unmap whatever that was */
 static void __iommu_dma_unmap(struct iommu_domain *domain, dma_addr_t dma_addr)
 {
-	struct iova_domain *iovad = domain->iova_cookie;
+	struct iova_domain *iovad = cookie_iovad(domain);
 	unsigned long shift = iova_shift(iovad);
 	unsigned long pfn = dma_addr >> shift;
 	struct iova *iova = find_iova(iovad, pfn);
@@ -294,7 +322,7 @@ struct page **iommu_dma_alloc(struct device *dev, size_t size, gfp_t gfp,
 		void (*flush_page)(struct device *, const void *, phys_addr_t))
 {
 	struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
-	struct iova_domain *iovad = domain->iova_cookie;
+	struct iova_domain *iovad = cookie_iovad(domain);
 	struct iova *iova;
 	struct page **pages;
 	struct sg_table sgt;
@@ -386,7 +414,7 @@ dma_addr_t iommu_dma_map_page(struct device *dev, struct page *page,
 {
 	dma_addr_t dma_addr;
 	struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
-	struct iova_domain *iovad = domain->iova_cookie;
+	struct iova_domain *iovad = cookie_iovad(domain);
 	phys_addr_t phys = page_to_phys(page) + offset;
 	size_t iova_off = iova_offset(iovad, phys);
 	size_t len = iova_align(iovad, size + iova_off);
@@ -495,7 +523,7 @@ int iommu_dma_map_sg(struct device *dev, struct scatterlist *sg,
 		int nents, int prot)
 {
 	struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
-	struct iova_domain *iovad = domain->iova_cookie;
+	struct iova_domain *iovad = cookie_iovad(domain);
 	struct iova *iova;
 	struct scatterlist *s, *prev = NULL;
 	dma_addr_t dma_addr;
@@ -587,3 +615,81 @@ int iommu_dma_mapping_error(struct device *dev, dma_addr_t dma_addr)
 {
 	return dma_addr == DMA_ERROR_CODE;
 }
+
+static struct iommu_dma_msi_page *iommu_dma_get_msi_page(struct device *dev,
+		phys_addr_t msi_addr, struct iommu_domain *domain)
+{
+	struct iommu_dma_cookie *cookie = domain->iova_cookie;
+	struct iommu_dma_msi_page *msi_page;
+	struct iova_domain *iovad = &cookie->iovad;
+	struct iova *iova;
+	int prot = IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_MMIO;
+
+	msi_addr &= ~(phys_addr_t)iova_mask(iovad);
+	list_for_each_entry(msi_page, &cookie->msi_page_list, list)
+		if (msi_page->phys == msi_addr)
+			return msi_page;
+
+	msi_page = kzalloc(sizeof(*msi_page), GFP_ATOMIC);
+	if (!msi_page)
+		return NULL;
+
+	iova = __alloc_iova(domain, iovad->granule, dma_get_mask(dev));
+	if (!iova)
+		goto out_free_page;
+
+	msi_page->phys = msi_addr;
+	msi_page->iova = iova_dma_addr(iovad, iova);
+	if (iommu_map(domain, msi_page->iova, msi_addr, iovad->granule, prot))
+		goto out_free_iova;
+
+	INIT_LIST_HEAD(&msi_page->list);
+	list_add(&msi_page->list, &cookie->msi_page_list);
+	return msi_page;
+
+out_free_iova:
+	__free_iova(iovad, iova);
+out_free_page:
+	kfree(msi_page);
+	return NULL;
+}
+
+void iommu_dma_map_msi_msg(int irq, struct msi_msg *msg)
+{
+	struct device *dev = msi_desc_to_dev(irq_get_msi_desc(irq));
+	struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
+	struct iommu_dma_cookie *cookie;
+	struct iommu_dma_msi_page *msi_page;
+	phys_addr_t msi_addr = (u64)msg->address_hi << 32 | msg->address_lo;
+	unsigned long flags;
+
+	if (!domain || !domain->iova_cookie)
+		return;
+
+	cookie = domain->iova_cookie;
+
+	/*
+	 * We disable IRQs to rule out a possible inversion against
+	 * irq_desc_lock if, say, someone tries to retarget the affinity
+	 * of an MSI from within an IPI handler.
+	 */
+	spin_lock_irqsave(&cookie->msi_lock, flags);
+	msi_page = iommu_dma_get_msi_page(dev, msi_addr, domain);
+	spin_unlock_irqrestore(&cookie->msi_lock, flags);
+
+	if (WARN_ON(!msi_page)) {
+		/*
+		 * We're called from a void callback, so the best we can do is
+		 * 'fail' by filling the message with obviously bogus values.
+		 * Since we got this far due to an IOMMU being present, it's
+		 * not like the existing address would have worked anyway...
+		 */
+		msg->address_hi = ~0U;
+		msg->address_lo = ~0U;
+		msg->data = ~0U;
+	} else {
+		msg->address_hi = upper_32_bits(msi_page->iova);
+		msg->address_lo &= iova_mask(&cookie->iovad);
+		msg->address_lo += lower_32_bits(msi_page->iova);
+	}
+}
diff --git a/drivers/irqchip/irq-gic-v2m.c b/drivers/irqchip/irq-gic-v2m.c
index 35eb7ac5d21f..863e073c6f7f 100644
--- a/drivers/irqchip/irq-gic-v2m.c
+++ b/drivers/irqchip/irq-gic-v2m.c
@@ -16,6 +16,7 @@
 #define pr_fmt(fmt) "GICv2m: " fmt
 
 #include <linux/acpi.h>
+#include <linux/dma-iommu.h>
 #include <linux/irq.h>
 #include <linux/irqdomain.h>
 #include <linux/kernel.h>
@@ -108,6 +109,8 @@ static void gicv2m_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
 
 	if (v2m->flags & GICV2M_NEEDS_SPI_OFFSET)
 		msg->data -= v2m->spi_offset;
+
+	iommu_dma_map_msi_msg(data->irq, msg);
 }
 
 static struct irq_chip gicv2m_irq_chip = {
diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c
index 36b9c28a5c91..98ff669d5962 100644
--- a/drivers/irqchip/irq-gic-v3-its.c
+++ b/drivers/irqchip/irq-gic-v3-its.c
@@ -18,6 +18,7 @@
 #include <linux/bitmap.h>
 #include <linux/cpu.h>
 #include <linux/delay.h>
+#include <linux/dma-iommu.h>
 #include <linux/interrupt.h>
 #include <linux/log2.h>
 #include <linux/mm.h>
@@ -655,6 +656,8 @@ static void its_irq_compose_msi_msg(struct irq_data *d, struct msi_msg *msg)
 	msg->address_lo		= addr & ((1UL << 32) - 1);
 	msg->address_hi		= addr >> 32;
 	msg->data		= its_get_event_id(d);
+
+	iommu_dma_map_msi_msg(d->irq, msg);
 }
 
 static struct irq_chip its_irq_chip = {
diff --git a/include/linux/dma-iommu.h b/include/linux/dma-iommu.h
index 81c5c8d167ad..5ee806e41b5c 100644
--- a/include/linux/dma-iommu.h
+++ b/include/linux/dma-iommu.h
@@ -21,6 +21,7 @@
 
 #ifdef CONFIG_IOMMU_DMA
 #include <linux/iommu.h>
+#include <linux/msi.h>
 
 int iommu_dma_init(void);
 
@@ -62,9 +63,13 @@ void iommu_dma_unmap_sg(struct device *dev, struct scatterlist *sg, int nents,
 int iommu_dma_supported(struct device *dev, u64 mask);
 int iommu_dma_mapping_error(struct device *dev, dma_addr_t dma_addr);
 
+/* The DMA API isn't _quite_ the whole story, though... */
+void iommu_dma_map_msi_msg(int irq, struct msi_msg *msg);
+
 #else
 
 struct iommu_domain;
+struct msi_msg;
 
 static inline int iommu_dma_init(void)
 {
@@ -80,6 +85,10 @@ static inline void iommu_put_dma_cookie(struct iommu_domain *domain)
 {
 }
 
+static inline void iommu_dma_map_msi_msg(int irq, struct msi_msg *msg)
+{
+}
+
 #endif	/* CONFIG_IOMMU_DMA */
 #endif	/* __KERNEL__ */
 #endif	/* __DMA_IOMMU_H */
-- 
2.8.1.dirty

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

* [PATCH v7 22/22] iommu/dma: Avoid PCI host bridge windows
  2016-09-12 16:13 ` Robin Murphy
@ 2016-09-12 16:14     ` Robin Murphy
  -1 siblings, 0 replies; 104+ messages in thread
From: Robin Murphy @ 2016-09-12 16:14 UTC (permalink / raw)
  To: will.deacon-5wv7dgnIgG8, joro-zLv9SwRftAIdnm+yROfE0A,
	iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, lorenzo.pieralisi-5wv7dgnIgG8,
	jean-philippe.brucker-5wv7dgnIgG8, punit.agrawal-5wv7dgnIgG8,
	thunder.leizhen-hv44wF8Li93QT0dZR+AlfA,
	eric.auger-H+wXaHxf7aLQT0dZR+AlfA, Marek Szyprowski, Inki Dae

With our DMA ops enabled for PCI devices, we should avoid allocating
IOVAs which a host bridge might misinterpret as peer-to-peer DMA and
lead to faults, corruption or other badness. To be safe, punch out holes
for all of the relevant host bridge's windows when initialising a DMA
domain for a PCI device.

CC: Marek Szyprowski <m.szyprowski-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
CC: Inki Dae <inki.dae-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
Reported-by: Lorenzo Pieralisi <lorenzo.pieralisi-5wv7dgnIgG8@public.gmane.org>
Signed-off-by: Robin Murphy <robin.murphy-5wv7dgnIgG8@public.gmane.org>

---

- Squash in the previous drm/exynos fixup
- If need be, this one can probably wait
---
 arch/arm64/mm/dma-mapping.c               |  2 +-
 drivers/gpu/drm/exynos/exynos_drm_iommu.h |  2 +-
 drivers/iommu/dma-iommu.c                 | 25 ++++++++++++++++++++++++-
 include/linux/dma-iommu.h                 |  3 ++-
 4 files changed, 28 insertions(+), 4 deletions(-)

diff --git a/arch/arm64/mm/dma-mapping.c b/arch/arm64/mm/dma-mapping.c
index c4284c432ae8..610d8e53011e 100644
--- a/arch/arm64/mm/dma-mapping.c
+++ b/arch/arm64/mm/dma-mapping.c
@@ -827,7 +827,7 @@ static bool do_iommu_attach(struct device *dev, const struct iommu_ops *ops,
 	 * then the IOMMU core will have already configured a group for this
 	 * device, and allocated the default domain for that group.
 	 */
-	if (!domain || iommu_dma_init_domain(domain, dma_base, size)) {
+	if (!domain || iommu_dma_init_domain(domain, dma_base, size, dev)) {
 		pr_warn("Failed to set up IOMMU for device %s; retaining platform DMA ops\n",
 			dev_name(dev));
 		return false;
diff --git a/drivers/gpu/drm/exynos/exynos_drm_iommu.h b/drivers/gpu/drm/exynos/exynos_drm_iommu.h
index c8de4913fdbe..87f6b5672e11 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_iommu.h
+++ b/drivers/gpu/drm/exynos/exynos_drm_iommu.h
@@ -66,7 +66,7 @@ static inline int __exynos_iommu_create_mapping(struct exynos_drm_private *priv,
 	if (ret)
 		goto free_domain;
 
-	ret = iommu_dma_init_domain(domain, start, size);
+	ret = iommu_dma_init_domain(domain, start, size, NULL);
 	if (ret)
 		goto put_cookie;
 
diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index 4329d18080cf..c5ab8667e6f2 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -27,6 +27,7 @@
 #include <linux/iova.h>
 #include <linux/irq.h>
 #include <linux/mm.h>
+#include <linux/pci.h>
 #include <linux/scatterlist.h>
 #include <linux/vmalloc.h>
 
@@ -103,18 +104,38 @@ void iommu_put_dma_cookie(struct iommu_domain *domain)
 }
 EXPORT_SYMBOL(iommu_put_dma_cookie);
 
+static void iova_reserve_pci_windows(struct pci_dev *dev,
+		struct iova_domain *iovad)
+{
+	struct pci_host_bridge *bridge = pci_find_host_bridge(dev->bus);
+	struct resource_entry *window;
+	unsigned long lo, hi;
+
+	resource_list_for_each_entry(window, &bridge->windows) {
+		if (resource_type(window->res) != IORESOURCE_MEM &&
+		    resource_type(window->res) != IORESOURCE_IO)
+			continue;
+
+		lo = iova_pfn(iovad, window->res->start - window->offset);
+		hi = iova_pfn(iovad, window->res->end - window->offset);
+		reserve_iova(iovad, lo, hi);
+	}
+}
+
 /**
  * iommu_dma_init_domain - Initialise a DMA mapping domain
  * @domain: IOMMU domain previously prepared by iommu_get_dma_cookie()
  * @base: IOVA at which the mappable address space starts
  * @size: Size of IOVA space
+ * @dev: Device the domain is being initialised for
  *
  * @base and @size should be exact multiples of IOMMU page granularity to
  * avoid rounding surprises. If necessary, we reserve the page at address 0
  * to ensure it is an invalid IOVA. It is safe to reinitialise a domain, but
  * any change which could make prior IOVAs invalid will fail.
  */
-int iommu_dma_init_domain(struct iommu_domain *domain, dma_addr_t base, u64 size)
+int iommu_dma_init_domain(struct iommu_domain *domain, dma_addr_t base,
+		u64 size, struct device *dev)
 {
 	struct iova_domain *iovad = cookie_iovad(domain);
 	unsigned long order, base_pfn, end_pfn;
@@ -152,6 +173,8 @@ int iommu_dma_init_domain(struct iommu_domain *domain, dma_addr_t base, u64 size
 		iovad->dma_32bit_pfn = end_pfn;
 	} else {
 		init_iova_domain(iovad, 1UL << order, base_pfn, end_pfn);
+		if (dev && dev_is_pci(dev))
+			iova_reserve_pci_windows(to_pci_dev(dev), iovad);
 	}
 	return 0;
 }
diff --git a/include/linux/dma-iommu.h b/include/linux/dma-iommu.h
index 5ee806e41b5c..32c589062bd9 100644
--- a/include/linux/dma-iommu.h
+++ b/include/linux/dma-iommu.h
@@ -30,7 +30,8 @@ int iommu_get_dma_cookie(struct iommu_domain *domain);
 void iommu_put_dma_cookie(struct iommu_domain *domain);
 
 /* Setup call for arch DMA mapping code */
-int iommu_dma_init_domain(struct iommu_domain *domain, dma_addr_t base, u64 size);
+int iommu_dma_init_domain(struct iommu_domain *domain, dma_addr_t base,
+		u64 size, struct device *dev);
 
 /* General helpers for DMA-API <-> IOMMU-API interaction */
 int dma_direction_to_prot(enum dma_data_direction dir, bool coherent);
-- 
2.8.1.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] 104+ messages in thread

* [PATCH v7 22/22] iommu/dma: Avoid PCI host bridge windows
@ 2016-09-12 16:14     ` Robin Murphy
  0 siblings, 0 replies; 104+ messages in thread
From: Robin Murphy @ 2016-09-12 16:14 UTC (permalink / raw)
  To: linux-arm-kernel

With our DMA ops enabled for PCI devices, we should avoid allocating
IOVAs which a host bridge might misinterpret as peer-to-peer DMA and
lead to faults, corruption or other badness. To be safe, punch out holes
for all of the relevant host bridge's windows when initialising a DMA
domain for a PCI device.

CC: Marek Szyprowski <m.szyprowski@samsung.com>
CC: Inki Dae <inki.dae@samsung.com>
Reported-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Signed-off-by: Robin Murphy <robin.murphy@arm.com>

---

- Squash in the previous drm/exynos fixup
- If need be, this one can probably wait
---
 arch/arm64/mm/dma-mapping.c               |  2 +-
 drivers/gpu/drm/exynos/exynos_drm_iommu.h |  2 +-
 drivers/iommu/dma-iommu.c                 | 25 ++++++++++++++++++++++++-
 include/linux/dma-iommu.h                 |  3 ++-
 4 files changed, 28 insertions(+), 4 deletions(-)

diff --git a/arch/arm64/mm/dma-mapping.c b/arch/arm64/mm/dma-mapping.c
index c4284c432ae8..610d8e53011e 100644
--- a/arch/arm64/mm/dma-mapping.c
+++ b/arch/arm64/mm/dma-mapping.c
@@ -827,7 +827,7 @@ static bool do_iommu_attach(struct device *dev, const struct iommu_ops *ops,
 	 * then the IOMMU core will have already configured a group for this
 	 * device, and allocated the default domain for that group.
 	 */
-	if (!domain || iommu_dma_init_domain(domain, dma_base, size)) {
+	if (!domain || iommu_dma_init_domain(domain, dma_base, size, dev)) {
 		pr_warn("Failed to set up IOMMU for device %s; retaining platform DMA ops\n",
 			dev_name(dev));
 		return false;
diff --git a/drivers/gpu/drm/exynos/exynos_drm_iommu.h b/drivers/gpu/drm/exynos/exynos_drm_iommu.h
index c8de4913fdbe..87f6b5672e11 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_iommu.h
+++ b/drivers/gpu/drm/exynos/exynos_drm_iommu.h
@@ -66,7 +66,7 @@ static inline int __exynos_iommu_create_mapping(struct exynos_drm_private *priv,
 	if (ret)
 		goto free_domain;
 
-	ret = iommu_dma_init_domain(domain, start, size);
+	ret = iommu_dma_init_domain(domain, start, size, NULL);
 	if (ret)
 		goto put_cookie;
 
diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index 4329d18080cf..c5ab8667e6f2 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -27,6 +27,7 @@
 #include <linux/iova.h>
 #include <linux/irq.h>
 #include <linux/mm.h>
+#include <linux/pci.h>
 #include <linux/scatterlist.h>
 #include <linux/vmalloc.h>
 
@@ -103,18 +104,38 @@ void iommu_put_dma_cookie(struct iommu_domain *domain)
 }
 EXPORT_SYMBOL(iommu_put_dma_cookie);
 
+static void iova_reserve_pci_windows(struct pci_dev *dev,
+		struct iova_domain *iovad)
+{
+	struct pci_host_bridge *bridge = pci_find_host_bridge(dev->bus);
+	struct resource_entry *window;
+	unsigned long lo, hi;
+
+	resource_list_for_each_entry(window, &bridge->windows) {
+		if (resource_type(window->res) != IORESOURCE_MEM &&
+		    resource_type(window->res) != IORESOURCE_IO)
+			continue;
+
+		lo = iova_pfn(iovad, window->res->start - window->offset);
+		hi = iova_pfn(iovad, window->res->end - window->offset);
+		reserve_iova(iovad, lo, hi);
+	}
+}
+
 /**
  * iommu_dma_init_domain - Initialise a DMA mapping domain
  * @domain: IOMMU domain previously prepared by iommu_get_dma_cookie()
  * @base: IOVA at which the mappable address space starts
  * @size: Size of IOVA space
+ * @dev: Device the domain is being initialised for
  *
  * @base and @size should be exact multiples of IOMMU page granularity to
  * avoid rounding surprises. If necessary, we reserve the page at address 0
  * to ensure it is an invalid IOVA. It is safe to reinitialise a domain, but
  * any change which could make prior IOVAs invalid will fail.
  */
-int iommu_dma_init_domain(struct iommu_domain *domain, dma_addr_t base, u64 size)
+int iommu_dma_init_domain(struct iommu_domain *domain, dma_addr_t base,
+		u64 size, struct device *dev)
 {
 	struct iova_domain *iovad = cookie_iovad(domain);
 	unsigned long order, base_pfn, end_pfn;
@@ -152,6 +173,8 @@ int iommu_dma_init_domain(struct iommu_domain *domain, dma_addr_t base, u64 size
 		iovad->dma_32bit_pfn = end_pfn;
 	} else {
 		init_iova_domain(iovad, 1UL << order, base_pfn, end_pfn);
+		if (dev && dev_is_pci(dev))
+			iova_reserve_pci_windows(to_pci_dev(dev), iovad);
 	}
 	return 0;
 }
diff --git a/include/linux/dma-iommu.h b/include/linux/dma-iommu.h
index 5ee806e41b5c..32c589062bd9 100644
--- a/include/linux/dma-iommu.h
+++ b/include/linux/dma-iommu.h
@@ -30,7 +30,8 @@ int iommu_get_dma_cookie(struct iommu_domain *domain);
 void iommu_put_dma_cookie(struct iommu_domain *domain);
 
 /* Setup call for arch DMA mapping code */
-int iommu_dma_init_domain(struct iommu_domain *domain, dma_addr_t base, u64 size);
+int iommu_dma_init_domain(struct iommu_domain *domain, dma_addr_t base,
+		u64 size, struct device *dev);
 
 /* General helpers for DMA-API <-> IOMMU-API interaction */
 int dma_direction_to_prot(enum dma_data_direction dir, bool coherent);
-- 
2.8.1.dirty

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

* [PATCH v7.1 04/22] iommu: Introduce iommu_fwspec
  2016-09-12 16:13     ` Robin Murphy
@ 2016-09-13  9:54         ` Robin Murphy
  -1 siblings, 0 replies; 104+ messages in thread
From: Robin Murphy @ 2016-09-13  9:54 UTC (permalink / raw)
  To: will.deacon-5wv7dgnIgG8, joro-zLv9SwRftAIdnm+yROfE0A,
	iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, Greg Kroah-Hartman,
	punit.agrawal-5wv7dgnIgG8,
	thunder.leizhen-hv44wF8Li93QT0dZR+AlfA

Introduce a common structure to hold the per-device firmware data that
most IOMMU drivers need to keep track of. This enables us to configure
much of that data from common firmware code, and consolidate a lot of
the equivalent implementations, device look-up tables, etc. which are
currently strewn across IOMMU drivers.

This will also be enable us to address the outstanding "multiple IOMMUs
on the platform bus" problem by tweaking IOMMU API calls to prefer
dev->fwspec->ops before falling back to dev->bus->iommu_ops, and thus
gracefully handle those troublesome systems which we currently cannot.

As the first user, hook up the OF IOMMU configuration mechanism. The
driver-defined nature of DT cells means that we still need the drivers
to translate and add the IDs themselves, but future users such as the
much less free-form ACPI IORT will be much simpler and self-contained.

CC: Greg Kroah-Hartman <gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r@public.gmane.org>
Suggested-by: Will Deacon <will.deacon-5wv7dgnIgG8@public.gmane.org>
Signed-off-by: Robin Murphy <robin.murphy-5wv7dgnIgG8@public.gmane.org>

---

- Drop the 'gradual introduction' fiddling and go straight to struct
  device and common code, as it prevents all the silly build issues
  and ultimately makes life simpler for everyone
- This time without missing "static inline"s on the stubs...

---
 drivers/iommu/iommu.c    | 58 ++++++++++++++++++++++++++++++++++++++++++++++++
 drivers/iommu/of_iommu.c |  8 +++++--
 include/linux/device.h   |  3 +++
 include/linux/iommu.h    | 39 ++++++++++++++++++++++++++++++++
 4 files changed, 106 insertions(+), 2 deletions(-)

diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index b06d93594436..9a2f1960873b 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -31,6 +31,7 @@
 #include <linux/err.h>
 #include <linux/pci.h>
 #include <linux/bitops.h>
+#include <linux/property.h>
 #include <trace/events/iommu.h>
 
 static struct kset *iommu_group_kset;
@@ -1613,3 +1614,60 @@ out:
 
 	return ret;
 }
+
+int iommu_fwspec_init(struct device *dev, struct fwnode_handle *iommu_fwnode,
+		      const struct iommu_ops *ops)
+{
+	struct iommu_fwspec *fwspec = dev->iommu_fwspec;
+
+	if (fwspec)
+		return ops == fwspec->ops ? 0 : -EINVAL;
+
+	fwspec = kzalloc(sizeof(*fwspec), GFP_KERNEL);
+	if (!fwspec)
+		return -ENOMEM;
+
+	of_node_get(to_of_node(iommu_fwnode));
+	fwspec->iommu_fwnode = iommu_fwnode;
+	fwspec->ops = ops;
+	dev->iommu_fwspec = fwspec;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(iommu_fwspec_init);
+
+void iommu_fwspec_free(struct device *dev)
+{
+	struct iommu_fwspec *fwspec = dev->iommu_fwspec;
+
+	if (fwspec) {
+		fwnode_handle_put(fwspec->iommu_fwnode);
+		kfree(fwspec);
+		dev->iommu_fwspec = NULL;
+	}
+}
+EXPORT_SYMBOL_GPL(iommu_fwspec_free);
+
+int iommu_fwspec_add_ids(struct device *dev, u32 *ids, int num_ids)
+{
+	struct iommu_fwspec *fwspec = dev->iommu_fwspec;
+	size_t size;
+	int i;
+
+	if (!fwspec)
+		return -EINVAL;
+
+	size = offsetof(struct iommu_fwspec, ids[fwspec->num_ids + num_ids]);
+	if (size > sizeof(*fwspec)) {
+		fwspec = krealloc(dev->iommu_fwspec, size, GFP_KERNEL);
+		if (!fwspec)
+			return -ENOMEM;
+	}
+
+	for (i = 0; i < num_ids; i++)
+		fwspec->ids[fwspec->num_ids + i] = ids[i];
+
+	fwspec->num_ids += num_ids;
+	dev->iommu_fwspec = fwspec;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(iommu_fwspec_add_ids);
diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c
index 19e1e8f2f871..5b82862f571f 100644
--- a/drivers/iommu/of_iommu.c
+++ b/drivers/iommu/of_iommu.c
@@ -167,7 +167,9 @@ static const struct iommu_ops
 		return NULL;
 
 	ops = of_iommu_get_ops(iommu_spec.np);
-	if (!ops || !ops->of_xlate || ops->of_xlate(&pdev->dev, &iommu_spec))
+	if (!ops || !ops->of_xlate ||
+	    iommu_fwspec_init(&pdev->dev, &iommu_spec.np->fwnode, ops) ||
+	    ops->of_xlate(&pdev->dev, &iommu_spec))
 		ops = NULL;
 
 	of_node_put(iommu_spec.np);
@@ -196,7 +198,9 @@ const struct iommu_ops *of_iommu_configure(struct device *dev,
 		np = iommu_spec.np;
 		ops = of_iommu_get_ops(np);
 
-		if (!ops || !ops->of_xlate || ops->of_xlate(dev, &iommu_spec))
+		if (!ops || !ops->of_xlate ||
+		    iommu_fwspec_init(dev, &np->fwnode, ops) ||
+		    ops->of_xlate(dev, &iommu_spec))
 			goto err_put_node;
 
 		of_node_put(np);
diff --git a/include/linux/device.h b/include/linux/device.h
index 38f02814d53a..bc41e87a969b 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -41,6 +41,7 @@ struct device_node;
 struct fwnode_handle;
 struct iommu_ops;
 struct iommu_group;
+struct iommu_fwspec;
 
 struct bus_attribute {
 	struct attribute	attr;
@@ -765,6 +766,7 @@ struct device_dma_parameters {
  * 		gone away. This should be set by the allocator of the
  * 		device (i.e. the bus driver that discovered the device).
  * @iommu_group: IOMMU group the device belongs to.
+ * @iommu_fwspec: IOMMU-specific properties supplied by firmware.
  *
  * @offline_disabled: If set, the device is permanently online.
  * @offline:	Set after successful invocation of bus type's .offline().
@@ -849,6 +851,7 @@ struct device {
 
 	void	(*release)(struct device *dev);
 	struct iommu_group	*iommu_group;
+	struct iommu_fwspec	*iommu_fwspec;
 
 	bool			offline_disabled:1;
 	bool			offline:1;
diff --git a/include/linux/iommu.h b/include/linux/iommu.h
index a35fb8b42e1a..436dc21318af 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -331,10 +331,32 @@ extern struct iommu_group *pci_device_group(struct device *dev);
 /* Generic device grouping function */
 extern struct iommu_group *generic_device_group(struct device *dev);
 
+/**
+ * struct iommu_fwspec - per-device IOMMU instance data
+ * @ops: ops for this device's IOMMU
+ * @iommu_fwnode: firmware handle for this device's IOMMU
+ * @iommu_priv: IOMMU driver private data for this device
+ * @num_ids: number of associated device IDs
+ * @ids: IDs which this device may present to the IOMMU
+ */
+struct iommu_fwspec {
+	const struct iommu_ops	*ops;
+	struct fwnode_handle	*iommu_fwnode;
+	void			*iommu_priv;
+	unsigned int		num_ids;
+	u32			ids[1];
+};
+
+int iommu_fwspec_init(struct device *dev, struct fwnode_handle *iommu_fwnode,
+		      const struct iommu_ops *ops);
+void iommu_fwspec_free(struct device *dev);
+int iommu_fwspec_add_ids(struct device *dev, u32 *ids, int num_ids);
+
 #else /* CONFIG_IOMMU_API */
 
 struct iommu_ops {};
 struct iommu_group {};
+struct iommu_fwspec {};
 
 static inline bool iommu_present(struct bus_type *bus)
 {
@@ -541,6 +563,23 @@ static inline void iommu_device_unlink(struct device *dev, struct device *link)
 {
 }
 
+static inline int iommu_fwspec_init(struct device *dev,
+				    struct fwnode_handle *iommu_fwnode,
+				    const struct iommu_ops *ops)
+{
+	return -ENODEV;
+}
+
+static inline void iommu_fwspec_free(struct device *dev)
+{
+}
+
+static inline int iommu_fwspec_add_ids(struct device *dev, u32 *ids,
+				       int num_ids)
+{
+	return -ENODEV;
+}
+
 #endif /* CONFIG_IOMMU_API */
 
 #endif /* __LINUX_IOMMU_H */
-- 
2.8.1.dirty

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

* [PATCH v7.1 04/22] iommu: Introduce iommu_fwspec
@ 2016-09-13  9:54         ` Robin Murphy
  0 siblings, 0 replies; 104+ messages in thread
From: Robin Murphy @ 2016-09-13  9:54 UTC (permalink / raw)
  To: linux-arm-kernel

Introduce a common structure to hold the per-device firmware data that
most IOMMU drivers need to keep track of. This enables us to configure
much of that data from common firmware code, and consolidate a lot of
the equivalent implementations, device look-up tables, etc. which are
currently strewn across IOMMU drivers.

This will also be enable us to address the outstanding "multiple IOMMUs
on the platform bus" problem by tweaking IOMMU API calls to prefer
dev->fwspec->ops before falling back to dev->bus->iommu_ops, and thus
gracefully handle those troublesome systems which we currently cannot.

As the first user, hook up the OF IOMMU configuration mechanism. The
driver-defined nature of DT cells means that we still need the drivers
to translate and add the IDs themselves, but future users such as the
much less free-form ACPI IORT will be much simpler and self-contained.

CC: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Suggested-by: Will Deacon <will.deacon@arm.com>
Signed-off-by: Robin Murphy <robin.murphy@arm.com>

---

- Drop the 'gradual introduction' fiddling and go straight to struct
  device and common code, as it prevents all the silly build issues
  and ultimately makes life simpler for everyone
- This time without missing "static inline"s on the stubs...

---
 drivers/iommu/iommu.c    | 58 ++++++++++++++++++++++++++++++++++++++++++++++++
 drivers/iommu/of_iommu.c |  8 +++++--
 include/linux/device.h   |  3 +++
 include/linux/iommu.h    | 39 ++++++++++++++++++++++++++++++++
 4 files changed, 106 insertions(+), 2 deletions(-)

diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index b06d93594436..9a2f1960873b 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -31,6 +31,7 @@
 #include <linux/err.h>
 #include <linux/pci.h>
 #include <linux/bitops.h>
+#include <linux/property.h>
 #include <trace/events/iommu.h>
 
 static struct kset *iommu_group_kset;
@@ -1613,3 +1614,60 @@ out:
 
 	return ret;
 }
+
+int iommu_fwspec_init(struct device *dev, struct fwnode_handle *iommu_fwnode,
+		      const struct iommu_ops *ops)
+{
+	struct iommu_fwspec *fwspec = dev->iommu_fwspec;
+
+	if (fwspec)
+		return ops == fwspec->ops ? 0 : -EINVAL;
+
+	fwspec = kzalloc(sizeof(*fwspec), GFP_KERNEL);
+	if (!fwspec)
+		return -ENOMEM;
+
+	of_node_get(to_of_node(iommu_fwnode));
+	fwspec->iommu_fwnode = iommu_fwnode;
+	fwspec->ops = ops;
+	dev->iommu_fwspec = fwspec;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(iommu_fwspec_init);
+
+void iommu_fwspec_free(struct device *dev)
+{
+	struct iommu_fwspec *fwspec = dev->iommu_fwspec;
+
+	if (fwspec) {
+		fwnode_handle_put(fwspec->iommu_fwnode);
+		kfree(fwspec);
+		dev->iommu_fwspec = NULL;
+	}
+}
+EXPORT_SYMBOL_GPL(iommu_fwspec_free);
+
+int iommu_fwspec_add_ids(struct device *dev, u32 *ids, int num_ids)
+{
+	struct iommu_fwspec *fwspec = dev->iommu_fwspec;
+	size_t size;
+	int i;
+
+	if (!fwspec)
+		return -EINVAL;
+
+	size = offsetof(struct iommu_fwspec, ids[fwspec->num_ids + num_ids]);
+	if (size > sizeof(*fwspec)) {
+		fwspec = krealloc(dev->iommu_fwspec, size, GFP_KERNEL);
+		if (!fwspec)
+			return -ENOMEM;
+	}
+
+	for (i = 0; i < num_ids; i++)
+		fwspec->ids[fwspec->num_ids + i] = ids[i];
+
+	fwspec->num_ids += num_ids;
+	dev->iommu_fwspec = fwspec;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(iommu_fwspec_add_ids);
diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c
index 19e1e8f2f871..5b82862f571f 100644
--- a/drivers/iommu/of_iommu.c
+++ b/drivers/iommu/of_iommu.c
@@ -167,7 +167,9 @@ static const struct iommu_ops
 		return NULL;
 
 	ops = of_iommu_get_ops(iommu_spec.np);
-	if (!ops || !ops->of_xlate || ops->of_xlate(&pdev->dev, &iommu_spec))
+	if (!ops || !ops->of_xlate ||
+	    iommu_fwspec_init(&pdev->dev, &iommu_spec.np->fwnode, ops) ||
+	    ops->of_xlate(&pdev->dev, &iommu_spec))
 		ops = NULL;
 
 	of_node_put(iommu_spec.np);
@@ -196,7 +198,9 @@ const struct iommu_ops *of_iommu_configure(struct device *dev,
 		np = iommu_spec.np;
 		ops = of_iommu_get_ops(np);
 
-		if (!ops || !ops->of_xlate || ops->of_xlate(dev, &iommu_spec))
+		if (!ops || !ops->of_xlate ||
+		    iommu_fwspec_init(dev, &np->fwnode, ops) ||
+		    ops->of_xlate(dev, &iommu_spec))
 			goto err_put_node;
 
 		of_node_put(np);
diff --git a/include/linux/device.h b/include/linux/device.h
index 38f02814d53a..bc41e87a969b 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -41,6 +41,7 @@ struct device_node;
 struct fwnode_handle;
 struct iommu_ops;
 struct iommu_group;
+struct iommu_fwspec;
 
 struct bus_attribute {
 	struct attribute	attr;
@@ -765,6 +766,7 @@ struct device_dma_parameters {
  * 		gone away. This should be set by the allocator of the
  * 		device (i.e. the bus driver that discovered the device).
  * @iommu_group: IOMMU group the device belongs to.
+ * @iommu_fwspec: IOMMU-specific properties supplied by firmware.
  *
  * @offline_disabled: If set, the device is permanently online.
  * @offline:	Set after successful invocation of bus type's .offline().
@@ -849,6 +851,7 @@ struct device {
 
 	void	(*release)(struct device *dev);
 	struct iommu_group	*iommu_group;
+	struct iommu_fwspec	*iommu_fwspec;
 
 	bool			offline_disabled:1;
 	bool			offline:1;
diff --git a/include/linux/iommu.h b/include/linux/iommu.h
index a35fb8b42e1a..436dc21318af 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -331,10 +331,32 @@ extern struct iommu_group *pci_device_group(struct device *dev);
 /* Generic device grouping function */
 extern struct iommu_group *generic_device_group(struct device *dev);
 
+/**
+ * struct iommu_fwspec - per-device IOMMU instance data
+ * @ops: ops for this device's IOMMU
+ * @iommu_fwnode: firmware handle for this device's IOMMU
+ * @iommu_priv: IOMMU driver private data for this device
+ * @num_ids: number of associated device IDs
+ * @ids: IDs which this device may present to the IOMMU
+ */
+struct iommu_fwspec {
+	const struct iommu_ops	*ops;
+	struct fwnode_handle	*iommu_fwnode;
+	void			*iommu_priv;
+	unsigned int		num_ids;
+	u32			ids[1];
+};
+
+int iommu_fwspec_init(struct device *dev, struct fwnode_handle *iommu_fwnode,
+		      const struct iommu_ops *ops);
+void iommu_fwspec_free(struct device *dev);
+int iommu_fwspec_add_ids(struct device *dev, u32 *ids, int num_ids);
+
 #else /* CONFIG_IOMMU_API */
 
 struct iommu_ops {};
 struct iommu_group {};
+struct iommu_fwspec {};
 
 static inline bool iommu_present(struct bus_type *bus)
 {
@@ -541,6 +563,23 @@ static inline void iommu_device_unlink(struct device *dev, struct device *link)
 {
 }
 
+static inline int iommu_fwspec_init(struct device *dev,
+				    struct fwnode_handle *iommu_fwnode,
+				    const struct iommu_ops *ops)
+{
+	return -ENODEV;
+}
+
+static inline void iommu_fwspec_free(struct device *dev)
+{
+}
+
+static inline int iommu_fwspec_add_ids(struct device *dev, u32 *ids,
+				       int num_ids)
+{
+	return -ENODEV;
+}
+
 #endif /* CONFIG_IOMMU_API */
 
 #endif /* __LINUX_IOMMU_H */
-- 
2.8.1.dirty

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

* Re: [PATCH v7 00/22] Generic DT bindings for PCI IOMMUs and ARM SMMU
  2016-09-12 16:13 ` Robin Murphy
@ 2016-09-13 12:14     ` Auger Eric
  -1 siblings, 0 replies; 104+ messages in thread
From: Auger Eric @ 2016-09-13 12:14 UTC (permalink / raw)
  To: Robin Murphy, will.deacon-5wv7dgnIgG8,
	joro-zLv9SwRftAIdnm+yROfE0A,
	iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, lorenzo.pieralisi-5wv7dgnIgG8,
	jean-philippe.brucker-5wv7dgnIgG8, punit.agrawal-5wv7dgnIgG8,
	thunder.leizhen-hv44wF8Li93QT0dZR+AlfA

[-- Attachment #1: Type: text/plain, Size: 3223 bytes --]

Hi Robin

On 12/09/2016 18:13, Robin Murphy wrote:
> Hi all,
> 
> To any more confusing fixups and crazily numbered extra patches, here's
> a quick v7 with everything rebased into the right order. The significant
> change this time is to implement iommu_fwspec properly from the start,
> which ends up being far simpler and more robust than faffing about
> introducing it somewhere 'less intrusive' to move toward core code later.
> 
> New branch in the logical place:
> 
> git://linux-arm.org/linux-rm iommu/generic-v7
I just tested your branch on AMD overdrive *without* updating the device
tree description according to the new syntax and I get a kernel oops.
See logs attached. Continuing my investigations ...

Best Regards

Eric
> 
> Robin.
> 
> Mark Rutland (1):
>   Docs: dt: add PCI IOMMU map bindings
> 
> Robin Murphy (21):
>   of/irq: Break out msi-map lookup (again)
>   iommu/of: Handle iommu-map property for PCI
>   iommu: Introduce iommu_fwspec
>   Docs: dt: document ARM SMMUv3 generic binding usage
>   iommu/arm-smmu: Fall back to global bypass
>   iommu/arm-smmu: Implement of_xlate() for SMMUv3
>   iommu/arm-smmu: Support non-PCI devices with SMMUv3
>   iommu/arm-smmu: Set PRIVCFG in stage 1 STEs
>   iommu/arm-smmu: Handle stream IDs more dynamically
>   iommu/arm-smmu: Consolidate stream map entry state
>   iommu/arm-smmu: Keep track of S2CR state
>   iommu/arm-smmu: Refactor mmu-masters handling
>   iommu/arm-smmu: Streamline SMMU data lookups
>   iommu/arm-smmu: Add a stream map entry iterator
>   iommu/arm-smmu: Intelligent SMR allocation
>   iommu/arm-smmu: Convert to iommu_fwspec
>   Docs: dt: document ARM SMMU generic binding usage
>   iommu/arm-smmu: Wire up generic configuration support
>   iommu/arm-smmu: Set domain geometry
>   iommu/dma: Add support for mapping MSIs
>   iommu/dma: Avoid PCI host bridge windows
> 
>  .../devicetree/bindings/iommu/arm,smmu-v3.txt      |   8 +-
>  .../devicetree/bindings/iommu/arm,smmu.txt         |  63 +-
>  .../devicetree/bindings/pci/pci-iommu.txt          | 171 ++++
>  arch/arm64/mm/dma-mapping.c                        |   2 +-
>  drivers/gpu/drm/exynos/exynos_drm_iommu.h          |   2 +-
>  drivers/iommu/Kconfig                              |   2 +-
>  drivers/iommu/arm-smmu-v3.c                        | 386 +++++----
>  drivers/iommu/arm-smmu.c                           | 962 ++++++++++-----------
>  drivers/iommu/dma-iommu.c                          | 161 +++-
>  drivers/iommu/iommu.c                              |  56 ++
>  drivers/iommu/of_iommu.c                           |  52 +-
>  drivers/irqchip/irq-gic-v2m.c                      |   3 +
>  drivers/irqchip/irq-gic-v3-its.c                   |   3 +
>  drivers/of/irq.c                                   |  78 +-
>  drivers/of/of_pci.c                                | 102 +++
>  include/linux/device.h                             |   3 +
>  include/linux/dma-iommu.h                          |  12 +-
>  include/linux/iommu.h                              |  38 +
>  include/linux/of_pci.h                             |  10 +
>  19 files changed, 1323 insertions(+), 791 deletions(-)
>  create mode 100644 Documentation/devicetree/bindings/pci/pci-iommu.txt
> 




[-- Attachment #2: robin2.txt --]
[-- Type: text/plain, Size: 9995 bytes --]

[    3.185447] CPU features: detected feature: 32-bit EL0 Support
[    3.185455] CPU: All CPU(s) started at EL2
[    3.185475] alternatives: patching kernel code
[    3.188549] devtmpfs: initialized
[    3.189484] SMBIOS 3.0.0 present.
[    3.189570] clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 7645041785100000 ns
[    3.189750] pinctrl core: initialized pinctrl subsystem
[    3.190113] NET: Registered protocol family 16
[    3.205134] cpuidle: using governor menu
[    3.205292] arm-smmu: deprecated "mmu-masters" DT property in use; DMA API support unavailable
[    3.205319] arm-smmu e0600000.smmu: probing hardware configuration...
[    3.205326] arm-smmu e0600000.smmu: SMMUv1 with:
[    3.205334] arm-smmu e0600000.smmu: 	stage 2 translation
[    3.205341] arm-smmu e0600000.smmu: 	non-coherent table walk
[    3.205348] arm-smmu e0600000.smmu: 	(IDR0.CTTW overridden by dma-coherent property)
[    3.205358] arm-smmu e0600000.smmu: 	stream matching with 32 register groups, mask 0x7fff
[    3.205369] arm-smmu e0600000.smmu: 	8 context banks (8 stage-2 only)
[    3.205377] arm-smmu e0600000.smmu: 	Supported page sizes: 0x60211000
[    3.205384] arm-smmu e0600000.smmu: 	Stage-2: 40-bit IPA -> 40-bit PA
[    3.205489] arm-smmu: deprecated "mmu-masters" DT property in use; DMA API support unavailable
[    3.205512] arm-smmu e0800000.smmu: probing hardware configuration...
[    3.205519] arm-smmu e0800000.smmu: SMMUv1 with:
[    3.205525] arm-smmu e0800000.smmu: 	stage 2 translation
[    3.205533] arm-smmu e0800000.smmu: 	non-coherent table walk
[    3.205539] arm-smmu e0800000.smmu: 	(IDR0.CTTW overridden by dma-coherent property)
[    3.205550] arm-smmu e0800000.smmu: 	stream matching with 32 register groups, mask 0x7fff
[    3.205560] arm-smmu e0800000.smmu: 	8 context banks (8 stage-2 only)
[    3.205567] arm-smmu e0800000.smmu: 	Supported page sizes: 0x60211000
[    3.205574] arm-smmu e0800000.smmu: 	Stage-2: 40-bit IPA -> 40-bit PA
[    3.205670] arm-smmu: deprecated "mmu-masters" DT property in use; DMA API support unavailable
[    3.205692] arm-smmu e0a00000.smmu: probing hardware configuration...
[    3.205699] arm-smmu e0a00000.smmu: SMMUv1 with:
[    3.205705] arm-smmu e0a00000.smmu: 	stage 2 translation
[    3.205712] arm-smmu e0a00000.smmu: 	non-coherent table walk
[    3.205719] arm-smmu e0a00000.smmu: 	(IDR0.CTTW overridden by dma-coherent property)
[    3.205729] arm-smmu e0a00000.smmu: 	stream matching with 32 register groups, mask 0x7fff
[    3.205738] arm-smmu e0a00000.smmu: 	8 context banks (8 stage-2 only)
[    3.205745] arm-smmu e0a00000.smmu: 	Supported page sizes: 0x60211000
[    3.205752] arm-smmu e0a00000.smmu: 	Stage-2: 40-bit IPA -> 40-bit PA
[    3.205801] vdso: 2 pages (1 code @ ffff0000088c6000, 1 data @ ffff000008dd4000)
[    3.205814] hw-breakpoint: found 6 breakpoint and 4 watchpoint registers.
[    3.206098] DMA: preallocated 256 KiB pool for atomic allocations
[    3.206186] Serial: AMBA PL011 UART driver
[    3.207040] e1010000.serial: ttyAMA0 at MMIO 0xe1010000 (irq = 20, base_baud = 0) is a PL011 rev3
[    3.995110] console [ttyAMA0] enabled
[    3.999198] OF: amba_device_add() failed (-19) for /smb/gpio@e0020000
[    4.006287] iommu: Adding device e0700000.xgmac to group 0
[    4.027943] HugeTLB registered 2 MB page size, pre-allocated 0 pages
[    4.034596] ACPI: Interpreter disabled.
[    4.038537] Unable to handle kernel paging request at virtual address ffffff8030010000
[    4.046452] pgd = ffff000008f61000
[    4.049845] [ffffff8030010000] *pgd=0000000000000000
[    4.054807] Internal error: Oops: 96000004 [#1] PREEMPT SMP
[    4.060371] Modules linked in:
[    4.063419] CPU: 3 PID: 1 Comm: swapper/0 Not tainted 4.8.0-rc4-26782-g83cf8c5 #14
[    4.070982] Hardware name: Default string Default string/Default string, BIOS WOD5527X 05/25/2015
[    4.079846] task: ffff8003ec108000 task.stack: ffff8003ec110000
[    4.085761] PC is at __arm_v7s_map+0x1cc/0x378
[    4.090196] LR is at __arm_v7s_map+0xb4/0x378
[    4.094543] pc : [<ffff000008522604>] lr : [<ffff0000085224ec>] pstate: 20000045
[    4.101931] sp : ffff8003ec113bf0
[    4.105236] x29: ffff8003ec113bf0 x28: 0000000000001000 
[    4.110543] x27: ffffff8030010000 x26: ffff8003eb47e518 
[    4.115850] x25: 0000000000000001 x24: ffffff8030010004 
[    4.121155] x23: 000000000000000c x22: ffff8003eb47e500 
[    4.126461] x21: 0000000000001000 x20: 0000000000000000 
[    4.131767] x19: ffffff8030010000 x18: 000000000000000e 
[    4.137072] x17: 00000000000040ff x16: 0000000000000000 
[    4.142379] x15: 000000000000008c x14: 0000000000000006 
[    4.147684] x13: 0000000000000007 x12: 0000000000000020 
[    4.152990] x11: 0000000000000003 x10: ffff000008dd6000 
[    4.158295] x9 : 0000000000000004 x8 : 0000000000000e71 
[    4.163601] x7 : ffffff8030010000 x6 : ffffff8030010000 
[    4.168907] x5 : 0000000000000002 x4 : 0000000000000000 
[    4.174212] x3 : 0000000000000001 x2 : 0000000000000001 
[    4.179518] x1 : 0000000000000e7d x0 : 0000000000000004 
[    4.184824] 
[    4.186306] Process swapper/0 (pid: 1, stack limit = 0xffff8003ec110020)
[    4.192998] Stack: (0xffff8003ec113bf0 to 0xffff8003ec114000)
[    4.198735] 3be0:                                   ffff8003ec113c70 ffff0000085224ec
[    4.206558] 3c00: 0000000000001000 0000000000000000 0000000000000000 ffff8003eb47e500
[    4.214381] 3c20: 0000000000000014 0000000000001000 0000000000000000 ffff8003eb47e518
[    4.222203] 3c40: ffff800030008000 0000000000001000 0000000000000000 ffff8003eb47e518
[    4.230026] 3c60: ffff8003ec113c70 ffffff8030010000 ffff8003ec113cf0 ffff0000085227f8
[    4.237848] 3c80: ffff8003eb47e568 0000000000001000 0000000000000000 000000000000002a
[    4.245671] 3ca0: 0000000000000001 0000000000001000 0000000000000001 ffff8003ec113d88
[    4.253493] 3cc0: ffff8003ec113d80 0000000000001000 0000000000000002 000000000000000f
[    4.261315] 3ce0: ffff800030010000 ffff000008bc2520 ffff8003ec113d20 ffff000008d0036c
[    4.269137] 3d00: ffff8003eb47e568 000000000000000c 0000000000000000 000000000000002a
[    4.276960] 3d20: ffff8003ec113dd0 ffff00000808335c ffff000008d0019c ffff8003ec110000
[    4.284782] 3d40: 0000000000000000 ffff000008f1c000 ffff000008cc0498 ffff000008cad0a0
[    4.292605] 3d60: ffff000008d2ab20 ffff000008f1c000 0000000000000109 0000000000000000
[    4.300427] 3d80: 0000000000000001 0000000001111000 0000002000000020 ffff000008e750b0
[    4.308249] 3da0: 0000000000000000 000000003000806a 4000400000000020 00000000800b8204
[    4.316072] 3dc0: 0000000000000000 0000000000000000 ffff8003ec113e40 ffff000008cc0d54
[    4.323894] 3de0: ffff000008d95df0 ffff000008d2aa70 0000000000000004 ffff000008f1c000
[    4.331717] 3e00: ffff8003ec113e00 ffff000008ef8f38 0000000000000000 0000000400000004
[    4.339539] 3e20: ffff000008ef8ec0 0000000000000000 ffff000008cc0498 ffff000008cad0a0
[    4.347361] 3e40: ffff8003ec113ea0 ffff0000088a7f88 ffff0000088a7f78 0000000000000000
[    4.355183] 3e60: 0000000000000000 0000000000000000 0000000000000000 0000000000000000
[    4.363006] 3e80: 0000000000000000 0000000000000000 0000000000000000 0000000000000000
[    4.370828] 3ea0: 0000000000000000 ffff000008082e90 ffff0000088a7f78 0000000000000000
[    4.378650] 3ec0: 0000000000000000 0000000000000000 0000000000000000 0000000000000000
[    4.386473] 3ee0: 0000000000000000 0000000000000000 0000000000000000 0000000000000000
[    4.394295] 3f00: 0000000000000000 0000000000000000 0000000000000000 0000000000000000
[    4.402117] 3f20: 0000000000000000 0000000000000000 0000000000000000 0000000000000000
[    4.409940] 3f40: 0000000000000000 0000000000000000 0000000000000000 0000000000000000
[    4.417762] 3f60: 0000000000000000 0000000000000000 0000000000000000 0000000000000000
[    4.425584] 3f80: 0000000000000000 0000000000000000 0000000000000000 0000000000000000
[    4.433406] 3fa0: 0000000000000000 0000000000000000 0000000000000000 0000000000000000
[    4.441229] 3fc0: 0000000000000000 0000000000000005 0000000000000000 0000000000000000
[    4.449051] 3fe0: 0000000000000000 0000000000000000 0000000000000000 0000000000000000
[    4.456873] Call trace:
[    4.459310] Exception stack(0xffff8003ec113a20 to 0xffff8003ec113b50)
[    4.465742] 3a20: ffffff8030010000 0001000000000000 ffff8003ec113bf0 ffff000008522604
[    4.473565] 3a40: 0000000002088021 ffff8003eb47e518 ffff8003fff4db00 ffff8003ffe81400
[    4.481388] 3a60: ffff000008dd5000 0208802100008001 0000000000000000 0000000000000000
[    4.489210] 3a80: ffff8003ec113b80 ffff00000820247c 0000000000000040 0000000000000000
[    4.497032] 3aa0: ffff8003ec110000 0000000000000003 0000000002088021 ffff000008521cbc
[    4.504854] 3ac0: 0000000000000004 0000000000000e7d 0000000000000001 0000000000000001
[    4.512677] 3ae0: 0000000000000000 0000000000000002 ffffff8030010000 ffffff8030010000
[    4.520499] 3b00: 0000000000000e71 0000000000000004 ffff000008dd6000 0000000000000003
[    4.528321] 3b20: 0000000000000020 0000000000000007 0000000000000006 000000000000008c
[    4.536143] 3b40: 0000000000000000 00000000000040ff
[    4.541012] [<ffff000008522604>] __arm_v7s_map+0x1cc/0x378
[    4.546489] [<ffff0000085224ec>] __arm_v7s_map+0xb4/0x378
[    4.551879] [<ffff0000085227f8>] arm_v7s_map+0x48/0xc4
[    4.557010] [<ffff000008d0036c>] arm_v7s_do_selftests+0x1d0/0x4b4
[    4.563096] [<ffff00000808335c>] do_one_initcall+0x38/0x12c
[    4.568662] [<ffff000008cc0d54>] kernel_init_freeable+0x1a8/0x24c
[    4.574748] [<ffff0000088a7f88>] kernel_init+0x10/0xfc
[    4.579878] [<ffff000008082e90>] ret_from_fork+0x10/0x40
[    4.585181] Code: aa1b03f3 8b180378 f9003fa7 710004bf (b9400266) 
[    4.591280] ---[ end trace d091d1618c2826ea ]---
[    4.595897] Kernel panic - not syncing: Attempted to kill init! exitcode=0x0000000b
[    4.595897] 
[    4.605024] SMP: stopping secondary CPUs
[    4.608942] ---[ end Kernel panic - not syncing: Attempted to kill init! exitcode=0x0000000b
[    4.608942] 


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

* [PATCH v7 00/22] Generic DT bindings for PCI IOMMUs and ARM SMMU
@ 2016-09-13 12:14     ` Auger Eric
  0 siblings, 0 replies; 104+ messages in thread
From: Auger Eric @ 2016-09-13 12:14 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Robin

On 12/09/2016 18:13, Robin Murphy wrote:
> Hi all,
> 
> To any more confusing fixups and crazily numbered extra patches, here's
> a quick v7 with everything rebased into the right order. The significant
> change this time is to implement iommu_fwspec properly from the start,
> which ends up being far simpler and more robust than faffing about
> introducing it somewhere 'less intrusive' to move toward core code later.
> 
> New branch in the logical place:
> 
> git://linux-arm.org/linux-rm iommu/generic-v7
I just tested your branch on AMD overdrive *without* updating the device
tree description according to the new syntax and I get a kernel oops.
See logs attached. Continuing my investigations ...

Best Regards

Eric
> 
> Robin.
> 
> Mark Rutland (1):
>   Docs: dt: add PCI IOMMU map bindings
> 
> Robin Murphy (21):
>   of/irq: Break out msi-map lookup (again)
>   iommu/of: Handle iommu-map property for PCI
>   iommu: Introduce iommu_fwspec
>   Docs: dt: document ARM SMMUv3 generic binding usage
>   iommu/arm-smmu: Fall back to global bypass
>   iommu/arm-smmu: Implement of_xlate() for SMMUv3
>   iommu/arm-smmu: Support non-PCI devices with SMMUv3
>   iommu/arm-smmu: Set PRIVCFG in stage 1 STEs
>   iommu/arm-smmu: Handle stream IDs more dynamically
>   iommu/arm-smmu: Consolidate stream map entry state
>   iommu/arm-smmu: Keep track of S2CR state
>   iommu/arm-smmu: Refactor mmu-masters handling
>   iommu/arm-smmu: Streamline SMMU data lookups
>   iommu/arm-smmu: Add a stream map entry iterator
>   iommu/arm-smmu: Intelligent SMR allocation
>   iommu/arm-smmu: Convert to iommu_fwspec
>   Docs: dt: document ARM SMMU generic binding usage
>   iommu/arm-smmu: Wire up generic configuration support
>   iommu/arm-smmu: Set domain geometry
>   iommu/dma: Add support for mapping MSIs
>   iommu/dma: Avoid PCI host bridge windows
> 
>  .../devicetree/bindings/iommu/arm,smmu-v3.txt      |   8 +-
>  .../devicetree/bindings/iommu/arm,smmu.txt         |  63 +-
>  .../devicetree/bindings/pci/pci-iommu.txt          | 171 ++++
>  arch/arm64/mm/dma-mapping.c                        |   2 +-
>  drivers/gpu/drm/exynos/exynos_drm_iommu.h          |   2 +-
>  drivers/iommu/Kconfig                              |   2 +-
>  drivers/iommu/arm-smmu-v3.c                        | 386 +++++----
>  drivers/iommu/arm-smmu.c                           | 962 ++++++++++-----------
>  drivers/iommu/dma-iommu.c                          | 161 +++-
>  drivers/iommu/iommu.c                              |  56 ++
>  drivers/iommu/of_iommu.c                           |  52 +-
>  drivers/irqchip/irq-gic-v2m.c                      |   3 +
>  drivers/irqchip/irq-gic-v3-its.c                   |   3 +
>  drivers/of/irq.c                                   |  78 +-
>  drivers/of/of_pci.c                                | 102 +++
>  include/linux/device.h                             |   3 +
>  include/linux/dma-iommu.h                          |  12 +-
>  include/linux/iommu.h                              |  38 +
>  include/linux/of_pci.h                             |  10 +
>  19 files changed, 1323 insertions(+), 791 deletions(-)
>  create mode 100644 Documentation/devicetree/bindings/pci/pci-iommu.txt
> 



-------------- next part --------------
[    3.185447] CPU features: detected feature: 32-bit EL0 Support
[    3.185455] CPU: All CPU(s) started at EL2
[    3.185475] alternatives: patching kernel code
[    3.188549] devtmpfs: initialized
[    3.189484] SMBIOS 3.0.0 present.
[    3.189570] clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 7645041785100000 ns
[    3.189750] pinctrl core: initialized pinctrl subsystem
[    3.190113] NET: Registered protocol family 16
[    3.205134] cpuidle: using governor menu
[    3.205292] arm-smmu: deprecated "mmu-masters" DT property in use; DMA API support unavailable
[    3.205319] arm-smmu e0600000.smmu: probing hardware configuration...
[    3.205326] arm-smmu e0600000.smmu: SMMUv1 with:
[    3.205334] arm-smmu e0600000.smmu: 	stage 2 translation
[    3.205341] arm-smmu e0600000.smmu: 	non-coherent table walk
[    3.205348] arm-smmu e0600000.smmu: 	(IDR0.CTTW overridden by dma-coherent property)
[    3.205358] arm-smmu e0600000.smmu: 	stream matching with 32 register groups, mask 0x7fff
[    3.205369] arm-smmu e0600000.smmu: 	8 context banks (8 stage-2 only)
[    3.205377] arm-smmu e0600000.smmu: 	Supported page sizes: 0x60211000
[    3.205384] arm-smmu e0600000.smmu: 	Stage-2: 40-bit IPA -> 40-bit PA
[    3.205489] arm-smmu: deprecated "mmu-masters" DT property in use; DMA API support unavailable
[    3.205512] arm-smmu e0800000.smmu: probing hardware configuration...
[    3.205519] arm-smmu e0800000.smmu: SMMUv1 with:
[    3.205525] arm-smmu e0800000.smmu: 	stage 2 translation
[    3.205533] arm-smmu e0800000.smmu: 	non-coherent table walk
[    3.205539] arm-smmu e0800000.smmu: 	(IDR0.CTTW overridden by dma-coherent property)
[    3.205550] arm-smmu e0800000.smmu: 	stream matching with 32 register groups, mask 0x7fff
[    3.205560] arm-smmu e0800000.smmu: 	8 context banks (8 stage-2 only)
[    3.205567] arm-smmu e0800000.smmu: 	Supported page sizes: 0x60211000
[    3.205574] arm-smmu e0800000.smmu: 	Stage-2: 40-bit IPA -> 40-bit PA
[    3.205670] arm-smmu: deprecated "mmu-masters" DT property in use; DMA API support unavailable
[    3.205692] arm-smmu e0a00000.smmu: probing hardware configuration...
[    3.205699] arm-smmu e0a00000.smmu: SMMUv1 with:
[    3.205705] arm-smmu e0a00000.smmu: 	stage 2 translation
[    3.205712] arm-smmu e0a00000.smmu: 	non-coherent table walk
[    3.205719] arm-smmu e0a00000.smmu: 	(IDR0.CTTW overridden by dma-coherent property)
[    3.205729] arm-smmu e0a00000.smmu: 	stream matching with 32 register groups, mask 0x7fff
[    3.205738] arm-smmu e0a00000.smmu: 	8 context banks (8 stage-2 only)
[    3.205745] arm-smmu e0a00000.smmu: 	Supported page sizes: 0x60211000
[    3.205752] arm-smmu e0a00000.smmu: 	Stage-2: 40-bit IPA -> 40-bit PA
[    3.205801] vdso: 2 pages (1 code @ ffff0000088c6000, 1 data @ ffff000008dd4000)
[    3.205814] hw-breakpoint: found 6 breakpoint and 4 watchpoint registers.
[    3.206098] DMA: preallocated 256 KiB pool for atomic allocations
[    3.206186] Serial: AMBA PL011 UART driver
[    3.207040] e1010000.serial: ttyAMA0 at MMIO 0xe1010000 (irq = 20, base_baud = 0) is a PL011 rev3
[    3.995110] console [ttyAMA0] enabled
[    3.999198] OF: amba_device_add() failed (-19) for /smb/gpio at e0020000
[    4.006287] iommu: Adding device e0700000.xgmac to group 0
[    4.027943] HugeTLB registered 2 MB page size, pre-allocated 0 pages
[    4.034596] ACPI: Interpreter disabled.
[    4.038537] Unable to handle kernel paging request at virtual address ffffff8030010000
[    4.046452] pgd = ffff000008f61000
[    4.049845] [ffffff8030010000] *pgd=0000000000000000
[    4.054807] Internal error: Oops: 96000004 [#1] PREEMPT SMP
[    4.060371] Modules linked in:
[    4.063419] CPU: 3 PID: 1 Comm: swapper/0 Not tainted 4.8.0-rc4-26782-g83cf8c5 #14
[    4.070982] Hardware name: Default string Default string/Default string, BIOS WOD5527X 05/25/2015
[    4.079846] task: ffff8003ec108000 task.stack: ffff8003ec110000
[    4.085761] PC is at __arm_v7s_map+0x1cc/0x378
[    4.090196] LR is at __arm_v7s_map+0xb4/0x378
[    4.094543] pc : [<ffff000008522604>] lr : [<ffff0000085224ec>] pstate: 20000045
[    4.101931] sp : ffff8003ec113bf0
[    4.105236] x29: ffff8003ec113bf0 x28: 0000000000001000 
[    4.110543] x27: ffffff8030010000 x26: ffff8003eb47e518 
[    4.115850] x25: 0000000000000001 x24: ffffff8030010004 
[    4.121155] x23: 000000000000000c x22: ffff8003eb47e500 
[    4.126461] x21: 0000000000001000 x20: 0000000000000000 
[    4.131767] x19: ffffff8030010000 x18: 000000000000000e 
[    4.137072] x17: 00000000000040ff x16: 0000000000000000 
[    4.142379] x15: 000000000000008c x14: 0000000000000006 
[    4.147684] x13: 0000000000000007 x12: 0000000000000020 
[    4.152990] x11: 0000000000000003 x10: ffff000008dd6000 
[    4.158295] x9 : 0000000000000004 x8 : 0000000000000e71 
[    4.163601] x7 : ffffff8030010000 x6 : ffffff8030010000 
[    4.168907] x5 : 0000000000000002 x4 : 0000000000000000 
[    4.174212] x3 : 0000000000000001 x2 : 0000000000000001 
[    4.179518] x1 : 0000000000000e7d x0 : 0000000000000004 
[    4.184824] 
[    4.186306] Process swapper/0 (pid: 1, stack limit = 0xffff8003ec110020)
[    4.192998] Stack: (0xffff8003ec113bf0 to 0xffff8003ec114000)
[    4.198735] 3be0:                                   ffff8003ec113c70 ffff0000085224ec
[    4.206558] 3c00: 0000000000001000 0000000000000000 0000000000000000 ffff8003eb47e500
[    4.214381] 3c20: 0000000000000014 0000000000001000 0000000000000000 ffff8003eb47e518
[    4.222203] 3c40: ffff800030008000 0000000000001000 0000000000000000 ffff8003eb47e518
[    4.230026] 3c60: ffff8003ec113c70 ffffff8030010000 ffff8003ec113cf0 ffff0000085227f8
[    4.237848] 3c80: ffff8003eb47e568 0000000000001000 0000000000000000 000000000000002a
[    4.245671] 3ca0: 0000000000000001 0000000000001000 0000000000000001 ffff8003ec113d88
[    4.253493] 3cc0: ffff8003ec113d80 0000000000001000 0000000000000002 000000000000000f
[    4.261315] 3ce0: ffff800030010000 ffff000008bc2520 ffff8003ec113d20 ffff000008d0036c
[    4.269137] 3d00: ffff8003eb47e568 000000000000000c 0000000000000000 000000000000002a
[    4.276960] 3d20: ffff8003ec113dd0 ffff00000808335c ffff000008d0019c ffff8003ec110000
[    4.284782] 3d40: 0000000000000000 ffff000008f1c000 ffff000008cc0498 ffff000008cad0a0
[    4.292605] 3d60: ffff000008d2ab20 ffff000008f1c000 0000000000000109 0000000000000000
[    4.300427] 3d80: 0000000000000001 0000000001111000 0000002000000020 ffff000008e750b0
[    4.308249] 3da0: 0000000000000000 000000003000806a 4000400000000020 00000000800b8204
[    4.316072] 3dc0: 0000000000000000 0000000000000000 ffff8003ec113e40 ffff000008cc0d54
[    4.323894] 3de0: ffff000008d95df0 ffff000008d2aa70 0000000000000004 ffff000008f1c000
[    4.331717] 3e00: ffff8003ec113e00 ffff000008ef8f38 0000000000000000 0000000400000004
[    4.339539] 3e20: ffff000008ef8ec0 0000000000000000 ffff000008cc0498 ffff000008cad0a0
[    4.347361] 3e40: ffff8003ec113ea0 ffff0000088a7f88 ffff0000088a7f78 0000000000000000
[    4.355183] 3e60: 0000000000000000 0000000000000000 0000000000000000 0000000000000000
[    4.363006] 3e80: 0000000000000000 0000000000000000 0000000000000000 0000000000000000
[    4.370828] 3ea0: 0000000000000000 ffff000008082e90 ffff0000088a7f78 0000000000000000
[    4.378650] 3ec0: 0000000000000000 0000000000000000 0000000000000000 0000000000000000
[    4.386473] 3ee0: 0000000000000000 0000000000000000 0000000000000000 0000000000000000
[    4.394295] 3f00: 0000000000000000 0000000000000000 0000000000000000 0000000000000000
[    4.402117] 3f20: 0000000000000000 0000000000000000 0000000000000000 0000000000000000
[    4.409940] 3f40: 0000000000000000 0000000000000000 0000000000000000 0000000000000000
[    4.417762] 3f60: 0000000000000000 0000000000000000 0000000000000000 0000000000000000
[    4.425584] 3f80: 0000000000000000 0000000000000000 0000000000000000 0000000000000000
[    4.433406] 3fa0: 0000000000000000 0000000000000000 0000000000000000 0000000000000000
[    4.441229] 3fc0: 0000000000000000 0000000000000005 0000000000000000 0000000000000000
[    4.449051] 3fe0: 0000000000000000 0000000000000000 0000000000000000 0000000000000000
[    4.456873] Call trace:
[    4.459310] Exception stack(0xffff8003ec113a20 to 0xffff8003ec113b50)
[    4.465742] 3a20: ffffff8030010000 0001000000000000 ffff8003ec113bf0 ffff000008522604
[    4.473565] 3a40: 0000000002088021 ffff8003eb47e518 ffff8003fff4db00 ffff8003ffe81400
[    4.481388] 3a60: ffff000008dd5000 0208802100008001 0000000000000000 0000000000000000
[    4.489210] 3a80: ffff8003ec113b80 ffff00000820247c 0000000000000040 0000000000000000
[    4.497032] 3aa0: ffff8003ec110000 0000000000000003 0000000002088021 ffff000008521cbc
[    4.504854] 3ac0: 0000000000000004 0000000000000e7d 0000000000000001 0000000000000001
[    4.512677] 3ae0: 0000000000000000 0000000000000002 ffffff8030010000 ffffff8030010000
[    4.520499] 3b00: 0000000000000e71 0000000000000004 ffff000008dd6000 0000000000000003
[    4.528321] 3b20: 0000000000000020 0000000000000007 0000000000000006 000000000000008c
[    4.536143] 3b40: 0000000000000000 00000000000040ff
[    4.541012] [<ffff000008522604>] __arm_v7s_map+0x1cc/0x378
[    4.546489] [<ffff0000085224ec>] __arm_v7s_map+0xb4/0x378
[    4.551879] [<ffff0000085227f8>] arm_v7s_map+0x48/0xc4
[    4.557010] [<ffff000008d0036c>] arm_v7s_do_selftests+0x1d0/0x4b4
[    4.563096] [<ffff00000808335c>] do_one_initcall+0x38/0x12c
[    4.568662] [<ffff000008cc0d54>] kernel_init_freeable+0x1a8/0x24c
[    4.574748] [<ffff0000088a7f88>] kernel_init+0x10/0xfc
[    4.579878] [<ffff000008082e90>] ret_from_fork+0x10/0x40
[    4.585181] Code: aa1b03f3 8b180378 f9003fa7 710004bf (b9400266) 
[    4.591280] ---[ end trace d091d1618c2826ea ]---
[    4.595897] Kernel panic - not syncing: Attempted to kill init! exitcode=0x0000000b
[    4.595897] 
[    4.605024] SMP: stopping secondary CPUs
[    4.608942] ---[ end Kernel panic - not syncing: Attempted to kill init! exitcode=0x0000000b
[    4.608942] 

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

* Re: [PATCH v7 00/22] Generic DT bindings for PCI IOMMUs and ARM SMMU
  2016-09-13 12:14     ` Auger Eric
@ 2016-09-13 12:40         ` Robin Murphy
  -1 siblings, 0 replies; 104+ messages in thread
From: Robin Murphy @ 2016-09-13 12:40 UTC (permalink / raw)
  To: Auger Eric, will.deacon-5wv7dgnIgG8, joro-zLv9SwRftAIdnm+yROfE0A,
	iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, punit.agrawal-5wv7dgnIgG8,
	thunder.leizhen-hv44wF8Li93QT0dZR+AlfA

Hi Eric,

On 13/09/16 13:14, Auger Eric wrote:
> Hi Robin
> 
> On 12/09/2016 18:13, Robin Murphy wrote:
>> Hi all,
>>
>> To any more confusing fixups and crazily numbered extra patches, here's
>> a quick v7 with everything rebased into the right order. The significant
>> change this time is to implement iommu_fwspec properly from the start,
>> which ends up being far simpler and more robust than faffing about
>> introducing it somewhere 'less intrusive' to move toward core code later.
>>
>> New branch in the logical place:
>>
>> git://linux-arm.org/linux-rm iommu/generic-v7
> I just tested your branch on AMD overdrive *without* updating the device
> tree description according to the new syntax and I get a kernel oops.
> See logs attached. Continuing my investigations ...

Looking at that backtrace, it seems the offending commit is actually in
Will's devel branch _underneath_ this series; what's blowing up there is
the short-descriptor io-pgtable selftests, which you should be able to
reproduce on anything back to 4.6-rc1 with
CONFIG_IOMMU_IO_PGTABLE_ARMV7S_SELFTEST=y.

The short-descriptor code is never going to work on Seattle due to the
lack of 32-bit addressable memory - in normal use it would fail
gracefully because it couldn't allocate anything, but since the
selftests bypass the DMA API and corresponding checks, you end up with
nastiness happening via truncated addresses. A while back I did start
looking into generalising the selftests to remove all the "if
(!selftest_running)" special-casing; might be time to pick that up again.

Robin.

> 
> Best Regards
> 
> Eric
>>
>> Robin.
>>
>> Mark Rutland (1):
>>   Docs: dt: add PCI IOMMU map bindings
>>
>> Robin Murphy (21):
>>   of/irq: Break out msi-map lookup (again)
>>   iommu/of: Handle iommu-map property for PCI
>>   iommu: Introduce iommu_fwspec
>>   Docs: dt: document ARM SMMUv3 generic binding usage
>>   iommu/arm-smmu: Fall back to global bypass
>>   iommu/arm-smmu: Implement of_xlate() for SMMUv3
>>   iommu/arm-smmu: Support non-PCI devices with SMMUv3
>>   iommu/arm-smmu: Set PRIVCFG in stage 1 STEs
>>   iommu/arm-smmu: Handle stream IDs more dynamically
>>   iommu/arm-smmu: Consolidate stream map entry state
>>   iommu/arm-smmu: Keep track of S2CR state
>>   iommu/arm-smmu: Refactor mmu-masters handling
>>   iommu/arm-smmu: Streamline SMMU data lookups
>>   iommu/arm-smmu: Add a stream map entry iterator
>>   iommu/arm-smmu: Intelligent SMR allocation
>>   iommu/arm-smmu: Convert to iommu_fwspec
>>   Docs: dt: document ARM SMMU generic binding usage
>>   iommu/arm-smmu: Wire up generic configuration support
>>   iommu/arm-smmu: Set domain geometry
>>   iommu/dma: Add support for mapping MSIs
>>   iommu/dma: Avoid PCI host bridge windows
>>
>>  .../devicetree/bindings/iommu/arm,smmu-v3.txt      |   8 +-
>>  .../devicetree/bindings/iommu/arm,smmu.txt         |  63 +-
>>  .../devicetree/bindings/pci/pci-iommu.txt          | 171 ++++
>>  arch/arm64/mm/dma-mapping.c                        |   2 +-
>>  drivers/gpu/drm/exynos/exynos_drm_iommu.h          |   2 +-
>>  drivers/iommu/Kconfig                              |   2 +-
>>  drivers/iommu/arm-smmu-v3.c                        | 386 +++++----
>>  drivers/iommu/arm-smmu.c                           | 962 ++++++++++-----------
>>  drivers/iommu/dma-iommu.c                          | 161 +++-
>>  drivers/iommu/iommu.c                              |  56 ++
>>  drivers/iommu/of_iommu.c                           |  52 +-
>>  drivers/irqchip/irq-gic-v2m.c                      |   3 +
>>  drivers/irqchip/irq-gic-v3-its.c                   |   3 +
>>  drivers/of/irq.c                                   |  78 +-
>>  drivers/of/of_pci.c                                | 102 +++
>>  include/linux/device.h                             |   3 +
>>  include/linux/dma-iommu.h                          |  12 +-
>>  include/linux/iommu.h                              |  38 +
>>  include/linux/of_pci.h                             |  10 +
>>  19 files changed, 1323 insertions(+), 791 deletions(-)
>>  create mode 100644 Documentation/devicetree/bindings/pci/pci-iommu.txt
>>
> 
> 
> 

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

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

Hi Eric,

On 13/09/16 13:14, Auger Eric wrote:
> Hi Robin
> 
> On 12/09/2016 18:13, Robin Murphy wrote:
>> Hi all,
>>
>> To any more confusing fixups and crazily numbered extra patches, here's
>> a quick v7 with everything rebased into the right order. The significant
>> change this time is to implement iommu_fwspec properly from the start,
>> which ends up being far simpler and more robust than faffing about
>> introducing it somewhere 'less intrusive' to move toward core code later.
>>
>> New branch in the logical place:
>>
>> git://linux-arm.org/linux-rm iommu/generic-v7
> I just tested your branch on AMD overdrive *without* updating the device
> tree description according to the new syntax and I get a kernel oops.
> See logs attached. Continuing my investigations ...

Looking at that backtrace, it seems the offending commit is actually in
Will's devel branch _underneath_ this series; what's blowing up there is
the short-descriptor io-pgtable selftests, which you should be able to
reproduce on anything back to 4.6-rc1 with
CONFIG_IOMMU_IO_PGTABLE_ARMV7S_SELFTEST=y.

The short-descriptor code is never going to work on Seattle due to the
lack of 32-bit addressable memory - in normal use it would fail
gracefully because it couldn't allocate anything, but since the
selftests bypass the DMA API and corresponding checks, you end up with
nastiness happening via truncated addresses. A while back I did start
looking into generalising the selftests to remove all the "if
(!selftest_running)" special-casing; might be time to pick that up again.

Robin.

> 
> Best Regards
> 
> Eric
>>
>> Robin.
>>
>> Mark Rutland (1):
>>   Docs: dt: add PCI IOMMU map bindings
>>
>> Robin Murphy (21):
>>   of/irq: Break out msi-map lookup (again)
>>   iommu/of: Handle iommu-map property for PCI
>>   iommu: Introduce iommu_fwspec
>>   Docs: dt: document ARM SMMUv3 generic binding usage
>>   iommu/arm-smmu: Fall back to global bypass
>>   iommu/arm-smmu: Implement of_xlate() for SMMUv3
>>   iommu/arm-smmu: Support non-PCI devices with SMMUv3
>>   iommu/arm-smmu: Set PRIVCFG in stage 1 STEs
>>   iommu/arm-smmu: Handle stream IDs more dynamically
>>   iommu/arm-smmu: Consolidate stream map entry state
>>   iommu/arm-smmu: Keep track of S2CR state
>>   iommu/arm-smmu: Refactor mmu-masters handling
>>   iommu/arm-smmu: Streamline SMMU data lookups
>>   iommu/arm-smmu: Add a stream map entry iterator
>>   iommu/arm-smmu: Intelligent SMR allocation
>>   iommu/arm-smmu: Convert to iommu_fwspec
>>   Docs: dt: document ARM SMMU generic binding usage
>>   iommu/arm-smmu: Wire up generic configuration support
>>   iommu/arm-smmu: Set domain geometry
>>   iommu/dma: Add support for mapping MSIs
>>   iommu/dma: Avoid PCI host bridge windows
>>
>>  .../devicetree/bindings/iommu/arm,smmu-v3.txt      |   8 +-
>>  .../devicetree/bindings/iommu/arm,smmu.txt         |  63 +-
>>  .../devicetree/bindings/pci/pci-iommu.txt          | 171 ++++
>>  arch/arm64/mm/dma-mapping.c                        |   2 +-
>>  drivers/gpu/drm/exynos/exynos_drm_iommu.h          |   2 +-
>>  drivers/iommu/Kconfig                              |   2 +-
>>  drivers/iommu/arm-smmu-v3.c                        | 386 +++++----
>>  drivers/iommu/arm-smmu.c                           | 962 ++++++++++-----------
>>  drivers/iommu/dma-iommu.c                          | 161 +++-
>>  drivers/iommu/iommu.c                              |  56 ++
>>  drivers/iommu/of_iommu.c                           |  52 +-
>>  drivers/irqchip/irq-gic-v2m.c                      |   3 +
>>  drivers/irqchip/irq-gic-v3-its.c                   |   3 +
>>  drivers/of/irq.c                                   |  78 +-
>>  drivers/of/of_pci.c                                | 102 +++
>>  include/linux/device.h                             |   3 +
>>  include/linux/dma-iommu.h                          |  12 +-
>>  include/linux/iommu.h                              |  38 +
>>  include/linux/of_pci.h                             |  10 +
>>  19 files changed, 1323 insertions(+), 791 deletions(-)
>>  create mode 100644 Documentation/devicetree/bindings/pci/pci-iommu.txt
>>
> 
> 
> 

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

* Re: [PATCH v7 00/22] Generic DT bindings for PCI IOMMUs and ARM SMMU
  2016-09-13 12:40         ` Robin Murphy
@ 2016-09-13 12:57             ` Auger Eric
  -1 siblings, 0 replies; 104+ messages in thread
From: Auger Eric @ 2016-09-13 12:57 UTC (permalink / raw)
  To: Robin Murphy, will.deacon-5wv7dgnIgG8,
	joro-zLv9SwRftAIdnm+yROfE0A,
	iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, lorenzo.pieralisi-5wv7dgnIgG8,
	jean-philippe.brucker-5wv7dgnIgG8, punit.agrawal-5wv7dgnIgG8,
	thunder.leizhen-hv44wF8Li93QT0dZR+AlfA

Hi Robin,

On 13/09/2016 14:40, Robin Murphy wrote:
> Hi Eric,
> 
> On 13/09/16 13:14, Auger Eric wrote:
>> Hi Robin
>>
>> On 12/09/2016 18:13, Robin Murphy wrote:
>>> Hi all,
>>>
>>> To any more confusing fixups and crazily numbered extra patches, here's
>>> a quick v7 with everything rebased into the right order. The significant
>>> change this time is to implement iommu_fwspec properly from the start,
>>> which ends up being far simpler and more robust than faffing about
>>> introducing it somewhere 'less intrusive' to move toward core code later.
>>>
>>> New branch in the logical place:
>>>
>>> git://linux-arm.org/linux-rm iommu/generic-v7
>> I just tested your branch on AMD overdrive *without* updating the device
>> tree description according to the new syntax and I get a kernel oops.
>> See logs attached. Continuing my investigations ...
> 
> Looking at that backtrace, it seems the offending commit is actually in
> Will's devel branch _underneath_ this series; what's blowing up there is
> the short-descriptor io-pgtable selftests, which you should be able to
> reproduce on anything back to 4.6-rc1 with
> CONFIG_IOMMU_IO_PGTABLE_ARMV7S_SELFTEST=y.
I confirm that when disabling the option, I don't get the oops anymore.

Thanks!

Eric
> 
> The short-descriptor code is never going to work on Seattle due to the
> lack of 32-bit addressable memory - in normal use it would fail
> gracefully because it couldn't allocate anything, but since the
> selftests bypass the DMA API and corresponding checks, you end up with
> nastiness happening via truncated addresses. A while back I did start
> looking into generalising the selftests to remove all the "if
> (!selftest_running)" special-casing; might be time to pick that up again.
> 
> Robin.
> 
>>
>> Best Regards
>>
>> Eric
>>>
>>> Robin.
>>>
>>> Mark Rutland (1):
>>>   Docs: dt: add PCI IOMMU map bindings
>>>
>>> Robin Murphy (21):
>>>   of/irq: Break out msi-map lookup (again)
>>>   iommu/of: Handle iommu-map property for PCI
>>>   iommu: Introduce iommu_fwspec
>>>   Docs: dt: document ARM SMMUv3 generic binding usage
>>>   iommu/arm-smmu: Fall back to global bypass
>>>   iommu/arm-smmu: Implement of_xlate() for SMMUv3
>>>   iommu/arm-smmu: Support non-PCI devices with SMMUv3
>>>   iommu/arm-smmu: Set PRIVCFG in stage 1 STEs
>>>   iommu/arm-smmu: Handle stream IDs more dynamically
>>>   iommu/arm-smmu: Consolidate stream map entry state
>>>   iommu/arm-smmu: Keep track of S2CR state
>>>   iommu/arm-smmu: Refactor mmu-masters handling
>>>   iommu/arm-smmu: Streamline SMMU data lookups
>>>   iommu/arm-smmu: Add a stream map entry iterator
>>>   iommu/arm-smmu: Intelligent SMR allocation
>>>   iommu/arm-smmu: Convert to iommu_fwspec
>>>   Docs: dt: document ARM SMMU generic binding usage
>>>   iommu/arm-smmu: Wire up generic configuration support
>>>   iommu/arm-smmu: Set domain geometry
>>>   iommu/dma: Add support for mapping MSIs
>>>   iommu/dma: Avoid PCI host bridge windows
>>>
>>>  .../devicetree/bindings/iommu/arm,smmu-v3.txt      |   8 +-
>>>  .../devicetree/bindings/iommu/arm,smmu.txt         |  63 +-
>>>  .../devicetree/bindings/pci/pci-iommu.txt          | 171 ++++
>>>  arch/arm64/mm/dma-mapping.c                        |   2 +-
>>>  drivers/gpu/drm/exynos/exynos_drm_iommu.h          |   2 +-
>>>  drivers/iommu/Kconfig                              |   2 +-
>>>  drivers/iommu/arm-smmu-v3.c                        | 386 +++++----
>>>  drivers/iommu/arm-smmu.c                           | 962 ++++++++++-----------
>>>  drivers/iommu/dma-iommu.c                          | 161 +++-
>>>  drivers/iommu/iommu.c                              |  56 ++
>>>  drivers/iommu/of_iommu.c                           |  52 +-
>>>  drivers/irqchip/irq-gic-v2m.c                      |   3 +
>>>  drivers/irqchip/irq-gic-v3-its.c                   |   3 +
>>>  drivers/of/irq.c                                   |  78 +-
>>>  drivers/of/of_pci.c                                | 102 +++
>>>  include/linux/device.h                             |   3 +
>>>  include/linux/dma-iommu.h                          |  12 +-
>>>  include/linux/iommu.h                              |  38 +
>>>  include/linux/of_pci.h                             |  10 +
>>>  19 files changed, 1323 insertions(+), 791 deletions(-)
>>>  create mode 100644 Documentation/devicetree/bindings/pci/pci-iommu.txt
>>>
>>
>>
>>
> 
--
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] 104+ messages in thread

* [PATCH v7 00/22] Generic DT bindings for PCI IOMMUs and ARM SMMU
@ 2016-09-13 12:57             ` Auger Eric
  0 siblings, 0 replies; 104+ messages in thread
From: Auger Eric @ 2016-09-13 12:57 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Robin,

On 13/09/2016 14:40, Robin Murphy wrote:
> Hi Eric,
> 
> On 13/09/16 13:14, Auger Eric wrote:
>> Hi Robin
>>
>> On 12/09/2016 18:13, Robin Murphy wrote:
>>> Hi all,
>>>
>>> To any more confusing fixups and crazily numbered extra patches, here's
>>> a quick v7 with everything rebased into the right order. The significant
>>> change this time is to implement iommu_fwspec properly from the start,
>>> which ends up being far simpler and more robust than faffing about
>>> introducing it somewhere 'less intrusive' to move toward core code later.
>>>
>>> New branch in the logical place:
>>>
>>> git://linux-arm.org/linux-rm iommu/generic-v7
>> I just tested your branch on AMD overdrive *without* updating the device
>> tree description according to the new syntax and I get a kernel oops.
>> See logs attached. Continuing my investigations ...
> 
> Looking at that backtrace, it seems the offending commit is actually in
> Will's devel branch _underneath_ this series; what's blowing up there is
> the short-descriptor io-pgtable selftests, which you should be able to
> reproduce on anything back to 4.6-rc1 with
> CONFIG_IOMMU_IO_PGTABLE_ARMV7S_SELFTEST=y.
I confirm that when disabling the option, I don't get the oops anymore.

Thanks!

Eric
> 
> The short-descriptor code is never going to work on Seattle due to the
> lack of 32-bit addressable memory - in normal use it would fail
> gracefully because it couldn't allocate anything, but since the
> selftests bypass the DMA API and corresponding checks, you end up with
> nastiness happening via truncated addresses. A while back I did start
> looking into generalising the selftests to remove all the "if
> (!selftest_running)" special-casing; might be time to pick that up again.
> 
> Robin.
> 
>>
>> Best Regards
>>
>> Eric
>>>
>>> Robin.
>>>
>>> Mark Rutland (1):
>>>   Docs: dt: add PCI IOMMU map bindings
>>>
>>> Robin Murphy (21):
>>>   of/irq: Break out msi-map lookup (again)
>>>   iommu/of: Handle iommu-map property for PCI
>>>   iommu: Introduce iommu_fwspec
>>>   Docs: dt: document ARM SMMUv3 generic binding usage
>>>   iommu/arm-smmu: Fall back to global bypass
>>>   iommu/arm-smmu: Implement of_xlate() for SMMUv3
>>>   iommu/arm-smmu: Support non-PCI devices with SMMUv3
>>>   iommu/arm-smmu: Set PRIVCFG in stage 1 STEs
>>>   iommu/arm-smmu: Handle stream IDs more dynamically
>>>   iommu/arm-smmu: Consolidate stream map entry state
>>>   iommu/arm-smmu: Keep track of S2CR state
>>>   iommu/arm-smmu: Refactor mmu-masters handling
>>>   iommu/arm-smmu: Streamline SMMU data lookups
>>>   iommu/arm-smmu: Add a stream map entry iterator
>>>   iommu/arm-smmu: Intelligent SMR allocation
>>>   iommu/arm-smmu: Convert to iommu_fwspec
>>>   Docs: dt: document ARM SMMU generic binding usage
>>>   iommu/arm-smmu: Wire up generic configuration support
>>>   iommu/arm-smmu: Set domain geometry
>>>   iommu/dma: Add support for mapping MSIs
>>>   iommu/dma: Avoid PCI host bridge windows
>>>
>>>  .../devicetree/bindings/iommu/arm,smmu-v3.txt      |   8 +-
>>>  .../devicetree/bindings/iommu/arm,smmu.txt         |  63 +-
>>>  .../devicetree/bindings/pci/pci-iommu.txt          | 171 ++++
>>>  arch/arm64/mm/dma-mapping.c                        |   2 +-
>>>  drivers/gpu/drm/exynos/exynos_drm_iommu.h          |   2 +-
>>>  drivers/iommu/Kconfig                              |   2 +-
>>>  drivers/iommu/arm-smmu-v3.c                        | 386 +++++----
>>>  drivers/iommu/arm-smmu.c                           | 962 ++++++++++-----------
>>>  drivers/iommu/dma-iommu.c                          | 161 +++-
>>>  drivers/iommu/iommu.c                              |  56 ++
>>>  drivers/iommu/of_iommu.c                           |  52 +-
>>>  drivers/irqchip/irq-gic-v2m.c                      |   3 +
>>>  drivers/irqchip/irq-gic-v3-its.c                   |   3 +
>>>  drivers/of/irq.c                                   |  78 +-
>>>  drivers/of/of_pci.c                                | 102 +++
>>>  include/linux/device.h                             |   3 +
>>>  include/linux/dma-iommu.h                          |  12 +-
>>>  include/linux/iommu.h                              |  38 +
>>>  include/linux/of_pci.h                             |  10 +
>>>  19 files changed, 1323 insertions(+), 791 deletions(-)
>>>  create mode 100644 Documentation/devicetree/bindings/pci/pci-iommu.txt
>>>
>>
>>
>>
> 

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

* Re: [PATCH v7 00/22] Generic DT bindings for PCI IOMMUs and ARM SMMU
  2016-09-12 16:13 ` Robin Murphy
@ 2016-09-14  8:41     ` Auger Eric
  -1 siblings, 0 replies; 104+ messages in thread
From: Auger Eric @ 2016-09-14  8:41 UTC (permalink / raw)
  To: Robin Murphy, will.deacon-5wv7dgnIgG8,
	joro-zLv9SwRftAIdnm+yROfE0A,
	iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, lorenzo.pieralisi-5wv7dgnIgG8,
	jean-philippe.brucker-5wv7dgnIgG8, punit.agrawal-5wv7dgnIgG8,
	thunder.leizhen-hv44wF8Li93QT0dZR+AlfA

Hi,

On 12/09/2016 18:13, Robin Murphy wrote:
> Hi all,
> 
> To any more confusing fixups and crazily numbered extra patches, here's
> a quick v7 with everything rebased into the right order. The significant
> change this time is to implement iommu_fwspec properly from the start,
> which ends up being far simpler and more robust than faffing about
> introducing it somewhere 'less intrusive' to move toward core code later.
> 
> New branch in the logical place:
> 
> git://linux-arm.org/linux-rm iommu/generic-v7

For information, as discussed privately with Robin I experience some
regressions with the former and now deprecated dt description.

on my AMD Overdrive board and my old dt description I now only see a
single group:

/sys/kernel/iommu_groups/
/sys/kernel/iommu_groups/0
/sys/kernel/iommu_groups/0/devices
/sys/kernel/iommu_groups/0/devices/e0700000.xgmac

whereas I formerly see

/sys/kernel/iommu_groups/
/sys/kernel/iommu_groups/3
/sys/kernel/iommu_groups/3/devices
/sys/kernel/iommu_groups/3/devices/0000:00:00.0
/sys/kernel/iommu_groups/1
/sys/kernel/iommu_groups/1/devices
/sys/kernel/iommu_groups/1/devices/e0700000.xgmac
/sys/kernel/iommu_groups/4
/sys/kernel/iommu_groups/4/devices
/sys/kernel/iommu_groups/4/devices/0000:00:02.2
/sys/kernel/iommu_groups/4/devices/0000:01:00.1
/sys/kernel/iommu_groups/4/devices/0000:00:02.0
/sys/kernel/iommu_groups/4/devices/0000:01:00.0
/sys/kernel/iommu_groups/2
/sys/kernel/iommu_groups/2/devices
/sys/kernel/iommu_groups/2/devices/e0900000.xgmac
/sys/kernel/iommu_groups/0
/sys/kernel/iommu_groups/0/devices
/sys/kernel/iommu_groups/0/devices/f0000000.pcie

This is the group topology without ACS override. Applying the non
upstreamed "pci: Enable overrides for missing ACS capabilities" I used
to see separate groups for each PCIe components. Now I don't see any
difference with and without ACS override.

Thanks

Eric
> 
> Robin.
> 
> Mark Rutland (1):
>   Docs: dt: add PCI IOMMU map bindings
> 
> Robin Murphy (21):
>   of/irq: Break out msi-map lookup (again)
>   iommu/of: Handle iommu-map property for PCI
>   iommu: Introduce iommu_fwspec
>   Docs: dt: document ARM SMMUv3 generic binding usage
>   iommu/arm-smmu: Fall back to global bypass
>   iommu/arm-smmu: Implement of_xlate() for SMMUv3
>   iommu/arm-smmu: Support non-PCI devices with SMMUv3
>   iommu/arm-smmu: Set PRIVCFG in stage 1 STEs
>   iommu/arm-smmu: Handle stream IDs more dynamically
>   iommu/arm-smmu: Consolidate stream map entry state
>   iommu/arm-smmu: Keep track of S2CR state
>   iommu/arm-smmu: Refactor mmu-masters handling
>   iommu/arm-smmu: Streamline SMMU data lookups
>   iommu/arm-smmu: Add a stream map entry iterator
>   iommu/arm-smmu: Intelligent SMR allocation
>   iommu/arm-smmu: Convert to iommu_fwspec
>   Docs: dt: document ARM SMMU generic binding usage
>   iommu/arm-smmu: Wire up generic configuration support
>   iommu/arm-smmu: Set domain geometry
>   iommu/dma: Add support for mapping MSIs
>   iommu/dma: Avoid PCI host bridge windows
> 
>  .../devicetree/bindings/iommu/arm,smmu-v3.txt      |   8 +-
>  .../devicetree/bindings/iommu/arm,smmu.txt         |  63 +-
>  .../devicetree/bindings/pci/pci-iommu.txt          | 171 ++++
>  arch/arm64/mm/dma-mapping.c                        |   2 +-
>  drivers/gpu/drm/exynos/exynos_drm_iommu.h          |   2 +-
>  drivers/iommu/Kconfig                              |   2 +-
>  drivers/iommu/arm-smmu-v3.c                        | 386 +++++----
>  drivers/iommu/arm-smmu.c                           | 962 ++++++++++-----------
>  drivers/iommu/dma-iommu.c                          | 161 +++-
>  drivers/iommu/iommu.c                              |  56 ++
>  drivers/iommu/of_iommu.c                           |  52 +-
>  drivers/irqchip/irq-gic-v2m.c                      |   3 +
>  drivers/irqchip/irq-gic-v3-its.c                   |   3 +
>  drivers/of/irq.c                                   |  78 +-
>  drivers/of/of_pci.c                                | 102 +++
>  include/linux/device.h                             |   3 +
>  include/linux/dma-iommu.h                          |  12 +-
>  include/linux/iommu.h                              |  38 +
>  include/linux/of_pci.h                             |  10 +
>  19 files changed, 1323 insertions(+), 791 deletions(-)
>  create mode 100644 Documentation/devicetree/bindings/pci/pci-iommu.txt
> 
--
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] 104+ messages in thread

* [PATCH v7 00/22] Generic DT bindings for PCI IOMMUs and ARM SMMU
@ 2016-09-14  8:41     ` Auger Eric
  0 siblings, 0 replies; 104+ messages in thread
From: Auger Eric @ 2016-09-14  8:41 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

On 12/09/2016 18:13, Robin Murphy wrote:
> Hi all,
> 
> To any more confusing fixups and crazily numbered extra patches, here's
> a quick v7 with everything rebased into the right order. The significant
> change this time is to implement iommu_fwspec properly from the start,
> which ends up being far simpler and more robust than faffing about
> introducing it somewhere 'less intrusive' to move toward core code later.
> 
> New branch in the logical place:
> 
> git://linux-arm.org/linux-rm iommu/generic-v7

For information, as discussed privately with Robin I experience some
regressions with the former and now deprecated dt description.

on my AMD Overdrive board and my old dt description I now only see a
single group:

/sys/kernel/iommu_groups/
/sys/kernel/iommu_groups/0
/sys/kernel/iommu_groups/0/devices
/sys/kernel/iommu_groups/0/devices/e0700000.xgmac

whereas I formerly see

/sys/kernel/iommu_groups/
/sys/kernel/iommu_groups/3
/sys/kernel/iommu_groups/3/devices
/sys/kernel/iommu_groups/3/devices/0000:00:00.0
/sys/kernel/iommu_groups/1
/sys/kernel/iommu_groups/1/devices
/sys/kernel/iommu_groups/1/devices/e0700000.xgmac
/sys/kernel/iommu_groups/4
/sys/kernel/iommu_groups/4/devices
/sys/kernel/iommu_groups/4/devices/0000:00:02.2
/sys/kernel/iommu_groups/4/devices/0000:01:00.1
/sys/kernel/iommu_groups/4/devices/0000:00:02.0
/sys/kernel/iommu_groups/4/devices/0000:01:00.0
/sys/kernel/iommu_groups/2
/sys/kernel/iommu_groups/2/devices
/sys/kernel/iommu_groups/2/devices/e0900000.xgmac
/sys/kernel/iommu_groups/0
/sys/kernel/iommu_groups/0/devices
/sys/kernel/iommu_groups/0/devices/f0000000.pcie

This is the group topology without ACS override. Applying the non
upstreamed "pci: Enable overrides for missing ACS capabilities" I used
to see separate groups for each PCIe components. Now I don't see any
difference with and without ACS override.

Thanks

Eric
> 
> Robin.
> 
> Mark Rutland (1):
>   Docs: dt: add PCI IOMMU map bindings
> 
> Robin Murphy (21):
>   of/irq: Break out msi-map lookup (again)
>   iommu/of: Handle iommu-map property for PCI
>   iommu: Introduce iommu_fwspec
>   Docs: dt: document ARM SMMUv3 generic binding usage
>   iommu/arm-smmu: Fall back to global bypass
>   iommu/arm-smmu: Implement of_xlate() for SMMUv3
>   iommu/arm-smmu: Support non-PCI devices with SMMUv3
>   iommu/arm-smmu: Set PRIVCFG in stage 1 STEs
>   iommu/arm-smmu: Handle stream IDs more dynamically
>   iommu/arm-smmu: Consolidate stream map entry state
>   iommu/arm-smmu: Keep track of S2CR state
>   iommu/arm-smmu: Refactor mmu-masters handling
>   iommu/arm-smmu: Streamline SMMU data lookups
>   iommu/arm-smmu: Add a stream map entry iterator
>   iommu/arm-smmu: Intelligent SMR allocation
>   iommu/arm-smmu: Convert to iommu_fwspec
>   Docs: dt: document ARM SMMU generic binding usage
>   iommu/arm-smmu: Wire up generic configuration support
>   iommu/arm-smmu: Set domain geometry
>   iommu/dma: Add support for mapping MSIs
>   iommu/dma: Avoid PCI host bridge windows
> 
>  .../devicetree/bindings/iommu/arm,smmu-v3.txt      |   8 +-
>  .../devicetree/bindings/iommu/arm,smmu.txt         |  63 +-
>  .../devicetree/bindings/pci/pci-iommu.txt          | 171 ++++
>  arch/arm64/mm/dma-mapping.c                        |   2 +-
>  drivers/gpu/drm/exynos/exynos_drm_iommu.h          |   2 +-
>  drivers/iommu/Kconfig                              |   2 +-
>  drivers/iommu/arm-smmu-v3.c                        | 386 +++++----
>  drivers/iommu/arm-smmu.c                           | 962 ++++++++++-----------
>  drivers/iommu/dma-iommu.c                          | 161 +++-
>  drivers/iommu/iommu.c                              |  56 ++
>  drivers/iommu/of_iommu.c                           |  52 +-
>  drivers/irqchip/irq-gic-v2m.c                      |   3 +
>  drivers/irqchip/irq-gic-v3-its.c                   |   3 +
>  drivers/of/irq.c                                   |  78 +-
>  drivers/of/of_pci.c                                | 102 +++
>  include/linux/device.h                             |   3 +
>  include/linux/dma-iommu.h                          |  12 +-
>  include/linux/iommu.h                              |  38 +
>  include/linux/of_pci.h                             |  10 +
>  19 files changed, 1323 insertions(+), 791 deletions(-)
>  create mode 100644 Documentation/devicetree/bindings/pci/pci-iommu.txt
> 

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

* Re: [PATCH v7 00/22] Generic DT bindings for PCI IOMMUs and ARM SMMU
  2016-09-14  8:41     ` Auger Eric
@ 2016-09-14  9:20         ` Will Deacon
  -1 siblings, 0 replies; 104+ messages in thread
From: Will Deacon @ 2016-09-14  9:20 UTC (permalink / raw)
  To: Auger Eric
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, punit.agrawal-5wv7dgnIgG8,
	iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	thunder.leizhen-hv44wF8Li93QT0dZR+AlfA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

On Wed, Sep 14, 2016 at 10:41:57AM +0200, Auger Eric wrote:
> Hi,

Hi Eric,

> On 12/09/2016 18:13, Robin Murphy wrote:
> > To any more confusing fixups and crazily numbered extra patches, here's
> > a quick v7 with everything rebased into the right order. The significant
> > change this time is to implement iommu_fwspec properly from the start,
> > which ends up being far simpler and more robust than faffing about
> > introducing it somewhere 'less intrusive' to move toward core code later.
> > 
> > New branch in the logical place:
> > 
> > git://linux-arm.org/linux-rm iommu/generic-v7
> 
> For information, as discussed privately with Robin I experience some
> regressions with the former and now deprecated dt description.

Please can you share the DT you're using so we can reproduce this locally?

Will

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

* [PATCH v7 00/22] Generic DT bindings for PCI IOMMUs and ARM SMMU
@ 2016-09-14  9:20         ` Will Deacon
  0 siblings, 0 replies; 104+ messages in thread
From: Will Deacon @ 2016-09-14  9:20 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Sep 14, 2016 at 10:41:57AM +0200, Auger Eric wrote:
> Hi,

Hi Eric,

> On 12/09/2016 18:13, Robin Murphy wrote:
> > To any more confusing fixups and crazily numbered extra patches, here's
> > a quick v7 with everything rebased into the right order. The significant
> > change this time is to implement iommu_fwspec properly from the start,
> > which ends up being far simpler and more robust than faffing about
> > introducing it somewhere 'less intrusive' to move toward core code later.
> > 
> > New branch in the logical place:
> > 
> > git://linux-arm.org/linux-rm iommu/generic-v7
> 
> For information, as discussed privately with Robin I experience some
> regressions with the former and now deprecated dt description.

Please can you share the DT you're using so we can reproduce this locally?

Will

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

* Re: [PATCH v7 00/22] Generic DT bindings for PCI IOMMUs and ARM SMMU
  2016-09-14  9:20         ` Will Deacon
@ 2016-09-14  9:35             ` Auger Eric
  -1 siblings, 0 replies; 104+ messages in thread
From: Auger Eric @ 2016-09-14  9:35 UTC (permalink / raw)
  To: Will Deacon
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, punit.agrawal-5wv7dgnIgG8,
	iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	thunder.leizhen-hv44wF8Li93QT0dZR+AlfA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

Hi Will

On 14/09/2016 11:20, Will Deacon wrote:
> On Wed, Sep 14, 2016 at 10:41:57AM +0200, Auger Eric wrote:
>> Hi,
> 
> Hi Eric,
> 
>> On 12/09/2016 18:13, Robin Murphy wrote:
>>> To any more confusing fixups and crazily numbered extra patches, here's
>>> a quick v7 with everything rebased into the right order. The significant
>>> change this time is to implement iommu_fwspec properly from the start,
>>> which ends up being far simpler and more robust than faffing about
>>> introducing it somewhere 'less intrusive' to move toward core code later.
>>>
>>> New branch in the logical place:
>>>
>>> git://linux-arm.org/linux-rm iommu/generic-v7
>>
>> For information, as discussed privately with Robin I experience some
>> regressions with the former and now deprecated dt description.
> 
> Please can you share the DT you're using so we can reproduce this locally?

Done already

Thanks

Eric
> 
> Will
> 
> _______________________________________________
> 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] 104+ messages in thread

* [PATCH v7 00/22] Generic DT bindings for PCI IOMMUs and ARM SMMU
@ 2016-09-14  9:35             ` Auger Eric
  0 siblings, 0 replies; 104+ messages in thread
From: Auger Eric @ 2016-09-14  9:35 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Will

On 14/09/2016 11:20, Will Deacon wrote:
> On Wed, Sep 14, 2016 at 10:41:57AM +0200, Auger Eric wrote:
>> Hi,
> 
> Hi Eric,
> 
>> On 12/09/2016 18:13, Robin Murphy wrote:
>>> To any more confusing fixups and crazily numbered extra patches, here's
>>> a quick v7 with everything rebased into the right order. The significant
>>> change this time is to implement iommu_fwspec properly from the start,
>>> which ends up being far simpler and more robust than faffing about
>>> introducing it somewhere 'less intrusive' to move toward core code later.
>>>
>>> New branch in the logical place:
>>>
>>> git://linux-arm.org/linux-rm iommu/generic-v7
>>
>> For information, as discussed privately with Robin I experience some
>> regressions with the former and now deprecated dt description.
> 
> Please can you share the DT you're using so we can reproduce this locally?

Done already

Thanks

Eric
> 
> Will
> 
> _______________________________________________
> 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] 104+ messages in thread

* Re: [PATCH v7 00/22] Generic DT bindings for PCI IOMMUs and ARM SMMU
  2016-09-14  8:41     ` Auger Eric
@ 2016-09-14 10:35         ` Robin Murphy
  -1 siblings, 0 replies; 104+ messages in thread
From: Robin Murphy @ 2016-09-14 10:35 UTC (permalink / raw)
  To: Auger Eric, will.deacon-5wv7dgnIgG8, joro-zLv9SwRftAIdnm+yROfE0A,
	iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, punit.agrawal-5wv7dgnIgG8,
	thunder.leizhen-hv44wF8Li93QT0dZR+AlfA

On 14/09/16 09:41, Auger Eric wrote:
> Hi,
> 
> On 12/09/2016 18:13, Robin Murphy wrote:
>> Hi all,
>>
>> To any more confusing fixups and crazily numbered extra patches, here's
>> a quick v7 with everything rebased into the right order. The significant
>> change this time is to implement iommu_fwspec properly from the start,
>> which ends up being far simpler and more robust than faffing about
>> introducing it somewhere 'less intrusive' to move toward core code later.
>>
>> New branch in the logical place:
>>
>> git://linux-arm.org/linux-rm iommu/generic-v7
> 
> For information, as discussed privately with Robin I experience some
> regressions with the former and now deprecated dt description.
> 
> on my AMD Overdrive board and my old dt description I now only see a
> single group:
> 
> /sys/kernel/iommu_groups/
> /sys/kernel/iommu_groups/0
> /sys/kernel/iommu_groups/0/devices
> /sys/kernel/iommu_groups/0/devices/e0700000.xgmac
> 
> whereas I formerly see
> 
> /sys/kernel/iommu_groups/
> /sys/kernel/iommu_groups/3
> /sys/kernel/iommu_groups/3/devices
> /sys/kernel/iommu_groups/3/devices/0000:00:00.0
> /sys/kernel/iommu_groups/1
> /sys/kernel/iommu_groups/1/devices
> /sys/kernel/iommu_groups/1/devices/e0700000.xgmac
> /sys/kernel/iommu_groups/4
> /sys/kernel/iommu_groups/4/devices
> /sys/kernel/iommu_groups/4/devices/0000:00:02.2
> /sys/kernel/iommu_groups/4/devices/0000:01:00.1
> /sys/kernel/iommu_groups/4/devices/0000:00:02.0
> /sys/kernel/iommu_groups/4/devices/0000:01:00.0
> /sys/kernel/iommu_groups/2
> /sys/kernel/iommu_groups/2/devices
> /sys/kernel/iommu_groups/2/devices/e0900000.xgmac
> /sys/kernel/iommu_groups/0
> /sys/kernel/iommu_groups/0/devices
> /sys/kernel/iommu_groups/0/devices/f0000000.pcie
> 
> This is the group topology without ACS override. Applying the non
> upstreamed "pci: Enable overrides for missing ACS capabilities" I used
> to see separate groups for each PCIe components. Now I don't see any
> difference with and without ACS override.

OK, having reproduced on my Juno, the problem looks to be that
of_for_each_phandle() leaves err set to -ENOENT after successfully
walking a phandle list, which makes __find_legacy_master_phandle()
always bail out after the first SMMU.

Can you confirm that the following diff fixes things for you?

Robin

--->8---
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index fa892d25004d..ac4aab97c93a 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -477,7 +477,7 @@ static int __find_legacy_master_phandle(struct
device *dev, void *data)
 			return 1;
 		}
 	it->node = np;
-	return err;
+	return err == -ENOENT ? 0 : err;
 }

 static struct platform_driver arm_smmu_driver;

--
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] 104+ messages in thread

* [PATCH v7 00/22] Generic DT bindings for PCI IOMMUs and ARM SMMU
@ 2016-09-14 10:35         ` Robin Murphy
  0 siblings, 0 replies; 104+ messages in thread
From: Robin Murphy @ 2016-09-14 10:35 UTC (permalink / raw)
  To: linux-arm-kernel

On 14/09/16 09:41, Auger Eric wrote:
> Hi,
> 
> On 12/09/2016 18:13, Robin Murphy wrote:
>> Hi all,
>>
>> To any more confusing fixups and crazily numbered extra patches, here's
>> a quick v7 with everything rebased into the right order. The significant
>> change this time is to implement iommu_fwspec properly from the start,
>> which ends up being far simpler and more robust than faffing about
>> introducing it somewhere 'less intrusive' to move toward core code later.
>>
>> New branch in the logical place:
>>
>> git://linux-arm.org/linux-rm iommu/generic-v7
> 
> For information, as discussed privately with Robin I experience some
> regressions with the former and now deprecated dt description.
> 
> on my AMD Overdrive board and my old dt description I now only see a
> single group:
> 
> /sys/kernel/iommu_groups/
> /sys/kernel/iommu_groups/0
> /sys/kernel/iommu_groups/0/devices
> /sys/kernel/iommu_groups/0/devices/e0700000.xgmac
> 
> whereas I formerly see
> 
> /sys/kernel/iommu_groups/
> /sys/kernel/iommu_groups/3
> /sys/kernel/iommu_groups/3/devices
> /sys/kernel/iommu_groups/3/devices/0000:00:00.0
> /sys/kernel/iommu_groups/1
> /sys/kernel/iommu_groups/1/devices
> /sys/kernel/iommu_groups/1/devices/e0700000.xgmac
> /sys/kernel/iommu_groups/4
> /sys/kernel/iommu_groups/4/devices
> /sys/kernel/iommu_groups/4/devices/0000:00:02.2
> /sys/kernel/iommu_groups/4/devices/0000:01:00.1
> /sys/kernel/iommu_groups/4/devices/0000:00:02.0
> /sys/kernel/iommu_groups/4/devices/0000:01:00.0
> /sys/kernel/iommu_groups/2
> /sys/kernel/iommu_groups/2/devices
> /sys/kernel/iommu_groups/2/devices/e0900000.xgmac
> /sys/kernel/iommu_groups/0
> /sys/kernel/iommu_groups/0/devices
> /sys/kernel/iommu_groups/0/devices/f0000000.pcie
> 
> This is the group topology without ACS override. Applying the non
> upstreamed "pci: Enable overrides for missing ACS capabilities" I used
> to see separate groups for each PCIe components. Now I don't see any
> difference with and without ACS override.

OK, having reproduced on my Juno, the problem looks to be that
of_for_each_phandle() leaves err set to -ENOENT after successfully
walking a phandle list, which makes __find_legacy_master_phandle()
always bail out after the first SMMU.

Can you confirm that the following diff fixes things for you?

Robin

--->8---
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index fa892d25004d..ac4aab97c93a 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -477,7 +477,7 @@ static int __find_legacy_master_phandle(struct
device *dev, void *data)
 			return 1;
 		}
 	it->node = np;
-	return err;
+	return err == -ENOENT ? 0 : err;
 }

 static struct platform_driver arm_smmu_driver;

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

* Re: [PATCH v7 22/22] iommu/dma: Avoid PCI host bridge windows
  2016-09-12 16:14     ` Robin Murphy
@ 2016-09-14 10:55         ` Marek Szyprowski
  -1 siblings, 0 replies; 104+ messages in thread
From: Marek Szyprowski @ 2016-09-14 10:55 UTC (permalink / raw)
  To: Robin Murphy, will.deacon-5wv7dgnIgG8,
	joro-zLv9SwRftAIdnm+yROfE0A,
	iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, lorenzo.pieralisi-5wv7dgnIgG8,
	jean-philippe.brucker-5wv7dgnIgG8, punit.agrawal-5wv7dgnIgG8,
	thunder.leizhen-hv44wF8Li93QT0dZR+AlfA,
	eric.auger-H+wXaHxf7aLQT0dZR+AlfA, Inki Dae

Hi Robin,


On 2016-09-12 18:14, Robin Murphy wrote:
> With our DMA ops enabled for PCI devices, we should avoid allocating
> IOVAs which a host bridge might misinterpret as peer-to-peer DMA and
> lead to faults, corruption or other badness. To be safe, punch out holes
> for all of the relevant host bridge's windows when initialising a DMA
> domain for a PCI device.
>
> CC: Marek Szyprowski <m.szyprowski-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
> CC: Inki Dae <inki.dae-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
> Reported-by: Lorenzo Pieralisi <lorenzo.pieralisi-5wv7dgnIgG8@public.gmane.org>
> Signed-off-by: Robin Murphy <robin.murphy-5wv7dgnIgG8@public.gmane.org>

I don't know much about PCI and their IOMMU integration, but can't we use
the direct mapping region feature of iommu core for it? There are already
iommu_get_dm_regions(), iommu_put_dm_regions() and 
iommu_request_dm_for_dev()
functions for handling them...

> ---
>
> - Squash in the previous drm/exynos fixup
> - If need be, this one can probably wait
> ---
>   arch/arm64/mm/dma-mapping.c               |  2 +-
>   drivers/gpu/drm/exynos/exynos_drm_iommu.h |  2 +-
>   drivers/iommu/dma-iommu.c                 | 25 ++++++++++++++++++++++++-
>   include/linux/dma-iommu.h                 |  3 ++-
>   4 files changed, 28 insertions(+), 4 deletions(-)
>
> diff --git a/arch/arm64/mm/dma-mapping.c b/arch/arm64/mm/dma-mapping.c
> index c4284c432ae8..610d8e53011e 100644
> --- a/arch/arm64/mm/dma-mapping.c
> +++ b/arch/arm64/mm/dma-mapping.c
> @@ -827,7 +827,7 @@ static bool do_iommu_attach(struct device *dev, const struct iommu_ops *ops,
>   	 * then the IOMMU core will have already configured a group for this
>   	 * device, and allocated the default domain for that group.
>   	 */
> -	if (!domain || iommu_dma_init_domain(domain, dma_base, size)) {
> +	if (!domain || iommu_dma_init_domain(domain, dma_base, size, dev)) {
>   		pr_warn("Failed to set up IOMMU for device %s; retaining platform DMA ops\n",
>   			dev_name(dev));
>   		return false;
> diff --git a/drivers/gpu/drm/exynos/exynos_drm_iommu.h b/drivers/gpu/drm/exynos/exynos_drm_iommu.h
> index c8de4913fdbe..87f6b5672e11 100644
> --- a/drivers/gpu/drm/exynos/exynos_drm_iommu.h
> +++ b/drivers/gpu/drm/exynos/exynos_drm_iommu.h
> @@ -66,7 +66,7 @@ static inline int __exynos_iommu_create_mapping(struct exynos_drm_private *priv,
>   	if (ret)
>   		goto free_domain;
>   
> -	ret = iommu_dma_init_domain(domain, start, size);
> +	ret = iommu_dma_init_domain(domain, start, size, NULL);
>   	if (ret)
>   		goto put_cookie;
>   
> diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
> index 4329d18080cf..c5ab8667e6f2 100644
> --- a/drivers/iommu/dma-iommu.c
> +++ b/drivers/iommu/dma-iommu.c
> @@ -27,6 +27,7 @@
>   #include <linux/iova.h>
>   #include <linux/irq.h>
>   #include <linux/mm.h>
> +#include <linux/pci.h>
>   #include <linux/scatterlist.h>
>   #include <linux/vmalloc.h>
>   
> @@ -103,18 +104,38 @@ void iommu_put_dma_cookie(struct iommu_domain *domain)
>   }
>   EXPORT_SYMBOL(iommu_put_dma_cookie);
>   
> +static void iova_reserve_pci_windows(struct pci_dev *dev,
> +		struct iova_domain *iovad)
> +{
> +	struct pci_host_bridge *bridge = pci_find_host_bridge(dev->bus);
> +	struct resource_entry *window;
> +	unsigned long lo, hi;
> +
> +	resource_list_for_each_entry(window, &bridge->windows) {
> +		if (resource_type(window->res) != IORESOURCE_MEM &&
> +		    resource_type(window->res) != IORESOURCE_IO)
> +			continue;
> +
> +		lo = iova_pfn(iovad, window->res->start - window->offset);
> +		hi = iova_pfn(iovad, window->res->end - window->offset);
> +		reserve_iova(iovad, lo, hi);
> +	}
> +}
> +
>   /**
>    * iommu_dma_init_domain - Initialise a DMA mapping domain
>    * @domain: IOMMU domain previously prepared by iommu_get_dma_cookie()
>    * @base: IOVA at which the mappable address space starts
>    * @size: Size of IOVA space
> + * @dev: Device the domain is being initialised for
>    *
>    * @base and @size should be exact multiples of IOMMU page granularity to
>    * avoid rounding surprises. If necessary, we reserve the page at address 0
>    * to ensure it is an invalid IOVA. It is safe to reinitialise a domain, but
>    * any change which could make prior IOVAs invalid will fail.
>    */
> -int iommu_dma_init_domain(struct iommu_domain *domain, dma_addr_t base, u64 size)
> +int iommu_dma_init_domain(struct iommu_domain *domain, dma_addr_t base,
> +		u64 size, struct device *dev)
>   {
>   	struct iova_domain *iovad = cookie_iovad(domain);
>   	unsigned long order, base_pfn, end_pfn;
> @@ -152,6 +173,8 @@ int iommu_dma_init_domain(struct iommu_domain *domain, dma_addr_t base, u64 size
>   		iovad->dma_32bit_pfn = end_pfn;
>   	} else {
>   		init_iova_domain(iovad, 1UL << order, base_pfn, end_pfn);
> +		if (dev && dev_is_pci(dev))
> +			iova_reserve_pci_windows(to_pci_dev(dev), iovad);
>   	}
>   	return 0;
>   }
> diff --git a/include/linux/dma-iommu.h b/include/linux/dma-iommu.h
> index 5ee806e41b5c..32c589062bd9 100644
> --- a/include/linux/dma-iommu.h
> +++ b/include/linux/dma-iommu.h
> @@ -30,7 +30,8 @@ int iommu_get_dma_cookie(struct iommu_domain *domain);
>   void iommu_put_dma_cookie(struct iommu_domain *domain);
>   
>   /* Setup call for arch DMA mapping code */
> -int iommu_dma_init_domain(struct iommu_domain *domain, dma_addr_t base, u64 size);
> +int iommu_dma_init_domain(struct iommu_domain *domain, dma_addr_t base,
> +		u64 size, struct device *dev);
>   
>   /* General helpers for DMA-API <-> IOMMU-API interaction */
>   int dma_direction_to_prot(enum dma_data_direction dir, bool coherent);

Best regards
-- 
Marek Szyprowski, PhD
Samsung R&D Institute Poland

--
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] 104+ messages in thread

* [PATCH v7 22/22] iommu/dma: Avoid PCI host bridge windows
@ 2016-09-14 10:55         ` Marek Szyprowski
  0 siblings, 0 replies; 104+ messages in thread
From: Marek Szyprowski @ 2016-09-14 10:55 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Robin,


On 2016-09-12 18:14, Robin Murphy wrote:
> With our DMA ops enabled for PCI devices, we should avoid allocating
> IOVAs which a host bridge might misinterpret as peer-to-peer DMA and
> lead to faults, corruption or other badness. To be safe, punch out holes
> for all of the relevant host bridge's windows when initialising a DMA
> domain for a PCI device.
>
> CC: Marek Szyprowski <m.szyprowski@samsung.com>
> CC: Inki Dae <inki.dae@samsung.com>
> Reported-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
> Signed-off-by: Robin Murphy <robin.murphy@arm.com>

I don't know much about PCI and their IOMMU integration, but can't we use
the direct mapping region feature of iommu core for it? There are already
iommu_get_dm_regions(), iommu_put_dm_regions() and 
iommu_request_dm_for_dev()
functions for handling them...

> ---
>
> - Squash in the previous drm/exynos fixup
> - If need be, this one can probably wait
> ---
>   arch/arm64/mm/dma-mapping.c               |  2 +-
>   drivers/gpu/drm/exynos/exynos_drm_iommu.h |  2 +-
>   drivers/iommu/dma-iommu.c                 | 25 ++++++++++++++++++++++++-
>   include/linux/dma-iommu.h                 |  3 ++-
>   4 files changed, 28 insertions(+), 4 deletions(-)
>
> diff --git a/arch/arm64/mm/dma-mapping.c b/arch/arm64/mm/dma-mapping.c
> index c4284c432ae8..610d8e53011e 100644
> --- a/arch/arm64/mm/dma-mapping.c
> +++ b/arch/arm64/mm/dma-mapping.c
> @@ -827,7 +827,7 @@ static bool do_iommu_attach(struct device *dev, const struct iommu_ops *ops,
>   	 * then the IOMMU core will have already configured a group for this
>   	 * device, and allocated the default domain for that group.
>   	 */
> -	if (!domain || iommu_dma_init_domain(domain, dma_base, size)) {
> +	if (!domain || iommu_dma_init_domain(domain, dma_base, size, dev)) {
>   		pr_warn("Failed to set up IOMMU for device %s; retaining platform DMA ops\n",
>   			dev_name(dev));
>   		return false;
> diff --git a/drivers/gpu/drm/exynos/exynos_drm_iommu.h b/drivers/gpu/drm/exynos/exynos_drm_iommu.h
> index c8de4913fdbe..87f6b5672e11 100644
> --- a/drivers/gpu/drm/exynos/exynos_drm_iommu.h
> +++ b/drivers/gpu/drm/exynos/exynos_drm_iommu.h
> @@ -66,7 +66,7 @@ static inline int __exynos_iommu_create_mapping(struct exynos_drm_private *priv,
>   	if (ret)
>   		goto free_domain;
>   
> -	ret = iommu_dma_init_domain(domain, start, size);
> +	ret = iommu_dma_init_domain(domain, start, size, NULL);
>   	if (ret)
>   		goto put_cookie;
>   
> diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
> index 4329d18080cf..c5ab8667e6f2 100644
> --- a/drivers/iommu/dma-iommu.c
> +++ b/drivers/iommu/dma-iommu.c
> @@ -27,6 +27,7 @@
>   #include <linux/iova.h>
>   #include <linux/irq.h>
>   #include <linux/mm.h>
> +#include <linux/pci.h>
>   #include <linux/scatterlist.h>
>   #include <linux/vmalloc.h>
>   
> @@ -103,18 +104,38 @@ void iommu_put_dma_cookie(struct iommu_domain *domain)
>   }
>   EXPORT_SYMBOL(iommu_put_dma_cookie);
>   
> +static void iova_reserve_pci_windows(struct pci_dev *dev,
> +		struct iova_domain *iovad)
> +{
> +	struct pci_host_bridge *bridge = pci_find_host_bridge(dev->bus);
> +	struct resource_entry *window;
> +	unsigned long lo, hi;
> +
> +	resource_list_for_each_entry(window, &bridge->windows) {
> +		if (resource_type(window->res) != IORESOURCE_MEM &&
> +		    resource_type(window->res) != IORESOURCE_IO)
> +			continue;
> +
> +		lo = iova_pfn(iovad, window->res->start - window->offset);
> +		hi = iova_pfn(iovad, window->res->end - window->offset);
> +		reserve_iova(iovad, lo, hi);
> +	}
> +}
> +
>   /**
>    * iommu_dma_init_domain - Initialise a DMA mapping domain
>    * @domain: IOMMU domain previously prepared by iommu_get_dma_cookie()
>    * @base: IOVA at which the mappable address space starts
>    * @size: Size of IOVA space
> + * @dev: Device the domain is being initialised for
>    *
>    * @base and @size should be exact multiples of IOMMU page granularity to
>    * avoid rounding surprises. If necessary, we reserve the page at address 0
>    * to ensure it is an invalid IOVA. It is safe to reinitialise a domain, but
>    * any change which could make prior IOVAs invalid will fail.
>    */
> -int iommu_dma_init_domain(struct iommu_domain *domain, dma_addr_t base, u64 size)
> +int iommu_dma_init_domain(struct iommu_domain *domain, dma_addr_t base,
> +		u64 size, struct device *dev)
>   {
>   	struct iova_domain *iovad = cookie_iovad(domain);
>   	unsigned long order, base_pfn, end_pfn;
> @@ -152,6 +173,8 @@ int iommu_dma_init_domain(struct iommu_domain *domain, dma_addr_t base, u64 size
>   		iovad->dma_32bit_pfn = end_pfn;
>   	} else {
>   		init_iova_domain(iovad, 1UL << order, base_pfn, end_pfn);
> +		if (dev && dev_is_pci(dev))
> +			iova_reserve_pci_windows(to_pci_dev(dev), iovad);
>   	}
>   	return 0;
>   }
> diff --git a/include/linux/dma-iommu.h b/include/linux/dma-iommu.h
> index 5ee806e41b5c..32c589062bd9 100644
> --- a/include/linux/dma-iommu.h
> +++ b/include/linux/dma-iommu.h
> @@ -30,7 +30,8 @@ int iommu_get_dma_cookie(struct iommu_domain *domain);
>   void iommu_put_dma_cookie(struct iommu_domain *domain);
>   
>   /* Setup call for arch DMA mapping code */
> -int iommu_dma_init_domain(struct iommu_domain *domain, dma_addr_t base, u64 size);
> +int iommu_dma_init_domain(struct iommu_domain *domain, dma_addr_t base,
> +		u64 size, struct device *dev);
>   
>   /* General helpers for DMA-API <-> IOMMU-API interaction */
>   int dma_direction_to_prot(enum dma_data_direction dir, bool coherent);

Best regards
-- 
Marek Szyprowski, PhD
Samsung R&D Institute Poland

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

* Re: [PATCH v7 22/22] iommu/dma: Avoid PCI host bridge windows
  2016-09-14 10:55         ` Marek Szyprowski
@ 2016-09-14 11:10             ` Robin Murphy
  -1 siblings, 0 replies; 104+ messages in thread
From: Robin Murphy @ 2016-09-14 11:10 UTC (permalink / raw)
  To: Marek Szyprowski, will.deacon-5wv7dgnIgG8,
	joro-zLv9SwRftAIdnm+yROfE0A,
	iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, punit.agrawal-5wv7dgnIgG8,
	Inki Dae, thunder.leizhen-hv44wF8Li93QT0dZR+AlfA

Hi Marek,

On 14/09/16 11:55, Marek Szyprowski wrote:
> Hi Robin,
> 
> 
> On 2016-09-12 18:14, Robin Murphy wrote:
>> With our DMA ops enabled for PCI devices, we should avoid allocating
>> IOVAs which a host bridge might misinterpret as peer-to-peer DMA and
>> lead to faults, corruption or other badness. To be safe, punch out holes
>> for all of the relevant host bridge's windows when initialising a DMA
>> domain for a PCI device.
>>
>> CC: Marek Szyprowski <m.szyprowski-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
>> CC: Inki Dae <inki.dae-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
>> Reported-by: Lorenzo Pieralisi <lorenzo.pieralisi-5wv7dgnIgG8@public.gmane.org>
>> Signed-off-by: Robin Murphy <robin.murphy-5wv7dgnIgG8@public.gmane.org>
> 
> I don't know much about PCI and their IOMMU integration, but can't we use
> the direct mapping region feature of iommu core for it? There are already
> iommu_get_dm_regions(), iommu_put_dm_regions() and
> iommu_request_dm_for_dev()
> functions for handling them...

It's rather the opposite problem - in the direct-mapping case, we're
making sure the iommu_domain has translations installed for the given
IOVAs (which are also the corresponding physical address) before it goes
live, whereas what we need to do here is make sure the these addresses
never get used as IOVAs at all, because any attempt to do so them will
likely go wrong. Thus we carve them out of the iova_domain such that
they will never get near an actual IOMMU API call.

This is a slightly generalised equivalent of e.g. amd_iommu.c's
init_reserved_iova_ranges().

Robin.

> 
>> ---
>>
>> - Squash in the previous drm/exynos fixup
>> - If need be, this one can probably wait
>> ---
>>   arch/arm64/mm/dma-mapping.c               |  2 +-
>>   drivers/gpu/drm/exynos/exynos_drm_iommu.h |  2 +-
>>   drivers/iommu/dma-iommu.c                 | 25
>> ++++++++++++++++++++++++-
>>   include/linux/dma-iommu.h                 |  3 ++-
>>   4 files changed, 28 insertions(+), 4 deletions(-)
>>
>> diff --git a/arch/arm64/mm/dma-mapping.c b/arch/arm64/mm/dma-mapping.c
>> index c4284c432ae8..610d8e53011e 100644
>> --- a/arch/arm64/mm/dma-mapping.c
>> +++ b/arch/arm64/mm/dma-mapping.c
>> @@ -827,7 +827,7 @@ static bool do_iommu_attach(struct device *dev,
>> const struct iommu_ops *ops,
>>        * then the IOMMU core will have already configured a group for
>> this
>>        * device, and allocated the default domain for that group.
>>        */
>> -    if (!domain || iommu_dma_init_domain(domain, dma_base, size)) {
>> +    if (!domain || iommu_dma_init_domain(domain, dma_base, size, dev)) {
>>           pr_warn("Failed to set up IOMMU for device %s; retaining
>> platform DMA ops\n",
>>               dev_name(dev));
>>           return false;
>> diff --git a/drivers/gpu/drm/exynos/exynos_drm_iommu.h
>> b/drivers/gpu/drm/exynos/exynos_drm_iommu.h
>> index c8de4913fdbe..87f6b5672e11 100644
>> --- a/drivers/gpu/drm/exynos/exynos_drm_iommu.h
>> +++ b/drivers/gpu/drm/exynos/exynos_drm_iommu.h
>> @@ -66,7 +66,7 @@ static inline int
>> __exynos_iommu_create_mapping(struct exynos_drm_private *priv,
>>       if (ret)
>>           goto free_domain;
>>   -    ret = iommu_dma_init_domain(domain, start, size);
>> +    ret = iommu_dma_init_domain(domain, start, size, NULL);
>>       if (ret)
>>           goto put_cookie;
>>   diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
>> index 4329d18080cf..c5ab8667e6f2 100644
>> --- a/drivers/iommu/dma-iommu.c
>> +++ b/drivers/iommu/dma-iommu.c
>> @@ -27,6 +27,7 @@
>>   #include <linux/iova.h>
>>   #include <linux/irq.h>
>>   #include <linux/mm.h>
>> +#include <linux/pci.h>
>>   #include <linux/scatterlist.h>
>>   #include <linux/vmalloc.h>
>>   @@ -103,18 +104,38 @@ void iommu_put_dma_cookie(struct iommu_domain
>> *domain)
>>   }
>>   EXPORT_SYMBOL(iommu_put_dma_cookie);
>>   +static void iova_reserve_pci_windows(struct pci_dev *dev,
>> +        struct iova_domain *iovad)
>> +{
>> +    struct pci_host_bridge *bridge = pci_find_host_bridge(dev->bus);
>> +    struct resource_entry *window;
>> +    unsigned long lo, hi;
>> +
>> +    resource_list_for_each_entry(window, &bridge->windows) {
>> +        if (resource_type(window->res) != IORESOURCE_MEM &&
>> +            resource_type(window->res) != IORESOURCE_IO)
>> +            continue;
>> +
>> +        lo = iova_pfn(iovad, window->res->start - window->offset);
>> +        hi = iova_pfn(iovad, window->res->end - window->offset);
>> +        reserve_iova(iovad, lo, hi);
>> +    }
>> +}
>> +
>>   /**
>>    * iommu_dma_init_domain - Initialise a DMA mapping domain
>>    * @domain: IOMMU domain previously prepared by iommu_get_dma_cookie()
>>    * @base: IOVA at which the mappable address space starts
>>    * @size: Size of IOVA space
>> + * @dev: Device the domain is being initialised for
>>    *
>>    * @base and @size should be exact multiples of IOMMU page
>> granularity to
>>    * avoid rounding surprises. If necessary, we reserve the page at
>> address 0
>>    * to ensure it is an invalid IOVA. It is safe to reinitialise a
>> domain, but
>>    * any change which could make prior IOVAs invalid will fail.
>>    */
>> -int iommu_dma_init_domain(struct iommu_domain *domain, dma_addr_t
>> base, u64 size)
>> +int iommu_dma_init_domain(struct iommu_domain *domain, dma_addr_t base,
>> +        u64 size, struct device *dev)
>>   {
>>       struct iova_domain *iovad = cookie_iovad(domain);
>>       unsigned long order, base_pfn, end_pfn;
>> @@ -152,6 +173,8 @@ int iommu_dma_init_domain(struct iommu_domain
>> *domain, dma_addr_t base, u64 size
>>           iovad->dma_32bit_pfn = end_pfn;
>>       } else {
>>           init_iova_domain(iovad, 1UL << order, base_pfn, end_pfn);
>> +        if (dev && dev_is_pci(dev))
>> +            iova_reserve_pci_windows(to_pci_dev(dev), iovad);
>>       }
>>       return 0;
>>   }
>> diff --git a/include/linux/dma-iommu.h b/include/linux/dma-iommu.h
>> index 5ee806e41b5c..32c589062bd9 100644
>> --- a/include/linux/dma-iommu.h
>> +++ b/include/linux/dma-iommu.h
>> @@ -30,7 +30,8 @@ int iommu_get_dma_cookie(struct iommu_domain *domain);
>>   void iommu_put_dma_cookie(struct iommu_domain *domain);
>>     /* Setup call for arch DMA mapping code */
>> -int iommu_dma_init_domain(struct iommu_domain *domain, dma_addr_t
>> base, u64 size);
>> +int iommu_dma_init_domain(struct iommu_domain *domain, dma_addr_t base,
>> +        u64 size, struct device *dev);
>>     /* General helpers for DMA-API <-> IOMMU-API interaction */
>>   int dma_direction_to_prot(enum dma_data_direction dir, bool coherent);
> 
> Best regards

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

* [PATCH v7 22/22] iommu/dma: Avoid PCI host bridge windows
@ 2016-09-14 11:10             ` Robin Murphy
  0 siblings, 0 replies; 104+ messages in thread
From: Robin Murphy @ 2016-09-14 11:10 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Marek,

On 14/09/16 11:55, Marek Szyprowski wrote:
> Hi Robin,
> 
> 
> On 2016-09-12 18:14, Robin Murphy wrote:
>> With our DMA ops enabled for PCI devices, we should avoid allocating
>> IOVAs which a host bridge might misinterpret as peer-to-peer DMA and
>> lead to faults, corruption or other badness. To be safe, punch out holes
>> for all of the relevant host bridge's windows when initialising a DMA
>> domain for a PCI device.
>>
>> CC: Marek Szyprowski <m.szyprowski@samsung.com>
>> CC: Inki Dae <inki.dae@samsung.com>
>> Reported-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
>> Signed-off-by: Robin Murphy <robin.murphy@arm.com>
> 
> I don't know much about PCI and their IOMMU integration, but can't we use
> the direct mapping region feature of iommu core for it? There are already
> iommu_get_dm_regions(), iommu_put_dm_regions() and
> iommu_request_dm_for_dev()
> functions for handling them...

It's rather the opposite problem - in the direct-mapping case, we're
making sure the iommu_domain has translations installed for the given
IOVAs (which are also the corresponding physical address) before it goes
live, whereas what we need to do here is make sure the these addresses
never get used as IOVAs at all, because any attempt to do so them will
likely go wrong. Thus we carve them out of the iova_domain such that
they will never get near an actual IOMMU API call.

This is a slightly generalised equivalent of e.g. amd_iommu.c's
init_reserved_iova_ranges().

Robin.

> 
>> ---
>>
>> - Squash in the previous drm/exynos fixup
>> - If need be, this one can probably wait
>> ---
>>   arch/arm64/mm/dma-mapping.c               |  2 +-
>>   drivers/gpu/drm/exynos/exynos_drm_iommu.h |  2 +-
>>   drivers/iommu/dma-iommu.c                 | 25
>> ++++++++++++++++++++++++-
>>   include/linux/dma-iommu.h                 |  3 ++-
>>   4 files changed, 28 insertions(+), 4 deletions(-)
>>
>> diff --git a/arch/arm64/mm/dma-mapping.c b/arch/arm64/mm/dma-mapping.c
>> index c4284c432ae8..610d8e53011e 100644
>> --- a/arch/arm64/mm/dma-mapping.c
>> +++ b/arch/arm64/mm/dma-mapping.c
>> @@ -827,7 +827,7 @@ static bool do_iommu_attach(struct device *dev,
>> const struct iommu_ops *ops,
>>        * then the IOMMU core will have already configured a group for
>> this
>>        * device, and allocated the default domain for that group.
>>        */
>> -    if (!domain || iommu_dma_init_domain(domain, dma_base, size)) {
>> +    if (!domain || iommu_dma_init_domain(domain, dma_base, size, dev)) {
>>           pr_warn("Failed to set up IOMMU for device %s; retaining
>> platform DMA ops\n",
>>               dev_name(dev));
>>           return false;
>> diff --git a/drivers/gpu/drm/exynos/exynos_drm_iommu.h
>> b/drivers/gpu/drm/exynos/exynos_drm_iommu.h
>> index c8de4913fdbe..87f6b5672e11 100644
>> --- a/drivers/gpu/drm/exynos/exynos_drm_iommu.h
>> +++ b/drivers/gpu/drm/exynos/exynos_drm_iommu.h
>> @@ -66,7 +66,7 @@ static inline int
>> __exynos_iommu_create_mapping(struct exynos_drm_private *priv,
>>       if (ret)
>>           goto free_domain;
>>   -    ret = iommu_dma_init_domain(domain, start, size);
>> +    ret = iommu_dma_init_domain(domain, start, size, NULL);
>>       if (ret)
>>           goto put_cookie;
>>   diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
>> index 4329d18080cf..c5ab8667e6f2 100644
>> --- a/drivers/iommu/dma-iommu.c
>> +++ b/drivers/iommu/dma-iommu.c
>> @@ -27,6 +27,7 @@
>>   #include <linux/iova.h>
>>   #include <linux/irq.h>
>>   #include <linux/mm.h>
>> +#include <linux/pci.h>
>>   #include <linux/scatterlist.h>
>>   #include <linux/vmalloc.h>
>>   @@ -103,18 +104,38 @@ void iommu_put_dma_cookie(struct iommu_domain
>> *domain)
>>   }
>>   EXPORT_SYMBOL(iommu_put_dma_cookie);
>>   +static void iova_reserve_pci_windows(struct pci_dev *dev,
>> +        struct iova_domain *iovad)
>> +{
>> +    struct pci_host_bridge *bridge = pci_find_host_bridge(dev->bus);
>> +    struct resource_entry *window;
>> +    unsigned long lo, hi;
>> +
>> +    resource_list_for_each_entry(window, &bridge->windows) {
>> +        if (resource_type(window->res) != IORESOURCE_MEM &&
>> +            resource_type(window->res) != IORESOURCE_IO)
>> +            continue;
>> +
>> +        lo = iova_pfn(iovad, window->res->start - window->offset);
>> +        hi = iova_pfn(iovad, window->res->end - window->offset);
>> +        reserve_iova(iovad, lo, hi);
>> +    }
>> +}
>> +
>>   /**
>>    * iommu_dma_init_domain - Initialise a DMA mapping domain
>>    * @domain: IOMMU domain previously prepared by iommu_get_dma_cookie()
>>    * @base: IOVA at which the mappable address space starts
>>    * @size: Size of IOVA space
>> + * @dev: Device the domain is being initialised for
>>    *
>>    * @base and @size should be exact multiples of IOMMU page
>> granularity to
>>    * avoid rounding surprises. If necessary, we reserve the page at
>> address 0
>>    * to ensure it is an invalid IOVA. It is safe to reinitialise a
>> domain, but
>>    * any change which could make prior IOVAs invalid will fail.
>>    */
>> -int iommu_dma_init_domain(struct iommu_domain *domain, dma_addr_t
>> base, u64 size)
>> +int iommu_dma_init_domain(struct iommu_domain *domain, dma_addr_t base,
>> +        u64 size, struct device *dev)
>>   {
>>       struct iova_domain *iovad = cookie_iovad(domain);
>>       unsigned long order, base_pfn, end_pfn;
>> @@ -152,6 +173,8 @@ int iommu_dma_init_domain(struct iommu_domain
>> *domain, dma_addr_t base, u64 size
>>           iovad->dma_32bit_pfn = end_pfn;
>>       } else {
>>           init_iova_domain(iovad, 1UL << order, base_pfn, end_pfn);
>> +        if (dev && dev_is_pci(dev))
>> +            iova_reserve_pci_windows(to_pci_dev(dev), iovad);
>>       }
>>       return 0;
>>   }
>> diff --git a/include/linux/dma-iommu.h b/include/linux/dma-iommu.h
>> index 5ee806e41b5c..32c589062bd9 100644
>> --- a/include/linux/dma-iommu.h
>> +++ b/include/linux/dma-iommu.h
>> @@ -30,7 +30,8 @@ int iommu_get_dma_cookie(struct iommu_domain *domain);
>>   void iommu_put_dma_cookie(struct iommu_domain *domain);
>>     /* Setup call for arch DMA mapping code */
>> -int iommu_dma_init_domain(struct iommu_domain *domain, dma_addr_t
>> base, u64 size);
>> +int iommu_dma_init_domain(struct iommu_domain *domain, dma_addr_t base,
>> +        u64 size, struct device *dev);
>>     /* General helpers for DMA-API <-> IOMMU-API interaction */
>>   int dma_direction_to_prot(enum dma_data_direction dir, bool coherent);
> 
> Best regards

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

* Re: [PATCH v7 00/22] Generic DT bindings for PCI IOMMUs and ARM SMMU
  2016-09-14 10:35         ` Robin Murphy
@ 2016-09-14 12:32             ` Auger Eric
  -1 siblings, 0 replies; 104+ messages in thread
From: Auger Eric @ 2016-09-14 12:32 UTC (permalink / raw)
  To: Robin Murphy, will.deacon-5wv7dgnIgG8,
	joro-zLv9SwRftAIdnm+yROfE0A,
	iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, punit.agrawal-5wv7dgnIgG8,
	thunder.leizhen-hv44wF8Li93QT0dZR+AlfA

Hi,
On 14/09/2016 12:35, Robin Murphy wrote:
> On 14/09/16 09:41, Auger Eric wrote:
>> Hi,
>>
>> On 12/09/2016 18:13, Robin Murphy wrote:
>>> Hi all,
>>>
>>> To any more confusing fixups and crazily numbered extra patches, here's
>>> a quick v7 with everything rebased into the right order. The significant
>>> change this time is to implement iommu_fwspec properly from the start,
>>> which ends up being far simpler and more robust than faffing about
>>> introducing it somewhere 'less intrusive' to move toward core code later.
>>>
>>> New branch in the logical place:
>>>
>>> git://linux-arm.org/linux-rm iommu/generic-v7
>>
>> For information, as discussed privately with Robin I experience some
>> regressions with the former and now deprecated dt description.
>>
>> on my AMD Overdrive board and my old dt description I now only see a
>> single group:
>>
>> /sys/kernel/iommu_groups/
>> /sys/kernel/iommu_groups/0
>> /sys/kernel/iommu_groups/0/devices
>> /sys/kernel/iommu_groups/0/devices/e0700000.xgmac
>>
>> whereas I formerly see
>>
>> /sys/kernel/iommu_groups/
>> /sys/kernel/iommu_groups/3
>> /sys/kernel/iommu_groups/3/devices
>> /sys/kernel/iommu_groups/3/devices/0000:00:00.0
>> /sys/kernel/iommu_groups/1
>> /sys/kernel/iommu_groups/1/devices
>> /sys/kernel/iommu_groups/1/devices/e0700000.xgmac
>> /sys/kernel/iommu_groups/4
>> /sys/kernel/iommu_groups/4/devices
>> /sys/kernel/iommu_groups/4/devices/0000:00:02.2
>> /sys/kernel/iommu_groups/4/devices/0000:01:00.1
>> /sys/kernel/iommu_groups/4/devices/0000:00:02.0
>> /sys/kernel/iommu_groups/4/devices/0000:01:00.0
>> /sys/kernel/iommu_groups/2
>> /sys/kernel/iommu_groups/2/devices
>> /sys/kernel/iommu_groups/2/devices/e0900000.xgmac
>> /sys/kernel/iommu_groups/0
>> /sys/kernel/iommu_groups/0/devices
>> /sys/kernel/iommu_groups/0/devices/f0000000.pcie
>>
>> This is the group topology without ACS override. Applying the non
>> upstreamed "pci: Enable overrides for missing ACS capabilities" I used
>> to see separate groups for each PCIe components. Now I don't see any
>> difference with and without ACS override.
> 
> OK, having reproduced on my Juno, the problem looks to be that
> of_for_each_phandle() leaves err set to -ENOENT after successfully
> walking a phandle list, which makes __find_legacy_master_phandle()
> always bail out after the first SMMU.
> 
> Can you confirm that the following diff fixes things for you?

Well it improves but there are still differences in the group topology.
The PFs now are in group 0.

root@trusty:~# lspci -nk
00:00.0 0600: 1022:1a00
        Subsystem: 1022:1a00
00:02.0 0600: 1022:1a01
00:02.2 0604: 1022:1a02
        Kernel driver in use: pcieport
01:00.0 0200: 8086:1521 (rev 01)
        Subsystem: 8086:0002
        Kernel driver in use: igb
01:00.1 0200: 8086:1521 (rev 01)
        Subsystem: 8086:0002
        Kernel driver in use: igb


with your series + fix:
/sys/kernel/iommu_groups/
/sys/kernel/iommu_groups/3
/sys/kernel/iommu_groups/3/devices
/sys/kernel/iommu_groups/3/devices/0000:00:00.0
/sys/kernel/iommu_groups/1
/sys/kernel/iommu_groups/1/devices
/sys/kernel/iommu_groups/1/devices/e0700000.xgmac
/sys/kernel/iommu_groups/4
/sys/kernel/iommu_groups/4/devices
/sys/kernel/iommu_groups/4/devices/0000:00:02.2
/sys/kernel/iommu_groups/4/devices/0000:00:02.0
/sys/kernel/iommu_groups/2
/sys/kernel/iommu_groups/2/devices
/sys/kernel/iommu_groups/2/devices/e0900000.xgmac
/sys/kernel/iommu_groups/0
/sys/kernel/iommu_groups/0/devices
/sys/kernel/iommu_groups/0/devices/0000:01:00.1
/sys/kernel/iommu_groups/0/devices/f0000000.pcie
/sys/kernel/iommu_groups/0/devices/0000:01:00.0

Before (4.8-rc5):

/sys/kernel/iommu_groups/
/sys/kernel/iommu_groups/3
/sys/kernel/iommu_groups/3/devices
/sys/kernel/iommu_groups/3/devices/0000:00:00.0
/sys/kernel/iommu_groups/1
/sys/kernel/iommu_groups/1/devices
/sys/kernel/iommu_groups/1/devices/e0700000.xgmac
/sys/kernel/iommu_groups/4
/sys/kernel/iommu_groups/4/devices
/sys/kernel/iommu_groups/4/devices/0000:00:02.2
/sys/kernel/iommu_groups/4/devices/0000:01:00.1
/sys/kernel/iommu_groups/4/devices/0000:00:02.0
/sys/kernel/iommu_groups/4/devices/0000:01:00.0
/sys/kernel/iommu_groups/2
/sys/kernel/iommu_groups/2/devices
/sys/kernel/iommu_groups/2/devices/e0900000.xgmac
/sys/kernel/iommu_groups/0
/sys/kernel/iommu_groups/0/devices
/sys/kernel/iommu_groups/0/devices/f0000000.pcie

Thanks

Eric

> 
> Robin
> 
> --->8---
> diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
> index fa892d25004d..ac4aab97c93a 100644
> --- a/drivers/iommu/arm-smmu.c
> +++ b/drivers/iommu/arm-smmu.c
> @@ -477,7 +477,7 @@ static int __find_legacy_master_phandle(struct
> device *dev, void *data)
>  			return 1;
>  		}
>  	it->node = np;
> -	return err;
> +	return err == -ENOENT ? 0 : err;
>  }
> 
>  static struct platform_driver arm_smmu_driver;
> 
> 
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
> 
--
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] 104+ messages in thread

* [PATCH v7 00/22] Generic DT bindings for PCI IOMMUs and ARM SMMU
@ 2016-09-14 12:32             ` Auger Eric
  0 siblings, 0 replies; 104+ messages in thread
From: Auger Eric @ 2016-09-14 12:32 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,
On 14/09/2016 12:35, Robin Murphy wrote:
> On 14/09/16 09:41, Auger Eric wrote:
>> Hi,
>>
>> On 12/09/2016 18:13, Robin Murphy wrote:
>>> Hi all,
>>>
>>> To any more confusing fixups and crazily numbered extra patches, here's
>>> a quick v7 with everything rebased into the right order. The significant
>>> change this time is to implement iommu_fwspec properly from the start,
>>> which ends up being far simpler and more robust than faffing about
>>> introducing it somewhere 'less intrusive' to move toward core code later.
>>>
>>> New branch in the logical place:
>>>
>>> git://linux-arm.org/linux-rm iommu/generic-v7
>>
>> For information, as discussed privately with Robin I experience some
>> regressions with the former and now deprecated dt description.
>>
>> on my AMD Overdrive board and my old dt description I now only see a
>> single group:
>>
>> /sys/kernel/iommu_groups/
>> /sys/kernel/iommu_groups/0
>> /sys/kernel/iommu_groups/0/devices
>> /sys/kernel/iommu_groups/0/devices/e0700000.xgmac
>>
>> whereas I formerly see
>>
>> /sys/kernel/iommu_groups/
>> /sys/kernel/iommu_groups/3
>> /sys/kernel/iommu_groups/3/devices
>> /sys/kernel/iommu_groups/3/devices/0000:00:00.0
>> /sys/kernel/iommu_groups/1
>> /sys/kernel/iommu_groups/1/devices
>> /sys/kernel/iommu_groups/1/devices/e0700000.xgmac
>> /sys/kernel/iommu_groups/4
>> /sys/kernel/iommu_groups/4/devices
>> /sys/kernel/iommu_groups/4/devices/0000:00:02.2
>> /sys/kernel/iommu_groups/4/devices/0000:01:00.1
>> /sys/kernel/iommu_groups/4/devices/0000:00:02.0
>> /sys/kernel/iommu_groups/4/devices/0000:01:00.0
>> /sys/kernel/iommu_groups/2
>> /sys/kernel/iommu_groups/2/devices
>> /sys/kernel/iommu_groups/2/devices/e0900000.xgmac
>> /sys/kernel/iommu_groups/0
>> /sys/kernel/iommu_groups/0/devices
>> /sys/kernel/iommu_groups/0/devices/f0000000.pcie
>>
>> This is the group topology without ACS override. Applying the non
>> upstreamed "pci: Enable overrides for missing ACS capabilities" I used
>> to see separate groups for each PCIe components. Now I don't see any
>> difference with and without ACS override.
> 
> OK, having reproduced on my Juno, the problem looks to be that
> of_for_each_phandle() leaves err set to -ENOENT after successfully
> walking a phandle list, which makes __find_legacy_master_phandle()
> always bail out after the first SMMU.
> 
> Can you confirm that the following diff fixes things for you?

Well it improves but there are still differences in the group topology.
The PFs now are in group 0.

root at trusty:~# lspci -nk
00:00.0 0600: 1022:1a00
        Subsystem: 1022:1a00
00:02.0 0600: 1022:1a01
00:02.2 0604: 1022:1a02
        Kernel driver in use: pcieport
01:00.0 0200: 8086:1521 (rev 01)
        Subsystem: 8086:0002
        Kernel driver in use: igb
01:00.1 0200: 8086:1521 (rev 01)
        Subsystem: 8086:0002
        Kernel driver in use: igb


with your series + fix:
/sys/kernel/iommu_groups/
/sys/kernel/iommu_groups/3
/sys/kernel/iommu_groups/3/devices
/sys/kernel/iommu_groups/3/devices/0000:00:00.0
/sys/kernel/iommu_groups/1
/sys/kernel/iommu_groups/1/devices
/sys/kernel/iommu_groups/1/devices/e0700000.xgmac
/sys/kernel/iommu_groups/4
/sys/kernel/iommu_groups/4/devices
/sys/kernel/iommu_groups/4/devices/0000:00:02.2
/sys/kernel/iommu_groups/4/devices/0000:00:02.0
/sys/kernel/iommu_groups/2
/sys/kernel/iommu_groups/2/devices
/sys/kernel/iommu_groups/2/devices/e0900000.xgmac
/sys/kernel/iommu_groups/0
/sys/kernel/iommu_groups/0/devices
/sys/kernel/iommu_groups/0/devices/0000:01:00.1
/sys/kernel/iommu_groups/0/devices/f0000000.pcie
/sys/kernel/iommu_groups/0/devices/0000:01:00.0

Before (4.8-rc5):

/sys/kernel/iommu_groups/
/sys/kernel/iommu_groups/3
/sys/kernel/iommu_groups/3/devices
/sys/kernel/iommu_groups/3/devices/0000:00:00.0
/sys/kernel/iommu_groups/1
/sys/kernel/iommu_groups/1/devices
/sys/kernel/iommu_groups/1/devices/e0700000.xgmac
/sys/kernel/iommu_groups/4
/sys/kernel/iommu_groups/4/devices
/sys/kernel/iommu_groups/4/devices/0000:00:02.2
/sys/kernel/iommu_groups/4/devices/0000:01:00.1
/sys/kernel/iommu_groups/4/devices/0000:00:02.0
/sys/kernel/iommu_groups/4/devices/0000:01:00.0
/sys/kernel/iommu_groups/2
/sys/kernel/iommu_groups/2/devices
/sys/kernel/iommu_groups/2/devices/e0900000.xgmac
/sys/kernel/iommu_groups/0
/sys/kernel/iommu_groups/0/devices
/sys/kernel/iommu_groups/0/devices/f0000000.pcie

Thanks

Eric

> 
> Robin
> 
> --->8---
> diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
> index fa892d25004d..ac4aab97c93a 100644
> --- a/drivers/iommu/arm-smmu.c
> +++ b/drivers/iommu/arm-smmu.c
> @@ -477,7 +477,7 @@ static int __find_legacy_master_phandle(struct
> device *dev, void *data)
>  			return 1;
>  		}
>  	it->node = np;
> -	return err;
> +	return err == -ENOENT ? 0 : err;
>  }
> 
>  static struct platform_driver arm_smmu_driver;
> 
> 
> _______________________________________________
> 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] 104+ messages in thread

* Re: [PATCH v7 22/22] iommu/dma: Avoid PCI host bridge windows
  2016-09-14 11:10             ` Robin Murphy
@ 2016-09-14 12:35                 ` Marek Szyprowski
  -1 siblings, 0 replies; 104+ messages in thread
From: Marek Szyprowski @ 2016-09-14 12:35 UTC (permalink / raw)
  To: Robin Murphy, will.deacon-5wv7dgnIgG8,
	joro-zLv9SwRftAIdnm+yROfE0A,
	iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, lorenzo.pieralisi-5wv7dgnIgG8,
	jean-philippe.brucker-5wv7dgnIgG8, punit.agrawal-5wv7dgnIgG8,
	thunder.leizhen-hv44wF8Li93QT0dZR+AlfA,
	eric.auger-H+wXaHxf7aLQT0dZR+AlfA, Inki Dae

Hi Robin,


On 2016-09-14 13:10, Robin Murphy wrote:
> On 14/09/16 11:55, Marek Szyprowski wrote:
>> On 2016-09-12 18:14, Robin Murphy wrote:
>>> With our DMA ops enabled for PCI devices, we should avoid allocating
>>> IOVAs which a host bridge might misinterpret as peer-to-peer DMA and
>>> lead to faults, corruption or other badness. To be safe, punch out holes
>>> for all of the relevant host bridge's windows when initialising a DMA
>>> domain for a PCI device.
>>>
>>> CC: Marek Szyprowski <m.szyprowski-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
>>> CC: Inki Dae <inki.dae-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
>>> Reported-by: Lorenzo Pieralisi <lorenzo.pieralisi-5wv7dgnIgG8@public.gmane.org>
>>> Signed-off-by: Robin Murphy <robin.murphy-5wv7dgnIgG8@public.gmane.org>
>> I don't know much about PCI and their IOMMU integration, but can't we use
>> the direct mapping region feature of iommu core for it? There are already
>> iommu_get_dm_regions(), iommu_put_dm_regions() and
>> iommu_request_dm_for_dev()
>> functions for handling them...
> It's rather the opposite problem - in the direct-mapping case, we're
> making sure the iommu_domain has translations installed for the given
> IOVAs (which are also the corresponding physical address) before it goes
> live, whereas what we need to do here is make sure the these addresses
> never get used as IOVAs at all, because any attempt to do so them will
> likely go wrong. Thus we carve them out of the iova_domain such that
> they will never get near an actual IOMMU API call.
>
> This is a slightly generalised equivalent of e.g. amd_iommu.c's
> init_reserved_iova_ranges().

Hmmm. Each dm_region have protection parameter. Can't we reuse them to
create prohibited/reserved regions by setting it to 0 (no read / no write)
and mapping to physical 0 address? That's just a quick idea, because
dm_regions and the proposed code for pci looks a bit similar for me...

[...]

Best regards
-- 
Marek Szyprowski, PhD
Samsung R&D Institute Poland

--
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] 104+ messages in thread

* [PATCH v7 22/22] iommu/dma: Avoid PCI host bridge windows
@ 2016-09-14 12:35                 ` Marek Szyprowski
  0 siblings, 0 replies; 104+ messages in thread
From: Marek Szyprowski @ 2016-09-14 12:35 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Robin,


On 2016-09-14 13:10, Robin Murphy wrote:
> On 14/09/16 11:55, Marek Szyprowski wrote:
>> On 2016-09-12 18:14, Robin Murphy wrote:
>>> With our DMA ops enabled for PCI devices, we should avoid allocating
>>> IOVAs which a host bridge might misinterpret as peer-to-peer DMA and
>>> lead to faults, corruption or other badness. To be safe, punch out holes
>>> for all of the relevant host bridge's windows when initialising a DMA
>>> domain for a PCI device.
>>>
>>> CC: Marek Szyprowski <m.szyprowski@samsung.com>
>>> CC: Inki Dae <inki.dae@samsung.com>
>>> Reported-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
>>> Signed-off-by: Robin Murphy <robin.murphy@arm.com>
>> I don't know much about PCI and their IOMMU integration, but can't we use
>> the direct mapping region feature of iommu core for it? There are already
>> iommu_get_dm_regions(), iommu_put_dm_regions() and
>> iommu_request_dm_for_dev()
>> functions for handling them...
> It's rather the opposite problem - in the direct-mapping case, we're
> making sure the iommu_domain has translations installed for the given
> IOVAs (which are also the corresponding physical address) before it goes
> live, whereas what we need to do here is make sure the these addresses
> never get used as IOVAs at all, because any attempt to do so them will
> likely go wrong. Thus we carve them out of the iova_domain such that
> they will never get near an actual IOMMU API call.
>
> This is a slightly generalised equivalent of e.g. amd_iommu.c's
> init_reserved_iova_ranges().

Hmmm. Each dm_region have protection parameter. Can't we reuse them to
create prohibited/reserved regions by setting it to 0 (no read / no write)
and mapping to physical 0 address? That's just a quick idea, because
dm_regions and the proposed code for pci looks a bit similar for me...

[...]

Best regards
-- 
Marek Szyprowski, PhD
Samsung R&D Institute Poland

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

* Re: [PATCH v7 00/22] Generic DT bindings for PCI IOMMUs and ARM SMMU
  2016-09-14 12:32             ` Auger Eric
@ 2016-09-14 12:53                 ` Robin Murphy
  -1 siblings, 0 replies; 104+ messages in thread
From: Robin Murphy @ 2016-09-14 12:53 UTC (permalink / raw)
  To: Auger Eric, will.deacon-5wv7dgnIgG8, joro-zLv9SwRftAIdnm+yROfE0A,
	iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, punit.agrawal-5wv7dgnIgG8,
	thunder.leizhen-hv44wF8Li93QT0dZR+AlfA

On 14/09/16 13:32, Auger Eric wrote:
> Hi,
> On 14/09/2016 12:35, Robin Murphy wrote:
>> On 14/09/16 09:41, Auger Eric wrote:
>>> Hi,
>>>
>>> On 12/09/2016 18:13, Robin Murphy wrote:
>>>> Hi all,
>>>>
>>>> To any more confusing fixups and crazily numbered extra patches, here's
>>>> a quick v7 with everything rebased into the right order. The significant
>>>> change this time is to implement iommu_fwspec properly from the start,
>>>> which ends up being far simpler and more robust than faffing about
>>>> introducing it somewhere 'less intrusive' to move toward core code later.
>>>>
>>>> New branch in the logical place:
>>>>
>>>> git://linux-arm.org/linux-rm iommu/generic-v7
>>>
>>> For information, as discussed privately with Robin I experience some
>>> regressions with the former and now deprecated dt description.
>>>
>>> on my AMD Overdrive board and my old dt description I now only see a
>>> single group:
>>>
>>> /sys/kernel/iommu_groups/
>>> /sys/kernel/iommu_groups/0
>>> /sys/kernel/iommu_groups/0/devices
>>> /sys/kernel/iommu_groups/0/devices/e0700000.xgmac
>>>
>>> whereas I formerly see
>>>
>>> /sys/kernel/iommu_groups/
>>> /sys/kernel/iommu_groups/3
>>> /sys/kernel/iommu_groups/3/devices
>>> /sys/kernel/iommu_groups/3/devices/0000:00:00.0
>>> /sys/kernel/iommu_groups/1
>>> /sys/kernel/iommu_groups/1/devices
>>> /sys/kernel/iommu_groups/1/devices/e0700000.xgmac
>>> /sys/kernel/iommu_groups/4
>>> /sys/kernel/iommu_groups/4/devices
>>> /sys/kernel/iommu_groups/4/devices/0000:00:02.2
>>> /sys/kernel/iommu_groups/4/devices/0000:01:00.1
>>> /sys/kernel/iommu_groups/4/devices/0000:00:02.0
>>> /sys/kernel/iommu_groups/4/devices/0000:01:00.0
>>> /sys/kernel/iommu_groups/2
>>> /sys/kernel/iommu_groups/2/devices
>>> /sys/kernel/iommu_groups/2/devices/e0900000.xgmac
>>> /sys/kernel/iommu_groups/0
>>> /sys/kernel/iommu_groups/0/devices
>>> /sys/kernel/iommu_groups/0/devices/f0000000.pcie
>>>
>>> This is the group topology without ACS override. Applying the non
>>> upstreamed "pci: Enable overrides for missing ACS capabilities" I used
>>> to see separate groups for each PCIe components. Now I don't see any
>>> difference with and without ACS override.
>>
>> OK, having reproduced on my Juno, the problem looks to be that
>> of_for_each_phandle() leaves err set to -ENOENT after successfully
>> walking a phandle list, which makes __find_legacy_master_phandle()
>> always bail out after the first SMMU.
>>
>> Can you confirm that the following diff fixes things for you?
> 
> Well it improves but there are still differences in the group topology.
> The PFs now are in group 0.
> 
> root@trusty:~# lspci -nk
> 00:00.0 0600: 1022:1a00
>         Subsystem: 1022:1a00
> 00:02.0 0600: 1022:1a01
> 00:02.2 0604: 1022:1a02
>         Kernel driver in use: pcieport
> 01:00.0 0200: 8086:1521 (rev 01)
>         Subsystem: 8086:0002
>         Kernel driver in use: igb
> 01:00.1 0200: 8086:1521 (rev 01)
>         Subsystem: 8086:0002
>         Kernel driver in use: igb
> 
> 
> with your series + fix:
> /sys/kernel/iommu_groups/
> /sys/kernel/iommu_groups/3
> /sys/kernel/iommu_groups/3/devices
> /sys/kernel/iommu_groups/3/devices/0000:00:00.0
> /sys/kernel/iommu_groups/1
> /sys/kernel/iommu_groups/1/devices
> /sys/kernel/iommu_groups/1/devices/e0700000.xgmac
> /sys/kernel/iommu_groups/4
> /sys/kernel/iommu_groups/4/devices
> /sys/kernel/iommu_groups/4/devices/0000:00:02.2
> /sys/kernel/iommu_groups/4/devices/0000:00:02.0
> /sys/kernel/iommu_groups/2
> /sys/kernel/iommu_groups/2/devices
> /sys/kernel/iommu_groups/2/devices/e0900000.xgmac
> /sys/kernel/iommu_groups/0
> /sys/kernel/iommu_groups/0/devices
> /sys/kernel/iommu_groups/0/devices/0000:01:00.1
> /sys/kernel/iommu_groups/0/devices/f0000000.pcie
> /sys/kernel/iommu_groups/0/devices/0000:01:00.0
> 
> Before (4.8-rc5):
> 
> /sys/kernel/iommu_groups/
> /sys/kernel/iommu_groups/3
> /sys/kernel/iommu_groups/3/devices
> /sys/kernel/iommu_groups/3/devices/0000:00:00.0
> /sys/kernel/iommu_groups/1
> /sys/kernel/iommu_groups/1/devices
> /sys/kernel/iommu_groups/1/devices/e0700000.xgmac
> /sys/kernel/iommu_groups/4
> /sys/kernel/iommu_groups/4/devices
> /sys/kernel/iommu_groups/4/devices/0000:00:02.2
> /sys/kernel/iommu_groups/4/devices/0000:01:00.1
> /sys/kernel/iommu_groups/4/devices/0000:00:02.0
> /sys/kernel/iommu_groups/4/devices/0000:01:00.0
> /sys/kernel/iommu_groups/2
> /sys/kernel/iommu_groups/2/devices
> /sys/kernel/iommu_groups/2/devices/e0900000.xgmac
> /sys/kernel/iommu_groups/0
> /sys/kernel/iommu_groups/0/devices
> /sys/kernel/iommu_groups/0/devices/f0000000.pcie

Your DT claims that f0000000.pcie (i.e. the "platform device" side of
the host controller) owns the IDs 0x100 0x101 0x102 0x103 0x200 0x201
0x202 0x203 0x300 0x301 0x302 0x303 0x400 0x401 0x402 0x403. Thus when
new devices (the PCI PFs) come along *also* claiming those IDs (via the
RID-to-SID assumption), we now detect the aliasing and assign them to
the existing group.

The only way that DT worked without SMR conflicts before is that
f0000000.pcie wasn't actually getting attached to a domain, thus wasn't
getting SMRs allocated (ISTR you _did_ get conflicts back when we first
tried to switch on default domains; that was probably the reason).

Robin.

> 
> Thanks
> 
> Eric
> 
>>
>> Robin
>>
>> --->8---
>> diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
>> index fa892d25004d..ac4aab97c93a 100644
>> --- a/drivers/iommu/arm-smmu.c
>> +++ b/drivers/iommu/arm-smmu.c
>> @@ -477,7 +477,7 @@ static int __find_legacy_master_phandle(struct
>> device *dev, void *data)
>>  			return 1;
>>  		}
>>  	it->node = np;
>> -	return err;
>> +	return err == -ENOENT ? 0 : err;
>>  }
>>
>>  static struct platform_driver arm_smmu_driver;
>>
>>
>> _______________________________________________
>> linux-arm-kernel mailing list
>> linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org
>> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>>
> 

--
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] 104+ messages in thread

* [PATCH v7 00/22] Generic DT bindings for PCI IOMMUs and ARM SMMU
@ 2016-09-14 12:53                 ` Robin Murphy
  0 siblings, 0 replies; 104+ messages in thread
From: Robin Murphy @ 2016-09-14 12:53 UTC (permalink / raw)
  To: linux-arm-kernel

On 14/09/16 13:32, Auger Eric wrote:
> Hi,
> On 14/09/2016 12:35, Robin Murphy wrote:
>> On 14/09/16 09:41, Auger Eric wrote:
>>> Hi,
>>>
>>> On 12/09/2016 18:13, Robin Murphy wrote:
>>>> Hi all,
>>>>
>>>> To any more confusing fixups and crazily numbered extra patches, here's
>>>> a quick v7 with everything rebased into the right order. The significant
>>>> change this time is to implement iommu_fwspec properly from the start,
>>>> which ends up being far simpler and more robust than faffing about
>>>> introducing it somewhere 'less intrusive' to move toward core code later.
>>>>
>>>> New branch in the logical place:
>>>>
>>>> git://linux-arm.org/linux-rm iommu/generic-v7
>>>
>>> For information, as discussed privately with Robin I experience some
>>> regressions with the former and now deprecated dt description.
>>>
>>> on my AMD Overdrive board and my old dt description I now only see a
>>> single group:
>>>
>>> /sys/kernel/iommu_groups/
>>> /sys/kernel/iommu_groups/0
>>> /sys/kernel/iommu_groups/0/devices
>>> /sys/kernel/iommu_groups/0/devices/e0700000.xgmac
>>>
>>> whereas I formerly see
>>>
>>> /sys/kernel/iommu_groups/
>>> /sys/kernel/iommu_groups/3
>>> /sys/kernel/iommu_groups/3/devices
>>> /sys/kernel/iommu_groups/3/devices/0000:00:00.0
>>> /sys/kernel/iommu_groups/1
>>> /sys/kernel/iommu_groups/1/devices
>>> /sys/kernel/iommu_groups/1/devices/e0700000.xgmac
>>> /sys/kernel/iommu_groups/4
>>> /sys/kernel/iommu_groups/4/devices
>>> /sys/kernel/iommu_groups/4/devices/0000:00:02.2
>>> /sys/kernel/iommu_groups/4/devices/0000:01:00.1
>>> /sys/kernel/iommu_groups/4/devices/0000:00:02.0
>>> /sys/kernel/iommu_groups/4/devices/0000:01:00.0
>>> /sys/kernel/iommu_groups/2
>>> /sys/kernel/iommu_groups/2/devices
>>> /sys/kernel/iommu_groups/2/devices/e0900000.xgmac
>>> /sys/kernel/iommu_groups/0
>>> /sys/kernel/iommu_groups/0/devices
>>> /sys/kernel/iommu_groups/0/devices/f0000000.pcie
>>>
>>> This is the group topology without ACS override. Applying the non
>>> upstreamed "pci: Enable overrides for missing ACS capabilities" I used
>>> to see separate groups for each PCIe components. Now I don't see any
>>> difference with and without ACS override.
>>
>> OK, having reproduced on my Juno, the problem looks to be that
>> of_for_each_phandle() leaves err set to -ENOENT after successfully
>> walking a phandle list, which makes __find_legacy_master_phandle()
>> always bail out after the first SMMU.
>>
>> Can you confirm that the following diff fixes things for you?
> 
> Well it improves but there are still differences in the group topology.
> The PFs now are in group 0.
> 
> root at trusty:~# lspci -nk
> 00:00.0 0600: 1022:1a00
>         Subsystem: 1022:1a00
> 00:02.0 0600: 1022:1a01
> 00:02.2 0604: 1022:1a02
>         Kernel driver in use: pcieport
> 01:00.0 0200: 8086:1521 (rev 01)
>         Subsystem: 8086:0002
>         Kernel driver in use: igb
> 01:00.1 0200: 8086:1521 (rev 01)
>         Subsystem: 8086:0002
>         Kernel driver in use: igb
> 
> 
> with your series + fix:
> /sys/kernel/iommu_groups/
> /sys/kernel/iommu_groups/3
> /sys/kernel/iommu_groups/3/devices
> /sys/kernel/iommu_groups/3/devices/0000:00:00.0
> /sys/kernel/iommu_groups/1
> /sys/kernel/iommu_groups/1/devices
> /sys/kernel/iommu_groups/1/devices/e0700000.xgmac
> /sys/kernel/iommu_groups/4
> /sys/kernel/iommu_groups/4/devices
> /sys/kernel/iommu_groups/4/devices/0000:00:02.2
> /sys/kernel/iommu_groups/4/devices/0000:00:02.0
> /sys/kernel/iommu_groups/2
> /sys/kernel/iommu_groups/2/devices
> /sys/kernel/iommu_groups/2/devices/e0900000.xgmac
> /sys/kernel/iommu_groups/0
> /sys/kernel/iommu_groups/0/devices
> /sys/kernel/iommu_groups/0/devices/0000:01:00.1
> /sys/kernel/iommu_groups/0/devices/f0000000.pcie
> /sys/kernel/iommu_groups/0/devices/0000:01:00.0
> 
> Before (4.8-rc5):
> 
> /sys/kernel/iommu_groups/
> /sys/kernel/iommu_groups/3
> /sys/kernel/iommu_groups/3/devices
> /sys/kernel/iommu_groups/3/devices/0000:00:00.0
> /sys/kernel/iommu_groups/1
> /sys/kernel/iommu_groups/1/devices
> /sys/kernel/iommu_groups/1/devices/e0700000.xgmac
> /sys/kernel/iommu_groups/4
> /sys/kernel/iommu_groups/4/devices
> /sys/kernel/iommu_groups/4/devices/0000:00:02.2
> /sys/kernel/iommu_groups/4/devices/0000:01:00.1
> /sys/kernel/iommu_groups/4/devices/0000:00:02.0
> /sys/kernel/iommu_groups/4/devices/0000:01:00.0
> /sys/kernel/iommu_groups/2
> /sys/kernel/iommu_groups/2/devices
> /sys/kernel/iommu_groups/2/devices/e0900000.xgmac
> /sys/kernel/iommu_groups/0
> /sys/kernel/iommu_groups/0/devices
> /sys/kernel/iommu_groups/0/devices/f0000000.pcie

Your DT claims that f0000000.pcie (i.e. the "platform device" side of
the host controller) owns the IDs 0x100 0x101 0x102 0x103 0x200 0x201
0x202 0x203 0x300 0x301 0x302 0x303 0x400 0x401 0x402 0x403. Thus when
new devices (the PCI PFs) come along *also* claiming those IDs (via the
RID-to-SID assumption), we now detect the aliasing and assign them to
the existing group.

The only way that DT worked without SMR conflicts before is that
f0000000.pcie wasn't actually getting attached to a domain, thus wasn't
getting SMRs allocated (ISTR you _did_ get conflicts back when we first
tried to switch on default domains; that was probably the reason).

Robin.

> 
> Thanks
> 
> Eric
> 
>>
>> Robin
>>
>> --->8---
>> diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
>> index fa892d25004d..ac4aab97c93a 100644
>> --- a/drivers/iommu/arm-smmu.c
>> +++ b/drivers/iommu/arm-smmu.c
>> @@ -477,7 +477,7 @@ static int __find_legacy_master_phandle(struct
>> device *dev, void *data)
>>  			return 1;
>>  		}
>>  	it->node = np;
>> -	return err;
>> +	return err == -ENOENT ? 0 : err;
>>  }
>>
>>  static struct platform_driver arm_smmu_driver;
>>
>>
>> _______________________________________________
>> 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] 104+ messages in thread

* Re: [PATCH v7 22/22] iommu/dma: Avoid PCI host bridge windows
  2016-09-14 12:35                 ` Marek Szyprowski
@ 2016-09-14 13:25                     ` Robin Murphy
  -1 siblings, 0 replies; 104+ messages in thread
From: Robin Murphy @ 2016-09-14 13:25 UTC (permalink / raw)
  To: Marek Szyprowski, will.deacon-5wv7dgnIgG8,
	joro-zLv9SwRftAIdnm+yROfE0A,
	iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, punit.agrawal-5wv7dgnIgG8,
	Inki Dae, thunder.leizhen-hv44wF8Li93QT0dZR+AlfA

On 14/09/16 13:35, Marek Szyprowski wrote:
> Hi Robin,
> 
> 
> On 2016-09-14 13:10, Robin Murphy wrote:
>> On 14/09/16 11:55, Marek Szyprowski wrote:
>>> On 2016-09-12 18:14, Robin Murphy wrote:
>>>> With our DMA ops enabled for PCI devices, we should avoid allocating
>>>> IOVAs which a host bridge might misinterpret as peer-to-peer DMA and
>>>> lead to faults, corruption or other badness. To be safe, punch out
>>>> holes
>>>> for all of the relevant host bridge's windows when initialising a DMA
>>>> domain for a PCI device.
>>>>
>>>> CC: Marek Szyprowski <m.szyprowski-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
>>>> CC: Inki Dae <inki.dae-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
>>>> Reported-by: Lorenzo Pieralisi <lorenzo.pieralisi-5wv7dgnIgG8@public.gmane.org>
>>>> Signed-off-by: Robin Murphy <robin.murphy-5wv7dgnIgG8@public.gmane.org>
>>> I don't know much about PCI and their IOMMU integration, but can't we
>>> use
>>> the direct mapping region feature of iommu core for it? There are
>>> already
>>> iommu_get_dm_regions(), iommu_put_dm_regions() and
>>> iommu_request_dm_for_dev()
>>> functions for handling them...
>> It's rather the opposite problem - in the direct-mapping case, we're
>> making sure the iommu_domain has translations installed for the given
>> IOVAs (which are also the corresponding physical address) before it goes
>> live, whereas what we need to do here is make sure the these addresses
>> never get used as IOVAs at all, because any attempt to do so them will
>> likely go wrong. Thus we carve them out of the iova_domain such that
>> they will never get near an actual IOMMU API call.
>>
>> This is a slightly generalised equivalent of e.g. amd_iommu.c's
>> init_reserved_iova_ranges().
> 
> Hmmm. Each dm_region have protection parameter. Can't we reuse them to
> create prohibited/reserved regions by setting it to 0 (no read / no write)
> and mapping to physical 0 address? That's just a quick idea, because
> dm_regions and the proposed code for pci looks a bit similar for me...

It might look similar, but at different levels (iommu_domain vs.
iova_domain) and with the opposite intent. The dm_region prot flag is
just the standard flag as passed to iommu_map() - trying to map a region
with no access in an empty pagetable isn't going to achieve anything
anyway (it's effectively unmapping addresses that are already unmapped).

But for this case, even if you _did_ map something in the pagetable
(i.e. the iommu_domain), it wouldn't make any difference, because the
thing we're mitigating against is handing out addresses which are going
to cause a device's accesses to blow up inside the PCI root complex
without ever even reaching the IOMMU. In short, dm_regions are about
"these addresses are already being used for DMA, so make sure the IOMMU
API doesn't block them", whereas reserved ranges are about "these
addresses are unusable for DMA, so make sure the DMA API can't allocate
them".

Robin.

> 
> [...]
> 
> Best regards

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

* [PATCH v7 22/22] iommu/dma: Avoid PCI host bridge windows
@ 2016-09-14 13:25                     ` Robin Murphy
  0 siblings, 0 replies; 104+ messages in thread
From: Robin Murphy @ 2016-09-14 13:25 UTC (permalink / raw)
  To: linux-arm-kernel

On 14/09/16 13:35, Marek Szyprowski wrote:
> Hi Robin,
> 
> 
> On 2016-09-14 13:10, Robin Murphy wrote:
>> On 14/09/16 11:55, Marek Szyprowski wrote:
>>> On 2016-09-12 18:14, Robin Murphy wrote:
>>>> With our DMA ops enabled for PCI devices, we should avoid allocating
>>>> IOVAs which a host bridge might misinterpret as peer-to-peer DMA and
>>>> lead to faults, corruption or other badness. To be safe, punch out
>>>> holes
>>>> for all of the relevant host bridge's windows when initialising a DMA
>>>> domain for a PCI device.
>>>>
>>>> CC: Marek Szyprowski <m.szyprowski@samsung.com>
>>>> CC: Inki Dae <inki.dae@samsung.com>
>>>> Reported-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
>>>> Signed-off-by: Robin Murphy <robin.murphy@arm.com>
>>> I don't know much about PCI and their IOMMU integration, but can't we
>>> use
>>> the direct mapping region feature of iommu core for it? There are
>>> already
>>> iommu_get_dm_regions(), iommu_put_dm_regions() and
>>> iommu_request_dm_for_dev()
>>> functions for handling them...
>> It's rather the opposite problem - in the direct-mapping case, we're
>> making sure the iommu_domain has translations installed for the given
>> IOVAs (which are also the corresponding physical address) before it goes
>> live, whereas what we need to do here is make sure the these addresses
>> never get used as IOVAs at all, because any attempt to do so them will
>> likely go wrong. Thus we carve them out of the iova_domain such that
>> they will never get near an actual IOMMU API call.
>>
>> This is a slightly generalised equivalent of e.g. amd_iommu.c's
>> init_reserved_iova_ranges().
> 
> Hmmm. Each dm_region have protection parameter. Can't we reuse them to
> create prohibited/reserved regions by setting it to 0 (no read / no write)
> and mapping to physical 0 address? That's just a quick idea, because
> dm_regions and the proposed code for pci looks a bit similar for me...

It might look similar, but at different levels (iommu_domain vs.
iova_domain) and with the opposite intent. The dm_region prot flag is
just the standard flag as passed to iommu_map() - trying to map a region
with no access in an empty pagetable isn't going to achieve anything
anyway (it's effectively unmapping addresses that are already unmapped).

But for this case, even if you _did_ map something in the pagetable
(i.e. the iommu_domain), it wouldn't make any difference, because the
thing we're mitigating against is handing out addresses which are going
to cause a device's accesses to blow up inside the PCI root complex
without ever even reaching the IOMMU. In short, dm_regions are about
"these addresses are already being used for DMA, so make sure the IOMMU
API doesn't block them", whereas reserved ranges are about "these
addresses are unusable for DMA, so make sure the DMA API can't allocate
them".

Robin.

> 
> [...]
> 
> Best regards

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

* [PATCH v7.1 13/22] iommu/arm-smmu: Refactor mmu-masters handling
  2016-09-12 16:13     ` Robin Murphy
@ 2016-09-14 14:21         ` Robin Murphy
  -1 siblings, 0 replies; 104+ messages in thread
From: Robin Murphy @ 2016-09-14 14:21 UTC (permalink / raw)
  To: will.deacon-5wv7dgnIgG8, joro-zLv9SwRftAIdnm+yROfE0A,
	iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, lorenzo.pieralisi-5wv7dgnIgG8,
	jean-philippe.brucker-5wv7dgnIgG8, punit.agrawal-5wv7dgnIgG8,
	thunder.leizhen-hv44wF8Li93QT0dZR+AlfA,
	eric.auger-H+wXaHxf7aLQT0dZR+AlfA

To be able to support the generic bindings and handle of_xlate() calls,
we need to be able to associate SMMUs and stream IDs directly with
devices *before* allocating IOMMU groups. Furthermore, to support real
default domains with multi-device groups we also have to handle domain
attach on a per-device basis, as the "whole group at a time" assumption
fails to properly handle subsequent devices added to a group after the
first has already triggered default domain creation and attachment.

To that end, use the now-vacant dev->archdata.iommu field for easy
config and SMMU instance lookup, and unify config management by chopping
down the platform-device-specific tree and probing the "mmu-masters"
property on-demand instead. This may add a bit of one-off overhead to
initially adding a new device, but we're about to deprecate that binding
in favour of the inherently-more-efficient generic ones anyway.

For the sake of simplicity, this patch does temporarily regress the case
of aliasing PCI devices by losing the duplicate stream ID detection that
the previous per-group config had. Stay tuned, because we'll be back to
fix that in a better and more general way momentarily...

Tested-by: Lorenzo Pieralisi <lorenzo.pieralisi-5wv7dgnIgG8@public.gmane.org>
Signed-off-by: Robin Murphy <robin.murphy-5wv7dgnIgG8@public.gmane.org>
---

- Fix __find_legacy_master_phandle() to properly cope with more than 
  one SMMU using the legacy binding.

 drivers/iommu/arm-smmu.c | 382 +++++++++++++----------------------------------
 1 file changed, 107 insertions(+), 275 deletions(-)

diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index 69b6cab65421..2023a77015a0 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -317,18 +317,13 @@ struct arm_smmu_smr {
 };
 
 struct arm_smmu_master_cfg {
+	struct arm_smmu_device		*smmu;
 	int				num_streamids;
 	u16				streamids[MAX_MASTER_STREAMIDS];
 	s16				smendx[MAX_MASTER_STREAMIDS];
 };
 #define INVALID_SMENDX			-1
 
-struct arm_smmu_master {
-	struct device_node		*of_node;
-	struct rb_node			node;
-	struct arm_smmu_master_cfg	cfg;
-};
-
 struct arm_smmu_device {
 	struct device			*dev;
 
@@ -376,7 +371,6 @@ struct arm_smmu_device {
 	unsigned int			*irqs;
 
 	struct list_head		list;
-	struct rb_root			masters;
 
 	u32				cavium_id_base; /* Specific to Cavium */
 };
@@ -415,12 +409,6 @@ struct arm_smmu_domain {
 	struct iommu_domain		domain;
 };
 
-struct arm_smmu_phandle_args {
-	struct device_node *np;
-	int args_count;
-	uint32_t args[MAX_MASTER_STREAMIDS];
-};
-
 static DEFINE_SPINLOCK(arm_smmu_devices_lock);
 static LIST_HEAD(arm_smmu_devices);
 
@@ -462,132 +450,89 @@ static struct device_node *dev_get_dev_node(struct device *dev)
 
 		while (!pci_is_root_bus(bus))
 			bus = bus->parent;
-		return bus->bridge->parent->of_node;
+		return of_node_get(bus->bridge->parent->of_node);
 	}
 
-	return dev->of_node;
+	return of_node_get(dev->of_node);
 }
 
-static struct arm_smmu_master *find_smmu_master(struct arm_smmu_device *smmu,
-						struct device_node *dev_node)
+static int __arm_smmu_get_pci_sid(struct pci_dev *pdev, u16 alias, void *data)
 {
-	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;
+	*((__be32 *)data) = cpu_to_be32(alias);
+	return 0; /* Continue walking */
 }
 
-static struct arm_smmu_master_cfg *
-find_smmu_master_cfg(struct device *dev)
+static int __find_legacy_master_phandle(struct device *dev, void *data)
 {
-	struct arm_smmu_master_cfg *cfg = NULL;
-	struct iommu_group *group = iommu_group_get(dev);
+	struct of_phandle_iterator *it = *(void **)data;
+	struct device_node *np = it->node;
+	int err;
 
-	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)
-{
-	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 device *dev,
-				struct arm_smmu_phandle_args *masterspec)
-{
-	int i;
-	struct arm_smmu_master *master;
-
-	master = find_smmu_master(smmu, masterspec->np);
-	if (master) {
-		dev_err(dev,
-			"rejecting multiple registrations for master device %s\n",
-			masterspec->np->name);
-		return -EBUSY;
-	}
-
-	if (masterspec->args_count > MAX_MASTER_STREAMIDS) {
-		dev_err(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);
-	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(dev,
-				"stream ID for master device %s greater than maximum allowed (%d)\n",
-				masterspec->np->name, smmu->num_mapping_groups);
-			return -ERANGE;
+	of_for_each_phandle(it, err, dev->of_node, "mmu-masters",
+			    "#stream-id-cells", 0)
+		if (it->node == np) {
+			*(void **)data = dev;
+			return 1;
 		}
-		master->cfg.streamids[i] = streamid;
-		master->cfg.smendx[i] = INVALID_SMENDX;
-	}
-	return insert_smmu_master(smmu, master);
+	it->node = np;
+	return err == -ENOENT ? 0 : err;
 }
 
-static struct arm_smmu_device *find_smmu_for_device(struct device *dev)
+static int arm_smmu_register_legacy_master(struct device *dev)
 {
 	struct arm_smmu_device *smmu;
-	struct arm_smmu_master *master = NULL;
-	struct device_node *dev_node = dev_get_dev_node(dev);
+	struct arm_smmu_master_cfg *cfg;
+	struct device_node *np;
+	struct of_phandle_iterator it;
+	void *data = &it;
+	__be32 pci_sid;
+	int err;
 
+	np = dev_get_dev_node(dev);
+	if (!np || !of_find_property(np, "#stream-id-cells", NULL)) {
+		of_node_put(np);
+		return -ENODEV;
+	}
+
+	it.node = np;
 	spin_lock(&arm_smmu_devices_lock);
 	list_for_each_entry(smmu, &arm_smmu_devices, list) {
-		master = find_smmu_master(smmu, dev_node);
-		if (master)
+		err = __find_legacy_master_phandle(smmu->dev, &data);
+		if (err)
 			break;
 	}
 	spin_unlock(&arm_smmu_devices_lock);
+	of_node_put(np);
+	if (err == 0)
+		return -ENODEV;
+	if (err < 0)
+		return err;
 
-	return master ? smmu : NULL;
+	if (it.cur_count > MAX_MASTER_STREAMIDS) {
+		dev_err(smmu->dev,
+			"reached maximum number (%d) of stream IDs for master device %s\n",
+			MAX_MASTER_STREAMIDS, dev_name(dev));
+		return -ENOSPC;
+	}
+	if (dev_is_pci(dev)) {
+		/* "mmu-masters" assumes Stream ID == Requester ID */
+		pci_for_each_dma_alias(to_pci_dev(dev), __arm_smmu_get_pci_sid,
+				       &pci_sid);
+		it.cur = &pci_sid;
+		it.cur_count = 1;
+	}
+
+	cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
+	if (!cfg)
+		return -ENOMEM;
+
+	cfg->smmu = smmu;
+	dev->archdata.iommu = cfg;
+
+	while (it.cur_count--)
+		cfg->streamids[cfg->num_streamids++] = be32_to_cpup(it.cur++);
+
+	return 0;
 }
 
 static int __arm_smmu_alloc_bitmap(unsigned long *map, int start, int end)
@@ -1119,8 +1064,7 @@ static void arm_smmu_free_smr(struct arm_smmu_device *smmu, int idx)
 static void arm_smmu_write_smr(struct arm_smmu_device *smmu, int idx)
 {
 	struct arm_smmu_smr *smr = smmu->smrs + idx;
-	u32 reg = (smr->id & smmu->streamid_mask) << SMR_ID_SHIFT |
-		  (smr->mask & smmu->smr_mask_mask) << SMR_MASK_SHIFT;
+	u32 reg = smr->id << SMR_ID_SHIFT | smr->mask << SMR_MASK_SHIFT;
 
 	if (smr->valid)
 		reg |= SMR_VALID;
@@ -1189,9 +1133,9 @@ err_free_smrs:
 	return -ENOSPC;
 }
 
-static void arm_smmu_master_free_smes(struct arm_smmu_device *smmu,
-				      struct arm_smmu_master_cfg *cfg)
+static void arm_smmu_master_free_smes(struct arm_smmu_master_cfg *cfg)
 {
+	struct arm_smmu_device *smmu = cfg->smmu;
 	int i;
 
 	/*
@@ -1262,17 +1206,15 @@ 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;
 	}
 
 	/* Ensure that the domain is finalised */
-	ret = arm_smmu_init_domain_context(domain, smmu);
+	ret = arm_smmu_init_domain_context(domain, cfg->smmu);
 	if (ret < 0)
 		return ret;
 
@@ -1280,18 +1222,14 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
 	 * Sanity check the domain. We don't support domains across
 	 * different SMMUs.
 	 */
-	if (smmu_domain->smmu != smmu) {
+	if (smmu_domain->smmu != cfg->smmu) {
 		dev_err(dev,
 			"cannot attach to SMMU %s whilst already attached to domain on SMMU %s\n",
-			dev_name(smmu_domain->smmu->dev), dev_name(smmu->dev));
+			dev_name(smmu_domain->smmu->dev), dev_name(cfg->smmu->dev));
 		return -EINVAL;
 	}
 
 	/* Looks ok, so add the device to the domain */
-	cfg = find_smmu_master_cfg(dev);
-	if (!cfg)
-		return -ENODEV;
-
 	return arm_smmu_domain_add_master(smmu_domain, cfg);
 }
 
@@ -1411,120 +1349,65 @@ static bool arm_smmu_capable(enum iommu_cap cap)
 	}
 }
 
-static int __arm_smmu_get_pci_sid(struct pci_dev *pdev, u16 alias, void *data)
-{
-	*((u16 *)data) = alias;
-	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)
-{
-	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;
-
-	/*
-	 * 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[i] = sid;
-		cfg->smendx[i] = INVALID_SMENDX;
-		cfg->num_streamids++;
-	}
-
-	return 0;
-}
-
-static int arm_smmu_init_platform_device(struct device *dev,
-					 struct iommu_group *group)
-{
-	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);
-
-	return 0;
-}
-
 static int arm_smmu_add_device(struct device *dev)
 {
+	struct arm_smmu_master_cfg *cfg;
 	struct iommu_group *group;
+	int i, ret;
+
+	ret = arm_smmu_register_legacy_master(dev);
+	cfg = dev->archdata.iommu;
+	if (ret)
+		goto out_free;
+
+	ret = -EINVAL;
+	for (i = 0; i < cfg->num_streamids; i++) {
+		u16 sid = cfg->streamids[i];
+
+		if (sid & ~cfg->smmu->streamid_mask) {
+			dev_err(dev, "stream ID 0x%x out of range for SMMU (0x%x)\n",
+				sid, cfg->smmu->streamid_mask);
+			goto out_free;
+		}
+		cfg->smendx[i] = INVALID_SMENDX;
+	}
 
 	group = iommu_group_get_for_dev(dev);
-	if (IS_ERR(group))
-		return PTR_ERR(group);
-
+	if (IS_ERR(group)) {
+		ret = PTR_ERR(group);
+		goto out_free;
+	}
 	iommu_group_put(group);
 	return 0;
+
+out_free:
+	kfree(cfg);
+	dev->archdata.iommu = NULL;
+	return ret;
 }
 
 static void arm_smmu_remove_device(struct device *dev)
 {
-	struct arm_smmu_device *smmu = find_smmu_for_device(dev);
-	struct arm_smmu_master_cfg *cfg = find_smmu_master_cfg(dev);
+	struct arm_smmu_master_cfg *cfg = dev->archdata.iommu;
 
-	if (smmu && cfg)
-		arm_smmu_master_free_smes(smmu, cfg);
+	if (!cfg)
+		return;
 
+	arm_smmu_master_free_smes(cfg);
 	iommu_group_remove_device(dev);
+	kfree(cfg);
+	dev->archdata.iommu = NULL;
 }
 
 static struct iommu_group *arm_smmu_device_group(struct device *dev)
 {
 	struct iommu_group *group;
-	int ret;
 
 	if (dev_is_pci(dev))
 		group = pci_device_group(dev);
 	else
 		group = generic_device_group(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);
-
-	if (ret) {
-		iommu_group_put(group);
-		group = ERR_PTR(ret);
-	}
-
 	return group;
 }
 
@@ -1938,9 +1821,6 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
 	struct resource *res;
 	struct arm_smmu_device *smmu;
 	struct device *dev = &pdev->dev;
-	struct rb_node *node;
-	struct of_phandle_iterator it;
-	struct arm_smmu_phandle_args *masterspec;
 	int num_irqs, i, err;
 
 	smmu = devm_kzalloc(dev, sizeof(*smmu), GFP_KERNEL);
@@ -2001,37 +1881,6 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
 	if (err)
 		return err;
 
-	i = 0;
-	smmu->masters = RB_ROOT;
-
-	err = -ENOMEM;
-	/* No need to zero the memory for masterspec */
-	masterspec = kmalloc(sizeof(*masterspec), GFP_KERNEL);
-	if (!masterspec)
-		goto out_put_masters;
-
-	of_for_each_phandle(&it, err, dev->of_node,
-			    "mmu-masters", "#stream-id-cells", 0) {
-		int count = of_phandle_iterator_args(&it, masterspec->args,
-						     MAX_MASTER_STREAMIDS);
-		masterspec->np		= of_node_get(it.node);
-		masterspec->args_count	= count;
-
-		err = register_smmu_master(smmu, dev, masterspec);
-		if (err) {
-			dev_err(dev, "failed to add master %s\n",
-				masterspec->np->name);
-			kfree(masterspec);
-			goto out_put_masters;
-		}
-
-		i++;
-	}
-
-	dev_notice(dev, "registered %d master devices\n", i);
-
-	kfree(masterspec);
-
 	parse_driver_options(smmu);
 
 	if (smmu->version == ARM_SMMU_V2 &&
@@ -2039,8 +1888,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) {
@@ -2052,7 +1900,7 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
 		if (err) {
 			dev_err(dev, "failed to request global IRQ %d (%u)\n",
 				i, smmu->irqs[i]);
-			goto out_put_masters;
+			return err;
 		}
 	}
 
@@ -2063,22 +1911,12 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
 
 	arm_smmu_device_reset(smmu);
 	return 0;
-
-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;
 }
 
 static int arm_smmu_device_remove(struct platform_device *pdev)
 {
 	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) {
@@ -2093,12 +1931,6 @@ static int arm_smmu_device_remove(struct platform_device *pdev)
 	if (!smmu)
 		return -ENODEV;
 
-	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(dev, "removing device with active domains!\n");
 
-- 
2.8.1.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] 104+ messages in thread

* [PATCH v7.1 13/22] iommu/arm-smmu: Refactor mmu-masters handling
@ 2016-09-14 14:21         ` Robin Murphy
  0 siblings, 0 replies; 104+ messages in thread
From: Robin Murphy @ 2016-09-14 14:21 UTC (permalink / raw)
  To: linux-arm-kernel

To be able to support the generic bindings and handle of_xlate() calls,
we need to be able to associate SMMUs and stream IDs directly with
devices *before* allocating IOMMU groups. Furthermore, to support real
default domains with multi-device groups we also have to handle domain
attach on a per-device basis, as the "whole group at a time" assumption
fails to properly handle subsequent devices added to a group after the
first has already triggered default domain creation and attachment.

To that end, use the now-vacant dev->archdata.iommu field for easy
config and SMMU instance lookup, and unify config management by chopping
down the platform-device-specific tree and probing the "mmu-masters"
property on-demand instead. This may add a bit of one-off overhead to
initially adding a new device, but we're about to deprecate that binding
in favour of the inherently-more-efficient generic ones anyway.

For the sake of simplicity, this patch does temporarily regress the case
of aliasing PCI devices by losing the duplicate stream ID detection that
the previous per-group config had. Stay tuned, because we'll be back to
fix that in a better and more general way momentarily...

Tested-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Signed-off-by: Robin Murphy <robin.murphy@arm.com>
---

- Fix __find_legacy_master_phandle() to properly cope with more than 
  one SMMU using the legacy binding.

 drivers/iommu/arm-smmu.c | 382 +++++++++++++----------------------------------
 1 file changed, 107 insertions(+), 275 deletions(-)

diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index 69b6cab65421..2023a77015a0 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -317,18 +317,13 @@ struct arm_smmu_smr {
 };
 
 struct arm_smmu_master_cfg {
+	struct arm_smmu_device		*smmu;
 	int				num_streamids;
 	u16				streamids[MAX_MASTER_STREAMIDS];
 	s16				smendx[MAX_MASTER_STREAMIDS];
 };
 #define INVALID_SMENDX			-1
 
-struct arm_smmu_master {
-	struct device_node		*of_node;
-	struct rb_node			node;
-	struct arm_smmu_master_cfg	cfg;
-};
-
 struct arm_smmu_device {
 	struct device			*dev;
 
@@ -376,7 +371,6 @@ struct arm_smmu_device {
 	unsigned int			*irqs;
 
 	struct list_head		list;
-	struct rb_root			masters;
 
 	u32				cavium_id_base; /* Specific to Cavium */
 };
@@ -415,12 +409,6 @@ struct arm_smmu_domain {
 	struct iommu_domain		domain;
 };
 
-struct arm_smmu_phandle_args {
-	struct device_node *np;
-	int args_count;
-	uint32_t args[MAX_MASTER_STREAMIDS];
-};
-
 static DEFINE_SPINLOCK(arm_smmu_devices_lock);
 static LIST_HEAD(arm_smmu_devices);
 
@@ -462,132 +450,89 @@ static struct device_node *dev_get_dev_node(struct device *dev)
 
 		while (!pci_is_root_bus(bus))
 			bus = bus->parent;
-		return bus->bridge->parent->of_node;
+		return of_node_get(bus->bridge->parent->of_node);
 	}
 
-	return dev->of_node;
+	return of_node_get(dev->of_node);
 }
 
-static struct arm_smmu_master *find_smmu_master(struct arm_smmu_device *smmu,
-						struct device_node *dev_node)
+static int __arm_smmu_get_pci_sid(struct pci_dev *pdev, u16 alias, void *data)
 {
-	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;
+	*((__be32 *)data) = cpu_to_be32(alias);
+	return 0; /* Continue walking */
 }
 
-static struct arm_smmu_master_cfg *
-find_smmu_master_cfg(struct device *dev)
+static int __find_legacy_master_phandle(struct device *dev, void *data)
 {
-	struct arm_smmu_master_cfg *cfg = NULL;
-	struct iommu_group *group = iommu_group_get(dev);
+	struct of_phandle_iterator *it = *(void **)data;
+	struct device_node *np = it->node;
+	int err;
 
-	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)
-{
-	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 device *dev,
-				struct arm_smmu_phandle_args *masterspec)
-{
-	int i;
-	struct arm_smmu_master *master;
-
-	master = find_smmu_master(smmu, masterspec->np);
-	if (master) {
-		dev_err(dev,
-			"rejecting multiple registrations for master device %s\n",
-			masterspec->np->name);
-		return -EBUSY;
-	}
-
-	if (masterspec->args_count > MAX_MASTER_STREAMIDS) {
-		dev_err(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);
-	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(dev,
-				"stream ID for master device %s greater than maximum allowed (%d)\n",
-				masterspec->np->name, smmu->num_mapping_groups);
-			return -ERANGE;
+	of_for_each_phandle(it, err, dev->of_node, "mmu-masters",
+			    "#stream-id-cells", 0)
+		if (it->node == np) {
+			*(void **)data = dev;
+			return 1;
 		}
-		master->cfg.streamids[i] = streamid;
-		master->cfg.smendx[i] = INVALID_SMENDX;
-	}
-	return insert_smmu_master(smmu, master);
+	it->node = np;
+	return err == -ENOENT ? 0 : err;
 }
 
-static struct arm_smmu_device *find_smmu_for_device(struct device *dev)
+static int arm_smmu_register_legacy_master(struct device *dev)
 {
 	struct arm_smmu_device *smmu;
-	struct arm_smmu_master *master = NULL;
-	struct device_node *dev_node = dev_get_dev_node(dev);
+	struct arm_smmu_master_cfg *cfg;
+	struct device_node *np;
+	struct of_phandle_iterator it;
+	void *data = &it;
+	__be32 pci_sid;
+	int err;
 
+	np = dev_get_dev_node(dev);
+	if (!np || !of_find_property(np, "#stream-id-cells", NULL)) {
+		of_node_put(np);
+		return -ENODEV;
+	}
+
+	it.node = np;
 	spin_lock(&arm_smmu_devices_lock);
 	list_for_each_entry(smmu, &arm_smmu_devices, list) {
-		master = find_smmu_master(smmu, dev_node);
-		if (master)
+		err = __find_legacy_master_phandle(smmu->dev, &data);
+		if (err)
 			break;
 	}
 	spin_unlock(&arm_smmu_devices_lock);
+	of_node_put(np);
+	if (err == 0)
+		return -ENODEV;
+	if (err < 0)
+		return err;
 
-	return master ? smmu : NULL;
+	if (it.cur_count > MAX_MASTER_STREAMIDS) {
+		dev_err(smmu->dev,
+			"reached maximum number (%d) of stream IDs for master device %s\n",
+			MAX_MASTER_STREAMIDS, dev_name(dev));
+		return -ENOSPC;
+	}
+	if (dev_is_pci(dev)) {
+		/* "mmu-masters" assumes Stream ID == Requester ID */
+		pci_for_each_dma_alias(to_pci_dev(dev), __arm_smmu_get_pci_sid,
+				       &pci_sid);
+		it.cur = &pci_sid;
+		it.cur_count = 1;
+	}
+
+	cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
+	if (!cfg)
+		return -ENOMEM;
+
+	cfg->smmu = smmu;
+	dev->archdata.iommu = cfg;
+
+	while (it.cur_count--)
+		cfg->streamids[cfg->num_streamids++] = be32_to_cpup(it.cur++);
+
+	return 0;
 }
 
 static int __arm_smmu_alloc_bitmap(unsigned long *map, int start, int end)
@@ -1119,8 +1064,7 @@ static void arm_smmu_free_smr(struct arm_smmu_device *smmu, int idx)
 static void arm_smmu_write_smr(struct arm_smmu_device *smmu, int idx)
 {
 	struct arm_smmu_smr *smr = smmu->smrs + idx;
-	u32 reg = (smr->id & smmu->streamid_mask) << SMR_ID_SHIFT |
-		  (smr->mask & smmu->smr_mask_mask) << SMR_MASK_SHIFT;
+	u32 reg = smr->id << SMR_ID_SHIFT | smr->mask << SMR_MASK_SHIFT;
 
 	if (smr->valid)
 		reg |= SMR_VALID;
@@ -1189,9 +1133,9 @@ err_free_smrs:
 	return -ENOSPC;
 }
 
-static void arm_smmu_master_free_smes(struct arm_smmu_device *smmu,
-				      struct arm_smmu_master_cfg *cfg)
+static void arm_smmu_master_free_smes(struct arm_smmu_master_cfg *cfg)
 {
+	struct arm_smmu_device *smmu = cfg->smmu;
 	int i;
 
 	/*
@@ -1262,17 +1206,15 @@ 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;
 	}
 
 	/* Ensure that the domain is finalised */
-	ret = arm_smmu_init_domain_context(domain, smmu);
+	ret = arm_smmu_init_domain_context(domain, cfg->smmu);
 	if (ret < 0)
 		return ret;
 
@@ -1280,18 +1222,14 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
 	 * Sanity check the domain. We don't support domains across
 	 * different SMMUs.
 	 */
-	if (smmu_domain->smmu != smmu) {
+	if (smmu_domain->smmu != cfg->smmu) {
 		dev_err(dev,
 			"cannot attach to SMMU %s whilst already attached to domain on SMMU %s\n",
-			dev_name(smmu_domain->smmu->dev), dev_name(smmu->dev));
+			dev_name(smmu_domain->smmu->dev), dev_name(cfg->smmu->dev));
 		return -EINVAL;
 	}
 
 	/* Looks ok, so add the device to the domain */
-	cfg = find_smmu_master_cfg(dev);
-	if (!cfg)
-		return -ENODEV;
-
 	return arm_smmu_domain_add_master(smmu_domain, cfg);
 }
 
@@ -1411,120 +1349,65 @@ static bool arm_smmu_capable(enum iommu_cap cap)
 	}
 }
 
-static int __arm_smmu_get_pci_sid(struct pci_dev *pdev, u16 alias, void *data)
-{
-	*((u16 *)data) = alias;
-	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)
-{
-	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;
-
-	/*
-	 * 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[i] = sid;
-		cfg->smendx[i] = INVALID_SMENDX;
-		cfg->num_streamids++;
-	}
-
-	return 0;
-}
-
-static int arm_smmu_init_platform_device(struct device *dev,
-					 struct iommu_group *group)
-{
-	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);
-
-	return 0;
-}
-
 static int arm_smmu_add_device(struct device *dev)
 {
+	struct arm_smmu_master_cfg *cfg;
 	struct iommu_group *group;
+	int i, ret;
+
+	ret = arm_smmu_register_legacy_master(dev);
+	cfg = dev->archdata.iommu;
+	if (ret)
+		goto out_free;
+
+	ret = -EINVAL;
+	for (i = 0; i < cfg->num_streamids; i++) {
+		u16 sid = cfg->streamids[i];
+
+		if (sid & ~cfg->smmu->streamid_mask) {
+			dev_err(dev, "stream ID 0x%x out of range for SMMU (0x%x)\n",
+				sid, cfg->smmu->streamid_mask);
+			goto out_free;
+		}
+		cfg->smendx[i] = INVALID_SMENDX;
+	}
 
 	group = iommu_group_get_for_dev(dev);
-	if (IS_ERR(group))
-		return PTR_ERR(group);
-
+	if (IS_ERR(group)) {
+		ret = PTR_ERR(group);
+		goto out_free;
+	}
 	iommu_group_put(group);
 	return 0;
+
+out_free:
+	kfree(cfg);
+	dev->archdata.iommu = NULL;
+	return ret;
 }
 
 static void arm_smmu_remove_device(struct device *dev)
 {
-	struct arm_smmu_device *smmu = find_smmu_for_device(dev);
-	struct arm_smmu_master_cfg *cfg = find_smmu_master_cfg(dev);
+	struct arm_smmu_master_cfg *cfg = dev->archdata.iommu;
 
-	if (smmu && cfg)
-		arm_smmu_master_free_smes(smmu, cfg);
+	if (!cfg)
+		return;
 
+	arm_smmu_master_free_smes(cfg);
 	iommu_group_remove_device(dev);
+	kfree(cfg);
+	dev->archdata.iommu = NULL;
 }
 
 static struct iommu_group *arm_smmu_device_group(struct device *dev)
 {
 	struct iommu_group *group;
-	int ret;
 
 	if (dev_is_pci(dev))
 		group = pci_device_group(dev);
 	else
 		group = generic_device_group(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);
-
-	if (ret) {
-		iommu_group_put(group);
-		group = ERR_PTR(ret);
-	}
-
 	return group;
 }
 
@@ -1938,9 +1821,6 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
 	struct resource *res;
 	struct arm_smmu_device *smmu;
 	struct device *dev = &pdev->dev;
-	struct rb_node *node;
-	struct of_phandle_iterator it;
-	struct arm_smmu_phandle_args *masterspec;
 	int num_irqs, i, err;
 
 	smmu = devm_kzalloc(dev, sizeof(*smmu), GFP_KERNEL);
@@ -2001,37 +1881,6 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
 	if (err)
 		return err;
 
-	i = 0;
-	smmu->masters = RB_ROOT;
-
-	err = -ENOMEM;
-	/* No need to zero the memory for masterspec */
-	masterspec = kmalloc(sizeof(*masterspec), GFP_KERNEL);
-	if (!masterspec)
-		goto out_put_masters;
-
-	of_for_each_phandle(&it, err, dev->of_node,
-			    "mmu-masters", "#stream-id-cells", 0) {
-		int count = of_phandle_iterator_args(&it, masterspec->args,
-						     MAX_MASTER_STREAMIDS);
-		masterspec->np		= of_node_get(it.node);
-		masterspec->args_count	= count;
-
-		err = register_smmu_master(smmu, dev, masterspec);
-		if (err) {
-			dev_err(dev, "failed to add master %s\n",
-				masterspec->np->name);
-			kfree(masterspec);
-			goto out_put_masters;
-		}
-
-		i++;
-	}
-
-	dev_notice(dev, "registered %d master devices\n", i);
-
-	kfree(masterspec);
-
 	parse_driver_options(smmu);
 
 	if (smmu->version == ARM_SMMU_V2 &&
@@ -2039,8 +1888,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) {
@@ -2052,7 +1900,7 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
 		if (err) {
 			dev_err(dev, "failed to request global IRQ %d (%u)\n",
 				i, smmu->irqs[i]);
-			goto out_put_masters;
+			return err;
 		}
 	}
 
@@ -2063,22 +1911,12 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
 
 	arm_smmu_device_reset(smmu);
 	return 0;
-
-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;
 }
 
 static int arm_smmu_device_remove(struct platform_device *pdev)
 {
 	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) {
@@ -2093,12 +1931,6 @@ static int arm_smmu_device_remove(struct platform_device *pdev)
 	if (!smmu)
 		return -ENODEV;
 
-	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(dev, "removing device with active domains!\n");
 
-- 
2.8.1.dirty

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

* [PATCH v7.1 19/22] iommu/arm-smmu: Wire up generic configuration support
  2016-09-12 16:13     ` Robin Murphy
@ 2016-09-14 14:26         ` Robin Murphy
  -1 siblings, 0 replies; 104+ messages in thread
From: Robin Murphy @ 2016-09-14 14:26 UTC (permalink / raw)
  To: will.deacon-5wv7dgnIgG8, joro-zLv9SwRftAIdnm+yROfE0A,
	iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, punit.agrawal-5wv7dgnIgG8,
	thunder.leizhen-hv44wF8Li93QT0dZR+AlfA

With everything else now in place, fill in an of_xlate callback and the
appropriate registration to plumb into the generic configuration
machinery, and watch everything just work.

Signed-off-by: Robin Murphy <robin.murphy-5wv7dgnIgG8@public.gmane.org>

---

- Don't pull in of_platform.h for no reason (was an old leftover)
- Don't spam deprecated binding message when multiple SMMUs are using it

---
 drivers/iommu/arm-smmu.c | 168 ++++++++++++++++++++++++++++++-----------------
 1 file changed, 108 insertions(+), 60 deletions(-)

diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index 9dbb6a37e625..fd6cc19c4ced 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -418,6 +418,8 @@ struct arm_smmu_option_prop {
 
 static atomic_t cavium_smmu_context_count = ATOMIC_INIT(0);
 
+static bool using_legacy_binding, using_generic_binding;
+
 static struct arm_smmu_option_prop arm_smmu_options[] = {
 	{ ARM_SMMU_OPT_SECURE_CFG_ACCESS, "calxeda,smmu-secure-config-access" },
 	{ 0, NULL},
@@ -817,12 +819,6 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain,
 	if (smmu_domain->smmu)
 		goto out_unlock;
 
-	/* We're bypassing these SIDs, so don't allocate an actual context */
-	if (domain->type == IOMMU_DOMAIN_DMA) {
-		smmu_domain->smmu = smmu;
-		goto out_unlock;
-	}
-
 	/*
 	 * Mapping the requested stage onto what we support is surprisingly
 	 * complicated, mainly because the spec allows S1+S2 SMMUs without
@@ -981,7 +977,7 @@ static void arm_smmu_destroy_domain_context(struct iommu_domain *domain)
 	void __iomem *cb_base;
 	int irq;
 
-	if (!smmu || domain->type == IOMMU_DOMAIN_DMA)
+	if (!smmu)
 		return;
 
 	/*
@@ -1015,8 +1011,8 @@ static struct iommu_domain *arm_smmu_domain_alloc(unsigned type)
 	if (!smmu_domain)
 		return NULL;
 
-	if (type == IOMMU_DOMAIN_DMA &&
-	    iommu_get_dma_cookie(&smmu_domain->domain)) {
+	if (type == IOMMU_DOMAIN_DMA && (using_legacy_binding ||
+	    iommu_get_dma_cookie(&smmu_domain->domain))) {
 		kfree(smmu_domain);
 		return NULL;
 	}
@@ -1133,19 +1129,22 @@ static int arm_smmu_master_alloc_smes(struct device *dev)
 	mutex_lock(&smmu->stream_map_mutex);
 	/* Figure out a viable stream map entry allocation */
 	for_each_cfg_sme(fwspec, i, idx) {
+		u16 sid = fwspec->ids[i];
+		u16 mask = fwspec->ids[i] >> SMR_MASK_SHIFT;
+
 		if (idx != INVALID_SMENDX) {
 			ret = -EEXIST;
 			goto out_err;
 		}
 
-		ret = arm_smmu_find_sme(smmu, fwspec->ids[i], 0);
+		ret = arm_smmu_find_sme(smmu, sid, mask);
 		if (ret < 0)
 			goto out_err;
 
 		idx = ret;
 		if (smrs && smmu->s2crs[idx].count == 0) {
-			smrs[idx].id = fwspec->ids[i];
-			smrs[idx].mask = 0; /* We don't currently share SMRs */
+			smrs[idx].id = sid;
+			smrs[idx].mask = mask;
 			smrs[idx].valid = true;
 		}
 		smmu->s2crs[idx].count++;
@@ -1203,15 +1202,6 @@ static int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain,
 	u8 cbndx = smmu_domain->cfg.cbndx;
 	int i, idx;
 
-	/*
-	 * FIXME: This won't be needed once we have IOMMU-backed DMA ops
-	 * for all devices behind the SMMU. Note that we need to take
-	 * care configuring SMRs for devices both a platform_device and
-	 * and a PCI device (i.e. a PCI host controller)
-	 */
-	if (smmu_domain->domain.type == IOMMU_DOMAIN_DMA)
-		type = S2CR_TYPE_BYPASS;
-
 	for_each_cfg_sme(fwspec, i, idx) {
 		if (type == s2cr[idx].type && cbndx == s2cr[idx].cbndx)
 			continue;
@@ -1373,25 +1363,50 @@ static bool arm_smmu_capable(enum iommu_cap cap)
 	}
 }
 
+static int arm_smmu_match_node(struct device *dev, void *data)
+{
+	return dev->of_node == data;
+}
+
+static struct arm_smmu_device *arm_smmu_get_by_node(struct device_node *np)
+{
+	struct device *dev = driver_find_device(&arm_smmu_driver.driver, NULL,
+						np, arm_smmu_match_node);
+	put_device(dev);
+	return dev ? dev_get_drvdata(dev) : NULL;
+}
+
 static int arm_smmu_add_device(struct device *dev)
 {
 	struct arm_smmu_device *smmu;
 	struct arm_smmu_master_cfg *cfg;
-	struct iommu_fwspec *fwspec;
+	struct iommu_fwspec *fwspec = dev->iommu_fwspec;
 	int i, ret;
 
-	ret = arm_smmu_register_legacy_master(dev, &smmu);
-	fwspec = dev->iommu_fwspec;
-	if (ret)
-		goto out_free;
+	if (using_legacy_binding) {
+		ret = arm_smmu_register_legacy_master(dev, &smmu);
+		fwspec = dev->iommu_fwspec;
+		if (ret)
+			goto out_free;
+	} else if (fwspec) {
+		smmu = arm_smmu_get_by_node(to_of_node(fwspec->iommu_fwnode));
+	} else {
+		return -ENODEV;
+	}
 
 	ret = -EINVAL;
 	for (i = 0; i < fwspec->num_ids; i++) {
 		u16 sid = fwspec->ids[i];
+		u16 mask = fwspec->ids[i] >> SMR_MASK_SHIFT;
 
 		if (sid & ~smmu->streamid_mask) {
 			dev_err(dev, "stream ID 0x%x out of range for SMMU (0x%x)\n",
-				sid, cfg->smmu->streamid_mask);
+				sid, smmu->streamid_mask);
+			goto out_free;
+		}
+		if (mask & ~smmu->smr_mask_mask) {
+			dev_err(dev, "SMR mask 0x%x out of range for SMMU (0x%x)\n",
+				sid, smmu->smr_mask_mask);
 			goto out_free;
 		}
 	}
@@ -1503,6 +1518,19 @@ out_unlock:
 	return ret;
 }
 
+static int arm_smmu_of_xlate(struct device *dev, struct of_phandle_args *args)
+{
+	u32 fwid = 0;
+
+	if (args->args_count > 0)
+		fwid |= (u16)args->args[0];
+
+	if (args->args_count > 1)
+		fwid |= (u16)args->args[1] << SMR_MASK_SHIFT;
+
+	return iommu_fwspec_add_ids(dev, &fwid, 1);
+}
+
 static struct iommu_ops arm_smmu_ops = {
 	.capable		= arm_smmu_capable,
 	.domain_alloc		= arm_smmu_domain_alloc,
@@ -1517,6 +1545,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 */
 };
 
@@ -1870,6 +1899,19 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
 	struct arm_smmu_device *smmu;
 	struct device *dev = &pdev->dev;
 	int num_irqs, i, err;
+	bool legacy_binding;
+
+	legacy_binding = of_find_property(dev->of_node, "mmu-masters", NULL);
+	if (legacy_binding && !using_generic_binding) {
+		if (!using_legacy_binding)
+			pr_notice("deprecated \"mmu-masters\" DT property in use; DMA API support unavailable\n");
+		using_legacy_binding = true;
+	} else if (!legacy_binding && !using_legacy_binding) {
+		using_generic_binding = true;
+	} else {
+		dev_err(dev, "not probing due to mismatched DT properties\n");
+		return -ENODEV;
+	}
 
 	smmu = devm_kzalloc(dev, sizeof(*smmu), GFP_KERNEL);
 	if (!smmu) {
@@ -1954,6 +1996,20 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
 	of_iommu_set_ops(dev->of_node, &arm_smmu_ops);
 	platform_set_drvdata(pdev, smmu);
 	arm_smmu_device_reset(smmu);
+
+	/* Oh, for a proper bus abstraction */
+	if (!iommu_present(&platform_bus_type))
+		bus_set_iommu(&platform_bus_type, &arm_smmu_ops);
+#ifdef CONFIG_ARM_AMBA
+	if (!iommu_present(&amba_bustype))
+		bus_set_iommu(&amba_bustype, &arm_smmu_ops);
+#endif
+#ifdef CONFIG_PCI
+	if (!iommu_present(&pci_bus_type)) {
+		pci_request_acs();
+		bus_set_iommu(&pci_bus_type, &arm_smmu_ops);
+	}
+#endif
 	return 0;
 }
 
@@ -1983,41 +2039,14 @@ static struct platform_driver arm_smmu_driver = {
 
 static int __init arm_smmu_init(void)
 {
-	struct device_node *np;
-	int ret;
+	static bool registered;
+	int ret = 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
-	 * and IOMMU bus operation registration.
-	 */
-	np = of_find_matching_node(NULL, arm_smmu_of_match);
-	if (!np)
-		return 0;
-
-	of_node_put(np);
-
-	ret = platform_driver_register(&arm_smmu_driver);
-	if (ret)
-		return ret;
-
-	/* Oh, for a proper bus abstraction */
-	if (!iommu_present(&platform_bus_type))
-		bus_set_iommu(&platform_bus_type, &arm_smmu_ops);
-
-#ifdef CONFIG_ARM_AMBA
-	if (!iommu_present(&amba_bustype))
-		bus_set_iommu(&amba_bustype, &arm_smmu_ops);
-#endif
-
-#ifdef CONFIG_PCI
-	if (!iommu_present(&pci_bus_type)) {
-		pci_request_acs();
-		bus_set_iommu(&pci_bus_type, &arm_smmu_ops);
+	if (!registered) {
+		ret = platform_driver_register(&arm_smmu_driver);
+		registered = !ret;
 	}
-#endif
-
-	return 0;
+	return ret;
 }
 
 static void __exit arm_smmu_exit(void)
@@ -2028,6 +2057,25 @@ 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)
+{
+	int ret = arm_smmu_init();
+
+	if (ret)
+		return ret;
+
+	if (!of_platform_device_create(np, NULL, platform_bus_type.dev_root))
+		return -ENODEV;
+
+	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);
+IOMMU_OF_DECLARE(cavium_smmuv2, "cavium,smmu-v2", 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.8.1.dirty

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

* [PATCH v7.1 19/22] iommu/arm-smmu: Wire up generic configuration support
@ 2016-09-14 14:26         ` Robin Murphy
  0 siblings, 0 replies; 104+ messages in thread
From: Robin Murphy @ 2016-09-14 14:26 UTC (permalink / raw)
  To: linux-arm-kernel

With everything else now in place, fill in an of_xlate callback and the
appropriate registration to plumb into the generic configuration
machinery, and watch everything just work.

Signed-off-by: Robin Murphy <robin.murphy@arm.com>

---

- Don't pull in of_platform.h for no reason (was an old leftover)
- Don't spam deprecated binding message when multiple SMMUs are using it

---
 drivers/iommu/arm-smmu.c | 168 ++++++++++++++++++++++++++++++-----------------
 1 file changed, 108 insertions(+), 60 deletions(-)

diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index 9dbb6a37e625..fd6cc19c4ced 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -418,6 +418,8 @@ struct arm_smmu_option_prop {
 
 static atomic_t cavium_smmu_context_count = ATOMIC_INIT(0);
 
+static bool using_legacy_binding, using_generic_binding;
+
 static struct arm_smmu_option_prop arm_smmu_options[] = {
 	{ ARM_SMMU_OPT_SECURE_CFG_ACCESS, "calxeda,smmu-secure-config-access" },
 	{ 0, NULL},
@@ -817,12 +819,6 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain,
 	if (smmu_domain->smmu)
 		goto out_unlock;
 
-	/* We're bypassing these SIDs, so don't allocate an actual context */
-	if (domain->type == IOMMU_DOMAIN_DMA) {
-		smmu_domain->smmu = smmu;
-		goto out_unlock;
-	}
-
 	/*
 	 * Mapping the requested stage onto what we support is surprisingly
 	 * complicated, mainly because the spec allows S1+S2 SMMUs without
@@ -981,7 +977,7 @@ static void arm_smmu_destroy_domain_context(struct iommu_domain *domain)
 	void __iomem *cb_base;
 	int irq;
 
-	if (!smmu || domain->type == IOMMU_DOMAIN_DMA)
+	if (!smmu)
 		return;
 
 	/*
@@ -1015,8 +1011,8 @@ static struct iommu_domain *arm_smmu_domain_alloc(unsigned type)
 	if (!smmu_domain)
 		return NULL;
 
-	if (type == IOMMU_DOMAIN_DMA &&
-	    iommu_get_dma_cookie(&smmu_domain->domain)) {
+	if (type == IOMMU_DOMAIN_DMA && (using_legacy_binding ||
+	    iommu_get_dma_cookie(&smmu_domain->domain))) {
 		kfree(smmu_domain);
 		return NULL;
 	}
@@ -1133,19 +1129,22 @@ static int arm_smmu_master_alloc_smes(struct device *dev)
 	mutex_lock(&smmu->stream_map_mutex);
 	/* Figure out a viable stream map entry allocation */
 	for_each_cfg_sme(fwspec, i, idx) {
+		u16 sid = fwspec->ids[i];
+		u16 mask = fwspec->ids[i] >> SMR_MASK_SHIFT;
+
 		if (idx != INVALID_SMENDX) {
 			ret = -EEXIST;
 			goto out_err;
 		}
 
-		ret = arm_smmu_find_sme(smmu, fwspec->ids[i], 0);
+		ret = arm_smmu_find_sme(smmu, sid, mask);
 		if (ret < 0)
 			goto out_err;
 
 		idx = ret;
 		if (smrs && smmu->s2crs[idx].count == 0) {
-			smrs[idx].id = fwspec->ids[i];
-			smrs[idx].mask = 0; /* We don't currently share SMRs */
+			smrs[idx].id = sid;
+			smrs[idx].mask = mask;
 			smrs[idx].valid = true;
 		}
 		smmu->s2crs[idx].count++;
@@ -1203,15 +1202,6 @@ static int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain,
 	u8 cbndx = smmu_domain->cfg.cbndx;
 	int i, idx;
 
-	/*
-	 * FIXME: This won't be needed once we have IOMMU-backed DMA ops
-	 * for all devices behind the SMMU. Note that we need to take
-	 * care configuring SMRs for devices both a platform_device and
-	 * and a PCI device (i.e. a PCI host controller)
-	 */
-	if (smmu_domain->domain.type == IOMMU_DOMAIN_DMA)
-		type = S2CR_TYPE_BYPASS;
-
 	for_each_cfg_sme(fwspec, i, idx) {
 		if (type == s2cr[idx].type && cbndx == s2cr[idx].cbndx)
 			continue;
@@ -1373,25 +1363,50 @@ static bool arm_smmu_capable(enum iommu_cap cap)
 	}
 }
 
+static int arm_smmu_match_node(struct device *dev, void *data)
+{
+	return dev->of_node == data;
+}
+
+static struct arm_smmu_device *arm_smmu_get_by_node(struct device_node *np)
+{
+	struct device *dev = driver_find_device(&arm_smmu_driver.driver, NULL,
+						np, arm_smmu_match_node);
+	put_device(dev);
+	return dev ? dev_get_drvdata(dev) : NULL;
+}
+
 static int arm_smmu_add_device(struct device *dev)
 {
 	struct arm_smmu_device *smmu;
 	struct arm_smmu_master_cfg *cfg;
-	struct iommu_fwspec *fwspec;
+	struct iommu_fwspec *fwspec = dev->iommu_fwspec;
 	int i, ret;
 
-	ret = arm_smmu_register_legacy_master(dev, &smmu);
-	fwspec = dev->iommu_fwspec;
-	if (ret)
-		goto out_free;
+	if (using_legacy_binding) {
+		ret = arm_smmu_register_legacy_master(dev, &smmu);
+		fwspec = dev->iommu_fwspec;
+		if (ret)
+			goto out_free;
+	} else if (fwspec) {
+		smmu = arm_smmu_get_by_node(to_of_node(fwspec->iommu_fwnode));
+	} else {
+		return -ENODEV;
+	}
 
 	ret = -EINVAL;
 	for (i = 0; i < fwspec->num_ids; i++) {
 		u16 sid = fwspec->ids[i];
+		u16 mask = fwspec->ids[i] >> SMR_MASK_SHIFT;
 
 		if (sid & ~smmu->streamid_mask) {
 			dev_err(dev, "stream ID 0x%x out of range for SMMU (0x%x)\n",
-				sid, cfg->smmu->streamid_mask);
+				sid, smmu->streamid_mask);
+			goto out_free;
+		}
+		if (mask & ~smmu->smr_mask_mask) {
+			dev_err(dev, "SMR mask 0x%x out of range for SMMU (0x%x)\n",
+				sid, smmu->smr_mask_mask);
 			goto out_free;
 		}
 	}
@@ -1503,6 +1518,19 @@ out_unlock:
 	return ret;
 }
 
+static int arm_smmu_of_xlate(struct device *dev, struct of_phandle_args *args)
+{
+	u32 fwid = 0;
+
+	if (args->args_count > 0)
+		fwid |= (u16)args->args[0];
+
+	if (args->args_count > 1)
+		fwid |= (u16)args->args[1] << SMR_MASK_SHIFT;
+
+	return iommu_fwspec_add_ids(dev, &fwid, 1);
+}
+
 static struct iommu_ops arm_smmu_ops = {
 	.capable		= arm_smmu_capable,
 	.domain_alloc		= arm_smmu_domain_alloc,
@@ -1517,6 +1545,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 */
 };
 
@@ -1870,6 +1899,19 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
 	struct arm_smmu_device *smmu;
 	struct device *dev = &pdev->dev;
 	int num_irqs, i, err;
+	bool legacy_binding;
+
+	legacy_binding = of_find_property(dev->of_node, "mmu-masters", NULL);
+	if (legacy_binding && !using_generic_binding) {
+		if (!using_legacy_binding)
+			pr_notice("deprecated \"mmu-masters\" DT property in use; DMA API support unavailable\n");
+		using_legacy_binding = true;
+	} else if (!legacy_binding && !using_legacy_binding) {
+		using_generic_binding = true;
+	} else {
+		dev_err(dev, "not probing due to mismatched DT properties\n");
+		return -ENODEV;
+	}
 
 	smmu = devm_kzalloc(dev, sizeof(*smmu), GFP_KERNEL);
 	if (!smmu) {
@@ -1954,6 +1996,20 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
 	of_iommu_set_ops(dev->of_node, &arm_smmu_ops);
 	platform_set_drvdata(pdev, smmu);
 	arm_smmu_device_reset(smmu);
+
+	/* Oh, for a proper bus abstraction */
+	if (!iommu_present(&platform_bus_type))
+		bus_set_iommu(&platform_bus_type, &arm_smmu_ops);
+#ifdef CONFIG_ARM_AMBA
+	if (!iommu_present(&amba_bustype))
+		bus_set_iommu(&amba_bustype, &arm_smmu_ops);
+#endif
+#ifdef CONFIG_PCI
+	if (!iommu_present(&pci_bus_type)) {
+		pci_request_acs();
+		bus_set_iommu(&pci_bus_type, &arm_smmu_ops);
+	}
+#endif
 	return 0;
 }
 
@@ -1983,41 +2039,14 @@ static struct platform_driver arm_smmu_driver = {
 
 static int __init arm_smmu_init(void)
 {
-	struct device_node *np;
-	int ret;
+	static bool registered;
+	int ret = 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
-	 * and IOMMU bus operation registration.
-	 */
-	np = of_find_matching_node(NULL, arm_smmu_of_match);
-	if (!np)
-		return 0;
-
-	of_node_put(np);
-
-	ret = platform_driver_register(&arm_smmu_driver);
-	if (ret)
-		return ret;
-
-	/* Oh, for a proper bus abstraction */
-	if (!iommu_present(&platform_bus_type))
-		bus_set_iommu(&platform_bus_type, &arm_smmu_ops);
-
-#ifdef CONFIG_ARM_AMBA
-	if (!iommu_present(&amba_bustype))
-		bus_set_iommu(&amba_bustype, &arm_smmu_ops);
-#endif
-
-#ifdef CONFIG_PCI
-	if (!iommu_present(&pci_bus_type)) {
-		pci_request_acs();
-		bus_set_iommu(&pci_bus_type, &arm_smmu_ops);
+	if (!registered) {
+		ret = platform_driver_register(&arm_smmu_driver);
+		registered = !ret;
 	}
-#endif
-
-	return 0;
+	return ret;
 }
 
 static void __exit arm_smmu_exit(void)
@@ -2028,6 +2057,25 @@ 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)
+{
+	int ret = arm_smmu_init();
+
+	if (ret)
+		return ret;
+
+	if (!of_platform_device_create(np, NULL, platform_bus_type.dev_root))
+		return -ENODEV;
+
+	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);
+IOMMU_OF_DECLARE(cavium_smmuv2, "cavium,smmu-v2", 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.8.1.dirty

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

* Re: [PATCH v7 22/22] iommu/dma: Avoid PCI host bridge windows
  2016-09-14 13:25                     ` Robin Murphy
@ 2016-09-15  7:08                         ` Marek Szyprowski
  -1 siblings, 0 replies; 104+ messages in thread
From: Marek Szyprowski @ 2016-09-15  7:08 UTC (permalink / raw)
  To: Robin Murphy, will.deacon-5wv7dgnIgG8,
	joro-zLv9SwRftAIdnm+yROfE0A,
	iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, lorenzo.pieralisi-5wv7dgnIgG8,
	jean-philippe.brucker-5wv7dgnIgG8, punit.agrawal-5wv7dgnIgG8,
	thunder.leizhen-hv44wF8Li93QT0dZR+AlfA,
	eric.auger-H+wXaHxf7aLQT0dZR+AlfA, Inki Dae

Hi Robin,


On 2016-09-14 15:25, Robin Murphy wrote:
> On 14/09/16 13:35, Marek Szyprowski wrote:
>> On 2016-09-14 13:10, Robin Murphy wrote:
>>> On 14/09/16 11:55, Marek Szyprowski wrote:
>>>> On 2016-09-12 18:14, Robin Murphy wrote:
>>>>> With our DMA ops enabled for PCI devices, we should avoid allocating
>>>>> IOVAs which a host bridge might misinterpret as peer-to-peer DMA and
>>>>> lead to faults, corruption or other badness. To be safe, punch out
>>>>> holes
>>>>> for all of the relevant host bridge's windows when initialising a DMA
>>>>> domain for a PCI device.
>>>>>
>>>>> CC: Marek Szyprowski <m.szyprowski-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
>>>>> CC: Inki Dae <inki.dae-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
>>>>> Reported-by: Lorenzo Pieralisi <lorenzo.pieralisi-5wv7dgnIgG8@public.gmane.org>
>>>>> Signed-off-by: Robin Murphy <robin.murphy-5wv7dgnIgG8@public.gmane.org>
>>>> I don't know much about PCI and their IOMMU integration, but can't we
>>>> use
>>>> the direct mapping region feature of iommu core for it? There are
>>>> already
>>>> iommu_get_dm_regions(), iommu_put_dm_regions() and
>>>> iommu_request_dm_for_dev()
>>>> functions for handling them...
>>> It's rather the opposite problem - in the direct-mapping case, we're
>>> making sure the iommu_domain has translations installed for the given
>>> IOVAs (which are also the corresponding physical address) before it goes
>>> live, whereas what we need to do here is make sure the these addresses
>>> never get used as IOVAs at all, because any attempt to do so them will
>>> likely go wrong. Thus we carve them out of the iova_domain such that
>>> they will never get near an actual IOMMU API call.
>>>
>>> This is a slightly generalised equivalent of e.g. amd_iommu.c's
>>> init_reserved_iova_ranges().
>> Hmmm. Each dm_region have protection parameter. Can't we reuse them to
>> create prohibited/reserved regions by setting it to 0 (no read / no write)
>> and mapping to physical 0 address? That's just a quick idea, because
>> dm_regions and the proposed code for pci looks a bit similar for me...
> It might look similar, but at different levels (iommu_domain vs.
> iova_domain) and with the opposite intent. The dm_region prot flag is
> just the standard flag as passed to iommu_map() - trying to map a region
> with no access in an empty pagetable isn't going to achieve anything
> anyway (it's effectively unmapping addresses that are already unmapped).
>
> But for this case, even if you _did_ map something in the pagetable
> (i.e. the iommu_domain), it wouldn't make any difference, because the
> thing we're mitigating against is handing out addresses which are going
> to cause a device's accesses to blow up inside the PCI root complex
> without ever even reaching the IOMMU. In short, dm_regions are about
> "these addresses are already being used for DMA, so make sure the IOMMU
> API doesn't block them", whereas reserved ranges are about "these
> addresses are unusable for DMA, so make sure the DMA API can't allocate
> them".

Okay, thanks for the explanation.

Best regards
-- 
Marek Szyprowski, PhD
Samsung R&D Institute Poland

--
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] 104+ messages in thread

* [PATCH v7 22/22] iommu/dma: Avoid PCI host bridge windows
@ 2016-09-15  7:08                         ` Marek Szyprowski
  0 siblings, 0 replies; 104+ messages in thread
From: Marek Szyprowski @ 2016-09-15  7:08 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Robin,


On 2016-09-14 15:25, Robin Murphy wrote:
> On 14/09/16 13:35, Marek Szyprowski wrote:
>> On 2016-09-14 13:10, Robin Murphy wrote:
>>> On 14/09/16 11:55, Marek Szyprowski wrote:
>>>> On 2016-09-12 18:14, Robin Murphy wrote:
>>>>> With our DMA ops enabled for PCI devices, we should avoid allocating
>>>>> IOVAs which a host bridge might misinterpret as peer-to-peer DMA and
>>>>> lead to faults, corruption or other badness. To be safe, punch out
>>>>> holes
>>>>> for all of the relevant host bridge's windows when initialising a DMA
>>>>> domain for a PCI device.
>>>>>
>>>>> CC: Marek Szyprowski <m.szyprowski@samsung.com>
>>>>> CC: Inki Dae <inki.dae@samsung.com>
>>>>> Reported-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
>>>>> Signed-off-by: Robin Murphy <robin.murphy@arm.com>
>>>> I don't know much about PCI and their IOMMU integration, but can't we
>>>> use
>>>> the direct mapping region feature of iommu core for it? There are
>>>> already
>>>> iommu_get_dm_regions(), iommu_put_dm_regions() and
>>>> iommu_request_dm_for_dev()
>>>> functions for handling them...
>>> It's rather the opposite problem - in the direct-mapping case, we're
>>> making sure the iommu_domain has translations installed for the given
>>> IOVAs (which are also the corresponding physical address) before it goes
>>> live, whereas what we need to do here is make sure the these addresses
>>> never get used as IOVAs at all, because any attempt to do so them will
>>> likely go wrong. Thus we carve them out of the iova_domain such that
>>> they will never get near an actual IOMMU API call.
>>>
>>> This is a slightly generalised equivalent of e.g. amd_iommu.c's
>>> init_reserved_iova_ranges().
>> Hmmm. Each dm_region have protection parameter. Can't we reuse them to
>> create prohibited/reserved regions by setting it to 0 (no read / no write)
>> and mapping to physical 0 address? That's just a quick idea, because
>> dm_regions and the proposed code for pci looks a bit similar for me...
> It might look similar, but at different levels (iommu_domain vs.
> iova_domain) and with the opposite intent. The dm_region prot flag is
> just the standard flag as passed to iommu_map() - trying to map a region
> with no access in an empty pagetable isn't going to achieve anything
> anyway (it's effectively unmapping addresses that are already unmapped).
>
> But for this case, even if you _did_ map something in the pagetable
> (i.e. the iommu_domain), it wouldn't make any difference, because the
> thing we're mitigating against is handing out addresses which are going
> to cause a device's accesses to blow up inside the PCI root complex
> without ever even reaching the IOMMU. In short, dm_regions are about
> "these addresses are already being used for DMA, so make sure the IOMMU
> API doesn't block them", whereas reserved ranges are about "these
> addresses are unusable for DMA, so make sure the DMA API can't allocate
> them".

Okay, thanks for the explanation.

Best regards
-- 
Marek Szyprowski, PhD
Samsung R&D Institute Poland

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

* Re: [PATCH v7 00/22] Generic DT bindings for PCI IOMMUs and ARM SMMU
  2016-09-14 12:53                 ` Robin Murphy
@ 2016-09-15  9:29                     ` Auger Eric
  -1 siblings, 0 replies; 104+ messages in thread
From: Auger Eric @ 2016-09-15  9:29 UTC (permalink / raw)
  To: Robin Murphy, will.deacon-5wv7dgnIgG8,
	joro-zLv9SwRftAIdnm+yROfE0A,
	iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, punit.agrawal-5wv7dgnIgG8,
	thunder.leizhen-hv44wF8Li93QT0dZR+AlfA

Hi Robin,

On 14/09/2016 14:53, Robin Murphy wrote:
> On 14/09/16 13:32, Auger Eric wrote:
>> Hi,
>> On 14/09/2016 12:35, Robin Murphy wrote:
>>> On 14/09/16 09:41, Auger Eric wrote:
>>>> Hi,
>>>>
>>>> On 12/09/2016 18:13, Robin Murphy wrote:
>>>>> Hi all,
>>>>>
>>>>> To any more confusing fixups and crazily numbered extra patches, here's
>>>>> a quick v7 with everything rebased into the right order. The significant
>>>>> change this time is to implement iommu_fwspec properly from the start,
>>>>> which ends up being far simpler and more robust than faffing about
>>>>> introducing it somewhere 'less intrusive' to move toward core code later.
>>>>>
>>>>> New branch in the logical place:
>>>>>
>>>>> git://linux-arm.org/linux-rm iommu/generic-v7
>>>>
>>>> For information, as discussed privately with Robin I experience some
>>>> regressions with the former and now deprecated dt description.
>>>>
>>>> on my AMD Overdrive board and my old dt description I now only see a
>>>> single group:
>>>>
>>>> /sys/kernel/iommu_groups/
>>>> /sys/kernel/iommu_groups/0
>>>> /sys/kernel/iommu_groups/0/devices
>>>> /sys/kernel/iommu_groups/0/devices/e0700000.xgmac
>>>>
>>>> whereas I formerly see
>>>>
>>>> /sys/kernel/iommu_groups/
>>>> /sys/kernel/iommu_groups/3
>>>> /sys/kernel/iommu_groups/3/devices
>>>> /sys/kernel/iommu_groups/3/devices/0000:00:00.0
>>>> /sys/kernel/iommu_groups/1
>>>> /sys/kernel/iommu_groups/1/devices
>>>> /sys/kernel/iommu_groups/1/devices/e0700000.xgmac
>>>> /sys/kernel/iommu_groups/4
>>>> /sys/kernel/iommu_groups/4/devices
>>>> /sys/kernel/iommu_groups/4/devices/0000:00:02.2
>>>> /sys/kernel/iommu_groups/4/devices/0000:01:00.1
>>>> /sys/kernel/iommu_groups/4/devices/0000:00:02.0
>>>> /sys/kernel/iommu_groups/4/devices/0000:01:00.0
>>>> /sys/kernel/iommu_groups/2
>>>> /sys/kernel/iommu_groups/2/devices
>>>> /sys/kernel/iommu_groups/2/devices/e0900000.xgmac
>>>> /sys/kernel/iommu_groups/0
>>>> /sys/kernel/iommu_groups/0/devices
>>>> /sys/kernel/iommu_groups/0/devices/f0000000.pcie
>>>>
>>>> This is the group topology without ACS override. Applying the non
>>>> upstreamed "pci: Enable overrides for missing ACS capabilities" I used
>>>> to see separate groups for each PCIe components. Now I don't see any
>>>> difference with and without ACS override.
>>>
>>> OK, having reproduced on my Juno, the problem looks to be that
>>> of_for_each_phandle() leaves err set to -ENOENT after successfully
>>> walking a phandle list, which makes __find_legacy_master_phandle()
>>> always bail out after the first SMMU.
>>>
>>> Can you confirm that the following diff fixes things for you?
>>
>> Well it improves but there are still differences in the group topology.
>> The PFs now are in group 0.
>>
>> root@trusty:~# lspci -nk
>> 00:00.0 0600: 1022:1a00
>>         Subsystem: 1022:1a00
>> 00:02.0 0600: 1022:1a01
>> 00:02.2 0604: 1022:1a02
>>         Kernel driver in use: pcieport
>> 01:00.0 0200: 8086:1521 (rev 01)
>>         Subsystem: 8086:0002
>>         Kernel driver in use: igb
>> 01:00.1 0200: 8086:1521 (rev 01)
>>         Subsystem: 8086:0002
>>         Kernel driver in use: igb
>>
>>
>> with your series + fix:
>> /sys/kernel/iommu_groups/
>> /sys/kernel/iommu_groups/3
>> /sys/kernel/iommu_groups/3/devices
>> /sys/kernel/iommu_groups/3/devices/0000:00:00.0
>> /sys/kernel/iommu_groups/1
>> /sys/kernel/iommu_groups/1/devices
>> /sys/kernel/iommu_groups/1/devices/e0700000.xgmac
>> /sys/kernel/iommu_groups/4
>> /sys/kernel/iommu_groups/4/devices
>> /sys/kernel/iommu_groups/4/devices/0000:00:02.2
>> /sys/kernel/iommu_groups/4/devices/0000:00:02.0
>> /sys/kernel/iommu_groups/2
>> /sys/kernel/iommu_groups/2/devices
>> /sys/kernel/iommu_groups/2/devices/e0900000.xgmac
>> /sys/kernel/iommu_groups/0
>> /sys/kernel/iommu_groups/0/devices
>> /sys/kernel/iommu_groups/0/devices/0000:01:00.1
>> /sys/kernel/iommu_groups/0/devices/f0000000.pcie
>> /sys/kernel/iommu_groups/0/devices/0000:01:00.0
>>
>> Before (4.8-rc5):
>>
>> /sys/kernel/iommu_groups/
>> /sys/kernel/iommu_groups/3
>> /sys/kernel/iommu_groups/3/devices
>> /sys/kernel/iommu_groups/3/devices/0000:00:00.0
>> /sys/kernel/iommu_groups/1
>> /sys/kernel/iommu_groups/1/devices
>> /sys/kernel/iommu_groups/1/devices/e0700000.xgmac
>> /sys/kernel/iommu_groups/4
>> /sys/kernel/iommu_groups/4/devices
>> /sys/kernel/iommu_groups/4/devices/0000:00:02.2
>> /sys/kernel/iommu_groups/4/devices/0000:01:00.1
>> /sys/kernel/iommu_groups/4/devices/0000:00:02.0
>> /sys/kernel/iommu_groups/4/devices/0000:01:00.0
>> /sys/kernel/iommu_groups/2
>> /sys/kernel/iommu_groups/2/devices
>> /sys/kernel/iommu_groups/2/devices/e0900000.xgmac
>> /sys/kernel/iommu_groups/0
>> /sys/kernel/iommu_groups/0/devices
>> /sys/kernel/iommu_groups/0/devices/f0000000.pcie
> 
> Your DT claims that f0000000.pcie (i.e. the "platform device" side of
> the host controller) owns the IDs 0x100 0x101 0x102 0x103 0x200 0x201
> 0x202 0x203 0x300 0x301 0x302 0x303 0x400 0x401 0x402 0x403. Thus when
> new devices (the PCI PFs) come along *also* claiming those IDs (via the
> RID-to-SID assumption), we now detect the aliasing and assign them to
> the existing group.
> 
> The only way that DT worked without SMR conflicts before is that
> f0000000.pcie wasn't actually getting attached to a domain, thus wasn't
> getting SMRs allocated (ISTR you _did_ get conflicts back when we first
> tried to switch on default domains; that was probably the reason).

Thanks for your explanations. meanwhile I converted the overdrive dt to
the new syntax and it works properly.

However I noticed there are MSI frame iommu mapping although the
device's domain is the default one. There is no check currently in
21/22. I guess this isn't needed?

Thanks

Eric
> 
> Robin.
> 
>>
>> Thanks
>>
>> Eric
>>
>>>
>>> Robin
>>>
>>> --->8---
>>> diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
>>> index fa892d25004d..ac4aab97c93a 100644
>>> --- a/drivers/iommu/arm-smmu.c
>>> +++ b/drivers/iommu/arm-smmu.c
>>> @@ -477,7 +477,7 @@ static int __find_legacy_master_phandle(struct
>>> device *dev, void *data)
>>>  			return 1;
>>>  		}
>>>  	it->node = np;
>>> -	return err;
>>> +	return err == -ENOENT ? 0 : err;
>>>  }
>>>
>>>  static struct platform_driver arm_smmu_driver;
>>>
>>>
>>> _______________________________________________
>>> linux-arm-kernel mailing list
>>> linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org
>>> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>>>
>>
> 
> 
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
> 
--
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] 104+ messages in thread

* [PATCH v7 00/22] Generic DT bindings for PCI IOMMUs and ARM SMMU
@ 2016-09-15  9:29                     ` Auger Eric
  0 siblings, 0 replies; 104+ messages in thread
From: Auger Eric @ 2016-09-15  9:29 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Robin,

On 14/09/2016 14:53, Robin Murphy wrote:
> On 14/09/16 13:32, Auger Eric wrote:
>> Hi,
>> On 14/09/2016 12:35, Robin Murphy wrote:
>>> On 14/09/16 09:41, Auger Eric wrote:
>>>> Hi,
>>>>
>>>> On 12/09/2016 18:13, Robin Murphy wrote:
>>>>> Hi all,
>>>>>
>>>>> To any more confusing fixups and crazily numbered extra patches, here's
>>>>> a quick v7 with everything rebased into the right order. The significant
>>>>> change this time is to implement iommu_fwspec properly from the start,
>>>>> which ends up being far simpler and more robust than faffing about
>>>>> introducing it somewhere 'less intrusive' to move toward core code later.
>>>>>
>>>>> New branch in the logical place:
>>>>>
>>>>> git://linux-arm.org/linux-rm iommu/generic-v7
>>>>
>>>> For information, as discussed privately with Robin I experience some
>>>> regressions with the former and now deprecated dt description.
>>>>
>>>> on my AMD Overdrive board and my old dt description I now only see a
>>>> single group:
>>>>
>>>> /sys/kernel/iommu_groups/
>>>> /sys/kernel/iommu_groups/0
>>>> /sys/kernel/iommu_groups/0/devices
>>>> /sys/kernel/iommu_groups/0/devices/e0700000.xgmac
>>>>
>>>> whereas I formerly see
>>>>
>>>> /sys/kernel/iommu_groups/
>>>> /sys/kernel/iommu_groups/3
>>>> /sys/kernel/iommu_groups/3/devices
>>>> /sys/kernel/iommu_groups/3/devices/0000:00:00.0
>>>> /sys/kernel/iommu_groups/1
>>>> /sys/kernel/iommu_groups/1/devices
>>>> /sys/kernel/iommu_groups/1/devices/e0700000.xgmac
>>>> /sys/kernel/iommu_groups/4
>>>> /sys/kernel/iommu_groups/4/devices
>>>> /sys/kernel/iommu_groups/4/devices/0000:00:02.2
>>>> /sys/kernel/iommu_groups/4/devices/0000:01:00.1
>>>> /sys/kernel/iommu_groups/4/devices/0000:00:02.0
>>>> /sys/kernel/iommu_groups/4/devices/0000:01:00.0
>>>> /sys/kernel/iommu_groups/2
>>>> /sys/kernel/iommu_groups/2/devices
>>>> /sys/kernel/iommu_groups/2/devices/e0900000.xgmac
>>>> /sys/kernel/iommu_groups/0
>>>> /sys/kernel/iommu_groups/0/devices
>>>> /sys/kernel/iommu_groups/0/devices/f0000000.pcie
>>>>
>>>> This is the group topology without ACS override. Applying the non
>>>> upstreamed "pci: Enable overrides for missing ACS capabilities" I used
>>>> to see separate groups for each PCIe components. Now I don't see any
>>>> difference with and without ACS override.
>>>
>>> OK, having reproduced on my Juno, the problem looks to be that
>>> of_for_each_phandle() leaves err set to -ENOENT after successfully
>>> walking a phandle list, which makes __find_legacy_master_phandle()
>>> always bail out after the first SMMU.
>>>
>>> Can you confirm that the following diff fixes things for you?
>>
>> Well it improves but there are still differences in the group topology.
>> The PFs now are in group 0.
>>
>> root at trusty:~# lspci -nk
>> 00:00.0 0600: 1022:1a00
>>         Subsystem: 1022:1a00
>> 00:02.0 0600: 1022:1a01
>> 00:02.2 0604: 1022:1a02
>>         Kernel driver in use: pcieport
>> 01:00.0 0200: 8086:1521 (rev 01)
>>         Subsystem: 8086:0002
>>         Kernel driver in use: igb
>> 01:00.1 0200: 8086:1521 (rev 01)
>>         Subsystem: 8086:0002
>>         Kernel driver in use: igb
>>
>>
>> with your series + fix:
>> /sys/kernel/iommu_groups/
>> /sys/kernel/iommu_groups/3
>> /sys/kernel/iommu_groups/3/devices
>> /sys/kernel/iommu_groups/3/devices/0000:00:00.0
>> /sys/kernel/iommu_groups/1
>> /sys/kernel/iommu_groups/1/devices
>> /sys/kernel/iommu_groups/1/devices/e0700000.xgmac
>> /sys/kernel/iommu_groups/4
>> /sys/kernel/iommu_groups/4/devices
>> /sys/kernel/iommu_groups/4/devices/0000:00:02.2
>> /sys/kernel/iommu_groups/4/devices/0000:00:02.0
>> /sys/kernel/iommu_groups/2
>> /sys/kernel/iommu_groups/2/devices
>> /sys/kernel/iommu_groups/2/devices/e0900000.xgmac
>> /sys/kernel/iommu_groups/0
>> /sys/kernel/iommu_groups/0/devices
>> /sys/kernel/iommu_groups/0/devices/0000:01:00.1
>> /sys/kernel/iommu_groups/0/devices/f0000000.pcie
>> /sys/kernel/iommu_groups/0/devices/0000:01:00.0
>>
>> Before (4.8-rc5):
>>
>> /sys/kernel/iommu_groups/
>> /sys/kernel/iommu_groups/3
>> /sys/kernel/iommu_groups/3/devices
>> /sys/kernel/iommu_groups/3/devices/0000:00:00.0
>> /sys/kernel/iommu_groups/1
>> /sys/kernel/iommu_groups/1/devices
>> /sys/kernel/iommu_groups/1/devices/e0700000.xgmac
>> /sys/kernel/iommu_groups/4
>> /sys/kernel/iommu_groups/4/devices
>> /sys/kernel/iommu_groups/4/devices/0000:00:02.2
>> /sys/kernel/iommu_groups/4/devices/0000:01:00.1
>> /sys/kernel/iommu_groups/4/devices/0000:00:02.0
>> /sys/kernel/iommu_groups/4/devices/0000:01:00.0
>> /sys/kernel/iommu_groups/2
>> /sys/kernel/iommu_groups/2/devices
>> /sys/kernel/iommu_groups/2/devices/e0900000.xgmac
>> /sys/kernel/iommu_groups/0
>> /sys/kernel/iommu_groups/0/devices
>> /sys/kernel/iommu_groups/0/devices/f0000000.pcie
> 
> Your DT claims that f0000000.pcie (i.e. the "platform device" side of
> the host controller) owns the IDs 0x100 0x101 0x102 0x103 0x200 0x201
> 0x202 0x203 0x300 0x301 0x302 0x303 0x400 0x401 0x402 0x403. Thus when
> new devices (the PCI PFs) come along *also* claiming those IDs (via the
> RID-to-SID assumption), we now detect the aliasing and assign them to
> the existing group.
> 
> The only way that DT worked without SMR conflicts before is that
> f0000000.pcie wasn't actually getting attached to a domain, thus wasn't
> getting SMRs allocated (ISTR you _did_ get conflicts back when we first
> tried to switch on default domains; that was probably the reason).

Thanks for your explanations. meanwhile I converted the overdrive dt to
the new syntax and it works properly.

However I noticed there are MSI frame iommu mapping although the
device's domain is the default one. There is no check currently in
21/22. I guess this isn't needed?

Thanks

Eric
> 
> Robin.
> 
>>
>> Thanks
>>
>> Eric
>>
>>>
>>> Robin
>>>
>>> --->8---
>>> diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
>>> index fa892d25004d..ac4aab97c93a 100644
>>> --- a/drivers/iommu/arm-smmu.c
>>> +++ b/drivers/iommu/arm-smmu.c
>>> @@ -477,7 +477,7 @@ static int __find_legacy_master_phandle(struct
>>> device *dev, void *data)
>>>  			return 1;
>>>  		}
>>>  	it->node = np;
>>> -	return err;
>>> +	return err == -ENOENT ? 0 : err;
>>>  }
>>>
>>>  static struct platform_driver arm_smmu_driver;
>>>
>>>
>>> _______________________________________________
>>> linux-arm-kernel mailing list
>>> linux-arm-kernel at lists.infradead.org
>>> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>>>
>>
> 
> 
> _______________________________________________
> 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] 104+ messages in thread

* Re: [PATCH v7 00/22] Generic DT bindings for PCI IOMMUs and ARM SMMU
  2016-09-15  9:29                     ` Auger Eric
@ 2016-09-15 10:15                         ` Robin Murphy
  -1 siblings, 0 replies; 104+ messages in thread
From: Robin Murphy @ 2016-09-15 10:15 UTC (permalink / raw)
  To: Auger Eric, will.deacon-5wv7dgnIgG8, joro-zLv9SwRftAIdnm+yROfE0A,
	iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, punit.agrawal-5wv7dgnIgG8,
	thunder.leizhen-hv44wF8Li93QT0dZR+AlfA

On 15/09/16 10:29, Auger Eric wrote:
> Hi Robin,
> 
> On 14/09/2016 14:53, Robin Murphy wrote:
>> On 14/09/16 13:32, Auger Eric wrote:
>>> Hi,
>>> On 14/09/2016 12:35, Robin Murphy wrote:
>>>> On 14/09/16 09:41, Auger Eric wrote:
>>>>> Hi,
>>>>>
>>>>> On 12/09/2016 18:13, Robin Murphy wrote:
>>>>>> Hi all,
>>>>>>
>>>>>> To any more confusing fixups and crazily numbered extra patches, here's
>>>>>> a quick v7 with everything rebased into the right order. The significant
>>>>>> change this time is to implement iommu_fwspec properly from the start,
>>>>>> which ends up being far simpler and more robust than faffing about
>>>>>> introducing it somewhere 'less intrusive' to move toward core code later.
>>>>>>
>>>>>> New branch in the logical place:
>>>>>>
>>>>>> git://linux-arm.org/linux-rm iommu/generic-v7
>>>>>
>>>>> For information, as discussed privately with Robin I experience some
>>>>> regressions with the former and now deprecated dt description.
>>>>>
>>>>> on my AMD Overdrive board and my old dt description I now only see a
>>>>> single group:
>>>>>
>>>>> /sys/kernel/iommu_groups/
>>>>> /sys/kernel/iommu_groups/0
>>>>> /sys/kernel/iommu_groups/0/devices
>>>>> /sys/kernel/iommu_groups/0/devices/e0700000.xgmac
>>>>>
>>>>> whereas I formerly see
>>>>>
>>>>> /sys/kernel/iommu_groups/
>>>>> /sys/kernel/iommu_groups/3
>>>>> /sys/kernel/iommu_groups/3/devices
>>>>> /sys/kernel/iommu_groups/3/devices/0000:00:00.0
>>>>> /sys/kernel/iommu_groups/1
>>>>> /sys/kernel/iommu_groups/1/devices
>>>>> /sys/kernel/iommu_groups/1/devices/e0700000.xgmac
>>>>> /sys/kernel/iommu_groups/4
>>>>> /sys/kernel/iommu_groups/4/devices
>>>>> /sys/kernel/iommu_groups/4/devices/0000:00:02.2
>>>>> /sys/kernel/iommu_groups/4/devices/0000:01:00.1
>>>>> /sys/kernel/iommu_groups/4/devices/0000:00:02.0
>>>>> /sys/kernel/iommu_groups/4/devices/0000:01:00.0
>>>>> /sys/kernel/iommu_groups/2
>>>>> /sys/kernel/iommu_groups/2/devices
>>>>> /sys/kernel/iommu_groups/2/devices/e0900000.xgmac
>>>>> /sys/kernel/iommu_groups/0
>>>>> /sys/kernel/iommu_groups/0/devices
>>>>> /sys/kernel/iommu_groups/0/devices/f0000000.pcie
>>>>>
>>>>> This is the group topology without ACS override. Applying the non
>>>>> upstreamed "pci: Enable overrides for missing ACS capabilities" I used
>>>>> to see separate groups for each PCIe components. Now I don't see any
>>>>> difference with and without ACS override.
>>>>
>>>> OK, having reproduced on my Juno, the problem looks to be that
>>>> of_for_each_phandle() leaves err set to -ENOENT after successfully
>>>> walking a phandle list, which makes __find_legacy_master_phandle()
>>>> always bail out after the first SMMU.
>>>>
>>>> Can you confirm that the following diff fixes things for you?
>>>
>>> Well it improves but there are still differences in the group topology.
>>> The PFs now are in group 0.
>>>
>>> root@trusty:~# lspci -nk
>>> 00:00.0 0600: 1022:1a00
>>>         Subsystem: 1022:1a00
>>> 00:02.0 0600: 1022:1a01
>>> 00:02.2 0604: 1022:1a02
>>>         Kernel driver in use: pcieport
>>> 01:00.0 0200: 8086:1521 (rev 01)
>>>         Subsystem: 8086:0002
>>>         Kernel driver in use: igb
>>> 01:00.1 0200: 8086:1521 (rev 01)
>>>         Subsystem: 8086:0002
>>>         Kernel driver in use: igb
>>>
>>>
>>> with your series + fix:
>>> /sys/kernel/iommu_groups/
>>> /sys/kernel/iommu_groups/3
>>> /sys/kernel/iommu_groups/3/devices
>>> /sys/kernel/iommu_groups/3/devices/0000:00:00.0
>>> /sys/kernel/iommu_groups/1
>>> /sys/kernel/iommu_groups/1/devices
>>> /sys/kernel/iommu_groups/1/devices/e0700000.xgmac
>>> /sys/kernel/iommu_groups/4
>>> /sys/kernel/iommu_groups/4/devices
>>> /sys/kernel/iommu_groups/4/devices/0000:00:02.2
>>> /sys/kernel/iommu_groups/4/devices/0000:00:02.0
>>> /sys/kernel/iommu_groups/2
>>> /sys/kernel/iommu_groups/2/devices
>>> /sys/kernel/iommu_groups/2/devices/e0900000.xgmac
>>> /sys/kernel/iommu_groups/0
>>> /sys/kernel/iommu_groups/0/devices
>>> /sys/kernel/iommu_groups/0/devices/0000:01:00.1
>>> /sys/kernel/iommu_groups/0/devices/f0000000.pcie
>>> /sys/kernel/iommu_groups/0/devices/0000:01:00.0
>>>
>>> Before (4.8-rc5):
>>>
>>> /sys/kernel/iommu_groups/
>>> /sys/kernel/iommu_groups/3
>>> /sys/kernel/iommu_groups/3/devices
>>> /sys/kernel/iommu_groups/3/devices/0000:00:00.0
>>> /sys/kernel/iommu_groups/1
>>> /sys/kernel/iommu_groups/1/devices
>>> /sys/kernel/iommu_groups/1/devices/e0700000.xgmac
>>> /sys/kernel/iommu_groups/4
>>> /sys/kernel/iommu_groups/4/devices
>>> /sys/kernel/iommu_groups/4/devices/0000:00:02.2
>>> /sys/kernel/iommu_groups/4/devices/0000:01:00.1
>>> /sys/kernel/iommu_groups/4/devices/0000:00:02.0
>>> /sys/kernel/iommu_groups/4/devices/0000:01:00.0
>>> /sys/kernel/iommu_groups/2
>>> /sys/kernel/iommu_groups/2/devices
>>> /sys/kernel/iommu_groups/2/devices/e0900000.xgmac
>>> /sys/kernel/iommu_groups/0
>>> /sys/kernel/iommu_groups/0/devices
>>> /sys/kernel/iommu_groups/0/devices/f0000000.pcie
>>
>> Your DT claims that f0000000.pcie (i.e. the "platform device" side of
>> the host controller) owns the IDs 0x100 0x101 0x102 0x103 0x200 0x201
>> 0x202 0x203 0x300 0x301 0x302 0x303 0x400 0x401 0x402 0x403. Thus when
>> new devices (the PCI PFs) come along *also* claiming those IDs (via the
>> RID-to-SID assumption), we now detect the aliasing and assign them to
>> the existing group.
>>
>> The only way that DT worked without SMR conflicts before is that
>> f0000000.pcie wasn't actually getting attached to a domain, thus wasn't
>> getting SMRs allocated (ISTR you _did_ get conflicts back when we first
>> tried to switch on default domains; that was probably the reason).
> 
> Thanks for your explanations. meanwhile I converted the overdrive dt to
> the new syntax and it works properly.

Yay!

> However I noticed there are MSI frame iommu mapping although the
> device's domain is the default one. There is no check currently in
> 21/22. I guess this isn't needed?

Au contraire ;) With the generic binding, default domains are properly
implemented, i.e. they have translation, so anything not mapped will
fault. I *had* to write patch 21 as part of this series, since having
DMA ops for PCI devices at the cost of breaking all MSIs was clearly not
a viable solution. If you see mappings, then it's because someone is
using MSIs - so if you fancy, you can try rewinding back to patch 19 or
20 to watch them get stuck.

Robin.

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

* [PATCH v7 00/22] Generic DT bindings for PCI IOMMUs and ARM SMMU
@ 2016-09-15 10:15                         ` Robin Murphy
  0 siblings, 0 replies; 104+ messages in thread
From: Robin Murphy @ 2016-09-15 10:15 UTC (permalink / raw)
  To: linux-arm-kernel

On 15/09/16 10:29, Auger Eric wrote:
> Hi Robin,
> 
> On 14/09/2016 14:53, Robin Murphy wrote:
>> On 14/09/16 13:32, Auger Eric wrote:
>>> Hi,
>>> On 14/09/2016 12:35, Robin Murphy wrote:
>>>> On 14/09/16 09:41, Auger Eric wrote:
>>>>> Hi,
>>>>>
>>>>> On 12/09/2016 18:13, Robin Murphy wrote:
>>>>>> Hi all,
>>>>>>
>>>>>> To any more confusing fixups and crazily numbered extra patches, here's
>>>>>> a quick v7 with everything rebased into the right order. The significant
>>>>>> change this time is to implement iommu_fwspec properly from the start,
>>>>>> which ends up being far simpler and more robust than faffing about
>>>>>> introducing it somewhere 'less intrusive' to move toward core code later.
>>>>>>
>>>>>> New branch in the logical place:
>>>>>>
>>>>>> git://linux-arm.org/linux-rm iommu/generic-v7
>>>>>
>>>>> For information, as discussed privately with Robin I experience some
>>>>> regressions with the former and now deprecated dt description.
>>>>>
>>>>> on my AMD Overdrive board and my old dt description I now only see a
>>>>> single group:
>>>>>
>>>>> /sys/kernel/iommu_groups/
>>>>> /sys/kernel/iommu_groups/0
>>>>> /sys/kernel/iommu_groups/0/devices
>>>>> /sys/kernel/iommu_groups/0/devices/e0700000.xgmac
>>>>>
>>>>> whereas I formerly see
>>>>>
>>>>> /sys/kernel/iommu_groups/
>>>>> /sys/kernel/iommu_groups/3
>>>>> /sys/kernel/iommu_groups/3/devices
>>>>> /sys/kernel/iommu_groups/3/devices/0000:00:00.0
>>>>> /sys/kernel/iommu_groups/1
>>>>> /sys/kernel/iommu_groups/1/devices
>>>>> /sys/kernel/iommu_groups/1/devices/e0700000.xgmac
>>>>> /sys/kernel/iommu_groups/4
>>>>> /sys/kernel/iommu_groups/4/devices
>>>>> /sys/kernel/iommu_groups/4/devices/0000:00:02.2
>>>>> /sys/kernel/iommu_groups/4/devices/0000:01:00.1
>>>>> /sys/kernel/iommu_groups/4/devices/0000:00:02.0
>>>>> /sys/kernel/iommu_groups/4/devices/0000:01:00.0
>>>>> /sys/kernel/iommu_groups/2
>>>>> /sys/kernel/iommu_groups/2/devices
>>>>> /sys/kernel/iommu_groups/2/devices/e0900000.xgmac
>>>>> /sys/kernel/iommu_groups/0
>>>>> /sys/kernel/iommu_groups/0/devices
>>>>> /sys/kernel/iommu_groups/0/devices/f0000000.pcie
>>>>>
>>>>> This is the group topology without ACS override. Applying the non
>>>>> upstreamed "pci: Enable overrides for missing ACS capabilities" I used
>>>>> to see separate groups for each PCIe components. Now I don't see any
>>>>> difference with and without ACS override.
>>>>
>>>> OK, having reproduced on my Juno, the problem looks to be that
>>>> of_for_each_phandle() leaves err set to -ENOENT after successfully
>>>> walking a phandle list, which makes __find_legacy_master_phandle()
>>>> always bail out after the first SMMU.
>>>>
>>>> Can you confirm that the following diff fixes things for you?
>>>
>>> Well it improves but there are still differences in the group topology.
>>> The PFs now are in group 0.
>>>
>>> root at trusty:~# lspci -nk
>>> 00:00.0 0600: 1022:1a00
>>>         Subsystem: 1022:1a00
>>> 00:02.0 0600: 1022:1a01
>>> 00:02.2 0604: 1022:1a02
>>>         Kernel driver in use: pcieport
>>> 01:00.0 0200: 8086:1521 (rev 01)
>>>         Subsystem: 8086:0002
>>>         Kernel driver in use: igb
>>> 01:00.1 0200: 8086:1521 (rev 01)
>>>         Subsystem: 8086:0002
>>>         Kernel driver in use: igb
>>>
>>>
>>> with your series + fix:
>>> /sys/kernel/iommu_groups/
>>> /sys/kernel/iommu_groups/3
>>> /sys/kernel/iommu_groups/3/devices
>>> /sys/kernel/iommu_groups/3/devices/0000:00:00.0
>>> /sys/kernel/iommu_groups/1
>>> /sys/kernel/iommu_groups/1/devices
>>> /sys/kernel/iommu_groups/1/devices/e0700000.xgmac
>>> /sys/kernel/iommu_groups/4
>>> /sys/kernel/iommu_groups/4/devices
>>> /sys/kernel/iommu_groups/4/devices/0000:00:02.2
>>> /sys/kernel/iommu_groups/4/devices/0000:00:02.0
>>> /sys/kernel/iommu_groups/2
>>> /sys/kernel/iommu_groups/2/devices
>>> /sys/kernel/iommu_groups/2/devices/e0900000.xgmac
>>> /sys/kernel/iommu_groups/0
>>> /sys/kernel/iommu_groups/0/devices
>>> /sys/kernel/iommu_groups/0/devices/0000:01:00.1
>>> /sys/kernel/iommu_groups/0/devices/f0000000.pcie
>>> /sys/kernel/iommu_groups/0/devices/0000:01:00.0
>>>
>>> Before (4.8-rc5):
>>>
>>> /sys/kernel/iommu_groups/
>>> /sys/kernel/iommu_groups/3
>>> /sys/kernel/iommu_groups/3/devices
>>> /sys/kernel/iommu_groups/3/devices/0000:00:00.0
>>> /sys/kernel/iommu_groups/1
>>> /sys/kernel/iommu_groups/1/devices
>>> /sys/kernel/iommu_groups/1/devices/e0700000.xgmac
>>> /sys/kernel/iommu_groups/4
>>> /sys/kernel/iommu_groups/4/devices
>>> /sys/kernel/iommu_groups/4/devices/0000:00:02.2
>>> /sys/kernel/iommu_groups/4/devices/0000:01:00.1
>>> /sys/kernel/iommu_groups/4/devices/0000:00:02.0
>>> /sys/kernel/iommu_groups/4/devices/0000:01:00.0
>>> /sys/kernel/iommu_groups/2
>>> /sys/kernel/iommu_groups/2/devices
>>> /sys/kernel/iommu_groups/2/devices/e0900000.xgmac
>>> /sys/kernel/iommu_groups/0
>>> /sys/kernel/iommu_groups/0/devices
>>> /sys/kernel/iommu_groups/0/devices/f0000000.pcie
>>
>> Your DT claims that f0000000.pcie (i.e. the "platform device" side of
>> the host controller) owns the IDs 0x100 0x101 0x102 0x103 0x200 0x201
>> 0x202 0x203 0x300 0x301 0x302 0x303 0x400 0x401 0x402 0x403. Thus when
>> new devices (the PCI PFs) come along *also* claiming those IDs (via the
>> RID-to-SID assumption), we now detect the aliasing and assign them to
>> the existing group.
>>
>> The only way that DT worked without SMR conflicts before is that
>> f0000000.pcie wasn't actually getting attached to a domain, thus wasn't
>> getting SMRs allocated (ISTR you _did_ get conflicts back when we first
>> tried to switch on default domains; that was probably the reason).
> 
> Thanks for your explanations. meanwhile I converted the overdrive dt to
> the new syntax and it works properly.

Yay!

> However I noticed there are MSI frame iommu mapping although the
> device's domain is the default one. There is no check currently in
> 21/22. I guess this isn't needed?

Au contraire ;) With the generic binding, default domains are properly
implemented, i.e. they have translation, so anything not mapped will
fault. I *had* to write patch 21 as part of this series, since having
DMA ops for PCI devices at the cost of breaking all MSIs was clearly not
a viable solution. If you see mappings, then it's because someone is
using MSIs - so if you fancy, you can try rewinding back to patch 19 or
20 to watch them get stuck.

Robin.

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

* Re: [PATCH v7 00/22] Generic DT bindings for PCI IOMMUs and ARM SMMU
  2016-09-15 10:15                         ` Robin Murphy
@ 2016-09-15 16:46                             ` Auger Eric
  -1 siblings, 0 replies; 104+ messages in thread
From: Auger Eric @ 2016-09-15 16:46 UTC (permalink / raw)
  To: Robin Murphy, will.deacon-5wv7dgnIgG8,
	joro-zLv9SwRftAIdnm+yROfE0A,
	iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, punit.agrawal-5wv7dgnIgG8,
	thunder.leizhen-hv44wF8Li93QT0dZR+AlfA

Hi Robin,
On 15/09/2016 12:15, Robin Murphy wrote:
> On 15/09/16 10:29, Auger Eric wrote:
>> Hi Robin,
>>
>> On 14/09/2016 14:53, Robin Murphy wrote:
>>> On 14/09/16 13:32, Auger Eric wrote:
>>>> Hi,
>>>> On 14/09/2016 12:35, Robin Murphy wrote:
>>>>> On 14/09/16 09:41, Auger Eric wrote:
>>>>>> Hi,
>>>>>>
>>>>>> On 12/09/2016 18:13, Robin Murphy wrote:
>>>>>>> Hi all,
>>>>>>>
>>>>>>> To any more confusing fixups and crazily numbered extra patches, here's
>>>>>>> a quick v7 with everything rebased into the right order. The significant
>>>>>>> change this time is to implement iommu_fwspec properly from the start,
>>>>>>> which ends up being far simpler and more robust than faffing about
>>>>>>> introducing it somewhere 'less intrusive' to move toward core code later.
>>>>>>>
>>>>>>> New branch in the logical place:
>>>>>>>
>>>>>>> git://linux-arm.org/linux-rm iommu/generic-v7
>>>>>>
>>>>>> For information, as discussed privately with Robin I experience some
>>>>>> regressions with the former and now deprecated dt description.
>>>>>>
>>>>>> on my AMD Overdrive board and my old dt description I now only see a
>>>>>> single group:
>>>>>>
>>>>>> /sys/kernel/iommu_groups/
>>>>>> /sys/kernel/iommu_groups/0
>>>>>> /sys/kernel/iommu_groups/0/devices
>>>>>> /sys/kernel/iommu_groups/0/devices/e0700000.xgmac
>>>>>>
>>>>>> whereas I formerly see
>>>>>>
>>>>>> /sys/kernel/iommu_groups/
>>>>>> /sys/kernel/iommu_groups/3
>>>>>> /sys/kernel/iommu_groups/3/devices
>>>>>> /sys/kernel/iommu_groups/3/devices/0000:00:00.0
>>>>>> /sys/kernel/iommu_groups/1
>>>>>> /sys/kernel/iommu_groups/1/devices
>>>>>> /sys/kernel/iommu_groups/1/devices/e0700000.xgmac
>>>>>> /sys/kernel/iommu_groups/4
>>>>>> /sys/kernel/iommu_groups/4/devices
>>>>>> /sys/kernel/iommu_groups/4/devices/0000:00:02.2
>>>>>> /sys/kernel/iommu_groups/4/devices/0000:01:00.1
>>>>>> /sys/kernel/iommu_groups/4/devices/0000:00:02.0
>>>>>> /sys/kernel/iommu_groups/4/devices/0000:01:00.0
>>>>>> /sys/kernel/iommu_groups/2
>>>>>> /sys/kernel/iommu_groups/2/devices
>>>>>> /sys/kernel/iommu_groups/2/devices/e0900000.xgmac
>>>>>> /sys/kernel/iommu_groups/0
>>>>>> /sys/kernel/iommu_groups/0/devices
>>>>>> /sys/kernel/iommu_groups/0/devices/f0000000.pcie
>>>>>>
>>>>>> This is the group topology without ACS override. Applying the non
>>>>>> upstreamed "pci: Enable overrides for missing ACS capabilities" I used
>>>>>> to see separate groups for each PCIe components. Now I don't see any
>>>>>> difference with and without ACS override.
>>>>>
>>>>> OK, having reproduced on my Juno, the problem looks to be that
>>>>> of_for_each_phandle() leaves err set to -ENOENT after successfully
>>>>> walking a phandle list, which makes __find_legacy_master_phandle()
>>>>> always bail out after the first SMMU.
>>>>>
>>>>> Can you confirm that the following diff fixes things for you?
>>>>
>>>> Well it improves but there are still differences in the group topology.
>>>> The PFs now are in group 0.
>>>>
>>>> root@trusty:~# lspci -nk
>>>> 00:00.0 0600: 1022:1a00
>>>>         Subsystem: 1022:1a00
>>>> 00:02.0 0600: 1022:1a01
>>>> 00:02.2 0604: 1022:1a02
>>>>         Kernel driver in use: pcieport
>>>> 01:00.0 0200: 8086:1521 (rev 01)
>>>>         Subsystem: 8086:0002
>>>>         Kernel driver in use: igb
>>>> 01:00.1 0200: 8086:1521 (rev 01)
>>>>         Subsystem: 8086:0002
>>>>         Kernel driver in use: igb
>>>>
>>>>
>>>> with your series + fix:
>>>> /sys/kernel/iommu_groups/
>>>> /sys/kernel/iommu_groups/3
>>>> /sys/kernel/iommu_groups/3/devices
>>>> /sys/kernel/iommu_groups/3/devices/0000:00:00.0
>>>> /sys/kernel/iommu_groups/1
>>>> /sys/kernel/iommu_groups/1/devices
>>>> /sys/kernel/iommu_groups/1/devices/e0700000.xgmac
>>>> /sys/kernel/iommu_groups/4
>>>> /sys/kernel/iommu_groups/4/devices
>>>> /sys/kernel/iommu_groups/4/devices/0000:00:02.2
>>>> /sys/kernel/iommu_groups/4/devices/0000:00:02.0
>>>> /sys/kernel/iommu_groups/2
>>>> /sys/kernel/iommu_groups/2/devices
>>>> /sys/kernel/iommu_groups/2/devices/e0900000.xgmac
>>>> /sys/kernel/iommu_groups/0
>>>> /sys/kernel/iommu_groups/0/devices
>>>> /sys/kernel/iommu_groups/0/devices/0000:01:00.1
>>>> /sys/kernel/iommu_groups/0/devices/f0000000.pcie
>>>> /sys/kernel/iommu_groups/0/devices/0000:01:00.0
>>>>
>>>> Before (4.8-rc5):
>>>>
>>>> /sys/kernel/iommu_groups/
>>>> /sys/kernel/iommu_groups/3
>>>> /sys/kernel/iommu_groups/3/devices
>>>> /sys/kernel/iommu_groups/3/devices/0000:00:00.0
>>>> /sys/kernel/iommu_groups/1
>>>> /sys/kernel/iommu_groups/1/devices
>>>> /sys/kernel/iommu_groups/1/devices/e0700000.xgmac
>>>> /sys/kernel/iommu_groups/4
>>>> /sys/kernel/iommu_groups/4/devices
>>>> /sys/kernel/iommu_groups/4/devices/0000:00:02.2
>>>> /sys/kernel/iommu_groups/4/devices/0000:01:00.1
>>>> /sys/kernel/iommu_groups/4/devices/0000:00:02.0
>>>> /sys/kernel/iommu_groups/4/devices/0000:01:00.0
>>>> /sys/kernel/iommu_groups/2
>>>> /sys/kernel/iommu_groups/2/devices
>>>> /sys/kernel/iommu_groups/2/devices/e0900000.xgmac
>>>> /sys/kernel/iommu_groups/0
>>>> /sys/kernel/iommu_groups/0/devices
>>>> /sys/kernel/iommu_groups/0/devices/f0000000.pcie
>>>
>>> Your DT claims that f0000000.pcie (i.e. the "platform device" side of
>>> the host controller) owns the IDs 0x100 0x101 0x102 0x103 0x200 0x201
>>> 0x202 0x203 0x300 0x301 0x302 0x303 0x400 0x401 0x402 0x403. Thus when
>>> new devices (the PCI PFs) come along *also* claiming those IDs (via the
>>> RID-to-SID assumption), we now detect the aliasing and assign them to
>>> the existing group.
>>>
>>> The only way that DT worked without SMR conflicts before is that
>>> f0000000.pcie wasn't actually getting attached to a domain, thus wasn't
>>> getting SMRs allocated (ISTR you _did_ get conflicts back when we first
>>> tried to switch on default domains; that was probably the reason).
>>
>> Thanks for your explanations. meanwhile I converted the overdrive dt to
>> the new syntax and it works properly.
> 
> Yay!
> 
>> However I noticed there are MSI frame iommu mapping although the
>> device's domain is the default one. There is no check currently in
>> 21/22. I guess this isn't needed?
> 
> Au contraire ;)
;-)

 With the generic binding, default domains are properly
> implemented, i.e. they have translation, so anything not mapped will
> fault. I *had* to write patch 21 as part of this series, since having
> DMA ops for PCI devices at the cost of breaking all MSIs was clearly not
> a viable solution. If you see mappings, then it's because someone is
> using MSIs - so if you fancy, you can try rewinding back to patch 19 or
> 20 to watch them get stuck.
Hum OK; thanks for the explanation. With that implementation however,
don't we face back the issue we encountered in early stage of default
domain implementation:

With this sample config (AMD overdrive + I350-T2 + 2VFs per PF) I fill
the 8 context banks. Whereas practically we didn't need them before?

00:00.0 0600: 1022:1a00
	Subsystem: 1022:1a00
00:02.0 0600: 1022:1a01
00:02.2 0604: 1022:1a02
	Kernel driver in use: pcieport
01:00.0 0200: 8086:1521 (rev 01)
	Subsystem: 8086:0002
	Kernel driver in use: igb
01:00.1 0200: 8086:1521 (rev 01)
	Subsystem: 8086:0002
	Kernel driver in use: igb
01:10.0 0200: 8086:1520 (rev 01) -> context 5
	Subsystem: 8086:0002
	Kernel driver in use: vfio-pci
01:10.1 0200: 8086:1520 (rev 01) -> context 7
	Subsystem: 8086:0002
	Kernel driver in use: igbvf
01:10.4 0200: 8086:1520 (rev 01) -> context 6
	Subsystem: 8086:0002
	Kernel driver in use: igbvf
01:10.5 0200: 8086:1520 (rev 01) -> shortage
	Subsystem: 8086:0002
	Kernel driver in use: igbvf

So I can't even do passthrough anymore with that config. Is there
anything wrong in my setup/understanding?

Thanks

Eric

> 
> Robin.
> 
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
> 
--
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] 104+ messages in thread

* [PATCH v7 00/22] Generic DT bindings for PCI IOMMUs and ARM SMMU
@ 2016-09-15 16:46                             ` Auger Eric
  0 siblings, 0 replies; 104+ messages in thread
From: Auger Eric @ 2016-09-15 16:46 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Robin,
On 15/09/2016 12:15, Robin Murphy wrote:
> On 15/09/16 10:29, Auger Eric wrote:
>> Hi Robin,
>>
>> On 14/09/2016 14:53, Robin Murphy wrote:
>>> On 14/09/16 13:32, Auger Eric wrote:
>>>> Hi,
>>>> On 14/09/2016 12:35, Robin Murphy wrote:
>>>>> On 14/09/16 09:41, Auger Eric wrote:
>>>>>> Hi,
>>>>>>
>>>>>> On 12/09/2016 18:13, Robin Murphy wrote:
>>>>>>> Hi all,
>>>>>>>
>>>>>>> To any more confusing fixups and crazily numbered extra patches, here's
>>>>>>> a quick v7 with everything rebased into the right order. The significant
>>>>>>> change this time is to implement iommu_fwspec properly from the start,
>>>>>>> which ends up being far simpler and more robust than faffing about
>>>>>>> introducing it somewhere 'less intrusive' to move toward core code later.
>>>>>>>
>>>>>>> New branch in the logical place:
>>>>>>>
>>>>>>> git://linux-arm.org/linux-rm iommu/generic-v7
>>>>>>
>>>>>> For information, as discussed privately with Robin I experience some
>>>>>> regressions with the former and now deprecated dt description.
>>>>>>
>>>>>> on my AMD Overdrive board and my old dt description I now only see a
>>>>>> single group:
>>>>>>
>>>>>> /sys/kernel/iommu_groups/
>>>>>> /sys/kernel/iommu_groups/0
>>>>>> /sys/kernel/iommu_groups/0/devices
>>>>>> /sys/kernel/iommu_groups/0/devices/e0700000.xgmac
>>>>>>
>>>>>> whereas I formerly see
>>>>>>
>>>>>> /sys/kernel/iommu_groups/
>>>>>> /sys/kernel/iommu_groups/3
>>>>>> /sys/kernel/iommu_groups/3/devices
>>>>>> /sys/kernel/iommu_groups/3/devices/0000:00:00.0
>>>>>> /sys/kernel/iommu_groups/1
>>>>>> /sys/kernel/iommu_groups/1/devices
>>>>>> /sys/kernel/iommu_groups/1/devices/e0700000.xgmac
>>>>>> /sys/kernel/iommu_groups/4
>>>>>> /sys/kernel/iommu_groups/4/devices
>>>>>> /sys/kernel/iommu_groups/4/devices/0000:00:02.2
>>>>>> /sys/kernel/iommu_groups/4/devices/0000:01:00.1
>>>>>> /sys/kernel/iommu_groups/4/devices/0000:00:02.0
>>>>>> /sys/kernel/iommu_groups/4/devices/0000:01:00.0
>>>>>> /sys/kernel/iommu_groups/2
>>>>>> /sys/kernel/iommu_groups/2/devices
>>>>>> /sys/kernel/iommu_groups/2/devices/e0900000.xgmac
>>>>>> /sys/kernel/iommu_groups/0
>>>>>> /sys/kernel/iommu_groups/0/devices
>>>>>> /sys/kernel/iommu_groups/0/devices/f0000000.pcie
>>>>>>
>>>>>> This is the group topology without ACS override. Applying the non
>>>>>> upstreamed "pci: Enable overrides for missing ACS capabilities" I used
>>>>>> to see separate groups for each PCIe components. Now I don't see any
>>>>>> difference with and without ACS override.
>>>>>
>>>>> OK, having reproduced on my Juno, the problem looks to be that
>>>>> of_for_each_phandle() leaves err set to -ENOENT after successfully
>>>>> walking a phandle list, which makes __find_legacy_master_phandle()
>>>>> always bail out after the first SMMU.
>>>>>
>>>>> Can you confirm that the following diff fixes things for you?
>>>>
>>>> Well it improves but there are still differences in the group topology.
>>>> The PFs now are in group 0.
>>>>
>>>> root at trusty:~# lspci -nk
>>>> 00:00.0 0600: 1022:1a00
>>>>         Subsystem: 1022:1a00
>>>> 00:02.0 0600: 1022:1a01
>>>> 00:02.2 0604: 1022:1a02
>>>>         Kernel driver in use: pcieport
>>>> 01:00.0 0200: 8086:1521 (rev 01)
>>>>         Subsystem: 8086:0002
>>>>         Kernel driver in use: igb
>>>> 01:00.1 0200: 8086:1521 (rev 01)
>>>>         Subsystem: 8086:0002
>>>>         Kernel driver in use: igb
>>>>
>>>>
>>>> with your series + fix:
>>>> /sys/kernel/iommu_groups/
>>>> /sys/kernel/iommu_groups/3
>>>> /sys/kernel/iommu_groups/3/devices
>>>> /sys/kernel/iommu_groups/3/devices/0000:00:00.0
>>>> /sys/kernel/iommu_groups/1
>>>> /sys/kernel/iommu_groups/1/devices
>>>> /sys/kernel/iommu_groups/1/devices/e0700000.xgmac
>>>> /sys/kernel/iommu_groups/4
>>>> /sys/kernel/iommu_groups/4/devices
>>>> /sys/kernel/iommu_groups/4/devices/0000:00:02.2
>>>> /sys/kernel/iommu_groups/4/devices/0000:00:02.0
>>>> /sys/kernel/iommu_groups/2
>>>> /sys/kernel/iommu_groups/2/devices
>>>> /sys/kernel/iommu_groups/2/devices/e0900000.xgmac
>>>> /sys/kernel/iommu_groups/0
>>>> /sys/kernel/iommu_groups/0/devices
>>>> /sys/kernel/iommu_groups/0/devices/0000:01:00.1
>>>> /sys/kernel/iommu_groups/0/devices/f0000000.pcie
>>>> /sys/kernel/iommu_groups/0/devices/0000:01:00.0
>>>>
>>>> Before (4.8-rc5):
>>>>
>>>> /sys/kernel/iommu_groups/
>>>> /sys/kernel/iommu_groups/3
>>>> /sys/kernel/iommu_groups/3/devices
>>>> /sys/kernel/iommu_groups/3/devices/0000:00:00.0
>>>> /sys/kernel/iommu_groups/1
>>>> /sys/kernel/iommu_groups/1/devices
>>>> /sys/kernel/iommu_groups/1/devices/e0700000.xgmac
>>>> /sys/kernel/iommu_groups/4
>>>> /sys/kernel/iommu_groups/4/devices
>>>> /sys/kernel/iommu_groups/4/devices/0000:00:02.2
>>>> /sys/kernel/iommu_groups/4/devices/0000:01:00.1
>>>> /sys/kernel/iommu_groups/4/devices/0000:00:02.0
>>>> /sys/kernel/iommu_groups/4/devices/0000:01:00.0
>>>> /sys/kernel/iommu_groups/2
>>>> /sys/kernel/iommu_groups/2/devices
>>>> /sys/kernel/iommu_groups/2/devices/e0900000.xgmac
>>>> /sys/kernel/iommu_groups/0
>>>> /sys/kernel/iommu_groups/0/devices
>>>> /sys/kernel/iommu_groups/0/devices/f0000000.pcie
>>>
>>> Your DT claims that f0000000.pcie (i.e. the "platform device" side of
>>> the host controller) owns the IDs 0x100 0x101 0x102 0x103 0x200 0x201
>>> 0x202 0x203 0x300 0x301 0x302 0x303 0x400 0x401 0x402 0x403. Thus when
>>> new devices (the PCI PFs) come along *also* claiming those IDs (via the
>>> RID-to-SID assumption), we now detect the aliasing and assign them to
>>> the existing group.
>>>
>>> The only way that DT worked without SMR conflicts before is that
>>> f0000000.pcie wasn't actually getting attached to a domain, thus wasn't
>>> getting SMRs allocated (ISTR you _did_ get conflicts back when we first
>>> tried to switch on default domains; that was probably the reason).
>>
>> Thanks for your explanations. meanwhile I converted the overdrive dt to
>> the new syntax and it works properly.
> 
> Yay!
> 
>> However I noticed there are MSI frame iommu mapping although the
>> device's domain is the default one. There is no check currently in
>> 21/22. I guess this isn't needed?
> 
> Au contraire ;)
;-)

 With the generic binding, default domains are properly
> implemented, i.e. they have translation, so anything not mapped will
> fault. I *had* to write patch 21 as part of this series, since having
> DMA ops for PCI devices at the cost of breaking all MSIs was clearly not
> a viable solution. If you see mappings, then it's because someone is
> using MSIs - so if you fancy, you can try rewinding back to patch 19 or
> 20 to watch them get stuck.
Hum OK; thanks for the explanation. With that implementation however,
don't we face back the issue we encountered in early stage of default
domain implementation:

With this sample config (AMD overdrive + I350-T2 + 2VFs per PF) I fill
the 8 context banks. Whereas practically we didn't need them before?

00:00.0 0600: 1022:1a00
	Subsystem: 1022:1a00
00:02.0 0600: 1022:1a01
00:02.2 0604: 1022:1a02
	Kernel driver in use: pcieport
01:00.0 0200: 8086:1521 (rev 01)
	Subsystem: 8086:0002
	Kernel driver in use: igb
01:00.1 0200: 8086:1521 (rev 01)
	Subsystem: 8086:0002
	Kernel driver in use: igb
01:10.0 0200: 8086:1520 (rev 01) -> context 5
	Subsystem: 8086:0002
	Kernel driver in use: vfio-pci
01:10.1 0200: 8086:1520 (rev 01) -> context 7
	Subsystem: 8086:0002
	Kernel driver in use: igbvf
01:10.4 0200: 8086:1520 (rev 01) -> context 6
	Subsystem: 8086:0002
	Kernel driver in use: igbvf
01:10.5 0200: 8086:1520 (rev 01) -> shortage
	Subsystem: 8086:0002
	Kernel driver in use: igbvf

So I can't even do passthrough anymore with that config. Is there
anything wrong in my setup/understanding?

Thanks

Eric

> 
> Robin.
> 
> _______________________________________________
> 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] 104+ messages in thread

* Re: [PATCH v7 00/22] Generic DT bindings for PCI IOMMUs and ARM SMMU
  2016-09-15 16:46                             ` Auger Eric
@ 2016-09-16 16:18                                 ` Robin Murphy
  -1 siblings, 0 replies; 104+ messages in thread
From: Robin Murphy @ 2016-09-16 16:18 UTC (permalink / raw)
  To: Auger Eric, will.deacon-5wv7dgnIgG8, joro-zLv9SwRftAIdnm+yROfE0A,
	iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, punit.agrawal-5wv7dgnIgG8,
	thunder.leizhen-hv44wF8Li93QT0dZR+AlfA

Hi Eric,

On 15/09/16 17:46, Auger Eric wrote:
[...]
> Hum OK; thanks for the explanation. With that implementation however,
> don't we face back the issue we encountered in early stage of default
> domain implementation:
> 
> With this sample config (AMD overdrive + I350-T2 + 2VFs per PF) I fill
> the 8 context banks. Whereas practically we didn't need them before?
> 
> 00:00.0 0600: 1022:1a00
> 	Subsystem: 1022:1a00
> 00:02.0 0600: 1022:1a01
> 00:02.2 0604: 1022:1a02
> 	Kernel driver in use: pcieport
> 01:00.0 0200: 8086:1521 (rev 01)
> 	Subsystem: 8086:0002
> 	Kernel driver in use: igb
> 01:00.1 0200: 8086:1521 (rev 01)
> 	Subsystem: 8086:0002
> 	Kernel driver in use: igb
> 01:10.0 0200: 8086:1520 (rev 01) -> context 5
> 	Subsystem: 8086:0002
> 	Kernel driver in use: vfio-pci
> 01:10.1 0200: 8086:1520 (rev 01) -> context 7
> 	Subsystem: 8086:0002
> 	Kernel driver in use: igbvf
> 01:10.4 0200: 8086:1520 (rev 01) -> context 6
> 	Subsystem: 8086:0002
> 	Kernel driver in use: igbvf
> 01:10.5 0200: 8086:1520 (rev 01) -> shortage
> 	Subsystem: 8086:0002
> 	Kernel driver in use: igbvf
> 
> So I can't even do passthrough anymore with that config. Is there
> anything wrong in my setup/understanding?

It's kind of hard to avoid, really - people want DMA ops support (try
plugging a card which can only do 32-bit DMA into that Seattle, for
instance); DMA ops need default domains; default domains are allocated
per group; each domain requires a context bank to back it; thus if you
have 9 groups and 8 context banks you're in a corner. It would relieve
the pressure to have a single default domain per SMMU, but we've no way
to do that due to the way the iommu_domain_alloc() API is intended to work.

Ultimately, it's a hardware limitation of that platform - plug in a card
with 16 VFs with ACS, and either way you're stuck. There are a number of
bodges I can think of that would make your specific situation work, but
none of them are really sufficiently general to consider upstreaming.
The most logical thing to do right now, if you were happy without DMA
ops using the old binding before, is to keep using the old binding (just
fixing your DT with #stream-id-cells=0 on the host controller so as not
to create the fake aliasing problem). Hopefully future platforms will be
in a position to couple their PCI host controllers to an IOMMU which is
actually designed to support a PCI host controller.

What I probably will do, though, since we have the functionality in
place for the sake of the old binding, and I think there are other folks
who want PCI iommu-map support but would prefer not to bother with DMA
ops on the host, is add a command-line option to disable DMA domains
even for the generic bindings.

Robin.
--
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] 104+ messages in thread

* [PATCH v7 00/22] Generic DT bindings for PCI IOMMUs and ARM SMMU
@ 2016-09-16 16:18                                 ` Robin Murphy
  0 siblings, 0 replies; 104+ messages in thread
From: Robin Murphy @ 2016-09-16 16:18 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Eric,

On 15/09/16 17:46, Auger Eric wrote:
[...]
> Hum OK; thanks for the explanation. With that implementation however,
> don't we face back the issue we encountered in early stage of default
> domain implementation:
> 
> With this sample config (AMD overdrive + I350-T2 + 2VFs per PF) I fill
> the 8 context banks. Whereas practically we didn't need them before?
> 
> 00:00.0 0600: 1022:1a00
> 	Subsystem: 1022:1a00
> 00:02.0 0600: 1022:1a01
> 00:02.2 0604: 1022:1a02
> 	Kernel driver in use: pcieport
> 01:00.0 0200: 8086:1521 (rev 01)
> 	Subsystem: 8086:0002
> 	Kernel driver in use: igb
> 01:00.1 0200: 8086:1521 (rev 01)
> 	Subsystem: 8086:0002
> 	Kernel driver in use: igb
> 01:10.0 0200: 8086:1520 (rev 01) -> context 5
> 	Subsystem: 8086:0002
> 	Kernel driver in use: vfio-pci
> 01:10.1 0200: 8086:1520 (rev 01) -> context 7
> 	Subsystem: 8086:0002
> 	Kernel driver in use: igbvf
> 01:10.4 0200: 8086:1520 (rev 01) -> context 6
> 	Subsystem: 8086:0002
> 	Kernel driver in use: igbvf
> 01:10.5 0200: 8086:1520 (rev 01) -> shortage
> 	Subsystem: 8086:0002
> 	Kernel driver in use: igbvf
> 
> So I can't even do passthrough anymore with that config. Is there
> anything wrong in my setup/understanding?

It's kind of hard to avoid, really - people want DMA ops support (try
plugging a card which can only do 32-bit DMA into that Seattle, for
instance); DMA ops need default domains; default domains are allocated
per group; each domain requires a context bank to back it; thus if you
have 9 groups and 8 context banks you're in a corner. It would relieve
the pressure to have a single default domain per SMMU, but we've no way
to do that due to the way the iommu_domain_alloc() API is intended to work.

Ultimately, it's a hardware limitation of that platform - plug in a card
with 16 VFs with ACS, and either way you're stuck. There are a number of
bodges I can think of that would make your specific situation work, but
none of them are really sufficiently general to consider upstreaming.
The most logical thing to do right now, if you were happy without DMA
ops using the old binding before, is to keep using the old binding (just
fixing your DT with #stream-id-cells=0 on the host controller so as not
to create the fake aliasing problem). Hopefully future platforms will be
in a position to couple their PCI host controllers to an IOMMU which is
actually designed to support a PCI host controller.

What I probably will do, though, since we have the functionality in
place for the sake of the old binding, and I think there are other folks
who want PCI iommu-map support but would prefer not to bother with DMA
ops on the host, is add a command-line option to disable DMA domains
even for the generic bindings.

Robin.

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

* Re: [PATCH v7 00/22] Generic DT bindings for PCI IOMMUs and ARM SMMU
  2016-09-16 16:18                                 ` Robin Murphy
@ 2016-09-19 12:13                                     ` Auger Eric
  -1 siblings, 0 replies; 104+ messages in thread
From: Auger Eric @ 2016-09-19 12:13 UTC (permalink / raw)
  To: Robin Murphy, will.deacon-5wv7dgnIgG8,
	joro-zLv9SwRftAIdnm+yROfE0A,
	iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, punit.agrawal-5wv7dgnIgG8,
	thunder.leizhen-hv44wF8Li93QT0dZR+AlfA

Hi Robin,

On 16/09/2016 18:18, Robin Murphy wrote:
> Hi Eric,
> 
> On 15/09/16 17:46, Auger Eric wrote:
> [...]
>> Hum OK; thanks for the explanation. With that implementation however,
>> don't we face back the issue we encountered in early stage of default
>> domain implementation:
>>
>> With this sample config (AMD overdrive + I350-T2 + 2VFs per PF) I fill
>> the 8 context banks. Whereas practically we didn't need them before?
>>
>> 00:00.0 0600: 1022:1a00
>> 	Subsystem: 1022:1a00
>> 00:02.0 0600: 1022:1a01
>> 00:02.2 0604: 1022:1a02
>> 	Kernel driver in use: pcieport
>> 01:00.0 0200: 8086:1521 (rev 01)
>> 	Subsystem: 8086:0002
>> 	Kernel driver in use: igb
>> 01:00.1 0200: 8086:1521 (rev 01)
>> 	Subsystem: 8086:0002
>> 	Kernel driver in use: igb
>> 01:10.0 0200: 8086:1520 (rev 01) -> context 5
>> 	Subsystem: 8086:0002
>> 	Kernel driver in use: vfio-pci
>> 01:10.1 0200: 8086:1520 (rev 01) -> context 7
>> 	Subsystem: 8086:0002
>> 	Kernel driver in use: igbvf
>> 01:10.4 0200: 8086:1520 (rev 01) -> context 6
>> 	Subsystem: 8086:0002
>> 	Kernel driver in use: igbvf
>> 01:10.5 0200: 8086:1520 (rev 01) -> shortage
>> 	Subsystem: 8086:0002
>> 	Kernel driver in use: igbvf
>>
>> So I can't even do passthrough anymore with that config. Is there
>> anything wrong in my setup/understanding?
> 
> It's kind of hard to avoid, really - people want DMA ops support (try
> plugging a card which can only do 32-bit DMA into that Seattle, for
> instance); DMA ops need default domains; default domains are allocated
> per group; each domain requires a context bank to back it; thus if you
> have 9 groups and 8 context banks you're in a corner. It would relieve
> the pressure to have a single default domain per SMMU, but we've no way
> to do that due to the way the iommu_domain_alloc() API is intended to work.
> 
> Ultimately, it's a hardware limitation of that platform - plug in a card
> with 16 VFs with ACS, and either way you're stuck. There are a number of
> bodges I can think of that would make your specific situation work, but
> none of them are really sufficiently general to consider upstreaming.
> The most logical thing to do right now, if you were happy without DMA
> ops using the old binding before, is to keep using the old binding (just
> fixing your DT with #stream-id-cells=0 on the host controller so as not
> to create the fake aliasing problem). Hopefully future platforms will be
> in a position to couple their PCI host controllers to an IOMMU which is
> actually designed to support a PCI host controller.
> 
> What I probably will do, though, since we have the functionality in
> place for the sake of the old binding, and I think there are other folks
> who want PCI iommu-map support but would prefer not to bother with DMA
> ops on the host, is add a command-line option to disable DMA domains
> even for the generic bindings.

Yes this would be a good thing I think. This series has an important
impact on platforms which do not have smmu v3, where contexts are scarce
HW resources.

Thanks

Eric

> 
> Robin.
> 
> _______________________________________________
> 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] 104+ messages in thread

* [PATCH v7 00/22] Generic DT bindings for PCI IOMMUs and ARM SMMU
@ 2016-09-19 12:13                                     ` Auger Eric
  0 siblings, 0 replies; 104+ messages in thread
From: Auger Eric @ 2016-09-19 12:13 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Robin,

On 16/09/2016 18:18, Robin Murphy wrote:
> Hi Eric,
> 
> On 15/09/16 17:46, Auger Eric wrote:
> [...]
>> Hum OK; thanks for the explanation. With that implementation however,
>> don't we face back the issue we encountered in early stage of default
>> domain implementation:
>>
>> With this sample config (AMD overdrive + I350-T2 + 2VFs per PF) I fill
>> the 8 context banks. Whereas practically we didn't need them before?
>>
>> 00:00.0 0600: 1022:1a00
>> 	Subsystem: 1022:1a00
>> 00:02.0 0600: 1022:1a01
>> 00:02.2 0604: 1022:1a02
>> 	Kernel driver in use: pcieport
>> 01:00.0 0200: 8086:1521 (rev 01)
>> 	Subsystem: 8086:0002
>> 	Kernel driver in use: igb
>> 01:00.1 0200: 8086:1521 (rev 01)
>> 	Subsystem: 8086:0002
>> 	Kernel driver in use: igb
>> 01:10.0 0200: 8086:1520 (rev 01) -> context 5
>> 	Subsystem: 8086:0002
>> 	Kernel driver in use: vfio-pci
>> 01:10.1 0200: 8086:1520 (rev 01) -> context 7
>> 	Subsystem: 8086:0002
>> 	Kernel driver in use: igbvf
>> 01:10.4 0200: 8086:1520 (rev 01) -> context 6
>> 	Subsystem: 8086:0002
>> 	Kernel driver in use: igbvf
>> 01:10.5 0200: 8086:1520 (rev 01) -> shortage
>> 	Subsystem: 8086:0002
>> 	Kernel driver in use: igbvf
>>
>> So I can't even do passthrough anymore with that config. Is there
>> anything wrong in my setup/understanding?
> 
> It's kind of hard to avoid, really - people want DMA ops support (try
> plugging a card which can only do 32-bit DMA into that Seattle, for
> instance); DMA ops need default domains; default domains are allocated
> per group; each domain requires a context bank to back it; thus if you
> have 9 groups and 8 context banks you're in a corner. It would relieve
> the pressure to have a single default domain per SMMU, but we've no way
> to do that due to the way the iommu_domain_alloc() API is intended to work.
> 
> Ultimately, it's a hardware limitation of that platform - plug in a card
> with 16 VFs with ACS, and either way you're stuck. There are a number of
> bodges I can think of that would make your specific situation work, but
> none of them are really sufficiently general to consider upstreaming.
> The most logical thing to do right now, if you were happy without DMA
> ops using the old binding before, is to keep using the old binding (just
> fixing your DT with #stream-id-cells=0 on the host controller so as not
> to create the fake aliasing problem). Hopefully future platforms will be
> in a position to couple their PCI host controllers to an IOMMU which is
> actually designed to support a PCI host controller.
> 
> What I probably will do, though, since we have the functionality in
> place for the sake of the old binding, and I think there are other folks
> who want PCI iommu-map support but would prefer not to bother with DMA
> ops on the host, is add a command-line option to disable DMA domains
> even for the generic bindings.

Yes this would be a good thing I think. This series has an important
impact on platforms which do not have smmu v3, where contexts are scarce
HW resources.

Thanks

Eric

> 
> Robin.
> 
> _______________________________________________
> 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] 104+ messages in thread

* Re: [PATCH v7 00/22] Generic DT bindings for PCI IOMMUs and ARM SMMU
  2016-09-19 12:13                                     ` Auger Eric
@ 2016-09-19 12:24                                         ` Will Deacon
  -1 siblings, 0 replies; 104+ messages in thread
From: Will Deacon @ 2016-09-19 12:24 UTC (permalink / raw)
  To: Auger Eric
  Cc: Robin Murphy, joro-zLv9SwRftAIdnm+yROfE0A,
	iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA, punit.agrawal-5wv7dgnIgG8,
	thunder.leizhen-hv44wF8Li93QT0dZR+AlfA

On Mon, Sep 19, 2016 at 02:13:45PM +0200, Auger Eric wrote:
> On 16/09/2016 18:18, Robin Murphy wrote:
> > What I probably will do, though, since we have the functionality in
> > place for the sake of the old binding, and I think there are other folks
> > who want PCI iommu-map support but would prefer not to bother with DMA
> > ops on the host, is add a command-line option to disable DMA domains
> > even for the generic bindings.
> 
> Yes this would be a good thing I think. This series has an important
> impact on platforms which do not have smmu v3, where contexts are scarce
> HW resources.

Rather than disabling DMA domains entirely, we could specify a number
of contexts to reserve for other use (e.g. VFIO). It's a pity that these
options are global for the system, as opposed to per SMMU instance,
but I can't see a good way around that.

Will
--
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] 104+ messages in thread

* [PATCH v7 00/22] Generic DT bindings for PCI IOMMUs and ARM SMMU
@ 2016-09-19 12:24                                         ` Will Deacon
  0 siblings, 0 replies; 104+ messages in thread
From: Will Deacon @ 2016-09-19 12:24 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Sep 19, 2016 at 02:13:45PM +0200, Auger Eric wrote:
> On 16/09/2016 18:18, Robin Murphy wrote:
> > What I probably will do, though, since we have the functionality in
> > place for the sake of the old binding, and I think there are other folks
> > who want PCI iommu-map support but would prefer not to bother with DMA
> > ops on the host, is add a command-line option to disable DMA domains
> > even for the generic bindings.
> 
> Yes this would be a good thing I think. This series has an important
> impact on platforms which do not have smmu v3, where contexts are scarce
> HW resources.

Rather than disabling DMA domains entirely, we could specify a number
of contexts to reserve for other use (e.g. VFIO). It's a pity that these
options are global for the system, as opposed to per SMMU instance,
but I can't see a good way around that.

Will

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

* Re: [PATCH v7 00/22] Generic DT bindings for PCI IOMMUs and ARM SMMU
  2016-09-19 12:24                                         ` Will Deacon
@ 2016-09-19 12:41                                             ` Robin Murphy
  -1 siblings, 0 replies; 104+ messages in thread
From: Robin Murphy @ 2016-09-19 12:41 UTC (permalink / raw)
  To: Will Deacon, Auger Eric
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
	iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	punit.agrawal-5wv7dgnIgG8,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	thunder.leizhen-hv44wF8Li93QT0dZR+AlfA

On 19/09/16 13:24, Will Deacon wrote:
> On Mon, Sep 19, 2016 at 02:13:45PM +0200, Auger Eric wrote:
>> On 16/09/2016 18:18, Robin Murphy wrote:
>>> What I probably will do, though, since we have the functionality in
>>> place for the sake of the old binding, and I think there are other folks
>>> who want PCI iommu-map support but would prefer not to bother with DMA
>>> ops on the host, is add a command-line option to disable DMA domains
>>> even for the generic bindings.
>>
>> Yes this would be a good thing I think. This series has an important
>> impact on platforms which do not have smmu v3, where contexts are scarce
>> HW resources.
> 
> Rather than disabling DMA domains entirely, we could specify a number
> of contexts to reserve for other use (e.g. VFIO). It's a pity that these
> options are global for the system, as opposed to per SMMU instance,
> but I can't see a good way around that.

The problem with that approach is that due to bus traversal order you'd
typically end up with the even-more-limited number of non-reserved
contexts getting consumed by bridges that are unlikely to ever actually
use their DMA ops, so you end up barely any better off than just not
doing DMA ops at all, which we already have the code for.

And yeah, if you had, say, "arm-smmu.reserved_s2_contexts=4", for the
benefit of your PCI SMMU, what do the SMMUs in front of your other
platform devices with only 2 contexts (and which only want DMA ops) do?

Robin.

> 
> Will
> _______________________________________________
> iommu mailing list
> iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA@public.gmane.org
> https://lists.linuxfoundation.org/mailman/listinfo/iommu
> 

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

* [PATCH v7 00/22] Generic DT bindings for PCI IOMMUs and ARM SMMU
@ 2016-09-19 12:41                                             ` Robin Murphy
  0 siblings, 0 replies; 104+ messages in thread
From: Robin Murphy @ 2016-09-19 12:41 UTC (permalink / raw)
  To: linux-arm-kernel

On 19/09/16 13:24, Will Deacon wrote:
> On Mon, Sep 19, 2016 at 02:13:45PM +0200, Auger Eric wrote:
>> On 16/09/2016 18:18, Robin Murphy wrote:
>>> What I probably will do, though, since we have the functionality in
>>> place for the sake of the old binding, and I think there are other folks
>>> who want PCI iommu-map support but would prefer not to bother with DMA
>>> ops on the host, is add a command-line option to disable DMA domains
>>> even for the generic bindings.
>>
>> Yes this would be a good thing I think. This series has an important
>> impact on platforms which do not have smmu v3, where contexts are scarce
>> HW resources.
> 
> Rather than disabling DMA domains entirely, we could specify a number
> of contexts to reserve for other use (e.g. VFIO). It's a pity that these
> options are global for the system, as opposed to per SMMU instance,
> but I can't see a good way around that.

The problem with that approach is that due to bus traversal order you'd
typically end up with the even-more-limited number of non-reserved
contexts getting consumed by bridges that are unlikely to ever actually
use their DMA ops, so you end up barely any better off than just not
doing DMA ops at all, which we already have the code for.

And yeah, if you had, say, "arm-smmu.reserved_s2_contexts=4", for the
benefit of your PCI SMMU, what do the SMMUs in front of your other
platform devices with only 2 contexts (and which only want DMA ops) do?

Robin.

> 
> Will
> _______________________________________________
> iommu mailing list
> iommu at lists.linux-foundation.org
> https://lists.linuxfoundation.org/mailman/listinfo/iommu
> 

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

* Re: [PATCH v7 00/22] Generic DT bindings for PCI IOMMUs and ARM SMMU
  2016-09-19 12:41                                             ` Robin Murphy
@ 2016-09-19 14:17                                                 ` Will Deacon
  -1 siblings, 0 replies; 104+ messages in thread
From: Will Deacon @ 2016-09-19 14:17 UTC (permalink / raw)
  To: Robin Murphy
  Cc: Auger Eric, devicetree-u79uwXL29TY76Z2rM5mHXA,
	punit.agrawal-5wv7dgnIgG8,
	iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	thunder.leizhen-hv44wF8Li93QT0dZR+AlfA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

On Mon, Sep 19, 2016 at 01:41:47PM +0100, Robin Murphy wrote:
> On 19/09/16 13:24, Will Deacon wrote:
> > On Mon, Sep 19, 2016 at 02:13:45PM +0200, Auger Eric wrote:
> >> On 16/09/2016 18:18, Robin Murphy wrote:
> >>> What I probably will do, though, since we have the functionality in
> >>> place for the sake of the old binding, and I think there are other folks
> >>> who want PCI iommu-map support but would prefer not to bother with DMA
> >>> ops on the host, is add a command-line option to disable DMA domains
> >>> even for the generic bindings.
> >>
> >> Yes this would be a good thing I think. This series has an important
> >> impact on platforms which do not have smmu v3, where contexts are scarce
> >> HW resources.
> > 
> > Rather than disabling DMA domains entirely, we could specify a number
> > of contexts to reserve for other use (e.g. VFIO). It's a pity that these
> > options are global for the system, as opposed to per SMMU instance,
> > but I can't see a good way around that.
> 
> The problem with that approach is that due to bus traversal order you'd
> typically end up with the even-more-limited number of non-reserved
> contexts getting consumed by bridges that are unlikely to ever actually
> use their DMA ops, so you end up barely any better off than just not
> doing DMA ops at all, which we already have the code for.

For Seattle, perhaps, but that's quite a big generalisation. If we're
going to add an option, I'd much rather add something with some flexibility,
rather than end up supporting both "disable_dma_domains" and
"reserved_s2_contexts" in the long run.

> And yeah, if you had, say, "arm-smmu.reserved_s2_contexts=4", for the
> benefit of your PCI SMMU, what do the SMMUs in front of your other
> platform devices with only 2 contexts (and which only want DMA ops) do?

There's two things in this example:

  (1) If other SMMUs only have 2 contexts, then they reserve both and warn
  (2) If some SMMUs want DMA ops and others don't, then you have an issue
      with *any* sort of cmdline option to control this

Will
--
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] 104+ messages in thread

* [PATCH v7 00/22] Generic DT bindings for PCI IOMMUs and ARM SMMU
@ 2016-09-19 14:17                                                 ` Will Deacon
  0 siblings, 0 replies; 104+ messages in thread
From: Will Deacon @ 2016-09-19 14:17 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Sep 19, 2016 at 01:41:47PM +0100, Robin Murphy wrote:
> On 19/09/16 13:24, Will Deacon wrote:
> > On Mon, Sep 19, 2016 at 02:13:45PM +0200, Auger Eric wrote:
> >> On 16/09/2016 18:18, Robin Murphy wrote:
> >>> What I probably will do, though, since we have the functionality in
> >>> place for the sake of the old binding, and I think there are other folks
> >>> who want PCI iommu-map support but would prefer not to bother with DMA
> >>> ops on the host, is add a command-line option to disable DMA domains
> >>> even for the generic bindings.
> >>
> >> Yes this would be a good thing I think. This series has an important
> >> impact on platforms which do not have smmu v3, where contexts are scarce
> >> HW resources.
> > 
> > Rather than disabling DMA domains entirely, we could specify a number
> > of contexts to reserve for other use (e.g. VFIO). It's a pity that these
> > options are global for the system, as opposed to per SMMU instance,
> > but I can't see a good way around that.
> 
> The problem with that approach is that due to bus traversal order you'd
> typically end up with the even-more-limited number of non-reserved
> contexts getting consumed by bridges that are unlikely to ever actually
> use their DMA ops, so you end up barely any better off than just not
> doing DMA ops at all, which we already have the code for.

For Seattle, perhaps, but that's quite a big generalisation. If we're
going to add an option, I'd much rather add something with some flexibility,
rather than end up supporting both "disable_dma_domains" and
"reserved_s2_contexts" in the long run.

> And yeah, if you had, say, "arm-smmu.reserved_s2_contexts=4", for the
> benefit of your PCI SMMU, what do the SMMUs in front of your other
> platform devices with only 2 contexts (and which only want DMA ops) do?

There's two things in this example:

  (1) If other SMMUs only have 2 contexts, then they reserve both and warn
  (2) If some SMMUs want DMA ops and others don't, then you have an issue
      with *any* sort of cmdline option to control this

Will

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

* Re: [PATCH v7 05/22] Docs: dt: document ARM SMMUv3 generic binding usage
  2016-09-12 16:13     ` Robin Murphy
@ 2016-09-20 14:46         ` Rob Herring
  -1 siblings, 0 replies; 104+ messages in thread
From: Rob Herring @ 2016-09-20 14:46 UTC (permalink / raw)
  To: Robin Murphy
  Cc: Mark Rutland, devicetree-u79uwXL29TY76Z2rM5mHXA,
	punit.agrawal-5wv7dgnIgG8, will.deacon-5wv7dgnIgG8,
	iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	thunder.leizhen-hv44wF8Li93QT0dZR+AlfA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

On Mon, Sep 12, 2016 at 05:13:43PM +0100, Robin Murphy wrote:
> We're about to ratify our use of the generic binding, so document it.
> 
> CC: Rob Herring <robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
> CC: Mark Rutland <mark.rutland-5wv7dgnIgG8@public.gmane.org>
> Signed-off-by: Robin Murphy <robin.murphy-5wv7dgnIgG8@public.gmane.org>
> 
> ---
> 
> - Reference PCI "iommu-map" binding instead, as that's our main concern
> - Fix "IDs" typo
> ---
>  Documentation/devicetree/bindings/iommu/arm,smmu-v3.txt | 8 +++++++-
>  1 file changed, 7 insertions(+), 1 deletion(-)

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

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

* [PATCH v7 05/22] Docs: dt: document ARM SMMUv3 generic binding usage
@ 2016-09-20 14:46         ` Rob Herring
  0 siblings, 0 replies; 104+ messages in thread
From: Rob Herring @ 2016-09-20 14:46 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Sep 12, 2016 at 05:13:43PM +0100, Robin Murphy wrote:
> We're about to ratify our use of the generic binding, so document it.
> 
> CC: Rob Herring <robh+dt@kernel.org>
> CC: Mark Rutland <mark.rutland@arm.com>
> Signed-off-by: Robin Murphy <robin.murphy@arm.com>
> 
> ---
> 
> - Reference PCI "iommu-map" binding instead, as that's our main concern
> - Fix "IDs" typo
> ---
>  Documentation/devicetree/bindings/iommu/arm,smmu-v3.txt | 8 +++++++-
>  1 file changed, 7 insertions(+), 1 deletion(-)

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

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

* RE: [PATCH v7 21/22] iommu/dma: Add support for mapping MSIs
  2016-09-12 16:13     ` Robin Murphy
@ 2016-10-05  7:00         ` Nipun Gupta
  -1 siblings, 0 replies; 104+ messages in thread
From: Nipun Gupta @ 2016-10-05  7:00 UTC (permalink / raw)
  To: Robin Murphy, will.deacon-5wv7dgnIgG8,
	joro-zLv9SwRftAIdnm+yROfE0A,
	iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, punit.agrawal-5wv7dgnIgG8,
	thunder.leizhen-hv44wF8Li93QT0dZR+AlfA



> -----Original Message-----
> From: iommu-bounces-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA@public.gmane.org [mailto:iommu-
> bounces-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA@public.gmane.org] On Behalf Of Robin Murphy
> Sent: Monday, September 12, 2016 21:44
> To: will.deacon-5wv7dgnIgG8@public.gmane.org; joro-zLv9SwRftAIdnm+yROfE0A@public.gmane.org; iommu-cunTk1MwBs/ROKNJybVBZg@public.gmane.org
> foundation.org; linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org
> Cc: devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org; punit.agrawal-5wv7dgnIgG8@public.gmane.org;
> thunder.leizhen-hv44wF8Li93QT0dZR+AlfA@public.gmane.org
> Subject: [PATCH v7 21/22] iommu/dma: Add support for mapping MSIs
> 
> When an MSI doorbell is located downstream of an IOMMU, attaching devices
> to a DMA ops domain and switching on translation leads to a rude shock when
> their attempt to write to the physical address returned by the irqchip driver
> faults (or worse, writes into some already-mapped
> buffer) and no interrupt is forthcoming.
> 
> Address this by adding a hook for relevant irqchip drivers to call from their
> compose_msi_msg() callback, to swizzle the physical address with an
> appropriatly-mapped IOVA for any device attached to one of our DMA ops
> domains.
> 
> Acked-by: Thomas Gleixner <tglx-hfZtesqFncYOwBW4kG4KsQ@public.gmane.org>
> Acked-by: Marc Zyngier <marc.zyngier-5wv7dgnIgG8@public.gmane.org>
> Signed-off-by: Robin Murphy <robin.murphy-5wv7dgnIgG8@public.gmane.org>
> ---
>  drivers/iommu/dma-iommu.c        | 136
> ++++++++++++++++++++++++++++++++++-----
>  drivers/irqchip/irq-gic-v2m.c    |   3 +
>  drivers/irqchip/irq-gic-v3-its.c |   3 +
>  include/linux/dma-iommu.h        |   9 +++
>  4 files changed, 136 insertions(+), 15 deletions(-)
> 
> diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c index
> 00c8a08d56e7..4329d18080cf 100644
> --- a/drivers/iommu/dma-iommu.c
> +++ b/drivers/iommu/dma-iommu.c
> @@ -25,10 +25,28 @@
>  #include <linux/huge_mm.h>
>  #include <linux/iommu.h>
>  #include <linux/iova.h>
> +#include <linux/irq.h>
>  #include <linux/mm.h>
>  #include <linux/scatterlist.h>
>  #include <linux/vmalloc.h>
> 
> +struct iommu_dma_msi_page {
> +	struct list_head	list;
> +	dma_addr_t		iova;
> +	phys_addr_t		phys;
> +};
> +
> +struct iommu_dma_cookie {
> +	struct iova_domain	iovad;
> +	struct list_head	msi_page_list;
> +	spinlock_t		msi_lock;
> +};
> +
> +static inline struct iova_domain *cookie_iovad(struct iommu_domain
> +*domain) {
> +	return &((struct iommu_dma_cookie *)domain->iova_cookie)->iovad; }
> +
>  int iommu_dma_init(void)
>  {
>  	return iova_cache_get();
> @@ -43,15 +61,19 @@ int iommu_dma_init(void)
>   */
>  int iommu_get_dma_cookie(struct iommu_domain *domain)  {
> -	struct iova_domain *iovad;
> +	struct iommu_dma_cookie *cookie;
> 
>  	if (domain->iova_cookie)
>  		return -EEXIST;
> 
> -	iovad = kzalloc(sizeof(*iovad), GFP_KERNEL);
> -	domain->iova_cookie = iovad;
> +	cookie = kzalloc(sizeof(*cookie), GFP_KERNEL);
> +	if (!cookie)
> +		return -ENOMEM;
> 
> -	return iovad ? 0 : -ENOMEM;
> +	spin_lock_init(&cookie->msi_lock);
> +	INIT_LIST_HEAD(&cookie->msi_page_list);
> +	domain->iova_cookie = cookie;
> +	return 0;
>  }
>  EXPORT_SYMBOL(iommu_get_dma_cookie);
> 
> @@ -63,14 +85,20 @@ EXPORT_SYMBOL(iommu_get_dma_cookie);
>   */
>  void iommu_put_dma_cookie(struct iommu_domain *domain)  {
> -	struct iova_domain *iovad = domain->iova_cookie;
> +	struct iommu_dma_cookie *cookie = domain->iova_cookie;
> +	struct iommu_dma_msi_page *msi, *tmp;
> 
> -	if (!iovad)
> +	if (!cookie)
>  		return;
> 
> -	if (iovad->granule)
> -		put_iova_domain(iovad);
> -	kfree(iovad);
> +	if (cookie->iovad.granule)
> +		put_iova_domain(&cookie->iovad);
> +
> +	list_for_each_entry_safe(msi, tmp, &cookie->msi_page_list, list) {
> +		list_del(&msi->list);
> +		kfree(msi);
> +	}
> +	kfree(cookie);
>  	domain->iova_cookie = NULL;
>  }
>  EXPORT_SYMBOL(iommu_put_dma_cookie);
> @@ -88,7 +116,7 @@ EXPORT_SYMBOL(iommu_put_dma_cookie);
>   */
>  int iommu_dma_init_domain(struct iommu_domain *domain, dma_addr_t
> base, u64 size)  {
> -	struct iova_domain *iovad = domain->iova_cookie;
> +	struct iova_domain *iovad = cookie_iovad(domain);
>  	unsigned long order, base_pfn, end_pfn;
> 
>  	if (!iovad)
> @@ -155,7 +183,7 @@ int dma_direction_to_prot(enum dma_data_direction
> dir, bool coherent)  static struct iova *__alloc_iova(struct iommu_domain
> *domain, size_t size,
>  		dma_addr_t dma_limit)
>  {
> -	struct iova_domain *iovad = domain->iova_cookie;
> +	struct iova_domain *iovad = cookie_iovad(domain);
>  	unsigned long shift = iova_shift(iovad);
>  	unsigned long length = iova_align(iovad, size) >> shift;
> 
> @@ -171,7 +199,7 @@ static struct iova *__alloc_iova(struct iommu_domain
> *domain, size_t size,
>  /* The IOVA allocator knows what we mapped, so just unmap whatever that
> was */  static void __iommu_dma_unmap(struct iommu_domain *domain,
> dma_addr_t dma_addr)  {
> -	struct iova_domain *iovad = domain->iova_cookie;
> +	struct iova_domain *iovad = cookie_iovad(domain);
>  	unsigned long shift = iova_shift(iovad);
>  	unsigned long pfn = dma_addr >> shift;
>  	struct iova *iova = find_iova(iovad, pfn); @@ -294,7 +322,7 @@ struct
> page **iommu_dma_alloc(struct device *dev, size_t size, gfp_t gfp,
>  		void (*flush_page)(struct device *, const void *, phys_addr_t))  {
>  	struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
> -	struct iova_domain *iovad = domain->iova_cookie;
> +	struct iova_domain *iovad = cookie_iovad(domain);
>  	struct iova *iova;
>  	struct page **pages;
>  	struct sg_table sgt;
> @@ -386,7 +414,7 @@ dma_addr_t iommu_dma_map_page(struct device
> *dev, struct page *page,  {
>  	dma_addr_t dma_addr;
>  	struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
> -	struct iova_domain *iovad = domain->iova_cookie;
> +	struct iova_domain *iovad = cookie_iovad(domain);
>  	phys_addr_t phys = page_to_phys(page) + offset;
>  	size_t iova_off = iova_offset(iovad, phys);
>  	size_t len = iova_align(iovad, size + iova_off); @@ -495,7 +523,7 @@
> int iommu_dma_map_sg(struct device *dev, struct scatterlist *sg,
>  		int nents, int prot)
>  {
>  	struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
> -	struct iova_domain *iovad = domain->iova_cookie;
> +	struct iova_domain *iovad = cookie_iovad(domain);
>  	struct iova *iova;
>  	struct scatterlist *s, *prev = NULL;
>  	dma_addr_t dma_addr;
> @@ -587,3 +615,81 @@ int iommu_dma_mapping_error(struct device *dev,
> dma_addr_t dma_addr)  {
>  	return dma_addr == DMA_ERROR_CODE;
>  }
> +
> +static struct iommu_dma_msi_page *iommu_dma_get_msi_page(struct device
> *dev,
> +		phys_addr_t msi_addr, struct iommu_domain *domain) {
> +	struct iommu_dma_cookie *cookie = domain->iova_cookie;
> +	struct iommu_dma_msi_page *msi_page;
> +	struct iova_domain *iovad = &cookie->iovad;
> +	struct iova *iova;
> +	int prot = IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_MMIO;
> +
> +	msi_addr &= ~(phys_addr_t)iova_mask(iovad);
> +	list_for_each_entry(msi_page, &cookie->msi_page_list, list)
> +		if (msi_page->phys == msi_addr)
> +			return msi_page;
> +
> +	msi_page = kzalloc(sizeof(*msi_page), GFP_ATOMIC);
> +	if (!msi_page)
> +		return NULL;
> +
> +	iova = __alloc_iova(domain, iovad->granule, dma_get_mask(dev));

I think this should be 'iova = __alloc_iova(domain, iovad->granule, dma_get_mask(dev));'
as __alloc_iova takes input parameter as 'struct iova_domain *'

Regards,
Nipun

> +	if (!iova)
> +		goto out_free_page;
> +
> +	msi_page->phys = msi_addr;
> +	msi_page->iova = iova_dma_addr(iovad, iova);
> +	if (iommu_map(domain, msi_page->iova, msi_addr, iovad->granule,
> prot))
> +		goto out_free_iova;
> +
> +	INIT_LIST_HEAD(&msi_page->list);
> +	list_add(&msi_page->list, &cookie->msi_page_list);
> +	return msi_page;
> +
> +out_free_iova:
> +	__free_iova(iovad, iova);
> +out_free_page:
> +	kfree(msi_page);
> +	return NULL;
> +}
> +
> +void iommu_dma_map_msi_msg(int irq, struct msi_msg *msg) {
> +	struct device *dev = msi_desc_to_dev(irq_get_msi_desc(irq));
> +	struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
> +	struct iommu_dma_cookie *cookie;
> +	struct iommu_dma_msi_page *msi_page;
> +	phys_addr_t msi_addr = (u64)msg->address_hi << 32 | msg->address_lo;
> +	unsigned long flags;
> +
> +	if (!domain || !domain->iova_cookie)
> +		return;
> +
> +	cookie = domain->iova_cookie;
> +
> +	/*
> +	 * We disable IRQs to rule out a possible inversion against
> +	 * irq_desc_lock if, say, someone tries to retarget the affinity
> +	 * of an MSI from within an IPI handler.
> +	 */
> +	spin_lock_irqsave(&cookie->msi_lock, flags);
> +	msi_page = iommu_dma_get_msi_page(dev, msi_addr, domain);
> +	spin_unlock_irqrestore(&cookie->msi_lock, flags);
> +
> +	if (WARN_ON(!msi_page)) {
> +		/*
> +		 * We're called from a void callback, so the best we can do is
> +		 * 'fail' by filling the message with obviously bogus values.
> +		 * Since we got this far due to an IOMMU being present, it's
> +		 * not like the existing address would have worked anyway...
> +		 */
> +		msg->address_hi = ~0U;
> +		msg->address_lo = ~0U;
> +		msg->data = ~0U;
> +	} else {
> +		msg->address_hi = upper_32_bits(msi_page->iova);
> +		msg->address_lo &= iova_mask(&cookie->iovad);
> +		msg->address_lo += lower_32_bits(msi_page->iova);
> +	}
> +}
> diff --git a/drivers/irqchip/irq-gic-v2m.c b/drivers/irqchip/irq-gic-v2m.c index
> 35eb7ac5d21f..863e073c6f7f 100644
> --- a/drivers/irqchip/irq-gic-v2m.c
> +++ b/drivers/irqchip/irq-gic-v2m.c
> @@ -16,6 +16,7 @@
>  #define pr_fmt(fmt) "GICv2m: " fmt
> 
>  #include <linux/acpi.h>
> +#include <linux/dma-iommu.h>
>  #include <linux/irq.h>
>  #include <linux/irqdomain.h>
>  #include <linux/kernel.h>
> @@ -108,6 +109,8 @@ static void gicv2m_compose_msi_msg(struct irq_data
> *data, struct msi_msg *msg)
> 
>  	if (v2m->flags & GICV2M_NEEDS_SPI_OFFSET)
>  		msg->data -= v2m->spi_offset;
> +
> +	iommu_dma_map_msi_msg(data->irq, msg);
>  }
> 
>  static struct irq_chip gicv2m_irq_chip = { diff --git a/drivers/irqchip/irq-gic-v3-
> its.c b/drivers/irqchip/irq-gic-v3-its.c
> index 36b9c28a5c91..98ff669d5962 100644
> --- a/drivers/irqchip/irq-gic-v3-its.c
> +++ b/drivers/irqchip/irq-gic-v3-its.c
> @@ -18,6 +18,7 @@
>  #include <linux/bitmap.h>
>  #include <linux/cpu.h>
>  #include <linux/delay.h>
> +#include <linux/dma-iommu.h>
>  #include <linux/interrupt.h>
>  #include <linux/log2.h>
>  #include <linux/mm.h>
> @@ -655,6 +656,8 @@ static void its_irq_compose_msi_msg(struct irq_data
> *d, struct msi_msg *msg)
>  	msg->address_lo		= addr & ((1UL << 32) - 1);
>  	msg->address_hi		= addr >> 32;
>  	msg->data		= its_get_event_id(d);
> +
> +	iommu_dma_map_msi_msg(d->irq, msg);
>  }
> 
>  static struct irq_chip its_irq_chip = { diff --git a/include/linux/dma-iommu.h
> b/include/linux/dma-iommu.h index 81c5c8d167ad..5ee806e41b5c 100644
> --- a/include/linux/dma-iommu.h
> +++ b/include/linux/dma-iommu.h
> @@ -21,6 +21,7 @@
> 
>  #ifdef CONFIG_IOMMU_DMA
>  #include <linux/iommu.h>
> +#include <linux/msi.h>
> 
>  int iommu_dma_init(void);
> 
> @@ -62,9 +63,13 @@ void iommu_dma_unmap_sg(struct device *dev, struct
> scatterlist *sg, int nents,  int iommu_dma_supported(struct device *dev, u64
> mask);  int iommu_dma_mapping_error(struct device *dev, dma_addr_t
> dma_addr);
> 
> +/* The DMA API isn't _quite_ the whole story, though... */ void
> +iommu_dma_map_msi_msg(int irq, struct msi_msg *msg);
> +
>  #else
> 
>  struct iommu_domain;
> +struct msi_msg;
> 
>  static inline int iommu_dma_init(void)
>  {
> @@ -80,6 +85,10 @@ static inline void iommu_put_dma_cookie(struct
> iommu_domain *domain)  {  }
> 
> +static inline void iommu_dma_map_msi_msg(int irq, struct msi_msg *msg)
> +{ }
> +
>  #endif	/* CONFIG_IOMMU_DMA */
>  #endif	/* __KERNEL__ */
>  #endif	/* __DMA_IOMMU_H */
> --
> 2.8.1.dirty
> 
> _______________________________________________
> iommu mailing list
> iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA@public.gmane.org
> https://lists.linuxfoundation.org/mailman/listinfo/iommu

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

* [PATCH v7 21/22] iommu/dma: Add support for mapping MSIs
@ 2016-10-05  7:00         ` Nipun Gupta
  0 siblings, 0 replies; 104+ messages in thread
From: Nipun Gupta @ 2016-10-05  7:00 UTC (permalink / raw)
  To: linux-arm-kernel



> -----Original Message-----
> From: iommu-bounces at lists.linux-foundation.org [mailto:iommu-
> bounces at lists.linux-foundation.org] On Behalf Of Robin Murphy
> Sent: Monday, September 12, 2016 21:44
> To: will.deacon at arm.com; joro at 8bytes.org; iommu at lists.linux-
> foundation.org; linux-arm-kernel at lists.infradead.org
> Cc: devicetree at vger.kernel.org; punit.agrawal at arm.com;
> thunder.leizhen at huawei.com
> Subject: [PATCH v7 21/22] iommu/dma: Add support for mapping MSIs
> 
> When an MSI doorbell is located downstream of an IOMMU, attaching devices
> to a DMA ops domain and switching on translation leads to a rude shock when
> their attempt to write to the physical address returned by the irqchip driver
> faults (or worse, writes into some already-mapped
> buffer) and no interrupt is forthcoming.
> 
> Address this by adding a hook for relevant irqchip drivers to call from their
> compose_msi_msg() callback, to swizzle the physical address with an
> appropriatly-mapped IOVA for any device attached to one of our DMA ops
> domains.
> 
> Acked-by: Thomas Gleixner <tglx@linutronix.de>
> Acked-by: Marc Zyngier <marc.zyngier@arm.com>
> Signed-off-by: Robin Murphy <robin.murphy@arm.com>
> ---
>  drivers/iommu/dma-iommu.c        | 136
> ++++++++++++++++++++++++++++++++++-----
>  drivers/irqchip/irq-gic-v2m.c    |   3 +
>  drivers/irqchip/irq-gic-v3-its.c |   3 +
>  include/linux/dma-iommu.h        |   9 +++
>  4 files changed, 136 insertions(+), 15 deletions(-)
> 
> diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c index
> 00c8a08d56e7..4329d18080cf 100644
> --- a/drivers/iommu/dma-iommu.c
> +++ b/drivers/iommu/dma-iommu.c
> @@ -25,10 +25,28 @@
>  #include <linux/huge_mm.h>
>  #include <linux/iommu.h>
>  #include <linux/iova.h>
> +#include <linux/irq.h>
>  #include <linux/mm.h>
>  #include <linux/scatterlist.h>
>  #include <linux/vmalloc.h>
> 
> +struct iommu_dma_msi_page {
> +	struct list_head	list;
> +	dma_addr_t		iova;
> +	phys_addr_t		phys;
> +};
> +
> +struct iommu_dma_cookie {
> +	struct iova_domain	iovad;
> +	struct list_head	msi_page_list;
> +	spinlock_t		msi_lock;
> +};
> +
> +static inline struct iova_domain *cookie_iovad(struct iommu_domain
> +*domain) {
> +	return &((struct iommu_dma_cookie *)domain->iova_cookie)->iovad; }
> +
>  int iommu_dma_init(void)
>  {
>  	return iova_cache_get();
> @@ -43,15 +61,19 @@ int iommu_dma_init(void)
>   */
>  int iommu_get_dma_cookie(struct iommu_domain *domain)  {
> -	struct iova_domain *iovad;
> +	struct iommu_dma_cookie *cookie;
> 
>  	if (domain->iova_cookie)
>  		return -EEXIST;
> 
> -	iovad = kzalloc(sizeof(*iovad), GFP_KERNEL);
> -	domain->iova_cookie = iovad;
> +	cookie = kzalloc(sizeof(*cookie), GFP_KERNEL);
> +	if (!cookie)
> +		return -ENOMEM;
> 
> -	return iovad ? 0 : -ENOMEM;
> +	spin_lock_init(&cookie->msi_lock);
> +	INIT_LIST_HEAD(&cookie->msi_page_list);
> +	domain->iova_cookie = cookie;
> +	return 0;
>  }
>  EXPORT_SYMBOL(iommu_get_dma_cookie);
> 
> @@ -63,14 +85,20 @@ EXPORT_SYMBOL(iommu_get_dma_cookie);
>   */
>  void iommu_put_dma_cookie(struct iommu_domain *domain)  {
> -	struct iova_domain *iovad = domain->iova_cookie;
> +	struct iommu_dma_cookie *cookie = domain->iova_cookie;
> +	struct iommu_dma_msi_page *msi, *tmp;
> 
> -	if (!iovad)
> +	if (!cookie)
>  		return;
> 
> -	if (iovad->granule)
> -		put_iova_domain(iovad);
> -	kfree(iovad);
> +	if (cookie->iovad.granule)
> +		put_iova_domain(&cookie->iovad);
> +
> +	list_for_each_entry_safe(msi, tmp, &cookie->msi_page_list, list) {
> +		list_del(&msi->list);
> +		kfree(msi);
> +	}
> +	kfree(cookie);
>  	domain->iova_cookie = NULL;
>  }
>  EXPORT_SYMBOL(iommu_put_dma_cookie);
> @@ -88,7 +116,7 @@ EXPORT_SYMBOL(iommu_put_dma_cookie);
>   */
>  int iommu_dma_init_domain(struct iommu_domain *domain, dma_addr_t
> base, u64 size)  {
> -	struct iova_domain *iovad = domain->iova_cookie;
> +	struct iova_domain *iovad = cookie_iovad(domain);
>  	unsigned long order, base_pfn, end_pfn;
> 
>  	if (!iovad)
> @@ -155,7 +183,7 @@ int dma_direction_to_prot(enum dma_data_direction
> dir, bool coherent)  static struct iova *__alloc_iova(struct iommu_domain
> *domain, size_t size,
>  		dma_addr_t dma_limit)
>  {
> -	struct iova_domain *iovad = domain->iova_cookie;
> +	struct iova_domain *iovad = cookie_iovad(domain);
>  	unsigned long shift = iova_shift(iovad);
>  	unsigned long length = iova_align(iovad, size) >> shift;
> 
> @@ -171,7 +199,7 @@ static struct iova *__alloc_iova(struct iommu_domain
> *domain, size_t size,
>  /* The IOVA allocator knows what we mapped, so just unmap whatever that
> was */  static void __iommu_dma_unmap(struct iommu_domain *domain,
> dma_addr_t dma_addr)  {
> -	struct iova_domain *iovad = domain->iova_cookie;
> +	struct iova_domain *iovad = cookie_iovad(domain);
>  	unsigned long shift = iova_shift(iovad);
>  	unsigned long pfn = dma_addr >> shift;
>  	struct iova *iova = find_iova(iovad, pfn); @@ -294,7 +322,7 @@ struct
> page **iommu_dma_alloc(struct device *dev, size_t size, gfp_t gfp,
>  		void (*flush_page)(struct device *, const void *, phys_addr_t))  {
>  	struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
> -	struct iova_domain *iovad = domain->iova_cookie;
> +	struct iova_domain *iovad = cookie_iovad(domain);
>  	struct iova *iova;
>  	struct page **pages;
>  	struct sg_table sgt;
> @@ -386,7 +414,7 @@ dma_addr_t iommu_dma_map_page(struct device
> *dev, struct page *page,  {
>  	dma_addr_t dma_addr;
>  	struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
> -	struct iova_domain *iovad = domain->iova_cookie;
> +	struct iova_domain *iovad = cookie_iovad(domain);
>  	phys_addr_t phys = page_to_phys(page) + offset;
>  	size_t iova_off = iova_offset(iovad, phys);
>  	size_t len = iova_align(iovad, size + iova_off); @@ -495,7 +523,7 @@
> int iommu_dma_map_sg(struct device *dev, struct scatterlist *sg,
>  		int nents, int prot)
>  {
>  	struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
> -	struct iova_domain *iovad = domain->iova_cookie;
> +	struct iova_domain *iovad = cookie_iovad(domain);
>  	struct iova *iova;
>  	struct scatterlist *s, *prev = NULL;
>  	dma_addr_t dma_addr;
> @@ -587,3 +615,81 @@ int iommu_dma_mapping_error(struct device *dev,
> dma_addr_t dma_addr)  {
>  	return dma_addr == DMA_ERROR_CODE;
>  }
> +
> +static struct iommu_dma_msi_page *iommu_dma_get_msi_page(struct device
> *dev,
> +		phys_addr_t msi_addr, struct iommu_domain *domain) {
> +	struct iommu_dma_cookie *cookie = domain->iova_cookie;
> +	struct iommu_dma_msi_page *msi_page;
> +	struct iova_domain *iovad = &cookie->iovad;
> +	struct iova *iova;
> +	int prot = IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_MMIO;
> +
> +	msi_addr &= ~(phys_addr_t)iova_mask(iovad);
> +	list_for_each_entry(msi_page, &cookie->msi_page_list, list)
> +		if (msi_page->phys == msi_addr)
> +			return msi_page;
> +
> +	msi_page = kzalloc(sizeof(*msi_page), GFP_ATOMIC);
> +	if (!msi_page)
> +		return NULL;
> +
> +	iova = __alloc_iova(domain, iovad->granule, dma_get_mask(dev));

I think this should be 'iova = __alloc_iova(domain, iovad->granule, dma_get_mask(dev));'
as __alloc_iova takes input parameter as 'struct iova_domain *'

Regards,
Nipun

> +	if (!iova)
> +		goto out_free_page;
> +
> +	msi_page->phys = msi_addr;
> +	msi_page->iova = iova_dma_addr(iovad, iova);
> +	if (iommu_map(domain, msi_page->iova, msi_addr, iovad->granule,
> prot))
> +		goto out_free_iova;
> +
> +	INIT_LIST_HEAD(&msi_page->list);
> +	list_add(&msi_page->list, &cookie->msi_page_list);
> +	return msi_page;
> +
> +out_free_iova:
> +	__free_iova(iovad, iova);
> +out_free_page:
> +	kfree(msi_page);
> +	return NULL;
> +}
> +
> +void iommu_dma_map_msi_msg(int irq, struct msi_msg *msg) {
> +	struct device *dev = msi_desc_to_dev(irq_get_msi_desc(irq));
> +	struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
> +	struct iommu_dma_cookie *cookie;
> +	struct iommu_dma_msi_page *msi_page;
> +	phys_addr_t msi_addr = (u64)msg->address_hi << 32 | msg->address_lo;
> +	unsigned long flags;
> +
> +	if (!domain || !domain->iova_cookie)
> +		return;
> +
> +	cookie = domain->iova_cookie;
> +
> +	/*
> +	 * We disable IRQs to rule out a possible inversion against
> +	 * irq_desc_lock if, say, someone tries to retarget the affinity
> +	 * of an MSI from within an IPI handler.
> +	 */
> +	spin_lock_irqsave(&cookie->msi_lock, flags);
> +	msi_page = iommu_dma_get_msi_page(dev, msi_addr, domain);
> +	spin_unlock_irqrestore(&cookie->msi_lock, flags);
> +
> +	if (WARN_ON(!msi_page)) {
> +		/*
> +		 * We're called from a void callback, so the best we can do is
> +		 * 'fail' by filling the message with obviously bogus values.
> +		 * Since we got this far due to an IOMMU being present, it's
> +		 * not like the existing address would have worked anyway...
> +		 */
> +		msg->address_hi = ~0U;
> +		msg->address_lo = ~0U;
> +		msg->data = ~0U;
> +	} else {
> +		msg->address_hi = upper_32_bits(msi_page->iova);
> +		msg->address_lo &= iova_mask(&cookie->iovad);
> +		msg->address_lo += lower_32_bits(msi_page->iova);
> +	}
> +}
> diff --git a/drivers/irqchip/irq-gic-v2m.c b/drivers/irqchip/irq-gic-v2m.c index
> 35eb7ac5d21f..863e073c6f7f 100644
> --- a/drivers/irqchip/irq-gic-v2m.c
> +++ b/drivers/irqchip/irq-gic-v2m.c
> @@ -16,6 +16,7 @@
>  #define pr_fmt(fmt) "GICv2m: " fmt
> 
>  #include <linux/acpi.h>
> +#include <linux/dma-iommu.h>
>  #include <linux/irq.h>
>  #include <linux/irqdomain.h>
>  #include <linux/kernel.h>
> @@ -108,6 +109,8 @@ static void gicv2m_compose_msi_msg(struct irq_data
> *data, struct msi_msg *msg)
> 
>  	if (v2m->flags & GICV2M_NEEDS_SPI_OFFSET)
>  		msg->data -= v2m->spi_offset;
> +
> +	iommu_dma_map_msi_msg(data->irq, msg);
>  }
> 
>  static struct irq_chip gicv2m_irq_chip = { diff --git a/drivers/irqchip/irq-gic-v3-
> its.c b/drivers/irqchip/irq-gic-v3-its.c
> index 36b9c28a5c91..98ff669d5962 100644
> --- a/drivers/irqchip/irq-gic-v3-its.c
> +++ b/drivers/irqchip/irq-gic-v3-its.c
> @@ -18,6 +18,7 @@
>  #include <linux/bitmap.h>
>  #include <linux/cpu.h>
>  #include <linux/delay.h>
> +#include <linux/dma-iommu.h>
>  #include <linux/interrupt.h>
>  #include <linux/log2.h>
>  #include <linux/mm.h>
> @@ -655,6 +656,8 @@ static void its_irq_compose_msi_msg(struct irq_data
> *d, struct msi_msg *msg)
>  	msg->address_lo		= addr & ((1UL << 32) - 1);
>  	msg->address_hi		= addr >> 32;
>  	msg->data		= its_get_event_id(d);
> +
> +	iommu_dma_map_msi_msg(d->irq, msg);
>  }
> 
>  static struct irq_chip its_irq_chip = { diff --git a/include/linux/dma-iommu.h
> b/include/linux/dma-iommu.h index 81c5c8d167ad..5ee806e41b5c 100644
> --- a/include/linux/dma-iommu.h
> +++ b/include/linux/dma-iommu.h
> @@ -21,6 +21,7 @@
> 
>  #ifdef CONFIG_IOMMU_DMA
>  #include <linux/iommu.h>
> +#include <linux/msi.h>
> 
>  int iommu_dma_init(void);
> 
> @@ -62,9 +63,13 @@ void iommu_dma_unmap_sg(struct device *dev, struct
> scatterlist *sg, int nents,  int iommu_dma_supported(struct device *dev, u64
> mask);  int iommu_dma_mapping_error(struct device *dev, dma_addr_t
> dma_addr);
> 
> +/* The DMA API isn't _quite_ the whole story, though... */ void
> +iommu_dma_map_msi_msg(int irq, struct msi_msg *msg);
> +
>  #else
> 
>  struct iommu_domain;
> +struct msi_msg;
> 
>  static inline int iommu_dma_init(void)
>  {
> @@ -80,6 +85,10 @@ static inline void iommu_put_dma_cookie(struct
> iommu_domain *domain)  {  }
> 
> +static inline void iommu_dma_map_msi_msg(int irq, struct msi_msg *msg)
> +{ }
> +
>  #endif	/* CONFIG_IOMMU_DMA */
>  #endif	/* __KERNEL__ */
>  #endif	/* __DMA_IOMMU_H */
> --
> 2.8.1.dirty
> 
> _______________________________________________
> iommu mailing list
> iommu at lists.linux-foundation.org
> https://lists.linuxfoundation.org/mailman/listinfo/iommu

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

* Re: [PATCH v7 21/22] iommu/dma: Add support for mapping MSIs
  2016-10-05  7:00         ` Nipun Gupta
@ 2016-10-05  9:55             ` Robin Murphy
  -1 siblings, 0 replies; 104+ messages in thread
From: Robin Murphy @ 2016-10-05  9:55 UTC (permalink / raw)
  To: Nipun Gupta
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, punit.agrawal-5wv7dgnIgG8,
	will.deacon-5wv7dgnIgG8,
	iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	thunder.leizhen-hv44wF8Li93QT0dZR+AlfA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

On 05/10/16 08:00, Nipun Gupta wrote:
> 
> 
>> -----Original Message-----
>> From: iommu-bounces-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA@public.gmane.org [mailto:iommu-
>> bounces-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA@public.gmane.org] On Behalf Of Robin Murphy
>> Sent: Monday, September 12, 2016 21:44
>> To: will.deacon-5wv7dgnIgG8@public.gmane.org; joro-zLv9SwRftAIdnm+yROfE0A@public.gmane.org; iommu-cunTk1MwBs/ROKNJybVBZg@public.gmane.org
>> foundation.org; linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org
>> Cc: devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org; punit.agrawal-5wv7dgnIgG8@public.gmane.org;
>> thunder.leizhen-hv44wF8Li93QT0dZR+AlfA@public.gmane.org
>> Subject: [PATCH v7 21/22] iommu/dma: Add support for mapping MSIs
>>
>> When an MSI doorbell is located downstream of an IOMMU, attaching devices
>> to a DMA ops domain and switching on translation leads to a rude shock when
>> their attempt to write to the physical address returned by the irqchip driver
>> faults (or worse, writes into some already-mapped
>> buffer) and no interrupt is forthcoming.
>>
>> Address this by adding a hook for relevant irqchip drivers to call from their
>> compose_msi_msg() callback, to swizzle the physical address with an
>> appropriatly-mapped IOVA for any device attached to one of our DMA ops
>> domains.
>>
>> Acked-by: Thomas Gleixner <tglx-hfZtesqFncYOwBW4kG4KsQ@public.gmane.org>
>> Acked-by: Marc Zyngier <marc.zyngier-5wv7dgnIgG8@public.gmane.org>
>> Signed-off-by: Robin Murphy <robin.murphy-5wv7dgnIgG8@public.gmane.org>
>> ---
>>  drivers/iommu/dma-iommu.c        | 136
>> ++++++++++++++++++++++++++++++++++-----
>>  drivers/irqchip/irq-gic-v2m.c    |   3 +
>>  drivers/irqchip/irq-gic-v3-its.c |   3 +
>>  include/linux/dma-iommu.h        |   9 +++
>>  4 files changed, 136 insertions(+), 15 deletions(-)
>>
>> diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c index
>> 00c8a08d56e7..4329d18080cf 100644
>> --- a/drivers/iommu/dma-iommu.c
>> +++ b/drivers/iommu/dma-iommu.c
>> @@ -25,10 +25,28 @@
>>  #include <linux/huge_mm.h>
>>  #include <linux/iommu.h>
>>  #include <linux/iova.h>
>> +#include <linux/irq.h>
>>  #include <linux/mm.h>
>>  #include <linux/scatterlist.h>
>>  #include <linux/vmalloc.h>
>>
>> +struct iommu_dma_msi_page {
>> +	struct list_head	list;
>> +	dma_addr_t		iova;
>> +	phys_addr_t		phys;
>> +};
>> +
>> +struct iommu_dma_cookie {
>> +	struct iova_domain	iovad;
>> +	struct list_head	msi_page_list;
>> +	spinlock_t		msi_lock;
>> +};
>> +
>> +static inline struct iova_domain *cookie_iovad(struct iommu_domain
>> +*domain) {
>> +	return &((struct iommu_dma_cookie *)domain->iova_cookie)->iovad; }
>> +
>>  int iommu_dma_init(void)
>>  {
>>  	return iova_cache_get();
>> @@ -43,15 +61,19 @@ int iommu_dma_init(void)
>>   */
>>  int iommu_get_dma_cookie(struct iommu_domain *domain)  {
>> -	struct iova_domain *iovad;
>> +	struct iommu_dma_cookie *cookie;
>>
>>  	if (domain->iova_cookie)
>>  		return -EEXIST;
>>
>> -	iovad = kzalloc(sizeof(*iovad), GFP_KERNEL);
>> -	domain->iova_cookie = iovad;
>> +	cookie = kzalloc(sizeof(*cookie), GFP_KERNEL);
>> +	if (!cookie)
>> +		return -ENOMEM;
>>
>> -	return iovad ? 0 : -ENOMEM;
>> +	spin_lock_init(&cookie->msi_lock);
>> +	INIT_LIST_HEAD(&cookie->msi_page_list);
>> +	domain->iova_cookie = cookie;
>> +	return 0;
>>  }
>>  EXPORT_SYMBOL(iommu_get_dma_cookie);
>>
>> @@ -63,14 +85,20 @@ EXPORT_SYMBOL(iommu_get_dma_cookie);
>>   */
>>  void iommu_put_dma_cookie(struct iommu_domain *domain)  {
>> -	struct iova_domain *iovad = domain->iova_cookie;
>> +	struct iommu_dma_cookie *cookie = domain->iova_cookie;
>> +	struct iommu_dma_msi_page *msi, *tmp;
>>
>> -	if (!iovad)
>> +	if (!cookie)
>>  		return;
>>
>> -	if (iovad->granule)
>> -		put_iova_domain(iovad);
>> -	kfree(iovad);
>> +	if (cookie->iovad.granule)
>> +		put_iova_domain(&cookie->iovad);
>> +
>> +	list_for_each_entry_safe(msi, tmp, &cookie->msi_page_list, list) {
>> +		list_del(&msi->list);
>> +		kfree(msi);
>> +	}
>> +	kfree(cookie);
>>  	domain->iova_cookie = NULL;
>>  }
>>  EXPORT_SYMBOL(iommu_put_dma_cookie);
>> @@ -88,7 +116,7 @@ EXPORT_SYMBOL(iommu_put_dma_cookie);
>>   */
>>  int iommu_dma_init_domain(struct iommu_domain *domain, dma_addr_t
>> base, u64 size)  {
>> -	struct iova_domain *iovad = domain->iova_cookie;
>> +	struct iova_domain *iovad = cookie_iovad(domain);
>>  	unsigned long order, base_pfn, end_pfn;
>>
>>  	if (!iovad)
>> @@ -155,7 +183,7 @@ int dma_direction_to_prot(enum dma_data_direction
>> dir, bool coherent)  static struct iova *__alloc_iova(struct iommu_domain
>> *domain, size_t size,
>>  		dma_addr_t dma_limit)
>>  {
>> -	struct iova_domain *iovad = domain->iova_cookie;
>> +	struct iova_domain *iovad = cookie_iovad(domain);
>>  	unsigned long shift = iova_shift(iovad);
>>  	unsigned long length = iova_align(iovad, size) >> shift;
>>
>> @@ -171,7 +199,7 @@ static struct iova *__alloc_iova(struct iommu_domain
>> *domain, size_t size,
>>  /* The IOVA allocator knows what we mapped, so just unmap whatever that
>> was */  static void __iommu_dma_unmap(struct iommu_domain *domain,
>> dma_addr_t dma_addr)  {
>> -	struct iova_domain *iovad = domain->iova_cookie;
>> +	struct iova_domain *iovad = cookie_iovad(domain);
>>  	unsigned long shift = iova_shift(iovad);
>>  	unsigned long pfn = dma_addr >> shift;
>>  	struct iova *iova = find_iova(iovad, pfn); @@ -294,7 +322,7 @@ struct
>> page **iommu_dma_alloc(struct device *dev, size_t size, gfp_t gfp,
>>  		void (*flush_page)(struct device *, const void *, phys_addr_t))  {
>>  	struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
>> -	struct iova_domain *iovad = domain->iova_cookie;
>> +	struct iova_domain *iovad = cookie_iovad(domain);
>>  	struct iova *iova;
>>  	struct page **pages;
>>  	struct sg_table sgt;
>> @@ -386,7 +414,7 @@ dma_addr_t iommu_dma_map_page(struct device
>> *dev, struct page *page,  {
>>  	dma_addr_t dma_addr;
>>  	struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
>> -	struct iova_domain *iovad = domain->iova_cookie;
>> +	struct iova_domain *iovad = cookie_iovad(domain);
>>  	phys_addr_t phys = page_to_phys(page) + offset;
>>  	size_t iova_off = iova_offset(iovad, phys);
>>  	size_t len = iova_align(iovad, size + iova_off); @@ -495,7 +523,7 @@
>> int iommu_dma_map_sg(struct device *dev, struct scatterlist *sg,
>>  		int nents, int prot)
>>  {
>>  	struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
>> -	struct iova_domain *iovad = domain->iova_cookie;
>> +	struct iova_domain *iovad = cookie_iovad(domain);
>>  	struct iova *iova;
>>  	struct scatterlist *s, *prev = NULL;
>>  	dma_addr_t dma_addr;
>> @@ -587,3 +615,81 @@ int iommu_dma_mapping_error(struct device *dev,
>> dma_addr_t dma_addr)  {
>>  	return dma_addr == DMA_ERROR_CODE;
>>  }
>> +
>> +static struct iommu_dma_msi_page *iommu_dma_get_msi_page(struct device
>> *dev,
>> +		phys_addr_t msi_addr, struct iommu_domain *domain) {
>> +	struct iommu_dma_cookie *cookie = domain->iova_cookie;
>> +	struct iommu_dma_msi_page *msi_page;
>> +	struct iova_domain *iovad = &cookie->iovad;
>> +	struct iova *iova;
>> +	int prot = IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_MMIO;
>> +
>> +	msi_addr &= ~(phys_addr_t)iova_mask(iovad);
>> +	list_for_each_entry(msi_page, &cookie->msi_page_list, list)
>> +		if (msi_page->phys == msi_addr)
>> +			return msi_page;
>> +
>> +	msi_page = kzalloc(sizeof(*msi_page), GFP_ATOMIC);
>> +	if (!msi_page)
>> +		return NULL;
>> +
>> +	iova = __alloc_iova(domain, iovad->granule, dma_get_mask(dev));
> 
> I think this should be 'iova = __alloc_iova(domain, iovad->granule, dma_get_mask(dev));'

Er, yes... I fully agree. That's why it is exactly that.

> as __alloc_iova takes input parameter as 'struct iova_domain *'

Joking aside, though, I guess you've overlooked the change introduced by
c987ff0d3cb3 ("iommu/dma: Respect IOMMU aperture when allocating")?

Robin.

> 
> Regards,
> Nipun
> 
>> +	if (!iova)
>> +		goto out_free_page;
>> +
>> +	msi_page->phys = msi_addr;
>> +	msi_page->iova = iova_dma_addr(iovad, iova);
>> +	if (iommu_map(domain, msi_page->iova, msi_addr, iovad->granule,
>> prot))
>> +		goto out_free_iova;
>> +
>> +	INIT_LIST_HEAD(&msi_page->list);
>> +	list_add(&msi_page->list, &cookie->msi_page_list);
>> +	return msi_page;
>> +
>> +out_free_iova:
>> +	__free_iova(iovad, iova);
>> +out_free_page:
>> +	kfree(msi_page);
>> +	return NULL;
>> +}
>> +
>> +void iommu_dma_map_msi_msg(int irq, struct msi_msg *msg) {
>> +	struct device *dev = msi_desc_to_dev(irq_get_msi_desc(irq));
>> +	struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
>> +	struct iommu_dma_cookie *cookie;
>> +	struct iommu_dma_msi_page *msi_page;
>> +	phys_addr_t msi_addr = (u64)msg->address_hi << 32 | msg->address_lo;
>> +	unsigned long flags;
>> +
>> +	if (!domain || !domain->iova_cookie)
>> +		return;
>> +
>> +	cookie = domain->iova_cookie;
>> +
>> +	/*
>> +	 * We disable IRQs to rule out a possible inversion against
>> +	 * irq_desc_lock if, say, someone tries to retarget the affinity
>> +	 * of an MSI from within an IPI handler.
>> +	 */
>> +	spin_lock_irqsave(&cookie->msi_lock, flags);
>> +	msi_page = iommu_dma_get_msi_page(dev, msi_addr, domain);
>> +	spin_unlock_irqrestore(&cookie->msi_lock, flags);
>> +
>> +	if (WARN_ON(!msi_page)) {
>> +		/*
>> +		 * We're called from a void callback, so the best we can do is
>> +		 * 'fail' by filling the message with obviously bogus values.
>> +		 * Since we got this far due to an IOMMU being present, it's
>> +		 * not like the existing address would have worked anyway...
>> +		 */
>> +		msg->address_hi = ~0U;
>> +		msg->address_lo = ~0U;
>> +		msg->data = ~0U;
>> +	} else {
>> +		msg->address_hi = upper_32_bits(msi_page->iova);
>> +		msg->address_lo &= iova_mask(&cookie->iovad);
>> +		msg->address_lo += lower_32_bits(msi_page->iova);
>> +	}
>> +}
>> diff --git a/drivers/irqchip/irq-gic-v2m.c b/drivers/irqchip/irq-gic-v2m.c index
>> 35eb7ac5d21f..863e073c6f7f 100644
>> --- a/drivers/irqchip/irq-gic-v2m.c
>> +++ b/drivers/irqchip/irq-gic-v2m.c
>> @@ -16,6 +16,7 @@
>>  #define pr_fmt(fmt) "GICv2m: " fmt
>>
>>  #include <linux/acpi.h>
>> +#include <linux/dma-iommu.h>
>>  #include <linux/irq.h>
>>  #include <linux/irqdomain.h>
>>  #include <linux/kernel.h>
>> @@ -108,6 +109,8 @@ static void gicv2m_compose_msi_msg(struct irq_data
>> *data, struct msi_msg *msg)
>>
>>  	if (v2m->flags & GICV2M_NEEDS_SPI_OFFSET)
>>  		msg->data -= v2m->spi_offset;
>> +
>> +	iommu_dma_map_msi_msg(data->irq, msg);
>>  }
>>
>>  static struct irq_chip gicv2m_irq_chip = { diff --git a/drivers/irqchip/irq-gic-v3-
>> its.c b/drivers/irqchip/irq-gic-v3-its.c
>> index 36b9c28a5c91..98ff669d5962 100644
>> --- a/drivers/irqchip/irq-gic-v3-its.c
>> +++ b/drivers/irqchip/irq-gic-v3-its.c
>> @@ -18,6 +18,7 @@
>>  #include <linux/bitmap.h>
>>  #include <linux/cpu.h>
>>  #include <linux/delay.h>
>> +#include <linux/dma-iommu.h>
>>  #include <linux/interrupt.h>
>>  #include <linux/log2.h>
>>  #include <linux/mm.h>
>> @@ -655,6 +656,8 @@ static void its_irq_compose_msi_msg(struct irq_data
>> *d, struct msi_msg *msg)
>>  	msg->address_lo		= addr & ((1UL << 32) - 1);
>>  	msg->address_hi		= addr >> 32;
>>  	msg->data		= its_get_event_id(d);
>> +
>> +	iommu_dma_map_msi_msg(d->irq, msg);
>>  }
>>
>>  static struct irq_chip its_irq_chip = { diff --git a/include/linux/dma-iommu.h
>> b/include/linux/dma-iommu.h index 81c5c8d167ad..5ee806e41b5c 100644
>> --- a/include/linux/dma-iommu.h
>> +++ b/include/linux/dma-iommu.h
>> @@ -21,6 +21,7 @@
>>
>>  #ifdef CONFIG_IOMMU_DMA
>>  #include <linux/iommu.h>
>> +#include <linux/msi.h>
>>
>>  int iommu_dma_init(void);
>>
>> @@ -62,9 +63,13 @@ void iommu_dma_unmap_sg(struct device *dev, struct
>> scatterlist *sg, int nents,  int iommu_dma_supported(struct device *dev, u64
>> mask);  int iommu_dma_mapping_error(struct device *dev, dma_addr_t
>> dma_addr);
>>
>> +/* The DMA API isn't _quite_ the whole story, though... */ void
>> +iommu_dma_map_msi_msg(int irq, struct msi_msg *msg);
>> +
>>  #else
>>
>>  struct iommu_domain;
>> +struct msi_msg;
>>
>>  static inline int iommu_dma_init(void)
>>  {
>> @@ -80,6 +85,10 @@ static inline void iommu_put_dma_cookie(struct
>> iommu_domain *domain)  {  }
>>
>> +static inline void iommu_dma_map_msi_msg(int irq, struct msi_msg *msg)
>> +{ }
>> +
>>  #endif	/* CONFIG_IOMMU_DMA */
>>  #endif	/* __KERNEL__ */
>>  #endif	/* __DMA_IOMMU_H */
>> --
>> 2.8.1.dirty
>>
>> _______________________________________________
>> iommu mailing list
>> iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA@public.gmane.org
>> https://lists.linuxfoundation.org/mailman/listinfo/iommu
> 

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

* [PATCH v7 21/22] iommu/dma: Add support for mapping MSIs
@ 2016-10-05  9:55             ` Robin Murphy
  0 siblings, 0 replies; 104+ messages in thread
From: Robin Murphy @ 2016-10-05  9:55 UTC (permalink / raw)
  To: linux-arm-kernel

On 05/10/16 08:00, Nipun Gupta wrote:
> 
> 
>> -----Original Message-----
>> From: iommu-bounces at lists.linux-foundation.org [mailto:iommu-
>> bounces at lists.linux-foundation.org] On Behalf Of Robin Murphy
>> Sent: Monday, September 12, 2016 21:44
>> To: will.deacon at arm.com; joro at 8bytes.org; iommu at lists.linux-
>> foundation.org; linux-arm-kernel at lists.infradead.org
>> Cc: devicetree at vger.kernel.org; punit.agrawal at arm.com;
>> thunder.leizhen at huawei.com
>> Subject: [PATCH v7 21/22] iommu/dma: Add support for mapping MSIs
>>
>> When an MSI doorbell is located downstream of an IOMMU, attaching devices
>> to a DMA ops domain and switching on translation leads to a rude shock when
>> their attempt to write to the physical address returned by the irqchip driver
>> faults (or worse, writes into some already-mapped
>> buffer) and no interrupt is forthcoming.
>>
>> Address this by adding a hook for relevant irqchip drivers to call from their
>> compose_msi_msg() callback, to swizzle the physical address with an
>> appropriatly-mapped IOVA for any device attached to one of our DMA ops
>> domains.
>>
>> Acked-by: Thomas Gleixner <tglx@linutronix.de>
>> Acked-by: Marc Zyngier <marc.zyngier@arm.com>
>> Signed-off-by: Robin Murphy <robin.murphy@arm.com>
>> ---
>>  drivers/iommu/dma-iommu.c        | 136
>> ++++++++++++++++++++++++++++++++++-----
>>  drivers/irqchip/irq-gic-v2m.c    |   3 +
>>  drivers/irqchip/irq-gic-v3-its.c |   3 +
>>  include/linux/dma-iommu.h        |   9 +++
>>  4 files changed, 136 insertions(+), 15 deletions(-)
>>
>> diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c index
>> 00c8a08d56e7..4329d18080cf 100644
>> --- a/drivers/iommu/dma-iommu.c
>> +++ b/drivers/iommu/dma-iommu.c
>> @@ -25,10 +25,28 @@
>>  #include <linux/huge_mm.h>
>>  #include <linux/iommu.h>
>>  #include <linux/iova.h>
>> +#include <linux/irq.h>
>>  #include <linux/mm.h>
>>  #include <linux/scatterlist.h>
>>  #include <linux/vmalloc.h>
>>
>> +struct iommu_dma_msi_page {
>> +	struct list_head	list;
>> +	dma_addr_t		iova;
>> +	phys_addr_t		phys;
>> +};
>> +
>> +struct iommu_dma_cookie {
>> +	struct iova_domain	iovad;
>> +	struct list_head	msi_page_list;
>> +	spinlock_t		msi_lock;
>> +};
>> +
>> +static inline struct iova_domain *cookie_iovad(struct iommu_domain
>> +*domain) {
>> +	return &((struct iommu_dma_cookie *)domain->iova_cookie)->iovad; }
>> +
>>  int iommu_dma_init(void)
>>  {
>>  	return iova_cache_get();
>> @@ -43,15 +61,19 @@ int iommu_dma_init(void)
>>   */
>>  int iommu_get_dma_cookie(struct iommu_domain *domain)  {
>> -	struct iova_domain *iovad;
>> +	struct iommu_dma_cookie *cookie;
>>
>>  	if (domain->iova_cookie)
>>  		return -EEXIST;
>>
>> -	iovad = kzalloc(sizeof(*iovad), GFP_KERNEL);
>> -	domain->iova_cookie = iovad;
>> +	cookie = kzalloc(sizeof(*cookie), GFP_KERNEL);
>> +	if (!cookie)
>> +		return -ENOMEM;
>>
>> -	return iovad ? 0 : -ENOMEM;
>> +	spin_lock_init(&cookie->msi_lock);
>> +	INIT_LIST_HEAD(&cookie->msi_page_list);
>> +	domain->iova_cookie = cookie;
>> +	return 0;
>>  }
>>  EXPORT_SYMBOL(iommu_get_dma_cookie);
>>
>> @@ -63,14 +85,20 @@ EXPORT_SYMBOL(iommu_get_dma_cookie);
>>   */
>>  void iommu_put_dma_cookie(struct iommu_domain *domain)  {
>> -	struct iova_domain *iovad = domain->iova_cookie;
>> +	struct iommu_dma_cookie *cookie = domain->iova_cookie;
>> +	struct iommu_dma_msi_page *msi, *tmp;
>>
>> -	if (!iovad)
>> +	if (!cookie)
>>  		return;
>>
>> -	if (iovad->granule)
>> -		put_iova_domain(iovad);
>> -	kfree(iovad);
>> +	if (cookie->iovad.granule)
>> +		put_iova_domain(&cookie->iovad);
>> +
>> +	list_for_each_entry_safe(msi, tmp, &cookie->msi_page_list, list) {
>> +		list_del(&msi->list);
>> +		kfree(msi);
>> +	}
>> +	kfree(cookie);
>>  	domain->iova_cookie = NULL;
>>  }
>>  EXPORT_SYMBOL(iommu_put_dma_cookie);
>> @@ -88,7 +116,7 @@ EXPORT_SYMBOL(iommu_put_dma_cookie);
>>   */
>>  int iommu_dma_init_domain(struct iommu_domain *domain, dma_addr_t
>> base, u64 size)  {
>> -	struct iova_domain *iovad = domain->iova_cookie;
>> +	struct iova_domain *iovad = cookie_iovad(domain);
>>  	unsigned long order, base_pfn, end_pfn;
>>
>>  	if (!iovad)
>> @@ -155,7 +183,7 @@ int dma_direction_to_prot(enum dma_data_direction
>> dir, bool coherent)  static struct iova *__alloc_iova(struct iommu_domain
>> *domain, size_t size,
>>  		dma_addr_t dma_limit)
>>  {
>> -	struct iova_domain *iovad = domain->iova_cookie;
>> +	struct iova_domain *iovad = cookie_iovad(domain);
>>  	unsigned long shift = iova_shift(iovad);
>>  	unsigned long length = iova_align(iovad, size) >> shift;
>>
>> @@ -171,7 +199,7 @@ static struct iova *__alloc_iova(struct iommu_domain
>> *domain, size_t size,
>>  /* The IOVA allocator knows what we mapped, so just unmap whatever that
>> was */  static void __iommu_dma_unmap(struct iommu_domain *domain,
>> dma_addr_t dma_addr)  {
>> -	struct iova_domain *iovad = domain->iova_cookie;
>> +	struct iova_domain *iovad = cookie_iovad(domain);
>>  	unsigned long shift = iova_shift(iovad);
>>  	unsigned long pfn = dma_addr >> shift;
>>  	struct iova *iova = find_iova(iovad, pfn); @@ -294,7 +322,7 @@ struct
>> page **iommu_dma_alloc(struct device *dev, size_t size, gfp_t gfp,
>>  		void (*flush_page)(struct device *, const void *, phys_addr_t))  {
>>  	struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
>> -	struct iova_domain *iovad = domain->iova_cookie;
>> +	struct iova_domain *iovad = cookie_iovad(domain);
>>  	struct iova *iova;
>>  	struct page **pages;
>>  	struct sg_table sgt;
>> @@ -386,7 +414,7 @@ dma_addr_t iommu_dma_map_page(struct device
>> *dev, struct page *page,  {
>>  	dma_addr_t dma_addr;
>>  	struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
>> -	struct iova_domain *iovad = domain->iova_cookie;
>> +	struct iova_domain *iovad = cookie_iovad(domain);
>>  	phys_addr_t phys = page_to_phys(page) + offset;
>>  	size_t iova_off = iova_offset(iovad, phys);
>>  	size_t len = iova_align(iovad, size + iova_off); @@ -495,7 +523,7 @@
>> int iommu_dma_map_sg(struct device *dev, struct scatterlist *sg,
>>  		int nents, int prot)
>>  {
>>  	struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
>> -	struct iova_domain *iovad = domain->iova_cookie;
>> +	struct iova_domain *iovad = cookie_iovad(domain);
>>  	struct iova *iova;
>>  	struct scatterlist *s, *prev = NULL;
>>  	dma_addr_t dma_addr;
>> @@ -587,3 +615,81 @@ int iommu_dma_mapping_error(struct device *dev,
>> dma_addr_t dma_addr)  {
>>  	return dma_addr == DMA_ERROR_CODE;
>>  }
>> +
>> +static struct iommu_dma_msi_page *iommu_dma_get_msi_page(struct device
>> *dev,
>> +		phys_addr_t msi_addr, struct iommu_domain *domain) {
>> +	struct iommu_dma_cookie *cookie = domain->iova_cookie;
>> +	struct iommu_dma_msi_page *msi_page;
>> +	struct iova_domain *iovad = &cookie->iovad;
>> +	struct iova *iova;
>> +	int prot = IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_MMIO;
>> +
>> +	msi_addr &= ~(phys_addr_t)iova_mask(iovad);
>> +	list_for_each_entry(msi_page, &cookie->msi_page_list, list)
>> +		if (msi_page->phys == msi_addr)
>> +			return msi_page;
>> +
>> +	msi_page = kzalloc(sizeof(*msi_page), GFP_ATOMIC);
>> +	if (!msi_page)
>> +		return NULL;
>> +
>> +	iova = __alloc_iova(domain, iovad->granule, dma_get_mask(dev));
> 
> I think this should be 'iova = __alloc_iova(domain, iovad->granule, dma_get_mask(dev));'

Er, yes... I fully agree. That's why it is exactly that.

> as __alloc_iova takes input parameter as 'struct iova_domain *'

Joking aside, though, I guess you've overlooked the change introduced by
c987ff0d3cb3 ("iommu/dma: Respect IOMMU aperture when allocating")?

Robin.

> 
> Regards,
> Nipun
> 
>> +	if (!iova)
>> +		goto out_free_page;
>> +
>> +	msi_page->phys = msi_addr;
>> +	msi_page->iova = iova_dma_addr(iovad, iova);
>> +	if (iommu_map(domain, msi_page->iova, msi_addr, iovad->granule,
>> prot))
>> +		goto out_free_iova;
>> +
>> +	INIT_LIST_HEAD(&msi_page->list);
>> +	list_add(&msi_page->list, &cookie->msi_page_list);
>> +	return msi_page;
>> +
>> +out_free_iova:
>> +	__free_iova(iovad, iova);
>> +out_free_page:
>> +	kfree(msi_page);
>> +	return NULL;
>> +}
>> +
>> +void iommu_dma_map_msi_msg(int irq, struct msi_msg *msg) {
>> +	struct device *dev = msi_desc_to_dev(irq_get_msi_desc(irq));
>> +	struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
>> +	struct iommu_dma_cookie *cookie;
>> +	struct iommu_dma_msi_page *msi_page;
>> +	phys_addr_t msi_addr = (u64)msg->address_hi << 32 | msg->address_lo;
>> +	unsigned long flags;
>> +
>> +	if (!domain || !domain->iova_cookie)
>> +		return;
>> +
>> +	cookie = domain->iova_cookie;
>> +
>> +	/*
>> +	 * We disable IRQs to rule out a possible inversion against
>> +	 * irq_desc_lock if, say, someone tries to retarget the affinity
>> +	 * of an MSI from within an IPI handler.
>> +	 */
>> +	spin_lock_irqsave(&cookie->msi_lock, flags);
>> +	msi_page = iommu_dma_get_msi_page(dev, msi_addr, domain);
>> +	spin_unlock_irqrestore(&cookie->msi_lock, flags);
>> +
>> +	if (WARN_ON(!msi_page)) {
>> +		/*
>> +		 * We're called from a void callback, so the best we can do is
>> +		 * 'fail' by filling the message with obviously bogus values.
>> +		 * Since we got this far due to an IOMMU being present, it's
>> +		 * not like the existing address would have worked anyway...
>> +		 */
>> +		msg->address_hi = ~0U;
>> +		msg->address_lo = ~0U;
>> +		msg->data = ~0U;
>> +	} else {
>> +		msg->address_hi = upper_32_bits(msi_page->iova);
>> +		msg->address_lo &= iova_mask(&cookie->iovad);
>> +		msg->address_lo += lower_32_bits(msi_page->iova);
>> +	}
>> +}
>> diff --git a/drivers/irqchip/irq-gic-v2m.c b/drivers/irqchip/irq-gic-v2m.c index
>> 35eb7ac5d21f..863e073c6f7f 100644
>> --- a/drivers/irqchip/irq-gic-v2m.c
>> +++ b/drivers/irqchip/irq-gic-v2m.c
>> @@ -16,6 +16,7 @@
>>  #define pr_fmt(fmt) "GICv2m: " fmt
>>
>>  #include <linux/acpi.h>
>> +#include <linux/dma-iommu.h>
>>  #include <linux/irq.h>
>>  #include <linux/irqdomain.h>
>>  #include <linux/kernel.h>
>> @@ -108,6 +109,8 @@ static void gicv2m_compose_msi_msg(struct irq_data
>> *data, struct msi_msg *msg)
>>
>>  	if (v2m->flags & GICV2M_NEEDS_SPI_OFFSET)
>>  		msg->data -= v2m->spi_offset;
>> +
>> +	iommu_dma_map_msi_msg(data->irq, msg);
>>  }
>>
>>  static struct irq_chip gicv2m_irq_chip = { diff --git a/drivers/irqchip/irq-gic-v3-
>> its.c b/drivers/irqchip/irq-gic-v3-its.c
>> index 36b9c28a5c91..98ff669d5962 100644
>> --- a/drivers/irqchip/irq-gic-v3-its.c
>> +++ b/drivers/irqchip/irq-gic-v3-its.c
>> @@ -18,6 +18,7 @@
>>  #include <linux/bitmap.h>
>>  #include <linux/cpu.h>
>>  #include <linux/delay.h>
>> +#include <linux/dma-iommu.h>
>>  #include <linux/interrupt.h>
>>  #include <linux/log2.h>
>>  #include <linux/mm.h>
>> @@ -655,6 +656,8 @@ static void its_irq_compose_msi_msg(struct irq_data
>> *d, struct msi_msg *msg)
>>  	msg->address_lo		= addr & ((1UL << 32) - 1);
>>  	msg->address_hi		= addr >> 32;
>>  	msg->data		= its_get_event_id(d);
>> +
>> +	iommu_dma_map_msi_msg(d->irq, msg);
>>  }
>>
>>  static struct irq_chip its_irq_chip = { diff --git a/include/linux/dma-iommu.h
>> b/include/linux/dma-iommu.h index 81c5c8d167ad..5ee806e41b5c 100644
>> --- a/include/linux/dma-iommu.h
>> +++ b/include/linux/dma-iommu.h
>> @@ -21,6 +21,7 @@
>>
>>  #ifdef CONFIG_IOMMU_DMA
>>  #include <linux/iommu.h>
>> +#include <linux/msi.h>
>>
>>  int iommu_dma_init(void);
>>
>> @@ -62,9 +63,13 @@ void iommu_dma_unmap_sg(struct device *dev, struct
>> scatterlist *sg, int nents,  int iommu_dma_supported(struct device *dev, u64
>> mask);  int iommu_dma_mapping_error(struct device *dev, dma_addr_t
>> dma_addr);
>>
>> +/* The DMA API isn't _quite_ the whole story, though... */ void
>> +iommu_dma_map_msi_msg(int irq, struct msi_msg *msg);
>> +
>>  #else
>>
>>  struct iommu_domain;
>> +struct msi_msg;
>>
>>  static inline int iommu_dma_init(void)
>>  {
>> @@ -80,6 +85,10 @@ static inline void iommu_put_dma_cookie(struct
>> iommu_domain *domain)  {  }
>>
>> +static inline void iommu_dma_map_msi_msg(int irq, struct msi_msg *msg)
>> +{ }
>> +
>>  #endif	/* CONFIG_IOMMU_DMA */
>>  #endif	/* __KERNEL__ */
>>  #endif	/* __DMA_IOMMU_H */
>> --
>> 2.8.1.dirty
>>
>> _______________________________________________
>> iommu mailing list
>> iommu at lists.linux-foundation.org
>> https://lists.linuxfoundation.org/mailman/listinfo/iommu
> 

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

* RE: [PATCH v7 21/22] iommu/dma: Add support for mapping MSIs
  2016-10-05  9:55             ` Robin Murphy
@ 2016-10-05 11:31                 ` Nipun Gupta
  -1 siblings, 0 replies; 104+ messages in thread
From: Nipun Gupta @ 2016-10-05 11:31 UTC (permalink / raw)
  To: Robin Murphy
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, punit.agrawal-5wv7dgnIgG8,
	will.deacon-5wv7dgnIgG8,
	iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	thunder.leizhen-hv44wF8Li93QT0dZR+AlfA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r



> -----Original Message-----
> From: Robin Murphy [mailto:robin.murphy-5wv7dgnIgG8@public.gmane.org]
> Sent: Wednesday, October 05, 2016 15:26
> To: Nipun Gupta <nipun.gupta-3arQi8VN3Tc@public.gmane.org>
> Cc: will.deacon-5wv7dgnIgG8@public.gmane.org; joro-zLv9SwRftAIdnm+yROfE0A@public.gmane.org; iommu-cunTk1MwBs/ROKNJybVBZg@public.gmane.org
> foundation.org; linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org;
> devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org; punit.agrawal-5wv7dgnIgG8@public.gmane.org;
> thunder.leizhen-hv44wF8Li93QT0dZR+AlfA@public.gmane.org
> Subject: Re: [PATCH v7 21/22] iommu/dma: Add support for mapping MSIs
> 
> On 05/10/16 08:00, Nipun Gupta wrote:
> >
> >
> >> -----Original Message-----
> >> From: iommu-bounces-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA@public.gmane.org [mailto:iommu-
> >> bounces-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA@public.gmane.org] On Behalf Of Robin Murphy
> >> Sent: Monday, September 12, 2016 21:44
> >> To: will.deacon-5wv7dgnIgG8@public.gmane.org; joro-zLv9SwRftAIdnm+yROfE0A@public.gmane.org; iommu-cunTk1MwBs/ROKNJybVBZg@public.gmane.org
> >> foundation.org; linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org
> >> Cc: devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org; punit.agrawal-5wv7dgnIgG8@public.gmane.org;
> >> thunder.leizhen-hv44wF8Li93QT0dZR+AlfA@public.gmane.org
> >> Subject: [PATCH v7 21/22] iommu/dma: Add support for mapping MSIs
> >>
> >> When an MSI doorbell is located downstream of an IOMMU, attaching
> >> devices to a DMA ops domain and switching on translation leads to a
> >> rude shock when their attempt to write to the physical address
> >> returned by the irqchip driver faults (or worse, writes into some
> >> already-mapped
> >> buffer) and no interrupt is forthcoming.
> >>
> >> Address this by adding a hook for relevant irqchip drivers to call
> >> from their
> >> compose_msi_msg() callback, to swizzle the physical address with an
> >> appropriatly-mapped IOVA for any device attached to one of our DMA
> >> ops domains.
> >>
> >> Acked-by: Thomas Gleixner <tglx-hfZtesqFncYOwBW4kG4KsQ@public.gmane.org>
> >> Acked-by: Marc Zyngier <marc.zyngier-5wv7dgnIgG8@public.gmane.org>
> >> Signed-off-by: Robin Murphy <robin.murphy-5wv7dgnIgG8@public.gmane.org>
> >> ---
> >>  drivers/iommu/dma-iommu.c        | 136
> >> ++++++++++++++++++++++++++++++++++-----
> >>  drivers/irqchip/irq-gic-v2m.c    |   3 +
> >>  drivers/irqchip/irq-gic-v3-its.c |   3 +
> >>  include/linux/dma-iommu.h        |   9 +++
> >>  4 files changed, 136 insertions(+), 15 deletions(-)
> >>
> >> diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
> >> index 00c8a08d56e7..4329d18080cf 100644
> >> --- a/drivers/iommu/dma-iommu.c
> >> +++ b/drivers/iommu/dma-iommu.c
> >> @@ -25,10 +25,28 @@
> >>  #include <linux/huge_mm.h>
> >>  #include <linux/iommu.h>
> >>  #include <linux/iova.h>
> >> +#include <linux/irq.h>
> >>  #include <linux/mm.h>
> >>  #include <linux/scatterlist.h>
> >>  #include <linux/vmalloc.h>
> >>
> >> +struct iommu_dma_msi_page {
> >> +	struct list_head	list;
> >> +	dma_addr_t		iova;
> >> +	phys_addr_t		phys;
> >> +};
> >> +
> >> +struct iommu_dma_cookie {
> >> +	struct iova_domain	iovad;
> >> +	struct list_head	msi_page_list;
> >> +	spinlock_t		msi_lock;
> >> +};
> >> +
> >> +static inline struct iova_domain *cookie_iovad(struct iommu_domain
> >> +*domain) {
> >> +	return &((struct iommu_dma_cookie *)domain->iova_cookie)->iovad; }
> >> +
> >>  int iommu_dma_init(void)
> >>  {
> >>  	return iova_cache_get();
> >> @@ -43,15 +61,19 @@ int iommu_dma_init(void)
> >>   */
> >>  int iommu_get_dma_cookie(struct iommu_domain *domain)  {
> >> -	struct iova_domain *iovad;
> >> +	struct iommu_dma_cookie *cookie;
> >>
> >>  	if (domain->iova_cookie)
> >>  		return -EEXIST;
> >>
> >> -	iovad = kzalloc(sizeof(*iovad), GFP_KERNEL);
> >> -	domain->iova_cookie = iovad;
> >> +	cookie = kzalloc(sizeof(*cookie), GFP_KERNEL);
> >> +	if (!cookie)
> >> +		return -ENOMEM;
> >>
> >> -	return iovad ? 0 : -ENOMEM;
> >> +	spin_lock_init(&cookie->msi_lock);
> >> +	INIT_LIST_HEAD(&cookie->msi_page_list);
> >> +	domain->iova_cookie = cookie;
> >> +	return 0;
> >>  }
> >>  EXPORT_SYMBOL(iommu_get_dma_cookie);
> >>
> >> @@ -63,14 +85,20 @@ EXPORT_SYMBOL(iommu_get_dma_cookie);
> >>   */
> >>  void iommu_put_dma_cookie(struct iommu_domain *domain)  {
> >> -	struct iova_domain *iovad = domain->iova_cookie;
> >> +	struct iommu_dma_cookie *cookie = domain->iova_cookie;
> >> +	struct iommu_dma_msi_page *msi, *tmp;
> >>
> >> -	if (!iovad)
> >> +	if (!cookie)
> >>  		return;
> >>
> >> -	if (iovad->granule)
> >> -		put_iova_domain(iovad);
> >> -	kfree(iovad);
> >> +	if (cookie->iovad.granule)
> >> +		put_iova_domain(&cookie->iovad);
> >> +
> >> +	list_for_each_entry_safe(msi, tmp, &cookie->msi_page_list, list) {
> >> +		list_del(&msi->list);
> >> +		kfree(msi);
> >> +	}
> >> +	kfree(cookie);
> >>  	domain->iova_cookie = NULL;
> >>  }
> >>  EXPORT_SYMBOL(iommu_put_dma_cookie);
> >> @@ -88,7 +116,7 @@ EXPORT_SYMBOL(iommu_put_dma_cookie);
> >>   */
> >>  int iommu_dma_init_domain(struct iommu_domain *domain, dma_addr_t
> >> base, u64 size)  {
> >> -	struct iova_domain *iovad = domain->iova_cookie;
> >> +	struct iova_domain *iovad = cookie_iovad(domain);
> >>  	unsigned long order, base_pfn, end_pfn;
> >>
> >>  	if (!iovad)
> >> @@ -155,7 +183,7 @@ int dma_direction_to_prot(enum
> dma_data_direction
> >> dir, bool coherent)  static struct iova *__alloc_iova(struct
> >> iommu_domain *domain, size_t size,
> >>  		dma_addr_t dma_limit)
> >>  {
> >> -	struct iova_domain *iovad = domain->iova_cookie;
> >> +	struct iova_domain *iovad = cookie_iovad(domain);
> >>  	unsigned long shift = iova_shift(iovad);
> >>  	unsigned long length = iova_align(iovad, size) >> shift;
> >>
> >> @@ -171,7 +199,7 @@ static struct iova *__alloc_iova(struct
> >> iommu_domain *domain, size_t size,
> >>  /* The IOVA allocator knows what we mapped, so just unmap whatever
> >> that was */  static void __iommu_dma_unmap(struct iommu_domain
> >> *domain, dma_addr_t dma_addr)  {
> >> -	struct iova_domain *iovad = domain->iova_cookie;
> >> +	struct iova_domain *iovad = cookie_iovad(domain);
> >>  	unsigned long shift = iova_shift(iovad);
> >>  	unsigned long pfn = dma_addr >> shift;
> >>  	struct iova *iova = find_iova(iovad, pfn); @@ -294,7 +322,7 @@
> >> struct page **iommu_dma_alloc(struct device *dev, size_t size, gfp_t gfp,
> >>  		void (*flush_page)(struct device *, const void *, phys_addr_t))  {
> >>  	struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
> >> -	struct iova_domain *iovad = domain->iova_cookie;
> >> +	struct iova_domain *iovad = cookie_iovad(domain);
> >>  	struct iova *iova;
> >>  	struct page **pages;
> >>  	struct sg_table sgt;
> >> @@ -386,7 +414,7 @@ dma_addr_t iommu_dma_map_page(struct device
> *dev,
> >> struct page *page,  {
> >>  	dma_addr_t dma_addr;
> >>  	struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
> >> -	struct iova_domain *iovad = domain->iova_cookie;
> >> +	struct iova_domain *iovad = cookie_iovad(domain);
> >>  	phys_addr_t phys = page_to_phys(page) + offset;
> >>  	size_t iova_off = iova_offset(iovad, phys);
> >>  	size_t len = iova_align(iovad, size + iova_off); @@ -495,7 +523,7
> >> @@ int iommu_dma_map_sg(struct device *dev, struct scatterlist *sg,
> >>  		int nents, int prot)
> >>  {
> >>  	struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
> >> -	struct iova_domain *iovad = domain->iova_cookie;
> >> +	struct iova_domain *iovad = cookie_iovad(domain);
> >>  	struct iova *iova;
> >>  	struct scatterlist *s, *prev = NULL;
> >>  	dma_addr_t dma_addr;
> >> @@ -587,3 +615,81 @@ int iommu_dma_mapping_error(struct device *dev,
> >> dma_addr_t dma_addr)  {
> >>  	return dma_addr == DMA_ERROR_CODE;
> >>  }
> >> +
> >> +static struct iommu_dma_msi_page *iommu_dma_get_msi_page(struct
> >> +device
> >> *dev,
> >> +		phys_addr_t msi_addr, struct iommu_domain *domain) {
> >> +	struct iommu_dma_cookie *cookie = domain->iova_cookie;
> >> +	struct iommu_dma_msi_page *msi_page;
> >> +	struct iova_domain *iovad = &cookie->iovad;
> >> +	struct iova *iova;
> >> +	int prot = IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_MMIO;
> >> +
> >> +	msi_addr &= ~(phys_addr_t)iova_mask(iovad);
> >> +	list_for_each_entry(msi_page, &cookie->msi_page_list, list)
> >> +		if (msi_page->phys == msi_addr)
> >> +			return msi_page;
> >> +
> >> +	msi_page = kzalloc(sizeof(*msi_page), GFP_ATOMIC);
> >> +	if (!msi_page)
> >> +		return NULL;
> >> +
> >> +	iova = __alloc_iova(domain, iovad->granule, dma_get_mask(dev));
> >
> > I think this should be 'iova = __alloc_iova(domain, iovad->granule,
> dma_get_mask(dev));'
> 
> Er, yes... I fully agree. That's why it is exactly that.
> 
> > as __alloc_iova takes input parameter as 'struct iova_domain *'
> 
> Joking aside, though, I guess you've overlooked the change introduced by
> c987ff0d3cb3 ("iommu/dma: Respect IOMMU aperture when allocating")?

Ooops!! My bad. That's right, I missed out this change.

Thanks,
Nipun

> 
> Robin.
> 
> >
> > Regards,
> > Nipun
> >
> >> +	if (!iova)
> >> +		goto out_free_page;
> >> +
> >> +	msi_page->phys = msi_addr;
> >> +	msi_page->iova = iova_dma_addr(iovad, iova);
> >> +	if (iommu_map(domain, msi_page->iova, msi_addr, iovad->granule,
> >> prot))
> >> +		goto out_free_iova;
> >> +
> >> +	INIT_LIST_HEAD(&msi_page->list);
> >> +	list_add(&msi_page->list, &cookie->msi_page_list);
> >> +	return msi_page;
> >> +
> >> +out_free_iova:
> >> +	__free_iova(iovad, iova);
> >> +out_free_page:
> >> +	kfree(msi_page);
> >> +	return NULL;
> >> +}
> >> +
> >> +void iommu_dma_map_msi_msg(int irq, struct msi_msg *msg) {
> >> +	struct device *dev = msi_desc_to_dev(irq_get_msi_desc(irq));
> >> +	struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
> >> +	struct iommu_dma_cookie *cookie;
> >> +	struct iommu_dma_msi_page *msi_page;
> >> +	phys_addr_t msi_addr = (u64)msg->address_hi << 32 | msg->address_lo;
> >> +	unsigned long flags;
> >> +
> >> +	if (!domain || !domain->iova_cookie)
> >> +		return;
> >> +
> >> +	cookie = domain->iova_cookie;
> >> +
> >> +	/*
> >> +	 * We disable IRQs to rule out a possible inversion against
> >> +	 * irq_desc_lock if, say, someone tries to retarget the affinity
> >> +	 * of an MSI from within an IPI handler.
> >> +	 */
> >> +	spin_lock_irqsave(&cookie->msi_lock, flags);
> >> +	msi_page = iommu_dma_get_msi_page(dev, msi_addr, domain);
> >> +	spin_unlock_irqrestore(&cookie->msi_lock, flags);
> >> +
> >> +	if (WARN_ON(!msi_page)) {
> >> +		/*
> >> +		 * We're called from a void callback, so the best we can do is
> >> +		 * 'fail' by filling the message with obviously bogus values.
> >> +		 * Since we got this far due to an IOMMU being present, it's
> >> +		 * not like the existing address would have worked anyway...
> >> +		 */
> >> +		msg->address_hi = ~0U;
> >> +		msg->address_lo = ~0U;
> >> +		msg->data = ~0U;
> >> +	} else {
> >> +		msg->address_hi = upper_32_bits(msi_page->iova);
> >> +		msg->address_lo &= iova_mask(&cookie->iovad);
> >> +		msg->address_lo += lower_32_bits(msi_page->iova);
> >> +	}
> >> +}
> >> diff --git a/drivers/irqchip/irq-gic-v2m.c
> >> b/drivers/irqchip/irq-gic-v2m.c index 35eb7ac5d21f..863e073c6f7f
> >> 100644
> >> --- a/drivers/irqchip/irq-gic-v2m.c
> >> +++ b/drivers/irqchip/irq-gic-v2m.c
> >> @@ -16,6 +16,7 @@
> >>  #define pr_fmt(fmt) "GICv2m: " fmt
> >>
> >>  #include <linux/acpi.h>
> >> +#include <linux/dma-iommu.h>
> >>  #include <linux/irq.h>
> >>  #include <linux/irqdomain.h>
> >>  #include <linux/kernel.h>
> >> @@ -108,6 +109,8 @@ static void gicv2m_compose_msi_msg(struct
> >> irq_data *data, struct msi_msg *msg)
> >>
> >>  	if (v2m->flags & GICV2M_NEEDS_SPI_OFFSET)
> >>  		msg->data -= v2m->spi_offset;
> >> +
> >> +	iommu_dma_map_msi_msg(data->irq, msg);
> >>  }
> >>
> >>  static struct irq_chip gicv2m_irq_chip = { diff --git
> >> a/drivers/irqchip/irq-gic-v3- its.c
> >> b/drivers/irqchip/irq-gic-v3-its.c
> >> index 36b9c28a5c91..98ff669d5962 100644
> >> --- a/drivers/irqchip/irq-gic-v3-its.c
> >> +++ b/drivers/irqchip/irq-gic-v3-its.c
> >> @@ -18,6 +18,7 @@
> >>  #include <linux/bitmap.h>
> >>  #include <linux/cpu.h>
> >>  #include <linux/delay.h>
> >> +#include <linux/dma-iommu.h>
> >>  #include <linux/interrupt.h>
> >>  #include <linux/log2.h>
> >>  #include <linux/mm.h>
> >> @@ -655,6 +656,8 @@ static void its_irq_compose_msi_msg(struct
> >> irq_data *d, struct msi_msg *msg)
> >>  	msg->address_lo		= addr & ((1UL << 32) - 1);
> >>  	msg->address_hi		= addr >> 32;
> >>  	msg->data		= its_get_event_id(d);
> >> +
> >> +	iommu_dma_map_msi_msg(d->irq, msg);
> >>  }
> >>
> >>  static struct irq_chip its_irq_chip = { diff --git
> >> a/include/linux/dma-iommu.h b/include/linux/dma-iommu.h index
> >> 81c5c8d167ad..5ee806e41b5c 100644
> >> --- a/include/linux/dma-iommu.h
> >> +++ b/include/linux/dma-iommu.h
> >> @@ -21,6 +21,7 @@
> >>
> >>  #ifdef CONFIG_IOMMU_DMA
> >>  #include <linux/iommu.h>
> >> +#include <linux/msi.h>
> >>
> >>  int iommu_dma_init(void);
> >>
> >> @@ -62,9 +63,13 @@ void iommu_dma_unmap_sg(struct device *dev,
> struct
> >> scatterlist *sg, int nents,  int iommu_dma_supported(struct device
> >> *dev, u64 mask);  int iommu_dma_mapping_error(struct device *dev,
> >> dma_addr_t dma_addr);
> >>
> >> +/* The DMA API isn't _quite_ the whole story, though... */ void
> >> +iommu_dma_map_msi_msg(int irq, struct msi_msg *msg);
> >> +
> >>  #else
> >>
> >>  struct iommu_domain;
> >> +struct msi_msg;
> >>
> >>  static inline int iommu_dma_init(void)  { @@ -80,6 +85,10 @@ static
> >> inline void iommu_put_dma_cookie(struct iommu_domain *domain)  {  }
> >>
> >> +static inline void iommu_dma_map_msi_msg(int irq, struct msi_msg
> >> +*msg) { }
> >> +
> >>  #endif	/* CONFIG_IOMMU_DMA */
> >>  #endif	/* __KERNEL__ */
> >>  #endif	/* __DMA_IOMMU_H */
> >> --
> >> 2.8.1.dirty
> >>
> >> _______________________________________________
> >> iommu mailing list
> >> iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA@public.gmane.org
> >> https://lists.linuxfoundation.org/mailman/listinfo/iommu
> >

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

* [PATCH v7 21/22] iommu/dma: Add support for mapping MSIs
@ 2016-10-05 11:31                 ` Nipun Gupta
  0 siblings, 0 replies; 104+ messages in thread
From: Nipun Gupta @ 2016-10-05 11:31 UTC (permalink / raw)
  To: linux-arm-kernel



> -----Original Message-----
> From: Robin Murphy [mailto:robin.murphy at arm.com]
> Sent: Wednesday, October 05, 2016 15:26
> To: Nipun Gupta <nipun.gupta@nxp.com>
> Cc: will.deacon at arm.com; joro at 8bytes.org; iommu at lists.linux-
> foundation.org; linux-arm-kernel at lists.infradead.org;
> devicetree at vger.kernel.org; punit.agrawal at arm.com;
> thunder.leizhen at huawei.com
> Subject: Re: [PATCH v7 21/22] iommu/dma: Add support for mapping MSIs
> 
> On 05/10/16 08:00, Nipun Gupta wrote:
> >
> >
> >> -----Original Message-----
> >> From: iommu-bounces at lists.linux-foundation.org [mailto:iommu-
> >> bounces at lists.linux-foundation.org] On Behalf Of Robin Murphy
> >> Sent: Monday, September 12, 2016 21:44
> >> To: will.deacon at arm.com; joro at 8bytes.org; iommu at lists.linux-
> >> foundation.org; linux-arm-kernel at lists.infradead.org
> >> Cc: devicetree at vger.kernel.org; punit.agrawal at arm.com;
> >> thunder.leizhen at huawei.com
> >> Subject: [PATCH v7 21/22] iommu/dma: Add support for mapping MSIs
> >>
> >> When an MSI doorbell is located downstream of an IOMMU, attaching
> >> devices to a DMA ops domain and switching on translation leads to a
> >> rude shock when their attempt to write to the physical address
> >> returned by the irqchip driver faults (or worse, writes into some
> >> already-mapped
> >> buffer) and no interrupt is forthcoming.
> >>
> >> Address this by adding a hook for relevant irqchip drivers to call
> >> from their
> >> compose_msi_msg() callback, to swizzle the physical address with an
> >> appropriatly-mapped IOVA for any device attached to one of our DMA
> >> ops domains.
> >>
> >> Acked-by: Thomas Gleixner <tglx@linutronix.de>
> >> Acked-by: Marc Zyngier <marc.zyngier@arm.com>
> >> Signed-off-by: Robin Murphy <robin.murphy@arm.com>
> >> ---
> >>  drivers/iommu/dma-iommu.c        | 136
> >> ++++++++++++++++++++++++++++++++++-----
> >>  drivers/irqchip/irq-gic-v2m.c    |   3 +
> >>  drivers/irqchip/irq-gic-v3-its.c |   3 +
> >>  include/linux/dma-iommu.h        |   9 +++
> >>  4 files changed, 136 insertions(+), 15 deletions(-)
> >>
> >> diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
> >> index 00c8a08d56e7..4329d18080cf 100644
> >> --- a/drivers/iommu/dma-iommu.c
> >> +++ b/drivers/iommu/dma-iommu.c
> >> @@ -25,10 +25,28 @@
> >>  #include <linux/huge_mm.h>
> >>  #include <linux/iommu.h>
> >>  #include <linux/iova.h>
> >> +#include <linux/irq.h>
> >>  #include <linux/mm.h>
> >>  #include <linux/scatterlist.h>
> >>  #include <linux/vmalloc.h>
> >>
> >> +struct iommu_dma_msi_page {
> >> +	struct list_head	list;
> >> +	dma_addr_t		iova;
> >> +	phys_addr_t		phys;
> >> +};
> >> +
> >> +struct iommu_dma_cookie {
> >> +	struct iova_domain	iovad;
> >> +	struct list_head	msi_page_list;
> >> +	spinlock_t		msi_lock;
> >> +};
> >> +
> >> +static inline struct iova_domain *cookie_iovad(struct iommu_domain
> >> +*domain) {
> >> +	return &((struct iommu_dma_cookie *)domain->iova_cookie)->iovad; }
> >> +
> >>  int iommu_dma_init(void)
> >>  {
> >>  	return iova_cache_get();
> >> @@ -43,15 +61,19 @@ int iommu_dma_init(void)
> >>   */
> >>  int iommu_get_dma_cookie(struct iommu_domain *domain)  {
> >> -	struct iova_domain *iovad;
> >> +	struct iommu_dma_cookie *cookie;
> >>
> >>  	if (domain->iova_cookie)
> >>  		return -EEXIST;
> >>
> >> -	iovad = kzalloc(sizeof(*iovad), GFP_KERNEL);
> >> -	domain->iova_cookie = iovad;
> >> +	cookie = kzalloc(sizeof(*cookie), GFP_KERNEL);
> >> +	if (!cookie)
> >> +		return -ENOMEM;
> >>
> >> -	return iovad ? 0 : -ENOMEM;
> >> +	spin_lock_init(&cookie->msi_lock);
> >> +	INIT_LIST_HEAD(&cookie->msi_page_list);
> >> +	domain->iova_cookie = cookie;
> >> +	return 0;
> >>  }
> >>  EXPORT_SYMBOL(iommu_get_dma_cookie);
> >>
> >> @@ -63,14 +85,20 @@ EXPORT_SYMBOL(iommu_get_dma_cookie);
> >>   */
> >>  void iommu_put_dma_cookie(struct iommu_domain *domain)  {
> >> -	struct iova_domain *iovad = domain->iova_cookie;
> >> +	struct iommu_dma_cookie *cookie = domain->iova_cookie;
> >> +	struct iommu_dma_msi_page *msi, *tmp;
> >>
> >> -	if (!iovad)
> >> +	if (!cookie)
> >>  		return;
> >>
> >> -	if (iovad->granule)
> >> -		put_iova_domain(iovad);
> >> -	kfree(iovad);
> >> +	if (cookie->iovad.granule)
> >> +		put_iova_domain(&cookie->iovad);
> >> +
> >> +	list_for_each_entry_safe(msi, tmp, &cookie->msi_page_list, list) {
> >> +		list_del(&msi->list);
> >> +		kfree(msi);
> >> +	}
> >> +	kfree(cookie);
> >>  	domain->iova_cookie = NULL;
> >>  }
> >>  EXPORT_SYMBOL(iommu_put_dma_cookie);
> >> @@ -88,7 +116,7 @@ EXPORT_SYMBOL(iommu_put_dma_cookie);
> >>   */
> >>  int iommu_dma_init_domain(struct iommu_domain *domain, dma_addr_t
> >> base, u64 size)  {
> >> -	struct iova_domain *iovad = domain->iova_cookie;
> >> +	struct iova_domain *iovad = cookie_iovad(domain);
> >>  	unsigned long order, base_pfn, end_pfn;
> >>
> >>  	if (!iovad)
> >> @@ -155,7 +183,7 @@ int dma_direction_to_prot(enum
> dma_data_direction
> >> dir, bool coherent)  static struct iova *__alloc_iova(struct
> >> iommu_domain *domain, size_t size,
> >>  		dma_addr_t dma_limit)
> >>  {
> >> -	struct iova_domain *iovad = domain->iova_cookie;
> >> +	struct iova_domain *iovad = cookie_iovad(domain);
> >>  	unsigned long shift = iova_shift(iovad);
> >>  	unsigned long length = iova_align(iovad, size) >> shift;
> >>
> >> @@ -171,7 +199,7 @@ static struct iova *__alloc_iova(struct
> >> iommu_domain *domain, size_t size,
> >>  /* The IOVA allocator knows what we mapped, so just unmap whatever
> >> that was */  static void __iommu_dma_unmap(struct iommu_domain
> >> *domain, dma_addr_t dma_addr)  {
> >> -	struct iova_domain *iovad = domain->iova_cookie;
> >> +	struct iova_domain *iovad = cookie_iovad(domain);
> >>  	unsigned long shift = iova_shift(iovad);
> >>  	unsigned long pfn = dma_addr >> shift;
> >>  	struct iova *iova = find_iova(iovad, pfn); @@ -294,7 +322,7 @@
> >> struct page **iommu_dma_alloc(struct device *dev, size_t size, gfp_t gfp,
> >>  		void (*flush_page)(struct device *, const void *, phys_addr_t))  {
> >>  	struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
> >> -	struct iova_domain *iovad = domain->iova_cookie;
> >> +	struct iova_domain *iovad = cookie_iovad(domain);
> >>  	struct iova *iova;
> >>  	struct page **pages;
> >>  	struct sg_table sgt;
> >> @@ -386,7 +414,7 @@ dma_addr_t iommu_dma_map_page(struct device
> *dev,
> >> struct page *page,  {
> >>  	dma_addr_t dma_addr;
> >>  	struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
> >> -	struct iova_domain *iovad = domain->iova_cookie;
> >> +	struct iova_domain *iovad = cookie_iovad(domain);
> >>  	phys_addr_t phys = page_to_phys(page) + offset;
> >>  	size_t iova_off = iova_offset(iovad, phys);
> >>  	size_t len = iova_align(iovad, size + iova_off); @@ -495,7 +523,7
> >> @@ int iommu_dma_map_sg(struct device *dev, struct scatterlist *sg,
> >>  		int nents, int prot)
> >>  {
> >>  	struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
> >> -	struct iova_domain *iovad = domain->iova_cookie;
> >> +	struct iova_domain *iovad = cookie_iovad(domain);
> >>  	struct iova *iova;
> >>  	struct scatterlist *s, *prev = NULL;
> >>  	dma_addr_t dma_addr;
> >> @@ -587,3 +615,81 @@ int iommu_dma_mapping_error(struct device *dev,
> >> dma_addr_t dma_addr)  {
> >>  	return dma_addr == DMA_ERROR_CODE;
> >>  }
> >> +
> >> +static struct iommu_dma_msi_page *iommu_dma_get_msi_page(struct
> >> +device
> >> *dev,
> >> +		phys_addr_t msi_addr, struct iommu_domain *domain) {
> >> +	struct iommu_dma_cookie *cookie = domain->iova_cookie;
> >> +	struct iommu_dma_msi_page *msi_page;
> >> +	struct iova_domain *iovad = &cookie->iovad;
> >> +	struct iova *iova;
> >> +	int prot = IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_MMIO;
> >> +
> >> +	msi_addr &= ~(phys_addr_t)iova_mask(iovad);
> >> +	list_for_each_entry(msi_page, &cookie->msi_page_list, list)
> >> +		if (msi_page->phys == msi_addr)
> >> +			return msi_page;
> >> +
> >> +	msi_page = kzalloc(sizeof(*msi_page), GFP_ATOMIC);
> >> +	if (!msi_page)
> >> +		return NULL;
> >> +
> >> +	iova = __alloc_iova(domain, iovad->granule, dma_get_mask(dev));
> >
> > I think this should be 'iova = __alloc_iova(domain, iovad->granule,
> dma_get_mask(dev));'
> 
> Er, yes... I fully agree. That's why it is exactly that.
> 
> > as __alloc_iova takes input parameter as 'struct iova_domain *'
> 
> Joking aside, though, I guess you've overlooked the change introduced by
> c987ff0d3cb3 ("iommu/dma: Respect IOMMU aperture when allocating")?

Ooops!! My bad. That's right, I missed out this change.

Thanks,
Nipun

> 
> Robin.
> 
> >
> > Regards,
> > Nipun
> >
> >> +	if (!iova)
> >> +		goto out_free_page;
> >> +
> >> +	msi_page->phys = msi_addr;
> >> +	msi_page->iova = iova_dma_addr(iovad, iova);
> >> +	if (iommu_map(domain, msi_page->iova, msi_addr, iovad->granule,
> >> prot))
> >> +		goto out_free_iova;
> >> +
> >> +	INIT_LIST_HEAD(&msi_page->list);
> >> +	list_add(&msi_page->list, &cookie->msi_page_list);
> >> +	return msi_page;
> >> +
> >> +out_free_iova:
> >> +	__free_iova(iovad, iova);
> >> +out_free_page:
> >> +	kfree(msi_page);
> >> +	return NULL;
> >> +}
> >> +
> >> +void iommu_dma_map_msi_msg(int irq, struct msi_msg *msg) {
> >> +	struct device *dev = msi_desc_to_dev(irq_get_msi_desc(irq));
> >> +	struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
> >> +	struct iommu_dma_cookie *cookie;
> >> +	struct iommu_dma_msi_page *msi_page;
> >> +	phys_addr_t msi_addr = (u64)msg->address_hi << 32 | msg->address_lo;
> >> +	unsigned long flags;
> >> +
> >> +	if (!domain || !domain->iova_cookie)
> >> +		return;
> >> +
> >> +	cookie = domain->iova_cookie;
> >> +
> >> +	/*
> >> +	 * We disable IRQs to rule out a possible inversion against
> >> +	 * irq_desc_lock if, say, someone tries to retarget the affinity
> >> +	 * of an MSI from within an IPI handler.
> >> +	 */
> >> +	spin_lock_irqsave(&cookie->msi_lock, flags);
> >> +	msi_page = iommu_dma_get_msi_page(dev, msi_addr, domain);
> >> +	spin_unlock_irqrestore(&cookie->msi_lock, flags);
> >> +
> >> +	if (WARN_ON(!msi_page)) {
> >> +		/*
> >> +		 * We're called from a void callback, so the best we can do is
> >> +		 * 'fail' by filling the message with obviously bogus values.
> >> +		 * Since we got this far due to an IOMMU being present, it's
> >> +		 * not like the existing address would have worked anyway...
> >> +		 */
> >> +		msg->address_hi = ~0U;
> >> +		msg->address_lo = ~0U;
> >> +		msg->data = ~0U;
> >> +	} else {
> >> +		msg->address_hi = upper_32_bits(msi_page->iova);
> >> +		msg->address_lo &= iova_mask(&cookie->iovad);
> >> +		msg->address_lo += lower_32_bits(msi_page->iova);
> >> +	}
> >> +}
> >> diff --git a/drivers/irqchip/irq-gic-v2m.c
> >> b/drivers/irqchip/irq-gic-v2m.c index 35eb7ac5d21f..863e073c6f7f
> >> 100644
> >> --- a/drivers/irqchip/irq-gic-v2m.c
> >> +++ b/drivers/irqchip/irq-gic-v2m.c
> >> @@ -16,6 +16,7 @@
> >>  #define pr_fmt(fmt) "GICv2m: " fmt
> >>
> >>  #include <linux/acpi.h>
> >> +#include <linux/dma-iommu.h>
> >>  #include <linux/irq.h>
> >>  #include <linux/irqdomain.h>
> >>  #include <linux/kernel.h>
> >> @@ -108,6 +109,8 @@ static void gicv2m_compose_msi_msg(struct
> >> irq_data *data, struct msi_msg *msg)
> >>
> >>  	if (v2m->flags & GICV2M_NEEDS_SPI_OFFSET)
> >>  		msg->data -= v2m->spi_offset;
> >> +
> >> +	iommu_dma_map_msi_msg(data->irq, msg);
> >>  }
> >>
> >>  static struct irq_chip gicv2m_irq_chip = { diff --git
> >> a/drivers/irqchip/irq-gic-v3- its.c
> >> b/drivers/irqchip/irq-gic-v3-its.c
> >> index 36b9c28a5c91..98ff669d5962 100644
> >> --- a/drivers/irqchip/irq-gic-v3-its.c
> >> +++ b/drivers/irqchip/irq-gic-v3-its.c
> >> @@ -18,6 +18,7 @@
> >>  #include <linux/bitmap.h>
> >>  #include <linux/cpu.h>
> >>  #include <linux/delay.h>
> >> +#include <linux/dma-iommu.h>
> >>  #include <linux/interrupt.h>
> >>  #include <linux/log2.h>
> >>  #include <linux/mm.h>
> >> @@ -655,6 +656,8 @@ static void its_irq_compose_msi_msg(struct
> >> irq_data *d, struct msi_msg *msg)
> >>  	msg->address_lo		= addr & ((1UL << 32) - 1);
> >>  	msg->address_hi		= addr >> 32;
> >>  	msg->data		= its_get_event_id(d);
> >> +
> >> +	iommu_dma_map_msi_msg(d->irq, msg);
> >>  }
> >>
> >>  static struct irq_chip its_irq_chip = { diff --git
> >> a/include/linux/dma-iommu.h b/include/linux/dma-iommu.h index
> >> 81c5c8d167ad..5ee806e41b5c 100644
> >> --- a/include/linux/dma-iommu.h
> >> +++ b/include/linux/dma-iommu.h
> >> @@ -21,6 +21,7 @@
> >>
> >>  #ifdef CONFIG_IOMMU_DMA
> >>  #include <linux/iommu.h>
> >> +#include <linux/msi.h>
> >>
> >>  int iommu_dma_init(void);
> >>
> >> @@ -62,9 +63,13 @@ void iommu_dma_unmap_sg(struct device *dev,
> struct
> >> scatterlist *sg, int nents,  int iommu_dma_supported(struct device
> >> *dev, u64 mask);  int iommu_dma_mapping_error(struct device *dev,
> >> dma_addr_t dma_addr);
> >>
> >> +/* The DMA API isn't _quite_ the whole story, though... */ void
> >> +iommu_dma_map_msi_msg(int irq, struct msi_msg *msg);
> >> +
> >>  #else
> >>
> >>  struct iommu_domain;
> >> +struct msi_msg;
> >>
> >>  static inline int iommu_dma_init(void)  { @@ -80,6 +85,10 @@ static
> >> inline void iommu_put_dma_cookie(struct iommu_domain *domain)  {  }
> >>
> >> +static inline void iommu_dma_map_msi_msg(int irq, struct msi_msg
> >> +*msg) { }
> >> +
> >>  #endif	/* CONFIG_IOMMU_DMA */
> >>  #endif	/* __KERNEL__ */
> >>  #endif	/* __DMA_IOMMU_H */
> >> --
> >> 2.8.1.dirty
> >>
> >> _______________________________________________
> >> iommu mailing list
> >> iommu at lists.linux-foundation.org
> >> https://lists.linuxfoundation.org/mailman/listinfo/iommu
> >

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

end of thread, other threads:[~2016-10-05 11:31 UTC | newest]

Thread overview: 104+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-09-12 16:13 [PATCH v7 00/22] Generic DT bindings for PCI IOMMUs and ARM SMMU Robin Murphy
2016-09-12 16:13 ` Robin Murphy
     [not found] ` <cover.1473695704.git.robin.murphy-5wv7dgnIgG8@public.gmane.org>
2016-09-12 16:13   ` [PATCH v7 01/22] Docs: dt: add PCI IOMMU map bindings Robin Murphy
2016-09-12 16:13     ` Robin Murphy
2016-09-12 16:13   ` [PATCH v7 02/22] of/irq: Break out msi-map lookup (again) Robin Murphy
2016-09-12 16:13     ` Robin Murphy
2016-09-12 16:13   ` [PATCH v7 03/22] iommu/of: Handle iommu-map property for PCI Robin Murphy
2016-09-12 16:13     ` Robin Murphy
2016-09-12 16:13   ` [PATCH v7 04/22] iommu: Introduce iommu_fwspec Robin Murphy
2016-09-12 16:13     ` Robin Murphy
     [not found]     ` <742a71630de502ac5a7a8641c6ed368d8409324d.1473695704.git.robin.murphy-5wv7dgnIgG8@public.gmane.org>
2016-09-13  9:54       ` [PATCH v7.1 " Robin Murphy
2016-09-13  9:54         ` Robin Murphy
2016-09-12 16:13   ` [PATCH v7 05/22] Docs: dt: document ARM SMMUv3 generic binding usage Robin Murphy
2016-09-12 16:13     ` Robin Murphy
     [not found]     ` <2273645f1fa5c76b6b98b5fd03804ab8b55a7691.1473695704.git.robin.murphy-5wv7dgnIgG8@public.gmane.org>
2016-09-20 14:46       ` Rob Herring
2016-09-20 14:46         ` Rob Herring
2016-09-12 16:13   ` [PATCH v7 06/22] iommu/arm-smmu: Fall back to global bypass Robin Murphy
2016-09-12 16:13     ` Robin Murphy
2016-09-12 16:13   ` [PATCH v7 07/22] iommu/arm-smmu: Implement of_xlate() for SMMUv3 Robin Murphy
2016-09-12 16:13     ` Robin Murphy
2016-09-12 16:13   ` [PATCH v7 08/22] iommu/arm-smmu: Support non-PCI devices with SMMUv3 Robin Murphy
2016-09-12 16:13     ` Robin Murphy
2016-09-12 16:13   ` [PATCH v7 09/22] iommu/arm-smmu: Set PRIVCFG in stage 1 STEs Robin Murphy
2016-09-12 16:13     ` Robin Murphy
2016-09-12 16:13   ` [PATCH v7 10/22] iommu/arm-smmu: Handle stream IDs more dynamically Robin Murphy
2016-09-12 16:13     ` Robin Murphy
2016-09-12 16:13   ` [PATCH v7 11/22] iommu/arm-smmu: Consolidate stream map entry state Robin Murphy
2016-09-12 16:13     ` Robin Murphy
2016-09-12 16:13   ` [PATCH v7 12/22] iommu/arm-smmu: Keep track of S2CR state Robin Murphy
2016-09-12 16:13     ` Robin Murphy
2016-09-12 16:13   ` [PATCH v7 13/22] iommu/arm-smmu: Refactor mmu-masters handling Robin Murphy
2016-09-12 16:13     ` Robin Murphy
     [not found]     ` <046d2d21f988d6ece916fc45b0af0804a7f200f2.1473695704.git.robin.murphy-5wv7dgnIgG8@public.gmane.org>
2016-09-14 14:21       ` [PATCH v7.1 " Robin Murphy
2016-09-14 14:21         ` Robin Murphy
2016-09-12 16:13   ` [PATCH v7 14/22] iommu/arm-smmu: Streamline SMMU data lookups Robin Murphy
2016-09-12 16:13     ` Robin Murphy
2016-09-12 16:13   ` [PATCH v7 15/22] iommu/arm-smmu: Add a stream map entry iterator Robin Murphy
2016-09-12 16:13     ` Robin Murphy
2016-09-12 16:13   ` [PATCH v7 16/22] iommu/arm-smmu: Intelligent SMR allocation Robin Murphy
2016-09-12 16:13     ` Robin Murphy
2016-09-12 16:13   ` [PATCH v7 17/22] iommu/arm-smmu: Convert to iommu_fwspec Robin Murphy
2016-09-12 16:13     ` Robin Murphy
2016-09-12 16:13   ` [PATCH v7 18/22] Docs: dt: document ARM SMMU generic binding usage Robin Murphy
2016-09-12 16:13     ` Robin Murphy
2016-09-12 16:13   ` [PATCH v7 19/22] iommu/arm-smmu: Wire up generic configuration support Robin Murphy
2016-09-12 16:13     ` Robin Murphy
     [not found]     ` <228dc6c675f10ae7481640d4ef2f4960c170621f.1473695704.git.robin.murphy-5wv7dgnIgG8@public.gmane.org>
2016-09-14 14:26       ` [PATCH v7.1 " Robin Murphy
2016-09-14 14:26         ` Robin Murphy
2016-09-12 16:13   ` [PATCH v7 20/22] iommu/arm-smmu: Set domain geometry Robin Murphy
2016-09-12 16:13     ` Robin Murphy
2016-09-12 16:13   ` [PATCH v7 21/22] iommu/dma: Add support for mapping MSIs Robin Murphy
2016-09-12 16:13     ` Robin Murphy
     [not found]     ` <2273af20d844bd618c6a90b57e639700328ebf7f.1473695704.git.robin.murphy-5wv7dgnIgG8@public.gmane.org>
2016-10-05  7:00       ` Nipun Gupta
2016-10-05  7:00         ` Nipun Gupta
     [not found]         ` <DB6PR0402MB2694B2E5AE266F138784D2C2E6C40-2mNvjAGDOPn2WJ5A9zev/o3W/0Ik+aLCnBOFsp37pqbUKgpGm//BTAC/G2K4zDHf@public.gmane.org>
2016-10-05  9:55           ` Robin Murphy
2016-10-05  9:55             ` Robin Murphy
     [not found]             ` <6ec9519b-01df-3be8-2967-7556bd306909-5wv7dgnIgG8@public.gmane.org>
2016-10-05 11:31               ` Nipun Gupta
2016-10-05 11:31                 ` Nipun Gupta
2016-09-12 16:14   ` [PATCH v7 22/22] iommu/dma: Avoid PCI host bridge windows Robin Murphy
2016-09-12 16:14     ` Robin Murphy
     [not found]     ` <5f7bfee298f98d29a35933d3e0252d32b83d62b8.1473695704.git.robin.murphy-5wv7dgnIgG8@public.gmane.org>
2016-09-14 10:55       ` Marek Szyprowski
2016-09-14 10:55         ` Marek Szyprowski
     [not found]         ` <ab8693f6-20d6-2a95-9f1f-0607e72bc012-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
2016-09-14 11:10           ` Robin Murphy
2016-09-14 11:10             ` Robin Murphy
     [not found]             ` <49c51c4f-cb00-445d-b8f8-b632babf2b3e-5wv7dgnIgG8@public.gmane.org>
2016-09-14 12:35               ` Marek Szyprowski
2016-09-14 12:35                 ` Marek Szyprowski
     [not found]                 ` <dc9f945f-2756-ab70-d061-9fdc7c5afdee-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
2016-09-14 13:25                   ` Robin Murphy
2016-09-14 13:25                     ` Robin Murphy
     [not found]                     ` <bbdc42fa-ea35-945f-3e2a-e0ab03fc997d-5wv7dgnIgG8@public.gmane.org>
2016-09-15  7:08                       ` Marek Szyprowski
2016-09-15  7:08                         ` Marek Szyprowski
2016-09-13 12:14   ` [PATCH v7 00/22] Generic DT bindings for PCI IOMMUs and ARM SMMU Auger Eric
2016-09-13 12:14     ` Auger Eric
     [not found]     ` <92f27a6b-9752-516d-3924-c552fc6a5ace-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
2016-09-13 12:40       ` Robin Murphy
2016-09-13 12:40         ` Robin Murphy
     [not found]         ` <e24821be-5cc4-52b3-f961-1eb32cf58293-5wv7dgnIgG8@public.gmane.org>
2016-09-13 12:57           ` Auger Eric
2016-09-13 12:57             ` Auger Eric
2016-09-14  8:41   ` Auger Eric
2016-09-14  8:41     ` Auger Eric
     [not found]     ` <11ebd81e-2ea5-5ff3-35b3-be95f03e05bd-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
2016-09-14  9:20       ` Will Deacon
2016-09-14  9:20         ` Will Deacon
     [not found]         ` <20160914092051.GB19622-5wv7dgnIgG8@public.gmane.org>
2016-09-14  9:35           ` Auger Eric
2016-09-14  9:35             ` Auger Eric
2016-09-14 10:35       ` Robin Murphy
2016-09-14 10:35         ` Robin Murphy
     [not found]         ` <d03ea5e7-59f1-8b49-4ba2-d05fc2030ebc-5wv7dgnIgG8@public.gmane.org>
2016-09-14 12:32           ` Auger Eric
2016-09-14 12:32             ` Auger Eric
     [not found]             ` <04a0a682-4fdc-8d62-57cd-efdf730582c6-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
2016-09-14 12:53               ` Robin Murphy
2016-09-14 12:53                 ` Robin Murphy
     [not found]                 ` <c2645c5e-edd3-2b31-4311-0ca621a915e2-5wv7dgnIgG8@public.gmane.org>
2016-09-15  9:29                   ` Auger Eric
2016-09-15  9:29                     ` Auger Eric
     [not found]                     ` <4d87d5f2-0350-b5f8-ffc3-4e9377cf1f87-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
2016-09-15 10:15                       ` Robin Murphy
2016-09-15 10:15                         ` Robin Murphy
     [not found]                         ` <fc4ce398-4eeb-f2ca-b964-e9f466be79c4-5wv7dgnIgG8@public.gmane.org>
2016-09-15 16:46                           ` Auger Eric
2016-09-15 16:46                             ` Auger Eric
     [not found]                             ` <1838c65d-5944-8946-781c-b420bea1acab-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
2016-09-16 16:18                               ` Robin Murphy
2016-09-16 16:18                                 ` Robin Murphy
     [not found]                                 ` <f16db032-1905-9804-0607-fe007af72b0e-5wv7dgnIgG8@public.gmane.org>
2016-09-19 12:13                                   ` Auger Eric
2016-09-19 12:13                                     ` Auger Eric
     [not found]                                     ` <48f3bc10-3966-7d50-d070-7ec7f0946c92-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
2016-09-19 12:24                                       ` Will Deacon
2016-09-19 12:24                                         ` Will Deacon
     [not found]                                         ` <20160919122435.GD9005-5wv7dgnIgG8@public.gmane.org>
2016-09-19 12:41                                           ` Robin Murphy
2016-09-19 12:41                                             ` Robin Murphy
     [not found]                                             ` <99ee0946-c7ff-e6e4-08c1-ff686ea1a8a5-5wv7dgnIgG8@public.gmane.org>
2016-09-19 14:17                                               ` Will Deacon
2016-09-19 14:17                                                 ` Will Deacon

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.