diff --git a/man/systemd.net-naming-scheme.xml b/man/systemd.net-naming-scheme.xml
index 1a024a0c30..9887aa8701 100644
--- a/man/systemd.net-naming-scheme.xml
+++ b/man/systemd.net-naming-scheme.xml
@@ -170,6 +170,7 @@
ID_NET_NAME_SLOT=prefix[Pdomain]sslot[ffunction][nport_name|ddev_port]uport…[cconfig][iinterface]
ID_NET_NAME_SLOT=prefix[Pdomain]sslot[ffunction][nport_name|ddev_port]vslot
ID_NET_NAME_SLOT=prefix[Pdomain]sslot[ffunction][nport_name|ddev_port]rslot
+ ID_NET_NAME_SLOT=prefix[Pdomain]sslot[ffunction][nport_name|ddev_port]Ssfnum
This property describes the slot position. Different schemes are used depending on
the bus type, as described in the table below. In case of USB, BCMA, and SR-VIO devices, the full
@@ -222,6 +223,11 @@
… rslot
SR-IOV slot number
+
+
+ … Ssfnum
+ Auxiliary sub-function (SF) number
+
@@ -247,6 +253,20 @@
is linked to the particular representor, with any leading zeros removed. The physical port
name and the bus number are ignored.
+ Auxiliary sub-function (SF) network devices, where the network device's parent is an
+ auxiliary device exposing a sfnum sysfs attribute (currently mlx5_core SFs),
+ are named based on the underlying PCI function (the PF, or for VF-SF the PF behind the VF),
+ with a suffix of S and the user-defined sub-function number from
+ sfnum. This is analogous to how SR-IOV virtual function devices are named
+ with a v suffix.
+
+ If the SF's parent PCI function is itself an SR-IOV virtual function (VF-SF), the
+ name is rooted at the PF and both suffixes are chained, with the v
+ suffix preceding the S suffix
+ (e.g. enp193s0f0v0S88). The PF, the VF, and the SF therefore form a
+ stable, hierarchical sequence regardless of the VF's underlying PCI bus/device/function
+ numbering.
+
In some configurations a parent PCI bridge of a given network controller may be associated
with a slot. In such case we do not generate this device property to avoid possible naming conflicts.
@@ -262,6 +282,7 @@
ID_NET_NAME_PATH=prefix[Pdomain]pbussslot[ffunction][nphys_port_name|ddev_port]
ID_NET_NAME_PATH=prefix[Pdomain]pbussslot[ffunction][nphys_port_name|ddev_port]bnumber
ID_NET_NAME_PATH=prefix[Pdomain]pbussslot[ffunction][nphys_port_name|ddev_port]uport…[cconfig][iinterface]
+ ID_NET_NAME_PATH=prefix[Pdomain]pbussslot[ffunction][nphys_port_name|ddev_port]Ssfnum
This property describes the device installation location. Different schemes are
used depending on the bus type, as described in the table below. For BCMA and USB devices, PCI path
@@ -310,6 +331,11 @@
USB port number chain
+
+ … Ssfnum
+ Auxiliary sub-function (SF) number
+
+
@@ -575,6 +601,22 @@
+
+
+ v261
+
+ Stable names are now generated for auxiliary sub-function (SF) network devices
+ (such as mlx5_core SFs). The name is built from the parent PCI Physical Function's
+ path with an Ssfnum suffix, where
+ sfnum is the user-defined SF number (the value passed to
+ devlink port add … sfnum N, exposed by the kernel as
+ the sfnum sysfs attribute on the auxiliary device). This is analogous to the
+ vN suffix used for SR-IOV virtual functions; for SFs
+ hosted on SR-IOV VFs (VF-SF), the two suffixes are chained on top of the PF's base name.
+
+
+
+
Note that latest may be used to denote the latest scheme known (to this
diff --git a/src/shared/netif-naming-scheme.c b/src/shared/netif-naming-scheme.c
index ddf3bdde9e..978399ca41 100644
--- a/src/shared/netif-naming-scheme.c
+++ b/src/shared/netif-naming-scheme.c
@@ -31,6 +31,7 @@ static const NamingScheme naming_schemes[] = {
{ "v258", NAMING_V258 },
{ "v259", NAMING_V259 },
{ "v260", NAMING_V260 },
+ { "v261", NAMING_V261 },
/* … add more schemes here, as the logic to name devices is updated … */
EXTRA_NET_NAMING_MAP
diff --git a/src/shared/netif-naming-scheme.h b/src/shared/netif-naming-scheme.h
index 52c462a6bb..1848cb570b 100644
--- a/src/shared/netif-naming-scheme.h
+++ b/src/shared/netif-naming-scheme.h
@@ -44,6 +44,9 @@ typedef enum NamingSchemeFlags {
NAMING_USE_INTERFACE_PROPERTY = 1 << 20, /* Use INTERFACE udev property, rather than sysname, when no renaming is requested. */
NAMING_DEVICETREE_ALIASES_WLAN = 1 << 21, /* Generate names from devicetree aliases for WLAN devices */
NAMING_MCTP = 1 << 22, /* Use "mc" prefix for MCTP devices */
+ NAMING_SUBFUNC = 1 << 23, /* Generate names for auxiliary sub-function (SF) network
+ * devices (e.g. mlx5_core SFs), based on the parent PF's
+ * PCI path and the user-defined sfnum, with an "S" suffix. */
/* And now the masks that combine the features above */
NAMING_V238 = 0,
@@ -67,6 +70,7 @@ typedef enum NamingSchemeFlags {
NAMING_V258 = NAMING_V257 | NAMING_USE_INTERFACE_PROPERTY,
NAMING_V259 = NAMING_V258 | NAMING_DEVICETREE_ALIASES_WLAN,
NAMING_V260 = NAMING_V259 | NAMING_MCTP,
+ NAMING_V261 = NAMING_V260 | NAMING_SUBFUNC,
EXTRA_NET_NAMING_SCHEMES
diff --git a/src/udev/udev-builtin-net_id.c b/src/udev/udev-builtin-net_id.c
index 2491484cf6..d368fb2337 100644
--- a/src/udev/udev-builtin-net_id.c
+++ b/src/udev/udev-builtin-net_id.c
@@ -165,6 +165,46 @@ static int get_virtfn_info(sd_device *pcidev, sd_device **ret_physfn_pcidev, cha
return -ENOENT;
}
+static int get_subfunc_info(sd_device *aux_dev, sd_device **ret_parent_pcidev, char **ret_suffix) {
+ sd_device *parent;
+ unsigned sfnum;
+ char *suffix;
+ int r;
+
+ assert(aux_dev);
+ assert(ret_parent_pcidev);
+ assert(ret_suffix);
+
+ /* The auxiliary device must expose an 'sfnum' attribute. This is currently used by the
+ * mlx5_core driver for sub-function (SF) auxiliary devices. The sfnum is the user-defined
+ * stable identifier passed to "devlink port add ... sfnum N". */
+ r = device_get_sysattr_unsigned_filtered(aux_dev, "sfnum", &sfnum);
+ if (r < 0)
+ return r;
+
+ /* Walk one hop up: the auxiliary device's parent must be a PCI function. It can be either
+ * the PF directly, or an SR-IOV VF — mlx5 also supports SFs hosted on VFs (VF-SF), see
+ * Documentation/networking/representors.rst in the kernel tree. The VF case is handled by
+ * the existing virtfn logic in names_pci(), so here we just return the immediate PCI
+ * parent and a single "S" suffix piece. */
+ r = sd_device_get_parent(aux_dev, &parent);
+ if (r < 0)
+ return r;
+
+ r = device_in_subsystem(parent, "pci");
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -ENODEV;
+
+ if (asprintf(&suffix, "S%u", sfnum) < 0)
+ return log_oom_debug();
+
+ *ret_parent_pcidev = sd_device_ref(parent);
+ *ret_suffix = suffix;
+ return 0;
+}
+
static int get_port_specifier(sd_device *dev, char **ret) {
const char *phys_port_name;
unsigned dev_port;
@@ -928,24 +968,56 @@ static int names_devicetree(UdevEvent *event, const char *prefix) {
static int names_pci(UdevEvent *event, const char *prefix) {
sd_device *parent, *dev = ASSERT_PTR(ASSERT_PTR(event)->dev);
- _cleanup_(sd_device_unrefp) sd_device *physfn_pcidev = NULL;
- _cleanup_free_ char *virtfn_suffix = NULL;
+ _cleanup_(sd_device_unrefp) sd_device *physfn_pcidev = NULL, *parent_pcidev = NULL;
+ _cleanup_free_ char *virtfn_suffix = NULL, *subfunc_suffix = NULL, *combined_suffix = NULL;
+ const char *suffix = NULL;
assert(prefix);
- /* check if our direct parent is a PCI device with no other bus in-between */
- if (get_matching_parent(dev, STRV_MAKE("pci"), /* skip_virtio= */ true, &parent) < 0)
+ /* If the network device's direct parent is an auxiliary device that exposes a stable
+ * 'sfnum' attribute (currently mlx5_core sub-functions), peel off the SF identity into
+ * a 'S' suffix piece and pick up the aux device's underlying PCI function as
+ * 'parent'. The aux device is just a bump on the path; everything below — PF/VF
+ * resolution, slot/onboard lookup — proceeds the same way as for any PCI-rooted
+ * network device. */
+ if (naming_scheme_has(NAMING_SUBFUNC)) {
+ sd_device *aux;
+
+ if (get_matching_parent(dev, STRV_MAKE("auxiliary"),
+ /* skip_virtio= */ false, &aux) >= 0)
+ (void) get_subfunc_info(aux, &parent_pcidev, &subfunc_suffix);
+ }
+
+ /* SF: parent is the aux device's PCI function. Otherwise the network device's direct
+ * parent must itself be a PCI device. */
+ if (subfunc_suffix)
+ parent = parent_pcidev;
+ else if (get_matching_parent(dev, STRV_MAKE("pci"), /* skip_virtio= */ true, &parent) < 0)
return 0;
- /* If this is an SR-IOV virtual device, get base name using physical device and add virtfn suffix. */
+ /* If parent is an SR-IOV VF, walk to the parent PF and add a 'v' suffix piece. The
+ * onboard BIOS label is intentionally not exposed for any "child function" (VF, SF, or
+ * VF-SF), since the label refers to the parent's physical port, not to a logical child. */
+ bool is_child_function = !!subfunc_suffix;
if (naming_scheme_has(NAMING_SR_IOV_V) &&
- get_virtfn_info(parent, &physfn_pcidev, &virtfn_suffix) >= 0)
+ get_virtfn_info(parent, &physfn_pcidev, &virtfn_suffix) >= 0) {
parent = physfn_pcidev;
- else
+ is_child_function = true;
+ }
+ if (!is_child_function)
(void) names_pci_onboard_label(event, parent, prefix);
- (void) names_pci_onboard(event, parent, prefix, virtfn_suffix);
- (void) names_pci_slot(event, parent, prefix, virtfn_suffix);
+ /* Compose the final suffix in PF -> [VF ->] SF order, e.g. "v0", "S88", or "v0S88". */
+ if (virtfn_suffix && subfunc_suffix) {
+ combined_suffix = strjoin(virtfn_suffix, subfunc_suffix);
+ if (!combined_suffix)
+ return log_oom_debug();
+ suffix = combined_suffix;
+ } else
+ suffix = virtfn_suffix ?: subfunc_suffix;
+
+ (void) names_pci_onboard(event, parent, prefix, suffix);
+ (void) names_pci_slot(event, parent, prefix, suffix);
return 0;
}