[LTP] [PATCH v2] uevent02: Make wait_for_uevents() order-independent

Avinesh Kumar avinesh.kumar@suse.com
Fri Aug 28 17:26:30 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. Track still-unmatched events in a
pending set and match incoming uevents against any of them regardless
of position.

wait_for_uevents() 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   | 39 +++++++++++++++++++++++------
 testcases/kernel/uevents/uevent02.c | 28 +++++++++++++--------
 2 files changed, 48 insertions(+), 19 deletions(-)

diff --git a/testcases/kernel/uevents/uevent.h b/testcases/kernel/uevents/uevent.h
index 1ad092d5ea39..f7e7c671e499 100644
--- a/testcases/kernel/uevents/uevent.h
+++ b/testcases/kernel/uevents/uevent.h
@@ -117,15 +117,34 @@ static inline int open_uevent_netlink(void)
 	return fd;
 }
 
+#define UEVENT_MAX_PENDING 16
+
 /*
  * Reads events from uevent netlink socket until all expected events passed in
- * the uevent array are matched.
+ * the uevent array are matched. 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(int fd, const struct uevent_desc *const uevents[])
 {
-	int i = 0;
+	const struct uevent_desc *pending[UEVENT_MAX_PENDING];
+	int i, cnt = 0, remaining;
+
+	while (uevents[cnt]) {
+		if (cnt >= UEVENT_MAX_PENDING)
+			tst_brk(TBROK, "Too many expected uevents, increase UEVENT_MAX_PENDING");
+		pending[cnt] = uevents[cnt];
+		cnt++;
+	}
+	remaining = cnt;
 
-	while (1) {
+	while (remaining) {
 		int len;
 		char buf[4096];
 
@@ -136,11 +155,15 @@ static inline void wait_for_uevents(int fd, const struct uevent_desc *const ueve
 
 		print_uevent(buf, len);
 
-		if (uevent_match(buf, len, uevents[i])) {
-			tst_res(TPASS, "Got expected UEVENT");
-			if (!uevents[++i]) {
-				close(fd);
-				return;
+		for (i = 0; i < cnt; i++) {
+			if (!pending[i])
+				continue;
+
+			if (uevent_match(buf, len, pending[i])) {
+				tst_res(TPASS, "Got expected UEVENT");
+				pending[i] = NULL;
+				remaining--;
+				break;
 			}
 		}
 	}
diff --git a/testcases/kernel/uevents/uevent02.c b/testcases/kernel/uevents/uevent02.c
index 1135f55a87db..26c0dbd10547 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,8 @@ 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 +116,9 @@ static void verify_uevent(void)
 	if (!pid) {
 		fd = open_uevent_netlink();
 		TST_CHECKPOINT_WAKE(0);
-		wait_for_uevents(fd, uevents);
+		wait_for_uevents(fd, add_uevents);
+		wait_for_uevents(fd, rem_uevents);
+		close(fd);
 		exit(0);
 	}
 
@@ -128,19 +132,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