xref: /dflybsd-src/sys/vfs/hammer/hammer_io.c (revision d26d0ae9b763bc95e7517d9825f5488bd3e53e67)
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*d26d0ae9SMatthew Dillon  * $DragonFly: src/sys/vfs/hammer/hammer_io.c,v 1.7 2007/12/29 09:01:27 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 		cluster->ondisk->clu_flags &= ~HAMMER_CLUF_OPEN;
105fbc6e32aSMatthew Dillon 		kprintf("CLOSE CLUSTER\n");
106fbc6e32aSMatthew Dillon 		hammer_modify_cluster(cluster);
10766325755SMatthew Dillon 	}
108fbc6e32aSMatthew Dillon }
109fbc6e32aSMatthew Dillon 
11066325755SMatthew Dillon 
11166325755SMatthew Dillon /*
11266325755SMatthew Dillon  * Load bp for a HAMMER structure.
11366325755SMatthew Dillon  */
11466325755SMatthew Dillon int
11566325755SMatthew Dillon hammer_io_read(struct vnode *devvp, struct hammer_io *io)
11666325755SMatthew Dillon {
11766325755SMatthew Dillon 	struct buf *bp;
11866325755SMatthew Dillon 	int error;
11966325755SMatthew Dillon 
12066325755SMatthew Dillon 	if ((bp = io->bp) == NULL) {
12166325755SMatthew Dillon 		error = bread(devvp, io->offset, HAMMER_BUFSIZE, &io->bp);
12266325755SMatthew Dillon 		if (error == 0) {
12366325755SMatthew Dillon 			bp = io->bp;
12466325755SMatthew Dillon 			bp->b_ops = &hammer_bioops;
12566325755SMatthew Dillon 			LIST_INSERT_HEAD(&bp->b_dep, &io->worklist, node);
12666325755SMatthew Dillon 			BUF_KERNPROC(bp);
12766325755SMatthew Dillon 		}
12866325755SMatthew Dillon 		io->modified = 0;	/* no new modifications yet */
12966325755SMatthew Dillon 		io->released = 0;	/* we hold an active lock on bp */
13066325755SMatthew Dillon 	} else {
13166325755SMatthew Dillon 		error = 0;
13266325755SMatthew Dillon 	}
13366325755SMatthew Dillon 	return(error);
13466325755SMatthew Dillon }
13566325755SMatthew Dillon 
13666325755SMatthew Dillon /*
13766325755SMatthew Dillon  * Similar to hammer_io_read() but returns a zero'd out buffer instead.
13866325755SMatthew Dillon  * vfs_bio_clrbuf() is kinda nasty, enforce serialization against background
13966325755SMatthew Dillon  * I/O so we can call it.
14066325755SMatthew Dillon  */
14166325755SMatthew Dillon int
14266325755SMatthew Dillon hammer_io_new(struct vnode *devvp, struct hammer_io *io)
14366325755SMatthew Dillon {
14466325755SMatthew Dillon 	struct buf *bp;
14566325755SMatthew Dillon 
14666325755SMatthew Dillon 	if ((bp = io->bp) == NULL) {
14766325755SMatthew Dillon 		io->bp = getblk(devvp, io->offset, HAMMER_BUFSIZE, 0, 0);
14866325755SMatthew Dillon 		bp = io->bp;
14966325755SMatthew Dillon 		bp->b_ops = &hammer_bioops;
15066325755SMatthew Dillon 		LIST_INSERT_HEAD(&bp->b_dep, &io->worklist, node);
15166325755SMatthew Dillon 		io->released = 0;	/* we hold an active lock on bp */
15266325755SMatthew Dillon 		BUF_KERNPROC(bp);
15366325755SMatthew Dillon 	} else {
15466325755SMatthew Dillon 		if (io->released) {
15566325755SMatthew Dillon 			regetblk(bp);
15666325755SMatthew Dillon 			io->released = 0;
15766325755SMatthew Dillon 			BUF_KERNPROC(bp);
15866325755SMatthew Dillon 		}
15966325755SMatthew Dillon 	}
16066325755SMatthew Dillon 	io->modified = 1;
16166325755SMatthew Dillon 	vfs_bio_clrbuf(bp);
16266325755SMatthew Dillon 	return(0);
16366325755SMatthew Dillon }
16466325755SMatthew Dillon 
16566325755SMatthew Dillon /*
166fbc6e32aSMatthew Dillon  * This routine is called when a buffer within a cluster is modified.  We
167fbc6e32aSMatthew Dillon  * mark the cluster open and immediately initiate asynchronous I/O.  Any
168fbc6e32aSMatthew Dillon  * related hammer_buffer write I/O blocks until our async write completes.
169fbc6e32aSMatthew Dillon  * This guarentees (inasmuch as the OS can) that the cluster recovery code
170fbc6e32aSMatthew Dillon  * will see a cluster marked open if a crash occured while the filesystem
171fbc6e32aSMatthew Dillon  * still had dirty buffers associated with that cluster.
172fbc6e32aSMatthew Dillon  */
173fbc6e32aSMatthew Dillon void
174fbc6e32aSMatthew Dillon hammer_io_notify_cluster(hammer_cluster_t cluster)
175fbc6e32aSMatthew Dillon {
176fbc6e32aSMatthew Dillon 	struct hammer_io *io = &cluster->io;
177fbc6e32aSMatthew Dillon 
178fbc6e32aSMatthew Dillon 	if (cluster->state == HAMMER_CLUSTER_IDLE) {
179fbc6e32aSMatthew Dillon 		hammer_lock_ex(&cluster->io.lock);
180fbc6e32aSMatthew Dillon 		if (cluster->state == HAMMER_CLUSTER_IDLE) {
181fbc6e32aSMatthew Dillon 			if (io->released)
182fbc6e32aSMatthew Dillon 				regetblk(io->bp);
183fbc6e32aSMatthew Dillon 			kprintf("MARK CLUSTER OPEN\n");
184fbc6e32aSMatthew Dillon 			cluster->ondisk->clu_flags |= HAMMER_CLUF_OPEN;
185fbc6e32aSMatthew Dillon 			cluster->state = HAMMER_CLUSTER_ASYNC;
186fbc6e32aSMatthew Dillon 			hammer_modify_cluster(cluster);
187fbc6e32aSMatthew Dillon 			bawrite(io->bp);
188fbc6e32aSMatthew Dillon 			io->released = 1;
189fbc6e32aSMatthew Dillon 			/* leave cluster marked as modified */
190fbc6e32aSMatthew Dillon 		}
191fbc6e32aSMatthew Dillon 		hammer_unlock(&cluster->io.lock);
192fbc6e32aSMatthew Dillon 	}
193fbc6e32aSMatthew Dillon }
194fbc6e32aSMatthew Dillon 
195fbc6e32aSMatthew Dillon /*
196fbc6e32aSMatthew Dillon  * This routine is called on the last reference to a hammer structure.  If
197fbc6e32aSMatthew Dillon  * flush is non-zero we have to completely disassociate the bp from the
198fbc6e32aSMatthew Dillon  * structure (which may involve blocking).  Otherwise we can leave the bp
199fbc6e32aSMatthew Dillon  * passively associated with the structure.
20066325755SMatthew Dillon  *
201fbc6e32aSMatthew Dillon  * The caller is holding io->lock exclusively.
20266325755SMatthew Dillon  */
20366325755SMatthew Dillon void
20466325755SMatthew Dillon hammer_io_release(struct hammer_io *io, int flush)
20566325755SMatthew Dillon {
20666325755SMatthew Dillon 	union hammer_io_structure *iou = (void *)io;
207fbc6e32aSMatthew Dillon 	hammer_cluster_t cluster;
20866325755SMatthew Dillon 	struct buf *bp;
20966325755SMatthew Dillon 
21066325755SMatthew Dillon 	if ((bp = io->bp) != NULL) {
21166325755SMatthew Dillon 		/*
212fbc6e32aSMatthew Dillon 		 * If neither we nor the kernel want to flush the bp, we can
213fbc6e32aSMatthew Dillon 		 * stop here.  Make sure the bp is passively released
214fbc6e32aSMatthew Dillon 		 * before returning.  Even though we are still holding it,
215fbc6e32aSMatthew Dillon 		 * we want to be notified when the kernel wishes to flush
216fbc6e32aSMatthew Dillon 		 * it out so make sure B_DELWRI is properly set if we had
217fbc6e32aSMatthew Dillon 		 * made modifications.
21866325755SMatthew Dillon 		 */
219fbc6e32aSMatthew Dillon 		if (flush == 0 && (bp->b_flags & B_LOCKED) == 0) {
220fbc6e32aSMatthew Dillon 			if ((bp->b_flags & B_DELWRI) == 0 && io->modified) {
221fbc6e32aSMatthew Dillon 				if (io->released)
22266325755SMatthew Dillon 					regetblk(bp);
22366325755SMatthew Dillon 				bdwrite(bp);
224fbc6e32aSMatthew Dillon 				io->released = 1;
225fbc6e32aSMatthew Dillon 			} else if (io->released == 0) {
226fbc6e32aSMatthew Dillon 				bqrelse(bp);
227fbc6e32aSMatthew Dillon 				io->released = 1;
228fbc6e32aSMatthew Dillon 			}
229fbc6e32aSMatthew Dillon 			return;
230fbc6e32aSMatthew Dillon 		}
231fbc6e32aSMatthew Dillon 
232fbc6e32aSMatthew Dillon 		/*
233fbc6e32aSMatthew Dillon 		 * We've been asked to flush the buffer.
234fbc6e32aSMatthew Dillon 		 *
235fbc6e32aSMatthew Dillon 		 * If this is a hammer_buffer we may have to wait for the
236fbc6e32aSMatthew Dillon 		 * cluster header write to complete.
237fbc6e32aSMatthew Dillon 		 */
238fbc6e32aSMatthew Dillon 		if (iou->io.type == HAMMER_STRUCTURE_BUFFER &&
239fbc6e32aSMatthew Dillon 		    (io->modified || (bp->b_flags & B_DELWRI))) {
240fbc6e32aSMatthew Dillon 			cluster = iou->buffer.cluster;
241fbc6e32aSMatthew Dillon 			while (cluster->state == HAMMER_CLUSTER_ASYNC)
242fbc6e32aSMatthew Dillon 				tsleep(iou->buffer.cluster, 0, "hmrdep", 0);
243fbc6e32aSMatthew Dillon 		}
244fbc6e32aSMatthew Dillon 
245fbc6e32aSMatthew Dillon 		/*
246fbc6e32aSMatthew Dillon 		 * If we have an open cluster header, close it
247fbc6e32aSMatthew Dillon 		 */
248fbc6e32aSMatthew Dillon 		if (iou->io.type == HAMMER_STRUCTURE_CLUSTER) {
249fbc6e32aSMatthew Dillon 			hammer_close_cluster(&iou->cluster);
250fbc6e32aSMatthew Dillon 		}
251fbc6e32aSMatthew Dillon 
252fbc6e32aSMatthew Dillon 
253fbc6e32aSMatthew Dillon 		/*
254fbc6e32aSMatthew Dillon 		 * Ok the dependancies are all gone.  Check for the simple
255fbc6e32aSMatthew Dillon 		 * disassociation case.
256fbc6e32aSMatthew Dillon 		 */
257fbc6e32aSMatthew Dillon 		if (io->released && (bp->b_flags & B_LOCKED) == 0 &&
258fbc6e32aSMatthew Dillon 		    (io->modified == 0 || (bp->b_flags & B_DELWRI))) {
259fbc6e32aSMatthew Dillon 			hammer_io_disassociate(iou);
260fbc6e32aSMatthew Dillon 			return;
261fbc6e32aSMatthew Dillon 		}
262fbc6e32aSMatthew Dillon 
263fbc6e32aSMatthew Dillon 		/*
264fbc6e32aSMatthew Dillon 		 * Handle the more complex disassociation case.  Acquire the
265fbc6e32aSMatthew Dillon 		 * buffer, clean up B_LOCKED, and deal with the modified
266fbc6e32aSMatthew Dillon 		 * flag.
267fbc6e32aSMatthew Dillon 		 */
268fbc6e32aSMatthew Dillon 		if (io->released)
269fbc6e32aSMatthew Dillon 			regetblk(bp);
270*d26d0ae9SMatthew Dillon 		io->released = 1;
271fbc6e32aSMatthew Dillon 		bp->b_flags &= ~B_LOCKED;
272fbc6e32aSMatthew Dillon 		if (io->modified || (bp->b_flags & B_DELWRI))
273fbc6e32aSMatthew Dillon 			bawrite(bp);
27466325755SMatthew Dillon 		else
27566325755SMatthew Dillon 			bqrelse(bp);
27666325755SMatthew Dillon 		hammer_io_disassociate(iou);
277fbc6e32aSMatthew Dillon 	}
278fbc6e32aSMatthew Dillon }
279fbc6e32aSMatthew Dillon 
280fbc6e32aSMatthew Dillon /*
281fbc6e32aSMatthew Dillon  * Flush dirty data, if any.
282fbc6e32aSMatthew Dillon  */
283fbc6e32aSMatthew Dillon void
284fbc6e32aSMatthew Dillon hammer_io_flush(struct hammer_io *io, struct hammer_sync_info *info)
285fbc6e32aSMatthew Dillon {
286fbc6e32aSMatthew Dillon 	struct buf *bp;
287fbc6e32aSMatthew Dillon 	int error;
288fbc6e32aSMatthew Dillon 
289fbc6e32aSMatthew Dillon 	if ((bp = io->bp) == NULL)
290fbc6e32aSMatthew Dillon 		return;
291fbc6e32aSMatthew Dillon 	if (bp->b_flags & B_DELWRI)
292fbc6e32aSMatthew Dillon 		io->modified = 1;
293fbc6e32aSMatthew Dillon 	if (io->modified == 0)
294fbc6e32aSMatthew Dillon 		return;
295fbc6e32aSMatthew Dillon 	kprintf("IO FLUSH BP %p TYPE %d REFS %d\n", bp, io->type, io->lock.refs);
296fbc6e32aSMatthew Dillon 	hammer_lock_ex(&io->lock);
297fbc6e32aSMatthew Dillon 
298fbc6e32aSMatthew Dillon 	if ((bp = io->bp) != NULL && io->modified) {
299fbc6e32aSMatthew Dillon 		if (io->released)
300fbc6e32aSMatthew Dillon 			regetblk(bp);
301fbc6e32aSMatthew Dillon 		io->released = 1;
302fbc6e32aSMatthew Dillon 
303fbc6e32aSMatthew Dillon 		/*
304fbc6e32aSMatthew Dillon 		 * We own the bp now
305fbc6e32aSMatthew Dillon 		 */
306fbc6e32aSMatthew Dillon 		if (info->waitfor & MNT_WAIT) {
307fbc6e32aSMatthew Dillon 			io->modified = 0;
308fbc6e32aSMatthew Dillon 			error = bwrite(bp);
309fbc6e32aSMatthew Dillon 			if (error)
310fbc6e32aSMatthew Dillon 				info->error = error;
311fbc6e32aSMatthew Dillon 		} else if (io->lock.refs == 1) {
312fbc6e32aSMatthew Dillon 			io->modified = 0;
313fbc6e32aSMatthew Dillon 			bawrite(bp);
31466325755SMatthew Dillon 		} else {
315*d26d0ae9SMatthew Dillon 			/*
316*d26d0ae9SMatthew Dillon 			 * structure is in-use, don't race the write, but
317*d26d0ae9SMatthew Dillon 			 * also set B_LOCKED so we know something tried to
318*d26d0ae9SMatthew Dillon 			 * flush it.
319*d26d0ae9SMatthew Dillon 			 */
320*d26d0ae9SMatthew Dillon 			kprintf("can't flush bp %p, %d refs - delaying\n",
321*d26d0ae9SMatthew Dillon 				bp, io->lock.refs);
322*d26d0ae9SMatthew Dillon 			bp->b_flags |= B_LOCKED;
323fbc6e32aSMatthew Dillon 			bqrelse(bp);
32466325755SMatthew Dillon 		}
32566325755SMatthew Dillon 	}
326fbc6e32aSMatthew Dillon 	hammer_unlock(&io->lock);
327fbc6e32aSMatthew Dillon }
328fbc6e32aSMatthew Dillon 
32966325755SMatthew Dillon 
33066325755SMatthew Dillon /*
33166325755SMatthew Dillon  * HAMMER_BIOOPS
33266325755SMatthew Dillon  */
33366325755SMatthew Dillon 
33466325755SMatthew Dillon /*
335fbc6e32aSMatthew Dillon  * Pre and post I/O callbacks.
33666325755SMatthew Dillon  */
33766325755SMatthew Dillon static void hammer_io_deallocate(struct buf *bp);
33866325755SMatthew Dillon 
33966325755SMatthew Dillon static void
34066325755SMatthew Dillon hammer_io_start(struct buf *bp)
34166325755SMatthew Dillon {
342fbc6e32aSMatthew Dillon #if 0
343fbc6e32aSMatthew Dillon 	union hammer_io_structure *io = (void *)LIST_FIRST(&bp->b_dep);
344fbc6e32aSMatthew Dillon 
345fbc6e32aSMatthew Dillon 	if (io->io.type == HAMMER_STRUCTURE_BUFFER) {
346fbc6e32aSMatthew Dillon 		while (io->buffer.cluster->io_in_progress) {
347fbc6e32aSMatthew Dillon 			kprintf("hammer_io_start: wait for cluster\n");
348fbc6e32aSMatthew Dillon 			tsleep(io->buffer.cluster, 0, "hmrdep", 0);
349fbc6e32aSMatthew Dillon 			kprintf("hammer_io_start: wait for cluster done\n");
350fbc6e32aSMatthew Dillon 		}
351fbc6e32aSMatthew Dillon 	}
352fbc6e32aSMatthew Dillon #endif
35366325755SMatthew Dillon }
35466325755SMatthew Dillon 
35566325755SMatthew Dillon static void
35666325755SMatthew Dillon hammer_io_complete(struct buf *bp)
35766325755SMatthew Dillon {
358fbc6e32aSMatthew Dillon 	union hammer_io_structure *io = (void *)LIST_FIRST(&bp->b_dep);
359fbc6e32aSMatthew Dillon 
360fbc6e32aSMatthew Dillon 	if (io->io.type == HAMMER_STRUCTURE_CLUSTER) {
361fbc6e32aSMatthew Dillon 		if (io->cluster.state == HAMMER_CLUSTER_ASYNC) {
362fbc6e32aSMatthew Dillon 			kprintf("cluster write complete flags %08x\n",
363fbc6e32aSMatthew Dillon 				io->cluster.ondisk->clu_flags);
364fbc6e32aSMatthew Dillon 			io->cluster.state = HAMMER_CLUSTER_OPEN;
365fbc6e32aSMatthew Dillon 			wakeup(&io->cluster);
366fbc6e32aSMatthew Dillon 		}
367fbc6e32aSMatthew Dillon 	}
36866325755SMatthew Dillon }
36966325755SMatthew Dillon 
37066325755SMatthew Dillon /*
37166325755SMatthew Dillon  * Callback from kernel when it wishes to deallocate a passively
37266325755SMatthew Dillon  * associated structure.  This can only occur if the buffer is
3738cd0a023SMatthew Dillon  * passively associated with the structure.  The kernel has locked
3748cd0a023SMatthew Dillon  * the buffer.
37566325755SMatthew Dillon  *
37666325755SMatthew Dillon  * If we cannot disassociate we set B_LOCKED to prevent the buffer
37766325755SMatthew Dillon  * from getting reused.
37866325755SMatthew Dillon  */
37966325755SMatthew Dillon static void
38066325755SMatthew Dillon hammer_io_deallocate(struct buf *bp)
38166325755SMatthew Dillon {
38266325755SMatthew Dillon 	union hammer_io_structure *io = (void *)LIST_FIRST(&bp->b_dep);
38366325755SMatthew Dillon 
38466325755SMatthew Dillon 	/* XXX memory interlock, spinlock to sync cpus */
38566325755SMatthew Dillon 
386fbc6e32aSMatthew Dillon 	/*
387fbc6e32aSMatthew Dillon 	 * Since the kernel is passing us a locked buffer, the HAMMER
388fbc6e32aSMatthew Dillon 	 * structure had better not believe it has a lock on the buffer.
389fbc6e32aSMatthew Dillon 	 */
39066325755SMatthew Dillon 	KKASSERT(io->io.released);
39166325755SMatthew Dillon 	crit_enter();
3928cd0a023SMatthew Dillon 
3938cd0a023SMatthew Dillon 	/*
3948cd0a023SMatthew Dillon 	 * First, ref the structure to prevent either the buffer or the
395a89aec1bSMatthew Dillon 	 * structure from going away or being unexpectedly flushed.
3968cd0a023SMatthew Dillon 	 */
3978cd0a023SMatthew Dillon 	hammer_ref(&io->io.lock);
3988cd0a023SMatthew Dillon 
3998cd0a023SMatthew Dillon 	/*
4008cd0a023SMatthew Dillon 	 * Buffers can have active references from cached hammer_node's,
4018cd0a023SMatthew Dillon 	 * even if those nodes are themselves passively cached.  Attempt
4028cd0a023SMatthew Dillon 	 * to clean them out.  This may not succeed.
4038cd0a023SMatthew Dillon 	 */
4048cd0a023SMatthew Dillon 	if (io->io.type == HAMMER_STRUCTURE_BUFFER &&
4058cd0a023SMatthew Dillon 	    hammer_lock_ex_try(&io->io.lock) == 0) {
4068cd0a023SMatthew Dillon 		hammer_flush_buffer_nodes(&io->buffer);
4078cd0a023SMatthew Dillon 		hammer_unlock(&io->io.lock);
4088cd0a023SMatthew Dillon 	}
4098cd0a023SMatthew Dillon 
4108cd0a023SMatthew Dillon 	if (hammer_islastref(&io->io.lock)) {
4118cd0a023SMatthew Dillon 		/*
412fbc6e32aSMatthew Dillon 		 * If we are the only ref left we can disassociate the I/O.
413fbc6e32aSMatthew Dillon 		 * It had better still be in a released state because the
414fbc6e32aSMatthew Dillon 		 * kernel is holding a lock on the buffer.  Any passive
415fbc6e32aSMatthew Dillon 		 * modifications should have already been synchronized with
416fbc6e32aSMatthew Dillon 		 * the buffer.
4178cd0a023SMatthew Dillon 		 */
4188cd0a023SMatthew Dillon 		KKASSERT(io->io.released);
4198cd0a023SMatthew Dillon 		hammer_io_disassociate(io);
4208cd0a023SMatthew Dillon 		bp->b_flags &= ~B_LOCKED;
421fbc6e32aSMatthew Dillon 		KKASSERT (io->io.modified == 0 || (bp->b_flags & B_DELWRI));
4228cd0a023SMatthew Dillon 
423a89aec1bSMatthew Dillon 		/*
424a89aec1bSMatthew Dillon 		 * Perform final rights on the structure.  This can cause
425a89aec1bSMatthew Dillon 		 * a chain reaction - e.g. last buffer -> last cluster ->
426a89aec1bSMatthew Dillon 		 * last supercluster -> last volume.
427a89aec1bSMatthew Dillon 		 */
42866325755SMatthew Dillon 		switch(io->io.type) {
42966325755SMatthew Dillon 		case HAMMER_STRUCTURE_VOLUME:
4308cd0a023SMatthew Dillon 			hammer_rel_volume(&io->volume, 1);
43166325755SMatthew Dillon 			break;
43266325755SMatthew Dillon 		case HAMMER_STRUCTURE_SUPERCL:
4338cd0a023SMatthew Dillon 			hammer_rel_supercl(&io->supercl, 1);
43466325755SMatthew Dillon 			break;
43566325755SMatthew Dillon 		case HAMMER_STRUCTURE_CLUSTER:
4368cd0a023SMatthew Dillon 			hammer_rel_cluster(&io->cluster, 1);
43766325755SMatthew Dillon 			break;
43866325755SMatthew Dillon 		case HAMMER_STRUCTURE_BUFFER:
4398cd0a023SMatthew Dillon 			hammer_rel_buffer(&io->buffer, 1);
44066325755SMatthew Dillon 			break;
44166325755SMatthew Dillon 		}
4428cd0a023SMatthew Dillon 	} else {
4438cd0a023SMatthew Dillon 		/*
444a89aec1bSMatthew Dillon 		 * Otherwise tell the kernel not to destroy the buffer.
445a89aec1bSMatthew Dillon 		 *
446a89aec1bSMatthew Dillon 		 * We have to unref the structure without performing any
447a89aec1bSMatthew Dillon 		 * final rights to it to avoid a deadlock.
4488cd0a023SMatthew Dillon 		 */
4498cd0a023SMatthew Dillon 		bp->b_flags |= B_LOCKED;
450a89aec1bSMatthew Dillon 		hammer_unref(&io->io.lock);
45166325755SMatthew Dillon 	}
452a89aec1bSMatthew Dillon 
45366325755SMatthew Dillon 	crit_exit();
45466325755SMatthew Dillon }
45566325755SMatthew Dillon 
45666325755SMatthew Dillon static int
45766325755SMatthew Dillon hammer_io_fsync(struct vnode *vp)
45866325755SMatthew Dillon {
45966325755SMatthew Dillon 	return(0);
46066325755SMatthew Dillon }
46166325755SMatthew Dillon 
46266325755SMatthew Dillon /*
46366325755SMatthew Dillon  * NOTE: will not be called unless we tell the kernel about the
46466325755SMatthew Dillon  * bioops.  Unused... we use the mount's VFS_SYNC instead.
46566325755SMatthew Dillon  */
46666325755SMatthew Dillon static int
46766325755SMatthew Dillon hammer_io_sync(struct mount *mp)
46866325755SMatthew Dillon {
46966325755SMatthew Dillon 	return(0);
47066325755SMatthew Dillon }
47166325755SMatthew Dillon 
47266325755SMatthew Dillon static void
47366325755SMatthew Dillon hammer_io_movedeps(struct buf *bp1, struct buf *bp2)
47466325755SMatthew Dillon {
47566325755SMatthew Dillon }
47666325755SMatthew Dillon 
47766325755SMatthew Dillon /*
47866325755SMatthew Dillon  * I/O pre-check for reading and writing.  HAMMER only uses this for
47966325755SMatthew Dillon  * B_CACHE buffers so checkread just shouldn't happen, but if it does
48066325755SMatthew Dillon  * allow it.
48166325755SMatthew Dillon  *
482fbc6e32aSMatthew Dillon  * Writing is a different case.  We don't want the kernel to try to write
483fbc6e32aSMatthew Dillon  * out a buffer that HAMMER may be modifying passively or which has a
484fbc6e32aSMatthew Dillon  * dependancy.
485fbc6e32aSMatthew Dillon  *
486fbc6e32aSMatthew Dillon  * This code enforces the following write ordering: buffers, then cluster
487fbc6e32aSMatthew Dillon  * headers, then volume headers.
48866325755SMatthew Dillon  */
48966325755SMatthew Dillon static int
49066325755SMatthew Dillon hammer_io_checkread(struct buf *bp)
49166325755SMatthew Dillon {
49266325755SMatthew Dillon 	return(0);
49366325755SMatthew Dillon }
49466325755SMatthew Dillon 
49566325755SMatthew Dillon static int
49666325755SMatthew Dillon hammer_io_checkwrite(struct buf *bp)
49766325755SMatthew Dillon {
498fbc6e32aSMatthew Dillon 	union hammer_io_structure *iou = (void *)LIST_FIRST(&bp->b_dep);
49966325755SMatthew Dillon 
500fbc6e32aSMatthew Dillon 	if (iou->io.type == HAMMER_STRUCTURE_BUFFER &&
501fbc6e32aSMatthew Dillon 	    iou->buffer.cluster->state == HAMMER_CLUSTER_ASYNC) {
502fbc6e32aSMatthew Dillon 		/*
503fbc6e32aSMatthew Dillon 		 * Cannot write out a cluster buffer if the cluster header
504fbc6e32aSMatthew Dillon 		 * I/O opening the cluster has not completed.
505fbc6e32aSMatthew Dillon 		 */
506fbc6e32aSMatthew Dillon 		kprintf("hammer_io_checkwrite: w/ depend - delayed\n");
507fbc6e32aSMatthew Dillon 		bp->b_flags |= B_LOCKED;
508fbc6e32aSMatthew Dillon 		return(-1);
509fbc6e32aSMatthew Dillon 	} else if (iou->io.lock.refs) {
510fbc6e32aSMatthew Dillon 		/*
511fbc6e32aSMatthew Dillon 		 * Cannot write out a bp if its associated buffer has active
512fbc6e32aSMatthew Dillon 		 * references.
513fbc6e32aSMatthew Dillon 		 */
514fbc6e32aSMatthew Dillon 		kprintf("hammer_io_checkwrite: w/ refs - delayed\n");
51566325755SMatthew Dillon 		bp->b_flags |= B_LOCKED;
51666325755SMatthew Dillon 		return(-1);
51766325755SMatthew Dillon 	} else {
518fbc6e32aSMatthew Dillon 		/*
519fbc6e32aSMatthew Dillon 		 * We're good, but before we can let the kernel proceed we
520fbc6e32aSMatthew Dillon 		 * may have to make some adjustments.
521fbc6e32aSMatthew Dillon 		 */
522fbc6e32aSMatthew Dillon 		if (iou->io.type == HAMMER_STRUCTURE_CLUSTER)
523fbc6e32aSMatthew Dillon 			hammer_close_cluster(&iou->cluster);
524fbc6e32aSMatthew Dillon 		kprintf("hammer_io_checkwrite: ok\n");
525fbc6e32aSMatthew Dillon 		KKASSERT(iou->io.released);
526fbc6e32aSMatthew Dillon 		hammer_io_disassociate(iou);
52766325755SMatthew Dillon 		return(0);
52866325755SMatthew Dillon 	}
52966325755SMatthew Dillon }
53066325755SMatthew Dillon 
5318cd0a023SMatthew Dillon /*
5328cd0a023SMatthew Dillon  * Return non-zero if the caller should flush the structure associated
5338cd0a023SMatthew Dillon  * with this io sub-structure.
5348cd0a023SMatthew Dillon  */
5358cd0a023SMatthew Dillon int
5368cd0a023SMatthew Dillon hammer_io_checkflush(struct hammer_io *io)
5378cd0a023SMatthew Dillon {
5388cd0a023SMatthew Dillon 	if (io->bp == NULL || (io->bp->b_flags & B_LOCKED))
5398cd0a023SMatthew Dillon 		return(1);
5408cd0a023SMatthew Dillon 	return(0);
5418cd0a023SMatthew Dillon }
54266325755SMatthew Dillon 
54366325755SMatthew Dillon /*
54466325755SMatthew Dillon  * Return non-zero if we wish to delay the kernel's attempt to flush
54566325755SMatthew Dillon  * this buffer to disk.
54666325755SMatthew Dillon  */
54766325755SMatthew Dillon static int
54866325755SMatthew Dillon hammer_io_countdeps(struct buf *bp, int n)
54966325755SMatthew Dillon {
55066325755SMatthew Dillon 	return(0);
55166325755SMatthew Dillon }
55266325755SMatthew Dillon 
55366325755SMatthew Dillon struct bio_ops hammer_bioops = {
55466325755SMatthew Dillon 	.io_start	= hammer_io_start,
55566325755SMatthew Dillon 	.io_complete	= hammer_io_complete,
55666325755SMatthew Dillon 	.io_deallocate	= hammer_io_deallocate,
55766325755SMatthew Dillon 	.io_fsync	= hammer_io_fsync,
55866325755SMatthew Dillon 	.io_sync	= hammer_io_sync,
55966325755SMatthew Dillon 	.io_movedeps	= hammer_io_movedeps,
56066325755SMatthew Dillon 	.io_countdeps	= hammer_io_countdeps,
56166325755SMatthew Dillon 	.io_checkread	= hammer_io_checkread,
56266325755SMatthew Dillon 	.io_checkwrite	= hammer_io_checkwrite,
56366325755SMatthew Dillon };
56466325755SMatthew Dillon 
565