summaryrefslogtreecommitdiffstats
path: root/sys/fs/nfsserver
diff options
context:
space:
mode:
authorrmacklem <rmacklem@FreeBSD.org>2014-12-25 01:55:17 +0000
committerrmacklem <rmacklem@FreeBSD.org>2014-12-25 01:55:17 +0000
commit5309296f8769d8056f8283b83e051e38d00a2b35 (patch)
tree247fee7ca7f3dffc2ee3acad5ab17683e2db71da /sys/fs/nfsserver
parentdac4c8494064f6eaeaaab703b5d228649ae5a1e9 (diff)
downloadFreeBSD-src-5309296f8769d8056f8283b83e051e38d00a2b35.zip
FreeBSD-src-5309296f8769d8056f8283b83e051e38d00a2b35.tar.gz
A deadlock in the NFSv4 server with vfs.nfsd.enable_locallocks=1
was reported via email. This was caused by a LOR between the sleep lock used to serialize the local locking (nfsrv_locklf()) and locking the vnode. I believe this patch fixes the problem by delaying relocking of the vnode until the sleep lock is unlocked (nfsrv_unlocklf()). To avoid nfsvno_advlock() having the side effect of unlocking the vnode, unlocking the vnode was moved to before the functions that call nfsvno_advlock(). It shouldn't affect the execution of the default case where vfs.nfsd.enable_locallocks=0. Reported by: loic.blot@unix-experience.fr Discussed with: kib MFC after: 1 week
Diffstat (limited to 'sys/fs/nfsserver')
-rw-r--r--sys/fs/nfsserver/nfs_nfsdport.c9
-rw-r--r--sys/fs/nfsserver/nfs_nfsdstate.c125
2 files changed, 101 insertions, 33 deletions
diff --git a/sys/fs/nfsserver/nfs_nfsdport.c b/sys/fs/nfsserver/nfs_nfsdport.c
index 5bd4538..17ca5a6 100644
--- a/sys/fs/nfsserver/nfs_nfsdport.c
+++ b/sys/fs/nfsserver/nfs_nfsdport.c
@@ -2970,12 +2970,7 @@ nfsvno_advlock(struct vnode *vp, int ftype, u_int64_t first,
if (nfsrv_dolocallocks == 0)
goto out;
-
- /* Check for VI_DOOMED here, so that VOP_ADVLOCK() isn't performed. */
- if ((vp->v_iflag & VI_DOOMED) != 0) {
- error = EPERM;
- goto out;
- }
+ ASSERT_VOP_UNLOCKED(vp, "nfsvno_advlock: vp locked");
fl.l_whence = SEEK_SET;
fl.l_type = ftype;
@@ -2999,14 +2994,12 @@ nfsvno_advlock(struct vnode *vp, int ftype, u_int64_t first,
fl.l_pid = (pid_t)0;
fl.l_sysid = (int)nfsv4_sysid;
- NFSVOPUNLOCK(vp, 0);
if (ftype == F_UNLCK)
error = VOP_ADVLOCK(vp, (caddr_t)td->td_proc, F_UNLCK, &fl,
(F_POSIX | F_REMOTE));
else
error = VOP_ADVLOCK(vp, (caddr_t)td->td_proc, F_SETLK, &fl,
(F_POSIX | F_REMOTE));
- NFSVOPLOCK(vp, LK_EXCLUSIVE | LK_RETRY);
out:
NFSEXITCODE(error);
diff --git a/sys/fs/nfsserver/nfs_nfsdstate.c b/sys/fs/nfsserver/nfs_nfsdstate.c
index 97f8fff..5e9beeb 100644
--- a/sys/fs/nfsserver/nfs_nfsdstate.c
+++ b/sys/fs/nfsserver/nfs_nfsdstate.c
@@ -1344,6 +1344,8 @@ nfsrv_freeallnfslocks(struct nfsstate *stp, vnode_t vp, int cansleep,
vnode_t tvp = NULL;
uint64_t first, end;
+ if (vp != NULL)
+ ASSERT_VOP_UNLOCKED(vp, "nfsrv_freeallnfslocks: vnode locked");
lop = LIST_FIRST(&stp->ls_lock);
while (lop != LIST_END(&stp->ls_lock)) {
nlop = LIST_NEXT(lop, lo_lckowner);
@@ -1363,9 +1365,10 @@ nfsrv_freeallnfslocks(struct nfsstate *stp, vnode_t vp, int cansleep,
if (gottvp == 0) {
if (nfsrv_dolocallocks == 0)
tvp = NULL;
- else if (vp == NULL && cansleep != 0)
+ else if (vp == NULL && cansleep != 0) {
tvp = nfsvno_getvp(&lfp->lf_fh);
- else
+ NFSVOPUNLOCK(tvp, 0);
+ } else
tvp = vp;
gottvp = 1;
}
@@ -1386,7 +1389,7 @@ nfsrv_freeallnfslocks(struct nfsstate *stp, vnode_t vp, int cansleep,
lop = nlop;
}
if (vp == NULL && tvp != NULL)
- vput(tvp);
+ vrele(tvp);
}
/*
@@ -1497,7 +1500,7 @@ nfsrv_lockctrl(vnode_t vp, struct nfsstate **new_stpp,
struct nfsclient *clp = NULL;
u_int32_t bits;
int error = 0, haslock = 0, ret, reterr;
- int getlckret, delegation = 0, filestruct_locked;
+ int getlckret, delegation = 0, filestruct_locked, vnode_unlocked = 0;
fhandle_t nfh;
uint64_t first, end;
uint32_t lock_flags;
@@ -1587,6 +1590,11 @@ tryagain:
* locking rolled back.
*/
NFSUNLOCKSTATE();
+ if (vnode_unlocked == 0) {
+ ASSERT_VOP_ELOCKED(vp, "nfsrv_lockctrl1");
+ vnode_unlocked = 1;
+ NFSVOPUNLOCK(vp, 0);
+ }
reterr = nfsrv_locallock(vp, lfp,
(new_lop->lo_flags & (NFSLCK_READ | NFSLCK_WRITE)),
new_lop->lo_first, new_lop->lo_end, cfp, p);
@@ -1756,6 +1764,11 @@ tryagain:
if (filestruct_locked != 0) {
/* Roll back local locks. */
NFSUNLOCKSTATE();
+ if (vnode_unlocked == 0) {
+ ASSERT_VOP_ELOCKED(vp, "nfsrv_lockctrl2");
+ vnode_unlocked = 1;
+ NFSVOPUNLOCK(vp, 0);
+ }
nfsrv_locallock_rollback(vp, lfp, p);
NFSLOCKSTATE();
nfsrv_unlocklf(lfp);
@@ -1833,6 +1846,12 @@ tryagain:
if (filestruct_locked != 0) {
/* Roll back local locks. */
NFSUNLOCKSTATE();
+ if (vnode_unlocked == 0) {
+ ASSERT_VOP_ELOCKED(vp,
+ "nfsrv_lockctrl3");
+ vnode_unlocked = 1;
+ NFSVOPUNLOCK(vp, 0);
+ }
nfsrv_locallock_rollback(vp, lfp, p);
NFSLOCKSTATE();
nfsrv_unlocklf(lfp);
@@ -1852,6 +1871,8 @@ tryagain:
bits = tstp->ls_flags;
bits >>= NFSLCK_SHIFT;
if (new_stp->ls_flags & bits & NFSLCK_ACCESSBITS) {
+ KASSERT(vnode_unlocked == 0,
+ ("nfsrv_lockctrl: vnode unlocked1"));
ret = nfsrv_clientconflict(tstp->ls_clp, &haslock,
vp, p);
if (ret == 1) {
@@ -1883,6 +1904,8 @@ tryagain:
* For setattr, just get rid of all the Delegations for other clients.
*/
if (new_stp->ls_flags & NFSLCK_SETATTR) {
+ KASSERT(vnode_unlocked == 0,
+ ("nfsrv_lockctrl: vnode unlocked2"));
ret = nfsrv_cleandeleg(vp, lfp, clp, &haslock, p);
if (ret) {
/*
@@ -1933,14 +1956,26 @@ tryagain:
(new_lop->lo_flags & NFSLCK_WRITE) &&
(clp != tstp->ls_clp ||
(tstp->ls_flags & NFSLCK_DELEGREAD)))) {
+ ret = 0;
if (filestruct_locked != 0) {
/* Roll back local locks. */
NFSUNLOCKSTATE();
+ if (vnode_unlocked == 0) {
+ ASSERT_VOP_ELOCKED(vp, "nfsrv_lockctrl4");
+ NFSVOPUNLOCK(vp, 0);
+ }
nfsrv_locallock_rollback(vp, lfp, p);
NFSLOCKSTATE();
nfsrv_unlocklf(lfp);
+ NFSUNLOCKSTATE();
+ NFSVOPLOCK(vp, LK_EXCLUSIVE | LK_RETRY);
+ vnode_unlocked = 0;
+ if ((vp->v_iflag & VI_DOOMED) != 0)
+ ret = NFSERR_SERVERFAULT;
+ NFSLOCKSTATE();
}
- ret = nfsrv_delegconflict(tstp, &haslock, p, vp);
+ if (ret == 0)
+ ret = nfsrv_delegconflict(tstp, &haslock, p, vp);
if (ret) {
/*
* nfsrv_delegconflict unlocks state when it
@@ -1979,6 +2014,11 @@ tryagain:
stateidp->other[2] = stp->ls_stateid.other[2];
if (filestruct_locked != 0) {
NFSUNLOCKSTATE();
+ if (vnode_unlocked == 0) {
+ ASSERT_VOP_ELOCKED(vp, "nfsrv_lockctrl5");
+ vnode_unlocked = 1;
+ NFSVOPUNLOCK(vp, 0);
+ }
/* Update the local locks. */
nfsrv_localunlock(vp, lfp, first, end, p);
NFSLOCKSTATE();
@@ -2009,14 +2049,29 @@ tryagain:
FREE((caddr_t)other_lop, M_NFSDLOCK);
other_lop = NULL;
}
- ret = nfsrv_clientconflict(lop->lo_stp->ls_clp,&haslock,vp,p);
+ if (vnode_unlocked != 0)
+ ret = nfsrv_clientconflict(lop->lo_stp->ls_clp, &haslock,
+ NULL, p);
+ else
+ ret = nfsrv_clientconflict(lop->lo_stp->ls_clp, &haslock,
+ vp, p);
if (ret == 1) {
if (filestruct_locked != 0) {
+ if (vnode_unlocked == 0) {
+ ASSERT_VOP_ELOCKED(vp, "nfsrv_lockctrl6");
+ NFSVOPUNLOCK(vp, 0);
+ }
/* Roll back local locks. */
nfsrv_locallock_rollback(vp, lfp, p);
NFSLOCKSTATE();
nfsrv_unlocklf(lfp);
NFSUNLOCKSTATE();
+ NFSVOPLOCK(vp, LK_EXCLUSIVE | LK_RETRY);
+ vnode_unlocked = 0;
+ if ((vp->v_iflag & VI_DOOMED) != 0) {
+ error = NFSERR_SERVERFAULT;
+ goto out;
+ }
}
/*
* nfsrv_clientconflict() unlocks state when it
@@ -2050,6 +2105,11 @@ tryagain:
if (filestruct_locked != 0 && ret == 0) {
/* Roll back local locks. */
NFSUNLOCKSTATE();
+ if (vnode_unlocked == 0) {
+ ASSERT_VOP_ELOCKED(vp, "nfsrv_lockctrl7");
+ vnode_unlocked = 1;
+ NFSVOPUNLOCK(vp, 0);
+ }
nfsrv_locallock_rollback(vp, lfp, p);
NFSLOCKSTATE();
nfsrv_unlocklf(lfp);
@@ -2128,6 +2188,11 @@ out:
nfsv4_unlock(&nfsv4rootfs_lock, 1);
NFSUNLOCKV4ROOTMUTEX();
}
+ if (vnode_unlocked != 0) {
+ NFSVOPLOCK(vp, LK_EXCLUSIVE | LK_RETRY);
+ if (error == 0 && (vp->v_iflag & VI_DOOMED) != 0)
+ error = NFSERR_SERVERFAULT;
+ }
if (other_lop)
FREE((caddr_t)other_lop, M_NFSDLOCK);
NFSEXITCODE2(error, nd);
@@ -3235,11 +3300,14 @@ nfsrv_openupdate(vnode_t vp, struct nfsstate *new_stp, nfsquad_t clientid,
/* Get the lf lock */
nfsrv_locklf(lfp);
NFSUNLOCKSTATE();
+ ASSERT_VOP_ELOCKED(vp, "nfsrv_openupdate");
+ NFSVOPUNLOCK(vp, 0);
if (nfsrv_freeopen(stp, vp, 1, p) == 0) {
NFSLOCKSTATE();
nfsrv_unlocklf(lfp);
NFSUNLOCKSTATE();
}
+ NFSVOPLOCK(vp, LK_EXCLUSIVE | LK_RETRY);
} else {
(void) nfsrv_freeopen(stp, NULL, 0, p);
NFSUNLOCKSTATE();
@@ -4627,7 +4695,7 @@ static int
nfsrv_clientconflict(struct nfsclient *clp, int *haslockp, vnode_t vp,
NFSPROC_T *p)
{
- int gotlock, lktype;
+ int gotlock, lktype = 0;
/*
* If lease hasn't expired, we can't fix it.
@@ -4637,8 +4705,10 @@ nfsrv_clientconflict(struct nfsclient *clp, int *haslockp, vnode_t vp,
return (0);
if (*haslockp == 0) {
NFSUNLOCKSTATE();
- lktype = NFSVOPISLOCKED(vp);
- NFSVOPUNLOCK(vp, 0);
+ if (vp != NULL) {
+ lktype = NFSVOPISLOCKED(vp);
+ NFSVOPUNLOCK(vp, 0);
+ }
NFSLOCKV4ROOTMUTEX();
nfsv4_relref(&nfsv4rootfs_lock);
do {
@@ -4647,11 +4717,12 @@ nfsrv_clientconflict(struct nfsclient *clp, int *haslockp, vnode_t vp,
} while (!gotlock);
NFSUNLOCKV4ROOTMUTEX();
*haslockp = 1;
- NFSVOPLOCK(vp, lktype | LK_RETRY);
- if ((vp->v_iflag & VI_DOOMED) != 0)
- return (2);
- else
- return (1);
+ if (vp != NULL) {
+ NFSVOPLOCK(vp, lktype | LK_RETRY);
+ if ((vp->v_iflag & VI_DOOMED) != 0)
+ return (2);
+ }
+ return (1);
}
NFSUNLOCKSTATE();
@@ -4692,7 +4763,7 @@ nfsrv_delegconflict(struct nfsstate *stp, int *haslockp, NFSPROC_T *p,
vnode_t vp)
{
struct nfsclient *clp = stp->ls_clp;
- int gotlock, error, lktype, retrycnt, zapped_clp;
+ int gotlock, error, lktype = 0, retrycnt, zapped_clp;
nfsv4stateid_t tstateid;
fhandle_t tfh;
@@ -4809,8 +4880,10 @@ nfsrv_delegconflict(struct nfsstate *stp, int *haslockp, NFSPROC_T *p,
*/
if (*haslockp == 0) {
NFSUNLOCKSTATE();
- lktype = NFSVOPISLOCKED(vp);
- NFSVOPUNLOCK(vp, 0);
+ if (vp != NULL) {
+ lktype = NFSVOPISLOCKED(vp);
+ NFSVOPUNLOCK(vp, 0);
+ }
NFSLOCKV4ROOTMUTEX();
nfsv4_relref(&nfsv4rootfs_lock);
do {
@@ -4819,14 +4892,16 @@ nfsrv_delegconflict(struct nfsstate *stp, int *haslockp, NFSPROC_T *p,
} while (!gotlock);
NFSUNLOCKV4ROOTMUTEX();
*haslockp = 1;
- NFSVOPLOCK(vp, lktype | LK_RETRY);
- if ((vp->v_iflag & VI_DOOMED) != 0) {
- *haslockp = 0;
- NFSLOCKV4ROOTMUTEX();
- nfsv4_unlock(&nfsv4rootfs_lock, 1);
- NFSUNLOCKV4ROOTMUTEX();
- error = NFSERR_PERM;
- goto out;
+ if (vp != NULL) {
+ NFSVOPLOCK(vp, lktype | LK_RETRY);
+ if ((vp->v_iflag & VI_DOOMED) != 0) {
+ *haslockp = 0;
+ NFSLOCKV4ROOTMUTEX();
+ nfsv4_unlock(&nfsv4rootfs_lock, 1);
+ NFSUNLOCKV4ROOTMUTEX();
+ error = NFSERR_PERM;
+ goto out;
+ }
}
error = -1;
goto out;
OpenPOWER on IntegriCloud