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