From db6ee95864a8fc5f33b7e95c19c8ff5058d37e92 Mon Sep 17 00:00:00 2001 From: Andrew Deason Date: Wed, 27 Oct 2010 17:34:40 -0500 Subject: [PATCH 1/1] vol: Add VGetVolumeTimed Replace the VGetVolumeNoWait interface with the more general VGetVolumeTimed interface, which allows for waiting for offlining volume for arbitrary amounts of time (instead of just "waiting forever" or "not waiting at all"). Also add VOL_CV_TIMEDWAIT and VTimedWaitStateChange_r as necessary to implement this. Change-Id: I3d9ea7a89f16ec5fd185eae7679e84033eb2d581 Reviewed-on: http://gerrit.openafs.org/3214 Reviewed-by: Derrick Brashear Tested-by: BuildBot --- src/viced/afsfileprocs.c | 8 +++- src/vol/volume.c | 95 ++++++++++++++++++++++++++++++++++++++---------- src/vol/volume.h | 34 ++++++++++++++++- src/vol/volume_inline.h | 41 +++++++++++++++++++++ 4 files changed, 157 insertions(+), 21 deletions(-) diff --git a/src/viced/afsfileprocs.c b/src/viced/afsfileprocs.c index 74fef85..6336e1f 100644 --- a/src/viced/afsfileprocs.c +++ b/src/viced/afsfileprocs.c @@ -489,9 +489,15 @@ CheckVnode(AFSFid * fid, Volume ** volptr, Vnode ** vptr, int lock) VRESTARTING #endif ; +#ifdef AFS_PTHREAD_ENV + static const struct timespec timeout_ts = { 0, 0 }; + static const struct timespec * const ts = &timeout_ts; +#else + static const struct timespec * const ts = NULL; +#endif errorCode = 0; - *volptr = VGetVolumeNoWait(&local_errorCode, &errorCode, (afs_int32) fid->Volume); + *volptr = VGetVolumeTimed(&local_errorCode, &errorCode, (afs_int32) fid->Volume, ts); if (!errorCode) { osi_Assert(*volptr); break; diff --git a/src/vol/volume.c b/src/vol/volume.c index 1a1a895..2a73c3c 100644 --- a/src/vol/volume.c +++ b/src/vol/volume.c @@ -199,7 +199,8 @@ static void VCloseVolumeHandles_r(Volume * vp); static void LoadVolumeHeader(Error * ec, Volume * vp); static int VCheckOffline(Volume * vp); static int VCheckDetach(Volume * vp); -static Volume * GetVolume(Error * ec, Error * client_ec, VolId volumeId, Volume * hint, int flags); +static Volume * GetVolume(Error * ec, Error * client_ec, VolId volumeId, + Volume * hint, const struct timespec *ts); int LogLevel; /* Vice loglevel--not defined as extern so that it will be * defined when not linked with vice, XXXX */ @@ -3501,6 +3502,46 @@ VHold_r(Volume * vp) } #endif /* AFS_DEMAND_ATTACH_FS */ +/**** volume timeout-related stuff ****/ + +#ifdef AFS_PTHREAD_ENV + +static_inline int +VTimedOut(const struct timespec *ts) +{ + struct timeval tv; + int code; + + if (ts->tv_sec == 0) { + /* short-circuit; this will have always timed out */ + return 1; + } + + code = gettimeofday(&tv, NULL); + if (code) { + Log("Error %d from gettimeofday, assuming we have not timed out\n", errno); + /* assume no timeout; failure mode is we just wait longer than normal + * instead of returning errors when we shouldn't */ + return 0; + } + + if (tv.tv_sec < ts->tv_sec || + (tv.tv_sec == ts->tv_sec && tv.tv_usec*1000 < ts->tv_nsec)) { + + return 0; + } + + return 1; +} + +#else /* AFS_PTHREAD_ENV */ + +/* Waiting a certain amount of time for offlining volumes is not supported + * for LWP due to a lack of primitives. So, we never time out */ +# define VTimedOut(x) (0) + +#endif /* !AFS_PTHREAD_ENV */ + #if 0 static int VHold(Volume * vp) @@ -3571,14 +3612,16 @@ VGetVolume(Error * ec, Error * client_ec, VolId volumeId) 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 */ +/* same as VGetVolume, but if a volume is waiting to go offline, we only wait + * until time ts. If we have waited longer than that, we return that it is + * actually offline, instead of waiting for it to go offline */ Volume * -VGetVolumeNoWait(Error * ec, Error * client_ec, VolId volumeId) +VGetVolumeTimed(Error * ec, Error * client_ec, VolId volumeId, + const struct timespec *ts) { Volume *retVal; VOL_LOCK; - retVal = GetVolume(ec, client_ec, volumeId, NULL, 1); + retVal = GetVolume(ec, client_ec, volumeId, NULL, ts); VOL_UNLOCK; return retVal; } @@ -3586,7 +3629,7 @@ VGetVolumeNoWait(Error * ec, Error * client_ec, VolId volumeId) Volume * VGetVolume_r(Error * ec, VolId volumeId) { - return GetVolume(ec, NULL, volumeId, NULL, 0); + return GetVolume(ec, NULL, volumeId, NULL, NULL); } /* try to get a volume we've previously looked up */ @@ -3594,7 +3637,7 @@ VGetVolume_r(Error * ec, VolId volumeId) Volume * VGetVolumeByVp_r(Error * ec, Volume * vp) { - return GetVolume(ec, NULL, vp->hashid, vp, 0); + return GetVolume(ec, NULL, vp->hashid, vp, NULL); } /** @@ -3604,17 +3647,24 @@ VGetVolumeByVp_r(Error * ec, Volume * vp) * @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 + * @param[in] timeout absolute deadline for waiting for the volume to go + * offline, if it is going offline. NULL to wait forever. * * @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' + * + * @note 'timeout' is only checked if the volume is actually going offline; so + * if you pass timeout->tv_sec = 0, this will exhibit typical + * nonblocking behavior. + * + * @note for LWP builds, 'timeout' must be NULL */ static Volume * -GetVolume(Error * ec, Error * client_ec, VolId volumeId, Volume * hint, int nowait) +GetVolume(Error * ec, Error * client_ec, VolId volumeId, Volume * hint, + const struct timespec *timeout) { Volume *vp = hint; /* pull this profiling/debugging code out of regular builds */ @@ -3869,19 +3919,26 @@ GetVolume(Error * ec, Error * client_ec, VolId volumeId, Volume * hint, int nowa if (programType == fileServer) { VGET_CTR_INC(V9); - if (vp->goingOffline && !nowait) { - VGET_CTR_INC(V10); + if (vp->goingOffline) { + if (timeout && VTimedOut(timeout)) { + /* we've timed out; don't wait for the vol */ + } else { + VGET_CTR_INC(V10); #ifdef AFS_DEMAND_ATTACH_FS - /* wait for the volume to go offline */ - if (V_attachState(vp) == VOL_STATE_GOING_OFFLINE) { - VWaitStateChange_r(vp); - } + /* wait for the volume to go offline */ + if (V_attachState(vp) == VOL_STATE_GOING_OFFLINE) { + VTimedWaitStateChange_r(vp, timeout, NULL); + } #elif defined(AFS_PTHREAD_ENV) - VOL_CV_WAIT(&vol_put_volume_cond); + VOL_CV_TIMEDWAIT(&vol_put_volume_cond, timeout, NULL); #else /* AFS_PTHREAD_ENV */ - LWP_WaitProcess(VPutVolume); + /* LWP has no timed wait, so the caller better not be + * expecting one */ + osi_Assert(!timeout); + LWP_WaitProcess(VPutVolume); #endif /* AFS_PTHREAD_ENV */ - continue; + continue; + } } if (vp->specialStatus) { VGET_CTR_INC(V11); diff --git a/src/vol/volume.h b/src/vol/volume.h index da961a6..9f43b54 100644 --- a/src/vol/volume.h +++ b/src/vol/volume.h @@ -95,6 +95,37 @@ extern pthread_t vol_glock_holder; #define VOL_UNLOCK MUTEX_EXIT(&vol_glock_mutex) #define VOL_CV_WAIT(cv) CV_WAIT((cv), &vol_glock_mutex) #endif /* !VOL_LOCK_DEBUG */ + +/** + * @param[in] cv cond var + * @param[in] ts deadline, or NULL to wait forever + * @param[out] timedout set to 1 if we returned due to the deadline, 0 if we + * returned due to the cond var getting signalled. If + * NULL, it is ignored. + */ +static_inline void +VOL_CV_TIMEDWAIT(pthread_cond_t *cv, const struct timespec *ts, int *timedout) +{ + int code; + if (timedout) { + *timedout = 0; + } + if (!ts) { + VOL_CV_WAIT(cv); + return; + } + VOL_LOCK_DBG_CV_WAIT_BEGIN; + code = CV_TIMEDWAIT(cv, &vol_glock_mutex, ts); + VOL_LOCK_DBG_CV_WAIT_END; + if (code == ETIMEDOUT) { + code = 0; + if (timedout) { + *timedout = 1; + } + } + osi_Assert(code == 0); +} + #define VSALVSYNC_LOCK MUTEX_ENTER(&vol_salvsync_mutex) #define VSALVSYNC_UNLOCK MUTEX_EXIT(&vol_salvsync_mutex) #define VTRANS_LOCK MUTEX_ENTER(&vol_trans_mutex) @@ -767,7 +798,8 @@ struct volHeader { extern char *VSalvageMessage; /* Canonical message when a volume is forced * offline */ extern Volume *VGetVolume(Error * ec, Error * client_ec, VolId volumeId); -extern Volume *VGetVolumeNoWait(Error * ec, Error * client_ec, VolId volumeId); +extern Volume *VGetVolumeTimed(Error * ec, Error * client_ec, VolId volumeId, + const struct timespec *ts); extern Volume *VGetVolume_r(Error * ec, VolId volumeId); extern void VPutVolume(Volume *); extern void VPutVolume_r(Volume *); diff --git a/src/vol/volume_inline.h b/src/vol/volume_inline.h index 2d8e498..7b75a7b 100644 --- a/src/vol/volume_inline.h +++ b/src/vol/volume_inline.h @@ -422,6 +422,47 @@ VWaitStateChange_r(Volume * vp) } /** + * wait for the volume to change states within a certain amount of time + * + * @param[in] vp volume object pointer + * @param[in] ts deadline (absolute time) or NULL to wait forever + * + * @pre VOL_LOCK held; ref held on volume + * @post VOL_LOCK held; volume state has changed and/or it is after the time + * specified in ts + * + * @note DEMAND_ATTACH_FS only + * @note if ts is NULL, this is identical to VWaitStateChange_r + */ +static_inline void +VTimedWaitStateChange_r(Volume * vp, const struct timespec *ts, int *atimedout) +{ + VolState state_save; + int timeout; + + if (atimedout) { + *atimedout = 0; + } + + if (!ts) { + VWaitStateChange_r(vp); + return; + } + + state_save = V_attachState(vp); + + assert(vp->nWaiters || vp->nUsers); + do { + VOL_CV_TIMEDWAIT(&V_attachCV(vp), ts, &timeout); + } while (V_attachState(vp) == state_save && !timeout); + assert(V_attachState(vp) != VOL_STATE_FREED); + + if (atimedout && timeout) { + *atimedout = 1; + } +} + +/** * wait for blocking ops to end. * * @pre VOL_LOCK held; ref held on volume -- 1.9.4