openafs/src/vol/volume_inline.h
Simon Wilkinson 304fac42b8 vol: Don't assume enum is an int
The type that an enum is promoted to for arithmetic is compiler
dependent. We can't assume that it's an int, or a short.

Explicitly cast the volume state enum where clang gets upset.

Change-Id: Iab285d3a04dac698797ab0df2337aee75e3876b2
Reviewed-on: http://gerrit.openafs.org/9137
Reviewed-by: Chas Williams - CONTRACTOR <chas@cmf.nrl.navy.mil>
Reviewed-by: Derrick Brashear <shadow@your-file-system.com>
Tested-by: BuildBot <buildbot@rampaginggeek.com>
2013-02-20 10:23:10 -08:00

601 lines
14 KiB
C

/*
* Copyright 2005-2008, Sine Nomine Associates and others.
* All Rights Reserved.
*
* This software has been released under the terms of the IBM Public
* License. For details, see the LICENSE file in the top-level source
* directory or online at http://www.openafs.org/dl/license10.html
*/
#ifndef _AFS_VOL_VOLUME_INLINE_H
#define _AFS_VOL_VOLUME_INLINE_H 1
#include "volume.h"
#include "partition.h"
#if defined(AFS_DEMAND_ATTACH_FS) || defined(AFS_DEMAND_ATTACH_UTIL)
# include "lock.h"
#endif
#ifdef AFS_PTHREAD_ENV
#include <afs/opr_assert.h>
/**
* @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 = opr_cv_timedwait(cv, &vol_glock_mutex, ts);
VOL_LOCK_DBG_CV_WAIT_END;
if (code == ETIMEDOUT) {
code = 0;
if (timedout) {
*timedout = 1;
}
}
opr_Assert(code == 0);
}
#endif /* AFS_PTHREAD_ENV */
/**
* tell caller whether the given program type represents a salvaging
* program.
*
* @param type program type enumeration
*
* @return whether program state is a salvager
* @retval 0 type is a non-salvaging program
* @retval 1 type is a salvaging program
*/
static_inline int
VIsSalvager(ProgramType type)
{
switch(type) {
case salvager:
case salvageServer:
case volumeSalvager:
return 1;
default:
return 0;
}
}
/**
* tells caller whether or not we need to lock the entire partition when
* attaching a volume.
*
* @return whether or not we need to lock the partition
* @retval 0 no, we do not
* @retval 1 yes, we do
*
* @note for DAFS, always returns 0, since we use per-header locks instead
*/
static_inline int
VRequiresPartLock(void)
{
#if defined(AFS_DEMAND_ATTACH_FS) || defined(AFS_DEMAND_ATTACH_UTIL)
return 0;
#else
switch (programType) {
case volumeServer:
case volumeUtility:
return 1;
default:
return 0;
}
#endif /* AFS_DEMAND_ATTACH_FS || AFS_DEMAND_ATTACH_UTIL */
}
/**
* tells caller whether or not we need to check out a volume from the
* fileserver before we can use it.
*
* @param[in] mode the mode of attachment for the volume
*
* @return whether or not we need to check out the volume from the fileserver
* @retval 0 no, we can just use the volume
* @retval 1 yes, we must check out the volume before use
*/
static_inline int
VMustCheckoutVolume(int mode)
{
if (VCanUseFSSYNC() && mode != V_SECRETLY && mode != V_PEEK) {
return 1;
}
return 0;
}
/**
* tells caller whether we should check the inUse field in the volume
* header when attaching a volume.
*
* If we check inUse, that generally means we will salvage the volume
* (or put it in an error state) if we detect that another program
* claims to be using the volume when we try to attach. We don't always
* want to do that, since sometimes we know that the volume may be in
* use by another program, e.g. when we are attaching with V_PEEK
* or attaching for only reading (V_READONLY).
*
* @param mode the mode of attachment for the volume
*
* @return whether or not we should check inUse
* @retval 0 no, we should not check inUse
* @retval 1 yes, we should check inUse
*/
static_inline int
VShouldCheckInUse(int mode)
{
if (VCanUnsafeAttach()) {
return 0;
}
if (programType == fileServer) {
return 1;
}
if (VMustCheckoutVolume(mode)) {
/*
* Before VShouldCheckInUse() was called, the caller checked out the
* volume from the fileserver. The volume may not be in use by the
* fileserver, or another program, at this point. The caller should
* verify by checking inUse is not set, otherwise the volume state
* is in error.
*
* However, an exception is made for the V_READONLY attach mode. The
* volume may still be in use by the fileserver when a caller has
* checked out the volume from the fileserver with the V_READONLY
* attach mode, and so it is not an error for the inUse field to be set
* at this point. The caller should not check the inUse and may
* not change any volume state.
*/
if (mode == V_READONLY) {
return 0; /* allowed to be inUse; do not check */
}
return 1; /* may not be inUse; check */
}
return 0;
}
#if defined(AFS_DEMAND_ATTACH_FS) || defined(AFS_DEMAND_ATTACH_UTIL)
/**
* acquire a non-blocking disk lock for a particular volume id.
*
* @param[in] volid the volume ID to lock
* @param[in] dp the partition on which 'volid' resides
* @param[in] locktype READ_LOCK or WRITE_LOCK
*
* @return operation status
* @retval 0 success, lock was obtained
* @retval EBUSY another process holds a conflicting lock
* @retval EIO error acquiring lock
*
* @note Use VLockVolumeNB instead, if possible; only use this directly if
* you are not dealing with 'Volume*'s and attached volumes and such
*
* @pre There must not be any other threads acquiring locks on the same volid
* and partition; the locks will not work correctly if two threads try to
* acquire locks for the same volume
*/
static_inline int
VLockVolumeByIdNB(VolumeId volid, struct DiskPartition64 *dp, int locktype)
{
return VLockFileLock(&dp->volLockFile, volid, locktype, 1 /* nonblock */);
}
/**
* release a lock acquired by VLockVolumeByIdNB.
*
* @param[in] volid the volume id to unlock
* @param[in] dp the partition on which 'volid' resides
*
* @pre volid was previously locked by VLockVolumeByIdNB
*/
static_inline void
VUnlockVolumeById(VolumeId volid, struct DiskPartition64 *dp)
{
VLockFileUnlock(&dp->volLockFile, volid);
}
/***************************************************/
/* demand attach fs state machine routines */
/***************************************************/
/**
* tells caller whether we need to keep volumes locked for the entire time we
* are using them, or if we can unlock volumes as soon as they are attached.
*
* @return whether we can unlock attached volumes or not
* @retval 1 yes, we can unlock attached volumes
* @retval 0 no, do not unlock volumes until we unattach them
*/
static_inline int
VCanUnlockAttached(void)
{
switch(programType) {
case fileServer:
return 1;
default:
return 0;
}
}
/**
* tells caller whether we need to lock a vol header with a write lock, a
* read lock, or if we do not need to lock it at all, when attaching.
*
* @param[in] mode volume attachment mode
* @param[in] writable 1 if the volume is writable, 0 if not
*
* @return how we need to lock the vol header
* @retval 0 do not lock the vol header at all
* @retval READ_LOCK lock the vol header with a read lock
* @retval WRITE_LOCK lock the vol header with a write lock
*
* @note DAFS only (non-DAFS uses partition locks)
*/
static_inline int
VVolLockType(int mode, int writable)
{
switch (programType) {
case fileServer:
if (writable) {
return WRITE_LOCK;
}
return READ_LOCK;
case volumeSalvager:
case salvageServer:
case salvager:
return WRITE_LOCK;
default:
/* volserver, vol utilies, etc */
switch (mode) {
case V_READONLY:
return READ_LOCK;
case V_VOLUPD:
case V_SECRETLY:
return WRITE_LOCK;
case V_CLONE:
case V_DUMP:
if (writable) {
return WRITE_LOCK;
}
return READ_LOCK;
case V_PEEK:
return 0;
default:
opr_Assert(0 /* unknown checkout mode */);
return 0;
}
}
}
#endif /* AFS_DEMAND_ATTACH_FS || AFS_DEMAND_ATTACH_UTIL */
#ifdef AFS_DEMAND_ATTACH_FS
/**
* tells caller whether or not the volume is effectively salvaging.
*
* @param vp volume pointer
*
* @return whether volume is salvaging or not
* @retval 0 no, volume is not salvaging
* @retval 1 yes, volume is salvaging
*
* @note The volume may not actually be getting salvaged at the moment if
* this returns 1, but may have just been requested or scheduled to be
* salvaged. Callers should treat these cases as pretty much the same
* anyway, since we should not touch a volume that is busy salvaging or
* waiting to be salvaged.
*/
static_inline int
VIsSalvaging(struct Volume *vp)
{
/* these tests are a bit redundant, but to be safe... */
switch(V_attachState(vp)) {
case VOL_STATE_SALVAGING:
case VOL_STATE_SALVAGE_REQ:
return 1;
default:
if (vp->salvage.requested || vp->salvage.scheduled) {
return 1;
}
}
return 0;
}
/**
* tells caller whether or not the current state requires
* exclusive access without holding glock.
*
* @param state volume state enumeration
*
* @return whether volume state is a mutually exclusive state
* @retval 0 no, state is re-entrant
* @retval 1 yes, state is mutually exclusive
*
* @note DEMAND_ATTACH_FS only
*/
static_inline int
VIsExclusiveState(VolState state)
{
switch (state) {
case VOL_STATE_UPDATING:
case VOL_STATE_ATTACHING:
case VOL_STATE_GET_BITMAP:
case VOL_STATE_HDR_LOADING:
case VOL_STATE_HDR_ATTACHING:
case VOL_STATE_OFFLINING:
case VOL_STATE_DETACHING:
case VOL_STATE_SALVSYNC_REQ:
case VOL_STATE_VNODE_ALLOC:
case VOL_STATE_VNODE_GET:
case VOL_STATE_VNODE_CLOSE:
case VOL_STATE_VNODE_RELEASE:
case VOL_STATE_VLRU_ADD:
case VOL_STATE_SCANNING_RXCALLS:
return 1;
default:
return 0;
}
}
/**
* tell caller whether V_attachState is an error condition.
*
* @param state volume state enumeration
*
* @return whether volume state is in error state
* @retval 0 state is not an error state
* @retval 1 state is an error state
*
* @note DEMAND_ATTACH_FS only
*/
static_inline int
VIsErrorState(VolState state)
{
switch (state) {
case VOL_STATE_ERROR:
case VOL_STATE_SALVAGING:
case VOL_STATE_SALVAGE_REQ:
return 1;
default:
return 0;
}
}
/**
* tell caller whether V_attachState is an offline condition.
*
* @param state volume state enumeration
*
* @return whether volume state is in offline state
* @retval 0 state is not an offline state
* @retval 1 state is an offline state
*
* @note DEMAND_ATTACH_FS only
*/
static_inline int
VIsOfflineState(VolState state)
{
switch (state) {
case VOL_STATE_UNATTACHED:
case VOL_STATE_ERROR:
case VOL_STATE_SALVAGING:
case VOL_STATE_DELETED:
return 1;
default:
return 0;
}
}
/**
* tell caller whether V_attachState is valid.
*
* @param state volume state enumeration
*
* @return whether volume state is a mutually exclusive state
* @retval 0 no, state is not valid
* @retval 1 yes, state is a valid enumeration member
*
* @note DEMAND_ATTACH_FS only
*
* @note do we really want to treat VOL_STATE_FREED as valid?
*/
static_inline int
VIsValidState(VolState state)
{
if (((int) state >= 0) &&
(state < VOL_STATE_COUNT)) {
return 1;
}
return 0;
}
/**
* increment volume-package internal refcount.
*
* @param vp volume object pointer
*
* @internal volume package internal use only
*
* @pre VOL_LOCK must be held
*
* @post volume waiters refcount is incremented
*
* @see VCancelReservation_r
*
* @note DEMAND_ATTACH_FS only
*/
static_inline void
VCreateReservation_r(Volume * vp)
{
vp->nWaiters++;
}
/**
* wait for the volume to change states.
*
* @param vp volume object pointer
*
* @pre VOL_LOCK held; ref held on volume
*
* @post VOL_LOCK held; volume state has changed from previous value
*
* @note DEMAND_ATTACH_FS only
*/
static_inline void
VWaitStateChange_r(Volume * vp)
{
VolState state_save = V_attachState(vp);
opr_Assert(vp->nWaiters || vp->nUsers);
do {
VOL_CV_WAIT(&V_attachCV(vp));
} while (V_attachState(vp) == state_save);
opr_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.
*
* @pre VOL_LOCK held; ref held on volume
*
* @post VOL_LOCK held; volume not in exclusive state
*
* @param vp volume object pointer
*
* @note DEMAND_ATTACH_FS only
*/
static_inline void
VWaitExclusiveState_r(Volume * vp)
{
opr_Assert(vp->nWaiters || vp->nUsers);
while (VIsExclusiveState(V_attachState(vp))) {
VOL_CV_WAIT(&V_attachCV(vp));
}
opr_Assert(V_attachState(vp) != VOL_STATE_FREED);
}
/**
* change state, and notify other threads,
* return previous state to caller.
*
* @param vp pointer to volume object
* @param new_state new volume state value
* @pre VOL_LOCK held
*
* @post volume state changed; stats updated
*
* @return previous volume state
*
* @note DEMAND_ATTACH_FS only
*/
static_inline VolState
VChangeState_r(Volume * vp, VolState new_state)
{
VolState old_state = V_attachState(vp);
/* XXX profiling need to make sure these counters
* don't kill performance... */
VStats.state_levels[old_state]--;
VStats.state_levels[new_state]++;
V_attachState(vp) = new_state;
opr_cv_broadcast(&V_attachCV(vp));
return old_state;
}
#endif /* AFS_DEMAND_ATTACH_FS */
#define VENUMCASE(en) \
case en: return #en
/**
* translate a ProgramType code to a string.
*
* @param[in] type ProgramType numeric code
*
* @return a human-readable string for that program type
* @retval "**UNKNOWN**" an unknown ProgramType was given
*/
static_inline char *
VPTypeToString(ProgramType type)
{
switch (type) {
VENUMCASE(fileServer);
VENUMCASE(volumeUtility);
VENUMCASE(salvager);
VENUMCASE(salvageServer);
VENUMCASE(debugUtility);
VENUMCASE(volumeServer);
VENUMCASE(volumeSalvager);
default:
return "**UNKNOWN**";
}
}
#undef VENUMCASE
#endif /* _AFS_VOL_VOLUME_INLINE_H */