summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSean Anderson <sean.anderson@seco.com>2022-08-09 13:38:26 -0400
committerLennart Poettering <lennart@poettering.net>2022-08-12 11:15:16 +0200
commit65c2ad985a8debdf6d7d11fee5b466f280260f4b (patch)
tree77a832a92d2e15361926f6cc4b7af3ec8be84545
parentd9c6956ee4c10b76b4ed4ae32550e15a300172f3 (diff)
downloadsystemd-65c2ad985a8debdf6d7d11fee5b466f280260f4b.tar.gz
udev: net_id: Use devicetree aliases when available
Devicetree firmware contains an "aliases" node, containing various aliases for devices described by the firmware. For ethernet devices, these are named "ethernet0", "ethernet1", etc. They provide a convenient means of numbering ethernet devices, especially on systems with no other stable number other than the address. In particular, U-Boot already uses these aliases to name its ethernet devices. Previously, there have been attempts (such as [1]) to add support for these aliases to Linux. However, these patches have been rejected because it is the maintainers' view that naming policy be left to userspace. Well, systemd is userspace, so here we are. In terms of implementation, apparently there can be multiple device trees at once. I have decided to dodge this problem for now, and just use /proc/device-tree. If it is desired to support multiple device trees later, then the scheme can be modified to include the device tree's index. For example, /sys/firmware/devicetree/base2/aliases/ethernet3 might be named enb2d3. For the moment we only support "ethernetX" aliases. Future patches might want to also handle "canX" and "wifiX". It is common on boards with only one ethernet device to use an alias of just "ethernet". In this case, the index is an implicit 0. In case the author of the firmware made a mistake, we check to ensure that aliases of "ethernet" and "ethernet0" do not both exist. [1] https://patchwork.kernel.org/project/linux-arm-kernel/patch/1399390594-1409-1-git-send-email-boris.brezillon@free-electrons.com/ Closes: #17625
-rw-r--r--man/systemd.net-naming-scheme.xml37
-rw-r--r--src/shared/netif-naming-scheme.c1
-rw-r--r--src/shared/netif-naming-scheme.h2
-rw-r--r--src/udev/udev-builtin-net_id.c96
4 files changed, 134 insertions, 2 deletions
diff --git a/man/systemd.net-naming-scheme.xml b/man/systemd.net-naming-scheme.xml
index e5a3d7a313..3d5b3326b1 100644
--- a/man/systemd.net-naming-scheme.xml
+++ b/man/systemd.net-naming-scheme.xml
@@ -98,10 +98,36 @@
<variablelist>
<varlistentry>
<term><varname>ID_NET_NAME_ONBOARD=<replaceable>prefix</replaceable><constant>o</constant><replaceable>number</replaceable></varname></term>
+ <term><varname>ID_NET_NAME_ONBOARD=<replaceable>prefix</replaceable><constant>d</constant><replaceable>number</replaceable></varname></term>
<listitem><para>This name is set based on the numeric ordering information given by the firmware
- for on-board devices. The name consists of the prefix, letter <constant>o</constant>, and a number
- specified by the firmware. This is only available for PCI devices.</para>
+ for on-board devices. Different schemes are used depending on the fiemware type, as described in the table below.</para>
+
+ <table>
+ <title>Onboard naming schemes</title>
+
+ <tgroup cols='2'>
+ <thead>
+ <row>
+ <entry>Format</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+
+ <tbody>
+ <row>
+ <entry><replaceable>prefix</replaceable><constant>o</constant><replaceable>number</replaceable></entry>
+ <entry>PCI onboard index</entry>
+ </row>
+
+ <row>
+ <entry><replaceable>prefix</replaceable><constant>d</constant><replaceable>number</replaceable></entry>
+ <entry>Devicetree alias index</entry>
+ </row>
+
+ </tbody>
+ </tgroup>
+ </table>
</listitem>
</varlistentry>
@@ -415,6 +441,13 @@
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><constant>v252</constant></term>
+
+ <listitem><para>Added naming scheme for platform devices with devicetree aliases.</para>
+ </listitem>
+ </varlistentry>
+
</variablelist>
<para>Note that <constant>latest</constant> 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 e95072e048..18748e5376 100644
--- a/src/shared/netif-naming-scheme.c
+++ b/src/shared/netif-naming-scheme.c
@@ -24,6 +24,7 @@ static const NamingScheme naming_schemes[] = {
{ "v249", NAMING_V249 },
{ "v250", NAMING_V250 },
{ "v251", NAMING_V251 },
+ { "v252", NAMING_V252 },
/* … 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 5303348e06..4fa9170969 100644
--- a/src/shared/netif-naming-scheme.h
+++ b/src/shared/netif-naming-scheme.h
@@ -37,6 +37,7 @@ typedef enum NamingSchemeFlags {
NAMING_REPLACE_STRICTLY = 1 << 12, /* Use udev_replace_ifname() for NAME= rule */
NAMING_XEN_VIF = 1 << 13, /* Generate names for Xen netfront devices */
NAMING_BRIDGE_MULTIFUNCTION_SLOT = 1 << 14, /* Use PCI hotplug slot information associated with bridge, but only if PCI device is multifunction */
+ NAMING_DEVICETREE_ALIASES = 1 << 15, /* Generate names from devicetree aliases */
/* And now the masks that combine the features above */
NAMING_V238 = 0,
@@ -49,6 +50,7 @@ typedef enum NamingSchemeFlags {
NAMING_V249 = NAMING_V247 | NAMING_SLOT_FUNCTION_ID | NAMING_16BIT_INDEX | NAMING_REPLACE_STRICTLY,
NAMING_V250 = NAMING_V249 | NAMING_XEN_VIF,
NAMING_V251 = NAMING_V250 | NAMING_BRIDGE_MULTIFUNCTION_SLOT,
+ NAMING_V252 = NAMING_V251 | NAMING_DEVICETREE_ALIASES,
EXTRA_NET_NAMING_SCHEMES
diff --git a/src/udev/udev-builtin-net_id.c b/src/udev/udev-builtin-net_id.c
index 45387b2bdb..ef1f398927 100644
--- a/src/udev/udev-builtin-net_id.c
+++ b/src/udev/udev-builtin-net_id.c
@@ -23,6 +23,7 @@
#include <linux/pci_regs.h>
#include "alloc-util.h"
+#include "chase-symlinks.h"
#include "device-util.h"
#include "dirent-util.h"
#include "fd-util.h"
@@ -52,6 +53,7 @@ typedef enum NetNameType {
NET_XENVIF,
NET_PLATFORM,
NET_NETDEVSIM,
+ NET_DEVICETREE,
} NetNameType;
typedef struct NetNames {
@@ -70,6 +72,7 @@ typedef struct NetNames {
char xen_slot[ALTIFNAMSIZ];
char platform_path[ALTIFNAMSIZ];
char netdevsim_path[ALTIFNAMSIZ];
+ char devicetree_onboard[ALTIFNAMSIZ];
} NetNames;
/* skip intermediate virtio devices */
@@ -600,6 +603,90 @@ static int names_platform(sd_device *dev, NetNames *names, bool test) {
return 0;
}
+static int dev_devicetree_onboard(sd_device *dev, NetNames *names) {
+ const char *alias, *ofnode_path, *ofnode_syspath;
+ _cleanup_free_ char *devicetree_syspath = NULL;
+ _cleanup_(sd_device_unrefp) sd_device *aliases_dev = NULL, *ofnode_dev = NULL;
+ sd_device *parent;
+ int r;
+
+ if (!naming_scheme_has(NAMING_DEVICETREE_ALIASES))
+ return 0;
+
+ /* check if our direct parent has an of_node */
+ r = sd_device_get_parent(dev, &parent);
+ if (r < 0)
+ return r;
+
+ r = sd_device_new_child(&ofnode_dev, parent, "of_node");
+ if (r < 0)
+ return r;
+
+ r = sd_device_get_syspath(ofnode_dev, &ofnode_syspath);
+ if (r < 0)
+ return r;
+
+ r = chase_symlinks("/proc/device-tree", NULL, 0, &devicetree_syspath, NULL);
+ if (r < 0)
+ return r;
+
+ /*
+ * Example paths:
+ * devicetree_syspath = /sys/firmware/devicetree/base
+ * ofnode_syspath = /sys/firmware/devicetree/base/soc/ethernet@deadbeef
+ * ofnode_path = soc/ethernet@deadbeef
+ */
+ ofnode_path = path_startswith(ofnode_syspath, devicetree_syspath);
+ if (!ofnode_path)
+ return -ENOENT;
+
+ /* Get back our leading / to match the contents of the aliases */
+ ofnode_path--;
+ assert(path_is_absolute(ofnode_path));
+
+ r = sd_device_new_from_syspath(&aliases_dev, strjoina(devicetree_syspath, "/aliases"));
+ if (r < 0)
+ return r;
+
+ FOREACH_DEVICE_SYSATTR(aliases_dev, alias) {
+ const char *alias_path, *alias_index, *conflict;
+ unsigned i;
+
+ alias_index = startswith(alias, "ethernet");
+ if (!alias_index)
+ continue;
+
+ if (sd_device_get_sysattr_value(aliases_dev, alias, &alias_path) < 0)
+ continue;
+
+ if (!path_equal(ofnode_path, alias_path))
+ continue;
+
+ /* If there's no index, we default to 0... */
+ if (isempty(alias_index)) {
+ i = 0;
+ conflict = "ethernet0";
+ } else {
+ r = safe_atou(alias_index, &i);
+ if (r < 0)
+ return log_device_debug_errno(dev, r,
+ "Could not get index of alias %s: %m", alias);
+ conflict = "ethernet";
+ }
+
+ /* ...but make sure we don't have an alias conflict */
+ if (i == 0 && sd_device_get_sysattr_value(aliases_dev, conflict, NULL) >= 0)
+ return log_device_debug_errno(dev, SYNTHETIC_ERRNO(EEXIST),
+ "Ethernet alias conflict: ethernet and ethernet0 both exist");
+
+ xsprintf(names->devicetree_onboard, "d%u", i);
+ names->type = NET_DEVICETREE;
+ return 0;
+ }
+
+ return -ENOENT;
+}
+
static int names_pci(sd_device *dev, const LinkInfo *info, NetNames *names) {
_cleanup_(sd_device_unrefp) sd_device *physfn_pcidev = NULL;
_cleanup_free_ char *virtfn_suffix = NULL;
@@ -1036,6 +1123,15 @@ static int builtin_net_id(sd_device *dev, sd_netlink **rtnl, int argc, char *arg
ieee_oui(dev, &info, test);
}
+ /* get devicetree aliases; only ethernet supported for now */
+ if (streq(prefix, "en") && dev_devicetree_onboard(dev, &names) >= 0 &&
+ names.type == NET_DEVICETREE) {
+ char str[ALTIFNAMSIZ];
+
+ if (snprintf_ok(str, sizeof str, "%s%s", prefix, names.devicetree_onboard))
+ udev_builtin_add_property(dev, test, "ID_NET_NAME_ONBOARD", str);
+ }
+
/* get path names for Linux on System z network devices */
if (names_ccw(dev, &names) >= 0 && names.type == NET_CCW) {
char str[ALTIFNAMSIZ];