#include <afsconfig.h>
#include <afs/param.h>
-RCSID
- ("$Header$");
#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)
#include "volume.h"
#include "partition.h"
#include "volume_inline.h"
+#include "common.h"
+
#ifdef AFS_PTHREAD_ENV
#include <assert.h>
#else /* AFS_PTHREAD_ENV */
#endif /* AFS_PTHREAD_ENV */
#include "vutils.h"
#ifndef AFS_NT40_ENV
-#include <dir/dir.h>
+#include <afs/dir.h>
#include <unistd.h>
#endif
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;
+
+/*
+ * Set this to 1 to disallow SALVSYNC communication in all threads; used
+ * during shutdown, since the salvageserver may have gone away.
+ */
+static volatile sig_atomic_t vol_disallow_salvsync = 0;
#endif /* AFS_DEMAND_ATTACH_FS */
#ifdef AFS_OSF_ENV
extern void *calloc(), *realloc();
#endif
-/*@printflike@*/ extern void Log(const char *format, ...);
-
/* Forward declarations */
-static Volume *attach2(Error * ec, VolId vid, char *path,
- register struct VolumeHeader *header,
+static Volume *attach2(Error * ec, VolId volumeId, char *path,
struct DiskPartition64 *partp, Volume * vp,
int isbusy, int mode);
static void ReallyFreeVolume(Volume * vp);
static void FreeVolumeHeader(register Volume * vp);
static void AddVolumeToHashTable(register Volume * vp, int hashid);
static void DeleteVolumeFromHashTable(register Volume * vp);
+#if 0
static int VHold(Volume * vp);
+#endif
static int VHold_r(Volume * vp);
static void VGetBitmap_r(Error * ec, Volume * vp, VnodeClass class);
static void VReleaseVolumeHandles_r(Volume * vp);
static int VCheckOffline(register Volume * vp);
static int VCheckDetach(register Volume * vp);
static Volume * GetVolume(Error * ec, Error * client_ec, VolId volumeId, Volume * hint, int flags);
-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;
#endif /* !AFS_HAVE_FFS */
#ifdef AFS_PTHREAD_ENV
+/**
+ * disk partition queue element
+ */
typedef struct diskpartition_queue_t {
- struct rx_queue queue;
- struct DiskPartition64 * diskP;
+ struct rx_queue queue; /**< queue header */
+ struct DiskPartition64 *diskP; /**< disk partition table entry */
} diskpartition_queue_t;
+
+#ifndef AFS_DEMAND_ATTACH_FS
+
typedef struct vinitvolumepackage_thread_t {
struct rx_queue queue;
pthread_cond_t thread_done_cv;
int n_threads_complete;
} vinitvolumepackage_thread_t;
static void * VInitVolumePackageThread(void * args);
+
+#else /* !AFS_DEMAND_ATTTACH_FS */
+#define VINIT_BATCH_MAX_SIZE 512
+
+/**
+ * disk partition work queue
+ */
+struct partition_queue {
+ struct rx_queue head; /**< diskpartition_queue_t queue */
+ pthread_mutex_t mutex;
+ pthread_cond_t cv;
+};
+
+/**
+ * volumes parameters for preattach
+ */
+struct volume_init_batch {
+ struct rx_queue queue; /**< queue header */
+ int thread; /**< posting worker thread */
+ int last; /**< indicates thread is done */
+ int size; /**< number of volume ids in batch */
+ Volume *batch[VINIT_BATCH_MAX_SIZE]; /**< volumes ids to preattach */
+};
+
+/**
+ * volume parameters work queue
+ */
+struct volume_init_queue {
+ struct rx_queue head; /**< volume_init_batch queue */
+ pthread_mutex_t mutex;
+ pthread_cond_t cv;
+};
+
+/**
+ * volume init worker thread parameters
+ */
+struct vinitvolumepackage_thread_param {
+ int nthreads; /**< total number of worker threads */
+ int thread; /**< thread number for this worker thread */
+ struct partition_queue *pq; /**< queue partitions to scan */
+ struct volume_init_queue *vq; /**< queue of volume to preattach */
+};
+
+static void *VInitVolumePackageThread(void *args);
+static struct DiskPartition64 *VInitNextPartition(struct partition_queue *pq);
+static VolId VInitNextVolumeId(DIR *dirp);
+static int VInitPreAttachVolumes(int nthreads, struct volume_init_queue *vq);
+
+#endif /* !AFS_DEMAND_ATTACH_FS */
#endif /* AFS_PTHREAD_ENV */
+#ifndef AFS_DEMAND_ATTACH_FS
static int VAttachVolumesByPartition(struct DiskPartition64 *diskP,
int * nAttached, int * nUnattached);
+#endif /* AFS_DEMAND_ATTACH_FS */
#ifdef AFS_DEMAND_ATTACH_FS
/* online salvager */
static int VCheckSalvage(register Volume * vp);
-static int VUpdateSalvagePriority_r(Volume * vp);
+#if defined(SALVSYNC_BUILD_CLIENT) || defined(FSSYNC_BUILD_CLIENT)
static int VScheduleSalvage_r(Volume * vp);
-static int VCancelSalvage_r(Volume * vp, int reason);
+#endif
/* Volume hash table */
static void VReorderHash_r(VolumeHashChainHead * head, Volume * pp, Volume * vp);
/* VLRU */
static void VLRU_ComputeConstants(void);
static void VInitVLRU(void);
-static void VLRU_Init_Node_r(volatile Volume * vp);
-static void VLRU_Add_r(volatile Volume * vp);
-static void VLRU_Delete_r(volatile Volume * vp);
-static void VLRU_UpdateAccess_r(volatile Volume * vp);
+static void VLRU_Init_Node_r(Volume * vp);
+static void VLRU_Add_r(Volume * vp);
+static void VLRU_Delete_r(Volume * vp);
+static void VLRU_UpdateAccess_r(Volume * vp);
static void * VLRU_ScannerThread(void * args);
static void VLRU_Scan_r(int idx);
static void VLRU_Promote_r(int idx);
static void VLRU_Demote_r(int idx);
-static void VLRU_SwitchQueues(volatile Volume * vp, int new_idx, int append);
+static void VLRU_SwitchQueues(Volume * vp, int new_idx, int append);
/* soft detach */
-static int VCheckSoftDetach(volatile Volume * vp, afs_uint32 thresh);
-static int VCheckSoftDetachCandidate(volatile Volume * vp, afs_uint32 thresh);
-static int VSoftDetachVolume_r(volatile Volume * vp, afs_uint32 thresh);
+static int VCheckSoftDetach(Volume * vp, afs_uint32 thresh);
+static int VCheckSoftDetachCandidate(Volume * vp, afs_uint32 thresh);
+static int VSoftDetachVolume_r(Volume * vp, afs_uint32 thresh);
pthread_key_t VThread_key;
* 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";
* 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
/* Startup routines */
/***************************************************/
+#if defined(FAST_RESTART) && defined(AFS_DEMAND_ATTACH_FS)
+# error FAST_RESTART and DAFS are incompatible. For the DAFS equivalent \
+ of FAST_RESTART, use the -unsafe-nosalvage fileserver argument
+#endif
+
+/**
+ * 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;
+
+#ifdef FAST_RESTART
+ opts->unsafe_attach = 1;
+#else /* !FAST_RESTART */
+ opts->unsafe_attach = 0;
+#endif /* !FAST_RESTART */
+
+ 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->canScheduleSalvage = 1;
+ 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;
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);
}
#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;
+}
+
+
+#if !defined(AFS_PTHREAD_ENV)
+/**
+ * Attach volumes in vice partitions
+ *
+ * @param[in] pt calling program type
+ *
+ * @return 0
+ * @note This is the original, non-threaded version of attach parititions.
+ *
+ * @post VInit state is 2
+ */
+int
+VInitAttachVolumes(ProgramType pt)
+{
+ assert(VInit==1);
+ if (pt == fileServer) {
+ struct DiskPartition64 *diskP;
+ /* Attach all the volumes in this partition */
+ for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
+ int nAttached = 0, nUnattached = 0;
+ assert(VAttachVolumesByPartition(diskP, &nAttached, &nUnattached) == 0);
+ }
+ }
+ VOL_LOCK;
+ VInit = 2; /* Initialized, and all volumes have been attached */
+ LWP_NoYieldSignal(VInitAttachVolumes);
+ VOL_UNLOCK;
+ return 0;
+}
+#endif /* !AFS_PTHREAD_ENV */
+
+#if defined(AFS_PTHREAD_ENV) && !defined(AFS_DEMAND_ATTACH_FS)
+/**
+ * Attach volumes in vice partitions
+ *
+ * @param[in] pt calling program type
+ *
+ * @return 0
+ * @note Threaded version of attach parititions.
+ *
+ * @post VInit state is 2
+ */
+int
+VInitAttachVolumes(ProgramType pt)
+{
+ assert(VInit==1);
+ if (pt == fileServer) {
struct DiskPartition64 *diskP;
-#ifdef AFS_PTHREAD_ENV
struct vinitvolumepackage_thread_t params;
struct diskpartition_queue_t * dpq;
int i, threads, parts;
assert(pthread_attr_setdetachstate(&attrs, PTHREAD_CREATE_DETACHED) == 0);
Log("VInitVolumePackage: beginning parallel fileserver startup\n");
-#ifdef AFS_DEMAND_ATTACH_FS
- Log("VInitVolumePackage: using %d threads to pre-attach volumes on %d partitions\n",
- threads, parts);
-#else /* AFS_DEMAND_ATTACH_FS */
Log("VInitVolumePackage: using %d threads to attach volumes on %d partitions\n",
threads, parts);
-#endif /* AFS_DEMAND_ATTACH_FS */
VOL_LOCK;
for (i=0; i < threads; i++) {
+ AFS_SIGSET_DECL;
+ AFS_SIGSET_CLEAR();
assert(pthread_create
(&tid, &attrs, &VInitVolumePackageThread,
¶ms) == 0);
+ AFS_SIGSET_RESTORE();
}
while(params.n_threads_complete < threads) {
/* if we're only going to run one init thread, don't bother creating
* another LWP */
Log("VInitVolumePackage: beginning single-threaded fileserver startup\n");
-#ifdef AFS_DEMAND_ATTACH_FS
- Log("VInitVolumePackage: using 1 thread to pre-attach volumes on %d partition(s)\n",
- parts);
-#else /* AFS_DEMAND_ATTACH_FS */
Log("VInitVolumePackage: using 1 thread to attach volumes on %d partition(s)\n",
parts);
-#endif /* AFS_DEMAND_ATTACH_FS */
VInitVolumePackageThread(¶ms);
}
assert(pthread_cond_destroy(¶ms.thread_done_cv) == 0);
-
-#else /* AFS_PTHREAD_ENV */
- DIR *dirp;
- struct dirent *dp;
-
- /* Attach all the volumes in this partition */
- for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
- int nAttached = 0, nUnattached = 0;
- assert(VAttachVolumesByPartition(diskP, &nAttached, &nUnattached) == 0);
- }
-#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 */
+ assert(pthread_cond_broadcast(&vol_init_attach_cond) == 0);
+ VOL_UNLOCK;
return 0;
}
-#ifdef AFS_PTHREAD_ENV
static void *
VInitVolumePackageThread(void * args) {
- int errors = 0; /* Number of errors while finding vice partitions. */
- DIR *dirp;
- struct dirent *dp;
struct DiskPartition64 *diskP;
struct vinitvolumepackage_thread_t * params;
struct diskpartition_queue_t * dpq;
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;
VOL_LOCK;
}
+done:
params->n_threads_complete++;
pthread_cond_signal(¶ms->thread_done_cv);
VOL_UNLOCK;
return NULL;
}
-#endif /* AFS_PTHREAD_ENV */
+#endif /* AFS_PTHREAD_ENV && !AFS_DEMAND_ATTACH_FS */
+
+#if defined(AFS_DEMAND_ATTACH_FS)
+/**
+ * Attach volumes in vice partitions
+ *
+ * @param[in] pt calling program type
+ *
+ * @return 0
+ * @note Threaded version of attach partitions.
+ *
+ * @post VInit state is 2
+ */
+int
+VInitAttachVolumes(ProgramType pt)
+{
+ assert(VInit==1);
+ if (pt == fileServer) {
+
+ struct DiskPartition64 *diskP;
+ struct partition_queue pq;
+ struct volume_init_queue vq;
+
+ int i, threads, parts;
+ pthread_t tid;
+ pthread_attr_t attrs;
+
+ /* create partition work queue */
+ queue_Init(&pq);
+ assert(pthread_cond_init(&(pq.cv), NULL) == 0);
+ assert(pthread_mutex_init(&(pq.mutex), NULL) == 0);
+ for (parts = 0, diskP = DiskPartitionList; diskP; diskP = diskP->next, parts++) {
+ struct diskpartition_queue_t *dp;
+ dp = (struct diskpartition_queue_t*)malloc(sizeof(struct diskpartition_queue_t));
+ assert(dp != NULL);
+ dp->diskP = diskP;
+ queue_Append(&pq, dp);
+ }
+
+ /* number of worker threads; at least one, not to exceed the number of partitions */
+ threads = MIN(parts, vol_attach_threads);
+
+ /* create volume work queue */
+ queue_Init(&vq);
+ assert(pthread_cond_init(&(vq.cv), NULL) == 0);
+ assert(pthread_mutex_init(&(vq.mutex), NULL) == 0);
+
+ assert(pthread_attr_init(&attrs) == 0);
+ assert(pthread_attr_setdetachstate(&attrs, PTHREAD_CREATE_DETACHED) == 0);
+
+ Log("VInitVolumePackage: beginning parallel fileserver startup\n");
+ Log("VInitVolumePackage: using %d threads to pre-attach volumes on %d partitions\n",
+ threads, parts);
+
+ /* create threads to scan disk partitions. */
+ for (i=0; i < threads; i++) {
+ struct vinitvolumepackage_thread_param *params;
+ AFS_SIGSET_DECL;
+
+ params = (struct vinitvolumepackage_thread_param *)malloc(sizeof(struct vinitvolumepackage_thread_param));
+ assert(params);
+ params->pq = &pq;
+ params->vq = &vq;
+ params->nthreads = threads;
+ params->thread = i+1;
+
+ AFS_SIGSET_CLEAR();
+ assert(pthread_create (&tid, &attrs, &VInitVolumePackageThread, (void*)params) == 0);
+ AFS_SIGSET_RESTORE();
+ }
+
+ VInitPreAttachVolumes(threads, &vq);
+
+ assert(pthread_attr_destroy(&attrs) == 0);
+ assert(pthread_cond_destroy(&pq.cv) == 0);
+ assert(pthread_mutex_destroy(&pq.mutex) == 0);
+ assert(pthread_cond_destroy(&vq.cv) == 0);
+ assert(pthread_mutex_destroy(&vq.mutex) == 0);
+ }
+
+ VOL_LOCK;
+ VInit = 2; /* Initialized, and all volumes have been attached */
+ assert(pthread_cond_broadcast(&vol_init_attach_cond) == 0);
+ VOL_UNLOCK;
+
+ return 0;
+}
+
+/**
+ * Volume package initialization worker thread. Scan partitions for volume
+ * header files. Gather batches of volume ids and dispatch them to
+ * the main thread to be preattached. The volume preattachement is done
+ * in the main thread to avoid global volume lock contention.
+ */
+static void *
+VInitVolumePackageThread(void *args)
+{
+ struct vinitvolumepackage_thread_param *params;
+ struct DiskPartition64 *partition;
+ struct partition_queue *pq;
+ struct volume_init_queue *vq;
+ struct volume_init_batch *vb;
+
+ assert(args);
+ params = (struct vinitvolumepackage_thread_param *)args;
+ pq = params->pq;
+ vq = params->vq;
+ assert(pq);
+ assert(vq);
+
+ vb = (struct volume_init_batch*)malloc(sizeof(struct volume_init_batch));
+ assert(vb);
+ vb->thread = params->thread;
+ vb->last = 0;
+ vb->size = 0;
+
+ Log("Scanning partitions on thread %d of %d\n", params->thread, params->nthreads);
+ while((partition = VInitNextPartition(pq))) {
+ DIR *dirp;
+ VolId vid;
+
+ Log("Partition %s: pre-attaching volumes\n", partition->name);
+ dirp = opendir(VPartitionPath(partition));
+ if (!dirp) {
+ Log("opendir on Partition %s failed, errno=%d!\n", partition->name, errno);
+ continue;
+ }
+ while ((vid = VInitNextVolumeId(dirp))) {
+ Volume *vp = (Volume*)malloc(sizeof(Volume));
+ assert(vp);
+ memset(vp, 0, sizeof(Volume));
+ vp->device = partition->device;
+ vp->partition = partition;
+ vp->hashid = vid;
+ queue_Init(&vp->vnode_list);
+ assert(pthread_cond_init(&V_attachCV(vp), NULL) == 0);
+
+ vb->batch[vb->size++] = vp;
+ if (vb->size == VINIT_BATCH_MAX_SIZE) {
+ assert(pthread_mutex_lock(&vq->mutex) == 0);
+ queue_Append(vq, vb);
+ assert(pthread_cond_broadcast(&vq->cv) == 0);
+ assert(pthread_mutex_unlock(&vq->mutex) == 0);
+
+ vb = (struct volume_init_batch*)malloc(sizeof(struct volume_init_batch));
+ assert(vb);
+ vb->thread = params->thread;
+ vb->size = 0;
+ vb->last = 0;
+ }
+ }
+ closedir(dirp);
+ }
+
+ vb->last = 1;
+ assert(pthread_mutex_lock(&vq->mutex) == 0);
+ queue_Append(vq, vb);
+ assert(pthread_cond_broadcast(&vq->cv) == 0);
+ assert(pthread_mutex_unlock(&vq->mutex) == 0);
+
+ Log("Partition scan thread %d of %d ended\n", params->thread, params->nthreads);
+ free(params);
+ return NULL;
+}
+
+/**
+ * Read next element from the pre-populated partition list.
+ */
+static struct DiskPartition64*
+VInitNextPartition(struct partition_queue *pq)
+{
+ struct DiskPartition64 *partition;
+ struct diskpartition_queue_t *dp; /* queue element */
+
+ if (vinit_attach_abort) {
+ Log("Aborting volume preattach thread.\n");
+ return NULL;
+ }
+
+ /* get next partition to scan */
+ assert(pthread_mutex_lock(&pq->mutex) == 0);
+ if (queue_IsEmpty(pq)) {
+ assert(pthread_mutex_unlock(&pq->mutex) == 0);
+ return NULL;
+ }
+ dp = queue_First(pq, diskpartition_queue_t);
+ queue_Remove(dp);
+ assert(pthread_mutex_unlock(&pq->mutex) == 0);
+
+ assert(dp);
+ assert(dp->diskP);
+
+ partition = dp->diskP;
+ free(dp);
+ return partition;
+}
+
+/**
+ * Find next volume id on the partition.
+ */
+static VolId
+VInitNextVolumeId(DIR *dirp)
+{
+ struct dirent *d;
+ VolId vid = 0;
+ char *ext;
+
+ while((d = readdir(dirp))) {
+ if (vinit_attach_abort) {
+ Log("Aborting volume preattach thread.\n");
+ break;
+ }
+ ext = strrchr(d->d_name, '.');
+ if (d->d_name[0] == 'V' && ext && strcmp(ext, VHDREXT) == 0) {
+ vid = VolumeNumber(d->d_name);
+ if (vid) {
+ break;
+ }
+ Log("Warning: bogus volume header file: %s\n", d->d_name);
+ }
+ }
+ return vid;
+}
+
+/**
+ * Preattach volumes in batches to avoid lock contention.
+ */
+static int
+VInitPreAttachVolumes(int nthreads, struct volume_init_queue *vq)
+{
+ struct volume_init_batch *vb;
+ int i;
+ while (nthreads) {
+ /* dequeue next volume */
+ pthread_mutex_lock(&vq->mutex);
+ if (queue_IsEmpty(vq)) {
+ pthread_cond_wait(&vq->cv, &vq->mutex);
+ }
+ vb = queue_First(vq, volume_init_batch);
+ queue_Remove(vb);
+ pthread_mutex_unlock(&vq->mutex);
+
+ if (vb->size) {
+ VOL_LOCK;
+ for (i = 0; i<vb->size; i++) {
+ Volume *vp;
+ Volume *dup;
+ Error ec = 0;
+
+ vp = vb->batch[i];
+ dup = VLookupVolume_r(&ec, vp->hashid, NULL);
+ if (ec) {
+ Log("Error looking up volume, code=%d\n", ec);
+ }
+ else if (dup) {
+ Log("Warning: Duplicate volume id %d detected.\n", vp->hashid);
+ }
+ else {
+ /* put pre-attached volume onto the hash table
+ * and bring it up to the pre-attached state */
+ AddVolumeToHashTable(vp, vp->hashid);
+ AddVolumeToVByPList_r(vp);
+ VLRU_Init_Node_r(vp);
+ VChangeState_r(vp, VOL_STATE_PREATTACHED);
+ }
+ }
+ VOL_UNLOCK;
+ }
+
+ if (vb->last) {
+ nthreads--;
+ }
+ free(vb);
+ }
+ return 0;
+}
+#endif /* AFS_DEMAND_ATTACH_FS */
+
+#if !defined(AFS_DEMAND_ATTACH_FS)
/*
* attach all volumes on a given disk partition
*/
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;
-#ifdef AFS_DEMAND_ATTACH_FS
- vp = VPreAttachVolumeByName(&error, diskP->name, dp->d_name);
-#else /* AFS_DEMAND_ATTACH_FS */
vp = VAttachVolumeByName(&error, diskP->name, dp->d_name,
V_VOLUPD);
-#endif /* AFS_DEMAND_ATTACH_FS */
(*(vp ? nAttached : nUnattached))++;
if (error == VOFFLINE)
Log("Volume %d stays offline (/vice/offline/%s exists)\n", VolumeNumber(dp->d_name), dp->d_name);
diskP->name, VolumeNumber(dp->d_name),
dp->d_name);
}
-#if !defined(AFS_DEMAND_ATTACH_FS)
if (vp) {
VPutVolume(vp);
}
-#endif /* AFS_DEMAND_ATTACH_FS */
}
}
Log("Partition %s: attached %d volumes; %d volumes not attached\n", diskP->name, *nAttached, *nUnattached);
+done:
closedir(dirp);
return ret;
}
-
+#endif /* !AFS_DEMAND_ATTACH_FS */
/***************************************************/
/* Shutdown routines */
* shutdown all remaining volumes
*/
+#ifdef AFS_DEMAND_ATTACH_FS
+
void
VShutdown_r(void)
{
int i;
- register Volume *vp, *np;
- register afs_int32 code;
-#ifdef AFS_DEMAND_ATTACH_FS
struct DiskPartition64 * diskP;
struct diskpartition_queue_t * dpq;
vshutdown_thread_t params;
memset(¶ms, 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++);
}
Log("VShutdown: complete.\n");
+}
+
#else /* AFS_DEMAND_ATTACH_FS */
+
+void
+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 */
}
}
Log("VShutdown: complete.\n");
-#endif /* AFS_DEMAND_ATTACH_FS */
}
+#endif /* AFS_DEMAND_ATTACH_FS */
+
void
VShutdown(void)
{
+ assert(VInit>0);
VOL_LOCK;
VShutdown_r();
VOL_UNLOCK;
}
-#ifdef AFS_DEMAND_ATTACH_FS
-/*
- * demand attach fs
- * shutdown control thread
+/**
+ * stop new activity (e.g. SALVSYNC) from occurring
+ *
+ * Use this to make the volume package less busy; for example, during
+ * shutdown. This doesn't actually shutdown/detach anything in the
+ * volume package, but prevents certain processes from ocurring. For
+ * example, preventing new SALVSYNC communication in DAFS. In theory, we
+ * could also use this to prevent new volume attachment, or prevent
+ * other programs from checking out volumes, etc.
*/
-static void
-ShutdownController(vshutdown_thread_t * params)
+void
+VSetTranquil(void)
+{
+#ifdef AFS_DEMAND_ATTACH_FS
+ /* make sure we don't try to contact the salvageserver, since it may
+ * not be around anymore */
+ vol_disallow_salvsync = 1;
+#endif
+}
+
+#ifdef AFS_DEMAND_ATTACH_FS
+/*
+ * demand attach fs
+ * shutdown control thread
+ */
+static void
+ShutdownController(vshutdown_thread_t * params)
{
/* XXX debug */
struct DiskPartition64 * diskP;
if (thr_left) {
/* try to assign any leftover threads to partitions that
* had volume lengths closer to needing thread_target+1 */
- int max_residue, max_id;
+ int max_residue, max_id = 0;
/* compute the residues */
for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
static void *
VShutdownThread(void * args)
{
- struct rx_queue *qp;
- Volume * vp;
vshutdown_thread_t * params;
- int part, code, found, pass, schedule_version_save, count;
+ int found, pass, schedule_version_save, count;
struct DiskPartition64 *diskP;
struct diskpartition_queue_t * dpq;
Device id;
int
VShutdownByPartition_r(struct DiskPartition64 * dp)
{
- int pass, retVal;
+ int pass;
int pass_stats[4];
int total;
Log("VShutdownByPartition: shut down %d volumes on %s (pass[0]=%d, pass[1]=%d, pass[2]=%d, pass[3]=%d)\n",
total, VPartitionPath(dp), pass_stats[0], pass_stats[1], pass_stats[2], pass_stats[3]);
- return retVal;
+ return 0;
}
/* internal shutdown functionality
switch(V_attachState(vp)) {
case VOL_STATE_SALVAGING:
- /* make sure salvager knows we don't want
- * the volume back */
- VCancelSalvage_r(vp, SALVSYNC_SHUTDOWN);
+ /* Leave salvaging volumes alone. Any in-progress salvages will
+ * continue working after viced shuts down. This is intentional.
+ */
+
case VOL_STATE_PREATTACHED:
case VOL_STATE_ERROR:
VChangeState_r(vp, VOL_STATE_UNATTACHED);
VOffline_r(vp, "File server was shut down");
}
break;
+ default:
+ break;
}
VCancelReservation_r(vp);
VolumeHeaderToDisk(VolumeDiskHeader_t * dh, VolumeHeader_t * h)
{
- memset((char *)dh, 0, sizeof(VolumeDiskHeader_t));
+ memset(dh, 0, sizeof(VolumeDiskHeader_t));
dh->stamp = h->stamp;
dh->id = h->id;
dh->parent = h->parent;
void
DiskToVolumeHeader(VolumeHeader_t * h, VolumeDiskHeader_t * dh)
{
- memset((char *)h, 0, sizeof(VolumeHeader_t));
+ memset(h, 0, sizeof(VolumeHeader_t));
h->stamp = dh->stamp;
h->id = dh->id;
h->parent = dh->parent;
Volume *
VAttachVolumeByName_r(Error * ec, char *partition, char *name, int mode)
{
- register Volume *vp = NULL, *svp = NULL;
- int fd, n;
- struct afs_stat status;
- struct VolumeDiskHeader diskHeader;
- struct VolumeHeader iheader;
+ register Volume *vp = NULL;
struct DiskPartition64 *partp;
char path[64];
int isbusy = 0;
VolId volumeId;
#ifdef AFS_DEMAND_ATTACH_FS
VolumeStats stats_save;
+ Volume *svp = NULL;
#endif /* AFS_DEMAND_ATTACH_FS */
*ec = 0;
goto done;
}
- if (programType == volumeUtility) {
+ if (VRequiresPartLock()) {
assert(VInit == 3);
VLockPartition_r(partition);
} else if (programType == fileServer) {
strcat(path, "/");
strcat(path, name);
- if ((fd = afs_open(path, O_RDONLY)) == -1 || afs_fstat(fd, &status) == -1) {
- Log("VAttachVolume: Failed to open %s (errno %d)\n", path, errno);
- if (fd > -1)
- close(fd);
- *ec = VNOVOL;
- VOL_LOCK;
- goto done;
- }
- n = read(fd, &diskHeader, sizeof(diskHeader));
- close(fd);
- if (n != sizeof(diskHeader)
- || diskHeader.stamp.magic != VOLUMEHEADERMAGIC) {
- Log("VAttachVolume: Error reading volume header %s\n", path);
- *ec = VSALVAGE;
- VOL_LOCK;
- goto done;
- }
- if (diskHeader.stamp.version != VOLUMEHEADERVERSION) {
- Log("VAttachVolume: Volume %s, version number is incorrect; volume needs salvaged\n", path);
- *ec = VSALVAGE;
- VOL_LOCK;
- goto done;
- }
-
- DiskToVolumeHeader(&iheader, &diskHeader);
-#ifdef FSSYNC_BUILD_CLIENT
- if (programType == volumeUtility && mode != V_SECRETLY && mode != V_PEEK) {
- VOL_LOCK;
- if (FSYNC_VolOp(iheader.id, partition, FSYNC_VOL_NEEDVOLUME, mode, NULL)
- != SYNC_OK) {
- Log("VAttachVolume: attach of volume %u apparently denied by file server\n", iheader.id);
- *ec = VNOVOL; /* XXXX */
- goto done;
- }
- VOL_UNLOCK;
- }
-#endif
if (!vp) {
vp = (Volume *) calloc(1, sizeof(Volume));
assert(vp != NULL);
+ vp->hashid = volumeId;
vp->device = partp->device;
vp->partition = partp;
queue_Init(&vp->vnode_list);
/* attach2 is entered without any locks, and returns
* with vol_glock_mutex held */
- vp = attach2(ec, volumeId, path, &iheader, partp, vp, isbusy, mode);
+ vp = attach2(ec, volumeId, path, partp, vp, isbusy, mode);
- if (programType == volumeUtility && vp) {
+ if (VCanUseFSSYNC() && vp) {
+#ifdef AFS_DEMAND_ATTACH_FS
if ((mode == V_VOLUPD) || (VolumeWriteable(vp) && (mode == V_CLONE))) {
/* mark volume header as in use so that volser crashes lead to a
* salvage attempt */
VUpdateVolume_r(ec, vp, 0);
}
-#ifdef AFS_DEMAND_ATTACH_FS
/* for dafs, we should tell the fileserver, except for V_PEEK
* where we know it is not necessary */
if (mode == V_PEEK) {
* 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);
+
+#ifdef AFS_DEMAND_ATTACH_FS
+ /* If we couldn't attach but we scheduled a salvage, we already
+ * notified the fileserver; don't online it now */
+ if (*ec != VSALVAGING)
+#endif /* AFS_DEMAND_ATTACH_FS */
+ FSYNC_VolOp(volumeId, partition, FSYNC_VOL_ON, 0, NULL);
} else
#endif
if (programType == fileServer && vp) {
goto done;
}
#endif
- V_needsCallback(vp) = 0;
-#ifdef notdef
- if (VInit >= 2 && V_BreakVolumeCallbacks) {
- Log("VAttachVolume: Volume %u was changed externally; breaking callbacks\n", V_id(vp));
- (*V_BreakVolumeCallbacks) (V_id(vp));
- }
-#endif
VUpdateVolume_r(ec, vp, 0);
if (*ec) {
Log("VAttachVolume: Error updating volume\n");
}
done:
- if (programType == volumeUtility) {
+ if (VRequiresPartLock()) {
VUnlockPartition_r(partition);
}
if (*ec) {
VAttachVolumeByVp_r(Error * ec, Volume * vp, int mode)
{
char name[VMAXPATHLEN];
- int fd, n, reserve = 0;
- struct afs_stat status;
- struct VolumeDiskHeader diskHeader;
- struct VolumeHeader iheader;
+ int reserve = 0;
struct DiskPartition64 *partp;
char path[64];
int isbusy = 0;
VolId volumeId;
- Volume * nvp;
+ Volume * nvp = NULL;
VolumeStats stats_save;
*ec = 0;
*ec = 0;
-
- /* compute path to disk header,
- * read in header,
- * and verify magic and version stamps */
+ /* compute path to disk header */
strcpy(path, VPartitionPath(partp));
VOL_UNLOCK;
strcat(path, "/");
strcat(path, name);
- if ((fd = afs_open(path, O_RDONLY)) == -1 || afs_fstat(fd, &status) == -1) {
- Log("VAttachVolume: Failed to open %s (errno %d)\n", path, errno);
- if (fd > -1)
- close(fd);
- *ec = VNOVOL;
- VOL_LOCK;
- goto done;
- }
- n = read(fd, &diskHeader, sizeof(diskHeader));
- close(fd);
- if (n != sizeof(diskHeader)
- || diskHeader.stamp.magic != VOLUMEHEADERMAGIC) {
- Log("VAttachVolume: Error reading volume header %s\n", path);
- *ec = VSALVAGE;
- VOL_LOCK;
- goto done;
- }
- if (diskHeader.stamp.version != VOLUMEHEADERVERSION) {
- Log("VAttachVolume: Volume %s, version number is incorrect; volume needs salvaged\n", path);
- *ec = VSALVAGE;
- VOL_LOCK;
- goto done;
- }
-
- /* convert on-disk header format to in-memory header format */
- DiskToVolumeHeader(&iheader, &diskHeader);
/* do volume attach
*
* NOTE: attach2 is entered without any locks, and returns
* with vol_glock_mutex held */
- vp = attach2(ec, volumeId, path, &iheader, partp, vp, isbusy, mode);
+ vp = attach2(ec, volumeId, path, partp, vp, isbusy, mode);
/*
* the event that an error was encountered, or
goto done;
}
- V_needsCallback(vp) = 0;
VUpdateVolume_r(ec, vp, 0);
if (*ec) {
Log("VAttachVolume: Error updating volume %u\n", vp->hashid);
return vp;
}
}
+
+/**
+ * lock a volume on disk (non-blocking).
+ *
+ * @param[in] vp The volume to lock
+ * @param[in] locktype READ_LOCK or WRITE_LOCK
+ *
+ * @return operation status
+ * @retval 0 success, lock was obtained
+ * @retval EBUSY a conflicting lock was held by another process
+ * @retval EIO error acquiring lock
+ *
+ * @pre If we're in the fileserver, vp is in an exclusive state
+ *
+ * @pre vp is not already locked
+ */
+static int
+VLockVolumeNB(Volume *vp, int locktype)
+{
+ int code;
+
+ assert(programType != fileServer || VIsExclusiveState(V_attachState(vp)));
+ assert(!(V_attachFlags(vp) & VOL_LOCKED));
+
+ code = VLockVolumeByIdNB(vp->hashid, vp->partition, locktype);
+ if (code == 0) {
+ V_attachFlags(vp) |= VOL_LOCKED;
+ }
+
+ return code;
+}
+
+/**
+ * unlock a volume on disk that was locked with VLockVolumeNB.
+ *
+ * @param[in] vp volume to unlock
+ *
+ * @pre If we're in the fileserver, vp is in an exclusive state
+ *
+ * @pre vp has already been locked
+ */
+static void
+VUnlockVolume(Volume *vp)
+{
+ assert(programType != fileServer || VIsExclusiveState(V_attachState(vp)));
+ assert((V_attachFlags(vp) & VOL_LOCKED));
+
+ VUnlockVolumeById(vp->hashid, vp->partition);
+
+ V_attachFlags(vp) &= ~VOL_LOCKED;
+}
#endif /* AFS_DEMAND_ATTACH_FS */
-/*
- * called without any locks held
- * returns with vol_glock_mutex held
+/**
+ * read in a vol header, possibly lock the vol header, and possibly check out
+ * the vol header from the fileserver, as part of volume attachment.
+ *
+ * @param[out] ec error code
+ * @param[in] vp volume pointer object
+ * @param[in] partp disk partition object of the attaching partition
+ * @param[in] mode attachment mode such as V_VOLUPD, V_DUMP, etc (see
+ * volume.h)
+ * @param[in] peek 1 to just try to read in the volume header and make sure
+ * we don't try to lock the vol, or check it out from
+ * FSSYNC or anything like that; 0 otherwise, for 'normal'
+ * operation
+ *
+ * @note As part of DAFS volume attachment, the volume header may be either
+ * read- or write-locked to ensure mutual exclusion of certain volume
+ * operations. In some cases in order to determine whether we need to
+ * read- or write-lock the header, we need to read in the header to see
+ * if the volume is RW or not. So, if we read in the header under a
+ * read-lock and determine that we actually need a write-lock on the
+ * volume header, this function will drop the read lock, acquire a write
+ * lock, and read the header in again.
*/
-private Volume *
-attach2(Error * ec, VolId volumeId, char *path, register struct VolumeHeader * header,
- struct DiskPartition64 * partp, register Volume * vp, int isbusy, int mode)
-{
- vp->specialStatus = (byte) (isbusy ? VBUSY : 0);
- IH_INIT(vp->vnodeIndex[vLarge].handle, partp->device, header->parent,
- header->largeVnodeIndex);
- IH_INIT(vp->vnodeIndex[vSmall].handle, partp->device, header->parent,
- header->smallVnodeIndex);
- IH_INIT(vp->diskDataHandle, partp->device, header->parent,
- header->volumeInfo);
- IH_INIT(vp->linkHandle, partp->device, header->parent, header->linkTable);
- vp->shuttingDown = 0;
- vp->goingOffline = 0;
- vp->nUsers = 1;
+static void
+attach_volume_header(Error *ec, Volume *vp, struct DiskPartition64 *partp,
+ int mode, int peek)
+{
+ struct VolumeDiskHeader diskHeader;
+ struct VolumeHeader header;
+ int code;
+ int first_try = 1;
+ int lock_tries = 0, checkout_tries = 0;
+ int retry;
+ VolumeId volid = vp->hashid;
+#ifdef FSSYNC_BUILD_CLIENT
+ int checkout, done_checkout = 0;
+#endif /* FSSYNC_BUILD_CLIENT */
#ifdef AFS_DEMAND_ATTACH_FS
- vp->stats.last_attach = FT_ApproxTime();
- vp->stats.attaches++;
+ int locktype = 0, use_locktype = -1;
+#endif /* AFS_DEMAND_ATTACH_FS */
+
+ retry:
+ retry = 0;
+ *ec = 0;
+
+ if (lock_tries > VOL_MAX_CHECKOUT_RETRIES) {
+ Log("VAttachVolume: retried too many times trying to lock header for "
+ "vol %lu part %s; giving up\n", afs_printable_uint32_lu(volid),
+ VPartitionPath(partp));
+ *ec = VNOVOL;
+ goto done;
+ }
+ if (checkout_tries > VOL_MAX_CHECKOUT_RETRIES) {
+ Log("VAttachVolume: retried too many times trying to checkout "
+ "vol %lu part %s; giving up\n", afs_printable_uint32_lu(volid),
+ VPartitionPath(partp));
+ *ec = VNOVOL;
+ goto done;
+ }
+
+ if (VReadVolumeDiskHeader(volid, partp, NULL)) {
+ /* short-circuit the 'volume does not exist' case */
+ *ec = VNOVOL;
+ goto done;
+ }
+
+#ifdef FSSYNC_BUILD_CLIENT
+ checkout = !done_checkout;
+ done_checkout = 1;
+ if (!peek && checkout && VMustCheckoutVolume(mode)) {
+ SYNC_response res;
+ memset(&res, 0, sizeof(res));
+
+ if (FSYNC_VolOp(volid, VPartitionPath(partp), FSYNC_VOL_NEEDVOLUME, mode, &res)
+ != SYNC_OK) {
+
+ if (res.hdr.reason == FSYNC_SALVAGE) {
+ Log("VAttachVolume: file server says volume %lu is salvaging\n",
+ afs_printable_uint32_lu(volid));
+ *ec = VSALVAGING;
+ } else {
+ Log("VAttachVolume: attach of volume %lu apparently denied by file server\n",
+ afs_printable_uint32_lu(volid));
+ *ec = VNOVOL; /* XXXX */
+ }
+ goto done;
+ }
+ }
#endif
- VOL_LOCK;
- IncUInt64(&VStats.attaches);
- vp->cacheCheck = ++VolumeCacheCheck;
- /* just in case this ever rolls over */
- if (!vp->cacheCheck)
- vp->cacheCheck = ++VolumeCacheCheck;
- GetVolumeHeader(vp);
- VOL_UNLOCK;
+#ifdef AFS_DEMAND_ATTACH_FS
+ if (use_locktype < 0) {
+ /* don't know whether vol is RO or RW; assume it's RO and we can retry
+ * if it turns out to be RW */
+ locktype = VVolLockType(mode, 0);
+
+ } else {
+ /* a previous try says we should use use_locktype to lock the volume,
+ * so use that */
+ locktype = use_locktype;
+ }
+
+ if (!peek && locktype) {
+ code = VLockVolumeNB(vp, locktype);
+ if (code) {
+ if (code == EBUSY) {
+ Log("VAttachVolume: another program has vol %lu locked\n",
+ afs_printable_uint32_lu(volid));
+ } else {
+ Log("VAttachVolume: error %d trying to lock vol %lu\n",
+ code, afs_printable_uint32_lu(volid));
+ }
+
+ *ec = VNOVOL;
+ goto done;
+ }
+ }
+#endif /* AFS_DEMAND_ATTACH_FS */
+
+ code = VReadVolumeDiskHeader(volid, partp, &diskHeader);
+ if (code) {
+ if (code == EIO) {
+ *ec = VSALVAGE;
+ } else {
+ *ec = VNOVOL;
+ }
+ goto done;
+ }
+
+ DiskToVolumeHeader(&header, &diskHeader);
+
+ IH_INIT(vp->vnodeIndex[vLarge].handle, partp->device, header.parent,
+ header.largeVnodeIndex);
+ IH_INIT(vp->vnodeIndex[vSmall].handle, partp->device, header.parent,
+ header.smallVnodeIndex);
+ IH_INIT(vp->diskDataHandle, partp->device, header.parent,
+ header.volumeInfo);
+ IH_INIT(vp->linkHandle, partp->device, header.parent, header.linkTable);
+
+ if (first_try) {
+ /* only need to do this once */
+ VOL_LOCK;
+ GetVolumeHeader(vp);
+ VOL_UNLOCK;
+ }
#if defined(AFS_DEMAND_ATTACH_FS) && defined(FSSYNC_BUILD_CLIENT)
/* demand attach changes the V_PEEK mechanism
* to demand attach fileservers. However, I'm trying
* to limit the number of common code changes)
*/
- if (programType != fileServer && mode == V_PEEK) {
+ if (VCanUseFSSYNC() && (mode == V_PEEK || peek)) {
SYNC_response res;
res.payload.len = sizeof(VolumeDiskData);
res.payload.buf = &vp->header->diskstuff;
- if (FSYNC_VolOp(volumeId,
+ if (FSYNC_VolOp(vp->hashid,
partp->name,
FSYNC_VOL_QUERY_HDR,
FSYNC_WHATEVER,
#endif /* AFS_DEMAND_ATTACH_FS */
if (*ec) {
- Log("VAttachVolume: Error reading diskDataHandle vol header %s; error=%u\n", path, *ec);
+ Log("VAttachVolume: Error reading diskDataHandle header for vol %lu; "
+ "error=%u\n", afs_printable_uint32_lu(volid), *ec);
+ goto done;
}
+#ifdef AFS_DEMAND_ATTACH_FS
+# ifdef FSSYNC_BUILD_CLIENT
disk_header_loaded:
+# endif /* FSSYNC_BUILD_CLIENT */
+
+ /* if the lock type we actually used to lock the volume is different than
+ * the lock type we should have used, retry with the lock type we should
+ * use */
+ use_locktype = VVolLockType(mode, VolumeWriteable(vp));
+ if (locktype != use_locktype) {
+ retry = 1;
+ lock_tries++;
+ }
+#endif /* AFS_DEMAND_ATTACH_FS */
+
+ *ec = 0;
+
+ done:
+#if defined(AFS_DEMAND_ATTACH_FS) && defined(FSSYNC_BUILD_CLIENT)
+ if (!peek && *ec == 0 && retry == 0 && VMustCheckoutVolume(mode)) {
+
+ code = FSYNC_VerifyCheckout(volid, VPartitionPath(partp), FSYNC_VOL_NEEDVOLUME, mode);
+
+ if (code == SYNC_DENIED) {
+ /* must retry checkout; fileserver no longer thinks we have
+ * the volume */
+ retry = 1;
+ checkout_tries++;
+ done_checkout = 0;
+
+ } else if (code != SYNC_OK) {
+ *ec = VNOVOL;
+ }
+ }
+#endif /* AFS_DEMAND_ATTACH_FS && FSSYNC_BUILD_CLIENT */
+
+ if (*ec || retry) {
+ /* either we are going to be called again for a second pass, or we
+ * encountered an error; clean up in either case */
#ifdef AFS_DEMAND_ATTACH_FS
- if (!*ec) {
+ if ((V_attachFlags(vp) & VOL_LOCKED)) {
+ VUnlockVolume(vp);
+ }
+#endif /* AFS_DEMAND_ATTACH_FS */
+ if (vp->linkHandle) {
+ IH_RELEASE(vp->vnodeIndex[vLarge].handle);
+ IH_RELEASE(vp->vnodeIndex[vSmall].handle);
+ IH_RELEASE(vp->diskDataHandle);
+ IH_RELEASE(vp->linkHandle);
+ }
+ }
- /* check for pending volume operations */
- if (vp->pending_vol_op) {
- /* see if the pending volume op requires exclusive access */
- if (!VVolOpLeaveOnline_r(vp, vp->pending_vol_op)) {
- /* mark the volume down */
- *ec = VOFFLINE;
- VChangeState_r(vp, VOL_STATE_UNATTACHED);
- if (V_offlineMessage(vp)[0] == '\0')
- strlcpy(V_offlineMessage(vp),
- "A volume utility is running.",
- sizeof(V_offlineMessage(vp)));
- V_offlineMessage(vp)[sizeof(V_offlineMessage(vp)) - 1] = '\0';
-
- /* check to see if we should set the specialStatus flag */
- if (VVolOpSetVBusy_r(vp, vp->pending_vol_op)) {
- vp->specialStatus = VBUSY;
+ if (*ec) {
+ return;
+ }
+ if (retry) {
+ first_try = 0;
+ goto retry;
+ }
+}
+
+#ifdef AFS_DEMAND_ATTACH_FS
+static void
+attach_check_vop(Error *ec, VolumeId volid, struct DiskPartition64 *partp,
+ Volume *vp)
+{
+ *ec = 0;
+
+ if (vp->pending_vol_op) {
+
+ VOL_LOCK;
+
+ if (vp->pending_vol_op->vol_op_state == FSSYNC_VolOpRunningUnknown) {
+ int code;
+ code = VVolOpLeaveOnlineNoHeader_r(vp, vp->pending_vol_op);
+ if (code == 1) {
+ vp->pending_vol_op->vol_op_state = FSSYNC_VolOpRunningOnline;
+ } else if (code == 0) {
+ vp->pending_vol_op->vol_op_state = FSSYNC_VolOpRunningOffline;
+
+ } else {
+ /* we need the vol header to determine if the volume can be
+ * left online for the vop, so... get the header */
+
+ VOL_UNLOCK;
+
+ /* attach header with peek=1 to avoid checking out the volume
+ * or locking it; we just want the header info, we're not
+ * messing with the volume itself at all */
+ attach_volume_header(ec, vp, partp, V_PEEK, 1);
+ if (*ec) {
+ return;
}
+
+ VOL_LOCK;
+
+ if (VVolOpLeaveOnline_r(vp, vp->pending_vol_op)) {
+ vp->pending_vol_op->vol_op_state = FSSYNC_VolOpRunningOnline;
+ } else {
+ vp->pending_vol_op->vol_op_state = FSSYNC_VolOpRunningOffline;
+ }
+
+ /* make sure we grab a new vol header and re-open stuff on
+ * actual attachment; we can't keep the data we grabbed, since
+ * it was not done under a lock and thus not safe */
+ FreeVolumeHeader(vp);
+ VReleaseVolumeHandles_r(vp);
}
}
+ /* see if the pending volume op requires exclusive access */
+ switch (vp->pending_vol_op->vol_op_state) {
+ case FSSYNC_VolOpPending:
+ /* this should never happen */
+ assert(vp->pending_vol_op->vol_op_state != FSSYNC_VolOpPending);
+ break;
+
+ case FSSYNC_VolOpRunningUnknown:
+ /* this should never happen; we resolved 'unknown' above */
+ assert(vp->pending_vol_op->vol_op_state != FSSYNC_VolOpRunningUnknown);
+ break;
+
+ case FSSYNC_VolOpRunningOffline:
+ /* mark the volume down */
+ *ec = VOFFLINE;
+ VChangeState_r(vp, VOL_STATE_UNATTACHED);
+
+ /* do not set V_offlineMessage here; we don't have ownership of
+ * the volume (and probably do not have the header loaded), so we
+ * can't alter the disk header */
+
+ /* check to see if we should set the specialStatus flag */
+ if (VVolOpSetVBusy_r(vp, vp->pending_vol_op)) {
+ vp->specialStatus = VBUSY;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ VOL_UNLOCK;
+ }
+}
+#endif /* AFS_DEMAND_ATTACH_FS */
+
+/**
+ * volume attachment helper function.
+ *
+ * @param[out] ec error code
+ * @param[in] volumeId volume ID of the attaching volume
+ * @param[in] path full path to the volume header .vol file
+ * @param[in] partp disk partition object for the attaching partition
+ * @param[in] vp volume object; vp->hashid, vp->device, vp->partition,
+ * vp->vnode_list, and V_attachCV (for DAFS) should already
+ * be initialized
+ * @param[in] isbusy 1 if vp->specialStatus should be set to VBUSY; that is,
+ * if there is a volume operation running for this volume
+ * that should set the volume to VBUSY during its run. 0
+ * otherwise. (see VVolOpSetVBusy_r)
+ * @param[in] mode attachment mode such as V_VOLUPD, V_DUMP, etc (see
+ * volume.h)
+ *
+ * @return pointer to the semi-attached volume pointer
+ * @retval NULL an error occurred (check value of *ec)
+ * @retval vp volume successfully attaching
+ *
+ * @pre no locks held
+ *
+ * @post VOL_LOCK held
+ */
+static Volume *
+attach2(Error * ec, VolId volumeId, char *path, struct DiskPartition64 *partp,
+ Volume * vp, int isbusy, int mode)
+{
+ /* have we read in the header successfully? */
+ int read_header = 0;
+
+ /* should we FreeVolume(vp) instead of VCheckFree(vp) in the error
+ * cleanup? */
+ int forcefree = 0;
+
+#ifdef AFS_DEMAND_ATTACH_FS
+ /* in the case of an error, to what state should the volume be
+ * transitioned? */
+ VolState error_state = VOL_STATE_ERROR;
+#endif /* AFS_DEMAND_ATTACH_FS */
+
+ *ec = 0;
+
+ vp->vnodeIndex[vLarge].handle = NULL;
+ vp->vnodeIndex[vSmall].handle = NULL;
+ vp->diskDataHandle = NULL;
+ vp->linkHandle = NULL;
+
+#ifdef AFS_DEMAND_ATTACH_FS
+ attach_check_vop(ec, volumeId, partp, vp);
+ if (!*ec) {
+ attach_volume_header(ec, vp, partp, mode, 0);
+ }
+#else
+ attach_volume_header(ec, vp, partp, mode, 0);
+#endif /* !AFS_DEMAND_ATTACH_FS */
+
+ if (*ec == VNOVOL) {
+ /* if the volume doesn't exist, skip straight to 'error' so we don't
+ * request a salvage */
+ goto error;
+ }
+
+ if (!*ec) {
+ read_header = 1;
+
+ vp->specialStatus = (byte) (isbusy ? VBUSY : 0);
+ vp->shuttingDown = 0;
+ vp->goingOffline = 0;
+ vp->nUsers = 1;
+#ifdef AFS_DEMAND_ATTACH_FS
+ vp->stats.last_attach = FT_ApproxTime();
+ vp->stats.attaches++;
+#endif
+
+ VOL_LOCK;
+ IncUInt64(&VStats.attaches);
+ vp->cacheCheck = ++VolumeCacheCheck;
+ /* just in case this ever rolls over */
+ if (!vp->cacheCheck)
+ vp->cacheCheck = ++VolumeCacheCheck;
+ VOL_UNLOCK;
+#ifdef AFS_DEMAND_ATTACH_FS
V_attachFlags(vp) |= VOL_HDR_LOADED;
vp->stats.last_hdr_load = vp->stats.last_attach;
- }
#endif /* AFS_DEMAND_ATTACH_FS */
+ }
if (!*ec) {
struct IndexFileHeader iHead;
* area and mark it as initialized.
*/
if (!(V_stat_initialized(vp))) {
- memset((char *)(V_stat_area(vp)), 0, VOL_STATS_BYTES);
+ memset((V_stat_area(vp)), 0, VOL_STATS_BYTES);
V_stat_initialized(vp) = 1;
}
#endif /* OPENAFS_VOL_STATS */
#if defined(AFS_DEMAND_ATTACH_FS)
if (*ec && ((*ec != VOFFLINE) || (V_attachState(vp) != VOL_STATE_UNATTACHED))) {
VOL_LOCK;
- if (programType == fileServer) {
- VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, VOL_SALVAGE_INVALIDATE_HEADER);
- vp->nUsers = 0;
- } else {
+ if (!VCanScheduleSalvage()) {
Log("VAttachVolume: Error attaching volume %s; volume needs salvage; error=%u\n", path, *ec);
- FreeVolume(vp);
- *ec = VSALVAGE;
}
- return NULL;
+ VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, VOL_SALVAGE_INVALIDATE_HEADER);
+ vp->nUsers = 0;
+
+ goto error;
} else if (*ec) {
/* volume operation in progress */
VOL_LOCK;
- return NULL;
+ goto error;
}
#else /* AFS_DEMAND_ATTACH_FS */
if (*ec) {
Log("VAttachVolume: Error attaching volume %s; volume needs salvage; error=%u\n", path, *ec);
VOL_LOCK;
- FreeVolume(vp);
- return NULL;
+ goto error;
}
#endif /* AFS_DEMAND_ATTACH_FS */
vp->specialStatus = 0;
VOL_LOCK;
#if defined(AFS_DEMAND_ATTACH_FS)
- if (programType == fileServer) {
- VRequestSalvage_r(ec, vp, SALVSYNC_NEEDED, VOL_SALVAGE_INVALIDATE_HEADER);
- vp->nUsers = 0;
- } else {
+ if (!VCanScheduleSalvage()) {
Log("VAttachVolume: volume salvage flag is ON for %s; volume needs salvage\n", path);
- FreeVolume(vp);
- *ec = VSALVAGE;
}
+ VRequestSalvage_r(ec, vp, SALVSYNC_NEEDED, VOL_SALVAGE_INVALIDATE_HEADER);
+ vp->nUsers = 0;
+
#else /* AFS_DEMAND_ATTACH_FS */
- FreeVolume(vp);
*ec = VSALVAGE;
#endif /* AFS_DEMAND_ATTACH_FS */
- return NULL;
+
+ goto error;
}
VOL_LOCK;
- if (programType == fileServer) {
-#ifndef FAST_RESTART
- if (V_inUse(vp) && VolumeWriteable(vp)) {
- if (!V_needsSalvaged(vp)) {
- V_needsSalvaged(vp) = 1;
- VUpdateVolume_r(ec, vp, 0);
- }
+ vp->nextVnodeUnique = V_uniquifier(vp);
+
+ if (VShouldCheckInUse(mode) && V_inUse(vp) && VolumeWriteable(vp)) {
+ if (!V_needsSalvaged(vp)) {
+ V_needsSalvaged(vp) = 1;
+ VUpdateVolume_r(ec, vp, 0);
+ }
#if defined(AFS_DEMAND_ATTACH_FS)
- VRequestSalvage_r(ec, vp, SALVSYNC_NEEDED, VOL_SALVAGE_INVALIDATE_HEADER);
- vp->nUsers = 0;
-#else /* AFS_DEMAND_ATTACH_FS */
+ if (!VCanScheduleSalvage()) {
Log("VAttachVolume: volume %s needs to be salvaged; not attached.\n", path);
- FreeVolume(vp);
- *ec = VSALVAGE;
-#endif /* AFS_DEMAND_ATTACH_FS */
- return NULL;
}
-#endif /* FAST_RESTART */
+ VRequestSalvage_r(ec, vp, SALVSYNC_NEEDED, VOL_SALVAGE_INVALIDATE_HEADER);
+ vp->nUsers = 0;
+
+#else /* AFS_DEMAND_ATTACH_FS */
+ Log("VAttachVolume: volume %s needs to be salvaged; not attached.\n", path);
+ *ec = VSALVAGE;
+#endif /* AFS_DEMAND_ATTACH_FS */
+
+ goto error;
+ }
+
+ 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 (V_destroyMe(vp) == DESTROY_ME) {
#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);
- VChangeState_r(vp, VOL_STATE_ERROR);
- vp->nUsers = 0;
+ /* schedule a salvage so the volume goes away on disk */
+ VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, VOL_SALVAGE_INVALIDATE_HEADER);
+ VChangeState_r(vp, VOL_STATE_ERROR);
+ vp->nUsers = 0;
#endif /* AFS_DEMAND_ATTACH_FS */
- FreeVolume(vp);
- Log("VAttachVolume: volume %s is junk; it should be destroyed at next salvage\n", path);
- *ec = VNOVOL;
- return NULL;
- }
+ Log("VAttachVolume: volume %s is junk; it should be destroyed at next salvage\n", path);
+ *ec = VNOVOL;
+ forcefree = 1;
+ goto error;
}
- vp->nextVnodeUnique = V_uniquifier(vp);
vp->vnodeIndex[vSmall].bitmap = vp->vnodeIndex[vLarge].bitmap = NULL;
#ifndef BITMAP_LATER
if (programType == fileServer && VolumeWriteable(vp)) {
#ifdef AFS_DEMAND_ATTACH_FS
VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, VOL_SALVAGE_INVALIDATE_HEADER);
vp->nUsers = 0;
-#else /* AFS_DEMAND_ATTACH_FS */
- FreeVolume(vp);
#endif /* AFS_DEMAND_ATTACH_FS */
Log("VAttachVolume: error getting bitmap for volume (%s)\n",
path);
- return NULL;
+ goto error;
}
}
}
#endif /* BITMAP_LATER */
+ if (VInit >= 2 && V_needsCallback(vp)) {
+ if (V_BreakVolumeCallbacks) {
+ Log("VAttachVolume: Volume %lu was changed externally; breaking callbacks\n",
+ afs_printable_uint32_lu(V_id(vp)));
+ V_needsCallback(vp) = 0;
+ VOL_UNLOCK;
+ (*V_BreakVolumeCallbacks) (V_id(vp));
+ VOL_LOCK;
+
+ VUpdateVolume_r(ec, vp, 0);
+ }
+#ifdef FSSYNC_BUILD_CLIENT
+ else if (VCanUseFSSYNC()) {
+ afs_int32 fsync_code;
+
+ V_needsCallback(vp) = 0;
+ VOL_UNLOCK;
+ fsync_code = FSYNC_VolOp(V_id(vp), NULL, FSYNC_VOL_BREAKCBKS, FSYNC_WHATEVER, NULL);
+ VOL_LOCK;
+
+ if (fsync_code) {
+ V_needsCallback(vp) = 1;
+ Log("Error trying to tell the fileserver to break callbacks for "
+ "changed volume %lu; error code %ld\n",
+ afs_printable_uint32_lu(V_id(vp)),
+ afs_printable_int32_ld(fsync_code));
+ } else {
+ VUpdateVolume_r(ec, vp, 0);
+ }
+ }
+#endif /* FSSYNC_BUILD_CLIENT */
+
+ if (*ec) {
+ Log("VAttachVolume: error %d clearing needsCallback on volume "
+ "%lu; needs salvage\n", (int)*ec,
+ afs_printable_uint32_lu(V_id(vp)));
+#ifdef AFS_DEMAND_ATTACH_FS
+ VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, VOL_SALVAGE_INVALIDATE_HEADER);
+ vp->nUsers = 0;
+#else /* !AFS_DEMAND_ATTACH_FS */
+ *ec = VSALVAGE;
+#endif /* !AFS_DEMAND_ATTACh_FS */
+ goto error;
+ }
+ }
+
if (programType == fileServer) {
if (vp->specialStatus)
vp->specialStatus = 0;
V_inUse(vp) = fileServer;
V_offlineMessage(vp)[0] = '\0';
}
+ if (!V_inUse(vp)) {
+ *ec = VNOVOL;
+#ifdef AFS_DEMAND_ATTACH_FS
+ /* Put the vol into PREATTACHED state, so if someone tries to
+ * access it again, we try to attach, see that we're not blessed,
+ * and give a VNOVOL error again. Putting it into UNATTACHED state
+ * would result in a VOFFLINE error instead. */
+ error_state = VOL_STATE_PREATTACHED;
+#endif /* AFS_DEMAND_ATTACH_FS */
+
+ /* mimic e.g. GetVolume errors */
+ if (!V_blessed(vp)) {
+ Log("Volume %lu offline: not blessed\n", afs_printable_uint32_lu(V_id(vp)));
+ FreeVolumeHeader(vp);
+ } else if (!V_inService(vp)) {
+ Log("Volume %lu offline: not in service\n", afs_printable_uint32_lu(V_id(vp)));
+ FreeVolumeHeader(vp);
+ } else {
+ Log("Volume %lu offline: needs salvage\n", afs_printable_uint32_lu(V_id(vp)));
+ *ec = VSALVAGE;
+#ifdef AFS_DEMAND_ATTACH_FS
+ error_state = VOL_STATE_ERROR;
+ /* see if we can recover */
+ VRequestSalvage_r(ec, vp, SALVSYNC_NEEDED, VOL_SALVAGE_INVALIDATE_HEADER);
+#endif
+ }
+#ifdef AFS_DEMAND_ATTACH_FS
+ vp->nUsers = 0;
+#endif
+ goto error;
+ }
} else {
- V_inUse(vp) = programType;
+#ifdef AFS_DEMAND_ATTACH_FS
+ if ((mode != V_PEEK) && (mode != V_SECRETLY))
+ V_inUse(vp) = programType;
+#endif /* AFS_DEMAND_ATTACH_FS */
V_checkoutMode(vp) = mode;
}
AddVolumeToHashTable(vp, V_id(vp));
#ifdef AFS_DEMAND_ATTACH_FS
+ if (VCanUnlockAttached() && (V_attachFlags(vp) & VOL_LOCKED)) {
+ VUnlockVolume(vp);
+ }
if ((programType != fileServer) ||
(V_inUse(vp) == fileServer)) {
AddVolumeToVByPList_r(vp);
VChangeState_r(vp, VOL_STATE_UNATTACHED);
}
#endif
+
return vp;
+
+ error:
+#ifdef AFS_DEMAND_ATTACH_FS
+ if (!VIsErrorState(V_attachState(vp))) {
+ VChangeState_r(vp, error_state);
+ }
+#endif /* AFS_DEMAND_ATTACH_FS */
+
+ if (read_header) {
+ VReleaseVolumeHandles_r(vp);
+ }
+
+#ifdef AFS_DEMAND_ATTACH_FS
+ VCheckSalvage(vp);
+ if (forcefree) {
+ FreeVolume(vp);
+ } else {
+ VCheckFree(vp);
+ }
+#else /* !AFS_DEMAND_ATTACH_FS */
+ FreeVolume(vp);
+#endif /* !AFS_DEMAND_ATTACH_FS */
+ return NULL;
}
/* Attach an existing volume.
}
#endif /* AFS_DEMAND_ATTACH_FS */
+#if 0
static int
VHold(register Volume * vp)
{
VOL_UNLOCK;
return retVal;
}
+#endif
/***************************************************/
return retVal;
}
+/* same as VGetVolume, but if a volume is waiting to go offline, we return
+ * that it is actually offline, instead of waiting for it to go offline */
+Volume *
+VGetVolumeNoWait(Error * ec, Error * client_ec, VolId volumeId)
+{
+ Volume *retVal;
+ VOL_LOCK;
+ retVal = GetVolume(ec, client_ec, volumeId, NULL, 1);
+ VOL_UNLOCK;
+ return retVal;
+}
+
Volume *
VGetVolume_r(Error * ec, VolId volumeId)
{
return GetVolume(ec, NULL, vp->hashid, vp, 0);
}
-/* private interface for getting a volume handle
- * volumeId must be provided.
- * hint is an optional parameter to speed up hash lookups
- * flags is not used at this time
+/**
+ * private interface for getting a volume handle
+ *
+ * @param[out] ec error code (0 if no error)
+ * @param[out] client_ec wire error code to be given to clients
+ * @param[in] volumeId ID of the volume we want
+ * @param[in] hint optional hint for hash lookups, or NULL
+ * @param[in] nowait 0 to wait for a 'goingOffline' volume to go offline
+ * before returning, 1 to return immediately
+ *
+ * @return a volume handle for the specified volume
+ * @retval NULL an error occurred, or the volume is in such a state that
+ * we cannot load a header or return any volume struct
+ *
+ * @note for DAFS, caller must NOT hold a ref count on 'hint'
*/
-/* for demand attach fs, caller MUST NOT hold a ref count on hint */
static Volume *
-GetVolume(Error * ec, Error * client_ec, VolId volumeId, Volume * hint, int flags)
+GetVolume(Error * ec, Error * client_ec, VolId volumeId, Volume * hint, int nowait)
{
Volume *vp = hint;
/* pull this profiling/debugging code out of regular builds */
* - VOL_STATE_SHUTTING_DOWN
*/
if ((V_attachState(vp) == VOL_STATE_ERROR) ||
- (V_attachState(vp) == VOL_STATE_SHUTTING_DOWN)) {
+ (V_attachState(vp) == VOL_STATE_SHUTTING_DOWN) ||
+ (V_attachState(vp) == VOL_STATE_GOING_OFFLINE)) {
*ec = VNOVOL;
vp = NULL;
break;
/* allowable states:
* - PREATTACHED
* - ATTACHED
- * - GOING_OFFLINE
* - SALVAGING
*/
}
#endif
- LoadVolumeHeader(ec, vp);
- if (*ec) {
- VGET_CTR_INC(V6);
- /* Only log the error if it was a totally unexpected error. Simply
- * a missing inode is likely to be caused by the volume being deleted */
- if (errno != ENXIO || LogLevel)
- Log("Volume %u: couldn't reread volume header\n",
- vp->hashid);
-#ifdef AFS_DEMAND_ATTACH_FS
- if (programType == fileServer) {
- VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, VOL_SALVAGE_INVALIDATE_HEADER);
- } else {
- FreeVolume(vp);
- vp = NULL;
- }
-#else /* AFS_DEMAND_ATTACH_FS */
- FreeVolume(vp);
- vp = NULL;
-#endif /* AFS_DEMAND_ATTACH_FS */
- break;
- }
-
#ifdef AFS_DEMAND_ATTACH_FS
/*
- * this test MUST happen after the volume header is loaded
+ * this test MUST happen after VAttachVolymeByVp, so vol_op_state is
+ * not VolOpRunningUnknown (attach2 would have converted it to Online
+ * or Offline)
*/
- if (vp->pending_vol_op && !VVolOpLeaveOnline_r(vp, vp->pending_vol_op)) {
+
+ /* only valid before/during demand attachment */
+ assert(!vp->pending_vol_op || vp->pending_vol_op->vol_op_state != FSSYNC_VolOpRunningUnknown);
+
+ /* deny getvolume due to running mutually exclusive vol op */
+ if (vp->pending_vol_op && vp->pending_vol_op->vol_op_state==FSSYNC_VolOpRunningOffline) {
/*
* volume cannot remain online during this volume operation.
* notify client.
}
*ec = VOFFLINE;
}
- ReleaseVolumeHeader(vp->header);
+ VChangeState_r(vp, VOL_STATE_UNATTACHED);
+ FreeVolumeHeader(vp);
vp = NULL;
break;
}
#endif /* AFS_DEMAND_ATTACH_FS */
+
+ LoadVolumeHeader(ec, vp);
+ if (*ec) {
+ VGET_CTR_INC(V6);
+ /* Only log the error if it was a totally unexpected error. Simply
+ * a missing inode is likely to be caused by the volume being deleted */
+ if (errno != ENXIO || LogLevel)
+ Log("Volume %u: couldn't reread volume header\n",
+ vp->hashid);
+#ifdef AFS_DEMAND_ATTACH_FS
+ if (VCanScheduleSalvage()) {
+ VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, VOL_SALVAGE_INVALIDATE_HEADER);
+ } else {
+ FreeVolume(vp);
+ vp = NULL;
+ }
+#else /* AFS_DEMAND_ATTACH_FS */
+ FreeVolume(vp);
+ vp = NULL;
+#endif /* AFS_DEMAND_ATTACH_FS */
+ break;
+ }
VGET_CTR_INC(V7);
if (vp->shuttingDown) {
if (programType == fileServer) {
VGET_CTR_INC(V9);
- if (vp->goingOffline) {
+ if (vp->goingOffline && !nowait) {
VGET_CTR_INC(V10);
#ifdef AFS_DEMAND_ATTACH_FS
/* wait for the volume to go offline */
} else if (V_inService(vp) == 0 || V_blessed(vp) == 0) {
VGET_CTR_INC(V12);
*ec = VNOVOL;
- } else if (V_inUse(vp) == 0) {
+ } else if (V_inUse(vp) == 0 || vp->goingOffline) {
VGET_CTR_INC(V13);
*ec = VOFFLINE;
} else {
void
VOffline_r(Volume * vp, char *message)
{
+#ifndef AFS_DEMAND_ATTACH_FS
Error error;
VolumeId vid = V_id(vp);
+#endif
- assert(programType != volumeUtility);
+ assert(programType != volumeUtility && programType != volumeServer);
if (!V_inUse(vp)) {
VPutVolume_r(vp);
return;
#endif /* AFS_DEMAND_ATTACH_FS */
}
+#ifdef AFS_DEMAND_ATTACH_FS
+/**
+ * Take a volume offline in order to perform a volume operation.
+ *
+ * @param[inout] ec address in which to store error code
+ * @param[in] vp volume object pointer
+ * @param[in] message volume offline status message
+ *
+ * @pre
+ * - VOL_LOCK is held
+ * - caller MUST hold a heavyweight ref on vp
+ *
+ * @post
+ * - volume is taken offline
+ * - if possible, volume operation is promoted to running state
+ * - on failure, *ec is set to nonzero
+ *
+ * @note Although this function does not return any value, it may
+ * still fail to promote our pending volume operation to
+ * a running state. Any caller MUST check the value of *ec,
+ * and MUST NOT blindly assume success.
+ *
+ * @warning if the caller does not hold a lightweight ref on vp,
+ * then it MUST NOT reference vp after this function
+ * returns to the caller.
+ *
+ * @internal volume package internal use only
+ */
+void
+VOfflineForVolOp_r(Error *ec, Volume *vp, char *message)
+{
+ assert(vp->pending_vol_op);
+ if (!V_inUse(vp)) {
+ VPutVolume_r(vp);
+ *ec = 1;
+ return;
+ }
+ if (V_offlineMessage(vp)[0] == '\0')
+ strncpy(V_offlineMessage(vp), message, sizeof(V_offlineMessage(vp)));
+ V_offlineMessage(vp)[sizeof(V_offlineMessage(vp)) - 1] = '\0';
+
+ vp->goingOffline = 1;
+ VChangeState_r(vp, VOL_STATE_GOING_OFFLINE);
+ VCreateReservation_r(vp);
+ VPutVolume_r(vp);
+
+ /* Wait for the volume to go offline */
+ while (!VIsOfflineState(V_attachState(vp))) {
+ /* do not give corrupted volumes to the volserver */
+ if (vp->salvage.requested && vp->pending_vol_op->com.programType != salvageServer) {
+ *ec = 1;
+ goto error;
+ }
+ VWaitStateChange_r(vp);
+ }
+ *ec = 0;
+ error:
+ VCancelReservation_r(vp);
+}
+#endif /* AFS_DEMAND_ATTACH_FS */
+
void
VOffline(Volume * vp, char *message)
{
{
VolumeId volume;
struct DiskPartition64 *tpartp;
- int notifyServer, useDone = FSYNC_VOL_ON;
+ int notifyServer = 0;
+ 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;
DeleteVolumeFromVByPList_r(vp);
VLRU_Delete_r(vp);
VChangeState_r(vp, VOL_STATE_SHUTTING_DOWN);
+#else
+ if (programType != fileServer)
+ V_inUse(vp) = 0;
#endif /* AFS_DEMAND_ATTACH_FS */
VPutVolume_r(vp);
/* Will be detached sometime in the future--this is OK since volume is offline */
* 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
* DFlushVolume outside of vol_glock_mutex...
*
* VCloseVnodeFiles_r drops the glock internally */
- DFlushVolume(V_id(vp));
+ DFlushVolume(vp->hashid);
VCloseVnodeFiles_r(vp);
#ifdef AFS_DEMAND_ATTACH_FS
#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);
IH_REALLYCLOSE(vp->linkHandle);
#ifdef AFS_DEMAND_ATTACH_FS
+ if ((V_attachFlags(vp) & VOL_LOCKED)) {
+ VUnlockVolume(vp);
+ }
+
VOL_LOCK;
VChangeState_r(vp, state_save);
#endif
/* XXX need to investigate whether we can perform
* DFlushVolume outside of vol_glock_mutex... */
- DFlushVolume(V_id(vp));
+ DFlushVolume(vp->hashid);
VReleaseVnodeFiles_r(vp); /* releases the glock internally */
#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);
IH_RELEASE(vp->linkHandle);
#ifdef AFS_DEMAND_ATTACH_FS
+ if ((V_attachFlags(vp) & VOL_LOCKED)) {
+ VUnlockVolume(vp);
+ }
+
VOL_LOCK;
VChangeState_r(vp, state_save);
#endif
if ((programType != fileServer) &&
(V_inUse(vp) == programType) &&
((V_checkoutMode(vp) == V_VOLUPD) ||
+ (V_checkoutMode(vp) == V_SECRETLY) ||
((V_checkoutMode(vp) == V_CLONE) &&
(VolumeWriteable(vp))))) {
V_inUse(vp) = 0;
if ((programType != fileServer) &&
(V_inUse(vp) == programType) &&
((V_checkoutMode(vp) == V_VOLUPD) ||
+ (V_checkoutMode(vp) == V_SECRETLY) ||
((V_checkoutMode(vp) == V_CLONE) &&
(VolumeWriteable(vp))))) {
V_inUse(vp) = 0;
static int
VCheckOffline(register Volume * vp)
{
- Volume * rvp = NULL;
int ret = 0;
if (vp->goingOffline && !vp->nUsers) {
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 */
static int
VCheckOffline(register Volume * vp)
{
- Volume * rvp = NULL;
int ret = 0;
if (vp->goingOffline && !vp->nUsers) {
int
VVolOpLeaveOnline_r(Volume * vp, FSSYNC_VolOp_info * vopinfo)
{
- return (vopinfo->com.command == FSYNC_VOL_NEEDVOLUME &&
+ return (vopinfo->vol_op_state == FSSYNC_VolOpRunningOnline ||
+ (vopinfo->com.command == FSYNC_VOL_NEEDVOLUME &&
(vopinfo->com.reason == V_READONLY ||
(!VolumeWriteable(vp) &&
(vopinfo->com.reason == V_CLONE ||
- vopinfo->com.reason == V_DUMP))));
+ vopinfo->com.reason == V_DUMP)))));
+}
+
+/**
+ * same as VVolOpLeaveOnline_r, but does not require a volume with an attached
+ * header.
+ *
+ * @param[in] vp volume object
+ * @param[in] vopinfo volume operation info object
+ *
+ * @return whether it is safe to leave volume online
+ * @retval 0 it is NOT SAFE to leave the volume online
+ * @retval 1 it is safe to leave the volume online during the operation
+ * @retval -1 unsure; volume header is required in order to know whether or
+ * not is is safe to leave the volume online
+ *
+ * @pre VOL_LOCK is held
+ *
+ * @internal volume package internal use only
+ */
+int
+VVolOpLeaveOnlineNoHeader_r(Volume * vp, FSSYNC_VolOp_info * vopinfo)
+{
+ /* follow the logic in VVolOpLeaveOnline_r; this is the same, except
+ * assume that we don't know VolumeWriteable; return -1 if the answer
+ * depends on VolumeWriteable */
+
+ if (vopinfo->vol_op_state == FSSYNC_VolOpRunningOnline) {
+ return 1;
+ }
+ if (vopinfo->com.command == FSYNC_VOL_NEEDVOLUME &&
+ vopinfo->com.reason == V_READONLY) {
+
+ return 1;
+ }
+ if (vopinfo->com.command == FSYNC_VOL_NEEDVOLUME &&
+ (vopinfo->com.reason == V_CLONE ||
+ vopinfo->com.reason == V_DUMP)) {
+
+ /* must know VolumeWriteable */
+ return -1;
+ }
+ return 0;
}
/**
/* online salvager routines */
/***************************************************/
#if defined(AFS_DEMAND_ATTACH_FS)
-#define SALVAGE_PRIO_UPDATE_INTERVAL 3 /**< number of seconds between prio updates */
-#define SALVAGE_COUNT_MAX 16 /**< number of online salvages we
- * allow before moving the volume
- * into a permanent error state
- *
- * once this threshold is reached,
- * the operator will have to manually
- * issue a 'bos salvage' to bring
- * the volume back online
- */
-
/**
* check whether a salvage needs to be performed on this volume.
*
VCheckSalvage(register Volume * vp)
{
int ret = 0;
-#ifdef SALVSYNC_BUILD_CLIENT
+#if defined(SALVSYNC_BUILD_CLIENT) || defined(FSSYNC_BUILD_CLIENT)
if (vp->nUsers || vp->nWaiters)
return ret;
if (vp->salvage.requested) {
VScheduleSalvage_r(vp);
ret = 1;
}
-#endif /* SALVSYNC_BUILD_CLIENT */
+#endif /* SALVSYNC_BUILD_CLIENT || FSSYNC_BUILD_CLIENT */
return ret;
}
* @retval 0 volume salvage will occur
* @retval 1 volume salvage could not be scheduled
*
- * @note DAFS fileserver only
+ * @note DAFS only
*
- * @note this call does not synchronously schedule a volume salvage. rather,
- * it sets volume state so that when volume refcounts reach zero, a
- * volume salvage will occur. by "refcounts", we mean both nUsers and
- * nWaiters must be zero.
+ * @note in the fileserver, this call does not synchronously schedule a volume
+ * salvage. rather, it sets volume state so that when volume refcounts
+ * reach zero, a volume salvage will occur. by "refcounts", we mean both
+ * nUsers and nWaiters must be zero.
*
* @internal volume package internal use only.
*/
{
int code = 0;
/*
- * for DAFS volume utilities, transition to error state
- * (at some point in the future, we should consider
- * making volser talk to salsrv)
+ * for DAFS volume utilities that are not supposed to schedule salvages,
+ * just transition to error state instead
*/
- if (programType != fileServer) {
+ if (!VCanScheduleSalvage()) {
VChangeState_r(vp, VOL_STATE_ERROR);
*ec = VSALVAGE;
return 1;
}
+ if (programType != fileServer && !VCanUseFSSYNC()) {
+ VChangeState_r(vp, VOL_STATE_ERROR);
+ *ec = VSALVAGE;
+ return 1;
+ }
+
if (!vp->salvage.requested) {
vp->salvage.requested = 1;
vp->salvage.reason = reason;
vp->stats.last_salvage = FT_ApproxTime();
- if (flags & VOL_SALVAGE_INVALIDATE_HEADER) {
- /* XXX this should likely be changed to FreeVolumeHeader() */
- ReleaseVolumeHeader(vp->header);
- }
+
+ /* Note that it is not possible for us to reach this point if a
+ * salvage is already running on this volume (even if the fileserver
+ * was restarted during the salvage). If a salvage were running, the
+ * salvager would have write-locked the volume header file, so when
+ * we tried to lock the volume header, the lock would have failed,
+ * and we would have failed during attachment prior to calling
+ * VRequestSalvage. So we know that we can schedule salvages without
+ * fear of a salvage already running for this volume. */
+
if (vp->stats.salvages < SALVAGE_COUNT_MAX) {
VChangeState_r(vp, VOL_STATE_SALVAGING);
*ec = VSALVAGING;
} else {
Log("VRequestSalvage: volume %u online salvaged too many times; forced offline.\n", vp->hashid);
+
+ /* make sure neither VScheduleSalvage_r nor
+ * VUpdateSalvagePriority_r try to schedule another salvage */
+ vp->salvage.requested = vp->salvage.scheduled = 0;
+
VChangeState_r(vp, VOL_STATE_ERROR);
*ec = VSALVAGE;
code = 1;
}
+ if (flags & VOL_SALVAGE_INVALIDATE_HEADER) {
+ /* Instead of ReleaseVolumeHeader, we do FreeVolumeHeader()
+ so that the the next VAttachVolumeByVp_r() invocation
+ of attach2() will pull in a cached header
+ entry and fail, then load a fresh one from disk and attach
+ it to the volume.
+ */
+ FreeVolumeHeader(vp);
+ }
}
return code;
}
*
* @internal volume package internal use only.
*/
-static int
+int
VUpdateSalvagePriority_r(Volume * vp)
{
- int code, ret=0;
- afs_uint32 now;
+ int ret=0;
#ifdef SALVSYNC_BUILD_CLIENT
+ afs_uint32 now;
+ int code;
+
vp->salvage.prio++;
now = FT_ApproxTime();
}
+#if defined(SALVSYNC_BUILD_CLIENT) || defined(FSSYNC_BUILD_CLIENT)
+
+/* A couple of little helper functions. These return true if we tried to
+ * use this mechanism to schedule a salvage, false if we haven't tried.
+ * If we did try a salvage then the results are contained in code.
+ */
+
+static_inline int
+try_SALVSYNC(Volume *vp, char *partName, int *code) {
+#ifdef SALVSYNC_BUILD_CLIENT
+ if (VCanUseSALVSYNC()) {
+ Log("Scheduling salvage for volume %lu on part %s over SALVSYNC\n",
+ afs_printable_uint32_lu(vp->hashid), partName);
+
+ /* can't use V_id() since there's no guarantee
+ * we have the disk data header at this point */
+ *code = SALVSYNC_SalvageVolume(vp->hashid,
+ partName,
+ SALVSYNC_SALVAGE,
+ vp->salvage.reason,
+ vp->salvage.prio,
+ NULL);
+ return 1;
+ }
+#endif
+ return 0;
+}
+
+static_inline int
+try_FSSYNC(Volume *vp, char *partName, int *code) {
+#ifdef FSSYNC_BUILD_CLIENT
+ if (VCanUseFSSYNC()) {
+ Log("Scheduling salvage for volume %lu on part %s over FSSYNC\n",
+ afs_printable_uint32_lu(vp->hashid), partName);
+
+ /*
+ * If we aren't the fileserver, tell the fileserver the volume
+ * needs to be salvaged. We could directly tell the
+ * salvageserver, but the fileserver keeps track of some stats
+ * related to salvages, and handles some other salvage-related
+ * complications for us.
+ */
+ *code = FSYNC_VolOp(vp->hashid, partName,
+ FSYNC_VOL_FORCE_ERROR, FSYNC_SALVAGE, NULL);
+ return 1;
+ }
+#endif /* FSSYNC_BUILD_CLIENT */
+ return 0;
+}
+
/**
- * schedule a salvage with the salvage server.
+ * schedule a salvage with the salvage server or fileserver.
*
* @param[in] vp pointer to volume object
*
* @return operation status
* @retval 0 salvage scheduled successfully
- * @retval 1 salvage not scheduled, or SALVSYNC com error
+ * @retval 1 salvage not scheduled, or SALVSYNC/FSSYNC com error
*
* @pre
* @arg VOL_LOCK is held.
* @arg nUsers and nWaiters should be zero.
*
- * @post salvageserver is sent a salvage request
+ * @post salvageserver or fileserver is sent a salvage request
*
- * @note DAFS fileserver only
+ * @note If we are the fileserver, the request will be sent to the salvage
+ * server over SALVSYNC. If we are not the fileserver, the request will be
+ * sent to the fileserver over FSSYNC (FSYNC_VOL_FORCE_ERROR/FSYNC_SALVAGE).
+ *
+ * @note DAFS only
*
* @internal volume package internal use only.
*/
static int
VScheduleSalvage_r(Volume * vp)
{
- int code, ret=0;
-#ifdef SALVSYNC_BUILD_CLIENT
+ int ret=0;
+ int code;
VolState state_save;
VThreadOptions_t * thread_opts;
char partName[16];
+ assert(VCanUseSALVSYNC() || VCanUseFSSYNC());
+
if (vp->nWaiters || vp->nUsers) {
return 1;
}
if (thread_opts == NULL) {
thread_opts = &VThread_defaults;
}
- if (thread_opts->disallow_salvsync) {
+ if (thread_opts->disallow_salvsync || vol_disallow_salvsync) {
return 1;
}
state_save = VChangeState_r(vp, VOL_STATE_SALVSYNC_REQ);
VOL_UNLOCK;
- /* can't use V_id() since there's no guarantee
- * we have the disk data header at this point */
- code = SALVSYNC_SalvageVolume(vp->hashid,
- partName,
- SALVSYNC_SALVAGE,
- vp->salvage.reason,
- vp->salvage.prio,
- NULL);
+ assert(try_SALVSYNC(vp, partName, &code) ||
+ try_FSSYNC(vp, partName, &code));
+
VOL_LOCK;
VChangeState_r(vp, state_save);
if (code == SYNC_OK) {
vp->salvage.scheduled = 1;
- vp->stats.salvages++;
vp->stats.last_salvage_req = FT_ApproxTime();
- IncUInt64(&VStats.salvages);
+ if (VCanUseSALVSYNC()) {
+ /* don't record these stats for non-fileservers; let the
+ * fileserver take care of these */
+ vp->stats.salvages++;
+ IncUInt64(&VStats.salvages);
+ }
} else {
ret = 1;
switch(code) {
case SYNC_COM_ERROR:
break;
case SYNC_DENIED:
- Log("VScheduleSalvage_r: SALVSYNC request denied\n");
+ Log("VScheduleSalvage_r: Salvage request for volume %lu "
+ "denied\n", afs_printable_uint32_lu(vp->hashid));
break;
default:
- Log("VScheduleSalvage_r: SALVSYNC unknown protocol error\n");
+ Log("VScheduleSalvage_r: Salvage request for volume %lu "
+ "received unknown protocol error %d\n",
+ afs_printable_uint32_lu(vp->hashid), code);
break;
}
- }
- }
-#endif /* SALVSYNC_BUILD_CLIENT */
- return ret;
-}
-
-/**
- * ask salvageserver to cancel a scheduled salvage operation.
- *
- * @param[in] vp pointer to volume object
- * @param[in] reason SALVSYNC protocol reason code
- *
- * @return operation status
- * @retval 0 success
- * @retval 1 request failed
- *
- * @pre VOL_LOCK is held.
- *
- * @post salvageserver is sent a request to cancel the volume salvage.
- * volume is transitioned to a hard error state.
- *
- * @internal volume package internal use only.
- */
-static int
-VCancelSalvage_r(Volume * vp, int reason)
-{
- int code, ret = 0;
-
-#ifdef SALVSYNC_BUILD_CLIENT
- if (vp->salvage.scheduled) {
- VChangeState_r(vp, VOL_STATE_SALVSYNC_REQ);
- VOL_UNLOCK;
-
- /* can't use V_id() since there's no guarantee
- * we have the disk data header at this point */
- code = SALVSYNC_SalvageVolume(vp->hashid,
- VPartitionPath(vp->partition),
- SALVSYNC_CANCEL,
- reason,
- 0,
- NULL);
-
- VOL_LOCK;
- VChangeState_r(vp, VOL_STATE_ERROR);
- if (code == SYNC_OK) {
- vp->salvage.scheduled = 0;
- vp->salvage.requested = 0;
- } else {
- ret = 1;
+ if (VCanUseFSSYNC()) {
+ VChangeState_r(vp, VOL_STATE_ERROR);
+ }
}
}
-#endif /* SALVSYNC_BUILD_CLIENT */
return ret;
}
-
+#endif /* SALVSYNC_BUILD_CLIENT || FSSYNC_BUILD_CLIENT */
#ifdef SALVSYNC_BUILD_CLIENT
+
/**
* connect to the salvageserver SYNC service.
*
int
VDisconnectSALV(void)
{
- int retVal;
VOL_LOCK;
VDisconnectSALV_r();
VOL_UNLOCK;
- return retVal;
+ return 0;
}
/**
/***************************************************/
/* 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
/* volume bitmap routines */
/***************************************************/
+/**
+ * allocate a vnode bitmap number for the vnode
+ *
+ * @param[out] ec error code
+ * @param[in] vp volume object pointer
+ * @param[in] index vnode index number for the vnode
+ * @param[in] flags flag values described in note
+ *
+ * @note for DAFS, flags parameter controls locking behavior.
+ * If (flags & VOL_ALLOC_BITMAP_WAIT) is set, then this function
+ * will create a reservation and block on any other exclusive
+ * operations. Otherwise, this function assumes the caller
+ * already has exclusive access to vp, and we just change the
+ * volume state.
+ *
+ * @pre VOL_LOCK held
+ *
+ * @return bit number allocated
+ */
/*
- * For demand attach fs, flags parameter controls
- * locking behavior. If (flags & VOL_ALLOC_BITMAP_WAIT)
- * is set, then this function will create a reservation
- * and block on any other exclusive operations. Otherwise,
- * this function assumes the caller already has exclusive
- * access to vp, and we just change the volume state.
+
*/
-VnodeId
+int
VAllocBitmapEntry_r(Error * ec, Volume * vp,
struct vnodeIndex *index, int flags)
{
- VnodeId ret;
+ int ret = 0;
register byte *bp, *ep;
#ifdef AFS_DEMAND_ATTACH_FS
VolState state_save;
/* This test is probably redundant */
if (!VolumeWriteable(vp)) {
*ec = (bit32) VREADONLY;
- return 0;
+ return ret;
}
#ifdef AFS_DEMAND_ATTACH_FS
vp->shuttingDown = 1; /* Let who has it free it. */
vp->specialStatus = 0;
#endif /* AFS_DEMAND_ATTACH_FS */
- ret = NULL;
goto done;
}
}
bp++;
o = ffs(~*bp) - 1; /* ffs is documented in BSTRING(3) */
*bp |= (1 << o);
- ret = (VnodeId) ((bp - index->bitmap) * 8 + o);
+ ret = ((bp - index->bitmap) * 8 + o);
#ifdef AFS_DEMAND_ATTACH_FS
VOL_LOCK;
#endif /* AFS_DEMAND_ATTACH_FS */
return ret;
}
-VnodeId
+int
VAllocBitmapEntry(Error * ec, Volume * vp, register struct vnodeIndex * index)
{
- VnodeId retVal;
+ int retVal;
VOL_LOCK;
retVal = VAllocBitmapEntry_r(ec, vp, index, VOL_ALLOC_BITMAP_WAIT);
VOL_UNLOCK;
VGetBitmap_r(Error * ec, Volume * vp, VnodeClass class)
{
StreamHandle_t *file;
- int nVnodes;
- int size;
+ afs_sfsize_t nVnodes, size;
struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
struct vnodeIndex *vip = &vp->vnodeIndex[class];
struct VnodeDiskObject *vnode;
*ec = 0;
name[0] = '/';
- (void)afs_snprintf(&name[1], (sizeof name) - 1, VFORMAT, volumeId);
+ (void)afs_snprintf(&name[1], (sizeof name) - 1, VFORMAT, afs_printable_uint32_lu(volumeId));
for (dp = DiskPartitionList; dp; dp = dp->next) {
struct afs_stat status;
strcpy(path, VPartitionPath(dp));
VolumeExternalName(VolumeId volumeId)
{
static char name[VMAXPATHLEN];
- (void)afs_snprintf(name, sizeof name, VFORMAT, volumeId);
+ (void)afs_snprintf(name, sizeof name, VFORMAT, afs_printable_uint32_lu(volumeId));
return name;
}
* @see afs_snprintf
*
* @note re-entrant equivalent of VolumeExternalName
- *
- * @internal volume package internal use only.
*/
-static int
+int
VolumeExternalName_r(VolumeId volumeId, char * name, size_t len)
{
- return afs_snprintf(name, len, VFORMAT, volumeId);
+ return afs_snprintf(name, len, VFORMAT, afs_printable_uint32_lu(volumeId));
}
#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
* All we need to do is bzero the entire VOL_STATS_BYTES of
* the detailed volume statistics area.
*/
- memset((char *)(V_stat_area(vp)), 0, VOL_STATS_BYTES);
+ memset((V_stat_area(vp)), 0, VOL_STATS_BYTES);
#endif /* OPENAFS_VOL_STATS */
}
VBumpVolumeUsage_r(register Volume * vp)
{
unsigned int now = FT_ApproxTime();
+ V_accessDate(vp) = now;
if (now - V_dayUseDate(vp) > OneDay)
VAdjustVolumeStatistics_r(vp);
/*
* @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
*
* @internal volume package interal use only.
*/
static void
-VLRU_Init_Node_r(volatile Volume * vp)
+VLRU_Init_Node_r(Volume * vp)
{
if (!VLRU_enabled)
return;
* @internal volume package internal use only.
*/
static void
-VLRU_Add_r(volatile Volume * vp)
+VLRU_Add_r(Volume * vp)
{
int idx;
VolState state_save;
* @internal volume package internal use only.
*/
static void
-VLRU_Delete_r(volatile Volume * vp)
+VLRU_Delete_r(Volume * vp)
{
int idx;
* @internal volume package internal use only.
*/
static void
-VLRU_UpdateAccess_r(volatile Volume * vp)
+VLRU_UpdateAccess_r(Volume * vp)
{
- afs_uint32 live_interval;
Volume * rvp = NULL;
if (!VLRU_enabled)
* @internal volume package internal use only.
*/
static void
-VLRU_SwitchQueues(volatile Volume * vp, int new_idx, int append)
+VLRU_SwitchQueues(Volume * vp, int new_idx, int append)
{
if (queue_IsNotOnQueue(&vp->vlru))
return;
VLRU_ScannerThread(void * args)
{
afs_uint32 now, min_delay, delay;
- afs_uint32 next_scan[VLRU_GENERATIONS];
- afs_uint32 next_promotion[VLRU_GENERATIONS];
int i, min_idx, min_op, overdue, state;
/* set t=0 for promotion cycle to be
}
/* 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);
int len, chaining, promote;
afs_uint32 now, thresh;
struct rx_queue *qp, *nqp;
- Volume * vp, *start, *end;
+ Volume * vp, *start = NULL, *end = NULL;
/* get exclusive access to two chains, and drop the glock */
VLRU_Wait_r(&volume_LRU.q[idx]);
int len, chaining, demote;
afs_uint32 now, thresh;
struct rx_queue *qp, *nqp;
- Volume * vp, *start, *end;
+ Volume * vp, *start = NULL, *end = NULL;
Volume ** salv_flag_vec = NULL;
int salv_vec_offset = 0;
{
afs_uint32 now, thresh;
struct rx_queue *qp, *nqp;
- volatile Volume * vp;
+ Volume * vp;
int i, locked = 1;
assert(idx == VLRU_QUEUE_NEW || idx == VLRU_QUEUE_CANDIDATE);
/* check whether volume is safe to soft detach
* caller MUST NOT hold a ref count on vp */
static int
-VCheckSoftDetach(volatile Volume * vp, afs_uint32 thresh)
+VCheckSoftDetach(Volume * vp, afs_uint32 thresh)
{
int ret=0;
/* check whether volume should be made a
* soft detach candidate */
static int
-VCheckSoftDetachCandidate(volatile Volume * vp, afs_uint32 thresh)
+VCheckSoftDetachCandidate(Volume * vp, afs_uint32 thresh)
{
int idx, ret = 0;
if (vp->nUsers || vp->nWaiters)
*
* caller MUST NOT hold a ref count on vp */
static int
-VSoftDetachVolume_r(volatile Volume * vp, afs_uint32 thresh)
+VSoftDetachVolume_r(Volume * vp, afs_uint32 thresh)
{
afs_uint32 ts_save;
int ret = 0;
V_attachFlags(vp) &= ~(VOL_ON_VLRU);
VCancelReservation_r(vp);
return 0;
+ default:
+ break;
}
/* hold the volume and take it offline.
volume_hdr_LRU.stats.used = howMany;
volume_hdr_LRU.stats.attached = 0;
hp = (struct volHeader *)(calloc(howMany, sizeof(struct volHeader)));
+ assert(hp != NULL);
+
while (howMany--)
+ /* We are using ReleaseVolumeHeader to initialize the values on the header list
+ * to ensure they have the right values
+ */
ReleaseVolumeHeader(hp++);
}
static int everLogged = 0;
#ifdef AFS_DEMAND_ATTACH_FS
- VolState vp_save, back_save;
+ VolState vp_save = 0, back_save = 0;
/* XXX debug 9/19/05 we've apparently got
* a ref counting bug somewhere that's
hd = vp->header;
queue_Remove(hd);
assert(hd->back == vp);
+#ifdef AFS_DEMAND_ATTACH_FS
+ V_attachFlags(vp) &= ~(VOL_HDR_IN_LRU);
+#endif
} else {
/* we need to grab a new element off the LRU */
if (queue_IsNotEmpty(&volume_hdr_LRU)) {
#endif /* AFS_DEMAND_ATTACH_FS */
if (*ec) {
/* maintain (nUsers==0) => header in LRU invariant */
- ReleaseVolumeHeader(vp->header);
+ FreeVolumeHeader(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
*/
VLookupVolume_r(Error * ec, VolId volumeId, Volume * hint)
{
register int looks = 0;
- Volume * vp, *np, *pp;
+ Volume * vp, *np;
+#ifdef AFS_DEMAND_ATTACH_FS
+ Volume *pp;
+#endif
VolumeHashChainHead * head;
*ec = 0;
void
VPrintExtendedCacheStats_r(int flags)
{
- int i, j;
+ int i;
afs_uint32 vol_sum = 0;
struct stats {
double min;
/* print extended VLRU statistics */
if (VVLRUExtStats_r(&vlru_stats, vol_sum) == 0) {
afs_uint32 idx, cur, lpos;
- VOL_UNLOCK;
VolumeId line[5];
+ VOL_UNLOCK;
+
Log("VLRU State Dump:\n\n");
for (idx = VLRU_QUEUE_NEW; idx < VLRU_QUEUE_INVALID; idx++) {
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;
+}
+
+afs_int32
+VCanUnsafeAttach(void)
+{
+ return vol_opts.unsafe_attach;
+}