summaryrefslogtreecommitdiffstats
path: root/sys/fs
diff options
context:
space:
mode:
Diffstat (limited to 'sys/fs')
-rw-r--r--sys/fs/nfs/nfs_commonsubs.c19
-rw-r--r--sys/fs/nfs/nfs_var.h4
-rw-r--r--sys/fs/nfsclient/nfs_clcomsubs.c2
-rw-r--r--sys/fs/nfsclient/nfs_clstate.c94
-rw-r--r--sys/fs/nfsserver/nfs_nfsdsocket.c6
-rw-r--r--sys/fs/nfsserver/nfs_nfsdstate.c18
6 files changed, 109 insertions, 34 deletions
diff --git a/sys/fs/nfs/nfs_commonsubs.c b/sys/fs/nfs/nfs_commonsubs.c
index e725889..03b5786 100644
--- a/sys/fs/nfs/nfs_commonsubs.c
+++ b/sys/fs/nfs/nfs_commonsubs.c
@@ -1726,11 +1726,13 @@ nfsmout:
* Any usecnt must be decremented by calling nfsv4_relref() before
* calling nfsv4_lock(). It was done this way, so nfsv4_lock() could
* be called in a loop.
- * The last argument is set to indicate if the call slept, iff not NULL.
+ * The isleptp argument is set to indicate if the call slept, iff not NULL
+ * and the mp argument indicates to check for a forced dismount, iff not
+ * NULL.
*/
APPLESTATIC int
nfsv4_lock(struct nfsv4lock *lp, int iwantlock, int *isleptp,
- void *mutex)
+ void *mutex, struct mount *mp)
{
if (isleptp)
@@ -1751,6 +1753,10 @@ nfsv4_lock(struct nfsv4lock *lp, int iwantlock, int *isleptp,
lp->nfslock_lock |= NFSV4LOCK_LOCKWANTED;
}
while (lp->nfslock_lock & (NFSV4LOCK_LOCK | NFSV4LOCK_LOCKWANTED)) {
+ if (mp != NULL && (mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0) {
+ lp->nfslock_lock &= ~NFSV4LOCK_LOCKWANTED;
+ return (0);
+ }
lp->nfslock_lock |= NFSV4LOCK_WANTED;
if (isleptp)
*isleptp = 1;
@@ -1801,9 +1807,12 @@ nfsv4_relref(struct nfsv4lock *lp)
* not wait for threads that want the exclusive lock. If priority needs
* to be given to threads that need the exclusive lock, a call to nfsv4_lock()
* with the 2nd argument == 0 should be done before calling nfsv4_getref().
+ * If the mp argument is not NULL, check for MNTK_UNMOUNTF being set and
+ * return without getting a refcnt for that case.
*/
APPLESTATIC void
-nfsv4_getref(struct nfsv4lock *lp, int *isleptp, void *mutex)
+nfsv4_getref(struct nfsv4lock *lp, int *isleptp, void *mutex,
+ struct mount *mp)
{
if (isleptp)
@@ -1813,12 +1822,16 @@ nfsv4_getref(struct nfsv4lock *lp, int *isleptp, void *mutex)
* Wait for a lock held.
*/
while (lp->nfslock_lock & NFSV4LOCK_LOCK) {
+ if (mp != NULL && (mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0)
+ return;
lp->nfslock_lock |= NFSV4LOCK_WANTED;
if (isleptp)
*isleptp = 1;
(void) nfsmsleep(&lp->nfslock_lock, mutex,
PZERO - 1, "nfsv4lck", NULL);
}
+ if (mp != NULL && (mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0)
+ return;
lp->nfslock_usecnt++;
}
diff --git a/sys/fs/nfs/nfs_var.h b/sys/fs/nfs/nfs_var.h
index d83d523..8ed60a7 100644
--- a/sys/fs/nfs/nfs_var.h
+++ b/sys/fs/nfs/nfs_var.h
@@ -247,10 +247,10 @@ int nfsv4_loadattr(struct nfsrv_descript *, vnode_t,
struct nfsv3_pathconf *, struct statfs *, struct nfsstatfs *,
struct nfsfsinfo *, NFSACL_T *,
int, int *, u_int32_t *, u_int32_t *, NFSPROC_T *, struct ucred *);
-int nfsv4_lock(struct nfsv4lock *, int, int *, void *);
+int nfsv4_lock(struct nfsv4lock *, int, int *, void *, struct mount *);
void nfsv4_unlock(struct nfsv4lock *, int);
void nfsv4_relref(struct nfsv4lock *);
-void nfsv4_getref(struct nfsv4lock *, int *, void *);
+void nfsv4_getref(struct nfsv4lock *, int *, void *, struct mount *);
int nfsv4_getref_nonblock(struct nfsv4lock *);
int nfsv4_testlock(struct nfsv4lock *);
int nfsrv_mtostr(struct nfsrv_descript *, char *, int);
diff --git a/sys/fs/nfsclient/nfs_clcomsubs.c b/sys/fs/nfsclient/nfs_clcomsubs.c
index 6866a73..c7fd69b 100644
--- a/sys/fs/nfsclient/nfs_clcomsubs.c
+++ b/sys/fs/nfsclient/nfs_clcomsubs.c
@@ -482,7 +482,7 @@ nfscl_lockexcl(struct nfsv4lock *lckp, void *mutex)
int igotlock;
do {
- igotlock = nfsv4_lock(lckp, 1, NULL, mutex);
+ igotlock = nfsv4_lock(lckp, 1, NULL, mutex, NULL);
} while (!igotlock);
}
diff --git a/sys/fs/nfsclient/nfs_clstate.c b/sys/fs/nfsclient/nfs_clstate.c
index 8e9aa6a..86d71b6 100644
--- a/sys/fs/nfsclient/nfs_clstate.c
+++ b/sys/fs/nfsclient/nfs_clstate.c
@@ -687,11 +687,14 @@ nfscl_getcl(vnode_t vp, struct ucred *cred, NFSPROC_T *p,
struct nfsclclient *clp;
struct nfsclclient *newclp = NULL;
struct nfscllockowner *lp, *nlp;
- struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
+ struct mount *mp;
+ struct nfsmount *nmp;
char uuid[HOSTUUIDLEN];
int igotlock = 0, error, trystalecnt, clidinusedelay, i;
u_int16_t idlen = 0;
+ mp = vnode_mount(vp);
+ nmp = VFSTONFS(mp);
if (cred != NULL) {
getcredhostuuid(cred, uuid, sizeof uuid);
idlen = strlen(uuid);
@@ -704,6 +707,17 @@ nfscl_getcl(vnode_t vp, struct ucred *cred, NFSPROC_T *p,
M_WAITOK);
}
NFSLOCKCLSTATE();
+ /*
+ * If a forced dismount is already in progress, don't
+ * allocate a new clientid and get out now. For the case where
+ * clp != NULL, this is a harmless optimization.
+ */
+ if ((mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0) {
+ NFSUNLOCKCLSTATE();
+ if (newclp != NULL)
+ free(newclp, M_NFSCLCLIENT);
+ return (EBADF);
+ }
clp = nmp->nm_clp;
if (clp == NULL) {
if (newclp == NULL) {
@@ -736,9 +750,21 @@ nfscl_getcl(vnode_t vp, struct ucred *cred, NFSPROC_T *p,
NFSLOCKCLSTATE();
while ((clp->nfsc_flags & NFSCLFLAGS_HASCLIENTID) == 0 && !igotlock)
igotlock = nfsv4_lock(&clp->nfsc_lock, 1, NULL,
- NFSCLSTATEMUTEXPTR);
+ NFSCLSTATEMUTEXPTR, mp);
if (!igotlock)
- nfsv4_getref(&clp->nfsc_lock, NULL, NFSCLSTATEMUTEXPTR);
+ nfsv4_getref(&clp->nfsc_lock, NULL, NFSCLSTATEMUTEXPTR, mp);
+ if (igotlock == 0 && (mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0) {
+ /*
+ * Both nfsv4_lock() and nfsv4_getref() know to check
+ * for MNTK_UNMOUNTF and return without sleeping to
+ * wait for the exclusive lock to be released, since it
+ * might be held by nfscl_umount() and we need to get out
+ * now for that case and not wait until nfscl_umount()
+ * releases it.
+ */
+ NFSUNLOCKCLSTATE();
+ return (EBADF);
+ }
NFSUNLOCKCLSTATE();
/*
@@ -1713,6 +1739,7 @@ nfscl_cleanupkext(struct nfsclclient *clp)
}
#endif /* APPLEKEXT || __FreeBSD__ */
+static int fake_global; /* Used to force visibility of MNTK_UNMOUNTF */
/*
* Called from nfs umount to free up the clientid.
*/
@@ -1723,6 +1750,33 @@ nfscl_umount(struct nfsmount *nmp, NFSPROC_T *p)
struct ucred *cred;
int igotlock;
+ /*
+ * For the case that matters, this is the thread that set
+ * MNTK_UNMOUNTF, so it will see it set. The code that follows is
+ * done to ensure that any thread executing nfscl_getcl() after
+ * this time, will see MNTK_UNMOUNTF set. nfscl_getcl() uses the
+ * mutex for NFSLOCKCLSTATE(), so it is "m" for the following
+ * explanation, courtesy of Alan Cox.
+ * What follows is a snippet from Alan Cox's email at:
+ * http://docs.FreeBSD.org/cgi/
+ * mid.cgi?BANLkTikR3d65zPHo9==08ZfJ2vmqZucEvw
+ *
+ * 1. Set MNTK_UNMOUNTF
+ * 2. Acquire a standard FreeBSD mutex "m".
+ * 3. Update some data structures.
+ * 4. Release mutex "m".
+ *
+ * Then, other threads that acquire "m" after step 4 has occurred will
+ * see MNTK_UNMOUNTF as set. But, other threads that beat thread X to
+ * step 2 may or may not see MNTK_UNMOUNTF as set.
+ */
+ NFSLOCKCLSTATE();
+ if ((nmp->nm_mountp->mnt_kern_flag & MNTK_UNMOUNTF) != 0) {
+ fake_global++;
+ NFSUNLOCKCLSTATE();
+ NFSLOCKCLSTATE();
+ }
+
clp = nmp->nm_clp;
if (clp != NULL) {
if ((clp->nfsc_flags & NFSCLFLAGS_INITED) == 0)
@@ -1734,12 +1788,16 @@ nfscl_umount(struct nfsmount *nmp, NFSPROC_T *p)
*/
clp->nfsc_flags |= NFSCLFLAGS_UMOUNT;
while (clp->nfsc_flags & NFSCLFLAGS_HASTHREAD)
- (void) tsleep((caddr_t)clp, PWAIT, "nfsclumnt", hz);
+ (void)mtx_sleep(clp, NFSCLSTATEMUTEXPTR, PWAIT,
+ "nfsclumnt", hz);
- NFSLOCKCLSTATE();
+ /*
+ * Now, get the exclusive lock on the client state, so
+ * that no uses of the state are still in progress.
+ */
do {
igotlock = nfsv4_lock(&clp->nfsc_lock, 1, NULL,
- NFSCLSTATEMUTEXPTR);
+ NFSCLSTATEMUTEXPTR, NULL);
} while (!igotlock);
NFSUNLOCKCLSTATE();
@@ -1756,8 +1814,8 @@ nfscl_umount(struct nfsmount *nmp, NFSPROC_T *p)
nmp->nm_clp = NULL;
NFSFREECRED(cred);
FREE((caddr_t)clp, M_NFSCLCLIENT);
- }
-
+ } else
+ NFSUNLOCKCLSTATE();
}
/*
@@ -1790,7 +1848,7 @@ nfscl_recover(struct nfsclclient *clp, struct ucred *cred, NFSPROC_T *p)
clp->nfsc_flags |= NFSCLFLAGS_RECVRINPROG;
do {
igotlock = nfsv4_lock(&clp->nfsc_lock, 1, NULL,
- NFSCLSTATEMUTEXPTR);
+ NFSCLSTATEMUTEXPTR, NULL);
} while (!igotlock);
NFSUNLOCKCLSTATE();
@@ -2105,7 +2163,7 @@ nfscl_hasexpired(struct nfsclclient *clp, u_int32_t clidrev, NFSPROC_T *p)
clp->nfsc_flags |= NFSCLFLAGS_EXPIREIT;
do {
igotlock = nfsv4_lock(&clp->nfsc_lock, 1, NULL,
- NFSCLSTATEMUTEXPTR);
+ NFSCLSTATEMUTEXPTR, NULL);
} while (!igotlock && (clp->nfsc_flags & NFSCLFLAGS_EXPIREIT));
if ((clp->nfsc_flags & NFSCLFLAGS_EXPIREIT) == 0) {
if (igotlock)
@@ -2464,7 +2522,7 @@ tryagain:
}
while (!igotlock) {
igotlock = nfsv4_lock(&clp->nfsc_lock, 1,
- &islept, NFSCLSTATEMUTEXPTR);
+ &islept, NFSCLSTATEMUTEXPTR, NULL);
if (islept)
goto tryagain;
}
@@ -2556,14 +2614,18 @@ tryagain:
}
#endif /* APPLEKEXT || __FreeBSD__ */
+ NFSLOCKCLSTATE();
if ((clp->nfsc_flags & NFSCLFLAGS_RECOVER) == 0)
- (void) tsleep((caddr_t)clp, PWAIT, "nfscl", hz);
+ (void)mtx_sleep(clp, NFSCLSTATEMUTEXPTR, PWAIT, "nfscl",
+ hz);
if (clp->nfsc_flags & NFSCLFLAGS_UMOUNT) {
- NFSFREECRED(cred);
clp->nfsc_flags &= ~NFSCLFLAGS_HASTHREAD;
+ NFSUNLOCKCLSTATE();
+ NFSFREECRED(cred);
wakeup((caddr_t)clp);
return;
}
+ NFSUNLOCKCLSTATE();
}
}
@@ -3864,7 +3926,7 @@ nfscl_removedeleg(vnode_t vp, NFSPROC_T *p, nfsv4stateid_t *stp)
islept = 0;
while (!igotlock) {
igotlock = nfsv4_lock(&clp->nfsc_lock, 1,
- &islept, NFSCLSTATEMUTEXPTR);
+ &islept, NFSCLSTATEMUTEXPTR, NULL);
if (islept)
break;
}
@@ -3963,7 +4025,7 @@ nfscl_renamedeleg(vnode_t fvp, nfsv4stateid_t *fstp, int *gotfdp, vnode_t tvp,
islept = 0;
while (!igotlock) {
igotlock = nfsv4_lock(&clp->nfsc_lock, 1,
- &islept, NFSCLSTATEMUTEXPTR);
+ &islept, NFSCLSTATEMUTEXPTR, NULL);
if (islept)
break;
}
@@ -4043,7 +4105,7 @@ nfscl_getref(struct nfsmount *nmp)
NFSUNLOCKCLSTATE();
return (0);
}
- nfsv4_getref(&clp->nfsc_lock, NULL, NFSCLSTATEMUTEXPTR);
+ nfsv4_getref(&clp->nfsc_lock, NULL, NFSCLSTATEMUTEXPTR, NULL);
NFSUNLOCKCLSTATE();
return (1);
}
diff --git a/sys/fs/nfsserver/nfs_nfsdsocket.c b/sys/fs/nfsserver/nfs_nfsdsocket.c
index eeecded..7007275 100644
--- a/sys/fs/nfsserver/nfs_nfsdsocket.c
+++ b/sys/fs/nfsserver/nfs_nfsdsocket.c
@@ -525,10 +525,10 @@ nfsrvd_compound(struct nfsrv_descript *nd, int isdgram,
NFSLOCKV4ROOTMUTEX();
if (nfsrv_stablefirst.nsf_flags & NFSNSF_NEEDLOCK)
igotlock = nfsv4_lock(&nfsv4rootfs_lock, 1, NULL,
- NFSV4ROOTLOCKMUTEXPTR);
+ NFSV4ROOTLOCKMUTEXPTR, NULL);
else
igotlock = nfsv4_lock(&nfsv4rootfs_lock, 0, NULL,
- NFSV4ROOTLOCKMUTEXPTR);
+ NFSV4ROOTLOCKMUTEXPTR, NULL);
NFSUNLOCKV4ROOTMUTEX();
if (igotlock) {
/*
@@ -576,7 +576,7 @@ nfsrvd_compound(struct nfsrv_descript *nd, int isdgram,
*/
NFSLOCKV4ROOTMUTEX();
nfsv4_getref(&nfsv4rootfs_lock, NULL,
- NFSV4ROOTLOCKMUTEXPTR);
+ NFSV4ROOTLOCKMUTEXPTR, NULL);
NFSUNLOCKV4ROOTMUTEX();
}
diff --git a/sys/fs/nfsserver/nfs_nfsdstate.c b/sys/fs/nfsserver/nfs_nfsdstate.c
index ab94f0e..fc84d72 100644
--- a/sys/fs/nfsserver/nfs_nfsdstate.c
+++ b/sys/fs/nfsserver/nfs_nfsdstate.c
@@ -169,7 +169,7 @@ nfsrv_setclient(struct nfsrv_descript *nd, struct nfsclient **new_clpp,
nfsv4_relref(&nfsv4rootfs_lock);
do {
igotlock = nfsv4_lock(&nfsv4rootfs_lock, 1, NULL,
- NFSV4ROOTLOCKMUTEXPTR);
+ NFSV4ROOTLOCKMUTEXPTR, NULL);
} while (!igotlock);
NFSUNLOCKV4ROOTMUTEX();
@@ -419,7 +419,7 @@ nfsrv_getclient(nfsquad_t clientid, int opflags, struct nfsclient **clpp,
nfsv4_relref(&nfsv4rootfs_lock);
do {
igotlock = nfsv4_lock(&nfsv4rootfs_lock, 1, NULL,
- NFSV4ROOTLOCKMUTEXPTR);
+ NFSV4ROOTLOCKMUTEXPTR, NULL);
} while (!igotlock);
NFSUNLOCKV4ROOTMUTEX();
} else if (opflags != CLOPS_RENEW) {
@@ -548,7 +548,7 @@ nfsrv_adminrevoke(struct nfsd_clid *revokep, NFSPROC_T *p)
NFSLOCKV4ROOTMUTEX();
do {
igotlock = nfsv4_lock(&nfsv4rootfs_lock, 1, NULL,
- NFSV4ROOTLOCKMUTEXPTR);
+ NFSV4ROOTLOCKMUTEXPTR, NULL);
} while (!igotlock);
NFSUNLOCKV4ROOTMUTEX();
@@ -608,7 +608,7 @@ nfsrv_dumpclients(struct nfsd_dumpclients *dumpp, int maxcnt)
* exclusive lock cannot be acquired while dumping the clients.
*/
NFSLOCKV4ROOTMUTEX();
- nfsv4_getref(&nfsv4rootfs_lock, NULL, NFSV4ROOTLOCKMUTEXPTR);
+ nfsv4_getref(&nfsv4rootfs_lock, NULL, NFSV4ROOTLOCKMUTEXPTR, NULL);
NFSUNLOCKV4ROOTMUTEX();
NFSLOCKSTATE();
/*
@@ -709,7 +709,7 @@ nfsrv_dumplocks(vnode_t vp, struct nfsd_dumplocks *ldumpp, int maxcnt,
* exclusive lock on it cannot be acquired while dumping the locks.
*/
NFSLOCKV4ROOTMUTEX();
- nfsv4_getref(&nfsv4rootfs_lock, NULL, NFSV4ROOTLOCKMUTEXPTR);
+ nfsv4_getref(&nfsv4rootfs_lock, NULL, NFSV4ROOTLOCKMUTEXPTR, NULL);
NFSUNLOCKV4ROOTMUTEX();
NFSLOCKSTATE();
if (!ret)
@@ -4254,7 +4254,7 @@ nfsrv_clientconflict(struct nfsclient *clp, int *haslockp, vnode_t vp,
nfsv4_relref(&nfsv4rootfs_lock);
do {
gotlock = nfsv4_lock(&nfsv4rootfs_lock, 1, NULL,
- NFSV4ROOTLOCKMUTEXPTR);
+ NFSV4ROOTLOCKMUTEXPTR, NULL);
} while (!gotlock);
NFSUNLOCKV4ROOTMUTEX();
*haslockp = 1;
@@ -4422,7 +4422,7 @@ nfsrv_delegconflict(struct nfsstate *stp, int *haslockp, NFSPROC_T *p,
nfsv4_relref(&nfsv4rootfs_lock);
do {
gotlock = nfsv4_lock(&nfsv4rootfs_lock, 1, NULL,
- NFSV4ROOTLOCKMUTEXPTR);
+ NFSV4ROOTLOCKMUTEXPTR, NULL);
} while (!gotlock);
NFSUNLOCKV4ROOTMUTEX();
*haslockp = 1;
@@ -4616,7 +4616,7 @@ nfsd_recalldelegation(vnode_t vp, NFSPROC_T *p)
* exclusive lock cannot be acquired by another thread.
*/
NFSLOCKV4ROOTMUTEX();
- nfsv4_getref(&nfsv4rootfs_lock, NULL, NFSV4ROOTLOCKMUTEXPTR);
+ nfsv4_getref(&nfsv4rootfs_lock, NULL, NFSV4ROOTLOCKMUTEXPTR, NULL);
NFSUNLOCKV4ROOTMUTEX();
/*
@@ -5179,7 +5179,7 @@ nfsrv_locklf(struct nfslockfile *lfp)
lfp->lf_usecount++;
do {
gotlock = nfsv4_lock(&lfp->lf_locallock_lck, 1, NULL,
- NFSSTATEMUTEXPTR);
+ NFSSTATEMUTEXPTR, NULL);
} while (gotlock == 0);
lfp->lf_usecount--;
}
OpenPOWER on IntegriCloud