Add opr/lock.h and tidy locking macros
[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, and
132  * we don't care.
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         /* assume we checked out the volume from the fileserver, so inUse
151          * should not be set */
152         return 1;
153     }
154     return 0;
155 }
156
157 #if defined(AFS_DEMAND_ATTACH_FS) || defined(AFS_DEMAND_ATTACH_UTIL)
158 /**
159  * acquire a non-blocking disk lock for a particular volume id.
160  *
161  * @param[in] volid the volume ID to lock
162  * @param[in] dp    the partition on which 'volid' resides
163  * @param[in] locktype READ_LOCK or WRITE_LOCK
164  *
165  * @return operation status
166  *  @retval 0 success, lock was obtained
167  *  @retval EBUSY another process holds a conflicting lock
168  *  @retval EIO   error acquiring lock
169  *
170  * @note Use VLockVolumeNB instead, if possible; only use this directly if
171  * you are not dealing with 'Volume*'s and attached volumes and such
172  *
173  * @pre There must not be any other threads acquiring locks on the same volid
174  * and partition; the locks will not work correctly if two threads try to
175  * acquire locks for the same volume
176  */
177 static_inline int
178 VLockVolumeByIdNB(VolumeId volid, struct DiskPartition64 *dp, int locktype)
179 {
180     return VLockFileLock(&dp->volLockFile, volid, locktype, 1 /* nonblock */);
181 }
182
183 /**
184  * release a lock acquired by VLockVolumeByIdNB.
185  *
186  * @param[in] volid the volume id to unlock
187  * @param[in] dp    the partition on which 'volid' resides
188  *
189  * @pre volid was previously locked by VLockVolumeByIdNB
190  */
191 static_inline void
192 VUnlockVolumeById(VolumeId volid, struct DiskPartition64 *dp)
193 {
194     VLockFileUnlock(&dp->volLockFile, volid);
195 }
196
197 /***************************************************/
198 /* demand attach fs state machine routines         */
199 /***************************************************/
200
201 /**
202  * tells caller whether we need to keep volumes locked for the entire time we
203  * are using them, or if we can unlock volumes as soon as they are attached.
204  *
205  * @return whether we can unlock attached volumes or not
206  *  @retval 1 yes, we can unlock attached volumes
207  *  @retval 0 no, do not unlock volumes until we unattach them
208  */
209 static_inline int
210 VCanUnlockAttached(void)
211 {
212     switch(programType) {
213     case fileServer:
214         return 1;
215     default:
216         return 0;
217     }
218 }
219
220 /**
221  * tells caller whether we need to lock a vol header with a write lock, a
222  * read lock, or if we do not need to lock it at all, when attaching.
223  *
224  * @param[in]  mode  volume attachment mode
225  * @param[in]  writable  1 if the volume is writable, 0 if not
226  *
227  * @return how we need to lock the vol header
228  *  @retval 0 do not lock the vol header at all
229  *  @retval READ_LOCK lock the vol header with a read lock
230  *  @retval WRITE_LOCK lock the vol header with a write lock
231  *
232  * @note DAFS only (non-DAFS uses partition locks)
233  */
234 static_inline int
235 VVolLockType(int mode, int writable)
236 {
237     switch (programType) {
238     case fileServer:
239         if (writable) {
240             return WRITE_LOCK;
241         }
242         return READ_LOCK;
243
244     case volumeSalvager:
245     case salvageServer:
246     case salvager:
247         return WRITE_LOCK;
248
249     default:
250         /* volserver, vol utilies, etc */
251
252         switch (mode) {
253         case V_READONLY:
254             return READ_LOCK;
255
256         case V_VOLUPD:
257         case V_SECRETLY:
258             return WRITE_LOCK;
259
260         case V_CLONE:
261         case V_DUMP:
262             if (writable) {
263                 return WRITE_LOCK;
264             }
265             return READ_LOCK;
266
267         case V_PEEK:
268             return 0;
269
270         default:
271             opr_Assert(0 /* unknown checkout mode */);
272             return 0;
273         }
274     }
275 }
276 #endif /* AFS_DEMAND_ATTACH_FS || AFS_DEMAND_ATTACH_UTIL */
277
278 #ifdef AFS_DEMAND_ATTACH_FS
279
280 /**
281  * tells caller whether or not the volume is effectively salvaging.
282  *
283  * @param vp  volume pointer
284  *
285  * @return whether volume is salvaging or not
286  *  @retval 0 no, volume is not salvaging
287  *  @retval 1 yes, volume is salvaging
288  *
289  * @note The volume may not actually be getting salvaged at the moment if
290  *       this returns 1, but may have just been requested or scheduled to be
291  *       salvaged. Callers should treat these cases as pretty much the same
292  *       anyway, since we should not touch a volume that is busy salvaging or
293  *       waiting to be salvaged.
294  */
295 static_inline int
296 VIsSalvaging(struct Volume *vp)
297 {
298     /* these tests are a bit redundant, but to be safe... */
299     switch(V_attachState(vp)) {
300     case VOL_STATE_SALVAGING:
301     case VOL_STATE_SALVAGE_REQ:
302         return 1;
303     default:
304         if (vp->salvage.requested || vp->salvage.scheduled) {
305             return 1;
306         }
307     }
308     return 0;
309 }
310
311 /**
312  * tells caller whether or not the current state requires
313  * exclusive access without holding glock.
314  *
315  * @param state  volume state enumeration
316  *
317  * @return whether volume state is a mutually exclusive state
318  *   @retval 0  no, state is re-entrant
319  *   @retval 1  yes, state is mutually exclusive
320  *
321  * @note DEMAND_ATTACH_FS only
322  */
323 static_inline int
324 VIsExclusiveState(VolState state)
325 {
326     switch (state) {
327     case VOL_STATE_UPDATING:
328     case VOL_STATE_ATTACHING:
329     case VOL_STATE_GET_BITMAP:
330     case VOL_STATE_HDR_LOADING:
331     case VOL_STATE_HDR_ATTACHING:
332     case VOL_STATE_OFFLINING:
333     case VOL_STATE_DETACHING:
334     case VOL_STATE_SALVSYNC_REQ:
335     case VOL_STATE_VNODE_ALLOC:
336     case VOL_STATE_VNODE_GET:
337     case VOL_STATE_VNODE_CLOSE:
338     case VOL_STATE_VNODE_RELEASE:
339     case VOL_STATE_VLRU_ADD:
340     case VOL_STATE_SCANNING_RXCALLS:
341         return 1;
342     default:
343         return 0;
344     }
345 }
346
347 /**
348  * tell caller whether V_attachState is an error condition.
349  *
350  * @param state  volume state enumeration
351  *
352  * @return whether volume state is in error state
353  *   @retval 0  state is not an error state
354  *   @retval 1  state is an error state
355  *
356  * @note DEMAND_ATTACH_FS only
357  */
358 static_inline int
359 VIsErrorState(VolState state)
360 {
361     switch (state) {
362     case VOL_STATE_ERROR:
363     case VOL_STATE_SALVAGING:
364     case VOL_STATE_SALVAGE_REQ:
365         return 1;
366     default:
367         return 0;
368     }
369 }
370
371 /**
372  * tell caller whether V_attachState is an offline condition.
373  *
374  * @param state  volume state enumeration
375  *
376  * @return whether volume state is in offline state
377  *   @retval 0  state is not an offline state
378  *   @retval 1  state is an offline state
379  *
380  * @note DEMAND_ATTACH_FS only
381  */
382 static_inline int
383 VIsOfflineState(VolState state)
384 {
385     switch (state) {
386     case VOL_STATE_UNATTACHED:
387     case VOL_STATE_ERROR:
388     case VOL_STATE_SALVAGING:
389     case VOL_STATE_DELETED:
390         return 1;
391     default:
392         return 0;
393     }
394 }
395
396 /**
397  * tell caller whether V_attachState is valid.
398  *
399  * @param state  volume state enumeration
400  *
401  * @return whether volume state is a mutually exclusive state
402  *   @retval 0  no, state is not valid
403  *   @retval 1  yes, state is a valid enumeration member
404  *
405  * @note DEMAND_ATTACH_FS only
406  *
407  * @note do we really want to treat VOL_STATE_FREED as valid?
408  */
409 static_inline int
410 VIsValidState(VolState state)
411 {
412     if ((state >= 0) &&
413         (state < VOL_STATE_COUNT)) {
414         return 1;
415     }
416     return 0;
417 }
418
419 /**
420  * increment volume-package internal refcount.
421  *
422  * @param vp  volume object pointer
423  *
424  * @internal volume package internal use only
425  *
426  * @pre VOL_LOCK must be held
427  *
428  * @post volume waiters refcount is incremented
429  *
430  * @see VCancelReservation_r
431  *
432  * @note DEMAND_ATTACH_FS only
433  */
434 static_inline void
435 VCreateReservation_r(Volume * vp)
436 {
437     vp->nWaiters++;
438 }
439
440 /**
441  * wait for the volume to change states.
442  *
443  * @param vp  volume object pointer
444  *
445  * @pre VOL_LOCK held; ref held on volume
446  *
447  * @post VOL_LOCK held; volume state has changed from previous value
448  *
449  * @note DEMAND_ATTACH_FS only
450  */
451 static_inline void
452 VWaitStateChange_r(Volume * vp)
453 {
454     VolState state_save = V_attachState(vp);
455
456     opr_Assert(vp->nWaiters || vp->nUsers);
457     do {
458         VOL_CV_WAIT(&V_attachCV(vp));
459     } while (V_attachState(vp) == state_save);
460     opr_Assert(V_attachState(vp) != VOL_STATE_FREED);
461 }
462
463 /**
464  * wait for the volume to change states within a certain amount of time
465  *
466  * @param[in] vp  volume object pointer
467  * @param[in] ts  deadline (absolute time) or NULL to wait forever
468  *
469  * @pre VOL_LOCK held; ref held on volume
470  * @post VOL_LOCK held; volume state has changed and/or it is after the time
471  *       specified in ts
472  *
473  * @note DEMAND_ATTACH_FS only
474  * @note if ts is NULL, this is identical to VWaitStateChange_r
475  */
476 static_inline void
477 VTimedWaitStateChange_r(Volume * vp, const struct timespec *ts, int *atimedout)
478 {
479     VolState state_save;
480     int timeout;
481
482     if (atimedout) {
483         *atimedout = 0;
484     }
485
486     if (!ts) {
487         VWaitStateChange_r(vp);
488         return;
489     }
490
491     state_save = V_attachState(vp);
492
493     assert(vp->nWaiters || vp->nUsers);
494     do {
495         VOL_CV_TIMEDWAIT(&V_attachCV(vp), ts, &timeout);
496     } while (V_attachState(vp) == state_save && !timeout);
497     assert(V_attachState(vp) != VOL_STATE_FREED);
498
499     if (atimedout && timeout) {
500         *atimedout = 1;
501     }
502 }
503
504 /**
505  * wait for blocking ops to end.
506  *
507  * @pre VOL_LOCK held; ref held on volume
508  *
509  * @post VOL_LOCK held; volume not in exclusive state
510  *
511  * @param vp  volume object pointer
512  *
513  * @note DEMAND_ATTACH_FS only
514  */
515 static_inline void
516 VWaitExclusiveState_r(Volume * vp)
517 {
518     opr_Assert(vp->nWaiters || vp->nUsers);
519     while (VIsExclusiveState(V_attachState(vp))) {
520         VOL_CV_WAIT(&V_attachCV(vp));
521     }
522     opr_Assert(V_attachState(vp) != VOL_STATE_FREED);
523 }
524
525 /**
526  * change state, and notify other threads,
527  * return previous state to caller.
528  *
529  * @param vp         pointer to volume object
530  * @param new_state  new volume state value
531  * @pre VOL_LOCK held
532  *
533  * @post volume state changed; stats updated
534  *
535  * @return previous volume state
536  *
537  * @note DEMAND_ATTACH_FS only
538  */
539 static_inline VolState
540 VChangeState_r(Volume * vp, VolState new_state)
541 {
542     VolState old_state = V_attachState(vp);
543
544     /* XXX profiling need to make sure these counters
545      * don't kill performance... */
546     VStats.state_levels[old_state]--;
547     VStats.state_levels[new_state]++;
548
549     V_attachState(vp) = new_state;
550     opr_cv_broadcast(&V_attachCV(vp));
551     return old_state;
552 }
553
554 #endif /* AFS_DEMAND_ATTACH_FS */
555
556 #define VENUMCASE(en) \
557     case en: return #en
558
559 /**
560  * translate a ProgramType code to a string.
561  *
562  * @param[in] type ProgramType numeric code
563  *
564  * @return a human-readable string for that program type
565  *  @retval "**UNKNOWN**" an unknown ProgramType was given
566  */
567 static_inline char *
568 VPTypeToString(ProgramType type)
569 {
570     switch (type) {
571         VENUMCASE(fileServer);
572         VENUMCASE(volumeUtility);
573         VENUMCASE(salvager);
574         VENUMCASE(salvageServer);
575         VENUMCASE(debugUtility);
576         VENUMCASE(volumeServer);
577         VENUMCASE(volumeSalvager);
578     default:
579         return "**UNKNOWN**";
580     }
581 }
582
583 #undef VENUMCASE
584
585 #endif /* _AFS_VOL_VOLUME_INLINE_H */