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; }