166325755SMatthew Dillon /* 266325755SMatthew Dillon * Copyright (c) 2007 The DragonFly Project. All rights reserved. 366325755SMatthew Dillon * 466325755SMatthew Dillon * This code is derived from software contributed to The DragonFly Project 566325755SMatthew Dillon * by Matthew Dillon <dillon@backplane.com> 666325755SMatthew Dillon * 766325755SMatthew Dillon * Redistribution and use in source and binary forms, with or without 866325755SMatthew Dillon * modification, are permitted provided that the following conditions 966325755SMatthew Dillon * are met: 1066325755SMatthew Dillon * 1166325755SMatthew Dillon * 1. Redistributions of source code must retain the above copyright 1266325755SMatthew Dillon * notice, this list of conditions and the following disclaimer. 1366325755SMatthew Dillon * 2. Redistributions in binary form must reproduce the above copyright 1466325755SMatthew Dillon * notice, this list of conditions and the following disclaimer in 1566325755SMatthew Dillon * the documentation and/or other materials provided with the 1666325755SMatthew Dillon * distribution. 1766325755SMatthew Dillon * 3. Neither the name of The DragonFly Project nor the names of its 1866325755SMatthew Dillon * contributors may be used to endorse or promote products derived 1966325755SMatthew Dillon * from this software without specific, prior written permission. 2066325755SMatthew Dillon * 2166325755SMatthew Dillon * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 2266325755SMatthew Dillon * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 2366325755SMatthew Dillon * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 2466325755SMatthew Dillon * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 2566325755SMatthew Dillon * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 2666325755SMatthew Dillon * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 2766325755SMatthew Dillon * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 2866325755SMatthew Dillon * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 2966325755SMatthew Dillon * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 3066325755SMatthew Dillon * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 3166325755SMatthew Dillon * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3266325755SMatthew Dillon * SUCH DAMAGE. 3366325755SMatthew Dillon * 34*0b075555SMatthew Dillon * $DragonFly: src/sys/vfs/hammer/hammer_io.c,v 1.9 2007/12/30 08:49:20 dillon Exp $ 3566325755SMatthew Dillon */ 3666325755SMatthew Dillon /* 3766325755SMatthew Dillon * IO Primitives and buffer cache management 3866325755SMatthew Dillon * 3966325755SMatthew Dillon * All major data-tracking structures in HAMMER contain a struct hammer_io 4066325755SMatthew Dillon * which is used to manage their backing store. We use filesystem buffers 4166325755SMatthew Dillon * for backing store and we leave them passively associated with their 4266325755SMatthew Dillon * HAMMER structures. 4366325755SMatthew Dillon * 4466325755SMatthew Dillon * If the kernel tries to release a passively associated buf which we cannot 4566325755SMatthew Dillon * yet let go we set B_LOCKED in the buffer and then actively released it 4666325755SMatthew Dillon * later when we can. 4766325755SMatthew Dillon */ 4866325755SMatthew Dillon 4966325755SMatthew Dillon #include "hammer.h" 5066325755SMatthew Dillon #include <sys/fcntl.h> 5166325755SMatthew Dillon #include <sys/nlookup.h> 5266325755SMatthew Dillon #include <sys/buf.h> 5366325755SMatthew Dillon #include <sys/buf2.h> 5466325755SMatthew Dillon 5566325755SMatthew Dillon /* 56fbc6e32aSMatthew Dillon * Helper routine to disassociate a buffer cache buffer from an I/O 57fbc6e32aSMatthew Dillon * structure. 5866325755SMatthew Dillon */ 5966325755SMatthew Dillon static void 6066325755SMatthew Dillon hammer_io_disassociate(union hammer_io_structure *io) 6166325755SMatthew Dillon { 6266325755SMatthew Dillon struct buf *bp = io->io.bp; 6366325755SMatthew Dillon 6466325755SMatthew Dillon LIST_INIT(&bp->b_dep); /* clear the association */ 657f7c1f84SMatthew Dillon bp->b_ops = NULL; 6666325755SMatthew Dillon io->io.bp = NULL; 6766325755SMatthew Dillon 6866325755SMatthew Dillon switch(io->io.type) { 6966325755SMatthew Dillon case HAMMER_STRUCTURE_VOLUME: 7066325755SMatthew Dillon io->volume.ondisk = NULL; 7166325755SMatthew Dillon io->volume.alist.meta = NULL; 7266325755SMatthew Dillon break; 7366325755SMatthew Dillon case HAMMER_STRUCTURE_SUPERCL: 7466325755SMatthew Dillon io->supercl.ondisk = NULL; 7566325755SMatthew Dillon io->supercl.alist.meta = NULL; 7666325755SMatthew Dillon break; 7766325755SMatthew Dillon case HAMMER_STRUCTURE_CLUSTER: 7866325755SMatthew Dillon io->cluster.ondisk = NULL; 7966325755SMatthew Dillon io->cluster.alist_master.meta = NULL; 8066325755SMatthew Dillon io->cluster.alist_btree.meta = NULL; 8166325755SMatthew Dillon io->cluster.alist_record.meta = NULL; 8266325755SMatthew Dillon io->cluster.alist_mdata.meta = NULL; 8366325755SMatthew Dillon break; 8466325755SMatthew Dillon case HAMMER_STRUCTURE_BUFFER: 8566325755SMatthew Dillon io->buffer.ondisk = NULL; 8666325755SMatthew Dillon io->buffer.alist.meta = NULL; 8766325755SMatthew Dillon break; 8866325755SMatthew Dillon } 89fbc6e32aSMatthew Dillon io->io.modified = 0; 90c0ade690SMatthew Dillon io->io.released = 1; 9166325755SMatthew Dillon } 92fbc6e32aSMatthew Dillon 93fbc6e32aSMatthew Dillon /* 94fbc6e32aSMatthew Dillon * Mark a cluster as being closed. This is done as late as possible, 95fbc6e32aSMatthew Dillon * only when we are asked to flush the cluster 96fbc6e32aSMatthew Dillon */ 97fbc6e32aSMatthew Dillon static void 98fbc6e32aSMatthew Dillon hammer_close_cluster(hammer_cluster_t cluster) 99fbc6e32aSMatthew Dillon { 100fbc6e32aSMatthew Dillon while (cluster->state == HAMMER_CLUSTER_ASYNC) 101fbc6e32aSMatthew Dillon tsleep(cluster, 0, "hmrdep", 0); 102fbc6e32aSMatthew Dillon if (cluster->state == HAMMER_CLUSTER_OPEN) { 103fbc6e32aSMatthew Dillon cluster->state = HAMMER_CLUSTER_IDLE; 104fbc6e32aSMatthew Dillon hammer_modify_cluster(cluster); 105*0b075555SMatthew Dillon cluster->ondisk->clu_flags &= ~HAMMER_CLUF_OPEN; 106*0b075555SMatthew Dillon hammer_modify_cluster_done(cluster); 107*0b075555SMatthew Dillon kprintf("CLOSE CLUSTER\n"); 10866325755SMatthew Dillon } 109fbc6e32aSMatthew Dillon } 110fbc6e32aSMatthew Dillon 11166325755SMatthew Dillon 11266325755SMatthew Dillon /* 11366325755SMatthew Dillon * Load bp for a HAMMER structure. 11466325755SMatthew Dillon */ 11566325755SMatthew Dillon int 11666325755SMatthew Dillon hammer_io_read(struct vnode *devvp, struct hammer_io *io) 11766325755SMatthew Dillon { 11866325755SMatthew Dillon struct buf *bp; 11966325755SMatthew Dillon int error; 12066325755SMatthew Dillon 12166325755SMatthew Dillon if ((bp = io->bp) == NULL) { 12266325755SMatthew Dillon error = bread(devvp, io->offset, HAMMER_BUFSIZE, &io->bp); 12366325755SMatthew Dillon if (error == 0) { 12466325755SMatthew Dillon bp = io->bp; 12566325755SMatthew Dillon bp->b_ops = &hammer_bioops; 12666325755SMatthew Dillon LIST_INSERT_HEAD(&bp->b_dep, &io->worklist, node); 12766325755SMatthew Dillon BUF_KERNPROC(bp); 12866325755SMatthew Dillon } 12966325755SMatthew Dillon io->modified = 0; /* no new modifications yet */ 13066325755SMatthew Dillon io->released = 0; /* we hold an active lock on bp */ 13166325755SMatthew Dillon } else { 13266325755SMatthew Dillon error = 0; 13366325755SMatthew Dillon } 13466325755SMatthew Dillon return(error); 13566325755SMatthew Dillon } 13666325755SMatthew Dillon 13766325755SMatthew Dillon /* 13866325755SMatthew Dillon * Similar to hammer_io_read() but returns a zero'd out buffer instead. 13966325755SMatthew Dillon * vfs_bio_clrbuf() is kinda nasty, enforce serialization against background 14066325755SMatthew Dillon * I/O so we can call it. 14166325755SMatthew Dillon */ 14266325755SMatthew Dillon int 14366325755SMatthew Dillon hammer_io_new(struct vnode *devvp, struct hammer_io *io) 14466325755SMatthew Dillon { 14566325755SMatthew Dillon struct buf *bp; 14666325755SMatthew Dillon 14766325755SMatthew Dillon if ((bp = io->bp) == NULL) { 14866325755SMatthew Dillon io->bp = getblk(devvp, io->offset, HAMMER_BUFSIZE, 0, 0); 14966325755SMatthew Dillon bp = io->bp; 15066325755SMatthew Dillon bp->b_ops = &hammer_bioops; 15166325755SMatthew Dillon LIST_INSERT_HEAD(&bp->b_dep, &io->worklist, node); 15266325755SMatthew Dillon io->released = 0; /* we hold an active lock on bp */ 15366325755SMatthew Dillon BUF_KERNPROC(bp); 15466325755SMatthew Dillon } else { 15566325755SMatthew Dillon if (io->released) { 15666325755SMatthew Dillon regetblk(bp); 15766325755SMatthew Dillon io->released = 0; 15866325755SMatthew Dillon BUF_KERNPROC(bp); 15966325755SMatthew Dillon } 16066325755SMatthew Dillon } 16166325755SMatthew Dillon io->modified = 1; 16266325755SMatthew Dillon vfs_bio_clrbuf(bp); 16366325755SMatthew Dillon return(0); 16466325755SMatthew Dillon } 16566325755SMatthew Dillon 16666325755SMatthew Dillon /* 167fbc6e32aSMatthew Dillon * This routine is called when a buffer within a cluster is modified. We 168fbc6e32aSMatthew Dillon * mark the cluster open and immediately initiate asynchronous I/O. Any 169fbc6e32aSMatthew Dillon * related hammer_buffer write I/O blocks until our async write completes. 170fbc6e32aSMatthew Dillon * This guarentees (inasmuch as the OS can) that the cluster recovery code 171fbc6e32aSMatthew Dillon * will see a cluster marked open if a crash occured while the filesystem 172fbc6e32aSMatthew Dillon * still had dirty buffers associated with that cluster. 173*0b075555SMatthew Dillon * 174*0b075555SMatthew Dillon * XXX 175fbc6e32aSMatthew Dillon */ 176fbc6e32aSMatthew Dillon void 177fbc6e32aSMatthew Dillon hammer_io_notify_cluster(hammer_cluster_t cluster) 178fbc6e32aSMatthew Dillon { 179fbc6e32aSMatthew Dillon struct hammer_io *io = &cluster->io; 180fbc6e32aSMatthew Dillon 181fbc6e32aSMatthew Dillon if (cluster->state == HAMMER_CLUSTER_IDLE) { 182fbc6e32aSMatthew Dillon hammer_lock_ex(&cluster->io.lock); 183fbc6e32aSMatthew Dillon if (cluster->state == HAMMER_CLUSTER_IDLE) { 184fbc6e32aSMatthew Dillon if (io->released) 185fbc6e32aSMatthew Dillon regetblk(io->bp); 186*0b075555SMatthew Dillon io->released = 1; 187fbc6e32aSMatthew Dillon kprintf("MARK CLUSTER OPEN\n"); 188fbc6e32aSMatthew Dillon cluster->ondisk->clu_flags |= HAMMER_CLUF_OPEN; 189fbc6e32aSMatthew Dillon cluster->state = HAMMER_CLUSTER_ASYNC; 190fbc6e32aSMatthew Dillon bawrite(io->bp); 191fbc6e32aSMatthew Dillon /* leave cluster marked as modified */ 192fbc6e32aSMatthew Dillon } 193fbc6e32aSMatthew Dillon hammer_unlock(&cluster->io.lock); 194fbc6e32aSMatthew Dillon } 195fbc6e32aSMatthew Dillon } 196fbc6e32aSMatthew Dillon 197fbc6e32aSMatthew Dillon /* 198fbc6e32aSMatthew Dillon * This routine is called on the last reference to a hammer structure. If 199fbc6e32aSMatthew Dillon * flush is non-zero we have to completely disassociate the bp from the 200fbc6e32aSMatthew Dillon * structure (which may involve blocking). Otherwise we can leave the bp 201fbc6e32aSMatthew Dillon * passively associated with the structure. 20266325755SMatthew Dillon * 203fbc6e32aSMatthew Dillon * The caller is holding io->lock exclusively. 20466325755SMatthew Dillon */ 20566325755SMatthew Dillon void 20666325755SMatthew Dillon hammer_io_release(struct hammer_io *io, int flush) 20766325755SMatthew Dillon { 20866325755SMatthew Dillon union hammer_io_structure *iou = (void *)io; 209fbc6e32aSMatthew Dillon hammer_cluster_t cluster; 21066325755SMatthew Dillon struct buf *bp; 21166325755SMatthew Dillon 21266325755SMatthew Dillon if ((bp = io->bp) != NULL) { 21366325755SMatthew Dillon /* 214fbc6e32aSMatthew Dillon * If neither we nor the kernel want to flush the bp, we can 215fbc6e32aSMatthew Dillon * stop here. Make sure the bp is passively released 216fbc6e32aSMatthew Dillon * before returning. Even though we are still holding it, 217fbc6e32aSMatthew Dillon * we want to be notified when the kernel wishes to flush 218fbc6e32aSMatthew Dillon * it out so make sure B_DELWRI is properly set if we had 219fbc6e32aSMatthew Dillon * made modifications. 22066325755SMatthew Dillon */ 221fbc6e32aSMatthew Dillon if (flush == 0 && (bp->b_flags & B_LOCKED) == 0) { 222fbc6e32aSMatthew Dillon if ((bp->b_flags & B_DELWRI) == 0 && io->modified) { 223fbc6e32aSMatthew Dillon if (io->released) 22466325755SMatthew Dillon regetblk(bp); 22566325755SMatthew Dillon bdwrite(bp); 226fbc6e32aSMatthew Dillon io->released = 1; 227fbc6e32aSMatthew Dillon } else if (io->released == 0) { 228fbc6e32aSMatthew Dillon bqrelse(bp); 229fbc6e32aSMatthew Dillon io->released = 1; 230fbc6e32aSMatthew Dillon } 231fbc6e32aSMatthew Dillon return; 232fbc6e32aSMatthew Dillon } 233fbc6e32aSMatthew Dillon 234fbc6e32aSMatthew Dillon /* 235fbc6e32aSMatthew Dillon * We've been asked to flush the buffer. 236fbc6e32aSMatthew Dillon * 237fbc6e32aSMatthew Dillon * If this is a hammer_buffer we may have to wait for the 238fbc6e32aSMatthew Dillon * cluster header write to complete. 239fbc6e32aSMatthew Dillon */ 240fbc6e32aSMatthew Dillon if (iou->io.type == HAMMER_STRUCTURE_BUFFER && 241fbc6e32aSMatthew Dillon (io->modified || (bp->b_flags & B_DELWRI))) { 242fbc6e32aSMatthew Dillon cluster = iou->buffer.cluster; 243fbc6e32aSMatthew Dillon while (cluster->state == HAMMER_CLUSTER_ASYNC) 244fbc6e32aSMatthew Dillon tsleep(iou->buffer.cluster, 0, "hmrdep", 0); 245fbc6e32aSMatthew Dillon } 246fbc6e32aSMatthew Dillon 247fbc6e32aSMatthew Dillon /* 248fbc6e32aSMatthew Dillon * If we have an open cluster header, close it 249fbc6e32aSMatthew Dillon */ 250fbc6e32aSMatthew Dillon if (iou->io.type == HAMMER_STRUCTURE_CLUSTER) { 251fbc6e32aSMatthew Dillon hammer_close_cluster(&iou->cluster); 252fbc6e32aSMatthew Dillon } 253fbc6e32aSMatthew Dillon 254fbc6e32aSMatthew Dillon 255fbc6e32aSMatthew Dillon /* 256fbc6e32aSMatthew Dillon * Ok the dependancies are all gone. Check for the simple 257fbc6e32aSMatthew Dillon * disassociation case. 258fbc6e32aSMatthew Dillon */ 259fbc6e32aSMatthew Dillon if (io->released && (bp->b_flags & B_LOCKED) == 0 && 260fbc6e32aSMatthew Dillon (io->modified == 0 || (bp->b_flags & B_DELWRI))) { 261fbc6e32aSMatthew Dillon hammer_io_disassociate(iou); 262fbc6e32aSMatthew Dillon return; 263fbc6e32aSMatthew Dillon } 264fbc6e32aSMatthew Dillon 265fbc6e32aSMatthew Dillon /* 266fbc6e32aSMatthew Dillon * Handle the more complex disassociation case. Acquire the 267fbc6e32aSMatthew Dillon * buffer, clean up B_LOCKED, and deal with the modified 268fbc6e32aSMatthew Dillon * flag. 269fbc6e32aSMatthew Dillon */ 270fbc6e32aSMatthew Dillon if (io->released) 271fbc6e32aSMatthew Dillon regetblk(bp); 272d26d0ae9SMatthew Dillon io->released = 1; 273fbc6e32aSMatthew Dillon bp->b_flags &= ~B_LOCKED; 274fbc6e32aSMatthew Dillon if (io->modified || (bp->b_flags & B_DELWRI)) 275fbc6e32aSMatthew Dillon bawrite(bp); 27666325755SMatthew Dillon else 27766325755SMatthew Dillon bqrelse(bp); 27866325755SMatthew Dillon hammer_io_disassociate(iou); 279fbc6e32aSMatthew Dillon } 280fbc6e32aSMatthew Dillon } 281fbc6e32aSMatthew Dillon 282fbc6e32aSMatthew Dillon /* 283fbc6e32aSMatthew Dillon * Flush dirty data, if any. 284fbc6e32aSMatthew Dillon */ 285fbc6e32aSMatthew Dillon void 286fbc6e32aSMatthew Dillon hammer_io_flush(struct hammer_io *io, struct hammer_sync_info *info) 287fbc6e32aSMatthew Dillon { 288fbc6e32aSMatthew Dillon struct buf *bp; 289fbc6e32aSMatthew Dillon int error; 290fbc6e32aSMatthew Dillon 291*0b075555SMatthew Dillon again: 292fbc6e32aSMatthew Dillon if ((bp = io->bp) == NULL) 293fbc6e32aSMatthew Dillon return; 294fbc6e32aSMatthew Dillon if (bp->b_flags & B_DELWRI) 295fbc6e32aSMatthew Dillon io->modified = 1; 296fbc6e32aSMatthew Dillon 297*0b075555SMatthew Dillon /* 298*0b075555SMatthew Dillon * We can't initiate a write while the buffer is being modified 299*0b075555SMatthew Dillon * by someone. 300*0b075555SMatthew Dillon */ 301*0b075555SMatthew Dillon while (io->lock.modifying) { 302*0b075555SMatthew Dillon io->lock.wanted = 1; 303*0b075555SMatthew Dillon kprintf("DELAYING IO FLUSH BP %p TYPE %d REFS %d modifying %d\n", 304*0b075555SMatthew Dillon bp, io->type, io->lock.refs, io->lock.modifying); 305*0b075555SMatthew Dillon tsleep(&io->lock, 0, "hmrfls", 0); 306*0b075555SMatthew Dillon } 307*0b075555SMatthew Dillon hammer_lock_ex(&io->lock); 308*0b075555SMatthew Dillon if (io->lock.modifying || io->bp == NULL) { 309*0b075555SMatthew Dillon hammer_unlock(&io->lock); 310*0b075555SMatthew Dillon goto again; 311*0b075555SMatthew Dillon } 312*0b075555SMatthew Dillon 313*0b075555SMatthew Dillon /* 314*0b075555SMatthew Dillon * Acquire ownership of the buffer cache buffer so we can flush it 315*0b075555SMatthew Dillon * out. 316*0b075555SMatthew Dillon */ 317*0b075555SMatthew Dillon if (io->released) { 318*0b075555SMatthew Dillon if (io->modified == 0) 319*0b075555SMatthew Dillon goto done; 320fbc6e32aSMatthew Dillon regetblk(bp); 321*0b075555SMatthew Dillon } 322fbc6e32aSMatthew Dillon io->released = 1; 323fbc6e32aSMatthew Dillon 324fbc6e32aSMatthew Dillon /* 325*0b075555SMatthew Dillon * Return the bp to the system, issuing I/O if necessary. The 326*0b075555SMatthew Dillon * system will issue a callback to us when it actually wants to 327*0b075555SMatthew Dillon * throw the bp away. 328fbc6e32aSMatthew Dillon */ 329*0b075555SMatthew Dillon if (io->modified == 0) { 330*0b075555SMatthew Dillon bqrelse(bp); 331*0b075555SMatthew Dillon } else if (info->waitfor & MNT_WAIT) { 332fbc6e32aSMatthew Dillon io->modified = 0; 333fbc6e32aSMatthew Dillon error = bwrite(bp); 334fbc6e32aSMatthew Dillon if (error) 335fbc6e32aSMatthew Dillon info->error = error; 336*0b075555SMatthew Dillon } else { 337fbc6e32aSMatthew Dillon io->modified = 0; 338fbc6e32aSMatthew Dillon bawrite(bp); 33966325755SMatthew Dillon } 340*0b075555SMatthew Dillon done: 341fbc6e32aSMatthew Dillon hammer_unlock(&io->lock); 342fbc6e32aSMatthew Dillon } 343fbc6e32aSMatthew Dillon 344*0b075555SMatthew Dillon /* 345*0b075555SMatthew Dillon * Called prior to any modifications being made to ondisk data. This 346*0b075555SMatthew Dillon * forces the caller to wait for any writes to complete. We explicitly 347*0b075555SMatthew Dillon * avoid the write-modify race. 348*0b075555SMatthew Dillon * 349*0b075555SMatthew Dillon * This routine is only called on hammer structures which are already 350*0b075555SMatthew Dillon * actively referenced. 351*0b075555SMatthew Dillon */ 352*0b075555SMatthew Dillon void 353*0b075555SMatthew Dillon hammer_io_intend_modify(struct hammer_io *io) 354*0b075555SMatthew Dillon { 355*0b075555SMatthew Dillon KKASSERT(io->lock.refs != 0 && io->bp != NULL); 356*0b075555SMatthew Dillon if (io->released) { 357*0b075555SMatthew Dillon hammer_lock_ex(&io->lock); 358*0b075555SMatthew Dillon if (io->released) { 359*0b075555SMatthew Dillon regetblk(io->bp); 360*0b075555SMatthew Dillon io->released = 0; 361*0b075555SMatthew Dillon BUF_KERNPROC(io->bp); 362*0b075555SMatthew Dillon } 363*0b075555SMatthew Dillon hammer_unlock(&io->lock); 364*0b075555SMatthew Dillon } 365*0b075555SMatthew Dillon } 366*0b075555SMatthew Dillon 367*0b075555SMatthew Dillon void 368*0b075555SMatthew Dillon hammer_io_modify_done(struct hammer_io *io) 369*0b075555SMatthew Dillon { 370*0b075555SMatthew Dillon KKASSERT(io->lock.modifying > 0); 371*0b075555SMatthew Dillon --io->lock.modifying; 372*0b075555SMatthew Dillon if (io->lock.wanted && io->lock.modifying == 0) { 373*0b075555SMatthew Dillon io->lock.wanted = 0; 374*0b075555SMatthew Dillon wakeup(&io->lock); 375*0b075555SMatthew Dillon } 376*0b075555SMatthew Dillon } 37766325755SMatthew Dillon 37866325755SMatthew Dillon /* 37966325755SMatthew Dillon * HAMMER_BIOOPS 38066325755SMatthew Dillon */ 38166325755SMatthew Dillon 38266325755SMatthew Dillon /* 383fbc6e32aSMatthew Dillon * Pre and post I/O callbacks. 38466325755SMatthew Dillon */ 38566325755SMatthew Dillon static void hammer_io_deallocate(struct buf *bp); 38666325755SMatthew Dillon 38766325755SMatthew Dillon static void 38866325755SMatthew Dillon hammer_io_start(struct buf *bp) 38966325755SMatthew Dillon { 390fbc6e32aSMatthew Dillon #if 0 391fbc6e32aSMatthew Dillon union hammer_io_structure *io = (void *)LIST_FIRST(&bp->b_dep); 392fbc6e32aSMatthew Dillon 393fbc6e32aSMatthew Dillon if (io->io.type == HAMMER_STRUCTURE_BUFFER) { 394fbc6e32aSMatthew Dillon while (io->buffer.cluster->io_in_progress) { 395fbc6e32aSMatthew Dillon kprintf("hammer_io_start: wait for cluster\n"); 396fbc6e32aSMatthew Dillon tsleep(io->buffer.cluster, 0, "hmrdep", 0); 397fbc6e32aSMatthew Dillon kprintf("hammer_io_start: wait for cluster done\n"); 398fbc6e32aSMatthew Dillon } 399fbc6e32aSMatthew Dillon } 400fbc6e32aSMatthew Dillon #endif 40166325755SMatthew Dillon } 40266325755SMatthew Dillon 40366325755SMatthew Dillon static void 40466325755SMatthew Dillon hammer_io_complete(struct buf *bp) 40566325755SMatthew Dillon { 406fbc6e32aSMatthew Dillon union hammer_io_structure *io = (void *)LIST_FIRST(&bp->b_dep); 407fbc6e32aSMatthew Dillon 408fbc6e32aSMatthew Dillon if (io->io.type == HAMMER_STRUCTURE_CLUSTER) { 409fbc6e32aSMatthew Dillon if (io->cluster.state == HAMMER_CLUSTER_ASYNC) { 410fbc6e32aSMatthew Dillon io->cluster.state = HAMMER_CLUSTER_OPEN; 411fbc6e32aSMatthew Dillon wakeup(&io->cluster); 412fbc6e32aSMatthew Dillon } 413fbc6e32aSMatthew Dillon } 41466325755SMatthew Dillon } 41566325755SMatthew Dillon 41666325755SMatthew Dillon /* 41766325755SMatthew Dillon * Callback from kernel when it wishes to deallocate a passively 41866325755SMatthew Dillon * associated structure. This can only occur if the buffer is 4198cd0a023SMatthew Dillon * passively associated with the structure. The kernel has locked 4208cd0a023SMatthew Dillon * the buffer. 42166325755SMatthew Dillon * 42266325755SMatthew Dillon * If we cannot disassociate we set B_LOCKED to prevent the buffer 42366325755SMatthew Dillon * from getting reused. 42466325755SMatthew Dillon */ 42566325755SMatthew Dillon static void 42666325755SMatthew Dillon hammer_io_deallocate(struct buf *bp) 42766325755SMatthew Dillon { 42866325755SMatthew Dillon union hammer_io_structure *io = (void *)LIST_FIRST(&bp->b_dep); 42966325755SMatthew Dillon 43066325755SMatthew Dillon /* XXX memory interlock, spinlock to sync cpus */ 43166325755SMatthew Dillon 432fbc6e32aSMatthew Dillon /* 433fbc6e32aSMatthew Dillon * Since the kernel is passing us a locked buffer, the HAMMER 434fbc6e32aSMatthew Dillon * structure had better not believe it has a lock on the buffer. 435fbc6e32aSMatthew Dillon */ 43666325755SMatthew Dillon KKASSERT(io->io.released); 43766325755SMatthew Dillon crit_enter(); 4388cd0a023SMatthew Dillon 4398cd0a023SMatthew Dillon /* 4408cd0a023SMatthew Dillon * First, ref the structure to prevent either the buffer or the 441a89aec1bSMatthew Dillon * structure from going away or being unexpectedly flushed. 4428cd0a023SMatthew Dillon */ 4438cd0a023SMatthew Dillon hammer_ref(&io->io.lock); 4448cd0a023SMatthew Dillon 4458cd0a023SMatthew Dillon /* 4468cd0a023SMatthew Dillon * Buffers can have active references from cached hammer_node's, 4478cd0a023SMatthew Dillon * even if those nodes are themselves passively cached. Attempt 4488cd0a023SMatthew Dillon * to clean them out. This may not succeed. 4498cd0a023SMatthew Dillon */ 4508cd0a023SMatthew Dillon if (io->io.type == HAMMER_STRUCTURE_BUFFER && 4518cd0a023SMatthew Dillon hammer_lock_ex_try(&io->io.lock) == 0) { 4528cd0a023SMatthew Dillon hammer_flush_buffer_nodes(&io->buffer); 4538cd0a023SMatthew Dillon hammer_unlock(&io->io.lock); 4548cd0a023SMatthew Dillon } 4558cd0a023SMatthew Dillon 4568cd0a023SMatthew Dillon if (hammer_islastref(&io->io.lock)) { 4578cd0a023SMatthew Dillon /* 458fbc6e32aSMatthew Dillon * If we are the only ref left we can disassociate the I/O. 459fbc6e32aSMatthew Dillon * It had better still be in a released state because the 460fbc6e32aSMatthew Dillon * kernel is holding a lock on the buffer. Any passive 461fbc6e32aSMatthew Dillon * modifications should have already been synchronized with 462fbc6e32aSMatthew Dillon * the buffer. 4638cd0a023SMatthew Dillon */ 4648cd0a023SMatthew Dillon KKASSERT(io->io.released); 4658cd0a023SMatthew Dillon hammer_io_disassociate(io); 4668cd0a023SMatthew Dillon bp->b_flags &= ~B_LOCKED; 467fbc6e32aSMatthew Dillon KKASSERT (io->io.modified == 0 || (bp->b_flags & B_DELWRI)); 4688cd0a023SMatthew Dillon 469a89aec1bSMatthew Dillon /* 470a89aec1bSMatthew Dillon * Perform final rights on the structure. This can cause 471a89aec1bSMatthew Dillon * a chain reaction - e.g. last buffer -> last cluster -> 472a89aec1bSMatthew Dillon * last supercluster -> last volume. 473a89aec1bSMatthew Dillon */ 47466325755SMatthew Dillon switch(io->io.type) { 47566325755SMatthew Dillon case HAMMER_STRUCTURE_VOLUME: 4768cd0a023SMatthew Dillon hammer_rel_volume(&io->volume, 1); 47766325755SMatthew Dillon break; 47866325755SMatthew Dillon case HAMMER_STRUCTURE_SUPERCL: 4798cd0a023SMatthew Dillon hammer_rel_supercl(&io->supercl, 1); 48066325755SMatthew Dillon break; 48166325755SMatthew Dillon case HAMMER_STRUCTURE_CLUSTER: 4828cd0a023SMatthew Dillon hammer_rel_cluster(&io->cluster, 1); 48366325755SMatthew Dillon break; 48466325755SMatthew Dillon case HAMMER_STRUCTURE_BUFFER: 4858cd0a023SMatthew Dillon hammer_rel_buffer(&io->buffer, 1); 48666325755SMatthew Dillon break; 48766325755SMatthew Dillon } 4888cd0a023SMatthew Dillon } else { 4898cd0a023SMatthew Dillon /* 490a89aec1bSMatthew Dillon * Otherwise tell the kernel not to destroy the buffer. 491a89aec1bSMatthew Dillon * 492a89aec1bSMatthew Dillon * We have to unref the structure without performing any 493a89aec1bSMatthew Dillon * final rights to it to avoid a deadlock. 4948cd0a023SMatthew Dillon */ 4958cd0a023SMatthew Dillon bp->b_flags |= B_LOCKED; 496a89aec1bSMatthew Dillon hammer_unref(&io->io.lock); 49766325755SMatthew Dillon } 498a89aec1bSMatthew Dillon 49966325755SMatthew Dillon crit_exit(); 50066325755SMatthew Dillon } 50166325755SMatthew Dillon 50266325755SMatthew Dillon static int 50366325755SMatthew Dillon hammer_io_fsync(struct vnode *vp) 50466325755SMatthew Dillon { 50566325755SMatthew Dillon return(0); 50666325755SMatthew Dillon } 50766325755SMatthew Dillon 50866325755SMatthew Dillon /* 50966325755SMatthew Dillon * NOTE: will not be called unless we tell the kernel about the 51066325755SMatthew Dillon * bioops. Unused... we use the mount's VFS_SYNC instead. 51166325755SMatthew Dillon */ 51266325755SMatthew Dillon static int 51366325755SMatthew Dillon hammer_io_sync(struct mount *mp) 51466325755SMatthew Dillon { 51566325755SMatthew Dillon return(0); 51666325755SMatthew Dillon } 51766325755SMatthew Dillon 51866325755SMatthew Dillon static void 51966325755SMatthew Dillon hammer_io_movedeps(struct buf *bp1, struct buf *bp2) 52066325755SMatthew Dillon { 52166325755SMatthew Dillon } 52266325755SMatthew Dillon 52366325755SMatthew Dillon /* 52466325755SMatthew Dillon * I/O pre-check for reading and writing. HAMMER only uses this for 52566325755SMatthew Dillon * B_CACHE buffers so checkread just shouldn't happen, but if it does 52666325755SMatthew Dillon * allow it. 52766325755SMatthew Dillon * 528fbc6e32aSMatthew Dillon * Writing is a different case. We don't want the kernel to try to write 529fbc6e32aSMatthew Dillon * out a buffer that HAMMER may be modifying passively or which has a 530fbc6e32aSMatthew Dillon * dependancy. 531fbc6e32aSMatthew Dillon * 532fbc6e32aSMatthew Dillon * This code enforces the following write ordering: buffers, then cluster 533fbc6e32aSMatthew Dillon * headers, then volume headers. 53466325755SMatthew Dillon */ 53566325755SMatthew Dillon static int 53666325755SMatthew Dillon hammer_io_checkread(struct buf *bp) 53766325755SMatthew Dillon { 53866325755SMatthew Dillon return(0); 53966325755SMatthew Dillon } 54066325755SMatthew Dillon 54166325755SMatthew Dillon static int 54266325755SMatthew Dillon hammer_io_checkwrite(struct buf *bp) 54366325755SMatthew Dillon { 544fbc6e32aSMatthew Dillon union hammer_io_structure *iou = (void *)LIST_FIRST(&bp->b_dep); 54566325755SMatthew Dillon 546fbc6e32aSMatthew Dillon if (iou->io.type == HAMMER_STRUCTURE_BUFFER && 547fbc6e32aSMatthew Dillon iou->buffer.cluster->state == HAMMER_CLUSTER_ASYNC) { 548fbc6e32aSMatthew Dillon /* 549fbc6e32aSMatthew Dillon * Cannot write out a cluster buffer if the cluster header 550fbc6e32aSMatthew Dillon * I/O opening the cluster has not completed. 551fbc6e32aSMatthew Dillon */ 552fbc6e32aSMatthew Dillon bp->b_flags |= B_LOCKED; 553fbc6e32aSMatthew Dillon return(-1); 554fbc6e32aSMatthew Dillon } else if (iou->io.lock.refs) { 555fbc6e32aSMatthew Dillon /* 556fbc6e32aSMatthew Dillon * Cannot write out a bp if its associated buffer has active 557fbc6e32aSMatthew Dillon * references. 558fbc6e32aSMatthew Dillon */ 55966325755SMatthew Dillon bp->b_flags |= B_LOCKED; 56066325755SMatthew Dillon return(-1); 56166325755SMatthew Dillon } else { 562fbc6e32aSMatthew Dillon /* 563fbc6e32aSMatthew Dillon * We're good, but before we can let the kernel proceed we 564fbc6e32aSMatthew Dillon * may have to make some adjustments. 565fbc6e32aSMatthew Dillon */ 566fbc6e32aSMatthew Dillon if (iou->io.type == HAMMER_STRUCTURE_CLUSTER) 567fbc6e32aSMatthew Dillon hammer_close_cluster(&iou->cluster); 568fbc6e32aSMatthew Dillon KKASSERT(iou->io.released); 569fbc6e32aSMatthew Dillon hammer_io_disassociate(iou); 57066325755SMatthew Dillon return(0); 57166325755SMatthew Dillon } 57266325755SMatthew Dillon } 57366325755SMatthew Dillon 5748cd0a023SMatthew Dillon /* 5758cd0a023SMatthew Dillon * Return non-zero if the caller should flush the structure associated 5768cd0a023SMatthew Dillon * with this io sub-structure. 5778cd0a023SMatthew Dillon */ 5788cd0a023SMatthew Dillon int 5798cd0a023SMatthew Dillon hammer_io_checkflush(struct hammer_io *io) 5808cd0a023SMatthew Dillon { 5818cd0a023SMatthew Dillon if (io->bp == NULL || (io->bp->b_flags & B_LOCKED)) 5828cd0a023SMatthew Dillon return(1); 5838cd0a023SMatthew Dillon return(0); 5848cd0a023SMatthew Dillon } 58566325755SMatthew Dillon 58666325755SMatthew Dillon /* 58766325755SMatthew Dillon * Return non-zero if we wish to delay the kernel's attempt to flush 58866325755SMatthew Dillon * this buffer to disk. 58966325755SMatthew Dillon */ 59066325755SMatthew Dillon static int 59166325755SMatthew Dillon hammer_io_countdeps(struct buf *bp, int n) 59266325755SMatthew Dillon { 59366325755SMatthew Dillon return(0); 59466325755SMatthew Dillon } 59566325755SMatthew Dillon 59666325755SMatthew Dillon struct bio_ops hammer_bioops = { 59766325755SMatthew Dillon .io_start = hammer_io_start, 59866325755SMatthew Dillon .io_complete = hammer_io_complete, 59966325755SMatthew Dillon .io_deallocate = hammer_io_deallocate, 60066325755SMatthew Dillon .io_fsync = hammer_io_fsync, 60166325755SMatthew Dillon .io_sync = hammer_io_sync, 60266325755SMatthew Dillon .io_movedeps = hammer_io_movedeps, 60366325755SMatthew Dillon .io_countdeps = hammer_io_countdeps, 60466325755SMatthew Dillon .io_checkread = hammer_io_checkread, 60566325755SMatthew Dillon .io_checkwrite = hammer_io_checkwrite, 60666325755SMatthew Dillon }; 60766325755SMatthew Dillon 608