git: cb83af64f161 - main - device: add generic named per-device property

From: Konstantin Belousov <kib_at_FreeBSD.org>
Date: Thu, 17 Oct 2024 14:32:15 UTC
The branch main has been updated by kib:

URL: https://cgit.FreeBSD.org/src/commit/?id=cb83af64f161b228171d6fb4f36d22493cefaa7f

commit cb83af64f161b228171d6fb4f36d22493cefaa7f
Author:     Konstantin Belousov <kib@FreeBSD.org>
AuthorDate: 2024-09-08 04:40:41 +0000
Commit:     Konstantin Belousov <kib@FreeBSD.org>
CommitDate: 2024-10-17 14:31:50 +0000

    device: add generic named per-device property
    
    The KPI allows to store arbitrary data within struct device, retrieve it
    back by name, and update the value.  The data is automatically destroyed
    on the device deletion.  Also, the user can request to clear the named
    property from all devices.
    
    My need for this interface is due to IOMMU needing to save some
    indicator on device, which must survive the re-creation of the domain
    context.
    
    Reviewed by:    imp
    Discussed with: emaste
    Sponsored by:   Advanced Micro Devices (AMD)
    Sponsored by:   The FreeBSD Foundation
    MFC after:      1 week
    Differential revision:  https://reviews.freebsd.org/D46605
---
 sys/kern/subr_bus.c | 120 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 sys/sys/bus.h       |   7 +++
 2 files changed, 127 insertions(+)

diff --git a/sys/kern/subr_bus.c b/sys/kern/subr_bus.c
index 7c21325f8684..26c476e9fef6 100644
--- a/sys/kern/subr_bus.c
+++ b/sys/kern/subr_bus.c
@@ -105,6 +105,16 @@ struct devclass {
 	struct sysctl_oid *sysctl_tree;
 };
 
+struct device_prop_elm {
+	const char *name;
+	void *val;
+	void *dtr_ctx;
+	device_prop_dtr_t dtr;
+	LIST_ENTRY(device_prop_elm) link;
+};
+
+static void device_destroy_props(device_t dev);
+
 /**
  * @brief Implementation of _device.
  *
@@ -141,6 +151,7 @@ struct _device {
 	u_int	order;			/**< order from device_add_child_ordered() */
 	void	*ivars;			/**< instance variables  */
 	void	*softc;			/**< current driver's variables  */
+	LIST_HEAD(, device_prop_elm) props;
 
 	struct sysctl_ctx_list sysctl_ctx; /**< state for sysctl variables  */
 	struct sysctl_oid *sysctl_tree;	/**< state for sysctl variables */
@@ -1349,6 +1360,7 @@ make_device(device_t parent, const char *name, int unit)
 		dev->flags |= DF_QUIET | DF_QUIET_CHILDREN;
 	dev->ivars = NULL;
 	dev->softc = NULL;
+	LIST_INIT(&dev->props);
 
 	dev->state = DS_NOTPRESENT;
 
@@ -1491,6 +1503,7 @@ device_delete_child(device_t dev, device_t child)
 			return (error);
 	}
 
+	device_destroy_props(dev);
 	if (child->devclass)
 		devclass_delete_device(child->devclass, child);
 	if (child->parent)
@@ -6015,6 +6028,113 @@ dev_wired_cache_match(device_location_cache_t *dcp, device_t dev,
 	return (strcmp(res->dln_path, cp) == 0);
 }
 
+static struct device_prop_elm *
+device_prop_find(device_t dev, const char *name)
+{
+	struct device_prop_elm *e;
+
+	bus_topo_assert();
+
+	LIST_FOREACH(e, &dev->props, link) {
+		if (strcmp(name, e->name) == 0)
+			return (e);
+	}
+	return (NULL);
+}
+
+int
+device_set_prop(device_t dev, const char *name, void *val,
+    device_prop_dtr_t dtr, void *dtr_ctx)
+{
+	struct device_prop_elm *e, *e1;
+
+	bus_topo_assert();
+
+	e = device_prop_find(dev, name);
+	if (e != NULL)
+		goto found;
+
+	e1 = malloc(sizeof(*e), M_BUS, M_WAITOK);
+	e = device_prop_find(dev, name);
+	if (e != NULL) {
+		free(e1, M_BUS);
+		goto found;
+	}
+
+	e1->name = name;
+	e1->val = val;
+	e1->dtr = dtr;
+	e1->dtr_ctx = dtr_ctx;
+	LIST_INSERT_HEAD(&dev->props, e1, link);
+	return (0);
+
+found:
+	LIST_REMOVE(e, link);
+	if (e->dtr != NULL)
+		e->dtr(dev, name, e->val, e->dtr_ctx);
+	e->val = val;
+	e->dtr = dtr;
+	e->dtr_ctx = dtr_ctx;
+	LIST_INSERT_HEAD(&dev->props, e, link);
+	return (EEXIST);
+}
+
+int
+device_get_prop(device_t dev, const char *name, void **valp)
+{
+	struct device_prop_elm *e;
+
+	bus_topo_assert();
+
+	e = device_prop_find(dev, name);
+	if (e == NULL)
+		return (ENOENT);
+	*valp = e->val;
+	return (0);
+}
+
+int
+device_clear_prop(device_t dev, const char *name)
+{
+	struct device_prop_elm *e;
+
+	bus_topo_assert();
+
+	e = device_prop_find(dev, name);
+	if (e == NULL)
+		return (ENOENT);
+	LIST_REMOVE(e, link);
+	if (e->dtr != NULL)
+		e->dtr(dev, e->name, e->val, e->dtr_ctx);
+	free(e, M_BUS);
+	return (0);
+}
+
+static void
+device_destroy_props(device_t dev)
+{
+	struct device_prop_elm *e;
+
+	bus_topo_assert();
+
+	while ((e = LIST_FIRST(&dev->props)) != NULL) {
+		LIST_REMOVE_HEAD(&dev->props, link);
+		if (e->dtr != NULL)
+			e->dtr(dev, e->name, e->val, e->dtr_ctx);
+		free(e, M_BUS);
+	}
+}
+
+void
+device_clear_prop_alldev(const char *name)
+{
+	device_t dev;
+
+	TAILQ_FOREACH(dev, &bus_data_devices, devlink) {
+		device_clear_prop(dev, name);
+	}
+}
+
 /*
  * APIs to manage deprecation and obsolescence.
  */
diff --git a/sys/sys/bus.h b/sys/sys/bus.h
index acadbb217711..49c5d62cebce 100644
--- a/sys/sys/bus.h
+++ b/sys/sys/bus.h
@@ -924,6 +924,13 @@ device_location_cache_t *dev_wired_cache_init(void);
 void dev_wired_cache_fini(device_location_cache_t *dcp);
 bool dev_wired_cache_match(device_location_cache_t *dcp, device_t dev, const char *at);
 
+typedef void (*device_prop_dtr_t)(device_t dev, const char *name, void *val,
+    void *dtr_ctx);
+int device_set_prop(device_t dev, const char *name, void *val,
+    device_prop_dtr_t dtr, void *dtr_ctx);
+int device_get_prop(device_t dev, const char *name, void **valp);
+int device_clear_prop(device_t dev, const char *name);
+void device_clear_prop_alldev(const char *name);
 
 /**
  * Shorthand macros, taking resource argument