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