tech-kern archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
Re: General device properties API
> Date: Sat, 11 Sep 2021 17:37:32 -0700
> From: Jason Thorpe <thorpej%me.com@localhost>
>
> > On Sep 11, 2021, at 4:29 PM, Taylor R Campbell <campbell+netbsd-tech-kern%mumble.net@localhost> wrote:
> >
> > Here's another sketch that is much more flexible and general.
> > Adapting it to link sets should be easy; the main point is that the
> > only untyped parts going through void * are limited to the definition
> > site of a generic operation; everything else is fully typed.
>
> Looks quite verbose, but I'll study it some more. The sketch seems
> somewhat incomplete, so I'm not really getting how you're suggesting
> it should be used.
To define an operation (a `device call' or a `generic') that can be
implemented and invoked, create a .h file and a .c file for the
interface.
- The .h file contains
(a) a convenience typedef for the operation's signature,
(b) a macro for creating a vtable entry, and
(c) a wrapper for extracting the vtable entry into the correct type.
(The i2c_enumerate_devices_typecheck declaration in the example
isn't actually defined as a symbol -- it's just there to allow
compile-time type-checking of I2C_ENUMERATE_DEVICES_METHOD, so it
can detect when the function has the wrong type before converting to
void *.)
- The .c file contains
(a) a definition of a symbol so the linker guarantees it's unique
(link-time namespace collision detection),
(b) a definition of the wrapper function that extracts the vtable
entry with the correct type.
Nothing else needs to be in the .c file -- it's very small (a few
bytes) and is just needed by any code that uses the interface. The
wrapper function could also be in the .h file as a static inline.
A lot of this can easily be gathered into a handful of macros. E.g.,
i2c_bus_interface.h: DECLARE_GENERIC(int, i2c_enumerate_devices,
(struct i2c_attach_args *, bool (*) [...]));
#define I2C_ENUMERATE_DEVICES_METHOD(fn) \
METHOD(i2c_enumerate_devices, fn)
i2c_bus_interface.c: DEFINE_GENERIC(i2c_enumerate_devices);
A component that implements an operation just puts it in its vtable
(or link set or whatever):
#include "i2c_enumdevices.h"
static const struct vtable_entry acpi_device_handle_methods[] = {
I2C_ENUMERATE_DEVICES_METHOD(acpi_i2c_enumerate_devices),
...
};
static const struct vtable acpi_device_handle_vtable = {
acpi_device_handle_methods,
__arraycount(acpi_device_handle_methods),
};
(Adapting to a link set left as an exercise for the reader; the basic
mechanism remains the same -- a list of entries tagged by a defined
symbol, with a wrapper for the correct type of that defined symbol.)
A component that needs to use an operation pulls it out of the vtable
using the wrapper to use it:
struct i2c_attach_args ia;
int error;
error = i2c_enumerate_devices(h->dh_vtab)(&ia,
i2c_enumerate_devices_callback, cookie);
> Another thing I'm trying to comprehend here is just what kind of
> typos and type errors you think will be prevented. If the name of
> the `args' structure is wrong, it will either fail to compile
> (because the args structure name used doesn't exist, so accessing
> fields will fail to compile), or the error will be glaringly obvious
> because you used `spi_enumerate_devices_args' when you should have
> use `i2c_enumerate_devices_args' and the member names are different.
struct spi_enumerate_device_args args = ...;
/*
* runtime memory corruption -- simply operates on the wrong
* type of object, when it should be a trivially detected
* compile-time type error
*/
rv = device_call(h, "i2c-enumerate-devices", &args);
/*
* quiet loss of functionality -- simply returns ENOTSUP at
* runtime, when it should be a trivially detected
* compile-time typo error
*/
rv = device_call(h, "spi-enumerate-device", &args);
/*
* more runtime memory corruption -- I've been bitten by
* something like pcibus_attach_args vs pci_attach_args
* because autoconf is not type-safe
*/
static int
acpi_i2c_enumerate_devices(device_t dev, devhandle_t handle, void *v)
{
struct i2cbus_enumerate_devices_args *args = v;
... args->ia, args->callback ...
}
These are basic mistakes that at least I make all the time and that we
have had the technology to completely rule out at compile-time for
decades; I don't think it should be surprising for me to object if new
systems entirely under our control can't detect them.
> > If this doesn't meet the attributes you laid out earlier either, I
> > would appreciate it if you could identify which ones I missed --
> > there's too much going on here to fit in my head at once.
>
> [...]
> ==> Does not require referencing a symbol that would cause a link
> failure if a method is not available on some platform scheme.
> [...]
> But one problem your vtable sketch has is that is requires
> referencing a symbol (i2c_enumerate_devices_generic,
> i2c_enumerate_devices_typecheck, and i2c_enumerate_devices).
Why is this a requirement/problem?
The current mechanism still needs space in the kernel for the text of
the name (the string "i2c-enumerate-devices"). Why refuse to expose
that name to the linker so it can detect typos and namespace
collisions?
> I suppose I should adjust my #2 property above... in addition to not
> causing a link failure if e.g. ACPI doesn't provide some method, nor
> should there be a link failure if ACPI provides enumeration support
> for I2C but the I2C module isn't currently loaded.
Only a .c file for the interface -- not the whole i2c subsystem --
needs to be included.
Home |
Main Index |
Thread Index |
Old Index