HAMMER 39B/Many: Cleanup pass
[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.8 2008/04/29 04:43:08 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_mount_t hmp,
50                     hammer_volume_t root_volume, 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_seq;
59                 wakeup(&hmp->flusher_seq);
60                 while ((int)(seq - hmp->flusher_act) > 0)
61                         tsleep(&hmp->flusher_act, 0, "hmrfls", 0);
62         }
63 }
64
65 void
66 hammer_flusher_async(hammer_mount_t hmp)
67 {
68         if (hmp->flusher_td) {
69                 ++hmp->flusher_seq;
70                 wakeup(&hmp->flusher_seq);
71         }
72 }
73
74 void
75 hammer_flusher_create(hammer_mount_t hmp)
76 {
77         lwkt_create(hammer_flusher_thread, hmp, &hmp->flusher_td, NULL,
78                     0, -1, "hammer");
79 }
80
81 void
82 hammer_flusher_destroy(hammer_mount_t hmp)
83 {
84         if (hmp->flusher_td) {
85                 hmp->flusher_exiting = 1;
86                 ++hmp->flusher_seq;
87                 wakeup(&hmp->flusher_seq);
88                 while (hmp->flusher_td)
89                         tsleep(&hmp->flusher_exiting, 0, "hmrwex", 0);
90         }
91 }
92
93 static void
94 hammer_flusher_thread(void *arg)
95 {
96         hammer_mount_t hmp = arg;
97         int seq;
98
99         hmp->flusher_demark = kmalloc(sizeof(struct hammer_inode),
100                                       M_HAMMER, M_WAITOK | M_ZERO);
101         TAILQ_INSERT_TAIL(&hmp->flush_list, hmp->flusher_demark, flush_entry);
102
103         for (;;) {
104                 seq = hmp->flusher_seq;
105                 hammer_flusher_clean_loose_ios(hmp);
106                 hammer_flusher_flush(hmp);
107                 hammer_flusher_clean_loose_ios(hmp);
108                 hmp->flusher_act = seq;
109                 wakeup(&hmp->flusher_act);
110
111                 /*
112                  * Loop if more got queued after our demark.
113                  */
114                 if (TAILQ_NEXT(hmp->flusher_demark, flush_entry))
115                         continue;
116
117                 if (hmp->flusher_exiting)
118                         break;
119                 while (hmp->flusher_seq == hmp->flusher_act)
120                         tsleep(&hmp->flusher_seq, 0, "hmrwwa", 0);
121         }
122         TAILQ_REMOVE(&hmp->flush_list, hmp->flusher_demark, flush_entry);
123         kfree(hmp->flusher_demark, M_HAMMER);
124         hmp->flusher_demark = NULL;
125         hmp->flusher_td = NULL;
126         wakeup(&hmp->flusher_exiting);
127         lwkt_exit();
128 }
129
130 static void
131 hammer_flusher_clean_loose_ios(hammer_mount_t hmp)
132 {
133         hammer_buffer_t buffer;
134         hammer_io_t io;
135
136         /*
137          * loose ends - buffers without bp's aren't tracked by the kernel
138          * and can build up, so clean them out.  This can occur when an
139          * IO completes on a buffer with no references left.
140          */
141         while ((io = TAILQ_FIRST(&hmp->lose_list)) != NULL) {
142                 KKASSERT(io->mod_list == &hmp->lose_list);
143                 TAILQ_REMOVE(io->mod_list, io, mod_entry);
144                 io->mod_list = NULL;
145                 hammer_ref(&io->lock);
146                 buffer = (void *)io;
147                 hammer_rel_buffer(buffer, 0);
148         }
149 }
150
151 /*
152  * Flush stuff
153  */
154 static void
155 hammer_flusher_flush(hammer_mount_t hmp)
156 {
157         hammer_volume_t root_volume;
158         hammer_blockmap_t rootmap;
159         hammer_inode_t ip;
160         hammer_off_t start_offset;
161         int error;
162
163         root_volume = hammer_get_root_volume(hmp, &error);
164         rootmap = &hmp->blockmap[HAMMER_ZONE_UNDO_INDEX];
165         start_offset = rootmap->next_offset;
166
167         if (hammer_debug_general & 0x00010000)
168                 kprintf("x");
169
170         TAILQ_REMOVE(&hmp->flush_list, hmp->flusher_demark, flush_entry);
171         TAILQ_INSERT_TAIL(&hmp->flush_list, hmp->flusher_demark, flush_entry);
172
173         while ((ip = TAILQ_FIRST(&hmp->flush_list)) != hmp->flusher_demark) {
174                 TAILQ_REMOVE(&hmp->flush_list, ip, flush_entry);
175
176                 /*
177                  * We inherit the inode ref from the flush list
178                  */
179                 ip->error = hammer_sync_inode(ip, (ip->vp ? 0 : 1));
180                 hammer_flush_inode_done(ip);
181                 if (hmp->locked_dirty_count > 64 ||
182                     hammer_must_finalize_undo(hmp)) {
183                         hammer_flusher_finalize(hmp, root_volume, start_offset);
184                         start_offset = rootmap->next_offset;
185                 }
186         }
187         hammer_flusher_finalize(hmp, root_volume, start_offset);
188         hammer_rel_volume(root_volume, 0);
189 }
190
191 /*
192  * If the UNDO area gets over half full we have to flush it.  We can't
193  * afford the UNDO area becoming completely full as that would break
194  * the crash recovery atomicy.
195  */
196 static
197 int
198 hammer_must_finalize_undo(hammer_mount_t hmp)
199 {
200         hammer_blockmap_t rootmap;
201         int bytes;
202         int max_bytes;
203
204         rootmap = &hmp->blockmap[HAMMER_ZONE_UNDO_INDEX];
205
206         if (rootmap->first_offset <= rootmap->next_offset) {
207                 bytes = (int)(rootmap->next_offset - rootmap->first_offset);
208         } else {
209                 bytes = (int)(rootmap->alloc_offset - rootmap->first_offset +
210                               rootmap->next_offset);
211         }
212         max_bytes = (int)(rootmap->alloc_offset & HAMMER_OFF_SHORT_MASK);
213         if (bytes > max_bytes / 2)
214                 kprintf("*");
215         return (bytes > max_bytes / 2);
216 }
217
218 /*
219  * To finalize the flush we finish flushing all undo and data buffers
220  * still present, then we update the volume header and flush it,
221  * then we flush out the mata-data (that can now be undone).
222  *
223  * Note that as long as the undo fifo's start and end points do not
224  * match, we always must at least update the volume header.
225  *
226  * The sync_lock is used by other threads to issue modifying operations
227  * to HAMMER media without crossing a synchronization boundary or messing
228  * up the media synchronization operation.  Specifically, the pruning
229  * the reblocking ioctls, and allowing the frontend strategy code to
230  * allocate media data space.
231  */
232 static
233 void
234 hammer_flusher_finalize(hammer_mount_t hmp, hammer_volume_t root_volume,
235                         hammer_off_t start_offset)
236 {
237         hammer_blockmap_t rootmap;
238         hammer_io_t io;
239
240         hammer_lock_ex(&hmp->sync_lock);
241
242         /*
243          * Flush undo bufs
244          */
245         while ((io = TAILQ_FIRST(&hmp->undo_list)) != NULL) {
246                 KKASSERT(io->modify_refs == 0);
247                 hammer_ref(&io->lock);
248                 KKASSERT(io->type != HAMMER_STRUCTURE_VOLUME);
249                 hammer_io_flush(io);
250                 hammer_rel_buffer((hammer_buffer_t)io, 1);
251         }
252
253         /*
254          * Flush data bufs
255          */
256         while ((io = TAILQ_FIRST(&hmp->data_list)) != NULL) {
257                 KKASSERT(io->modify_refs == 0);
258                 hammer_ref(&io->lock);
259                 KKASSERT(io->type != HAMMER_STRUCTURE_VOLUME);
260                 hammer_io_flush(io);
261                 hammer_rel_buffer((hammer_buffer_t)io, 1);
262         }
263
264         /*
265          * Wait for I/O to complete
266          */
267         crit_enter();
268         while (hmp->io_running_count) {
269                 kprintf("W[%d]", hmp->io_running_count);
270                 tsleep(&hmp->io_running_count, 0, "hmrfl1", 0);
271         }
272         crit_exit();
273
274         /*
275          * Update the volume header
276          */
277         rootmap = &hmp->blockmap[HAMMER_ZONE_UNDO_INDEX];
278         if (rootmap->first_offset != start_offset) {
279                 hammer_modify_volume(NULL, root_volume, NULL, 0);
280                 rootmap->first_offset = start_offset;
281                 hammer_modify_volume_done(root_volume);
282         }
283         if (root_volume->ondisk->vol0_next_tid != hmp->next_tid) {
284                 hammer_modify_volume(NULL, root_volume, NULL, 0);
285                 root_volume->ondisk->vol0_next_tid = hmp->next_tid;
286                 hammer_modify_volume_done(root_volume);
287         }
288
289         /*
290          * Sync our cached blockmap array with the one in the root
291          * volume header.
292          */
293         if (root_volume->io.modified) {
294                 bcopy(hmp->blockmap, root_volume->ondisk->vol0_blockmap,
295                       sizeof(hmp->blockmap));
296                 hammer_io_flush(&root_volume->io);
297         }
298
299         /*
300          * Wait for I/O to complete
301          */
302         crit_enter();
303         while (hmp->io_running_count) {
304                 tsleep(&hmp->io_running_count, 0, "hmrfl2", 0);
305         }
306         crit_exit();
307
308         /*
309          * Flush meta-data
310          */
311         while ((io = TAILQ_FIRST(&hmp->meta_list)) != NULL) {
312                 KKASSERT(io->modify_refs == 0);
313                 hammer_ref(&io->lock);
314                 KKASSERT(io->type != HAMMER_STRUCTURE_VOLUME);
315                 hammer_io_flush(io);
316                 hammer_rel_buffer((hammer_buffer_t)io, 1);
317         }
318         hammer_unlock(&hmp->sync_lock);
319 }
320