HAMMER 53D/Many: Stabilization
[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.21 2008/06/10 00:40:31 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_flush_inode(hammer_inode_t ip,
49                                         hammer_transaction_t trans);
50 static int hammer_must_finalize_undo(hammer_mount_t hmp);
51 static void hammer_flusher_finalize(hammer_transaction_t trans, int final);
52
53 #define HAMMER_FLUSHER_IMMEDIATE        16
54
55 void
56 hammer_flusher_sync(hammer_mount_t hmp)
57 {
58         int seq;
59
60         if (hmp->flusher_td) {
61                 seq = hmp->flusher_next;
62                 if (hmp->flusher_signal++ == 0)
63                         wakeup(&hmp->flusher_signal);
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 = 16;
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                 hammer_flusher_clean_loose_ios(hmp);
113                 hammer_flusher_flush(hmp);
114                 hammer_flusher_clean_loose_ios(hmp);
115                 hmp->flusher_done = hmp->flusher_act;
116
117                 wakeup(&hmp->flusher_done);
118
119                 /*
120                  * Wait for activity.
121                  */
122                 if (hmp->flusher_exiting && TAILQ_EMPTY(&hmp->flush_list))
123                         break;
124
125                 /*
126                  * This is a hack until we can dispose of frontend buffer
127                  * cache buffers on the frontend.
128                  */
129                 while (hmp->flusher_signal == 0)
130                         tsleep(&hmp->flusher_signal, 0, "hmrwwa", 0);
131                 hmp->flusher_signal = 0;
132         }
133         hmp->flusher_td = NULL;
134         wakeup(&hmp->flusher_exiting);
135         lwkt_exit();
136 }
137
138 static void
139 hammer_flusher_clean_loose_ios(hammer_mount_t hmp)
140 {
141         hammer_buffer_t buffer;
142         hammer_io_t io;
143
144         /*
145          * loose ends - buffers without bp's aren't tracked by the kernel
146          * and can build up, so clean them out.  This can occur when an
147          * IO completes on a buffer with no references left.
148          */
149         while ((io = TAILQ_FIRST(&hmp->lose_list)) != NULL) {
150                 KKASSERT(io->mod_list == &hmp->lose_list);
151                 TAILQ_REMOVE(io->mod_list, io, mod_entry);
152                 io->mod_list = NULL;
153                 hammer_ref(&io->lock);
154                 buffer = (void *)io;
155                 hammer_rel_buffer(buffer, 0);
156         }
157 }
158
159 /*
160  * Flush all inodes in the current flush group.
161  */
162 static void
163 hammer_flusher_flush(hammer_mount_t hmp)
164 {
165         struct hammer_transaction trans;
166         hammer_inode_t ip;
167         hammer_reserve_t resv;
168
169         /*
170          * Flush the inodes
171          */
172         hammer_start_transaction_fls(&trans, hmp);
173         while ((ip = TAILQ_FIRST(&hmp->flush_list)) != NULL) {
174                 if (ip->flush_group != hmp->flusher_act)
175                         break;
176                 TAILQ_REMOVE(&hmp->flush_list, ip, flush_entry);
177                 hammer_flusher_flush_inode(ip, &trans);
178         }
179         hammer_flusher_finalize(&trans, 1);
180         hmp->flusher_tid = trans.tid;
181
182         /*
183          * Clean up any freed big-blocks (typically zone-2). 
184          * resv->flush_group is typically set several flush groups ahead
185          * of the free to ensure that the freed block is not reused until
186          * it can no longer be reused.
187          */
188         while ((resv = TAILQ_FIRST(&hmp->delay_list)) != NULL) {
189                 if (resv->flush_group != hmp->flusher_act)
190                         break;
191                 TAILQ_REMOVE(&hmp->delay_list, resv, delay_entry);
192                 hammer_blockmap_reserve_complete(hmp, resv);
193         }
194
195
196         hammer_done_transaction(&trans);
197 }
198
199 /*
200  * Flush a single inode that is part of a flush group.
201  */
202 static
203 void
204 hammer_flusher_flush_inode(hammer_inode_t ip, hammer_transaction_t trans)
205 {
206         hammer_mount_t hmp = ip->hmp;
207
208         /*hammer_lock_ex(&ip->lock);*/
209         ip->error = hammer_sync_inode(ip);
210         hammer_flush_inode_done(ip);
211         /*hammer_unlock(&ip->lock);*/
212
213         if (hammer_must_finalize_undo(hmp)) {
214                 kprintf("HAMMER: Warning: UNDO area too small!");
215                 hammer_flusher_finalize(trans, 1);
216         } else if (trans->hmp->locked_dirty_count +
217                    trans->hmp->io_running_count > hammer_limit_dirtybufs) {
218                 kprintf("t");
219                 hammer_flusher_finalize(trans, 0);
220         }
221 }
222
223 /*
224  * If the UNDO area gets over half full we have to flush it.  We can't
225  * afford the UNDO area becoming completely full as that would break
226  * the crash recovery atomicy.
227  */
228 static
229 int
230 hammer_must_finalize_undo(hammer_mount_t hmp)
231 {
232         if (hammer_undo_space(hmp) < hammer_undo_max(hmp) / 2) {
233                 hkprintf("*");
234                 return(1);
235         } else {
236                 return(0);
237         }
238 }
239
240 /*
241  * Flush all pending UNDOs, wait for write completion, update the volume
242  * header with the new UNDO end position, and flush it.  Then
243  * asynchronously flush the meta-data.
244  *
245  * If this is the last finalization in a flush group we also synchronize
246  * our cached blockmap and set hmp->flusher_undo_start and our cached undo
247  * fifo first_offset so the next flush resets the FIFO pointers.
248  */
249 static
250 void
251 hammer_flusher_finalize(hammer_transaction_t trans, int final)
252 {
253         hammer_volume_t root_volume;
254         hammer_blockmap_t cundomap, dundomap;
255         hammer_mount_t hmp;
256         hammer_io_t io;
257         int count;
258         int i;
259
260         hmp = trans->hmp;
261         root_volume = trans->rootvol;
262
263         /*
264          * Flush data buffers.  This can occur asynchronously and at any
265          * time.  We must interlock against the frontend direct-data write
266          * but do not have to acquire the sync-lock yet.
267          */
268         count = 0;
269         while ((io = TAILQ_FIRST(&hmp->data_list)) != NULL) {
270                 hammer_ref(&io->lock);
271                 hammer_io_write_interlock(io);
272                 KKASSERT(io->type != HAMMER_STRUCTURE_VOLUME);
273                 hammer_io_flush(io);
274                 hammer_io_done_interlock(io);
275                 hammer_rel_buffer((hammer_buffer_t)io, 0);
276                 ++count;
277         }
278
279         /*
280          * The sync-lock is required for the remaining sequence.  This lock
281          * prevents meta-data from being modified.
282          */
283         hammer_sync_lock_ex(trans);
284
285         /*
286          * If we have been asked to finalize the volume header sync the
287          * cached blockmap to the on-disk blockmap.  Generate an UNDO
288          * record for the update.
289          */
290         if (final) {
291                 cundomap = &hmp->blockmap[0];
292                 dundomap = &root_volume->ondisk->vol0_blockmap[0];
293                 if (root_volume->io.modified) {
294                         hammer_modify_volume(trans, root_volume,
295                                              dundomap, sizeof(hmp->blockmap));
296                         for (i = 0; i < HAMMER_MAX_ZONES; ++i)
297                                 hammer_crc_set_blockmap(&cundomap[i]);
298                         bcopy(cundomap, dundomap, sizeof(hmp->blockmap));
299                         hammer_modify_volume_done(root_volume);
300                 }
301         }
302
303         /*
304          * Flush UNDOs
305          */
306         count = 0;
307         while ((io = TAILQ_FIRST(&hmp->undo_list)) != NULL) {
308                 KKASSERT(io->modify_refs == 0);
309                 hammer_ref(&io->lock);
310                 KKASSERT(io->type != HAMMER_STRUCTURE_VOLUME);
311                 hammer_io_flush(io);
312                 hammer_rel_buffer((hammer_buffer_t)io, 0);
313                 ++count;
314         }
315
316         /*
317          * Wait for I/Os to complete
318          */
319         crit_enter();
320         while (hmp->io_running_count)
321                 tsleep(&hmp->io_running_count, 0, "hmrfl1", 0);
322         crit_exit();
323
324         /*
325          * Update the on-disk volume header with new UNDO FIFO end position
326          * (do not generate new UNDO records for this change).  We have to
327          * do this for the UNDO FIFO whether (final) is set or not.
328          *
329          * Also update the on-disk next_tid field.  This does not require
330          * an UNDO.  However, because our TID is generated before we get
331          * the sync lock another sync may have beat us to the punch.
332          *
333          * The volume header will be flushed out synchronously.
334          */
335         dundomap = &root_volume->ondisk->vol0_blockmap[HAMMER_ZONE_UNDO_INDEX];
336         cundomap = &hmp->blockmap[HAMMER_ZONE_UNDO_INDEX];
337
338         if (dundomap->first_offset != cundomap->first_offset ||
339             dundomap->next_offset != cundomap->next_offset) {
340                 hammer_modify_volume(NULL, root_volume, NULL, 0);
341                 dundomap->first_offset = cundomap->first_offset;
342                 dundomap->next_offset = cundomap->next_offset;
343                 hammer_crc_set_blockmap(dundomap);
344                 hammer_crc_set_volume(root_volume->ondisk);
345                 if (root_volume->ondisk->vol0_next_tid < trans->tid)
346                         root_volume->ondisk->vol0_next_tid = trans->tid;
347                 hammer_modify_volume_done(root_volume);
348         }
349
350         if (root_volume->io.modified) {
351                 kprintf("S");
352                 hammer_io_flush(&root_volume->io);
353         }
354
355         /*
356          * Wait for I/Os to complete
357          */
358         crit_enter();
359         while (hmp->io_running_count)
360                 tsleep(&hmp->io_running_count, 0, "hmrfl2", 0);
361         crit_exit();
362
363         /*
364          * Flush meta-data.  The meta-data will be undone if we crash
365          * so we can safely flush it asynchronously.
366          *
367          * Repeated catchups will wind up flushing this update's meta-data
368          * and the UNDO buffers for the next update simultaniously.  This
369          * is ok.
370          */
371         count = 0;
372         while ((io = TAILQ_FIRST(&hmp->meta_list)) != NULL) {
373                 KKASSERT(io->modify_refs == 0);
374                 hammer_ref(&io->lock);
375                 KKASSERT(io->type != HAMMER_STRUCTURE_VOLUME);
376                 hammer_io_flush(io);
377                 hammer_rel_buffer((hammer_buffer_t)io, 0);
378                 ++count;
379         }
380
381         /*
382          * If this is the final finalization for the flush group set
383          * up for the next sequence by setting a new first_offset in
384          * our cached blockmap and
385          * clearing the undo history.
386          */
387         if (final) {
388                 cundomap = &hmp->blockmap[HAMMER_ZONE_UNDO_INDEX];
389                 cundomap->first_offset = cundomap->next_offset;
390                 hammer_clear_undo_history(hmp);
391         }
392
393         hammer_sync_unlock(trans);
394 }
395