[LTP] [PATCH v4] uevents: introduce wait_for_uevents_unordered()

Avinesh Kumar avinesh.kumar@suse.com
Tue Sep 15 13:37:31 CEST 2026


From: Avinesh Kumar <avinesh.kumar@suse.com>

Commit 8e63c9e6179a ("net: Defer netdev KOBJ_ADD uevent until the
device is published")[0] moved a network interface's "add" uevent to
after its queue kobjects are created, flipping the uevent order for
tun device creation from:

    add(net), add(rx queue), add(tx queue)
to:
    add(rx queue), add(tx queue), add(net)

wait_for_uevents() matches events strictly in array order, so on
kernels with the new order "add(net)" is still awaited after its queue
events have already been consumed off the socket, and uevent02 hangs
until it times out and fails.

The relative order of these uevents is a kernel implementation detail
the test shouldn't depend on, but other callers rely on
wait_for_uevents() enforcing order between related events, so add a
separate wait_for_uevents_unordered() instead of relaxing it there:
it tracks still-unmatched events and matches incoming uevents against
any of them regardless of position.

wait_for_uevents_unordered() also leaves fd open so it can be called
more than once on the same socket, keeping independent lifecycle
phases ordered: uevent02 matches all add events before looking for the
remove events.

[0] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=8e63c9e6179a

Signed-off-by: Avinesh Kumar <avinesh.kumar@suse.com>
---
 testcases/kernel/uevents/uevent.h   | 54 ++++++++++++++++++++++++++++-
 testcases/kernel/uevents/uevent02.c | 29 ++++++++++------
 2 files changed, 71 insertions(+), 12 deletions(-)

diff --git a/testcases/kernel/uevents/uevent.h b/testcases/kernel/uevents/uevent.h
index 1ad092d5ea39..dee63d20f0af 100644
--- a/testcases/kernel/uevents/uevent.h
+++ b/testcases/kernel/uevents/uevent.h
@@ -6,6 +6,8 @@
 #ifndef UEVENT_H__
 #define UEVENT_H__
 
+#include <stdint.h>
+#include <string.h>
 #include "tst_netlink.h"
 
 /*
@@ -119,7 +121,7 @@ static inline int open_uevent_netlink(void)
 
 /*
  * Reads events from uevent netlink socket until all expected events passed in
- * the uevent array are matched.
+ * the uevent array are matched, in the exact order they are listed.
  */
 static inline void wait_for_uevents(int fd, const struct uevent_desc *const uevents[])
 {
@@ -146,6 +148,56 @@ static inline void wait_for_uevents(int fd, const struct uevent_desc *const ueve
 	}
 }
 
+/*
+ * Reads events from uevent netlink socket until all expected events passed in
+ * the uevent array are matched. Unlike wait_for_uevents(), events do not have
+ * to arrive in the order they are listed in the uevents array, since the kernel
+ * is free to reorder unrelated uevents, e.g. commit 8e63c9e6179a ("net: Defer
+ * netdev KOBJ_ADD uevent until the device is published") changed the order in
+ * which a network interface and its queues announce themselves.
+ *
+ * Does not close fd; the caller owns the socket and can call this more
+ * than once on it to keep independent lifecycle phases ordered, e.g.
+ * matching all "add" events before looking for "remove" events.
+ */
+static inline void wait_for_uevents_unordered(int fd, const struct uevent_desc *const uevents[])
+{
+	int i, cnt = 0, remaining;
+
+	while (uevents[cnt])
+		cnt++;
+
+	uint8_t matched[cnt];
+
+	memset(matched, 0, sizeof(matched));
+
+	remaining = cnt;
+
+	while (remaining) {
+		int len;
+		char buf[4096];
+
+		len = recv(fd, &buf, sizeof(buf), 0);
+
+		if (len == 0)
+			continue;
+
+		print_uevent(buf, len);
+
+		for (i = 0; i < cnt; i++) {
+			if (matched[i])
+				continue;
+
+			if (uevent_match(buf, len, uevents[i])) {
+				tst_res(TPASS, "Got expected UEVENT");
+				matched[i] = 1;
+				remaining--;
+				break;
+			}
+		}
+	}
+}
+
 /*
  * Waits 5 seconds for a child to exit, kills the child after a timeout.
  */
diff --git a/testcases/kernel/uevents/uevent02.c b/testcases/kernel/uevents/uevent02.c
index 1135f55a87db..f3d2ebc87a74 100644
--- a/testcases/kernel/uevents/uevent02.c
+++ b/testcases/kernel/uevents/uevent02.c
@@ -25,7 +25,8 @@
 #include "uevent.h"
 
 #define TUN_PATH "/dev/net/tun"
-#define MAX_UEVENTS 7
+#define MAX_ADD_UEVENTS 4
+#define MAX_REM_UEVENTS 4
 
 static struct uevent_desc add = {
 	.msg = "add@/devices/virtual/net/ltp-tun0",
@@ -88,7 +89,9 @@ static struct uevent_desc rem = {
 		"INTERFACE=ltp-tun0",
 	}
 };
-static const struct uevent_desc *uevents[MAX_UEVENTS];
+
+static const struct uevent_desc *add_uevents[MAX_ADD_UEVENTS];
+static const struct uevent_desc *rem_uevents[MAX_REM_UEVENTS];
 
 static void generate_tun_uevents(void)
 {
@@ -114,7 +117,9 @@ static void verify_uevent(void)
 	if (!pid) {
 		fd = open_uevent_netlink();
 		TST_CHECKPOINT_WAKE(0);
-		wait_for_uevents(fd, uevents);
+		wait_for_uevents_unordered(fd, add_uevents);
+		wait_for_uevents_unordered(fd, rem_uevents);
+		close(fd);
 		exit(0);
 	}
 
@@ -128,19 +133,21 @@ static void verify_uevent(void)
 static void setup(void)
 {
 	struct tst_kconfig_var kconfig = TST_KCONFIG_INIT("CONFIG_RPS");
-	int i = 0;
+	int i = 0, j = 0;
 
 	tst_kconfig_read(&kconfig, 1);
 
-	uevents[i++] = &add;
+	add_uevents[i++] = &add;
 	if (kconfig.choice == 'y')
-		uevents[i++] = &add_rx;
-	uevents[i++] = &add_tx;
+		add_uevents[i++] = &add_rx;
+	add_uevents[i++] = &add_tx;
+	add_uevents[i++] = NULL;
+
 	if (kconfig.choice == 'y')
-		uevents[i++] = &rem_rx;
-	uevents[i++] = &rem_tx;
-	uevents[i++] = &rem;
-	uevents[i++] = NULL;
+		rem_uevents[j++] = &rem_rx;
+	rem_uevents[j++] = &rem_tx;
+	rem_uevents[j++] = &rem;
+	rem_uevents[j++] = NULL;
 }
 
 static struct tst_test test = {
-- 
2.55.0



More information about the ltp mailing list