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