144626Smckusick /* 2*47526Spendry * $Id: srvr_nfs.c,v 5.2.1.3 91/03/17 17:44:37 jsp Alpha $ 344626Smckusick * 444626Smckusick * Copyright (c) 1990 Jan-Simon Pendry 544626Smckusick * Copyright (c) 1990 Imperial College of Science, Technology & Medicine 644626Smckusick * Copyright (c) 1990 The Regents of the University of California. 744626Smckusick * All rights reserved. 844626Smckusick * 944626Smckusick * This code is derived from software contributed to Berkeley by 1044626Smckusick * Jan-Simon Pendry at Imperial College, London. 1144626Smckusick * 1244626Smckusick * %sccs.include.redist.c% 1344626Smckusick * 14*47526Spendry * @(#)srvr_nfs.c 5.2 (Berkeley) 03/17/91 1544626Smckusick */ 1644626Smckusick 1744626Smckusick /* 1844626Smckusick * NFS server modeling 1944626Smckusick */ 2044626Smckusick 2144626Smckusick #include "am.h" 2244626Smckusick #include <netdb.h> 2344626Smckusick #include <rpc/pmap_prot.h> 2444626Smckusick #include "mount.h" 2544626Smckusick 2644626Smckusick extern qelem nfs_srvr_list; 2744626Smckusick qelem nfs_srvr_list = { &nfs_srvr_list, &nfs_srvr_list }; 2844626Smckusick 2944626Smckusick typedef struct nfs_private { 3044626Smckusick u_short np_mountd; /* Mount daemon port number */ 31*47526Spendry char np_mountd_inval; /* Port *may* be invalid */ 3244626Smckusick int np_ping; /* Number of failed ping attempts */ 3344626Smckusick time_t np_ttl; /* Time when server is thought dead */ 3444626Smckusick int np_xid; /* RPC transaction id for pings */ 3544626Smckusick int np_error; /* Error during portmap request */ 3644626Smckusick } nfs_private; 3744626Smckusick 3844626Smckusick static int np_xid; /* For NFS pings */ 3944626Smckusick #define NPXID_ALLOC() (++np_xid) 4044626Smckusick /*#define NPXID_ALLOC() ((++np_xid&0x0fffffff) == 0 ? npxid_gc() : np_xid)*/ 4144626Smckusick 4244626Smckusick /* 4344626Smckusick * Number of pings allowed to fail before host is declared down 4444626Smckusick * - three-fifths of the allowed mount time... 4544626Smckusick #define MAX_ALLOWED_PINGS ((((ALLOWED_MOUNT_TIME + 5 * AM_PINGER - 1) * 3) / 5) / AM_PINGER) 4644626Smckusick */ 4744626Smckusick #define MAX_ALLOWED_PINGS (3 + /* for luck ... */ 1) 4844626Smckusick 4944626Smckusick /* 5044626Smckusick * How often to ping when starting a new server 5144626Smckusick */ 5244626Smckusick #define FAST_NFS_PING 3 5344626Smckusick 5444626Smckusick #if (FAST_NFS_PING * MAX_ALLOWED_PINGS) >= ALLOWED_MOUNT_TIME 5544626Smckusick #error: sanity check failed 5644626Smckusick /* 5744626Smckusick you cannot do things this way... 5844626Smckusick sufficient fast pings must be given the chance to fail 5944626Smckusick within the allowed mount time 6044626Smckusick */ 6144626Smckusick #endif /* (FAST_NFS_PING * MAX_ALLOWED_PINGS) >= ALLOWED_MOUNT_TIME */ 6244626Smckusick 6344626Smckusick static int ping_len; 6444626Smckusick static char ping_buf[sizeof(struct rpc_msg) + 32]; 6544626Smckusick 6644626Smckusick /* 67*47526Spendry * Flush any cached data 68*47526Spendry */ 69*47526Spendry void flush_srvr_nfs_cache P((void)); 70*47526Spendry void flush_srvr_nfs_cache() 71*47526Spendry { 72*47526Spendry fserver *fs = 0; 73*47526Spendry 74*47526Spendry ITER(fs, fserver, &nfs_srvr_list) { 75*47526Spendry nfs_private *np = (nfs_private *) fs->fs_private; 76*47526Spendry if (np) { 77*47526Spendry np->np_mountd_inval = TRUE; 78*47526Spendry np->np_error = -1; 79*47526Spendry } 80*47526Spendry } 81*47526Spendry } 82*47526Spendry 83*47526Spendry /* 8444626Smckusick * Startup the NFS ping 8544626Smckusick */ 86*47526Spendry static void start_ping(P_void); 8744626Smckusick static void start_ping() 8844626Smckusick { 8944626Smckusick XDR ping_xdr; 9044626Smckusick struct rpc_msg ping_msg; 9144626Smckusick 9244626Smckusick rpc_msg_init(&ping_msg, NFS_PROGRAM, NFS_VERSION, NFSPROC_NULL); 9344626Smckusick 9444626Smckusick /* 9544626Smckusick * Create an XDR endpoint 9644626Smckusick */ 9744626Smckusick xdrmem_create(&ping_xdr, ping_buf, sizeof(ping_buf), XDR_ENCODE); 9844626Smckusick 9944626Smckusick /* 10044626Smckusick * Create the NFS ping message 10144626Smckusick */ 10244626Smckusick if (!xdr_callmsg(&ping_xdr, &ping_msg)) { 10344626Smckusick plog(XLOG_ERROR, "Couldn't create ping RPC message"); 10444626Smckusick going_down(3); 10544626Smckusick } 10644626Smckusick 10744626Smckusick /* 10844626Smckusick * Find out how long it is 10944626Smckusick */ 11044626Smckusick ping_len = xdr_getpos(&ping_xdr); 11144626Smckusick 11244626Smckusick /* 11344626Smckusick * Destroy the XDR endpoint - we don't need it anymore 11444626Smckusick */ 11544626Smckusick xdr_destroy(&ping_xdr); 11644626Smckusick } 11744626Smckusick 11844626Smckusick 11944626Smckusick /* 12044626Smckusick * Called when a portmap reply arrives 12144626Smckusick */ 122*47526Spendry /*ARGSUSED*/ 123*47526Spendry static void got_portmap P((voidp pkt, int len, struct sockaddr_in *sa, struct sockaddr_in *ia, voidp idv, int done)); 12444626Smckusick static void got_portmap(pkt, len, sa, ia, idv, done) 12544626Smckusick voidp pkt; 12644626Smckusick int len; 127*47526Spendry struct sockaddr_in *sa; 128*47526Spendry struct sockaddr_in *ia; 12944626Smckusick voidp idv; 13044626Smckusick int done; 13144626Smckusick { 13244626Smckusick fserver *fs2 = (fserver *) idv; 13344626Smckusick fserver *fs = 0; 134*47526Spendry 135*47526Spendry /* 136*47526Spendry * Find which fileserver we are talking about 137*47526Spendry */ 13844626Smckusick ITER(fs, fserver, &nfs_srvr_list) 13944626Smckusick if (fs == fs2) 14044626Smckusick break; 14144626Smckusick 14244626Smckusick if (fs == fs2) { 14344626Smckusick u_long port = 0; /* XXX - should be short but protocol is naff */ 14444626Smckusick int error = done ? pickup_rpc_reply(pkt, len, (voidp) &port, xdr_u_long) : -1; 14544626Smckusick nfs_private *np = (nfs_private *) fs->fs_private; 14644626Smckusick if (!error && port) { 14744626Smckusick #ifdef DEBUG 14844626Smckusick dlog("got port (%d) for mountd on %s", port, fs->fs_host); 14944626Smckusick #endif /* DEBUG */ 15044626Smckusick /* 15144626Smckusick * Grab the port number. Portmap sends back 15244626Smckusick * an unsigned long in native ordering, so it 15344626Smckusick * needs converting to a unsigned short in 15444626Smckusick * network ordering. 15544626Smckusick */ 15644626Smckusick np->np_mountd = htons((u_short) port); 15744626Smckusick np->np_mountd_inval = FALSE; 15844626Smckusick np->np_error = 0; 15944626Smckusick } else { 16044626Smckusick #ifdef DEBUG 16144626Smckusick dlog("Error fetching port for mountd on %s", fs->fs_host); 16244626Smckusick #endif /* DEBUG */ 16344626Smckusick /* 16444626Smckusick * Almost certainly no mountd running on remote host 16544626Smckusick */ 16644626Smckusick np->np_error = error ? error : ETIMEDOUT; 16744626Smckusick } 16844626Smckusick if (fs->fs_flags & FSF_WANT) 16944626Smckusick wakeup_srvr(fs); 17044626Smckusick } else if (done) { 17144626Smckusick #ifdef DEBUG 17244626Smckusick dlog("Got portmap for old port request"); 17344626Smckusick #endif /* DEBUG */ 17444626Smckusick } else { 17544626Smckusick #ifdef DEBUG 17644626Smckusick dlog("portmap request timed out"); 17744626Smckusick #endif /* DEBUG */ 17844626Smckusick } 17944626Smckusick } 18044626Smckusick 18144626Smckusick /* 18244626Smckusick * Obtain portmap information 18344626Smckusick */ 184*47526Spendry static int call_portmap P((fserver *fs, AUTH *auth, unsigned long prog, unsigned long vers, unsigned long prot)); 18544626Smckusick static int call_portmap(fs, auth, prog, vers, prot) 18644626Smckusick fserver *fs; 18744626Smckusick AUTH *auth; 18844626Smckusick unsigned long prog, vers, prot; 18944626Smckusick { 19044626Smckusick struct rpc_msg pmap_msg; 19144626Smckusick int len; 19244626Smckusick char iobuf[UDPMSGSIZE]; 19344626Smckusick int error; 19444626Smckusick struct pmap pmap; 19544626Smckusick 19644626Smckusick rpc_msg_init(&pmap_msg, PMAPPROG, PMAPVERS, (unsigned long) 0); 19744626Smckusick pmap.pm_prog = prog; 19844626Smckusick pmap.pm_vers = vers; 19944626Smckusick pmap.pm_prot = prot; 20044626Smckusick pmap.pm_port = 0; 20144626Smckusick len = make_rpc_packet(iobuf, sizeof(iobuf), PMAPPROC_GETPORT, 20244626Smckusick &pmap_msg, (voidp) &pmap, xdr_pmap, auth); 20344626Smckusick if (len > 0) { 20444626Smckusick struct sockaddr_in sin; 20544626Smckusick bzero((voidp) &sin, sizeof(sin)); 20644626Smckusick sin = *fs->fs_ip; 20744626Smckusick sin.sin_port = htons(PMAPPORT); 20844626Smckusick error = fwd_packet(RPC_XID_PORTMAP, (voidp) iobuf, len, 20944626Smckusick &sin, &sin, (voidp) fs, got_portmap); 21044626Smckusick } else { 21144626Smckusick error = -len; 21244626Smckusick } 21344626Smckusick return error; 21444626Smckusick } 21544626Smckusick 21644626Smckusick static void nfs_keepalive P((fserver*)); 21744626Smckusick 21844626Smckusick static void recompute_portmap P((fserver *fs)); 21944626Smckusick static void recompute_portmap(fs) 22044626Smckusick fserver *fs; 22144626Smckusick { 22244626Smckusick if (!nfs_auth) 22344626Smckusick nfs_auth = authunix_create_default(); 22444626Smckusick if (!nfs_auth) { 22544626Smckusick nfs_private *np = (nfs_private *) fs->fs_private; 22644626Smckusick np->np_error = ENOBUFS; 22744626Smckusick } else { 22844626Smckusick call_portmap(fs, nfs_auth, MOUNTPROG, 22944626Smckusick MOUNTVERS, (unsigned long) IPPROTO_UDP); 23044626Smckusick } 23144626Smckusick } 23244626Smckusick 23344626Smckusick /* 23444626Smckusick * This is called when we get a reply to an RPC ping. 23544626Smckusick * The value of id was taken from the nfs_private 23644626Smckusick * structure when the ping was transmitted. 23744626Smckusick */ 23844626Smckusick /*ARGSUSED*/ 239*47526Spendry static void nfs_pinged P((voidp pkt, int len, struct sockaddr_in *sp, struct sockaddr_in *tsp, voidp idv, int done)); 24044626Smckusick static void nfs_pinged(pkt, len, sp, tsp, idv, done) 24144626Smckusick voidp pkt; 24244626Smckusick int len; 243*47526Spendry struct sockaddr_in *sp; 244*47526Spendry struct sockaddr_in *tsp; 24544626Smckusick voidp idv; 24644626Smckusick int done; 24744626Smckusick { 24844626Smckusick int xid = (int) idv; 24944626Smckusick fserver *fs; 250*47526Spendry #ifdef DEBUG 25144626Smckusick int found_map = 0; 252*47526Spendry #endif /* DEBUG */ 25344626Smckusick 25444626Smckusick if (!done) 25544626Smckusick return; 25644626Smckusick 25744626Smckusick /* 25844626Smckusick * For each node... 25944626Smckusick */ 26044626Smckusick ITER(fs, fserver, &nfs_srvr_list) { 26144626Smckusick nfs_private *np = (nfs_private *) fs->fs_private; 26244626Smckusick if (np->np_xid == xid) { 26344626Smckusick /* 26444626Smckusick * Reset the ping counter. 26544626Smckusick * Update the keepalive timer. 26644626Smckusick * Log what happened. 26744626Smckusick */ 26844626Smckusick if (fs->fs_flags & FSF_DOWN) { 26944626Smckusick fs->fs_flags &= ~FSF_DOWN; 27044626Smckusick if (fs->fs_flags & FSF_VALID) { 27144626Smckusick srvrlog(fs, "is up"); 27244626Smckusick } else { 273*47526Spendry if (np->np_ping > 1) 274*47526Spendry srvrlog(fs, "ok"); 275*47526Spendry #ifdef DEBUG 276*47526Spendry else 277*47526Spendry srvrlog(fs, "starts up"); 278*47526Spendry #endif 27944626Smckusick fs->fs_flags |= FSF_VALID; 28044626Smckusick } 28144626Smckusick 28244626Smckusick #ifdef notdef 28344626Smckusick /* why ??? */ 28444626Smckusick if (fs->fs_flags & FSF_WANT) 28544626Smckusick wakeup_srvr(fs); 28644626Smckusick #endif /* notdef */ 28744626Smckusick } else { 28844626Smckusick if (fs->fs_flags & FSF_VALID) { 28944626Smckusick #ifdef DEBUG 29044626Smckusick dlog("file server %s type nfs is still up", fs->fs_host); 29144626Smckusick #endif /* DEBUG */ 29244626Smckusick } else { 293*47526Spendry if (np->np_ping > 1) 294*47526Spendry srvrlog(fs, "ok"); 29544626Smckusick fs->fs_flags |= FSF_VALID; 29644626Smckusick } 29744626Smckusick } 29844626Smckusick 29944626Smckusick /* 30044626Smckusick * Adjust ping interval 30144626Smckusick */ 30244626Smckusick untimeout(fs->fs_cid); 30344626Smckusick fs->fs_cid = timeout(fs->fs_pinger, nfs_keepalive, (voidp) fs); 30444626Smckusick 30544626Smckusick /* 30644626Smckusick * Update ttl for this server 30744626Smckusick */ 30844626Smckusick np->np_ttl = clocktime() + 30944626Smckusick (MAX_ALLOWED_PINGS - 1) * FAST_NFS_PING + fs->fs_pinger - 1; 31044626Smckusick 31144626Smckusick /* 31244626Smckusick * New RPC xid... 31344626Smckusick */ 31444626Smckusick np->np_xid = NPXID_ALLOC(); 31544626Smckusick 31644626Smckusick /* 31744626Smckusick * Failed pings is zero... 31844626Smckusick */ 31944626Smckusick np->np_ping = 0; 32044626Smckusick 32144626Smckusick /* 32244626Smckusick * Recompute portmap information if not known 32344626Smckusick */ 32444626Smckusick if (np->np_mountd_inval) 32544626Smckusick recompute_portmap(fs); 32644626Smckusick 327*47526Spendry #ifdef DEBUG 32844626Smckusick found_map++; 329*47526Spendry #endif /* DEBUG */ 33044626Smckusick break; 33144626Smckusick } 33244626Smckusick } 33344626Smckusick 33444626Smckusick #ifdef DEBUG 33544626Smckusick if (found_map == 0) 33644626Smckusick dlog("Spurious ping packet"); 33744626Smckusick #endif /* DEBUG */ 33844626Smckusick } 33944626Smckusick 34044626Smckusick /* 34144626Smckusick * Called when no ping-reply received 34244626Smckusick */ 34344626Smckusick static void nfs_timed_out P((fserver *fs)); 34444626Smckusick static void nfs_timed_out(fs) 34544626Smckusick fserver *fs; 34644626Smckusick { 34744626Smckusick nfs_private *np = (nfs_private *) fs->fs_private; 34844626Smckusick 34944626Smckusick /* 350*47526Spendry * Another ping has failed 351*47526Spendry */ 352*47526Spendry np->np_ping++; 353*47526Spendry 354*47526Spendry /* 35544626Smckusick * Not known to be up any longer 35644626Smckusick */ 35744626Smckusick if (FSRV_ISUP(fs)) { 35844626Smckusick fs->fs_flags &= ~FSF_VALID; 359*47526Spendry if (np->np_ping > 1) 360*47526Spendry srvrlog(fs, "not responding"); 36144626Smckusick } 36244626Smckusick 36344626Smckusick /* 36444626Smckusick * If ttl has expired then guess that it is dead 36544626Smckusick */ 36644626Smckusick if (np->np_ttl < clocktime()) { 36744626Smckusick if ((fs->fs_flags & FSF_DOWN) == 0) { 36844626Smckusick /* 36944626Smckusick * Server was up, but is now down. 37044626Smckusick */ 37144626Smckusick srvrlog(fs, "is down"); 37244626Smckusick fs->fs_flags |= FSF_DOWN|FSF_VALID; 37344626Smckusick /* 37444626Smckusick * Since the server is down, the portmap 37544626Smckusick * information may now be wrong, so it 37644626Smckusick * must be flushed from the local cache 37744626Smckusick */ 37844626Smckusick flush_nfs_fhandle_cache(fs); 37944626Smckusick np->np_error = -1; 380*47526Spendry #ifdef notdef 38144626Smckusick /* 38244626Smckusick * Pretend just one ping has failed now 38344626Smckusick */ 38444626Smckusick np->np_ping = 1; 385*47526Spendry #endif 38644626Smckusick } else { 38744626Smckusick /* 38844626Smckusick * Known to be down 38944626Smckusick */ 39044626Smckusick fs->fs_flags |= FSF_VALID; 391*47526Spendry #ifdef DEBUG 392*47526Spendry srvrlog(fs, "starts down"); 393*47526Spendry #endif 39444626Smckusick } 395*47526Spendry if (fs->fs_flags & FSF_WANT) 396*47526Spendry wakeup_srvr(fs); 39744626Smckusick } else { 39844626Smckusick #ifdef DEBUG 39944626Smckusick if (np->np_ping > 1) 40044626Smckusick dlog("%d pings to %s failed - at most %d allowed", np->np_ping, fs->fs_host, MAX_ALLOWED_PINGS); 40144626Smckusick #endif /* DEBUG */ 40244626Smckusick } 40344626Smckusick 40444626Smckusick /* 40544626Smckusick * Run keepalive again 40644626Smckusick */ 40744626Smckusick nfs_keepalive(fs); 40844626Smckusick } 40944626Smckusick 41044626Smckusick /* 41144626Smckusick * Keep track of whether a server is alive 41244626Smckusick */ 41344626Smckusick static void nfs_keepalive P((fserver *fs)); 41444626Smckusick static void nfs_keepalive(fs) 41544626Smckusick fserver *fs; 41644626Smckusick { 41744626Smckusick int error; 41844626Smckusick nfs_private *np = (nfs_private *) fs->fs_private; 41944626Smckusick int fstimeo = -1; 42044626Smckusick 42144626Smckusick /* 42244626Smckusick * Send an NFS ping to this node 42344626Smckusick */ 42444626Smckusick 42544626Smckusick if (ping_len == 0) 42644626Smckusick start_ping(); 42744626Smckusick 42844626Smckusick /* 42944626Smckusick * Queue the packet... 43044626Smckusick */ 43144626Smckusick error = fwd_packet(MK_RPC_XID(RPC_XID_NFSPING, np->np_xid), (voidp) ping_buf, 43244626Smckusick ping_len, fs->fs_ip, (struct sockaddr_in *) 0, (voidp) np->np_xid, nfs_pinged); 43344626Smckusick 43444626Smckusick /* 43544626Smckusick * See if a hard error occured 43644626Smckusick */ 43744626Smckusick switch (error) { 43844626Smckusick case ENETDOWN: 43944626Smckusick case ENETUNREACH: 44044626Smckusick case EHOSTDOWN: 44144626Smckusick case EHOSTUNREACH: 44244626Smckusick np->np_ping = MAX_ALLOWED_PINGS; /* immediately down */ 44344626Smckusick np->np_ttl = (time_t) 0; 44444626Smckusick /* 44544626Smckusick * This causes an immediate call to nfs_timed_out 44644626Smckusick * whenever the server was thought to be up. 44744626Smckusick * See +++ below. 44844626Smckusick */ 44944626Smckusick fstimeo = 0; 45044626Smckusick break; 45144626Smckusick 45244626Smckusick case 0: 45344626Smckusick #ifdef DEBUG 45444626Smckusick dlog("Sent NFS ping to %s", fs->fs_host); 45544626Smckusick #endif /* DEBUG */ 45644626Smckusick break; 45744626Smckusick } 45844626Smckusick 45944626Smckusick #ifdef DEBUG 46044626Smckusick /*dlog("keepalive, ping = %d", np->np_ping);*/ 46144626Smckusick #endif /* DEBUG */ 46244626Smckusick 46344626Smckusick /* 46444626Smckusick * Back off the ping interval if we are not getting replies and 46544626Smckusick * the remote system is know to be down. 46644626Smckusick */ 46744626Smckusick switch (fs->fs_flags & (FSF_DOWN|FSF_VALID)) { 46844626Smckusick case FSF_VALID: /* Up */ 46944626Smckusick if (fstimeo < 0) /* +++ see above */ 47044626Smckusick fstimeo = FAST_NFS_PING; 47144626Smckusick break; 47244626Smckusick 47344626Smckusick case FSF_VALID|FSF_DOWN: /* Down */ 47444626Smckusick fstimeo = fs->fs_pinger; 47544626Smckusick break; 47644626Smckusick 47744626Smckusick default: /* Unknown */ 47844626Smckusick fstimeo = FAST_NFS_PING; 47944626Smckusick break; 48044626Smckusick } 48144626Smckusick 48244626Smckusick #ifdef DEBUG 48344626Smckusick dlog("NFS timeout in %d seconds", fstimeo); 48444626Smckusick #endif /* DEBUG */ 48544626Smckusick 48644626Smckusick fs->fs_cid = timeout(fstimeo, nfs_timed_out, (voidp) fs); 48744626Smckusick } 48844626Smckusick 489*47526Spendry int nfs_srvr_port P((fserver *fs, u_short *port, voidp wchan)); 49044626Smckusick int nfs_srvr_port(fs, port, wchan) 49144626Smckusick fserver *fs; 49244626Smckusick u_short *port; 49344626Smckusick voidp wchan; 49444626Smckusick { 49544626Smckusick int error = -1; 49644626Smckusick if ((fs->fs_flags & FSF_VALID) == FSF_VALID) { 49744626Smckusick if ((fs->fs_flags & FSF_DOWN) == 0) { 49844626Smckusick nfs_private *np = (nfs_private *) fs->fs_private; 49944626Smckusick if (np->np_error == 0) { 50044626Smckusick *port = np->np_mountd; 50144626Smckusick error = 0; 50244626Smckusick } else { 50344626Smckusick error = np->np_error; 50444626Smckusick } 505*47526Spendry /* 506*47526Spendry * Now go get the port mapping again in case it changed. 507*47526Spendry * Note that it is used even if (np_mountd_inval) 508*47526Spendry * is True. The flag is used simply as an 509*47526Spendry * indication that the mountd may be invalid, not 510*47526Spendry * that it is known to be invalid. 511*47526Spendry */ 512*47526Spendry if (np->np_mountd_inval) 513*47526Spendry recompute_portmap(fs); 514*47526Spendry else 515*47526Spendry np->np_mountd_inval = TRUE; 51644626Smckusick } else { 51744626Smckusick error = EWOULDBLOCK; 51844626Smckusick } 51944626Smckusick } 52044626Smckusick if (error < 0 && wchan && !(fs->fs_flags & FSF_WANT)) { 52144626Smckusick /* 52244626Smckusick * If a wait channel is supplied, and no 52344626Smckusick * error has yet occured, then arrange 52444626Smckusick * that a wakeup is done on the wait channel, 52544626Smckusick * whenever a wakeup is done on this fs node. 52644626Smckusick * Wakeup's are done on the fs node whenever 52744626Smckusick * it changes state - thus causing control to 52844626Smckusick * come back here and new, better things to happen. 52944626Smckusick */ 53044626Smckusick fs->fs_flags |= FSF_WANT; 53144626Smckusick sched_task(wakeup_task, wchan, (voidp) fs); 53244626Smckusick } 53344626Smckusick return error; 53444626Smckusick } 53544626Smckusick 53644626Smckusick static void start_nfs_pings P((fserver *fs, int pingval)); 53744626Smckusick static void start_nfs_pings(fs, pingval) 53844626Smckusick fserver *fs; 53944626Smckusick int pingval; 54044626Smckusick { 54144626Smckusick if (!(fs->fs_flags & FSF_PINGING)) { 54244626Smckusick fs->fs_flags |= FSF_PINGING; 54344626Smckusick if (fs->fs_cid) 54444626Smckusick untimeout(fs->fs_cid); 54544626Smckusick if (pingval < 0) { 54644626Smckusick srvrlog(fs, "wired up"); 54744626Smckusick fs->fs_flags |= FSF_VALID; 54844626Smckusick fs->fs_flags &= ~FSF_DOWN; 54944626Smckusick } else { 55044626Smckusick nfs_keepalive(fs); 55144626Smckusick } 55244626Smckusick } else { 55344626Smckusick #ifdef DEBUG 55444626Smckusick dlog("Already running pings to %s", fs->fs_host); 55544626Smckusick #endif /* DEBUG */ 55644626Smckusick } 55744626Smckusick } 55844626Smckusick 55944626Smckusick /* 56044626Smckusick * Find an nfs server for a host. 56144626Smckusick */ 56244626Smckusick fserver *find_nfs_srvr P((mntfs *mf)); 56344626Smckusick fserver *find_nfs_srvr(mf) 56444626Smckusick mntfs *mf; 56544626Smckusick { 56644626Smckusick fserver *fs; 56744626Smckusick struct hostent *hp = 0; 56844626Smckusick char *host = mf->mf_fo->opt_rhost; 56944626Smckusick struct sockaddr_in *ip; 57044626Smckusick nfs_private *np; 57144626Smckusick int pingval; 57244626Smckusick 57344626Smckusick /* 57444626Smckusick * Get ping interval from mount options. 57544626Smckusick * Current only used to decide whether pings 57644626Smckusick * are required or not. < 0 = no pings. 57744626Smckusick */ 57844626Smckusick { struct mntent mnt; 579*47526Spendry mnt.mnt_opts = mf->mf_mopts; 58044626Smckusick pingval = hasmntval(&mnt, "ping"); 58144626Smckusick #ifdef HAS_TCP_NFS 58244626Smckusick /* 58344626Smckusick * Over TCP mount, don't bother to do pings. 58444626Smckusick * This is experimental - maybe you want to 58544626Smckusick * do pings anyway... 58644626Smckusick */ 58744626Smckusick if (pingval == 0 && hasmntopt(&mnt, "tcp")) 58844626Smckusick pingval = -1; 58944626Smckusick #endif /* HAS_TCP_NFS */ 59044626Smckusick } 59144626Smckusick 59244626Smckusick 59344626Smckusick /* 594*47526Spendry * lookup host address and canonical name 59544626Smckusick */ 596*47526Spendry hp = gethostbyname(host); 597*47526Spendry 598*47526Spendry /* 599*47526Spendry * New code from Bob Harris <harris@basil-rathbone.mit.edu> 600*47526Spendry * Use canonical name to keep track of file server 601*47526Spendry * information. This way aliases do not generate 602*47526Spendry * multiple NFS pingers. (Except when we're normalizing 603*47526Spendry * hosts.) 604*47526Spendry */ 605*47526Spendry if (hp && !normalize_hosts) host = hp->h_name; 606*47526Spendry 60744626Smckusick ITER(fs, fserver, &nfs_srvr_list) { 60844626Smckusick if (STREQ(host, fs->fs_host)) { 60944626Smckusick start_nfs_pings(fs, pingval); 61044626Smckusick fs->fs_refc++; 61144626Smckusick return fs; 61244626Smckusick } 61344626Smckusick } 61444626Smckusick 61544626Smckusick 616*47526Spendry 61744626Smckusick /* 61844626Smckusick * Get here if we can't find an entry 61944626Smckusick */ 62044626Smckusick if (hp) { 62144626Smckusick switch (hp->h_addrtype) { 62244626Smckusick case AF_INET: 62344626Smckusick ip = ALLOC(sockaddr_in); 62444626Smckusick bzero((voidp) ip, sizeof(*ip)); 62544626Smckusick ip->sin_family = AF_INET; 626*47526Spendry bcopy((voidp) hp->h_addr, (voidp) &ip->sin_addr, sizeof(ip->sin_addr)); 627*47526Spendry 62844626Smckusick ip->sin_port = htons(NFS_PORT); 62944626Smckusick break; 63044626Smckusick 63144626Smckusick default: 63244626Smckusick ip = 0; 63344626Smckusick break; 63444626Smckusick } 63544626Smckusick } else { 636*47526Spendry plog(XLOG_USER, "Unknown host: %s", host); 63744626Smckusick ip = 0; 63844626Smckusick } 63944626Smckusick 64044626Smckusick /* 64144626Smckusick * Allocate a new server 64244626Smckusick */ 64344626Smckusick fs = ALLOC(fserver); 64444626Smckusick fs->fs_refc = 1; 64544626Smckusick fs->fs_host = strdup(hp ? hp->h_name : "unknown_hostname"); 646*47526Spendry if (normalize_hosts) host_normalize(&fs->fs_host); 64744626Smckusick fs->fs_ip = ip; 64844626Smckusick fs->fs_cid = 0; 64944626Smckusick if (ip) { 65044626Smckusick fs->fs_flags = FSF_DOWN; /* Starts off down */ 65144626Smckusick } else { 65244626Smckusick fs->fs_flags = FSF_ERROR|FSF_VALID; 65344626Smckusick mf->mf_flags |= MFF_ERROR; 65444626Smckusick mf->mf_error = ENOENT; 65544626Smckusick } 65644626Smckusick fs->fs_type = "nfs"; 65744626Smckusick fs->fs_pinger = AM_PINGER; 65844626Smckusick np = ALLOC(nfs_private); 65944626Smckusick bzero((voidp) np, sizeof(*np)); 66044626Smckusick np->np_mountd_inval = TRUE; 66144626Smckusick np->np_xid = NPXID_ALLOC(); 66244626Smckusick np->np_error = -1; 66344626Smckusick /* 66444626Smckusick * Initially the server will be deemed dead after 66544626Smckusick * MAX_ALLOWED_PINGS of the fast variety have failed. 66644626Smckusick */ 66744626Smckusick np->np_ttl = clocktime() + MAX_ALLOWED_PINGS * FAST_NFS_PING - 1; 66844626Smckusick fs->fs_private = (voidp) np; 66944626Smckusick fs->fs_prfree = (void (*)()) free; 67044626Smckusick 67144626Smckusick if (!(fs->fs_flags & FSF_ERROR)) { 67244626Smckusick /* 67344626Smckusick * Start of keepalive timer 67444626Smckusick */ 67544626Smckusick start_nfs_pings(fs, pingval); 67644626Smckusick } 67744626Smckusick 67844626Smckusick /* 67944626Smckusick * Add to list of servers 68044626Smckusick */ 68144626Smckusick ins_que(&fs->fs_q, &nfs_srvr_list); 68244626Smckusick 68344626Smckusick return fs; 68444626Smckusick } 685