summaryrefslogtreecommitdiffstats
path: root/sys/kern/uipc_syscalls.c
diff options
context:
space:
mode:
authorkib <kib@FreeBSD.org>2013-04-28 19:12:09 +0000
committerkib <kib@FreeBSD.org>2013-04-28 19:12:09 +0000
commitebecd57ee2dd46812376994b4fb9cf9c81af71c5 (patch)
treef5f1fdcfe24acd5d7cba9de8738765abf6e8d874 /sys/kern/uipc_syscalls.c
parent0e1bea778f3efcfe057a0617c4536ea6c867d2a0 (diff)
downloadFreeBSD-src-ebecd57ee2dd46812376994b4fb9cf9c81af71c5.zip
FreeBSD-src-ebecd57ee2dd46812376994b4fb9cf9c81af71c5.tar.gz
Eliminate the layering violation in the kern_sendfile(). When quering
the file size, use VOP_GETATTR() instead of accessing vnode vm_object un_pager.vnp.vnp_size. Take the shared vnode lock earlier to cover the added VOP_GETATTR() call and, as consequence, the whole internal sendfile loop. Reduce vm object lock scope to not protect the local calculations. Note that this is the last misuse of the vnp_size in the tree, the others were removed from the ELF image activator by r230246. Reviewed by: alc Tested by: pho, bf (previous version) MFC after: 1 week
Diffstat (limited to 'sys/kern/uipc_syscalls.c')
-rw-r--r--sys/kern/uipc_syscalls.c30
1 files changed, 16 insertions, 14 deletions
diff --git a/sys/kern/uipc_syscalls.c b/sys/kern/uipc_syscalls.c
index ac2fd42..171274a 100644
--- a/sys/kern/uipc_syscalls.c
+++ b/sys/kern/uipc_syscalls.c
@@ -1902,8 +1902,10 @@ kern_sendfile(struct thread *td, struct sendfile_args *uap,
struct mbuf *m = NULL;
struct sf_buf *sf;
struct vm_page *pg;
+ struct vattr va;
off_t off, xfsize, fsbytes = 0, sbytes = 0, rem = 0;
int error, hdrlen = 0, mnw = 0;
+ int bsize;
struct sendfile_sync *sfs = NULL;
/*
@@ -2102,6 +2104,16 @@ retry_space:
*/
space -= hdrlen;
+ error = vn_lock(vp, LK_SHARED);
+ if (error != 0)
+ goto done;
+ error = VOP_GETATTR(vp, &va, td->td_ucred);
+ if (error != 0) {
+ 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.
@@ -2111,7 +2123,6 @@ retry_space:
vm_offset_t pgoff;
struct mbuf *m0;
- VM_OBJECT_WLOCK(obj);
/*
* Calculate the amount to transfer.
* Not to exceed a page, the EOF,
@@ -2121,12 +2132,11 @@ retry_space:
if (uap->nbytes)
rem = (uap->nbytes - fsbytes - loopbytes);
else
- rem = obj->un_pager.vnp.vnp_size -
+ rem = va.va_size -
uap->offset - fsbytes - loopbytes;
xfsize = omin(PAGE_SIZE - pgoff, rem);
xfsize = omin(space - loopbytes, xfsize);
if (xfsize <= 0) {
- VM_OBJECT_WUNLOCK(obj);
done = 1; /* all data sent */
break;
}
@@ -2136,7 +2146,6 @@ retry_space:
* Let the outer loop figure out how to handle it.
*/
if (space <= loopbytes) {
- VM_OBJECT_WUNLOCK(obj);
done = 0;
break;
}
@@ -2146,6 +2155,7 @@ retry_space:
* if not found or wait and loop if busy.
*/
pindex = OFF_TO_IDX(off);
+ VM_OBJECT_WLOCK(obj);
pg = vm_page_grab(obj, pindex, VM_ALLOC_NOBUSY |
VM_ALLOC_NORMAL | VM_ALLOC_WIRED | VM_ALLOC_RETRY);
@@ -2163,7 +2173,6 @@ retry_space:
else if (uap->flags & SF_NODISKIO)
error = EBUSY;
else {
- int bsize;
ssize_t resid;
/*
@@ -2175,13 +2184,6 @@ retry_space:
/*
* Get the page from backing store.
- */
- error = vn_lock(vp, LK_SHARED);
- if (error != 0)
- goto after_read;
- bsize = vp->v_mount->mnt_stat.f_iosize;
-
- /*
* XXXMAC: Because we don't have fp->f_cred
* here, we pass in NOCRED. This is probably
* wrong, but is consistent with our original
@@ -2191,8 +2193,6 @@ retry_space:
trunc_page(off), UIO_NOCOPY, IO_NODELOCKED |
IO_VMIO | ((MAXBSIZE / bsize) << IO_SEQSHIFT),
td->td_ucred, NOCRED, &resid, td);
- VOP_UNLOCK(vp, 0);
- after_read:
VM_OBJECT_WLOCK(obj);
vm_page_io_finish(pg);
if (!error)
@@ -2281,6 +2281,8 @@ retry_space:
}
}
+ VOP_UNLOCK(vp, 0);
+
/* Add the buffer chain to the socket buffer. */
if (m != NULL) {
int mlen, err;
OpenPOWER on IntegriCloud