99bc6f1685c399c13ed9d63c534283af9ddc4732
[openafs.git] / src / vol / vg_scan.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  * asynchronous partition scanner
14  */
15
16 #include <afsconfig.h>
17 #include <afs/param.h>
18
19 #ifdef AFS_DEMAND_ATTACH_FS
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <sys/stat.h>
26 #include <dirent.h>
27 #include <afs/assert.h>
28 #include <string.h>
29 #include <sys/file.h>
30 #include <sys/param.h>
31 #include <lock.h>
32 #if defined(AFS_SUN5_ENV) || defined(AFS_HPUX_ENV)
33 #include <unistd.h>
34 #endif
35 #include <afs/afsutil.h>
36 #include <lwp.h>
37 #include "nfs.h"
38 #include <afs/afsint.h>
39 #include "ihandle.h"
40 #include "vnode.h"
41 #include "volume.h"
42 #include "viceinode.h"
43 #include "voldefs.h"
44 #include "partition.h"
45 #include <afs/errors.h>
46
47 #define __VOL_VG_CACHE_IMPL 1
48
49 #include "vg_cache.h"
50 #include "vg_cache_impl.h"
51
52 #ifdef O_LARGEFILE
53 #define afs_open        open64
54 #else /* !O_LARGEFILE */
55 #define afs_open        open
56 #endif /* !O_LARGEFILE */
57
58 static int _VVGC_scan_table_init(VVGCache_scan_table_t * tbl);
59 static int _VVGC_scan_table_add(VVGCache_scan_table_t * tbl,
60                                 struct DiskPartition64 * dp,
61                                 VolumeId volid,
62                                 VolumeId parent);
63 static int _VVGC_scan_table_flush(VVGCache_scan_table_t * tbl,
64                                   struct DiskPartition64 * dp);
65 static void * _VVGC_scanner_thread(void *);
66 static int _VVGC_scan_partition(struct DiskPartition64 * part);
67 static VVGCache_dlist_entry_t * _VVGC_dlist_lookup_r(struct DiskPartition64 *dp,
68                                                      VolumeId parent,
69                                                      VolumeId child);
70 static void _VVGC_flush_dlist(struct DiskPartition64 *dp);
71
72 /**
73  * init a thread-local scan table.
74  *
75  * @param[in] tbl  scan table
76  *
77  * @return operation status
78  *    @retval 0 success
79  *
80  * @internal
81  */
82 static int
83 _VVGC_scan_table_init(VVGCache_scan_table_t * tbl)
84 {
85     memset(tbl, 0, sizeof(*tbl));
86
87     return 0;
88 }
89
90 /**
91  * add an entry to the thread-local scan table.
92  *
93  * @param[in] tbl     scan table
94  * @param[in] dp      disk partition object
95  * @param[in] volid   volume id
96  * @param[in] parent  parent volume id
97  *
98  * @pre VOL_LOCK is NOT held
99  *
100  * @note if the table is full, this routine will acquire
101  *       VOL_LOCK and flush the table to the global one.
102  *
103  * @return operation status
104  *    @retval 0 success
105  *    @retval nonzero a VVGCache_entry_add_r operation failed during a
106  *                    flush of the thread-local table
107  *
108  * @internal
109  */
110 static int
111 _VVGC_scan_table_add(VVGCache_scan_table_t * tbl,
112                      struct DiskPartition64 * dp,
113                      VolumeId volid,
114                      VolumeId parent)
115 {
116     int code = 0;
117
118     if (tbl->idx == VVGC_SCAN_TBL_LEN) {
119         code = _VVGC_scan_table_flush(tbl, dp);
120     }
121
122     tbl->entries[tbl->idx].volid = volid;
123     tbl->entries[tbl->idx].parent = parent;
124     tbl->idx++;
125
126     return code;
127 }
128
129 /**
130  * flush thread-local scan table to the global VG cache.
131  *
132  * @param[in] tbl     scan table
133  * @param[in] dp      disk partition object
134  *
135  * @pre VOL_LOCK is NOT held
136  *
137  * @return operation status
138  *    @retval 0 success
139  *    @retval nonzero a VVGCache_entry_add_r operation failed during a
140  *                    flush of the thread-local table
141  *
142  * @internal
143  */
144 static int
145 _VVGC_scan_table_flush(VVGCache_scan_table_t * tbl,
146                        struct DiskPartition64 * dp)
147 {
148     int code = 0, res, i;
149     afs_int32 newvg = 0;
150     unsigned long newvols, newvgs;
151
152     newvols = tbl->newvols;
153     newvgs = tbl->newvgs;
154
155     VOL_LOCK;
156
157     for (i = 0; i < tbl->idx; i++) {
158         /*
159          * We need to check the 'to-delete' list and prevent adding any entries
160          * that are on it. The volser could potentially create a volume in one
161          * VG, then delete it and put it on another VG. If we are doing a scan
162          * when that happens, tbl->entries could have the entries for trying to
163          * put the vol on both VGs, though at least one of them will also be on
164          * the dlist.  If we put everything in tbl->entries on the VGC then try
165          * to delete afterwards, putting one entry on the VGC cause an error,
166          * and we'll fail to add it. So instead, avoid adding any new VGC
167          * entries if it is on the dlist.
168          */
169         if (_VVGC_dlist_lookup_r(dp, tbl->entries[i].parent,
170                                  tbl->entries[i].volid)) {
171             continue;
172         }
173         res = VVGCache_entry_add_r(dp,
174                                    tbl->entries[i].parent,
175                                    tbl->entries[i].volid,
176                                    &newvg);
177         if (res) {
178             code = res;
179         } else {
180             newvols++;
181             newvgs += newvg;
182         }
183     }
184
185     /* flush the to-delete list while we're here. We don't need to preserve
186      * the list across the entire scan, and flushing it each time we flush
187      * a scan table will keep the size of the dlist down */
188     _VVGC_flush_dlist(dp);
189
190     VOL_UNLOCK;
191
192     ViceLog(125, ("VVGC_scan_table_flush: flushed %d entries from "
193                   "scan table to global VG cache\n", tbl->idx));
194     ViceLog(125, ("VVGC_scan_table_flush: %s total: %lu vols, %lu groups\n",
195                   VPartitionPath(dp), newvols, newvgs));
196
197     res = _VVGC_scan_table_init(tbl);
198     if (res) {
199         code = res;
200     }
201
202     tbl->newvols = newvols;
203     tbl->newvgs = newvgs;
204
205     return code;
206 }
207
208 /**
209  * read a volume header from disk into a VolumeHeader structure.
210  *
211  * @param[in]  path     absolute path to .vol volume header
212  * @param[out] hdr      volume header object
213  *
214  * @return operation status
215  *    @retval 0 success
216  *    @retval ENOENT volume header does not exist
217  *    @retval EINVAL volume header is invalid
218  *
219  * @internal
220  */
221 static int
222 _VVGC_read_header(const char *path, struct VolumeHeader *hdr)
223 {
224     int fd;
225     int code;
226     struct VolumeDiskHeader diskHeader;
227
228     fd = afs_open(path, O_RDONLY);
229     if (fd == -1) {
230         ViceLog(0, ("_VVGC_read_header: could not open %s; error = %d\n",
231             path, errno));
232         return ENOENT;
233     }
234
235     code = read(fd, &diskHeader, sizeof(diskHeader));
236     close(fd);
237     if (code != sizeof(diskHeader)) {
238         ViceLog(0, ("_VVGC_read_header: could not read disk header from %s; error = %d\n",
239             path, errno));
240         return EINVAL;
241     }
242
243     if (diskHeader.stamp.magic != VOLUMEHEADERMAGIC) {
244         ViceLog(0, ("_VVGC_read_header: disk header %s has magic %lu, should "
245                     "be %lu\n", path,
246                     afs_printable_uint32_lu(diskHeader.stamp.magic),
247                     afs_printable_uint32_lu(VOLUMEHEADERMAGIC)));
248         return EINVAL;
249     }
250
251     DiskToVolumeHeader(hdr, &diskHeader);
252     return 0;
253 }
254
255 /**
256  * determines what to do with a volume header during a VGC scan.
257  *
258  * @param[in] dp        the disk partition object
259  * @param[in] node_path the absolute path to the header to handle
260  * @param[out] hdr      the header read in from disk
261  * @param[out] skip     1 if we should skip the header (pretend it doesn't
262  *                      exist), 0 otherwise
263  *
264  * @return operation status
265  *  @retval 0  success
266  *  @retval -1 internal error beyond just failing to read the header file
267  */
268 static int
269 _VVGC_handle_header(struct DiskPartition64 *dp, const char *node_path,
270                     struct VolumeHeader *hdr, int *skip)
271 {
272     int code;
273
274     *skip = 1;
275
276     code = _VVGC_read_header(node_path, hdr);
277     if (code) {
278         /* retry while holding a partition write lock, to ensure we're not
279          * racing a writer/creator of the header */
280
281         if (code == ENOENT) {
282             /* Ignore ENOENT; it's as if we never got it from readdir in the
283              * first place. Other error codes means the header exists, but
284              * there's something wrong with it. */
285             return 0;
286         }
287
288         code = VPartHeaderLock(dp, WRITE_LOCK);
289         if (code) {
290             ViceLog(0, ("_VVGC_handle_header: error acquiring partition "
291                         "write lock while trying to open %s\n",
292                         node_path));
293             return -1;
294         }
295         code = _VVGC_read_header(node_path, hdr);
296         VPartHeaderUnlock(dp, WRITE_LOCK);
297     }
298
299     if (code) {
300         if (code != ENOENT) {
301             ViceLog(0, ("_VVGC_scan_partition: %s does not appear to be a "
302                         "legitimate volume header file; deleted\n",
303                         node_path));
304
305             if (unlink(node_path)) {
306                 ViceLog(0, ("Unable to unlink %s (errno = %d)\n",
307                             node_path, errno));
308             }
309         }
310         return 0;
311     }
312
313     /* header is fine; do not skip it, and do not error out */
314     *skip = 0;
315     return 0;
316 }
317
318 /**
319  * scan a disk partition for .vol files
320  *
321  * @param[in] part   disk partition object
322  *
323  * @pre VOL_LOCK is NOT held
324  *
325  * @return operation status
326  *    @retval 0 success
327  *    @retval -1 invalid disk partition object
328  *    @retval -2 failed to flush stale entries for this partition
329  *
330  * @internal
331  */
332 static int
333 _VVGC_scan_partition(struct DiskPartition64 * part)
334 {
335     int code, res, skip;
336     DIR *dirp = NULL;
337     struct VolumeHeader hdr;
338     struct dirent *dp;
339     VVGCache_scan_table_t tbl;
340     char *part_path = NULL, *p;
341     char node_path[MAXPATHLEN];
342
343     code = _VVGC_scan_table_init(&tbl);
344     if (code) {
345         ViceLog(0, ("VVGC_scan_partition: could not init scan table; error = %d\n",
346             code));
347         goto done;
348     }
349     part_path = VPartitionPath(part);
350     if (part_path == NULL) {
351         ViceLog(0, ("VVGC_scan_partition: invalid partition object given; aborting scan\n"));
352         code = -1;
353         goto done;
354     }
355
356     VOL_LOCK;
357     res = _VVGC_flush_part_r(part);
358     if (res) {
359         ViceLog(0, ("VVGC_scan_partition: error flushing partition %s; error = %d\n",
360             VPartitionPath(part), res));
361         code = -2;
362     }
363     VOL_UNLOCK;
364     if (code) {
365         goto done;
366     }
367
368     dirp = opendir(part_path);
369     if (dirp == NULL) {
370         ViceLog(0, ("VVGC_scan_partition: could not open %s, aborting scan; error = %d\n",
371             part_path, errno));
372         code = -1;
373         goto done;
374     }
375
376     ViceLog(5, ("VVGC_scan_partition: scanning partition %s for VG cache\n",
377                  part_path));
378
379     while ((dp = readdir(dirp))) {
380         p = strrchr(dp->d_name, '.');
381         if (p == NULL || strcmp(p, VHDREXT) != 0) {
382             continue;
383         }
384         snprintf(node_path,
385                  sizeof(node_path),
386                  "%s/%s",
387                  VPartitionPath(part),
388                  dp->d_name);
389
390         res = _VVGC_handle_header(part, node_path, &hdr, &skip);
391         if (res) {
392             /* internal error; error out */
393             code = -1;
394             goto done;
395         }
396         if (skip) {
397             continue;
398         }
399
400         res = _VVGC_scan_table_add(&tbl,
401                                    part,
402                                    hdr.id,
403                                    hdr.parent);
404         if (res) {
405             ViceLog(0, ("VVGC_scan_partition: error %d adding volume %s to scan table\n",
406                 res, node_path));
407             code = res;
408         }
409     }
410
411     _VVGC_scan_table_flush(&tbl, part);
412
413  done:
414     if (dirp) {
415         closedir(dirp);
416         dirp = NULL;
417     }
418     if (code) {
419         ViceLog(0, ("VVGC_scan_partition: error %d while scanning %s\n",
420                     code, part_path));
421     } else {
422         ViceLog(0, ("VVGC_scan_partition: finished scanning %s: %lu volumes in %lu groups\n",
423                      part_path, tbl.newvols, tbl.newvgs));
424     }
425
426     VOL_LOCK;
427
428     _VVGC_flush_dlist(part);
429     free(VVGCache.part[part->index].dlist_hash_buckets);
430     VVGCache.part[part->index].dlist_hash_buckets = NULL;
431
432     if (code) {
433         _VVGC_state_change(part, VVGC_PART_STATE_INVALID);
434     } else {
435         _VVGC_state_change(part, VVGC_PART_STATE_VALID);
436     }
437
438     VOL_UNLOCK;
439
440     return code;
441 }
442
443 /**
444  * scanner thread.
445  */
446 static void *
447 _VVGC_scanner_thread(void * args)
448 {
449     struct DiskPartition64 *part = args;
450     int code;
451
452     code = _VVGC_scan_partition(part);
453     if (code) {
454         ViceLog(0, ("Error: _VVGC_scan_partition failed with code %d for partition %s\n",
455             code, VPartitionPath(part)));
456     }
457
458     return NULL;
459 }
460
461 /**
462  * start a background scan.
463  *
464  * @param[in] dp  disk partition object
465  *
466  * @return operation status
467  *    @retval 0 success
468  *    @retval -1 internal error
469  *    @retval -3 racing against another thread
470  *
471  * @internal
472  */
473 int
474 _VVGC_scan_start(struct DiskPartition64 * dp)
475 {
476     int code = 0;
477     pthread_t tid;
478     pthread_attr_t attrs;
479     int i;
480
481     if (_VVGC_state_change(dp,
482                            VVGC_PART_STATE_UPDATING)
483         == VVGC_PART_STATE_UPDATING) {
484         /* race */
485         ViceLog(0, ("VVGC_scan_partition: race detected; aborting scanning partition %s\n",
486                     VPartitionPath(dp)));
487         code = -3;
488         goto error;
489     }
490
491     /* initialize partition's to-delete list */
492     VVGCache.part[dp->index].dlist_hash_buckets =
493         malloc(VolumeHashTable.Size * sizeof(struct rx_queue));
494     if (!VVGCache.part[dp->index].dlist_hash_buckets) {
495         code = -1;
496         goto error;
497     }
498     for (i = 0; i < VolumeHashTable.Size; i++) {
499         queue_Init(&VVGCache.part[dp->index].dlist_hash_buckets[i]);
500     }
501
502     code = pthread_attr_init(&attrs);
503     if (code) {
504         goto error;
505     }
506
507     code = pthread_attr_setdetachstate(&attrs, PTHREAD_CREATE_DETACHED);
508     if (code) {
509         goto error;
510     }
511
512     code = pthread_create(&tid, &attrs, &_VVGC_scanner_thread, dp);
513
514     if (code) {
515         VVGCache_part_state_t old_state;
516
517         ViceLog(0, ("_VVGC_scan_start: pthread_create failed with %d\n", code));
518
519         old_state = _VVGC_state_change(dp, VVGC_PART_STATE_INVALID);
520         assert(old_state == VVGC_PART_STATE_UPDATING);
521     }
522
523  error:
524     if (code) {
525         ViceLog(0, ("_VVGC_scan_start failed with code %d for partition %s\n",
526                 code, VPartitionPath(dp)));
527         if (VVGCache.part[dp->index].dlist_hash_buckets) {
528             free(VVGCache.part[dp->index].dlist_hash_buckets);
529             VVGCache.part[dp->index].dlist_hash_buckets = NULL;
530         }
531     }
532
533     return code;
534 }
535
536 /**
537  * looks up an entry on the to-delete list, if it exists.
538  *
539  * @param[in] dp     the partition whose dlist we are looking at
540  * @param[in] parent the parent volume ID we're looking for
541  * @param[in] child  the child volume ID we're looking for
542  *
543  * @return a pointer to the entry in the dlist for that entry
544  *  @retval NULL the requested entry does not exist in the dlist
545  */
546 static VVGCache_dlist_entry_t *
547 _VVGC_dlist_lookup_r(struct DiskPartition64 *dp, VolumeId parent,
548                      VolumeId child)
549 {
550     int bucket = VVGC_HASH(child);
551     VVGCache_dlist_entry_t *ent, *nent;
552
553     for (queue_Scan(&VVGCache.part[dp->index].dlist_hash_buckets[bucket],
554                     ent, nent,
555                     VVGCache_dlist_entry)) {
556
557         if (ent->child == child && ent->parent == parent) {
558             return ent;
559         }
560     }
561
562     return NULL;
563 }
564
565 /**
566  * delete all of the entries in the dlist from the VGC.
567  *
568  * Traverses the to-delete list for the specified partition, and deletes
569  * the specified entries from the global VGC. Also deletes the entries from
570  * the dlist itself as it goes along.
571  *
572  * @param[in] dp  the partition whose dlist we are flushing
573  */
574 static void
575 _VVGC_flush_dlist(struct DiskPartition64 *dp)
576 {
577     int i;
578     VVGCache_dlist_entry_t *ent, *nent;
579
580     for (i = 0; i < VolumeHashTable.Size; i++) {
581         for (queue_Scan(&VVGCache.part[dp->index].dlist_hash_buckets[i],
582                         ent, nent,
583                         VVGCache_dlist_entry)) {
584
585             _VVGC_entry_purge_r(dp, ent->parent, ent->child);
586             queue_Remove(ent);
587             free(ent);
588         }
589     }
590 }
591
592 /**
593  * add a VGC entry to the partition's to-delete list.
594  *
595  * This adds a VGC entry (a parent/child pair) to a list of VGC entries to
596  * be deleted from the VGC at the end of a VGC scan. This is necessary,
597  * while a VGC scan is ocurring, volumes may be deleted. Since a VGC scan
598  * scans a partition in VVGC_SCAN_TBL_LEN chunks, a VGC delete operation
599  * may delete a volume, only for it to be added again when the VGC scan's
600  * table adds it to the VGC. So when a VGC entry is deleted and a VGC scan
601  * is running, this function must be called to ensure it does not come
602  * back onto the VGC.
603  *
604  * @param[in] dp      the partition to whose dlist we are adding
605  * @param[in] parent  the parent volumeID of the VGC entry
606  * @param[in] child   the child volumeID of the VGC entry
607  *
608  * @return operation status
609  *  @retval 0 success
610  *  @retval ENOMEM memory allocation error
611  *
612  * @pre VVGCache.part[dp->index].state == VVGC_PART_STATE_UPDATING
613  *
614  * @internal VGC use only
615  */
616 int
617 _VVGC_dlist_add_r(struct DiskPartition64 *dp, VolumeId parent,
618                   VolumeId child)
619 {
620     int bucket = VVGC_HASH(child);
621     VVGCache_dlist_entry_t *entry;
622
623     entry = malloc(sizeof(*entry));
624     if (!entry) {
625         return ENOMEM;
626     }
627
628     entry->child = child;
629     entry->parent = parent;
630
631     queue_Append(&VVGCache.part[dp->index].dlist_hash_buckets[bucket],
632                  entry);
633     return 0;
634 }
635
636 /**
637  * delete a VGC entry from the partition's to-delete list.
638  *
639  * When a VGC scan is ocurring, and a volume is removed, but then created
640  * again, we need to ensure that it does not get deleted from being on the
641  * dlist. Call this function whenever adding a new entry to the VGC during
642  * a VGC scan to ensure it doesn't get deleted later.
643  *
644  * @param[in] dp      the partition from whose dlist we are deleting
645  * @param[in] parent  the parent volumeID of the VGC entry
646  * @param[in] child   the child volumeID of the VGC entry
647  *
648  * @return operation status
649  *  @retval 0 success
650  *  @retval ENOENT the specified VGC entry is not on the dlist
651  *
652  * @pre VVGCache.part[dp->index].state == VVGC_PART_STATE_UPDATING
653  *
654  * @internal VGC use only
655  *
656  * @see _VVGC_dlist_add_r
657  */
658 int
659 _VVGC_dlist_del_r(struct DiskPartition64 *dp, VolumeId parent,
660                   VolumeId child)
661 {
662     VVGCache_dlist_entry_t *ent;
663
664     ent = _VVGC_dlist_lookup_r(dp, parent, child);
665     if (!ent) {
666         return ENOENT;
667     }
668
669     queue_Remove(ent);
670     free(ent);
671
672     return 0;
673 }
674
675 #endif /* AFS_DEMAND_ATTACH_FS */