summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordelphij <delphij@FreeBSD.org>2009-10-07 23:17:15 +0000
committerdelphij <delphij@FreeBSD.org>2009-10-07 23:17:15 +0000
commit9fa7d8b89bc54e635e532fede4cf46c06ec0c45d (patch)
treea5aab78c4514d8956887a9200095b8c1e020a7a6
parentc1f5ff22b033569951c811143690967e46dca5a4 (diff)
downloadFreeBSD-src-9fa7d8b89bc54e635e532fede4cf46c06ec0c45d.zip
FreeBSD-src-9fa7d8b89bc54e635e532fede4cf46c06ec0c45d.tar.gz
Add a special workaround to handle UIO_NOCOPY case. This fixes data
corruption observed when sendfile() is being used. PR: kern/127213 Submitted by: gk MFC after: 2 weeks
-rw-r--r--sys/fs/tmpfs/tmpfs_vnops.c104
1 files changed, 80 insertions, 24 deletions
diff --git a/sys/fs/tmpfs/tmpfs_vnops.c b/sys/fs/tmpfs/tmpfs_vnops.c
index 59d94d7..49a7197 100644
--- a/sys/fs/tmpfs/tmpfs_vnops.c
+++ b/sys/fs/tmpfs/tmpfs_vnops.c
@@ -43,6 +43,8 @@ __FBSDID("$FreeBSD$");
#include <sys/priv.h>
#include <sys/proc.h>
#include <sys/resourcevar.h>
+#include <sys/sched.h>
+#include <sys/sf_buf.h>
#include <sys/stat.h>
#include <sys/systm.h>
#include <sys/unistd.h>
@@ -428,15 +430,72 @@ tmpfs_setattr(struct vop_setattr_args *v)
}
/* --------------------------------------------------------------------- */
+static int
+tmpfs_nocacheread(vm_object_t tobj, vm_pindex_t idx,
+ vm_offset_t offset, size_t tlen, struct uio *uio)
+{
+ vm_page_t m;
+ int error;
+
+ VM_OBJECT_LOCK(tobj);
+ vm_object_pip_add(tobj, 1);
+ m = vm_page_grab(tobj, idx, VM_ALLOC_WIRED |
+ VM_ALLOC_ZERO | VM_ALLOC_NORMAL | VM_ALLOC_RETRY);
+ if (m->valid != VM_PAGE_BITS_ALL) {
+ if (vm_pager_has_page(tobj, idx, NULL, NULL)) {
+ error = vm_pager_get_pages(tobj, &m, 1, 0);
+ if (error != 0) {
+ printf("tmpfs get pages from pager error [read]\n");
+ goto out;
+ }
+ } else
+ vm_page_zero_invalid(m, TRUE);
+ }
+ VM_OBJECT_UNLOCK(tobj);
+ error = uiomove_fromphys(&m, offset, tlen, uio);
+ VM_OBJECT_LOCK(tobj);
+out:
+ vm_page_lock_queues();
+ vm_page_unwire(m, TRUE);
+ vm_page_unlock_queues();
+ vm_page_wakeup(m);
+ vm_object_pip_subtract(tobj, 1);
+ VM_OBJECT_UNLOCK(tobj);
+
+ return (error);
+}
+
+static __inline int
+tmpfs_nocacheread_buf(vm_object_t tobj, vm_pindex_t idx,
+ vm_offset_t offset, size_t tlen, void *buf)
+{
+ struct uio uio;
+ struct iovec iov;
+
+ uio.uio_iovcnt = 1;
+ uio.uio_iov = &iov;
+ iov.iov_base = buf;
+ iov.iov_len = tlen;
+
+ uio.uio_offset = 0;
+ uio.uio_resid = tlen;
+ uio.uio_rw = UIO_READ;
+ uio.uio_segflg = UIO_SYSSPACE;
+ uio.uio_td = curthread;
+
+ return (tmpfs_nocacheread(tobj, idx, offset, tlen, &uio));
+}
static int
tmpfs_mappedread(vm_object_t vobj, vm_object_t tobj, size_t len, struct uio *uio)
{
+ struct sf_buf *sf;
vm_pindex_t idx;
vm_page_t m;
vm_offset_t offset;
off_t addr;
size_t tlen;
+ char *ma;
int error;
addr = uio->uio_offset;
@@ -461,33 +520,30 @@ lookupvpg:
vm_page_wakeup(m);
VM_OBJECT_UNLOCK(vobj);
return (error);
+ } else if (m != NULL && uio->uio_segflg == UIO_NOCOPY) {
+ if (vm_page_sleep_if_busy(m, FALSE, "tmfsmr"))
+ goto lookupvpg;
+ vm_page_busy(m);
+ VM_OBJECT_UNLOCK(vobj);
+ sched_pin();
+ sf = sf_buf_alloc(m, SFB_CPUPRIVATE);
+ ma = (char *)sf_buf_kva(sf);
+ error = tmpfs_nocacheread_buf(tobj, idx, offset, tlen,
+ ma + offset);
+ if (error == 0) {
+ uio->uio_offset += tlen;
+ uio->uio_resid -= tlen;
+ }
+ sf_buf_free(sf);
+ sched_unpin();
+ VM_OBJECT_LOCK(vobj);
+ vm_page_wakeup(m);
+ VM_OBJECT_UNLOCK(vobj);
+ return (error);
}
VM_OBJECT_UNLOCK(vobj);
nocache:
- VM_OBJECT_LOCK(tobj);
- vm_object_pip_add(tobj, 1);
- m = vm_page_grab(tobj, idx, VM_ALLOC_WIRED |
- VM_ALLOC_ZERO | VM_ALLOC_NORMAL | VM_ALLOC_RETRY);
- if (m->valid != VM_PAGE_BITS_ALL) {
- if (vm_pager_has_page(tobj, idx, NULL, NULL)) {
- error = vm_pager_get_pages(tobj, &m, 1, 0);
- if (error != 0) {
- printf("tmpfs get pages from pager error [read]\n");
- goto out;
- }
- } else
- vm_page_zero_invalid(m, TRUE);
- }
- VM_OBJECT_UNLOCK(tobj);
- error = uiomove_fromphys(&m, offset, tlen, uio);
- VM_OBJECT_LOCK(tobj);
-out:
- vm_page_lock_queues();
- vm_page_unwire(m, TRUE);
- vm_page_unlock_queues();
- vm_page_wakeup(m);
- vm_object_pip_subtract(tobj, 1);
- VM_OBJECT_UNLOCK(tobj);
+ error = tmpfs_nocacheread(tobj, idx, offset, tlen, uio);
return (error);
}
OpenPOWER on IntegriCloud