udev: net_id: Improve predictable names for NPAR devices
authorStuart Hayes <stuart_hayes@dell.com>
Thu, 18 Jan 2018 20:14:56 +0000 (15:14 -0500)
committerStuart Hayes <stuart_hayes@dell.com>
Mon, 26 Mar 2018 18:00:43 +0000 (14:00 -0400)
NPAR is a technology that allows a single network interface to
be divided into number of partitions. The partitions show up
as functions on the same PCI device... when there are more than
8 functions, ARI (alternative routing-ID interpretation) is
used. With ARI is enabled, the 8 bit field that normally has 5
bits for the PCI device and 3 bits for the PCI function is instead
interpreted as (implicit) device 0, with 8 bits for the function
number.

Because the linux kernel exposes the PCI device/function numbers
to userspace the same regardless of whether ARI is enabled,
systemd predictable device naming can generate unpredictable
names in this case, because network names using the PCI slot use
the function number, but not the device number, causing systemd
to generate the same name for mulitple network devices (so some
will revert to the "ethX" names).

With this patch, device naming code checks if ARI is enabled for
a PCI network device, and uses the full 8-bit function number
for naming to avoid this situation. This should improve
readability and predictability of device names.

Here is an example of how this change would affect naming:

before patch  |  after patch
-----------------------------
ens2f0        |  ens2f0        NPAR partition 0 (in PCI slot 2)
ens2f1        |  ens2f1        NPAR partition 1
...
ens2f7        |  ens2f7        NPAR partition 7
eth1          |  ens2f8        NPAR partition 8
eth2          |  ens2f9        NPAR partition 9

src/udev/udev-builtin-net_id.c

index 233bdd2..54d2ea0 100644 (file)
@@ -299,6 +299,10 @@ static bool is_pci_multifunction(struct udev_device *dev) {
         return false;
 }
 
+static bool is_pci_ari_enabled(struct udev_device *dev) {
+        return !!udev_device_get_sysattr_value(dev, "ari_enabled");
+}
+
 static int dev_pci_slot(struct udev_device *dev, struct netnames *names) {
         struct udev *udev = udev_device_get_udev(names->pcidev);
         unsigned domain, bus, slot, func, dev_port = 0, hotplug_slot = 0;
@@ -313,6 +317,11 @@ static int dev_pci_slot(struct udev_device *dev, struct netnames *names) {
 
         if (sscanf(udev_device_get_sysname(names->pcidev), "%x:%x:%x.%u", &domain, &bus, &slot, &func) != 4)
                 return -ENOENT;
+        if (is_pci_ari_enabled(names->pcidev))
+                /* ARI devices support up to 256 functions on a single device ("slot"), and interpret the
+                 * traditional 5-bit slot and 3-bit function number as a single 8-bit function number,
+                 * where the slot makes up the upper 5 bits. */
+                func += slot * 8;
 
         /* kernel provided port index for multiple ports on a single PCI function */
         attr = udev_device_get_sysattr_value(dev, "dev_port");