Import Upstream version 1.8.5
[hcoop/debian/openafs.git] / src / vol / vg_cache.c
1 /*
2 * Copyright 2009-2010, 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 /*
11 * demand attach fs
12 * volume group membership cache
13 */
14
15 #include <afsconfig.h>
16 #include <afs/param.h>
17
18 #include <roken.h>
19
20 #ifdef HAVE_SYS_FILE_H
21 #include <sys/file.h>
22 #endif
23
24 #ifdef AFS_DEMAND_ATTACH_FS
25
26 #include <afs/opr.h>
27 #include <rx/rx_queue.h>
28 #include <opr/lock.h>
29 #include <lock.h>
30 #include <afs/afsutil.h>
31 #include "nfs.h"
32 #include <afs/afsint.h>
33 #include "ihandle.h"
34 #include "vnode.h"
35 #include "volume.h"
36 #include "viceinode.h"
37 #include "voldefs.h"
38 #include "partition.h"
39 #include <afs/errors.h>
40
41 #define __VOL_VG_CACHE_IMPL 1
42
43 #include "vg_cache.h"
44 #include "vg_cache_impl.h"
45
46 static int _VVGC_lookup(struct DiskPartition64 *,
47 VolumeId volid,
48 VVGCache_entry_t ** entry,
49 VVGCache_hash_entry_t ** hentry);
50 static int _VVGC_entry_alloc(VVGCache_entry_t ** entry);
51 static int _VVGC_entry_free(VVGCache_entry_t * entry);
52 static int _VVGC_entry_get(VVGCache_entry_t * entry);
53 static int _VVGC_entry_put(struct DiskPartition64 *,
54 VVGCache_entry_t * entry);
55 static int _VVGC_entry_add(struct DiskPartition64 *,
56 VolumeId volid,
57 VVGCache_entry_t **,
58 VVGCache_hash_entry_t **);
59 static int _VVGC_entry_cl_add(VVGCache_entry_t *, VolumeId);
60 static int _VVGC_entry_cl_del(struct DiskPartition64 *, VVGCache_entry_t *,
61 VolumeId);
62 static int _VVGC_entry_export(VVGCache_entry_t *, VVGCache_query_t *);
63 static int _VVGC_hash_entry_alloc(VVGCache_hash_entry_t ** entry);
64 static int _VVGC_hash_entry_free(VVGCache_hash_entry_t * entry);
65 static int _VVGC_hash_entry_add(struct DiskPartition64 *,
66 VolumeId,
67 VVGCache_entry_t *,
68 VVGCache_hash_entry_t **);
69 static int _VVGC_hash_entry_del(VVGCache_hash_entry_t * entry);
70 static int _VVGC_hash_entry_unlink(VVGCache_hash_entry_t * entry);
71
72 VVGCache_hash_table_t VVGCache_hash_table;
73 VVGCache_t VVGCache;
74
75 /**
76 * initialize volume group cache subsystem.
77 *
78 * @return operation status
79 * @retval 0 success
80 */
81 int
82 VVGCache_PkgInit(void)
83 {
84 int code = 0;
85 int i;
86
87 /* allocate hash table */
88 VVGCache_hash_table.hash_buckets =
89 malloc(VolumeHashTable.Size * sizeof(struct rx_queue));
90 if (VVGCache_hash_table.hash_buckets == NULL) {
91 code = ENOMEM;
92 goto error;
93 }
94
95 /* setup hash chain heads */
96 for (i = 0; i < VolumeHashTable.Size; i++) {
97 queue_Init(&VVGCache_hash_table.hash_buckets[i]);
98 }
99
100 /* initialize per-partition VVGC state */
101 for (i = 0; i <= VOLMAXPARTS; i++) {
102 VVGCache.part[i].state = VVGC_PART_STATE_INVALID;
103 VVGCache.part[i].dlist_hash_buckets = NULL;
104 CV_INIT(&VVGCache.part[i].cv, "cache part", CV_DEFAULT, 0);
105 if (code) {
106 goto error;
107 }
108 }
109
110 error:
111 return code;
112 }
113
114 /**
115 * shut down volume group cache subsystem.
116 *
117 * @return operation status
118 * @retval 0 success
119 *
120 * @todo implement
121 */
122 int
123 VVGCache_PkgShutdown(void)
124 {
125 int i;
126
127 /* fix it later */
128
129 /* free hash table */
130 free(VVGCache_hash_table.hash_buckets);
131 VVGCache_hash_table.hash_buckets = NULL;
132
133 /* destroy per-partition VVGC state */
134 for (i = 0; i <= VOLMAXPARTS; i++) {
135 VVGCache.part[i].state = VVGC_PART_STATE_INVALID;
136 CV_DESTROY(&VVGCache.part[i].cv);
137 }
138
139 return EOPNOTSUPP;
140 }
141
142 /**
143 * allocate a cache entry.
144 *
145 * @param[out] entry_out pointer to newly allocated entry
146 *
147 * @return operation status
148 * @retval 0 success
149 *
150 * @internal
151 */
152 static int
153 _VVGC_entry_alloc(VVGCache_entry_t ** entry_out)
154 {
155 *entry_out = calloc(1, sizeof(VVGCache_entry_t));
156
157 if (*entry_out == NULL)
158 return ENOMEM;
159
160 return 0;
161 }
162
163 /**
164 * free a cache entry.
165 *
166 * @param[in] entry cache entry
167 *
168 * @return operation status
169 * @retval 0 success
170 *
171 * @internal
172 */
173 static int
174 _VVGC_entry_free(VVGCache_entry_t * entry)
175 {
176 int code = 0;
177
178 opr_Assert(entry->refcnt == 0);
179 free(entry);
180
181 return code;
182 }
183
184 /**
185 * allocate and register an entry for a volume group.
186 *
187 * @param[in] dp disk partition object
188 * @param[in] volid volume id
189 * @param[out] entry_out vg cache object pointer
190 * @param[out] hash_out vg cache hash entry object pointer
191 *
192 * @pre - VOL_LOCK held
193 * - no such entry exists in hash table
194 *
195 * @return operation status
196 * @retval 0 success
197 *
198 * @internal
199 */
200 static int
201 _VVGC_entry_add(struct DiskPartition64 * dp,
202 VolumeId volid,
203 VVGCache_entry_t ** entry_out,
204 VVGCache_hash_entry_t ** hash_out)
205 {
206 int code = 0;
207 VVGCache_entry_t * ent;
208
209 code = _VVGC_entry_alloc(&ent);
210 if (code) {
211 goto error;
212 }
213
214 ent->rw = volid;
215 /* refcnt will be inc'd when a child is added */
216 ent->refcnt = 0;
217
218 code = _VVGC_hash_entry_add(dp, volid, ent, hash_out);
219 if (code) {
220 goto error;
221 }
222
223 if (entry_out) {
224 *entry_out = ent;
225 }
226 return code;
227
228 error:
229 if (ent) {
230 _VVGC_entry_free(ent);
231 ent = NULL;
232 }
233 return code;
234 }
235
236 /**
237 * add a volid to the entry's child list.
238 *
239 * @param[in] ent volume group object
240 * @param[in] volid volume id
241 *
242 * @return operation status
243 * @retval 0 success
244 * @retval -1 child table is full
245 *
246 * @internal
247 */
248 static int
249 _VVGC_entry_cl_add(VVGCache_entry_t * ent,
250 VolumeId volid)
251 {
252 int code = 0, i;
253 int empty_idx = -1;
254
255 /* search table to avoid duplicates */
256 for (i = 0; i < VOL_VG_MAX_VOLS; i++) {
257 if (ent->children[i] == volid) {
258 ViceLog(1, ("VVGC_entry_cl_add: tried to add duplicate vol "
259 "%lu to VG %lu\n",
260 afs_printable_uint32_lu(volid),
261 afs_printable_uint32_lu(ent->rw)));
262 goto done;
263 }
264 if (empty_idx == -1 && !ent->children[i]) {
265 empty_idx = i;
266 /* don't break; make sure we go through all children so we don't
267 * add a duplicate entry */
268 }
269 }
270
271 /* verify table isn't full */
272 if (empty_idx == -1) {
273 code = -1;
274 ViceLog(0, ("VVGC_entry_cl_add: tried to add vol %lu to VG %lu, but VG "
275 "is full\n", afs_printable_uint32_lu(volid),
276 afs_printable_uint32_lu(ent->rw)));
277 goto done;
278 }
279
280 /* add entry */
281 ent->children[empty_idx] = volid;
282
283 /* inc refcount */
284 code = _VVGC_entry_get(ent);
285
286 done:
287 return code;
288 }
289
290 /**
291 * delete a volid from the entry's child list.
292 *
293 * @param[in] dp disk partition object
294 * @param[in] ent volume group object
295 * @param[in] volid volume id
296 *
297 * @return operation status
298 * @retval 0 success
299 * @retval -1 no such entry found
300 *
301 * @internal
302 */
303 static int
304 _VVGC_entry_cl_del(struct DiskPartition64 *dp,
305 VVGCache_entry_t * ent,
306 VolumeId volid)
307 {
308 int code = -1, i;
309
310 for (i = 0; i < VOL_VG_MAX_VOLS; i++) {
311 if (ent->children[i] == volid) {
312 ent->children[i] = 0;
313 code = 0;
314 goto done;
315 }
316 }
317
318 done:
319 if (!code) {
320 code = _VVGC_entry_put(dp, ent);
321 }
322
323 return code;
324 }
325
326 /**
327 * add a refcount to an entry.
328 *
329 * @param[in] entry cache entry
330 *
331 * @pre VOL_LOCK held
332 *
333 * @return operation status
334 * @retval 0 success
335 *
336 * @internal
337 */
338 static int _VVGC_entry_get(VVGCache_entry_t * entry)
339 {
340 entry->refcnt++;
341 return 0;
342 }
343
344 /**
345 * put back a reference to an entry.
346 *
347 * @param[in] dp disk partition object
348 * @param[in] entry cache entry
349 *
350 * @pre VOL_LOCK held
351 *
352 * @warning do not attempt to deref pointer after calling this interface
353 *
354 * @return operation status
355 * @retval 0 success
356 *
357 * @note dp is needed to lookup the RW hash entry to unlink, if we are
358 * putting back the final reference and freeing
359 *
360 * @internal
361 */
362 static int
363 _VVGC_entry_put(struct DiskPartition64 * dp, VVGCache_entry_t * entry)
364 {
365 int code = 0;
366
367 opr_Assert(entry->refcnt > 0);
368
369 if (--entry->refcnt == 0) {
370 VVGCache_entry_t *nentry;
371 VVGCache_hash_entry_t *hentry;
372
373 /* first, try to delete the RW id hash entry pointing to this
374 * entry */
375 code = _VVGC_lookup(dp, entry->rw, &nentry, &hentry);
376 if (!code) {
377 if (nentry != entry) {
378 /* looking up the rw of this entry points to a different
379 * entry; should not happen */
380 ViceLog(0, ("VVGC_entry_put: error: entry lookup for entry %lu "
381 "found different entry than was passed",
382 afs_printable_uint32_lu(entry->rw)));
383 code = -1;
384 } else {
385 code = _VVGC_hash_entry_unlink(hentry);
386 hentry = NULL;
387 }
388 } else if (code == ENOENT) {
389 /* ignore ENOENT; this shouldn't happen, since the RW hash
390 * entry should always exist if the entry does... but we
391 * were going to delete it anyway, so try to continue */
392 ViceLog(0, ("VVGC_entry_put: warning: tried to unlink entry for "
393 "vol %lu, but RW hash entry doesn't exist; continuing "
394 "anyway...\n", afs_printable_uint32_lu(entry->rw)));
395
396 code = 0;
397 }
398
399 /* now, just free the entry itself */
400 if (!code) {
401 code = _VVGC_entry_free(entry);
402 }
403 }
404
405 return code;
406 }
407
408 /**
409 * export a volume group entry in the external object format.
410 *
411 * @param[in] ent internal-format volume group object
412 * @param[out] qry external-format volume group object
413 *
414 * @pre VOL_LOCK held
415 *
416 * @return operation status
417 * @retval 0 success
418 *
419 * @internal
420 */
421 static int
422 _VVGC_entry_export(VVGCache_entry_t * ent, VVGCache_query_t * qry)
423 {
424 int i;
425
426 qry->rw = ent->rw;
427 for (i = 0; i < VOL_VG_MAX_VOLS; i++) {
428 qry->children[i] = ent->children[i];
429 }
430
431 return 0;
432 }
433
434 /**
435 * allocate a hash table entry structure.
436 *
437 * @param[out] entry_out address in which to store newly allocated hash entry struct
438 *
439 * @return operation status
440 * @retval 0 success
441 *
442 * @internal
443 */
444 static int
445 _VVGC_hash_entry_alloc(VVGCache_hash_entry_t ** entry_out)
446 {
447 int code = 0;
448 VVGCache_hash_entry_t * ent;
449
450 *entry_out = ent = malloc(sizeof(VVGCache_hash_entry_t));
451 if (ent == NULL) {
452 code = ENOMEM;
453 }
454
455 return code;
456 }
457
458 /**
459 * free a hash table entry structure.
460 *
461 * @param[in] entry hash table entry structure to be freed
462 *
463 * @return operation status
464 * @retval 0 success
465 *
466 * @internal
467 */
468 static int
469 _VVGC_hash_entry_free(VVGCache_hash_entry_t * entry)
470 {
471 int code = 0;
472
473 free(entry);
474
475 return code;
476 }
477
478 /**
479 * add an entry to the hash table.
480 *
481 * @param[in] dp disk partition object
482 * @param[in] volid volume id
483 * @param[in] ent volume group object
484 * @param[out] hash_out address in which to store pointer to hash entry
485 *
486 * @pre VOL_LOCK held
487 *
488 * @return operation status
489 * @retval 0 success
490 * @retval EEXIST hash entry for volid already exists, and it points to
491 * a different VG entry
492 *
493 * @internal
494 */
495 static int
496 _VVGC_hash_entry_add(struct DiskPartition64 * dp,
497 VolumeId volid,
498 VVGCache_entry_t * ent,
499 VVGCache_hash_entry_t ** hash_out)
500 {
501 int code = 0;
502 VVGCache_hash_entry_t * hent;
503 int hash = VVGC_HASH(volid);
504 VVGCache_entry_t *nent;
505
506 code = _VVGC_lookup(dp, volid, &nent, hash_out);
507 if (!code) {
508 if (ent != nent) {
509 ViceLog(0, ("_VVGC_hash_entry_add: tried to add a duplicate "
510 " nonmatching entry for vol %lu: original "
511 "(%"AFS_PTR_FMT",%lu) new (%"AFS_PTR_FMT",%lu)\n",
512 afs_printable_uint32_lu(volid),
513 nent, afs_printable_uint32_lu(nent->rw),
514 ent, afs_printable_uint32_lu(ent->rw)));
515 return EEXIST;
516 }
517 ViceLog(1, ("_VVGC_hash_entry_add: tried to add duplicate "
518 "hash entry for vol %lu, VG %lu",
519 afs_printable_uint32_lu(volid),
520 afs_printable_uint32_lu(ent->rw)));
521 /* accept attempts to add matching duplicate entries; just
522 * pretend we added it */
523 return 0;
524 }
525
526 code = _VVGC_hash_entry_alloc(&hent);
527 if (code) {
528 goto done;
529 }
530
531 hent->entry = ent;
532 hent->dp = dp;
533 hent->volid = volid;
534 queue_Append(&VVGCache_hash_table.hash_buckets[hash],
535 hent);
536
537 done:
538 if (hash_out) {
539 *hash_out = hent;
540 }
541 return code;
542 }
543
544 /**
545 * remove an entry from the hash table.
546 *
547 * @param[in] hent hash table entry
548 *
549 * @pre VOL_LOCK held
550 *
551 * @return operation status
552 * @retval 0 success
553 *
554 * @internal
555 */
556 static int
557 _VVGC_hash_entry_del(VVGCache_hash_entry_t * hent)
558 {
559 int code = 0, res;
560 int rw = 0;
561
562 if (hent->entry->rw == hent->volid) {
563 rw = 1;
564 }
565
566 code = _VVGC_entry_cl_del(hent->dp, hent->entry, hent->volid);
567 /* note: hent->entry is possibly NULL after _VVGC_entry_cl_del, and
568 * if hent->entry->rw == hent->volid, it is possible for hent to
569 * have been freed */
570
571 if (!rw) {
572 /* If we are the RW id, don't unlink, since we still need the
573 * hash entry to exist, so when we lookup children, they can
574 * look up the RW id hash chain, and they will all go to the
575 * same object.
576 *
577 * If we are the last entry and the entry should be deleted,
578 * _VVGC_entry_cl_del will take care of unlinking the RW hash entry.
579 */
580 res = _VVGC_hash_entry_unlink(hent);
581 if (res) {
582 code = res;
583 }
584 }
585
586 return code;
587 }
588
589 /**
590 * low-level interface to remove an entry from the hash table.
591 *
592 * Does not alter the refcount or worry about the children lists or
593 * anything like that; just removes the hash table entry, frees it, and
594 * that's all. You probably want @see _VVGC_hash_entry_del instead.
595 *
596 * @param[in] hent hash table entry
597 *
598 * @pre VOL_LOCK held
599 *
600 * @return operation status
601 * @retval 0 success
602 *
603 * @internal
604 */
605 static int
606 _VVGC_hash_entry_unlink(VVGCache_hash_entry_t * hent)
607 {
608 int code;
609
610 queue_Remove(hent);
611 hent->entry = NULL;
612 hent->volid = 0;
613 code = _VVGC_hash_entry_free(hent);
614
615 return code;
616 }
617
618 /**
619 * lookup a vg cache entry given any member volume id.
620 *
621 * @param[in] dp disk partition object
622 * @param[in] volid vg member volume id
623 * @param[out] entry_out address in which to store volume group entry structure pointer
624 * @param[out] hash_out address in which to store hash entry pointer
625 *
626 * @pre VOL_LOCK held
627 *
628 * @warning - it is up to the caller to get a ref to entry_out, if needed
629 * - hash_out must not be referenced after dropping VOL_LOCK
630 *
631 * @return operation status
632 * @retval 0 success
633 * @retval ENOENT volume id not found
634 * @retval EINVAL partition's VGC is invalid
635 *
636 * @internal
637 */
638 static int
639 _VVGC_lookup(struct DiskPartition64 * dp,
640 VolumeId volid,
641 VVGCache_entry_t ** entry_out,
642 VVGCache_hash_entry_t ** hash_out)
643 {
644 int code = ENOENT;
645 int bucket = VVGC_HASH(volid);
646 struct VVGCache_hash_entry * ent, * nent;
647
648 if (VVGCache.part[dp->index].state == VVGC_PART_STATE_INVALID) {
649 return EINVAL;
650 }
651
652 *entry_out = NULL;
653
654 for (queue_Scan(&VVGCache_hash_table.hash_buckets[bucket],
655 ent,
656 nent,
657 VVGCache_hash_entry)) {
658 if (ent->volid == volid && ent->dp == dp) {
659 code = 0;
660 *entry_out = ent->entry;
661 if (hash_out) {
662 *hash_out = ent;
663 }
664 break;
665 }
666 }
667
668 return code;
669 }
670
671 /**
672 * add an entry to the volume group cache.
673 *
674 * @param[in] dp disk partition object
675 * @param[in] parent parent volume id
676 * @param[in] child child volume id
677 * @param[out] newvg if non-NULL, *newvg is 1 if adding this added a
678 * new VG, 0 if we added to an existing VG
679 *
680 * @pre VOL_LOCK held
681 *
682 * @return operation status
683 * @retval 0 success
684 * @retval -1 parent and child are already registered in
685 * different VGs
686 */
687 int
688 VVGCache_entry_add_r(struct DiskPartition64 * dp,
689 VolumeId parent,
690 VolumeId child,
691 afs_int32 *newvg)
692 {
693 int code = 0, res;
694 VVGCache_entry_t * child_ent, * parent_ent;
695
696 if (newvg) {
697 *newvg = 0;
698 }
699
700 /* check for existing entries */
701 res = _VVGC_lookup(dp, child, &child_ent, NULL);
702 if (res && res != ENOENT) {
703 code = res;
704 goto done;
705 }
706
707 res = _VVGC_lookup(dp, parent, &parent_ent, NULL);
708 if (res && res != ENOENT) {
709 code = res;
710 goto done;
711 }
712
713 /*
714 * branch based upon existence of parent and child nodes
715 */
716 if (parent_ent && child_ent) {
717 /* both exist. we're done.
718 * if they point different places, then report the error. */
719 if (child_ent != parent_ent) {
720 code = -1;
721 }
722 if (parent == child) {
723 /* if we're adding the RW entry as a child, the RW id may
724 * not be in the child array yet, so make sure not to skip
725 * over that */
726 goto cladd;
727 }
728 goto done;
729 } else if (!parent_ent && child_ent) {
730 /* child exists.
731 * update vg root volid, and add hash entry. */
732 parent_ent = child_ent;
733 parent_ent->rw = parent;
734
735 code = _VVGC_hash_entry_add(dp,
736 parent,
737 parent_ent,
738 NULL);
739 goto done;
740 } else if (!child_ent && !parent_ent) {
741 code = _VVGC_entry_add(dp,
742 parent,
743 &parent_ent,
744 NULL);
745 if (code) {
746 goto done;
747 }
748 if (newvg) {
749 *newvg = 1;
750 }
751 if (child == parent) {
752 /* if we're the RW, skip over adding the child hash entry;
753 * we already added the hash entry when creating the entry */
754 child_ent = parent_ent;
755 goto cladd;
756 }
757 }
758
759 opr_Assert(!child_ent);
760 child_ent = parent_ent;
761 code = _VVGC_hash_entry_add(dp,
762 child,
763 child_ent,
764 NULL);
765 if (code) {
766 goto done;
767 }
768
769 cladd:
770 code = _VVGC_entry_cl_add(child_ent, child);
771
772 done:
773 if (code && code != EINVAL) {
774 ViceLog(0, ("VVGCache_entry_add: error %d trying to add vol %lu to VG"
775 " %lu on partition %s", code, afs_printable_uint32_lu(child),
776 afs_printable_uint32_lu(parent), VPartitionPath(dp)));
777 }
778
779 if (code == 0 && VVGCache.part[dp->index].state == VVGC_PART_STATE_UPDATING) {
780 /* we successfully added the entry; make sure it's not on the
781 * to-delete list, so it doesn't get deleted later */
782 code = _VVGC_dlist_del_r(dp, parent, child);
783 if (code && code != ENOENT) {
784 ViceLog(0, ("VVGCache_entry_add: error %d trying to remove vol "
785 "%lu (parent %lu) from the to-delete list for part "
786 "%s.\n", code, afs_printable_uint32_lu(child),
787 afs_printable_uint32_lu(parent),
788 VPartitionPath(dp)));
789 } else {
790 code = 0;
791 }
792 }
793
794 return code;
795 }
796
797 /**
798 * add an entry to the volume group cache.
799 *
800 * @param[in] dp disk partition object
801 * @param[in] parent parent volume id
802 * @param[in] child child volume id
803 * @param[out] newvg if non-NULL, *newvg is 1 if adding this added a
804 * new VG, 0 if we added to an existing VG
805 *
806 * @return operation status
807 * @retval 0 success
808 */
809 int
810 VVGCache_entry_add(struct DiskPartition64 * dp,
811 VolumeId parent,
812 VolumeId child,
813 afs_int32 *newvg)
814 {
815 int code = 0;
816
817 VOL_LOCK;
818 VVGCache_entry_add_r(dp, parent, child, newvg);
819 VOL_UNLOCK;
820
821 return code;
822 }
823
824 /**
825 * delete an entry from the volume group cache.
826 *
827 * If partition is scanning, actually puts the entry on a list of entries
828 * to delete when the scan is done.
829 *
830 * @param[in] dp disk partition object
831 * @param[in] parent parent volume id
832 * @param[in] child child volume id
833 *
834 * @pre VOL_LOCK held
835 *
836 * @return operation status
837 * @retval 0 success
838 */
839 int
840 VVGCache_entry_del_r(struct DiskPartition64 * dp,
841 VolumeId parent, VolumeId child)
842 {
843 if (VVGCache.part[dp->index].state == VVGC_PART_STATE_UPDATING) {
844 int code;
845 code = _VVGC_dlist_add_r(dp, parent, child);
846 if (code) {
847 return code;
848 }
849 }
850 return _VVGC_entry_purge_r(dp, parent, child);
851 }
852
853 /**
854 * delete an entry from the volume group cache.
855 *
856 * @param[in] dp disk partition object
857 * @param[in] parent parent volume id
858 * @param[in] child child volume id
859 *
860 * @pre VOL_LOCK held
861 *
862 * @internal
863 *
864 * @return operation status
865 * @retval 0 success
866 */
867 int
868 _VVGC_entry_purge_r(struct DiskPartition64 * dp,
869 VolumeId parent, VolumeId child)
870 {
871 int code = 0, res;
872 VVGCache_entry_t * parent_ent, * child_ent;
873 VVGCache_hash_entry_t * child_hent;
874
875 /* check mappings for each volid */
876 res = _VVGC_lookup(dp, parent, &parent_ent, NULL);
877 if (res) {
878 code = res;
879 goto done;
880 }
881 res = _VVGC_lookup(dp, child, &child_ent, &child_hent);
882 if (res) {
883 code = res;
884 goto done;
885 }
886
887 /* if the mappings don't match, we have a serious error */
888 if (parent_ent != child_ent) {
889 ViceLog(0, ("VVGCache_entry_del: trying to delete vol %lu from VG %lu, "
890 "but vol %lu points to VGC entry %"AFS_PTR_FMT" and VG %lu "
891 "points to VGC entry %"AFS_PTR_FMT"\n",
892 afs_printable_uint32_lu(child),
893 afs_printable_uint32_lu(parent),
894 afs_printable_uint32_lu(child),
895 child_ent, afs_printable_uint32_lu(parent), parent_ent));
896 code = -1;
897 goto done;
898 }
899
900 code = _VVGC_hash_entry_del(child_hent);
901
902 done:
903 return code;
904 }
905
906 /**
907 * delete an entry from the volume group cache.
908 *
909 * @param[in] dp disk partition object
910 * @param[in] parent parent volume id
911 * @param[in] child child volume id
912 *
913 * @return operation status
914 * @retval 0 success
915 */
916 int
917 VVGCache_entry_del(struct DiskPartition64 * dp,
918 VolumeId parent, VolumeId child)
919 {
920 int code;
921
922 VOL_LOCK;
923 code = VVGCache_entry_del_r(dp, parent, child);
924 VOL_UNLOCK;
925
926 return code;
927 }
928
929 /**
930 * query a volume group by any member volume id.
931 *
932 * @param[in] dp disk partition object
933 * @param[in] volume volume id of a member of VG
934 * @param[out] res vg membership data
935 *
936 * @pre VOL_LOCK held
937 *
938 * @return operation status
939 * @retval 0 success
940 * @retval EAGAIN partition needs to finish scanning
941 */
942 int
943 VVGCache_query_r(struct DiskPartition64 * dp,
944 VolumeId volume,
945 VVGCache_query_t * res)
946 {
947 int code = 0;
948 VVGCache_entry_t * ent;
949
950 /* If cache for this partition doesn't exist; start a scan */
951 if (VVGCache.part[dp->index].state == VVGC_PART_STATE_INVALID) {
952 code = VVGCache_scanStart_r(dp);
953 if (code == 0 || code == -3) {
954 /* -3 means another thread already started scanning */
955 return EAGAIN;
956 }
957 return code;
958 }
959 if (VVGCache.part[dp->index].state == VVGC_PART_STATE_UPDATING) {
960 return EAGAIN;
961 }
962
963 code = _VVGC_lookup(dp, volume, &ent, NULL);
964 if (!code) {
965 code = _VVGC_entry_export(ent, res);
966 }
967
968 return code;
969 }
970
971 /**
972 * query a volume group by any member volume id.
973 *
974 * @param[in] dp disk partition object
975 * @param[in] volume volume id of a member of VG
976 * @param[out] res vg membership data
977 *
978 * @return operation status
979 * @retval 0 success
980 */
981 int
982 VVGCache_query(struct DiskPartition64 * dp,
983 VolumeId volume, VVGCache_query_t * res)
984 {
985 int code;
986
987 VOL_LOCK;
988 code = VVGCache_query_r(dp, volume, res);
989 VOL_UNLOCK;
990
991 return code;
992 }
993
994 /**
995 * begin asynchronous scan of on-disk volume group metadata.
996 *
997 * @param[in] dp disk partition object
998 *
999 * @pre VOL_LOCK held
1000 *
1001 * @return operation status
1002 * @retval 0 success
1003 */
1004 int
1005 VVGCache_scanStart_r(struct DiskPartition64 * dp)
1006 {
1007 int code = 0, res;
1008
1009 if (dp) {
1010 code = _VVGC_scan_start(dp);
1011 } else {
1012 /* start a scanner thread on each partition */
1013 for (dp = DiskPartitionList; dp; dp = dp->next) {
1014 res = _VVGC_scan_start(dp);
1015 if (res) {
1016 code = res;
1017 }
1018 }
1019 }
1020
1021 return code;
1022 }
1023
1024 /**
1025 * begin asynchronous scan of on-disk volume group metadata.
1026 *
1027 * @param[in] dp disk partition object
1028 *
1029 * @return operation status
1030 * @retval 0 success
1031 */
1032 int
1033 VVGCache_scanStart(struct DiskPartition64 * dp)
1034 {
1035 int code;
1036
1037 VOL_LOCK;
1038 code = VVGCache_scanStart_r(dp);
1039 VOL_UNLOCK;
1040
1041 return code;
1042 }
1043
1044 /**
1045 * wait for async on-disk VG metadata scan to complete.
1046 *
1047 * @param[in] dp disk partition object
1048 *
1049 * @pre VOL_LOCK held
1050 *
1051 * @warning this routine must drop VOL_LOCK internally
1052 *
1053 * @return operation status
1054 * @retval 0 success
1055 */
1056 int
1057 VVGCache_scanWait_r(struct DiskPartition64 * dp)
1058 {
1059 int code = 0;
1060
1061 while (VVGCache.part[dp->index].state == VVGC_PART_STATE_UPDATING) {
1062 VOL_CV_WAIT(&VVGCache.part[dp->index].cv);
1063 }
1064
1065 return code;
1066 }
1067
1068 /**
1069 * wait for async on-disk VG metadata scan to complete.
1070 *
1071 * @param[in] dp disk partition object
1072 *
1073 * @return operation status
1074 * @retval 0 success
1075 */
1076 int
1077 VVGCache_scanWait(struct DiskPartition64 * dp)
1078 {
1079 int code;
1080
1081 VOL_LOCK;
1082 code = VVGCache_scanWait_r(dp);
1083 VOL_UNLOCK;
1084
1085 return code;
1086 }
1087
1088 /**
1089 * flush all cache entries for a given disk partition.
1090 *
1091 * @param[in] part disk partition object
1092 *
1093 * @pre VOL_LOCK held
1094 *
1095 * @return operation status
1096 * @retval 0 success
1097 *
1098 * @internal
1099 */
1100 int
1101 _VVGC_flush_part_r(struct DiskPartition64 * part)
1102 {
1103 int code = 0, res;
1104 int i;
1105 VVGCache_hash_entry_t * ent, * nent;
1106
1107 for (i = 0; i < VolumeHashTable.Size; i++) {
1108 for (queue_Scan(&VVGCache_hash_table.hash_buckets[i],
1109 ent,
1110 nent,
1111 VVGCache_hash_entry)) {
1112 if (ent->dp == part) {
1113 VolumeId volid = ent->volid;
1114 res = _VVGC_hash_entry_del(ent);
1115 if (res) {
1116 ViceLog(0, ("_VVGC_flush_part_r: error %d deleting hash entry for %lu\n",
1117 res, afs_printable_uint32_lu(volid)));
1118 code = res;
1119 }
1120 }
1121 }
1122 }
1123
1124 return code;
1125 }
1126
1127 /**
1128 * flush all cache entries for a given disk partition.
1129 *
1130 * @param[in] part disk partition object
1131 *
1132 * @return operation status
1133 * @retval 0 success
1134 *
1135 * @internal
1136 */
1137 int
1138 _VVGC_flush_part(struct DiskPartition64 * part)
1139 {
1140 int code;
1141
1142 VOL_LOCK;
1143 code = _VVGC_flush_part_r(part);
1144 VOL_UNLOCK;
1145
1146 return code;
1147 }
1148
1149
1150 /**
1151 * change VVGC partition state.
1152 *
1153 * @param[in] part disk partition object
1154 * @param[in] state new state
1155 *
1156 * @pre VOL_LOCK is held
1157 *
1158 * @return old state
1159 *
1160 * @internal
1161 */
1162 int
1163 _VVGC_state_change(struct DiskPartition64 * part,
1164 VVGCache_part_state_t state)
1165 {
1166 VVGCache_part_state_t old_state;
1167
1168 old_state = VVGCache.part[part->index].state;
1169 VVGCache.part[part->index].state = state;
1170
1171 if (old_state != state) {
1172 CV_BROADCAST(&VVGCache.part[part->index].cv);
1173 }
1174
1175 return old_state;
1176 }
1177
1178 #endif /* AFS_DEMAND_ATTACH_FS */