I'm working at a system based on Linux servers hosting multiple PCIe data acquisition boards. Each board has its individual Device Serial Number available in the extended PCIe configuration space. The cards are handled as UIO devices. Unfortunately, it appears that the boards are sometimes enumerated in the random order. Proper operation of the system requires that there are symlinks created with names permanently associated with the individual boards.
The natural solution is to use udev (e.g., based on information from http://reactivated.net/writing_udev_rules.html ). However, udev does not provide the Device Serial Number capability between the device attributes. It may be tested with command like this:
#udevadm info --attribute-walk /dev/uio0 [...] looking at device '/devices/pci0000:00/0000:00:03.0/uio/uio0': KERNEL=="uio0" SUBSYSTEM=="uio" DRIVER=="" ATTR{event}=="0" ATTR{name}=="uio_pci_generic" ATTR{power/async}=="disabled" ATTR{power/control}=="auto" ATTR{power/runtime_active_kids}=="0" ATTR{power/runtime_active_time}=="0" ATTR{power/runtime_enabled}=="disabled" ATTR{power/runtime_status}=="unsupported" ATTR{power/runtime_suspended_time}=="0" ATTR{power/runtime_usage}=="0" ATTR{version}=="0.01.0" looking at parent device '/devices/pci0000:00/0000:00:03.0': KERNELS=="0000:00:03.0" SUBSYSTEMS=="pci" DRIVERS=="uio_pci_generic" ATTRS{ari_enabled}=="0" ATTRS{broken_parity_status}=="0" ATTRS{class}=="0x00ff00" ATTRS{consistent_dma_mask_bits}=="32" ATTRS{current_link_speed}=="Unknown" ATTRS{current_link_width}=="0" ATTRS{d3cold_allowed}=="0" ATTRS{device}=="0x3342" ATTRS{dma_mask_bits}=="32" ATTRS{driver_override}=="(null)" ATTRS{enable}=="1" ATTRS{irq}=="23" ATTRS{local_cpulist}=="0" ATTRS{local_cpus}=="1" ATTRS{max_link_speed}=="Unknown" ATTRS{max_link_width}=="255" ATTRS{msi_bus}=="1" ATTRS{numa_node}=="-1" ATTRS{power/async}=="enabled" ATTRS{power/control}=="on" ATTRS{power/runtime_active_kids}=="0" ATTRS{power/runtime_active_time}=="696133" ATTRS{power/runtime_enabled}=="forbidden" ATTRS{power/runtime_status}=="active" ATTRS{power/runtime_suspended_time}=="0" ATTRS{power/runtime_usage}=="2" ATTRS{revision}=="0x00" ATTRS{subsystem_device}=="0x1100" ATTRS{subsystem_vendor}=="0x1af4" ATTRS{vendor}=="0xabba" looking at parent device '/devices/pci0000:00': KERNELS=="pci0000:00" SUBSYSTEMS=="" DRIVERS=="" ATTRS{power/async}=="enabled" ATTRS{power/control}=="auto" ATTRS{power/runtime_active_kids}=="7" ATTRS{power/runtime_active_time}=="0" ATTRS{power/runtime_enabled}=="disabled" ATTRS{power/runtime_status}=="unsupported" ATTRS{power/runtime_suspended_time}=="0" ATTRS{power/runtime_usage}=="0" The udevadm finds the associated parent PCIe device, but does not show its Device Serial Number.
The DSN may be read with lspci:
# lspci -vvv -s 0000:00:03.0 [...] Capabilities: [100 v1] Device Serial Number 12-34-56-78-90-ab-cd-ef [...] Based on that I have created a working solution. The first part is the udev rule stored in the file /etc/udev/rules.d/30-daq.rules:
SUBSYSTEM=="uio" PROGRAM="/opt/bin/uio_namer" SYMLINK="%c" The DSN of the PCIe board is found by the script /opt/bin/uio_namer which is the second part of the solution:
#!/bin/sh DEVID=abba:3342 SLOTNAME=`basename \`readlink /sys/${DEVPATH}/device\`` DLSPCI=`lspci -vvv -s ${SLOTNAME}` #Check if it is our device if [ -z "`echo ${DLSPCI} | grep Device | grep ${DEVID}`" ] ; then # It is UIO device associated with other PCIe device # don't create the symlink for it exit 1 fi DSNCAP=`lspci -vvv -s ${SLOTNAME} | grep "Device Serial Number" ` echo daq/${DSNCAP##*" "} The script also filters out UIO created for other PCIe boards. For "our" boards the symlinks to the device files are created in the /dev/daq directory as the board's serial numbers. For example: /dev/daq/12-34-56-78-90-ab-cd-ef
The solution has been tested in QEMU with added model of the PCIe DAQ board. It is working. However, I don't think it is optimal and here are the questions:
- Is there any way to access the attributes of the parent device in a udev rule?
- My solution relies on the particular format of the text output of
lspci -vvv. If that format is changed, the solution won't work any more. Using the "-m" option for "machine readable output" does not help, as it does not print DSN. Is there any utility that may reliably output DSN of a PCIe device in a stable format?