11a7bd32f5992ebe3b8e4c9dfa5aec020134ae28
[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.11 2008/05/03 20:21:20 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 int hammer_must_finalize_undo(hammer_mount_t hmp);
49 static void hammer_flusher_finalize(hammer_transaction_t trans,
50                     hammer_off_t start_offset);
51
52 void
53 hammer_flusher_sync(hammer_mount_t hmp)
54 {
55         int seq;
56
57         if (hmp->flusher_td) {
58                 seq = hmp->flusher_next;
59                 if (hmp->flusher_signal == 0) {
60                         hmp->flusher_signal = 1;
61                         wakeup(&hmp->flusher_signal);
62                 }
63                 while ((int)(seq - hmp->flusher_done) > 0)
64                         tsleep(&hmp->flusher_done, 0, "hmrfls", 0);
65         }
66 }
67
68 void
69 hammer_flusher_async(hammer_mount_t hmp)
70 {
71         if (hmp->flusher_td) {
72                 if (hmp->flusher_signal == 0) {
73                         hmp->flusher_signal = 1;
74                         wakeup(&hmp->flusher_signal);
75                 }
76         }
77 }
78
79 void
80 hammer_flusher_create(hammer_mount_t hmp)
81 {
82         hmp->flusher_signal = 0;
83         hmp->flusher_act = 0;
84         hmp->flusher_done = 0;
85         hmp->flusher_next = 1;
86         lwkt_create(hammer_flusher_thread, hmp, &hmp->flusher_td, NULL,
87                     0, -1, "hammer");
88 }
89
90 void
91 hammer_flusher_destroy(hammer_mount_t hmp)
92 {
93         if (hmp->flusher_td) {
94                 hmp->flusher_exiting = 1;
95                 while (hmp->flusher_td) {
96                         hmp->flusher_signal = 1;
97                         wakeup(&hmp->flusher_signal);
98                         tsleep(&hmp->flusher_exiting, 0, "hmrwex", 0);
99                 }
100         }
101 }
102
103 static void
104 hammer_flusher_thread(void *arg)
105 {
106         hammer_mount_t hmp = arg;
107
108         for (;;) {
109                 while (hmp->flusher_lock)
110                         tsleep(&hmp->flusher_lock, 0, "hmrhld", 0);
111                 hmp->flusher_act = hmp->flusher_next;
112                 ++hmp->flusher_next;
113                 kprintf("F");
114                 hammer_flusher_clean_loose_ios(hmp);
115                 hammer_flusher_flush(hmp);
116                 hammer_flusher_clean_loose_ios(hmp);
117                 hmp->flusher_done = hmp->flusher_act;
118
119                 wakeup(&hmp->flusher_done);
120
121                 /*
122                  * Wait for activity.
123                  */
124                 if (hmp->flusher_exiting && TAILQ_EMPTY(&hmp->flush_list))
125                         break;
126                 kprintf("E");
127
128                 while (hmp->flusher_signal == 0 &&
129                        TAILQ_EMPTY(&hmp->flush_list)) {
130                         tsleep(&hmp->flusher_signal, 0, "hmrwwa", 0);
131                 }
132                 hmp->flusher_signal = 0;
133         }
134         hmp->flusher_td = NULL;
135         wakeup(&hmp->flusher_exiting);
136         lwkt_exit();
137 }
138
139 static void
140 hammer_flusher_clean_loose_ios(hammer_mount_t hmp)
141 {
142         hammer_buffer_t buffer;
143         hammer_io_t io;
144
145         /*
146          * loose ends - buffers without bp's aren't tracked by the kernel
147          * and can build up, so clean them out.  This can occur when an
148          * IO completes on a buffer with no references left.
149          */
150         while ((io = TAILQ_FIRST(&hmp->lose_list)) != NULL) {
151                 KKASSERT(io->mod_list == &hmp->lose_list);
152                 TAILQ_REMOVE(io->mod_list, io, mod_entry);
153                 io->mod_list = NULL;
154                 hammer_ref(&io->lock);
155                 buffer = (void *)io;
156                 hammer_rel_buffer(buffer, 0);
157         }
158 }
159
160 /*
161  * Flush stuff
162  */
163 static void
164 hammer_flusher_flush(hammer_mount_t hmp)
165 {
166         struct hammer_transaction trans;
167         hammer_blockmap_t rootmap;
168         hammer_inode_t ip;
169         hammer_off_t start_offset;
170
171         hammer_start_transaction_fls(&trans, hmp);
172         rootmap = &hmp->blockmap[HAMMER_ZONE_UNDO_INDEX];
173         start_offset = rootmap->next_offset;
174
175         while ((ip = TAILQ_FIRST(&hmp->flush_list)) != NULL) {
176                 /*
177                  * Stop when we hit a different flush group
178                  */
179                 if (ip->flush_group != hmp->flusher_act)
180                         break;
181
182                 /*
183                  * Remove the inode from the flush list and inherit
184                  * its reference, sync, and clean-up.
185                  */
186                 TAILQ_REMOVE(&hmp->flush_list, ip, flush_entry);
187                 ip->error = hammer_sync_inode(ip);
188                 hammer_flush_inode_done(ip);
189
190                 /*
191                  * XXX this breaks atomicy
192                  */
193                 if (hammer_must_finalize_undo(hmp)) {
194                         Debugger("Too many undos!!");
195                         hammer_flusher_finalize(&trans, start_offset);
196                         start_offset = rootmap->next_offset;
197                 }
198         }
199         hammer_flusher_finalize(&trans, start_offset);
200         hammer_done_transaction(&trans);
201 }
202
203 /*
204  * If the UNDO area gets over half full we have to flush it.  We can't
205  * afford the UNDO area becoming completely full as that would break
206  * the crash recovery atomicy.
207  */
208 static
209 int
210 hammer_must_finalize_undo(hammer_mount_t hmp)
211 {
212         if (hammer_undo_space(hmp) < hammer_undo_max(hmp) / 2) {
213                 kprintf("*");
214                 return(1);
215         } else {
216                 return(0);
217         }
218 }
219
220 /*
221  * To finalize the flush we finish flushing all undo and data buffers
222  * still present, then we update the volume header and flush it,
223  * then we flush out the mata-data (that can now be undone).
224  *
225  * Note that as long as the undo fifo's start and end points do not
226  * match, we always must at least update the volume header.
227  *
228  * The sync_lock is used by other threads to issue modifying operations
229  * to HAMMER media without crossing a synchronization boundary or messing
230  * up the media synchronization operation.  Specifically, the pruning
231  * the reblocking ioctls, and allowing the frontend strategy code to
232  * allocate media data space.
233  */
234 static
235 void
236 hammer_flusher_finalize(hammer_transaction_t trans, hammer_off_t start_offset)
237 {
238         hammer_mount_t hmp = trans->hmp;
239         hammer_volume_t root_volume = trans->rootvol;
240         hammer_blockmap_t rootmap;
241         hammer_io_t io;
242
243         hammer_lock_ex(&hmp->sync_lock);
244
245         /*
246          * Sync the blockmap to the root volume ondisk buffer and generate
247          * the appropriate undo record.  We have to generate the UNDO even
248          * though we flush the volume header along with the UNDO fifo update
249          * because the meta-data (including the volume header) is flushed
250          * after the fifo update, not before.
251          */
252         if (root_volume->io.modified) {
253                 hammer_modify_volume(trans, root_volume,
254                                     &root_volume->ondisk->vol0_blockmap,
255                                     sizeof(root_volume->ondisk->vol0_blockmap));
256                 bcopy(hmp->blockmap, root_volume->ondisk->vol0_blockmap,
257                       sizeof(root_volume->ondisk->vol0_blockmap));
258                 hammer_modify_volume_done(root_volume);
259         }
260
261         /*
262          * Flush undo bufs
263          */
264
265         hammer_clear_undo_history(hmp);
266
267         while ((io = TAILQ_FIRST(&hmp->undo_list)) != NULL) {
268                 KKASSERT(io->modify_refs == 0);
269                 hammer_ref(&io->lock);
270                 KKASSERT(io->type != HAMMER_STRUCTURE_VOLUME);
271                 hammer_io_flush(io);
272                 hammer_rel_buffer((hammer_buffer_t)io, 1);
273         }
274
275         /*
276          * Flush data bufs
277          */
278         while ((io = TAILQ_FIRST(&hmp->data_list)) != NULL) {
279                 KKASSERT(io->modify_refs == 0);
280                 hammer_ref(&io->lock);
281                 KKASSERT(io->type != HAMMER_STRUCTURE_VOLUME);
282                 hammer_io_flush(io);
283                 hammer_rel_buffer((hammer_buffer_t)io, 1);
284         }
285
286         /*
287          * Wait for I/O to complete
288          */
289         crit_enter();
290         while (hmp->io_running_count) {
291                 kprintf("W[%d]", hmp->io_running_count);
292                 tsleep(&hmp->io_running_count, 0, "hmrfl1", 0);
293         }
294         crit_exit();
295
296         /*
297          * Move the undo FIFO's markers and flush the root volume header.
298          *
299          * If a crash occurs while the root volume header is being written
300          * we just have to hope that the undo range has been updated.  It
301          * should be done in one I/O but XXX this won't be perfect.
302          */
303         rootmap = &hmp->blockmap[HAMMER_ZONE_UNDO_INDEX];
304         if (rootmap->first_offset != start_offset) {
305                 hammer_modify_volume(NULL, root_volume, NULL, 0);
306                 rootmap->first_offset = start_offset;
307                 hammer_modify_volume_done(root_volume);
308         }
309         if (root_volume->ondisk->vol0_next_tid != hmp->next_tid) {
310                 hammer_modify_volume(NULL, root_volume, NULL, 0);
311                 root_volume->ondisk->vol0_next_tid = hmp->next_tid;
312                 hammer_modify_volume_done(root_volume);
313         }
314         if (root_volume->io.modified)
315                 hammer_io_flush(&root_volume->io);
316
317         /*
318          * Wait for I/O to complete
319          */
320         crit_enter();
321         while (hmp->io_running_count) {
322                 tsleep(&hmp->io_running_count, 0, "hmrfl2", 0);
323         }
324         crit_exit();
325
326         /*
327          * Flush meta-data.  The meta-data will be undone if we crash
328          * so we can safely flush it asynchronously.
329          */
330         while ((io = TAILQ_FIRST(&hmp->meta_list)) != NULL) {
331                 KKASSERT(io->modify_refs == 0);
332                 hammer_ref(&io->lock);
333                 KKASSERT(io->type != HAMMER_STRUCTURE_VOLUME);
334                 hammer_io_flush(io);
335                 hammer_rel_buffer((hammer_buffer_t)io, 1);
336         }
337         hammer_unlock(&hmp->sync_lock);
338 }
339