mirror of
https://github.com/freebsd/freebsd-src.git
synced 2026-11-07 12:48:33 -10:00
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:
1 parent
b7541acacb
commit
477c594d90
4 files changed
+142
-8
No files matched your search
+16
-3
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
@@ -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.
|
||||
*/
|
||||
|
||||
Reference in new issue
Block a user