git: 477c594d9063 - main - kobj: allow multiple inheritance with per-class softc
Date: Fri, 11 Sep 2026 14:09:45 UTC
The branch main has been updated by mmel:
URL: https://cgit.FreeBSD.org/src/commit/?id=477c594d906328e210c30cf2c21d983152f21de3
commit 477c594d906328e210c30cf2c21d983152f21de3
Author: Michal Meloun <mmel@FreeBSD.org>
AuthorDate: 2026-09-11 11:47:30 +0000
Commit: Michal Meloun <mmel@FreeBSD.org>
CommitDate: 2026-09-11 14:09:36 +0000
kobj: allow multiple inheritance with per-class softc
Add support for hierarchical softc layout so that a leaf class and each
of its base classes owns a private softc region inside a single
allocation.
device_get_softc_class(dev, cls) returns a pointer to the softc that
belongs to the requested class. The classic device_get_softc() still
returns the leaf softc and remains fully compatible with existing
drivers.
Existing drivers are unaffected; they simply obtain a slightly larger
softc block when they inherit from base classes.
MFC after: 2 months
Reviewed by: kib
Differential Revision: https://reviews.freebsd.org/D59115
---
sys/kern/subr_bus.c | 19 ++++++++--
sys/kern/subr_kobj.c | 100 +++++++++++++++++++++++++++++++++++++++++++++++++++
sys/sys/bus.h | 1 +
sys/sys/kobj.h | 30 +++++++++++++---
4 files changed, 142 insertions(+), 8 deletions(-)
diff --git a/sys/kern/subr_bus.c b/sys/kern/subr_bus.c
index b5c1c5b61f60..4398b3fa9e13 100644
--- a/sys/kern/subr_bus.c
+++ b/sys/kern/subr_bus.c
@@ -2159,6 +2159,17 @@ device_get_softc(device_t dev)
return (dev->softc);
}
+void *
+device_get_softc_class(device_t dev, kobj_class_t cls)
+{
+ char *ptr;
+
+ ptr = dev->softc;
+ ptr += kobj_instance_offset(device_get_driver(dev), cls);
+
+ return (ptr);
+}
+
/**
* @brief Set the device's softc field
*
@@ -2472,6 +2483,7 @@ device_set_driver(device_t dev, driver_t *driver)
{
int domain;
struct domainset *policy;
+ size_t size;
if (dev->state >= DS_ATTACHED)
return (EBUSY);
@@ -2488,13 +2500,14 @@ device_set_driver(device_t dev, driver_t *driver)
dev->driver = driver;
if (driver) {
kobj_init((kobj_t) dev, (kobj_class_t) driver);
- if (!(dev->flags & DF_EXTERNALSOFTC) && driver->size > 0) {
+ size = kobj_total_data_size(driver);
+ if (!(dev->flags & DF_EXTERNALSOFTC) && size > 0) {
if (bus_get_domain(dev, &domain) == 0)
policy = DOMAINSET_PREF(domain);
else
policy = DOMAINSET_RR();
- dev->softc = malloc_domainset(driver->size, M_BUS_SC,
- policy, M_WAITOK | M_ZERO);
+ dev->softc = malloc_domainset(size, M_BUS_SC, policy,
+ M_WAITOK | M_ZERO);
}
} else {
kobj_init((kobj_t) dev, &null_class);
diff --git a/sys/kern/subr_kobj.c b/sys/kern/subr_kobj.c
index a88639289954..0f9f7a79aaf0 100644
--- a/sys/kern/subr_kobj.c
+++ b/sys/kern/subr_kobj.c
@@ -94,6 +94,35 @@ kobj_error_method(void)
return ENXIO;
}
+static inline size_t
+kobj_data_size_roundup(size_t size)
+{
+
+ return (roundup2((size), _Alignof(__max_align_t)));
+}
+
+static void
+kobj_class_init_total_size(kobj_class_t cls)
+{
+ kobj_class_t *bases;
+ size_t size;
+ int i;
+
+ MPASS(!cls->total_size_inited);
+ bases = cls->baseclasses;
+
+ size = kobj_data_size_roundup(cls->size);
+ if (bases != NULL) {
+ for (i = 0; bases[i] != NULL; i++) {
+ if (!bases[i]->total_size_inited)
+ kobj_class_init_total_size(bases[i]);
+ size += bases[i]->total_size;
+ }
+ }
+ cls->total_size_inited = true;
+ cls->total_size = size;
+}
+
static void
kobj_class_compile_common(kobj_class_t cls, kobj_ops_t ops)
{
@@ -121,6 +150,12 @@ kobj_class_compile_common(kobj_class_t cls, kobj_ops_t ops)
ops->cache[i] = &null_method;
ops->cls = cls;
cls->ops = ops;
+
+ /*
+ * Then compute total object data size.
+ */
+ if (!cls->total_size_inited)
+ kobj_class_init_total_size(cls);
}
static int
@@ -349,3 +384,68 @@ kobj_delete(kobj_t obj, struct malloc_type *mtype)
if (mtype)
free(obj, mtype);
}
+
+static inline bool
+kobj_get_instance_offset(kobj_class_t cls, kobj_class_t subclass, size_t *offs)
+{
+ kobj_class_t *bases;
+ size_t tmp;
+ int i;
+
+ if (cls == subclass)
+ return (true);
+
+ bases = cls->baseclasses;
+ *offs += kobj_data_size_roundup(cls->size);
+
+ if (bases == NULL || bases[0] == NULL)
+ return (false);
+
+ /* Try most common cases (offset of direct subclass) first. */
+ tmp = *offs;
+ for (i = 0; bases[i] != NULL; i++) {
+ if (bases[i] == subclass) {
+ *offs = tmp;
+ return (true);
+ }
+ tmp += bases[i]->total_size;
+ }
+
+ /* Then do recursion for nested classes. */
+ for (i = 0; bases[i] != NULL; i++) {
+ if (kobj_get_instance_offset(bases[i], subclass, offs))
+ return (true);
+ }
+ return (false);
+}
+
+/*
+ * Get the data offset for the given class.
+ * layout is: [main | base0 | base1 | ... ]
+ */
+size_t
+kobj_instance_offset(kobj_class_t cls, kobj_class_t subclass)
+{
+ size_t offs;
+
+ if (cls == subclass)
+ return (0);
+
+ offs = 0;
+ if (!kobj_get_instance_offset(cls, subclass, &offs))
+ panic("%s: class %s not found in hierarchy of %s", __func__,
+ subclass->name, cls->name);
+ return (offs);
+}
+
+/*
+ * Get the total data size of this class and all its subclasses.
+ */
+size_t
+kobj_total_data_size(kobj_class_t cls)
+{
+
+ MPASS(cls->total_size_inited);
+
+ return (cls->total_size);
+}
diff --git a/sys/sys/bus.h b/sys/sys/bus.h
index a2e315bb9dee..4ea411a4dd80 100644
--- a/sys/sys/bus.h
+++ b/sys/sys/bus.h
@@ -724,6 +724,7 @@ void device_set_ivars(device_t dev, void *ivars);
const char *device_get_name(device_t dev);
const char *device_get_nameunit(device_t dev);
void *device_get_softc(device_t dev);
+void *device_get_softc_class(device_t dev, kobj_class_t cls);
device_state_t device_get_state(device_t dev);
int device_get_unit(device_t dev);
struct sysctl_ctx_list *device_get_sysctl_ctx(device_t dev);
diff --git a/sys/sys/kobj.h b/sys/sys/kobj.h
index 7705f609bea9..e248f9eafaf8 100644
--- a/sys/sys/kobj.h
+++ b/sys/sys/kobj.h
@@ -31,6 +31,11 @@
#include <sys/types.h>
+/* XXX workaround for bool type for tools/build/test-includes */
+#if !defined(_KERNEL) && !defined(_STANDALONE)
+#include <stdbool.h>
+#endif
+
/*
* Forward declarations
*/
@@ -59,7 +64,9 @@ struct kobj_method {
size_t size; /* object size */ \
kobj_class_t *baseclasses; /* base classes */ \
u_int refs; /* reference count */ \
- kobj_ops_t ops /* compiled method table */
+ kobj_ops_t ops; /* compiled method table */ \
+ size_t total_size; /* total object size */ \
+ bool total_size_inited
struct kobj_class {
KOBJ_CLASS_FIELDS;
@@ -124,7 +131,7 @@ DEFINE_CLASS_0(name, name ## _class, methods, size)
#define DEFINE_CLASS_0(name, classvar, methods, size) \
\
struct kobj_class classvar = { \
- #name, methods, size, NULL \
+ #name, methods, size, NULL, 0, false \
}
/*
@@ -139,7 +146,8 @@ struct kobj_class classvar = { \
static kobj_class_t name ## _baseclasses[] = \
{ &base1, NULL }; \
struct kobj_class classvar = { \
- #name, methods, size, name ## _baseclasses \
+ #name, methods, size, name ## _baseclasses, 0, \
+ false \
}
/*
@@ -155,7 +163,8 @@ static kobj_class_t name ## _baseclasses[] = \
{ &base1, \
&base2, NULL }; \
struct kobj_class classvar = { \
- #name, methods, size, name ## _baseclasses \
+ #name, methods, size, name ## _baseclasses, 0, \
+ false \
}
/*
@@ -172,7 +181,8 @@ static kobj_class_t name ## _baseclasses[] = \
&base2, \
&base3, NULL }; \
struct kobj_class classvar = { \
- #name, methods, size, name ## _baseclasses \
+ #name, methods, size, name ## _baseclasses, 0, \
+ false \
}
/*
@@ -209,6 +219,16 @@ void kobj_init_static(kobj_t obj, kobj_class_t cls);
*/
void kobj_delete(kobj_t obj, struct malloc_type *mtype);
+/*
+ * Get the data offset for the given class.
+ */
+size_t kobj_instance_offset(kobj_class_t cls, kobj_class_t subclass);
+
+/*
+ * Get the total data size of this class and all its subclasses.
+ */
+size_t kobj_total_data_size(kobj_class_t cls);
+
/*
* Maintain stats on hits/misses in lookup caches.
*/