git: 74a3652f832f - main - ufs_direnter: move directory truncation to ffs_vput_pair().

Konstantin Belousov kib at FreeBSD.org
Fri Feb 12 01:07:25 UTC 2021


The branch main has been updated by kib:

URL: https://cgit.FreeBSD.org/src/commit/?id=74a3652f832f4ed0f1ad9f7eb60d70013b478e1a

commit 74a3652f832f4ed0f1ad9f7eb60d70013b478e1a
Author:     Konstantin Belousov <kib at FreeBSD.org>
AuthorDate: 2021-01-27 20:34:14 +0000
Commit:     Konstantin Belousov <kib at FreeBSD.org>
CommitDate: 2021-02-12 01:02:21 +0000

    ufs_direnter: move directory truncation to ffs_vput_pair().
    
    VOP_VPUT_PAIR() provides the hook to do the truncation right before
    unlock, which is required since truncation might need to fsync(), which
    itself might unlock the directory vnode.
    
    Set new flag IN_ENDOFF which indicates that i_endoff is valid and should
    be checked against inode size. Excessive size is chomped, but this
    operation is advisory and failure to truncate should not result in the
    failure of the main VOP.
    
    Reviewed by:    chs, mckusick
    Tested by:      pho
    MFC after:      2 weeks
    Sponsored by:   The FreeBSD Foundation
---
 sys/ufs/ffs/ffs_vnops.c  | 44 ++++++++++++++++++++++++++++++++++++++------
 sys/ufs/ufs/inode.h      |  2 ++
 sys/ufs/ufs/ufs_lookup.c | 25 ++++++-------------------
 3 files changed, 46 insertions(+), 25 deletions(-)

diff --git a/sys/ufs/ffs/ffs_vnops.c b/sys/ufs/ffs/ffs_vnops.c
index 2ac67adad5f2..dd0f1ba6b81d 100644
--- a/sys/ufs/ffs/ffs_vnops.c
+++ b/sys/ufs/ffs/ffs_vnops.c
@@ -68,6 +68,7 @@ __FBSDID("$FreeBSD$");
 
 #include "opt_directio.h"
 #include "opt_ffs.h"
+#include "opt_ufs.h"
 
 #include <sys/param.h>
 #include <sys/bio.h>
@@ -99,6 +100,10 @@ __FBSDID("$FreeBSD$");
 #include <ufs/ufs/inode.h>
 #include <ufs/ufs/ufs_extern.h>
 #include <ufs/ufs/ufsmount.h>
+#include <ufs/ufs/dir.h>
+#ifdef UFS_DIRHASH
+#include <ufs/ufs/dirhash.h>
+#endif
 
 #include <ufs/ffs/fs.h>
 #include <ufs/ffs/ffs_extern.h>
@@ -1929,6 +1934,7 @@ ffs_vput_pair(struct vop_vput_pair_args *ap)
 	struct inode *dp, *ip;
 	ino_t ip_ino;
 	u_int64_t ip_gen;
+	off_t old_size;
 	int error, vp_locked;
 
 	dvp = ap->a_dvp;
@@ -1936,14 +1942,14 @@ ffs_vput_pair(struct vop_vput_pair_args *ap)
 	vpp = ap->a_vpp;
 	vp = vpp != NULL ? *vpp : NULL;
 
-	if ((dp->i_flag & IN_NEEDSYNC) == 0) {
+	if ((dp->i_flag & (IN_NEEDSYNC | IN_ENDOFF)) == 0) {
 		vput(dvp);
 		if (vp != NULL && ap->a_unlock_vp)
 			vput(vp);
 		return (0);
 	}
 
-	mp = NULL;
+	mp = dvp->v_mount;
 	if (vp != NULL) {
 		if (ap->a_unlock_vp) {
 			vput(vp);
@@ -1953,14 +1959,40 @@ ffs_vput_pair(struct vop_vput_pair_args *ap)
 			ip = VTOI(vp);
 			ip_ino = ip->i_number;
 			ip_gen = ip->i_gen;
-			mp = vp->v_mount;
 			VOP_UNLOCK(vp);
 		}
 	}
 
-	do {
-		error = ffs_syncvnode(dvp, MNT_WAIT, 0);
-	} while (error == ERELOOKUP);
+	/*
+	 * If compaction or fsync was requested do it in ffs_vput_pair()
+	 * now that other locks are no longer held.
+         */
+	if ((dp->i_flag & IN_ENDOFF) != 0) {
+		dp->i_flag &= ~IN_ENDOFF;
+		if (I_ENDOFF(dp) != 0 && I_ENDOFF(dp) < dp->i_size) {
+			old_size = dp->i_size;
+			error = UFS_TRUNCATE(dvp, (off_t)I_ENDOFF(dp),
+			    IO_NORMAL | (DOINGASYNC(dvp) ? 0 : IO_SYNC),
+			    curthread->td_ucred);
+			if (error != 0 && error != ERELOOKUP) {
+				if (!ffs_fsfail_cleanup(VFSTOUFS(mp), error)) {
+					vn_printf(dvp,
+					    "IN_ENDOFF: failed to truncate, "
+					    "error %d\n", error);
+				}
+#ifdef UFS_DIRHASH
+				ufsdirhash_free(dp);
+#endif
+			}
+		}
+		SET_I_ENDOFF(dp, 0);
+	}
+	if ((dp->i_flag & IN_NEEDSYNC) != 0) {
+		do {
+			error = ffs_syncvnode(dvp, MNT_WAIT, 0);
+		} while (error == ERELOOKUP);
+	}
+
 	vput(dvp);
 
 	if (vp == NULL || ap->a_unlock_vp)
diff --git a/sys/ufs/ufs/inode.h b/sys/ufs/ufs/inode.h
index 16db8d6d5cea..4515dcbed401 100644
--- a/sys/ufs/ufs/inode.h
+++ b/sys/ufs/ufs/inode.h
@@ -152,6 +152,8 @@ struct inode {
 #define	IN_IBLKDATA	0x0800		/* datasync requires inode block
 					   update */
 #define	IN_SIZEMOD	0x1000		/* Inode size has been modified */
+#define	IN_ENDOFF	0x2000		/* Free space at the end of directory,
+					   try to truncate when possible */
 
 #define PRINT_INODE_FLAGS "\20\20b16\17b15\16b14\15sizemod" \
 	"\14iblkdata\13is_ufs2\12truncated\11ea_lockwait\10ea_locked" \
diff --git a/sys/ufs/ufs/ufs_lookup.c b/sys/ufs/ufs/ufs_lookup.c
index e614f189a623..3036bce81caf 100644
--- a/sys/ufs/ufs/ufs_lookup.c
+++ b/sys/ufs/ufs/ufs_lookup.c
@@ -1112,27 +1112,14 @@ ufs_direnter(dvp, tvp, dirp, cnp, newdirbp, isrename)
 		}
 	}
 	UFS_INODE_SET_FLAG(dp, IN_CHANGE | IN_UPDATE);
+
 	/*
-	 * If all went well, and the directory can be shortened, proceed
-	 * with the truncation. Note that we have to unlock the inode for
-	 * the entry that we just entered, as the truncation may need to
-	 * lock other inodes which can lead to deadlock if we also hold a
-	 * lock on the newly entered node.
+	 * If all went well, and the directory can be shortened, mark directory inode
+	 * with the truncation request right before unlock.
 	 */
-	if (isrename == 0 && error == 0 &&
-	    I_ENDOFF(dp) != 0 && I_ENDOFF(dp) < dp->i_size) {
-		if (tvp != NULL)
-			VOP_UNLOCK(tvp);
-		error = UFS_TRUNCATE(dvp, (off_t)I_ENDOFF(dp),
-		    IO_NORMAL | (DOINGASYNC(dvp) ? 0 : IO_SYNC), cr);
-		if (error != 0)
-			vn_printf(dvp,
-			    "ufs_direnter: failed to truncate, error %d\n",
-			    error);
-		error = 0;
-		if (tvp != NULL)
-			vn_lock(tvp, LK_EXCLUSIVE | LK_RETRY);
-	}
+	if (isrename == 0 && error == 0)
+		UFS_INODE_SET_FLAG(dp, IN_ENDOFF);
+
 	return (error);
 }
 


More information about the dev-commits-src-all mailing list