Commit | Line | Data |
---|---|---|
805e021f CE |
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 | #ifdef AFS_DEMAND_ATTACH_FS | |
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 | #ifdef AFS_DEMAND_ATTACH_FS | |
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 */ | |
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 | #ifdef AFS_DEMAND_ATTACH_FS | |
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 | ||
292 | /** | |
293 | * tells caller whether or not the volume is effectively salvaging. | |
294 | * | |
295 | * @param vp volume pointer | |
296 | * | |
297 | * @return whether volume is salvaging or not | |
298 | * @retval 0 no, volume is not salvaging | |
299 | * @retval 1 yes, volume is salvaging | |
300 | * | |
301 | * @note The volume may not actually be getting salvaged at the moment if | |
302 | * this returns 1, but may have just been requested or scheduled to be | |
303 | * salvaged. Callers should treat these cases as pretty much the same | |
304 | * anyway, since we should not touch a volume that is busy salvaging or | |
305 | * waiting to be salvaged. | |
306 | */ | |
307 | static_inline int | |
308 | VIsSalvaging(struct Volume *vp) | |
309 | { | |
310 | /* these tests are a bit redundant, but to be safe... */ | |
311 | switch(V_attachState(vp)) { | |
312 | case VOL_STATE_SALVAGING: | |
313 | case VOL_STATE_SALVAGE_REQ: | |
314 | return 1; | |
315 | default: | |
316 | if (vp->salvage.requested || vp->salvage.scheduled) { | |
317 | return 1; | |
318 | } | |
319 | } | |
320 | return 0; | |
321 | } | |
322 | ||
323 | /** | |
324 | * tells caller whether or not the current state requires | |
325 | * exclusive access without holding glock. | |
326 | * | |
327 | * @param state volume state enumeration | |
328 | * | |
329 | * @return whether volume state is a mutually exclusive state | |
330 | * @retval 0 no, state is re-entrant | |
331 | * @retval 1 yes, state is mutually exclusive | |
332 | * | |
333 | * @note DEMAND_ATTACH_FS only | |
334 | */ | |
335 | static_inline int | |
336 | VIsExclusiveState(VolState state) | |
337 | { | |
338 | switch (state) { | |
339 | case VOL_STATE_UPDATING: | |
340 | case VOL_STATE_ATTACHING: | |
341 | case VOL_STATE_GET_BITMAP: | |
342 | case VOL_STATE_HDR_LOADING: | |
343 | case VOL_STATE_HDR_ATTACHING: | |
344 | case VOL_STATE_OFFLINING: | |
345 | case VOL_STATE_DETACHING: | |
346 | case VOL_STATE_SALVSYNC_REQ: | |
347 | case VOL_STATE_VNODE_ALLOC: | |
348 | case VOL_STATE_VNODE_GET: | |
349 | case VOL_STATE_VNODE_CLOSE: | |
350 | case VOL_STATE_VNODE_RELEASE: | |
351 | case VOL_STATE_VLRU_ADD: | |
352 | case VOL_STATE_SCANNING_RXCALLS: | |
353 | return 1; | |
354 | default: | |
355 | return 0; | |
356 | } | |
357 | } | |
358 | ||
359 | /** | |
360 | * tell caller whether V_attachState is an error condition. | |
361 | * | |
362 | * @param state volume state enumeration | |
363 | * | |
364 | * @return whether volume state is in error state | |
365 | * @retval 0 state is not an error state | |
366 | * @retval 1 state is an error state | |
367 | * | |
368 | * @note DEMAND_ATTACH_FS only | |
369 | */ | |
370 | static_inline int | |
371 | VIsErrorState(VolState state) | |
372 | { | |
373 | switch (state) { | |
374 | case VOL_STATE_ERROR: | |
375 | case VOL_STATE_SALVAGING: | |
376 | case VOL_STATE_SALVAGE_REQ: | |
377 | return 1; | |
378 | default: | |
379 | return 0; | |
380 | } | |
381 | } | |
382 | ||
383 | /** | |
384 | * tell caller whether V_attachState is an offline condition. | |
385 | * | |
386 | * @param state volume state enumeration | |
387 | * | |
388 | * @return whether volume state is in offline state | |
389 | * @retval 0 state is not an offline state | |
390 | * @retval 1 state is an offline state | |
391 | * | |
392 | * @note DEMAND_ATTACH_FS only | |
393 | */ | |
394 | static_inline int | |
395 | VIsOfflineState(VolState state) | |
396 | { | |
397 | switch (state) { | |
398 | case VOL_STATE_UNATTACHED: | |
399 | case VOL_STATE_ERROR: | |
400 | case VOL_STATE_SALVAGING: | |
401 | case VOL_STATE_DELETED: | |
402 | return 1; | |
403 | default: | |
404 | return 0; | |
405 | } | |
406 | } | |
407 | ||
408 | /** | |
409 | * tell caller whether V_attachState is valid. | |
410 | * | |
411 | * @param state volume state enumeration | |
412 | * | |
413 | * @return whether volume state is a mutually exclusive state | |
414 | * @retval 0 no, state is not valid | |
415 | * @retval 1 yes, state is a valid enumeration member | |
416 | * | |
417 | * @note DEMAND_ATTACH_FS only | |
418 | * | |
419 | * @note do we really want to treat VOL_STATE_FREED as valid? | |
420 | */ | |
421 | static_inline int | |
422 | VIsValidState(VolState state) | |
423 | { | |
424 | if (((int) state >= 0) && | |
425 | (state < VOL_STATE_COUNT)) { | |
426 | return 1; | |
427 | } | |
428 | return 0; | |
429 | } | |
430 | ||
431 | /** | |
432 | * increment volume-package internal refcount. | |
433 | * | |
434 | * @param vp volume object pointer | |
435 | * | |
436 | * @internal volume package internal use only | |
437 | * | |
438 | * @pre VOL_LOCK must be held | |
439 | * | |
440 | * @post volume waiters refcount is incremented | |
441 | * | |
442 | * @see VCancelReservation_r | |
443 | * | |
444 | * @note DEMAND_ATTACH_FS only | |
445 | */ | |
446 | static_inline void | |
447 | VCreateReservation_r(Volume * vp) | |
448 | { | |
449 | vp->nWaiters++; | |
450 | } | |
451 | ||
452 | /** | |
453 | * wait for the volume to change states. | |
454 | * | |
455 | * @param vp volume object pointer | |
456 | * | |
457 | * @pre VOL_LOCK held; ref held on volume | |
458 | * | |
459 | * @post VOL_LOCK held; volume state has changed from previous value | |
460 | * | |
461 | * @note DEMAND_ATTACH_FS only | |
462 | */ | |
463 | static_inline void | |
464 | VWaitStateChange_r(Volume * vp) | |
465 | { | |
466 | VolState state_save = V_attachState(vp); | |
467 | ||
468 | opr_Assert(vp->nWaiters || vp->nUsers); | |
469 | do { | |
470 | VOL_CV_WAIT(&V_attachCV(vp)); | |
471 | } while (V_attachState(vp) == state_save); | |
472 | opr_Assert(V_attachState(vp) != VOL_STATE_FREED); | |
473 | } | |
474 | ||
475 | /** | |
476 | * wait for the volume to change states within a certain amount of time | |
477 | * | |
478 | * @param[in] vp volume object pointer | |
479 | * @param[in] ts deadline (absolute time) or NULL to wait forever | |
480 | * | |
481 | * @pre VOL_LOCK held; ref held on volume | |
482 | * @post VOL_LOCK held; volume state has changed and/or it is after the time | |
483 | * specified in ts | |
484 | * | |
485 | * @note DEMAND_ATTACH_FS only | |
486 | * @note if ts is NULL, this is identical to VWaitStateChange_r | |
487 | */ | |
488 | static_inline void | |
489 | VTimedWaitStateChange_r(Volume * vp, const struct timespec *ts, int *atimedout) | |
490 | { | |
491 | VolState state_save; | |
492 | int timeout; | |
493 | ||
494 | if (atimedout) { | |
495 | *atimedout = 0; | |
496 | } | |
497 | ||
498 | if (!ts) { | |
499 | VWaitStateChange_r(vp); | |
500 | return; | |
501 | } | |
502 | ||
503 | state_save = V_attachState(vp); | |
504 | ||
505 | opr_Assert(vp->nWaiters || vp->nUsers); | |
506 | do { | |
507 | VOL_CV_TIMEDWAIT(&V_attachCV(vp), ts, &timeout); | |
508 | } while (V_attachState(vp) == state_save && !timeout); | |
509 | opr_Assert(V_attachState(vp) != VOL_STATE_FREED); | |
510 | ||
511 | if (atimedout && timeout) { | |
512 | *atimedout = 1; | |
513 | } | |
514 | } | |
515 | ||
516 | /** | |
517 | * wait for blocking ops to end. | |
518 | * | |
519 | * @pre VOL_LOCK held; ref held on volume | |
520 | * | |
521 | * @post VOL_LOCK held; volume not in exclusive state | |
522 | * | |
523 | * @param vp volume object pointer | |
524 | * | |
525 | * @note DEMAND_ATTACH_FS only | |
526 | */ | |
527 | static_inline void | |
528 | VWaitExclusiveState_r(Volume * vp) | |
529 | { | |
530 | opr_Assert(vp->nWaiters || vp->nUsers); | |
531 | while (VIsExclusiveState(V_attachState(vp))) { | |
532 | VOL_CV_WAIT(&V_attachCV(vp)); | |
533 | } | |
534 | opr_Assert(V_attachState(vp) != VOL_STATE_FREED); | |
535 | } | |
536 | ||
537 | /** | |
538 | * change state, and notify other threads, | |
539 | * return previous state to caller. | |
540 | * | |
541 | * @param vp pointer to volume object | |
542 | * @param new_state new volume state value | |
543 | * @pre VOL_LOCK held | |
544 | * | |
545 | * @post volume state changed; stats updated | |
546 | * | |
547 | * @return previous volume state | |
548 | * | |
549 | * @note DEMAND_ATTACH_FS only | |
550 | */ | |
551 | static_inline VolState | |
552 | VChangeState_r(Volume * vp, VolState new_state) | |
553 | { | |
554 | VolState old_state = V_attachState(vp); | |
555 | ||
556 | /* XXX profiling need to make sure these counters | |
557 | * don't kill performance... */ | |
558 | VStats.state_levels[old_state]--; | |
559 | VStats.state_levels[new_state]++; | |
560 | ||
561 | V_attachState(vp) = new_state; | |
562 | opr_cv_broadcast(&V_attachCV(vp)); | |
563 | return old_state; | |
564 | } | |
565 | ||
566 | #endif /* AFS_DEMAND_ATTACH_FS */ | |
567 | ||
568 | #define VENUMCASE(en) \ | |
569 | case en: return #en | |
570 | ||
571 | /** | |
572 | * translate a ProgramType code to a string. | |
573 | * | |
574 | * @param[in] type ProgramType numeric code | |
575 | * | |
576 | * @return a human-readable string for that program type | |
577 | * @retval "**UNKNOWN**" an unknown ProgramType was given | |
578 | */ | |
579 | static_inline char * | |
580 | VPTypeToString(ProgramType type) | |
581 | { | |
582 | switch (type) { | |
583 | VENUMCASE(fileServer); | |
584 | VENUMCASE(volumeUtility); | |
585 | VENUMCASE(salvager); | |
586 | VENUMCASE(salvageServer); | |
587 | VENUMCASE(debugUtility); | |
588 | VENUMCASE(volumeServer); | |
589 | VENUMCASE(volumeSalvager); | |
590 | default: | |
591 | return "**UNKNOWN**"; | |
592 | } | |
593 | } | |
594 | ||
595 | #undef VENUMCASE | |
596 | ||
597 | #endif /* _AFS_VOL_VOLUME_INLINE_H */ |