svn commit: r367668 - in head/sys/ufs: ffs ufs

Konstantin Belousov kib at FreeBSD.org
Sat Nov 14 05:10:41 UTC 2020


Author: kib
Date: Sat Nov 14 05:10:39 2020
New Revision: 367668
URL: https://svnweb.freebsd.org/changeset/base/367668

Log:
  Add a framework that tracks exclusive vnode lock generation count for UFS.
  
  This count is memoized together with the lookup metadata in directory
  inode, and we assert that accesses to lookup metadata are done under
  the same lock generation as they were stored.  Enabled under DIAGNOSTICS.
  
  UFS saves additional data for parent dirent when doing lookup
  (i_offset, i_count, i_endoff), and this data is used later by VOPs
  operating on dirents.  If parent vnode exclusive lock is dropped and
  re-acquired between lookup and the VOP call, we corrupt directories.
  
  Framework asserts that corruption cannot occur that way, by tracking
  vnode lock generation counter.  Updates to inode dirent members also
  save the counter, while users compare current and saved counters
  values.
  
  Also, fix a case in ufs_lookup_ino() where i_offset and i_count could
  be updated under shared lock.  It is not a bug on its own since dvp
  i_offset results from such lookup cannot be used, but it causes false
  positive in the checker.
  
  In collaboration with:	pho
  Reviewed by:	mckusick (previous version), markj
  Tested by:	markj (syzkaller), pho
  Sponsored by:	The FreeBSD Foundation
  Differential revision:	https://reviews.freebsd.org/D26136

Modified:
  head/sys/ufs/ffs/ffs_alloc.c
  head/sys/ufs/ffs/ffs_softdep.c
  head/sys/ufs/ffs/ffs_vfsops.c
  head/sys/ufs/ffs/ffs_vnops.c
  head/sys/ufs/ufs/inode.h
  head/sys/ufs/ufs/ufs_lookup.c
  head/sys/ufs/ufs/ufs_vnops.c

Modified: head/sys/ufs/ffs/ffs_alloc.c
==============================================================================
--- head/sys/ufs/ffs/ffs_alloc.c	Sat Nov 14 02:11:56 2020	(r367667)
+++ head/sys/ufs/ffs/ffs_alloc.c	Sat Nov 14 05:10:39 2020	(r367668)
@@ -3468,7 +3468,7 @@ sysctl_ffs_fsck(SYSCTL_HANDLER_ARGS)
 			break;
 		}
 		dp = VTOI(dvp);
-		dp->i_offset = 12;	/* XXX mastertemplate.dot_reclen */
+		SET_I_OFFSET(dp, 12);	/* XXX mastertemplate.dot_reclen */
 		error = ufs_dirrewrite(dp, VTOI(fdvp), (ino_t)cmd.size,
 		    DT_DIR, 0);
 		cache_purge(fdvp);

Modified: head/sys/ufs/ffs/ffs_softdep.c
==============================================================================
--- head/sys/ufs/ffs/ffs_softdep.c	Sat Nov 14 02:11:56 2020	(r367667)
+++ head/sys/ufs/ffs/ffs_softdep.c	Sat Nov 14 05:10:39 2020	(r367668)
@@ -8764,11 +8764,11 @@ softdep_change_directoryentry_offset(bp, dp, base, old
 	if (MOUNTEDSUJ(mp)) {
 		flags = DEPALLOC;
 		jmvref = newjmvref(dp, de->d_ino,
-		    dp->i_offset + (oldloc - base),
-		    dp->i_offset + (newloc - base));
+		    I_OFFSET(dp) + (oldloc - base),
+		    I_OFFSET(dp) + (newloc - base));
 	}
-	lbn = lblkno(ump->um_fs, dp->i_offset);
-	offset = blkoff(ump->um_fs, dp->i_offset);
+	lbn = lblkno(ump->um_fs, I_OFFSET(dp));
+	offset = blkoff(ump->um_fs, I_OFFSET(dp));
 	oldoffset = offset + (oldloc - base);
 	newoffset = offset + (newloc - base);
 	ACQUIRE_LOCK(ump);
@@ -9280,7 +9280,7 @@ newdirrem(bp, dp, ip, isrmdir, prevdirremp)
 	jremref = dotremref = dotdotremref = NULL;
 	if (DOINGSUJ(dvp)) {
 		if (isrmdir) {
-			jremref = newjremref(dirrem, dp, ip, dp->i_offset,
+			jremref = newjremref(dirrem, dp, ip, I_OFFSET(dp),
 			    ip->i_effnlink + 2);
 			dotremref = newjremref(dirrem, ip, ip, DOT_OFFSET,
 			    ip->i_effnlink + 1);
@@ -9288,12 +9288,12 @@ newdirrem(bp, dp, ip, isrmdir, prevdirremp)
 			    dp->i_effnlink + 1);
 			dotdotremref->jr_state |= MKDIR_PARENT;
 		} else
-			jremref = newjremref(dirrem, dp, ip, dp->i_offset,
+			jremref = newjremref(dirrem, dp, ip, I_OFFSET(dp),
 			    ip->i_effnlink + 1);
 	}
 	ACQUIRE_LOCK(ump);
-	lbn = lblkno(ump->um_fs, dp->i_offset);
-	offset = blkoff(ump->um_fs, dp->i_offset);
+	lbn = lblkno(ump->um_fs, I_OFFSET(dp));
+	offset = blkoff(ump->um_fs, I_OFFSET(dp));
 	pagedep_lookup(UFSTOVFS(ump), bp, dp->i_number, lbn, DEPALLOC,
 	    &pagedep);
 	dirrem->dm_pagedep = pagedep;
@@ -9304,7 +9304,7 @@ newdirrem(bp, dp, ip, isrmdir, prevdirremp)
 	 * the jremref is preserved for any potential diradd in this
 	 * location.  This can not coincide with a rmdir.
 	 */
-	if (dp->i_offset == DOTDOT_OFFSET) {
+	if (I_OFFSET(dp) == DOTDOT_OFFSET) {
 		if (isrmdir)
 			panic("newdirrem: .. directory change during remove?");
 		jremref = cancel_mkdir_dotdot(dp, dirrem, jremref);
@@ -9405,7 +9405,7 @@ softdep_setup_directory_change(bp, dp, ip, newinum, is
 
 	mp = ITOVFS(dp);
 	ump = VFSTOUFS(mp);
-	offset = blkoff(ump->um_fs, dp->i_offset);
+	offset = blkoff(ump->um_fs, I_OFFSET(dp));
 	KASSERT(MOUNTEDSOFTDEP(mp) != 0,
 	   ("softdep_setup_directory_change called on non-softdep filesystem"));
 
@@ -9508,7 +9508,7 @@ softdep_setup_directory_change(bp, dp, ip, newinum, is
 		KASSERT(jaddref != NULL && jaddref->ja_parent == dp->i_number,
 		    ("softdep_setup_directory_change: bad jaddref %p",
 		    jaddref));
-		jaddref->ja_diroff = dp->i_offset;
+		jaddref->ja_diroff = I_OFFSET(dp);
 		jaddref->ja_diradd = dap;
 		LIST_INSERT_HEAD(&pagedep->pd_diraddhd[DIRADDHASH(offset)],
 		    dap, da_pdlist);
@@ -9527,7 +9527,7 @@ softdep_setup_directory_change(bp, dp, ip, newinum, is
 	 * committed when need to move the dot and dotdot references to
 	 * this new name.
 	 */
-	if (inodedep->id_mkdiradd && dp->i_offset != DOTDOT_OFFSET)
+	if (inodedep->id_mkdiradd && I_OFFSET(dp) != DOTDOT_OFFSET)
 		merge_diradd(inodedep, dap);
 	FREE_LOCK(ump);
 }

Modified: head/sys/ufs/ffs/ffs_vfsops.c
==============================================================================
--- head/sys/ufs/ffs/ffs_vfsops.c	Sat Nov 14 02:11:56 2020	(r367667)
+++ head/sys/ufs/ffs/ffs_vfsops.c	Sat Nov 14 05:10:39 2020	(r367668)
@@ -2001,6 +2001,9 @@ ffs_vgetf(mp, ino, flags, vpp, ffs_flags)
 	ip->i_nextclustercg = -1;
 	ip->i_flag = fs->fs_magic == FS_UFS1_MAGIC ? 0 : IN_UFS2;
 	ip->i_mode = 0; /* ensure error cases below throw away vnode */
+#ifdef DIAGNOSTIC
+	ufs_init_trackers(ip);
+#endif
 #ifdef QUOTA
 	{
 		int i;

Modified: head/sys/ufs/ffs/ffs_vnops.c
==============================================================================
--- head/sys/ufs/ffs/ffs_vnops.c	Sat Nov 14 02:11:56 2020	(r367667)
+++ head/sys/ufs/ffs/ffs_vnops.c	Sat Nov 14 05:10:39 2020	(r367668)
@@ -434,16 +434,20 @@ ffs_lock(ap)
 	struct vop_lock1_args /* {
 		struct vnode *a_vp;
 		int a_flags;
-		struct thread *a_td;
 		char *file;
 		int line;
 	} */ *ap;
 {
+#if !defined(NO_FFS_SNAPSHOT) || defined(DIAGNOSTIC)
+	struct vnode *vp = ap->a_vp;
+#endif	/* !NO_FFS_SNAPSHOT || DIAGNOSTIC */
+#ifdef DIAGNOSTIC
+	struct inode *ip;
+#endif	/* DIAGNOSTIC */
+	int result;
 #ifndef NO_FFS_SNAPSHOT
-	struct vnode *vp;
 	int flags;
 	struct lock *lkp;
-	int result;
 
 	/*
 	 * Adaptive spinning mixed with SU leads to trouble. use a giant hammer
@@ -456,12 +460,11 @@ ffs_lock(ap)
 	case LK_SHARED:
 	case LK_UPGRADE:
 	case LK_EXCLUSIVE:
-		vp = ap->a_vp;
 		flags = ap->a_flags;
 		for (;;) {
 #ifdef DEBUG_VFS_LOCKS
 			VNPASS(vp->v_holdcnt != 0, vp);
-#endif
+#endif	/* DEBUG_VFS_LOCKS */
 			lkp = vp->v_vnlock;
 			result = lockmgr_lock_flags(lkp, flags,
 			    &VI_MTX(vp)->lock_object, ap->a_file, ap->a_line);
@@ -483,28 +486,67 @@ ffs_lock(ap)
 				flags = (flags & ~LK_TYPE_MASK) | LK_EXCLUSIVE;
 			flags &= ~LK_INTERLOCK;
 		}
+#ifdef DIAGNOSTIC
+		switch (ap->a_flags & LK_TYPE_MASK) {
+		case LK_UPGRADE:
+		case LK_EXCLUSIVE:
+			if (result == 0 && vp->v_vnlock->lk_recurse == 0) {
+				ip = VTOI(vp);
+				if (ip != NULL)
+					ip->i_lock_gen++;
+			}
+		}
+#endif	/* DIAGNOSTIC */
 		break;
 	default:
+#ifdef DIAGNOSTIC
+		if ((ap->a_flags & LK_TYPE_MASK) == LK_DOWNGRADE) {
+			ip = VTOI(vp);
+			if (ip != NULL)
+				ufs_unlock_tracker(ip);
+		}
+#endif	/* DIAGNOSTIC */
 		result = VOP_LOCK1_APV(&ufs_vnodeops, ap);
+		break;
 	}
-	return (result);
-#else
+#else	/* NO_FFS_SNAPSHOT */
 	/*
 	 * See above for an explanation.
 	 */
 	if ((ap->a_flags & LK_NODDLKTREAT) != 0)
 		ap->a_flags |= LK_ADAPTIVE;
-	return (VOP_LOCK1_APV(&ufs_vnodeops, ap));
-#endif
+#ifdef DIAGNOSTIC
+	if ((ap->a_flags & LK_TYPE_MASK) == LK_DOWNGRADE) {
+		ip = VTOI(vp);
+		if (ip != NULL)
+			ufs_unlock_tracker(ip);
+	}
+#endif	/* DIAGNOSTIC */
+	result =  VOP_LOCK1_APV(&ufs_vnodeops, ap);
+#endif	/* NO_FFS_SNAPSHOT */
+#ifdef DIAGNOSTIC
+	switch (ap->a_flags & LK_TYPE_MASK) {
+	case LK_UPGRADE:
+	case LK_EXCLUSIVE:
+		if (result == 0 && vp->v_vnlock->lk_recurse == 0) {
+			ip = VTOI(vp);
+			if (ip != NULL)
+				ip->i_lock_gen++;
+		}
+	}
+#endif	/* DIAGNOSTIC */
+	return (result);
 }
 
 #ifdef INVARIANTS
 static int
 ffs_unlock_debug(struct vop_unlock_args *ap)
 {
-	struct vnode *vp = ap->a_vp;
-	struct inode *ip = VTOI(vp);
+	struct vnode *vp;
+	struct inode *ip;
 
+	vp = ap->a_vp;
+	ip = VTOI(vp);
 	if (ip->i_flag & UFS_INODE_FLAG_LAZY_MASK_ASSERTABLE) {
 		if ((vp->v_mflag & VMP_LAZYLIST) == 0) {
 			VI_LOCK(vp);
@@ -514,6 +556,11 @@ ffs_unlock_debug(struct vop_unlock_args *ap)
 			VI_UNLOCK(vp);
 		}
 	}
+#ifdef DIAGNOSTIC
+	if (VOP_ISLOCKED(vp) == LK_EXCLUSIVE && ip != NULL &&
+	    vp->v_vnlock->lk_recurse == 0)
+		ufs_unlock_tracker(ip);
+#endif
 	return (VOP_UNLOCK_APV(&ufs_vnodeops, ap));
 }
 #endif

Modified: head/sys/ufs/ufs/inode.h
==============================================================================
--- head/sys/ufs/ufs/inode.h	Sat Nov 14 02:11:56 2020	(r367667)
+++ head/sys/ufs/ufs/inode.h	Sat Nov 14 05:10:39 2020	(r367668)
@@ -44,12 +44,24 @@
 #include <sys/queue.h>
 #include <ufs/ufs/dinode.h>
 #include <sys/seqc.h>
+#ifdef DIAGNOSTIC
+#include <sys/stack.h>
+#endif
 
 /*
  * This must agree with the definition in <ufs/ufs/dir.h>.
  */
 #define	doff_t		int32_t
 
+#ifdef DIAGNOSTIC
+struct iown_tracker {
+	struct thread	*tr_owner;
+	struct stack	tr_st;
+	struct stack	tr_unlock;
+	int		tr_gen;
+};
+#endif
+
 /*
  * The inode is used to describe each active (or recently active) file in the
  * UFS filesystem. It is composed of two types of information. The first part
@@ -94,6 +106,12 @@ struct inode {
 	doff_t	  i_endoff;	/* End of useful stuff in directory. */
 	doff_t	  i_diroff;	/* Offset in dir, where we found last entry. */
 	doff_t	  i_offset;	/* Offset of free space in directory. */
+#ifdef DIAGNOSTIC
+	int			i_lock_gen;
+	struct iown_tracker	i_count_tracker;
+	struct iown_tracker	i_endoff_tracker;
+	struct iown_tracker	i_offset_tracker;
+#endif
 
 	int	i_nextclustercg; /* last cg searched for cluster */
 
@@ -254,6 +272,35 @@ struct ufid {
 	uint32_t  ufid_ino;	/* File number (ino). */
 	uint32_t  ufid_gen;	/* Generation number. */
 };
+
+#ifdef DIAGNOSTIC
+void ufs_init_trackers(struct inode *ip);
+void ufs_unlock_tracker(struct inode *ip);
+
+doff_t ufs_get_i_offset(struct inode *ip, const char *file, int line);
+void ufs_set_i_offset(struct inode *ip, doff_t off, const char *file, int line);
+#define	I_OFFSET(ip)		ufs_get_i_offset(ip, __FILE__, __LINE__)
+#define	SET_I_OFFSET(ip, off)	ufs_set_i_offset(ip, off, __FILE__, __LINE__)
+
+int32_t ufs_get_i_count(struct inode *ip, const char *file, int line);
+void ufs_set_i_count(struct inode *ip, int32_t cnt, const char *file, int line);
+#define	I_COUNT(ip)		ufs_get_i_count(ip, __FILE__, __LINE__)
+#define	SET_I_COUNT(ip, cnt)	ufs_set_i_count(ip, cnt, __FILE__, __LINE__)
+
+doff_t ufs_get_i_endoff(struct inode *ip, const char *file, int line);
+void ufs_set_i_endoff(struct inode *ip, doff_t off, const char *file, int line);
+#define	I_ENDOFF(ip)		ufs_get_i_endoff(ip, __FILE__, __LINE__)
+#define	SET_I_ENDOFF(ip, off)	ufs_set_i_endoff(ip, off, __FILE__, __LINE__)
+
+#else
+#define	I_OFFSET(ip)		((ip)->i_offset)
+#define	SET_I_OFFSET(ip, off)	((ip)->i_offset = (off))
+#define	I_COUNT(ip)		((ip)->i_count)
+#define	SET_I_COUNT(ip, cnt)	((ip)->i_count = cnt)
+#define	I_ENDOFF(ip)		((ip)->i_endoff)
+#define	SET_I_ENDOFF(ip, off)	((ip)->i_endoff = off)
+#endif
+
 #endif /* _KERNEL */
 
 #endif /* !_UFS_UFS_INODE_H_ */

Modified: head/sys/ufs/ufs/ufs_lookup.c
==============================================================================
--- head/sys/ufs/ufs/ufs_lookup.c	Sat Nov 14 02:11:56 2020	(r367667)
+++ head/sys/ufs/ufs/ufs_lookup.c	Sat Nov 14 05:10:39 2020	(r367668)
@@ -66,6 +66,7 @@ __FBSDID("$FreeBSD$");
 #endif
 #include <ufs/ufs/ufsmount.h>
 #include <ufs/ufs/ufs_extern.h>
+#include <ufs/ffs/ffs_extern.h>
 
 #ifdef DIAGNOSTIC
 static int	dirchk = 1;
@@ -504,22 +505,22 @@ notfound:
 		 * dp->i_offset + dp->i_count.
 		 */
 		if (slotstatus == NONE) {
-			dp->i_offset = roundup2(dp->i_size, DIRBLKSIZ);
-			dp->i_count = 0;
-			enduseful = dp->i_offset;
+			SET_I_OFFSET(dp, roundup2(dp->i_size, DIRBLKSIZ));
+			SET_I_COUNT(dp, 0);
+			enduseful = I_OFFSET(dp);
 		} else if (nameiop == DELETE) {
-			dp->i_offset = slotoffset;
-			if ((dp->i_offset & (DIRBLKSIZ - 1)) == 0)
-				dp->i_count = 0;
+			SET_I_OFFSET(dp, slotoffset);
+			if ((I_OFFSET(dp) & (DIRBLKSIZ - 1)) == 0)
+				SET_I_COUNT(dp, 0);
 			else
-				dp->i_count = dp->i_offset - prevoff;
+				SET_I_COUNT(dp, I_OFFSET(dp) - prevoff);
 		} else {
-			dp->i_offset = slotoffset;
-			dp->i_count = slotsize;
+			SET_I_OFFSET(dp, slotoffset);
+			SET_I_COUNT(dp, slotsize);
 			if (enduseful < slotoffset + slotsize)
 				enduseful = slotoffset + slotsize;
 		}
-		dp->i_endoff = roundup2(enduseful, DIRBLKSIZ);
+		SET_I_ENDOFF(dp, roundup2(enduseful, DIRBLKSIZ));
 		/*
 		 * We return with the directory locked, so that
 		 * the parameters we set up above will still be
@@ -575,24 +576,32 @@ found:
 	if (nameiop == DELETE && (flags & ISLASTCN)) {
 		if (flags & LOCKPARENT)
 			ASSERT_VOP_ELOCKED(vdp, __FUNCTION__);
-		/*
-		 * Return pointer to current entry in dp->i_offset,
-		 * and distance past previous entry (if there
-		 * is a previous entry in this block) in dp->i_count.
-		 * Save directory inode pointer in ndp->ni_dvp for dirremove().
-		 *
-		 * Technically we shouldn't be setting these in the
-		 * WANTPARENT case (first lookup in rename()), but any
-		 * lookups that will result in directory changes will
-		 * overwrite these.
-		 */
-		dp->i_offset = i_offset;
-		if ((dp->i_offset & (DIRBLKSIZ - 1)) == 0)
-			dp->i_count = 0;
-		else
-			dp->i_count = dp->i_offset - prevoff;
+
+		if (VOP_ISLOCKED(vdp) == LK_EXCLUSIVE) {
+			/*
+			 * Return pointer to current entry in
+			 * dp->i_offset, and distance past previous
+			 * entry (if there is a previous entry in this
+			 * block) in dp->i_count.
+			 *
+			 * We shouldn't be setting these in the
+			 * WANTPARENT case (first lookup in rename()), but any
+			 * lookups that will result in directory changes will
+			 * overwrite these.
+			 */
+			SET_I_OFFSET(dp, i_offset);
+			if ((I_OFFSET(dp) & (DIRBLKSIZ - 1)) == 0)
+				SET_I_COUNT(dp, 0);
+			else
+				SET_I_COUNT(dp, I_OFFSET(dp) - prevoff);
+		}
 		if (dd_ino != NULL)
 			return (0);
+
+		/*
+		 * Save directory inode pointer in ndp->ni_dvp for
+		 * dirremove().
+		 */
 		if ((error = VFS_VGET(vdp->v_mount, ino,
 		    LK_EXCLUSIVE, &tdp)) != 0)
 			return (error);
@@ -629,7 +638,7 @@ found:
 		 * Careful about locking second inode.
 		 * This can only occur if the target is ".".
 		 */
-		dp->i_offset = i_offset;
+		SET_I_OFFSET(dp, i_offset);
 		if (dp->i_number == ino)
 			return (EISDIR);
 		if (dd_ino != NULL)
@@ -887,14 +896,14 @@ ufs_direnter(dvp, tvp, dirp, cnp, newdirbp, isrename)
 	dp = VTOI(dvp);
 	newentrysize = DIRSIZ(OFSFMT(dvp), dirp);
 
-	if (dp->i_count == 0) {
+	if (I_COUNT(dp) == 0) {
 		/*
 		 * If dp->i_count is 0, then namei could find no
 		 * space in the directory. Here, dp->i_offset will
 		 * be on a directory block boundary and we will write the
 		 * new entry into a fresh block.
 		 */
-		if (dp->i_offset & (DIRBLKSIZ - 1))
+		if (I_OFFSET(dp) & (DIRBLKSIZ - 1))
 			panic("ufs_direnter: newblk");
 		flags = BA_CLRBUF;
 		if (!DOINGSOFTDEP(dvp) && !DOINGASYNC(dvp))
@@ -907,28 +916,28 @@ ufs_direnter(dvp, tvp, dirp, cnp, newdirbp, isrename)
 		}
 #endif
 		old_isize = dp->i_size;
-		vnode_pager_setsize(dvp, (u_long)dp->i_offset + DIRBLKSIZ);
-		if ((error = UFS_BALLOC(dvp, (off_t)dp->i_offset, DIRBLKSIZ,
+		vnode_pager_setsize(dvp, (u_long)I_OFFSET(dp) + DIRBLKSIZ);
+		if ((error = UFS_BALLOC(dvp, (off_t)I_OFFSET(dp), DIRBLKSIZ,
 		    cr, flags, &bp)) != 0) {
 			if (DOINGSOFTDEP(dvp) && newdirbp != NULL)
 				bdwrite(newdirbp);
 			vnode_pager_setsize(dvp, (u_long)old_isize);
 			return (error);
 		}
-		dp->i_size = dp->i_offset + DIRBLKSIZ;
+		dp->i_size = I_OFFSET(dp) + DIRBLKSIZ;
 		DIP_SET(dp, i_size, dp->i_size);
-		dp->i_endoff = dp->i_size;
+		SET_I_ENDOFF(dp, dp->i_size);
 		UFS_INODE_SET_FLAG(dp, IN_SIZEMOD | IN_CHANGE | IN_UPDATE);
 		dirp->d_reclen = DIRBLKSIZ;
-		blkoff = dp->i_offset &
+		blkoff = I_OFFSET(dp) &
 		    (VFSTOUFS(dvp->v_mount)->um_mountp->mnt_stat.f_iosize - 1);
 		bcopy((caddr_t)dirp, (caddr_t)bp->b_data + blkoff,newentrysize);
 #ifdef UFS_DIRHASH
 		if (dp->i_dirhash != NULL) {
-			ufsdirhash_newblk(dp, dp->i_offset);
-			ufsdirhash_add(dp, dirp, dp->i_offset);
+			ufsdirhash_newblk(dp, I_OFFSET(dp));
+			ufsdirhash_add(dp, dirp, I_OFFSET(dp));
 			ufsdirhash_checkblock(dp, (char *)bp->b_data + blkoff,
-			    dp->i_offset);
+			    I_OFFSET(dp));
 		}
 #endif
 		if (DOINGSOFTDEP(dvp)) {
@@ -944,7 +953,7 @@ ufs_direnter(dvp, tvp, dirp, cnp, newdirbp, isrename)
 				   (bp->b_data + blkoff))->d_reclen = DIRBLKSIZ;
 				blkoff += DIRBLKSIZ;
 			}
-			if (softdep_setup_directory_add(bp, dp, dp->i_offset,
+			if (softdep_setup_directory_add(bp, dp, I_OFFSET(dp),
 			    dirp->d_ino, newdirbp, 1))
 				UFS_INODE_SET_FLAG(dp, IN_NEEDSYNC);
 			if (newdirbp)
@@ -1001,15 +1010,15 @@ ufs_direnter(dvp, tvp, dirp, cnp, newdirbp, isrename)
 	 *
 	 * N.B. - THIS IS AN ARTIFACT OF 4.2 AND SHOULD NEVER HAPPEN.
 	 */
-	if (dp->i_offset + dp->i_count > dp->i_size) {
-		dp->i_size = dp->i_offset + dp->i_count;
+	if (I_OFFSET(dp) + I_COUNT(dp) > dp->i_size) {
+		dp->i_size = I_OFFSET(dp) + I_COUNT(dp);
 		DIP_SET(dp, i_size, dp->i_size);
 		UFS_INODE_SET_FLAG(dp, IN_SIZEMOD | IN_MODIFIED);
 	}
 	/*
 	 * Get the block containing the space for the new directory entry.
 	 */
-	error = UFS_BLKATOFF(dvp, (off_t)dp->i_offset, &dirbuf, &bp);
+	error = UFS_BLKATOFF(dvp, (off_t)I_OFFSET(dp), &dirbuf, &bp);
 	if (error) {
 		if (DOINGSOFTDEP(dvp) && newdirbp != NULL)
 			bdwrite(newdirbp);
@@ -1024,7 +1033,7 @@ ufs_direnter(dvp, tvp, dirp, cnp, newdirbp, isrename)
 	ep = (struct direct *)dirbuf;
 	dsize = ep->d_ino ? DIRSIZ(OFSFMT(dvp), ep) : 0;
 	spacefree = ep->d_reclen - dsize;
-	for (loc = ep->d_reclen; loc < dp->i_count; ) {
+	for (loc = ep->d_reclen; loc < I_COUNT(dp); ) {
 		nep = (struct direct *)(dirbuf + loc);
 
 		/* Trim the existing slot (NB: dsize may be zero). */
@@ -1052,8 +1061,8 @@ ufs_direnter(dvp, tvp, dirp, cnp, newdirbp, isrename)
 #ifdef UFS_DIRHASH
 		if (dp->i_dirhash != NULL)
 			ufsdirhash_move(dp, nep,
-			    dp->i_offset + ((char *)nep - dirbuf),
-			    dp->i_offset + ((char *)ep - dirbuf));
+			    I_OFFSET(dp) + ((char *)nep - dirbuf),
+			    I_OFFSET(dp) + ((char *)ep - dirbuf));
 #endif
 		if (DOINGSOFTDEP(dvp))
 			softdep_change_directoryentry_offset(bp, dp, dirbuf,
@@ -1094,19 +1103,19 @@ ufs_direnter(dvp, tvp, dirp, cnp, newdirbp, isrename)
 #ifdef UFS_DIRHASH
 	if (dp->i_dirhash != NULL && (ep->d_ino == 0 ||
 	    dirp->d_reclen == spacefree))
-		ufsdirhash_add(dp, dirp, dp->i_offset + ((char *)ep - dirbuf));
+		ufsdirhash_add(dp, dirp, I_OFFSET(dp) + ((char *)ep - dirbuf));
 #endif
 	bcopy((caddr_t)dirp, (caddr_t)ep, (u_int)newentrysize);
 #ifdef UFS_DIRHASH
 	if (dp->i_dirhash != NULL)
 		ufsdirhash_checkblock(dp, dirbuf -
-		    (dp->i_offset & (DIRBLKSIZ - 1)),
-		    rounddown2(dp->i_offset, DIRBLKSIZ));
+		    (I_OFFSET(dp) & (DIRBLKSIZ - 1)),
+		    rounddown2(I_OFFSET(dp), DIRBLKSIZ));
 #endif
 
 	if (DOINGSOFTDEP(dvp)) {
 		(void) softdep_setup_directory_add(bp, dp,
-		    dp->i_offset + (caddr_t)ep - dirbuf,
+		    I_OFFSET(dp) + (caddr_t)ep - dirbuf,
 		    dirp->d_ino, newdirbp, 0);
 		if (newdirbp != NULL)
 			bdwrite(newdirbp);
@@ -1128,10 +1137,10 @@ ufs_direnter(dvp, tvp, dirp, cnp, newdirbp, isrename)
 	 * lock on the newly entered node.
 	 */
 	if (isrename == 0 && error == 0 &&
-	    dp->i_endoff && dp->i_endoff < dp->i_size) {
+	    I_ENDOFF(dp) != 0 && I_ENDOFF(dp) < dp->i_size) {
 		if (tvp != NULL)
 			VOP_UNLOCK(tvp);
-		error = UFS_TRUNCATE(dvp, (off_t)dp->i_endoff,
+		error = UFS_TRUNCATE(dvp, (off_t)I_ENDOFF(dp),
 		    IO_NORMAL | (DOINGASYNC(dvp) ? 0 : IO_SYNC), cr);
 		if (error != 0)
 			vn_printf(dvp,
@@ -1139,7 +1148,7 @@ ufs_direnter(dvp, tvp, dirp, cnp, newdirbp, isrename)
 			    error);
 #ifdef UFS_DIRHASH
 		if (error == 0 && dp->i_dirhash != NULL)
-			ufsdirhash_dirtrunc(dp, dp->i_endoff);
+			ufsdirhash_dirtrunc(dp, I_ENDOFF(dp));
 #endif
 		error = 0;
 		if (tvp != NULL)
@@ -1190,9 +1199,9 @@ ufs_dirremove(dvp, ip, flags, isrmdir)
 		}
 	}
 	if (flags & DOWHITEOUT)
-		offset = dp->i_offset;
+		offset = I_OFFSET(dp);
 	else
-		offset = dp->i_offset - dp->i_count;
+		offset = I_OFFSET(dp) - I_COUNT(dp);
 	if ((error = UFS_BLKATOFF(dvp, offset, (char **)&ep, &bp)) != 0) {
 		if (ip) {
 			ip->i_effnlink++;
@@ -1216,7 +1225,7 @@ ufs_dirremove(dvp, ip, flags, isrmdir)
 		goto out;
 	}
 	/* Set 'rep' to the entry being removed. */
-	if (dp->i_count == 0)
+	if (I_COUNT(dp) == 0)
 		rep = ep;
 	else
 		rep = (struct direct *)((char *)ep + ep->d_reclen);
@@ -1226,7 +1235,7 @@ ufs_dirremove(dvp, ip, flags, isrmdir)
 	 * that `ep' is the previous entry when dp->i_count != 0.
 	 */
 	if (dp->i_dirhash != NULL)
-		ufsdirhash_remove(dp, rep, dp->i_offset);
+		ufsdirhash_remove(dp, rep, I_OFFSET(dp));
 #endif
 	if (ip && rep->d_ino != ip->i_number)
 		panic("ufs_dirremove: ip %ju does not match dirent ino %ju\n",
@@ -1240,7 +1249,7 @@ ufs_dirremove(dvp, ip, flags, isrmdir)
 	rep->d_type = 0;
 	rep->d_ino = 0;
 
-	if (dp->i_count != 0) {
+	if (I_COUNT(dp) != 0) {
 		/*
 		 * Collapse new free space into previous entry.
 		 */
@@ -1250,8 +1259,8 @@ ufs_dirremove(dvp, ip, flags, isrmdir)
 #ifdef UFS_DIRHASH
 	if (dp->i_dirhash != NULL)
 		ufsdirhash_checkblock(dp, (char *)ep -
-		    ((dp->i_offset - dp->i_count) & (DIRBLKSIZ - 1)),
-		    rounddown2(dp->i_offset, DIRBLKSIZ));
+		    ((I_OFFSET(dp) - I_COUNT(dp)) & (DIRBLKSIZ - 1)),
+		    rounddown2(I_OFFSET(dp), DIRBLKSIZ));
 #endif
 out:
 	error = 0;
@@ -1313,7 +1322,7 @@ ufs_dirrewrite(dp, oip, newinum, newtype, isrmdir)
 		UFS_INODE_SET_FLAG(oip, IN_CHANGE);
 	}
 
-	error = UFS_BLKATOFF(vdp, (off_t)dp->i_offset, (char **)&ep, &bp);
+	error = UFS_BLKATOFF(vdp, (off_t)I_OFFSET(dp), (char **)&ep, &bp);
 	if (error == 0 && ep->d_namlen == 2 && ep->d_name[1] == '.' &&
 	    ep->d_name[0] == '.' && ep->d_ino != oip->i_number) {
 		brelse(bp);
@@ -1522,3 +1531,115 @@ ufs_checkpath(ino_t source_ino, ino_t parent_ino, stru
 		vput(vp);
 	return (error);
 }
+
+#ifdef DIAGNOSTIC
+static void
+ufs_assert_inode_offset_owner(struct inode *ip, struct iown_tracker *tr,
+    const char *name, const char *file, int line)
+{
+	char msg[128];
+
+	snprintf(msg, sizeof(msg), "at %s@%d", file, line);
+	ASSERT_VOP_ELOCKED(ITOV(ip), msg);
+	MPASS((ip->i_mode & IFMT) == IFDIR);
+	if (curthread == tr->tr_owner && ip->i_lock_gen == tr->tr_gen)
+		return;
+	printf("locked at\n");
+	stack_print(&tr->tr_st);
+	printf("unlocked at\n");
+	stack_print(&tr->tr_unlock);
+	panic("%s ip %p %jd offset owner %p %d gen %d "
+	    "curthread %p %d gen %d at %s@%d\n",
+	    name, ip, (uintmax_t)ip->i_number, tr->tr_owner,
+	    tr->tr_owner->td_tid, tr->tr_gen,
+	    curthread, curthread->td_tid, ip->i_lock_gen,
+	    file, line);
+}
+
+static void
+ufs_set_inode_offset_owner(struct inode *ip, struct iown_tracker *tr,
+    const char *file, int line)
+{
+	char msg[128];
+
+	snprintf(msg, sizeof(msg), "at %s@%d", file, line);
+	ASSERT_VOP_ELOCKED(ITOV(ip), msg);
+	MPASS((ip->i_mode & IFMT) == IFDIR);
+	tr->tr_owner = curthread;
+	tr->tr_gen = ip->i_lock_gen;
+	stack_save(&tr->tr_st);
+}
+
+static void
+ufs_init_one_tracker(struct iown_tracker *tr)
+{
+	tr->tr_owner = NULL;
+	stack_zero(&tr->tr_st);
+}
+
+void
+ufs_init_trackers(struct inode *ip)
+{
+	ufs_init_one_tracker(&ip->i_offset_tracker);
+	ufs_init_one_tracker(&ip->i_count_tracker);
+	ufs_init_one_tracker(&ip->i_endoff_tracker);
+}
+
+void
+ufs_unlock_tracker(struct inode *ip)
+{
+	if (ip->i_count_tracker.tr_gen == ip->i_lock_gen)
+		stack_save(&ip->i_count_tracker.tr_unlock);
+	if (ip->i_offset_tracker.tr_gen == ip->i_lock_gen)
+		stack_save(&ip->i_offset_tracker.tr_unlock);
+	if (ip->i_endoff_tracker.tr_gen == ip->i_lock_gen)
+		stack_save(&ip->i_endoff_tracker.tr_unlock);
+	ip->i_lock_gen++;
+}
+
+doff_t
+ufs_get_i_offset(struct inode *ip, const char *file, int line)
+{
+	ufs_assert_inode_offset_owner(ip, &ip->i_offset_tracker, "i_offset",
+	    file, line);
+	return (ip->i_offset);
+}
+
+void
+ufs_set_i_offset(struct inode *ip, doff_t off, const char *file, int line)
+{
+	ufs_set_inode_offset_owner(ip, &ip->i_offset_tracker, file, line);
+	ip->i_offset = off;
+}
+
+int32_t
+ufs_get_i_count(struct inode *ip, const char *file, int line)
+{
+	ufs_assert_inode_offset_owner(ip, &ip->i_count_tracker, "i_count",
+	    file, line);
+	return (ip->i_count);
+}
+
+void
+ufs_set_i_count(struct inode *ip, int32_t cnt, const char *file, int line)
+{
+	ufs_set_inode_offset_owner(ip, &ip->i_count_tracker, file, line);
+	ip->i_count = cnt;
+}
+
+doff_t
+ufs_get_i_endoff(struct inode *ip, const char *file, int line)
+{
+	ufs_assert_inode_offset_owner(ip, &ip->i_endoff_tracker, "i_endoff",
+	    file, line);
+	return (ip->i_endoff);
+}
+
+void
+ufs_set_i_endoff(struct inode *ip, doff_t off, const char *file, int line)
+{
+	ufs_set_inode_offset_owner(ip, &ip->i_endoff_tracker, file, line);
+	ip->i_endoff = off;
+}
+
+#endif

Modified: head/sys/ufs/ufs/ufs_vnops.c
==============================================================================
--- head/sys/ufs/ufs/ufs_vnops.c	Sat Nov 14 02:11:56 2020	(r367667)
+++ head/sys/ufs/ufs/ufs_vnops.c	Sat Nov 14 05:10:39 2020	(r367668)
@@ -1481,9 +1481,9 @@ relock:
 		if (error)
 			goto bad;
 		/* Setup tdvp for directory compaction if needed. */
-		if (tdp->i_count && tdp->i_endoff &&
-		    tdp->i_endoff < tdp->i_size)
-			endoff = tdp->i_endoff;
+		if (I_COUNT(tdp) != 0 && I_ENDOFF(tdp) != 0 &&
+		    I_ENDOFF(tdp) < tdp->i_size)
+			endoff = I_ENDOFF(tdp);
 	} else {
 		if (ITODEV(tip) != ITODEV(tdp) || ITODEV(tip) != ITODEV(fip))
 			panic("ufs_rename: EXDEV");
@@ -1611,7 +1611,7 @@ relock:
 		} else if (DOINGSUJ(tdvp))
 			/* Journal must account for each new link. */
 			softdep_setup_dotdot_link(tdp, fip);
-		fip->i_offset = mastertemplate.dot_reclen;
+		SET_I_OFFSET(fip, mastertemplate.dot_reclen);
 		ufs_dirrewrite(fip, fdp, newparent, DT_DIR, 0);
 		cache_purge(fdvp);
 	}


More information about the svn-src-all mailing list