1 /* $NetBSD: rumpblk.c,v 1.34 2009/12/03 14:05:46 pooka Exp $ */
4 * Copyright (c) 2009 Antti Kantee. All Rights Reserved.
6 * Development of this software was supported by the
7 * Finnish Cultural Foundation.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
19 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * Block device emulation. Presents a block device interface and
33 * uses rumpuser system calls to satisfy I/O requests.
35 * We provide fault injection. The driver can be made to fail
38 * The driver also provides an optimization for regular files by
39 * using memory-mapped I/O. This avoids kernel access for every
40 * I/O operation. It also gives finer-grained control of how to
41 * flush data. Additionally, in case the rump kernel dumps core,
42 * we get way less carnage.
44 * However, it is quite costly in writing large amounts of
45 * file data, since old contents cannot merely be overwritten, but
46 * must be paged in first before replacing (i.e. r/m/w). Ideally,
47 * we should use directio. The problem is that directio can fail
48 * silently causing improper file system semantics (i.e. unflushed
49 * data). Therefore, default to mmap for now. Even so, directio
50 * _should_ be safe and can be enabled by compiling this module
51 * with -DHAS_DIRECTIO.
54 #include <sys/cdefs.h>
55 __KERNEL_RCSID(0, "$NetBSD: rumpblk.c,v 1.34 2009/12/03 14:05:46 pooka Exp $");
57 #include <sys/param.h>
60 #include <sys/condvar.h>
61 #include <sys/disklabel.h>
62 #include <sys/evcnt.h>
63 #include <sys/fcntl.h>
65 #include <sys/malloc.h>
66 #include <sys/queue.h>
69 #include <rump/rumpuser.h>
71 #include "rump_private.h"
72 #include "rump_vfs_private.h"
75 #define DPRINTF(x) printf x
80 /* Default: 16 x 1MB windows */
81 unsigned memwinsize
= (1<<20);
82 unsigned memwincnt
= 16;
84 #define STARTWIN(off) ((off) & ~(memwinsize-1))
85 #define INWIN(win,off) ((win)->win_off == STARTWIN(off))
86 #define WINSIZE(rblk, win) (MIN((rblk->rblk_size-win->win_off),memwinsize))
87 #define WINVALID(win) ((win)->win_off != (off_t)-1)
88 #define WINVALIDATE(win) ((win)->win_off = (off_t)-1)
94 TAILQ_ENTRY(blkwin
) win_lru
;
97 #define RUMPBLK_SIZE 16
98 static struct rblkdev
{
106 uint64_t rblk_hostoffset
;
111 kmutex_t rblk_memmtx
;
112 kcondvar_t rblk_memcv
;
113 TAILQ_HEAD(winlru
, blkwin
) rblk_lruq
;
116 struct disklabel rblk_label
;
117 } minors
[RUMPBLK_SIZE
];
119 static struct evcnt ev_io_total
;
120 static struct evcnt ev_io_async
;
122 static struct evcnt ev_memblk_hits
;
123 static struct evcnt ev_memblk_busy
;
125 static struct evcnt ev_bwrite_total
;
126 static struct evcnt ev_bwrite_async
;
127 static struct evcnt ev_bread_total
;
129 dev_type_open(rumpblk_open
);
130 dev_type_close(rumpblk_close
);
131 dev_type_read(rumpblk_read
);
132 dev_type_write(rumpblk_write
);
133 dev_type_ioctl(rumpblk_ioctl
);
134 dev_type_strategy(rumpblk_strategy
);
135 dev_type_strategy(rumpblk_strategy_fail
);
136 dev_type_dump(rumpblk_dump
);
137 dev_type_size(rumpblk_size
);
139 static const struct bdevsw rumpblk_bdevsw
= {
140 rumpblk_open
, rumpblk_close
, rumpblk_strategy
, rumpblk_ioctl
,
141 nodump
, nosize
, D_DISK
144 static const struct bdevsw rumpblk_bdevsw_fail
= {
145 rumpblk_open
, rumpblk_close
, rumpblk_strategy_fail
, rumpblk_ioctl
,
146 nodump
, nosize
, D_DISK
149 static const struct cdevsw rumpblk_cdevsw
= {
150 rumpblk_open
, rumpblk_close
, rumpblk_read
, rumpblk_write
,
151 rumpblk_ioctl
, nostop
, notty
, nopoll
, nommap
, nokqfilter
, D_DISK
154 /* fail every n out of BLKFAIL_MAX */
155 #define BLKFAIL_MAX 10000
157 static unsigned randstate
;
158 static kmutex_t rumpblk_lock
;
161 makedefaultlabel(struct disklabel
*lp
, off_t size
, int part
)
165 memset(lp
, 0, sizeof(*lp
));
167 lp
->d_secperunit
= size
;
168 lp
->d_secsize
= DEV_BSIZE
;
169 lp
->d_nsectors
= size
>> DEV_BSHIFT
;
171 lp
->d_ncylinders
= 1;
172 lp
->d_secpercyl
= lp
->d_nsectors
;
174 /* oh dear oh dear */
175 strncpy(lp
->d_typename
, "rumpd", sizeof(lp
->d_typename
));
176 strncpy(lp
->d_packname
, "fictitious", sizeof(lp
->d_packname
));
178 lp
->d_type
= DTYPE_RUMPD
;
180 lp
->d_interleave
= 1;
183 /* XXX: RAW_PART handling? */
184 for (i
= 0; i
< part
; i
++) {
185 lp
->d_partitions
[i
].p_fstype
= FS_UNUSED
;
187 lp
->d_partitions
[part
].p_size
= size
>> DEV_BSHIFT
;
188 lp
->d_npartitions
= part
+1;
189 /* XXX: file system type? */
191 lp
->d_magic
= DISKMAGIC
;
192 lp
->d_magic2
= DISKMAGIC
;
193 lp
->d_checksum
= 0; /* XXX */
196 static struct blkwin
*
197 getwindow(struct rblkdev
*rblk
, off_t off
, int *wsize
, int *error
)
201 mutex_enter(&rblk
->rblk_memmtx
);
203 /* search for window */
204 TAILQ_FOREACH(win
, &rblk
->rblk_lruq
, win_lru
) {
205 if (INWIN(win
, off
) && WINVALID(win
))
211 ev_memblk_hits
.ev_count
++;
212 TAILQ_REMOVE(&rblk
->rblk_lruq
, win
, win_lru
);
217 * Else, create new window. If the least recently used is not
218 * currently in use, reuse that. Otherwise we need to wait.
220 win
= TAILQ_LAST(&rblk
->rblk_lruq
, winlru
);
221 if (win
->win_refcnt
== 0) {
222 TAILQ_REMOVE(&rblk
->rblk_lruq
, win
, win_lru
);
223 mutex_exit(&rblk
->rblk_memmtx
);
226 DPRINTF(("win %p, unmap mem %p, off 0x%" PRIx64
"\n",
227 win
, win
->win_mem
, win
->win_off
));
228 rumpuser_unmap(win
->win_mem
, WINSIZE(rblk
, win
));
232 win
->win_off
= STARTWIN(off
);
233 win
->win_mem
= rumpuser_filemmap(rblk
->rblk_fd
, win
->win_off
,
234 WINSIZE(rblk
, win
), rblk
->rblk_mmflags
, error
);
235 DPRINTF(("win %p, off 0x%" PRIx64
", mem %p\n",
236 win
, win
->win_off
, win
->win_mem
));
238 mutex_enter(&rblk
->rblk_memmtx
);
239 if (win
->win_mem
== NULL
) {
241 TAILQ_INSERT_TAIL(&rblk
->rblk_lruq
, win
, win_lru
);
242 mutex_exit(&rblk
->rblk_memmtx
);
246 DPRINTF(("memwin wait\n"));
247 ev_memblk_busy
.ev_count
++;
249 rblk
->rblk_waiting
= true;
250 cv_wait(&rblk
->rblk_memcv
, &rblk
->rblk_memmtx
);
257 TAILQ_INSERT_HEAD(&rblk
->rblk_lruq
, win
, win_lru
);
258 mutex_exit(&rblk
->rblk_memmtx
);
259 *wsize
= MIN(*wsize
, memwinsize
- (off
-win
->win_off
));
266 putwindow(struct rblkdev
*rblk
, struct blkwin
*win
)
269 mutex_enter(&rblk
->rblk_memmtx
);
270 if (--win
->win_refcnt
== 0 && rblk
->rblk_waiting
) {
271 rblk
->rblk_waiting
= false;
272 cv_signal(&rblk
->rblk_memcv
);
274 KASSERT(win
->win_refcnt
>= 0);
275 mutex_exit(&rblk
->rblk_memmtx
);
279 wincleanup(struct rblkdev
*rblk
)
283 while ((win
= TAILQ_FIRST(&rblk
->rblk_lruq
)) != NULL
) {
284 TAILQ_REMOVE(&rblk
->rblk_lruq
, win
, win_lru
);
286 DPRINTF(("cleanup win %p addr %p\n",
288 rumpuser_unmap(win
->win_mem
, WINSIZE(rblk
, win
));
290 kmem_free(win
, sizeof(*win
));
292 rblk
->rblk_mmflags
= 0;
299 int rumpblk
= RUMPBLK
;
303 mutex_init(&rumpblk_lock
, MUTEX_DEFAULT
, IPL_NONE
);
305 if (rumpuser_getenv("RUMP_BLKFAIL", buf
, sizeof(buf
), &error
) == 0) {
306 blkfail
= strtoul(buf
, NULL
, 10);
307 /* fail everything */
308 if (blkfail
> BLKFAIL_MAX
)
309 blkfail
= BLKFAIL_MAX
;
310 if (rumpuser_getenv("RUMP_BLKFAIL_SEED", buf
, sizeof(buf
),
312 randstate
= strtoul(buf
, NULL
, 10);
314 randstate
= arc4random();
316 printf("rumpblk: FAULT INJECTION ACTIVE! fail %d/%d. "
317 "seed %u\n", blkfail
, BLKFAIL_MAX
, randstate
);
322 if (rumpuser_getenv("RUMP_BLKWINSIZE", buf
, sizeof(buf
), &error
) == 0) {
324 tmp
= strtoul(buf
, NULL
, 10);
325 if (tmp
&& !(tmp
& (tmp
-1)))
328 printf("invalid RUMP_BLKWINSIZE %d, ", tmp
);
329 printf("using %d for memwinsize\n", memwinsize
);
331 if (rumpuser_getenv("RUMP_BLKWINCOUNT", buf
, sizeof(buf
), &error
) == 0){
333 tmp
= strtoul(buf
, NULL
, 10);
337 printf("invalid RUMP_BLKWINCOUNT %d, ", tmp
);
338 printf("using %d for memwincount\n", memwincnt
);
341 memset(minors
, 0, sizeof(minors
));
342 for (i
= 0; i
< RUMPBLK_SIZE
; i
++) {
343 mutex_init(&minors
[i
].rblk_memmtx
, MUTEX_DEFAULT
, IPL_NONE
);
344 cv_init(&minors
[i
].rblk_memcv
, "rblkmcv");
347 evcnt_attach_dynamic(&ev_io_total
, EVCNT_TYPE_MISC
, NULL
,
348 "rumpblk", "rumpblk I/O reqs");
349 evcnt_attach_dynamic(&ev_io_async
, EVCNT_TYPE_MISC
, NULL
,
350 "rumpblk", "rumpblk async I/O");
352 evcnt_attach_dynamic(&ev_bread_total
, EVCNT_TYPE_MISC
, NULL
,
353 "rumpblk", "rumpblk bytes read");
354 evcnt_attach_dynamic(&ev_bwrite_total
, EVCNT_TYPE_MISC
, NULL
,
355 "rumpblk", "rumpblk bytes written");
356 evcnt_attach_dynamic(&ev_bwrite_async
, EVCNT_TYPE_MISC
, NULL
,
357 "rumpblk", "rumpblk bytes written async");
359 evcnt_attach_dynamic(&ev_memblk_hits
, EVCNT_TYPE_MISC
, NULL
,
360 "rumpblk", "memblk window hits");
361 evcnt_attach_dynamic(&ev_memblk_busy
, EVCNT_TYPE_MISC
, NULL
,
362 "rumpblk", "memblk all windows busy");
365 return devsw_attach("rumpblk", &rumpblk_bdevsw_fail
, &rumpblk
,
366 &rumpblk_cdevsw
, &rumpblk
);
368 return devsw_attach("rumpblk", &rumpblk_bdevsw
, &rumpblk
,
369 &rumpblk_cdevsw
, &rumpblk
);
373 /* XXX: no deregister */
375 rumpblk_register(const char *path
, devminor_t
*dmin
,
376 uint64_t offset
, uint64_t size
)
378 struct rblkdev
*rblk
;
383 /* devices might not report correct size unless they're open */
384 if (rumpuser_getfileinfo(path
, &flen
, &ftype
, &error
) == -1)
387 /* verify host file is of supported type */
388 if (!(ftype
== RUMPUSER_FT_REG
389 || ftype
== RUMPUSER_FT_BLK
390 || ftype
== RUMPUSER_FT_CHR
))
393 mutex_enter(&rumpblk_lock
);
394 for (i
= 0; i
< RUMPBLK_SIZE
; i
++) {
395 if (minors
[i
].rblk_path
&&strcmp(minors
[i
].rblk_path
, path
)==0) {
396 mutex_exit(&rumpblk_lock
);
402 for (i
= 0; i
< RUMPBLK_SIZE
; i
++)
403 if (minors
[i
].rblk_path
== NULL
)
405 if (i
== RUMPBLK_SIZE
) {
406 mutex_exit(&rumpblk_lock
);
412 rblk
->rblk_path
= malloc(len
+ 1, M_TEMP
, M_WAITOK
);
413 strcpy(rblk
->rblk_path
, path
);
415 rblk
->rblk_hostoffset
= offset
;
416 if (size
!= RUMPBLK_SIZENOTSET
) {
417 KASSERT(size
+ offset
<= flen
);
418 rblk
->rblk_size
= size
;
420 KASSERT(offset
< flen
);
421 rblk
->rblk_size
= flen
- offset
;
423 rblk
->rblk_ftype
= ftype
;
424 makedefaultlabel(&rblk
->rblk_label
, rblk
->rblk_size
, i
);
425 mutex_exit(&rumpblk_lock
);
432 rumpblk_open(dev_t dev
, int flag
, int fmt
, struct lwp
*l
)
434 struct rblkdev
*rblk
= &minors
[minor(dev
)];
437 if (rblk
->rblk_path
== NULL
)
440 if (rblk
->rblk_fd
!= -1)
441 return 0; /* XXX: refcount, open mode */
442 fd
= rumpuser_open(rblk
->rblk_path
, OFLAGS(flag
), &error
);
447 rblk
->rblk_dfd
= rumpuser_open(rblk
->rblk_path
,
448 OFLAGS(flag
) | O_DIRECT
, &error
);
453 if (rblk
->rblk_ftype
== RUMPUSER_FT_REG
) {
454 uint64_t fsize
= rblk
->rblk_size
, off
= rblk
->rblk_hostoffset
;
459 * Use mmap to access a regular file. Allocate and
460 * cache initial windows here. Failure to allocate one
461 * means fallback to read/write i/o.
464 rblk
->rblk_mmflags
= 0;
466 rblk
->rblk_mmflags
|= RUMPUSER_FILEMMAP_READ
;
468 rblk
->rblk_mmflags
|= RUMPUSER_FILEMMAP_WRITE
;
469 rblk
->rblk_mmflags
|= RUMPUSER_FILEMMAP_SHARED
;
472 TAILQ_INIT(&rblk
->rblk_lruq
);
475 for (i
= 0; i
< memwincnt
&& off
+ i
*memwinsize
< fsize
; i
++) {
476 win
= kmem_zalloc(sizeof(*win
), KM_SLEEP
);
478 TAILQ_INSERT_TAIL(&rblk
->rblk_lruq
, win
, win_lru
);
481 * Allocate first windows. Here we just generally
482 * make sure a) we can mmap at all b) we have the
483 * necessary VA available
485 winsize
= memwinsize
;
486 win
= getwindow(rblk
, off
+ i
*memwinsize
, &winsize
,
489 putwindow(rblk
, win
);
499 KASSERT(rblk
->rblk_fd
!= -1);
504 rumpblk_close(dev_t dev
, int flag
, int fmt
, struct lwp
*l
)
506 struct rblkdev
*rblk
= &minors
[minor(dev
)];
509 if (rblk
->rblk_mmflags
)
511 rumpuser_fsync(rblk
->rblk_fd
, &dummy
);
512 rumpuser_close(rblk
->rblk_fd
, &dummy
);
519 rumpblk_ioctl(dev_t dev
, u_long xfer
, void *addr
, int flag
, struct lwp
*l
)
521 devminor_t dmin
= minor(dev
);
522 struct rblkdev
*rblk
= &minors
[dmin
];
526 /* well, me should support a few more, but we don't for now */
529 *(struct disklabel
*)addr
= rblk
->rblk_label
;
534 pi
->part
= &rblk
->rblk_label
.d_partitions
[DISKPART(dmin
)];
535 pi
->disklab
= &rblk
->rblk_label
;
538 /* it's synced enough along the write path */
551 do_physio(dev_t dev
, struct uio
*uio
, int which
)
553 void (*strat
)(struct buf
*);
556 strat
= rumpblk_strategy_fail
;
558 strat
= rumpblk_strategy
;
560 return physio(strat
, NULL
, dev
, which
, minphys
, uio
);
564 rumpblk_read(dev_t dev
, struct uio
*uio
, int flags
)
567 return do_physio(dev
, uio
, B_READ
);
571 rumpblk_write(dev_t dev
, struct uio
*uio
, int flags
)
574 return do_physio(dev
, uio
, B_WRITE
);
578 dostrategy(struct buf
*bp
)
580 struct rblkdev
*rblk
= &minors
[minor(bp
->b_dev
)];
582 int async
= bp
->b_flags
& B_ASYNC
;
585 /* collect statistics */
586 ev_io_total
.ev_count
++;
588 ev_io_async
.ev_count
++;
589 if (BUF_ISWRITE(bp
)) {
590 ev_bwrite_total
.ev_count
+= bp
->b_bcount
;
592 ev_bwrite_async
.ev_count
+= bp
->b_bcount
;
594 ev_bread_total
.ev_count
++;
597 off
= bp
->b_blkno
<< DEV_BSHIFT
;
599 * Do bounds checking if we're working on a file. Otherwise
600 * invalid file systems might attempt to read beyond EOF. This
601 * is bad(tm) especially on mmapped images. This is essentially
602 * the kernel bounds_check() routines.
604 if (off
+ bp
->b_bcount
> rblk
->rblk_size
) {
605 int64_t sz
= rblk
->rblk_size
- off
;
609 rump_biodone(bp
, 0, 0);
612 /* beyond EOF ==> error */
614 rump_biodone(bp
, 0, EINVAL
);
618 /* truncate to device size */
622 off
+= rblk
->rblk_hostoffset
;
623 DPRINTF(("rumpblk_strategy: 0x%x bytes %s off 0x%" PRIx64
624 " (0x%" PRIx64
" - 0x%" PRIx64
"), %ssync\n",
625 bp
->b_bcount
, BUF_ISREAD(bp
) ? "READ" : "WRITE",
626 off
, off
, (off
+ bp
->b_bcount
), async
? "a" : ""));
628 /* mmap? handle here and return */
629 if (rblk
->rblk_mmflags
) {
632 uint8_t *ioaddr
, *bufaddr
;
634 for (iodone
= 0; iodone
< bp
->b_bcount
;
635 iodone
+= winsize
, off
+= winsize
) {
636 winsize
= bp
->b_bcount
- iodone
;
637 win
= getwindow(rblk
, off
, &winsize
, &error
);
639 rump_biodone(bp
, iodone
, error
);
643 ioaddr
= (uint8_t *)win
->win_mem
+ (off
-STARTWIN(off
));
644 bufaddr
= (uint8_t *)bp
->b_data
+ iodone
;
646 DPRINTF(("strat: %p off 0x%" PRIx64
647 ", ioaddr %p (%p)/buf %p\n", win
,
648 win
->win_off
, ioaddr
, win
->win_mem
, bufaddr
));
649 if (BUF_ISREAD(bp
)) {
650 memcpy(bufaddr
, ioaddr
, winsize
);
652 memcpy(ioaddr
, bufaddr
, winsize
);
655 /* synchronous write, sync bits back to disk */
656 if (BUF_ISWRITE(bp
) && !async
) {
657 rumpuser_memsync(ioaddr
, winsize
, &error
);
659 putwindow(rblk
, win
);
662 rump_biodone(bp
, bp
->b_bcount
, 0);
667 * Do I/O. We have different paths for async and sync I/O.
668 * Async I/O is done by passing a request to rumpuser where
669 * it is executed. The rumpuser routine then calls
670 * biodone() to signal any waiters in the kernel. I/O's are
671 * executed in series. Technically executing them in parallel
672 * would produce better results, but then we'd need either
673 * more threads or posix aio. Maybe worth investigating
676 * Using bufq here might be a good idea.
680 struct rumpuser_aio
*rua
;
684 if (BUF_ISREAD(bp
)) {
689 /* O_DIRECT not fully automatic yet */
691 if ((off
& (DEV_BSIZE
-1)) == 0
692 && ((intptr_t)bp
->b_data
&(DEV_BSIZE
-1)) == 0
693 && (bp
->b_bcount
& (DEV_BSIZE
-1)) == 0)
701 rumpuser_mutex_enter(&rumpuser_aio_mtx
);
702 while ((rumpuser_aio_head
+1) % N_AIOS
== rumpuser_aio_tail
) {
703 rumpuser_cv_wait(&rumpuser_aio_cv
, &rumpuser_aio_mtx
);
706 rua
= &rumpuser_aios
[rumpuser_aio_head
];
707 KASSERT(rua
->rua_bp
== NULL
);
709 rua
->rua_data
= bp
->b_data
;
710 rua
->rua_dlen
= bp
->b_bcount
;
715 /* insert into queue & signal */
716 rumpuser_aio_head
= (rumpuser_aio_head
+1) % N_AIOS
;
717 rumpuser_cv_signal(&rumpuser_aio_cv
);
718 rumpuser_mutex_exit(&rumpuser_aio_mtx
);
720 if (BUF_ISREAD(bp
)) {
721 rumpuser_read_bio(rblk
->rblk_fd
, bp
->b_data
,
722 bp
->b_bcount
, off
, rump_biodone
, bp
);
724 rumpuser_write_bio(rblk
->rblk_fd
, bp
->b_data
,
725 bp
->b_bcount
, off
, rump_biodone
, bp
);
727 if (BUF_ISWRITE(bp
) && !async
)
728 rumpuser_fsync(rblk
->rblk_fd
, &error
);
733 rumpblk_strategy(struct buf
*bp
)
740 * Simple random number generator. This is private so that we can
741 * very repeatedly control which blocks will fail.
743 * <mlelstv> pooka, rand()
750 return (randstate
= randstate
* 1103515245 + 12345) % (0x80000000L
);
754 * Block device with very simple fault injection. Fails every
755 * n out of BLKFAIL_MAX I/O with EIO. n is determined by the env
756 * variable RUMP_BLKFAIL.
759 rumpblk_strategy_fail(struct buf
*bp
)
762 if (gimmerand() % BLKFAIL_MAX
>= blkfail
) {
765 printf("block fault injection: failing I/O on block %lld\n",
766 (long long)bp
->b_blkno
);