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

Jordan Hargrave jharg93 at gmail.com
Mon Nov 9 20:49:39 PST 2015


Cleaned up linux coding style

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 | 206 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 206 insertions(+)

diff --git a/src/udev/udev-builtin-net_id.c b/src/udev/udev-builtin-net_id.c
index bf5c9c6..04b08dd 100644
--- a/src/udev/udev-builtin-net_id.c
+++ b/src/udev/udev-builtin-net_id.c
@@ -119,16 +119,130 @@ 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) {
+                if (sscanf(dcm+off, fmt, &port, &df, &pfi, &flag) != 4)
+                        continue;
+                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");
+        fd = open(filename, O_RDONLY);
+        if (fd < 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 */
+        dcm = vpd_findtag(buf, len, "DCM");
+        if (dcm != NULL)
+                dev_pci_npar_dcm(dev, names, dcm->len, dcm->data,
+                                 "%1x%1x%2x%6x", 10);
+        else {
+                dcm = vpd_findtag(buf, len, "DC2");
+                if (dcm != NULL)
+                        dev_pci_npar_dcm(dev, names, dcm->len, dcm->data,
+                                         "%1x%2x%2x%6x", 11);
+        }
+done:
+        close(fd);
+}
+
 /* 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 +301,78 @@ static bool is_pci_multifunction(struct udev_device *dev) {
         return false;
 }

+enum {
+        SLOT_USAGE_OTHER = 1,
+        SLOT_USAGE_UNKNOWN,
+        SLOT_USAGE_AVAILABLE,
+        SLOT_USAGE_INUSE,
+};
+
+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;
+} __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);
+                fd = open(path, O_RDONLY);
+                if (fd < 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 +393,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 +466,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 +491,15 @@ 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 +760,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;
         }

-- 
1.9.1


More information about the systemd-devel mailing list