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