xref: /dflybsd-src/sys/vfs/hammer/hammer_flusher.c (revision 1f07f68660b83c5d87d56552b65ddc489b520a53)
1059819e3SMatthew Dillon /*
2059819e3SMatthew Dillon  * Copyright (c) 2008 The DragonFly Project.  All rights reserved.
3059819e3SMatthew Dillon  *
4059819e3SMatthew Dillon  * This code is derived from software contributed to The DragonFly Project
5059819e3SMatthew Dillon  * by Matthew Dillon <dillon@backplane.com>
6059819e3SMatthew Dillon  *
7059819e3SMatthew Dillon  * Redistribution and use in source and binary forms, with or without
8059819e3SMatthew Dillon  * modification, are permitted provided that the following conditions
9059819e3SMatthew Dillon  * are met:
10059819e3SMatthew Dillon  *
11059819e3SMatthew Dillon  * 1. Redistributions of source code must retain the above copyright
12059819e3SMatthew Dillon  *    notice, this list of conditions and the following disclaimer.
13059819e3SMatthew Dillon  * 2. Redistributions in binary form must reproduce the above copyright
14059819e3SMatthew Dillon  *    notice, this list of conditions and the following disclaimer in
15059819e3SMatthew Dillon  *    the documentation and/or other materials provided with the
16059819e3SMatthew Dillon  *    distribution.
17059819e3SMatthew Dillon  * 3. Neither the name of The DragonFly Project nor the names of its
18059819e3SMatthew Dillon  *    contributors may be used to endorse or promote products derived
19059819e3SMatthew Dillon  *    from this software without specific, prior written permission.
20059819e3SMatthew Dillon  *
21059819e3SMatthew Dillon  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22059819e3SMatthew Dillon  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23059819e3SMatthew Dillon  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24059819e3SMatthew Dillon  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25059819e3SMatthew Dillon  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26059819e3SMatthew Dillon  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27059819e3SMatthew Dillon  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28059819e3SMatthew Dillon  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29059819e3SMatthew Dillon  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30059819e3SMatthew Dillon  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31059819e3SMatthew Dillon  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32059819e3SMatthew Dillon  * SUCH DAMAGE.
33059819e3SMatthew Dillon  *
34*1f07f686SMatthew Dillon  * $DragonFly: src/sys/vfs/hammer/hammer_flusher.c,v 1.9 2008/05/02 01:00:42 dillon Exp $
35059819e3SMatthew Dillon  */
36059819e3SMatthew Dillon /*
37059819e3SMatthew Dillon  * HAMMER dependancy flusher thread
38059819e3SMatthew Dillon  *
39059819e3SMatthew Dillon  * Meta data updates create buffer dependancies which are arranged as a
40059819e3SMatthew Dillon  * hierarchy of lists.
41059819e3SMatthew Dillon  */
42059819e3SMatthew Dillon 
43059819e3SMatthew Dillon #include "hammer.h"
44059819e3SMatthew Dillon 
45059819e3SMatthew Dillon static void hammer_flusher_thread(void *arg);
4610a5d1baSMatthew Dillon static void hammer_flusher_clean_loose_ios(hammer_mount_t hmp);
47059819e3SMatthew Dillon static void hammer_flusher_flush(hammer_mount_t hmp);
480729c8c8SMatthew Dillon static int hammer_must_finalize_undo(hammer_mount_t hmp);
4910a5d1baSMatthew Dillon static void hammer_flusher_finalize(hammer_mount_t hmp,
5010a5d1baSMatthew Dillon 		    hammer_volume_t root_volume, hammer_off_t start_offset);
51059819e3SMatthew Dillon 
52059819e3SMatthew Dillon void
53059819e3SMatthew Dillon hammer_flusher_sync(hammer_mount_t hmp)
54059819e3SMatthew Dillon {
55059819e3SMatthew Dillon 	int seq;
56059819e3SMatthew Dillon 
57f90dde4cSMatthew Dillon 	if (hmp->flusher_td) {
58*1f07f686SMatthew Dillon 		seq = hmp->flusher_next;
59*1f07f686SMatthew Dillon 		if (hmp->flusher_signal == 0) {
60*1f07f686SMatthew Dillon 			hmp->flusher_signal = 1;
61*1f07f686SMatthew Dillon 			wakeup(&hmp->flusher_signal);
62*1f07f686SMatthew Dillon 		}
63*1f07f686SMatthew Dillon 		while ((int)(seq - hmp->flusher_done) > 0)
64*1f07f686SMatthew Dillon 			tsleep(&hmp->flusher_done, 0, "hmrfls", 0);
65059819e3SMatthew Dillon 	}
66f90dde4cSMatthew Dillon }
67059819e3SMatthew Dillon 
68059819e3SMatthew Dillon void
69059819e3SMatthew Dillon hammer_flusher_async(hammer_mount_t hmp)
70059819e3SMatthew Dillon {
71f90dde4cSMatthew Dillon 	if (hmp->flusher_td) {
72*1f07f686SMatthew Dillon 		if (hmp->flusher_signal == 0) {
73*1f07f686SMatthew Dillon 			hmp->flusher_signal = 1;
74*1f07f686SMatthew Dillon 			wakeup(&hmp->flusher_signal);
75*1f07f686SMatthew Dillon 		}
76059819e3SMatthew Dillon 	}
77f90dde4cSMatthew Dillon }
78059819e3SMatthew Dillon 
79059819e3SMatthew Dillon void
80059819e3SMatthew Dillon hammer_flusher_create(hammer_mount_t hmp)
81059819e3SMatthew Dillon {
82*1f07f686SMatthew Dillon 	hmp->flusher_signal = 0;
83*1f07f686SMatthew Dillon 	hmp->flusher_act = 0;
84*1f07f686SMatthew Dillon 	hmp->flusher_done = 0;
85*1f07f686SMatthew Dillon 	hmp->flusher_next = 1;
86059819e3SMatthew Dillon 	lwkt_create(hammer_flusher_thread, hmp, &hmp->flusher_td, NULL,
87059819e3SMatthew Dillon 		    0, -1, "hammer");
88059819e3SMatthew Dillon }
89059819e3SMatthew Dillon 
90059819e3SMatthew Dillon void
91059819e3SMatthew Dillon hammer_flusher_destroy(hammer_mount_t hmp)
92059819e3SMatthew Dillon {
93f90dde4cSMatthew Dillon 	if (hmp->flusher_td) {
94059819e3SMatthew Dillon 		hmp->flusher_exiting = 1;
95*1f07f686SMatthew Dillon 		while (hmp->flusher_td) {
96*1f07f686SMatthew Dillon 			hmp->flusher_signal = 1;
97*1f07f686SMatthew Dillon 			wakeup(&hmp->flusher_signal);
98059819e3SMatthew Dillon 			tsleep(&hmp->flusher_exiting, 0, "hmrwex", 0);
99059819e3SMatthew Dillon 		}
100f90dde4cSMatthew Dillon 	}
101*1f07f686SMatthew Dillon }
102059819e3SMatthew Dillon 
103059819e3SMatthew Dillon static void
104059819e3SMatthew Dillon hammer_flusher_thread(void *arg)
105059819e3SMatthew Dillon {
106059819e3SMatthew Dillon 	hammer_mount_t hmp = arg;
1070729c8c8SMatthew Dillon 
108059819e3SMatthew Dillon 	for (;;) {
109*1f07f686SMatthew Dillon 		hmp->flusher_act = hmp->flusher_next;
110*1f07f686SMatthew Dillon 		++hmp->flusher_next;
111*1f07f686SMatthew Dillon 		kprintf("F");
11210a5d1baSMatthew Dillon 		hammer_flusher_clean_loose_ios(hmp);
113059819e3SMatthew Dillon 		hammer_flusher_flush(hmp);
11410a5d1baSMatthew Dillon 		hammer_flusher_clean_loose_ios(hmp);
115*1f07f686SMatthew Dillon 		hmp->flusher_done = hmp->flusher_act;
116*1f07f686SMatthew Dillon 
117*1f07f686SMatthew Dillon 		wakeup(&hmp->flusher_done);
118c32a6806SMatthew Dillon 
119c32a6806SMatthew Dillon 		/*
120*1f07f686SMatthew Dillon 		 * Wait for activity.
121c32a6806SMatthew Dillon 		 */
122*1f07f686SMatthew Dillon 		if (hmp->flusher_exiting && TAILQ_EMPTY(&hmp->flush_list))
123059819e3SMatthew Dillon 			break;
124*1f07f686SMatthew Dillon 		kprintf("E");
125*1f07f686SMatthew Dillon 
126*1f07f686SMatthew Dillon 		while (hmp->flusher_signal == 0 &&
127*1f07f686SMatthew Dillon 		       TAILQ_EMPTY(&hmp->flush_list)) {
128*1f07f686SMatthew Dillon 			tsleep(&hmp->flusher_signal, 0, "hmrwwa", 0);
129059819e3SMatthew Dillon 		}
130*1f07f686SMatthew Dillon 		hmp->flusher_signal = 0;
131*1f07f686SMatthew Dillon 	}
132059819e3SMatthew Dillon 	hmp->flusher_td = NULL;
133059819e3SMatthew Dillon 	wakeup(&hmp->flusher_exiting);
134059819e3SMatthew Dillon 	lwkt_exit();
135059819e3SMatthew Dillon }
136059819e3SMatthew Dillon 
13710a5d1baSMatthew Dillon static void
13810a5d1baSMatthew Dillon hammer_flusher_clean_loose_ios(hammer_mount_t hmp)
13910a5d1baSMatthew Dillon {
14010a5d1baSMatthew Dillon 	hammer_buffer_t buffer;
14110a5d1baSMatthew Dillon 	hammer_io_t io;
14210a5d1baSMatthew Dillon 
14310a5d1baSMatthew Dillon 	/*
14410a5d1baSMatthew Dillon 	 * loose ends - buffers without bp's aren't tracked by the kernel
14510a5d1baSMatthew Dillon 	 * and can build up, so clean them out.  This can occur when an
14610a5d1baSMatthew Dillon 	 * IO completes on a buffer with no references left.
14710a5d1baSMatthew Dillon 	 */
14810a5d1baSMatthew Dillon 	while ((io = TAILQ_FIRST(&hmp->lose_list)) != NULL) {
14910a5d1baSMatthew Dillon 		KKASSERT(io->mod_list == &hmp->lose_list);
15010a5d1baSMatthew Dillon 		TAILQ_REMOVE(io->mod_list, io, mod_entry);
15110a5d1baSMatthew Dillon 		io->mod_list = NULL;
15210a5d1baSMatthew Dillon 		hammer_ref(&io->lock);
15310a5d1baSMatthew Dillon 		buffer = (void *)io;
15410a5d1baSMatthew Dillon 		hammer_rel_buffer(buffer, 0);
15510a5d1baSMatthew Dillon 	}
15610a5d1baSMatthew Dillon }
15710a5d1baSMatthew Dillon 
158059819e3SMatthew Dillon /*
159059819e3SMatthew Dillon  * Flush stuff
160059819e3SMatthew Dillon  */
161059819e3SMatthew Dillon static void
162059819e3SMatthew Dillon hammer_flusher_flush(hammer_mount_t hmp)
163059819e3SMatthew Dillon {
16410a5d1baSMatthew Dillon 	hammer_volume_t root_volume;
16510a5d1baSMatthew Dillon 	hammer_blockmap_t rootmap;
166059819e3SMatthew Dillon 	hammer_inode_t ip;
16710a5d1baSMatthew Dillon 	hammer_off_t start_offset;
16810a5d1baSMatthew Dillon 	int error;
16910a5d1baSMatthew Dillon 
17010a5d1baSMatthew Dillon 	root_volume = hammer_get_root_volume(hmp, &error);
1710729c8c8SMatthew Dillon 	rootmap = &hmp->blockmap[HAMMER_ZONE_UNDO_INDEX];
17210a5d1baSMatthew Dillon 	start_offset = rootmap->next_offset;
173059819e3SMatthew Dillon 
174*1f07f686SMatthew Dillon 	while ((ip = TAILQ_FIRST(&hmp->flush_list)) != NULL) {
175*1f07f686SMatthew Dillon 		/*
176*1f07f686SMatthew Dillon 		 * Stop when we hit a different flush group
177*1f07f686SMatthew Dillon 		 */
178*1f07f686SMatthew Dillon 		if (ip->flush_group != hmp->flusher_act)
179*1f07f686SMatthew Dillon 			break;
180059819e3SMatthew Dillon 
181059819e3SMatthew Dillon 		/*
182*1f07f686SMatthew Dillon 		 * Remove the inode from the flush list and inherit
183*1f07f686SMatthew Dillon 		 * its reference, sync, and clean-up.
184059819e3SMatthew Dillon 		 */
185*1f07f686SMatthew Dillon 		TAILQ_REMOVE(&hmp->flush_list, ip, flush_entry);
186*1f07f686SMatthew Dillon 		kprintf("s");
187*1f07f686SMatthew Dillon 		ip->error = hammer_sync_inode(ip);
188b84de5afSMatthew Dillon 		hammer_flush_inode_done(ip);
189*1f07f686SMatthew Dillon 
190*1f07f686SMatthew Dillon 		/*
191*1f07f686SMatthew Dillon 		 * XXX this breaks atomicy
192*1f07f686SMatthew Dillon 		 */
193*1f07f686SMatthew Dillon 		if (hammer_must_finalize_undo(hmp)) {
194*1f07f686SMatthew Dillon 			Debugger("Too many undos!!");
19510a5d1baSMatthew Dillon 			hammer_flusher_finalize(hmp, root_volume, start_offset);
19610a5d1baSMatthew Dillon 			start_offset = rootmap->next_offset;
197059819e3SMatthew Dillon 		}
198059819e3SMatthew Dillon 	}
19910a5d1baSMatthew Dillon 	hammer_flusher_finalize(hmp, root_volume, start_offset);
20010a5d1baSMatthew Dillon 	hammer_rel_volume(root_volume, 0);
20110a5d1baSMatthew Dillon }
202059819e3SMatthew Dillon 
20310a5d1baSMatthew Dillon /*
204ec4e8497SMatthew Dillon  * If the UNDO area gets over half full we have to flush it.  We can't
205ec4e8497SMatthew Dillon  * afford the UNDO area becoming completely full as that would break
206ec4e8497SMatthew Dillon  * the crash recovery atomicy.
207ec4e8497SMatthew Dillon  */
208ec4e8497SMatthew Dillon static
209ec4e8497SMatthew Dillon int
2100729c8c8SMatthew Dillon hammer_must_finalize_undo(hammer_mount_t hmp)
211ec4e8497SMatthew Dillon {
212*1f07f686SMatthew Dillon 	if (hammer_undo_space(hmp) < hammer_undo_max(hmp) / 2) {
213ec4e8497SMatthew Dillon 		kprintf("*");
214*1f07f686SMatthew Dillon 		return(1);
215*1f07f686SMatthew Dillon 	} else {
216*1f07f686SMatthew Dillon 		return(0);
217*1f07f686SMatthew Dillon 	}
218ec4e8497SMatthew Dillon }
219ec4e8497SMatthew Dillon 
220ec4e8497SMatthew Dillon /*
22110a5d1baSMatthew Dillon  * To finalize the flush we finish flushing all undo and data buffers
22210a5d1baSMatthew Dillon  * still present, then we update the volume header and flush it,
22310a5d1baSMatthew Dillon  * then we flush out the mata-data (that can now be undone).
22410a5d1baSMatthew Dillon  *
22510a5d1baSMatthew Dillon  * Note that as long as the undo fifo's start and end points do not
22610a5d1baSMatthew Dillon  * match, we always must at least update the volume header.
2279480ff55SMatthew Dillon  *
2289480ff55SMatthew Dillon  * The sync_lock is used by other threads to issue modifying operations
2299480ff55SMatthew Dillon  * to HAMMER media without crossing a synchronization boundary or messing
2309480ff55SMatthew Dillon  * up the media synchronization operation.  Specifically, the pruning
2319480ff55SMatthew Dillon  * the reblocking ioctls, and allowing the frontend strategy code to
2329480ff55SMatthew Dillon  * allocate media data space.
23310a5d1baSMatthew Dillon  */
23410a5d1baSMatthew Dillon static
23510a5d1baSMatthew Dillon void
23610a5d1baSMatthew Dillon hammer_flusher_finalize(hammer_mount_t hmp, hammer_volume_t root_volume,
23710a5d1baSMatthew Dillon 			hammer_off_t start_offset)
238059819e3SMatthew Dillon {
239059819e3SMatthew Dillon 	hammer_blockmap_t rootmap;
24010a5d1baSMatthew Dillon 	hammer_io_t io;
24110a5d1baSMatthew Dillon 
2429480ff55SMatthew Dillon 	hammer_lock_ex(&hmp->sync_lock);
2439480ff55SMatthew Dillon 
244059819e3SMatthew Dillon 	/*
24510a5d1baSMatthew Dillon 	 * Flush undo bufs
246059819e3SMatthew Dillon 	 */
24710a5d1baSMatthew Dillon 	while ((io = TAILQ_FIRST(&hmp->undo_list)) != NULL) {
24810a5d1baSMatthew Dillon 		KKASSERT(io->modify_refs == 0);
24910a5d1baSMatthew Dillon 		hammer_ref(&io->lock);
25010a5d1baSMatthew Dillon 		KKASSERT(io->type != HAMMER_STRUCTURE_VOLUME);
25110a5d1baSMatthew Dillon 		hammer_io_flush(io);
25210a5d1baSMatthew Dillon 		hammer_rel_buffer((hammer_buffer_t)io, 1);
253059819e3SMatthew Dillon 	}
254059819e3SMatthew Dillon 
255059819e3SMatthew Dillon 	/*
25610a5d1baSMatthew Dillon 	 * Flush data bufs
257059819e3SMatthew Dillon 	 */
25810a5d1baSMatthew Dillon 	while ((io = TAILQ_FIRST(&hmp->data_list)) != NULL) {
25910a5d1baSMatthew Dillon 		KKASSERT(io->modify_refs == 0);
26010a5d1baSMatthew Dillon 		hammer_ref(&io->lock);
26110a5d1baSMatthew Dillon 		KKASSERT(io->type != HAMMER_STRUCTURE_VOLUME);
26210a5d1baSMatthew Dillon 		hammer_io_flush(io);
26310a5d1baSMatthew Dillon 		hammer_rel_buffer((hammer_buffer_t)io, 1);
264059819e3SMatthew Dillon 	}
265059819e3SMatthew Dillon 
266059819e3SMatthew Dillon 	/*
267f90dde4cSMatthew Dillon 	 * Wait for I/O to complete
268059819e3SMatthew Dillon 	 */
269f90dde4cSMatthew Dillon 	crit_enter();
270f90dde4cSMatthew Dillon 	while (hmp->io_running_count) {
2719480ff55SMatthew Dillon 		kprintf("W[%d]", hmp->io_running_count);
272f90dde4cSMatthew Dillon 		tsleep(&hmp->io_running_count, 0, "hmrfl1", 0);
273f90dde4cSMatthew Dillon 	}
274f90dde4cSMatthew Dillon 	crit_exit();
275059819e3SMatthew Dillon 
276059819e3SMatthew Dillon 	/*
27710a5d1baSMatthew Dillon 	 * Update the volume header
278059819e3SMatthew Dillon 	 */
2790729c8c8SMatthew Dillon 	rootmap = &hmp->blockmap[HAMMER_ZONE_UNDO_INDEX];
28010a5d1baSMatthew Dillon 	if (rootmap->first_offset != start_offset) {
28110a5d1baSMatthew Dillon 		hammer_modify_volume(NULL, root_volume, NULL, 0);
28210a5d1baSMatthew Dillon 		rootmap->first_offset = start_offset;
28310a5d1baSMatthew Dillon 		hammer_modify_volume_done(root_volume);
2840729c8c8SMatthew Dillon 	}
2850729c8c8SMatthew Dillon 	if (root_volume->ondisk->vol0_next_tid != hmp->next_tid) {
2860729c8c8SMatthew Dillon 		hammer_modify_volume(NULL, root_volume, NULL, 0);
2870729c8c8SMatthew Dillon 		root_volume->ondisk->vol0_next_tid = hmp->next_tid;
2880729c8c8SMatthew Dillon 		hammer_modify_volume_done(root_volume);
2890729c8c8SMatthew Dillon 	}
2900729c8c8SMatthew Dillon 
2910729c8c8SMatthew Dillon 	/*
2920729c8c8SMatthew Dillon 	 * Sync our cached blockmap array with the one in the root
2930729c8c8SMatthew Dillon 	 * volume header.
2940729c8c8SMatthew Dillon 	 */
2950729c8c8SMatthew Dillon 	if (root_volume->io.modified) {
2960729c8c8SMatthew Dillon 		bcopy(hmp->blockmap, root_volume->ondisk->vol0_blockmap,
2970729c8c8SMatthew Dillon 		      sizeof(hmp->blockmap));
29810a5d1baSMatthew Dillon 		hammer_io_flush(&root_volume->io);
299059819e3SMatthew Dillon 	}
300059819e3SMatthew Dillon 
301059819e3SMatthew Dillon 	/*
302f90dde4cSMatthew Dillon 	 * Wait for I/O to complete
303059819e3SMatthew Dillon 	 */
304f90dde4cSMatthew Dillon 	crit_enter();
305f90dde4cSMatthew Dillon 	while (hmp->io_running_count) {
306f90dde4cSMatthew Dillon 		tsleep(&hmp->io_running_count, 0, "hmrfl2", 0);
307f90dde4cSMatthew Dillon 	}
308f90dde4cSMatthew Dillon 	crit_exit();
309059819e3SMatthew Dillon 
310059819e3SMatthew Dillon 	/*
31110a5d1baSMatthew Dillon 	 * Flush meta-data
312059819e3SMatthew Dillon 	 */
31310a5d1baSMatthew Dillon 	while ((io = TAILQ_FIRST(&hmp->meta_list)) != NULL) {
31410a5d1baSMatthew Dillon 		KKASSERT(io->modify_refs == 0);
31510a5d1baSMatthew Dillon 		hammer_ref(&io->lock);
31610a5d1baSMatthew Dillon 		KKASSERT(io->type != HAMMER_STRUCTURE_VOLUME);
31710a5d1baSMatthew Dillon 		hammer_io_flush(io);
31810a5d1baSMatthew Dillon 		hammer_rel_buffer((hammer_buffer_t)io, 1);
319059819e3SMatthew Dillon 	}
3209480ff55SMatthew Dillon 	hammer_unlock(&hmp->sync_lock);
321059819e3SMatthew Dillon }
322059819e3SMatthew Dillon 
323