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