svn commit: r354419 - head/sys/kern

Gleb Smirnoff glebius at FreeBSD.org
Wed Nov 6 23:45:44 UTC 2019


Author: glebius
Date: Wed Nov  6 23:45:43 2019
New Revision: 354419
URL: https://svnweb.freebsd.org/changeset/base/354419

Log:
  If vm_pager_get_pages_async() returns an error synchronously we leak wired
  and busy pages.  Add code that would carefully cleanups the state in case
  of synchronous error return.  Cover a case when a first I/O went on
  asynchronously, but second or N-th returned error synchronously.
  
  In collaboration with:	chs
  Reviewed by:		jtl, kib

Modified:
  head/sys/kern/kern_sendfile.c

Modified: head/sys/kern/kern_sendfile.c
==============================================================================
--- head/sys/kern/kern_sendfile.c	Wed Nov  6 23:44:38 2019	(r354418)
+++ head/sys/kern/kern_sendfile.c	Wed Nov  6 23:45:43 2019	(r354419)
@@ -269,6 +269,16 @@ sendfile_iodone(void *arg, vm_page_t *pg, int count, i
 	if (!refcount_release(&sfio->nios))
 		return;
 
+	if (__predict_false(sfio->error && sfio->m == NULL)) {
+		/*
+		 * I/O operation failed, but pru_send hadn't been executed -
+		 * nothing had been sent to the socket.  The syscall has
+		 * returned error to the user.
+		 */
+		free(sfio, M_TEMP);
+		return;
+	}
+
 #if defined(KERN_TLS) && defined(INVARIANTS)
 	if ((sfio->m->m_flags & M_EXT) != 0 &&
 	    sfio->m->m_ext.ext_type == EXT_PGS)
@@ -279,7 +289,7 @@ sendfile_iodone(void *arg, vm_page_t *pg, int count, i
 		    ("non-ext_pgs mbuf with TLS session"));
 #endif
 	CURVNET_SET(so->so_vnet);
-	if (sfio->error) {
+	if (__predict_false(sfio->error)) {
 		/*
 		 * I/O operation failed.  The state of data in the socket
 		 * is now inconsistent, and all what we can do is to tear
@@ -414,10 +424,25 @@ sendfile_swapin(vm_object_t obj, struct sf_io *sfio, i
 		rv = vm_pager_get_pages_async(obj, pa + i, count, NULL,
 		    i + count == npages ? &rhpages : NULL,
 		    &sendfile_iodone, sfio);
-		if (rv != VM_PAGER_OK) {
-			for (j = i; j < i + count; j++) {
-				if (pa[j] != bogus_page)
-					vm_page_unwire(pa[j], PQ_INACTIVE);
+		if (__predict_false(rv != VM_PAGER_OK)) {
+			/*
+			 * Perform full pages recovery before returning EIO.
+			 * Pages from 0 to npages are wired.
+			 * Pages from i to npages are also busied.
+			 * Pages from (i + 1) to (i + count - 1) may be
+			 * substituted to bogus page, and not busied.
+			 */
+			for (j = 0; j < npages; j++) {
+				if (j > i && j < i + count - 1 &&
+				    pa[j] == bogus_page)
+					pa[j] = vm_page_lookup(obj,
+					    OFF_TO_IDX(vmoff(j, off)));
+				else if (j >= i)
+					vm_page_xunbusy(pa[j]);
+				KASSERT(pa[j] != NULL && pa[j] != bogus_page,
+				    ("%s: page %p[%d] I/O recovery failure",
+				    __func__, pa, j));
+				vm_page_unwire(pa[j], PQ_INACTIVE);
 			}
 			VM_OBJECT_WUNLOCK(obj);
 			return (EIO);
@@ -806,7 +831,8 @@ retry_space:
 		if (error != 0) {
 			if (vp != NULL)
 				VOP_UNLOCK(vp, 0);
-			free(sfio, M_TEMP);
+			sfio->m = NULL;
+			sendfile_iodone(sfio, NULL, 0, error);
 			goto done;
 		}
 


More information about the svn-src-head mailing list