Hot Plugging: hotplug

  • admin 



The hotplug kernel subsystem loads drivers for peripherals that can be hotplugged. This includes USB peripherals (increasingly common), PCMCIA (common expansion cards for laptops), IEEE 1394 (also called “Firewire” or “I-Link”), some SATA hard drives, and even, for some high-end servers, PCI or SCSI devices.

The kernel has a database that associates each device ID with the required driver. This database is used during boot to load all the drivers for the peripheral devices detected on the different buses mentioned, but also when an additional hotplug device is connected.

Once a driver is loaded, a message is sent to udevd so it will be able to create the corresponding entry in /dev/.
The Naming Problem

Before the appearance of hotplug connections, it was easy to assign a fixed name to a device. It was based simply on the position of the devices on their respective bus. But this is not possible when such devices can come and go on the bus. The typical case is the use of a digital camera and a USB key, both of which appear to the computer as disk drives. The first one connected may be /dev/sdb and the second /dev/sdc (with /dev/sda representing the computer’s own hard drive). The device name is not fixed; it depends on the order in which devices are connected.

Additionally, more and more drivers use dynamic values for devices’ major/minor numbers, which makes it impossible to have static entries for the given devices, since these essential characteristics may vary after a reboot.

udev was created precisely to solve this problem.

NOTE: IN PRACTICE Network card management
Many computers have multiple network cards (sometimes two wired interfaces and a wifi interface), and with hotplug support on most bus types, the 2.6 kernel no longer guarantees fixed naming of network interfaces. But a user who wants to configure their network in /etc/network/interfaces needs a fixed name!

It would be difficult to ask every user to create their own udev rules to address this problem. This is why udev was configured in a rather peculiar manner; on first boot (and, more generally, each time that a new network card appears) it uses the name of the network interface and its MAC address to create new rules that will reassign the same name on subsequent boots. These rules are stored in /etc/udev/rules.d/70-persistent-net.rules.
This mechanism has some side effects that you should know about. Let’s consider the case of computer that has only one PCI network card. The network interface is named eth0, logically. Now say the card breaks down, and the administrator replaces it; the new card will have a new MAC address. Since the old card was assigned the name, eth0, the new one will be assigned eth1, even though the eth0 card is gone for good (and the network will not be functional because /etc/network/interfaces likely configures an eth0 interface). In this case, it is enough to simply delete the /etc/udev/rules.d/70-persistent-net.rules file before rebooting the computer. The new card will then be given the expected eth0 name.

How udev Works
When udev is notified by the kernel of the appearance of a new device, it collects various information on the given device by consulting the corresponding entries in /sys/, especially those that uniquely identify it (MAC address for a network card, serial number for some USB devices, etc.).
Armed with all of this information, udev then consults all of the rules contained in /etc/udev/rules.d/ and /lib/udev/rules.d/. In this process it decides how to name the device, what symbolic links to create (to give it alternative names), and what commands to execute. All of these files are consulted, and the rules are all evaluated sequentially (except when a file uses “GOTO” directives). Thus, there may be several rules that correspond to a given event.
The syntax of rules files is quite simple: each row contains selection criteria and variable assignments. The former are used to select events for which there is a need to react, and the latter defines the action to take. They are all simply separated with commas, and the operator implicitly differentiates between a selection criterion (with comparison operators, such as == or !=) or an assignment directive (with operators such as =, += or :=).
Comparison operators are used on the following variables:


  • KERNEL: the name that the kernel assigns to the device;
  • ACTION: the action corresponding to the event (“add” when a device has been added, “remove” when it has been removed);
  • DEVPATH: the path of the device’s /sys/ entry;
  • SUBSYSTEM: the kernel subsystem which generated the request (there are many, but a few examples are “usb”, “ide”, “net”, “firmware”, etc.);
  • ATTR{attribut}: file contents of the attribute file in the /sys/$devpath/ directory of the device. This is where you find the MAC address and other bus specific identifiers;
  • KERNELS, SUBSYSTEMS and ATTRS{attributes} are variations that will try to match the different options on one of the parent devices of the current device;
  • PROGRAM: delegates the test to the indicated program (true if it returns 0, false if not). The content of the program’s standard output is stored so that it can be reused by the RESULT test;
  • RESULT: execute tests on the standard output stored during the last call to PROGRAM.

The right operands can use pattern expressions to match several values at the same time. For instance, * matches any string (even an empty one); ? matches any character, and [] matches the set of characters listed between the square brackets (or the opposite thereof if the first character is an exclamation point, and contiguous ranges of characters are indicated like a-z).
Regarding the assignment operators, = assigns a value (and replaces the current value); in the case of a list, it is emptied and contains only the value assigned. := does the same, but prevents later changes to the same variable. As for +=, it adds an item to a list. The following variables can be changed:

  • NAME: the device filename to be created in /dev/. Only the first assignment counts; the others are ignored;
  • SYMLINK: the list of symbolic links that will point to the same device;
  • OWNER, GROUP and MODE define the user and group that owns the device, as well as the associated permission;
  • RUN: the list of programs to execute in response to this event.

The values assigned to these variables may use a number of substitutions:
$kernel or %k: equivalent to KERNEL;
$number or %n: the order number of the device, for example, for sda3, it would be “3”;
$devpath or %p: equivalent to DEVPATH;
$attr{attribute} or %s{attribute}: equivalent to ATTRS{attribute};
$major or %M: the kernel major number of the device;
$minor or %m: the kernel minor number of the device;
$result or %c: the string output by the last program invoked by PROGRAM;
and, finally, %% and $$ for the percent and dollar sign, respectively.
The above lists are not complete (they include only the most important parameters), but the udev(7) manual page should be.
A concrete example
Let us consider the case of a simple USB key and try to assign it a fixed name. First, you must find the elements that will identify it in a unique manner. For this, plug it in and run udevadm info -a -n /dev/sdc (replacing /dev/sdc with the actual name assigned to the key).
# udevadm info -a -n /dev/sdc
looking at device ‘/devices/pci0000:00/0000:00:10.3/usb1/1-2/1-2.2/1-2.2:1.0/host9/target9:0:0/9:0:0:0/block/sdc’:
ATTR{stat}==» 51 100 1208 256 0 0 0 0 0 192 25 6″
ATTR{inflight}==» 0 0″
looking at parent device ‘/devices/pci0000:00/0000:00:10.3/usb1/1-2/1-2.2/1-2.2:1.0/host9/target9:0:0/9:0:0:0’:
ATTRS{vendor}==»I0MEGA »
ATTRS{model}==»UMni64MB*IOM2C4 »
ATTRS{rev}==» »
looking at parent device ‘/devices/pci0000:00/0000:00:10.3/usb1/1-2/1-2.2’:
ATTRS{bNumInterfaces}==» 1″
ATTRS{manufacturer}==»USB Disk»
ATTRS{product}==»USB Mass Storage Device»
To create a new rule, you can use tests on the device’s variables, as well as those of one of the parent devices. The above case allows us to create two rules like these:
KERNEL==»sd?», SUBSYSTEM==»block», ATTRS{serial}==»M004021000001″, SYMLINK+=»usb_key/disk»
KERNEL==»sd?[0-9]», SUBSYSTEM==»block», ATTRS{serial}==»M004021000001″, SYMLINK+=»usb_key/part%n»
Once these rules are set in a file, named for example /etc/udev/rules.d/010_local.rules, you can simply remove and reconnect the USB key. You can then see that /dev/usb_key/disk represents the disk associated with the USB key, and /dev/usb_key/part1 is its first partition.
GOING FURTHER Debugging udev’s configuration

Like many daemons, udevd stores logs in /var/log/daemon.log. But it is not very verbose by default, and it’s usually not enough to understand what’s happening. The udevadm control –log-priority=info command increases the verbosity level and solves this problem. udevadm control –log-priority=err returns to the default verbosity level.