#include "volume.h"
#include "partition.h"
#include "volume_inline.h"
+#include "common.h"
+
#ifdef AFS_PTHREAD_ENV
#include <assert.h>
#else /* AFS_PTHREAD_ENV */
extern void *calloc(), *realloc();
#endif
-/*@printflike@*/ extern void Log(const char *format, ...);
-
/* Forward declarations */
static Volume *attach2(Error * ec, VolId volumeId, char *path,
struct DiskPartition64 *partp, Volume * vp,
/***************************************************/
/* 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.
*
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;
/* create threads to scan disk partitions. */
for (i=0; i < threads; i++) {
struct vinitvolumepackage_thread_param *params;
- params = (struct vinitvolumepackage_thread_param *)malloc(sizeof(struct vinitvolumepackage_thread_param));
+ 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_DECL;
AFS_SIGSET_CLEAR();
assert(pthread_create (&tid, &attrs, &VInitVolumePackageThread, (void*)params) == 0);
AFS_SIGSET_RESTORE();
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");
goto done;
}
- V_needsCallback(vp) = 0;
VUpdateVolume_r(ec, vp, 0);
if (*ec) {
Log("VAttachVolume: Error updating volume %u\n", vp->hashid);
* 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;
VOL_LOCK;
vp->nextVnodeUnique = V_uniquifier(vp);
- if (VShouldCheckInUse(mode)) {
-#ifndef FAST_RESTART
- if (V_inUse(vp) && VolumeWriteable(vp)) {
- if (!V_needsSalvaged(vp)) {
- V_needsSalvaged(vp) = 1;
- VUpdateVolume_r(ec, vp, 0);
- }
+
+ 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)
- if (!VCanScheduleSalvage()) {
- Log("VAttachVolume: volume %s needs to be salvaged; not attached.\n", path);
- }
- VRequestSalvage_r(ec, vp, SALVSYNC_NEEDED, VOL_SALVAGE_INVALIDATE_HEADER);
- vp->nUsers = 0;
+ if (!VCanScheduleSalvage()) {
+ Log("VAttachVolume: volume %s needs to be salvaged; not attached.\n", path);
+ }
+ 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;
+ Log("VAttachVolume: volume %s needs to be salvaged; not attached.\n", path);
+ *ec = VSALVAGE;
#endif /* AFS_DEMAND_ATTACH_FS */
- goto error;
- }
-#endif /* FAST_RESTART */
+ 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 (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);
- 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 */
- Log("VAttachVolume: volume %s is junk; it should be destroyed at next salvage\n", path);
- *ec = VNOVOL;
- forcefree = 1;
- goto error;
- }
+ Log("VAttachVolume: volume %s is junk; it should be destroyed at next salvage\n", path);
+ *ec = VNOVOL;
+ forcefree = 1;
+ goto error;
}
vp->vnodeIndex[vSmall].bitmap = vp->vnodeIndex[vLarge].bitmap = NULL;
}
#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 {
#ifdef AFS_DEMAND_ATTACH_FS
if ((mode != V_PEEK) && (mode != V_SECRETLY))
error:
#ifdef AFS_DEMAND_ATTACH_FS
if (!VIsErrorState(V_attachState(vp))) {
- VChangeState_r(vp, VOL_STATE_ERROR);
+ VChangeState_r(vp, error_state);
}
#endif /* AFS_DEMAND_ATTACH_FS */
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 */
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 {
* If we did try a salvage then the results are contained in code.
*/
-static inline int
+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,
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
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;
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)) {
/* 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++) {
{
return vol_opts.canUseSALVSYNC;
}
+
+afs_int32
+VCanUnsafeAttach(void)
+{
+ return vol_opts.unsafe_attach;
+}