aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKonstantin Belousov <kib@FreeBSD.org>2021-01-27 20:34:14 +0000
committerKonstantin Belousov <kib@FreeBSD.org>2021-02-25 20:49:09 +0000
commitb62d17802dcf9b589b6572a010e1a65820117155 (patch)
treea5d6b7aadcf69a6eb5623bad8bcd5e2b995a8239
parent0c3cdbf4293afa1f6faa50e368e47327957ceb85 (diff)
downloadsrc-b62d17802dcf9b589b6572a010e1a65820117155.tar.gz
src-b62d17802dcf9b589b6572a010e1a65820117155.zip
ufs_direnter: move directory truncation to ffs_vput_pair().
Approved by: re (delphij, gjb) (cherry picked from commit 74a3652f832f4ed0f1ad9f7eb60d70013b478e1a)
-rw-r--r--sys/ufs/ffs/ffs_vnops.c44
-rw-r--r--sys/ufs/ufs/inode.h2
-rw-r--r--sys/ufs/ufs/ufs_lookup.c25
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);
}