diff --git a/sys/fs/nullfs/null.h b/sys/fs/nullfs/null.h --- a/sys/fs/nullfs/null.h +++ b/sys/fs/nullfs/null.h @@ -64,6 +64,7 @@ #define MOUNTTONULLMOUNT(mp) ((struct null_mount *)((mp)->mnt_data)) #define VTONULL(vp) ((struct null_node *)(vp)->v_data) +#define VTONULL_SMR(vp) ((struct null_node *)vn_load_v_data_smr(vp)) #define NULLTOV(xp) ((xp)->null_vnode) int nullfs_init(struct vfsconf *vfsp); diff --git a/sys/fs/nullfs/null_vnops.c b/sys/fs/nullfs/null_vnops.c --- a/sys/fs/nullfs/null_vnops.c +++ b/sys/fs/nullfs/null_vnops.c @@ -177,6 +177,8 @@ #include #include #include +#include +#include #include @@ -185,10 +187,17 @@ #include #include +VFS_SMR_DECLARE; + static int null_bug_bypass = 0; /* for debugging: enables bypass printf'ing */ SYSCTL_INT(_debug, OID_AUTO, nullfs_bug_bypass, CTLFLAG_RW, &null_bug_bypass, 0, ""); +static bool null_smr_lock = true; +SYSCTL_BOOL(_debug, OID_AUTO, nullfs_smr_lock, CTLFLAG_RW, + &null_smr_lock, 0, ""); + + /* * Synchronize inotify flags with the lower vnode: * - If the upper vnode has the flag set and the lower does not, then the lower @@ -772,6 +781,43 @@ * interlock flag as it applies only to our vnode, not the * vnodes below us on the stack. */ +static int +null_lock_smr(struct vnode *vp, int flags) +{ + struct null_node *nn; + struct vnode *lvp; + int error; + + if (!atomic_load_bool(&null_smr_lock)) + return (EAGAIN); + + vfs_smr_enter(); + + nn = VTONULL_SMR(vp); + if (__predict_false(nn == NULL)) + goto out_bad; + + lvp = nn->null_lowervp; + if (__predict_false(lvp == NULL)) + goto out_bad; + + if (__predict_false(!vhold_smr(lvp))) + goto out_bad; + + vfs_smr_exit(); + + error = VOP_LOCK(lvp, flags); + if (VTONULL(vp) == NULL && error == 0) { + VOP_UNLOCK(lvp); + error = EAGAIN; + } + vdrop(lvp); + return (error); +out_bad: + vfs_smr_exit(); + return (EAGAIN); +} + static int null_lock(struct vop_lock1_args *ap) { @@ -781,10 +827,14 @@ struct vnode *lvp; int error; - if ((ap->a_flags & LK_INTERLOCK) == 0) + if ((ap->a_flags & LK_INTERLOCK) == 0) { + error = null_lock_smr(vp, ap->a_flags); + if (error == 0) + return (error); VI_LOCK(vp); - else + } else { ap->a_flags &= ~LK_INTERLOCK; + } flags = ap->a_flags; nn = VTONULL(vp); /*