summaryrefslogtreecommitdiffstats
path: root/sys/fs/tmpfs
diff options
context:
space:
mode:
Diffstat (limited to 'sys/fs/tmpfs')
-rw-r--r--sys/fs/tmpfs/tmpfs_subr.c78
1 files changed, 59 insertions, 19 deletions
diff --git a/sys/fs/tmpfs/tmpfs_subr.c b/sys/fs/tmpfs/tmpfs_subr.c
index e9324cf..e733f19 100644
--- a/sys/fs/tmpfs/tmpfs_subr.c
+++ b/sys/fs/tmpfs/tmpfs_subr.c
@@ -48,6 +48,7 @@ __FBSDID("$FreeBSD$");
#include <vm/vm.h>
#include <vm/vm_object.h>
#include <vm/vm_page.h>
+#include <vm/vm_pageout.h>
#include <vm/vm_pager.h>
#include <vm/vm_extern.h>
@@ -886,10 +887,10 @@ tmpfs_reg_resize(struct vnode *vp, off_t newsize)
struct tmpfs_mount *tmp;
struct tmpfs_node *node;
vm_object_t uobj;
- vm_page_t m;
- vm_pindex_t newpages, oldpages;
+ vm_page_t m, ma[1];
+ vm_pindex_t idx, newpages, oldpages;
off_t oldsize;
- size_t zerolen;
+ int base, rv;
MPASS(vp->v_type == VREG);
MPASS(newsize >= 0);
@@ -912,15 +913,57 @@ tmpfs_reg_resize(struct vnode *vp, off_t newsize)
newpages - oldpages > TMPFS_PAGES_AVAIL(tmp))
return (ENOSPC);
- TMPFS_LOCK(tmp);
- tmp->tm_pages_used += (newpages - oldpages);
- TMPFS_UNLOCK(tmp);
-
- node->tn_size = newsize;
- vnode_pager_setsize(vp, newsize);
VM_OBJECT_LOCK(uobj);
if (newsize < oldsize) {
/*
+ * Zero the truncated part of the last page.
+ */
+ base = newsize & PAGE_MASK;
+ if (base != 0) {
+ idx = OFF_TO_IDX(newsize);
+retry:
+ m = vm_page_lookup(uobj, idx);
+ if (m != NULL) {
+ if ((m->oflags & VPO_BUSY) != 0 ||
+ m->busy != 0) {
+ vm_page_sleep(m, "tmfssz");
+ goto retry;
+ }
+ } else if (vm_pager_has_page(uobj, idx, NULL, NULL)) {
+ m = vm_page_alloc(uobj, idx, VM_ALLOC_NORMAL);
+ if (m == NULL) {
+ VM_OBJECT_UNLOCK(uobj);
+ VM_WAIT;
+ VM_OBJECT_LOCK(uobj);
+ goto retry;
+ } else if (m->valid != VM_PAGE_BITS_ALL) {
+ ma[0] = m;
+ rv = vm_pager_get_pages(uobj, ma, 1, 0);
+ m = vm_page_lookup(uobj, idx);
+ } else
+ /* A cached page was reactivated. */
+ rv = VM_PAGER_OK;
+ vm_page_lock(m);
+ if (rv == VM_PAGER_OK) {
+ vm_page_deactivate(m);
+ vm_page_unlock(m);
+ vm_page_wakeup(m);
+ } else {
+ vm_page_free(m);
+ vm_page_unlock(m);
+ VM_OBJECT_UNLOCK(uobj);
+ return (EIO);
+ }
+ }
+ if (m != NULL) {
+ pmap_zero_page_area(m, base, PAGE_SIZE - base);
+ MPASS(m->valid == VM_PAGE_BITS_ALL);
+ vm_page_dirty(m);
+ vm_pager_page_unswapped(m);
+ }
+ }
+
+ /*
* Release any swap space and free any whole pages.
*/
if (newpages < oldpages) {
@@ -928,19 +971,16 @@ tmpfs_reg_resize(struct vnode *vp, off_t newsize)
newpages);
vm_object_page_remove(uobj, newpages, 0, 0);
}
-
- /*
- * Zero the truncated part of the last page.
- */
- zerolen = round_page(newsize) - newsize;
- if (zerolen > 0) {
- m = vm_page_grab(uobj, OFF_TO_IDX(newsize),
- VM_ALLOC_NOBUSY | VM_ALLOC_NORMAL | VM_ALLOC_RETRY);
- pmap_zero_page_area(m, PAGE_SIZE - zerolen, zerolen);
- }
}
uobj->size = newpages;
VM_OBJECT_UNLOCK(uobj);
+
+ TMPFS_LOCK(tmp);
+ tmp->tm_pages_used += (newpages - oldpages);
+ TMPFS_UNLOCK(tmp);
+
+ node->tn_size = newsize;
+ vnode_pager_setsize(vp, newsize);
return (0);
}
OpenPOWER on IntegriCloud