Module Name:    src
Committed By:   rmind
Date:           Sat Nov 23 16:35:32 UTC 2013

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

Log Message:
- Simplify tmpfs_update(), eliminate tmpfs_note_t::tn_status and deferred
  timestamp updates.  Fix some incorrect updates and plug some missing ones.
  Should fix PR/48385.
- tmpfs_rmdir: avoid O(n) scan when the directory is not empty and whiteout
  entries were never added.


To generate a diff of this commit:
cvs rdiff -u -r1.47 -r1.48 src/sys/fs/tmpfs/tmpfs.h
cvs rdiff -u -r1.9 -r1.10 src/sys/fs/tmpfs/tmpfs_fifoops.c
cvs rdiff -u -r1.5 -r1.6 src/sys/fs/tmpfs/tmpfs_rename.c
cvs rdiff -u -r1.10 -r1.11 src/sys/fs/tmpfs/tmpfs_specops.c
cvs rdiff -u -r1.89 -r1.90 src/sys/fs/tmpfs/tmpfs_subr.c
cvs rdiff -u -r1.54 -r1.55 src/sys/fs/tmpfs/tmpfs_vfsops.c
cvs rdiff -u -r1.106 -r1.107 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.47 src/sys/fs/tmpfs/tmpfs.h:1.48
--- src/sys/fs/tmpfs/tmpfs.h:1.47	Mon Nov 18 01:39:34 2013
+++ src/sys/fs/tmpfs/tmpfs.h	Sat Nov 23 16:35:32 2013
@@ -1,4 +1,4 @@
-/*	$NetBSD: tmpfs.h,v 1.47 2013/11/18 01:39:34 rmind Exp $	*/
+/*	$NetBSD: tmpfs.h,v 1.48 2013/11/23 16:35:32 rmind Exp $	*/
 
 /*
  * Copyright (c) 2005, 2006, 2007 The NetBSD Foundation, Inc.
@@ -101,9 +101,6 @@ typedef struct tmpfs_node {
 	ino_t			tn_id;
 	uint32_t		tn_gen;
 
-	/* Inode status flags (for operations in delayed manner). */
-	unsigned		tn_status;
-
 	/* The inode size. */
 	off_t			tn_size;
 
@@ -183,20 +180,18 @@ CTASSERT(TMPFS_MAXNAMLEN < UINT16_MAX);
 /* Mark to indicate that the number is not set. */
 #define	TMPFS_DIRSEQ_NONE	(1U << 31)
 
-/* Status flags. */
-#define	TMPFS_NODE_ACCESSED	0x01
-#define	TMPFS_NODE_MODIFIED	0x02
-#define	TMPFS_NODE_CHANGED	0x04
-
-#define	TMPFS_NODE_STATUSALL	\
-    (TMPFS_NODE_ACCESSED | TMPFS_NODE_MODIFIED | TMPFS_NODE_CHANGED)
+/* Flags: time update requests. */
+#define	TMPFS_UPDATE_ATIME	0x01
+#define	TMPFS_UPDATE_MTIME	0x02
+#define	TMPFS_UPDATE_CTIME	0x04
 
 /*
- * Bit indicating vnode reclamation.
+ * Bits indicating vnode reclamation and whiteout use for the directory.
  * We abuse tmpfs_node_t::tn_gen for that.
  */
 #define	TMPFS_RECLAIMING_BIT	(1U << 31)
-#define	TMPFS_NODE_GEN_MASK	(TMPFS_RECLAIMING_BIT - 1)
+#define	TMPFS_WHITEOUT_BIT	(1U << 30)
+#define	TMPFS_NODE_GEN_MASK	(TMPFS_WHITEOUT_BIT - 1)
 
 #define	TMPFS_NODE_RECLAIMING(node) \
     (((node)->tn_gen & TMPFS_RECLAIMING_BIT) != 0)
@@ -249,7 +244,7 @@ int		tmpfs_alloc_node(tmpfs_mount_t *, e
 		    mode_t, char *, dev_t, tmpfs_node_t **);
 void		tmpfs_free_node(tmpfs_mount_t *, tmpfs_node_t *);
 
-int		tmpfs_alloc_file(vnode_t *, vnode_t **, struct vattr *,
+int		tmpfs_construct_node(vnode_t *, vnode_t **, struct vattr *,
 		    struct componentname *, char *);
 
 int		tmpfs_vnode_get(struct mount *, tmpfs_node_t *, vnode_t **);
@@ -268,7 +263,6 @@ tmpfs_dirent_t *tmpfs_dir_lookupbyseq(tm
 int		tmpfs_dir_getdents(tmpfs_node_t *, struct uio *, off_t *);
 
 int		tmpfs_reg_resize(vnode_t *, off_t);
-int		tmpfs_truncate(vnode_t *, off_t);
 
 int		tmpfs_chflags(vnode_t *, int, kauth_cred_t, lwp_t *);
 int		tmpfs_chmod(vnode_t *, mode_t, kauth_cred_t, lwp_t *);
@@ -277,8 +271,7 @@ int		tmpfs_chsize(vnode_t *, u_quad_t, k
 int		tmpfs_chtimes(vnode_t *, const struct timespec *,
 		    const struct timespec *, const struct timespec *, int,
 		    kauth_cred_t, lwp_t *);
-void		tmpfs_update(vnode_t *, const struct timespec *,
-		    const struct timespec *, const struct timespec *, int);
+void		tmpfs_update(vnode_t *, unsigned);
 
 /*
  * Prototypes for tmpfs_mem.c.

Index: src/sys/fs/tmpfs/tmpfs_fifoops.c
diff -u src/sys/fs/tmpfs/tmpfs_fifoops.c:1.9 src/sys/fs/tmpfs/tmpfs_fifoops.c:1.10
--- src/sys/fs/tmpfs/tmpfs_fifoops.c:1.9	Tue May 24 20:17:49 2011
+++ src/sys/fs/tmpfs/tmpfs_fifoops.c	Sat Nov 23 16:35:32 2013
@@ -1,4 +1,4 @@
-/*	$NetBSD: tmpfs_fifoops.c,v 1.9 2011/05/24 20:17:49 rmind Exp $	*/
+/*	$NetBSD: tmpfs_fifoops.c,v 1.10 2013/11/23 16:35:32 rmind Exp $	*/
 
 /*
  * Copyright (c) 2005 The NetBSD Foundation, Inc.
@@ -35,7 +35,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: tmpfs_fifoops.c,v 1.9 2011/05/24 20:17:49 rmind Exp $");
+__KERNEL_RCSID(0, "$NetBSD: tmpfs_fifoops.c,v 1.10 2013/11/23 16:35:32 rmind Exp $");
 
 #include <sys/param.h>
 #include <sys/vnode.h>
@@ -103,10 +103,8 @@ tmpfs_fifo_close(void *v)
 		struct vnode	*a_vp;
 		int		a_fflag;
 		kauth_cred_t	a_cred;
-	} */ *ap = v;
-	vnode_t *vp = ap->a_vp;
+	} */ *ap __unused = v;
 
-	tmpfs_update(vp, NULL, NULL, NULL, UPDATE_CLOSE);
 	return VOCALL(fifo_vnodeop_p, VOFFSET(vop_close), v);
 }
 
@@ -121,7 +119,7 @@ tmpfs_fifo_read(void *v)
 	} */ *ap = v;
 	vnode_t *vp = ap->a_vp;
 
-	VP_TO_TMPFS_NODE(vp)->tn_status |= TMPFS_NODE_ACCESSED;
+	tmpfs_update(vp, TMPFS_UPDATE_ATIME);
 	return VOCALL(fifo_vnodeop_p, VOFFSET(vop_read), v);
 }
 
@@ -136,6 +134,6 @@ tmpfs_fifo_write(void *v)
 	} */ *ap = v;
 	vnode_t *vp = ap->a_vp;
 
-	VP_TO_TMPFS_NODE(vp)->tn_status |= TMPFS_NODE_MODIFIED;
+	tmpfs_update(vp, TMPFS_UPDATE_MTIME);
 	return VOCALL(fifo_vnodeop_p, VOFFSET(vop_write), v);
 }

Index: src/sys/fs/tmpfs/tmpfs_rename.c
diff -u src/sys/fs/tmpfs/tmpfs_rename.c:1.5 src/sys/fs/tmpfs/tmpfs_rename.c:1.6
--- src/sys/fs/tmpfs/tmpfs_rename.c:1.5	Fri Nov  8 15:44:23 2013
+++ src/sys/fs/tmpfs/tmpfs_rename.c	Sat Nov 23 16:35:32 2013
@@ -1,4 +1,4 @@
-/*	$NetBSD: tmpfs_rename.c,v 1.5 2013/11/08 15:44:23 rmind Exp $	*/
+/*	$NetBSD: tmpfs_rename.c,v 1.6 2013/11/23 16:35:32 rmind 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.5 2013/11/08 15:44:23 rmind Exp $");
+__KERNEL_RCSID(0, "$NetBSD: tmpfs_rename.c,v 1.6 2013/11/23 16:35:32 rmind Exp $");
 
 #include <sys/param.h>
 #include <sys/errno.h>
@@ -266,6 +266,8 @@ tmpfs_gro_rename(struct mount *mp, kauth
     struct vnode *tdvp, struct componentname *tcnp,
     void *tde, struct vnode *tvp)
 {
+	tmpfs_node_t *fdnode = VP_TO_TMPFS_DIR(fdvp);
+	tmpfs_node_t *tdnode = VP_TO_TMPFS_DIR(tdvp);
 	struct tmpfs_dirent **fdep = fde;
 	struct tmpfs_dirent **tdep = tde;
 	char *newname;
@@ -313,9 +315,6 @@ tmpfs_gro_rename(struct mount *mp, kauth
 	 * source entry and reattach it to the target directory.
 	 */
 	if (fdvp != tdvp) {
-		tmpfs_node_t *fdnode = VP_TO_TMPFS_DIR(fdvp);
-		tmpfs_node_t *tdnode = VP_TO_TMPFS_DIR(tdvp);
-
 		tmpfs_dir_detach(fdnode, *fdep);
 		tmpfs_dir_attach(tdnode, *fdep, VP_TO_TMPFS_NODE(fvp));
 	} else if (tvp == NULL) {
@@ -334,7 +333,7 @@ tmpfs_gro_rename(struct mount *mp, kauth
 	 * XXX What if the target is a directory with whiteout entries?
 	 */
 	if (tvp != NULL) {
-		tmpfs_node_t *tdnode = VP_TO_TMPFS_DIR(tdvp);
+		tdnode = VP_TO_TMPFS_DIR(tdvp);
 
 		KASSERT((*tdep) != NULL);
 		KASSERT((*tdep)->td_node == VP_TO_TMPFS_NODE(tvp));
@@ -346,11 +345,6 @@ tmpfs_gro_rename(struct mount *mp, kauth
 			/*
 			 * Decrement the extra link count for `.' so
 			 * the vnode will be recycled when released.
-			 *
-			 * XXX Why not just release directory vnodes
-			 * when their link count is 1 instead of 0 in
-			 * tmpfs_inactive, since `.' is being treated
-			 * specially anyway?
 			 */
 			VP_TO_TMPFS_NODE(tvp)->tn_links--;
 		}
@@ -373,11 +367,16 @@ tmpfs_gro_rename(struct mount *mp, kauth
 		(*fdep)->td_namelen = (uint16_t)tcnp->cn_namelen;
 		(void)memcpy(newname, tcnp->cn_nameptr, tcnp->cn_namelen);
 		(*fdep)->td_name = newname;
-
-		VP_TO_TMPFS_NODE(fvp)->tn_status |= TMPFS_NODE_CHANGED;
-		VP_TO_TMPFS_NODE(tdvp)->tn_status |= TMPFS_NODE_MODIFIED;
 	}
 
+	/*
+	 * Update the timestamps of both parent directories and
+	 * the renamed file itself.
+	 */
+	tmpfs_update(fdvp, TMPFS_UPDATE_MTIME | TMPFS_UPDATE_CTIME);
+	tmpfs_update(tdvp, TMPFS_UPDATE_MTIME | TMPFS_UPDATE_CTIME);
+	tmpfs_update(fvp, TMPFS_UPDATE_CTIME);
+
 	VN_KNOTE(fvp, NOTE_RENAME);
 
 	genfs_rename_cache_purge(fdvp, fvp, tdvp, tvp);
@@ -412,6 +411,7 @@ tmpfs_gro_remove(struct mount *mp, kauth
 
 	tmpfs_dir_detach(dnode, *dep);
 	tmpfs_free_dirent(VFS_TO_TMPFS(mp), *dep);
+	tmpfs_update(dvp, TMPFS_UPDATE_MTIME | TMPFS_UPDATE_CTIME);
 
 	return 0;
 }

Index: src/sys/fs/tmpfs/tmpfs_specops.c
diff -u src/sys/fs/tmpfs/tmpfs_specops.c:1.10 src/sys/fs/tmpfs/tmpfs_specops.c:1.11
--- src/sys/fs/tmpfs/tmpfs_specops.c:1.10	Tue May 24 20:17:49 2011
+++ src/sys/fs/tmpfs/tmpfs_specops.c	Sat Nov 23 16:35:32 2013
@@ -1,4 +1,4 @@
-/*	$NetBSD: tmpfs_specops.c,v 1.10 2011/05/24 20:17:49 rmind Exp $	*/
+/*	$NetBSD: tmpfs_specops.c,v 1.11 2013/11/23 16:35:32 rmind Exp $	*/
 
 /*
  * Copyright (c) 2005 The NetBSD Foundation, Inc.
@@ -35,7 +35,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: tmpfs_specops.c,v 1.10 2011/05/24 20:17:49 rmind Exp $");
+__KERNEL_RCSID(0, "$NetBSD: tmpfs_specops.c,v 1.11 2013/11/23 16:35:32 rmind Exp $");
 
 #include <sys/param.h>
 #include <sys/vnode.h>
@@ -106,10 +106,8 @@ tmpfs_spec_close(void *v)
 		struct vnode	*a_vp;
 		int		a_fflag;
 		kauth_cred_t	a_cred;
-	} */ *ap = v;
-	vnode_t *vp = ap->a_vp;
+	} */ *ap __unused = v;
 
-	tmpfs_update(vp, NULL, NULL, NULL, UPDATE_CLOSE);
 	return VOCALL(spec_vnodeop_p, VOFFSET(vop_close), v);
 }
 
@@ -124,7 +122,7 @@ tmpfs_spec_read(void *v)
 	} */ *ap = v;
 	vnode_t *vp = ap->a_vp;
 
-	VP_TO_TMPFS_NODE(vp)->tn_status |= TMPFS_NODE_ACCESSED;
+	tmpfs_update(vp, TMPFS_UPDATE_ATIME);
 	return VOCALL(spec_vnodeop_p, VOFFSET(vop_read), v);
 }
 
@@ -139,6 +137,6 @@ tmpfs_spec_write(void *v)
 	} */ *ap = v;
 	vnode_t *vp = ap->a_vp;
 
-	VP_TO_TMPFS_NODE(vp)->tn_status |= TMPFS_NODE_MODIFIED;
+	tmpfs_update(vp, TMPFS_UPDATE_MTIME);
 	return VOCALL(spec_vnodeop_p, VOFFSET(vop_write), v);
 }

Index: src/sys/fs/tmpfs/tmpfs_subr.c
diff -u src/sys/fs/tmpfs/tmpfs_subr.c:1.89 src/sys/fs/tmpfs/tmpfs_subr.c:1.90
--- src/sys/fs/tmpfs/tmpfs_subr.c:1.89	Thu Nov 21 14:39:09 2013
+++ src/sys/fs/tmpfs/tmpfs_subr.c	Sat Nov 23 16:35:32 2013
@@ -1,4 +1,4 @@
-/*	$NetBSD: tmpfs_subr.c,v 1.89 2013/11/21 14:39:09 rmind Exp $	*/
+/*	$NetBSD: tmpfs_subr.c,v 1.90 2013/11/23 16:35:32 rmind Exp $	*/
 
 /*
  * Copyright (c) 2005-2013 The NetBSD Foundation, Inc.
@@ -59,7 +59,7 @@
  *	reference counting and link counting.  That is, an inode can only be
  *	destroyed if its associated vnode is inactive.  The destruction is
  *	done on vnode reclamation i.e. tmpfs_reclaim().  It should be noted
- *	that tmpfs_node_t::tn_links being 0 is a destruction criterion. 
+ *	that tmpfs_node_t::tn_links being 0 is a destruction criterion.
  *
  *	If an inode has references within the file system (tn_links > 0) and
  *	its inactive vnode gets reclaimed/recycled - then the association is
@@ -74,7 +74,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: tmpfs_subr.c,v 1.89 2013/11/21 14:39:09 rmind Exp $");
+__KERNEL_RCSID(0, "$NetBSD: tmpfs_subr.c,v 1.90 2013/11/23 16:35:32 rmind Exp $");
 
 #include <sys/param.h>
 #include <sys/cprng.h>
@@ -132,7 +132,6 @@ tmpfs_alloc_node(tmpfs_mount_t *tmp, enu
 	/* Generic initialization. */
 	nnode->tn_type = type;
 	nnode->tn_size = 0;
-	nnode->tn_status = 0;
 	nnode->tn_flags = 0;
 	nnode->tn_lockf = NULL;
 
@@ -244,6 +243,7 @@ tmpfs_free_node(tmpfs_mount_t *tmp, tmpf
 		}
 		break;
 	case VDIR:
+		KASSERT(node->tn_size == 0);
 		KASSERT(node->tn_spec.tn_dir.tn_seq_arena == NULL);
 		KASSERT(TAILQ_EMPTY(&node->tn_spec.tn_dir.tn_dir));
 		KASSERT(node->tn_spec.tn_dir.tn_parent == NULL ||
@@ -252,6 +252,7 @@ tmpfs_free_node(tmpfs_mount_t *tmp, tmpf
 	default:
 		break;
 	}
+	KASSERT(node->tn_links == 0);
 
 	mutex_destroy(&node->tn_vlock);
 	tmpfs_node_put(tmp, node);
@@ -340,13 +341,13 @@ again:
 }
 
 /*
- * tmpfs_alloc_file: allocate a new file of specified type and adds it
+ * tmpfs_construct_node: allocate a new file of specified type and adds it
  * into the parent directory.
  *
  * => Credentials of the caller are used.
  */
 int
-tmpfs_alloc_file(vnode_t *dvp, vnode_t **vpp, struct vattr *vap,
+tmpfs_construct_node(vnode_t *dvp, vnode_t **vpp, struct vattr *vap,
     struct componentname *cnp, char *target)
 {
 	tmpfs_mount_t *tmp = VFS_TO_TMPFS(dvp->v_mount);
@@ -403,6 +404,9 @@ tmpfs_alloc_file(vnode_t *dvp, vnode_t *
 	/* Make node opaque if requested. */
 	if (cnp->cn_flags & ISWHITEOUT)
 		node->tn_flags |= UF_OPAQUE;
+
+	/* Update the parent's timestamps. */
+	tmpfs_update(dvp, TMPFS_UPDATE_MTIME | TMPFS_UPDATE_CTIME);
 out:
 	vput(dvp);
 	return error;
@@ -452,8 +456,8 @@ tmpfs_free_dirent(tmpfs_mount_t *tmp, tm
  * and attach the entry into the directory, specified by vnode.
  *
  * => Increases link count on the associated node.
- * => Increases link count on directory node, if our node is VDIR.
- *    It is caller's responsibility to check for the LINK_MAX limit.
+ * => Increases link count on directory node if our node is VDIR.
+ * => It is caller's responsibility to check for the LINK_MAX limit.
  * => Triggers kqueue events here.
  */
 void
@@ -477,12 +481,14 @@ tmpfs_dir_attach(tmpfs_node_t *dnode, tm
 
 		/* Save the hint (might overwrite). */
 		node->tn_dirent_hint = de;
+	} else if ((dnode->tn_gen & TMPFS_WHITEOUT_BIT) == 0) {
+		/* Flag that there are whiteout entries. */
+		atomic_or_32(&dnode->tn_gen, TMPFS_WHITEOUT_BIT);
 	}
 
 	/* Insert the entry to the directory (parent of inode). */
 	TAILQ_INSERT_TAIL(&dnode->tn_spec.tn_dir.tn_dir, de, td_entries);
 	dnode->tn_size += sizeof(tmpfs_dirent_t);
-	dnode->tn_status |= TMPFS_NODE_STATUSALL;
 	uvm_vnp_setsize(dvp, dnode->tn_size);
 
 	if (node != TMPFS_NODE_WHITEOUT && node->tn_type == VDIR) {
@@ -548,9 +554,7 @@ tmpfs_dir_detach(tmpfs_node_t *dnode, tm
 		dnode->tn_spec.tn_dir.tn_readdir_lastp = NULL;
 	}
 	TAILQ_REMOVE(&dnode->tn_spec.tn_dir.tn_dir, de, td_entries);
-
 	dnode->tn_size -= sizeof(tmpfs_dirent_t);
-	dnode->tn_status |= TMPFS_NODE_STATUSALL;
 	tmpfs_dir_putseq(dnode, de);
 
 	if (dvp) {
@@ -584,7 +588,6 @@ tmpfs_dir_lookup(tmpfs_node_t *node, str
 			continue;
 		break;
 	}
-	node->tn_status |= TMPFS_NODE_ACCESSED;
 	return de;
 }
 
@@ -853,7 +856,7 @@ tmpfs_dir_getdents(tmpfs_node_t *node, s
 	uio->uio_offset = de ? tmpfs_dir_getseq(node, de) : TMPFS_DIRSEQ_EOF;
 	node->tn_spec.tn_dir.tn_readdir_lastp = de;
 done:
-	node->tn_status |= TMPFS_NODE_ACCESSED;
+	tmpfs_update(node->tn_vnode, TMPFS_UPDATE_ATIME);
 	kmem_free(dentp, sizeof(struct dirent));
 
 	if (error == EJUSTRETURN) {
@@ -921,8 +924,6 @@ tmpfs_reg_resize(struct vnode *vp, off_t
 
 /*
  * tmpfs_chflags: change flags of the given vnode.
- *
- * => Caller should perform tmpfs_update().
  */
 int
 tmpfs_chflags(vnode_t *vp, int flags, kauth_cred_t cred, lwp_t *l)
@@ -977,15 +978,13 @@ tmpfs_chflags(vnode_t *vp, int flags, ka
 	} else {
 		node->tn_flags = flags;
 	}
-	node->tn_status |= TMPFS_NODE_CHANGED;
+	tmpfs_update(vp, TMPFS_UPDATE_CTIME);
 	VN_KNOTE(vp, NOTE_ATTRIB);
 	return 0;
 }
 
 /*
  * tmpfs_chmod: change access mode on the given vnode.
- *
- * => Caller should perform tmpfs_update().
  */
 int
 tmpfs_chmod(vnode_t *vp, mode_t mode, kauth_cred_t cred, lwp_t *l)
@@ -1009,7 +1008,7 @@ tmpfs_chmod(vnode_t *vp, mode_t mode, ka
 		return error;
 	}
 	node->tn_mode = (mode & ALLPERMS);
-	node->tn_status |= TMPFS_NODE_CHANGED;
+	tmpfs_update(vp, TMPFS_UPDATE_CTIME);
 	VN_KNOTE(vp, NOTE_ATTRIB);
 	return 0;
 }
@@ -1019,7 +1018,6 @@ tmpfs_chmod(vnode_t *vp, mode_t mode, ka
  *
  * => At least one of uid or gid must be different than VNOVAL.
  * => Attribute is unchanged for VNOVAL case.
- * => Caller should perform tmpfs_update().
  */
 int
 tmpfs_chown(vnode_t *vp, uid_t uid, gid_t gid, kauth_cred_t cred, lwp_t *l)
@@ -1054,7 +1052,7 @@ tmpfs_chown(vnode_t *vp, uid_t uid, gid_
 	}
 	node->tn_uid = uid;
 	node->tn_gid = gid;
-	node->tn_status |= TMPFS_NODE_CHANGED;
+	tmpfs_update(vp, TMPFS_UPDATE_CTIME);
 	VN_KNOTE(vp, NOTE_ATTRIB);
 	return 0;
 }
@@ -1066,6 +1064,8 @@ int
 tmpfs_chsize(vnode_t *vp, u_quad_t size, kauth_cred_t cred, lwp_t *l)
 {
 	tmpfs_node_t *node = VP_TO_TMPFS_NODE(vp);
+	const off_t length = size;
+	int error;
 
 	KASSERT(VOP_ISLOCKED(vp));
 
@@ -1096,8 +1096,19 @@ tmpfs_chsize(vnode_t *vp, u_quad_t size,
 		return EPERM;
 	}
 
-	/* Note: tmpfs_truncate() will raise NOTE_EXTEND and NOTE_ATTRIB. */
-	return tmpfs_truncate(vp, size);
+	if (length < 0) {
+		return EINVAL;
+	}
+	if (node->tn_size == length) {
+		return 0;
+	}
+
+	/* Note: tmpfs_reg_resize() will raise NOTE_EXTEND and NOTE_ATTRIB. */
+	if ((error = tmpfs_reg_resize(vp, length)) != 0) {
+		return error;
+	}
+	tmpfs_update(vp, TMPFS_UPDATE_CTIME | TMPFS_UPDATE_MTIME);
+	return 0;
 }
 
 /*
@@ -1126,75 +1137,40 @@ tmpfs_chtimes(vnode_t *vp, const struct 
 	if (error)
 		return error;
 
-	if (atime->tv_sec != VNOVAL && atime->tv_nsec != VNOVAL)
-		node->tn_status |= TMPFS_NODE_ACCESSED;
-
-	if (mtime->tv_sec != VNOVAL && mtime->tv_nsec != VNOVAL)
-		node->tn_status |= TMPFS_NODE_MODIFIED;
-
-	if (btime->tv_sec == VNOVAL && btime->tv_nsec == VNOVAL)
-		btime = NULL;
-
-	tmpfs_update(vp, atime, mtime, btime, 0);
+	if (atime->tv_sec != VNOVAL) {
+		node->tn_atime = *atime;
+	}
+	if (mtime->tv_sec != VNOVAL) {
+		node->tn_mtime = *mtime;
+	}
+	if (btime->tv_sec != VNOVAL) {
+		node->tn_birthtime = *btime;
+	}
 	VN_KNOTE(vp, NOTE_ATTRIB);
 	return 0;
 }
 
 /*
- * tmpfs_update: update timestamps, et al.
+ * tmpfs_update: update the timestamps as indicated by the flags.
  */
 void
-tmpfs_update(vnode_t *vp, const struct timespec *acc,
-    const struct timespec *mod, const struct timespec *birth, int flags)
+tmpfs_update(vnode_t *vp, unsigned tflags)
 {
 	tmpfs_node_t *node = VP_TO_TMPFS_NODE(vp);
 	struct timespec nowtm;
 
-	/* KASSERT(VOP_ISLOCKED(vp)); */
-
-	if (flags & UPDATE_CLOSE) {
-		/* XXX Need to do anything special? */
-	}
-	if ((node->tn_status & TMPFS_NODE_STATUSALL) == 0) {
+	if (tflags == 0) {
 		return;
 	}
-	if (birth != NULL) {
-		node->tn_birthtime = *birth;
-	}
 	vfs_timestamp(&nowtm);
 
-	if (node->tn_status & TMPFS_NODE_ACCESSED) {
-		node->tn_atime = acc ? *acc : nowtm;
+	if (tflags & TMPFS_UPDATE_ATIME) {
+		node->tn_atime = nowtm;
 	}
-	if (node->tn_status & TMPFS_NODE_MODIFIED) {
-		node->tn_mtime = mod ? *mod : nowtm;
+	if (tflags & TMPFS_UPDATE_MTIME) {
+		node->tn_mtime = nowtm;
 	}
-	if (node->tn_status & TMPFS_NODE_CHANGED) {
+	if (tflags & TMPFS_UPDATE_CTIME) {
 		node->tn_ctime = nowtm;
 	}
-
-	node->tn_status &= ~TMPFS_NODE_STATUSALL;
-}
-
-int
-tmpfs_truncate(vnode_t *vp, off_t length)
-{
-	tmpfs_node_t *node = VP_TO_TMPFS_NODE(vp);
-	int error;
-
-	if (length < 0) {
-		error = EINVAL;
-		goto out;
-	}
-	if (node->tn_size == length) {
-		error = 0;
-		goto out;
-	}
-	error = tmpfs_reg_resize(vp, length);
-	if (error == 0) {
-		node->tn_status |= TMPFS_NODE_CHANGED | TMPFS_NODE_MODIFIED;
-	}
-out:
-	tmpfs_update(vp, NULL, NULL, NULL, 0);
-	return error;
 }

Index: src/sys/fs/tmpfs/tmpfs_vfsops.c
diff -u src/sys/fs/tmpfs/tmpfs_vfsops.c:1.54 src/sys/fs/tmpfs/tmpfs_vfsops.c:1.55
--- src/sys/fs/tmpfs/tmpfs_vfsops.c:1.54	Sun Nov 10 12:46:19 2013
+++ src/sys/fs/tmpfs/tmpfs_vfsops.c	Sat Nov 23 16:35:32 2013
@@ -1,4 +1,4 @@
-/*	$NetBSD: tmpfs_vfsops.c,v 1.54 2013/11/10 12:46:19 rmind Exp $	*/
+/*	$NetBSD: tmpfs_vfsops.c,v 1.55 2013/11/23 16:35:32 rmind Exp $	*/
 
 /*
  * Copyright (c) 2005, 2006, 2007 The NetBSD Foundation, Inc.
@@ -42,7 +42,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: tmpfs_vfsops.c,v 1.54 2013/11/10 12:46:19 rmind Exp $");
+__KERNEL_RCSID(0, "$NetBSD: tmpfs_vfsops.c,v 1.55 2013/11/23 16:35:32 rmind Exp $");
 
 #include <sys/param.h>
 #include <sys/types.h>
@@ -237,8 +237,14 @@ tmpfs_unmount(struct mount *mp, int mntf
 			tmpfs_dir_detach(node, de);
 			tmpfs_free_dirent(tmp, de);
 		}
+		/* Extra virtual entry (itself for the root). */
+		node->tn_links--;
 	}
 
+	/* Release the reference on root (diagnostic). */
+	node = tmp->tm_root;
+	node->tn_links--;
+
 	/* Second round, destroy all inodes. */
 	while ((node = LIST_FIRST(&tmp->tm_nodes)) != NULL) {
 		tmpfs_free_node(tmp, node);

Index: src/sys/fs/tmpfs/tmpfs_vnops.c
diff -u src/sys/fs/tmpfs/tmpfs_vnops.c:1.106 src/sys/fs/tmpfs/tmpfs_vnops.c:1.107
--- src/sys/fs/tmpfs/tmpfs_vnops.c:1.106	Fri Nov  8 15:44:23 2013
+++ src/sys/fs/tmpfs/tmpfs_vnops.c	Sat Nov 23 16:35:32 2013
@@ -1,4 +1,4 @@
-/*	$NetBSD: tmpfs_vnops.c,v 1.106 2013/11/08 15:44:23 rmind Exp $	*/
+/*	$NetBSD: tmpfs_vnops.c,v 1.107 2013/11/23 16:35:32 rmind 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.106 2013/11/08 15:44:23 rmind Exp $");
+__KERNEL_RCSID(0, "$NetBSD: tmpfs_vnops.c,v 1.107 2013/11/23 16:35:32 rmind Exp $");
 
 #include <sys/param.h>
 #include <sys/dirent.h>
@@ -313,7 +313,7 @@ tmpfs_create(void *v)
 
 	KASSERT(VOP_ISLOCKED(dvp));
 	KASSERT(vap->va_type == VREG || vap->va_type == VSOCK);
-	return tmpfs_alloc_file(dvp, vpp, vap, cnp, NULL);
+	return tmpfs_construct_node(dvp, vpp, vap, cnp, NULL);
 }
 
 int
@@ -334,7 +334,7 @@ tmpfs_mknod(void *v)
 		vput(dvp);
 		return EINVAL;
 	}
-	return tmpfs_alloc_file(dvp, vpp, vap, cnp, NULL);
+	return tmpfs_construct_node(dvp, vpp, vap, cnp, NULL);
 }
 
 int
@@ -380,8 +380,6 @@ tmpfs_close(void *v)
 	vnode_t *vp = ap->a_vp;
 
 	KASSERT(VOP_ISLOCKED(vp));
-
-	tmpfs_update(vp, NULL, NULL, NULL, UPDATE_CLOSE);
 	return 0;
 }
 
@@ -441,8 +439,6 @@ tmpfs_getattr(void *v)
 
 	vattr_null(vap);
 
-	tmpfs_update(vp, NULL, NULL, NULL, 0);
-
 	vap->va_type = vp->v_type;
 	vap->va_mode = node->tn_mode;
 	vap->va_nlink = node->tn_links;
@@ -468,9 +464,6 @@ tmpfs_getattr(void *v)
 	return 0;
 }
 
-#define GOODTIME(tv)	((tv)->tv_sec != VNOVAL || (tv)->tv_nsec != VNOVAL)
-/* XXX Should this operation be atomic?  I think it should, but code in
- * XXX other places (e.g., ufs) doesn't seem to be... */
 int
 tmpfs_setattr(void *v)
 {
@@ -490,31 +483,32 @@ tmpfs_setattr(void *v)
 	/* Abort if any unsettable attribute is given. */
 	if (vap->va_type != VNON || vap->va_nlink != VNOVAL ||
 	    vap->va_fsid != VNOVAL || vap->va_fileid != VNOVAL ||
-	    vap->va_blocksize != VNOVAL || GOODTIME(&vap->va_ctime) ||
+	    vap->va_blocksize != VNOVAL || vap->va_ctime.tv_sec != VNOVAL ||
 	    vap->va_gen != VNOVAL || vap->va_rdev != VNOVAL ||
 	    vap->va_bytes != VNOVAL) {
 		return EINVAL;
 	}
-	if (error == 0 && (vap->va_flags != VNOVAL))
+
+	if (error == 0 && vap->va_flags != VNOVAL)
 		error = tmpfs_chflags(vp, vap->va_flags, cred, l);
 
-	if (error == 0 && (vap->va_size != VNOVAL))
+	if (error == 0 && vap->va_size != VNOVAL)
 		error = tmpfs_chsize(vp, vap->va_size, cred, l);
 
 	if (error == 0 && (vap->va_uid != VNOVAL || vap->va_gid != VNOVAL))
 		error = tmpfs_chown(vp, vap->va_uid, vap->va_gid, cred, l);
 
-	if (error == 0 && (vap->va_mode != VNOVAL))
+	if (error == 0 && vap->va_mode != VNOVAL)
 		error = tmpfs_chmod(vp, vap->va_mode, cred, l);
 
-	if (error == 0 && (GOODTIME(&vap->va_atime) || GOODTIME(&vap->va_mtime)
-	    || GOODTIME(&vap->va_birthtime))) {
+	const bool chsometime =
+	    vap->va_atime.tv_sec != VNOVAL ||
+	    vap->va_mtime.tv_sec != VNOVAL ||
+	    vap->va_birthtime.tv_sec != VNOVAL;
+	if (error == 0 && chsometime) {
 		error = tmpfs_chtimes(vp, &vap->va_atime, &vap->va_mtime,
 		    &vap->va_birthtime, vap->va_vaflags, cred, l);
-		if (error == 0)
-			return 0;
 	}
-	tmpfs_update(vp, NULL, NULL, NULL, 0);
 	return error;
 }
 
@@ -543,8 +537,12 @@ tmpfs_read(void *v)
 		return EINVAL;
 	}
 
+	/* Note: reading zero bytes should not update atime. */
+	if (uio->uio_resid == 0) {
+		return 0;
+	}
+
 	node = VP_TO_TMPFS_NODE(vp);
-	node->tn_status |= TMPFS_NODE_ACCESSED;
 	uobj = node->tn_spec.tn_reg.tn_aobj;
 	error = 0;
 
@@ -561,6 +559,8 @@ tmpfs_read(void *v)
 		error = ubc_uiomove(uobj, uio, len, IO_ADV_DECODE(ioflag),
 		    UBC_READ | UBC_PARTIALOK | UBC_UNMAP_FLAG(vp));
 	}
+
+	tmpfs_update(vp, TMPFS_UPDATE_ATIME);
 	return error;
 }
 
@@ -579,7 +579,6 @@ tmpfs_write(void *v)
 	tmpfs_node_t *node;
 	struct uvm_object *uobj;
 	off_t oldsize;
-	bool extended;
 	int error;
 
 	KASSERT(VOP_ISLOCKED(vp));
@@ -599,8 +598,7 @@ tmpfs_write(void *v)
 		uio->uio_offset = node->tn_size;
 	}
 
-	extended = uio->uio_offset + uio->uio_resid > node->tn_size;
-	if (extended) {
+	if (uio->uio_offset + uio->uio_resid > node->tn_size) {
 		error = tmpfs_reg_resize(vp, uio->uio_offset + uio->uio_resid);
 		if (error)
 			goto out;
@@ -622,8 +620,7 @@ tmpfs_write(void *v)
 		(void)tmpfs_reg_resize(vp, oldsize);
 	}
 
-	node->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_MODIFIED |
-	    (extended ? TMPFS_NODE_CHANGED : 0);
+	tmpfs_update(vp, TMPFS_UPDATE_MTIME | TMPFS_UPDATE_CTIME);
 	VN_KNOTE(vp, NOTE_WRITE);
 out:
 	if (error) {
@@ -647,9 +644,8 @@ tmpfs_fsync(void *v)
 	} */ *ap = v;
 	vnode_t *vp = ap->a_vp;
 
-	/* Nothing to do.  Just update. */
+	/* Nothing to do.  Should be up to date. */
 	KASSERT(VOP_ISLOCKED(vp));
-	tmpfs_update(vp, NULL, NULL, NULL, 0);
 	return 0;
 }
 
@@ -706,11 +702,14 @@ tmpfs_remove(void *v)
 
 	/*
 	 * Remove the entry from the directory (drops the link count) and
-	 * destroy it or replace it with a whiteout.
-	 * Note: the inode referred by it will not be destroyed
-	 * until the vnode is reclaimed/recycled.
+	 * destroy it or replace with a whiteout.
+	 *
+	 * Note: the inode referred by it will not be destroyed until the
+	 * vnode is reclaimed/recycled.
 	 */
+
 	tmpfs_dir_detach(dnode, de);
+
 	if (ap->a_cnp->cn_flags & DOWHITEOUT)
 		tmpfs_dir_attach(dnode, de, TMPFS_NODE_WHITEOUT);
 	else
@@ -718,9 +717,9 @@ tmpfs_remove(void *v)
 
 	if (node->tn_links > 0) {
 		/* We removed a hard link. */
-		node->tn_status |= TMPFS_NODE_CHANGED;
-		tmpfs_update(vp, NULL, NULL, NULL, 0);
+		tmpfs_update(vp, TMPFS_UPDATE_CTIME);
 	}
+	tmpfs_update(dvp, TMPFS_UPDATE_MTIME | TMPFS_UPDATE_CTIME);
 	error = 0;
 out:
 	/* Drop the references and unlock the vnodes. */
@@ -786,13 +785,13 @@ tmpfs_link(void *v)
 	 * It will increase the inode link count.
 	 */
 	tmpfs_dir_attach(dnode, de, node);
+	tmpfs_update(dvp, TMPFS_UPDATE_MTIME | TMPFS_UPDATE_CTIME);
 
 	/* Update the timestamps and trigger the event. */
 	if (node->tn_vnode) {
 		VN_KNOTE(node->tn_vnode, NOTE_LINK);
 	}
-	node->tn_status |= TMPFS_NODE_CHANGED;
-	tmpfs_update(vp, NULL, NULL, NULL, 0);
+	tmpfs_update(vp, TMPFS_UPDATE_CTIME);
 	error = 0;
 out:
 	VOP_UNLOCK(vp);
@@ -815,7 +814,7 @@ tmpfs_mkdir(void *v)
 	struct vattr *vap = ap->a_vap;
 
 	KASSERT(vap->va_type == VDIR);
-	return tmpfs_alloc_file(dvp, vpp, vap, cnp, NULL);
+	return tmpfs_construct_node(dvp, vpp, vap, cnp, NULL);
 }
 
 int
@@ -839,19 +838,25 @@ tmpfs_rmdir(void *v)
 	KASSERT(node->tn_spec.tn_dir.tn_parent == dnode);
 
 	/*
-	 * Directories with more than two non-whiteout
-	 * entries ('.' and '..') cannot be removed.
+	 * Directories with more than two entries ('.' and '..') cannot be
+	 * removed.  There may be whiteout entries, which we will destroy.
 	 */
 	if (node->tn_size > 0) {
-		KASSERT(error == 0);
+		/*
+		 * If never had whiteout entries, the directory is certainly
+		 * not empty.  Otherwise, scan for any non-whiteout entry.
+		 */
+		if ((node->tn_gen & TMPFS_WHITEOUT_BIT) == 0) {
+			error = ENOTEMPTY;
+			goto out;
+		}
 		TAILQ_FOREACH(de, &node->tn_spec.tn_dir.tn_dir, td_entries) {
 			if (de->td_node != TMPFS_NODE_WHITEOUT) {
 				error = ENOTEMPTY;
-				break;
+				goto out;
 			}
 		}
-		if (error)
-			goto out;
+		KASSERT(error == 0);
 	}
 
 	/* Lookup the directory entry (check the cached hint first). */
@@ -870,7 +875,6 @@ tmpfs_rmdir(void *v)
 
 	/* Decrement the link count for the virtual '.' entry. */
 	node->tn_links--;
-	node->tn_status |= TMPFS_NODE_STATUSALL;
 
 	/* Detach the directory entry from the directory. */
 	tmpfs_dir_detach(dnode, de);
@@ -880,8 +884,9 @@ tmpfs_rmdir(void *v)
 
 	/*
 	 * Destroy the directory entry or replace it with a whiteout.
-	 * Note: the inode referred by it will not be destroyed
-	 * until the vnode is reclaimed.
+	 *
+	 * Note: the inode referred by it will not be destroyed until the
+	 * vnode is reclaimed.
 	 */
 	if (ap->a_cnp->cn_flags & DOWHITEOUT)
 		tmpfs_dir_attach(dnode, de, TMPFS_NODE_WHITEOUT);
@@ -894,7 +899,9 @@ tmpfs_rmdir(void *v)
 		tmpfs_dir_detach(node, de);
 		tmpfs_free_dirent(tmp, de);
 	}
+	tmpfs_update(dvp, TMPFS_UPDATE_MTIME | TMPFS_UPDATE_CTIME);
 
+	KASSERT(node->tn_size == 0);
 	KASSERT(node->tn_links == 0);
 out:
 	/* Release the nodes. */
@@ -920,7 +927,7 @@ tmpfs_symlink(void *v)
 	char *target = ap->a_target;
 
 	KASSERT(vap->va_type == VLNK);
-	return tmpfs_alloc_file(dvp, vpp, vap, cnp, target);
+	return tmpfs_construct_node(dvp, vpp, vap, cnp, target);
 }
 
 int
@@ -1026,7 +1033,7 @@ tmpfs_readlink(void *v)
 	} else {
 		error = 0;
 	}
-	node->tn_status |= TMPFS_NODE_ACCESSED;
+	tmpfs_update(vp, TMPFS_UPDATE_ATIME);
 
 	return error;
 }
@@ -1185,14 +1192,17 @@ tmpfs_getpages(void *v)
 		return EBUSY;
 
 	if ((flags & PGO_NOTIMESTAMP) == 0) {
+		u_int tflags = 0;
+
 		if ((vp->v_mount->mnt_flag & MNT_NOATIME) == 0)
-			node->tn_status |= TMPFS_NODE_ACCESSED;
+			tflags |= TMPFS_UPDATE_ATIME;
 
 		if ((access_type & VM_PROT_WRITE) != 0) {
-			node->tn_status |= TMPFS_NODE_MODIFIED;
+			tflags |= TMPFS_UPDATE_MTIME;
 			if (vp->v_mount->mnt_flag & MNT_RELATIME)
-				node->tn_status |= TMPFS_NODE_ACCESSED;
+				tflags |= TMPFS_UPDATE_ATIME;
 		}
+		tmpfs_update(vp, tflags);
 	}
 
 	/*
@@ -1289,6 +1299,7 @@ tmpfs_whiteout(void *v)
 		tmpfs_free_dirent(tmp, de);
 		break;
 	}
+	tmpfs_update(dvp, TMPFS_UPDATE_MTIME | TMPFS_UPDATE_CTIME);
 	return 0;
 }
 
@@ -1302,9 +1313,9 @@ tmpfs_print(void *v)
 	tmpfs_node_t *node = VP_TO_TMPFS_NODE(vp);
 
 	printf("tag VT_TMPFS, tmpfs_node %p, flags 0x%x, links %d\n"
-	    "\tmode 0%o, owner %d, group %d, size %" PRIdMAX ", status 0x%x",
+	    "\tmode 0%o, owner %d, group %d, size %" PRIdMAX,
 	    node, node->tn_flags, node->tn_links, node->tn_mode, node->tn_uid,
-	    node->tn_gid, (uintmax_t)node->tn_size, node->tn_status);
+	    node->tn_gid, (uintmax_t)node->tn_size);
 	if (vp->v_type == VFIFO) {
 		VOCALL(fifo_vnodeop_p, VOFFSET(vop_print), v);
 	}

Reply via email to