xref: /openbsd/usr.sbin/amd/amd/srvr_nfs.c (revision 26d0c865)
1 /*
2  * Copyright (c) 1990 Jan-Simon Pendry
3  * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
4  * Copyright (c) 1990, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * This code is derived from software contributed to Berkeley by
8  * Jan-Simon Pendry at Imperial College, London.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. Neither the name of the University nor the names of its contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  *
34  *	from: @(#)srvr_nfs.c	8.1 (Berkeley) 6/6/93
35  *	$Id: srvr_nfs.c,v 1.10 2014/10/26 03:28:41 guenther Exp $
36  */
37 
38 /*
39  * NFS server modeling
40  */
41 
42 #include "am.h"
43 #include <netdb.h>
44 #include <rpc/pmap_prot.h>
45 #include "mount.h"
46 
47 extern qelem nfs_srvr_list;
48 qelem nfs_srvr_list = { &nfs_srvr_list, &nfs_srvr_list };
49 
50 typedef struct nfs_private {
51 	u_short np_mountd;	/* Mount daemon port number */
52 	char np_mountd_inval;	/* Port *may* be invalid */
53 	int np_ping;		/* Number of failed ping attempts */
54 	time_t np_ttl;		/* Time when server is thought dead */
55 	int np_xid;		/* RPC transaction id for pings */
56 	int np_error;		/* Error during portmap request */
57 } nfs_private;
58 
59 static int np_xid;	/* For NFS pings */
60 #define	NPXID_ALLOC()	(++np_xid)
61 /*#define	NPXID_ALLOC()	((++np_xid&0x0fffffff) == 0 ? npxid_gc() : np_xid)*/
62 
63 /*
64  * Number of pings allowed to fail before host is declared down
65  * - three-fifths of the allowed mount time...
66 #define	MAX_ALLOWED_PINGS	((((ALLOWED_MOUNT_TIME + 5 * AM_PINGER - 1) * 3) / 5) / AM_PINGER)
67  */
68 #define	MAX_ALLOWED_PINGS	(3 + /* for luck ... */ 1)
69 
70 /*
71  * How often to ping when starting a new server
72  */
73 #define	FAST_NFS_PING		3
74 
75 #if (FAST_NFS_PING * MAX_ALLOWED_PINGS) >= ALLOWED_MOUNT_TIME
76  #error: sanity check failed
77 /*
78  you cannot do things this way...
79  sufficient fast pings must be given the chance to fail
80  within the allowed mount time
81  */
82 #endif /* (FAST_NFS_PING * MAX_ALLOWED_PINGS) >= ALLOWED_MOUNT_TIME */
83 
84 static int ping_len;
85 static char ping_buf[sizeof(struct rpc_msg) + 32];
86 
87 /*
88  * Flush any cached data
89  */
90 void
flush_srvr_nfs_cache(void)91 flush_srvr_nfs_cache(void)
92 {
93 	fserver *fs = 0;
94 
95 	ITER(fs, fserver, &nfs_srvr_list) {
96 		nfs_private *np = (nfs_private *) fs->fs_private;
97 		if (np) {
98 			np->np_mountd_inval = TRUE;
99 			np->np_error = -1;
100 		}
101 	}
102 }
103 
104 /*
105  * Startup the NFS ping
106  */
107 static void
start_ping(void)108 start_ping(void)
109 {
110 	XDR ping_xdr;
111 	struct rpc_msg ping_msg;
112 
113 	rpc_msg_init(&ping_msg, NFS_PROGRAM, NFS_VERSION, NFSPROC_NULL);
114 
115 	/*
116 	 * Create an XDR endpoint
117 	 */
118 	xdrmem_create(&ping_xdr, ping_buf, sizeof(ping_buf), XDR_ENCODE);
119 
120 	/*
121 	 * Create the NFS ping message
122 	 */
123 	if (!xdr_callmsg(&ping_xdr, &ping_msg)) {
124 		plog(XLOG_ERROR, "Couldn't create ping RPC message");
125 		going_down(3);
126 	}
127 
128 	/*
129 	 * Find out how long it is
130 	 */
131 	ping_len = xdr_getpos(&ping_xdr);
132 
133 	/*
134 	 * Destroy the XDR endpoint - we don't need it anymore
135 	 */
136 	xdr_destroy(&ping_xdr);
137 }
138 
139 
140 /*
141  * Called when a portmap reply arrives
142  */
143 static void
got_portmap(void * pkt,int len,struct sockaddr_in * sa,struct sockaddr_in * ia,void * idv,int done)144 got_portmap(void *pkt, int len, struct sockaddr_in *sa,
145     struct sockaddr_in *ia, void *idv, int done)
146 {
147 	fserver *fs2 = (fserver *) idv;
148 	fserver *fs = 0;
149 
150 	/*
151 	 * Find which fileserver we are talking about
152 	 */
153 	ITER(fs, fserver, &nfs_srvr_list)
154 		if (fs == fs2)
155 			break;
156 
157 	if (fs == fs2) {
158 		u_long port = 0;	/* XXX - should be short but protocol is naff */
159 		int error = done ? pickup_rpc_reply(pkt, len, &port, xdr_u_long) : -1;
160 		nfs_private *np = (nfs_private *) fs->fs_private;
161 		if (!error && port) {
162 #ifdef DEBUG
163 			dlog("got port (%d) for mountd on %s", port, fs->fs_host);
164 #endif /* DEBUG */
165 			/*
166 			 * Grab the port number.  Portmap sends back
167 			 * an unsigned long in native ordering, so it
168 			 * needs converting to a unsigned short in
169 			 * network ordering.
170 			 */
171 			np->np_mountd = htons((u_short) port);
172 			np->np_mountd_inval = FALSE;
173 			np->np_error = 0;
174 		} else {
175 #ifdef DEBUG
176 			dlog("Error fetching port for mountd on %s", fs->fs_host);
177 #endif /* DEBUG */
178 			/*
179 			 * Almost certainly no mountd running on remote host
180 			 */
181 			np->np_error = error ? error : ETIMEDOUT;
182 		}
183 		if (fs->fs_flags & FSF_WANT)
184 			wakeup_srvr(fs);
185 	} else if (done) {
186 #ifdef DEBUG
187 		dlog("Got portmap for old port request");
188 #endif /* DEBUG */
189 	} else {
190 #ifdef DEBUG
191 		dlog("portmap request timed out");
192 #endif /* DEBUG */
193 	}
194 }
195 
196 /*
197  * Obtain portmap information
198  */
199 static int
call_portmap(fserver * fs,AUTH * auth,unsigned long prog,unsigned long vers,unsigned long prot)200 call_portmap(fserver *fs, AUTH *auth, unsigned long prog,
201     unsigned long vers, unsigned long prot)
202 {
203 	struct rpc_msg pmap_msg;
204 	int len;
205 	char iobuf[UDPMSGSIZE];
206 	int error;
207 	struct pmap pmap;
208 
209 	rpc_msg_init(&pmap_msg, PMAPPROG, PMAPVERS, (unsigned long) 0);
210 	pmap.pm_prog = prog;
211 	pmap.pm_vers = vers;
212 	pmap.pm_prot = prot;
213 	pmap.pm_port = 0;
214 	len = make_rpc_packet(iobuf, sizeof(iobuf), PMAPPROC_GETPORT,
215 			&pmap_msg, &pmap, xdr_pmap, auth);
216 	if (len > 0) {
217 		struct sockaddr_in sin;
218 		bzero(&sin, sizeof(sin));
219 		sin = *fs->fs_ip;
220 		sin.sin_port = htons(PMAPPORT);
221 		error = fwd_packet(RPC_XID_PORTMAP, iobuf, len,
222 				&sin, &sin, fs, got_portmap);
223 	} else {
224 		error = -len;
225 	}
226 	return error;
227 }
228 
229 static void nfs_keepalive(void *);
230 
231 static void
recompute_portmap(fserver * fs)232 recompute_portmap(fserver *fs)
233 {
234 	int error;
235 
236 	if (nfs_auth)
237 		error = 0;
238 	else
239 		error = make_nfs_auth();
240 
241 	if (error) {
242 		nfs_private *np = (nfs_private *) fs->fs_private;
243 		np->np_error = error;
244 	} else {
245 		call_portmap(fs, nfs_auth, MOUNTPROG,
246 			MOUNTVERS, (unsigned long) IPPROTO_UDP);
247 	}
248 }
249 
250 /*
251  * This is called when we get a reply to an RPC ping.
252  * The value of id was taken from the nfs_private
253  * structure when the ping was transmitted.
254  */
255 static void
nfs_pinged(void * pkt,int len,struct sockaddr_in * sp,struct sockaddr_in * tsp,void * idv,int done)256 nfs_pinged(void *pkt, int len, struct sockaddr_in *sp,
257     struct sockaddr_in *tsp, void *idv, int done)
258 {
259 	/* XXX EVIL! XXX */
260 	int xid = (int) ((long)idv);
261 	fserver *fs;
262 #ifdef DEBUG
263 	int found_map = 0;
264 #endif /* DEBUG */
265 
266 	if (!done)
267 		return;
268 
269 	/*
270 	 * For each node...
271 	 */
272 	ITER(fs, fserver, &nfs_srvr_list) {
273 		nfs_private *np = (nfs_private *) fs->fs_private;
274 		if (np->np_xid == xid) {
275 			/*
276 			 * Reset the ping counter.
277 			 * Update the keepalive timer.
278 			 * Log what happened.
279 			 */
280 			if (fs->fs_flags & FSF_DOWN) {
281 				fs->fs_flags &= ~FSF_DOWN;
282 				if (fs->fs_flags & FSF_VALID) {
283 					srvrlog(fs, "is up");
284 				} else {
285 					if (np->np_ping > 1)
286 						srvrlog(fs, "ok");
287 #ifdef DEBUG
288 					else
289 						srvrlog(fs, "starts up");
290 #endif
291 					fs->fs_flags |= FSF_VALID;
292 				}
293 
294 #ifdef notdef
295 				/* why ??? */
296 				if (fs->fs_flags & FSF_WANT)
297 					wakeup_srvr(fs);
298 #endif /* notdef */
299 				map_flush_srvr(fs);
300 			} else {
301 				if (fs->fs_flags & FSF_VALID) {
302 #ifdef DEBUG
303 					dlog("file server %s type nfs is still up", fs->fs_host);
304 #endif /* DEBUG */
305 				} else {
306 					if (np->np_ping > 1)
307 						srvrlog(fs, "ok");
308 					fs->fs_flags |= FSF_VALID;
309 				}
310 			}
311 
312 			/*
313 			 * Adjust ping interval
314 			 */
315 			untimeout(fs->fs_cid);
316 			fs->fs_cid = timeout(fs->fs_pinger, nfs_keepalive, fs);
317 
318 			/*
319 			 * Update ttl for this server
320 			 */
321 			np->np_ttl = clocktime() +
322 				(MAX_ALLOWED_PINGS - 1) * FAST_NFS_PING + fs->fs_pinger - 1;
323 
324 			/*
325 			 * New RPC xid...
326 			 */
327 			np->np_xid = NPXID_ALLOC();
328 
329 			/*
330 			 * Failed pings is zero...
331 			 */
332 			np->np_ping = 0;
333 
334 			/*
335 			 * Recompute portmap information if not known
336 			 */
337 			if (np->np_mountd_inval)
338 				recompute_portmap(fs);
339 
340 #ifdef DEBUG
341 			found_map++;
342 #endif /* DEBUG */
343 			break;
344 		}
345 	}
346 
347 #ifdef DEBUG
348 	if (found_map == 0)
349 		dlog("Spurious ping packet");
350 #endif /* DEBUG */
351 }
352 
353 /*
354  * Called when no ping-reply received
355  */
356 static void
nfs_timed_out(void * arg)357 nfs_timed_out(void *arg)
358 {
359 	fserver *fs = arg;
360 
361 	nfs_private *np = (nfs_private *) fs->fs_private;
362 
363 	/*
364 	 * Another ping has failed
365 	 */
366 	np->np_ping++;
367 
368 	/*
369 	 * Not known to be up any longer
370 	 */
371 	if (FSRV_ISUP(fs)) {
372 		fs->fs_flags &= ~FSF_VALID;
373 		if (np->np_ping > 1)
374 			srvrlog(fs, "not responding");
375 	}
376 
377 	/*
378 	 * If ttl has expired then guess that it is dead
379 	 */
380 	if (np->np_ttl < clocktime()) {
381 		int oflags = fs->fs_flags;
382 		if ((fs->fs_flags & FSF_DOWN) == 0) {
383 			/*
384 			 * Server was up, but is now down.
385 			 */
386 			srvrlog(fs, "is down");
387 			fs->fs_flags |= FSF_DOWN|FSF_VALID;
388 			/*
389 			 * Since the server is down, the portmap
390 			 * information may now be wrong, so it
391 			 * must be flushed from the local cache
392 			 */
393 			flush_nfs_fhandle_cache(fs);
394 			np->np_error = -1;
395 #ifdef notdef
396 			/*
397 			 * Pretend just one ping has failed now
398 			 */
399 			np->np_ping = 1;
400 #endif
401 		} else {
402 			/*
403 			 * Known to be down
404 			 */
405 #ifdef DEBUG
406 			if ((fs->fs_flags & FSF_VALID) == 0)
407 				srvrlog(fs, "starts down");
408 #endif
409 			fs->fs_flags |= FSF_VALID;
410 		}
411 		if (oflags != fs->fs_flags && (fs->fs_flags & FSF_WANT))
412 			wakeup_srvr(fs);
413 	} else {
414 #ifdef DEBUG
415 		if (np->np_ping > 1)
416 			dlog("%d pings to %s failed - at most %d allowed", np->np_ping, fs->fs_host, MAX_ALLOWED_PINGS);
417 #endif /* DEBUG */
418 	}
419 
420 	/*
421 	 * Run keepalive again
422 	 */
423 	nfs_keepalive(fs);
424 }
425 
426 /*
427  * Keep track of whether a server is alive
428  */
429 static void
nfs_keepalive(void * arg)430 nfs_keepalive(void *arg)
431 {
432 	fserver *fs = arg;
433 
434 	int error;
435 	nfs_private *np = (nfs_private *) fs->fs_private;
436 	int fstimeo = -1;
437 
438 	/*
439 	 * Send an NFS ping to this node
440 	 */
441 
442 	if (ping_len == 0)
443 		start_ping();
444 
445 	/*
446 	 * Queue the packet...
447 	 */
448 	/*
449 	 * XXX EVIL!  We cast xid to a pointer, then back to an int when
450 	 * XXX we get the reply.
451 	 */
452 	error = fwd_packet(MK_RPC_XID(RPC_XID_NFSPING, np->np_xid), ping_buf,
453 		ping_len, fs->fs_ip, NULL, (void *)((long)np->np_xid),
454 		nfs_pinged);
455 
456 	/*
457 	 * See if a hard error occured
458 	 */
459 	switch (error) {
460 	case ENETDOWN:
461 	case ENETUNREACH:
462 	case EHOSTDOWN:
463 	case EHOSTUNREACH:
464 		np->np_ping = MAX_ALLOWED_PINGS;	/* immediately down */
465 		np->np_ttl = (time_t) 0;
466 		/*
467 		 * This causes an immediate call to nfs_timed_out
468 		 * whenever the server was thought to be up.
469 		 * See +++ below.
470 		 */
471 		fstimeo = 0;
472 		break;
473 
474 	case 0:
475 #ifdef DEBUG
476 		dlog("Sent NFS ping to %s", fs->fs_host);
477 #endif /* DEBUG */
478 		break;
479 	}
480 
481 #ifdef DEBUG
482 	/*dlog("keepalive, ping = %d", np->np_ping);*/
483 #endif /* DEBUG */
484 
485 	/*
486 	 * Back off the ping interval if we are not getting replies and
487 	 * the remote system is know to be down.
488 	 */
489 	switch (fs->fs_flags & (FSF_DOWN|FSF_VALID)) {
490 	case FSF_VALID:			/* Up */
491 		if (fstimeo < 0)	/* +++ see above */
492 			fstimeo = FAST_NFS_PING;
493 		break;
494 
495 	case FSF_VALID|FSF_DOWN:	/* Down */
496 		fstimeo = fs->fs_pinger;
497 		break;
498 
499 	default:			/* Unknown */
500 		fstimeo = FAST_NFS_PING;
501 		break;
502 	}
503 
504 #ifdef DEBUG
505 	dlog("NFS timeout in %d seconds", fstimeo);
506 #endif /* DEBUG */
507 
508 	fs->fs_cid = timeout(fstimeo, nfs_timed_out, fs);
509 }
510 
511 int
nfs_srvr_port(fserver * fs,u_short * port,void * wchan)512 nfs_srvr_port(fserver *fs, u_short *port, void *wchan)
513 {
514 	int error = -1;
515 	if ((fs->fs_flags & FSF_VALID) == FSF_VALID) {
516 		if ((fs->fs_flags & FSF_DOWN) == 0) {
517 			nfs_private *np = (nfs_private *) fs->fs_private;
518 			if (np->np_error == 0) {
519 				*port = np->np_mountd;
520 				error = 0;
521 			} else {
522 				error = np->np_error;
523 			}
524 			/*
525 			 * Now go get the port mapping again in case it changed.
526 			 * Note that it is used even if (np_mountd_inval)
527 			 * is True.  The flag is used simply as an
528 			 * indication that the mountd may be invalid, not
529 			 * that it is known to be invalid.
530 			 */
531 			if (np->np_mountd_inval)
532 				recompute_portmap(fs);
533 			else
534 				np->np_mountd_inval = TRUE;
535 		} else {
536 			error = EWOULDBLOCK;
537 		}
538 	}
539 	if (error < 0 && wchan && !(fs->fs_flags & FSF_WANT)) {
540 		/*
541 		 * If a wait channel is supplied, and no
542 		 * error has yet occured, then arrange
543 		 * that a wakeup is done on the wait channel,
544 		 * whenever a wakeup is done on this fs node.
545 		 * Wakeup's are done on the fs node whenever
546 		 * it changes state - thus causing control to
547 		 * come back here and new, better things to happen.
548 		 */
549 		fs->fs_flags |= FSF_WANT;
550 		sched_task(wakeup_task, wchan, fs);
551 	}
552 	return error;
553 }
554 
555 static void
start_nfs_pings(fserver * fs,int pingval)556 start_nfs_pings(fserver *fs, int pingval)
557 {
558 	if (!(fs->fs_flags & FSF_PINGING)) {
559 		fs->fs_flags |= FSF_PINGING;
560 		if (fs->fs_cid)
561 			untimeout(fs->fs_cid);
562 		if (pingval < 0) {
563 			srvrlog(fs, "wired up");
564 			fs->fs_flags |= FSF_VALID;
565 			fs->fs_flags &= ~FSF_DOWN;
566 		} else {
567 			nfs_keepalive(fs);
568 		}
569 	} else {
570 #ifdef DEBUG
571 		dlog("Already running pings to %s", fs->fs_host);
572 #endif /* DEBUG */
573 	}
574 }
575 
576 /*
577  * Find an nfs server for a host.
578  */
579 fserver *
find_nfs_srvr(mntfs * mf)580 find_nfs_srvr(mntfs *mf)
581 {
582 	fserver *fs;
583 	struct hostent *hp = 0;
584 	char *host = mf->mf_fo->opt_rhost;
585 	struct sockaddr_in *ip;
586 	nfs_private *np;
587 	int pingval;
588 
589 	/*
590 	 * Get ping interval from mount options.
591 	 * Current only used to decide whether pings
592 	 * are required or not.  < 0 = no pings.
593 	 */
594 	{ struct mntent mnt;
595 	  mnt.mnt_opts = mf->mf_mopts;
596 	  pingval = hasmntval(&mnt, "ping");
597 	  /*
598 	   * Over TCP mount, don't bother to do pings.
599 	   * This is experimental - maybe you want to
600 	   * do pings anyway...
601 	   */
602 	  if (pingval == 0 && hasmntopt(&mnt, "tcp"))
603 		pingval = -1;
604 	}
605 
606 
607 	/*
608 	 * lookup host address and canonical name
609 	 */
610 	hp = gethostbyname(host);
611 
612 	/*
613 	 * New code from Bob Harris <harris@basil-rathbone.mit.edu>
614 	 * Use canonical name to keep track of file server
615 	 * information.  This way aliases do not generate
616 	 * multiple NFS pingers.  (Except when we're normalizing
617 	 * hosts.)
618 	 */
619 	if (hp && !normalize_hosts) host = hp->h_name;
620 
621 	ITER(fs, fserver, &nfs_srvr_list) {
622 		if (STREQ(host, fs->fs_host)) {
623 			start_nfs_pings(fs, pingval);
624 			fs->fs_refc++;
625 			return fs;
626 		}
627 	}
628 
629 
630 
631 	/*
632 	 * Get here if we can't find an entry
633 	 */
634 	if (hp) {
635 		switch (hp->h_addrtype) {
636 		case AF_INET:
637 			ip = ALLOC(sockaddr_in);
638 			bzero(ip, sizeof(*ip));
639 			ip->sin_family = AF_INET;
640 			bcopy(hp->h_addr, &ip->sin_addr, sizeof(ip->sin_addr));
641 
642 			ip->sin_port = htons(NFS_PORT);
643 			break;
644 
645 		default:
646 			ip = 0;
647 			break;
648 		}
649 	} else {
650 		plog(XLOG_USER, "Unknown host: %s", host);
651 		ip = 0;
652 	}
653 
654 	/*
655 	 * Allocate a new server
656 	 */
657 	fs = ALLOC(fserver);
658 	fs->fs_refc = 1;
659 	fs->fs_host = strdup(hp ? hp->h_name : "unknown_hostname");
660 	if (normalize_hosts) host_normalize(&fs->fs_host);
661 	fs->fs_ip = ip;
662 	fs->fs_cid = 0;
663 	if (ip) {
664 		fs->fs_flags = FSF_DOWN;	/* Starts off down */
665 	} else {
666 		fs->fs_flags = FSF_ERROR|FSF_VALID;
667 		mf->mf_flags |= MFF_ERROR;
668 		mf->mf_error = ENOENT;
669 	}
670 	fs->fs_type = "nfs";
671 	fs->fs_pinger = AM_PINGER;
672 	np = ALLOC(nfs_private);
673 	bzero(np, sizeof(*np));
674 	np->np_mountd_inval = TRUE;
675 	np->np_xid = NPXID_ALLOC();
676 	np->np_error = -1;
677 	/*
678 	 * Initially the server will be deemed dead after
679 	 * MAX_ALLOWED_PINGS of the fast variety have failed.
680 	 */
681 	np->np_ttl = clocktime() + MAX_ALLOWED_PINGS * FAST_NFS_PING - 1;
682 	fs->fs_private = np;
683 	fs->fs_prfree = free;
684 
685 	if (!(fs->fs_flags & FSF_ERROR)) {
686 		/*
687 		 * Start of keepalive timer
688 		 */
689 		start_nfs_pings(fs, pingval);
690 	}
691 
692 	/*
693 	 * Add to list of servers
694 	 */
695 	ins_que(&fs->fs_q, &nfs_srvr_list);
696 
697 	return fs;
698 }
699