svn commit: r368618 - in projects/aio_writev: sys/kern tests/sys/aio

Alan Somers asomers at FreeBSD.org
Sun Dec 13 21:32:20 UTC 2020


Author: asomers
Date: Sun Dec 13 21:32:20 2020
New Revision: 368618
URL: https://svnweb.freebsd.org/changeset/base/368618

Log:
  aio_writev: fall back to slow path if iovec is not sectorsize-aligned
  
  However, for most disk-like devices the I/O will eventually fail anyway.
  physio has the same constraints as aio_qbio.  I think it might work for
  zols, though; I need to add a test for that.
  
  This commit also adds a few more test cases.
  
  This commit add some dtrace probes, too, though I might remove those later.

Modified:
  projects/aio_writev/sys/kern/vfs_aio.c
  projects/aio_writev/tests/sys/aio/aio_test.c

Modified: projects/aio_writev/sys/kern/vfs_aio.c
==============================================================================
--- projects/aio_writev/sys/kern/vfs_aio.c	Sun Dec 13 21:32:19 2020	(r368617)
+++ projects/aio_writev/sys/kern/vfs_aio.c	Sun Dec 13 21:32:20 2020	(r368618)
@@ -45,6 +45,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/proc.h>
 #include <sys/resourcevar.h>
 #include <sys/signalvar.h>
+#include <sys/sdt.h>
 #include <sys/syscallsubr.h>
 #include <sys/protosw.h>
 #include <sys/rwlock.h>
@@ -74,6 +75,8 @@ __FBSDID("$FreeBSD$");
 #include <vm/uma.h>
 #include <sys/aio.h>
 
+SDT_PROVIDER_DEFINE(aio);
+
 /*
  * Counter for allocating reference ids to new jobs.  Wrapped to 1 on
  * overflow. (XXX will be removed soon.)
@@ -1218,6 +1221,8 @@ aio_newproc(int *start)
  * structure's reference count, preventing its deallocation for the
  * duration of this call.
  */
+SDT_PROBE_DEFINE2(aio, aio, aio_qbio, entry, "struct proc*", "struct kaiocb*");
+SDT_PROBE_DEFINE2(aio, aio, aio_qbio, dev, "struct kaiocb*", "struct cdev*");
 static int
 aio_qbio(struct proc *p, struct kaiocb *job)
 {
@@ -1239,6 +1244,8 @@ aio_qbio(struct proc *p, struct kaiocb *job)
 	fp = job->fd_file;
 	opcode = cb->aio_lio_opcode;
 
+	SDT_PROBE2(aio, aio, aio_qbio, entry, p, job);
+
 	if (!(opcode == LIO_WRITE ||
 	    opcode == LIO_WRITEV ||
 	    opcode == LIO_READ))
@@ -1262,24 +1269,33 @@ aio_qbio(struct proc *p, struct kaiocb *job)
 		error = copyinuio(cb->aio_iov, cb->aio_iovcnt, &auiop);
 		if (error)
 			return (error);
+		for (i = 0; i < iovcnt; i++) {
+			if (auiop->uio_iov[i].iov_len % vp->v_bufobj.bo_bsize) {
+				// TODO: are there any disk-like devices that
+				// would balk here but would work with
+				// aio_process_rw?  I don't know of any.  With
+				// md, at least, aio_process_rw calls physio,
+				// which has this same problem.
+				error = -1;
+				goto free_uio;
+			}
+		}
 		nbytes = auiop->uio_resid;
 	} else {
 		nbytes = cb->aio_nbytes;
+		if (nbytes % vp->v_bufobj.bo_bsize)
+			return (-1);
 		iovcnt = 1;
 	}
 	offset = cb->aio_offset;
 
-	if (nbytes % vp->v_bufobj.bo_bsize) {
-		error = -1;
-		goto free_uio;
-	}
-
 	ref = 0;
 	csw = devvn_refthread(vp, &dev, &ref);
 	if (csw == NULL) {
 		error = ENXIO;
 		goto free_uio;
 	}
+	SDT_PROBE2(aio, aio, aio_qbio, dev, job, dev);
 
 	if ((csw->d_flags & D_DISK) == 0) {
 		error = -1;

Modified: projects/aio_writev/tests/sys/aio/aio_test.c
==============================================================================
--- projects/aio_writev/tests/sys/aio/aio_test.c	Sun Dec 13 21:32:19 2020	(r368617)
+++ projects/aio_writev/tests/sys/aio/aio_test.c	Sun Dec 13 21:32:20 2020	(r368618)
@@ -678,40 +678,11 @@ ATF_TC_BODY(pipe_waitcomplete, tc)
 #define	MD_LEN		GLOBAL_MAX
 #define	MDUNIT_LINK	"mdunit_link"
 
-static void
-aio_md_cleanup(void)
+static int
+aio_md_setup(void)
 {
-	struct md_ioctl mdio;
-	int mdctl_fd, error, n, unit;
-	char buf[80];
-
-	mdctl_fd = open("/dev/" MDCTL_NAME, O_RDWR, 0);
-	ATF_REQUIRE(mdctl_fd >= 0);
-	n = readlink(MDUNIT_LINK, buf, sizeof(buf));
-	if (n > 0) {
-		if (sscanf(buf, "%d", &unit) == 1 && unit >= 0) {
-			bzero(&mdio, sizeof(mdio));
-			mdio.md_version = MDIOVERSION;
-			mdio.md_unit = unit;
-			if (ioctl(mdctl_fd, MDIOCDETACH, &mdio) == -1) {
-				error = errno;
-				close(mdctl_fd);
-				errno = error;
-				atf_tc_fail("ioctl MDIOCDETACH failed: %s",
-				    strerror(errno));
-			}
-		}
-	}
-		
-	close(mdctl_fd);
-}
-
-static void
-aio_md_test(completion comp, struct sigevent *sev, bool vectored)
-{
 	int error, fd, mdctl_fd, unit;
 	char pathname[PATH_MAX];
-	struct aio_context ac;
 	struct md_ioctl mdio;
 	char buf[80];
 
@@ -743,7 +714,45 @@ aio_md_test(completion comp, struct sigevent *sev, boo
 	fd = open(pathname, O_RDWR);
 	ATF_REQUIRE_MSG(fd != -1,
 	    "opening %s failed: %s", pathname, strerror(errno));
+	
+	return (fd);
+}
 
+static void
+aio_md_cleanup(void)
+{
+	struct md_ioctl mdio;
+	int mdctl_fd, error, n, unit;
+	char buf[80];
+
+	mdctl_fd = open("/dev/" MDCTL_NAME, O_RDWR, 0);
+	ATF_REQUIRE(mdctl_fd >= 0);
+	n = readlink(MDUNIT_LINK, buf, sizeof(buf));
+	if (n > 0) {
+		if (sscanf(buf, "%d", &unit) == 1 && unit >= 0) {
+			bzero(&mdio, sizeof(mdio));
+			mdio.md_version = MDIOVERSION;
+			mdio.md_unit = unit;
+			if (ioctl(mdctl_fd, MDIOCDETACH, &mdio) == -1) {
+				error = errno;
+				close(mdctl_fd);
+				errno = error;
+				atf_tc_fail("ioctl MDIOCDETACH failed: %s",
+				    strerror(errno));
+			}
+		}
+	}
+		
+	close(mdctl_fd);
+}
+
+static void
+aio_md_test(completion comp, struct sigevent *sev, bool vectored)
+{
+	struct aio_context ac;
+	int fd;
+
+	fd = aio_md_setup();
 	aio_context_init(&ac, fd, fd, MD_LEN);
 	if (vectored)
 		aio_writev_test(&ac, comp, sev);
@@ -1283,11 +1292,10 @@ ATF_TC_BODY(aio_writev_dos_iovcnt, tc)
 	close(fd);
 }
 
-ATF_TC_WITHOUT_HEAD(aio_writev_empty);
-ATF_TC_BODY(aio_writev_empty, tc)
+ATF_TC_WITHOUT_HEAD(aio_writev_empty_file_poll);
+ATF_TC_BODY(aio_writev_empty_file_poll, tc)
 {
 	struct aiocb aio;
-	const struct aiocb *const iocbs[] = {&aio};
 	int fd;
 
 	ATF_REQUIRE_KERNEL_MODULE("aio");
@@ -1302,13 +1310,98 @@ ATF_TC_BODY(aio_writev_empty, tc)
 	aio.aio_iovcnt = 0;
 
 	ATF_REQUIRE_EQ(0, aio_writev(&aio));
+	ATF_REQUIRE_EQ(0, suspend(&aio));
 
-	ATF_REQUIRE_EQ(0, aio_suspend(iocbs, 1, NULL));
-	ATF_REQUIRE_EQ(0, aio_return(&aio));
+	close(fd);
+}
 
+ATF_TC_WITHOUT_HEAD(aio_writev_empty_file_signal);
+ATF_TC_BODY(aio_writev_empty_file_signal, tc)
+{
+	struct aiocb aio;
+	int fd;
+
+	ATF_REQUIRE_KERNEL_MODULE("aio");
+	ATF_REQUIRE_UNSAFE_AIO();
+
+	fd = open("testfile", O_RDWR | O_CREAT, 0600);
+	ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno));
+
+	bzero(&aio, sizeof(aio));
+	aio.aio_fildes = fd;
+	aio.aio_offset = 0;
+	aio.aio_iovcnt = 0;
+	aio.aio_sigevent = *setup_signal();
+
+	ATF_REQUIRE_EQ(0, aio_writev(&aio));
+	ATF_REQUIRE_EQ(0, poll_signaled(&aio));
+
 	close(fd);
 }
 
+// aio_writev and aio_readv should still work even if the iovcnt is greater
+// than the number of buffered AIO operations permitted per process.
+ATF_TC_WITH_CLEANUP(vectored_big_iovcnt);
+ATF_TC_HEAD(vectored_big_iovcnt, tc)
+{
+	atf_tc_set_md_var(tc, "descr",
+	    "Vectored AIO should still work even if the iovcnt is greater than "
+	    "the number of buffered AIO operations permitted by the process");
+	atf_tc_set_md_var(tc, "require.user", "root");
+}
+ATF_TC_BODY(vectored_big_iovcnt, tc)
+{
+	struct aiocb aio;
+	struct iovec *iov;
+	ssize_t len, buflen;
+	char *buffer;
+	const char *oid = "vfs.aio.max_buf_aio";
+	long seed;
+	int max_buf_aio;
+	int fd, i;
+	ssize_t sysctl_len = sizeof(max_buf_aio);
+
+	ATF_REQUIRE_KERNEL_MODULE("aio");
+	ATF_REQUIRE_UNSAFE_AIO();
+
+	if (sysctlbyname(oid, &max_buf_aio, &sysctl_len, NULL, 0) == -1)
+		atf_libc_error(errno, "Failed to read %s", oid);
+
+	seed = random();
+	buflen = 512 * (max_buf_aio + 1);
+	buffer = malloc(buflen);
+	aio_fill_buffer(buffer, buflen, seed);
+	iov = calloc(max_buf_aio + 1, sizeof(struct iovec));
+
+	fd = aio_md_setup();
+
+	bzero(&aio, sizeof(aio));
+	aio.aio_fildes = fd;
+	aio.aio_offset = 0;
+	for (i = 0; i < max_buf_aio + 1; i++) {
+		iov[i].iov_base = &buffer[i * 512];
+		iov[i].iov_len = 512;
+	}
+	aio.aio_iov = iov;
+	aio.aio_iovcnt = max_buf_aio + 1;
+
+	if (aio_writev(&aio) < 0)
+		atf_tc_fail("aio_writev failed: %s", strerror(errno));
+
+	len = poll(&aio);
+	if (len < 0)
+		atf_tc_fail("aio failed: %s", strerror(errno));
+
+	if (len != buflen)
+		atf_tc_fail("aio short write (%jd)", (intmax_t)len);
+	// TODO: aio_readv
+	close(fd);
+}
+ATF_TC_CLEANUP(vectored_big_iovcnt, tc)
+{
+	aio_md_cleanup();
+}
+
 ATF_TC_WITHOUT_HEAD(vectored_file_poll);
 ATF_TC_BODY(vectored_file_poll, tc)
 {
@@ -1335,6 +1428,67 @@ ATF_TC_BODY(vectored_socket_poll, tc)
 	aio_unix_socketpair_test(poll, NULL, true);
 }
 
+// aio_writev and aio_readv should still work even if the iov contains elements
+// that aren't a multiple of the device's sector size, and even if the total
+// amount if I/O _is_ a multiple of the device's sector size.
+ATF_TC_WITH_CLEANUP(vectored_unaligned);
+ATF_TC_HEAD(vectored_unaligned, tc)
+{
+	atf_tc_set_md_var(tc, "descr",
+	    "Vectored AIO should still work even if the iov contains elements "
+	    "that aren't a multiple of the sector size.");
+	atf_tc_set_md_var(tc, "require.user", "root");
+}
+ATF_TC_BODY(vectored_unaligned, tc)
+{
+	struct aio_context ac;
+	struct aiocb aio;
+	struct iovec iov[3];
+	ssize_t len, total_len;
+	int fd;
+
+	ATF_REQUIRE_KERNEL_MODULE("aio");
+	ATF_REQUIRE_UNSAFE_AIO();
+
+	fd = aio_md_setup();
+	aio_context_init(&ac, fd, fd, FILE_LEN);
+
+	/* Break the buffer into 3 parts:
+	 * * A 4kB part, aligned to 4kB
+	 * * Two other parts that add up to 4kB:
+	 *   - 256B
+	 *   - 4kB - 256B
+	 */
+	iov[0].iov_base = ac.ac_buffer;
+	iov[0].iov_len = 4096;
+	iov[1].iov_base = (void*)((uintptr_t)iov[0].iov_base + iov[0].iov_len);
+	iov[1].iov_len = 256;
+	iov[2].iov_base = (void*)((uintptr_t)iov[1].iov_base + iov[1].iov_len);
+	iov[2].iov_len = 4096 - iov[1].iov_len;
+	total_len = iov[0].iov_len + iov[1].iov_len + iov[2].iov_len;
+	bzero(&aio, sizeof(aio));
+	aio.aio_fildes = ac.ac_write_fd;
+	aio.aio_offset = 0;
+	aio.aio_iov = iov;
+	aio.aio_iovcnt = 3;
+
+	if (aio_writev(&aio) < 0)
+		atf_tc_fail("aio_writev failed: %s", strerror(errno));
+
+	len = poll(&aio);
+	if (len < 0)
+		atf_tc_fail("aio failed: %s", strerror(errno));
+
+	if (len != total_len)
+		atf_tc_fail("aio short write (%jd)", (intmax_t)len);
+	// TODO: aio_readv
+	close(fd);
+}
+ATF_TC_CLEANUP(vectored_unaligned, tc)
+{
+	aio_md_cleanup();
+}
+
 ATF_TP_ADD_TCS(tp)
 {
 
@@ -1376,13 +1530,12 @@ ATF_TP_ADD_TCS(tp)
 	ATF_TP_ADD_TC(tp, aio_socket_short_write_cancel);
 	ATF_TP_ADD_TC(tp, aio_writev_dos_iov_len);
 	ATF_TP_ADD_TC(tp, aio_writev_dos_iovcnt);
-	ATF_TP_ADD_TC(tp, aio_writev_empty);
+	ATF_TP_ADD_TC(tp, aio_writev_empty_file_poll);
+	ATF_TP_ADD_TC(tp, aio_writev_empty_file_signal);
+	ATF_TP_ADD_TC(tp, vectored_big_iovcnt);
 	ATF_TP_ADD_TC(tp, vectored_file_poll);
-	/* 
-	 * TODO: add a test for vectored I/O to a md, where the individual
-	 * iovec elements are not sector-aligned
-	 */
 	ATF_TP_ADD_TC(tp, vectored_md_poll);
+	ATF_TP_ADD_TC(tp, vectored_unaligned);
 	ATF_TP_ADD_TC(tp, vectored_socket_poll);
 
 	return (atf_no_error());


More information about the svn-src-projects mailing list