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