All of lore.kernel.org
 help / color / mirror / Atom feed
From: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
To: Lyude Paul <lyude@redhat.com>
Cc: rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org,
	"Danilo Krummrich" <dakr@kernel.org>,
	"Maíra Canal" <mairacanal@riseup.net>,
	"Rafael J. Wysocki" <rafael@kernel.org>,
	"Jonathan Cameron" <Jonathan.Cameron@huawei.com>,
	"Zijun Hu" <quic_zijuhu@quicinc.com>,
	"Andy Shevchenko" <andriy.shevchenko@linux.intel.com>,
	"Robin Murphy" <robin.murphy@arm.com>,
	"Alexander Lobakin" <aleksander.lobakin@intel.com>,
	"Lukas Wunner" <lukas@wunner.de>,
	"Bjorn Helgaas" <bhelgaas@google.com>
Subject: Re: [PATCH] WIP: drivers/base: Add virtual_device_create()
Date: Fri, 31 Jan 2025 17:40:01 +0100	[thread overview]
Message-ID: <2025013140-propeller-dirtiness-6cb4@gregkh> (raw)
In-Reply-To: <2025013159-shabby-professor-515b@gregkh>

On Fri, Jan 31, 2025 at 09:00:32AM +0100, Greg Kroah-Hartman wrote:
> On Thu, Jan 30, 2025 at 04:28:26PM -0500, Lyude Paul wrote:
> > As Greg KH pointed out, we have a nice /sys/devices/virtual directory free
> > for the taking - but the vast majority of device drivers concerned with
> > virtual devices do not use this and instead misuse the platform device API.
> > 
> > To fix this, let's start by adding a simple function that can be used for
> > creating virtual devices - virtual_device_create().
> > 
> > Signed-off-by: Lyude Paul <lyude@redhat.com>
> > 
> > ---
> > 
> > So, WIP obviously because I wrote this up in a few minutes - but this goes
> > off the idea that Danilo suggested to me off-list of coming up with a
> > simple API for handling virtual devices that's a little more obvious to
> > use. I wanted to get people's feedback and if we're happy with this idea,
> > I'm willing to go through and add some pointers to this function in various
> > platform API docs - along with porting over the C version of VKMS over to
> > this API.
> 
> This is a big better, but not quite.  Let me carve out some time today
> to knock something a bit nicer together...

Ok, here's a rough first-cut.  It builds, and boots, and I've converted
a driver to use the api to prove it works here.  I'll add a bunch more
documentation before turning it into a "real" patch, but this should
give you something to work off of.

I've run out of time for tonight (dinner is calling), but I think you
get the idea, right?  If you want to knock up a rust binding for this
api, it should almost be identical to the platform api you were trying
to use before, right?

thanks,

greg k-h

diff --git a/drivers/base/Makefile b/drivers/base/Makefile
index 7fb21768ca36..13eec7a1a9db 100644
--- a/drivers/base/Makefile
+++ b/drivers/base/Makefile
@@ -6,7 +6,7 @@ obj-y			:= component.o core.o bus.o dd.o syscore.o \
 			   cpu.o firmware.o init.o map.o devres.o \
 			   attribute_container.o transport_class.o \
 			   topology.o container.o property.o cacheinfo.o \
-			   swnode.o
+			   swnode.o virtual.o
 obj-$(CONFIG_AUXILIARY_BUS) += auxiliary.o
 obj-$(CONFIG_DEVTMPFS)	+= devtmpfs.o
 obj-y			+= power/
diff --git a/drivers/base/base.h b/drivers/base/base.h
index 8cf04a557bdb..1eb68e416ee1 100644
--- a/drivers/base/base.h
+++ b/drivers/base/base.h
@@ -137,6 +137,7 @@ int hypervisor_init(void);
 static inline int hypervisor_init(void) { return 0; }
 #endif
 int platform_bus_init(void);
+int virtual_bus_init(void);
 void cpu_dev_init(void);
 void container_dev_init(void);
 #ifdef CONFIG_AUXILIARY_BUS
diff --git a/drivers/base/init.c b/drivers/base/init.c
index c4954835128c..58c98a156220 100644
--- a/drivers/base/init.c
+++ b/drivers/base/init.c
@@ -35,6 +35,7 @@ void __init driver_init(void)
 	of_core_init();
 	platform_bus_init();
 	auxiliary_bus_init();
+	virtual_bus_init();
 	memory_dev_init();
 	node_dev_init();
 	cpu_dev_init();
diff --git a/drivers/base/virtual.c b/drivers/base/virtual.c
new file mode 100644
index 000000000000..6ade5dcbe2f3
--- /dev/null
+++ b/drivers/base/virtual.c
@@ -0,0 +1,200 @@
+// SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2025 Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+ * Copyright (c) 2025 Linux Foundation
+ */
+#include <linux/device/virtual.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include "base.h"
+
+/*
+ * Wrapper structure so we can hold the memory
+ * for the name string of the virtual device.
+ */
+struct virtual_object {
+	struct virtual_device virt_dev;
+	char name[];
+};
+
+static struct device virtual_bus = {
+	.init_name	= "virt_bus",
+};
+
+static int virtual_match(struct device *dev, const struct device_driver *drv)
+{
+	const struct virtual_driver *virt_drv = to_virtual_driver(drv);
+	const struct virtual_device *virt_dev = to_virtual_device(dev);
+
+	pr_info("%s: %s %s\n", __func__, virt_dev->name, virt_drv->name);
+
+	/* Match is simple, strcmp()! */
+	return (strcmp(virt_dev->name, virt_drv->name) == 0);
+}
+
+static int virtual_probe(struct device *dev)
+{
+	struct virtual_driver *virt_drv = to_virtual_driver(dev->driver);
+	struct virtual_device *virt_dev = to_virtual_device(dev);
+	int ret = 0;
+
+	if (virt_drv->probe)
+		ret = virt_drv->probe(virt_dev);
+
+	return ret;
+}
+
+static void virtual_remove(struct device *dev)
+{
+	struct virtual_driver *virt_drv = to_virtual_driver(dev->driver);
+	struct virtual_device *virt_dev = to_virtual_device(dev);
+
+	if (virt_drv->remove)
+		virt_drv->remove(virt_dev);
+}
+
+static const struct bus_type virtual_bus_type = {
+	.name		= "virtual",
+	.match		= virtual_match,
+	.probe		= virtual_probe,
+	.remove		= virtual_remove,
+};
+
+static void virtual_device_release(struct device *dev)
+{
+	struct virtual_object *virt_obj = container_of(dev, struct virtual_object, virt_dev.dev);
+
+	kfree(virt_obj);
+}
+
+/**
+ * virtual_device_alloc - create a virtual device
+ * @name: name of the device we're adding
+ *
+ * Create a virtual device object which will be bound to any driver of the same name.
+ */
+struct virtual_device *virtual_device_alloc(const char *name)
+{
+	struct virtual_object *virt_obj;
+	struct virtual_device *virt_dev;
+
+	pr_info("%s: %s\n", __func__, name);
+
+	virt_obj = kzalloc(sizeof(*virt_obj) + strlen(name) + 1, GFP_KERNEL);
+	if (!virt_obj)
+		return NULL;
+
+	strcpy(virt_obj->name, name);
+	virt_dev = &virt_obj->virt_dev;
+	virt_dev->name = virt_obj->name;
+	device_initialize(&virt_dev->dev);
+	virt_dev->dev.release = virtual_device_release;
+
+	return virt_dev;
+}
+EXPORT_SYMBOL_GPL(virtual_device_alloc);
+
+int virtual_device_add(struct virtual_device *virt_dev)
+{
+	struct device *dev = &virt_dev->dev;
+
+	pr_info("%s: %s\n", __func__, virt_dev->name);
+
+	if (!dev->parent)
+		dev->parent = &virtual_bus;
+
+	dev_set_name(dev, "%s", virt_dev->name);
+	dev->bus = &virtual_bus_type;
+
+	return device_add(dev);
+}
+EXPORT_SYMBOL_GPL(virtual_device_add);
+
+/**
+ * virtual_device_del - remove a virtual device
+ * @virt_dev: virtual device to be removed
+ *
+ * This function must _only_ be externally called in error cases.  All other usage is a bug.
+ */
+void virtual_device_del(struct virtual_device *virt_dev)
+{
+	if (IS_ERR_OR_NULL(virt_dev))
+	    return;
+
+	device_del(&virt_dev->dev);
+}
+EXPORT_SYMBOL_GPL(virtual_device_del);
+
+/**
+ * virtual_device_put - destroy a platform device
+ * @virtual: virtual device to free
+ *
+ * Free all memory associated with a virtual device.  This function must
+ * _only_ be externally called in error cases.  All other usage is a bug.
+ */
+void virtual_device_put(struct virtual_device *virt_dev)
+{
+	if (IS_ERR_OR_NULL(virt_dev))
+	    return;
+
+	put_device(&virt_dev->dev);
+}
+EXPORT_SYMBOL_GPL(virtual_device_put);
+
+
+void virtual_device_unregister(struct virtual_device *virt_dev)
+{
+	virtual_device_del(virt_dev);
+	virtual_device_put(virt_dev);
+}
+EXPORT_SYMBOL_GPL(virtual_device_unregister);
+
+
+/**
+ * __virtual_driver_register - register a driver for virtual devices
+ * @virt_drv: virtual driver structure
+ * @owner: owning module/driver
+ */
+int __virtual_driver_register(struct virtual_driver *virt_drv, struct module *owner)
+{
+	struct device_driver *drv = &virt_drv->driver;
+
+	pr_info("%s: %s\n", __func__, virt_drv->name);
+
+	drv->owner = owner;
+	drv->name = virt_drv->name;
+	drv->bus = &virtual_bus_type;
+	drv->probe_type = PROBE_PREFER_ASYNCHRONOUS;
+
+	return driver_register(&virt_drv->driver);
+}
+EXPORT_SYMBOL_GPL(__virtual_driver_register);
+
+/**
+ * virtual_driver_unregister - unregister a driver for virtual devices
+ * @drv: virtual driver structure
+ */
+void virtual_driver_unregister(struct virtual_driver *virt_drv)
+{
+	driver_unregister(&virt_drv->driver);
+}
+EXPORT_SYMBOL_GPL(virtual_driver_unregister);
+
+int __init virtual_bus_init(void)
+{
+	int error;
+
+	error = device_register(&virtual_bus);
+	if (error) {
+		put_device(&virtual_bus);
+		return error;
+	}
+
+	error =  bus_register(&virtual_bus_type);
+	if (error)
+		device_unregister(&virtual_bus);
+
+	return error;
+}
diff --git a/drivers/regulator/dummy.c b/drivers/regulator/dummy.c
index 5b9b9e4e762d..652250455c6d 100644
--- a/drivers/regulator/dummy.c
+++ b/drivers/regulator/dummy.c
@@ -13,7 +13,7 @@
 
 #include <linux/err.h>
 #include <linux/export.h>
-#include <linux/platform_device.h>
+#include <linux/device/virtual.h>
 #include <linux/regulator/driver.h>
 #include <linux/regulator/machine.h>
 
@@ -37,15 +37,15 @@ static const struct regulator_desc dummy_desc = {
 	.ops = &dummy_ops,
 };
 
-static int dummy_regulator_probe(struct platform_device *pdev)
+static int dummy_regulator_probe(struct virtual_device *vdev)
 {
 	struct regulator_config config = { };
 	int ret;
 
-	config.dev = &pdev->dev;
+	config.dev = &vdev->dev;
 	config.init_data = &dummy_initdata;
 
-	dummy_regulator_rdev = devm_regulator_register(&pdev->dev, &dummy_desc,
+	dummy_regulator_rdev = devm_regulator_register(&vdev->dev, &dummy_desc,
 						       &config);
 	if (IS_ERR(dummy_regulator_rdev)) {
 		ret = PTR_ERR(dummy_regulator_rdev);
@@ -56,36 +56,33 @@ static int dummy_regulator_probe(struct platform_device *pdev)
 	return 0;
 }
 
-static struct platform_driver dummy_regulator_driver = {
+static struct virtual_driver dummy_regulator_driver = {
+	.name		= "reg-dummy",
 	.probe		= dummy_regulator_probe,
-	.driver		= {
-		.name		= "reg-dummy",
-		.probe_type	= PROBE_PREFER_ASYNCHRONOUS,
-	},
 };
 
-static struct platform_device *dummy_pdev;
+static struct virtual_device *dummy_vdev;
 
 void __init regulator_dummy_init(void)
 {
 	int ret;
 
-	dummy_pdev = platform_device_alloc("reg-dummy", -1);
-	if (!dummy_pdev) {
+	dummy_vdev = virtual_device_alloc("reg-dummy");
+	if (!dummy_vdev) {
 		pr_err("Failed to allocate dummy regulator device\n");
 		return;
 	}
 
-	ret = platform_device_add(dummy_pdev);
+	ret = virtual_device_add(dummy_vdev);
 	if (ret != 0) {
 		pr_err("Failed to register dummy regulator device: %d\n", ret);
-		platform_device_put(dummy_pdev);
+		virtual_device_put(dummy_vdev);
 		return;
 	}
 
-	ret = platform_driver_register(&dummy_regulator_driver);
+	ret = virtual_driver_register(&dummy_regulator_driver);
 	if (ret != 0) {
 		pr_err("Failed to register dummy regulator driver: %d\n", ret);
-		platform_device_unregister(dummy_pdev);
+		virtual_device_unregister(dummy_vdev);
 	}
 }
diff --git a/include/linux/device/virtual.h b/include/linux/device/virtual.h
new file mode 100644
index 000000000000..b646defe4a97
--- /dev/null
+++ b/include/linux/device/virtual.h
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2025 Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+ * Copyright (c) 2025 Linux Foundation
+ */
+#ifndef _VIRTUAL_DEVICE_H
+#define _VIRTUAL_DEVICE_H_
+
+#include <linux/device.h>
+struct virtual_device {
+	struct device dev;
+	const char *name;
+
+};
+#define to_virtual_device(x) container_of_const((x), struct virtual_device, dev)
+
+struct virtual_driver {
+	struct device_driver driver;
+	const char *name;
+	int (*probe)(struct virtual_device *virt_dev);
+	void (*remove)(struct virtual_device *virt_dev);
+};
+#define to_virtual_driver(drv)	(container_of_const((drv), struct virtual_driver, driver))
+
+struct virtual_device *virtual_device_alloc(const char *name);
+int virtual_device_add(struct virtual_device *virt_dev);
+void virtual_device_del(struct virtual_device *virt_dev);
+void virtual_device_put(struct virtual_device *virt_dev);
+void virtual_device_unregister(struct virtual_device *virt_dev);
+
+#define virtual_driver_register(drv) __virtual_driver_register(drv, THIS_MODULE)
+int __virtual_driver_register(struct virtual_driver *virt_drv, struct module *module);
+void virtual_driver_unregister(struct virtual_driver *virt_drv);
+
+#endif /* _VIRTUAL_DEVICE_H_ */
-- 
2.48.1



  reply	other threads:[~2025-01-31 16:40 UTC|newest]

Thread overview: 34+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-01-22 23:49 [PATCH 0/2] rust/kernel: Add bindings for manually creating devices Lyude Paul
2025-01-22 23:49 ` [PATCH 1/2] rust/kernel: Add platform::Device::from_raw() Lyude Paul
2025-01-28 14:35   ` Alice Ryhl
2025-01-22 23:49 ` [PATCH 2/2] rust/kernel: Add platform::ModuleDevice Lyude Paul
2025-01-23  6:23   ` Greg Kroah-Hartman
2025-01-23 10:21     ` Danilo Krummrich
2025-01-23 14:17       ` Greg Kroah-Hartman
2025-01-24 10:52         ` Danilo Krummrich
2025-01-30 21:28           ` [PATCH] WIP: drivers/base: Add virtual_device_create() Lyude Paul
2025-01-30 21:58             ` Lyude Paul
2025-02-01  8:32               ` Greg Kroah-Hartman
2025-01-31  3:34             ` kernel test robot
2025-01-31  8:00             ` Greg Kroah-Hartman
2025-01-31 16:40               ` Greg Kroah-Hartman [this message]
2025-01-31 18:43                 ` Danilo Krummrich
2025-02-01  8:00                   ` Greg Kroah-Hartman
2025-02-03  9:39                     ` [RFC] driver core: add a virtual bus for use when a simple device/bus is needed Greg Kroah-Hartman
2025-02-03 10:02                       ` Greg Kroah-Hartman
2025-02-03 11:01                       ` Danilo Krummrich
2025-02-03 11:25                         ` Greg Kroah-Hartman
2025-02-03 14:33                           ` Greg Kroah-Hartman
2025-02-03 15:32                             ` Simona Vetter
2025-02-03 15:38                               ` Greg Kroah-Hartman
2025-02-03 22:45                             ` Lyude Paul
2025-02-03 21:13                           ` Danilo Krummrich
2025-02-04  6:05                             ` Greg Kroah-Hartman
2025-02-03  9:45                     ` [PATCH] WIP: drivers/base: Add virtual_device_create() Simona Vetter
2025-02-03  9:51                       ` Greg Kroah-Hartman
2025-01-31 16:42               ` Simona Vetter
2025-01-31 10:43             ` Andy Shevchenko
2025-01-24  0:33     ` [PATCH 2/2] rust/kernel: Add platform::ModuleDevice Lyude Paul
2025-01-24 11:02       ` Danilo Krummrich
2025-01-31 16:41         ` Simona Vetter
2025-01-24 21:19       ` Lyude Paul

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=2025013140-propeller-dirtiness-6cb4@gregkh \
    --to=gregkh@linuxfoundation.org \
    --cc=Jonathan.Cameron@huawei.com \
    --cc=aleksander.lobakin@intel.com \
    --cc=andriy.shevchenko@linux.intel.com \
    --cc=bhelgaas@google.com \
    --cc=dakr@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=lukas@wunner.de \
    --cc=lyude@redhat.com \
    --cc=mairacanal@riseup.net \
    --cc=quic_zijuhu@quicinc.com \
    --cc=rafael@kernel.org \
    --cc=robin.murphy@arm.com \
    --cc=rust-for-linux@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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.