* [PATCH v7 0/4] driver core: unify the release path for dynamically allocated platform devices
@ 2026-07-13 15:11 Bartosz Golaszewski
2026-07-13 15:11 ` [PATCH v7 1/4] platform/surface: gpe: use platform_device_register_full() Bartosz Golaszewski
` (3 more replies)
0 siblings, 4 replies; 8+ messages in thread
From: Bartosz Golaszewski @ 2026-07-13 15:11 UTC (permalink / raw)
To: Maximilian Luz, Hans de Goede, Ilpo Järvinen, Matthew Brost,
Thomas Hellström, Rodrigo Vivi, David Airlie, Simona Vetter,
Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich
Cc: brgl, platform-driver-x86, linux-kernel, intel-xe, dri-devel,
driver-core, Bartosz Golaszewski
This is a follow-up to the fix in platform device code that introduced
platform_device_release_full(). With the two prerequisite patch series
applied and with the two last remaining offenders converted, we can
finally unify the release path for platform devices using
platform_device_register_full() as well as those using the split
approach of platform_device_alloc() + platform_device_add().
With an ack from DRM and platform maintainers, this can go through the
driver core tree.
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
---
Changes in v7:
- Drop patch 1/6 as it's already upstream
- Drop patch 5/6 as its been replaced by another changeset sent upstream
- Lock device when calling device_is_bound() in kunit tests
- Fix invalid reference put of the firmware node in surface_gpe
- Rebase on top of recent driver core changes
- Link to v6: https://patch.msgid.link/20260513-swnode-remove-on-dev-unreg-v6-0-f9c58939df27@oss.qualcomm.com
Changes in v6:
- Make patch 1 a minimal fix that can go into v7.1
- Don't provide platform_device_add_software_node(), instead: rework
drivers in question to use other existing interfaces instead
- Fix life-time issues in the xe_i2c patch and remove unneeded fwnode
removal
- Fix typos and tweak commit messages
- Link to v5: https://patch.msgid.link/20260512-swnode-remove-on-dev-unreg-v5-0-0035eff63812@oss.qualcomm.com
Changes in v5:
- Provide a helper for users of platform_device_alloc() that wish to
assign a software node to a platform device
- Rework drivers that could regress after the swnode leak fix
- Split the series into several patches as necessary
- Link to v4: https://patch.msgid.link/20260430-swnode-remove-on-dev-unreg-v4-0-01574da0aed3@oss.qualcomm.com
Changes in v4:
- Drop redundant !pdevinfo->swnode check in
platform_device_register_full()
- Handle all three combinations with which two software nodes can be
assigned to a platform device
- Extend the new kunit test to verify all three combinations
- Link to v3: https://patch.msgid.link/20260428-swnode-remove-on-dev-unreg-v3-0-533bdc71b22e@oss.qualcomm.com
Changes in v3:
- Make sure the reference of the primary software node we possibly take,
is always released by moving the get() before calls that may fail
- Don't allow passing two software nodes
- Add a test case for that situation
- Link to v2: https://patch.msgid.link/20260423-swnode-remove-on-dev-unreg-v2-0-0e5213cde2ed@oss.qualcomm.com
Changes in v2:
- Change the order between removing the software node and dropping the
reference to the device's OF node
- Address a situation where a software node is used as the primary
firmware node
- Add a patch adding a new kunit helper
- Add another test case
- Link to v1: https://patch.msgid.link/20260410-swnode-remove-on-dev-unreg-v1-0-cd7d305f3db2@oss.qualcomm.com
---
Bartosz Golaszewski (4):
platform/surface: gpe: use platform_device_register_full()
drm/xe/i2c: use device_create_managed_software_node()
driver core: platform: unify release path
driver core: platform: tests: add test cases for correct swnode removal
drivers/base/platform.c | 19 ++--
drivers/base/test/platform-device-test.c | 180 +++++++++++++++++++++++++++++++
drivers/gpu/drm/xe/xe_i2c.c | 22 ++--
drivers/gpu/drm/xe/xe_i2c.h | 1 -
drivers/platform/surface/surface_gpe.c | 39 ++-----
5 files changed, 210 insertions(+), 51 deletions(-)
---
base-commit: 28a74302c6acf69768804a279eeb9eb2d9124956
change-id: 20260410-swnode-remove-on-dev-unreg-42bfc4b23ba8
prerequisite-message-id: 20260706-pdev-fwnode-ref-v3-0-1ff028e33779@oss.qualcomm.com
prerequisite-patch-id: 76dea1205a0905915a03c0d7c9e94399c92501e5
prerequisite-patch-id: f6c074c48e5eb2f5e92c57fa1320b6b0fab3ce1d
prerequisite-patch-id: 07a0c422cc3233e2fe87cb9bbd6baab608d0de82
prerequisite-patch-id: 5e980cccbfdc82adef1573f8a8a9639689836432
prerequisite-patch-id: bb193e0baf41714061b83b3925cc4e28036f2b9c
prerequisite-patch-id: 5ff0ac2c7c4cc548692ed5f567db30e27e920786
prerequisite-patch-id: deb425beb85441169aad53c1282f35df513b8015
prerequisite-patch-id: b0e9e719c8daf30d97c110478a5c3c7af106472c
prerequisite-patch-id: 9f118a7cdb92893ed4e913c7c98bbdb766ff18c4
prerequisite-patch-id: 1b2391d8295e80086182f55e5189b9aa588687f8
prerequisite-patch-id: 986a0f4f9a682a255f6eef0fe2128a86c595ece5
prerequisite-patch-id: aae63ceb82cbe6f65810751e596e542df0ff513b
prerequisite-patch-id: 155601719d4f5c433fd7dce63842007f255bec81
prerequisite-patch-id: f9bebca5cd8f5ebf9b52e9c688a3c83ddc7efb7f
prerequisite-patch-id: aff2299bcf258e4e6dd1f01c591141385f31fdff
prerequisite-patch-id: 51cfbca3ab011688be133a259d598007289237c4
prerequisite-patch-id: 0e9bddaa83a1f5a99745c2f50f7c3213d7b17fbd
prerequisite-patch-id: 7ca12b7bbf0168eadaece00587dad68e1ff3cbec
prerequisite-patch-id: 8d483c4b391ed1b95897c951555e7ab6c5807e2a
prerequisite-patch-id: 495173352a585bdfd0c58721c8be0558d06a5335
prerequisite-message-id: 20260713-swnode-fw-devlink-v4-0-d4f2dee27ad9@oss.qualcomm.com
prerequisite-patch-id: d7f936bb1eb8e9fdc0a6a35770e376d36a0b595e
prerequisite-patch-id: 77b6a4eb67cd6bcb6fbc974a4d0368be6a2810ef
prerequisite-patch-id: 744bdc8ea502dab2db13b1cbe963c1db49c951f0
prerequisite-patch-id: 185deea7fe874a7d4a70c93e0b60cc3bbc75ec25
prerequisite-patch-id: d9e41fb8f4070e53ac293db82ccbad83c8d2a40b
Best regards,
--
Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH v7 1/4] platform/surface: gpe: use platform_device_register_full()
2026-07-13 15:11 [PATCH v7 0/4] driver core: unify the release path for dynamically allocated platform devices Bartosz Golaszewski
@ 2026-07-13 15:11 ` Bartosz Golaszewski
2026-07-13 15:11 ` [PATCH v7 2/4] drm/xe/i2c: use device_create_managed_software_node() Bartosz Golaszewski
` (2 subsequent siblings)
3 siblings, 0 replies; 8+ messages in thread
From: Bartosz Golaszewski @ 2026-07-13 15:11 UTC (permalink / raw)
To: Maximilian Luz, Hans de Goede, Ilpo Järvinen, Matthew Brost,
Thomas Hellström, Rodrigo Vivi, David Airlie, Simona Vetter,
Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich
Cc: brgl, platform-driver-x86, linux-kernel, intel-xe, dri-devel,
driver-core, Bartosz Golaszewski
Creating a software node for a given set of properties and adding it to
a platform device can be achieved with a single call to
platform_device_register_full(). There's nothing in this driver that
suggests using the more fine-grained interfaces was intentional so
switch to using the high-level helper.
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
---
drivers/platform/surface/surface_gpe.c | 39 ++++++++++------------------------
1 file changed, 11 insertions(+), 28 deletions(-)
diff --git a/drivers/platform/surface/surface_gpe.c b/drivers/platform/surface/surface_gpe.c
index 40896a8544b0a4da4261ea881b1eaed62d93b32b..4ca7ad76f3b9d2b6c455a5d5b37f23a85eb69c69 100644
--- a/drivers/platform/surface/surface_gpe.c
+++ b/drivers/platform/surface/surface_gpe.c
@@ -290,9 +290,9 @@ static struct platform_device *surface_gpe_device;
static int __init surface_gpe_init(void)
{
+ struct platform_device_info pdevinfo;
const struct dmi_system_id *match;
struct platform_device *pdev;
- struct fwnode_handle *fwnode;
int status;
match = dmi_first_match(dmi_lid_device_table);
@@ -305,44 +305,27 @@ static int __init surface_gpe_init(void)
if (status)
return status;
- fwnode = fwnode_create_software_node(match->driver_data, NULL);
- if (IS_ERR(fwnode)) {
- status = PTR_ERR(fwnode);
- goto err_node;
- }
-
- pdev = platform_device_alloc("surface_gpe", PLATFORM_DEVID_NONE);
- if (!pdev) {
- status = -ENOMEM;
- goto err_alloc;
+ pdevinfo = (struct platform_device_info){
+ .name = "surface_gpe",
+ .id = PLATFORM_DEVID_NONE,
+ .properties = match->driver_data,
+ };
+
+ pdev = platform_device_register_full(&pdevinfo);
+ if (IS_ERR(pdev)) {
+ platform_driver_unregister(&surface_gpe_driver);
+ return PTR_ERR(pdev);
}
- platform_device_set_fwnode(pdev, fwnode);
-
- status = platform_device_add(pdev);
- if (status)
- goto err_add;
-
surface_gpe_device = pdev;
return 0;
-
-err_add:
- platform_device_put(pdev);
-err_alloc:
- fwnode_remove_software_node(fwnode);
-err_node:
- platform_driver_unregister(&surface_gpe_driver);
- return status;
}
module_init(surface_gpe_init);
static void __exit surface_gpe_exit(void)
{
- struct fwnode_handle *fwnode = surface_gpe_device->dev.fwnode;
-
platform_device_unregister(surface_gpe_device);
platform_driver_unregister(&surface_gpe_driver);
- fwnode_remove_software_node(fwnode);
}
module_exit(surface_gpe_exit);
--
2.47.3
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH v7 2/4] drm/xe/i2c: use device_create_managed_software_node()
2026-07-13 15:11 [PATCH v7 0/4] driver core: unify the release path for dynamically allocated platform devices Bartosz Golaszewski
2026-07-13 15:11 ` [PATCH v7 1/4] platform/surface: gpe: use platform_device_register_full() Bartosz Golaszewski
@ 2026-07-13 15:11 ` Bartosz Golaszewski
2026-07-13 15:37 ` Rodrigo Vivi
2026-07-13 15:11 ` [PATCH v7 3/4] driver core: platform: unify release path Bartosz Golaszewski
2026-07-13 15:12 ` [PATCH v7 4/4] driver core: platform: tests: add test cases for correct swnode removal Bartosz Golaszewski
3 siblings, 1 reply; 8+ messages in thread
From: Bartosz Golaszewski @ 2026-07-13 15:11 UTC (permalink / raw)
To: Maximilian Luz, Hans de Goede, Ilpo Järvinen, Matthew Brost,
Thomas Hellström, Rodrigo Vivi, David Airlie, Simona Vetter,
Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich
Cc: brgl, platform-driver-x86, linux-kernel, intel-xe, dri-devel,
driver-core, Bartosz Golaszewski
This driver intentionally uses the fine-grained approach to creating
platform devices. It assigns a software node as the primary firmware
node of the device it creates. Ahead of improving the reference counting
of platform device software nodes, switch to using
device_create_managed_software_node(). This way, we create a dynamic
software node whose life-time is tied to the device to which it's
assigned.
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
---
drivers/gpu/drm/xe/xe_i2c.c | 22 ++++++++--------------
drivers/gpu/drm/xe/xe_i2c.h | 1 -
2 files changed, 8 insertions(+), 15 deletions(-)
diff --git a/drivers/gpu/drm/xe/xe_i2c.c b/drivers/gpu/drm/xe/xe_i2c.c
index 3d80eafb9a1318996d10db11e14af4cfde07ba55..af6e973e5e68125a9c006bb646447ca6e377a6e6 100644
--- a/drivers/gpu/drm/xe/xe_i2c.c
+++ b/drivers/gpu/drm/xe/xe_i2c.c
@@ -93,13 +93,8 @@ static int xe_i2c_register_adapter(struct xe_i2c *i2c)
{
struct pci_dev *pci = to_pci_dev(i2c->drm_dev);
struct platform_device *pdev;
- struct fwnode_handle *fwnode;
int ret;
- fwnode = fwnode_create_software_node(xe_i2c_adapter_properties, NULL);
- if (IS_ERR(fwnode))
- return PTR_ERR(fwnode);
-
/*
* Not using platform_device_register_full() here because we don't have
* a handle to the platform_device before it returns. xe_i2c_notifier()
@@ -107,10 +102,14 @@ static int xe_i2c_register_adapter(struct xe_i2c *i2c)
* platform_device_register_full() is done.
*/
pdev = platform_device_alloc(adapter_name, pci_dev_id(pci));
- if (!pdev) {
- ret = -ENOMEM;
- goto err_fwnode_remove;
- }
+ if (!pdev)
+ return -ENOMEM;
+
+ ret = device_create_managed_software_node(&pdev->dev,
+ xe_i2c_adapter_properties,
+ NULL);
+ if (ret)
+ goto err_pdev_put;
if (i2c->adapter_irq) {
struct resource res;
@@ -123,8 +122,6 @@ static int xe_i2c_register_adapter(struct xe_i2c *i2c)
}
pdev->dev.parent = i2c->drm_dev;
- platform_device_set_fwnode(pdev, fwnode);
- i2c->adapter_node = fwnode;
i2c->pdev = pdev;
ret = platform_device_add(pdev);
@@ -135,8 +132,6 @@ static int xe_i2c_register_adapter(struct xe_i2c *i2c)
err_pdev_put:
platform_device_put(pdev);
-err_fwnode_remove:
- fwnode_remove_software_node(fwnode);
return ret;
}
@@ -144,7 +139,6 @@ static int xe_i2c_register_adapter(struct xe_i2c *i2c)
static void xe_i2c_unregister_adapter(struct xe_i2c *i2c)
{
platform_device_unregister(i2c->pdev);
- fwnode_remove_software_node(i2c->adapter_node);
}
/**
diff --git a/drivers/gpu/drm/xe/xe_i2c.h b/drivers/gpu/drm/xe/xe_i2c.h
index 425d8160835f4648891ff75f2a9c06284241710e..b28229f056c5a664e1d41dc0cc1978782fe1c2f1 100644
--- a/drivers/gpu/drm/xe/xe_i2c.h
+++ b/drivers/gpu/drm/xe/xe_i2c.h
@@ -30,7 +30,6 @@ struct xe_i2c_endpoint {
};
struct xe_i2c {
- struct fwnode_handle *adapter_node;
struct platform_device *pdev;
struct i2c_adapter *adapter;
struct i2c_client *client[XE_I2C_MAX_CLIENTS];
--
2.47.3
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH v7 3/4] driver core: platform: unify release path
2026-07-13 15:11 [PATCH v7 0/4] driver core: unify the release path for dynamically allocated platform devices Bartosz Golaszewski
2026-07-13 15:11 ` [PATCH v7 1/4] platform/surface: gpe: use platform_device_register_full() Bartosz Golaszewski
2026-07-13 15:11 ` [PATCH v7 2/4] drm/xe/i2c: use device_create_managed_software_node() Bartosz Golaszewski
@ 2026-07-13 15:11 ` Bartosz Golaszewski
2026-07-13 18:45 ` Danilo Krummrich
2026-07-13 15:12 ` [PATCH v7 4/4] driver core: platform: tests: add test cases for correct swnode removal Bartosz Golaszewski
3 siblings, 1 reply; 8+ messages in thread
From: Bartosz Golaszewski @ 2026-07-13 15:11 UTC (permalink / raw)
To: Maximilian Luz, Hans de Goede, Ilpo Järvinen, Matthew Brost,
Thomas Hellström, Rodrigo Vivi, David Airlie, Simona Vetter,
Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich
Cc: brgl, platform-driver-x86, linux-kernel, intel-xe, dri-devel,
driver-core, Bartosz Golaszewski
With no drivers that manually assign software nodes to platform devices
created with platform_device_alloc(), we can now unify the release path
and remove platform_device_release_full().
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
---
drivers/base/platform.c | 19 +++++++++++--------
1 file changed, 11 insertions(+), 8 deletions(-)
diff --git a/drivers/base/platform.c b/drivers/base/platform.c
index a71015f1d915340aa01ebfae67196d4d7ccecba8..1df1a9ebd59ca6e0efdcd4038b100bebe224ee82 100644
--- a/drivers/base/platform.c
+++ b/drivers/base/platform.c
@@ -599,6 +599,7 @@ static void platform_device_release(struct device *dev)
struct platform_object *pa = container_of(dev, struct platform_object,
pdev.dev);
+ device_remove_software_node(dev);
fwnode_handle_put(pa->pdev.dev.fwnode);
kfree(pa->pdev.dev.platform_data);
kfree(pa->pdev.mfd_cell);
@@ -606,12 +607,6 @@ static void platform_device_release(struct device *dev)
kfree(pa);
}
-static void platform_device_release_full(struct device *dev)
-{
- device_remove_software_node(dev);
- platform_device_release(dev);
-}
-
/**
* platform_device_alloc - create a platform device
* @name: base name of the device we're adding
@@ -933,6 +928,16 @@ struct platform_device *platform_device_register_full(const struct platform_devi
pdev->dev.coherent_dma_mask = pdevinfo->dma_mask;
}
+ /*
+ * If the primary firmware node is a software node and there's no
+ * secondary firmware node, the primary will be affected by the call
+ * to device_remove_software_node() in platform_device_release() and
+ * its reference count will be dropped by one. Take another reference
+ * here to make it have no effect.
+ */
+ if (is_software_node(pdevinfo->fwnode))
+ fwnode_handle_get(pdevinfo->fwnode);
+
ret = platform_device_add_resources(pdev, pdevinfo->res, pdevinfo->num_res);
if (ret)
goto err;
@@ -945,8 +950,6 @@ struct platform_device *platform_device_register_full(const struct platform_devi
ret = device_add_software_node(&pdev->dev, pdevinfo->swnode);
if (ret)
goto err;
-
- pdev->dev.release = platform_device_release_full;
} else if (pdevinfo->properties) {
ret = device_create_managed_software_node(&pdev->dev,
pdevinfo->properties, NULL);
--
2.47.3
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH v7 4/4] driver core: platform: tests: add test cases for correct swnode removal
2026-07-13 15:11 [PATCH v7 0/4] driver core: unify the release path for dynamically allocated platform devices Bartosz Golaszewski
` (2 preceding siblings ...)
2026-07-13 15:11 ` [PATCH v7 3/4] driver core: platform: unify release path Bartosz Golaszewski
@ 2026-07-13 15:12 ` Bartosz Golaszewski
3 siblings, 0 replies; 8+ messages in thread
From: Bartosz Golaszewski @ 2026-07-13 15:12 UTC (permalink / raw)
To: Maximilian Luz, Hans de Goede, Ilpo Järvinen, Matthew Brost,
Thomas Hellström, Rodrigo Vivi, David Airlie, Simona Vetter,
Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich
Cc: brgl, platform-driver-x86, linux-kernel, intel-xe, dri-devel,
driver-core, Bartosz Golaszewski
Extend the kunit module for platform devices with test cases verifying
that the same software node can be added to platform devices repeatedly.
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
---
drivers/base/test/platform-device-test.c | 180 +++++++++++++++++++++++++++++++
1 file changed, 180 insertions(+)
diff --git a/drivers/base/test/platform-device-test.c b/drivers/base/test/platform-device-test.c
index 6355a2231b741791b54eb78af42e13f31f745184..9ce563f76aad6943472dc78f3639ea1a2d72dbde 100644
--- a/drivers/base/test/platform-device-test.c
+++ b/drivers/base/test/platform-device-test.c
@@ -1,12 +1,15 @@
// SPDX-License-Identifier: GPL-2.0
+#include <kunit/fwnode.h>
#include <kunit/platform_device.h>
#include <kunit/resource.h>
#include <linux/device.h>
#include <linux/device/bus.h>
+#include <linux/fwnode.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
+#include <linux/property.h>
#define DEVICE_NAME "test"
@@ -253,9 +256,186 @@ static struct kunit_suite platform_device_match_test_suite = {
.test_cases = platform_device_match_tests,
};
+static int platform_device_swnode_test_probe(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static struct platform_driver platform_swnode_test_driver = {
+ .probe = platform_device_swnode_test_probe,
+ .driver = {
+ .name = DEVICE_NAME,
+ },
+};
+
+static const struct software_node platform_device_test_swnode = { };
+
+/*
+ * Check that reusing a software node works correctly. If the call to
+ * platform_device_register_full() fails after adding the secondary firmware
+ * node, the software node must be unregistered in the device's release()
+ * callback or the subsequent call to platform_device_register_full() will fail
+ * with -EBUSY due to the software node already having been registered.
+ */
+static void platform_device_swnode_add_twice(struct kunit *test)
+{
+ struct platform_device_info pdevinfo;
+ struct platform_device *pdev;
+ struct fwnode_handle *fwnode;
+ bool bound = false;
+ int ret;
+
+ fwnode = kunit_kzalloc(test, sizeof(*fwnode), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fwnode);
+
+ ret = kunit_platform_driver_register(test, &platform_swnode_test_driver);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ fwnode_init(fwnode, NULL);
+ pdevinfo = (struct platform_device_info){
+ .name = DEVICE_NAME,
+ .id = PLATFORM_DEVID_NONE,
+ .fwnode = fwnode,
+ .swnode = &platform_device_test_swnode,
+ };
+
+ pdev = platform_device_register_full(&pdevinfo);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev);
+
+ wait_for_device_probe();
+ scoped_guard(device, &pdev->dev)
+ bound = device_is_bound(&pdev->dev);
+
+ KUNIT_ASSERT_TRUE(test, bound);
+
+ platform_device_unregister(pdev);
+
+ pdev = platform_device_register_full(&pdevinfo);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev);
+
+ wait_for_device_probe();
+ scoped_guard(device, &pdev->dev)
+ bound = device_is_bound(&pdev->dev);
+
+ KUNIT_ASSERT_TRUE(test, bound);
+
+ platform_device_unregister(pdev);
+}
+
+/*
+ * Check that passing a software node as the primary firmware node of the
+ * platform device does not result in it being unregistered by the call to
+ * device_remove_software_node() in its release path.
+ */
+static void platform_device_swnode_as_primary(struct kunit *test)
+{
+ struct platform_device_info pdevinfo;
+ struct platform_device *pdev;
+ struct fwnode_handle *fwnode;
+ bool bound = false;
+ int ret;
+
+ ret = kunit_platform_driver_register(test, &platform_swnode_test_driver);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ fwnode = kunit_software_node_register(test, &platform_device_test_swnode);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fwnode);
+
+ pdevinfo = (struct platform_device_info){
+ .name = DEVICE_NAME,
+ .id = PLATFORM_DEVID_NONE,
+ .fwnode = fwnode,
+ };
+
+ pdev = platform_device_register_full(&pdevinfo);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev);
+
+ wait_for_device_probe();
+ scoped_guard(device, &pdev->dev)
+ bound = device_is_bound(&pdev->dev);
+
+ KUNIT_ASSERT_TRUE(test, bound);
+
+ platform_device_unregister(pdev);
+
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, software_node_fwnode(&platform_device_test_swnode));
+}
+
+/*
+ * Check that passing two software nodes to platform_device_register_full()
+ * fails.
+ */
+static void platform_device_two_swnodes(struct kunit *test)
+{
+ static const struct property_entry properties[] = {
+ PROPERTY_ENTRY_U32("foo", 42),
+ { }
+ };
+
+ struct platform_device_info pdevinfo;
+ struct platform_device *pdev;
+ struct fwnode_handle *fwnode;
+ int ret;
+
+ ret = kunit_platform_driver_register(test, &platform_swnode_test_driver);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ fwnode = kunit_software_node_register(test, &platform_device_test_swnode);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fwnode);
+
+ pdevinfo = (struct platform_device_info){
+ .name = DEVICE_NAME,
+ .id = PLATFORM_DEVID_NONE,
+ .fwnode = fwnode,
+ .swnode = &platform_device_test_swnode,
+ };
+
+ pdev = platform_device_register_full(&pdevinfo);
+ KUNIT_ASSERT_TRUE(test, IS_ERR(pdev));
+ KUNIT_ASSERT_EQ_MSG(test, PTR_ERR(pdev), -EINVAL,
+ "Expected errno == -EINVAL, got: %pe", pdev);
+
+ pdevinfo = (struct platform_device_info){
+ .name = DEVICE_NAME,
+ .id = PLATFORM_DEVID_NONE,
+ .swnode = &platform_device_test_swnode,
+ .properties = properties,
+ };
+
+ pdev = platform_device_register_full(&pdevinfo);
+ KUNIT_ASSERT_TRUE(test, IS_ERR(pdev));
+ KUNIT_ASSERT_EQ_MSG(test, PTR_ERR(pdev), -EINVAL,
+ "Expected errno == -EINVAL, got: %pe", pdev);
+
+ pdevinfo = (struct platform_device_info){
+ .name = DEVICE_NAME,
+ .id = PLATFORM_DEVID_NONE,
+ .fwnode = fwnode,
+ .properties = properties,
+ };
+
+ pdev = platform_device_register_full(&pdevinfo);
+ KUNIT_ASSERT_TRUE(test, IS_ERR(pdev));
+ KUNIT_ASSERT_EQ_MSG(test, PTR_ERR(pdev), -EINVAL,
+ "Expected errno == -EINVAL, got: %pe", pdev);
+}
+
+static struct kunit_case platform_device_swnode_tests[] = {
+ KUNIT_CASE(platform_device_swnode_add_twice),
+ KUNIT_CASE(platform_device_swnode_as_primary),
+ KUNIT_CASE(platform_device_two_swnodes),
+ {}
+};
+
+static struct kunit_suite platform_device_swnode_test_suite = {
+ .name = "platform-device-swnode",
+ .test_cases = platform_device_swnode_tests,
+};
+
kunit_test_suites(
&platform_device_devm_test_suite,
&platform_device_match_test_suite,
+ &platform_device_swnode_test_suite,
);
MODULE_DESCRIPTION("Test module for platform devices");
--
2.47.3
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH v7 2/4] drm/xe/i2c: use device_create_managed_software_node()
2026-07-13 15:11 ` [PATCH v7 2/4] drm/xe/i2c: use device_create_managed_software_node() Bartosz Golaszewski
@ 2026-07-13 15:37 ` Rodrigo Vivi
2026-07-14 9:40 ` Heikki Krogerus
0 siblings, 1 reply; 8+ messages in thread
From: Rodrigo Vivi @ 2026-07-13 15:37 UTC (permalink / raw)
To: Bartosz Golaszewski, Heikki Krogerus
Cc: Maximilian Luz, Hans de Goede, Ilpo Järvinen, Matthew Brost,
Thomas Hellström, David Airlie, Simona Vetter,
Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich, brgl,
platform-driver-x86, linux-kernel, intel-xe, dri-devel,
driver-core
On Mon, Jul 13, 2026 at 05:11:58PM +0200, Bartosz Golaszewski wrote:
> This driver intentionally uses the fine-grained approach to creating
> platform devices. It assigns a software node as the primary firmware
> node of the device it creates. Ahead of improving the reference counting
> of platform device software nodes, switch to using
> device_create_managed_software_node(). This way, we create a dynamic
> software node whose life-time is tied to the device to which it's
> assigned.
>
> Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Cc: Heikki Krogerus <heikki.krogerus@linux.intel.com>
> ---
> drivers/gpu/drm/xe/xe_i2c.c | 22 ++++++++--------------
> drivers/gpu/drm/xe/xe_i2c.h | 1 -
Acked-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
> 2 files changed, 8 insertions(+), 15 deletions(-)
>
> diff --git a/drivers/gpu/drm/xe/xe_i2c.c b/drivers/gpu/drm/xe/xe_i2c.c
> index 3d80eafb9a1318996d10db11e14af4cfde07ba55..af6e973e5e68125a9c006bb646447ca6e377a6e6 100644
> --- a/drivers/gpu/drm/xe/xe_i2c.c
> +++ b/drivers/gpu/drm/xe/xe_i2c.c
> @@ -93,13 +93,8 @@ static int xe_i2c_register_adapter(struct xe_i2c *i2c)
> {
> struct pci_dev *pci = to_pci_dev(i2c->drm_dev);
> struct platform_device *pdev;
> - struct fwnode_handle *fwnode;
> int ret;
>
> - fwnode = fwnode_create_software_node(xe_i2c_adapter_properties, NULL);
> - if (IS_ERR(fwnode))
> - return PTR_ERR(fwnode);
> -
> /*
> * Not using platform_device_register_full() here because we don't have
> * a handle to the platform_device before it returns. xe_i2c_notifier()
> @@ -107,10 +102,14 @@ static int xe_i2c_register_adapter(struct xe_i2c *i2c)
> * platform_device_register_full() is done.
> */
> pdev = platform_device_alloc(adapter_name, pci_dev_id(pci));
> - if (!pdev) {
> - ret = -ENOMEM;
> - goto err_fwnode_remove;
> - }
> + if (!pdev)
> + return -ENOMEM;
> +
> + ret = device_create_managed_software_node(&pdev->dev,
> + xe_i2c_adapter_properties,
> + NULL);
> + if (ret)
> + goto err_pdev_put;
>
> if (i2c->adapter_irq) {
> struct resource res;
> @@ -123,8 +122,6 @@ static int xe_i2c_register_adapter(struct xe_i2c *i2c)
> }
>
> pdev->dev.parent = i2c->drm_dev;
> - platform_device_set_fwnode(pdev, fwnode);
> - i2c->adapter_node = fwnode;
> i2c->pdev = pdev;
>
> ret = platform_device_add(pdev);
> @@ -135,8 +132,6 @@ static int xe_i2c_register_adapter(struct xe_i2c *i2c)
>
> err_pdev_put:
> platform_device_put(pdev);
> -err_fwnode_remove:
> - fwnode_remove_software_node(fwnode);
>
> return ret;
> }
> @@ -144,7 +139,6 @@ static int xe_i2c_register_adapter(struct xe_i2c *i2c)
> static void xe_i2c_unregister_adapter(struct xe_i2c *i2c)
> {
> platform_device_unregister(i2c->pdev);
> - fwnode_remove_software_node(i2c->adapter_node);
> }
>
> /**
> diff --git a/drivers/gpu/drm/xe/xe_i2c.h b/drivers/gpu/drm/xe/xe_i2c.h
> index 425d8160835f4648891ff75f2a9c06284241710e..b28229f056c5a664e1d41dc0cc1978782fe1c2f1 100644
> --- a/drivers/gpu/drm/xe/xe_i2c.h
> +++ b/drivers/gpu/drm/xe/xe_i2c.h
> @@ -30,7 +30,6 @@ struct xe_i2c_endpoint {
> };
>
> struct xe_i2c {
> - struct fwnode_handle *adapter_node;
> struct platform_device *pdev;
> struct i2c_adapter *adapter;
> struct i2c_client *client[XE_I2C_MAX_CLIENTS];
>
> --
> 2.47.3
>
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v7 3/4] driver core: platform: unify release path
2026-07-13 15:11 ` [PATCH v7 3/4] driver core: platform: unify release path Bartosz Golaszewski
@ 2026-07-13 18:45 ` Danilo Krummrich
0 siblings, 0 replies; 8+ messages in thread
From: Danilo Krummrich @ 2026-07-13 18:45 UTC (permalink / raw)
To: Bartosz Golaszewski
Cc: Maximilian Luz, Hans de Goede, Ilpo Järvinen, Matthew Brost,
Thomas Hellström, Rodrigo Vivi, David Airlie, Simona Vetter,
Greg Kroah-Hartman, Rafael J. Wysocki, brgl, platform-driver-x86,
linux-kernel, intel-xe, dri-devel, driver-core
On Mon Jul 13, 2026 at 5:11 PM CEST, Bartosz Golaszewski wrote:
> @@ -599,6 +599,7 @@ static void platform_device_release(struct device *dev)
> struct platform_object *pa = container_of(dev, struct platform_object,
> pdev.dev);
>
> + device_remove_software_node(dev);
> fwnode_handle_put(pa->pdev.dev.fwnode);
This technically changes the semantics of platform_device_set_fwnode():
Previously it took an additional reference count for the fwnode pointer passed
to it. But now, *iff* the fwnode is a software node it consumes the callers
reference count, which for obvious reasons isn't great.
Now, platform_device_set_fwnode() is unused now, so we could just get rid of it,
which also gets us rid of its asymmetric semantics.
However, couldn't we also just move the if (is_software_node(pdevinfo->fwnode))
conditional up here, such that we have:
device_remove_software_node(dev);
if (!is_software_node(dev->fwnode))
fwnode_handle_put(pa->pdev.dev.fwnode);
This way, the swnode special case would go away. Well, at least the "two
reference counts" special case.
Another special case I just noticed still remains, but is independent of this
change:
If platform_device_set_fwnode() or platform_device_set_of_node() is called for a
device that already has a swnode set, we only call fwnode_handle_put(), but
software_node_notify_remove() etc. isn't called.
Of course that never happens, but it is an inconsistency in the API. Now, it
seems that neither platform_device_set_fwnode() (which we should remove anyway),
nor platform_device_set_of_node() is ever called with a fwnode already set.
So, either we have to special case platform_device_set_of_node() too (for the
case that a swnode is set already), or just require that the function must only
be called when no node has been set, as all users already do; I'd go for the
latter.
If my proposal about moving the is_software_node() check into
platform_device_release() holds, it would probably be good to just add those
patches in a subsequent version, otherwise I'm happy to pull this in as is and
address the other stuff subsequently.
> kfree(pa->pdev.dev.platform_data);
> kfree(pa->pdev.mfd_cell);
> @@ -606,12 +607,6 @@ static void platform_device_release(struct device *dev)
> kfree(pa);
> }
>
> -static void platform_device_release_full(struct device *dev)
> -{
> - device_remove_software_node(dev);
> - platform_device_release(dev);
> -}
> -
> /**
> * platform_device_alloc - create a platform device
> * @name: base name of the device we're adding
> @@ -933,6 +928,16 @@ struct platform_device *platform_device_register_full(const struct platform_devi
> pdev->dev.coherent_dma_mask = pdevinfo->dma_mask;
> }
>
> + /*
> + * If the primary firmware node is a software node and there's no
> + * secondary firmware node, the primary will be affected by the call
> + * to device_remove_software_node() in platform_device_release() and
> + * its reference count will be dropped by one. Take another reference
> + * here to make it have no effect.
> + */
> + if (is_software_node(pdevinfo->fwnode))
> + fwnode_handle_get(pdevinfo->fwnode);
> +
> ret = platform_device_add_resources(pdev, pdevinfo->res, pdevinfo->num_res);
> if (ret)
> goto err;
> @@ -945,8 +950,6 @@ struct platform_device *platform_device_register_full(const struct platform_devi
> ret = device_add_software_node(&pdev->dev, pdevinfo->swnode);
> if (ret)
> goto err;
> -
> - pdev->dev.release = platform_device_release_full;
> } else if (pdevinfo->properties) {
> ret = device_create_managed_software_node(&pdev->dev,
> pdevinfo->properties, NULL);
>
> --
> 2.47.3
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v7 2/4] drm/xe/i2c: use device_create_managed_software_node()
2026-07-13 15:37 ` Rodrigo Vivi
@ 2026-07-14 9:40 ` Heikki Krogerus
0 siblings, 0 replies; 8+ messages in thread
From: Heikki Krogerus @ 2026-07-14 9:40 UTC (permalink / raw)
To: Bartosz Golaszewski, Rodrigo Vivi
Cc: Maximilian Luz, Hans de Goede, Ilpo Järvinen, Matthew Brost,
Thomas Hellström, David Airlie, Simona Vetter,
Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich, brgl,
platform-driver-x86, linux-kernel, intel-xe, dri-devel,
driver-core
On Mon, Jul 13, 2026 at 11:37:03AM -0400, Rodrigo Vivi wrote:
> On Mon, Jul 13, 2026 at 05:11:58PM +0200, Bartosz Golaszewski wrote:
> > This driver intentionally uses the fine-grained approach to creating
> > platform devices. It assigns a software node as the primary firmware
> > node of the device it creates. Ahead of improving the reference counting
> > of platform device software nodes, switch to using
> > device_create_managed_software_node(). This way, we create a dynamic
> > software node whose life-time is tied to the device to which it's
> > assigned.
> >
> > Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
>
> Cc: Heikki Krogerus <heikki.krogerus@linux.intel.com>
LGTM:
Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
> > ---
> > drivers/gpu/drm/xe/xe_i2c.c | 22 ++++++++--------------
> > drivers/gpu/drm/xe/xe_i2c.h | 1 -
>
> Acked-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
>
> > 2 files changed, 8 insertions(+), 15 deletions(-)
> >
> > diff --git a/drivers/gpu/drm/xe/xe_i2c.c b/drivers/gpu/drm/xe/xe_i2c.c
> > index 3d80eafb9a1318996d10db11e14af4cfde07ba55..af6e973e5e68125a9c006bb646447ca6e377a6e6 100644
> > --- a/drivers/gpu/drm/xe/xe_i2c.c
> > +++ b/drivers/gpu/drm/xe/xe_i2c.c
> > @@ -93,13 +93,8 @@ static int xe_i2c_register_adapter(struct xe_i2c *i2c)
> > {
> > struct pci_dev *pci = to_pci_dev(i2c->drm_dev);
> > struct platform_device *pdev;
> > - struct fwnode_handle *fwnode;
> > int ret;
> >
> > - fwnode = fwnode_create_software_node(xe_i2c_adapter_properties, NULL);
> > - if (IS_ERR(fwnode))
> > - return PTR_ERR(fwnode);
> > -
> > /*
> > * Not using platform_device_register_full() here because we don't have
> > * a handle to the platform_device before it returns. xe_i2c_notifier()
> > @@ -107,10 +102,14 @@ static int xe_i2c_register_adapter(struct xe_i2c *i2c)
> > * platform_device_register_full() is done.
> > */
> > pdev = platform_device_alloc(adapter_name, pci_dev_id(pci));
> > - if (!pdev) {
> > - ret = -ENOMEM;
> > - goto err_fwnode_remove;
> > - }
> > + if (!pdev)
> > + return -ENOMEM;
> > +
> > + ret = device_create_managed_software_node(&pdev->dev,
> > + xe_i2c_adapter_properties,
> > + NULL);
> > + if (ret)
> > + goto err_pdev_put;
> >
> > if (i2c->adapter_irq) {
> > struct resource res;
> > @@ -123,8 +122,6 @@ static int xe_i2c_register_adapter(struct xe_i2c *i2c)
> > }
> >
> > pdev->dev.parent = i2c->drm_dev;
> > - platform_device_set_fwnode(pdev, fwnode);
> > - i2c->adapter_node = fwnode;
> > i2c->pdev = pdev;
> >
> > ret = platform_device_add(pdev);
> > @@ -135,8 +132,6 @@ static int xe_i2c_register_adapter(struct xe_i2c *i2c)
> >
> > err_pdev_put:
> > platform_device_put(pdev);
> > -err_fwnode_remove:
> > - fwnode_remove_software_node(fwnode);
> >
> > return ret;
> > }
> > @@ -144,7 +139,6 @@ static int xe_i2c_register_adapter(struct xe_i2c *i2c)
> > static void xe_i2c_unregister_adapter(struct xe_i2c *i2c)
> > {
> > platform_device_unregister(i2c->pdev);
> > - fwnode_remove_software_node(i2c->adapter_node);
> > }
> >
> > /**
> > diff --git a/drivers/gpu/drm/xe/xe_i2c.h b/drivers/gpu/drm/xe/xe_i2c.h
> > index 425d8160835f4648891ff75f2a9c06284241710e..b28229f056c5a664e1d41dc0cc1978782fe1c2f1 100644
> > --- a/drivers/gpu/drm/xe/xe_i2c.h
> > +++ b/drivers/gpu/drm/xe/xe_i2c.h
> > @@ -30,7 +30,6 @@ struct xe_i2c_endpoint {
> > };
> >
> > struct xe_i2c {
> > - struct fwnode_handle *adapter_node;
> > struct platform_device *pdev;
> > struct i2c_adapter *adapter;
> > struct i2c_client *client[XE_I2C_MAX_CLIENTS];
> >
> > --
> > 2.47.3
> >
--
heikki
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-07-14 9:40 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-13 15:11 [PATCH v7 0/4] driver core: unify the release path for dynamically allocated platform devices Bartosz Golaszewski
2026-07-13 15:11 ` [PATCH v7 1/4] platform/surface: gpe: use platform_device_register_full() Bartosz Golaszewski
2026-07-13 15:11 ` [PATCH v7 2/4] drm/xe/i2c: use device_create_managed_software_node() Bartosz Golaszewski
2026-07-13 15:37 ` Rodrigo Vivi
2026-07-14 9:40 ` Heikki Krogerus
2026-07-13 15:11 ` [PATCH v7 3/4] driver core: platform: unify release path Bartosz Golaszewski
2026-07-13 18:45 ` Danilo Krummrich
2026-07-13 15:12 ` [PATCH v7 4/4] driver core: platform: tests: add test cases for correct swnode removal Bartosz Golaszewski
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.