xref: /dflybsd-src/sys/vfs/hammer/hammer_io.c (revision d8971d2b7bd78be583378b5013c5b54429f622fe)
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*d8971d2bSMatthew Dillon  * $DragonFly: src/sys/vfs/hammer/hammer_io.c,v 1.16 2008/01/11 05:45:19 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 
55055f5ff8SMatthew Dillon static void hammer_io_deallocate(struct buf *bp);
56055f5ff8SMatthew Dillon static int hammer_io_checkwrite(struct buf *bp);
57055f5ff8SMatthew Dillon 
58055f5ff8SMatthew Dillon /*
59055f5ff8SMatthew Dillon  * Initialize an already-zero'd hammer_io structure
60055f5ff8SMatthew Dillon  */
61055f5ff8SMatthew Dillon void
62055f5ff8SMatthew Dillon hammer_io_init(hammer_io_t io, enum hammer_io_type type)
63055f5ff8SMatthew Dillon {
64055f5ff8SMatthew Dillon 	io->type = type;
65055f5ff8SMatthew Dillon 	TAILQ_INIT(&io->deplist);
66055f5ff8SMatthew Dillon }
67055f5ff8SMatthew Dillon 
6866325755SMatthew Dillon /*
69fbc6e32aSMatthew Dillon  * Helper routine to disassociate a buffer cache buffer from an I/O
70055f5ff8SMatthew Dillon  * structure.  Called with the io structure exclusively locked.
71055f5ff8SMatthew Dillon  *
72055f5ff8SMatthew Dillon  * The io may have 0 or 1 references depending on who called us.  The
73055f5ff8SMatthew Dillon  * caller is responsible for dealing with the refs.
74055f5ff8SMatthew Dillon  *
75055f5ff8SMatthew Dillon  * This call can only be made when no action is required on the buffer.
76*d8971d2bSMatthew Dillon  * HAMMER must own the buffer (released == 0) since we mess around with it.
7766325755SMatthew Dillon  */
7866325755SMatthew Dillon static void
79055f5ff8SMatthew Dillon hammer_io_disassociate(hammer_io_structure_t iou, int elseit)
8066325755SMatthew Dillon {
81055f5ff8SMatthew Dillon 	struct buf *bp = iou->io.bp;
8266325755SMatthew Dillon 
83055f5ff8SMatthew Dillon 	KKASSERT(TAILQ_EMPTY(&iou->io.deplist) && iou->io.modified == 0);
844d75d829SMatthew Dillon 	buf_dep_init(bp);
85055f5ff8SMatthew Dillon 	iou->io.bp = NULL;
86*d8971d2bSMatthew Dillon 	bp->b_flags &= ~B_LOCKED;
87055f5ff8SMatthew Dillon 	if (elseit) {
88055f5ff8SMatthew Dillon 		KKASSERT(iou->io.released == 0);
89055f5ff8SMatthew Dillon 		iou->io.released = 1;
90055f5ff8SMatthew Dillon 		bqrelse(bp);
91055f5ff8SMatthew Dillon 	} else {
92055f5ff8SMatthew Dillon 		KKASSERT(iou->io.released);
93055f5ff8SMatthew Dillon 	}
9466325755SMatthew Dillon 
95055f5ff8SMatthew Dillon 	switch(iou->io.type) {
9666325755SMatthew Dillon 	case HAMMER_STRUCTURE_VOLUME:
97055f5ff8SMatthew Dillon 		iou->volume.ondisk = NULL;
98055f5ff8SMatthew Dillon 		iou->volume.alist.meta = NULL;
9966325755SMatthew Dillon 		break;
10066325755SMatthew Dillon 	case HAMMER_STRUCTURE_SUPERCL:
101055f5ff8SMatthew Dillon 		iou->supercl.ondisk = NULL;
102055f5ff8SMatthew Dillon 		iou->supercl.alist.meta = NULL;
10366325755SMatthew Dillon 		break;
10466325755SMatthew Dillon 	case HAMMER_STRUCTURE_CLUSTER:
105055f5ff8SMatthew Dillon 		iou->cluster.ondisk = NULL;
106055f5ff8SMatthew Dillon 		iou->cluster.alist_master.meta = NULL;
107055f5ff8SMatthew Dillon 		iou->cluster.alist_btree.meta = NULL;
108055f5ff8SMatthew Dillon 		iou->cluster.alist_record.meta = NULL;
109055f5ff8SMatthew Dillon 		iou->cluster.alist_mdata.meta = NULL;
11066325755SMatthew Dillon 		break;
11166325755SMatthew Dillon 	case HAMMER_STRUCTURE_BUFFER:
112055f5ff8SMatthew Dillon 		iou->buffer.ondisk = NULL;
113055f5ff8SMatthew Dillon 		iou->buffer.alist.meta = NULL;
11466325755SMatthew Dillon 		break;
11566325755SMatthew Dillon 	}
11666325755SMatthew Dillon }
117fbc6e32aSMatthew Dillon 
118fbc6e32aSMatthew Dillon /*
119055f5ff8SMatthew Dillon  * Wait for any physical IO to complete
120fbc6e32aSMatthew Dillon  */
121fbc6e32aSMatthew Dillon static void
122055f5ff8SMatthew Dillon hammer_io_wait(hammer_io_t io)
123fbc6e32aSMatthew Dillon {
124055f5ff8SMatthew Dillon 	if (io->running) {
125055f5ff8SMatthew Dillon 		crit_enter();
126055f5ff8SMatthew Dillon 		tsleep_interlock(io);
127055f5ff8SMatthew Dillon 		io->waiting = 1;
128055f5ff8SMatthew Dillon 		for (;;) {
129055f5ff8SMatthew Dillon 			tsleep(io, 0, "hmrflw", 0);
130055f5ff8SMatthew Dillon 			if (io->running == 0)
131055f5ff8SMatthew Dillon 				break;
132055f5ff8SMatthew Dillon 			tsleep_interlock(io);
133055f5ff8SMatthew Dillon 			io->waiting = 1;
134055f5ff8SMatthew Dillon 			if (io->running == 0)
135055f5ff8SMatthew Dillon 				break;
136055f5ff8SMatthew Dillon 		}
137055f5ff8SMatthew Dillon 		crit_exit();
138055f5ff8SMatthew Dillon 	}
139055f5ff8SMatthew Dillon }
140055f5ff8SMatthew Dillon 
141055f5ff8SMatthew Dillon void
142055f5ff8SMatthew Dillon hammer_io_waitdep(hammer_io_t io)
143055f5ff8SMatthew Dillon {
144055f5ff8SMatthew Dillon 	while (TAILQ_FIRST(&io->deplist)) {
145055f5ff8SMatthew Dillon 		kprintf("waitdep %p\n", io);
146055f5ff8SMatthew Dillon 		tsleep(io, 0, "hmrdep", hz);
14766325755SMatthew Dillon 	}
148fbc6e32aSMatthew Dillon }
149fbc6e32aSMatthew Dillon 
15061aeeb33SMatthew Dillon /*
151055f5ff8SMatthew Dillon  * Load bp for a HAMMER structure.  The io is exclusively locked by the
152055f5ff8SMatthew Dillon  * caller.
15366325755SMatthew Dillon  */
15466325755SMatthew Dillon int
15566325755SMatthew Dillon hammer_io_read(struct vnode *devvp, struct hammer_io *io)
15666325755SMatthew Dillon {
15766325755SMatthew Dillon 	struct buf *bp;
15866325755SMatthew Dillon 	int error;
15966325755SMatthew Dillon 
16066325755SMatthew Dillon 	if ((bp = io->bp) == NULL) {
16166325755SMatthew Dillon 		error = bread(devvp, io->offset, HAMMER_BUFSIZE, &io->bp);
16266325755SMatthew Dillon 		if (error == 0) {
16366325755SMatthew Dillon 			bp = io->bp;
16466325755SMatthew Dillon 			bp->b_ops = &hammer_bioops;
16566325755SMatthew Dillon 			LIST_INSERT_HEAD(&bp->b_dep, &io->worklist, node);
16666325755SMatthew Dillon 			BUF_KERNPROC(bp);
16766325755SMatthew Dillon 		}
16866325755SMatthew Dillon 		io->modified = 0;	/* no new modifications yet */
16966325755SMatthew Dillon 		io->released = 0;	/* we hold an active lock on bp */
170055f5ff8SMatthew Dillon 		io->running = 0;
171055f5ff8SMatthew Dillon 		io->waiting = 0;
17266325755SMatthew Dillon 	} else {
17366325755SMatthew Dillon 		error = 0;
17466325755SMatthew Dillon 	}
17566325755SMatthew Dillon 	return(error);
17666325755SMatthew Dillon }
17766325755SMatthew Dillon 
17866325755SMatthew Dillon /*
17966325755SMatthew Dillon  * Similar to hammer_io_read() but returns a zero'd out buffer instead.
18066325755SMatthew Dillon  * vfs_bio_clrbuf() is kinda nasty, enforce serialization against background
18166325755SMatthew Dillon  * I/O so we can call it.
182055f5ff8SMatthew Dillon  *
183055f5ff8SMatthew Dillon  * The caller is responsible for calling hammer_modify_*() on the appropriate
184055f5ff8SMatthew Dillon  * HAMMER structure.
18566325755SMatthew Dillon  */
18666325755SMatthew Dillon int
18766325755SMatthew Dillon hammer_io_new(struct vnode *devvp, struct hammer_io *io)
18866325755SMatthew Dillon {
18966325755SMatthew Dillon 	struct buf *bp;
19066325755SMatthew Dillon 
19166325755SMatthew Dillon 	if ((bp = io->bp) == NULL) {
19266325755SMatthew Dillon 		io->bp = getblk(devvp, io->offset, HAMMER_BUFSIZE, 0, 0);
19366325755SMatthew Dillon 		bp = io->bp;
19466325755SMatthew Dillon 		bp->b_ops = &hammer_bioops;
19566325755SMatthew Dillon 		LIST_INSERT_HEAD(&bp->b_dep, &io->worklist, node);
196055f5ff8SMatthew Dillon 		io->modified = 0;
197055f5ff8SMatthew Dillon 		io->released = 0;
198055f5ff8SMatthew Dillon 		io->running = 0;
199055f5ff8SMatthew Dillon 		io->waiting = 0;
20066325755SMatthew Dillon 		BUF_KERNPROC(bp);
20166325755SMatthew Dillon 	} else {
20266325755SMatthew Dillon 		if (io->released) {
20366325755SMatthew Dillon 			regetblk(bp);
20466325755SMatthew Dillon 			BUF_KERNPROC(bp);
205d113fda1SMatthew Dillon 			io->released = 0;
20666325755SMatthew Dillon 		}
20766325755SMatthew Dillon 	}
20866325755SMatthew Dillon 	vfs_bio_clrbuf(bp);
20966325755SMatthew Dillon 	return(0);
21066325755SMatthew Dillon }
21166325755SMatthew Dillon 
21266325755SMatthew Dillon /*
213b3deaf57SMatthew Dillon  * This routine is called on the last reference to a hammer structure.
214055f5ff8SMatthew Dillon  * The io is usually locked exclusively (but may not be during unmount).
215b3deaf57SMatthew Dillon  *
216055f5ff8SMatthew Dillon  * If flush is 1, or B_LOCKED was set indicating that the kernel
217055f5ff8SMatthew Dillon  * wanted to recycle the buffer, and there are no dependancies, this
218055f5ff8SMatthew Dillon  * function will issue an asynchronous write.
21966325755SMatthew Dillon  *
220055f5ff8SMatthew Dillon  * If flush is 2 this function waits until all I/O has completed and
221055f5ff8SMatthew Dillon  * disassociates the bp from the IO before returning, unless there
222055f5ff8SMatthew Dillon  * are still other references.
22366325755SMatthew Dillon  */
22466325755SMatthew Dillon void
22566325755SMatthew Dillon hammer_io_release(struct hammer_io *io, int flush)
22666325755SMatthew Dillon {
22766325755SMatthew Dillon 	struct buf *bp;
22866325755SMatthew Dillon 
229fbc6e32aSMatthew Dillon 	if ((bp = io->bp) == NULL)
230fbc6e32aSMatthew Dillon 		return;
231fbc6e32aSMatthew Dillon 
232055f5ff8SMatthew Dillon #if 0
2330b075555SMatthew Dillon 	/*
234055f5ff8SMatthew Dillon 	 * If flush is 2 wait for dependancies
2350b075555SMatthew Dillon 	 */
236055f5ff8SMatthew Dillon 	while (flush == 2 && TAILQ_FIRST(&io->deplist)) {
237055f5ff8SMatthew Dillon 		hammer_io_wait(TAILQ_FIRST(&io->deplist));
2380b075555SMatthew Dillon 	}
239055f5ff8SMatthew Dillon #endif
2400b075555SMatthew Dillon 
2410b075555SMatthew Dillon 	/*
242055f5ff8SMatthew Dillon 	 * Try to flush a dirty IO to disk if asked to by the caller
243055f5ff8SMatthew Dillon 	 * or if the kernel tried to flush the buffer in the past.
2440b075555SMatthew Dillon 	 *
245055f5ff8SMatthew Dillon 	 * The flush will fail if any dependancies are present.
246055f5ff8SMatthew Dillon 	 */
247055f5ff8SMatthew Dillon 	if (io->modified && (flush || bp->b_flags & B_LOCKED))
248055f5ff8SMatthew Dillon 		hammer_io_flush(io);
249055f5ff8SMatthew Dillon 
250055f5ff8SMatthew Dillon 	/*
251055f5ff8SMatthew Dillon 	 * If flush is 2 we wait for the IO to complete.
252055f5ff8SMatthew Dillon 	 */
253055f5ff8SMatthew Dillon 	if (flush == 2 && io->running) {
254055f5ff8SMatthew Dillon 		hammer_io_wait(io);
255055f5ff8SMatthew Dillon 	}
256055f5ff8SMatthew Dillon 
257055f5ff8SMatthew Dillon 	/*
258055f5ff8SMatthew Dillon 	 * Actively or passively release the buffer.  Modified IOs with
259055f5ff8SMatthew Dillon 	 * dependancies cannot be released.
260055f5ff8SMatthew Dillon 	 */
261055f5ff8SMatthew Dillon 	if (flush && io->modified == 0 && io->running == 0) {
262055f5ff8SMatthew Dillon 		KKASSERT(TAILQ_EMPTY(&io->deplist));
263055f5ff8SMatthew Dillon 		if (io->released) {
264055f5ff8SMatthew Dillon 			regetblk(bp);
265055f5ff8SMatthew Dillon 			io->released = 0;
266055f5ff8SMatthew Dillon 		}
267055f5ff8SMatthew Dillon 		hammer_io_disassociate((hammer_io_structure_t)io, 1);
268055f5ff8SMatthew Dillon 	} else if (io->modified) {
269055f5ff8SMatthew Dillon 		if (io->released == 0 && TAILQ_EMPTY(&io->deplist)) {
270055f5ff8SMatthew Dillon 			io->released = 1;
271055f5ff8SMatthew Dillon 			bdwrite(bp);
272055f5ff8SMatthew Dillon 		}
273055f5ff8SMatthew Dillon 	} else if (io->released == 0) {
274055f5ff8SMatthew Dillon 		io->released = 1;
275055f5ff8SMatthew Dillon 		bqrelse(bp);
276055f5ff8SMatthew Dillon 	}
277055f5ff8SMatthew Dillon }
278055f5ff8SMatthew Dillon 
279055f5ff8SMatthew Dillon /*
280055f5ff8SMatthew Dillon  * This routine is called with a locked IO when a flush is desired.
2810b075555SMatthew Dillon  */
2820b075555SMatthew Dillon void
283055f5ff8SMatthew Dillon hammer_io_flush(struct hammer_io *io)
2840b075555SMatthew Dillon {
285055f5ff8SMatthew Dillon 	struct buf *bp;
286055f5ff8SMatthew Dillon 
287055f5ff8SMatthew Dillon 	/*
288055f5ff8SMatthew Dillon 	 * Can't flush if the IO isn't modified or if it has dependancies.
289055f5ff8SMatthew Dillon 	 */
290055f5ff8SMatthew Dillon 	if (io->modified == 0)
291055f5ff8SMatthew Dillon 		return;
292055f5ff8SMatthew Dillon 	if (TAILQ_FIRST(&io->deplist))
293055f5ff8SMatthew Dillon 		return;
294055f5ff8SMatthew Dillon 
295055f5ff8SMatthew Dillon 	KKASSERT(io->bp);
296055f5ff8SMatthew Dillon 
297055f5ff8SMatthew Dillon 	bp = io->bp;
298055f5ff8SMatthew Dillon 
299055f5ff8SMatthew Dillon 	/*
300055f5ff8SMatthew Dillon 	 * If we are trying to flush a buffer we have to wait until the
301055f5ff8SMatthew Dillon 	 * cluster header for the mark-OPEN has completed its I/O.
302055f5ff8SMatthew Dillon 	 */
303055f5ff8SMatthew Dillon 	if (io->type == HAMMER_STRUCTURE_BUFFER) {
304055f5ff8SMatthew Dillon 		hammer_io_structure_t iou = (void *)io;
305055f5ff8SMatthew Dillon 		hammer_cluster_t cluster = iou->buffer.cluster;
306055f5ff8SMatthew Dillon 
307055f5ff8SMatthew Dillon 		if (cluster->io.running) {
308055f5ff8SMatthew Dillon 			kprintf("WAIT CLUSTER OPEN %d\n", cluster->clu_no);
309055f5ff8SMatthew Dillon 			hammer_io_wait(&cluster->io);
310055f5ff8SMatthew Dillon 			kprintf("WAIT CLUSTER OPEN OK\n");
311055f5ff8SMatthew Dillon 		}
312055f5ff8SMatthew Dillon 	}
313055f5ff8SMatthew Dillon 	if (io->type == HAMMER_STRUCTURE_CLUSTER) {
314055f5ff8SMatthew Dillon 		/*
315055f5ff8SMatthew Dillon 		 * Mark the cluster closed if we can
316055f5ff8SMatthew Dillon 		 */
317055f5ff8SMatthew Dillon 		hammer_io_checkwrite(io->bp);
318055f5ff8SMatthew Dillon 	}
3190b075555SMatthew Dillon 	if (io->released) {
320055f5ff8SMatthew Dillon 		regetblk(bp);
321055f5ff8SMatthew Dillon 		/* BUF_KERNPROC(io->bp); */
322055f5ff8SMatthew Dillon 		io->released = 0;
323055f5ff8SMatthew Dillon 	}
324055f5ff8SMatthew Dillon 	io->released = 1;
325055f5ff8SMatthew Dillon 	io->running = 1;
326055f5ff8SMatthew Dillon 	bawrite(bp);
327055f5ff8SMatthew Dillon }
328055f5ff8SMatthew Dillon 
329055f5ff8SMatthew Dillon /************************************************************************
330055f5ff8SMatthew Dillon  *				BUFFER DIRTYING				*
331055f5ff8SMatthew Dillon  ************************************************************************
332055f5ff8SMatthew Dillon  *
333055f5ff8SMatthew Dillon  * These routines deal with dependancies created when IO buffers get
334055f5ff8SMatthew Dillon  * modified.  The caller must call hammer_modify_*() on a referenced
335055f5ff8SMatthew Dillon  * HAMMER structure prior to modifying its on-disk data.
336055f5ff8SMatthew Dillon  *
337055f5ff8SMatthew Dillon  * Any intent to modify an IO buffer acquires the related bp and imposes
338055f5ff8SMatthew Dillon  * various write ordering dependancies.
339055f5ff8SMatthew Dillon  */
340055f5ff8SMatthew Dillon 
341055f5ff8SMatthew Dillon /*
342055f5ff8SMatthew Dillon  * Ensure that the bp is acquired and return non-zero on a 0->1 transition
343055f5ff8SMatthew Dillon  * of the modified bit.
344055f5ff8SMatthew Dillon  */
345055f5ff8SMatthew Dillon static __inline
346055f5ff8SMatthew Dillon int
347055f5ff8SMatthew Dillon hammer_io_modify(hammer_io_t io, struct hammer_io_list *list)
348055f5ff8SMatthew Dillon {
349055f5ff8SMatthew Dillon 	int r = 0;
350055f5ff8SMatthew Dillon 
351055f5ff8SMatthew Dillon 	KKASSERT(io->lock.refs != 0 && io->bp != NULL);
352055f5ff8SMatthew Dillon 	if (io->modified == 0) {
353055f5ff8SMatthew Dillon 		hammer_lock_ex(&io->lock);
354055f5ff8SMatthew Dillon 		if (io->modified == 0) {
355055f5ff8SMatthew Dillon 			if (io->released) {
356055f5ff8SMatthew Dillon 				regetblk(io->bp);
357055f5ff8SMatthew Dillon 				BUF_KERNPROC(io->bp);
358055f5ff8SMatthew Dillon 				io->released = 0;
359055f5ff8SMatthew Dillon 			}
360055f5ff8SMatthew Dillon 			io->modified = 1;
361055f5ff8SMatthew Dillon 			io->entry_list = list;
362055f5ff8SMatthew Dillon 			if (list)
363055f5ff8SMatthew Dillon 				TAILQ_INSERT_TAIL(list, io, entry);
364055f5ff8SMatthew Dillon 			r = 1;
365055f5ff8SMatthew Dillon 		}
366055f5ff8SMatthew Dillon 		hammer_unlock(&io->lock);
367055f5ff8SMatthew Dillon 	} else if (io->released) {
368055f5ff8SMatthew Dillon 		/*
369055f5ff8SMatthew Dillon 		 * Make sure no IO is occuring while we modify the contents
370055f5ff8SMatthew Dillon 		 * of the buffer. XXX should be able to avoid doing this.
371055f5ff8SMatthew Dillon 		 */
3720b075555SMatthew Dillon 		hammer_lock_ex(&io->lock);
3730b075555SMatthew Dillon 		if (io->released) {
3740b075555SMatthew Dillon 			regetblk(io->bp);
3750b075555SMatthew Dillon 			BUF_KERNPROC(io->bp);
376d113fda1SMatthew Dillon 			io->released = 0;
3770b075555SMatthew Dillon 		}
3780b075555SMatthew Dillon 		hammer_unlock(&io->lock);
3790b075555SMatthew Dillon 	}
380055f5ff8SMatthew Dillon 	return(r);
3810b075555SMatthew Dillon }
3820b075555SMatthew Dillon 
3830b075555SMatthew Dillon void
384055f5ff8SMatthew Dillon hammer_modify_volume(hammer_volume_t volume)
3850b075555SMatthew Dillon {
386055f5ff8SMatthew Dillon 	hammer_io_modify(&volume->io, NULL);
387055f5ff8SMatthew Dillon }
388055f5ff8SMatthew Dillon 
389055f5ff8SMatthew Dillon void
390055f5ff8SMatthew Dillon hammer_modify_supercl(hammer_supercl_t supercl)
391055f5ff8SMatthew Dillon {
392055f5ff8SMatthew Dillon 	hammer_io_modify(&supercl->io, &supercl->volume->io.deplist);
393055f5ff8SMatthew Dillon }
394055f5ff8SMatthew Dillon 
395055f5ff8SMatthew Dillon /*
396055f5ff8SMatthew Dillon  * Caller intends to modify a cluster's ondisk structure.
397055f5ff8SMatthew Dillon  */
398055f5ff8SMatthew Dillon void
399055f5ff8SMatthew Dillon hammer_modify_cluster(hammer_cluster_t cluster)
400055f5ff8SMatthew Dillon {
401055f5ff8SMatthew Dillon 	hammer_io_modify(&cluster->io, &cluster->volume->io.deplist);
402055f5ff8SMatthew Dillon }
403055f5ff8SMatthew Dillon 
404055f5ff8SMatthew Dillon /*
405055f5ff8SMatthew Dillon  * Caller intends to modify a buffer's ondisk structure.  The related
406055f5ff8SMatthew Dillon  * cluster must be marked open prior to being able to flush the modified
407055f5ff8SMatthew Dillon  * buffer so get that I/O going now.
408055f5ff8SMatthew Dillon  */
409055f5ff8SMatthew Dillon void
410055f5ff8SMatthew Dillon hammer_modify_buffer(hammer_buffer_t buffer)
411055f5ff8SMatthew Dillon {
412055f5ff8SMatthew Dillon 	hammer_cluster_t cluster = buffer->cluster;
413055f5ff8SMatthew Dillon 
414055f5ff8SMatthew Dillon 	if (hammer_io_modify(&buffer->io, &cluster->io.deplist)) {
415055f5ff8SMatthew Dillon 		hammer_modify_cluster(cluster);
416055f5ff8SMatthew Dillon 		if ((cluster->ondisk->clu_flags & HAMMER_CLUF_OPEN) == 0) {
417055f5ff8SMatthew Dillon 			hammer_lock_ex(&cluster->io.lock);
418055f5ff8SMatthew Dillon 			if ((cluster->ondisk->clu_flags & HAMMER_CLUF_OPEN) == 0) {
419055f5ff8SMatthew Dillon 				KKASSERT(cluster->io.released == 0);
420055f5ff8SMatthew Dillon 				cluster->ondisk->clu_flags |= HAMMER_CLUF_OPEN;
421055f5ff8SMatthew Dillon 				cluster->io.released = 1;
422055f5ff8SMatthew Dillon 				cluster->io.running = 1;
423055f5ff8SMatthew Dillon 				bawrite(cluster->io.bp);
424f3b0f382SMatthew Dillon 				kprintf("OPEN CLUSTER %d:%d\n",
425f3b0f382SMatthew Dillon 					cluster->volume->vol_no,
426f3b0f382SMatthew Dillon 					cluster->clu_no);
427055f5ff8SMatthew Dillon 			}
428055f5ff8SMatthew Dillon 			hammer_unlock(&cluster->io.lock);
429055f5ff8SMatthew Dillon 		}
4300b075555SMatthew Dillon 	}
4310b075555SMatthew Dillon }
43266325755SMatthew Dillon 
43366325755SMatthew Dillon /*
434055f5ff8SMatthew Dillon  * Mark an entity as not being dirty any more -- this usually occurs when
43561aeeb33SMatthew Dillon  * the governing a-list has freed the entire entity.
436055f5ff8SMatthew Dillon  *
437055f5ff8SMatthew Dillon  * XXX
43861aeeb33SMatthew Dillon  */
43961aeeb33SMatthew Dillon void
44061aeeb33SMatthew Dillon hammer_io_clear_modify(struct hammer_io *io)
44161aeeb33SMatthew Dillon {
442055f5ff8SMatthew Dillon #if 0
44361aeeb33SMatthew Dillon 	struct buf *bp;
44461aeeb33SMatthew Dillon 
44561aeeb33SMatthew Dillon 	io->modified = 0;
44661aeeb33SMatthew Dillon 	if ((bp = io->bp) != NULL) {
447055f5ff8SMatthew Dillon 		if (io->released) {
44861aeeb33SMatthew Dillon 			regetblk(bp);
449055f5ff8SMatthew Dillon 			/* BUF_KERNPROC(io->bp); */
450055f5ff8SMatthew Dillon 		} else {
45161aeeb33SMatthew Dillon 			io->released = 1;
452055f5ff8SMatthew Dillon 		}
45361aeeb33SMatthew Dillon 		if (io->modified == 0) {
45461aeeb33SMatthew Dillon 			kprintf("hammer_io_clear_modify: cleared %p\n", io);
45561aeeb33SMatthew Dillon 			bundirty(bp);
45661aeeb33SMatthew Dillon 			bqrelse(bp);
45761aeeb33SMatthew Dillon 		} else {
45861aeeb33SMatthew Dillon 			bdwrite(bp);
45961aeeb33SMatthew Dillon 		}
46061aeeb33SMatthew Dillon 	}
461fbc6e32aSMatthew Dillon #endif
46266325755SMatthew Dillon }
46366325755SMatthew Dillon 
464055f5ff8SMatthew Dillon /************************************************************************
465055f5ff8SMatthew Dillon  *				HAMMER_BIOOPS				*
466055f5ff8SMatthew Dillon  ************************************************************************
467055f5ff8SMatthew Dillon  *
468055f5ff8SMatthew Dillon  */
469055f5ff8SMatthew Dillon 
470055f5ff8SMatthew Dillon /*
471055f5ff8SMatthew Dillon  * Pre-IO initiation kernel callback - cluster build only
472055f5ff8SMatthew Dillon  */
473055f5ff8SMatthew Dillon static void
474055f5ff8SMatthew Dillon hammer_io_start(struct buf *bp)
475055f5ff8SMatthew Dillon {
476055f5ff8SMatthew Dillon }
477055f5ff8SMatthew Dillon 
478055f5ff8SMatthew Dillon /*
479055f5ff8SMatthew Dillon  * Post-IO completion kernel callback
480055f5ff8SMatthew Dillon  */
48166325755SMatthew Dillon static void
48266325755SMatthew Dillon hammer_io_complete(struct buf *bp)
48366325755SMatthew Dillon {
484055f5ff8SMatthew Dillon 	union hammer_io_structure *iou = (void *)LIST_FIRST(&bp->b_dep);
485fbc6e32aSMatthew Dillon 
486055f5ff8SMatthew Dillon 	KKASSERT(iou->io.released == 1);
487055f5ff8SMatthew Dillon 
488055f5ff8SMatthew Dillon 	if (iou->io.modified == 0)
489055f5ff8SMatthew Dillon 		return;
490055f5ff8SMatthew Dillon 
491055f5ff8SMatthew Dillon 	/*
492055f5ff8SMatthew Dillon 	 * If we were writing the cluster header out and CLUF_OPEN is set,
493055f5ff8SMatthew Dillon 	 * do NOT clear the modify bit.  Just clear the IO running bit
494055f5ff8SMatthew Dillon 	 * and do a wakeup.
495055f5ff8SMatthew Dillon 	 */
496055f5ff8SMatthew Dillon 	if (iou->io.type == HAMMER_STRUCTURE_CLUSTER) {
497055f5ff8SMatthew Dillon 		if (iou->cluster.ondisk->clu_flags & HAMMER_CLUF_OPEN) {
498055f5ff8SMatthew Dillon 			iou->io.running = 0;
499055f5ff8SMatthew Dillon 			if (iou->io.waiting) {
500055f5ff8SMatthew Dillon 				iou->io.waiting = 0;
501055f5ff8SMatthew Dillon 				wakeup(iou);
502fbc6e32aSMatthew Dillon 			}
503055f5ff8SMatthew Dillon 			return;
504055f5ff8SMatthew Dillon 		}
505055f5ff8SMatthew Dillon 	}
506055f5ff8SMatthew Dillon 
507055f5ff8SMatthew Dillon 
508055f5ff8SMatthew Dillon 	/*
509055f5ff8SMatthew Dillon 	 * If this was a write then clear the modified status and remove us
510055f5ff8SMatthew Dillon 	 * from the dependancy list.
511055f5ff8SMatthew Dillon 	 *
512055f5ff8SMatthew Dillon 	 * If no lock references remain and we can acquire the IO lock and
513055f5ff8SMatthew Dillon 	 * someone at some point wanted us to flush (B_LOCKED test), then
514055f5ff8SMatthew Dillon 	 * try to dispose of the IO.
515055f5ff8SMatthew Dillon 	 */
516055f5ff8SMatthew Dillon 	iou->io.modified = 0;
517055f5ff8SMatthew Dillon 	if (iou->io.entry_list) {
518055f5ff8SMatthew Dillon 		TAILQ_REMOVE(iou->io.entry_list, &iou->io, entry);
519055f5ff8SMatthew Dillon 		iou->io.entry_list = NULL;
520055f5ff8SMatthew Dillon 	}
521055f5ff8SMatthew Dillon 	iou->io.running = 0;
522055f5ff8SMatthew Dillon 	if (iou->io.waiting) {
523055f5ff8SMatthew Dillon 		iou->io.waiting = 0;
524055f5ff8SMatthew Dillon 		wakeup(iou);
525055f5ff8SMatthew Dillon 	}
526055f5ff8SMatthew Dillon 
527055f5ff8SMatthew Dillon 	/*
528055f5ff8SMatthew Dillon 	 * Someone wanted us to flush, try to clean out the buffer.
529055f5ff8SMatthew Dillon 	 */
530055f5ff8SMatthew Dillon 	if ((bp->b_flags & B_LOCKED) && iou->io.lock.refs == 0) {
531055f5ff8SMatthew Dillon 		hammer_io_deallocate(bp);
532055f5ff8SMatthew Dillon 		/* structure may be dead now */
533fbc6e32aSMatthew Dillon 	}
53466325755SMatthew Dillon }
53566325755SMatthew Dillon 
53666325755SMatthew Dillon /*
53766325755SMatthew Dillon  * Callback from kernel when it wishes to deallocate a passively
538055f5ff8SMatthew Dillon  * associated structure.  This case can only occur with read-only
539055f5ff8SMatthew Dillon  * bp's.
54066325755SMatthew Dillon  *
54166325755SMatthew Dillon  * If we cannot disassociate we set B_LOCKED to prevent the buffer
54266325755SMatthew Dillon  * from getting reused.
54366325755SMatthew Dillon  */
54466325755SMatthew Dillon static void
54566325755SMatthew Dillon hammer_io_deallocate(struct buf *bp)
54666325755SMatthew Dillon {
547055f5ff8SMatthew Dillon 	hammer_io_structure_t iou = (void *)LIST_FIRST(&bp->b_dep);
54866325755SMatthew Dillon 
549055f5ff8SMatthew Dillon 	KKASSERT((bp->b_flags & B_LOCKED) == 0 && iou->io.running == 0);
550055f5ff8SMatthew Dillon 	if (iou->io.modified) {
551055f5ff8SMatthew Dillon 		bp->b_flags |= B_LOCKED;
552055f5ff8SMatthew Dillon 		return;
5538cd0a023SMatthew Dillon 	}
554055f5ff8SMatthew Dillon 	hammer_ref(&iou->io.lock);
555055f5ff8SMatthew Dillon 	if (iou->io.lock.refs > 1 || iou->io.modified) {
556055f5ff8SMatthew Dillon 		hammer_unref(&iou->io.lock);
557055f5ff8SMatthew Dillon 		bp->b_flags |= B_LOCKED;
558055f5ff8SMatthew Dillon 	} else {
559055f5ff8SMatthew Dillon 		hammer_io_disassociate(iou, 0);
5608cd0a023SMatthew Dillon 
561055f5ff8SMatthew Dillon 		switch(iou->io.type) {
56266325755SMatthew Dillon 		case HAMMER_STRUCTURE_VOLUME:
563055f5ff8SMatthew Dillon 			hammer_rel_volume(&iou->volume, 1);
56466325755SMatthew Dillon 			break;
56566325755SMatthew Dillon 		case HAMMER_STRUCTURE_SUPERCL:
566055f5ff8SMatthew Dillon 			hammer_rel_supercl(&iou->supercl, 1);
56766325755SMatthew Dillon 			break;
56866325755SMatthew Dillon 		case HAMMER_STRUCTURE_CLUSTER:
569055f5ff8SMatthew Dillon 			hammer_rel_cluster(&iou->cluster, 1);
57066325755SMatthew Dillon 			break;
57166325755SMatthew Dillon 		case HAMMER_STRUCTURE_BUFFER:
572055f5ff8SMatthew Dillon 			hammer_rel_buffer(&iou->buffer, 1);
57366325755SMatthew Dillon 			break;
57466325755SMatthew Dillon 		}
57566325755SMatthew Dillon 	}
57666325755SMatthew Dillon }
57766325755SMatthew Dillon 
57866325755SMatthew Dillon static int
57966325755SMatthew Dillon hammer_io_fsync(struct vnode *vp)
58066325755SMatthew Dillon {
58166325755SMatthew Dillon 	return(0);
58266325755SMatthew Dillon }
58366325755SMatthew Dillon 
58466325755SMatthew Dillon /*
58566325755SMatthew Dillon  * NOTE: will not be called unless we tell the kernel about the
58666325755SMatthew Dillon  * bioops.  Unused... we use the mount's VFS_SYNC instead.
58766325755SMatthew Dillon  */
58866325755SMatthew Dillon static int
58966325755SMatthew Dillon hammer_io_sync(struct mount *mp)
59066325755SMatthew Dillon {
59166325755SMatthew Dillon 	return(0);
59266325755SMatthew Dillon }
59366325755SMatthew Dillon 
59466325755SMatthew Dillon static void
59566325755SMatthew Dillon hammer_io_movedeps(struct buf *bp1, struct buf *bp2)
59666325755SMatthew Dillon {
59766325755SMatthew Dillon }
59866325755SMatthew Dillon 
59966325755SMatthew Dillon /*
60066325755SMatthew Dillon  * I/O pre-check for reading and writing.  HAMMER only uses this for
60166325755SMatthew Dillon  * B_CACHE buffers so checkread just shouldn't happen, but if it does
60266325755SMatthew Dillon  * allow it.
60366325755SMatthew Dillon  *
604fbc6e32aSMatthew Dillon  * Writing is a different case.  We don't want the kernel to try to write
605fbc6e32aSMatthew Dillon  * out a buffer that HAMMER may be modifying passively or which has a
606fbc6e32aSMatthew Dillon  * dependancy.
607fbc6e32aSMatthew Dillon  *
608fbc6e32aSMatthew Dillon  * This code enforces the following write ordering: buffers, then cluster
609fbc6e32aSMatthew Dillon  * headers, then volume headers.
61066325755SMatthew Dillon  */
61166325755SMatthew Dillon static int
61266325755SMatthew Dillon hammer_io_checkread(struct buf *bp)
61366325755SMatthew Dillon {
61466325755SMatthew Dillon 	return(0);
61566325755SMatthew Dillon }
61666325755SMatthew Dillon 
61766325755SMatthew Dillon static int
61866325755SMatthew Dillon hammer_io_checkwrite(struct buf *bp)
61966325755SMatthew Dillon {
620fbc6e32aSMatthew Dillon 	union hammer_io_structure *iou = (void *)LIST_FIRST(&bp->b_dep);
62166325755SMatthew Dillon 
622fbc6e32aSMatthew Dillon 	/*
623055f5ff8SMatthew Dillon 	 * A modified cluster with no dependancies can be closed.
624fbc6e32aSMatthew Dillon 	 */
625055f5ff8SMatthew Dillon 	if (iou->io.type == HAMMER_STRUCTURE_CLUSTER && iou->io.modified) {
626055f5ff8SMatthew Dillon 		hammer_cluster_t cluster = &iou->cluster;
627055f5ff8SMatthew Dillon 
628055f5ff8SMatthew Dillon 		if (TAILQ_EMPTY(&cluster->io.deplist)) {
629055f5ff8SMatthew Dillon 			cluster->ondisk->clu_flags &= ~HAMMER_CLUF_OPEN;
630f3b0f382SMatthew Dillon 			kprintf("CLOSE CLUSTER %d:%d\n",
631f3b0f382SMatthew Dillon 				cluster->volume->vol_no,
632f3b0f382SMatthew Dillon 				cluster->clu_no);
63366325755SMatthew Dillon 		}
63466325755SMatthew Dillon 	}
635055f5ff8SMatthew Dillon 	return(0);
636055f5ff8SMatthew Dillon }
63766325755SMatthew Dillon 
6388cd0a023SMatthew Dillon /*
6398cd0a023SMatthew Dillon  * Return non-zero if the caller should flush the structure associated
6408cd0a023SMatthew Dillon  * with this io sub-structure.
6418cd0a023SMatthew Dillon  */
6428cd0a023SMatthew Dillon int
6438cd0a023SMatthew Dillon hammer_io_checkflush(struct hammer_io *io)
6448cd0a023SMatthew Dillon {
645055f5ff8SMatthew Dillon 	if (io->bp == NULL || (io->bp->b_flags & B_LOCKED)) {
6468cd0a023SMatthew Dillon 		return(1);
647055f5ff8SMatthew Dillon 	}
6488cd0a023SMatthew Dillon 	return(0);
6498cd0a023SMatthew Dillon }
65066325755SMatthew Dillon 
65166325755SMatthew Dillon /*
65266325755SMatthew Dillon  * Return non-zero if we wish to delay the kernel's attempt to flush
65366325755SMatthew Dillon  * this buffer to disk.
65466325755SMatthew Dillon  */
65566325755SMatthew Dillon static int
65666325755SMatthew Dillon hammer_io_countdeps(struct buf *bp, int n)
65766325755SMatthew Dillon {
65866325755SMatthew Dillon 	return(0);
65966325755SMatthew Dillon }
66066325755SMatthew Dillon 
66166325755SMatthew Dillon struct bio_ops hammer_bioops = {
66266325755SMatthew Dillon 	.io_start	= hammer_io_start,
66366325755SMatthew Dillon 	.io_complete	= hammer_io_complete,
66466325755SMatthew Dillon 	.io_deallocate	= hammer_io_deallocate,
66566325755SMatthew Dillon 	.io_fsync	= hammer_io_fsync,
66666325755SMatthew Dillon 	.io_sync	= hammer_io_sync,
66766325755SMatthew Dillon 	.io_movedeps	= hammer_io_movedeps,
66866325755SMatthew Dillon 	.io_countdeps	= hammer_io_countdeps,
66966325755SMatthew Dillon 	.io_checkread	= hammer_io_checkread,
67066325755SMatthew Dillon 	.io_checkwrite	= hammer_io_checkwrite,
67166325755SMatthew Dillon };
67266325755SMatthew Dillon 
673