144626Smckusick /* 244626Smckusick * Copyright (c) 1990 Jan-Simon Pendry 344626Smckusick * Copyright (c) 1990 Imperial College of Science, Technology & Medicine 444626Smckusick * Copyright (c) 1990 The Regents of the University of California. 544626Smckusick * All rights reserved. 644626Smckusick * 744626Smckusick * This code is derived from software contributed to Berkeley by 844626Smckusick * Jan-Simon Pendry at Imperial College, London. 944626Smckusick * 1044626Smckusick * %sccs.include.redist.c% 1144626Smckusick * 12*49683Spendry * @(#)srvr_nfs.c 5.3 (Berkeley) 05/12/91 13*49683Spendry * 14*49683Spendry * $Id: srvr_nfs.c,v 5.2.1.6 91/05/07 22:18:36 jsp Alpha $ 15*49683Spendry * 1644626Smckusick */ 1744626Smckusick 1844626Smckusick /* 1944626Smckusick * NFS server modeling 2044626Smckusick */ 2144626Smckusick 2244626Smckusick #include "am.h" 2344626Smckusick #include <netdb.h> 2444626Smckusick #include <rpc/pmap_prot.h> 2544626Smckusick #include "mount.h" 2644626Smckusick 2744626Smckusick extern qelem nfs_srvr_list; 2844626Smckusick qelem nfs_srvr_list = { &nfs_srvr_list, &nfs_srvr_list }; 2944626Smckusick 3044626Smckusick typedef struct nfs_private { 3144626Smckusick u_short np_mountd; /* Mount daemon port number */ 3247526Spendry char np_mountd_inval; /* Port *may* be invalid */ 3344626Smckusick int np_ping; /* Number of failed ping attempts */ 3444626Smckusick time_t np_ttl; /* Time when server is thought dead */ 3544626Smckusick int np_xid; /* RPC transaction id for pings */ 3644626Smckusick int np_error; /* Error during portmap request */ 3744626Smckusick } nfs_private; 3844626Smckusick 3944626Smckusick static int np_xid; /* For NFS pings */ 4044626Smckusick #define NPXID_ALLOC() (++np_xid) 4144626Smckusick /*#define NPXID_ALLOC() ((++np_xid&0x0fffffff) == 0 ? npxid_gc() : np_xid)*/ 4244626Smckusick 4344626Smckusick /* 4444626Smckusick * Number of pings allowed to fail before host is declared down 4544626Smckusick * - three-fifths of the allowed mount time... 4644626Smckusick #define MAX_ALLOWED_PINGS ((((ALLOWED_MOUNT_TIME + 5 * AM_PINGER - 1) * 3) / 5) / AM_PINGER) 4744626Smckusick */ 4844626Smckusick #define MAX_ALLOWED_PINGS (3 + /* for luck ... */ 1) 4944626Smckusick 5044626Smckusick /* 5144626Smckusick * How often to ping when starting a new server 5244626Smckusick */ 5344626Smckusick #define FAST_NFS_PING 3 5444626Smckusick 5544626Smckusick #if (FAST_NFS_PING * MAX_ALLOWED_PINGS) >= ALLOWED_MOUNT_TIME 5644626Smckusick #error: sanity check failed 5744626Smckusick /* 5844626Smckusick you cannot do things this way... 5944626Smckusick sufficient fast pings must be given the chance to fail 6044626Smckusick within the allowed mount time 6144626Smckusick */ 6244626Smckusick #endif /* (FAST_NFS_PING * MAX_ALLOWED_PINGS) >= ALLOWED_MOUNT_TIME */ 6344626Smckusick 6444626Smckusick static int ping_len; 6544626Smckusick static char ping_buf[sizeof(struct rpc_msg) + 32]; 6644626Smckusick 6744626Smckusick /* 6847526Spendry * Flush any cached data 6947526Spendry */ 7047526Spendry void flush_srvr_nfs_cache P((void)); 7147526Spendry void flush_srvr_nfs_cache() 7247526Spendry { 7347526Spendry fserver *fs = 0; 7447526Spendry 7547526Spendry ITER(fs, fserver, &nfs_srvr_list) { 7647526Spendry nfs_private *np = (nfs_private *) fs->fs_private; 7747526Spendry if (np) { 7847526Spendry np->np_mountd_inval = TRUE; 7947526Spendry np->np_error = -1; 8047526Spendry } 8147526Spendry } 8247526Spendry } 8347526Spendry 8447526Spendry /* 8544626Smckusick * Startup the NFS ping 8644626Smckusick */ 8747526Spendry static void start_ping(P_void); 8844626Smckusick static void start_ping() 8944626Smckusick { 9044626Smckusick XDR ping_xdr; 9144626Smckusick struct rpc_msg ping_msg; 9244626Smckusick 9344626Smckusick rpc_msg_init(&ping_msg, NFS_PROGRAM, NFS_VERSION, NFSPROC_NULL); 9444626Smckusick 9544626Smckusick /* 9644626Smckusick * Create an XDR endpoint 9744626Smckusick */ 9844626Smckusick xdrmem_create(&ping_xdr, ping_buf, sizeof(ping_buf), XDR_ENCODE); 9944626Smckusick 10044626Smckusick /* 10144626Smckusick * Create the NFS ping message 10244626Smckusick */ 10344626Smckusick if (!xdr_callmsg(&ping_xdr, &ping_msg)) { 10444626Smckusick plog(XLOG_ERROR, "Couldn't create ping RPC message"); 10544626Smckusick going_down(3); 10644626Smckusick } 10744626Smckusick 10844626Smckusick /* 10944626Smckusick * Find out how long it is 11044626Smckusick */ 11144626Smckusick ping_len = xdr_getpos(&ping_xdr); 11244626Smckusick 11344626Smckusick /* 11444626Smckusick * Destroy the XDR endpoint - we don't need it anymore 11544626Smckusick */ 11644626Smckusick xdr_destroy(&ping_xdr); 11744626Smckusick } 11844626Smckusick 11944626Smckusick 12044626Smckusick /* 12144626Smckusick * Called when a portmap reply arrives 12244626Smckusick */ 12347526Spendry /*ARGSUSED*/ 12447526Spendry static void got_portmap P((voidp pkt, int len, struct sockaddr_in *sa, struct sockaddr_in *ia, voidp idv, int done)); 12544626Smckusick static void got_portmap(pkt, len, sa, ia, idv, done) 12644626Smckusick voidp pkt; 12744626Smckusick int len; 12847526Spendry struct sockaddr_in *sa; 12947526Spendry struct sockaddr_in *ia; 13044626Smckusick voidp idv; 13144626Smckusick int done; 13244626Smckusick { 13344626Smckusick fserver *fs2 = (fserver *) idv; 13444626Smckusick fserver *fs = 0; 13547526Spendry 13647526Spendry /* 13747526Spendry * Find which fileserver we are talking about 13847526Spendry */ 13944626Smckusick ITER(fs, fserver, &nfs_srvr_list) 14044626Smckusick if (fs == fs2) 14144626Smckusick break; 14244626Smckusick 14344626Smckusick if (fs == fs2) { 14444626Smckusick u_long port = 0; /* XXX - should be short but protocol is naff */ 14544626Smckusick int error = done ? pickup_rpc_reply(pkt, len, (voidp) &port, xdr_u_long) : -1; 14644626Smckusick nfs_private *np = (nfs_private *) fs->fs_private; 14744626Smckusick if (!error && port) { 14844626Smckusick #ifdef DEBUG 14944626Smckusick dlog("got port (%d) for mountd on %s", port, fs->fs_host); 15044626Smckusick #endif /* DEBUG */ 15144626Smckusick /* 15244626Smckusick * Grab the port number. Portmap sends back 15344626Smckusick * an unsigned long in native ordering, so it 15444626Smckusick * needs converting to a unsigned short in 15544626Smckusick * network ordering. 15644626Smckusick */ 15744626Smckusick np->np_mountd = htons((u_short) port); 15844626Smckusick np->np_mountd_inval = FALSE; 15944626Smckusick np->np_error = 0; 16044626Smckusick } else { 16144626Smckusick #ifdef DEBUG 16244626Smckusick dlog("Error fetching port for mountd on %s", fs->fs_host); 16344626Smckusick #endif /* DEBUG */ 16444626Smckusick /* 16544626Smckusick * Almost certainly no mountd running on remote host 16644626Smckusick */ 16744626Smckusick np->np_error = error ? error : ETIMEDOUT; 16844626Smckusick } 16944626Smckusick if (fs->fs_flags & FSF_WANT) 17044626Smckusick wakeup_srvr(fs); 17144626Smckusick } else if (done) { 17244626Smckusick #ifdef DEBUG 17344626Smckusick dlog("Got portmap for old port request"); 17444626Smckusick #endif /* DEBUG */ 17544626Smckusick } else { 17644626Smckusick #ifdef DEBUG 17744626Smckusick dlog("portmap request timed out"); 17844626Smckusick #endif /* DEBUG */ 17944626Smckusick } 18044626Smckusick } 18144626Smckusick 18244626Smckusick /* 18344626Smckusick * Obtain portmap information 18444626Smckusick */ 18547526Spendry static int call_portmap P((fserver *fs, AUTH *auth, unsigned long prog, unsigned long vers, unsigned long prot)); 18644626Smckusick static int call_portmap(fs, auth, prog, vers, prot) 18744626Smckusick fserver *fs; 18844626Smckusick AUTH *auth; 18944626Smckusick unsigned long prog, vers, prot; 19044626Smckusick { 19144626Smckusick struct rpc_msg pmap_msg; 19244626Smckusick int len; 19344626Smckusick char iobuf[UDPMSGSIZE]; 19444626Smckusick int error; 19544626Smckusick struct pmap pmap; 19644626Smckusick 19744626Smckusick rpc_msg_init(&pmap_msg, PMAPPROG, PMAPVERS, (unsigned long) 0); 19844626Smckusick pmap.pm_prog = prog; 19944626Smckusick pmap.pm_vers = vers; 20044626Smckusick pmap.pm_prot = prot; 20144626Smckusick pmap.pm_port = 0; 20244626Smckusick len = make_rpc_packet(iobuf, sizeof(iobuf), PMAPPROC_GETPORT, 20344626Smckusick &pmap_msg, (voidp) &pmap, xdr_pmap, auth); 20444626Smckusick if (len > 0) { 20544626Smckusick struct sockaddr_in sin; 20644626Smckusick bzero((voidp) &sin, sizeof(sin)); 20744626Smckusick sin = *fs->fs_ip; 20844626Smckusick sin.sin_port = htons(PMAPPORT); 20944626Smckusick error = fwd_packet(RPC_XID_PORTMAP, (voidp) iobuf, len, 21044626Smckusick &sin, &sin, (voidp) fs, got_portmap); 21144626Smckusick } else { 21244626Smckusick error = -len; 21344626Smckusick } 21444626Smckusick return error; 21544626Smckusick } 21644626Smckusick 21744626Smckusick static void nfs_keepalive P((fserver*)); 21844626Smckusick 21944626Smckusick static void recompute_portmap P((fserver *fs)); 22044626Smckusick static void recompute_portmap(fs) 22144626Smckusick fserver *fs; 22244626Smckusick { 223*49683Spendry int error; 224*49683Spendry 225*49683Spendry if (nfs_auth) 226*49683Spendry error = 0; 227*49683Spendry else 228*49683Spendry error = make_nfs_auth(); 229*49683Spendry 230*49683Spendry if (error) { 23144626Smckusick nfs_private *np = (nfs_private *) fs->fs_private; 232*49683Spendry np->np_error = error; 23344626Smckusick } else { 23444626Smckusick call_portmap(fs, nfs_auth, MOUNTPROG, 23544626Smckusick MOUNTVERS, (unsigned long) IPPROTO_UDP); 23644626Smckusick } 23744626Smckusick } 23844626Smckusick 23944626Smckusick /* 24044626Smckusick * This is called when we get a reply to an RPC ping. 24144626Smckusick * The value of id was taken from the nfs_private 24244626Smckusick * structure when the ping was transmitted. 24344626Smckusick */ 24444626Smckusick /*ARGSUSED*/ 24547526Spendry static void nfs_pinged P((voidp pkt, int len, struct sockaddr_in *sp, struct sockaddr_in *tsp, voidp idv, int done)); 24644626Smckusick static void nfs_pinged(pkt, len, sp, tsp, idv, done) 24744626Smckusick voidp pkt; 24844626Smckusick int len; 24947526Spendry struct sockaddr_in *sp; 25047526Spendry struct sockaddr_in *tsp; 25144626Smckusick voidp idv; 25244626Smckusick int done; 25344626Smckusick { 25444626Smckusick int xid = (int) idv; 25544626Smckusick fserver *fs; 25647526Spendry #ifdef DEBUG 25744626Smckusick int found_map = 0; 25847526Spendry #endif /* DEBUG */ 25944626Smckusick 26044626Smckusick if (!done) 26144626Smckusick return; 26244626Smckusick 26344626Smckusick /* 26444626Smckusick * For each node... 26544626Smckusick */ 26644626Smckusick ITER(fs, fserver, &nfs_srvr_list) { 26744626Smckusick nfs_private *np = (nfs_private *) fs->fs_private; 26844626Smckusick if (np->np_xid == xid) { 26944626Smckusick /* 27044626Smckusick * Reset the ping counter. 27144626Smckusick * Update the keepalive timer. 27244626Smckusick * Log what happened. 27344626Smckusick */ 27444626Smckusick if (fs->fs_flags & FSF_DOWN) { 27544626Smckusick fs->fs_flags &= ~FSF_DOWN; 27644626Smckusick if (fs->fs_flags & FSF_VALID) { 27744626Smckusick srvrlog(fs, "is up"); 27844626Smckusick } else { 27947526Spendry if (np->np_ping > 1) 28047526Spendry srvrlog(fs, "ok"); 28147526Spendry #ifdef DEBUG 28247526Spendry else 28347526Spendry srvrlog(fs, "starts up"); 28447526Spendry #endif 28544626Smckusick fs->fs_flags |= FSF_VALID; 28644626Smckusick } 28744626Smckusick 28844626Smckusick #ifdef notdef 28944626Smckusick /* why ??? */ 29044626Smckusick if (fs->fs_flags & FSF_WANT) 29144626Smckusick wakeup_srvr(fs); 29244626Smckusick #endif /* notdef */ 293*49683Spendry map_flush_srvr(fs); 29444626Smckusick } else { 29544626Smckusick if (fs->fs_flags & FSF_VALID) { 29644626Smckusick #ifdef DEBUG 29744626Smckusick dlog("file server %s type nfs is still up", fs->fs_host); 29844626Smckusick #endif /* DEBUG */ 29944626Smckusick } else { 30047526Spendry if (np->np_ping > 1) 30147526Spendry srvrlog(fs, "ok"); 30244626Smckusick fs->fs_flags |= FSF_VALID; 30344626Smckusick } 30444626Smckusick } 30544626Smckusick 30644626Smckusick /* 30744626Smckusick * Adjust ping interval 30844626Smckusick */ 30944626Smckusick untimeout(fs->fs_cid); 31044626Smckusick fs->fs_cid = timeout(fs->fs_pinger, nfs_keepalive, (voidp) fs); 31144626Smckusick 31244626Smckusick /* 31344626Smckusick * Update ttl for this server 31444626Smckusick */ 31544626Smckusick np->np_ttl = clocktime() + 31644626Smckusick (MAX_ALLOWED_PINGS - 1) * FAST_NFS_PING + fs->fs_pinger - 1; 31744626Smckusick 31844626Smckusick /* 31944626Smckusick * New RPC xid... 32044626Smckusick */ 32144626Smckusick np->np_xid = NPXID_ALLOC(); 32244626Smckusick 32344626Smckusick /* 32444626Smckusick * Failed pings is zero... 32544626Smckusick */ 32644626Smckusick np->np_ping = 0; 32744626Smckusick 32844626Smckusick /* 32944626Smckusick * Recompute portmap information if not known 33044626Smckusick */ 33144626Smckusick if (np->np_mountd_inval) 33244626Smckusick recompute_portmap(fs); 33344626Smckusick 33447526Spendry #ifdef DEBUG 33544626Smckusick found_map++; 33647526Spendry #endif /* DEBUG */ 33744626Smckusick break; 33844626Smckusick } 33944626Smckusick } 34044626Smckusick 34144626Smckusick #ifdef DEBUG 34244626Smckusick if (found_map == 0) 34344626Smckusick dlog("Spurious ping packet"); 34444626Smckusick #endif /* DEBUG */ 34544626Smckusick } 34644626Smckusick 34744626Smckusick /* 34844626Smckusick * Called when no ping-reply received 34944626Smckusick */ 35044626Smckusick static void nfs_timed_out P((fserver *fs)); 35144626Smckusick static void nfs_timed_out(fs) 35244626Smckusick fserver *fs; 35344626Smckusick { 35444626Smckusick nfs_private *np = (nfs_private *) fs->fs_private; 35544626Smckusick 35644626Smckusick /* 35747526Spendry * Another ping has failed 35847526Spendry */ 35947526Spendry np->np_ping++; 36047526Spendry 36147526Spendry /* 36244626Smckusick * Not known to be up any longer 36344626Smckusick */ 36444626Smckusick if (FSRV_ISUP(fs)) { 36544626Smckusick fs->fs_flags &= ~FSF_VALID; 36647526Spendry if (np->np_ping > 1) 36747526Spendry srvrlog(fs, "not responding"); 36844626Smckusick } 36944626Smckusick 37044626Smckusick /* 37144626Smckusick * If ttl has expired then guess that it is dead 37244626Smckusick */ 37344626Smckusick if (np->np_ttl < clocktime()) { 374*49683Spendry int oflags = fs->fs_flags; 37544626Smckusick if ((fs->fs_flags & FSF_DOWN) == 0) { 37644626Smckusick /* 37744626Smckusick * Server was up, but is now down. 37844626Smckusick */ 37944626Smckusick srvrlog(fs, "is down"); 38044626Smckusick fs->fs_flags |= FSF_DOWN|FSF_VALID; 38144626Smckusick /* 38244626Smckusick * Since the server is down, the portmap 38344626Smckusick * information may now be wrong, so it 38444626Smckusick * must be flushed from the local cache 38544626Smckusick */ 38644626Smckusick flush_nfs_fhandle_cache(fs); 38744626Smckusick np->np_error = -1; 38847526Spendry #ifdef notdef 38944626Smckusick /* 39044626Smckusick * Pretend just one ping has failed now 39144626Smckusick */ 39244626Smckusick np->np_ping = 1; 39347526Spendry #endif 39444626Smckusick } else { 39544626Smckusick /* 39644626Smckusick * Known to be down 39744626Smckusick */ 39847526Spendry #ifdef DEBUG 399*49683Spendry if ((fs->fs_flags & FSF_VALID) == 0) 400*49683Spendry srvrlog(fs, "starts down"); 40147526Spendry #endif 402*49683Spendry fs->fs_flags |= FSF_VALID; 40344626Smckusick } 404*49683Spendry if (oflags != fs->fs_flags && (fs->fs_flags & FSF_WANT)) 40547526Spendry wakeup_srvr(fs); 40644626Smckusick } else { 40744626Smckusick #ifdef DEBUG 40844626Smckusick if (np->np_ping > 1) 40944626Smckusick dlog("%d pings to %s failed - at most %d allowed", np->np_ping, fs->fs_host, MAX_ALLOWED_PINGS); 41044626Smckusick #endif /* DEBUG */ 41144626Smckusick } 41244626Smckusick 41344626Smckusick /* 41444626Smckusick * Run keepalive again 41544626Smckusick */ 41644626Smckusick nfs_keepalive(fs); 41744626Smckusick } 41844626Smckusick 41944626Smckusick /* 42044626Smckusick * Keep track of whether a server is alive 42144626Smckusick */ 42244626Smckusick static void nfs_keepalive P((fserver *fs)); 42344626Smckusick static void nfs_keepalive(fs) 42444626Smckusick fserver *fs; 42544626Smckusick { 42644626Smckusick int error; 42744626Smckusick nfs_private *np = (nfs_private *) fs->fs_private; 42844626Smckusick int fstimeo = -1; 42944626Smckusick 43044626Smckusick /* 43144626Smckusick * Send an NFS ping to this node 43244626Smckusick */ 43344626Smckusick 43444626Smckusick if (ping_len == 0) 43544626Smckusick start_ping(); 43644626Smckusick 43744626Smckusick /* 43844626Smckusick * Queue the packet... 43944626Smckusick */ 44044626Smckusick error = fwd_packet(MK_RPC_XID(RPC_XID_NFSPING, np->np_xid), (voidp) ping_buf, 44144626Smckusick ping_len, fs->fs_ip, (struct sockaddr_in *) 0, (voidp) np->np_xid, nfs_pinged); 44244626Smckusick 44344626Smckusick /* 44444626Smckusick * See if a hard error occured 44544626Smckusick */ 44644626Smckusick switch (error) { 44744626Smckusick case ENETDOWN: 44844626Smckusick case ENETUNREACH: 44944626Smckusick case EHOSTDOWN: 45044626Smckusick case EHOSTUNREACH: 45144626Smckusick np->np_ping = MAX_ALLOWED_PINGS; /* immediately down */ 45244626Smckusick np->np_ttl = (time_t) 0; 45344626Smckusick /* 45444626Smckusick * This causes an immediate call to nfs_timed_out 45544626Smckusick * whenever the server was thought to be up. 45644626Smckusick * See +++ below. 45744626Smckusick */ 45844626Smckusick fstimeo = 0; 45944626Smckusick break; 46044626Smckusick 46144626Smckusick case 0: 46244626Smckusick #ifdef DEBUG 46344626Smckusick dlog("Sent NFS ping to %s", fs->fs_host); 46444626Smckusick #endif /* DEBUG */ 46544626Smckusick break; 46644626Smckusick } 46744626Smckusick 46844626Smckusick #ifdef DEBUG 46944626Smckusick /*dlog("keepalive, ping = %d", np->np_ping);*/ 47044626Smckusick #endif /* DEBUG */ 47144626Smckusick 47244626Smckusick /* 47344626Smckusick * Back off the ping interval if we are not getting replies and 47444626Smckusick * the remote system is know to be down. 47544626Smckusick */ 47644626Smckusick switch (fs->fs_flags & (FSF_DOWN|FSF_VALID)) { 47744626Smckusick case FSF_VALID: /* Up */ 47844626Smckusick if (fstimeo < 0) /* +++ see above */ 47944626Smckusick fstimeo = FAST_NFS_PING; 48044626Smckusick break; 48144626Smckusick 48244626Smckusick case FSF_VALID|FSF_DOWN: /* Down */ 48344626Smckusick fstimeo = fs->fs_pinger; 48444626Smckusick break; 48544626Smckusick 48644626Smckusick default: /* Unknown */ 48744626Smckusick fstimeo = FAST_NFS_PING; 48844626Smckusick break; 48944626Smckusick } 49044626Smckusick 49144626Smckusick #ifdef DEBUG 49244626Smckusick dlog("NFS timeout in %d seconds", fstimeo); 49344626Smckusick #endif /* DEBUG */ 49444626Smckusick 49544626Smckusick fs->fs_cid = timeout(fstimeo, nfs_timed_out, (voidp) fs); 49644626Smckusick } 49744626Smckusick 49847526Spendry int nfs_srvr_port P((fserver *fs, u_short *port, voidp wchan)); 49944626Smckusick int nfs_srvr_port(fs, port, wchan) 50044626Smckusick fserver *fs; 50144626Smckusick u_short *port; 50244626Smckusick voidp wchan; 50344626Smckusick { 50444626Smckusick int error = -1; 50544626Smckusick if ((fs->fs_flags & FSF_VALID) == FSF_VALID) { 50644626Smckusick if ((fs->fs_flags & FSF_DOWN) == 0) { 50744626Smckusick nfs_private *np = (nfs_private *) fs->fs_private; 50844626Smckusick if (np->np_error == 0) { 50944626Smckusick *port = np->np_mountd; 51044626Smckusick error = 0; 51144626Smckusick } else { 51244626Smckusick error = np->np_error; 51344626Smckusick } 51447526Spendry /* 51547526Spendry * Now go get the port mapping again in case it changed. 51647526Spendry * Note that it is used even if (np_mountd_inval) 51747526Spendry * is True. The flag is used simply as an 51847526Spendry * indication that the mountd may be invalid, not 51947526Spendry * that it is known to be invalid. 52047526Spendry */ 52147526Spendry if (np->np_mountd_inval) 52247526Spendry recompute_portmap(fs); 52347526Spendry else 52447526Spendry np->np_mountd_inval = TRUE; 52544626Smckusick } else { 52644626Smckusick error = EWOULDBLOCK; 52744626Smckusick } 52844626Smckusick } 52944626Smckusick if (error < 0 && wchan && !(fs->fs_flags & FSF_WANT)) { 53044626Smckusick /* 53144626Smckusick * If a wait channel is supplied, and no 53244626Smckusick * error has yet occured, then arrange 53344626Smckusick * that a wakeup is done on the wait channel, 53444626Smckusick * whenever a wakeup is done on this fs node. 53544626Smckusick * Wakeup's are done on the fs node whenever 53644626Smckusick * it changes state - thus causing control to 53744626Smckusick * come back here and new, better things to happen. 53844626Smckusick */ 53944626Smckusick fs->fs_flags |= FSF_WANT; 54044626Smckusick sched_task(wakeup_task, wchan, (voidp) fs); 54144626Smckusick } 54244626Smckusick return error; 54344626Smckusick } 54444626Smckusick 54544626Smckusick static void start_nfs_pings P((fserver *fs, int pingval)); 54644626Smckusick static void start_nfs_pings(fs, pingval) 54744626Smckusick fserver *fs; 54844626Smckusick int pingval; 54944626Smckusick { 55044626Smckusick if (!(fs->fs_flags & FSF_PINGING)) { 55144626Smckusick fs->fs_flags |= FSF_PINGING; 55244626Smckusick if (fs->fs_cid) 55344626Smckusick untimeout(fs->fs_cid); 55444626Smckusick if (pingval < 0) { 55544626Smckusick srvrlog(fs, "wired up"); 55644626Smckusick fs->fs_flags |= FSF_VALID; 55744626Smckusick fs->fs_flags &= ~FSF_DOWN; 55844626Smckusick } else { 55944626Smckusick nfs_keepalive(fs); 56044626Smckusick } 56144626Smckusick } else { 56244626Smckusick #ifdef DEBUG 56344626Smckusick dlog("Already running pings to %s", fs->fs_host); 56444626Smckusick #endif /* DEBUG */ 56544626Smckusick } 56644626Smckusick } 56744626Smckusick 56844626Smckusick /* 56944626Smckusick * Find an nfs server for a host. 57044626Smckusick */ 57144626Smckusick fserver *find_nfs_srvr P((mntfs *mf)); 57244626Smckusick fserver *find_nfs_srvr(mf) 57344626Smckusick mntfs *mf; 57444626Smckusick { 57544626Smckusick fserver *fs; 57644626Smckusick struct hostent *hp = 0; 57744626Smckusick char *host = mf->mf_fo->opt_rhost; 57844626Smckusick struct sockaddr_in *ip; 57944626Smckusick nfs_private *np; 58044626Smckusick int pingval; 58144626Smckusick 58244626Smckusick /* 58344626Smckusick * Get ping interval from mount options. 58444626Smckusick * Current only used to decide whether pings 58544626Smckusick * are required or not. < 0 = no pings. 58644626Smckusick */ 58744626Smckusick { struct mntent mnt; 58847526Spendry mnt.mnt_opts = mf->mf_mopts; 58944626Smckusick pingval = hasmntval(&mnt, "ping"); 59044626Smckusick #ifdef HAS_TCP_NFS 59144626Smckusick /* 59244626Smckusick * Over TCP mount, don't bother to do pings. 59344626Smckusick * This is experimental - maybe you want to 59444626Smckusick * do pings anyway... 59544626Smckusick */ 59644626Smckusick if (pingval == 0 && hasmntopt(&mnt, "tcp")) 59744626Smckusick pingval = -1; 59844626Smckusick #endif /* HAS_TCP_NFS */ 59944626Smckusick } 60044626Smckusick 60144626Smckusick 60244626Smckusick /* 60347526Spendry * lookup host address and canonical name 60444626Smckusick */ 60547526Spendry hp = gethostbyname(host); 60647526Spendry 60747526Spendry /* 60847526Spendry * New code from Bob Harris <harris@basil-rathbone.mit.edu> 60947526Spendry * Use canonical name to keep track of file server 61047526Spendry * information. This way aliases do not generate 61147526Spendry * multiple NFS pingers. (Except when we're normalizing 61247526Spendry * hosts.) 61347526Spendry */ 61447526Spendry if (hp && !normalize_hosts) host = hp->h_name; 61547526Spendry 61644626Smckusick ITER(fs, fserver, &nfs_srvr_list) { 61744626Smckusick if (STREQ(host, fs->fs_host)) { 61844626Smckusick start_nfs_pings(fs, pingval); 61944626Smckusick fs->fs_refc++; 62044626Smckusick return fs; 62144626Smckusick } 62244626Smckusick } 62344626Smckusick 62444626Smckusick 62547526Spendry 62644626Smckusick /* 62744626Smckusick * Get here if we can't find an entry 62844626Smckusick */ 62944626Smckusick if (hp) { 63044626Smckusick switch (hp->h_addrtype) { 63144626Smckusick case AF_INET: 63244626Smckusick ip = ALLOC(sockaddr_in); 63344626Smckusick bzero((voidp) ip, sizeof(*ip)); 63444626Smckusick ip->sin_family = AF_INET; 63547526Spendry bcopy((voidp) hp->h_addr, (voidp) &ip->sin_addr, sizeof(ip->sin_addr)); 63647526Spendry 63744626Smckusick ip->sin_port = htons(NFS_PORT); 63844626Smckusick break; 63944626Smckusick 64044626Smckusick default: 64144626Smckusick ip = 0; 64244626Smckusick break; 64344626Smckusick } 64444626Smckusick } else { 64547526Spendry plog(XLOG_USER, "Unknown host: %s", host); 64644626Smckusick ip = 0; 64744626Smckusick } 64844626Smckusick 64944626Smckusick /* 65044626Smckusick * Allocate a new server 65144626Smckusick */ 65244626Smckusick fs = ALLOC(fserver); 65344626Smckusick fs->fs_refc = 1; 65444626Smckusick fs->fs_host = strdup(hp ? hp->h_name : "unknown_hostname"); 65547526Spendry if (normalize_hosts) host_normalize(&fs->fs_host); 65644626Smckusick fs->fs_ip = ip; 65744626Smckusick fs->fs_cid = 0; 65844626Smckusick if (ip) { 65944626Smckusick fs->fs_flags = FSF_DOWN; /* Starts off down */ 66044626Smckusick } else { 66144626Smckusick fs->fs_flags = FSF_ERROR|FSF_VALID; 66244626Smckusick mf->mf_flags |= MFF_ERROR; 66344626Smckusick mf->mf_error = ENOENT; 66444626Smckusick } 66544626Smckusick fs->fs_type = "nfs"; 66644626Smckusick fs->fs_pinger = AM_PINGER; 66744626Smckusick np = ALLOC(nfs_private); 66844626Smckusick bzero((voidp) np, sizeof(*np)); 66944626Smckusick np->np_mountd_inval = TRUE; 67044626Smckusick np->np_xid = NPXID_ALLOC(); 67144626Smckusick np->np_error = -1; 67244626Smckusick /* 67344626Smckusick * Initially the server will be deemed dead after 67444626Smckusick * MAX_ALLOWED_PINGS of the fast variety have failed. 67544626Smckusick */ 67644626Smckusick np->np_ttl = clocktime() + MAX_ALLOWED_PINGS * FAST_NFS_PING - 1; 67744626Smckusick fs->fs_private = (voidp) np; 67844626Smckusick fs->fs_prfree = (void (*)()) free; 67944626Smckusick 68044626Smckusick if (!(fs->fs_flags & FSF_ERROR)) { 68144626Smckusick /* 68244626Smckusick * Start of keepalive timer 68344626Smckusick */ 68444626Smckusick start_nfs_pings(fs, pingval); 68544626Smckusick } 68644626Smckusick 68744626Smckusick /* 68844626Smckusick * Add to list of servers 68944626Smckusick */ 69044626Smckusick ins_que(&fs->fs_q, &nfs_srvr_list); 69144626Smckusick 69244626Smckusick return fs; 69344626Smckusick } 694