Make nfs_timer() MPSAFE. With this change, the bottom half of the NFS

client (the interface with the protocol stack and callouts) is
Giant-free.

Submitted by:	Mohan Srinivasan.
This commit is contained in:
Paul Saab 2005-07-19 21:27:25 +00:00
parent 2c9b913248
commit 4321eae6b7
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=148162
3 changed files with 23 additions and 12 deletions

View file

@ -194,6 +194,7 @@ extern TAILQ_HEAD(nfs_reqq, nfsreq) nfs_reqq;
#define R_TPRINTFMSG 0x20 /* Did a tprintf msg. */
#define R_MUSTRESEND 0x40 /* Must resend request */
#define R_GETONEREP 0x80 /* Probe for one reply only */
#define R_REXMIT_INPROG 0x100 /* Re-transmit in progress */
/*
* Pointers to ops that differ from v3 to v4

View file

@ -1032,6 +1032,17 @@ nfs_request(struct vnode *vp, struct mbuf *mrest, int procnum,
*/
s = splsoftclock();
mtx_lock(&nfs_reqq_mtx);
/*
* nfs_timer() may be in the process of re-transmitting this request.
* nfs_timer() drops the nfs_reqq_mtx before the pru_send() (to avoid LORs).
* Wait till nfs_timer() completes the re-transmission. When the reply
* comes back, it will be discarded (since the req struct for it no longer
* exists).
*/
while (rep->r_flags & R_REXMIT_INPROG) {
msleep((caddr_t)&rep->r_flags, &nfs_reqq_mtx,
(PZERO - 1), "nfsrxmt", 0);
}
TAILQ_REMOVE(&nfs_reqq, rep, r_chain);
if (TAILQ_EMPTY(&nfs_reqq))
callout_stop(&nfs_callout);
@ -1152,19 +1163,11 @@ nfs_request(struct vnode *vp, struct mbuf *mrest, int procnum,
* To avoid retransmission attempts on STREAM sockets (in the future) make
* sure to set the r_retry field to 0 (implies nm_retry == 0).
*
* XXX -
* For now, since we don't register MPSAFE callouts for the NFS client -
* softclock() acquires Giant before calling us. That prevents req entries
* from being removed from the list (from nfs_request()). But we still
* acquire the nfs reqq mutex to make sure the state of individual req
* entries is not modified from RPC reply handling (from socket callback)
* while nfs_timer is walking the list of reqs.
* The nfs reqq lock cannot be held while we do the pru_send() because of a
* lock ordering violation. The NFS client socket callback acquires
* inp_lock->nfsreq mutex and pru_send acquires inp_lock. So we drop the
* reqq mutex (and reacquire it after the pru_send()). This won't work
* when we move to fine grained locking for NFS. When we get to that point,
* a rewrite of nfs_timer() will be needed.
* reqq mutex (and reacquire it after the pru_send()). The req structure
* (for the rexmit) is prevented from being removed by the R_REXMIT_INPROG flag.
*/
void
nfs_timer(void *arg)
@ -1245,7 +1248,12 @@ nfs_timer(void *arg)
((nmp->nm_flag & NFSMNT_DUMBTIMR) ||
(rep->r_flags & R_SENT) ||
nmp->nm_sent < nmp->nm_cwnd) &&
(m = m_copym(rep->r_mreq, 0, M_COPYALL, M_DONTWAIT))){
(m = m_copym(rep->r_mreq, 0, M_COPYALL, M_DONTWAIT))) {
/*
* Mark the request to indicate that a XMIT is in progress
* to prevent the req structure being removed in nfs_request().
*/
rep->r_flags |= R_REXMIT_INPROG;
mtx_unlock(&nfs_reqq_mtx);
if ((nmp->nm_flag & NFSMNT_NOCONN) == 0)
error = (*so->so_proto->pr_usrreqs->pru_send)
@ -1254,6 +1262,8 @@ nfs_timer(void *arg)
error = (*so->so_proto->pr_usrreqs->pru_send)
(so, 0, m, nmp->nm_nam, NULL, curthread);
mtx_lock(&nfs_reqq_mtx);
rep->r_flags &= ~R_REXMIT_INPROG;
wakeup((caddr_t)&rep->r_flags);
if (error) {
if (NFSIGNORE_SOERROR(nmp->nm_soflags, error))
so->so_error = 0;

View file

@ -413,7 +413,7 @@ nfs_init(struct vfsconf *vfsp)
* Initialize reply list and start timer
*/
TAILQ_INIT(&nfs_reqq);
callout_init(&nfs_callout, 0);
callout_init(&nfs_callout, CALLOUT_MPSAFE);
mtx_init(&nfs_reqq_mtx, "NFS reqq lock", NULL, MTX_DEF);
mtx_init(&nfs_reply_mtx, "Synch NFS reply posting", NULL, MTX_DEF);