aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/sys/namei.h12
-rw-r--r--sys/ufs/ffs/ffs_extern.h3
-rw-r--r--sys/ufs/ffs/ffs_softdep.c32
-rw-r--r--sys/ufs/ufs/ufs_vnops.c12
4 files changed, 46 insertions, 13 deletions
diff --git a/sys/sys/namei.h b/sys/sys/namei.h
index b4db0e758e2b..9e0a82ea1659 100644
--- a/sys/sys/namei.h
+++ b/sys/sys/namei.h
@@ -38,6 +38,7 @@
#include <sys/caprights.h>
#include <sys/filedesc.h>
#include <sys/queue.h>
+#include <sys/_seqc.h>
#include <sys/_uio.h>
enum nameiop { LOOKUP, CREATE, DELETE, RENAME };
@@ -111,6 +112,12 @@ struct nameidata {
*/
struct componentname ni_cnd;
struct nameicap_tracker_head ni_cap_tracker;
+ /*
+ * Private helper data for UFS, must be at the end. See
+ * NDINIT_PREFILL().
+ */
+ seqc_t ni_dvp_seqc;
+ seqc_t ni_vp_seqc;
};
#ifdef _KERNEL
@@ -224,7 +231,8 @@ int cache_fplookup(struct nameidata *ndp, enum cache_fpl_status *status,
* Note the constant pattern may *hide* bugs.
*/
#ifdef INVARIANTS
-#define NDINIT_PREFILL(arg) memset(arg, 0xff, sizeof(*arg))
+#define NDINIT_PREFILL(arg) memset(arg, 0xff, offsetof(struct nameidata, \
+ ni_dvp_seqc))
#define NDINIT_DBG(arg) { (arg)->ni_debugflags = NAMEI_DBG_INITED; }
#define NDREINIT_DBG(arg) { \
if (((arg)->ni_debugflags & NAMEI_DBG_INITED) == 0) \
@@ -266,6 +274,8 @@ do { \
} while (0)
#define NDPREINIT(ndp) do { \
+ (ndp)->ni_dvp_seqc = SEQC_MOD; \
+ (ndp)->ni_vp_seqc = SEQC_MOD; \
} while (0)
#define NDF_NO_DVP_RELE 0x00000001
diff --git a/sys/ufs/ffs/ffs_extern.h b/sys/ufs/ffs/ffs_extern.h
index 7080edd21ab3..2ea828861b42 100644
--- a/sys/ufs/ffs/ffs_extern.h
+++ b/sys/ufs/ffs/ffs_extern.h
@@ -181,7 +181,8 @@ int softdep_request_cleanup(struct fs *, struct vnode *,
struct ucred *, int);
int softdep_prerename(struct vnode *, struct vnode *, struct vnode *,
struct vnode *);
-int softdep_prelink(struct vnode *, struct vnode *);
+int softdep_prelink(struct vnode *, struct vnode *,
+ struct componentname *);
void softdep_setup_freeblocks(struct inode *, off_t, int);
void softdep_setup_inomapdep(struct buf *, struct inode *, ino_t, int);
void softdep_setup_blkmapdep(struct buf *, struct mount *, ufs2_daddr_t,
diff --git a/sys/ufs/ffs/ffs_softdep.c b/sys/ufs/ffs/ffs_softdep.c
index 2a76ec41f03d..d274e0898dfb 100644
--- a/sys/ufs/ffs/ffs_softdep.c
+++ b/sys/ufs/ffs/ffs_softdep.c
@@ -621,9 +621,10 @@ softdep_prerename(fdvp, fvp, tdvp, tvp)
}
int
-softdep_prelink(dvp, vp)
+softdep_prelink(dvp, vp, cnp)
struct vnode *dvp;
struct vnode *vp;
+ struct componentname *cnp;
{
panic("softdep_prelink called");
@@ -3384,11 +3385,13 @@ softdep_prerename(fdvp, fvp, tdvp, tvp)
* syscall must be restarted at top level from the lookup.
*/
int
-softdep_prelink(dvp, vp)
+softdep_prelink(dvp, vp, cnp)
struct vnode *dvp;
struct vnode *vp;
+ struct componentname *cnp;
{
struct ufsmount *ump;
+ struct nameidata *ndp;
ASSERT_VOP_ELOCKED(dvp, "prelink dvp");
if (vp != NULL)
@@ -3404,13 +3407,28 @@ softdep_prelink(dvp, vp)
if (journal_space(ump, 0) || (vp != NULL && IS_SNAPSHOT(VTOI(vp))))
return (0);
+ /*
+ * Check if the journal space consumption can in theory be
+ * accounted on dvp and vp. If the vnodes metadata was not
+ * changed comparing with the previous round-trip into
+ * softdep_prelink(), as indicated by the seqc generation
+ * recorded in the nameidata, then there is no point in
+ * starting the sync.
+ */
+ ndp = __containerof(cnp, struct nameidata, ni_cnd);
+ if (!seqc_in_modify(ndp->ni_dvp_seqc) &&
+ vn_seqc_consistent(dvp, ndp->ni_dvp_seqc) &&
+ (vp == NULL || (!seqc_in_modify(ndp->ni_vp_seqc) &&
+ vn_seqc_consistent(vp, ndp->ni_vp_seqc))))
+ return (0);
+
stat_journal_low++;
if (vp != NULL) {
VOP_UNLOCK(dvp);
ffs_syncvnode(vp, MNT_NOWAIT, 0);
vn_lock_pair(dvp, false, vp, true);
if (dvp->v_data == NULL)
- return (ERELOOKUP);
+ goto out;
}
if (vp != NULL)
VOP_UNLOCK(vp);
@@ -3421,7 +3439,7 @@ softdep_prelink(dvp, vp)
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
if (vp->v_data == NULL) {
vn_lock_pair(dvp, false, vp, true);
- return (ERELOOKUP);
+ goto out;
}
ACQUIRE_LOCK(ump);
process_removes(vp);
@@ -3431,7 +3449,7 @@ softdep_prelink(dvp, vp)
vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY);
if (dvp->v_data == NULL) {
vn_lock_pair(dvp, true, vp, false);
- return (ERELOOKUP);
+ goto out;
}
}
@@ -3450,6 +3468,10 @@ softdep_prelink(dvp, vp)
FREE_LOCK(ump);
vn_lock_pair(dvp, false, vp, false);
+out:
+ ndp->ni_dvp_seqc = vn_seqc_read_any(dvp);
+ if (vp != NULL)
+ ndp->ni_vp_seqc = vn_seqc_read_any(vp);
return (ERELOOKUP);
}
diff --git a/sys/ufs/ufs/ufs_vnops.c b/sys/ufs/ufs/ufs_vnops.c
index b0fb1b74b900..2dfc2e24f772 100644
--- a/sys/ufs/ufs/ufs_vnops.c
+++ b/sys/ufs/ufs/ufs_vnops.c
@@ -1010,7 +1010,7 @@ ufs_remove(ap)
(VTOI(dvp)->i_flags & APPEND))
return (EPERM);
if (DOINGSUJ(dvp)) {
- error = softdep_prelink(dvp, vp);
+ error = softdep_prelink(dvp, vp, ap->a_cnp);
if (error != 0) {
MPASS(error == ERELOOKUP);
return (error);
@@ -1075,7 +1075,7 @@ ufs_link(ap)
#endif
if (DOINGSUJ(tdvp)) {
- error = softdep_prelink(tdvp, vp);
+ error = softdep_prelink(tdvp, vp, cnp);
if (error != 0) {
MPASS(error == ERELOOKUP);
return (error);
@@ -1147,7 +1147,7 @@ ufs_whiteout(ap)
if (DOINGSUJ(dvp) && (ap->a_flags == CREATE ||
ap->a_flags == DELETE)) {
- error = softdep_prelink(dvp, NULL);
+ error = softdep_prelink(dvp, NULL, cnp);
if (error != 0) {
MPASS(error == ERELOOKUP);
return (error);
@@ -1962,7 +1962,7 @@ ufs_mkdir(ap)
}
if (DOINGSUJ(dvp)) {
- error = softdep_prelink(dvp, NULL);
+ error = softdep_prelink(dvp, NULL, cnp);
if (error != 0) {
MPASS(error == ERELOOKUP);
return (error);
@@ -2226,7 +2226,7 @@ ufs_rmdir(ap)
goto out;
}
if (DOINGSUJ(dvp)) {
- error = softdep_prelink(dvp, vp);
+ error = softdep_prelink(dvp, vp, cnp);
if (error != 0) {
MPASS(error == ERELOOKUP);
return (error);
@@ -2751,7 +2751,7 @@ ufs_makeinode(mode, dvp, vpp, cnp, callfunc)
return (EINVAL);
}
if (DOINGSUJ(dvp)) {
- error = softdep_prelink(dvp, NULL);
+ error = softdep_prelink(dvp, NULL, cnp);
if (error != 0) {
MPASS(error == ERELOOKUP);
return (error);