kernel-boot: Add naming support for sub-function (SF) RDMA devices
PCI Sub-function (SF) is anchored on the auxiliary bus rather than
directly on the PCI bus, so by_pci() previously rejected them as
"Non-PCI" and they fell back to their kernel-assigned name.
Detect this case by treating an "auxiliary" device subsystem as a valid
parent: read the stable 'sfnum' attribute from the aux device and
follow its 'device' symlink up to the underlying PCI BDF. The PCI
parent can be a PF or an SR-IOV VF; feeding the BDF into the
existing get_virtfn_info() / fill_pci_info() path handles both layouts
uniformly. The PCI-derived portion of the name is composed unchanged;
an S<sfnum> suffix is then appended last, after any f<func>/v<vf>
components, since SF identity is independent of them.
Examples:
SF on a multi-function PF:
parent 0000:c1:00.0, sfnum 88 -> rocep193s0f0S88
SF on an SR-IOV VF (VF-SF):
parent 0000:c1:00.4, sfnum 99 -> rocep193s0f0v0S99
Also update Documentation/udev.md and the rdma-persistent-naming.rules
header comment with the new S<sfnum> suffix.
Signed-off-by: Jiri Pirko <jiri@nvidia.com>
diff --git a/Documentation/udev.md b/Documentation/udev.md
index 293db3d..14a6e83 100644
--- a/Documentation/udev.md
+++ b/Documentation/udev.md
@@ -190,6 +190,7 @@
* s<slot>[f<function>] - hotplug slot index number
* x<GUID> - Node GUID
* [P<domain>]p<bus>s<slot>[f<function>] - PCI geographical location
+ * S<sfnum> - sub-function number, appended to the parent PCI device name
Notes:
@@ -198,3 +199,5 @@
* When using PCI geography, The PCI domain is only prepended when it is not 0.
* SR-IOV virtual devices are named based on the name of the parent interface,
with a suffix of "v<N>", where <N> is the virtual device number.
+ * Sub-functions (SFs) are named based on the name of their parent PCI device,
+ with a suffix of "S<N>", where <N> is the kernel-exposed sfnum.
diff --git a/kernel-boot/rdma-persistent-naming.rules b/kernel-boot/rdma-persistent-naming.rules
index 6f9c53a..b009555 100644
--- a/kernel-boot/rdma-persistent-naming.rules
+++ b/kernel-boot/rdma-persistent-naming.rules
@@ -21,6 +21,14 @@
# pci = 0000:00:0c.4
# Device type = IB
# mlx5_0 -> ibp0s12f4
+# * NAME_PCI with sub-function (SF)
+# parent pci = 0000:c1:00.0, sfnum = 88
+# Device type = RoCE
+# mlx5_5 -> rocep193s0f0S88
+# * NAME_PCI with SF on SR-IOV VF (VF-SF)
+# parent pci = 0000:c1:00.4 (VF of 0000:c1:00.0), sfnum = 99
+# Device type = RoCE
+# mlx5_6 -> rocep193s0f0v0S99
# * NAME_GUID
# GUID = 5254:00c0:fe12:3455
# Device type = RoCE
diff --git a/kernel-boot/rdma_rename.c b/kernel-boot/rdma_rename.c
index 24207dc..e6ea8f4 100644
--- a/kernel-boot/rdma_rename.c
+++ b/kernel-boot/rdma_rename.c
@@ -5,6 +5,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <libgen.h>
+#include <limits.h>
#include <stdbool.h>
#include <errno.h>
#include <unistd.h>
@@ -41,6 +42,14 @@
* pci = 0000:00:0c.4
* Device type = IB
* mlx5_0 -> ibp0s12f4
+ * NAME_PCI with sub-function (SF)
+ * parent pci = 0000:c1:00.0, sfnum = 88
+ * Device type = RoCE
+ * mlx5_5 -> rocep193s0f0S88
+ * NAME_PCI with SF on SR-IOV VF (VF-SF)
+ * parent pci = 0000:c1:00.4 (VF of 0000:c1:00.0), sfnum = 99
+ * Device type = RoCE
+ * mlx5_6 -> rocep193s0f0v0S99
* NAME_GUID
* GUID = 5254:00c0:fe12:3455
* Device type = RoCE
@@ -245,9 +254,82 @@
unsigned int func;
unsigned int sun;
unsigned int vf;
+ unsigned int sfnum;
bool valid_vf;
+ bool valid_sf;
};
+/*
+ * If the ibdev's parent (the "device" symlink) is an auxiliary device that
+ * carries a stable 'sfnum' attribute, this is a sub-function (SF) host RDMA
+ * device. Read sfnum, then re-point p->pcidev at /sys/bus/pci/devices/<BDF>
+ * of the aux device's PCI parent so that the rest of by_pci() (i.e.
+ * get_virtfn_info(), fill_pci_info() and the name composition) proceeds
+ * unchanged. The PCI parent can be a PF or an SR-IOV VF; both layouts
+ * are handled by the same code path.
+ */
+static int detect_sf(struct data *d, struct pci_info *p)
+{
+ char real_path[PATH_MAX];
+ char *sfnum_path = NULL;
+ char *device_path = NULL;
+ char *last_slash, *bdf;
+ unsigned int sfnum;
+ FILE *fp;
+ int ret;
+
+ ret = asprintf(&sfnum_path,
+ "/sys/class/infiniband/%s/device/sfnum", d->curr);
+ if (ret < 0)
+ return -ENOMEM;
+
+ fp = fopen(sfnum_path, "r");
+ free(sfnum_path);
+ if (!fp) {
+ /* Auxiliary parent without 'sfnum'; not an SF we recognize. */
+ pr_dbg("%s: Auxiliary parent has no 'sfnum' attribute\n",
+ d->curr);
+ return -EINVAL;
+ }
+
+ if (fscanf(fp, "%u", &sfnum) != 1) {
+ fclose(fp);
+ return -EINVAL;
+ }
+ fclose(fp);
+
+ ret = asprintf(&device_path, "/sys/class/infiniband/%s/device",
+ d->curr);
+ if (ret < 0)
+ return -ENOMEM;
+
+ /* The 'device' symlink target is relative (e.g. "../../../<driver>.sf.<id>")
+ * and does not itself encode the PCI BDF. Resolve it to its absolute,
+ * canonical form so the path's parent directory exposes the BDF. */
+ if (!realpath(device_path, real_path)) {
+ free(device_path);
+ return -EINVAL;
+ }
+ free(device_path);
+
+ /* Strip the trailing aux-device component to expose the PCI parent. */
+ last_slash = strrchr(real_path, '/');
+ if (!last_slash || last_slash == real_path)
+ return -EINVAL;
+ *last_slash = 0;
+
+ bdf = basename(real_path);
+ ret = asprintf(&p->pcidev, "/sys/bus/pci/devices/%s", bdf);
+ if (ret < 0) {
+ p->pcidev = NULL;
+ return -ENOMEM;
+ }
+
+ p->valid_sf = true;
+ p->sfnum = sfnum;
+ return 0;
+}
+
static int fill_pci_info(struct data *d, struct pci_info *p)
{
char buf[256] = {};
@@ -379,21 +461,26 @@
buf[ret] = 0;
subs = basename(buf);
- if (strcmp(subs, "pci")) {
+ if (!strcmp(subs, "auxiliary")) {
+ /* SFs sit one auxiliary hop below the underlying PCI function. */
+ ret = detect_sf(d, &p);
+ if (ret)
+ goto out;
+ } else if (!strcmp(subs, "pci")) {
+ ret = asprintf(&p.pcidev,
+ "/sys/class/infiniband/%s/device", d->curr);
+ if (ret < 0) {
+ ret = -ENOMEM;
+ p.pcidev = NULL;
+ goto out;
+ }
+ } else {
/* Ball out virtual devices */
pr_dbg("%s: Non-PCI device (%s) was detected\n", d->curr, subs);
ret = -EINVAL;
goto out;
}
- /* Real devices */
- ret = asprintf(&p.pcidev, "/sys/class/infiniband/%s/device", d->curr);
- if (ret < 0) {
- ret = -ENOMEM;
- p.pcidev = NULL;
- goto out;
- }
-
ret = get_virtfn_info(d, &p);
if (ret)
goto out;
@@ -453,6 +540,23 @@
strcat(d->name, buf);
}
}
+
+ /*
+ * SF identity is independent of any preceding f<func>/v<vf>
+ * components, so append S<sfnum> last whenever it was set. For an SF
+ * whose parent is a multi-function PF this yields
+ * <prefix>p<bus>s<slot>f<func>S<sfnum>; for an SF on an SR-IOV VF
+ * (VF-SF) the same rule yields
+ * <prefix>p<bus>s<slot>f<func>v<vf>S<sfnum>.
+ */
+ if (p.valid_sf) {
+ ret = sprintf(buf, "S%u", p.sfnum);
+ if (ret == -1) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ strcat(d->name, buf);
+ }
ret = 0;
out:
free(p.pcidev);