afs: clarify cold and warm shutdown logic
[openafs.git] / src / afs / afs_call.c
index d63c793..b046df3 100644 (file)
@@ -41,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. */
@@ -87,6 +87,11 @@ afs_int32 afs_rx_idledead_rep = AFS_IDLEDEADTIME_REP;
 
 static int afscall_set_rxpck_received = 0;
 
+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.
  */
@@ -94,6 +99,7 @@ static int
 afs_InitSetup(int preallocs)
 {
     int code;
+    afs_uint32 host;
 
     if (afs_InitSetup_done)
        return EAGAIN;
@@ -122,6 +128,14 @@ afs_InitSetup(int preallocs)
     /* 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);
@@ -276,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;
@@ -595,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
@@ -692,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),
@@ -699,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);
        }
@@ -911,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);
        }
@@ -1097,6 +1369,10 @@ afs_syscall_call(long parm, long parm2, long parm3,
        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) /
@@ -1188,14 +1464,14 @@ afs_syscall_call(long parm, long parm2, long parm3,
        }
     }
     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);
@@ -1217,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)
@@ -1254,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)
@@ -1295,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) {
@@ -1323,6 +1595,30 @@ afs_syscall_call(long parm, long parm2, long 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;
     }
@@ -1364,7 +1660,7 @@ afs_CheckInit(void)
 
 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;
@@ -1380,6 +1676,8 @@ afs_shutdown(void)
     if (afs_shuttingdown != AFS_RUNNING)
        return;
 
+    afs_cold_shutdown = ((cold_flag == AFS_COLD) ? 1 : 0);
+
     afs_shuttingdown = AFS_FLUSHING_CB;
 
     /* Give up all of our callbacks if we can. */
@@ -1387,6 +1685,10 @@ afs_shutdown(void)
 
     afs_shuttingdown = AFS_SHUTDOWN;
 
+#if defined(AFS_SUN5_ENV)
+    afs_kstat_shutdown();
+#endif
+
     if (afs_cold_shutdown)
        afs_warn("afs: COLD ");
     else
@@ -1465,6 +1767,7 @@ afs_shutdown(void)
     afs_warn("NetIfPoller... ");
     osi_StopNetIfPoller();
 #endif
+    rxi_FreeAllPackets();
 
     afs_termState = AFSOP_STOP_COMPLETE;
 
@@ -1499,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));