fstests: skip AIO-related tests when CONFIG_AIO=n

When running xfstests on a kernel configured with CONFIG_AIO=n, all
AIO-related tests fail, often due to an error similar to the
following:

    error Function not implemented during io_setup

This affected at least the following tests: generic/036,
generic/112, generic/113, generic/198, generic/207, generic/208,
generic/210, generic/211, generic/239, generic/323, generic/427,
xfs/240, xfs/241.

Fix this by enhancing the 'feature' program to allow testing for
asynchronous I/O support, then skipping all AIO-related tests when
AIO is unsupported.

This change is useful because CONFIG_AIO is sometimes disabled to
reduce the kernel's attack surface (e.g. see
https://android-review.googlesource.com/#/c/292158/).

Signed-off-by: Eric Biggers <ebiggers@google.com>
Reviewed-by: Eryu Guan <eguan@redhat.com>
Signed-off-by: Eryu Guan <eguan@redhat.com>
diff --git a/common/rc b/common/rc
index a7af1f2..ba21596 100644
--- a/common/rc
+++ b/common/rc
@@ -1868,7 +1868,24 @@
 	_notrun "External device testing in progress, skipped this test"
 }
 
+# this test requires that the kernel supports asynchronous I/O
+_require_aio()
+{
+	$here/src/feature -A
+	case $? in
+	0)
+		;;
+	1)
+		_notrun "kernel does not support asynchronous I/O"
+		;;
+	*)
+		_fail "unexpected error testing for asynchronous I/O support"
+		;;
+	esac
+}
+
 # this test requires that a (specified) aio-dio executable exists
+# and that the kernel supports asynchronous I/O.
 # $1 - command (optional)
 #
 _require_aiodio()
@@ -1881,6 +1898,7 @@
         AIO_TEST=src/aio-dio-regress/$1
         [ -x $AIO_TEST ] || _notrun "$AIO_TEST not built"
     fi
+    _require_aio
     _require_odirect
 }
 
diff --git a/src/Makefile b/src/Makefile
index b505b42..4724662 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -60,6 +60,7 @@
 
 ifeq ($(HAVE_AIO), true)
 SUBDIRS += aio-dio-regress
+LLDLIBS += -laio
 endif
 
 ifeq ($(HAVE_SSL), true)
diff --git a/src/feature.c b/src/feature.c
index 334063b..b735468 100644
--- a/src/feature.c
+++ b/src/feature.c
@@ -30,6 +30,7 @@
  * Return code: 0 is true, anything else is error/not supported
  *
  * Test for machine features
+ *   -A  test whether AIO syscalls are available
  *   -o  report a number of online cpus
  *   -s  report pagesize
  *   -w  report bits per long
@@ -46,6 +47,10 @@
 #include <xfs/xqm.h>
 #endif
 
+#ifdef HAVE_LIBAIO_H
+#include <libaio.h>
+#endif
+
 #ifndef USRQUOTA
 #define USRQUOTA  0
 #endif
@@ -66,7 +71,7 @@
 	fprintf(stderr, "Usage: feature [-v] -<q|u|g|p|U|G|P> <filesystem>\n");
 	fprintf(stderr, "       feature [-v] -c <file>\n");
 	fprintf(stderr, "       feature [-v] -t <file>\n");
-	fprintf(stderr, "       feature -o | -s | -w\n");
+	fprintf(stderr, "       feature -A | -o | -s | -w\n");
 	exit(1);
 }
 
@@ -199,10 +204,35 @@
 	return (1);
 }
 
+static int
+check_aio_support(void)
+{
+#ifdef HAVE_LIBAIO_H
+	struct io_context *ctx = NULL;
+	int err;
+
+	err = io_setup(1, &ctx);
+	if (err == 0)
+		return 0;
+
+	if (err == -ENOSYS) /* CONFIG_AIO=n */
+		return 1;
+
+	fprintf(stderr, "unexpected error from io_setup(): %s\n",
+		strerror(-err));
+	return 2;
+#else
+	/* libaio was unavailable at build time; assume AIO is unsupported */
+	return 1;
+#endif
+}
+
+
 int
 main(int argc, char **argv)
 {
 	int	c;
+	int	Aflag = 0;
 	int	cflag = 0;
 	int	tflag = 0;
 	int	gflag = 0;
@@ -217,8 +247,11 @@
 	int	oflag = 0;
 	char	*fs = NULL;
 
-	while ((c = getopt(argc, argv, "ctgGopPqsuUvw")) != EOF) {
+	while ((c = getopt(argc, argv, "ActgGopPqsuUvw")) != EOF) {
 		switch (c) {
+		case 'A':
+			Aflag++;
+			break;
 		case 'c':
 			cflag++;
 			break;
@@ -268,7 +301,7 @@
 		if (optind != argc-1)	/* need a device */
 			usage();
 		fs = argv[argc-1];
-	} else if (wflag || sflag || oflag) {
+	} else if (Aflag || wflag || sflag || oflag) {
 		if (optind != argc)
 			usage();
 	} else 
@@ -293,6 +326,9 @@
 	if (Uflag)
 		return(hasxfsquota(USRQUOTA, XFS_QUOTA_UDQ_ACCT, fs));
 
+	if (Aflag)
+		return(check_aio_support());
+
 	if (sflag) {
 		printf("%d\n", getpagesize());
 		exit(0);
diff --git a/tests/generic/112 b/tests/generic/112
index 55be394..0ab1c8c 100755
--- a/tests/generic/112
+++ b/tests/generic/112
@@ -122,6 +122,7 @@
 _supported_fs generic
 _supported_os Linux
 _require_test
+_require_aio
 
 [ -x $here/ltp/aio-stress ] || \
 	_notrun "fsx not built with AIO for this platform"
diff --git a/tests/generic/113 b/tests/generic/113
index 54d6191..7e20d68 100755
--- a/tests/generic/113
+++ b/tests/generic/113
@@ -77,6 +77,7 @@
 _supported_fs generic
 _supported_os Linux
 _require_test
+_require_aio
 _require_odirect
 
 [ -x $here/ltp/aio-stress ] || _notrun "aio-stress not built for this platform"
diff --git a/tests/xfs/240 b/tests/xfs/240
index 62a7735..fa0edc7 100755
--- a/tests/xfs/240
+++ b/tests/xfs/240
@@ -49,9 +49,8 @@
 _require_cp_reflink
 _require_dm_target error
 _require_xfs_io_command "cowextsize"
-_require_test_program "aio-dio-regress/aiocp"
+_require_aiodio "aiocp"
 AIO_TEST="src/aio-dio-regress/aiocp"
-_require_odirect
 
 rm -f $seqres.full
 
diff --git a/tests/xfs/241 b/tests/xfs/241
index 5b29f4d..b309673 100755
--- a/tests/xfs/241
+++ b/tests/xfs/241
@@ -46,9 +46,8 @@
 _require_scratch_reflink
 _require_cp_reflink
 _require_xfs_io_command "cowextsize"
-_require_test_program "aio-dio-regress/aiocp"
+_require_aiodio "aiocp"
 AIO_TEST="src/aio-dio-regress/aiocp"
-_require_odirect
 
 rm -f $seqres.full