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
This commit is contained in:
Michal Meloun committed 2026-09-11 16:09:36 +02:00
1 parent b7541acacb
commit 477c594d90
4 files changed
+142 -8

No files matched your search

+16 -3
View File
@@ -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);
+100
View File
@@ -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);
}
+1
View File
@@ -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);
+25 -5
View File
@@ -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.
*/