[systemd-devel] [PATCH] Add support for detecting NIC partitions on Dell Servers

jharg93 at gmail.com jharg93 at gmail.com
Mon Nov 9 09:52:54 PST 2015


From: Jordan Hargrave <jharg93 at gmail.com>

This patch will integrate some of the features of biosdevname into systemd.
The code detects the port and index for detecting NIC partitions. This creates
a new environment variable, ID_NET_NAME_PARTITION of the format <PORT>_<PARTITION>

The patch will also decode SMBIOS slot number for NIC, and store in the variable ID_NET_NAME_SMBIOS_SLOT.  Systemd does not have a method for naming ports on a multi-port card plugged into a slot.

Signed-off-by: Jordan Hargrave <jordan_hargrave at dell.com>
---
 src/udev/udev-builtin-net_id.c | 208 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 208 insertions(+)

diff --git a/src/udev/udev-builtin-net_id.c b/src/udev/udev-builtin-net_id.c
index ef9c398..db9e133 100644
--- a/src/udev/udev-builtin-net_id.c
+++ b/src/udev/udev-builtin-net_id.c
@@ -119,16 +119,132 @@ struct netnames {
         bool mac_valid;
 
         struct udev_device *pcidev;
+        struct udev_device *physdev;
         char pci_slot[IFNAMSIZ];
         char pci_path[IFNAMSIZ];
         char pci_onboard[IFNAMSIZ];
         const char *pci_onboard_label;
+        int  npar_port;
+        int  npar_pfi;
+        int  smbios_slot;
 
         char usb_ports[IFNAMSIZ];
         char bcma_core[IFNAMSIZ];
         char ccw_group[IFNAMSIZ];
 };
 
+#define FLAG_IOV 0x80
+#define FLAG_NPAR 0x1000
+
+#define VPDI_TAG 0x82
+#define VPDR_TAG 0x90
+
+struct vpd_tag
+{
+        char          cc[2];
+        unsigned char len;
+        char          data[1];
+};
+
+/* Read VPD tag ID */
+static int vpd_readtag(int fd, int *len)
+{
+        unsigned char tag, tlen[2];
+
+        if (read(fd, &tag, 1) != 1)
+                return -1;
+        if (tag == 0x00 || tag == 0xFF || tag == 0x7F)
+                return -1;
+        if (tag & 0x80) {
+                if (read(fd, tlen, 2) != 2)
+                        return -1;
+                *len = tlen[0] + (tlen[1] << 8);
+                return tag;
+        }
+        *len = (tag & 0x7);
+        return (tag & ~0x7);
+}
+
+static void *vpd_findtag(void *buf, int len, const char *sig)
+{
+        int off, siglen;
+        struct vpd_tag *t;
+
+        off = 0;
+        siglen = strlen(sig);
+        while (off < len) {
+                t = (struct vpd_tag *)((unsigned char *)buf + off);
+                if (!memcmp(t->data, sig, siglen))
+                        return t;
+                off += (t->len + 3);
+        }
+        return NULL;
+}
+
+static void dev_pci_npar_dcm(struct udev_device *dev, struct netnames *names,
+                             int len, const char *dcm,
+                             const char *fmt, int step)
+{
+        int domain, bus, slot, func, off, mydf;
+        int port, df, pfi, flag;
+
+        if (sscanf(udev_device_get_sysname(names->physdev), "%x:%x:%x.%u",
+                   &domain, &bus, &slot, &func) != 4)
+                return;
+        mydf = (slot << 3) + func;
+        for (off=3; off<len; off+=step) {
+                sscanf(dcm+off, fmt, &port, &df, &pfi, &flag);
+                if ((flag & FLAG_NPAR) && mydf == df) {
+                        names->npar_port = port;
+                        names->npar_pfi = pfi;
+                }
+        }
+}
+
+static void dev_pci_npar(struct udev_device *dev, struct netnames *names) {
+        const char *filename;
+        int len, fd;
+        struct vpd_tag *dcm;
+        void *buf;
+
+        /* Search for VPD or IOV VPD */
+        filename = strjoina(udev_device_get_syspath(names->physdev), "/vpd");
+        if ((fd = open(filename, O_RDONLY)) < 0) {
+                return;
+        }
+        if (vpd_readtag(fd, &len) != VPDI_TAG) {
+                goto done;
+        }
+        lseek(fd, len, SEEK_CUR);
+
+        /* Check VPD-R */
+        if (vpd_readtag(fd, &len) != VPDR_TAG) {
+                goto done;
+        }
+        buf = alloca(len);
+        if (read(fd, buf, len) != len) {
+                goto done;
+        }
+
+        /* Check for DELL VPD tag */
+        if (!vpd_findtag(buf, len, "DSV1028VPDR.VER")) {
+                goto done;
+        }
+
+        /* Find DCM/DC2 tag */
+        if ((dcm = vpd_findtag(buf, len, "DCM")) != NULL) {
+                dev_pci_npar_dcm(dev, names, dcm->len, dcm->data,
+                                 "%1x%1x%2x%6x", 10);
+        }
+        else if ((dcm = vpd_findtag(buf, len, "DC2")) != NULL) {
+                dev_pci_npar_dcm(dev, names, dcm->len, dcm->data,
+                                 "%1x%2x%2x%6x", 11);
+        }
+ done:
+        close(fd);
+        return;
+}
+
 /* retrieve on-board index number and label from firmware */
 static int dev_pci_onboard(struct udev_device *dev, struct netnames *names) {
         unsigned dev_port = 0;
@@ -187,6 +303,79 @@ static bool is_pci_multifunction(struct udev_device *dev) {
         return false;
 }
 
+enum {
+        SLOT_USAGE_OTHER=1,
+        SLOT_USAGE_UNKNOWN=2,
+        SLOT_USAGE_AVAILABLE=3,
+        SLOT_USAGE_INUSE=4,
+};
+
+struct smbios_type9 {
+        unsigned char  hdr_type;
+        unsigned char  hdr_len;
+        unsigned short hdr_handle;
+
+        unsigned char  ref;
+        unsigned char  type;
+        unsigned char  buswidth;
+        unsigned char  usage;
+        unsigned char  length;
+        unsigned short id;
+        unsigned char  flags1;
+        unsigned char  flags2;
+        unsigned short seg;
+        unsigned char  bus;
+        unsigned char  devfn;
+} __attribute__((packed));
+
+static int dev_pci_smbios_slot(struct udev_device *dev,
+                               struct netnames *names,
+                               int dev_port) {
+        int domain, bus, slot, func;
+        struct smbios_type9 t9;
+        char path[256];
+        int n, fd, rc;
+        struct udev_device *parent;
+        uint8_t hdr;
+
+        for(n=0; ; n++) {
+                snprintf(path, sizeof(path),
+                         "/sys/firmware/dmi/entries/9-%u/raw", n);
+                if ((fd = open(path, O_RDONLY)) < 0) {
+                        break;
+                }
+                /* Read SMBIOS slot data */
+                rc = read(fd, &t9, sizeof(t9));
+                close(fd);
+                if (rc != sizeof(t9)) {
+                        continue;
+                }
+                /* Ignore slots not marked in use */
+                if (t9.usage != SLOT_USAGE_INUSE)
+                        continue;
+                /* Ignore 00:00.0 and FF:1F.7 */
+                if (t9.bus == 0 && t9.devfn == 0)
+                        continue;
+                if (t9.bus == 0xFF && t9.devfn == 0xFF)
+                        continue;
+                /* Scan parent until we find match */
+                parent = names->physdev;
+                do {
+                        if (sscanf(udev_device_get_sysname(parent),
+                                   "%x:%x:%x.%u", &domain, &bus,
+                                   &slot, &func) != 4)
+                                break;
+                        if (domain == t9.seg &&
+                            bus == t9.bus &&
+                            slot == (t9.devfn >> 3) &&
+                            func == (t9.devfn & 7)) {
+                                names->smbios_slot = t9.id;
+                        }
+                } while ((parent = udev_device_get_parent(parent)) != NULL);
+         }
+        return 0;
+}
+
 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;
@@ -207,6 +396,9 @@ static int dev_pci_slot(struct udev_device *dev, struct netnames *names) {
         if (attr)
                 dev_port = strtol(attr, NULL, 10);
 
+        if (dev_pci_smbios_slot(dev, names, dev_port))
+                return 0;
+
         /* compose a name based on the raw kernel's PCI bus, slot numbers */
         s = names->pci_path;
         l = sizeof(names->pci_path);
@@ -277,6 +469,7 @@ out:
 
 static int names_pci(struct udev_device *dev, struct netnames *names) {
         struct udev_device *parent;
+        char path[256];
 
         assert(dev);
         assert(names);
@@ -301,8 +494,14 @@ static int names_pci(struct udev_device *dev, struct netnames *names) {
                 if (!names->pcidev)
                         return -ENOENT;
         }
+        /* find SR-IOV parent device */
+        snprintf(path, sizeof(path), "%s/physfn", udev_device_get_syspath(names->pcidev));
+        names->physdev = udev_device_new_from_syspath(names->pcidev, path);
+        if (!names->physdev)
+                names->physdev = names->pcidev;
         dev_pci_onboard(dev, names);
         dev_pci_slot(dev, names);
+dev_pci_npar(dev, names);
         return 0;
 }
 
@@ -563,6 +762,15 @@ static int builtin_net_id(struct udev_device *dev, int argc, char *argv[], bool
                 if (names.pci_slot[0])
                         if (snprintf(str, sizeof(str), "%s%s", prefix, names.pci_slot) < (int)sizeof(str))
                                 udev_builtin_add_property(dev, test, "ID_NET_NAME_SLOT", str);
+
+
+                if (names.npar_port)
+                        if (snprintf(str, sizeof(str), "%d_%d", names.npar_port, names.npar_pfi) < (int)sizeof(str))
+                                udev_builtin_add_property(dev, test, "ID_NET_NAME_PARTITION", str);
+
+                if (names.smbios_slot)
+                        if (snprintf(str, sizeof(str), "%d", names.smbios_slot) < (int)sizeof(str))
+                                udev_builtin_add_property(dev, test, "ID_NET_NAME_SMBIOS_SLOT", str);
                 goto out;
         }
 
-- 
2.5.0



More information about the systemd-devel mailing list