summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorkib <kib@FreeBSD.org>2017-02-02 01:11:49 +0000
committerkib <kib@FreeBSD.org>2017-02-02 01:11:49 +0000
commit1473483a33af3fc230708ab799feba7e44a664b6 (patch)
tree8b50f621d4a8d713deeffe76bdaf861227281c78
parent0cba2806d6ca10f967c520d253de979c4c0cdc6e (diff)
downloadFreeBSD-src-1473483a33af3fc230708ab799feba7e44a664b6.zip
FreeBSD-src-1473483a33af3fc230708ab799feba7e44a664b6.tar.gz
MFC r312428:
Refcount tmpfs nodes and mount structures.
-rw-r--r--sys/fs/tmpfs/tmpfs.h14
-rw-r--r--sys/fs/tmpfs/tmpfs_subr.c89
-rw-r--r--sys/fs/tmpfs/tmpfs_vfsops.c62
-rw-r--r--sys/fs/tmpfs/tmpfs_vnops.c9
4 files changed, 130 insertions, 44 deletions
diff --git a/sys/fs/tmpfs/tmpfs.h b/sys/fs/tmpfs/tmpfs.h
index fe81d39..8661f42 100644
--- a/sys/fs/tmpfs/tmpfs.h
+++ b/sys/fs/tmpfs/tmpfs.h
@@ -158,9 +158,11 @@ struct tmpfs_node {
* Doubly-linked list entry which links all existing nodes for
* a single file system. This is provided to ease the removal
* of all nodes during the unmount operation, and to support
- * the implementation of VOP_VNTOCNP().
+ * the implementation of VOP_VNTOCNP(). tn_attached is false
+ * when the node is removed from list and unlocked.
*/
LIST_ENTRY(tmpfs_node) tn_entries; /* (m) */
+ bool tn_attached; /* (m) */
/*
* The node's type. Any of 'VBLK', 'VCHR', 'VDIR', 'VFIFO',
@@ -231,6 +233,9 @@ struct tmpfs_node {
*/
int tn_vpstate; /* (i) */
+ /* Transient refcounter on this node. */
+ u_int tn_refcount; /* (m) + (i) */
+
/* misc data field for different tn_type node */
union {
/* Valid when tn_type == VBLK || tn_type == VCHR. */
@@ -358,6 +363,9 @@ struct tmpfs_mount {
/* Number of nodes currently that are in use. */
ino_t tm_nodes_inuse;
+ /* Refcounter on this struct tmpfs_mount. */
+ uint64_t tm_refcount;
+
/* maximum representable file size */
u_int64_t tm_maxfilesize;
@@ -402,10 +410,14 @@ struct tmpfs_dir_cursor {
* Prototypes for tmpfs_subr.c.
*/
+void tmpfs_ref_node(struct tmpfs_node *node);
+void tmpfs_ref_node_locked(struct tmpfs_node *node);
int tmpfs_alloc_node(struct mount *mp, struct tmpfs_mount *, enum vtype,
uid_t uid, gid_t gid, mode_t mode, struct tmpfs_node *,
char *, dev_t, struct tmpfs_node **);
void tmpfs_free_node(struct tmpfs_mount *, struct tmpfs_node *);
+bool tmpfs_free_node_locked(struct tmpfs_mount *, struct tmpfs_node *, bool);
+void tmpfs_free_tmp(struct tmpfs_mount *);
int tmpfs_alloc_dirent(struct tmpfs_mount *, struct tmpfs_node *,
const char *, u_int, struct tmpfs_dirent **);
void tmpfs_free_dirent(struct tmpfs_mount *, struct tmpfs_dirent *);
diff --git a/sys/fs/tmpfs/tmpfs_subr.c b/sys/fs/tmpfs/tmpfs_subr.c
index 5acb3c0..32091de 100644
--- a/sys/fs/tmpfs/tmpfs_subr.c
+++ b/sys/fs/tmpfs/tmpfs_subr.c
@@ -132,6 +132,26 @@ tmpfs_pages_check_avail(struct tmpfs_mount *tmp, size_t req_pages)
return (1);
}
+void
+tmpfs_ref_node(struct tmpfs_node *node)
+{
+
+ TMPFS_NODE_LOCK(node);
+ tmpfs_ref_node_locked(node);
+ TMPFS_NODE_UNLOCK(node);
+}
+
+void
+tmpfs_ref_node_locked(struct tmpfs_node *node)
+{
+
+ TMPFS_NODE_ASSERT_LOCKED(node);
+ KASSERT(node->tn_refcount > 0, ("node %p zero refcount", node));
+ KASSERT(node->tn_refcount < UINT_MAX, ("node %p refcount %u", node,
+ node->tn_refcount));
+ node->tn_refcount++;
+}
+
/*
* Allocates a new node of type 'type' inside the 'tmp' mount point, with
* its owner set to 'uid', its group to 'gid' and its mode set to 'mode',
@@ -206,6 +226,7 @@ tmpfs_alloc_node(struct mount *mp, struct tmpfs_mount *tmp, enum vtype type,
nnode->tn_gid = gid;
nnode->tn_mode = mode;
nnode->tn_id = alloc_unr(tmp->tm_ino_unr);
+ nnode->tn_refcount = 1;
/* Type-specific initialization. */
switch (nnode->tn_type) {
@@ -259,7 +280,9 @@ tmpfs_alloc_node(struct mount *mp, struct tmpfs_mount *tmp, enum vtype type,
TMPFS_LOCK(tmp);
LIST_INSERT_HEAD(&tmp->tm_nodes_used, nnode, tn_entries);
+ nnode->tn_attached = true;
tmp->tm_nodes_inuse++;
+ tmp->tm_refcount++;
TMPFS_UNLOCK(tmp);
*node = nnode;
@@ -273,18 +296,40 @@ tmpfs_alloc_node(struct mount *mp, struct tmpfs_mount *tmp, enum vtype type,
void
tmpfs_free_node(struct tmpfs_mount *tmp, struct tmpfs_node *node)
{
+
+ TMPFS_LOCK(tmp);
+ TMPFS_NODE_LOCK(node);
+ if (!tmpfs_free_node_locked(tmp, node, false)) {
+ TMPFS_NODE_UNLOCK(node);
+ TMPFS_UNLOCK(tmp);
+ }
+}
+
+bool
+tmpfs_free_node_locked(struct tmpfs_mount *tmp, struct tmpfs_node *node,
+ bool detach)
+{
vm_object_t uobj;
+ TMPFS_MP_ASSERT_LOCKED(tmp);
+ TMPFS_NODE_ASSERT_LOCKED(node);
+ KASSERT(node->tn_refcount > 0, ("node %p refcount zero", node));
+
+ node->tn_refcount--;
+ if (node->tn_attached && (detach || node->tn_refcount == 0)) {
+ MPASS(tmp->tm_nodes_inuse > 0);
+ tmp->tm_nodes_inuse--;
+ LIST_REMOVE(node, tn_entries);
+ node->tn_attached = false;
+ }
+ if (node->tn_refcount > 0)
+ return (false);
+
#ifdef INVARIANTS
- TMPFS_NODE_LOCK(node);
MPASS(node->tn_vnode == NULL);
MPASS((node->tn_vpstate & TMPFS_VNODE_ALLOCATING) == 0);
- TMPFS_NODE_UNLOCK(node);
#endif
-
- TMPFS_LOCK(tmp);
- LIST_REMOVE(node, tn_entries);
- tmp->tm_nodes_inuse--;
+ TMPFS_NODE_UNLOCK(node);
TMPFS_UNLOCK(tmp);
switch (node->tn_type) {
@@ -324,6 +369,9 @@ tmpfs_free_node(struct tmpfs_mount *tmp, struct tmpfs_node *node)
free_unr(tmp->tm_ino_unr, node->tn_id);
uma_zfree(tmp->tm_node_pool, node);
+ TMPFS_LOCK(tmp);
+ tmpfs_free_tmp(tmp);
+ return (true);
}
static __inline uint32_t
@@ -469,13 +517,16 @@ tmpfs_alloc_vp(struct mount *mp, struct tmpfs_node *node, int lkflag,
struct vnode **vpp)
{
struct vnode *vp;
+ struct tmpfs_mount *tm;
vm_object_t object;
int error;
error = 0;
-loop:
+ tm = VFS_TO_TMPFS(mp);
TMPFS_NODE_LOCK(node);
-loop1:
+ tmpfs_ref_node_locked(node);
+loop:
+ TMPFS_NODE_ASSERT_LOCKED(node);
if ((vp = node->tn_vnode) != NULL) {
MPASS((node->tn_vpstate & TMPFS_VNODE_DOOMED) == 0);
VI_LOCK(vp);
@@ -495,12 +546,14 @@ loop1:
msleep(&node->tn_vnode, TMPFS_NODE_MTX(node),
0, "tmpfsE", 0);
}
- goto loop1;
+ goto loop;
}
TMPFS_NODE_UNLOCK(node);
error = vget(vp, lkflag | LK_INTERLOCK, curthread);
- if (error == ENOENT)
+ if (error == ENOENT) {
+ TMPFS_NODE_LOCK(node);
goto loop;
+ }
if (error != 0) {
vp = NULL;
goto out;
@@ -512,6 +565,7 @@ loop1:
*/
if (node->tn_vnode == NULL || node->tn_vnode != vp) {
vput(vp);
+ TMPFS_NODE_LOCK(node);
goto loop;
}
@@ -533,11 +587,9 @@ loop1:
if (node->tn_vpstate & TMPFS_VNODE_ALLOCATING) {
node->tn_vpstate |= TMPFS_VNODE_WANT;
error = msleep((caddr_t) &node->tn_vpstate,
- TMPFS_NODE_MTX(node), PDROP | PCATCH,
- "tmpfs_alloc_vp", 0);
- if (error)
- return error;
-
+ TMPFS_NODE_MTX(node), 0, "tmpfs_alloc_vp", 0);
+ if (error != 0)
+ goto out;
goto loop;
} else
node->tn_vpstate |= TMPFS_VNODE_ALLOCATING;
@@ -611,16 +663,17 @@ unlock:
TMPFS_NODE_UNLOCK(node);
out:
- *vpp = vp;
+ if (error == 0) {
+ *vpp = vp;
#ifdef INVARIANTS
- if (error == 0) {
MPASS(*vpp != NULL && VOP_ISLOCKED(*vpp));
TMPFS_NODE_LOCK(node);
MPASS(*vpp == node->tn_vnode);
TMPFS_NODE_UNLOCK(node);
- }
#endif
+ }
+ tmpfs_free_node(tm, node);
return (error);
}
diff --git a/sys/fs/tmpfs/tmpfs_vfsops.c b/sys/fs/tmpfs/tmpfs_vfsops.c
index 88b12ef..188e476 100644
--- a/sys/fs/tmpfs/tmpfs_vfsops.c
+++ b/sys/fs/tmpfs/tmpfs_vfsops.c
@@ -222,6 +222,7 @@ tmpfs_mount(struct mount *mp)
mtx_init(&tmp->tm_allnode_lock, "tmpfs allnode lock", NULL, MTX_DEF);
tmp->tm_nodes_max = nodes_max;
tmp->tm_nodes_inuse = 0;
+ tmp->tm_refcount = 1;
tmp->tm_maxfilesize = maxfilesize > 0 ? maxfilesize : OFF_MAX;
LIST_INIT(&tmp->tm_nodes_used);
@@ -304,11 +305,35 @@ tmpfs_unmount(struct mount *mp, int mntflags)
TMPFS_LOCK(tmp);
while ((node = LIST_FIRST(&tmp->tm_nodes_used)) != NULL) {
- TMPFS_UNLOCK(tmp);
+ TMPFS_NODE_LOCK(node);
if (node->tn_type == VDIR)
tmpfs_dir_destroy(tmp, node);
- tmpfs_free_node(tmp, node);
- TMPFS_LOCK(tmp);
+ if (tmpfs_free_node_locked(tmp, node, true))
+ TMPFS_LOCK(tmp);
+ else
+ TMPFS_NODE_UNLOCK(node);
+ }
+
+ mp->mnt_data = NULL;
+ tmpfs_free_tmp(tmp);
+ vfs_write_resume(mp, VR_START_WRITE);
+
+ MNT_ILOCK(mp);
+ mp->mnt_flag &= ~MNT_LOCAL;
+ MNT_IUNLOCK(mp);
+
+ return (0);
+}
+
+void
+tmpfs_free_tmp(struct tmpfs_mount *tmp)
+{
+
+ MPASS(tmp->tm_refcount > 0);
+ tmp->tm_refcount--;
+ if (tmp->tm_refcount > 0) {
+ TMPFS_UNLOCK(tmp);
+ return;
}
TMPFS_UNLOCK(tmp);
@@ -320,16 +345,7 @@ tmpfs_unmount(struct mount *mp, int mntflags)
MPASS(tmp->tm_pages_used == 0);
MPASS(tmp->tm_nodes_inuse == 0);
- /* Throw away the tmpfs_mount structure. */
- free(mp->mnt_data, M_TMPFSMNT);
- mp->mnt_data = NULL;
- vfs_write_resume(mp, VR_START_WRITE);
-
- MNT_ILOCK(mp);
- mp->mnt_flag &= ~MNT_LOCAL;
- MNT_IUNLOCK(mp);
-
- return (0);
+ free(tmp, M_TMPFSMNT);
}
static int
@@ -347,36 +363,36 @@ static int
tmpfs_fhtovp(struct mount *mp, struct fid *fhp, int flags,
struct vnode **vpp)
{
- boolean_t found;
struct tmpfs_fid *tfhp;
struct tmpfs_mount *tmp;
struct tmpfs_node *node;
+ int error;
tmp = VFS_TO_TMPFS(mp);
tfhp = (struct tmpfs_fid *)fhp;
if (tfhp->tf_len != sizeof(struct tmpfs_fid))
- return EINVAL;
+ return (EINVAL);
if (tfhp->tf_id >= tmp->tm_nodes_max)
- return EINVAL;
-
- found = FALSE;
+ return (EINVAL);
TMPFS_LOCK(tmp);
LIST_FOREACH(node, &tmp->tm_nodes_used, tn_entries) {
if (node->tn_id == tfhp->tf_id &&
node->tn_gen == tfhp->tf_gen) {
- found = TRUE;
+ tmpfs_ref_node(node);
break;
}
}
TMPFS_UNLOCK(tmp);
- if (found)
- return (tmpfs_alloc_vp(mp, node, LK_EXCLUSIVE, vpp));
-
- return (EINVAL);
+ if (node != NULL) {
+ error = tmpfs_alloc_vp(mp, node, LK_EXCLUSIVE, vpp);
+ tmpfs_free_node(tmp, node);
+ } else
+ error = EINVAL;
+ return (error);
}
/* ARGSUSED2 */
diff --git a/sys/fs/tmpfs/tmpfs_vnops.c b/sys/fs/tmpfs/tmpfs_vnops.c
index 1b498de..becc72b 100644
--- a/sys/fs/tmpfs/tmpfs_vnops.c
+++ b/sys/fs/tmpfs/tmpfs_vnops.c
@@ -82,7 +82,8 @@ tmpfs_lookup(struct vop_cachedlookup_args *v)
struct vnode **vpp = v->a_vpp;
struct componentname *cnp = v->a_cnp;
struct tmpfs_dirent *de;
- struct tmpfs_node *dnode;
+ struct tmpfs_node *dnode, *pnode;
+ struct tmpfs_mount *tm;
int error;
dnode = VP_TO_TMPFS_DIR(dvp);
@@ -104,8 +105,12 @@ tmpfs_lookup(struct vop_cachedlookup_args *v)
goto out;
}
if (cnp->cn_flags & ISDOTDOT) {
+ tm = VFS_TO_TMPFS(dvp->v_mount);
+ pnode = dnode->tn_dir.tn_parent;
+ tmpfs_ref_node(pnode);
error = vn_vget_ino_gen(dvp, tmpfs_vn_get_ino_alloc,
- dnode->tn_dir.tn_parent, cnp->cn_lkflags, vpp);
+ pnode, cnp->cn_lkflags, vpp);
+ tmpfs_free_node(tm, pnode);
if (error != 0)
goto out;
} else if (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.') {
OpenPOWER on IntegriCloud