test/napi-test: turn into a test case

- Have napi-test.t invoke the shell script if no arguments are passed
- Have napi-test.sh test with various queue flags
- Add description
- Remove some unneeded includes
- Have connect retry if we get ECONNREFUSED, probably racing with
  receiver setting up. Timeout after 1 second.
- Register napi instance
- Test for root, can't work as a normal user
- Test for ip and ethtool, and skip if not available

Signed-off-by: Jens Axboe <axboe@kernel.dk>
diff --git a/test/napi-test.c b/test/napi-test.c
index eef2e1f..3512376 100644
--- a/test/napi-test.c
+++ b/test/napi-test.c
@@ -1,46 +1,26 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: run NAPI receive test. Meant to be run from the associated
+ *		script, napi-test.sh. That will invoke this test program
+ *		as either a sender or receiver, with the queue flags passed
+ *		in for testing.
+ *
+ */
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdint.h>
 #include <assert.h>
 #include <errno.h>
-#include <limits.h>
 #include <fcntl.h>
 #include <unistd.h>
-#include <stdbool.h>
-#include <stdarg.h>
 #include <string.h>
-#include <pthread.h>
-
-#include <poll.h>
-#include <sched.h>
 #include <arpa/inet.h>
 #include <linux/if_packet.h>
-#include <linux/ipv6.h>
 #include <linux/socket.h>
-#include <linux/sockios.h>
-#include <net/ethernet.h>
-#include <net/if.h>
-#include <netinet/ip.h>
-#include <netinet/in.h>
-#include <netinet/ip6.h>
-#include <netinet/tcp.h>
-#include <netinet/udp.h>
 #include <sys/socket.h>
-#include <sys/time.h>
-#include <sys/resource.h>
-#include <sys/un.h>
-#include <sys/ioctl.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <sys/time.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <sys/mman.h>
-#include <linux/mman.h>
 
 #include "liburing.h"
-
-#include <assert.h>
+#include "helpers.h"
 
 static const char receiver_address[] = "10.10.10.20";
 static const int port = 9999;
@@ -56,7 +36,7 @@
 	assert(ret == 0);
 }
 
-static void sender(void)
+static int sender(void)
 {
 	unsigned long long written = 0;
 	struct sockaddr_in addr;
@@ -71,14 +51,22 @@
 	fd = socket(PF_INET, SOCK_STREAM, 0);
 	assert(fd >= 0);
 
-	printf("sender: connect\n");
-
-	if (connect(fd, (void *)&addr, sizeof(addr))) {
-		fprintf(stderr, "connect fail %i\n", errno);
-		exit(1);
-	}
-
-	printf("sender: connected\n");
+	/* don't race with receiver, give it 1 second to connect */
+	i = 0;
+	do {
+		ret = connect(fd, (void *)&addr, sizeof(addr));
+		if (!ret)
+			break;
+		if (ret == -1 && errno == ECONNREFUSED) {
+			if (i >= 10000) {
+				fprintf(stderr, "Gave up trying to connect\n");
+				return 1;
+			}
+			usleep(100);
+			continue;
+		}
+		i++;
+	} while (1);
 
 	while (written < 8 * 1024 * 1024) {
 		for (i = 0; i < BUF_SIZE; i++)
@@ -89,32 +77,37 @@
 			if (!ret || errno == ECONNRESET)
 				break;
 			fprintf(stderr, "write failed %i %i\n", ret, errno);
-			exit(1);
+			return 1;
 		}
 		written += ret;
 		current_byte += ret;
 	}
 
 	close(fd);
-	printf("bytes sent %llu\n", written);
+	return 0;
 }
 
-static void receiver(void)
+static int receiver(int queue_flags)
 {
 	struct io_uring_sqe *sqe;
 	struct io_uring_cqe *cqe;
 	struct io_uring ring;
 	unsigned long long received = 0;
+	struct io_uring_napi napi = { };
 	struct sockaddr_in addr;
 	int fd, listen_fd;
 	int i, ret;
 
-	ret = io_uring_queue_init(8, &ring, 0);
+	ret = io_uring_queue_init(8, &ring, queue_flags);
 	if (ret < 0) {
 		fprintf(stderr, "queue_init: %s\n", strerror(-ret));
-		return;
+		return 1;
 	}
 
+	napi.prefer_busy_poll = 1;
+	napi.busy_poll_to = 50;
+	io_uring_register_napi(&ring, &napi);
+
 	memset(&addr, 0, sizeof(addr));
 	addr.sin_family = AF_INET;
 	addr.sin_port = htons(port);
@@ -127,16 +120,12 @@
 	ret = bind(listen_fd, (void *)&addr, sizeof(addr));
 	if (ret) {
 		fprintf(stderr, "bind failed %i %i\n", ret, errno);
-		exit(1);
+		return 1;
 	}
 
-	printf("receiver: listen()\n");
-
 	ret = listen(listen_fd, 8);
 	assert(ret == 0);
 
-	printf("receiver: accept()\n");
-
 	fd = accept(listen_fd, NULL, NULL);
 	assert(fd >= 0);
 
@@ -147,13 +136,13 @@
 		ret = io_uring_submit(&ring);
 		if (ret != 1) {
 			fprintf(stderr, "io_uring_submit: %i\n", ret);
-			return;
+			return 1;
 		}
 
 		ret = io_uring_wait_cqe(&ring, &cqe);
 		if (ret < 0) {
 			fprintf(stderr, "io_uring_wait_cqe: %i\n", ret);
-			return;
+			return 1;
 		}
 
 		ret = cqe->res;
@@ -161,7 +150,7 @@
 			if (!ret)
 				break;
 			fprintf(stderr, "recv failed %i %i\n", ret, errno);
-			exit(1);
+			return 1;
 		}
 
 		for (i = 0; i < ret; i++) {
@@ -170,7 +159,7 @@
 			if (buffer[i] != expected) {
 				fprintf(stderr, "data mismatch: idx %i, %c vs %c\n",
 					i, buffer[i], expected);
-				exit(1);
+				return 1;
 			}
 		}
 
@@ -181,22 +170,37 @@
 
 	close(fd);
 	io_uring_queue_exit(&ring);
-	printf("bytes received %llu\n", received);
+	return 0;
 }
 
 int main(int argc, char **argv)
 {
+	int queue_flags;
 	int is_rx;
 
-	assert(argc == 2);
-	is_rx = strtoul(argv[1], NULL, 0);
+	if (geteuid()) {
+		fprintf(stdout, "NAPI test requires root\n");
+		return T_EXIT_SKIP;
+	}
 
-	printf("%s: start\n", is_rx ? "receiver" : "sender");
+	if (argc == 1)
+		return system("bash napi-test.sh");
+	else if (argc == 2)
+		return T_EXIT_SKIP;
+	else if (argc != 3)
+		return T_EXIT_SKIP;
+
+	if (!strcmp(argv[1], "receive"))
+		is_rx = 1;
+	else if (!strcmp(argv[1], "send"))
+		is_rx = 0;
+	else
+		return T_EXIT_FAIL;
+
+	queue_flags = strtoul(argv[2], NULL, 16);
 
 	if (is_rx)
-		receiver();
-	else
-		sender();
+		return receiver(queue_flags);
 
-	return 0;
+	return sender();
 }
diff --git a/test/napi-test.sh b/test/napi-test.sh
index f7248ed..6fe34d1 100644
--- a/test/napi-test.sh
+++ b/test/napi-test.sh
@@ -1,10 +1,17 @@
 #! /usr/bin/env bash
 
+if [ ! -x "$(command -v ip)" ]; then
+	echo "Need ip installed"
+	exit 77
+fi
+if [ ! -x "$(command -v ethtool)" ]; then
+	echo "Need ethool installed"
+	exit 77
+fi
+
 function clean_namespaces {
 	ip netns del nscl
 	ip netns del nsserv
-	ip link del ptp-serv
-	echo 10
 }
 trap clean_namespaces EXIT
 
@@ -24,5 +31,18 @@
 ip netns exec nsserv ethtool -K ptp-serv generic-receive-offload on
 ip netns exec nsserv ip link set dev ptp-serv up
 
-ip netns exec nsserv ./prog 1 &
-ip netns exec nscl ./prog 0
+# test basic init, defer_taskrun, and sqpoll
+QUEUE_FLAGS="0x0 0x3000 0x2"
+for flags in $QUEUE_FLAGS; do
+	if [ -f "napi-test.t" ]; then
+		NAPI_TEST="./napi-test.t"
+	elif [ -f "test/napi-test.t" ]; then
+		NAPI_TEST="test/napi-test.t"
+	else
+		echo "Can't find napi-test.t"
+		exit 77
+	fi
+	ip netns exec nsserv $NAPI_TEST receive $flags &
+	ip netns exec nscl $NAPI_TEST send $flags
+	wait
+done