|
|
1.1 ! root 1: /* ! 2: * Copyright (c) 1989, 1991 The Regents of the University of California. ! 3: * All rights reserved. ! 4: * ! 5: * This code is derived from software contributed to Berkeley by ! 6: * Rick Macklem at The University of Guelph. ! 7: * ! 8: * Redistribution and use in source and binary forms, with or without ! 9: * modification, are permitted provided that the following conditions ! 10: * are met: ! 11: * 1. Redistributions of source code must retain the above copyright ! 12: * notice, this list of conditions and the following disclaimer. ! 13: * 2. Redistributions in binary form must reproduce the above copyright ! 14: * notice, this list of conditions and the following disclaimer in the ! 15: * documentation and/or other materials provided with the distribution. ! 16: * 3. All advertising materials mentioning features or use of this software ! 17: * must display the following acknowledgement: ! 18: * This product includes software developed by the University of ! 19: * California, Berkeley and its contributors. ! 20: * 4. Neither the name of the University nor the names of its contributors ! 21: * may be used to endorse or promote products derived from this software ! 22: * without specific prior written permission. ! 23: * ! 24: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ! 25: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ! 26: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ! 27: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE ! 28: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ! 29: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ! 30: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ! 31: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ! 32: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ! 33: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ! 34: * SUCH DAMAGE. ! 35: * ! 36: * @(#)nfs_socket.c 7.23 (Berkeley) 4/20/91 ! 37: */ ! 38: ! 39: /* ! 40: * Socket operations for use by nfs ! 41: */ ! 42: ! 43: #include "param.h" ! 44: #include "proc.h" ! 45: #include "mount.h" ! 46: #include "kernel.h" ! 47: #include "malloc.h" ! 48: #include "mbuf.h" ! 49: #include "namei.h" ! 50: #include "vnode.h" ! 51: #include "domain.h" ! 52: #include "protosw.h" ! 53: #include "socket.h" ! 54: #include "socketvar.h" ! 55: #include "syslog.h" ! 56: #include "tprintf.h" ! 57: #include "../netinet/in.h" ! 58: #include "../netinet/tcp.h" ! 59: ! 60: #include "rpcv2.h" ! 61: #include "nfsv2.h" ! 62: #include "nfs.h" ! 63: #include "xdr_subs.h" ! 64: #include "nfsm_subs.h" ! 65: #include "nfsmount.h" ! 66: ! 67: #define TRUE 1 ! 68: #define FALSE 0 ! 69: ! 70: /* ! 71: * External data, mostly RPC constants in XDR form ! 72: */ ! 73: extern u_long rpc_reply, rpc_msgdenied, rpc_mismatch, rpc_vers, rpc_auth_unix, ! 74: rpc_msgaccepted, rpc_call; ! 75: extern u_long nfs_prog, nfs_vers; ! 76: /* Maybe these should be bits in a u_long ?? */ ! 77: extern int nonidempotent[NFS_NPROCS]; ! 78: static int compressrequest[NFS_NPROCS] = { ! 79: FALSE, ! 80: TRUE, ! 81: TRUE, ! 82: FALSE, ! 83: TRUE, ! 84: TRUE, ! 85: TRUE, ! 86: FALSE, ! 87: FALSE, ! 88: TRUE, ! 89: TRUE, ! 90: TRUE, ! 91: TRUE, ! 92: TRUE, ! 93: TRUE, ! 94: TRUE, ! 95: TRUE, ! 96: TRUE, ! 97: }; ! 98: int nfs_sbwait(); ! 99: void nfs_disconnect(); ! 100: struct mbuf *nfs_compress(), *nfs_uncompress(); ! 101: ! 102: int nfsrv_null(), ! 103: nfsrv_getattr(), ! 104: nfsrv_setattr(), ! 105: nfsrv_lookup(), ! 106: nfsrv_readlink(), ! 107: nfsrv_read(), ! 108: nfsrv_write(), ! 109: nfsrv_create(), ! 110: nfsrv_remove(), ! 111: nfsrv_rename(), ! 112: nfsrv_link(), ! 113: nfsrv_symlink(), ! 114: nfsrv_mkdir(), ! 115: nfsrv_rmdir(), ! 116: nfsrv_readdir(), ! 117: nfsrv_statfs(), ! 118: nfsrv_noop(); ! 119: ! 120: int (*nfsrv_procs[NFS_NPROCS])() = { ! 121: nfsrv_null, ! 122: nfsrv_getattr, ! 123: nfsrv_setattr, ! 124: nfsrv_noop, ! 125: nfsrv_lookup, ! 126: nfsrv_readlink, ! 127: nfsrv_read, ! 128: nfsrv_noop, ! 129: nfsrv_write, ! 130: nfsrv_create, ! 131: nfsrv_remove, ! 132: nfsrv_rename, ! 133: nfsrv_link, ! 134: nfsrv_symlink, ! 135: nfsrv_mkdir, ! 136: nfsrv_rmdir, ! 137: nfsrv_readdir, ! 138: nfsrv_statfs, ! 139: }; ! 140: ! 141: struct nfsreq nfsreqh; ! 142: int nfsrexmtthresh = NFS_FISHY; ! 143: int nfs_tcpnodelay = 1; ! 144: ! 145: /* ! 146: * Initialize sockets and congestion for a new NFS connection. ! 147: * We do not free the sockaddr if error. ! 148: */ ! 149: nfs_connect(nmp) ! 150: register struct nfsmount *nmp; ! 151: { ! 152: register struct socket *so; ! 153: int s, error, bufsize; ! 154: struct mbuf *m; ! 155: ! 156: nmp->nm_so = (struct socket *)0; ! 157: if (error = socreate(mtod(nmp->nm_nam, struct sockaddr *)->sa_family, ! 158: &nmp->nm_so, nmp->nm_sotype, nmp->nm_soproto)) ! 159: goto bad; ! 160: so = nmp->nm_so; ! 161: nmp->nm_soflags = so->so_proto->pr_flags; ! 162: ! 163: if (nmp->nm_sotype == SOCK_DGRAM) ! 164: bufsize = min(4 * (nmp->nm_wsize + NFS_MAXPKTHDR), ! 165: NFS_MAXPACKET); ! 166: else ! 167: bufsize = min(4 * (nmp->nm_wsize + NFS_MAXPKTHDR + sizeof(u_long)), ! 168: NFS_MAXPACKET + sizeof(u_long)); ! 169: if (error = soreserve(so, bufsize, bufsize)) ! 170: goto bad; ! 171: ! 172: /* ! 173: * Protocols that do not require connections may be optionally left ! 174: * unconnected for servers that reply from a port other than NFS_PORT. ! 175: */ ! 176: if (nmp->nm_flag & NFSMNT_NOCONN) { ! 177: if (nmp->nm_soflags & PR_CONNREQUIRED) { ! 178: error = ENOTCONN; ! 179: goto bad; ! 180: } ! 181: } else { ! 182: if (error = soconnect(so, nmp->nm_nam)) ! 183: goto bad; ! 184: ! 185: /* ! 186: * Wait for the connection to complete. Cribbed from the ! 187: * connect system call but with the wait at negative prio. ! 188: */ ! 189: s = splnet(); ! 190: while ((so->so_state & SS_ISCONNECTING) && so->so_error == 0) ! 191: (void) tsleep((caddr_t)&so->so_timeo, PSOCK, "nfscon", 0); ! 192: splx(s); ! 193: if (so->so_error) { ! 194: error = so->so_error; ! 195: goto bad; ! 196: } ! 197: } ! 198: if (nmp->nm_sotype == SOCK_DGRAM) { ! 199: if (nmp->nm_flag & (NFSMNT_SOFT | NFSMNT_SPONGY | NFSMNT_INT)) { ! 200: so->so_rcv.sb_timeo = (5 * hz); ! 201: so->so_snd.sb_timeo = (5 * hz); ! 202: } else { ! 203: so->so_rcv.sb_timeo = 0; ! 204: so->so_snd.sb_timeo = 0; ! 205: } ! 206: nmp->nm_rto = NFS_TIMEO; ! 207: } else { ! 208: if (nmp->nm_flag & (NFSMNT_SOFT | NFSMNT_SPONGY | NFSMNT_INT)) { ! 209: so->so_rcv.sb_timeo = (5 * hz); ! 210: so->so_snd.sb_timeo = (5 * hz); ! 211: } else { ! 212: so->so_rcv.sb_timeo = 0; ! 213: so->so_snd.sb_timeo = 0; ! 214: } ! 215: if (so->so_proto->pr_flags & PR_CONNREQUIRED) { ! 216: MGET(m, M_WAIT, MT_SOOPTS); ! 217: *mtod(m, int *) = 1; ! 218: m->m_len = sizeof(int); ! 219: sosetopt(so, SOL_SOCKET, SO_KEEPALIVE, m); ! 220: } ! 221: if (so->so_proto->pr_domain->dom_family == AF_INET && ! 222: so->so_proto->pr_protocol == IPPROTO_TCP && ! 223: nfs_tcpnodelay) { ! 224: MGET(m, M_WAIT, MT_SOOPTS); ! 225: *mtod(m, int *) = 1; ! 226: m->m_len = sizeof(int); ! 227: sosetopt(so, IPPROTO_TCP, TCP_NODELAY, m); ! 228: } ! 229: nmp->nm_rto = 10 * NFS_TIMEO; /* XXX */ ! 230: } ! 231: so->so_rcv.sb_flags |= SB_NOINTR; ! 232: so->so_snd.sb_flags |= SB_NOINTR; ! 233: ! 234: /* Initialize other non-zero congestion variables */ ! 235: nmp->nm_window = 2; /* Initial send window */ ! 236: nmp->nm_ssthresh = NFS_MAXWINDOW; /* Slowstart threshold */ ! 237: nmp->nm_rttvar = nmp->nm_rto << 1; ! 238: nmp->nm_sent = 0; ! 239: nmp->nm_currexmit = 0; ! 240: return (0); ! 241: ! 242: bad: ! 243: nfs_disconnect(nmp); ! 244: return (error); ! 245: } ! 246: ! 247: /* ! 248: * Reconnect routine: ! 249: * Called when a connection is broken on a reliable protocol. ! 250: * - clean up the old socket ! 251: * - nfs_connect() again ! 252: * - set R_MUSTRESEND for all outstanding requests on mount point ! 253: * If this fails the mount point is DEAD! ! 254: * nb: Must be called with the nfs_solock() set on the mount point. ! 255: */ ! 256: nfs_reconnect(rep, nmp) ! 257: register struct nfsreq *rep; ! 258: register struct nfsmount *nmp; ! 259: { ! 260: register struct nfsreq *rp; ! 261: int error; ! 262: ! 263: nfs_msg(rep->r_procp, nmp->nm_mountp->mnt_stat.f_mntfromname, ! 264: "trying reconnect"); ! 265: while (error = nfs_connect(nmp)) { ! 266: #ifdef lint ! 267: error = error; ! 268: #endif /* lint */ ! 269: if ((nmp->nm_flag & NFSMNT_INT) && nfs_sigintr(rep->r_procp)) ! 270: return (EINTR); ! 271: (void) tsleep((caddr_t)&lbolt, PSOCK, "nfscon", 0); ! 272: } ! 273: nfs_msg(rep->r_procp, nmp->nm_mountp->mnt_stat.f_mntfromname, ! 274: "reconnected"); ! 275: ! 276: /* ! 277: * Loop through outstanding request list and fix up all requests ! 278: * on old socket. ! 279: */ ! 280: rp = nfsreqh.r_next; ! 281: while (rp != &nfsreqh) { ! 282: if (rp->r_nmp == nmp) ! 283: rp->r_flags |= R_MUSTRESEND; ! 284: rp = rp->r_next; ! 285: } ! 286: return (0); ! 287: } ! 288: ! 289: /* ! 290: * NFS disconnect. Clean up and unlink. ! 291: */ ! 292: void ! 293: nfs_disconnect(nmp) ! 294: register struct nfsmount *nmp; ! 295: { ! 296: register struct socket *so; ! 297: ! 298: if (nmp->nm_so) { ! 299: so = nmp->nm_so; ! 300: nmp->nm_so = (struct socket *)0; ! 301: soshutdown(so, 2); ! 302: soclose(so); ! 303: } ! 304: } ! 305: ! 306: /* ! 307: * This is the nfs send routine. For connection based socket types, it ! 308: * must be called with an nfs_solock() on the socket. ! 309: * "rep == NULL" indicates that it has been called from a server. ! 310: */ ! 311: nfs_send(so, nam, top, rep) ! 312: register struct socket *so; ! 313: struct mbuf *nam; ! 314: register struct mbuf *top; ! 315: struct nfsreq *rep; ! 316: { ! 317: struct mbuf *sendnam; ! 318: int error, soflags; ! 319: ! 320: if (rep) { ! 321: if (rep->r_flags & R_SOFTTERM) { ! 322: m_freem(top); ! 323: return (EINTR); ! 324: } ! 325: if (rep->r_nmp->nm_so == NULL && ! 326: (error = nfs_reconnect(rep, rep->r_nmp))) ! 327: return (error); ! 328: rep->r_flags &= ~R_MUSTRESEND; ! 329: so = rep->r_nmp->nm_so; ! 330: soflags = rep->r_nmp->nm_soflags; ! 331: } else ! 332: soflags = so->so_proto->pr_flags; ! 333: if ((soflags & PR_CONNREQUIRED) || (so->so_state & SS_ISCONNECTED)) ! 334: sendnam = (struct mbuf *)0; ! 335: else ! 336: sendnam = nam; ! 337: ! 338: error = sosend(so, sendnam, (struct uio *)0, top, ! 339: (struct mbuf *)0, 0); ! 340: if (error == EWOULDBLOCK && rep) { ! 341: if (rep->r_flags & R_SOFTTERM) ! 342: error = EINTR; ! 343: else { ! 344: rep->r_flags |= R_MUSTRESEND; ! 345: error = 0; ! 346: } ! 347: } ! 348: /* ! 349: * Ignore socket errors?? ! 350: */ ! 351: if (error && error != EINTR && error != ERESTART) ! 352: error = 0; ! 353: return (error); ! 354: } ! 355: ! 356: /* ! 357: * Receive a Sun RPC Request/Reply. For SOCK_DGRAM, the work is all ! 358: * done by soreceive(), but for SOCK_STREAM we must deal with the Record ! 359: * Mark and consolidate the data into a new mbuf list. ! 360: * nb: Sometimes TCP passes the data up to soreceive() in long lists of ! 361: * small mbufs. ! 362: * For SOCK_STREAM we must be very careful to read an entire record once ! 363: * we have read any of it, even if the system call has been interrupted. ! 364: */ ! 365: nfs_receive(so, aname, mp, rep) ! 366: register struct socket *so; ! 367: struct mbuf **aname; ! 368: struct mbuf **mp; ! 369: register struct nfsreq *rep; ! 370: { ! 371: struct uio auio; ! 372: struct iovec aio; ! 373: register struct mbuf *m; ! 374: struct mbuf *m2, *mnew, **mbp; ! 375: caddr_t fcp, tcp; ! 376: u_long len; ! 377: struct mbuf **getnam; ! 378: int error, siz, mlen, soflags, rcvflg; ! 379: ! 380: /* ! 381: * Set up arguments for soreceive() ! 382: */ ! 383: *mp = (struct mbuf *)0; ! 384: *aname = (struct mbuf *)0; ! 385: if (rep) ! 386: soflags = rep->r_nmp->nm_soflags; ! 387: else ! 388: soflags = so->so_proto->pr_flags; ! 389: ! 390: /* ! 391: * For reliable protocols, lock against other senders/receivers ! 392: * in case a reconnect is necessary. ! 393: * For SOCK_STREAM, first get the Record Mark to find out how much ! 394: * more there is to get. ! 395: * We must lock the socket against other receivers ! 396: * until we have an entire rpc request/reply. ! 397: */ ! 398: if (soflags & PR_CONNREQUIRED) { ! 399: tryagain: ! 400: /* ! 401: * Check for fatal errors and resending request. ! 402: */ ! 403: if (rep) { ! 404: /* ! 405: * Ugh: If a reconnect attempt just happened, nm_so ! 406: * would have changed. NULL indicates a failed ! 407: * attempt that has essentially shut down this ! 408: * mount point. ! 409: */ ! 410: if (rep->r_mrep || (so = rep->r_nmp->nm_so) == NULL || ! 411: (rep->r_flags & R_SOFTTERM)) ! 412: return (EINTR); ! 413: while (rep->r_flags & R_MUSTRESEND) { ! 414: m = m_copym(rep->r_mreq, 0, M_COPYALL, M_WAIT); ! 415: nfsstats.rpcretries++; ! 416: if (error = nfs_send(so, rep->r_nmp->nm_nam, m, ! 417: rep)) ! 418: goto errout; ! 419: } ! 420: } ! 421: if ((soflags & PR_ATOMIC) == 0) { ! 422: aio.iov_base = (caddr_t) &len; ! 423: aio.iov_len = sizeof(u_long); ! 424: auio.uio_iov = &aio; ! 425: auio.uio_iovcnt = 1; ! 426: auio.uio_segflg = UIO_SYSSPACE; ! 427: auio.uio_rw = UIO_READ; ! 428: auio.uio_procp = (struct proc *)0; ! 429: auio.uio_offset = 0; ! 430: auio.uio_resid = sizeof(u_long); ! 431: do { ! 432: rcvflg = MSG_WAITALL; ! 433: error = soreceive(so, (struct mbuf **)0, &auio, ! 434: (struct mbuf **)0, (struct mbuf **)0, &rcvflg); ! 435: if (error == EWOULDBLOCK && rep) { ! 436: if (rep->r_flags & R_SOFTTERM) ! 437: return (EINTR); ! 438: if (rep->r_flags & R_MUSTRESEND) ! 439: goto tryagain; ! 440: } ! 441: } while (error == EWOULDBLOCK); ! 442: if (!error && auio.uio_resid > 0) { ! 443: if (rep) ! 444: log(LOG_INFO, ! 445: "short receive (%d/%d) from nfs server %s\n", ! 446: sizeof(u_long) - auio.uio_resid, ! 447: sizeof(u_long), ! 448: rep->r_nmp->nm_mountp->mnt_stat.f_mntfromname); ! 449: error = EPIPE; ! 450: } ! 451: if (error) ! 452: goto errout; ! 453: len = ntohl(len) & ~0x80000000; ! 454: /* ! 455: * This is SERIOUS! We are out of sync with the sender ! 456: * and forcing a disconnect/reconnect is all I can do. ! 457: */ ! 458: if (len > NFS_MAXPACKET) { ! 459: if (rep) ! 460: log(LOG_ERR, "%s (%d) from nfs server %s\n", ! 461: "impossible packet length", ! 462: len, ! 463: rep->r_nmp->nm_mountp->mnt_stat.f_mntfromname); ! 464: error = EFBIG; ! 465: goto errout; ! 466: } ! 467: auio.uio_resid = len; ! 468: do { ! 469: rcvflg = MSG_WAITALL; ! 470: error = soreceive(so, (struct mbuf **)0, ! 471: &auio, mp, (struct mbuf **)0, &rcvflg); ! 472: } while (error == EWOULDBLOCK || error == EINTR || ! 473: error == ERESTART); ! 474: if (!error && auio.uio_resid > 0) { ! 475: if (rep) ! 476: log(LOG_INFO, ! 477: "short receive (%d/%d) from nfs server %s\n", ! 478: len - auio.uio_resid, len, ! 479: rep->r_nmp->nm_mountp->mnt_stat.f_mntfromname); ! 480: error = EPIPE; ! 481: } ! 482: } else { ! 483: auio.uio_resid = len = 1000000; /* Anything Big */ ! 484: do { ! 485: rcvflg = 0; ! 486: error = soreceive(so, (struct mbuf **)0, ! 487: &auio, mp, (struct mbuf **)0, &rcvflg); ! 488: if (error == EWOULDBLOCK && rep) { ! 489: if (rep->r_flags & R_SOFTTERM) ! 490: return (EINTR); ! 491: if (rep->r_flags & R_MUSTRESEND) ! 492: goto tryagain; ! 493: } ! 494: } while (error == EWOULDBLOCK); ! 495: if (!error && *mp == NULL) ! 496: error = EPIPE; ! 497: len -= auio.uio_resid; ! 498: } ! 499: errout: ! 500: if (error && rep && error != EINTR && error != ERESTART) { ! 501: m_freem(*mp); ! 502: *mp = (struct mbuf *)0; ! 503: if (error != EPIPE && rep) ! 504: log(LOG_INFO, ! 505: "receive error %d from nfs server %s\n", ! 506: error, ! 507: rep->r_nmp->nm_mountp->mnt_stat.f_mntfromname); ! 508: nfs_disconnect(rep->r_nmp); ! 509: error = nfs_reconnect(rep, rep->r_nmp); ! 510: if (!error) ! 511: goto tryagain; ! 512: } ! 513: } else { ! 514: if (so->so_state & SS_ISCONNECTED) ! 515: getnam = (struct mbuf **)0; ! 516: else ! 517: getnam = aname; ! 518: auio.uio_resid = len = 1000000; ! 519: do { ! 520: rcvflg = 0; ! 521: error = soreceive(so, getnam, &auio, mp, ! 522: (struct mbuf **)0, &rcvflg); ! 523: if (error == EWOULDBLOCK && rep && ! 524: (rep->r_flags & R_SOFTTERM)) ! 525: return (EINTR); ! 526: } while (error == EWOULDBLOCK); ! 527: len -= auio.uio_resid; ! 528: } ! 529: if (error) { ! 530: m_freem(*mp); ! 531: *mp = (struct mbuf *)0; ! 532: } ! 533: /* ! 534: * Search for any mbufs that are not a multiple of 4 bytes long. ! 535: * These could cause pointer alignment problems, so copy them to ! 536: * well aligned mbufs. ! 537: */ ! 538: m = *mp; ! 539: mbp = mp; ! 540: while (m) { ! 541: /* ! 542: * All this for something that may never happen. ! 543: */ ! 544: if (m->m_next && (m->m_len & 0x3)) { ! 545: printf("nfs_rcv odd length!\n"); ! 546: mlen = 0; ! 547: while (m) { ! 548: fcp = mtod(m, caddr_t); ! 549: while (m->m_len > 0) { ! 550: if (mlen == 0) { ! 551: MGET(m2, M_WAIT, MT_DATA); ! 552: if (len >= MINCLSIZE) ! 553: MCLGET(m2, M_WAIT); ! 554: m2->m_len = 0; ! 555: mlen = M_TRAILINGSPACE(m2); ! 556: tcp = mtod(m2, caddr_t); ! 557: *mbp = m2; ! 558: mbp = &m2->m_next; ! 559: } ! 560: siz = MIN(mlen, m->m_len); ! 561: bcopy(fcp, tcp, siz); ! 562: m2->m_len += siz; ! 563: mlen -= siz; ! 564: len -= siz; ! 565: tcp += siz; ! 566: m->m_len -= siz; ! 567: fcp += siz; ! 568: } ! 569: MFREE(m, mnew); ! 570: m = mnew; ! 571: } ! 572: break; ! 573: } ! 574: len -= m->m_len; ! 575: mbp = &m->m_next; ! 576: m = m->m_next; ! 577: } ! 578: return (error); ! 579: } ! 580: ! 581: /* ! 582: * Implement receipt of reply on a socket. ! 583: * We must search through the list of received datagrams matching them ! 584: * with outstanding requests using the xid, until ours is found. ! 585: */ ! 586: /* ARGSUSED */ ! 587: nfs_reply(nmp, myrep) ! 588: struct nfsmount *nmp; ! 589: struct nfsreq *myrep; ! 590: { ! 591: register struct mbuf *m; ! 592: register struct nfsreq *rep; ! 593: register int error = 0; ! 594: u_long rxid; ! 595: struct mbuf *mp, *nam; ! 596: char *cp; ! 597: int cnt, xfer; ! 598: ! 599: /* ! 600: * Loop around until we get our own reply ! 601: */ ! 602: for (;;) { ! 603: /* ! 604: * Lock against other receivers so that I don't get stuck in ! 605: * sbwait() after someone else has received my reply for me. ! 606: * Also necessary for connection based protocols to avoid ! 607: * race conditions during a reconnect. ! 608: */ ! 609: nfs_solock(&nmp->nm_flag); ! 610: /* Already received, bye bye */ ! 611: if (myrep->r_mrep != NULL) { ! 612: nfs_sounlock(&nmp->nm_flag); ! 613: return (0); ! 614: } ! 615: /* ! 616: * Get the next Rpc reply off the socket ! 617: */ ! 618: if (error = nfs_receive(nmp->nm_so, &nam, &mp, myrep)) { ! 619: nfs_sounlock(&nmp->nm_flag); ! 620: ! 621: /* ! 622: * Ignore routing errors on connectionless protocols?? ! 623: */ ! 624: if (NFSIGNORE_SOERROR(nmp->nm_soflags, error)) { ! 625: nmp->nm_so->so_error = 0; ! 626: continue; ! 627: } ! 628: ! 629: /* ! 630: * Otherwise cleanup and return a fatal error. ! 631: */ ! 632: if (myrep->r_flags & R_TIMING) { ! 633: myrep->r_flags &= ~R_TIMING; ! 634: nmp->nm_rtt = -1; ! 635: } ! 636: if (myrep->r_flags & R_SENT) { ! 637: myrep->r_flags &= ~R_SENT; ! 638: nmp->nm_sent--; ! 639: } ! 640: return (error); ! 641: } ! 642: ! 643: /* ! 644: * Get the xid and check that it is an rpc reply ! 645: */ ! 646: m = mp; ! 647: while (m && m->m_len == 0) ! 648: m = m->m_next; ! 649: if (m == NULL) { ! 650: nfsstats.rpcinvalid++; ! 651: m_freem(mp); ! 652: nfs_sounlock(&nmp->nm_flag); ! 653: continue; ! 654: } ! 655: bcopy(mtod(m, caddr_t), (caddr_t)&rxid, NFSX_UNSIGNED); ! 656: /* ! 657: * Loop through the request list to match up the reply ! 658: * Iff no match, just drop the datagram ! 659: */ ! 660: m = mp; ! 661: rep = nfsreqh.r_next; ! 662: while (rep != &nfsreqh) { ! 663: if (rep->r_mrep == NULL && rxid == rep->r_xid) { ! 664: /* Found it.. */ ! 665: rep->r_mrep = m; ! 666: /* ! 667: * Update timing ! 668: */ ! 669: if (rep->r_flags & R_TIMING) { ! 670: nfs_updatetimer(rep->r_nmp); ! 671: rep->r_flags &= ~R_TIMING; ! 672: rep->r_nmp->nm_rtt = -1; ! 673: } ! 674: if (rep->r_flags & R_SENT) { ! 675: rep->r_flags &= ~R_SENT; ! 676: rep->r_nmp->nm_sent--; ! 677: } ! 678: break; ! 679: } ! 680: rep = rep->r_next; ! 681: } ! 682: nfs_sounlock(&nmp->nm_flag); ! 683: if (nam) ! 684: m_freem(nam); ! 685: /* ! 686: * If not matched to a request, drop it. ! 687: * If it's mine, get out. ! 688: */ ! 689: if (rep == &nfsreqh) { ! 690: nfsstats.rpcunexpected++; ! 691: m_freem(m); ! 692: } else if (rep == myrep) ! 693: return (0); ! 694: } ! 695: } ! 696: ! 697: /* ! 698: * nfs_request - goes something like this ! 699: * - fill in request struct ! 700: * - links it into list ! 701: * - calls nfs_send() for first transmit ! 702: * - calls nfs_receive() to get reply ! 703: * - break down rpc header and return with nfs reply pointed to ! 704: * by mrep or error ! 705: * nb: always frees up mreq mbuf list ! 706: */ ! 707: nfs_request(vp, mreq, xid, procnum, procp, tryhard, mp, mrp, mdp, dposp) ! 708: struct vnode *vp; ! 709: struct mbuf *mreq; ! 710: u_long xid; ! 711: int procnum; ! 712: struct proc *procp; ! 713: int tryhard; ! 714: struct mount *mp; ! 715: struct mbuf **mrp; ! 716: struct mbuf **mdp; ! 717: caddr_t *dposp; ! 718: { ! 719: register struct mbuf *m, *mrep; ! 720: register struct nfsreq *rep; ! 721: register u_long *tl; ! 722: register int len; ! 723: struct nfsmount *nmp; ! 724: struct mbuf *md; ! 725: struct nfsreq *reph; ! 726: caddr_t dpos; ! 727: char *cp2; ! 728: int t1; ! 729: int s, compressed; ! 730: int error = 0; ! 731: ! 732: nmp = VFSTONFS(mp); ! 733: m = mreq; ! 734: MALLOC(rep, struct nfsreq *, sizeof(struct nfsreq), M_NFSREQ, M_WAITOK); ! 735: rep->r_xid = xid; ! 736: rep->r_nmp = nmp; ! 737: rep->r_vp = vp; ! 738: rep->r_procp = procp; ! 739: if ((nmp->nm_flag & NFSMNT_SOFT) || ! 740: ((nmp->nm_flag & NFSMNT_SPONGY) && !tryhard)) ! 741: rep->r_retry = nmp->nm_retry; ! 742: else ! 743: rep->r_retry = NFS_MAXREXMIT + 1; /* past clip limit */ ! 744: rep->r_flags = rep->r_rexmit = 0; ! 745: /* ! 746: * Three cases: ! 747: * - non-idempotent requests on SOCK_DGRAM use NFS_MINIDEMTIMEO ! 748: * - idempotent requests on SOCK_DGRAM use 0 ! 749: * - Reliable transports, NFS_RELIABLETIMEO ! 750: * Timeouts are still done on reliable transports to ensure detection ! 751: * of excessive connection delay. ! 752: */ ! 753: if (nmp->nm_sotype != SOCK_DGRAM) ! 754: rep->r_timerinit = -NFS_RELIABLETIMEO; ! 755: else if (nonidempotent[procnum]) ! 756: rep->r_timerinit = -NFS_MINIDEMTIMEO; ! 757: else ! 758: rep->r_timerinit = 0; ! 759: rep->r_timer = rep->r_timerinit; ! 760: rep->r_mrep = NULL; ! 761: len = 0; ! 762: while (m) { ! 763: len += m->m_len; ! 764: m = m->m_next; ! 765: } ! 766: mreq->m_pkthdr.len = len; ! 767: mreq->m_pkthdr.rcvif = (struct ifnet *)0; ! 768: compressed = 0; ! 769: m = mreq; ! 770: if ((nmp->nm_flag & NFSMNT_COMPRESS) && compressrequest[procnum]) { ! 771: mreq = nfs_compress(mreq); ! 772: if (mreq != m) { ! 773: len = mreq->m_pkthdr.len; ! 774: compressed++; ! 775: } ! 776: } ! 777: /* ! 778: * For non-atomic protocols, insert a Sun RPC Record Mark. ! 779: */ ! 780: if ((nmp->nm_soflags & PR_ATOMIC) == 0) { ! 781: M_PREPEND(mreq, sizeof(u_long), M_WAIT); ! 782: *mtod(mreq, u_long *) = htonl(0x80000000 | len); ! 783: } ! 784: rep->r_mreq = mreq; ! 785: ! 786: /* ! 787: * Do the client side RPC. ! 788: */ ! 789: nfsstats.rpcrequests++; ! 790: /* ! 791: * Chain request into list of outstanding requests. Be sure ! 792: * to put it LAST so timer finds oldest requests first. ! 793: */ ! 794: s = splnet(); ! 795: reph = &nfsreqh; ! 796: reph->r_prev->r_next = rep; ! 797: rep->r_prev = reph->r_prev; ! 798: reph->r_prev = rep; ! 799: rep->r_next = reph; ! 800: /* ! 801: * If backing off another request or avoiding congestion, don't ! 802: * send this one now but let timer do it. If not timing a request, ! 803: * do it now. ! 804: */ ! 805: if (nmp->nm_sent <= 0 || nmp->nm_sotype != SOCK_DGRAM || ! 806: (nmp->nm_currexmit == 0 && nmp->nm_sent < nmp->nm_window)) { ! 807: nmp->nm_sent++; ! 808: rep->r_flags |= R_SENT; ! 809: if (nmp->nm_rtt == -1) { ! 810: nmp->nm_rtt = 0; ! 811: rep->r_flags |= R_TIMING; ! 812: } ! 813: splx(s); ! 814: m = m_copym(mreq, 0, M_COPYALL, M_WAIT); ! 815: if (nmp->nm_soflags & PR_CONNREQUIRED) ! 816: nfs_solock(&nmp->nm_flag); ! 817: error = nfs_send(nmp->nm_so, nmp->nm_nam, m, rep); ! 818: if (nmp->nm_soflags & PR_CONNREQUIRED) ! 819: nfs_sounlock(&nmp->nm_flag); ! 820: if (error && NFSIGNORE_SOERROR(nmp->nm_soflags, error)) ! 821: nmp->nm_so->so_error = error = 0; ! 822: } else ! 823: splx(s); ! 824: ! 825: /* ! 826: * Wait for the reply from our send or the timer's. ! 827: */ ! 828: if (!error) ! 829: error = nfs_reply(nmp, rep); ! 830: ! 831: /* ! 832: * RPC done, unlink the request. ! 833: */ ! 834: s = splnet(); ! 835: rep->r_prev->r_next = rep->r_next; ! 836: rep->r_next->r_prev = rep->r_prev; ! 837: splx(s); ! 838: ! 839: /* ! 840: * If there was a successful reply and a tprintf msg. ! 841: * tprintf a response. ! 842: */ ! 843: if (!error && (rep->r_flags & R_TPRINTFMSG)) ! 844: nfs_msg(rep->r_procp, nmp->nm_mountp->mnt_stat.f_mntfromname, ! 845: "is alive again"); ! 846: m_freem(rep->r_mreq); ! 847: mrep = rep->r_mrep; ! 848: FREE((caddr_t)rep, M_NFSREQ); ! 849: if (error) ! 850: return (error); ! 851: ! 852: if (compressed) ! 853: mrep = nfs_uncompress(mrep); ! 854: md = mrep; ! 855: /* ! 856: * break down the rpc header and check if ok ! 857: */ ! 858: dpos = mtod(md, caddr_t); ! 859: nfsm_disect(tl, u_long *, 5*NFSX_UNSIGNED); ! 860: tl += 2; ! 861: if (*tl++ == rpc_msgdenied) { ! 862: if (*tl == rpc_mismatch) ! 863: error = EOPNOTSUPP; ! 864: else ! 865: error = EACCES; ! 866: m_freem(mrep); ! 867: return (error); ! 868: } ! 869: /* ! 870: * skip over the auth_verf, someday we may want to cache auth_short's ! 871: * for nfs_reqhead(), but for now just dump it ! 872: */ ! 873: if (*++tl != 0) { ! 874: len = nfsm_rndup(fxdr_unsigned(long, *tl)); ! 875: nfsm_adv(len); ! 876: } ! 877: nfsm_disect(tl, u_long *, NFSX_UNSIGNED); ! 878: /* 0 == ok */ ! 879: if (*tl == 0) { ! 880: nfsm_disect(tl, u_long *, NFSX_UNSIGNED); ! 881: if (*tl != 0) { ! 882: error = fxdr_unsigned(int, *tl); ! 883: m_freem(mrep); ! 884: return (error); ! 885: } ! 886: *mrp = mrep; ! 887: *mdp = md; ! 888: *dposp = dpos; ! 889: return (0); ! 890: } ! 891: m_freem(mrep); ! 892: return (EPROTONOSUPPORT); ! 893: nfsmout: ! 894: return (error); ! 895: } ! 896: ! 897: /* ! 898: * Get a request for the server main loop ! 899: * - receive a request via. nfs_soreceive() ! 900: * - verify it ! 901: * - fill in the cred struct. ! 902: */ ! 903: nfs_getreq(so, prog, vers, maxproc, nam, mrp, mdp, dposp, retxid, procnum, cr, ! 904: msk, mtch, wascomp) ! 905: struct socket *so; ! 906: u_long prog; ! 907: u_long vers; ! 908: int maxproc; ! 909: struct mbuf **nam; ! 910: struct mbuf **mrp; ! 911: struct mbuf **mdp; ! 912: caddr_t *dposp; ! 913: u_long *retxid; ! 914: u_long *procnum; ! 915: register struct ucred *cr; ! 916: struct mbuf *msk, *mtch; ! 917: int *wascomp; ! 918: { ! 919: register int i; ! 920: register u_long *tl; ! 921: register long t1; ! 922: caddr_t dpos, cp2; ! 923: int error = 0; ! 924: struct mbuf *mrep, *md; ! 925: int len; ! 926: ! 927: if (so->so_proto->pr_flags & PR_CONNREQUIRED) { ! 928: error = nfs_receive(so, nam, &mrep, (struct nfsreq *)0); ! 929: } else { ! 930: mrep = (struct mbuf *)0; ! 931: do { ! 932: if (mrep) { ! 933: m_freem(*nam); ! 934: m_freem(mrep); ! 935: } ! 936: error = nfs_receive(so, nam, &mrep, (struct nfsreq *)0); ! 937: } while (!error && nfs_badnam(*nam, msk, mtch)); ! 938: } ! 939: if (error) ! 940: return (error); ! 941: md = mrep; ! 942: mrep = nfs_uncompress(mrep); ! 943: if (mrep != md) { ! 944: *wascomp = 1; ! 945: md = mrep; ! 946: } else ! 947: *wascomp = 0; ! 948: dpos = mtod(mrep, caddr_t); ! 949: nfsm_disect(tl, u_long *, 10*NFSX_UNSIGNED); ! 950: *retxid = *tl++; ! 951: if (*tl++ != rpc_call) { ! 952: m_freem(mrep); ! 953: return (ERPCMISMATCH); ! 954: } ! 955: if (*tl++ != rpc_vers) { ! 956: m_freem(mrep); ! 957: return (ERPCMISMATCH); ! 958: } ! 959: if (*tl++ != prog) { ! 960: m_freem(mrep); ! 961: return (EPROGUNAVAIL); ! 962: } ! 963: if (*tl++ != vers) { ! 964: m_freem(mrep); ! 965: return (EPROGMISMATCH); ! 966: } ! 967: *procnum = fxdr_unsigned(u_long, *tl++); ! 968: if (*procnum == NFSPROC_NULL) { ! 969: *mrp = mrep; ! 970: return (0); ! 971: } ! 972: if (*procnum > maxproc || *tl++ != rpc_auth_unix) { ! 973: m_freem(mrep); ! 974: return (EPROCUNAVAIL); ! 975: } ! 976: len = fxdr_unsigned(int, *tl++); ! 977: if (len < 0 || len > RPCAUTH_MAXSIZ) { ! 978: m_freem(mrep); ! 979: return (EBADRPC); ! 980: } ! 981: len = fxdr_unsigned(int, *++tl); ! 982: if (len < 0 || len > NFS_MAXNAMLEN) { ! 983: m_freem(mrep); ! 984: return (EBADRPC); ! 985: } ! 986: nfsm_adv(nfsm_rndup(len)); ! 987: nfsm_disect(tl, u_long *, 3*NFSX_UNSIGNED); ! 988: cr->cr_uid = fxdr_unsigned(uid_t, *tl++); ! 989: cr->cr_gid = fxdr_unsigned(gid_t, *tl++); ! 990: len = fxdr_unsigned(int, *tl); ! 991: if (len < 0 || len > RPCAUTH_UNIXGIDS) { ! 992: m_freem(mrep); ! 993: return (EBADRPC); ! 994: } ! 995: nfsm_disect(tl, u_long *, (len + 2)*NFSX_UNSIGNED); ! 996: for (i = 1; i <= len; i++) ! 997: if (i < NGROUPS) ! 998: cr->cr_groups[i] = fxdr_unsigned(gid_t, *tl++); ! 999: else ! 1000: tl++; ! 1001: cr->cr_ngroups = (len >= NGROUPS) ? NGROUPS : (len + 1); ! 1002: /* ! 1003: * Do we have any use for the verifier. ! 1004: * According to the "Remote Procedure Call Protocol Spec." it ! 1005: * should be AUTH_NULL, but some clients make it AUTH_UNIX? ! 1006: * For now, just skip over it ! 1007: */ ! 1008: len = fxdr_unsigned(int, *++tl); ! 1009: if (len < 0 || len > RPCAUTH_MAXSIZ) { ! 1010: m_freem(mrep); ! 1011: return (EBADRPC); ! 1012: } ! 1013: if (len > 0) ! 1014: nfsm_adv(nfsm_rndup(len)); ! 1015: *mrp = mrep; ! 1016: *mdp = md; ! 1017: *dposp = dpos; ! 1018: return (0); ! 1019: nfsmout: ! 1020: return (error); ! 1021: } ! 1022: ! 1023: /* ! 1024: * Generate the rpc reply header ! 1025: * siz arg. is used to decide if adding a cluster is worthwhile ! 1026: */ ! 1027: nfs_rephead(siz, retxid, err, mrq, mbp, bposp) ! 1028: int siz; ! 1029: u_long retxid; ! 1030: int err; ! 1031: struct mbuf **mrq; ! 1032: struct mbuf **mbp; ! 1033: caddr_t *bposp; ! 1034: { ! 1035: register u_long *tl; ! 1036: register long t1; ! 1037: caddr_t bpos; ! 1038: struct mbuf *mreq, *mb, *mb2; ! 1039: ! 1040: NFSMGETHDR(mreq); ! 1041: mb = mreq; ! 1042: if ((siz+RPC_REPLYSIZ) > MHLEN) ! 1043: MCLGET(mreq, M_WAIT); ! 1044: tl = mtod(mreq, u_long *); ! 1045: mreq->m_len = 6*NFSX_UNSIGNED; ! 1046: bpos = ((caddr_t)tl)+mreq->m_len; ! 1047: *tl++ = retxid; ! 1048: *tl++ = rpc_reply; ! 1049: if (err == ERPCMISMATCH) { ! 1050: *tl++ = rpc_msgdenied; ! 1051: *tl++ = rpc_mismatch; ! 1052: *tl++ = txdr_unsigned(2); ! 1053: *tl = txdr_unsigned(2); ! 1054: } else { ! 1055: *tl++ = rpc_msgaccepted; ! 1056: *tl++ = 0; ! 1057: *tl++ = 0; ! 1058: switch (err) { ! 1059: case EPROGUNAVAIL: ! 1060: *tl = txdr_unsigned(RPC_PROGUNAVAIL); ! 1061: break; ! 1062: case EPROGMISMATCH: ! 1063: *tl = txdr_unsigned(RPC_PROGMISMATCH); ! 1064: nfsm_build(tl, u_long *, 2*NFSX_UNSIGNED); ! 1065: *tl++ = txdr_unsigned(2); ! 1066: *tl = txdr_unsigned(2); /* someday 3 */ ! 1067: break; ! 1068: case EPROCUNAVAIL: ! 1069: *tl = txdr_unsigned(RPC_PROCUNAVAIL); ! 1070: break; ! 1071: default: ! 1072: *tl = 0; ! 1073: if (err != VNOVAL) { ! 1074: nfsm_build(tl, u_long *, NFSX_UNSIGNED); ! 1075: *tl = txdr_unsigned(err); ! 1076: } ! 1077: break; ! 1078: }; ! 1079: } ! 1080: *mrq = mreq; ! 1081: *mbp = mb; ! 1082: *bposp = bpos; ! 1083: if (err != 0 && err != VNOVAL) ! 1084: nfsstats.srvrpc_errs++; ! 1085: return (0); ! 1086: } ! 1087: ! 1088: /* ! 1089: * Nfs timer routine ! 1090: * Scan the nfsreq list and retranmit any requests that have timed out ! 1091: * To avoid retransmission attempts on STREAM sockets (in the future) make ! 1092: * sure to set the r_retry field to 0 (implies nm_retry == 0). ! 1093: */ ! 1094: nfs_timer() ! 1095: { ! 1096: register struct nfsreq *rep; ! 1097: register struct mbuf *m; ! 1098: register struct socket *so; ! 1099: register struct nfsmount *nmp; ! 1100: int s, error; ! 1101: ! 1102: s = splnet(); ! 1103: for (rep = nfsreqh.r_next; rep != &nfsreqh; rep = rep->r_next) { ! 1104: nmp = rep->r_nmp; ! 1105: if (rep->r_mrep || (rep->r_flags & R_SOFTTERM) || ! 1106: (so = nmp->nm_so) == NULL) ! 1107: continue; ! 1108: if ((nmp->nm_flag & NFSMNT_INT) && nfs_sigintr(rep->r_procp)) { ! 1109: rep->r_flags |= R_SOFTTERM; ! 1110: continue; ! 1111: } ! 1112: if (rep->r_flags & R_TIMING) /* update rtt in mount */ ! 1113: nmp->nm_rtt++; ! 1114: /* If not timed out */ ! 1115: if (++rep->r_timer < nmp->nm_rto) ! 1116: continue; ! 1117: /* Do backoff and save new timeout in mount */ ! 1118: if (rep->r_flags & R_TIMING) { ! 1119: nfs_backofftimer(nmp); ! 1120: rep->r_flags &= ~R_TIMING; ! 1121: nmp->nm_rtt = -1; ! 1122: } ! 1123: if (rep->r_flags & R_SENT) { ! 1124: rep->r_flags &= ~R_SENT; ! 1125: nmp->nm_sent--; ! 1126: } ! 1127: ! 1128: /* ! 1129: * Check for too many retries on soft mount. ! 1130: * nb: For hard mounts, r_retry == NFS_MAXREXMIT+1 ! 1131: */ ! 1132: if (++rep->r_rexmit > NFS_MAXREXMIT) ! 1133: rep->r_rexmit = NFS_MAXREXMIT; ! 1134: ! 1135: /* ! 1136: * Check for server not responding ! 1137: */ ! 1138: if ((rep->r_flags & R_TPRINTFMSG) == 0 && ! 1139: rep->r_rexmit > NFS_FISHY) { ! 1140: nfs_msg(rep->r_procp, ! 1141: nmp->nm_mountp->mnt_stat.f_mntfromname, ! 1142: "not responding"); ! 1143: rep->r_flags |= R_TPRINTFMSG; ! 1144: } ! 1145: if (rep->r_rexmit >= rep->r_retry) { /* too many */ ! 1146: nfsstats.rpctimeouts++; ! 1147: rep->r_flags |= R_SOFTTERM; ! 1148: continue; ! 1149: } ! 1150: if (nmp->nm_sotype != SOCK_DGRAM) ! 1151: continue; ! 1152: ! 1153: /* ! 1154: * If there is enough space and the window allows.. ! 1155: * Resend it ! 1156: */ ! 1157: if (sbspace(&so->so_snd) >= rep->r_mreq->m_pkthdr.len && ! 1158: nmp->nm_sent < nmp->nm_window && ! 1159: (m = m_copym(rep->r_mreq, 0, M_COPYALL, M_DONTWAIT))){ ! 1160: nfsstats.rpcretries++; ! 1161: if ((nmp->nm_flag & NFSMNT_NOCONN) == 0) ! 1162: error = (*so->so_proto->pr_usrreq)(so, PRU_SEND, m, ! 1163: (caddr_t)0, (struct mbuf *)0, (struct mbuf *)0); ! 1164: else ! 1165: error = (*so->so_proto->pr_usrreq)(so, PRU_SEND, m, ! 1166: nmp->nm_nam, (struct mbuf *)0, (struct mbuf *)0); ! 1167: if (error) { ! 1168: if (NFSIGNORE_SOERROR(nmp->nm_soflags, error)) ! 1169: so->so_error = 0; ! 1170: } else { ! 1171: /* ! 1172: * We need to time the request even though we ! 1173: * are retransmitting. ! 1174: */ ! 1175: nmp->nm_rtt = 0; ! 1176: nmp->nm_sent++; ! 1177: rep->r_flags |= (R_SENT|R_TIMING); ! 1178: rep->r_timer = rep->r_timerinit; ! 1179: } ! 1180: } ! 1181: } ! 1182: splx(s); ! 1183: timeout(nfs_timer, (caddr_t)0, hz/NFS_HZ); ! 1184: } ! 1185: ! 1186: /* ! 1187: * NFS timer update and backoff. The "Jacobson/Karels/Karn" scheme is ! 1188: * used here. The timer state is held in the nfsmount structure and ! 1189: * a single request is used to clock the response. When successful ! 1190: * the rtt smoothing in nfs_updatetimer is used, when failed the backoff ! 1191: * is done by nfs_backofftimer. We also log failure messages in these ! 1192: * routines. ! 1193: * ! 1194: * Congestion variables are held in the nfshost structure which ! 1195: * is referenced by nfsmounts and shared per-server. This separation ! 1196: * makes it possible to do per-mount timing which allows varying disk ! 1197: * access times to be dealt with, while preserving a network oriented ! 1198: * congestion control scheme. ! 1199: * ! 1200: * The windowing implements the Jacobson/Karels slowstart algorithm ! 1201: * with adjusted scaling factors. We start with one request, then send ! 1202: * 4 more after each success until the ssthresh limit is reached, then ! 1203: * we increment at a rate proportional to the window. On failure, we ! 1204: * remember 3/4 the current window and clamp the send limit to 1. Note ! 1205: * ICMP source quench is not reflected in so->so_error so we ignore that ! 1206: * for now. ! 1207: * ! 1208: * NFS behaves much more like a transport protocol with these changes, ! 1209: * shedding the teenage pedal-to-the-metal tendencies of "other" ! 1210: * implementations. ! 1211: * ! 1212: * Timers and congestion avoidance by Tom Talpey, Open Software Foundation. ! 1213: */ ! 1214: ! 1215: /* ! 1216: * The TCP algorithm was not forgiving enough. Because the NFS server ! 1217: * responds only after performing lookups/diskio/etc, we have to be ! 1218: * more prepared to accept a spiky variance. The TCP algorithm is: ! 1219: * TCP_RTO(nmp) ((((nmp)->nm_srtt >> 2) + (nmp)->nm_rttvar) >> 1) ! 1220: */ ! 1221: #define NFS_RTO(nmp) (((nmp)->nm_srtt >> 3) + (nmp)->nm_rttvar) ! 1222: ! 1223: nfs_updatetimer(nmp) ! 1224: register struct nfsmount *nmp; ! 1225: { ! 1226: ! 1227: /* If retransmitted, clear and return */ ! 1228: if (nmp->nm_rexmit || nmp->nm_currexmit) { ! 1229: nmp->nm_rexmit = nmp->nm_currexmit = 0; ! 1230: return; ! 1231: } ! 1232: /* If have a measurement, do smoothing */ ! 1233: if (nmp->nm_srtt) { ! 1234: register short delta; ! 1235: delta = nmp->nm_rtt - (nmp->nm_srtt >> 3); ! 1236: if ((nmp->nm_srtt += delta) <= 0) ! 1237: nmp->nm_srtt = 1; ! 1238: if (delta < 0) ! 1239: delta = -delta; ! 1240: delta -= (nmp->nm_rttvar >> 2); ! 1241: if ((nmp->nm_rttvar += delta) <= 0) ! 1242: nmp->nm_rttvar = 1; ! 1243: /* Else initialize */ ! 1244: } else { ! 1245: nmp->nm_rttvar = nmp->nm_rtt << 1; ! 1246: if (nmp->nm_rttvar == 0) nmp->nm_rttvar = 2; ! 1247: nmp->nm_srtt = nmp->nm_rttvar << 2; ! 1248: } ! 1249: /* Compute new Retransmission TimeOut and clip */ ! 1250: nmp->nm_rto = NFS_RTO(nmp); ! 1251: if (nmp->nm_rto < NFS_MINTIMEO) ! 1252: nmp->nm_rto = NFS_MINTIMEO; ! 1253: else if (nmp->nm_rto > NFS_MAXTIMEO) ! 1254: nmp->nm_rto = NFS_MAXTIMEO; ! 1255: ! 1256: /* Update window estimate */ ! 1257: if (nmp->nm_window < nmp->nm_ssthresh) /* quickly */ ! 1258: nmp->nm_window += 4; ! 1259: else { /* slowly */ ! 1260: register long incr = ++nmp->nm_winext; ! 1261: incr = (incr * incr) / nmp->nm_window; ! 1262: if (incr > 0) { ! 1263: nmp->nm_winext = 0; ! 1264: ++nmp->nm_window; ! 1265: } ! 1266: } ! 1267: if (nmp->nm_window > NFS_MAXWINDOW) ! 1268: nmp->nm_window = NFS_MAXWINDOW; ! 1269: } ! 1270: ! 1271: nfs_backofftimer(nmp) ! 1272: register struct nfsmount *nmp; ! 1273: { ! 1274: register unsigned long newrto; ! 1275: ! 1276: /* Clip shift count */ ! 1277: if (++nmp->nm_rexmit > 8 * sizeof nmp->nm_rto) ! 1278: nmp->nm_rexmit = 8 * sizeof nmp->nm_rto; ! 1279: /* Back off RTO exponentially */ ! 1280: newrto = NFS_RTO(nmp); ! 1281: newrto <<= (nmp->nm_rexmit - 1); ! 1282: if (newrto == 0 || newrto > NFS_MAXTIMEO) ! 1283: newrto = NFS_MAXTIMEO; ! 1284: nmp->nm_rto = newrto; ! 1285: ! 1286: /* If too many retries, message, assume a bogus RTT and re-measure */ ! 1287: if (nmp->nm_currexmit < nmp->nm_rexmit) { ! 1288: nmp->nm_currexmit = nmp->nm_rexmit; ! 1289: if (nmp->nm_currexmit >= nfsrexmtthresh) { ! 1290: if (nmp->nm_currexmit == nfsrexmtthresh) { ! 1291: nmp->nm_rttvar += (nmp->nm_srtt >> 2); ! 1292: nmp->nm_srtt = 0; ! 1293: } ! 1294: } ! 1295: } ! 1296: /* Close down window but remember this point (3/4 current) for later */ ! 1297: nmp->nm_ssthresh = ((nmp->nm_window << 1) + nmp->nm_window) >> 2; ! 1298: nmp->nm_window = 1; ! 1299: nmp->nm_winext = 0; ! 1300: } ! 1301: ! 1302: /* ! 1303: * Test for a termination signal pending on procp. ! 1304: * This is used for NFSMNT_INT mounts. ! 1305: */ ! 1306: nfs_sigintr(p) ! 1307: register struct proc *p; ! 1308: { ! 1309: if (p && p->p_sig && (((p->p_sig &~ p->p_sigmask) &~ p->p_sigignore) & ! 1310: NFSINT_SIGMASK)) ! 1311: return (1); ! 1312: else ! 1313: return (0); ! 1314: } ! 1315: ! 1316: nfs_msg(p, server, msg) ! 1317: struct proc *p; ! 1318: char *server, *msg; ! 1319: { ! 1320: tpr_t tpr; ! 1321: ! 1322: if (p) ! 1323: tpr = tprintf_open(p); ! 1324: else ! 1325: tpr = NULL; ! 1326: tprintf(tpr, "nfs server %s: %s\n", server, msg); ! 1327: tprintf_close(tpr); ! 1328: } ! 1329: ! 1330: /* ! 1331: * Lock a socket against others. ! 1332: * Necessary for STREAM sockets to ensure you get an entire rpc request/reply ! 1333: * and also to avoid race conditions between the processes with nfs requests ! 1334: * in progress when a reconnect is necessary. ! 1335: */ ! 1336: nfs_solock(flagp) ! 1337: register int *flagp; ! 1338: { ! 1339: ! 1340: while (*flagp & NFSMNT_SCKLOCK) { ! 1341: *flagp |= NFSMNT_WANTSCK; ! 1342: (void) tsleep((caddr_t)flagp, PZERO-1, "nfsolck", 0); ! 1343: } ! 1344: *flagp |= NFSMNT_SCKLOCK; ! 1345: } ! 1346: ! 1347: /* ! 1348: * Unlock the stream socket for others. ! 1349: */ ! 1350: nfs_sounlock(flagp) ! 1351: register int *flagp; ! 1352: { ! 1353: ! 1354: if ((*flagp & NFSMNT_SCKLOCK) == 0) ! 1355: panic("nfs sounlock"); ! 1356: *flagp &= ~NFSMNT_SCKLOCK; ! 1357: if (*flagp & NFSMNT_WANTSCK) { ! 1358: *flagp &= ~NFSMNT_WANTSCK; ! 1359: wakeup((caddr_t)flagp); ! 1360: } ! 1361: } ! 1362: ! 1363: /* ! 1364: * This function compares two net addresses by family and returns TRUE ! 1365: * if they are the same. ! 1366: * If there is any doubt, return FALSE. ! 1367: */ ! 1368: nfs_netaddr_match(nam1, nam2) ! 1369: struct mbuf *nam1, *nam2; ! 1370: { ! 1371: register struct sockaddr *saddr1, *saddr2; ! 1372: ! 1373: saddr1 = mtod(nam1, struct sockaddr *); ! 1374: saddr2 = mtod(nam2, struct sockaddr *); ! 1375: if (saddr1->sa_family != saddr2->sa_family) ! 1376: return (0); ! 1377: ! 1378: /* ! 1379: * Must do each address family separately since unused fields ! 1380: * are undefined values and not always zeroed. ! 1381: */ ! 1382: switch (saddr1->sa_family) { ! 1383: case AF_INET: ! 1384: if (((struct sockaddr_in *)saddr1)->sin_addr.s_addr == ! 1385: ((struct sockaddr_in *)saddr2)->sin_addr.s_addr) ! 1386: return (1); ! 1387: break; ! 1388: default: ! 1389: break; ! 1390: }; ! 1391: return (0); ! 1392: } ! 1393: ! 1394: /* ! 1395: * Check the hostname fields for nfsd's mask and match fields. ! 1396: * By address family: ! 1397: * - Bitwise AND the mask with the host address field ! 1398: * - Compare for == with match ! 1399: * return TRUE if not equal ! 1400: */ ! 1401: nfs_badnam(nam, msk, mtch) ! 1402: register struct mbuf *nam, *msk, *mtch; ! 1403: { ! 1404: switch (mtod(nam, struct sockaddr *)->sa_family) { ! 1405: case AF_INET: ! 1406: return ((mtod(nam, struct sockaddr_in *)->sin_addr.s_addr & ! 1407: mtod(msk, struct sockaddr_in *)->sin_addr.s_addr) != ! 1408: mtod(mtch, struct sockaddr_in *)->sin_addr.s_addr); ! 1409: default: ! 1410: printf("nfs_badmatch, unknown sa_family\n"); ! 1411: return (0); ! 1412: }; ! 1413: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.