Linux-Samsung-soc Archive mirror
 help / color / mirror / Atom feed
* [PATCH 00/30] Make a new API for drivers to use to get their FW
@ 2023-11-30  1:10 Jason Gunthorpe
  2023-11-30  1:10 ` [PATCH 01/30] iommu/of: Make a of_iommu_for_each_id() Jason Gunthorpe
                   ` (29 more replies)
  0 siblings, 30 replies; 34+ messages in thread
From: Jason Gunthorpe @ 2023-11-30  1:10 UTC (permalink / raw)
  To: acpica-devel, Andy Gross, Alim Akhtar, Alyssa Rosenzweig,
	Bjorn Andersson, AngeloGioacchino Del Regno, asahi, Baolin Wang,
	devicetree, Frank Rowand, Hanjun Guo, Gustavo A. R. Silva,
	Heiko Stuebner, iommu, Jean-Philippe Brucker, Jernej Skrabec,
	Jonathan Hunter, Joerg Roedel, Kees Cook, Konrad Dybcio,
	Krzysztof Kozlowski, Len Brown, linux-acpi, linux-arm-kernel,
	linux-arm-msm, linux-hardening, linux-mediatek, linux-rockchip,
	linux-samsung-soc, linux-sunxi, linux-tegra, Lorenzo Pieralisi,
	Marek Szyprowski, Hector Martin, Matthias Brugger, Orson Zhai,
	Rafael J. Wysocki, Rob Clark, Robert Moore, Rob Herring,
	Robin Murphy, Samuel Holland, Sudeep Holla, Sven Peter,
	Thierry Reding, Krishna Reddy, virtualization, Chen-Yu Tsai,
	Will Deacon, Yong Wu, Chunyan Zhang
  Cc: André Draszik, patches

This series improves the way drivers get their FW data. It introduces a
series of driver facing APIs and converts every driver to use them and
replaces fwspec with this mechanism.

The new API allows drivers to do less stuff in alot of cases, and permits
all drivers to sequence their operations in a logical way with correct
error handling. It doesn't force the idea of pre or post parse on the
driver, though I choose to implement post-parse here.

Aside from the API change this takes on a few other goals:

 1) New API for drivers to simplify drivers and make them more robust
 2) Remove all FW parsing from _dma_configure()
 3) Make the global lock back into a static
 4) Organize things to reduce the probe time memory allocations from
     fw_spec, N*realloc, dev_iommu, device-private
    To just allocating device-private. The last step to merge dev_iommu
    and device-private would be a follow on couple of patches.
 5) Get everything setup to allow all devices to probe during bus scan,
    not during _dma_configure.
 6) Better layering between the ACPI and IOMMU code, less
    iommu related things in the ACPI directory.

The drivers break down into two broad approaches for accessing FW in a
driver's probe_device() function. AMD, Intel, mtk_v1, omap, S390,
smmu-legacy and tegra can "self probe" using code in their probe_device()
functions to parse their associated FW descriptions.

The rest (DART, smmu-modern, qcom, smmu-v3, exynos, ipmmu, msm, mtk,
rockchip, sprd, sun50i, virtio) use the iommu_fwspec system and rely on
the core code to do the FW parsing for them before invoking
probe_device().

To understand the philosophy of this design it is worth to take a moment
and review the 2009 LWN article on Kernel Design
patterns (https://lwn.net/Articles/336262/) exploring the "midlayer
mistake". While I do not entirely agree that midlayers are unconditionally
bad, the discussion does nicely frame the architectural choice I've made
in this series.

Organize the FW parsing so the drivers have an toolbox style API to
extract the information they require from the FW. Create a set of APIs for
the drivers to call that do exactly what the current set of drivers need
in an efficient and understandable way. The API is designed to
significantly clean and simplify the drivers.

The API transforms the iommu_fwspec from a long-term allocated struct into
a short-term on-stack struct iommu_probe_info. Using a short-term
structure means we no longer have to be memory efficient and can store
temporary details about the ongoing parsing in the structure which is the
key to allowing the new API.

Further, philosophically, the API changes to make the iommu driver
responsible to provide per-device storage for any parsed FW data. It is
stored in whatever format the driver finds appropriate. The API provides
concise helpers to make this easy for the standard 'u32 id[]' cases. The
unusual cases were already using unique per-driver storage and continue to
do so in a cleaner, less convoluted way.

The design allows the API to be implemented either as a 'parse on demand'
or 'parse and cache in advance' (more like fwspec) approach. I've
implemented the 'parse on demand' version here, but I'm not fixed on
it. Let's discuss.

The choice is deliberately not baked into the driver facing API,
iommu_probe_info combined with the generic *_iommu_for_each_id() lower
layer provides a lot of flexibility to do whatever organization we want.

The specific commit messages provide details, but the drivers break down
into three basic idiomatic sequences for their probe_device with the new
API.

Single iommu with no ID list:
  iommu_driver = iommu_of_get_single_iommu(pinf, ops, num_cells,
                                iommu_driver_struct, iommu_member);
  per_driver = kzalloc(..)
  per_driver->iommu = iommu_driver;
  dev_iommu_priv_set(dev, per_driver);
  return &iommu_driver->iommu_member;

Single iommu with a simple u32 ID list:
  iommu_driver = iommu_of_get_single_iommu(pinf, ops, num_cells,
                                iommu_driver_struct, iommu_member);
  per_driver = iommu_fw_alloc_per_device_ids(pinf, per_driver);
  per_driver->iommu = iommu_driver;
  dev_iommu_priv_set(dev, per_driver);
  return &iommu_driver->iommu_member;

The iommu_fw_alloc_per_device_ids() helper allocates a correctly sized
per-driver struct and places a u32 ID list into the flex array
per_driver->ids[]. This removes the need for a fw_spec allocation.

Complex multi-iommu or complex ID list:
  static int driver_of_xlate(struct iommu_device *iommu,
			 struct of_phandle_args *args, void *priv);

  per_driver = kzalloc(...);
  iommu_of_xlate(pinf, &ops, num_cells, &driver_of_xlate, per_driver);

  dev_iommu_priv_set(dev, per_driver);
  return &per_driver->iommu; // The first iommu_device parsed

The driver will process the given (iommu, args) tuples and store them into
the per_driver struct in its own way (eg DART encodes things into a 2D
array). Allocating the per_driver struct before running the parse cleans
all the tortured logic.

The VIOT and IORT ACPI parsers provide the same API. Drivers supporting
ACPI will call the ACPI helper (ie iommu_iort_get_single_iommu()) which
understands how to parse OF if no ACPI is present. Since the SMMU drivers
directly invoke the IORT parser they also get back the extra IORT ACPI
data in a "struct iort_params", which eliminates the need to create SW
properties or store SMMU only stuff in the fwspec.

The bulk of the series is converting each driver to this toolbox API. The
design of the toolbox functions are lightweight from a driver perspective
and generally replace existing steps that the drivers already had to do. A
significant amount of inefficient boiler plate is removed from all drivers
related to how the driver obtains the iommu_device pointers.

The implementation of the three flavours of FW parsers (OF/IORT/VIOT) are
all structured the same. The lower FW level provides
a *_iommu_for_each_id() function which parses and iterates over the actual
FW table and calls a function pointer for each (instance, id). The iommu
layer then uses this for_each primitive to do the IOMMU specific stuff and
create the above APIs.

To allow the iommu_of_get_single_iommu()/iommu_fw_alloc_per_device_ids()
split API the get_single will count all of the IDs and then alloc will
size the flex array. The iommu_probe_info has scratch space for some ids
which can then be memcpy'd without a reparse. Reparse is kept as a backup
so we can handle arbitary complexity without burdening the typical fast
path.

The removal of all FW parsing from _dma_configure leaves only the
initialization of the iommu_probe_info behind. My plan is to add a new bus
op 'init iommu probe info' that will do this step and then we can fully
execute probe outside the *_dma_configure() context.

The big win here is the extensive cleaning of the driver's probe
paths. There is alot of "creative" stuff there, this cleans almost all of
it away.

This is on github: https://github.com/jgunthorpe/linux/commits/iommu_fwspec

Cc: Hector Martin <marcan@marcan.st>
Cc: André Draszik <andre.draszik@linaro.org>
Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>

Jason Gunthorpe (30):
  iommu/of: Make a of_iommu_for_each_id()
  ACPI: VIOT: Make a viot_iommu_for_each_id()
  ACPI: IORT: Make a iort_iommu_for_each_id()
  ACPI: IORT: Remove fwspec from the reserved region code
  iommu: Add iommu_probe_info
  iommu: Make iommu_ops_from_fwnode() return the iommu_device
  iommu/of: Call of_iommu_get_resv_regions() directly
  iommu/of: Add iommu_of_get_single_iommu()
  iommu/rockchip: Move to iommu_of_get_single_iommu()
  iommu/sprd: Move to iommu_of_get_single_iommu()
  iommu/sun50i: Move to iommu_of_get_single_iommu()
  iommu/of: Add iommu_of_xlate()
  iommu/dart: Move to iommu_of_xlate()
  iommu/exynos: Move to iommu_of_xlate()
  iommu/msm: Move to iommu_of_xlate()
  iommu/tegra: Route tegra_dev_iommu_get_stream_id() through an op
  iommu: Add iommu_fw_alloc_per_device_ids()
  iommu/tegra: Move to iommu_fw_alloc_per_device_ids()
  iommu/mtk: Move to iommu_fw_alloc_per_device_ids()
  iommu/ipmmu-vmsa: Move to iommu_fw_alloc_per_device_ids()
  iommu/mtk_v1: Move to iommu_fw_alloc_per_device_ids()
  iommu/qcom: Move to iommu_fw_alloc_per_device_ids()
  iommu/viot: Add iommu_viot_get_single_iommu()
  iommu/virtio: Move to iommu_fw_alloc_per_device_ids()
  iommu/iort: Add iommu_iort_get_single_iommu()
  iommu/arm-smmu-v3: Move to iommu_fw_alloc_per_device_ids()
  iommu/arm-smmu: Move to iommu_of_xlate()
  iommu: Call all drivers if there is no fwspec
  iommu: Check for EPROBE_DEFER using the new FW parsers
  iommu: Remove fwspec and related

 drivers/acpi/arm64/iort.c                   | 233 +++++++---------
 drivers/acpi/scan.c                         |  58 +---
 drivers/acpi/viot.c                         |  67 ++---
 drivers/iommu/Makefile                      |   2 +
 drivers/iommu/apple-dart.c                  |  62 +++--
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c |  73 ++---
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h |   4 +
 drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c  |   6 +-
 drivers/iommu/arm/arm-smmu/arm-smmu.c       | 209 +++++++-------
 drivers/iommu/arm/arm-smmu/arm-smmu.h       |  13 +-
 drivers/iommu/arm/arm-smmu/qcom_iommu.c     | 160 +++++------
 drivers/iommu/dma-iommu.c                   |  22 --
 drivers/iommu/dma-iommu.h                   |   6 -
 drivers/iommu/exynos-iommu.c                |  78 +++---
 drivers/iommu/iommu.c                       | 271 ++++++++++++------
 drivers/iommu/iort_iommu.c                  |  99 +++++++
 drivers/iommu/ipmmu-vmsa.c                  |  95 +++----
 drivers/iommu/msm_iommu.c                   |  92 +++----
 drivers/iommu/mtk_iommu.c                   | 115 ++++----
 drivers/iommu/mtk_iommu_v1.c                | 162 +++++------
 drivers/iommu/of_iommu.c                    | 288 ++++++++++++++------
 drivers/iommu/rockchip-iommu.c              |  73 ++---
 drivers/iommu/sprd-iommu.c                  |  31 +--
 drivers/iommu/sun50i-iommu.c                |  59 ++--
 drivers/iommu/tegra-smmu.c                  | 158 ++++-------
 drivers/iommu/viot_iommu.c                  |  71 +++++
 drivers/iommu/virtio-iommu.c                |  71 ++---
 include/acpi/acpi_bus.h                     |   3 -
 include/linux/acpi_iort.h                   |  24 +-
 include/linux/acpi_viot.h                   |  16 +-
 include/linux/iommu-driver.h                | 250 +++++++++++++++++
 include/linux/iommu.h                       | 101 +------
 include/linux/of_iommu.h                    |   8 -
 33 files changed, 1649 insertions(+), 1331 deletions(-)
 create mode 100644 drivers/iommu/iort_iommu.c
 create mode 100644 drivers/iommu/viot_iommu.c
 create mode 100644 include/linux/iommu-driver.h


base-commit: 68ec454bc1514f557686b5895dd9719e18d31705
-- 
2.42.0


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

* [PATCH 01/30] iommu/of: Make a of_iommu_for_each_id()
  2023-11-30  1:10 [PATCH 00/30] Make a new API for drivers to use to get their FW Jason Gunthorpe
@ 2023-11-30  1:10 ` Jason Gunthorpe
  2023-11-30  1:10 ` [PATCH 02/30] ACPI: VIOT: Make a viot_iommu_for_each_id() Jason Gunthorpe
                   ` (28 subsequent siblings)
  29 siblings, 0 replies; 34+ messages in thread
From: Jason Gunthorpe @ 2023-11-30  1:10 UTC (permalink / raw)
  To: acpica-devel, Andy Gross, Alim Akhtar, Alyssa Rosenzweig,
	Bjorn Andersson, AngeloGioacchino Del Regno, asahi, Baolin Wang,
	devicetree, Frank Rowand, Hanjun Guo, Gustavo A. R. Silva,
	Heiko Stuebner, iommu, Jean-Philippe Brucker, Jernej Skrabec,
	Jonathan Hunter, Joerg Roedel, Kees Cook, Konrad Dybcio,
	Krzysztof Kozlowski, Len Brown, linux-acpi, linux-arm-kernel,
	linux-arm-msm, linux-hardening, linux-mediatek, linux-rockchip,
	linux-samsung-soc, linux-sunxi, linux-tegra, Lorenzo Pieralisi,
	Marek Szyprowski, Hector Martin, Matthias Brugger, Orson Zhai,
	Rafael J. Wysocki, Rob Clark, Robert Moore, Rob Herring,
	Robin Murphy, Samuel Holland, Sudeep Holla, Sven Peter,
	Thierry Reding, Krishna Reddy, virtualization, Chen-Yu Tsai,
	Will Deacon, Yong Wu, Chunyan Zhang
  Cc: André Draszik, patches

Take the existing machinery that was wired to invoking of_xlate on each id
through the various maps and aliases and allow it to call a function
pointer with an opaque.

Call of_iommu_xlate() using the existing of_iommu_for_each_id().

Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
---
 drivers/iommu/of_iommu.c | 82 ++++++++++++++++++++++------------------
 1 file changed, 46 insertions(+), 36 deletions(-)

diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c
index 164317bfb8a81f..3d4580f1fbb378 100644
--- a/drivers/iommu/of_iommu.c
+++ b/drivers/iommu/of_iommu.c
@@ -17,9 +17,9 @@
 #include <linux/slab.h>
 #include <linux/fsl/mc.h>
 
-static int of_iommu_xlate(struct device *dev,
-			  struct of_phandle_args *iommu_spec)
+static int of_iommu_xlate(struct of_phandle_args *iommu_spec, void *info)
 {
+	struct device *dev = info;
 	const struct iommu_ops *ops;
 	struct fwnode_handle *fwnode = &iommu_spec->np->fwnode;
 	int ret;
@@ -48,26 +48,27 @@ static int of_iommu_xlate(struct device *dev,
 	return ret;
 }
 
-static int of_iommu_configure_dev_id(struct device_node *master_np,
-				     struct device *dev,
-				     const u32 *id)
+typedef int (*of_for_each_fn)(struct of_phandle_args *args, void *info);
+
+static int __for_each_map_id(struct device_node *master_np, u32 id,
+			     of_for_each_fn fn, void *info)
 {
 	struct of_phandle_args iommu_spec = { .args_count = 1 };
 	int err;
 
-	err = of_map_id(master_np, *id, "iommu-map",
+	err = of_map_id(master_np, id, "iommu-map",
 			 "iommu-map-mask", &iommu_spec.np,
 			 iommu_spec.args);
 	if (err)
 		return err;
 
-	err = of_iommu_xlate(dev, &iommu_spec);
+	err = fn(&iommu_spec, info);
 	of_node_put(iommu_spec.np);
 	return err;
 }
 
-static int of_iommu_configure_dev(struct device_node *master_np,
-				  struct device *dev)
+static int __for_each_iommus(struct device_node *master_np, of_for_each_fn fn,
+			     void *info)
 {
 	struct of_phandle_args iommu_spec;
 	int err = -ENODEV, idx = 0;
@@ -75,7 +76,7 @@ static int of_iommu_configure_dev(struct device_node *master_np,
 	while (!of_parse_phandle_with_args(master_np, "iommus",
 					   "#iommu-cells",
 					   idx, &iommu_spec)) {
-		err = of_iommu_xlate(dev, &iommu_spec);
+		err = fn(&iommu_spec, info);
 		of_node_put(iommu_spec.np);
 		idx++;
 		if (err)
@@ -86,23 +87,46 @@ static int of_iommu_configure_dev(struct device_node *master_np,
 }
 
 struct of_pci_iommu_alias_info {
-	struct device *dev;
 	struct device_node *np;
+	of_for_each_fn fn;
+	void *info;
 };
 
-static int of_pci_iommu_init(struct pci_dev *pdev, u16 alias, void *data)
+static int __for_each_map_pci(struct pci_dev *pdev, u16 alias, void *data)
 {
-	struct of_pci_iommu_alias_info *info = data;
-	u32 input_id = alias;
+	struct of_pci_iommu_alias_info *pci_info = data;
 
-	return of_iommu_configure_dev_id(info->np, info->dev, &input_id);
+	return __for_each_map_id(pci_info->np, alias, pci_info->fn,
+				 pci_info->info);
 }
 
-static int of_iommu_configure_device(struct device_node *master_np,
-				     struct device *dev, const u32 *id)
+static int of_iommu_for_each_id(struct device *dev,
+				struct device_node *master_np, const u32 *id,
+				of_for_each_fn fn, void *info)
 {
-	return (id) ? of_iommu_configure_dev_id(master_np, dev, id) :
-		      of_iommu_configure_dev(master_np, dev);
+	/*
+	 * We don't currently walk up the tree looking for a parent IOMMU.
+	 * See the `Notes:' section of
+	 * Documentation/devicetree/bindings/iommu/iommu.txt
+	 */
+	if (dev_is_pci(dev)) {
+		struct of_pci_iommu_alias_info pci_info = {
+			.np = master_np,
+			.fn = fn,
+			.info = info,
+		};
+
+		/* In PCI mode the ID comes from the RID */
+		if (WARN_ON(id))
+			return -EINVAL;
+
+		return pci_for_each_dma_alias(to_pci_dev(dev),
+					     __for_each_map_pci, &pci_info);
+	}
+
+	if (id)
+		return __for_each_map_id(master_np, *id, fn, info);
+	return __for_each_iommus(master_np, fn, info);
 }
 
 /*
@@ -133,25 +157,11 @@ int of_iommu_configure(struct device *dev, struct device_node *master_np,
 		iommu_fwspec_free(dev);
 	}
 
-	/*
-	 * We don't currently walk up the tree looking for a parent IOMMU.
-	 * See the `Notes:' section of
-	 * Documentation/devicetree/bindings/iommu/iommu.txt
-	 */
-	if (dev_is_pci(dev)) {
-		struct of_pci_iommu_alias_info info = {
-			.dev = dev,
-			.np = master_np,
-		};
-
+	if (dev_is_pci(dev))
 		pci_request_acs();
-		err = pci_for_each_dma_alias(to_pci_dev(dev),
-					     of_pci_iommu_init, &info);
-	} else {
-		err = of_iommu_configure_device(master_np, dev, id);
-	}
-	mutex_unlock(&iommu_probe_device_lock);
 
+	err = of_iommu_for_each_id(dev, master_np, id, of_iommu_xlate, dev);
+	mutex_unlock(&iommu_probe_device_lock);
 	if (err == -ENODEV || err == -EPROBE_DEFER)
 		return err;
 	if (err)
-- 
2.42.0


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

* [PATCH 02/30] ACPI: VIOT: Make a viot_iommu_for_each_id()
  2023-11-30  1:10 [PATCH 00/30] Make a new API for drivers to use to get their FW Jason Gunthorpe
  2023-11-30  1:10 ` [PATCH 01/30] iommu/of: Make a of_iommu_for_each_id() Jason Gunthorpe
@ 2023-11-30  1:10 ` Jason Gunthorpe
  2023-11-30 13:22   ` Rafael J. Wysocki
  2023-11-30  1:10 ` [PATCH 03/30] ACPI: IORT: Make a iort_iommu_for_each_id() Jason Gunthorpe
                   ` (27 subsequent siblings)
  29 siblings, 1 reply; 34+ messages in thread
From: Jason Gunthorpe @ 2023-11-30  1:10 UTC (permalink / raw)
  To: acpica-devel, Andy Gross, Alim Akhtar, Alyssa Rosenzweig,
	Bjorn Andersson, AngeloGioacchino Del Regno, asahi, Baolin Wang,
	devicetree, Frank Rowand, Hanjun Guo, Gustavo A. R. Silva,
	Heiko Stuebner, iommu, Jean-Philippe Brucker, Jernej Skrabec,
	Jonathan Hunter, Joerg Roedel, Kees Cook, Konrad Dybcio,
	Krzysztof Kozlowski, Len Brown, linux-acpi, linux-arm-kernel,
	linux-arm-msm, linux-hardening, linux-mediatek, linux-rockchip,
	linux-samsung-soc, linux-sunxi, linux-tegra, Lorenzo Pieralisi,
	Marek Szyprowski, Hector Martin, Matthias Brugger, Orson Zhai,
	Rafael J. Wysocki, Rob Clark, Robert Moore, Rob Herring,
	Robin Murphy, Samuel Holland, Sudeep Holla, Sven Peter,
	Thierry Reding, Krishna Reddy, virtualization, Chen-Yu Tsai,
	Will Deacon, Yong Wu, Chunyan Zhang
  Cc: André Draszik, patches

Similar to of_iommu_for_each_id() this parses the VIOT ACPI description
and invokes a function over each entry in the table.

Have viot_iommu_configure() use the new function to call
viot_dev_iommu_init().

Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
---
 drivers/acpi/viot.c       | 54 +++++++++++++++++++++++----------------
 include/linux/acpi_viot.h | 11 ++++++++
 2 files changed, 43 insertions(+), 22 deletions(-)

diff --git a/drivers/acpi/viot.c b/drivers/acpi/viot.c
index c8025921c129b2..7ab35ef05c84e0 100644
--- a/drivers/acpi/viot.c
+++ b/drivers/acpi/viot.c
@@ -25,13 +25,6 @@
 #include <linux/pci.h>
 #include <linux/platform_device.h>
 
-struct viot_iommu {
-	/* Node offset within the table */
-	unsigned int			offset;
-	struct fwnode_handle		*fwnode;
-	struct list_head		list;
-};
-
 struct viot_endpoint {
 	union {
 		/* PCI range */
@@ -304,10 +297,10 @@ void __init acpi_viot_init(void)
 	acpi_put_table(hdr);
 }
 
-static int viot_dev_iommu_init(struct device *dev, struct viot_iommu *viommu,
-			       u32 epid)
+static int viot_dev_iommu_init(struct viot_iommu *viommu, u32 epid, void *info)
 {
 	const struct iommu_ops *ops;
+	struct device *dev = info;
 
 	if (!viommu)
 		return -ENODEV;
@@ -324,11 +317,17 @@ static int viot_dev_iommu_init(struct device *dev, struct viot_iommu *viommu,
 	return acpi_iommu_fwspec_init(dev, epid, viommu->fwnode, ops);
 }
 
-static int viot_pci_dev_iommu_init(struct pci_dev *pdev, u16 dev_id, void *data)
+struct viot_pci_iommu_alias_info {
+	struct device *dev;
+	viot_for_each_fn fn;
+	void *info;
+};
+
+static int __for_each_pci_alias(struct pci_dev *pdev, u16 dev_id, void *data)
 {
 	u32 epid;
 	struct viot_endpoint *ep;
-	struct device *aliased_dev = data;
+	struct viot_pci_iommu_alias_info *info = data;
 	u32 domain_nr = pci_domain_nr(pdev->bus);
 
 	list_for_each_entry(ep, &viot_pci_ranges, list) {
@@ -339,14 +338,14 @@ static int viot_pci_dev_iommu_init(struct pci_dev *pdev, u16 dev_id, void *data)
 			epid = ((domain_nr - ep->segment_start) << 16) +
 				dev_id - ep->bdf_start + ep->endpoint_id;
 
-			return viot_dev_iommu_init(aliased_dev, ep->viommu,
-						   epid);
+			return info->fn(ep->viommu, epid, info->info);
 		}
 	}
 	return -ENODEV;
 }
 
-static int viot_mmio_dev_iommu_init(struct platform_device *pdev)
+static int __for_each_platform(struct platform_device *pdev,
+			       viot_for_each_fn fn, void *info)
 {
 	struct resource *mem;
 	struct viot_endpoint *ep;
@@ -357,12 +356,28 @@ static int viot_mmio_dev_iommu_init(struct platform_device *pdev)
 
 	list_for_each_entry(ep, &viot_mmio_endpoints, list) {
 		if (ep->address == mem->start)
-			return viot_dev_iommu_init(&pdev->dev, ep->viommu,
-						   ep->endpoint_id);
+			return fn(ep->viommu, ep->endpoint_id, info);
 	}
 	return -ENODEV;
 }
 
+int viot_iommu_for_each_id(struct device *dev, viot_for_each_fn fn, void *info)
+{
+	if (dev_is_pci(dev)) {
+		struct viot_pci_iommu_alias_info pci_info = {
+			.dev = dev,
+			.fn = fn,
+			.info = info,
+		};
+		return pci_for_each_dma_alias(to_pci_dev(dev),
+					      __for_each_pci_alias, &pci_info);
+	}
+
+	if (dev_is_platform(dev))
+		return __for_each_platform(to_platform_device(dev), fn, info);
+	return -ENODEV;
+}
+
 /**
  * viot_iommu_configure - Setup IOMMU ops for an endpoint described by VIOT
  * @dev: the endpoint
@@ -371,10 +386,5 @@ static int viot_mmio_dev_iommu_init(struct platform_device *pdev)
  */
 int viot_iommu_configure(struct device *dev)
 {
-	if (dev_is_pci(dev))
-		return pci_for_each_dma_alias(to_pci_dev(dev),
-					      viot_pci_dev_iommu_init, dev);
-	else if (dev_is_platform(dev))
-		return viot_mmio_dev_iommu_init(to_platform_device(dev));
-	return -ENODEV;
+	return viot_iommu_for_each_id(dev, viot_dev_iommu_init, dev);
 }
diff --git a/include/linux/acpi_viot.h b/include/linux/acpi_viot.h
index a5a12243156377..fce4eefcae4aad 100644
--- a/include/linux/acpi_viot.h
+++ b/include/linux/acpi_viot.h
@@ -5,6 +5,17 @@
 
 #include <linux/acpi.h>
 
+struct viot_iommu {
+	/* Node offset within the table */
+	unsigned int			offset;
+	struct fwnode_handle		*fwnode;
+	struct list_head		list;
+};
+
+typedef int (*viot_for_each_fn)(struct viot_iommu *viommu, u32 epid,
+				void *info);
+int viot_iommu_for_each_id(struct device *dev, viot_for_each_fn fn, void *info);
+
 #ifdef CONFIG_ACPI_VIOT
 void __init acpi_viot_early_init(void);
 void __init acpi_viot_init(void);
-- 
2.42.0


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

* [PATCH 03/30] ACPI: IORT: Make a iort_iommu_for_each_id()
  2023-11-30  1:10 [PATCH 00/30] Make a new API for drivers to use to get their FW Jason Gunthorpe
  2023-11-30  1:10 ` [PATCH 01/30] iommu/of: Make a of_iommu_for_each_id() Jason Gunthorpe
  2023-11-30  1:10 ` [PATCH 02/30] ACPI: VIOT: Make a viot_iommu_for_each_id() Jason Gunthorpe
@ 2023-11-30  1:10 ` Jason Gunthorpe
  2023-11-30 13:21   ` Rafael J. Wysocki
  2023-11-30  1:10 ` [PATCH 04/30] ACPI: IORT: Remove fwspec from the reserved region code Jason Gunthorpe
                   ` (26 subsequent siblings)
  29 siblings, 1 reply; 34+ messages in thread
From: Jason Gunthorpe @ 2023-11-30  1:10 UTC (permalink / raw)
  To: acpica-devel, Andy Gross, Alim Akhtar, Alyssa Rosenzweig,
	Bjorn Andersson, AngeloGioacchino Del Regno, asahi, Baolin Wang,
	devicetree, Frank Rowand, Hanjun Guo, Gustavo A. R. Silva,
	Heiko Stuebner, iommu, Jean-Philippe Brucker, Jernej Skrabec,
	Jonathan Hunter, Joerg Roedel, Kees Cook, Konrad Dybcio,
	Krzysztof Kozlowski, Len Brown, linux-acpi, linux-arm-kernel,
	linux-arm-msm, linux-hardening, linux-mediatek, linux-rockchip,
	linux-samsung-soc, linux-sunxi, linux-tegra, Lorenzo Pieralisi,
	Marek Szyprowski, Hector Martin, Matthias Brugger, Orson Zhai,
	Rafael J. Wysocki, Rob Clark, Robert Moore, Rob Herring,
	Robin Murphy, Samuel Holland, Sudeep Holla, Sven Peter,
	Thierry Reding, Krishna Reddy, virtualization, Chen-Yu Tsai,
	Will Deacon, Yong Wu, Chunyan Zhang
  Cc: André Draszik, patches

Similar to of_iommu_for_each_id() this parses the IORT ACPI description
and invokes a function over each entry in the table.

Have iort_iommu_configure_id() use the new function to call
iort_iommu_xlate().

Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
---
 drivers/acpi/arm64/iort.c | 118 ++++++++++++++++++++++++--------------
 include/linux/acpi_iort.h |  12 ++++
 2 files changed, 86 insertions(+), 44 deletions(-)

diff --git a/drivers/acpi/arm64/iort.c b/drivers/acpi/arm64/iort.c
index bdaf9256870d92..5c9b4c23f96a87 100644
--- a/drivers/acpi/arm64/iort.c
+++ b/drivers/acpi/arm64/iort.c
@@ -1218,9 +1218,10 @@ static bool iort_pci_rc_supports_ats(struct acpi_iort_node *node)
 	return pci_rc->ats_attribute & ACPI_IORT_ATS_SUPPORTED;
 }
 
-static int iort_iommu_xlate(struct device *dev, struct acpi_iort_node *node,
-			    u32 streamid)
+static int iort_iommu_xlate(struct acpi_iort_node *node, u32 streamid,
+			    void *info)
 {
+	struct device *dev = info;
 	const struct iommu_ops *ops;
 	struct fwnode_handle *iort_fwnode;
 
@@ -1250,9 +1251,11 @@ static int iort_iommu_xlate(struct device *dev, struct acpi_iort_node *node,
 struct iort_pci_alias_info {
 	struct device *dev;
 	struct acpi_iort_node *node;
+	iort_for_each_fn fn;
+	void *info;
 };
 
-static int iort_pci_iommu_init(struct pci_dev *pdev, u16 alias, void *data)
+static int __for_each_pci_alias(struct pci_dev *pdev, u16 alias, void *data)
 {
 	struct iort_pci_alias_info *info = data;
 	struct acpi_iort_node *parent;
@@ -1260,7 +1263,7 @@ static int iort_pci_iommu_init(struct pci_dev *pdev, u16 alias, void *data)
 
 	parent = iort_node_map_id(info->node, alias, &streamid,
 				  IORT_IOMMU_TYPE);
-	return iort_iommu_xlate(info->dev, parent, streamid);
+	return info->fn(parent, streamid, info->info);
 }
 
 static void iort_named_component_init(struct device *dev,
@@ -1280,7 +1283,8 @@ static void iort_named_component_init(struct device *dev,
 		dev_warn(dev, "Could not add device properties\n");
 }
 
-static int iort_nc_iommu_map(struct device *dev, struct acpi_iort_node *node)
+static int __for_each_platform(struct acpi_iort_node *node, iort_for_each_fn fn,
+			       void *info)
 {
 	struct acpi_iort_node *parent;
 	int err = -ENODEV, i = 0;
@@ -1293,27 +1297,71 @@ static int iort_nc_iommu_map(struct device *dev, struct acpi_iort_node *node)
 						   i++);
 
 		if (parent)
-			err = iort_iommu_xlate(dev, parent, streamid);
+			err = fn(parent, streamid, info);
 	} while (parent && !err);
 
 	return err;
 }
 
-static int iort_nc_iommu_map_id(struct device *dev,
-				struct acpi_iort_node *node,
-				const u32 *in_id)
+int iort_iommu_for_each_id(struct device *dev, const u32 *id_in,
+			   struct iort_params *params, iort_for_each_fn fn,
+			   void *info)
 {
-	struct acpi_iort_node *parent;
-	u32 streamid;
+	struct acpi_iort_named_component *nc;
+	struct acpi_iort_node *node;
+	int err = -ENODEV;
 
-	parent = iort_node_map_id(node, *in_id, &streamid, IORT_IOMMU_TYPE);
-	if (parent)
-		return iort_iommu_xlate(dev, parent, streamid);
+	memset(params, 0, sizeof(*params));
+	if (dev_is_pci(dev)) {
+		struct pci_bus *bus = to_pci_dev(dev)->bus;
+		struct iort_pci_alias_info pci_info = { .dev = dev,
+							.fn = fn,
+							.info = info };
 
-	return -ENODEV;
+		node = iort_scan_node(ACPI_IORT_NODE_PCI_ROOT_COMPLEX,
+				      iort_match_node_callback, &bus->dev);
+		if (!node)
+			return -ENODEV;
+
+		pci_info.node = node;
+		err = pci_for_each_dma_alias(to_pci_dev(dev),
+					     __for_each_pci_alias, &pci_info);
+
+		if (iort_pci_rc_supports_ats(node))
+			params->pci_rc_ats = true;
+		return 0;
+	}
+
+	node = iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT,
+			      iort_match_node_callback, dev);
+	if (!node)
+		return -ENODEV;
+
+	if (id_in) {
+		struct acpi_iort_node *parent;
+		u32 streamid;
+
+		parent = iort_node_map_id(node, *id_in, &streamid,
+					  IORT_IOMMU_TYPE);
+		if (!parent)
+			return -ENODEV;
+		err = fn(parent, streamid, info);
+	} else {
+		err = __for_each_platform(node, fn, info);
+	}
+	if (err)
+		return err;
+
+	nc = (struct acpi_iort_named_component *)node->node_data;
+	params->pasid_num_bits = FIELD_GET(ACPI_IORT_NC_PASID_BITS,
+						nc->node_flags);
+	if (nc->node_flags & ACPI_IORT_NC_STALL_SUPPORTED)
+		params->dma_can_stall = true;
+
+	iort_named_component_init(dev, node);
+	return 0;
 }
 
-
 /**
  * iort_iommu_configure_id - Set-up IOMMU configuration for a device.
  *
@@ -1324,40 +1372,22 @@ static int iort_nc_iommu_map_id(struct device *dev,
  */
 int iort_iommu_configure_id(struct device *dev, const u32 *id_in)
 {
-	struct acpi_iort_node *node;
-	int err = -ENODEV;
+	struct iort_params params;
+	int err;
 
-	if (dev_is_pci(dev)) {
+	err = iort_iommu_for_each_id(dev, id_in, &params, &iort_iommu_xlate,
+				     dev);
+	if (err)
+		return err;
+
+	if (params.pci_rc_ats) {
 		struct iommu_fwspec *fwspec;
-		struct pci_bus *bus = to_pci_dev(dev)->bus;
-		struct iort_pci_alias_info info = { .dev = dev };
-
-		node = iort_scan_node(ACPI_IORT_NODE_PCI_ROOT_COMPLEX,
-				      iort_match_node_callback, &bus->dev);
-		if (!node)
-			return -ENODEV;
-
-		info.node = node;
-		err = pci_for_each_dma_alias(to_pci_dev(dev),
-					     iort_pci_iommu_init, &info);
 
 		fwspec = dev_iommu_fwspec_get(dev);
-		if (fwspec && iort_pci_rc_supports_ats(node))
+		if (fwspec)
 			fwspec->flags |= IOMMU_FWSPEC_PCI_RC_ATS;
-	} else {
-		node = iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT,
-				      iort_match_node_callback, dev);
-		if (!node)
-			return -ENODEV;
-
-		err = id_in ? iort_nc_iommu_map_id(dev, node, id_in) :
-			      iort_nc_iommu_map(dev, node);
-
-		if (!err)
-			iort_named_component_init(dev, node);
 	}
-
-	return err;
+	return 0;
 }
 
 #else
diff --git a/include/linux/acpi_iort.h b/include/linux/acpi_iort.h
index 1cb65592c95dd3..5423abff9b6b09 100644
--- a/include/linux/acpi_iort.h
+++ b/include/linux/acpi_iort.h
@@ -29,6 +29,18 @@ void iort_deregister_domain_token(int trans_id);
 struct fwnode_handle *iort_find_domain_token(int trans_id);
 int iort_pmsi_get_dev_id(struct device *dev, u32 *dev_id);
 
+struct iort_params {
+	unsigned int pasid_num_bits;
+	bool dma_can_stall : 1;
+	bool pci_rc_ats : 1;
+};
+
+typedef int (*iort_for_each_fn)(struct acpi_iort_node *iommu, u32 streamid,
+				void *info);
+int iort_iommu_for_each_id(struct device *dev, const u32 *id_in,
+			   struct iort_params *params, iort_for_each_fn fn,
+			   void *info);
+
 #ifdef CONFIG_ACPI_IORT
 u32 iort_msi_map_id(struct device *dev, u32 id);
 struct irq_domain *iort_get_device_domain(struct device *dev, u32 id,
-- 
2.42.0


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

* [PATCH 04/30] ACPI: IORT: Remove fwspec from the reserved region code
  2023-11-30  1:10 [PATCH 00/30] Make a new API for drivers to use to get their FW Jason Gunthorpe
                   ` (2 preceding siblings ...)
  2023-11-30  1:10 ` [PATCH 03/30] ACPI: IORT: Make a iort_iommu_for_each_id() Jason Gunthorpe
@ 2023-11-30  1:10 ` Jason Gunthorpe
  2023-11-30 13:23   ` Rafael J. Wysocki
  2023-11-30  1:10 ` [PATCH 05/30] iommu: Add iommu_probe_info Jason Gunthorpe
                   ` (25 subsequent siblings)
  29 siblings, 1 reply; 34+ messages in thread
From: Jason Gunthorpe @ 2023-11-30  1:10 UTC (permalink / raw)
  To: acpica-devel, Andy Gross, Alim Akhtar, Alyssa Rosenzweig,
	Bjorn Andersson, AngeloGioacchino Del Regno, asahi, Baolin Wang,
	devicetree, Frank Rowand, Hanjun Guo, Gustavo A. R. Silva,
	Heiko Stuebner, iommu, Jean-Philippe Brucker, Jernej Skrabec,
	Jonathan Hunter, Joerg Roedel, Kees Cook, Konrad Dybcio,
	Krzysztof Kozlowski, Len Brown, linux-acpi, linux-arm-kernel,
	linux-arm-msm, linux-hardening, linux-mediatek, linux-rockchip,
	linux-samsung-soc, linux-sunxi, linux-tegra, Lorenzo Pieralisi,
	Marek Szyprowski, Hector Martin, Matthias Brugger, Orson Zhai,
	Rafael J. Wysocki, Rob Clark, Robert Moore, Rob Herring,
	Robin Murphy, Samuel Holland, Sudeep Holla, Sven Peter,
	Thierry Reding, Krishna Reddy, virtualization, Chen-Yu Tsai,
	Will Deacon, Yong Wu, Chunyan Zhang
  Cc: André Draszik, patches

iort_iommu_get_resv_regions() needs access to the parsed id array that is
currently stored in the iommu_fwspec.

Instead of getting this from the fwspec inside the iort code have the
caller pass it in.

Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
---
 drivers/acpi/arm64/iort.c | 88 ++++++++++++++++++++++++---------------
 drivers/iommu/dma-iommu.c |  7 +++-
 include/linux/acpi_iort.h |  8 +++-
 3 files changed, 65 insertions(+), 38 deletions(-)

diff --git a/drivers/acpi/arm64/iort.c b/drivers/acpi/arm64/iort.c
index 5c9b4c23f96a87..93e30f2f5004f0 100644
--- a/drivers/acpi/arm64/iort.c
+++ b/drivers/acpi/arm64/iort.c
@@ -946,11 +946,19 @@ static u32 *iort_rmr_alloc_sids(u32 *sids, u32 count, u32 id_start,
 	return new_sids;
 }
 
-static bool iort_rmr_has_dev(struct device *dev, u32 id_start,
+struct iort_resv_args {
+	struct device *dev;
+	struct list_head *head;
+	struct fwnode_handle *iommu_fwnode;
+	const u32 *fw_ids;
+	unsigned int fw_num_ids;
+};
+
+static bool iort_rmr_has_dev(struct iort_resv_args *args, u32 id_start,
 			     u32 id_count)
 {
+	struct device *dev = args->dev;
 	int i;
-	struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
 
 	/*
 	 * Make sure the kernel has preserved the boot firmware PCIe
@@ -965,18 +973,18 @@ static bool iort_rmr_has_dev(struct device *dev, u32 id_start,
 			return false;
 	}
 
-	for (i = 0; i < fwspec->num_ids; i++) {
-		if (fwspec->ids[i] >= id_start &&
-		    fwspec->ids[i] <= id_start + id_count)
+	for (i = 0; i < args->fw_num_ids; i++) {
+		if (args->fw_ids[i] >= id_start &&
+		    args->fw_ids[i] <= id_start + id_count)
 			return true;
 	}
 
 	return false;
 }
 
-static void iort_node_get_rmr_info(struct acpi_iort_node *node,
-				   struct acpi_iort_node *iommu,
-				   struct device *dev, struct list_head *head)
+static void iort_node_get_rmr_info(struct iort_resv_args *args,
+				   struct acpi_iort_node *node,
+				   struct acpi_iort_node *iommu)
 {
 	struct acpi_iort_node *smmu = NULL;
 	struct acpi_iort_rmr *rmr;
@@ -1013,8 +1021,8 @@ static void iort_node_get_rmr_info(struct acpi_iort_node *node,
 			continue;
 
 		/* If dev is valid, check RMR node corresponds to the dev SID */
-		if (dev && !iort_rmr_has_dev(dev, map->output_base,
-					     map->id_count))
+		if (args->dev &&
+		    !iort_rmr_has_dev(args, map->output_base, map->id_count))
 			continue;
 
 		/* Retrieve SIDs associated with the Node. */
@@ -1029,12 +1037,12 @@ static void iort_node_get_rmr_info(struct acpi_iort_node *node,
 	if (!sids)
 		return;
 
-	iort_get_rmrs(node, smmu, sids, num_sids, head);
+	iort_get_rmrs(node, smmu, sids, num_sids, args->head);
 	kfree(sids);
 }
 
-static void iort_find_rmrs(struct acpi_iort_node *iommu, struct device *dev,
-			   struct list_head *head)
+static void iort_find_rmrs(struct iort_resv_args *args,
+			   struct acpi_iort_node *iommu)
 {
 	struct acpi_table_iort *iort;
 	struct acpi_iort_node *iort_node, *iort_end;
@@ -1057,7 +1065,7 @@ static void iort_find_rmrs(struct acpi_iort_node *iommu, struct device *dev,
 			return;
 
 		if (iort_node->type == ACPI_IORT_NODE_RMR)
-			iort_node_get_rmr_info(iort_node, iommu, dev, head);
+			iort_node_get_rmr_info(args, iort_node, iommu);
 
 		iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort_node,
 					 iort_node->length);
@@ -1069,25 +1077,23 @@ static void iort_find_rmrs(struct acpi_iort_node *iommu, struct device *dev,
  * If dev is NULL, the function populates all the RMRs associated with the
  * given IOMMU.
  */
-static void iort_iommu_rmr_get_resv_regions(struct fwnode_handle *iommu_fwnode,
-					    struct device *dev,
-					    struct list_head *head)
+static void iort_iommu_rmr_get_resv_regions(struct iort_resv_args *args)
 {
 	struct acpi_iort_node *iommu;
 
-	iommu = iort_get_iort_node(iommu_fwnode);
+	iommu = iort_get_iort_node(args->iommu_fwnode);
 	if (!iommu)
 		return;
 
-	iort_find_rmrs(iommu, dev, head);
+	iort_find_rmrs(args, iommu);
 }
 
-static struct acpi_iort_node *iort_get_msi_resv_iommu(struct device *dev)
+static struct acpi_iort_node *
+iort_get_msi_resv_iommu(struct iort_resv_args *args)
 {
 	struct acpi_iort_node *iommu;
-	struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
 
-	iommu = iort_get_iort_node(fwspec->iommu_fwnode);
+	iommu = iort_get_iort_node(args->iommu_fwnode);
 
 	if (iommu && (iommu->type == ACPI_IORT_NODE_SMMU_V3)) {
 		struct acpi_iort_smmu_v3 *smmu;
@@ -1105,15 +1111,13 @@ static struct acpi_iort_node *iort_get_msi_resv_iommu(struct device *dev)
  * The ITS interrupt translation spaces (ITS_base + SZ_64K, SZ_64K)
  * associated with the device are the HW MSI reserved regions.
  */
-static void iort_iommu_msi_get_resv_regions(struct device *dev,
-					    struct list_head *head)
+static void iort_iommu_msi_get_resv_regions(struct iort_resv_args *args)
 {
-	struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
 	struct acpi_iort_its_group *its;
 	struct acpi_iort_node *iommu_node, *its_node = NULL;
 	int i;
 
-	iommu_node = iort_get_msi_resv_iommu(dev);
+	iommu_node = iort_get_msi_resv_iommu(args);
 	if (!iommu_node)
 		return;
 
@@ -1126,9 +1130,9 @@ static void iort_iommu_msi_get_resv_regions(struct device *dev,
 	 * a given PCI or named component may map IDs to.
 	 */
 
-	for (i = 0; i < fwspec->num_ids; i++) {
+	for (i = 0; i < args->fw_num_ids; i++) {
 		its_node = iort_node_map_id(iommu_node,
-					fwspec->ids[i],
+					args->fw_ids[i],
 					NULL, IORT_MSI_TYPE);
 		if (its_node)
 			break;
@@ -1151,7 +1155,7 @@ static void iort_iommu_msi_get_resv_regions(struct device *dev,
 							 prot, IOMMU_RESV_MSI,
 							 GFP_KERNEL);
 			if (region)
-				list_add_tail(&region->list, head);
+				list_add_tail(&region->list, args->head);
 		}
 	}
 }
@@ -1160,13 +1164,24 @@ static void iort_iommu_msi_get_resv_regions(struct device *dev,
  * iort_iommu_get_resv_regions - Generic helper to retrieve reserved regions.
  * @dev: Device from iommu_get_resv_regions()
  * @head: Reserved region list from iommu_get_resv_regions()
+ * @iommu_fwnode: fwnode that describes the iommu connection for the device
+ * @fw_ids: Parsed IDs
+ * @fw_num_ids: Length of fw_ids
  */
-void iort_iommu_get_resv_regions(struct device *dev, struct list_head *head)
+void iort_iommu_get_resv_regions(struct device *dev, struct list_head *head,
+				 struct fwnode_handle *iommu_fwnode,
+				 const u32 *fw_ids, unsigned int fw_num_ids)
 {
-	struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
+	struct iort_resv_args args = {
+		.dev = dev,
+		.head = head,
+		.iommu_fwnode = iommu_fwnode,
+		.fw_ids = fw_ids,
+		.fw_num_ids = fw_num_ids,
+	};
 
-	iort_iommu_msi_get_resv_regions(dev, head);
-	iort_iommu_rmr_get_resv_regions(fwspec->iommu_fwnode, dev, head);
+	iort_iommu_msi_get_resv_regions(&args);
+	iort_iommu_rmr_get_resv_regions(&args);
 }
 
 /**
@@ -1178,7 +1193,12 @@ void iort_iommu_get_resv_regions(struct device *dev, struct list_head *head)
 void iort_get_rmr_sids(struct fwnode_handle *iommu_fwnode,
 		       struct list_head *head)
 {
-	iort_iommu_rmr_get_resv_regions(iommu_fwnode, NULL, head);
+	struct iort_resv_args args = {
+		.head = head,
+		.iommu_fwnode = iommu_fwnode,
+	};
+
+	iort_iommu_rmr_get_resv_regions(&args);
 }
 EXPORT_SYMBOL_GPL(iort_get_rmr_sids);
 
diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index 85163a83df2f68..d644b0502ef48e 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -468,9 +468,12 @@ void iommu_put_dma_cookie(struct iommu_domain *domain)
  */
 void iommu_dma_get_resv_regions(struct device *dev, struct list_head *list)
 {
+	struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
 
-	if (!is_of_node(dev_iommu_fwspec_get(dev)->iommu_fwnode))
-		iort_iommu_get_resv_regions(dev, list);
+	if (!is_of_node(fwspec->iommu_fwnode)) {
+		iort_iommu_get_resv_regions(dev, list, fwspec->iommu_fwnode,
+					    fwspec->ids, fwspec->num_ids);
+	}
 
 	if (dev->of_node)
 		of_iommu_get_resv_regions(dev, list);
diff --git a/include/linux/acpi_iort.h b/include/linux/acpi_iort.h
index 5423abff9b6b09..13f0cefb930693 100644
--- a/include/linux/acpi_iort.h
+++ b/include/linux/acpi_iort.h
@@ -53,7 +53,9 @@ void iort_put_rmr_sids(struct fwnode_handle *iommu_fwnode,
 /* IOMMU interface */
 int iort_dma_get_ranges(struct device *dev, u64 *size);
 int iort_iommu_configure_id(struct device *dev, const u32 *id_in);
-void iort_iommu_get_resv_regions(struct device *dev, struct list_head *head);
+void iort_iommu_get_resv_regions(struct device *dev, struct list_head *head,
+				 struct fwnode_handle *iommu_fwnode,
+				 const u32 *fw_ids, unsigned int fw_num_ids);
 phys_addr_t acpi_iort_dma_get_max_cpu_address(void);
 #else
 static inline u32 iort_msi_map_id(struct device *dev, u32 id)
@@ -72,7 +74,9 @@ static inline int iort_dma_get_ranges(struct device *dev, u64 *size)
 static inline int iort_iommu_configure_id(struct device *dev, const u32 *id_in)
 { return -ENODEV; }
 static inline
-void iort_iommu_get_resv_regions(struct device *dev, struct list_head *head)
+void iort_iommu_get_resv_regions(struct device *dev, struct list_head *head,
+				 struct fwnode_handle *iommu_fwnode,
+				 const u32 *fw_ids, unsigned int fw_num_ids)
 { }
 
 static inline phys_addr_t acpi_iort_dma_get_max_cpu_address(void)
-- 
2.42.0


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

* [PATCH 05/30] iommu: Add iommu_probe_info
  2023-11-30  1:10 [PATCH 00/30] Make a new API for drivers to use to get their FW Jason Gunthorpe
                   ` (3 preceding siblings ...)
  2023-11-30  1:10 ` [PATCH 04/30] ACPI: IORT: Remove fwspec from the reserved region code Jason Gunthorpe
@ 2023-11-30  1:10 ` Jason Gunthorpe
  2023-11-30  1:10 ` [PATCH 06/30] iommu: Make iommu_ops_from_fwnode() return the iommu_device Jason Gunthorpe
                   ` (24 subsequent siblings)
  29 siblings, 0 replies; 34+ messages in thread
From: Jason Gunthorpe @ 2023-11-30  1:10 UTC (permalink / raw)
  To: acpica-devel, Andy Gross, Alim Akhtar, Alyssa Rosenzweig,
	Bjorn Andersson, AngeloGioacchino Del Regno, asahi, Baolin Wang,
	devicetree, Frank Rowand, Hanjun Guo, Gustavo A. R. Silva,
	Heiko Stuebner, iommu, Jean-Philippe Brucker, Jernej Skrabec,
	Jonathan Hunter, Joerg Roedel, Kees Cook, Konrad Dybcio,
	Krzysztof Kozlowski, Len Brown, linux-acpi, linux-arm-kernel,
	linux-arm-msm, linux-hardening, linux-mediatek, linux-rockchip,
	linux-samsung-soc, linux-sunxi, linux-tegra, Lorenzo Pieralisi,
	Marek Szyprowski, Hector Martin, Matthias Brugger, Orson Zhai,
	Rafael J. Wysocki, Rob Clark, Robert Moore, Rob Herring,
	Robin Murphy, Samuel Holland, Sudeep Holla, Sven Peter,
	Thierry Reding, Krishna Reddy, virtualization, Chen-Yu Tsai,
	Will Deacon, Yong Wu, Chunyan Zhang
  Cc: André Draszik, patches

This is a stack structure that is passed around all the parts of probe to
allow them to exchange data.

With the new design this will be a place for the FW logic to cache data to
avoid reparsing and a to convey the currently active call path for probe
while we work on restructuring parts of it.

Place this in a new header "iommu-driver.h" which is intended to help
isolate APIs that are only for use by the drivers away from the consumers
of the IOMMU API.

Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
---
 drivers/acpi/scan.c          |  7 +++++-
 drivers/iommu/iommu.c        | 42 ++++++++++++++++++++++++++----------
 drivers/iommu/of_iommu.c     |  6 +++++-
 include/linux/iommu-driver.h | 25 +++++++++++++++++++++
 include/linux/iommu.h        |  3 +++
 5 files changed, 70 insertions(+), 13 deletions(-)
 create mode 100644 include/linux/iommu-driver.h

diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index 340ba720c72129..9c13df632aa5e0 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -1543,6 +1543,8 @@ int acpi_dma_get_range(struct device *dev, const struct bus_dma_region **map)
 }
 
 #ifdef CONFIG_IOMMU_API
+#include <linux/iommu-driver.h>
+
 int acpi_iommu_fwspec_init(struct device *dev, u32 id,
 			   struct fwnode_handle *fwnode,
 			   const struct iommu_ops *ops)
@@ -1566,6 +1568,9 @@ static int acpi_iommu_configure_id(struct device *dev, const u32 *id_in)
 {
 	int err;
 	const struct iommu_ops *ops;
+	struct iommu_probe_info pinf = {
+		.dev = dev,
+	};
 
 	/* Serialise to make dev->iommu stable under our potential fwspec */
 	mutex_lock(&iommu_probe_device_lock);
@@ -1589,7 +1594,7 @@ static int acpi_iommu_configure_id(struct device *dev, const u32 *id_in)
 	 * iommu_probe_device() call for dev, replay it to get things in order.
 	 */
 	if (!err && dev->bus)
-		err = iommu_probe_device(dev);
+		err = iommu_probe_device_pinf(&pinf);
 
 	/* Ignore all other errors apart from EPROBE_DEFER */
 	if (err == -EPROBE_DEFER) {
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index 9557c2ec08d915..76b245973cfafc 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -18,6 +18,7 @@
 #include <linux/errno.h>
 #include <linux/host1x_context_bus.h>
 #include <linux/iommu.h>
+#include <linux/iommu-driver.h>
 #include <linux/idr.h>
 #include <linux/err.h>
 #include <linux/pci.h>
@@ -399,8 +400,10 @@ EXPORT_SYMBOL_GPL(dev_iommu_priv_set);
  * Init the dev->iommu and dev->iommu_group in the struct device and get the
  * driver probed
  */
-static int iommu_init_device(struct device *dev, const struct iommu_ops *ops)
+static int iommu_init_device(struct iommu_probe_info *pinf,
+			     const struct iommu_ops *ops)
 {
+	struct device *dev = pinf->dev;
 	struct iommu_device *iommu_dev;
 	struct iommu_group *group;
 	int ret;
@@ -413,7 +416,10 @@ static int iommu_init_device(struct device *dev, const struct iommu_ops *ops)
 		goto err_free;
 	}
 
-	iommu_dev = ops->probe_device(dev);
+	if (ops->probe_device_pinf)
+		iommu_dev = ops->probe_device_pinf(pinf);
+	else
+		iommu_dev = ops->probe_device(dev);
 	if (IS_ERR(iommu_dev)) {
 		ret = PTR_ERR(iommu_dev);
 		goto err_module_put;
@@ -496,8 +502,9 @@ static void iommu_deinit_device(struct device *dev)
 
 DEFINE_MUTEX(iommu_probe_device_lock);
 
-static int __iommu_probe_device(struct device *dev, struct list_head *group_list)
+static int __iommu_probe_device(struct iommu_probe_info *pinf)
 {
+	struct device *dev = pinf->dev;
 	const struct iommu_ops *ops;
 	struct iommu_fwspec *fwspec;
 	struct iommu_group *group;
@@ -533,7 +540,7 @@ static int __iommu_probe_device(struct device *dev, struct list_head *group_list
 	if (dev->iommu_group)
 		return 0;
 
-	ret = iommu_init_device(dev, ops);
+	ret = iommu_init_device(pinf, ops);
 	if (ret)
 		return ret;
 
@@ -557,7 +564,7 @@ static int __iommu_probe_device(struct device *dev, struct list_head *group_list
 		ret = __iommu_device_set_domain(group, dev, group->domain, 0);
 		if (ret)
 			goto err_remove_gdev;
-	} else if (!group->default_domain && !group_list) {
+	} else if (!group->default_domain && !pinf->defer_setup) {
 		ret = iommu_setup_default_domain(group, 0);
 		if (ret)
 			goto err_remove_gdev;
@@ -568,7 +575,7 @@ static int __iommu_probe_device(struct device *dev, struct list_head *group_list
 		 * that need further setup.
 		 */
 		if (list_empty(&group->entry))
-			list_add_tail(&group->entry, group_list);
+			list_add_tail(&group->entry, pinf->deferred_group_list);
 	}
 	mutex_unlock(&group->mutex);
 
@@ -588,13 +595,14 @@ static int __iommu_probe_device(struct device *dev, struct list_head *group_list
 	return ret;
 }
 
-int iommu_probe_device(struct device *dev)
+int iommu_probe_device_pinf(struct iommu_probe_info *pinf)
 {
+	struct device *dev = pinf->dev;
 	const struct iommu_ops *ops;
 	int ret;
 
 	mutex_lock(&iommu_probe_device_lock);
-	ret = __iommu_probe_device(dev, NULL);
+	ret = __iommu_probe_device(pinf);
 	mutex_unlock(&iommu_probe_device_lock);
 	if (ret)
 		return ret;
@@ -606,6 +614,13 @@ int iommu_probe_device(struct device *dev)
 	return 0;
 }
 
+int iommu_probe_device(struct device *dev)
+{
+	struct iommu_probe_info pinf = {.dev = dev};
+
+	return iommu_probe_device_pinf(&pinf);
+}
+
 static void __iommu_group_free_device(struct iommu_group *group,
 				      struct group_device *grp_dev)
 {
@@ -1830,11 +1845,12 @@ struct iommu_domain *iommu_group_default_domain(struct iommu_group *group)
 
 static int probe_iommu_group(struct device *dev, void *data)
 {
-	struct list_head *group_list = data;
+	struct iommu_probe_info *pinf = data;
 	int ret;
 
+	pinf->dev = dev;
 	mutex_lock(&iommu_probe_device_lock);
-	ret = __iommu_probe_device(dev, group_list);
+	ret = __iommu_probe_device(pinf);
 	mutex_unlock(&iommu_probe_device_lock);
 	if (ret == -ENODEV)
 		ret = 0;
@@ -1977,9 +1993,13 @@ int bus_iommu_probe(const struct bus_type *bus)
 {
 	struct iommu_group *group, *next;
 	LIST_HEAD(group_list);
+	struct iommu_probe_info pinf = {
+		.deferred_group_list = &group_list,
+		.defer_setup = true,
+	};
 	int ret;
 
-	ret = bus_for_each_dev(bus, NULL, &group_list, probe_iommu_group);
+	ret = bus_for_each_dev(bus, NULL, &pinf, probe_iommu_group);
 	if (ret)
 		return ret;
 
diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c
index 3d4580f1fbb378..fb743ddd239e0b 100644
--- a/drivers/iommu/of_iommu.c
+++ b/drivers/iommu/of_iommu.c
@@ -7,6 +7,7 @@
 
 #include <linux/export.h>
 #include <linux/iommu.h>
+#include <linux/iommu-driver.h>
 #include <linux/limits.h>
 #include <linux/module.h>
 #include <linux/of.h>
@@ -139,6 +140,9 @@ static int of_iommu_for_each_id(struct device *dev,
 int of_iommu_configure(struct device *dev, struct device_node *master_np,
 		       const u32 *id)
 {
+	struct iommu_probe_info pinf = {
+		.dev = dev,
+	};
 	struct iommu_fwspec *fwspec;
 	int err;
 
@@ -167,7 +171,7 @@ int of_iommu_configure(struct device *dev, struct device_node *master_np,
 	if (err)
 		goto err_log;
 
-	err = iommu_probe_device(dev);
+	err = iommu_probe_device_pinf(&pinf);
 	if (err)
 		goto err_log;
 	return 0;
diff --git a/include/linux/iommu-driver.h b/include/linux/iommu-driver.h
new file mode 100644
index 00000000000000..b85c9f15cf478b
--- /dev/null
+++ b/include/linux/iommu-driver.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES
+ *
+ * This file should ONLY be included by iommu drivers. These API
+ * calls are NOT to be used generally.
+ */
+#ifndef __LINUX_IOMMU_DRIVER_H
+#define __LINUX_IOMMU_DRIVER_H
+
+#ifndef CONFIG_IOMMU_API
+#error "CONFIG_IOMMU_API is not set, should this header be included?"
+#endif
+
+#include <linux/types.h>
+
+struct iommu_probe_info {
+	struct device *dev;
+	struct list_head *deferred_group_list;
+	bool defer_setup : 1;
+};
+
+int iommu_probe_device_pinf(struct iommu_probe_info *pinf);
+
+#endif
diff --git a/include/linux/iommu.h b/include/linux/iommu.h
index c24933a1d0d643..cf578b8e0b59a4 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -43,6 +43,7 @@ struct notifier_block;
 struct iommu_sva;
 struct iommu_fault_event;
 struct iommu_dma_cookie;
+struct iommu_probe_info;
 
 /* iommu fault flags */
 #define IOMMU_FAULT_READ	0x0
@@ -347,6 +348,7 @@ static inline int __iommu_copy_struct_from_user(
  * @domain_alloc_paging: Allocate an iommu_domain that can be used for
  *                       UNMANAGED, DMA, and DMA_FQ domain types.
  * @probe_device: Add device to iommu driver handling
+ * @probe_device_pinf: New API for probe_device
  * @release_device: Remove device from iommu driver handling
  * @probe_finalize: Do final setup work after the device is added to an IOMMU
  *                  group and attached to the groups domain
@@ -388,6 +390,7 @@ struct iommu_ops {
 	struct iommu_domain *(*domain_alloc_paging)(struct device *dev);
 
 	struct iommu_device *(*probe_device)(struct device *dev);
+	struct iommu_device *(*probe_device_pinf)(struct iommu_probe_info *pinf);
 	void (*release_device)(struct device *dev);
 	void (*probe_finalize)(struct device *dev);
 	struct iommu_group *(*device_group)(struct device *dev);
-- 
2.42.0


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

* [PATCH 06/30] iommu: Make iommu_ops_from_fwnode() return the iommu_device
  2023-11-30  1:10 [PATCH 00/30] Make a new API for drivers to use to get their FW Jason Gunthorpe
                   ` (4 preceding siblings ...)
  2023-11-30  1:10 ` [PATCH 05/30] iommu: Add iommu_probe_info Jason Gunthorpe
@ 2023-11-30  1:10 ` Jason Gunthorpe
  2023-11-30  1:10 ` [PATCH 07/30] iommu/of: Call of_iommu_get_resv_regions() directly Jason Gunthorpe
                   ` (23 subsequent siblings)
  29 siblings, 0 replies; 34+ messages in thread
From: Jason Gunthorpe @ 2023-11-30  1:10 UTC (permalink / raw)
  To: acpica-devel, Andy Gross, Alim Akhtar, Alyssa Rosenzweig,
	Bjorn Andersson, AngeloGioacchino Del Regno, asahi, Baolin Wang,
	devicetree, Frank Rowand, Hanjun Guo, Gustavo A. R. Silva,
	Heiko Stuebner, iommu, Jean-Philippe Brucker, Jernej Skrabec,
	Jonathan Hunter, Joerg Roedel, Kees Cook, Konrad Dybcio,
	Krzysztof Kozlowski, Len Brown, linux-acpi, linux-arm-kernel,
	linux-arm-msm, linux-hardening, linux-mediatek, linux-rockchip,
	linux-samsung-soc, linux-sunxi, linux-tegra, Lorenzo Pieralisi,
	Marek Szyprowski, Hector Martin, Matthias Brugger, Orson Zhai,
	Rafael J. Wysocki, Rob Clark, Robert Moore, Rob Herring,
	Robin Murphy, Samuel Holland, Sudeep Holla, Sven Peter,
	Thierry Reding, Krishna Reddy, virtualization, Chen-Yu Tsai,
	Will Deacon, Yong Wu, Chunyan Zhang
  Cc: André Draszik, patches

Return the entire struct iommu_device instead of just the ops. Name
the changed function iommu_device_from_fwnode().

The iommu_device pointer is kept valid because this is always called
under the iommu_probe_device_lock.

If iommu_device is valid then ops is valid too, the module refcounting
is pointless. Remove it.

Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
---
 drivers/acpi/arm64/iort.c    | 12 +++++++-----
 drivers/acpi/viot.c          |  9 +++++----
 drivers/iommu/iommu.c        | 20 +++++++++++++-------
 drivers/iommu/of_iommu.c     | 16 ++++++----------
 include/linux/iommu-driver.h |  3 +++
 include/linux/iommu.h        |  7 -------
 6 files changed, 34 insertions(+), 33 deletions(-)

diff --git a/drivers/acpi/arm64/iort.c b/drivers/acpi/arm64/iort.c
index 93e30f2f5004f0..798c0b344f4be8 100644
--- a/drivers/acpi/arm64/iort.c
+++ b/drivers/acpi/arm64/iort.c
@@ -797,6 +797,8 @@ void acpi_configure_pmsi_domain(struct device *dev)
 }
 
 #ifdef CONFIG_IOMMU_API
+#include <linux/iommu-driver.h>
+
 static void iort_rmr_free(struct device *dev,
 			  struct iommu_resv_region *region)
 {
@@ -1242,7 +1244,7 @@ static int iort_iommu_xlate(struct acpi_iort_node *node, u32 streamid,
 			    void *info)
 {
 	struct device *dev = info;
-	const struct iommu_ops *ops;
+	struct iommu_device *iommu;
 	struct fwnode_handle *iort_fwnode;
 
 	if (!node)
@@ -1253,19 +1255,19 @@ static int iort_iommu_xlate(struct acpi_iort_node *node, u32 streamid,
 		return -ENODEV;
 
 	/*
-	 * If the ops look-up fails, this means that either
+	 * If the iommu look-up fails, this means that either
 	 * the SMMU drivers have not been probed yet or that
 	 * the SMMU drivers are not built in the kernel;
 	 * Depending on whether the SMMU drivers are built-in
 	 * in the kernel or not, defer the IOMMU configuration
 	 * or just abort it.
 	 */
-	ops = iommu_ops_from_fwnode(iort_fwnode);
-	if (!ops)
+	iommu = iommu_device_from_fwnode(iort_fwnode);
+	if (!iommu)
 		return iort_iommu_driver_enabled(node->type) ?
 		       -EPROBE_DEFER : -ENODEV;
 
-	return acpi_iommu_fwspec_init(dev, streamid, iort_fwnode, ops);
+	return acpi_iommu_fwspec_init(dev, streamid, iort_fwnode, iommu->ops);
 }
 
 struct iort_pci_alias_info {
diff --git a/drivers/acpi/viot.c b/drivers/acpi/viot.c
index 7ab35ef05c84e0..9780b1d477503e 100644
--- a/drivers/acpi/viot.c
+++ b/drivers/acpi/viot.c
@@ -21,6 +21,7 @@
 #include <linux/acpi_viot.h>
 #include <linux/fwnode.h>
 #include <linux/iommu.h>
+#include <linux/iommu-driver.h>
 #include <linux/list.h>
 #include <linux/pci.h>
 #include <linux/platform_device.h>
@@ -299,7 +300,7 @@ void __init acpi_viot_init(void)
 
 static int viot_dev_iommu_init(struct viot_iommu *viommu, u32 epid, void *info)
 {
-	const struct iommu_ops *ops;
+	struct iommu_device *iommu;
 	struct device *dev = info;
 
 	if (!viommu)
@@ -309,12 +310,12 @@ static int viot_dev_iommu_init(struct viot_iommu *viommu, u32 epid, void *info)
 	if (device_match_fwnode(dev, viommu->fwnode))
 		return -EINVAL;
 
-	ops = iommu_ops_from_fwnode(viommu->fwnode);
-	if (!ops)
+	iommu = iommu_device_from_fwnode(viommu->fwnode);
+	if (!iommu)
 		return IS_ENABLED(CONFIG_VIRTIO_IOMMU) ?
 			-EPROBE_DEFER : -ENODEV;
 
-	return acpi_iommu_fwspec_init(dev, epid, viommu->fwnode, ops);
+	return acpi_iommu_fwspec_init(dev, epid, viommu->fwnode, iommu->ops);
 }
 
 struct viot_pci_iommu_alias_info {
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index 76b245973cfafc..45e6543748fd46 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -520,13 +520,19 @@ static int __iommu_probe_device(struct iommu_probe_info *pinf)
 	 * ops for probing, and thus cheekily co-opt the same mechanism.
 	 */
 	fwspec = dev_iommu_fwspec_get(dev);
-	if (fwspec && fwspec->ops)
+	if (fwspec && fwspec->ops) {
 		ops = fwspec->ops;
-	else
-		ops = iommu_ops_from_fwnode(NULL);
+		if (!ops)
+			return -ENODEV;
+	} else {
+		struct iommu_device *iommu;
+
+		iommu = iommu_device_from_fwnode(NULL);
+		if (!iommu)
+			return -ENODEV;
+		ops = iommu->ops;
+	}
 
-	if (!ops)
-		return -ENODEV;
 	/*
 	 * Serialise to avoid races between IOMMU drivers registering in
 	 * parallel and/or the "replay" calls from ACPI/OF code via client
@@ -2997,7 +3003,7 @@ bool iommu_default_passthrough(void)
 }
 EXPORT_SYMBOL_GPL(iommu_default_passthrough);
 
-const struct iommu_ops *iommu_ops_from_fwnode(struct fwnode_handle *fwnode)
+struct iommu_device *iommu_device_from_fwnode(struct fwnode_handle *fwnode)
 {
 	struct iommu_device *iommu;
 
@@ -3005,7 +3011,7 @@ const struct iommu_ops *iommu_ops_from_fwnode(struct fwnode_handle *fwnode)
 
 	list_for_each_entry(iommu, &iommu_device_list, list)
 		if (iommu->fwnode == fwnode)
-			return iommu->ops;
+			return iommu;
 	return NULL;
 }
 
diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c
index fb743ddd239e0b..cf68cdebc9f318 100644
--- a/drivers/iommu/of_iommu.c
+++ b/drivers/iommu/of_iommu.c
@@ -21,16 +21,16 @@
 static int of_iommu_xlate(struct of_phandle_args *iommu_spec, void *info)
 {
 	struct device *dev = info;
-	const struct iommu_ops *ops;
+	struct iommu_device *iommu;
 	struct fwnode_handle *fwnode = &iommu_spec->np->fwnode;
 	int ret;
 
-	ops = iommu_ops_from_fwnode(fwnode);
-	if ((ops && !ops->of_xlate) ||
+	iommu = iommu_device_from_fwnode(fwnode);
+	if ((iommu && !iommu->ops->of_xlate) ||
 	    !of_device_is_available(iommu_spec->np))
 		return -ENODEV;
 
-	ret = iommu_fwspec_init(dev, &iommu_spec->np->fwnode, ops);
+	ret = iommu_fwspec_init(dev, &iommu_spec->np->fwnode, iommu->ops);
 	if (ret)
 		return ret;
 	/*
@@ -38,14 +38,10 @@ static int of_iommu_xlate(struct of_phandle_args *iommu_spec, void *info)
 	 * IOMMU device we're waiting for, which will be useful if we ever get
 	 * a proper probe-ordering dependency mechanism in future.
 	 */
-	if (!ops)
+	if (!iommu)
 		return driver_deferred_probe_check_state(dev);
 
-	if (!try_module_get(ops->owner))
-		return -ENODEV;
-
-	ret = ops->of_xlate(dev, iommu_spec);
-	module_put(ops->owner);
+	ret = iommu->ops->of_xlate(dev, iommu_spec);
 	return ret;
 }
 
diff --git a/include/linux/iommu-driver.h b/include/linux/iommu-driver.h
index b85c9f15cf478b..636b5b5f18f76f 100644
--- a/include/linux/iommu-driver.h
+++ b/include/linux/iommu-driver.h
@@ -14,6 +14,8 @@
 
 #include <linux/types.h>
 
+struct fwnode_handle;
+
 struct iommu_probe_info {
 	struct device *dev;
 	struct list_head *deferred_group_list;
@@ -21,5 +23,6 @@ struct iommu_probe_info {
 };
 
 int iommu_probe_device_pinf(struct iommu_probe_info *pinf);
+struct iommu_device *iommu_device_from_fwnode(struct fwnode_handle *fwnode);
 
 #endif
diff --git a/include/linux/iommu.h b/include/linux/iommu.h
index cf578b8e0b59a4..f0aaf55db3c09b 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -819,7 +819,6 @@ 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);
-const struct iommu_ops *iommu_ops_from_fwnode(struct fwnode_handle *fwnode);
 
 static inline struct iommu_fwspec *dev_iommu_fwspec_get(struct device *dev)
 {
@@ -1168,12 +1167,6 @@ static inline int iommu_fwspec_add_ids(struct device *dev, u32 *ids,
 	return -ENODEV;
 }
 
-static inline
-const struct iommu_ops *iommu_ops_from_fwnode(struct fwnode_handle *fwnode)
-{
-	return NULL;
-}
-
 static inline int
 iommu_dev_enable_feature(struct device *dev, enum iommu_dev_features feat)
 {
-- 
2.42.0


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

* [PATCH 07/30] iommu/of: Call of_iommu_get_resv_regions() directly
  2023-11-30  1:10 [PATCH 00/30] Make a new API for drivers to use to get their FW Jason Gunthorpe
                   ` (5 preceding siblings ...)
  2023-11-30  1:10 ` [PATCH 06/30] iommu: Make iommu_ops_from_fwnode() return the iommu_device Jason Gunthorpe
@ 2023-11-30  1:10 ` Jason Gunthorpe
  2023-11-30  1:10 ` [PATCH 08/30] iommu/of: Add iommu_of_get_single_iommu() Jason Gunthorpe
                   ` (22 subsequent siblings)
  29 siblings, 0 replies; 34+ messages in thread
From: Jason Gunthorpe @ 2023-11-30  1:10 UTC (permalink / raw)
  To: acpica-devel, Andy Gross, Alim Akhtar, Alyssa Rosenzweig,
	Bjorn Andersson, AngeloGioacchino Del Regno, asahi, Baolin Wang,
	devicetree, Frank Rowand, Hanjun Guo, Gustavo A. R. Silva,
	Heiko Stuebner, iommu, Jean-Philippe Brucker, Jernej Skrabec,
	Jonathan Hunter, Joerg Roedel, Kees Cook, Konrad Dybcio,
	Krzysztof Kozlowski, Len Brown, linux-acpi, linux-arm-kernel,
	linux-arm-msm, linux-hardening, linux-mediatek, linux-rockchip,
	linux-samsung-soc, linux-sunxi, linux-tegra, Lorenzo Pieralisi,
	Marek Szyprowski, Hector Martin, Matthias Brugger, Orson Zhai,
	Rafael J. Wysocki, Rob Clark, Robert Moore, Rob Herring,
	Robin Murphy, Samuel Holland, Sudeep Holla, Sven Peter,
	Thierry Reding, Krishna Reddy, virtualization, Chen-Yu Tsai,
	Will Deacon, Yong Wu, Chunyan Zhang
  Cc: André Draszik, patches

virtio-iommu and dart already parse the ACPI firmware description in their
own get_resv_regions() callback. They just need to parse the OF
description.

The generic iommu_dma_get_resv_regions() really just knows how to parse
the IORT ACPI in addition to OF.

Directly call of_iommu_get_resv_regions() instead.

Move the declaration of of_iommu_get_resv_regions() to iommu-driver.h
since it is now intended to be called by drivers.

Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
---
 drivers/iommu/apple-dart.c   | 5 ++---
 drivers/iommu/dma-iommu.c    | 5 ++---
 drivers/iommu/of_iommu.c     | 3 +++
 drivers/iommu/virtio-iommu.c | 5 ++---
 include/linux/iommu-driver.h | 9 +++++++++
 include/linux/of_iommu.h     | 8 --------
 6 files changed, 18 insertions(+), 17 deletions(-)

diff --git a/drivers/iommu/apple-dart.c b/drivers/iommu/apple-dart.c
index 25135440b5dd54..bb0e5a4577fc03 100644
--- a/drivers/iommu/apple-dart.c
+++ b/drivers/iommu/apple-dart.c
@@ -20,6 +20,7 @@
 #include <linux/interrupt.h>
 #include <linux/io-pgtable.h>
 #include <linux/iommu.h>
+#include <linux/iommu-driver.h>
 #include <linux/iopoll.h>
 #include <linux/module.h>
 #include <linux/of.h>
@@ -32,8 +33,6 @@
 #include <linux/swab.h>
 #include <linux/types.h>
 
-#include "dma-iommu.h"
-
 #define DART_MAX_STREAMS 256
 #define DART_MAX_TTBR 4
 #define MAX_DARTS_PER_DEVICE 2
@@ -972,7 +971,7 @@ static void apple_dart_get_resv_regions(struct device *dev,
 		list_add_tail(&region->list, head);
 	}
 
-	iommu_dma_get_resv_regions(dev, head);
+	of_iommu_get_resv_regions(dev, head);
 }
 
 static const struct iommu_ops apple_dart_iommu_ops = {
diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index d644b0502ef48e..5a828c92cd38b2 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -23,7 +23,7 @@
 #include <linux/memremap.h>
 #include <linux/mm.h>
 #include <linux/mutex.h>
-#include <linux/of_iommu.h>
+#include <linux/iommu-driver.h>
 #include <linux/pci.h>
 #include <linux/scatterlist.h>
 #include <linux/spinlock.h>
@@ -475,8 +475,7 @@ void iommu_dma_get_resv_regions(struct device *dev, struct list_head *list)
 					    fwspec->ids, fwspec->num_ids);
 	}
 
-	if (dev->of_node)
-		of_iommu_get_resv_regions(dev, list);
+	of_iommu_get_resv_regions(dev, list);
 }
 EXPORT_SYMBOL(iommu_dma_get_resv_regions);
 
diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c
index cf68cdebc9f318..20266a8edd5c71 100644
--- a/drivers/iommu/of_iommu.c
+++ b/drivers/iommu/of_iommu.c
@@ -217,6 +217,9 @@ void of_iommu_get_resv_regions(struct device *dev, struct list_head *list)
 	struct of_phandle_iterator it;
 	int err;
 
+	if (!dev->of_node)
+		return;
+
 	of_for_each_phandle(&it, err, dev->of_node, "memory-region", NULL, 0) {
 		const __be32 *maps, *end;
 		struct resource phys;
diff --git a/drivers/iommu/virtio-iommu.c b/drivers/iommu/virtio-iommu.c
index 34db37fd9675cd..b1a7b14a6c7a2f 100644
--- a/drivers/iommu/virtio-iommu.c
+++ b/drivers/iommu/virtio-iommu.c
@@ -12,6 +12,7 @@
 #include <linux/freezer.h>
 #include <linux/interval_tree.h>
 #include <linux/iommu.h>
+#include <linux/iommu-driver.h>
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/pci.h>
@@ -22,8 +23,6 @@
 
 #include <uapi/linux/virtio_iommu.h>
 
-#include "dma-iommu.h"
-
 #define MSI_IOVA_BASE			0x8000000
 #define MSI_IOVA_LENGTH			0x100000
 
@@ -969,7 +968,7 @@ static void viommu_get_resv_regions(struct device *dev, struct list_head *head)
 		list_add_tail(&msi->list, head);
 	}
 
-	iommu_dma_get_resv_regions(dev, head);
+	of_iommu_get_resv_regions(dev, head);
 }
 
 static struct iommu_ops viommu_ops;
diff --git a/include/linux/iommu-driver.h b/include/linux/iommu-driver.h
index 636b5b5f18f76f..c572620d3069b4 100644
--- a/include/linux/iommu-driver.h
+++ b/include/linux/iommu-driver.h
@@ -25,4 +25,13 @@ struct iommu_probe_info {
 int iommu_probe_device_pinf(struct iommu_probe_info *pinf);
 struct iommu_device *iommu_device_from_fwnode(struct fwnode_handle *fwnode);
 
+#if IS_ENABLED(CONFIG_OF_IOMMU)
+void of_iommu_get_resv_regions(struct device *dev, struct list_head *list);
+#else
+static inline void of_iommu_get_resv_regions(struct device *dev,
+					     struct list_head *list)
+{
+}
+#endif
+
 #endif
diff --git a/include/linux/of_iommu.h b/include/linux/of_iommu.h
index e61cbbe12dac6f..9d5532f2f11486 100644
--- a/include/linux/of_iommu.h
+++ b/include/linux/of_iommu.h
@@ -11,9 +11,6 @@ struct iommu_ops;
 extern int of_iommu_configure(struct device *dev, struct device_node *master_np,
 			      const u32 *id);
 
-extern void of_iommu_get_resv_regions(struct device *dev,
-				      struct list_head *list);
-
 #else
 
 static inline int of_iommu_configure(struct device *dev,
@@ -23,11 +20,6 @@ static inline int of_iommu_configure(struct device *dev,
 	return -ENODEV;
 }
 
-static inline void of_iommu_get_resv_regions(struct device *dev,
-					     struct list_head *list)
-{
-}
-
 #endif	/* CONFIG_OF_IOMMU */
 
 #endif /* __OF_IOMMU_H */
-- 
2.42.0


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

* [PATCH 08/30] iommu/of: Add iommu_of_get_single_iommu()
  2023-11-30  1:10 [PATCH 00/30] Make a new API for drivers to use to get their FW Jason Gunthorpe
                   ` (6 preceding siblings ...)
  2023-11-30  1:10 ` [PATCH 07/30] iommu/of: Call of_iommu_get_resv_regions() directly Jason Gunthorpe
@ 2023-11-30  1:10 ` Jason Gunthorpe
  2023-11-30  1:10 ` [PATCH 09/30] iommu/rockchip: Move to iommu_of_get_single_iommu() Jason Gunthorpe
                   ` (21 subsequent siblings)
  29 siblings, 0 replies; 34+ messages in thread
From: Jason Gunthorpe @ 2023-11-30  1:10 UTC (permalink / raw)
  To: acpica-devel, Andy Gross, Alim Akhtar, Alyssa Rosenzweig,
	Bjorn Andersson, AngeloGioacchino Del Regno, asahi, Baolin Wang,
	devicetree, Frank Rowand, Hanjun Guo, Gustavo A. R. Silva,
	Heiko Stuebner, iommu, Jean-Philippe Brucker, Jernej Skrabec,
	Jonathan Hunter, Joerg Roedel, Kees Cook, Konrad Dybcio,
	Krzysztof Kozlowski, Len Brown, linux-acpi, linux-arm-kernel,
	linux-arm-msm, linux-hardening, linux-mediatek, linux-rockchip,
	linux-samsung-soc, linux-sunxi, linux-tegra, Lorenzo Pieralisi,
	Marek Szyprowski, Hector Martin, Matthias Brugger, Orson Zhai,
	Rafael J. Wysocki, Rob Clark, Robert Moore, Rob Herring,
	Robin Murphy, Samuel Holland, Sudeep Holla, Sven Peter,
	Thierry Reding, Krishna Reddy, virtualization, Chen-Yu Tsai,
	Will Deacon, Yong Wu, Chunyan Zhang
  Cc: André Draszik, patches

This function can be called by drivers in their probe function to return a
single iommu_device instance associated with the current probe.

All drivers need a way to get the iommu_device instance the FW says the
device should be using. Wrap the function with a macro that does the
container_of().

The driver indicates what instances it accepts by passing in its ops.

num_cells is provided to validate that the args are correctly sized.

This function is all that is required by drivers that only support a
single IOMMU instance and no IDs data. Driver's should follow a typical
pattern in their probe_device:

	iommu = iommu_of_get_single_iommu(pinf, &rk_iommu_ops, -1,
					  struct rk_iommu, iommu);
	if (IS_ERR(iommu)) return ERR_CAST(iommu);

	data = kzalloc(sizeof(*data), GFP_KERNEL);
	if (!data) return ERR_PTR(-ENOMEM);
	[..]
	dev_iommu_priv_set(dev, data);
        return &iommu->iommu;

Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
---
 drivers/acpi/scan.c          |  1 +
 drivers/iommu/iommu.c        | 52 ++++++++++++++++++++++
 drivers/iommu/of_iommu.c     | 59 +++++++++++++++++++++++++
 include/linux/iommu-driver.h | 85 ++++++++++++++++++++++++++++++++++++
 4 files changed, 197 insertions(+)

diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index 9c13df632aa5e0..de36299c3b75bf 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -1570,6 +1570,7 @@ static int acpi_iommu_configure_id(struct device *dev, const u32 *id_in)
 	const struct iommu_ops *ops;
 	struct iommu_probe_info pinf = {
 		.dev = dev,
+		.is_dma_configure = true,
 	};
 
 	/* Serialise to make dev->iommu stable under our potential fwspec */
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index 45e6543748fd46..ca411ad14c1182 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -3015,6 +3015,58 @@ struct iommu_device *iommu_device_from_fwnode(struct fwnode_handle *fwnode)
 	return NULL;
 }
 
+/*
+ * Helper for FW interfaces to parse the fwnode into an iommu_driver. This
+ * caches past search results to avoid re-searching the linked list and computes
+ * if the FW is describing a single or multi-instance ID list.
+ */
+struct iommu_device *
+iommu_device_from_fwnode_pinf(struct iommu_probe_info *pinf,
+			      const struct iommu_ops *ops,
+			      struct fwnode_handle *fwnode)
+{
+	struct iommu_device *iommu = pinf->cached_iommu;
+
+	if (!pinf->num_ids)
+		pinf->cached_single_iommu = true;
+
+	if (!iommu || iommu->fwnode != fwnode) {
+		iommu = iommu_device_from_fwnode(fwnode);
+		if (!iommu)
+			return ERR_PTR(
+				driver_deferred_probe_check_state(pinf->dev));
+		pinf->cached_iommu = iommu;
+		if (pinf->num_ids)
+			pinf->cached_single_iommu = false;
+	}
+
+	/* NULL ops is used for the -EPROBE_DEFER check, match everything */
+	if (ops && iommu->ops != ops) {
+		if (!pinf->num_ids)
+			return ERR_PTR(-ENODEV);
+		dev_err(pinf->dev,
+			FW_BUG
+			"One device in the FW has iommu's with different Linux drivers, expecting %ps FW wants %ps.",
+			ops, iommu->ops);
+		return ERR_PTR(-EINVAL);
+	}
+	return iommu;
+}
+
+struct iommu_device *iommu_fw_finish_get_single(struct iommu_probe_info *pinf)
+{
+	if (WARN_ON(!pinf->num_ids || !pinf->cached_iommu))
+		return ERR_PTR(-EINVAL);
+	if (!pinf->cached_single_iommu) {
+		dev_err(pinf->dev,
+			FW_BUG
+			"The iommu driver %ps expects only one iommu instance, the FW has more.\n",
+			pinf->cached_iommu->ops);
+		return ERR_PTR(-EINVAL);
+	}
+	return pinf->cached_iommu;
+}
+
 int iommu_fwspec_init(struct device *dev, struct fwnode_handle *iommu_fwnode,
 		      const struct iommu_ops *ops)
 {
diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c
index 20266a8edd5c71..37af32a6bc84e5 100644
--- a/drivers/iommu/of_iommu.c
+++ b/drivers/iommu/of_iommu.c
@@ -138,6 +138,9 @@ int of_iommu_configure(struct device *dev, struct device_node *master_np,
 {
 	struct iommu_probe_info pinf = {
 		.dev = dev,
+		.of_master_np = master_np,
+		.of_map_id = id,
+		.is_dma_configure = true,
 	};
 	struct iommu_fwspec *fwspec;
 	int err;
@@ -277,3 +280,59 @@ void of_iommu_get_resv_regions(struct device *dev, struct list_head *list)
 #endif
 }
 EXPORT_SYMBOL(of_iommu_get_resv_regions);
+
+struct parse_info {
+	struct iommu_probe_info *pinf;
+	const struct iommu_ops *ops;
+	int num_cells;
+};
+
+static struct iommu_device *parse_iommu(struct parse_info *info,
+					struct of_phandle_args *iommu_spec)
+{
+	if (!of_device_is_available(iommu_spec->np))
+		return ERR_PTR(-ENODEV);
+
+	if (info->num_cells != -1 && iommu_spec->args_count != info->num_cells) {
+		dev_err(info->pinf->dev,
+			FW_BUG
+			"Driver %ps expects number of cells %u but DT has %u\n",
+			info->ops, info->num_cells, iommu_spec->args_count);
+		return ERR_PTR(-EINVAL);
+	}
+	return iommu_device_from_fwnode_pinf(info->pinf, info->ops,
+					     &iommu_spec->np->fwnode);
+}
+
+static int parse_single_iommu(struct of_phandle_args *iommu_spec, void *_info)
+{
+	struct parse_info *info = _info;
+	struct iommu_device *iommu;
+
+	iommu = parse_iommu(info, iommu_spec);
+	if (IS_ERR(iommu))
+		return PTR_ERR(iommu);
+	info->pinf->num_ids++;
+	return 0;
+}
+
+struct iommu_device *__iommu_of_get_single_iommu(struct iommu_probe_info *pinf,
+						 const struct iommu_ops *ops,
+						 int num_cells)
+{
+	struct parse_info info = { .pinf = pinf,
+				   .ops = ops,
+				   .num_cells = num_cells };
+	int err;
+
+	if (!pinf->is_dma_configure || !pinf->of_master_np)
+		return ERR_PTR(-ENODEV);
+
+	iommu_fw_clear_cache(pinf);
+	err = of_iommu_for_each_id(pinf->dev, pinf->of_master_np,
+				   pinf->of_map_id, parse_single_iommu, &info);
+	if (err)
+		return ERR_PTR(err);
+	return iommu_fw_finish_get_single(pinf);
+}
+EXPORT_SYMBOL_GPL(__iommu_of_get_single_iommu);
diff --git a/include/linux/iommu-driver.h b/include/linux/iommu-driver.h
index c572620d3069b4..597998a62b0dd6 100644
--- a/include/linux/iommu-driver.h
+++ b/include/linux/iommu-driver.h
@@ -13,25 +13,110 @@
 #endif
 
 #include <linux/types.h>
+#include <linux/err.h>
+#include <linux/slab.h>
 
+struct of_phandle_args;
 struct fwnode_handle;
+struct iommu_device;
+struct iommu_ops;
+
+/*
+ * FIXME this is sort of like container_of_safe() that was removed, do we want
+ * to put it in the common header?
+ */
+#define container_of_err(ptr, type, member)                       \
+	({                                                        \
+		void *__mptr = (void *)(ptr);                     \
+								  \
+		(offsetof(type, member) != 0 && IS_ERR(__mptr)) ? \
+			(type *)ERR_CAST(__mptr) :                \
+			container_of(ptr, type, member);          \
+	})
 
 struct iommu_probe_info {
 	struct device *dev;
 	struct list_head *deferred_group_list;
+	struct iommu_device *cached_iommu;
+	struct device_node *of_master_np;
+	const u32 *of_map_id;
+	unsigned int num_ids;
 	bool defer_setup : 1;
+	bool is_dma_configure : 1;
+	bool cached_single_iommu : 1;
 };
 
+static inline void iommu_fw_clear_cache(struct iommu_probe_info *pinf)
+{
+	pinf->num_ids = 0;
+	pinf->cached_single_iommu = true;
+}
+
 int iommu_probe_device_pinf(struct iommu_probe_info *pinf);
 struct iommu_device *iommu_device_from_fwnode(struct fwnode_handle *fwnode);
+struct iommu_device *
+iommu_device_from_fwnode_pinf(struct iommu_probe_info *pinf,
+			      const struct iommu_ops *ops,
+			      struct fwnode_handle *fwnode);
+struct iommu_device *iommu_fw_finish_get_single(struct iommu_probe_info *pinf);
 
 #if IS_ENABLED(CONFIG_OF_IOMMU)
 void of_iommu_get_resv_regions(struct device *dev, struct list_head *list);
+
+struct iommu_device *__iommu_of_get_single_iommu(struct iommu_probe_info *pinf,
+						 const struct iommu_ops *ops,
+						 int num_cells);
 #else
 static inline void of_iommu_get_resv_regions(struct device *dev,
 					     struct list_head *list)
 {
 }
+static inline
+struct iommu_device *__iommu_of_get_single_iommu(struct iommu_probe_info *pinf,
+						 const struct iommu_ops *ops,
+						 int num_cells)
+{
+	return ERR_PTR(-ENODEV);
+}
 #endif
 
+/**
+ * iommu_of_get_single_iommu - Return the driver's iommu instance
+ * @pinf: The iommu_probe_info
+ * @ops: The ops the iommu instance must have
+ * @num_cells: #iommu-cells value to enforce, -1 is no check
+ * @drv_struct: The driver struct containing the struct iommu_device
+ * @member: The name of the iommu_device member
+ *
+ * Parse the OF table describing the iommus and return a pointer to the driver's
+ * iommu_device struct that the OF table points to. Check that the OF table is
+ * well formed with a single iommu for all the entries and that the table refers
+ * to this iommu driver. Integrates a container_of() to simplify all users.
+ */
+#define iommu_of_get_single_iommu(pinf, ops, num_cells, drv_struct, member)  \
+	container_of_err(__iommu_of_get_single_iommu(pinf, ops, num_cells), \
+			  drv_struct, member)
+
+/**
+ * iommu_of_num_ids - Return the number of iommu associations the FW has
+ * @pinf: The iommu_probe_info
+ *
+ * For drivers using iommu_of_get_single_iommu() this will return the number
+ * of ids associated with the iommu instance. For other cases this will return
+ * the sum of all ids across all instances. Returns >= 1.
+ */
+static inline unsigned int iommu_of_num_ids(struct iommu_probe_info *pinf)
+{
+	return pinf->num_ids;
+}
+
+/*
+ * Used temporarily to indicate drivers that have moved to the new probe method.
+ */
+static inline int iommu_dummy_of_xlate(struct device *dev,
+				       struct of_phandle_args *args)
+{
+	return 0;
+}
+
 #endif
-- 
2.42.0


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

* [PATCH 09/30] iommu/rockchip: Move to iommu_of_get_single_iommu()
  2023-11-30  1:10 [PATCH 00/30] Make a new API for drivers to use to get their FW Jason Gunthorpe
                   ` (7 preceding siblings ...)
  2023-11-30  1:10 ` [PATCH 08/30] iommu/of: Add iommu_of_get_single_iommu() Jason Gunthorpe
@ 2023-11-30  1:10 ` Jason Gunthorpe
  2023-11-30  1:10 ` [PATCH 10/30] iommu/sprd: " Jason Gunthorpe
                   ` (20 subsequent siblings)
  29 siblings, 0 replies; 34+ messages in thread
From: Jason Gunthorpe @ 2023-11-30  1:10 UTC (permalink / raw)
  To: acpica-devel, Andy Gross, Alim Akhtar, Alyssa Rosenzweig,
	Bjorn Andersson, AngeloGioacchino Del Regno, asahi, Baolin Wang,
	devicetree, Frank Rowand, Hanjun Guo, Gustavo A. R. Silva,
	Heiko Stuebner, iommu, Jean-Philippe Brucker, Jernej Skrabec,
	Jonathan Hunter, Joerg Roedel, Kees Cook, Konrad Dybcio,
	Krzysztof Kozlowski, Len Brown, linux-acpi, linux-arm-kernel,
	linux-arm-msm, linux-hardening, linux-mediatek, linux-rockchip,
	linux-samsung-soc, linux-sunxi, linux-tegra, Lorenzo Pieralisi,
	Marek Szyprowski, Hector Martin, Matthias Brugger, Orson Zhai,
	Rafael J. Wysocki, Rob Clark, Robert Moore, Rob Herring,
	Robin Murphy, Samuel Holland, Sudeep Holla, Sven Peter,
	Thierry Reding, Krishna Reddy, virtualization, Chen-Yu Tsai,
	Will Deacon, Yong Wu, Chunyan Zhang
  Cc: André Draszik, patches

rockchip supports a single iommu instance and does not support multiple
IDs.

Move the per-device allocation from rk_iommu_of_xlate() and completely
delete rk_iommu_of_xlate(). The iommu instance is obtained via
iommu_of_get_single_iommu().

Don't use devm to manage the lifetime of the per-device data, this just
results in memory leaking if there are probe error/retry paths. Use the
normal lifecycle with alloc in probe_device and free in release_device.

The comment about "virtual devices" seems out of date. With today's code
the core will not call attach_device/detach_device unless dev->iommu is
set and has an ops. This can only happen if probe_device was done. Remove
the checks.

Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
---
 drivers/iommu/rockchip-iommu.c | 74 +++++++++++-----------------------
 1 file changed, 24 insertions(+), 50 deletions(-)

diff --git a/drivers/iommu/rockchip-iommu.c b/drivers/iommu/rockchip-iommu.c
index 2685861c0a1262..4cff06a2a24f74 100644
--- a/drivers/iommu/rockchip-iommu.c
+++ b/drivers/iommu/rockchip-iommu.c
@@ -15,6 +15,7 @@
 #include <linux/interrupt.h>
 #include <linux/io.h>
 #include <linux/iommu.h>
+#include <linux/iommu-driver.h>
 #include <linux/iopoll.h>
 #include <linux/list.h>
 #include <linux/mm.h>
@@ -123,6 +124,7 @@ struct rk_iommudata {
 static struct device *dma_dev;
 static const struct rk_iommu_ops *rk_ops;
 static struct iommu_domain rk_identity_domain;
+static const struct iommu_ops rk_iommu_ops;
 
 static inline void rk_table_flush(struct rk_iommu_domain *dom, dma_addr_t dma,
 				  unsigned int count)
@@ -896,13 +898,6 @@ static size_t rk_iommu_unmap(struct iommu_domain *domain, unsigned long _iova,
 	return unmap_size;
 }
 
-static struct rk_iommu *rk_iommu_from_dev(struct device *dev)
-{
-	struct rk_iommudata *data = dev_iommu_priv_get(dev);
-
-	return data ? data->iommu : NULL;
-}
-
 /* Must be called with iommu powered on and attached */
 static void rk_iommu_disable(struct rk_iommu *iommu)
 {
@@ -958,16 +953,12 @@ static int rk_iommu_enable(struct rk_iommu *iommu)
 static int rk_iommu_identity_attach(struct iommu_domain *identity_domain,
 				    struct device *dev)
 {
-	struct rk_iommu *iommu;
+	struct rk_iommudata *data = dev_iommu_priv_get(dev);
+	struct rk_iommu *iommu = data->iommu;
 	struct rk_iommu_domain *rk_domain;
 	unsigned long flags;
 	int ret;
 
-	/* Allow 'virtual devices' (eg drm) to detach from domain */
-	iommu = rk_iommu_from_dev(dev);
-	if (!iommu)
-		return -ENODEV;
-
 	rk_domain = to_rk_domain(iommu->domain);
 
 	dev_dbg(dev, "Detaching from iommu domain\n");
@@ -1003,19 +994,12 @@ static struct iommu_domain rk_identity_domain = {
 static int rk_iommu_attach_device(struct iommu_domain *domain,
 		struct device *dev)
 {
-	struct rk_iommu *iommu;
+	struct rk_iommudata *data = dev_iommu_priv_get(dev);
+	struct rk_iommu *iommu = data->iommu;
 	struct rk_iommu_domain *rk_domain = to_rk_domain(domain);
 	unsigned long flags;
 	int ret;
 
-	/*
-	 * Allow 'virtual devices' (e.g., drm) to attach to domain.
-	 * Such a device does not belong to an iommu group.
-	 */
-	iommu = rk_iommu_from_dev(dev);
-	if (!iommu)
-		return 0;
-
 	dev_dbg(dev, "Attaching to iommu domain\n");
 
 	/* iommu already attached */
@@ -1115,20 +1099,30 @@ static void rk_iommu_domain_free(struct iommu_domain *domain)
 	kfree(rk_domain);
 }
 
-static struct iommu_device *rk_iommu_probe_device(struct device *dev)
+static struct iommu_device *rk_iommu_probe_device(struct iommu_probe_info *pinf)
 {
+	struct device *dev = pinf->dev;
 	struct rk_iommudata *data;
 	struct rk_iommu *iommu;
 
-	data = dev_iommu_priv_get(dev);
-	if (!data)
-		return ERR_PTR(-ENODEV);
+	iommu = iommu_of_get_single_iommu(pinf, &rk_iommu_ops, -1,
+					  struct rk_iommu, iommu);
+	if (IS_ERR(iommu))
+		return ERR_CAST(iommu);
+	if (iommu_of_num_ids(pinf) != 1)
+		return ERR_PTR(-EINVAL);
 
-	iommu = rk_iommu_from_dev(dev);
+	data = kzalloc(sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return ERR_PTR(-ENOMEM);
+	data->iommu = iommu;
+	data->iommu->domain = &rk_identity_domain;
 
 	data->link = device_link_add(dev, iommu->dev,
 				     DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME);
 
+	dev_iommu_priv_set(dev, data);
+
 	return &iommu->iommu;
 }
 
@@ -1137,37 +1131,17 @@ static void rk_iommu_release_device(struct device *dev)
 	struct rk_iommudata *data = dev_iommu_priv_get(dev);
 
 	device_link_del(data->link);
-}
-
-static int rk_iommu_of_xlate(struct device *dev,
-			     struct of_phandle_args *args)
-{
-	struct platform_device *iommu_dev;
-	struct rk_iommudata *data;
-
-	data = devm_kzalloc(dma_dev, sizeof(*data), GFP_KERNEL);
-	if (!data)
-		return -ENOMEM;
-
-	iommu_dev = of_find_device_by_node(args->np);
-
-	data->iommu = platform_get_drvdata(iommu_dev);
-	data->iommu->domain = &rk_identity_domain;
-	dev_iommu_priv_set(dev, data);
-
-	platform_device_put(iommu_dev);
-
-	return 0;
+	kfree(data);
 }
 
 static const struct iommu_ops rk_iommu_ops = {
 	.identity_domain = &rk_identity_domain,
 	.domain_alloc_paging = rk_iommu_domain_alloc_paging,
-	.probe_device = rk_iommu_probe_device,
+	.probe_device_pinf = rk_iommu_probe_device,
 	.release_device = rk_iommu_release_device,
 	.device_group = generic_single_device_group,
 	.pgsize_bitmap = RK_IOMMU_PGSIZE_BITMAP,
-	.of_xlate = rk_iommu_of_xlate,
+	.of_xlate = iommu_dummy_of_xlate,
 	.default_domain_ops = &(const struct iommu_domain_ops) {
 		.attach_dev	= rk_iommu_attach_device,
 		.map_pages	= rk_iommu_map,
-- 
2.42.0


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

* [PATCH 10/30] iommu/sprd: Move to iommu_of_get_single_iommu()
  2023-11-30  1:10 [PATCH 00/30] Make a new API for drivers to use to get their FW Jason Gunthorpe
                   ` (8 preceding siblings ...)
  2023-11-30  1:10 ` [PATCH 09/30] iommu/rockchip: Move to iommu_of_get_single_iommu() Jason Gunthorpe
@ 2023-11-30  1:10 ` Jason Gunthorpe
  2023-11-30  1:10 ` [PATCH 11/30] iommu/sun50i: " Jason Gunthorpe
                   ` (19 subsequent siblings)
  29 siblings, 0 replies; 34+ messages in thread
From: Jason Gunthorpe @ 2023-11-30  1:10 UTC (permalink / raw)
  To: acpica-devel, Andy Gross, Alim Akhtar, Alyssa Rosenzweig,
	Bjorn Andersson, AngeloGioacchino Del Regno, asahi, Baolin Wang,
	devicetree, Frank Rowand, Hanjun Guo, Gustavo A. R. Silva,
	Heiko Stuebner, iommu, Jean-Philippe Brucker, Jernej Skrabec,
	Jonathan Hunter, Joerg Roedel, Kees Cook, Konrad Dybcio,
	Krzysztof Kozlowski, Len Brown, linux-acpi, linux-arm-kernel,
	linux-arm-msm, linux-hardening, linux-mediatek, linux-rockchip,
	linux-samsung-soc, linux-sunxi, linux-tegra, Lorenzo Pieralisi,
	Marek Szyprowski, Hector Martin, Matthias Brugger, Orson Zhai,
	Rafael J. Wysocki, Rob Clark, Robert Moore, Rob Herring,
	Robin Murphy, Samuel Holland, Sudeep Holla, Sven Peter,
	Thierry Reding, Krishna Reddy, virtualization, Chen-Yu Tsai,
	Will Deacon, Yong Wu, Chunyan Zhang
  Cc: André Draszik, patches

sprd suports a single iommu instance and only a single id. Parse it
directly using iommu_of_get_single_iommu() and remove
sprd_iommu_of_xlate().

It stores the iommu, not a per-driver struct in the dev_iommu_priv(), keep
it that way for now.

Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
---
 drivers/iommu/sprd-iommu.c | 32 ++++++++++++++------------------
 1 file changed, 14 insertions(+), 18 deletions(-)

diff --git a/drivers/iommu/sprd-iommu.c b/drivers/iommu/sprd-iommu.c
index 537359f109979b..f1b87f8661e199 100644
--- a/drivers/iommu/sprd-iommu.c
+++ b/drivers/iommu/sprd-iommu.c
@@ -11,6 +11,7 @@
 #include <linux/dma-mapping.h>
 #include <linux/errno.h>
 #include <linux/iommu.h>
+#include <linux/iommu-driver.h>
 #include <linux/mfd/syscon.h>
 #include <linux/module.h>
 #include <linux/of_platform.h>
@@ -383,32 +384,27 @@ static phys_addr_t sprd_iommu_iova_to_phys(struct iommu_domain *domain,
 	return pa;
 }
 
-static struct iommu_device *sprd_iommu_probe_device(struct device *dev)
+static struct iommu_device *sprd_iommu_probe_device(struct iommu_probe_info *pinf)
 {
-	struct sprd_iommu_device *sdev = dev_iommu_priv_get(dev);
+	struct sprd_iommu_device *sdev;
+
+	sdev = iommu_of_get_single_iommu(pinf, &sprd_iommu_ops, -1,
+					 struct sprd_iommu_device, iommu);
+	if (IS_ERR(sdev))
+		return ERR_CAST(sdev);
+	if (iommu_of_num_ids(pinf) != 1)
+		return ERR_PTR(-EINVAL);
+
+	dev_iommu_priv_set(pinf->dev, sdev);
 
 	return &sdev->iommu;
 }
 
-static int sprd_iommu_of_xlate(struct device *dev, struct of_phandle_args *args)
-{
-	struct platform_device *pdev;
-
-	if (!dev_iommu_priv_get(dev)) {
-		pdev = of_find_device_by_node(args->np);
-		dev_iommu_priv_set(dev, platform_get_drvdata(pdev));
-		platform_device_put(pdev);
-	}
-
-	return 0;
-}
-
-
 static const struct iommu_ops sprd_iommu_ops = {
 	.domain_alloc_paging = sprd_iommu_domain_alloc_paging,
-	.probe_device	= sprd_iommu_probe_device,
+	.probe_device_pinf = sprd_iommu_probe_device,
 	.device_group	= generic_single_device_group,
-	.of_xlate	= sprd_iommu_of_xlate,
+	.of_xlate = iommu_dummy_of_xlate,
 	.pgsize_bitmap	= SPRD_IOMMU_PAGE_SIZE,
 	.owner		= THIS_MODULE,
 	.default_domain_ops = &(const struct iommu_domain_ops) {
-- 
2.42.0


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

* [PATCH 11/30] iommu/sun50i: Move to iommu_of_get_single_iommu()
  2023-11-30  1:10 [PATCH 00/30] Make a new API for drivers to use to get their FW Jason Gunthorpe
                   ` (9 preceding siblings ...)
  2023-11-30  1:10 ` [PATCH 10/30] iommu/sprd: " Jason Gunthorpe
@ 2023-11-30  1:10 ` Jason Gunthorpe
  2023-11-30  1:10 ` [PATCH 12/30] iommu/of: Add iommu_of_xlate() Jason Gunthorpe
                   ` (18 subsequent siblings)
  29 siblings, 0 replies; 34+ messages in thread
From: Jason Gunthorpe @ 2023-11-30  1:10 UTC (permalink / raw)
  To: acpica-devel, Andy Gross, Alim Akhtar, Alyssa Rosenzweig,
	Bjorn Andersson, AngeloGioacchino Del Regno, asahi, Baolin Wang,
	devicetree, Frank Rowand, Hanjun Guo, Gustavo A. R. Silva,
	Heiko Stuebner, iommu, Jean-Philippe Brucker, Jernej Skrabec,
	Jonathan Hunter, Joerg Roedel, Kees Cook, Konrad Dybcio,
	Krzysztof Kozlowski, Len Brown, linux-acpi, linux-arm-kernel,
	linux-arm-msm, linux-hardening, linux-mediatek, linux-rockchip,
	linux-samsung-soc, linux-sunxi, linux-tegra, Lorenzo Pieralisi,
	Marek Szyprowski, Hector Martin, Matthias Brugger, Orson Zhai,
	Rafael J. Wysocki, Rob Clark, Robert Moore, Rob Herring,
	Robin Murphy, Samuel Holland, Sudeep Holla, Sven Peter,
	Thierry Reding, Krishna Reddy, virtualization, Chen-Yu Tsai,
	Will Deacon, Yong Wu, Chunyan Zhang
  Cc: André Draszik, patches

sun50i uses a simple binding where a the OF iommus's can describe a single
iommu instrance with a single ID, reflecting the master, on it.

The driver ignores the ID from the OF, it looks like the instance can only
do a single translation as the entire thing is managed with
generic_single_device_group(). Since there is a single translation the ID
presumably doesn't matter.

Allocate a sun50i_iommu_device struct during probe to be like all the
other drivers.

Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
---
 drivers/iommu/sun50i-iommu.c | 60 +++++++++++++++++++++---------------
 1 file changed, 36 insertions(+), 24 deletions(-)

diff --git a/drivers/iommu/sun50i-iommu.c b/drivers/iommu/sun50i-iommu.c
index 41484a5a399bb1..84038705cf657d 100644
--- a/drivers/iommu/sun50i-iommu.c
+++ b/drivers/iommu/sun50i-iommu.c
@@ -12,6 +12,7 @@
 #include <linux/errno.h>
 #include <linux/interrupt.h>
 #include <linux/iommu.h>
+#include <linux/iommu-driver.h>
 #include <linux/iopoll.h>
 #include <linux/ioport.h>
 #include <linux/log2.h>
@@ -95,6 +96,8 @@
 
 #define SPAGE_SIZE			4096
 
+static const struct iommu_ops sun50i_iommu_ops;
+
 struct sun50i_iommu {
 	struct iommu_device iommu;
 
@@ -110,6 +113,10 @@ struct sun50i_iommu {
 	struct kmem_cache *pt_pool;
 };
 
+struct sun50i_iommu_device {
+	struct sun50i_iommu *iommu;
+};
+
 struct sun50i_iommu_domain {
 	struct iommu_domain domain;
 
@@ -128,11 +135,6 @@ static struct sun50i_iommu_domain *to_sun50i_domain(struct iommu_domain *domain)
 	return container_of(domain, struct sun50i_iommu_domain, domain);
 }
 
-static struct sun50i_iommu *sun50i_iommu_from_dev(struct device *dev)
-{
-	return dev_iommu_priv_get(dev);
-}
-
 static u32 iommu_read(struct sun50i_iommu *iommu, u32 offset)
 {
 	return readl(iommu->base + offset);
@@ -760,7 +762,8 @@ static void sun50i_iommu_detach_domain(struct sun50i_iommu *iommu,
 static int sun50i_iommu_identity_attach(struct iommu_domain *identity_domain,
 					struct device *dev)
 {
-	struct sun50i_iommu *iommu = dev_iommu_priv_get(dev);
+	struct sun50i_iommu_device *sdev = dev_iommu_priv_get(dev);
+	struct sun50i_iommu *iommu = sdev->iommu;
 	struct sun50i_iommu_domain *sun50i_domain;
 
 	dev_dbg(dev, "Detaching from IOMMU domain\n");
@@ -786,12 +789,9 @@ static struct iommu_domain sun50i_iommu_identity_domain = {
 static int sun50i_iommu_attach_device(struct iommu_domain *domain,
 				      struct device *dev)
 {
+	struct sun50i_iommu_device *sdev = dev_iommu_priv_get(dev);
 	struct sun50i_iommu_domain *sun50i_domain = to_sun50i_domain(domain);
-	struct sun50i_iommu *iommu;
-
-	iommu = sun50i_iommu_from_dev(dev);
-	if (!iommu)
-		return -ENODEV;
+	struct sun50i_iommu *iommu = sdev->iommu;
 
 	dev_dbg(dev, "Attaching to IOMMU domain\n");
 
@@ -807,26 +807,37 @@ static int sun50i_iommu_attach_device(struct iommu_domain *domain,
 	return 0;
 }
 
-static struct iommu_device *sun50i_iommu_probe_device(struct device *dev)
+static struct iommu_device *
+sun50i_iommu_probe_device(struct iommu_probe_info *pinf)
 {
+	struct sun50i_iommu_device *sdev;
 	struct sun50i_iommu *iommu;
 
-	iommu = sun50i_iommu_from_dev(dev);
-	if (!iommu)
-		return ERR_PTR(-ENODEV);
+	iommu = iommu_of_get_single_iommu(pinf, &sun50i_iommu_ops, 1,
+					 struct sun50i_iommu, iommu);
+	if (IS_ERR(iommu))
+		return ERR_CAST(iommu);
 
+	/*
+	 * The ids are ignored because the all the devices are placed in a
+	 * single group and the core code will enforce the same translation for
+	 * all ids.
+	 */
+
+	sdev = kzalloc(sizeof(*sdev), GFP_KERNEL);
+	if (!sdev)
+		return ERR_PTR(-ENOMEM);
+	sdev->iommu = iommu;
+
+	dev_iommu_priv_set(pinf->dev, sdev);
 	return &iommu->iommu;
 }
 
-static int sun50i_iommu_of_xlate(struct device *dev,
-				 struct of_phandle_args *args)
+static void sun50i_iommu_release_device(struct device *dev)
 {
-	struct platform_device *iommu_pdev = of_find_device_by_node(args->np);
-	unsigned id = args->args[0];
+	struct sun50i_iommu_device *sdev = dev_iommu_priv_get(dev);
 
-	dev_iommu_priv_set(dev, platform_get_drvdata(iommu_pdev));
-
-	return iommu_fwspec_add_ids(dev, &id, 1);
+	kfree(sdev);
 }
 
 static const struct iommu_ops sun50i_iommu_ops = {
@@ -834,8 +845,9 @@ static const struct iommu_ops sun50i_iommu_ops = {
 	.pgsize_bitmap	= SZ_4K,
 	.device_group	= generic_single_device_group,
 	.domain_alloc_paging = sun50i_iommu_domain_alloc_paging,
-	.of_xlate	= sun50i_iommu_of_xlate,
-	.probe_device	= sun50i_iommu_probe_device,
+	.of_xlate = iommu_dummy_of_xlate,
+	.probe_device_pinf	= sun50i_iommu_probe_device,
+	.release_device = sun50i_iommu_release_device,
 	.default_domain_ops = &(const struct iommu_domain_ops) {
 		.attach_dev	= sun50i_iommu_attach_device,
 		.flush_iotlb_all = sun50i_iommu_flush_iotlb_all,
-- 
2.42.0


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

* [PATCH 12/30] iommu/of: Add iommu_of_xlate()
  2023-11-30  1:10 [PATCH 00/30] Make a new API for drivers to use to get their FW Jason Gunthorpe
                   ` (10 preceding siblings ...)
  2023-11-30  1:10 ` [PATCH 11/30] iommu/sun50i: " Jason Gunthorpe
@ 2023-11-30  1:10 ` Jason Gunthorpe
  2023-11-30  1:10 ` [PATCH 13/30] iommu/dart: Move to iommu_of_xlate() Jason Gunthorpe
                   ` (17 subsequent siblings)
  29 siblings, 0 replies; 34+ messages in thread
From: Jason Gunthorpe @ 2023-11-30  1:10 UTC (permalink / raw)
  To: acpica-devel, Andy Gross, Alim Akhtar, Alyssa Rosenzweig,
	Bjorn Andersson, AngeloGioacchino Del Regno, asahi, Baolin Wang,
	devicetree, Frank Rowand, Hanjun Guo, Gustavo A. R. Silva,
	Heiko Stuebner, iommu, Jean-Philippe Brucker, Jernej Skrabec,
	Jonathan Hunter, Joerg Roedel, Kees Cook, Konrad Dybcio,
	Krzysztof Kozlowski, Len Brown, linux-acpi, linux-arm-kernel,
	linux-arm-msm, linux-hardening, linux-mediatek, linux-rockchip,
	linux-samsung-soc, linux-sunxi, linux-tegra, Lorenzo Pieralisi,
	Marek Szyprowski, Hector Martin, Matthias Brugger, Orson Zhai,
	Rafael J. Wysocki, Rob Clark, Robert Moore, Rob Herring,
	Robin Murphy, Samuel Holland, Sudeep Holla, Sven Peter,
	Thierry Reding, Krishna Reddy, virtualization, Chen-Yu Tsai,
	Will Deacon, Yong Wu, Chunyan Zhang
  Cc: André Draszik, patches

This function can be called by drivers in their probe function if they
want to parse their own ID table, almost always because the driver
supports a multi-instance configuration and needs to extract the list of
iommu_driver's and data from the ID into some internal format.

The core code will find the iommu_driver for each ID table entry and
validate that it matches the driver's ops. A driver provided function is
called to handle the (iommu_driver, ID) tuple.

Before calling this function the driver should allocate its per-driver
private data and pass it through the opaque cookie priv argument.

Driver's should follow a typical pattern in their probe_device:

 static int apple_dart_of_xlate(struct iommu_device *iommu,
                                struct of_phandle_args *args, void *priv);
[..]

	cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
 	if (!cfg) return ERR_PTR(-ENOMEM);

	ret = iommu_of_xlate(pinf, &apple_dart_iommu_ops, 1,
			     &apple_dart_of_xlate, cfg);
	if (ret) goto err_free;

	dev_iommu_priv_set(dev, cfg);
        return &??->iommu; // The first iommu_device parsed

Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
---
 drivers/iommu/of_iommu.c     | 58 ++++++++++++++++++++++++++++++++++++
 include/linux/iommu-driver.h | 13 ++++++++
 2 files changed, 71 insertions(+)

diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c
index 37af32a6bc84e5..9c1d398aa2cd9c 100644
--- a/drivers/iommu/of_iommu.c
+++ b/drivers/iommu/of_iommu.c
@@ -285,6 +285,8 @@ struct parse_info {
 	struct iommu_probe_info *pinf;
 	const struct iommu_ops *ops;
 	int num_cells;
+	iommu_of_xlate_fn xlate_fn;
+	void *priv;
 };
 
 static struct iommu_device *parse_iommu(struct parse_info *info,
@@ -336,3 +338,59 @@ struct iommu_device *__iommu_of_get_single_iommu(struct iommu_probe_info *pinf,
 	return iommu_fw_finish_get_single(pinf);
 }
 EXPORT_SYMBOL_GPL(__iommu_of_get_single_iommu);
+
+static int parse_of_xlate(struct of_phandle_args *iommu_spec, void *_info)
+{
+	struct parse_info *info = _info;
+	struct iommu_device *iommu;
+
+	iommu = parse_iommu(info, iommu_spec);
+	if (IS_ERR(iommu))
+		return PTR_ERR(iommu);
+	info->pinf->num_ids++;
+	return info->xlate_fn(iommu, iommu_spec, info->priv);
+}
+
+/**
+ * iommu_of_xlate - Parse all OF ids for an IOMMU
+ * @pinf: The iommu_probe_info
+ * @ops: The ops the iommu instance must have
+ * @num_cells: #iommu-cells value to enforce, -1 is no check
+ * @fn: Call for each Instance and ID
+ * @priv: Opaque cookie for fn
+ *
+ * Drivers that support multiple iommu instances must call this function to
+ * parse each instance from the OF table. fn will be called with the driver's
+ * iommu_driver instance and the raw of_phandle_args that contains the ID.
+ *
+ * Drivers that need to parse a complex ID format should also use this function.
+ */
+int iommu_of_xlate(struct iommu_probe_info *pinf, const struct iommu_ops *ops,
+		   int num_cells, iommu_of_xlate_fn fn, void *priv)
+{
+	struct parse_info info = { .pinf = pinf,
+				   .ops = ops,
+				   .num_cells = num_cells,
+				   .xlate_fn = fn,
+				   .priv = priv };
+
+	pinf->num_ids = 0;
+	return of_iommu_for_each_id(pinf->dev, pinf->of_master_np,
+				    pinf->of_map_id, parse_of_xlate, &info);
+}
+EXPORT_SYMBOL_GPL(iommu_of_xlate);
+
+/*
+ * Temporary approach to allow drivers to opt into the bus probe. It configures
+ * the iommu_probe_info to probe the dev->of_node. This is a bit hacky because
+ * it mutates the iommu_probe_info and thus assumes there is only one op in the
+ * system. Remove when we call probe from the bus always anyhow.
+ */
+void iommu_of_allow_bus_probe(struct iommu_probe_info *pinf)
+{
+	if (pinf->is_dma_configure)
+		return;
+	pinf->of_master_np = pinf->dev->of_node;
+	pinf->is_dma_configure = true;
+}
+EXPORT_SYMBOL_GPL(iommu_of_allow_bus_probe);
diff --git a/include/linux/iommu-driver.h b/include/linux/iommu-driver.h
index 597998a62b0dd6..622d6ad9056ce0 100644
--- a/include/linux/iommu-driver.h
+++ b/include/linux/iommu-driver.h
@@ -60,9 +60,16 @@ iommu_device_from_fwnode_pinf(struct iommu_probe_info *pinf,
 			      struct fwnode_handle *fwnode);
 struct iommu_device *iommu_fw_finish_get_single(struct iommu_probe_info *pinf);
 
+typedef int (*iommu_of_xlate_fn)(struct iommu_device *iommu,
+				struct of_phandle_args *args, void *priv);
+void iommu_of_allow_bus_probe(struct iommu_probe_info *pinf);
+
 #if IS_ENABLED(CONFIG_OF_IOMMU)
 void of_iommu_get_resv_regions(struct device *dev, struct list_head *list);
 
+int iommu_of_xlate(struct iommu_probe_info *pinf, const struct iommu_ops *ops,
+		   int num_cells, iommu_of_xlate_fn fn, void *priv);
+
 struct iommu_device *__iommu_of_get_single_iommu(struct iommu_probe_info *pinf,
 						 const struct iommu_ops *ops,
 						 int num_cells);
@@ -71,6 +78,12 @@ static inline void of_iommu_get_resv_regions(struct device *dev,
 					     struct list_head *list)
 {
 }
+static inline int iommu_of_xlate(struct iommu_probe_info *pinf,
+				 const struct iommu_ops *ops, int num_cells,
+				 iommu_of_xlate_fn fn, void *priv)
+{
+	return -ENODEV;
+}
 static inline
 struct iommu_device *__iommu_of_get_single_iommu(struct iommu_probe_info *pinf,
 						 const struct iommu_ops *ops,
-- 
2.42.0


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

* [PATCH 13/30] iommu/dart: Move to iommu_of_xlate()
  2023-11-30  1:10 [PATCH 00/30] Make a new API for drivers to use to get their FW Jason Gunthorpe
                   ` (11 preceding siblings ...)
  2023-11-30  1:10 ` [PATCH 12/30] iommu/of: Add iommu_of_xlate() Jason Gunthorpe
@ 2023-11-30  1:10 ` Jason Gunthorpe
  2023-11-30  1:10 ` [PATCH 14/30] iommu/exynos: " Jason Gunthorpe
                   ` (16 subsequent siblings)
  29 siblings, 0 replies; 34+ messages in thread
From: Jason Gunthorpe @ 2023-11-30  1:10 UTC (permalink / raw)
  To: acpica-devel, Andy Gross, Alim Akhtar, Alyssa Rosenzweig,
	Bjorn Andersson, AngeloGioacchino Del Regno, asahi, Baolin Wang,
	devicetree, Frank Rowand, Hanjun Guo, Gustavo A. R. Silva,
	Heiko Stuebner, iommu, Jean-Philippe Brucker, Jernej Skrabec,
	Jonathan Hunter, Joerg Roedel, Kees Cook, Konrad Dybcio,
	Krzysztof Kozlowski, Len Brown, linux-acpi, linux-arm-kernel,
	linux-arm-msm, linux-hardening, linux-mediatek, linux-rockchip,
	linux-samsung-soc, linux-sunxi, linux-tegra, Lorenzo Pieralisi,
	Marek Szyprowski, Hector Martin, Matthias Brugger, Orson Zhai,
	Rafael J. Wysocki, Rob Clark, Robert Moore, Rob Herring,
	Robin Murphy, Samuel Holland, Sudeep Holla, Sven Peter,
	Thierry Reding, Krishna Reddy, virtualization, Chen-Yu Tsai,
	Will Deacon, Yong Wu, Chunyan Zhang
  Cc: André Draszik, patches

dart supports multiple instances with multiple IDs per instance. It loads
the FW data into a pre-allocated 2d array inside it's per-device data.

Switch over by allocating the per-device data at the top of
probe then calling iommu_of_xlate() to fill in the 2d array. The iommu
instance is located by the core code and container_of() gets to the dart
version.

Solve a few issues:

 - A bus probe was failing by accident because the of_xlate not being
   called left a NULL cfg in the priv, and other code tended to free the
   dev->iommu. iommu_of_xlate() will fail bus probe directly

 - Missing validation that the node in the iommus instance is actually
   pointing at this driver

 - Don't leak the cfg. It is allocated during probe, freed on probe
   failure, and freed in release_device() on probe success. Previously it
   would allocate it in of_xlate and leak it in some possible error flows.

Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
---
 drivers/iommu/apple-dart.c | 58 +++++++++++++++++++++-----------------
 1 file changed, 32 insertions(+), 26 deletions(-)

diff --git a/drivers/iommu/apple-dart.c b/drivers/iommu/apple-dart.c
index bb0e5a4577fc03..b796c68ae45ad8 100644
--- a/drivers/iommu/apple-dart.c
+++ b/drivers/iommu/apple-dart.c
@@ -268,10 +268,10 @@ struct apple_dart_domain {
 };
 
 /*
- * This structure is attached to devices with dev_iommu_priv_set() on of_xlate
- * and contains a list of streams bound to this device.
- * So far the worst case seen is a single device with two streams
- * from different darts, such that this simple static array is enough.
+ * This structure is attached to devices with dev_iommu_priv_set() on
+ * probe_device and contains a list of streams bound to this device. So far the
+ * worst case seen is a single device with two streams from different darts,
+ * such that this simple static array is enough.
  *
  * @streams: streams for this device
  */
@@ -295,6 +295,9 @@ struct apple_dart_master_cfg {
 static struct platform_driver apple_dart_driver;
 static const struct iommu_ops apple_dart_iommu_ops;
 
+static int apple_dart_of_xlate(struct iommu_device *iommu,
+			       struct of_phandle_args *args, void *priv);
+
 static struct apple_dart_domain *to_dart_domain(struct iommu_domain *dom)
 {
 	return container_of(dom, struct apple_dart_domain, domain);
@@ -721,21 +724,34 @@ static struct iommu_domain apple_dart_blocked_domain = {
 	.ops = &apple_dart_blocked_ops,
 };
 
-static struct iommu_device *apple_dart_probe_device(struct device *dev)
+static struct iommu_device *
+apple_dart_probe_device(struct iommu_probe_info *pinf)
 {
-	struct apple_dart_master_cfg *cfg = dev_iommu_priv_get(dev);
+	struct device *dev = pinf->dev;
+	struct apple_dart_master_cfg *cfg;
 	struct apple_dart_stream_map *stream_map;
+	int ret;
 	int i;
 
+	cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
 	if (!cfg)
-		return ERR_PTR(-ENODEV);
+		return ERR_PTR(-ENOMEM);
+	ret = iommu_of_xlate(pinf, &apple_dart_iommu_ops, 1,
+			     &apple_dart_of_xlate, cfg);
+	if (ret)
+		goto err_free;
 
 	for_each_stream_map(i, cfg, stream_map)
 		device_link_add(
 			dev, stream_map->dart->dev,
 			DL_FLAG_PM_RUNTIME | DL_FLAG_AUTOREMOVE_SUPPLIER);
 
+	dev_iommu_priv_set(dev, cfg);
 	return &cfg->stream_maps[0].dart->iommu;
+
+err_free:
+	kfree(cfg);
+	return ERR_PTR(ret);
 }
 
 static void apple_dart_release_device(struct device *dev)
@@ -778,25 +794,15 @@ static void apple_dart_domain_free(struct iommu_domain *domain)
 	kfree(dart_domain);
 }
 
-static int apple_dart_of_xlate(struct device *dev, struct of_phandle_args *args)
+static int apple_dart_of_xlate(struct iommu_device *iommu,
+			       struct of_phandle_args *args, void *priv)
 {
-	struct apple_dart_master_cfg *cfg = dev_iommu_priv_get(dev);
-	struct platform_device *iommu_pdev = of_find_device_by_node(args->np);
-	struct apple_dart *dart = platform_get_drvdata(iommu_pdev);
-	struct apple_dart *cfg_dart;
-	int i, sid;
+	struct apple_dart *dart = container_of(iommu, struct apple_dart, iommu);
+	struct apple_dart_master_cfg *cfg = priv;
+	struct apple_dart *cfg_dart = cfg->stream_maps[0].dart;
+	int sid = args->args[0];
+	int i;
 
-	if (args->args_count != 1)
-		return -EINVAL;
-	sid = args->args[0];
-
-	if (!cfg)
-		cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
-	if (!cfg)
-		return -ENOMEM;
-	dev_iommu_priv_set(dev, cfg);
-
-	cfg_dart = cfg->stream_maps[0].dart;
 	if (cfg_dart) {
 		if (cfg_dart->supports_bypass != dart->supports_bypass)
 			return -EINVAL;
@@ -978,10 +984,10 @@ static const struct iommu_ops apple_dart_iommu_ops = {
 	.identity_domain = &apple_dart_identity_domain,
 	.blocked_domain = &apple_dart_blocked_domain,
 	.domain_alloc_paging = apple_dart_domain_alloc_paging,
-	.probe_device = apple_dart_probe_device,
+	.probe_device_pinf = apple_dart_probe_device,
 	.release_device = apple_dart_release_device,
 	.device_group = apple_dart_device_group,
-	.of_xlate = apple_dart_of_xlate,
+	.of_xlate = iommu_dummy_of_xlate,
 	.def_domain_type = apple_dart_def_domain_type,
 	.get_resv_regions = apple_dart_get_resv_regions,
 	.pgsize_bitmap = -1UL, /* Restricted during dart probe */
-- 
2.42.0


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

* [PATCH 14/30] iommu/exynos: Move to iommu_of_xlate()
  2023-11-30  1:10 [PATCH 00/30] Make a new API for drivers to use to get their FW Jason Gunthorpe
                   ` (12 preceding siblings ...)
  2023-11-30  1:10 ` [PATCH 13/30] iommu/dart: Move to iommu_of_xlate() Jason Gunthorpe
@ 2023-11-30  1:10 ` Jason Gunthorpe
  2023-11-30  1:10 ` [PATCH 15/30] iommu/msm: " Jason Gunthorpe
                   ` (15 subsequent siblings)
  29 siblings, 0 replies; 34+ messages in thread
From: Jason Gunthorpe @ 2023-11-30  1:10 UTC (permalink / raw)
  To: acpica-devel, Andy Gross, Alim Akhtar, Alyssa Rosenzweig,
	Bjorn Andersson, AngeloGioacchino Del Regno, asahi, Baolin Wang,
	devicetree, Frank Rowand, Hanjun Guo, Gustavo A. R. Silva,
	Heiko Stuebner, iommu, Jean-Philippe Brucker, Jernej Skrabec,
	Jonathan Hunter, Joerg Roedel, Kees Cook, Konrad Dybcio,
	Krzysztof Kozlowski, Len Brown, linux-acpi, linux-arm-kernel,
	linux-arm-msm, linux-hardening, linux-mediatek, linux-rockchip,
	linux-samsung-soc, linux-sunxi, linux-tegra, Lorenzo Pieralisi,
	Marek Szyprowski, Hector Martin, Matthias Brugger, Orson Zhai,
	Rafael J. Wysocki, Rob Clark, Robert Moore, Rob Herring,
	Robin Murphy, Samuel Holland, Sudeep Holla, Sven Peter,
	Thierry Reding, Krishna Reddy, virtualization, Chen-Yu Tsai,
	Will Deacon, Yong Wu, Chunyan Zhang
  Cc: André Draszik, patches

exynos allocates a per-device struct in the of_xlate() that can point to
multiple instances of the iommu. It looks like each iommu instance can
point to only one device however.

Move the allocation of the per-device struct to the top of probe and use
iommu_of_xlate() to fill in the linked list. Rely on the core code to
locate the iommu.

Solve a few issues:

 - A bus probe was failing by accident because the of_xlate not being
   called left a NULL owner in the priv, and other code tended to free the
   dev->iommu. iommu_of_xlate() will fail bus probe directly

 - Missing validation that the node in the iommus instance is actually
   pointing at this driver

 - Don't leak the owner. It is allocated during probe, freed on probe
   failure, and freed in release_device() on probe success. Previously it
   would allocate it in of_xlate and leak it in some possible error flows.

Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
---
 drivers/iommu/exynos-iommu.c | 79 +++++++++++++++++-------------------
 1 file changed, 38 insertions(+), 41 deletions(-)

diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c
index 2c6e9094f1e979..c301aa87fe0ff0 100644
--- a/drivers/iommu/exynos-iommu.c
+++ b/drivers/iommu/exynos-iommu.c
@@ -13,6 +13,7 @@
 #include <linux/err.h>
 #include <linux/io.h>
 #include <linux/iommu.h>
+#include <linux/iommu-driver.h>
 #include <linux/interrupt.h>
 #include <linux/kmemleak.h>
 #include <linux/list.h>
@@ -26,6 +27,9 @@ typedef u32 sysmmu_iova_t;
 typedef u32 sysmmu_pte_t;
 static struct iommu_domain exynos_identity_domain;
 
+static int exynos_iommu_of_xlate(struct iommu_device *iommu,
+				 struct of_phandle_args *args, void *priv);
+
 /* We do not consider super section mapping (16MB) */
 #define SECT_ORDER 20
 #define LPAGE_ORDER 16
@@ -168,8 +172,6 @@ static u32 lv2ent_offset(sysmmu_iova_t iova)
 #define REG_V7_CAPA1		0x874
 #define REG_V7_CTRL_VM		0x8000
 
-#define has_sysmmu(dev)		(dev_iommu_priv_get(dev) != NULL)
-
 static struct device *dma_dev;
 static struct kmem_cache *lv2table_kmem_cache;
 static sysmmu_pte_t *zero_lv2_table;
@@ -779,8 +781,6 @@ static int exynos_sysmmu_probe(struct platform_device *pdev)
 	if (ret)
 		return ret;
 
-	platform_set_drvdata(pdev, data);
-
 	if (PG_ENT_SHIFT < 0) {
 		if (MMU_MAJ_VER(data->version) < 5) {
 			PG_ENT_SHIFT = SYSMMU_PG_ENT_SHIFT;
@@ -1393,15 +1393,29 @@ static phys_addr_t exynos_iommu_iova_to_phys(struct iommu_domain *iommu_domain,
 	return phys;
 }
 
-static struct iommu_device *exynos_iommu_probe_device(struct device *dev)
+static struct iommu_device *
+exynos_iommu_probe_device(struct iommu_probe_info *pinf)
 {
-	struct exynos_iommu_owner *owner = dev_iommu_priv_get(dev);
+	struct exynos_iommu_owner *owner;
+	struct device *dev = pinf->dev;
 	struct sysmmu_drvdata *data;
+	int ret;
 
-	if (!has_sysmmu(dev))
-		return ERR_PTR(-ENODEV);
+	owner = kzalloc(sizeof(*owner), GFP_KERNEL);
+	if (!owner)
+		return ERR_PTR(-ENOMEM);
+
+	INIT_LIST_HEAD(&owner->controllers);
+	mutex_init(&owner->rpm_lock);
+	owner->domain = &exynos_identity_domain;
+
+	ret = iommu_of_xlate(pinf, &exynos_iommu_ops, -1,
+			     &exynos_iommu_of_xlate, owner);
+	if (ret)
+		goto err_free;
 
 	list_for_each_entry(data, &owner->controllers, owner_node) {
+		data->master = dev;
 		/*
 		 * SYSMMU will be runtime activated via device link
 		 * (dependency) to its master device, so there are no
@@ -1412,11 +1426,17 @@ static struct iommu_device *exynos_iommu_probe_device(struct device *dev)
 					     DL_FLAG_PM_RUNTIME);
 	}
 
-	/* There is always at least one entry, see exynos_iommu_of_xlate() */
+	/* iommu_of_xlate() fails if there are no entries */
 	data = list_first_entry(&owner->controllers,
 				struct sysmmu_drvdata, owner_node);
 
+	dev_iommu_priv_set(dev, owner);
+
 	return &data->iommu;
+
+err_free:
+	kfree(owner);
+	return ERR_PTR(ret);
 }
 
 static void exynos_iommu_release_device(struct device *dev)
@@ -1430,42 +1450,19 @@ static void exynos_iommu_release_device(struct device *dev)
 		device_link_del(data->link);
 }
 
-static int exynos_iommu_of_xlate(struct device *dev,
-				 struct of_phandle_args *spec)
+static int exynos_iommu_of_xlate(struct iommu_device *iommu,
+				 struct of_phandle_args *args, void *priv)
 {
-	struct platform_device *sysmmu = of_find_device_by_node(spec->np);
-	struct exynos_iommu_owner *owner = dev_iommu_priv_get(dev);
-	struct sysmmu_drvdata *data, *entry;
-
-	if (!sysmmu)
-		return -ENODEV;
-
-	data = platform_get_drvdata(sysmmu);
-	if (!data) {
-		put_device(&sysmmu->dev);
-		return -ENODEV;
-	}
-
-	if (!owner) {
-		owner = kzalloc(sizeof(*owner), GFP_KERNEL);
-		if (!owner) {
-			put_device(&sysmmu->dev);
-			return -ENOMEM;
-		}
-
-		INIT_LIST_HEAD(&owner->controllers);
-		mutex_init(&owner->rpm_lock);
-		owner->domain = &exynos_identity_domain;
-		dev_iommu_priv_set(dev, owner);
-	}
+	struct sysmmu_drvdata *data =
+		container_of(iommu, struct sysmmu_drvdata, iommu);
+	struct exynos_iommu_owner *owner = priv;
+	struct sysmmu_drvdata *entry;
 
+	/* FIXME this relies on iommu_probe_device_lock */
 	list_for_each_entry(entry, &owner->controllers, owner_node)
 		if (entry == data)
 			return 0;
-
 	list_add_tail(&data->owner_node, &owner->controllers);
-	data->master = dev;
-
 	return 0;
 }
 
@@ -1473,10 +1470,10 @@ static const struct iommu_ops exynos_iommu_ops = {
 	.identity_domain = &exynos_identity_domain,
 	.domain_alloc_paging = exynos_iommu_domain_alloc_paging,
 	.device_group = generic_device_group,
-	.probe_device = exynos_iommu_probe_device,
+	.probe_device_pinf = exynos_iommu_probe_device,
 	.release_device = exynos_iommu_release_device,
 	.pgsize_bitmap = SECT_SIZE | LPAGE_SIZE | SPAGE_SIZE,
-	.of_xlate = exynos_iommu_of_xlate,
+	.of_xlate = iommu_dummy_of_xlate,
 	.default_domain_ops = &(const struct iommu_domain_ops) {
 		.attach_dev	= exynos_iommu_attach_device,
 		.map_pages	= exynos_iommu_map,
-- 
2.42.0


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

* [PATCH 15/30] iommu/msm: Move to iommu_of_xlate()
  2023-11-30  1:10 [PATCH 00/30] Make a new API for drivers to use to get their FW Jason Gunthorpe
                   ` (13 preceding siblings ...)
  2023-11-30  1:10 ` [PATCH 14/30] iommu/exynos: " Jason Gunthorpe
@ 2023-11-30  1:10 ` Jason Gunthorpe
  2023-11-30  1:10 ` [PATCH 16/30] iommu/tegra: Route tegra_dev_iommu_get_stream_id() through an op Jason Gunthorpe
                   ` (14 subsequent siblings)
  29 siblings, 0 replies; 34+ messages in thread
From: Jason Gunthorpe @ 2023-11-30  1:10 UTC (permalink / raw)
  To: acpica-devel, Andy Gross, Alim Akhtar, Alyssa Rosenzweig,
	Bjorn Andersson, AngeloGioacchino Del Regno, asahi, Baolin Wang,
	devicetree, Frank Rowand, Hanjun Guo, Gustavo A. R. Silva,
	Heiko Stuebner, iommu, Jean-Philippe Brucker, Jernej Skrabec,
	Jonathan Hunter, Joerg Roedel, Kees Cook, Konrad Dybcio,
	Krzysztof Kozlowski, Len Brown, linux-acpi, linux-arm-kernel,
	linux-arm-msm, linux-hardening, linux-mediatek, linux-rockchip,
	linux-samsung-soc, linux-sunxi, linux-tegra, Lorenzo Pieralisi,
	Marek Szyprowski, Hector Martin, Matthias Brugger, Orson Zhai,
	Rafael J. Wysocki, Rob Clark, Robert Moore, Rob Herring,
	Robin Murphy, Samuel Holland, Sudeep Holla, Sven Peter,
	Thierry Reding, Krishna Reddy, virtualization, Chen-Yu Tsai,
	Will Deacon, Yong Wu, Chunyan Zhang
  Cc: André Draszik, patches

It is confusing what this driver is doing. The insert_iommu_master(),
especially the list_empty(), doesn't make a lot of sense.

Based on the dtsi this supports an iommu instance that has exactly one
device attached it. However each device may be connected to multiple
instances with multiple stream ids. The iommus list must be sorted by
instance or it will not parse correctly.

Ideally this driver would work more like dart where each master allocates
memory for dev_iommu_priv and records a list of the all the iommus and
stream ids in that struct. That is too big of a change for this patch.

Keep things basically the same, but rely on the core code to discover the
iommu_device and stop confusingly using dev_iommu_priv to join SIDs into
the same master when processing the assumed-to-be-sorted iommus list.

Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
---
 drivers/iommu/msm_iommu.c | 93 ++++++++++++++++-----------------------
 1 file changed, 37 insertions(+), 56 deletions(-)

diff --git a/drivers/iommu/msm_iommu.c b/drivers/iommu/msm_iommu.c
index f86af9815d6f98..6f21eec857c7d7 100644
--- a/drivers/iommu/msm_iommu.c
+++ b/drivers/iommu/msm_iommu.c
@@ -16,6 +16,7 @@
 #include <linux/spinlock.h>
 #include <linux/slab.h>
 #include <linux/iommu.h>
+#include <linux/iommu-driver.h>
 #include <linux/clk.h>
 #include <linux/err.h>
 
@@ -37,6 +38,15 @@ static DEFINE_SPINLOCK(msm_iommu_lock);
 static LIST_HEAD(qcom_iommu_devices);
 static struct iommu_ops msm_iommu_ops;
 
+struct msm_xlate_args {
+	struct device *dev;
+	struct msm_iommu_ctx_dev *master;
+	struct msm_iommu_dev *iommu;
+};
+
+static int msm_iommu_of_xlate(struct iommu_device *core_iommu,
+			      struct of_phandle_args *args, void *priv);
+
 struct msm_priv {
 	struct list_head list_attached;
 	struct iommu_domain domain;
@@ -357,38 +367,17 @@ static int msm_iommu_domain_config(struct msm_priv *priv)
 	return 0;
 }
 
-/* Must be called under msm_iommu_lock */
-static struct msm_iommu_dev *find_iommu_for_dev(struct device *dev)
+static struct iommu_device *
+msm_iommu_probe_device(struct iommu_probe_info *pinf)
 {
-	struct msm_iommu_dev *iommu, *ret = NULL;
-	struct msm_iommu_ctx_dev *master;
+	struct msm_xlate_args args = { .dev = pinf->dev };
+	int ret;
 
-	list_for_each_entry(iommu, &qcom_iommu_devices, dev_node) {
-		master = list_first_entry(&iommu->ctx_list,
-					  struct msm_iommu_ctx_dev,
-					  list);
-		if (master->of_node == dev->of_node) {
-			ret = iommu;
-			break;
-		}
-	}
-
-	return ret;
-}
-
-static struct iommu_device *msm_iommu_probe_device(struct device *dev)
-{
-	struct msm_iommu_dev *iommu;
-	unsigned long flags;
-
-	spin_lock_irqsave(&msm_iommu_lock, flags);
-	iommu = find_iommu_for_dev(dev);
-	spin_unlock_irqrestore(&msm_iommu_lock, flags);
-
-	if (!iommu)
-		return ERR_PTR(-ENODEV);
-
-	return &iommu->iommu;
+	ret = iommu_of_xlate(pinf, &msm_iommu_ops, -1, &msm_iommu_of_xlate,
+			     &args);
+	if (ret)
+		return ERR_PTR(ret);
+	return &args.iommu->iommu;
 }
 
 static int msm_iommu_attach_dev(struct iommu_domain *domain, struct device *dev)
@@ -596,22 +585,26 @@ static void print_ctx_regs(void __iomem *base, int ctx)
 	       GET_SCTLR(base, ctx), GET_ACTLR(base, ctx));
 }
 
-static int insert_iommu_master(struct device *dev,
-				struct msm_iommu_dev **iommu,
-				struct of_phandle_args *spec)
+static int insert_iommu_master(struct msm_xlate_args *args,
+			       struct msm_iommu_dev *iommu,
+			       struct of_phandle_args *spec)
 {
-	struct msm_iommu_ctx_dev *master = dev_iommu_priv_get(dev);
+	struct msm_iommu_ctx_dev *master = args->master;
+	struct device *dev = args->dev;
 	int sid;
 
-	if (list_empty(&(*iommu)->ctx_list)) {
+	if (!args->iommu)
+		args->iommu = iommu;
+
+	if (list_empty(&iommu->ctx_list)) {
 		master = kzalloc(sizeof(*master), GFP_ATOMIC);
 		if (!master) {
 			dev_err(dev, "Failed to allocate iommu_master\n");
 			return -ENOMEM;
 		}
 		master->of_node = dev->of_node;
-		list_add(&master->list, &(*iommu)->ctx_list);
-		dev_iommu_priv_set(dev, master);
+		list_add(&master->list, &iommu->ctx_list);
+		args->master = master;
 	}
 
 	for (sid = 0; sid < master->num_mids; sid++)
@@ -625,28 +618,16 @@ static int insert_iommu_master(struct device *dev,
 	return 0;
 }
 
-static int qcom_iommu_of_xlate(struct device *dev,
-			       struct of_phandle_args *spec)
+static int msm_iommu_of_xlate(struct iommu_device *core_iommu,
+			      struct of_phandle_args *args, void *priv)
 {
-	struct msm_iommu_dev *iommu = NULL, *iter;
+	struct msm_iommu_dev *iommu =
+		container_of(core_iommu, struct msm_iommu_dev, iommu);
 	unsigned long flags;
 	int ret = 0;
 
 	spin_lock_irqsave(&msm_iommu_lock, flags);
-	list_for_each_entry(iter, &qcom_iommu_devices, dev_node) {
-		if (iter->dev->of_node == spec->np) {
-			iommu = iter;
-			break;
-		}
-	}
-
-	if (!iommu) {
-		ret = -ENODEV;
-		goto fail;
-	}
-
-	ret = insert_iommu_master(dev, &iommu, spec);
-fail:
+	ret = insert_iommu_master(priv, iommu, args);
 	spin_unlock_irqrestore(&msm_iommu_lock, flags);
 
 	return ret;
@@ -690,10 +671,10 @@ irqreturn_t msm_iommu_fault_handler(int irq, void *dev_id)
 static struct iommu_ops msm_iommu_ops = {
 	.identity_domain = &msm_iommu_identity_domain,
 	.domain_alloc_paging = msm_iommu_domain_alloc_paging,
-	.probe_device = msm_iommu_probe_device,
+	.probe_device_pinf = msm_iommu_probe_device,
 	.device_group = generic_device_group,
 	.pgsize_bitmap = MSM_IOMMU_PGSIZES,
-	.of_xlate = qcom_iommu_of_xlate,
+	.of_xlate = iommu_dummy_of_xlate,
 	.default_domain_ops = &(const struct iommu_domain_ops) {
 		.attach_dev	= msm_iommu_attach_dev,
 		.map_pages	= msm_iommu_map,
-- 
2.42.0


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

* [PATCH 16/30] iommu/tegra: Route tegra_dev_iommu_get_stream_id() through an op
  2023-11-30  1:10 [PATCH 00/30] Make a new API for drivers to use to get their FW Jason Gunthorpe
                   ` (14 preceding siblings ...)
  2023-11-30  1:10 ` [PATCH 15/30] iommu/msm: " Jason Gunthorpe
@ 2023-11-30  1:10 ` Jason Gunthorpe
  2023-11-30  1:10 ` [PATCH 17/30] iommu: Add iommu_fw_alloc_per_device_ids() Jason Gunthorpe
                   ` (13 subsequent siblings)
  29 siblings, 0 replies; 34+ messages in thread
From: Jason Gunthorpe @ 2023-11-30  1:10 UTC (permalink / raw)
  To: acpica-devel, Andy Gross, Alim Akhtar, Alyssa Rosenzweig,
	Bjorn Andersson, AngeloGioacchino Del Regno, asahi, Baolin Wang,
	devicetree, Frank Rowand, Hanjun Guo, Gustavo A. R. Silva,
	Heiko Stuebner, iommu, Jean-Philippe Brucker, Jernej Skrabec,
	Jonathan Hunter, Joerg Roedel, Kees Cook, Konrad Dybcio,
	Krzysztof Kozlowski, Len Brown, linux-acpi, linux-arm-kernel,
	linux-arm-msm, linux-hardening, linux-mediatek, linux-rockchip,
	linux-samsung-soc, linux-sunxi, linux-tegra, Lorenzo Pieralisi,
	Marek Szyprowski, Hector Martin, Matthias Brugger, Orson Zhai,
	Rafael J. Wysocki, Rob Clark, Robert Moore, Rob Herring,
	Robin Murphy, Samuel Holland, Sudeep Holla, Sven Peter,
	Thierry Reding, Krishna Reddy, virtualization, Chen-Yu Tsai,
	Will Deacon, Yong Wu, Chunyan Zhang
  Cc: André Draszik, patches

This special function exists because fwspec->ids is intended to be private
to the driver but tegra needs to program the FW ID into registers on the
initiating units. The function allows such units, only for tegra, to get
the IDs they are supposed to program.

The tegra HW that needs this function only supports tegra-smmu and
arm-smmu, so implement the function there.

This makes way to moving the id list into the private memory of the
driver.

Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
---
 drivers/iommu/arm/arm-smmu/arm-smmu.c | 11 +++++++++++
 drivers/iommu/of_iommu.c              | 18 ++++++++++++++++++
 drivers/iommu/tegra-smmu.c            | 11 +++++++++++
 include/linux/iommu.h                 | 21 +++++++--------------
 4 files changed, 47 insertions(+), 14 deletions(-)

diff --git a/drivers/iommu/arm/arm-smmu/arm-smmu.c b/drivers/iommu/arm/arm-smmu/arm-smmu.c
index adc7937fd8a3a3..02b8dc4f366aa9 100644
--- a/drivers/iommu/arm/arm-smmu/arm-smmu.c
+++ b/drivers/iommu/arm/arm-smmu/arm-smmu.c
@@ -1551,6 +1551,16 @@ static int arm_smmu_def_domain_type(struct device *dev)
 	return 0;
 }
 
+static bool arm_smmu_get_stream_id(struct device *dev, u32 *stream_id)
+{
+	struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
+
+	if (fwspec->num_ids != 1)
+		return false;
+	*stream_id = fwspec->ids[0] & 0xffff;
+	return true;
+}
+
 static struct iommu_ops arm_smmu_ops = {
 	.capable		= arm_smmu_capable,
 	.domain_alloc		= arm_smmu_domain_alloc,
@@ -1561,6 +1571,7 @@ static struct iommu_ops arm_smmu_ops = {
 	.of_xlate		= arm_smmu_of_xlate,
 	.get_resv_regions	= arm_smmu_get_resv_regions,
 	.def_domain_type	= arm_smmu_def_domain_type,
+	.tegra_dev_iommu_get_stream_id = arm_smmu_get_stream_id,
 	.pgsize_bitmap		= -1UL, /* Restricted during device attach */
 	.owner			= THIS_MODULE,
 	.default_domain_ops = &(const struct iommu_domain_ops) {
diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c
index 9c1d398aa2cd9c..8d5495f03dbbcb 100644
--- a/drivers/iommu/of_iommu.c
+++ b/drivers/iommu/of_iommu.c
@@ -8,6 +8,7 @@
 #include <linux/export.h>
 #include <linux/iommu.h>
 #include <linux/iommu-driver.h>
+#include "iommu-priv.h"
 #include <linux/limits.h>
 #include <linux/module.h>
 #include <linux/of.h>
@@ -281,6 +282,23 @@ void of_iommu_get_resv_regions(struct device *dev, struct list_head *list)
 }
 EXPORT_SYMBOL(of_iommu_get_resv_regions);
 
+#if IS_ENABLED(CONFIG_TEGRA_IOMMU_SMMU) || IS_ENABLED(CONFIG_ARM_SMMU)
+/*
+ * Newer generations of Tegra SoCs require devices' stream IDs to be directly
+ * programmed into some registers. These are always paired with a Tegra SMMU or
+ * ARM SMMU which provides an implementation of this op.
+ */
+bool tegra_dev_iommu_get_stream_id(struct device *dev, u32 *stream_id)
+{
+	const struct iommu_ops *ops = dev_iommu_ops(dev);
+
+	if (!ops || !ops->tegra_dev_iommu_get_stream_id)
+		return false;
+	return ops->tegra_dev_iommu_get_stream_id(dev, stream_id);
+}
+EXPORT_SYMBOL_GPL(tegra_dev_iommu_get_stream_id);
+#endif
+
 struct parse_info {
 	struct iommu_probe_info *pinf;
 	const struct iommu_ops *ops;
diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c
index 310871728ab4b6..cf563db3e3b48d 100644
--- a/drivers/iommu/tegra-smmu.c
+++ b/drivers/iommu/tegra-smmu.c
@@ -989,6 +989,16 @@ static int tegra_smmu_def_domain_type(struct device *dev)
 	return IOMMU_DOMAIN_IDENTITY;
 }
 
+static bool tegra_smmu_get_stream_id(struct device *dev, u32 *stream_id)
+{
+	struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
+
+	if (fwspec->num_ids != 1)
+		return false;
+	*stream_id = fwspec->ids[0] & 0xffff;
+	return true;
+}
+
 static const struct iommu_ops tegra_smmu_ops = {
 	.identity_domain = &tegra_smmu_identity_domain,
 	.def_domain_type = &tegra_smmu_def_domain_type,
@@ -996,6 +1006,7 @@ static const struct iommu_ops tegra_smmu_ops = {
 	.probe_device = tegra_smmu_probe_device,
 	.device_group = tegra_smmu_device_group,
 	.of_xlate = tegra_smmu_of_xlate,
+	.tegra_dev_iommu_get_stream_id = tegra_smmu_get_stream_id,
 	.pgsize_bitmap = SZ_4K,
 	.default_domain_ops = &(const struct iommu_domain_ops) {
 		.attach_dev	= tegra_smmu_attach_dev,
diff --git a/include/linux/iommu.h b/include/linux/iommu.h
index f0aaf55db3c09b..0ba12e0e450705 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -412,6 +412,9 @@ struct iommu_ops {
 	int (*def_domain_type)(struct device *dev);
 	void (*remove_dev_pasid)(struct device *dev, ioasid_t pasid);
 
+	bool (*tegra_dev_iommu_get_stream_id)(struct device *dev,
+					      u32 *stream_id);
+
 	const struct iommu_domain_ops *default_domain_ops;
 	unsigned long pgsize_bitmap;
 	struct module *owner;
@@ -1309,26 +1312,16 @@ static inline void iommu_dma_compose_msi_msg(struct msi_desc *desc, struct msi_m
 
 #endif	/* CONFIG_IOMMU_DMA */
 
-/*
- * Newer generations of Tegra SoCs require devices' stream IDs to be directly programmed into
- * some registers. These are always paired with a Tegra SMMU or ARM SMMU, for which the contents
- * of the struct iommu_fwspec are known. Use this helper to formalize access to these internals.
- */
 #define TEGRA_STREAM_ID_BYPASS 0x7f
 
+#if IS_ENABLED(CONFIG_TEGRA_IOMMU_SMMU) || IS_ENABLED(CONFIG_ARM_SMMU)
+bool tegra_dev_iommu_get_stream_id(struct device *dev, u32 *stream_id);
+#else
 static inline bool tegra_dev_iommu_get_stream_id(struct device *dev, u32 *stream_id)
 {
-#ifdef CONFIG_IOMMU_API
-	struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
-
-	if (fwspec && fwspec->num_ids == 1) {
-		*stream_id = fwspec->ids[0] & 0xffff;
-		return true;
-	}
-#endif
-
 	return false;
 }
+#endif
 
 #ifdef CONFIG_IOMMU_SVA
 static inline void mm_pasid_init(struct mm_struct *mm)
-- 
2.42.0


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

* [PATCH 17/30] iommu: Add iommu_fw_alloc_per_device_ids()
  2023-11-30  1:10 [PATCH 00/30] Make a new API for drivers to use to get their FW Jason Gunthorpe
                   ` (15 preceding siblings ...)
  2023-11-30  1:10 ` [PATCH 16/30] iommu/tegra: Route tegra_dev_iommu_get_stream_id() through an op Jason Gunthorpe
@ 2023-11-30  1:10 ` Jason Gunthorpe
  2023-11-30  1:10 ` [PATCH 18/30] iommu/tegra: Move to iommu_fw_alloc_per_device_ids() Jason Gunthorpe
                   ` (12 subsequent siblings)
  29 siblings, 0 replies; 34+ messages in thread
From: Jason Gunthorpe @ 2023-11-30  1:10 UTC (permalink / raw)
  To: acpica-devel, Andy Gross, Alim Akhtar, Alyssa Rosenzweig,
	Bjorn Andersson, AngeloGioacchino Del Regno, asahi, Baolin Wang,
	devicetree, Frank Rowand, Hanjun Guo, Gustavo A. R. Silva,
	Heiko Stuebner, iommu, Jean-Philippe Brucker, Jernej Skrabec,
	Jonathan Hunter, Joerg Roedel, Kees Cook, Konrad Dybcio,
	Krzysztof Kozlowski, Len Brown, linux-acpi, linux-arm-kernel,
	linux-arm-msm, linux-hardening, linux-mediatek, linux-rockchip,
	linux-samsung-soc, linux-sunxi, linux-tegra, Lorenzo Pieralisi,
	Marek Szyprowski, Hector Martin, Matthias Brugger, Orson Zhai,
	Rafael J. Wysocki, Rob Clark, Robert Moore, Rob Herring,
	Robin Murphy, Samuel Holland, Sudeep Holla, Sven Peter,
	Thierry Reding, Krishna Reddy, virtualization, Chen-Yu Tsai,
	Will Deacon, Yong Wu, Chunyan Zhang
  Cc: André Draszik, patches

This helper can be called after iommu_of_get_single_iommu() to
automatically extract the raw list of IDs. It can be used by drivers that
expect to have a list of simple u32 IDs for a single IOMMU instance.

The driver must provide a per-driver structure with a trailing flex array
to hold the IDs. This helper will allocate the structure, size the ids
flex array, and fill it in with data.

Driver's should follow a typical pattern in their probe_device:

  struct tegra_smmu_device {
	  struct tegra_smmu *smmu;
	  unsigned int num_ids;
	  u32 ids[] __counted_by(num_ids);
  };

	smmu = iommu_of_get_single_iommu(pinf, &tegra_smmu_ops, 1,
					 struct tegra_smmu, iommu);
	if (IS_ERR(smmu)) return ERR_CAST(smmu);

	smmu_device = iommu_fw_alloc_per_device_ids(pinf, smmu_device);
	if (IS_ERR(smmu_device)) return ERR_CAST(smmu_device);
	[..]
	dev_iommu_priv_set(dev, data);
        return &iommu->iommu;

Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
---
 drivers/iommu/iommu.c        | 21 ++++++++++++++
 drivers/iommu/of_iommu.c     | 21 +++++++++++++-
 include/linux/iommu-driver.h | 56 ++++++++++++++++++++++++++++++++++++
 3 files changed, 97 insertions(+), 1 deletion(-)

diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index ca411ad14c1182..caf14a53ed1952 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -3067,6 +3067,27 @@ struct iommu_device *iommu_fw_finish_get_single(struct iommu_probe_info *pinf)
 	return pinf->cached_iommu;
 }
 
+/*
+ * This function shouldn't be called directly without a pretty good reason,
+ * prefer to structure the driver to use iommu_fw_alloc_per_device_ids()
+ * instead.
+ */
+int iommu_fw_get_u32_ids(struct iommu_probe_info *pinf, u32 *ids)
+{
+	if (WARN_ON(!pinf->get_u32_ids))
+		return -EINVAL;
+
+	/*
+	 * We pre-parse a small number if IDs and keep it on the stack. If that
+	 * isn't enough then just reparse again.
+	 */
+	if (pinf->num_ids > ARRAY_SIZE(pinf->cached_ids))
+		return pinf->get_u32_ids(pinf, ids);
+	memcpy(ids, pinf->cached_ids, pinf->num_ids * sizeof(*ids));
+	return 0;
+}
+EXPORT_SYMBOL_GPL(iommu_fw_get_u32_ids);
+
 int iommu_fwspec_init(struct device *dev, struct fwnode_handle *iommu_fwnode,
 		      const struct iommu_ops *ops)
 {
diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c
index 8d5495f03dbbcb..6f6e442f899ded 100644
--- a/drivers/iommu/of_iommu.c
+++ b/drivers/iommu/of_iommu.c
@@ -332,10 +332,28 @@ static int parse_single_iommu(struct of_phandle_args *iommu_spec, void *_info)
 	iommu = parse_iommu(info, iommu_spec);
 	if (IS_ERR(iommu))
 		return PTR_ERR(iommu);
-	info->pinf->num_ids++;
+	iommu_fw_cache_id(info->pinf, iommu_spec->args[0]);
 	return 0;
 }
 
+static int parse_read_ids(struct of_phandle_args *iommu_spec, void *_info)
+{
+	struct parse_info *info = _info;
+	u32 *ids = info->priv;
+
+	*ids = iommu_spec->args[0];
+	info->priv = ids + 1;
+	return 0;
+}
+
+static int iommu_of_get_u32_ids(struct iommu_probe_info *pinf, u32 *ids)
+{
+	struct parse_info info = { .pinf = pinf, .priv = ids };
+
+	return of_iommu_for_each_id(pinf->dev, pinf->of_master_np,
+				    pinf->of_map_id, parse_read_ids, &info);
+}
+
 struct iommu_device *__iommu_of_get_single_iommu(struct iommu_probe_info *pinf,
 						 const struct iommu_ops *ops,
 						 int num_cells)
@@ -353,6 +371,7 @@ struct iommu_device *__iommu_of_get_single_iommu(struct iommu_probe_info *pinf,
 				   pinf->of_map_id, parse_single_iommu, &info);
 	if (err)
 		return ERR_PTR(err);
+	pinf->get_u32_ids = iommu_of_get_u32_ids;
 	return iommu_fw_finish_get_single(pinf);
 }
 EXPORT_SYMBOL_GPL(__iommu_of_get_single_iommu);
diff --git a/include/linux/iommu-driver.h b/include/linux/iommu-driver.h
index 622d6ad9056ce0..632c7b4a389abe 100644
--- a/include/linux/iommu-driver.h
+++ b/include/linux/iommu-driver.h
@@ -40,7 +40,9 @@ struct iommu_probe_info {
 	struct iommu_device *cached_iommu;
 	struct device_node *of_master_np;
 	const u32 *of_map_id;
+	int (*get_u32_ids)(struct iommu_probe_info *pinf, u32 *ids);
 	unsigned int num_ids;
+	u32 cached_ids[8];
 	bool defer_setup : 1;
 	bool is_dma_configure : 1;
 	bool cached_single_iommu : 1;
@@ -123,6 +125,60 @@ static inline unsigned int iommu_of_num_ids(struct iommu_probe_info *pinf)
 	return pinf->num_ids;
 }
 
+unsigned int iommu_of_num_ids(struct iommu_probe_info *pinf);
+int iommu_fw_get_u32_ids(struct iommu_probe_info *pinf, u32 *ids);
+
+static inline void iommu_fw_cache_id(struct iommu_probe_info *pinf, u32 id)
+{
+	if (pinf->num_ids < ARRAY_SIZE(pinf->cached_ids))
+		pinf->cached_ids[pinf->num_ids] = id;
+	pinf->num_ids++;
+}
+
+static inline void *
+__iommu_fw_alloc_per_device_ids(struct iommu_probe_info *pinf, void *mem,
+				unsigned int num_ids, unsigned int *num_ids_p,
+				u32 *ids_p)
+{
+	int ret;
+
+	if (!mem)
+		return ERR_PTR(-ENOMEM);
+
+	ret = iommu_fw_get_u32_ids(pinf, ids_p);
+	if (ret) {
+		kfree(mem);
+		return ERR_PTR(ret);
+	}
+
+	*num_ids_p = num_ids;
+	return mem;
+}
+
+/**
+ * iommu_fw_alloc_per_device_ids - Allocate a per-device struct with ids
+ * @pinf: The iommu_probe_info
+ * @drv_struct: Name of a variable to a pointer of the driver structure
+ *
+ * Called by a driver during probe this helper allocates and initializes the
+ * driver struct that embeds the ids array with the trailing members:
+ *
+ *	unsigned int num_ids;
+ *	u32 ids[] __counted_by(num_ids);
+ *
+ * The helper allocates the driver struct with the right size flex array,
+ * and initializes both members. Returns the driver struct or ERR_PTR.
+ */
+#define iommu_fw_alloc_per_device_ids(pinf, drv_struct)                    \
+	({                                                                 \
+		unsigned int num_ids = iommu_of_num_ids(pinf);             \
+		typeof(drv_struct) drv;                                    \
+                                                                           \
+		drv = kzalloc(struct_size(drv, ids, num_ids), GFP_KERNEL); \
+		drv = __iommu_fw_alloc_per_device_ids(                     \
+			pinf, drv, num_ids, &drv->num_ids, drv->ids);      \
+	})
+
 /*
  * Used temporarily to indicate drivers that have moved to the new probe method.
  */
-- 
2.42.0


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

* [PATCH 18/30] iommu/tegra: Move to iommu_fw_alloc_per_device_ids()
  2023-11-30  1:10 [PATCH 00/30] Make a new API for drivers to use to get their FW Jason Gunthorpe
                   ` (16 preceding siblings ...)
  2023-11-30  1:10 ` [PATCH 17/30] iommu: Add iommu_fw_alloc_per_device_ids() Jason Gunthorpe
@ 2023-11-30  1:10 ` Jason Gunthorpe
  2023-11-30  1:10 ` [PATCH 19/30] iommu/mtk: " Jason Gunthorpe
                   ` (11 subsequent siblings)
  29 siblings, 0 replies; 34+ messages in thread
From: Jason Gunthorpe @ 2023-11-30  1:10 UTC (permalink / raw)
  To: acpica-devel, Andy Gross, Alim Akhtar, Alyssa Rosenzweig,
	Bjorn Andersson, AngeloGioacchino Del Regno, asahi, Baolin Wang,
	devicetree, Frank Rowand, Hanjun Guo, Gustavo A. R. Silva,
	Heiko Stuebner, iommu, Jean-Philippe Brucker, Jernej Skrabec,
	Jonathan Hunter, Joerg Roedel, Kees Cook, Konrad Dybcio,
	Krzysztof Kozlowski, Len Brown, linux-acpi, linux-arm-kernel,
	linux-arm-msm, linux-hardening, linux-mediatek, linux-rockchip,
	linux-samsung-soc, linux-sunxi, linux-tegra, Lorenzo Pieralisi,
	Marek Szyprowski, Hector Martin, Matthias Brugger, Orson Zhai,
	Rafael J. Wysocki, Rob Clark, Robert Moore, Rob Herring,
	Robin Murphy, Samuel Holland, Sudeep Holla, Sven Peter,
	Thierry Reding, Krishna Reddy, virtualization, Chen-Yu Tsai,
	Will Deacon, Yong Wu, Chunyan Zhang
  Cc: André Draszik, patches

Tegra supports a single iommu instance with multiple ids.

Introduce a per-device data to store the iommu and ids list. Allocate and
initialize it with iommu_fw_alloc_per_device_ids(). Remove
tegra_smmu_of_xlate().

This now checks that all the iommu instances are the same.

Tegra can self-probe without having to go through the fwspec path because
it self-initializes the fwspec. Use iommu_of_allow_bus_probe() to achieve
the same thing.

Convert the rest of the funcs from calling dev_iommu_fwspec_get() to using
the per-device data and remove all use of fwspec.

Convert the places using dev_iommu_priv_get() to use the per-device data
not the iommu.

Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
---
 drivers/iommu/tegra-smmu.c | 154 +++++++++++--------------------------
 1 file changed, 47 insertions(+), 107 deletions(-)

diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c
index cf563db3e3b48d..1daa92f524452b 100644
--- a/drivers/iommu/tegra-smmu.c
+++ b/drivers/iommu/tegra-smmu.c
@@ -7,6 +7,7 @@
 #include <linux/debugfs.h>
 #include <linux/err.h>
 #include <linux/iommu.h>
+#include <linux/iommu-driver.h>
 #include <linux/kernel.h>
 #include <linux/of.h>
 #include <linux/of_platform.h>
@@ -19,6 +20,8 @@
 #include <soc/tegra/ahb.h>
 #include <soc/tegra/mc.h>
 
+static const struct iommu_ops tegra_smmu_ops;
+
 struct tegra_smmu_group {
 	struct list_head list;
 	struct tegra_smmu *smmu;
@@ -49,6 +52,12 @@ struct tegra_smmu {
 	struct iommu_device iommu;	/* IOMMU Core code handle */
 };
 
+struct tegra_smmu_device {
+	struct tegra_smmu *smmu;
+	unsigned int num_ids;
+	u32 ids[] __counted_by(num_ids);
+};
+
 struct tegra_smmu_as {
 	struct iommu_domain domain;
 	struct tegra_smmu *smmu;
@@ -477,21 +486,18 @@ static void tegra_smmu_as_unprepare(struct tegra_smmu *smmu,
 static int tegra_smmu_attach_dev(struct iommu_domain *domain,
 				 struct device *dev)
 {
-	struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
-	struct tegra_smmu *smmu = dev_iommu_priv_get(dev);
+	struct tegra_smmu_device *smmu_device = dev_iommu_priv_get(dev);
+	struct tegra_smmu *smmu = smmu_device->smmu;
 	struct tegra_smmu_as *as = to_smmu_as(domain);
 	unsigned int index;
 	int err;
 
-	if (!fwspec)
-		return -ENOENT;
-
-	for (index = 0; index < fwspec->num_ids; index++) {
+	for (index = 0; index < smmu_device->num_ids; index++) {
 		err = tegra_smmu_as_prepare(smmu, as);
 		if (err)
 			goto disable;
 
-		tegra_smmu_enable(smmu, fwspec->ids[index], as->id);
+		tegra_smmu_enable(smmu, smmu_device->ids[index], as->id);
 	}
 
 	if (index == 0)
@@ -501,7 +507,7 @@ static int tegra_smmu_attach_dev(struct iommu_domain *domain,
 
 disable:
 	while (index--) {
-		tegra_smmu_disable(smmu, fwspec->ids[index], as->id);
+		tegra_smmu_disable(smmu, smmu_device->ids[index], as->id);
 		tegra_smmu_as_unprepare(smmu, as);
 	}
 
@@ -511,22 +517,19 @@ static int tegra_smmu_attach_dev(struct iommu_domain *domain,
 static int tegra_smmu_identity_attach(struct iommu_domain *identity_domain,
 				      struct device *dev)
 {
+	struct tegra_smmu_device *smmu_device = dev_iommu_priv_get(dev);
 	struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
-	struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
 	struct tegra_smmu_as *as;
 	struct tegra_smmu *smmu;
 	unsigned int index;
 
-	if (!fwspec)
-		return -ENODEV;
-
 	if (domain == identity_domain || !domain)
 		return 0;
 
 	as = to_smmu_as(domain);
 	smmu = as->smmu;
-	for (index = 0; index < fwspec->num_ids; index++) {
-		tegra_smmu_disable(smmu, fwspec->ids[index], as->id);
+	for (index = 0; index < smmu_device->num_ids; index++) {
+		tegra_smmu_disable(smmu, smmu_device->ids[index], as->id);
 		tegra_smmu_as_unprepare(smmu, as);
 	}
 	return 0;
@@ -811,77 +814,34 @@ static phys_addr_t tegra_smmu_iova_to_phys(struct iommu_domain *domain,
 	return SMMU_PFN_PHYS(pfn) + SMMU_OFFSET_IN_PAGE(iova);
 }
 
-static struct tegra_smmu *tegra_smmu_find(struct device_node *np)
+static struct iommu_device *
+tegra_smmu_probe_device(struct iommu_probe_info *pinf)
 {
-	struct platform_device *pdev;
-	struct tegra_mc *mc;
+	struct tegra_smmu_device *smmu_device;
+	struct tegra_smmu *smmu;
 
-	pdev = of_find_device_by_node(np);
-	if (!pdev)
-		return NULL;
+	iommu_of_allow_bus_probe(pinf);
+	smmu = iommu_of_get_single_iommu(pinf, &tegra_smmu_ops, 1,
+					 struct tegra_smmu, iommu);
+	if (IS_ERR(smmu))
+		return ERR_CAST(smmu);
 
-	mc = platform_get_drvdata(pdev);
-	if (!mc) {
-		put_device(&pdev->dev);
-		return NULL;
-	}
-
-	return mc->smmu;
-}
-
-static int tegra_smmu_configure(struct tegra_smmu *smmu, struct device *dev,
-				struct of_phandle_args *args)
-{
-	const struct iommu_ops *ops = smmu->iommu.ops;
-	int err;
-
-	err = iommu_fwspec_init(dev, &dev->of_node->fwnode, ops);
-	if (err < 0) {
-		dev_err(dev, "failed to initialize fwspec: %d\n", err);
-		return err;
-	}
-
-	err = ops->of_xlate(dev, args);
-	if (err < 0) {
-		dev_err(dev, "failed to parse SW group ID: %d\n", err);
-		iommu_fwspec_free(dev);
-		return err;
-	}
-
-	return 0;
-}
-
-static struct iommu_device *tegra_smmu_probe_device(struct device *dev)
-{
-	struct device_node *np = dev->of_node;
-	struct tegra_smmu *smmu = NULL;
-	struct of_phandle_args args;
-	unsigned int index = 0;
-	int err;
-
-	while (of_parse_phandle_with_args(np, "iommus", "#iommu-cells", index,
-					  &args) == 0) {
-		smmu = tegra_smmu_find(args.np);
-		if (smmu) {
-			err = tegra_smmu_configure(smmu, dev, &args);
-
-			if (err < 0) {
-				of_node_put(args.np);
-				return ERR_PTR(err);
-			}
-		}
-
-		of_node_put(args.np);
-		index++;
-	}
-
-	smmu = dev_iommu_priv_get(dev);
-	if (!smmu)
-		return ERR_PTR(-ENODEV);
+	smmu_device = iommu_fw_alloc_per_device_ids(pinf, smmu_device);
+	if (IS_ERR(smmu_device))
+		return ERR_CAST(smmu_device);
+	smmu_device->smmu = smmu;
 
+	dev_iommu_priv_set(pinf->dev, smmu_device);
 	return &smmu->iommu;
 }
 
+static void tegra_smmu_release_device(struct device *dev)
+{
+	struct tegra_smmu_device *smmu_device = dev_iommu_priv_get(dev);
+
+	kfree(smmu_device);
+}
+
 static const struct tegra_smmu_group_soc *
 tegra_smmu_find_group(struct tegra_smmu *smmu, unsigned int swgroup)
 {
@@ -907,10 +867,10 @@ static void tegra_smmu_group_release(void *iommu_data)
 
 static struct iommu_group *tegra_smmu_device_group(struct device *dev)
 {
-	struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
-	struct tegra_smmu *smmu = dev_iommu_priv_get(dev);
+	struct tegra_smmu_device *smmu_device = dev_iommu_priv_get(dev);
+	struct tegra_smmu *smmu = smmu_device->smmu;
 	const struct tegra_smmu_group_soc *soc;
-	unsigned int swgroup = fwspec->ids[0];
+	unsigned int swgroup = smmu_device->ids[0];
 	struct tegra_smmu_group *group;
 	struct iommu_group *grp;
 
@@ -958,27 +918,6 @@ static struct iommu_group *tegra_smmu_device_group(struct device *dev)
 	return group->group;
 }
 
-static int tegra_smmu_of_xlate(struct device *dev,
-			       struct of_phandle_args *args)
-{
-	struct platform_device *iommu_pdev = of_find_device_by_node(args->np);
-	struct tegra_mc *mc = platform_get_drvdata(iommu_pdev);
-	u32 id = args->args[0];
-
-	/*
-	 * Note: we are here releasing the reference of &iommu_pdev->dev, which
-	 * is mc->dev. Although some functions in tegra_smmu_ops may keep using
-	 * its private data beyond this point, it's still safe to do so because
-	 * the SMMU parent device is the same as the MC, so the reference count
-	 * isn't strictly necessary.
-	 */
-	put_device(&iommu_pdev->dev);
-
-	dev_iommu_priv_set(dev, mc->smmu);
-
-	return iommu_fwspec_add_ids(dev, &id, 1);
-}
-
 static int tegra_smmu_def_domain_type(struct device *dev)
 {
 	/*
@@ -991,11 +930,11 @@ static int tegra_smmu_def_domain_type(struct device *dev)
 
 static bool tegra_smmu_get_stream_id(struct device *dev, u32 *stream_id)
 {
-	struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
+	struct tegra_smmu_device *smmu_device = dev_iommu_priv_get(dev);
 
-	if (fwspec->num_ids != 1)
+	if (smmu_device->num_ids != 1)
 		return false;
-	*stream_id = fwspec->ids[0] & 0xffff;
+	*stream_id = smmu_device->ids[0] & 0xffff;
 	return true;
 }
 
@@ -1003,9 +942,10 @@ static const struct iommu_ops tegra_smmu_ops = {
 	.identity_domain = &tegra_smmu_identity_domain,
 	.def_domain_type = &tegra_smmu_def_domain_type,
 	.domain_alloc_paging = tegra_smmu_domain_alloc_paging,
-	.probe_device = tegra_smmu_probe_device,
+	.probe_device_pinf = tegra_smmu_probe_device,
+	.release_device = tegra_smmu_release_device,
 	.device_group = tegra_smmu_device_group,
-	.of_xlate = tegra_smmu_of_xlate,
+	.of_xlate = iommu_dummy_of_xlate,
 	.tegra_dev_iommu_get_stream_id = tegra_smmu_get_stream_id,
 	.pgsize_bitmap = SZ_4K,
 	.default_domain_ops = &(const struct iommu_domain_ops) {
-- 
2.42.0


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

* [PATCH 19/30] iommu/mtk: Move to iommu_fw_alloc_per_device_ids()
  2023-11-30  1:10 [PATCH 00/30] Make a new API for drivers to use to get their FW Jason Gunthorpe
                   ` (17 preceding siblings ...)
  2023-11-30  1:10 ` [PATCH 18/30] iommu/tegra: Move to iommu_fw_alloc_per_device_ids() Jason Gunthorpe
@ 2023-11-30  1:10 ` Jason Gunthorpe
  2023-11-30  1:10 ` [PATCH 20/30] iommu/ipmmu-vmsa: " Jason Gunthorpe
                   ` (10 subsequent siblings)
  29 siblings, 0 replies; 34+ messages in thread
From: Jason Gunthorpe @ 2023-11-30  1:10 UTC (permalink / raw)
  To: acpica-devel, Andy Gross, Alim Akhtar, Alyssa Rosenzweig,
	Bjorn Andersson, AngeloGioacchino Del Regno, asahi, Baolin Wang,
	devicetree, Frank Rowand, Hanjun Guo, Gustavo A. R. Silva,
	Heiko Stuebner, iommu, Jean-Philippe Brucker, Jernej Skrabec,
	Jonathan Hunter, Joerg Roedel, Kees Cook, Konrad Dybcio,
	Krzysztof Kozlowski, Len Brown, linux-acpi, linux-arm-kernel,
	linux-arm-msm, linux-hardening, linux-mediatek, linux-rockchip,
	linux-samsung-soc, linux-sunxi, linux-tegra, Lorenzo Pieralisi,
	Marek Szyprowski, Hector Martin, Matthias Brugger, Orson Zhai,
	Rafael J. Wysocki, Rob Clark, Robert Moore, Rob Herring,
	Robin Murphy, Samuel Holland, Sudeep Holla, Sven Peter,
	Thierry Reding, Krishna Reddy, virtualization, Chen-Yu Tsai,
	Will Deacon, Yong Wu, Chunyan Zhang
  Cc: André Draszik, patches

mtk was doing a lot of stuff under of_xlate, and it looked kind of like it
might support multi-instances. But the dt files don't do that, and the
driver has no way to keep track of which instance the ids are for.

Enforce single instance with iommu_of_get_single_iommu().

Introduce a per-device data to store the iommu and ids list. Allocate and
initialize it with iommu_fw_alloc_per_device_ids(). Remove
mtk_iommu_of_xlate().

Convert the rest of the funcs from calling dev_iommu_fwspec_get() to using
the per-device data and remove all use of fwspec.

Covnert the places using dev_iommu_priv_get() to use the per-device data
not the iommu.

Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
---
 drivers/iommu/mtk_iommu.c | 116 ++++++++++++++++++++------------------
 1 file changed, 62 insertions(+), 54 deletions(-)

diff --git a/drivers/iommu/mtk_iommu.c b/drivers/iommu/mtk_iommu.c
index 7abe9e85a57063..477171e83eaa6e 100644
--- a/drivers/iommu/mtk_iommu.c
+++ b/drivers/iommu/mtk_iommu.c
@@ -13,6 +13,7 @@
 #include <linux/interrupt.h>
 #include <linux/io.h>
 #include <linux/iommu.h>
+#include <linux/iommu-driver.h>
 #include <linux/iopoll.h>
 #include <linux/io-pgtable.h>
 #include <linux/list.h>
@@ -277,6 +278,12 @@ struct mtk_iommu_data {
 	struct mtk_smi_larb_iommu	larb_imu[MTK_LARB_NR_MAX];
 };
 
+struct mtk_iommu_device {
+	struct mtk_iommu_data *iommu;
+	unsigned int num_ids;
+	u32 ids[] __counted_by(num_ids);
+};
+
 struct mtk_iommu_domain {
 	struct io_pgtable_cfg		cfg;
 	struct io_pgtable_ops		*iop;
@@ -526,14 +533,14 @@ static irqreturn_t mtk_iommu_isr(int irq, void *dev_id)
 static unsigned int mtk_iommu_get_bank_id(struct device *dev,
 					  const struct mtk_iommu_plat_data *plat_data)
 {
-	struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
+	struct mtk_iommu_device *mtkdev = dev_iommu_priv_get(dev);
 	unsigned int i, portmsk = 0, bankid = 0;
 
 	if (plat_data->banks_num == 1)
 		return bankid;
 
-	for (i = 0; i < fwspec->num_ids; i++)
-		portmsk |= BIT(MTK_M4U_TO_PORT(fwspec->ids[i]));
+	for (i = 0; i < mtkdev->num_ids; i++)
+		portmsk |= BIT(MTK_M4U_TO_PORT(mtkdev->ids[i]));
 
 	for (i = 0; i < plat_data->banks_num && i < MTK_IOMMU_BANK_MAX; i++) {
 		if (!plat_data->banks_enable[i])
@@ -550,7 +557,7 @@ static unsigned int mtk_iommu_get_bank_id(struct device *dev,
 static int mtk_iommu_get_iova_region_id(struct device *dev,
 					const struct mtk_iommu_plat_data *plat_data)
 {
-	struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
+	struct mtk_iommu_device *mtkdev = dev_iommu_priv_get(dev);
 	unsigned int portidmsk = 0, larbid;
 	const u32 *rgn_larb_msk;
 	int i;
@@ -558,9 +565,9 @@ static int mtk_iommu_get_iova_region_id(struct device *dev,
 	if (plat_data->iova_region_nr == 1)
 		return 0;
 
-	larbid = MTK_M4U_TO_LARB(fwspec->ids[0]);
-	for (i = 0; i < fwspec->num_ids; i++)
-		portidmsk |= BIT(MTK_M4U_TO_PORT(fwspec->ids[i]));
+	larbid = MTK_M4U_TO_LARB(mtkdev->ids[0]);
+	for (i = 0; i < mtkdev->num_ids; i++)
+		portidmsk |= BIT(MTK_M4U_TO_PORT(mtkdev->ids[i]));
 
 	for (i = 0; i < plat_data->iova_region_nr; i++) {
 		rgn_larb_msk = plat_data->iova_region_larb_msk[i];
@@ -579,22 +586,22 @@ static int mtk_iommu_get_iova_region_id(struct device *dev,
 static int mtk_iommu_config(struct mtk_iommu_data *data, struct device *dev,
 			    bool enable, unsigned int regionid)
 {
+	struct mtk_iommu_device *mtkdev = dev_iommu_priv_get(dev);
 	struct mtk_smi_larb_iommu    *larb_mmu;
 	unsigned int                 larbid, portid;
-	struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
 	const struct mtk_iommu_iova_region *region;
 	unsigned long portid_msk = 0;
 	struct arm_smccc_res res;
 	int i, ret = 0;
 
-	for (i = 0; i < fwspec->num_ids; ++i) {
-		portid = MTK_M4U_TO_PORT(fwspec->ids[i]);
+	for (i = 0; i < mtkdev->num_ids; ++i) {
+		portid = MTK_M4U_TO_PORT(mtkdev->ids[i]);
 		portid_msk |= BIT(portid);
 	}
 
 	if (MTK_IOMMU_IS_TYPE(data->plat_data, MTK_IOMMU_TYPE_MM)) {
 		/* All ports should be in the same larb. just use 0 here */
-		larbid = MTK_M4U_TO_LARB(fwspec->ids[0]);
+		larbid = MTK_M4U_TO_LARB(mtkdev->ids[0]);
 		larb_mmu = &data->larb_imu[larbid];
 		region = data->plat_data->iova_region + regionid;
 
@@ -618,7 +625,7 @@ static int mtk_iommu_config(struct mtk_iommu_data *data, struct device *dev,
 		} else {
 			/* PCI dev has only one output id, enable the next writing bit for PCIe */
 			if (dev_is_pci(dev)) {
-				if (fwspec->num_ids != 1) {
+				if (mtkdev->num_ids != 1) {
 					dev_err(dev, "PCI dev can only have one port.\n");
 					return -ENODEV;
 				}
@@ -708,7 +715,9 @@ static void mtk_iommu_domain_free(struct iommu_domain *domain)
 static int mtk_iommu_attach_device(struct iommu_domain *domain,
 				   struct device *dev)
 {
-	struct mtk_iommu_data *data = dev_iommu_priv_get(dev), *frstdata;
+	struct mtk_iommu_device *mtkdev = dev_iommu_priv_get(dev);
+	struct mtk_iommu_data *data = mtkdev->iommu;
+	struct mtk_iommu_data *frstdata;
 	struct mtk_iommu_domain *dom = to_mtk_domain(domain);
 	struct list_head *hw_list = data->hw_list;
 	struct device *m4udev = data->dev;
@@ -777,12 +786,12 @@ static int mtk_iommu_identity_attach(struct iommu_domain *identity_domain,
 				     struct device *dev)
 {
 	struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
-	struct mtk_iommu_data *data = dev_iommu_priv_get(dev);
+	struct mtk_iommu_device *mtkdev = dev_iommu_priv_get(dev);
 
 	if (domain == identity_domain || !domain)
 		return 0;
 
-	mtk_iommu_config(data, dev, false, 0);
+	mtk_iommu_config(mtkdev->iommu, dev, false, 0);
 	return 0;
 }
 
@@ -860,14 +869,28 @@ static phys_addr_t mtk_iommu_iova_to_phys(struct iommu_domain *domain,
 	return pa;
 }
 
-static struct iommu_device *mtk_iommu_probe_device(struct device *dev)
+static struct iommu_device *
+mtk_iommu_probe_device(struct iommu_probe_info *pinf)
 {
-	struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
-	struct mtk_iommu_data *data = dev_iommu_priv_get(dev);
+	struct mtk_iommu_device *mtkdev;
+	struct device *dev = pinf->dev;
+	struct mtk_iommu_data *data;
 	struct device_link *link;
 	struct device *larbdev;
 	unsigned int larbid, larbidx, i;
 
+	data = iommu_of_get_single_iommu(pinf, &mtk_iommu_ops, 1,
+					 struct mtk_iommu_data, iommu);
+	if (IS_ERR(data))
+		return ERR_CAST(data);
+
+	mtkdev = iommu_fw_alloc_per_device_ids(pinf, mtkdev);
+	if (IS_ERR(mtkdev))
+		return ERR_CAST(mtkdev);
+	mtkdev->iommu = data;
+
+	dev_iommu_priv_set(dev, mtkdev);
+
 	if (!MTK_IOMMU_IS_TYPE(data->plat_data, MTK_IOMMU_TYPE_MM))
 		return &data->iommu;
 
@@ -876,42 +899,46 @@ static struct iommu_device *mtk_iommu_probe_device(struct device *dev)
 	 * The device that connects with each a larb is a independent HW.
 	 * All the ports in each a device should be in the same larbs.
 	 */
-	larbid = MTK_M4U_TO_LARB(fwspec->ids[0]);
+	larbid = MTK_M4U_TO_LARB(mtkdev->ids[0]);
 	if (larbid >= MTK_LARB_NR_MAX)
-		return ERR_PTR(-EINVAL);
+		goto err_out;
 
-	for (i = 1; i < fwspec->num_ids; i++) {
-		larbidx = MTK_M4U_TO_LARB(fwspec->ids[i]);
+	for (i = 1; i < mtkdev->num_ids; i++) {
+		larbidx = MTK_M4U_TO_LARB(mtkdev->ids[i]);
 		if (larbid != larbidx) {
 			dev_err(dev, "Can only use one larb. Fail@larb%d-%d.\n",
 				larbid, larbidx);
-			return ERR_PTR(-EINVAL);
+			goto err_out;
 		}
 	}
 	larbdev = data->larb_imu[larbid].dev;
 	if (!larbdev)
-		return ERR_PTR(-EINVAL);
+		goto err_out;
 
 	link = device_link_add(dev, larbdev,
 			       DL_FLAG_PM_RUNTIME | DL_FLAG_STATELESS);
 	if (!link)
 		dev_err(dev, "Unable to link %s\n", dev_name(larbdev));
 	return &data->iommu;
+
+err_out:
+	kfree(mtkdev);
+	return ERR_PTR(-EINVAL);
 }
 
 static void mtk_iommu_release_device(struct device *dev)
 {
-	struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
-	struct mtk_iommu_data *data;
+	struct mtk_iommu_device *mtkdev = dev_iommu_priv_get(dev);
+	struct mtk_iommu_data *data = mtkdev->iommu;
 	struct device *larbdev;
 	unsigned int larbid;
 
-	data = dev_iommu_priv_get(dev);
 	if (MTK_IOMMU_IS_TYPE(data->plat_data, MTK_IOMMU_TYPE_MM)) {
-		larbid = MTK_M4U_TO_LARB(fwspec->ids[0]);
+		larbid = MTK_M4U_TO_LARB(mtkdev->ids[0]);
 		larbdev = data->larb_imu[larbid].dev;
 		device_link_remove(dev, larbdev);
 	}
+	kfree(mtkdev);
 }
 
 static int mtk_iommu_get_group_id(struct device *dev, const struct mtk_iommu_plat_data *plat_data)
@@ -931,7 +958,9 @@ static int mtk_iommu_get_group_id(struct device *dev, const struct mtk_iommu_pla
 
 static struct iommu_group *mtk_iommu_device_group(struct device *dev)
 {
-	struct mtk_iommu_data *c_data = dev_iommu_priv_get(dev), *data;
+	struct mtk_iommu_device *mtkdev = dev_iommu_priv_get(dev);
+	struct mtk_iommu_data *c_data = mtkdev->iommu;
+	struct mtk_iommu_data *data;
 	struct list_head *hw_list = c_data->hw_list;
 	struct iommu_group *group;
 	int groupid;
@@ -957,32 +986,11 @@ static struct iommu_group *mtk_iommu_device_group(struct device *dev)
 	return group;
 }
 
-static int mtk_iommu_of_xlate(struct device *dev, struct of_phandle_args *args)
-{
-	struct platform_device *m4updev;
-
-	if (args->args_count != 1) {
-		dev_err(dev, "invalid #iommu-cells(%d) property for IOMMU\n",
-			args->args_count);
-		return -EINVAL;
-	}
-
-	if (!dev_iommu_priv_get(dev)) {
-		/* Get the m4u device */
-		m4updev = of_find_device_by_node(args->np);
-		if (WARN_ON(!m4updev))
-			return -EINVAL;
-
-		dev_iommu_priv_set(dev, platform_get_drvdata(m4updev));
-	}
-
-	return iommu_fwspec_add_ids(dev, args->args, 1);
-}
-
 static void mtk_iommu_get_resv_regions(struct device *dev,
 				       struct list_head *head)
 {
-	struct mtk_iommu_data *data = dev_iommu_priv_get(dev);
+	struct mtk_iommu_device *mtkdev = dev_iommu_priv_get(dev);
+	struct mtk_iommu_data *data = mtkdev->iommu;
 	unsigned int regionid = mtk_iommu_get_iova_region_id(dev, data->plat_data), i;
 	const struct mtk_iommu_iova_region *resv, *curdom;
 	struct iommu_resv_region *region;
@@ -1012,10 +1020,10 @@ static void mtk_iommu_get_resv_regions(struct device *dev,
 static const struct iommu_ops mtk_iommu_ops = {
 	.identity_domain = &mtk_iommu_identity_domain,
 	.domain_alloc_paging = mtk_iommu_domain_alloc_paging,
-	.probe_device	= mtk_iommu_probe_device,
+	.probe_device_pinf = mtk_iommu_probe_device,
 	.release_device	= mtk_iommu_release_device,
 	.device_group	= mtk_iommu_device_group,
-	.of_xlate	= mtk_iommu_of_xlate,
+	.of_xlate = iommu_dummy_of_xlate,
 	.get_resv_regions = mtk_iommu_get_resv_regions,
 	.pgsize_bitmap	= SZ_4K | SZ_64K | SZ_1M | SZ_16M,
 	.owner		= THIS_MODULE,
-- 
2.42.0


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

* [PATCH 20/30] iommu/ipmmu-vmsa: Move to iommu_fw_alloc_per_device_ids()
  2023-11-30  1:10 [PATCH 00/30] Make a new API for drivers to use to get their FW Jason Gunthorpe
                   ` (18 preceding siblings ...)
  2023-11-30  1:10 ` [PATCH 19/30] iommu/mtk: " Jason Gunthorpe
@ 2023-11-30  1:10 ` Jason Gunthorpe
  2023-11-30  1:10 ` [PATCH 21/30] iommu/mtk_v1: " Jason Gunthorpe
                   ` (9 subsequent siblings)
  29 siblings, 0 replies; 34+ messages in thread
From: Jason Gunthorpe @ 2023-11-30  1:10 UTC (permalink / raw)
  To: acpica-devel, Andy Gross, Alim Akhtar, Alyssa Rosenzweig,
	Bjorn Andersson, AngeloGioacchino Del Regno, asahi, Baolin Wang,
	devicetree, Frank Rowand, Hanjun Guo, Gustavo A. R. Silva,
	Heiko Stuebner, iommu, Jean-Philippe Brucker, Jernej Skrabec,
	Jonathan Hunter, Joerg Roedel, Kees Cook, Konrad Dybcio,
	Krzysztof Kozlowski, Len Brown, linux-acpi, linux-arm-kernel,
	linux-arm-msm, linux-hardening, linux-mediatek, linux-rockchip,
	linux-samsung-soc, linux-sunxi, linux-tegra, Lorenzo Pieralisi,
	Marek Szyprowski, Hector Martin, Matthias Brugger, Orson Zhai,
	Rafael J. Wysocki, Rob Clark, Robert Moore, Rob Herring,
	Robin Murphy, Samuel Holland, Sudeep Holla, Sven Peter,
	Thierry Reding, Krishna Reddy, virtualization, Chen-Yu Tsai,
	Will Deacon, Yong Wu, Chunyan Zhang
  Cc: André Draszik, patches

ipmmmu-vmsa supports a single instance with multiple ids. It has a special
check if the device is permitted, move that to the top of probe. Use
iommu_of_get_single_iommu() to check and obtain the iommu device.

Introduce a per-device data to store the iommu and ids list. Allocate and
initialize it with iommu_fw_alloc_per_device_ids(). Remove
ipmmu_of_xlate(), ipmmu_init_platform_device(), and to_ipmmu().

Convert the rest of the funcs from calling dev_iommu_fwspec_get() to using
the per-device data and remove all use of fwspec.

Convert the places using dev_iommu_priv_get() to use the per-device data
not the iommu.

Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
---
 drivers/iommu/ipmmu-vmsa.c | 96 +++++++++++++++++---------------------
 1 file changed, 42 insertions(+), 54 deletions(-)

diff --git a/drivers/iommu/ipmmu-vmsa.c b/drivers/iommu/ipmmu-vmsa.c
index ace1fc4bd34b0f..ba984017065f98 100644
--- a/drivers/iommu/ipmmu-vmsa.c
+++ b/drivers/iommu/ipmmu-vmsa.c
@@ -17,6 +17,7 @@
 #include <linux/iopoll.h>
 #include <linux/io-pgtable.h>
 #include <linux/iommu.h>
+#include <linux/iommu-driver.h>
 #include <linux/of.h>
 #include <linux/of_platform.h>
 #include <linux/pci.h>
@@ -38,6 +39,8 @@
 
 #define IPMMU_UTLB_MAX		64U
 
+static const struct iommu_ops ipmmu_ops;
+
 struct ipmmu_features {
 	bool use_ns_alias_offset;
 	bool has_cache_leaf_nodes;
@@ -67,6 +70,12 @@ struct ipmmu_vmsa_device {
 	struct dma_iommu_mapping *mapping;
 };
 
+struct ipmmu_vmsa_master {
+	struct ipmmu_vmsa_device *iommu;
+	unsigned int num_ids;
+	u32 ids[] __counted_by(num_ids);
+};
+
 struct ipmmu_vmsa_domain {
 	struct ipmmu_vmsa_device *mmu;
 	struct iommu_domain io_domain;
@@ -83,11 +92,6 @@ static struct ipmmu_vmsa_domain *to_vmsa_domain(struct iommu_domain *dom)
 	return container_of(dom, struct ipmmu_vmsa_domain, io_domain);
 }
 
-static struct ipmmu_vmsa_device *to_ipmmu(struct device *dev)
-{
-	return dev_iommu_priv_get(dev);
-}
-
 #define TLB_LOOP_TIMEOUT		100	/* 100us */
 
 /* -----------------------------------------------------------------------------
@@ -591,9 +595,9 @@ static void ipmmu_domain_free(struct iommu_domain *io_domain)
 static int ipmmu_attach_device(struct iommu_domain *io_domain,
 			       struct device *dev)
 {
-	struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
-	struct ipmmu_vmsa_device *mmu = to_ipmmu(dev);
+	struct ipmmu_vmsa_master *master = dev_iommu_priv_get(dev);
 	struct ipmmu_vmsa_domain *domain = to_vmsa_domain(io_domain);
+	struct ipmmu_vmsa_device *mmu = master->iommu;
 	unsigned int i;
 	int ret = 0;
 
@@ -629,8 +633,8 @@ static int ipmmu_attach_device(struct iommu_domain *io_domain,
 	if (ret < 0)
 		return ret;
 
-	for (i = 0; i < fwspec->num_ids; ++i)
-		ipmmu_utlb_enable(domain, fwspec->ids[i]);
+	for (i = 0; i < master->num_ids; ++i)
+		ipmmu_utlb_enable(domain, master->ids[i]);
 
 	return 0;
 }
@@ -639,7 +643,7 @@ static int ipmmu_iommu_identity_attach(struct iommu_domain *identity_domain,
 				       struct device *dev)
 {
 	struct iommu_domain *io_domain = iommu_get_domain_for_dev(dev);
-	struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
+	struct ipmmu_vmsa_master *master = dev_iommu_priv_get(dev);
 	struct ipmmu_vmsa_domain *domain;
 	unsigned int i;
 
@@ -647,8 +651,8 @@ static int ipmmu_iommu_identity_attach(struct iommu_domain *identity_domain,
 		return 0;
 
 	domain = to_vmsa_domain(io_domain);
-	for (i = 0; i < fwspec->num_ids; ++i)
-		ipmmu_utlb_disable(domain, fwspec->ids[i]);
+	for (i = 0; i < master->num_ids; ++i)
+		ipmmu_utlb_disable(domain, master->ids[i]);
 
 	/*
 	 * TODO: Optimize by disabling the context when no device is attached.
@@ -708,20 +712,6 @@ static phys_addr_t ipmmu_iova_to_phys(struct iommu_domain *io_domain,
 	return domain->iop->iova_to_phys(domain->iop, iova);
 }
 
-static int ipmmu_init_platform_device(struct device *dev,
-				      struct of_phandle_args *args)
-{
-	struct platform_device *ipmmu_pdev;
-
-	ipmmu_pdev = of_find_device_by_node(args->np);
-	if (!ipmmu_pdev)
-		return -ENODEV;
-
-	dev_iommu_priv_set(dev, platform_get_drvdata(ipmmu_pdev));
-
-	return 0;
-}
-
 static const struct soc_device_attribute soc_needs_opt_in[] = {
 	{ .family = "R-Car Gen3", },
 	{ .family = "R-Car Gen4", },
@@ -772,24 +762,10 @@ static bool ipmmu_device_is_allowed(struct device *dev)
 	return false;
 }
 
-static int ipmmu_of_xlate(struct device *dev,
-			  struct of_phandle_args *spec)
-{
-	if (!ipmmu_device_is_allowed(dev))
-		return -ENODEV;
-
-	iommu_fwspec_add_ids(dev, spec->args, 1);
-
-	/* Initialize once - xlate() will call multiple times */
-	if (to_ipmmu(dev))
-		return 0;
-
-	return ipmmu_init_platform_device(dev, spec);
-}
-
 static int ipmmu_init_arm_mapping(struct device *dev)
 {
-	struct ipmmu_vmsa_device *mmu = to_ipmmu(dev);
+	struct ipmmu_vmsa_master *master = dev_iommu_priv_get(dev);
+	struct ipmmu_vmsa_device *mmu = master->iommu;
 	int ret;
 
 	/*
@@ -831,16 +807,27 @@ static int ipmmu_init_arm_mapping(struct device *dev)
 	return ret;
 }
 
-static struct iommu_device *ipmmu_probe_device(struct device *dev)
+static struct iommu_device *ipmmu_probe_device(struct iommu_probe_info *pinf)
 {
-	struct ipmmu_vmsa_device *mmu = to_ipmmu(dev);
+	struct device *dev = pinf->dev;
+	struct ipmmu_vmsa_master *master;
+	struct ipmmu_vmsa_device *mmu;
 
-	/*
-	 * Only let through devices that have been verified in xlate()
-	 */
-	if (!mmu)
+	if (!ipmmu_device_is_allowed(dev))
 		return ERR_PTR(-ENODEV);
 
+	mmu = iommu_of_get_single_iommu(pinf, &ipmmu_ops, -1,
+					struct ipmmu_vmsa_device, iommu);
+	if (IS_ERR(mmu))
+		return ERR_CAST(mmu);
+
+	master = iommu_fw_alloc_per_device_ids(pinf, master);
+	if (IS_ERR(master))
+		return ERR_CAST(master);
+	master->iommu = mmu;
+
+	dev_iommu_priv_set(dev, master);
+
 	return &mmu->iommu;
 }
 
@@ -857,24 +844,25 @@ static void ipmmu_probe_finalize(struct device *dev)
 
 static void ipmmu_release_device(struct device *dev)
 {
-	struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
-	struct ipmmu_vmsa_device *mmu = to_ipmmu(dev);
+	struct ipmmu_vmsa_master *master = dev_iommu_priv_get(dev);
+	struct ipmmu_vmsa_device *mmu = master->iommu;
 	unsigned int i;
 
-	for (i = 0; i < fwspec->num_ids; ++i) {
-		unsigned int utlb = fwspec->ids[i];
+	for (i = 0; i < master->num_ids; ++i) {
+		unsigned int utlb = master->ids[i];
 
 		ipmmu_imuctr_write(mmu, utlb, 0);
 		mmu->utlb_ctx[utlb] = IPMMU_CTX_INVALID;
 	}
 
 	arm_iommu_release_mapping(mmu->mapping);
+	kfree(master);
 }
 
 static const struct iommu_ops ipmmu_ops = {
 	.identity_domain = &ipmmu_iommu_identity_domain,
 	.domain_alloc_paging = ipmmu_domain_alloc_paging,
-	.probe_device = ipmmu_probe_device,
+	.probe_device_pinf = ipmmu_probe_device,
 	.release_device = ipmmu_release_device,
 	.probe_finalize = ipmmu_probe_finalize,
 	/*
@@ -884,7 +872,7 @@ static const struct iommu_ops ipmmu_ops = {
 	.device_group = IS_ENABLED(CONFIG_ARM) && !IS_ENABLED(CONFIG_IOMMU_DMA)
 			? generic_device_group : generic_single_device_group,
 	.pgsize_bitmap = SZ_1G | SZ_2M | SZ_4K,
-	.of_xlate = ipmmu_of_xlate,
+	.of_xlate = iommu_dummy_of_xlate,
 	.default_domain_ops = &(const struct iommu_domain_ops) {
 		.attach_dev	= ipmmu_attach_device,
 		.map_pages	= ipmmu_map,
-- 
2.42.0


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

* [PATCH 21/30] iommu/mtk_v1: Move to iommu_fw_alloc_per_device_ids()
  2023-11-30  1:10 [PATCH 00/30] Make a new API for drivers to use to get their FW Jason Gunthorpe
                   ` (19 preceding siblings ...)
  2023-11-30  1:10 ` [PATCH 20/30] iommu/ipmmu-vmsa: " Jason Gunthorpe
@ 2023-11-30  1:10 ` Jason Gunthorpe
  2023-11-30  1:10 ` [PATCH 22/30] iommu/qcom: " Jason Gunthorpe
                   ` (8 subsequent siblings)
  29 siblings, 0 replies; 34+ messages in thread
From: Jason Gunthorpe @ 2023-11-30  1:10 UTC (permalink / raw)
  To: acpica-devel, Andy Gross, Alim Akhtar, Alyssa Rosenzweig,
	Bjorn Andersson, AngeloGioacchino Del Regno, asahi, Baolin Wang,
	devicetree, Frank Rowand, Hanjun Guo, Gustavo A. R. Silva,
	Heiko Stuebner, iommu, Jean-Philippe Brucker, Jernej Skrabec,
	Jonathan Hunter, Joerg Roedel, Kees Cook, Konrad Dybcio,
	Krzysztof Kozlowski, Len Brown, linux-acpi, linux-arm-kernel,
	linux-arm-msm, linux-hardening, linux-mediatek, linux-rockchip,
	linux-samsung-soc, linux-sunxi, linux-tegra, Lorenzo Pieralisi,
	Marek Szyprowski, Hector Martin, Matthias Brugger, Orson Zhai,
	Rafael J. Wysocki, Rob Clark, Robert Moore, Rob Herring,
	Robin Murphy, Samuel Holland, Sudeep Holla, Sven Peter,
	Thierry Reding, Krishna Reddy, virtualization, Chen-Yu Tsai,
	Will Deacon, Yong Wu, Chunyan Zhang
  Cc: André Draszik, patches

mtk_v1 supports a single iommu instance with multiple ids.

It open codes the fwspec parse inside the driver, remove all of this and
just call iommu_of_get_single_iommu() to do the same parsing. Using
iommu_of_allow_bus_probe() so this continues to work at bus probe time.

Introduce a per-device data to store the iommu and ids list. Allocate and
initialize it with iommu_fw_alloc_per_device_ids(). Fold
mtk_iommu_v1_create_mapping() into mtk_iommu_v1_probe_device() as it is
done only once per device.

Fix the error handling so we don't leak mappings on error paths.

Convert the rest of the funcs from calling dev_iommu_fwspec_get() to using
the per-device data and remove all use of fwspec.

Convert the places using dev_iommu_priv_get() to use the per-device data
not the iommu.

Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
---
 drivers/iommu/mtk_iommu_v1.c | 162 +++++++++++++++--------------------
 1 file changed, 67 insertions(+), 95 deletions(-)

diff --git a/drivers/iommu/mtk_iommu_v1.c b/drivers/iommu/mtk_iommu_v1.c
index 25b41222abaec1..82f500a1ad74e9 100644
--- a/drivers/iommu/mtk_iommu_v1.c
+++ b/drivers/iommu/mtk_iommu_v1.c
@@ -16,6 +16,7 @@
 #include <linux/interrupt.h>
 #include <linux/io.h>
 #include <linux/iommu.h>
+#include <linux/iommu-driver.h>
 #include <linux/iopoll.h>
 #include <linux/list.h>
 #include <linux/module.h>
@@ -108,6 +109,12 @@ struct mtk_iommu_v1_data {
 	struct mtk_iommu_v1_suspend_reg	reg;
 };
 
+struct mtk_iommu_v1_device {
+	struct mtk_iommu_v1_data *iommu;
+	unsigned int num_ids;
+	u32 ids[] __counted_by(num_ids);
+};
+
 struct mtk_iommu_v1_domain {
 	spinlock_t			pgtlock; /* lock for page table */
 	struct iommu_domain		domain;
@@ -232,14 +239,14 @@ static irqreturn_t mtk_iommu_v1_isr(int irq, void *dev_id)
 static void mtk_iommu_v1_config(struct mtk_iommu_v1_data *data,
 				struct device *dev, bool enable)
 {
+	struct mtk_iommu_v1_device *mdev = dev_iommu_priv_get(dev);
 	struct mtk_smi_larb_iommu    *larb_mmu;
 	unsigned int                 larbid, portid;
-	struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
 	int i;
 
-	for (i = 0; i < fwspec->num_ids; ++i) {
-		larbid = mt2701_m4u_to_larb(fwspec->ids[i]);
-		portid = mt2701_m4u_to_port(fwspec->ids[i]);
+	for (i = 0; i < mdev->num_ids; ++i) {
+		larbid = mt2701_m4u_to_larb(mdev->ids[i]);
+		portid = mt2701_m4u_to_port(mdev->ids[i]);
 		larb_mmu = &data->larb_imu[larbid];
 
 		dev_dbg(dev, "%s iommu port: %d\n",
@@ -293,7 +300,8 @@ static void mtk_iommu_v1_domain_free(struct iommu_domain *domain)
 
 static int mtk_iommu_v1_attach_device(struct iommu_domain *domain, struct device *dev)
 {
-	struct mtk_iommu_v1_data *data = dev_iommu_priv_get(dev);
+	struct mtk_iommu_v1_device *mdev = dev_iommu_priv_get(dev);
+	struct mtk_iommu_v1_data *data = mdev->iommu;
 	struct mtk_iommu_v1_domain *dom = to_mtk_domain(domain);
 	struct dma_iommu_mapping *mtk_mapping;
 	int ret;
@@ -319,7 +327,8 @@ static int mtk_iommu_v1_attach_device(struct iommu_domain *domain, struct device
 static int mtk_iommu_v1_identity_attach(struct iommu_domain *identity_domain,
 					struct device *dev)
 {
-	struct mtk_iommu_v1_data *data = dev_iommu_priv_get(dev);
+	struct mtk_iommu_v1_device *mdev = dev_iommu_priv_get(dev);
+	struct mtk_iommu_v1_data *data = mdev->iommu;
 
 	mtk_iommu_v1_config(data, dev, false);
 	return 0;
@@ -394,128 +403,91 @@ static phys_addr_t mtk_iommu_v1_iova_to_phys(struct iommu_domain *domain, dma_ad
 
 static const struct iommu_ops mtk_iommu_v1_ops;
 
-/*
- * MTK generation one iommu HW only support one iommu domain, and all the client
- * sharing the same iova address space.
- */
-static int mtk_iommu_v1_create_mapping(struct device *dev, struct of_phandle_args *args)
+static struct iommu_device *
+mtk_iommu_v1_probe_device(struct iommu_probe_info *pinf)
 {
-	struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
-	struct mtk_iommu_v1_data *data;
-	struct platform_device *m4updev;
 	struct dma_iommu_mapping *mtk_mapping;
+	struct mtk_iommu_v1_device *mdev;
+	struct device *dev = pinf->dev;
+	struct mtk_iommu_v1_data *data;
+	int idx, larbid, larbidx;
+	struct device_link *link;
+	struct device *larbdev;
 	int ret;
 
-	if (args->args_count != 1) {
-		dev_err(dev, "invalid #iommu-cells(%d) property for IOMMU\n",
-			args->args_count);
-		return -EINVAL;
-	}
+	iommu_of_allow_bus_probe(pinf);
+	data = iommu_of_get_single_iommu(pinf, &mtk_iommu_v1_ops, 1,
+					 struct mtk_iommu_v1_data, iommu);
+	if (IS_ERR(data))
+		return ERR_CAST(data);
 
-	if (!fwspec) {
-		ret = iommu_fwspec_init(dev, &args->np->fwnode, &mtk_iommu_v1_ops);
-		if (ret)
-			return ret;
-		fwspec = dev_iommu_fwspec_get(dev);
-	} else if (dev_iommu_fwspec_get(dev)->ops != &mtk_iommu_v1_ops) {
-		return -EINVAL;
-	}
+	mdev = iommu_fw_alloc_per_device_ids(pinf, mdev);
+	if (IS_ERR(mdev))
+		return ERR_CAST(mdev);
+	mdev->iommu = data;
 
-	if (!dev_iommu_priv_get(dev)) {
-		/* Get the m4u device */
-		m4updev = of_find_device_by_node(args->np);
-		if (WARN_ON(!m4updev))
-			return -EINVAL;
-
-		dev_iommu_priv_set(dev, platform_get_drvdata(m4updev));
-	}
-
-	ret = iommu_fwspec_add_ids(dev, args->args, 1);
-	if (ret)
-		return ret;
-
-	data = dev_iommu_priv_get(dev);
+	/*
+	 * MTK generation one iommu HW only support one iommu domain, and all
+	 * the client sharing the same iova address space.
+	 */
 	mtk_mapping = data->mapping;
 	if (!mtk_mapping) {
 		/* MTK iommu support 4GB iova address space. */
 		mtk_mapping = arm_iommu_create_mapping(&platform_bus_type,
 						0, 1ULL << 32);
-		if (IS_ERR(mtk_mapping))
-			return PTR_ERR(mtk_mapping);
+		if (IS_ERR(mtk_mapping)) {
+			ret = PTR_ERR(mtk_mapping);
+			goto err_free;
+		}
 
 		data->mapping = mtk_mapping;
 	}
 
-	return 0;
-}
-
-static struct iommu_device *mtk_iommu_v1_probe_device(struct device *dev)
-{
-	struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
-	struct of_phandle_args iommu_spec;
-	struct mtk_iommu_v1_data *data;
-	int err, idx = 0, larbid, larbidx;
-	struct device_link *link;
-	struct device *larbdev;
-
-	/*
-	 * In the deferred case, free the existed fwspec.
-	 * Always initialize the fwspec internally.
-	 */
-	if (fwspec) {
-		iommu_fwspec_free(dev);
-		fwspec = dev_iommu_fwspec_get(dev);
-	}
-
-	while (!of_parse_phandle_with_args(dev->of_node, "iommus",
-					   "#iommu-cells",
-					   idx, &iommu_spec)) {
-
-		err = mtk_iommu_v1_create_mapping(dev, &iommu_spec);
-		of_node_put(iommu_spec.np);
-		if (err)
-			return ERR_PTR(err);
-
-		/* dev->iommu_fwspec might have changed */
-		fwspec = dev_iommu_fwspec_get(dev);
-		idx++;
-	}
-
-	data = dev_iommu_priv_get(dev);
-
 	/* Link the consumer device with the smi-larb device(supplier) */
-	larbid = mt2701_m4u_to_larb(fwspec->ids[0]);
-	if (larbid >= MT2701_LARB_NR_MAX)
-		return ERR_PTR(-EINVAL);
+	larbid = mt2701_m4u_to_larb(mdev->ids[0]);
+	if (larbid >= MT2701_LARB_NR_MAX) {
+		ret = -EINVAL;
+		goto err_mapping;
+	}
 
-	for (idx = 1; idx < fwspec->num_ids; idx++) {
-		larbidx = mt2701_m4u_to_larb(fwspec->ids[idx]);
+	for (idx = 1; idx < mdev->num_ids; idx++) {
+		larbidx = mt2701_m4u_to_larb(mdev->ids[idx]);
 		if (larbid != larbidx) {
 			dev_err(dev, "Can only use one larb. Fail@larb%d-%d.\n",
 				larbid, larbidx);
-			return ERR_PTR(-EINVAL);
+			ret = -EINVAL;
+			goto err_mapping;
 		}
 	}
 
 	larbdev = data->larb_imu[larbid].dev;
-	if (!larbdev)
-		return ERR_PTR(-EINVAL);
+	if (!larbdev) {
+		ret = -EINVAL;
+		goto err_mapping;
+	}
 
 	link = device_link_add(dev, larbdev,
 			       DL_FLAG_PM_RUNTIME | DL_FLAG_STATELESS);
 	if (!link)
 		dev_err(dev, "Unable to link %s\n", dev_name(larbdev));
 
+	dev_iommu_priv_set(pinf->dev, mdev);
 	return &data->iommu;
+
+err_mapping:
+	arm_iommu_release_mapping(mtk_mapping);
+err_free:
+	kfree(mdev);
+	return ERR_PTR(ret);
 }
 
 static void mtk_iommu_v1_probe_finalize(struct device *dev)
 {
+	struct mtk_iommu_v1_device *mdev = dev_iommu_priv_get(dev);
+	struct mtk_iommu_v1_data *data = mdev->iommu;
 	struct dma_iommu_mapping *mtk_mapping;
-	struct mtk_iommu_v1_data *data;
 	int err;
 
-	data        = dev_iommu_priv_get(dev);
 	mtk_mapping = data->mapping;
 
 	err = arm_iommu_attach_device(dev, mtk_mapping);
@@ -525,15 +497,15 @@ static void mtk_iommu_v1_probe_finalize(struct device *dev)
 
 static void mtk_iommu_v1_release_device(struct device *dev)
 {
-	struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
-	struct mtk_iommu_v1_data *data;
+	struct mtk_iommu_v1_device *mdev = dev_iommu_priv_get(dev);
+	struct mtk_iommu_v1_data *data = mdev->iommu;
 	struct device *larbdev;
 	unsigned int larbid;
 
-	data = dev_iommu_priv_get(dev);
-	larbid = mt2701_m4u_to_larb(fwspec->ids[0]);
+	larbid = mt2701_m4u_to_larb(mdev->ids[0]);
 	larbdev = data->larb_imu[larbid].dev;
 	device_link_remove(dev, larbdev);
+	kfree(mdev);
 }
 
 static int mtk_iommu_v1_hw_init(const struct mtk_iommu_v1_data *data)
@@ -580,7 +552,7 @@ static int mtk_iommu_v1_hw_init(const struct mtk_iommu_v1_data *data)
 static const struct iommu_ops mtk_iommu_v1_ops = {
 	.identity_domain = &mtk_iommu_v1_identity_domain,
 	.domain_alloc_paging = mtk_iommu_v1_domain_alloc_paging,
-	.probe_device	= mtk_iommu_v1_probe_device,
+	.probe_device_pinf = mtk_iommu_v1_probe_device,
 	.probe_finalize = mtk_iommu_v1_probe_finalize,
 	.release_device	= mtk_iommu_v1_release_device,
 	.device_group	= generic_device_group,
-- 
2.42.0


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

* [PATCH 22/30] iommu/qcom: Move to iommu_fw_alloc_per_device_ids()
  2023-11-30  1:10 [PATCH 00/30] Make a new API for drivers to use to get their FW Jason Gunthorpe
                   ` (20 preceding siblings ...)
  2023-11-30  1:10 ` [PATCH 21/30] iommu/mtk_v1: " Jason Gunthorpe
@ 2023-11-30  1:10 ` Jason Gunthorpe
  2023-11-30  1:10 ` [PATCH 23/30] iommu/viot: Add iommu_viot_get_single_iommu() Jason Gunthorpe
                   ` (7 subsequent siblings)
  29 siblings, 0 replies; 34+ messages in thread
From: Jason Gunthorpe @ 2023-11-30  1:10 UTC (permalink / raw)
  To: acpica-devel, Andy Gross, Alim Akhtar, Alyssa Rosenzweig,
	Bjorn Andersson, AngeloGioacchino Del Regno, asahi, Baolin Wang,
	devicetree, Frank Rowand, Hanjun Guo, Gustavo A. R. Silva,
	Heiko Stuebner, iommu, Jean-Philippe Brucker, Jernej Skrabec,
	Jonathan Hunter, Joerg Roedel, Kees Cook, Konrad Dybcio,
	Krzysztof Kozlowski, Len Brown, linux-acpi, linux-arm-kernel,
	linux-arm-msm, linux-hardening, linux-mediatek, linux-rockchip,
	linux-samsung-soc, linux-sunxi, linux-tegra, Lorenzo Pieralisi,
	Marek Szyprowski, Hector Martin, Matthias Brugger, Orson Zhai,
	Rafael J. Wysocki, Rob Clark, Robert Moore, Rob Herring,
	Robin Murphy, Samuel Holland, Sudeep Holla, Sven Peter,
	Thierry Reding, Krishna Reddy, virtualization, Chen-Yu Tsai,
	Will Deacon, Yong Wu, Chunyan Zhang
  Cc: André Draszik, patches

qcom supports a single iommu instance with multiple ids.

Introduce a per-device data to store the iommu and ids list. Allocate and
initialize it with iommu_fw_alloc_per_device_ids(). Remove
qcom_iommu_of_xlate().

This already was checking that all instances are the same, it is now done
in common code.

Convert the rest of the funcs from calling dev_iommu_fwspec_get() to using
the per-device data and remove all use of fwspec. Store the per-dev data
in the qcom_iommu_domain instead of the iommu and fwspec pointers.

Convert the places using dev_iommu_priv_get() to use the per-device data
not the iommu.

Remove to_iommu().

Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
---
 drivers/iommu/arm/arm-smmu/qcom_iommu.c | 161 ++++++++++++------------
 1 file changed, 81 insertions(+), 80 deletions(-)

diff --git a/drivers/iommu/arm/arm-smmu/qcom_iommu.c b/drivers/iommu/arm/arm-smmu/qcom_iommu.c
index 33f3c870086cea..4baca45df99971 100644
--- a/drivers/iommu/arm/arm-smmu/qcom_iommu.c
+++ b/drivers/iommu/arm/arm-smmu/qcom_iommu.c
@@ -17,6 +17,7 @@
 #include <linux/io-64-nonatomic-hi-lo.h>
 #include <linux/io-pgtable.h>
 #include <linux/iommu.h>
+#include <linux/iommu-driver.h>
 #include <linux/iopoll.h>
 #include <linux/kconfig.h>
 #include <linux/init.h>
@@ -54,6 +55,12 @@ struct qcom_iommu_dev {
 	struct qcom_iommu_ctx	*ctxs[];   /* indexed by asid */
 };
 
+struct qcom_iommu_master {
+	struct qcom_iommu_dev *iommu;
+	unsigned int num_ids;
+	u32 ids[] __counted_by(num_ids);
+};
+
 struct qcom_iommu_ctx {
 	struct device		*dev;
 	void __iomem		*base;
@@ -68,8 +75,7 @@ struct qcom_iommu_domain {
 	spinlock_t		 pgtbl_lock;
 	struct mutex		 init_mutex; /* Protects iommu pointer */
 	struct iommu_domain	 domain;
-	struct qcom_iommu_dev	*iommu;
-	struct iommu_fwspec	*fwspec;
+	struct qcom_iommu_master *master;
 };
 
 static struct qcom_iommu_domain *to_qcom_iommu_domain(struct iommu_domain *dom)
@@ -81,7 +87,7 @@ static const struct iommu_ops qcom_iommu_ops;
 
 static struct qcom_iommu_ctx * to_ctx(struct qcom_iommu_domain *d, unsigned asid)
 {
-	struct qcom_iommu_dev *qcom_iommu = d->iommu;
+	struct qcom_iommu_dev *qcom_iommu = d->master->iommu;
 	if (!qcom_iommu)
 		return NULL;
 	return qcom_iommu->ctxs[asid];
@@ -114,11 +120,11 @@ iommu_readq(struct qcom_iommu_ctx *ctx, unsigned reg)
 static void qcom_iommu_tlb_sync(void *cookie)
 {
 	struct qcom_iommu_domain *qcom_domain = cookie;
-	struct iommu_fwspec *fwspec = qcom_domain->fwspec;
+	struct qcom_iommu_master *master = qcom_domain->master;
 	unsigned i;
 
-	for (i = 0; i < fwspec->num_ids; i++) {
-		struct qcom_iommu_ctx *ctx = to_ctx(qcom_domain, fwspec->ids[i]);
+	for (i = 0; i < master->num_ids; i++) {
+		struct qcom_iommu_ctx *ctx = to_ctx(qcom_domain, master->ids[i]);
 		unsigned int val, ret;
 
 		iommu_writel(ctx, ARM_SMMU_CB_TLBSYNC, 0);
@@ -133,11 +139,11 @@ static void qcom_iommu_tlb_sync(void *cookie)
 static void qcom_iommu_tlb_inv_context(void *cookie)
 {
 	struct qcom_iommu_domain *qcom_domain = cookie;
-	struct iommu_fwspec *fwspec = qcom_domain->fwspec;
+	struct qcom_iommu_master *master = qcom_domain->master;
 	unsigned i;
 
-	for (i = 0; i < fwspec->num_ids; i++) {
-		struct qcom_iommu_ctx *ctx = to_ctx(qcom_domain, fwspec->ids[i]);
+	for (i = 0; i < master->num_ids; i++) {
+		struct qcom_iommu_ctx *ctx = to_ctx(qcom_domain, master->ids[i]);
 		iommu_writel(ctx, ARM_SMMU_CB_S1_TLBIASID, ctx->asid);
 	}
 
@@ -148,13 +154,13 @@ static void qcom_iommu_tlb_inv_range_nosync(unsigned long iova, size_t size,
 					    size_t granule, bool leaf, void *cookie)
 {
 	struct qcom_iommu_domain *qcom_domain = cookie;
-	struct iommu_fwspec *fwspec = qcom_domain->fwspec;
+	struct qcom_iommu_master *master = qcom_domain->master;
 	unsigned i, reg;
 
 	reg = leaf ? ARM_SMMU_CB_S1_TLBIVAL : ARM_SMMU_CB_S1_TLBIVA;
 
-	for (i = 0; i < fwspec->num_ids; i++) {
-		struct qcom_iommu_ctx *ctx = to_ctx(qcom_domain, fwspec->ids[i]);
+	for (i = 0; i < master->num_ids; i++) {
+		struct qcom_iommu_ctx *ctx = to_ctx(qcom_domain, master->ids[i]);
 		size_t s = size;
 
 		iova = (iova >> 12) << 12;
@@ -218,14 +224,14 @@ static int qcom_iommu_init_domain(struct iommu_domain *domain,
 				  struct device *dev)
 {
 	struct qcom_iommu_domain *qcom_domain = to_qcom_iommu_domain(domain);
-	struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
+	struct qcom_iommu_master *master = dev_iommu_priv_get(dev);
 	struct io_pgtable_ops *pgtbl_ops;
 	struct io_pgtable_cfg pgtbl_cfg;
 	int i, ret = 0;
 	u32 reg;
 
 	mutex_lock(&qcom_domain->init_mutex);
-	if (qcom_domain->iommu)
+	if (qcom_domain->master)
 		goto out_unlock;
 
 	pgtbl_cfg = (struct io_pgtable_cfg) {
@@ -236,8 +242,7 @@ static int qcom_iommu_init_domain(struct iommu_domain *domain,
 		.iommu_dev	= qcom_iommu->dev,
 	};
 
-	qcom_domain->iommu = qcom_iommu;
-	qcom_domain->fwspec = fwspec;
+	qcom_domain->master = master;
 
 	pgtbl_ops = alloc_io_pgtable_ops(ARM_32_LPAE_S1, &pgtbl_cfg, qcom_domain);
 	if (!pgtbl_ops) {
@@ -251,8 +256,8 @@ static int qcom_iommu_init_domain(struct iommu_domain *domain,
 	domain->geometry.aperture_end = (1ULL << pgtbl_cfg.ias) - 1;
 	domain->geometry.force_aperture = true;
 
-	for (i = 0; i < fwspec->num_ids; i++) {
-		struct qcom_iommu_ctx *ctx = to_ctx(qcom_domain, fwspec->ids[i]);
+	for (i = 0; i < master->num_ids; i++) {
+		struct qcom_iommu_ctx *ctx = to_ctx(qcom_domain, master->ids[i]);
 
 		if (!ctx->secure_init) {
 			ret = qcom_scm_restore_sec_cfg(qcom_iommu->sec_id, ctx->asid);
@@ -316,7 +321,7 @@ static int qcom_iommu_init_domain(struct iommu_domain *domain,
 	return 0;
 
 out_clear_iommu:
-	qcom_domain->iommu = NULL;
+	qcom_domain->master = NULL;
 out_unlock:
 	mutex_unlock(&qcom_domain->init_mutex);
 	return ret;
@@ -345,16 +350,16 @@ static void qcom_iommu_domain_free(struct iommu_domain *domain)
 {
 	struct qcom_iommu_domain *qcom_domain = to_qcom_iommu_domain(domain);
 
-	if (qcom_domain->iommu) {
+	if (qcom_domain->master) {
 		/*
 		 * NOTE: unmap can be called after client device is powered
 		 * off, for example, with GPUs or anything involving dma-buf.
 		 * So we cannot rely on the device_link.  Make sure the IOMMU
 		 * is on to avoid unclocked accesses in the TLB inv path:
 		 */
-		pm_runtime_get_sync(qcom_domain->iommu->dev);
+		pm_runtime_get_sync(qcom_domain->master->iommu->dev);
 		free_io_pgtable_ops(qcom_domain->pgtbl_ops);
-		pm_runtime_put_sync(qcom_domain->iommu->dev);
+		pm_runtime_put_sync(qcom_domain->master->iommu->dev);
 	}
 
 	kfree(qcom_domain);
@@ -362,7 +367,8 @@ static void qcom_iommu_domain_free(struct iommu_domain *domain)
 
 static int qcom_iommu_attach_dev(struct iommu_domain *domain, struct device *dev)
 {
-	struct qcom_iommu_dev *qcom_iommu = dev_iommu_priv_get(dev);
+	struct qcom_iommu_master *master = dev_iommu_priv_get(dev);
+	struct qcom_iommu_dev *qcom_iommu = master->iommu;
 	struct qcom_iommu_domain *qcom_domain = to_qcom_iommu_domain(domain);
 	int ret;
 
@@ -382,7 +388,7 @@ static int qcom_iommu_attach_dev(struct iommu_domain *domain, struct device *dev
 	 * Sanity check the domain. We don't support domains across
 	 * different IOMMUs.
 	 */
-	if (qcom_domain->iommu != qcom_iommu)
+	if (qcom_domain->master->iommu != qcom_iommu)
 		return -EINVAL;
 
 	return 0;
@@ -393,20 +399,20 @@ static int qcom_iommu_identity_attach(struct iommu_domain *identity_domain,
 {
 	struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
 	struct qcom_iommu_domain *qcom_domain;
-	struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
-	struct qcom_iommu_dev *qcom_iommu = dev_iommu_priv_get(dev);
+	struct qcom_iommu_master *master = dev_iommu_priv_get(dev);
+	struct qcom_iommu_dev *qcom_iommu = master->iommu;
 	unsigned int i;
 
 	if (domain == identity_domain || !domain)
 		return 0;
 
 	qcom_domain = to_qcom_iommu_domain(domain);
-	if (WARN_ON(!qcom_domain->iommu))
+	if (WARN_ON(!qcom_domain->master))
 		return -EINVAL;
 
 	pm_runtime_get_sync(qcom_iommu->dev);
-	for (i = 0; i < fwspec->num_ids; i++) {
-		struct qcom_iommu_ctx *ctx = to_ctx(qcom_domain, fwspec->ids[i]);
+	for (i = 0; i < master->num_ids; i++) {
+		struct qcom_iommu_ctx *ctx = to_ctx(qcom_domain, master->ids[i]);
 
 		/* Disable the context bank: */
 		iommu_writel(ctx, ARM_SMMU_CB_SCTLR, 0);
@@ -461,11 +467,11 @@ static size_t qcom_iommu_unmap(struct iommu_domain *domain, unsigned long iova,
 	 * cannot rely on the device_link.  Make sure the IOMMU is on to
 	 * avoid unclocked accesses in the TLB inv path:
 	 */
-	pm_runtime_get_sync(qcom_domain->iommu->dev);
+	pm_runtime_get_sync(qcom_domain->master->iommu->dev);
 	spin_lock_irqsave(&qcom_domain->pgtbl_lock, flags);
 	ret = ops->unmap_pages(ops, iova, pgsize, pgcount, gather);
 	spin_unlock_irqrestore(&qcom_domain->pgtbl_lock, flags);
-	pm_runtime_put_sync(qcom_domain->iommu->dev);
+	pm_runtime_put_sync(qcom_domain->master->iommu->dev);
 
 	return ret;
 }
@@ -478,9 +484,9 @@ static void qcom_iommu_flush_iotlb_all(struct iommu_domain *domain)
 	if (!qcom_domain->pgtbl_ops)
 		return;
 
-	pm_runtime_get_sync(qcom_domain->iommu->dev);
+	pm_runtime_get_sync(qcom_domain->master->iommu->dev);
 	qcom_iommu_tlb_sync(pgtable->cookie);
-	pm_runtime_put_sync(qcom_domain->iommu->dev);
+	pm_runtime_put_sync(qcom_domain->master->iommu->dev);
 }
 
 static void qcom_iommu_iotlb_sync(struct iommu_domain *domain,
@@ -523,13 +529,38 @@ static bool qcom_iommu_capable(struct device *dev, enum iommu_cap cap)
 	}
 }
 
-static struct iommu_device *qcom_iommu_probe_device(struct device *dev)
+static struct iommu_device *
+qcom_iommu_probe_device(struct iommu_probe_info *pinf)
 {
-	struct qcom_iommu_dev *qcom_iommu = dev_iommu_priv_get(dev);
+	struct qcom_iommu_dev *qcom_iommu;
+	struct qcom_iommu_master *master;
+	struct device *dev = pinf->dev;
 	struct device_link *link;
+	int ret;
+	int i;
 
-	if (!qcom_iommu)
-		return ERR_PTR(-ENODEV);
+	qcom_iommu = iommu_of_get_single_iommu(pinf, &qcom_iommu_ops, 1,
+					       struct qcom_iommu_dev, iommu);
+	if (IS_ERR(qcom_iommu))
+		return ERR_CAST(qcom_iommu);
+
+	master = iommu_fw_alloc_per_device_ids(pinf, master);
+	if (IS_ERR(master))
+		return ERR_CAST(master);
+
+	for (i = 0; i != master->num_ids; i++) {
+		u32 asid = master->ids[i];
+
+		/*
+		 * Make sure the asid specified in dt is valid, so we don't have
+		 * to sanity check this elsewhere:
+		 */
+		if (WARN_ON(asid > qcom_iommu->max_asid) ||
+		    WARN_ON(qcom_iommu->ctxs[asid] == NULL)) {
+			ret = -EINVAL;
+			goto err_free;
+		}
+	}
 
 	/*
 	 * Establish the link between iommu and master, so that the
@@ -540,63 +571,33 @@ static struct iommu_device *qcom_iommu_probe_device(struct device *dev)
 	if (!link) {
 		dev_err(qcom_iommu->dev, "Unable to create device link between %s and %s\n",
 			dev_name(qcom_iommu->dev), dev_name(dev));
-		return ERR_PTR(-ENODEV);
+		ret = -ENODEV;
+		goto err_free;
 	}
 
+	dev_iommu_priv_set(dev, master);
 	return &qcom_iommu->iommu;
+
+err_free:
+	kfree(master);
+	return ERR_PTR(ret);
 }
 
-static int qcom_iommu_of_xlate(struct device *dev, struct of_phandle_args *args)
+static void qcom_iommu_release_device(struct device *dev)
 {
-	struct qcom_iommu_dev *qcom_iommu;
-	struct platform_device *iommu_pdev;
-	unsigned asid = args->args[0];
+	struct qcom_iommu_master *master = dev_iommu_priv_get(dev);
 
-	if (args->args_count != 1) {
-		dev_err(dev, "incorrect number of iommu params found for %s "
-			"(found %d, expected 1)\n",
-			args->np->full_name, args->args_count);
-		return -EINVAL;
-	}
-
-	iommu_pdev = of_find_device_by_node(args->np);
-	if (WARN_ON(!iommu_pdev))
-		return -EINVAL;
-
-	qcom_iommu = platform_get_drvdata(iommu_pdev);
-
-	/* make sure the asid specified in dt is valid, so we don't have
-	 * to sanity check this elsewhere:
-	 */
-	if (WARN_ON(asid > qcom_iommu->max_asid) ||
-	    WARN_ON(qcom_iommu->ctxs[asid] == NULL)) {
-		put_device(&iommu_pdev->dev);
-		return -EINVAL;
-	}
-
-	if (!dev_iommu_priv_get(dev)) {
-		dev_iommu_priv_set(dev, qcom_iommu);
-	} else {
-		/* make sure devices iommus dt node isn't referring to
-		 * multiple different iommu devices.  Multiple context
-		 * banks are ok, but multiple devices are not:
-		 */
-		if (WARN_ON(qcom_iommu != dev_iommu_priv_get(dev))) {
-			put_device(&iommu_pdev->dev);
-			return -EINVAL;
-		}
-	}
-
-	return iommu_fwspec_add_ids(dev, &asid, 1);
+	kfree(master);
 }
 
 static const struct iommu_ops qcom_iommu_ops = {
 	.identity_domain = &qcom_iommu_identity_domain,
 	.capable	= qcom_iommu_capable,
 	.domain_alloc_paging = qcom_iommu_domain_alloc_paging,
-	.probe_device	= qcom_iommu_probe_device,
+	.probe_device_pinf = qcom_iommu_probe_device,
+	.release_device = qcom_iommu_release_device,
 	.device_group	= generic_device_group,
-	.of_xlate	= qcom_iommu_of_xlate,
+	.of_xlate	= iommu_dummy_of_xlate,
 	.pgsize_bitmap	= SZ_4K | SZ_64K | SZ_1M | SZ_16M,
 	.default_domain_ops = &(const struct iommu_domain_ops) {
 		.attach_dev	= qcom_iommu_attach_dev,
-- 
2.42.0


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

* [PATCH 23/30] iommu/viot: Add iommu_viot_get_single_iommu()
  2023-11-30  1:10 [PATCH 00/30] Make a new API for drivers to use to get their FW Jason Gunthorpe
                   ` (21 preceding siblings ...)
  2023-11-30  1:10 ` [PATCH 22/30] iommu/qcom: " Jason Gunthorpe
@ 2023-11-30  1:10 ` Jason Gunthorpe
  2023-11-30  1:10 ` [PATCH 24/30] iommu/virtio: Move to iommu_fw_alloc_per_device_ids() Jason Gunthorpe
                   ` (6 subsequent siblings)
  29 siblings, 0 replies; 34+ messages in thread
From: Jason Gunthorpe @ 2023-11-30  1:10 UTC (permalink / raw)
  To: acpica-devel, Andy Gross, Alim Akhtar, Alyssa Rosenzweig,
	Bjorn Andersson, AngeloGioacchino Del Regno, asahi, Baolin Wang,
	devicetree, Frank Rowand, Hanjun Guo, Gustavo A. R. Silva,
	Heiko Stuebner, iommu, Jean-Philippe Brucker, Jernej Skrabec,
	Jonathan Hunter, Joerg Roedel, Kees Cook, Konrad Dybcio,
	Krzysztof Kozlowski, Len Brown, linux-acpi, linux-arm-kernel,
	linux-arm-msm, linux-hardening, linux-mediatek, linux-rockchip,
	linux-samsung-soc, linux-sunxi, linux-tegra, Lorenzo Pieralisi,
	Marek Szyprowski, Hector Martin, Matthias Brugger, Orson Zhai,
	Rafael J. Wysocki, Rob Clark, Robert Moore, Rob Herring,
	Robin Murphy, Samuel Holland, Sudeep Holla, Sven Peter,
	Thierry Reding, Krishna Reddy, virtualization, Chen-Yu Tsai,
	Will Deacon, Yong Wu, Chunyan Zhang
  Cc: André Draszik, patches

This is the ACPI VIOT version like iommu_of_get_single_iommu().

It parses the ACPI table, confirms all entries points to a single
iommu_driver and then returns a pointer to it.

Also cache the u32 id list in the iommu_probe_info and provide a getter
function which re-parses in case we overflow the cache.

Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
---
 drivers/acpi/scan.c          |  1 +
 drivers/iommu/Makefile       |  1 +
 drivers/iommu/viot_iommu.c   | 70 ++++++++++++++++++++++++++++++++++++
 include/linux/iommu-driver.h | 25 +++++++++++++
 4 files changed, 97 insertions(+)
 create mode 100644 drivers/iommu/viot_iommu.c

diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index de36299c3b75bf..9ec01196573b6e 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -1571,6 +1571,7 @@ static int acpi_iommu_configure_id(struct device *dev, const u32 *id_in)
 	struct iommu_probe_info pinf = {
 		.dev = dev,
 		.is_dma_configure = true,
+		.is_acpi = true,
 	};
 
 	/* Serialise to make dev->iommu stable under our potential fwspec */
diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile
index 95ad9dbfbda022..9c35b106cecb2e 100644
--- a/drivers/iommu/Makefile
+++ b/drivers/iommu/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_IOMMU_IO_PGTABLE_LPAE) += io-pgtable-arm.o
 obj-$(CONFIG_IOMMU_IO_PGTABLE_DART) += io-pgtable-dart.o
 obj-$(CONFIG_IOMMU_IOVA) += iova.o
 obj-$(CONFIG_OF_IOMMU)	+= of_iommu.o
+obj-$(CONFIG_ACPI_VIOT)	+= viot_iommu.o
 obj-$(CONFIG_MSM_IOMMU) += msm_iommu.o
 obj-$(CONFIG_IPMMU_VMSA) += ipmmu-vmsa.o
 obj-$(CONFIG_IRQ_REMAP) += irq_remapping.o
diff --git a/drivers/iommu/viot_iommu.c b/drivers/iommu/viot_iommu.c
new file mode 100644
index 00000000000000..e35bd4099e6c6a
--- /dev/null
+++ b/drivers/iommu/viot_iommu.c
@@ -0,0 +1,70 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES
+ */
+#include <linux/acpi_viot.h>
+#include <linux/iommu.h>
+#include <linux/iommu-driver.h>
+
+struct parse_info {
+	struct iommu_probe_info *pinf;
+	const struct iommu_ops *ops;
+	u32 *ids;
+};
+
+static int parse_single_iommu(struct viot_iommu *viommu, u32 epid, void *_info)
+{
+	struct fwnode_handle *fwnode = viommu->fwnode;
+	struct parse_info *info = _info;
+	struct iommu_probe_info *pinf = info->pinf;
+	struct iommu_device *iommu;
+
+	/* We're not translating ourself */
+	if (device_match_fwnode(pinf->dev, fwnode))
+		return -ENODEV;
+
+	iommu = iommu_device_from_fwnode_pinf(pinf, info->ops, fwnode);
+	if (IS_ERR(iommu)) {
+		if (!IS_ENABLED(CONFIG_VIRTIO_IOMMU) &&
+		    iommu == ERR_PTR(-EPROBE_DEFER))
+			return -ENODEV;
+		return PTR_ERR(iommu);
+	}
+	iommu_fw_cache_id(pinf, epid);
+	return 0;
+}
+
+static int parse_read_ids(struct viot_iommu *viommu, u32 epid, void *_info)
+{
+	struct parse_info *info = _info;
+
+	*info->ids = epid;
+	(*info->ids)++;
+	return 0;
+}
+
+static int viot_get_u32_ids(struct iommu_probe_info *pinf, u32 *ids)
+{
+	struct parse_info info = { .pinf = pinf, .ids = ids };
+
+	return viot_iommu_for_each_id(pinf->dev, parse_read_ids, &info);
+}
+
+struct iommu_device *
+__iommu_viot_get_single_iommu(struct iommu_probe_info *pinf,
+			      const struct iommu_ops *ops)
+{
+	struct parse_info info = { .pinf = pinf, .ops = ops };
+	int err;
+
+	if (!pinf->is_dma_configure || !pinf->is_acpi)
+		return ERR_PTR(-ENODEV);
+
+	iommu_fw_clear_cache(pinf);
+	err = viot_iommu_for_each_id(pinf->dev, parse_single_iommu, &info);
+	if (err)
+		return ERR_PTR(err);
+	pinf->get_u32_ids = viot_get_u32_ids;
+	return iommu_fw_finish_get_single(pinf);
+}
+EXPORT_SYMBOL(__iommu_viot_get_single_iommu);
diff --git a/include/linux/iommu-driver.h b/include/linux/iommu-driver.h
index 632c7b4a389abe..ce0ba1f35bb5dc 100644
--- a/include/linux/iommu-driver.h
+++ b/include/linux/iommu-driver.h
@@ -45,6 +45,7 @@ struct iommu_probe_info {
 	u32 cached_ids[8];
 	bool defer_setup : 1;
 	bool is_dma_configure : 1;
+	bool is_acpi : 1;
 	bool cached_single_iommu : 1;
 };
 
@@ -188,4 +189,28 @@ static inline int iommu_dummy_of_xlate(struct device *dev,
 	return 0;
 }
 
+#define __iommu_first(a, b)                              \
+	({                                               \
+		struct iommu_device *a_dev = a;          \
+		a_dev != ERR_PTR(-ENODEV) ? a_dev : (b); \
+	})
+
+#if IS_ENABLED(CONFIG_ACPI_VIOT)
+struct iommu_device *
+__iommu_viot_get_single_iommu(struct iommu_probe_info *pinf,
+			      const struct iommu_ops *ops);
+#else
+static inline struct iommu_device *
+__iommu_viot_get_single_iommu(struct iommu_probe_info *pinf,
+			      const struct iommu_ops *ops)
+{
+	return ERR_PTR(-ENODEV);
+}
+#endif
+#define iommu_viot_get_single_iommu(pinf, ops, drv_struct, member)         \
+	container_of_err(                                                  \
+		__iommu_first(__iommu_viot_get_single_iommu(pinf, ops),    \
+			      __iommu_of_get_single_iommu(pinf, ops, -1)), \
+		drv_struct, member)
+
 #endif
-- 
2.42.0


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

* [PATCH 24/30] iommu/virtio: Move to iommu_fw_alloc_per_device_ids()
  2023-11-30  1:10 [PATCH 00/30] Make a new API for drivers to use to get their FW Jason Gunthorpe
                   ` (22 preceding siblings ...)
  2023-11-30  1:10 ` [PATCH 23/30] iommu/viot: Add iommu_viot_get_single_iommu() Jason Gunthorpe
@ 2023-11-30  1:10 ` Jason Gunthorpe
  2023-11-30  1:10 ` [PATCH 25/30] iommu/iort: Add iommu_iort_get_single_iommu() Jason Gunthorpe
                   ` (5 subsequent siblings)
  29 siblings, 0 replies; 34+ messages in thread
From: Jason Gunthorpe @ 2023-11-30  1:10 UTC (permalink / raw)
  To: acpica-devel, Andy Gross, Alim Akhtar, Alyssa Rosenzweig,
	Bjorn Andersson, AngeloGioacchino Del Regno, asahi, Baolin Wang,
	devicetree, Frank Rowand, Hanjun Guo, Gustavo A. R. Silva,
	Heiko Stuebner, iommu, Jean-Philippe Brucker, Jernej Skrabec,
	Jonathan Hunter, Joerg Roedel, Kees Cook, Konrad Dybcio,
	Krzysztof Kozlowski, Len Brown, linux-acpi, linux-arm-kernel,
	linux-arm-msm, linux-hardening, linux-mediatek, linux-rockchip,
	linux-samsung-soc, linux-sunxi, linux-tegra, Lorenzo Pieralisi,
	Marek Szyprowski, Hector Martin, Matthias Brugger, Orson Zhai,
	Rafael J. Wysocki, Rob Clark, Robert Moore, Rob Herring,
	Robin Murphy, Samuel Holland, Sudeep Holla, Sven Peter,
	Thierry Reding, Krishna Reddy, virtualization, Chen-Yu Tsai,
	Will Deacon, Yong Wu, Chunyan Zhang
  Cc: André Draszik, patches

virtio supports a single iommu instance with multiple ids.

It has a combined ACPI (via the VIOT table) and OF probe path, add
iommu_viot_get_single_iommu() to respresent this.

It already has a per-instance structure, extend it with the ids[]
array and use iommu_fw_alloc_per_device_ids() to populate it.

Convert the rest of the funcs from calling dev_iommu_fwspec_get() to using
he per-device data and remove all use of fwspec.

Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
---
 drivers/iommu/virtio-iommu.c | 67 +++++++++++++-----------------------
 1 file changed, 23 insertions(+), 44 deletions(-)

diff --git a/drivers/iommu/virtio-iommu.c b/drivers/iommu/virtio-iommu.c
index b1a7b14a6c7a2f..767919bf848999 100644
--- a/drivers/iommu/virtio-iommu.c
+++ b/drivers/iommu/virtio-iommu.c
@@ -77,6 +77,8 @@ struct viommu_endpoint {
 	struct viommu_dev		*viommu;
 	struct viommu_domain		*vdomain;
 	struct list_head		resv_regions;
+	unsigned int			num_ids;
+	u32				ids[] __counted_by(num_ids);
 };
 
 struct viommu_request {
@@ -510,19 +512,16 @@ static int viommu_add_resv_mem(struct viommu_endpoint *vdev,
 	return 0;
 }
 
-static int viommu_probe_endpoint(struct viommu_dev *viommu, struct device *dev)
+static int viommu_probe_endpoint(struct viommu_endpoint *vdev)
 {
 	int ret;
 	u16 type, len;
 	size_t cur = 0;
 	size_t probe_len;
+	struct device *dev = vdev->dev;
 	struct virtio_iommu_req_probe *probe;
 	struct virtio_iommu_probe_property *prop;
-	struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
-	struct viommu_endpoint *vdev = dev_iommu_priv_get(dev);
-
-	if (!fwspec->num_ids)
-		return -EINVAL;
+	struct viommu_dev *viommu = vdev->viommu;
 
 	probe_len = sizeof(*probe) + viommu->probe_size +
 		    sizeof(struct virtio_iommu_req_tail);
@@ -535,7 +534,7 @@ static int viommu_probe_endpoint(struct viommu_dev *viommu, struct device *dev)
 	 * For now, assume that properties of an endpoint that outputs multiple
 	 * IDs are consistent. Only probe the first one.
 	 */
-	probe->endpoint = cpu_to_le32(fwspec->ids[0]);
+	probe->endpoint = cpu_to_le32(vdev->ids[0]);
 
 	ret = viommu_send_req_sync(viommu, probe, probe_len);
 	if (ret)
@@ -721,7 +720,6 @@ static int viommu_attach_dev(struct iommu_domain *domain, struct device *dev)
 	int i;
 	int ret = 0;
 	struct virtio_iommu_req_attach req;
-	struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
 	struct viommu_endpoint *vdev = dev_iommu_priv_get(dev);
 	struct viommu_domain *vdomain = to_viommu_domain(domain);
 
@@ -763,8 +761,8 @@ static int viommu_attach_dev(struct iommu_domain *domain, struct device *dev)
 	if (vdomain->bypass)
 		req.flags |= cpu_to_le32(VIRTIO_IOMMU_ATTACH_F_BYPASS);
 
-	for (i = 0; i < fwspec->num_ids; i++) {
-		req.endpoint = cpu_to_le32(fwspec->ids[i]);
+	for (i = 0; i < vdev->num_ids; i++) {
+		req.endpoint = cpu_to_le32(vdev->ids[i]);
 
 		ret = viommu_send_req_sync(vdomain->viommu, &req, sizeof(req));
 		if (ret)
@@ -792,7 +790,6 @@ static void viommu_detach_dev(struct viommu_endpoint *vdev)
 	int i;
 	struct virtio_iommu_req_detach req;
 	struct viommu_domain *vdomain = vdev->vdomain;
-	struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(vdev->dev);
 
 	if (!vdomain)
 		return;
@@ -802,8 +799,8 @@ static void viommu_detach_dev(struct viommu_endpoint *vdev)
 		.domain		= cpu_to_le32(vdomain->id),
 	};
 
-	for (i = 0; i < fwspec->num_ids; i++) {
-		req.endpoint = cpu_to_le32(fwspec->ids[i]);
+	for (i = 0; i < vdev->num_ids; i++) {
+		req.endpoint = cpu_to_le32(vdev->ids[i]);
 		WARN_ON(viommu_send_req_sync(vdev->viommu, &req, sizeof(req)));
 	}
 	vdomain->nr_endpoints--;
@@ -974,34 +971,21 @@ static void viommu_get_resv_regions(struct device *dev, struct list_head *head)
 static struct iommu_ops viommu_ops;
 static struct virtio_driver virtio_iommu_drv;
 
-static int viommu_match_node(struct device *dev, const void *data)
-{
-	return device_match_fwnode(dev->parent, data);
-}
-
-static struct viommu_dev *viommu_get_by_fwnode(struct fwnode_handle *fwnode)
-{
-	struct device *dev = driver_find_device(&virtio_iommu_drv.driver, NULL,
-						fwnode, viommu_match_node);
-	put_device(dev);
-
-	return dev ? dev_to_virtio(dev)->priv : NULL;
-}
-
-static struct iommu_device *viommu_probe_device(struct device *dev)
+static struct iommu_device *viommu_probe_device(struct iommu_probe_info *pinf)
 {
 	int ret;
+	struct viommu_dev *viommu;
 	struct viommu_endpoint *vdev;
-	struct viommu_dev *viommu = NULL;
-	struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
+	struct device *dev = pinf->dev;
 
-	viommu = viommu_get_by_fwnode(fwspec->iommu_fwnode);
-	if (!viommu)
-		return ERR_PTR(-ENODEV);
+	viommu = iommu_viot_get_single_iommu(pinf, &viommu_ops,
+					     struct viommu_dev, iommu);
+	if (IS_ERR(viommu))
+		return ERR_CAST(viommu);
 
-	vdev = kzalloc(sizeof(*vdev), GFP_KERNEL);
-	if (!vdev)
-		return ERR_PTR(-ENOMEM);
+	vdev = iommu_fw_alloc_per_device_ids(pinf, vdev);
+	if (IS_ERR(vdev))
+		return ERR_CAST(vdev);
 
 	vdev->dev = dev;
 	vdev->viommu = viommu;
@@ -1010,7 +994,7 @@ static struct iommu_device *viommu_probe_device(struct device *dev)
 
 	if (viommu->probe_size) {
 		/* Get additional information for this endpoint */
-		ret = viommu_probe_endpoint(viommu, dev);
+		ret = viommu_probe_endpoint(vdev);
 		if (ret)
 			goto err_free_dev;
 	}
@@ -1050,11 +1034,6 @@ static struct iommu_group *viommu_device_group(struct device *dev)
 		return generic_device_group(dev);
 }
 
-static int viommu_of_xlate(struct device *dev, struct of_phandle_args *args)
-{
-	return iommu_fwspec_add_ids(dev, args->args, 1);
-}
-
 static bool viommu_capable(struct device *dev, enum iommu_cap cap)
 {
 	switch (cap) {
@@ -1070,12 +1049,12 @@ static bool viommu_capable(struct device *dev, enum iommu_cap cap)
 static struct iommu_ops viommu_ops = {
 	.capable		= viommu_capable,
 	.domain_alloc		= viommu_domain_alloc,
-	.probe_device		= viommu_probe_device,
+	.probe_device_pinf	= viommu_probe_device,
 	.probe_finalize		= viommu_probe_finalize,
 	.release_device		= viommu_release_device,
 	.device_group		= viommu_device_group,
 	.get_resv_regions	= viommu_get_resv_regions,
-	.of_xlate		= viommu_of_xlate,
+	.of_xlate		= iommu_dummy_of_xlate,
 	.owner			= THIS_MODULE,
 	.default_domain_ops = &(const struct iommu_domain_ops) {
 		.attach_dev		= viommu_attach_dev,
-- 
2.42.0


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

* [PATCH 25/30] iommu/iort: Add iommu_iort_get_single_iommu()
  2023-11-30  1:10 [PATCH 00/30] Make a new API for drivers to use to get their FW Jason Gunthorpe
                   ` (23 preceding siblings ...)
  2023-11-30  1:10 ` [PATCH 24/30] iommu/virtio: Move to iommu_fw_alloc_per_device_ids() Jason Gunthorpe
@ 2023-11-30  1:10 ` Jason Gunthorpe
  2023-11-30  1:10 ` [PATCH 26/30] iommu/arm-smmu-v3: Move to iommu_fw_alloc_per_device_ids() Jason Gunthorpe
                   ` (4 subsequent siblings)
  29 siblings, 0 replies; 34+ messages in thread
From: Jason Gunthorpe @ 2023-11-30  1:10 UTC (permalink / raw)
  To: acpica-devel, Andy Gross, Alim Akhtar, Alyssa Rosenzweig,
	Bjorn Andersson, AngeloGioacchino Del Regno, asahi, Baolin Wang,
	devicetree, Frank Rowand, Hanjun Guo, Gustavo A. R. Silva,
	Heiko Stuebner, iommu, Jean-Philippe Brucker, Jernej Skrabec,
	Jonathan Hunter, Joerg Roedel, Kees Cook, Konrad Dybcio,
	Krzysztof Kozlowski, Len Brown, linux-acpi, linux-arm-kernel,
	linux-arm-msm, linux-hardening, linux-mediatek, linux-rockchip,
	linux-samsung-soc, linux-sunxi, linux-tegra, Lorenzo Pieralisi,
	Marek Szyprowski, Hector Martin, Matthias Brugger, Orson Zhai,
	Rafael J. Wysocki, Rob Clark, Robert Moore, Rob Herring,
	Robin Murphy, Samuel Holland, Sudeep Holla, Sven Peter,
	Thierry Reding, Krishna Reddy, virtualization, Chen-Yu Tsai,
	Will Deacon, Yong Wu, Chunyan Zhang
  Cc: André Draszik, patches

This API is basically the same as iommu_of_get_single_iommu(), except that
it will try to parse the ACPI IORT table if it is available.

The ACPI IORT table can return a flags value to indicate
IOMMU_FWSPEC_PCI_RC_ATS, return this through an output flags pointer.

Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
---
 drivers/acpi/arm64/iort.c    |  3 +-
 drivers/acpi/scan.c          |  1 +
 drivers/iommu/Makefile       |  1 +
 drivers/iommu/iommu.c        |  3 ++
 drivers/iommu/iort_iommu.c   | 98 ++++++++++++++++++++++++++++++++++++
 include/linux/acpi_iort.h    |  1 +
 include/linux/iommu-driver.h | 41 +++++++++++++++
 7 files changed, 146 insertions(+), 2 deletions(-)
 create mode 100644 drivers/iommu/iort_iommu.c

diff --git a/drivers/acpi/arm64/iort.c b/drivers/acpi/arm64/iort.c
index 798c0b344f4be8..6b2d50cc9ac180 100644
--- a/drivers/acpi/arm64/iort.c
+++ b/drivers/acpi/arm64/iort.c
@@ -79,8 +79,7 @@ static inline int iort_set_fwnode(struct acpi_iort_node *iort_node,
  *
  * Returns: fwnode_handle pointer on success, NULL on failure
  */
-static inline struct fwnode_handle *iort_get_fwnode(
-			struct acpi_iort_node *node)
+struct fwnode_handle *iort_get_fwnode(struct acpi_iort_node *node)
 {
 	struct iort_fwnode *curr;
 	struct fwnode_handle *fwnode = NULL;
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index 9ec01196573b6e..eb7406cdc9a464 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -1571,6 +1571,7 @@ static int acpi_iommu_configure_id(struct device *dev, const u32 *id_in)
 	struct iommu_probe_info pinf = {
 		.dev = dev,
 		.is_dma_configure = true,
+		.acpi_map_id = id_in,
 		.is_acpi = true,
 	};
 
diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile
index 9c35b106cecb2e..ebf6c151a97746 100644
--- a/drivers/iommu/Makefile
+++ b/drivers/iommu/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_IOMMU_IO_PGTABLE_LPAE) += io-pgtable-arm.o
 obj-$(CONFIG_IOMMU_IO_PGTABLE_DART) += io-pgtable-dart.o
 obj-$(CONFIG_IOMMU_IOVA) += iova.o
 obj-$(CONFIG_OF_IOMMU)	+= of_iommu.o
+obj-$(CONFIG_ACPI_IORT)	+= iort_iommu.o
 obj-$(CONFIG_ACPI_VIOT)	+= viot_iommu.o
 obj-$(CONFIG_MSM_IOMMU) += msm_iommu.o
 obj-$(CONFIG_IPMMU_VMSA) += ipmmu-vmsa.o
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index caf14a53ed1952..7468a64778931b 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -3030,6 +3030,9 @@ iommu_device_from_fwnode_pinf(struct iommu_probe_info *pinf,
 	if (!pinf->num_ids)
 		pinf->cached_single_iommu = true;
 
+	if (pinf->is_acpi)
+		pinf->acpi_fwnode = fwnode;
+
 	if (!iommu || iommu->fwnode != fwnode) {
 		iommu = iommu_device_from_fwnode(fwnode);
 		if (!iommu)
diff --git a/drivers/iommu/iort_iommu.c b/drivers/iommu/iort_iommu.c
new file mode 100644
index 00000000000000..9a997b0fd5d5f1
--- /dev/null
+++ b/drivers/iommu/iort_iommu.c
@@ -0,0 +1,98 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES
+ */
+#include <linux/acpi_iort.h>
+#include <acpi/actbl2.h>
+
+#include <linux/iommu.h>
+#include <linux/iommu-driver.h>
+
+struct parse_info {
+	struct iommu_probe_info *pinf;
+	const struct iommu_ops *ops;
+	u32 *ids;
+};
+
+static bool iort_iommu_driver_enabled(struct iommu_probe_info *pinf, u8 type)
+{
+	switch (type) {
+	case ACPI_IORT_NODE_SMMU_V3:
+		return IS_ENABLED(CONFIG_ARM_SMMU_V3);
+	case ACPI_IORT_NODE_SMMU:
+		return IS_ENABLED(CONFIG_ARM_SMMU);
+	default:
+		dev_warn(pinf->dev,
+			 FW_WARN
+			 "IORT node type %u does not describe an SMMU\n",
+			 type);
+		return false;
+	}
+}
+
+static int parse_single_iommu(struct acpi_iort_node *iort_iommu, u32 streamid,
+			      void *_info)
+{
+	struct parse_info *info = _info;
+	struct iommu_probe_info *pinf = info->pinf;
+	struct fwnode_handle *fwnode;
+	struct iommu_device *iommu;
+
+	fwnode = iort_get_fwnode(iort_iommu);
+	if (!fwnode)
+		return -ENODEV;
+
+	iommu = iommu_device_from_fwnode_pinf(pinf, info->ops, fwnode);
+	if (IS_ERR(iommu)) {
+		if (iommu == ERR_PTR(-EPROBE_DEFER) &&
+		    !iort_iommu_driver_enabled(pinf, iort_iommu->type))
+			return -ENODEV;
+		return PTR_ERR(iommu);
+	}
+	iommu_fw_cache_id(pinf, streamid);
+	return 0;
+}
+
+static int parse_read_ids(struct acpi_iort_node *iommu, u32 streamid,
+			  void *_info)
+{
+	struct parse_info *info = _info;
+
+	*info->ids = streamid;
+	(*info->ids)++;
+	return 0;
+}
+
+static int iort_get_u32_ids(struct iommu_probe_info *pinf, u32 *ids)
+{
+	struct parse_info info = { .pinf = pinf, .ids = ids };
+	struct iort_params params;
+
+	return iort_iommu_for_each_id(pinf->dev, pinf->acpi_map_id, &params,
+				      parse_read_ids, &info);
+}
+
+struct iommu_device *
+__iommu_iort_get_single_iommu(struct iommu_probe_info *pinf,
+			      const struct iommu_ops *ops,
+			      struct iort_params *params)
+{
+	struct parse_info info = { .pinf = pinf, .ops = ops };
+	struct iort_params unused_params;
+	int err;
+
+	if (!pinf->is_dma_configure || !pinf->is_acpi)
+		return ERR_PTR(-ENODEV);
+
+	if (!params)
+		params = &unused_params;
+
+	iommu_fw_clear_cache(pinf);
+	err = iort_iommu_for_each_id(pinf->dev, pinf->acpi_map_id, params,
+				     parse_single_iommu, &info);
+	if (err)
+		return ERR_PTR(err);
+	pinf->get_u32_ids = iort_get_u32_ids;
+	return iommu_fw_finish_get_single(pinf);
+}
+EXPORT_SYMBOL(__iommu_iort_get_single_iommu);
diff --git a/include/linux/acpi_iort.h b/include/linux/acpi_iort.h
index 13f0cefb930693..bacba2a76c3acb 100644
--- a/include/linux/acpi_iort.h
+++ b/include/linux/acpi_iort.h
@@ -40,6 +40,7 @@ typedef int (*iort_for_each_fn)(struct acpi_iort_node *iommu, u32 streamid,
 int iort_iommu_for_each_id(struct device *dev, const u32 *id_in,
 			   struct iort_params *params, iort_for_each_fn fn,
 			   void *info);
+struct fwnode_handle *iort_get_fwnode(struct acpi_iort_node *node);
 
 #ifdef CONFIG_ACPI_IORT
 u32 iort_msi_map_id(struct device *dev, u32 id);
diff --git a/include/linux/iommu-driver.h b/include/linux/iommu-driver.h
index ce0ba1f35bb5dc..c4e133cdef2c78 100644
--- a/include/linux/iommu-driver.h
+++ b/include/linux/iommu-driver.h
@@ -19,6 +19,7 @@
 struct of_phandle_args;
 struct fwnode_handle;
 struct iommu_device;
+struct iort_params;
 struct iommu_ops;
 
 /*
@@ -39,7 +40,9 @@ struct iommu_probe_info {
 	struct list_head *deferred_group_list;
 	struct iommu_device *cached_iommu;
 	struct device_node *of_master_np;
+	struct fwnode_handle *acpi_fwnode;
 	const u32 *of_map_id;
+	const u32 *acpi_map_id;
 	int (*get_u32_ids)(struct iommu_probe_info *pinf, u32 *ids);
 	unsigned int num_ids;
 	u32 cached_ids[8];
@@ -63,6 +66,21 @@ iommu_device_from_fwnode_pinf(struct iommu_probe_info *pinf,
 			      struct fwnode_handle *fwnode);
 struct iommu_device *iommu_fw_finish_get_single(struct iommu_probe_info *pinf);
 
+/**
+ * iommu_fw_acpi_fwnode - Get an ACPI fwnode_handle
+ * @pinf: The iommu_probe_info
+ *
+ * Return the ACPI version of the fwnode describing the iommu data that is
+ * associated with the device being probed.
+ */
+static inline struct fwnode_handle *
+iommu_fw_acpi_fwnode(struct iommu_probe_info *pinf)
+{
+	if (!pinf->is_acpi)
+		return NULL;
+	return pinf->acpi_fwnode;
+}
+
 typedef int (*iommu_of_xlate_fn)(struct iommu_device *iommu,
 				struct of_phandle_args *args, void *priv);
 void iommu_of_allow_bus_probe(struct iommu_probe_info *pinf);
@@ -213,4 +231,27 @@ __iommu_viot_get_single_iommu(struct iommu_probe_info *pinf,
 			      __iommu_of_get_single_iommu(pinf, ops, -1)), \
 		drv_struct, member)
 
+#if IS_ENABLED(CONFIG_ACPI_IORT)
+struct iommu_device *
+__iommu_iort_get_single_iommu(struct iommu_probe_info *pinf,
+			      const struct iommu_ops *ops,
+			      struct iort_params *params);
+#else
+static inline struct iommu_device *
+__iommu_iort_get_single_iommu(struct iommu_probe_info *pinf,
+			      const struct iommu_ops *ops,
+			      struct iort_params *params)
+{
+	return ERR_PTR(-ENODEV);
+}
+#endif
+#define iommu_iort_get_single_iommu(pinf, ops, params, drv_struct, member)    \
+	({                                                                    \
+		memset(params, 0, sizeof(*(params)));                         \
+		container_of_err(__iommu_first(__iommu_iort_get_single_iommu( \
+						       pinf, ops, params),    \
+					       __iommu_of_get_single_iommu(   \
+						       pinf, ops, -1)),       \
+				 drv_struct, member)                          \
+	})
 #endif
-- 
2.42.0


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

* [PATCH 26/30] iommu/arm-smmu-v3: Move to iommu_fw_alloc_per_device_ids()
  2023-11-30  1:10 [PATCH 00/30] Make a new API for drivers to use to get their FW Jason Gunthorpe
                   ` (24 preceding siblings ...)
  2023-11-30  1:10 ` [PATCH 25/30] iommu/iort: Add iommu_iort_get_single_iommu() Jason Gunthorpe
@ 2023-11-30  1:10 ` Jason Gunthorpe
  2023-11-30  1:10 ` [PATCH 27/30] iommu/arm-smmu: Move to iommu_of_xlate() Jason Gunthorpe
                   ` (3 subsequent siblings)
  29 siblings, 0 replies; 34+ messages in thread
From: Jason Gunthorpe @ 2023-11-30  1:10 UTC (permalink / raw)
  To: acpica-devel, Andy Gross, Alim Akhtar, Alyssa Rosenzweig,
	Bjorn Andersson, AngeloGioacchino Del Regno, asahi, Baolin Wang,
	devicetree, Frank Rowand, Hanjun Guo, Gustavo A. R. Silva,
	Heiko Stuebner, iommu, Jean-Philippe Brucker, Jernej Skrabec,
	Jonathan Hunter, Joerg Roedel, Kees Cook, Konrad Dybcio,
	Krzysztof Kozlowski, Len Brown, linux-acpi, linux-arm-kernel,
	linux-arm-msm, linux-hardening, linux-mediatek, linux-rockchip,
	linux-samsung-soc, linux-sunxi, linux-tegra, Lorenzo Pieralisi,
	Marek Szyprowski, Hector Martin, Matthias Brugger, Orson Zhai,
	Rafael J. Wysocki, Rob Clark, Robert Moore, Rob Herring,
	Robin Murphy, Samuel Holland, Sudeep Holla, Sven Peter,
	Thierry Reding, Krishna Reddy, virtualization, Chen-Yu Tsai,
	Will Deacon, Yong Wu, Chunyan Zhang
  Cc: André Draszik, patches

SMMUv3 supports a single iommu instance with multiple ids.

It has a combined ACPI (via the IORT table) and OF probe path, add
iommu_iort_get_single_iommu() to respresent this.

It already has a per-instance structure, extend it with the ids[]
array and use iommu_fwb_alloc_per_device_ids() to populate it.

Convert the rest of the funcs from calling dev_iommu_fwspec_get() to using
the per-device data and remove all use of fwspec.

Directly call iort_iommu_get_resv_regions() and pass in the internal id
array instead of getting it from the fwspec.

Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
---
 drivers/acpi/arm64/iort.c                   |  2 -
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 74 +++++++++------------
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h |  4 ++
 include/linux/iommu-driver.h                |  2 +-
 4 files changed, 35 insertions(+), 47 deletions(-)

diff --git a/drivers/acpi/arm64/iort.c b/drivers/acpi/arm64/iort.c
index 6b2d50cc9ac180..acd2e48590f37a 100644
--- a/drivers/acpi/arm64/iort.c
+++ b/drivers/acpi/arm64/iort.c
@@ -1297,8 +1297,6 @@ static void iort_named_component_init(struct device *dev,
 	props[0] = PROPERTY_ENTRY_U32("pasid-num-bits",
 				      FIELD_GET(ACPI_IORT_NC_PASID_BITS,
 						nc->node_flags));
-	if (nc->node_flags & ACPI_IORT_NC_STALL_SUPPORTED)
-		props[1] = PROPERTY_ENTRY_BOOL("dma-can-stall");
 
 	if (device_create_managed_software_node(dev, props, NULL))
 		dev_warn(dev, "Could not add device properties\n");
diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
index 1855d3892b15f8..1a43c677e2feaf 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -26,9 +26,9 @@
 #include <linux/pci.h>
 #include <linux/pci-ats.h>
 #include <linux/platform_device.h>
+#include <linux/iommu-driver.h>
 
 #include "arm-smmu-v3.h"
-#include "../../dma-iommu.h"
 #include "../../iommu-sva.h"
 
 static bool disable_bypass = true;
@@ -2255,12 +2255,11 @@ static bool arm_smmu_ats_supported(struct arm_smmu_master *master)
 {
 	struct device *dev = master->dev;
 	struct arm_smmu_device *smmu = master->smmu;
-	struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
 
 	if (!(smmu->features & ARM_SMMU_FEAT_ATS))
 		return false;
 
-	if (!(fwspec->flags & IOMMU_FWSPEC_PCI_RC_ATS))
+	if (!master->pci_rc_ats)
 		return false;
 
 	return dev_is_pci(dev) && pci_ats_supported(to_pci_dev(dev));
@@ -2382,14 +2381,10 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
 {
 	int ret = 0;
 	unsigned long flags;
-	struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
 	struct arm_smmu_device *smmu;
 	struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
 	struct arm_smmu_master *master;
 
-	if (!fwspec)
-		return -ENOENT;
-
 	master = dev_iommu_priv_get(dev);
 	smmu = master->smmu;
 
@@ -2529,15 +2524,6 @@ arm_smmu_iova_to_phys(struct iommu_domain *domain, dma_addr_t iova)
 
 static struct platform_driver arm_smmu_driver;
 
-static
-struct arm_smmu_device *arm_smmu_get_by_fwnode(struct fwnode_handle *fwnode)
-{
-	struct device *dev = driver_find_device_by_fwnode(&arm_smmu_driver.driver,
-							  fwnode);
-	put_device(dev);
-	return dev ? dev_get_drvdata(dev) : NULL;
-}
-
 static bool arm_smmu_sid_in_range(struct arm_smmu_device *smmu, u32 sid)
 {
 	unsigned long limit = smmu->strtab_cfg.num_l1_ents;
@@ -2568,17 +2554,16 @@ static int arm_smmu_insert_master(struct arm_smmu_device *smmu,
 	int ret = 0;
 	struct arm_smmu_stream *new_stream, *cur_stream;
 	struct rb_node **new_node, *parent_node = NULL;
-	struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(master->dev);
 
-	master->streams = kcalloc(fwspec->num_ids, sizeof(*master->streams),
+	master->streams = kcalloc(master->num_ids, sizeof(*master->streams),
 				  GFP_KERNEL);
 	if (!master->streams)
 		return -ENOMEM;
-	master->num_streams = fwspec->num_ids;
+	master->num_streams = master->num_ids;
 
 	mutex_lock(&smmu->streams_mutex);
-	for (i = 0; i < fwspec->num_ids; i++) {
-		u32 sid = fwspec->ids[i];
+	for (i = 0; i < master->num_ids; i++) {
+		u32 sid = master->ids[i];
 
 		new_stream = &master->streams[i];
 		new_stream->id = sid;
@@ -2627,13 +2612,12 @@ static void arm_smmu_remove_master(struct arm_smmu_master *master)
 {
 	int i;
 	struct arm_smmu_device *smmu = master->smmu;
-	struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(master->dev);
 
 	if (!smmu || !master->streams)
 		return;
 
 	mutex_lock(&smmu->streams_mutex);
-	for (i = 0; i < fwspec->num_ids; i++)
+	for (i = 0; i < master->num_ids; i++)
 		rb_erase(&master->streams[i].node, &smmu->streams);
 	mutex_unlock(&smmu->streams_mutex);
 
@@ -2642,26 +2626,27 @@ static void arm_smmu_remove_master(struct arm_smmu_master *master)
 
 static struct iommu_ops arm_smmu_ops;
 
-static struct iommu_device *arm_smmu_probe_device(struct device *dev)
+static struct iommu_device *arm_smmu_probe_device(struct iommu_probe_info *pinf)
 {
 	int ret;
+	struct device *dev = pinf->dev;
 	struct arm_smmu_device *smmu;
 	struct arm_smmu_master *master;
-	struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
+	struct iort_params params;
 
-	if (WARN_ON_ONCE(dev_iommu_priv_get(dev)))
-		return ERR_PTR(-EBUSY);
+	smmu = iommu_iort_get_single_iommu(pinf, &arm_smmu_ops, &params,
+					   struct arm_smmu_device, iommu);
+	if (IS_ERR(smmu))
+		return ERR_CAST(smmu);
 
-	smmu = arm_smmu_get_by_fwnode(fwspec->iommu_fwnode);
-	if (!smmu)
-		return ERR_PTR(-ENODEV);
-
-	master = kzalloc(sizeof(*master), GFP_KERNEL);
-	if (!master)
-		return ERR_PTR(-ENOMEM);
+	master = iommu_fw_alloc_per_device_ids(pinf, master);
+	if (IS_ERR(master))
+		return ERR_CAST(master);
 
 	master->dev = dev;
 	master->smmu = smmu;
+	master->pci_rc_ats = params.pci_rc_ats;
+	master->acpi_fwnode = iommu_fw_acpi_fwnode(pinf);
 	INIT_LIST_HEAD(&master->bonds);
 	dev_iommu_priv_set(dev, master);
 
@@ -2670,7 +2655,8 @@ static struct iommu_device *arm_smmu_probe_device(struct device *dev)
 		goto err_free_master;
 
 	device_property_read_u32(dev, "pasid-num-bits", &master->ssid_bits);
-	master->ssid_bits = min(smmu->ssid_bits, master->ssid_bits);
+	master->ssid_bits = min(smmu->ssid_bits,
+				max(params.pasid_num_bits, master->ssid_bits));
 
 	/*
 	 * Note that PASID must be enabled before, and disabled after ATS:
@@ -2687,7 +2673,8 @@ static struct iommu_device *arm_smmu_probe_device(struct device *dev)
 					  CTXDESC_LINEAR_CDMAX);
 
 	if ((smmu->features & ARM_SMMU_FEAT_STALLS &&
-	     device_property_read_bool(dev, "dma-can-stall")) ||
+	     (device_property_read_bool(dev, "dma-can-stall") ||
+	      params.dma_can_stall)) ||
 	    smmu->features & ARM_SMMU_FEAT_STALL_FORCE)
 		master->stall_enabled = true;
 
@@ -2744,14 +2731,10 @@ static int arm_smmu_enable_nesting(struct iommu_domain *domain)
 	return ret;
 }
 
-static int arm_smmu_of_xlate(struct device *dev, struct of_phandle_args *args)
-{
-	return iommu_fwspec_add_ids(dev, args->args, 1);
-}
-
 static void arm_smmu_get_resv_regions(struct device *dev,
 				      struct list_head *head)
 {
+	struct arm_smmu_master *master = dev_iommu_priv_get(dev);
 	struct iommu_resv_region *region;
 	int prot = IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_MMIO;
 
@@ -2762,7 +2745,10 @@ static void arm_smmu_get_resv_regions(struct device *dev,
 
 	list_add_tail(&region->list, head);
 
-	iommu_dma_get_resv_regions(dev, head);
+	if (master->acpi_fwnode)
+		iort_iommu_get_resv_regions(dev, head, master->acpi_fwnode,
+					    master->ids, master->num_ids);
+	of_iommu_get_resv_regions(dev, head);
 }
 
 static int arm_smmu_dev_enable_feature(struct device *dev,
@@ -2851,10 +2837,10 @@ static void arm_smmu_remove_dev_pasid(struct device *dev, ioasid_t pasid)
 static struct iommu_ops arm_smmu_ops = {
 	.capable		= arm_smmu_capable,
 	.domain_alloc		= arm_smmu_domain_alloc,
-	.probe_device		= arm_smmu_probe_device,
+	.probe_device_pinf	= arm_smmu_probe_device,
 	.release_device		= arm_smmu_release_device,
 	.device_group		= arm_smmu_device_group,
-	.of_xlate		= arm_smmu_of_xlate,
+	.of_xlate		= iommu_dummy_of_xlate,
 	.get_resv_regions	= arm_smmu_get_resv_regions,
 	.remove_dev_pasid	= arm_smmu_remove_dev_pasid,
 	.dev_enable_feat	= arm_smmu_dev_enable_feature,
diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
index 961205ba86d25d..ac293265b21a13 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
@@ -692,6 +692,7 @@ struct arm_smmu_stream {
 struct arm_smmu_master {
 	struct arm_smmu_device		*smmu;
 	struct device			*dev;
+	struct fwnode_handle            *acpi_fwnode;
 	struct arm_smmu_domain		*domain;
 	struct list_head		domain_head;
 	struct arm_smmu_stream		*streams;
@@ -702,8 +703,11 @@ struct arm_smmu_master {
 	bool				stall_enabled;
 	bool				sva_enabled;
 	bool				iopf_enabled;
+	bool				pci_rc_ats;
 	struct list_head		bonds;
 	unsigned int			ssid_bits;
+	unsigned int			num_ids;
+	u32				ids[] __counted_by(num_ids);
 };
 
 /* SMMU private data for an IOMMU domain */
diff --git a/include/linux/iommu-driver.h b/include/linux/iommu-driver.h
index c4e133cdef2c78..8f7089d3bb7135 100644
--- a/include/linux/iommu-driver.h
+++ b/include/linux/iommu-driver.h
@@ -252,6 +252,6 @@ __iommu_iort_get_single_iommu(struct iommu_probe_info *pinf,
 						       pinf, ops, params),    \
 					       __iommu_of_get_single_iommu(   \
 						       pinf, ops, -1)),       \
-				 drv_struct, member)                          \
+				 drv_struct, member);                         \
 	})
 #endif
-- 
2.42.0


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

* [PATCH 27/30] iommu/arm-smmu: Move to iommu_of_xlate()
  2023-11-30  1:10 [PATCH 00/30] Make a new API for drivers to use to get their FW Jason Gunthorpe
                   ` (25 preceding siblings ...)
  2023-11-30  1:10 ` [PATCH 26/30] iommu/arm-smmu-v3: Move to iommu_fw_alloc_per_device_ids() Jason Gunthorpe
@ 2023-11-30  1:10 ` Jason Gunthorpe
  2023-11-30  1:10 ` [PATCH 28/30] iommu: Call all drivers if there is no fwspec Jason Gunthorpe
                   ` (2 subsequent siblings)
  29 siblings, 0 replies; 34+ messages in thread
From: Jason Gunthorpe @ 2023-11-30  1:10 UTC (permalink / raw)
  To: acpica-devel, Andy Gross, Alim Akhtar, Alyssa Rosenzweig,
	Bjorn Andersson, AngeloGioacchino Del Regno, asahi, Baolin Wang,
	devicetree, Frank Rowand, Hanjun Guo, Gustavo A. R. Silva,
	Heiko Stuebner, iommu, Jean-Philippe Brucker, Jernej Skrabec,
	Jonathan Hunter, Joerg Roedel, Kees Cook, Konrad Dybcio,
	Krzysztof Kozlowski, Len Brown, linux-acpi, linux-arm-kernel,
	linux-arm-msm, linux-hardening, linux-mediatek, linux-rockchip,
	linux-samsung-soc, linux-sunxi, linux-tegra, Lorenzo Pieralisi,
	Marek Szyprowski, Hector Martin, Matthias Brugger, Orson Zhai,
	Rafael J. Wysocki, Rob Clark, Robert Moore, Rob Herring,
	Robin Murphy, Samuel Holland, Sudeep Holla, Sven Peter,
	Thierry Reding, Krishna Reddy, virtualization, Chen-Yu Tsai,
	Will Deacon, Yong Wu, Chunyan Zhang
  Cc: André Draszik, patches

Compared to every other driver SMMU supports a lot of different formats
for it's firmware description:

 - The legacy OF mmu-masters with a 1 cell id
 - The OF iommus with a 1 cell id
 - The OF iommus with a 2 cell id
 - The OF iommus with a 1 cell id and stream-match-mask
 - ACPI with a 1 cell id

They all get reduced down to a single array of u32 ids in the driver.

Store the id array as a flex array in the arm_smmu_master_cfg, and change
the smendx to an allocated array. This allows using the
iommu_fw_alloc_per_device_ids() path for ACPI.

Have the legacy flow just allocate the cfg of the proper size and copy the
cells into the ids.

The OF and ACPI flows all call iommu_iort_get_single_iommu(). ACPI will
use iommu_fw_alloc_per_device_ids().

The remaining OF flows will make another pass using iommu_of_xlate() to
parse the complex details and format the IDs list.

Remove fwspec from the other places in the driver.

Directly call iort_iommu_get_resv_regions() and pass in the internal id
array instead of getting it from the fwspec.

Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
---
 drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c |   6 +-
 drivers/iommu/arm/arm-smmu/arm-smmu.c      | 205 +++++++++++----------
 drivers/iommu/arm/arm-smmu/arm-smmu.h      |  13 +-
 3 files changed, 118 insertions(+), 106 deletions(-)

diff --git a/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c b/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c
index 549ae4dba3a681..95199de33ca865 100644
--- a/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c
+++ b/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c
@@ -101,15 +101,15 @@ static void qcom_adreno_smmu_resume_translation(const void *cookie, bool termina
 
 static bool qcom_adreno_smmu_is_gpu_device(struct device *dev)
 {
-	struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
+	struct arm_smmu_master_cfg *cfg = dev_iommu_priv_get(dev);
 	int i;
 
 	/*
 	 * The GPU will always use SID 0 so that is a handy way to uniquely
 	 * identify it and configure it for per-instance pagetables
 	 */
-	for (i = 0; i < fwspec->num_ids; i++) {
-		u16 sid = FIELD_GET(ARM_SMMU_SMR_ID, fwspec->ids[i]);
+	for (i = 0; i < cfg->num_ids; i++) {
+		u16 sid = FIELD_GET(ARM_SMMU_SMR_ID, cfg->ids[i]);
 
 		if (sid == QCOM_ADRENO_SMMU_GPU_SID)
 			return true;
diff --git a/drivers/iommu/arm/arm-smmu/arm-smmu.c b/drivers/iommu/arm/arm-smmu/arm-smmu.c
index 02b8dc4f366aa9..f18d40532af433 100644
--- a/drivers/iommu/arm/arm-smmu/arm-smmu.c
+++ b/drivers/iommu/arm/arm-smmu/arm-smmu.c
@@ -34,11 +34,11 @@
 #include <linux/pm_runtime.h>
 #include <linux/ratelimit.h>
 #include <linux/slab.h>
+#include <linux/iommu-driver.h>
 
 #include <linux/fsl/mc.h>
 
 #include "arm-smmu.h"
-#include "../../dma-iommu.h"
 
 /*
  * Apparently, some Qualcomm arm64 platforms which appear to expose their SMMU
@@ -89,6 +89,8 @@ static struct arm_smmu_domain *to_smmu_domain(struct iommu_domain *dom)
 
 static struct platform_driver arm_smmu_driver;
 static struct iommu_ops arm_smmu_ops;
+static int arm_smmu_of_xlate(struct iommu_device *iommu,
+			     struct of_phandle_args *args, void *priv);
 
 #ifdef CONFIG_ARM_SMMU_LEGACY_DT_BINDINGS
 static struct device_node *dev_get_dev_node(struct device *dev)
@@ -126,21 +128,21 @@ static int __find_legacy_master_phandle(struct device *dev, void *data)
 	return err == -ENOENT ? 0 : err;
 }
 
-static int arm_smmu_register_legacy_master(struct device *dev,
-					   struct arm_smmu_device **smmu)
+static struct arm_smmu_master_cfg *
+arm_smmu_register_legacy_master(struct device *dev)
 {
+	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;
 
 	np = dev_get_dev_node(dev);
 	if (!np || !of_property_present(np, "#stream-id-cells")) {
 		of_node_put(np);
-		return -ENODEV;
+		return ERR_PTR(-ENODEV);
 	}
 
 	it.node = np;
@@ -149,9 +151,9 @@ static int arm_smmu_register_legacy_master(struct device *dev,
 	smmu_dev = data;
 	of_node_put(np);
 	if (err == 0)
-		return -ENODEV;
+		return ERR_PTR(-ENODEV);
 	if (err < 0)
-		return err;
+		return ERR_PTR(err);
 
 	if (dev_is_pci(dev)) {
 		/* "mmu-masters" assumes Stream ID == Requester ID */
@@ -161,26 +163,20 @@ static int arm_smmu_register_legacy_master(struct device *dev,
 		it.cur_count = 1;
 	}
 
-	err = iommu_fwspec_init(dev, &smmu_dev->of_node->fwnode,
-				&arm_smmu_ops);
-	if (err)
-		return err;
+	cfg = kzalloc(struct_size(cfg, ids, it.cur_count), GFP_KERNEL);
+	if (!cfg)
+		return ERR_PTR(-ENOMEM);
 
-	sids = kcalloc(it.cur_count, sizeof(*sids), GFP_KERNEL);
-	if (!sids)
-		return -ENOMEM;
-
-	*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;
+	cfg->num_ids = it.cur_count;
+	cfg->smmu = dev_get_drvdata(smmu_dev);
+	of_phandle_iterator_args(&it, cfg->ids, it.cur_count);
+	return 0;
 }
 #else
-static int arm_smmu_register_legacy_master(struct device *dev,
-					   struct arm_smmu_device **smmu)
+static struct arm_smmu_master_cfg *
+arm_smmu_register_legacy_master(struct device *dev)
 {
-	return -ENODEV;
+	return ERR_PTR(-ENODEV);
 }
 #endif /* CONFIG_ARM_SMMU_LEGACY_DT_BINDINGS */
 
@@ -1019,7 +1015,6 @@ static bool arm_smmu_free_sme(struct arm_smmu_device *smmu, int idx)
 
 static int arm_smmu_master_alloc_smes(struct device *dev)
 {
-	struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
 	struct arm_smmu_master_cfg *cfg = dev_iommu_priv_get(dev);
 	struct arm_smmu_device *smmu = cfg->smmu;
 	struct arm_smmu_smr *smrs = smmu->smrs;
@@ -1027,9 +1022,9 @@ 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, fwspec, i, idx) {
-		u16 sid = FIELD_GET(ARM_SMMU_SMR_ID, fwspec->ids[i]);
-		u16 mask = FIELD_GET(ARM_SMMU_SMR_MASK, fwspec->ids[i]);
+	for_each_cfg_sme(cfg, i, idx) {
+		u16 sid = FIELD_GET(ARM_SMMU_SMR_ID, cfg->ids[i]);
+		u16 mask = FIELD_GET(ARM_SMMU_SMR_MASK, cfg->ids[i]);
 
 		if (idx != INVALID_SMENDX) {
 			ret = -EEXIST;
@@ -1051,7 +1046,7 @@ static int arm_smmu_master_alloc_smes(struct device *dev)
 	}
 
 	/* It worked! Now, poke the actual hardware */
-	for_each_cfg_sme(cfg, fwspec, i, idx)
+	for_each_cfg_sme(cfg, i, idx)
 		arm_smmu_write_sme(smmu, idx);
 
 	mutex_unlock(&smmu->stream_map_mutex);
@@ -1066,14 +1061,13 @@ static int arm_smmu_master_alloc_smes(struct device *dev)
 	return ret;
 }
 
-static void arm_smmu_master_free_smes(struct arm_smmu_master_cfg *cfg,
-				      struct iommu_fwspec *fwspec)
+static void arm_smmu_master_free_smes(struct arm_smmu_master_cfg *cfg)
 {
 	struct arm_smmu_device *smmu = cfg->smmu;
 	int i, idx;
 
 	mutex_lock(&smmu->stream_map_mutex);
-	for_each_cfg_sme(cfg, fwspec, i, idx) {
+	for_each_cfg_sme(cfg, i, idx) {
 		if (arm_smmu_free_sme(smmu, idx))
 			arm_smmu_write_sme(smmu, idx);
 		cfg->smendx[i] = INVALID_SMENDX;
@@ -1082,8 +1076,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_master_cfg *cfg)
 {
 	struct arm_smmu_device *smmu = smmu_domain->smmu;
 	struct arm_smmu_s2cr *s2cr = smmu->s2crs;
@@ -1096,7 +1089,7 @@ static int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain,
 	else
 		type = S2CR_TYPE_TRANS;
 
-	for_each_cfg_sme(cfg, fwspec, i, idx) {
+	for_each_cfg_sme(cfg, i, idx) {
 		if (type == s2cr[idx].type && cbndx == s2cr[idx].cbndx)
 			continue;
 
@@ -1111,24 +1104,10 @@ 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)
 {
 	struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
-	struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
-	struct arm_smmu_master_cfg *cfg;
-	struct arm_smmu_device *smmu;
+	struct arm_smmu_master_cfg *cfg = dev_iommu_priv_get(dev);
+	struct arm_smmu_device *smmu = cfg->smmu;
 	int ret;
 
-	/*
-	 * FIXME: The arch/arm DMA API code tries to attach devices to its own
-	 * domains between of_xlate() and probe_device() - we have no way to cope
-	 * with that, so until ARM gets converted to rely on groups and default
-	 * domains, just say no (but more politely than by dereferencing NULL).
-	 * This should be at least a WARN_ON once that's sorted.
-	 */
-	cfg = dev_iommu_priv_get(dev);
-	if (!cfg)
-		return -ENODEV;
-
-	smmu = cfg->smmu;
-
 	ret = arm_smmu_rpm_get(smmu);
 	if (ret < 0)
 		return ret;
@@ -1148,7 +1127,7 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
 	}
 
 	/* Looks ok, so add the device to the domain */
-	ret = arm_smmu_domain_add_master(smmu_domain, cfg, fwspec);
+	ret = arm_smmu_domain_add_master(smmu_domain, cfg);
 
 	/*
 	 * Setup an autosuspend delay to avoid bouncing runpm state.
@@ -1325,59 +1304,85 @@ static bool arm_smmu_capable(struct device *dev, enum iommu_cap cap)
 	}
 }
 
-static
-struct arm_smmu_device *arm_smmu_get_by_fwnode(struct fwnode_handle *fwnode)
+static struct arm_smmu_master_cfg *
+arm_smmu_probe_new_master(struct iommu_probe_info *pinf)
 {
-	struct device *dev = driver_find_device_by_fwnode(&arm_smmu_driver.driver,
-							  fwnode);
-	put_device(dev);
-	return dev ? dev_get_drvdata(dev) : NULL;
-}
-
-static struct iommu_device *arm_smmu_probe_device(struct device *dev)
-{
-	struct arm_smmu_device *smmu = NULL;
 	struct arm_smmu_master_cfg *cfg;
-	struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
-	int i, ret;
+	struct arm_smmu_device *smmu;
 
-	if (using_legacy_binding) {
-		ret = arm_smmu_register_legacy_master(dev, &smmu);
+	smmu = iommu_iort_get_single_iommu(pinf, &arm_smmu_ops, NULL,
+					   struct arm_smmu_device, iommu);
+	if (IS_ERR(smmu))
+		return ERR_CAST(smmu);
+
+	if (!pinf->of_master_np) {
+		/* In ACPI mode the smmu uses the usual u32 format */
+		cfg = iommu_fw_alloc_per_device_ids(pinf, cfg);
+		if (IS_ERR(cfg))
+			return cfg;
+		cfg->acpi_fwnode = iommu_fw_acpi_fwnode(pinf);
+	} else {
+		unsigned int num_ids;
+		int ret;
 
 		/*
-		 * If dev->iommu_fwspec is initally NULL, arm_smmu_register_legacy_master()
-		 * will allocate/initialise a new one. Thus we need to update fwspec for
-		 * later use.
+		 * In OF mode it supports several different formats for the arg,
+		 * pass through arm_smmu_of_xlate to extract it.
 		 */
-		fwspec = dev_iommu_fwspec_get(dev);
-		if (ret)
-			goto out_free;
-	} else {
-		smmu = arm_smmu_get_by_fwnode(fwspec->iommu_fwnode);
+		num_ids = iommu_of_num_ids(pinf);
+		cfg = kzalloc(struct_size(cfg, ids, num_ids), GFP_KERNEL);
+		if (!cfg)
+			return ERR_PTR(-ENOMEM);
+
+		ret = iommu_of_xlate(pinf, &arm_smmu_ops, -1,
+				     &arm_smmu_of_xlate, cfg);
+		if (ret) {
+			kfree(cfg);
+			return ERR_PTR(ret);
+		}
 	}
 
+	cfg->smmu = smmu;
+	return cfg;
+}
+
+static struct iommu_device *arm_smmu_probe_device(struct iommu_probe_info *pinf)
+{
+	struct arm_smmu_master_cfg *cfg;
+	struct device *dev = pinf->dev;
+	struct arm_smmu_device *smmu;
+	int i, ret;
+
+	if (using_legacy_binding)
+		cfg = arm_smmu_register_legacy_master(dev);
+	else
+		cfg = arm_smmu_probe_new_master(pinf);
+	if (IS_ERR(cfg))
+		return ERR_CAST(cfg);
+	smmu = cfg->smmu;
+
 	ret = -EINVAL;
-	for (i = 0; i < fwspec->num_ids; i++) {
-		u16 sid = FIELD_GET(ARM_SMMU_SMR_ID, fwspec->ids[i]);
-		u16 mask = FIELD_GET(ARM_SMMU_SMR_MASK, fwspec->ids[i]);
+	for (i = 0; i < cfg->num_ids; i++) {
+		u16 sid = FIELD_GET(ARM_SMMU_SMR_ID, cfg->ids[i]);
+		u16 mask = FIELD_GET(ARM_SMMU_SMR_MASK, cfg->ids[i]);
 
 		if (sid & ~smmu->streamid_mask) {
 			dev_err(dev, "stream ID 0x%x out of range for SMMU (0x%x)\n",
 				sid, smmu->streamid_mask);
-			goto out_free;
+			goto out_cfg_free;
 		}
 		if (mask & ~smmu->smr_mask_mask) {
 			dev_err(dev, "SMR mask 0x%x out of range for SMMU (0x%x)\n",
 				mask, smmu->smr_mask_mask);
-			goto out_free;
+			goto out_cfg_free;
 		}
 	}
 
 	ret = -ENOMEM;
-	cfg = kzalloc(offsetof(struct arm_smmu_master_cfg, smendx[i]),
-		      GFP_KERNEL);
+	cfg->smendx = kzalloc(array_size(sizeof(*cfg->smendx), cfg->num_ids),
+			      GFP_KERNEL);
 	if (!cfg)
-		goto out_free;
+		goto out_cfg_free;
 
 	cfg->smmu = smmu;
 	dev_iommu_priv_set(dev, cfg);
@@ -1400,15 +1405,13 @@ static struct iommu_device *arm_smmu_probe_device(struct device *dev)
 	return &smmu->iommu;
 
 out_cfg_free:
+	kfree(cfg->smendx);
 	kfree(cfg);
-out_free:
-	iommu_fwspec_free(dev);
 	return ERR_PTR(ret);
 }
 
 static void arm_smmu_release_device(struct device *dev)
 {
-	struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
 	struct arm_smmu_master_cfg *cfg = dev_iommu_priv_get(dev);
 	int ret;
 
@@ -1416,10 +1419,11 @@ static void arm_smmu_release_device(struct device *dev)
 	if (ret < 0)
 		return;
 
-	arm_smmu_master_free_smes(cfg, fwspec);
+	arm_smmu_master_free_smes(cfg);
 
 	arm_smmu_rpm_put(cfg->smmu);
 
+	kfree(cfg->smendx);
 	kfree(cfg);
 }
 
@@ -1438,13 +1442,12 @@ static void arm_smmu_probe_finalize(struct device *dev)
 static struct iommu_group *arm_smmu_device_group(struct device *dev)
 {
 	struct arm_smmu_master_cfg *cfg = dev_iommu_priv_get(dev);
-	struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
 	struct arm_smmu_device *smmu = cfg->smmu;
 	struct iommu_group *group = NULL;
 	int i, idx;
 
 	mutex_lock(&smmu->stream_map_mutex);
-	for_each_cfg_sme(cfg, fwspec, i, idx) {
+	for_each_cfg_sme(cfg, i, idx) {
 		if (group && smmu->s2crs[idx].group &&
 		    group != smmu->s2crs[idx].group) {
 			mutex_unlock(&smmu->stream_map_mutex);
@@ -1468,7 +1471,7 @@ static struct iommu_group *arm_smmu_device_group(struct device *dev)
 
 	/* Remember group for faster lookups */
 	if (!IS_ERR(group))
-		for_each_cfg_sme(cfg, fwspec, i, idx)
+		for_each_cfg_sme(cfg, i, idx)
 			smmu->s2crs[idx].group = group;
 
 	mutex_unlock(&smmu->stream_map_mutex);
@@ -1506,8 +1509,10 @@ static int arm_smmu_set_pgtable_quirks(struct iommu_domain *domain,
 	return ret;
 }
 
-static int arm_smmu_of_xlate(struct device *dev, struct of_phandle_args *args)
+static int arm_smmu_of_xlate(struct iommu_device *iommu,
+			     struct of_phandle_args *args, void *priv)
 {
+	struct arm_smmu_master_cfg *cfg = priv;
 	u32 mask, fwid = 0;
 
 	if (args->args_count > 0)
@@ -1517,13 +1522,14 @@ static int arm_smmu_of_xlate(struct device *dev, struct of_phandle_args *args)
 		fwid |= FIELD_PREP(ARM_SMMU_SMR_MASK, args->args[1]);
 	else if (!of_property_read_u32(args->np, "stream-match-mask", &mask))
 		fwid |= FIELD_PREP(ARM_SMMU_SMR_MASK, mask);
-
-	return iommu_fwspec_add_ids(dev, &fwid, 1);
+	cfg->ids[cfg->num_ids++] = fwid;
+	return 0;
 }
 
 static void arm_smmu_get_resv_regions(struct device *dev,
 				      struct list_head *head)
 {
+	struct arm_smmu_master_cfg *cfg = dev_iommu_priv_get(dev);
 	struct iommu_resv_region *region;
 	int prot = IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_MMIO;
 
@@ -1534,7 +1540,10 @@ static void arm_smmu_get_resv_regions(struct device *dev,
 
 	list_add_tail(&region->list, head);
 
-	iommu_dma_get_resv_regions(dev, head);
+	if (cfg->acpi_fwnode)
+		iort_iommu_get_resv_regions(dev, head, cfg->acpi_fwnode,
+					    cfg->ids, cfg->num_ids);
+	of_iommu_get_resv_regions(dev, head);
 }
 
 static int arm_smmu_def_domain_type(struct device *dev)
@@ -1553,22 +1562,22 @@ static int arm_smmu_def_domain_type(struct device *dev)
 
 static bool arm_smmu_get_stream_id(struct device *dev, u32 *stream_id)
 {
-	struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
+	struct arm_smmu_master_cfg *cfg = dev_iommu_priv_get(dev);
 
-	if (fwspec->num_ids != 1)
+	if (cfg->num_ids != 1)
 		return false;
-	*stream_id = fwspec->ids[0] & 0xffff;
+	*stream_id = cfg->ids[0] & 0xffff;
 	return true;
 }
 
 static struct iommu_ops arm_smmu_ops = {
 	.capable		= arm_smmu_capable,
 	.domain_alloc		= arm_smmu_domain_alloc,
-	.probe_device		= arm_smmu_probe_device,
+	.probe_device_pinf	= arm_smmu_probe_device,
 	.release_device		= arm_smmu_release_device,
 	.probe_finalize		= arm_smmu_probe_finalize,
 	.device_group		= arm_smmu_device_group,
-	.of_xlate		= arm_smmu_of_xlate,
+	.of_xlate		= iommu_dummy_of_xlate,
 	.get_resv_regions	= arm_smmu_get_resv_regions,
 	.def_domain_type	= arm_smmu_def_domain_type,
 	.tegra_dev_iommu_get_stream_id = arm_smmu_get_stream_id,
diff --git a/drivers/iommu/arm/arm-smmu/arm-smmu.h b/drivers/iommu/arm/arm-smmu/arm-smmu.h
index 703fd5817ec11f..ba8224751fdcdc 100644
--- a/drivers/iommu/arm/arm-smmu/arm-smmu.h
+++ b/drivers/iommu/arm/arm-smmu/arm-smmu.h
@@ -378,7 +378,10 @@ struct arm_smmu_domain {
 
 struct arm_smmu_master_cfg {
 	struct arm_smmu_device		*smmu;
-	s16				smendx[];
+	struct fwnode_handle		*acpi_fwnode;
+	s16				*smendx;
+	unsigned int			num_ids;
+	u32				ids[] __counted_by(num_ids);
 };
 
 static inline u32 arm_smmu_lpae_tcr(const struct io_pgtable_cfg *cfg)
@@ -446,10 +449,10 @@ struct arm_smmu_impl {
 };
 
 #define INVALID_SMENDX			-1
-#define cfg_smendx(cfg, fw, i) \
-	(i >= fw->num_ids ? INVALID_SMENDX : cfg->smendx[i])
-#define for_each_cfg_sme(cfg, fw, i, idx) \
-	for (i = 0; idx = cfg_smendx(cfg, fw, i), i < fw->num_ids; ++i)
+#define cfg_smendx(cfg, i) \
+	(i >= cfg->num_ids ? INVALID_SMENDX : cfg->smendx[i])
+#define for_each_cfg_sme(cfg, i, idx) \
+	for (i = 0; idx = cfg_smendx(cfg, i), i < cfg->num_ids; ++i)
 
 static inline int __arm_smmu_alloc_bitmap(unsigned long *map, int start, int end)
 {
-- 
2.42.0


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

* [PATCH 28/30] iommu: Call all drivers if there is no fwspec
  2023-11-30  1:10 [PATCH 00/30] Make a new API for drivers to use to get their FW Jason Gunthorpe
                   ` (26 preceding siblings ...)
  2023-11-30  1:10 ` [PATCH 27/30] iommu/arm-smmu: Move to iommu_of_xlate() Jason Gunthorpe
@ 2023-11-30  1:10 ` Jason Gunthorpe
  2023-11-30  1:10 ` [PATCH 29/30] iommu: Check for EPROBE_DEFER using the new FW parsers Jason Gunthorpe
  2023-11-30  1:10 ` [PATCH 30/30] iommu: Remove fwspec and related Jason Gunthorpe
  29 siblings, 0 replies; 34+ messages in thread
From: Jason Gunthorpe @ 2023-11-30  1:10 UTC (permalink / raw)
  To: acpica-devel, Andy Gross, Alim Akhtar, Alyssa Rosenzweig,
	Bjorn Andersson, AngeloGioacchino Del Regno, asahi, Baolin Wang,
	devicetree, Frank Rowand, Hanjun Guo, Gustavo A. R. Silva,
	Heiko Stuebner, iommu, Jean-Philippe Brucker, Jernej Skrabec,
	Jonathan Hunter, Joerg Roedel, Kees Cook, Konrad Dybcio,
	Krzysztof Kozlowski, Len Brown, linux-acpi, linux-arm-kernel,
	linux-arm-msm, linux-hardening, linux-mediatek, linux-rockchip,
	linux-samsung-soc, linux-sunxi, linux-tegra, Lorenzo Pieralisi,
	Marek Szyprowski, Hector Martin, Matthias Brugger, Orson Zhai,
	Rafael J. Wysocki, Rob Clark, Robert Moore, Rob Herring,
	Robin Murphy, Samuel Holland, Sudeep Holla, Sven Peter,
	Thierry Reding, Krishna Reddy, virtualization, Chen-Yu Tsai,
	Will Deacon, Yong Wu, Chunyan Zhang
  Cc: André Draszik, patches

Now all the iommu drivers can self probe by checking the struct device to
see if it has an appropriate FW attached to it. We don't need the concept
of "global" drivers with a NULL fwspec, just invoke all the ops.

Real systems only have one ops, so this effectively invokes the single op
in the system to probe each device. If there are multiple ops we invoke
each one once, and drivers that don't understand the struct device should
return -ENODEV.

Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
---
 drivers/iommu/iommu.c | 59 +++++++++++++++++++++++++++++++++++--------
 1 file changed, 48 insertions(+), 11 deletions(-)

diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index 7468a64778931b..54e3f14429b3b4 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -241,6 +241,26 @@ static int remove_iommu_group(struct device *dev, void *data)
 	return 0;
 }
 
+static void iommu_device_add(struct iommu_device *iommu)
+{
+	struct iommu_device *cur;
+
+	/*
+	 * Keep the iommu_device_list grouped by ops so that
+	 * iommu_find_init_device() works efficiently.
+	 */
+	mutex_lock(&iommu_probe_device_lock);
+	list_for_each_entry(cur, &iommu_device_list, list) {
+		if (cur->ops == iommu->ops) {
+			list_add(&iommu->list, &cur->list);
+			goto out;
+		}
+	}
+	list_add(&iommu->list, &iommu_device_list);
+out:
+	mutex_unlock(&iommu_probe_device_lock);
+}
+
 /**
  * iommu_device_register() - Register an IOMMU hardware instance
  * @iommu: IOMMU handle for the instance
@@ -262,9 +282,7 @@ int iommu_device_register(struct iommu_device *iommu,
 	if (hwdev)
 		iommu->fwnode = dev_fwnode(hwdev);
 
-	mutex_lock(&iommu_probe_device_lock);
-	list_add_tail(&iommu->list, &iommu_device_list);
-	mutex_unlock(&iommu_probe_device_lock);
+	iommu_device_add(iommu);
 
 	for (int i = 0; i < ARRAY_SIZE(iommu_buses) && !err; i++)
 		err = bus_iommu_probe(iommu_buses[i]);
@@ -502,6 +520,29 @@ static void iommu_deinit_device(struct device *dev)
 
 DEFINE_MUTEX(iommu_probe_device_lock);
 
+static int iommu_find_init_device(struct iommu_probe_info *pinf)
+{
+	const struct iommu_ops *ops = NULL;
+	struct iommu_device *iommu;
+	int ret;
+
+	lockdep_assert_held(&iommu_probe_device_lock);
+
+	/*
+	 * Each unique ops gets a chance to claim the device, -ENODEV means the
+	 * driver does not support the device.
+	 */
+	list_for_each_entry(iommu, &iommu_device_list, list) {
+		if (iommu->ops != ops) {
+			ops = iommu->ops;
+			ret = iommu_init_device(pinf, iommu->ops);
+			if (ret != -ENODEV)
+				return ret;
+		}
+	}
+	return -ENODEV;
+}
+
 static int __iommu_probe_device(struct iommu_probe_info *pinf)
 {
 	struct device *dev = pinf->dev;
@@ -524,13 +565,6 @@ static int __iommu_probe_device(struct iommu_probe_info *pinf)
 		ops = fwspec->ops;
 		if (!ops)
 			return -ENODEV;
-	} else {
-		struct iommu_device *iommu;
-
-		iommu = iommu_device_from_fwnode(NULL);
-		if (!iommu)
-			return -ENODEV;
-		ops = iommu->ops;
 	}
 
 	/*
@@ -546,7 +580,10 @@ static int __iommu_probe_device(struct iommu_probe_info *pinf)
 	if (dev->iommu_group)
 		return 0;
 
-	ret = iommu_init_device(pinf, ops);
+	if (ops)
+		ret = iommu_init_device(pinf, ops);
+	else
+		ret = iommu_find_init_device(pinf);
 	if (ret)
 		return ret;
 
-- 
2.42.0


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

* [PATCH 29/30] iommu: Check for EPROBE_DEFER using the new FW parsers
  2023-11-30  1:10 [PATCH 00/30] Make a new API for drivers to use to get their FW Jason Gunthorpe
                   ` (27 preceding siblings ...)
  2023-11-30  1:10 ` [PATCH 28/30] iommu: Call all drivers if there is no fwspec Jason Gunthorpe
@ 2023-11-30  1:10 ` Jason Gunthorpe
  2023-11-30  1:10 ` [PATCH 30/30] iommu: Remove fwspec and related Jason Gunthorpe
  29 siblings, 0 replies; 34+ messages in thread
From: Jason Gunthorpe @ 2023-11-30  1:10 UTC (permalink / raw)
  To: acpica-devel, Andy Gross, Alim Akhtar, Alyssa Rosenzweig,
	Bjorn Andersson, AngeloGioacchino Del Regno, asahi, Baolin Wang,
	devicetree, Frank Rowand, Hanjun Guo, Gustavo A. R. Silva,
	Heiko Stuebner, iommu, Jean-Philippe Brucker, Jernej Skrabec,
	Jonathan Hunter, Joerg Roedel, Kees Cook, Konrad Dybcio,
	Krzysztof Kozlowski, Len Brown, linux-acpi, linux-arm-kernel,
	linux-arm-msm, linux-hardening, linux-mediatek, linux-rockchip,
	linux-samsung-soc, linux-sunxi, linux-tegra, Lorenzo Pieralisi,
	Marek Szyprowski, Hector Martin, Matthias Brugger, Orson Zhai,
	Rafael J. Wysocki, Rob Clark, Robert Moore, Rob Herring,
	Robin Murphy, Samuel Holland, Sudeep Holla, Sven Peter,
	Thierry Reding, Krishna Reddy, virtualization, Chen-Yu Tsai,
	Will Deacon, Yong Wu, Chunyan Zhang
  Cc: André Draszik, patches

The last thing the iommu_fwspec logic is doing is to generate an
EPROBE_DEFER if we don't have the iommu driver loaded. The OF side does
this by checking for the iommus OF property and the ACPI side does this by
checking both VIOT and IORT tables.

Duplicate this behavior. If probing gets -ENODEV and we are under a
dma_configure then ensure that all the applicable FW parsers are given a
chance to run.

Keep track of any parsers that may have run during the probe ops call and
don't do them again.

Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
---
 drivers/iommu/iommu.c        | 51 +++++++++++++++++++++++++++++++++---
 drivers/iommu/iort_iommu.c   |  1 +
 drivers/iommu/of_iommu.c     |  2 ++
 drivers/iommu/viot_iommu.c   |  1 +
 include/linux/iommu-driver.h |  3 +++
 5 files changed, 55 insertions(+), 3 deletions(-)

diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index 54e3f14429b3b4..c76edc9061f123 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -518,6 +518,48 @@ static void iommu_deinit_device(struct device *dev)
 	dev_iommu_free(dev);
 }
 
+/*
+ * When being called from dma_configure it is necessary to check if an ENODEV
+ * result indicates that we haven't loaded the driver yet. Since we may not have
+ * any drivers registered yet we have no way to predict what kind of FW
+ * description could need to be parsed. Check the widely used ones we have
+ * common parsers for.
+ *
+ * Drivers that use FW descriptions other than these common ones (AMD, Intel,
+ * etc) must probe the iommus during early boot otherwise they will have
+ * problems with probe ordering.
+ *
+ * The *_get_single_iommu() implementations will generate EPROBE_DEFER if they
+ * detect a FW description with no registered iommu driver which will cause
+ * the driver binding from dma_configure to abort and try later.
+ */
+static int iommu_fw_check_deferred(struct iommu_probe_info *pinf)
+{
+	struct iommu_device *iommu;
+
+	if (!pinf->is_dma_configure)
+		return -ENODEV;
+
+	if (!pinf->cached_checked_of && pinf->of_master_np) {
+		iommu = __iommu_of_get_single_iommu(pinf, NULL, -1);
+		if (iommu != ERR_PTR(-ENODEV))
+			return PTR_ERR(iommu);
+	}
+
+	if (!pinf->cached_checked_iort && pinf->is_acpi) {
+		iommu = __iommu_iort_get_single_iommu(pinf, NULL, NULL);
+		if (iommu != ERR_PTR(-ENODEV))
+			return PTR_ERR(iommu);
+	}
+
+	if (!pinf->cached_checked_viot && pinf->is_acpi) {
+		iommu = __iommu_viot_get_single_iommu(pinf, NULL);
+		if (iommu != ERR_PTR(-ENODEV))
+			return PTR_ERR(iommu);
+	}
+	return -ENODEV;
+}
+
 DEFINE_MUTEX(iommu_probe_device_lock);
 
 static int iommu_find_init_device(struct iommu_probe_info *pinf)
@@ -540,7 +582,7 @@ static int iommu_find_init_device(struct iommu_probe_info *pinf)
 				return ret;
 		}
 	}
-	return -ENODEV;
+	return iommu_fw_check_deferred(pinf);
 }
 
 static int __iommu_probe_device(struct iommu_probe_info *pinf)
@@ -580,10 +622,13 @@ static int __iommu_probe_device(struct iommu_probe_info *pinf)
 	if (dev->iommu_group)
 		return 0;
 
-	if (ops)
+	if (ops) {
 		ret = iommu_init_device(pinf, ops);
-	else
+		if (ret == -ENODEV)
+			return iommu_fw_check_deferred(pinf);
+	} else {
 		ret = iommu_find_init_device(pinf);
+	}
 	if (ret)
 		return ret;
 
diff --git a/drivers/iommu/iort_iommu.c b/drivers/iommu/iort_iommu.c
index 9a997b0fd5d5f1..2174ae477486a6 100644
--- a/drivers/iommu/iort_iommu.c
+++ b/drivers/iommu/iort_iommu.c
@@ -88,6 +88,7 @@ __iommu_iort_get_single_iommu(struct iommu_probe_info *pinf,
 		params = &unused_params;
 
 	iommu_fw_clear_cache(pinf);
+	pinf->cached_checked_iort = true;
 	err = iort_iommu_for_each_id(pinf->dev, pinf->acpi_map_id, params,
 				     parse_single_iommu, &info);
 	if (err)
diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c
index 6f6e442f899ded..463d17ab5057d6 100644
--- a/drivers/iommu/of_iommu.c
+++ b/drivers/iommu/of_iommu.c
@@ -367,6 +367,7 @@ struct iommu_device *__iommu_of_get_single_iommu(struct iommu_probe_info *pinf,
 		return ERR_PTR(-ENODEV);
 
 	iommu_fw_clear_cache(pinf);
+	pinf->cached_checked_of = true;
 	err = of_iommu_for_each_id(pinf->dev, pinf->of_master_np,
 				   pinf->of_map_id, parse_single_iommu, &info);
 	if (err)
@@ -412,6 +413,7 @@ int iommu_of_xlate(struct iommu_probe_info *pinf, const struct iommu_ops *ops,
 				   .priv = priv };
 
 	pinf->num_ids = 0;
+	pinf->cached_checked_of = true;
 	return of_iommu_for_each_id(pinf->dev, pinf->of_master_np,
 				    pinf->of_map_id, parse_of_xlate, &info);
 }
diff --git a/drivers/iommu/viot_iommu.c b/drivers/iommu/viot_iommu.c
index e35bd4099e6c6a..32abda73eb3b6c 100644
--- a/drivers/iommu/viot_iommu.c
+++ b/drivers/iommu/viot_iommu.c
@@ -61,6 +61,7 @@ __iommu_viot_get_single_iommu(struct iommu_probe_info *pinf,
 		return ERR_PTR(-ENODEV);
 
 	iommu_fw_clear_cache(pinf);
+	pinf->cached_checked_viot = true;
 	err = viot_iommu_for_each_id(pinf->dev, parse_single_iommu, &info);
 	if (err)
 		return ERR_PTR(err);
diff --git a/include/linux/iommu-driver.h b/include/linux/iommu-driver.h
index 8f7089d3bb7135..aa4cbf0cb91907 100644
--- a/include/linux/iommu-driver.h
+++ b/include/linux/iommu-driver.h
@@ -50,6 +50,9 @@ struct iommu_probe_info {
 	bool is_dma_configure : 1;
 	bool is_acpi : 1;
 	bool cached_single_iommu : 1;
+	bool cached_checked_of : 1;
+	bool cached_checked_iort : 1;
+	bool cached_checked_viot : 1;
 };
 
 static inline void iommu_fw_clear_cache(struct iommu_probe_info *pinf)
-- 
2.42.0


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

* [PATCH 30/30] iommu: Remove fwspec and related
  2023-11-30  1:10 [PATCH 00/30] Make a new API for drivers to use to get their FW Jason Gunthorpe
                   ` (28 preceding siblings ...)
  2023-11-30  1:10 ` [PATCH 29/30] iommu: Check for EPROBE_DEFER using the new FW parsers Jason Gunthorpe
@ 2023-11-30  1:10 ` Jason Gunthorpe
  29 siblings, 0 replies; 34+ messages in thread
From: Jason Gunthorpe @ 2023-11-30  1:10 UTC (permalink / raw)
  To: acpica-devel, Andy Gross, Alim Akhtar, Alyssa Rosenzweig,
	Bjorn Andersson, AngeloGioacchino Del Regno, asahi, Baolin Wang,
	devicetree, Frank Rowand, Hanjun Guo, Gustavo A. R. Silva,
	Heiko Stuebner, iommu, Jean-Philippe Brucker, Jernej Skrabec,
	Jonathan Hunter, Joerg Roedel, Kees Cook, Konrad Dybcio,
	Krzysztof Kozlowski, Len Brown, linux-acpi, linux-arm-kernel,
	linux-arm-msm, linux-hardening, linux-mediatek, linux-rockchip,
	linux-samsung-soc, linux-sunxi, linux-tegra, Lorenzo Pieralisi,
	Marek Szyprowski, Hector Martin, Matthias Brugger, Orson Zhai,
	Rafael J. Wysocki, Rob Clark, Robert Moore, Rob Herring,
	Robin Murphy, Samuel Holland, Sudeep Holla, Sven Peter,
	Thierry Reding, Krishna Reddy, virtualization, Chen-Yu Tsai,
	Will Deacon, Yong Wu, Chunyan Zhang
  Cc: André Draszik, patches

Delete all the now unused code connected to fwspec.

Remove all IOMMU related FW parsing from the *_dma_configure() functions.

Remove no longer needed includes of iommu-driver.h in the ACPI code.

Return the iommu_probe_device_lock back to being a static inside iommu.c

Make __iommu_probe_device() rely on iommu_find_init_device() for
everything.

Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
---
 drivers/acpi/arm64/iort.c                   | 76 ----------------
 drivers/acpi/scan.c                         | 52 +----------
 drivers/acpi/viot.c                         | 32 -------
 drivers/iommu/apple-dart.c                  |  1 -
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c |  1 -
 drivers/iommu/arm/arm-smmu/arm-smmu.c       |  1 -
 drivers/iommu/arm/arm-smmu/qcom_iommu.c     |  1 -
 drivers/iommu/dma-iommu.c                   | 24 -----
 drivers/iommu/dma-iommu.h                   |  6 --
 drivers/iommu/exynos-iommu.c                |  1 -
 drivers/iommu/iommu.c                       | 97 +--------------------
 drivers/iommu/ipmmu-vmsa.c                  |  1 -
 drivers/iommu/msm_iommu.c                   |  1 -
 drivers/iommu/mtk_iommu.c                   |  1 -
 drivers/iommu/of_iommu.c                    | 47 ----------
 drivers/iommu/rockchip-iommu.c              |  1 -
 drivers/iommu/sprd-iommu.c                  |  1 -
 drivers/iommu/sun50i-iommu.c                |  1 -
 drivers/iommu/tegra-smmu.c                  |  1 -
 drivers/iommu/virtio-iommu.c                |  1 -
 include/acpi/acpi_bus.h                     |  3 -
 include/linux/acpi_iort.h                   |  3 -
 include/linux/acpi_viot.h                   |  5 --
 include/linux/iommu-driver.h                | 10 ---
 include/linux/iommu.h                       | 70 ---------------
 25 files changed, 8 insertions(+), 430 deletions(-)

diff --git a/drivers/acpi/arm64/iort.c b/drivers/acpi/arm64/iort.c
index acd2e48590f37a..8457874c789456 100644
--- a/drivers/acpi/arm64/iort.c
+++ b/drivers/acpi/arm64/iort.c
@@ -796,8 +796,6 @@ void acpi_configure_pmsi_domain(struct device *dev)
 }
 
 #ifdef CONFIG_IOMMU_API
-#include <linux/iommu-driver.h>
-
 static void iort_rmr_free(struct device *dev,
 			  struct iommu_resv_region *region)
 {
@@ -1218,19 +1216,6 @@ void iort_put_rmr_sids(struct fwnode_handle *iommu_fwnode,
 }
 EXPORT_SYMBOL_GPL(iort_put_rmr_sids);
 
-static inline bool iort_iommu_driver_enabled(u8 type)
-{
-	switch (type) {
-	case ACPI_IORT_NODE_SMMU_V3:
-		return IS_ENABLED(CONFIG_ARM_SMMU_V3);
-	case ACPI_IORT_NODE_SMMU:
-		return IS_ENABLED(CONFIG_ARM_SMMU);
-	default:
-		pr_warn("IORT node type %u does not describe an SMMU\n", type);
-		return false;
-	}
-}
-
 static bool iort_pci_rc_supports_ats(struct acpi_iort_node *node)
 {
 	struct acpi_iort_root_complex *pci_rc;
@@ -1239,36 +1224,6 @@ static bool iort_pci_rc_supports_ats(struct acpi_iort_node *node)
 	return pci_rc->ats_attribute & ACPI_IORT_ATS_SUPPORTED;
 }
 
-static int iort_iommu_xlate(struct acpi_iort_node *node, u32 streamid,
-			    void *info)
-{
-	struct device *dev = info;
-	struct iommu_device *iommu;
-	struct fwnode_handle *iort_fwnode;
-
-	if (!node)
-		return -ENODEV;
-
-	iort_fwnode = iort_get_fwnode(node);
-	if (!iort_fwnode)
-		return -ENODEV;
-
-	/*
-	 * If the iommu look-up fails, this means that either
-	 * the SMMU drivers have not been probed yet or that
-	 * the SMMU drivers are not built in the kernel;
-	 * Depending on whether the SMMU drivers are built-in
-	 * in the kernel or not, defer the IOMMU configuration
-	 * or just abort it.
-	 */
-	iommu = iommu_device_from_fwnode(iort_fwnode);
-	if (!iommu)
-		return iort_iommu_driver_enabled(node->type) ?
-		       -EPROBE_DEFER : -ENODEV;
-
-	return acpi_iommu_fwspec_init(dev, streamid, iort_fwnode, iommu->ops);
-}
-
 struct iort_pci_alias_info {
 	struct device *dev;
 	struct acpi_iort_node *node;
@@ -1380,40 +1335,9 @@ int iort_iommu_for_each_id(struct device *dev, const u32 *id_in,
 	iort_named_component_init(dev, node);
 	return 0;
 }
-
-/**
- * iort_iommu_configure_id - Set-up IOMMU configuration for a device.
- *
- * @dev: device to configure
- * @id_in: optional input id const value pointer
- *
- * Returns: 0 on success, <0 on failure
- */
-int iort_iommu_configure_id(struct device *dev, const u32 *id_in)
-{
-	struct iort_params params;
-	int err;
-
-	err = iort_iommu_for_each_id(dev, id_in, &params, &iort_iommu_xlate,
-				     dev);
-	if (err)
-		return err;
-
-	if (params.pci_rc_ats) {
-		struct iommu_fwspec *fwspec;
-
-		fwspec = dev_iommu_fwspec_get(dev);
-		if (fwspec)
-			fwspec->flags |= IOMMU_FWSPEC_PCI_RC_ATS;
-	}
-	return 0;
-}
-
 #else
 void iort_iommu_get_resv_regions(struct device *dev, struct list_head *head)
 { }
-int iort_iommu_configure_id(struct device *dev, const u32 *input_id)
-{ return -ENODEV; }
 #endif
 
 static int nc_dma_get_range(struct device *dev, u64 *size)
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index eb7406cdc9a464..c86ac07a7a6420 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -1545,29 +1545,9 @@ int acpi_dma_get_range(struct device *dev, const struct bus_dma_region **map)
 #ifdef CONFIG_IOMMU_API
 #include <linux/iommu-driver.h>
 
-int acpi_iommu_fwspec_init(struct device *dev, u32 id,
-			   struct fwnode_handle *fwnode,
-			   const struct iommu_ops *ops)
-{
-	int ret = iommu_fwspec_init(dev, fwnode, ops);
-
-	if (!ret)
-		ret = iommu_fwspec_add_ids(dev, &id, 1);
-
-	return ret;
-}
-
-static inline const struct iommu_ops *acpi_iommu_fwspec_ops(struct device *dev)
-{
-	struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
-
-	return fwspec ? fwspec->ops : NULL;
-}
-
 static int acpi_iommu_configure_id(struct device *dev, const u32 *id_in)
 {
 	int err;
-	const struct iommu_ops *ops;
 	struct iommu_probe_info pinf = {
 		.dev = dev,
 		.is_dma_configure = true,
@@ -1575,29 +1555,14 @@ static int acpi_iommu_configure_id(struct device *dev, const u32 *id_in)
 		.is_acpi = true,
 	};
 
-	/* Serialise to make dev->iommu stable under our potential fwspec */
-	mutex_lock(&iommu_probe_device_lock);
-	/*
-	 * If we already translated the fwspec there is nothing left to do,
-	 * return the iommu_ops.
-	 */
-	ops = acpi_iommu_fwspec_ops(dev);
-	if (ops) {
-		mutex_unlock(&iommu_probe_device_lock);
-		return 0;
-	}
-
-	err = iort_iommu_configure_id(dev, id_in);
-	if (err && err != -EPROBE_DEFER)
-		err = viot_iommu_configure(dev);
-	mutex_unlock(&iommu_probe_device_lock);
-
 	/*
 	 * If we have reason to believe the IOMMU driver missed the initial
 	 * iommu_probe_device() call for dev, replay it to get things in order.
 	 */
-	if (!err && dev->bus)
-		err = iommu_probe_device_pinf(&pinf);
+	if (!dev->bus)
+		return 0;
+
+	err = iommu_probe_device_pinf(&pinf);
 
 	/* Ignore all other errors apart from EPROBE_DEFER */
 	if (err == -EPROBE_DEFER) {
@@ -1606,20 +1571,11 @@ static int acpi_iommu_configure_id(struct device *dev, const u32 *id_in)
 		dev_dbg(dev, "Adding to IOMMU failed: %d\n", err);
 		return -ENODEV;
 	}
-	if (!acpi_iommu_fwspec_ops(dev))
-		return -ENODEV;
 	return 0;
 }
 
 #else /* !CONFIG_IOMMU_API */
 
-int acpi_iommu_fwspec_init(struct device *dev, u32 id,
-			   struct fwnode_handle *fwnode,
-			   const struct iommu_ops *ops)
-{
-	return -ENODEV;
-}
-
 static int acpi_iommu_configure_id(struct device *dev, const u32 *id_in)
 {
 	return -ENODEV;
diff --git a/drivers/acpi/viot.c b/drivers/acpi/viot.c
index 9780b1d477503e..67b7c4e21eeeb3 100644
--- a/drivers/acpi/viot.c
+++ b/drivers/acpi/viot.c
@@ -21,7 +21,6 @@
 #include <linux/acpi_viot.h>
 #include <linux/fwnode.h>
 #include <linux/iommu.h>
-#include <linux/iommu-driver.h>
 #include <linux/list.h>
 #include <linux/pci.h>
 #include <linux/platform_device.h>
@@ -298,26 +297,6 @@ void __init acpi_viot_init(void)
 	acpi_put_table(hdr);
 }
 
-static int viot_dev_iommu_init(struct viot_iommu *viommu, u32 epid, void *info)
-{
-	struct iommu_device *iommu;
-	struct device *dev = info;
-
-	if (!viommu)
-		return -ENODEV;
-
-	/* We're not translating ourself */
-	if (device_match_fwnode(dev, viommu->fwnode))
-		return -EINVAL;
-
-	iommu = iommu_device_from_fwnode(viommu->fwnode);
-	if (!iommu)
-		return IS_ENABLED(CONFIG_VIRTIO_IOMMU) ?
-			-EPROBE_DEFER : -ENODEV;
-
-	return acpi_iommu_fwspec_init(dev, epid, viommu->fwnode, iommu->ops);
-}
-
 struct viot_pci_iommu_alias_info {
 	struct device *dev;
 	viot_for_each_fn fn;
@@ -378,14 +357,3 @@ int viot_iommu_for_each_id(struct device *dev, viot_for_each_fn fn, void *info)
 		return __for_each_platform(to_platform_device(dev), fn, info);
 	return -ENODEV;
 }
-
-/**
- * viot_iommu_configure - Setup IOMMU ops for an endpoint described by VIOT
- * @dev: the endpoint
- *
- * Return: 0 on success, <0 on failure
- */
-int viot_iommu_configure(struct device *dev)
-{
-	return viot_iommu_for_each_id(dev, viot_dev_iommu_init, dev);
-}
diff --git a/drivers/iommu/apple-dart.c b/drivers/iommu/apple-dart.c
index b796c68ae45ad8..81b129ed81cc03 100644
--- a/drivers/iommu/apple-dart.c
+++ b/drivers/iommu/apple-dart.c
@@ -987,7 +987,6 @@ static const struct iommu_ops apple_dart_iommu_ops = {
 	.probe_device_pinf = apple_dart_probe_device,
 	.release_device = apple_dart_release_device,
 	.device_group = apple_dart_device_group,
-	.of_xlate = iommu_dummy_of_xlate,
 	.def_domain_type = apple_dart_def_domain_type,
 	.get_resv_regions = apple_dart_get_resv_regions,
 	.pgsize_bitmap = -1UL, /* Restricted during dart probe */
diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
index 1a43c677e2feaf..71c47d3a5cdde9 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -2840,7 +2840,6 @@ static struct iommu_ops arm_smmu_ops = {
 	.probe_device_pinf	= arm_smmu_probe_device,
 	.release_device		= arm_smmu_release_device,
 	.device_group		= arm_smmu_device_group,
-	.of_xlate		= iommu_dummy_of_xlate,
 	.get_resv_regions	= arm_smmu_get_resv_regions,
 	.remove_dev_pasid	= arm_smmu_remove_dev_pasid,
 	.dev_enable_feat	= arm_smmu_dev_enable_feature,
diff --git a/drivers/iommu/arm/arm-smmu/arm-smmu.c b/drivers/iommu/arm/arm-smmu/arm-smmu.c
index f18d40532af433..537b47cb0da2b6 100644
--- a/drivers/iommu/arm/arm-smmu/arm-smmu.c
+++ b/drivers/iommu/arm/arm-smmu/arm-smmu.c
@@ -1577,7 +1577,6 @@ static struct iommu_ops arm_smmu_ops = {
 	.release_device		= arm_smmu_release_device,
 	.probe_finalize		= arm_smmu_probe_finalize,
 	.device_group		= arm_smmu_device_group,
-	.of_xlate		= iommu_dummy_of_xlate,
 	.get_resv_regions	= arm_smmu_get_resv_regions,
 	.def_domain_type	= arm_smmu_def_domain_type,
 	.tegra_dev_iommu_get_stream_id = arm_smmu_get_stream_id,
diff --git a/drivers/iommu/arm/arm-smmu/qcom_iommu.c b/drivers/iommu/arm/arm-smmu/qcom_iommu.c
index 4baca45df99971..308b439b955be6 100644
--- a/drivers/iommu/arm/arm-smmu/qcom_iommu.c
+++ b/drivers/iommu/arm/arm-smmu/qcom_iommu.c
@@ -597,7 +597,6 @@ static const struct iommu_ops qcom_iommu_ops = {
 	.probe_device_pinf = qcom_iommu_probe_device,
 	.release_device = qcom_iommu_release_device,
 	.device_group	= generic_device_group,
-	.of_xlate	= iommu_dummy_of_xlate,
 	.pgsize_bitmap	= SZ_4K | SZ_64K | SZ_1M | SZ_16M,
 	.default_domain_ops = &(const struct iommu_domain_ops) {
 		.attach_dev	= qcom_iommu_attach_dev,
diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index 5a828c92cd38b2..e66aacc12b5ee1 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -23,7 +23,6 @@
 #include <linux/memremap.h>
 #include <linux/mm.h>
 #include <linux/mutex.h>
-#include <linux/iommu-driver.h>
 #include <linux/pci.h>
 #include <linux/scatterlist.h>
 #include <linux/spinlock.h>
@@ -456,29 +455,6 @@ void iommu_put_dma_cookie(struct iommu_domain *domain)
 	domain->iova_cookie = NULL;
 }
 
-/**
- * iommu_dma_get_resv_regions - Reserved region driver helper
- * @dev: Device from iommu_get_resv_regions()
- * @list: Reserved region list from iommu_get_resv_regions()
- *
- * IOMMU drivers can use this to implement their .get_resv_regions callback
- * for general non-IOMMU-specific reservations. Currently, this covers GICv3
- * ITS region reservation on ACPI based ARM platforms that may require HW MSI
- * reservation.
- */
-void iommu_dma_get_resv_regions(struct device *dev, struct list_head *list)
-{
-	struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
-
-	if (!is_of_node(fwspec->iommu_fwnode)) {
-		iort_iommu_get_resv_regions(dev, list, fwspec->iommu_fwnode,
-					    fwspec->ids, fwspec->num_ids);
-	}
-
-	of_iommu_get_resv_regions(dev, list);
-}
-EXPORT_SYMBOL(iommu_dma_get_resv_regions);
-
 static int cookie_init_hw_msi_region(struct iommu_dma_cookie *cookie,
 		phys_addr_t start, phys_addr_t end)
 {
diff --git a/drivers/iommu/dma-iommu.h b/drivers/iommu/dma-iommu.h
index c829f1f82a991c..69196f421b9b10 100644
--- a/drivers/iommu/dma-iommu.h
+++ b/drivers/iommu/dma-iommu.h
@@ -14,8 +14,6 @@ void iommu_put_dma_cookie(struct iommu_domain *domain);
 
 int iommu_dma_init_fq(struct iommu_domain *domain);
 
-void iommu_dma_get_resv_regions(struct device *dev, struct list_head *list);
-
 extern bool iommu_dma_forcedac;
 static inline void iommu_dma_set_pci_32bit_workaround(struct device *dev)
 {
@@ -38,10 +36,6 @@ static inline void iommu_put_dma_cookie(struct iommu_domain *domain)
 {
 }
 
-static inline void iommu_dma_get_resv_regions(struct device *dev, struct list_head *list)
-{
-}
-
 static inline void iommu_dma_set_pci_32bit_workaround(struct device *dev)
 {
 }
diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c
index c301aa87fe0ff0..80cca10e8dfdcb 100644
--- a/drivers/iommu/exynos-iommu.c
+++ b/drivers/iommu/exynos-iommu.c
@@ -1473,7 +1473,6 @@ static const struct iommu_ops exynos_iommu_ops = {
 	.probe_device_pinf = exynos_iommu_probe_device,
 	.release_device = exynos_iommu_release_device,
 	.pgsize_bitmap = SECT_SIZE | LPAGE_SIZE | SPAGE_SIZE,
-	.of_xlate = iommu_dummy_of_xlate,
 	.default_domain_ops = &(const struct iommu_domain_ops) {
 		.attach_dev	= exynos_iommu_attach_device,
 		.map_pages	= exynos_iommu_map,
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index c76edc9061f123..ba0c0c7f251ace 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -42,6 +42,7 @@
 static struct kset *iommu_group_kset;
 static DEFINE_IDA(iommu_group_ida);
 static DEFINE_IDA(iommu_global_pasid_ida);
+static DEFINE_MUTEX(iommu_probe_device_lock);
 
 static unsigned int iommu_def_domain_type __read_mostly;
 static bool iommu_dma_strict __read_mostly = IS_ENABLED(CONFIG_IOMMU_DEFAULT_DMA_STRICT);
@@ -371,10 +372,6 @@ static void dev_iommu_free(struct device *dev)
 	struct dev_iommu *param = dev->iommu;
 
 	dev->iommu = NULL;
-	if (param->fwspec) {
-		fwnode_handle_put(param->fwspec->iommu_fwnode);
-		kfree(param->fwspec);
-	}
 	kfree(param);
 }
 
@@ -560,8 +557,6 @@ static int iommu_fw_check_deferred(struct iommu_probe_info *pinf)
 	return -ENODEV;
 }
 
-DEFINE_MUTEX(iommu_probe_device_lock);
-
 static int iommu_find_init_device(struct iommu_probe_info *pinf)
 {
 	const struct iommu_ops *ops = NULL;
@@ -588,27 +583,10 @@ static int iommu_find_init_device(struct iommu_probe_info *pinf)
 static int __iommu_probe_device(struct iommu_probe_info *pinf)
 {
 	struct device *dev = pinf->dev;
-	const struct iommu_ops *ops;
-	struct iommu_fwspec *fwspec;
 	struct iommu_group *group;
 	struct group_device *gdev;
 	int ret;
 
-	/*
-	 * For FDT-based systems and ACPI IORT/VIOT, drivers register IOMMU
-	 * instances with non-NULL fwnodes, and client devices should have been
-	 * identified with a fwspec by this point. Otherwise, we can currently
-	 * assume that only one of Intel, AMD, s390, PAMU or legacy SMMUv2 can
-	 * be present, and that any of their registered instances has suitable
-	 * ops for probing, and thus cheekily co-opt the same mechanism.
-	 */
-	fwspec = dev_iommu_fwspec_get(dev);
-	if (fwspec && fwspec->ops) {
-		ops = fwspec->ops;
-		if (!ops)
-			return -ENODEV;
-	}
-
 	/*
 	 * Serialise to avoid races between IOMMU drivers registering in
 	 * parallel and/or the "replay" calls from ACPI/OF code via client
@@ -622,13 +600,7 @@ static int __iommu_probe_device(struct iommu_probe_info *pinf)
 	if (dev->iommu_group)
 		return 0;
 
-	if (ops) {
-		ret = iommu_init_device(pinf, ops);
-		if (ret == -ENODEV)
-			return iommu_fw_check_deferred(pinf);
-	} else {
-		ret = iommu_find_init_device(pinf);
-	}
+	ret = iommu_find_init_device(pinf);
 	if (ret)
 		return ret;
 
@@ -3085,7 +3057,8 @@ bool iommu_default_passthrough(void)
 }
 EXPORT_SYMBOL_GPL(iommu_default_passthrough);
 
-struct iommu_device *iommu_device_from_fwnode(struct fwnode_handle *fwnode)
+static struct iommu_device *
+iommu_device_from_fwnode(struct fwnode_handle *fwnode)
 {
 	struct iommu_device *iommu;
 
@@ -3173,68 +3146,6 @@ int iommu_fw_get_u32_ids(struct iommu_probe_info *pinf, u32 *ids)
 }
 EXPORT_SYMBOL_GPL(iommu_fw_get_u32_ids);
 
-int iommu_fwspec_init(struct device *dev, struct fwnode_handle *iommu_fwnode,
-		      const struct iommu_ops *ops)
-{
-	struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
-
-	if (fwspec)
-		return ops == fwspec->ops ? 0 : -EINVAL;
-
-	if (!dev_iommu_get(dev))
-		return -ENOMEM;
-
-	/* Preallocate for the overwhelmingly common case of 1 ID */
-	fwspec = kzalloc(struct_size(fwspec, ids, 1), 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_set(dev, fwspec);
-	return 0;
-}
-EXPORT_SYMBOL_GPL(iommu_fwspec_init);
-
-void iommu_fwspec_free(struct device *dev)
-{
-	struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
-
-	if (fwspec) {
-		fwnode_handle_put(fwspec->iommu_fwnode);
-		kfree(fwspec);
-		dev_iommu_fwspec_set(dev, 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_get(dev);
-	int i, new_num;
-
-	if (!fwspec)
-		return -EINVAL;
-
-	new_num = fwspec->num_ids + num_ids;
-	if (new_num > 1) {
-		fwspec = krealloc(fwspec, struct_size(fwspec, ids, new_num),
-				  GFP_KERNEL);
-		if (!fwspec)
-			return -ENOMEM;
-
-		dev_iommu_fwspec_set(dev, fwspec);
-	}
-
-	for (i = 0; i < num_ids; i++)
-		fwspec->ids[fwspec->num_ids + i] = ids[i];
-
-	fwspec->num_ids = new_num;
-	return 0;
-}
-EXPORT_SYMBOL_GPL(iommu_fwspec_add_ids);
-
 /*
  * Per device IOMMU features.
  */
diff --git a/drivers/iommu/ipmmu-vmsa.c b/drivers/iommu/ipmmu-vmsa.c
index ba984017065f98..96a6a13538ed77 100644
--- a/drivers/iommu/ipmmu-vmsa.c
+++ b/drivers/iommu/ipmmu-vmsa.c
@@ -872,7 +872,6 @@ static const struct iommu_ops ipmmu_ops = {
 	.device_group = IS_ENABLED(CONFIG_ARM) && !IS_ENABLED(CONFIG_IOMMU_DMA)
 			? generic_device_group : generic_single_device_group,
 	.pgsize_bitmap = SZ_1G | SZ_2M | SZ_4K,
-	.of_xlate = iommu_dummy_of_xlate,
 	.default_domain_ops = &(const struct iommu_domain_ops) {
 		.attach_dev	= ipmmu_attach_device,
 		.map_pages	= ipmmu_map,
diff --git a/drivers/iommu/msm_iommu.c b/drivers/iommu/msm_iommu.c
index 6f21eec857c7d7..acb4858032f22e 100644
--- a/drivers/iommu/msm_iommu.c
+++ b/drivers/iommu/msm_iommu.c
@@ -674,7 +674,6 @@ static struct iommu_ops msm_iommu_ops = {
 	.probe_device_pinf = msm_iommu_probe_device,
 	.device_group = generic_device_group,
 	.pgsize_bitmap = MSM_IOMMU_PGSIZES,
-	.of_xlate = iommu_dummy_of_xlate,
 	.default_domain_ops = &(const struct iommu_domain_ops) {
 		.attach_dev	= msm_iommu_attach_dev,
 		.map_pages	= msm_iommu_map,
diff --git a/drivers/iommu/mtk_iommu.c b/drivers/iommu/mtk_iommu.c
index 477171e83eaa6e..53099af3908ea3 100644
--- a/drivers/iommu/mtk_iommu.c
+++ b/drivers/iommu/mtk_iommu.c
@@ -1023,7 +1023,6 @@ static const struct iommu_ops mtk_iommu_ops = {
 	.probe_device_pinf = mtk_iommu_probe_device,
 	.release_device	= mtk_iommu_release_device,
 	.device_group	= mtk_iommu_device_group,
-	.of_xlate = iommu_dummy_of_xlate,
 	.get_resv_regions = mtk_iommu_get_resv_regions,
 	.pgsize_bitmap	= SZ_4K | SZ_64K | SZ_1M | SZ_16M,
 	.owner		= THIS_MODULE,
diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c
index 463d17ab5057d6..1daf16323fdbc3 100644
--- a/drivers/iommu/of_iommu.c
+++ b/drivers/iommu/of_iommu.c
@@ -19,33 +19,6 @@
 #include <linux/slab.h>
 #include <linux/fsl/mc.h>
 
-static int of_iommu_xlate(struct of_phandle_args *iommu_spec, void *info)
-{
-	struct device *dev = info;
-	struct iommu_device *iommu;
-	struct fwnode_handle *fwnode = &iommu_spec->np->fwnode;
-	int ret;
-
-	iommu = iommu_device_from_fwnode(fwnode);
-	if ((iommu && !iommu->ops->of_xlate) ||
-	    !of_device_is_available(iommu_spec->np))
-		return -ENODEV;
-
-	ret = iommu_fwspec_init(dev, &iommu_spec->np->fwnode, iommu->ops);
-	if (ret)
-		return ret;
-	/*
-	 * The otherwise-empty fwspec handily serves to indicate the specific
-	 * IOMMU device we're waiting for, which will be useful if we ever get
-	 * a proper probe-ordering dependency mechanism in future.
-	 */
-	if (!iommu)
-		return driver_deferred_probe_check_state(dev);
-
-	ret = iommu->ops->of_xlate(dev, iommu_spec);
-	return ret;
-}
-
 typedef int (*of_for_each_fn)(struct of_phandle_args *args, void *info);
 
 static int __for_each_map_id(struct device_node *master_np, u32 id,
@@ -143,34 +116,14 @@ int of_iommu_configure(struct device *dev, struct device_node *master_np,
 		.of_map_id = id,
 		.is_dma_configure = true,
 	};
-	struct iommu_fwspec *fwspec;
 	int err;
 
 	if (!master_np)
 		return -ENODEV;
 
-	/* Serialise to make dev->iommu stable under our potential fwspec */
-	mutex_lock(&iommu_probe_device_lock);
-	fwspec = dev_iommu_fwspec_get(dev);
-	if (fwspec) {
-		if (fwspec->ops) {
-			mutex_unlock(&iommu_probe_device_lock);
-			return 0;
-		}
-		/* In the deferred case, start again from scratch */
-		iommu_fwspec_free(dev);
-	}
-
 	if (dev_is_pci(dev))
 		pci_request_acs();
 
-	err = of_iommu_for_each_id(dev, master_np, id, of_iommu_xlate, dev);
-	mutex_unlock(&iommu_probe_device_lock);
-	if (err == -ENODEV || err == -EPROBE_DEFER)
-		return err;
-	if (err)
-		goto err_log;
-
 	err = iommu_probe_device_pinf(&pinf);
 	if (err)
 		goto err_log;
diff --git a/drivers/iommu/rockchip-iommu.c b/drivers/iommu/rockchip-iommu.c
index 4cff06a2a24f74..72ee43d3230f5c 100644
--- a/drivers/iommu/rockchip-iommu.c
+++ b/drivers/iommu/rockchip-iommu.c
@@ -1141,7 +1141,6 @@ static const struct iommu_ops rk_iommu_ops = {
 	.release_device = rk_iommu_release_device,
 	.device_group = generic_single_device_group,
 	.pgsize_bitmap = RK_IOMMU_PGSIZE_BITMAP,
-	.of_xlate = iommu_dummy_of_xlate,
 	.default_domain_ops = &(const struct iommu_domain_ops) {
 		.attach_dev	= rk_iommu_attach_device,
 		.map_pages	= rk_iommu_map,
diff --git a/drivers/iommu/sprd-iommu.c b/drivers/iommu/sprd-iommu.c
index f1b87f8661e199..6e7634872bfcb7 100644
--- a/drivers/iommu/sprd-iommu.c
+++ b/drivers/iommu/sprd-iommu.c
@@ -404,7 +404,6 @@ static const struct iommu_ops sprd_iommu_ops = {
 	.domain_alloc_paging = sprd_iommu_domain_alloc_paging,
 	.probe_device_pinf = sprd_iommu_probe_device,
 	.device_group	= generic_single_device_group,
-	.of_xlate = iommu_dummy_of_xlate,
 	.pgsize_bitmap	= SPRD_IOMMU_PAGE_SIZE,
 	.owner		= THIS_MODULE,
 	.default_domain_ops = &(const struct iommu_domain_ops) {
diff --git a/drivers/iommu/sun50i-iommu.c b/drivers/iommu/sun50i-iommu.c
index 84038705cf657d..e91aacdb7104b6 100644
--- a/drivers/iommu/sun50i-iommu.c
+++ b/drivers/iommu/sun50i-iommu.c
@@ -845,7 +845,6 @@ static const struct iommu_ops sun50i_iommu_ops = {
 	.pgsize_bitmap	= SZ_4K,
 	.device_group	= generic_single_device_group,
 	.domain_alloc_paging = sun50i_iommu_domain_alloc_paging,
-	.of_xlate = iommu_dummy_of_xlate,
 	.probe_device_pinf	= sun50i_iommu_probe_device,
 	.release_device = sun50i_iommu_release_device,
 	.default_domain_ops = &(const struct iommu_domain_ops) {
diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c
index 1daa92f524452b..1c14f8b5ed847d 100644
--- a/drivers/iommu/tegra-smmu.c
+++ b/drivers/iommu/tegra-smmu.c
@@ -945,7 +945,6 @@ static const struct iommu_ops tegra_smmu_ops = {
 	.probe_device_pinf = tegra_smmu_probe_device,
 	.release_device = tegra_smmu_release_device,
 	.device_group = tegra_smmu_device_group,
-	.of_xlate = iommu_dummy_of_xlate,
 	.tegra_dev_iommu_get_stream_id = tegra_smmu_get_stream_id,
 	.pgsize_bitmap = SZ_4K,
 	.default_domain_ops = &(const struct iommu_domain_ops) {
diff --git a/drivers/iommu/virtio-iommu.c b/drivers/iommu/virtio-iommu.c
index 767919bf848999..f6b8e796a792e7 100644
--- a/drivers/iommu/virtio-iommu.c
+++ b/drivers/iommu/virtio-iommu.c
@@ -1054,7 +1054,6 @@ static struct iommu_ops viommu_ops = {
 	.release_device		= viommu_release_device,
 	.device_group		= viommu_device_group,
 	.get_resv_regions	= viommu_get_resv_regions,
-	.of_xlate		= iommu_dummy_of_xlate,
 	.owner			= THIS_MODULE,
 	.default_domain_ops = &(const struct iommu_domain_ops) {
 		.attach_dev		= viommu_attach_dev,
diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h
index 89079787905d40..79dea7bea7a6f0 100644
--- a/include/acpi/acpi_bus.h
+++ b/include/acpi/acpi_bus.h
@@ -631,9 +631,6 @@ struct iommu_ops;
 
 bool acpi_dma_supported(const struct acpi_device *adev);
 enum dev_dma_attr acpi_get_dma_attr(struct acpi_device *adev);
-int acpi_iommu_fwspec_init(struct device *dev, u32 id,
-			   struct fwnode_handle *fwnode,
-			   const struct iommu_ops *ops);
 int acpi_dma_get_range(struct device *dev, const struct bus_dma_region **map);
 int acpi_dma_configure_id(struct device *dev, enum dev_dma_attr attr,
 			   const u32 *input_id);
diff --git a/include/linux/acpi_iort.h b/include/linux/acpi_iort.h
index bacba2a76c3acb..9e64a18676a4c7 100644
--- a/include/linux/acpi_iort.h
+++ b/include/linux/acpi_iort.h
@@ -53,7 +53,6 @@ void iort_put_rmr_sids(struct fwnode_handle *iommu_fwnode,
 		       struct list_head *head);
 /* IOMMU interface */
 int iort_dma_get_ranges(struct device *dev, u64 *size);
-int iort_iommu_configure_id(struct device *dev, const u32 *id_in);
 void iort_iommu_get_resv_regions(struct device *dev, struct list_head *head,
 				 struct fwnode_handle *iommu_fwnode,
 				 const u32 *fw_ids, unsigned int fw_num_ids);
@@ -72,8 +71,6 @@ void iort_put_rmr_sids(struct fwnode_handle *iommu_fwnode, struct list_head *hea
 /* IOMMU interface */
 static inline int iort_dma_get_ranges(struct device *dev, u64 *size)
 { return -ENODEV; }
-static inline int iort_iommu_configure_id(struct device *dev, const u32 *id_in)
-{ return -ENODEV; }
 static inline
 void iort_iommu_get_resv_regions(struct device *dev, struct list_head *head,
 				 struct fwnode_handle *iommu_fwnode,
diff --git a/include/linux/acpi_viot.h b/include/linux/acpi_viot.h
index fce4eefcae4aad..0bc01d456bcb6b 100644
--- a/include/linux/acpi_viot.h
+++ b/include/linux/acpi_viot.h
@@ -19,14 +19,9 @@ int viot_iommu_for_each_id(struct device *dev, viot_for_each_fn fn, void *info);
 #ifdef CONFIG_ACPI_VIOT
 void __init acpi_viot_early_init(void);
 void __init acpi_viot_init(void);
-int viot_iommu_configure(struct device *dev);
 #else
 static inline void acpi_viot_early_init(void) {}
 static inline void acpi_viot_init(void) {}
-static inline int viot_iommu_configure(struct device *dev)
-{
-	return -ENODEV;
-}
 #endif
 
 #endif /* __ACPI_VIOT_H__ */
diff --git a/include/linux/iommu-driver.h b/include/linux/iommu-driver.h
index aa4cbf0cb91907..7ddd0b94e13c0d 100644
--- a/include/linux/iommu-driver.h
+++ b/include/linux/iommu-driver.h
@@ -62,7 +62,6 @@ static inline void iommu_fw_clear_cache(struct iommu_probe_info *pinf)
 }
 
 int iommu_probe_device_pinf(struct iommu_probe_info *pinf);
-struct iommu_device *iommu_device_from_fwnode(struct fwnode_handle *fwnode);
 struct iommu_device *
 iommu_device_from_fwnode_pinf(struct iommu_probe_info *pinf,
 			      const struct iommu_ops *ops,
@@ -201,15 +200,6 @@ __iommu_fw_alloc_per_device_ids(struct iommu_probe_info *pinf, void *mem,
 			pinf, drv, num_ids, &drv->num_ids, drv->ids);      \
 	})
 
-/*
- * Used temporarily to indicate drivers that have moved to the new probe method.
- */
-static inline int iommu_dummy_of_xlate(struct device *dev,
-				       struct of_phandle_args *args)
-{
-	return 0;
-}
-
 #define __iommu_first(a, b)                              \
 	({                                               \
 		struct iommu_device *a_dev = a;          \
diff --git a/include/linux/iommu.h b/include/linux/iommu.h
index 0ba12e0e450705..456b9b16599ce0 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -354,7 +354,6 @@ static inline int __iommu_copy_struct_from_user(
  *                  group and attached to the groups domain
  * @device_group: find iommu group for a particular device
  * @get_resv_regions: Request list of reserved regions for a device
- * @of_xlate: add OF master IDs to iommu grouping
  * @is_attach_deferred: Check if domain attach should be deferred from iommu
  *                      driver init to device driver init (default no)
  * @dev_enable/disable_feat: per device entries to enable/disable
@@ -398,7 +397,6 @@ struct iommu_ops {
 	/* Request/Free a list of reserved regions for a device */
 	void (*get_resv_regions)(struct device *dev, struct list_head *list);
 
-	int (*of_xlate)(struct device *dev, struct of_phandle_args *args);
 	bool (*is_attach_deferred)(struct device *dev);
 
 	/* Per device IOMMU features */
@@ -534,7 +532,6 @@ struct iommu_fault_param {
  *
  * @fault_param: IOMMU detected device fault reporting data
  * @iopf_param:	 I/O Page Fault queue and data
- * @fwspec:	 IOMMU fwspec data
  * @iommu_dev:	 IOMMU device this device is linked to
  * @priv:	 IOMMU Driver private data
  * @max_pasids:  number of PASIDs this device can consume
@@ -550,7 +547,6 @@ struct dev_iommu {
 	struct mutex lock;
 	struct iommu_fault_param	*fault_param;
 	struct iopf_device_param	*iopf_param;
-	struct iommu_fwspec		*fwspec;
 	struct iommu_device		*iommu_dev;
 	void				*priv;
 	u32				max_pasids;
@@ -787,29 +783,6 @@ extern struct iommu_group *generic_device_group(struct device *dev);
 struct iommu_group *fsl_mc_device_group(struct device *dev);
 extern struct iommu_group *generic_single_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
- * @flags: IOMMU_FWSPEC_* flags
- * @num_ids: number of associated device IDs
- * @ids: IDs which this device may present to the IOMMU
- *
- * Note that the IDs (and any other information, really) stored in this structure should be
- * considered private to the IOMMU device driver and are not to be used directly by IOMMU
- * consumers.
- */
-struct iommu_fwspec {
-	const struct iommu_ops	*ops;
-	struct fwnode_handle	*iommu_fwnode;
-	u32			flags;
-	unsigned int		num_ids;
-	u32			ids[];
-};
-
-/* ATS is supported */
-#define IOMMU_FWSPEC_PCI_RC_ATS			(1 << 0)
-
 /**
  * struct iommu_sva - handle to a device-mm bond
  */
@@ -818,25 +791,6 @@ struct iommu_sva {
 	struct iommu_domain		*domain;
 };
 
-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);
-
-static inline struct iommu_fwspec *dev_iommu_fwspec_get(struct device *dev)
-{
-	if (dev->iommu)
-		return dev->iommu->fwspec;
-	else
-		return NULL;
-}
-
-static inline void dev_iommu_fwspec_set(struct device *dev,
-					struct iommu_fwspec *fwspec)
-{
-	dev->iommu->fwspec = fwspec;
-}
-
 static inline void *dev_iommu_priv_get(struct device *dev)
 {
 	if (dev->iommu)
@@ -847,7 +801,6 @@ static inline void *dev_iommu_priv_get(struct device *dev)
 
 void dev_iommu_priv_set(struct device *dev, void *priv);
 
-extern struct mutex iommu_probe_device_lock;
 int iommu_probe_device(struct device *dev);
 
 int iommu_dev_enable_feature(struct device *dev, enum iommu_dev_features f);
@@ -878,7 +831,6 @@ void iommu_free_global_pasid(ioasid_t pasid);
 
 struct iommu_ops {};
 struct iommu_group {};
-struct iommu_fwspec {};
 struct iommu_device {};
 struct iommu_fault_param {};
 struct iommu_iotlb_gather {};
@@ -1153,23 +1105,6 @@ 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;
-}
-
 static inline int
 iommu_dev_enable_feature(struct device *dev, enum iommu_dev_features feat)
 {
@@ -1182,11 +1117,6 @@ iommu_dev_disable_feature(struct device *dev, enum iommu_dev_features feat)
 	return -ENODEV;
 }
 
-static inline struct iommu_fwspec *dev_iommu_fwspec_get(struct device *dev)
-{
-	return NULL;
-}
-
 static inline int iommu_device_use_default_domain(struct device *dev)
 {
 	return 0;
-- 
2.42.0


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

* Re: [PATCH 03/30] ACPI: IORT: Make a iort_iommu_for_each_id()
  2023-11-30  1:10 ` [PATCH 03/30] ACPI: IORT: Make a iort_iommu_for_each_id() Jason Gunthorpe
@ 2023-11-30 13:21   ` Rafael J. Wysocki
  0 siblings, 0 replies; 34+ messages in thread
From: Rafael J. Wysocki @ 2023-11-30 13:21 UTC (permalink / raw)
  To: Jason Gunthorpe
  Cc: acpica-devel, Andy Gross, Alim Akhtar, Alyssa Rosenzweig,
	Bjorn Andersson, AngeloGioacchino Del Regno, asahi, Baolin Wang,
	devicetree, Frank Rowand, Hanjun Guo, Gustavo A. R. Silva,
	Heiko Stuebner, iommu, Jean-Philippe Brucker, Jernej Skrabec,
	Jonathan Hunter, Joerg Roedel, Kees Cook, Konrad Dybcio,
	Krzysztof Kozlowski, Len Brown, linux-acpi, linux-arm-kernel,
	linux-arm-msm, linux-hardening, linux-mediatek, linux-rockchip,
	linux-samsung-soc, linux-sunxi, linux-tegra, Lorenzo Pieralisi,
	Marek Szyprowski, Hector Martin, Matthias Brugger, Orson Zhai,
	Rafael J. Wysocki, Rob Clark, Robert Moore, Rob Herring,
	Robin Murphy, Samuel Holland, Sudeep Holla, Sven Peter,
	Thierry Reding, Krishna Reddy, virtualization, Chen-Yu Tsai,
	Will Deacon, Yong Wu, Chunyan Zhang, André Draszik, patches

On Thu, Nov 30, 2023 at 2:11 AM Jason Gunthorpe <jgg@nvidia.com> wrote:
>
> Similar to of_iommu_for_each_id() this parses the IORT ACPI description
> and invokes a function over each entry in the table.
>
> Have iort_iommu_configure_id() use the new function to call
> iort_iommu_xlate().
>
> Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>

Acked-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>

> ---
>  drivers/acpi/arm64/iort.c | 118 ++++++++++++++++++++++++--------------
>  include/linux/acpi_iort.h |  12 ++++
>  2 files changed, 86 insertions(+), 44 deletions(-)
>
> diff --git a/drivers/acpi/arm64/iort.c b/drivers/acpi/arm64/iort.c
> index bdaf9256870d92..5c9b4c23f96a87 100644
> --- a/drivers/acpi/arm64/iort.c
> +++ b/drivers/acpi/arm64/iort.c
> @@ -1218,9 +1218,10 @@ static bool iort_pci_rc_supports_ats(struct acpi_iort_node *node)
>         return pci_rc->ats_attribute & ACPI_IORT_ATS_SUPPORTED;
>  }
>
> -static int iort_iommu_xlate(struct device *dev, struct acpi_iort_node *node,
> -                           u32 streamid)
> +static int iort_iommu_xlate(struct acpi_iort_node *node, u32 streamid,
> +                           void *info)
>  {
> +       struct device *dev = info;
>         const struct iommu_ops *ops;
>         struct fwnode_handle *iort_fwnode;
>
> @@ -1250,9 +1251,11 @@ static int iort_iommu_xlate(struct device *dev, struct acpi_iort_node *node,
>  struct iort_pci_alias_info {
>         struct device *dev;
>         struct acpi_iort_node *node;
> +       iort_for_each_fn fn;
> +       void *info;
>  };
>
> -static int iort_pci_iommu_init(struct pci_dev *pdev, u16 alias, void *data)
> +static int __for_each_pci_alias(struct pci_dev *pdev, u16 alias, void *data)
>  {
>         struct iort_pci_alias_info *info = data;
>         struct acpi_iort_node *parent;
> @@ -1260,7 +1263,7 @@ static int iort_pci_iommu_init(struct pci_dev *pdev, u16 alias, void *data)
>
>         parent = iort_node_map_id(info->node, alias, &streamid,
>                                   IORT_IOMMU_TYPE);
> -       return iort_iommu_xlate(info->dev, parent, streamid);
> +       return info->fn(parent, streamid, info->info);
>  }
>
>  static void iort_named_component_init(struct device *dev,
> @@ -1280,7 +1283,8 @@ static void iort_named_component_init(struct device *dev,
>                 dev_warn(dev, "Could not add device properties\n");
>  }
>
> -static int iort_nc_iommu_map(struct device *dev, struct acpi_iort_node *node)
> +static int __for_each_platform(struct acpi_iort_node *node, iort_for_each_fn fn,
> +                              void *info)
>  {
>         struct acpi_iort_node *parent;
>         int err = -ENODEV, i = 0;
> @@ -1293,27 +1297,71 @@ static int iort_nc_iommu_map(struct device *dev, struct acpi_iort_node *node)
>                                                    i++);
>
>                 if (parent)
> -                       err = iort_iommu_xlate(dev, parent, streamid);
> +                       err = fn(parent, streamid, info);
>         } while (parent && !err);
>
>         return err;
>  }
>
> -static int iort_nc_iommu_map_id(struct device *dev,
> -                               struct acpi_iort_node *node,
> -                               const u32 *in_id)
> +int iort_iommu_for_each_id(struct device *dev, const u32 *id_in,
> +                          struct iort_params *params, iort_for_each_fn fn,
> +                          void *info)
>  {
> -       struct acpi_iort_node *parent;
> -       u32 streamid;
> +       struct acpi_iort_named_component *nc;
> +       struct acpi_iort_node *node;
> +       int err = -ENODEV;
>
> -       parent = iort_node_map_id(node, *in_id, &streamid, IORT_IOMMU_TYPE);
> -       if (parent)
> -               return iort_iommu_xlate(dev, parent, streamid);
> +       memset(params, 0, sizeof(*params));
> +       if (dev_is_pci(dev)) {
> +               struct pci_bus *bus = to_pci_dev(dev)->bus;
> +               struct iort_pci_alias_info pci_info = { .dev = dev,
> +                                                       .fn = fn,
> +                                                       .info = info };
>
> -       return -ENODEV;
> +               node = iort_scan_node(ACPI_IORT_NODE_PCI_ROOT_COMPLEX,
> +                                     iort_match_node_callback, &bus->dev);
> +               if (!node)
> +                       return -ENODEV;
> +
> +               pci_info.node = node;
> +               err = pci_for_each_dma_alias(to_pci_dev(dev),
> +                                            __for_each_pci_alias, &pci_info);
> +
> +               if (iort_pci_rc_supports_ats(node))
> +                       params->pci_rc_ats = true;
> +               return 0;
> +       }
> +
> +       node = iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT,
> +                             iort_match_node_callback, dev);
> +       if (!node)
> +               return -ENODEV;
> +
> +       if (id_in) {
> +               struct acpi_iort_node *parent;
> +               u32 streamid;
> +
> +               parent = iort_node_map_id(node, *id_in, &streamid,
> +                                         IORT_IOMMU_TYPE);
> +               if (!parent)
> +                       return -ENODEV;
> +               err = fn(parent, streamid, info);
> +       } else {
> +               err = __for_each_platform(node, fn, info);
> +       }
> +       if (err)
> +               return err;
> +
> +       nc = (struct acpi_iort_named_component *)node->node_data;
> +       params->pasid_num_bits = FIELD_GET(ACPI_IORT_NC_PASID_BITS,
> +                                               nc->node_flags);
> +       if (nc->node_flags & ACPI_IORT_NC_STALL_SUPPORTED)
> +               params->dma_can_stall = true;
> +
> +       iort_named_component_init(dev, node);
> +       return 0;
>  }
>
> -
>  /**
>   * iort_iommu_configure_id - Set-up IOMMU configuration for a device.
>   *
> @@ -1324,40 +1372,22 @@ static int iort_nc_iommu_map_id(struct device *dev,
>   */
>  int iort_iommu_configure_id(struct device *dev, const u32 *id_in)
>  {
> -       struct acpi_iort_node *node;
> -       int err = -ENODEV;
> +       struct iort_params params;
> +       int err;
>
> -       if (dev_is_pci(dev)) {
> +       err = iort_iommu_for_each_id(dev, id_in, &params, &iort_iommu_xlate,
> +                                    dev);
> +       if (err)
> +               return err;
> +
> +       if (params.pci_rc_ats) {
>                 struct iommu_fwspec *fwspec;
> -               struct pci_bus *bus = to_pci_dev(dev)->bus;
> -               struct iort_pci_alias_info info = { .dev = dev };
> -
> -               node = iort_scan_node(ACPI_IORT_NODE_PCI_ROOT_COMPLEX,
> -                                     iort_match_node_callback, &bus->dev);
> -               if (!node)
> -                       return -ENODEV;
> -
> -               info.node = node;
> -               err = pci_for_each_dma_alias(to_pci_dev(dev),
> -                                            iort_pci_iommu_init, &info);
>
>                 fwspec = dev_iommu_fwspec_get(dev);
> -               if (fwspec && iort_pci_rc_supports_ats(node))
> +               if (fwspec)
>                         fwspec->flags |= IOMMU_FWSPEC_PCI_RC_ATS;
> -       } else {
> -               node = iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT,
> -                                     iort_match_node_callback, dev);
> -               if (!node)
> -                       return -ENODEV;
> -
> -               err = id_in ? iort_nc_iommu_map_id(dev, node, id_in) :
> -                             iort_nc_iommu_map(dev, node);
> -
> -               if (!err)
> -                       iort_named_component_init(dev, node);
>         }
> -
> -       return err;
> +       return 0;
>  }
>
>  #else
> diff --git a/include/linux/acpi_iort.h b/include/linux/acpi_iort.h
> index 1cb65592c95dd3..5423abff9b6b09 100644
> --- a/include/linux/acpi_iort.h
> +++ b/include/linux/acpi_iort.h
> @@ -29,6 +29,18 @@ void iort_deregister_domain_token(int trans_id);
>  struct fwnode_handle *iort_find_domain_token(int trans_id);
>  int iort_pmsi_get_dev_id(struct device *dev, u32 *dev_id);
>
> +struct iort_params {
> +       unsigned int pasid_num_bits;
> +       bool dma_can_stall : 1;
> +       bool pci_rc_ats : 1;
> +};
> +
> +typedef int (*iort_for_each_fn)(struct acpi_iort_node *iommu, u32 streamid,
> +                               void *info);
> +int iort_iommu_for_each_id(struct device *dev, const u32 *id_in,
> +                          struct iort_params *params, iort_for_each_fn fn,
> +                          void *info);
> +
>  #ifdef CONFIG_ACPI_IORT
>  u32 iort_msi_map_id(struct device *dev, u32 id);
>  struct irq_domain *iort_get_device_domain(struct device *dev, u32 id,
> --
> 2.42.0
>

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

* Re: [PATCH 02/30] ACPI: VIOT: Make a viot_iommu_for_each_id()
  2023-11-30  1:10 ` [PATCH 02/30] ACPI: VIOT: Make a viot_iommu_for_each_id() Jason Gunthorpe
@ 2023-11-30 13:22   ` Rafael J. Wysocki
  0 siblings, 0 replies; 34+ messages in thread
From: Rafael J. Wysocki @ 2023-11-30 13:22 UTC (permalink / raw)
  To: Jason Gunthorpe
  Cc: acpica-devel, Andy Gross, Alim Akhtar, Alyssa Rosenzweig,
	Bjorn Andersson, AngeloGioacchino Del Regno, asahi, Baolin Wang,
	devicetree, Frank Rowand, Hanjun Guo, Gustavo A. R. Silva,
	Heiko Stuebner, iommu, Jean-Philippe Brucker, Jernej Skrabec,
	Jonathan Hunter, Joerg Roedel, Kees Cook, Konrad Dybcio,
	Krzysztof Kozlowski, Len Brown, linux-acpi, linux-arm-kernel,
	linux-arm-msm, linux-hardening, linux-mediatek, linux-rockchip,
	linux-samsung-soc, linux-sunxi, linux-tegra, Lorenzo Pieralisi,
	Marek Szyprowski, Hector Martin, Matthias Brugger, Orson Zhai,
	Rafael J. Wysocki, Rob Clark, Robert Moore, Rob Herring,
	Robin Murphy, Samuel Holland, Sudeep Holla, Sven Peter,
	Thierry Reding, Krishna Reddy, virtualization, Chen-Yu Tsai,
	Will Deacon, Yong Wu, Chunyan Zhang, André Draszik, patches

On Thu, Nov 30, 2023 at 2:11 AM Jason Gunthorpe <jgg@nvidia.com> wrote:
>
> Similar to of_iommu_for_each_id() this parses the VIOT ACPI description
> and invokes a function over each entry in the table.
>
> Have viot_iommu_configure() use the new function to call
> viot_dev_iommu_init().
>
> Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>

Acked-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>

> ---
>  drivers/acpi/viot.c       | 54 +++++++++++++++++++++++----------------
>  include/linux/acpi_viot.h | 11 ++++++++
>  2 files changed, 43 insertions(+), 22 deletions(-)
>
> diff --git a/drivers/acpi/viot.c b/drivers/acpi/viot.c
> index c8025921c129b2..7ab35ef05c84e0 100644
> --- a/drivers/acpi/viot.c
> +++ b/drivers/acpi/viot.c
> @@ -25,13 +25,6 @@
>  #include <linux/pci.h>
>  #include <linux/platform_device.h>
>
> -struct viot_iommu {
> -       /* Node offset within the table */
> -       unsigned int                    offset;
> -       struct fwnode_handle            *fwnode;
> -       struct list_head                list;
> -};
> -
>  struct viot_endpoint {
>         union {
>                 /* PCI range */
> @@ -304,10 +297,10 @@ void __init acpi_viot_init(void)
>         acpi_put_table(hdr);
>  }
>
> -static int viot_dev_iommu_init(struct device *dev, struct viot_iommu *viommu,
> -                              u32 epid)
> +static int viot_dev_iommu_init(struct viot_iommu *viommu, u32 epid, void *info)
>  {
>         const struct iommu_ops *ops;
> +       struct device *dev = info;
>
>         if (!viommu)
>                 return -ENODEV;
> @@ -324,11 +317,17 @@ static int viot_dev_iommu_init(struct device *dev, struct viot_iommu *viommu,
>         return acpi_iommu_fwspec_init(dev, epid, viommu->fwnode, ops);
>  }
>
> -static int viot_pci_dev_iommu_init(struct pci_dev *pdev, u16 dev_id, void *data)
> +struct viot_pci_iommu_alias_info {
> +       struct device *dev;
> +       viot_for_each_fn fn;
> +       void *info;
> +};
> +
> +static int __for_each_pci_alias(struct pci_dev *pdev, u16 dev_id, void *data)
>  {
>         u32 epid;
>         struct viot_endpoint *ep;
> -       struct device *aliased_dev = data;
> +       struct viot_pci_iommu_alias_info *info = data;
>         u32 domain_nr = pci_domain_nr(pdev->bus);
>
>         list_for_each_entry(ep, &viot_pci_ranges, list) {
> @@ -339,14 +338,14 @@ static int viot_pci_dev_iommu_init(struct pci_dev *pdev, u16 dev_id, void *data)
>                         epid = ((domain_nr - ep->segment_start) << 16) +
>                                 dev_id - ep->bdf_start + ep->endpoint_id;
>
> -                       return viot_dev_iommu_init(aliased_dev, ep->viommu,
> -                                                  epid);
> +                       return info->fn(ep->viommu, epid, info->info);
>                 }
>         }
>         return -ENODEV;
>  }
>
> -static int viot_mmio_dev_iommu_init(struct platform_device *pdev)
> +static int __for_each_platform(struct platform_device *pdev,
> +                              viot_for_each_fn fn, void *info)
>  {
>         struct resource *mem;
>         struct viot_endpoint *ep;
> @@ -357,12 +356,28 @@ static int viot_mmio_dev_iommu_init(struct platform_device *pdev)
>
>         list_for_each_entry(ep, &viot_mmio_endpoints, list) {
>                 if (ep->address == mem->start)
> -                       return viot_dev_iommu_init(&pdev->dev, ep->viommu,
> -                                                  ep->endpoint_id);
> +                       return fn(ep->viommu, ep->endpoint_id, info);
>         }
>         return -ENODEV;
>  }
>
> +int viot_iommu_for_each_id(struct device *dev, viot_for_each_fn fn, void *info)
> +{
> +       if (dev_is_pci(dev)) {
> +               struct viot_pci_iommu_alias_info pci_info = {
> +                       .dev = dev,
> +                       .fn = fn,
> +                       .info = info,
> +               };
> +               return pci_for_each_dma_alias(to_pci_dev(dev),
> +                                             __for_each_pci_alias, &pci_info);
> +       }
> +
> +       if (dev_is_platform(dev))
> +               return __for_each_platform(to_platform_device(dev), fn, info);
> +       return -ENODEV;
> +}
> +
>  /**
>   * viot_iommu_configure - Setup IOMMU ops for an endpoint described by VIOT
>   * @dev: the endpoint
> @@ -371,10 +386,5 @@ static int viot_mmio_dev_iommu_init(struct platform_device *pdev)
>   */
>  int viot_iommu_configure(struct device *dev)
>  {
> -       if (dev_is_pci(dev))
> -               return pci_for_each_dma_alias(to_pci_dev(dev),
> -                                             viot_pci_dev_iommu_init, dev);
> -       else if (dev_is_platform(dev))
> -               return viot_mmio_dev_iommu_init(to_platform_device(dev));
> -       return -ENODEV;
> +       return viot_iommu_for_each_id(dev, viot_dev_iommu_init, dev);
>  }
> diff --git a/include/linux/acpi_viot.h b/include/linux/acpi_viot.h
> index a5a12243156377..fce4eefcae4aad 100644
> --- a/include/linux/acpi_viot.h
> +++ b/include/linux/acpi_viot.h
> @@ -5,6 +5,17 @@
>
>  #include <linux/acpi.h>
>
> +struct viot_iommu {
> +       /* Node offset within the table */
> +       unsigned int                    offset;
> +       struct fwnode_handle            *fwnode;
> +       struct list_head                list;
> +};
> +
> +typedef int (*viot_for_each_fn)(struct viot_iommu *viommu, u32 epid,
> +                               void *info);
> +int viot_iommu_for_each_id(struct device *dev, viot_for_each_fn fn, void *info);
> +
>  #ifdef CONFIG_ACPI_VIOT
>  void __init acpi_viot_early_init(void);
>  void __init acpi_viot_init(void);
> --
> 2.42.0
>

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

* Re: [PATCH 04/30] ACPI: IORT: Remove fwspec from the reserved region code
  2023-11-30  1:10 ` [PATCH 04/30] ACPI: IORT: Remove fwspec from the reserved region code Jason Gunthorpe
@ 2023-11-30 13:23   ` Rafael J. Wysocki
  0 siblings, 0 replies; 34+ messages in thread
From: Rafael J. Wysocki @ 2023-11-30 13:23 UTC (permalink / raw)
  To: Jason Gunthorpe
  Cc: acpica-devel, Andy Gross, Alim Akhtar, Alyssa Rosenzweig,
	Bjorn Andersson, AngeloGioacchino Del Regno, asahi, Baolin Wang,
	devicetree, Frank Rowand, Hanjun Guo, Gustavo A. R. Silva,
	Heiko Stuebner, iommu, Jean-Philippe Brucker, Jernej Skrabec,
	Jonathan Hunter, Joerg Roedel, Kees Cook, Konrad Dybcio,
	Krzysztof Kozlowski, Len Brown, linux-acpi, linux-arm-kernel,
	linux-arm-msm, linux-hardening, linux-mediatek, linux-rockchip,
	linux-samsung-soc, linux-sunxi, linux-tegra, Lorenzo Pieralisi,
	Marek Szyprowski, Hector Martin, Matthias Brugger, Orson Zhai,
	Rafael J. Wysocki, Rob Clark, Robert Moore, Rob Herring,
	Robin Murphy, Samuel Holland, Sudeep Holla, Sven Peter,
	Thierry Reding, Krishna Reddy, virtualization, Chen-Yu Tsai,
	Will Deacon, Yong Wu, Chunyan Zhang, André Draszik, patches

On Thu, Nov 30, 2023 at 2:10 AM Jason Gunthorpe <jgg@nvidia.com> wrote:
>
> iort_iommu_get_resv_regions() needs access to the parsed id array that is
> currently stored in the iommu_fwspec.
>
> Instead of getting this from the fwspec inside the iort code have the
> caller pass it in.
>
> Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>

Acked-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>

> ---
>  drivers/acpi/arm64/iort.c | 88 ++++++++++++++++++++++++---------------
>  drivers/iommu/dma-iommu.c |  7 +++-
>  include/linux/acpi_iort.h |  8 +++-
>  3 files changed, 65 insertions(+), 38 deletions(-)
>
> diff --git a/drivers/acpi/arm64/iort.c b/drivers/acpi/arm64/iort.c
> index 5c9b4c23f96a87..93e30f2f5004f0 100644
> --- a/drivers/acpi/arm64/iort.c
> +++ b/drivers/acpi/arm64/iort.c
> @@ -946,11 +946,19 @@ static u32 *iort_rmr_alloc_sids(u32 *sids, u32 count, u32 id_start,
>         return new_sids;
>  }
>
> -static bool iort_rmr_has_dev(struct device *dev, u32 id_start,
> +struct iort_resv_args {
> +       struct device *dev;
> +       struct list_head *head;
> +       struct fwnode_handle *iommu_fwnode;
> +       const u32 *fw_ids;
> +       unsigned int fw_num_ids;
> +};
> +
> +static bool iort_rmr_has_dev(struct iort_resv_args *args, u32 id_start,
>                              u32 id_count)
>  {
> +       struct device *dev = args->dev;
>         int i;
> -       struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
>
>         /*
>          * Make sure the kernel has preserved the boot firmware PCIe
> @@ -965,18 +973,18 @@ static bool iort_rmr_has_dev(struct device *dev, u32 id_start,
>                         return false;
>         }
>
> -       for (i = 0; i < fwspec->num_ids; i++) {
> -               if (fwspec->ids[i] >= id_start &&
> -                   fwspec->ids[i] <= id_start + id_count)
> +       for (i = 0; i < args->fw_num_ids; i++) {
> +               if (args->fw_ids[i] >= id_start &&
> +                   args->fw_ids[i] <= id_start + id_count)
>                         return true;
>         }
>
>         return false;
>  }
>
> -static void iort_node_get_rmr_info(struct acpi_iort_node *node,
> -                                  struct acpi_iort_node *iommu,
> -                                  struct device *dev, struct list_head *head)
> +static void iort_node_get_rmr_info(struct iort_resv_args *args,
> +                                  struct acpi_iort_node *node,
> +                                  struct acpi_iort_node *iommu)
>  {
>         struct acpi_iort_node *smmu = NULL;
>         struct acpi_iort_rmr *rmr;
> @@ -1013,8 +1021,8 @@ static void iort_node_get_rmr_info(struct acpi_iort_node *node,
>                         continue;
>
>                 /* If dev is valid, check RMR node corresponds to the dev SID */
> -               if (dev && !iort_rmr_has_dev(dev, map->output_base,
> -                                            map->id_count))
> +               if (args->dev &&
> +                   !iort_rmr_has_dev(args, map->output_base, map->id_count))
>                         continue;
>
>                 /* Retrieve SIDs associated with the Node. */
> @@ -1029,12 +1037,12 @@ static void iort_node_get_rmr_info(struct acpi_iort_node *node,
>         if (!sids)
>                 return;
>
> -       iort_get_rmrs(node, smmu, sids, num_sids, head);
> +       iort_get_rmrs(node, smmu, sids, num_sids, args->head);
>         kfree(sids);
>  }
>
> -static void iort_find_rmrs(struct acpi_iort_node *iommu, struct device *dev,
> -                          struct list_head *head)
> +static void iort_find_rmrs(struct iort_resv_args *args,
> +                          struct acpi_iort_node *iommu)
>  {
>         struct acpi_table_iort *iort;
>         struct acpi_iort_node *iort_node, *iort_end;
> @@ -1057,7 +1065,7 @@ static void iort_find_rmrs(struct acpi_iort_node *iommu, struct device *dev,
>                         return;
>
>                 if (iort_node->type == ACPI_IORT_NODE_RMR)
> -                       iort_node_get_rmr_info(iort_node, iommu, dev, head);
> +                       iort_node_get_rmr_info(args, iort_node, iommu);
>
>                 iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort_node,
>                                          iort_node->length);
> @@ -1069,25 +1077,23 @@ static void iort_find_rmrs(struct acpi_iort_node *iommu, struct device *dev,
>   * If dev is NULL, the function populates all the RMRs associated with the
>   * given IOMMU.
>   */
> -static void iort_iommu_rmr_get_resv_regions(struct fwnode_handle *iommu_fwnode,
> -                                           struct device *dev,
> -                                           struct list_head *head)
> +static void iort_iommu_rmr_get_resv_regions(struct iort_resv_args *args)
>  {
>         struct acpi_iort_node *iommu;
>
> -       iommu = iort_get_iort_node(iommu_fwnode);
> +       iommu = iort_get_iort_node(args->iommu_fwnode);
>         if (!iommu)
>                 return;
>
> -       iort_find_rmrs(iommu, dev, head);
> +       iort_find_rmrs(args, iommu);
>  }
>
> -static struct acpi_iort_node *iort_get_msi_resv_iommu(struct device *dev)
> +static struct acpi_iort_node *
> +iort_get_msi_resv_iommu(struct iort_resv_args *args)
>  {
>         struct acpi_iort_node *iommu;
> -       struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
>
> -       iommu = iort_get_iort_node(fwspec->iommu_fwnode);
> +       iommu = iort_get_iort_node(args->iommu_fwnode);
>
>         if (iommu && (iommu->type == ACPI_IORT_NODE_SMMU_V3)) {
>                 struct acpi_iort_smmu_v3 *smmu;
> @@ -1105,15 +1111,13 @@ static struct acpi_iort_node *iort_get_msi_resv_iommu(struct device *dev)
>   * The ITS interrupt translation spaces (ITS_base + SZ_64K, SZ_64K)
>   * associated with the device are the HW MSI reserved regions.
>   */
> -static void iort_iommu_msi_get_resv_regions(struct device *dev,
> -                                           struct list_head *head)
> +static void iort_iommu_msi_get_resv_regions(struct iort_resv_args *args)
>  {
> -       struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
>         struct acpi_iort_its_group *its;
>         struct acpi_iort_node *iommu_node, *its_node = NULL;
>         int i;
>
> -       iommu_node = iort_get_msi_resv_iommu(dev);
> +       iommu_node = iort_get_msi_resv_iommu(args);
>         if (!iommu_node)
>                 return;
>
> @@ -1126,9 +1130,9 @@ static void iort_iommu_msi_get_resv_regions(struct device *dev,
>          * a given PCI or named component may map IDs to.
>          */
>
> -       for (i = 0; i < fwspec->num_ids; i++) {
> +       for (i = 0; i < args->fw_num_ids; i++) {
>                 its_node = iort_node_map_id(iommu_node,
> -                                       fwspec->ids[i],
> +                                       args->fw_ids[i],
>                                         NULL, IORT_MSI_TYPE);
>                 if (its_node)
>                         break;
> @@ -1151,7 +1155,7 @@ static void iort_iommu_msi_get_resv_regions(struct device *dev,
>                                                          prot, IOMMU_RESV_MSI,
>                                                          GFP_KERNEL);
>                         if (region)
> -                               list_add_tail(&region->list, head);
> +                               list_add_tail(&region->list, args->head);
>                 }
>         }
>  }
> @@ -1160,13 +1164,24 @@ static void iort_iommu_msi_get_resv_regions(struct device *dev,
>   * iort_iommu_get_resv_regions - Generic helper to retrieve reserved regions.
>   * @dev: Device from iommu_get_resv_regions()
>   * @head: Reserved region list from iommu_get_resv_regions()
> + * @iommu_fwnode: fwnode that describes the iommu connection for the device
> + * @fw_ids: Parsed IDs
> + * @fw_num_ids: Length of fw_ids
>   */
> -void iort_iommu_get_resv_regions(struct device *dev, struct list_head *head)
> +void iort_iommu_get_resv_regions(struct device *dev, struct list_head *head,
> +                                struct fwnode_handle *iommu_fwnode,
> +                                const u32 *fw_ids, unsigned int fw_num_ids)
>  {
> -       struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
> +       struct iort_resv_args args = {
> +               .dev = dev,
> +               .head = head,
> +               .iommu_fwnode = iommu_fwnode,
> +               .fw_ids = fw_ids,
> +               .fw_num_ids = fw_num_ids,
> +       };
>
> -       iort_iommu_msi_get_resv_regions(dev, head);
> -       iort_iommu_rmr_get_resv_regions(fwspec->iommu_fwnode, dev, head);
> +       iort_iommu_msi_get_resv_regions(&args);
> +       iort_iommu_rmr_get_resv_regions(&args);
>  }
>
>  /**
> @@ -1178,7 +1193,12 @@ void iort_iommu_get_resv_regions(struct device *dev, struct list_head *head)
>  void iort_get_rmr_sids(struct fwnode_handle *iommu_fwnode,
>                        struct list_head *head)
>  {
> -       iort_iommu_rmr_get_resv_regions(iommu_fwnode, NULL, head);
> +       struct iort_resv_args args = {
> +               .head = head,
> +               .iommu_fwnode = iommu_fwnode,
> +       };
> +
> +       iort_iommu_rmr_get_resv_regions(&args);
>  }
>  EXPORT_SYMBOL_GPL(iort_get_rmr_sids);
>
> diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
> index 85163a83df2f68..d644b0502ef48e 100644
> --- a/drivers/iommu/dma-iommu.c
> +++ b/drivers/iommu/dma-iommu.c
> @@ -468,9 +468,12 @@ void iommu_put_dma_cookie(struct iommu_domain *domain)
>   */
>  void iommu_dma_get_resv_regions(struct device *dev, struct list_head *list)
>  {
> +       struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
>
> -       if (!is_of_node(dev_iommu_fwspec_get(dev)->iommu_fwnode))
> -               iort_iommu_get_resv_regions(dev, list);
> +       if (!is_of_node(fwspec->iommu_fwnode)) {
> +               iort_iommu_get_resv_regions(dev, list, fwspec->iommu_fwnode,
> +                                           fwspec->ids, fwspec->num_ids);
> +       }
>
>         if (dev->of_node)
>                 of_iommu_get_resv_regions(dev, list);
> diff --git a/include/linux/acpi_iort.h b/include/linux/acpi_iort.h
> index 5423abff9b6b09..13f0cefb930693 100644
> --- a/include/linux/acpi_iort.h
> +++ b/include/linux/acpi_iort.h
> @@ -53,7 +53,9 @@ void iort_put_rmr_sids(struct fwnode_handle *iommu_fwnode,
>  /* IOMMU interface */
>  int iort_dma_get_ranges(struct device *dev, u64 *size);
>  int iort_iommu_configure_id(struct device *dev, const u32 *id_in);
> -void iort_iommu_get_resv_regions(struct device *dev, struct list_head *head);
> +void iort_iommu_get_resv_regions(struct device *dev, struct list_head *head,
> +                                struct fwnode_handle *iommu_fwnode,
> +                                const u32 *fw_ids, unsigned int fw_num_ids);
>  phys_addr_t acpi_iort_dma_get_max_cpu_address(void);
>  #else
>  static inline u32 iort_msi_map_id(struct device *dev, u32 id)
> @@ -72,7 +74,9 @@ static inline int iort_dma_get_ranges(struct device *dev, u64 *size)
>  static inline int iort_iommu_configure_id(struct device *dev, const u32 *id_in)
>  { return -ENODEV; }
>  static inline
> -void iort_iommu_get_resv_regions(struct device *dev, struct list_head *head)
> +void iort_iommu_get_resv_regions(struct device *dev, struct list_head *head,
> +                                struct fwnode_handle *iommu_fwnode,
> +                                const u32 *fw_ids, unsigned int fw_num_ids)
>  { }
>
>  static inline phys_addr_t acpi_iort_dma_get_max_cpu_address(void)
> --
> 2.42.0
>

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

end of thread, other threads:[~2023-11-30 13:23 UTC | newest]

Thread overview: 34+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-11-30  1:10 [PATCH 00/30] Make a new API for drivers to use to get their FW Jason Gunthorpe
2023-11-30  1:10 ` [PATCH 01/30] iommu/of: Make a of_iommu_for_each_id() Jason Gunthorpe
2023-11-30  1:10 ` [PATCH 02/30] ACPI: VIOT: Make a viot_iommu_for_each_id() Jason Gunthorpe
2023-11-30 13:22   ` Rafael J. Wysocki
2023-11-30  1:10 ` [PATCH 03/30] ACPI: IORT: Make a iort_iommu_for_each_id() Jason Gunthorpe
2023-11-30 13:21   ` Rafael J. Wysocki
2023-11-30  1:10 ` [PATCH 04/30] ACPI: IORT: Remove fwspec from the reserved region code Jason Gunthorpe
2023-11-30 13:23   ` Rafael J. Wysocki
2023-11-30  1:10 ` [PATCH 05/30] iommu: Add iommu_probe_info Jason Gunthorpe
2023-11-30  1:10 ` [PATCH 06/30] iommu: Make iommu_ops_from_fwnode() return the iommu_device Jason Gunthorpe
2023-11-30  1:10 ` [PATCH 07/30] iommu/of: Call of_iommu_get_resv_regions() directly Jason Gunthorpe
2023-11-30  1:10 ` [PATCH 08/30] iommu/of: Add iommu_of_get_single_iommu() Jason Gunthorpe
2023-11-30  1:10 ` [PATCH 09/30] iommu/rockchip: Move to iommu_of_get_single_iommu() Jason Gunthorpe
2023-11-30  1:10 ` [PATCH 10/30] iommu/sprd: " Jason Gunthorpe
2023-11-30  1:10 ` [PATCH 11/30] iommu/sun50i: " Jason Gunthorpe
2023-11-30  1:10 ` [PATCH 12/30] iommu/of: Add iommu_of_xlate() Jason Gunthorpe
2023-11-30  1:10 ` [PATCH 13/30] iommu/dart: Move to iommu_of_xlate() Jason Gunthorpe
2023-11-30  1:10 ` [PATCH 14/30] iommu/exynos: " Jason Gunthorpe
2023-11-30  1:10 ` [PATCH 15/30] iommu/msm: " Jason Gunthorpe
2023-11-30  1:10 ` [PATCH 16/30] iommu/tegra: Route tegra_dev_iommu_get_stream_id() through an op Jason Gunthorpe
2023-11-30  1:10 ` [PATCH 17/30] iommu: Add iommu_fw_alloc_per_device_ids() Jason Gunthorpe
2023-11-30  1:10 ` [PATCH 18/30] iommu/tegra: Move to iommu_fw_alloc_per_device_ids() Jason Gunthorpe
2023-11-30  1:10 ` [PATCH 19/30] iommu/mtk: " Jason Gunthorpe
2023-11-30  1:10 ` [PATCH 20/30] iommu/ipmmu-vmsa: " Jason Gunthorpe
2023-11-30  1:10 ` [PATCH 21/30] iommu/mtk_v1: " Jason Gunthorpe
2023-11-30  1:10 ` [PATCH 22/30] iommu/qcom: " Jason Gunthorpe
2023-11-30  1:10 ` [PATCH 23/30] iommu/viot: Add iommu_viot_get_single_iommu() Jason Gunthorpe
2023-11-30  1:10 ` [PATCH 24/30] iommu/virtio: Move to iommu_fw_alloc_per_device_ids() Jason Gunthorpe
2023-11-30  1:10 ` [PATCH 25/30] iommu/iort: Add iommu_iort_get_single_iommu() Jason Gunthorpe
2023-11-30  1:10 ` [PATCH 26/30] iommu/arm-smmu-v3: Move to iommu_fw_alloc_per_device_ids() Jason Gunthorpe
2023-11-30  1:10 ` [PATCH 27/30] iommu/arm-smmu: Move to iommu_of_xlate() Jason Gunthorpe
2023-11-30  1:10 ` [PATCH 28/30] iommu: Call all drivers if there is no fwspec Jason Gunthorpe
2023-11-30  1:10 ` [PATCH 29/30] iommu: Check for EPROBE_DEFER using the new FW parsers Jason Gunthorpe
2023-11-30  1:10 ` [PATCH 30/30] iommu: Remove fwspec and related Jason Gunthorpe

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).