summaryrefslogtreecommitdiff
path: root/sys/fs/tmpfs
diff options
context:
space:
mode:
authorhannken <hannken@NetBSD.org>2015-07-06 10:07:12 +0000
committerhannken <hannken@NetBSD.org>2015-07-06 10:07:12 +0000
commitce08cdd2d24162c6d0c152dec4dbd4eded25ef3c (patch)
treec52f061b092248d80a56425159ad4961af1106ef /sys/fs/tmpfs
parent87ba93a45a993b59d6fe9f97e1ee174c8e61d07e (diff)
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 '..'.
Diffstat (limited to 'sys/fs/tmpfs')
-rw-r--r--sys/fs/tmpfs/tmpfs.h26
-rw-r--r--sys/fs/tmpfs/tmpfs_rename.c39
-rw-r--r--sys/fs/tmpfs/tmpfs_subr.c385
-rw-r--r--sys/fs/tmpfs/tmpfs_vfsops.c57
-rw-r--r--sys/fs/tmpfs/tmpfs_vnops.c40
5 files changed, 278 insertions, 269 deletions
diff --git a/sys/fs/tmpfs/tmpfs.h b/sys/fs/tmpfs/tmpfs.h
index c69902064a7..b95584bf5fd 100644
--- a/sys/fs/tmpfs/tmpfs.h
+++ b/sys/fs/tmpfs/tmpfs.h
@@ -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 *);
diff --git a/sys/fs/tmpfs/tmpfs_rename.c b/sys/fs/tmpfs/tmpfs_rename.c
index 7bb7649290c..79329f61d88 100644
--- a/sys/fs/tmpfs/tmpfs_rename.c
+++ b/sys/fs/tmpfs/tmpfs_rename.c
@@ -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, struct vnode *dvp,
{
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, struct vnode *dvp,
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, kauth_cred_t cred,
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, kauth_cred_t cred,
}
/* 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.
diff --git a/sys/fs/tmpfs/tmpfs_subr.c b/sys/fs/tmpfs/tmpfs_subr.c
index 373b352dab5..dce77daa9a5 100644
--- a/sys/fs/tmpfs/tmpfs_subr.c
+++ b/sys/fs/tmpfs/tmpfs_subr.c
@@ -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,v 1.98 2015/04/20 13:44:16 riastradh Ex
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_loadvnode(struct mount *mp, struct vnode *vp,
+ const void *key, size_t key_len, const void **new_key)
+{
+ 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;
+}
+
+/*
+ * tmpfs_newvnode: allocate a new inode of a specified type and
+ * attach the vonode.
*/
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_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_node_t *nnode;
+ 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;
- nnode = tmpfs_node_get(tmp);
- if (nnode == 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, tmpfs_node_t *node)
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_t **vpp, struct vattr *vap,
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_t **vpp, struct vattr *vap,
/* 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;
}
/*
diff --git a/sys/fs/tmpfs/tmpfs_vfsops.c b/sys/fs/tmpfs/tmpfs_vfsops.c
index b35d013aa0c..2b64497aee0 100644
--- a/sys/fs/tmpfs/tmpfs_vfsops.c
+++ b/sys/fs/tmpfs/tmpfs_vfsops.c
@@ -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 *path, void *data, size_t *data_len)
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 *path, void *data, size_t *data_len)
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 *path, void *data, size_t *data_len)
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 fid *fhp, vnode_t **vpp)
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,
diff --git a/sys/fs/tmpfs/tmpfs_vnops.c b/sys/fs/tmpfs/tmpfs_vnops.c
index 1da683e9cdf..9e85d290416 100644
--- a/sys/fs/tmpfs/tmpfs_vnops.c
+++ b/sys/fs/tmpfs/tmpfs_vnops.c
@@ -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;
}