Citation: http://possiblelossofprecision.net/?p=343

INTRO

 

UDEV is a service/package that detects new devices connected to the linux server and launches predefined scripts and also makes them accessible through /dev. In the old days people had to use “mknod DEVICENAME [b,c,p] MAJOR# MINOR#(ex: “mknod /dev/random b 12 5” or “mknod /dev/ttyS0 c 4 64” or “mknod /dev/nicedisk b 45 0” or “mknod /dev/sda b 8 0” or “mknod /dev/sda1 b 8 1” or “mknod /dev/sda2 b 8 2” or “mknod /dev/sdb b 8 16” or “mknod /dev/sdb1 b 8 17” – notice the device name can be anything, such /dev/nicedisk. notice the major number stays the same for similar devices as they use the same drives. Also notice that there are b[block], c[character], or p[fifo] type of devices that can be made) to associate a new device to its major number (which represented a unique identifier that tied a certain device to a driver) and minor number (which sub-categorized the device within the driver). For example: The major number 8 is used for “sd” devices and the minor numbers represent the drive letter and partition – you can see that if you run “cat /proc/partitions“. There was a newer system then mknod which udev built from called mdev. mdev was a device discovery tool which had to be manually ran like this “mdev -s“. Well udev is like “mdev -s” except it runs when it detects a new device. udevadm can be used to gather lots of information about a device. udev can also use all of this information when it detects a device to do certain things, such as make a symlink of sda that looks like this “/dev/0:7200 –> /dev/sda” meaning “port 1. rpm 7200” and sdb can be “/dev/5:5600 –> /dev/sdb” meaning meaning “port 6 rpm 5600″ – this is just an example of how useful udev can be, for example a linux admin can reference up a drive based on slot number or rpm.

SIDENOTE: With mknod, how did one know what major and minor number to associate to a device? Well one can use previous similar devices to figure out the pattern such as based on the above info I can predict that since sda is b,8,0 and sda1 is b,8,1 then I can assume sda3 is b,8,3 (TIP: for making the pattern connection use “cat /proc/partitions” for storage block devices). Now if you dont have anything to make a pattern out of, then just use the LANANA LINUX DEVICE LIST (administred by Alan Cox – one of the main linux beards). You can find the latest copy online (http://www.lanana.org/docs/device-list/devices-2.6+.txt) or in the linux kernle source code under the folder Documentation/devices.txt

SIDENOTE: History timeline, first it was mknod, then mdev, then udev

UDEVADM SECTION 1: EXAMPLES

 

GET DEVICE SYS PATH

To gather info about a device using udevadm you need to know the path of the device. To get the path of the device find it in /sys/devices or use “udevadm info -q path -n DEV_PATH

Example1: sda & sdb

udevadm info -q path -n /dev/sda
# or you can omit the /dev prefix
udevadm info -q path -n sda
/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sda
udevadm info -q path -n /dev/sdb
# or you can omit the /dev prefix
udevadm info -q path -n sdb
/devices/pci0000:00/0000:00:1f.2/ata2/host1/target1:0:0/1:0:0:0/block/sdb

Imagine a prefix of /sys for each of those devices paths.
sda is really /sys/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sda
sdb is really /sys/devices/pci0000:00/0000:00:1f.2/ata2/host1/target1:0:0/1:0:0:0/block/sdb

GET DEVICE INFO

Now to get info about the device (udev info) do this: “udevadm info -a -p DEV_SYS_PATH

Example2: for sda

udevadm info -a -p /sys/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sda
# or write it without the /sys prefix (same result)
udevadm info -a -p /devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sda

See output below. sdb will have similar output

COMBINE THE TWO

You can can combine both results like this: “udevadm info -a -p `udevadm info -q path -n DEV_PATH`” or instead of back ticks command substitution use dollar-sign-parenthesis substitution, “udevadm info -a -p $(udevadm info -q path -n DEV_PATH)”

Example3: sda

udevadm info -a -p $(udevadm info -q path -n sda)
# or to get same answer:
udevadm info -a -p $(udevadm info -q path -n /dev/sda)
# or to get same answer:
udevadm info -a -p `udevadm info -q path -n sda`
# or to get same answer:
udevadm info -a -p `udevadm info -q path -n /dev/sda`

All of the above will provide the same output seen at the bottom

TO GET ALL OUTPUT

Now what if we wanted to poll every device in /dev, then use this script:

for i in /dev/*; do echo "######## ${i} #########"; echo; udevadm info -a -p `udevadm info -q path -n "${i}"` | grep .; echo; echo; done;

SIDENOTE: the i will equal /dev/sda and /dev/sdb and so on.
SIDENOTE: the | grep . removes those extra new lines you see at the bottom. Then the echo; echo; just adds a good seperator between the devices for easier viewing
SIDENOTE: I didnt include my own system output so that you didnt get too much info. Just run it on your own system to check it out.

SDA DEVICE INFO OUTPUT

# udevadm info -a -p $(udevadm info -q path -n sda)

Udevadm info starts with the device specified by the devpath and then
walks up the chain of parent devices. It prints for every device
found, all possible attributes in the udev rules key format.
A rule to match, can be composed by the attributes of the device
and the attributes from one single parent device.

looking at device '/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sda':
KERNEL=="sda"
SUBSYSTEM=="block"
DRIVER==""
ATTR{ro}=="0"
ATTR{size}=="1953525168"
ATTR{stat}==" 1799945 769925 214302484 36182890 542669 6772016 119502568 39048627 0 10411312 75937287"
ATTR{range}=="16"
ATTR{discard_alignment}=="0"
ATTR{events}==""
ATTR{ext_range}=="256"
ATTR{events_poll_msecs}=="-1"
ATTR{alignment_offset}=="0"
ATTR{inflight}==" 0 0"
ATTR{removable}=="0"
ATTR{capability}=="50"
ATTR{events_async}==""

looking at parent device '/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0':
KERNELS=="0:0:0:0"
SUBSYSTEMS=="scsi"
DRIVERS=="sd"
ATTRS{rev}=="SN06"
ATTRS{type}=="0"
ATTRS{scsi_level}=="6"
ATTRS{model}=="ST31000340NS "
ATTRS{state}=="running"
ATTRS{queue_type}=="simple"
ATTRS{iodone_cnt}=="0x25ea38"
ATTRS{iorequest_cnt}=="0x265658"
ATTRS{queue_ramp_up_period}=="120000"
ATTRS{evt_capacity_change_reported}=="0"
ATTRS{timeout}=="30"
ATTRS{evt_media_change}=="0"
ATTRS{ioerr_cnt}=="0x11b0"
ATTRS{queue_depth}=="31"
ATTRS{vendor}=="ATA "
ATTRS{evt_soft_threshold_reached}=="0"
ATTRS{device_blocked}=="0"
ATTRS{evt_mode_parameter_change_reported}=="0"
ATTRS{evt_lun_change_reported}=="0"
ATTRS{evt_inquiry_change_reported}=="0"
ATTRS{iocounterbits}=="32"
ATTRS{vpd_pg80}==""
ATTRS{vpd_pg83}==""
ATTRS{eh_timeout}=="10"

looking at parent device '/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0':
KERNELS=="target0:0:0"
SUBSYSTEMS=="scsi"
DRIVERS==""

looking at parent device '/devices/pci0000:00/0000:00:1f.2/ata1/host0':
KERNELS=="host0"
SUBSYSTEMS=="scsi"
DRIVERS==""

looking at parent device '/devices/pci0000:00/0000:00:1f.2/ata1':
KERNELS=="ata1"
SUBSYSTEMS==""
DRIVERS==""

looking at parent device '/devices/pci0000:00/0000:00:1f.2':
KERNELS=="0000:00:1f.2"
SUBSYSTEMS=="pci"
DRIVERS=="ahci"
ATTRS{irq}=="41"
ATTRS{subsystem_vendor}=="0x8086"
ATTRS{broken_parity_status}=="0"
ATTRS{class}=="0x010601"
ATTRS{consistent_dma_mask_bits}=="64"
ATTRS{dma_mask_bits}=="64"
ATTRS{local_cpus}=="ff"
ATTRS{device}=="0x2821"
ATTRS{enable}=="1"
ATTRS{msi_bus}==""
ATTRS{local_cpulist}=="0-7"
ATTRS{vendor}=="0x8086"
ATTRS{subsystem_device}=="0x2821"
ATTRS{d3cold_allowed}=="1"

looking at parent device '/devices/pci0000:00':
KERNELS=="pci0000:00"
SUBSYSTEMS==""
DRIVERS==""

 

UDEVADM SECTION 2: Deeper Dive

 

Lets dive deeper into udevadm info arguments, and maybe we can unlock more things.

Checkout the –help of “udevadm info”, to make sense of the options

# udevadm info --help
Usage: udevadm info OPTIONS
--query=<type> query device information:
* name name of device node
* symlink pointing to node
* path sys device path
* property the device properties
* all all values
--path=<syspath> sys device path used for query or attribute walk
--name=<name> node or symlink name used for query or attribute walk
--root prepend dev directory to path names
--attribute-walk print all key matches while walking along the chain
of parent devices
--device-id-of-file=<file> print major:minor of device containing this file
--export export key/value pairs
--export-prefix export the key name with a prefix
--export-db export the content of the udev database
--cleanup-db cleanup the udev database
--help

Each of those can also be used by single letters:
-q for query, -n for name, -a for attribute walk (get all attributes), -p for path

So check this out, we can actually get the device path by doing this (as seen above):

udevadm info -q path -n sda

Meaning: udevadm-info query(get) the path of device named sda. In other words: Udevadm-info please show us the path of the device sda.

Or you can get all of the query-able information like this:

udevadm info -q all -n sda

You can also get all of the query-able information by path:

udevadm info -q all -p /devices/pci0000:00/0000:00:01.1/0000:02:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/block/sda

Or you can get every attribute by path or name* (see CORRECTION TO SECTION 1 below)

Get all Attributes by device sys path:

udevadm info -a -p /devices/pci0000:00/0000:00:01.1/0000:02:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/block/sda

Get all Attributes by device name:

udevadm info -a -n sda

* CORRECTION TO SECTION 1: I said you cant get all attribute by using the name (such as sda) and that you needed to know the path (such as /devices/pci0000:00/0000:00:01.1/0000:02:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/block/sda). But that is in correct. PS: I only said that we are limited to get all attributes if we quote the full devices’s sys path because the citation I quoted said that, but I tested it myself and the message of the author was incorrect.

So that means instead of using this to get all the attributes of sda:

udevadm info -a -p $(udevadm info -q path -n sda)

We can use this simpler one:

udevadm info -a -n sda

And to get the attributes of every device:

for i in /dev/*; do echo "######## ${i} #########"; echo; udevadm info -a -n ${i} | grep .; echo; echo; done;

NOTE: getting all of the attributes, udevadm does an “attribute walk” through all of the sys files

UDEVADM TEST

You can use udevadm test “sys path of device” to get even more information. The above just shows all of the attributes with udevadm info -a (–attribute). Or it shows all of the queriable information with udevadm info -q (–query). However there is also env variables for each device. Which can be used when device rules are made. For example the ID_CHANNEL env variable of a device tells you the slot number.

TIP: /sys/block/ has symlinks to all of the /sys/devices paths are useful

Like this:

# ls -l /sys/block
total 0
lrwxrwxrwx 1 root root 0 Mar 18 21:19 loop0 -> ../devices/virtual/block/loop0
lrwxrwxrwx 1 root root 0 Mar 18 21:19 loop1 -> ../devices/virtual/block/loop1
lrwxrwxrwx 1 root root 0 Mar 18 21:19 loop2 -> ../devices/virtual/block/loop2
lrwxrwxrwx 1 root root 0 Mar 18 21:19 loop3 -> ../devices/virtual/block/loop3
lrwxrwxrwx 1 root root 0 Mar 18 21:19 loop4 -> ../devices/virtual/block/loop4
lrwxrwxrwx 1 root root 0 Mar 18 21:19 loop5 -> ../devices/virtual/block/loop5
lrwxrwxrwx 1 root root 0 Mar 18 21:19 loop6 -> ../devices/virtual/block/loop6
lrwxrwxrwx 1 root root 0 Mar 18 21:19 loop7 -> ../devices/virtual/block/loop7
lrwxrwxrwx 1 root root 0 Mar 18 21:13 md0 -> ../devices/virtual/block/md0
lrwxrwxrwx 1 root root 0 Mar 18 21:13 md1 -> ../devices/virtual/block/md1
lrwxrwxrwx 1 root root 0 Mar 18 21:13 md127 -> ../devices/virtual/block/md127
lrwxrwxrwx 1 root root 0 Mar 16 14:44 sda -> ../devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sda
lrwxrwxrwx 1 root root 0 Mar 16 14:44 sdb -> ../devices/pci0000:00/0000:00:1f.2/ata2/host1/target1:0:0/1:0:0:0/block/sdb
lrwxrwxrwx 1 root root 0 Mar 16 14:44 sdc -> ../devices/pci0000:00/0000:00:1f.2/ata3/host2/target2:0:0/2:0:0:0/block/sdc
lrwxrwxrwx 1 root root 0 Mar 16 14:44 sdd -> ../devices/pci0000:00/0000:00:1f.2/ata4/host3/target3:0:0/3:0:0:0/block/sdd
lrwxrwxrwx 1 root root 0 Mar 16 14:42 sde -> ../devices/pci0000:00/0000:00:1d.7/usb1/1-1/1-1:1.0/host6/target6:0:0/6:0:0:0/block/sde

Check out the output of these 3 identical commands:

udevadm test /sys/block/sda
udevadm test /sys/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sda
udevadm test /devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sda

Output:

# This just the bottom part of the output which shows the ENV variables:

DEVLINKS=/dev/disk/by-id/ata-ST3000DM001-1CH166_W1F28168 /dev/disk/by-id/scsi-SATA_ST3000DM001-1CH_W1F28168 /dev/disk/by-id/wwn-0x5000c5005e43ed87 /dev/disk/by-path/pci-0000:00:1f.2-scsi-0:0:0:0 /dev/disk/internal/0:3:ST3000DM001-1CH166:W1F28168:CC24:7200
DEVNAME=/dev/sdd
DEVPATH=/devices/pci0000:00/0000:00:1f.2/ata4/host3/target3:0:0/3:0:0:0/block/sdd
DEVTYPE=disk
ID_ATA=1
ID_ATA_DOWNLOAD_MICROCODE=1
ID_ATA_FEATURE_SET_APM=1
ID_ATA_FEATURE_SET_APM_CURRENT_VALUE=254
ID_ATA_FEATURE_SET_APM_ENABLED=1
ID_ATA_FEATURE_SET_HPA=1
ID_ATA_FEATURE_SET_HPA_ENABLED=1
ID_ATA_FEATURE_SET_PM=1
ID_ATA_FEATURE_SET_PM_ENABLED=1
ID_ATA_FEATURE_SET_SECURITY=1
ID_ATA_FEATURE_SET_SECURITY_ENABLED=0
ID_ATA_FEATURE_SET_SECURITY_ENHANCED_ERASE_UNIT_MIN=334
ID_ATA_FEATURE_SET_SECURITY_ERASE_UNIT_MIN=334
ID_ATA_FEATURE_SET_SECURITY_FROZEN=1
ID_ATA_FEATURE_SET_SMART=1
ID_ATA_FEATURE_SET_SMART_ENABLED=1
ID_ATA_ROTATION_RATE_RPM=7200
ID_ATA_SATA=1
ID_ATA_SATA_SIGNAL_RATE_GEN1=1
ID_ATA_SATA_SIGNAL_RATE_GEN2=1
ID_ATA_WRITE_CACHE=1
ID_ATA_WRITE_CACHE_ENABLED=1
ID_BUS=ata
ID_CHANNEL=0:3
ID_MODEL=ST3000DM001-1CH166
ID_MODEL_ENC=ST3000DM001-1CH166\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20
ID_PART_TABLE_TYPE=gpt
ID_PATH=pci-0000:00:1f.2-scsi-0:0:0:0
ID_PATH_TAG=pci-0000_00_1f_2-scsi-0_0_0_0
ID_REVISION=CC24
ID_SCSI_COMPAT=SATA_ST3000DM001-1CH_W1F28168
ID_SERIAL=ST3000DM001-1CH166_W1F28168
ID_SERIAL_SHORT=W1F28168
ID_TYPE=disk
ID_WWN=0x5000c5005e43ed87
ID_WWN_WITH_EXTENSION=0x5000c5005e43ed87
MAJOR=8
MINOR=48
SUBSYSTEM=block
TAGS=:systemd:
UDEV_LOG=6
USEC_INITIALIZED=196460535692

NOTE: all of these variables seen in “udevadm test” and “udevadm info” can be used while making rules. Checkout these rules.

Here is an example of a rule that looks at the drives and makes a symlink for it in /dev/disk/internal which has the channel (so it has the slot number), model number, serial number, firmware version of the drive, and rpm of the drive

### 1 rule per line ###

# cat /etc/udev/rules.d/99-disk-chan.rules
ENV{DEVTYPE}=="disk", ENV{ID_CHANNEL}=="?*", SYMLINK+="disk/internal/$env{ID_CHANNEL}:$env{ID_MODEL}:$env{ID_SERIAL_SHORT}:$env{ID_REVISION}:$env{ID_ATA_ROTATION_RATE_RPM}"

### note that rules should be 1 per line, however you can extend them like this, with a hanging line ###

# cat /etc/udev/rules.d/99-disk-chan.rules
ENV{DEVTYPE}=="disk", ENV{ID_CHANNEL}=="?*", \
        SYMLINK+="disk/internal/$env{ID_CHANNEL}:$env{ID_MODEL}:$env{ID_SERIAL_SHORT}:$env{ID_REVISION}:$env{ID_ATA_ROTATION_RATE_RPM}"

### same rule, just no spaces on the hanging line. ###

# cat /etc/udev/rules.d/99-disk-chan.rules
ENV{DEVTYPE}=="disk", ENV{ID_CHANNEL}=="?*", \
SYMLINK+="disk/internal/$env{ID_CHANNEL}:$env{ID_MODEL}:$env{ID_SERIAL_SHORT}:$env{ID_REVISION}:$env{ID_ATA_ROTATION_RATE_RPM}"<span style="font-family: Lato, sans-serif; font-size: 16px; line-height: 1.5; background-color: #ffffff;"> </span>

That rule looks for a device that is a “disk” and has an ID_CHANNEL of anything (“?*” means it just cant be empty – usb drives dont have ID_CHANNEL so they dont appear here. ID_CHANNEL is output like this 0:3 where 3 is the slot number, just add 1 and it will 4). If the ID_CHANNEL doesnt exist then this rule is skipped and a symlink is not made. The Symlink that is made if the device is a disk and has the variable ID_CHANNEL will be of the format disk/internal/stuff. meaning that it will go in the /dev/disk/internal folder (udev will auto make the folder /dev/disk/internal). The “stuff” will be ID_CHANNEL:ID_MODEL:ID_SERIAL_SHORT:ID_REVISION:ID_ATA_ROTATION_RATE_RPM. Notice all of these variables are shown with “udevadm test /sys/block/sd*”.

Example of this rules /dev’s results:

# ls -lisah /dev/disk/internal/
total 0
  99 0 drwxrwxrwx 2 root root 160 Mar 18 21:19 .
  98 0 drwxrwxrwx 5 root root 100 Mar 18 21:17 ..
2091 0 lrwxrwxrwx 1 root root   9 Mar 16 14:44 0:0 -> ../../sda
1540 0 lrwxrwxrwx 1 root root   9 Mar 18 21:20 0:0:WDC_WD30EFRX-68EUZN1:WD-WMC4N0479141:80.00A08:5400 -> ../../sda
2092 0 lrwxrwxrwx 1 root root   9 Mar 16 14:44 0:1 -> ../../sdb
 100 0 lrwxrwxrwx 1 root root   9 Mar 16 14:44 0:2 -> ../../sdc
 101 0 lrwxrwxrwx 1 root root   9 Mar 16 14:44 0:3 -> ../../sdd

### So we know that sda is in slot 0+1, meaning slot1
### sdb is in slot 2, sdc is in slot 3, sdd is in slot 4

SIDENOTE: this rule exists on the new ReadyNAS OS6 units. I dont recommend editing them without risk of hurting your warranty. However now you know how to see which /dev/sd* letter ties to which slot. Simply look at “ls -lisah /dev/disk/internal” and add 1 to the second number to get the slot number.

NOTE: if you edit any rules (dont do so on the ReadyNAS or else might hurt). Reset them like this and check out the /dev folder:

udevadm control --reload-rules; udevadm trigger;

 

2 thoughts on “udev & mknod & mdev — and — Udevadm to gather info about a device & all devices

Leave a Reply

Your email address will not be published. Required fields are marked *