xref: /dflybsd-src/sys/vfs/hammer/hammer_flusher.c (revision 0729c8c846bbed334033de1cf717c06a8202685a)
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*0729c8c8SMatthew Dillon  * $DragonFly: src/sys/vfs/hammer/hammer_flusher.c,v 1.7 2008/04/29 01:10:37 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);
48*0729c8c8SMatthew 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) {
58059819e3SMatthew Dillon 		seq = ++hmp->flusher_seq;
59059819e3SMatthew Dillon 		wakeup(&hmp->flusher_seq);
60059819e3SMatthew Dillon 		while ((int)(seq - hmp->flusher_act) > 0)
61059819e3SMatthew Dillon 			tsleep(&hmp->flusher_act, 0, "hmrfls", 0);
62059819e3SMatthew Dillon 	}
63f90dde4cSMatthew Dillon }
64059819e3SMatthew Dillon 
65059819e3SMatthew Dillon void
66059819e3SMatthew Dillon hammer_flusher_async(hammer_mount_t hmp)
67059819e3SMatthew Dillon {
68f90dde4cSMatthew Dillon 	if (hmp->flusher_td) {
69059819e3SMatthew Dillon 		++hmp->flusher_seq;
70059819e3SMatthew Dillon 		wakeup(&hmp->flusher_seq);
71059819e3SMatthew Dillon 	}
72f90dde4cSMatthew Dillon }
73059819e3SMatthew Dillon 
74059819e3SMatthew Dillon void
75059819e3SMatthew Dillon hammer_flusher_create(hammer_mount_t hmp)
76059819e3SMatthew Dillon {
77059819e3SMatthew Dillon 	lwkt_create(hammer_flusher_thread, hmp, &hmp->flusher_td, NULL,
78059819e3SMatthew Dillon 		    0, -1, "hammer");
79059819e3SMatthew Dillon }
80059819e3SMatthew Dillon 
81059819e3SMatthew Dillon void
82059819e3SMatthew Dillon hammer_flusher_destroy(hammer_mount_t hmp)
83059819e3SMatthew Dillon {
84f90dde4cSMatthew Dillon 	if (hmp->flusher_td) {
85059819e3SMatthew Dillon 		hmp->flusher_exiting = 1;
86059819e3SMatthew Dillon 		++hmp->flusher_seq;
87059819e3SMatthew Dillon 		wakeup(&hmp->flusher_seq);
88059819e3SMatthew Dillon 		while (hmp->flusher_td)
89059819e3SMatthew Dillon 			tsleep(&hmp->flusher_exiting, 0, "hmrwex", 0);
90059819e3SMatthew Dillon 	}
91f90dde4cSMatthew Dillon }
92059819e3SMatthew Dillon 
93059819e3SMatthew Dillon static void
94059819e3SMatthew Dillon hammer_flusher_thread(void *arg)
95059819e3SMatthew Dillon {
96059819e3SMatthew Dillon 	hammer_mount_t hmp = arg;
97059819e3SMatthew Dillon 	int seq;
98059819e3SMatthew Dillon 
99*0729c8c8SMatthew Dillon 	hmp->flusher_demark = kmalloc(sizeof(struct hammer_inode),
100*0729c8c8SMatthew Dillon 				      M_HAMMER, M_WAITOK | M_ZERO);
101*0729c8c8SMatthew Dillon 	TAILQ_INSERT_TAIL(&hmp->flush_list, hmp->flusher_demark, flush_entry);
102*0729c8c8SMatthew Dillon 
103059819e3SMatthew Dillon 	for (;;) {
104059819e3SMatthew Dillon 		seq = hmp->flusher_seq;
10510a5d1baSMatthew Dillon 		hammer_flusher_clean_loose_ios(hmp);
106059819e3SMatthew Dillon 		hammer_flusher_flush(hmp);
10710a5d1baSMatthew Dillon 		hammer_flusher_clean_loose_ios(hmp);
108059819e3SMatthew Dillon 		hmp->flusher_act = seq;
109059819e3SMatthew Dillon 		wakeup(&hmp->flusher_act);
110059819e3SMatthew Dillon 		if (hmp->flusher_exiting)
111059819e3SMatthew Dillon 			break;
112059819e3SMatthew Dillon 		while (hmp->flusher_seq == hmp->flusher_act)
113*0729c8c8SMatthew Dillon 			tsleep(&hmp->flusher_seq, 0, "hmrwwa", 0);
114059819e3SMatthew Dillon 	}
115*0729c8c8SMatthew Dillon 	TAILQ_REMOVE(&hmp->flush_list, hmp->flusher_demark, flush_entry);
116*0729c8c8SMatthew Dillon 	kfree(hmp->flusher_demark, M_HAMMER);
117*0729c8c8SMatthew Dillon 	hmp->flusher_demark = NULL;
118059819e3SMatthew Dillon 	hmp->flusher_td = NULL;
119059819e3SMatthew Dillon 	wakeup(&hmp->flusher_exiting);
120059819e3SMatthew Dillon 	lwkt_exit();
121059819e3SMatthew Dillon }
122059819e3SMatthew Dillon 
12310a5d1baSMatthew Dillon static void
12410a5d1baSMatthew Dillon hammer_flusher_clean_loose_ios(hammer_mount_t hmp)
12510a5d1baSMatthew Dillon {
12610a5d1baSMatthew Dillon 	hammer_buffer_t buffer;
12710a5d1baSMatthew Dillon 	hammer_io_t io;
12810a5d1baSMatthew Dillon 
12910a5d1baSMatthew Dillon 	/*
13010a5d1baSMatthew Dillon 	 * loose ends - buffers without bp's aren't tracked by the kernel
13110a5d1baSMatthew Dillon 	 * and can build up, so clean them out.  This can occur when an
13210a5d1baSMatthew Dillon 	 * IO completes on a buffer with no references left.
13310a5d1baSMatthew Dillon 	 */
13410a5d1baSMatthew Dillon 	while ((io = TAILQ_FIRST(&hmp->lose_list)) != NULL) {
13510a5d1baSMatthew Dillon 		KKASSERT(io->mod_list == &hmp->lose_list);
13610a5d1baSMatthew Dillon 		TAILQ_REMOVE(io->mod_list, io, mod_entry);
13710a5d1baSMatthew Dillon 		io->mod_list = NULL;
13810a5d1baSMatthew Dillon 		hammer_ref(&io->lock);
13910a5d1baSMatthew Dillon 		buffer = (void *)io;
14010a5d1baSMatthew Dillon 		hammer_rel_buffer(buffer, 0);
14110a5d1baSMatthew Dillon 	}
14210a5d1baSMatthew Dillon }
14310a5d1baSMatthew Dillon 
144059819e3SMatthew Dillon /*
145059819e3SMatthew Dillon  * Flush stuff
146059819e3SMatthew Dillon  */
147059819e3SMatthew Dillon static void
148059819e3SMatthew Dillon hammer_flusher_flush(hammer_mount_t hmp)
149059819e3SMatthew Dillon {
15010a5d1baSMatthew Dillon 	hammer_volume_t root_volume;
15110a5d1baSMatthew Dillon 	hammer_blockmap_t rootmap;
152059819e3SMatthew Dillon 	hammer_inode_t ip;
15310a5d1baSMatthew Dillon 	hammer_off_t start_offset;
15410a5d1baSMatthew Dillon 	int error;
15510a5d1baSMatthew Dillon 
15610a5d1baSMatthew Dillon 	root_volume = hammer_get_root_volume(hmp, &error);
157*0729c8c8SMatthew Dillon 	rootmap = &hmp->blockmap[HAMMER_ZONE_UNDO_INDEX];
15810a5d1baSMatthew Dillon 	start_offset = rootmap->next_offset;
159059819e3SMatthew Dillon 
160f90dde4cSMatthew Dillon 	if (hammer_debug_general & 0x00010000)
161f90dde4cSMatthew Dillon 		kprintf("x");
162f90dde4cSMatthew Dillon 
163*0729c8c8SMatthew Dillon 	TAILQ_REMOVE(&hmp->flush_list, hmp->flusher_demark, flush_entry);
164*0729c8c8SMatthew Dillon 	TAILQ_INSERT_TAIL(&hmp->flush_list, hmp->flusher_demark, flush_entry);
165*0729c8c8SMatthew Dillon 
166*0729c8c8SMatthew Dillon 	while ((ip = TAILQ_FIRST(&hmp->flush_list)) != hmp->flusher_demark) {
167059819e3SMatthew Dillon 		TAILQ_REMOVE(&hmp->flush_list, ip, flush_entry);
168059819e3SMatthew Dillon 
169059819e3SMatthew Dillon 		/*
170059819e3SMatthew Dillon 		 * We inherit the inode ref from the flush list
171059819e3SMatthew Dillon 		 */
172b84de5afSMatthew Dillon 		ip->error = hammer_sync_inode(ip, (ip->vp ? 0 : 1));
173b84de5afSMatthew Dillon 		hammer_flush_inode_done(ip);
174ec4e8497SMatthew Dillon 		if (hmp->locked_dirty_count > 64 ||
175*0729c8c8SMatthew Dillon 		    hammer_must_finalize_undo(hmp)) {
17610a5d1baSMatthew Dillon 			hammer_flusher_finalize(hmp, root_volume, start_offset);
17710a5d1baSMatthew Dillon 			start_offset = rootmap->next_offset;
178059819e3SMatthew Dillon 		}
179059819e3SMatthew Dillon 	}
18010a5d1baSMatthew Dillon 	hammer_flusher_finalize(hmp, root_volume, start_offset);
18110a5d1baSMatthew Dillon 	hammer_rel_volume(root_volume, 0);
18210a5d1baSMatthew Dillon }
183059819e3SMatthew Dillon 
18410a5d1baSMatthew Dillon /*
185ec4e8497SMatthew Dillon  * If the UNDO area gets over half full we have to flush it.  We can't
186ec4e8497SMatthew Dillon  * afford the UNDO area becoming completely full as that would break
187ec4e8497SMatthew Dillon  * the crash recovery atomicy.
188ec4e8497SMatthew Dillon  */
189ec4e8497SMatthew Dillon static
190ec4e8497SMatthew Dillon int
191*0729c8c8SMatthew Dillon hammer_must_finalize_undo(hammer_mount_t hmp)
192ec4e8497SMatthew Dillon {
193ec4e8497SMatthew Dillon 	hammer_blockmap_t rootmap;
194ec4e8497SMatthew Dillon 	int bytes;
195ec4e8497SMatthew Dillon 	int max_bytes;
196ec4e8497SMatthew Dillon 
197*0729c8c8SMatthew Dillon 	rootmap = &hmp->blockmap[HAMMER_ZONE_UNDO_INDEX];
198ec4e8497SMatthew Dillon 
199ec4e8497SMatthew Dillon 	if (rootmap->first_offset <= rootmap->next_offset) {
200ec4e8497SMatthew Dillon 		bytes = (int)(rootmap->next_offset - rootmap->first_offset);
201ec4e8497SMatthew Dillon 	} else {
202ec4e8497SMatthew Dillon 		bytes = (int)(rootmap->alloc_offset - rootmap->first_offset +
203ec4e8497SMatthew Dillon 			      rootmap->next_offset);
204ec4e8497SMatthew Dillon 	}
205ec4e8497SMatthew Dillon 	max_bytes = (int)(rootmap->alloc_offset & HAMMER_OFF_SHORT_MASK);
206ec4e8497SMatthew Dillon 	if (bytes > max_bytes / 2)
207ec4e8497SMatthew Dillon 		kprintf("*");
208ec4e8497SMatthew Dillon 	return (bytes > max_bytes / 2);
209ec4e8497SMatthew Dillon }
210ec4e8497SMatthew Dillon 
211ec4e8497SMatthew Dillon /*
21210a5d1baSMatthew Dillon  * To finalize the flush we finish flushing all undo and data buffers
21310a5d1baSMatthew Dillon  * still present, then we update the volume header and flush it,
21410a5d1baSMatthew Dillon  * then we flush out the mata-data (that can now be undone).
21510a5d1baSMatthew Dillon  *
21610a5d1baSMatthew Dillon  * Note that as long as the undo fifo's start and end points do not
21710a5d1baSMatthew Dillon  * match, we always must at least update the volume header.
2189480ff55SMatthew Dillon  *
2199480ff55SMatthew Dillon  * The sync_lock is used by other threads to issue modifying operations
2209480ff55SMatthew Dillon  * to HAMMER media without crossing a synchronization boundary or messing
2219480ff55SMatthew Dillon  * up the media synchronization operation.  Specifically, the pruning
2229480ff55SMatthew Dillon  * the reblocking ioctls, and allowing the frontend strategy code to
2239480ff55SMatthew Dillon  * allocate media data space.
22410a5d1baSMatthew Dillon  */
22510a5d1baSMatthew Dillon static
22610a5d1baSMatthew Dillon void
22710a5d1baSMatthew Dillon hammer_flusher_finalize(hammer_mount_t hmp, hammer_volume_t root_volume,
22810a5d1baSMatthew Dillon 			hammer_off_t start_offset)
229059819e3SMatthew Dillon {
230059819e3SMatthew Dillon 	hammer_blockmap_t rootmap;
23110a5d1baSMatthew Dillon 	hammer_io_t io;
23210a5d1baSMatthew Dillon 
2339480ff55SMatthew Dillon 	hammer_lock_ex(&hmp->sync_lock);
2349480ff55SMatthew Dillon 
235059819e3SMatthew Dillon 	/*
23610a5d1baSMatthew Dillon 	 * Flush undo bufs
237059819e3SMatthew Dillon 	 */
23810a5d1baSMatthew Dillon 	while ((io = TAILQ_FIRST(&hmp->undo_list)) != NULL) {
23910a5d1baSMatthew Dillon 		KKASSERT(io->modify_refs == 0);
24010a5d1baSMatthew Dillon 		hammer_ref(&io->lock);
24110a5d1baSMatthew Dillon 		KKASSERT(io->type != HAMMER_STRUCTURE_VOLUME);
24210a5d1baSMatthew Dillon 		hammer_io_flush(io);
24310a5d1baSMatthew Dillon 		hammer_rel_buffer((hammer_buffer_t)io, 1);
244059819e3SMatthew Dillon 	}
245059819e3SMatthew Dillon 
246059819e3SMatthew Dillon 	/*
24710a5d1baSMatthew Dillon 	 * Flush data bufs
248059819e3SMatthew Dillon 	 */
24910a5d1baSMatthew Dillon 	while ((io = TAILQ_FIRST(&hmp->data_list)) != NULL) {
25010a5d1baSMatthew Dillon 		KKASSERT(io->modify_refs == 0);
25110a5d1baSMatthew Dillon 		hammer_ref(&io->lock);
25210a5d1baSMatthew Dillon 		KKASSERT(io->type != HAMMER_STRUCTURE_VOLUME);
25310a5d1baSMatthew Dillon 		hammer_io_flush(io);
25410a5d1baSMatthew Dillon 		hammer_rel_buffer((hammer_buffer_t)io, 1);
255059819e3SMatthew Dillon 	}
256059819e3SMatthew Dillon 
257059819e3SMatthew Dillon 	/*
258f90dde4cSMatthew Dillon 	 * Wait for I/O to complete
259059819e3SMatthew Dillon 	 */
260f90dde4cSMatthew Dillon 	crit_enter();
261f90dde4cSMatthew Dillon 	while (hmp->io_running_count) {
2629480ff55SMatthew Dillon 		kprintf("W[%d]", hmp->io_running_count);
263f90dde4cSMatthew Dillon 		tsleep(&hmp->io_running_count, 0, "hmrfl1", 0);
264f90dde4cSMatthew Dillon 	}
265f90dde4cSMatthew Dillon 	crit_exit();
266059819e3SMatthew Dillon 
267059819e3SMatthew Dillon 	/*
26810a5d1baSMatthew Dillon 	 * Update the volume header
269059819e3SMatthew Dillon 	 */
270*0729c8c8SMatthew Dillon 	rootmap = &hmp->blockmap[HAMMER_ZONE_UNDO_INDEX];
27110a5d1baSMatthew Dillon 	if (rootmap->first_offset != start_offset) {
27210a5d1baSMatthew Dillon 		hammer_modify_volume(NULL, root_volume, NULL, 0);
27310a5d1baSMatthew Dillon 		rootmap->first_offset = start_offset;
27410a5d1baSMatthew Dillon 		hammer_modify_volume_done(root_volume);
275*0729c8c8SMatthew Dillon 	}
276*0729c8c8SMatthew Dillon 	if (root_volume->ondisk->vol0_next_tid != hmp->next_tid) {
277*0729c8c8SMatthew Dillon 		hammer_modify_volume(NULL, root_volume, NULL, 0);
278*0729c8c8SMatthew Dillon 		root_volume->ondisk->vol0_next_tid = hmp->next_tid;
279*0729c8c8SMatthew Dillon 		hammer_modify_volume_done(root_volume);
280*0729c8c8SMatthew Dillon 	}
281*0729c8c8SMatthew Dillon 
282*0729c8c8SMatthew Dillon 	/*
283*0729c8c8SMatthew Dillon 	 * Sync our cached blockmap array with the one in the root
284*0729c8c8SMatthew Dillon 	 * volume header.
285*0729c8c8SMatthew Dillon 	 */
286*0729c8c8SMatthew Dillon 	if (root_volume->io.modified) {
287*0729c8c8SMatthew Dillon 		bcopy(hmp->blockmap, root_volume->ondisk->vol0_blockmap,
288*0729c8c8SMatthew Dillon 		      sizeof(hmp->blockmap));
28910a5d1baSMatthew Dillon 		hammer_io_flush(&root_volume->io);
290059819e3SMatthew Dillon 	}
291059819e3SMatthew Dillon 
292059819e3SMatthew Dillon 	/*
293f90dde4cSMatthew Dillon 	 * Wait for I/O to complete
294059819e3SMatthew Dillon 	 */
295f90dde4cSMatthew Dillon 	crit_enter();
296f90dde4cSMatthew Dillon 	while (hmp->io_running_count) {
297f90dde4cSMatthew Dillon 		tsleep(&hmp->io_running_count, 0, "hmrfl2", 0);
298f90dde4cSMatthew Dillon 	}
299f90dde4cSMatthew Dillon 	crit_exit();
300059819e3SMatthew Dillon 
301059819e3SMatthew Dillon 	/*
30210a5d1baSMatthew Dillon 	 * Flush meta-data
303059819e3SMatthew Dillon 	 */
30410a5d1baSMatthew Dillon 	while ((io = TAILQ_FIRST(&hmp->meta_list)) != NULL) {
30510a5d1baSMatthew Dillon 		KKASSERT(io->modify_refs == 0);
30610a5d1baSMatthew Dillon 		hammer_ref(&io->lock);
30710a5d1baSMatthew Dillon 		KKASSERT(io->type != HAMMER_STRUCTURE_VOLUME);
30810a5d1baSMatthew Dillon 		hammer_io_flush(io);
30910a5d1baSMatthew Dillon 		hammer_rel_buffer((hammer_buffer_t)io, 1);
310059819e3SMatthew Dillon 	}
3119480ff55SMatthew Dillon 	hammer_unlock(&hmp->sync_lock);
312059819e3SMatthew Dillon }
313059819e3SMatthew Dillon 
314