aboutsummaryrefslogtreecommitdiff
path: root/sys/fs/tmpfs/tmpfs_subr.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/fs/tmpfs/tmpfs_subr.c')
-rw-r--r--sys/fs/tmpfs/tmpfs_subr.c106
1 files changed, 55 insertions, 51 deletions
diff --git a/sys/fs/tmpfs/tmpfs_subr.c b/sys/fs/tmpfs/tmpfs_subr.c
index 9bdcc4575511..1237f6b92cdb 100644
--- a/sys/fs/tmpfs/tmpfs_subr.c
+++ b/sys/fs/tmpfs/tmpfs_subr.c
@@ -120,7 +120,7 @@ tmpfs_pager_writecount_recalc(vm_object_t object, vm_offset_t old,
/*
* Forced unmount?
*/
- if (vp == NULL) {
+ if (vp == NULL || vp->v_object == NULL) {
KASSERT((object->flags & OBJ_TMPFS_VREF) == 0,
("object %p with OBJ_TMPFS_VREF but without vnode",
object));
@@ -183,6 +183,9 @@ tmpfs_pager_release_writecount(vm_object_t object, vm_offset_t start,
KASSERT((object->flags & OBJ_ANON) == 0,
("%s: object %p with OBJ_ANON", __func__, object));
old = object->un_pager.swp.writemappings;
+ KASSERT(old >= (vm_ooffset_t)end - start,
+ ("tmpfs obj %p writecount %jx dec %jx", object, (uintmax_t)old,
+ (uintmax_t)((vm_ooffset_t)end - start)));
object->un_pager.swp.writemappings -= (vm_ooffset_t)end - start;
new = object->un_pager.swp.writemappings;
tmpfs_pager_writecount_recalc(object, old, new);
@@ -346,7 +349,7 @@ tmpfs_node_init(void *mem, int size, int flags)
node = mem;
node->tn_id = 0;
- mtx_init(&node->tn_interlock, "tmpfsni", NULL, MTX_DEF);
+ mtx_init(&node->tn_interlock, "tmpfsni", NULL, MTX_DEF | MTX_NEW);
node->tn_gen = arc4random();
return (0);
}
@@ -425,7 +428,7 @@ sysctl_mem_percent(SYSCTL_HANDLER_ARGS)
if ((unsigned) percent > 100)
return (EINVAL);
- *(long *)arg1 = percent;
+ *(int *)arg1 = percent;
tmpfs_set_reserve_from_percent();
return (0);
}
@@ -440,7 +443,7 @@ tmpfs_set_reserve_from_percent(void)
}
SYSCTL_PROC(_vfs_tmpfs, OID_AUTO, memory_percent,
- CTLTYPE_INT | CTLFLAG_MPSAFE | CTLFLAG_RW, &tmpfs_mem_percent, 0,
+ CTLTYPE_INT | CTLFLAG_MPSAFE | CTLFLAG_RWTUN, &tmpfs_mem_percent, 0,
sysctl_mem_percent, "I",
"Percent of available memory that can be used if no size limit");
@@ -490,50 +493,11 @@ static int
tmpfs_partial_page_invalidate(vm_object_t object, vm_pindex_t idx, int base,
int end, boolean_t ignerr)
{
- vm_page_t m;
- int rv, error;
-
- VM_OBJECT_ASSERT_WLOCKED(object);
- KASSERT(base >= 0, ("%s: base %d", __func__, base));
- KASSERT(end - base <= PAGE_SIZE, ("%s: base %d end %d", __func__, base,
- end));
- error = 0;
-
-retry:
- m = vm_page_grab(object, idx, VM_ALLOC_NOCREAT);
- if (m != NULL) {
- MPASS(vm_page_all_valid(m));
- } else if (vm_pager_has_page(object, idx, NULL, NULL)) {
- m = vm_page_alloc(object, idx, VM_ALLOC_NORMAL |
- VM_ALLOC_WAITFAIL);
- if (m == NULL)
- goto retry;
- vm_object_pip_add(object, 1);
- VM_OBJECT_WUNLOCK(object);
- rv = vm_pager_get_pages(object, &m, 1, NULL, NULL);
- VM_OBJECT_WLOCK(object);
- vm_object_pip_wakeup(object);
- if (rv == VM_PAGER_OK) {
- /*
- * Since the page was not resident, and therefore not
- * recently accessed, immediately enqueue it for
- * asynchronous laundering. The current operation is
- * not regarded as an access.
- */
- vm_page_launder(m);
- } else {
- vm_page_free(m);
- m = NULL;
- if (!ignerr)
- error = EIO;
- }
- }
- if (m != NULL) {
- pmap_zero_page_area(m, base, end - base);
- vm_page_set_dirty(m);
- vm_page_xunbusy(m);
- }
+ int error;
+ error = vm_page_grab_zero_partial(object, idx, base, end);
+ if (ignerr)
+ error = 0;
return (error);
}
@@ -643,6 +607,7 @@ tmpfs_alloc_node(struct mount *mp, struct tmpfs_mount *tmp, __enum_uint8(vtype)
nnode->tn_dir.tn_parent = (parent == NULL) ? nnode : parent;
nnode->tn_dir.tn_readdir_lastn = 0;
nnode->tn_dir.tn_readdir_lastp = NULL;
+ nnode->tn_dir.tn_wht_size = 0;
nnode->tn_links++;
TMPFS_NODE_LOCK(nnode->tn_dir.tn_parent);
nnode->tn_dir.tn_parent->tn_links++;
@@ -954,6 +919,8 @@ tmpfs_destroy_vobject(struct vnode *vp, vm_object_t obj)
VM_OBJECT_WLOCK(obj);
VI_LOCK(vp);
+ vp->v_object = NULL;
+
/*
* May be going through forced unmount.
*/
@@ -1094,15 +1061,19 @@ loop:
KASSERT((object->flags & OBJ_TMPFS_VREF) == 0,
("%s: object %p with OBJ_TMPFS_VREF but without vnode",
__func__, object));
- KASSERT(object->un_pager.swp.writemappings == 0,
- ("%s: object %p has writemappings",
- __func__, object));
VI_LOCK(vp);
KASSERT(vp->v_object == NULL, ("Not NULL v_object in tmpfs"));
vp->v_object = object;
vn_irflag_set_locked(vp, (tm->tm_pgread ? VIRF_PGREAD : 0) |
VIRF_TEXT_REF);
VI_UNLOCK(vp);
+ VNASSERT((object->flags & OBJ_TMPFS_VREF) == 0, vp,
+ ("leaked OBJ_TMPFS_VREF"));
+ if (object->un_pager.swp.writemappings > 0) {
+ vrefact(vp);
+ vlazy(vp);
+ vm_object_set_flag(object, OBJ_TMPFS_VREF);
+ }
VM_OBJECT_WUNLOCK(object);
break;
case VDIR:
@@ -1822,13 +1793,16 @@ int
tmpfs_dir_whiteout_add(struct vnode *dvp, struct componentname *cnp)
{
struct tmpfs_dirent *de;
+ struct tmpfs_node *dnode;
int error;
error = tmpfs_alloc_dirent(VFS_TO_TMPFS(dvp->v_mount), NULL,
cnp->cn_nameptr, cnp->cn_namelen, &de);
if (error != 0)
return (error);
+ dnode = VP_TO_TMPFS_DIR(dvp);
tmpfs_dir_attach(dvp, de);
+ dnode->tn_dir.tn_wht_size += sizeof(*de);
return (0);
}
@@ -1836,14 +1810,44 @@ void
tmpfs_dir_whiteout_remove(struct vnode *dvp, struct componentname *cnp)
{
struct tmpfs_dirent *de;
+ struct tmpfs_node *dnode;
- de = tmpfs_dir_lookup(VP_TO_TMPFS_DIR(dvp), NULL, cnp);
+ dnode = VP_TO_TMPFS_DIR(dvp);
+ de = tmpfs_dir_lookup(dnode, NULL, cnp);
MPASS(de != NULL && de->td_node == NULL);
+ MPASS(dnode->tn_dir.tn_wht_size >= sizeof(*de));
+ dnode->tn_dir.tn_wht_size -= sizeof(*de);
tmpfs_dir_detach(dvp, de);
tmpfs_free_dirent(VFS_TO_TMPFS(dvp->v_mount), de);
}
/*
+ * Frees any dirents still associated with the directory represented
+ * by dvp in preparation for the removal of the directory. This is
+ * required when removing a directory which contains only whiteout
+ * entries.
+ */
+void
+tmpfs_dir_clear_whiteouts(struct vnode *dvp)
+{
+ struct tmpfs_dir_cursor dc;
+ struct tmpfs_dirent *de;
+ struct tmpfs_node *dnode;
+
+ dnode = VP_TO_TMPFS_DIR(dvp);
+
+ while ((de = tmpfs_dir_first(dnode, &dc)) != NULL) {
+ KASSERT(de->td_node == NULL, ("%s: non-whiteout dirent %p",
+ __func__, de));
+ dnode->tn_dir.tn_wht_size -= sizeof(*de);
+ tmpfs_dir_detach(dvp, de);
+ tmpfs_free_dirent(VFS_TO_TMPFS(dvp->v_mount), de);
+ }
+ MPASS(dnode->tn_size == 0);
+ MPASS(dnode->tn_dir.tn_wht_size == 0);
+}
+
+/*
* Resizes the aobj associated with the regular file pointed to by 'vp' to the
* size 'newsize'. 'vp' must point to a vnode that represents a regular file.
* 'newsize' must be positive.