Tvheadend starting slowly with a HD-901T2 DVB-T2 USB stick: The udev solution
The problem
Having A HD-901T2 DVB-T2 stick on a Linux Mint 18.1 running a 4.4.0-53-generic kernel, Tvheadend started painfully slow. It took more than a minute before the web interface was available. And here’s why:
Mar 11 10:22:35 tv systemd[1]: Starting tvheadend.service...
Mar 11 10:22:35 tv tvheadend[6019]: * Starting Tvheadend tvheadend
Mar 11 10:22:35 tv tvheadend[6019]: ...done.
Mar 11 10:22:35 tv systemd[1]: Started tvheadend.service.
Mar 11 10:22:35 tv tvheadend[6030]: main: Log started
Mar 11 10:22:35 tv tvheadend[6030]: config: loaded
Mar 11 10:22:35 tv tvheadend[6030]: scanfile: DVB-T - loaded 43 regions with 1106 networks
Mar 11 10:22:35 tv tvheadend[6030]: scanfile: DVB-S - loaded 1 regions with 112 networks
Mar 11 10:22:35 tv tvheadend[6030]: scanfile: DVB-C - loaded 17 regions with 56 networks
Mar 11 10:22:35 tv tvheadend[6030]: scanfile: ATSC - loaded 2 regions with 14 networks
Mar 11 10:22:40 tv tvheadend[6030]: linuxdvb: adapter added /dev/dvb/adapter0
Mar 11 10:22:40 tv kernel: [ 5333.042417] usb 1-4: DVB: adapter 0 frontend 0 frequency 0 out of range (174000000..862000000)
Mar 11 10:24:23 tv tvheadend[6030]: linuxdvb: unable to open /dev/dvb/adapter0/frontend1
So that’s it: Tvheadend fails with /dev/dvb/adapter0/frontend1 somehow, and that failure takes time (the driver’s access timeouts?) and blocks the setup of other functionalities. The thing is that frontend1 is in fact the front end for the Panasonic MN88473 DVB-T/T2/C demodulator, and is redundant, because Tvheadend’s interface with the DVB stick is through /dev/dvb/adapter0/frontend0 (Realtek RTL2832).
The elegant solution is to make the offending device file inaccessible using udev. Since udev doesn’t rename device files that are created by the kernel for a long time, playing with its permissions is the way out. If Tvheadend is denied access to /dev/dvb/adapter0/frontend1, it won’t mess with it.
I have old post on udev hacking, and one on udev rules for a NIC, by the way.
You may skip the following section, and go directly to “The fix”, if the gory details seem boring.
Analyzing the udev situation
Plugging in the DVB stick into the computer with
# udevadm monitor --udev --property
running makes a lot of output, but this among others:
UDEV [90.760633] add /devices/pci0000:00/0000:00:14.0/usb1/1-4/dvb/dvb0.frontend1 (dvb) ACTION=add DEVNAME=/dev/dvb/adapter0/frontend1 DEVPATH=/devices/pci0000:00/0000:00:14.0/usb1/1-4/dvb/dvb0.frontend1 DVB_ADAPTER_NUM=0 DVB_DEVICE_NUM=1 DVB_DEVICE_TYPE=frontend ID_FOR_SEAT=dvb-pci-0000_00_14_0-usb-0_4 ID_PATH=pci-0000:00:14.0-usb-0:4 ID_PATH_TAG=pci-0000_00_14_0-usb-0_4 MAJOR=212 MINOR=4 SEQNUM=2829 SUBSYSTEM=dvb TAGS=:uaccess:seat: USEC_INITIALIZED=90759736
And looking at the device itself:
$ udevadm info -a -n /dev/dvb/adapter0/frontend1 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:14.0/usb1/1-3/dvb/dvb0.frontend1': KERNEL=="dvb0.frontend1" SUBSYSTEM=="dvb" DRIVER=="" looking at parent device '/devices/pci0000:00/0000:00:14.0/usb1/1-3': KERNELS=="1-3" SUBSYSTEMS=="usb" DRIVERS=="usb" ATTRS{authorized}=="1" ATTRS{avoid_reset_quirk}=="0" ATTRS{bConfigurationValue}=="1" ATTRS{bDeviceClass}=="00" ATTRS{bDeviceProtocol}=="00" ATTRS{bDeviceSubClass}=="00" ATTRS{bMaxPacketSize0}=="64" ATTRS{bMaxPower}=="500mA" ATTRS{bNumConfigurations}=="1" ATTRS{bNumInterfaces}==" 2" ATTRS{bcdDevice}=="0100" ATTRS{bmAttributes}=="80" ATTRS{busnum}=="1" ATTRS{configuration}=="USB2.0-Bulk&Iso" ATTRS{devnum}=="3" ATTRS{devpath}=="3" ATTRS{idProduct}=="0131" ATTRS{idVendor}=="15f4" ATTRS{ltm_capable}=="no" ATTRS{manufacturer}=="astrometadvbt2" ATTRS{maxchild}=="0" ATTRS{product}=="dvbt2" ATTRS{quirks}=="0x0" ATTRS{removable}=="removable" ATTRS{speed}=="480" ATTRS{urbnum}=="18880" ATTRS{version}==" 2.00" looking at parent device '/devices/pci0000:00/0000:00:14.0/usb1': KERNELS=="usb1" SUBSYSTEMS=="usb" DRIVERS=="usb" ATTRS{authorized}=="1" ATTRS{authorized_default}=="1" ATTRS{avoid_reset_quirk}=="0" ATTRS{bConfigurationValue}=="1" ATTRS{bDeviceClass}=="09" ATTRS{bDeviceProtocol}=="01" ATTRS{bDeviceSubClass}=="00" ATTRS{bMaxPacketSize0}=="64" ATTRS{bMaxPower}=="0mA" ATTRS{bNumConfigurations}=="1" ATTRS{bNumInterfaces}==" 1" ATTRS{bcdDevice}=="0404" ATTRS{bmAttributes}=="e0" ATTRS{busnum}=="1" ATTRS{configuration}=="" ATTRS{devnum}=="1" ATTRS{devpath}=="0" ATTRS{idProduct}=="0002" ATTRS{idVendor}=="1d6b" ATTRS{interface_authorized_default}=="1" ATTRS{ltm_capable}=="no" ATTRS{manufacturer}=="Linux 4.4.0-53-generic xhci-hcd" ATTRS{maxchild}=="7" ATTRS{product}=="xHCI Host Controller" ATTRS{quirks}=="0x0" ATTRS{removable}=="unknown" ATTRS{serial}=="0000:00:14.0" ATTRS{speed}=="480" ATTRS{urbnum}=="52" ATTRS{version}==" 2.00" looking at parent device '/devices/pci0000:00/0000:00:14.0': KERNELS=="0000:00:14.0" SUBSYSTEMS=="pci" DRIVERS=="xhci_hcd" ATTRS{broken_parity_status}=="0" ATTRS{class}=="0x0c0330" ATTRS{consistent_dma_mask_bits}=="64" ATTRS{d3cold_allowed}=="1" ATTRS{device}=="0x22b5" ATTRS{dma_mask_bits}=="64" ATTRS{driver_override}=="(null)" ATTRS{enable}=="1" ATTRS{irq}=="115" ATTRS{local_cpulist}=="0-3" ATTRS{local_cpus}=="f" ATTRS{msi_bus}=="1" ATTRS{numa_node}=="-1" ATTRS{subsystem_device}=="0x1000" ATTRS{subsystem_vendor}=="0x1458" ATTRS{vendor}=="0x8086" looking at parent device '/devices/pci0000:00': KERNELS=="pci0000:00" SUBSYSTEMS=="" DRIVERS==""
Only the first two entries are interesting: The others climb up through the USB port, the PCIebus and finally the PCI root port.
The fix
So I set up this file as /etc/udev/rules.d/50-dvb-rename.rules
SUBSYSTEM=="dvb", ATTRS{idVendor}=="15f4", ATTRS{idProduct}=="0131", KERNEL=="dvb*.frontend1", MODE:="0000", GROUP:="root"
And refreshed the udev system:
# udevadm control --reload
Don’t get discouraged by /etc/udev/ being almost empty: The systemd rules have moved to /lib/udev/, and following systemd conventions, local fixes should be in /etc/udev/.
That didn’t work exactly as I expected, but good enough (after replugging the DVB stick):
$ ls -l /dev/dvb/adapter0/
total 0
crw-rw----+ 1 root video 212, 0 Mar 11 10:50 demux0
crw-rw----+ 1 root video 212, 1 Mar 11 10:50 dvr0
crw-rw----+ 1 root video 212, 3 Mar 11 10:50 frontend0
c---rw----+ 1 root root 212, 4 Mar 11 10:50 frontend1
crw-rw----+ 1 root video 212, 2 Mar 11 10:50 net0
WTF? Note that the MODE assignment is with a := which is supposed to prevent overruling. And still, it’s group accessible. Where did that come from? Anyhow, that’s why the group is set to root. That fixed the problem with Tvheadend, which now finishes initializing 12 seconds after systemd announces its launch.
More udev rambling
I went on a bit on this. Nothing really for TLDRers.
Tesring (doesn’t really require root privileges, but the actions won’t take place):
# udevadm test -a add $(udevadm info -q path -n /dev/dvb/adapter0/frontend1)
or stating the path explicitly:
# udevadm info -q path -n /dev/dvb/adapter0/frontend1 /devices/pci0000:00/0000:00:14.0/usb1/1-4/dvb/dvb0.frontend1 # udevadm test -a add /devices/pci0000:00/0000:00:14.0/usb1/1-4/dvb/dvb0.frontend1 [ ... ] GROUP 0 /etc/udev/rules.d/50-dvb-rename.rules:4 MODE 0 /etc/udev/rules.d/50-dvb-rename.rules:4 IMPORT builtin 'path_id' /lib/udev/rules.d/71-seat.rules:49 RUN 'uaccess' /lib/udev/rules.d/73-seat-late.rules:15 handling device node '/dev/dvb/adapter0/frontend1', devnum=c212:4, mode=0, uid=0 , gid=0 set permissions /dev/dvb/adapter0/frontend1, 020000, uid=0, gid=0 preserve already existing symlink '/dev/char/212:4' to '../dvb/adapter0/frontend 1' created db file '/run/udev/data/c212:4' for '/devices/pci0000:00/0000:00:14.0/usb1/1-4/dvb/dvb0.frontend1' [ ... ]
And here comes the big surprise: After the test, the frontend1′s permission was actually 0000 as required! So who’s fiddling with the permission after udev?
$ ls -l /dev/dvb/adapter0/total 0
crw-rw----+ 1 root video 212, 0 Mar 11 10:50 demux0
crw-rw----+ 1 root video 212, 1 Mar 11 10:50 dvr0
crw-rw----+ 1 root video 212, 3 Mar 11 10:50 frontend0
c---------+ 1 root root 212, 4 Mar 11 10:53 frontend1
crw-rw----+ 1 root video 212, 2 Mar 11 10:50 net0
If anyone knows, please comment below. For my current purpose, I could work this around by changing the group.
And if I’m at it: When Tvheadend runs, all device files in /dev/dvb/adapter0/ appear to have been deleted. They return when the Tvheadend is stopped. Does anyone have an idea on how and why?
Unlike what one might have expected, there was no need to update the initramfs. The attributes of frontend1 were the same (as the replug above, not the test) after a reboot:
# mount -o remount,rw /boot/ # update-initramfs -u -v
which didn’t make any difference, as expected.