Expand ProgramType enumeration
[openafs.git] / src / vol / volume.c
index 313cb42..46b238a 100644 (file)
@@ -25,6 +25,7 @@
 #include <rx/xdr.h>
 #include <afs/afsint.h>
 #include <ctype.h>
+#include <signal.h>
 #ifndef AFS_NT40_ENV
 #include <sys/param.h>
 #if !defined(AFS_SGI_ENV)
 #endif /* AFS_PTHREAD_ENV */
 #include "vutils.h"
 #ifndef AFS_NT40_ENV
-#include <dir/dir.h>
+#include <afs/dir.h>
 #include <unistd.h>
 #endif
 
@@ -152,9 +153,13 @@ pthread_mutex_t vol_glock_mutex;
 pthread_mutex_t vol_trans_mutex;
 pthread_cond_t vol_put_volume_cond;
 pthread_cond_t vol_sleep_cond;
+pthread_cond_t vol_init_attach_cond;
 int vol_attach_threads = 1;
 #endif /* AFS_PTHREAD_ENV */
 
+/* start-time configurable I/O parameters */
+ih_init_params vol_io_params;
+
 #ifdef AFS_DEMAND_ATTACH_FS
 pthread_mutex_t vol_salvsync_mutex;
 
@@ -207,6 +212,7 @@ static int VolumeExternalName_r(VolumeId volumeId, char * name, size_t len);
 int LogLevel;                  /* Vice loglevel--not defined as extern so that it will be
                                 * defined when not linked with vice, XXXX */
 ProgramType programType;       /* The type of program using the package */
+static VolumePackageOptions vol_opts;
 
 /* extended volume package statistics */
 VolPkgStats VStats;
@@ -407,8 +413,6 @@ struct Lock vol_listLock;   /* Lock obtained when listing volumes:
                                 * list volumes */
 
 
-static int TimeZoneCorrection; /* Number of seconds west of GMT */
-
 /* Common message used when the volume goes off line */
 char *VSalvageMessage =
     "Files in this volume are currently unavailable; call operations";
@@ -419,6 +423,7 @@ int VInit;                  /* 0 - uninitialized,
                                 * 3 - initialized, all volumes have been attached, and
                                 * VConnectFS() has completed. */
 
+static int vinit_attach_abort = 0;
 
 bit32 VolumeCacheCheck;                /* Incremented everytime a volume goes on line--
                                 * used to stamp volume headers and in-core
@@ -432,16 +437,55 @@ bit32 VolumeCacheCheck;           /* Incremented everytime a volume goes on line--
 /***************************************************/
 /* Startup routines                                */
 /***************************************************/
+/**
+ * assign default values to a VolumePackageOptions struct.
+ *
+ * Always call this on a VolumePackageOptions struct first, then set any
+ * specific options you want, then call VInitVolumePackage2.
+ *
+ * @param[in]  pt   caller's program type
+ * @param[out] opts volume package options
+ */
+void
+VOptDefaults(ProgramType pt, VolumePackageOptions *opts)
+{
+    opts->nLargeVnodes = opts->nSmallVnodes = 5;
+    opts->volcache = 0;
+
+    opts->canScheduleSalvage = 0;
+    opts->canUseFSSYNC = 0;
+    opts->canUseSALVSYNC = 0;
+
+    switch (pt) {
+    case fileServer:
+       opts->canScheduleSalvage = 1;
+       opts->canUseSALVSYNC = 1;
+       break;
+
+    case salvageServer:
+       opts->canUseFSSYNC = 1;
+       break;
+
+    case volumeServer:
+       opts->nLargeVnodes = 0;
+       opts->nSmallVnodes = 0;
+
+       opts->canUseFSSYNC = 1;
+       break;
+
+    default:
+       /* noop */
+       break;
+    }
+}
 
 int
-VInitVolumePackage(ProgramType pt, afs_uint32 nLargeVnodes, afs_uint32 nSmallVnodes,
-                  int connect, afs_uint32 volcache)
+VInitVolumePackage2(ProgramType pt, VolumePackageOptions * opts)
 {
     int errors = 0;            /* Number of errors while finding vice partitions. */
-    struct timeval tv;
-    struct timezone tz;
 
     programType = pt;
+    vol_opts = *opts;
 
     memset(&VStats, 0, sizeof(VStats));
     VStats.hdr_cache_size = 200;
@@ -462,14 +506,13 @@ VInitVolumePackage(ProgramType pt, afs_uint32 nLargeVnodes, afs_uint32 nSmallVno
     assert(pthread_mutex_init(&vol_trans_mutex, NULL) == 0);
     assert(pthread_cond_init(&vol_put_volume_cond, NULL) == 0);
     assert(pthread_cond_init(&vol_sleep_cond, NULL) == 0);
+    assert(pthread_cond_init(&vol_init_attach_cond, NULL) == 0);
 #else /* AFS_PTHREAD_ENV */
     IOMGR_Initialize();
 #endif /* AFS_PTHREAD_ENV */
     Lock_Init(&vol_listLock);
 
     srandom(time(0));          /* For VGetVolumeInfo */
-    gettimeofday(&tv, &tz);
-    TimeZoneCorrection = tz.tz_minuteswest * 60;
 
 #ifdef AFS_DEMAND_ATTACH_FS
     assert(pthread_mutex_init(&vol_salvsync_mutex, NULL) == 0);
@@ -492,25 +535,52 @@ VInitVolumePackage(ProgramType pt, afs_uint32 nLargeVnodes, afs_uint32 nSmallVno
     }
 #endif
 #if defined(AFS_DEMAND_ATTACH_FS) && defined(SALVSYNC_BUILD_CLIENT)
-    if (programType == fileServer) {
+    if (VCanUseSALVSYNC()) {
        /* establish a connection to the salvager at this point */
        assert(VConnectSALV() != 0);
     }
 #endif /* AFS_DEMAND_ATTACH_FS */
 
-    if (volcache > VStats.hdr_cache_size)
-       VStats.hdr_cache_size = volcache;
+    if (opts->volcache > VStats.hdr_cache_size)
+       VStats.hdr_cache_size = opts->volcache;
     VInitVolumeHeaderCache(VStats.hdr_cache_size);
 
-    VInitVnodes(vLarge, nLargeVnodes);
-    VInitVnodes(vSmall, nSmallVnodes);
+    VInitVnodes(vLarge, opts->nLargeVnodes);
+    VInitVnodes(vSmall, opts->nSmallVnodes);
 
 
     errors = VAttachPartitions();
     if (errors)
        return -1;
 
-    if (programType == fileServer) {
+    if (programType != fileServer) {
+        errors = VInitAttachVolumes(programType);
+        if (errors) {
+            return -1;
+        }
+    }
+
+#ifdef FSSYNC_BUILD_CLIENT
+    if (VCanUseFSSYNC()) {
+       if (!VConnectFS()) {
+#ifdef AFS_DEMAND_ATTACH_FS
+           if (programType == salvageServer) {
+               Log("Unable to connect to file server; aborted\n");
+               exit(1);
+           }
+#endif /* AFS_DEMAND_ATTACH_FS */
+           Log("Unable to connect to file server; will retry at need\n");
+       }
+    }
+#endif /* FSSYNC_BUILD_CLIENT */
+    return 0;
+}
+
+int
+VInitAttachVolumes(ProgramType pt)
+{
+    assert(VInit==1);
+    if (pt == fileServer) {
        struct DiskPartition64 *diskP;
 #ifdef AFS_PTHREAD_ENV
        struct vinitvolumepackage_thread_t params;
@@ -549,9 +619,12 @@ VInitVolumePackage(ProgramType pt, afs_uint32 nLargeVnodes, afs_uint32 nSmallVno
 
            VOL_LOCK;
            for (i=0; i < threads; i++) {
+                AFS_SIGSET_DECL;
+                AFS_SIGSET_CLEAR();
                assert(pthread_create
                       (&tid, &attrs, &VInitVolumePackageThread,
                        &params) == 0);
+                AFS_SIGSET_RESTORE();
            }
 
            while(params.n_threads_complete < threads) {
@@ -586,24 +659,14 @@ VInitVolumePackage(ProgramType pt, afs_uint32 nLargeVnodes, afs_uint32 nSmallVno
        }
 #endif /* AFS_PTHREAD_ENV */
     }
-
+    VOL_LOCK;
     VInit = 2;                 /* Initialized, and all volumes have been attached */
-#ifdef FSSYNC_BUILD_CLIENT
-    if (programType == volumeUtility && connect) {
-       if (!VConnectFS()) {
-           Log("Unable to connect to file server; will retry at need\n");
-           /*exit(1);*/
-       }
-    }
-#ifdef AFS_DEMAND_ATTACH_FS
-    else if (programType == salvageServer) {
-       if (!VConnectFS()) {
-           Log("Unable to connect to file server; aborted\n");
-           exit(1);
-       }
-    }
-#endif /* AFS_DEMAND_ATTACH_FS */
-#endif /* FSSYNC_BUILD_CLIENT */
+#ifdef AFS_PTHREAD_ENV
+    assert(pthread_cond_broadcast(&vol_init_attach_cond) == 0);
+#else
+    LWP_NoYieldSignal(VInitAttachVolumes);
+#endif /* AFS_PTHREAD_ENV */
+    VOL_UNLOCK;
     return 0;
 }
 
@@ -623,6 +686,11 @@ VInitVolumePackageThread(void * args) {
     while (queue_IsNotEmpty(params)) {
         int nAttached = 0, nUnattached = 0;
 
+        if (vinit_attach_abort) {
+            Log("Aborting initialization\n");
+            goto done;
+        }
+
         dpq = queue_First(params,diskpartition_queue_t);
        queue_Remove(dpq);
        VOL_UNLOCK;
@@ -634,6 +702,7 @@ VInitVolumePackageThread(void * args) {
        VOL_LOCK;
     }
 
+done:
     params->n_threads_complete++;
     pthread_cond_signal(&params->thread_done_cv);
     VOL_UNLOCK;
@@ -661,6 +730,12 @@ VAttachVolumesByPartition(struct DiskPartition64 *diskP, int * nAttached, int *
   while ((dp = readdir(dirp))) {
     char *p;
     p = strrchr(dp->d_name, '.');
+
+    if (vinit_attach_abort) {
+      Log("Partition %s: abort attach volumes\n", diskP->name);
+      goto done;
+    }
+
     if (p != NULL && strcmp(p, VHDREXT) == 0) {
       Error error;
       Volume *vp;
@@ -687,6 +762,7 @@ VAttachVolumesByPartition(struct DiskPartition64 *diskP, int * nAttached, int *
   }
 
   Log("Partition %s: attached %d volumes; %d volumes not attached\n", diskP->name, *nAttached, *nUnattached);
+done:
   closedir(dirp);
   return ret;
 }
@@ -763,6 +839,12 @@ VShutdown_r(void)
 
     memset(&params, 0, sizeof(vshutdown_thread_t));
 
+    if (VInit < 2) {
+        Log("VShutdown:  aborting attach volumes\n");
+        vinit_attach_abort = 1;
+        VOL_CV_WAIT(&vol_init_attach_cond);
+    }
+
     for (params.n_parts=0, diskP = DiskPartitionList;
         diskP; diskP = diskP->next, params.n_parts++);
 
@@ -879,6 +961,17 @@ VShutdown_r(void)
     int i;
     register Volume *vp, *np;
     register afs_int32 code;
+
+    if (VInit < 2) {
+        Log("VShutdown:  aborting attach volumes\n");
+        vinit_attach_abort = 1;
+#ifdef AFS_PTHREAD_ENV
+        VOL_CV_WAIT(&vol_init_attach_cond);
+#else
+        LWP_WaitProcess(VInitAttachVolumes);
+#endif /* AFS_PTHREAD_ENV */
+    }
+
     Log("VShutdown:  shutting down on-line volumes...\n");
     for (i = 0; i < VolumeHashTable.Size; i++) {
        /* try to hold first volume in the hash table */
@@ -902,6 +995,7 @@ VShutdown_r(void)
 void
 VShutdown(void)
 {
+    assert(VInit>0);
     VOL_LOCK;
     VShutdown_r();
     VOL_UNLOCK;
@@ -1806,7 +1900,7 @@ VAttachVolumeByName_r(Error * ec, char *partition, char *name, int mode)
        goto done;
     }
 
-    if (programType == volumeUtility) {
+    if (VRequiresPartLock()) {
        assert(VInit == 3);
        VLockPartition_r(partition);
     } else if (programType == fileServer) {
@@ -1964,7 +2058,7 @@ VAttachVolumeByName_r(Error * ec, char *partition, char *name, int mode)
 
     DiskToVolumeHeader(&iheader, &diskHeader);
 #ifdef FSSYNC_BUILD_CLIENT
-    if (programType == volumeUtility && mode != V_SECRETLY && mode != V_PEEK) {
+    if (VCanUseFSSYNC() && mode != V_SECRETLY && mode != V_PEEK) {
         VOL_LOCK;
        if (FSYNC_VolOp(iheader.id, partition, FSYNC_VOL_NEEDVOLUME, mode, NULL)
            != SYNC_OK) {
@@ -1991,7 +2085,7 @@ VAttachVolumeByName_r(Error * ec, char *partition, char *name, int mode)
      * with vol_glock_mutex held */
     vp = attach2(ec, volumeId, path, &iheader, partp, vp, isbusy, mode);
 
-    if (programType == volumeUtility && vp) {
+    if (VCanUseFSSYNC() && vp) {
        if ((mode == V_VOLUPD) || (VolumeWriteable(vp) && (mode == V_CLONE))) {
            /* mark volume header as in use so that volser crashes lead to a
             * salvage attempt */
@@ -2029,7 +2123,7 @@ VAttachVolumeByName_r(Error * ec, char *partition, char *name, int mode)
      * fix is for the server to allow the return of readonly volumes
      * that it doesn't think are really checked out. */
 #ifdef FSSYNC_BUILD_CLIENT
-    if (programType == volumeUtility && vp == NULL &&
+    if (VCanUseFSSYNC() && vp == NULL &&
        mode != V_SECRETLY && mode != V_PEEK) {
        FSYNC_VolOp(iheader.id, partition, FSYNC_VOL_ON, 0, NULL);
     } else 
@@ -2085,7 +2179,7 @@ VAttachVolumeByName_r(Error * ec, char *partition, char *name, int mode)
     }
 
   done:
-    if (programType == volumeUtility) {
+    if (VRequiresPartLock()) {
        VUnlockPartition_r(partition);
     }
     if (*ec) {
@@ -2449,7 +2543,7 @@ attach2(Error * ec, VolId volumeId, char *path, register struct VolumeHeader * h
 #if defined(AFS_DEMAND_ATTACH_FS)
     if (*ec && ((*ec != VOFFLINE) || (V_attachState(vp) != VOL_STATE_UNATTACHED))) {
         VOL_LOCK;
-       if (programType == fileServer) {
+       if (VCanScheduleSalvage()) {
            VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, VOL_SALVAGE_INVALIDATE_HEADER);
            vp->nUsers = 0;
        } else {
@@ -2477,7 +2571,7 @@ attach2(Error * ec, VolId volumeId, char *path, register struct VolumeHeader * h
            vp->specialStatus = 0;
         VOL_LOCK;
 #if defined(AFS_DEMAND_ATTACH_FS)
-       if (programType == fileServer) {
+       if (VCanScheduleSalvage()) {
            VRequestSalvage_r(ec, vp, SALVSYNC_NEEDED, VOL_SALVAGE_INVALIDATE_HEADER);
            vp->nUsers = 0;
        } else {
@@ -2493,7 +2587,7 @@ attach2(Error * ec, VolId volumeId, char *path, register struct VolumeHeader * h
     }
 
     VOL_LOCK;
-    if (programType == fileServer) {
+    if (VShouldCheckInUse(mode)) {
 #ifndef FAST_RESTART
        if (V_inUse(vp) && VolumeWriteable(vp)) {
            if (!V_needsSalvaged(vp)) {
@@ -2512,7 +2606,15 @@ attach2(Error * ec, VolId volumeId, char *path, register struct VolumeHeader * h
        }
 #endif /* FAST_RESTART */
 
-       if (V_destroyMe(vp) == DESTROY_ME) {
+       if (programType == fileServer && V_destroyMe(vp) == DESTROY_ME) {
+           /* Only check destroyMe if we are the fileserver, since the
+            * volserver et al sometimes need to work with volumes with
+            * destroyMe set. Examples are 'temporary' volumes the
+            * volserver creates, and when we create a volume (destroyMe
+            * is set on creation; sometimes a separate volserver
+            * transaction is created to clear destroyMe).
+            */
+
 #if defined(AFS_DEMAND_ATTACH_FS)
            /* schedule a salvage so the volume goes away on disk */
            VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, VOL_SALVAGE_INVALIDATE_HEADER);
@@ -2927,7 +3029,7 @@ GetVolume(Error * ec, Error * client_ec, VolId volumeId, Volume * hint, int flag
                Log("Volume %u: couldn't reread volume header\n",
                    vp->hashid);
 #ifdef AFS_DEMAND_ATTACH_FS
-           if (programType == fileServer) {
+           if (VCanScheduleSalvage()) {
                VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, VOL_SALVAGE_INVALIDATE_HEADER);
            } else {
                FreeVolume(vp);
@@ -3187,7 +3289,7 @@ VOffline_r(Volume * vp, char *message)
     VolumeId vid = V_id(vp);
 #endif
 
-    assert(programType != volumeUtility);
+    assert(programType != volumeUtility && programType != volumeServer);
     if (!V_inUse(vp)) {
        VPutVolume_r(vp);
        return;
@@ -3300,7 +3402,7 @@ VDetachVolume_r(Error * ec, Volume * vp)
     int  useDone = FSYNC_VOL_ON;
 
     *ec = 0;                   /* always "succeeds" */
-    if (programType == volumeUtility) {
+    if (VCanUseFSSYNC()) {
        notifyServer = vp->needsPutBack;
        if (V_destroyMe(vp) == DESTROY_ME)
            useDone = FSYNC_VOL_DONE;
@@ -3328,7 +3430,7 @@ VDetachVolume_r(Error * ec, Volume * vp)
      * is not technically detached until the refcounts reach zero
      */
 #ifdef FSSYNC_BUILD_CLIENT
-    if (programType == volumeUtility && notifyServer) {
+    if (VCanUseFSSYNC() && notifyServer) {
        /* 
         * Note:  The server is not notified in the case of a bogus volume 
         * explicitly to make it possible to create a volume, do a partial 
@@ -3393,7 +3495,7 @@ VCloseVolumeHandles_r(Volume * vp)
 #endif
 
     /* Too time consuming and unnecessary for the volserver */
-    if (programType != volumeUtility) {
+    if (programType == fileServer) {
        IH_CONDSYNC(vp->vnodeIndex[vLarge].handle);
        IH_CONDSYNC(vp->vnodeIndex[vSmall].handle);
        IH_CONDSYNC(vp->diskDataHandle);
@@ -3438,7 +3540,7 @@ VReleaseVolumeHandles_r(Volume * vp)
 #endif
 
     /* Too time consuming and unnecessary for the volserver */
-    if (programType != volumeUtility) {
+    if (programType == fileServer) {
        IH_CONDSYNC(vp->vnodeIndex[vLarge].handle);
        IH_CONDSYNC(vp->vnodeIndex[vSmall].handle);
        IH_CONDSYNC(vp->diskDataHandle);
@@ -3712,11 +3814,14 @@ VCheckOffline(register Volume * vp)
        VCloseVolumeHandles_r(vp);
 
        if (LogLevel) {
-           Log("VOffline: Volume %u (%s) is now offline", V_id(vp),
-               V_name(vp));
-           if (V_offlineMessage(vp)[0])
-               Log(" (%s)", V_offlineMessage(vp));
-           Log("\n");
+           if (V_offlineMessage(vp)[0]) {
+               Log("VOffline: Volume %lu (%s) is now offline (%s)\n",
+                   afs_printable_uint32_lu(V_id(vp)), V_name(vp),
+                   V_offlineMessage(vp));
+           } else {
+               Log("VOffline: Volume %lu (%s) is now offline\n",
+                   afs_printable_uint32_lu(V_id(vp)), V_name(vp));
+           }
        }
 
        /* invalidate the volume header cache entry */
@@ -4041,7 +4146,7 @@ VRequestSalvage_r(Error * ec, Volume * vp, int reason, int flags)
      * (at some point in the future, we should consider
      *  making volser talk to salsrv)
      */
-    if (programType != fileServer) {
+    if (!VCanScheduleSalvage()) {
        VChangeState_r(vp, VOL_STATE_ERROR);
        *ec = VSALVAGE;
        return 1;
@@ -4405,7 +4510,7 @@ VReconnectSALV_r(void)
 /***************************************************/
 
 /* This must be called by any volume utility which needs to run while the
-   file server is also running.  This is separated from VInitVolumePackage so
+   file server is also running.  This is separated from VInitVolumePackage2 so
    that a utility can fork--and each of the children can independently
    initialize communication with the file server */
 #ifdef FSSYNC_BUILD_CLIENT
@@ -4989,7 +5094,34 @@ VolumeExternalName_r(VolumeId volumeId, char * name, size_t len)
 #define OneDay (24*60*60)      /* 24 hours */
 #endif /* OPENAFS_VOL_STATS */
 
-#define Midnight(date) ((date-TimeZoneCorrection)/OneDay*OneDay+TimeZoneCorrection)
+static time_t
+Midnight(time_t t) {
+    struct tm local, *l;
+    time_t midnight;
+
+#if defined(AFS_PTHREAD_ENV) && !defined(AFS_NT40_ENV)
+    l = localtime_r(&t, &local);
+#else
+    l = localtime(&t);
+#endif
+
+    if (l != NULL) {
+       /* the following is strictly speaking problematic on the
+          switching day to daylight saving time, after the switch,
+          as tm_isdst does not match.  Similarly, on the looong day when
+          switching back the OneDay check will not do what naively expected!
+          The effects are minor, though, and more a matter of interpreting
+          the numbers. */
+#ifndef AFS_PTHREAD_ENV
+       local = *l;
+#endif
+       local.tm_hour = local.tm_min=local.tm_sec = 0;
+       midnight = mktime(&local);
+       if (midnight != (time_t) -1) return(midnight);
+    }
+    return( (t/OneDay)*OneDay );
+
+}
 
 /*------------------------------------------------------------------------
  * [export] VAdjustVolumeStatistics
@@ -5332,7 +5464,7 @@ static void VLRU_Wait_r(struct VLRU_q * q);
  * @param[in] option  tunable option to modify
  * @param[in] val     new value for tunable parameter
  *
- * @pre @c VInitVolumePackage has not yet been called.
+ * @pre @c VInitVolumePackage2 has not yet been called.
  *
  * @post tunable parameter is modified
  *
@@ -5729,7 +5861,7 @@ VLRU_ScannerThread(void * args)
     }
 
     /* don't start the scanner until VLRU_offline_thresh
-     * plus a small delay for VInitVolumePackage to finish
+     * plus a small delay for VInitVolumePackage2 to finish
      * has gone by */
 
     sleep(VLRU_offline_thresh + 60);
@@ -6576,7 +6708,7 @@ FreeVolumeHeader(register Volume * vp)
  *    @retval 0 success
  *    @retval -1 failure
  *
- * @pre MUST be called prior to VInitVolumePackage
+ * @pre MUST be called prior to VInitVolumePackage2
  *
  * @post Volume Hash Table will have 2^logsize buckets
  */
@@ -7538,3 +7670,21 @@ VPrintExtendedCacheStats(int flags)
     VOL_UNLOCK;
 }
 #endif /* AFS_DEMAND_ATTACH_FS */
+
+afs_int32
+VCanScheduleSalvage(void)
+{
+    return vol_opts.canScheduleSalvage;
+}
+
+afs_int32
+VCanUseFSSYNC(void)
+{
+    return vol_opts.canUseFSSYNC;
+}
+
+afs_int32
+VCanUseSALVSYNC(void)
+{
+    return vol_opts.canUseSALVSYNC;
+}