xref: /dragonfly/sys/vfs/hammer2/hammer2_io.c (revision 97703ab4)
1 /*
2  * Copyright (c) 2013-2014 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Matthew Dillon <dillon@dragonflybsd.org>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 
35 #include "hammer2.h"
36 
37 /*
38  * Implements an abstraction layer for synchronous and asynchronous
39  * buffered device I/O.  Can be used for OS-abstraction but the main
40  * purpose is to allow larger buffers to be used against hammer2_chain's
41  * using smaller allocations, without causing deadlocks.
42  *
43  */
44 static int hammer2_io_cleanup_callback(hammer2_io_t *dio, void *arg);
45 
46 static int
47 hammer2_io_cmp(hammer2_io_t *io1, hammer2_io_t *io2)
48 {
49 	if (io2->pbase < io1->pbase)
50 		return(-1);
51 	if (io2->pbase > io1->pbase)
52 		return(1);
53 	return(0);
54 }
55 
56 RB_PROTOTYPE2(hammer2_io_tree, hammer2_io, rbnode, hammer2_io_cmp, off_t);
57 RB_GENERATE2(hammer2_io_tree, hammer2_io, rbnode, hammer2_io_cmp,
58 		off_t, pbase);
59 
60 struct hammer2_cleanupcb_info {
61 	struct hammer2_io_tree tmptree;
62 	int	count;
63 };
64 
65 #define HAMMER2_GETBLK_GOOD	0
66 #define HAMMER2_GETBLK_QUEUED	1
67 #define HAMMER2_GETBLK_OWNED	2
68 
69 /*
70  * Allocate/Locate the requested dio, reference it, issue or queue iocb.
71  */
72 void
73 hammer2_io_getblk(hammer2_mount_t *hmp, off_t lbase, int lsize,
74 		  hammer2_iocb_t *iocb)
75 {
76 	hammer2_io_t *dio;
77 	hammer2_io_t *xio;
78 	off_t pbase;
79 	off_t pmask;
80 	int psize = hammer2_devblksize(lsize);
81 	int refs;
82 
83 	pmask = ~(hammer2_off_t)(psize - 1);
84 
85 	KKASSERT((1 << (int)(lbase & HAMMER2_OFF_MASK_RADIX)) == lsize);
86 	lbase &= ~HAMMER2_OFF_MASK_RADIX;
87 	pbase = lbase & pmask;
88 	KKASSERT(pbase != 0 && ((lbase + lsize - 1) & pmask) == pbase);
89 
90 	/*
91 	 * Access/Allocate the DIO, bump dio->refs to prevent destruction.
92 	 */
93 	spin_lock_shared(&hmp->io_spin);
94 	dio = RB_LOOKUP(hammer2_io_tree, &hmp->iotree, pbase);
95 	if (dio) {
96 		if ((atomic_fetchadd_int(&dio->refs, 1) &
97 		     HAMMER2_DIO_MASK) == 0) {
98 			atomic_add_int(&dio->hmp->iofree_count, -1);
99 		}
100 		spin_unlock_shared(&hmp->io_spin);
101 	} else {
102 		spin_unlock_shared(&hmp->io_spin);
103 		dio = kmalloc(sizeof(*dio), M_HAMMER2, M_INTWAIT | M_ZERO);
104 		dio->hmp = hmp;
105 		dio->pbase = pbase;
106 		dio->psize = psize;
107 		dio->refs = 1;
108 		spin_init(&dio->spin, "h2dio");
109 		TAILQ_INIT(&dio->iocbq);
110 		spin_lock(&hmp->io_spin);
111 		xio = RB_INSERT(hammer2_io_tree, &hmp->iotree, dio);
112 		if (xio == NULL) {
113 			atomic_add_int(&hammer2_dio_count, 1);
114 			spin_unlock(&hmp->io_spin);
115 		} else {
116 			if ((atomic_fetchadd_int(&xio->refs, 1) &
117 			     HAMMER2_DIO_MASK) == 0) {
118 				atomic_add_int(&xio->hmp->iofree_count, -1);
119 			}
120 			spin_unlock(&hmp->io_spin);
121 			kfree(dio, M_HAMMER2);
122 			dio = xio;
123 		}
124 	}
125 
126 	/*
127 	 * Obtain/Validate the buffer.
128 	 */
129 	iocb->dio = dio;
130 
131 	for (;;) {
132 		refs = dio->refs;
133 		cpu_ccfence();
134 
135 		/*
136 		 * Issue the iocb immediately if the buffer is already good.
137 		 * Once set GOOD cannot be cleared until refs drops to 0.
138 		 */
139 		if (refs & HAMMER2_DIO_GOOD) {
140 			iocb->callback(iocb);
141 			break;
142 		}
143 
144 		/*
145 		 * Try to own the DIO by setting INPROG so we can issue
146 		 * I/O on it.
147 		 */
148 		if (refs & HAMMER2_DIO_INPROG) {
149 			/*
150 			 * If DIO_INPROG is already set then set WAITING and
151 			 * queue the iocb.
152 			 */
153 			spin_lock(&dio->spin);
154 			if (atomic_cmpset_int(&dio->refs, refs,
155 					      refs | HAMMER2_DIO_WAITING)) {
156 				iocb->flags |= HAMMER2_IOCB_ONQ |
157 					       HAMMER2_IOCB_INPROG;
158 				TAILQ_INSERT_TAIL(&dio->iocbq, iocb, entry);
159 				spin_unlock(&dio->spin);
160 				break;
161 			}
162 			spin_unlock(&dio->spin);
163 			/* retry */
164 		} else {
165 			/*
166 			 * If DIO_INPROG is not set then set it and issue the
167 			 * callback immediately to start I/O.
168 			 */
169 			if (atomic_cmpset_int(&dio->refs, refs,
170 					      refs | HAMMER2_DIO_INPROG)) {
171 				iocb->flags |= HAMMER2_IOCB_INPROG;
172 				iocb->callback(iocb);
173 				break;
174 			}
175 			/* retry */
176 		}
177 		/* retry */
178 	}
179 	if (dio->act < 5)
180 		++dio->act;
181 }
182 
183 /*
184  * The originator of the iocb is finished with it.
185  */
186 void
187 hammer2_io_complete(hammer2_iocb_t *iocb)
188 {
189 	hammer2_io_t *dio = iocb->dio;
190 	uint32_t orefs;
191 	uint32_t nrefs;
192 	uint32_t oflags;
193 	uint32_t nflags;
194 
195 	/*
196 	 * If IOCB_INPROG was not set completion is synchronous due to the
197 	 * buffer already being good.  We can simply set IOCB_DONE and return.
198 	 * In this situation DIO_INPROG is not set and we have no visibility
199 	 * on dio->bp.
200 	 */
201 	if ((iocb->flags & HAMMER2_IOCB_INPROG) == 0) {
202 		iocb->flags |= HAMMER2_IOCB_DONE;
203 		return;
204 	}
205 
206 	/*
207 	 * The iocb was queued, obtained DIO_INPROG, and its callback was
208 	 * made.  The callback is now complete.  We still own DIO_INPROG.
209 	 *
210 	 * We can set DIO_GOOD if no error occurred, which gives certain
211 	 * stability guarantees to dio->bp and allows other accessors to
212 	 * short-cut access.  DIO_GOOD cannot be cleared until the last
213 	 * ref is dropped.
214 	 */
215 	KKASSERT(dio->refs & HAMMER2_DIO_INPROG);
216 	if (dio->bp) {
217 		BUF_KERNPROC(dio->bp);
218 		if ((dio->bp->b_flags & B_ERROR) == 0) {
219 			KKASSERT(dio->bp->b_flags & B_CACHE);
220 			atomic_set_int(&dio->refs, HAMMER2_DIO_GOOD);
221 		}
222 	}
223 
224 	for (;;) {
225 		oflags = iocb->flags;
226 		cpu_ccfence();
227 		nflags = oflags;
228 		nflags &= ~(HAMMER2_IOCB_WAKEUP | HAMMER2_IOCB_INPROG);
229 		nflags |= HAMMER2_IOCB_DONE;
230 
231 		if (atomic_cmpset_int(&iocb->flags, oflags, nflags)) {
232 			if (oflags & HAMMER2_IOCB_WAKEUP)
233 				wakeup(iocb);
234 			/* SMP: iocb is now stale */
235 			break;
236 		}
237 		/* retry */
238 	}
239 	iocb = NULL;
240 
241 	/*
242 	 * Now finish up the dio.  If another iocb is pending chain to it
243 	 * leaving DIO_INPROG set.  Otherwise clear DIO_INPROG
244 	 * (and DIO_WAITING).
245 	 */
246 	for (;;) {
247 		orefs = dio->refs;
248 		nrefs = orefs & ~(HAMMER2_DIO_WAITING | HAMMER2_DIO_INPROG);
249 
250 		if ((orefs & HAMMER2_DIO_WAITING) && TAILQ_FIRST(&dio->iocbq)) {
251 			spin_lock(&dio->spin);
252 			iocb = TAILQ_FIRST(&dio->iocbq);
253 			if (iocb) {
254 				TAILQ_REMOVE(&dio->iocbq, iocb, entry);
255 				spin_unlock(&dio->spin);
256 				iocb->callback(iocb);	/* chained */
257 				break;
258 			}
259 			spin_unlock(&dio->spin);
260 			/* retry */
261 		} else if (atomic_cmpset_int(&dio->refs, orefs, nrefs)) {
262 			break;
263 		} /* else retry */
264 		/* retry */
265 	}
266 	/* SMP: dio is stale now */
267 }
268 
269 /*
270  * Wait for an iocb's I/O to finish.
271  */
272 void
273 hammer2_iocb_wait(hammer2_iocb_t *iocb)
274 {
275 	uint32_t oflags;
276 	uint32_t nflags;
277 
278 	for (;;) {
279 		oflags = iocb->flags;
280 		cpu_ccfence();
281 		nflags = oflags | HAMMER2_IOCB_WAKEUP;
282 		if (oflags & HAMMER2_IOCB_DONE)
283 			break;
284 		tsleep_interlock(iocb, 0);
285 		if (atomic_cmpset_int(&iocb->flags, oflags, nflags)) {
286 			tsleep(iocb, PINTERLOCKED, "h2iocb", hz);
287 		}
288 	}
289 
290 }
291 
292 /*
293  * Release our ref on *diop.
294  *
295  * On the last ref we must atomically clear DIO_GOOD and set DIO_INPROG,
296  * then dispose of the underlying buffer.
297  */
298 void
299 hammer2_io_putblk(hammer2_io_t **diop)
300 {
301 	hammer2_mount_t *hmp;
302 	hammer2_io_t *dio;
303 	hammer2_iocb_t iocb;
304 	struct buf *bp;
305 	off_t peof;
306 	off_t pbase;
307 	int psize;
308 	int refs;
309 
310 	dio = *diop;
311 	*diop = NULL;
312 
313 	/*
314 	 * Drop refs, on 1->0 transition clear flags, set INPROG.
315 	 */
316 	for (;;) {
317 		refs = dio->refs;
318 
319 		if ((refs & HAMMER2_DIO_MASK) == 1) {
320 			KKASSERT((refs & HAMMER2_DIO_INPROG) == 0);
321 			if (atomic_cmpset_int(&dio->refs, refs,
322 					      ((refs - 1) &
323 					       ~(HAMMER2_DIO_GOOD |
324 						 HAMMER2_DIO_DIRTY)) |
325 					      HAMMER2_DIO_INPROG)) {
326 				break;
327 			}
328 			/* retry */
329 		} else {
330 			if (atomic_cmpset_int(&dio->refs, refs, refs - 1))
331 				return;
332 			/* retry */
333 		}
334 		/* retry */
335 	}
336 
337 	/*
338 	 * We have set DIO_INPROG to gain control of the buffer and we have
339 	 * cleared DIO_GOOD to prevent other accessors from thinking it is
340 	 * still good.
341 	 *
342 	 * We can now dispose of the buffer, and should do it before calling
343 	 * io_complete() in case there's a race against a new reference
344 	 * which causes io_complete() to chain and instantiate the bp again.
345 	 */
346 	pbase = dio->pbase;
347 	psize = dio->psize;
348 	bp = dio->bp;
349 	dio->bp = NULL;
350 
351 	if (refs & HAMMER2_DIO_GOOD) {
352 		KKASSERT(bp != NULL);
353 		if (refs & HAMMER2_DIO_DIRTY) {
354 			if (hammer2_cluster_enable) {
355 				peof = (pbase + HAMMER2_SEGMASK64) &
356 				       ~HAMMER2_SEGMASK64;
357 				cluster_write(bp, peof, psize, 4);
358 			} else {
359 				bp->b_flags |= B_CLUSTEROK;
360 				bdwrite(bp);
361 			}
362 		} else if (bp->b_flags & (B_ERROR | B_INVAL | B_RELBUF)) {
363 			brelse(bp);
364 		} else {
365 			bqrelse(bp);
366 		}
367 	} else if (bp) {
368 		if (refs & HAMMER2_DIO_DIRTY) {
369 			bdwrite(bp);
370 		} else {
371 			brelse(bp);
372 		}
373 	}
374 
375 	/*
376 	 * The instant we call io_complete dio is a free agent again and
377 	 * can be ripped out from under us.
378 	 *
379 	 * we can cleanup our final DIO_INPROG by simulating an iocb
380 	 * completion.
381 	 */
382 	hmp = dio->hmp;				/* extract fields */
383 	atomic_add_int(&hmp->iofree_count, 1);
384 	cpu_ccfence();
385 
386 	iocb.dio = dio;
387 	iocb.flags = HAMMER2_IOCB_INPROG;
388 	hammer2_io_complete(&iocb);
389 	dio = NULL;				/* dio stale */
390 
391 	/*
392 	 * We cache free buffers so re-use cases can use a shared lock, but
393 	 * if too many build up we have to clean them out.
394 	 */
395 	if (hmp->iofree_count > 1000) {
396 		struct hammer2_cleanupcb_info info;
397 
398 		RB_INIT(&info.tmptree);
399 		spin_lock(&hmp->io_spin);
400 		if (hmp->iofree_count > 1000) {
401 			info.count = hmp->iofree_count / 2;
402 			RB_SCAN(hammer2_io_tree, &hmp->iotree, NULL,
403 				hammer2_io_cleanup_callback, &info);
404 		}
405 		spin_unlock(&hmp->io_spin);
406 		hammer2_io_cleanup(hmp, &info.tmptree);
407 	}
408 }
409 
410 /*
411  * Cleanup any dio's with (INPROG | refs) == 0.
412  *
413  * Called to clean up cached DIOs on umount after all activity has been
414  * flushed.
415  */
416 static
417 int
418 hammer2_io_cleanup_callback(hammer2_io_t *dio, void *arg)
419 {
420 	struct hammer2_cleanupcb_info *info = arg;
421 	hammer2_io_t *xio;
422 
423 	if ((dio->refs & (HAMMER2_DIO_MASK | HAMMER2_DIO_INPROG)) == 0) {
424 		if (dio->act > 0) {
425 			--dio->act;
426 			return 0;
427 		}
428 		KKASSERT(dio->bp == NULL);
429 		RB_REMOVE(hammer2_io_tree, &dio->hmp->iotree, dio);
430 		xio = RB_INSERT(hammer2_io_tree, &info->tmptree, dio);
431 		KKASSERT(xio == NULL);
432 		if (--info->count <= 0)	/* limit scan */
433 			return(-1);
434 	}
435 	return 0;
436 }
437 
438 void
439 hammer2_io_cleanup(hammer2_mount_t *hmp, struct hammer2_io_tree *tree)
440 {
441 	hammer2_io_t *dio;
442 
443 	while ((dio = RB_ROOT(tree)) != NULL) {
444 		RB_REMOVE(hammer2_io_tree, tree, dio);
445 		KKASSERT(dio->bp == NULL &&
446 		    (dio->refs & (HAMMER2_DIO_MASK | HAMMER2_DIO_INPROG)) == 0);
447 		kfree(dio, M_HAMMER2);
448 		atomic_add_int(&hammer2_dio_count, -1);
449 		atomic_add_int(&hmp->iofree_count, -1);
450 	}
451 }
452 
453 /*
454  * Returns a pointer to the requested data.
455  */
456 char *
457 hammer2_io_data(hammer2_io_t *dio, off_t lbase)
458 {
459 	struct buf *bp;
460 	int off;
461 
462 	bp = dio->bp;
463 	KKASSERT(bp != NULL);
464 	off = (lbase & ~HAMMER2_OFF_MASK_RADIX) - bp->b_loffset;
465 	KKASSERT(off >= 0 && off < bp->b_bufsize);
466 	return(bp->b_data + off);
467 }
468 
469 /*
470  * Helpers for hammer2_io_new*() functions
471  */
472 static
473 void
474 hammer2_iocb_new_callback(hammer2_iocb_t *iocb)
475 {
476 	hammer2_io_t *dio = iocb->dio;
477 	int gbctl = (iocb->flags & HAMMER2_IOCB_QUICK) ? GETBLK_NOWAIT : 0;
478 
479 	/*
480 	 * If IOCB_INPROG is not set the dio already has a good buffer and we
481 	 * can't mess with it other than zero the requested range.
482 	 *
483 	 * If IOCB_INPROG is set we also own DIO_INPROG at this time and can
484 	 * do what needs to be done with dio->bp.
485 	 */
486 	if (iocb->flags & HAMMER2_IOCB_INPROG) {
487 		if ((iocb->flags & HAMMER2_IOCB_READ) == 0) {
488 			if (iocb->lsize == dio->psize) {
489 				/*
490 				 * Fully covered buffer, try to optimize to
491 				 * avoid any I/O.  We might already have the
492 				 * buffer due to iocb chaining.
493 				 */
494 				if (dio->bp == NULL) {
495 					dio->bp = getblk(dio->hmp->devvp,
496 							 dio->pbase, dio->psize,
497 							 gbctl, 0);
498 				}
499 				if (dio->bp) {
500 					vfs_bio_clrbuf(dio->bp);
501 					dio->bp->b_flags |= B_CACHE;
502 				}
503 			} else if (iocb->flags & HAMMER2_IOCB_QUICK) {
504 				/*
505 				 * Partial buffer, quick mode.  Do nothing.
506 				 * Do not instantiate the buffer or try to
507 				 * mark it B_CACHE because other portions of
508 				 * the buffer might have to be read by other
509 				 * accessors.
510 				 */
511 			} else if (dio->bp == NULL ||
512 				   (dio->bp->b_flags & B_CACHE) == 0) {
513 				/*
514 				 * Partial buffer, normal mode, requires
515 				 * read-before-write.  Chain the read.
516 				 *
517 				 * We might already have the buffer due to
518 				 * iocb chaining.  XXX unclear if we really
519 				 * need to write/release it and reacquire
520 				 * in that case.
521 				 *
522 				 * QUEUE ASYNC I/O, IOCB IS NOT YET COMPLETE.
523 				 */
524 				if (dio->bp) {
525 					if (dio->refs & HAMMER2_DIO_DIRTY)
526 						bdwrite(dio->bp);
527 					else
528 						bqrelse(dio->bp);
529 					dio->bp = NULL;
530 				}
531 				iocb->flags |= HAMMER2_IOCB_READ;
532 				breadcb(dio->hmp->devvp,
533 					dio->pbase, dio->psize,
534 					hammer2_io_callback, iocb);
535 				return;
536 			} /* else buffer is good */
537 		} /* else callback from breadcb is complete */
538 	}
539 	if (dio->bp) {
540 		if (iocb->flags & HAMMER2_IOCB_ZERO)
541 			bzero(hammer2_io_data(dio, iocb->lbase), iocb->lsize);
542 		atomic_set_int(&dio->refs, HAMMER2_DIO_DIRTY);
543 	}
544 	hammer2_io_complete(iocb);
545 }
546 
547 static
548 int
549 _hammer2_io_new(hammer2_mount_t *hmp, off_t lbase, int lsize,
550 	        hammer2_io_t **diop, int flags)
551 {
552 	hammer2_iocb_t iocb;
553 	hammer2_io_t *dio;
554 
555 	iocb.callback = hammer2_iocb_new_callback;
556 	iocb.cluster = NULL;
557 	iocb.chain = NULL;
558 	iocb.ptr = NULL;
559 	iocb.lbase = lbase;
560 	iocb.lsize = lsize;
561 	iocb.flags = flags;
562 	iocb.error = 0;
563 	hammer2_io_getblk(hmp, lbase, lsize, &iocb);
564 	if ((iocb.flags & HAMMER2_IOCB_DONE) == 0)
565 		hammer2_iocb_wait(&iocb);
566 	dio = *diop = iocb.dio;
567 
568 	return (iocb.error);
569 }
570 
571 int
572 hammer2_io_new(hammer2_mount_t *hmp, off_t lbase, int lsize,
573 	       hammer2_io_t **diop)
574 {
575 	return(_hammer2_io_new(hmp, lbase, lsize, diop, HAMMER2_IOCB_ZERO));
576 }
577 
578 int
579 hammer2_io_newnz(hammer2_mount_t *hmp, off_t lbase, int lsize,
580 	       hammer2_io_t **diop)
581 {
582 	return(_hammer2_io_new(hmp, lbase, lsize, diop, 0));
583 }
584 
585 int
586 hammer2_io_newq(hammer2_mount_t *hmp, off_t lbase, int lsize,
587 	       hammer2_io_t **diop)
588 {
589 	return(_hammer2_io_new(hmp, lbase, lsize, diop, HAMMER2_IOCB_QUICK));
590 }
591 
592 static
593 void
594 hammer2_iocb_bread_callback(hammer2_iocb_t *iocb)
595 {
596 	hammer2_io_t *dio = iocb->dio;
597 	off_t peof;
598 	int error;
599 
600 	/*
601 	 * If IOCB_INPROG is not set the dio already has a good buffer and we
602 	 * can't mess with it other than zero the requested range.
603 	 *
604 	 * If IOCB_INPROG is set we also own DIO_INPROG at this time and can
605 	 * do what needs to be done with dio->bp.
606 	 */
607 	if (iocb->flags & HAMMER2_IOCB_INPROG) {
608 		if (dio->bp && (dio->bp->b_flags & B_CACHE)) {
609 			/*
610 			 * Already good, likely due to being chained from
611 			 * another iocb.
612 			 */
613 			error = 0;
614 		} else if (hammer2_cluster_enable) {
615 			/*
616 			 * Synchronous cluster I/O for now.
617 			 */
618 			if (dio->bp) {
619 				bqrelse(dio->bp);
620 				dio->bp = NULL;
621 			}
622 			peof = (dio->pbase + HAMMER2_SEGMASK64) &
623 			       ~HAMMER2_SEGMASK64;
624 			error = cluster_read(dio->hmp->devvp, peof, dio->pbase,
625 					     dio->psize,
626 					     dio->psize, HAMMER2_PBUFSIZE*4,
627 					     &dio->bp);
628 		} else {
629 			/*
630 			 * Synchronous I/O for now.
631 			 */
632 			if (dio->bp) {
633 				bqrelse(dio->bp);
634 				dio->bp = NULL;
635 			}
636 			error = bread(dio->hmp->devvp, dio->pbase,
637 				      dio->psize, &dio->bp);
638 		}
639 		if (error) {
640 			brelse(dio->bp);
641 			dio->bp = NULL;
642 		}
643 	}
644 	hammer2_io_complete(iocb);
645 }
646 
647 int
648 hammer2_io_bread(hammer2_mount_t *hmp, off_t lbase, int lsize,
649 		hammer2_io_t **diop)
650 {
651 	hammer2_iocb_t iocb;
652 	hammer2_io_t *dio;
653 
654 	iocb.callback = hammer2_iocb_bread_callback;
655 	iocb.cluster = NULL;
656 	iocb.chain = NULL;
657 	iocb.ptr = NULL;
658 	iocb.lbase = lbase;
659 	iocb.lsize = lsize;
660 	iocb.flags = 0;
661 	iocb.error = 0;
662 	hammer2_io_getblk(hmp, lbase, lsize, &iocb);
663 	if ((iocb.flags & HAMMER2_IOCB_DONE) == 0)
664 		hammer2_iocb_wait(&iocb);
665 	dio = *diop = iocb.dio;
666 
667 	return (iocb.error);
668 }
669 
670 /*
671  * System buf/bio async callback extracts the iocb and chains
672  * to the iocb callback.
673  */
674 void
675 hammer2_io_callback(struct bio *bio)
676 {
677 	struct buf *dbp = bio->bio_buf;
678 	hammer2_iocb_t *iocb = bio->bio_caller_info1.ptr;
679 	hammer2_io_t *dio;
680 
681 	dio = iocb->dio;
682 	if ((bio->bio_flags & BIO_DONE) == 0)
683 		bpdone(dbp, 0);
684 	bio->bio_flags &= ~(BIO_DONE | BIO_SYNC);
685 	dio->bp = bio->bio_buf;
686 	iocb->callback(iocb);
687 }
688 
689 void
690 hammer2_io_bawrite(hammer2_io_t **diop)
691 {
692 	atomic_set_int(&(*diop)->refs, HAMMER2_DIO_DIRTY);
693 	hammer2_io_putblk(diop);
694 }
695 
696 void
697 hammer2_io_bdwrite(hammer2_io_t **diop)
698 {
699 	atomic_set_int(&(*diop)->refs, HAMMER2_DIO_DIRTY);
700 	hammer2_io_putblk(diop);
701 }
702 
703 int
704 hammer2_io_bwrite(hammer2_io_t **diop)
705 {
706 	atomic_set_int(&(*diop)->refs, HAMMER2_DIO_DIRTY);
707 	hammer2_io_putblk(diop);
708 	return (0);	/* XXX */
709 }
710 
711 void
712 hammer2_io_setdirty(hammer2_io_t *dio)
713 {
714 	atomic_set_int(&dio->refs, HAMMER2_DIO_DIRTY);
715 }
716 
717 void
718 hammer2_io_setinval(hammer2_io_t *dio, u_int bytes)
719 {
720 	if ((u_int)dio->psize == bytes)
721 		dio->bp->b_flags |= B_INVAL | B_RELBUF;
722 }
723 
724 void
725 hammer2_io_brelse(hammer2_io_t **diop)
726 {
727 	hammer2_io_putblk(diop);
728 }
729 
730 void
731 hammer2_io_bqrelse(hammer2_io_t **diop)
732 {
733 	hammer2_io_putblk(diop);
734 }
735 
736 int
737 hammer2_io_isdirty(hammer2_io_t *dio)
738 {
739 	return((dio->refs & HAMMER2_DIO_DIRTY) != 0);
740 }
741