changeset 4920:8b23b71806fa

4693666 fix for 4235823 reintroduced 1199162 (mmap'ed writes may fail to update mtime) 6460239 snv_46; Panic:assertion failed:(threadp())->t_flag file: ../../common/fs/ufs/lufs_debug.c, line: 198
author frankho
date Tue, 21 Aug 2007 04:08:01 -0700
parents b6b235c6e23b
children 6179db775944
files usr/src/uts/common/fs/ufs/ufs_vnops.c usr/src/uts/common/sys/fs/ufs_inode.h
diffstat 2 files changed, 53 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/uts/common/fs/ufs/ufs_vnops.c	Tue Aug 21 00:30:10 2007 -0700
+++ b/usr/src/uts/common/fs/ufs/ufs_vnops.c	Tue Aug 21 04:08:01 2007 -0700
@@ -2252,6 +2252,12 @@
 		if (mask & AT_MTIME) {
 			ip->i_mtime.tv_sec = vap->va_mtime.tv_sec;
 			ip->i_mtime.tv_usec = vap->va_mtime.tv_nsec / 1000;
+			/*
+			 * Allow ufs_putapage() to distinguish manual mtime
+			 * update from one done on the fly by e.g. write().
+			 */
+			ip->i_mtime_last = ip->i_mtime;
+
 			gethrestime(&now);
 			if (now.tv_sec > TIME32_MAX) {
 				/*
@@ -5241,6 +5247,7 @@
 	daddr_t bn;
 	int err;
 	int contig;
+	int dotrans;
 
 	ASSERT(RW_LOCK_HELD(&ip->i_contents));
 
@@ -5254,12 +5261,19 @@
 
 	/*
 	 * If the modified time on the inode has not already been
-	 * set elsewhere (e.g. for write/setattr) we set the time now.
+	 * set elsewhere (e.g. for write) we set the time now.
 	 * This gives us approximate modified times for mmap'ed files
 	 * which are modified via stores in the user address space.
+	 * We _will_ override timestamps from setattr here, because
+	 * these can be arbitrary, not "approximate" (anywhere close
+	 * to "now"). So check whether the last timestamp update on
+	 * this file came from setattr.
 	 */
-	if ((ip->i_flag & IMODTIME) == 0) {
+	if ((ip->i_flag & IMODTIME) == 0 ||
+	    (ip->i_mtime.tv_sec == ip->i_mtime_last.tv_sec &&
+	    ip->i_mtime.tv_usec == ip->i_mtime_last.tv_usec)) {
 		mutex_enter(&ip->i_tlock);
+		ip->i_flag &= ~IMODTIME;
 		ip->i_flag |= IUPD;
 		ip->i_seq++;
 		ITIMES_NOLOCK(ip);
@@ -5367,6 +5381,25 @@
 	bp->b_un.b_addr = (caddr_t)0;
 	bp->b_file = ip->i_vnode;
 
+	/*
+	 * File contents of shadow or quota inodes are metadata, and updates
+	 * to these need to be put into a logging transaction. All direct
+	 * callers in UFS do that, but fsflush can come here _before_ the
+	 * normal codepath, for example on updating ACL information that'd be
+	 * ufs_si_store()->ufs_rdwri()->wrip()->segmap_release()->VOP_PUTPAGE()
+	 * reaches this point.
+	 * We therefore need to test whether a transaction exists, and if not
+	 * create one - for fsflush.
+	 */
+	dotrans =
+	    (((ip->i_mode & IFMT) == IFSHAD || ufsvfsp->vfs_qinod == ip) &&
+	    ((curthread->t_flag & T_DONTBLOCK) == 0) &&
+	    (TRANS_ISTRANS(ufsvfsp)));
+
+	if (dotrans) {
+		curthread->t_flag |= T_DONTBLOCK;
+		TRANS_BEGIN_ASYNC(ufsvfsp, TOP_PUTPAGE, TOP_PUTPAGE_SIZE(ip));
+	}
 	if (TRANS_ISTRANS(ufsvfsp)) {
 		if ((ip->i_mode & IFMT) == IFSHAD) {
 			TRANS_BUF(ufsvfsp, 0, io_len, bp, DT_SHAD);
@@ -5375,6 +5408,10 @@
 			    0, 0);
 		}
 	}
+	if (dotrans) {
+		TRANS_END_ASYNC(ufsvfsp, TOP_PUTPAGE, TOP_PUTPAGE_SIZE(ip));
+		curthread->t_flag &= ~T_DONTBLOCK;
+	}
 
 	/* write throttle */
 
@@ -5565,8 +5602,21 @@
 	}
 
 	mutex_enter(&ip->i_tlock);
+
 	ip->i_mapcnt -= btopr(len); 	/* Count released mappings */
 	ASSERT(ip->i_mapcnt >= 0);
+
+	/*
+	 * If there are cached pages on this vnode, a timestamp update
+	 * on the next fsflush run might be required to preserve mmap
+	 * semantics for mtime/atime updates.
+	 * We have to force this by clearing the IMODTIME flag here.
+	 */
+	if ((flags & MAP_SHARED) && (prot & PROT_WRITE) &&
+	    vn_has_cached_data(vp)) {
+		ip->i_flag &= ~IMODTIME;
+	}
+
 	mutex_exit(&ip->i_tlock);
 	return (0);
 }
--- a/usr/src/uts/common/sys/fs/ufs_inode.h	Tue Aug 21 00:30:10 2007 -0700
+++ b/usr/src/uts/common/sys/fs/ufs_inode.h	Tue Aug 21 04:08:01 2007 -0700
@@ -271,6 +271,7 @@
 	si_t *i_ufs_acl;	/* pointer to acl entry */
 	dcanchor_t i_danchor;	/* directory cache anchor */
 	kthread_t *i_writer;	/* thread which is in window in wrip() */
+	struct timeval32 i_mtime_last;	/* last explicit mtime modification */
 } inode_t;
 
 struct dinode {