udev

Dynamic Kernel Device Management with udev

Udev is the Linux subsystem that supplies your computer with device events.

1 The /dev Directory

The device nodes in the /dev directory provide access to the corresponding kernel devices.
The content of the /dev directory is kept on a temporary file system and all files are rendered at every system start-up.

2 Kernel uevents and udev

The required device information is exported by the sysfs file system.
For every device the kernel has detected and initialized, a directory with the device name is created. It contains attribute files with device-specific properties.
Every time a device is added or removed, the kernel sends a uevent to notify the udev daemon of the change.
  • The udev daemon reads and parses all rules from the /usr/lib/udev/rules.d/*.rules and /etc/udev/rules.d/*.rules files at start-up and keeps them in memory.
  • If rules files are changed, added or removed, the daemon can reload their in-memory representation with the command udevadm control --reload.
  • The driver core uevents(a MODALIAS string) are received from a kernel netlink socket.
  • Every received event by the udev daemon is matched against the set of provided udev rules.
    The rules can
    • add or change event environment keys
    • request a specific name for the device node to create
    • add symbolic links pointing to the node or add programs to run after the device node is created

3 Drivers, Kernel Modules and Devices

The kernel bus drivers probe for devices.
For every detected device, the kernel creates an internal device structure while the driver core sends a uevent to the udev daemon. Modalias is a little sysfs trick that exports hardware information to a file named 'modalias'.
The hardware information is told by the hardware to the kernel.
This file simply contains a formatted form of the information normal hardware exposes.

$ cat /sys/devices/pci0000:00/0000:00:1f.0/modalias
pci:v00008086d0000A143sv00001043sd00008694bc06sc01i00
This specifies a structure for exposing this information.
  • the path name
  • This expose the information about the hardware system related to a device:
    • pci0000:00.
    • the id for the first PCI bus
    • 0000:00:1f.0
    • the index of the given device on the PCI bus. Specifically, this is on bus 0000:00 and has index 1f.1
    lscpi can show the readable information:
    
    $ lspci | grep "00:1f.0"
    00:1f.0 ISA bridge: Intel Corporation H110 Chipset LPC/eSPI Controller (rev 31)
    
    $ lspci -n  | grep "00:1f.0"
    00:1f.0 0601: 8086:a143 (rev 31)    
        
  • the content in the modalias file
  • This is a structured data, each of these identifiers, and corresponding hex numbers represent some of the information a hardware device exposes:
    • v
    • the vendor id, 00008086
    • d
    • the device id, 0000A143
    • sv
    • the subsystem version of vendor id, 00001043
    • sd
    • the subsystem version of device id, 00008694
    • bc
    • base class, 06
    • sc
    • sub class, 01
    • i
    • Programming interface 00
You can even find websites to look up specific hardware identification based on the vendor and device ids, for instance, https://devicehunt.com/.

The kernel takes the device information, composes a MODALIAS string from it and sends that string along with the event.
Every device driver carries a list of known aliases for devices it can handle. The list is contained in the kernel module file itself.
When you run depmod, it reads the ID lists and creates a series of map file in the kernel's /lib/modules/$(uname -r) directory for all currently available modules.


$ find /lib/modules -name modules.alias -print
/lib/modules/5.11.0-37-generic/modules.alias
/lib/modules/5.11.0-38-generic/modules.alias
/lib/modules/5.8.0-43-generic/modules.alias
/lib/modules/5.13.0-27-generic/modules.alias
/lib/modules/5.11.0-46-generic/modules.alias
/lib/modules/5.13.0-28-generic/modules.alias
/lib/modules/5.11.0-40-generic/modules.alias
/lib/modules/5.11.0-42-generic/modules.alias
/lib/modules/5.11.0-44-generic/modules.alias
/lib/modules/5.13.0-30-generic/modules.alias
/lib/modules/5.11.0-41-generic/modules.alias
This file module.alias contains aliases, or secondary names for modules. It's a rather simple format of

alias <something>                   <actual module>
For ex., snd_intel8x0m:

$ grep snd_intel8x0m /lib/modules/`uname -r`/modules.alias
alias pci:v00001022d0000746Esv*sd*bc*sc*i* snd_intel8x0m
alias pci:v000010DEd000000D9sv*sd*bc*sc*i* snd_intel8x0m
alias pci:v000010DEd00000089sv*sd*bc*sc*i* snd_intel8x0m
alias pci:v000010DEd00000069sv*sd*bc*sc*i* snd_intel8x0m
alias pci:v000010DEd000001C1sv*sd*bc*sc*i* snd_intel8x0m
alias pci:v00001039d00007013sv*sd*bc*sc*i* snd_intel8x0m
alias pci:v00001022d00007446sv*sd*bc*sc*i* snd_intel8x0m
alias pci:v00008086d00007196sv*sd*bc*sc*i* snd_intel8x0m
alias pci:v00008086d000027DDsv*sd*bc*sc*i* snd_intel8x0m
alias pci:v00008086d0000266Dsv*sd*bc*sc*i* snd_intel8x0m
alias pci:v00008086d000024D6sv*sd*bc*sc*i* snd_intel8x0m
alias pci:v00008086d000024C6sv*sd*bc*sc*i* snd_intel8x0m
alias pci:v00008086d00002486sv*sd*bc*sc*i* snd_intel8x0m
alias pci:v00008086d00002446sv*sd*bc*sc*i* snd_intel8x0m
alias pci:v00008086d00002426sv*sd*bc*sc*i* snd_intel8x0m
alias pci:v00008086d00002416sv*sd*bc*sc*i* snd_intel8x0m

The "*" indicates it will match anything.
I can add "alias boogabooga snd_intel8x0m" and then safely "modprobe boogabooga".


$ grep fs- /lib/modules/`uname -r`/modules.alias
alias fs-msdos msdos
alias fs-virtiofs virtiofs
alias fs-binfmt_misc binfmt_misc
alias fs-reiserfs reiserfs
alias fs-cramfs cramfs
...
Each specific module should know what hardware it supports, as it's coded specifically for that hardware.
Look at the modinfo for snd_intel8x0m:

$ modinfo snd_intel8x0m
  filename:       /lib/modules/2.6.14-ARCH/kernel/sound/pci/snd-intel8x0m.ko
  author:         Jaroslav Kysela <perex@suse.cz>
  description:    Intel 82801AA,82901AB,i810,i820,i830,i840,i845,MX440; SiS 7013; NVidia MCP/2/2S/3 modems
  license:        GPL
  vermagic:       2.6.14-ARCH SMP preempt 686 gcc-4.1
  depends:        snd-ac97-codec,snd-pcm,snd-page-alloc,snd
  alias:          pci:v00008086d00002416sv*sd*bc*sc*i*
  alias:          pci:v00008086d00002426sv*sd*bc*sc*i*
  alias:          pci:v00008086d00002446sv*sd*bc*sc*i*
  alias:          pci:v00008086d00002486sv*sd*bc*sc*i*
  alias:          pci:v00008086d000024C6sv*sd*bc*sc*i*
  alias:          pci:v00008086d000024D6sv*sd*bc*sc*i*
  alias:          pci:v00008086d0000266Dsv*sd*bc*sc*i*
  alias:          pci:v00008086d000027DDsv*sd*bc*sc*i*
  alias:          pci:v00008086d00007196sv*sd*bc*sc*i*
  alias:          pci:v00001022d00007446sv*sd*bc*sc*i*
  alias:          pci:v00001039d00007013sv*sd*bc*sc*i*
  alias:          pci:v000010DEd000001C1sv*sd*bc*sc*i*
  alias:          pci:v000010DEd00000069sv*sd*bc*sc*i*
  alias:          pci:v000010DEd00000089sv*sd*bc*sc*i*
  alias:          pci:v000010DEd000000D9sv*sd*bc*sc*i*
Each module knows exactly what it supports, and therefore depmod can use that information to help load modules.
These aliases exported by each module, are gathered by depmod and merged into the modules.alias file dynamically.

udev daemon load modules based on the modalias when a new device is added (or when udev is first started on boot).
The following line is part of the default udev rules:


DRIVER!="?*", ENV{MODALIAS}=="?*", RUN{builtin}="kmod load $env{MODALIAS}"
If modprobe $ALIAS is called, modprobe looks in the module directory /lib/modules/`uname -r` for all the modules and other files. It matches the device alias composed for the device with the aliases provided by the modules. If a matching entry is found, that module is loaded.
modprobe expects an up-to-date modules.dep.bin file as generated by the depmod. This file lists what other modules each module needs (if any), and modprobe uses this to add or remove these dependencies automatically.

4 Booting and Initial Device Setup

All device events happening during the boot process before the udev daemon is running are lost, therefore, the kernel provides a uevent file located in the device directory of every device in the sysfs file system.
udev daemon requests all device events from the kernel after the root file system is available.
A simple loop over all uevent files in /sys triggers all events again to create the device nodes and perform device setup.

5 Monitoring the Running udev Daemon

The program udevadm monitor can be used to visualize the driver core events and the timing of the udev event processes.
The events after plug off a USB WiFi dongle:

$ udevadm monitor
monitor will print the received events for:
UDEV - the event which udev sends out after rule processing
KERNEL - the kernel uevent

KERNEL[3284.562361] remove   /devices/pci0000:00/0000:00:14.0/usb1/1-8/1-8:1.0/net/wlx2c4d54ccfca6/queues/rx-0 (queues)
KERNEL[3284.562431] remove   /devices/pci0000:00/0000:00:14.0/usb1/1-8/1-8:1.0/net/wlx2c4d54ccfca6/queues/tx-0 (queues)
KERNEL[3284.562488] remove   /devices/pci0000:00/0000:00:14.0/usb1/1-8/1-8:1.0/net/wlx2c4d54ccfca6 (net)
UDEV  [3284.566068] remove   /devices/pci0000:00/0000:00:14.0/usb1/1-8/1-8:1.0/net/wlx2c4d54ccfca6/queues/rx-0 (queues)
UDEV  [3284.567845] remove   /devices/pci0000:00/0000:00:14.0/usb1/1-8/1-8:1.0/net/wlx2c4d54ccfca6/queues/tx-0 (queues)
UDEV  [3284.570459] remove   /devices/pci0000:00/0000:00:14.0/usb1/1-8/1-8:1.0/net/wlx2c4d54ccfca6 (net)

KERNEL[3284.593957] remove   /devices/pci0000:00/0000:00:14.0/usb1/1-8/1-8:1.0/ieee80211/phy0/rfkill0 (rfkill)
UDEV  [3284.596105] remove   /devices/pci0000:00/0000:00:14.0/usb1/1-8/1-8:1.0/ieee80211/phy0/rfkill0 (rfkill)

KERNEL[3284.610008] remove   /devices/pci0000:00/0000:00:14.0/usb1/1-8/1-8:1.0/ieee80211/phy0 (ieee80211)
KERNEL[3284.610283] unbind   /devices/pci0000:00/0000:00:14.0/usb1/1-8/1-8:1.0 (usb)
KERNEL[3284.610346] remove   /devices/pci0000:00/0000:00:14.0/usb1/1-8/1-8:1.0 (usb)
KERNEL[3284.610832] unbind   /devices/pci0000:00/0000:00:14.0/usb1/1-8 (usb)
KERNEL[3284.610904] remove   /devices/pci0000:00/0000:00:14.0/usb1/1-8 (usb)
UDEV  [3284.612271] remove   /devices/pci0000:00/0000:00:14.0/usb1/1-8/1-8:1.0/ieee80211/phy0 (ieee80211)
UDEV  [3284.614170] unbind   /devices/pci0000:00/0000:00:14.0/usb1/1-8/1-8:1.0 (usb)
UDEV  [3284.616314] remove   /devices/pci0000:00/0000:00:14.0/usb1/1-8/1-8:1.0 (usb)
UDEV  [3284.618993] unbind   /devices/pci0000:00/0000:00:14.0/usb1/1-8 (usb)
UDEV  [3284.620921] remove   /devices/pci0000:00/0000:00:14.0/usb1/1-8 (usb)

  • The KERNEL lines show the events the kernel has sent over netlink.
  • The UDEV lines show the the event udev sends out after rule processing.
udevadm monitor --env can show the complete event environment:

UDEV  [3865.660400] remove   /devices/pci0000:00/0000:00:14.0/usb1/1-6/1-6.4 (usb)
ACTION=remove
BUSNUM=001
DEVNAME=/dev/bus/usb/001/006
DEVNUM=006
DEVPATH=/devices/pci0000:00/0000:00:14.0/usb1/1-6/1-6.4
DEVTYPE=usb_device
MAJOR=189
MINOR=5
PRODUCT=5e3/723/9451
SEQNUM=4017
SUBSYSTEM=usb
TYPE=0/0/0
USEC_INITIALIZED=3865648161

udev also sends messages to syslog.

6 Influencing Kernel Device Event Handling with udev Rules

A udev rule can match any property the kernel adds to the event itself or any information that the kernel exports to sysfs.
The udev rules are read from the files located in:
  • the system rules directory: /lib/udev/rules.d
  • the volatile runtime directory: /run/udev/rules.d
  • the local administration directory: /etc/udev/rules.d
Files in /etc have the highest priority.
Rule files must have the extension .rules.
Every line in the rules file contains at least one key-value pair.
  • keys
  • The following key names can be used to match against device properties.
    • ACTION
    • Match the name of the event action.
    • DEVPATH
    • Match the devpath of the event device.
    • KERNEL
    • Match the name of the event device.
    • NAME
    • Match the name of a network interface. It can be used once the NAME key has been set in one of the preceding rules.
    • SYMLINK
    • Match the name of a symlink targeting the node.
    • SUBSYSTEM
    • Match the subsystem of the event device.
    • DRIVER
    • Match the driver name of the event device.
    • ATTR{filename}
    • Match sysfs attribute values of the event device.
    • SYSCTL{kernel parameter}
    • KERNELS
    • Search the devpath upwards for a matching device name.
    • SUBSYSTEMS
    • Search the devpath upwards for a matching device subsystem name.
    • DRIVERS
    • Search the devpath upwards for a matching device driver name.
    • ATTRS{filename}
    • Search the devpath upwards for a device with matching sysfs attribute values.
    • TAGS
    • Search the devpath upwards for a device with matching tag.
    • ENV{key}
    • Match against a device property value.
    • TAG
    • Match against a device tag.
    • TEST{octal mode mask}
    • PROGRAM
    • RESULT
  • operators
    • ==
    • Compare for equality.
    • !=
    • Compare for inequality.
    • =
    • Assign a value to a key.
    • +=
    • Add the value to a key that holds a list of entries.
    • -=
    • Remove the value from a key that holds a list of entries.
    • :=
    • Assign a value to a key finally; disallow any later changes.
A rule consists of a comma-separated list of one or more key-value pairs.
Each key has a distinct operation, depending on the used operator.
There are two kinds of keys:
  • match
  • If all match keys match against their values, the rule gets applied
  • assignment
  • The assignment keys get the specified values assigned.
For ex., /lib/udev/rules.d/*.rules:

# do not edit this file, it will be overwritten on update

# run a command on remove events
ACTION=="remove", ENV{REMOVE_CMD}!="", RUN+="$env{REMOVE_CMD}"
ACTION=="remove", GOTO="default_end"

# console
KERNEL=="console", MODE="0600", OPTIONS="last_rule"

# serial devices
KERNEL=="ttyUSB*", ATTRS{product}=="[Pp]alm*Handheld*", SYMLINK+="pilot"

# printer
SUBSYSTEM=="usb", KERNEL=="lp*", NAME="usb/%k", SYMLINK+="usb%k", GROUP="lp"

# kernel firmware loader
SUBSYSTEM=="firmware", ACTION=="add", RUN+="firmware.sh"

LABEL="default_end"
  • The console rule
  • This consists of three keys: one match(==) key (KERNEL) and two assign(=) keys (MODE, OPTIONS).
    1. The KERNEL match rule searches the device list for any items of the type console.
    2. Only exact matches are valid and trigger this rule to be executed.
    3. The MODE key assigns special permissions to the device node
    4. In this case, read and write permissions to the owner of this device only.
    5. The OPTIONS key makes this rule the last rule to be applie to any device of this type.
    6. Any later rule matching this particular device type does not have any effect.
  • The serial devices rule
  • is not available in 50-udev-default.rules anymore, but it is still worth considering. It consists of two match keys (KERNEL and ATTRS) and one assign key (SYMLINK). The KERNEL key searches for all devices of the ttyUSB type. Using the * wild card, this key matches several of these devices. The second match key, ATTRS, checks whether the product attribute file in sysfs for any ttyUSB device contains a certain string. The assign key (SYMLINK) triggers the addition of a symbolic link to this device under /dev/pilot. The operator used in this key (+=) tells udev to additionally perform this action, even if previous or later rules add other symbolic links. As this rule contains two match keys, it is only applied if both conditions are met.
  • The printer rule
  • deals with USB printers and contains two match keys which must both apply to get the entire rule applied (SUBSYSTEM and KERNEL). Three assign keys deal with the naming for this device type (NAME), the creation of symbolic device links (SYMLINK) and the group membership for this device type (GROUP). Using the * wild card in the KERNEL key makes it match several lp printer devices. Substitutions are used in both, the NAME and the SYMLINK keys to extend these strings by the internal device name. For example, the symbolic link to the first lp USB printer would read /dev/usblp0.
  • The kernel firmware loader rule
  • This rule makes udev load additional firmware by an external helper script during runtime.
    1. The SUBSYSTEM match key searches for the firmware subsystem.
    2. The ACTION key checks whether any device belonging to the firmware subsystem has been added.
    3. The RUN+= key triggers the execution of the firmware.sh script to locate the firmware that is to be loaded.

Hotplugging with udev

udev takes advantage of sysfs, it is entirely in user space.
It automatically creates/removes device entriesin /dev/ according to inserted/removed devices.
At the very beginning of user-space startup, udev mounts the /dev/ directory as a tmpfs filesystem:

sudo mount -t tmpfs udev /dev
/dev/ is populated with static devices available in /lib/udev/devices/.

The udevd daemon is started.
It listens to uevents from the driver core, which are sent whenever devices are inserted or removed.

The udevd daemon reads and parses all the rules found in /etc/udev/rules.d/ and keeps them in memory.

Whenever rules are added, removed or modified,udevd receives an inotify event and updates itsruleset in memory.

Example, inserting a USB mouse


recv(4, // socket id"
  add@/class/input/input9/mouse2\0 // message
  ACTION=add\0         // action type
  DEVPATH=/class/input/input9/mouse2\0 // path in /sys
  SUBSYSTEM=input\0 // subsystem (class)
  SEQNUM=1064\0     // sequence number
  PHYSDEVPATH=/devices/pci0000:00/0000:00:1d.1/usb2/2-2/2-2:1.0\0// device path in /sys
  PHYSDEVBUS=usb\0  // bus
  PHYSDEVDRIVER=usbhid\0 // driver
  MAJOR=13\0             // major number
  MINOR=34\0",           // minor number2048,                  // message buffer size
  0)                     // flags
= 221                     // actual message size
When an event is received, udevd starts a process to:
  • try to match the event against udev rules
  • create / remove device files
  • run programs (to load / remove a driver, to notify user space...)

Writing udev rules

When a udev rule matching event information is found,it can be used:
  • To define the name and path of a device file.
  • udev naming rule examples:
    
    # Naming testing the output of a program
    BUS=="scsi", PROGRAM="/sbin/scsi_id", RESULT=="OEM 0815", NAME="disk1"
    # USB printer to be called lp_color
    BUS=="usb", SYSFS{serial}=="W09090207101241330", NAME="lp_color"
    # SCSI disk with a specific vendor and model number will be called boot
    BUS=="scsi", SYSFS{vendor}=="IBM", SYSFS{model}=="ST336", NAME="boot%n"
    # sound card with PCI bus id 00:0b.0 to be called dsp
    BUS=="pci", ID=="00:0b.0", NAME="dsp"
    # USB mouse at third port of the second hub to be called mouse1
    BUS=="usb", PLACE=="2.3", NAME="mouse1"
    # ttyUSB1 should always be called pda with two additional symlinks
    KERNEL=="ttyUSB1", NAME="pda", SYMLINK="palmtop handheld"
    # multiple USB webcams with symlinks to be called webcam0, webcam1, ...
    BUS=="usb", SYSFS{model}=="XV3", NAME="video%n", SYMLINK="webcam%n"  
      	
    lshw is a small tool to extract detailed information on the hardware configuration of the machine.
    
      *-network
           description: Ethernet interface
           product: Intel Corporation
           vendor: Intel Corporation
           physical id: 0
           bus info: pci@0000:04:00.0
           logical name: eno1
           version: 03
           serial: 38:68:dd:61:73:4c
           capacity: 1Gbit/s
           width: 32 bits
           clock: 33MHz
           capabilities: bus_master cap_list ethernet physical 10bt 10bt-fd 100bt 100bt-fd 1000bt-fd autonegotiation
           configuration: autonegotiation=on broadcast=yes driver=igc driverversion=5.13.0-1009-intel firmware=1057:8754 latency=0 link=no multicast=yes port=twisted pair
           resources: irq:18 memory:50a00000-50afffff memory:50b00000-50b03fff    
        
  • To define the owner, group and permissions of a device file.
  • To execute a specified program.

Identifying device driver modules

Kernel / module compiling:
  1. Each driver announces which device and vendorids it supports. Information stored in module files *.ko.
  2. The depmod -a command processes module files and generates /lib/modules/$(uname -r)/modules.alias
Run time :

The Debian Administrator's Handbook: Hot Plugging

1. Introduction

The hotplug kernel subsystem dynamically handles the addition and removal of devices, by loading the appropriate drivers and by creating the corresponding device files (with the help of udevd).
The kernel has a database that associates each device ID with the required driver.
This database is used to load all the drivers when :
  • booting
  • the peripheral devices detected on the different buses
  • hotplugging
  • an additional hotplug device is connected.
Once the device is ready for use, a message is sent to udevd so it will be able to create the corresponding entry in /dev/.

2. The Naming Problem

The device name is not fixed; it depends on the order in which devices are connected.
Besides, more and more drivers use dynamic values for devices' major/minor numbers, which makes it impossible to have static device numbers for the given devices.
udev was created precisely to solve this problem.

systemd.net-naming-scheme — Network device naming schemes

Ppossible names may be generated by the systemd-udevd.service builtin net_id and exported as udev properties (ID_NET_NAME_ONBOARD=, ID_NET_LABEL_ONBOARD=, ID_NET_NAME_PATH=, ID_NET_NAME_SLOT=).

The naming scheme may also be overridden using the net.naming-scheme= kernel command line switch.
After the udev properties have been generated, appropriate udev rules may be used to actually rename devices based on those properties.

All names start with a two-character prefix that signifies the interface type.

  • en
  • Ethernet
  • ib
  • InfiniBand
  • sl
  • Serial line IP (slip)
  • wl
  • Wireless local area network (WLAN)
  • ww
  • Wireless wide area network (WWAN)
The udev net_id builtin exports the following udev device properties:
  • ID_NET_NAME_ONBOARD=prefixonumber
  • This name is set based on the numeric ordering information given by the firmware for on-board devices.
    The name consists of the prefix, letter o, and a number specified by the firmware. This is only available for PCI devices.
  • ID_NET_LABEL_ONBOARD=prefix label
  • This property is set based on textual label given by the firmware for on-board devices.
    The name consists of the prefix concatenated with the label. This is only available for PCI devices.
  • ID_NET_NAME_MAC=prefixxAABBCCDDEEFF
  • This name consists of the prefix, letter x, and 12 hexadecimal digits of the MAC address.
    It is available if the device has a fixed MAC address.
    Because this name is based on an attribute of the card itself, it remains "stable" when the device is moved (even between machines), but will change when the hardware is replaced.
  • ID_NET_NAME_SLOT=prefix[Pdomain]sslot[ffunction][nport_name|ddev_port], ID_NET_NAME_SLOT=prefixvslot, ID_NET_NAME_SLOT=prefixxslot, ID_NET_NAME_SLOT=prefix[Pdomain]sslot[ffunction][nport_name|ddev_port]bnumber, ID_NET_NAME_SLOT=prefix[Pdomain]sslot[ffunction][nport_name|ddev_port]uport…[cconfig][iinterface], ID_NET_NAME_SLOT=prefix[Pdomain]sslot[ffunction][nport_name|ddev_port]vslot
  • This property describes the slot position. Different schemes are used depending on the bus type.

3. How udev Works

When udev is notified by the kernel of the appearance of a new device, it collects various information(SN, MAC, vendor ID, device ID, ...) 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.).

With all of this collected 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 :=).

Selection

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{attribute}
  • 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).

Assignment

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 as =, but prevents later changes to the same variable.
  • +=
  • 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:
  • $id, %b
  • The name of the device matched while searching the devpath upwards for SUBSYSTEMS, KERNELS, DRIVERS, and ATTRS.
  • $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;
  • use man dev to get the full list of available substitutions

A concrete example

Rename a USB stick

Let us consider the case of a simple USB stick and try to assign it a fixed name.

  1. find the elements that will identify it in a unique manner
  2. Plug it in and use "lsblk" to find the device name.
    
    $ lsusb
    Bus 003 Device 003: ID 8564:1000 Transcend Information, Inc. JetFlash
      	
    Run
    
    $ udevadm info -a -n /dev/sdb
    ...
      looking at device '/devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2.4/1-2.4:1.0/host3/target3:0:0/3:0:0:0/block/sdb':
        KERNEL=="sdb"
        SUBSYSTEM=="block"
    ...
      looking at parent device '/devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2.4':
        KERNELS=="1-2.4"
        SUBSYSTEMS=="usb"
        DRIVERS=="usb"
        ATTRS{bmAttributes}=="80"
        ATTRS{speed}=="480"
        ATTRS{rx_lanes}=="1"
        ATTRS{bDeviceSubClass}=="00"
        ATTRS{version}==" 2.00"
        ATTRS{bDeviceClass}=="00"
        ATTRS{avoid_reset_quirk}=="0"
        ATTRS{bMaxPacketSize0}=="64"
        ATTRS{tx_lanes}=="1"
        ATTRS{idProduct}=="1000"
        ATTRS{bMaxPower}=="100mA"
        ATTRS{product}=="Mass Storage Device"
        ATTRS{idVendor}=="8564"
        ATTRS{bNumConfigurations}=="1"
        ATTRS{devpath}=="2.4"
        ATTRS{maxchild}=="0"
        ATTRS{devnum}=="7"
        ATTRS{bDeviceProtocol}=="00"
        ATTRS{bNumInterfaces}==" 1"
        ATTRS{authorized}=="1"
        ATTRS{bcdDevice}=="0100"
        ATTRS{quirks}=="0x0"
        ATTRS{configuration}==""
        ATTRS{busnum}=="1"
        ATTRS{urbnum}=="1112"
        ATTRS{serial}=="RAVEHPWS"
        ATTRS{manufacturer}=="JetFlash"
        ATTRS{bConfigurationValue}=="1"
        ATTRS{removable}=="unknown"
        ATTRS{ltm_capable}=="no"
    ...
          
  3. create a new rule, you can use tests on the device's variables, as well as those of one of the parent devices.
  4. For example, use 2 variables to create /etc/udev/rules.d/010_local.rules,
    
    KERNEL=="sd?", SUBSYSTEM=="block", ATTRS{idVendor}=="8564", ATTRS{idProduct}=="1000", SYMLINK+="usb_jetflash/disk"
    KERNEL=="sd?[0-9]", SUBSYSTEM=="block", ATTRS{idVendor}=="8564", ATTRS{idProduct}=="1000", SYMLINK+="usb_jetflash/part%n"  
      
  5. simply remove and reconnect the USB key.
  6. 
    $ tree /dev/usb_jetflash
    /dev/usb_jetflash
    |-- disk -> ../sda
    |-- part1 -> ../sda1
    |-- part2 -> ../sda2
    |-- part3 -> ../sda3
    `-- part4 -> ../sda4
        
        

Debugging udev's configuration

To enable debug verbose,

$ sudo udevadm control --log-priority=info
To restart an dtrigger events:

$ sudo udevadm control --reload-rules
$ sudo udevadm trigger

Fix a USB modem name


$ lsusb
Bus 004 Device 002: ID 2cb7:0104 Fibocom Fibocom Modem_SN:9C08AC4C

$ udevadm info -a -n /dev/cdc-wdm0

  looking at device '/devices/pci0000:00/0000:00:14.0/usb4/4-4/4-4:1.4/usbmisc/cdc-wdm0':
    KERNEL=="cdc-wdm0"
    SUBSYSTEM=="usbmisc"
    DRIVER==""

  looking at parent device '/devices/pci0000:00/0000:00:14.0/usb4/4-4/4-4:1.4':
    KERNELS=="4-4:1.4"
    SUBSYSTEMS=="usb"
    DRIVERS=="qmi_wwan"
    ATTRS{supports_autosuspend}=="1"
    ATTRS{bNumEndpoints}=="03"
    ATTRS{authorized}=="1"
    ATTRS{bInterfaceProtocol}=="ff"
    ATTRS{bInterfaceNumber}=="04"
    ATTRS{bInterfaceSubClass}=="ff"
    ATTRS{bInterfaceClass}=="ff"
    ATTRS{bAlternateSetting}==" 0"

  looking at parent device '/devices/pci0000:00/0000:00:14.0/usb4/4-4':
    KERNELS=="4-4"
    SUBSYSTEMS=="usb"
    DRIVERS=="usb"
    ATTRS{bNumConfigurations}=="1"
    ATTRS{bNumInterfaces}==" 5"
    ATTRS{bConfigurationValue}=="1"
    ATTRS{quirks}=="0x0"
    ATTRS{idVendor}=="2cb7"
    ATTRS{bDeviceSubClass}=="00"
    ATTRS{removable}=="fixed"
    ATTRS{busnum}=="4"
    ATTRS{bmAttributes}=="a0"
    ATTRS{serial}=="9c08ac4c"
    ATTRS{idProduct}=="0104"
    ATTRS{urbnum}=="750"
    ATTRS{bMaxPacketSize0}=="9"
    ATTRS{tx_lanes}=="1"
    ATTRS{ltm_capable}=="no"
    ATTRS{bDeviceProtocol}=="00"
    ATTRS{devnum}=="2"
    ATTRS{manufacturer}=="Fibocom"
    ATTRS{speed}=="5000"
    ATTRS{devpath}=="4"
    ATTRS{bMaxPower}=="896mA"
    ATTRS{rx_lanes}=="1"
    ATTRS{bDeviceClass}=="00"
    ATTRS{bcdDevice}=="0414"
    ATTRS{product}=="Fibocom Modem_SN:9C08AC4C"
    ATTRS{version}==" 3.20"
    ATTRS{configuration}=="DIAG_DUN_SER_NMEA_RMNET"
    ATTRS{avoid_reset_quirk}=="0"
    ATTRS{authorized}=="1"
    ATTRS{maxchild}=="0"

  looking at parent device '/devices/pci0000:00/0000:00:14.0/usb4':
    KERNELS=="usb4"
    SUBSYSTEMS=="usb"
    DRIVERS=="usb"
    ATTRS{configuration}==""
    ATTRS{speed}=="10000"
    ATTRS{bMaxPacketSize0}=="9"
    ATTRS{bNumConfigurations}=="1"
    ATTRS{bConfigurationValue}=="1"
    ATTRS{avoid_reset_quirk}=="0"
    ATTRS{bcdDevice}=="0513"
    ATTRS{authorized_default}=="1"
    ATTRS{idProduct}=="0003"
    ATTRS{devpath}=="0"
    ATTRS{ltm_capable}=="yes"
    ATTRS{bmAttributes}=="e0"
    ATTRS{tx_lanes}=="1"
    ATTRS{quirks}=="0x0"
    ATTRS{devnum}=="1"
    ATTRS{idVendor}=="1d6b"
    ATTRS{bDeviceProtocol}=="03"
    ATTRS{bMaxPower}=="0mA"
    ATTRS{bDeviceClass}=="09"
    ATTRS{authorized}=="1"
    ATTRS{rx_lanes}=="1"
    ATTRS{serial}=="0000:00:14.0"
    ATTRS{interface_authorized_default}=="1"
    ATTRS{maxchild}=="4"
    ATTRS{bDeviceSubClass}=="00"
    ATTRS{manufacturer}=="Linux 5.13.0-1009-intel xhci-hcd"
    ATTRS{product}=="xHCI Host Controller"
    ATTRS{removable}=="unknown"
    ATTRS{version}==" 3.10"
    ATTRS{bNumInterfaces}==" 1"
    ATTRS{busnum}=="4"
    ATTRS{urbnum}=="42"

  looking at parent device '/devices/pci0000:00/0000:00:14.0':
    KERNELS=="0000:00:14.0"
    SUBSYSTEMS=="pci"
    DRIVERS=="xhci_hcd"
    ATTRS{class}=="0x0c0330"
    ATTRS{irq}=="143"
    ATTRS{dbc}=="disabled"
    ATTRS{msi_bus}=="1"
    ATTRS{device}=="0xa0ed"
    ATTRS{local_cpus}=="ff"
    ATTRS{broken_parity_status}=="0"
    ATTRS{numa_node}=="-1"
    ATTRS{dma_mask_bits}=="64"
    ATTRS{driver_override}=="(null)"
    ATTRS{enable}=="1"
    ATTRS{subsystem_device}=="0x7270"
    ATTRS{power_state}=="D0"
    ATTRS{d3cold_allowed}=="1"
    ATTRS{consistent_dma_mask_bits}=="64"
    ATTRS{ari_enabled}=="0"
    ATTRS{subsystem_vendor}=="0x8086"
    ATTRS{revision}=="0x20"
    ATTRS{local_cpulist}=="0-7"
    ATTRS{vendor}=="0x8086"

  looking at parent device '/devices/pci0000:00':
    KERNELS=="pci0000:00"
    SUBSYSTEMS==""
    DRIVERS==""
    ATTRS{waiting_for_supplier}=="0"
Test if the keys can be matched:

ACTION=="add|change", SUBSYSTEM=="usbmisc", KERNEL=="cdc-wdm*", RUN+="/bin/sh -c 'echo jerry >> /tmp/udevtest.txt'"
Create the symlink for all cdc-wdm:

ACTION=="add|change", SUBSYSTEM=="usbmisc", KERNEL=="cdc-wdm*",  SYMLINK+="cdc-wdm0"
Trigger the rules:

$ sudo udevadm control --reload-rules
$ sudo udevadm trigger
Check the log:

$ sudo journalctl | grep cdc-wdm
...
May 02 12:47:32 ubuntu systemd-udevd[2710]: cdc-wdm0: /etc/udev/rules.d/69-e200.rules:1 LINK 'cdc-wdm0'
May 02 12:47:32 ubuntu systemd-udevd[2710]: cdc-wdm0: /etc/udev/rules.d/70-snap.modem-manager.rules:1013 RUN '/usr/lib/snapd/snap-device-helper $env{ACTION} snap_modem-manager_mbim-network $devpath $major:$minor'
May 02 12:47:32 ubuntu systemd-udevd[2710]: cdc-wdm0: /etc/udev/rules.d/70-snap.modem-manager.rules:1016 RUN '/usr/lib/snapd/snap-device-helper $env{ACTION} snap_modem-manager_mbimcli $devpath $major:$minor'
May 02 12:47:32 ubuntu systemd-udevd[2710]: cdc-wdm0: /etc/udev/rules.d/70-snap.modem-manager.rules:1019 RUN '/usr/lib/snapd/snap-device-helper $env{ACTION} snap_modem-manager_modemmanager $devpath $major:$minor'
May 02 12:47:32 ubuntu systemd-udevd[2710]: cdc-wdm0: /etc/udev/rules.d/70-snap.modem-manager.rules:1022 RUN '/usr/lib/snapd/snap-device-helper $env{ACTION} snap_modem-manager_qmi-network $devpath $major:$minor'
May 02 12:47:32 ubuntu systemd-udevd[2710]: cdc-wdm0: /etc/udev/rules.d/70-snap.modem-manager.rules:1025 RUN '/usr/lib/snapd/snap-device-helper $env{ACTION} snap_modem-manager_qmicli $devpath $major:$minor'
May 02 12:47:32 ubuntu systemd-udevd[2710]: cdc-wdm0: Handling device node '/dev/cdc-wdm0', devnum=c180:0
May 02 12:47:32 ubuntu systemd-udevd[2710]: cdc-wdm0: Preserve already existing symlink '/dev/char/180:0' to '../cdc-wdm0'
May 02 12:47:32 ubuntu systemd-udevd[2710]: cdc-wdm0: Conflicting device node '/dev/cdc-wdm0' found, link to '/dev/cdc-wdm0' will not be created.
...

Tips

How to add the ID of a known-compatible device to a driver (or: making “echo new_id” permanent)?

If a networking device works with the driver already in the kernel, and you know the vendor and device ID.
A quick look through the driver source, confirms that my device is not in the device table.
For ex., a uncommon USB WiFi device works with the rt2800usb:

sudo modprobe rt2800usb
sudo bash -c "echo 0e66 0004 > /sys/bus/usb/drivers/rt2800/new_id"
You can add a udev rule, /etc/udev/rules.d/network_drivers.rules:

ACTION=="add", SUBSYSTEM=="usb", ATTR{idVendor}=="0e66", ATTR{idProduct}=="0004", RUN+="/sbin/modprobe -qba rt2800usb"

How to rename Ethernet devices through udev

Situation

  • ethX was replaced with a new NIC. The new NIC is showing as ethY, but it needs to have the same name as the replaced ethX.
  • A virtual machine was cloned and eth0 does not show, but an eth1 does.
  • Business rules dictate that interface X needs to be known by a certain name.
  • A wireless NIC is showing as wlanX but needs to appear as ethX.

Resolution

Renaming Ethernet devices is done through udev.
  1. Identify the hardware MAC address of the Ethernet device
  2. Open /etc/udev/rules.d/30-net_persistent_names.rules for editing.
  3. In this example, the interface eth0 will be renamed to eth5:
    
    SUBSYSTEM=="net", ACTION=="add", SYSFS{address}=="00:1a:4b:b7:31:13", IMPORT="/lib/udev/rename_netiface %k eth5"
    	
  4. Save the file.
  5. Reboot to test changes.

留言

熱門文章