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
This commit is contained in:
Konstantin Belousov 2013-05-09 16:05:51 +00:00
parent caedab2c56
commit 3328431dec
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=250409

View File

@ -1956,6 +1956,17 @@ kern_sendfile(struct thread *td, struct sendfile_args *uap,
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 sendfile_args *uap,
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 sendfile_args *uap,
* 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 @@ kern_sendfile(struct thread *td, struct sendfile_args *uap,
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;
@ -2174,15 +2194,6 @@ kern_sendfile(struct thread *td, struct sendfile_args *uap,
break;
}
/*
* 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.