#include <nfs/nfsmount.h>
#include <nfs/nfsnode.h>
+#define NFS_SOCK_DBG(...) NFS_DBG(NFS_FAC_SOCK, 7, ## __VA_ARGS__)
+
/* XXX */
boolean_t current_thread_aborted(void);
kern_return_t thread_terminate(thread_t);
#if NFSCLIENT
+int nfs_connect_search_new_socket(struct nfsmount *, struct nfs_socket_search *, struct timeval *);
+int nfs_connect_search_socket_connect(struct nfsmount *, struct nfs_socket *, int);
+int nfs_connect_search_ping(struct nfsmount *, struct nfs_socket *, struct timeval *);
+void nfs_connect_search_socket_found(struct nfsmount *, struct nfs_socket_search *, struct nfs_socket *);
+void nfs_connect_search_socket_reap(struct nfsmount *, struct nfs_socket_search *, struct timeval *);
+int nfs_connect_search_check(struct nfsmount *, struct nfs_socket_search *, struct timeval *);
int nfs_reconnect(struct nfsmount *);
int nfs_connect_setup(struct nfsmount *);
void nfs_mount_sock_thread(void *, wait_result_t);
struct nfsreq *nfs_reqnext(struct nfsreq *);
int nfs_wait_reply(struct nfsreq *);
void nfs_softterm(struct nfsreq *);
-
-#ifdef NFS_SOCKET_DEBUGGING
-#define NFS_SOCK_DBG(X) printf X
-#else
-#define NFS_SOCK_DBG(X)
-#endif
+int nfs_can_squish(struct nfsmount *);
+int nfs_is_squishy(struct nfsmount *);
+int nfs_is_dead(int, struct nfsmount *);
/*
* Estimate rto for an nfs rpc sent via. an unreliable datagram.
int error = 0, recv = 1;
if (nso->nso_flags & NSO_CONNECTING) {
- NFS_SOCK_DBG(("nfs connect - socket %p upcall - connecting\n", nso));
+ NFS_SOCK_DBG("nfs connect - socket %p upcall - connecting\n", nso);
wakeup(nso->nso_wake);
return;
}
lck_mtx_lock(&nso->nso_lock);
if ((nso->nso_flags & (NSO_UPCALL|NSO_DISCONNECTING|NSO_DEAD)) || !(nso->nso_flags & NSO_PINGING)) {
- NFS_SOCK_DBG(("nfs connect - socket %p upcall - nevermind\n", nso));
+ NFS_SOCK_DBG("nfs connect - socket %p upcall - nevermind\n", nso);
lck_mtx_unlock(&nso->nso_lock);
return;
}
- NFS_SOCK_DBG(("nfs connect - socket %p upcall\n", nso));
+ NFS_SOCK_DBG("nfs connect - socket %p upcall\n", nso);
nso->nso_flags |= NSO_UPCALL;
/* loop while we make error-free progress */
sinaddr = &((struct sockaddr_in6*)sa)->sin6_addr;
if (inet_ntop(sa->sa_family, sinaddr, naddr, sizeof(naddr)) != naddr)
strlcpy(naddr, "<unknown>", sizeof(naddr));
+#else
+ char naddr[1] = { 0 };
#endif
*nsop = NULL;
}
if (error) {
- NFS_SOCK_DBG(("nfs connect %s error %d creating socket %p %s type %d%s port %d prot %d %d\n",
+ NFS_SOCK_DBG("nfs connect %s error %d creating socket %p %s type %d%s port %d prot %d %d\n",
vfs_statfs(nmp->nm_mountp)->f_mntfromname, error, nso, naddr, sotype,
- resvport ? "r" : "", port, protocol, vers));
+ resvport ? "r" : "", port, protocol, vers);
nfs_socket_destroy(nso);
} else {
- NFS_SOCK_DBG(("nfs connect %s created socket %p %s type %d%s port %d prot %d %d\n",
+ NFS_SOCK_DBG("nfs connect %s created socket %p %s type %d%s port %d prot %d %d\n",
vfs_statfs(nmp->nm_mountp)->f_mntfromname, nso, naddr,
- sotype, resvport ? "r" : "", port, protocol, vers));
+ sotype, resvport ? "r" : "", port, protocol, vers);
*nsop = nso;
}
return (error);
FREE(nso->nso_saddr, M_SONAME);
if (nso->nso_saddr2)
FREE(nso->nso_saddr2, M_SONAME);
- NFS_SOCK_DBG(("nfs connect - socket %p destroyed\n", nso));
+ NFS_SOCK_DBG("nfs connect - socket %p destroyed\n", nso);
FREE(nso, M_TEMP);
}
int on = 1, proto;
timeo.tv_usec = 0;
- timeo.tv_sec = NMFLAG(nmp, SOFT) ? 5 : 60;
+ timeo.tv_sec = (NMFLAG(nmp, SOFT) || nfs_can_squish(nmp)) ? 5 : 60;
sock_setsockopt(nso->nso_so, SOL_SOCKET, SO_RCVTIMEO, &timeo, sizeof(timeo));
sock_setsockopt(nso->nso_so, SOL_SOCKET, SO_SNDTIMEO, &timeo, sizeof(timeo));
if (nso->nso_sotype == SOCK_STREAM) {
nss->nss_error = error;
}
-/*
- * Continue the socket search until we have something to report.
+/* nfs_connect_search_new_socket:
+ * Given a socket search structure for an nfs mount try to find a new socket from the set of addresses specified
+ * by nss.
+ *
+ * nss_last is set to -1 at initialization to indicate the first time. Its set to -2 if address was found but
+ * could not be used or if a socket timed out.
*/
int
-nfs_connect_search_loop(struct nfsmount *nmp, struct nfs_socket_search *nss)
+nfs_connect_search_new_socket(struct nfsmount *nmp, struct nfs_socket_search *nss, struct timeval *now)
{
- struct nfs_socket *nso, *nsonext;
- struct timeval now;
struct nfs_fs_location *fsl;
struct nfs_fs_server *fss;
struct sockaddr_storage ss;
+ struct nfs_socket *nso;
char *addrstr;
- int error, nomore = 0;
+ int error = 0;
+
-loop:
- microuptime(&now);
- NFS_SOCK_DBG(("nfs connect %s search %ld\n", vfs_statfs(nmp->nm_mountp)->f_mntfromname, now.tv_sec));
+ NFS_SOCK_DBG("nfs connect %s nss_addrcnt = %d\n",
+ vfs_statfs(nmp->nm_mountp)->f_mntfromname, nss->nss_addrcnt);
- /* Time to start another socket? */
- while ((nss->nss_last < 0) || (nss->nss_sockcnt == 0) ||
- ((nss->nss_sockcnt < 4) && (now.tv_sec >= (nss->nss_last + 2)))) {
+ /*
+ * while there are addresses and:
+ * we have no sockets or
+ * the last address failed and did not produce a socket (nss_last < 0) or
+ * Its been a while (2 seconds) and we have less than the max number of concurrent sockets to search (4)
+ * then attempt to create a socket with the current address.
+ */
+ while (nss->nss_addrcnt > 0 && ((nss->nss_last < 0) || (nss->nss_sockcnt == 0) ||
+ ((nss->nss_sockcnt < 4) && (now->tv_sec >= (nss->nss_last + 2))))) {
if (nmp->nm_sockflags & NMSOCK_UNMOUNT)
return (EINTR);
- /* Find the next address to try... */
- /* Have we run out of locations? */
- if (!nomore && (nss->nss_last != -1) && !nfs_location_index_cmp(&nss->nss_nextloc, &nss->nss_startloc))
- nomore = 1;
- if (nomore) {
- if (nss->nss_last < 0)
- nss->nss_last = now.tv_sec;
- break;
- }
/* Can we convert the address to a sockaddr? */
fsl = nmp->nm_locations.nl_locations[nss->nss_nextloc.nli_loc];
fss = fsl->nl_servers[nss->nss_nextloc.nli_serv];
addrstr = fss->ns_addresses[nss->nss_nextloc.nli_addr];
if (!nfs_uaddr2sockaddr(addrstr, (struct sockaddr*)&ss)) {
nfs_location_next(&nmp->nm_locations, &nss->nss_nextloc);
+ nss->nss_addrcnt -= 1;
nss->nss_last = -2;
continue;
}
/* Check that socket family is acceptable. */
if (nmp->nm_sofamily && (ss.ss_family != nmp->nm_sofamily)) {
nfs_location_next(&nmp->nm_locations, &nss->nss_nextloc);
+ nss->nss_addrcnt -= 1;
nss->nss_last = -2;
continue;
}
TAILQ_INSERT_TAIL(&nss->nss_socklist, nso, nso_link);
nss->nss_sockcnt++;
nfs_location_next(&nmp->nm_locations, &nss->nss_nextloc);
-
- nss->nss_last = now.tv_sec;
+ nss->nss_addrcnt -= 1;
+
+ nss->nss_last = now->tv_sec;
}
- /* check each active socket and try to push it along */
- TAILQ_FOREACH(nso, &nss->nss_socklist, nso_link) {
+ if (nss->nss_addrcnt == 0 && nss->nss_last < 0)
+ nss->nss_last = now->tv_sec;
+
+ return (error);
+}
+
+/*
+ * nfs_connect_search_socket_connect: Connect an nfs socket nso for nfsmount nmp.
+ * If successful set the socket options for the socket as require from the mount.
+ *
+ * Assumes: nso->nso_lock is held on entry and return.
+ */
+int
+nfs_connect_search_socket_connect(struct nfsmount *nmp, struct nfs_socket *nso, int verbose)
+{
+ int error;
+
+ if ((nso->nso_sotype != SOCK_STREAM) && NMFLAG(nmp, NOCONNECT)) {
+ /* no connection needed, just say it's already connected */
+ NFS_SOCK_DBG("nfs connect %s UDP socket %p noconnect\n",
+ vfs_statfs(nmp->nm_mountp)->f_mntfromname, nso);
+ nso->nso_flags |= NSO_CONNECTED;
+ nfs_socket_options(nmp, nso);
+ return (1); /* Socket is connected and setup */
+ } else if (!(nso->nso_flags & NSO_CONNECTING)) {
+ /* initiate the connection */
+ nso->nso_flags |= NSO_CONNECTING;
+ lck_mtx_unlock(&nso->nso_lock);
+ NFS_SOCK_DBG("nfs connect %s connecting socket %p\n",
+ vfs_statfs(nmp->nm_mountp)->f_mntfromname, nso);
+ error = sock_connect(nso->nso_so, nso->nso_saddr, MSG_DONTWAIT);
lck_mtx_lock(&nso->nso_lock);
- if (!(nso->nso_flags & NSO_CONNECTED)) {
- if ((nso->nso_sotype != SOCK_STREAM) && NMFLAG(nmp, NOCONNECT)) {
- /* no connection needed, just say it's already connected */
- nso->nso_flags |= NSO_CONNECTED;
- NFS_SOCK_DBG(("nfs connect %s UDP socket %p noconnect\n",
- vfs_statfs(nmp->nm_mountp)->f_mntfromname, nso));
- } else if (!(nso->nso_flags & NSO_CONNECTING)) {
- /* initiate the connection */
- nso->nso_flags |= NSO_CONNECTING;
- lck_mtx_unlock(&nso->nso_lock);
- NFS_SOCK_DBG(("nfs connect %s connecting socket %p\n",
- vfs_statfs(nmp->nm_mountp)->f_mntfromname, nso));
- error = sock_connect(nso->nso_so, nso->nso_saddr, MSG_DONTWAIT);
- lck_mtx_lock(&nso->nso_lock);
- if (error && (error != EINPROGRESS)) {
- nso->nso_error = error;
- nso->nso_flags |= NSO_DEAD;
- lck_mtx_unlock(&nso->nso_lock);
- continue;
- }
- }
- if (nso->nso_flags & NSO_CONNECTING) {
- /* check the connection */
- if (sock_isconnected(nso->nso_so)) {
- NFS_SOCK_DBG(("nfs connect %s socket %p is connected\n",
- vfs_statfs(nmp->nm_mountp)->f_mntfromname, nso));
- nso->nso_flags &= ~NSO_CONNECTING;
- nso->nso_flags |= NSO_CONNECTED;
- } else {
- int optlen = sizeof(error);
- error = 0;
- sock_getsockopt(nso->nso_so, SOL_SOCKET, SO_ERROR, &error, &optlen);
- if (error) { /* we got an error on the socket */
- NFS_SOCK_DBG(("nfs connect %s socket %p connection error %d\n",
- vfs_statfs(nmp->nm_mountp)->f_mntfromname, nso, error));
- if (nss->nss_flags & NSS_VERBOSE)
- log(LOG_INFO, "nfs_connect: socket error %d for %s\n",
- error, vfs_statfs(nmp->nm_mountp)->f_mntfromname);
- nso->nso_error = error;
- nso->nso_flags |= NSO_DEAD;
- lck_mtx_unlock(&nso->nso_lock);
- continue;
- }
- }
- }
- if (nso->nso_flags & NSO_CONNECTED)
- nfs_socket_options(nmp, nso);
- }
- if (!(nso->nso_flags & NSO_CONNECTED)) {
- lck_mtx_unlock(&nso->nso_lock);
- continue;
+ if (error && (error != EINPROGRESS)) {
+ nso->nso_error = error;
+ nso->nso_flags |= NSO_DEAD;
+ return (0);
}
- if (!(nso->nso_flags & (NSO_PINGING|NSO_VERIFIED)) ||
- ((nso->nso_sotype == SOCK_DGRAM) && (now.tv_sec >= nso->nso_reqtimestamp+2))) {
- /* initiate a NULL RPC request */
- uint64_t xid = nso->nso_pingxid;
- mbuf_t m, mreq = NULL;
- struct msghdr msg;
- size_t reqlen, sentlen;
- uint32_t vers;
-
- if (!(vers = nso->nso_version)) {
- 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;
- }
- lck_mtx_unlock(&nso->nso_lock);
- error = nfsm_rpchead2(nmp, nso->nso_sotype, nso->nso_protocol, vers, 0, RPCAUTH_SYS,
- vfs_context_ucred(vfs_context_kernel()), NULL, NULL, &xid, &mreq);
- lck_mtx_lock(&nso->nso_lock);
- if (!error) {
- nso->nso_flags |= NSO_PINGING;
- nso->nso_pingxid = R_XID32(xid);
- nso->nso_reqtimestamp = now.tv_sec;
- bzero(&msg, sizeof(msg));
- if ((nso->nso_sotype != SOCK_STREAM) && !sock_isconnected(nso->nso_so)) {
- msg.msg_name = nso->nso_saddr;
- msg.msg_namelen = nso->nso_saddr->sa_len;
- }
- for (reqlen=0, m=mreq; m; m = mbuf_next(m))
- reqlen += mbuf_len(m);
- lck_mtx_unlock(&nso->nso_lock);
- error = sock_sendmbuf(nso->nso_so, &msg, mreq, 0, &sentlen);
- NFS_SOCK_DBG(("nfs connect %s verifying socket %p send rv %d\n",
- vfs_statfs(nmp->nm_mountp)->f_mntfromname, nso, error));
- lck_mtx_lock(&nso->nso_lock);
- if (!error && (sentlen != reqlen))
- error = ETIMEDOUT;
- }
- if (error) {
+ }
+ if (nso->nso_flags & NSO_CONNECTING) {
+ /* check the connection */
+ if (sock_isconnected(nso->nso_so)) {
+ NFS_SOCK_DBG("nfs connect %s socket %p is connected\n",
+ vfs_statfs(nmp->nm_mountp)->f_mntfromname, nso);
+ nso->nso_flags &= ~NSO_CONNECTING;
+ nso->nso_flags |= NSO_CONNECTED;
+ nfs_socket_options(nmp, nso);
+ return (1); /* Socket is connected and setup */
+ } else {
+ int optlen = sizeof(error);
+ error = 0;
+ sock_getsockopt(nso->nso_so, SOL_SOCKET, SO_ERROR, &error, &optlen);
+ if (error) { /* we got an error on the socket */
+ NFS_SOCK_DBG("nfs connect %s socket %p connection error %d\n",
+ vfs_statfs(nmp->nm_mountp)->f_mntfromname, nso, error);
+ if (verbose)
+ printf("nfs connect socket error %d for %s\n",
+ error, vfs_statfs(nmp->nm_mountp)->f_mntfromname);
nso->nso_error = error;
nso->nso_flags |= NSO_DEAD;
- lck_mtx_unlock(&nso->nso_lock);
- continue;
+ return (0);
}
}
- if (nso->nso_flags & NSO_VERIFIED) {
- /* WOOHOO!! This socket looks good! */
- NFS_SOCK_DBG(("nfs connect %s socket %p verified\n",
- vfs_statfs(nmp->nm_mountp)->f_mntfromname, nso));
- if (!nso->nso_version) {
- /* If the version isn't set, the default must have worked. */
- 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;
- }
- lck_mtx_unlock(&nso->nso_lock);
- TAILQ_REMOVE(&nss->nss_socklist, nso, nso_link);
- nss->nss_sockcnt--;
- nss->nss_sock = nso;
- break;
+ }
+
+ return (0); /* Waiting to be connected */
+}
+
+/*
+ * nfs_connect_search_ping: Send a null proc on the nso socket.
+ */
+int
+nfs_connect_search_ping(struct nfsmount *nmp, struct nfs_socket *nso, struct timeval *now)
+{
+ /* initiate a NULL RPC request */
+ uint64_t xid = nso->nso_pingxid;
+ mbuf_t m, mreq = NULL;
+ struct msghdr msg;
+ size_t reqlen, sentlen;
+ uint32_t vers = nso->nso_version;
+ int error;
+
+ if (!vers) {
+ 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;
+ }
+ lck_mtx_unlock(&nso->nso_lock);
+ error = nfsm_rpchead2(nmp, nso->nso_sotype, nso->nso_protocol, vers, 0, RPCAUTH_SYS,
+ vfs_context_ucred(vfs_context_kernel()), NULL, NULL, &xid, &mreq);
+ lck_mtx_lock(&nso->nso_lock);
+ if (!error) {
+ nso->nso_flags |= NSO_PINGING;
+ nso->nso_pingxid = R_XID32(xid);
+ nso->nso_reqtimestamp = now->tv_sec;
+ bzero(&msg, sizeof(msg));
+ if ((nso->nso_sotype != SOCK_STREAM) && !sock_isconnected(nso->nso_so)) {
+ msg.msg_name = nso->nso_saddr;
+ msg.msg_namelen = nso->nso_saddr->sa_len;
}
+ for (reqlen=0, m=mreq; m; m = mbuf_next(m))
+ reqlen += mbuf_len(m);
lck_mtx_unlock(&nso->nso_lock);
+ error = sock_sendmbuf(nso->nso_so, &msg, mreq, 0, &sentlen);
+ NFS_SOCK_DBG("nfs connect %s verifying socket %p send rv %d\n",
+ vfs_statfs(nmp->nm_mountp)->f_mntfromname, nso, error);
+ lck_mtx_lock(&nso->nso_lock);
+ if (!error && (sentlen != reqlen))
+ error = ETIMEDOUT;
}
+ if (error) {
+ nso->nso_error = error;
+ nso->nso_flags |= NSO_DEAD;
+ return (0);
+ }
+
+ return (1);
+}
+
+/*
+ * nfs_connect_search_socket_found: Take the found socket of the socket search list and assign it to the searched socket.
+ * 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_SOCK_DBG("nfs connect %s socket %p verified\n",
+ vfs_statfs(nmp->nm_mountp)->f_mntfromname, nso);
+ if (!nso->nso_version) {
+ /* If the version isn't set, the default must have worked. */
+ 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;
+ }
+ TAILQ_REMOVE(&nss->nss_socklist, nso, nso_link);
+ nss->nss_sockcnt--;
+ nss->nss_sock = nso;
+}
+/*
+ * nfs_connect_search_socket_reap: For each socket in the search list mark any timed out socket as dead and remove from
+ * the list. Dead socket are then destroyed.
+ */
+void
+nfs_connect_search_socket_reap(struct nfsmount *nmp __unused, struct nfs_socket_search *nss, struct timeval *now)
+{
+ struct nfs_socket *nso, *nsonext;
+
TAILQ_FOREACH_SAFE(nso, &nss->nss_socklist, nso_link, nsonext) {
lck_mtx_lock(&nso->nso_lock);
- if (now.tv_sec >= (nso->nso_timestamp + nss->nss_timeo)) {
+ if (now->tv_sec >= (nso->nso_timestamp + nss->nss_timeo)) {
/* took too long */
- NFS_SOCK_DBG(("nfs connect %s socket %p timed out\n",
- vfs_statfs(nmp->nm_mountp)->f_mntfromname, nso));
+ NFS_SOCK_DBG("nfs connect %s socket %p timed out\n",
+ vfs_statfs(nmp->nm_mountp)->f_mntfromname, nso);
nso->nso_error = ETIMEDOUT;
nso->nso_flags |= NSO_DEAD;
}
continue;
}
lck_mtx_unlock(&nso->nso_lock);
- NFS_SOCK_DBG(("nfs connect %s reaping socket %p %d\n",
- vfs_statfs(nmp->nm_mountp)->f_mntfromname, nso, nso->nso_error));
+ NFS_SOCK_DBG("nfs connect %s reaping socket %p %d\n",
+ vfs_statfs(nmp->nm_mountp)->f_mntfromname, nso, nso->nso_error);
nfs_socket_search_update_error(nss, nso->nso_error);
TAILQ_REMOVE(&nss->nss_socklist, nso, nso_link);
nss->nss_sockcnt--;
nfs_socket_destroy(nso);
- if (!nomore)
+ /* If there are more sockets to try, force the starting of another socket */
+ if (nss->nss_addrcnt > 0)
nss->nss_last = -2;
}
+}
+
+/*
+ * nfs_connect_search_check: Check on the status of search and wait for replies if needed.
+ */
+int
+nfs_connect_search_check(struct nfsmount *nmp, struct nfs_socket_search *nss, struct timeval *now)
+{
+ int error;
+
+ /* log a warning if connect is taking a while */
+ if (((now->tv_sec - nss->nss_timestamp) >= 8) && ((nss->nss_flags & (NSS_VERBOSE|NSS_WARNED)) == NSS_VERBOSE)) {
+ printf("nfs_connect: socket connect taking a while for %s\n", vfs_statfs(nmp->nm_mountp)->f_mntfromname);
+ nss->nss_flags |= NSS_WARNED;
+ }
+ if (nmp->nm_sockflags & NMSOCK_UNMOUNT)
+ return (EINTR);
+ if ((error = nfs_sigintr(nmp, NULL, current_thread(), 0)))
+ return (error);
+
+ /* If we were succesfull at sending a ping, wait up to a second for a reply */
+ if (nss->nss_last >= 0)
+ tsleep(nss, PSOCK, "nfs_connect_search_wait", hz);
+
+ return (0);
+}
+
+
+/*
+ * Continue the socket search until we have something to report.
+ */
+int
+nfs_connect_search_loop(struct nfsmount *nmp, struct nfs_socket_search *nss)
+{
+ struct nfs_socket *nso;
+ struct timeval now;
+ int error;
+ int verbose = (nss->nss_flags & NSS_VERBOSE);
+
+loop:
+ microuptime(&now);
+ NFS_SOCK_DBG("nfs connect %s search %ld\n", vfs_statfs(nmp->nm_mountp)->f_mntfromname, now.tv_sec);
+
+ /* add a new socket to the socket list if needed and available */
+ error = nfs_connect_search_new_socket(nmp, nss, &now);
+ if (error) {
+ NFS_SOCK_DBG("nfs connect returned %d\n", error);
+ return (error);
+ }
+
+ /* check each active socket on the list and try to push it along */
+ TAILQ_FOREACH(nso, &nss->nss_socklist, nso_link) {
+ lck_mtx_lock(&nso->nso_lock);
+
+ /* If not connected connect it */
+ if (!(nso->nso_flags & NSO_CONNECTED)) {
+ if (!nfs_connect_search_socket_connect(nmp, nso, verbose)) {
+ lck_mtx_unlock(&nso->nso_lock);
+ continue;
+ }
+ }
+ /* If the socket hasn't been verified or in a ping, ping it. We also handle UDP retransmits */
+ if (!(nso->nso_flags & (NSO_PINGING|NSO_VERIFIED)) ||
+ ((nso->nso_sotype == SOCK_DGRAM) && (now.tv_sec >= nso->nso_reqtimestamp+2))) {
+ if (!nfs_connect_search_ping(nmp, nso, &now)) {
+ lck_mtx_unlock(&nso->nso_lock);
+ continue;
+ }
+ }
+
+ /* Has the socket been verified by the up call routine? */
+ if (nso->nso_flags & NSO_VERIFIED) {
+ /* WOOHOO!! This socket looks good! */
+ nfs_connect_search_socket_found(nmp, nss, nso);
+ lck_mtx_unlock(&nso->nso_lock);
+ break;
+ }
+ lck_mtx_unlock(&nso->nso_lock);
+ }
+
+ /* Check for timed out sockets and mark as dead and then remove all dead sockets. */
+ nfs_connect_search_socket_reap(nmp, nss, &now);
+
/*
* Keep looping if we haven't found a socket yet and we have more
* sockets to (continue to) try.
*/
error = 0;
- if (!nss->nss_sock && (!TAILQ_EMPTY(&nss->nss_socklist) || !nomore)) {
- /* log a warning if connect is taking a while */
- if (((now.tv_sec - nss->nss_timestamp) >= 30) && ((nss->nss_flags & (NSS_VERBOSE|NSS_WARNED)) == NSS_VERBOSE)) {
- log(LOG_INFO, "nfs_connect: socket connect taking a while for %s\n",
- vfs_statfs(nmp->nm_mountp)->f_mntfromname);
- nss->nss_flags |= NSS_WARNED;
- }
- if (nmp->nm_sockflags & NMSOCK_UNMOUNT)
- return (EINTR);
- if ((error = nfs_sigintr(nmp, NULL, current_thread(), 0)))
- return (error);
- if (nss->nss_last >= 0)
- tsleep(nss, PSOCK, "nfs_connect_search_wait", hz);
- goto loop;
+ if (!nss->nss_sock && (!TAILQ_EMPTY(&nss->nss_socklist) || nss->nss_addrcnt)) {
+ error = nfs_connect_search_check(nmp, nss, &now);
+ if (!error)
+ goto loop;
}
- NFS_SOCK_DBG(("nfs connect %s returning %d\n", vfs_statfs(nmp->nm_mountp)->f_mntfromname, error));
+ NFS_SOCK_DBG("nfs connect %s returning %d\n", vfs_statfs(nmp->nm_mountp)->f_mntfromname, error);
return (error);
}
fhandle_t *fh = NULL;
char *path = NULL;
in_port_t port;
-
+ int addrtotal = 0;
+
/* paranoia... check that we have at least one address in the locations */
uint32_t loc, serv;
for (loc=0; loc < nmp->nm_locations.nl_numlocs; loc++) {
for (serv=0; serv < nmp->nm_locations.nl_locations[loc]->nl_servcount; serv++) {
- if (nmp->nm_locations.nl_locations[loc]->nl_servers[serv]->ns_addrcount)
- break;
- NFS_SOCK_DBG(("nfs connect %s search, server %s has no addresses\n",
- vfs_statfs(nmp->nm_mountp)->f_mntfromname,
- nmp->nm_locations.nl_locations[loc]->nl_servers[serv]->ns_name));
+ addrtotal += nmp->nm_locations.nl_locations[loc]->nl_servers[serv]->ns_addrcount;
+ if (nmp->nm_locations.nl_locations[loc]->nl_servers[serv]->ns_addrcount == 0)
+ NFS_SOCK_DBG("nfs connect %s search, server %s has no addresses\n",
+ vfs_statfs(nmp->nm_mountp)->f_mntfromname,
+ nmp->nm_locations.nl_locations[loc]->nl_servers[serv]->ns_name);
}
- if (serv < nmp->nm_locations.nl_locations[loc]->nl_servcount)
- break;
}
- if (loc >= nmp->nm_locations.nl_numlocs) {
- NFS_SOCK_DBG(("nfs connect %s search failed, no addresses\n",
- vfs_statfs(nmp->nm_mountp)->f_mntfromname));
+
+ if (addrtotal == 0) {
+ NFS_SOCK_DBG("nfs connect %s search failed, no addresses\n",
+ vfs_statfs(nmp->nm_mountp)->f_mntfromname);
return (EINVAL);
- }
+ } else
+ NFS_SOCK_DBG("nfs connect %s has %d addresses\n",
+ vfs_statfs(nmp->nm_mountp)->f_mntfromname, addrtotal);
lck_mtx_lock(&nmp->nm_lock);
nmp->nm_sockflags |= NMSOCK_CONNECTING;
tryagain:
/* initialize socket search state */
bzero(&nss, sizeof(nss));
+ nss.nss_addrcnt = addrtotal;
nss.nss_error = savederror;
TAILQ_INIT(&nss.nss_socklist);
nss.nss_sotype = sotype;
nss.nss_version = nmp->nm_vers;
}
}
- NFS_SOCK_DBG(("nfs connect first %s, so type %d port %d prot %d %d\n",
+ NFS_SOCK_DBG("nfs connect first %s, so type %d port %d prot %d %d\n",
vfs_statfs(nmp->nm_mountp)->f_mntfromname, nss.nss_sotype, nss.nss_port,
- nss.nss_protocol, nss.nss_version));
+ nss.nss_protocol, nss.nss_version);
} else {
/* we've connected before, just connect to NFS port */
if (!nmp->nm_nfsport) {
nss.nss_protocol = NFS_PROG;
nss.nss_version = nmp->nm_vers;
}
- NFS_SOCK_DBG(("nfs connect %s, so type %d port %d prot %d %d\n",
+ NFS_SOCK_DBG("nfs connect %s, so type %d port %d prot %d %d\n",
vfs_statfs(nmp->nm_mountp)->f_mntfromname, nss.nss_sotype, nss.nss_port,
- nss.nss_protocol, nss.nss_version));
+ nss.nss_protocol, nss.nss_version);
}
/* Set next location to first valid location. */
(nss.nss_nextloc.nli_addr >= nmp->nm_locations.nl_locations[nss.nss_nextloc.nli_loc]->nl_servers[nss.nss_nextloc.nli_serv]->ns_addrcount)) {
nfs_location_next(&nmp->nm_locations, &nss.nss_nextloc);
if (!nfs_location_index_cmp(&nss.nss_nextloc, &nss.nss_startloc)) {
- NFS_SOCK_DBG(("nfs connect %s search failed, couldn't find a valid location index\n",
- vfs_statfs(nmp->nm_mountp)->f_mntfromname));
+ NFS_SOCK_DBG("nfs connect %s search failed, couldn't find a valid location index\n",
+ vfs_statfs(nmp->nm_mountp)->f_mntfromname);
return (ENOENT);
}
}
/* Try using UDP */
sotype = SOCK_DGRAM;
savederror = nss.nss_error;
- NFS_SOCK_DBG(("nfs connect %s TCP failed %d %d, trying UDP\n",
- vfs_statfs(nmp->nm_mountp)->f_mntfromname, error, nss.nss_error));
+ NFS_SOCK_DBG("nfs connect %s TCP failed %d %d, trying UDP\n",
+ vfs_statfs(nmp->nm_mountp)->f_mntfromname, error, nss.nss_error);
goto tryagain;
}
if (!error)
FREE(fh, M_TEMP);
if (path)
FREE_ZONE(path, MAXPATHLEN, M_NAMEI);
- NFS_SOCK_DBG(("nfs connect %s search failed, returning %d\n",
- vfs_statfs(nmp->nm_mountp)->f_mntfromname, error));
+ NFS_SOCK_DBG("nfs connect %s search failed, returning %d\n",
+ vfs_statfs(nmp->nm_mountp)->f_mntfromname, error);
return (error);
}
port = ntohs(((struct sockaddr_in6*)nso->nso_saddr)->sin6_port);
if (port == PMAPPORT) {
/* Use this portmapper port to get the port #s we need. */
- NFS_SOCK_DBG(("nfs connect %s got portmapper socket %p\n",
- vfs_statfs(nmp->nm_mountp)->f_mntfromname, nso));
+ NFS_SOCK_DBG("nfs connect %s got portmapper socket %p\n",
+ vfs_statfs(nmp->nm_mountp)->f_mntfromname, nso);
/* remove the connect upcall so nfs_portmap_lookup() can use this socket */
sock_setupcall(nso->nso_so, NULL, NULL);
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_so, NFS_PROG, nfsvers,
(nso->nso_sotype == SOCK_DGRAM) ? IPPROTO_UDP : IPPROTO_TCP, timeo);
if (!error) {
if (ss.ss_family == AF_INET)
if (error && !nmp->nm_vers) {
nfsvers = NFS_VER2;
error = nfs_portmap_lookup(nmp, vfs_context_current(), (struct sockaddr*)&ss,
- nso->nso_so, NFS_PROG, nfsvers,
+ nso->nso_so, NFS_PROG, nfsvers,
(nso->nso_sotype == SOCK_DGRAM) ? IPPROTO_UDP : IPPROTO_TCP, timeo);
if (!error) {
if (ss.ss_family == AF_INET)
}
/* nso is an NFS socket */
- NFS_SOCK_DBG(("nfs connect %s got NFS socket %p\n", vfs_statfs(nmp->nm_mountp)->f_mntfromname, nso));
+ NFS_SOCK_DBG("nfs connect %s got NFS socket %p\n", vfs_statfs(nmp->nm_mountp)->f_mntfromname, nso);
/* If NFS version wasn't specified, it was determined during the connect. */
nfsvers = nmp->nm_vers ? nmp->nm_vers : (int)nso->nso_version;
if (saddr)
MALLOC(fh, fhandle_t *, sizeof(fhandle_t), M_TEMP, M_WAITOK|M_ZERO);
if (saddr && fh)
- MALLOC_ZONE(path, char *, MAXPATHLEN, M_NAMEI, M_WAITOK);
+ MALLOC_ZONE(path, char *, MAXPATHLEN, M_NAMEI, M_WAITOK);
if (!saddr || !fh || !path) {
if (!error)
error = ENOMEM;
nfs_location_mntfromname(&nmp->nm_locations, nso->nso_location, path, MAXPATHLEN, 1);
error = nfs3_mount_rpc(nmp, saddr, nso->nso_sotype, nfsvers,
path, vfs_context_current(), timeo, fh, &nmp->nm_servsec);
- NFS_SOCK_DBG(("nfs connect %s socket %p mount %d\n",
- vfs_statfs(nmp->nm_mountp)->f_mntfromname, nso, error));
+ NFS_SOCK_DBG("nfs connect %s socket %p mount %d\n",
+ vfs_statfs(nmp->nm_mountp)->f_mntfromname, nso, error);
if (!error) {
/* Make sure we can agree on a security flavor. */
int o, s; /* indices into mount option and server security flavor lists */
wakeup(&nmp->nm_sockflags);
}
if (error) {
- NFS_SOCK_DBG(("nfs connect %s socket %p setup failed %d\n",
- vfs_statfs(nmp->nm_mountp)->f_mntfromname, nso, error));
+ NFS_SOCK_DBG("nfs connect %s socket %p setup failed %d\n",
+ vfs_statfs(nmp->nm_mountp)->f_mntfromname, nso, error);
nfs_socket_search_update_error(&nss, error);
nmp->nm_saddr = oldsaddr;
if (!(nmp->nm_sockflags & NMSOCK_HASCONNECTED)) {
FREE(fh, M_TEMP);
if (path)
FREE_ZONE(path, MAXPATHLEN, M_NAMEI);
- NFS_SOCK_DBG(("nfs connect %s success\n", vfs_statfs(nmp->nm_mountp)->f_mntfromname));
+ NFS_SOCK_DBG("nfs connect %s success\n", vfs_statfs(nmp->nm_mountp)->f_mntfromname);
return (0);
}
thread_t thd = current_thread();
int error, wentdown = 0, verbose = 1;
time_t lastmsg;
+ int timeo;
microuptime(&now);
lastmsg = now.tv_sec - (nmp->nm_tprintf_delay - nmp->nm_tprintf_initial_delay);
nfs_disconnect(nmp);
- while ((error = nfs_connect(nmp, verbose, 30))) {
+
+ lck_mtx_lock(&nmp->nm_lock);
+ timeo = nfs_is_squishy(nmp) ? 8 : 30;
+ lck_mtx_unlock(&nmp->nm_lock);
+
+ while ((error = nfs_connect(nmp, verbose, timeo))) {
verbose = 0;
nfs_disconnect(nmp);
if ((error == EINTR) || (error == ERESTART))
/* we're not yet completely mounted and */
/* we can't reconnect, so we fail */
lck_mtx_unlock(&nmp->nm_lock);
+ NFS_SOCK_DBG("Not mounted returning %d\n", error);
return (error);
}
nfs_mount_check_dead_timeout(nmp);
return (error);
}
lck_mtx_unlock(&nmp->nm_lock);
- tsleep(&lbolt, PSOCK, "nfs_reconnect_delay", 0);
+ tsleep(nfs_reconnect, PSOCK, "nfs_reconnect_delay", 2*hz);
if ((error = nfs_sigintr(nmp, NULL, thd, 0)))
return (error);
}
struct timeval now;
int error, dofinish;
nfsnode_t np;
+ int do_reconnect_sleep = 0;
lck_mtx_lock(&nmp->nm_lock);
nmp->nm_reconnect_start = now.tv_sec;
}
lck_mtx_unlock(&nmp->nm_lock);
- NFS_SOCK_DBG(("nfs reconnect %s\n", vfs_statfs(nmp->nm_mountp)->f_mntfromname));
- if (nfs_reconnect(nmp) == 0)
+ NFS_SOCK_DBG("nfs reconnect %s\n", vfs_statfs(nmp->nm_mountp)->f_mntfromname);
+ /*
+ * XXX We don't want to call reconnect again right away if returned errors
+ * before that may not have blocked. This has caused spamming null procs
+ * from machines in the pass.
+ */
+ if (do_reconnect_sleep)
+ tsleep(nfs_mount_sock_thread, PSOCK, "nfs_reconnect_sock_thread_delay", hz);
+ error = nfs_reconnect(nmp);
+ if (error) {
+ int lvl = 7;
+ if (error == EIO || error == EINTR) {
+ lvl = (do_reconnect_sleep++ % 600) ? 7 : 0;
+ }
+ nfs_printf(NFS_FAC_SOCK, lvl, "nfs reconnect %s: returned %d\n",
+ vfs_statfs(nmp->nm_mountp)->f_mntfromname, error);
+ } else {
nmp->nm_reconnect_start = 0;
+ do_reconnect_sleep = 0;
+ }
lck_mtx_lock(&nmp->nm_lock);
}
if ((nmp->nm_sockflags & NMSOCK_READY) &&
if (error == ENEEDAUTH)
req->r_xid = 0;
}
- NFS_SOCK_DBG(("nfs async%s restart: p %d x 0x%llx f 0x%x rtt %d\n",
+ 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));
+ 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 = 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));
+ 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);
{
struct timeval now;
- if (nmp->nm_deadtimeout <= 0)
- return;
if (nmp->nm_deadto_start == 0)
return;
if (nmp->nm_state & NFSSTA_DEAD)
return;
+ nfs_is_squishy(nmp);
+ if (nmp->nm_curdeadtimeout <= 0)
+ return;
microuptime(&now);
- if ((now.tv_sec - nmp->nm_deadto_start) < nmp->nm_deadtimeout)
+ if ((now.tv_sec - nmp->nm_deadto_start) < nmp->nm_curdeadtimeout)
return;
- printf("nfs server %s: dead\n", vfs_statfs(nmp->nm_mountp)->f_mntfromname);
+ 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);
}
status = error;
else if ((error == ENOBUFS) || (error == ENOMEM))
status = NFSERR_RESOURCE;
- else
+ else
status = NFSERR_SERVERFAULT;
error = 0;
nfsm_chain_null(&nmrep);
status = error;
else if ((error == ENOBUFS) || (error == ENOMEM))
status = NFSERR_RESOURCE;
- else
+ else
status = NFSERR_SERVERFAULT;
error = 0;
}
nfsmout:
if (status == EBADRPC)
- OSAddAtomic(1, &nfsstats.rpcinvalid);
+ OSAddAtomic64(1, &nfsstats.rpcinvalid);
/* build reply header */
error = mbuf_gethdr(MBUF_WAITOK, MBUF_TYPE_DATA, &mhead);
lck_mtx_unlock(&req->r_mtx);
return (0);
}
- NFS_SOCK_DBG(("nfs_send: 0x%llx wait reconnect\n", req->r_xid));
+ NFS_SOCK_DBG("nfs_send: 0x%llx wait reconnect\n", req->r_xid);
lck_mtx_lock(&req->r_mtx);
req->r_flags &= ~R_MUSTRESEND;
req->r_rtt = 0;
microuptime(&now);
if ((now.tv_sec - nmp->nm_reconnect_start) >= 8) {
/* soft mount in reconnect for a while... terminate ASAP */
- OSAddAtomic(1, &nfsstats.rpctimeouts);
+ OSAddAtomic64(1, &nfsstats.rpctimeouts);
req->r_flags |= R_SOFTTERM;
req->r_error = error = ETIMEDOUT;
break;
} else {
/*
* When retransmitting, turn timing off
- * and divide congestion window by 2.
+ * and divide congestion window by 2.
*/
req->r_flags &= ~R_TIMING;
nmp->nm_cwnd >>= 1;
error = sock_sendmbuf(nso->nso_so, &msg, mreqcopy, 0, &sentlen);
#ifdef NFS_SOCKET_DEBUGGING
if (error || (sentlen != req->r_mreqlen))
- NFS_SOCK_DBG(("nfs_send: 0x%llx sent %d/%d error %d\n",
- req->r_xid, (int)sentlen, (int)req->r_mreqlen, error));
+ NFS_SOCK_DBG("nfs_send: 0x%llx sent %d/%d error %d\n",
+ req->r_xid, (int)sentlen, (int)req->r_mreqlen, error);
#endif
if (!error && (sentlen != req->r_mreqlen))
error = EWOULDBLOCK;
/* SUCCESS */
req->r_flags &= ~R_RESENDERR;
if (rexmit)
- OSAddAtomic(1, &nfsstats.rpcretries);
+ OSAddAtomic64(1, &nfsstats.rpcretries);
req->r_flags |= R_SENT;
if (req->r_flags & R_WAITSENT) {
req->r_flags &= ~R_WAITSENT;
sock_getsockopt(nso->nso_so, SOL_SOCKET, SO_ERROR, &clearerror, &optlen);
#ifdef NFS_SOCKET_DEBUGGING
if (clearerror)
- NFS_SOCK_DBG(("nfs_send: ignoring UDP socket error %d so %d\n",
- error, clearerror));
+ NFS_SOCK_DBG("nfs_send: ignoring UDP socket error %d so %d\n",
+ error, clearerror);
#endif
}
}
break;
}
if (needrecon && (nso == nmp->nm_nso)) { /* mark socket as needing reconnect */
- NFS_SOCK_DBG(("nfs_send: 0x%llx need reconnect %d\n", req->r_xid, error));
+ NFS_SOCK_DBG("nfs_send: 0x%llx need reconnect %d\n", req->r_xid, error);
nfs_need_reconnect(nmp);
}
!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)
if (error && (error != EWOULDBLOCK)) {
/* problems with the socket... mark for reconnection */
- NFS_SOCK_DBG(("nfs_udp_rcv: need reconnect %d\n", error));
+ NFS_SOCK_DBG("nfs_udp_rcv: need reconnect %d\n", error);
nfs_need_reconnect(nmp);
}
}
}
#ifdef NFS_SOCKET_DEBUGGING
if (!recv && (error != EWOULDBLOCK))
- NFS_SOCK_DBG(("nfs_tcp_rcv: got nothing, error %d, got FIN?\n", error));
+ NFS_SOCK_DBG("nfs_tcp_rcv: got nothing, error %d, got FIN?\n", error);
#endif
/* note: no error and no data indicates server closed its end */
if ((error != EWOULDBLOCK) && (error || !recv)) {
/* problems with the socket... mark for reconnection */
- NFS_SOCK_DBG(("nfs_tcp_rcv: need reconnect %d\n", error));
+ NFS_SOCK_DBG("nfs_tcp_rcv: need reconnect %d\n", error);
nfs_need_reconnect(nmp);
}
}
msg.msg_iov = &aio;
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));
+ NFS_SOCK_DBG("nfs_sock_poke: error %d\n", error);
+ nfs_is_dead(error, nmp);
}
/*
nfsm_chain_get_32(error, &nmrep, rxid);
nfsm_chain_get_32(error, &nmrep, reply);
if (error || (reply != RPC_REPLY)) {
- OSAddAtomic(1, &nfsstats.rpcinvalid);
+ OSAddAtomic64(1, &nfsstats.rpcinvalid);
mbuf_freem(mrep);
return;
}
if (!req) {
/* not matched to a request, so drop it. */
lck_mtx_unlock(nfs_request_mutex);
- OSAddAtomic(1, &nfsstats.rpcunexpected);
+ OSAddAtomic64(1, &nfsstats.rpcunexpected);
mbuf_freem(mrep);
}
}
break;
/* check if we need to resend */
if (req->r_flags & R_MUSTRESEND) {
- NFS_SOCK_DBG(("nfs wait resend: p %d x 0x%llx f 0x%x rtt %d\n",
- req->r_procnum, req->r_xid, req->r_flags, req->r_rtt));
+ NFS_SOCK_DBG("nfs wait resend: p %d x 0x%llx f 0x%x rtt %d\n",
+ req->r_procnum, req->r_xid, req->r_flags, req->r_rtt);
req->r_flags |= R_SENDING;
lck_mtx_unlock(&req->r_mtx);
if (nfs_request_using_gss(req)) {
}
error = nfs_send(req, 1);
lck_mtx_lock(&req->r_mtx);
- NFS_SOCK_DBG(("nfs wait resend: p %d x 0x%llx f 0x%x rtt %d err %d\n",
- req->r_procnum, req->r_xid, req->r_flags, req->r_rtt, error));
+ NFS_SOCK_DBG("nfs wait resend: p %d x 0x%llx f 0x%x rtt %d err %d\n",
+ req->r_procnum, req->r_xid, req->r_flags, req->r_rtt, error);
if (error)
break;
if (((error = req->r_error)) || req->r_nmrep.nmc_mhead)
}
if ((nmp->nm_vers != NFS_VER4) && (procnum >= 0) && (procnum < NFS_NPROCS))
- OSAddAtomic(1, &nfsstats.rpccnt[procnum]);
+ OSAddAtomic64(1, &nfsstats.rpccnt[procnum]);
if ((nmp->nm_vers == NFS_VER4) && (procnum != NFSPROC4_COMPOUND) && (procnum != NFSPROC4_NULL))
panic("nfs_request: invalid NFSv4 RPC request %d\n", procnum);
((nmp->nm_tprintf_delay) - (nmp->nm_tprintf_initial_delay));
}
- OSAddAtomic(1, &nfsstats.rpcrequests);
+ OSAddAtomic64(1, &nfsstats.rpcrequests);
/*
* Chain request into list of outstanding requests. Be sure
if ((req->r_delay >= 30) && !(nmp->nm_state & NFSSTA_MOUNTED)) {
/* we're not yet completely mounted and */
/* we can't complete an RPC, so we fail */
- OSAddAtomic(1, &nfsstats.rpctimeouts);
+ OSAddAtomic64(1, &nfsstats.rpctimeouts);
nfs_softterm(req);
error = req->r_error;
goto nfsmout;
}
if (NMFLAG(nmp, SOFT) && (req->r_delay == 30) && !(req->r_flags & R_NOINTR)) {
/* for soft mounts, just give up after a short while */
- OSAddAtomic(1, &nfsstats.rpctimeouts);
+ OSAddAtomic64(1, &nfsstats.rpctimeouts);
nfs_softterm(req);
error = req->r_error;
goto nfsmout;
do {
if ((error = nfs_sigintr(req->r_nmp, req, req->r_thread, 0)))
goto nfsmout;
- tsleep(&lbolt, PSOCK|slpflag, "nfs_jukebox_trylater", 0);
+ tsleep(nfs_request_finish, PSOCK|slpflag, "nfs_jukebox_trylater", hz);
slpflag = 0;
} while (--delay > 0);
}
* server. Associate the context that we are setting up with the request that we
* are sending.
*/
-
+
int
nfs_request_gss(
mount_t mp,
if ((error = nfs_request_create(NULL, mp, nmrest, NFSPROC_NULL, thd, cred, &req)))
return (error);
req->r_flags |= (flags & R_OPTMASK);
-
+
if (cp == NULL) {
printf("nfs_request_gss request has no context\n");
nfs_request_rele(req);
nfs_request_rele(req);
return (error);
}
-
+
/*
* Create and start an asynchronous NFS request.
*/
lck_mtx_unlock(&nmp->nm_lock);
/* we're not yet completely mounted and */
/* we can't complete an RPC, so we fail */
- OSAddAtomic(1, &nfsstats.rpctimeouts);
+ OSAddAtomic64(1, &nfsstats.rpctimeouts);
nfs_softterm(req);
finish_asyncio = ((req->r_callback.rcb_func != NULL) && !(req->r_flags & R_WAITSENT));
wakeup(req);
* 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))
+ if (!NMFLAG(nmp, 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)))
+ ((nmp->nm_reconnect_start <= 0) || ((now.tv_sec - nmp->nm_reconnect_start) < 8)))
maxtime = (NFS_MAXTIMEO / (nmp->nm_timeouts+1))/2;
else
maxtime = NFS_MINTIMEO/4;
continue;
}
/* The request has timed out */
- NFS_SOCK_DBG(("nfs timeout: proc %d %d xid %llx rtt %d to %d # %d, t %ld/%d\n",
+ NFS_SOCK_DBG("nfs timeout: proc %d %d xid %llx rtt %d to %d # %d, t %ld/%d\n",
req->r_procnum, proct[req->r_procnum],
req->r_xid, req->r_rtt, timeo, nmp->nm_timeouts,
- (now.tv_sec - req->r_start)*NFS_HZ, maxtime));
+ (now.tv_sec - req->r_start)*NFS_HZ, maxtime);
if (nmp->nm_timeouts < 8)
nmp->nm_timeouts++;
nfs_mount_check_dead_timeout(nmp);
}
/* 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))) &&
((req->r_rexmit >= req->r_retry) || /* too many */
((now.tv_sec - req->r_start)*NFS_HZ > maxtime))) { /* too long */
- OSAddAtomic(1, &nfsstats.rpctimeouts);
+ OSAddAtomic64(1, &nfsstats.rpctimeouts);
lck_mtx_lock(&nmp->nm_lock);
if (!(nmp->nm_state & NFSSTA_TIMEO)) {
lck_mtx_unlock(&nmp->nm_lock);
lck_mtx_unlock(&req->r_mtx);
continue;
}
- NFS_SOCK_DBG(("nfs timer TERMINATE: p %d x 0x%llx f 0x%x rtt %d t %ld\n",
+ NFS_SOCK_DBG("nfs timer TERMINATE: p %d x 0x%llx f 0x%x rtt %d t %ld\n",
req->r_procnum, req->r_xid, req->r_flags, req->r_rtt,
- now.tv_sec - req->r_start));
+ now.tv_sec - req->r_start);
nfs_softterm(req);
finish_asyncio = ((req->r_callback.rcb_func != NULL) && !(req->r_flags & R_WAITSENT));
wakeup(req);
lck_mtx_unlock(&req->r_mtx);
continue;
}
- NFS_SOCK_DBG(("nfs timer mark resend: p %d x 0x%llx f 0x%x rtt %d\n",
- req->r_procnum, req->r_xid, req->r_flags, req->r_rtt));
+ NFS_SOCK_DBG("nfs timer mark resend: p %d x 0x%llx f 0x%x rtt %d\n",
+ req->r_procnum, req->r_xid, req->r_flags, req->r_rtt);
req->r_flags |= R_MUSTRESEND;
req->r_rtt = -1;
wakeup(req);
* If the mount is hung and we've requested not to hang
* on remote filesystems, then bail now.
*/
- if (!error && (nmp->nm_state & NFSSTA_TIMEO) && nfs_noremotehang(thd))
+ if (current_proc() != kernproc &&
+ !error && (nmp->nm_state & NFSSTA_TIMEO) && nfs_noremotehang(thd))
error = EIO;
if (!nmplocked)
return (error);
/* may not have a thread for async I/O */
- if (thd == NULL)
+ if (thd == NULL || current_proc() == kernproc)
return (0);
/*
pmvers = RPCBVERS4;
pmproc = RPCBPROC_GETVERSADDR;
} else {
- return (EINVAL);
+ return (EINVAL);
}
nfsm_chain_null(&nmreq);
nfsm_chain_null(&nmrep);
return (0);
}
+#define NFS_SQUISH_MOBILE_ONLY 0x0001 /* Squish mounts only on mobile machines */
+#define NFS_SQUISH_AUTOMOUNTED_ONLY 0x0002 /* Squish mounts only if the are automounted */
+#define NFS_SQUISH_SOFT 0x0004 /* Treat all soft mounts as though they were on a mobile machine */
+#define NFS_SQUISH_QUICK 0x0008 /* Try to squish mounts more quickly. */
+#define NFS_SQUISH_SHUTDOWN 0x1000 /* Squish all mounts on shutdown. Currently not implemented */
+
+uint32_t nfs_squishy_flags = NFS_SQUISH_MOBILE_ONLY | NFS_SQUISH_AUTOMOUNTED_ONLY | NFS_SQUISH_QUICK;
+int32_t nfs_is_mobile;
+
+#define NFS_SQUISHY_DEADTIMEOUT 8 /* Dead time out for squishy mounts */
+#define NFS_SQUISHY_QUICKTIMEOUT 4 /* Quicker dead time out when nfs_squish_flags NFS_SQUISH_QUICK bit is set*/
+
+/*
+ * Could this mount be squished?
+ */
+int
+nfs_can_squish(struct nfsmount *nmp)
+{
+ uint64_t flags = vfs_flags(nmp->nm_mountp);
+ int softsquish = ((nfs_squishy_flags & NFS_SQUISH_SOFT) & NMFLAG(nmp, SOFT));
+
+ if (!softsquish && (nfs_squishy_flags & NFS_SQUISH_MOBILE_ONLY) && nfs_is_mobile == 0)
+ return (0);
+
+ if ((nfs_squishy_flags & NFS_SQUISH_AUTOMOUNTED_ONLY) && (flags & MNT_AUTOMOUNTED) == 0)
+ return (0);
+
+ return (1);
+}
+
+/*
+ * NFS mounts default to "rw,hard" - but frequently on mobile clients
+ * the mount may become "not responding". It's desirable to be able
+ * to unmount these dead mounts, but only if there is no risk of
+ * losing data or crashing applications. A "squishy" NFS mount is one
+ * that can be force unmounted with little risk of harm.
+ *
+ * nfs_is_squishy checks if a mount is in a squishy state. A mount is
+ * in a squishy state iff it is allowed to be squishy and there are no
+ * dirty pages and there are no mmapped files and there are no files
+ * open for write. Mounts are allowed to be squishy is controlled by
+ * the settings of the nfs_squishy_flags and its mobility state. These
+ * flags can be set by sysctls.
+ *
+ * If nfs_is_squishy determines that we are in a squishy state we will
+ * update the current dead timeout to at least NFS_SQUISHY_DEADTIMEOUT
+ * (or NFS_SQUISHY_QUICKTIMEOUT if NFS_SQUISH_QUICK is set) (see
+ * above) or 1/8th of the mount's nm_deadtimeout value, otherwise we just
+ * update the current dead timeout with the mount's nm_deadtimeout
+ * value set at mount time.
+ *
+ * Assumes that nm_lock is held.
+ *
+ * Note this routine is racey, but its effects on setting the
+ * dead timeout only have effects when we're in trouble and are likely
+ * to stay that way. Since by default its only for automounted
+ * volumes on mobile machines; this is a reasonable trade off between
+ * data integrity and user experience. It can be disabled or set via
+ * nfs.conf file.
+ */
+
+int
+nfs_is_squishy(struct nfsmount *nmp)
+{
+ mount_t mp = nmp->nm_mountp;
+ 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",
+ vfs_statfs(mp)->f_mntfromname, nmp->nm_curdeadtimeout, nfs_is_mobile);
+
+ if (!nfs_can_squish(nmp))
+ goto out;
+
+ timeo = (nmp->nm_deadtimeout > timeo) ? max(nmp->nm_deadtimeout/8, timeo) : timeo;
+ NFS_SOCK_DBG("nm_writers = %d nm_mappers = %d timeo = %d\n", nmp->nm_writers, nmp->nm_mappers, timeo);
+
+ if (nmp->nm_writers == 0 && nmp->nm_mappers == 0) {
+ uint64_t flags = mp ? vfs_flags(mp) : 0;
+ squishy = 1;
+
+ /*
+ * Walk the nfs nodes and check for dirty buffers it we're not
+ * RDONLY and we've not already been declared as squishy since
+ * this can be a bit expensive.
+ */
+ if (!(flags & MNT_RDONLY) && !(nmp->nm_state & NFSSTA_SQUISHY))
+ squishy = !nfs_mount_is_dirty(mp);
+ }
+
+out:
+ if (squishy)
+ nmp->nm_state |= NFSSTA_SQUISHY;
+ else
+ nmp->nm_state &= ~NFSSTA_SQUISHY;
+
+ nmp->nm_curdeadtimeout = squishy ? timeo : nmp->nm_deadtimeout;
+
+ NFS_SOCK_DBG("nm_curdeadtimeout = %d\n", nmp->nm_curdeadtimeout);
+
+ return (squishy);
+}
+
+/*
+ * On a send operation, if we can't reach the server and we've got only one server to talk to
+ * 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)
+{
+ if (nmp->nm_state & NFSSTA_DEAD)
+ return (1);
+
+ if ((error != ENETUNREACH && error != EHOSTUNREACH && error != EADDRNOTAVAIL) ||
+ !(nmp->nm_locations.nl_numlocs == 1 && nmp->nm_locations.nl_locations[0]->nl_servcount == 1))
+ 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);
+ nmp->nm_state |= NFSSTA_DEAD;
+ vfs_event_signal(&vfs_statfs(nmp->nm_mountp)->f_fsid, VQ_DEAD, 0);
+ return (1);
+ }
+ return (0);
+}
+
+int
+nfs_is_dead(int error, struct nfsmount *nmp)
+{
+ int is_dead;
+
+ lck_mtx_lock(&nmp->nm_lock);
+ is_dead = nfs_is_dead_lock(error, nmp);
+ lck_mtx_unlock(&nmp->nm_lock);
+
+ return (is_dead);
+}
+
void
nfs_down(struct nfsmount *nmp, thread_t thd, int error, int flags, const char *msg)
{
unresponsive = (nmp->nm_state & timeoutmask);
- if (unresponsive && (nmp->nm_deadtimeout > 0)) {
+ nfs_is_squishy(nmp);
+
+ if (unresponsive && (nmp->nm_curdeadtimeout > 0)) {
microuptime(&now);
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_deadtimeout) {
+ } else if ((now.tv_sec - nmp->nm_deadto_start) > nmp->nm_curdeadtimeout) {
if (!(nmp->nm_state & NFSSTA_DEAD))
- printf("nfs server %s: dead\n", vfs_statfs(nmp->nm_mountp)->f_mntfromname);
+ 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;
}
}
unresponsive = (nmp->nm_state & timeoutmask);
- if (nmp->nm_deadto_start)
- nmp->nm_deadto_start = 0;
+ nmp->nm_deadto_start = 0;
+ nmp->nm_curdeadtimeout = nmp->nm_deadtimeout;
+ nmp->nm_state &= ~NFSSTA_SQUISHY;
lck_mtx_unlock(&nmp->nm_lock);
if (softnobrowse)
*nmrepp = nmrep;
if ((err != 0) && (err != NFSERR_RETVOID))
- OSAddAtomic(1, &nfsstats.srvrpc_errs);
+ OSAddAtomic64(1, &nfsstats.srvrpc_errs);
return (0);
}
ns_flag = SLP_NEEDQ;
goto dorecs;
}
-
+
bzero(&msg, sizeof(msg));
msg.msg_name = (caddr_t)&nam;
msg.msg_namelen = sizeof(nam);
-
+
do {
bytes_read = 1000000000;
error = sock_receivembuf(so, &msg, &mp, MSG_DONTWAIT | MSG_NEEDSA, &bytes_read);
if (slp->ns_frag == NULL) {
slp->ns_frag = recm;
} else {
- m = slp->ns_frag;
+ m = slp->ns_frag;
while ((m2 = mbuf_next(m)))
m = m2;
if ((error = mbuf_setnext(m, recm)))
}
#endif /* NFSSERVER */
-