Module Name:    src
Committed By:   hannken
Date:           Mon Jul  6 10:07:12 UTC 2015

Modified Files:
        src/sys/fs/tmpfs: tmpfs.h tmpfs_rename.c tmpfs_subr.c tmpfs_vfsops.c
            tmpfs_vnops.c

Log Message:
Change tmpfs to vcache.
- Use tmpfs node address as key.
- Remove tn_vlock, field tn_vnode now protected by vcache.
- Add a hold count to tmpfs node to prevent nodes from disappearing
  while tmpfs_fhtovp() trys to vcache_get() them.  Last holder
  destroys reclaimed nodes.
- Remove the now unneeded parent unlock/lock for lookup of '..'.


To generate a diff of this commit:
cvs rdiff -u -r1.51 -r1.52 src/sys/fs/tmpfs/tmpfs.h
cvs rdiff -u -r1.6 -r1.7 src/sys/fs/tmpfs/tmpfs_rename.c
cvs rdiff -u -r1.98 -r1.99 src/sys/fs/tmpfs/tmpfs_subr.c
cvs rdiff -u -r1.64 -r1.65 src/sys/fs/tmpfs/tmpfs_vfsops.c
cvs rdiff -u -r1.122 -r1.123 src/sys/fs/tmpfs/tmpfs_vnops.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/fs/tmpfs/tmpfs.h
diff -u src/sys/fs/tmpfs/tmpfs.h:1.51 src/sys/fs/tmpfs/tmpfs.h:1.52
--- src/sys/fs/tmpfs/tmpfs.h:1.51	Mon Jul  6 10:05:50 2015
+++ src/sys/fs/tmpfs/tmpfs.h	Mon Jul  6 10:07:12 2015
@@ -1,4 +1,4 @@
-/*	$NetBSD: tmpfs.h,v 1.51 2015/07/06 10:05:50 hannken Exp $	*/
+/*	$NetBSD: tmpfs.h,v 1.52 2015/07/06 10:07:12 hannken Exp $	*/
 
 /*
  * Copyright (c) 2005, 2006, 2007 The NetBSD Foundation, Inc.
@@ -72,7 +72,7 @@ TAILQ_HEAD(tmpfs_dir, tmpfs_dirent);
  * a particular type.
  *
  * All fields are protected by vnode lock.  The vnode association itself
- * is protected by tmpfs_node_t::tn_vlock.
+ * is protected by vcache.
  */
 typedef struct tmpfs_node {
 	LIST_ENTRY(tmpfs_node)	tn_entries;
@@ -88,9 +88,11 @@ typedef struct tmpfs_node {
 	 * tn_vnode.  It may be NULL when the node is unused (that is,
 	 * no vnode has been allocated or it has been reclaimed).
 	 */
-	kmutex_t		tn_vlock;
 	vnode_t *		tn_vnode;
 
+	/* Prevent node from being reclaimed. */
+	uint32_t		tn_holdcount;
+
 	/* Directory entry.  Only a hint, since hard link can have multiple. */
 	tmpfs_dirent_t *	tn_dirent_hint;
 
@@ -188,16 +190,12 @@ CTASSERT(TMPFS_MAXNAMLEN < UINT16_MAX);
 #define	TMPFS_UPDATE_CTIME	0x04
 
 /*
- * Bits indicating vnode reclamation and whiteout use for the directory.
+ * Bits indicating whiteout use for the directory.
  * We abuse tmpfs_node_t::tn_gen for that.
  */
-#define	TMPFS_RECLAIMING_BIT	(1U << 31)
-#define	TMPFS_WHITEOUT_BIT	(1U << 30)
+#define	TMPFS_WHITEOUT_BIT	(1U << 31)
 #define	TMPFS_NODE_GEN_MASK	(TMPFS_WHITEOUT_BIT - 1)
 
-#define	TMPFS_NODE_RECLAIMING(node) \
-    (((node)->tn_gen & TMPFS_RECLAIMING_BIT) != 0)
-
 #define	TMPFS_NODE_GEN(node) \
     ((node)->tn_gen & TMPFS_NODE_GEN_MASK)
 
@@ -205,6 +203,12 @@ CTASSERT(TMPFS_MAXNAMLEN < UINT16_MAX);
 #define	TMPFS_NODE_WHITEOUT	((tmpfs_node_t *)-1)
 
 /*
+ * Bit indicating this node must be reclaimed when holdcount reaches zero.
+ * Ored into tmpfs_node_t::tn_holdcount.
+ */
+#define TMPFS_NODE_RECLAIMED		(1U << 30)
+
+/*
  * Internal representation of a tmpfs mount point.
  */
 typedef struct tmpfs_mount {
@@ -242,15 +246,11 @@ typedef struct tmpfs_fid {
  * Prototypes for tmpfs_subr.c.
  */
 
-int		tmpfs_alloc_node(tmpfs_mount_t *, enum vtype, uid_t, gid_t,
-		    mode_t, char *, dev_t, tmpfs_node_t **);
 void		tmpfs_free_node(tmpfs_mount_t *, tmpfs_node_t *);
 
 int		tmpfs_construct_node(vnode_t *, vnode_t **, struct vattr *,
 		    struct componentname *, char *);
 
-int		tmpfs_vnode_get(struct mount *, tmpfs_node_t *, vnode_t **);
-
 int		tmpfs_alloc_dirent(tmpfs_mount_t *, const char *, uint16_t,
 		    tmpfs_dirent_t **);
 void		tmpfs_free_dirent(tmpfs_mount_t *, tmpfs_dirent_t *);

Index: src/sys/fs/tmpfs/tmpfs_rename.c
diff -u src/sys/fs/tmpfs/tmpfs_rename.c:1.6 src/sys/fs/tmpfs/tmpfs_rename.c:1.7
--- src/sys/fs/tmpfs/tmpfs_rename.c:1.6	Sat Nov 23 16:35:32 2013
+++ src/sys/fs/tmpfs/tmpfs_rename.c	Mon Jul  6 10:07:12 2015
@@ -1,4 +1,4 @@
-/*	$NetBSD: tmpfs_rename.c,v 1.6 2013/11/23 16:35:32 rmind Exp $	*/
+/*	$NetBSD: tmpfs_rename.c,v 1.7 2015/07/06 10:07:12 hannken Exp $	*/
 
 /*-
  * Copyright (c) 2012 The NetBSD Foundation, Inc.
@@ -34,7 +34,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: tmpfs_rename.c,v 1.6 2013/11/23 16:35:32 rmind Exp $");
+__KERNEL_RCSID(0, "$NetBSD: tmpfs_rename.c,v 1.7 2015/07/06 10:07:12 hannken Exp $");
 
 #include <sys/param.h>
 #include <sys/errno.h>
@@ -425,7 +425,7 @@ tmpfs_gro_lookup(struct mount *mp, struc
 {
 	struct tmpfs_dirent *dirent, **dep_ret = de_ret;
 	struct vnode *vp;
-	int error;
+	int error __diagused;
 
 	(void)mp;
 	KASSERT(mp != NULL);
@@ -439,27 +439,15 @@ tmpfs_gro_lookup(struct mount *mp, struc
 	if (dirent == NULL)
 		return ENOENT;
 
-	mutex_enter(&dirent->td_node->tn_vlock);
-	error = tmpfs_vnode_get(mp, dirent->td_node, &vp);
-	/* Note: tmpfs_vnode_get always releases dirent->td_node->tn_vlock.  */
+	error = vcache_get(mp, &dirent->td_node, sizeof(dirent->td_node), &vp);
 	if (error)
 		return error;
-
 	KASSERT(vp != NULL);
-	KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE);
 
 	/*
-	 * tmpfs_vnode_get returns this locked for us, which would be
-	 * convenient but for lossage in other file systems.  So, for
-	 * hysterical raisins, we have to unlock it here.  The caller
-	 * will lock it again, and we don't rely on any interesting
-	 * invariants in the interim, beyond that it won't vanish from
-	 * under us, which it won't because it's referenced.
-	 *
 	 * XXX Once namei is fixed, we can change the genfs_rename
-	 * protocol so that this unlock is not necessary.
+	 * protocol so that we have to lock vp her.
 	 */
-	VOP_UNLOCK(vp);
 
 	*dep_ret = dirent;
 	*vp_ret = vp;
@@ -491,7 +479,7 @@ tmpfs_gro_genealogy(struct mount *mp, ka
     struct vnode *fdvp, struct vnode *tdvp,
     struct vnode **intermediate_node_ret)
 {
-	struct vnode *vp;
+	struct vnode *vp, *ovp;
 	struct tmpfs_node *dnode;
 	int error;
 
@@ -549,15 +537,20 @@ tmpfs_gro_genealogy(struct mount *mp, ka
 		}
 
 		/* Neither -- keep ascending the family tree.  */
-		mutex_enter(&dnode->tn_vlock);
-		vput(vp);
-		vp = NULL;	/* Just in case, for the kassert above...  */
-		error = tmpfs_vnode_get(mp, dnode, &vp);
+		ovp = vp;
+		vp = NULL;
+		error = vcache_get(mp, &dnode, sizeof(dnode), &vp);
+		vput(ovp);
 		if (error)
 			return error;
+		error = vn_lock(vp, LK_EXCLUSIVE);
+		if (error) {
+			vrele(vp);
+			return error;
+		}
 
 		/*
-		 * tmpfs_vnode_get only guarantees that dnode will not
+		 * vcache_get only guarantees that dnode will not
 		 * be freed while we get a vnode for it.  It does not
 		 * preserve any other invariants, so we must check
 		 * whether the parent has been removed in the meantime.

Index: src/sys/fs/tmpfs/tmpfs_subr.c
diff -u src/sys/fs/tmpfs/tmpfs_subr.c:1.98 src/sys/fs/tmpfs/tmpfs_subr.c:1.99
--- src/sys/fs/tmpfs/tmpfs_subr.c:1.98	Mon Apr 20 13:44:16 2015
+++ src/sys/fs/tmpfs/tmpfs_subr.c	Mon Jul  6 10:07:12 2015
@@ -1,4 +1,4 @@
-/*	$NetBSD: tmpfs_subr.c,v 1.98 2015/04/20 13:44:16 riastradh Exp $	*/
+/*	$NetBSD: tmpfs_subr.c,v 1.99 2015/07/06 10:07:12 hannken Exp $	*/
 
 /*
  * Copyright (c) 2005-2013 The NetBSD Foundation, Inc.
@@ -64,17 +64,16 @@
  *	If an inode has references within the file system (tn_links > 0) and
  *	its inactive vnode gets reclaimed/recycled - then the association is
  *	broken in tmpfs_reclaim().  In such case, an inode will always pass
- *	tmpfs_lookup() and thus tmpfs_vnode_get() to associate a new vnode.
+ *	tmpfs_lookup() and thus vcache_get() to associate a new vnode.
  *
  * Lock order
  *
- *	tmpfs_node_t::tn_vlock ->
- *		vnode_t::v_vlock ->
- *			vnode_t::v_interlock
+ *	vnode_t::v_vlock ->
+ *		vnode_t::v_interlock
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: tmpfs_subr.c,v 1.98 2015/04/20 13:44:16 riastradh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: tmpfs_subr.c,v 1.99 2015/07/06 10:07:12 hannken Exp $");
 
 #include <sys/param.h>
 #include <sys/cprng.h>
@@ -102,115 +101,197 @@ __KERNEL_RCSID(0, "$NetBSD: tmpfs_subr.c
 static void	tmpfs_dir_putseq(tmpfs_node_t *, tmpfs_dirent_t *);
 
 /*
- * tmpfs_alloc_node: allocate a new inode of a specified type and
- * insert it into the list of specified mount point.
+ * Initialize vnode with tmpfs node.
+ */
+static void
+tmpfs_init_vnode(struct vnode *vp, tmpfs_node_t *node)
+{
+	kmutex_t *slock;
+
+	KASSERT(node->tn_vnode == NULL);
+
+	/* Share the interlock with the node. */
+	if (node->tn_type == VREG) {
+		slock = node->tn_spec.tn_reg.tn_aobj->vmobjlock;
+		mutex_obj_hold(slock);
+		uvm_obj_setlock(&vp->v_uobj, slock);
+	}
+
+	vp->v_tag = VT_TMPFS;
+	vp->v_type = node->tn_type;
+
+	/* Type-specific initialization. */
+	switch (vp->v_type) {
+	case VBLK:
+	case VCHR:
+		vp->v_op = tmpfs_specop_p;
+		spec_node_init(vp, node->tn_spec.tn_dev.tn_rdev);
+		break;
+	case VFIFO:
+		vp->v_op = tmpfs_fifoop_p;
+		break;
+	case VDIR:
+		if (node->tn_spec.tn_dir.tn_parent == node)
+			vp->v_vflag |= VV_ROOT;
+		/* FALLTHROUGH */
+	case VLNK:
+	case VREG:
+	case VSOCK:
+		vp->v_op = tmpfs_vnodeop_p;
+		break;
+	default:
+		panic("bad node type %d", vp->v_type);
+		break;
+	}
+
+	vp->v_data = node;
+	node->tn_vnode = vp;
+	uvm_vnp_setsize(vp, node->tn_size);
+}
+
+/*
+ * tmpfs_loadvnode: initialise a vnode for a specified inode.
  */
 int
-tmpfs_alloc_node(tmpfs_mount_t *tmp, enum vtype type, uid_t uid, gid_t gid,
-    mode_t mode, char *target, dev_t rdev, tmpfs_node_t **node)
+tmpfs_loadvnode(struct mount *mp, struct vnode *vp,
+    const void *key, size_t key_len, const void **new_key)
 {
-	tmpfs_node_t *nnode;
+	tmpfs_node_t *node;
+
+	KASSERT(key_len == sizeof(node));
+	memcpy(&node, key, key_len);
+
+	if (node->tn_links == 0)
+		return ENOENT;
+
+	tmpfs_init_vnode(vp, node);
+
+	*new_key = &vp->v_data;
+
+	return 0;
+}
 
-	nnode = tmpfs_node_get(tmp);
-	if (nnode == NULL) {
+/*
+ * tmpfs_newvnode: allocate a new inode of a specified type and
+ * attach the vonode.
+ */
+int
+tmpfs_newvnode(struct mount *mp, struct vnode *dvp, struct vnode *vp,
+    struct vattr *vap, kauth_cred_t cred,
+    size_t *key_len, const void **new_key)
+{
+	tmpfs_mount_t *tmp = VFS_TO_TMPFS(mp);
+	tmpfs_node_t *node, *dnode;
+
+	if (dvp != NULL) {
+		KASSERT(VOP_ISLOCKED(dvp));
+		dnode = VP_TO_TMPFS_DIR(dvp);
+		if (dnode->tn_links == 0)
+			return ENOENT;
+		if (vap->va_type == VDIR) {
+			/* Check for maximum links limit. */
+			if (dnode->tn_links == LINK_MAX)
+				return EMLINK;
+			KASSERT(dnode->tn_links < LINK_MAX);
+		}
+	} else
+		dnode = NULL;
+
+	node = tmpfs_node_get(tmp);
+	if (node == NULL)
 		return ENOSPC;
-	}
 
 	/* Initially, no references and no associations. */
-	nnode->tn_links = 0;
-	nnode->tn_vnode = NULL;
-	nnode->tn_dirent_hint = NULL;
+	node->tn_links = 0;
+	node->tn_vnode = NULL;
+	node->tn_holdcount = 0;
+	node->tn_dirent_hint = NULL;
 
 	/*
 	 * XXX Where the pool is backed by a map larger than (4GB *
-	 * sizeof(*nnode)), this may produce duplicate inode numbers
+	 * sizeof(*node)), this may produce duplicate inode numbers
 	 * for applications that do not understand 64-bit ino_t.
 	 */
-	nnode->tn_id = (ino_t)((uintptr_t)nnode / sizeof(*nnode));
+	node->tn_id = (ino_t)((uintptr_t)node / sizeof(*node));
 	/*
 	 * Make sure the generation number is not zero.
 	 * tmpfs_inactive() uses generation zero to mark dead nodes.
 	 */
 	do {
-		nnode->tn_gen = TMPFS_NODE_GEN_MASK & cprng_fast32();
-	} while (nnode->tn_gen == 0);
+		node->tn_gen = TMPFS_NODE_GEN_MASK & cprng_fast32();
+	} while (node->tn_gen == 0);
 
 	/* Generic initialization. */
-	nnode->tn_type = type;
-	nnode->tn_size = 0;
-	nnode->tn_flags = 0;
-	nnode->tn_lockf = NULL;
-
-	vfs_timestamp(&nnode->tn_atime);
-	nnode->tn_birthtime = nnode->tn_atime;
-	nnode->tn_ctime = nnode->tn_atime;
-	nnode->tn_mtime = nnode->tn_atime;
-
-	KASSERT(uid != VNOVAL && gid != VNOVAL && mode != VNOVAL);
-	nnode->tn_uid = uid;
-	nnode->tn_gid = gid;
-	nnode->tn_mode = mode;
+	KASSERT(vap->va_type != VNOVAL);
+	node->tn_type = vap->va_type;
+	node->tn_size = 0;
+	node->tn_flags = 0;
+	node->tn_lockf = NULL;
+
+	vfs_timestamp(&node->tn_atime);
+	node->tn_birthtime = node->tn_atime;
+	node->tn_ctime = node->tn_atime;
+	node->tn_mtime = node->tn_atime;
+
+	if (dvp == NULL) {
+		KASSERT(vap->va_uid != VNOVAL && vap->va_gid != VNOVAL);
+		node->tn_uid = vap->va_uid;
+		node->tn_gid = vap->va_gid;
+		vp->v_vflag |= VV_ROOT;
+	} else {
+		KASSERT(dnode != NULL);
+		node->tn_uid = kauth_cred_geteuid(cred);
+		node->tn_gid = dnode->tn_gid;
+	}
+	KASSERT(vap->va_mode != VNOVAL);
+	node->tn_mode = vap->va_mode;
 
 	/* Type-specific initialization. */
-	switch (nnode->tn_type) {
+	switch (node->tn_type) {
 	case VBLK:
 	case VCHR:
 		/* Character/block special device. */
-		KASSERT(rdev != VNOVAL);
-		nnode->tn_spec.tn_dev.tn_rdev = rdev;
+		KASSERT(vap->va_rdev != VNOVAL);
+		node->tn_spec.tn_dev.tn_rdev = vap->va_rdev;
 		break;
 	case VDIR:
 		/* Directory. */
-		TAILQ_INIT(&nnode->tn_spec.tn_dir.tn_dir);
-		nnode->tn_spec.tn_dir.tn_parent = NULL;
-		nnode->tn_spec.tn_dir.tn_seq_arena = NULL;
-		nnode->tn_spec.tn_dir.tn_next_seq = TMPFS_DIRSEQ_START;
-		nnode->tn_spec.tn_dir.tn_readdir_lastp = NULL;
+		TAILQ_INIT(&node->tn_spec.tn_dir.tn_dir);
+		node->tn_spec.tn_dir.tn_parent = NULL;
+		node->tn_spec.tn_dir.tn_seq_arena = NULL;
+		node->tn_spec.tn_dir.tn_next_seq = TMPFS_DIRSEQ_START;
+		node->tn_spec.tn_dir.tn_readdir_lastp = NULL;
 
 		/* Extra link count for the virtual '.' entry. */
-		nnode->tn_links++;
+		node->tn_links++;
 		break;
 	case VFIFO:
 	case VSOCK:
 		break;
 	case VLNK:
-		/* Symbolic link.  Target specifies the file name. */
-		KASSERT(target != NULL);
-		nnode->tn_size = strlen(target);
-
-		if (nnode->tn_size == 0) {
-			/* Zero-length targets are supported. */
-			nnode->tn_spec.tn_lnk.tn_link = NULL;
-			break;
-		}
-
-		KASSERT(nnode->tn_size < MAXPATHLEN);
-
-		nnode->tn_spec.tn_lnk.tn_link =
-		    tmpfs_strname_alloc(tmp, nnode->tn_size);
-		if (nnode->tn_spec.tn_lnk.tn_link == NULL) {
-			tmpfs_node_put(tmp, nnode);
-			return ENOSPC;
-		}
-		memcpy(nnode->tn_spec.tn_lnk.tn_link, target, nnode->tn_size);
+		node->tn_size = 0;
+		node->tn_spec.tn_lnk.tn_link = NULL;
 		break;
 	case VREG:
 		/* Regular file.  Create an underlying UVM object. */
-		nnode->tn_spec.tn_reg.tn_aobj =
+		node->tn_spec.tn_reg.tn_aobj =
 		    uao_create(INT32_MAX - PAGE_SIZE, 0);
-		nnode->tn_spec.tn_reg.tn_aobj_pages = 0;
+		node->tn_spec.tn_reg.tn_aobj_pages = 0;
 		break;
 	default:
-		KASSERT(false);
+		panic("bad node type %d", vp->v_type);
+		break;
 	}
 
-	mutex_init(&nnode->tn_vlock, MUTEX_DEFAULT, IPL_NONE);
+	tmpfs_init_vnode(vp, node);
 
 	mutex_enter(&tmp->tm_lock);
-	LIST_INSERT_HEAD(&tmp->tm_nodes, nnode, tn_entries);
+	LIST_INSERT_HEAD(&tmp->tm_nodes, node, tn_entries);
 	mutex_exit(&tmp->tm_lock);
 
-	*node = nnode;
+	*key_len = sizeof(vp->v_data);
+	*new_key = &vp->v_data;
+
 	return 0;
 }
 
@@ -222,8 +303,15 @@ void
 tmpfs_free_node(tmpfs_mount_t *tmp, tmpfs_node_t *node)
 {
 	size_t objsz;
+	uint32_t hold;
 
 	mutex_enter(&tmp->tm_lock);
+	hold = atomic_or_32_nv(&node->tn_holdcount, TMPFS_NODE_RECLAIMED);
+	/* Defer destruction to last thread holding this node. */
+	if (hold != TMPFS_NODE_RECLAIMED) {
+		mutex_exit(&tmp->tm_lock);
+		return;
+	}
 	LIST_REMOVE(node, tn_entries);
 	mutex_exit(&tmp->tm_lock);
 
@@ -260,94 +348,10 @@ tmpfs_free_node(tmpfs_mount_t *tmp, tmpf
 	KASSERT(node->tn_vnode == NULL);
 	KASSERT(node->tn_links == 0);
 
-	mutex_destroy(&node->tn_vlock);
 	tmpfs_node_put(tmp, node);
 }
 
 /*
- * tmpfs_vnode_get: allocate or reclaim a vnode for a specified inode.
- *
- * => Must be called with tmpfs_node_t::tn_vlock held.
- * => Returns vnode (*vpp) locked.
- */
-int
-tmpfs_vnode_get(struct mount *mp, tmpfs_node_t *node, vnode_t **vpp)
-{
-	vnode_t *vp;
-	kmutex_t *slock;
-	int error;
-again:
-	/* If there is already a vnode, try to reclaim it. */
-	if ((vp = node->tn_vnode) != NULL) {
-		atomic_or_32(&node->tn_gen, TMPFS_RECLAIMING_BIT);
-		mutex_enter(vp->v_interlock);
-		mutex_exit(&node->tn_vlock);
-		error = vget(vp, 0, true /* wait */);
-		if (error == ENOENT) {
-			mutex_enter(&node->tn_vlock);
-			goto again;
-		}
-		vn_lock(vp, LK_EXCLUSIVE);
-		atomic_and_32(&node->tn_gen, ~TMPFS_RECLAIMING_BIT);
-		*vpp = vp;
-		return error;
-	}
-	if (TMPFS_NODE_RECLAIMING(node)) {
-		atomic_and_32(&node->tn_gen, ~TMPFS_RECLAIMING_BIT);
-	}
-
-	/*
-	 * Get a new vnode and associate it with our inode.  Share the
-	 * lock with underlying UVM object, if there is one (VREG case).
-	 */
-	if (node->tn_type == VREG) {
-		struct uvm_object *uobj = node->tn_spec.tn_reg.tn_aobj;
-		slock = uobj->vmobjlock;
-	} else {
-		slock = NULL;
-	}
-	error = getnewvnode(VT_TMPFS, mp, tmpfs_vnodeop_p, slock, &vp);
-	if (error) {
-		mutex_exit(&node->tn_vlock);
-		return error;
-	}
-
-	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
-	vp->v_type = node->tn_type;
-
-	/* Type-specific initialization. */
-	switch (node->tn_type) {
-	case VBLK:
-	case VCHR:
-		vp->v_op = tmpfs_specop_p;
-		spec_node_init(vp, node->tn_spec.tn_dev.tn_rdev);
-		break;
-	case VDIR:
-		vp->v_vflag |= node->tn_spec.tn_dir.tn_parent == node ?
-		    VV_ROOT : 0;
-		break;
-	case VFIFO:
-		vp->v_op = tmpfs_fifoop_p;
-		break;
-	case VLNK:
-	case VREG:
-	case VSOCK:
-		break;
-	default:
-		KASSERT(false);
-	}
-
-	uvm_vnp_setsize(vp, node->tn_size);
-	vp->v_data = node;
-	node->tn_vnode = vp;
-	mutex_exit(&node->tn_vlock);
-
-	KASSERT(VOP_ISLOCKED(vp));
-	*vpp = vp;
-	return 0;
-}
-
-/*
  * tmpfs_construct_node: allocate a new file of specified type and adds it
  * into the parent directory.
  *
@@ -360,50 +364,54 @@ tmpfs_construct_node(vnode_t *dvp, vnode
 	tmpfs_mount_t *tmp = VFS_TO_TMPFS(dvp->v_mount);
 	tmpfs_node_t *dnode = VP_TO_TMPFS_DIR(dvp), *node;
 	tmpfs_dirent_t *de, *wde;
+	char *slink = NULL;
+	int ssize = 0;
 	int error;
 
-	KASSERT(VOP_ISLOCKED(dvp));
-	*vpp = NULL;
-
-	/*
-	 * If directory was removed, prevent from node creation.  The vnode
-	 * might still be referenced, but it is about to be reclaimed.
-	 */
-	if (dnode->tn_links == 0) {
-		error = ENOENT;
-		goto out;
-	}
-
-	/* Check for the maximum number of links limit. */
-	if (vap->va_type == VDIR) {
-		/* Check for maximum links limit. */
-		if (dnode->tn_links == LINK_MAX) {
-			error = EMLINK;
-			goto out;
+	/* Allocate symlink target. */
+	if (target != NULL) {
+		KASSERT(vap->va_type == VLNK);
+		ssize = strlen(target);
+		KASSERT(ssize < MAXPATHLEN);
+		if (ssize > 0) {
+			slink = tmpfs_strname_alloc(tmp, ssize);
+			if (slink == NULL)
+				return ENOSPC;
+			memcpy(slink, target, ssize);
 		}
-		KASSERT(dnode->tn_links < LINK_MAX);
 	}
 
-	/* Allocate a node that represents the new file. */
-	error = tmpfs_alloc_node(tmp, vap->va_type, kauth_cred_geteuid(cnp->cn_cred),
-	    dnode->tn_gid, vap->va_mode, target, vap->va_rdev, &node);
-	if (error)
-		goto out;
-
 	/* Allocate a directory entry that points to the new file. */
 	error = tmpfs_alloc_dirent(tmp, cnp->cn_nameptr, cnp->cn_namelen, &de);
 	if (error) {
-		tmpfs_free_node(tmp, node);
-		goto out;
+		if (slink != NULL)
+			tmpfs_strname_free(tmp, slink, ssize);
+		return error;
 	}
 
-	/* Get a vnode for the new file. */
-	mutex_enter(&node->tn_vlock);
-	error = tmpfs_vnode_get(dvp->v_mount, node, vpp);
+	/* Allocate a vnode that represents the new file. */
+	error = vcache_new(dvp->v_mount, dvp, vap, cnp->cn_cred, vpp);
 	if (error) {
+		if (slink != NULL)
+			tmpfs_strname_free(tmp, slink, ssize);
 		tmpfs_free_dirent(tmp, de);
-		tmpfs_free_node(tmp, node);
-		goto out;
+		return error;
+	}
+	error = vn_lock(*vpp, LK_EXCLUSIVE);
+	if (error) {
+		vrele(*vpp);
+		*vpp = NULL;
+		if (slink != NULL)
+			tmpfs_strname_free(tmp, slink, ssize);
+		tmpfs_free_dirent(tmp, de);
+		return error;
+	}
+
+	node = VP_TO_TMPFS_NODE(*vpp);
+
+	if (slink != NULL) {
+		node->tn_spec.tn_lnk.tn_link = slink;
+		node->tn_size = ssize;
 	}
 
 	/* Remove whiteout before adding the new entry. */
@@ -423,11 +431,10 @@ tmpfs_construct_node(vnode_t *dvp, vnode
 
 	/* Update the parent's timestamps. */
 	tmpfs_update(dvp, TMPFS_UPDATE_MTIME | TMPFS_UPDATE_CTIME);
-out:
-	if (error == 0)
-		VOP_UNLOCK(*vpp);
 
-	return error;
+	VOP_UNLOCK(*vpp);
+
+	return 0;
 }
 
 /*

Index: src/sys/fs/tmpfs/tmpfs_vfsops.c
diff -u src/sys/fs/tmpfs/tmpfs_vfsops.c:1.64 src/sys/fs/tmpfs/tmpfs_vfsops.c:1.65
--- src/sys/fs/tmpfs/tmpfs_vfsops.c:1.64	Mon Jul  6 10:05:50 2015
+++ src/sys/fs/tmpfs/tmpfs_vfsops.c	Mon Jul  6 10:07:12 2015
@@ -1,4 +1,4 @@
-/*	$NetBSD: tmpfs_vfsops.c,v 1.64 2015/07/06 10:05:50 hannken Exp $	*/
+/*	$NetBSD: tmpfs_vfsops.c,v 1.65 2015/07/06 10:07:12 hannken Exp $	*/
 
 /*
  * Copyright (c) 2005, 2006, 2007 The NetBSD Foundation, Inc.
@@ -42,15 +42,17 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: tmpfs_vfsops.c,v 1.64 2015/07/06 10:05:50 hannken Exp $");
+__KERNEL_RCSID(0, "$NetBSD: tmpfs_vfsops.c,v 1.65 2015/07/06 10:07:12 hannken Exp $");
 
 #include <sys/param.h>
+#include <sys/atomic.h>
 #include <sys/types.h>
 #include <sys/kmem.h>
 #include <sys/mount.h>
 #include <sys/stat.h>
 #include <sys/systm.h>
 #include <sys/vnode.h>
+#include <sys/kauth.h>
 #include <sys/module.h>
 
 #include <miscfs/genfs/genfs.h>
@@ -86,6 +88,8 @@ tmpfs_mount(struct mount *mp, const char
 	struct tmpfs_args *args = data;
 	tmpfs_mount_t *tmp;
 	tmpfs_node_t *root;
+	struct vattr va;
+	struct vnode *vp;
 	uint64_t memlimit;
 	ino_t nodes;
 	int error;
@@ -171,11 +175,16 @@ tmpfs_mount(struct mount *mp, const char
 
 	mutex_init(&tmp->tm_lock, MUTEX_DEFAULT, IPL_NONE);
 	tmpfs_mntmem_init(tmp, memlimit);
+	mp->mnt_data = tmp;
 
 	/* Allocate the root node. */
-	error = tmpfs_alloc_node(tmp, VDIR, args->ta_root_uid,
-	    args->ta_root_gid, args->ta_root_mode & ALLPERMS, NULL,
-	    VNOVAL, &root);
+	vattr_null(&va);
+	va.va_type = VDIR;
+	va.va_mode = args->ta_root_mode & ALLPERMS;
+	va.va_uid = args->ta_root_uid;
+	va.va_gid = args->ta_root_gid;
+	error = vcache_new(mp, NULL, &va, NOCRED, &vp);
+	root = VP_TO_TMPFS_NODE(vp);
 	KASSERT(error == 0 && root != NULL);
 
 	/*
@@ -186,8 +195,8 @@ tmpfs_mount(struct mount *mp, const char
 	root->tn_links++;
 	root->tn_spec.tn_dir.tn_parent = root;
 	tmp->tm_root = root;
+	vrele(vp);
 
-	mp->mnt_data = tmp;
 	mp->mnt_flag |= MNT_LOCAL;
 	mp->mnt_stat.f_namemax = TMPFS_MAXNAMLEN;
 	mp->mnt_fs_bshift = PAGE_SHIFT;
@@ -271,9 +280,19 @@ int
 tmpfs_root(struct mount *mp, vnode_t **vpp)
 {
 	tmpfs_node_t *node = VFS_TO_TMPFS(mp)->tm_root;
+	int error;
+
+	error = vcache_get(mp, &node, sizeof(node), vpp);
+	if (error)
+		return error;
+	error = vn_lock(*vpp, LK_EXCLUSIVE);
+	if (error) {
+		vrele(*vpp);
+		*vpp = NULL;
+		return error;
+	}
 
-	mutex_enter(&node->tn_vlock);
-	return tmpfs_vnode_get(mp, node, vpp);
+	return 0;
 }
 
 int
@@ -299,17 +318,29 @@ tmpfs_fhtovp(struct mount *mp, struct fi
 	mutex_enter(&tmp->tm_lock);
 	LIST_FOREACH(node, &tmp->tm_nodes, tn_entries) {
 		if (node->tn_id == tfh.tf_id) {
-			mutex_enter(&node->tn_vlock);
+			/* Prevent this node from disappearing. */
+			atomic_inc_32(&node->tn_holdcount);
 			break;
 		}
 	}
 	mutex_exit(&tmp->tm_lock);
-
 	if (node == NULL)
 		return ESTALE;
-	/* Will release the tn_vlock. */
-	if ((error = tmpfs_vnode_get(mp, node, vpp)) != 0)
+
+	error = vcache_get(mp, &node, sizeof(node), vpp);
+	/* If this node has been reclaimed free it now. */
+	if (atomic_dec_32_nv(&node->tn_holdcount) == TMPFS_NODE_RECLAIMED) {
+		KASSERT(error != 0);
+		tmpfs_free_node(tmp, node);
+	}
+	if (error)
+		return (error == ENOENT ? ESTALE : error);
+	error = vn_lock(*vpp, LK_EXCLUSIVE);
+	if (error) {
+		vrele(*vpp);
+		*vpp = NULL;
 		return error;
+	}
 	if (TMPFS_NODE_GEN(node) != tfh.tf_gen) {
 		vput(*vpp);
 		*vpp = NULL;
@@ -411,6 +442,8 @@ struct vfsops tmpfs_vfsops = {
 	.vfs_statvfs = tmpfs_statvfs,
 	.vfs_sync = tmpfs_sync,
 	.vfs_vget = tmpfs_vget,
+	.vfs_loadvnode = tmpfs_loadvnode,
+	.vfs_newvnode = tmpfs_newvnode,
 	.vfs_fhtovp = tmpfs_fhtovp,
 	.vfs_vptofh = tmpfs_vptofh,
 	.vfs_init = tmpfs_init,

Index: src/sys/fs/tmpfs/tmpfs_vnops.c
diff -u src/sys/fs/tmpfs/tmpfs_vnops.c:1.122 src/sys/fs/tmpfs/tmpfs_vnops.c:1.123
--- src/sys/fs/tmpfs/tmpfs_vnops.c:1.122	Mon Apr 20 23:03:08 2015
+++ src/sys/fs/tmpfs/tmpfs_vnops.c	Mon Jul  6 10:07:12 2015
@@ -1,4 +1,4 @@
-/*	$NetBSD: tmpfs_vnops.c,v 1.122 2015/04/20 23:03:08 riastradh Exp $	*/
+/*	$NetBSD: tmpfs_vnops.c,v 1.123 2015/07/06 10:07:12 hannken Exp $	*/
 
 /*
  * Copyright (c) 2005, 2006, 2007 The NetBSD Foundation, Inc.
@@ -35,7 +35,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: tmpfs_vnops.c,v 1.122 2015/04/20 23:03:08 riastradh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: tmpfs_vnops.c,v 1.123 2015/07/06 10:07:12 hannken Exp $");
 
 #include <sys/param.h>
 #include <sys/dirent.h>
@@ -173,10 +173,10 @@ tmpfs_lookup(void *v)
 	if (cachefound && *vpp == NULLVP) {
 		/* Negative cache hit. */
 		error = ENOENT;
-		goto out_unlocked;
+		goto out;
 	} else if (cachefound) {
 		error = 0;
-		goto out_unlocked;
+		goto out;
 	}
 
 	/*
@@ -205,21 +205,8 @@ tmpfs_lookup(void *v)
 			goto out;
 		}
 
-		/*
-		 * Lock the parent tn_vlock before releasing the vnode lock,
-		 * and thus prevent parent from disappearing.
-		 */
-		mutex_enter(&pnode->tn_vlock);
-		VOP_UNLOCK(dvp);
-
-		/*
-		 * Get a vnode of the '..' entry and re-acquire the lock.
-		 * Release the tn_vlock.
-		 */
-		error = tmpfs_vnode_get(dvp->v_mount, pnode, vpp);
-		vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY);
+		error = vcache_get(dvp->v_mount, &pnode, sizeof(pnode), vpp);
 		goto out;
-
 	} else if (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.') {
 		/*
 		 * Lookup of "." case.
@@ -292,8 +279,7 @@ tmpfs_lookup(void *v)
 	}
 
 	/* Get a vnode for the matching entry. */
-	mutex_enter(&tnode->tn_vlock);
-	error = tmpfs_vnode_get(dvp->v_mount, tnode, vpp);
+	error = vcache_get(dvp->v_mount, &tnode, sizeof(tnode), vpp);
 done:
 	/*
 	 * Cache the result, unless request was for creation (as it does
@@ -304,9 +290,6 @@ done:
 			    cnp->cn_flags);
 	}
 out:
-	if (error == 0 && *vpp != dvp)
-		VOP_UNLOCK(*vpp);
-out_unlocked:
 	KASSERT(VOP_ISLOCKED(dvp));
 
 	return error;
@@ -1080,22 +1063,15 @@ tmpfs_reclaim(void *v)
 	vnode_t *vp = ap->a_vp;
 	tmpfs_mount_t *tmp = VFS_TO_TMPFS(vp->v_mount);
 	tmpfs_node_t *node = VP_TO_TMPFS_NODE(vp);
-	bool recycle;
-
-	mutex_enter(&node->tn_vlock);
 
 	/* Disassociate inode from vnode. */
 	node->tn_vnode = NULL;
+	vcache_remove(vp->v_mount, &node, sizeof(node));
 	vp->v_data = NULL;
 
 	/* If inode is not referenced, i.e. no links, then destroy it. */
-	recycle = node->tn_links == 0 && TMPFS_NODE_RECLAIMING(node) == 0;
-
-	mutex_exit(&node->tn_vlock);
-
-	if (recycle) {
+	if (node->tn_links == 0)
 		tmpfs_free_node(tmp, node);
-	}
 	return 0;
 }
 

Reply via email to