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