aboutsummaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorKonstantin Belousov <kib@FreeBSD.org>2021-01-27 20:34:14 +0000
committerKonstantin Belousov <kib@FreeBSD.org>2021-02-12 01:02:21 +0000
commit74a3652f832f4ed0f1ad9f7eb60d70013b478e1a (patch)
tree562706ff37e05106bee459473b19a6a021a3e51f /sys
parent30bfb2fa0fad8e5bbcce369df46dcaa2e08324f3 (diff)
downloadsrc-74a3652f832f4ed0f1ad9f7eb60d70013b478e1a.tar.gz
src-74a3652f832f4ed0f1ad9f7eb60d70013b478e1a.zip
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
Diffstat (limited to 'sys')
-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);
}