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