xref: /dflybsd-src/sys/vfs/hammer/hammer_io.c (revision 0b07555568dd51216abf92a42c87d4af4e2362c0)
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