diff --git a/sys/nfsclient/nfs.h b/sys/nfsclient/nfs.h index bc228ed1d5f7..ec0e6acc9be6 100644 --- a/sys/nfsclient/nfs.h +++ b/sys/nfsclient/nfs.h @@ -276,6 +276,7 @@ int nfs_loadattrcache(struct vnode **, struct mbuf **, caddr_t *, struct vattr *, int); int nfsm_mbuftouio(struct mbuf **, struct uio *, int, caddr_t *); void nfs_nhinit(void); +int nfs_nmcancelreqs(struct nfsmount *); void nfs_timer(void*); int nfs_connect(struct nfsmount *, struct nfsreq *); void nfs_disconnect(struct nfsmount *); diff --git a/sys/nfsclient/nfs_socket.c b/sys/nfsclient/nfs_socket.c index b34f739d28c2..858b14e396e2 100644 --- a/sys/nfsclient/nfs_socket.c +++ b/sys/nfsclient/nfs_socket.c @@ -250,13 +250,8 @@ nfs_connect(struct nfsmount *nmp, struct nfsreq *rep) } splx(s); } - if (nmp->nm_flag & (NFSMNT_SOFT | NFSMNT_INT)) { - so->so_rcv.sb_timeo = (5 * hz); - so->so_snd.sb_timeo = (5 * hz); - } else { - so->so_rcv.sb_timeo = 0; - so->so_snd.sb_timeo = 0; - } + so->so_rcv.sb_timeo = 5 * hz; + so->so_snd.sb_timeo = 5 * hz; /* * Get buffer reservation size from sysctl, but impose reasonable @@ -855,6 +850,11 @@ nfs_request(struct vnode *vp, struct mbuf *mrest, int procnum, int trylater_delay = NQ_TRYLATERDEL, trylater_cnt = 0; u_int32_t xid; + /* Reject requests while attempting to unmount. */ + if (vp->v_mount->mnt_kern_flag & MNTK_UNMOUNT) { + m_freem(mrest); + return (ESTALE); + } nmp = VFSTONFS(vp->v_mount); MALLOC(rep, struct nfsreq *, sizeof(struct nfsreq), M_NFSREQ, M_WAITOK); rep->r_nmp = nmp; @@ -1166,6 +1166,41 @@ nfs_timer(void *arg) nfs_timer_handle = timeout(nfs_timer, (void *)0, nfs_ticks); } +/* + * Mark all of an nfs mount's outstanding requests with R_SOFTTERM and + * wait for all requests to complete. This is used by forced unmounts + * to terminate any outstanding RPCs. + */ +int +nfs_nmcancelreqs(nmp) + struct nfsmount *nmp; +{ + struct nfsreq *req; + int i, s; + + s = splnet(); + TAILQ_FOREACH(req, &nfs_reqq, r_chain) { + if (nmp != req->r_nmp || req->r_mrep != NULL || + (req->r_flags & R_SOFTTERM)) + continue; + nfs_softterm(req); + } + splx(s); + + for (i = 0; i < 30; i++) { + tsleep(&lbolt, PSOCK, "nfscancel", 0); + s = splnet(); + TAILQ_FOREACH(req, &nfs_reqq, r_chain) { + if (nmp == req->r_nmp) + break; + } + splx(s); + if (req == NULL) + return (0); + } + return (EBUSY); +} + /* * Flag a request as being about to terminate (due to NFSMNT_INT/NFSMNT_SOFT). * The nm_send count is decremented now to avoid deadlocks when the process in @@ -1289,6 +1324,9 @@ nfs_rcvlock(struct nfsreq *rep) slptimeo = 2 * hz; } } + /* Always fail if our request has been cancelled. */ + if (rep != NULL && (rep->r_flags & R_SOFTTERM)) + return (EINTR); *statep |= NFSSTA_RCVLOCK; return (0); } diff --git a/sys/nfsclient/nfs_vfsops.c b/sys/nfsclient/nfs_vfsops.c index 1cd6275b8f47..62d412eb06b6 100644 --- a/sys/nfsclient/nfs_vfsops.c +++ b/sys/nfsclient/nfs_vfsops.c @@ -918,6 +918,12 @@ nfs_unmount(struct mount *mp, int mntflags, struct thread *td) * - Close the socket * - Free up the data structures */ + /* In the forced case, cancel any outstanding requests. */ + if (flags & FORCECLOSE) { + error = nfs_nmcancelreqs(nmp); + if (error) + return (error); + } /* We hold 1 extra ref on the root vnode; see comment in mountnfs(). */ error = vflush(mp, 1, flags); if (error)