From c57b5a419292d16e662f5ab6c5422b8475e8d139 Mon Sep 17 00:00:00 2001 From: Andrew Deason Date: Wed, 27 Oct 2010 17:34:40 -0500 Subject: [PATCH] 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. Reviewed-on: http://gerrit.openafs.org/3214 Reviewed-by: Derrick Brashear Tested-by: BuildBot (cherry picked from commit db6ee95864a8fc5f33b7e95c19c8ff5058d37e92) Change-Id: I6c7b3a0b9fe174ebffeb03153dda1c4705d7dac5 Reviewed-on: http://gerrit.openafs.org/6264 Tested-by: BuildBot Reviewed-by: Derrick Brashear --- 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 4adcd1973..b7319b8df 100644 --- a/src/viced/afsfileprocs.c +++ b/src/viced/afsfileprocs.c @@ -549,9 +549,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 c7cca3f10..37c2e44e5 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 */ @@ -3504,6 +3505,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) @@ -3574,14 +3615,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; } @@ -3589,7 +3632,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 */ @@ -3597,7 +3640,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); } /** @@ -3607,17 +3650,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 */ @@ -3877,19 +3927,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 89ff9eea3..3e0031422 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 514582468..445dc3bf2 100644 --- a/src/vol/volume_inline.h +++ b/src/vol/volume_inline.h @@ -424,6 +424,47 @@ VWaitStateChange_r(Volume * vp) osi_Assert(V_attachState(vp) != VOL_STATE_FREED); } +/** + * 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. * -- 2.39.5