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