HAMMER 38D/Many: Undo/Synchronization and crash recovery
[dragonfly.git] / sys / vfs / hammer / hammer_flusher.c
1 /*
2  * Copyright (c) 2008 The DragonFly Project.  All rights reserved.
3  * 
4  * This code is derived from software contributed to The DragonFly Project
5  * by Matthew Dillon <dillon@backplane.com>
6  * 
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  * 
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  * 
34  * $DragonFly: src/sys/vfs/hammer/hammer_flusher.c,v 1.4 2008/04/26 02:54:00 dillon Exp $
35  */
36 /*
37  * HAMMER dependancy flusher thread
38  *
39  * Meta data updates create buffer dependancies which are arranged as a
40  * hierarchy of lists.
41  */
42
43 #include "hammer.h"
44
45 static void hammer_flusher_thread(void *arg);
46 static void hammer_flusher_clean_loose_ios(hammer_mount_t hmp);
47 static void hammer_flusher_flush(hammer_mount_t hmp);
48 static void hammer_flusher_finalize(hammer_mount_t hmp,
49                     hammer_volume_t root_volume, hammer_off_t start_offset);
50
51 void
52 hammer_flusher_sync(hammer_mount_t hmp)
53 {
54         int seq;
55
56         if (hmp->flusher_td) {
57                 seq = ++hmp->flusher_seq;
58                 wakeup(&hmp->flusher_seq);
59                 while ((int)(seq - hmp->flusher_act) > 0)
60                         tsleep(&hmp->flusher_act, 0, "hmrfls", 0);
61         }
62 }
63
64 void
65 hammer_flusher_async(hammer_mount_t hmp)
66 {
67         if (hmp->flusher_td) {
68                 ++hmp->flusher_seq;
69                 wakeup(&hmp->flusher_seq);
70         }
71 }
72
73 void
74 hammer_flusher_create(hammer_mount_t hmp)
75 {
76         lwkt_create(hammer_flusher_thread, hmp, &hmp->flusher_td, NULL,
77                     0, -1, "hammer");
78 }
79
80 void
81 hammer_flusher_destroy(hammer_mount_t hmp)
82 {
83         if (hmp->flusher_td) {
84                 hmp->flusher_exiting = 1;
85                 ++hmp->flusher_seq;
86                 wakeup(&hmp->flusher_seq);
87                 while (hmp->flusher_td)
88                         tsleep(&hmp->flusher_exiting, 0, "hmrwex", 0);
89         }
90 }
91
92 static void
93 hammer_flusher_thread(void *arg)
94 {
95         hammer_mount_t hmp = arg;
96         int seq;
97
98         for (;;) {
99                 seq = hmp->flusher_seq;
100                 hammer_flusher_clean_loose_ios(hmp);
101                 hammer_flusher_flush(hmp);
102                 hammer_flusher_clean_loose_ios(hmp);
103                 hmp->flusher_act = seq;
104                 wakeup(&hmp->flusher_act);
105                 if (hmp->flusher_exiting)
106                         break;
107                 while (hmp->flusher_seq == hmp->flusher_act)
108                         tsleep(&hmp->flusher_seq, 0, "hmrflt", 0);
109         }
110         hmp->flusher_td = NULL;
111         wakeup(&hmp->flusher_exiting);
112         lwkt_exit();
113 }
114
115 static void
116 hammer_flusher_clean_loose_ios(hammer_mount_t hmp)
117 {
118         hammer_buffer_t buffer;
119         hammer_io_t io;
120
121         /*
122          * loose ends - buffers without bp's aren't tracked by the kernel
123          * and can build up, so clean them out.  This can occur when an
124          * IO completes on a buffer with no references left.
125          */
126         while ((io = TAILQ_FIRST(&hmp->lose_list)) != NULL) {
127                 KKASSERT(io->mod_list == &hmp->lose_list);
128                 TAILQ_REMOVE(io->mod_list, io, mod_entry);
129                 io->mod_list = NULL;
130                 hammer_ref(&io->lock);
131                 buffer = (void *)io;
132                 hammer_rel_buffer(buffer, 0);
133         }
134 }
135
136 /*
137  * Flush stuff
138  */
139 static void
140 hammer_flusher_flush(hammer_mount_t hmp)
141 {
142         hammer_volume_t root_volume;
143         hammer_blockmap_t rootmap;
144         hammer_inode_t ip;
145         hammer_off_t start_offset;
146         int error;
147
148         root_volume = hammer_get_root_volume(hmp, &error);
149         rootmap = &root_volume->ondisk->vol0_blockmap[HAMMER_ZONE_UNDO_INDEX];
150         start_offset = rootmap->next_offset;
151
152         if (hammer_debug_general & 0x00010000)
153                 kprintf("x");
154
155         while ((ip = TAILQ_FIRST(&hmp->flush_list)) != NULL) {
156                 TAILQ_REMOVE(&hmp->flush_list, ip, flush_entry);
157
158                 /*
159                  * We inherit the inode ref from the flush list
160                  */
161                 ip->error = hammer_sync_inode(ip, (ip->vp ? 0 : 1));
162                 hammer_flush_inode_done(ip);
163                 if (hmp->locked_dirty_count > 64) {
164                         hammer_flusher_finalize(hmp, root_volume, start_offset);
165                         start_offset = rootmap->next_offset;
166                 }
167         }
168         hammer_flusher_finalize(hmp, root_volume, start_offset);
169         hammer_rel_volume(root_volume, 0);
170 }
171
172 /*
173  * To finalize the flush we finish flushing all undo and data buffers
174  * still present, then we update the volume header and flush it,
175  * then we flush out the mata-data (that can now be undone).
176  *
177  * Note that as long as the undo fifo's start and end points do not
178  * match, we always must at least update the volume header.
179  */
180 static
181 void
182 hammer_flusher_finalize(hammer_mount_t hmp, hammer_volume_t root_volume,
183                         hammer_off_t start_offset)
184 {
185         hammer_blockmap_t rootmap;
186         hammer_io_t io;
187
188         /*
189          * Flush undo bufs
190          */
191         while ((io = TAILQ_FIRST(&hmp->undo_list)) != NULL) {
192                 KKASSERT(io->modify_refs == 0);
193                 hammer_ref(&io->lock);
194                 KKASSERT(io->type != HAMMER_STRUCTURE_VOLUME);
195                 hammer_io_flush(io);
196                 hammer_rel_buffer((hammer_buffer_t)io, 1);
197         }
198
199         /*
200          * Flush data bufs
201          */
202         while ((io = TAILQ_FIRST(&hmp->data_list)) != NULL) {
203                 KKASSERT(io->modify_refs == 0);
204                 hammer_ref(&io->lock);
205                 KKASSERT(io->type != HAMMER_STRUCTURE_VOLUME);
206                 hammer_io_flush(io);
207                 hammer_rel_buffer((hammer_buffer_t)io, 1);
208         }
209
210         /*
211          * Wait for I/O to complete
212          */
213         crit_enter();
214         while (hmp->io_running_count) {
215                 kprintf("WAIT1 %d\n", hmp->io_running_count);
216                 tsleep(&hmp->io_running_count, 0, "hmrfl1", 0);
217         }
218         crit_exit();
219
220         /*
221          * Update the volume header
222          */
223         rootmap = &root_volume->ondisk->vol0_blockmap[HAMMER_ZONE_UNDO_INDEX];
224         if (rootmap->first_offset != start_offset) {
225                 hammer_modify_volume(NULL, root_volume, NULL, 0);
226                 rootmap->first_offset = start_offset;
227                 hammer_modify_volume_done(root_volume);
228                 hammer_io_flush(&root_volume->io);
229         }
230
231         /*
232          * Wait for I/O to complete
233          */
234         crit_enter();
235         while (hmp->io_running_count) {
236                 tsleep(&hmp->io_running_count, 0, "hmrfl2", 0);
237         }
238         crit_exit();
239
240         /*
241          * Flush meta-data
242          */
243         while ((io = TAILQ_FIRST(&hmp->meta_list)) != NULL) {
244                 KKASSERT(io->modify_refs == 0);
245                 hammer_ref(&io->lock);
246                 KKASSERT(io->type != HAMMER_STRUCTURE_VOLUME);
247                 hammer_io_flush(io);
248                 hammer_rel_buffer((hammer_buffer_t)io, 1);
249         }
250 }
251