LinuxPPC-Dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 17/19] usb: musb: use platform_device_set_of_node_from_dev()
From: Bartosz Golaszewski @ 2026-06-29  9:12 UTC (permalink / raw)
  To: Lee Jones, Mark Brown, Thierry Reding, Sebastian Hesselbarth,
	Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Srinivas Kandagatla, Greg Kroah-Hartman, Vinod Koul,
	Rafael J. Wysocki, Danilo Krummrich, Rob Herring, Saravana Kannan,
	Madhavan Srinivasan, Michael Ellerman, Nicholas Piggin,
	Christophe Leroy (CS GROUP), Andi Shyti, Andy Shevchenko,
	Joerg Roedel, Will Deacon, Robin Murphy, Doug Berger,
	Florian Fainelli, Broadcom internal kernel review list,
	Ulf Hansson, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Matthew Brost, Thomas Hellström, Rodrigo Vivi,
	David Airlie, Simona Vetter, Peter Chen, Paul Cercueil, Bin Liu,
	Philipp Zabel, Maximilian Luz, Hans de Goede, Ilpo Järvinen,
	Krzysztof Kozlowski, Benjamin Herrenschmidt
  Cc: brgl, linux-kernel, netdev, linux-arm-msm, linux-sound,
	driver-core, devicetree, linuxppc-dev, linux-i2c, iommu, linux-pm,
	imx, linux-arm-kernel, intel-xe, dri-devel, linux-usb, linux-mips,
	platform-driver-x86, Bartosz Golaszewski
In-Reply-To: <20260629-pdev-fwnode-ref-v2-0-8abe2513f96e@oss.qualcomm.com>

Ahead of reworking the reference counting logic for platform devices,
encapsulate the assignment of the OF node from another device for
dynamically allocated platform devices with the provided helper.

Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
---
 drivers/usb/musb/jz4740.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/usb/musb/jz4740.c b/drivers/usb/musb/jz4740.c
index df56c972986f7c4f5174a227f35c7e1ac9afa7ca..c770ba576f05b6b672836753cd9b696b752d017a 100644
--- a/drivers/usb/musb/jz4740.c
+++ b/drivers/usb/musb/jz4740.c
@@ -273,7 +273,7 @@ static int jz4740_probe(struct platform_device *pdev)
 	musb->dev.parent		= dev;
 	musb->dev.dma_mask		= &musb->dev.coherent_dma_mask;
 	musb->dev.coherent_dma_mask	= DMA_BIT_MASK(32);
-	device_set_of_node_from_dev(&musb->dev, dev);
+	platform_device_set_of_node_from_dev(musb, dev);
 
 	glue->pdev			= musb;
 	glue->clk			= clk;

-- 
2.47.3



^ permalink raw reply related

* [PATCH v2 00/19] driver core: count references of the platform device's fwnode, not OF node
From: Bartosz Golaszewski @ 2026-06-29  9:12 UTC (permalink / raw)
  To: Lee Jones, Mark Brown, Thierry Reding, Sebastian Hesselbarth,
	Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Srinivas Kandagatla, Greg Kroah-Hartman, Vinod Koul,
	Rafael J. Wysocki, Danilo Krummrich, Rob Herring, Saravana Kannan,
	Madhavan Srinivasan, Michael Ellerman, Nicholas Piggin,
	Christophe Leroy (CS GROUP), Andi Shyti, Andy Shevchenko,
	Joerg Roedel, Will Deacon, Robin Murphy, Doug Berger,
	Florian Fainelli, Broadcom internal kernel review list,
	Ulf Hansson, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Matthew Brost, Thomas Hellström, Rodrigo Vivi,
	David Airlie, Simona Vetter, Peter Chen, Paul Cercueil, Bin Liu,
	Philipp Zabel, Maximilian Luz, Hans de Goede, Ilpo Järvinen,
	Krzysztof Kozlowski, Benjamin Herrenschmidt
  Cc: brgl, linux-kernel, netdev, linux-arm-msm, linux-sound,
	driver-core, devicetree, linuxppc-dev, linux-i2c, iommu, linux-pm,
	imx, linux-arm-kernel, intel-xe, dri-devel, linux-usb, linux-mips,
	platform-driver-x86, Bartosz Golaszewski, stable, Wolfram Sang

Platform device core provides helper interfaces for dealing with
dynamically created platform devices. Most users should use
platform_device_register_full() which encapsulates most of the
operations but some modules will want to use the split approach of
calling platform_device_alloc() + platform_device_add() separately for
various reasons.

With many platform devices now using dynamic software nodes as their
primary firmware nodes and with the platform device interface being
extended to also better cover the use-cases of secondary software nodes,
I believe it makes sense to switch to counting the references of all
kinds of firmware nodes.

To that end, I identified all users of platform_device_alloc() that also
assign dev.of_node or dev.fwnode manually. I noticed five cases where
the references are not increased as they should (patches 1-5 fix these
users) and provided three new functions in platform_device.h that now
become the preferred interfaces for assigning firmware nodes to dynamic
platform devices (in line with platform_device_add_data(),
platform_device_add_resources(), etc.). The bulk of the patches in this
series are small driver conversions to port all users to going through
the new functions that now encapsulate the refcount logic. With that
done, the final patch seamlessly switches to counting the references of
all firmware node types.

This effort is prerequisite of removing platform_device_release_full()
and unifying the release path for dynamic platform devices using
unmanaged software nodes.

Merging strategy: The entire series should go through the driver core
tree, possibly with an immutable branch provided to solve any potential
conflicts though these are rather unlikely.

Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
---
Changes in v2:
- Rebased on top of v7.2-rc1, dropped applied patches, collected tags
- Link to v1: https://patch.msgid.link/20260521-pdev-fwnode-ref-v1-0-88c324a1b8d2@oss.qualcomm.com

---
Bartosz Golaszewski (19):
      powerpc/powermac: fix OF node refcount
      driver core: platform: provide platform_device_set_of_node()
      driver core: platform: provide platform_device_set_fwnode()
      driver core: platform: provide platform_device_set_of_node_from_dev()
      of: platform: use platform_device_set_of_node()
      powerpc/powermac: use platform_device_set_of_node()
      i2c: pxa-pci: use platform_device_set_of_node()
      iommu/fsl: use platform_device_set_of_node()
      net: bcmgenet: use platform_device_set_of_node()
      pmdomain: imx: use platform_device_set_of_node()
      mfd: tps6586: use platform_device_set_of_node()
      slimbus: qcom-ngd-ctrl: use platform_device_set_of_node()
      net: mv643xx: use platform_device_set_of_node()
      drm/xe/i2c: use platform_device_set_fwnode()
      platform/surface: gpe: use platform_device_set_fwnode()
      usb: chipidea: use platform_device_set_of_node_from_dev()
      usb: musb: use platform_device_set_of_node_from_dev()
      reset: rzg2l: use platform_device_set_of_node_from_dev()
      driver core: platform: count references to all kinds of firmware nodes

 arch/powerpc/platforms/powermac/low_i2c.c    |  2 +-
 drivers/base/platform.c                      | 56 ++++++++++++++++++++++++++--
 drivers/gpu/drm/xe/xe_i2c.c                  |  2 +-
 drivers/i2c/busses/i2c-pxa-pci.c             |  3 +-
 drivers/iommu/fsl_pamu.c                     |  7 ++--
 drivers/mfd/tps6586x.c                       |  2 +-
 drivers/net/ethernet/broadcom/genet/bcmmii.c | 10 +++--
 drivers/net/ethernet/marvell/mv643xx_eth.c   |  2 +-
 drivers/of/platform.c                        |  2 +-
 drivers/platform/surface/surface_gpe.c       |  2 +-
 drivers/pmdomain/imx/gpc.c                   |  3 +-
 drivers/reset/reset-rzg2l-usbphy-ctrl.c      |  2 +-
 drivers/slimbus/qcom-ngd-ctrl.c              |  2 +-
 drivers/usb/chipidea/core.c                  |  2 +-
 drivers/usb/musb/jz4740.c                    |  2 +-
 include/linux/platform_device.h              |  8 ++++
 16 files changed, 84 insertions(+), 23 deletions(-)
---
base-commit: dc59e4fea9d83f03bad6bddf3fa2e52491777482
change-id: 20260520-pdev-fwnode-ref-d867836971eb

Best regards,
-- 
Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>



^ permalink raw reply

* [PATCH v2 06/19] powerpc/powermac: use platform_device_set_of_node()
From: Bartosz Golaszewski @ 2026-06-29  9:12 UTC (permalink / raw)
  To: Lee Jones, Mark Brown, Thierry Reding, Sebastian Hesselbarth,
	Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Srinivas Kandagatla, Greg Kroah-Hartman, Vinod Koul,
	Rafael J. Wysocki, Danilo Krummrich, Rob Herring, Saravana Kannan,
	Madhavan Srinivasan, Michael Ellerman, Nicholas Piggin,
	Christophe Leroy (CS GROUP), Andi Shyti, Andy Shevchenko,
	Joerg Roedel, Will Deacon, Robin Murphy, Doug Berger,
	Florian Fainelli, Broadcom internal kernel review list,
	Ulf Hansson, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Matthew Brost, Thomas Hellström, Rodrigo Vivi,
	David Airlie, Simona Vetter, Peter Chen, Paul Cercueil, Bin Liu,
	Philipp Zabel, Maximilian Luz, Hans de Goede, Ilpo Järvinen,
	Krzysztof Kozlowski, Benjamin Herrenschmidt
  Cc: brgl, linux-kernel, netdev, linux-arm-msm, linux-sound,
	driver-core, devicetree, linuxppc-dev, linux-i2c, iommu, linux-pm,
	imx, linux-arm-kernel, intel-xe, dri-devel, linux-usb, linux-mips,
	platform-driver-x86, Bartosz Golaszewski
In-Reply-To: <20260629-pdev-fwnode-ref-v2-0-8abe2513f96e@oss.qualcomm.com>

Ahead of reworking the reference counting logic for platform devices,
encapsulate the assignment of the OF node for dynamically allocated
platform devices with the provided helper.

Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
---
 arch/powerpc/platforms/powermac/low_i2c.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/powerpc/platforms/powermac/low_i2c.c b/arch/powerpc/platforms/powermac/low_i2c.c
index 973f58771d9636605ed5d3e91b45008543b584d3..a175a32a222bab4cc7400f6ab6071f5630db2cb8 100644
--- a/arch/powerpc/platforms/powermac/low_i2c.c
+++ b/arch/powerpc/platforms/powermac/low_i2c.c
@@ -1471,7 +1471,7 @@ static int __init pmac_i2c_create_platform_devices(void)
 		if (bus->platform_dev == NULL)
 			return -ENOMEM;
 		bus->platform_dev->dev.platform_data = bus;
-		bus->platform_dev->dev.of_node = of_node_get(bus->busnode);
+		platform_device_set_of_node(bus->platform_dev, bus->busnode);
 		platform_device_add(bus->platform_dev);
 	}
 

-- 
2.47.3



^ permalink raw reply related

* [PATCH v2 11/19] mfd: tps6586: use platform_device_set_of_node()
From: Bartosz Golaszewski @ 2026-06-29  9:12 UTC (permalink / raw)
  To: Lee Jones, Mark Brown, Thierry Reding, Sebastian Hesselbarth,
	Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Srinivas Kandagatla, Greg Kroah-Hartman, Vinod Koul,
	Rafael J. Wysocki, Danilo Krummrich, Rob Herring, Saravana Kannan,
	Madhavan Srinivasan, Michael Ellerman, Nicholas Piggin,
	Christophe Leroy (CS GROUP), Andi Shyti, Andy Shevchenko,
	Joerg Roedel, Will Deacon, Robin Murphy, Doug Berger,
	Florian Fainelli, Broadcom internal kernel review list,
	Ulf Hansson, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Matthew Brost, Thomas Hellström, Rodrigo Vivi,
	David Airlie, Simona Vetter, Peter Chen, Paul Cercueil, Bin Liu,
	Philipp Zabel, Maximilian Luz, Hans de Goede, Ilpo Järvinen,
	Krzysztof Kozlowski, Benjamin Herrenschmidt
  Cc: brgl, linux-kernel, netdev, linux-arm-msm, linux-sound,
	driver-core, devicetree, linuxppc-dev, linux-i2c, iommu, linux-pm,
	imx, linux-arm-kernel, intel-xe, dri-devel, linux-usb, linux-mips,
	platform-driver-x86, Bartosz Golaszewski
In-Reply-To: <20260629-pdev-fwnode-ref-v2-0-8abe2513f96e@oss.qualcomm.com>

Ahead of reworking the reference counting logic for platform devices,
encapsulate the assignment of the OF node for dynamically allocated
platform devices with the provided helper.

Acked-by: Lee Jones <lee@kernel.org>
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
---
 drivers/mfd/tps6586x.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/mfd/tps6586x.c b/drivers/mfd/tps6586x.c
index f5f805446603315ba76ce1fc501c908f1cec0d16..3cfd2f02b62f3cc370e0d970ec2643b638fd0fec 100644
--- a/drivers/mfd/tps6586x.c
+++ b/drivers/mfd/tps6586x.c
@@ -397,7 +397,7 @@ static int tps6586x_add_subdevs(struct tps6586x *tps6586x,
 
 		pdev->dev.parent = tps6586x->dev;
 		pdev->dev.platform_data = subdev->platform_data;
-		pdev->dev.of_node = of_node_get(subdev->of_node);
+		platform_device_set_of_node(pdev, subdev->of_node);
 
 		ret = platform_device_add(pdev);
 		if (ret) {

-- 
2.47.3



^ permalink raw reply related

* [PATCH v2 01/19] powerpc/powermac: fix OF node refcount
From: Bartosz Golaszewski @ 2026-06-29  9:12 UTC (permalink / raw)
  To: Lee Jones, Mark Brown, Thierry Reding, Sebastian Hesselbarth,
	Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Srinivas Kandagatla, Greg Kroah-Hartman, Vinod Koul,
	Rafael J. Wysocki, Danilo Krummrich, Rob Herring, Saravana Kannan,
	Madhavan Srinivasan, Michael Ellerman, Nicholas Piggin,
	Christophe Leroy (CS GROUP), Andi Shyti, Andy Shevchenko,
	Joerg Roedel, Will Deacon, Robin Murphy, Doug Berger,
	Florian Fainelli, Broadcom internal kernel review list,
	Ulf Hansson, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Matthew Brost, Thomas Hellström, Rodrigo Vivi,
	David Airlie, Simona Vetter, Peter Chen, Paul Cercueil, Bin Liu,
	Philipp Zabel, Maximilian Luz, Hans de Goede, Ilpo Järvinen,
	Krzysztof Kozlowski, Benjamin Herrenschmidt
  Cc: brgl, linux-kernel, netdev, linux-arm-msm, linux-sound,
	driver-core, devicetree, linuxppc-dev, linux-i2c, iommu, linux-pm,
	imx, linux-arm-kernel, intel-xe, dri-devel, linux-usb, linux-mips,
	platform-driver-x86, Bartosz Golaszewski, stable
In-Reply-To: <20260629-pdev-fwnode-ref-v2-0-8abe2513f96e@oss.qualcomm.com>

Platform devices created with platform_device_alloc() call
platform_device_release() when the last reference to the device's
kobject is dropped. This function calls of_node_put() unconditionally.
This works fine for devices created with platform_device_register_full()
but users of the split approach (platform_device_alloc() +
platform_device_add()) must bump the reference of the of_node they
assign manually. Add the missing call to of_node_get().

Cc: stable@vger.kernel.org
Fixes: 81e5d8646ff6 ("i2c/powermac: Register i2c devices from device-tree")
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
---
 arch/powerpc/platforms/powermac/low_i2c.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/powerpc/platforms/powermac/low_i2c.c b/arch/powerpc/platforms/powermac/low_i2c.c
index da72a30ab8657e6dc7e6f3437af612155783d8f9..973f58771d9636605ed5d3e91b45008543b584d3 100644
--- a/arch/powerpc/platforms/powermac/low_i2c.c
+++ b/arch/powerpc/platforms/powermac/low_i2c.c
@@ -1471,7 +1471,7 @@ static int __init pmac_i2c_create_platform_devices(void)
 		if (bus->platform_dev == NULL)
 			return -ENOMEM;
 		bus->platform_dev->dev.platform_data = bus;
-		bus->platform_dev->dev.of_node = bus->busnode;
+		bus->platform_dev->dev.of_node = of_node_get(bus->busnode);
 		platform_device_add(bus->platform_dev);
 	}
 

-- 
2.47.3



^ permalink raw reply related

* [PATCH v2 02/19] driver core: platform: provide platform_device_set_of_node()
From: Bartosz Golaszewski @ 2026-06-29  9:12 UTC (permalink / raw)
  To: Lee Jones, Mark Brown, Thierry Reding, Sebastian Hesselbarth,
	Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Srinivas Kandagatla, Greg Kroah-Hartman, Vinod Koul,
	Rafael J. Wysocki, Danilo Krummrich, Rob Herring, Saravana Kannan,
	Madhavan Srinivasan, Michael Ellerman, Nicholas Piggin,
	Christophe Leroy (CS GROUP), Andi Shyti, Andy Shevchenko,
	Joerg Roedel, Will Deacon, Robin Murphy, Doug Berger,
	Florian Fainelli, Broadcom internal kernel review list,
	Ulf Hansson, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Matthew Brost, Thomas Hellström, Rodrigo Vivi,
	David Airlie, Simona Vetter, Peter Chen, Paul Cercueil, Bin Liu,
	Philipp Zabel, Maximilian Luz, Hans de Goede, Ilpo Järvinen,
	Krzysztof Kozlowski, Benjamin Herrenschmidt
  Cc: brgl, linux-kernel, netdev, linux-arm-msm, linux-sound,
	driver-core, devicetree, linuxppc-dev, linux-i2c, iommu, linux-pm,
	imx, linux-arm-kernel, intel-xe, dri-devel, linux-usb, linux-mips,
	platform-driver-x86, Bartosz Golaszewski
In-Reply-To: <20260629-pdev-fwnode-ref-v2-0-8abe2513f96e@oss.qualcomm.com>

Encapsulate the reference counting logic for OF nodes assigned to
platform devices created with platform_device_alloc() in a helper
function. Make the kerneldoc state that this is the proper interface for
assigning OF nodes to dynamically allocated platform devices. This will
allow us to switch to counting the references of the device's firmware
nodes, not only the OF nodes.

Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
---
 drivers/base/platform.c         | 18 ++++++++++++++++++
 include/linux/platform_device.h |  3 +++
 2 files changed, 21 insertions(+)

diff --git a/drivers/base/platform.c b/drivers/base/platform.c
index fb9120b0bcfe0e7dd9dfc0d29b91e0ad40a01440..3188d5aba5f90622f821c695049cacda030204fb 100644
--- a/drivers/base/platform.c
+++ b/drivers/base/platform.c
@@ -693,6 +693,24 @@ int platform_device_add_data(struct platform_device *pdev, const void *data,
 }
 EXPORT_SYMBOL_GPL(platform_device_add_data);
 
+/**
+ * platform_device_set_of_node - assign an OF node to device
+ * @pdev: platform device to add the node for
+ * @np: new device node
+ *
+ * Assign an OF node to this platform device. Internally keep track of the
+ * reference count. Devices created with platform_device_alloc() must use this
+ * function instead of assigning the node manually.
+ */
+void platform_device_set_of_node(struct platform_device *pdev,
+				 struct device_node *np)
+{
+	of_node_put(pdev->dev.of_node);
+	pdev->dev.of_node = of_node_get(np);
+	pdev->dev.fwnode = of_fwnode_handle(np);
+}
+EXPORT_SYMBOL_GPL(platform_device_set_of_node);
+
 /**
  * platform_device_add - add a platform device to device hierarchy
  * @pdev: platform device we're adding
diff --git a/include/linux/platform_device.h b/include/linux/platform_device.h
index 26e6a43358e25cce2e2c38245dc0f0fc43923bf5..870d168aeff8558749eae71723e657ab150ce0be 100644
--- a/include/linux/platform_device.h
+++ b/include/linux/platform_device.h
@@ -19,6 +19,7 @@ struct irq_affinity;
 struct mfd_cell;
 struct property_entry;
 struct platform_device_id;
+struct device_node;
 
 struct platform_device {
 	const char	*name;
@@ -262,6 +263,8 @@ extern int platform_device_add_resources(struct platform_device *pdev,
 					 unsigned int num);
 extern int platform_device_add_data(struct platform_device *pdev,
 				    const void *data, size_t size);
+void platform_device_set_of_node(struct platform_device *pdev,
+				 struct device_node *np);
 extern int platform_device_add(struct platform_device *pdev);
 extern void platform_device_del(struct platform_device *pdev);
 extern void platform_device_put(struct platform_device *pdev);

-- 
2.47.3



^ permalink raw reply related

* [PATCH v2 05/19] of: platform: use platform_device_set_of_node()
From: Bartosz Golaszewski @ 2026-06-29  9:12 UTC (permalink / raw)
  To: Lee Jones, Mark Brown, Thierry Reding, Sebastian Hesselbarth,
	Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Srinivas Kandagatla, Greg Kroah-Hartman, Vinod Koul,
	Rafael J. Wysocki, Danilo Krummrich, Rob Herring, Saravana Kannan,
	Madhavan Srinivasan, Michael Ellerman, Nicholas Piggin,
	Christophe Leroy (CS GROUP), Andi Shyti, Andy Shevchenko,
	Joerg Roedel, Will Deacon, Robin Murphy, Doug Berger,
	Florian Fainelli, Broadcom internal kernel review list,
	Ulf Hansson, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Matthew Brost, Thomas Hellström, Rodrigo Vivi,
	David Airlie, Simona Vetter, Peter Chen, Paul Cercueil, Bin Liu,
	Philipp Zabel, Maximilian Luz, Hans de Goede, Ilpo Järvinen,
	Krzysztof Kozlowski, Benjamin Herrenschmidt
  Cc: brgl, linux-kernel, netdev, linux-arm-msm, linux-sound,
	driver-core, devicetree, linuxppc-dev, linux-i2c, iommu, linux-pm,
	imx, linux-arm-kernel, intel-xe, dri-devel, linux-usb, linux-mips,
	platform-driver-x86, Bartosz Golaszewski
In-Reply-To: <20260629-pdev-fwnode-ref-v2-0-8abe2513f96e@oss.qualcomm.com>

Ahead of reworking the reference counting logic for platform devices,
encapsulate the assignment of the OF node for dynamically allocated
platform devices with the provided helper.

Acked-by: Rob Herring (Arm) <robh@kernel.org>
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
---
 drivers/of/platform.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/of/platform.c b/drivers/of/platform.c
index 53bca8c6f7810ce2235b4d084a361626b1d2da33..8b1e7640778253492c080085b4015e1423389d83 100644
--- a/drivers/of/platform.c
+++ b/drivers/of/platform.c
@@ -126,7 +126,7 @@ struct platform_device *of_device_alloc(struct device_node *np,
 	}
 
 	/* setup generic device info */
-	device_set_node(&dev->dev, of_fwnode_handle(of_node_get(np)));
+	platform_device_set_of_node(dev, np);
 	dev->dev.parent = parent ? : &platform_bus;
 
 	if (bus_id)

-- 
2.47.3



^ permalink raw reply related

* [PATCH v2 19/19] driver core: platform: count references to all kinds of firmware nodes
From: Bartosz Golaszewski @ 2026-06-29  9:12 UTC (permalink / raw)
  To: Lee Jones, Mark Brown, Thierry Reding, Sebastian Hesselbarth,
	Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Srinivas Kandagatla, Greg Kroah-Hartman, Vinod Koul,
	Rafael J. Wysocki, Danilo Krummrich, Rob Herring, Saravana Kannan,
	Madhavan Srinivasan, Michael Ellerman, Nicholas Piggin,
	Christophe Leroy (CS GROUP), Andi Shyti, Andy Shevchenko,
	Joerg Roedel, Will Deacon, Robin Murphy, Doug Berger,
	Florian Fainelli, Broadcom internal kernel review list,
	Ulf Hansson, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Matthew Brost, Thomas Hellström, Rodrigo Vivi,
	David Airlie, Simona Vetter, Peter Chen, Paul Cercueil, Bin Liu,
	Philipp Zabel, Maximilian Luz, Hans de Goede, Ilpo Järvinen,
	Krzysztof Kozlowski, Benjamin Herrenschmidt
  Cc: brgl, linux-kernel, netdev, linux-arm-msm, linux-sound,
	driver-core, devicetree, linuxppc-dev, linux-i2c, iommu, linux-pm,
	imx, linux-arm-kernel, intel-xe, dri-devel, linux-usb, linux-mips,
	platform-driver-x86, Bartosz Golaszewski
In-Reply-To: <20260629-pdev-fwnode-ref-v2-0-8abe2513f96e@oss.qualcomm.com>

When using platform_device_register_full(), we currently only increase
the reference count of the OF node associated with a platform device. We
symmetrically decrease it in platform_device_release(). With all users in
tree now converted to using provided platform device helpers for
assigning OF and firmware nodes, we can now switch to counting references
of all kinds of firmware nodes.

Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
---
 drivers/base/platform.c | 17 +++++++----------
 1 file changed, 7 insertions(+), 10 deletions(-)

diff --git a/drivers/base/platform.c b/drivers/base/platform.c
index f24a5f406746b53ca9eaab9472f6dd1345e04ad6..bb5f5bddd047d4ec6f238e36dfe4f4ea36b92a76 100644
--- a/drivers/base/platform.c
+++ b/drivers/base/platform.c
@@ -599,7 +599,7 @@ static void platform_device_release(struct device *dev)
 	struct platform_object *pa = container_of(dev, struct platform_object,
 						  pdev.dev);
 
-	of_node_put(pa->pdev.dev.of_node);
+	fwnode_handle_put(pa->pdev.dev.fwnode);
 	kfree(pa->pdev.dev.platform_data);
 	kfree(pa->pdev.mfd_cell);
 	kfree(pa->pdev.resource);
@@ -705,9 +705,7 @@ EXPORT_SYMBOL_GPL(platform_device_add_data);
 void platform_device_set_of_node(struct platform_device *pdev,
 				 struct device_node *np)
 {
-	of_node_put(pdev->dev.of_node);
-	pdev->dev.of_node = of_node_get(np);
-	pdev->dev.fwnode = of_fwnode_handle(np);
+	platform_device_set_fwnode(pdev, of_fwnode_handle(np));
 }
 EXPORT_SYMBOL_GPL(platform_device_set_of_node);
 
@@ -723,10 +721,9 @@ EXPORT_SYMBOL_GPL(platform_device_set_of_node);
 void platform_device_set_fwnode(struct platform_device *pdev,
 				struct fwnode_handle *fwnode)
 {
-	if (is_of_node(fwnode))
-		platform_device_set_of_node(pdev, to_of_node(fwnode));
-	else
-		pdev->dev.fwnode = fwnode;
+	fwnode_handle_put(pdev->dev.fwnode);
+	pdev->dev.fwnode = fwnode_handle_get(fwnode);
+	pdev->dev.of_node = to_of_node(fwnode);
 }
 EXPORT_SYMBOL_GPL(platform_device_set_fwnode);
 
@@ -921,8 +918,8 @@ struct platform_device *platform_device_register_full(const struct platform_devi
 		return ERR_PTR(-ENOMEM);
 
 	pdev->dev.parent = pdevinfo->parent;
-	pdev->dev.fwnode = pdevinfo->fwnode;
-	pdev->dev.of_node = of_node_get(to_of_node(pdev->dev.fwnode));
+	pdev->dev.fwnode = fwnode_handle_get(pdevinfo->fwnode);
+	pdev->dev.of_node = to_of_node(pdev->dev.fwnode);
 	dev_assign_of_node_reused(&pdev->dev, pdevinfo->of_node_reused);
 
 	if (pdevinfo->dma_mask) {

-- 
2.47.3



^ permalink raw reply related

* [PATCH v2 08/19] iommu/fsl: use platform_device_set_of_node()
From: Bartosz Golaszewski @ 2026-06-29  9:12 UTC (permalink / raw)
  To: Lee Jones, Mark Brown, Thierry Reding, Sebastian Hesselbarth,
	Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Srinivas Kandagatla, Greg Kroah-Hartman, Vinod Koul,
	Rafael J. Wysocki, Danilo Krummrich, Rob Herring, Saravana Kannan,
	Madhavan Srinivasan, Michael Ellerman, Nicholas Piggin,
	Christophe Leroy (CS GROUP), Andi Shyti, Andy Shevchenko,
	Joerg Roedel, Will Deacon, Robin Murphy, Doug Berger,
	Florian Fainelli, Broadcom internal kernel review list,
	Ulf Hansson, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Matthew Brost, Thomas Hellström, Rodrigo Vivi,
	David Airlie, Simona Vetter, Peter Chen, Paul Cercueil, Bin Liu,
	Philipp Zabel, Maximilian Luz, Hans de Goede, Ilpo Järvinen,
	Krzysztof Kozlowski, Benjamin Herrenschmidt
  Cc: brgl, linux-kernel, netdev, linux-arm-msm, linux-sound,
	driver-core, devicetree, linuxppc-dev, linux-i2c, iommu, linux-pm,
	imx, linux-arm-kernel, intel-xe, dri-devel, linux-usb, linux-mips,
	platform-driver-x86, Bartosz Golaszewski
In-Reply-To: <20260629-pdev-fwnode-ref-v2-0-8abe2513f96e@oss.qualcomm.com>

Ahead of reworking the reference counting logic for platform devices,
encapsulate the assignment of the OF node for dynamically allocated
platform devices with the provided helper.

Acked-by: Robin Murphy <robin.murphy@arm.com>
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
---
 drivers/iommu/fsl_pamu.c | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/drivers/iommu/fsl_pamu.c b/drivers/iommu/fsl_pamu.c
index 25aa477a95a95cb4fa4e132727cde0a936750ee2..012839fa0d8a27cafc6a441373f4f6da794388c1 100644
--- a/drivers/iommu/fsl_pamu.c
+++ b/drivers/iommu/fsl_pamu.c
@@ -973,7 +973,8 @@ static __init int fsl_pamu_init(void)
 		ret = -ENOMEM;
 		goto error_device_alloc;
 	}
-	pdev->dev.of_node = of_node_get(np);
+
+	platform_device_set_of_node(pdev, np);
 
 	ret = pamu_domain_init();
 	if (ret)
@@ -985,12 +986,10 @@ static __init int fsl_pamu_init(void)
 		goto error_device_add;
 	}
 
+	of_node_put(np);
 	return 0;
 
 error_device_add:
-	of_node_put(pdev->dev.of_node);
-	pdev->dev.of_node = NULL;
-
 	platform_device_put(pdev);
 
 error_device_alloc:

-- 
2.47.3



^ permalink raw reply related

* [PATCH v2 12/19] slimbus: qcom-ngd-ctrl: use platform_device_set_of_node()
From: Bartosz Golaszewski @ 2026-06-29  9:12 UTC (permalink / raw)
  To: Lee Jones, Mark Brown, Thierry Reding, Sebastian Hesselbarth,
	Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Srinivas Kandagatla, Greg Kroah-Hartman, Vinod Koul,
	Rafael J. Wysocki, Danilo Krummrich, Rob Herring, Saravana Kannan,
	Madhavan Srinivasan, Michael Ellerman, Nicholas Piggin,
	Christophe Leroy (CS GROUP), Andi Shyti, Andy Shevchenko,
	Joerg Roedel, Will Deacon, Robin Murphy, Doug Berger,
	Florian Fainelli, Broadcom internal kernel review list,
	Ulf Hansson, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Matthew Brost, Thomas Hellström, Rodrigo Vivi,
	David Airlie, Simona Vetter, Peter Chen, Paul Cercueil, Bin Liu,
	Philipp Zabel, Maximilian Luz, Hans de Goede, Ilpo Järvinen,
	Krzysztof Kozlowski, Benjamin Herrenschmidt
  Cc: brgl, linux-kernel, netdev, linux-arm-msm, linux-sound,
	driver-core, devicetree, linuxppc-dev, linux-i2c, iommu, linux-pm,
	imx, linux-arm-kernel, intel-xe, dri-devel, linux-usb, linux-mips,
	platform-driver-x86, Bartosz Golaszewski
In-Reply-To: <20260629-pdev-fwnode-ref-v2-0-8abe2513f96e@oss.qualcomm.com>

Ahead of reworking the reference counting logic for platform devices,
encapsulate the assignment of the OF node for dynamically allocated
platform devices with the provided helper.

Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
---
 drivers/slimbus/qcom-ngd-ctrl.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/slimbus/qcom-ngd-ctrl.c b/drivers/slimbus/qcom-ngd-ctrl.c
index 3071e46d03beaaae2321e858c564b512c22e820e..6e89415712ce1a7a7781af6bd0eaf0d6879da52b 100644
--- a/drivers/slimbus/qcom-ngd-ctrl.c
+++ b/drivers/slimbus/qcom-ngd-ctrl.c
@@ -1539,7 +1539,7 @@ static int of_qcom_slim_ngd_register(struct device *parent,
 			kfree(ngd);
 			return ret;
 		}
-		ngd->pdev->dev.of_node = of_node_get(node);
+		platform_device_set_of_node(ngd->pdev, node);
 		ctrl->ngd = ngd;
 
 		ret = platform_device_add(ngd->pdev);

-- 
2.47.3



^ permalink raw reply related

* [PATCH v2 18/19] reset: rzg2l: use platform_device_set_of_node_from_dev()
From: Bartosz Golaszewski @ 2026-06-29  9:12 UTC (permalink / raw)
  To: Lee Jones, Mark Brown, Thierry Reding, Sebastian Hesselbarth,
	Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Srinivas Kandagatla, Greg Kroah-Hartman, Vinod Koul,
	Rafael J. Wysocki, Danilo Krummrich, Rob Herring, Saravana Kannan,
	Madhavan Srinivasan, Michael Ellerman, Nicholas Piggin,
	Christophe Leroy (CS GROUP), Andi Shyti, Andy Shevchenko,
	Joerg Roedel, Will Deacon, Robin Murphy, Doug Berger,
	Florian Fainelli, Broadcom internal kernel review list,
	Ulf Hansson, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Matthew Brost, Thomas Hellström, Rodrigo Vivi,
	David Airlie, Simona Vetter, Peter Chen, Paul Cercueil, Bin Liu,
	Philipp Zabel, Maximilian Luz, Hans de Goede, Ilpo Järvinen,
	Krzysztof Kozlowski, Benjamin Herrenschmidt
  Cc: brgl, linux-kernel, netdev, linux-arm-msm, linux-sound,
	driver-core, devicetree, linuxppc-dev, linux-i2c, iommu, linux-pm,
	imx, linux-arm-kernel, intel-xe, dri-devel, linux-usb, linux-mips,
	platform-driver-x86, Bartosz Golaszewski
In-Reply-To: <20260629-pdev-fwnode-ref-v2-0-8abe2513f96e@oss.qualcomm.com>

Ahead of reworking the reference counting logic for platform devices,
encapsulate the assignment of the OF node from another device for
dynamically allocated platform devices with the provided helper.

Acked-by: Philipp Zabel <p.zabel@pengutronix.de>
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
---
 drivers/reset/reset-rzg2l-usbphy-ctrl.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/reset/reset-rzg2l-usbphy-ctrl.c b/drivers/reset/reset-rzg2l-usbphy-ctrl.c
index fd75d9601a3bfde7b7e3f6db287ec8c5c45a20ab..f003b360629c90bb37ed0ade7a675b5b0f28fa7e 100644
--- a/drivers/reset/reset-rzg2l-usbphy-ctrl.c
+++ b/drivers/reset/reset-rzg2l-usbphy-ctrl.c
@@ -249,7 +249,7 @@ static int rzg2l_usbphy_ctrl_probe(struct platform_device *pdev)
 	vdev->dev.parent = dev;
 	priv->vdev = vdev;
 
-	device_set_of_node_from_dev(&vdev->dev, dev);
+	platform_device_set_of_node_from_dev(vdev, dev);
 	error = platform_device_add(vdev);
 	if (error)
 		goto err_device_put;

-- 
2.47.3



^ permalink raw reply related

* Re: [PATCH V16 4/7] rust/powerpc: Set min rustc version for powerpc
From: Miguel Ojeda @ 2026-06-29  9:11 UTC (permalink / raw)
  To: Mukesh Kumar Chaurasiya
  Cc: maddy, mpe, npiggin, chleroy, peterz, jpoimboe, jbaron, aliceryhl,
	rostedt, ardb, ojeda, boqun, gary, bjorn3_gh, lossin, a.hindborg,
	tmgross, dakr, nathan, nick.desaulniers+lkml, morbo, justinstitt,
	daniel.almeida, acourbot, fujita.tomonori, gregkh, prafulrai522,
	tamird, kees, lyude, airlied, linuxppc-dev, linux-kernel,
	rust-for-linux, llvm
In-Reply-To: <akH6yuDH9ZwXVoVA@li-1a3e774c-28e4-11b2-a85c-acc9f2883e29.ibm.com>

On Mon, Jun 29, 2026 at 6:57 AM Mukesh Kumar Chaurasiya
<mkchauras@gmail.com> wrote:
>
> Sure i'll bisect. Meanwhile i would still like to get this upstream.

It is very early on the cycle -- I would suggest bisecting first.

But it is up to ppc, of course.

Cheers,
Miguel


^ permalink raw reply

* Re: [PATCH 21/23] usb: musb: use platform_device_set_of_node_from_dev()
From: Bartosz Golaszewski @ 2026-06-29  9:06 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: Lee Jones, Mark Brown, Thierry Reding, Sebastian Hesselbarth,
	Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Srinivas Kandagatla, Greg Kroah-Hartman, Vinod Koul,
	Rafael J. Wysocki, Danilo Krummrich, Rob Herring, Saravana Kannan,
	Madhavan Srinivasan, Michael Ellerman, Nicholas Piggin,
	Christophe Leroy (CS GROUP), Andi Shyti, Joerg Roedel,
	Will Deacon, Robin Murphy, Doug Berger, Florian Fainelli,
	Broadcom internal kernel review list, Ulf Hansson, Frank Li,
	Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
	Matthew Brost, Thomas Hellström, Rodrigo Vivi, David Airlie,
	Simona Vetter, Peter Chen, Paul Cercueil, Bin Liu, Philipp Zabel,
	Maximilian Luz, Hans de Goede, Ilpo Järvinen,
	Krzysztof Kozlowski, Benjamin Herrenschmidt, brgl, linux-kernel,
	netdev, linux-arm-msm, linux-sound, driver-core, devicetree,
	linuxppc-dev, linux-i2c, iommu, linux-pm, imx, linux-arm-kernel,
	intel-xe, dri-devel, linux-usb, linux-mips, platform-driver-x86,
	Bartosz Golaszewski
In-Reply-To: <ah9QPgco9BxEix7T@ashevche-desk.local>

On Tue, 2 Jun 2026 23:50:54 +0200, Andy Shevchenko
<andriy.shevchenko@linux.intel.com> said:
> On Thu, May 21, 2026 at 10:36:44AM +0200, Bartosz Golaszewski wrote:
>> Ahead of reworking the reference counting logic for platform devices,
>> encapsulate the assignment of the OF node from another device for
>> dynamically allocated platform devices with the provided helper.
>
> Same Q as per chipidea. Do they provide a pin control facility at the same time?
>
> Otherwise simple device_set_node() or whatever platform analogue should suffice.
>

Hi!

I don't know and I can't test it so let me respin the series as is. This can
always be amended separately later.

Bart


^ permalink raw reply

* Re: [PATCH v6 00/20] dma-mapping: Use DMA_ATTR_CC_SHARED through direct, pool and swiotlb paths
From: Aneesh Kumar K.V @ 2026-06-29  6:46 UTC (permalink / raw)
  To: Jason Gunthorpe
  Cc: Alexey Kardashevskiy, Catalin Marinas, iommu, linux-arm-kernel,
	linux-kernel, linux-coco, Robin Murphy, Marek Szyprowski,
	Will Deacon, Marc Zyngier, Steven Price, Suzuki K Poulose,
	Jiri Pirko, Mostafa Saleh, Petr Tesarik, Dan Williams, Xu Yilun,
	linuxppc-dev, linux-s390, Madhavan Srinivasan, Michael Ellerman,
	Nicholas Piggin, Christophe Leroy (CS GROUP), Alexander Gordeev,
	Gerald Schaefer, Heiko Carstens, Vasily Gorbik,
	Christian Borntraeger, Sven Schnelle, x86
In-Reply-To: <20260619140616.GB1068655@ziepe.ca>

Jason Gunthorpe <jgg@ziepe.ca> writes:

> On Fri, Jun 19, 2026 at 02:36:19PM +0100, Aneesh Kumar K.V wrote:
>> >> Agreed. If the device can do encrypted DMA and requires bouncing, it
>> >> should bounce through encrypted pools. We don't support encrypted pools
>> >> now and that means, we mark the option ("mem_encrypt=on iommu=pt
>> >> swiotlb=force") not supported for now? 
>> >
>> > ?? if you don't have a CC system then the swiotlb is "encrypted"
>> > meaning ordinary struct page system memory.
>> >
>> > The hypervisor should not be triggering any CC special stuff here, it
>> > is not a CC guest.
>> >
>> > Agree we don't need to worry about swiotlb=force with a trusted device
>> > in the GUEST for now, but it should be something to fix eventually.
>> >
>> 
>> If i understand this correctly, the setup Alexey is referring to here is
>> bare metal system with memory encryption enabled and dma address doesn't
>> need C bit cleared because it is handled in iommu.
>
> This is how I understand it too, if the iommu is turned on then it can
> take the high PA with the C bit set and map it to an IOVA that matches
> the device's dma limit.
>
>> ( I consider this as memory encryption that is handled
>> transparently, device can access any address because that encryption
>> details are now managed by iommu).
>
> Compared to the guest side there are some important host side differences:
>
>  - On the host the iommu can fix it because this is only a matter of
>    IOVA range not access control. On a guest even a IOMMU cannot
>    permit access to private memory
>  - On the host the state of the device is driven by the dma limit
>    which is not set until after the driver probes. On guest the state is
>    set by the tsm and device security level before the driver
>    probes
>  - Both flows end up using pgprot_decrypted and set_memory_decrypted()
>    to create their special pools, but for completely different
>    reasons.
>  - The memory coming from the special swiotlb pool must NOT be used by
>    a trusted device on a CC guest, while there is no problem for any
>    device to use it on the host.
>

Agreed.

>> Thinking about this more, I guess we should mark the swiotlb as
>> cc_shared only with  CC_ATTR_GUEST_MEM_ENCRYPT instead of
>> CC_ATTR_MEM_ENCRYPT as we have below.
>
> The name cc_shared should be used for GUEST scenarios only.
>
> I guess there is some merit in keeping swiotlb using "decrypted" to
> mean it usinig pgprot_decrypted and set_memory_decyped() which AMD
> gives meaning to on both host and guest.
>

Are you suggesting to change the struct io_tlb_mem::cc_shared back to
struct io_tlb_mem::unencrypted?. If we want to split cc_shared and
unencrypted as two flags, I think we will add quiet a lot of code
duplication.

> IDK what AMD should do on the host by default. I guess it should setup
> a swiotlb pool of low dma addrs "unencrypted", but not "cc_shared"?
>

If by low DMA address you mean using an address with the C-bit
cleared. Currently the SME code uses force_dma_unencrypted() as the hook to
determine whether the C-bit needs to be cleared. Therefore,
force_dma_unencrypted(dev) must be true to use such a pool.

The current code already does this and uses the swiotlb pool correctly
on SME. The challenge arises when we want to force SWIOTLB
bouncing even for devices that can handle encrypted DMA addresses (more
on that below). For such a config force_dma_uencrypted(dev) will return
false and swiotlb will be marked cc_shared/decrypted = true; This trip
the new check we added.

	/* swiotlb pool is incorrect for this device */
	if (unlikely(mem->cc_shared != force_dma_unencrypted(dev)))
		return (phys_addr_t)DMA_MAPPING_ERROR;

We can also do

	if (cc_platform_has(CC_ATTR_GUEST_MEM_ENCRYPT)) {
		/* swiotlb pool is incorrect for this device */
		if (unlikely(mem->cc_shared != force_dma_unencrypted(dev)))
			return (phys_addr_t)DMA_MAPPING_ERROR;

		/* Force attrs to match the kind of memory in the pool */
		if (mem->cc_shared)
			*attrs |= DMA_ATTR_CC_SHARED;
		else
			*attrs &= ~DMA_ATTR_CC_SHARED;
	} else {
		/*
		 * Host memory encryption where device requires an
		 * unencrypted dma_addr_t due to dma mask limit
    		 */
		if (force_dma_unencrypted(dev))
			*attrs |= DMA_ATTR_CC_SHARED;
		else
			*attrs &= ~DMA_ATTR_CC_SHARED;
	}


Here I see value in having DMA_ATTR_UNENCRYPTED. The question is do we
need to split this into two flags and introduce the resulting code
duplication.

>
> But if we are operating on the host then this pool is not limited to
> only T=0 devices, every device can "safely" use it. (ignoring this
> destroys the security memory encryption on bare metal was supposed to
> provide)
>
>> Now we have the case of host memory encryption where the C-bit needs to
>> be cleared in dma_addr_t. That requires special handling in the kernel, and
>> I believe we need to mark swiotlb as unencrypted in this configuration.
>
> I think we need to split the two things up, they have different
> behaviors and need different flags and labels to make it all work
> right.
>
>> I am still not clear whether there is a config option or runtime check
>> we can use to identify this case.
>
> The dma api has to detect, after the driver sets the dma limit, that
> none of system memory is usable when:
>  - The direct path is being used
>  - phys to dma for 0 is outside the dma limit
>
> Then it should assume the arch has setup a swiotlb pool for it to use
> to fix the high memory problem.
>
> Similar hackery would be needed in the dma alloc path to know that
> decrypted can be used to fix the high memory problem like for GUEST.
>
> I guess some 'dev_cannot_reach_memory(dev)' sort of test in a
> few key places? Setup with a static branch to be a nop on everything
> but AMD, compiled out on every other arch.
>

If we are not able to reach the memory because of the memory encryption
bit, then isn't dev_cannot_reach_memory(dev) the same as
force_dma_unencrypted(dev)? If so, that is how it is already done.

I am wondering whether we can keep this simpler by ignoring the
swiotlb=force kernel parameter and keeping cc_shared as it is, even
though that can be confusing when looking at SME.

The three configurations we need to consider here are:

1) SEV-SNP guest
2) SME host with iommu=translated
3) SME host with iommu=passthrough

IIUC, all of the above work with the current code because we mark the
swiotlb as cc_shared/decrypted when CC_ATTR_MEM_ENCRYPT is set (i.e.,
this applies to an SME host as well).

The challenge arises when the user forces swiotlb bouncing with the
swiotlb=force command-line option. At that point, all devices, including
those whose DMA mask can handle encrypted DMA addresses, are forced to
use SWIOTLB. That becomes a problem because SWIOTLB is marked as
decrypted by default.

How about something like the following?

x86/dma: Disable forced SWIOTLB bouncing for SME IOMMU passthrough

With host memory encryption and IOMMU passthrough, DMA address handling
depends on whether a device can address the C-bit. Devices that cannot
address it need DMA addresses with the C-bit cleared, while devices that
can address encrypted memory should keep using encrypted DMA addresses.

The default swiotlb pool is marked shared when memory encryption is active.
Forcing all devices through that pool would also force devices capable of
encrypted DMA to use shared mappings. Clear the global swiotlb-force-bounce
state in this mode, and warn when this overrides an explicit swiotlb=force
command-line request.

Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>

modified   arch/x86/kernel/pci-dma.c
@@ -51,8 +51,24 @@ static void __init pci_swiotlb_detect(void)
 	 * Set swiotlb to 1 so that bounce buffers are allocated and used for
 	 * devices that can't support DMA to encrypted memory.
 	 */
-	if (cc_platform_has(CC_ATTR_HOST_MEM_ENCRYPT))
+	if (cc_platform_has(CC_ATTR_HOST_MEM_ENCRYPT)) {
 		x86_swiotlb_enable = true;
+		/*
+		 * With host memory encryption and IOMMU passthrough, devices
+		 * that cannot address the C-bit need DMA addresses with the
+		 * C-bit cleared, while devices that can address encrypted
+		 * memory should keep using encrypted DMA addresses.
+		 *
+		 * The default SWIOTLB pool is marked shared when memory
+		 * encryption is active, so forcing all devices through it would
+		 * also force devices that support encrypted DMA to use shared
+		 * mappings. Disable global forced bouncing in this mode.
+		 */
+		if (iommu_default_passthrough() &&
+		    clear_swiotlb_force_bounce())
+			pr_warn("Ignoring swiotlb=force with host memory encryption and "
+				"IOMMU passthrough\n");
+	}
 
 	/*
 	 * Guest with guest memory encryption currently perform all DMA through
modified   include/linux/swiotlb.h
@@ -40,6 +40,7 @@ void __init swiotlb_init_remap(bool addressing_limit, unsigned int flags,
 int swiotlb_init_late(size_t size, gfp_t gfp_mask,
 	int (*remap)(void *tlb, unsigned long nslabs));
 extern void __init swiotlb_update_mem_attributes(void);
+bool __init clear_swiotlb_force_bounce(void);
 
 #ifdef CONFIG_SWIOTLB
 
modified   kernel/dma/swiotlb.c
@@ -208,6 +208,15 @@ unsigned long swiotlb_size_or_default(void)
 	return default_nslabs << IO_TLB_SHIFT;
 }
 
+bool __init clear_swiotlb_force_bounce(void)
+{
+	if (!swiotlb_force_bounce)
+		return false;
+
+	swiotlb_force_bounce = false;
+	return true;
+}
+
 void __init swiotlb_adjust_size(unsigned long size)
 {
 	/*


^ permalink raw reply

* [RFC 0/3] powerpc/perf: Add Device Tree based PMU description framework
From: Shivani Nittor @ 2026-06-29  6:10 UTC (permalink / raw)
  To: maddy, linuxppc-dev; +Cc: linux-kernel, atrajeev, shivani, tshah

PowerPC has a Performance Monitoring Unit (PMU) infrastructure which provides
comprehensive performance monitoring capabilities at the processor level. The
PMU includes programmable counters (PMCs) and control registers (MMCRs) that
enable detailed performance analysis and profiling.

The PowerPC PMU consists of 6 Performance Monitor Counters (PMCs), where PMC1-4
are fully programmable and can monitor any supported event, while PMC5 and PMC6
are dedicated to counting instructions and cycles respectively. The PMU is
controlled through 5 Monitor Mode Control Registers (MMCRs) that configure
event selection, sampling modes, thresholds, and other monitoring parameters.

This patchset enables the PowerPC PMU by providing a device tree specification
that describes the PMU hardware capabilities, event encoding format, register
mappings, and event constraints. The device tree approach allows firmware
(OPAL/skiboot) to communicate PMU capabilities to the kernel dynamically,
enabling better hardware abstraction and forward compatibility.

PMU Hardware Components:
The PowerPC PMU provides the following hardware components:
- 6 Performance Monitor Counters (PMC1-PMC6)
 * PMC1-4: Programmable counters (32-bit, any event)
 * PMC5: Fixed counter for instructions (32-bit)
 * PMC6: Fixed counter for cycles (32-bit)
- 5 Monitor Mode Control Registers
 * MMCR0 (SPR 795): Primary control register
 * MMCR1 (SPR 798): Event selection and configuration
 * MMCR2 (SPR 785): Extended event configuration
 * MMCR3 (SPR 754): Additional event source selection
 * MMCRA (SPR 0x312): Sampling and marking control

Event Encoding Format:
The device tree describes how 64-bit event codes are decomposed into fields
that map to specific bits in the MMCR registers. Key encoding fields include:
- PMCxSEL: 8-bit event selector (256 events per PMC)
- PMCxUNIT: 4-bit unit selector for event source
- PMCxCOMB: 2-bit combine mode for multi-counter operations
- THRESH_SEL/START/STOP: Threshold-based event filtering
- SDAR_MODE: Data address sampling configuration
- MARK: Instruction marking for sampling
- L2L3_SELECT: Cache event selection
- IFM: Instruction fetch marking mode

Event Constraints:
The PMU enforces several hardware constraints that are described in the DTS:
- Counter restrictions: PMC5 limited to instructions (0x500fa), PMC6 limited
 to cycles (0x600f4)
- Sampling constraints: Events with sampling require specific bit patterns
- Threshold constraints: Threshold events require coordinated MMCR field values
- Cache constraints: Cache events restricted to specific units (6,7,8,9) and
 require PMC4 for certain operations
- EBB (Event-Based Branching) constraints: EBB events require PMC assignment
- BHRB (Branch History Rolling Buffer) constraints: BHRB requires EBB enabled
- L1 qualifier constraints: L1 cache qualifiers for load/store filtering
- Radix scope constraints: Radix page table scope qualification

PMU Events Information:
The device tree includes definitions for common performance events such as:
- Core events: cycles, instructions, dispatch/execution stalls
- Branch events: branches, branch-misses, branch predictions
- Cache events: L1/L2/L3 hits and misses for data and instructions
- TLB events: DTLB and ITLB misses
- Memory events: load/store operations and cache reloads

The kernel discovers the PMU configuration in the device tree at the "pmus"
device node which contains a "pmu_dts@0" child node with compatible field
"ibm,power-pmu".

Parsing of the PMU Information:
To parse the PMU configuration, the kernel discovers the "pmus" node and
walks through the PMU definition, extracting:
- Hardware capabilities (number of PMCs, MMCRs)
- SPR (Special Purpose Register) definitions
- Event code format and field mappings
- MMCR bit field configurations
- Event constraints and restrictions
- Pre-defined event list with codes and descriptions

Here is an excerpt of the DTS showing the PMU node structure:
pmus {
	#address-cells = <1>;
	#size-cells = <0>;

	pmu_dts@0 {
		compatible = "ibm,power-pmu";
		reg = <0>;
		pmu-name = "POWER10 PMU";
		pmu-version = "PowerISA 3.1";
		platform = "power10";
		status = "okay";
		nr_pmc = <6>;
		nr_mmcr = <5>;

		sprs {
			pmcs {
				pmc1 {
					sprn = <787>;
					register-width = <32>;
					privilege = "hv";
					programmable = <1>;
					event = "any";
					status = "okay";
				};
				[...]
			};
			mmcr {
				mmcr0 {
					sprn = <795>;
					register-width = <64>;
					privilege = "hv";
					status = "okay";
				};
				[...]
			};
		};

		evt_code_format {
			compatible = "ibm,power-pmu";

			PMCxSEL {
				description = "PMC event selector (256 possible events per PMC)";
				bits = <0 7>;
				length = <8>;
				mmcr = <1>;
				target_field_base = <32>;
				target_field_shift = <8>;
			};
			[...]
		};

		constraints {
			pmc-constraints {
				max-counter = <6>;

				restricted-counters-5 {
					pmc = <5>;
					valid-events = <0x00000000 0x000500fa>;
				};

				restricted-counters-6 {
					pmc = <6>;
					valid-events = <0x00000000 0x000600f4>;
				};
			};
			[...]
		};

		events {
			cycles {
				event_code = <0x600f4>;
				event-category = "core";
				event-class = "primary";
				description = "Number of processor cycles";
				status = "okay";
			};
			instructions {
				event_code = <0x500fa>;
				event-category = "core";
				event-class = "primary";
				description = "Number of instructions completed";
				status = "okay";
			};
			[...]
		};
	};
};

From the device tree, the kernel parses the PMU hardware description, event
encoding format, constraints, and pre-defined events.

After parsing the PMU configuration, the PMU and its events are registered
with the kernel's perf subsystem, making them available for performance
monitoring and profiling.

This series depends on the corresponding skiboot patches:
https://github.com/maddy-kerneldev/skiboot/commit/783e33f17f13412b89b774124cef0988c0578829

Comments/feedback/suggestions are welcome.

Next Steps:
1) Add DTS support for peripheral and unit-specific PMU events.
2) Extend the DTS schema for additional MMCR registers and controls.
3) Support advanced PMU features such as BHRB.
4) Feedback from Community

Shivani Nittor (3):
  powerpc/perf: Register PMU from device tree and expose events
  powerpc/perf: Add DTS-based MMCR computation
  powerpc/perf: Add DTS-based event constraints

 arch/powerpc/include/asm/dts_pmu.h    |  82 ++++
 arch/powerpc/perf/Makefile            |   4 +-
 arch/powerpc/perf/core-book3s.c       |  12 +-
 arch/powerpc/perf/dts_pmu.c           | 536 ++++++++++++++++++++++++++
 arch/powerpc/perf/internal.h          |   1 +
 arch/powerpc/perf/isa207-common.c     | 262 +++++++++++++
 arch/powerpc/perf/isa207-common.h     |   4 +
 arch/powerpc/platforms/powernv/opal.c |  15 +
 8 files changed, 914 insertions(+), 2 deletions(-)
 create mode 100644 arch/powerpc/include/asm/dts_pmu.h
 create mode 100644 arch/powerpc/perf/dts_pmu.c

-- 
2.54.0



^ permalink raw reply

* [RFC 2/3] powerpc/perf: Add DTS-based MMCR computation
From: Shivani Nittor @ 2026-06-29  6:10 UTC (permalink / raw)
  To: maddy, linuxppc-dev; +Cc: linux-kernel, atrajeev, shivani, tshah
In-Reply-To: <20260629061101.43119-1-shivani@linux.ibm.com>

Add support for computing MMCR register values from event
descriptions provided in the device tree.

Parse the evt_code_format node to build mappings between event
code fields and MMCR programming fields. Use these mappings to
generate MMCR register values dynamically from the event code.

This allows event encodings and MMCR programming information to
be described in the device tree rather than being hardcoded in
the PMU implementation.

Add compute_mmcr_dts() to isa207-common.c to compute MMCR register
values dynamically from event codes described in the device tree.

The function extracts PMC and field values from the event code using
field_maps[], which are populated by parsing the evt_code_format node
in the DTS during pmu_dts_probe(). Each field entry captures the bit
range in the event code, the target MMCR register, and the shift
position to use when programming the register.

For non-PMC, non-MMCRA fields, the shift is computed either using a
per-PMC target_field_shift formula (target_field_base - pmc *
target_field_shift) or a fixed pgm_start offset. MMCRA and PMC fields
always use the fixed pgm_start path.

MMCR2 privilege filtering (FCP, FCS, FCH) is applied per-event based
on perf_event attr exclude_* flags.

Wire up the DTS PMU to use dts_compute_mmcr() as its compute_mmcr
callback, and carry the ISA207 constraint fields, alternatives,
memory data source and weight helpers, and PPMU flags to match the
Power10 PMU profile.

Also temporarily comment out the duplicate compute_mmcr call site and
the surrounding mtspr block in power_pmu_enable() while the DTS path
is brought up; these will be restored once the transition is complete.

Signed-off-by: Shivani Nittor <shivani@linux.ibm.com>
---
 arch/powerpc/include/asm/dts_pmu.h |  17 ++++
 arch/powerpc/perf/dts_pmu.c        | 118 +++++++++++++++++++++++++++-
 arch/powerpc/perf/isa207-common.c  | 120 +++++++++++++++++++++++++++++
 arch/powerpc/perf/isa207-common.h  |   2 +
 4 files changed, 253 insertions(+), 4 deletions(-)

diff --git a/arch/powerpc/include/asm/dts_pmu.h b/arch/powerpc/include/asm/dts_pmu.h
index 791dda370de8..1309a45ce604 100644
--- a/arch/powerpc/include/asm/dts_pmu.h
+++ b/arch/powerpc/include/asm/dts_pmu.h
@@ -3,10 +3,27 @@
 
 #include <linux/types.h>
 
+#define MAX_FIELDS 17
 #define MAX_MMCR   5
 #define MAX_DTS_EVENTS 32
 #define MAX_PMU_COUNTERS 6
 
+struct dts_field_map {
+	u32 bits_start, bits_end;
+	u32 target_field_base, target_field_shift;
+	u32 pgm_start, mmcr;
+	bool use_target_field_shift, is_pmc;
+	char name[32];
+};
+
+extern struct dts_field_map field_maps[MAX_FIELDS];
+extern int field_count;
+
 extern u32 mmcr_regs_sprs[MAX_MMCR];
 extern int mmcr_count;
+
+int compute_mmcr_dts(u64 event[], int n_ev,
+			unsigned int hwc[], struct mmcr_regs *mmcr,
+			struct perf_event *pevents[], u32 flags);
+
 #endif
diff --git a/arch/powerpc/perf/dts_pmu.c b/arch/powerpc/perf/dts_pmu.c
index 67eabd5ec6e5..39107af6d467 100644
--- a/arch/powerpc/perf/dts_pmu.c
+++ b/arch/powerpc/perf/dts_pmu.c
@@ -10,10 +10,14 @@
 #include <linux/string.h>
 #include <asm/reg.h>
 #include <asm/dts_pmu.h>
-
+#include "isa207-common.h"
 
 extern void unregister_power_pmu(struct power_pmu *pmu);
 static u32 pmu_dts_nr_pmc;
+struct dts_field_map pmcsel_map;
+struct dts_field_map pmc_map;
+struct dts_field_map field_maps[MAX_FIELDS];
+int field_count;
 
 u32 mmcr_regs_sprs[MAX_MMCR];
 int mmcr_count;
@@ -84,10 +88,55 @@ static const struct attribute_group *pmu_dts_attr_groups[] = {
 	NULL,
 };
 
+static int dts_compute_mmcr(u64 event[], int n_ev,
+					unsigned int hwc[], struct mmcr_regs *mmcr,
+					struct perf_event *pevents[], u32 flags)
+{
+	int ret;
+
+	ret = compute_mmcr_dts(event, n_ev, hwc, mmcr, pevents, flags);
+	if (!ret)
+		mmcr->mmcr0 |= MMCR0_C56RUN;
+	return ret;
+}
+
+static const unsigned int dts_event_alternatives[][MAX_ALT] = {
+	{ 0x600f4, 0x1001e },
+};
+
+static int dts_get_alternatives(u64 event, unsigned int flags, u64 alt[])
+{
+	int num_alt = 0;
+
+	num_alt = isa207_get_alternatives(event, alt,
+					  ARRAY_SIZE(dts_event_alternatives), flags,
+					  dts_event_alternatives);
+
+	return num_alt;
+}
+
 static struct power_pmu dts_pmu = {
-	.name           = "cpu_dts",
-	.n_counter      = MAX_PMU_COUNTERS,
-	.attr_groups    = pmu_dts_attr_groups,
+	.name                   = "cpu_dts",
+	.n_counter              = MAX_PMU_COUNTERS,
+	.attr_groups            = pmu_dts_attr_groups,
+	.add_fields             = ISA207_ADD_FIELDS,
+	.test_adder             = ISA207_TEST_ADDER,
+	.group_constraint_mask  = CNST_CACHE_PMC4_MASK,
+	.group_constraint_val   = CNST_CACHE_PMC4_VAL,
+	.compute_mmcr           = dts_compute_mmcr,
+	// .config_bhrb         = power10_config_bhrb,
+	// .bhrb_filter_map     = power10_bhrb_filte-r_map,
+	.get_alternatives       = dts_get_alternatives,
+	.get_mem_data_src       = isa207_get_mem_data_src,
+	.get_mem_weight         = isa207_get_mem_weight,
+	.disable_pmc            = isa207_disable_pmc,
+	.flags                  = PPMU_HAS_SIER | PPMU_ARCH_207S |
+					PPMU_ARCH_31 | PPMU_HAS_ATTR_CONFIG1 |
+					PPMU_P10,
+	.attr_groups            = pmu_dts_attr_groups,
+	//.bhrb_nr              = 32,
+	.capabilities           = PERF_PMU_CAP_EXTENDED_REGS,
+	//.check_attr_config    = power10_check_attr_config,
 };
 
 /* Device Tree match */
@@ -108,6 +157,8 @@ static int pmu_dts_probe(struct platform_device *pdev)
 	u32 code64[2];
 	u32 code128[4];
 	int cells;
+	struct device_node *fmt_np, *field_np;
+	u32 bits[2], pgm[2];
 	const char *str;
 
 	pr_info("PMU DTS probe node = %s\n", np->full_name);
@@ -170,6 +221,65 @@ static int pmu_dts_probe(struct platform_device *pdev)
 	}
 
 	/* Parse events */
+	fmt_np = of_get_child_by_name(np, "evt_code_format");
+	if (!fmt_np) {
+		pr_err("pmu_dts: no evt_code_format node\n");
+		return -EINVAL;
+	}
+
+	field_count = 0;
+	for_each_child_of_node(fmt_np, field_np) {
+
+		struct dts_field_map *f = &field_maps[field_count];
+
+		printk("field_np->name is %s\n", field_np->name);
+		snprintf(f->name, sizeof(f->name), "%s", field_np->name);
+		f->is_pmc = false;
+		f->use_target_field_shift = false;
+
+		/* Identify PMC field */
+		if (!strcmp(field_np->name, "PMCx"))
+			f->is_pmc = true;
+
+		if (of_property_read_u32_array(field_np, "bits", bits, 2))
+			continue;
+
+		f->bits_start = bits[0];
+		f->bits_end   = bits[1];
+
+		if (of_property_read_u32(field_np, "mmcr", &f->mmcr))
+			continue;
+
+	/* Check target_field_shift-based mapping */
+		if (!f->is_pmc && f->mmcr != 4) {
+			if (!of_property_read_u32(field_np, "target_field_base",
+					&f->target_field_base)) {
+				of_property_read_u32(field_np, "target_field_shift",
+					&f->target_field_shift);
+				f->use_target_field_shift = true;
+
+			} else {
+				if (!of_property_read_u32_array(field_np, "target_fields", pgm, 2))
+					f->pgm_start = pgm[0];
+				else
+					f->pgm_start = 0;
+			}
+
+		} else {
+			/* MMCRA or PMC field → no target_field_shift */
+			if (!of_property_read_u32_array(field_np, "target_fields", pgm, 2))
+				f->pgm_start = pgm[0];
+			else
+				f->pgm_start = 0;
+
+			f->use_target_field_shift = false;
+		}
+
+		field_count++;
+		if (field_count >= MAX_FIELDS)
+			break;
+	}
+
 	events_np = of_get_child_by_name(np, "events");
 	if (!events_np) {
 		pr_err("pmu_dts: no events node found\n");
diff --git a/arch/powerpc/perf/isa207-common.c b/arch/powerpc/perf/isa207-common.c
index e11d1bbbc27b..f912432fd6db 100644
--- a/arch/powerpc/perf/isa207-common.c
+++ b/arch/powerpc/perf/isa207-common.c
@@ -80,6 +80,11 @@ static unsigned long sdar_mod_val(u64 event)
 	return p9_SDAR_MODE(event);
 }
 
+static u64 extract_bits(u64 val, int start, int end)
+{
+	return (val >> start) & ((1ULL << (end - start + 1)) - 1);
+}
+
 static void mmcra_sdar_mode(u64 event, unsigned long *mmcra)
 {
 	/*
@@ -743,6 +748,121 @@ int isa207_compute_mmcr(u64 event[], int n_ev,
 	return 0;
 }
 
+int compute_mmcr_dts(u64 event[], int n_ev,
+			unsigned int hwc[], struct mmcr_regs *mmcr,
+			struct perf_event *pevents[], u32 flags)
+{
+	u64 mmcr_val[MAX_MMCR] = {0};
+	u32 pmc = 0, pmc_inuse = 0;
+	int i, ev;
+	u32 pmc_arr[MAX_HWEVENTS] = {0};
+
+	for (ev = 0; ev < n_ev; ev++) {
+		pmc = (event[ev] >> EVENT_PMC_SHIFT) & EVENT_PMC_MASK;
+		pmc_arr[ev] = pmc;
+		if (pmc)
+			pmc_inuse |= 1 << pmc;
+
+		hwc[ev] = pmc - 1;
+	}
+
+	/*
+	 * Disable bhrb unless explicitly requested
+	 * by setting MMCRA (BHRBRD) bit.
+	 */
+	if (cpu_has_feature(CPU_FTR_ARCH_31))
+		mmcr->mmcra |= MMCRA_BHRB_DISABLE;
+
+	if (!pmc) {
+		for (pmc = 1; pmc <= 4; ++pmc) {
+			if (!(pmc_inuse & (1 << pmc)))
+				break;
+		}
+
+		pmc_inuse |= 1 << pmc;
+	}
+
+	/* Extract PMC from DTS field */
+	for (i = 0; i < field_count; i++) {
+		struct dts_field_map *f = &field_maps[i];
+
+		if (f->is_pmc) {
+			pmc = extract_bits(event[0], f->bits_start, f->bits_end);
+			break;
+		}
+	}
+	for (ev = 0; ev < n_ev; ev++) {
+		pmc = pmc_arr[ev];
+
+		for (i = 0; i < field_count; i++) {
+			struct dts_field_map *f = &field_maps[i];
+			u64 val, shift;
+
+			val = extract_bits(event[ev], f->bits_start, f->bits_end);
+			if (f->is_pmc)
+				continue;
+
+			if (pmc == 5 || pmc == 6)
+				continue;
+
+			if (f->use_target_field_shift)
+				shift = f->target_field_base - (pmc * f->target_field_shift);
+			else
+				shift = f->pgm_start;
+
+			mmcr_val[f->mmcr] |= (val << shift);
+		}
+
+		/* MMCR2 privilege filtering */
+		if (pmc <= 6 && pevents && pevents[ev]) {
+
+			if (pevents[ev]->attr.exclude_user)
+				mmcr_val[2] |= MMCR2_FCP(pmc);
+
+			if (pevents[ev]->attr.exclude_hv)
+				mmcr_val[2] |= MMCR2_FCH(pmc);
+
+			if (pevents[ev]->attr.exclude_kernel) {
+				if (cpu_has_feature(CPU_FTR_HVMODE))
+					mmcr_val[2] |= MMCR2_FCH(pmc);
+				else
+					mmcr_val[2] |= MMCR2_FCS(pmc);
+			}
+		}
+	}
+
+	mmcr->mmcr0 = 0;
+
+	/* pmc_inuse is 1-based */
+	if (pmc_inuse & 2)
+		mmcr->mmcr0 = MMCR0_PMC1CE;
+
+	if (pmc_inuse & 0x7c)
+		mmcr->mmcr0 |= MMCR0_PMCjCE;
+
+	/* If we're not using PMC 5 or 6, freeze them */
+	if (!(pmc_inuse & 0x60))
+		mmcr->mmcr0 |= MMCR0_FC56;
+
+	/*
+	 * Set mmcr0 (PMCCEXT) for p10 which
+	 * will restrict access to group B registers
+	 * when MMCR0 PMCC=0b00.
+	 */
+	if (cpu_has_feature(CPU_FTR_ARCH_31))
+		mmcr->mmcr0 |= MMCR0_PMCCEXT;
+
+	/*
+	 * Many places in core-book3s uses cpuhw->mmcr for enabling events
+	 * Till we move away completely to DTS, maintain values in cpu->mmcr
+	 */
+	mmcr->mmcr1 = mmcr_val[1];
+	mmcr->mmcra = mmcr_val[4];
+	mmcr->mmcr2 = mmcr_val[2];
+	mmcr->mmcr3 = mmcr_val[3];
+	return 0;
+}
+
 void isa207_disable_pmc(unsigned int pmc, struct mmcr_regs *mmcr)
 {
 	if (pmc <= 3)
diff --git a/arch/powerpc/perf/isa207-common.h b/arch/powerpc/perf/isa207-common.h
index f594fa6580d1..e9a7c5a39ec2 100644
--- a/arch/powerpc/perf/isa207-common.h
+++ b/arch/powerpc/perf/isa207-common.h
@@ -281,6 +281,8 @@ int isa207_get_constraint(u64 event, unsigned long *maskp, unsigned long *valp,
 int isa207_compute_mmcr(u64 event[], int n_ev,
 				unsigned int hwc[], struct mmcr_regs *mmcr,
 				struct perf_event *pevents[], u32 flags);
+int compute_mmcr_dts(u64 event[], int n_ev, unsigned int hwc[], struct mmcr_regs *mmcr,
+				struct perf_event *pevents[], u32 flags);
 void isa207_disable_pmc(unsigned int pmc, struct mmcr_regs *mmcr);
 int isa207_get_alternatives(u64 event, u64 alt[], int size, unsigned int flags,
 					const unsigned int ev_alt[][MAX_ALT]);
-- 
2.54.0



^ permalink raw reply related

* [RFC 1/3] powerpc/perf: Register PMU from device tree and expose events
From: Shivani Nittor @ 2026-06-29  6:10 UTC (permalink / raw)
  To: maddy, linuxppc-dev; +Cc: linux-kernel, atrajeev, shivani, tshah
In-Reply-To: <20260629061101.43119-1-shivani@linux.ibm.com>

Introduce a DTS-based PMU driver that discovers PMU information
from the device tree and registers a PowerPC PMU instance at
runtime.

The driver parses basic PMU properties such as the number of
counters and MMCR register definitions, creates sysfs event
entries from the device tree event descriptions, and registers
the PMU with the perf subsystem.

To support dynamic PMU registration, add PMU unregistration
support and create a platform device for PMU nodes described
in the device tree.

This forms the foundation for moving PMU descriptions out of
architecture-specific kernel code and into device tree data.

The driver implements the following functionality:

Device Tree Parsing:
- Reads PMU properties including counter count (nr_pmc), PMU name,
  version, and platform information
- Parses MMCR (Monitor Mode Control Register) definitions from the
  sprs/mmcr node hierarchy, storing the MMCR register SPRNs
- Extracts event definitions from the events node, supporting both
  32-bit and 64-bit event codes

Dynamic Event Registration:
- Creates sysfs event entries dynamically from device tree event
  descriptions
- Generates event attributes with format "event=0x<code>" for each
  discovered event
- Exposes events under /sys/devices/<pmu-name>/events/

PMU Registration:
- Registers a power_pmu instance with the perf subsystem using the
  device tree-provided PMU name
- Adds PMU unregistration support to enable proper cleanup
- Creates platform device for "ibm,power-pmu" compatible nodes

Sysfs Interface:
- Provides format attributes (event, pmcsel) under
  /sys/devices/<pmu-name>/format/
- Exposes device tree debug information (nr_pmc) under
  /sys/devices/<pmu-name>/dt/

Platform Integration:
- Adds opal_pmus_init_dev() to create platform devices for PMU nodes
  during OPAL initialization on PowerNV systems
- Uses platform driver model with probe/remove callbacks

With the device tree based PMU registered:
 # ls /sys/devices/cpu_dts/events/

 # Snippet of "perf list" :

  cpu_dts/branch-finished/                               [Kernel PMU event]
  cpu_dts/branch-mispredict-finished/                    [Kernel PMU event]
  cpu_dts/branch-misses/                                 [Kernel PMU event]
  cpu_dts/branches/                                      [Kernel PMU event]
  cpu_dts/cycles/                                        [Kernel PMU event]

Signed-off-by: Shivani Nittor <shivani@linux.ibm.com>
---
 arch/powerpc/include/asm/dts_pmu.h    |  12 ++
 arch/powerpc/perf/Makefile            |   3 +-
 arch/powerpc/perf/core-book3s.c       |  12 +-
 arch/powerpc/perf/dts_pmu.c           | 257 ++++++++++++++++++++++++++
 arch/powerpc/perf/internal.h          |   1 +
 arch/powerpc/perf/isa207-common.c     |   1 +
 arch/powerpc/platforms/powernv/opal.c |  15 ++
 7 files changed, 299 insertions(+), 2 deletions(-)
 create mode 100644 arch/powerpc/include/asm/dts_pmu.h
 create mode 100644 arch/powerpc/perf/dts_pmu.c

diff --git a/arch/powerpc/include/asm/dts_pmu.h b/arch/powerpc/include/asm/dts_pmu.h
new file mode 100644
index 000000000000..791dda370de8
--- /dev/null
+++ b/arch/powerpc/include/asm/dts_pmu.h
@@ -0,0 +1,12 @@
+#ifndef _ASM_DTS_PMU_H
+#define _ASM_DTS_PMU_H
+
+#include <linux/types.h>
+
+#define MAX_MMCR   5
+#define MAX_DTS_EVENTS 32
+#define MAX_PMU_COUNTERS 6
+
+extern u32 mmcr_regs_sprs[MAX_MMCR];
+extern int mmcr_count;
+#endif
diff --git a/arch/powerpc/perf/Makefile b/arch/powerpc/perf/Makefile
index 78dd7e25219e..537f4b87cffe 100644
--- a/arch/powerpc/perf/Makefile
+++ b/arch/powerpc/perf/Makefile
@@ -7,7 +7,8 @@ obj-$(CONFIG_PPC_PERF_CTRS)	+= core-book3s.o
 obj64-$(CONFIG_PPC_PERF_CTRS)	+= ppc970-pmu.o power5-pmu.o \
 				   power5+-pmu.o power6-pmu.o power7-pmu.o \
 				   isa207-common.o power8-pmu.o power9-pmu.o \
-				   generic-compat-pmu.o power10-pmu.o bhrb.o
+				   generic-compat-pmu.o power10-pmu.o bhrb.o \
+				   dts_pmu.o
 obj32-$(CONFIG_PPC_PERF_CTRS)	+= mpc7450-pmu.o
 
 obj-$(CONFIG_PPC_POWERNV)	+= imc-pmu.o
diff --git a/arch/powerpc/perf/core-book3s.c b/arch/powerpc/perf/core-book3s.c
index 8b0081441f85..f33ed1423046 100644
--- a/arch/powerpc/perf/core-book3s.c
+++ b/arch/powerpc/perf/core-book3s.c
@@ -2569,12 +2569,19 @@ int __init register_power_pmu(struct power_pmu *pmu)
 		freeze_events_kernel = MMCR0_FCHV;
 #endif /* CONFIG_PPC64 */
 
-	perf_pmu_register(&power_pmu, "cpu", PERF_TYPE_RAW);
+	perf_pmu_register(&power_pmu, pmu->name, PERF_TYPE_RAW);
 	cpuhp_setup_state(CPUHP_PERF_POWER, "perf/powerpc:prepare",
 			  power_pmu_prepare_cpu, NULL);
 	return 0;
 }
 
+void unregister_power_pmu(struct power_pmu *pmu)
+{
+	if (!ppmu)
+		return;
+
+	perf_pmu_unregister(&power_pmu);
+}
 #ifdef CONFIG_PPC64
 static bool pmu_override = false;
 static unsigned long pmu_override_val;
@@ -2588,6 +2595,9 @@ static void do_pmu_override(void *data)
 
 static int __init init_ppc64_pmu(void)
 {
+	/* Disable registering PMU from here and enable picking from device tree */
+	return 0;
+
 	if (cpu_has_feature(CPU_FTR_HVMODE) && pmu_override) {
 		pr_warn("disabling perf due to pmu_override= command line option.\n");
 		on_each_cpu(do_pmu_override, NULL, 1);
diff --git a/arch/powerpc/perf/dts_pmu.c b/arch/powerpc/perf/dts_pmu.c
new file mode 100644
index 000000000000..67eabd5ec6e5
--- /dev/null
+++ b/arch/powerpc/perf/dts_pmu.c
@@ -0,0 +1,257 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/module.h>
+#include <linux/perf_event.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/string.h>
+#include <asm/reg.h>
+#include <asm/dts_pmu.h>
+
+
+extern void unregister_power_pmu(struct power_pmu *pmu);
+static u32 pmu_dts_nr_pmc;
+
+u32 mmcr_regs_sprs[MAX_MMCR];
+int mmcr_count;
+
+struct pmu_dts_event {
+	struct device_attribute attr;
+	char name[32];
+	char config[32];
+};
+
+static struct pmu_dts_event *dts_events[MAX_DTS_EVENTS];
+static struct attribute *pmu_dts_events_attrs[MAX_DTS_EVENTS + 1];
+static int dts_event_count;
+
+static ssize_t pmu_dts_event_show(struct device *dev,
+				struct device_attribute *attr,
+				char *buf)
+{
+	struct pmu_dts_event *evt =
+			container_of(attr, struct pmu_dts_event, attr);
+
+	return sprintf(buf, "%s\n", evt->config);
+}
+
+/* Format attributes */
+PMU_FORMAT_ATTR(event, "config:0-59");
+PMU_FORMAT_ATTR(pmcsel, "config1:18-25");
+
+static struct attribute *pmu_dts_format_attrs[] = {
+	&format_attr_event.attr,
+	&format_attr_pmcsel.attr,
+	NULL,
+};
+
+static struct attribute_group pmu_dts_format_group = {
+	.name = "format",
+	.attrs = pmu_dts_format_attrs,
+};
+
+static ssize_t nr_pmc_show(struct device *dev,
+				struct device_attribute *attr,
+				char *buf)
+{
+	return sprintf(buf, "%u\n", pmu_dts_nr_pmc);
+}
+
+static DEVICE_ATTR_RO(nr_pmc);
+
+static struct attribute *pmu_dts_dt_attrs[] = {
+	&dev_attr_nr_pmc.attr,
+	NULL,
+};
+
+static struct attribute_group pmu_dts_dt_group = {
+	.name = "dt",
+	.attrs = pmu_dts_dt_attrs,
+};
+
+static struct attribute_group pmu_dts_events_group = {
+	.name = "events",
+	.attrs = pmu_dts_events_attrs,
+};
+
+static const struct attribute_group *pmu_dts_attr_groups[] = {
+	&pmu_dts_events_group,
+	&pmu_dts_format_group,
+	&pmu_dts_dt_group,
+	NULL,
+};
+
+static struct power_pmu dts_pmu = {
+	.name           = "cpu_dts",
+	.n_counter      = MAX_PMU_COUNTERS,
+	.attr_groups    = pmu_dts_attr_groups,
+};
+
+/* Device Tree match */
+static const struct of_device_id pmu_dts_of_match[] = {
+	{ .compatible = "ibm,power-pmu" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, pmu_dts_of_match);
+
+/* Probe function */
+static int pmu_dts_probe(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node, *child, *events_np;
+	struct device_node *sprs_np, *mmcr_np, *mmcr_child;
+	struct pmu_dts_event *evt;
+	u64 code;
+	u32 code32, sprn;
+	u32 code64[2];
+	u32 code128[4];
+	int cells;
+	const char *str;
+
+	pr_info("PMU DTS probe node = %s\n", np->full_name);
+
+	if (!of_property_present(np, "nr_pmc")) {
+
+		for_each_child_of_node(np, child) {
+			pr_info("child node = %s\n", child->full_name);
+
+			if (of_property_present(child, "nr_pmc")) {
+				np = child;
+				break;
+			}
+		}
+	}
+
+	if (of_property_read_u32(np, "nr_pmc", &pmu_dts_nr_pmc)) {
+		pr_err("pmu_dts: nr_pmc not found in %s\n", np->full_name);
+		return -EINVAL;
+	}
+
+	if (!of_property_read_string(np, "pmu-name", &str))
+		pr_info("PMU Name: %s\n", str);
+
+	if (!of_property_read_string(np, "pmu-version", &str))
+		pr_info("PMU Version: %s\n", str);
+
+	if (!of_property_read_string(np, "platform", &str))
+		pr_info("Platform: %s\n", str);
+
+	sprs_np = of_get_child_by_name(np, "sprs");
+	if (!sprs_np) {
+		pr_err("pmu_dts: no sprs node\n");
+		return -EINVAL;
+	}
+
+	mmcr_np = of_get_child_by_name(sprs_np, "mmcr");
+	if (!mmcr_np) {
+		pr_err("pmu_dts: no mmcr node\n");
+		return -EINVAL;
+	}
+
+	mmcr_count = 0;
+	for_each_child_of_node(mmcr_np, mmcr_child) {
+
+		if (of_property_read_u32(mmcr_child, "sprn", &sprn))
+			continue;
+
+		mmcr_regs_sprs[mmcr_count++] = sprn;
+		pr_info("pmu_dts: MMCR[%d] = %u (%s)\n", mmcr_count - 1,
+				sprn, mmcr_child->name);
+
+		if (mmcr_count >= MAX_MMCR)
+			break;
+	}
+
+	if (!mmcr_count) {
+		pr_err("pmu_dts: no MMCR SPRs found\n");
+		return -EINVAL;
+	}
+
+	/* Parse events */
+	events_np = of_get_child_by_name(np, "events");
+	if (!events_np) {
+		pr_err("pmu_dts: no events node found\n");
+		return -EINVAL;
+	}
+
+	dts_event_count = 0;
+	for_each_child_of_node(events_np, child) {
+		if (!of_device_is_available(child))
+			continue;
+
+		cells = of_property_count_u32_elems(child, "event_code");
+		if (cells == 1) {
+			if (of_property_read_u32(child, "event_code", &code32))
+				continue;
+
+			code = code32;
+
+		} else if (cells == 2) {
+			if (of_property_read_u32_array(child, "event_code", code64, 2))
+				continue;
+			code = ((u64)code64[0] << 32) | code64[1];
+
+		} else if (cells == 4) {
+			if (of_property_read_u32_array(child, "event_code", code128, 4))
+				continue;
+			code = ((u64)code128[1] << 32) | code128[3];
+
+		} else {
+			pr_warn("pmu_dts: invalid event_code for %s\n", child->name);
+			continue;
+		}
+
+		evt = kzalloc(sizeof(*evt), GFP_KERNEL);
+		if (!evt)
+			continue;
+
+		snprintf(evt->name, sizeof(evt->name), "%s", child->name);
+		snprintf(evt->config, sizeof(evt->config), "event=0x%llx", code);
+
+		sysfs_attr_init(&evt->attr.attr);
+		evt->attr.attr.name = evt->name;
+		evt->attr.attr.mode = 0444;
+		evt->attr.show = pmu_dts_event_show;
+		dts_events[dts_event_count] = evt;
+		pmu_dts_events_attrs[dts_event_count] = &evt->attr.attr;
+		dts_event_count++;
+
+		if (dts_event_count >= MAX_DTS_EVENTS)
+			break;
+	}
+	pmu_dts_events_attrs[dts_event_count] = NULL;
+
+	/* Register PMU */
+	pr_info("pmu_dts: registering PMU\n");
+	return register_power_pmu(&dts_pmu);
+}
+
+/* Platform driver */
+static struct platform_driver pmu_dts_driver = {
+	.probe = pmu_dts_probe,
+	.driver = {
+		.name = "pmu_dts",
+		.of_match_table = pmu_dts_of_match,
+	},
+};
+
+static int __init pmu_dts_init(void)
+{
+	pr_info("pmu_dts: init\n");
+	return platform_driver_register(&pmu_dts_driver);
+}
+
+static void __exit pmu_dts_exit(void)
+{
+	pr_info("pmu_dts: exit\n");
+	platform_driver_unregister(&pmu_dts_driver);
+	unregister_power_pmu(&dts_pmu);
+}
+module_init(pmu_dts_init);
+module_exit(pmu_dts_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Shivani Nittor");
+MODULE_DESCRIPTION("PMU DTS driver");
diff --git a/arch/powerpc/perf/internal.h b/arch/powerpc/perf/internal.h
index a70ac471a5a5..11154ee31f8e 100644
--- a/arch/powerpc/perf/internal.h
+++ b/arch/powerpc/perf/internal.h
@@ -2,6 +2,7 @@
 //
 // Copyright 2019 Madhavan Srinivasan, IBM Corporation.
 
+void unregister_power_pmu(struct power_pmu *pmu);
 int __init init_ppc970_pmu(void);
 int __init init_power5_pmu(void);
 int __init init_power5p_pmu(void);
diff --git a/arch/powerpc/perf/isa207-common.c b/arch/powerpc/perf/isa207-common.c
index 2b3547fdba4a..e11d1bbbc27b 100644
--- a/arch/powerpc/perf/isa207-common.c
+++ b/arch/powerpc/perf/isa207-common.c
@@ -7,6 +7,7 @@
  * Copyright 2016 Madhavan Srinivasan, IBM Corporation.
  */
 #include "isa207-common.h"
+#include <asm/dts_pmu.h>
 
 PMU_FORMAT_ATTR(event,		"config:0-49");
 PMU_FORMAT_ATTR(pmcxsel,	"config:0-7");
diff --git a/arch/powerpc/platforms/powernv/opal.c b/arch/powerpc/platforms/powernv/opal.c
index 1946dbdc9fa1..35be67eef2c6 100644
--- a/arch/powerpc/platforms/powernv/opal.c
+++ b/arch/powerpc/platforms/powernv/opal.c
@@ -947,6 +947,18 @@ static void __init opal_imc_init_dev(void)
 
 	of_node_put(np);
 }
+#define PMU_DTB "ibm,power-pmu"
+
+static void __init opal_pmus_init_dev(void)
+{
+	struct device_node *np;
+
+	np = of_find_compatible_node(NULL, NULL, PMU_DTB);
+	if (np)
+		of_platform_device_create(np, NULL, NULL);
+
+	of_node_put(np);
+}
 
 static int kopald(void *unused)
 {
@@ -1032,6 +1044,9 @@ static int __init opal_init(void)
 	/* Detect In-Memory Collection counters and create devices*/
 	opal_imc_init_dev();
 
+    /*Detect PMU node and create device*/
+	opal_pmus_init_dev();
+
 	/* Create leds platform devices */
 	leds = of_find_node_by_path("/ibm,opal/leds");
 	if (leds) {
-- 
2.54.0



^ permalink raw reply related

* [RFC 3/3] powerpc/perf: Add DTS-based event constraints
From: Shivani Nittor @ 2026-06-29  6:10 UTC (permalink / raw)
  To: maddy, linuxppc-dev; +Cc: linux-kernel, atrajeev, shivani, tshah
In-Reply-To: <20260629061101.43119-1-shivani@linux.ibm.com>

Add support for describing PMU event constraints in the device
tree.

Parse constraint definitions from the constraints node and use
them to build PMU scheduling constraints at runtime. Implement
isa207_get_constraint_dts() to validate event combinations and
derive constraint masks and values from the device tree
description.

The constraint system handles:
- PMC restrictions (e.g., PMC5 for instructions, PMC6 for cycles)
- Maximum counter limits
- Cache selector requirements
- EBB (Event-Based Branch) and BHRB (Branch History Rolling Buffer)
  dependencies
- Threshold, sampling, and L1 qualifier constraints
- Radix scope and fabric match constraints
- NC (Next Counter) increment logic

Constraints are conditionally applied based on event properties
(marked events, threshold events, L1 events, BHRB requests) and
CPU features (ARCH_31 for radix scope).

The parse_constraints() function reads constraint definitions from
DTS nodes including pmc-constraints, sample, threshold, cache, ebb,
bhrb, l1-qualifier, fab-match, radix-scope, and nc. Each constraint
field specifies event masks, shifts, constraint masks, and shifts
to build the final constraint value.

This allows PMU constraint information to be described in DTS
rather than being hardcoded in the PMU implementation.

Signed-off-by: Shivani Nittor <shivani@linux.ibm.com>
---
 arch/powerpc/include/asm/dts_pmu.h |  53 +++++++++
 arch/powerpc/perf/Makefile         |   1 +
 arch/powerpc/perf/dts_pmu.c        | 177 ++++++++++++++++++++++++++++-
 arch/powerpc/perf/isa207-common.c  | 141 +++++++++++++++++++++++
 arch/powerpc/perf/isa207-common.h  |   2 +
 5 files changed, 370 insertions(+), 4 deletions(-)

diff --git a/arch/powerpc/include/asm/dts_pmu.h b/arch/powerpc/include/asm/dts_pmu.h
index 1309a45ce604..5632c4f8f748 100644
--- a/arch/powerpc/include/asm/dts_pmu.h
+++ b/arch/powerpc/include/asm/dts_pmu.h
@@ -7,6 +7,13 @@
 #define MAX_MMCR   5
 #define MAX_DTS_EVENTS 32
 #define MAX_PMU_COUNTERS 6
+#define DTS_COND_ALWAYS      0
+#define DTS_COND_MARKED      1
+#define DTS_COND_THRESHOLD   2
+#define DTS_COND_L1          3
+#define DTS_COND_BHRB        4
+#define DTS_COND_CACHE       5
+#define DTS_COND_RADIX       6
 
 struct dts_field_map {
 	u32 bits_start, bits_end;
@@ -21,9 +28,55 @@ extern int field_count;
 
 extern u32 mmcr_regs_sprs[MAX_MMCR];
 extern int mmcr_count;
+extern struct dts_constraint_map *constraint_maps;
+extern int constraint_map_count;
 
 int compute_mmcr_dts(u64 event[], int n_ev,
 			unsigned int hwc[], struct mmcr_regs *mmcr,
 			struct perf_event *pevents[], u32 flags);
 
+struct dts_constraint_field {
+	u64 event_mask, constraint_mask;
+	u32 event_shift, constraint_shift, condition;
+};
+
+struct dts_constraint_map {
+	struct dts_constraint_field field;
+};
+
+struct restricted_counter {
+	u32 pmc;
+	u64 event;
+};
+
+struct dts_threshold_constraints {
+	bool supported;
+	struct dts_constraint_field thresh_sel, thresh_cmp, thresh_ctl;
+};
+
+struct dts_nc_constraints {
+	u64 mask;
+	u32 shift, increment;
+};
+
+struct dts_pmu_constraints {
+	u32 max_counter;
+
+	struct restricted_counter restricted[8];
+	int num_restricted;
+
+	bool require_cache_selector_zero;
+	u32 cache_selector_mask;
+
+	bool require_pmc_for_ebb, bhrb_requires_ebb;
+
+	struct dts_constraint_field sample, ebb, bhrb, l1_qualifier,
+				    fab_match, radix_scope, cache_group,
+				    cache_pmc4, l2l3_group;
+
+	struct dts_threshold_constraints threshold;
+	struct dts_nc_constraints nc;
+};
+
+extern struct dts_pmu_constraints dts_constraints;
 #endif
diff --git a/arch/powerpc/perf/Makefile b/arch/powerpc/perf/Makefile
index 537f4b87cffe..7b474bd0d090 100644
--- a/arch/powerpc/perf/Makefile
+++ b/arch/powerpc/perf/Makefile
@@ -9,6 +9,7 @@ obj64-$(CONFIG_PPC_PERF_CTRS)	+= ppc970-pmu.o power5-pmu.o \
 				   isa207-common.o power8-pmu.o power9-pmu.o \
 				   generic-compat-pmu.o power10-pmu.o bhrb.o \
 				   dts_pmu.o
+
 obj32-$(CONFIG_PPC_PERF_CTRS)	+= mpc7450-pmu.o
 
 obj-$(CONFIG_PPC_POWERNV)	+= imc-pmu.o
diff --git a/arch/powerpc/perf/dts_pmu.c b/arch/powerpc/perf/dts_pmu.c
index 39107af6d467..0faea573f144 100644
--- a/arch/powerpc/perf/dts_pmu.c
+++ b/arch/powerpc/perf/dts_pmu.c
@@ -14,10 +14,13 @@
 
 extern void unregister_power_pmu(struct power_pmu *pmu);
 static u32 pmu_dts_nr_pmc;
+struct dts_pmu_constraints dts_constraints;
 struct dts_field_map pmcsel_map;
 struct dts_field_map pmc_map;
 struct dts_field_map field_maps[MAX_FIELDS];
 int field_count;
+struct dts_constraint_map *constraint_maps;
+int constraint_map_count;
 
 u32 mmcr_regs_sprs[MAX_MMCR];
 int mmcr_count;
@@ -32,6 +35,172 @@ static struct pmu_dts_event *dts_events[MAX_DTS_EVENTS];
 static struct attribute *pmu_dts_events_attrs[MAX_DTS_EVENTS + 1];
 static int dts_event_count;
 
+/* Constraints Structure */
+static void parse_constraint_field(struct device_node *np,
+				struct dts_constraint_field *field)
+{
+	u32 val32[2] = {0};
+
+	if (!np)
+		return;
+
+	/* initialize */
+	field->event_mask = 0;
+	field->event_shift = 0;
+	field->constraint_mask = 0;
+	field->constraint_shift = 0;
+
+	/* event-mask */
+	if (!of_property_read_u32(np, "event-mask", &val32[0]))
+		field->event_mask = val32[0];
+
+	/* event-shift */
+	of_property_read_u32(np, "event-shift", &field->event_shift);
+
+	/* constraint-mask */
+	if (!of_property_read_u32_array(np, "constraint-mask", val32, 2))
+		field->constraint_mask = ((u64)val32[0] << 32) | val32[1];
+	else if (!of_property_read_u32(np, "constraint-mask", &val32[0]))
+		field->constraint_mask = val32[0];
+
+	/* constraint-shift */
+	of_property_read_u32(np, "constraint-shift", &field->constraint_shift);
+
+	of_property_read_u32(np, "condition", &field->condition);
+
+	if (constraint_maps && constraint_map_count < 16) {
+		memcpy(&constraint_maps[constraint_map_count].field,
+					field, sizeof(*field));
+		constraint_map_count++;
+	}
+}
+
+static void parse_constraints(struct device_node *pmu_np)
+{
+	struct device_node *np;
+	struct device_node *child;
+	int i = 0;
+
+	constraint_maps = kcalloc(16, sizeof(*constraint_maps), GFP_KERNEL);
+
+	constraint_map_count = 0;
+
+	np = of_get_child_by_name(pmu_np, "constraints");
+	if (!np)
+		return;
+
+	/* PMC constraints */
+	child = of_get_child_by_name(np, "pmc-constraints");
+
+	if (child) {
+		of_property_read_u32(child,
+					"max-counter", &dts_constraints.max_counter);
+
+		for_each_child_of_node(child, child) {
+			of_property_read_u32(child, "pmc",
+					&dts_constraints.restricted[i].pmc);
+
+			of_property_read_u64(child, "valid-events",
+					&dts_constraints.restricted[i].event);
+			i++;
+		}
+
+		dts_constraints.restricted[0].event = 0x500fa;
+		dts_constraints.restricted[1].event = 0x600f4;
+		dts_constraints.num_restricted = i;
+	}
+
+	/* sample */
+	child = of_get_child_by_name(np, "sample");
+
+	if (child)
+		parse_constraint_field(child, &dts_constraints.sample);
+
+	/* threshold */
+	child = of_get_child_by_name(np, "threshold");
+
+	if (child) {
+		dts_constraints.threshold.supported =
+				of_property_read_bool(child, "supported");
+
+		parse_constraint_field(of_get_child_by_name(child, "thresh-sel"),
+					&dts_constraints.threshold.thresh_sel);
+
+		parse_constraint_field(of_get_child_by_name(child, "thresh-cmp"),
+					&dts_constraints.threshold.thresh_cmp);
+
+		parse_constraint_field(of_get_child_by_name(child, "thresh-ctl"),
+					&dts_constraints.threshold.thresh_ctl);
+	}
+
+	/* cache */
+	child = of_get_child_by_name(np, "cache");
+
+	if (child) {
+		of_property_read_u32(child, "cache-selector-mask",
+					&dts_constraints.cache_selector_mask);
+
+		dts_constraints.require_cache_selector_zero =
+					of_property_read_bool(child, "require-cache-selector-zero");
+
+		/* cache-group */
+		parse_constraint_field(of_get_child_by_name(child, "cache-group"),
+					&dts_constraints.cache_group);
+
+		/* cache-pmc4 */
+		parse_constraint_field(of_get_child_by_name(child, "cache-pmc4"),
+					&dts_constraints.cache_pmc4);
+
+		/* l2l3-group */
+		parse_constraint_field(of_get_child_by_name(child, "l2l3-group"),
+					&dts_constraints.l2l3_group);
+	}
+
+	/* ebb */
+	child = of_get_child_by_name(np, "ebb");
+
+	if (child) {
+		parse_constraint_field(child, &dts_constraints.ebb);
+
+		dts_constraints.require_pmc_for_ebb =
+					of_property_read_bool(child, "require-pmc");
+	}
+
+	/* bhrb */
+	child = of_get_child_by_name(np, "bhrb");
+
+	if (child) {
+		parse_constraint_field(child, &dts_constraints.bhrb);
+
+		dts_constraints.bhrb_requires_ebb =
+					of_property_read_bool(child, "requires-ebb");
+	}
+
+	/* others */
+	parse_constraint_field(of_get_child_by_name(np, "l1-qualifier"),
+					&dts_constraints.l1_qualifier);
+
+	parse_constraint_field(of_get_child_by_name(np, "fab-match"),
+					&dts_constraints.fab_match);
+
+	parse_constraint_field(of_get_child_by_name(np, "radix-scope"),
+					&dts_constraints.radix_scope);
+
+	/* NC */
+	child = of_get_child_by_name(np, "nc");
+
+	if (child) {
+		of_property_read_u64(child, "mask",
+					&dts_constraints.nc.mask);
+
+		of_property_read_u32(child, "shift",
+					&dts_constraints.nc.shift);
+
+		of_property_read_u32(child, "increment",
+					&dts_constraints.nc.increment);
+	}
+}
+
 static ssize_t pmu_dts_event_show(struct device *dev,
 				struct device_attribute *attr,
 				char *buf)
@@ -124,8 +293,7 @@ static struct power_pmu dts_pmu = {
 	.group_constraint_mask  = CNST_CACHE_PMC4_MASK,
 	.group_constraint_val   = CNST_CACHE_PMC4_VAL,
 	.compute_mmcr           = dts_compute_mmcr,
-	// .config_bhrb         = power10_config_bhrb,
-	// .bhrb_filter_map     = power10_bhrb_filte-r_map,
+	.get_constraint         = isa207_get_constraint_dts,
 	.get_alternatives       = dts_get_alternatives,
 	.get_mem_data_src       = isa207_get_mem_data_src,
 	.get_mem_weight         = isa207_get_mem_weight,
@@ -134,9 +302,7 @@ static struct power_pmu dts_pmu = {
 					PPMU_ARCH_31 | PPMU_HAS_ATTR_CONFIG1 |
 					PPMU_P10,
 	.attr_groups            = pmu_dts_attr_groups,
-	//.bhrb_nr              = 32,
 	.capabilities           = PERF_PMU_CAP_EXTENDED_REGS,
-	//.check_attr_config    = power10_check_attr_config,
 };
 
 /* Device Tree match */
@@ -175,6 +341,9 @@ static int pmu_dts_probe(struct platform_device *pdev)
 		}
 	}
 
+	/* For format parsing */
+	parse_constraints(np);
+
 	if (of_property_read_u32(np, "nr_pmc", &pmu_dts_nr_pmc)) {
 		pr_err("pmu_dts: nr_pmc not found in %s\n", np->full_name);
 		return -EINVAL;
diff --git a/arch/powerpc/perf/isa207-common.c b/arch/powerpc/perf/isa207-common.c
index f912432fd6db..1d93f9e5c5f2 100644
--- a/arch/powerpc/perf/isa207-common.c
+++ b/arch/powerpc/perf/isa207-common.c
@@ -748,6 +748,147 @@ int isa207_compute_mmcr(u64 event[], int n_ev,
 	return 0;
 }
 
+static void apply_constraint(u64 event,
+			struct dts_constraint_field *field,
+			unsigned long *mask,
+			unsigned long *value)
+{
+	u64 extracted;
+
+	extracted = (event >> field->event_shift) & field->event_mask;
+	*mask |= field->constraint_mask;
+	*value |= (extracted << field->constraint_shift);
+}
+
+static bool constraint_enabled(struct dts_constraint_map *m, u64 event)
+{
+	switch (m->field.condition) {
+
+	case DTS_COND_ALWAYS:
+		return true;
+
+	case DTS_COND_MARKED:
+		return is_event_marked(event);
+
+	case DTS_COND_THRESHOLD:
+		return event_is_threshold(event);
+
+	case DTS_COND_L1:
+		return (event & EVENT_IS_L1);
+
+	case DTS_COND_BHRB:
+		return (event & EVENT_WANTS_BHRB);
+
+	case DTS_COND_RADIX:
+		return cpu_has_feature(CPU_FTR_ARCH_31);
+
+	default:
+		return false;
+	}
+	return false;
+}
+
+int isa207_get_constraint_dts(u64 event,
+			      unsigned long *maskp,
+			      unsigned long *valp,
+			      u64 event_config1)
+{
+	unsigned int pmc, unit, cache, ebb;
+	unsigned long mask = 0;
+	unsigned long value = 0;
+	u64 base_event;
+	int i, valid = 0;
+
+	pmc = (event >> EVENT_PMC_SHIFT) & EVENT_PMC_MASK;
+	unit = (event >> EVENT_UNIT_SHIFT) & EVENT_UNIT_MASK;
+	cache = (event >> EVENT_CACHE_SEL_SHIFT) & EVENT_CACHE_SEL_MASK;
+	ebb = (event >> EVENT_EBB_SHIFT) & EVENT_EBB_MASK;
+	base_event = event & ~EVENT_LINUX_MASK;
+
+	/* max counter */
+	if (pmc > dts_constraints.max_counter)
+		return -1;
+
+	/* restricted counters */
+	if (pmc >= 5) {
+		valid = 0;
+
+		for (i = 0;
+			i < dts_constraints.num_restricted;
+			i++) {
+
+			if (dts_constraints.restricted[i].pmc == pmc &&
+					dts_constraints.restricted[i].event == base_event) {
+				valid = 1;
+				break;
+			}
+		}
+
+		if (!valid)
+			return -1;
+	}
+
+	/* cache selector */
+	if (dts_constraints.require_cache_selector_zero &&
+			(cache & dts_constraints.cache_selector_mask))
+		return -1;
+
+	/* EBB */
+	if (dts_constraints.require_pmc_for_ebb && !pmc && ebb)
+		return -1;
+
+	/* BHRB */
+	if ((event & EVENT_WANTS_BHRB) &&
+	    dts_constraints.bhrb_requires_ebb && !ebb)
+		return -1;
+
+	/* PMC */
+	if (pmc) {
+		mask |= CNST_PMC_MASK(pmc);
+		value |= CNST_PMC_VAL(pmc);
+
+		if (pmc >= 5)
+			goto post_general_constraints;
+	}
+
+	/* NC */
+	if (pmc <= 4) {
+		mask |= dts_constraints.nc.mask;
+
+		value |=
+			(dts_constraints.nc.increment << dts_constraints.nc.shift);
+	}
+
+	/* cache / l2l3 */
+	if (unit >= 6 && unit <= 9) {
+		if (cpu_has_feature(CPU_FTR_ARCH_31)) {
+			apply_constraint (event, &dts_constraints.l2l3_group,
+				&mask, &value);
+		} else {
+			apply_constraint (event, &dts_constraints.cache_group,
+				&mask, &value);
+
+			if (pmc == 4)
+				mask |= dts_constraints.cache_pmc4.constraint_mask;
+		}
+	}
+
+post_general_constraints:
+
+	for (i = 0; i < constraint_map_count; i++) {
+		if (!constraint_enabled(&constraint_maps[i], event))
+			continue;
+
+		apply_constraint(event, &constraint_maps[i].field,
+				&mask, &value);
+	}
+
+	*maskp = mask;
+	*valp = value;
+
+	return 0;
+}
+
 int compute_mmcr_dts(u64 event[], int n_ev,
 			unsigned int hwc[], struct mmcr_regs *mmcr,
 			struct perf_event *pevents[], u32 flags)
diff --git a/arch/powerpc/perf/isa207-common.h b/arch/powerpc/perf/isa207-common.h
index e9a7c5a39ec2..99cf1a7bac20 100644
--- a/arch/powerpc/perf/isa207-common.h
+++ b/arch/powerpc/perf/isa207-common.h
@@ -278,6 +278,8 @@
 #define REM				P(REMOTE, REMOTE)
 
 int isa207_get_constraint(u64 event, unsigned long *maskp, unsigned long *valp, u64 event_config1);
+int isa207_get_constraint_dts(u64 event, unsigned long *maskp,
+				unsigned long *valp, u64 event_config1);
 int isa207_compute_mmcr(u64 event[], int n_ev,
 				unsigned int hwc[], struct mmcr_regs *mmcr,
 				struct perf_event *pevents[], u32 flags);
-- 
2.54.0



^ permalink raw reply related

* Re: [PATCH V16 4/7] rust/powerpc: Set min rustc version for powerpc
From: Mukesh Kumar Chaurasiya @ 2026-06-29  4:57 UTC (permalink / raw)
  To: Miguel Ojeda
  Cc: maddy, mpe, npiggin, chleroy, peterz, jpoimboe, jbaron, aliceryhl,
	rostedt, ardb, ojeda, boqun, gary, bjorn3_gh, lossin, a.hindborg,
	tmgross, dakr, nathan, nick.desaulniers+lkml, morbo, justinstitt,
	daniel.almeida, acourbot, fujita.tomonori, gregkh, prafulrai522,
	tamird, kees, lyude, airlied, linuxppc-dev, linux-kernel,
	rust-for-linux, llvm
In-Reply-To: <CANiq72=EVsTFkQK3LWrT2Gs5UKKViemGkv1WuitY6NOEtvcsNg@mail.gmail.com>

On Fri, Jun 26, 2026 at 12:17:50PM +0200, Miguel Ojeda wrote:
> On Fri, Jun 26, 2026 at 8:50 AM Mukesh Kumar Chaurasiya
> <mkchauras@gmail.com> wrote:
> >
> > This corresponds to a floating point instruction. Although we have
> > disabled fpu, vsx and altivec we are still generating these. Seems like
> > we have some issue with the rustc here. I didn't dive deeper into this.
> >
> > Is it ok if we keep 1.95 as base for powerpc64le?
> 
> It may be that it is generated but unused (we have that issue
> elsewhere -- see e.g. `rust/compiler_builtins.rs`).
> 
> Having said that, if 1.95 is fine for the powerpc arch maintainers,
> then that is fine (s390 also used a higher minimum -- please see the
> recent change in `scripts/min-tool-version.sh`).
> 
> But please try to bisect upstream Rust to see where the change
> happened, i.e. what is the root cause? e.g. `core` changes, `rustc`
> changes, built-in LLVM changes... It could be that it is not tied to
> the `rustc` version but LLVM's.
> 
> Cheers,
> Miguel
Hey Miguel, 

Sure i'll bisect. Meanwhile i would still like to get this upstream.

Hey @Madhavan,

Can we pick this version up for ppc?

Regards,
Mukesh


^ permalink raw reply

* Re: [PATCH] powerpc/syscall: Fix seccomp errno handling with GENERIC_ENTRY
From: Mukesh Kumar Chaurasiya @ 2026-06-29  4:54 UTC (permalink / raw)
  To: Christophe Leroy (CS GROUP)
  Cc: maddy, mpe, npiggin, sshegde, mkchauras, kees, mark.rutland,
	ryan.roberts, linuxppc-dev, linux-kernel, Michal Suchánek
In-Reply-To: <17eecc4d-ae7b-4c32-9edc-d3b8e131549c@kernel.org>

On Fri, Jun 26, 2026 at 04:31:47PM +0200, Christophe Leroy (CS GROUP) wrote:
> 
> 
> Le 24/06/2026 à 19:15, Mukesh Kumar Chaurasiya (IBM) a écrit :
> > After enabling GENERIC_ENTRY on PowerPC, seccomp filters using
> > SCMP_ACT_ERRNO without an explicit errnoRet value return ENOSYS
> > (Function not implemented) instead of the expected EPERM (Operation
> > not permitted).
> > 
> > The issue occurs in system_call_exception() when syscall_enter_from_user_mode()
> > returns -1 to indicate the syscall should be skipped (e.g., blocked by seccomp).
> > The current code treats this -1 as a syscall number and compares it against
> > NR_syscalls. Since -1 (when cast to unsigned long) is greater than NR_syscalls,
> > the code incorrectly returns -ENOSYS, overwriting the errno that seccomp
> > already set via syscall_set_return_value().
> > 
> > The generic entry code in syscall_trace_enter() calls __secure_computing(),
> > which sets the appropriate errno in regs->gpr[3] and returns -1 to signal
> > that the syscall should be skipped. However, the PowerPC syscall handler
> > was not checking for this -1 return value before validating the syscall
> > number.
> > 
> > Fix this by explicitly checking if syscall_enter_from_user_mode() returns
> > -1 and returning the value already set in regs->gpr[3] (the errno from
> > seccomp) before performing the syscall number validation.
> > 
> > This aligns PowerPC's behavior with other architectures using GENERIC_ENTRY
> > and restores correct seccomp errno handling.
> > 
> > Fixes: bee25f97ad24 ("powerpc: Enable GENERIC_ENTRY feature")
> > Reported-by: Michal Suchánek <msuchanek@suse.de>
> 
> Closes: https://lore.kernel.org/all/ajpp-_XnbF3UTM_E@kunlun.suse.cz/
> 
> > Signed-off-by: Mukesh Kumar Chaurasiya (IBM) <mkchauras@gmail.com>
> > ---
> >   arch/powerpc/kernel/syscall.c | 4 ++++
> >   1 file changed, 4 insertions(+)
> > 
> > diff --git a/arch/powerpc/kernel/syscall.c b/arch/powerpc/kernel/syscall.c
> > index a9da2af6efa8..5b58c8d396c8 100644
> > --- a/arch/powerpc/kernel/syscall.c
> > +++ b/arch/powerpc/kernel/syscall.c
> > @@ -22,6 +22,10 @@ notrace long system_call_exception(struct pt_regs *regs, unsigned long r0)
> >   	add_random_kstack_offset();
> >   	r0 = syscall_enter_from_user_mode(regs, r0);
> > +	/* Seccomp or ptrace may have set return value, skip syscall */
> > +	if (unlikely(r0 == -1L))
> 
> Is it really needed to add the L after 1 ?
> 
I guess it's not needed, but syscall_trace_enter is returning -1L, and i
wanted to keep it consistent to that.

Regards,
Mukesh
> > +		return regs->gpr[3];
> > +
> >   	if (unlikely(r0 >= NR_syscalls)) {
> >   		if (unlikely(trap_is_unsupported_scv(regs))) {
> >   			/* Unsupported scv vector */
> 


^ permalink raw reply

* Re: [PATCH] powerpc/syscall: Fix seccomp errno handling with GENERIC_ENTRY
From: Mukesh Kumar Chaurasiya @ 2026-06-29  4:50 UTC (permalink / raw)
  To: Michal Suchánek
  Cc: maddy, mpe, npiggin, chleroy, sshegde, mkchauras, kees,
	mark.rutland, ryan.roberts, linuxppc-dev, linux-kernel
In-Reply-To: <aj4UAGaPdaI1wEED@kunlun.suse.cz>

On Fri, Jun 26, 2026 at 07:54:08AM +0200, Michal Suchánek wrote:
> Hello,
> 
> On Wed, Jun 24, 2026 at 10:45:20PM +0530, Mukesh Kumar Chaurasiya (IBM) wrote:
> > After enabling GENERIC_ENTRY on PowerPC, seccomp filters using
> > SCMP_ACT_ERRNO without an explicit errnoRet value return ENOSYS
> > (Function not implemented) instead of the expected EPERM (Operation
> > not permitted).
> > 
> > The issue occurs in system_call_exception() when syscall_enter_from_user_mode()
> > returns -1 to indicate the syscall should be skipped (e.g., blocked by seccomp).
> > The current code treats this -1 as a syscall number and compares it against
> > NR_syscalls. Since -1 (when cast to unsigned long) is greater than NR_syscalls,
> > the code incorrectly returns -ENOSYS, overwriting the errno that seccomp
> > already set via syscall_set_return_value().
> > 
> > The generic entry code in syscall_trace_enter() calls __secure_computing(),
> > which sets the appropriate errno in regs->gpr[3] and returns -1 to signal
> > that the syscall should be skipped. However, the PowerPC syscall handler
> > was not checking for this -1 return value before validating the syscall
> > number.
> > 
> > Fix this by explicitly checking if syscall_enter_from_user_mode() returns
> > -1 and returning the value already set in regs->gpr[3] (the errno from
> > seccomp) before performing the syscall number validation.
> > 
> > This aligns PowerPC's behavior with other architectures using GENERIC_ENTRY
> > and restores correct seccomp errno handling.
> > 
> > Fixes: bee25f97ad24 ("powerpc: Enable GENERIC_ENTRY feature")
> > Reported-by: Michal Suchánek <msuchanek@suse.de>
> > Signed-off-by: Mukesh Kumar Chaurasiya (IBM) <mkchauras@gmail.com>
> > ---
> >  arch/powerpc/kernel/syscall.c | 4 ++++
> >  1 file changed, 4 insertions(+)
> > 
> > diff --git a/arch/powerpc/kernel/syscall.c b/arch/powerpc/kernel/syscall.c
> > index a9da2af6efa8..5b58c8d396c8 100644
> > --- a/arch/powerpc/kernel/syscall.c
> > +++ b/arch/powerpc/kernel/syscall.c
> > @@ -22,6 +22,10 @@ notrace long system_call_exception(struct pt_regs *regs, unsigned long r0)
> >  	add_random_kstack_offset();
> >  	r0 = syscall_enter_from_user_mode(regs, r0);
> >  
> > +	/* Seccomp or ptrace may have set return value, skip syscall */
> > +	if (unlikely(r0 == -1L))
> > +		return regs->gpr[3];
> 
> this should use syscall_get_error instead of accessing the register
> value directly. The error is represented differently in the scv and
> non-scv case, and for non-scv case this will return wrong value.
> 
> Thanks
> 
> Michal
> 
Yeah, Thanks.

Will send out a new version.

Regards,
Mukesh
> > +
> >  	if (unlikely(r0 >= NR_syscalls)) {
> >  		if (unlikely(trap_is_unsupported_scv(regs))) {
> >  			/* Unsupported scv vector */
> > -- 
> > 2.54.0
> > 


^ permalink raw reply

* Re: [PATCH 13/16] perf: Use sysfs_emit() for cpumask show callbacks
From: Yury Norov @ 2026-06-27 17:49 UTC (permalink / raw)
  To: David Laight
  Cc: Robin Murphy, Andrew Morton, Rasmus Villemoes, Russell King,
	Frank Li, Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
	Madhavan Srinivasan, Michael Ellerman, Nicholas Piggin,
	Christophe Leroy (CS GROUP), Peter Zijlstra, Ingo Molnar,
	Arnaldo Carvalho de Melo, Namhyung Kim, Mark Rutland,
	Alexander Shishkin, Jiri Olsa, Ian Rogers, Adrian Hunter,
	James Clark, Thomas Gleixner, Borislav Petkov, Dave Hansen,
	H. Peter Anvin, Rafael J. Wysocki, Len Brown, Greg Kroah-Hartman,
	Danilo Krummrich, Chanwoo Choi, MyungJoo Ham, Kyungmin Park,
	Heiko Stuebner, Lorenzo Pieralisi, Xu Yilun, Tom Rix,
	Moritz Fischer, Yicong Yang, Jonathan Cameron, Dennis Dalessandro,
	Jason Gunthorpe, Leon Romanovsky, Dan Williams, Vishal Verma,
	Dave Jiang, Ira Weiny, Bjorn Helgaas, Shuai Xue, Will Deacon,
	Jiucheng Xu, Neil Armstrong, Kevin Hilman, Jerome Brunet,
	Martin Blumenstingl, Jing Zhang, Xu Yang, Linu Cherian,
	Gowthami Thiagarajan, Ji Sheng Teoh, Khuong Dinh, Daniel Lezcano,
	Zhang Rui, Lukasz Luba, Yury Norov, Kees Cook,
	Thomas Weißschuh, Aboorva Devarajan, Ritesh Harjani (IBM),
	Ilkka Koskinen, Besar Wicaksono, Ma Ke, Chengwen Feng,
	linux-arm-kernel, imx, linux-kernel, linuxppc-dev,
	linux-perf-users, linux-acpi, driver-core, linux-pm,
	linux-rockchip, linux-fpga, linux-rdma, nvdimm, linux-pci,
	linux-amlogic, linux-cxl, linux-arm-msm
In-Reply-To: <20260529130619.12f24264@pumpkin>

On Fri, May 29, 2026 at 01:06:19PM +0100, David Laight wrote:
> On Fri, 29 May 2026 12:05:08 +0100
> Robin Murphy <robin.murphy@arm.com> wrote:
> 
> > On 2026-05-28 7:36 pm, Yury Norov wrote:
> > > These callbacks are sysfs show paths.
> > > 
> > > Use sysfs_emit() and cpumask_pr_args() to emit the masks.
> > > 
> > > This prepares for removing cpumap_print_to_pagebuf().  
> > 
> > TBH, looking at this diff I think it only shows the value of having a 
> > helper to abstract the boilerplate...
> > 
> > I'm not sure I agree with the argument of removing something entirely 
> > just because it may occasionally be misused, but could we at least have 
> > something like:
> > 
> > #define sysfs_emit_cpumask(buf, mask) \
> > 	sysfs_emit((buf), "%*pbl\n", cpumask_pr_args(mask))
> >
> > to save the mess in all the many places where the current 
> > cpumap_print_to_pagebuf() usage _is_ entirely appropriate?

This way you have to add 2 wrappers:

 #define sysfs_emit_cpulist(buf, mask) \
 	sysfs_emit((buf), "%*pbl\n", cpumask_pr_args(mask))

 and

 #define sysfs_emit_cpumask(buf, mask) \
 	sysfs_emit((buf), "%*pb\n", cpumask_pr_args(mask))

There are people who complain even about DIV_ROUND_UP(), how hard it is
to keep all that helpers in memory, and all that things.

https://lore.kernel.org/all/20260304124805.GB2277644@noisy.programming.kicks-ass.net/

Disagree about DIV_ROUND_UP() (because yeah, I'm bad in math), but
this sysfs_emit_cpumask() is a complete syntax redundancy.

Once we have it, people will do this type of things:

        tmp = kmalloc(PAGE_SIZE);
        sysfs_emit_cpumask(tmp, mask);
        sysfs_emit(buf, "my prefix: %s\n", tmp);
        kfree(tmp);

Patch #1 in this series is one example. My series that removes
bitmap_print_to_pagebuf() will give you more:

https://lore.kernel.org/all/20260303200842.124996-2-ynorov@nvidia.com/

It doesn't mean that *you* will misuse the API. It means that *I* will
have to inspect the codebase for that type of bugs periodically.

So, the overall state is simple: we've got well-established
printf()-like functions that people know and understand, and we also
have exotic APIs here and there with a non-standard interface and a
clear potential to misuse. In this case, they have historical roots,
but now we don't need them.

> That has the advantage of letting you change how it is done (again)
> without having to find all the callers.

You mean things like silencing the prints or adding a prefix?

If you believe that perf subsystem would benefit from it - that's
OK. Just please keep it local. The kernel globally doesn't need to
'change how it is done' beyond the lib/vsprintf.  The kernel really
needs people to use something that the other people are familiar with.

Thanks,
Yury


^ permalink raw reply

* Re: [PATCH 0/1] KVM: powerpc/book3s_hv: Handle deferred CFS bandwidth throttle on guest re-entry
From: Harsh Prateek Bora @ 2026-06-27 12:12 UTC (permalink / raw)
  To: Vishal Chourasia
  Cc: maddy, Nicholas Piggin, mpe, chleroy, Gautam Menghani, bigeasy,
	linuxppc-dev, kvm, linux-kernel
In-Reply-To: <20260626105449.2897924-2-vishalc@linux.ibm.com>

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

On Fri, 26 Jun, 2026, 4:26 pm Vishal Chourasia, <vishalc@linux.ibm.com>
wrote:

> This series fixes a KVM scheduling bug on Book3S HV (POWER8/POWER9/POWER10)
> where a guest VM under a cpu.max bandwidth limit can run arbitrarily past
> its
> quota and then appear completely frozen for minutes afterwards.
>
> == Background ==
>
> Commit 2cd571245b43 ("sched/fair: Add related data structure for task based
> throttle"), merged in v6.18, changed how CFS bandwidth throttling enforces
> its limit. Previously, throttle_cfs_rq() dequeued tasks directly. Under the
> new scheme it queues a task_work item via task_work_add(..., TWA_RESUME),
> sets TIF_NOTIFY_RESUME, and relies on that work running on the kernel
> return
> path to actually dequeue the task.
>
> For KVM guests this means the work must be drained before each guest entry,
> not just on the normal syscall return path. commit 935ace2fb5cc ("entry:
> Provide infrastructure for work before transitioning to guest mode")
> introduced kvm_xfer_to_guest_mode_handle_work() for exactly this purpose.
> x86 (commit 72c3c0fe54a3), arm64 (commit 6caa5812e2d1), riscv, s390, and
> loongarch all adopted it. Book3S HV did not. [1]
>

IMHO, there is significant info in the cover letter (Background and RCA)
that deserves to be part of the patch commit log.

Thanks
Harsh

>
> == Root Cause ==
>
> Book3S HV's vCPU run loops — kvmhv_run_single_vcpu() for POWER9+ and
> kvmppc_run_vcpu() for pre-POWER9 — only test TIF_SIGPENDING and
> TIF_NEED_RESCHED before re-entering the guest. TIF_NOTIFY_RESUME is never
> checked, and the deferred throttle task_work therefore never runs while a
> vCPU is inside the run loop.
>
> For a CPU-bound guest that generates few KVM exits back to QEMU user space
> (e.g. a compute-heavy or busy-looping workload), the vCPU thread never
> returns to user mode. throttle_cfs_rq() sets cfs_rq->throttled = 1 and
> queues the task_work, but the guest continues to run unchecked.
> cfs_rq->runtime_remaining goes increasingly negative with every scheduling
> period while the throttle flag sits ignored.
>
> The only mechanism recovering that debt is the periodic bandwidth timer
> replenishment: 30 ms of quota is added per 100 ms period. When
> runtime_remaining has drifted hundreds of seconds negative, recovering to
> zero at 300 ms/s takes minutes — during which the cgroup is legitimately
> throttled and the VM is completely frozen once it finally exits to user
> space.
>
> == Debugging ==
>
> vCPU was placed in a cgroup where CPU bandwidth limits were set.
> quota = 30ms
> period = 100ms
>
> The bug was diagnosed using a bpftrace script probing throttle_cfs_rq()
> and unthrottle_cfs_rq() and sampling cfs_rq->runtime_remaining every
> second. The trace shows the debt accumulation phase, the slow recovery
> phase, and the immediate re-throttle on resumption:
>
>   Debt accumulation (vCPU in guest, no exits):
>     +1471 s  runtime_remaining=-209702865115 ns  throttled=1
>     +1472 s  runtime_remaining=-210402866357 ns  throttled=1
>     ...                                    # ~-700 ms/s (growing debt)
>     +1477 s  runtime_remaining=-213902833931 ns  throttled=1
>
>   Recovery (vCPU exits to QEMU user space; bandwidth timer replenishes):
>     +1478 s  runtime_remaining=-213617443453 ns  throttled=1
>     +1479 s  runtime_remaining=-213317443453 ns  throttled=1
>     ...                                    # ~+300 ms/s (30ms quota/100ms)
>
>   After ~710 seconds of recovery, debt reaches zero:
>     ──── unthrottle_cfs_rq @ cpu=768  +2190.029568131 s ────
>       runtime_remaining = 1 ns            # just crossed zero
>
>   The vCPU immediately re-enters the guest and over-runs its quota again:
>     ──── throttle_cfs_rq @ cpu=768  +2190.055327252 s ────
>       runtime_remaining = -5667293 ns     # 26 ms of debt already
>
>   The cycle then repeats identically from a fresh -700 ms/s accumulation.
>
> cpu.stat confirms the pathology — 100% throttle rate and virtually all
> CPU time accumulated in kernel (KVM) mode:
>
>   nr_periods   = 117457
>   nr_throttled = 117457     # every single period
>   system_usec  = 4334782636 # >99.99% kernel time (QEMU in KVM_RUN)
>
> strace of the QEMU vCPU thread confirms long stretches where
> ioctl(KVM_RUN) does not return — the vCPU is running in guest mode
> with no VM-exits reaching user space.
>
> == Fix Summary ==
>
> Opt Book3S HV into VIRT_XFER_TO_GUEST_WORK and drain pending guest-mode
> work (including the deferred CFS throttle task_work) on every guest
> re-entry in both run loops. The changes are supersets of the existing
> need_resched() checks and do not alter the signal or exit accounting.
>
> [1]
> https://lore.kernel.org/all/20250421102837.78515-2-sshegde@linux.ibm.com/
>
> Vishal Chourasia (1):
>   KVM: powerpc/book3s_hv: Use generic xfer to guest work function
>
>  arch/powerpc/kvm/Kconfig     |  1 +
>  arch/powerpc/kvm/book3s_hv.c | 58 +++++++++++++++++++++++++++++++-----
>  2 files changed, 52 insertions(+), 7 deletions(-)
>
> --
> 2.54.0
>
>
>

[-- Attachment #2: Type: text/html, Size: 6292 bytes --]

^ permalink raw reply

* [PATCH v2 1/2] powerpc/rtasd: Use struct_size() to simplify log_rtas_len()
From: Thorsten Blum @ 2026-06-27 10:47 UTC (permalink / raw)
  To: Madhavan Srinivasan, Michael Ellerman, Nicholas Piggin,
	Christophe Leroy (CS GROUP), Haren Myneni, Mahesh Salgaonkar,
	Tyrel Datwyler
  Cc: Thorsten Blum, linuxppc-dev, linux-kernel

Now that struct rtas_error_log uses a flexible array member for the
extended log buffer, use struct_size() to calculate the total RTAS error
log size and avoid using the hard-coded header size of 8 bytes.

Use min() to replace the open-coded implementation while at it.

Signed-off-by: Thorsten Blum <thorsten.blum@linux.dev>
---
No changes in patch 1/2.
---
 arch/powerpc/kernel/rtasd.c | 19 ++++++-------------
 1 file changed, 6 insertions(+), 13 deletions(-)

diff --git a/arch/powerpc/kernel/rtasd.c b/arch/powerpc/kernel/rtasd.c
index 6336ec9aedd0..fd40864bdb70 100644
--- a/arch/powerpc/kernel/rtasd.c
+++ b/arch/powerpc/kernel/rtasd.c
@@ -10,6 +10,7 @@
 #include <linux/sched.h>
 #include <linux/kernel.h>
 #include <linux/of.h>
+#include <linux/overflow.h>
 #include <linux/poll.h>
 #include <linux/proc_fs.h>
 #include <linux/init.h>
@@ -160,25 +161,17 @@ static void printk_log_rtas(char *buf, int len)
 
 static int log_rtas_len(char * buf)
 {
-	int len;
+	size_t len;
 	struct rtas_error_log *err;
-	uint32_t extended_log_length;
+	u32 extended_log_length;
 
-	/* rtas fixed header */
-	len = 8;
 	err = (struct rtas_error_log *)buf;
-	extended_log_length = rtas_error_extended_log_length(err);
-	if (rtas_error_extended(err) && extended_log_length) {
-
-		/* extended header */
-		len += extended_log_length;
-	}
+	extended_log_length = rtas_error_extended(err) ? rtas_error_extended_log_length(err) : 0;
+	len = struct_size(err, buffer, extended_log_length);
 
 	if (rtas_error_log_max == 0)
 		rtas_error_log_max = rtas_get_error_log_max();
-
-	if (len > rtas_error_log_max)
-		len = rtas_error_log_max;
+	len = min(len, rtas_error_log_max);
 
 	return len;
 }

base-commit: ab8bbf8024b7434e2b630965fd373fba5b89f29f


^ permalink raw reply related

* [PATCH v2 2/2] powerpc/pseries/ras: Use struct_size() to simplify fwnmi_get_errinfo()
From: Thorsten Blum @ 2026-06-27 10:47 UTC (permalink / raw)
  To: Madhavan Srinivasan, Michael Ellerman, Nicholas Piggin,
	Christophe Leroy (CS GROUP)
  Cc: Thorsten Blum, linuxppc-dev, linux-kernel
In-Reply-To: <20260627104730.276858-3-thorsten.blum@linux.dev>

Now that struct rtas_error_log uses a flexible array member for the
extended log buffer, use struct_size() to calculate the total RTAS error
log size and avoid using the hard-coded header size of 8 bytes.

Signed-off-by: Thorsten Blum <thorsten.blum@linux.dev>
---
Changes in v2:
- memcpy_and_pad() cannot be used in real mode; use memset() + memcpy()
- v1: https://lore.kernel.org/r/20260626184750.166642-4-thorsten.blum@linux.dev/
---
 arch/powerpc/platforms/pseries/ras.c | 16 +++++++---------
 1 file changed, 7 insertions(+), 9 deletions(-)

diff --git a/arch/powerpc/platforms/pseries/ras.c b/arch/powerpc/platforms/pseries/ras.c
index adafd593d9d3..7b8713bdd978 100644
--- a/arch/powerpc/platforms/pseries/ras.c
+++ b/arch/powerpc/platforms/pseries/ras.c
@@ -7,6 +7,7 @@
 #include <linux/interrupt.h>
 #include <linux/irq.h>
 #include <linux/of.h>
+#include <linux/overflow.h>
 #include <linux/fs.h>
 #include <linux/reboot.h>
 #include <linux/irq_work.h>
@@ -440,6 +441,8 @@ static __be64 *fwnmi_get_savep(struct pt_regs *regs)
 static struct rtas_error_log *fwnmi_get_errinfo(struct pt_regs *regs)
 {
 	struct rtas_error_log *h;
+	u32 extended_log_length;
+	size_t len;
 	__be64 *savep;
 
 	savep = fwnmi_get_savep(regs);
@@ -449,17 +452,12 @@ static struct rtas_error_log *fwnmi_get_errinfo(struct pt_regs *regs)
 	regs->gpr[3] = be64_to_cpu(savep[0]); /* restore original r3 */
 
 	h = (struct rtas_error_log *)&savep[1];
+	extended_log_length = rtas_error_extended(h) ? rtas_error_extended_log_length(h) : 0;
+	len = struct_size(h, buffer, extended_log_length);
+	len = min(len, RTAS_ERROR_LOG_MAX);
 	/* Use the per cpu buffer from paca to store rtas error log */
 	memset(local_paca->mce_data_buf, 0, RTAS_ERROR_LOG_MAX);
-	if (!rtas_error_extended(h)) {
-		memcpy(local_paca->mce_data_buf, h, sizeof(__u64));
-	} else {
-		int len, error_log_length;
-
-		error_log_length = 8 + rtas_error_extended_log_length(h);
-		len = min_t(int, error_log_length, RTAS_ERROR_LOG_MAX);
-		memcpy(local_paca->mce_data_buf, h, len);
-	}
+	memcpy(local_paca->mce_data_buf, h, len);
 
 	return (struct rtas_error_log *)local_paca->mce_data_buf;
 }


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox