LWN.net Logo

 


 
Summary page
Return to the Kernel page
 
Recent Features

LWN.net Weekly Edition for March 18, 2004

LWN.net Weekly Edition for March 11, 2004

The annotated SCO stock price chart

A grumpy editor's calendar search

LWN.net Weekly Edition for March 4, 2004

Printable page
 

 

kobjects and hotplug events

Last week, in the article about kobjects, it was mentioned that a kset has a set of hotplug operations. This week we will introduce the hotplug operations, and detail how they work.

Remember that a kset is a group of kobjects which are all embedded in the same type of structure. In the definition of a kset, a pointer to a struct kset_hotplug_ops is specified. If this pointer is set, whenever a kobject that is a member of that kset is created or destroyed by the kernel, the userspace program /sbin/hotplug will be called. If a kobject does not have a kset associated with it, the kernel will traverse up the kobject hierarchy (using the parent pointer) to try to find a kset to use for this test.

struct kset_hotplug_ops is a structure containing three function pointers and is defined as:

    struct kset_hotplug_ops {
	int (*filter)(struct kset *kset, struct kobject *kobj);
	char *(*name)(struct kset *kset, struct kobject *kobj);
	int (*hotplug)(struct kset *kset, struct kobject *kobj, 
                       char **envp, int num_envp, 
		       char *buffer, int buffer_size);
    };

Hotplug filters

The filter function will be called by the kernel before a hotplug operation happens. The kobject and the kset which are being used for the hotplug event are passed as parameters to the function. If this function returns 1 then the hotplug event will be generated; otherwise (if the function returns 0), the hotplug event will not be generated. This function is used by the driver core and the block subsystem to filter out hotplug events for kobjects that are owned by these systems but which should not have hotplug events generated for them.

As an example, the driver core's hotplug filter is contained in the file drivers/base/core.c and looks like:

static int dev_hotplug_filter(struct kset *kset, struct kobject *kobj)
{
	struct kobj_type *ktype = get_ktype(kobj);

	if (ktype == &ktype_device) {
		struct device *dev = to_dev(kobj); 
		if (dev->bus)
			return 1;
	}
	return 0;
}

In this function, the first thing that happens is the type of the kobject is checked. If this really is a device type of kobject, then we know it is safe to cast this kobject to a struct device, which is done in the line:

    struct class_device *class_dev = to_class_dev(kobj);

If this class device has a class assigned to it (dev->bus), the filter function tells the kobject core that it is acceptable to generate a hotplug event for this object. If any of these tests fail, the function returns 0 stating that no hotplug event should be generated.

The filter function allows objects in the device tree to own kobjects themselves (to create subdirectories, and for other uses) and prevent hotplug events from being created for these child kobjects.

Hotplug event names

When /sbin/hotplug is called by the kernel, it only has one argument passed to it, the name of the subsystem creating the event. All other information about the hotplug event is passed in environment variables. For detailed examples of some of the hotplug events and environment variables, see the Linux Hotplug project website.

For the kobject core to know what kind of name to provide to this hotplug event, the name function callback is provided. If the kset associated with this kobject wants to override the name of the kset for the hotplug event, then this function needs to return a pointer to a string that is more suitable. If this function is not provided, or it returns NULL, then the kset's name will be used.

For example, all struct device objects in the kernel belong to the same device kset (the device, driver, and class model sits on top of kobjects and ksets, making it simpler for driver authors to use). This kset is called "devices". It would not make much sense for every USB or IEEE1394 device that was plugged into, or removed from the system to generate a hotplug event with the name "devices". Because of this, the device subsystem has a name function for its hotplug operations:

static char *dev_hotplug_name(struct kset *kset, struct kobject *kobj)
{       
	struct device *dev = to_dev(kobj);

	return dev->bus->name; 
}       

In this function, the kobject is converted to a struct device, and then the name of the bus associated with this device is returned. This allows USB devices to create hotplug events with the name "usb" and IEEE1394 devices to create hotplug events with the name "ieee1394".

One note about this function: the only way that we know it is safe to directly cast this kobject into a struct device is that it has passed the filter function first. In that function, the type of the kobject and the fact that the device had a pointer to a bus was verified. Without that filter function, that information would have to be checked before blindly casting and following two levels of pointer indirection.

Hotplug environment variables

All calls to /sbin/hotplug provide the majority of information within environment variables. The three variables that are always set for every hotplug call are the following:

Variable Value Description
ACTION add or remove Describes if the kobject is being added or removed from the system.
SEQNUM numeric Provides the sequence number of the hotplug event. It is used for userspace to determine if it has received the hotplug event out of order or not. The value starts out a 0 when the kernel boots, and increments with every /sbin/hotplug call. It is a 64-bit number, so it will not roll over for a very long time.
DEVPATH string The path to the kobject that the hotplug event is happening on, within the sysfs file system. To get the true filesystem location for this kobject, add the mount point for sysfs (usually /sys) to the beginning of this string.

These variables are usually enough for userspace to determine what is happening with this hotplug event, but a lot of subsystems want to provide more information. This is especially true when a kobject is removed from the system, as the sysfs entry for the device will also be removed, preventing userspace from being able to look up any attributes about the device that was just removed. Because of this, the hotplug callback is provided for the kset to provide any additional environment variables that it wants to.

The hotplug function callback is allowed to add any additional environment variables that the kset might want added for this call to /sbin/hotplug. To review the prototype for this function:

    int (*hotplug)(struct kset *kset, struct kobject *kobj, 
                   char **envp, int num_envp, 
		   char *buffer, int buffer_size);

Here, kset and kobj are the objects for which the event is happening, envp is a pointer to an array of environment variables (in the usual "NAME=value" format), num_envp is the length of envp, buffer is a buffer where additional variables can be put, and buffer_size is the size of buffer. The hotplug function should create any additional environment variables that are called for, store pointers to them in envp, and terminate envp with a NULL. If the hotplug callback returns a non-zero value, the hotplug event is aborted, and /sbin/hotplug will not be called.

The driver and class subsystems pass hotplug calls down to the bus and class owners of the kobject that is being created or removed, allowing these individual subsystems to add their own environment variables. For example, for all devices located on the USB bus, the function usb_hotplug() in the drivers/usb/core/usb.c file will be called. This function is defined as (with much of the boring code removed):

static int usb_hotplug(struct device *dev, char **envp, int num_envp,
		       char *buffer, int buffer_size)
{
	struct usb_interface *intf;
	struct usb_device *usb_dev;
	char *scratch;
	int i = 0;
	int length = 0;

	/* ... */
	intf = to_usb_interface(dev);
	usb_dev = interface_to_usbdev(intf);

	/* ... */
	scratch = buffer;
	envp[i++] = scratch;
	length += snprintf(scratch, buffer_size - length, "PRODUCT=%x/%x/%x",
			   usb_dev->descriptor.idVendor,
			   usb_dev->descriptor.idProduct,
			   usb_dev->descriptor.bcdDevice);
	if ((buffer_size - length <= 0) || (i >= num_envp))
		return -ENOMEM;
	++length;
	scratch += length;

	/* ... */
	envp[i++] = NULL;
	return 0;
}

The lines:

	scratch = buffer;
	envp[i++] = scratch;
set up the environment pointer to point to the next location in the buffer passed to us. Then the big call to snprintf creates a variable called PRODUCT which is assigned the value of the USB device's vendor, product and device ids separated by a '/' character. If snprintf succeeded in not overrunning the buffer provided to us, and we still have enough room for one more environment variable, then the function continues on. The last environment variable pointer is set to NULL before returning.

All that work for a simple result

With the combined effort of the kset hotplug function callbacks every kset can customize the call to /sbin/hotplug in whatever way it likes while still providing userspace a consistent interface from the kernel. Every kobject that is registered with sysfs can generate this call easily, so all parts of the kernel that use kobjects and ksets automatically get the /sbin/hotplug interface for free. This allows userspace projects such as the module loading scripts, devlabel, udev, and D-BUS valuable information as to what the kernel is doing whenever a change in the kobject tree occurs.


Post a comment

  kobjects and hotplug events
(Posted Dec 3, 2003 19:09 UTC (Wed) by pipo) (Post reply)

Misleading in paragraph 'hotplug events'. The code seems to be right, but the accompanying text suddenly speaks about class_device instead of struct device (a class_device doesn't have a member named bus).

Copyright (©) 2003, Eklektix, Inc.
Linux (®) is a registered trademark of Linus Torvalds
Powered by Rackspace Managed Hosting.