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