Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 9 May 2013 16:05:52 +0000 (UTC)
From:      Konstantin Belousov <kib@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r250409 - head/sys/kern
Message-ID:  <201305091605.r49G5q8M032338@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: kib
Date: Thu May  9 16:05:51 2013
New Revision: 250409
URL: http://svnweb.freebsd.org/changeset/base/250409

Log:
  Item 1 in r248830 causes earlier exits from the sendfile(2), before
  all requested data was sent.  The reason is that xfsize <= 0 condition
  must not be tested at all if space == loopbytes.  Otherwise, the done
  is set to 1, and sendfile(2) is aborted too early.
  
  Instead of moving the condition to exiting the inner loop after the
  xfersize check, directly check for the completed transfer before the
  testing of the available space in the socket buffer, and revert item 1
  of r248830.  It is arguably another bug to sleep waiting for socket
  buffer space (or return EAGAIN for non-blocking socket) if all bytes
  are already transferred.
  
  Reported by:	pho
  Discussed with:	scottl, gibbs
  Tested by:	scottl (stable/9 backport), pho

Modified:
  head/sys/kern/uipc_syscalls.c

Modified: head/sys/kern/uipc_syscalls.c
==============================================================================
--- head/sys/kern/uipc_syscalls.c	Thu May  9 15:57:55 2013	(r250408)
+++ head/sys/kern/uipc_syscalls.c	Thu May  9 16:05:51 2013	(r250409)
@@ -1956,6 +1956,17 @@ kern_sendfile(struct thread *td, struct 
 		goto out;
 	vn_lock(vp, LK_SHARED | LK_RETRY);
 	if (vp->v_type == VREG) {
+		bsize = vp->v_mount->mnt_stat.f_iosize;
+		if (uap->nbytes == 0) {
+			error = VOP_GETATTR(vp, &va, td->td_ucred);
+			if (error != 0) {
+				VOP_UNLOCK(vp, 0);
+				obj = NULL;
+				goto out;
+			}
+			rem = va.va_size;
+		} else
+			rem = uap->nbytes;
 		obj = vp->v_object;
 		if (obj != NULL) {
 			/*
@@ -1973,7 +1984,8 @@ kern_sendfile(struct thread *td, struct 
 				obj = NULL;
 			}
 		}
-	}
+	} else
+		bsize = 0;	/* silence gcc */
 	VOP_UNLOCK(vp, 0);
 	if (obj == NULL) {
 		error = EINVAL;
@@ -2065,11 +2077,20 @@ kern_sendfile(struct thread *td, struct 
 	 * The outer loop checks the state and available space of the socket
 	 * and takes care of the overall progress.
 	 */
-	for (off = uap->offset, rem = uap->nbytes; ; ) {
-		struct mbuf *mtail = NULL;
-		int loopbytes = 0;
-		int space = 0;
-		int done = 0;
+	for (off = uap->offset; ; ) {
+		struct mbuf *mtail;
+		int loopbytes;
+		int space;
+		int done;
+
+		if ((uap->nbytes != 0 && uap->nbytes == fsbytes) ||
+		    (uap->nbytes == 0 && va.va_size == fsbytes))
+			break;
+
+		mtail = NULL;
+		loopbytes = 0;
+		space = 0;
+		done = 0;
 
 		/*
 		 * Check the socket state for ongoing connection,
@@ -2141,17 +2162,16 @@ retry_space:
 		if (error != 0)
 			goto done;
 		error = VOP_GETATTR(vp, &va, td->td_ucred);
-		if (error != 0) {
+		if (error != 0 || off >= va.va_size) {
 			VOP_UNLOCK(vp, 0);
 			goto done;
 		}
-		bsize = vp->v_mount->mnt_stat.f_iosize;
 
 		/*
 		 * Loop and construct maximum sized mbuf chain to be bulk
 		 * dumped into socket buffer.
 		 */
-		while (1) {
+		while (space > loopbytes) {
 			vm_pindex_t pindex;
 			vm_offset_t pgoff;
 			struct mbuf *m0;
@@ -2175,15 +2195,6 @@ retry_space:
 			}
 
 			/*
-			 * We've already overfilled the socket.
-			 * Let the outer loop figure out how to handle it.
-			 */
-			if (space <= loopbytes) {
-				done = 0;
-				break;
-			}
-
-			/*
 			 * Attempt to look up the page.  Allocate
 			 * if not found or wait and loop if busy.
 			 */



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201305091605.r49G5q8M032338>