vol: allow non-dafs volume utils to attach with V_READONLY again
[openafs.git] / src / vol / volume_inline.h
1 /*
2  * Copyright 2005-2008, Sine Nomine Associates and others.
3  * All Rights Reserved.
4  *
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 #ifndef _AFS_VOL_VOLUME_INLINE_H
11 #define _AFS_VOL_VOLUME_INLINE_H 1
12
13 #include "volume.h"
14 #include "partition.h"
15
16 #if defined(AFS_DEMAND_ATTACH_FS) || defined(AFS_DEMAND_ATTACH_UTIL)
17 # include "lock.h"
18 #endif
19
20 #ifdef AFS_PTHREAD_ENV
21
22 #include <afs/opr_assert.h>
23
24 /**
25  * @param[in] cv cond var
26  * @param[in] ts deadline, or NULL to wait forever
27  * @param[out] timedout  set to 1 if we returned due to the deadline, 0 if we
28  *                       returned due to the cond var getting signalled. If
29  *                       NULL, it is ignored.
30  */
31 static_inline void
32 VOL_CV_TIMEDWAIT(pthread_cond_t *cv, const struct timespec *ts, int *timedout)
33 {
34     int code;
35     if (timedout) {
36         *timedout = 0;
37     }
38     if (!ts) {
39         VOL_CV_WAIT(cv);
40         return;
41     }
42     VOL_LOCK_DBG_CV_WAIT_BEGIN;
43     code = opr_cv_timedwait(cv, &vol_glock_mutex, ts);
44     VOL_LOCK_DBG_CV_WAIT_END;
45     if (code == ETIMEDOUT) {
46         code = 0;
47         if (timedout) {
48             *timedout = 1;
49         }
50     }
51     opr_Assert(code == 0);
52 }
53 #endif /* AFS_PTHREAD_ENV */
54
55 /**
56  * tell caller whether the given program type represents a salvaging
57  * program.
58  *
59  * @param type  program type enumeration
60  *
61  * @return whether program state is a salvager
62  *   @retval 0  type is a non-salvaging program
63  *   @retval 1  type is a salvaging program
64  */
65 static_inline int
66 VIsSalvager(ProgramType type)
67 {
68     switch(type) {
69     case salvager:
70     case salvageServer:
71     case volumeSalvager:
72         return 1;
73     default:
74         return 0;
75     }
76 }
77
78 /**
79  * tells caller whether or not we need to lock the entire partition when
80  * attaching a volume.
81  *
82  * @return whether or not we need to lock the partition
83  *  @retval 0  no, we do not
84  *  @retval 1  yes, we do
85  *
86  * @note for DAFS, always returns 0, since we use per-header locks instead
87  */
88 static_inline int
89 VRequiresPartLock(void)
90 {
91 #if defined(AFS_DEMAND_ATTACH_FS) || defined(AFS_DEMAND_ATTACH_UTIL)
92     return 0;
93 #else
94     switch (programType) {
95     case volumeServer:
96     case volumeUtility:
97         return 1;
98     default:
99         return 0;
100     }
101 #endif /* AFS_DEMAND_ATTACH_FS || AFS_DEMAND_ATTACH_UTIL */
102 }
103
104 /**
105  * tells caller whether or not we need to check out a volume from the
106  * fileserver before we can use it.
107  *
108  * @param[in] mode the mode of attachment for the volume
109  *
110  * @return whether or not we need to check out the volume from the fileserver
111  *  @retval 0 no, we can just use the volume
112  *  @retval 1 yes, we must check out the volume before use
113  */
114 static_inline int
115 VMustCheckoutVolume(int mode)
116 {
117     if (VCanUseFSSYNC() && mode != V_SECRETLY && mode != V_PEEK) {
118         return 1;
119     }
120     return 0;
121 }
122
123 /**
124  * tells caller whether we should check the inUse field in the volume
125  * header when attaching a volume.
126  *
127  * If we check inUse, that generally means we will salvage the volume
128  * (or put it in an error state) if we detect that another program
129  * claims to be using the volume when we try to attach. We don't always
130  * want to do that, since sometimes we know that the volume may be in
131  * use by another program, e.g. when we are attaching with V_PEEK
132  * or attaching for only reading (V_READONLY).
133  *
134  * @param mode  the mode of attachment for the volume
135  *
136  * @return whether or not we should check inUse
137  *  @retval 0  no, we should not check inUse
138  *  @retval 1  yes, we should check inUse
139  */
140 static_inline int
141 VShouldCheckInUse(int mode)
142 {
143     if (VCanUnsafeAttach()) {
144         return 0;
145     }
146     if (programType == fileServer) {
147        return 1;
148     }
149     if (VMustCheckoutVolume(mode)) {
150         /*
151          * Before VShouldCheckInUse() was called, the caller checked out the
152          * volume from the fileserver. The volume may not be in use by the
153          * fileserver, or another program, at this point. The caller should
154          * verify by checking inUse is not set, otherwise the volume state
155          * is in error.
156          *
157          * However, an exception is made for the V_READONLY attach mode.  The
158          * volume may still be in use by the fileserver when a caller has
159          * checked out the volume from the fileserver with the V_READONLY
160          * attach mode, and so it is not an error for the inUse field to be set
161          * at this point. The caller should not check the inUse and may
162          * not change any volume state.
163          */
164         if (mode == V_READONLY) {
165             return 0; /* allowed to be inUse; do not check */
166         }
167         return 1; /* may not be inUse; check */
168     }
169     return 0;
170 }
171
172 #if defined(AFS_DEMAND_ATTACH_FS) || defined(AFS_DEMAND_ATTACH_UTIL)
173 /**
174  * acquire a non-blocking disk lock for a particular volume id.
175  *
176  * @param[in] volid the volume ID to lock
177  * @param[in] dp    the partition on which 'volid' resides
178  * @param[in] locktype READ_LOCK or WRITE_LOCK
179  *
180  * @return operation status
181  *  @retval 0 success, lock was obtained
182  *  @retval EBUSY another process holds a conflicting lock
183  *  @retval EIO   error acquiring lock
184  *
185  * @note Use VLockVolumeNB instead, if possible; only use this directly if
186  * you are not dealing with 'Volume*'s and attached volumes and such
187  *
188  * @pre There must not be any other threads acquiring locks on the same volid
189  * and partition; the locks will not work correctly if two threads try to
190  * acquire locks for the same volume
191  */
192 static_inline int
193 VLockVolumeByIdNB(VolumeId volid, struct DiskPartition64 *dp, int locktype)
194 {
195     return VLockFileLock(&dp->volLockFile, volid, locktype, 1 /* nonblock */);
196 }
197
198 /**
199  * release a lock acquired by VLockVolumeByIdNB.
200  *
201  * @param[in] volid the volume id to unlock
202  * @param[in] dp    the partition on which 'volid' resides
203  *
204  * @pre volid was previously locked by VLockVolumeByIdNB
205  */
206 static_inline void
207 VUnlockVolumeById(VolumeId volid, struct DiskPartition64 *dp)
208 {
209     VLockFileUnlock(&dp->volLockFile, volid);
210 }
211
212 /***************************************************/
213 /* demand attach fs state machine routines         */
214 /***************************************************/
215
216 /**
217  * tells caller whether we need to keep volumes locked for the entire time we
218  * are using them, or if we can unlock volumes as soon as they are attached.
219  *
220  * @return whether we can unlock attached volumes or not
221  *  @retval 1 yes, we can unlock attached volumes
222  *  @retval 0 no, do not unlock volumes until we unattach them
223  */
224 static_inline int
225 VCanUnlockAttached(void)
226 {
227     switch(programType) {
228     case fileServer:
229         return 1;
230     default:
231         return 0;
232     }
233 }
234
235 /**
236  * tells caller whether we need to lock a vol header with a write lock, a
237  * read lock, or if we do not need to lock it at all, when attaching.
238  *
239  * @param[in]  mode  volume attachment mode
240  * @param[in]  writable  1 if the volume is writable, 0 if not
241  *
242  * @return how we need to lock the vol header
243  *  @retval 0 do not lock the vol header at all
244  *  @retval READ_LOCK lock the vol header with a read lock
245  *  @retval WRITE_LOCK lock the vol header with a write lock
246  *
247  * @note DAFS only (non-DAFS uses partition locks)
248  */
249 static_inline int
250 VVolLockType(int mode, int writable)
251 {
252     switch (programType) {
253     case fileServer:
254         if (writable) {
255             return WRITE_LOCK;
256         }
257         return READ_LOCK;
258
259     case volumeSalvager:
260     case salvageServer:
261     case salvager:
262         return WRITE_LOCK;
263
264     default:
265         /* volserver, vol utilies, etc */
266
267         switch (mode) {
268         case V_READONLY:
269             return READ_LOCK;
270
271         case V_VOLUPD:
272         case V_SECRETLY:
273             return WRITE_LOCK;
274
275         case V_CLONE:
276         case V_DUMP:
277             if (writable) {
278                 return WRITE_LOCK;
279             }
280             return READ_LOCK;
281
282         case V_PEEK:
283             return 0;
284
285         default:
286             opr_Assert(0 /* unknown checkout mode */);
287             return 0;
288         }
289     }
290 }
291 #endif /* AFS_DEMAND_ATTACH_FS || AFS_DEMAND_ATTACH_UTIL */
292
293 #ifdef AFS_DEMAND_ATTACH_FS
294
295 /**
296  * tells caller whether or not the volume is effectively salvaging.
297  *
298  * @param vp  volume pointer
299  *
300  * @return whether volume is salvaging or not
301  *  @retval 0 no, volume is not salvaging
302  *  @retval 1 yes, volume is salvaging
303  *
304  * @note The volume may not actually be getting salvaged at the moment if
305  *       this returns 1, but may have just been requested or scheduled to be
306  *       salvaged. Callers should treat these cases as pretty much the same
307  *       anyway, since we should not touch a volume that is busy salvaging or
308  *       waiting to be salvaged.
309  */
310 static_inline int
311 VIsSalvaging(struct Volume *vp)
312 {
313     /* these tests are a bit redundant, but to be safe... */
314     switch(V_attachState(vp)) {
315     case VOL_STATE_SALVAGING:
316     case VOL_STATE_SALVAGE_REQ:
317         return 1;
318     default:
319         if (vp->salvage.requested || vp->salvage.scheduled) {
320             return 1;
321         }
322     }
323     return 0;
324 }
325
326 /**
327  * tells caller whether or not the current state requires
328  * exclusive access without holding glock.
329  *
330  * @param state  volume state enumeration
331  *
332  * @return whether volume state is a mutually exclusive state
333  *   @retval 0  no, state is re-entrant
334  *   @retval 1  yes, state is mutually exclusive
335  *
336  * @note DEMAND_ATTACH_FS only
337  */
338 static_inline int
339 VIsExclusiveState(VolState state)
340 {
341     switch (state) {
342     case VOL_STATE_UPDATING:
343     case VOL_STATE_ATTACHING:
344     case VOL_STATE_GET_BITMAP:
345     case VOL_STATE_HDR_LOADING:
346     case VOL_STATE_HDR_ATTACHING:
347     case VOL_STATE_OFFLINING:
348     case VOL_STATE_DETACHING:
349     case VOL_STATE_SALVSYNC_REQ:
350     case VOL_STATE_VNODE_ALLOC:
351     case VOL_STATE_VNODE_GET:
352     case VOL_STATE_VNODE_CLOSE:
353     case VOL_STATE_VNODE_RELEASE:
354     case VOL_STATE_VLRU_ADD:
355     case VOL_STATE_SCANNING_RXCALLS:
356         return 1;
357     default:
358         return 0;
359     }
360 }
361
362 /**
363  * tell caller whether V_attachState is an error condition.
364  *
365  * @param state  volume state enumeration
366  *
367  * @return whether volume state is in error state
368  *   @retval 0  state is not an error state
369  *   @retval 1  state is an error state
370  *
371  * @note DEMAND_ATTACH_FS only
372  */
373 static_inline int
374 VIsErrorState(VolState state)
375 {
376     switch (state) {
377     case VOL_STATE_ERROR:
378     case VOL_STATE_SALVAGING:
379     case VOL_STATE_SALVAGE_REQ:
380         return 1;
381     default:
382         return 0;
383     }
384 }
385
386 /**
387  * tell caller whether V_attachState is an offline condition.
388  *
389  * @param state  volume state enumeration
390  *
391  * @return whether volume state is in offline state
392  *   @retval 0  state is not an offline state
393  *   @retval 1  state is an offline state
394  *
395  * @note DEMAND_ATTACH_FS only
396  */
397 static_inline int
398 VIsOfflineState(VolState state)
399 {
400     switch (state) {
401     case VOL_STATE_UNATTACHED:
402     case VOL_STATE_ERROR:
403     case VOL_STATE_SALVAGING:
404     case VOL_STATE_DELETED:
405         return 1;
406     default:
407         return 0;
408     }
409 }
410
411 /**
412  * tell caller whether V_attachState is valid.
413  *
414  * @param state  volume state enumeration
415  *
416  * @return whether volume state is a mutually exclusive state
417  *   @retval 0  no, state is not valid
418  *   @retval 1  yes, state is a valid enumeration member
419  *
420  * @note DEMAND_ATTACH_FS only
421  *
422  * @note do we really want to treat VOL_STATE_FREED as valid?
423  */
424 static_inline int
425 VIsValidState(VolState state)
426 {
427     if ((state >= 0) &&
428         (state < VOL_STATE_COUNT)) {
429         return 1;
430     }
431     return 0;
432 }
433
434 /**
435  * increment volume-package internal refcount.
436  *
437  * @param vp  volume object pointer
438  *
439  * @internal volume package internal use only
440  *
441  * @pre VOL_LOCK must be held
442  *
443  * @post volume waiters refcount is incremented
444  *
445  * @see VCancelReservation_r
446  *
447  * @note DEMAND_ATTACH_FS only
448  */
449 static_inline void
450 VCreateReservation_r(Volume * vp)
451 {
452     vp->nWaiters++;
453 }
454
455 /**
456  * wait for the volume to change states.
457  *
458  * @param vp  volume object pointer
459  *
460  * @pre VOL_LOCK held; ref held on volume
461  *
462  * @post VOL_LOCK held; volume state has changed from previous value
463  *
464  * @note DEMAND_ATTACH_FS only
465  */
466 static_inline void
467 VWaitStateChange_r(Volume * vp)
468 {
469     VolState state_save = V_attachState(vp);
470
471     opr_Assert(vp->nWaiters || vp->nUsers);
472     do {
473         VOL_CV_WAIT(&V_attachCV(vp));
474     } while (V_attachState(vp) == state_save);
475     opr_Assert(V_attachState(vp) != VOL_STATE_FREED);
476 }
477
478 /**
479  * wait for the volume to change states within a certain amount of time
480  *
481  * @param[in] vp  volume object pointer
482  * @param[in] ts  deadline (absolute time) or NULL to wait forever
483  *
484  * @pre VOL_LOCK held; ref held on volume
485  * @post VOL_LOCK held; volume state has changed and/or it is after the time
486  *       specified in ts
487  *
488  * @note DEMAND_ATTACH_FS only
489  * @note if ts is NULL, this is identical to VWaitStateChange_r
490  */
491 static_inline void
492 VTimedWaitStateChange_r(Volume * vp, const struct timespec *ts, int *atimedout)
493 {
494     VolState state_save;
495     int timeout;
496
497     if (atimedout) {
498         *atimedout = 0;
499     }
500
501     if (!ts) {
502         VWaitStateChange_r(vp);
503         return;
504     }
505
506     state_save = V_attachState(vp);
507
508     assert(vp->nWaiters || vp->nUsers);
509     do {
510         VOL_CV_TIMEDWAIT(&V_attachCV(vp), ts, &timeout);
511     } while (V_attachState(vp) == state_save && !timeout);
512     assert(V_attachState(vp) != VOL_STATE_FREED);
513
514     if (atimedout && timeout) {
515         *atimedout = 1;
516     }
517 }
518
519 /**
520  * wait for blocking ops to end.
521  *
522  * @pre VOL_LOCK held; ref held on volume
523  *
524  * @post VOL_LOCK held; volume not in exclusive state
525  *
526  * @param vp  volume object pointer
527  *
528  * @note DEMAND_ATTACH_FS only
529  */
530 static_inline void
531 VWaitExclusiveState_r(Volume * vp)
532 {
533     opr_Assert(vp->nWaiters || vp->nUsers);
534     while (VIsExclusiveState(V_attachState(vp))) {
535         VOL_CV_WAIT(&V_attachCV(vp));
536     }
537     opr_Assert(V_attachState(vp) != VOL_STATE_FREED);
538 }
539
540 /**
541  * change state, and notify other threads,
542  * return previous state to caller.
543  *
544  * @param vp         pointer to volume object
545  * @param new_state  new volume state value
546  * @pre VOL_LOCK held
547  *
548  * @post volume state changed; stats updated
549  *
550  * @return previous volume state
551  *
552  * @note DEMAND_ATTACH_FS only
553  */
554 static_inline VolState
555 VChangeState_r(Volume * vp, VolState new_state)
556 {
557     VolState old_state = V_attachState(vp);
558
559     /* XXX profiling need to make sure these counters
560      * don't kill performance... */
561     VStats.state_levels[old_state]--;
562     VStats.state_levels[new_state]++;
563
564     V_attachState(vp) = new_state;
565     opr_cv_broadcast(&V_attachCV(vp));
566     return old_state;
567 }
568
569 #endif /* AFS_DEMAND_ATTACH_FS */
570
571 #define VENUMCASE(en) \
572     case en: return #en
573
574 /**
575  * translate a ProgramType code to a string.
576  *
577  * @param[in] type ProgramType numeric code
578  *
579  * @return a human-readable string for that program type
580  *  @retval "**UNKNOWN**" an unknown ProgramType was given
581  */
582 static_inline char *
583 VPTypeToString(ProgramType type)
584 {
585     switch (type) {
586         VENUMCASE(fileServer);
587         VENUMCASE(volumeUtility);
588         VENUMCASE(salvager);
589         VENUMCASE(salvageServer);
590         VENUMCASE(debugUtility);
591         VENUMCASE(volumeServer);
592         VENUMCASE(volumeSalvager);
593     default:
594         return "**UNKNOWN**";
595     }
596 }
597
598 #undef VENUMCASE
599
600 #endif /* _AFS_VOL_VOLUME_INLINE_H */