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