git: 802f5e4d6926 - main - ggate: fix test intermittency

From: Alan Somers <asomers_at_FreeBSD.org>
Date: Thu, 15 Oct 2026 12:16:33 UTC
The branch main has been updated by asomers:

URL: https://cgit.FreeBSD.org/src/commit/?id=802f5e4d6926f0be9294d71c6cadcbe872dbddf2

commit 802f5e4d6926f0be9294d71c6cadcbe872dbddf2
Author:     Alan Somers <asomers@FreeBSD.org>
AuthorDate: 2026-10-13 17:40:22 +0000
Commit:     Alan Somers <asomers@FreeBSD.org>
CommitDate: 2026-10-15 12:16:16 +0000

    ggate: fix test intermittency
    
    * ggatel: fix piped output.  Flush stdout before daemonizing, so the
      output can be read when it's in a command pipeline.
    
    * ggated: bind the port _before_ daemonizing, so an invoking script can
      be sure that the port is bound as soon as the command returns.
    
    * tests: allow ggatec or ggatel to automatically allocate a device
      number instead of choosing one in a racy way.
    
    With all of these changes I can no run the tests 1000 times without
    fail.  And in parallel, too.
    
    MFC after:      1 week
    Sponsored by:   ConnectWise
    PR:             298942
    Reported by:    siva
    Reviewed by:    ngie
    Differential Revision: https://reviews.freebsd.org/D60707
---
 sbin/ggate/ggated/ggated.c              | 21 +++++++++------
 sbin/ggate/ggatel/ggatel.c              |  4 ++-
 tests/sys/geom/class/gate/Makefile      |  3 ---
 tests/sys/geom/class/gate/ggate_test.sh | 48 ++++++++++++---------------------
 4 files changed, 33 insertions(+), 43 deletions(-)

diff --git a/sbin/ggate/ggated/ggated.c b/sbin/ggate/ggated/ggated.c
index ef7b61cb13c4..dd6c53c80ccf 100644
--- a/sbin/ggate/ggated/ggated.c
+++ b/sbin/ggate/ggated/ggated.c
@@ -1100,14 +1100,6 @@ main(int argc, char *argv[])
 		err(EXIT_FAILURE, "Cannot open/create pidfile");
 	}
 
-	if (!g_gate_verbose) {
-		/* Run in daemon mode. */
-		if (daemon(0, 0) == -1)
-			g_gate_xlog("Cannot daemonize: %s", strerror(errno));
-	}
-
-	pidfile_write(pfh);
-
 	signal(SIGCHLD, SIG_IGN);
 
 	sfd = socket(AF_INET, SOCK_STREAM, 0);
@@ -1129,6 +1121,19 @@ main(int argc, char *argv[])
 
 	signal(SIGHUP, huphandler);
 
+	if (!g_gate_verbose) {
+		/*
+		 * Run in daemon mode.  Daemonize _after_ opening the socket,
+		 * so consumers may connect as soon as the ggated command
+		 * returns, but _before_ writing the pidfile, because daemon()
+		 * changes the pid.
+		 */
+		if (daemon(0, 0) == -1)
+			g_gate_xlog("Cannot daemonize: %s", strerror(errno));
+	}
+
+	pidfile_write(pfh);
+
 	for (;;) {
 		fromlen = sizeof(from);
 		tmpsfd = accept(sfd, &from, &fromlen);
diff --git a/sbin/ggate/ggatel/ggatel.c b/sbin/ggate/ggatel/ggatel.c
index 1cbb6af28b7e..cafa6a385457 100644
--- a/sbin/ggate/ggatel/ggatel.c
+++ b/sbin/ggate/ggatel/ggatel.c
@@ -191,8 +191,10 @@ g_gatel_create(void)
 	ggioc.gctl_maxcount = 0;
 	strlcpy(ggioc.gctl_info, path, sizeof(ggioc.gctl_info));
 	g_gate_ioctl(G_GATE_CMD_CREATE, &ggioc);
-	if (unit == -1)
+	if (unit == -1) {
 		printf("%s%u\n", G_GATE_PROVIDER_NAME, ggioc.gctl_unit);
+		fflush(stdout);
+	}
 	unit = ggioc.gctl_unit;
 	g_gatel_serve(fd);
 }
diff --git a/tests/sys/geom/class/gate/Makefile b/tests/sys/geom/class/gate/Makefile
index 5ebc1d9304e0..8ecb2e1a3751 100644
--- a/tests/sys/geom/class/gate/Makefile
+++ b/tests/sys/geom/class/gate/Makefile
@@ -4,7 +4,4 @@ TESTSDIR=	${TESTSBASE}/sys/geom/class/${.CURDIR:T}
 
 ATF_TESTS_SH+=	ggate_test
 
-# Tests listen on the same port.
-TEST_METADATA.ggate_test+= is_exclusive=true
-
 .include <bsd.test.mk>
diff --git a/tests/sys/geom/class/gate/ggate_test.sh b/tests/sys/geom/class/gate/ggate_test.sh
index 4cbc5d80ae89..398d5bee6d80 100644
--- a/tests/sys/geom/class/gate/ggate_test.sh
+++ b/tests/sys/geom/class/gate/ggate_test.sh
@@ -16,14 +16,14 @@ ggatec_trim_body()
 	load_ggate
 
 	port=33080
-	us=$(alloc_ggate_dev)
 	work=$(alloc_md)
 	atf_check -e ignore -o ignore dd if=/dev/random of=/dev/$work bs=1m count=1 conv=notrunc
 	echo $CONF >> $PLAINFILES
 	echo "localhost RW /dev/$work" > $CONF
 	atf_check ggated -p $port -F $PIDFILE $CONF
-	atf_check ggatec create -p $port -u $us localhost /dev/$work
-	ggate_dev=/dev/ggate${us}
+	atf_check -o save:ggateu ggatec create -p $port localhost /dev/$work
+	ggate_dev=/dev/`cat ggateu`
+	cat ggateu | sed 's:ggate::' >> ggate.devs
 	wait_for_ggate_device ${ggate_dev}
 
 	# ggatec only supports read or write.
@@ -55,7 +55,6 @@ ggated_body()
 	load_ggate
 
 	port=33081
-	us=$(alloc_ggate_dev)
 	work=$(alloc_md)
 	src=$(alloc_md)
 
@@ -68,16 +67,16 @@ ggated_body()
 	echo "127.0.0.1 RW /dev/$work" > $CONF
 
 	atf_check ggated -p $port -F $PIDFILE $CONF
-	atf_check ggatec create -p $port -u $us 127.0.0.1 /dev/$work
-
-	ggate_dev=/dev/ggate${us}
+	atf_check -o save:ggateu ggatec create -p $port 127.0.0.1 /dev/$work
+	ggate_dev=/dev/`cat ggateu`
+	cat ggateu | sed 's:ggate::' >> ggate.devs
 
 	wait_for_ggate_device ${ggate_dev}
 
 	atf_check -e ignore -o ignore \
 	    dd if=/dev/${src} of=${ggate_dev} bs=1m count=1 conv=notrunc
 
-	checksum /dev/$src /dev/$work
+	checksum /dev/$src /dev/$work $ggate_dev
 }
 
 ggated_cleanup()
@@ -98,22 +97,21 @@ ggatel_file_body()
 {
 	load_ggate
 
-	us=$(alloc_ggate_dev)
 
 	echo src work >> ${PLAINFILES}
 	dd if=/dev/random of=work bs=1m count=1
 	dd if=/dev/random of=src bs=1m count=1
 
-	atf_check ggatel create -u $us work
-
-	ggate_dev=/dev/ggate${us}
+	atf_check -o save:ggateu ggatel create work
+	ggate_dev=/dev/`cat ggateu`
+	cat ggateu | sed 's:ggate::' >> ggate.devs
 
 	wait_for_ggate_device ${ggate_dev}
 
 	atf_check -e ignore -o ignore \
 	    dd if=src of=${ggate_dev} bs=1m count=1 conv=notrunc
 
-	checksum src work
+	checksum src work $ggate_dev
 }
 
 ggatel_file_cleanup()
@@ -134,7 +132,6 @@ ggatel_md_body()
 {
 	load_ggate
 
-	us=$(alloc_ggate_dev)
 	work=$(alloc_md)
 	src=$(alloc_md)
 
@@ -143,16 +140,16 @@ ggatel_md_body()
 	atf_check -e ignore -o ignore \
 	    dd if=/dev/random of=$src bs=1m count=1 conv=notrunc
 
-	atf_check ggatel create -u $us /dev/$work
-
-	ggate_dev=/dev/ggate${us}
+	atf_check -o save:ggateu ggatel create /dev/$work
+	ggate_dev=/dev/`cat ggateu`
+	cat ggateu | sed 's:ggate::' >> ggate.devs
 
 	wait_for_ggate_device ${ggate_dev}
 
 	atf_check -e ignore -o ignore \
 	    dd if=/dev/$src of=${ggate_dev} bs=1m count=1 conv=notrunc
 
-	checksum /dev/$src /dev/$work
+	checksum /dev/$src /dev/$work $ggate_dev
 }
 
 ggatel_md_cleanup()
@@ -168,18 +165,6 @@ atf_init_test_cases()
 	atf_add_test_case ggatec_trim
 }
 
-alloc_ggate_dev()
-{
-	local us
-
-	us=0
-	while [ -c /dev/ggate${us} ]; do
-		: $(( us += 1 ))
-	done
-	echo ${us} > ggate.devs
-	echo ${us}
-}
-
 alloc_md()
 {
 	local md
@@ -195,6 +180,7 @@ checksum()
 	local src work
 	src=$1
 	work=$2
+	ggate_dev=$3
 
 	src_checksum=$(md5 -q $src)
 	work_checksum=$(md5 -q $work)
@@ -203,7 +189,7 @@ checksum()
 		atf_fail "work md5 checksum didn't match"
 	fi
 
-	ggate_checksum=$(md5 -q /dev/ggate${us})
+	ggate_checksum=$(md5 -q $ggate_dev)
 	if [ "$ggate_checksum" != "$src_checksum" ]; then
 		atf_fail "ggate md5 checksum didn't match"
 	fi