+ timeout(nfs_timer, (caddr_t)0, hz/NFS_HZ);
+}
+
+/*
+ * NFS timer update and backoff. The "Jacobson/Karels/Karn" scheme is
+ * used here. The timer state is held in the nfsmount structure and
+ * a single request is used to clock the response. When successful
+ * the rtt smoothing in nfs_updatetimer is used, when failed the backoff
+ * is done by nfs_backofftimer. We also log failure messages in these
+ * routines.
+ *
+ * Congestion variables are held in the nfshost structure which
+ * is referenced by nfsmounts and shared per-server. This separation
+ * makes it possible to do per-mount timing which allows varying disk
+ * access times to be dealt with, while preserving a network oriented
+ * congestion control scheme.
+ *
+ * The windowing implements the Jacobson/Karels slowstart algorithm
+ * with adjusted scaling factors. We start with one request, then send
+ * 4 more after each success until the ssthresh limit is reached, then
+ * we increment at a rate proportional to the window. On failure, we
+ * remember 3/4 the current window and clamp the send limit to 1. Note
+ * ICMP source quench is not reflected in so->so_error so we ignore that
+ * for now.
+ *
+ * NFS behaves much more like a transport protocol with these changes,
+ * shedding the teenage pedal-to-the-metal tendencies of "other"
+ * implementations.
+ *
+ * Timers and congestion avoidance by Tom Talpey, Open Software Foundation.
+ */
+
+/*
+ * The TCP algorithm was not forgiving enough. Because the NFS server
+ * responds only after performing lookups/diskio/etc, we have to be
+ * more prepared to accept a spiky variance. The TCP algorithm is:
+ * TCP_RTO(nmp) ((((nmp)->nm_srtt >> 2) + (nmp)->nm_rttvar) >> 1)
+ */
+#define NFS_RTO(nmp) (((nmp)->nm_srtt >> 3) + (nmp)->nm_rttvar)
+
+nfs_updatetimer(nmp)
+ register struct nfsmount *nmp;
+{
+
+ /* If retransmitted, clear and return */
+ if (nmp->nm_rexmit || nmp->nm_currexmit) {
+ nmp->nm_rexmit = nmp->nm_currexmit = 0;
+ return;
+ }
+ /* If have a measurement, do smoothing */
+ if (nmp->nm_srtt) {
+ register short delta;
+ delta = nmp->nm_rtt - (nmp->nm_srtt >> 3);
+ if ((nmp->nm_srtt += delta) <= 0)
+ nmp->nm_srtt = 1;
+ if (delta < 0)
+ delta = -delta;
+ delta -= (nmp->nm_rttvar >> 2);
+ if ((nmp->nm_rttvar += delta) <= 0)
+ nmp->nm_rttvar = 1;
+ /* Else initialize */
+ } else {
+ nmp->nm_rttvar = nmp->nm_rtt << 1;
+ if (nmp->nm_rttvar == 0) nmp->nm_rttvar = 2;
+ nmp->nm_srtt = nmp->nm_rttvar << 2;
+ }
+ /* Compute new Retransmission TimeOut and clip */
+ nmp->nm_rto = NFS_RTO(nmp);
+ if (nmp->nm_rto < NFS_MINTIMEO)
+ nmp->nm_rto = NFS_MINTIMEO;
+ else if (nmp->nm_rto > NFS_MAXTIMEO)
+ nmp->nm_rto = NFS_MAXTIMEO;
+
+ /* Update window estimate */
+ if (nmp->nm_window < nmp->nm_ssthresh) /* quickly */
+ nmp->nm_window += 4;
+ else { /* slowly */
+ register long incr = ++nmp->nm_winext;
+ incr = (incr * incr) / nmp->nm_window;
+ if (incr > 0) {
+ nmp->nm_winext = 0;
+ ++nmp->nm_window;
+ }
+ }
+ if (nmp->nm_window > NFS_MAXWINDOW)
+ nmp->nm_window = NFS_MAXWINDOW;
+}
+
+nfs_backofftimer(nmp)
+ register struct nfsmount *nmp;
+{
+ register unsigned long newrto;
+
+ /* Clip shift count */
+ if (++nmp->nm_rexmit > 8 * sizeof nmp->nm_rto)
+ nmp->nm_rexmit = 8 * sizeof nmp->nm_rto;
+ /* Back off RTO exponentially */
+ newrto = NFS_RTO(nmp);
+ newrto <<= (nmp->nm_rexmit - 1);
+ if (newrto == 0 || newrto > NFS_MAXTIMEO)
+ newrto = NFS_MAXTIMEO;
+ nmp->nm_rto = newrto;
+
+ /* If too many retries, message, assume a bogus RTT and re-measure */
+ if (nmp->nm_currexmit < nmp->nm_rexmit) {
+ nmp->nm_currexmit = nmp->nm_rexmit;
+ if (nmp->nm_currexmit >= nfsrexmtthresh) {
+ if (nmp->nm_currexmit == nfsrexmtthresh) {
+ nmp->nm_rttvar += (nmp->nm_srtt >> 2);
+ nmp->nm_srtt = 0;
+ }
+ }
+ }
+ /* Close down window but remember this point (3/4 current) for later */
+ nmp->nm_ssthresh = ((nmp->nm_window << 1) + nmp->nm_window) >> 2;
+ nmp->nm_window = 1;
+ nmp->nm_winext = 0;
+}
+
+/*
+ * Test for a termination signal pending on procp.
+ * This is used for NFSMNT_INT mounts.
+ */
+nfs_sigintr(p)
+ register struct proc *p;
+{
+ if (p && p->p_sig && (((p->p_sig &~ p->p_sigmask) &~ p->p_sigignore) &
+ NFSINT_SIGMASK))
+ return (1);
+ else
+ return (0);
+}
+
+nfs_msg(p, server, msg)
+ struct proc *p;
+ char *server, *msg;
+{
+ tpr_t tpr;
+
+ if (p)
+ tpr = tprintf_open(p);
+ else
+ tpr = NULL;
+ tprintf(tpr, "nfs server %s: %s\n", server, msg);
+ tprintf_close(tpr);
+}
+
+/*
+ * Lock a socket against others.
+ * Necessary for STREAM sockets to ensure you get an entire rpc request/reply
+ * and also to avoid race conditions between the processes with nfs requests
+ * in progress when a reconnect is necessary.
+ */
+nfs_solock(flagp)
+ register int *flagp;
+{
+
+ while (*flagp & NFSMNT_SCKLOCK) {
+ *flagp |= NFSMNT_WANTSCK;
+ (void) tsleep((caddr_t)flagp, PZERO-1, "nfsolck", 0);
+ }
+ *flagp |= NFSMNT_SCKLOCK;
+}
+
+/*
+ * Unlock the stream socket for others.
+ */
+nfs_sounlock(flagp)
+ register int *flagp;
+{
+
+ if ((*flagp & NFSMNT_SCKLOCK) == 0)
+ panic("nfs sounlock");
+ *flagp &= ~NFSMNT_SCKLOCK;
+ if (*flagp & NFSMNT_WANTSCK) {
+ *flagp &= ~NFSMNT_WANTSCK;
+ wakeup((caddr_t)flagp);
+ }
+}
+
+/*
+ * This function compares two net addresses by family and returns TRUE
+ * if they are the same.
+ * If there is any doubt, return FALSE.
+ */
+nfs_netaddr_match(nam1, nam2)
+ struct mbuf *nam1, *nam2;
+{
+ register struct sockaddr *saddr1, *saddr2;
+
+ saddr1 = mtod(nam1, struct sockaddr *);
+ saddr2 = mtod(nam2, struct sockaddr *);
+ if (saddr1->sa_family != saddr2->sa_family)
+ return (0);
+
+ /*
+ * Must do each address family separately since unused fields
+ * are undefined values and not always zeroed.
+ */
+ switch (saddr1->sa_family) {
+ case AF_INET:
+ if (((struct sockaddr_in *)saddr1)->sin_addr.s_addr ==
+ ((struct sockaddr_in *)saddr2)->sin_addr.s_addr)
+ return (1);
+ break;
+ default:
+ break;
+ };
+ return (0);