aboutsummaryrefslogtreecommitdiff
path: root/sys/fs
diff options
context:
space:
mode:
authorKonstantin Belousov <kib@FreeBSD.org>2020-10-19 19:23:22 +0000
committerKonstantin Belousov <kib@FreeBSD.org>2020-10-19 19:23:22 +0000
commit6b56b0ca93412306967a3be3b75f7ee208db90c9 (patch)
tree5207c22980d86dac4928028dfeb5d96d00cec5f7 /sys/fs
parentc0baa3dc4a8c5613f4d7ac4dea4073b4f210c8b8 (diff)
downloadsrc-6b56b0ca93412306967a3be3b75f7ee208db90c9.tar.gz
src-6b56b0ca93412306967a3be3b75f7ee208db90c9.zip
nullfs: ensure correct lock is taken after bypass.
If lower VOP relocked the lower vnode, it is possible that nullfs vnode was reclaimed meantime. In this case nullfs vnode no longer shares lock with lower vnode, which breaks locking protocol. Check for the condition and acquire nullfs vnode lock if detected. Reported and tested by: pho Sponsored by: The FreeBSD Foundation MFC after: 1 week
Notes
Notes: svn path=/head/; revision=366849
Diffstat (limited to 'sys/fs')
-rw-r--r--sys/fs/nullfs/null_vnops.c18
1 files changed, 18 insertions, 0 deletions
diff --git a/sys/fs/nullfs/null_vnops.c b/sys/fs/nullfs/null_vnops.c
index eba498d2554c..bb24ce60c177 100644
--- a/sys/fs/nullfs/null_vnops.c
+++ b/sys/fs/nullfs/null_vnops.c
@@ -227,6 +227,7 @@ null_bypass(struct vop_generic_args *ap)
struct vnode *old_vps[VDESC_MAX_VPS];
struct vnode **vps_p[VDESC_MAX_VPS];
struct vnode ***vppp;
+ struct vnode *lvp;
struct vnodeop_desc *descp = ap->a_desc;
int reles, i;
@@ -295,6 +296,23 @@ null_bypass(struct vop_generic_args *ap)
if (descp->vdesc_vp_offsets[i] == VDESC_NO_OFFSET)
break; /* bail out at end of list */
if (old_vps[i]) {
+ lvp = *(vps_p[i]);
+
+ /*
+ * If lowervp was unlocked during VOP
+ * operation, nullfs upper vnode could have
+ * been reclaimed, which changes its v_vnlock
+ * back to private v_lock. In this case we
+ * must move lock ownership from lower to
+ * upper (reclaimed) vnode.
+ */
+ if (lvp != NULLVP &&
+ VOP_ISLOCKED(lvp) == LK_EXCLUSIVE &&
+ old_vps[i]->v_vnlock != lvp->v_vnlock) {
+ VOP_UNLOCK(lvp);
+ VOP_LOCK(old_vps[i], LK_EXCLUSIVE | LK_RETRY);
+ }
+
*(vps_p[i]) = old_vps[i];
#if 0
if (reles & VDESC_VP0_WILLUNLOCK)