Tvheadend starting slowly with a HD-901T2 DVB-T2 USB stick: The udev solution

This post was written by eli on March 11, 2017
Posted Under: DVB,Linux,systemd,udev

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.

Add a Comment

required, use real name
required, will not be published
optional, your blog address