1fcf3ce44SJohn Forte /*
2fcf3ce44SJohn Forte  * CDDL HEADER START
3fcf3ce44SJohn Forte  *
4fcf3ce44SJohn Forte  * The contents of this file are subject to the terms of the
5fcf3ce44SJohn Forte  * Common Development and Distribution License (the "License").
6fcf3ce44SJohn Forte  * You may not use this file except in compliance with the License.
7fcf3ce44SJohn Forte  *
8fcf3ce44SJohn Forte  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9fcf3ce44SJohn Forte  * or http://www.opensolaris.org/os/licensing.
10fcf3ce44SJohn Forte  * See the License for the specific language governing permissions
11fcf3ce44SJohn Forte  * and limitations under the License.
12fcf3ce44SJohn Forte  *
13fcf3ce44SJohn Forte  * When distributing Covered Code, include this CDDL HEADER in each
14fcf3ce44SJohn Forte  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15fcf3ce44SJohn Forte  * If applicable, add the following below this CDDL HEADER, with the
16fcf3ce44SJohn Forte  * fields enclosed by brackets "[]" replaced with your own identifying
17fcf3ce44SJohn Forte  * information: Portions Copyright [yyyy] [name of copyright owner]
18fcf3ce44SJohn Forte  *
19fcf3ce44SJohn Forte  * CDDL HEADER END
20fcf3ce44SJohn Forte  */
2161dfa509SRick McNeal 
22fcf3ce44SJohn Forte /*
23f4e76ddfSNattuvetty Bhavyan  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
24047c81d3SSaso Kiselkov  * Copyright 2017 Nexenta Systems, Inc.  All rights reserved.
2509409df0SJeff Biseda  * Copyright (c) 2013 by Delphix. All rights reserved.
26ec38b3f5SJerry Jelinek  * Copyright 2019 Joyent, Inc.
27fcf3ce44SJohn Forte  */
28fcf3ce44SJohn Forte 
29fcf3ce44SJohn Forte #include <sys/conf.h>
30fcf3ce44SJohn Forte #include <sys/file.h>
31fcf3ce44SJohn Forte #include <sys/ddi.h>
32fcf3ce44SJohn Forte #include <sys/sunddi.h>
33fcf3ce44SJohn Forte #include <sys/modctl.h>
34fcf3ce44SJohn Forte #include <sys/scsi/scsi.h>
35fcf3ce44SJohn Forte #include <sys/scsi/impl/scsi_reset_notify.h>
36fcf3ce44SJohn Forte #include <sys/scsi/generic/mode.h>
37fcf3ce44SJohn Forte #include <sys/disp.h>
38fcf3ce44SJohn Forte #include <sys/byteorder.h>
39fcf3ce44SJohn Forte #include <sys/atomic.h>
40e17f3b22Stim szeto #include <sys/sdt.h>
418fe96085Stim szeto #include <sys/dkio.h>
42047c81d3SSaso Kiselkov #include <sys/dkioc_free_util.h>
43fcf3ce44SJohn Forte 
444558d122SViswanathan Kannappan #include <sys/stmf.h>
454558d122SViswanathan Kannappan #include <sys/lpif.h>
464558d122SViswanathan Kannappan #include <sys/portif.h>
474558d122SViswanathan Kannappan #include <sys/stmf_ioctl.h>
484558d122SViswanathan Kannappan #include <sys/stmf_sbd_ioctl.h>
494558d122SViswanathan Kannappan 
504558d122SViswanathan Kannappan #include "stmf_sbd.h"
514558d122SViswanathan Kannappan #include "sbd_impl.h"
52fcf3ce44SJohn Forte 
538fe96085Stim szeto #define	SCSI2_CONFLICT_FREE_CMDS(cdb)	( \
548fe96085Stim szeto 	/* ----------------------- */                                      \
558fe96085Stim szeto 	/* Refer Both		   */                                      \
568fe96085Stim szeto 	/* SPC-2 (rev 20) Table 10 */                                      \
578fe96085Stim szeto 	/* SPC-3 (rev 23) Table 31 */                                      \
588fe96085Stim szeto 	/* ----------------------- */                                      \
598fe96085Stim szeto 	((cdb[0]) == SCMD_INQUIRY)					|| \
608fe96085Stim szeto 	((cdb[0]) == SCMD_LOG_SENSE_G1)					|| \
618fe96085Stim szeto 	((cdb[0]) == SCMD_RELEASE)					|| \
628fe96085Stim szeto 	((cdb[0]) == SCMD_RELEASE_G1)					|| \
638fe96085Stim szeto 	((cdb[0]) == SCMD_REPORT_LUNS)					|| \
648fe96085Stim szeto 	((cdb[0]) == SCMD_REQUEST_SENSE)				|| \
658fe96085Stim szeto 	/* PREVENT ALLOW MEDIUM REMOVAL with prevent == 0 */               \
668fe96085Stim szeto 	((((cdb[0]) == SCMD_DOORLOCK) && (((cdb[4]) & 0x3) == 0)))	|| \
678fe96085Stim szeto 	/* SERVICE ACTION IN with READ MEDIA SERIAL NUMBER (0x01) */       \
688fe96085Stim szeto 	(((cdb[0]) == SCMD_SVC_ACTION_IN_G5) && (                          \
698fe96085Stim szeto 	    ((cdb[1]) & 0x1F) == 0x01))					|| \
708fe96085Stim szeto 	/* MAINTENANCE IN with service actions REPORT ALIASES (0x0Bh) */   \
718fe96085Stim szeto 	/* REPORT DEVICE IDENTIFIER (0x05)  REPORT PRIORITY (0x0Eh) */     \
728fe96085Stim szeto 	/* REPORT TARGET PORT GROUPS (0x0A) REPORT TIMESTAMP (0x0F) */     \
738fe96085Stim szeto 	(((cdb[0]) == SCMD_MAINTENANCE_IN) && (                            \
748fe96085Stim szeto 	    (((cdb[1]) & 0x1F) == 0x0B) ||                                 \
758fe96085Stim szeto 	    (((cdb[1]) & 0x1F) == 0x05) ||                                 \
768fe96085Stim szeto 	    (((cdb[1]) & 0x1F) == 0x0E) ||                                 \
778fe96085Stim szeto 	    (((cdb[1]) & 0x1F) == 0x0A) ||                                 \
788fe96085Stim szeto 	    (((cdb[1]) & 0x1F) == 0x0F)))				|| \
798fe96085Stim szeto 	/* ----------------------- */                                      \
808fe96085Stim szeto 	/* SBC-3 (rev 17) Table 3  */                                      \
818fe96085Stim szeto 	/* ----------------------- */                                      \
828fe96085Stim szeto 	/* READ CAPACITY(10) */                                            \
838fe96085Stim szeto 	((cdb[0]) == SCMD_READ_CAPACITY)				|| \
848fe96085Stim szeto 	/* READ CAPACITY(16) */                                            \
858fe96085Stim szeto 	(((cdb[0]) == SCMD_SVC_ACTION_IN_G4) && (                          \
868fe96085Stim szeto 	    ((cdb[1]) & 0x1F) == 0x10))					|| \
878fe96085Stim szeto 	/* START STOP UNIT with START bit 0 and POWER CONDITION 0  */      \
888fe96085Stim szeto 	(((cdb[0]) == SCMD_START_STOP) && (                                \
898fe96085Stim szeto 	    (((cdb[4]) & 0xF0) == 0) && (((cdb[4]) & 0x01) == 0))))
908fe96085Stim szeto /* End of SCSI2_CONFLICT_FREE_CMDS */
918fe96085Stim szeto 
9261dfa509SRick McNeal uint8_t HardwareAcceleratedInit = 1;
9361dfa509SRick McNeal uint8_t sbd_unmap_enable = 1;		/* allow unmap by default */
9461dfa509SRick McNeal 
9561dfa509SRick McNeal /*
9661dfa509SRick McNeal  * An /etc/system tunable which specifies the maximum number of LBAs supported
9761dfa509SRick McNeal  * in a single UNMAP operation. Default is 0x002000 blocks or 4MB in size.
9861dfa509SRick McNeal  */
9961dfa509SRick McNeal int stmf_sbd_unmap_max_nblks  = 0x002000;
10061dfa509SRick McNeal 
10161dfa509SRick McNeal /*
10261dfa509SRick McNeal  * An /etc/system tunable which indicates if READ ops can run on the standby
10361dfa509SRick McNeal  * path or return an error.
10461dfa509SRick McNeal  */
10561dfa509SRick McNeal int stmf_standby_fail_reads = 0;
10661dfa509SRick McNeal 
107fcf3ce44SJohn Forte stmf_status_t sbd_lu_reset_state(stmf_lu_t *lu);
108fcf3ce44SJohn Forte static void sbd_handle_sync_cache(struct scsi_task *task,
109fcf3ce44SJohn Forte     struct stmf_data_buf *initial_dbuf);
110fcf3ce44SJohn Forte void sbd_handle_read_xfer_completion(struct scsi_task *task,
111fcf3ce44SJohn Forte     sbd_cmd_t *scmd, struct stmf_data_buf *dbuf);
1128fe96085Stim szeto void sbd_handle_short_write_xfer_completion(scsi_task_t *task,
1138fe96085Stim szeto     stmf_data_buf_t *dbuf);
1148fe96085Stim szeto void sbd_handle_mode_select_xfer(scsi_task_t *task, uint8_t *buf,
1158fe96085Stim szeto     uint32_t buflen);
1168fe96085Stim szeto void sbd_handle_mode_select(scsi_task_t *task, stmf_data_buf_t *dbuf);
1176e4cf8b7SJohn Forte void sbd_handle_identifying_info(scsi_task_t *task, stmf_data_buf_t *dbuf);
1188fe96085Stim szeto 
119b77b9231SDan McDonald static void sbd_handle_unmap_xfer(scsi_task_t *task, uint8_t *buf,
120b77b9231SDan McDonald     uint32_t buflen);
121b77b9231SDan McDonald static void sbd_handle_unmap(scsi_task_t *task, stmf_data_buf_t *dbuf);
122b77b9231SDan McDonald 
123f4e76ddfSNattuvetty Bhavyan extern void sbd_pgr_initialize_it(scsi_task_t *, sbd_it_data_t *);
12461dfa509SRick McNeal extern int sbd_pgr_reservation_conflict(scsi_task_t *, struct sbd_lu *sl);
12545039663SJohn Forte extern void sbd_pgr_reset(sbd_lu_t *);
1268fe96085Stim szeto extern void sbd_pgr_remove_it_handle(sbd_lu_t *, sbd_it_data_t *);
1278fe96085Stim szeto extern void sbd_handle_pgr_in_cmd(scsi_task_t *, stmf_data_buf_t *);
1288fe96085Stim szeto extern void sbd_handle_pgr_out_cmd(scsi_task_t *, stmf_data_buf_t *);
1298fe96085Stim szeto extern void sbd_handle_pgr_out_data(scsi_task_t *, stmf_data_buf_t *);
1303fb517f7SJames Moore void sbd_do_sgl_write_xfer(struct scsi_task *task, sbd_cmd_t *scmd,
1313fb517f7SJames Moore     int first_xfer);
1326140ef00SDan McDonald static void sbd_handle_write_same(scsi_task_t *task,
1336140ef00SDan McDonald     struct stmf_data_buf *initial_dbuf);
1346140ef00SDan McDonald static void sbd_do_write_same_xfer(struct scsi_task *task, sbd_cmd_t *scmd,
1356140ef00SDan McDonald     struct stmf_data_buf *dbuf, uint8_t dbuf_reusable);
1366140ef00SDan McDonald static void sbd_handle_write_same_xfer_completion(struct scsi_task *task,
1376140ef00SDan McDonald     sbd_cmd_t *scmd, struct stmf_data_buf *dbuf, uint8_t dbuf_reusable);
138fcf3ce44SJohn Forte /*
139fcf3ce44SJohn Forte  * IMPORTANT NOTE:
140fcf3ce44SJohn Forte  * =================
141fcf3ce44SJohn Forte  * The whole world here is based on the assumption that everything within
142fcf3ce44SJohn Forte  * a scsi task executes in a single threaded manner, even the aborts.
143fcf3ce44SJohn Forte  * Dont ever change that. There wont be any performance gain but there
144fcf3ce44SJohn Forte  * will be tons of race conditions.
145fcf3ce44SJohn Forte  */
146fcf3ce44SJohn Forte 
147fcf3ce44SJohn Forte void
sbd_do_read_xfer(struct scsi_task * task,sbd_cmd_t * scmd,struct stmf_data_buf * dbuf)148fcf3ce44SJohn Forte sbd_do_read_xfer(struct scsi_task *task, sbd_cmd_t *scmd,
149047c81d3SSaso Kiselkov     struct stmf_data_buf *dbuf)
150fcf3ce44SJohn Forte {
1518fe96085Stim szeto 	sbd_lu_t *sl = (sbd_lu_t *)task->task_lu->lu_provider_private;
152fcf3ce44SJohn Forte 	uint64_t laddr;
153fcf3ce44SJohn Forte 	uint32_t len, buflen, iolen;
154fcf3ce44SJohn Forte 	int ndx;
155fcf3ce44SJohn Forte 	int bufs_to_take;
156fcf3ce44SJohn Forte 
157fcf3ce44SJohn Forte 	/* Lets try not to hog all the buffers the port has. */
158fcf3ce44SJohn Forte 	bufs_to_take = ((task->task_max_nbufs > 2) &&
159fcf3ce44SJohn Forte 	    (task->task_cmd_xfer_length < (32 * 1024))) ? 2 :
160fcf3ce44SJohn Forte 	    task->task_max_nbufs;
161fcf3ce44SJohn Forte 
16261dfa509SRick McNeal 	len = ATOMIC32_GET(scmd->len) > dbuf->db_buf_size ?
16361dfa509SRick McNeal 	    dbuf->db_buf_size : ATOMIC32_GET(scmd->len);
1648fe96085Stim szeto 	laddr = scmd->addr + scmd->current_ro;
165fcf3ce44SJohn Forte 
166fcf3ce44SJohn Forte 	for (buflen = 0, ndx = 0; (buflen < len) &&
167fcf3ce44SJohn Forte 	    (ndx < dbuf->db_sglist_length); ndx++) {
168fcf3ce44SJohn Forte 		iolen = min(len - buflen, dbuf->db_sglist[ndx].seg_length);
169fcf3ce44SJohn Forte 		if (iolen == 0)
170fcf3ce44SJohn Forte 			break;
171427fcaf8Stim szeto 		if (sbd_data_read(sl, task, laddr, (uint64_t)iolen,
1728e326937SZhong Wang 		    dbuf->db_sglist[ndx].seg_addr) != STMF_SUCCESS) {
173fcf3ce44SJohn Forte 			scmd->flags |= SBD_SCSI_CMD_XFER_FAIL;
174fcf3ce44SJohn Forte 			/* Do not need to do xfer anymore, just complete it */
175fcf3ce44SJohn Forte 			dbuf->db_data_size = 0;
176fcf3ce44SJohn Forte 			dbuf->db_xfer_status = STMF_SUCCESS;
177fcf3ce44SJohn Forte 			sbd_handle_read_xfer_completion(task, scmd, dbuf);
178fcf3ce44SJohn Forte 			return;
179fcf3ce44SJohn Forte 		}
180fcf3ce44SJohn Forte 		buflen += iolen;
181fcf3ce44SJohn Forte 		laddr += (uint64_t)iolen;
182fcf3ce44SJohn Forte 	}
183fcf3ce44SJohn Forte 	dbuf->db_relative_offset = scmd->current_ro;
184fcf3ce44SJohn Forte 	dbuf->db_data_size = buflen;
185fcf3ce44SJohn Forte 	dbuf->db_flags = DB_DIRECTION_TO_RPORT;
186fcf3ce44SJohn Forte 	(void) stmf_xfer_data(task, dbuf, 0);
18761dfa509SRick McNeal 	atomic_add_32(&scmd->len, -buflen);
188fcf3ce44SJohn Forte 	scmd->current_ro += buflen;
18961dfa509SRick McNeal 	if (ATOMIC32_GET(scmd->len) &&
19061dfa509SRick McNeal 	    (ATOMIC8_GET(scmd->nbufs) < bufs_to_take)) {
191fcf3ce44SJohn Forte 		uint32_t maxsize, minsize, old_minsize;
192fcf3ce44SJohn Forte 
19361dfa509SRick McNeal 		maxsize = (ATOMIC32_GET(scmd->len) > (128*1024)) ? 128 * 1024 :
19461dfa509SRick McNeal 		    ATOMIC32_GET(scmd->len);
195fcf3ce44SJohn Forte 		minsize = maxsize >> 2;
196fcf3ce44SJohn Forte 		do {
197fcf3ce44SJohn Forte 			/*
198fcf3ce44SJohn Forte 			 * A bad port implementation can keep on failing the
199fcf3ce44SJohn Forte 			 * the request but keep on sending us a false
200fcf3ce44SJohn Forte 			 * minsize.
201fcf3ce44SJohn Forte 			 */
202fcf3ce44SJohn Forte 			old_minsize = minsize;
203fcf3ce44SJohn Forte 			dbuf = stmf_alloc_dbuf(task, maxsize, &minsize, 0);
204fcf3ce44SJohn Forte 		} while ((dbuf == NULL) && (old_minsize > minsize) &&
205fcf3ce44SJohn Forte 		    (minsize >= 512));
206fcf3ce44SJohn Forte 		if (dbuf == NULL) {
207fcf3ce44SJohn Forte 			return;
208fcf3ce44SJohn Forte 		}
20961dfa509SRick McNeal 		atomic_inc_8(&scmd->nbufs);
210fcf3ce44SJohn Forte 		sbd_do_read_xfer(task, scmd, dbuf);
211fcf3ce44SJohn Forte 	}
212fcf3ce44SJohn Forte }
213fcf3ce44SJohn Forte 
2143fb517f7SJames Moore /*
2153fb517f7SJames Moore  * sbd_zcopy: Bail-out switch for reduced copy path.
2163fb517f7SJames Moore  *
2173fb517f7SJames Moore  * 0 - read & write off
2183fb517f7SJames Moore  * 1 - read & write on
2193fb517f7SJames Moore  * 2 - only read on
2203fb517f7SJames Moore  * 4 - only write on
2213fb517f7SJames Moore  */
2223fb517f7SJames Moore int sbd_zcopy = 1;	/* enable zcopy read & write path */
2233fb517f7SJames Moore uint32_t sbd_max_xfer_len = 0;		/* Valid if non-zero */
2243fb517f7SJames Moore uint32_t sbd_1st_xfer_len = 0;		/* Valid if non-zero */
2253fb517f7SJames Moore uint32_t sbd_copy_threshold = 0;		/* Valid if non-zero */
2263fb517f7SJames Moore 
2273fb517f7SJames Moore static void
sbd_do_sgl_read_xfer(struct scsi_task * task,sbd_cmd_t * scmd,int first_xfer)2283fb517f7SJames Moore sbd_do_sgl_read_xfer(struct scsi_task *task, sbd_cmd_t *scmd, int first_xfer)
2293fb517f7SJames Moore {
2303fb517f7SJames Moore 	sbd_lu_t *sl = (sbd_lu_t *)task->task_lu->lu_provider_private;
2313fb517f7SJames Moore 	sbd_zvol_io_t *zvio;
2323fb517f7SJames Moore 	int ret, final_xfer;
2333fb517f7SJames Moore 	uint64_t offset;
2343fb517f7SJames Moore 	uint32_t xfer_len, max_len, first_len;
2353fb517f7SJames Moore 	stmf_status_t xstat;
2363fb517f7SJames Moore 	stmf_data_buf_t *dbuf;
2373fb517f7SJames Moore 	uint_t nblks;
2383fb517f7SJames Moore 	uint64_t blksize = sl->sl_blksize;
2393fb517f7SJames Moore 	size_t db_private_sz;
24061dfa509SRick McNeal 	hrtime_t xfer_start;
2413fb517f7SJames Moore 	uintptr_t pad;
2423fb517f7SJames Moore 
2433fb517f7SJames Moore 	ASSERT(rw_read_held(&sl->sl_access_state_lock));
2443fb517f7SJames Moore 	ASSERT((sl->sl_flags & SL_MEDIA_LOADED) != 0);
2453fb517f7SJames Moore 
2463fb517f7SJames Moore 	/*
2473fb517f7SJames Moore 	 * Calculate the limits on xfer_len to the minimum of :
2483fb517f7SJames Moore 	 *    - task limit
2493fb517f7SJames Moore 	 *    - lun limit
2503fb517f7SJames Moore 	 *    - sbd global limit if set
2513fb517f7SJames Moore 	 *    - first xfer limit if set
2523fb517f7SJames Moore 	 *
2533fb517f7SJames Moore 	 * First, protect against silly over-ride value
2543fb517f7SJames Moore 	 */
2553fb517f7SJames Moore 	if (sbd_max_xfer_len && ((sbd_max_xfer_len % DEV_BSIZE) != 0)) {
2563fb517f7SJames Moore 		cmn_err(CE_WARN, "sbd_max_xfer_len invalid %d, resetting\n",
2573fb517f7SJames Moore 		    sbd_max_xfer_len);
2583fb517f7SJames Moore 		sbd_max_xfer_len = 0;
2593fb517f7SJames Moore 	}
2603fb517f7SJames Moore 	if (sbd_1st_xfer_len && ((sbd_1st_xfer_len % DEV_BSIZE) != 0)) {
2613fb517f7SJames Moore 		cmn_err(CE_WARN, "sbd_1st_xfer_len invalid %d, resetting\n",
2623fb517f7SJames Moore 		    sbd_1st_xfer_len);
2633fb517f7SJames Moore 		sbd_1st_xfer_len = 0;
2643fb517f7SJames Moore 	}
2653fb517f7SJames Moore 
2663fb517f7SJames Moore 	max_len = MIN(task->task_max_xfer_len, sl->sl_max_xfer_len);
2673fb517f7SJames Moore 	if (sbd_max_xfer_len)
2683fb517f7SJames Moore 		max_len = MIN(max_len, sbd_max_xfer_len);
2693fb517f7SJames Moore 	/*
2703fb517f7SJames Moore 	 * Special case the first xfer if hints are set.
2713fb517f7SJames Moore 	 */
2723fb517f7SJames Moore 	if (first_xfer && (sbd_1st_xfer_len || task->task_1st_xfer_len)) {
2733fb517f7SJames Moore 		/* global over-ride has precedence */
2743fb517f7SJames Moore 		if (sbd_1st_xfer_len)
2753fb517f7SJames Moore 			first_len = sbd_1st_xfer_len;
2763fb517f7SJames Moore 		else
2773fb517f7SJames Moore 			first_len = task->task_1st_xfer_len;
2783fb517f7SJames Moore 	} else {
2793fb517f7SJames Moore 		first_len = 0;
2803fb517f7SJames Moore 	}
2813fb517f7SJames Moore 
28261dfa509SRick McNeal 	while (ATOMIC32_GET(scmd->len) &&
28361dfa509SRick McNeal 	    ATOMIC8_GET(scmd->nbufs) < task->task_max_nbufs) {
2843fb517f7SJames Moore 
28561dfa509SRick McNeal 		xfer_len = MIN(max_len, ATOMIC32_GET(scmd->len));
2863fb517f7SJames Moore 		if (first_len) {
2873fb517f7SJames Moore 			xfer_len = MIN(xfer_len, first_len);
2883fb517f7SJames Moore 			first_len = 0;
2893fb517f7SJames Moore 		}
29061dfa509SRick McNeal 		if (ATOMIC32_GET(scmd->len) == xfer_len) {
2913fb517f7SJames Moore 			final_xfer = 1;
2923fb517f7SJames Moore 		} else {
2933fb517f7SJames Moore 			/*
2943fb517f7SJames Moore 			 * Attempt to end xfer on a block boundary.
2953fb517f7SJames Moore 			 * The only way this does not happen is if the
2963fb517f7SJames Moore 			 * xfer_len is small enough to stay contained
2973fb517f7SJames Moore 			 * within the same block.
2983fb517f7SJames Moore 			 */
2993fb517f7SJames Moore 			uint64_t xfer_offset, xfer_aligned_end;
3003fb517f7SJames Moore 
3013fb517f7SJames Moore 			final_xfer = 0;
3023fb517f7SJames Moore 			xfer_offset = scmd->addr + scmd->current_ro;
3033fb517f7SJames Moore 			xfer_aligned_end =
3043fb517f7SJames Moore 			    P2ALIGN(xfer_offset+xfer_len, blksize);
3053fb517f7SJames Moore 			if (xfer_aligned_end > xfer_offset)
3063fb517f7SJames Moore 				xfer_len = xfer_aligned_end - xfer_offset;
3073fb517f7SJames Moore 		}
3083fb517f7SJames Moore 		/*
3093fb517f7SJames Moore 		 * Allocate object to track the read and reserve
3103fb517f7SJames Moore 		 * enough space for scatter/gather list.
3113fb517f7SJames Moore 		 */
3123fb517f7SJames Moore 		offset = scmd->addr + scmd->current_ro;
3133fb517f7SJames Moore 		nblks = sbd_zvol_numsegs(sl, offset, xfer_len);
3143fb517f7SJames Moore 
3153fb517f7SJames Moore 		db_private_sz = sizeof (*zvio) + sizeof (uintptr_t) /* PAD */ +
3163fb517f7SJames Moore 		    (nblks * sizeof (stmf_sglist_ent_t));
3173fb517f7SJames Moore 		dbuf = stmf_alloc(STMF_STRUCT_DATA_BUF, db_private_sz,
3183fb517f7SJames Moore 		    AF_DONTZERO);
3193fb517f7SJames Moore 		/*
3203fb517f7SJames Moore 		 * Setup the dbuf
3213fb517f7SJames Moore 		 *
3223fb517f7SJames Moore 		 * XXX Framework does not handle variable length sglists
3233fb517f7SJames Moore 		 * properly, so setup db_lu_private and db_port_private
3243fb517f7SJames Moore 		 * fields here. db_stmf_private is properly set for
3253fb517f7SJames Moore 		 * calls to stmf_free.
3263fb517f7SJames Moore 		 */
3273fb517f7SJames Moore 		if (dbuf->db_port_private == NULL) {
3283fb517f7SJames Moore 			/*
3293fb517f7SJames Moore 			 * XXX Framework assigns space to PP after db_sglist[0]
3303fb517f7SJames Moore 			 */
3313fb517f7SJames Moore 			cmn_err(CE_PANIC, "db_port_private == NULL");
3323fb517f7SJames Moore 		}
3333fb517f7SJames Moore 		pad = (uintptr_t)&dbuf->db_sglist[nblks];
3343fb517f7SJames Moore 		dbuf->db_lu_private = (void *)P2ROUNDUP(pad, sizeof (pad));
3353fb517f7SJames Moore 		dbuf->db_port_private = NULL;
3363fb517f7SJames Moore 		dbuf->db_buf_size = xfer_len;
3373fb517f7SJames Moore 		dbuf->db_data_size = xfer_len;
3383fb517f7SJames Moore 		dbuf->db_relative_offset = scmd->current_ro;
3393fb517f7SJames Moore 		dbuf->db_sglist_length = (uint16_t)nblks;
3403fb517f7SJames Moore 		dbuf->db_xfer_status = 0;
3413fb517f7SJames Moore 		dbuf->db_handle = 0;
3423fb517f7SJames Moore 
3433fb517f7SJames Moore 		dbuf->db_flags = (DB_DONT_CACHE | DB_DONT_REUSE |
3443fb517f7SJames Moore 		    DB_DIRECTION_TO_RPORT | DB_LU_DATA_BUF);
3453fb517f7SJames Moore 		if (final_xfer)
3463fb517f7SJames Moore 			dbuf->db_flags |= DB_SEND_STATUS_GOOD;
3473fb517f7SJames Moore 
3483fb517f7SJames Moore 		zvio = dbuf->db_lu_private;
3493fb517f7SJames Moore 		/* Need absolute offset for zvol access */
3503fb517f7SJames Moore 		zvio->zvio_offset = offset;
3513fb517f7SJames Moore 		zvio->zvio_flags = ZVIO_SYNC;
3523fb517f7SJames Moore 
3533fb517f7SJames Moore 		/*
3543fb517f7SJames Moore 		 * Accounting for start of read.
3553fb517f7SJames Moore 		 * Note there is no buffer address for the probe yet.
3563fb517f7SJames Moore 		 */
35761dfa509SRick McNeal 		xfer_start = gethrtime();
3583fb517f7SJames Moore 		DTRACE_PROBE5(backing__store__read__start, sbd_lu_t *, sl,
3593fb517f7SJames Moore 		    uint8_t *, NULL, uint64_t, xfer_len,
3603fb517f7SJames Moore 		    uint64_t, offset, scsi_task_t *, task);
3613fb517f7SJames Moore 
3623fb517f7SJames Moore 		ret = sbd_zvol_alloc_read_bufs(sl, dbuf);
3633fb517f7SJames Moore 
36461dfa509SRick McNeal 		stmf_lu_xfer_done(task, B_TRUE /* read */,
36561dfa509SRick McNeal 		    (gethrtime() - xfer_start));
3663fb517f7SJames Moore 		DTRACE_PROBE6(backing__store__read__end, sbd_lu_t *, sl,
3673fb517f7SJames Moore 		    uint8_t *, NULL, uint64_t, xfer_len,
3683fb517f7SJames Moore 		    uint64_t, offset, int, ret, scsi_task_t *, task);
3693fb517f7SJames Moore 
3703fb517f7SJames Moore 		if (ret != 0) {
3713fb517f7SJames Moore 			/*
3723fb517f7SJames Moore 			 * Read failure from the backend.
3733fb517f7SJames Moore 			 */
3743fb517f7SJames Moore 			stmf_free(dbuf);
37561dfa509SRick McNeal 			if (ATOMIC8_GET(scmd->nbufs) == 0) {
3763fb517f7SJames Moore 				/* nothing queued, just finish */
3773fb517f7SJames Moore 				scmd->flags &= ~SBD_SCSI_CMD_ACTIVE;
37861dfa509SRick McNeal 				sbd_ats_remove_by_task(task);
3793fb517f7SJames Moore 				stmf_scsilib_send_status(task, STATUS_CHECK,
3803fb517f7SJames Moore 				    STMF_SAA_READ_ERROR);
3813fb517f7SJames Moore 				rw_exit(&sl->sl_access_state_lock);
3823fb517f7SJames Moore 			} else {
3833fb517f7SJames Moore 				/* process failure when other dbufs finish */
3843fb517f7SJames Moore 				scmd->flags |= SBD_SCSI_CMD_XFER_FAIL;
3853fb517f7SJames Moore 			}
3863fb517f7SJames Moore 			return;
3873fb517f7SJames Moore 		}
3883fb517f7SJames Moore 
3893fb517f7SJames Moore 		/*
3903fb517f7SJames Moore 		 * Allow PP to do setup
3913fb517f7SJames Moore 		 */
3923fb517f7SJames Moore 		xstat = stmf_setup_dbuf(task, dbuf, 0);
3933fb517f7SJames Moore 		if (xstat != STMF_SUCCESS) {
3943fb517f7SJames Moore 			/*
3953fb517f7SJames Moore 			 * This could happen if the driver cannot get the
3963fb517f7SJames Moore 			 * DDI resources it needs for this request.
3973fb517f7SJames Moore 			 * If other dbufs are queued, try again when the next
3983fb517f7SJames Moore 			 * one completes, otherwise give up.
3993fb517f7SJames Moore 			 */
4003fb517f7SJames Moore 			sbd_zvol_rele_read_bufs(sl, dbuf);
4013fb517f7SJames Moore 			stmf_free(dbuf);
40261dfa509SRick McNeal 			if (ATOMIC8_GET(scmd->nbufs) > 0) {
4033fb517f7SJames Moore 				/* completion of previous dbuf will retry */
4043fb517f7SJames Moore 				return;
4053fb517f7SJames Moore 			}
4063fb517f7SJames Moore 			/*
4073fb517f7SJames Moore 			 * Done with this command.
4083fb517f7SJames Moore 			 */
4093fb517f7SJames Moore 			scmd->flags &= ~SBD_SCSI_CMD_ACTIVE;
41061dfa509SRick McNeal 			sbd_ats_remove_by_task(task);
4113fb517f7SJames Moore 			if (first_xfer)
4123fb517f7SJames Moore 				stmf_scsilib_send_status(task, STATUS_QFULL, 0);
4133fb517f7SJames Moore 			else
4143fb517f7SJames Moore 				stmf_scsilib_send_status(task, STATUS_CHECK,
4153fb517f7SJames Moore 				    STMF_SAA_READ_ERROR);
4163fb517f7SJames Moore 			rw_exit(&sl->sl_access_state_lock);
4173fb517f7SJames Moore 			return;
4183fb517f7SJames Moore 		}
4193fb517f7SJames Moore 		/*
4203fb517f7SJames Moore 		 * dbuf is now queued on task
4213fb517f7SJames Moore 		 */
42261dfa509SRick McNeal 		atomic_inc_8(&scmd->nbufs);
4233fb517f7SJames Moore 
4243fb517f7SJames Moore 		/* XXX leave this in for FW? */
4253fb517f7SJames Moore 		DTRACE_PROBE4(sbd__xfer, struct scsi_task *, task,
4263fb517f7SJames Moore 		    struct stmf_data_buf *, dbuf, uint64_t, offset,
4273fb517f7SJames Moore 		    uint32_t, xfer_len);
4283fb517f7SJames Moore 		/*
4293fb517f7SJames Moore 		 * Do not pass STMF_IOF_LU_DONE so that the zvol
4303fb517f7SJames Moore 		 * state can be released in the completion callback.
4313fb517f7SJames Moore 		 */
4323fb517f7SJames Moore 		xstat = stmf_xfer_data(task, dbuf, 0);
4333fb517f7SJames Moore 		switch (xstat) {
4343fb517f7SJames Moore 		case STMF_SUCCESS:
4353fb517f7SJames Moore 			break;
4363fb517f7SJames Moore 		case STMF_BUSY:
4373fb517f7SJames Moore 			/*
4383fb517f7SJames Moore 			 * The dbuf is queued on the task, but unknown
4393fb517f7SJames Moore 			 * to the PP, thus no completion will occur.
4403fb517f7SJames Moore 			 */
4413fb517f7SJames Moore 			sbd_zvol_rele_read_bufs(sl, dbuf);
4423fb517f7SJames Moore 			stmf_teardown_dbuf(task, dbuf);
4433fb517f7SJames Moore 			stmf_free(dbuf);
44461dfa509SRick McNeal 			atomic_dec_8(&scmd->nbufs);
44561dfa509SRick McNeal 			if (ATOMIC8_GET(scmd->nbufs) > 0) {
4463fb517f7SJames Moore 				/* completion of previous dbuf will retry */
4473fb517f7SJames Moore 				return;
4483fb517f7SJames Moore 			}
4493fb517f7SJames Moore 			/*
4503fb517f7SJames Moore 			 * Done with this command.
4513fb517f7SJames Moore 			 */
4523fb517f7SJames Moore 			rw_exit(&sl->sl_access_state_lock);
4533fb517f7SJames Moore 			scmd->flags &= ~SBD_SCSI_CMD_ACTIVE;
45461dfa509SRick McNeal 			sbd_ats_remove_by_task(task);
4553fb517f7SJames Moore 			if (first_xfer)
4563fb517f7SJames Moore 				stmf_scsilib_send_status(task, STATUS_QFULL, 0);
4573fb517f7SJames Moore 			else
4583fb517f7SJames Moore 				stmf_scsilib_send_status(task, STATUS_CHECK,
4593fb517f7SJames Moore 				    STMF_SAA_READ_ERROR);
4603fb517f7SJames Moore 			return;
4613fb517f7SJames Moore 		case STMF_ABORTED:
4623fb517f7SJames Moore 			/*
4633fb517f7SJames Moore 			 * Completion from task_done will cleanup
4643fb517f7SJames Moore 			 */
4653fb517f7SJames Moore 			scmd->flags &= ~SBD_SCSI_CMD_ACTIVE;
46661dfa509SRick McNeal 			sbd_ats_remove_by_task(task);
4673fb517f7SJames Moore 			return;
4683fb517f7SJames Moore 		}
4693fb517f7SJames Moore 		/*
4703fb517f7SJames Moore 		 * Update the xfer progress.
4713fb517f7SJames Moore 		 */
4723fb517f7SJames Moore 		ASSERT(scmd->len >= xfer_len);
47361dfa509SRick McNeal 		atomic_add_32(&scmd->len, -xfer_len);
4743fb517f7SJames Moore 		scmd->current_ro += xfer_len;
4753fb517f7SJames Moore 	}
4763fb517f7SJames Moore }
4773fb517f7SJames Moore 
478fcf3ce44SJohn Forte void
sbd_handle_read_xfer_completion(struct scsi_task * task,sbd_cmd_t * scmd,struct stmf_data_buf * dbuf)479fcf3ce44SJohn Forte sbd_handle_read_xfer_completion(struct scsi_task *task, sbd_cmd_t *scmd,
480047c81d3SSaso Kiselkov     struct stmf_data_buf *dbuf)
481fcf3ce44SJohn Forte {
482fcf3ce44SJohn Forte 	if (dbuf->db_xfer_status != STMF_SUCCESS) {
483fcf3ce44SJohn Forte 		stmf_abort(STMF_QUEUE_TASK_ABORT, task,
484fcf3ce44SJohn Forte 		    dbuf->db_xfer_status, NULL);
485fcf3ce44SJohn Forte 		return;
486fcf3ce44SJohn Forte 	}
487fcf3ce44SJohn Forte 	task->task_nbytes_transferred += dbuf->db_data_size;
48861dfa509SRick McNeal 	if (ATOMIC32_GET(scmd->len) == 0 ||
48961dfa509SRick McNeal 	    scmd->flags & SBD_SCSI_CMD_XFER_FAIL) {
490fcf3ce44SJohn Forte 		stmf_free_dbuf(task, dbuf);
49161dfa509SRick McNeal 		atomic_dec_8(&scmd->nbufs);
49261dfa509SRick McNeal 		if (ATOMIC8_GET(scmd->nbufs))
493fcf3ce44SJohn Forte 			return;	/* wait for all buffers to complete */
494fcf3ce44SJohn Forte 		scmd->flags &= ~SBD_SCSI_CMD_ACTIVE;
49561dfa509SRick McNeal 		sbd_ats_remove_by_task(task);
496fcf3ce44SJohn Forte 		if (scmd->flags & SBD_SCSI_CMD_XFER_FAIL)
497fcf3ce44SJohn Forte 			stmf_scsilib_send_status(task, STATUS_CHECK,
498fcf3ce44SJohn Forte 			    STMF_SAA_READ_ERROR);
499fcf3ce44SJohn Forte 		else
500fcf3ce44SJohn Forte 			stmf_scsilib_send_status(task, STATUS_GOOD, 0);
501fcf3ce44SJohn Forte 		return;
502fcf3ce44SJohn Forte 	}
5032a8164dfSZhong Wang 	if (dbuf->db_flags & DB_DONT_REUSE) {
5042a8164dfSZhong Wang 		/* allocate new dbuf */
5052a8164dfSZhong Wang 		uint32_t maxsize, minsize, old_minsize;
5062a8164dfSZhong Wang 		stmf_free_dbuf(task, dbuf);
5072a8164dfSZhong Wang 
50861dfa509SRick McNeal 		maxsize = (ATOMIC32_GET(scmd->len) > (128*1024)) ?
50961dfa509SRick McNeal 		    128 * 1024 : ATOMIC32_GET(scmd->len);
5102a8164dfSZhong Wang 		minsize = maxsize >> 2;
5112a8164dfSZhong Wang 		do {
5122a8164dfSZhong Wang 			old_minsize = minsize;
5132a8164dfSZhong Wang 			dbuf = stmf_alloc_dbuf(task, maxsize, &minsize, 0);
5142a8164dfSZhong Wang 		} while ((dbuf == NULL) && (old_minsize > minsize) &&
5152a8164dfSZhong Wang 		    (minsize >= 512));
5162a8164dfSZhong Wang 		if (dbuf == NULL) {
51761dfa509SRick McNeal 			atomic_dec_8(&scmd->nbufs);
51861dfa509SRick McNeal 			if (ATOMIC8_GET(scmd->nbufs) == 0) {
5192a8164dfSZhong Wang 				stmf_abort(STMF_QUEUE_TASK_ABORT, task,
5202a8164dfSZhong Wang 				    STMF_ALLOC_FAILURE, NULL);
5212a8164dfSZhong Wang 			}
5222a8164dfSZhong Wang 			return;
5232a8164dfSZhong Wang 		}
5242a8164dfSZhong Wang 	}
525fcf3ce44SJohn Forte 	sbd_do_read_xfer(task, scmd, dbuf);
526fcf3ce44SJohn Forte }
527fcf3ce44SJohn Forte 
5283fb517f7SJames Moore /*
5293fb517f7SJames Moore  * This routine must release the DMU resources and free the dbuf
5303fb517f7SJames Moore  * in all cases.  If this is the final dbuf of the task, then drop
5313fb517f7SJames Moore  * the reader lock on the LU state. If there are no errors and more
5323fb517f7SJames Moore  * work to do, then queue more xfer operations.
5333fb517f7SJames Moore  */
5343fb517f7SJames Moore void
sbd_handle_sgl_read_xfer_completion(struct scsi_task * task,sbd_cmd_t * scmd,struct stmf_data_buf * dbuf)5353fb517f7SJames Moore sbd_handle_sgl_read_xfer_completion(struct scsi_task *task, sbd_cmd_t *scmd,
536047c81d3SSaso Kiselkov     struct stmf_data_buf *dbuf)
5373fb517f7SJames Moore {
5383fb517f7SJames Moore 	sbd_lu_t *sl = (sbd_lu_t *)task->task_lu->lu_provider_private;
5393fb517f7SJames Moore 	stmf_status_t xfer_status;
5403fb517f7SJames Moore 	uint32_t data_size;
5413fb517f7SJames Moore 	int scmd_err;
5423fb517f7SJames Moore 
5433fb517f7SJames Moore 	ASSERT(dbuf->db_lu_private);
5443fb517f7SJames Moore 	ASSERT(scmd->cmd_type == SBD_CMD_SCSI_READ);
5453fb517f7SJames Moore 
54661dfa509SRick McNeal 	atomic_dec_8(&scmd->nbufs);	/* account for this dbuf */
5473fb517f7SJames Moore 	/*
5483fb517f7SJames Moore 	 * Release the DMU resources.
5493fb517f7SJames Moore 	 */
5503fb517f7SJames Moore 	sbd_zvol_rele_read_bufs(sl, dbuf);
5513fb517f7SJames Moore 	/*
5523fb517f7SJames Moore 	 * Release the dbuf after retrieving needed fields.
5533fb517f7SJames Moore 	 */
5543fb517f7SJames Moore 	xfer_status = dbuf->db_xfer_status;
5553fb517f7SJames Moore 	data_size = dbuf->db_data_size;
5563fb517f7SJames Moore 	stmf_teardown_dbuf(task, dbuf);
5573fb517f7SJames Moore 	stmf_free(dbuf);
5583fb517f7SJames Moore 	/*
5593fb517f7SJames Moore 	 * Release the state lock if this is the last completion.
5603fb517f7SJames Moore 	 * If this is the last dbuf on task and all data has been
5613fb517f7SJames Moore 	 * transferred or an error encountered, then no more dbufs
5623fb517f7SJames Moore 	 * will be queued.
5633fb517f7SJames Moore 	 */
5643fb517f7SJames Moore 	scmd_err = (((scmd->flags & SBD_SCSI_CMD_ACTIVE) == 0) ||
5653fb517f7SJames Moore 	    (scmd->flags & SBD_SCSI_CMD_XFER_FAIL) ||
5663fb517f7SJames Moore 	    (xfer_status != STMF_SUCCESS));
56761dfa509SRick McNeal 	if ((ATOMIC8_GET(scmd->nbufs) == 0) &&
56861dfa509SRick McNeal 	    (ATOMIC32_GET(scmd->len) == 0 || scmd_err)) {
5693fb517f7SJames Moore 		/* all DMU state has been released */
5703fb517f7SJames Moore 		rw_exit(&sl->sl_access_state_lock);
5713fb517f7SJames Moore 	}
5723fb517f7SJames Moore 
5733fb517f7SJames Moore 	/*
5743fb517f7SJames Moore 	 * If there have been no errors, either complete the task
5753fb517f7SJames Moore 	 * or issue more data xfer operations.
5763fb517f7SJames Moore 	 */
5773fb517f7SJames Moore 	if (!scmd_err) {
5783fb517f7SJames Moore 		/*
5793fb517f7SJames Moore 		 * This chunk completed successfully
5803fb517f7SJames Moore 		 */
5813fb517f7SJames Moore 		task->task_nbytes_transferred += data_size;
58261dfa509SRick McNeal 		if (ATOMIC8_GET(scmd->nbufs) == 0 &&
58361dfa509SRick McNeal 		    ATOMIC32_GET(scmd->len) == 0) {
5843fb517f7SJames Moore 			/*
5853fb517f7SJames Moore 			 * This command completed successfully
5863fb517f7SJames Moore 			 *
5873fb517f7SJames Moore 			 * Status was sent along with data, so no status
5883fb517f7SJames Moore 			 * completion will occur. Tell stmf we are done.
5893fb517f7SJames Moore 			 */
5903fb517f7SJames Moore 			scmd->flags &= ~SBD_SCSI_CMD_ACTIVE;
59161dfa509SRick McNeal 			sbd_ats_remove_by_task(task);
5923fb517f7SJames Moore 			stmf_task_lu_done(task);
5933fb517f7SJames Moore 			return;
5943fb517f7SJames Moore 		}
5953fb517f7SJames Moore 		/*
5963fb517f7SJames Moore 		 * Start more xfers
5973fb517f7SJames Moore 		 */
5983fb517f7SJames Moore 		sbd_do_sgl_read_xfer(task, scmd, 0);
5993fb517f7SJames Moore 		return;
6003fb517f7SJames Moore 	}
6013fb517f7SJames Moore 	/*
6023fb517f7SJames Moore 	 * Sort out the failure
6033fb517f7SJames Moore 	 */
6043fb517f7SJames Moore 	if (scmd->flags & SBD_SCSI_CMD_ACTIVE) {
6053fb517f7SJames Moore 		/*
6063fb517f7SJames Moore 		 * If a previous error occurred, leave the command active
6073fb517f7SJames Moore 		 * and wait for the last completion to send the status check.
6083fb517f7SJames Moore 		 */
6093fb517f7SJames Moore 		if (scmd->flags & SBD_SCSI_CMD_XFER_FAIL) {
61061dfa509SRick McNeal 			if (ATOMIC8_GET(scmd->nbufs) == 0) {
6113fb517f7SJames Moore 				scmd->flags &= ~SBD_SCSI_CMD_ACTIVE;
61261dfa509SRick McNeal 				sbd_ats_remove_by_task(task);
6133fb517f7SJames Moore 				stmf_scsilib_send_status(task, STATUS_CHECK,
6143fb517f7SJames Moore 				    STMF_SAA_READ_ERROR);
6153fb517f7SJames Moore 			}
6163fb517f7SJames Moore 			return;
6173fb517f7SJames Moore 		}
6183fb517f7SJames Moore 		/*
6193fb517f7SJames Moore 		 * Must have been a failure on current dbuf
6203fb517f7SJames Moore 		 */
6213fb517f7SJames Moore 		ASSERT(xfer_status != STMF_SUCCESS);
62261dfa509SRick McNeal 
62361dfa509SRick McNeal 		/*
62461dfa509SRick McNeal 		 * Actually this is a bug. stmf abort should have reset the
62561dfa509SRick McNeal 		 * active flag but since its been there for some time.
62661dfa509SRick McNeal 		 * I wont change it.
62761dfa509SRick McNeal 		 */
6283fb517f7SJames Moore 		scmd->flags &= ~SBD_SCSI_CMD_ACTIVE;
62961dfa509SRick McNeal 		sbd_ats_remove_by_task(task);
6303fb517f7SJames Moore 		stmf_abort(STMF_QUEUE_TASK_ABORT, task, xfer_status, NULL);
6313fb517f7SJames Moore 	}
6323fb517f7SJames Moore }
6333fb517f7SJames Moore 
6343fb517f7SJames Moore void
sbd_handle_sgl_write_xfer_completion(struct scsi_task * task,sbd_cmd_t * scmd,struct stmf_data_buf * dbuf)6353fb517f7SJames Moore sbd_handle_sgl_write_xfer_completion(struct scsi_task *task, sbd_cmd_t *scmd,
636047c81d3SSaso Kiselkov     struct stmf_data_buf *dbuf)
6373fb517f7SJames Moore {
6383fb517f7SJames Moore 	sbd_zvol_io_t *zvio = dbuf->db_lu_private;
6393fb517f7SJames Moore 	sbd_lu_t *sl = (sbd_lu_t *)task->task_lu->lu_provider_private;
6403fb517f7SJames Moore 	int ret;
6413fb517f7SJames Moore 	int scmd_err, scmd_xfer_done;
6423fb517f7SJames Moore 	stmf_status_t xfer_status = dbuf->db_xfer_status;
6433fb517f7SJames Moore 	uint32_t data_size = dbuf->db_data_size;
64461dfa509SRick McNeal 	hrtime_t xfer_start;
6453fb517f7SJames Moore 
6463fb517f7SJames Moore 	ASSERT(zvio);
6473fb517f7SJames Moore 
6483fb517f7SJames Moore 	/*
6493fb517f7SJames Moore 	 * Allow PP to free up resources before releasing the write bufs
6503fb517f7SJames Moore 	 * as writing to the backend could take some time.
6513fb517f7SJames Moore 	 */
6523fb517f7SJames Moore 	stmf_teardown_dbuf(task, dbuf);
6533fb517f7SJames Moore 
65461dfa509SRick McNeal 	atomic_dec_8(&scmd->nbufs);	/* account for this dbuf */
6553fb517f7SJames Moore 	/*
6563fb517f7SJames Moore 	 * All data was queued and this is the last completion,
6573fb517f7SJames Moore 	 * but there could still be an error.
6583fb517f7SJames Moore 	 */
65961dfa509SRick McNeal 	scmd_xfer_done = (ATOMIC32_GET(scmd->len) == 0 &&
66061dfa509SRick McNeal 	    (ATOMIC8_GET(scmd->nbufs) == 0));
6613fb517f7SJames Moore 	scmd_err = (((scmd->flags & SBD_SCSI_CMD_ACTIVE) == 0) ||
6623fb517f7SJames Moore 	    (scmd->flags & SBD_SCSI_CMD_XFER_FAIL) ||
6633fb517f7SJames Moore 	    (xfer_status != STMF_SUCCESS));
6643fb517f7SJames Moore 
66561dfa509SRick McNeal 	xfer_start = gethrtime();
6663fb517f7SJames Moore 	DTRACE_PROBE5(backing__store__write__start, sbd_lu_t *, sl,
6673fb517f7SJames Moore 	    uint8_t *, NULL, uint64_t, data_size,
6683fb517f7SJames Moore 	    uint64_t, zvio->zvio_offset, scsi_task_t *, task);
6693fb517f7SJames Moore 
6703fb517f7SJames Moore 	if (scmd_err) {
6713fb517f7SJames Moore 		/* just return the write buffers */
6723fb517f7SJames Moore 		sbd_zvol_rele_write_bufs_abort(sl, dbuf);
6733fb517f7SJames Moore 		ret = 0;
6743fb517f7SJames Moore 	} else {
6753fb517f7SJames Moore 		if (scmd_xfer_done)
6763fb517f7SJames Moore 			zvio->zvio_flags = ZVIO_COMMIT;
6773fb517f7SJames Moore 		else
6783fb517f7SJames Moore 			zvio->zvio_flags = 0;
6793fb517f7SJames Moore 		/* write the data */
6803fb517f7SJames Moore 		ret = sbd_zvol_rele_write_bufs(sl, dbuf);
6813fb517f7SJames Moore 	}
6823fb517f7SJames Moore 
68361dfa509SRick McNeal 	stmf_lu_xfer_done(task, B_FALSE /* write */,
68461dfa509SRick McNeal 	    (gethrtime() - xfer_start));
6853fb517f7SJames Moore 	DTRACE_PROBE6(backing__store__write__end, sbd_lu_t *, sl,
6863fb517f7SJames Moore 	    uint8_t *, NULL, uint64_t, data_size,
6873fb517f7SJames Moore 	    uint64_t, zvio->zvio_offset, int, ret,  scsi_task_t *, task);
6883fb517f7SJames Moore 
6893fb517f7SJames Moore 	if (ret != 0) {
6903fb517f7SJames Moore 		/* update the error flag */
6913fb517f7SJames Moore 		scmd->flags |= SBD_SCSI_CMD_XFER_FAIL;
6923fb517f7SJames Moore 		scmd_err = 1;
6933fb517f7SJames Moore 	}
6943fb517f7SJames Moore 
6953fb517f7SJames Moore 	/* Release the dbuf */
6963fb517f7SJames Moore 	stmf_free(dbuf);
6973fb517f7SJames Moore 
6983fb517f7SJames Moore 	/*
6993fb517f7SJames Moore 	 * Release the state lock if this is the last completion.
7003fb517f7SJames Moore 	 * If this is the last dbuf on task and all data has been
7013fb517f7SJames Moore 	 * transferred or an error encountered, then no more dbufs
7023fb517f7SJames Moore 	 * will be queued.
7033fb517f7SJames Moore 	 */
70461dfa509SRick McNeal 	if ((ATOMIC8_GET(scmd->nbufs) == 0) &&
70561dfa509SRick McNeal 	    (ATOMIC32_GET(scmd->len) == 0 || scmd_err)) {
7063fb517f7SJames Moore 		/* all DMU state has been released */
7073fb517f7SJames Moore 		rw_exit(&sl->sl_access_state_lock);
7083fb517f7SJames Moore 	}
7093fb517f7SJames Moore 	/*
7103fb517f7SJames Moore 	 * If there have been no errors, either complete the task
7113fb517f7SJames Moore 	 * or issue more data xfer operations.
7123fb517f7SJames Moore 	 */
7133fb517f7SJames Moore 	if (!scmd_err) {
7143fb517f7SJames Moore 		/* This chunk completed successfully */
7153fb517f7SJames Moore 		task->task_nbytes_transferred += data_size;
7163fb517f7SJames Moore 		if (scmd_xfer_done) {
7173fb517f7SJames Moore 			/* This command completed successfully */
7183fb517f7SJames Moore 			scmd->flags &= ~SBD_SCSI_CMD_ACTIVE;
71961dfa509SRick McNeal 			sbd_ats_remove_by_task(task);
7203fb517f7SJames Moore 			if ((scmd->flags & SBD_SCSI_CMD_SYNC_WRITE) &&
7213fb517f7SJames Moore 			    (sbd_flush_data_cache(sl, 0) != SBD_SUCCESS)) {
7223fb517f7SJames Moore 				stmf_scsilib_send_status(task, STATUS_CHECK,
7233fb517f7SJames Moore 				    STMF_SAA_WRITE_ERROR);
7243fb517f7SJames Moore 			} else {
7253fb517f7SJames Moore 				stmf_scsilib_send_status(task, STATUS_GOOD, 0);
7263fb517f7SJames Moore 			}
7273fb517f7SJames Moore 			return;
7283fb517f7SJames Moore 		}
7293fb517f7SJames Moore 		/*
7303fb517f7SJames Moore 		 * Start more xfers
7313fb517f7SJames Moore 		 */
7323fb517f7SJames Moore 		sbd_do_sgl_write_xfer(task, scmd, 0);
7333fb517f7SJames Moore 		return;
7343fb517f7SJames Moore 	}
7353fb517f7SJames Moore 	/*
7363fb517f7SJames Moore 	 * Sort out the failure
7373fb517f7SJames Moore 	 */
7383fb517f7SJames Moore 	if (scmd->flags & SBD_SCSI_CMD_ACTIVE) {
7393fb517f7SJames Moore 		if (scmd->flags & SBD_SCSI_CMD_XFER_FAIL) {
74061dfa509SRick McNeal 			if (ATOMIC8_GET(scmd->nbufs) == 0) {
7413fb517f7SJames Moore 				scmd->flags &= ~SBD_SCSI_CMD_ACTIVE;
74261dfa509SRick McNeal 				sbd_ats_remove_by_task(task);
7433fb517f7SJames Moore 				stmf_scsilib_send_status(task, STATUS_CHECK,
7443fb517f7SJames Moore 				    STMF_SAA_WRITE_ERROR);
7453fb517f7SJames Moore 			}
7463fb517f7SJames Moore 			/*
7473fb517f7SJames Moore 			 * Leave the command active until last dbuf completes.
7483fb517f7SJames Moore 			 */
7493fb517f7SJames Moore 			return;
7503fb517f7SJames Moore 		}
7513fb517f7SJames Moore 		scmd->flags &= ~SBD_SCSI_CMD_ACTIVE;
75261dfa509SRick McNeal 		sbd_ats_remove_by_task(task);
7533fb517f7SJames Moore 		ASSERT(xfer_status != STMF_SUCCESS);
7543fb517f7SJames Moore 		stmf_abort(STMF_QUEUE_TASK_ABORT, task, xfer_status, NULL);
7553fb517f7SJames Moore 	}
7563fb517f7SJames Moore }
7573fb517f7SJames Moore 
7583fb517f7SJames Moore /*
7593fb517f7SJames Moore  * Handle a copy operation using the zvol interface.
7603fb517f7SJames Moore  *
7613fb517f7SJames Moore  * Similar to the sbd_data_read/write path, except it goes directly through
7623fb517f7SJames Moore  * the zvol interfaces. It can pass a port provider sglist in the
7633fb517f7SJames Moore  * form of uio which is lost through the vn_rdwr path.
7643fb517f7SJames Moore  *
7653fb517f7SJames Moore  * Returns:
7663fb517f7SJames Moore  *	STMF_SUCCESS - request handled
7673fb517f7SJames Moore  *	STMF_FAILURE - request not handled, caller must deal with error
7683fb517f7SJames Moore  */
7693fb517f7SJames Moore static stmf_status_t
sbd_copy_rdwr(scsi_task_t * task,uint64_t laddr,stmf_data_buf_t * dbuf,int cmd,int commit)7703fb517f7SJames Moore sbd_copy_rdwr(scsi_task_t *task, uint64_t laddr, stmf_data_buf_t *dbuf,
7713fb517f7SJames Moore     int cmd, int commit)
7723fb517f7SJames Moore {
7733fb517f7SJames Moore 	sbd_lu_t		*sl = task->task_lu->lu_provider_private;
7743fb517f7SJames Moore 	struct uio		uio;
7753fb517f7SJames Moore 	struct iovec		*iov, *tiov, iov1[8];
7763fb517f7SJames Moore 	uint32_t		len, resid;
7773fb517f7SJames Moore 	int			ret, i, iovcnt, flags;
77861dfa509SRick McNeal 	hrtime_t		xfer_start;
7793fb517f7SJames Moore 	boolean_t		is_read;
7803fb517f7SJames Moore 
7813fb517f7SJames Moore 	ASSERT(cmd == SBD_CMD_SCSI_READ || cmd == SBD_CMD_SCSI_WRITE);
7823fb517f7SJames Moore 
7833fb517f7SJames Moore 	is_read = (cmd == SBD_CMD_SCSI_READ) ? B_TRUE : B_FALSE;
7843fb517f7SJames Moore 	iovcnt = dbuf->db_sglist_length;
7853fb517f7SJames Moore 	/* use the stack for small iovecs */
7863fb517f7SJames Moore 	if (iovcnt > 8) {
7873fb517f7SJames Moore 		iov = kmem_alloc(iovcnt * sizeof (*iov), KM_SLEEP);
7883fb517f7SJames Moore 	} else {
7893fb517f7SJames Moore 		iov = &iov1[0];
7903fb517f7SJames Moore 	}
7913fb517f7SJames Moore 
7923fb517f7SJames Moore 	/* Convert dbuf sglist to iovec format */
7933fb517f7SJames Moore 	len = dbuf->db_data_size;
7943fb517f7SJames Moore 	resid = len;
7953fb517f7SJames Moore 	tiov = iov;
7963fb517f7SJames Moore 	for (i = 0; i < iovcnt; i++) {
7973fb517f7SJames Moore 		tiov->iov_base = (caddr_t)dbuf->db_sglist[i].seg_addr;
7983fb517f7SJames Moore 		tiov->iov_len = MIN(resid, dbuf->db_sglist[i].seg_length);
7993fb517f7SJames Moore 		resid -= tiov->iov_len;
8003fb517f7SJames Moore 		tiov++;
8013fb517f7SJames Moore 	}
8023fb517f7SJames Moore 	if (resid != 0) {
8033fb517f7SJames Moore 		cmn_err(CE_WARN, "inconsistant sglist rem %d", resid);
8043fb517f7SJames Moore 		if (iov != &iov1[0])
8053fb517f7SJames Moore 			kmem_free(iov, iovcnt * sizeof (*iov));
8063fb517f7SJames Moore 		return (STMF_FAILURE);
8073fb517f7SJames Moore 	}
8083fb517f7SJames Moore 	/* Setup the uio struct */
8093fb517f7SJames Moore 	uio.uio_iov = iov;
8103fb517f7SJames Moore 	uio.uio_iovcnt = iovcnt;
8113fb517f7SJames Moore 	uio.uio_loffset = laddr;
8123fb517f7SJames Moore 	uio.uio_segflg = (short)UIO_SYSSPACE;
8133fb517f7SJames Moore 	uio.uio_resid = (uint64_t)len;
8143fb517f7SJames Moore 	uio.uio_llimit = RLIM64_INFINITY;
8153fb517f7SJames Moore 
81661dfa509SRick McNeal 	xfer_start = gethrtime();
8173fb517f7SJames Moore 	if (is_read == B_TRUE) {
8183fb517f7SJames Moore 		uio.uio_fmode = FREAD;
8193fb517f7SJames Moore 		uio.uio_extflg = UIO_COPY_CACHED;
8203fb517f7SJames Moore 		DTRACE_PROBE5(backing__store__read__start, sbd_lu_t *, sl,
8213fb517f7SJames Moore 		    uint8_t *, NULL, uint64_t, len, uint64_t, laddr,
8223fb517f7SJames Moore 		    scsi_task_t *, task);
8233fb517f7SJames Moore 
8243fb517f7SJames Moore 		/* Fetch the data */
8253fb517f7SJames Moore 		ret = sbd_zvol_copy_read(sl, &uio);
8263fb517f7SJames Moore 
8273fb517f7SJames Moore 		DTRACE_PROBE6(backing__store__read__end, sbd_lu_t *, sl,
8283fb517f7SJames Moore 		    uint8_t *, NULL, uint64_t, len, uint64_t, laddr, int, ret,
8293fb517f7SJames Moore 		    scsi_task_t *, task);
8303fb517f7SJames Moore 	} else {
8313fb517f7SJames Moore 		uio.uio_fmode = FWRITE;
8323fb517f7SJames Moore 		uio.uio_extflg = UIO_COPY_DEFAULT;
8333fb517f7SJames Moore 		DTRACE_PROBE5(backing__store__write__start, sbd_lu_t *, sl,
8343fb517f7SJames Moore 		    uint8_t *, NULL, uint64_t, len, uint64_t, laddr,
8353fb517f7SJames Moore 		    scsi_task_t *, task);
8363fb517f7SJames Moore 
8373fb517f7SJames Moore 		flags = (commit) ? ZVIO_COMMIT : 0;
8383fb517f7SJames Moore 		/* Write the data */
8393fb517f7SJames Moore 		ret = sbd_zvol_copy_write(sl, &uio, flags);
8403fb517f7SJames Moore 
8413fb517f7SJames Moore 		DTRACE_PROBE6(backing__store__write__end, sbd_lu_t *, sl,
8423fb517f7SJames Moore 		    uint8_t *, NULL, uint64_t, len, uint64_t, laddr, int, ret,
8433fb517f7SJames Moore 		    scsi_task_t *, task);
8443fb517f7SJames Moore 	}
84561dfa509SRick McNeal 	/* finalize accounting */
84661dfa509SRick McNeal 	stmf_lu_xfer_done(task, is_read, (gethrtime() - xfer_start));
8473fb517f7SJames Moore 
8483fb517f7SJames Moore 	if (iov != &iov1[0])
8493fb517f7SJames Moore 		kmem_free(iov, iovcnt * sizeof (*iov));
8503fb517f7SJames Moore 	if (ret != 0) {
8513fb517f7SJames Moore 		/* Backend I/O error */
8523fb517f7SJames Moore 		return (STMF_FAILURE);
8533fb517f7SJames Moore 	}
8543fb517f7SJames Moore 	return (STMF_SUCCESS);
8553fb517f7SJames Moore }
8563fb517f7SJames Moore 
857fcf3ce44SJohn Forte void
sbd_handle_read(struct scsi_task * task,struct stmf_data_buf * initial_dbuf)858fcf3ce44SJohn Forte sbd_handle_read(struct scsi_task *task, struct stmf_data_buf *initial_dbuf)
859fcf3ce44SJohn Forte {
860fcf3ce44SJohn Forte 	uint64_t lba, laddr;
86161dfa509SRick McNeal 	uint64_t blkcount;
862fcf3ce44SJohn Forte 	uint32_t len;
863fcf3ce44SJohn Forte 	uint8_t op = task->task_cdb[0];
8648fe96085Stim szeto 	sbd_lu_t *sl = (sbd_lu_t *)task->task_lu->lu_provider_private;
865fcf3ce44SJohn Forte 	sbd_cmd_t *scmd;
866fcf3ce44SJohn Forte 	stmf_data_buf_t *dbuf;
867fcf3ce44SJohn Forte 	int fast_path;
86861dfa509SRick McNeal 	boolean_t fua_bit = B_FALSE;
869fcf3ce44SJohn Forte 
87061dfa509SRick McNeal 	/*
87161dfa509SRick McNeal 	 * Check to see if the command is READ(10), READ(12), or READ(16).
87261dfa509SRick McNeal 	 * If it is then check for bit 3 being set to indicate if Forced
87361dfa509SRick McNeal 	 * Unit Access is being requested. If so, we'll bypass the use of
87461dfa509SRick McNeal 	 * DMA buffers to simplify support of this feature.
87561dfa509SRick McNeal 	 */
87661dfa509SRick McNeal 	if (((op == SCMD_READ_G1) || (op == SCMD_READ_G4) ||
87761dfa509SRick McNeal 	    (op == SCMD_READ_G5)) &&
87861dfa509SRick McNeal 	    (task->task_cdb[1] & BIT_3)) {
87961dfa509SRick McNeal 		fua_bit = B_TRUE;
88061dfa509SRick McNeal 	}
881fcf3ce44SJohn Forte 	if (op == SCMD_READ) {
882fcf3ce44SJohn Forte 		lba = READ_SCSI21(&task->task_cdb[1], uint64_t);
883fcf3ce44SJohn Forte 		len = (uint32_t)task->task_cdb[4];
884fcf3ce44SJohn Forte 
885fcf3ce44SJohn Forte 		if (len == 0) {
886fcf3ce44SJohn Forte 			len = 256;
887fcf3ce44SJohn Forte 		}
888fcf3ce44SJohn Forte 	} else if (op == SCMD_READ_G1) {
889fcf3ce44SJohn Forte 		lba = READ_SCSI32(&task->task_cdb[2], uint64_t);
890fcf3ce44SJohn Forte 		len = READ_SCSI16(&task->task_cdb[7], uint32_t);
891fcf3ce44SJohn Forte 	} else if (op == SCMD_READ_G5) {
892fcf3ce44SJohn Forte 		lba = READ_SCSI32(&task->task_cdb[2], uint64_t);
893fcf3ce44SJohn Forte 		len = READ_SCSI32(&task->task_cdb[6], uint32_t);
894fcf3ce44SJohn Forte 	} else if (op == SCMD_READ_G4) {
895fcf3ce44SJohn Forte 		lba = READ_SCSI64(&task->task_cdb[2], uint64_t);
896fcf3ce44SJohn Forte 		len = READ_SCSI32(&task->task_cdb[10], uint32_t);
897fcf3ce44SJohn Forte 	} else {
898fcf3ce44SJohn Forte 		stmf_scsilib_send_status(task, STATUS_CHECK,
899fcf3ce44SJohn Forte 		    STMF_SAA_INVALID_OPCODE);
900fcf3ce44SJohn Forte 		return;
901fcf3ce44SJohn Forte 	}
902fcf3ce44SJohn Forte 
9038fe96085Stim szeto 	laddr = lba << sl->sl_data_blocksize_shift;
90461dfa509SRick McNeal 	blkcount = len;
9058fe96085Stim szeto 	len <<= sl->sl_data_blocksize_shift;
906fcf3ce44SJohn Forte 
9078fe96085Stim szeto 	if ((laddr + (uint64_t)len) > sl->sl_lu_size) {
908fcf3ce44SJohn Forte 		stmf_scsilib_send_status(task, STATUS_CHECK,
909fcf3ce44SJohn Forte 		    STMF_SAA_LBA_OUT_OF_RANGE);
910fcf3ce44SJohn Forte 		return;
911fcf3ce44SJohn Forte 	}
912fcf3ce44SJohn Forte 
913fcf3ce44SJohn Forte 	task->task_cmd_xfer_length = len;
914fcf3ce44SJohn Forte 	if (task->task_additional_flags & TASK_AF_NO_EXPECTED_XFER_LENGTH) {
915fcf3ce44SJohn Forte 		task->task_expected_xfer_length = len;
916fcf3ce44SJohn Forte 	}
917fcf3ce44SJohn Forte 
918fcf3ce44SJohn Forte 	if (len != task->task_expected_xfer_length) {
919fcf3ce44SJohn Forte 		fast_path = 0;
920fcf3ce44SJohn Forte 		len = (len > task->task_expected_xfer_length) ?
9211b7fc709Stim szeto 		    task->task_expected_xfer_length : len;
922fcf3ce44SJohn Forte 	} else {
923fcf3ce44SJohn Forte 		fast_path = 1;
924fcf3ce44SJohn Forte 	}
925fcf3ce44SJohn Forte 
926fcf3ce44SJohn Forte 	if (len == 0) {
927fcf3ce44SJohn Forte 		stmf_scsilib_send_status(task, STATUS_GOOD, 0);
928fcf3ce44SJohn Forte 		return;
929fcf3ce44SJohn Forte 	}
930fcf3ce44SJohn Forte 
93161dfa509SRick McNeal 	if (sbd_ats_handling_before_io(task, sl, lba, blkcount) !=
93261dfa509SRick McNeal 	    SBD_SUCCESS) {
93361dfa509SRick McNeal 		if (stmf_task_poll_lu(task, 10) != STMF_SUCCESS) {
93461dfa509SRick McNeal 			stmf_scsilib_send_status(task, STATUS_BUSY, 0);
93561dfa509SRick McNeal 		}
93661dfa509SRick McNeal 		return;
93761dfa509SRick McNeal 	}
9383fb517f7SJames Moore 	/*
9393fb517f7SJames Moore 	 * Determine if this read can directly use DMU buffers.
9403fb517f7SJames Moore 	 */
9413fb517f7SJames Moore 	if (sbd_zcopy & (2|1) &&		/* Debug switch */
9423fb517f7SJames Moore 	    initial_dbuf == NULL &&		/* No PP buffer passed in */
9433fb517f7SJames Moore 	    sl->sl_flags & SL_CALL_ZVOL &&	/* zvol backing store */
9443fb517f7SJames Moore 	    (task->task_additional_flags &
94561dfa509SRick McNeal 	    TASK_AF_ACCEPT_LU_DBUF) &&		/* PP allows it */
94661dfa509SRick McNeal 	    !fua_bit) {
9473fb517f7SJames Moore 		/*
9483fb517f7SJames Moore 		 * Reduced copy path
9493fb517f7SJames Moore 		 */
9503fb517f7SJames Moore 		uint32_t copy_threshold, minsize;
9513fb517f7SJames Moore 		int ret;
9523fb517f7SJames Moore 
9533fb517f7SJames Moore 		/*
9543fb517f7SJames Moore 		 * The sl_access_state_lock will be held shared
9553fb517f7SJames Moore 		 * for the entire request and released when all
9563fb517f7SJames Moore 		 * dbufs have completed.
9573fb517f7SJames Moore 		 */
9583fb517f7SJames Moore 		rw_enter(&sl->sl_access_state_lock, RW_READER);
9593fb517f7SJames Moore 		if ((sl->sl_flags & SL_MEDIA_LOADED) == 0) {
9603fb517f7SJames Moore 			rw_exit(&sl->sl_access_state_lock);
96161dfa509SRick McNeal 			sbd_ats_remove_by_task(task);
9623fb517f7SJames Moore 			stmf_scsilib_send_status(task, STATUS_CHECK,
9633fb517f7SJames Moore 			    STMF_SAA_READ_ERROR);
9643fb517f7SJames Moore 			return;
9653fb517f7SJames Moore 		}
9663fb517f7SJames Moore 
9673fb517f7SJames Moore 		/*
9683fb517f7SJames Moore 		 * Check if setup is more expensive than copying the data.
9693fb517f7SJames Moore 		 *
9703fb517f7SJames Moore 		 * Use the global over-ride sbd_zcopy_threshold if set.
9713fb517f7SJames Moore 		 */
9723fb517f7SJames Moore 		copy_threshold = (sbd_copy_threshold > 0) ?
9733fb517f7SJames Moore 		    sbd_copy_threshold : task->task_copy_threshold;
9743fb517f7SJames Moore 		minsize = len;
9753fb517f7SJames Moore 		if (len < copy_threshold &&
9763fb517f7SJames Moore 		    (dbuf = stmf_alloc_dbuf(task, len, &minsize, 0)) != 0) {
9773fb517f7SJames Moore 
9783fb517f7SJames Moore 			ret = sbd_copy_rdwr(task, laddr, dbuf,
9793fb517f7SJames Moore 			    SBD_CMD_SCSI_READ, 0);
9803fb517f7SJames Moore 			/* done with the backend */
9813fb517f7SJames Moore 			rw_exit(&sl->sl_access_state_lock);
98261dfa509SRick McNeal 			sbd_ats_remove_by_task(task);
9833fb517f7SJames Moore 			if (ret != 0) {
9843fb517f7SJames Moore 				/* backend error */
9853fb517f7SJames Moore 				stmf_scsilib_send_status(task, STATUS_CHECK,
9863fb517f7SJames Moore 				    STMF_SAA_READ_ERROR);
9873fb517f7SJames Moore 			} else {
9883fb517f7SJames Moore 				/* send along good data */
9893fb517f7SJames Moore 				dbuf->db_relative_offset = 0;
9903fb517f7SJames Moore 				dbuf->db_data_size = len;
9913fb517f7SJames Moore 				dbuf->db_flags = DB_SEND_STATUS_GOOD |
9923fb517f7SJames Moore 				    DB_DIRECTION_TO_RPORT;
9933fb517f7SJames Moore 				/* XXX keep for FW? */
9943fb517f7SJames Moore 				DTRACE_PROBE4(sbd__xfer,
9953fb517f7SJames Moore 				    struct scsi_task *, task,
9963fb517f7SJames Moore 				    struct stmf_data_buf *, dbuf,
9973fb517f7SJames Moore 				    uint64_t, laddr, uint32_t, len);
9983fb517f7SJames Moore 				(void) stmf_xfer_data(task, dbuf,
9993fb517f7SJames Moore 				    STMF_IOF_LU_DONE);
10003fb517f7SJames Moore 			}
10013fb517f7SJames Moore 			return;
10023fb517f7SJames Moore 		}
10033fb517f7SJames Moore 
10043fb517f7SJames Moore 		/* committed to reduced copy */
10053fb517f7SJames Moore 		if (task->task_lu_private) {
10063fb517f7SJames Moore 			scmd = (sbd_cmd_t *)task->task_lu_private;
10073fb517f7SJames Moore 		} else {
10083fb517f7SJames Moore 			scmd = (sbd_cmd_t *)kmem_alloc(sizeof (sbd_cmd_t),
10093fb517f7SJames Moore 			    KM_SLEEP);
10103fb517f7SJames Moore 			task->task_lu_private = scmd;
10113fb517f7SJames Moore 		}
10123fb517f7SJames Moore 		/*
10133fb517f7SJames Moore 		 * Setup scmd to track read progress.
10143fb517f7SJames Moore 		 */
101561dfa509SRick McNeal 		scmd->flags = SBD_SCSI_CMD_ACTIVE | SBD_SCSI_CMD_ATS_RELATED;
10163fb517f7SJames Moore 		scmd->cmd_type = SBD_CMD_SCSI_READ;
10173fb517f7SJames Moore 		scmd->nbufs = 0;
10183fb517f7SJames Moore 		scmd->addr = laddr;
10193fb517f7SJames Moore 		scmd->len = len;
10203fb517f7SJames Moore 		scmd->current_ro = 0;
10213fb517f7SJames Moore 		/*
10223fb517f7SJames Moore 		 * Kick-off the read.
10233fb517f7SJames Moore 		 */
10243fb517f7SJames Moore 		sbd_do_sgl_read_xfer(task, scmd, 1);
10253fb517f7SJames Moore 		return;
10263fb517f7SJames Moore 	}
10273fb517f7SJames Moore 
1028fcf3ce44SJohn Forte 	if (initial_dbuf == NULL) {
1029fcf3ce44SJohn Forte 		uint32_t maxsize, minsize, old_minsize;
1030fcf3ce44SJohn Forte 
1031fcf3ce44SJohn Forte 		maxsize = (len > (128*1024)) ? 128*1024 : len;
1032fcf3ce44SJohn Forte 		minsize = maxsize >> 2;
1033fcf3ce44SJohn Forte 		do {
1034fcf3ce44SJohn Forte 			old_minsize = minsize;
1035fcf3ce44SJohn Forte 			initial_dbuf = stmf_alloc_dbuf(task, maxsize,
1036fcf3ce44SJohn Forte 			    &minsize, 0);
1037fcf3ce44SJohn Forte 		} while ((initial_dbuf == NULL) && (old_minsize > minsize) &&
1038fcf3ce44SJohn Forte 		    (minsize >= 512));
1039fcf3ce44SJohn Forte 		if (initial_dbuf == NULL) {
104061dfa509SRick McNeal 			sbd_ats_remove_by_task(task);
10411b7fc709Stim szeto 			stmf_scsilib_send_status(task, STATUS_QFULL, 0);
1042fcf3ce44SJohn Forte 			return;
1043fcf3ce44SJohn Forte 		}
1044fcf3ce44SJohn Forte 	}
1045fcf3ce44SJohn Forte 	dbuf = initial_dbuf;
1046fcf3ce44SJohn Forte 
1047fcf3ce44SJohn Forte 	if ((dbuf->db_buf_size >= len) && fast_path &&
1048fcf3ce44SJohn Forte 	    (dbuf->db_sglist_length == 1)) {
1049427fcaf8Stim szeto 		if (sbd_data_read(sl, task, laddr, (uint64_t)len,
1050fcf3ce44SJohn Forte 		    dbuf->db_sglist[0].seg_addr) == STMF_SUCCESS) {
1051fcf3ce44SJohn Forte 			dbuf->db_relative_offset = 0;
1052fcf3ce44SJohn Forte 			dbuf->db_data_size = len;
1053fcf3ce44SJohn Forte 			dbuf->db_flags = DB_SEND_STATUS_GOOD |
1054fcf3ce44SJohn Forte 			    DB_DIRECTION_TO_RPORT;
10553fb517f7SJames Moore 			/* XXX keep for FW? */
10563fb517f7SJames Moore 			DTRACE_PROBE4(sbd__xfer, struct scsi_task *, task,
10573fb517f7SJames Moore 			    struct stmf_data_buf *, dbuf,
10583fb517f7SJames Moore 			    uint64_t, laddr, uint32_t, len);
1059fcf3ce44SJohn Forte 			(void) stmf_xfer_data(task, dbuf, STMF_IOF_LU_DONE);
1060fcf3ce44SJohn Forte 		} else {
1061fcf3ce44SJohn Forte 			stmf_scsilib_send_status(task, STATUS_CHECK,
1062fcf3ce44SJohn Forte 			    STMF_SAA_READ_ERROR);
1063fcf3ce44SJohn Forte 		}
106461dfa509SRick McNeal 		sbd_ats_remove_by_task(task);
1065fcf3ce44SJohn Forte 		return;
1066fcf3ce44SJohn Forte 	}
1067fcf3ce44SJohn Forte 
1068fcf3ce44SJohn Forte 	if (task->task_lu_private) {
1069fcf3ce44SJohn Forte 		scmd = (sbd_cmd_t *)task->task_lu_private;
1070fcf3ce44SJohn Forte 	} else {
1071fcf3ce44SJohn Forte 		scmd = (sbd_cmd_t *)kmem_alloc(sizeof (sbd_cmd_t), KM_SLEEP);
1072fcf3ce44SJohn Forte 		task->task_lu_private = scmd;
1073fcf3ce44SJohn Forte 	}
107461dfa509SRick McNeal 	scmd->flags = SBD_SCSI_CMD_ACTIVE | SBD_SCSI_CMD_ATS_RELATED;
1075fcf3ce44SJohn Forte 	scmd->cmd_type = SBD_CMD_SCSI_READ;
1076fcf3ce44SJohn Forte 	scmd->nbufs = 1;
1077fcf3ce44SJohn Forte 	scmd->addr = laddr;
1078fcf3ce44SJohn Forte 	scmd->len = len;
1079fcf3ce44SJohn Forte 	scmd->current_ro = 0;
1080fcf3ce44SJohn Forte 
1081fcf3ce44SJohn Forte 	sbd_do_read_xfer(task, scmd, dbuf);
1082fcf3ce44SJohn Forte }
1083fcf3ce44SJohn Forte 
1084fcf3ce44SJohn Forte void
sbd_do_write_xfer(struct scsi_task * task,sbd_cmd_t * scmd,struct stmf_data_buf * dbuf,uint8_t dbuf_reusable)1085fcf3ce44SJohn Forte sbd_do_write_xfer(struct scsi_task *task, sbd_cmd_t *scmd,
108679a8ba49SCharles Ting     struct stmf_data_buf *dbuf, uint8_t dbuf_reusable)
1087fcf3ce44SJohn Forte {
1088fcf3ce44SJohn Forte 	uint32_t len;
1089fcf3ce44SJohn Forte 	int bufs_to_take;
1090fcf3ce44SJohn Forte 
109161dfa509SRick McNeal 	if (ATOMIC32_GET(scmd->len) == 0) {
109279a8ba49SCharles Ting 		goto DO_WRITE_XFER_DONE;
109379a8ba49SCharles Ting 	}
109479a8ba49SCharles Ting 
1095fcf3ce44SJohn Forte 	/* Lets try not to hog all the buffers the port has. */
1096fcf3ce44SJohn Forte 	bufs_to_take = ((task->task_max_nbufs > 2) &&
1097fcf3ce44SJohn Forte 	    (task->task_cmd_xfer_length < (32 * 1024))) ? 2 :
1098fcf3ce44SJohn Forte 	    task->task_max_nbufs;
1099fcf3ce44SJohn Forte 
110079a8ba49SCharles Ting 	if ((dbuf != NULL) &&
110179a8ba49SCharles Ting 	    ((dbuf->db_flags & DB_DONT_REUSE) || (dbuf_reusable == 0))) {
110279a8ba49SCharles Ting 		/* free current dbuf and allocate a new one */
110379a8ba49SCharles Ting 		stmf_free_dbuf(task, dbuf);
110479a8ba49SCharles Ting 		dbuf = NULL;
110579a8ba49SCharles Ting 	}
110661dfa509SRick McNeal 	if (ATOMIC8_GET(scmd->nbufs) >= bufs_to_take) {
110779a8ba49SCharles Ting 		goto DO_WRITE_XFER_DONE;
110879a8ba49SCharles Ting 	}
110979a8ba49SCharles Ting 	if (dbuf == NULL) {
1110fcf3ce44SJohn Forte 		uint32_t maxsize, minsize, old_minsize;
1111fcf3ce44SJohn Forte 
111261dfa509SRick McNeal 		maxsize = (ATOMIC32_GET(scmd->len) > (128*1024)) ? 128*1024 :
111361dfa509SRick McNeal 		    ATOMIC32_GET(scmd->len);
1114fcf3ce44SJohn Forte 		minsize = maxsize >> 2;
1115fcf3ce44SJohn Forte 		do {
1116fcf3ce44SJohn Forte 			old_minsize = minsize;
1117fcf3ce44SJohn Forte 			dbuf = stmf_alloc_dbuf(task, maxsize, &minsize, 0);
1118fcf3ce44SJohn Forte 		} while ((dbuf == NULL) && (old_minsize > minsize) &&
1119fcf3ce44SJohn Forte 		    (minsize >= 512));
1120fcf3ce44SJohn Forte 		if (dbuf == NULL) {
112161dfa509SRick McNeal 			if (ATOMIC8_GET(scmd->nbufs) == 0) {
112279a8ba49SCharles Ting 				stmf_abort(STMF_QUEUE_TASK_ABORT, task,
112379a8ba49SCharles Ting 				    STMF_ALLOC_FAILURE, NULL);
112479a8ba49SCharles Ting 			}
1125fcf3ce44SJohn Forte 			return;
1126fcf3ce44SJohn Forte 		}
112779a8ba49SCharles Ting 	}
112879a8ba49SCharles Ting 
112961dfa509SRick McNeal 	len = ATOMIC32_GET(scmd->len) > dbuf->db_buf_size ? dbuf->db_buf_size :
113061dfa509SRick McNeal 	    ATOMIC32_GET(scmd->len);
113179a8ba49SCharles Ting 
113279a8ba49SCharles Ting 	dbuf->db_relative_offset = scmd->current_ro;
113379a8ba49SCharles Ting 	dbuf->db_data_size = len;
113479a8ba49SCharles Ting 	dbuf->db_flags = DB_DIRECTION_FROM_RPORT;
113579a8ba49SCharles Ting 	(void) stmf_xfer_data(task, dbuf, 0);
113661dfa509SRick McNeal 	/* outstanding port xfers and bufs used */
113761dfa509SRick McNeal 	atomic_inc_8(&scmd->nbufs);
113861dfa509SRick McNeal 	atomic_add_32(&scmd->len, -len);
113979a8ba49SCharles Ting 	scmd->current_ro += len;
114079a8ba49SCharles Ting 
114161dfa509SRick McNeal 	if ((ATOMIC32_GET(scmd->len) != 0) &&
114261dfa509SRick McNeal 	    (ATOMIC8_GET(scmd->nbufs) < bufs_to_take)) {
114379a8ba49SCharles Ting 		sbd_do_write_xfer(task, scmd, NULL, 0);
114479a8ba49SCharles Ting 	}
114579a8ba49SCharles Ting 	return;
114679a8ba49SCharles Ting 
114779a8ba49SCharles Ting DO_WRITE_XFER_DONE:
114879a8ba49SCharles Ting 	if (dbuf != NULL) {
114979a8ba49SCharles Ting 		stmf_free_dbuf(task, dbuf);
1150fcf3ce44SJohn Forte 	}
1151fcf3ce44SJohn Forte }
1152fcf3ce44SJohn Forte 
11533fb517f7SJames Moore void
sbd_do_sgl_write_xfer(struct scsi_task * task,sbd_cmd_t * scmd,int first_xfer)11543fb517f7SJames Moore sbd_do_sgl_write_xfer(struct scsi_task *task, sbd_cmd_t *scmd, int first_xfer)
11553fb517f7SJames Moore {
11563fb517f7SJames Moore 	sbd_lu_t *sl = (sbd_lu_t *)task->task_lu->lu_provider_private;
11573fb517f7SJames Moore 	sbd_zvol_io_t *zvio;
11583fb517f7SJames Moore 	int ret;
11593fb517f7SJames Moore 	uint32_t xfer_len, max_len, first_len;
11603fb517f7SJames Moore 	stmf_status_t xstat;
11613fb517f7SJames Moore 	stmf_data_buf_t *dbuf;
11623fb517f7SJames Moore 	uint_t nblks;
11633fb517f7SJames Moore 	uint64_t blksize = sl->sl_blksize;
11643fb517f7SJames Moore 	uint64_t offset;
11653fb517f7SJames Moore 	size_t db_private_sz;
11663fb517f7SJames Moore 	uintptr_t pad;
11673fb517f7SJames Moore 
11683fb517f7SJames Moore 	ASSERT(rw_read_held(&sl->sl_access_state_lock));
11693fb517f7SJames Moore 	ASSERT((sl->sl_flags & SL_MEDIA_LOADED) != 0);
11703fb517f7SJames Moore 
11713fb517f7SJames Moore 	/*
11723fb517f7SJames Moore 	 * Calculate the limits on xfer_len to the minimum of :
11733fb517f7SJames Moore 	 *    - task limit
11743fb517f7SJames Moore 	 *    - lun limit
11753fb517f7SJames Moore 	 *    - sbd global limit if set
11763fb517f7SJames Moore 	 *    - first xfer limit if set
11773fb517f7SJames Moore 	 *
11783fb517f7SJames Moore 	 * First, protect against silly over-ride value
11793fb517f7SJames Moore 	 */
11803fb517f7SJames Moore 	if (sbd_max_xfer_len && ((sbd_max_xfer_len % DEV_BSIZE) != 0)) {
11813fb517f7SJames Moore 		cmn_err(CE_WARN, "sbd_max_xfer_len invalid %d, resetting\n",
11823fb517f7SJames Moore 		    sbd_max_xfer_len);
11833fb517f7SJames Moore 		sbd_max_xfer_len = 0;
11843fb517f7SJames Moore 	}
11853fb517f7SJames Moore 	if (sbd_1st_xfer_len && ((sbd_1st_xfer_len % DEV_BSIZE) != 0)) {
11863fb517f7SJames Moore 		cmn_err(CE_WARN, "sbd_1st_xfer_len invalid %d, resetting\n",
11873fb517f7SJames Moore 		    sbd_1st_xfer_len);
11883fb517f7SJames Moore 		sbd_1st_xfer_len = 0;
11893fb517f7SJames Moore 	}
11903fb517f7SJames Moore 
11913fb517f7SJames Moore 	max_len = MIN(task->task_max_xfer_len, sl->sl_max_xfer_len);
11923fb517f7SJames Moore 	if (sbd_max_xfer_len)
11933fb517f7SJames Moore 		max_len = MIN(max_len, sbd_max_xfer_len);
11943fb517f7SJames Moore 	/*
11953fb517f7SJames Moore 	 * Special case the first xfer if hints are set.
11963fb517f7SJames Moore 	 */
11973fb517f7SJames Moore 	if (first_xfer && (sbd_1st_xfer_len || task->task_1st_xfer_len)) {
11983fb517f7SJames Moore 		/* global over-ride has precedence */
11993fb517f7SJames Moore 		if (sbd_1st_xfer_len)
12003fb517f7SJames Moore 			first_len = sbd_1st_xfer_len;
12013fb517f7SJames Moore 		else
12023fb517f7SJames Moore 			first_len = task->task_1st_xfer_len;
12033fb517f7SJames Moore 	} else {
12043fb517f7SJames Moore 		first_len = 0;
12053fb517f7SJames Moore 	}
12063fb517f7SJames Moore 
12073fb517f7SJames Moore 
120861dfa509SRick McNeal 	while (ATOMIC32_GET(scmd->len) &&
120961dfa509SRick McNeal 	    ATOMIC8_GET(scmd->nbufs) < task->task_max_nbufs) {
121061dfa509SRick McNeal 		xfer_len = MIN(max_len, ATOMIC32_GET(scmd->len));
12113fb517f7SJames Moore 		if (first_len) {
12123fb517f7SJames Moore 			xfer_len = MIN(xfer_len, first_len);
12133fb517f7SJames Moore 			first_len = 0;
12143fb517f7SJames Moore 		}
121561dfa509SRick McNeal 		if (xfer_len < ATOMIC32_GET(scmd->len)) {
12163fb517f7SJames Moore 			/*
12173fb517f7SJames Moore 			 * Attempt to end xfer on a block boundary.
12183fb517f7SJames Moore 			 * The only way this does not happen is if the
12193fb517f7SJames Moore 			 * xfer_len is small enough to stay contained
12203fb517f7SJames Moore 			 * within the same block.
12213fb517f7SJames Moore 			 */
12223fb517f7SJames Moore 			uint64_t xfer_offset, xfer_aligned_end;
12233fb517f7SJames Moore 
12243fb517f7SJames Moore 			xfer_offset = scmd->addr + scmd->current_ro;
12253fb517f7SJames Moore 			xfer_aligned_end =
12263fb517f7SJames Moore 			    P2ALIGN(xfer_offset+xfer_len, blksize);
12273fb517f7SJames Moore 			if (xfer_aligned_end > xfer_offset)
12283fb517f7SJames Moore 				xfer_len = xfer_aligned_end - xfer_offset;
12293fb517f7SJames Moore 		}
12303fb517f7SJames Moore 		/*
12313fb517f7SJames Moore 		 * Allocate object to track the write and reserve
12323fb517f7SJames Moore 		 * enough space for scatter/gather list.
12333fb517f7SJames Moore 		 */
12343fb517f7SJames Moore 		offset = scmd->addr + scmd->current_ro;
12353fb517f7SJames Moore 		nblks = sbd_zvol_numsegs(sl, offset, xfer_len);
12363fb517f7SJames Moore 		db_private_sz = sizeof (*zvio) + sizeof (uintptr_t) /* PAD */ +
12373fb517f7SJames Moore 		    (nblks * sizeof (stmf_sglist_ent_t));
12383fb517f7SJames Moore 		dbuf = stmf_alloc(STMF_STRUCT_DATA_BUF, db_private_sz,
12393fb517f7SJames Moore 		    AF_DONTZERO);
12403fb517f7SJames Moore 
12413fb517f7SJames Moore 		/*
12423fb517f7SJames Moore 		 * Setup the dbuf
12433fb517f7SJames Moore 		 *
12443fb517f7SJames Moore 		 * XXX Framework does not handle variable length sglists
12453fb517f7SJames Moore 		 * properly, so setup db_lu_private and db_port_private
12463fb517f7SJames Moore 		 * fields here. db_stmf_private is properly set for
12473fb517f7SJames Moore 		 * calls to stmf_free.
12483fb517f7SJames Moore 		 */
12493fb517f7SJames Moore 		if (dbuf->db_port_private == NULL) {
12503fb517f7SJames Moore 			/*
12513fb517f7SJames Moore 			 * XXX Framework assigns space to PP after db_sglist[0]
12523fb517f7SJames Moore 			 */
12533fb517f7SJames Moore 			cmn_err(CE_PANIC, "db_port_private == NULL");
12543fb517f7SJames Moore 		}
12553fb517f7SJames Moore 		pad = (uintptr_t)&dbuf->db_sglist[nblks];
12563fb517f7SJames Moore 		dbuf->db_lu_private = (void *)P2ROUNDUP(pad, sizeof (pad));
12573fb517f7SJames Moore 		dbuf->db_port_private = NULL;
12583fb517f7SJames Moore 		dbuf->db_buf_size = xfer_len;
12593fb517f7SJames Moore 		dbuf->db_data_size = xfer_len;
12603fb517f7SJames Moore 		dbuf->db_relative_offset = scmd->current_ro;
12613fb517f7SJames Moore 		dbuf->db_sglist_length = (uint16_t)nblks;
12623fb517f7SJames Moore 		dbuf->db_xfer_status = 0;
12633fb517f7SJames Moore 		dbuf->db_handle = 0;
12643fb517f7SJames Moore 		dbuf->db_flags = (DB_DONT_CACHE | DB_DONT_REUSE |
12653fb517f7SJames Moore 		    DB_DIRECTION_FROM_RPORT | DB_LU_DATA_BUF);
12663fb517f7SJames Moore 
12673fb517f7SJames Moore 		zvio = dbuf->db_lu_private;
12683fb517f7SJames Moore 		zvio->zvio_offset = offset;
12693fb517f7SJames Moore 
12703fb517f7SJames Moore 		/* get the buffers */
12713fb517f7SJames Moore 		ret = sbd_zvol_alloc_write_bufs(sl, dbuf);
12723fb517f7SJames Moore 		if (ret != 0) {
12733fb517f7SJames Moore 			/*
12743fb517f7SJames Moore 			 * Could not allocate buffers from the backend;
12753fb517f7SJames Moore 			 * treat it like an IO error.
12763fb517f7SJames Moore 			 */
12773fb517f7SJames Moore 			stmf_free(dbuf);
12783fb517f7SJames Moore 			scmd->flags |= SBD_SCSI_CMD_XFER_FAIL;
127961dfa509SRick McNeal 			if (ATOMIC8_GET(scmd->nbufs) == 0) {
12803fb517f7SJames Moore 				/*
12813fb517f7SJames Moore 				 * Nothing queued, so no completions coming
12823fb517f7SJames Moore 				 */
128361dfa509SRick McNeal 				sbd_ats_remove_by_task(task);
12843fb517f7SJames Moore 				stmf_scsilib_send_status(task, STATUS_CHECK,
12853fb517f7SJames Moore 				    STMF_SAA_WRITE_ERROR);
12863fb517f7SJames Moore 				rw_exit(&sl->sl_access_state_lock);
12873fb517f7SJames Moore 			}
12883fb517f7SJames Moore 			/*
12893fb517f7SJames Moore 			 * Completions of previous buffers will cleanup.
12903fb517f7SJames Moore 			 */
12913fb517f7SJames Moore 			return;
12923fb517f7SJames Moore 		}
12933fb517f7SJames Moore 
12943fb517f7SJames Moore 		/*
12953fb517f7SJames Moore 		 * Allow PP to do setup
12963fb517f7SJames Moore 		 */
12973fb517f7SJames Moore 		xstat = stmf_setup_dbuf(task, dbuf, 0);
12983fb517f7SJames Moore 		if (xstat != STMF_SUCCESS) {
12993fb517f7SJames Moore 			/*
13003fb517f7SJames Moore 			 * This could happen if the driver cannot get the
13013fb517f7SJames Moore 			 * DDI resources it needs for this request.
13023fb517f7SJames Moore 			 * If other dbufs are queued, try again when the next
13033fb517f7SJames Moore 			 * one completes, otherwise give up.
13043fb517f7SJames Moore 			 */
13053fb517f7SJames Moore 			sbd_zvol_rele_write_bufs_abort(sl, dbuf);
13063fb517f7SJames Moore 			stmf_free(dbuf);
130761dfa509SRick McNeal 			if (ATOMIC8_GET(scmd->nbufs) > 0) {
13083fb517f7SJames Moore 				/* completion of previous dbuf will retry */
13093fb517f7SJames Moore 				return;
13103fb517f7SJames Moore 			}
13113fb517f7SJames Moore 			/*
13123fb517f7SJames Moore 			 * Done with this command.
13133fb517f7SJames Moore 			 */
13143fb517f7SJames Moore 			scmd->flags &= ~SBD_SCSI_CMD_ACTIVE;
131561dfa509SRick McNeal 			sbd_ats_remove_by_task(task);
13163fb517f7SJames Moore 			if (first_xfer)
13173fb517f7SJames Moore 				stmf_scsilib_send_status(task, STATUS_QFULL, 0);
13183fb517f7SJames Moore 			else
13193fb517f7SJames Moore 				stmf_scsilib_send_status(task, STATUS_CHECK,
13203fb517f7SJames Moore 				    STMF_SAA_WRITE_ERROR);
13213fb517f7SJames Moore 			rw_exit(&sl->sl_access_state_lock);
13223fb517f7SJames Moore 			return;
13233fb517f7SJames Moore 		}
13243fb517f7SJames Moore 
13253fb517f7SJames Moore 		/*
13263fb517f7SJames Moore 		 * dbuf is now queued on task
13273fb517f7SJames Moore 		 */
132861dfa509SRick McNeal 		atomic_inc_8(&scmd->nbufs);
13293fb517f7SJames Moore 
13303fb517f7SJames Moore 		xstat = stmf_xfer_data(task, dbuf, 0);
13313fb517f7SJames Moore 		switch (xstat) {
13323fb517f7SJames Moore 		case STMF_SUCCESS:
13333fb517f7SJames Moore 			break;
13343fb517f7SJames Moore 		case STMF_BUSY:
13353fb517f7SJames Moore 			/*
13363fb517f7SJames Moore 			 * The dbuf is queued on the task, but unknown
13373fb517f7SJames Moore 			 * to the PP, thus no completion will occur.
13383fb517f7SJames Moore 			 */
13393fb517f7SJames Moore 			sbd_zvol_rele_write_bufs_abort(sl, dbuf);
13403fb517f7SJames Moore 			stmf_teardown_dbuf(task, dbuf);
13413fb517f7SJames Moore 			stmf_free(dbuf);
134261dfa509SRick McNeal 			atomic_dec_8(&scmd->nbufs);
134361dfa509SRick McNeal 			if (ATOMIC8_GET(scmd->nbufs) > 0) {
13443fb517f7SJames Moore 				/* completion of previous dbuf will retry */
13453fb517f7SJames Moore 				return;
13463fb517f7SJames Moore 			}
13473fb517f7SJames Moore 			/*
13483fb517f7SJames Moore 			 * Done with this command.
13493fb517f7SJames Moore 			 */
13503fb517f7SJames Moore 			scmd->flags &= ~SBD_SCSI_CMD_ACTIVE;
135161dfa509SRick McNeal 			sbd_ats_remove_by_task(task);
13523fb517f7SJames Moore 			if (first_xfer)
13533fb517f7SJames Moore 				stmf_scsilib_send_status(task, STATUS_QFULL, 0);
13543fb517f7SJames Moore 			else
13553fb517f7SJames Moore 				stmf_scsilib_send_status(task, STATUS_CHECK,
13563fb517f7SJames Moore 				    STMF_SAA_WRITE_ERROR);
13573fb517f7SJames Moore 			rw_exit(&sl->sl_access_state_lock);
13583fb517f7SJames Moore 			return;
13593fb517f7SJames Moore 		case STMF_ABORTED:
13603fb517f7SJames Moore 			/*
13613fb517f7SJames Moore 			 * Completion code will cleanup.
13623fb517f7SJames Moore 			 */
13633fb517f7SJames Moore 			scmd->flags |= SBD_SCSI_CMD_XFER_FAIL;
13643fb517f7SJames Moore 			return;
13653fb517f7SJames Moore 		}
13663fb517f7SJames Moore 		/*
13673fb517f7SJames Moore 		 * Update the xfer progress.
13683fb517f7SJames Moore 		 */
136961dfa509SRick McNeal 		atomic_add_32(&scmd->len, -xfer_len);
13703fb517f7SJames Moore 		scmd->current_ro += xfer_len;
13713fb517f7SJames Moore 	}
13723fb517f7SJames Moore }
13733fb517f7SJames Moore 
1374fcf3ce44SJohn Forte void
sbd_handle_write_xfer_completion(struct scsi_task * task,sbd_cmd_t * scmd,struct stmf_data_buf * dbuf,uint8_t dbuf_reusable)1375fcf3ce44SJohn Forte sbd_handle_write_xfer_completion(struct scsi_task *task, sbd_cmd_t *scmd,
1376fcf3ce44SJohn Forte     struct stmf_data_buf *dbuf, uint8_t dbuf_reusable)
1377fcf3ce44SJohn Forte {
13788fe96085Stim szeto 	sbd_lu_t *sl = (sbd_lu_t *)task->task_lu->lu_provider_private;
1379fcf3ce44SJohn Forte 	uint64_t laddr;
1380fcf3ce44SJohn Forte 	uint32_t buflen, iolen;
1381fcf3ce44SJohn Forte 	int ndx;
138261dfa509SRick McNeal 	uint8_t op = task->task_cdb[0];
138361dfa509SRick McNeal 	boolean_t fua_bit = B_FALSE;
1384fcf3ce44SJohn Forte 
138561dfa509SRick McNeal 	if (ATOMIC8_GET(scmd->nbufs) > 0) {
138679a8ba49SCharles Ting 		/*
138779a8ba49SCharles Ting 		 * Decrement the count to indicate the port xfer
138879a8ba49SCharles Ting 		 * into the dbuf has completed even though the buf is
138979a8ba49SCharles Ting 		 * still in use here in the LU provider.
139079a8ba49SCharles Ting 		 */
139161dfa509SRick McNeal 		atomic_dec_8(&scmd->nbufs);
139279a8ba49SCharles Ting 	}
139379a8ba49SCharles Ting 
1394fcf3ce44SJohn Forte 	if (dbuf->db_xfer_status != STMF_SUCCESS) {
139561dfa509SRick McNeal 		sbd_ats_remove_by_task(task);
1396fcf3ce44SJohn Forte 		stmf_abort(STMF_QUEUE_TASK_ABORT, task,
1397fcf3ce44SJohn Forte 		    dbuf->db_xfer_status, NULL);
1398fcf3ce44SJohn Forte 		return;
1399fcf3ce44SJohn Forte 	}
1400fcf3ce44SJohn Forte 
1401fcf3ce44SJohn Forte 	if (scmd->flags & SBD_SCSI_CMD_XFER_FAIL) {
1402fcf3ce44SJohn Forte 		goto WRITE_XFER_DONE;
1403fcf3ce44SJohn Forte 	}
1404fcf3ce44SJohn Forte 
140561dfa509SRick McNeal 	if (ATOMIC32_GET(scmd->len) != 0) {
140679a8ba49SCharles Ting 		/*
140779a8ba49SCharles Ting 		 * Initiate the next port xfer to occur in parallel
140879a8ba49SCharles Ting 		 * with writing this buf.
140979a8ba49SCharles Ting 		 */
141079a8ba49SCharles Ting 		sbd_do_write_xfer(task, scmd, NULL, 0);
141179a8ba49SCharles Ting 	}
141279a8ba49SCharles Ting 
141361dfa509SRick McNeal 	/*
141461dfa509SRick McNeal 	 * Check to see if the command is WRITE(10), WRITE(12), or WRITE(16).
141561dfa509SRick McNeal 	 * If it is then check for bit 3 being set to indicate if Forced
141661dfa509SRick McNeal 	 * Unit Access is being requested. If so, we'll bypass the direct
141761dfa509SRick McNeal 	 * call and handle it in sbd_data_write().
141861dfa509SRick McNeal 	 */
141961dfa509SRick McNeal 	if (((op == SCMD_WRITE_G1) || (op == SCMD_WRITE_G4) ||
142061dfa509SRick McNeal 	    (op == SCMD_WRITE_G5)) && (task->task_cdb[1] & BIT_3)) {
142161dfa509SRick McNeal 		fua_bit = B_TRUE;
142261dfa509SRick McNeal 	}
14238fe96085Stim szeto 	laddr = scmd->addr + dbuf->db_relative_offset;
1424fcf3ce44SJohn Forte 
14253fb517f7SJames Moore 	/*
14263fb517f7SJames Moore 	 * If this is going to a zvol, use the direct call to
14273fb517f7SJames Moore 	 * sbd_zvol_copy_{read,write}. The direct call interface is
14283fb517f7SJames Moore 	 * restricted to PPs that accept sglists, but that is not required.
14293fb517f7SJames Moore 	 */
14303fb517f7SJames Moore 	if (sl->sl_flags & SL_CALL_ZVOL &&
14313fb517f7SJames Moore 	    (task->task_additional_flags & TASK_AF_ACCEPT_LU_DBUF) &&
143261dfa509SRick McNeal 	    (sbd_zcopy & (4|1)) && !fua_bit) {
14333fb517f7SJames Moore 		int commit;
14343fb517f7SJames Moore 
143561dfa509SRick McNeal 		commit = (ATOMIC32_GET(scmd->len) == 0 &&
143661dfa509SRick McNeal 		    ATOMIC8_GET(scmd->nbufs) == 0);
143761dfa509SRick McNeal 		rw_enter(&sl->sl_access_state_lock, RW_READER);
143861dfa509SRick McNeal 		if ((sl->sl_flags & SL_MEDIA_LOADED) == 0 ||
143961dfa509SRick McNeal 		    sbd_copy_rdwr(task, laddr, dbuf, SBD_CMD_SCSI_WRITE,
14403fb517f7SJames Moore 		    commit) != STMF_SUCCESS)
1441fcf3ce44SJohn Forte 			scmd->flags |= SBD_SCSI_CMD_XFER_FAIL;
144261dfa509SRick McNeal 		rw_exit(&sl->sl_access_state_lock);
14433fb517f7SJames Moore 		buflen = dbuf->db_data_size;
14443fb517f7SJames Moore 	} else {
14453fb517f7SJames Moore 		for (buflen = 0, ndx = 0; (buflen < dbuf->db_data_size) &&
14463fb517f7SJames Moore 		    (ndx < dbuf->db_sglist_length); ndx++) {
14473fb517f7SJames Moore 			iolen = min(dbuf->db_data_size - buflen,
14483fb517f7SJames Moore 			    dbuf->db_sglist[ndx].seg_length);
14493fb517f7SJames Moore 			if (iolen == 0)
14503fb517f7SJames Moore 				break;
14513fb517f7SJames Moore 			if (sbd_data_write(sl, task, laddr, (uint64_t)iolen,
14523fb517f7SJames Moore 			    dbuf->db_sglist[ndx].seg_addr) != STMF_SUCCESS) {
14533fb517f7SJames Moore 				scmd->flags |= SBD_SCSI_CMD_XFER_FAIL;
14543fb517f7SJames Moore 				break;
14553fb517f7SJames Moore 			}
14563fb517f7SJames Moore 			buflen += iolen;
14573fb517f7SJames Moore 			laddr += (uint64_t)iolen;
1458fcf3ce44SJohn Forte 		}
1459fcf3ce44SJohn Forte 	}
1460fcf3ce44SJohn Forte 	task->task_nbytes_transferred += buflen;
1461fcf3ce44SJohn Forte WRITE_XFER_DONE:
146261dfa509SRick McNeal 	if (ATOMIC32_GET(scmd->len) == 0 ||
146361dfa509SRick McNeal 	    scmd->flags & SBD_SCSI_CMD_XFER_FAIL) {
1464fcf3ce44SJohn Forte 		stmf_free_dbuf(task, dbuf);
146561dfa509SRick McNeal 		if (ATOMIC8_GET(scmd->nbufs))
1466fcf3ce44SJohn Forte 			return;	/* wait for all buffers to complete */
1467fcf3ce44SJohn Forte 		scmd->flags &= ~SBD_SCSI_CMD_ACTIVE;
146861dfa509SRick McNeal 		sbd_ats_remove_by_task(task);
1469309bed43SCharles Binford - Sun Microsystems - Wichita 		if (scmd->flags & SBD_SCSI_CMD_XFER_FAIL) {
1470fcf3ce44SJohn Forte 			stmf_scsilib_send_status(task, STATUS_CHECK,
1471fcf3ce44SJohn Forte 			    STMF_SAA_WRITE_ERROR);
1472309bed43SCharles Binford - Sun Microsystems - Wichita 		} else {
1473309bed43SCharles Binford - Sun Microsystems - Wichita 			/*
1474309bed43SCharles Binford - Sun Microsystems - Wichita 			 * If SYNC_WRITE flag is on then we need to flush
1475309bed43SCharles Binford - Sun Microsystems - Wichita 			 * cache before sending status.
1476309bed43SCharles Binford - Sun Microsystems - Wichita 			 * Note: this may be a no-op because of how
1477309bed43SCharles Binford - Sun Microsystems - Wichita 			 * SL_WRITEBACK_CACHE_DISABLE and
1478309bed43SCharles Binford - Sun Microsystems - Wichita 			 * SL_FLUSH_ON_DISABLED_WRITECACHE are set, but not
1479309bed43SCharles Binford - Sun Microsystems - Wichita 			 * worth code complexity of checking those in this code
1480309bed43SCharles Binford - Sun Microsystems - Wichita 			 * path, SBD_SCSI_CMD_SYNC_WRITE is rarely set.
1481309bed43SCharles Binford - Sun Microsystems - Wichita 			 */
1482309bed43SCharles Binford - Sun Microsystems - Wichita 			if ((scmd->flags & SBD_SCSI_CMD_SYNC_WRITE) &&
1483309bed43SCharles Binford - Sun Microsystems - Wichita 			    (sbd_flush_data_cache(sl, 0) != SBD_SUCCESS)) {
1484309bed43SCharles Binford - Sun Microsystems - Wichita 				stmf_scsilib_send_status(task, STATUS_CHECK,
1485309bed43SCharles Binford - Sun Microsystems - Wichita 				    STMF_SAA_WRITE_ERROR);
1486309bed43SCharles Binford - Sun Microsystems - Wichita 			} else {
1487309bed43SCharles Binford - Sun Microsystems - Wichita 				stmf_scsilib_send_status(task, STATUS_GOOD, 0);
1488309bed43SCharles Binford - Sun Microsystems - Wichita 			}
1489309bed43SCharles Binford - Sun Microsystems - Wichita 		}
1490fcf3ce44SJohn Forte 		return;
1491fcf3ce44SJohn Forte 	}
149279a8ba49SCharles Ting 	sbd_do_write_xfer(task, scmd, dbuf, dbuf_reusable);
1493fcf3ce44SJohn Forte }
1494fcf3ce44SJohn Forte 
14953fb517f7SJames Moore /*
14963fb517f7SJames Moore  * Return true if copy avoidance is beneficial.
14973fb517f7SJames Moore  */
14983fb517f7SJames Moore static int
sbd_zcopy_write_useful(scsi_task_t * task,uint64_t laddr,uint32_t len,uint64_t blksize)14993fb517f7SJames Moore sbd_zcopy_write_useful(scsi_task_t *task, uint64_t laddr, uint32_t len,
15003fb517f7SJames Moore     uint64_t blksize)
15013fb517f7SJames Moore {
15023fb517f7SJames Moore 	/*
15033fb517f7SJames Moore 	 * If there is a global copy threshold over-ride, use it.
15043fb517f7SJames Moore 	 * Otherwise use the PP value with the caveat that at least
15053fb517f7SJames Moore 	 * 1/2 the data must avoid being copied to be useful.
15063fb517f7SJames Moore 	 */
15073fb517f7SJames Moore 	if (sbd_copy_threshold > 0) {
15083fb517f7SJames Moore 		return (len >= sbd_copy_threshold);
15093fb517f7SJames Moore 	} else {
15103fb517f7SJames Moore 		uint64_t no_copy_span;
15113fb517f7SJames Moore 
15123fb517f7SJames Moore 		/* sub-blocksize writes always copy */
15133fb517f7SJames Moore 		if (len < task->task_copy_threshold || len < blksize)
15143fb517f7SJames Moore 			return (0);
15153fb517f7SJames Moore 		/*
15163fb517f7SJames Moore 		 * Calculate amount of data that will avoid the copy path.
15173fb517f7SJames Moore 		 * The calculation is only valid if len >= blksize.
15183fb517f7SJames Moore 		 */
15193fb517f7SJames Moore 		no_copy_span = P2ALIGN(laddr+len, blksize) -
15203fb517f7SJames Moore 		    P2ROUNDUP(laddr, blksize);
15213fb517f7SJames Moore 		return (no_copy_span >= len/2);
15223fb517f7SJames Moore 	}
15233fb517f7SJames Moore }
15243fb517f7SJames Moore 
1525fcf3ce44SJohn Forte void
sbd_handle_write(struct scsi_task * task,struct stmf_data_buf * initial_dbuf)1526fcf3ce44SJohn Forte sbd_handle_write(struct scsi_task *task, struct stmf_data_buf *initial_dbuf)
1527fcf3ce44SJohn Forte {
1528fcf3ce44SJohn Forte 	uint64_t lba, laddr;
1529fcf3ce44SJohn Forte 	uint32_t len;
1530fcf3ce44SJohn Forte 	uint8_t op = task->task_cdb[0], do_immediate_data = 0;
15318fe96085Stim szeto 	sbd_lu_t *sl = (sbd_lu_t *)task->task_lu->lu_provider_private;
1532fcf3ce44SJohn Forte 	sbd_cmd_t *scmd;
1533fcf3ce44SJohn Forte 	stmf_data_buf_t *dbuf;
153461dfa509SRick McNeal 	uint64_t blkcount;
1535309bed43SCharles Binford - Sun Microsystems - Wichita 	uint8_t	sync_wr_flag = 0;
153661dfa509SRick McNeal 	boolean_t fua_bit = B_FALSE;
1537fcf3ce44SJohn Forte 
15388fe96085Stim szeto 	if (sl->sl_flags & SL_WRITE_PROTECTED) {
15398fe96085Stim szeto 		stmf_scsilib_send_status(task, STATUS_CHECK,
15408fe96085Stim szeto 		    STMF_SAA_WRITE_PROTECTED);
15418fe96085Stim szeto 		return;
15428fe96085Stim szeto 	}
154361dfa509SRick McNeal 	/*
154461dfa509SRick McNeal 	 * Check to see if the command is WRITE(10), WRITE(12), or WRITE(16).
154561dfa509SRick McNeal 	 * If it is then check for bit 3 being set to indicate if Forced
154661dfa509SRick McNeal 	 * Unit Access is being requested. If so, we'll bypass the fast path
154761dfa509SRick McNeal 	 * code to simplify support of this feature.
154861dfa509SRick McNeal 	 */
154961dfa509SRick McNeal 	if (((op == SCMD_WRITE_G1) || (op == SCMD_WRITE_G4) ||
155061dfa509SRick McNeal 	    (op == SCMD_WRITE_G5)) && (task->task_cdb[1] & BIT_3)) {
155161dfa509SRick McNeal 		fua_bit = B_TRUE;
155261dfa509SRick McNeal 	}
1553fcf3ce44SJohn Forte 	if (op == SCMD_WRITE) {
1554fcf3ce44SJohn Forte 		lba = READ_SCSI21(&task->task_cdb[1], uint64_t);
1555fcf3ce44SJohn Forte 		len = (uint32_t)task->task_cdb[4];
1556fcf3ce44SJohn Forte 
1557fcf3ce44SJohn Forte 		if (len == 0) {
1558fcf3ce44SJohn Forte 			len = 256;
1559fcf3ce44SJohn Forte 		}
1560fcf3ce44SJohn Forte 	} else if (op == SCMD_WRITE_G1) {
1561fcf3ce44SJohn Forte 		lba = READ_SCSI32(&task->task_cdb[2], uint64_t);
1562fcf3ce44SJohn Forte 		len = READ_SCSI16(&task->task_cdb[7], uint32_t);
1563fcf3ce44SJohn Forte 	} else if (op == SCMD_WRITE_G5) {
1564fcf3ce44SJohn Forte 		lba = READ_SCSI32(&task->task_cdb[2], uint64_t);
1565fcf3ce44SJohn Forte 		len = READ_SCSI32(&task->task_cdb[6], uint32_t);
1566fcf3ce44SJohn Forte 	} else if (op == SCMD_WRITE_G4) {
1567fcf3ce44SJohn Forte 		lba = READ_SCSI64(&task->task_cdb[2], uint64_t);
1568fcf3ce44SJohn Forte 		len = READ_SCSI32(&task->task_cdb[10], uint32_t);
1569309bed43SCharles Binford - Sun Microsystems - Wichita 	} else if (op == SCMD_WRITE_VERIFY) {
1570309bed43SCharles Binford - Sun Microsystems - Wichita 		lba = READ_SCSI32(&task->task_cdb[2], uint64_t);
1571309bed43SCharles Binford - Sun Microsystems - Wichita 		len = READ_SCSI16(&task->task_cdb[7], uint32_t);
1572309bed43SCharles Binford - Sun Microsystems - Wichita 		sync_wr_flag = SBD_SCSI_CMD_SYNC_WRITE;
1573309bed43SCharles Binford - Sun Microsystems - Wichita 	} else if (op == SCMD_WRITE_VERIFY_G5) {
1574309bed43SCharles Binford - Sun Microsystems - Wichita 		lba = READ_SCSI32(&task->task_cdb[2], uint64_t);
1575309bed43SCharles Binford - Sun Microsystems - Wichita 		len = READ_SCSI32(&task->task_cdb[6], uint32_t);
1576309bed43SCharles Binford - Sun Microsystems - Wichita 		sync_wr_flag = SBD_SCSI_CMD_SYNC_WRITE;
1577309bed43SCharles Binford - Sun Microsystems - Wichita 	} else if (op == SCMD_WRITE_VERIFY_G4) {
1578309bed43SCharles Binford - Sun Microsystems - Wichita 		lba = READ_SCSI64(&task->task_cdb[2], uint64_t);
1579309bed43SCharles Binford - Sun Microsystems - Wichita 		len = READ_SCSI32(&task->task_cdb[10], uint32_t);
1580309bed43SCharles Binford - Sun Microsystems - Wichita 		sync_wr_flag = SBD_SCSI_CMD_SYNC_WRITE;
1581fcf3ce44SJohn Forte 	} else {
1582fcf3ce44SJohn Forte 		stmf_scsilib_send_status(task, STATUS_CHECK,
1583fcf3ce44SJohn Forte 		    STMF_SAA_INVALID_OPCODE);
1584fcf3ce44SJohn Forte 		return;
1585fcf3ce44SJohn Forte 	}
1586fcf3ce44SJohn Forte 
15878fe96085Stim szeto 	laddr = lba << sl->sl_data_blocksize_shift;
158861dfa509SRick McNeal 	blkcount = len;
15898fe96085Stim szeto 	len <<= sl->sl_data_blocksize_shift;
1590fcf3ce44SJohn Forte 
15918fe96085Stim szeto 	if ((laddr + (uint64_t)len) > sl->sl_lu_size) {
1592fcf3ce44SJohn Forte 		stmf_scsilib_send_status(task, STATUS_CHECK,
1593fcf3ce44SJohn Forte 		    STMF_SAA_LBA_OUT_OF_RANGE);
1594fcf3ce44SJohn Forte 		return;
1595fcf3ce44SJohn Forte 	}
1596fcf3ce44SJohn Forte 
1597fcf3ce44SJohn Forte 	task->task_cmd_xfer_length = len;
1598fcf3ce44SJohn Forte 	if (task->task_additional_flags & TASK_AF_NO_EXPECTED_XFER_LENGTH) {
1599fcf3ce44SJohn Forte 		task->task_expected_xfer_length = len;
1600fcf3ce44SJohn Forte 	}
1601fcf3ce44SJohn Forte 
1602fcf3ce44SJohn Forte 	len = (len > task->task_expected_xfer_length) ?
1603fcf3ce44SJohn Forte 	    task->task_expected_xfer_length : len;
1604fcf3ce44SJohn Forte 
1605fcf3ce44SJohn Forte 	if (len == 0) {
1606fcf3ce44SJohn Forte 		stmf_scsilib_send_status(task, STATUS_GOOD, 0);
1607fcf3ce44SJohn Forte 		return;
1608fcf3ce44SJohn Forte 	}
1609fcf3ce44SJohn Forte 
161061dfa509SRick McNeal 	if (sbd_ats_handling_before_io(task, sl, lba, blkcount) !=
161161dfa509SRick McNeal 	    SBD_SUCCESS) {
161261dfa509SRick McNeal 		if (stmf_task_poll_lu(task, 10) != STMF_SUCCESS) {
161361dfa509SRick McNeal 			stmf_scsilib_send_status(task, STATUS_BUSY, 0);
161461dfa509SRick McNeal 		}
161561dfa509SRick McNeal 		return;
161661dfa509SRick McNeal 	}
161761dfa509SRick McNeal 
16183fb517f7SJames Moore 	if (sbd_zcopy & (4|1) &&		/* Debug switch */
16193fb517f7SJames Moore 	    initial_dbuf == NULL &&		/* No PP buf passed in */
16203fb517f7SJames Moore 	    sl->sl_flags & SL_CALL_ZVOL &&	/* zvol backing store */
16213fb517f7SJames Moore 	    (task->task_additional_flags &
16223fb517f7SJames Moore 	    TASK_AF_ACCEPT_LU_DBUF) &&		/* PP allows it */
162361dfa509SRick McNeal 	    sbd_zcopy_write_useful(task, laddr, len, sl->sl_blksize) &&
162461dfa509SRick McNeal 	    !fua_bit) {
16253fb517f7SJames Moore 
16263fb517f7SJames Moore 		/*
16273fb517f7SJames Moore 		 * XXX Note that disallowing initial_dbuf will eliminate
16283fb517f7SJames Moore 		 * iSCSI from participating. For small writes, that is
16293fb517f7SJames Moore 		 * probably ok. For large writes, it may be best to just
16303fb517f7SJames Moore 		 * copy the data from the initial dbuf and use zcopy for
16313fb517f7SJames Moore 		 * the rest.
16323fb517f7SJames Moore 		 */
16333fb517f7SJames Moore 		rw_enter(&sl->sl_access_state_lock, RW_READER);
16343fb517f7SJames Moore 		if ((sl->sl_flags & SL_MEDIA_LOADED) == 0) {
16353fb517f7SJames Moore 			rw_exit(&sl->sl_access_state_lock);
163661dfa509SRick McNeal 			sbd_ats_remove_by_task(task);
16373fb517f7SJames Moore 			stmf_scsilib_send_status(task, STATUS_CHECK,
16383fb517f7SJames Moore 			    STMF_SAA_READ_ERROR);
16393fb517f7SJames Moore 			return;
16403fb517f7SJames Moore 		}
16413fb517f7SJames Moore 		/*
16423fb517f7SJames Moore 		 * Setup scmd to track the write progress.
16433fb517f7SJames Moore 		 */
16443fb517f7SJames Moore 		if (task->task_lu_private) {
16453fb517f7SJames Moore 			scmd = (sbd_cmd_t *)task->task_lu_private;
16463fb517f7SJames Moore 		} else {
16473fb517f7SJames Moore 			scmd = (sbd_cmd_t *)kmem_alloc(sizeof (sbd_cmd_t),
16483fb517f7SJames Moore 			    KM_SLEEP);
16493fb517f7SJames Moore 			task->task_lu_private = scmd;
16503fb517f7SJames Moore 		}
165161dfa509SRick McNeal 		scmd->flags = SBD_SCSI_CMD_ACTIVE | SBD_SCSI_CMD_ATS_RELATED |
165261dfa509SRick McNeal 		    sync_wr_flag;
16533fb517f7SJames Moore 		scmd->cmd_type = SBD_CMD_SCSI_WRITE;
16543fb517f7SJames Moore 		scmd->nbufs = 0;
16553fb517f7SJames Moore 		scmd->addr = laddr;
16563fb517f7SJames Moore 		scmd->len = len;
16573fb517f7SJames Moore 		scmd->current_ro = 0;
16583fb517f7SJames Moore 		sbd_do_sgl_write_xfer(task, scmd, 1);
16593fb517f7SJames Moore 		return;
16603fb517f7SJames Moore 	}
16613fb517f7SJames Moore 
166279a8ba49SCharles Ting 	if ((initial_dbuf != NULL) && (task->task_flags & TF_INITIAL_BURST)) {
1663fcf3ce44SJohn Forte 		if (initial_dbuf->db_data_size > len) {
1664fcf3ce44SJohn Forte 			if (initial_dbuf->db_data_size >
1665fcf3ce44SJohn Forte 			    task->task_expected_xfer_length) {
1666fcf3ce44SJohn Forte 				/* protocol error */
1667fcf3ce44SJohn Forte 				stmf_abort(STMF_QUEUE_TASK_ABORT, task,
1668fcf3ce44SJohn Forte 				    STMF_INVALID_ARG, NULL);
1669fcf3ce44SJohn Forte 				return;
1670fcf3ce44SJohn Forte 			}
1671fcf3ce44SJohn Forte 			initial_dbuf->db_data_size = len;
1672fcf3ce44SJohn Forte 		}
1673fcf3ce44SJohn Forte 		do_immediate_data = 1;
1674fcf3ce44SJohn Forte 	}
1675fcf3ce44SJohn Forte 	dbuf = initial_dbuf;
1676fcf3ce44SJohn Forte 
1677fcf3ce44SJohn Forte 	if (task->task_lu_private) {
1678fcf3ce44SJohn Forte 		scmd = (sbd_cmd_t *)task->task_lu_private;
1679fcf3ce44SJohn Forte 	} else {
1680fcf3ce44SJohn Forte 		scmd = (sbd_cmd_t *)kmem_alloc(sizeof (sbd_cmd_t), KM_SLEEP);
1681fcf3ce44SJohn Forte 		task->task_lu_private = scmd;
1682fcf3ce44SJohn Forte 	}
168361dfa509SRick McNeal 	scmd->flags = SBD_SCSI_CMD_ACTIVE | SBD_SCSI_CMD_ATS_RELATED |
168461dfa509SRick McNeal 	    sync_wr_flag;
1685fcf3ce44SJohn Forte 	scmd->cmd_type = SBD_CMD_SCSI_WRITE;
168679a8ba49SCharles Ting 	scmd->nbufs = 0;
1687fcf3ce44SJohn Forte 	scmd->addr = laddr;
1688fcf3ce44SJohn Forte 	scmd->len = len;
1689fcf3ce44SJohn Forte 	scmd->current_ro = 0;
1690fcf3ce44SJohn Forte 
1691fcf3ce44SJohn Forte 	if (do_immediate_data) {
1692427fcaf8Stim szeto 		/*
16933fb517f7SJames Moore 		 * Account for data passed in this write command
1694427fcaf8Stim szeto 		 */
1695427fcaf8Stim szeto 		(void) stmf_xfer_data(task, dbuf, STMF_IOF_STATS_ONLY);
169661dfa509SRick McNeal 		atomic_add_32(&scmd->len, -dbuf->db_data_size);
1697fcf3ce44SJohn Forte 		scmd->current_ro += dbuf->db_data_size;
1698fcf3ce44SJohn Forte 		dbuf->db_xfer_status = STMF_SUCCESS;
1699fcf3ce44SJohn Forte 		sbd_handle_write_xfer_completion(task, scmd, dbuf, 0);
1700fcf3ce44SJohn Forte 	} else {
170179a8ba49SCharles Ting 		sbd_do_write_xfer(task, scmd, dbuf, 0);
1702fcf3ce44SJohn Forte 	}
1703fcf3ce44SJohn Forte }
1704fcf3ce44SJohn Forte 
1705fcf3ce44SJohn Forte /*
1706fcf3ce44SJohn Forte  * Utility routine to handle small non performance data transfers to the
1707fcf3ce44SJohn Forte  * initiators. dbuf is an initial data buf (if any), 'p' points to a data
1708fcf3ce44SJohn Forte  * buffer which is source of data for transfer, cdb_xfer_size is the
1709fcf3ce44SJohn Forte  * transfer size based on CDB, cmd_xfer_size is the actual amount of data
1710fcf3ce44SJohn Forte  * which this command would transfer (the size of data pointed to by 'p').
1711fcf3ce44SJohn Forte  */
1712fcf3ce44SJohn Forte void
sbd_handle_short_read_transfers(scsi_task_t * task,stmf_data_buf_t * dbuf,uint8_t * p,uint32_t cdb_xfer_size,uint32_t cmd_xfer_size)1713fcf3ce44SJohn Forte sbd_handle_short_read_transfers(scsi_task_t *task, stmf_data_buf_t *dbuf,
1714fcf3ce44SJohn Forte     uint8_t *p, uint32_t cdb_xfer_size, uint32_t cmd_xfer_size)
1715fcf3ce44SJohn Forte {
1716fcf3ce44SJohn Forte 	uint32_t bufsize, ndx;
1717fcf3ce44SJohn Forte 	sbd_cmd_t *scmd;
1718fcf3ce44SJohn Forte 
1719fcf3ce44SJohn Forte 	cmd_xfer_size = min(cmd_xfer_size, cdb_xfer_size);
1720fcf3ce44SJohn Forte 
1721fcf3ce44SJohn Forte 	task->task_cmd_xfer_length = cmd_xfer_size;
1722fcf3ce44SJohn Forte 	if (task->task_additional_flags & TASK_AF_NO_EXPECTED_XFER_LENGTH) {
1723fcf3ce44SJohn Forte 		task->task_expected_xfer_length = cmd_xfer_size;
1724fcf3ce44SJohn Forte 	} else {
1725fcf3ce44SJohn Forte 		cmd_xfer_size = min(cmd_xfer_size,
1726fcf3ce44SJohn Forte 		    task->task_expected_xfer_length);
1727fcf3ce44SJohn Forte 	}
1728fcf3ce44SJohn Forte 
1729fcf3ce44SJohn Forte 	if (cmd_xfer_size == 0) {
1730fcf3ce44SJohn Forte 		stmf_scsilib_send_status(task, STATUS_CHECK,
1731fcf3ce44SJohn Forte 		    STMF_SAA_INVALID_FIELD_IN_CDB);
1732fcf3ce44SJohn Forte 		return;
1733fcf3ce44SJohn Forte 	}
1734fcf3ce44SJohn Forte 	if (dbuf == NULL) {
1735fcf3ce44SJohn Forte 		uint32_t minsize = cmd_xfer_size;
1736fcf3ce44SJohn Forte 
1737fcf3ce44SJohn Forte 		dbuf = stmf_alloc_dbuf(task, cmd_xfer_size, &minsize, 0);
1738fcf3ce44SJohn Forte 	}
1739fcf3ce44SJohn Forte 	if (dbuf == NULL) {
17401b7fc709Stim szeto 		stmf_scsilib_send_status(task, STATUS_QFULL, 0);
1741fcf3ce44SJohn Forte 		return;
1742fcf3ce44SJohn Forte 	}
1743fcf3ce44SJohn Forte 
1744fcf3ce44SJohn Forte 	for (bufsize = 0, ndx = 0; bufsize < cmd_xfer_size; ndx++) {
1745fcf3ce44SJohn Forte 		uint8_t *d;
1746fcf3ce44SJohn Forte 		uint32_t s;
1747fcf3ce44SJohn Forte 
1748fcf3ce44SJohn Forte 		d = dbuf->db_sglist[ndx].seg_addr;
1749fcf3ce44SJohn Forte 		s = min((cmd_xfer_size - bufsize),
1750fcf3ce44SJohn Forte 		    dbuf->db_sglist[ndx].seg_length);
1751fcf3ce44SJohn Forte 		bcopy(p+bufsize, d, s);
1752fcf3ce44SJohn Forte 		bufsize += s;
1753fcf3ce44SJohn Forte 	}
1754fcf3ce44SJohn Forte 	dbuf->db_relative_offset = 0;
1755fcf3ce44SJohn Forte 	dbuf->db_data_size = cmd_xfer_size;
1756fcf3ce44SJohn Forte 	dbuf->db_flags = DB_DIRECTION_TO_RPORT;
1757fcf3ce44SJohn Forte 
1758fcf3ce44SJohn Forte 	if (task->task_lu_private == NULL) {
1759fcf3ce44SJohn Forte 		task->task_lu_private =
1760fcf3ce44SJohn Forte 		    kmem_alloc(sizeof (sbd_cmd_t), KM_SLEEP);
1761fcf3ce44SJohn Forte 	}
1762fcf3ce44SJohn Forte 	scmd = (sbd_cmd_t *)task->task_lu_private;
1763fcf3ce44SJohn Forte 
1764fcf3ce44SJohn Forte 	scmd->cmd_type = SBD_CMD_SMALL_READ;
1765fcf3ce44SJohn Forte 	scmd->flags = SBD_SCSI_CMD_ACTIVE;
1766fcf3ce44SJohn Forte 	(void) stmf_xfer_data(task, dbuf, 0);
1767fcf3ce44SJohn Forte }
1768fcf3ce44SJohn Forte 
1769fcf3ce44SJohn Forte void
sbd_handle_short_read_xfer_completion(struct scsi_task * task,sbd_cmd_t * scmd,struct stmf_data_buf * dbuf)1770fcf3ce44SJohn Forte sbd_handle_short_read_xfer_completion(struct scsi_task *task, sbd_cmd_t *scmd,
1771047c81d3SSaso Kiselkov     struct stmf_data_buf *dbuf)
1772fcf3ce44SJohn Forte {
1773fcf3ce44SJohn Forte 	if (dbuf->db_xfer_status != STMF_SUCCESS) {
1774fcf3ce44SJohn Forte 		stmf_abort(STMF_QUEUE_TASK_ABORT, task,
1775fcf3ce44SJohn Forte 		    dbuf->db_xfer_status, NULL);
1776fcf3ce44SJohn Forte 		return;
1777fcf3ce44SJohn Forte 	}
1778fcf3ce44SJohn Forte 	task->task_nbytes_transferred = dbuf->db_data_size;
1779fcf3ce44SJohn Forte 	scmd->flags &= ~SBD_SCSI_CMD_ACTIVE;
1780fcf3ce44SJohn Forte 	stmf_scsilib_send_status(task, STATUS_GOOD, 0);
1781fcf3ce44SJohn Forte }
1782fcf3ce44SJohn Forte 
17838fe96085Stim szeto void
sbd_handle_short_write_transfers(scsi_task_t * task,stmf_data_buf_t * dbuf,uint32_t cdb_xfer_size)17848fe96085Stim szeto sbd_handle_short_write_transfers(scsi_task_t *task,
17858fe96085Stim szeto     stmf_data_buf_t *dbuf, uint32_t cdb_xfer_size)
17868fe96085Stim szeto {
17878fe96085Stim szeto 	sbd_cmd_t *scmd;
17888fe96085Stim szeto 
17898fe96085Stim szeto 	task->task_cmd_xfer_length = cdb_xfer_size;
17908fe96085Stim szeto 	if (task->task_additional_flags & TASK_AF_NO_EXPECTED_XFER_LENGTH) {
17918fe96085Stim szeto 		task->task_expected_xfer_length = cdb_xfer_size;
17928fe96085Stim szeto 	} else {
17938fe96085Stim szeto 		cdb_xfer_size = min(cdb_xfer_size,
17948fe96085Stim szeto 		    task->task_expected_xfer_length);
17958fe96085Stim szeto 	}
17968fe96085Stim szeto 
17978fe96085Stim szeto 	if (cdb_xfer_size == 0) {
17988fe96085Stim szeto 		stmf_scsilib_send_status(task, STATUS_CHECK,
17998fe96085Stim szeto 		    STMF_SAA_INVALID_FIELD_IN_CDB);
18008fe96085Stim szeto 		return;
18018fe96085Stim szeto 	}
18028fe96085Stim szeto 	if (task->task_lu_private == NULL) {
18038fe96085Stim szeto 		task->task_lu_private = kmem_zalloc(sizeof (sbd_cmd_t),
18048fe96085Stim szeto 		    KM_SLEEP);
18058fe96085Stim szeto 	} else {
18068fe96085Stim szeto 		bzero(task->task_lu_private, sizeof (sbd_cmd_t));
18078fe96085Stim szeto 	}
18088fe96085Stim szeto 	scmd = (sbd_cmd_t *)task->task_lu_private;
18098fe96085Stim szeto 	scmd->cmd_type = SBD_CMD_SMALL_WRITE;
18108fe96085Stim szeto 	scmd->flags = SBD_SCSI_CMD_ACTIVE;
18118fe96085Stim szeto 	scmd->len = cdb_xfer_size;
18128fe96085Stim szeto 	if (dbuf == NULL) {
18138fe96085Stim szeto 		uint32_t minsize = cdb_xfer_size;
18148fe96085Stim szeto 
18158fe96085Stim szeto 		dbuf = stmf_alloc_dbuf(task, cdb_xfer_size, &minsize, 0);
18168fe96085Stim szeto 		if (dbuf == NULL) {
18178fe96085Stim szeto 			stmf_abort(STMF_QUEUE_TASK_ABORT, task,
18188fe96085Stim szeto 			    STMF_ALLOC_FAILURE, NULL);
18198fe96085Stim szeto 			return;
18208fe96085Stim szeto 		}
18218fe96085Stim szeto 		dbuf->db_data_size = cdb_xfer_size;
18228fe96085Stim szeto 		dbuf->db_relative_offset = 0;
18238fe96085Stim szeto 		dbuf->db_flags = DB_DIRECTION_FROM_RPORT;
1824c1374a13SSurya Prakki 		(void) stmf_xfer_data(task, dbuf, 0);
18258fe96085Stim szeto 	} else {
18268fe96085Stim szeto 		if (dbuf->db_data_size < cdb_xfer_size) {
18278fe96085Stim szeto 			stmf_abort(STMF_QUEUE_TASK_ABORT, task,
18288fe96085Stim szeto 			    STMF_ABORTED, NULL);
18298fe96085Stim szeto 			return;
18308fe96085Stim szeto 		}
18318fe96085Stim szeto 		dbuf->db_data_size = cdb_xfer_size;
18328fe96085Stim szeto 		sbd_handle_short_write_xfer_completion(task, dbuf);
18338fe96085Stim szeto 	}
18348fe96085Stim szeto }
18358fe96085Stim szeto 
18368fe96085Stim szeto void
sbd_handle_short_write_xfer_completion(scsi_task_t * task,stmf_data_buf_t * dbuf)18378fe96085Stim szeto sbd_handle_short_write_xfer_completion(scsi_task_t *task,
18388fe96085Stim szeto     stmf_data_buf_t *dbuf)
18398fe96085Stim szeto {
18408fe96085Stim szeto 	sbd_cmd_t *scmd;
184145039663SJohn Forte 	stmf_status_t st_ret;
184245039663SJohn Forte 	sbd_lu_t *sl = (sbd_lu_t *)task->task_lu->lu_provider_private;
18438fe96085Stim szeto 
18448fe96085Stim szeto 	/*
18458fe96085Stim szeto 	 * For now lets assume we will get only one sglist element
18468fe96085Stim szeto 	 * for short writes. If that ever changes, we should allocate
18478fe96085Stim szeto 	 * a local buffer and copy all the sg elements to one linear space.
18488fe96085Stim szeto 	 */
18498fe96085Stim szeto 	if ((dbuf->db_xfer_status != STMF_SUCCESS) ||
18508fe96085Stim szeto 	    (dbuf->db_sglist_length > 1)) {
18518fe96085Stim szeto 		stmf_abort(STMF_QUEUE_TASK_ABORT, task,
18528fe96085Stim szeto 		    dbuf->db_xfer_status, NULL);
18538fe96085Stim szeto 		return;
18548fe96085Stim szeto 	}
18558fe96085Stim szeto 
18568fe96085Stim szeto 	task->task_nbytes_transferred = dbuf->db_data_size;
18578fe96085Stim szeto 	scmd = (sbd_cmd_t *)task->task_lu_private;
18588fe96085Stim szeto 	scmd->flags &= ~SBD_SCSI_CMD_ACTIVE;
18598fe96085Stim szeto 
18608fe96085Stim szeto 	/* Lets find out who to call */
18618fe96085Stim szeto 	switch (task->task_cdb[0]) {
18628fe96085Stim szeto 	case SCMD_MODE_SELECT:
18638fe96085Stim szeto 	case SCMD_MODE_SELECT_G1:
186445039663SJohn Forte 		if (sl->sl_access_state == SBD_LU_STANDBY) {
186545039663SJohn Forte 			st_ret = stmf_proxy_scsi_cmd(task, dbuf);
186645039663SJohn Forte 			if (st_ret != STMF_SUCCESS) {
186745039663SJohn Forte 				stmf_scsilib_send_status(task, STATUS_CHECK,
186845039663SJohn Forte 				    STMF_SAA_LU_NO_ACCESS_UNAVAIL);
186945039663SJohn Forte 			}
187045039663SJohn Forte 		} else {
187145039663SJohn Forte 			sbd_handle_mode_select_xfer(task,
187245039663SJohn Forte 			    dbuf->db_sglist[0].seg_addr, dbuf->db_data_size);
187345039663SJohn Forte 		}
18748fe96085Stim szeto 		break;
1875b77b9231SDan McDonald 	case SCMD_UNMAP:
1876b77b9231SDan McDonald 		sbd_handle_unmap_xfer(task,
1877b77b9231SDan McDonald 		    dbuf->db_sglist[0].seg_addr, dbuf->db_data_size);
1878b77b9231SDan McDonald 		break;
187961dfa509SRick McNeal 	case SCMD_EXTENDED_COPY:
188061dfa509SRick McNeal 		sbd_handle_xcopy_xfer(task, dbuf->db_sglist[0].seg_addr);
188161dfa509SRick McNeal 		break;
18828fe96085Stim szeto 	case SCMD_PERSISTENT_RESERVE_OUT:
188345039663SJohn Forte 		if (sl->sl_access_state == SBD_LU_STANDBY) {
188445039663SJohn Forte 			st_ret = stmf_proxy_scsi_cmd(task, dbuf);
188545039663SJohn Forte 			if (st_ret != STMF_SUCCESS) {
188645039663SJohn Forte 				stmf_scsilib_send_status(task, STATUS_CHECK,
188745039663SJohn Forte 				    STMF_SAA_LU_NO_ACCESS_UNAVAIL);
188845039663SJohn Forte 			}
188945039663SJohn Forte 		} else {
189045039663SJohn Forte 			sbd_handle_pgr_out_data(task, dbuf);
189145039663SJohn Forte 		}
18928fe96085Stim szeto 		break;
18938fe96085Stim szeto 	default:
18948fe96085Stim szeto 		/* This should never happen */
18958fe96085Stim szeto 		stmf_abort(STMF_QUEUE_TASK_ABORT, task,
18968fe96085Stim szeto 		    STMF_ABORTED, NULL);
18978fe96085Stim szeto 	}
18988fe96085Stim szeto }
18998fe96085Stim szeto 
1900fcf3ce44SJohn Forte void
sbd_handle_read_capacity(struct scsi_task * task,struct stmf_data_buf * initial_dbuf)1901fcf3ce44SJohn Forte sbd_handle_read_capacity(struct scsi_task *task,
1902fcf3ce44SJohn Forte     struct stmf_data_buf *initial_dbuf)
1903fcf3ce44SJohn Forte {
19048fe96085Stim szeto 	sbd_lu_t *sl = (sbd_lu_t *)task->task_lu->lu_provider_private;
1905fcf3ce44SJohn Forte 	uint32_t cdb_len;
1906fcf3ce44SJohn Forte 	uint8_t p[32];
1907fcf3ce44SJohn Forte 	uint64_t s;
19088fe96085Stim szeto 	uint16_t blksize;
1909fcf3ce44SJohn Forte 
19108fe96085Stim szeto 	s = sl->sl_lu_size >> sl->sl_data_blocksize_shift;
1911fcf3ce44SJohn Forte 	s--;
19128fe96085Stim szeto 	blksize = ((uint16_t)1) << sl->sl_data_blocksize_shift;
19138fe96085Stim szeto 
1914fcf3ce44SJohn Forte 	switch (task->task_cdb[0]) {
1915fcf3ce44SJohn Forte 	case SCMD_READ_CAPACITY:
1916fcf3ce44SJohn Forte 		if (s & 0xffffffff00000000ull) {
1917fcf3ce44SJohn Forte 			p[0] = p[1] = p[2] = p[3] = 0xFF;
1918fcf3ce44SJohn Forte 		} else {
1919fcf3ce44SJohn Forte 			p[0] = (s >> 24) & 0xff;
1920fcf3ce44SJohn Forte 			p[1] = (s >> 16) & 0xff;
1921fcf3ce44SJohn Forte 			p[2] = (s >> 8) & 0xff;
1922fcf3ce44SJohn Forte 			p[3] = s & 0xff;
1923fcf3ce44SJohn Forte 		}
1924fcf3ce44SJohn Forte 		p[4] = 0; p[5] = 0;
19258fe96085Stim szeto 		p[6] = (blksize >> 8) & 0xff;
19268fe96085Stim szeto 		p[7] = blksize & 0xff;
1927fcf3ce44SJohn Forte 		sbd_handle_short_read_transfers(task, initial_dbuf, p, 8, 8);
19288fe96085Stim szeto 		break;
1929fcf3ce44SJohn Forte 
1930fcf3ce44SJohn Forte 	case SCMD_SVC_ACTION_IN_G4:
1931fcf3ce44SJohn Forte 		cdb_len = READ_SCSI32(&task->task_cdb[10], uint32_t);
1932fcf3ce44SJohn Forte 		bzero(p, 32);
1933fcf3ce44SJohn Forte 		p[0] = (s >> 56) & 0xff;
1934fcf3ce44SJohn Forte 		p[1] = (s >> 48) & 0xff;
1935fcf3ce44SJohn Forte 		p[2] = (s >> 40) & 0xff;
1936fcf3ce44SJohn Forte 		p[3] = (s >> 32) & 0xff;
1937fcf3ce44SJohn Forte 		p[4] = (s >> 24) & 0xff;
1938fcf3ce44SJohn Forte 		p[5] = (s >> 16) & 0xff;
1939fcf3ce44SJohn Forte 		p[6] = (s >> 8) & 0xff;
1940fcf3ce44SJohn Forte 		p[7] = s & 0xff;
19418fe96085Stim szeto 		p[10] = (blksize >> 8) & 0xff;
19428fe96085Stim szeto 		p[11] = blksize & 0xff;
1943b77b9231SDan McDonald 		if (sl->sl_flags & SL_UNMAP_ENABLED) {
1944b77b9231SDan McDonald 			p[14] = 0x80;
1945b77b9231SDan McDonald 		}
1946fcf3ce44SJohn Forte 		sbd_handle_short_read_transfers(task, initial_dbuf, p,
1947fcf3ce44SJohn Forte 		    cdb_len, 32);
19488fe96085Stim szeto 		break;
1949fcf3ce44SJohn Forte 	}
1950fcf3ce44SJohn Forte }
1951fcf3ce44SJohn Forte 
19528fe96085Stim szeto void
sbd_calc_geometry(uint64_t s,uint16_t blksize,uint8_t * nsectors,uint8_t * nheads,uint32_t * ncyl)19538fe96085Stim szeto sbd_calc_geometry(uint64_t s, uint16_t blksize, uint8_t *nsectors,
19548fe96085Stim szeto     uint8_t *nheads, uint32_t *ncyl)
19558fe96085Stim szeto {
19568fe96085Stim szeto 	if (s < (4ull * 1024ull * 1024ull * 1024ull)) {
19578fe96085Stim szeto 		*nsectors = 32;
19588fe96085Stim szeto 		*nheads = 8;
19598fe96085Stim szeto 	} else {
19608fe96085Stim szeto 		*nsectors = 254;
19618fe96085Stim szeto 		*nheads = 254;
19628fe96085Stim szeto 	}
19638fe96085Stim szeto 	*ncyl = s / ((uint64_t)blksize * (uint64_t)(*nsectors) *
19648fe96085Stim szeto 	    (uint64_t)(*nheads));
19658fe96085Stim szeto }
1966fcf3ce44SJohn Forte 
1967fcf3ce44SJohn Forte void
sbd_handle_mode_sense(struct scsi_task * task,struct stmf_data_buf * initial_dbuf,uint8_t * buf)1968fcf3ce44SJohn Forte sbd_handle_mode_sense(struct scsi_task *task,
19698fe96085Stim szeto     struct stmf_data_buf *initial_dbuf, uint8_t *buf)
1970fcf3ce44SJohn Forte {
19718fe96085Stim szeto 	sbd_lu_t *sl = (sbd_lu_t *)task->task_lu->lu_provider_private;
19728fe96085Stim szeto 	uint32_t cmd_size, n;
19738fe96085Stim szeto 	uint8_t *cdb;
19748fe96085Stim szeto 	uint32_t ncyl;
19758fe96085Stim szeto 	uint8_t nsectors, nheads;
197661dfa509SRick McNeal 	uint8_t page, ctrl, header_size;
19778fe96085Stim szeto 	uint16_t nbytes;
19788fe96085Stim szeto 	uint8_t *p;
19798fe96085Stim szeto 	uint64_t s = sl->sl_lu_size;
19808fe96085Stim szeto 	uint32_t dev_spec_param_offset;
19818fe96085Stim szeto 
19828fe96085Stim szeto 	p = buf;	/* buf is assumed to be zeroed out and large enough */
19838fe96085Stim szeto 	n = 0;
19848fe96085Stim szeto 	cdb = &task->task_cdb[0];
19858fe96085Stim szeto 	page = cdb[2] & 0x3F;
19868fe96085Stim szeto 	ctrl = (cdb[2] >> 6) & 3;
19878fe96085Stim szeto 
19888fe96085Stim szeto 	if (cdb[0] == SCMD_MODE_SENSE) {
198961dfa509SRick McNeal 		cmd_size = cdb[4];
19908fe96085Stim szeto 		header_size = 4;
19918fe96085Stim szeto 		dev_spec_param_offset = 2;
19928fe96085Stim szeto 	} else {
199361dfa509SRick McNeal 		cmd_size = READ_SCSI16(&cdb[7], uint32_t);
19948fe96085Stim szeto 		header_size = 8;
19958fe96085Stim szeto 		dev_spec_param_offset = 3;
19968fe96085Stim szeto 	}
1997fcf3ce44SJohn Forte 
19988fe96085Stim szeto 	/* Now validate the command */
199961dfa509SRick McNeal 	if ((cdb[2] != 0) && (page != MODEPAGE_ALLPAGES) &&
200061dfa509SRick McNeal 	    (page != MODEPAGE_CACHING) && (page != MODEPAGE_CTRL_MODE) &&
200161dfa509SRick McNeal 	    (page != MODEPAGE_FORMAT) && (page != MODEPAGE_GEOMETRY)) {
20028fe96085Stim szeto 		stmf_scsilib_send_status(task, STATUS_CHECK,
20038fe96085Stim szeto 		    STMF_SAA_INVALID_FIELD_IN_CDB);
20048fe96085Stim szeto 		return;
20058fe96085Stim szeto 	}
20068fe96085Stim szeto 
20078fe96085Stim szeto 	/* We will update the length in the mode header at the end */
20088fe96085Stim szeto 
20098fe96085Stim szeto 	/* Block dev device specific param in mode param header has wp bit */
20108fe96085Stim szeto 	if (sl->sl_flags & SL_WRITE_PROTECTED) {
20118fe96085Stim szeto 		p[n + dev_spec_param_offset] = BIT_7;
20128fe96085Stim szeto 	}
20138fe96085Stim szeto 	n += header_size;
20148fe96085Stim szeto 	/* We are not going to return any block descriptor */
20158fe96085Stim szeto 
20168fe96085Stim szeto 	nbytes = ((uint16_t)1) << sl->sl_data_blocksize_shift;
20178fe96085Stim szeto 	sbd_calc_geometry(s, nbytes, &nsectors, &nheads, &ncyl);
20188fe96085Stim szeto 
201961dfa509SRick McNeal 	if ((page == MODEPAGE_FORMAT) || (page == MODEPAGE_ALLPAGES)) {
20208fe96085Stim szeto 		p[n] = 0x03;
20218fe96085Stim szeto 		p[n+1] = 0x16;
20228fe96085Stim szeto 		if (ctrl != 1) {
20238fe96085Stim szeto 			p[n + 11] = nsectors;
20248fe96085Stim szeto 			p[n + 12] = nbytes >> 8;
20258fe96085Stim szeto 			p[n + 13] = nbytes & 0xff;
20268fe96085Stim szeto 			p[n + 20] = 0x80;
20278fe96085Stim szeto 		}
20288fe96085Stim szeto 		n += 24;
20298fe96085Stim szeto 	}
203061dfa509SRick McNeal 	if ((page == MODEPAGE_GEOMETRY) || (page == MODEPAGE_ALLPAGES)) {
20318fe96085Stim szeto 		p[n] = 0x04;
20328fe96085Stim szeto 		p[n + 1] = 0x16;
20338fe96085Stim szeto 		if (ctrl != 1) {
20348fe96085Stim szeto 			p[n + 2] = ncyl >> 16;
20358fe96085Stim szeto 			p[n + 3] = ncyl >> 8;
20368fe96085Stim szeto 			p[n + 4] = ncyl & 0xff;
20378fe96085Stim szeto 			p[n + 5] = nheads;
20388fe96085Stim szeto 			p[n + 20] = 0x15;
20398fe96085Stim szeto 			p[n + 21] = 0x18;
20408fe96085Stim szeto 		}
20418fe96085Stim szeto 		n += 24;
20428fe96085Stim szeto 	}
20438fe96085Stim szeto 	if ((page == MODEPAGE_CACHING) || (page == MODEPAGE_ALLPAGES)) {
20448fe96085Stim szeto 		struct mode_caching *mode_caching_page;
20458fe96085Stim szeto 
20468fe96085Stim szeto 		mode_caching_page = (struct mode_caching *)&p[n];
20478fe96085Stim szeto 
20488fe96085Stim szeto 		mode_caching_page->mode_page.code = MODEPAGE_CACHING;
20498fe96085Stim szeto 		mode_caching_page->mode_page.ps = 1; /* A saveable page */
20508fe96085Stim szeto 		mode_caching_page->mode_page.length = 0x12;
20518fe96085Stim szeto 
20528fe96085Stim szeto 		switch (ctrl) {
20538fe96085Stim szeto 		case (0):
20548fe96085Stim szeto 			/* Current */
20558fe96085Stim szeto 			if ((sl->sl_flags & SL_WRITEBACK_CACHE_DISABLE) == 0) {
20568fe96085Stim szeto 				mode_caching_page->wce = 1;
20578fe96085Stim szeto 			}
20588fe96085Stim szeto 			break;
20598fe96085Stim szeto 
20608fe96085Stim szeto 		case (1):
20618fe96085Stim szeto 			/* Changeable */
20628fe96085Stim szeto 			if ((sl->sl_flags &
20638fe96085Stim szeto 			    SL_WRITEBACK_CACHE_SET_UNSUPPORTED) == 0) {
20648fe96085Stim szeto 				mode_caching_page->wce = 1;
20658fe96085Stim szeto 			}
20668fe96085Stim szeto 			break;
20678fe96085Stim szeto 
20688fe96085Stim szeto 		default:
20698fe96085Stim szeto 			if ((sl->sl_flags &
20708fe96085Stim szeto 			    SL_SAVED_WRITE_CACHE_DISABLE) == 0) {
20718fe96085Stim szeto 				mode_caching_page->wce = 1;
20728fe96085Stim szeto 			}
20738fe96085Stim szeto 			break;
2074fcf3ce44SJohn Forte 		}
20758fe96085Stim szeto 		n += (sizeof (struct mode_page) +
20768fe96085Stim szeto 		    mode_caching_page->mode_page.length);
20778fe96085Stim szeto 	}
20788fe96085Stim szeto 	if ((page == MODEPAGE_CTRL_MODE) || (page == MODEPAGE_ALLPAGES)) {
20798fe96085Stim szeto 		struct mode_control_scsi3 *mode_control_page;
2080fcf3ce44SJohn Forte 
20818fe96085Stim szeto 		mode_control_page = (struct mode_control_scsi3 *)&p[n];
20828fe96085Stim szeto 
20838fe96085Stim szeto 		mode_control_page->mode_page.code = MODEPAGE_CTRL_MODE;
20848fe96085Stim szeto 		mode_control_page->mode_page.length =
20858fe96085Stim szeto 		    PAGELENGTH_MODE_CONTROL_SCSI3;
20868fe96085Stim szeto 		if (ctrl != 1) {
20878fe96085Stim szeto 			/* If not looking for changeable values, report this. */
20888fe96085Stim szeto 			mode_control_page->que_mod = CTRL_QMOD_UNRESTRICT;
2089fcf3ce44SJohn Forte 		}
20908fe96085Stim szeto 		n += (sizeof (struct mode_page) +
20918fe96085Stim szeto 		    mode_control_page->mode_page.length);
20928fe96085Stim szeto 	}
2093fcf3ce44SJohn Forte 
20948fe96085Stim szeto 	if (cdb[0] == SCMD_MODE_SENSE) {
20958fe96085Stim szeto 		if (n > 255) {
20968fe96085Stim szeto 			stmf_scsilib_send_status(task, STATUS_CHECK,
20978fe96085Stim szeto 			    STMF_SAA_INVALID_FIELD_IN_CDB);
20988fe96085Stim szeto 			return;
20998fe96085Stim szeto 		}
21008fe96085Stim szeto 		/*
21018fe96085Stim szeto 		 * Mode parameter header length doesn't include the number
21028fe96085Stim szeto 		 * of bytes in the length field, so adjust the count.
21038fe96085Stim szeto 		 * Byte count minus header length field size.
21048fe96085Stim szeto 		 */
210561dfa509SRick McNeal 		buf[0] = (n - header_size) & 0xff;
21068fe96085Stim szeto 	} else {
21078fe96085Stim szeto 		/* Byte count minus header length field size. */
210861dfa509SRick McNeal 		buf[1] = (n - header_size) & 0xff;
210961dfa509SRick McNeal 		buf[0] = ((n - header_size) >> 8) & 0xff;
2110fcf3ce44SJohn Forte 	}
2111fcf3ce44SJohn Forte 
21128fe96085Stim szeto 	sbd_handle_short_read_transfers(task, initial_dbuf, buf,
21138fe96085Stim szeto 	    cmd_size, n);
21148fe96085Stim szeto }
21158fe96085Stim szeto 
21168fe96085Stim szeto void
sbd_handle_mode_select(scsi_task_t * task,stmf_data_buf_t * dbuf)21178fe96085Stim szeto sbd_handle_mode_select(scsi_task_t *task, stmf_data_buf_t *dbuf)
21188fe96085Stim szeto {
21198fe96085Stim szeto 	uint32_t cmd_xfer_len;
21208fe96085Stim szeto 
21218fe96085Stim szeto 	if (task->task_cdb[0] == SCMD_MODE_SELECT) {
21228fe96085Stim szeto 		cmd_xfer_len = (uint32_t)task->task_cdb[4];
21238fe96085Stim szeto 	} else {
21248fe96085Stim szeto 		cmd_xfer_len = READ_SCSI16(&task->task_cdb[7], uint32_t);
21258fe96085Stim szeto 	}
2126fcf3ce44SJohn Forte 
21278fe96085Stim szeto 	if ((task->task_cdb[1] & 0xFE) != 0x10) {
2128fcf3ce44SJohn Forte 		stmf_scsilib_send_status(task, STATUS_CHECK,
2129fcf3ce44SJohn Forte 		    STMF_SAA_INVALID_FIELD_IN_CDB);
2130fcf3ce44SJohn Forte 		return;
2131fcf3ce44SJohn Forte 	}
2132fcf3ce44SJohn Forte 
21338fe96085Stim szeto 	if (cmd_xfer_len == 0) {
21348fe96085Stim szeto 		/* zero byte mode selects are allowed */
21358fe96085Stim szeto 		stmf_scsilib_send_status(task, STATUS_GOOD, 0);
21368fe96085Stim szeto 		return;
21378fe96085Stim szeto 	}
2138fcf3ce44SJohn Forte 
21398fe96085Stim szeto 	sbd_handle_short_write_transfers(task, dbuf, cmd_xfer_len);
21408fe96085Stim szeto }
21418fe96085Stim szeto 
21428fe96085Stim szeto void
sbd_handle_mode_select_xfer(scsi_task_t * task,uint8_t * buf,uint32_t buflen)21438fe96085Stim szeto sbd_handle_mode_select_xfer(scsi_task_t *task, uint8_t *buf, uint32_t buflen)
21448fe96085Stim szeto {
21458fe96085Stim szeto 	sbd_lu_t *sl = (sbd_lu_t *)task->task_lu->lu_provider_private;
21468fe96085Stim szeto 	sbd_it_data_t *it;
21478fe96085Stim szeto 	int hdr_len, bd_len;
21488fe96085Stim szeto 	sbd_status_t sret;
21498fe96085Stim szeto 	int i;
21508fe96085Stim szeto 
21518fe96085Stim szeto 	if (task->task_cdb[0] == SCMD_MODE_SELECT) {
21528fe96085Stim szeto 		hdr_len = 4;
2153fcf3ce44SJohn Forte 	} else {
21548fe96085Stim szeto 		hdr_len = 8;
2155fcf3ce44SJohn Forte 	}
2156fcf3ce44SJohn Forte 
21578fe96085Stim szeto 	if (buflen < hdr_len)
21588fe96085Stim szeto 		goto mode_sel_param_len_err;
2159fcf3ce44SJohn Forte 
21608fe96085Stim szeto 	bd_len = hdr_len == 4 ? buf[3] : READ_SCSI16(&buf[6], int);
2161fcf3ce44SJohn Forte 
21628fe96085Stim szeto 	if (buflen < (hdr_len + bd_len + 2))
21638fe96085Stim szeto 		goto mode_sel_param_len_err;
21648fe96085Stim szeto 
21658fe96085Stim szeto 	buf += hdr_len + bd_len;
21668fe96085Stim szeto 	buflen -= hdr_len + bd_len;
21678fe96085Stim szeto 
21688fe96085Stim szeto 	if ((buf[0] != 8) || (buflen != ((uint32_t)buf[1] + 2))) {
21698fe96085Stim szeto 		goto mode_sel_param_len_err;
21708fe96085Stim szeto 	}
2171fcf3ce44SJohn Forte 
21728fe96085Stim szeto 	if (buf[2] & 0xFB) {
21738fe96085Stim szeto 		goto mode_sel_param_field_err;
21748fe96085Stim szeto 	}
21758fe96085Stim szeto 
21768fe96085Stim szeto 	for (i = 3; i < (buf[1] + 2); i++) {
21778fe96085Stim szeto 		if (buf[i]) {
21788fe96085Stim szeto 			goto mode_sel_param_field_err;
2179fcf3ce44SJohn Forte 		}
21808fe96085Stim szeto 	}
21818fe96085Stim szeto 
21828fe96085Stim szeto 	sret = SBD_SUCCESS;
21838fe96085Stim szeto 
21848fe96085Stim szeto 	/* All good. Lets handle the write cache change, if any */
21858fe96085Stim szeto 	if (buf[2] & BIT_2) {
21868fe96085Stim szeto 		sret = sbd_wcd_set(0, sl);
21878fe96085Stim szeto 	} else {
21888fe96085Stim szeto 		sret = sbd_wcd_set(1, sl);
21898fe96085Stim szeto 	}
21908fe96085Stim szeto 
21918fe96085Stim szeto 	if (sret != SBD_SUCCESS) {
21928fe96085Stim szeto 		stmf_scsilib_send_status(task, STATUS_CHECK,
21938fe96085Stim szeto 		    STMF_SAA_WRITE_ERROR);
21948fe96085Stim szeto 		return;
21958fe96085Stim szeto 	}
2196fcf3ce44SJohn Forte 
21978fe96085Stim szeto 	/* set on the device passed, now set the flags */
21988fe96085Stim szeto 	mutex_enter(&sl->sl_lock);
21998fe96085Stim szeto 	if (buf[2] & BIT_2) {
22008fe96085Stim szeto 		sl->sl_flags &= ~SL_WRITEBACK_CACHE_DISABLE;
22018fe96085Stim szeto 	} else {
22028fe96085Stim szeto 		sl->sl_flags |= SL_WRITEBACK_CACHE_DISABLE;
22038fe96085Stim szeto 	}
2204fcf3ce44SJohn Forte 
22058fe96085Stim szeto 	for (it = sl->sl_it_list; it != NULL; it = it->sbd_it_next) {
22068fe96085Stim szeto 		if (it == task->task_lu_itl_handle)
22078fe96085Stim szeto 			continue;
22088fe96085Stim szeto 		it->sbd_it_ua_conditions |= SBD_UA_MODE_PARAMETERS_CHANGED;
22098fe96085Stim szeto 	}
2210fcf3ce44SJohn Forte 
22118fe96085Stim szeto 	if (task->task_cdb[1] & 1) {
22128fe96085Stim szeto 		if (buf[2] & BIT_2) {
22138fe96085Stim szeto 			sl->sl_flags &= ~SL_SAVED_WRITE_CACHE_DISABLE;
2214fcf3ce44SJohn Forte 		} else {
22158fe96085Stim szeto 			sl->sl_flags |= SL_SAVED_WRITE_CACHE_DISABLE;
2216fcf3ce44SJohn Forte 		}
22178fe96085Stim szeto 		mutex_exit(&sl->sl_lock);
22188fe96085Stim szeto 		sret = sbd_write_lu_info(sl);
22198fe96085Stim szeto 	} else {
22208fe96085Stim szeto 		mutex_exit(&sl->sl_lock);
2221fcf3ce44SJohn Forte 	}
22228fe96085Stim szeto 	if (sret == SBD_SUCCESS) {
22238fe96085Stim szeto 		stmf_scsilib_send_status(task, STATUS_GOOD, 0);
22248fe96085Stim szeto 	} else {
22258fe96085Stim szeto 		stmf_scsilib_send_status(task, STATUS_CHECK,
22268fe96085Stim szeto 		    STMF_SAA_WRITE_ERROR);
22278fe96085Stim szeto 	}
22288fe96085Stim szeto 	return;
2229fcf3ce44SJohn Forte 
22308fe96085Stim szeto mode_sel_param_len_err:
22318fe96085Stim szeto 	stmf_scsilib_send_status(task, STATUS_CHECK,
22328fe96085Stim szeto 	    STMF_SAA_PARAM_LIST_LENGTH_ERROR);
22338fe96085Stim szeto 	return;
22348fe96085Stim szeto mode_sel_param_field_err:
22358fe96085Stim szeto 	stmf_scsilib_send_status(task, STATUS_CHECK,
22368fe96085Stim szeto 	    STMF_SAA_INVALID_FIELD_IN_PARAM_LIST);
2237fcf3ce44SJohn Forte }
2238fcf3ce44SJohn Forte 
22396e4cf8b7SJohn Forte /*
22406e4cf8b7SJohn Forte  * Command support added from SPC-4 r24
22416e4cf8b7SJohn Forte  * Supports info type 0, 2, 127
22426e4cf8b7SJohn Forte  */
22436e4cf8b7SJohn Forte void
sbd_handle_identifying_info(struct scsi_task * task,stmf_data_buf_t * initial_dbuf)22446e4cf8b7SJohn Forte sbd_handle_identifying_info(struct scsi_task *task,
22456e4cf8b7SJohn Forte     stmf_data_buf_t *initial_dbuf)
22466e4cf8b7SJohn Forte {
22476e4cf8b7SJohn Forte 	sbd_lu_t *sl = (sbd_lu_t *)task->task_lu->lu_provider_private;
22486e4cf8b7SJohn Forte 	uint8_t *cdb;
22496e4cf8b7SJohn Forte 	uint32_t cmd_size;
22506e4cf8b7SJohn Forte 	uint32_t param_len;
22516e4cf8b7SJohn Forte 	uint32_t xfer_size;
22526e4cf8b7SJohn Forte 	uint8_t info_type;
22536e4cf8b7SJohn Forte 	uint8_t *buf, *p;
22546e4cf8b7SJohn Forte 
22556e4cf8b7SJohn Forte 	cdb = &task->task_cdb[0];
22566e4cf8b7SJohn Forte 	cmd_size = READ_SCSI32(&cdb[6], uint32_t);
22576e4cf8b7SJohn Forte 	info_type = cdb[10]>>1;
22586e4cf8b7SJohn Forte 
22596e4cf8b7SJohn Forte 	/* Validate the command */
22606e4cf8b7SJohn Forte 	if (cmd_size < 4) {
22616e4cf8b7SJohn Forte 		stmf_scsilib_send_status(task, STATUS_CHECK,
22626e4cf8b7SJohn Forte 		    STMF_SAA_INVALID_FIELD_IN_CDB);
22636e4cf8b7SJohn Forte 		return;
22646e4cf8b7SJohn Forte 	}
22656e4cf8b7SJohn Forte 
22666e4cf8b7SJohn Forte 	p = buf = kmem_zalloc(260, KM_SLEEP);
22676e4cf8b7SJohn Forte 
22686e4cf8b7SJohn Forte 	switch (info_type) {
22696e4cf8b7SJohn Forte 		case 0:
22706e4cf8b7SJohn Forte 			/*
22716e4cf8b7SJohn Forte 			 * No value is supplied but this info type
22726e4cf8b7SJohn Forte 			 * is mandatory.
22736e4cf8b7SJohn Forte 			 */
22746e4cf8b7SJohn Forte 			xfer_size = 4;
22756e4cf8b7SJohn Forte 			break;
22766e4cf8b7SJohn Forte 		case 2:
22776e4cf8b7SJohn Forte 			mutex_enter(&sl->sl_lock);
22786e4cf8b7SJohn Forte 			param_len = strlcpy((char *)(p+4), sl->sl_alias, 256);
22796e4cf8b7SJohn Forte 			mutex_exit(&sl->sl_lock);
22806e4cf8b7SJohn Forte 			/* text info must be null terminated */
22816e4cf8b7SJohn Forte 			if (++param_len > 256)
22826e4cf8b7SJohn Forte 				param_len = 256;
22836e4cf8b7SJohn Forte 			SCSI_WRITE16(p+2, param_len);
22846e4cf8b7SJohn Forte 			xfer_size = param_len + 4;
22856e4cf8b7SJohn Forte 			break;
22866e4cf8b7SJohn Forte 		case 127:
22876e4cf8b7SJohn Forte 			/* 0 and 2 descriptor supported */
22886e4cf8b7SJohn Forte 			SCSI_WRITE16(p+2, 8); /* set param length */
22896e4cf8b7SJohn Forte 			p += 8;
22906e4cf8b7SJohn Forte 			*p = 4; /* set type to 2 (7 hi bits) */
22916e4cf8b7SJohn Forte 			p += 2;
22926e4cf8b7SJohn Forte 			SCSI_WRITE16(p, 256); /* 256 max length */
22936e4cf8b7SJohn Forte 			xfer_size = 12;
22946e4cf8b7SJohn Forte 			break;
22956e4cf8b7SJohn Forte 		default:
22966e4cf8b7SJohn Forte 			stmf_scsilib_send_status(task, STATUS_CHECK,
22976e4cf8b7SJohn Forte 			    STMF_SAA_INVALID_FIELD_IN_CDB);
22986e4cf8b7SJohn Forte 			kmem_free(buf, 260);
22996e4cf8b7SJohn Forte 			return;
23006e4cf8b7SJohn Forte 	}
23016e4cf8b7SJohn Forte 	sbd_handle_short_read_transfers(task, initial_dbuf, buf,
23026e4cf8b7SJohn Forte 	    cmd_size, xfer_size);
23036e4cf8b7SJohn Forte 	kmem_free(buf, 260);
23046e4cf8b7SJohn Forte }
23056e4cf8b7SJohn Forte 
23062f624233SNattuvetty Bhavyan /*
23072f624233SNattuvetty Bhavyan  * This function parse through a string, passed to it as a pointer to a string,
23082f624233SNattuvetty Bhavyan  * by adjusting the pointer to the first non-space character and returns
23092f624233SNattuvetty Bhavyan  * the count/length of the first bunch of non-space characters. Multiple
23102f624233SNattuvetty Bhavyan  * Management URLs are stored as a space delimited string in sl_mgmt_url
23112f624233SNattuvetty Bhavyan  * field of sbd_lu_t. This function is used to retrieve one url at a time.
23122f624233SNattuvetty Bhavyan  *
23132f624233SNattuvetty Bhavyan  * i/p : pointer to pointer to a url string
23142f624233SNattuvetty Bhavyan  * o/p : Adjust the pointer to the url to the first non white character
23152f624233SNattuvetty Bhavyan  *       and returns the length of the URL
23162f624233SNattuvetty Bhavyan  */
23172f624233SNattuvetty Bhavyan uint16_t
sbd_parse_mgmt_url(char ** url_addr)2318047c81d3SSaso Kiselkov sbd_parse_mgmt_url(char **url_addr)
2319047c81d3SSaso Kiselkov {
23202f624233SNattuvetty Bhavyan 	uint16_t url_length = 0;
23212f624233SNattuvetty Bhavyan 	char *url;
23222f624233SNattuvetty Bhavyan 	url = *url_addr;
23232f624233SNattuvetty Bhavyan 
23242f624233SNattuvetty Bhavyan 	while (*url != '\0') {
23252f624233SNattuvetty Bhavyan 		if (*url == ' ' || *url == '\t' || *url == '\n') {
23262f624233SNattuvetty Bhavyan 			(*url_addr)++;
23272f624233SNattuvetty Bhavyan 			url = *url_addr;
23282f624233SNattuvetty Bhavyan 		} else {
23292f624233SNattuvetty Bhavyan 			break;
23302f624233SNattuvetty Bhavyan 		}
23312f624233SNattuvetty Bhavyan 	}
23322f624233SNattuvetty Bhavyan 
23332f624233SNattuvetty Bhavyan 	while (*url != '\0') {
23342f624233SNattuvetty Bhavyan 		if (*url == ' ' || *url == '\t' ||
23352f624233SNattuvetty Bhavyan 		    *url == '\n' || *url == '\0') {
23362f624233SNattuvetty Bhavyan 			break;
23372f624233SNattuvetty Bhavyan 		}
23382f624233SNattuvetty Bhavyan 		url++;
23392f624233SNattuvetty Bhavyan 		url_length++;
23402f624233SNattuvetty Bhavyan 	}
23412f624233SNattuvetty Bhavyan 	return (url_length);
23422f624233SNattuvetty Bhavyan }
23432f624233SNattuvetty Bhavyan 
23446140ef00SDan McDonald /* Try to make this the size of a kmem allocation cache. */
23456140ef00SDan McDonald static uint_t sbd_write_same_optimal_chunk = 128 * 1024;
23466140ef00SDan McDonald 
23476140ef00SDan McDonald static sbd_status_t
sbd_write_same_data(struct scsi_task * task,sbd_cmd_t * scmd)23486140ef00SDan McDonald sbd_write_same_data(struct scsi_task *task, sbd_cmd_t *scmd)
23496140ef00SDan McDonald {
23506140ef00SDan McDonald 	sbd_lu_t *sl = (sbd_lu_t *)task->task_lu->lu_provider_private;
23516140ef00SDan McDonald 	uint64_t addr, len, sz_done;
23526140ef00SDan McDonald 	uint32_t big_buf_size, xfer_size, off;
23536140ef00SDan McDonald 	uint8_t *big_buf;
23546140ef00SDan McDonald 	sbd_status_t ret;
23556140ef00SDan McDonald 
23566140ef00SDan McDonald 	if (task->task_cdb[0] == SCMD_WRITE_SAME_G1) {
23576140ef00SDan McDonald 		addr = READ_SCSI32(&task->task_cdb[2], uint64_t);
23586140ef00SDan McDonald 		len = READ_SCSI16(&task->task_cdb[7], uint64_t);
23596140ef00SDan McDonald 	} else {
23606140ef00SDan McDonald 		addr = READ_SCSI64(&task->task_cdb[2], uint64_t);
23616140ef00SDan McDonald 		len = READ_SCSI32(&task->task_cdb[10], uint64_t);
23626140ef00SDan McDonald 	}
23636140ef00SDan McDonald 	addr <<= sl->sl_data_blocksize_shift;
23646140ef00SDan McDonald 	len <<= sl->sl_data_blocksize_shift;
23656140ef00SDan McDonald 
23666140ef00SDan McDonald 	/*
23676140ef00SDan McDonald 	 * Reminders:
23686140ef00SDan McDonald 	 *    "len" is total size of what we wish to "write same".
23696140ef00SDan McDonald 	 *
23706140ef00SDan McDonald 	 *    xfer_size will be scmd->trans_data_len, which is the length
23716140ef00SDan McDonald 	 *    of the pattern we wish to replicate over "len".  We replicate
23726140ef00SDan McDonald 	 *    "xfer_size" of pattern over "len".
23736140ef00SDan McDonald 	 *
23746140ef00SDan McDonald 	 *    big_buf_size is set to an ideal actual-write size for an output
23756140ef00SDan McDonald 	 *    operation.  It may be the same as "len".  If it's not, it should
23766140ef00SDan McDonald 	 *    be an exact multiple of "xfer_size" so we don't get pattern
23776140ef00SDan McDonald 	 *    breakage until the very end of "len".
23786140ef00SDan McDonald 	 */
23796140ef00SDan McDonald 	big_buf_size = len > sbd_write_same_optimal_chunk ?
23806140ef00SDan McDonald 	    sbd_write_same_optimal_chunk : (uint32_t)len;
23816140ef00SDan McDonald 	xfer_size = scmd->trans_data_len;
23826140ef00SDan McDonald 
23836140ef00SDan McDonald 	/*
23846140ef00SDan McDonald 	 * All transfers should be an integral multiple of the sector size.
23856140ef00SDan McDonald 	 */
23866140ef00SDan McDonald 	ASSERT((big_buf_size % xfer_size) == 0);
23876140ef00SDan McDonald 
23886140ef00SDan McDonald 	/*
23896140ef00SDan McDonald 	 * Don't sleep for the allocation, and don't make the system
23906140ef00SDan McDonald 	 * reclaim memory.  Trade higher I/Os if in a low-memory situation.
23916140ef00SDan McDonald 	 */
2392*ca783257SDan McDonald 	big_buf = kmem_alloc(big_buf_size, KM_NOSLEEP_LAZY);
23936140ef00SDan McDonald 
23946140ef00SDan McDonald 	if (big_buf == NULL) {
23956140ef00SDan McDonald 		/*
23966140ef00SDan McDonald 		 * Just send it in terms of of the transmitted data.  This
23976140ef00SDan McDonald 		 * will be very slow.
23986140ef00SDan McDonald 		 */
23996140ef00SDan McDonald 		DTRACE_PROBE1(write__same__low__memory, uint64_t, big_buf_size);
24006140ef00SDan McDonald 		big_buf = scmd->trans_data;
24016140ef00SDan McDonald 		big_buf_size = scmd->trans_data_len;
24026140ef00SDan McDonald 	} else {
24036140ef00SDan McDonald 		/*
24046140ef00SDan McDonald 		 * We already ASSERT()ed big_buf_size is an integral multiple
24056140ef00SDan McDonald 		 * of xfer_size.
24066140ef00SDan McDonald 		 */
24076140ef00SDan McDonald 		for (off = 0; off < big_buf_size; off += xfer_size)
24086140ef00SDan McDonald 			bcopy(scmd->trans_data, big_buf + off, xfer_size);
24096140ef00SDan McDonald 	}
24106140ef00SDan McDonald 
24116140ef00SDan McDonald 	/* Do the actual I/O.  Recycle xfer_size now to be write size. */
24126140ef00SDan McDonald 	DTRACE_PROBE1(write__same__io__begin, uint64_t, len);
24136140ef00SDan McDonald 	for (sz_done = 0; sz_done < len; sz_done += (uint64_t)xfer_size) {
24146140ef00SDan McDonald 		xfer_size = ((big_buf_size + sz_done) <= len) ? big_buf_size :
24156140ef00SDan McDonald 		    len - sz_done;
24166140ef00SDan McDonald 		ret = sbd_data_write(sl, task, addr + sz_done,
24176140ef00SDan McDonald 		    (uint64_t)xfer_size, big_buf);
24186140ef00SDan McDonald 		if (ret != SBD_SUCCESS)
24196140ef00SDan McDonald 			break;
24206140ef00SDan McDonald 	}
24216140ef00SDan McDonald 	DTRACE_PROBE2(write__same__io__end, uint64_t, len, uint64_t, sz_done);
24226140ef00SDan McDonald 
24236140ef00SDan McDonald 	if (big_buf != scmd->trans_data)
24246140ef00SDan McDonald 		kmem_free(big_buf, big_buf_size);
24256140ef00SDan McDonald 
24266140ef00SDan McDonald 	return (ret);
24276140ef00SDan McDonald }
24286140ef00SDan McDonald 
242961dfa509SRick McNeal static void
sbd_write_same_release_resources(struct scsi_task * task)243061dfa509SRick McNeal sbd_write_same_release_resources(struct scsi_task *task)
243161dfa509SRick McNeal {
243261dfa509SRick McNeal 	sbd_cmd_t *scmd = (sbd_cmd_t *)task->task_lu_private;
243361dfa509SRick McNeal 
243461dfa509SRick McNeal 	if (scmd->nbufs == 0XFF)
243561dfa509SRick McNeal 		cmn_err(CE_WARN, "%s invalid buffer count %x",
243661dfa509SRick McNeal 		    __func__, scmd->nbufs);
243761dfa509SRick McNeal 	if ((scmd->trans_data_len != 0) && (scmd->trans_data != NULL))
243861dfa509SRick McNeal 		kmem_free(scmd->trans_data, scmd->trans_data_len);
243961dfa509SRick McNeal 	scmd->trans_data = NULL;
244061dfa509SRick McNeal 	scmd->trans_data_len = 0;
244161dfa509SRick McNeal 	scmd->flags &= ~SBD_SCSI_CMD_TRANS_DATA;
244261dfa509SRick McNeal }
244361dfa509SRick McNeal 
24446140ef00SDan McDonald static void
sbd_handle_write_same_xfer_completion(struct scsi_task * task,sbd_cmd_t * scmd,struct stmf_data_buf * dbuf,uint8_t dbuf_reusable)24456140ef00SDan McDonald sbd_handle_write_same_xfer_completion(struct scsi_task *task, sbd_cmd_t *scmd,
24466140ef00SDan McDonald     struct stmf_data_buf *dbuf, uint8_t dbuf_reusable)
24476140ef00SDan McDonald {
24486140ef00SDan McDonald 	uint64_t laddr;
24496140ef00SDan McDonald 	uint32_t buflen, iolen;
24506140ef00SDan McDonald 	int ndx, ret;
24516140ef00SDan McDonald 
245261dfa509SRick McNeal 	if (ATOMIC8_GET(scmd->nbufs) > 0) {
245361dfa509SRick McNeal 		atomic_dec_8(&scmd->nbufs);
245461dfa509SRick McNeal 	}
245561dfa509SRick McNeal 
24566140ef00SDan McDonald 	if (dbuf->db_xfer_status != STMF_SUCCESS) {
245761dfa509SRick McNeal 		sbd_write_same_release_resources(task);
245861dfa509SRick McNeal 		sbd_ats_remove_by_task(task);
24596140ef00SDan McDonald 		stmf_abort(STMF_QUEUE_TASK_ABORT, task,
24606140ef00SDan McDonald 		    dbuf->db_xfer_status, NULL);
24616140ef00SDan McDonald 		return;
24626140ef00SDan McDonald 	}
24636140ef00SDan McDonald 
24646140ef00SDan McDonald 	if (scmd->flags & SBD_SCSI_CMD_XFER_FAIL) {
24656140ef00SDan McDonald 		goto write_same_xfer_done;
24666140ef00SDan McDonald 	}
24676140ef00SDan McDonald 
246861dfa509SRick McNeal 	/* if this is a unnessary callback just return */
246961dfa509SRick McNeal 	if (((scmd->flags & SBD_SCSI_CMD_TRANS_DATA) == 0) ||
247061dfa509SRick McNeal 	    ((scmd->flags & SBD_SCSI_CMD_ACTIVE) == 0) ||
247161dfa509SRick McNeal 	    (scmd->trans_data == NULL)) {
247261dfa509SRick McNeal 		sbd_ats_remove_by_task(task);
247361dfa509SRick McNeal 		scmd->flags &= ~SBD_SCSI_CMD_ACTIVE;
247461dfa509SRick McNeal 		return;
247561dfa509SRick McNeal 	}
247661dfa509SRick McNeal 
247761dfa509SRick McNeal 	if (ATOMIC32_GET(scmd->len) != 0) {
24786140ef00SDan McDonald 		/*
24796140ef00SDan McDonald 		 * Initiate the next port xfer to occur in parallel
24806140ef00SDan McDonald 		 * with writing this buf.
24816140ef00SDan McDonald 		 */
24826140ef00SDan McDonald 		sbd_do_write_same_xfer(task, scmd, NULL, 0);
24836140ef00SDan McDonald 	}
24846140ef00SDan McDonald 
24856140ef00SDan McDonald 	laddr = dbuf->db_relative_offset;
24866140ef00SDan McDonald 
24876140ef00SDan McDonald 	for (buflen = 0, ndx = 0; (buflen < dbuf->db_data_size) &&
24886140ef00SDan McDonald 	    (ndx < dbuf->db_sglist_length); ndx++) {
24896140ef00SDan McDonald 		iolen = min(dbuf->db_data_size - buflen,
24906140ef00SDan McDonald 		    dbuf->db_sglist[ndx].seg_length);
24916140ef00SDan McDonald 		if (iolen == 0)
24926140ef00SDan McDonald 			break;
24936140ef00SDan McDonald 		bcopy(dbuf->db_sglist[ndx].seg_addr, &scmd->trans_data[laddr],
24946140ef00SDan McDonald 		    iolen);
24956140ef00SDan McDonald 		buflen += iolen;
24966140ef00SDan McDonald 		laddr += (uint64_t)iolen;
24976140ef00SDan McDonald 	}
24986140ef00SDan McDonald 	task->task_nbytes_transferred += buflen;
24996140ef00SDan McDonald 
25006140ef00SDan McDonald write_same_xfer_done:
250161dfa509SRick McNeal 	if (ATOMIC32_GET(scmd->len) == 0 ||
250261dfa509SRick McNeal 	    scmd->flags & SBD_SCSI_CMD_XFER_FAIL) {
25036140ef00SDan McDonald 		stmf_free_dbuf(task, dbuf);
250461dfa509SRick McNeal 		if (ATOMIC8_GET(scmd->nbufs) > 0)
250561dfa509SRick McNeal 			return;
25066140ef00SDan McDonald 		scmd->flags &= ~SBD_SCSI_CMD_ACTIVE;
25076140ef00SDan McDonald 		if (scmd->flags & SBD_SCSI_CMD_XFER_FAIL) {
250861dfa509SRick McNeal 			sbd_ats_remove_by_task(task);
250961dfa509SRick McNeal 			sbd_write_same_release_resources(task);
25106140ef00SDan McDonald 			stmf_scsilib_send_status(task, STATUS_CHECK,
25116140ef00SDan McDonald 			    STMF_SAA_WRITE_ERROR);
25126140ef00SDan McDonald 		} else {
25136140ef00SDan McDonald 			ret = sbd_write_same_data(task, scmd);
251461dfa509SRick McNeal 			sbd_ats_remove_by_task(task);
251561dfa509SRick McNeal 			sbd_write_same_release_resources(task);
25166140ef00SDan McDonald 			if (ret != SBD_SUCCESS) {
25176140ef00SDan McDonald 				stmf_scsilib_send_status(task, STATUS_CHECK,
25186140ef00SDan McDonald 				    STMF_SAA_WRITE_ERROR);
25196140ef00SDan McDonald 			} else {
25206140ef00SDan McDonald 				stmf_scsilib_send_status(task, STATUS_GOOD, 0);
25216140ef00SDan McDonald 			}
25226140ef00SDan McDonald 		}
25236140ef00SDan McDonald 		return;
25246140ef00SDan McDonald 	}
25256140ef00SDan McDonald 	sbd_do_write_same_xfer(task, scmd, dbuf, dbuf_reusable);
25266140ef00SDan McDonald }
25276140ef00SDan McDonald 
25286140ef00SDan McDonald static void
sbd_do_write_same_xfer(struct scsi_task * task,sbd_cmd_t * scmd,struct stmf_data_buf * dbuf,uint8_t dbuf_reusable)25296140ef00SDan McDonald sbd_do_write_same_xfer(struct scsi_task *task, sbd_cmd_t *scmd,
25306140ef00SDan McDonald     struct stmf_data_buf *dbuf, uint8_t dbuf_reusable)
25316140ef00SDan McDonald {
25326140ef00SDan McDonald 	uint32_t len;
25336140ef00SDan McDonald 
253461dfa509SRick McNeal 	if (ATOMIC32_GET(scmd->len) == 0) {
25356140ef00SDan McDonald 		if (dbuf != NULL)
25366140ef00SDan McDonald 			stmf_free_dbuf(task, dbuf);
25376140ef00SDan McDonald 		return;
25386140ef00SDan McDonald 	}
25396140ef00SDan McDonald 
25406140ef00SDan McDonald 	if ((dbuf != NULL) &&
25416140ef00SDan McDonald 	    ((dbuf->db_flags & DB_DONT_REUSE) || (dbuf_reusable == 0))) {
25426140ef00SDan McDonald 		/* free current dbuf and allocate a new one */
25436140ef00SDan McDonald 		stmf_free_dbuf(task, dbuf);
25446140ef00SDan McDonald 		dbuf = NULL;
25456140ef00SDan McDonald 	}
25466140ef00SDan McDonald 	if (dbuf == NULL) {
25476140ef00SDan McDonald 		uint32_t maxsize, minsize, old_minsize;
25486140ef00SDan McDonald 
254961dfa509SRick McNeal 		maxsize = (ATOMIC32_GET(scmd->len) > (128*1024)) ? 128*1024 :
255061dfa509SRick McNeal 		    ATOMIC32_GET(scmd->len);
25516140ef00SDan McDonald 		minsize = maxsize >> 2;
25526140ef00SDan McDonald 		do {
25536140ef00SDan McDonald 			old_minsize = minsize;
25546140ef00SDan McDonald 			dbuf = stmf_alloc_dbuf(task, maxsize, &minsize, 0);
25556140ef00SDan McDonald 		} while ((dbuf == NULL) && (old_minsize > minsize) &&
25566140ef00SDan McDonald 		    (minsize >= 512));
25576140ef00SDan McDonald 		if (dbuf == NULL) {
255861dfa509SRick McNeal 			sbd_ats_remove_by_task(task);
255961dfa509SRick McNeal 			sbd_write_same_release_resources(task);
256061dfa509SRick McNeal 			if (ATOMIC8_GET(scmd->nbufs) == 0) {
25616140ef00SDan McDonald 				stmf_abort(STMF_QUEUE_TASK_ABORT, task,
25626140ef00SDan McDonald 				    STMF_ALLOC_FAILURE, NULL);
25636140ef00SDan McDonald 			}
25646140ef00SDan McDonald 			return;
25656140ef00SDan McDonald 		}
25666140ef00SDan McDonald 	}
25676140ef00SDan McDonald 
256861dfa509SRick McNeal 	len = ATOMIC32_GET(scmd->len) > dbuf->db_buf_size ? dbuf->db_buf_size :
256961dfa509SRick McNeal 	    ATOMIC32_GET(scmd->len);
25706140ef00SDan McDonald 
25716140ef00SDan McDonald 	dbuf->db_relative_offset = scmd->current_ro;
25726140ef00SDan McDonald 	dbuf->db_data_size = len;
25736140ef00SDan McDonald 	dbuf->db_flags = DB_DIRECTION_FROM_RPORT;
25746140ef00SDan McDonald 	(void) stmf_xfer_data(task, dbuf, 0);
257561dfa509SRick McNeal 	/* outstanding port xfers and bufs used */
257661dfa509SRick McNeal 	atomic_inc_8(&scmd->nbufs);
257761dfa509SRick McNeal 	atomic_add_32(&scmd->len, -len);
25786140ef00SDan McDonald 	scmd->current_ro += len;
25796140ef00SDan McDonald }
25806140ef00SDan McDonald 
2581b77b9231SDan McDonald static void
sbd_handle_write_same(scsi_task_t * task,struct stmf_data_buf * initial_dbuf)25826140ef00SDan McDonald sbd_handle_write_same(scsi_task_t *task, struct stmf_data_buf *initial_dbuf)
2583b77b9231SDan McDonald {
2584b77b9231SDan McDonald 	sbd_lu_t *sl = (sbd_lu_t *)task->task_lu->lu_provider_private;
2585b77b9231SDan McDonald 	uint64_t addr, len;
25866140ef00SDan McDonald 	sbd_cmd_t *scmd;
25876140ef00SDan McDonald 	stmf_data_buf_t *dbuf;
25886140ef00SDan McDonald 	uint8_t unmap;
25896140ef00SDan McDonald 	uint8_t do_immediate_data = 0;
2590b77b9231SDan McDonald 
259161dfa509SRick McNeal 	if (HardwareAcceleratedInit == 0) {
259261dfa509SRick McNeal 		stmf_scsilib_send_status(task, STATUS_CHECK,
259361dfa509SRick McNeal 		    STMF_SAA_INVALID_OPCODE);
259461dfa509SRick McNeal 		return;
259561dfa509SRick McNeal 	}
259661dfa509SRick McNeal 
2597b77b9231SDan McDonald 	task->task_cmd_xfer_length = 0;
2598b77b9231SDan McDonald 	if (task->task_additional_flags &
2599b77b9231SDan McDonald 	    TASK_AF_NO_EXPECTED_XFER_LENGTH) {
2600b77b9231SDan McDonald 		task->task_expected_xfer_length = 0;
2601b77b9231SDan McDonald 	}
26026140ef00SDan McDonald 	if (sl->sl_flags & SL_WRITE_PROTECTED) {
26036140ef00SDan McDonald 		stmf_scsilib_send_status(task, STATUS_CHECK,
26046140ef00SDan McDonald 		    STMF_SAA_WRITE_PROTECTED);
26056140ef00SDan McDonald 		return;
26066140ef00SDan McDonald 	}
2607b77b9231SDan McDonald 	if (task->task_cdb[1] & 0xF7) {
2608b77b9231SDan McDonald 		stmf_scsilib_send_status(task, STATUS_CHECK,
2609b77b9231SDan McDonald 		    STMF_SAA_INVALID_FIELD_IN_CDB);
2610b77b9231SDan McDonald 		return;
2611b77b9231SDan McDonald 	}
26126140ef00SDan McDonald 	unmap = task->task_cdb[1] & 0x08;
261361dfa509SRick McNeal 
26146140ef00SDan McDonald 	if (unmap && ((sl->sl_flags & SL_UNMAP_ENABLED) == 0)) {
26156140ef00SDan McDonald 		stmf_scsilib_send_status(task, STATUS_CHECK,
26166140ef00SDan McDonald 		    STMF_SAA_INVALID_FIELD_IN_CDB);
26176140ef00SDan McDonald 		return;
26186140ef00SDan McDonald 	}
261961dfa509SRick McNeal 
26206140ef00SDan McDonald 	if (task->task_cdb[0] == SCMD_WRITE_SAME_G1) {
26216140ef00SDan McDonald 		addr = READ_SCSI32(&task->task_cdb[2], uint64_t);
26226140ef00SDan McDonald 		len = READ_SCSI16(&task->task_cdb[7], uint64_t);
26236140ef00SDan McDonald 	} else {
26246140ef00SDan McDonald 		addr = READ_SCSI64(&task->task_cdb[2], uint64_t);
26256140ef00SDan McDonald 		len = READ_SCSI32(&task->task_cdb[10], uint64_t);
26266140ef00SDan McDonald 	}
262761dfa509SRick McNeal 
26286140ef00SDan McDonald 	if (len == 0) {
26296140ef00SDan McDonald 		stmf_scsilib_send_status(task, STATUS_CHECK,
26306140ef00SDan McDonald 		    STMF_SAA_INVALID_FIELD_IN_CDB);
26316140ef00SDan McDonald 		return;
26326140ef00SDan McDonald 	}
263361dfa509SRick McNeal 
263461dfa509SRick McNeal 	if (sbd_ats_handling_before_io(task, sl, addr, len) !=
263561dfa509SRick McNeal 	    SBD_SUCCESS) {
263661dfa509SRick McNeal 		if (stmf_task_poll_lu(task, 10) != STMF_SUCCESS)
263761dfa509SRick McNeal 			stmf_scsilib_send_status(task, STATUS_BUSY, 0);
263861dfa509SRick McNeal 		return;
263961dfa509SRick McNeal 	}
264061dfa509SRick McNeal 
2641b77b9231SDan McDonald 	addr <<= sl->sl_data_blocksize_shift;
2642b77b9231SDan McDonald 	len <<= sl->sl_data_blocksize_shift;
2643b77b9231SDan McDonald 
26446140ef00SDan McDonald 	/* Check if the command is for the unmap function */
26456140ef00SDan McDonald 	if (unmap) {
2646047c81d3SSaso Kiselkov 		dkioc_free_list_t *dfl = kmem_zalloc(DFL_SZ(1), KM_SLEEP);
2647047c81d3SSaso Kiselkov 
2648047c81d3SSaso Kiselkov 		dfl->dfl_num_exts = 1;
2649047c81d3SSaso Kiselkov 		dfl->dfl_exts[0].dfle_start = addr;
2650047c81d3SSaso Kiselkov 		dfl->dfl_exts[0].dfle_length = len;
2651047c81d3SSaso Kiselkov 		if (sbd_unmap(sl, dfl) != 0) {
26526140ef00SDan McDonald 			stmf_scsilib_send_status(task, STATUS_CHECK,
26536140ef00SDan McDonald 			    STMF_SAA_LBA_OUT_OF_RANGE);
26546140ef00SDan McDonald 		} else {
26556140ef00SDan McDonald 			stmf_scsilib_send_status(task, STATUS_GOOD, 0);
26566140ef00SDan McDonald 		}
2657047c81d3SSaso Kiselkov 		dfl_free(dfl);
26586140ef00SDan McDonald 		return;
26596140ef00SDan McDonald 	}
26606140ef00SDan McDonald 
26616140ef00SDan McDonald 	/* Write same function */
26626140ef00SDan McDonald 
26636140ef00SDan McDonald 	task->task_cmd_xfer_length = 1 << sl->sl_data_blocksize_shift;
26646140ef00SDan McDonald 	if (task->task_additional_flags &
26656140ef00SDan McDonald 	    TASK_AF_NO_EXPECTED_XFER_LENGTH) {
26666140ef00SDan McDonald 		task->task_expected_xfer_length = task->task_cmd_xfer_length;
26676140ef00SDan McDonald 	}
26686140ef00SDan McDonald 	if ((addr + len) > sl->sl_lu_size) {
266961dfa509SRick McNeal 		sbd_ats_remove_by_task(task);
2670b77b9231SDan McDonald 		stmf_scsilib_send_status(task, STATUS_CHECK,
2671b77b9231SDan McDonald 		    STMF_SAA_LBA_OUT_OF_RANGE);
2672b77b9231SDan McDonald 		return;
2673b77b9231SDan McDonald 	}
26746140ef00SDan McDonald 
26756140ef00SDan McDonald 	/* For rest of this I/O the transfer length is 1 block */
26766140ef00SDan McDonald 	len = ((uint64_t)1) << sl->sl_data_blocksize_shift;
26776140ef00SDan McDonald 
26786140ef00SDan McDonald 	/* Some basic checks */
26796140ef00SDan McDonald 	if ((len == 0) || (len != task->task_expected_xfer_length)) {
268061dfa509SRick McNeal 		sbd_ats_remove_by_task(task);
26816140ef00SDan McDonald 		stmf_scsilib_send_status(task, STATUS_CHECK,
26826140ef00SDan McDonald 		    STMF_SAA_INVALID_FIELD_IN_CDB);
26836140ef00SDan McDonald 		return;
26846140ef00SDan McDonald 	}
26856140ef00SDan McDonald 
26866140ef00SDan McDonald 
26876140ef00SDan McDonald 	if ((initial_dbuf != NULL) && (task->task_flags & TF_INITIAL_BURST)) {
26886140ef00SDan McDonald 		if (initial_dbuf->db_data_size > len) {
26896140ef00SDan McDonald 			if (initial_dbuf->db_data_size >
26906140ef00SDan McDonald 			    task->task_expected_xfer_length) {
26916140ef00SDan McDonald 				/* protocol error */
269261dfa509SRick McNeal 				sbd_ats_remove_by_task(task);
26936140ef00SDan McDonald 				stmf_abort(STMF_QUEUE_TASK_ABORT, task,
26946140ef00SDan McDonald 				    STMF_INVALID_ARG, NULL);
26956140ef00SDan McDonald 				return;
26966140ef00SDan McDonald 			}
26976140ef00SDan McDonald 			initial_dbuf->db_data_size = (uint32_t)len;
26986140ef00SDan McDonald 		}
26996140ef00SDan McDonald 		do_immediate_data = 1;
27006140ef00SDan McDonald 	}
27016140ef00SDan McDonald 	dbuf = initial_dbuf;
27026140ef00SDan McDonald 
27036140ef00SDan McDonald 	if (task->task_lu_private) {
27046140ef00SDan McDonald 		scmd = (sbd_cmd_t *)task->task_lu_private;
27056140ef00SDan McDonald 	} else {
27066140ef00SDan McDonald 		scmd = (sbd_cmd_t *)kmem_alloc(sizeof (sbd_cmd_t), KM_SLEEP);
27076140ef00SDan McDonald 		task->task_lu_private = scmd;
27086140ef00SDan McDonald 	}
270961dfa509SRick McNeal 	scmd->flags = SBD_SCSI_CMD_ACTIVE | SBD_SCSI_CMD_TRANS_DATA |
271061dfa509SRick McNeal 	    SBD_SCSI_CMD_ATS_RELATED;
27116140ef00SDan McDonald 	scmd->cmd_type = SBD_CMD_SCSI_WRITE;
27126140ef00SDan McDonald 	scmd->nbufs = 0;
27136140ef00SDan McDonald 	scmd->len = (uint32_t)len;
27146140ef00SDan McDonald 	scmd->trans_data_len = (uint32_t)len;
27156140ef00SDan McDonald 	scmd->trans_data = kmem_alloc((size_t)len, KM_SLEEP);
27166140ef00SDan McDonald 	scmd->current_ro = 0;
27176140ef00SDan McDonald 
27186140ef00SDan McDonald 	if (do_immediate_data) {
27196140ef00SDan McDonald 		/*
27206140ef00SDan McDonald 		 * Account for data passed in this write command
27216140ef00SDan McDonald 		 */
27226140ef00SDan McDonald 		(void) stmf_xfer_data(task, dbuf, STMF_IOF_STATS_ONLY);
272361dfa509SRick McNeal 		atomic_add_32(&scmd->len, -dbuf->db_data_size);
27246140ef00SDan McDonald 		scmd->current_ro += dbuf->db_data_size;
27256140ef00SDan McDonald 		dbuf->db_xfer_status = STMF_SUCCESS;
27266140ef00SDan McDonald 		sbd_handle_write_same_xfer_completion(task, scmd, dbuf, 0);
27276140ef00SDan McDonald 	} else {
27286140ef00SDan McDonald 		sbd_do_write_same_xfer(task, scmd, dbuf, 0);
27296140ef00SDan McDonald 	}
2730b77b9231SDan McDonald }
2731b77b9231SDan McDonald 
2732b77b9231SDan McDonald static void
sbd_handle_unmap(scsi_task_t * task,stmf_data_buf_t * dbuf)2733b77b9231SDan McDonald sbd_handle_unmap(scsi_task_t *task, stmf_data_buf_t *dbuf)
2734b77b9231SDan McDonald {
273561dfa509SRick McNeal 	sbd_lu_t *sl = (sbd_lu_t *)task->task_lu->lu_provider_private;
2736b77b9231SDan McDonald 	uint32_t cmd_xfer_len;
2737b77b9231SDan McDonald 
273861dfa509SRick McNeal 	if (sbd_unmap_enable == 0) {
273961dfa509SRick McNeal 		stmf_scsilib_send_status(task, STATUS_CHECK,
274061dfa509SRick McNeal 		    STMF_SAA_INVALID_OPCODE);
274161dfa509SRick McNeal 		return;
274261dfa509SRick McNeal 	}
274361dfa509SRick McNeal 
274461dfa509SRick McNeal 	if (sl->sl_flags & SL_WRITE_PROTECTED) {
274561dfa509SRick McNeal 		stmf_scsilib_send_status(task, STATUS_CHECK,
274661dfa509SRick McNeal 		    STMF_SAA_WRITE_PROTECTED);
274761dfa509SRick McNeal 		return;
274861dfa509SRick McNeal 	}
2749b77b9231SDan McDonald 	cmd_xfer_len = READ_SCSI16(&task->task_cdb[7], uint32_t);
2750b77b9231SDan McDonald 
2751b77b9231SDan McDonald 	if (task->task_cdb[1] & 1) {
2752b77b9231SDan McDonald 		stmf_scsilib_send_status(task, STATUS_CHECK,
2753b77b9231SDan McDonald 		    STMF_SAA_INVALID_FIELD_IN_CDB);
2754b77b9231SDan McDonald 		return;
2755b77b9231SDan McDonald 	}
2756b77b9231SDan McDonald 
2757b77b9231SDan McDonald 	if (cmd_xfer_len == 0) {
2758b77b9231SDan McDonald 		task->task_cmd_xfer_length = 0;
2759b77b9231SDan McDonald 		if (task->task_additional_flags &
2760b77b9231SDan McDonald 		    TASK_AF_NO_EXPECTED_XFER_LENGTH) {
2761b77b9231SDan McDonald 			task->task_expected_xfer_length = 0;
2762b77b9231SDan McDonald 		}
2763b77b9231SDan McDonald 		stmf_scsilib_send_status(task, STATUS_GOOD, 0);
2764b77b9231SDan McDonald 		return;
2765b77b9231SDan McDonald 	}
2766b77b9231SDan McDonald 
2767b77b9231SDan McDonald 	sbd_handle_short_write_transfers(task, dbuf, cmd_xfer_len);
2768b77b9231SDan McDonald }
2769b77b9231SDan McDonald 
2770b77b9231SDan McDonald static void
sbd_handle_unmap_xfer(scsi_task_t * task,uint8_t * buf,uint32_t buflen)2771b77b9231SDan McDonald sbd_handle_unmap_xfer(scsi_task_t *task, uint8_t *buf, uint32_t buflen)
2772b77b9231SDan McDonald {
2773b77b9231SDan McDonald 	sbd_lu_t *sl = (sbd_lu_t *)task->task_lu->lu_provider_private;
2774b77b9231SDan McDonald 	uint32_t ulen, dlen, num_desc;
2775b77b9231SDan McDonald 	uint64_t addr, len;
2776b77b9231SDan McDonald 	uint8_t *p;
2777047c81d3SSaso Kiselkov 	dkioc_free_list_t *dfl;
2778b77b9231SDan McDonald 	int ret;
2779047c81d3SSaso Kiselkov 	int i;
2780b77b9231SDan McDonald 
2781b77b9231SDan McDonald 	if (buflen < 24) {
2782b77b9231SDan McDonald 		stmf_scsilib_send_status(task, STATUS_CHECK,
2783b77b9231SDan McDonald 		    STMF_SAA_INVALID_FIELD_IN_CDB);
2784b77b9231SDan McDonald 		return;
2785b77b9231SDan McDonald 	}
2786b77b9231SDan McDonald 	ulen = READ_SCSI16(buf, uint32_t);
2787b77b9231SDan McDonald 	dlen = READ_SCSI16(buf + 2, uint32_t);
2788b77b9231SDan McDonald 	num_desc = dlen >> 4;
2789b77b9231SDan McDonald 	if (((ulen + 2) != buflen) || ((dlen + 8) != buflen) || (dlen & 0xf) ||
2790b77b9231SDan McDonald 	    (num_desc == 0)) {
2791b77b9231SDan McDonald 		stmf_scsilib_send_status(task, STATUS_CHECK,
2792b77b9231SDan McDonald 		    STMF_SAA_INVALID_FIELD_IN_CDB);
2793b77b9231SDan McDonald 		return;
2794b77b9231SDan McDonald 	}
2795b77b9231SDan McDonald 
2796047c81d3SSaso Kiselkov 	dfl = kmem_zalloc(DFL_SZ(num_desc), KM_SLEEP);
2797047c81d3SSaso Kiselkov 	dfl->dfl_num_exts = num_desc;
279861dfa509SRick McNeal 	/*
279961dfa509SRick McNeal 	 * This should use ATS locking but that was disabled by the
280061dfa509SRick McNeal 	 * changes to ZFS top take advantage of TRIM in SSDs.
280161dfa509SRick McNeal 	 *
280261dfa509SRick McNeal 	 * Since the entire list is passed to ZFS in one list ATS
280361dfa509SRick McNeal 	 * locking is not done.  This may be detectable, and if it is
280461dfa509SRick McNeal 	 * then the entire list needs to be locked and then after the
280561dfa509SRick McNeal 	 * unmap completes the entire list must be unlocked
280661dfa509SRick McNeal 	 */
2807047c81d3SSaso Kiselkov 	for (p = buf + 8, i = 0; num_desc; num_desc--, p += 16, i++) {
2808b77b9231SDan McDonald 		addr = READ_SCSI64(p, uint64_t);
2809b77b9231SDan McDonald 		len = READ_SCSI32(p+8, uint64_t);
281061dfa509SRick McNeal 		addr <<= sl->sl_data_blocksize_shift;
2811b77b9231SDan McDonald 		len <<= sl->sl_data_blocksize_shift;
281261dfa509SRick McNeal 
2813047c81d3SSaso Kiselkov 		/* Prepare a list of extents to unmap */
2814047c81d3SSaso Kiselkov 		dfl->dfl_exts[i].dfle_start = addr;
2815047c81d3SSaso Kiselkov 		dfl->dfl_exts[i].dfle_length = len;
281661dfa509SRick McNeal 
281761dfa509SRick McNeal 		/* release the overlap */
2818047c81d3SSaso Kiselkov 	}
2819047c81d3SSaso Kiselkov 	ASSERT(i == dfl->dfl_num_exts);
2820047c81d3SSaso Kiselkov 
2821047c81d3SSaso Kiselkov 	/* Finally execute the unmap operations in a single step */
2822047c81d3SSaso Kiselkov 	ret = sbd_unmap(sl, dfl);
2823047c81d3SSaso Kiselkov 	dfl_free(dfl);
2824047c81d3SSaso Kiselkov 	if (ret != 0) {
2825047c81d3SSaso Kiselkov 		stmf_scsilib_send_status(task, STATUS_CHECK,
2826047c81d3SSaso Kiselkov 		    STMF_SAA_LBA_OUT_OF_RANGE);
2827047c81d3SSaso Kiselkov 		return;
2828b77b9231SDan McDonald 	}
2829b77b9231SDan McDonald 
2830b77b9231SDan McDonald 	stmf_scsilib_send_status(task, STATUS_GOOD, 0);
2831b77b9231SDan McDonald }
2832b77b9231SDan McDonald 
2833fcf3ce44SJohn Forte void
sbd_handle_inquiry(struct scsi_task * task,struct stmf_data_buf * initial_dbuf)28342f624233SNattuvetty Bhavyan sbd_handle_inquiry(struct scsi_task *task, struct stmf_data_buf *initial_dbuf)
2835fcf3ce44SJohn Forte {
28368fe96085Stim szeto 	sbd_lu_t *sl = (sbd_lu_t *)task->task_lu->lu_provider_private;
28378fe96085Stim szeto 	uint8_t *cdbp = (uint8_t *)&task->task_cdb[0];
28382f624233SNattuvetty Bhavyan 	uint8_t *p;
28398fe96085Stim szeto 	uint8_t byte0;
28402f624233SNattuvetty Bhavyan 	uint8_t page_length;
28412f624233SNattuvetty Bhavyan 	uint16_t bsize = 512;
28422f624233SNattuvetty Bhavyan 	uint16_t cmd_size;
28432f624233SNattuvetty Bhavyan 	uint32_t xfer_size = 4;
28442f624233SNattuvetty Bhavyan 	uint32_t mgmt_url_size = 0;
2845b77b9231SDan McDonald 	uint8_t exp;
2846b77b9231SDan McDonald 	uint64_t s;
28477beff157SJohn Forte 	char *mgmt_url = NULL;
28482f624233SNattuvetty Bhavyan 
2849fcf3ce44SJohn Forte 
28508fe96085Stim szeto 	byte0 = DTYPE_DIRECT;
2851fcf3ce44SJohn Forte 	/*
2852fcf3ce44SJohn Forte 	 * Basic protocol checks.
2853fcf3ce44SJohn Forte 	 */
2854fcf3ce44SJohn Forte 
2855fcf3ce44SJohn Forte 	if ((((cdbp[1] & 1) == 0) && cdbp[2]) || cdbp[5]) {
2856fcf3ce44SJohn Forte 		stmf_scsilib_send_status(task, STATUS_CHECK,
2857fcf3ce44SJohn Forte 		    STMF_SAA_INVALID_FIELD_IN_CDB);
2858fcf3ce44SJohn Forte 		return;
2859fcf3ce44SJohn Forte 	}
2860fcf3ce44SJohn Forte 
2861fcf3ce44SJohn Forte 	/*
2862fcf3ce44SJohn Forte 	 * Zero byte allocation length is not an error.  Just
2863fcf3ce44SJohn Forte 	 * return success.
2864fcf3ce44SJohn Forte 	 */
2865fcf3ce44SJohn Forte 
28662f624233SNattuvetty Bhavyan 	cmd_size = (((uint16_t)cdbp[3]) << 8) | cdbp[4];
2867fcf3ce44SJohn Forte 
2868fcf3ce44SJohn Forte 	if (cmd_size == 0) {
2869fcf3ce44SJohn Forte 		task->task_cmd_xfer_length = 0;
2870fcf3ce44SJohn Forte 		if (task->task_additional_flags &
2871fcf3ce44SJohn Forte 		    TASK_AF_NO_EXPECTED_XFER_LENGTH) {
2872fcf3ce44SJohn Forte 			task->task_expected_xfer_length = 0;
2873fcf3ce44SJohn Forte 		}
2874fcf3ce44SJohn Forte 		stmf_scsilib_send_status(task, STATUS_GOOD, 0);
2875fcf3ce44SJohn Forte 		return;
2876fcf3ce44SJohn Forte 	}
2877fcf3ce44SJohn Forte 
2878fcf3ce44SJohn Forte 	/*
2879fcf3ce44SJohn Forte 	 * Standard inquiry
2880fcf3ce44SJohn Forte 	 */
2881fcf3ce44SJohn Forte 
2882fcf3ce44SJohn Forte 	if ((cdbp[1] & 1) == 0) {
28832f624233SNattuvetty Bhavyan 		int	i;
28842f624233SNattuvetty Bhavyan 		struct scsi_inquiry *inq;
28852f624233SNattuvetty Bhavyan 
28862f624233SNattuvetty Bhavyan 		p = (uint8_t *)kmem_zalloc(bsize, KM_SLEEP);
28872f624233SNattuvetty Bhavyan 		inq = (struct scsi_inquiry *)p;
2888fcf3ce44SJohn Forte 
28892f624233SNattuvetty Bhavyan 		page_length = 69;
28902f624233SNattuvetty Bhavyan 		xfer_size = page_length + 5;
2891fcf3ce44SJohn Forte 
28928fe96085Stim szeto 		inq->inq_dtype = DTYPE_DIRECT;
2893fcf3ce44SJohn Forte 		inq->inq_ansi = 5;	/* SPC-3 */
2894fcf3ce44SJohn Forte 		inq->inq_hisup = 1;
2895fcf3ce44SJohn Forte 		inq->inq_rdf = 2;	/* Response data format for SPC-3 */
2896fcf3ce44SJohn Forte 		inq->inq_len = page_length;
2897fcf3ce44SJohn Forte 
28982f624233SNattuvetty Bhavyan 		inq->inq_tpgs = TPGS_FAILOVER_IMPLICIT;
2899fcf3ce44SJohn Forte 		inq->inq_cmdque = 1;
290061dfa509SRick McNeal 		inq->inq_3pc = 1;
2901fcf3ce44SJohn Forte 
29028fe96085Stim szeto 		if (sl->sl_flags & SL_VID_VALID) {
29038fe96085Stim szeto 			bcopy(sl->sl_vendor_id, inq->inq_vid, 8);
29048fe96085Stim szeto 		} else {
29058fe96085Stim szeto 			bcopy(sbd_vendor_id, inq->inq_vid, 8);
29068fe96085Stim szeto 		}
29078fe96085Stim szeto 
29088fe96085Stim szeto 		if (sl->sl_flags & SL_PID_VALID) {
29098fe96085Stim szeto 			bcopy(sl->sl_product_id, inq->inq_pid, 16);
29108fe96085Stim szeto 		} else {
29118fe96085Stim szeto 			bcopy(sbd_product_id, inq->inq_pid, 16);
29128fe96085Stim szeto 		}
29138fe96085Stim szeto 
29148fe96085Stim szeto 		if (sl->sl_flags & SL_REV_VALID) {
29158fe96085Stim szeto 			bcopy(sl->sl_revision, inq->inq_revision, 4);
29168fe96085Stim szeto 		} else {
29178fe96085Stim szeto 			bcopy(sbd_revision, inq->inq_revision, 4);
29188fe96085Stim szeto 		}
2919fcf3ce44SJohn Forte 
29202f624233SNattuvetty Bhavyan 		/* Adding Version Descriptors */
29212f624233SNattuvetty Bhavyan 		i = 0;
29222f624233SNattuvetty Bhavyan 		/* SAM-3 no version */
29232f624233SNattuvetty Bhavyan 		inq->inq_vd[i].inq_vd_msb = 0x00;
29242f624233SNattuvetty Bhavyan 		inq->inq_vd[i].inq_vd_lsb = 0x60;
29252f624233SNattuvetty Bhavyan 		i++;
29262f624233SNattuvetty Bhavyan 
29272f624233SNattuvetty Bhavyan 		/* transport */
29282f624233SNattuvetty Bhavyan 		switch (task->task_lport->lport_id->protocol_id) {
29292f624233SNattuvetty Bhavyan 		case PROTOCOL_FIBRE_CHANNEL:
29302f624233SNattuvetty Bhavyan 			inq->inq_vd[i].inq_vd_msb = 0x09;
29312f624233SNattuvetty Bhavyan 			inq->inq_vd[i].inq_vd_lsb = 0x00;
29322f624233SNattuvetty Bhavyan 			i++;
29332f624233SNattuvetty Bhavyan 			break;
29342f624233SNattuvetty Bhavyan 
29352f624233SNattuvetty Bhavyan 		case PROTOCOL_PARALLEL_SCSI:
29362f624233SNattuvetty Bhavyan 		case PROTOCOL_SSA:
29372f624233SNattuvetty Bhavyan 		case PROTOCOL_IEEE_1394:
29382f624233SNattuvetty Bhavyan 			/* Currently no claims of conformance */
29392f624233SNattuvetty Bhavyan 			break;
29402f624233SNattuvetty Bhavyan 
29412f624233SNattuvetty Bhavyan 		case PROTOCOL_SRP:
29422f624233SNattuvetty Bhavyan 			inq->inq_vd[i].inq_vd_msb = 0x09;
29432f624233SNattuvetty Bhavyan 			inq->inq_vd[i].inq_vd_lsb = 0x40;
29442f624233SNattuvetty Bhavyan 			i++;
29452f624233SNattuvetty Bhavyan 			break;
29462f624233SNattuvetty Bhavyan 
29472f624233SNattuvetty Bhavyan 		case PROTOCOL_iSCSI:
29482f624233SNattuvetty Bhavyan 			inq->inq_vd[i].inq_vd_msb = 0x09;
29492f624233SNattuvetty Bhavyan 			inq->inq_vd[i].inq_vd_lsb = 0x60;
29502f624233SNattuvetty Bhavyan 			i++;
29512f624233SNattuvetty Bhavyan 			break;
29522f624233SNattuvetty Bhavyan 
29532f624233SNattuvetty Bhavyan 		case PROTOCOL_SAS:
29542f624233SNattuvetty Bhavyan 		case PROTOCOL_ADT:
29552f624233SNattuvetty Bhavyan 		case PROTOCOL_ATAPI:
29562f624233SNattuvetty Bhavyan 		default:
29572f624233SNattuvetty Bhavyan 			/* Currently no claims of conformance */
29582f624233SNattuvetty Bhavyan 			break;
29592f624233SNattuvetty Bhavyan 		}
29602f624233SNattuvetty Bhavyan 
29612f624233SNattuvetty Bhavyan 		/* SPC-3 no version */
29622f624233SNattuvetty Bhavyan 		inq->inq_vd[i].inq_vd_msb = 0x03;
29632f624233SNattuvetty Bhavyan 		inq->inq_vd[i].inq_vd_lsb = 0x00;
29642f624233SNattuvetty Bhavyan 		i++;
29652f624233SNattuvetty Bhavyan 
29662f624233SNattuvetty Bhavyan 		/* SBC-2 no version */
29672f624233SNattuvetty Bhavyan 		inq->inq_vd[i].inq_vd_msb = 0x03;
29682f624233SNattuvetty Bhavyan 		inq->inq_vd[i].inq_vd_lsb = 0x20;
29692f624233SNattuvetty Bhavyan 
2970fcf3ce44SJohn Forte 		sbd_handle_short_read_transfers(task, initial_dbuf, p, cmd_size,
29712f624233SNattuvetty Bhavyan 		    min(cmd_size, xfer_size));
29722f624233SNattuvetty Bhavyan 		kmem_free(p, bsize);
2973fcf3ce44SJohn Forte 
2974fcf3ce44SJohn Forte 		return;
2975fcf3ce44SJohn Forte 	}
2976fcf3ce44SJohn Forte 
29777beff157SJohn Forte 	rw_enter(&sbd_global_prop_lock, RW_READER);
29787beff157SJohn Forte 	if (sl->sl_mgmt_url) {
29797beff157SJohn Forte 		mgmt_url_size = strlen(sl->sl_mgmt_url);
29807beff157SJohn Forte 		mgmt_url = sl->sl_mgmt_url;
29817beff157SJohn Forte 	} else if (sbd_mgmt_url) {
29827beff157SJohn Forte 		mgmt_url_size = strlen(sbd_mgmt_url);
29837beff157SJohn Forte 		mgmt_url = sbd_mgmt_url;
29847beff157SJohn Forte 	}
29857beff157SJohn Forte 
2986fcf3ce44SJohn Forte 	/*
2987fcf3ce44SJohn Forte 	 * EVPD handling
2988fcf3ce44SJohn Forte 	 */
2989fcf3ce44SJohn Forte 
29902f624233SNattuvetty Bhavyan 	/* Default 512 bytes may not be enough, increase bsize if necessary */
29912f624233SNattuvetty Bhavyan 	if (cdbp[2] == 0x83 || cdbp[2] == 0x85) {
29922f624233SNattuvetty Bhavyan 		if (bsize <  cmd_size)
29932f624233SNattuvetty Bhavyan 			bsize = cmd_size;
29942f624233SNattuvetty Bhavyan 	}
29952f624233SNattuvetty Bhavyan 	p = (uint8_t *)kmem_zalloc(bsize, KM_SLEEP);
29962f624233SNattuvetty Bhavyan 
2997fcf3ce44SJohn Forte 	switch (cdbp[2]) {
2998fcf3ce44SJohn Forte 	case 0x00:
299961dfa509SRick McNeal 		page_length = 5 + (mgmt_url_size ? 1 : 0);
300061dfa509SRick McNeal 
3001b77b9231SDan McDonald 		if (sl->sl_flags & SL_UNMAP_ENABLED)
300261dfa509SRick McNeal 			page_length += 1;
3003fcf3ce44SJohn Forte 
30048fe96085Stim szeto 		p[0] = byte0;
30058fe96085Stim szeto 		p[3] = page_length;
30062f624233SNattuvetty Bhavyan 		/* Supported VPD pages in ascending order */
300761dfa509SRick McNeal 		/* CSTYLED */
30082f624233SNattuvetty Bhavyan 		{
30092f624233SNattuvetty Bhavyan 			uint8_t i = 5;
30102f624233SNattuvetty Bhavyan 
30112f624233SNattuvetty Bhavyan 			p[i++] = 0x80;
30122f624233SNattuvetty Bhavyan 			p[i++] = 0x83;
30132f624233SNattuvetty Bhavyan 			if (mgmt_url_size != 0)
30142f624233SNattuvetty Bhavyan 				p[i++] = 0x85;
30152f624233SNattuvetty Bhavyan 			p[i++] = 0x86;
301661dfa509SRick McNeal 			p[i++] = 0xb0;
3017b77b9231SDan McDonald 			if (sl->sl_flags & SL_UNMAP_ENABLED) {
3018b77b9231SDan McDonald 				p[i++] = 0xb2;
3019b77b9231SDan McDonald 			}
30202f624233SNattuvetty Bhavyan 		}
30212f624233SNattuvetty Bhavyan 		xfer_size = page_length + 4;
3022fcf3ce44SJohn Forte 		break;
3023fcf3ce44SJohn Forte 
30248fe96085Stim szeto 	case 0x80:
30258fe96085Stim szeto 		if (sl->sl_serial_no_size) {
30268fe96085Stim szeto 			page_length = sl->sl_serial_no_size;
30278fe96085Stim szeto 			bcopy(sl->sl_serial_no, p + 4, sl->sl_serial_no_size);
30288fe96085Stim szeto 		} else {
30292f624233SNattuvetty Bhavyan 			/* if no serial num is specified set 4 spaces */
30302f624233SNattuvetty Bhavyan 			page_length = 4;
30318fe96085Stim szeto 			bcopy("    ", p + 4, 4);
30328fe96085Stim szeto 		}
30338fe96085Stim szeto 		p[0] = byte0;
30348fe96085Stim szeto 		p[1] = 0x80;
30358fe96085Stim szeto 		p[3] = page_length;
30362f624233SNattuvetty Bhavyan 		xfer_size = page_length + 4;
30378fe96085Stim szeto 		break;
30388fe96085Stim szeto 
3039fcf3ce44SJohn Forte 	case 0x83:
30402f624233SNattuvetty Bhavyan 		xfer_size = stmf_scsilib_prepare_vpd_page83(task, p,
30418fe96085Stim szeto 		    bsize, byte0, STMF_VPD_LU_ID|STMF_VPD_TARGET_ID|
30422f624233SNattuvetty Bhavyan 		    STMF_VPD_TP_GROUP|STMF_VPD_RELATIVE_TP_ID);
3043fcf3ce44SJohn Forte 		break;
3044fcf3ce44SJohn Forte 
30452f624233SNattuvetty Bhavyan 	case 0x85:
30462f624233SNattuvetty Bhavyan 		if (mgmt_url_size == 0) {
30472f624233SNattuvetty Bhavyan 			stmf_scsilib_send_status(task, STATUS_CHECK,
30482f624233SNattuvetty Bhavyan 			    STMF_SAA_INVALID_FIELD_IN_CDB);
30497beff157SJohn Forte 			goto err_done;
305061dfa509SRick McNeal 		} /* CSTYLED */
30512f624233SNattuvetty Bhavyan 		{
30522f624233SNattuvetty Bhavyan 			uint16_t idx, newidx, sz, url_size;
30532f624233SNattuvetty Bhavyan 			char *url;
30542f624233SNattuvetty Bhavyan 
30552f624233SNattuvetty Bhavyan 			p[0] = byte0;
30562f624233SNattuvetty Bhavyan 			p[1] = 0x85;
30572f624233SNattuvetty Bhavyan 
30582f624233SNattuvetty Bhavyan 			idx = 4;
30597beff157SJohn Forte 			url = mgmt_url;
30602f624233SNattuvetty Bhavyan 			url_size = sbd_parse_mgmt_url(&url);
30612f624233SNattuvetty Bhavyan 			/* Creating Network Service Descriptors */
30622f624233SNattuvetty Bhavyan 			while (url_size != 0) {
30632f624233SNattuvetty Bhavyan 				/* Null terminated and 4 Byte aligned */
30642f624233SNattuvetty Bhavyan 				sz = url_size + 1;
30652f624233SNattuvetty Bhavyan 				sz += (sz % 4) ? 4 - (sz % 4) : 0;
30662f624233SNattuvetty Bhavyan 				newidx = idx + sz + 4;
30672f624233SNattuvetty Bhavyan 
30682f624233SNattuvetty Bhavyan 				if (newidx < bsize) {
30692f624233SNattuvetty Bhavyan 					/*
30702f624233SNattuvetty Bhavyan 					 * SPC-3r23 : Table 320  (Sec 7.6.5)
30712f624233SNattuvetty Bhavyan 					 * (Network service descriptor format
30722f624233SNattuvetty Bhavyan 					 *
30732f624233SNattuvetty Bhavyan 					 * Note: Hard coding service type as
30742f624233SNattuvetty Bhavyan 					 * "Storage Configuration Service".
30752f624233SNattuvetty Bhavyan 					 */
30762f624233SNattuvetty Bhavyan 					p[idx] = 1;
30772f624233SNattuvetty Bhavyan 					SCSI_WRITE16(p + idx + 2, sz);
30782f624233SNattuvetty Bhavyan 					bcopy(url, p + idx + 4, url_size);
30792f624233SNattuvetty Bhavyan 					xfer_size = newidx + 4;
30802f624233SNattuvetty Bhavyan 				}
30812f624233SNattuvetty Bhavyan 				idx = newidx;
30822f624233SNattuvetty Bhavyan 
30832f624233SNattuvetty Bhavyan 				/* skip to next mgmt url if any */
30842f624233SNattuvetty Bhavyan 				url += url_size;
30852f624233SNattuvetty Bhavyan 				url_size = sbd_parse_mgmt_url(&url);
30862f624233SNattuvetty Bhavyan 			}
30872f624233SNattuvetty Bhavyan 
30882f624233SNattuvetty Bhavyan 			/* Total descriptor length */
30892f624233SNattuvetty Bhavyan 			SCSI_WRITE16(p + 2, idx - 4);
30902f624233SNattuvetty Bhavyan 			break;
30912f624233SNattuvetty Bhavyan 		}
30922f624233SNattuvetty Bhavyan 
3093fcf3ce44SJohn Forte 	case 0x86:
3094fcf3ce44SJohn Forte 		page_length = 0x3c;
3095fcf3ce44SJohn Forte 
30968fe96085Stim szeto 		p[0] = byte0;
3097fcf3ce44SJohn Forte 		p[1] = 0x86;		/* Page 86 response */
3098fcf3ce44SJohn Forte 		p[3] = page_length;
3099fcf3ce44SJohn Forte 
3100fcf3ce44SJohn Forte 		/*
3101fcf3ce44SJohn Forte 		 * Bits 0, 1, and 2 will need to be updated
3102fcf3ce44SJohn Forte 		 * to reflect the queue tag handling if/when
3103fcf3ce44SJohn Forte 		 * that is implemented.  For now, we're going
3104fcf3ce44SJohn Forte 		 * to claim support only for Simple TA.
3105fcf3ce44SJohn Forte 		 */
3106fcf3ce44SJohn Forte 		p[5] = 1;
31072f624233SNattuvetty Bhavyan 		xfer_size = page_length + 4;
3108fcf3ce44SJohn Forte 		break;
3109fcf3ce44SJohn Forte 
3110b77b9231SDan McDonald 	case 0xb0:
3111b77b9231SDan McDonald 		page_length = 0x3c;
3112b77b9231SDan McDonald 		p[0] = byte0;
3113b77b9231SDan McDonald 		p[1] = 0xb0;
3114b77b9231SDan McDonald 		p[3] = page_length;
311561dfa509SRick McNeal 		p[4] = 1;
311661dfa509SRick McNeal 		p[5] = sbd_ats_max_nblks();
311761dfa509SRick McNeal 		if (sl->sl_flags & SL_UNMAP_ENABLED && sbd_unmap_enable) {
311861dfa509SRick McNeal 			p[20] = (stmf_sbd_unmap_max_nblks >> 24) & 0xff;
311961dfa509SRick McNeal 			p[21] = (stmf_sbd_unmap_max_nblks >> 16) & 0xff;
312061dfa509SRick McNeal 			p[22] = (stmf_sbd_unmap_max_nblks >> 8) & 0xff;
312161dfa509SRick McNeal 			p[23] = stmf_sbd_unmap_max_nblks & 0xff;
312261dfa509SRick McNeal 
312361dfa509SRick McNeal 			p[24] = 0;
312461dfa509SRick McNeal 			p[25] = 0;
312561dfa509SRick McNeal 			p[26] = 0;
312661dfa509SRick McNeal 			p[27] = 0xFF;
312761dfa509SRick McNeal 		}
3128b77b9231SDan McDonald 		xfer_size = page_length + 4;
3129b77b9231SDan McDonald 		break;
3130b77b9231SDan McDonald 
3131b77b9231SDan McDonald 	case 0xb2:
3132b77b9231SDan McDonald 		if ((sl->sl_flags & SL_UNMAP_ENABLED) == 0) {
3133b77b9231SDan McDonald 			stmf_scsilib_send_status(task, STATUS_CHECK,
3134b77b9231SDan McDonald 			    STMF_SAA_INVALID_FIELD_IN_CDB);
3135b77b9231SDan McDonald 			goto err_done;
3136b77b9231SDan McDonald 		}
3137b77b9231SDan McDonald 		page_length = 4;
3138b77b9231SDan McDonald 		p[0] = byte0;
3139b77b9231SDan McDonald 		p[1] = 0xb2;
3140b77b9231SDan McDonald 		p[3] = page_length;
3141b77b9231SDan McDonald 
3142b77b9231SDan McDonald 		exp = (uint8_t)sl->sl_data_blocksize_shift;
3143b77b9231SDan McDonald 		s = sl->sl_lu_size >> sl->sl_data_blocksize_shift;
3144b77b9231SDan McDonald 		while (s & ((uint64_t)0xFFFFFFFF80000000ull)) {
3145b77b9231SDan McDonald 			s >>= 1;
3146b77b9231SDan McDonald 			exp++;
3147b77b9231SDan McDonald 		}
3148b77b9231SDan McDonald 		p[4] = exp;
314961dfa509SRick McNeal 		p[5] = 0xc0;	/* Logical provisioning UNMAP and WRITE SAME */
3150b77b9231SDan McDonald 		xfer_size = page_length + 4;
3151b77b9231SDan McDonald 		break;
3152b77b9231SDan McDonald 
3153fcf3ce44SJohn Forte 	default:
3154fcf3ce44SJohn Forte 		stmf_scsilib_send_status(task, STATUS_CHECK,
3155fcf3ce44SJohn Forte 		    STMF_SAA_INVALID_FIELD_IN_CDB);
31567beff157SJohn Forte 		goto err_done;
3157fcf3ce44SJohn Forte 	}
3158fcf3ce44SJohn Forte 
3159fcf3ce44SJohn Forte 	sbd_handle_short_read_transfers(task, initial_dbuf, p, cmd_size,
31602f624233SNattuvetty Bhavyan 	    min(cmd_size, xfer_size));
31617beff157SJohn Forte err_done:
31622f624233SNattuvetty Bhavyan 	kmem_free(p, bsize);
31637beff157SJohn Forte 	rw_exit(&sbd_global_prop_lock);
3164fcf3ce44SJohn Forte }
3165fcf3ce44SJohn Forte 
3166fcf3ce44SJohn Forte stmf_status_t
sbd_task_alloc(struct scsi_task * task)3167fcf3ce44SJohn Forte sbd_task_alloc(struct scsi_task *task)
3168fcf3ce44SJohn Forte {
3169fcf3ce44SJohn Forte 	if ((task->task_lu_private =
317061dfa509SRick McNeal 	    kmem_zalloc(sizeof (sbd_cmd_t), KM_NOSLEEP)) != NULL) {
3171fcf3ce44SJohn Forte 		sbd_cmd_t *scmd = (sbd_cmd_t *)task->task_lu_private;
3172fcf3ce44SJohn Forte 		scmd->flags = 0;
3173fcf3ce44SJohn Forte 		return (STMF_SUCCESS);
3174fcf3ce44SJohn Forte 	}
3175fcf3ce44SJohn Forte 	return (STMF_ALLOC_FAILURE);
3176fcf3ce44SJohn Forte }
3177fcf3ce44SJohn Forte 
3178fcf3ce44SJohn Forte void
sbd_remove_it_handle(sbd_lu_t * sl,sbd_it_data_t * it)31798fe96085Stim szeto sbd_remove_it_handle(sbd_lu_t *sl, sbd_it_data_t *it)
3180fcf3ce44SJohn Forte {
3181fcf3ce44SJohn Forte 	sbd_it_data_t **ppit;
3182fcf3ce44SJohn Forte 
31838fe96085Stim szeto 	sbd_pgr_remove_it_handle(sl, it);
31848fe96085Stim szeto 	mutex_enter(&sl->sl_lock);
31858fe96085Stim szeto 	for (ppit = &sl->sl_it_list; *ppit != NULL;
31861b7fc709Stim szeto 	    ppit = &((*ppit)->sbd_it_next)) {
3187fcf3ce44SJohn Forte 		if ((*ppit) == it) {
3188fcf3ce44SJohn Forte 			*ppit = it->sbd_it_next;
3189fcf3ce44SJohn Forte 			break;
3190fcf3ce44SJohn Forte 		}
3191fcf3ce44SJohn Forte 	}
31928fe96085Stim szeto 	mutex_exit(&sl->sl_lock);
3193e17f3b22Stim szeto 
31948fe96085Stim szeto 	DTRACE_PROBE2(itl__nexus__end, stmf_lu_t *, sl->sl_lu,
3195e17f3b22Stim szeto 	    sbd_it_data_t *, it);
3196e17f3b22Stim szeto 
3197fcf3ce44SJohn Forte 	kmem_free(it, sizeof (*it));
3198fcf3ce44SJohn Forte }
3199fcf3ce44SJohn Forte 
3200fcf3ce44SJohn Forte void
sbd_check_and_clear_scsi2_reservation(sbd_lu_t * sl,sbd_it_data_t * it)32018fe96085Stim szeto sbd_check_and_clear_scsi2_reservation(sbd_lu_t *sl, sbd_it_data_t *it)
3202fcf3ce44SJohn Forte {
32038fe96085Stim szeto 	mutex_enter(&sl->sl_lock);
32048fe96085Stim szeto 	if ((sl->sl_flags & SL_LU_HAS_SCSI2_RESERVATION) == 0) {
3205fcf3ce44SJohn Forte 		/* If we dont have any reservations, just get out. */
32068fe96085Stim szeto 		mutex_exit(&sl->sl_lock);
3207fcf3ce44SJohn Forte 		return;
3208fcf3ce44SJohn Forte 	}
3209fcf3ce44SJohn Forte 
3210fcf3ce44SJohn Forte 	if (it == NULL) {
3211fcf3ce44SJohn Forte 		/* Find the I_T nexus which is holding the reservation. */
32128fe96085Stim szeto 		for (it = sl->sl_it_list; it != NULL; it = it->sbd_it_next) {
3213fcf3ce44SJohn Forte 			if (it->sbd_it_flags & SBD_IT_HAS_SCSI2_RESERVATION) {
3214fcf3ce44SJohn Forte 				ASSERT(it->sbd_it_session_id ==
32158fe96085Stim szeto 				    sl->sl_rs_owner_session_id);
3216fcf3ce44SJohn Forte 				break;
3217fcf3ce44SJohn Forte 			}
3218fcf3ce44SJohn Forte 		}
3219fcf3ce44SJohn Forte 		ASSERT(it != NULL);
3220fcf3ce44SJohn Forte 	} else {
3221fcf3ce44SJohn Forte 		/*
3222fcf3ce44SJohn Forte 		 * We were passed an I_T nexus. If this nexus does not hold
3223fcf3ce44SJohn Forte 		 * the reservation, do nothing. This is why this function is
3224fcf3ce44SJohn Forte 		 * called "check_and_clear".
3225fcf3ce44SJohn Forte 		 */
3226fcf3ce44SJohn Forte 		if ((it->sbd_it_flags & SBD_IT_HAS_SCSI2_RESERVATION) == 0) {
32278fe96085Stim szeto 			mutex_exit(&sl->sl_lock);
3228fcf3ce44SJohn Forte 			return;
3229fcf3ce44SJohn Forte 		}
3230fcf3ce44SJohn Forte 	}
3231fcf3ce44SJohn Forte 	it->sbd_it_flags &= ~SBD_IT_HAS_SCSI2_RESERVATION;
32328fe96085Stim szeto 	sl->sl_flags &= ~SL_LU_HAS_SCSI2_RESERVATION;
32338fe96085Stim szeto 	mutex_exit(&sl->sl_lock);
3234fcf3ce44SJohn Forte }
3235fcf3ce44SJohn Forte 
323661dfa509SRick McNeal /*
323761dfa509SRick McNeal  * Given a LU and a task, check if the task is causing reservation
323861dfa509SRick McNeal  * conflict. Returns 1 in case of conflict, 0 otherwise.
323961dfa509SRick McNeal  * Note that the LU might not be the same LU as in the task but the
324061dfa509SRick McNeal  * caller makes sure that the LU can be accessed.
324161dfa509SRick McNeal  */
324261dfa509SRick McNeal int
sbd_check_reservation_conflict(struct sbd_lu * sl,struct scsi_task * task)324361dfa509SRick McNeal sbd_check_reservation_conflict(struct sbd_lu *sl, struct scsi_task *task)
324461dfa509SRick McNeal {
324561dfa509SRick McNeal 	sbd_it_data_t *it;
3246fcf3ce44SJohn Forte 
324761dfa509SRick McNeal 	it = task->task_lu_itl_handle;
324861dfa509SRick McNeal 	ASSERT(it);
324961dfa509SRick McNeal 	if (sl->sl_access_state == SBD_LU_ACTIVE) {
325061dfa509SRick McNeal 		if (SBD_PGR_RSVD(sl->sl_pgr)) {
325161dfa509SRick McNeal 			if (sbd_pgr_reservation_conflict(task, sl)) {
325261dfa509SRick McNeal 				return (1);
325361dfa509SRick McNeal 			}
325461dfa509SRick McNeal 		} else if ((sl->sl_flags & SL_LU_HAS_SCSI2_RESERVATION) &&
325561dfa509SRick McNeal 		    ((it->sbd_it_flags & SBD_IT_HAS_SCSI2_RESERVATION) == 0)) {
325661dfa509SRick McNeal 			if (!(SCSI2_CONFLICT_FREE_CMDS(task->task_cdb))) {
325761dfa509SRick McNeal 				return (1);
325861dfa509SRick McNeal 			}
325961dfa509SRick McNeal 		}
326061dfa509SRick McNeal 	}
3261fcf3ce44SJohn Forte 
326261dfa509SRick McNeal 	return (0);
326361dfa509SRick McNeal }
326461dfa509SRick McNeal 
326561dfa509SRick McNeal /*
326661dfa509SRick McNeal  * Keep in mind that sbd_new_task can be called multiple times for the same
326761dfa509SRick McNeal  * task because of us calling stmf_task_poll_lu resulting in a call to
326861dfa509SRick McNeal  * sbd_task_poll().
326961dfa509SRick McNeal  */
3270fcf3ce44SJohn Forte void
sbd_new_task(struct scsi_task * task,struct stmf_data_buf * initial_dbuf)3271fcf3ce44SJohn Forte sbd_new_task(struct scsi_task *task, struct stmf_data_buf *initial_dbuf)
3272fcf3ce44SJohn Forte {
32738fe96085Stim szeto 	sbd_lu_t *sl = (sbd_lu_t *)task->task_lu->lu_provider_private;
3274fcf3ce44SJohn Forte 	sbd_it_data_t *it;
3275fcf3ce44SJohn Forte 	uint8_t cdb0, cdb1;
327645039663SJohn Forte 	stmf_status_t st_ret;
3277fcf3ce44SJohn Forte 
3278fcf3ce44SJohn Forte 	if ((it = task->task_lu_itl_handle) == NULL) {
32798fe96085Stim szeto 		mutex_enter(&sl->sl_lock);
32808fe96085Stim szeto 		for (it = sl->sl_it_list; it != NULL; it = it->sbd_it_next) {
3281fcf3ce44SJohn Forte 			if (it->sbd_it_session_id ==
3282fcf3ce44SJohn Forte 			    task->task_session->ss_session_id) {
32838fe96085Stim szeto 				mutex_exit(&sl->sl_lock);
3284fcf3ce44SJohn Forte 				stmf_scsilib_send_status(task, STATUS_BUSY, 0);
3285fcf3ce44SJohn Forte 				return;
3286fcf3ce44SJohn Forte 			}
3287fcf3ce44SJohn Forte 		}
3288fcf3ce44SJohn Forte 		it = (sbd_it_data_t *)kmem_zalloc(sizeof (*it), KM_NOSLEEP);
3289fcf3ce44SJohn Forte 		if (it == NULL) {
32908fe96085Stim szeto 			mutex_exit(&sl->sl_lock);
3291fcf3ce44SJohn Forte 			stmf_scsilib_send_status(task, STATUS_BUSY, 0);
3292fcf3ce44SJohn Forte 			return;
3293fcf3ce44SJohn Forte 		}
3294fcf3ce44SJohn Forte 		it->sbd_it_session_id = task->task_session->ss_session_id;
3295fcf3ce44SJohn Forte 		bcopy(task->task_lun_no, it->sbd_it_lun, 8);
32968fe96085Stim szeto 		it->sbd_it_next = sl->sl_it_list;
32978fe96085Stim szeto 		sl->sl_it_list = it;
32988fe96085Stim szeto 		mutex_exit(&sl->sl_lock);
3299e17f3b22Stim szeto 
3300e17f3b22Stim szeto 		DTRACE_PROBE1(itl__nexus__start, scsi_task *, task);
3301e17f3b22Stim szeto 
3302f4e76ddfSNattuvetty Bhavyan 		sbd_pgr_initialize_it(task, it);
3303fcf3ce44SJohn Forte 		if (stmf_register_itl_handle(task->task_lu, task->task_lun_no,
3304fcf3ce44SJohn Forte 		    task->task_session, it->sbd_it_session_id, it)
3305fcf3ce44SJohn Forte 		    != STMF_SUCCESS) {
33068fe96085Stim szeto 			sbd_remove_it_handle(sl, it);
3307fcf3ce44SJohn Forte 			stmf_scsilib_send_status(task, STATUS_BUSY, 0);
3308fcf3ce44SJohn Forte 			return;
3309fcf3ce44SJohn Forte 		}
3310fcf3ce44SJohn Forte 		task->task_lu_itl_handle = it;
331145039663SJohn Forte 		if (sl->sl_access_state != SBD_LU_STANDBY) {
331245039663SJohn Forte 			it->sbd_it_ua_conditions = SBD_UA_POR;
331345039663SJohn Forte 		}
33148fe96085Stim szeto 	} else if (it->sbd_it_flags & SBD_IT_PGR_CHECK_FLAG) {
33158fe96085Stim szeto 		mutex_enter(&sl->sl_lock);
33168fe96085Stim szeto 		it->sbd_it_flags &= ~SBD_IT_PGR_CHECK_FLAG;
33178fe96085Stim szeto 		mutex_exit(&sl->sl_lock);
3318716c1805SNattuvetty Bhavyan 		sbd_pgr_initialize_it(task, it);
3319fcf3ce44SJohn Forte 	}
3320fcf3ce44SJohn Forte 
3321fcf3ce44SJohn Forte 	if (task->task_mgmt_function) {
3322fcf3ce44SJohn Forte 		stmf_scsilib_handle_task_mgmt(task);
3323fcf3ce44SJohn Forte 		return;
3324fcf3ce44SJohn Forte 	}
3325fcf3ce44SJohn Forte 
332645039663SJohn Forte 	/*
332745039663SJohn Forte 	 * if we're transitioning between access
332845039663SJohn Forte 	 * states, return NOT READY
332945039663SJohn Forte 	 */
333045039663SJohn Forte 	if (sl->sl_access_state == SBD_LU_TRANSITION_TO_STANDBY ||
333145039663SJohn Forte 	    sl->sl_access_state == SBD_LU_TRANSITION_TO_ACTIVE) {
333245039663SJohn Forte 		stmf_scsilib_send_status(task, STATUS_CHECK,
333361dfa509SRick McNeal 		    STMF_SAA_LU_NO_ACCESS_TRANSITION);
333445039663SJohn Forte 		return;
333545039663SJohn Forte 	}
333645039663SJohn Forte 
333761dfa509SRick McNeal 	cdb0 = task->task_cdb[0];
333861dfa509SRick McNeal 	cdb1 = task->task_cdb[1];
333961dfa509SRick McNeal 	/*
334061dfa509SRick McNeal 	 * Special case for different versions of Windows.
334161dfa509SRick McNeal 	 * 1) Windows 2012 and VMWare will fail to discover LU's if a READ
334261dfa509SRick McNeal 	 *    operation sent down the standby path returns an error. By default
334361dfa509SRick McNeal 	 *    standby_fail_reads will be set to 0.
334461dfa509SRick McNeal 	 * 2) Windows 2008 R2 has a severe performace problem if READ ops
334561dfa509SRick McNeal 	 *    aren't rejected on the standby path. 2008 sends commands
334661dfa509SRick McNeal 	 *    down the standby path which then must be proxied over to the
334761dfa509SRick McNeal 	 *    active node and back.
334861dfa509SRick McNeal 	 */
334961dfa509SRick McNeal 	if ((sl->sl_access_state == SBD_LU_STANDBY) &&
335061dfa509SRick McNeal 	    stmf_standby_fail_reads &&
335161dfa509SRick McNeal 	    (cdb0 == SCMD_READ || cdb0 == SCMD_READ_G1 ||
335261dfa509SRick McNeal 	    cdb0 == SCMD_READ_G4 || cdb0 == SCMD_READ_G5)) {
335361dfa509SRick McNeal 		stmf_scsilib_send_status(task, STATUS_CHECK,
335461dfa509SRick McNeal 		    STMF_SAA_LU_NO_ACCESS_STANDBY);
335561dfa509SRick McNeal 		return;
335661dfa509SRick McNeal 	}
335761dfa509SRick McNeal 
335861dfa509SRick McNeal 	/*
335961dfa509SRick McNeal 	 * Don't go further if cmd is unsupported in standby mode
336061dfa509SRick McNeal 	 */
336161dfa509SRick McNeal 	if (sl->sl_access_state == SBD_LU_STANDBY) {
336261dfa509SRick McNeal 		if (cdb0 != SCMD_INQUIRY &&
336361dfa509SRick McNeal 		    cdb0 != SCMD_MODE_SENSE &&
336461dfa509SRick McNeal 		    cdb0 != SCMD_MODE_SENSE_G1 &&
336561dfa509SRick McNeal 		    cdb0 != SCMD_MODE_SELECT &&
336661dfa509SRick McNeal 		    cdb0 != SCMD_MODE_SELECT_G1 &&
336761dfa509SRick McNeal 		    cdb0 != SCMD_RESERVE &&
336861dfa509SRick McNeal 		    cdb0 != SCMD_RELEASE &&
336961dfa509SRick McNeal 		    cdb0 != SCMD_PERSISTENT_RESERVE_OUT &&
337061dfa509SRick McNeal 		    cdb0 != SCMD_PERSISTENT_RESERVE_IN &&
337161dfa509SRick McNeal 		    cdb0 != SCMD_REQUEST_SENSE &&
337261dfa509SRick McNeal 		    cdb0 != SCMD_READ_CAPACITY &&
337361dfa509SRick McNeal 		    cdb0 != SCMD_TEST_UNIT_READY &&
337461dfa509SRick McNeal 		    cdb0 != SCMD_START_STOP &&
337561dfa509SRick McNeal 		    cdb0 != SCMD_READ &&
337661dfa509SRick McNeal 		    cdb0 != SCMD_READ_G1 &&
337761dfa509SRick McNeal 		    cdb0 != SCMD_READ_G4 &&
337861dfa509SRick McNeal 		    cdb0 != SCMD_READ_G5 &&
337961dfa509SRick McNeal 		    !(cdb0 == SCMD_SVC_ACTION_IN_G4 &&
338061dfa509SRick McNeal 		    cdb1 == SSVC_ACTION_READ_CAPACITY_G4) &&
338161dfa509SRick McNeal 		    !(cdb0 == SCMD_MAINTENANCE_IN &&
338261dfa509SRick McNeal 		    (cdb1 & 0x1F) == 0x05) &&
338361dfa509SRick McNeal 		    !(cdb0 == SCMD_MAINTENANCE_IN &&
338461dfa509SRick McNeal 		    (cdb1 & 0x1F) == 0x0A)) {
338561dfa509SRick McNeal 			stmf_scsilib_send_status(task, STATUS_CHECK,
338661dfa509SRick McNeal 			    STMF_SAA_LU_NO_ACCESS_STANDBY);
338761dfa509SRick McNeal 			return;
338861dfa509SRick McNeal 		}
338961dfa509SRick McNeal 	}
339061dfa509SRick McNeal 
339161dfa509SRick McNeal 	/*
339261dfa509SRick McNeal 	 * Checking ua conditions as per SAM3R14 5.3.2 specified order. During
339361dfa509SRick McNeal 	 * MPIO/ALUA failover, cmds come in through local ports and proxy port
339461dfa509SRick McNeal 	 * port provider (i.e. pppt), we want to report unit attention to
339561dfa509SRick McNeal 	 * only local cmds since initiators (Windows MPIO/DSM) would continue
339661dfa509SRick McNeal 	 * sending I/O to the target that reported unit attention.
339761dfa509SRick McNeal 	 */
339861dfa509SRick McNeal 	if ((it->sbd_it_ua_conditions) &&
339961dfa509SRick McNeal 	    !(task->task_additional_flags & TASK_AF_PPPT_TASK) &&
340061dfa509SRick McNeal 	    (task->task_cdb[0] != SCMD_INQUIRY)) {
34018fe96085Stim szeto 		uint32_t saa = 0;
34028fe96085Stim szeto 
34038fe96085Stim szeto 		mutex_enter(&sl->sl_lock);
34048fe96085Stim szeto 		if (it->sbd_it_ua_conditions & SBD_UA_POR) {
34058fe96085Stim szeto 			it->sbd_it_ua_conditions &= ~SBD_UA_POR;
34068fe96085Stim szeto 			saa = STMF_SAA_POR;
340761dfa509SRick McNeal 		} else if (it->sbd_it_ua_conditions &
340861dfa509SRick McNeal 		    SBD_UA_ASYMMETRIC_ACCESS_CHANGED) {
340961dfa509SRick McNeal 			it->sbd_it_ua_conditions &=
341061dfa509SRick McNeal 			    ~SBD_UA_ASYMMETRIC_ACCESS_CHANGED;
341161dfa509SRick McNeal 			saa = STMF_SAA_ASYMMETRIC_ACCESS_CHANGED;
34128fe96085Stim szeto 		}
34138fe96085Stim szeto 		mutex_exit(&sl->sl_lock);
34148fe96085Stim szeto 		if (saa) {
34158fe96085Stim szeto 			stmf_scsilib_send_status(task, STATUS_CHECK, saa);
34168fe96085Stim szeto 			return;
34178fe96085Stim szeto 		}
34188fe96085Stim szeto 	}
34198fe96085Stim szeto 
34208fe96085Stim szeto 	/* Reservation conflict checks */
342161dfa509SRick McNeal 	if (sbd_check_reservation_conflict(sl, task)) {
342261dfa509SRick McNeal 		stmf_scsilib_send_status(task,
342361dfa509SRick McNeal 		    STATUS_RESERVATION_CONFLICT, 0);
342461dfa509SRick McNeal 		return;
3425fcf3ce44SJohn Forte 	}
3426fcf3ce44SJohn Forte 
34278fe96085Stim szeto 	/* Rest of the ua conndition checks */
3428fcf3ce44SJohn Forte 	if ((it->sbd_it_ua_conditions) && (task->task_cdb[0] != SCMD_INQUIRY)) {
3429fcf3ce44SJohn Forte 		uint32_t saa = 0;
3430fcf3ce44SJohn Forte 
34318fe96085Stim szeto 		mutex_enter(&sl->sl_lock);
34328fe96085Stim szeto 		if (it->sbd_it_ua_conditions & SBD_UA_CAPACITY_CHANGED) {
3433fcf3ce44SJohn Forte 			it->sbd_it_ua_conditions &= ~SBD_UA_CAPACITY_CHANGED;
3434fcf3ce44SJohn Forte 			if ((task->task_cdb[0] == SCMD_READ_CAPACITY) ||
3435fcf3ce44SJohn Forte 			    ((task->task_cdb[0] == SCMD_SVC_ACTION_IN_G4) &&
3436fcf3ce44SJohn Forte 			    (task->task_cdb[1] ==
3437fcf3ce44SJohn Forte 			    SSVC_ACTION_READ_CAPACITY_G4))) {
3438fcf3ce44SJohn Forte 				saa = 0;
3439fcf3ce44SJohn Forte 			} else {
3440fcf3ce44SJohn Forte 				saa = STMF_SAA_CAPACITY_DATA_HAS_CHANGED;
3441fcf3ce44SJohn Forte 			}
34428fe96085Stim szeto 		} else if (it->sbd_it_ua_conditions &
34438fe96085Stim szeto 		    SBD_UA_MODE_PARAMETERS_CHANGED) {
34448fe96085Stim szeto 			it->sbd_it_ua_conditions &=
34458fe96085Stim szeto 			    ~SBD_UA_MODE_PARAMETERS_CHANGED;
34468fe96085Stim szeto 			saa = STMF_SAA_MODE_PARAMETERS_CHANGED;
344745039663SJohn Forte 		} else if (it->sbd_it_ua_conditions &
344845039663SJohn Forte 		    SBD_UA_ASYMMETRIC_ACCESS_CHANGED) {
344961dfa509SRick McNeal 			saa = 0;
345061dfa509SRick McNeal 		} else if (it->sbd_it_ua_conditions & SBD_UA_POR) {
345161dfa509SRick McNeal 			saa = 0;
345245039663SJohn Forte 		} else if (it->sbd_it_ua_conditions &
345345039663SJohn Forte 		    SBD_UA_ACCESS_STATE_TRANSITION) {
345445039663SJohn Forte 			it->sbd_it_ua_conditions &=
345545039663SJohn Forte 			    ~SBD_UA_ACCESS_STATE_TRANSITION;
345645039663SJohn Forte 			saa = STMF_SAA_LU_NO_ACCESS_TRANSITION;
3457fcf3ce44SJohn Forte 		} else {
3458fcf3ce44SJohn Forte 			it->sbd_it_ua_conditions = 0;
3459fcf3ce44SJohn Forte 			saa = 0;
3460fcf3ce44SJohn Forte 		}
34618fe96085Stim szeto 		mutex_exit(&sl->sl_lock);
3462fcf3ce44SJohn Forte 		if (saa) {
3463fcf3ce44SJohn Forte 			stmf_scsilib_send_status(task, STATUS_CHECK, saa);
3464fcf3ce44SJohn Forte 			return;
3465fcf3ce44SJohn Forte 		}
3466fcf3ce44SJohn Forte 	}
3467fcf3ce44SJohn Forte 
346845039663SJohn Forte 	if (sl->sl_access_state == SBD_LU_STANDBY) {
346945039663SJohn Forte 		/*
347045039663SJohn Forte 		 * is this a short write?
347145039663SJohn Forte 		 * if so, we'll need to wait until we have the buffer
347245039663SJohn Forte 		 * before proxying the command
347345039663SJohn Forte 		 */
347445039663SJohn Forte 		switch (cdb0) {
347545039663SJohn Forte 			case SCMD_MODE_SELECT:
347645039663SJohn Forte 			case SCMD_MODE_SELECT_G1:
347745039663SJohn Forte 			case SCMD_PERSISTENT_RESERVE_OUT:
347845039663SJohn Forte 				break;
347945039663SJohn Forte 			default:
348045039663SJohn Forte 				st_ret = stmf_proxy_scsi_cmd(task,
348145039663SJohn Forte 				    initial_dbuf);
348245039663SJohn Forte 				if (st_ret != STMF_SUCCESS) {
348345039663SJohn Forte 					stmf_scsilib_send_status(task,
348445039663SJohn Forte 					    STATUS_CHECK,
348545039663SJohn Forte 					    STMF_SAA_LU_NO_ACCESS_UNAVAIL);
348645039663SJohn Forte 				}
348745039663SJohn Forte 				return;
348845039663SJohn Forte 		}
348945039663SJohn Forte 	}
349045039663SJohn Forte 
3491fcf3ce44SJohn Forte 	cdb0 = task->task_cdb[0] & 0x1F;
3492fcf3ce44SJohn Forte 
3493fcf3ce44SJohn Forte 	if ((cdb0 == SCMD_READ) || (cdb0 == SCMD_WRITE)) {
3494fcf3ce44SJohn Forte 		if (task->task_additional_flags & TASK_AF_PORT_LOAD_HIGH) {
3495fcf3ce44SJohn Forte 			stmf_scsilib_send_status(task, STATUS_QFULL, 0);
3496fcf3ce44SJohn Forte 			return;
3497fcf3ce44SJohn Forte 		}
3498fcf3ce44SJohn Forte 		if (cdb0 == SCMD_READ) {
3499fcf3ce44SJohn Forte 			sbd_handle_read(task, initial_dbuf);
3500fcf3ce44SJohn Forte 			return;
3501fcf3ce44SJohn Forte 		}
3502fcf3ce44SJohn Forte 		sbd_handle_write(task, initial_dbuf);
3503fcf3ce44SJohn Forte 		return;
3504fcf3ce44SJohn Forte 	}
3505fcf3ce44SJohn Forte 
3506fcf3ce44SJohn Forte 	cdb0 = task->task_cdb[0];
3507fcf3ce44SJohn Forte 	cdb1 = task->task_cdb[1];
3508fcf3ce44SJohn Forte 
3509fcf3ce44SJohn Forte 	if (cdb0 == SCMD_INQUIRY) {		/* Inquiry */
35102f624233SNattuvetty Bhavyan 		sbd_handle_inquiry(task, initial_dbuf);
3511fcf3ce44SJohn Forte 		return;
3512fcf3ce44SJohn Forte 	}
3513fcf3ce44SJohn Forte 
35148fe96085Stim szeto 	if (cdb0  == SCMD_PERSISTENT_RESERVE_OUT) {
35158fe96085Stim szeto 		sbd_handle_pgr_out_cmd(task, initial_dbuf);
35168fe96085Stim szeto 		return;
3517fcf3ce44SJohn Forte 	}
3518fcf3ce44SJohn Forte 
35198fe96085Stim szeto 	if (cdb0  == SCMD_PERSISTENT_RESERVE_IN) {
35208fe96085Stim szeto 		sbd_handle_pgr_in_cmd(task, initial_dbuf);
3521fcf3ce44SJohn Forte 		return;
3522fcf3ce44SJohn Forte 	}
35238fe96085Stim szeto 
35248fe96085Stim szeto 	if (cdb0 == SCMD_RELEASE) {
35258fe96085Stim szeto 		if (cdb1) {
35268fe96085Stim szeto 			stmf_scsilib_send_status(task, STATUS_CHECK,
35278fe96085Stim szeto 			    STMF_SAA_INVALID_FIELD_IN_CDB);
35288fe96085Stim szeto 			return;
35298fe96085Stim szeto 		}
35308fe96085Stim szeto 
35318fe96085Stim szeto 		mutex_enter(&sl->sl_lock);
35328fe96085Stim szeto 		if (sl->sl_flags & SL_LU_HAS_SCSI2_RESERVATION) {
35338fe96085Stim szeto 			/* If not owner don't release it, just return good */
35348fe96085Stim szeto 			if (it->sbd_it_session_id !=
35358fe96085Stim szeto 			    sl->sl_rs_owner_session_id) {
35368fe96085Stim szeto 				mutex_exit(&sl->sl_lock);
35378fe96085Stim szeto 				stmf_scsilib_send_status(task, STATUS_GOOD, 0);
35388fe96085Stim szeto 				return;
35398fe96085Stim szeto 			}
35408fe96085Stim szeto 		}
35418fe96085Stim szeto 		sl->sl_flags &= ~SL_LU_HAS_SCSI2_RESERVATION;
35428fe96085Stim szeto 		it->sbd_it_flags &= ~SBD_IT_HAS_SCSI2_RESERVATION;
35438fe96085Stim szeto 		mutex_exit(&sl->sl_lock);
35448fe96085Stim szeto 		stmf_scsilib_send_status(task, STATUS_GOOD, 0);
3545fcf3ce44SJohn Forte 		return;
3546fcf3ce44SJohn Forte 	}
35478fe96085Stim szeto 
35488fe96085Stim szeto 	if (cdb0 == SCMD_RESERVE) {
35498fe96085Stim szeto 		if (cdb1) {
35508fe96085Stim szeto 			stmf_scsilib_send_status(task, STATUS_CHECK,
35518fe96085Stim szeto 			    STMF_SAA_INVALID_FIELD_IN_CDB);
35528fe96085Stim szeto 			return;
35538fe96085Stim szeto 		}
35548fe96085Stim szeto 
35558fe96085Stim szeto 		mutex_enter(&sl->sl_lock);
35568fe96085Stim szeto 		if (sl->sl_flags & SL_LU_HAS_SCSI2_RESERVATION) {
35578fe96085Stim szeto 			/* If not owner, return conflict status */
35588fe96085Stim szeto 			if (it->sbd_it_session_id !=
35598fe96085Stim szeto 			    sl->sl_rs_owner_session_id) {
35608fe96085Stim szeto 				mutex_exit(&sl->sl_lock);
35618fe96085Stim szeto 				stmf_scsilib_send_status(task,
35628fe96085Stim szeto 				    STATUS_RESERVATION_CONFLICT, 0);
35638fe96085Stim szeto 				return;
35648fe96085Stim szeto 			}
35658fe96085Stim szeto 		}
35668fe96085Stim szeto 		sl->sl_flags |= SL_LU_HAS_SCSI2_RESERVATION;
35678fe96085Stim szeto 		it->sbd_it_flags |= SBD_IT_HAS_SCSI2_RESERVATION;
35688fe96085Stim szeto 		sl->sl_rs_owner_session_id = it->sbd_it_session_id;
35698fe96085Stim szeto 		mutex_exit(&sl->sl_lock);
35708fe96085Stim szeto 		stmf_scsilib_send_status(task, STATUS_GOOD, 0);
3571fcf3ce44SJohn Forte 		return;
3572fcf3ce44SJohn Forte 	}
3573fcf3ce44SJohn Forte 
3574fcf3ce44SJohn Forte 	if (cdb0 == SCMD_REQUEST_SENSE) {
3575fcf3ce44SJohn Forte 		/*
3576fcf3ce44SJohn Forte 		 * LU provider needs to store unretrieved sense data
3577fcf3ce44SJohn Forte 		 * (e.g. after power-on/reset).  For now, we'll just
3578fcf3ce44SJohn Forte 		 * return good status with no sense.
3579fcf3ce44SJohn Forte 		 */
3580fcf3ce44SJohn Forte 
3581fcf3ce44SJohn Forte 		if ((cdb1 & ~1) || task->task_cdb[2] || task->task_cdb[3] ||
3582fcf3ce44SJohn Forte 		    task->task_cdb[5]) {
3583fcf3ce44SJohn Forte 			stmf_scsilib_send_status(task, STATUS_CHECK,
3584fcf3ce44SJohn Forte 			    STMF_SAA_INVALID_FIELD_IN_CDB);
3585fcf3ce44SJohn Forte 		} else {
3586fcf3ce44SJohn Forte 			stmf_scsilib_send_status(task, STATUS_GOOD, 0);
3587fcf3ce44SJohn Forte 		}
3588fcf3ce44SJohn Forte 
3589fcf3ce44SJohn Forte 		return;
3590fcf3ce44SJohn Forte 	}
3591fcf3ce44SJohn Forte 
35928fe96085Stim szeto 	/* Report Target Port Groups */
35938fe96085Stim szeto 	if ((cdb0 == SCMD_MAINTENANCE_IN) &&
35948fe96085Stim szeto 	    ((cdb1 & 0x1F) == 0x0A)) {
35958fe96085Stim szeto 		stmf_scsilib_handle_report_tpgs(task, initial_dbuf);
3596fcf3ce44SJohn Forte 		return;
3597fcf3ce44SJohn Forte 	}
3598fcf3ce44SJohn Forte 
35996e4cf8b7SJohn Forte 	/* Report Identifying Information */
36006e4cf8b7SJohn Forte 	if ((cdb0 == SCMD_MAINTENANCE_IN) &&
36016e4cf8b7SJohn Forte 	    ((cdb1 & 0x1F) == 0x05)) {
36026e4cf8b7SJohn Forte 		sbd_handle_identifying_info(task, initial_dbuf);
36036e4cf8b7SJohn Forte 		return;
36046e4cf8b7SJohn Forte 	}
36056e4cf8b7SJohn Forte 
36068fe96085Stim szeto 	if (cdb0 == SCMD_START_STOP) {			/* Start stop */
36078fe96085Stim szeto 		task->task_cmd_xfer_length = 0;
36088fe96085Stim szeto 		if (task->task_cdb[4] & 0xFC) {
3609fcf3ce44SJohn Forte 			stmf_scsilib_send_status(task, STATUS_CHECK,
36101b7fc709Stim szeto 			    STMF_SAA_INVALID_FIELD_IN_CDB);
3611fcf3ce44SJohn Forte 			return;
3612fcf3ce44SJohn Forte 		}
36138fe96085Stim szeto 		if (task->task_cdb[4] & 2) {
36148fe96085Stim szeto 			stmf_scsilib_send_status(task, STATUS_CHECK,
36158fe96085Stim szeto 			    STMF_SAA_INVALID_FIELD_IN_CDB);
36168fe96085Stim szeto 		} else {
36178fe96085Stim szeto 			stmf_scsilib_send_status(task, STATUS_GOOD, 0);
3618fcf3ce44SJohn Forte 		}
36198fe96085Stim szeto 		return;
36208fe96085Stim szeto 
3621fcf3ce44SJohn Forte 	}
3622fcf3ce44SJohn Forte 
36238fe96085Stim szeto 	if ((cdb0 == SCMD_MODE_SENSE) || (cdb0 == SCMD_MODE_SENSE_G1)) {
36248fe96085Stim szeto 		uint8_t *p;
36258fe96085Stim szeto 		p = kmem_zalloc(512, KM_SLEEP);
36268fe96085Stim szeto 		sbd_handle_mode_sense(task, initial_dbuf, p);
36278fe96085Stim szeto 		kmem_free(p, 512);
36288fe96085Stim szeto 		return;
36298fe96085Stim szeto 	}
36308fe96085Stim szeto 
36318fe96085Stim szeto 	if ((cdb0 == SCMD_MODE_SELECT) || (cdb0 == SCMD_MODE_SELECT_G1)) {
36328fe96085Stim szeto 		sbd_handle_mode_select(task, initial_dbuf);
36338fe96085Stim szeto 		return;
36348fe96085Stim szeto 	}
36358fe96085Stim szeto 
3636b77b9231SDan McDonald 	if ((cdb0 == SCMD_UNMAP) && (sl->sl_flags & SL_UNMAP_ENABLED)) {
3637b77b9231SDan McDonald 		sbd_handle_unmap(task, initial_dbuf);
3638b77b9231SDan McDonald 		return;
3639b77b9231SDan McDonald 	}
3640b77b9231SDan McDonald 
36416140ef00SDan McDonald 	if ((cdb0 == SCMD_WRITE_SAME_G4) || (cdb0 == SCMD_WRITE_SAME_G1)) {
36426140ef00SDan McDonald 		sbd_handle_write_same(task, initial_dbuf);
3643b77b9231SDan McDonald 		return;
3644b77b9231SDan McDonald 	}
3645b77b9231SDan McDonald 
364661dfa509SRick McNeal 	if (cdb0 == SCMD_COMPARE_AND_WRITE) {
364761dfa509SRick McNeal 		sbd_handle_ats(task, initial_dbuf);
364861dfa509SRick McNeal 		return;
364961dfa509SRick McNeal 	}
365061dfa509SRick McNeal 
365161dfa509SRick McNeal 	if (cdb0 == SCMD_EXTENDED_COPY) {
365261dfa509SRick McNeal 		sbd_handle_xcopy(task, initial_dbuf);
365361dfa509SRick McNeal 		return;
365461dfa509SRick McNeal 	}
365561dfa509SRick McNeal 
365661dfa509SRick McNeal 	if (cdb0 == SCMD_RECV_COPY_RESULTS) {
365761dfa509SRick McNeal 		sbd_handle_recv_copy_results(task, initial_dbuf);
365861dfa509SRick McNeal 		return;
365961dfa509SRick McNeal 	}
366061dfa509SRick McNeal 
36618fe96085Stim szeto 	if (cdb0 == SCMD_TEST_UNIT_READY) {	/* Test unit ready */
36628fe96085Stim szeto 		task->task_cmd_xfer_length = 0;
3663fcf3ce44SJohn Forte 		stmf_scsilib_send_status(task, STATUS_GOOD, 0);
3664fcf3ce44SJohn Forte 		return;
3665fcf3ce44SJohn Forte 	}
36668fe96085Stim szeto 
36678fe96085Stim szeto 	if (cdb0 == SCMD_READ_CAPACITY) {		/* Read Capacity */
36688fe96085Stim szeto 		sbd_handle_read_capacity(task, initial_dbuf);
36698fe96085Stim szeto 		return;
36708fe96085Stim szeto 	}
36718fe96085Stim szeto 
36726140ef00SDan McDonald 	if (cdb0 == SCMD_SVC_ACTION_IN_G4) { /* Read Capacity or read long */
36738fe96085Stim szeto 		if (cdb1 == SSVC_ACTION_READ_CAPACITY_G4) {
36748fe96085Stim szeto 			sbd_handle_read_capacity(task, initial_dbuf);
36758fe96085Stim szeto 			return;
36768fe96085Stim szeto 		/*
36778fe96085Stim szeto 		 * } else if (cdb1 == SSVC_ACTION_READ_LONG_G4) {
3678e59325b7SToomas Soome 		 *	sbd_handle_read(task, initial_dbuf);
3679e59325b7SToomas Soome 		 *	return;
36808fe96085Stim szeto 		 */
36818fe96085Stim szeto 		}
36828fe96085Stim szeto 	}
36838fe96085Stim szeto 
36848fe96085Stim szeto 	/*
36858fe96085Stim szeto 	 * if (cdb0 == SCMD_SVC_ACTION_OUT_G4) {
36868fe96085Stim szeto 	 *	if (cdb1 == SSVC_ACTION_WRITE_LONG_G4) {
36878fe96085Stim szeto 	 *		 sbd_handle_write(task, initial_dbuf);
3688e59325b7SToomas Soome 	 *		return;
36898fe96085Stim szeto 	 *	}
36908fe96085Stim szeto 	 * }
36918fe96085Stim szeto 	 */
36928fe96085Stim szeto 
36938fe96085Stim szeto 	if (cdb0 == SCMD_VERIFY) {
36948fe96085Stim szeto 		/*
36958fe96085Stim szeto 		 * Something more likely needs to be done here.
36968fe96085Stim szeto 		 */
36978fe96085Stim szeto 		task->task_cmd_xfer_length = 0;
3698fcf3ce44SJohn Forte 		stmf_scsilib_send_status(task, STATUS_GOOD, 0);
3699fcf3ce44SJohn Forte 		return;
3700fcf3ce44SJohn Forte 	}
3701fcf3ce44SJohn Forte 
3702fcf3ce44SJohn Forte 	if (cdb0 == SCMD_SYNCHRONIZE_CACHE ||
3703fcf3ce44SJohn Forte 	    cdb0 == SCMD_SYNCHRONIZE_CACHE_G4) {
3704fcf3ce44SJohn Forte 		sbd_handle_sync_cache(task, initial_dbuf);
3705fcf3ce44SJohn Forte 		return;
3706fcf3ce44SJohn Forte 	}
3707fcf3ce44SJohn Forte 
3708309bed43SCharles Binford - Sun Microsystems - Wichita 	/*
3709309bed43SCharles Binford - Sun Microsystems - Wichita 	 * Write and Verify use the same path as write, but don't clutter the
3710309bed43SCharles Binford - Sun Microsystems - Wichita 	 * performance path above with checking for write_verify opcodes.  We
3711309bed43SCharles Binford - Sun Microsystems - Wichita 	 * rely on zfs's integrity checks for the "Verify" part of Write &
3712309bed43SCharles Binford - Sun Microsystems - Wichita 	 * Verify.  (Even if we did a read to "verify" we'd merely be reading
3713309bed43SCharles Binford - Sun Microsystems - Wichita 	 * cache, not actual media.)
3714309bed43SCharles Binford - Sun Microsystems - Wichita 	 * Therefore we
3715309bed43SCharles Binford - Sun Microsystems - Wichita 	 *   a) only support this if sbd_is_zvol, and
3716309bed43SCharles Binford - Sun Microsystems - Wichita 	 *   b) run the IO through the normal write path with a forced
3717309bed43SCharles Binford - Sun Microsystems - Wichita 	 *	sbd_flush_data_cache at the end.
3718309bed43SCharles Binford - Sun Microsystems - Wichita 	 */
3719309bed43SCharles Binford - Sun Microsystems - Wichita 
3720309bed43SCharles Binford - Sun Microsystems - Wichita 	if ((sl->sl_flags & SL_ZFS_META) && (
3721309bed43SCharles Binford - Sun Microsystems - Wichita 	    cdb0 == SCMD_WRITE_VERIFY ||
3722309bed43SCharles Binford - Sun Microsystems - Wichita 	    cdb0 == SCMD_WRITE_VERIFY_G4 ||
3723309bed43SCharles Binford - Sun Microsystems - Wichita 	    cdb0 == SCMD_WRITE_VERIFY_G5)) {
3724309bed43SCharles Binford - Sun Microsystems - Wichita 		sbd_handle_write(task, initial_dbuf);
3725309bed43SCharles Binford - Sun Microsystems - Wichita 		return;
3726309bed43SCharles Binford - Sun Microsystems - Wichita 	}
3727fcf3ce44SJohn Forte 	stmf_scsilib_send_status(task, STATUS_CHECK, STMF_SAA_INVALID_OPCODE);
3728fcf3ce44SJohn Forte }
3729fcf3ce44SJohn Forte 
3730fcf3ce44SJohn Forte void
sbd_dbuf_xfer_done(struct scsi_task * task,struct stmf_data_buf * dbuf)3731fcf3ce44SJohn Forte sbd_dbuf_xfer_done(struct scsi_task *task, struct stmf_data_buf *dbuf)
3732fcf3ce44SJohn Forte {
37333fb517f7SJames Moore 	sbd_cmd_t *scmd = (sbd_cmd_t *)task->task_lu_private;
37343fb517f7SJames Moore 
37353fb517f7SJames Moore 	if (dbuf->db_flags & DB_LU_DATA_BUF) {
37363fb517f7SJames Moore 		/*
37373fb517f7SJames Moore 		 * Buffers passed in from the LU always complete
37383fb517f7SJames Moore 		 * even if the task is no longer active.
37393fb517f7SJames Moore 		 */
37403fb517f7SJames Moore 		ASSERT(task->task_additional_flags & TASK_AF_ACCEPT_LU_DBUF);
37413fb517f7SJames Moore 		ASSERT(scmd);
37423fb517f7SJames Moore 		switch (scmd->cmd_type) {
37433fb517f7SJames Moore 		case (SBD_CMD_SCSI_READ):
37443fb517f7SJames Moore 			sbd_handle_sgl_read_xfer_completion(task, scmd, dbuf);
37453fb517f7SJames Moore 			break;
37463fb517f7SJames Moore 		case (SBD_CMD_SCSI_WRITE):
37473fb517f7SJames Moore 			sbd_handle_sgl_write_xfer_completion(task, scmd, dbuf);
37483fb517f7SJames Moore 			break;
37493fb517f7SJames Moore 		default:
37503fb517f7SJames Moore 			cmn_err(CE_PANIC, "Unknown cmd type, task = %p",
37513fb517f7SJames Moore 			    (void *)task);
37523fb517f7SJames Moore 			break;
37533fb517f7SJames Moore 		}
37543fb517f7SJames Moore 		return;
37553fb517f7SJames Moore 	}
3756fcf3ce44SJohn Forte 
3757fcf3ce44SJohn Forte 	if ((scmd == NULL) || ((scmd->flags & SBD_SCSI_CMD_ACTIVE) == 0))
3758fcf3ce44SJohn Forte 		return;
3759fcf3ce44SJohn Forte 
37608fe96085Stim szeto 	switch (scmd->cmd_type) {
37618fe96085Stim szeto 	case (SBD_CMD_SCSI_READ):
3762fcf3ce44SJohn Forte 		sbd_handle_read_xfer_completion(task, scmd, dbuf);
37638fe96085Stim szeto 		break;
37648fe96085Stim szeto 
37658fe96085Stim szeto 	case (SBD_CMD_SCSI_WRITE):
376661dfa509SRick McNeal 		switch (task->task_cdb[0]) {
376761dfa509SRick McNeal 		case SCMD_WRITE_SAME_G1:
376861dfa509SRick McNeal 		case SCMD_WRITE_SAME_G4:
37696140ef00SDan McDonald 			sbd_handle_write_same_xfer_completion(task, scmd, dbuf,
37706140ef00SDan McDonald 			    1);
377161dfa509SRick McNeal 			break;
377261dfa509SRick McNeal 		case SCMD_COMPARE_AND_WRITE:
377361dfa509SRick McNeal 			sbd_handle_ats_xfer_completion(task, scmd, dbuf, 1);
377461dfa509SRick McNeal 			break;
377561dfa509SRick McNeal 		default:
37766140ef00SDan McDonald 			sbd_handle_write_xfer_completion(task, scmd, dbuf, 1);
377761dfa509SRick McNeal 			/* FALLTHRU */
37786140ef00SDan McDonald 		}
37798fe96085Stim szeto 		break;
37808fe96085Stim szeto 
37818fe96085Stim szeto 	case (SBD_CMD_SMALL_READ):
3782fcf3ce44SJohn Forte 		sbd_handle_short_read_xfer_completion(task, scmd, dbuf);
37838fe96085Stim szeto 		break;
37848fe96085Stim szeto 
37858fe96085Stim szeto 	case (SBD_CMD_SMALL_WRITE):
37868fe96085Stim szeto 		sbd_handle_short_write_xfer_completion(task, dbuf);
37878fe96085Stim szeto 		break;
37888fe96085Stim szeto 
37898fe96085Stim szeto 	default:
3790fcf3ce44SJohn Forte 		cmn_err(CE_PANIC, "Unknown cmd type, task = %p", (void *)task);
37918fe96085Stim szeto 		break;
3792fcf3ce44SJohn Forte 	}
3793fcf3ce44SJohn Forte }
3794fcf3ce44SJohn Forte 
3795fcf3ce44SJohn Forte /* ARGSUSED */
3796fcf3ce44SJohn Forte void
sbd_send_status_done(struct scsi_task * task)3797fcf3ce44SJohn Forte sbd_send_status_done(struct scsi_task *task)
3798fcf3ce44SJohn Forte {
3799fcf3ce44SJohn Forte 	cmn_err(CE_PANIC,
38001b7fc709Stim szeto 	    "sbd_send_status_done: this should not have been called");
3801fcf3ce44SJohn Forte }
3802fcf3ce44SJohn Forte 
3803fcf3ce44SJohn Forte void
sbd_task_free(struct scsi_task * task)3804fcf3ce44SJohn Forte sbd_task_free(struct scsi_task *task)
3805fcf3ce44SJohn Forte {
3806fcf3ce44SJohn Forte 	if (task->task_lu_private) {
3807fcf3ce44SJohn Forte 		sbd_cmd_t *scmd = (sbd_cmd_t *)task->task_lu_private;
3808fcf3ce44SJohn Forte 		if (scmd->flags & SBD_SCSI_CMD_ACTIVE) {
3809fcf3ce44SJohn Forte 			cmn_err(CE_PANIC, "cmd is active, task = %p",
3810fcf3ce44SJohn Forte 			    (void *)task);
3811fcf3ce44SJohn Forte 		}
3812fcf3ce44SJohn Forte 		kmem_free(scmd, sizeof (sbd_cmd_t));
3813fcf3ce44SJohn Forte 	}
3814fcf3ce44SJohn Forte }
3815fcf3ce44SJohn Forte 
3816fcf3ce44SJohn Forte /*
3817fcf3ce44SJohn Forte  * Aborts are synchronus w.r.t. I/O AND
3818fcf3ce44SJohn Forte  * All the I/O which SBD does is synchronous AND
3819fcf3ce44SJohn Forte  * Everything within a task is single threaded.
3820fcf3ce44SJohn Forte  *   IT MEANS
3821fcf3ce44SJohn Forte  * If this function is called, we are doing nothing with this task
3822fcf3ce44SJohn Forte  * inside of sbd module.
3823fcf3ce44SJohn Forte  */
3824fcf3ce44SJohn Forte /* ARGSUSED */
3825fcf3ce44SJohn Forte stmf_status_t
sbd_abort(struct stmf_lu * lu,int abort_cmd,void * arg,uint32_t flags)3826fcf3ce44SJohn Forte sbd_abort(struct stmf_lu *lu, int abort_cmd, void *arg, uint32_t flags)
3827fcf3ce44SJohn Forte {
38288fe96085Stim szeto 	sbd_lu_t *sl = (sbd_lu_t *)lu->lu_provider_private;
3829fcf3ce44SJohn Forte 	scsi_task_t *task;
3830fcf3ce44SJohn Forte 
3831fcf3ce44SJohn Forte 	if (abort_cmd == STMF_LU_RESET_STATE) {
3832fcf3ce44SJohn Forte 		return (sbd_lu_reset_state(lu));
3833fcf3ce44SJohn Forte 	}
3834fcf3ce44SJohn Forte 
3835fcf3ce44SJohn Forte 	if (abort_cmd == STMF_LU_ITL_HANDLE_REMOVED) {
38368fe96085Stim szeto 		sbd_check_and_clear_scsi2_reservation(sl, (sbd_it_data_t *)arg);
38378fe96085Stim szeto 		sbd_remove_it_handle(sl, (sbd_it_data_t *)arg);
3838fcf3ce44SJohn Forte 		return (STMF_SUCCESS);
3839fcf3ce44SJohn Forte 	}
3840fcf3ce44SJohn Forte 
3841fcf3ce44SJohn Forte 	ASSERT(abort_cmd == STMF_LU_ABORT_TASK);
3842fcf3ce44SJohn Forte 	task = (scsi_task_t *)arg;
384361dfa509SRick McNeal 	sbd_ats_remove_by_task(task);
3844fcf3ce44SJohn Forte 	if (task->task_lu_private) {
3845fcf3ce44SJohn Forte 		sbd_cmd_t *scmd = (sbd_cmd_t *)task->task_lu_private;
3846fcf3ce44SJohn Forte 
3847fcf3ce44SJohn Forte 		if (scmd->flags & SBD_SCSI_CMD_ACTIVE) {
38486140ef00SDan McDonald 			if (scmd->flags & SBD_SCSI_CMD_TRANS_DATA) {
38496140ef00SDan McDonald 				kmem_free(scmd->trans_data,
38506140ef00SDan McDonald 				    scmd->trans_data_len);
38516140ef00SDan McDonald 				scmd->flags &= ~SBD_SCSI_CMD_TRANS_DATA;
38526140ef00SDan McDonald 			}
3853fcf3ce44SJohn Forte 			scmd->flags &= ~SBD_SCSI_CMD_ACTIVE;
3854fcf3ce44SJohn Forte 			return (STMF_ABORT_SUCCESS);
3855fcf3ce44SJohn Forte 		}
3856fcf3ce44SJohn Forte 	}
3857fcf3ce44SJohn Forte 
3858fcf3ce44SJohn Forte 	return (STMF_NOT_FOUND);
3859fcf3ce44SJohn Forte }
3860fcf3ce44SJohn Forte 
386161dfa509SRick McNeal void
sbd_task_poll(struct scsi_task * task)386261dfa509SRick McNeal sbd_task_poll(struct scsi_task *task)
386361dfa509SRick McNeal {
386461dfa509SRick McNeal 	stmf_data_buf_t *initial_dbuf;
386561dfa509SRick McNeal 
386661dfa509SRick McNeal 	initial_dbuf = stmf_handle_to_buf(task, 0);
386761dfa509SRick McNeal 	sbd_new_task(task, initial_dbuf);
386861dfa509SRick McNeal }
386961dfa509SRick McNeal 
38703fb517f7SJames Moore /*
38713fb517f7SJames Moore  * This function is called during task clean-up if the
38723fb517f7SJames Moore  * DB_LU_FLAG is set on the dbuf. This should only be called for
38733fb517f7SJames Moore  * abort processing after sbd_abort has been called for the task.
38743fb517f7SJames Moore  */
38753fb517f7SJames Moore void
sbd_dbuf_free(struct scsi_task * task,struct stmf_data_buf * dbuf)38763fb517f7SJames Moore sbd_dbuf_free(struct scsi_task *task, struct stmf_data_buf *dbuf)
38773fb517f7SJames Moore {
38783fb517f7SJames Moore 	sbd_cmd_t *scmd = (sbd_cmd_t *)task->task_lu_private;
38793fb517f7SJames Moore 	sbd_lu_t *sl = (sbd_lu_t *)task->task_lu->lu_provider_private;
38803fb517f7SJames Moore 
38813fb517f7SJames Moore 	ASSERT(dbuf->db_lu_private);
388261dfa509SRick McNeal 	ASSERT(scmd && ATOMIC8_GET(scmd->nbufs) > 0);
38833fb517f7SJames Moore 	ASSERT((scmd->flags & SBD_SCSI_CMD_ACTIVE) == 0);
38843fb517f7SJames Moore 	ASSERT(dbuf->db_flags & DB_LU_DATA_BUF);
38853fb517f7SJames Moore 	ASSERT(task->task_additional_flags & TASK_AF_ACCEPT_LU_DBUF);
38863fb517f7SJames Moore 	ASSERT((curthread->t_flag & T_INTR_THREAD) == 0);
38873fb517f7SJames Moore 
38883fb517f7SJames Moore 	if (scmd->cmd_type == SBD_CMD_SCSI_READ) {
38893fb517f7SJames Moore 		sbd_zvol_rele_read_bufs(sl, dbuf);
38903fb517f7SJames Moore 	} else if (scmd->cmd_type == SBD_CMD_SCSI_WRITE) {
38913fb517f7SJames Moore 		sbd_zvol_rele_write_bufs_abort(sl, dbuf);
38923fb517f7SJames Moore 	} else {
38933fb517f7SJames Moore 		cmn_err(CE_PANIC, "Unknown cmd type %d, task = %p",
38943fb517f7SJames Moore 		    scmd->cmd_type, (void *)task);
38953fb517f7SJames Moore 	}
389661dfa509SRick McNeal 	if (atomic_dec_8_nv(&scmd->nbufs) == 0)
38973fb517f7SJames Moore 		rw_exit(&sl->sl_access_state_lock);
38983fb517f7SJames Moore 	stmf_teardown_dbuf(task, dbuf);
38993fb517f7SJames Moore 	stmf_free(dbuf);
39003fb517f7SJames Moore }
39013fb517f7SJames Moore 
3902fcf3ce44SJohn Forte /* ARGSUSED */
3903fcf3ce44SJohn Forte void
sbd_ctl(struct stmf_lu * lu,int cmd,void * arg)3904fcf3ce44SJohn Forte sbd_ctl(struct stmf_lu *lu, int cmd, void *arg)
3905fcf3ce44SJohn Forte {
39068fe96085Stim szeto 	sbd_lu_t *sl = (sbd_lu_t *)lu->lu_provider_private;
3907fcf3ce44SJohn Forte 	stmf_change_status_t st;
3908fcf3ce44SJohn Forte 
3909fcf3ce44SJohn Forte 	ASSERT((cmd == STMF_CMD_LU_ONLINE) ||
39101b7fc709Stim szeto 	    (cmd == STMF_CMD_LU_OFFLINE) ||
39111b7fc709Stim szeto 	    (cmd == STMF_ACK_LU_ONLINE_COMPLETE) ||
39121b7fc709Stim szeto 	    (cmd == STMF_ACK_LU_OFFLINE_COMPLETE));
3913fcf3ce44SJohn Forte 
3914fcf3ce44SJohn Forte 	st.st_completion_status = STMF_SUCCESS;
3915fcf3ce44SJohn Forte 	st.st_additional_info = NULL;
3916fcf3ce44SJohn Forte 
3917fcf3ce44SJohn Forte 	switch (cmd) {
3918fcf3ce44SJohn Forte 	case STMF_CMD_LU_ONLINE:
39198fe96085Stim szeto 		if (sl->sl_state == STMF_STATE_ONLINE)
3920fcf3ce44SJohn Forte 			st.st_completion_status = STMF_ALREADY;
39218fe96085Stim szeto 		else if (sl->sl_state != STMF_STATE_OFFLINE)
3922fcf3ce44SJohn Forte 			st.st_completion_status = STMF_FAILURE;
3923fcf3ce44SJohn Forte 		if (st.st_completion_status == STMF_SUCCESS) {
39248fe96085Stim szeto 			sl->sl_state = STMF_STATE_ONLINE;
39258fe96085Stim szeto 			sl->sl_state_not_acked = 1;
3926fcf3ce44SJohn Forte 		}
3927fcf3ce44SJohn Forte 		(void) stmf_ctl(STMF_CMD_LU_ONLINE_COMPLETE, lu, &st);
3928fcf3ce44SJohn Forte 		break;
3929fcf3ce44SJohn Forte 
3930fcf3ce44SJohn Forte 	case STMF_CMD_LU_OFFLINE:
39318fe96085Stim szeto 		if (sl->sl_state == STMF_STATE_OFFLINE)
3932fcf3ce44SJohn Forte 			st.st_completion_status = STMF_ALREADY;
39338fe96085Stim szeto 		else if (sl->sl_state != STMF_STATE_ONLINE)
3934fcf3ce44SJohn Forte 			st.st_completion_status = STMF_FAILURE;
3935fcf3ce44SJohn Forte 		if (st.st_completion_status == STMF_SUCCESS) {
39368fe96085Stim szeto 			sl->sl_flags &= ~(SL_MEDIUM_REMOVAL_PREVENTED |
39378fe96085Stim szeto 			    SL_LU_HAS_SCSI2_RESERVATION);
39388fe96085Stim szeto 			sl->sl_state = STMF_STATE_OFFLINE;
39398fe96085Stim szeto 			sl->sl_state_not_acked = 1;
394045039663SJohn Forte 			sbd_pgr_reset(sl);
3941fcf3ce44SJohn Forte 		}
3942fcf3ce44SJohn Forte 		(void) stmf_ctl(STMF_CMD_LU_OFFLINE_COMPLETE, lu, &st);
3943fcf3ce44SJohn Forte 		break;
3944fcf3ce44SJohn Forte 
3945fcf3ce44SJohn Forte 	case STMF_ACK_LU_ONLINE_COMPLETE:
3946fcf3ce44SJohn Forte 		/* Fallthrough */
3947fcf3ce44SJohn Forte 	case STMF_ACK_LU_OFFLINE_COMPLETE:
39488fe96085Stim szeto 		sl->sl_state_not_acked = 0;
3949fcf3ce44SJohn Forte 		break;
3950fcf3ce44SJohn Forte 
3951fcf3ce44SJohn Forte 	}
3952fcf3ce44SJohn Forte }
3953fcf3ce44SJohn Forte 
3954fcf3ce44SJohn Forte /* ARGSUSED */
3955fcf3ce44SJohn Forte stmf_status_t
sbd_info(uint32_t cmd,stmf_lu_t * lu,void * arg,uint8_t * buf,uint32_t * bufsizep)3956fcf3ce44SJohn Forte sbd_info(uint32_t cmd, stmf_lu_t *lu, void *arg, uint8_t *buf,
39578fe96085Stim szeto     uint32_t *bufsizep)
3958fcf3ce44SJohn Forte {
3959fcf3ce44SJohn Forte 	return (STMF_NOT_SUPPORTED);
3960fcf3ce44SJohn Forte }
3961fcf3ce44SJohn Forte 
3962fcf3ce44SJohn Forte stmf_status_t
sbd_lu_reset_state(stmf_lu_t * lu)3963fcf3ce44SJohn Forte sbd_lu_reset_state(stmf_lu_t *lu)
3964fcf3ce44SJohn Forte {
39658fe96085Stim szeto 	sbd_lu_t *sl = (sbd_lu_t *)lu->lu_provider_private;
3966fcf3ce44SJohn Forte 
39678fe96085Stim szeto 	mutex_enter(&sl->sl_lock);
39688fe96085Stim szeto 	if (sl->sl_flags & SL_SAVED_WRITE_CACHE_DISABLE) {
39698fe96085Stim szeto 		sl->sl_flags |= SL_WRITEBACK_CACHE_DISABLE;
39708fe96085Stim szeto 		mutex_exit(&sl->sl_lock);
397145039663SJohn Forte 		if (sl->sl_access_state == SBD_LU_ACTIVE) {
397245039663SJohn Forte 			(void) sbd_wcd_set(1, sl);
397345039663SJohn Forte 		}
39748fe96085Stim szeto 	} else {
39758fe96085Stim szeto 		sl->sl_flags &= ~SL_WRITEBACK_CACHE_DISABLE;
39768fe96085Stim szeto 		mutex_exit(&sl->sl_lock);
397745039663SJohn Forte 		if (sl->sl_access_state == SBD_LU_ACTIVE) {
397845039663SJohn Forte 			(void) sbd_wcd_set(0, sl);
397945039663SJohn Forte 		}
39808fe96085Stim szeto 	}
398145039663SJohn Forte 	sbd_pgr_reset(sl);
39828fe96085Stim szeto 	sbd_check_and_clear_scsi2_reservation(sl, NULL);
3983fcf3ce44SJohn Forte 	if (stmf_deregister_all_lu_itl_handles(lu) != STMF_SUCCESS) {
3984fcf3ce44SJohn Forte 		return (STMF_FAILURE);
3985fcf3ce44SJohn Forte 	}
3986fcf3ce44SJohn Forte 	return (STMF_SUCCESS);
3987fcf3ce44SJohn Forte }
3988fcf3ce44SJohn Forte 
39898fe96085Stim szeto sbd_status_t
sbd_flush_data_cache(sbd_lu_t * sl,int fsync_done)39908fe96085Stim szeto sbd_flush_data_cache(sbd_lu_t *sl, int fsync_done)
39918fe96085Stim szeto {
3992ec38b3f5SJerry Jelinek 	sbd_status_t ret = SBD_SUCCESS;
39938fe96085Stim szeto 
399461dfa509SRick McNeal 	rw_enter(&sl->sl_access_state_lock, RW_READER);
399561dfa509SRick McNeal 	if ((sl->sl_flags & SL_MEDIA_LOADED) == 0) {
399661dfa509SRick McNeal 		ret = SBD_FILEIO_FAILURE;
399761dfa509SRick McNeal 		goto flush_fail;
399861dfa509SRick McNeal 	}
39998fe96085Stim szeto 	if (fsync_done)
40008fe96085Stim szeto 		goto over_fsync;
40018fe96085Stim szeto 	if ((sl->sl_data_vtype == VREG) || (sl->sl_data_vtype == VBLK)) {
400261dfa509SRick McNeal 		if (VOP_FSYNC(sl->sl_data_vp, FSYNC, kcred, NULL)) {
400361dfa509SRick McNeal 			ret = SBD_FAILURE;
400461dfa509SRick McNeal 			goto flush_fail;
400561dfa509SRick McNeal 		}
40068fe96085Stim szeto 	}
40078fe96085Stim szeto over_fsync:
40088fe96085Stim szeto 	if (((sl->sl_data_vtype == VCHR) || (sl->sl_data_vtype == VBLK)) &&
40098fe96085Stim szeto 	    ((sl->sl_flags & SL_NO_DATA_DKIOFLUSH) == 0)) {
4010ec38b3f5SJerry Jelinek 		int r = 0;
4011ec38b3f5SJerry Jelinek 
4012e59325b7SToomas Soome 		ret = VOP_IOCTL(sl->sl_data_vp, DKIOCFLUSHWRITECACHE, 0,
40138fe96085Stim szeto 		    FKIOCTL, kcred, &r, NULL);
40148fe96085Stim szeto 		if ((ret == ENOTTY) || (ret == ENOTSUP)) {
40158fe96085Stim szeto 			mutex_enter(&sl->sl_lock);
40168fe96085Stim szeto 			sl->sl_flags |= SL_NO_DATA_DKIOFLUSH;
40178fe96085Stim szeto 			mutex_exit(&sl->sl_lock);
401861dfa509SRick McNeal 		} else {
401961dfa509SRick McNeal 			ret = (ret != 0) ? SBD_FAILURE : SBD_SUCCESS;
40208fe96085Stim szeto 		}
40218fe96085Stim szeto 	}
402261dfa509SRick McNeal flush_fail:
402361dfa509SRick McNeal 	rw_exit(&sl->sl_access_state_lock);
40248fe96085Stim szeto 
402561dfa509SRick McNeal 	return (ret);
40268fe96085Stim szeto }
40278fe96085Stim szeto 
4028fcf3ce44SJohn Forte /* ARGSUSED */
4029fcf3ce44SJohn Forte static void
sbd_handle_sync_cache(struct scsi_task * task,struct stmf_data_buf * initial_dbuf)4030fcf3ce44SJohn Forte sbd_handle_sync_cache(struct scsi_task *task,
4031fcf3ce44SJohn Forte     struct stmf_data_buf *initial_dbuf)
4032fcf3ce44SJohn Forte {
40338fe96085Stim szeto 	sbd_lu_t *sl = (sbd_lu_t *)task->task_lu->lu_provider_private;
4034fcf3ce44SJohn Forte 	uint64_t	lba, laddr;
40358fe96085Stim szeto 	sbd_status_t	sret;
4036fcf3ce44SJohn Forte 	uint32_t	len;
4037fcf3ce44SJohn Forte 	int		is_g4 = 0;
4038fcf3ce44SJohn Forte 	int		immed;
4039fcf3ce44SJohn Forte 
40408fe96085Stim szeto 	task->task_cmd_xfer_length = 0;
4041fcf3ce44SJohn Forte 	/*
4042fcf3ce44SJohn Forte 	 * Determine if this is a 10 or 16 byte CDB
4043fcf3ce44SJohn Forte 	 */
4044fcf3ce44SJohn Forte 
4045fcf3ce44SJohn Forte 	if (task->task_cdb[0] == SCMD_SYNCHRONIZE_CACHE_G4)
4046fcf3ce44SJohn Forte 		is_g4 = 1;
4047fcf3ce44SJohn Forte 
4048fcf3ce44SJohn Forte 	/*
4049fcf3ce44SJohn Forte 	 * Determine other requested parameters
4050fcf3ce44SJohn Forte 	 *
4051fcf3ce44SJohn Forte 	 * We don't have a non-volatile cache, so don't care about SYNC_NV.
4052fcf3ce44SJohn Forte 	 * Do not support the IMMED bit.
4053fcf3ce44SJohn Forte 	 */
4054fcf3ce44SJohn Forte 
4055fcf3ce44SJohn Forte 	immed = (task->task_cdb[1] & 0x02);
4056fcf3ce44SJohn Forte 
4057fcf3ce44SJohn Forte 	if (immed) {
4058fcf3ce44SJohn Forte 		stmf_scsilib_send_status(task, STATUS_CHECK,
4059fcf3ce44SJohn Forte 		    STMF_SAA_INVALID_FIELD_IN_CDB);
4060fcf3ce44SJohn Forte 		return;
4061fcf3ce44SJohn Forte 	}
4062fcf3ce44SJohn Forte 
4063fcf3ce44SJohn Forte 	/*
4064fcf3ce44SJohn Forte 	 * Check to be sure we're not being asked to sync an LBA
4065fcf3ce44SJohn Forte 	 * that is out of range.  While checking, verify reserved fields.
4066fcf3ce44SJohn Forte 	 */
4067fcf3ce44SJohn Forte 
4068fcf3ce44SJohn Forte 	if (is_g4) {
4069fcf3ce44SJohn Forte 		if ((task->task_cdb[1] & 0xf9) || task->task_cdb[14] ||
4070fcf3ce44SJohn Forte 		    task->task_cdb[15]) {
4071fcf3ce44SJohn Forte 			stmf_scsilib_send_status(task, STATUS_CHECK,
4072fcf3ce44SJohn Forte 			    STMF_SAA_INVALID_FIELD_IN_CDB);
4073fcf3ce44SJohn Forte 			return;
4074fcf3ce44SJohn Forte 		}
4075fcf3ce44SJohn Forte 
4076fcf3ce44SJohn Forte 		lba = READ_SCSI64(&task->task_cdb[2], uint64_t);
4077fcf3ce44SJohn Forte 		len = READ_SCSI32(&task->task_cdb[10], uint32_t);
4078fcf3ce44SJohn Forte 	} else {
4079fcf3ce44SJohn Forte 		if ((task->task_cdb[1] & 0xf9) || task->task_cdb[6] ||
4080fcf3ce44SJohn Forte 		    task->task_cdb[9]) {
4081fcf3ce44SJohn Forte 			stmf_scsilib_send_status(task, STATUS_CHECK,
4082fcf3ce44SJohn Forte 			    STMF_SAA_INVALID_FIELD_IN_CDB);
4083fcf3ce44SJohn Forte 			return;
4084fcf3ce44SJohn Forte 		}
4085fcf3ce44SJohn Forte 
4086fcf3ce44SJohn Forte 		lba = READ_SCSI32(&task->task_cdb[2], uint64_t);
4087fcf3ce44SJohn Forte 		len = READ_SCSI16(&task->task_cdb[7], uint32_t);
4088fcf3ce44SJohn Forte 	}
4089fcf3ce44SJohn Forte 
40908fe96085Stim szeto 	laddr = lba << sl->sl_data_blocksize_shift;
40918fe96085Stim szeto 	len <<= sl->sl_data_blocksize_shift;
4092fcf3ce44SJohn Forte 
40938fe96085Stim szeto 	if ((laddr + (uint64_t)len) > sl->sl_lu_size) {
4094fcf3ce44SJohn Forte 		stmf_scsilib_send_status(task, STATUS_CHECK,
4095fcf3ce44SJohn Forte 		    STMF_SAA_LBA_OUT_OF_RANGE);
4096fcf3ce44SJohn Forte 		return;
4097fcf3ce44SJohn Forte 	}
4098fcf3ce44SJohn Forte 
40998fe96085Stim szeto 	sret = sbd_flush_data_cache(sl, 0);
41008fe96085Stim szeto 	if (sret != SBD_SUCCESS) {
4101fcf3ce44SJohn Forte 		stmf_scsilib_send_status(task, STATUS_CHECK,
4102fcf3ce44SJohn Forte 		    STMF_SAA_WRITE_ERROR);
4103fcf3ce44SJohn Forte 		return;
4104fcf3ce44SJohn Forte 	}
4105fcf3ce44SJohn Forte 
4106fcf3ce44SJohn Forte 	stmf_scsilib_send_status(task, STATUS_GOOD, 0);
4107fcf3ce44SJohn Forte }
4108