emitting uevents with extra env info

the number of different uevent actions that can be emitted by the kernel
these days is limited, and seems to be fixed.

from include/linux/kobject.h:

40/* 41 * The actions here must match the index to the string array 42 * in lib/kobject_uevent.c 43 * 44 * Do not add new actions here without checking with the driver-core 45 * maintainers. Action strings are not meant to express subsystem 46 * or device specific properties. In most cases you want to send a 47 * kobject_uevent_env(kobj, KOBJ_CHANGE, env) with additional event 48 * specific variables added to the event environment. 49 */50enumkobject_action{51KOBJ_ADD,52KOBJ_REMOVE,53KOBJ_CHANGE,54KOBJ_MOVE,55KOBJ_ONLINE,56KOBJ_OFFLINE,57KOBJ_MAX58};

devs are effectively “gently nudged” towards using KOBJ_ADD+KOBJ_ONLINE
and KOBJ_OFFLINE+KOBJ_REMOVE on start and stop, respectively, of
whatever they’re representing by their kobject-s, with KOBJ_CHANGE acting
as the wildcard/void* of uevents. whatever else you want to tell
userspace - you’re pretty much stuck with KOBJ_CHANGE.

using just one action for all the interesting events might seem a
little limiting at first. but there are two ways in which you can
“enrich” the “context” of the events you’re emitting:

make sure that by the time you’re emitting the event you set some
kobject attr of interest to the new value, and let udev rule match
the subtype of the uevent based on the value of that attr:

SYSFS{attribute}=="some_value"

this is simpler implementation-wise (less code to write in kernel,
trivial to set up in a udev rule), but has the disadvantage of being
slightly slower and not atomic. i.e. if the kobject attr might be
changing reasonably fast, by the time udev will get to match the
contents of the corresponding sysfs file to the udev rule, the
attr might have changed to, e.g., whatever your next uevent wanted
to communicate to userspace.
on the upside, it’s easier to debug - sysfs files are there for
userspace scripts to peek at, whereas uevent env is ephemeral, if
your udev rule was wrong - you just missed the uevent.

when emitting uevent-s, you can add your own KEY=val to the env. while
the performance aspect of choosing this is usually frowned upon, the
atomicity argument in favor of this approach is perfectly valid and rather
important. the tradeoff is pretty much the opposite of the above -
slightly more to write on the kernel side, atomicity, and the env
is ephemeral. the latter obstacle can be partially overcome at dev
time by making udevadm spill the beans on incoming uevent-s to stdout:

udevadm monitor --kernel --property

you can also add --subsystem-match= and --tag-match= to narrow
down what you want to see.

since the former approach is rather self explanatory - just maintain
kobject attrs neatly prior to emitting uevent-s, the rest of the text
deals with the latter approach: directly enriching uevent env.

a simple example comes dock/undock notifications, which just emit the
events with the specified env in one go. instead of the plain vanilla
kobject_uevent(), dock is using the underlying kobject_uevent_env().

392staticvoiddock_event(structdock_station*ds,u32event,intnum)393{394structdevice*dev=&ds->dock_device->dev;395charevent_string[13];396char*envp[]={event_string,NULL};397structdock_dependent_device*dd;398399if(num==UNDOCK_EVENT)400sprintf(event_string,"EVENT=undock");401else402sprintf(event_string,"EVENT=dock");403404/* 405 * Indicate that the status of the dock station has 406 * changed. 407 */408if(num==DOCK_EVENT)409kobject_uevent_env(&dev->kobj,KOBJ_CHANGE,envp);410411list_for_each_entry(dd,&ds->hotplug_devices,hotplug_list)412if(dd->ops&&dd->ops->uevent)413dd->ops->uevent(dd->handle,event,dd->context);414415if(num!=DOCK_EVENT)416kobject_uevent_env(&dev->kobj,KOBJ_CHANGE,envp);417}

a little more complex example comes from GFS2 that installs custom
struct kset_uevent_ops to enrich the env of every event it emits. see
the overview at:

Documentation/filesystems/gfs2-uevents.txt

and impl, including some handy inspiration of start/stop events as well:

fs/gfs2/sys.c

from Documentation/kobject.txt:

If a kset wishes to control the uevent operations of the kobjects
associated with it, it can use the struct kset_uevent_ops to handle it:
struct kset_uevent_ops {
int (*filter)(struct kset *kset, struct kobject *kobj);
const char *(*name)(struct kset *kset, struct kobject *kobj);
int (*uevent)(struct kset *kset, struct kobject *kobj,
struct kobj_uevent_env *env);
};
[...]
The name function will be called to override the default name of the kset
that the uevent sends to userspace. By default, the name will be the same
as the kset itself, but this function, if present, can override that name.
The uevent function will be called when the uevent is about to be sent to
userspace to allow more environment variables to be added to the uevent.

and it’s the uevent() that GFS2 overrides to add several common KEY=val
entries to the env of all the events it emits.