Example #1
0
/*
 * Called from roll thread;
 *	buffer set for reading master
 * Returns
 *	0 - success, can continue with next buffer
 *	1 - failure due to logmap deltas being in use
 */
int
top_read_roll(rollbuf_t *rbp, ml_unit_t *ul)
{
	buf_t		*bp	= &rbp->rb_bh;
	offset_t	mof	= ldbtob(bp->b_blkno);

	/*
	 * get a list of deltas
	 */
	if (logmap_list_get_roll(ul->un_logmap, mof, rbp)) {
		/* logmap deltas are in use */
		return (1);
	}

	/*
	 * no deltas were found, nothing to roll
	 */
	if (rbp->rb_age == NULL) {
		bp->b_flags |= B_INVAL;
		return (0);
	}

	/*
	 * If there is one cached roll buffer that cover all the deltas then
	 * we can use that instead of copying to a separate roll buffer.
	 */
	if (rbp->rb_crb) {
		rbp->rb_bh.b_blkno = lbtodb(rbp->rb_crb->c_mof);
		return (0);
	}

	/*
	 * Set up the read.
	 * If no read is needed logmap_setup_read() returns 0.
	 */
	if (logmap_setup_read(rbp->rb_age, rbp)) {
		/*
		 * async read the data from master
		 */
		logstats.ls_rreads.value.ui64++;
		bp->b_bcount = MAPBLOCKSIZE;
		(void) bdev_strategy(bp);
		lwp_stat_update(LWP_STAT_INBLK, 1);
	} else {
		sema_v(&bp->b_io); /* mark read as complete */
	}
	return (0);
}
Example #2
0
/*ARGSUSED*/
int
ufs_rdwr_data(
	vnode_t		*vnodep,
	u_offset_t	offset,
	size_t		len,
	fdbuffer_t	*fdbp,
	int		flags,
	cred_t		*credp)
{
	struct inode	*ip = VTOI(vnodep);
	struct fs	*fs;
	struct ufsvfs	*ufsvfsp = ip->i_ufsvfs;
	struct buf	*bp;
	krw_t		rwtype = RW_READER;
	u_offset_t	offset1 = offset;	/* Initial offset */
	size_t		iolen;
	int		curlen = 0;
	int		pplen;
	daddr_t		bn;
	int		contig = 0;
	int		error = 0;
	int		nbytes;			/* Number bytes this IO */
	int		offsetn;		/* Start point this IO */
	int		iswrite = flags & B_WRITE;
	int		io_started = 0;		/* No IO started */
	struct ulockfs	*ulp;
	uint_t		protp = PROT_ALL;

	error = ufs_lockfs_begin_getpage(ufsvfsp, &ulp, segkmap, !iswrite,
	    &protp);
	if (error) {
		if (flags & B_ASYNC) {
			fdb_ioerrdone(fdbp, error);
		}
		return (error);
	}
	fs = ufsvfsp->vfs_fs;
	iolen = len;

	DEBUGF((CE_CONT, "?ufs_rdwr: %s vp: %p pages:%p  off %llx len %lx"
	    " isize: %llx fdb: %p\n",
	    flags & B_READ ? "READ" : "WRITE", (void *)vnodep,
	    (void *)vnodep->v_pages, offset1, iolen, ip->i_size, (void *)fdbp));

	rw_enter(&ip->i_ufsvfs->vfs_dqrwlock, RW_READER);
	rw_enter(&ip->i_contents, rwtype);

	ASSERT(offset1 < ip->i_size);

	if ((offset1 + iolen) > ip->i_size) {
		iolen = ip->i_size - offset1;
	}
	while (!error && curlen < iolen) {

		contig = 0;

		if ((error = bmap_read(ip, offset1, &bn, &contig)) != 0) {
			break;
		}
		ASSERT(!(bn == UFS_HOLE && iswrite));
		if (bn == UFS_HOLE) {
			/*
			 * If the above assertion is true,
			 * then the following if statement can never be true.
			 */
			if (iswrite && (rwtype == RW_READER)) {
				rwtype = RW_WRITER;
				if (!rw_tryupgrade(&ip->i_contents)) {
					rw_exit(&ip->i_contents);
					rw_enter(&ip->i_contents, rwtype);
					continue;
				}
			}
			offsetn = blkoff(fs, offset1);
			pplen = P2ROUNDUP(len, PAGESIZE);
			nbytes = MIN((pplen - curlen),
			    (fs->fs_bsize - offsetn));
			ASSERT(nbytes > 0);

			/*
			 * We may be reading or writing.
			 */
			DEBUGF((CE_CONT, "?ufs_rdwr_data: hole %llx - %lx\n",
			    offset1, (iolen - curlen)));

			if (iswrite) {
				printf("**WARNING: ignoring hole in write\n");
				error = ENOSPC;
			} else {
				fdb_add_hole(fdbp, offset1 - offset, nbytes);
			}
			offset1 += nbytes;
			curlen += nbytes;
			continue;

		}
		ASSERT(contig > 0);
		pplen = P2ROUNDUP(len, PAGESIZE);

		contig = MIN(contig, len - curlen);
		contig = P2ROUNDUP(contig, DEV_BSIZE);

		bp = fdb_iosetup(fdbp, offset1 - offset, contig, vnodep, flags);

		bp->b_edev = ip->i_dev;
		bp->b_dev = cmpdev(ip->i_dev);
		bp->b_blkno = bn;
		bp->b_file = ip->i_vnode;
		bp->b_offset = (offset_t)offset1;

		if (ufsvfsp->vfs_snapshot) {
			fssnap_strategy(&ufsvfsp->vfs_snapshot, bp);
		} else {
			(void) bdev_strategy(bp);
		}
		io_started = 1;

		offset1 += contig;
		curlen += contig;
		if (iswrite)
			lwp_stat_update(LWP_STAT_OUBLK, 1);
		else
			lwp_stat_update(LWP_STAT_INBLK, 1);

		if ((flags & B_ASYNC) == 0) {
			error = biowait(bp);
			fdb_iodone(bp);
		}

		DEBUGF((CE_CONT, "?loop ufs_rdwr_data.. off %llx len %lx\n",
		    offset1, (iolen - curlen)));
	}

	DEBUGF((CE_CONT, "?ufs_rdwr_data: off %llx len %lx pages: %p ------\n",
	    offset1, (iolen - curlen), (void *)vnodep->v_pages));

	rw_exit(&ip->i_contents);
	rw_exit(&ip->i_ufsvfs->vfs_dqrwlock);

	if (flags & B_ASYNC) {
		/*
		 * Show that no more asynchronous IO will be added
		 */
		fdb_ioerrdone(fdbp, error);
	}
	if (ulp) {
		ufs_lockfs_end(ulp);
	}
	if (io_started && flags & B_ASYNC) {
		return (0);
	} else {
		return (error);
	}
}
Example #3
0
/*
 * ufs_alloc_data - supports allocating space and reads or writes
 * that involve changes to file length or space allocation.
 *
 * This function is more expensive, because of the UFS log transaction,
 * so ufs_rdwr_data() should be used when space or file length changes
 * will not occur.
 *
 * Inputs:
 * fdb - A null pointer instructs this function to only allocate
 *	space for the specified offset and length.
 *	An actual fdbuffer instructs this function to perform
 *	the read or write operation.
 * flags - defaults (zero value) to synchronous write
 *	B_READ - indicates read operation
 *	B_ASYNC - indicates perform operation asynchronously
 */
int
ufs_alloc_data(
	vnode_t		*vnodep,
	u_offset_t	offset,
	size_t		*len,
	fdbuffer_t	*fdbp,
	int		flags,
	cred_t		*credp)
{
	struct inode	*ip = VTOI(vnodep);
	size_t		done_len, io_len;
	int		contig;
	u_offset_t	uoff, io_off;
	int		error = 0;		/* No error occured */
	int		offsetn;		/* Start point this IO */
	int		nbytes;			/* Number bytes in this IO */
	daddr_t		bn;
	struct fs	*fs;
	struct ufsvfs	*ufsvfsp = ip->i_ufsvfs;
	int		i_size_changed = 0;
	u_offset_t	old_i_size;
	struct ulockfs	*ulp;
	int		trans_size;
	int		issync;			/* UFS Log transaction */
						/* synchronous when non-zero */

	int		io_started = 0;		/* No IO started */
	uint_t		protp = PROT_ALL;

	ASSERT((flags & B_WRITE) == 0);

	/*
	 * Obey the lockfs protocol
	 */
	error = ufs_lockfs_begin_getpage(ufsvfsp, &ulp, segkmap, 0, &protp);
	if (error) {
		if ((fdbp != NULL) && (flags & B_ASYNC)) {
			fdb_ioerrdone(fdbp, error);
		}
		return (error);
	}
	if (ulp) {
		/*
		 * Try to begin a UFS log transaction
		 */
		trans_size = TOP_GETPAGE_SIZE(ip);
		TRANS_TRY_BEGIN_CSYNC(ufsvfsp, issync, TOP_GETPAGE,
		    trans_size, error);
		if (error == EWOULDBLOCK) {
			ufs_lockfs_end(ulp);
			if ((fdbp != NULL) && (flags & B_ASYNC)) {
				fdb_ioerrdone(fdbp, EDEADLK);
			}
			return (EDEADLK);
		}
	}

	uoff = offset;
	io_off = offset;
	io_len = *len;
	done_len = 0;

	DEBUGF((CE_CONT, "?ufs_alloc: off %llx len %lx size %llx fdb: %p\n",
	    uoff, (io_len - done_len), ip->i_size, (void *)fdbp));

	rw_enter(&ip->i_ufsvfs->vfs_dqrwlock, RW_READER);
	rw_enter(&ip->i_contents, RW_WRITER);

	ASSERT((ip->i_mode & IFMT) == IFREG);

	fs = ip->i_fs;

	while (error == 0 && done_len < io_len) {
		uoff = (u_offset_t)(io_off + done_len);
		offsetn = (int)blkoff(fs, uoff);
		nbytes = (int)MIN(fs->fs_bsize - offsetn, io_len - done_len);

		DEBUGF((CE_CONT, "?ufs_alloc_data: offset: %llx len %x\n",
		    uoff, nbytes));

		if (uoff + nbytes > ip->i_size) {
			/*
			 * We are extending the length of the file.
			 * bmap is used so that we are sure that
			 * if we need to allocate new blocks, that it
			 * is done here before we up the file size.
			 */
			DEBUGF((CE_CONT, "?ufs_alloc_data: grow %llx -> %llx\n",
			    ip->i_size, uoff + nbytes));

			error = bmap_write(ip, uoff, (offsetn + nbytes),
			    BI_ALLOC_ONLY, NULL, credp);
			if (ip->i_flag & (ICHG|IUPD))
				ip->i_seq++;
			if (error) {
				DEBUGF((CE_CONT, "?ufs_alloc_data: grow "
				    "failed err: %d\n", error));
				break;
			}
			if (fdbp != NULL) {
				if (uoff >= ip->i_size) {
					/*
					 * Desired offset is past end of bytes
					 * in file, so we have a hole.
					 */
					fdb_add_hole(fdbp, uoff - offset,
					    nbytes);
				} else {
					int contig;
					buf_t *bp;

					error = bmap_read(ip, uoff, &bn,
					    &contig);
					if (error) {
						break;
					}

					contig = ip->i_size - uoff;
					contig = P2ROUNDUP(contig, DEV_BSIZE);

					bp = fdb_iosetup(fdbp, uoff - offset,
					    contig, vnodep, flags);

					bp->b_edev = ip->i_dev;
					bp->b_dev = cmpdev(ip->i_dev);
					bp->b_blkno = bn;
					bp->b_file = ip->i_vnode;
					bp->b_offset = (offset_t)uoff;

					if (ufsvfsp->vfs_snapshot) {
						fssnap_strategy(
						    &ufsvfsp->vfs_snapshot, bp);
					} else {
						(void) bdev_strategy(bp);
					}
					io_started = 1;

					lwp_stat_update(LWP_STAT_OUBLK, 1);

					if ((flags & B_ASYNC) == 0) {
						error = biowait(bp);
						fdb_iodone(bp);
						if (error) {
							break;
						}
					}
					if (contig > (ip->i_size - uoff)) {
						contig -= ip->i_size - uoff;

						fdb_add_hole(fdbp,
						    ip->i_size - offset,
						    contig);
					}
				}
			}

			i_size_changed = 1;
			old_i_size = ip->i_size;
			UFS_SET_ISIZE(uoff + nbytes, ip);
			TRANS_INODE(ip->i_ufsvfs, ip);
			/*
			 * file has grown larger than 2GB. Set flag
			 * in superblock to indicate this, if it
			 * is not already set.
			 */
			if ((ip->i_size > MAXOFF32_T) &&
			    !(fs->fs_flags & FSLARGEFILES)) {
				ASSERT(ufsvfsp->vfs_lfflags & UFS_LARGEFILES);
				mutex_enter(&ufsvfsp->vfs_lock);
				fs->fs_flags |= FSLARGEFILES;
				ufs_sbwrite(ufsvfsp);
				mutex_exit(&ufsvfsp->vfs_lock);
			}
		} else {
			/*
			 * The file length is not being extended.
			 */
			error = bmap_read(ip, uoff, &bn, &contig);
			if (error) {
				DEBUGF((CE_CONT, "?ufs_alloc_data: "
				    "bmap_read err: %d\n", error));
				break;
			}

			if (bn != UFS_HOLE) {
				/*
				 * Did not map a hole in the file
				 */
				int	contig = P2ROUNDUP(nbytes, DEV_BSIZE);
				buf_t	*bp;

				if (fdbp != NULL) {
					bp = fdb_iosetup(fdbp, uoff - offset,
					    contig, vnodep, flags);

					bp->b_edev = ip->i_dev;
					bp->b_dev = cmpdev(ip->i_dev);
					bp->b_blkno = bn;
					bp->b_file = ip->i_vnode;
					bp->b_offset = (offset_t)uoff;

					if (ufsvfsp->vfs_snapshot) {
						fssnap_strategy(
						    &ufsvfsp->vfs_snapshot, bp);
					} else {
						(void) bdev_strategy(bp);
					}
					io_started = 1;

					lwp_stat_update(LWP_STAT_OUBLK, 1);

					if ((flags & B_ASYNC) == 0) {
						error = biowait(bp);
						fdb_iodone(bp);
						if (error) {
							break;
						}
					}
				}
			} else {
				/*
				 * We read a hole in the file.
				 * We have to allocate blocks for the hole.
				 */
				error = bmap_write(ip, uoff, (offsetn + nbytes),
				    BI_ALLOC_ONLY, NULL, credp);
				if (ip->i_flag & (ICHG|IUPD))
					ip->i_seq++;
				if (error) {
					DEBUGF((CE_CONT, "?ufs_alloc_data: fill"
					    " hole failed error: %d\n", error));
					break;
				}
				if (fdbp != NULL) {
					fdb_add_hole(fdbp, uoff - offset,
					    nbytes);
				}
			}
		}
		done_len += nbytes;
	}

	if (error) {
		if (i_size_changed) {
			/*
			 * Allocation of the blocks for the file failed.
			 * So truncate the file size back to its original size.
			 */
			(void) ufs_itrunc(ip, old_i_size, 0, credp);
		}
	}

	DEBUGF((CE_CONT, "?ufs_alloc: uoff %llx len %lx\n",
	    uoff, (io_len - done_len)));

	if ((offset + *len) < (NDADDR * fs->fs_bsize)) {
		*len = (size_t)(roundup(offset + *len, fs->fs_fsize) - offset);
	} else {
		*len = (size_t)(roundup(offset + *len, fs->fs_bsize) - offset);
	}

	/*
	 * Flush cached pages.
	 *
	 * XXX - There should be no pages involved, since the I/O was performed
	 * through the device strategy routine and the page cache was bypassed.
	 * However, testing has demonstrated that this VOP_PUTPAGE is
	 * necessary. Without this, data might not always be read back as it
	 * was written.
	 *
	 */
	(void) VOP_PUTPAGE(vnodep, 0, 0, B_INVAL, credp);

	rw_exit(&ip->i_contents);
	rw_exit(&ip->i_ufsvfs->vfs_dqrwlock);

	if ((fdbp != NULL) && (flags & B_ASYNC)) {
		/*
		 * Show that no more asynchronous IO will be added
		 */
		fdb_ioerrdone(fdbp, error);
	}
	if (ulp) {
		/*
		 * End the UFS Log transaction
		 */
		TRANS_END_CSYNC(ufsvfsp, error, issync, TOP_GETPAGE,
		    trans_size);
		ufs_lockfs_end(ulp);
	}
	if (io_started && (flags & B_ASYNC)) {
		return (0);
	} else {
		return (error);
	}
}
Example #4
0
void
lqfs_write_strategy(ml_unit_t *ul, buf_t *bp)
{
	offset_t	mof	= ldbtob(bp->b_blkno);
	off_t		nb	= bp->b_bcount;
	char		*va;
	mapentry_t	*me;
	uchar_t		ord;
	qfsvfs_t	*qfsvfsp = ul->un_qfsvfs;
#ifdef LUFS
#else
	caddr_t		buf;

	va = bp_mapin_common(bp, VM_SLEEP);
	buf = bp->b_un.b_addr;
#endif /* LUFS */

	ASSERT((nb & DEV_BMASK) == 0);
	ul->un_logmap->mtm_ref = 1;

	/*
	 * if there are deltas, move into log
	 */
	ord = lqfs_find_ord(qfsvfsp, bp);
#ifdef LUFS
	me = deltamap_remove(ul->un_deltamap, mof, ord, nb);
	if (me) {
		va = bp_mapin_common(bp, VM_SLEEP);

		ASSERT(((ul->un_debug & MT_WRITE_CHECK) == 0) ||
		    (ul->un_matamap == NULL)||
		    matamap_within(ul->un_matamap, mof, ord, nb));

		/*
		 * move to logmap
		 */
		if (qfs_crb_enable) {
			logmap_add_buf(ul, va, mof, ord, me,
			    bp->b_un.b_addr, nb);
		} else {
			logmap_add(ul, va, mof, ord, me);
		}

		if (ul->un_flags & LDL_ERROR) {
			bp->b_flags |= B_ERROR;
			bp->b_error = EIO;
		}
		biodone(bp);
		return;
	}
#else
	if (buf && qfs_crb_enable) {
		uint32_t	bufsz;
		offset_t	vamof;
		offset_t	hmof;
		uchar_t		vaord;
		uchar_t		hord;
		uint32_t	hnb, nb1;

		bufsz = bp->b_bcount;
		ASSERT((bufsz & DEV_BMASK) == 0);

		vamof = mof;
		vaord = ord;

		/*
		 * Move any deltas to the logmap. Split requests that
		 * straddle MAPBLOCKSIZE hash boundaries (i.e. summary info).
		 */
		for (hmof = vamof - (va - buf), nb1 = nb; bufsz;
		    bufsz -= hnb, hmof += hnb, buf += hnb, nb1 -= hnb) {
			hnb = MAPBLOCKSIZE - (hmof & MAPBLOCKOFF);
			if (hnb > bufsz) {
				hnb = bufsz;
			}
			LQFS_MSG(CE_WARN, "lqfs_write_strategy(): Removing "
			    "deltamap deltas within mof 0x%llx ord %d nb %d\n",
			    MAX(hmof, vamof), vaord, MIN(hnb, nb1));
			me = deltamap_remove(ul->un_deltamap,
			    MAX(hmof, vamof), vaord, MIN(hnb, nb1));
			hord = vaord;
			if (me) {
				logmap_add_buf(ul, va, hmof, hord,
				    me, buf, hnb);

				if (ul->un_flags & LDL_ERROR) {
					bp->b_flags |= B_ERROR;
					bp->b_error = EIO;
				}
				biodone(bp);
				return;
			}
		}
	} else {
		/*
		 * if there are deltas
		 */
		LQFS_MSG(CE_WARN, "lqfs_write_strategy(): Removing "
		    "deltamap deltas within mof 0x%x ord %d nb %d\n",
		    mof, ord, nb);
		me = deltamap_remove(ul->un_deltamap, mof, ord, nb);
		if (me) {
			ASSERT(((ul->un_debug & MT_WRITE_CHECK) == 0) ||
			    (ul->un_matamap == NULL)||
			    matamap_within(ul->un_matamap, mof, ord, nb));

			/*
			 * move to logmap
			 */
			logmap_add(ul, va, mof, ord, me);

			if (ul->un_flags & LDL_ERROR) {
				bp->b_flags |= B_ERROR;
				bp->b_error = EIO;
			}
			biodone(bp);
			return;
		}
	}
#endif /* LUFS */
	if (ul->un_flags & LDL_ERROR) {
		bp->b_flags |= B_ERROR;
		bp->b_error = EIO;
		biodone(bp);
		return;
	}

	/*
	 * Check that we are not updating metadata, or if so then via B_PHYS.
	 */
	ASSERT((ul->un_matamap == NULL) ||
	    !(matamap_overlap(ul->un_matamap, mof, ord, nb) &&
	    ((bp->b_flags & B_PHYS) == 0)));

	LQFS_SET_IOTSTAMP(ul->un_qfsvfs, ddi_get_lbolt());
	logstats.ls_lwrites.value.ui64++;

#ifdef LQFS_TODO_SNAPSHOT
	/* If snapshots are enabled, write through the snapshot driver */
	if (ul->un_qfsvfs->vfs_snapshot) {
		fssnap_strategy(&ul->un_qfsvfs->vfs_snapshot, bp);
	} else {
#endif /* LQFS_TODO_SNAPSHOT */
		if ((bp->b_flags & B_READ) == 0) {
			LQFS_MSG(CE_WARN, "lqfs_write_strategy(): "
			    "bdev_strategy writing mof 0x%x edev %ld "
			    "nb %d\n", bp->b_blkno * 512, bp->b_edev,
			    bp->b_bcount);
		} else {
			LQFS_MSG(CE_WARN, "lqfs_write_strategy(): "
			    "bdev_strategy reading mof 0x%x edev %ld "
			    "nb %d\n", bp->b_blkno * 512, bp->b_edev,
			    bp->b_bcount);
		}
		(void) bdev_strategy(bp);
#ifdef LQFS_TODO_SNAPSHOT
	}
#endif /* LQFS_TODO_SNAPSHOT */

#ifdef LQFS_TODO_STATS
	lwp_stat_update(LWP_STAT_OUBLK, 1);
#endif /* LQFS_TODO_STATS */
}
Example #5
0
void
lqfs_read_strategy(ml_unit_t *ul, buf_t *bp)
{
	mt_map_t	*logmap	= ul->un_logmap;
	offset_t	mof	= ldbtob(bp->b_blkno);
	off_t		nb	= bp->b_bcount;
	mapentry_t	*age;
	char		*va;
	int		(*saviodone)();
	int		entire_range;
	uchar_t		ord;
	qfsvfs_t	*qfsvfsp = ul->un_qfsvfs;

	/*
	 * get a linked list of overlapping deltas
	 * returns with &mtm->mtm_rwlock held
	 */
	ord = lqfs_find_ord(qfsvfsp, bp);
	entire_range = logmap_list_get(logmap, mof, ord, nb, &age);

	/*
	 * no overlapping deltas were found; read master
	 */
	if (age == NULL) {
		rw_exit(&logmap->mtm_rwlock);
		if (ul->un_flags & LDL_ERROR) {
			bp->b_flags |= B_ERROR;
			bp->b_error = EIO;
			biodone(bp);
		} else {
			LQFS_SET_IOTSTAMP(ul->un_qfsvfs, ddi_get_lbolt());
			logstats.ls_lreads.value.ui64++;
			if ((bp->b_flags & B_READ) == 0) {
				LQFS_MSG(CE_WARN, "lqfs_read_strategy(): "
				    "bdev_strategy writing mof 0x%x "
				    "edev %ld nb %d\n", bp->b_blkno * 512,
				    bp->b_edev, bp->b_bcount);
			} else {
				LQFS_MSG(CE_WARN, "lqfs_read_strategy(): "
				    "bdev_strategy reading mof 0x%x "
				    "edev %ld nb %d\n", bp->b_blkno * 512,
				    bp->b_edev, bp->b_bcount);
			}
			(void) bdev_strategy(bp);
#ifdef LQFS_TODO_STATS
			lwp_stat_update(LWP_STAT_INBLK, 1);
#endif /* LQFS_TODO_STATS */
		}
		return;
	}

	va = bp_mapin_common(bp, VM_SLEEP);
	/*
	 * if necessary, sync read the data from master
	 *	errors are returned in bp
	 */
	if (!entire_range) {
		saviodone = bp->b_iodone;
		bp->b_iodone = trans_not_done;
		logstats.ls_mreads.value.ui64++;
		if ((bp->b_flags & B_READ) == 0) {
			LQFS_MSG(CE_WARN, "lqfs_read_strategy(): "
			    "bdev_strategy writing mof 0x%x edev %ld "
			    "nb %d\n", bp->b_blkno * 512, bp->b_edev,
			    bp->b_bcount);
		} else {
			LQFS_MSG(CE_WARN, "lqfs_read_strategy(): "
			    "bdev_strategy reading mof 0x%x edev %ld "
			    "nb %d\n", bp->b_blkno * 512, bp->b_edev,
			    bp->b_bcount);
		}
		(void) bdev_strategy(bp);
#ifdef LQFS_TODO_STATS
		lwp_stat_update(LWP_STAT_INBLK, 1);
#endif /* LQFS_TODO_STATS */
		if (trans_not_wait(bp)) {
			ldl_seterror(ul, "Error reading master");
		}
		bp->b_iodone = saviodone;
	}

	/*
	 * sync read the data from the log
	 *	errors are returned inline
	 */
	if (ldl_read(ul, va, mof, ord, nb, age)) {
		bp->b_flags |= B_ERROR;
		bp->b_error = EIO;
	}

	/*
	 * unlist the deltas
	 */
	logmap_list_put(logmap, age);

	/*
	 * all done
	 */
	if (ul->un_flags & LDL_ERROR) {
		bp->b_flags |= B_ERROR;
		bp->b_error = EIO;
	}
	biodone(bp);
}