xref: /original-bsd/sys/nfs/nfs_socket.c (revision 07d71086)
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  * Redistribution and use in source and binary forms are permitted
9  * provided that the above copyright notice and this paragraph are
10  * duplicated in all such forms and that any documentation,
11  * advertising materials, and other materials related to such
12  * distribution and use acknowledge that the software was developed
13  * by the University of California, Berkeley.  The name of the
14  * University may not be used to endorse or promote products derived
15  * from this software without specific prior written permission.
16  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
19  *
20  *	@(#)nfs_socket.c	7.2 (Berkeley) 07/06/89
21  */
22 
23 /*
24  * Socket operations for use by nfs (similar to uipc_socket.c, but never
25  * with copies to/from a uio vector)
26  * NB: For now, they only work for UDP datagram sockets.
27  * (Use on stream sockets would require some record boundary mark in the
28  *  stream such as Sun's RM (Section 3.2 of the Sun RPC Message Protocol
29  *  manual, in Networking on the Sun Workstation, Part #800-1324-03
30  *  and different versions of send, receive and reply that do not assume
31  *  an atomic protocol
32  */
33 
34 #include "types.h"
35 #include "param.h"
36 #include "uio.h"
37 #include "user.h"
38 #include "mount.h"
39 #include "kernel.h"
40 #include "malloc.h"
41 #include "mbuf.h"
42 #include "vnode.h"
43 #include "domain.h"
44 #include "protosw.h"
45 #include "socket.h"
46 #include "socketvar.h"
47 #include "netinet/in.h"
48 #include "rpcv2.h"
49 #include "nfsv2.h"
50 #include "nfs.h"
51 #include "xdr_subs.h"
52 #include "nfsm_subs.h"
53 #include "nfsmount.h"
54 
55 #define	TRUE	1
56 
57 /* set lock on sockbuf sb, sleep at neg prio */
58 #define nfs_sblock(sb) { \
59 	while ((sb)->sb_flags & SB_LOCK) { \
60 		(sb)->sb_flags |= SB_WANT; \
61 		sleep((caddr_t)&(sb)->sb_flags, PZERO-1); \
62 	} \
63 	(sb)->sb_flags |= SB_LOCK; \
64 }
65 
66 /*
67  * External data, mostly RPC constants in XDR form
68  */
69 extern u_long rpc_reply, rpc_msgdenied, rpc_mismatch, rpc_vers, rpc_auth_unix,
70 	rpc_msgaccepted, rpc_call;
71 extern u_long nfs_prog, nfs_vers;
72 int	nfsrv_null(),
73 	nfsrv_getattr(),
74 	nfsrv_setattr(),
75 	nfsrv_lookup(),
76 	nfsrv_readlink(),
77 	nfsrv_read(),
78 	nfsrv_write(),
79 	nfsrv_create(),
80 	nfsrv_remove(),
81 	nfsrv_rename(),
82 	nfsrv_link(),
83 	nfsrv_symlink(),
84 	nfsrv_mkdir(),
85 	nfsrv_rmdir(),
86 	nfsrv_readdir(),
87 	nfsrv_statfs(),
88 	nfsrv_noop();
89 
90 int (*nfsrv_procs[NFS_NPROCS])() = {
91 	nfsrv_null,
92 	nfsrv_getattr,
93 	nfsrv_setattr,
94 	nfsrv_noop,
95 	nfsrv_lookup,
96 	nfsrv_readlink,
97 	nfsrv_read,
98 	nfsrv_noop,
99 	nfsrv_write,
100 	nfsrv_create,
101 	nfsrv_remove,
102 	nfsrv_rename,
103 	nfsrv_link,
104 	nfsrv_symlink,
105 	nfsrv_mkdir,
106 	nfsrv_rmdir,
107 	nfsrv_readdir,
108 	nfsrv_statfs,
109 };
110 
111 
112 /*
113  * This is a stripped down version of sosend() specific to
114  * udp/ip and uses the mbuf list provdied
115  */
116 nfs_udpsend(so, nam, top, flags, siz)
117 	register struct socket *so;
118 	struct mbuf *nam;
119 	struct mbuf *top;
120 	int flags;
121 	int siz;
122 {
123 	register int space;
124 	int error = 0, s, dontroute, first = 1;
125 
126 	dontroute =
127 	    (flags & MSG_DONTROUTE) && (so->so_options & SO_DONTROUTE) == 0 &&
128 	    (so->so_proto->pr_flags & PR_ATOMIC);
129 #define	snderr(errno)	{ error = errno; splx(s); goto release; }
130 
131 #ifdef MGETHDR
132 	top->m_pkthdr.len = siz;
133 #endif
134 restart:
135 	nfs_sblock(&so->so_snd);
136 	s = splnet();
137 	if (so->so_state & SS_CANTSENDMORE)
138 		snderr(EPIPE);
139 	if (so->so_error)
140 		snderr(so->so_error);
141 	space = sbspace(&so->so_snd);
142 	if (space < siz) {
143 		sbunlock(&so->so_snd);
144 		nfs_sbwait(&so->so_snd);
145 		splx(s);
146 		goto restart;
147 	}
148 	splx(s);
149 	if (dontroute)
150 		so->so_options |= SO_DONTROUTE;
151 	s = splnet();					/* XXX */
152 	error = (*so->so_proto->pr_usrreq)(so,
153 	    PRU_SEND,
154 	    top, (caddr_t)nam, (struct mbuf *)0, (struct mbuf *)0);
155 	splx(s);
156 	if (dontroute)
157 		so->so_options &= ~SO_DONTROUTE;
158 	top = (struct mbuf *)0;
159 
160 release:
161 	sbunlock(&so->so_snd);
162 	if (top)
163 		m_freem(top);
164 	return (error);
165 }
166 
167 /*
168  * This is a stripped down udp specific version of soreceive()
169  */
170 nfs_udpreceive(so, aname, mp)
171 	register struct socket *so;
172 	struct mbuf **aname;
173 	struct mbuf **mp;
174 {
175 	register struct mbuf *m;
176 	int s, error = 0;
177 	struct protosw *pr = so->so_proto;
178 	struct mbuf *nextrecord;
179 
180 	if (aname)
181 		*aname = 0;
182 
183 restart:
184 	sblock(&so->so_rcv);
185 	s = splnet();
186 
187 	if (so->so_rcv.sb_cc == 0) {
188 		if (so->so_error) {
189 			error = so->so_error;
190 			so->so_error = 0;
191 			goto release;
192 		}
193 		if (so->so_state & SS_CANTRCVMORE)
194 			goto release;
195 		sbunlock(&so->so_rcv);
196 		sbwait(&so->so_rcv);
197 		splx(s);
198 		goto restart;
199 	}
200 	m = so->so_rcv.sb_mb;
201 	if (m == 0)
202 		panic("nfs_receive 1");
203 	nextrecord = m->m_nextpkt;
204 	if (m->m_type != MT_SONAME)
205 		panic("nfs_receive 1a");
206 	sbfree(&so->so_rcv, m);
207 	if (aname) {
208 		*aname = m;
209 		so->so_rcv.sb_mb = m->m_next;
210 		m->m_next = 0;
211 		m = so->so_rcv.sb_mb;
212 	} else {
213 		MFREE(m, so->so_rcv.sb_mb);
214 		m = so->so_rcv.sb_mb;
215 	}
216 	if (m && m->m_type == MT_RIGHTS)
217 		panic("nfs_receive 2");
218 	if (m && m->m_type == MT_CONTROL) {
219 		sbfree(&so->so_rcv, m);
220 		MFREE(m, so->so_rcv.sb_mb);
221 		m = so->so_rcv.sb_mb;
222 	}
223 	*mp = m;
224 	while (m) {
225 		if (m->m_type != MT_DATA && m->m_type != MT_HEADER)
226 			panic("nfs_receive 3");
227 		sbfree(&so->so_rcv, m);
228 		m = so->so_rcv.sb_mb = m->m_next;
229 	}
230 	so->so_rcv.sb_mb = nextrecord;
231 	so->so_state &= ~SS_RCVATMARK;	/* Necessary ?? */
232 release:
233 	sbunlock(&so->so_rcv);
234 	splx(s);
235 	return (error);
236 }
237 
238 struct nfsreq nfsreqh = {
239 	(struct nfsreq *)0,
240 	(struct nfsreq *)0,
241 	(struct mbuf *)0,
242 	(struct mbuf *)0,
243 	(struct nfsmount *)0,
244 	0, 0, 0, 0, 0,
245 };
246 
247 struct rpc_replyhead {
248 	u_long	r_xid;
249 	u_long	r_rep;
250 };
251 
252 /*
253  * Implement receipt of reply on a socket.
254  * We depend on the way that records are added to the sockbuf
255  * by sbappend*.  In particular, each record (mbufs linked through m_next)
256  * must begin with an address, followed by optional MT_CONTROL mbuf
257  * and then zero or more mbufs of data.
258  * Although the sockbuf is locked, new data may still be appended,
259  * and thus we must maintain consistency of the sockbuf during that time.
260  * We must search through the list of received datagrams matching them
261  * with outstanding requests using the xid, until ours is found.
262  */
263 nfs_udpreply(so, mntp, repl, myrep)
264 	register struct socket *so;
265 	struct nfsmount *mntp;
266 	struct nfsreq *repl, *myrep;
267 {
268 	register struct mbuf *m;
269 	register struct nfsreq *rep;
270 	register int error = 0, s;
271 	struct protosw *pr = so->so_proto;
272 	struct mbuf *nextrecord;
273 	struct sockaddr_in *sad, *sad2;
274 	struct rpc_replyhead replyh;
275 	struct mbuf *mp;
276 	char *cp;
277 	int cnt, xfer;
278 	int found;
279 
280 restart:
281 	/* Already received, bye bye */
282 	if (myrep->r_mrep != NULL)
283 		return (0);
284 	/* If a soft mount and we have run out of retries */
285 	if (myrep->r_retry == 0 && myrep->r_timer == 0)
286 		return (ETIMEDOUT);
287 	nfs_sblock(&so->so_rcv);
288 	s = splnet();
289 
290 	if (so->so_rcv.sb_cc == 0) {
291 		if (so->so_error) {
292 			error = so->so_error;
293 			so->so_error = 0;
294 			goto release;
295 		}
296 		if (so->so_state & SS_CANTRCVMORE)
297 			goto release;
298 		sbunlock(&so->so_rcv);
299 		nfs_sbwait(&so->so_rcv);
300 		splx(s);
301 		goto restart;
302 	}
303 	m = so->so_rcv.sb_mb;
304 	if (m == 0)
305 		panic("nfs_soreply 1");
306 	nextrecord = m->m_nextpkt;
307 
308 	/*
309 	 * Take off the address, check for rights and ditch any control
310 	 * mbufs.
311 	 */
312 	if (m->m_type != MT_SONAME)
313 		panic("nfs reply SONAME");
314 	sad = mtod(m, struct sockaddr_in *);
315 	sad2 = mtod(mntp->nm_sockaddr, struct sockaddr_in *);
316 	found = 0;
317 	if (sad->sin_addr.s_addr != sad2->sin_addr.s_addr)
318 		goto dropit;
319 	sbfree(&so->so_rcv, m);
320 	MFREE(m, so->so_rcv.sb_mb);
321 	m = so->so_rcv.sb_mb;
322 	if (m && m->m_type == MT_RIGHTS)
323 		panic("nfs reply RIGHTS");
324 	if (m && m->m_type == MT_CONTROL) {
325 		sbfree(&so->so_rcv, m);
326 		MFREE(m, so->so_rcv.sb_mb);
327 		m = so->so_rcv.sb_mb;
328 	}
329 	if (m)
330 		m->m_nextpkt = nextrecord;
331 	else {
332 		sbunlock(&so->so_rcv);
333 		splx(s);
334 		goto restart;
335 	}
336 
337 	/*
338 	 * Get the xid and check that it is an rpc reply
339 	 */
340 	mp = m;
341 	if (m->m_len >= 2*NFSX_UNSIGNED)
342 		bcopy(mtod(m, caddr_t), (caddr_t)&replyh, 2*NFSX_UNSIGNED);
343 	else {
344 		cnt = 2*NFSX_UNSIGNED;
345 		cp = (caddr_t)&replyh;
346 		while (mp && cnt > 0) {
347 			if (mp->m_len > 0) {
348 				xfer = (mp->m_len >= cnt) ? cnt : mp->m_len;
349 				bcopy(mtod(mp, caddr_t), cp, xfer);
350 				cnt -= xfer;
351 				cp += xfer;
352 			}
353 			if (cnt > 0)
354 				mp = mp->m_next;
355 		}
356 	}
357 	if (replyh.r_rep != rpc_reply || mp == NULL)
358 		goto dropit;
359 	/*
360 	 * Loop through the request list to match up the reply
361 	 * Iff no match, just drop the datagram
362 	 */
363 	rep = repl;
364 	while (!found && rep) {
365 		if (rep->r_mrep == NULL && replyh.r_xid == rep->r_xid) {
366 			/* Found it.. */
367 			rep->r_mrep = m;
368 			while (m) {
369 				if (m->m_type != MT_DATA && m->m_type != MT_HEADER)
370 					panic("nfs_soreply 3");
371 				sbfree(&so->so_rcv, m);
372 				m = so->so_rcv.sb_mb = m->m_next;
373 			}
374 			so->so_rcv.sb_mb = nextrecord;
375 			if (rep == myrep)
376 				goto release;
377 			found++;
378 		}
379 		rep = rep->r_next;
380 	}
381 	/* Iff not matched to request, drop it */
382 dropit:
383 	if (!found)
384 		sbdroprecord(&so->so_rcv);
385 	sbunlock(&so->so_rcv);
386 	splx(s);
387 	goto restart;
388 release:
389 	sbunlock(&so->so_rcv);
390 	splx(s);
391 	return (error);
392 }
393 
394 /*
395  * nfs_request - goes something like this
396  *	- fill in request struct
397  *	- links it into list
398  *	- calls nfs_sosend() for first transmit
399  *	- calls nfs_soreceive() to get reply
400  *	- break down rpc header and return with nfs reply pointed to
401  *	  by mrep or error
402  * nb: always frees up mreq mbuf list
403  */
404 nfs_request(vp, mreq, xid, mp, mrp, mdp, dposp)
405 	struct vnode *vp;
406 	struct mbuf *mreq;
407 	u_long xid;
408 	struct mount *mp;
409 	struct mbuf **mrp;
410 	struct mbuf **mdp;
411 	caddr_t *dposp;
412 {
413 	register struct mbuf *m, *mrep;
414 	register struct nfsreq *rep;
415 	register u_long *p;
416 	register int len;
417 	struct nfsmount *mntp;
418 	struct mbuf *md;
419 	caddr_t dpos;
420 	char *cp2;
421 	int t1;
422 	int s;
423 	int error;
424 
425 	mntp = vfs_to_nfs(mp);
426 	m = mreq;
427 	MALLOC(rep, struct nfsreq *, sizeof(struct nfsreq), M_NFSREQ, M_WAITOK);
428 	rep->r_xid = xid;
429 	rep->r_mntp = mntp;
430 	rep->r_vp = vp;
431 	if (mntp->nm_flag & NFSMNT_SOFT)
432 		rep->r_retry = mntp->nm_retrans;
433 	else
434 		rep->r_retry = VNOVAL;
435 	rep->r_mrep = NULL;
436 	rep->r_mreq = m;
437 	rep->r_timer = rep->r_timeout = mntp->nm_timeo;
438 	len = 0;
439 	while (m) {
440 		len += m->m_len;
441 		m = m->m_next;
442 	}
443 	rep->r_msiz = len;
444 	m = NFSMCOPY(mreq, 0, M_COPYALL, M_WAIT);
445 
446 	/* Chain it into list of outstanding requests */
447 	s = splnet();
448 	rep->r_next = nfsreqh.r_next;
449 	if (rep->r_next != NULL)
450 		rep->r_next->r_prev = rep;
451 	nfsreqh.r_next = rep;
452 	rep->r_prev = &nfsreqh;
453 	splx(s);
454 
455 	/*
456 	 * Iff the NFSMCOPY above succeeded, send it off...
457 	 * otherwise the timer will retransmit later
458 	 */
459 	if (m != NULL)
460 		error = nfs_udpsend(mntp->nm_so, (struct mbuf *)0, m, 0, len);
461 	error = nfs_udpreply(mntp->nm_so, mntp, nfsreqh.r_next, rep);
462 
463 	s = splnet();
464 	rep->r_prev->r_next = rep->r_next;
465 	if (rep->r_next != NULL)
466 		rep->r_next->r_prev = rep->r_prev;
467 	splx(s);
468 	m_freem(rep->r_mreq);
469 	mrep = md = rep->r_mrep;
470 	FREE((caddr_t)rep, M_NFSREQ);
471 	if (error)
472 		return (error);
473 
474 	/*
475 	 * break down the rpc header and check if ok
476 	 */
477 	dpos = mtod(md, caddr_t);
478 	nfsm_disect(p, u_long *, 5*NFSX_UNSIGNED);
479 	p += 2;
480 	if (*p++ == rpc_msgdenied) {
481 		if (*p == rpc_mismatch)
482 			error = EOPNOTSUPP;
483 		else
484 			error = EACCES;
485 		m_freem(mrep);
486 		return (error);
487 	}
488 	/*
489 	 * skip over the auth_verf, someday we may want to cache auth_short's
490 	 * for nfs_reqhead(), but for now just dump it
491 	 */
492 	if (*++p != 0) {
493 		len = nfsm_rndup(fxdr_unsigned(long, *p));
494 		nfsm_adv(len);
495 	}
496 	nfsm_disect(p, u_long *, NFSX_UNSIGNED);
497 	/* 0 == ok */
498 	if (*p == 0) {
499 		nfsm_disect(p, u_long *, NFSX_UNSIGNED);
500 		if (*p != 0) {
501 			error = fxdr_unsigned(int, *p);
502 			m_freem(mrep);
503 			return (error);
504 		}
505 		*mrp = mrep;
506 		*mdp = md;
507 		*dposp = dpos;
508 		return (0);
509 	}
510 	m_freem(mrep);
511 	return (EPROTONOSUPPORT);
512 nfsmout:
513 	return (error);
514 }
515 
516 /*
517  * Get a request for the server main loop
518  * - receive a request via. nfs_soreceive()
519  * - verify it
520  * - fill in the cred struct.
521  */
522 nfs_getreq(so, prog, vers, maxproc, nam, mrp, mdp, dposp, retxid, proc, cr)
523 	struct socket *so;
524 	u_long prog;
525 	u_long vers;
526 	int maxproc;
527 	struct mbuf **nam;
528 	struct mbuf **mrp;
529 	struct mbuf **mdp;
530 	caddr_t *dposp;
531 	u_long *retxid;
532 	u_long *proc;
533 	register struct ucred *cr;
534 {
535 	register int i;
536 	register struct mbuf *m;
537 	nfsm_vars;
538 	int len, len2;
539 
540 	if (error = nfs_udpreceive(so, nam, &mrep))
541 		return (error);
542 	md = mrep;
543 	dpos = mtod(mrep, caddr_t);
544 	nfsm_disect(p, u_long *, 10*NFSX_UNSIGNED);
545 	*retxid = *p++;
546 	if (*p++ != rpc_call) {
547 		m_freem(mrep);
548 		return (ERPCMISMATCH);
549 	}
550 	if (*p++ != rpc_vers) {
551 		m_freem(mrep);
552 		return (ERPCMISMATCH);
553 	}
554 	if (*p++ != prog) {
555 		m_freem(mrep);
556 		return (EPROGUNAVAIL);
557 	}
558 	if (*p++ != vers) {
559 		m_freem(mrep);
560 		return (EPROGMISMATCH);
561 	}
562 	*proc = fxdr_unsigned(u_long, *p++);
563 	if (*proc == NFSPROC_NULL) {
564 		*mrp = mrep;
565 		return (0);
566 	}
567 	if (*proc > maxproc || *p++ != rpc_auth_unix) {
568 		m_freem(mrep);
569 		return (EPROCUNAVAIL);
570 	}
571 	len = fxdr_unsigned(int, *p++);
572 	len2 = fxdr_unsigned(int, *++p);
573 	nfsm_adv(nfsm_rndup(len2));
574 	nfsm_disect(p, u_long *, 3*NFSX_UNSIGNED);
575 	cr->cr_uid = fxdr_unsigned(uid_t, *p++);
576 	cr->cr_gid = fxdr_unsigned(gid_t, *p++);
577 	len2 = fxdr_unsigned(int, *p);
578 	if (len2 > 10) {
579 		m_freem(mrep);
580 		return (EBADRPC);
581 	}
582 	nfsm_disect(p, u_long *, (len2+2)*NFSX_UNSIGNED);
583 	for (i = 1; i <= len2; i++)
584 		cr->cr_groups[i] = fxdr_unsigned(gid_t, *p++);
585 	cr->cr_ngroups = len2+1;
586 	/*
587 	 * Do we have any use for the verifier.
588 	 * According to the "Remote Procedure Call Protocol Spec." it
589 	 * should be AUTH_NULL, but some clients make it AUTH_UNIX?
590 	 * For now, just skip over it
591 	 */
592 	len2 = fxdr_unsigned(int, *++p);
593 	if (len2 > 0)
594 		nfsm_adv(nfsm_rndup(len2));
595 	*mrp = mrep;
596 	*mdp = md;
597 	*dposp = dpos;
598 	return (0);
599 nfsmout:
600 	return (error);
601 }
602 
603 /*
604  * Generate the rpc reply header
605  * siz arg. is used to decide if adding a cluster is worthwhile
606  */
607 nfs_rephead(siz, retxid, err, mrq, mbp, bposp)
608 	int siz;
609 	u_long retxid;
610 	int err;
611 	struct mbuf **mrq;
612 	struct mbuf **mbp;
613 	caddr_t *bposp;
614 {
615 	nfsm_vars;
616 
617 	NFSMGETHDR(mreq);
618 	mb = mreq;
619 	if ((siz+RPC_REPLYSIZ) > MHLEN)
620 		NFSMCLGET(mreq, M_WAIT);
621 	p = mtod(mreq, u_long *);
622 	mreq->m_len = 6*NFSX_UNSIGNED;
623 	bpos = ((caddr_t)p)+mreq->m_len;
624 	*p++ = retxid;
625 	*p++ = rpc_reply;
626 	if (err == ERPCMISMATCH) {
627 		*p++ = rpc_msgdenied;
628 		*p++ = rpc_mismatch;
629 		*p++ = txdr_unsigned(2);
630 		*p = txdr_unsigned(2);
631 	} else {
632 		*p++ = rpc_msgaccepted;
633 		*p++ = 0;
634 		*p++ = 0;
635 		switch (err) {
636 		case EPROGUNAVAIL:
637 			*p = txdr_unsigned(RPC_PROGUNAVAIL);
638 			break;
639 		case EPROGMISMATCH:
640 			*p = txdr_unsigned(RPC_PROGMISMATCH);
641 			nfsm_build(p, u_long *, 2*NFSX_UNSIGNED);
642 			*p++ = txdr_unsigned(2);
643 			*p = txdr_unsigned(2);	/* someday 3 */
644 			break;
645 		case EPROCUNAVAIL:
646 			*p = txdr_unsigned(RPC_PROCUNAVAIL);
647 			break;
648 		default:
649 			*p = 0;
650 			if (err != VNOVAL) {
651 				nfsm_build(p, u_long *, NFSX_UNSIGNED);
652 				*p = txdr_unsigned(err);
653 			}
654 			break;
655 		};
656 	}
657 	*mrq = mreq;
658 	*mbp = mb;
659 	*bposp = bpos;
660 	if (err != 0 && err != VNOVAL)
661 		nfsstats.srvrpc_errs++;
662 	return (0);
663 }
664 
665 /*
666  * Nfs timer routine
667  * Scan the nfsreq list and retranmit any requests that have timed out
668  * To avoid retransmission attempts on STREAM sockets (in the future) make
669  * sure to set the r_retry field to 0.
670  */
671 nfs_timer()
672 {
673 	register struct nfsreq *rep;
674 	register struct mbuf *m;
675 	register struct socket *so;
676 	int s, len;
677 
678 	s = splnet();
679 	rep = nfsreqh.r_next;
680 	while (rep != NULL) {
681 		if (rep->r_timer > 0)
682 			rep->r_timer--;
683 		else if (rep->r_mrep == NULL && rep->r_retry > 0) {
684 			so = rep->r_mntp->nm_so;
685 			if ((so->so_state & SS_CANTSENDMORE) == 0 &&
686 			    !so->so_error &&
687 			    sbspace(&so->so_snd) >= rep->r_msiz) {
688 				m = NFSMCOPY(rep->r_mreq, 0, M_COPYALL, M_DONTWAIT);
689 				if (m != NULL) {
690 					nfsstats.rpcretries++;
691 					rep->r_timeout <<= 2; /* x4 backoff */
692 					if (rep->r_timeout > NFS_MAXTIMEO)
693 						rep->r_timeout = NFS_MAXTIMEO;
694 					rep->r_timer = rep->r_timeout;
695 					if (rep->r_retry != VNOVAL)
696 						rep->r_retry--;
697 #ifdef MGETHDR
698 					m->m_pkthdr.len = rep->r_msiz;
699 #endif
700 					(*so->so_proto->pr_usrreq)(so, PRU_SEND,
701 						m, (caddr_t)0, (struct mbuf *)0,
702 						(struct mbuf *)0);
703 				}
704 			}
705 		}
706 		rep = rep->r_next;
707 	}
708 	splx(s);
709 	timeout(nfs_timer, (caddr_t)0, hz/10);
710 }
711 
712 /*
713  * nfs_sbwait() is simply sbwait() but at a negative priority so that it
714  * can not be interrupted by a signal.
715  */
716 nfs_sbwait(sb)
717 	struct sockbuf *sb;
718 {
719 	sb->sb_flags |= SB_WAIT;
720 	sleep((caddr_t)&sb->sb_cc, PZERO-1);
721 }
722