udev: predictable names for auxiliary sub-function (SF) devices (#42154)
Some drivers (currently mlx5_core) expose sub-functions (SFs) of a PCI Physical or Virtual Function as auxiliary devices that carry a stable `sfnum` sysfs attribute (the user-defined SF number passed to `devlink port add ... sfnum N`). Their leaf devices (net, infiniband, ...) currently inherit the parent PF/VF's `ID_PATH` and the SF netdev falls through to the kernel-assigned `eth<N>` name. Two patches: 1. `udev-builtin-path_id`: prepend an `sf-<N>` token when the walk crosses an aux device with `sfnum`, so SF leaf devices get an `ID_PATH` distinct from the parent PF/VF (e.g. `pci-0000:c1:00.0-sf-88`). Aux devices without `sfnum` keep the pre-patch behaviour, so existing `ID_PATH` values are unchanged and the patch needs no naming-scheme gate (path_id is unversioned). 2. `udev-builtin-net_id`: name SF host netdevs analogously to SR-IOV VF host netdevs — walk to the parent PCI function and append a single-character `S<sfnum>` suffix. SFs hosted on SR-IOV VFs (VF-SF) get both suffixes chained on the PF's base name (`enp193s0f0v0S88`). Gated behind `NAMING_SUBFUNC` / `NAMING_V261`. Man page (`systemd.net-naming-scheme(7)`) updated. The patches apply to any driver that exposes its SF leaf devices below an aux device exposing `sfnum`. Validated on mlx5 hardware: PF, VF, SF-on-PF (`enp8s0f0S88`, `pci-0000:08:00.0-sf-88`). VF-SF case verified with a fake-sysfs setup since this is not supported by any real device and is more a theoretical use case.
This commit is contained in:
@@ -299,6 +299,11 @@ CHANGES WITH 261 in spe:
|
||||
GPT header (i.e. is partitioned) but the block device has partition
|
||||
table processing turned off.
|
||||
|
||||
* Persistent network interface naming has been extended to auxiliary
|
||||
sub-function (SF) network devices (such as mlx5_core SFs), using an
|
||||
"S<sfnum>" suffix appended to the parent PCI function's name (e.g.
|
||||
"enp193s0f0S88").
|
||||
|
||||
Changes in systemd-boot, systemd-stub, bootctl, ukify:
|
||||
|
||||
* systemd-stub will now maintain a "boot secret" and pass it to the OS
|
||||
|
||||
@@ -170,6 +170,7 @@
|
||||
<term><varname>ID_NET_NAME_SLOT=</varname><replaceable>prefix</replaceable>[<constant>P</constant><replaceable>domain</replaceable>]<constant>s</constant><replaceable>slot</replaceable>[<constant>f</constant><replaceable>function</replaceable>][<constant>n</constant><replaceable>port_name</replaceable>|<constant>d</constant><replaceable>dev_port</replaceable>]<constant>u</constant><replaceable>port</replaceable>…[<constant>c</constant><replaceable>config</replaceable>][<constant>i</constant><replaceable>interface</replaceable>]</term>
|
||||
<term><varname>ID_NET_NAME_SLOT=</varname><replaceable>prefix</replaceable>[<constant>P</constant><replaceable>domain</replaceable>]<constant>s</constant><replaceable>slot</replaceable>[<constant>f</constant><replaceable>function</replaceable>][<constant>n</constant><replaceable>port_name</replaceable>|<constant>d</constant><replaceable>dev_port</replaceable>]<constant>v</constant><replaceable>slot</replaceable></term>
|
||||
<term><varname>ID_NET_NAME_SLOT=</varname><replaceable>prefix</replaceable>[<constant>P</constant><replaceable>domain</replaceable>]<constant>s</constant><replaceable>slot</replaceable>[<constant>f</constant><replaceable>function</replaceable>][<constant>n</constant><replaceable>port_name</replaceable>|<constant>d</constant><replaceable>dev_port</replaceable>]<constant>r</constant><replaceable>slot</replaceable></term>
|
||||
<term><varname>ID_NET_NAME_SLOT=</varname><replaceable>prefix</replaceable>[<constant>P</constant><replaceable>domain</replaceable>]<constant>s</constant><replaceable>slot</replaceable>[<constant>f</constant><replaceable>function</replaceable>][<constant>n</constant><replaceable>port_name</replaceable>|<constant>d</constant><replaceable>dev_port</replaceable>]<constant>S</constant><replaceable>sfnum</replaceable></term>
|
||||
|
||||
<listitem><para>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 @@
|
||||
<entry>… <constant>r</constant><replaceable>slot</replaceable></entry>
|
||||
<entry>SR-IOV slot number</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry>… <constant>S</constant><replaceable>sfnum</replaceable></entry>
|
||||
<entry>Auxiliary sub-function (SF) number</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
@@ -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.</para>
|
||||
|
||||
<para>Auxiliary sub-function (SF) network devices, where the network device's parent is an
|
||||
auxiliary device exposing a <constant>sfnum</constant> 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 <constant>S</constant> and the user-defined sub-function number from
|
||||
<constant>sfnum</constant>. This is analogous to how SR-IOV virtual function devices are named
|
||||
with a <constant>v</constant> suffix.</para>
|
||||
|
||||
<para>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 <constant>v</constant>
|
||||
suffix preceding the <constant>S</constant> suffix
|
||||
(e.g. <literal>enp193s0f0v0S88</literal>). The PF, the VF, and the SF therefore form a
|
||||
stable, hierarchical sequence regardless of the VF's underlying PCI bus/device/function
|
||||
numbering.</para>
|
||||
|
||||
<para>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.</para>
|
||||
|
||||
@@ -262,6 +282,7 @@
|
||||
<term><varname>ID_NET_NAME_PATH=</varname><replaceable>prefix</replaceable>[<constant>P</constant><replaceable>domain</replaceable>]<constant>p</constant><replaceable>bus</replaceable><constant>s</constant><replaceable>slot</replaceable>[<constant>f</constant><replaceable>function</replaceable>][<constant>n</constant><replaceable>phys_port_name</replaceable>|<constant>d</constant><replaceable>dev_port</replaceable>]</term>
|
||||
<term><varname>ID_NET_NAME_PATH=</varname><replaceable>prefix</replaceable>[<constant>P</constant><replaceable>domain</replaceable>]<constant>p</constant><replaceable>bus</replaceable><constant>s</constant><replaceable>slot</replaceable>[<constant>f</constant><replaceable>function</replaceable>][<constant>n</constant><replaceable>phys_port_name</replaceable>|<constant>d</constant><replaceable>dev_port</replaceable>]<constant>b</constant><replaceable>number</replaceable></term>
|
||||
<term><varname>ID_NET_NAME_PATH=</varname><replaceable>prefix</replaceable>[<constant>P</constant><replaceable>domain</replaceable>]<constant>p</constant><replaceable>bus</replaceable><constant>s</constant><replaceable>slot</replaceable>[<constant>f</constant><replaceable>function</replaceable>][<constant>n</constant><replaceable>phys_port_name</replaceable>|<constant>d</constant><replaceable>dev_port</replaceable>]<constant>u</constant><replaceable>port</replaceable>…[<constant>c</constant><replaceable>config</replaceable>][<constant>i</constant><replaceable>interface</replaceable>]</term>
|
||||
<term><varname>ID_NET_NAME_PATH=</varname><replaceable>prefix</replaceable>[<constant>P</constant><replaceable>domain</replaceable>]<constant>p</constant><replaceable>bus</replaceable><constant>s</constant><replaceable>slot</replaceable>[<constant>f</constant><replaceable>function</replaceable>][<constant>n</constant><replaceable>phys_port_name</replaceable>|<constant>d</constant><replaceable>dev_port</replaceable>]<constant>S</constant><replaceable>sfnum</replaceable></term>
|
||||
|
||||
<listitem><para>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 @@
|
||||
<entry>USB port number chain</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry>… <constant>S</constant><replaceable>sfnum</replaceable></entry>
|
||||
<entry>Auxiliary sub-function (SF) number</entry>
|
||||
</row>
|
||||
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
@@ -575,6 +601,22 @@
|
||||
<xi:include href="version-info.xml" xpointer="v260"/>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><constant>v261</constant></term>
|
||||
|
||||
<listitem><para>Stable names are now generated for auxiliary sub-function (SF) network devices
|
||||
(such as <literal>mlx5_core</literal> SFs). The name is built from the parent PCI Physical Function's
|
||||
path with an <literal>S<replaceable>sfnum</replaceable></literal> suffix, where
|
||||
<replaceable>sfnum</replaceable> is the user-defined SF number (the value passed to
|
||||
<command>devlink port add … sfnum <replaceable>N</replaceable></command>, exposed by the kernel as
|
||||
the <constant>sfnum</constant> sysfs attribute on the auxiliary device). This is analogous to the
|
||||
<literal>v<replaceable>N</replaceable></literal> 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.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v261"/>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
|
||||
<para>Note that <constant>latest</constant> may be used to denote the latest scheme known (to this
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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<sfnum>" 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<sfnum>' 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<N>' 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -711,6 +711,18 @@ static int builtin_path_id(UdevEvent *event, int argc, char *argv[]) {
|
||||
path_prepend(&path, "serio-%s", sysnum);
|
||||
parent = skip_subsystem(parent, "serio");
|
||||
}
|
||||
} else if (device_in_subsystem(parent, "auxiliary") > 0) {
|
||||
unsigned sfnum;
|
||||
|
||||
/* sfnum is the user-defined sub-function number (devlink port add ...
|
||||
* sfnum N). Prepend it so an SF leaf device gets an ID_PATH distinct
|
||||
* from its parent PF/VF; aux devices without 'sfnum' emit no token to
|
||||
* preserve pre-patch ID_PATH values. */
|
||||
if (device_get_sysattr_unsigned(parent, "sfnum", &sfnum) >= 0) {
|
||||
path_prepend(&path, "sf-%u", sfnum);
|
||||
if (compat_path)
|
||||
path_prepend(&compat_path, "sf-%u", sfnum);
|
||||
}
|
||||
} else if (device_in_subsystem(parent, "pci") > 0) {
|
||||
path_prepend(&path, "pci-%s", sysname);
|
||||
if (compat_path)
|
||||
|
||||
Reference in New Issue
Block a user