]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/nfs/nfs_socket.c
xnu-3248.50.21.tar.gz
[apple/xnu.git] / bsd / nfs / nfs_socket.c
index c163bfa04e395f1381d3199e1132d6e29dbfa07b..191a8fa29ce2785c29b5a482d135ca78cbe8cd18 100644 (file)
@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (c) 2000-2011 Apple Inc. All rights reserved.
+ * Copyright (c) 2000-2015 Apple Inc. All rights reserved.
  *
  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
  * 
  *
  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
  * 
@@ -389,14 +389,23 @@ nfs_connect_upcall(socket_t so, void *arg, __unused int waitflag)
                                                        nso->nso_version = RPCBVERS3;
                                        }
                                } else if (nso->nso_protocol == NFS_PROG) {
                                                        nso->nso_version = RPCBVERS3;
                                        }
                                } else if (nso->nso_protocol == NFS_PROG) {
-                                       if ((minvers > NFS_VER4) || (maxvers < NFS_VER2))
-                                               error = EPROGMISMATCH;
-                                       else if ((NFS_VER3 >= minvers) && (NFS_VER3 <= maxvers))
-                                               nso->nso_version = NFS_VER3;
-                                       else if ((NFS_VER2 >= minvers) && (NFS_VER2 <= maxvers))
-                                               nso->nso_version = NFS_VER2;
-                                       else if ((NFS_VER4 >= minvers) && (NFS_VER4 <= maxvers))
-                                               nso->nso_version = NFS_VER4;
+                                       int vers;
+
+                                       /*
+                                        * N.B. Both portmapper and rpcbind V3 are happy to return
+                                        * addresses for other versions than the one you ask (getport or
+                                        * getaddr) and thus we may have fallen to this code path. So if
+                                        * we get a version that we support, use highest supported
+                                        * version.  This assumes that the server supports all versions
+                                        * between minvers and maxvers.  Note for IPv6 we will try and
+                                        * use rpcbind V4 which has getversaddr and we should not get
+                                        * here if that was successful.
+                                        */
+                                       for (vers = nso->nso_nfs_max_vers; vers >= (int)nso->nso_nfs_min_vers; vers--) {
+                                               if (vers >= (int)minvers && vers <= (int)maxvers)
+                                                               break;
+                                       }
+                                       nso->nso_version = (vers < (int)nso->nso_nfs_min_vers) ? 0 : vers;
                                }
                                if (!error && nso->nso_version)
                                        accepted_status = RPC_SUCCESS;
                                }
                                if (!error && nso->nso_version)
                                        accepted_status = RPC_SUCCESS;
@@ -456,7 +465,7 @@ nfsmout:
  */
 int
 nfs_socket_create(
  */
 int
 nfs_socket_create(
-       __unused struct nfsmount *nmp,
+       struct nfsmount *nmp,
        struct sockaddr *sa,
        int sotype,
        in_port_t port,
        struct sockaddr *sa,
        int sotype,
        in_port_t port,
@@ -506,6 +515,8 @@ nfs_socket_create(
                ((struct sockaddr_in6*)nso->nso_saddr)->sin6_port = htons(port);
        nso->nso_protocol = protocol;
        nso->nso_version = vers;
                ((struct sockaddr_in6*)nso->nso_saddr)->sin6_port = htons(port);
        nso->nso_protocol = protocol;
        nso->nso_version = vers;
+       nso->nso_nfs_min_vers = PVER2MAJOR(nmp->nm_min_vers);
+       nso->nso_nfs_max_vers = PVER2MAJOR(nmp->nm_max_vers);
 
        error = sock_socket(sa->sa_family, nso->nso_sotype, 0, NULL, NULL, &nso->nso_so);
 
 
        error = sock_socket(sa->sa_family, nso->nso_sotype, 0, NULL, NULL, &nso->nso_so);
 
@@ -844,7 +855,7 @@ nfs_connect_search_ping(struct nfsmount *nmp, struct nfs_socket *nso, struct tim
                if (nso->nso_protocol == PMAPPROG)
                        vers = (nso->nso_saddr->sa_family == AF_INET) ? PMAPVERS : RPCBVERS4;
                else if (nso->nso_protocol == NFS_PROG)
                if (nso->nso_protocol == PMAPPROG)
                        vers = (nso->nso_saddr->sa_family == AF_INET) ? PMAPVERS : RPCBVERS4;
                else if (nso->nso_protocol == NFS_PROG)
-                       vers = NFS_VER3;
+                       vers = PVER2MAJOR(nmp->nm_max_vers);
        }
        lck_mtx_unlock(&nso->nso_lock);
        error = nfsm_rpchead2(nmp, nso->nso_sotype, nso->nso_protocol, vers, 0, RPCAUTH_SYS,
        }
        lck_mtx_unlock(&nso->nso_lock);
        error = nfsm_rpchead2(nmp, nso->nso_sotype, nso->nso_protocol, vers, 0, RPCAUTH_SYS,
@@ -883,7 +894,7 @@ nfs_connect_search_ping(struct nfsmount *nmp, struct nfs_socket *nso, struct tim
  *                                     Set the nfs socket protocol and version if needed. 
  */
 void
  *                                     Set the nfs socket protocol and version if needed. 
  */
 void
-nfs_connect_search_socket_found(struct nfsmount *nmp __unused, struct nfs_socket_search *nss, struct nfs_socket *nso)
+nfs_connect_search_socket_found(struct nfsmount *nmp, struct nfs_socket_search *nss, struct nfs_socket *nso)
 {
        NFS_SOCK_DBG("nfs connect %s socket %p verified\n",
                      vfs_statfs(nmp->nm_mountp)->f_mntfromname, nso);
 {
        NFS_SOCK_DBG("nfs connect %s socket %p verified\n",
                      vfs_statfs(nmp->nm_mountp)->f_mntfromname, nso);
@@ -892,7 +903,7 @@ nfs_connect_search_socket_found(struct nfsmount *nmp __unused, struct nfs_socket
                if (nso->nso_protocol == PMAPPROG)
                        nso->nso_version = (nso->nso_saddr->sa_family == AF_INET) ? PMAPVERS : RPCBVERS4;
                if (nso->nso_protocol == NFS_PROG)
                if (nso->nso_protocol == PMAPPROG)
                        nso->nso_version = (nso->nso_saddr->sa_family == AF_INET) ? PMAPVERS : RPCBVERS4;
                if (nso->nso_protocol == NFS_PROG)
-                       nso->nso_version = NFS_VER3;
+                       nso->nso_version = PVER2MAJOR(nmp->nm_max_vers);
        }
        TAILQ_REMOVE(&nss->nss_socklist, nso, nso_link);
        nss->nss_sockcnt--;
        }
        TAILQ_REMOVE(&nss->nss_socklist, nso, nso_link);
        nss->nss_sockcnt--;
@@ -1046,6 +1057,7 @@ loop:
  * A mount's initial connection may require negotiating some parameters such
  * as socket type and NFS version.
  */
  * A mount's initial connection may require negotiating some parameters such
  * as socket type and NFS version.
  */
+
 int
 nfs_connect(struct nfsmount *nmp, int verbose, int timeo)
 {
 int
 nfs_connect(struct nfsmount *nmp, int verbose, int timeo)
 {
@@ -1056,6 +1068,7 @@ nfs_connect(struct nfsmount *nmp, int verbose, int timeo)
        sock_upcall upcall;
        struct timeval now, start;
        int error, savederror, nfsvers;
        sock_upcall upcall;
        struct timeval now, start;
        int error, savederror, nfsvers;
+       int tryv4 = 1;
        uint8_t sotype = nmp->nm_sotype ? nmp->nm_sotype : SOCK_STREAM;
        fhandle_t *fh = NULL;
        char *path = NULL;
        uint8_t sotype = nmp->nm_sotype ? nmp->nm_sotype : SOCK_STREAM;
        fhandle_t *fh = NULL;
        char *path = NULL;
@@ -1107,10 +1120,17 @@ tryagain:
                if (!nmp->nm_vers) {
                        /* No NFS version specified... */
                        if (!nmp->nm_nfsport || (!NM_OMATTR_GIVEN(nmp, FH) && !nmp->nm_mountport)) {
                if (!nmp->nm_vers) {
                        /* No NFS version specified... */
                        if (!nmp->nm_nfsport || (!NM_OMATTR_GIVEN(nmp, FH) && !nmp->nm_mountport)) {
-                               /* ...connect to portmapper first if we (may) need any ports. */
-                               nss.nss_port = PMAPPORT;
-                               nss.nss_protocol = PMAPPROG;
-                               nss.nss_version = 0;
+                               if (PVER2MAJOR(nmp->nm_max_vers) >= NFS_VER4 && tryv4) {
+                                       nss.nss_port = NFS_PORT;
+                                       nss.nss_protocol = NFS_PROG;
+                                       nss.nss_version = 4;
+                                       nss.nss_flags |= NSS_FALLBACK2PMAP;
+                               } else {
+                                       /* ...connect to portmapper first if we (may) need any ports. */
+                                       nss.nss_port = PMAPPORT;
+                                       nss.nss_protocol = PMAPPROG;
+                                       nss.nss_version = 0;
+                               }
                        } else {
                                /* ...connect to NFS port first. */
                                nss.nss_port = nmp->nm_nfsport;
                        } else {
                                /* ...connect to NFS port first. */
                                nss.nss_port = nmp->nm_nfsport;
@@ -1118,10 +1138,23 @@ tryagain:
                                nss.nss_version = 0;
                        }
                } else if (nmp->nm_vers >= NFS_VER4) {
                                nss.nss_version = 0;
                        }
                } else if (nmp->nm_vers >= NFS_VER4) {
-                       /* For NFSv4, we use the given (or default) port. */
-                       nss.nss_port = nmp->nm_nfsport ? nmp->nm_nfsport : NFS_PORT;
-                       nss.nss_protocol = NFS_PROG;
-                       nss.nss_version = 4;
+                       if (tryv4) {
+                               /* For NFSv4, we use the given (or default) port. */
+                               nss.nss_port = nmp->nm_nfsport ? nmp->nm_nfsport : NFS_PORT;
+                               nss.nss_protocol = NFS_PROG;
+                               nss.nss_version = 4;
+                               /*
+                                * set NSS_FALLBACK2PMAP here to pick up any non standard port
+                                * if no port is specified on the mount;
+                                * Note nm_vers is set so we will only try NFS_VER4.
+                                */
+                               if (!nmp->nm_nfsport)
+                                       nss.nss_flags |= NSS_FALLBACK2PMAP;
+                       } else {
+                               nss.nss_port = PMAPPORT;
+                               nss.nss_protocol = PMAPPROG;
+                               nss.nss_version = 0;
+                       }
                } else {
                        /* For NFSv3/v2... */
                        if (!nmp->nm_nfsport || (!NM_OMATTR_GIVEN(nmp, FH) && !nmp->nm_mountport)) {
                } else {
                        /* For NFSv3/v2... */
                        if (!nmp->nm_nfsport || (!NM_OMATTR_GIVEN(nmp, FH) && !nmp->nm_mountport)) {
@@ -1176,6 +1209,13 @@ keepsearching:
        if (error || !nss.nss_sock) {
                /* search failed */
                nfs_socket_search_cleanup(&nss);
        if (error || !nss.nss_sock) {
                /* search failed */
                nfs_socket_search_cleanup(&nss);
+               if (nss.nss_flags & NSS_FALLBACK2PMAP) {
+                       tryv4 = 0;
+                       NFS_SOCK_DBG("nfs connect %s TCP failed for V4 %d %d, trying PORTMAP\n",
+                               vfs_statfs(nmp->nm_mountp)->f_mntfromname, error, nss.nss_error);
+                       goto tryagain;
+               }
+
                if (!error && (nss.nss_sotype == SOCK_STREAM) && !nmp->nm_sotype && (nmp->nm_vers < NFS_VER4)) {
                        /* Try using UDP */
                        sotype = SOCK_DGRAM;
                if (!error && (nss.nss_sotype == SOCK_STREAM) && !nmp->nm_sotype && (nmp->nm_vers < NFS_VER4)) {
                        /* Try using UDP */
                        sotype = SOCK_DGRAM;
@@ -1222,30 +1262,21 @@ keepsearching:
                /* Set up socket address and port for NFS socket. */
                bcopy(nso->nso_saddr, &ss, nso->nso_saddr->sa_len);
 
                /* Set up socket address and port for NFS socket. */
                bcopy(nso->nso_saddr, &ss, nso->nso_saddr->sa_len);
 
-               /* If NFS version not set, try NFSv3 then NFSv2. */
-               nfsvers = nmp->nm_vers ? nmp->nm_vers : NFS_VER3;
-
+               /* If NFS version not set, try nm_max_vers down to nm_min_vers */
+               nfsvers = nmp->nm_vers ? nmp->nm_vers : PVER2MAJOR(nmp->nm_max_vers);
                if (!(port = nmp->nm_nfsport)) {
                        if (ss.ss_family == AF_INET)
                                ((struct sockaddr_in*)&ss)->sin_port = htons(0);
                        else if (ss.ss_family == AF_INET6)
                                ((struct sockaddr_in6*)&ss)->sin6_port = htons(0);
                if (!(port = nmp->nm_nfsport)) {
                        if (ss.ss_family == AF_INET)
                                ((struct sockaddr_in*)&ss)->sin_port = htons(0);
                        else if (ss.ss_family == AF_INET6)
                                ((struct sockaddr_in6*)&ss)->sin6_port = htons(0);
-                       error = nfs_portmap_lookup(nmp, vfs_context_current(), (struct sockaddr*)&ss,
-                                       nso->nso_so, NFS_PROG, nfsvers,
-                                       (nso->nso_sotype == SOCK_DGRAM) ? IPPROTO_UDP : IPPROTO_TCP, timeo);
-                       if (!error) {
-                               if (ss.ss_family == AF_INET)
-                                       port = ntohs(((struct sockaddr_in*)&ss)->sin_port);
-                               else if (ss.ss_family == AF_INET6)
-                                       port = ntohs(((struct sockaddr_in6*)&ss)->sin6_port);
-                               if (!port)
-                                       error = EPROGUNAVAIL;
-                       }
-                       if (error && !nmp->nm_vers) {
-                               nfsvers = NFS_VER2;
+                       for (; nfsvers >= (int)PVER2MAJOR(nmp->nm_min_vers); nfsvers--) {
+                               if (nmp->nm_vers && nmp->nm_vers != nfsvers)
+                                       continue; /* Wrong version */
+                               if (nfsvers == NFS_VER4 && nso->nso_sotype == SOCK_DGRAM)
+                                       continue; /* NFSv4 does not do UDP */
                                error = nfs_portmap_lookup(nmp, vfs_context_current(), (struct sockaddr*)&ss,
                                error = nfs_portmap_lookup(nmp, vfs_context_current(), (struct sockaddr*)&ss,
-                                               nso->nso_so, NFS_PROG, nfsvers,
-                                               (nso->nso_sotype == SOCK_DGRAM) ? IPPROTO_UDP : IPPROTO_TCP, timeo);
+                                                          nso->nso_so, NFS_PROG, nfsvers,
+                                                          (nso->nso_sotype == SOCK_DGRAM) ? IPPROTO_UDP : IPPROTO_TCP, timeo);
                                if (!error) {
                                        if (ss.ss_family == AF_INET)
                                                port = ntohs(((struct sockaddr_in*)&ss)->sin_port);
                                if (!error) {
                                        if (ss.ss_family == AF_INET)
                                                port = ntohs(((struct sockaddr_in*)&ss)->sin_port);
@@ -1253,8 +1284,14 @@ keepsearching:
                                                port = ntohs(((struct sockaddr_in6*)&ss)->sin6_port);
                                        if (!port)
                                                error = EPROGUNAVAIL;
                                                port = ntohs(((struct sockaddr_in6*)&ss)->sin6_port);
                                        if (!port)
                                                error = EPROGUNAVAIL;
+                                       if (port == NFS_PORT && nfsvers == NFS_VER4 && tryv4 == 0)
+                                               continue; /* We already tried this */
                                }
                                }
+                               if (!error)
+                                       break;
                        }
                        }
+                       if (nfsvers < (int)PVER2MAJOR(nmp->nm_min_vers) && error == 0)
+                               error = EPROGUNAVAIL;
                        if (error) {
                                nfs_socket_search_update_error(&nss, error);
                                nfs_socket_destroy(nso);
                        if (error) {
                                nfs_socket_search_update_error(&nss, error);
                                nfs_socket_destroy(nso);
@@ -1262,6 +1299,7 @@ keepsearching:
                        }
                }
                /* Create NFS protocol socket and add it to the list of sockets. */
                        }
                }
                /* Create NFS protocol socket and add it to the list of sockets. */
+               /* N.B. If nfsvers is NFS_VER4 at this point then we're on a non standard port */
                error = nfs_socket_create(nmp, (struct sockaddr*)&ss, nso->nso_sotype, port,
                                NFS_PROG, nfsvers, NMFLAG(nmp, RESVPORT), &nsonfs);
                if (error) {
                error = nfs_socket_create(nmp, (struct sockaddr*)&ss, nso->nso_sotype, port,
                                NFS_PROG, nfsvers, NMFLAG(nmp, RESVPORT), &nsonfs);
                if (error) {
@@ -1636,7 +1674,7 @@ nfs_reconnect(struct nfsmount *nmp)
                microuptime(&now);
                if ((lastmsg + nmp->nm_tprintf_delay) < now.tv_sec) {
                        lastmsg = now.tv_sec;
                microuptime(&now);
                if ((lastmsg + nmp->nm_tprintf_delay) < now.tv_sec) {
                        lastmsg = now.tv_sec;
-                       nfs_down(nmp, thd, error, NFSSTA_TIMEO, "can not connect");
+                       nfs_down(nmp, thd, error, NFSSTA_TIMEO, "can not connect", 0);
                        wentdown = 1;
                }
                lck_mtx_lock(&nmp->nm_lock);
                        wentdown = 1;
                }
                lck_mtx_lock(&nmp->nm_lock);
@@ -1647,7 +1685,13 @@ nfs_reconnect(struct nfsmount *nmp)
                        NFS_SOCK_DBG("Not mounted returning %d\n", error);
                        return (error);
                }
                        NFS_SOCK_DBG("Not mounted returning %d\n", error);
                        return (error);
                }
-               nfs_mount_check_dead_timeout(nmp);
+
+               if (nfs_mount_check_dead_timeout(nmp)) {
+                       nfs_mount_make_zombie(nmp);
+                       lck_mtx_unlock(&nmp->nm_lock);
+                       return (ENXIO);
+               }
+               
                if ((error = nfs_sigintr(nmp, NULL, thd, 1))) {
                        lck_mtx_unlock(&nmp->nm_lock);
                        return (error);
                if ((error = nfs_sigintr(nmp, NULL, thd, 1))) {
                        lck_mtx_unlock(&nmp->nm_lock);
                        return (error);
@@ -1674,7 +1718,7 @@ nfs_reconnect(struct nfsmount *nmp)
                                rq->r_flags |= R_MUSTRESEND;
                                rq->r_rtt = -1;
                                wakeup(rq);
                                rq->r_flags |= R_MUSTRESEND;
                                rq->r_rtt = -1;
                                wakeup(rq);
-                               if ((rq->r_flags & (R_ASYNC|R_ASYNCWAIT|R_SENDING)) == R_ASYNC)
+                               if ((rq->r_flags & (R_IOD|R_ASYNC|R_ASYNCWAIT|R_SENDING)) == R_ASYNC)
                                        nfs_asyncio_resend(rq);
                        }
                        lck_mtx_unlock(&rq->r_mtx);
                                        nfs_asyncio_resend(rq);
                        }
                        lck_mtx_unlock(&rq->r_mtx);
@@ -1745,7 +1789,7 @@ nfs_need_reconnect(struct nfsmount *nmp)
                                rq->r_flags |= R_MUSTRESEND;
                                rq->r_rtt = -1;
                                wakeup(rq);
                                rq->r_flags |= R_MUSTRESEND;
                                rq->r_rtt = -1;
                                wakeup(rq);
-                               if ((rq->r_flags & (R_ASYNC|R_ASYNCWAIT|R_SENDING)) == R_ASYNC)
+                               if ((rq->r_flags & (R_IOD|R_ASYNC|R_ASYNCWAIT|R_SENDING)) == R_ASYNC)
                                        nfs_asyncio_resend(rq);
                        }
                        lck_mtx_unlock(&rq->r_mtx);
                                        nfs_asyncio_resend(rq);
                        }
                        lck_mtx_unlock(&rq->r_mtx);
@@ -1771,7 +1815,6 @@ nfs_mount_sock_thread(void *arg, __unused wait_result_t wr)
        int do_reconnect_sleep = 0;
 
        lck_mtx_lock(&nmp->nm_lock);
        int do_reconnect_sleep = 0;
 
        lck_mtx_lock(&nmp->nm_lock);
-
        while (!(nmp->nm_sockflags & NMSOCK_READY) ||
               !TAILQ_EMPTY(&nmp->nm_resendq) ||
               !LIST_EMPTY(&nmp->nm_monlist) ||
        while (!(nmp->nm_sockflags & NMSOCK_READY) ||
               !TAILQ_EMPTY(&nmp->nm_resendq) ||
               !LIST_EMPTY(&nmp->nm_monlist) ||
@@ -1782,7 +1825,7 @@ nfs_mount_sock_thread(void *arg, __unused wait_result_t wr)
                if (nmp->nm_sockflags & NMSOCK_UNMOUNT)
                        break;
                /* do reconnect, if necessary */
                if (nmp->nm_sockflags & NMSOCK_UNMOUNT)
                        break;
                /* do reconnect, if necessary */
-               if (!(nmp->nm_sockflags & NMSOCK_READY) && !(nmp->nm_state & NFSSTA_FORCE)) {
+               if (!(nmp->nm_sockflags & NMSOCK_READY) && !(nmp->nm_state & (NFSSTA_FORCE|NFSSTA_DEAD))) {
                        if (nmp->nm_reconnect_start <= 0) {
                                microuptime(&now);
                                nmp->nm_reconnect_start = now.tv_sec;
                        if (nmp->nm_reconnect_start <= 0) {
                                microuptime(&now);
                                nmp->nm_reconnect_start = now.tv_sec;
@@ -1813,14 +1856,14 @@ nfs_mount_sock_thread(void *arg, __unused wait_result_t wr)
                if ((nmp->nm_sockflags & NMSOCK_READY) &&
                    (nmp->nm_state & NFSSTA_RECOVER) &&
                    !(nmp->nm_sockflags & NMSOCK_UNMOUNT) &&
                if ((nmp->nm_sockflags & NMSOCK_READY) &&
                    (nmp->nm_state & NFSSTA_RECOVER) &&
                    !(nmp->nm_sockflags & NMSOCK_UNMOUNT) &&
-                   !(nmp->nm_state & NFSSTA_FORCE)) {
+                   !(nmp->nm_state & (NFSSTA_FORCE|NFSSTA_DEAD))) {
                        /* perform state recovery */
                        lck_mtx_unlock(&nmp->nm_lock);
                        nfs_recover(nmp);
                        lck_mtx_lock(&nmp->nm_lock);
                }
                /* handle NFSv4 delegation returns */
                        /* perform state recovery */
                        lck_mtx_unlock(&nmp->nm_lock);
                        nfs_recover(nmp);
                        lck_mtx_lock(&nmp->nm_lock);
                }
                /* handle NFSv4 delegation returns */
-               while ((nmp->nm_vers >= NFS_VER4) && !(nmp->nm_state & NFSSTA_FORCE) &&
+               while ((nmp->nm_vers >= NFS_VER4) && !(nmp->nm_state & (NFSSTA_FORCE|NFSSTA_DEAD)) &&
                       (nmp->nm_sockflags & NMSOCK_READY) && !(nmp->nm_state & NFSSTA_RECOVER) &&
                       ((np = TAILQ_FIRST(&nmp->nm_dreturnq)))) {
                        lck_mtx_unlock(&nmp->nm_lock);
                       (nmp->nm_sockflags & NMSOCK_READY) && !(nmp->nm_state & NFSSTA_RECOVER) &&
                       ((np = TAILQ_FIRST(&nmp->nm_dreturnq)))) {
                        lck_mtx_unlock(&nmp->nm_lock);
@@ -1828,11 +1871,12 @@ nfs_mount_sock_thread(void *arg, __unused wait_result_t wr)
                        lck_mtx_lock(&nmp->nm_lock);
                }
                /* do resends, if necessary/possible */
                        lck_mtx_lock(&nmp->nm_lock);
                }
                /* do resends, if necessary/possible */
-               while ((((nmp->nm_sockflags & NMSOCK_READY) && !(nmp->nm_state & NFSSTA_RECOVER)) || (nmp->nm_state & NFSSTA_FORCE)) &&
+               while ((((nmp->nm_sockflags & NMSOCK_READY) && !(nmp->nm_state & NFSSTA_RECOVER)) ||
+                       (nmp->nm_state & (NFSSTA_FORCE|NFSSTA_DEAD))) &&
                       ((req = TAILQ_FIRST(&nmp->nm_resendq)))) {
                        if (req->r_resendtime)
                                microuptime(&now);
                       ((req = TAILQ_FIRST(&nmp->nm_resendq)))) {
                        if (req->r_resendtime)
                                microuptime(&now);
-                       while (req && !(nmp->nm_state & NFSSTA_FORCE) && req->r_resendtime && (now.tv_sec < req->r_resendtime))
+                       while (req && !(nmp->nm_state & (NFSSTA_FORCE|NFSSTA_DEAD)) && req->r_resendtime && (now.tv_sec < req->r_resendtime))
                                req = TAILQ_NEXT(req, r_rchain);
                        if (!req)
                                break;
                                req = TAILQ_NEXT(req, r_rchain);
                        if (!req)
                                break;
@@ -1840,6 +1884,7 @@ nfs_mount_sock_thread(void *arg, __unused wait_result_t wr)
                        req->r_rchain.tqe_next = NFSREQNOLIST;
                        lck_mtx_unlock(&nmp->nm_lock);
                        lck_mtx_lock(&req->r_mtx);
                        req->r_rchain.tqe_next = NFSREQNOLIST;
                        lck_mtx_unlock(&nmp->nm_lock);
                        lck_mtx_lock(&req->r_mtx);
+                       /* Note that we have a reference on the request that was taken nfs_asyncio_resend */
                        if (req->r_error || req->r_nmrep.nmc_mhead) {
                                dofinish = req->r_callback.rcb_func && !(req->r_flags & R_WAITSENT);
                                req->r_flags &= ~R_RESENDQ;
                        if (req->r_error || req->r_nmrep.nmc_mhead) {
                                dofinish = req->r_callback.rcb_func && !(req->r_flags & R_WAITSENT);
                                req->r_flags &= ~R_RESENDQ;
@@ -1847,6 +1892,7 @@ nfs_mount_sock_thread(void *arg, __unused wait_result_t wr)
                                lck_mtx_unlock(&req->r_mtx);
                                if (dofinish)
                                        nfs_asyncio_finish(req);
                                lck_mtx_unlock(&req->r_mtx);
                                if (dofinish)
                                        nfs_asyncio_finish(req);
+                               nfs_request_rele(req);
                                lck_mtx_lock(&nmp->nm_lock);
                                continue;
                        }
                                lck_mtx_lock(&nmp->nm_lock);
                                continue;
                        }
@@ -1865,9 +1911,7 @@ nfs_mount_sock_thread(void *arg, __unused wait_result_t wr)
                                NFS_SOCK_DBG("nfs async%s restart: p %d x 0x%llx f 0x%x rtt %d\n",
                                        nfs_request_using_gss(req) ? " gss" : "", req->r_procnum, req->r_xid,
                                        req->r_flags, req->r_rtt);
                                NFS_SOCK_DBG("nfs async%s restart: p %d x 0x%llx f 0x%x rtt %d\n",
                                        nfs_request_using_gss(req) ? " gss" : "", req->r_procnum, req->r_xid,
                                        req->r_flags, req->r_rtt);
-                               error = !req->r_nmp ? ENXIO : 0;        /* unmounted? */
-                               if (!error)
-                                       error = nfs_sigintr(nmp, req, req->r_thread, 0);
+                               error = nfs_sigintr(nmp, req, req->r_thread, 0);
                                if (!error)
                                        error = nfs_request_add_header(req);
                                if (!error)
                                if (!error)
                                        error = nfs_request_add_header(req);
                                if (!error)
@@ -1882,15 +1926,14 @@ nfs_mount_sock_thread(void *arg, __unused wait_result_t wr)
                                lck_mtx_unlock(&req->r_mtx);
                                if (dofinish)
                                        nfs_asyncio_finish(req);
                                lck_mtx_unlock(&req->r_mtx);
                                if (dofinish)
                                        nfs_asyncio_finish(req);
+                               nfs_request_rele(req);
                                lck_mtx_lock(&nmp->nm_lock);
                                error = 0;
                                continue;
                        }
                        NFS_SOCK_DBG("nfs async resend: p %d x 0x%llx f 0x%x rtt %d\n",
                                req->r_procnum, req->r_xid, req->r_flags, req->r_rtt);
                                lck_mtx_lock(&nmp->nm_lock);
                                error = 0;
                                continue;
                        }
                        NFS_SOCK_DBG("nfs async resend: p %d x 0x%llx f 0x%x rtt %d\n",
                                req->r_procnum, req->r_xid, req->r_flags, req->r_rtt);
-                       error = !req->r_nmp ? ENXIO : 0;        /* unmounted? */
-                       if (!error)
-                               error = nfs_sigintr(nmp, req, req->r_thread, 0);
+                       error = nfs_sigintr(nmp, req, req->r_thread, 0);
                        if (!error) {
                                req->r_flags |= R_SENDING;
                                lck_mtx_unlock(&req->r_mtx);
                        if (!error) {
                                req->r_flags |= R_SENDING;
                                lck_mtx_unlock(&req->r_mtx);
@@ -1901,6 +1944,7 @@ nfs_mount_sock_thread(void *arg, __unused wait_result_t wr)
                                                req->r_flags &= ~R_RESENDQ;
                                        wakeup(req);
                                        lck_mtx_unlock(&req->r_mtx);
                                                req->r_flags &= ~R_RESENDQ;
                                        wakeup(req);
                                        lck_mtx_unlock(&req->r_mtx);
+                                       nfs_request_rele(req);
                                        lck_mtx_lock(&nmp->nm_lock);
                                        continue;
                                }
                                        lck_mtx_lock(&nmp->nm_lock);
                                        continue;
                                }
@@ -1913,17 +1957,22 @@ nfs_mount_sock_thread(void *arg, __unused wait_result_t wr)
                        lck_mtx_unlock(&req->r_mtx);
                        if (dofinish)
                                nfs_asyncio_finish(req);
                        lck_mtx_unlock(&req->r_mtx);
                        if (dofinish)
                                nfs_asyncio_finish(req);
+                       nfs_request_rele(req);
                        lck_mtx_lock(&nmp->nm_lock);
                }
                        lck_mtx_lock(&nmp->nm_lock);
                }
-               if (nmp->nm_deadto_start)
-                       nfs_mount_check_dead_timeout(nmp);
+               if (nfs_mount_check_dead_timeout(nmp)) {
+                       nfs_mount_make_zombie(nmp);
+                       break;
+               }
+               
                if (nmp->nm_state & (NFSSTA_FORCE|NFSSTA_DEAD))
                        break;
                /* check monitored nodes, if necessary/possible */
                if (!LIST_EMPTY(&nmp->nm_monlist)) {
                        nmp->nm_state |= NFSSTA_MONITOR_SCAN;
                        LIST_FOREACH(np, &nmp->nm_monlist, n_monlink) {
                if (nmp->nm_state & (NFSSTA_FORCE|NFSSTA_DEAD))
                        break;
                /* check monitored nodes, if necessary/possible */
                if (!LIST_EMPTY(&nmp->nm_monlist)) {
                        nmp->nm_state |= NFSSTA_MONITOR_SCAN;
                        LIST_FOREACH(np, &nmp->nm_monlist, n_monlink) {
-                               if (!(nmp->nm_sockflags & NMSOCK_READY) || (nmp->nm_state & (NFSSTA_RECOVER|NFSSTA_UNMOUNTING|NFSSTA_FORCE)))
+                               if (!(nmp->nm_sockflags & NMSOCK_READY) ||
+                                   (nmp->nm_state & (NFSSTA_RECOVER|NFSSTA_UNMOUNTING|NFSSTA_FORCE|NFSSTA_DEAD)))
                                        break;
                                np->n_mflag |= NMMONSCANINPROG;
                                lck_mtx_unlock(&nmp->nm_lock);
                                        break;
                                np->n_mflag |= NMMONSCANINPROG;
                                lck_mtx_unlock(&nmp->nm_lock);
@@ -1936,7 +1985,8 @@ nfs_mount_sock_thread(void *arg, __unused wait_result_t wr)
                                        np->n_mflag &= ~NMMONSCANWANT;
                                        wakeup(&np->n_mflag);
                                }
                                        np->n_mflag &= ~NMMONSCANWANT;
                                        wakeup(&np->n_mflag);
                                }
-                               if (error || !(nmp->nm_sockflags & NMSOCK_READY) || (nmp->nm_state & (NFSSTA_RECOVER|NFSSTA_UNMOUNTING|NFSSTA_FORCE)))
+                               if (error || !(nmp->nm_sockflags & NMSOCK_READY) ||
+                                   (nmp->nm_state & (NFSSTA_RECOVER|NFSSTA_UNMOUNTING|NFSSTA_FORCE|NFSSTA_DEAD)))
                                        break;
                        }
                        nmp->nm_state &= ~NFSSTA_MONITOR_SCAN;
                                        break;
                        }
                        nmp->nm_state &= ~NFSSTA_MONITOR_SCAN;
@@ -1985,27 +2035,52 @@ nfs_mount_sock_thread_wake(struct nfsmount *nmp)
  * unresponsive mount has reached the dead timeout.
  * (must be called with nmp locked)
  */
  * unresponsive mount has reached the dead timeout.
  * (must be called with nmp locked)
  */
-void
+int
 nfs_mount_check_dead_timeout(struct nfsmount *nmp)
 {
        struct timeval now;
 
 nfs_mount_check_dead_timeout(struct nfsmount *nmp)
 {
        struct timeval now;
 
-       if (nmp->nm_deadto_start == 0)
-               return;
        if (nmp->nm_state & NFSSTA_DEAD)
        if (nmp->nm_state & NFSSTA_DEAD)
-               return;
+               return 1;
+       if (nmp->nm_deadto_start == 0)
+               return 0;
        nfs_is_squishy(nmp);
        if (nmp->nm_curdeadtimeout <= 0)
        nfs_is_squishy(nmp);
        if (nmp->nm_curdeadtimeout <= 0)
-               return;
+               return 0;
        microuptime(&now);
        if ((now.tv_sec - nmp->nm_deadto_start) < nmp->nm_curdeadtimeout)
        microuptime(&now);
        if ((now.tv_sec - nmp->nm_deadto_start) < nmp->nm_curdeadtimeout)
+               return 0;
+       return 1;
+}
+
+/*
+ * Call nfs_mount_zombie to remove most of the
+ * nfs state for the mount, and then ask to be forcibly unmounted.
+ *
+ * Assumes the nfs mount structure lock nm_lock is held.
+ */
+
+void
+nfs_mount_make_zombie(struct nfsmount *nmp)
+{
+       fsid_t fsid;
+       
+       if (!nmp)
                return;
                return;
+
+       if (nmp->nm_state & NFSSTA_DEAD)
+               return;
+
        printf("nfs server %s: %sdead\n", vfs_statfs(nmp->nm_mountp)->f_mntfromname,
               (nmp->nm_curdeadtimeout != nmp->nm_deadtimeout) ? "squished " : "");
        printf("nfs server %s: %sdead\n", vfs_statfs(nmp->nm_mountp)->f_mntfromname,
               (nmp->nm_curdeadtimeout != nmp->nm_deadtimeout) ? "squished " : "");
-       nmp->nm_state |= NFSSTA_DEAD;
-       vfs_event_signal(&vfs_statfs(nmp->nm_mountp)->f_fsid, VQ_DEAD, 0);
+       fsid = vfs_statfs(nmp->nm_mountp)->f_fsid;
+       lck_mtx_unlock(&nmp->nm_lock);
+       nfs_mount_zombie(nmp, NFSSTA_DEAD);
+       vfs_event_signal(&fsid, VQ_DEAD, 0);
+       lck_mtx_lock(&nmp->nm_lock);
 }
 
 }
 
+
 /*
  * NFS callback channel socket state
  */
 /*
  * NFS callback channel socket state
  */
@@ -2972,11 +3047,11 @@ again:
                lck_mtx_lock(&nmp->nm_lock);
                while (!(nmp->nm_sockflags & NMSOCK_READY)) {
                        /* don't bother waiting if the socket thread won't be reconnecting it */
                lck_mtx_lock(&nmp->nm_lock);
                while (!(nmp->nm_sockflags & NMSOCK_READY)) {
                        /* don't bother waiting if the socket thread won't be reconnecting it */
-                       if (nmp->nm_state & NFSSTA_FORCE) {
+                       if (nmp->nm_state & (NFSSTA_FORCE|NFSSTA_DEAD)) {
                                error = EIO;
                                break;
                        }
                                error = EIO;
                                break;
                        }
-                       if (NMFLAG(nmp, SOFT) && (nmp->nm_reconnect_start > 0)) {
+                       if ((NMFLAG(nmp, SOFT) || (req->r_flags & R_SOFT)) && (nmp->nm_reconnect_start > 0)) {
                                struct timeval now;
                                microuptime(&now);
                                if ((now.tv_sec - nmp->nm_reconnect_start) >= 8) {
                                struct timeval now;
                                microuptime(&now);
                                if ((now.tv_sec - nmp->nm_reconnect_start) >= 8) {
@@ -3094,11 +3169,11 @@ again:
                msg.msg_namelen = sendnam->sa_len;
        }
        error = sock_sendmbuf(nso->nso_so, &msg, mreqcopy, 0, &sentlen);
                msg.msg_namelen = sendnam->sa_len;
        }
        error = sock_sendmbuf(nso->nso_so, &msg, mreqcopy, 0, &sentlen);
-#ifdef NFS_SOCKET_DEBUGGING
-       if (error || (sentlen != req->r_mreqlen))
+       if (error || (sentlen != req->r_mreqlen)) {
                NFS_SOCK_DBG("nfs_send: 0x%llx sent %d/%d error %d\n",
                NFS_SOCK_DBG("nfs_send: 0x%llx sent %d/%d error %d\n",
-                       req->r_xid, (int)sentlen, (int)req->r_mreqlen, error);
-#endif
+                            req->r_xid, (int)sentlen, (int)req->r_mreqlen, error);
+       }
+       
        if (!error && (sentlen != req->r_mreqlen))
                error = EWOULDBLOCK;
        needrecon = ((sotype == SOCK_STREAM) && sentlen && (sentlen != req->r_mreqlen));
        if (!error && (sentlen != req->r_mreqlen))
                error = EWOULDBLOCK;
        needrecon = ((sotype == SOCK_STREAM) && sentlen && (sentlen != req->r_mreqlen));
@@ -3182,6 +3257,9 @@ again:
 
        nfs_sndunlock(req);
 
 
        nfs_sndunlock(req);
 
+       if (nfs_is_dead(error, nmp))
+               error = EIO;
+
        /*
         * Don't log some errors:
         * EPIPE errors may be common with servers that drop idle connections.
        /*
         * Don't log some errors:
         * EPIPE errors may be common with servers that drop idle connections.
@@ -3195,9 +3273,6 @@ again:
                        !req->r_nmp ? "<unmounted>" :
                        vfs_statfs(req->r_nmp->nm_mountp)->f_mntfromname);
 
                        !req->r_nmp ? "<unmounted>" :
                        vfs_statfs(req->r_nmp->nm_mountp)->f_mntfromname);
 
-       if (nfs_is_dead(error, nmp))
-               error = EIO;
-
        /* prefer request termination error over other errors */
        error2 = nfs_sigintr(req->r_nmp, req, req->r_thread, 0);
        if (error2)
        /* prefer request termination error over other errors */
        error2 = nfs_sigintr(req->r_nmp, req, req->r_thread, 0);
        if (error2)
@@ -3265,6 +3340,7 @@ nfs_tcp_rcv(socket_t so, void *arg, __unused int waitflag)
        mbuf_t m;
        int error = 0;
        int recv = 1;
        mbuf_t m;
        int error = 0;
        int recv = 1;
+       int wup = 0;
 
        if (nmp->nm_sockflags & NMSOCK_CONNECTING)
                return;
 
        if (nmp->nm_sockflags & NMSOCK_CONNECTING)
                return;
@@ -3295,19 +3371,16 @@ nfs_tcp_rcv(socket_t so, void *arg, __unused int waitflag)
                        nfs_request_match_reply(nmp, m);
        }
 
                        nfs_request_match_reply(nmp, m);
        }
 
-       lck_mtx_lock(&nmp->nm_lock);
-       if (nmp->nm_nso == nso) {
-               /* still the same socket, so update socket's RPC parsing state */
-               lck_mtx_unlock(&nmp->nm_lock);
-               lck_mtx_lock(&nso->nso_lock);
-               nso->nso_rrs = nrrs;
-               nso->nso_flags &= ~NSO_UPCALL;
-               lck_mtx_unlock(&nso->nso_lock);
-               if (nmp->nm_sockflags & NMSOCK_DISCONNECTING)
-                       wakeup(&nmp->nm_sockflags);
-       } else {
-               lck_mtx_unlock(&nmp->nm_lock);
-       }
+       /* Update the sockets's rpc parsing state */
+       lck_mtx_lock(&nso->nso_lock);
+       nso->nso_rrs = nrrs;
+       if (nso->nso_flags & NSO_DISCONNECTING)
+               wup = 1;
+       nso->nso_flags &= ~NSO_UPCALL;
+       lck_mtx_unlock(&nso->nso_lock);
+       if (wup)
+               wakeup(&nso->nso_flags);
+
 #ifdef NFS_SOCKET_DEBUGGING
        if (!recv && (error != EWOULDBLOCK))
                NFS_SOCK_DBG("nfs_tcp_rcv: got nothing, error %d, got FIN?\n", error);
 #ifdef NFS_SOCKET_DEBUGGING
        if (!recv && (error != EWOULDBLOCK))
                NFS_SOCK_DBG("nfs_tcp_rcv: got nothing, error %d, got FIN?\n", error);
@@ -3335,6 +3408,9 @@ nfs_sock_poke(struct nfsmount *nmp)
        lck_mtx_lock(&nmp->nm_lock);
        if ((nmp->nm_sockflags & NMSOCK_UNMOUNT) ||
            !(nmp->nm_sockflags & NMSOCK_READY) || !nmp->nm_nso || !nmp->nm_nso->nso_so) {
        lck_mtx_lock(&nmp->nm_lock);
        if ((nmp->nm_sockflags & NMSOCK_UNMOUNT) ||
            !(nmp->nm_sockflags & NMSOCK_READY) || !nmp->nm_nso || !nmp->nm_nso->nso_so) {
+               /* Nothing to poke */
+               nmp->nm_sockflags &= ~NMSOCK_POKE;
+               wakeup(&nmp->nm_sockflags);
                lck_mtx_unlock(&nmp->nm_lock);
                return;
        }
                lck_mtx_unlock(&nmp->nm_lock);
                return;
        }
@@ -3347,6 +3423,10 @@ nfs_sock_poke(struct nfsmount *nmp)
        msg.msg_iovlen = 1;
        error = sock_send(nmp->nm_nso->nso_so, &msg, MSG_DONTWAIT, &len);
        NFS_SOCK_DBG("nfs_sock_poke: error %d\n", error);
        msg.msg_iovlen = 1;
        error = sock_send(nmp->nm_nso->nso_so, &msg, MSG_DONTWAIT, &len);
        NFS_SOCK_DBG("nfs_sock_poke: error %d\n", error);
+       lck_mtx_lock(&nmp->nm_lock);
+       nmp->nm_sockflags &= ~NMSOCK_POKE;
+       wakeup(&nmp->nm_sockflags);
+       lck_mtx_unlock(&nmp->nm_lock);
        nfs_is_dead(error, nmp);
 }
 
        nfs_is_dead(error, nmp);
 }
 
@@ -3573,14 +3653,14 @@ nfs_request_create(
                req->r_flags = R_ALLOCATED;
 
        nmp = VFSTONFS(np ? NFSTOMP(np) : mp);
                req->r_flags = R_ALLOCATED;
 
        nmp = VFSTONFS(np ? NFSTOMP(np) : mp);
-       if (!nmp) {
+       if (nfs_mount_gone(nmp)) {
                if (newreq)
                        FREE_ZONE(newreq, sizeof(*newreq), M_NFSREQ);
                return (ENXIO);
        }
        lck_mtx_lock(&nmp->nm_lock);
                if (newreq)
                        FREE_ZONE(newreq, sizeof(*newreq), M_NFSREQ);
                return (ENXIO);
        }
        lck_mtx_lock(&nmp->nm_lock);
-       if ((nmp->nm_state & (NFSSTA_FORCE|NFSSTA_TIMEO)) ==
-           (NFSSTA_FORCE|NFSSTA_TIMEO)) {
+       if ((nmp->nm_state & (NFSSTA_FORCE|NFSSTA_DEAD)) &&
+           (nmp->nm_state & NFSSTA_TIMEO)) {
                lck_mtx_unlock(&nmp->nm_lock);
                mbuf_freem(nmrest->nmc_mhead);
                nmrest->nmc_mhead = NULL;
                lck_mtx_unlock(&nmp->nm_lock);
                mbuf_freem(nmrest->nmc_mhead);
                nmrest->nmc_mhead = NULL;
@@ -3588,7 +3668,7 @@ nfs_request_create(
                        FREE_ZONE(newreq, sizeof(*newreq), M_NFSREQ);
                return (ENXIO);
        }
                        FREE_ZONE(newreq, sizeof(*newreq), M_NFSREQ);
                return (ENXIO);
        }
-
+       
        if ((nmp->nm_vers != NFS_VER4) && (procnum >= 0) && (procnum < NFS_NPROCS))
                OSAddAtomic64(1, &nfsstats.rpccnt[procnum]);
        if ((nmp->nm_vers == NFS_VER4) && (procnum != NFSPROC4_COMPOUND) && (procnum != NFSPROC4_NULL))
        if ((nmp->nm_vers != NFS_VER4) && (procnum >= 0) && (procnum < NFS_NPROCS))
                OSAddAtomic64(1, &nfsstats.rpccnt[procnum]);
        if ((nmp->nm_vers == NFS_VER4) && (procnum != NFSPROC4_COMPOUND) && (procnum != NFSPROC4_NULL))
@@ -3596,6 +3676,7 @@ nfs_request_create(
 
        lck_mtx_init(&req->r_mtx, nfs_request_grp, LCK_ATTR_NULL);
        req->r_nmp = nmp;
 
        lck_mtx_init(&req->r_mtx, nfs_request_grp, LCK_ATTR_NULL);
        req->r_nmp = nmp;
+       nmp->nm_ref++;
        req->r_np = np;
        req->r_thread = thd;
        if (!thd)
        req->r_np = np;
        req->r_thread = thd;
        if (!thd)
@@ -3640,25 +3721,30 @@ nfs_request_create(
 void
 nfs_request_destroy(struct nfsreq *req)
 {
 void
 nfs_request_destroy(struct nfsreq *req)
 {
-       struct nfsmount *nmp = req->r_np ? NFSTONMP(req->r_np) : req->r_nmp;
+       struct nfsmount *nmp;
        struct gss_seq *gsp, *ngsp;
        struct gss_seq *gsp, *ngsp;
-       struct timespec ts = { 1, 0 };
        int clearjbtimeo = 0;
 
        if (!req || !(req->r_flags & R_INITTED))
                return;
        int clearjbtimeo = 0;
 
        if (!req || !(req->r_flags & R_INITTED))
                return;
+       nmp  = req->r_np ? NFSTONMP(req->r_np) : req->r_nmp;
        req->r_flags &= ~R_INITTED;
        if (req->r_lflags & RL_QUEUED)
                nfs_reqdequeue(req);
        req->r_flags &= ~R_INITTED;
        if (req->r_lflags & RL_QUEUED)
                nfs_reqdequeue(req);
+
        if (req->r_achain.tqe_next != NFSREQNOLIST) {
        if (req->r_achain.tqe_next != NFSREQNOLIST) {
-               /* still on an async I/O queue? */
+               /* 
+                * Still on an async I/O queue?
+                * %%% But which one, we may be on a local iod.
+                */
                lck_mtx_lock(nfsiod_mutex);
                lck_mtx_lock(nfsiod_mutex);
-               if (nmp && (req->r_achain.tqe_next != NFSREQNOLIST)) {
+               if (nmp && req->r_achain.tqe_next != NFSREQNOLIST) {
                        TAILQ_REMOVE(&nmp->nm_iodq, req, r_achain);
                        req->r_achain.tqe_next = NFSREQNOLIST;
                }
                lck_mtx_unlock(nfsiod_mutex);
        }
                        TAILQ_REMOVE(&nmp->nm_iodq, req, r_achain);
                        req->r_achain.tqe_next = NFSREQNOLIST;
                }
                lck_mtx_unlock(nfsiod_mutex);
        }
+
        lck_mtx_lock(&req->r_mtx);
        if (nmp) {
                lck_mtx_lock(&nmp->nm_lock);
        lck_mtx_lock(&req->r_mtx);
        if (nmp) {
                lck_mtx_lock(&nmp->nm_lock);
@@ -3674,6 +3760,8 @@ nfs_request_destroy(struct nfsreq *req)
                                wakeup(req2);
                        }
                }
                                wakeup(req2);
                        }
                }
+               assert((req->r_flags & R_RESENDQ) == 0);
+               /* XXX should we just remove this conditional, we should have a reference if we're resending */
                if (req->r_rchain.tqe_next != NFSREQNOLIST) {
                        TAILQ_REMOVE(&nmp->nm_resendq, req, r_rchain);
                        req->r_rchain.tqe_next = NFSREQNOLIST;
                if (req->r_rchain.tqe_next != NFSREQNOLIST) {
                        TAILQ_REMOVE(&nmp->nm_resendq, req, r_rchain);
                        req->r_rchain.tqe_next = NFSREQNOLIST;
@@ -3691,9 +3779,8 @@ nfs_request_destroy(struct nfsreq *req)
                }
                lck_mtx_unlock(&nmp->nm_lock);
        }
                }
                lck_mtx_unlock(&nmp->nm_lock);
        }
-       while (req->r_flags & R_RESENDQ)
-               msleep(req, &req->r_mtx, (PZERO - 1), "nfsresendqwait", &ts);
        lck_mtx_unlock(&req->r_mtx);
        lck_mtx_unlock(&req->r_mtx);
+
        if (clearjbtimeo)
                nfs_up(nmp, req->r_thread, clearjbtimeo, NULL);
        if (req->r_mhead)
        if (clearjbtimeo)
                nfs_up(nmp, req->r_thread, clearjbtimeo, NULL);
        if (req->r_mhead)
@@ -3712,7 +3799,8 @@ nfs_request_destroy(struct nfsreq *req)
                nfs_gss_clnt_ctx_unref(req);
        if (req->r_wrongsec)
                FREE(req->r_wrongsec, M_TEMP);
                nfs_gss_clnt_ctx_unref(req);
        if (req->r_wrongsec)
                FREE(req->r_wrongsec, M_TEMP);
-
+       if (nmp)
+               nfs_mount_rele(nmp);
        lck_mtx_destroy(&req->r_mtx, nfs_request_grp);
        if (req->r_flags & R_ALLOCATED)
                FREE_ZONE(req, sizeof(*req), M_NFSREQ);
        lck_mtx_destroy(&req->r_mtx, nfs_request_grp);
        if (req->r_flags & R_ALLOCATED)
                FREE_ZONE(req, sizeof(*req), M_NFSREQ);
@@ -3764,7 +3852,7 @@ nfs_request_add_header(struct nfsreq *req)
        }
 
        nmp = req->r_np ? NFSTONMP(req->r_np) : req->r_nmp;
        }
 
        nmp = req->r_np ? NFSTONMP(req->r_np) : req->r_nmp;
-       if (!nmp)
+       if (nfs_mount_gone(nmp))
                return (ENXIO);
 
        error = nfsm_rpchead(req, req->r_mrest, &req->r_xid, &req->r_mhead);
                return (ENXIO);
 
        error = nfsm_rpchead(req, req->r_mrest, &req->r_xid, &req->r_mhead);
@@ -3773,10 +3861,10 @@ nfs_request_add_header(struct nfsreq *req)
 
        req->r_mreqlen = mbuf_pkthdr_len(req->r_mhead);
        nmp = req->r_np ? NFSTONMP(req->r_np) : req->r_nmp;
 
        req->r_mreqlen = mbuf_pkthdr_len(req->r_mhead);
        nmp = req->r_np ? NFSTONMP(req->r_np) : req->r_nmp;
-       if (!nmp)
+       if (nfs_mount_gone(nmp))
                return (ENXIO);
        lck_mtx_lock(&nmp->nm_lock);
                return (ENXIO);
        lck_mtx_lock(&nmp->nm_lock);
-       if (NMFLAG(nmp, SOFT))
+       if (NMFLAG(nmp, SOFT) || (req->r_flags & R_SOFT))
                req->r_retry = nmp->nm_retry;
        else
                req->r_retry = NFS_MAXREXMIT + 1;       /* past clip limit */
                req->r_retry = nmp->nm_retry;
        else
                req->r_retry = NFS_MAXREXMIT + 1;       /* past clip limit */
@@ -3802,7 +3890,7 @@ nfs_request_send(struct nfsreq *req, int wait)
        lck_mtx_lock(nfs_request_mutex);
 
        nmp = req->r_np ? NFSTONMP(req->r_np) : req->r_nmp;
        lck_mtx_lock(nfs_request_mutex);
 
        nmp = req->r_np ? NFSTONMP(req->r_np) : req->r_nmp;
-       if (!nmp) {
+       if (nfs_mount_gone(nmp)) {
                lck_mtx_unlock(nfs_request_mutex);
                return (ENXIO);
        }
                lck_mtx_unlock(nfs_request_mutex);
                return (ENXIO);
        }
@@ -3930,8 +4018,8 @@ nfs_request_finish(
         */
        if (!error) {
                if ((req->r_flags & R_TPRINTFMSG) ||
         */
        if (!error) {
                if ((req->r_flags & R_TPRINTFMSG) ||
-                   (nmp && NMFLAG(nmp, SOFT) &&
-                    ((nmp->nm_state & (NFSSTA_TIMEO|NFSSTA_FORCE)) == NFSSTA_TIMEO)))
+                   (nmp && (NMFLAG(nmp, SOFT) || (req->r_flags & R_SOFT)) &&
+                    ((nmp->nm_state & (NFSSTA_TIMEO|NFSSTA_FORCE|NFSSTA_DEAD)) == NFSSTA_TIMEO)))
                        nfs_up(nmp, req->r_thread, NFSSTA_TIMEO, "is alive again");
                else
                        nfs_up(nmp, req->r_thread, NFSSTA_TIMEO, NULL);
                        nfs_up(nmp, req->r_thread, NFSSTA_TIMEO, "is alive again");
                else
                        nfs_up(nmp, req->r_thread, NFSSTA_TIMEO, NULL);
@@ -4047,9 +4135,10 @@ nfs_request_finish(
                                        lck_mtx_unlock(&nmp->nm_lock);
                                }
                                nfs_down(req->r_nmp, req->r_thread, 0, NFSSTA_JUKEBOXTIMEO,
                                        lck_mtx_unlock(&nmp->nm_lock);
                                }
                                nfs_down(req->r_nmp, req->r_thread, 0, NFSSTA_JUKEBOXTIMEO,
-                                       "resource temporarily unavailable (jukebox)");
+                                        "resource temporarily unavailable (jukebox)", 0);
                        }
                        }
-                       if (NMFLAG(nmp, SOFT) && (req->r_delay == 30) && !(req->r_flags & R_NOINTR)) {
+                       if ((NMFLAG(nmp, SOFT) || (req->r_flags & R_SOFT)) && (req->r_delay == 30) &&
+                               !(req->r_flags & R_NOINTR)) {
                                /* for soft mounts, just give up after a short while */
                                OSAddAtomic64(1, &nfsstats.rpctimeouts);
                                nfs_softterm(req);
                                /* for soft mounts, just give up after a short while */
                                OSAddAtomic64(1, &nfsstats.rpctimeouts);
                                nfs_softterm(req);
@@ -4133,14 +4222,6 @@ nfs_request_finish(
                        /* find the next flavor to try */
                        for(i=0; i < NX_MAX_SEC_FLAVORS; i++)
                                if (req->r_wrongsec[i] != RPCAUTH_INVALID) {
                        /* find the next flavor to try */
                        for(i=0; i < NX_MAX_SEC_FLAVORS; i++)
                                if (req->r_wrongsec[i] != RPCAUTH_INVALID) {
-                                       if (((req->r_wrongsec[i] == RPCAUTH_KRB5P) ||
-                                            (req->r_wrongsec[i] == RPCAUTH_KRB5I) ||
-                                            (req->r_wrongsec[i] == RPCAUTH_KRB5)) && (req->r_gss_ctx &&
-                                           (req->r_gss_ctx->gss_clnt_service == RPCSEC_GSS_SVC_SYS))) {
-                                               /* don't bother trying Kerberos if we've already got a fallback context */
-                                               req->r_wrongsec[i] = RPCAUTH_INVALID;
-                                               continue;
-                                       }
                                        if (!srvcount) /* no server list, just try it */
                                                break;
                                        /* check that it's in the server's list */
                                        if (!srvcount) /* no server list, just try it */
                                                break;
                                        /* check that it's in the server's list */
@@ -4291,7 +4372,7 @@ nfs_request2(
 
        if ((error = nfs_request_create(np, mp, nmrest, procnum, thd, cred, &req)))
                return (error);
 
        if ((error = nfs_request_create(np, mp, nmrest, procnum, thd, cred, &req)))
                return (error);
-       req->r_flags |= (flags & R_OPTMASK);
+       req->r_flags |= (flags & (R_OPTMASK | R_SOFT));
        if (si)
                req->r_secinfo = *si;
 
        if (si)
                req->r_secinfo = *si;
 
@@ -4334,7 +4415,7 @@ nfs_request_gss(
                int *status)
 {
        struct nfsreq rq, *req = &rq;
                int *status)
 {
        struct nfsreq rq, *req = &rq;
-       int error;
+       int error, wait = 1;
 
        if ((error = nfs_request_create(NULL, mp, nmrest, NFSPROC_NULL, thd, cred, &req)))
                return (error);
 
        if ((error = nfs_request_create(NULL, mp, nmrest, NFSPROC_NULL, thd, cred, &req)))
                return (error);
@@ -4347,6 +4428,13 @@ nfs_request_gss(
        }
        nfs_gss_clnt_ctx_ref(req, cp);
 
        }
        nfs_gss_clnt_ctx_ref(req, cp);
 
+       /*
+        * Don't wait for a reply to a context destroy advisory
+        * to avoid hanging on a dead server.
+        */
+       if (cp->gss_clnt_proc == RPCSEC_GSS_DESTROY)
+               wait = 0;
+
        FSDBG_TOP(273, R_XID32(req->r_xid), NULL, NFSPROC_NULL, 0);
        do {
                req->r_error = 0;
        FSDBG_TOP(273, R_XID32(req->r_xid), NULL, NFSPROC_NULL, 0);
        do {
                req->r_error = 0;
@@ -4354,15 +4442,21 @@ nfs_request_gss(
                if ((error = nfs_request_add_header(req)))
                        break;
 
                if ((error = nfs_request_add_header(req)))
                        break;
 
-               if ((error = nfs_request_send(req, 1)))
+               if ((error = nfs_request_send(req, wait)))
                        break;
                        break;
+               if (!wait)
+                       break;
+
                nfs_request_wait(req);
                if ((error = nfs_request_finish(req, nmrepp, status)))
                        break;
        } while (req->r_flags & R_RESTART);
 
        FSDBG_BOT(273, R_XID32(req->r_xid), NULL, NFSPROC_NULL, error);
                nfs_request_wait(req);
                if ((error = nfs_request_finish(req, nmrepp, status)))
                        break;
        } while (req->r_flags & R_RESTART);
 
        FSDBG_BOT(273, R_XID32(req->r_xid), NULL, NFSPROC_NULL, error);
+
+       nfs_gss_clnt_ctx_unref(req);
        nfs_request_rele(req);
        nfs_request_rele(req);
+
        return (error);
 }
 
        return (error);
 }
 
@@ -4409,7 +4503,8 @@ nfs_request_async(
                        int slpflag = (req->r_nmp && NMFLAG(req->r_nmp, INTR) && req->r_thread && !(req->r_flags & R_NOINTR)) ? PCATCH : 0;
                        struct timespec ts = { 2, 0 };
                        while (!(req->r_flags & R_SENT)) {
                        int slpflag = (req->r_nmp && NMFLAG(req->r_nmp, INTR) && req->r_thread && !(req->r_flags & R_NOINTR)) ? PCATCH : 0;
                        struct timespec ts = { 2, 0 };
                        while (!(req->r_flags & R_SENT)) {
-                               if ((req->r_flags & R_RESENDQ) && ((nmp = req->r_nmp))) {
+                               nmp = req->r_nmp;
+                               if ((req->r_flags & R_RESENDQ) && !nfs_mount_gone(nmp)) {
                                        lck_mtx_lock(&nmp->nm_lock);
                                        if ((nmp->nm_state & NFSSTA_RECOVER) && (req->r_rchain.tqe_next != NFSREQNOLIST)) {
                                                /*
                                        lck_mtx_lock(&nmp->nm_lock);
                                        if ((nmp->nm_state & NFSSTA_RECOVER) && (req->r_rchain.tqe_next != NFSREQNOLIST)) {
                                                /*
@@ -4425,6 +4520,8 @@ nfs_request_async(
                                                req->r_flags |= R_SENDING;
                                                lck_mtx_unlock(&req->r_mtx);
                                                error = nfs_send(req, 1);
                                                req->r_flags |= R_SENDING;
                                                lck_mtx_unlock(&req->r_mtx);
                                                error = nfs_send(req, 1);
+                                               /* Remove the R_RESENDQ reference */
+                                               nfs_request_rele(req);
                                                lck_mtx_lock(&req->r_mtx);
                                                if (error)
                                                        break;
                                                lck_mtx_lock(&req->r_mtx);
                                                if (error)
                                                        break;
@@ -4440,12 +4537,14 @@ nfs_request_async(
                }
                sent = req->r_flags & R_SENT;
                lck_mtx_unlock(&req->r_mtx);
                }
                sent = req->r_flags & R_SENT;
                lck_mtx_unlock(&req->r_mtx);
-               if (error && req->r_callback.rcb_func && !sent)
+               if (error && req->r_callback.rcb_func && !sent) {
                        nfs_request_rele(req);
                        nfs_request_rele(req);
+               }
        }
        FSDBG(274, R_XID32(req->r_xid), np, procnum, error);
        if (error || req->r_callback.rcb_func)
                nfs_request_rele(req);
        }
        FSDBG(274, R_XID32(req->r_xid), np, procnum, error);
        if (error || req->r_callback.rcb_func)
                nfs_request_rele(req);
+
        return (error);
 }
 
        return (error);
 }
 
@@ -4467,6 +4566,7 @@ nfs_request_async_finish(
                req->r_flags |= R_ASYNCWAIT;
        while (req->r_flags & R_RESENDQ) {  /* wait until the request is off the resend queue */
                struct timespec ts = { 2, 0 };
                req->r_flags |= R_ASYNCWAIT;
        while (req->r_flags & R_RESENDQ) {  /* wait until the request is off the resend queue */
                struct timespec ts = { 2, 0 };
+
                if ((nmp = req->r_nmp)) {
                        lck_mtx_lock(&nmp->nm_lock);
                        if ((nmp->nm_state & NFSSTA_RECOVER) && (req->r_rchain.tqe_next != NFSREQNOLIST)) {
                if ((nmp = req->r_nmp)) {
                        lck_mtx_lock(&nmp->nm_lock);
                        if ((nmp->nm_state & NFSSTA_RECOVER) && (req->r_rchain.tqe_next != NFSREQNOLIST)) {
@@ -4479,6 +4579,9 @@ nfs_request_async_finish(
                                req->r_rchain.tqe_next = NFSREQNOLIST;
                                if (req->r_flags & R_RESENDQ)
                                        req->r_flags &= ~R_RESENDQ;
                                req->r_rchain.tqe_next = NFSREQNOLIST;
                                if (req->r_flags & R_RESENDQ)
                                        req->r_flags &= ~R_RESENDQ;
+                               /* Remove the R_RESENDQ reference */
+                               assert(req->r_refs > 0);
+                               req->r_refs--;
                                lck_mtx_unlock(&nmp->nm_lock);
                                break;
                        }
                                lck_mtx_unlock(&nmp->nm_lock);
                                break;
                        }
@@ -4496,11 +4599,16 @@ nfs_request_async_finish(
        }
 
        while (!error && (req->r_flags & R_RESTART)) {
        }
 
        while (!error && (req->r_flags & R_RESTART)) {
-               if (asyncio && req->r_resendtime) {  /* send later */
+               if (asyncio) {
+                       assert(req->r_achain.tqe_next == NFSREQNOLIST);
                        lck_mtx_lock(&req->r_mtx);
                        lck_mtx_lock(&req->r_mtx);
-                       nfs_asyncio_resend(req);
+                       req->r_flags &= ~R_IOD;
+                       if (req->r_resendtime) {  /* send later */
+                               nfs_asyncio_resend(req);
+                               lck_mtx_unlock(&req->r_mtx);
+                               return (EINPROGRESS);
+                       }
                        lck_mtx_unlock(&req->r_mtx);
                        lck_mtx_unlock(&req->r_mtx);
-                       return (EINPROGRESS);
                }
                req->r_error = 0;
                req->r_flags &= ~R_RESTART;
                }
                req->r_error = 0;
                req->r_flags &= ~R_RESTART;
@@ -4528,7 +4636,6 @@ nfs_request_async_finish(
 void
 nfs_request_async_cancel(struct nfsreq *req)
 {
 void
 nfs_request_async_cancel(struct nfsreq *req)
 {
-       nfs_reqdequeue(req);
        FSDBG(275, R_XID32(req->r_xid), req->r_np, req->r_procnum, 0xD1ED1E);
        nfs_request_rele(req);
 }
        FSDBG(275, R_XID32(req->r_xid), req->r_np, req->r_procnum, 0xD1ED1E);
        nfs_request_rele(req);
 }
@@ -4542,7 +4649,7 @@ nfs_softterm(struct nfsreq *req)
        struct nfsmount *nmp = req->r_nmp;
        req->r_flags |= R_SOFTTERM;
        req->r_error = ETIMEDOUT;
        struct nfsmount *nmp = req->r_nmp;
        req->r_flags |= R_SOFTTERM;
        req->r_error = ETIMEDOUT;
-       if (!(req->r_flags & R_CWND) || !nmp)
+       if (!(req->r_flags & R_CWND) || nfs_mount_gone(nmp))
                return;
        /* update congestion window */
        req->r_flags &= ~R_CWND;
                return;
        /* update congestion window */
        req->r_flags &= ~R_CWND;
@@ -4634,6 +4741,7 @@ nfs_request_timer(__unused void *param0, __unused void *param1)
        struct timeval now;
        TAILQ_HEAD(nfs_mount_pokeq, nfsmount) nfs_mount_poke_queue;
 
        struct timeval now;
        TAILQ_HEAD(nfs_mount_pokeq, nfsmount) nfs_mount_poke_queue;
 
+restart:
        lck_mtx_lock(nfs_request_mutex);
        req = TAILQ_FIRST(&nfs_reqq);
        if (req == NULL) {      /* no requests - turn timer off */
        lck_mtx_lock(nfs_request_mutex);
        req = TAILQ_FIRST(&nfs_reqq);
        if (req == NULL) {      /* no requests - turn timer off */
@@ -4648,8 +4756,10 @@ nfs_request_timer(__unused void *param0, __unused void *param1)
        microuptime(&now);
        for ( ; req != NULL ; req = nfs_reqnext(req)) {
                nmp = req->r_nmp;
        microuptime(&now);
        for ( ; req != NULL ; req = nfs_reqnext(req)) {
                nmp = req->r_nmp;
-               if (!nmp) /* unmounted */
+               if (nmp == NULL) {
+                       NFS_SOCK_DBG("Found a request with out a mount!\n");
                        continue;
                        continue;
+               }
                if (req->r_error || req->r_nmrep.nmc_mhead)
                        continue;
                if ((error = nfs_sigintr(nmp, req, req->r_thread, 0))) {
                if (req->r_error || req->r_nmrep.nmc_mhead)
                        continue;
                if ((error = nfs_sigintr(nmp, req, req->r_thread, 0))) {
@@ -4673,7 +4783,7 @@ nfs_request_timer(__unused void *param0, __unused void *param1)
                    ((req->r_lastmsg + nmp->nm_tprintf_delay) < now.tv_sec)) {
                        req->r_lastmsg = now.tv_sec;
                        nfs_down(req->r_nmp, req->r_thread, 0, NFSSTA_TIMEO,
                    ((req->r_lastmsg + nmp->nm_tprintf_delay) < now.tv_sec)) {
                        req->r_lastmsg = now.tv_sec;
                        nfs_down(req->r_nmp, req->r_thread, 0, NFSSTA_TIMEO,
-                               "not responding");
+                                "not responding", 1);
                        req->r_flags |= R_TPRINTFMSG;
                        lck_mtx_lock(&nmp->nm_lock);
                        if (!(nmp->nm_state & NFSSTA_MOUNTED)) {
                        req->r_flags |= R_TPRINTFMSG;
                        lck_mtx_lock(&nmp->nm_lock);
                        if (!(nmp->nm_state & NFSSTA_MOUNTED)) {
@@ -4696,7 +4806,7 @@ nfs_request_timer(__unused void *param0, __unused void *param1)
                 * Put a reasonable limit on the maximum timeout,
                 * and reduce that limit when soft mounts get timeouts or are in reconnect.
                 */
                 * Put a reasonable limit on the maximum timeout,
                 * and reduce that limit when soft mounts get timeouts or are in reconnect.
                 */
-               if (!NMFLAG(nmp, SOFT) && !nfs_can_squish(nmp))
+               if (!(NMFLAG(nmp, SOFT) || (req->r_flags & R_SOFT)) && !nfs_can_squish(nmp))
                        maxtime = NFS_MAXTIMEO;
                else if ((req->r_flags & (R_SETUP|R_RECOVER)) ||
                         ((nmp->nm_reconnect_start <= 0) || ((now.tv_sec - nmp->nm_reconnect_start) < 8)))
                        maxtime = NFS_MAXTIMEO;
                else if ((req->r_flags & (R_SETUP|R_RECOVER)) ||
                         ((nmp->nm_reconnect_start <= 0) || ((now.tv_sec - nmp->nm_reconnect_start) < 8)))
@@ -4731,6 +4841,7 @@ nfs_request_timer(__unused void *param0, __unused void *param1)
                        if (timeo > maxtime)
                                timeo = maxtime;
                        if (req->r_rtt <= timeo) {
                        if (timeo > maxtime)
                                timeo = maxtime;
                        if (req->r_rtt <= timeo) {
+                               NFS_SOCK_DBG("nfs timeout: req time %d and timeo is %d continue\n", req->r_rtt, timeo);
                                lck_mtx_unlock(&nmp->nm_lock);
                                lck_mtx_unlock(&req->r_mtx);
                                continue;
                                lck_mtx_unlock(&nmp->nm_lock);
                                lck_mtx_unlock(&req->r_mtx);
                                continue;
@@ -4742,7 +4853,48 @@ nfs_request_timer(__unused void *param0, __unused void *param1)
                                (now.tv_sec - req->r_start)*NFS_HZ, maxtime);
                        if (nmp->nm_timeouts < 8)
                                nmp->nm_timeouts++;
                                (now.tv_sec - req->r_start)*NFS_HZ, maxtime);
                        if (nmp->nm_timeouts < 8)
                                nmp->nm_timeouts++;
-                       nfs_mount_check_dead_timeout(nmp);
+                       if (nfs_mount_check_dead_timeout(nmp)) {
+                               /* Unbusy this request */
+                               req->r_lflags &= ~RL_BUSY;
+                               if (req->r_lflags & RL_WAITING) {
+                                       req->r_lflags &= ~RL_WAITING;
+                                       wakeup(&req->r_lflags);
+                               }
+                               lck_mtx_unlock(&req->r_mtx);
+
+                               /* No need to poke this mount */
+                               if (nmp->nm_sockflags & NMSOCK_POKE) {
+                                       nmp->nm_sockflags &= ~NMSOCK_POKE;
+                                       TAILQ_REMOVE(&nfs_mount_poke_queue, nmp, nm_pokeq);
+                               }
+                               /* Release our lock state, so we can become a zombie */
+                               lck_mtx_unlock(nfs_request_mutex);
+
+                               /*
+                                * Note nfs_mount_make zombie(nmp) must be
+                                * called with nm_lock held. After doing some
+                                * work we release nm_lock in
+                                * nfs_make_mount_zombie with out acquiring any
+                                * other locks. (Later, in nfs_mount_zombie we
+                                * will acquire nfs_request_mutex, r_mtx,
+                                * nm_lock in that order). So we should not be
+                                * introducing deadlock here. We take a reference
+                                * on the mount so that its still there when we
+                                * release the lock.
+                                */
+                               nmp->nm_ref++;
+                               nfs_mount_make_zombie(nmp);
+                               lck_mtx_unlock(&nmp->nm_lock);
+                               nfs_mount_rele(nmp);
+
+                               /*
+                                * All the request for this mount have now been
+                                * removed from the request queue. Restart to
+                                * process the remaining mounts
+                                */
+                               goto restart;
+                       }
+                       
                        /* if it's been a few seconds, try poking the socket */
                        if ((nmp->nm_sotype == SOCK_STREAM) &&
                            ((now.tv_sec - req->r_start) >= 3) &&
                        /* if it's been a few seconds, try poking the socket */
                        if ((nmp->nm_sotype == SOCK_STREAM) &&
                            ((now.tv_sec - req->r_start) >= 3) &&
@@ -4755,7 +4907,7 @@ nfs_request_timer(__unused void *param0, __unused void *param1)
                }
 
                /* For soft mounts (& SETUPs/RECOVERs), check for too many retransmits/timeout. */
                }
 
                /* For soft mounts (& SETUPs/RECOVERs), check for too many retransmits/timeout. */
-               if ((NMFLAG(nmp, SOFT) ||  (req->r_flags & (R_SETUP|R_RECOVER))) &&
+               if ((NMFLAG(nmp, SOFT) ||  (req->r_flags & (R_SETUP|R_RECOVER|R_SOFT))) &&
                    ((req->r_rexmit >= req->r_retry) || /* too many */
                     ((now.tv_sec - req->r_start)*NFS_HZ > maxtime))) { /* too long */
                        OSAddAtomic64(1, &nfsstats.rpctimeouts);
                    ((req->r_rexmit >= req->r_retry) || /* too many */
                     ((now.tv_sec - req->r_start)*NFS_HZ > maxtime))) { /* too long */
                        OSAddAtomic64(1, &nfsstats.rpctimeouts);
@@ -4765,7 +4917,7 @@ nfs_request_timer(__unused void *param0, __unused void *param1)
                                /* make sure we note the unresponsive server */
                                /* (maxtime may be less than tprintf delay) */
                                nfs_down(req->r_nmp, req->r_thread, 0, NFSSTA_TIMEO,
                                /* make sure we note the unresponsive server */
                                /* (maxtime may be less than tprintf delay) */
                                nfs_down(req->r_nmp, req->r_thread, 0, NFSSTA_TIMEO,
-                                       "not responding");
+                                        "not responding", 1);
                                req->r_lastmsg = now.tv_sec;
                                req->r_flags |= R_TPRINTFMSG;
                        } else {
                                req->r_lastmsg = now.tv_sec;
                                req->r_flags |= R_TPRINTFMSG;
                        } else {
@@ -4810,7 +4962,7 @@ nfs_request_timer(__unused void *param0, __unused void *param1)
                req->r_flags |= R_MUSTRESEND;
                req->r_rtt = -1;
                wakeup(req);
                req->r_flags |= R_MUSTRESEND;
                req->r_rtt = -1;
                wakeup(req);
-               if ((req->r_flags & (R_ASYNC|R_ASYNCWAIT|R_SENDING)) == R_ASYNC)
+               if ((req->r_flags & (R_IOD|R_ASYNC|R_ASYNCWAIT|R_SENDING)) == R_ASYNC)
                        nfs_asyncio_resend(req);
                lck_mtx_unlock(&req->r_mtx);
        }
                        nfs_asyncio_resend(req);
                lck_mtx_unlock(&req->r_mtx);
        }
@@ -4821,10 +4973,6 @@ nfs_request_timer(__unused void *param0, __unused void *param1)
        while ((nmp = TAILQ_FIRST(&nfs_mount_poke_queue))) {
                TAILQ_REMOVE(&nfs_mount_poke_queue, nmp, nm_pokeq);
                nfs_sock_poke(nmp);
        while ((nmp = TAILQ_FIRST(&nfs_mount_poke_queue))) {
                TAILQ_REMOVE(&nfs_mount_poke_queue, nmp, nm_pokeq);
                nfs_sock_poke(nmp);
-               lck_mtx_lock(&nmp->nm_lock);
-               nmp->nm_sockflags &= ~NMSOCK_POKE;
-               wakeup(&nmp->nm_sockflags);
-               lck_mtx_unlock(&nmp->nm_lock);
        }
 
        nfs_interval_timer_start(nfs_request_timer_call, NFS_REQUESTDELAY);
        }
 
        nfs_interval_timer_start(nfs_request_timer_call, NFS_REQUESTDELAY);
@@ -4855,7 +5003,7 @@ nfs_sigintr(struct nfsmount *nmp, struct nfsreq *req, thread_t thd, int nmplocke
        proc_t p;
        int error = 0;
 
        proc_t p;
        int error = 0;
 
-       if (nmp == NULL)
+       if (!nmp)
                return (ENXIO);
 
        if (req && (req->r_flags & R_SOFTTERM))
                return (ENXIO);
 
        if (req && (req->r_flags & R_SOFTTERM))
@@ -4868,7 +5016,7 @@ nfs_sigintr(struct nfsmount *nmp, struct nfsreq *req, thread_t thd, int nmplocke
        if (nmp->nm_state & NFSSTA_FORCE) {
                /* If a force unmount is in progress then fail. */
                error = EIO;
        if (nmp->nm_state & NFSSTA_FORCE) {
                /* If a force unmount is in progress then fail. */
                error = EIO;
-       } else if (nmp->nm_mountp->mnt_kern_flag & MNTK_FRCUNMOUNT) {
+       } else if (vfs_isforce(nmp->nm_mountp)) {
                /* Someone is unmounting us, go soft and mark it. */
                NFS_BITMAP_SET(nmp->nm_flags, NFS_MFLAG_SOFT);
                nmp->nm_state |= NFSSTA_FORCE;
                /* Someone is unmounting us, go soft and mark it. */
                NFS_BITMAP_SET(nmp->nm_flags, NFS_MFLAG_SOFT);
                nmp->nm_state |= NFSSTA_FORCE;
@@ -4928,7 +5076,7 @@ nfs_sndlock(struct nfsreq *req)
        int error = 0, slpflag = 0;
        struct timespec ts = { 0, 0 };
 
        int error = 0, slpflag = 0;
        struct timespec ts = { 0, 0 };
 
-       if (nmp == NULL)
+       if (nfs_mount_gone(nmp))
                return (ENXIO);
 
        lck_mtx_lock(&nmp->nm_lock);
                return (ENXIO);
 
        lck_mtx_lock(&nmp->nm_lock);
@@ -4963,7 +5111,7 @@ nfs_sndunlock(struct nfsreq *req)
        struct nfsmount *nmp = req->r_nmp;
        int *statep, wake = 0;
 
        struct nfsmount *nmp = req->r_nmp;
        int *statep, wake = 0;
 
-       if (nmp == NULL)
+       if (!nmp)
                return;
        lck_mtx_lock(&nmp->nm_lock);
        statep = &nmp->nm_state;
                return;
        lck_mtx_lock(&nmp->nm_lock);
        statep = &nmp->nm_state;
@@ -5032,9 +5180,21 @@ nfs_aux_request(
                }
 
                if (sotype == SOCK_STREAM) {
                }
 
                if (sotype == SOCK_STREAM) {
-                       on = 4; /* don't wait too long for the socket to connect */
-                       sock_setsockopt(newso, IPPROTO_TCP, TCP_CONNECTIONTIMEOUT, &on, sizeof(on));
-                       error = sock_connect(newso, saddr, 0);
+#                      define NFS_AUX_CONNECTION_TIMEOUT 4   /* 4 second timeout for connections */
+                       int count = 0;
+                       
+                       error = sock_connect(newso, saddr, MSG_DONTWAIT);
+                       if (error == EINPROGRESS)
+                               error = 0;
+                       nfsmout_if(error);
+
+                       while ((error = sock_connectwait(newso, &tv)) == EINPROGRESS) {
+                               /* After NFS_AUX_CONNECTION_TIMEOUT bail */
+                               if (++count >= NFS_AUX_CONNECTION_TIMEOUT) {
+                                       error = ETIMEDOUT;
+                                       break;
+                               }
+                       }
                        nfsmout_if(error);
                }
                if (((error = sock_setsockopt(newso, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)))) ||
                        nfsmout_if(error);
                }
                if (((error = sock_setsockopt(newso, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)))) ||
@@ -5356,7 +5516,7 @@ nfs_is_squishy(struct nfsmount *nmp)
        int squishy = 0;
        int timeo = (nfs_squishy_flags & NFS_SQUISH_QUICK) ? NFS_SQUISHY_QUICKTIMEOUT : NFS_SQUISHY_DEADTIMEOUT;
 
        int squishy = 0;
        int timeo = (nfs_squishy_flags & NFS_SQUISH_QUICK) ? NFS_SQUISHY_QUICKTIMEOUT : NFS_SQUISHY_DEADTIMEOUT;
 
-       NFS_SOCK_DBG("%s: nm_curdeadtiemout = %d, nfs_is_mobile = %d\n",
+       NFS_SOCK_DBG("%s: nm_curdeadtimeout = %d, nfs_is_mobile = %d\n",
                      vfs_statfs(mp)->f_mntfromname, nmp->nm_curdeadtimeout,  nfs_is_mobile);
 
        if (!nfs_can_squish(nmp))
                      vfs_statfs(mp)->f_mntfromname, nmp->nm_curdeadtimeout,  nfs_is_mobile);
 
        if (!nfs_can_squish(nmp))
@@ -5396,45 +5556,77 @@ out:
  * and NFS_SQUISH_QUICK flag is set and we are in a squishy state then mark the mount as dead
  * and ask to be forcibly unmounted. Return 1 if we're dead and 0 otherwise.
  */
  * and NFS_SQUISH_QUICK flag is set and we are in a squishy state then mark the mount as dead
  * and ask to be forcibly unmounted. Return 1 if we're dead and 0 otherwise.
  */
-static int
-nfs_is_dead_lock(int error, struct nfsmount *nmp)
+int
+nfs_is_dead(int error, struct nfsmount *nmp)
 {
 {
-       if (nmp->nm_state & NFSSTA_DEAD)
+       fsid_t fsid;
+
+       lck_mtx_lock(&nmp->nm_lock);
+       if (nmp->nm_state & NFSSTA_DEAD) {
+               lck_mtx_unlock(&nmp->nm_lock);
                return (1);
                return (1);
+       }
 
        if ((error != ENETUNREACH && error != EHOSTUNREACH && error != EADDRNOTAVAIL) ||
 
        if ((error != ENETUNREACH && error != EHOSTUNREACH && error != EADDRNOTAVAIL) ||
-           !(nmp->nm_locations.nl_numlocs == 1 && nmp->nm_locations.nl_locations[0]->nl_servcount == 1))
+           !(nmp->nm_locations.nl_numlocs == 1 && nmp->nm_locations.nl_locations[0]->nl_servcount == 1)) {
+               lck_mtx_unlock(&nmp->nm_lock);
                return (0);
                return (0);
+       }
 
        if ((nfs_squishy_flags & NFS_SQUISH_QUICK) && nfs_is_squishy(nmp)) {
                printf("nfs_is_dead: nfs server %s: unreachable. Squished dead\n", vfs_statfs(nmp->nm_mountp)->f_mntfromname);
 
        if ((nfs_squishy_flags & NFS_SQUISH_QUICK) && nfs_is_squishy(nmp)) {
                printf("nfs_is_dead: nfs server %s: unreachable. Squished dead\n", vfs_statfs(nmp->nm_mountp)->f_mntfromname);
-               nmp->nm_state |= NFSSTA_DEAD;
-               vfs_event_signal(&vfs_statfs(nmp->nm_mountp)->f_fsid, VQ_DEAD, 0);
+               fsid = vfs_statfs(nmp->nm_mountp)->f_fsid;
+               lck_mtx_unlock(&nmp->nm_lock);
+               nfs_mount_zombie(nmp, NFSSTA_DEAD);
+               vfs_event_signal(&fsid, VQ_DEAD, 0);
                return (1);
        }
                return (1);
        }
+       lck_mtx_unlock(&nmp->nm_lock);
        return (0);
 }
 
        return (0);
 }
 
+/*
+ * If we've experienced timeouts and we're not really a
+ * classic hard mount, then just return cached data to
+ * the caller instead of likely hanging on an RPC.
+ */
 int
 int
-nfs_is_dead(int error, struct nfsmount *nmp)
+nfs_use_cache(struct nfsmount *nmp)
 {
 {
-       int is_dead;
+       /*
+        *%%% We always let mobile users goto the cache,
+        * perhaps we should not even require them to have
+        * a timeout?
+        */
+       int cache_ok = (nfs_is_mobile || NMFLAG(nmp, SOFT) ||
+                       nfs_can_squish(nmp) || nmp->nm_deadtimeout);
 
 
-       lck_mtx_lock(&nmp->nm_lock);
-       is_dead = nfs_is_dead_lock(error, nmp);
-       lck_mtx_unlock(&nmp->nm_lock);
+       int timeoutmask = NFSSTA_TIMEO | NFSSTA_LOCKTIMEO | NFSSTA_JUKEBOXTIMEO;
 
 
-       return (is_dead);
+       /*
+        * So if we have a timeout and we're not really a hard hard-mount,
+        * return 1 to not get things out of the cache.
+        */
+
+       return ((nmp->nm_state & timeoutmask) && cache_ok);
 }
 
 }
 
+/*
+ * Log a message that nfs or lockd server is unresponsive. Check if we
+ * can be squished and if we can, or that our dead timeout has
+ * expired, and we're not holding state, set our mount as dead, remove
+ * our mount state and ask to be unmounted. If we are holding state
+ * we're being called from the nfs_request_timer and will soon detect
+ * that we need to unmount.
+ */
 void
 void
-nfs_down(struct nfsmount *nmp, thread_t thd, int error, int flags, const char *msg)
+nfs_down(struct nfsmount *nmp, thread_t thd, int error, int flags, const char *msg, int holding_state)
 {
        int timeoutmask, wasunresponsive, unresponsive, softnobrowse;
 {
        int timeoutmask, wasunresponsive, unresponsive, softnobrowse;
-       uint32_t do_vfs_signal;
+       uint32_t do_vfs_signal = 0;
        struct timeval now;
 
        struct timeval now;
 
-       if (nmp == NULL)
+       if (nfs_mount_gone(nmp))
                return;
 
        lck_mtx_lock(&nmp->nm_lock);
                return;
 
        lck_mtx_lock(&nmp->nm_lock);
@@ -5463,17 +5655,17 @@ nfs_down(struct nfsmount *nmp, thread_t thd, int error, int flags, const char *m
                if (!wasunresponsive) {
                        nmp->nm_deadto_start = now.tv_sec;
                        nfs_mount_sock_thread_wake(nmp);
                if (!wasunresponsive) {
                        nmp->nm_deadto_start = now.tv_sec;
                        nfs_mount_sock_thread_wake(nmp);
-               } else if ((now.tv_sec - nmp->nm_deadto_start) > nmp->nm_curdeadtimeout) {
+               } else if ((now.tv_sec - nmp->nm_deadto_start) > nmp->nm_curdeadtimeout && !holding_state) {
                        if (!(nmp->nm_state & NFSSTA_DEAD))
                                printf("nfs server %s: %sdead\n", vfs_statfs(nmp->nm_mountp)->f_mntfromname,
                                       (nmp->nm_curdeadtimeout != nmp->nm_deadtimeout) ? "squished " : "");
                        if (!(nmp->nm_state & NFSSTA_DEAD))
                                printf("nfs server %s: %sdead\n", vfs_statfs(nmp->nm_mountp)->f_mntfromname,
                                       (nmp->nm_curdeadtimeout != nmp->nm_deadtimeout) ? "squished " : "");
-                       nmp->nm_state |= NFSSTA_DEAD;
+                       do_vfs_signal = VQ_DEAD;
                }
        }
        lck_mtx_unlock(&nmp->nm_lock);
 
                }
        }
        lck_mtx_unlock(&nmp->nm_lock);
 
-       if (nmp->nm_state & NFSSTA_DEAD)
-               do_vfs_signal = VQ_DEAD;
+       if (do_vfs_signal == VQ_DEAD && !(nmp->nm_state & NFSSTA_DEAD))
+               nfs_mount_zombie(nmp, NFSSTA_DEAD);
        else if (softnobrowse || wasunresponsive || !unresponsive)
                do_vfs_signal = 0;
        else
        else if (softnobrowse || wasunresponsive || !unresponsive)
                do_vfs_signal = 0;
        else
@@ -5490,7 +5682,7 @@ nfs_up(struct nfsmount *nmp, thread_t thd, int flags, const char *msg)
        int timeoutmask, wasunresponsive, unresponsive, softnobrowse;
        int do_vfs_signal;
 
        int timeoutmask, wasunresponsive, unresponsive, softnobrowse;
        int do_vfs_signal;
 
-       if (nmp == NULL)
+       if (nfs_mount_gone(nmp))
                return;
 
        if (msg)
                return;
 
        if (msg)