afs: clarify cold and warm shutdown logic
[openafs.git] / src / afs / afs_call.c
index 7400236..b046df3 100644 (file)
 #include <afsconfig.h>
 #include "afs/param.h"
 
+#if defined(HAVE_LINUX_KTHREAD_RUN) && !defined(UKERNEL)
+#  include "h/kthread.h"
+#endif
 
 #include "afs/sysincludes.h"   /* Standard vendor system headers */
 #include "afsincludes.h"       /* Afs-based standard headers */
 #include "afs/afs_stats.h"
 #include "rx/rx_globals.h"
-#if !defined(UKERNEL) && !defined(AFS_LINUX20_ENV)
-#include "net/if.h"
-#ifdef AFS_SGI62_ENV
-#include "h/hashing.h"
-#endif
-#if !defined(AFS_HPUX110_ENV) && !defined(AFS_DARWIN_ENV)
-#include "netinet/in_var.h"
-#endif
+#if !defined(UKERNEL)
+# if !defined(AFS_LINUX20_ENV)
+#  include "net/if.h"
+#  ifdef AFS_SGI62_ENV
+#   include "h/hashing.h"
+#  endif
+#  if !defined(AFS_HPUX110_ENV) && !defined(AFS_DARWIN_ENV)
+#   include "netinet/in_var.h"
+#  endif
+# endif
 #endif /* !defined(UKERNEL) */
 #ifdef AFS_SUN510_ENV
 #include "h/ksynch.h"
 #include "h/sunddi.h"
 #endif
+#include <hcrypto/rand.h>
 
 #if defined(AFS_SUN5_ENV) || defined(AFS_AIX_ENV) || defined(AFS_SGI_ENV) || defined(AFS_HPUX_ENV)
 #define        AFS_MINBUFFERS  100
@@ -35,7 +41,7 @@
 #define        AFS_MINBUFFERS  50
 #endif
 
-#if (defined(AFS_SUN5_ENV) || (defined(AFS_LINUX24_ENV) && defined(HAVE_LINUX_COMPLETION_H)) || defined(AFS_DARWIN80_ENV)) && !defined(UKERNEL)
+#if (defined(AFS_SUN5_ENV) || defined(AFS_LINUX26_ENV) || defined(AFS_DARWIN80_ENV)) && !defined(UKERNEL)
 /* If AFS_DAEMONOP_ENV is defined, it indicates we run "daemon" AFS syscalls by
  * spawning a kernel thread to do the work, instead of running them in the
  * calling process. */
@@ -58,7 +64,6 @@ krwlock_t afsifinfo_lock;
 
 afs_int32 afs_initState = 0;
 afs_int32 afs_termState = 0;
-afs_int32 afs_setTime = 0;
 int afs_cold_shutdown = 0;
 char afs_SynchronousCloses = '\0';
 static int afs_CB_Running = 0;
@@ -66,21 +71,26 @@ static int AFS_Running = 0;
 static int afs_CacheInit_Done = 0;
 static int afs_Go_Done = 0;
 extern struct interfaceAddr afs_cb_interface;
+#ifdef RXK_LISTENER_ENV
 static int afs_RX_Running = 0;
+#endif
 static int afs_InitSetup_done = 0;
 afs_int32 afs_numcachefiles = -1;
 afs_int32 afs_numfilesperdir = -1;
 char afs_cachebasedir[1024];
+afs_int32 afs_rmtsys_enable = 0;
 
 afs_int32 afs_rx_deadtime = AFS_RXDEADTIME;
 afs_int32 afs_rx_harddead = AFS_HARDDEADTIME;
 afs_int32 afs_rx_idledead = AFS_IDLEDEADTIME;
+afs_int32 afs_rx_idledead_rep = AFS_IDLEDEADTIME_REP;
 
 static int afscall_set_rxpck_received = 0;
 
-#if defined(AFS_HPUX_ENV)
-extern int afs_vfs_mount();
-#endif /* defined(AFS_HPUX_ENV) */
+extern afs_int32 afs_volume_ttl;
+
+/* From afs_util.c */
+extern afs_int32 afs_md5inum;
 
 /* This is code which needs to be called once when the first daemon enters
  * the client. A non-zero return means an error and AFS should not start.
@@ -89,6 +99,7 @@ static int
 afs_InitSetup(int preallocs)
 {
     int code;
+    afs_uint32 host;
 
     if (afs_InitSetup_done)
        return EAGAIN;
@@ -114,11 +125,17 @@ afs_InitSetup(int preallocs)
 
     memset(afs_zeros, 0, AFS_ZEROS);
 
-    rx_SetBusyChannelError(RX_CALL_TIMEOUT);
-
     /* start RX */
     if(!afscall_set_rxpck_received)
     rx_extraPackets = AFS_NRXPACKETS;  /* smaller # of packets */
+
+    host = ntohl(rx_bindhost);
+    afs_warn("afs: Binding rx to %d.%d.%d.%d:%d\n",
+             (host >> 24),
+             (host >> 16) & 0xff,
+             (host >>  8) & 0xff,
+             (host)       & 0xff,
+             7001);
     code = rx_InitHost(rx_bindhost, htons(7001));
     if (code) {
        afs_warn("AFS: RX failed to initialize %d).\n", code);
@@ -273,9 +290,9 @@ afs_DaemonOp(long parm, long parm2, long parm3, long parm4, long parm5,
 #endif
 
 
-#if defined(AFS_LINUX24_ENV) && defined(HAVE_LINUX_COMPLETION_H)
+#if defined(AFS_LINUX26_ENV)
 struct afsd_thread_info {
-# if defined(AFS_LINUX26_ENV) && !defined(INIT_WORK_HAS_DATA)
+# if !defined(INIT_WORK_HAS_DATA)
     struct work_struct tq;
 # endif
     unsigned long parm;
@@ -290,11 +307,13 @@ afsd_thread(void *rock)
 # ifdef SYS_SETPRIORITY_EXPORTED
     int (*sys_setpriority) (int, int, int) = sys_call_table[__NR_setpriority];
 # endif
-# if defined(AFS_LINUX26_ENV)
+# if !defined(HAVE_LINUX_KTHREAD_RUN)
+#  if defined(AFS_LINUX26_ENV)
     daemonize("afsd");
-# else
+#  else
     daemonize();
-# endif
+#  endif
+# endif /* !HAVE_LINUX_KTHREAD_RUN */
                                /* doesn't do much, since we were forked from keventd, but
                                 * does call mm_release, which wakes up our parent (since it
                                 * used CLONE_VFORK) */
@@ -335,6 +354,9 @@ afsd_thread(void *rock)
        complete_and_exit(0, 0);
        break;
     case AFSOP_START_BKG:
+#ifdef AFS_NEW_BKG
+       afs_warn("Install matching afsd! Old background daemons not supported.\n");
+#else
        sprintf(current->comm, "afs_bkgstart");
        AFS_GLOCK();
        complete(arg->complete);
@@ -347,6 +369,7 @@ afsd_thread(void *rock)
        sprintf(current->comm, "afs_background");
        afs_BackgroundDaemon();
        AFS_GUNLOCK();
+#endif
        complete_and_exit(0, 0);
        break;
     case AFSOP_START_TRUNCDAEMON:
@@ -427,8 +450,14 @@ afsd_launcher(void *rock)
     struct afsd_thread_info *rock = container_of(work, struct afsd_thread_info, tq);
 # endif
 
+# if defined(HAVE_LINUX_KTHREAD_RUN)
+    if (IS_ERR(kthread_run(afsd_thread, (void *)rock, "afsd"))) {
+       afs_warn("kthread_run failed; afs startup will not complete\n");
+    }
+# else /* !HAVE_LINUX_KTHREAD_RUN */
     if (!kernel_thread(afsd_thread, (void *)rock, CLONE_VFORK | SIGCHLD))
        afs_warn("kernel_thread failed. afs startup will not complete\n");
+# endif /* !HAVE_LINUX_KTHREAD_RUN */
 }
 
 void
@@ -580,6 +609,268 @@ afs_DaemonOp(long parm, long parm2, long parm3, long parm4, long parm5,
 }
 #endif /* AFS_SUN5_ENV */
 
+#ifdef AFS_SOCKPROXY_ENV
+/**
+ * Deallocate packets.
+ *
+ * @param[inout]  a_pktlist  list of packets to be freed
+ * @param[in]     a_npkts    number of packets
+ */
+static void
+sockproxy_pkts_free(struct afs_pkt_hdr **a_pktlist, size_t a_npkts)
+{
+    int pkt_i;
+    struct afs_pkt_hdr *pktlist = *a_pktlist;
+
+    if (pktlist == NULL) {
+       return;
+    }
+    *a_pktlist = NULL;
+
+    for (pkt_i = 0; pkt_i < a_npkts; pkt_i++) {
+       struct afs_pkt_hdr *pkt = &pktlist[pkt_i];
+       afs_osi_Free(pkt->payload, pkt->size);
+       pkt->payload = NULL;
+    }
+    afs_osi_Free(pktlist, a_npkts * sizeof(pktlist[0]));
+}
+
+/**
+ * Copy packets from user-space.
+ *
+ * @param[in]   a_uaddr    address of list of packets in user-space
+ * @param[out]  a_pktlist  packets copied from user-space
+ * @param[in]   a_npkts    number of packets to be copied
+ *
+ * @return errno error codes.
+ */
+static int
+sockproxy_pkts_copyin(user_addr_t a_uaddr, struct afs_pkt_hdr **a_pktlist,
+                     size_t a_npkts)
+{
+    int code, pkt_i;
+    struct afs_pkt_hdr *pktlist_u;
+    struct afs_pkt_hdr *pktlist_k = NULL;
+
+    *a_pktlist = NULL;
+    if (a_npkts == 0) {
+       return 0;
+    }
+    if (a_npkts > AFS_SOCKPROXY_PKT_MAX) {
+       return E2BIG;
+    }
+
+    pktlist_u = afs_osi_Alloc(a_npkts * sizeof(pktlist_u[0]));
+    if (pktlist_u == NULL) {
+       code = ENOMEM;
+       goto done;
+    }
+
+    pktlist_k = afs_osi_Alloc(a_npkts * sizeof(pktlist_k[0]));
+    if (pktlist_k == NULL) {
+       code = ENOMEM;
+       goto done;
+    }
+    memset(pktlist_k, 0, a_npkts * sizeof(pktlist_k[0]));
+
+    AFS_COPYIN(a_uaddr, pktlist_u, a_npkts * sizeof(pktlist_u[0]), code);
+    if (code != 0) {
+       goto done;
+    }
+
+    for (pkt_i = 0; pkt_i < a_npkts; pkt_i++) {
+       struct afs_pkt_hdr *pkt_k = &pktlist_k[pkt_i];
+       struct afs_pkt_hdr *pkt_u = &pktlist_u[pkt_i];
+
+       if (pkt_u->size > AFS_SOCKPROXY_PAYLOAD_MAX) {
+           code = E2BIG;
+           goto done;
+       }
+       /*
+        * Notice that pkt_u->payload points to a user-space address. When
+        * copying the data from pkt_u to pkt_k, make sure that pkt_k->payload
+        * points to a valid address in kernel-space.
+        */
+       *pkt_k = *pkt_u;
+       pkt_k->payload = afs_osi_Alloc(pkt_k->size);
+       if (pkt_k->payload == NULL) {
+           code = ENOMEM;
+           goto done;
+       }
+
+       AFS_COPYIN((user_addr_t)pkt_u->payload, pkt_k->payload, pkt_k->size,
+                  code);
+       if (code != 0) {
+           goto done;
+       }
+    }
+
+    *a_pktlist = pktlist_k;
+    pktlist_k = NULL;
+    code = 0;
+
+ done:
+    sockproxy_pkts_free(&pktlist_k, a_npkts);
+    afs_osi_Free(pktlist_u, a_npkts * sizeof(pktlist_u[0]));
+
+    return code;
+}
+
+/**
+ * Copy packets to user-space.
+ *
+ * @param[in]  a_uaddr    dst address of list of packets in user-space
+ * @param[in]  a_pktlist  packets to be copied to user-space
+ * @param[in]  a_npkts    number of packets to be copied
+ *
+ * @return 0 on success; non-zero otherwise.
+ */
+static int
+sockproxy_pkts_copyout(user_addr_t a_uaddr, struct afs_pkt_hdr *a_pktlist,
+                      size_t a_npkts)
+{
+    int code, pkt_i;
+    struct afs_pkt_hdr *pktlist_u = NULL;
+    struct afs_pkt_hdr *pktlist_k = a_pktlist;
+
+    if (a_npkts == 0) {
+       return 0;
+    }
+    if (a_npkts > AFS_SOCKPROXY_PKT_MAX) {
+       return E2BIG;
+    }
+    if (a_pktlist == NULL) {
+       return EINVAL;
+    }
+
+    pktlist_u = afs_osi_Alloc(a_npkts * sizeof(pktlist_u[0]));
+    if (pktlist_u == NULL) {
+       code = ENOMEM;
+       goto done;
+    }
+
+    AFS_COPYIN(a_uaddr, pktlist_u, a_npkts * sizeof(pktlist_u[0]), code);
+    if (code != 0) {
+       goto done;
+    }
+
+    for (pkt_i = 0; pkt_i < a_npkts; pkt_i++) {
+       struct afs_pkt_hdr *pkt_k = &pktlist_k[pkt_i];
+       struct afs_pkt_hdr *pkt_u = &pktlist_u[pkt_i];
+       void *payload_uaddr;
+
+       if (pkt_k->size > pkt_u->size) {
+           code = ENOSPC;
+           goto done;
+       }
+
+       /* Copy pkt_k -> pkt_u, but preserve pkt_u->payload */
+       payload_uaddr = pkt_u->payload;
+       *pkt_u = *pkt_k;
+       pkt_u->payload = payload_uaddr;
+
+       AFS_COPYOUT(pkt_k->payload, (user_addr_t)pkt_u->payload, pkt_k->size,
+                   code);
+       if (code != 0) {
+           goto done;
+       }
+    }
+
+    AFS_COPYOUT(pktlist_u, a_uaddr, a_npkts * sizeof(pktlist_u[0]), code);
+    if (code != 0) {
+       goto done;
+    }
+
+ done:
+    afs_osi_Free(pktlist_u, a_npkts * sizeof(pktlist_u[0]));
+    return code;
+}
+
+/**
+ * Receive / send packets from / to user-space.
+ *
+ * @param[in]  a_parm_uspc  afs_uspc_param struct
+ * @param[in]  a_parm_pkts  packets to be received / sent
+ *
+ * @return 0 on success; non-zero otherwise.
+ */
+static int
+sockproxy_handler(user_addr_t a_parm_uspc, user_addr_t a_parm_pkts)
+{
+    int code;
+    size_t orig_npkts, npkts;
+    struct afs_uspc_param uspc;
+
+    struct afs_pkt_hdr *pkts_recv;
+    struct afs_pkt_hdr *pkts_send;
+
+    AFS_GUNLOCK();
+
+    orig_npkts = 0;
+    memset(&uspc, 0, sizeof(uspc));
+
+    pkts_recv = NULL;
+    pkts_send = NULL;
+
+    /* get response from user-space */
+    AFS_COPYIN(a_parm_uspc, &uspc, sizeof(uspc), code);
+    if (code != 0) {
+       afs_warn("afs: AFSOP_SOCKPROXY_HANDLER can't read uspc\n");
+       goto done;
+    }
+
+    npkts = uspc.req.usp.npkts;
+    orig_npkts = npkts;
+
+    if (uspc.reqtype == AFS_USPC_SOCKPROXY_RECV && npkts > 0) {
+       /* copyin packets in from user-space */
+       code = sockproxy_pkts_copyin(a_parm_pkts, &pkts_recv, npkts);
+       if (code) {
+           afs_warn("afs: AFSOP_SOCKPROXY_HANDLER can't read pkts\n");
+           goto done;
+       }
+    }
+
+    /*
+     * send response from user-space (if any) to the rx layer and wait for a
+     * new request.
+     */
+    code = rxk_SockProxyReply(&uspc, pkts_recv, &pkts_send);
+    if (code) {
+       afs_warn("afs: AFSOP_SOCKPROXY_HANDLER rxk_SockProxyReply failed\n");
+       goto done;
+    }
+
+    /* send request to user-space process */
+    AFS_COPYOUT(&uspc, a_parm_uspc, sizeof(uspc), code);
+    if (code) {
+       afs_warn("afs: AFSOP_SOCKPROXY_HANDLER can't write uspc\n");
+       goto done;
+    }
+
+    npkts = uspc.req.usp.npkts;
+    if (uspc.reqtype == AFS_USPC_SOCKPROXY_SEND && npkts > 0) {
+       /* check if process allocated enough memory to receive the packets */
+       if (npkts > orig_npkts) {
+           code = ENOSPC;
+           goto done;
+       }
+
+       /* copyout packets to user-space */
+       code = sockproxy_pkts_copyout(a_parm_pkts, pkts_send, npkts);
+       if (code != 0) {
+           afs_warn("afs: AFSOP_SOCKPROXY_HANDLER can't write pkts\n");
+           goto done;
+       }
+    }
+ done:
+    sockproxy_pkts_free(&pkts_recv, orig_npkts);
+    AFS_GLOCK();
+
+    return code;
+}
+#endif /* AFS_SOCKPROXY_ENV */
+
 #ifdef AFS_DARWIN100_ENV
 # define AFSKPTR(X) k ## X
 int
@@ -606,7 +897,7 @@ afs_syscall_call(long parm, long parm2, long parm3,
 #endif
 {
     afs_int32 code = 0;
-#if defined(AFS_SGI61_ENV) || defined(AFS_SUN57_ENV) || defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
+#if defined(AFS_SGI61_ENV) || defined(AFS_SUN5_ENV) || defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
     size_t bufferSize;
 #else /* AFS_SGI61_ENV */
     u_int bufferSize;
@@ -649,7 +940,7 @@ afs_syscall_call(long parm, long parm2, long parm3,
     put_vfs_context();
 #endif
 #ifdef AFS_DAEMONOP_ENV
-# if defined(AFS_DARWIN80_ENV)
+# if defined(AFS_NEW_BKG)
     if (parm == AFSOP_BKG_HANDLER) {
        /* if afs_uspc_param grows this should be checked */
        struct afs_uspc_param *mvParam = osi_AllocSmallSpace(AFS_SMALLOCSIZ);
@@ -677,6 +968,7 @@ afs_syscall_call(long parm, long parm2, long parm3,
        if (!code) {
            mvParam->retval = 0;
            /* for reqs where pointers are strings: */
+#  ifdef AFS_DARWIN_ENV
            if (mvParam->reqtype == AFS_USPC_UMV) {
                /* don't copy out random kernel memory */
                AFS_COPYOUT(param2, AFSKPTR(parm4),
@@ -684,6 +976,7 @@ afs_syscall_call(long parm, long parm2, long parm3,
                AFS_COPYOUT(param1, AFSKPTR(parm3),
                            MIN(namebufsz, strlen((char *)param1)+1), code);
            }
+#  endif /* AFS_DARWIN_ENV */
            AFS_COPYOUT((caddr_t)mvParam, AFSKPTR(parm2),
                       sizeof(struct afs_uspc_param), code);
        }
@@ -692,7 +985,7 @@ afs_syscall_call(long parm, long parm2, long parm3,
        afs_osi_Free(param2, namebufsz);
        osi_FreeSmallSpace(mvParam);
     } else
-# endif /* DARWIN80 */
+# endif /* AFS_NEW_BKG */
     if (parm < AFSOP_ADDCELL || parm == AFSOP_RXEVENT_DAEMON
        || parm == AFSOP_RXLISTENER_DAEMON) {
        afs_DaemonOp(parm, parm2, parm3, parm4, parm5, parm6);
@@ -784,7 +1077,7 @@ afs_syscall_call(long parm, long parm2, long parm3,
        AFS_GUNLOCK();
        exit(CLD_EXITED, 0);
 # endif /* AFS_SGI_ENV */
-# ifndef AFS_DARWIN80_ENV
+# ifndef AFS_NEW_BKG
     } else if (parm == AFSOP_START_BKG) {
        while (afs_initState < AFSOP_START_BKG)
            afs_osi_Sleep(&afs_initState);
@@ -805,7 +1098,7 @@ afs_syscall_call(long parm, long parm2, long parm3,
        AFS_GUNLOCK();
        exit(CLD_EXITED, 0);
 #  endif /* AFS_SGI_ENV */
-# endif /* ! AFS_DARWIN80_ENV */
+# endif /* ! AFS_NEW_BKG */
     } else if (parm == AFSOP_START_TRUNCDAEMON) {
        while (afs_initState < AFSOP_GO)
            afs_osi_Sleep(&afs_initState);
@@ -896,12 +1189,6 @@ afs_syscall_call(long parm, long parm2, long parm3,
        osi_Assert(tbuffer1 != NULL);
        code = afs_InitDynroot();
        if (!code) {
-#if 0
-           /* wait for basic init - XXX can't find any reason we need this? */
-           while (afs_initState < AFSOP_START_BKG)
-               afs_osi_Sleep(&afs_initState);
-#endif
-
            AFS_COPYIN(AFSKPTR(parm2), (caddr_t)tcell->hosts, sizeof(tcell->hosts),
                       code);
        }
@@ -981,7 +1268,10 @@ afs_syscall_call(long parm, long parm2, long parm3,
        }
        afs_CacheInit_Done = 1;
         code = afs_icl_InitLogs();
-       afs_setTime = cparms.setTimeFlag;
+       if (cparms.setTimeFlag) {
+           afs_warn("afs: AFSOP_CACHEINIT setTimeFlag ignored; are you "
+                    "running an old afsd?\n");
+       }
 
        code =
            afs_CacheInit(cparms.cacheScaches, cparms.cacheFiles,
@@ -1031,10 +1321,6 @@ afs_syscall_call(long parm, long parm2, long parm3,
        code = 0;
        AFS_COPYINSTR(AFSKPTR(parm2), tbuffer, AFS_SMALLOCSIZ, &bufferSize,
                      code);
-       if (code) {
-           osi_FreeSmallSpace(tbuffer);
-           goto out;
-       }
        if (!code) {
            tbuffer[AFS_SMALLOCSIZ - 1] = '\0'; /* null-terminate the name */
            /* We have the cache dir copied in.  Call the cache init routine */
@@ -1064,7 +1350,11 @@ afs_syscall_call(long parm, long parm2, long parm3,
        while (afs_initState < AFSOP_GO)
            afs_osi_Sleep(&afs_initState);
        afs_initState = 101;
-       afs_setTime = parm2;
+       if (parm2) {
+           /* parm2 used to set afs_setTime */
+           afs_warn("afs: AFSOP_GO setTime flag ignored; are you running an "
+                    "old afsd?\n");
+       }
        if (afs_tpct1 + afs_tpct2 != 100) {
            afs_tpct1 = 0;
            afs_tpct2 = 0;
@@ -1076,7 +1366,13 @@ afs_syscall_call(long parm, long parm2, long parm3,
 #if    (!defined(AFS_NONFSTRANS)) || defined(AFS_AIX_IAUTH_ENV)
        afs_nfsclient_init();
 #endif
-       afs_uuid_create(&afs_cb_interface.uuid);
+       if (afs_uuid_create(&afs_cb_interface.uuid) != 0)
+           memset(&afs_cb_interface.uuid, 0, sizeof(afsUUID));
+
+#if defined(AFS_SUN5_ENV)
+       afs_kstat_init();
+#endif
+
        printf("found %d non-empty cache files (%d%%).\n",
               afs_stats_cmperf.cacheFilesReused,
               (100 * afs_stats_cmperf.cacheFilesReused) /
@@ -1167,41 +1463,15 @@ afs_syscall_call(long parm, long parm2, long parm3,
            afs_CheckServers(0, NULL);     /* check up servers */
        }
     }
-#ifdef AFS_SGI53_ENV
-    else if (parm == AFSOP_NFSSTATICADDR) {
-       extern int (*nfs_rfsdisptab_v2) ();
-       nfs_rfsdisptab_v2 = (int (*)())parm2;
-    } else if (parm == AFSOP_NFSSTATICADDR2) {
-       extern int (*nfs_rfsdisptab_v2) ();
-# ifdef _K64U64
-       nfs_rfsdisptab_v2 = (int (*)())((parm2 << 32) | (parm3 & 0xffffffff));
-# else /* _K64U64 */
-       nfs_rfsdisptab_v2 = (int (*)())(parm3 & 0xffffffff);
-# endif /* _K64U64 */
-    }
-# if defined(AFS_SGI62_ENV) && !defined(AFS_SGI65_ENV)
-    else if (parm == AFSOP_SBLOCKSTATICADDR2) {
-       extern int (*afs_sblockp) ();
-       extern void (*afs_sbunlockp) ();
-#  ifdef _K64U64
-       afs_sblockp = (int (*)())((parm2 << 32) | (parm3 & 0xffffffff));
-       afs_sbunlockp = (void (*)())((parm4 << 32) | (parm5 & 0xffffffff));
-#  else
-       afs_sblockp = (int (*)())(parm3 & 0xffffffff);
-       afs_sbunlockp = (void (*)())(parm5 & 0xffffffff);
-#  endif /* _K64U64 */
-    }
-# endif /* AFS_SGI62_ENV && !AFS_SGI65_ENV */
-#endif /* AFS_SGI53_ENV */
     else if (parm == AFSOP_SHUTDOWN) {
-       afs_cold_shutdown = 0;
-       if (parm2 == 1)
-           afs_cold_shutdown = 1;
        if (afs_globalVFS != 0) {
            afs_warn("AFS isn't unmounted yet! Call aborted\n");
            code = EACCES;
-       } else
-           afs_shutdown();
+       } else if (parm2 == AFS_COLD) {
+           afs_shutdown(AFS_COLD);
+       } else {
+           afs_shutdown(AFS_WARM);
+       }
     } else if (parm == AFSOP_AFS_VFSMOUNT) {
 #ifdef AFS_HPUX_ENV
        vfsmount(parm2, parm3, parm4, parm5);
@@ -1223,30 +1493,17 @@ afs_syscall_call(long parm, long parm2, long parm3,
        mtu = ((i == -1) ? htonl(1500) : afs_cb_interface.mtu[i]);
 # else /* AFS_USERSPACE_IP_ADDR */
        rx_ifnet_t tifnp;
+       RX_NET_EPOCH_ENTER();
 
        tifnp = rxi_FindIfnet(parm2, NULL);     /*  make iterative */
        mtu = (tifnp ? rx_ifnet_mtu(tifnp) : htonl(1500));
+
+       RX_NET_EPOCH_EXIT();
 # endif /* else AFS_USERSPACE_IP_ADDR */
 #endif /* !AFS_SUN5_ENV */
        if (!code)
            AFS_COPYOUT((caddr_t) & mtu, AFSKPTR(parm3),
                        sizeof(afs_int32), code);
-#ifdef AFS_AIX32_ENV
-/* this is disabled for now because I can't figure out how to get access
- * to these kernel variables.  It's only for supporting user-mode rx
- * programs -- it makes a huge difference on the 220's in my testbed,
- * though I don't know why. The bosserver does this with /etc/no, so it's
- * being handled a different way for the servers right now.  */
-/*      {
-       static adjusted = 0;
-       extern u_long sb_max_dflt;
-       if (!adjusted) {
-         adjusted = 1;
-         if (sb_max_dflt < 131072) sb_max_dflt = 131072;
-         if (sb_max < 131072) sb_max = 131072;
-       }
-      } */
-#endif /* AFS_AIX32_ENV */
     } else if (parm == AFSOP_GETMASK) {        /* parm2 == addr in net order */
        afs_uint32 mask = 0;
 #if    !defined(AFS_SUN5_ENV)
@@ -1260,10 +1517,13 @@ afs_syscall_call(long parm, long parm2, long parm3,
        }
 # else /* AFS_USERSPACE_IP_ADDR */
        rx_ifnet_t tifnp;
+       RX_NET_EPOCH_ENTER();
 
        tifnp = rxi_FindIfnet(parm2, &mask);    /* make iterative */
        if (!tifnp)
            code = -1;
+
+       RX_NET_EPOCH_EXIT();
 # endif /* else AFS_USERSPACE_IP_ADDR */
 #endif /* !AFS_SUN5_ENV */
        if (!code)
@@ -1301,8 +1561,14 @@ afs_syscall_call(long parm, long parm2, long parm3,
     else if (parm == AFSOP_SET_DYNROOT) {
        code = afs_SetDynrootEnable(parm2);
     } else if (parm == AFSOP_SET_FAKESTAT) {
-       afs_fakestat_enable = parm2;
-       code = 0;
+       if (parm2 >= 0 && parm2 <= 2) {
+           afs_fakestat_enable = parm2;
+           code = 0;
+       } else {
+           afs_warn("afs: afsd gave us unknown fakestat value %ld (are afsd "
+                    "and libafs running the same version?\n", parm2);
+           code = EINVAL;
+       }
     } else if (parm == AFSOP_SET_BACKUPTREE) {
        afs_bkvolpref = parm2;
     } else if (parm == AFSOP_SET_RXPCK) {
@@ -1310,6 +1576,49 @@ afs_syscall_call(long parm, long parm2, long parm3,
        afscall_set_rxpck_received = 1;
     } else if (parm == AFSOP_SET_RXMAXMTU) {
        rx_MyMaxSendSize = rx_maxReceiveSizeUser = rx_maxReceiveSize = parm2;
+    } else if (parm == AFSOP_SET_RXMAXFRAGS) {
+       rxi_nSendFrags = rxi_nRecvFrags = parm2;
+    } else if (parm == AFSOP_SET_RMTSYS_FLAG) {
+       afs_rmtsys_enable = parm2;
+       code = 0;
+#ifndef UKERNEL
+    } else if (parm == AFSOP_SEED_ENTROPY) {
+       unsigned char *seedbuf;
+
+       if (parm3 > 4096) {
+           code = EFAULT;
+       } else {
+           seedbuf = afs_osi_Alloc(parm3);
+           AFS_COPYIN(AFSKPTR(parm2), seedbuf, parm3, code);
+           RAND_seed(seedbuf, parm3);
+           memset(seedbuf, 0, parm3);
+           afs_osi_Free(seedbuf, parm3);
+       }
+#endif
+    } else if (parm == AFSOP_SET_INUMCALC) {
+       switch (parm2) {
+       case AFS_INUMCALC_COMPAT:
+           afs_md5inum = 0;
+           code = 0;
+           break;
+       case AFS_INUMCALC_MD5:
+           afs_md5inum = 1;
+           code = 0;
+           break;
+       default:
+           code = EINVAL;
+       }
+    } else if (parm == AFSOP_SET_VOLUME_TTL) {
+       if ((parm2 < AFS_MIN_VOLUME_TTL) || (parm2 > AFS_MAX_VOLUME_TTL)) {
+           code = EFAULT;
+       } else {
+           afs_volume_ttl = parm2;
+           code = 0;
+       }
+#ifdef AFS_SOCKPROXY_ENV
+    } else if (parm == AFSOP_SOCKPROXY_HANDLER) {
+       code = sockproxy_handler(AFSKPTR(parm2), AFSKPTR(parm3));
+#endif
     } else {
        code = EINVAL;
     }
@@ -1349,9 +1658,9 @@ afs_CheckInit(void)
     return code;
 }
 
-int afs_shuttingdown = 0;
+enum afs_shutdown_state afs_shuttingdown = AFS_RUNNING;
 void
-afs_shutdown(void)
+afs_shutdown(enum afs_shutdown_type cold_flag)
 {
     extern short afs_brsDaemons;
     extern afs_int32 afs_CheckServerDaemonStarted;
@@ -1364,21 +1673,45 @@ afs_shutdown(void)
       return;
     }
 
-    if (afs_shuttingdown)
+    if (afs_shuttingdown != AFS_RUNNING)
        return;
 
-    /* Give up all of our callbacks if we can. This must be done before setting
-     * afs_shuttingdown, since it calls afs_InitReq, which will fail if
-     * afs_shuttingdown is set. */
+    afs_cold_shutdown = ((cold_flag == AFS_COLD) ? 1 : 0);
+
+    afs_shuttingdown = AFS_FLUSHING_CB;
+
+    /* Give up all of our callbacks if we can. */
     afs_FlushVCBs(2);
 
-    afs_shuttingdown = 1;
+    afs_shuttingdown = AFS_SHUTDOWN;
+
+#if defined(AFS_SUN5_ENV)
+    afs_kstat_shutdown();
+#endif
 
     if (afs_cold_shutdown)
        afs_warn("afs: COLD ");
     else
        afs_warn("afs: WARM ");
-    afs_warn("shutting down of: CB... ");
+    afs_warn("shutting down of: vcaches... ");
+
+#if !defined(AFS_FBSD_ENV)
+    /* The FBSD afs_unmount() calls vflush(), which reclaims all vnodes
+     * on the mountpoint, flushing them in the process.  In the presence
+     * of bugs, flushing again here can cause panics. */
+    afs_FlushAllVCaches();
+#endif
+
+    afs_termState = AFSOP_STOP_BKG;
+
+    afs_warn("BkG... ");
+    /* Wake-up afs_brsDaemons so that we don't have to wait for a bkg job! */
+    while (afs_termState == AFSOP_STOP_BKG) {
+       afs_osi_Wakeup(&afs_brsDaemons);
+       afs_osi_Sleep(&afs_termState);
+    }
+
+    afs_warn("CB... ");
 
     afs_termState = AFSOP_STOP_RXCALLBACK;
     rx_WakeupServerProcs();
@@ -1400,12 +1733,6 @@ afs_shutdown(void)
            afs_osi_Sleep(&afs_termState);
        }
     }
-    afs_warn("BkG... ");
-    /* Wake-up afs_brsDaemons so that we don't have to wait for a bkg job! */
-    while (afs_termState == AFSOP_STOP_BKG) {
-       afs_osi_Wakeup(&afs_brsDaemons);
-       afs_osi_Sleep(&afs_termState);
-    }
     afs_warn("CTrunc... ");
     /* Cancel cache truncate daemon. */
     while (afs_termState == AFSOP_STOP_TRUNCDAEMON) {
@@ -1416,7 +1743,7 @@ afs_shutdown(void)
     afs_StopAFSDB();
     while (afs_termState == AFSOP_STOP_AFSDB)
        afs_osi_Sleep(&afs_termState);
-#if    defined(AFS_SUN5_ENV) || defined(RXK_LISTENER_ENV)
+#if    defined(AFS_SUN5_ENV) || defined(RXK_LISTENER_ENV) || defined(RXK_UPCALL_ENV)
     afs_warn("RxEvent... ");
     /* cancel rx event daemon */
     while (afs_termState == AFSOP_STOP_RXEVENT)
@@ -1440,6 +1767,7 @@ afs_shutdown(void)
     afs_warn("NetIfPoller... ");
     osi_StopNetIfPoller();
 #endif
+    rxi_FreeAllPackets();
 
     afs_termState = AFSOP_STOP_COMPLETE;
 
@@ -1474,6 +1802,7 @@ afs_shutdown(void)
     shutdown_nfsclnt();
 #endif
     shutdown_afstest();
+    shutdown_dynroot();
     shutdown_AFS();
     /* The following hold the cm stats */
     memset(&afs_cmstats, 0, sizeof(struct afs_CMStats));
@@ -1481,7 +1810,7 @@ afs_shutdown(void)
     memset(&afs_stats_cmfullperf, 0, sizeof(struct afs_stats_CMFullPerf));
     afs_warn(" ALL allocated tables... ");
 
-    afs_shuttingdown = 0;
+    afs_shuttingdown = AFS_RUNNING;
     afs_warn("done\n");
 
     return;                    /* Just kill daemons for now */
@@ -1491,7 +1820,7 @@ void
 shutdown_afstest(void)
 {
     AFS_STATCNT(shutdown_afstest);
-    afs_initState = afs_termState = afs_setTime = 0;
+    afs_initState = afs_termState = 0;
     AFS_Running = afs_CB_Running = 0;
     afs_CacheInit_Done = afs_Go_Done = 0;
     if (afs_cold_shutdown) {