13fb517f7SJames Moore /*
23fb517f7SJames Moore  * CDDL HEADER START
33fb517f7SJames Moore  *
43fb517f7SJames Moore  * The contents of this file are subject to the terms of the
53fb517f7SJames Moore  * Common Development and Distribution License (the "License").
63fb517f7SJames Moore  * You may not use this file except in compliance with the License.
73fb517f7SJames Moore  *
83fb517f7SJames Moore  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
93fb517f7SJames Moore  * or http://www.opensolaris.org/os/licensing.
103fb517f7SJames Moore  * See the License for the specific language governing permissions
113fb517f7SJames Moore  * and limitations under the License.
123fb517f7SJames Moore  *
133fb517f7SJames Moore  * When distributing Covered Code, include this CDDL HEADER in each
143fb517f7SJames Moore  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
153fb517f7SJames Moore  * If applicable, add the following below this CDDL HEADER, with the
163fb517f7SJames Moore  * fields enclosed by brackets "[]" replaced with your own identifying
173fb517f7SJames Moore  * information: Portions Copyright [yyyy] [name of copyright owner]
183fb517f7SJames Moore  *
193fb517f7SJames Moore  * CDDL HEADER END
203fb517f7SJames Moore  */
213fb517f7SJames Moore /*
223fb517f7SJames Moore  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
2379315247SMatthew Ahrens  * Copyright (c) 2014, 2018 by Delphix. All rights reserved.
243fb517f7SJames Moore  */
253fb517f7SJames Moore 
263fb517f7SJames Moore #include <sys/conf.h>
273fb517f7SJames Moore #include <sys/file.h>
283fb517f7SJames Moore #include <sys/ddi.h>
293fb517f7SJames Moore #include <sys/sunddi.h>
303fb517f7SJames Moore #include <sys/modctl.h>
313fb517f7SJames Moore #include <sys/scsi/scsi.h>
323fb517f7SJames Moore #include <sys/scsi/impl/scsi_reset_notify.h>
333fb517f7SJames Moore #include <sys/scsi/generic/mode.h>
343fb517f7SJames Moore #include <sys/disp.h>
353fb517f7SJames Moore #include <sys/byteorder.h>
363fb517f7SJames Moore #include <sys/atomic.h>
373fb517f7SJames Moore #include <sys/sdt.h>
383fb517f7SJames Moore #include <sys/dkio.h>
393fb517f7SJames Moore #include <sys/dmu.h>
403fb517f7SJames Moore #include <sys/arc.h>
413fb517f7SJames Moore #include <sys/zvol.h>
423fb517f7SJames Moore #include <sys/zfs_rlock.h>
4379315247SMatthew Ahrens #include <sys/zil.h>
443fb517f7SJames Moore 
454558d122SViswanathan Kannappan #include <sys/stmf.h>
464558d122SViswanathan Kannappan #include <sys/lpif.h>
474558d122SViswanathan Kannappan #include <sys/portif.h>
484558d122SViswanathan Kannappan #include <sys/stmf_ioctl.h>
494558d122SViswanathan Kannappan #include <sys/stmf_sbd_ioctl.h>
504558d122SViswanathan Kannappan 
514558d122SViswanathan Kannappan #include "stmf_sbd.h"
524558d122SViswanathan Kannappan #include "sbd_impl.h"
533fb517f7SJames Moore 
543fb517f7SJames Moore 
553fb517f7SJames Moore /*
563fb517f7SJames Moore  * This file contains direct calls into the zfs module.
573fb517f7SJames Moore  * These functions mimic zvol_read and zvol_write except pointers
583fb517f7SJames Moore  * to the data buffers are passed instead of copying the data itself.
593fb517f7SJames Moore  *
603fb517f7SJames Moore  * zfs internal interfaces referenced here:
613fb517f7SJames Moore  *
623fb517f7SJames Moore  * FUNCTIONS
638dfe5547SRichard Yao  *    dmu_buf_hold_array_by_dnode()
643fb517f7SJames Moore  *    dmu_buf_rele_array()
653fb517f7SJames Moore  *
668dfe5547SRichard Yao  *    arc_loan_buf()
673fb517f7SJames Moore  *    dmu_assign_arcbuf()
688dfe5547SRichard Yao  *    dmu_return_arcbuf()
693fb517f7SJames Moore  *    arc_buf_size()
703fb517f7SJames Moore  *
713fb517f7SJames Moore  *    dmu_tx_create()
723fb517f7SJames Moore  *    dmu_tx_hold_write()
733fb517f7SJames Moore  *    dmu_tx_assign()
743fb517f7SJames Moore  *    dmu_tx_commit(tx)
753fb517f7SJames Moore  *    dmu_tx_abort(tx)
763fb517f7SJames Moore  *    zil_commit()
773fb517f7SJames Moore  *
7879315247SMatthew Ahrens  *    rangelock_enter()
7979315247SMatthew Ahrens  *    rangelock_exit()
803fb517f7SJames Moore  *
813fb517f7SJames Moore  *    zvol_log_write()
823fb517f7SJames Moore  *
833fb517f7SJames Moore  *    dmu_read_uio()
843fb517f7SJames Moore  *    dmu_write_uio()
853fb517f7SJames Moore  * MINOR DATA
863fb517f7SJames Moore  *    zv_volsize
873fb517f7SJames Moore  *    zv_volblocksize
883fb517f7SJames Moore  *    zv_flags		- for WCE
893fb517f7SJames Moore  *    zv_objset		- dmu_tx_create
903fb517f7SJames Moore  *    zv_zilog		- zil_commit
9179315247SMatthew Ahrens  *    zv_znode		- rangelock_enter
928dfe5547SRichard Yao  *    zv_dn		- dmu_buf_hold_array_by_bonus, dmu_request_arcbuf
933fb517f7SJames Moore  * GLOBAL DATA
943fb517f7SJames Moore  *    zvol_maxphys
953fb517f7SJames Moore  */
963fb517f7SJames Moore 
973fb517f7SJames Moore /*
983fb517f7SJames Moore  * Take direct control of the volume instead of using the driver
993fb517f7SJames Moore  * interfaces provided by zvol.c. Gather parameters and handles
1003fb517f7SJames Moore  * needed to make direct calls into zfs/dmu/zvol. The driver is
1013fb517f7SJames Moore  * opened exclusively at this point, so these parameters cannot change.
1023fb517f7SJames Moore  *
1033fb517f7SJames Moore  * NOTE: the object size and WCE can change while the device
1043fb517f7SJames Moore  * is open, so they must be fetched for every operation.
1053fb517f7SJames Moore  */
1063fb517f7SJames Moore int
sbd_zvol_get_volume_params(sbd_lu_t * sl)1073fb517f7SJames Moore sbd_zvol_get_volume_params(sbd_lu_t *sl)
1083fb517f7SJames Moore {
1093fb517f7SJames Moore 	int ret;
1103fb517f7SJames Moore 
1113fb517f7SJames Moore 	ret = zvol_get_volume_params(sl->sl_zvol_minor,
1123fb517f7SJames Moore 	    &sl->sl_blksize,		/* volume block size */
1133fb517f7SJames Moore 	    &sl->sl_max_xfer_len,	/* max data chunk size */
1143fb517f7SJames Moore 	    &sl->sl_zvol_minor_hdl,	/* minor soft state */
1153fb517f7SJames Moore 	    &sl->sl_zvol_objset_hdl,	/* dmu_tx_create */
1163fb517f7SJames Moore 	    &sl->sl_zvol_zil_hdl,	/* zil_commit */
11779315247SMatthew Ahrens 	    &sl->sl_zvol_rl_hdl,	/* locked_range_t */
1188dfe5547SRichard Yao 	    &sl->sl_zvol_dn_hdl);	/* dmu_buf_hold_array_by_dnode, */
1193fb517f7SJames Moore 					/* dmu_request_arcbuf, */
1203fb517f7SJames Moore 					/* dmu_assign_arcbuf */
1213fb517f7SJames Moore 
1223fb517f7SJames Moore 	if (ret == 0 && sl->sl_blksize < MMU_PAGESIZE) {
1233fb517f7SJames Moore 		cmn_err(CE_NOTE, "COMSTAR reduced copy disabled due to "
1243fb517f7SJames Moore 		    "small zvol blocksize (%d)\n", (int)sl->sl_blksize);
1253fb517f7SJames Moore 		ret = ENOTSUP;
1263fb517f7SJames Moore 	}
1273fb517f7SJames Moore 
1283fb517f7SJames Moore 	return (ret);
1293fb517f7SJames Moore }
1303fb517f7SJames Moore 
1313fb517f7SJames Moore /*
1323fb517f7SJames Moore  * Return the number of elements in a scatter/gather list required for
1333fb517f7SJames Moore  * the given span in the zvol. Elements are 1:1 with zvol blocks.
1343fb517f7SJames Moore  */
1353fb517f7SJames Moore uint32_t
sbd_zvol_numsegs(sbd_lu_t * sl,uint64_t off,uint32_t len)1363fb517f7SJames Moore sbd_zvol_numsegs(sbd_lu_t *sl, uint64_t off, uint32_t len)
1373fb517f7SJames Moore {
1383fb517f7SJames Moore 	uint64_t blksz = sl->sl_blksize;
1393fb517f7SJames Moore 	uint64_t endoff = off + len;
1403fb517f7SJames Moore 	uint64_t numsegs;
1413fb517f7SJames Moore 
1423fb517f7SJames Moore 	numsegs = (P2ROUNDUP(endoff, blksz) - P2ALIGN(off, blksz)) / blksz;
1433fb517f7SJames Moore 	return ((uint32_t)numsegs);
1443fb517f7SJames Moore }
1453fb517f7SJames Moore 
1463fb517f7SJames Moore /*
1473fb517f7SJames Moore  * Return an array of dmu_buf_t pointers for the requested range.
1483fb517f7SJames Moore  * The dmu buffers are either in cache or read in synchronously.
1493fb517f7SJames Moore  * Fill in the dbuf sglist from the dmu_buf_t array.
1503fb517f7SJames Moore  */
1513fb517f7SJames Moore static void *RDTAG = "sbd_zvol_read";
1523fb517f7SJames Moore 
1533fb517f7SJames Moore int
sbd_zvol_alloc_read_bufs(sbd_lu_t * sl,stmf_data_buf_t * dbuf)1543fb517f7SJames Moore sbd_zvol_alloc_read_bufs(sbd_lu_t *sl, stmf_data_buf_t *dbuf)
1553fb517f7SJames Moore {
1563fb517f7SJames Moore 	sbd_zvol_io_t	*zvio = dbuf->db_lu_private;
15779315247SMatthew Ahrens 	locked_range_t	*lr;
1583fb517f7SJames Moore 	int		numbufs, error;
1593fb517f7SJames Moore 	uint64_t	len = dbuf->db_data_size;
1603fb517f7SJames Moore 	uint64_t	offset = zvio->zvio_offset;
1613fb517f7SJames Moore 	dmu_buf_t	**dbpp, *dbp;
1623fb517f7SJames Moore 
1633fb517f7SJames Moore 	/* Make sure request is reasonable */
1643fb517f7SJames Moore 	if (len > sl->sl_max_xfer_len)
1653fb517f7SJames Moore 		return (E2BIG);
1663fb517f7SJames Moore 	if (offset + len  > zvol_get_volume_size(sl->sl_zvol_minor_hdl))
1673fb517f7SJames Moore 		return (EIO);
1683fb517f7SJames Moore 
1693fb517f7SJames Moore 	/*
1703fb517f7SJames Moore 	 * The range lock is only held until the dmu buffers read in and
1713fb517f7SJames Moore 	 * held; not during the callers use of the data.
1723fb517f7SJames Moore 	 */
17379315247SMatthew Ahrens 	lr = rangelock_enter(sl->sl_zvol_rl_hdl, offset, len, RL_READER);
1743fb517f7SJames Moore 
1758dfe5547SRichard Yao 	error = dmu_buf_hold_array_by_dnode(sl->sl_zvol_dn_hdl,
1768dfe5547SRichard Yao 	    offset, len, TRUE, RDTAG, &numbufs, &dbpp,
1778dfe5547SRichard Yao 	    DMU_READ_PREFETCH);
1783fb517f7SJames Moore 
17979315247SMatthew Ahrens 	rangelock_exit(lr);
1803fb517f7SJames Moore 
1813fb517f7SJames Moore 	if (error == ECKSUM)
1823fb517f7SJames Moore 		error = EIO;
1833fb517f7SJames Moore 
1843fb517f7SJames Moore 	if (error == 0) {
1853fb517f7SJames Moore 		/*
1863fb517f7SJames Moore 		 * Fill in db_sglist from the dmu_buf_t array.
1873fb517f7SJames Moore 		 */
1883fb517f7SJames Moore 		int		i;
1893fb517f7SJames Moore 		stmf_sglist_ent_t *sgl;
1903fb517f7SJames Moore 		uint64_t	odiff, seglen;
1913fb517f7SJames Moore 
1923fb517f7SJames Moore 		zvio->zvio_dbp = dbpp;
1933fb517f7SJames Moore 		/* make sure db_sglist is large enough */
1943fb517f7SJames Moore 		if (dbuf->db_sglist_length != numbufs) {
1953fb517f7SJames Moore 			cmn_err(CE_PANIC, "wrong size sglist: dbuf %d != %d\n",
1963fb517f7SJames Moore 			    dbuf->db_sglist_length, numbufs);
1973fb517f7SJames Moore 		}
1983fb517f7SJames Moore 
1993fb517f7SJames Moore 		sgl = &dbuf->db_sglist[0];
2003fb517f7SJames Moore 		for (i = 0; i < numbufs; i++) {
2013fb517f7SJames Moore 			dbp = dbpp[i];
2023fb517f7SJames Moore 			odiff =  offset - dbp->db_offset;
2033fb517f7SJames Moore 			ASSERT(odiff == 0 || i == 0);
2043fb517f7SJames Moore 			sgl->seg_addr = (uint8_t *)dbp->db_data + odiff;
2053fb517f7SJames Moore 			seglen = MIN(len, dbp->db_size - odiff);
2063fb517f7SJames Moore 			sgl->seg_length = (uint32_t)seglen;
2073fb517f7SJames Moore 			offset += seglen;
2083fb517f7SJames Moore 			len -= seglen;
2093fb517f7SJames Moore 			sgl++;
2103fb517f7SJames Moore 		}
2113fb517f7SJames Moore 		ASSERT(len == 0);
2123fb517f7SJames Moore 
2133fb517f7SJames Moore 	}
2143fb517f7SJames Moore 	return (error);
2153fb517f7SJames Moore }
2163fb517f7SJames Moore 
2173fb517f7SJames Moore /*
2183fb517f7SJames Moore  * Release a dmu_buf_t array.
2193fb517f7SJames Moore  */
2203fb517f7SJames Moore /*ARGSUSED*/
2213fb517f7SJames Moore void
sbd_zvol_rele_read_bufs(sbd_lu_t * sl,stmf_data_buf_t * dbuf)2223fb517f7SJames Moore sbd_zvol_rele_read_bufs(sbd_lu_t *sl, stmf_data_buf_t *dbuf)
2233fb517f7SJames Moore {
2243fb517f7SJames Moore 	sbd_zvol_io_t *zvio = dbuf->db_lu_private;
2253fb517f7SJames Moore 
2263fb517f7SJames Moore 	ASSERT(zvio->zvio_dbp);
2273fb517f7SJames Moore 	ASSERT(dbuf->db_sglist_length);
2283fb517f7SJames Moore 
2293fb517f7SJames Moore 	dmu_buf_rele_array(zvio->zvio_dbp, (int)dbuf->db_sglist_length, RDTAG);
2303fb517f7SJames Moore }
2313fb517f7SJames Moore 
2323fb517f7SJames Moore /*
2333fb517f7SJames Moore  * Allocate enough loaned arc buffers for the requested region.
2343fb517f7SJames Moore  * Mimic the handling of the dmu_buf_t array used for reads as closely
2353fb517f7SJames Moore  * as possible even though the arc_buf_t's are anonymous until released.
2363fb517f7SJames Moore  * The buffers will match the zvol object blocks sizes and alignments
2373fb517f7SJames Moore  * such that a data copy may be avoided when the buffers are assigned.
2383fb517f7SJames Moore  */
2393fb517f7SJames Moore int
sbd_zvol_alloc_write_bufs(sbd_lu_t * sl,stmf_data_buf_t * dbuf)2403fb517f7SJames Moore sbd_zvol_alloc_write_bufs(sbd_lu_t *sl, stmf_data_buf_t *dbuf)
2413fb517f7SJames Moore {
2423fb517f7SJames Moore 	sbd_zvol_io_t	*zvio = dbuf->db_lu_private;
2433fb517f7SJames Moore 	int		blkshift, numbufs, i;
2443fb517f7SJames Moore 	uint64_t	blksize;
2453fb517f7SJames Moore 	arc_buf_t	**abp;
2463fb517f7SJames Moore 	stmf_sglist_ent_t *sgl;
2473fb517f7SJames Moore 	uint64_t	len = dbuf->db_data_size;
2483fb517f7SJames Moore 	uint64_t	offset = zvio->zvio_offset;
2493fb517f7SJames Moore 
2503fb517f7SJames Moore 	/* Make sure request is reasonable */
2513fb517f7SJames Moore 	if (len > sl->sl_max_xfer_len)
2523fb517f7SJames Moore 		return (E2BIG);
2533fb517f7SJames Moore 	if (offset + len  > zvol_get_volume_size(sl->sl_zvol_minor_hdl))
2543fb517f7SJames Moore 		return (EIO);
2553fb517f7SJames Moore 
2563fb517f7SJames Moore 	/*
2573fb517f7SJames Moore 	 * Break up the request into chunks to match
2583fb517f7SJames Moore 	 * the volume block size. Only full, and aligned
2593fb517f7SJames Moore 	 * buffers will avoid the data copy in the dmu.
2603fb517f7SJames Moore 	 */
2613fb517f7SJames Moore 	/*
2623fb517f7SJames Moore 	 * calculate how may dbufs are needed
2633fb517f7SJames Moore 	 */
2643fb517f7SJames Moore 	blksize = sl->sl_blksize;
2653fb517f7SJames Moore 	ASSERT(ISP2(blksize));
2663fb517f7SJames Moore 	blkshift = highbit(blksize - 1);
2673fb517f7SJames Moore 	/*
2683fb517f7SJames Moore 	 * taken from dmu_buf_hold_array_by_dnode()
2693fb517f7SJames Moore 	 */
2703fb517f7SJames Moore 	numbufs = (P2ROUNDUP(offset+len, 1ULL<<blkshift) -
2713fb517f7SJames Moore 	    P2ALIGN(offset, 1ULL<<blkshift)) >> blkshift;
2723fb517f7SJames Moore 	if (dbuf->db_sglist_length != numbufs) {
2733fb517f7SJames Moore 		cmn_err(CE_PANIC, "wrong size sglist: dbuf %d != %d\n",
2743fb517f7SJames Moore 		    dbuf->db_sglist_length, numbufs);
2753fb517f7SJames Moore 	}
2763fb517f7SJames Moore 	/*
2773fb517f7SJames Moore 	 * allocate a holder for the needed arc_buf pointers
2783fb517f7SJames Moore 	 */
2793fb517f7SJames Moore 	abp = kmem_alloc(sizeof (arc_buf_t *) * numbufs, KM_SLEEP);
2803fb517f7SJames Moore 	/*
2813fb517f7SJames Moore 	 * The write operation uses loaned arc buffers so that
2823fb517f7SJames Moore 	 * the xfer_data is done outside of a dmu transaction.
2833fb517f7SJames Moore 	 * These buffers will exactly match the request unlike
2843fb517f7SJames Moore 	 * the dmu buffers obtained from the read operation.
2853fb517f7SJames Moore 	 */
2863fb517f7SJames Moore 	/*
2873fb517f7SJames Moore 	 * allocate the arc buffers and fill in the stmf sglist
2883fb517f7SJames Moore 	 */
2893fb517f7SJames Moore 	sgl = &dbuf->db_sglist[0];
2903fb517f7SJames Moore 	for (i = 0; i < numbufs; i++) {
2913fb517f7SJames Moore 		uint64_t seglen;
2923fb517f7SJames Moore 
2933fb517f7SJames Moore 		/* first block may not be aligned */
2943fb517f7SJames Moore 		seglen = P2NPHASE(offset, blksize);
2953fb517f7SJames Moore 		if (seglen == 0)
2963fb517f7SJames Moore 			seglen = blksize;
2973fb517f7SJames Moore 		seglen = MIN(seglen, len);
2988dfe5547SRichard Yao 		abp[i] = arc_loan_buf(dmu_objset_spa(sl->sl_zvol_objset_hdl),
2998dfe5547SRichard Yao 		    B_FALSE, (int)seglen);
3003fb517f7SJames Moore 		ASSERT(arc_buf_size(abp[i]) == (int)seglen);
3013fb517f7SJames Moore 		sgl->seg_addr = abp[i]->b_data;
3023fb517f7SJames Moore 		sgl->seg_length = (uint32_t)seglen;
3033fb517f7SJames Moore 		sgl++;
3043fb517f7SJames Moore 		offset += seglen;
3053fb517f7SJames Moore 		len -= seglen;
3063fb517f7SJames Moore 	}
3073fb517f7SJames Moore 	ASSERT(len == 0);
3083fb517f7SJames Moore 
3093fb517f7SJames Moore 	zvio->zvio_abp = abp;
3103fb517f7SJames Moore 	return (0);
3113fb517f7SJames Moore }
3123fb517f7SJames Moore 
3133fb517f7SJames Moore /*ARGSUSED*/
3143fb517f7SJames Moore void
sbd_zvol_rele_write_bufs_abort(sbd_lu_t * sl,stmf_data_buf_t * dbuf)3153fb517f7SJames Moore sbd_zvol_rele_write_bufs_abort(sbd_lu_t *sl, stmf_data_buf_t *dbuf)
3163fb517f7SJames Moore {
3173fb517f7SJames Moore 	sbd_zvol_io_t *zvio = dbuf->db_lu_private;
3183fb517f7SJames Moore 	int i;
3193fb517f7SJames Moore 	arc_buf_t **abp = zvio->zvio_abp;
3203fb517f7SJames Moore 
3213fb517f7SJames Moore 	/* free arcbufs */
3223fb517f7SJames Moore 	for (i = 0; i < dbuf->db_sglist_length; i++)
3233fb517f7SJames Moore 		dmu_return_arcbuf(*abp++);
3243fb517f7SJames Moore 	kmem_free(zvio->zvio_abp,
3253fb517f7SJames Moore 	    sizeof (arc_buf_t *) * dbuf->db_sglist_length);
3263fb517f7SJames Moore 	zvio->zvio_abp = NULL;
3273fb517f7SJames Moore }
3283fb517f7SJames Moore 
3293fb517f7SJames Moore /*
3303fb517f7SJames Moore  * Release the arc_buf_t array allocated above and handle these cases :
3313fb517f7SJames Moore  *
3323fb517f7SJames Moore  * flags == 0 - create transaction and assign all arc bufs to offsets
3333fb517f7SJames Moore  * flags == ZVIO_COMMIT - same as above and commit to zil on sync devices
3343fb517f7SJames Moore  */
3353fb517f7SJames Moore int
sbd_zvol_rele_write_bufs(sbd_lu_t * sl,stmf_data_buf_t * dbuf)3363fb517f7SJames Moore sbd_zvol_rele_write_bufs(sbd_lu_t *sl, stmf_data_buf_t *dbuf)
3373fb517f7SJames Moore {
3383fb517f7SJames Moore 	sbd_zvol_io_t	*zvio = dbuf->db_lu_private;
3393fb517f7SJames Moore 	dmu_tx_t	*tx;
3403fb517f7SJames Moore 	int		sync, i, error;
34179315247SMatthew Ahrens 	locked_range_t	*lr;
3423fb517f7SJames Moore 	arc_buf_t	**abp = zvio->zvio_abp;
3433fb517f7SJames Moore 	int		flags = zvio->zvio_flags;
3443fb517f7SJames Moore 	uint64_t	toffset, offset = zvio->zvio_offset;
3453fb517f7SJames Moore 	uint64_t	resid, len = dbuf->db_data_size;
3463fb517f7SJames Moore 
3473fb517f7SJames Moore 	ASSERT(flags == 0 || flags == ZVIO_COMMIT || flags == ZVIO_ABORT);
3483fb517f7SJames Moore 
34979315247SMatthew Ahrens 	lr = rangelock_enter(sl->sl_zvol_rl_hdl, offset, len, RL_WRITER);
3503fb517f7SJames Moore 
3513fb517f7SJames Moore 	tx = dmu_tx_create(sl->sl_zvol_objset_hdl);
3523fb517f7SJames Moore 	dmu_tx_hold_write(tx, ZVOL_OBJ, offset, (int)len);
3533fb517f7SJames Moore 	error = dmu_tx_assign(tx, TXG_WAIT);
3543fb517f7SJames Moore 
3553fb517f7SJames Moore 	if (error) {
3563fb517f7SJames Moore 		dmu_tx_abort(tx);
35779315247SMatthew Ahrens 		rangelock_exit(lr);
3583fb517f7SJames Moore 		sbd_zvol_rele_write_bufs_abort(sl, dbuf);
3593fb517f7SJames Moore 		return (error);
3603fb517f7SJames Moore 	}
3613fb517f7SJames Moore 
3623fb517f7SJames Moore 	toffset = offset;
3633fb517f7SJames Moore 	resid = len;
3643fb517f7SJames Moore 	for (i = 0; i < dbuf->db_sglist_length; i++) {
3653fb517f7SJames Moore 		arc_buf_t *abuf;
3663fb517f7SJames Moore 		int size;
3673fb517f7SJames Moore 
3683fb517f7SJames Moore 		abuf = abp[i];
3693fb517f7SJames Moore 		size = arc_buf_size(abuf);
370*eb633035STom Caputi 		(void) dmu_assign_arcbuf_by_dnode(sl->sl_zvol_dn_hdl,
371*eb633035STom Caputi 		    toffset, abuf, tx);
3723fb517f7SJames Moore 		toffset += size;
3733fb517f7SJames Moore 		resid -= size;
3743fb517f7SJames Moore 	}
3753fb517f7SJames Moore 	ASSERT(resid == 0);
3763fb517f7SJames Moore 
3773fb517f7SJames Moore 	sync = !zvol_get_volume_wce(sl->sl_zvol_minor_hdl);
3783fb517f7SJames Moore 	zvol_log_write_minor(sl->sl_zvol_minor_hdl, tx, offset,
3793fb517f7SJames Moore 	    (ssize_t)len, sync);
3803fb517f7SJames Moore 	dmu_tx_commit(tx);
38179315247SMatthew Ahrens 	rangelock_exit(lr);
3823fb517f7SJames Moore 	kmem_free(zvio->zvio_abp,
3833fb517f7SJames Moore 	    sizeof (arc_buf_t *) * dbuf->db_sglist_length);
3843fb517f7SJames Moore 	zvio->zvio_abp = NULL;
3853fb517f7SJames Moore 	if (sync && (flags & ZVIO_COMMIT))
3865002558fSNeil Perrin 		zil_commit(sl->sl_zvol_zil_hdl, ZVOL_OBJ);
3873fb517f7SJames Moore 	return (0);
3883fb517f7SJames Moore }
3893fb517f7SJames Moore 
3903fb517f7SJames Moore /*
3913fb517f7SJames Moore  * Copy interface for callers using direct zvol access.
3923fb517f7SJames Moore  * Very similar to zvol_read but the uio may have multiple iovec entries.
3933fb517f7SJames Moore  */
3943fb517f7SJames Moore int
sbd_zvol_copy_read(sbd_lu_t * sl,uio_t * uio)3953fb517f7SJames Moore sbd_zvol_copy_read(sbd_lu_t *sl, uio_t *uio)
3963fb517f7SJames Moore {
3973fb517f7SJames Moore 	uint64_t	len = (uint64_t)uio->uio_resid;
3983fb517f7SJames Moore 	uint64_t	offset = (uint64_t)uio->uio_loffset;
3993fb517f7SJames Moore 
4003fb517f7SJames Moore 	/* Make sure request is reasonable */
4013fb517f7SJames Moore 	if (len > sl->sl_max_xfer_len)
4023fb517f7SJames Moore 		return (E2BIG);
4033fb517f7SJames Moore 	if (offset + len  > zvol_get_volume_size(sl->sl_zvol_minor_hdl))
4043fb517f7SJames Moore 		return (EIO);
4053fb517f7SJames Moore 
40679315247SMatthew Ahrens 	locked_range_t *lr = rangelock_enter(sl->sl_zvol_rl_hdl, offset, len,
40779315247SMatthew Ahrens 	    RL_READER);
40879315247SMatthew Ahrens 	int error = dmu_read_uio_dnode(sl->sl_zvol_dn_hdl, uio, len);
40979315247SMatthew Ahrens 	rangelock_exit(lr);
4103fb517f7SJames Moore 
4113fb517f7SJames Moore 	if (error == ECKSUM)
4123fb517f7SJames Moore 		error = EIO;
4133fb517f7SJames Moore 	return (error);
4143fb517f7SJames Moore }
4153fb517f7SJames Moore 
4163fb517f7SJames Moore /*
4173fb517f7SJames Moore  * Copy interface for callers using direct zvol access.
4183fb517f7SJames Moore  * Very similar to zvol_write but the uio may have multiple iovec entries.
4193fb517f7SJames Moore  */
4203fb517f7SJames Moore int
sbd_zvol_copy_write(sbd_lu_t * sl,uio_t * uio,int flags)4213fb517f7SJames Moore sbd_zvol_copy_write(sbd_lu_t *sl, uio_t *uio, int flags)
4223fb517f7SJames Moore {
4233fb517f7SJames Moore 	dmu_tx_t	*tx;
4243fb517f7SJames Moore 	int		error, sync;
4253fb517f7SJames Moore 	uint64_t	len = (uint64_t)uio->uio_resid;
4263fb517f7SJames Moore 	uint64_t	offset = (uint64_t)uio->uio_loffset;
4273fb517f7SJames Moore 
4283fb517f7SJames Moore 	ASSERT(flags == 0 || flags == ZVIO_COMMIT);
4293fb517f7SJames Moore 
4303fb517f7SJames Moore 	/* Make sure request is reasonable */
4313fb517f7SJames Moore 	if (len > sl->sl_max_xfer_len)
4323fb517f7SJames Moore 		return (E2BIG);
4333fb517f7SJames Moore 	if (offset + len  > zvol_get_volume_size(sl->sl_zvol_minor_hdl))
4343fb517f7SJames Moore 		return (EIO);
4353fb517f7SJames Moore 
43679315247SMatthew Ahrens 	locked_range_t *lr = rangelock_enter(sl->sl_zvol_rl_hdl, offset, len,
43779315247SMatthew Ahrens 	    RL_WRITER);
4383fb517f7SJames Moore 	sync = !zvol_get_volume_wce(sl->sl_zvol_minor_hdl);
4393fb517f7SJames Moore 
4403fb517f7SJames Moore 	tx = dmu_tx_create(sl->sl_zvol_objset_hdl);
4413fb517f7SJames Moore 	dmu_tx_hold_write(tx, ZVOL_OBJ, offset, (int)uio->uio_resid);
4423fb517f7SJames Moore 	error = dmu_tx_assign(tx, TXG_WAIT);
4433fb517f7SJames Moore 	if (error) {
4443fb517f7SJames Moore 		dmu_tx_abort(tx);
4453fb517f7SJames Moore 	} else {
4468dfe5547SRichard Yao 		error = dmu_write_uio_dnode(sl->sl_zvol_dn_hdl, uio, len, tx);
4473fb517f7SJames Moore 		if (error == 0) {
4483fb517f7SJames Moore 			zvol_log_write_minor(sl->sl_zvol_minor_hdl, tx, offset,
4493fb517f7SJames Moore 			    (ssize_t)len, sync);
4503fb517f7SJames Moore 		}
4513fb517f7SJames Moore 		dmu_tx_commit(tx);
4523fb517f7SJames Moore 	}
45379315247SMatthew Ahrens 	rangelock_exit(lr);
45479315247SMatthew Ahrens 
4553fb517f7SJames Moore 	if (sync && (flags & ZVIO_COMMIT))
4565002558fSNeil Perrin 		zil_commit(sl->sl_zvol_zil_hdl, ZVOL_OBJ);
4573fb517f7SJames Moore 	if (error == ECKSUM)
4583fb517f7SJames Moore 		error = EIO;
4593fb517f7SJames Moore 	return (error);
4603fb517f7SJames Moore }
461