1*61dfa509SRick McNeal /*
2*61dfa509SRick McNeal  * This file and its contents are supplied under the terms of the
3*61dfa509SRick McNeal  * Common Development and Distribution License ("CDDL"), version 1.0.
4*61dfa509SRick McNeal  * You may only use this file in accordance with the terms of version
5*61dfa509SRick McNeal  * 1.0 of the CDDL.
6*61dfa509SRick McNeal  *
7*61dfa509SRick McNeal  * A full copy of the text of the CDDL should have accompanied this
8*61dfa509SRick McNeal  * source.  A copy of the CDDL is also available via the Internet at
9*61dfa509SRick McNeal  * http://www.illumos.org/license/CDDL.
10*61dfa509SRick McNeal  */
11*61dfa509SRick McNeal 
12*61dfa509SRick McNeal /*
13*61dfa509SRick McNeal  * Copyright 2019 Nexenta Systems, Inc.  All rights reserved.
14*61dfa509SRick McNeal  */
15*61dfa509SRick McNeal 
16*61dfa509SRick McNeal #include <sys/conf.h>
17*61dfa509SRick McNeal #include <sys/file.h>
18*61dfa509SRick McNeal #include <sys/ddi.h>
19*61dfa509SRick McNeal #include <sys/sunddi.h>
20*61dfa509SRick McNeal #include <sys/modctl.h>
21*61dfa509SRick McNeal #include <sys/scsi/scsi.h>
22*61dfa509SRick McNeal #include <sys/scsi/impl/scsi_reset_notify.h>
23*61dfa509SRick McNeal #include <sys/scsi/generic/mode.h>
24*61dfa509SRick McNeal #include <sys/disp.h>
25*61dfa509SRick McNeal #include <sys/byteorder.h>
26*61dfa509SRick McNeal #include <sys/atomic.h>
27*61dfa509SRick McNeal #include <sys/sdt.h>
28*61dfa509SRick McNeal #include <sys/dkio.h>
29*61dfa509SRick McNeal 
30*61dfa509SRick McNeal #include <sys/dmu.h>
31*61dfa509SRick McNeal #include <sys/txg.h>
32*61dfa509SRick McNeal #include <sys/refcount.h>
33*61dfa509SRick McNeal #include <sys/zvol.h>
34*61dfa509SRick McNeal 
35*61dfa509SRick McNeal #include <sys/stmf.h>
36*61dfa509SRick McNeal #include <sys/lpif.h>
37*61dfa509SRick McNeal #include <sys/portif.h>
38*61dfa509SRick McNeal #include <sys/stmf_ioctl.h>
39*61dfa509SRick McNeal #include <sys/stmf_sbd_ioctl.h>
40*61dfa509SRick McNeal 
41*61dfa509SRick McNeal #include "stmf_sbd.h"
42*61dfa509SRick McNeal #include "sbd_impl.h"
43*61dfa509SRick McNeal 
44*61dfa509SRick McNeal /* ATS tuning parameters */
45*61dfa509SRick McNeal #define	OVERLAP_OFF 0
46*61dfa509SRick McNeal #define	OVERLAP_LOW 1
47*61dfa509SRick McNeal #define	OVERLAP_MEDIUM 2
48*61dfa509SRick McNeal #define	OVERLAP_HIGH 3
49*61dfa509SRick McNeal uint8_t ats_overlap_check = OVERLAP_LOW; /* check for rw overlap with ATS */
50*61dfa509SRick McNeal 
51*61dfa509SRick McNeal uint8_t HardwareAcceleratedLocking = 1; /* 0 for disabled */
52*61dfa509SRick McNeal uint8_t HardwareAcceleratedMove = 1;
53*61dfa509SRick McNeal uint64_t sbd_list_length = 0;
54*61dfa509SRick McNeal 
55*61dfa509SRick McNeal #define	SBD_ATS_MAX_NBLKS	32
56*61dfa509SRick McNeal /* ATS routines. */
57*61dfa509SRick McNeal uint8_t
sbd_ats_max_nblks(void)58*61dfa509SRick McNeal sbd_ats_max_nblks(void)
59*61dfa509SRick McNeal {
60*61dfa509SRick McNeal 	if (HardwareAcceleratedLocking == 0)
61*61dfa509SRick McNeal 		return (0);
62*61dfa509SRick McNeal 	return (SBD_ATS_MAX_NBLKS);
63*61dfa509SRick McNeal }
64*61dfa509SRick McNeal 
65*61dfa509SRick McNeal #define	is_overlapping(start1, len1, start2, len2) \
66*61dfa509SRick McNeal 	((start2) > (start1) ? ((start2) - (start1)) < (len1) : \
67*61dfa509SRick McNeal 	((start1) - (start2)) < (len2))
68*61dfa509SRick McNeal 
69*61dfa509SRick McNeal /*ARGSUSED*/
70*61dfa509SRick McNeal static sbd_status_t
sbd_ats_do_handling_before_io(scsi_task_t * task,struct sbd_lu * sl,uint64_t lba,uint64_t count,uint32_t flags)71*61dfa509SRick McNeal sbd_ats_do_handling_before_io(scsi_task_t *task, struct sbd_lu *sl,
72*61dfa509SRick McNeal     uint64_t lba, uint64_t count, uint32_t flags)
73*61dfa509SRick McNeal {
74*61dfa509SRick McNeal 	sbd_status_t ret = SBD_SUCCESS;
75*61dfa509SRick McNeal 	ats_state_t *ats_state, *ats_state_ret;
76*61dfa509SRick McNeal 	sbd_cmd_t *scmd = (sbd_cmd_t *)task->task_lu_private;
77*61dfa509SRick McNeal 	uint8_t cdb0 = task->task_cdb[0];
78*61dfa509SRick McNeal 
79*61dfa509SRick McNeal 	if (scmd == NULL)
80*61dfa509SRick McNeal 		return (SBD_SUCCESS);
81*61dfa509SRick McNeal 
82*61dfa509SRick McNeal 	if (HardwareAcceleratedLocking == 0)
83*61dfa509SRick McNeal 		return (SBD_SUCCESS);
84*61dfa509SRick McNeal 	/*
85*61dfa509SRick McNeal 	 * if ATS overlap checking is disabled just return.  The check
86*61dfa509SRick McNeal 	 * is not done in the function to remove items from the list which
87*61dfa509SRick McNeal 	 * allows this value to be changed at runtime.  If it is turned on
88*61dfa509SRick McNeal 	 * at runtime the remove will just start taking items off the list.
89*61dfa509SRick McNeal 	 * If it is turned off at runtime the list is still cleaned up.
90*61dfa509SRick McNeal 	 */
91*61dfa509SRick McNeal 	if (ats_overlap_check == OVERLAP_OFF)
92*61dfa509SRick McNeal 		return (SBD_SUCCESS);
93*61dfa509SRick McNeal 
94*61dfa509SRick McNeal 	/* overlap checking for compare and write only */
95*61dfa509SRick McNeal 	if (ats_overlap_check == OVERLAP_LOW) {
96*61dfa509SRick McNeal 		if (cdb0 != SCMD_COMPARE_AND_WRITE)
97*61dfa509SRick McNeal 			return (SBD_SUCCESS);
98*61dfa509SRick McNeal 	}
99*61dfa509SRick McNeal 
100*61dfa509SRick McNeal 	/* overlap checking for compare and write and write only */
101*61dfa509SRick McNeal 	if (ats_overlap_check == OVERLAP_MEDIUM) {
102*61dfa509SRick McNeal 		if ((cdb0 != SCMD_COMPARE_AND_WRITE) && (cdb0 != SCMD_WRITE))
103*61dfa509SRick McNeal 			return (SBD_SUCCESS);
104*61dfa509SRick McNeal 	}
105*61dfa509SRick McNeal 
106*61dfa509SRick McNeal 	mutex_enter(&sl->sl_lock);
107*61dfa509SRick McNeal 	/*
108*61dfa509SRick McNeal 	 * if the list is empty then just add the element to the list and
109*61dfa509SRick McNeal 	 * return success. There is no overlap.  This is done for every
110*61dfa509SRick McNeal 	 * read, write or compare and write.
111*61dfa509SRick McNeal 	 */
112*61dfa509SRick McNeal 	if (list_is_empty(&sl->sl_ats_io_list)) {
113*61dfa509SRick McNeal 		goto done;
114*61dfa509SRick McNeal 	}
115*61dfa509SRick McNeal 
116*61dfa509SRick McNeal 	/*
117*61dfa509SRick McNeal 	 * There are inflight operations.  As a result the list must be scanned
118*61dfa509SRick McNeal 	 * and if there are any overlaps then SBD_BUSY should be returned.
119*61dfa509SRick McNeal 	 *
120*61dfa509SRick McNeal 	 * Duplicate reads and writes are allowed and kept on the list
121*61dfa509SRick McNeal 	 * since there is no reason that overlapping IO operations should
122*61dfa509SRick McNeal 	 * be delayed.
123*61dfa509SRick McNeal 	 *
124*61dfa509SRick McNeal 	 * A command that conflicts with a running compare and write will
125*61dfa509SRick McNeal 	 * be rescheduled and rerun.  This is handled by stmf_task_poll_lu.
126*61dfa509SRick McNeal 	 * There is a possibility that a command can be starved and still
127*61dfa509SRick McNeal 	 * return busy, which is valid in the SCSI protocol.
128*61dfa509SRick McNeal 	 */
129*61dfa509SRick McNeal 
130*61dfa509SRick McNeal 	for (ats_state = list_head(&sl->sl_ats_io_list); ats_state != NULL;
131*61dfa509SRick McNeal 	    ats_state = list_next(&sl->sl_ats_io_list, ats_state)) {
132*61dfa509SRick McNeal 
133*61dfa509SRick McNeal 		if (is_overlapping(ats_state->as_cur_ats_lba,
134*61dfa509SRick McNeal 		    ats_state->as_cur_ats_len, lba, count) == 0)
135*61dfa509SRick McNeal 			continue;
136*61dfa509SRick McNeal 
137*61dfa509SRick McNeal 		/* if the task is already listed just return */
138*61dfa509SRick McNeal 		if (task == ats_state->as_cur_ats_task) {
139*61dfa509SRick McNeal 			cmn_err(CE_WARN, "sbd_ats_handling_before_io: "
140*61dfa509SRick McNeal 			    "task %p already on list", (void *) task);
141*61dfa509SRick McNeal 			ret = SBD_SUCCESS;
142*61dfa509SRick McNeal 			goto exit;
143*61dfa509SRick McNeal 		}
144*61dfa509SRick McNeal 		/*
145*61dfa509SRick McNeal 		 * the current command is a compare and write, if there is any
146*61dfa509SRick McNeal 		 * overlap return error
147*61dfa509SRick McNeal 		 */
148*61dfa509SRick McNeal 
149*61dfa509SRick McNeal 		if ((cdb0 == SCMD_COMPARE_AND_WRITE) ||
150*61dfa509SRick McNeal 		    (ats_state->as_cmd == SCMD_COMPARE_AND_WRITE)) {
151*61dfa509SRick McNeal 			ret = SBD_BUSY;
152*61dfa509SRick McNeal 			goto exit;
153*61dfa509SRick McNeal 		}
154*61dfa509SRick McNeal 	}
155*61dfa509SRick McNeal done:
156*61dfa509SRick McNeal 	ats_state_ret =
157*61dfa509SRick McNeal 	    (ats_state_t *)kmem_zalloc(sizeof (ats_state_t), KM_SLEEP);
158*61dfa509SRick McNeal 	ats_state_ret->as_cur_ats_lba = lba;
159*61dfa509SRick McNeal 	ats_state_ret->as_cur_ats_len = count;
160*61dfa509SRick McNeal 	ats_state_ret->as_cmd = cdb0;
161*61dfa509SRick McNeal 	ats_state_ret->as_cur_ats_task = task;
162*61dfa509SRick McNeal 	if (list_is_empty(&sl->sl_ats_io_list)) {
163*61dfa509SRick McNeal 		list_insert_head(&sl->sl_ats_io_list, ats_state_ret);
164*61dfa509SRick McNeal 	} else {
165*61dfa509SRick McNeal 		list_insert_tail(&sl->sl_ats_io_list, ats_state_ret);
166*61dfa509SRick McNeal 	}
167*61dfa509SRick McNeal 	scmd->flags |= SBD_SCSI_CMD_ATS_RELATED;
168*61dfa509SRick McNeal 	scmd->ats_state = ats_state;
169*61dfa509SRick McNeal 	sbd_list_length++;
170*61dfa509SRick McNeal 	mutex_exit(&sl->sl_lock);
171*61dfa509SRick McNeal 	return (SBD_SUCCESS);
172*61dfa509SRick McNeal 
173*61dfa509SRick McNeal exit:
174*61dfa509SRick McNeal 	mutex_exit(&sl->sl_lock);
175*61dfa509SRick McNeal 	return (ret);
176*61dfa509SRick McNeal }
177*61dfa509SRick McNeal 
178*61dfa509SRick McNeal sbd_status_t
sbd_ats_handling_before_io(scsi_task_t * task,struct sbd_lu * sl,uint64_t lba,uint64_t count)179*61dfa509SRick McNeal sbd_ats_handling_before_io(scsi_task_t *task, struct sbd_lu *sl,
180*61dfa509SRick McNeal     uint64_t lba, uint64_t count)
181*61dfa509SRick McNeal {
182*61dfa509SRick McNeal 	return (sbd_ats_do_handling_before_io(task, sl, lba, count, 0));
183*61dfa509SRick McNeal }
184*61dfa509SRick McNeal 
185*61dfa509SRick McNeal void
sbd_ats_remove_by_task(scsi_task_t * task)186*61dfa509SRick McNeal sbd_ats_remove_by_task(scsi_task_t *task)
187*61dfa509SRick McNeal {
188*61dfa509SRick McNeal 	ats_state_t *ats_state;
189*61dfa509SRick McNeal 	sbd_lu_t *sl = (sbd_lu_t *)task->task_lu->lu_provider_private;
190*61dfa509SRick McNeal 	sbd_cmd_t *scmd = task->task_lu_private;
191*61dfa509SRick McNeal 
192*61dfa509SRick McNeal 	if (scmd == NULL)
193*61dfa509SRick McNeal 		return;
194*61dfa509SRick McNeal 	/*
195*61dfa509SRick McNeal 	 * Scan the list and take the task off of the list. It is possible
196*61dfa509SRick McNeal 	 * that the call is made in a situation where the task is not
197*61dfa509SRick McNeal 	 * listed.  That is a valid but unlikely case. If it happens
198*61dfa509SRick McNeal 	 * just fall through and return.  The list removal is done by
199*61dfa509SRick McNeal 	 * task not LBA range and a task cannot be active for more than
200*61dfa509SRick McNeal 	 * one command so there is never an issue about removing the
201*61dfa509SRick McNeal 	 * wrong element.
202*61dfa509SRick McNeal 	 */
203*61dfa509SRick McNeal 	mutex_enter(&sl->sl_lock);
204*61dfa509SRick McNeal 	if (list_is_empty(&sl->sl_ats_io_list)) {
205*61dfa509SRick McNeal 		mutex_exit(&sl->sl_lock);
206*61dfa509SRick McNeal 		return;
207*61dfa509SRick McNeal 	}
208*61dfa509SRick McNeal 
209*61dfa509SRick McNeal 	for (ats_state = list_head(&sl->sl_ats_io_list); ats_state != NULL;
210*61dfa509SRick McNeal 	    ats_state = list_next(&sl->sl_ats_io_list, ats_state)) {
211*61dfa509SRick McNeal 
212*61dfa509SRick McNeal 		if (ats_state->as_cur_ats_task == task) {
213*61dfa509SRick McNeal 			list_remove(&sl->sl_ats_io_list, ats_state);
214*61dfa509SRick McNeal 			kmem_free(ats_state, sizeof (ats_state_t));
215*61dfa509SRick McNeal 			scmd->flags &= ~SBD_SCSI_CMD_ATS_RELATED;
216*61dfa509SRick McNeal 			scmd->ats_state = NULL;
217*61dfa509SRick McNeal 			sbd_list_length--;
218*61dfa509SRick McNeal 			break;
219*61dfa509SRick McNeal 		}
220*61dfa509SRick McNeal 	}
221*61dfa509SRick McNeal 	mutex_exit(&sl->sl_lock);
222*61dfa509SRick McNeal }
223*61dfa509SRick McNeal 
224*61dfa509SRick McNeal static sbd_status_t
sbd_compare_and_write(struct scsi_task * task,sbd_cmd_t * scmd,uint32_t * ret_off)225*61dfa509SRick McNeal sbd_compare_and_write(struct scsi_task *task, sbd_cmd_t *scmd,
226*61dfa509SRick McNeal     uint32_t *ret_off)
227*61dfa509SRick McNeal {
228*61dfa509SRick McNeal 	sbd_lu_t *sl = (sbd_lu_t *)task->task_lu->lu_provider_private;
229*61dfa509SRick McNeal 	uint8_t *buf;
230*61dfa509SRick McNeal 	sbd_status_t ret;
231*61dfa509SRick McNeal 	uint64_t addr;
232*61dfa509SRick McNeal 	uint32_t len, i;
233*61dfa509SRick McNeal 
234*61dfa509SRick McNeal 	addr = READ_SCSI64(&task->task_cdb[2], uint64_t);
235*61dfa509SRick McNeal 	len = (uint32_t)task->task_cdb[13];
236*61dfa509SRick McNeal 
237*61dfa509SRick McNeal 	addr <<= sl->sl_data_blocksize_shift;
238*61dfa509SRick McNeal 	len <<= sl->sl_data_blocksize_shift;
239*61dfa509SRick McNeal 	buf = kmem_alloc(len, KM_SLEEP);
240*61dfa509SRick McNeal 	ret = sbd_data_read(sl, task, addr, (uint64_t)len, buf);
241*61dfa509SRick McNeal 	if (ret != SBD_SUCCESS) {
242*61dfa509SRick McNeal 		goto compare_and_write_done;
243*61dfa509SRick McNeal 	}
244*61dfa509SRick McNeal 	/*
245*61dfa509SRick McNeal 	 * Can't use bcmp here. We need mismatch offset.
246*61dfa509SRick McNeal 	 */
247*61dfa509SRick McNeal 	for (i = 0; i < len; i++) {
248*61dfa509SRick McNeal 		if (buf[i] != scmd->trans_data[i])
249*61dfa509SRick McNeal 			break;
250*61dfa509SRick McNeal 	}
251*61dfa509SRick McNeal 	if (i != len) {
252*61dfa509SRick McNeal 		*ret_off = i;
253*61dfa509SRick McNeal 		ret = SBD_COMPARE_FAILED;
254*61dfa509SRick McNeal 		goto compare_and_write_done;
255*61dfa509SRick McNeal 	}
256*61dfa509SRick McNeal 
257*61dfa509SRick McNeal 	ret = sbd_data_write(sl, task, addr, (uint64_t)len,
258*61dfa509SRick McNeal 	    scmd->trans_data + len);
259*61dfa509SRick McNeal 
260*61dfa509SRick McNeal compare_and_write_done:
261*61dfa509SRick McNeal 	kmem_free(buf, len);
262*61dfa509SRick McNeal 	return (ret);
263*61dfa509SRick McNeal }
264*61dfa509SRick McNeal 
265*61dfa509SRick McNeal static void
sbd_send_miscompare_status(struct scsi_task * task,uint32_t miscompare_off)266*61dfa509SRick McNeal sbd_send_miscompare_status(struct scsi_task *task, uint32_t miscompare_off)
267*61dfa509SRick McNeal {
268*61dfa509SRick McNeal 	uint8_t sd[18];
269*61dfa509SRick McNeal 
270*61dfa509SRick McNeal 	task->task_scsi_status = STATUS_CHECK;
271*61dfa509SRick McNeal 	bzero(sd, 18);
272*61dfa509SRick McNeal 	sd[0] = 0xF0;
273*61dfa509SRick McNeal 	sd[2] = 0xe;
274*61dfa509SRick McNeal 	SCSI_WRITE32(&sd[3], miscompare_off);
275*61dfa509SRick McNeal 	sd[7] = 10;
276*61dfa509SRick McNeal 	sd[12] = 0x1D;
277*61dfa509SRick McNeal 	task->task_sense_data = sd;
278*61dfa509SRick McNeal 	task->task_sense_length = 18;
279*61dfa509SRick McNeal 	(void) stmf_send_scsi_status(task, STMF_IOF_LU_DONE);
280*61dfa509SRick McNeal }
281*61dfa509SRick McNeal 
282*61dfa509SRick McNeal static void
sbd_ats_release_resources(struct scsi_task * task)283*61dfa509SRick McNeal sbd_ats_release_resources(struct scsi_task *task)
284*61dfa509SRick McNeal {
285*61dfa509SRick McNeal 	sbd_cmd_t *scmd = (sbd_cmd_t *)task->task_lu_private;
286*61dfa509SRick McNeal 
287*61dfa509SRick McNeal 	/*
288*61dfa509SRick McNeal 	 * a few basic check here to be sure that there are not multiple
289*61dfa509SRick McNeal 	 * calls going on.  If scmd is null just return.  This is very
290*61dfa509SRick McNeal 	 * unlikely, but could happed if the task is freed by an abort.
291*61dfa509SRick McNeal 	 * If nbufs is invalid warn but ignore the error.  Last if the
292*61dfa509SRick McNeal 	 * trans_data is either null or the lenght is zero just blow
293*61dfa509SRick McNeal 	 * off the operation and leak the memory buffer.
294*61dfa509SRick McNeal 	 */
295*61dfa509SRick McNeal 	if (scmd == NULL)
296*61dfa509SRick McNeal 		return;
297*61dfa509SRick McNeal 
298*61dfa509SRick McNeal 	if (scmd->nbufs == 0xFF)
299*61dfa509SRick McNeal 		cmn_err(CE_WARN, "%s invalid buffer count %x", __func__,
300*61dfa509SRick McNeal 		    scmd->nbufs);
301*61dfa509SRick McNeal 
302*61dfa509SRick McNeal 	if ((scmd->trans_data != NULL) && (scmd->trans_data_len != 0))
303*61dfa509SRick McNeal 		kmem_free(scmd->trans_data, scmd->trans_data_len);
304*61dfa509SRick McNeal 
305*61dfa509SRick McNeal 	scmd->trans_data = NULL; /* force panic later if re-entered */
306*61dfa509SRick McNeal 	scmd->trans_data_len = 0;
307*61dfa509SRick McNeal 	scmd->flags &= ~SBD_SCSI_CMD_TRANS_DATA;
308*61dfa509SRick McNeal }
309*61dfa509SRick McNeal 
310*61dfa509SRick McNeal void
sbd_handle_ats_xfer_completion(struct scsi_task * task,sbd_cmd_t * scmd,struct stmf_data_buf * dbuf,uint8_t dbuf_reusable)311*61dfa509SRick McNeal sbd_handle_ats_xfer_completion(struct scsi_task *task, sbd_cmd_t *scmd,
312*61dfa509SRick McNeal     struct stmf_data_buf *dbuf, uint8_t dbuf_reusable)
313*61dfa509SRick McNeal {
314*61dfa509SRick McNeal 	uint64_t laddr;
315*61dfa509SRick McNeal 	uint32_t buflen, iolen, miscompare_off;
316*61dfa509SRick McNeal 	int ndx;
317*61dfa509SRick McNeal 	sbd_status_t ret;
318*61dfa509SRick McNeal 
319*61dfa509SRick McNeal 	if (ATOMIC8_GET(scmd->nbufs) > 0) {
320*61dfa509SRick McNeal 		atomic_dec_8(&scmd->nbufs);
321*61dfa509SRick McNeal 	}
322*61dfa509SRick McNeal 
323*61dfa509SRick McNeal 	if (dbuf->db_xfer_status != STMF_SUCCESS) {
324*61dfa509SRick McNeal 		sbd_ats_remove_by_task(task);
325*61dfa509SRick McNeal 		sbd_ats_release_resources(task);
326*61dfa509SRick McNeal 		stmf_abort(STMF_QUEUE_TASK_ABORT, task,
327*61dfa509SRick McNeal 		    dbuf->db_xfer_status, NULL);
328*61dfa509SRick McNeal 		return;
329*61dfa509SRick McNeal 	}
330*61dfa509SRick McNeal 
331*61dfa509SRick McNeal 	if (scmd->flags & SBD_SCSI_CMD_XFER_FAIL) {
332*61dfa509SRick McNeal 		goto ATS_XFER_DONE;
333*61dfa509SRick McNeal 	}
334*61dfa509SRick McNeal 
335*61dfa509SRick McNeal 	/* if state is confused drop the command */
336*61dfa509SRick McNeal 	if ((scmd->trans_data == NULL) ||
337*61dfa509SRick McNeal 	    ((scmd->flags & SBD_SCSI_CMD_TRANS_DATA) == 0) ||
338*61dfa509SRick McNeal 	    ((scmd->flags & SBD_SCSI_CMD_ACTIVE) == 0)) {
339*61dfa509SRick McNeal 		scmd->flags &= ~SBD_SCSI_CMD_ACTIVE;
340*61dfa509SRick McNeal 		return;
341*61dfa509SRick McNeal 	}
342*61dfa509SRick McNeal 
343*61dfa509SRick McNeal 	if (ATOMIC32_GET(scmd->len) != 0) {
344*61dfa509SRick McNeal 		/*
345*61dfa509SRick McNeal 		 * Initiate the next port xfer to occur in parallel
346*61dfa509SRick McNeal 		 * with writing this buf.  A side effect of sbd_do_ats_xfer is
347*61dfa509SRick McNeal 		 * it may set scmd_len to 0.  This means all the data
348*61dfa509SRick McNeal 		 * transfers have been started, not that they are done.
349*61dfa509SRick McNeal 		 */
350*61dfa509SRick McNeal 		sbd_do_ats_xfer(task, scmd, NULL, 0);
351*61dfa509SRick McNeal 	}
352*61dfa509SRick McNeal 
353*61dfa509SRick McNeal 	/*
354*61dfa509SRick McNeal 	 * move the most recent data transfer to the temporary buffer
355*61dfa509SRick McNeal 	 * used for the compare and write function.
356*61dfa509SRick McNeal 	 */
357*61dfa509SRick McNeal 	laddr = dbuf->db_relative_offset;
358*61dfa509SRick McNeal 	for (buflen = 0, ndx = 0; (buflen < dbuf->db_data_size) &&
359*61dfa509SRick McNeal 	    (ndx < dbuf->db_sglist_length); ndx++) {
360*61dfa509SRick McNeal 		iolen = min(dbuf->db_data_size - buflen,
361*61dfa509SRick McNeal 		    dbuf->db_sglist[ndx].seg_length);
362*61dfa509SRick McNeal 		if (iolen == 0)
363*61dfa509SRick McNeal 			break;
364*61dfa509SRick McNeal 		bcopy(dbuf->db_sglist[ndx].seg_addr, &scmd->trans_data[laddr],
365*61dfa509SRick McNeal 		    iolen);
366*61dfa509SRick McNeal 		buflen += iolen;
367*61dfa509SRick McNeal 		laddr += (uint64_t)iolen;
368*61dfa509SRick McNeal 	}
369*61dfa509SRick McNeal 	task->task_nbytes_transferred += buflen;
370*61dfa509SRick McNeal 
371*61dfa509SRick McNeal ATS_XFER_DONE:
372*61dfa509SRick McNeal 	if (ATOMIC32_GET(scmd->len) == 0 ||
373*61dfa509SRick McNeal 	    scmd->flags & SBD_SCSI_CMD_XFER_FAIL) {
374*61dfa509SRick McNeal 		stmf_free_dbuf(task, dbuf);
375*61dfa509SRick McNeal 		/*
376*61dfa509SRick McNeal 		 * if this is not the last buffer to be transfered then exit
377*61dfa509SRick McNeal 		 * and wait for the next buffer.  Once nbufs is 0 then all the
378*61dfa509SRick McNeal 		 * data has arrived and the compare can be done.
379*61dfa509SRick McNeal 		 */
380*61dfa509SRick McNeal 		if (ATOMIC8_GET(scmd->nbufs) > 0)
381*61dfa509SRick McNeal 			return;
382*61dfa509SRick McNeal 		scmd->flags &= ~SBD_SCSI_CMD_ACTIVE;
383*61dfa509SRick McNeal 		if (scmd->flags & SBD_SCSI_CMD_XFER_FAIL) {
384*61dfa509SRick McNeal 			sbd_ats_remove_by_task(task);
385*61dfa509SRick McNeal 			stmf_scsilib_send_status(task, STATUS_CHECK,
386*61dfa509SRick McNeal 			    STMF_SAA_WRITE_ERROR);
387*61dfa509SRick McNeal 		} else {
388*61dfa509SRick McNeal 			ret = sbd_compare_and_write(task, scmd,
389*61dfa509SRick McNeal 			    &miscompare_off);
390*61dfa509SRick McNeal 			sbd_ats_remove_by_task(task);
391*61dfa509SRick McNeal 			sbd_ats_release_resources(task);
392*61dfa509SRick McNeal 			if (ret != SBD_SUCCESS) {
393*61dfa509SRick McNeal 				if (ret != SBD_COMPARE_FAILED) {
394*61dfa509SRick McNeal 					stmf_scsilib_send_status(task,
395*61dfa509SRick McNeal 					    STATUS_CHECK, STMF_SAA_WRITE_ERROR);
396*61dfa509SRick McNeal 				} else {
397*61dfa509SRick McNeal 					sbd_send_miscompare_status(task,
398*61dfa509SRick McNeal 					    miscompare_off);
399*61dfa509SRick McNeal 				}
400*61dfa509SRick McNeal 			} else {
401*61dfa509SRick McNeal 				stmf_scsilib_send_status(task, STATUS_GOOD, 0);
402*61dfa509SRick McNeal 			}
403*61dfa509SRick McNeal 		}
404*61dfa509SRick McNeal 		return;
405*61dfa509SRick McNeal 	}
406*61dfa509SRick McNeal 	sbd_do_ats_xfer(task, scmd, dbuf, dbuf_reusable);
407*61dfa509SRick McNeal }
408*61dfa509SRick McNeal 
409*61dfa509SRick McNeal void
sbd_do_ats_xfer(struct scsi_task * task,sbd_cmd_t * scmd,struct stmf_data_buf * dbuf,uint8_t dbuf_reusable)410*61dfa509SRick McNeal sbd_do_ats_xfer(struct scsi_task *task, sbd_cmd_t *scmd,
411*61dfa509SRick McNeal     struct stmf_data_buf *dbuf, uint8_t dbuf_reusable)
412*61dfa509SRick McNeal {
413*61dfa509SRick McNeal 	uint32_t len;
414*61dfa509SRick McNeal 
415*61dfa509SRick McNeal 	if (ATOMIC32_GET(scmd->len) == 0) {
416*61dfa509SRick McNeal 		if (dbuf != NULL) {
417*61dfa509SRick McNeal 			stmf_free_dbuf(task, dbuf);
418*61dfa509SRick McNeal 		}
419*61dfa509SRick McNeal 		return;
420*61dfa509SRick McNeal 	}
421*61dfa509SRick McNeal 
422*61dfa509SRick McNeal 	if ((dbuf != NULL) &&
423*61dfa509SRick McNeal 	    ((dbuf->db_flags & DB_DONT_REUSE) || (dbuf_reusable == 0))) {
424*61dfa509SRick McNeal 		/* free current dbuf and allocate a new one */
425*61dfa509SRick McNeal 		stmf_free_dbuf(task, dbuf);
426*61dfa509SRick McNeal 		dbuf = NULL;
427*61dfa509SRick McNeal 	}
428*61dfa509SRick McNeal 	if (dbuf == NULL) {
429*61dfa509SRick McNeal 		uint32_t maxsize, minsize, old_minsize;
430*61dfa509SRick McNeal 
431*61dfa509SRick McNeal 		maxsize = (ATOMIC32_GET(scmd->len) > (128*1024)) ? 128*1024 :
432*61dfa509SRick McNeal 		    ATOMIC32_GET(scmd->len);
433*61dfa509SRick McNeal 		minsize = maxsize >> 2;
434*61dfa509SRick McNeal 		do {
435*61dfa509SRick McNeal 			old_minsize = minsize;
436*61dfa509SRick McNeal 			dbuf = stmf_alloc_dbuf(task, maxsize, &minsize, 0);
437*61dfa509SRick McNeal 		} while ((dbuf == NULL) && (old_minsize > minsize) &&
438*61dfa509SRick McNeal 		    (minsize >= 512));
439*61dfa509SRick McNeal 		if (dbuf == NULL) {
440*61dfa509SRick McNeal 			if (ATOMIC8_GET(scmd->nbufs) == 0) {
441*61dfa509SRick McNeal 				sbd_ats_remove_by_task(task);
442*61dfa509SRick McNeal 				sbd_ats_release_resources(task);
443*61dfa509SRick McNeal 				stmf_abort(STMF_QUEUE_TASK_ABORT, task,
444*61dfa509SRick McNeal 				    STMF_ALLOC_FAILURE, NULL);
445*61dfa509SRick McNeal 			}
446*61dfa509SRick McNeal 			return;
447*61dfa509SRick McNeal 		}
448*61dfa509SRick McNeal 	}
449*61dfa509SRick McNeal 
450*61dfa509SRick McNeal 	len = ATOMIC32_GET(scmd->len) > dbuf->db_buf_size ? dbuf->db_buf_size :
451*61dfa509SRick McNeal 	    ATOMIC32_GET(scmd->len);
452*61dfa509SRick McNeal 
453*61dfa509SRick McNeal 	dbuf->db_relative_offset = scmd->current_ro;
454*61dfa509SRick McNeal 	dbuf->db_data_size = len;
455*61dfa509SRick McNeal 	dbuf->db_flags = DB_DIRECTION_FROM_RPORT;
456*61dfa509SRick McNeal 	(void) stmf_xfer_data(task, dbuf, 0);
457*61dfa509SRick McNeal 	/*
458*61dfa509SRick McNeal 	 * scmd->nbufs is the outstanding transfers
459*61dfa509SRick McNeal 	 * scmd->len is the number of bytes that are remaing for requests
460*61dfa509SRick McNeal 	 */
461*61dfa509SRick McNeal 	atomic_inc_8(&scmd->nbufs);
462*61dfa509SRick McNeal 	atomic_add_32(&scmd->len, -len);
463*61dfa509SRick McNeal 	scmd->current_ro += len;
464*61dfa509SRick McNeal }
465*61dfa509SRick McNeal 
466*61dfa509SRick McNeal void
sbd_handle_ats(scsi_task_t * task,struct stmf_data_buf * initial_dbuf)467*61dfa509SRick McNeal sbd_handle_ats(scsi_task_t *task, struct stmf_data_buf *initial_dbuf)
468*61dfa509SRick McNeal {
469*61dfa509SRick McNeal 	sbd_lu_t *sl = (sbd_lu_t *)task->task_lu->lu_provider_private;
470*61dfa509SRick McNeal 	uint64_t addr, len;
471*61dfa509SRick McNeal 	sbd_cmd_t *scmd;
472*61dfa509SRick McNeal 	stmf_data_buf_t *dbuf;
473*61dfa509SRick McNeal 	uint8_t do_immediate_data = 0;
474*61dfa509SRick McNeal 	/* int ret; */
475*61dfa509SRick McNeal 
476*61dfa509SRick McNeal 	if (HardwareAcceleratedLocking == 0) {
477*61dfa509SRick McNeal 		stmf_scsilib_send_status(task, STATUS_CHECK,
478*61dfa509SRick McNeal 		    STMF_SAA_INVALID_OPCODE);
479*61dfa509SRick McNeal 		return;
480*61dfa509SRick McNeal 	}
481*61dfa509SRick McNeal 
482*61dfa509SRick McNeal 	task->task_cmd_xfer_length = 0;
483*61dfa509SRick McNeal 	if (task->task_additional_flags &
484*61dfa509SRick McNeal 	    TASK_AF_NO_EXPECTED_XFER_LENGTH) {
485*61dfa509SRick McNeal 		task->task_expected_xfer_length = 0;
486*61dfa509SRick McNeal 	}
487*61dfa509SRick McNeal 	if (sl->sl_flags & SL_WRITE_PROTECTED) {
488*61dfa509SRick McNeal 		stmf_scsilib_send_status(task, STATUS_CHECK,
489*61dfa509SRick McNeal 		    STMF_SAA_WRITE_PROTECTED);
490*61dfa509SRick McNeal 		return;
491*61dfa509SRick McNeal 	}
492*61dfa509SRick McNeal 	addr = READ_SCSI64(&task->task_cdb[2], uint64_t);
493*61dfa509SRick McNeal 	len = (uint64_t)task->task_cdb[13];
494*61dfa509SRick McNeal 
495*61dfa509SRick McNeal 	if ((task->task_cdb[1]) || (len > SBD_ATS_MAX_NBLKS)) {
496*61dfa509SRick McNeal 		stmf_scsilib_send_status(task, STATUS_CHECK,
497*61dfa509SRick McNeal 		    STMF_SAA_INVALID_FIELD_IN_CDB);
498*61dfa509SRick McNeal 		return;
499*61dfa509SRick McNeal 	}
500*61dfa509SRick McNeal 	if (len == 0) {
501*61dfa509SRick McNeal 		stmf_scsilib_send_status(task, STATUS_GOOD, 0);
502*61dfa509SRick McNeal 		return;
503*61dfa509SRick McNeal 	}
504*61dfa509SRick McNeal 
505*61dfa509SRick McNeal 	/*
506*61dfa509SRick McNeal 	 * This can be called again. It will return the same handle again.
507*61dfa509SRick McNeal 	 */
508*61dfa509SRick McNeal 	if (sbd_ats_handling_before_io(task, sl, addr, len) != SBD_SUCCESS) {
509*61dfa509SRick McNeal 		if (stmf_task_poll_lu(task, 10) != STMF_SUCCESS) {
510*61dfa509SRick McNeal 			stmf_scsilib_send_status(task, STATUS_BUSY, 0);
511*61dfa509SRick McNeal 		}
512*61dfa509SRick McNeal 		return;
513*61dfa509SRick McNeal 	}
514*61dfa509SRick McNeal 
515*61dfa509SRick McNeal 	addr <<= sl->sl_data_blocksize_shift;
516*61dfa509SRick McNeal 	len <<= sl->sl_data_blocksize_shift;
517*61dfa509SRick McNeal 
518*61dfa509SRick McNeal 	task->task_cmd_xfer_length = len << 1;	/* actual amt of data is 2x */
519*61dfa509SRick McNeal 	if (task->task_additional_flags &
520*61dfa509SRick McNeal 	    TASK_AF_NO_EXPECTED_XFER_LENGTH) {
521*61dfa509SRick McNeal 		task->task_expected_xfer_length = task->task_cmd_xfer_length;
522*61dfa509SRick McNeal 	}
523*61dfa509SRick McNeal 	if ((addr + len) > sl->sl_lu_size) {
524*61dfa509SRick McNeal 		sbd_ats_remove_by_task(task);
525*61dfa509SRick McNeal 		stmf_scsilib_send_status(task, STATUS_CHECK,
526*61dfa509SRick McNeal 		    STMF_SAA_LBA_OUT_OF_RANGE);
527*61dfa509SRick McNeal 		return;
528*61dfa509SRick McNeal 	}
529*61dfa509SRick McNeal 
530*61dfa509SRick McNeal 	len <<= 1;
531*61dfa509SRick McNeal 
532*61dfa509SRick McNeal 	if (len != task->task_expected_xfer_length) {
533*61dfa509SRick McNeal 		sbd_ats_remove_by_task(task);
534*61dfa509SRick McNeal 		stmf_scsilib_send_status(task, STATUS_CHECK,
535*61dfa509SRick McNeal 		    STMF_SAA_INVALID_FIELD_IN_CDB);
536*61dfa509SRick McNeal 		return;
537*61dfa509SRick McNeal 	}
538*61dfa509SRick McNeal 
539*61dfa509SRick McNeal 	if ((initial_dbuf != NULL) && (task->task_flags & TF_INITIAL_BURST)) {
540*61dfa509SRick McNeal 		if (initial_dbuf->db_data_size > len) {
541*61dfa509SRick McNeal 			if (initial_dbuf->db_data_size >
542*61dfa509SRick McNeal 			    task->task_expected_xfer_length) {
543*61dfa509SRick McNeal 				/* protocol error */
544*61dfa509SRick McNeal 				sbd_ats_remove_by_task(task);
545*61dfa509SRick McNeal 				stmf_abort(STMF_QUEUE_TASK_ABORT, task,
546*61dfa509SRick McNeal 				    STMF_INVALID_ARG, NULL);
547*61dfa509SRick McNeal 				return;
548*61dfa509SRick McNeal 			}
549*61dfa509SRick McNeal 			ASSERT(len <= 0xFFFFFFFFull);
550*61dfa509SRick McNeal 			initial_dbuf->db_data_size = (uint32_t)len;
551*61dfa509SRick McNeal 		}
552*61dfa509SRick McNeal 		do_immediate_data = 1;
553*61dfa509SRick McNeal 	}
554*61dfa509SRick McNeal 	dbuf = initial_dbuf;
555*61dfa509SRick McNeal 
556*61dfa509SRick McNeal 	if (task->task_lu_private) {
557*61dfa509SRick McNeal 		scmd = (sbd_cmd_t *)task->task_lu_private;
558*61dfa509SRick McNeal 	} else {
559*61dfa509SRick McNeal 		scmd = (sbd_cmd_t *)kmem_alloc(sizeof (sbd_cmd_t), KM_SLEEP);
560*61dfa509SRick McNeal 		task->task_lu_private = scmd;
561*61dfa509SRick McNeal 	}
562*61dfa509SRick McNeal 
563*61dfa509SRick McNeal 	/* We dont set the ATS_RELATED flag here */
564*61dfa509SRick McNeal 	scmd->flags = SBD_SCSI_CMD_ACTIVE | SBD_SCSI_CMD_TRANS_DATA;
565*61dfa509SRick McNeal 	scmd->cmd_type = SBD_CMD_SCSI_WRITE;
566*61dfa509SRick McNeal 	scmd->nbufs = 0;
567*61dfa509SRick McNeal 	ASSERT(len <= 0xFFFFFFFFull);
568*61dfa509SRick McNeal 	scmd->len = (uint32_t)len;
569*61dfa509SRick McNeal 	scmd->trans_data_len = (uint32_t)len;
570*61dfa509SRick McNeal 	scmd->trans_data = kmem_alloc((size_t)len, KM_SLEEP);
571*61dfa509SRick McNeal 	scmd->current_ro = 0;
572*61dfa509SRick McNeal 
573*61dfa509SRick McNeal 	if (do_immediate_data) {
574*61dfa509SRick McNeal 		/*
575*61dfa509SRick McNeal 		 * Account for data passed in this write command
576*61dfa509SRick McNeal 		 */
577*61dfa509SRick McNeal 		(void) stmf_xfer_data(task, dbuf, STMF_IOF_STATS_ONLY);
578*61dfa509SRick McNeal 		atomic_add_32(&scmd->len, -dbuf->db_data_size);
579*61dfa509SRick McNeal 		scmd->current_ro += dbuf->db_data_size;
580*61dfa509SRick McNeal 		dbuf->db_xfer_status = STMF_SUCCESS;
581*61dfa509SRick McNeal 		sbd_handle_ats_xfer_completion(task, scmd, dbuf, 0);
582*61dfa509SRick McNeal 	} else {
583*61dfa509SRick McNeal 		sbd_do_ats_xfer(task, scmd, dbuf, 0);
584*61dfa509SRick McNeal 	}
585*61dfa509SRick McNeal }
586*61dfa509SRick McNeal 
587*61dfa509SRick McNeal /*
588*61dfa509SRick McNeal  * SCSI Copy Manager
589*61dfa509SRick McNeal  *
590*61dfa509SRick McNeal  * SCSI copy manager is the state machine which implements
591*61dfa509SRick McNeal  * SCSI extended copy functionality (SPC). There is one
592*61dfa509SRick McNeal  * cpmgr instance per extended copy command.
593*61dfa509SRick McNeal  *
594*61dfa509SRick McNeal  * Exported block-copy functions:
595*61dfa509SRick McNeal  *   cpmgr_create()  - Creates the state machine.
596*61dfa509SRick McNeal  *   cpmgr_destroy() - Cleans up a completed cpmgr.
597*61dfa509SRick McNeal  *   cpmgr_run()     - Performs time bound copy.
598*61dfa509SRick McNeal  *   cpmgr_abort()   - Aborts a cpmgr(if not already completed).
599*61dfa509SRick McNeal  *   cpmgr_done()    - Tests if the copy is done.
600*61dfa509SRick McNeal  */
601*61dfa509SRick McNeal 
602*61dfa509SRick McNeal static void cpmgr_completion_cleanup(cpmgr_t *cm);
603*61dfa509SRick McNeal int sbd_check_reservation_conflict(sbd_lu_t *sl, scsi_task_t *task);
604*61dfa509SRick McNeal 
605*61dfa509SRick McNeal static uint8_t sbd_recv_copy_results_op_params[] = {
606*61dfa509SRick McNeal     0, 0, 0, 42, 1, 0, 0, 0,
607*61dfa509SRick McNeal     0, 2, 0, 1, 0, 0, 0xFF, 0xFF, 0, 0, 0, 0,
608*61dfa509SRick McNeal     0, 0, 0, 0, 0, 0, 0, 0,
609*61dfa509SRick McNeal     0, 0, 0, 0, 0, 0,
610*61dfa509SRick McNeal     0xFF, 0xFF, 0, 9, 0, 0, 0, 0, 0,
611*61dfa509SRick McNeal     2, 2, 0xE4
612*61dfa509SRick McNeal };
613*61dfa509SRick McNeal 
614*61dfa509SRick McNeal cpmgr_handle_t
cpmgr_create(scsi_task_t * task,uint8_t * params)615*61dfa509SRick McNeal cpmgr_create(scsi_task_t *task, uint8_t *params)
616*61dfa509SRick McNeal {
617*61dfa509SRick McNeal 	cpmgr_t *cm = NULL;
618*61dfa509SRick McNeal 	uint8_t *p;
619*61dfa509SRick McNeal 	uint32_t plist_len;
620*61dfa509SRick McNeal 	uint32_t dbl;
621*61dfa509SRick McNeal 	int i;
622*61dfa509SRick McNeal 	uint16_t tdlen;
623*61dfa509SRick McNeal 	uint16_t n;
624*61dfa509SRick McNeal 
625*61dfa509SRick McNeal 	cm = kmem_zalloc(sizeof (*cm), KM_NOSLEEP);
626*61dfa509SRick McNeal 	if (cm == NULL)
627*61dfa509SRick McNeal 		return (CPMGR_INVALID_HANDLE);
628*61dfa509SRick McNeal 
629*61dfa509SRick McNeal 	cm->cm_task = task;
630*61dfa509SRick McNeal 	p = task->task_cdb;
631*61dfa509SRick McNeal 	plist_len = READ_SCSI32(&p[10], uint32_t);
632*61dfa509SRick McNeal 
633*61dfa509SRick McNeal 	/*
634*61dfa509SRick McNeal 	 * In case of error. Otherwise we will change this to CM_COPYING.
635*61dfa509SRick McNeal 	 */
636*61dfa509SRick McNeal 	cm->cm_state = CM_COMPLETE;
637*61dfa509SRick McNeal 
638*61dfa509SRick McNeal 	if (plist_len == 0) {
639*61dfa509SRick McNeal 		cm->cm_status = 0;
640*61dfa509SRick McNeal 		goto cpmgr_create_done;
641*61dfa509SRick McNeal 	}
642*61dfa509SRick McNeal 
643*61dfa509SRick McNeal 	if (plist_len < CPMGR_PARAM_HDR_LEN) {
644*61dfa509SRick McNeal 		cm->cm_status = CPMGR_PARAM_LIST_LEN_ERROR;
645*61dfa509SRick McNeal 		goto cpmgr_create_done;
646*61dfa509SRick McNeal 	} else if ((params[0] != 0) || ((params[1] & 0x18) != 0x18)) {
647*61dfa509SRick McNeal 		/*
648*61dfa509SRick McNeal 		 * Current implementation does not allow the use
649*61dfa509SRick McNeal 		 * of list ID field.
650*61dfa509SRick McNeal 		 */
651*61dfa509SRick McNeal 		cm->cm_status = CPMGR_INVALID_FIELD_IN_PARAM_LIST;
652*61dfa509SRick McNeal 		goto cpmgr_create_done;
653*61dfa509SRick McNeal 	}
654*61dfa509SRick McNeal 	/* No inline data either */
655*61dfa509SRick McNeal 	if (*((uint32_t *)(&params[12])) != 0) {
656*61dfa509SRick McNeal 		cm->cm_status = CPMGR_INVALID_FIELD_IN_PARAM_LIST;
657*61dfa509SRick McNeal 		goto cpmgr_create_done;
658*61dfa509SRick McNeal 	}
659*61dfa509SRick McNeal 
660*61dfa509SRick McNeal 	tdlen = READ_SCSI16(&params[2], uint16_t);
661*61dfa509SRick McNeal 	if ((tdlen == 0) || (tdlen % CPMGR_TARGET_DESCRIPTOR_SIZE) ||
662*61dfa509SRick McNeal 	    (plist_len < (CPMGR_PARAM_HDR_LEN + tdlen))) {
663*61dfa509SRick McNeal 		cm->cm_status = CPMGR_PARAM_LIST_LEN_ERROR;
664*61dfa509SRick McNeal 		goto cpmgr_create_done;
665*61dfa509SRick McNeal 	}
666*61dfa509SRick McNeal 	cm->cm_td_count = tdlen / CPMGR_TARGET_DESCRIPTOR_SIZE;
667*61dfa509SRick McNeal 	if (cm->cm_td_count > CPMGR_MAX_TARGET_DESCRIPTORS) {
668*61dfa509SRick McNeal 		cm->cm_status = CPMGR_TOO_MANY_TARGET_DESCRIPTORS;
669*61dfa509SRick McNeal 		goto cpmgr_create_done;
670*61dfa509SRick McNeal 	}
671*61dfa509SRick McNeal 	if (plist_len != (CPMGR_PARAM_HDR_LEN + tdlen +
672*61dfa509SRick McNeal 	    CPMGR_B2B_SEGMENT_DESCRIPTOR_SIZE)) {
673*61dfa509SRick McNeal 		cm->cm_status = CPMGR_PARAM_LIST_LEN_ERROR;
674*61dfa509SRick McNeal 		goto cpmgr_create_done;
675*61dfa509SRick McNeal 	}
676*61dfa509SRick McNeal 	for (i = 0; i < cm->cm_td_count; i++) {
677*61dfa509SRick McNeal 		p = params + CPMGR_PARAM_HDR_LEN;
678*61dfa509SRick McNeal 		p += i * CPMGR_TARGET_DESCRIPTOR_SIZE;
679*61dfa509SRick McNeal 		if ((p[0] != CPMGR_IDENT_TARGET_DESCRIPTOR) ||
680*61dfa509SRick McNeal 		    ((p[5] & 0x30) != 0) || (p[7] != 16)) {
681*61dfa509SRick McNeal 			cm->cm_status = CPMGR_UNSUPPORTED_TARGET_DESCRIPTOR;
682*61dfa509SRick McNeal 			goto cpmgr_create_done;
683*61dfa509SRick McNeal 		}
684*61dfa509SRick McNeal 		/*
685*61dfa509SRick McNeal 		 * stmf should be able to find this LU and lock it. Also
686*61dfa509SRick McNeal 		 * make sure that is indeed a sbd lu.
687*61dfa509SRick McNeal 		 */
688*61dfa509SRick McNeal 		if (((cm->cm_tds[i].td_lu =
689*61dfa509SRick McNeal 		    stmf_check_and_hold_lu(task, &p[8])) == NULL) ||
690*61dfa509SRick McNeal 		    (!sbd_is_valid_lu(cm->cm_tds[i].td_lu))) {
691*61dfa509SRick McNeal 			cm->cm_status = CPMGR_COPY_TARGET_NOT_REACHABLE;
692*61dfa509SRick McNeal 			goto cpmgr_create_done;
693*61dfa509SRick McNeal 		}
694*61dfa509SRick McNeal 		dbl = p[29];
695*61dfa509SRick McNeal 		dbl <<= 8;
696*61dfa509SRick McNeal 		dbl |= p[30];
697*61dfa509SRick McNeal 		dbl <<= 8;
698*61dfa509SRick McNeal 		dbl |= p[31];
699*61dfa509SRick McNeal 		cm->cm_tds[i].td_disk_block_len = dbl;
700*61dfa509SRick McNeal 		cm->cm_tds[i].td_lbasize_shift =
701*61dfa509SRick McNeal 		    sbd_get_lbasize_shift(cm->cm_tds[i].td_lu);
702*61dfa509SRick McNeal 	}
703*61dfa509SRick McNeal 	/* p now points to segment descriptor */
704*61dfa509SRick McNeal 	p += CPMGR_TARGET_DESCRIPTOR_SIZE;
705*61dfa509SRick McNeal 
706*61dfa509SRick McNeal 	if (p[0] != CPMGR_B2B_SEGMENT_DESCRIPTOR) {
707*61dfa509SRick McNeal 		cm->cm_status = CPMGR_UNSUPPORTED_SEGMENT_DESCRIPTOR;
708*61dfa509SRick McNeal 		goto cpmgr_create_done;
709*61dfa509SRick McNeal 	}
710*61dfa509SRick McNeal 	n = READ_SCSI16(&p[2], uint16_t);
711*61dfa509SRick McNeal 	if (n != (CPMGR_B2B_SEGMENT_DESCRIPTOR_SIZE - 4)) {
712*61dfa509SRick McNeal 		cm->cm_status = CPMGR_INVALID_FIELD_IN_PARAM_LIST;
713*61dfa509SRick McNeal 		goto cpmgr_create_done;
714*61dfa509SRick McNeal 	}
715*61dfa509SRick McNeal 
716*61dfa509SRick McNeal 	n = READ_SCSI16(&p[4], uint16_t);
717*61dfa509SRick McNeal 	if (n >= cm->cm_td_count) {
718*61dfa509SRick McNeal 		cm->cm_status = CPMGR_INVALID_FIELD_IN_PARAM_LIST;
719*61dfa509SRick McNeal 		goto cpmgr_create_done;
720*61dfa509SRick McNeal 	}
721*61dfa509SRick McNeal 	cm->cm_src_td_ndx = n;
722*61dfa509SRick McNeal 
723*61dfa509SRick McNeal 	n = READ_SCSI16(&p[6], uint16_t);
724*61dfa509SRick McNeal 	if (n >= cm->cm_td_count) {
725*61dfa509SRick McNeal 		cm->cm_status = CPMGR_INVALID_FIELD_IN_PARAM_LIST;
726*61dfa509SRick McNeal 		goto cpmgr_create_done;
727*61dfa509SRick McNeal 	}
728*61dfa509SRick McNeal 	cm->cm_dst_td_ndx = n;
729*61dfa509SRick McNeal 
730*61dfa509SRick McNeal 	cm->cm_copy_size = READ_SCSI16(&p[10], uint64_t);
731*61dfa509SRick McNeal 	cm->cm_copy_size *= (uint64_t)(cm->cm_tds[(p[1] & 2) ?
732*61dfa509SRick McNeal 	    cm->cm_dst_td_ndx : cm->cm_src_td_ndx].td_disk_block_len);
733*61dfa509SRick McNeal 	cm->cm_src_offset = (READ_SCSI64(&p[12], uint64_t)) <<
734*61dfa509SRick McNeal 	    cm->cm_tds[cm->cm_src_td_ndx].td_lbasize_shift;
735*61dfa509SRick McNeal 	cm->cm_dst_offset = (READ_SCSI64(&p[20], uint64_t)) <<
736*61dfa509SRick McNeal 	    cm->cm_tds[cm->cm_dst_td_ndx].td_lbasize_shift;
737*61dfa509SRick McNeal 
738*61dfa509SRick McNeal 	/* Allocate the xfer buffer. */
739*61dfa509SRick McNeal 	cm->cm_xfer_buf = kmem_alloc(CPMGR_XFER_BUF_SIZE, KM_NOSLEEP);
740*61dfa509SRick McNeal 	if (cm->cm_xfer_buf == NULL) {
741*61dfa509SRick McNeal 		cm->cm_status = CPMGR_INSUFFICIENT_RESOURCES;
742*61dfa509SRick McNeal 		goto cpmgr_create_done;
743*61dfa509SRick McNeal 	}
744*61dfa509SRick McNeal 
745*61dfa509SRick McNeal 	/*
746*61dfa509SRick McNeal 	 * No need to check block limits. cpmgr_run() will
747*61dfa509SRick McNeal 	 * take care of that.
748*61dfa509SRick McNeal 	 */
749*61dfa509SRick McNeal 
750*61dfa509SRick McNeal 	/* All checks passed */
751*61dfa509SRick McNeal 	cm->cm_state = CM_COPYING;
752*61dfa509SRick McNeal 
753*61dfa509SRick McNeal cpmgr_create_done:
754*61dfa509SRick McNeal 	if (cm->cm_state == CM_COMPLETE) {
755*61dfa509SRick McNeal 		cpmgr_completion_cleanup(cm);
756*61dfa509SRick McNeal 	}
757*61dfa509SRick McNeal 	return (cm);
758*61dfa509SRick McNeal }
759*61dfa509SRick McNeal 
760*61dfa509SRick McNeal void
cpmgr_destroy(cpmgr_handle_t h)761*61dfa509SRick McNeal cpmgr_destroy(cpmgr_handle_t h)
762*61dfa509SRick McNeal {
763*61dfa509SRick McNeal 	cpmgr_t *cm = (cpmgr_t *)h;
764*61dfa509SRick McNeal 
765*61dfa509SRick McNeal 	ASSERT(cm->cm_state == CM_COMPLETE);
766*61dfa509SRick McNeal 	kmem_free(cm, sizeof (*cm));
767*61dfa509SRick McNeal }
768*61dfa509SRick McNeal 
769*61dfa509SRick McNeal static void
cpmgr_completion_cleanup(cpmgr_t * cm)770*61dfa509SRick McNeal cpmgr_completion_cleanup(cpmgr_t *cm)
771*61dfa509SRick McNeal {
772*61dfa509SRick McNeal 	int i;
773*61dfa509SRick McNeal 
774*61dfa509SRick McNeal 	for (i = 0; i < cm->cm_td_count; i++) {
775*61dfa509SRick McNeal 		if (cm->cm_tds[i].td_lu) {
776*61dfa509SRick McNeal 			stmf_release_lu(cm->cm_tds[i].td_lu);
777*61dfa509SRick McNeal 			cm->cm_tds[i].td_lu = NULL;
778*61dfa509SRick McNeal 		}
779*61dfa509SRick McNeal 	}
780*61dfa509SRick McNeal 	if (cm->cm_xfer_buf) {
781*61dfa509SRick McNeal 		kmem_free(cm->cm_xfer_buf, CPMGR_XFER_BUF_SIZE);
782*61dfa509SRick McNeal 		cm->cm_xfer_buf = NULL;
783*61dfa509SRick McNeal 	}
784*61dfa509SRick McNeal }
785*61dfa509SRick McNeal 
786*61dfa509SRick McNeal void
cpmgr_run(cpmgr_t * cm,clock_t preemption_point)787*61dfa509SRick McNeal cpmgr_run(cpmgr_t *cm, clock_t preemption_point)
788*61dfa509SRick McNeal {
789*61dfa509SRick McNeal 	stmf_lu_t *lu;
790*61dfa509SRick McNeal 	sbd_lu_t *src_slu, *dst_slu;
791*61dfa509SRick McNeal 	uint64_t xfer_size, start, end;
792*61dfa509SRick McNeal 	sbd_status_t ret;
793*61dfa509SRick McNeal 
794*61dfa509SRick McNeal 	/*
795*61dfa509SRick McNeal 	 * XXX: Handle reservations and read-only LU here.
796*61dfa509SRick McNeal 	 */
797*61dfa509SRick McNeal 	ASSERT(cm->cm_state == CM_COPYING);
798*61dfa509SRick McNeal 	lu = cm->cm_tds[cm->cm_src_td_ndx].td_lu;
799*61dfa509SRick McNeal 	src_slu = (sbd_lu_t *)lu->lu_provider_private;
800*61dfa509SRick McNeal 	if (sbd_check_reservation_conflict(src_slu, cm->cm_task)) {
801*61dfa509SRick McNeal 		cpmgr_abort(cm, CPMGR_RESERVATION_CONFLICT);
802*61dfa509SRick McNeal 		return;
803*61dfa509SRick McNeal 	}
804*61dfa509SRick McNeal 
805*61dfa509SRick McNeal 	lu = cm->cm_tds[cm->cm_dst_td_ndx].td_lu;
806*61dfa509SRick McNeal 	dst_slu = (sbd_lu_t *)lu->lu_provider_private;
807*61dfa509SRick McNeal 	if (sbd_check_reservation_conflict(dst_slu, cm->cm_task)) {
808*61dfa509SRick McNeal 		cpmgr_abort(cm, CPMGR_RESERVATION_CONFLICT);
809*61dfa509SRick McNeal 		return;
810*61dfa509SRick McNeal 	}
811*61dfa509SRick McNeal 	if (dst_slu->sl_flags & SL_WRITE_PROTECTED) {
812*61dfa509SRick McNeal 		cpmgr_abort(cm, STMF_SAA_WRITE_PROTECTED);
813*61dfa509SRick McNeal 		return;
814*61dfa509SRick McNeal 	}
815*61dfa509SRick McNeal 
816*61dfa509SRick McNeal 	while (cm->cm_size_done < cm->cm_copy_size) {
817*61dfa509SRick McNeal 		xfer_size = ((cm->cm_copy_size - cm->cm_size_done) >
818*61dfa509SRick McNeal 		    CPMGR_XFER_BUF_SIZE) ? CPMGR_XFER_BUF_SIZE :
819*61dfa509SRick McNeal 		    (cm->cm_copy_size - cm->cm_size_done);
820*61dfa509SRick McNeal 		start = cm->cm_src_offset + cm->cm_size_done;
821*61dfa509SRick McNeal 		ret = sbd_data_read(src_slu, cm->cm_task, start, xfer_size,
822*61dfa509SRick McNeal 		    cm->cm_xfer_buf);
823*61dfa509SRick McNeal 		if (ret != SBD_SUCCESS) {
824*61dfa509SRick McNeal 			if (ret == SBD_IO_PAST_EOF) {
825*61dfa509SRick McNeal 				cpmgr_abort(cm, CPMGR_LBA_OUT_OF_RANGE);
826*61dfa509SRick McNeal 			} else {
827*61dfa509SRick McNeal 				cpmgr_abort(cm,
828*61dfa509SRick McNeal 				    CPMGR_THIRD_PARTY_DEVICE_FAILURE);
829*61dfa509SRick McNeal 			}
830*61dfa509SRick McNeal 			break;
831*61dfa509SRick McNeal 		}
832*61dfa509SRick McNeal 		end = cm->cm_dst_offset + cm->cm_size_done;
833*61dfa509SRick McNeal 		ret = sbd_data_write(dst_slu, cm->cm_task, end, xfer_size,
834*61dfa509SRick McNeal 		    cm->cm_xfer_buf);
835*61dfa509SRick McNeal 		if (ret != SBD_SUCCESS) {
836*61dfa509SRick McNeal 			if (ret == SBD_IO_PAST_EOF) {
837*61dfa509SRick McNeal 				cpmgr_abort(cm, CPMGR_LBA_OUT_OF_RANGE);
838*61dfa509SRick McNeal 			} else {
839*61dfa509SRick McNeal 				cpmgr_abort(cm,
840*61dfa509SRick McNeal 				    CPMGR_THIRD_PARTY_DEVICE_FAILURE);
841*61dfa509SRick McNeal 			}
842*61dfa509SRick McNeal 			break;
843*61dfa509SRick McNeal 		}
844*61dfa509SRick McNeal 		cm->cm_size_done += xfer_size;
845*61dfa509SRick McNeal 		if (ddi_get_lbolt() >= preemption_point)
846*61dfa509SRick McNeal 			break;
847*61dfa509SRick McNeal 	}
848*61dfa509SRick McNeal 	if (cm->cm_size_done == cm->cm_copy_size) {
849*61dfa509SRick McNeal 		cm->cm_state = CM_COMPLETE;
850*61dfa509SRick McNeal 		cm->cm_status = 0;
851*61dfa509SRick McNeal 		cpmgr_completion_cleanup(cm);
852*61dfa509SRick McNeal 	}
853*61dfa509SRick McNeal }
854*61dfa509SRick McNeal 
855*61dfa509SRick McNeal void
cpmgr_abort(cpmgr_t * cm,uint32_t s)856*61dfa509SRick McNeal cpmgr_abort(cpmgr_t *cm, uint32_t s)
857*61dfa509SRick McNeal {
858*61dfa509SRick McNeal 	if (cm->cm_state == CM_COPYING) {
859*61dfa509SRick McNeal 		cm->cm_state = CM_COMPLETE;
860*61dfa509SRick McNeal 		cm->cm_status = s;
861*61dfa509SRick McNeal 		cpmgr_completion_cleanup(cm);
862*61dfa509SRick McNeal 	}
863*61dfa509SRick McNeal }
864*61dfa509SRick McNeal 
865*61dfa509SRick McNeal void
sbd_handle_xcopy(scsi_task_t * task,stmf_data_buf_t * dbuf)866*61dfa509SRick McNeal sbd_handle_xcopy(scsi_task_t *task, stmf_data_buf_t *dbuf)
867*61dfa509SRick McNeal {
868*61dfa509SRick McNeal 	uint32_t cmd_xfer_len;
869*61dfa509SRick McNeal 
870*61dfa509SRick McNeal 	if (HardwareAcceleratedMove == 0) {
871*61dfa509SRick McNeal 		stmf_scsilib_send_status(task, STATUS_CHECK,
872*61dfa509SRick McNeal 		    STMF_SAA_INVALID_OPCODE);
873*61dfa509SRick McNeal 		return;
874*61dfa509SRick McNeal 	}
875*61dfa509SRick McNeal 
876*61dfa509SRick McNeal 	cmd_xfer_len = READ_SCSI32(&task->task_cdb[10], uint32_t);
877*61dfa509SRick McNeal 
878*61dfa509SRick McNeal 	if (cmd_xfer_len == 0) {
879*61dfa509SRick McNeal 		task->task_cmd_xfer_length = 0;
880*61dfa509SRick McNeal 		if (task->task_additional_flags &
881*61dfa509SRick McNeal 		    TASK_AF_NO_EXPECTED_XFER_LENGTH) {
882*61dfa509SRick McNeal 			task->task_expected_xfer_length = 0;
883*61dfa509SRick McNeal 		}
884*61dfa509SRick McNeal 		stmf_scsilib_send_status(task, STATUS_GOOD, 0);
885*61dfa509SRick McNeal 		return;
886*61dfa509SRick McNeal 	}
887*61dfa509SRick McNeal 
888*61dfa509SRick McNeal 	sbd_handle_short_write_transfers(task, dbuf, cmd_xfer_len);
889*61dfa509SRick McNeal }
890*61dfa509SRick McNeal 
891*61dfa509SRick McNeal void
sbd_handle_xcopy_xfer(scsi_task_t * task,uint8_t * buf)892*61dfa509SRick McNeal sbd_handle_xcopy_xfer(scsi_task_t *task, uint8_t *buf)
893*61dfa509SRick McNeal {
894*61dfa509SRick McNeal 	cpmgr_handle_t h;
895*61dfa509SRick McNeal 	uint32_t s;
896*61dfa509SRick McNeal 	clock_t tic, end;
897*61dfa509SRick McNeal 
898*61dfa509SRick McNeal 	/*
899*61dfa509SRick McNeal 	 * No need to pass buf size. Its taken from cdb.
900*61dfa509SRick McNeal 	 */
901*61dfa509SRick McNeal 	h = cpmgr_create(task, buf);
902*61dfa509SRick McNeal 	if (h == CPMGR_INVALID_HANDLE) {
903*61dfa509SRick McNeal 		stmf_scsilib_send_status(task, STATUS_CHECK,
904*61dfa509SRick McNeal 		    CPMGR_INSUFFICIENT_RESOURCES);
905*61dfa509SRick McNeal 		return;
906*61dfa509SRick McNeal 	}
907*61dfa509SRick McNeal 	tic = drv_usectohz(1000000);
908*61dfa509SRick McNeal 	end = ddi_get_lbolt() + (CPMGR_DEFAULT_TIMEOUT * tic);
909*61dfa509SRick McNeal 	while (!cpmgr_done(h)) {
910*61dfa509SRick McNeal 		if (stmf_is_task_being_aborted(task) || (ddi_get_lbolt() > end))
911*61dfa509SRick McNeal 			cpmgr_abort(h, CPMGR_THIRD_PARTY_DEVICE_FAILURE);
912*61dfa509SRick McNeal 		else
913*61dfa509SRick McNeal 			cpmgr_run(h, ddi_get_lbolt() + tic);
914*61dfa509SRick McNeal 	}
915*61dfa509SRick McNeal 	s = cpmgr_status(h);
916*61dfa509SRick McNeal 	if (s) {
917*61dfa509SRick McNeal 		if (s == CPMGR_RESERVATION_CONFLICT) {
918*61dfa509SRick McNeal 			stmf_scsilib_send_status(task,
919*61dfa509SRick McNeal 			    STATUS_RESERVATION_CONFLICT, 0);
920*61dfa509SRick McNeal 		} else {
921*61dfa509SRick McNeal 			stmf_scsilib_send_status(task, STATUS_CHECK, s);
922*61dfa509SRick McNeal 		}
923*61dfa509SRick McNeal 	} else {
924*61dfa509SRick McNeal 		stmf_scsilib_send_status(task, STATUS_GOOD, 0);
925*61dfa509SRick McNeal 	}
926*61dfa509SRick McNeal 	cpmgr_destroy(h);
927*61dfa509SRick McNeal }
928*61dfa509SRick McNeal 
929*61dfa509SRick McNeal void
sbd_handle_recv_copy_results(struct scsi_task * task,struct stmf_data_buf * initial_dbuf)930*61dfa509SRick McNeal sbd_handle_recv_copy_results(struct scsi_task *task,
931*61dfa509SRick McNeal     struct stmf_data_buf *initial_dbuf)
932*61dfa509SRick McNeal {
933*61dfa509SRick McNeal 	uint32_t cdb_len;
934*61dfa509SRick McNeal 
935*61dfa509SRick McNeal 	cdb_len = READ_SCSI32(&task->task_cdb[10], uint32_t);
936*61dfa509SRick McNeal 	if ((task->task_cdb[1] & 0x1F) != 3) {
937*61dfa509SRick McNeal 		stmf_scsilib_send_status(task, STATUS_CHECK,
938*61dfa509SRick McNeal 		    STMF_SAA_INVALID_FIELD_IN_CDB);
939*61dfa509SRick McNeal 		return;
940*61dfa509SRick McNeal 	}
941*61dfa509SRick McNeal 	sbd_handle_short_read_transfers(task, initial_dbuf,
942*61dfa509SRick McNeal 	    sbd_recv_copy_results_op_params, cdb_len,
943*61dfa509SRick McNeal 	    sizeof (sbd_recv_copy_results_op_params));
944*61dfa509SRick McNeal }
945