xref: /freebsd/sys/dev/ocs_fc/ocs_scsi.c (revision fc620f9782d1fda5a243fd51a93a0fb46bc80f64)
1ef270ab1SKenneth D. Merry /*-
2ef270ab1SKenneth D. Merry  * Copyright (c) 2017 Broadcom. All rights reserved.
3ef270ab1SKenneth D. Merry  * The term "Broadcom" refers to Broadcom Limited and/or its subsidiaries.
4ef270ab1SKenneth D. Merry  *
5ef270ab1SKenneth D. Merry  * Redistribution and use in source and binary forms, with or without
6ef270ab1SKenneth D. Merry  * modification, are permitted provided that the following conditions are met:
7ef270ab1SKenneth D. Merry  *
8ef270ab1SKenneth D. Merry  * 1. Redistributions of source code must retain the above copyright notice,
9ef270ab1SKenneth D. Merry  *    this list of conditions and the following disclaimer.
10ef270ab1SKenneth D. Merry  *
11ef270ab1SKenneth D. Merry  * 2. Redistributions in binary form must reproduce the above copyright notice,
12ef270ab1SKenneth D. Merry  *    this list of conditions and the following disclaimer in the documentation
13ef270ab1SKenneth D. Merry  *    and/or other materials provided with the distribution.
14ef270ab1SKenneth D. Merry  *
15ef270ab1SKenneth D. Merry  * 3. Neither the name of the copyright holder nor the names of its contributors
16ef270ab1SKenneth D. Merry  *    may be used to endorse or promote products derived from this software
17ef270ab1SKenneth D. Merry  *    without specific prior written permission.
18ef270ab1SKenneth D. Merry  *
19ef270ab1SKenneth D. Merry  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20ef270ab1SKenneth D. Merry  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21ef270ab1SKenneth D. Merry  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22ef270ab1SKenneth D. Merry  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23ef270ab1SKenneth D. Merry  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24ef270ab1SKenneth D. Merry  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25ef270ab1SKenneth D. Merry  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26ef270ab1SKenneth D. Merry  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27ef270ab1SKenneth D. Merry  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28ef270ab1SKenneth D. Merry  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29ef270ab1SKenneth D. Merry  * POSSIBILITY OF SUCH DAMAGE.
30ef270ab1SKenneth D. Merry  *
31ef270ab1SKenneth D. Merry  * $FreeBSD$
32ef270ab1SKenneth D. Merry  */
33ef270ab1SKenneth D. Merry 
34ef270ab1SKenneth D. Merry /**
35ef270ab1SKenneth D. Merry  * @file
36ef270ab1SKenneth D. Merry  * OCS Linux SCSI API base driver implementation.
37ef270ab1SKenneth D. Merry  */
38ef270ab1SKenneth D. Merry 
39ef270ab1SKenneth D. Merry /**
40ef270ab1SKenneth D. Merry  * @defgroup scsi_api_base SCSI Base Target/Initiator
41ef270ab1SKenneth D. Merry  */
42ef270ab1SKenneth D. Merry 
43ef270ab1SKenneth D. Merry #include "ocs.h"
44ef270ab1SKenneth D. Merry #include "ocs_els.h"
45ef270ab1SKenneth D. Merry #include "ocs_scsi.h"
46ef270ab1SKenneth D. Merry #if defined(OCS_ENABLE_VPD_SUPPORT)
47ef270ab1SKenneth D. Merry #include "ocs_vpd.h"
48ef270ab1SKenneth D. Merry #endif
49ef270ab1SKenneth D. Merry #include "ocs_utils.h"
50ef270ab1SKenneth D. Merry #include "ocs_device.h"
51ef270ab1SKenneth D. Merry 
52ef270ab1SKenneth D. Merry #define SCSI_IOFMT "[%04x][i:%0*x t:%0*x h:%04x]"
53ef270ab1SKenneth D. Merry #define SCSI_ITT_SIZE(ocs)	((ocs->ocs_xport == OCS_XPORT_FC) ? 4 : 8)
54ef270ab1SKenneth D. Merry 
55ef270ab1SKenneth D. Merry #define SCSI_IOFMT_ARGS(io) io->instance_index, SCSI_ITT_SIZE(io->ocs), io->init_task_tag, SCSI_ITT_SIZE(io->ocs), io->tgt_task_tag, io->hw_tag
56ef270ab1SKenneth D. Merry 
57ef270ab1SKenneth D. Merry #define enable_tsend_auto_resp(ocs)		((ocs->ctrlmask & OCS_CTRLMASK_XPORT_DISABLE_AUTORSP_TSEND) == 0)
58ef270ab1SKenneth D. Merry #define enable_treceive_auto_resp(ocs)	((ocs->ctrlmask & OCS_CTRLMASK_XPORT_DISABLE_AUTORSP_TRECEIVE) == 0)
59ef270ab1SKenneth D. Merry 
60ef270ab1SKenneth D. Merry #define scsi_io_printf(io, fmt, ...) ocs_log_info(io->ocs, "[%s]" SCSI_IOFMT fmt, \
61ef270ab1SKenneth D. Merry 	io->node->display_name, SCSI_IOFMT_ARGS(io), ##__VA_ARGS__)
62ef270ab1SKenneth D. Merry 
63ef270ab1SKenneth D. Merry #define scsi_io_trace(io, fmt, ...) \
64ef270ab1SKenneth D. Merry 	do { \
65ef270ab1SKenneth D. Merry 		if (OCS_LOG_ENABLE_SCSI_TRACE(io->ocs)) \
66ef270ab1SKenneth D. Merry 			scsi_io_printf(io, fmt, ##__VA_ARGS__); \
67ef270ab1SKenneth D. Merry 	} while (0)
68ef270ab1SKenneth D. Merry 
69ef270ab1SKenneth D. Merry #define scsi_log(ocs, fmt, ...) \
70ef270ab1SKenneth D. Merry 	do { \
71ef270ab1SKenneth D. Merry 		if (OCS_LOG_ENABLE_SCSI_TRACE(ocs)) \
72ef270ab1SKenneth D. Merry 			ocs_log_info(ocs, fmt, ##__VA_ARGS__); \
73ef270ab1SKenneth D. Merry 	} while (0)
74ef270ab1SKenneth D. Merry 
75ef270ab1SKenneth D. Merry static int32_t ocs_target_send_bls_resp(ocs_io_t *io, ocs_scsi_io_cb_t cb, void *arg);
76ef270ab1SKenneth D. Merry static int32_t ocs_scsi_abort_io_cb(struct ocs_hw_io_s *hio, ocs_remote_node_t *rnode, uint32_t len, int32_t status,
77ef270ab1SKenneth D. Merry 	uint32_t ext, void *arg);
78ef270ab1SKenneth D. Merry 
79ef270ab1SKenneth D. Merry static void ocs_scsi_io_free_ovfl(ocs_io_t *io);
80ef270ab1SKenneth D. Merry static uint32_t ocs_scsi_count_sgls(ocs_hw_dif_info_t *hw_dif, ocs_scsi_sgl_t *sgl, uint32_t sgl_count);
81ef270ab1SKenneth D. Merry static int ocs_scsi_dif_guard_is_crc(uint8_t direction, ocs_hw_dif_info_t *dif_info);
82ef270ab1SKenneth D. Merry static ocs_scsi_io_status_e ocs_scsi_dif_check_unknown(ocs_io_t *io, uint32_t length, uint32_t check_length, int is_crc);
83ef270ab1SKenneth D. Merry static uint32_t ocs_scsi_dif_check_guard(ocs_hw_dif_info_t *dif_info, ocs_scsi_vaddr_len_t addrlen[],
84ef270ab1SKenneth D. Merry 	uint32_t addrlen_count, ocs_dif_t *dif, int is_crc);
85ef270ab1SKenneth D. Merry static uint32_t ocs_scsi_dif_check_app_tag(ocs_t *ocs, ocs_hw_dif_info_t *dif_info, uint16_t exp_app_tag, ocs_dif_t *dif);
86ef270ab1SKenneth D. Merry static uint32_t ocs_scsi_dif_check_ref_tag(ocs_t *ocs, ocs_hw_dif_info_t *dif_info, uint32_t exp_ref_tag, ocs_dif_t *dif);
87ef270ab1SKenneth D. Merry static int32_t ocs_scsi_convert_dif_info(ocs_t *ocs, ocs_scsi_dif_info_t *scsi_dif_info,
88ef270ab1SKenneth D. Merry 	ocs_hw_dif_info_t *hw_dif_info);
89ef270ab1SKenneth D. Merry static int32_t ocs_scsi_io_dispatch_hw_io(ocs_io_t *io, ocs_hw_io_t *hio);
90ef270ab1SKenneth D. Merry static int32_t ocs_scsi_io_dispatch_no_hw_io(ocs_io_t *io);
91ef270ab1SKenneth D. Merry static void _ocs_scsi_io_free(void *arg);
92ef270ab1SKenneth D. Merry 
93ef270ab1SKenneth D. Merry /**
94ef270ab1SKenneth D. Merry  * @ingroup scsi_api_base
95ef270ab1SKenneth D. Merry  * @brief Returns a big-endian 32-bit value given a pointer.
96ef270ab1SKenneth D. Merry  *
97ef270ab1SKenneth D. Merry  * @param p Pointer to the 32-bit big-endian location.
98ef270ab1SKenneth D. Merry  *
99ef270ab1SKenneth D. Merry  * @return Returns the byte-swapped 32-bit value.
100ef270ab1SKenneth D. Merry  */
101ef270ab1SKenneth D. Merry 
102ef270ab1SKenneth D. Merry static inline uint32_t
103ef270ab1SKenneth D. Merry ocs_fc_getbe32(void *p)
104ef270ab1SKenneth D. Merry {
105ef270ab1SKenneth D. Merry 	return ocs_be32toh(*((uint32_t*)p));
106ef270ab1SKenneth D. Merry }
107ef270ab1SKenneth D. Merry 
108ef270ab1SKenneth D. Merry /**
109ef270ab1SKenneth D. Merry  * @ingroup scsi_api_base
110ef270ab1SKenneth D. Merry  * @brief Enable IO allocation.
111ef270ab1SKenneth D. Merry  *
112ef270ab1SKenneth D. Merry  * @par Description
113ef270ab1SKenneth D. Merry  * The SCSI and Transport IO allocation functions are enabled. If the allocation functions
114ef270ab1SKenneth D. Merry  * are not enabled, then calls to ocs_scsi_io_alloc() (and ocs_els_io_alloc() for FC) will
115ef270ab1SKenneth D. Merry  * fail.
116ef270ab1SKenneth D. Merry  *
117ef270ab1SKenneth D. Merry  * @param node Pointer to node object.
118ef270ab1SKenneth D. Merry  *
119ef270ab1SKenneth D. Merry  * @return None.
120ef270ab1SKenneth D. Merry  */
121ef270ab1SKenneth D. Merry void
122ef270ab1SKenneth D. Merry ocs_scsi_io_alloc_enable(ocs_node_t *node)
123ef270ab1SKenneth D. Merry {
124ef270ab1SKenneth D. Merry 	ocs_assert(node != NULL);
125ef270ab1SKenneth D. Merry 	ocs_lock(&node->active_ios_lock);
126ef270ab1SKenneth D. Merry 		node->io_alloc_enabled = TRUE;
127ef270ab1SKenneth D. Merry 	ocs_unlock(&node->active_ios_lock);
128ef270ab1SKenneth D. Merry }
129ef270ab1SKenneth D. Merry 
130ef270ab1SKenneth D. Merry /**
131ef270ab1SKenneth D. Merry  * @ingroup scsi_api_base
132ef270ab1SKenneth D. Merry  * @brief Disable IO allocation
133ef270ab1SKenneth D. Merry  *
134ef270ab1SKenneth D. Merry  * @par Description
135ef270ab1SKenneth D. Merry  * The SCSI and Transport IO allocation functions are disabled. If the allocation functions
136ef270ab1SKenneth D. Merry  * are not enabled, then calls to ocs_scsi_io_alloc() (and ocs_els_io_alloc() for FC) will
137ef270ab1SKenneth D. Merry  * fail.
138ef270ab1SKenneth D. Merry  *
139ef270ab1SKenneth D. Merry  * @param node Pointer to node object
140ef270ab1SKenneth D. Merry  *
141ef270ab1SKenneth D. Merry  * @return None.
142ef270ab1SKenneth D. Merry  */
143ef270ab1SKenneth D. Merry void
144ef270ab1SKenneth D. Merry ocs_scsi_io_alloc_disable(ocs_node_t *node)
145ef270ab1SKenneth D. Merry {
146ef270ab1SKenneth D. Merry 	ocs_assert(node != NULL);
147ef270ab1SKenneth D. Merry 	ocs_lock(&node->active_ios_lock);
148ef270ab1SKenneth D. Merry 		node->io_alloc_enabled = FALSE;
149ef270ab1SKenneth D. Merry 	ocs_unlock(&node->active_ios_lock);
150ef270ab1SKenneth D. Merry }
151ef270ab1SKenneth D. Merry 
152ef270ab1SKenneth D. Merry /**
153ef270ab1SKenneth D. Merry  * @ingroup scsi_api_base
154ef270ab1SKenneth D. Merry  * @brief Allocate a SCSI IO context.
155ef270ab1SKenneth D. Merry  *
156ef270ab1SKenneth D. Merry  * @par Description
157ef270ab1SKenneth D. Merry  * A SCSI IO context is allocated and associated with a @c node. This function
158ef270ab1SKenneth D. Merry  * is called by an initiator-client when issuing SCSI commands to remote
159ef270ab1SKenneth D. Merry  * target devices. On completion, ocs_scsi_io_free() is called.
160ef270ab1SKenneth D. Merry  * @n @n
161ef270ab1SKenneth D. Merry  * The returned ocs_io_t structure has an element of type ocs_scsi_ini_io_t named
162ef270ab1SKenneth D. Merry  * "ini_io" that is declared and used by an initiator-client for private information.
163ef270ab1SKenneth D. Merry  *
164ef270ab1SKenneth D. Merry  * @param node Pointer to the associated node structure.
165ef270ab1SKenneth D. Merry  * @param role Role for IO (originator/responder).
166ef270ab1SKenneth D. Merry  *
167ef270ab1SKenneth D. Merry  * @return Returns the pointer to the IO context, or NULL.
168ef270ab1SKenneth D. Merry  *
169ef270ab1SKenneth D. Merry  */
170ef270ab1SKenneth D. Merry 
171ef270ab1SKenneth D. Merry ocs_io_t *
172ef270ab1SKenneth D. Merry ocs_scsi_io_alloc(ocs_node_t *node, ocs_scsi_io_role_e role)
173ef270ab1SKenneth D. Merry {
174ef270ab1SKenneth D. Merry 	ocs_t *ocs;
175ef270ab1SKenneth D. Merry 	ocs_xport_t *xport;
176ef270ab1SKenneth D. Merry 	ocs_io_t *io;
177ef270ab1SKenneth D. Merry 
178ef270ab1SKenneth D. Merry 	ocs_assert(node, NULL);
179ef270ab1SKenneth D. Merry 	ocs_assert(node->ocs, NULL);
180ef270ab1SKenneth D. Merry 
181ef270ab1SKenneth D. Merry 	ocs = node->ocs;
182ef270ab1SKenneth D. Merry 	ocs_assert(ocs->xport, NULL);
183ef270ab1SKenneth D. Merry 	xport = ocs->xport;
184ef270ab1SKenneth D. Merry 
185ef270ab1SKenneth D. Merry 	ocs_lock(&node->active_ios_lock);
186ef270ab1SKenneth D. Merry 
187ef270ab1SKenneth D. Merry 		if (!node->io_alloc_enabled) {
188ef270ab1SKenneth D. Merry 			ocs_unlock(&node->active_ios_lock);
189ef270ab1SKenneth D. Merry 			return NULL;
190ef270ab1SKenneth D. Merry 		}
191ef270ab1SKenneth D. Merry 
192ef270ab1SKenneth D. Merry 		io = ocs_io_alloc(ocs);
193ef270ab1SKenneth D. Merry 		if (io == NULL) {
194ef270ab1SKenneth D. Merry 			ocs_atomic_add_return(&xport->io_alloc_failed_count, 1);
195ef270ab1SKenneth D. Merry 			ocs_unlock(&node->active_ios_lock);
196ef270ab1SKenneth D. Merry 			return NULL;
197ef270ab1SKenneth D. Merry 		}
198ef270ab1SKenneth D. Merry 
199ef270ab1SKenneth D. Merry 		/* initialize refcount */
200ef270ab1SKenneth D. Merry 		ocs_ref_init(&io->ref, _ocs_scsi_io_free, io);
201ef270ab1SKenneth D. Merry 
202ef270ab1SKenneth D. Merry 		if (io->hio != NULL) {
203ef270ab1SKenneth D. Merry 			ocs_log_err(node->ocs, "assertion failed: io->hio is not NULL\n");
204*fc620f97SRam Kishore Vegesna 			ocs_io_free(ocs, io);
205ef270ab1SKenneth D. Merry 			ocs_unlock(&node->active_ios_lock);
206ef270ab1SKenneth D. Merry 			return NULL;
207ef270ab1SKenneth D. Merry 		}
208ef270ab1SKenneth D. Merry 
209ef270ab1SKenneth D. Merry 		/* set generic fields */
210ef270ab1SKenneth D. Merry 		io->ocs = ocs;
211ef270ab1SKenneth D. Merry 		io->node = node;
212ef270ab1SKenneth D. Merry 
213ef270ab1SKenneth D. Merry 		/* set type and name */
214ef270ab1SKenneth D. Merry 		io->io_type = OCS_IO_TYPE_IO;
215ef270ab1SKenneth D. Merry 		io->display_name = "scsi_io";
216ef270ab1SKenneth D. Merry 
217ef270ab1SKenneth D. Merry 		switch (role) {
218ef270ab1SKenneth D. Merry 		case OCS_SCSI_IO_ROLE_ORIGINATOR:
219ef270ab1SKenneth D. Merry 			io->cmd_ini = TRUE;
220ef270ab1SKenneth D. Merry 			io->cmd_tgt = FALSE;
221ef270ab1SKenneth D. Merry 			break;
222ef270ab1SKenneth D. Merry 		case OCS_SCSI_IO_ROLE_RESPONDER:
223ef270ab1SKenneth D. Merry 			io->cmd_ini = FALSE;
224ef270ab1SKenneth D. Merry 			io->cmd_tgt = TRUE;
225ef270ab1SKenneth D. Merry 			break;
226ef270ab1SKenneth D. Merry 		}
227ef270ab1SKenneth D. Merry 
228ef270ab1SKenneth D. Merry 		/* Add to node's active_ios list */
229ef270ab1SKenneth D. Merry 		ocs_list_add_tail(&node->active_ios, io);
230ef270ab1SKenneth D. Merry 
231ef270ab1SKenneth D. Merry 	ocs_unlock(&node->active_ios_lock);
232ef270ab1SKenneth D. Merry 
233ef270ab1SKenneth D. Merry 	return io;
234ef270ab1SKenneth D. Merry }
235ef270ab1SKenneth D. Merry 
236ef270ab1SKenneth D. Merry /**
237ef270ab1SKenneth D. Merry  * @ingroup scsi_api_base
238ef270ab1SKenneth D. Merry  * @brief Free a SCSI IO context (internal).
239ef270ab1SKenneth D. Merry  *
240ef270ab1SKenneth D. Merry  * @par Description
241ef270ab1SKenneth D. Merry  * The IO context previously allocated using ocs_scsi_io_alloc()
242ef270ab1SKenneth D. Merry  * is freed. This is called from within the transport layer,
243ef270ab1SKenneth D. Merry  * when the reference count goes to zero.
244ef270ab1SKenneth D. Merry  *
245ef270ab1SKenneth D. Merry  * @param arg Pointer to the IO context.
246ef270ab1SKenneth D. Merry  *
247ef270ab1SKenneth D. Merry  * @return None.
248ef270ab1SKenneth D. Merry  */
249ef270ab1SKenneth D. Merry static void
250ef270ab1SKenneth D. Merry _ocs_scsi_io_free(void *arg)
251ef270ab1SKenneth D. Merry {
252ef270ab1SKenneth D. Merry 	ocs_io_t *io = (ocs_io_t *)arg;
253ef270ab1SKenneth D. Merry 	ocs_t *ocs = io->ocs;
254ef270ab1SKenneth D. Merry 	ocs_node_t *node = io->node;
255ef270ab1SKenneth D. Merry 	int send_empty_event;
256ef270ab1SKenneth D. Merry 
257ef270ab1SKenneth D. Merry 	ocs_assert(io != NULL);
258ef270ab1SKenneth D. Merry 
259ef270ab1SKenneth D. Merry 	scsi_io_trace(io, "freeing io 0x%p %s\n", io, io->display_name);
260ef270ab1SKenneth D. Merry 
261ef270ab1SKenneth D. Merry 	ocs_assert(ocs_io_busy(io));
262ef270ab1SKenneth D. Merry 
263ef270ab1SKenneth D. Merry 	ocs_lock(&node->active_ios_lock);
264ef270ab1SKenneth D. Merry 		ocs_list_remove(&node->active_ios, io);
265ef270ab1SKenneth D. Merry 		send_empty_event = (!node->io_alloc_enabled) && ocs_list_empty(&node->active_ios);
266ef270ab1SKenneth D. Merry 	ocs_unlock(&node->active_ios_lock);
267ef270ab1SKenneth D. Merry 
268ef270ab1SKenneth D. Merry 	if (send_empty_event) {
269ef270ab1SKenneth D. Merry 		ocs_node_post_event(node, OCS_EVT_NODE_ACTIVE_IO_LIST_EMPTY, NULL);
270ef270ab1SKenneth D. Merry 	}
271ef270ab1SKenneth D. Merry 
272ef270ab1SKenneth D. Merry 	io->node = NULL;
273ef270ab1SKenneth D. Merry 	ocs_io_free(ocs, io);
274ef270ab1SKenneth D. Merry 
275ef270ab1SKenneth D. Merry }
276ef270ab1SKenneth D. Merry 
277ef270ab1SKenneth D. Merry /**
278ef270ab1SKenneth D. Merry  * @ingroup scsi_api_base
279ef270ab1SKenneth D. Merry  * @brief Free a SCSI IO context.
280ef270ab1SKenneth D. Merry  *
281ef270ab1SKenneth D. Merry  * @par Description
282ef270ab1SKenneth D. Merry  * The IO context previously allocated using ocs_scsi_io_alloc() is freed.
283ef270ab1SKenneth D. Merry  *
284ef270ab1SKenneth D. Merry  * @param io Pointer to the IO context.
285ef270ab1SKenneth D. Merry  *
286ef270ab1SKenneth D. Merry  * @return None.
287ef270ab1SKenneth D. Merry  */
288ef270ab1SKenneth D. Merry void
289ef270ab1SKenneth D. Merry ocs_scsi_io_free(ocs_io_t *io)
290ef270ab1SKenneth D. Merry {
291ef270ab1SKenneth D. Merry 	scsi_io_trace(io, "freeing io 0x%p %s\n", io, io->display_name);
292ef270ab1SKenneth D. Merry 	ocs_assert(ocs_ref_read_count(&io->ref) > 0);
293ef270ab1SKenneth D. Merry 	ocs_ref_put(&io->ref); /* ocs_ref_get(): ocs_scsi_io_alloc() */
294ef270ab1SKenneth D. Merry }
295ef270ab1SKenneth D. Merry 
296ef270ab1SKenneth D. Merry static int32_t
297ef270ab1SKenneth D. Merry ocs_scsi_send_io(ocs_hw_io_type_e type, ocs_node_t *node, ocs_io_t *io, uint64_t lun,
298ef270ab1SKenneth D. Merry 	ocs_scsi_tmf_cmd_e tmf, uint8_t *cdb, uint32_t cdb_len,
299ef270ab1SKenneth D. Merry 	ocs_scsi_dif_info_t *dif_info,
300ef270ab1SKenneth D. Merry 	ocs_scsi_sgl_t *sgl, uint32_t sgl_count, uint32_t wire_len, uint32_t first_burst,
30188364968SAlexander Motin 	ocs_scsi_rsp_io_cb_t cb, void *arg, uint32_t flags);
302ef270ab1SKenneth D. Merry 
303ef270ab1SKenneth D. Merry /**
304ef270ab1SKenneth D. Merry  * @brief Target response completion callback.
305ef270ab1SKenneth D. Merry  *
306ef270ab1SKenneth D. Merry  * @par Description
307ef270ab1SKenneth D. Merry  * Function is called upon the completion of a target IO request.
308ef270ab1SKenneth D. Merry  *
309ef270ab1SKenneth D. Merry  * @param hio Pointer to the HW IO structure.
310ef270ab1SKenneth D. Merry  * @param rnode Remote node associated with the IO that is completing.
311ef270ab1SKenneth D. Merry  * @param length Length of the response payload.
312ef270ab1SKenneth D. Merry  * @param status Completion status.
313ef270ab1SKenneth D. Merry  * @param ext_status Extended completion status.
314ef270ab1SKenneth D. Merry  * @param app Application-specific data (generally a pointer to the IO context).
315ef270ab1SKenneth D. Merry  *
316ef270ab1SKenneth D. Merry  * @return None.
317ef270ab1SKenneth D. Merry  */
318ef270ab1SKenneth D. Merry 
319ef270ab1SKenneth D. Merry static void
320ef270ab1SKenneth D. Merry ocs_target_io_cb(ocs_hw_io_t *hio, ocs_remote_node_t *rnode, uint32_t length,
321ef270ab1SKenneth D. Merry 	int32_t status, uint32_t ext_status, void *app)
322ef270ab1SKenneth D. Merry {
323ef270ab1SKenneth D. Merry 	ocs_io_t *io = app;
324ef270ab1SKenneth D. Merry 	ocs_t *ocs;
325ef270ab1SKenneth D. Merry 	ocs_scsi_io_status_e scsi_status = OCS_SCSI_STATUS_GOOD;
326ef270ab1SKenneth D. Merry 	uint16_t additional_length;
327ef270ab1SKenneth D. Merry 	uint8_t edir;
328ef270ab1SKenneth D. Merry 	uint8_t tdpv;
329ef270ab1SKenneth D. Merry 	ocs_hw_dif_info_t *dif_info = &io->hw_dif;
330ef270ab1SKenneth D. Merry 	int is_crc;
331ef270ab1SKenneth D. Merry 
332ef270ab1SKenneth D. Merry 	ocs_assert(io);
333ef270ab1SKenneth D. Merry 
334ef270ab1SKenneth D. Merry 	scsi_io_trace(io, "status x%x ext_status x%x\n", status, ext_status);
335ef270ab1SKenneth D. Merry 
336ef270ab1SKenneth D. Merry 	ocs = io->ocs;
337ef270ab1SKenneth D. Merry 	ocs_assert(ocs);
338ef270ab1SKenneth D. Merry 
339ef270ab1SKenneth D. Merry 	ocs_scsi_io_free_ovfl(io);
340ef270ab1SKenneth D. Merry 
341ef270ab1SKenneth D. Merry 	io->transferred += length;
342ef270ab1SKenneth D. Merry 
343ef270ab1SKenneth D. Merry 	/* Call target server completion */
344ef270ab1SKenneth D. Merry 	if (io->scsi_tgt_cb) {
345ef270ab1SKenneth D. Merry 		ocs_scsi_io_cb_t cb = io->scsi_tgt_cb;
346ef270ab1SKenneth D. Merry 		uint32_t flags = 0;
347ef270ab1SKenneth D. Merry 
348ef270ab1SKenneth D. Merry 		/* Clear the callback before invoking the callback */
349ef270ab1SKenneth D. Merry 		io->scsi_tgt_cb = NULL;
350ef270ab1SKenneth D. Merry 
351ef270ab1SKenneth D. Merry 		/* if status was good, and auto-good-response was set, then callback
352ef270ab1SKenneth D. Merry 		 * target-server with IO_CMPL_RSP_SENT, otherwise send IO_CMPL
353ef270ab1SKenneth D. Merry 		 */
354ef270ab1SKenneth D. Merry 		if ((status == 0) && (io->auto_resp))
355ef270ab1SKenneth D. Merry 			flags |= OCS_SCSI_IO_CMPL_RSP_SENT;
356ef270ab1SKenneth D. Merry 		else
357ef270ab1SKenneth D. Merry 			flags |= OCS_SCSI_IO_CMPL;
358ef270ab1SKenneth D. Merry 
359ef270ab1SKenneth D. Merry 		switch (status) {
360ef270ab1SKenneth D. Merry 		case SLI4_FC_WCQE_STATUS_SUCCESS:
361ef270ab1SKenneth D. Merry 			scsi_status = OCS_SCSI_STATUS_GOOD;
362ef270ab1SKenneth D. Merry 			break;
363ef270ab1SKenneth D. Merry 		case SLI4_FC_WCQE_STATUS_DI_ERROR:
364ef270ab1SKenneth D. Merry 			if (ext_status & SLI4_FC_DI_ERROR_GE) {
365ef270ab1SKenneth D. Merry 				scsi_status = OCS_SCSI_STATUS_DIF_GUARD_ERROR;
366ef270ab1SKenneth D. Merry 			} else if (ext_status & SLI4_FC_DI_ERROR_AE) {
367ef270ab1SKenneth D. Merry 				scsi_status = OCS_SCSI_STATUS_DIF_APP_TAG_ERROR;
368ef270ab1SKenneth D. Merry 			} else if (ext_status & SLI4_FC_DI_ERROR_RE) {
369ef270ab1SKenneth D. Merry 				scsi_status = OCS_SCSI_STATUS_DIF_REF_TAG_ERROR;
370ef270ab1SKenneth D. Merry 			} else {
371ef270ab1SKenneth D. Merry 				additional_length = ((ext_status >> 16) & 0xFFFF);
372ef270ab1SKenneth D. Merry 
373ef270ab1SKenneth D. Merry 				/* Capture the EDIR and TDPV bits as 0 or 1 for easier printing. */
374ef270ab1SKenneth D. Merry 				edir = !!(ext_status & SLI4_FC_DI_ERROR_EDIR);
375ef270ab1SKenneth D. Merry 				tdpv = !!(ext_status & SLI4_FC_DI_ERROR_TDPV);
376ef270ab1SKenneth D. Merry 
377ef270ab1SKenneth D. Merry 				is_crc = ocs_scsi_dif_guard_is_crc(edir, dif_info);
378ef270ab1SKenneth D. Merry 
379ef270ab1SKenneth D. Merry 				if (edir == 0) {
380ef270ab1SKenneth D. Merry 					/* For reads, we have everything in memory.  Start checking from beginning. */
381ef270ab1SKenneth D. Merry 					scsi_status = ocs_scsi_dif_check_unknown(io, 0, io->wire_len, is_crc);
382ef270ab1SKenneth D. Merry 				} else {
383ef270ab1SKenneth D. Merry 					/* For writes, use the additional length to determine where to look for the error.
384ef270ab1SKenneth D. Merry 					 * The additional_length field is set to 0 if it is not supported.
385ef270ab1SKenneth D. Merry 					 * The additional length field is valid if:
386ef270ab1SKenneth D. Merry 					 *    . additional_length is not zero
387ef270ab1SKenneth D. Merry 					 *    . Total Data Placed is valid
388ef270ab1SKenneth D. Merry 					 *    . Error Direction is RX (1)
389ef270ab1SKenneth D. Merry 					 *    . Operation is a pass thru (CRC or CKSUM on IN, and CRC or CHKSUM on OUT) (all pass-thru cases except raw)
390ef270ab1SKenneth D. Merry 					 */
391ef270ab1SKenneth D. Merry 					if ((additional_length != 0) && (tdpv != 0) &&
392ef270ab1SKenneth D. Merry 					    (dif_info->dif == SLI4_DIF_PASS_THROUGH) && (dif_info->dif_oper != OCS_HW_SGE_DIF_OP_IN_RAW_OUT_RAW) ) {
393ef270ab1SKenneth D. Merry 						scsi_status = ocs_scsi_dif_check_unknown(io, length, additional_length, is_crc);
394ef270ab1SKenneth D. Merry 					} else {
395ef270ab1SKenneth D. Merry 						/* If we can't do additional checking, then fall-back to guard error */
396ef270ab1SKenneth D. Merry 						scsi_status = OCS_SCSI_STATUS_DIF_GUARD_ERROR;
397ef270ab1SKenneth D. Merry 					}
398ef270ab1SKenneth D. Merry 				}
399ef270ab1SKenneth D. Merry 			}
400ef270ab1SKenneth D. Merry 			break;
401ef270ab1SKenneth D. Merry 		case SLI4_FC_WCQE_STATUS_LOCAL_REJECT:
402ef270ab1SKenneth D. Merry 			switch (ext_status) {
403ef270ab1SKenneth D. Merry 			case SLI4_FC_LOCAL_REJECT_INVALID_RELOFFSET:
404ef270ab1SKenneth D. Merry 			case SLI4_FC_LOCAL_REJECT_ABORT_REQUESTED:
405ef270ab1SKenneth D. Merry 				scsi_status = OCS_SCSI_STATUS_ABORTED;
406ef270ab1SKenneth D. Merry 				break;
407ef270ab1SKenneth D. Merry 			case SLI4_FC_LOCAL_REJECT_INVALID_RPI:
408ef270ab1SKenneth D. Merry 				scsi_status = OCS_SCSI_STATUS_NEXUS_LOST;
409ef270ab1SKenneth D. Merry 				break;
410ef270ab1SKenneth D. Merry 			case SLI4_FC_LOCAL_REJECT_NO_XRI:
411ef270ab1SKenneth D. Merry 				scsi_status = OCS_SCSI_STATUS_NO_IO;
412ef270ab1SKenneth D. Merry 				break;
413ef270ab1SKenneth D. Merry 			default:
414ef270ab1SKenneth D. Merry 				/* TODO: we have seen 0x0d (TX_DMA_FAILED error) */
415ef270ab1SKenneth D. Merry 				scsi_status = OCS_SCSI_STATUS_ERROR;
416ef270ab1SKenneth D. Merry 				break;
417ef270ab1SKenneth D. Merry 			}
418ef270ab1SKenneth D. Merry 			break;
419ef270ab1SKenneth D. Merry 
420ef270ab1SKenneth D. Merry 		case SLI4_FC_WCQE_STATUS_TARGET_WQE_TIMEOUT:
421ef270ab1SKenneth D. Merry 			/* target IO timed out */
422ef270ab1SKenneth D. Merry 			scsi_status = OCS_SCSI_STATUS_TIMEDOUT_AND_ABORTED;
423ef270ab1SKenneth D. Merry 			break;
424ef270ab1SKenneth D. Merry 
425ef270ab1SKenneth D. Merry 		case SLI4_FC_WCQE_STATUS_SHUTDOWN:
426ef270ab1SKenneth D. Merry 			/* Target IO cancelled by HW */
427ef270ab1SKenneth D. Merry 			scsi_status = OCS_SCSI_STATUS_SHUTDOWN;
428ef270ab1SKenneth D. Merry 			break;
429ef270ab1SKenneth D. Merry 
430ef270ab1SKenneth D. Merry 		default:
431ef270ab1SKenneth D. Merry 			scsi_status = OCS_SCSI_STATUS_ERROR;
432ef270ab1SKenneth D. Merry 			break;
433ef270ab1SKenneth D. Merry 		}
434ef270ab1SKenneth D. Merry 
435ef270ab1SKenneth D. Merry 		cb(io, scsi_status, flags, io->scsi_tgt_cb_arg);
436ef270ab1SKenneth D. Merry 	}
437ef270ab1SKenneth D. Merry 	ocs_scsi_check_pending(ocs);
438ef270ab1SKenneth D. Merry }
439ef270ab1SKenneth D. Merry 
440ef270ab1SKenneth D. Merry /**
441ef270ab1SKenneth D. Merry  * @brief Determine if an IO is using CRC for DIF guard format.
442ef270ab1SKenneth D. Merry  *
443ef270ab1SKenneth D. Merry  * @param direction IO direction: 1 for write, 0 for read.
444ef270ab1SKenneth D. Merry  * @param dif_info Pointer to HW DIF info data.
445ef270ab1SKenneth D. Merry  *
446ef270ab1SKenneth D. Merry  * @return Returns TRUE if using CRC, FALSE if not.
447ef270ab1SKenneth D. Merry  */
448ef270ab1SKenneth D. Merry static int
449ef270ab1SKenneth D. Merry ocs_scsi_dif_guard_is_crc(uint8_t direction, ocs_hw_dif_info_t *dif_info)
450ef270ab1SKenneth D. Merry {
451ef270ab1SKenneth D. Merry 	int is_crc;
452ef270ab1SKenneth D. Merry 
453ef270ab1SKenneth D. Merry 	if (direction) {
454ef270ab1SKenneth D. Merry 		/* For writes, check if operation is "OUT_CRC" or not */
455ef270ab1SKenneth D. Merry 		switch(dif_info->dif_oper) {
456ef270ab1SKenneth D. Merry 			case OCS_HW_SGE_DIF_OP_IN_NODIF_OUT_CRC:
457ef270ab1SKenneth D. Merry 			case OCS_HW_SGE_DIF_OP_IN_CRC_OUT_CRC:
458ef270ab1SKenneth D. Merry 			case OCS_HW_SGE_DIF_OP_IN_CHKSUM_OUT_CRC:
459ef270ab1SKenneth D. Merry 				is_crc = TRUE;
460ef270ab1SKenneth D. Merry 				break;
461ef270ab1SKenneth D. Merry 			default:
462ef270ab1SKenneth D. Merry 				is_crc = FALSE;
463ef270ab1SKenneth D. Merry 				break;
464ef270ab1SKenneth D. Merry 		}
465ef270ab1SKenneth D. Merry 	} else {
466ef270ab1SKenneth D. Merry 		/* For reads, check if operation is "IN_CRC" or not */
467ef270ab1SKenneth D. Merry 		switch(dif_info->dif_oper) {
468ef270ab1SKenneth D. Merry 			case OCS_HW_SGE_DIF_OP_IN_CRC_OUT_NODIF:
469ef270ab1SKenneth D. Merry 			case OCS_HW_SGE_DIF_OP_IN_CRC_OUT_CRC:
470ef270ab1SKenneth D. Merry 			case OCS_HW_SGE_DIF_OP_IN_CRC_OUT_CHKSUM:
471ef270ab1SKenneth D. Merry 				is_crc = TRUE;
472ef270ab1SKenneth D. Merry 				break;
473ef270ab1SKenneth D. Merry 			default:
474ef270ab1SKenneth D. Merry 				is_crc = FALSE;
475ef270ab1SKenneth D. Merry 				break;
476ef270ab1SKenneth D. Merry 		}
477ef270ab1SKenneth D. Merry 	}
478ef270ab1SKenneth D. Merry 
479ef270ab1SKenneth D. Merry 	return is_crc;
480ef270ab1SKenneth D. Merry }
481ef270ab1SKenneth D. Merry 
482ef270ab1SKenneth D. Merry /**
483ef270ab1SKenneth D. Merry  * @brief Check a block and DIF data, computing the appropriate SCSI status
484ef270ab1SKenneth D. Merry  *
485ef270ab1SKenneth D. Merry  * @par Description
486ef270ab1SKenneth D. Merry  * This function is used to check blocks and DIF when given an unknown DIF
487ef270ab1SKenneth D. Merry  * status using the following logic:
488ef270ab1SKenneth D. Merry  *
489ef270ab1SKenneth D. Merry  * Given the address of the last good block, and a length of bytes that includes
490ef270ab1SKenneth D. Merry  * the block with the DIF error, find the bad block. If a block is found with an
491ef270ab1SKenneth D. Merry  * app_tag or ref_tag error, then return the appropriate error. No block is expected
492ef270ab1SKenneth D. Merry  * to have a block guard error since hardware "fixes" the crc. So if no block in the
493ef270ab1SKenneth D. Merry  * range of blocks has an error, then it is presumed to be a BLOCK GUARD error.
494ef270ab1SKenneth D. Merry  *
495ef270ab1SKenneth D. Merry  * @param io Pointer to the IO object.
496ef270ab1SKenneth D. Merry  * @param length Length of bytes covering the good blocks.
497ef270ab1SKenneth D. Merry  * @param check_length Length of bytes that covers the bad block.
498ef270ab1SKenneth D. Merry  * @param is_crc True if guard is using CRC format.
499ef270ab1SKenneth D. Merry  *
500ef270ab1SKenneth D. Merry  * @return Returns SCSI status.
501ef270ab1SKenneth D. Merry  */
502ef270ab1SKenneth D. Merry 
503ef270ab1SKenneth D. Merry static ocs_scsi_io_status_e
504ef270ab1SKenneth D. Merry ocs_scsi_dif_check_unknown(ocs_io_t *io, uint32_t length, uint32_t check_length, int is_crc)
505ef270ab1SKenneth D. Merry {
506ef270ab1SKenneth D. Merry 	uint32_t i;
507ef270ab1SKenneth D. Merry 	ocs_t *ocs = io->ocs;
508ef270ab1SKenneth D. Merry 	ocs_hw_dif_info_t *dif_info = &io->hw_dif;
509ef270ab1SKenneth D. Merry 	ocs_scsi_io_status_e scsi_status = OCS_SCSI_STATUS_DIF_GUARD_ERROR;
510ef270ab1SKenneth D. Merry 	uint32_t blocksize;			/* data block size */
511ef270ab1SKenneth D. Merry 	uint64_t first_check_block;		/* first block following total data placed */
512ef270ab1SKenneth D. Merry 	uint64_t last_check_block;		/* last block to check */
513ef270ab1SKenneth D. Merry 	uint32_t check_count;			/* count of blocks to check */
514ef270ab1SKenneth D. Merry 	ocs_scsi_vaddr_len_t addrlen[4];	/* address-length pairs returned from target */
515ef270ab1SKenneth D. Merry 	int32_t addrlen_count;			/* count of address-length pairs */
516ef270ab1SKenneth D. Merry 	ocs_dif_t *dif;				/* pointer to DIF block returned from target */
517ef270ab1SKenneth D. Merry 	ocs_scsi_dif_info_t scsi_dif_info = io->scsi_dif_info;
518ef270ab1SKenneth D. Merry 
519ef270ab1SKenneth D. Merry 	blocksize = ocs_hw_dif_mem_blocksize(&io->hw_dif, TRUE);
520ef270ab1SKenneth D. Merry 	first_check_block = length / blocksize;
521ef270ab1SKenneth D. Merry 	last_check_block = ((length + check_length) / blocksize);
522ef270ab1SKenneth D. Merry 	check_count = last_check_block - first_check_block;
523ef270ab1SKenneth D. Merry 
524ef270ab1SKenneth D. Merry 	ocs_log_debug(ocs, "blocksize %d first check_block %" PRId64 " last_check_block %" PRId64 " check_count %d\n",
525ef270ab1SKenneth D. Merry 		blocksize, first_check_block, last_check_block, check_count);
526ef270ab1SKenneth D. Merry 
527ef270ab1SKenneth D. Merry 	for (i = first_check_block; i < last_check_block; i++) {
528ef270ab1SKenneth D. Merry 		addrlen_count = ocs_scsi_get_block_vaddr(io, (scsi_dif_info.lba + i), addrlen, ARRAY_SIZE(addrlen), (void**) &dif);
529ef270ab1SKenneth D. Merry 		if (addrlen_count < 0) {
530ef270ab1SKenneth D. Merry 			ocs_log_test(ocs, "ocs_scsi_get_block_vaddr() failed: %d\n", addrlen_count);
531ef270ab1SKenneth D. Merry 			scsi_status = OCS_SCSI_STATUS_DIF_UNKNOWN_ERROR;
532ef270ab1SKenneth D. Merry 			break;
533ef270ab1SKenneth D. Merry 		}
534ef270ab1SKenneth D. Merry 
535ef270ab1SKenneth D. Merry 		if (! ocs_scsi_dif_check_guard(dif_info, addrlen, addrlen_count, dif, is_crc)) {
536ef270ab1SKenneth D. Merry 			ocs_log_debug(ocs, "block guard check error, lba %" PRId64 "\n", scsi_dif_info.lba + i);
537ef270ab1SKenneth D. Merry 			scsi_status = OCS_SCSI_STATUS_DIF_GUARD_ERROR;
538ef270ab1SKenneth D. Merry 			break;
539ef270ab1SKenneth D. Merry 		}
540ef270ab1SKenneth D. Merry 		if (! ocs_scsi_dif_check_app_tag(ocs, dif_info, scsi_dif_info.app_tag, dif)) {
541ef270ab1SKenneth D. Merry 			ocs_log_debug(ocs, "app tag check error, lba %" PRId64 "\n", scsi_dif_info.lba + i);
542ef270ab1SKenneth D. Merry 			scsi_status = OCS_SCSI_STATUS_DIF_APP_TAG_ERROR;
543ef270ab1SKenneth D. Merry 			break;
544ef270ab1SKenneth D. Merry 		}
545ef270ab1SKenneth D. Merry 		if (! ocs_scsi_dif_check_ref_tag(ocs, dif_info, (scsi_dif_info.ref_tag + i), dif)) {
546ef270ab1SKenneth D. Merry 			ocs_log_debug(ocs, "ref tag check error, lba %" PRId64 "\n", scsi_dif_info.lba + i);
547ef270ab1SKenneth D. Merry 			scsi_status = OCS_SCSI_STATUS_DIF_REF_TAG_ERROR;
548ef270ab1SKenneth D. Merry 			break;
549ef270ab1SKenneth D. Merry 		}
550ef270ab1SKenneth D. Merry 	}
551ef270ab1SKenneth D. Merry 	return scsi_status;
552ef270ab1SKenneth D. Merry }
553ef270ab1SKenneth D. Merry 
554ef270ab1SKenneth D. Merry /**
555ef270ab1SKenneth D. Merry  * @brief Check the block guard of block data
556ef270ab1SKenneth D. Merry  *
557ef270ab1SKenneth D. Merry  * @par Description
558ef270ab1SKenneth D. Merry  * Using the dif_info for the transfer, check the block guard value.
559ef270ab1SKenneth D. Merry  *
560ef270ab1SKenneth D. Merry  * @param dif_info Pointer to HW DIF info data.
561ef270ab1SKenneth D. Merry  * @param addrlen Array of address length pairs.
562ef270ab1SKenneth D. Merry  * @param addrlen_count Number of entries in the addrlen[] array.
563ef270ab1SKenneth D. Merry  * @param dif Pointer to the DIF data block being checked.
564ef270ab1SKenneth D. Merry  * @param is_crc True if guard is using CRC format.
565ef270ab1SKenneth D. Merry  *
566ef270ab1SKenneth D. Merry  * @return Returns TRUE if block guard check is ok.
567ef270ab1SKenneth D. Merry  */
568ef270ab1SKenneth D. Merry static uint32_t
569ef270ab1SKenneth D. Merry ocs_scsi_dif_check_guard(ocs_hw_dif_info_t *dif_info, ocs_scsi_vaddr_len_t addrlen[], uint32_t addrlen_count,
570ef270ab1SKenneth D. Merry 	ocs_dif_t *dif, int is_crc)
571ef270ab1SKenneth D. Merry {
572ef270ab1SKenneth D. Merry 	uint16_t crc = dif_info->dif_seed;
573ef270ab1SKenneth D. Merry 	uint32_t i;
574ef270ab1SKenneth D. Merry 	uint16_t checksum;
575ef270ab1SKenneth D. Merry 
576ef270ab1SKenneth D. Merry 	if ((dif == NULL)  || !dif_info->check_guard) {
577ef270ab1SKenneth D. Merry 		return TRUE;
578ef270ab1SKenneth D. Merry 	}
579ef270ab1SKenneth D. Merry 
580ef270ab1SKenneth D. Merry 	if (is_crc) {
581ef270ab1SKenneth D. Merry 		for (i = 0; i < addrlen_count; i++) {
582ef270ab1SKenneth D. Merry 			crc = ocs_scsi_dif_calc_crc(addrlen[i].vaddr, addrlen[i].length, crc);
583ef270ab1SKenneth D. Merry 		}
584ef270ab1SKenneth D. Merry 		return (crc == ocs_be16toh(dif->crc));
585ef270ab1SKenneth D. Merry 	} else {
586ef270ab1SKenneth D. Merry 		checksum = ocs_scsi_dif_calc_checksum(addrlen, addrlen_count);
587ef270ab1SKenneth D. Merry 
588ef270ab1SKenneth D. Merry 		return (checksum == dif->crc);
589ef270ab1SKenneth D. Merry 	}
590ef270ab1SKenneth D. Merry }
591ef270ab1SKenneth D. Merry 
592ef270ab1SKenneth D. Merry /**
593ef270ab1SKenneth D. Merry  * @brief Check the app tag of dif data
594ef270ab1SKenneth D. Merry  *
595ef270ab1SKenneth D. Merry  * @par Description
596ef270ab1SKenneth D. Merry  * Using the dif_info for the transfer, check the app tag.
597ef270ab1SKenneth D. Merry  *
598ef270ab1SKenneth D. Merry  * @param ocs Pointer to the ocs structure for logging.
599ef270ab1SKenneth D. Merry  * @param dif_info Pointer to HW DIF info data.
600ef270ab1SKenneth D. Merry  * @param exp_app_tag The value the app tag is expected to be.
601ef270ab1SKenneth D. Merry  * @param dif Pointer to the DIF data block being checked.
602ef270ab1SKenneth D. Merry  *
603ef270ab1SKenneth D. Merry  * @return Returns TRUE if app tag check is ok.
604ef270ab1SKenneth D. Merry  */
605ef270ab1SKenneth D. Merry static uint32_t
606ef270ab1SKenneth D. Merry ocs_scsi_dif_check_app_tag(ocs_t *ocs, ocs_hw_dif_info_t *dif_info, uint16_t exp_app_tag, ocs_dif_t *dif)
607ef270ab1SKenneth D. Merry {
608ef270ab1SKenneth D. Merry 	if ((dif == NULL)  || !dif_info->check_app_tag) {
609ef270ab1SKenneth D. Merry 		return TRUE;
610ef270ab1SKenneth D. Merry 	}
611ef270ab1SKenneth D. Merry 
612ef270ab1SKenneth D. Merry 	ocs_log_debug(ocs, "expected app tag 0x%x,  actual 0x%x\n",
613ef270ab1SKenneth D. Merry 		exp_app_tag, ocs_be16toh(dif->app_tag));
614ef270ab1SKenneth D. Merry 
615ef270ab1SKenneth D. Merry 	return (exp_app_tag == ocs_be16toh(dif->app_tag));
616ef270ab1SKenneth D. Merry }
617ef270ab1SKenneth D. Merry 
618ef270ab1SKenneth D. Merry /**
619ef270ab1SKenneth D. Merry  * @brief Check the ref tag of dif data
620ef270ab1SKenneth D. Merry  *
621ef270ab1SKenneth D. Merry  * @par Description
622ef270ab1SKenneth D. Merry  * Using the dif_info for the transfer, check the app tag.
623ef270ab1SKenneth D. Merry  *
624ef270ab1SKenneth D. Merry  * @param ocs Pointer to the ocs structure for logging.
625ef270ab1SKenneth D. Merry  * @param dif_info Pointer to HW DIF info data.
626ef270ab1SKenneth D. Merry  * @param exp_ref_tag The value the ref tag is expected to be.
627ef270ab1SKenneth D. Merry  * @param dif Pointer to the DIF data block being checked.
628ef270ab1SKenneth D. Merry  *
629ef270ab1SKenneth D. Merry  * @return Returns TRUE if ref tag check is ok.
630ef270ab1SKenneth D. Merry  */
631ef270ab1SKenneth D. Merry static uint32_t
632ef270ab1SKenneth D. Merry ocs_scsi_dif_check_ref_tag(ocs_t *ocs, ocs_hw_dif_info_t *dif_info, uint32_t exp_ref_tag, ocs_dif_t *dif)
633ef270ab1SKenneth D. Merry {
634ef270ab1SKenneth D. Merry 	if ((dif == NULL)  || !dif_info->check_ref_tag) {
635ef270ab1SKenneth D. Merry 		return TRUE;
636ef270ab1SKenneth D. Merry 	}
637ef270ab1SKenneth D. Merry 
638ef270ab1SKenneth D. Merry 	if (exp_ref_tag != ocs_be32toh(dif->ref_tag)) {
639ef270ab1SKenneth D. Merry 		ocs_log_debug(ocs, "expected ref tag 0x%x, actual 0x%x\n",
640ef270ab1SKenneth D. Merry 			exp_ref_tag, ocs_be32toh(dif->ref_tag));
641ef270ab1SKenneth D. Merry 		return FALSE;
642ef270ab1SKenneth D. Merry 	} else {
643ef270ab1SKenneth D. Merry 		return TRUE;
644ef270ab1SKenneth D. Merry 	}
645ef270ab1SKenneth D. Merry }
646ef270ab1SKenneth D. Merry 
647ef270ab1SKenneth D. Merry /**
648ef270ab1SKenneth D. Merry  * @brief Return count of SGE's required for request
649ef270ab1SKenneth D. Merry  *
650ef270ab1SKenneth D. Merry  * @par Description
651ef270ab1SKenneth D. Merry  * An accurate count of SGEs is computed and returned.
652ef270ab1SKenneth D. Merry  *
653ef270ab1SKenneth D. Merry  * @param hw_dif Pointer to HW dif information.
654ef270ab1SKenneth D. Merry  * @param sgl Pointer to SGL from back end.
655ef270ab1SKenneth D. Merry  * @param sgl_count Count of SGEs in SGL.
656ef270ab1SKenneth D. Merry  *
657ef270ab1SKenneth D. Merry  * @return Count of SGEs.
658ef270ab1SKenneth D. Merry  */
659ef270ab1SKenneth D. Merry static uint32_t
660ef270ab1SKenneth D. Merry ocs_scsi_count_sgls(ocs_hw_dif_info_t *hw_dif, ocs_scsi_sgl_t *sgl, uint32_t sgl_count)
661ef270ab1SKenneth D. Merry {
662ef270ab1SKenneth D. Merry 	uint32_t count = 0;
663ef270ab1SKenneth D. Merry 	uint32_t i;
664ef270ab1SKenneth D. Merry 
665ef270ab1SKenneth D. Merry 	/* Convert DIF Information */
666ef270ab1SKenneth D. Merry 	if (hw_dif->dif_oper != OCS_HW_DIF_OPER_DISABLED) {
667ef270ab1SKenneth D. Merry 		/* If we're not DIF separate, then emit a seed SGE */
668ef270ab1SKenneth D. Merry 		if (!hw_dif->dif_separate) {
669ef270ab1SKenneth D. Merry 			count++;
670ef270ab1SKenneth D. Merry 		}
671ef270ab1SKenneth D. Merry 
672ef270ab1SKenneth D. Merry 		for (i = 0; i < sgl_count; i++) {
673ef270ab1SKenneth D. Merry 			/* If DIF is enabled, and DIF is separate, then append a SEED then DIF SGE */
674ef270ab1SKenneth D. Merry 			if (hw_dif->dif_separate) {
675ef270ab1SKenneth D. Merry 				count += 2;
676ef270ab1SKenneth D. Merry 			}
677ef270ab1SKenneth D. Merry 
678ef270ab1SKenneth D. Merry 			count++;
679ef270ab1SKenneth D. Merry 		}
680ef270ab1SKenneth D. Merry 	} else {
681ef270ab1SKenneth D. Merry 		count = sgl_count;
682ef270ab1SKenneth D. Merry 	}
683ef270ab1SKenneth D. Merry 	return count;
684ef270ab1SKenneth D. Merry }
685ef270ab1SKenneth D. Merry 
686ef270ab1SKenneth D. Merry static int32_t
687ef270ab1SKenneth D. Merry ocs_scsi_build_sgls(ocs_hw_t *hw, ocs_hw_io_t *hio, ocs_hw_dif_info_t *hw_dif, ocs_scsi_sgl_t *sgl, uint32_t sgl_count, ocs_hw_io_type_e type)
688ef270ab1SKenneth D. Merry {
689ef270ab1SKenneth D. Merry 	int32_t rc;
690ef270ab1SKenneth D. Merry 	uint32_t i;
691ef270ab1SKenneth D. Merry 	ocs_t *ocs = hw->os;
692ef270ab1SKenneth D. Merry 	uint32_t blocksize = 0;
693ef270ab1SKenneth D. Merry 	uint32_t blockcount;
694ef270ab1SKenneth D. Merry 
695ef270ab1SKenneth D. Merry 	ocs_assert(hio, -1);
696ef270ab1SKenneth D. Merry 
697ef270ab1SKenneth D. Merry 	/* Initialize HW SGL */
698ef270ab1SKenneth D. Merry 	rc = ocs_hw_io_init_sges(hw, hio, type);
699ef270ab1SKenneth D. Merry 	if (rc) {
700ef270ab1SKenneth D. Merry 		ocs_log_err(ocs, "ocs_hw_io_init_sges failed: %d\n", rc);
701ef270ab1SKenneth D. Merry 		return -1;
702ef270ab1SKenneth D. Merry 	}
703ef270ab1SKenneth D. Merry 
704ef270ab1SKenneth D. Merry 	/* Convert DIF Information */
705ef270ab1SKenneth D. Merry 	if (hw_dif->dif_oper != OCS_HW_DIF_OPER_DISABLED) {
706ef270ab1SKenneth D. Merry 		/* If we're not DIF separate, then emit a seed SGE */
707ef270ab1SKenneth D. Merry 		if (!hw_dif->dif_separate) {
708ef270ab1SKenneth D. Merry 			rc = ocs_hw_io_add_seed_sge(hw, hio, hw_dif);
709ef270ab1SKenneth D. Merry 			if (rc) {
710ef270ab1SKenneth D. Merry 				return rc;
711ef270ab1SKenneth D. Merry 			}
712ef270ab1SKenneth D. Merry 		}
713ef270ab1SKenneth D. Merry 
714ef270ab1SKenneth D. Merry 		/* if we are doing DIF separate, then figure out the block size so that we
715ef270ab1SKenneth D. Merry 		 * can update the ref tag in the DIF seed SGE.   Also verify that the
716ef270ab1SKenneth D. Merry 		 * the sgl lengths are all multiples of the blocksize
717ef270ab1SKenneth D. Merry 		 */
718ef270ab1SKenneth D. Merry 		if (hw_dif->dif_separate) {
719ef270ab1SKenneth D. Merry 			switch(hw_dif->blk_size) {
720ef270ab1SKenneth D. Merry 			case OCS_HW_DIF_BK_SIZE_512:	blocksize = 512; break;
721ef270ab1SKenneth D. Merry 			case OCS_HW_DIF_BK_SIZE_1024:	blocksize = 1024; break;
722ef270ab1SKenneth D. Merry 			case OCS_HW_DIF_BK_SIZE_2048:	blocksize = 2048; break;
723ef270ab1SKenneth D. Merry 			case OCS_HW_DIF_BK_SIZE_4096:	blocksize = 4096; break;
724ef270ab1SKenneth D. Merry 			case OCS_HW_DIF_BK_SIZE_520:	blocksize = 520; break;
725ef270ab1SKenneth D. Merry 			case OCS_HW_DIF_BK_SIZE_4104:	blocksize = 4104; break;
726ef270ab1SKenneth D. Merry 			default:
727ef270ab1SKenneth D. Merry 				ocs_log_test(hw->os, "Inavlid hw_dif blocksize %d\n", hw_dif->blk_size);
728ef270ab1SKenneth D. Merry 				return -1;
729ef270ab1SKenneth D. Merry 			}
730ef270ab1SKenneth D. Merry 			for (i = 0; i < sgl_count; i++) {
731ef270ab1SKenneth D. Merry 				if ((sgl[i].len % blocksize) != 0) {
732ef270ab1SKenneth D. Merry 					ocs_log_test(hw->os, "sgl[%d] len of %ld is not multiple of blocksize\n",
733ef270ab1SKenneth D. Merry 						     i, sgl[i].len);
734ef270ab1SKenneth D. Merry 					return -1;
735ef270ab1SKenneth D. Merry 				}
736ef270ab1SKenneth D. Merry 			}
737ef270ab1SKenneth D. Merry 		}
738ef270ab1SKenneth D. Merry 
739ef270ab1SKenneth D. Merry 		for (i = 0; i < sgl_count; i++) {
740ef270ab1SKenneth D. Merry 			ocs_assert(sgl[i].addr, -1);
741ef270ab1SKenneth D. Merry 			ocs_assert(sgl[i].len, -1);
742ef270ab1SKenneth D. Merry 
743ef270ab1SKenneth D. Merry 			/* If DIF is enabled, and DIF is separate, then append a SEED then DIF SGE */
744ef270ab1SKenneth D. Merry 			if (hw_dif->dif_separate) {
745ef270ab1SKenneth D. Merry 				rc = ocs_hw_io_add_seed_sge(hw, hio, hw_dif);
746ef270ab1SKenneth D. Merry 				if (rc) {
747ef270ab1SKenneth D. Merry 					return rc;
748ef270ab1SKenneth D. Merry 				}
749ef270ab1SKenneth D. Merry 				rc = ocs_hw_io_add_dif_sge(hw, hio, sgl[i].dif_addr);
750ef270ab1SKenneth D. Merry 				if (rc) {
751ef270ab1SKenneth D. Merry 					return rc;
752ef270ab1SKenneth D. Merry 				}
753ef270ab1SKenneth D. Merry 				/* Update the ref_tag for the next DIF seed SGE */
754ef270ab1SKenneth D. Merry 				blockcount = sgl[i].len / blocksize;
755ef270ab1SKenneth D. Merry 				if (hw_dif->dif_oper == OCS_HW_DIF_OPER_INSERT) {
756ef270ab1SKenneth D. Merry 					hw_dif->ref_tag_repl += blockcount;
757ef270ab1SKenneth D. Merry 				} else {
758ef270ab1SKenneth D. Merry 					hw_dif->ref_tag_cmp += blockcount;
759ef270ab1SKenneth D. Merry 				}
760ef270ab1SKenneth D. Merry 			}
761ef270ab1SKenneth D. Merry 
762ef270ab1SKenneth D. Merry 			/* Add data SGE */
763ef270ab1SKenneth D. Merry 			rc = ocs_hw_io_add_sge(hw, hio, sgl[i].addr, sgl[i].len);
764ef270ab1SKenneth D. Merry 			if (rc) {
765ef270ab1SKenneth D. Merry 				ocs_log_err(ocs, "ocs_hw_io_add_sge failed: count=%d rc=%d\n",
766ef270ab1SKenneth D. Merry 						sgl_count, rc);
767ef270ab1SKenneth D. Merry 				return rc;
768ef270ab1SKenneth D. Merry 			}
769ef270ab1SKenneth D. Merry 		}
770ef270ab1SKenneth D. Merry 	} else {
771ef270ab1SKenneth D. Merry 		for (i = 0; i < sgl_count; i++) {
772ef270ab1SKenneth D. Merry 			ocs_assert(sgl[i].addr, -1);
773ef270ab1SKenneth D. Merry 			ocs_assert(sgl[i].len, -1);
774ef270ab1SKenneth D. Merry 
775ef270ab1SKenneth D. Merry 			/* Add data SGE */
776ef270ab1SKenneth D. Merry 			rc = ocs_hw_io_add_sge(hw, hio, sgl[i].addr, sgl[i].len);
777ef270ab1SKenneth D. Merry 			if (rc) {
778ef270ab1SKenneth D. Merry 				ocs_log_err(ocs, "ocs_hw_io_add_sge failed: count=%d rc=%d\n",
779ef270ab1SKenneth D. Merry 						sgl_count, rc);
780ef270ab1SKenneth D. Merry 				return rc;
781ef270ab1SKenneth D. Merry 			}
782ef270ab1SKenneth D. Merry 		}
783ef270ab1SKenneth D. Merry 	}
784ef270ab1SKenneth D. Merry 	return 0;
785ef270ab1SKenneth D. Merry }
786ef270ab1SKenneth D. Merry 
787ef270ab1SKenneth D. Merry /**
788ef270ab1SKenneth D. Merry  * @ingroup scsi_api_base
789ef270ab1SKenneth D. Merry  * @brief Convert SCSI API T10 DIF information into the FC HW format.
790ef270ab1SKenneth D. Merry  *
791ef270ab1SKenneth D. Merry  * @param ocs Pointer to the ocs structure for logging.
792ef270ab1SKenneth D. Merry  * @param scsi_dif_info Pointer to the SCSI API T10 DIF fields.
793ef270ab1SKenneth D. Merry  * @param hw_dif_info Pointer to the FC HW API T10 DIF fields.
794ef270ab1SKenneth D. Merry  *
795ef270ab1SKenneth D. Merry  * @return Returns 0 on success, or a negative error code value on failure.
796ef270ab1SKenneth D. Merry  */
797ef270ab1SKenneth D. Merry 
798ef270ab1SKenneth D. Merry static int32_t
799ef270ab1SKenneth D. Merry ocs_scsi_convert_dif_info(ocs_t *ocs, ocs_scsi_dif_info_t *scsi_dif_info, ocs_hw_dif_info_t *hw_dif_info)
800ef270ab1SKenneth D. Merry {
801ef270ab1SKenneth D. Merry 	uint32_t dif_seed;
802ef270ab1SKenneth D. Merry 	ocs_memset(hw_dif_info, 0, sizeof(ocs_hw_dif_info_t));
803ef270ab1SKenneth D. Merry 
804ef270ab1SKenneth D. Merry 	if (scsi_dif_info == NULL) {
805ef270ab1SKenneth D. Merry 		hw_dif_info->dif_oper = OCS_HW_DIF_OPER_DISABLED;
806ef270ab1SKenneth D. Merry 		hw_dif_info->blk_size =  OCS_HW_DIF_BK_SIZE_NA;
807ef270ab1SKenneth D. Merry 		return 0;
808ef270ab1SKenneth D. Merry 	}
809ef270ab1SKenneth D. Merry 
810ef270ab1SKenneth D. Merry 	/* Convert the DIF operation */
811ef270ab1SKenneth D. Merry 	switch(scsi_dif_info->dif_oper) {
812ef270ab1SKenneth D. Merry 	case OCS_SCSI_DIF_OPER_IN_NODIF_OUT_CRC:
813ef270ab1SKenneth D. Merry 		hw_dif_info->dif_oper = OCS_HW_SGE_DIF_OP_IN_NODIF_OUT_CRC;
814ef270ab1SKenneth D. Merry 		hw_dif_info->dif = SLI4_DIF_INSERT;
815ef270ab1SKenneth D. Merry 		break;
816ef270ab1SKenneth D. Merry 	case OCS_SCSI_DIF_OPER_IN_CRC_OUT_NODIF:
817ef270ab1SKenneth D. Merry 		hw_dif_info->dif_oper = OCS_HW_SGE_DIF_OP_IN_CRC_OUT_NODIF;
818ef270ab1SKenneth D. Merry 		hw_dif_info->dif = SLI4_DIF_STRIP;
819ef270ab1SKenneth D. Merry 		break;
820ef270ab1SKenneth D. Merry 	case OCS_SCSI_DIF_OPER_IN_NODIF_OUT_CHKSUM:
821ef270ab1SKenneth D. Merry 		hw_dif_info->dif_oper = OCS_HW_SGE_DIF_OP_IN_NODIF_OUT_CHKSUM;
822ef270ab1SKenneth D. Merry 		hw_dif_info->dif = SLI4_DIF_INSERT;
823ef270ab1SKenneth D. Merry 		break;
824ef270ab1SKenneth D. Merry 	case OCS_SCSI_DIF_OPER_IN_CHKSUM_OUT_NODIF:
825ef270ab1SKenneth D. Merry 		hw_dif_info->dif_oper = OCS_HW_SGE_DIF_OP_IN_CHKSUM_OUT_NODIF;
826ef270ab1SKenneth D. Merry 		hw_dif_info->dif = SLI4_DIF_STRIP;
827ef270ab1SKenneth D. Merry 		break;
828ef270ab1SKenneth D. Merry 	case OCS_SCSI_DIF_OPER_IN_CRC_OUT_CRC:
829ef270ab1SKenneth D. Merry 		hw_dif_info->dif_oper = OCS_HW_SGE_DIF_OP_IN_CRC_OUT_CRC;
830ef270ab1SKenneth D. Merry 		hw_dif_info->dif = SLI4_DIF_PASS_THROUGH;
831ef270ab1SKenneth D. Merry 		break;
832ef270ab1SKenneth D. Merry 	case OCS_SCSI_DIF_OPER_IN_CHKSUM_OUT_CHKSUM:
833ef270ab1SKenneth D. Merry 		hw_dif_info->dif_oper = OCS_HW_SGE_DIF_OP_IN_CHKSUM_OUT_CHKSUM;
834ef270ab1SKenneth D. Merry 		hw_dif_info->dif = SLI4_DIF_PASS_THROUGH;
835ef270ab1SKenneth D. Merry 		break;
836ef270ab1SKenneth D. Merry 	case OCS_SCSI_DIF_OPER_IN_CRC_OUT_CHKSUM:
837ef270ab1SKenneth D. Merry 		hw_dif_info->dif_oper = OCS_HW_SGE_DIF_OP_IN_CRC_OUT_CHKSUM;
838ef270ab1SKenneth D. Merry 		hw_dif_info->dif = SLI4_DIF_PASS_THROUGH;
839ef270ab1SKenneth D. Merry 		break;
840ef270ab1SKenneth D. Merry 	case OCS_SCSI_DIF_OPER_IN_CHKSUM_OUT_CRC:
841ef270ab1SKenneth D. Merry 		hw_dif_info->dif_oper = OCS_HW_SGE_DIF_OP_IN_CHKSUM_OUT_CRC;
842ef270ab1SKenneth D. Merry 		hw_dif_info->dif = SLI4_DIF_PASS_THROUGH;
843ef270ab1SKenneth D. Merry 		break;
844ef270ab1SKenneth D. Merry 	case OCS_SCSI_DIF_OPER_IN_RAW_OUT_RAW:
845ef270ab1SKenneth D. Merry 		hw_dif_info->dif_oper = OCS_HW_SGE_DIF_OP_IN_RAW_OUT_RAW;
846ef270ab1SKenneth D. Merry 		hw_dif_info->dif = SLI4_DIF_PASS_THROUGH;
847ef270ab1SKenneth D. Merry 		break;
848ef270ab1SKenneth D. Merry 	default:
849ef270ab1SKenneth D. Merry 		ocs_log_test(ocs, "unhandled SCSI DIF operation %d\n",
850ef270ab1SKenneth D. Merry 			     scsi_dif_info->dif_oper);
851ef270ab1SKenneth D. Merry 		return -1;
852ef270ab1SKenneth D. Merry 	}
853ef270ab1SKenneth D. Merry 
854ef270ab1SKenneth D. Merry 	switch(scsi_dif_info->blk_size) {
855ef270ab1SKenneth D. Merry 	case OCS_SCSI_DIF_BK_SIZE_512:
856ef270ab1SKenneth D. Merry 		hw_dif_info->blk_size = OCS_HW_DIF_BK_SIZE_512;
857ef270ab1SKenneth D. Merry 		break;
858ef270ab1SKenneth D. Merry 	case OCS_SCSI_DIF_BK_SIZE_1024:
859ef270ab1SKenneth D. Merry 		hw_dif_info->blk_size = OCS_HW_DIF_BK_SIZE_1024;
860ef270ab1SKenneth D. Merry 		break;
861ef270ab1SKenneth D. Merry 	case OCS_SCSI_DIF_BK_SIZE_2048:
862ef270ab1SKenneth D. Merry 		hw_dif_info->blk_size = OCS_HW_DIF_BK_SIZE_2048;
863ef270ab1SKenneth D. Merry 		break;
864ef270ab1SKenneth D. Merry 	case OCS_SCSI_DIF_BK_SIZE_4096:
865ef270ab1SKenneth D. Merry 		hw_dif_info->blk_size = OCS_HW_DIF_BK_SIZE_4096;
866ef270ab1SKenneth D. Merry 		break;
867ef270ab1SKenneth D. Merry 	case OCS_SCSI_DIF_BK_SIZE_520:
868ef270ab1SKenneth D. Merry 		hw_dif_info->blk_size = OCS_HW_DIF_BK_SIZE_520;
869ef270ab1SKenneth D. Merry 		break;
870ef270ab1SKenneth D. Merry 	case OCS_SCSI_DIF_BK_SIZE_4104:
871ef270ab1SKenneth D. Merry 		hw_dif_info->blk_size = OCS_HW_DIF_BK_SIZE_4104;
872ef270ab1SKenneth D. Merry 		break;
873ef270ab1SKenneth D. Merry 	default:
874ef270ab1SKenneth D. Merry 		ocs_log_test(ocs, "unhandled SCSI DIF block size %d\n",
875ef270ab1SKenneth D. Merry 			     scsi_dif_info->blk_size);
876ef270ab1SKenneth D. Merry 		return -1;
877ef270ab1SKenneth D. Merry 	}
878ef270ab1SKenneth D. Merry 
879ef270ab1SKenneth D. Merry 	/* If the operation is an INSERT the tags provided are the ones that should be
880ef270ab1SKenneth D. Merry 	 * inserted, otherwise they're the ones to be checked against. */
881ef270ab1SKenneth D. Merry 	if (hw_dif_info->dif == SLI4_DIF_INSERT ) {
882ef270ab1SKenneth D. Merry 		hw_dif_info->ref_tag_repl = scsi_dif_info->ref_tag;
883ef270ab1SKenneth D. Merry 		hw_dif_info->app_tag_repl = scsi_dif_info->app_tag;
884ef270ab1SKenneth D. Merry 	} else {
885ef270ab1SKenneth D. Merry 		hw_dif_info->ref_tag_cmp = scsi_dif_info->ref_tag;
886ef270ab1SKenneth D. Merry 		hw_dif_info->app_tag_cmp = scsi_dif_info->app_tag;
887ef270ab1SKenneth D. Merry 	}
888ef270ab1SKenneth D. Merry 
889ef270ab1SKenneth D. Merry 	hw_dif_info->check_ref_tag = scsi_dif_info->check_ref_tag;
890ef270ab1SKenneth D. Merry 	hw_dif_info->check_app_tag = scsi_dif_info->check_app_tag;
891ef270ab1SKenneth D. Merry 	hw_dif_info->check_guard = scsi_dif_info->check_guard;
892ef270ab1SKenneth D. Merry 	hw_dif_info->auto_incr_ref_tag = 1;
893ef270ab1SKenneth D. Merry 	hw_dif_info->dif_separate = scsi_dif_info->dif_separate;
894ef270ab1SKenneth D. Merry 	hw_dif_info->disable_app_ffff = scsi_dif_info->disable_app_ffff;
895ef270ab1SKenneth D. Merry 	hw_dif_info->disable_app_ref_ffff = scsi_dif_info->disable_app_ref_ffff;
896ef270ab1SKenneth D. Merry 
897ef270ab1SKenneth D. Merry 	ocs_hw_get(&ocs->hw, OCS_HW_DIF_SEED, &dif_seed);
898ef270ab1SKenneth D. Merry 	hw_dif_info->dif_seed = dif_seed;
899ef270ab1SKenneth D. Merry 
900ef270ab1SKenneth D. Merry 	return 0;
901ef270ab1SKenneth D. Merry }
902ef270ab1SKenneth D. Merry 
903ef270ab1SKenneth D. Merry /**
904ef270ab1SKenneth D. Merry  * @ingroup scsi_api_base
905ef270ab1SKenneth D. Merry  * @brief This function logs the SGLs for an IO.
906ef270ab1SKenneth D. Merry  *
907ef270ab1SKenneth D. Merry  * @param io Pointer to the IO context.
908ef270ab1SKenneth D. Merry  */
909ef270ab1SKenneth D. Merry static void ocs_log_sgl(ocs_io_t *io)
910ef270ab1SKenneth D. Merry {
911ef270ab1SKenneth D. Merry 	ocs_hw_io_t *hio = io->hio;
912ef270ab1SKenneth D. Merry 	sli4_sge_t *data = NULL;
913ef270ab1SKenneth D. Merry 	uint32_t *dword = NULL;
914ef270ab1SKenneth D. Merry 	uint32_t i;
915ef270ab1SKenneth D. Merry 	uint32_t n_sge;
916ef270ab1SKenneth D. Merry 
917ef270ab1SKenneth D. Merry 	scsi_io_trace(io, "def_sgl at 0x%x 0x%08x\n",
918ef270ab1SKenneth D. Merry 		      ocs_addr32_hi(hio->def_sgl.phys),
919ef270ab1SKenneth D. Merry 		      ocs_addr32_lo(hio->def_sgl.phys));
920ef270ab1SKenneth D. Merry 	n_sge = (hio->sgl == &hio->def_sgl ? hio->n_sge : hio->def_sgl_count);
921ef270ab1SKenneth D. Merry 	for (i = 0, data = hio->def_sgl.virt; i < n_sge; i++, data++) {
922ef270ab1SKenneth D. Merry 		dword = (uint32_t*)data;
923ef270ab1SKenneth D. Merry 
924ef270ab1SKenneth D. Merry 		scsi_io_trace(io, "SGL %2d 0x%08x 0x%08x 0x%08x 0x%08x\n",
925ef270ab1SKenneth D. Merry 			 i, dword[0], dword[1], dword[2], dword[3]);
926ef270ab1SKenneth D. Merry 
927ef270ab1SKenneth D. Merry 		if (dword[2] & (1U << 31)) {
928ef270ab1SKenneth D. Merry 			break;
929ef270ab1SKenneth D. Merry 		}
930ef270ab1SKenneth D. Merry 	}
931ef270ab1SKenneth D. Merry 
932ef270ab1SKenneth D. Merry 	if (hio->ovfl_sgl != NULL &&
933ef270ab1SKenneth D. Merry 		hio->sgl == hio->ovfl_sgl) {
934ef270ab1SKenneth D. Merry 		scsi_io_trace(io, "Overflow at 0x%x 0x%08x\n",
935ef270ab1SKenneth D. Merry 			      ocs_addr32_hi(hio->ovfl_sgl->phys),
936ef270ab1SKenneth D. Merry 			      ocs_addr32_lo(hio->ovfl_sgl->phys));
937ef270ab1SKenneth D. Merry 		for (i = 0, data = hio->ovfl_sgl->virt; i < hio->n_sge; i++, data++) {
938ef270ab1SKenneth D. Merry 			dword = (uint32_t*)data;
939ef270ab1SKenneth D. Merry 
940ef270ab1SKenneth D. Merry 			scsi_io_trace(io, "SGL %2d 0x%08x 0x%08x 0x%08x 0x%08x\n",
941ef270ab1SKenneth D. Merry 				 i, dword[0], dword[1], dword[2], dword[3]);
942ef270ab1SKenneth D. Merry 			if (dword[2] & (1U << 31)) {
943ef270ab1SKenneth D. Merry 				break;
944ef270ab1SKenneth D. Merry 			}
945ef270ab1SKenneth D. Merry 		}
946ef270ab1SKenneth D. Merry 	}
947ef270ab1SKenneth D. Merry 
948ef270ab1SKenneth D. Merry }
949ef270ab1SKenneth D. Merry 
950ef270ab1SKenneth D. Merry /**
951ef270ab1SKenneth D. Merry  * @brief Check pending error asynchronous callback function.
952ef270ab1SKenneth D. Merry  *
953ef270ab1SKenneth D. Merry  * @par Description
954ef270ab1SKenneth D. Merry  * Invoke the HW callback function for a given IO. This function is called
955ef270ab1SKenneth D. Merry  * from the NOP mailbox completion context.
956ef270ab1SKenneth D. Merry  *
957ef270ab1SKenneth D. Merry  * @param hw Pointer to HW object.
958ef270ab1SKenneth D. Merry  * @param status Completion status.
959ef270ab1SKenneth D. Merry  * @param mqe Mailbox completion queue entry.
960ef270ab1SKenneth D. Merry  * @param arg General purpose argument.
961ef270ab1SKenneth D. Merry  *
962ef270ab1SKenneth D. Merry  * @return Returns 0.
963ef270ab1SKenneth D. Merry  */
964ef270ab1SKenneth D. Merry static int32_t
965ef270ab1SKenneth D. Merry ocs_scsi_check_pending_async_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
966ef270ab1SKenneth D. Merry {
967ef270ab1SKenneth D. Merry 	ocs_io_t *io = arg;
968ef270ab1SKenneth D. Merry 
969ef270ab1SKenneth D. Merry 	if (io != NULL) {
970ef270ab1SKenneth D. Merry 		if (io->hw_cb != NULL) {
971ef270ab1SKenneth D. Merry 			ocs_hw_done_t cb = io->hw_cb;
972ef270ab1SKenneth D. Merry 
973ef270ab1SKenneth D. Merry 			io->hw_cb = NULL;
974ef270ab1SKenneth D. Merry 			cb(io->hio, NULL, 0, SLI4_FC_WCQE_STATUS_DISPATCH_ERROR, 0, io);
975ef270ab1SKenneth D. Merry 		}
976ef270ab1SKenneth D. Merry 	}
977ef270ab1SKenneth D. Merry 	return 0;
978ef270ab1SKenneth D. Merry }
979ef270ab1SKenneth D. Merry 
980ef270ab1SKenneth D. Merry /**
981ef270ab1SKenneth D. Merry  * @brief Check for pending IOs to dispatch.
982ef270ab1SKenneth D. Merry  *
983ef270ab1SKenneth D. Merry  * @par Description
984ef270ab1SKenneth D. Merry  * If there are IOs on the pending list, and a HW IO is available, then
985ef270ab1SKenneth D. Merry  * dispatch the IOs.
986ef270ab1SKenneth D. Merry  *
987ef270ab1SKenneth D. Merry  * @param ocs Pointer to the OCS structure.
988ef270ab1SKenneth D. Merry  *
989ef270ab1SKenneth D. Merry  * @return None.
990ef270ab1SKenneth D. Merry  */
991ef270ab1SKenneth D. Merry 
992ef270ab1SKenneth D. Merry void
993ef270ab1SKenneth D. Merry ocs_scsi_check_pending(ocs_t *ocs)
994ef270ab1SKenneth D. Merry {
995ef270ab1SKenneth D. Merry 	ocs_xport_t *xport = ocs->xport;
996ef270ab1SKenneth D. Merry 	ocs_io_t *io;
997ef270ab1SKenneth D. Merry 	ocs_hw_io_t *hio;
998ef270ab1SKenneth D. Merry 	int32_t status;
999ef270ab1SKenneth D. Merry 	int count = 0;
1000ef270ab1SKenneth D. Merry 	int dispatch;
1001ef270ab1SKenneth D. Merry 
1002ef270ab1SKenneth D. Merry 	/* Guard against recursion */
1003ef270ab1SKenneth D. Merry 	if (ocs_atomic_add_return(&xport->io_pending_recursing, 1)) {
1004ef270ab1SKenneth D. Merry 		/* This function is already running.  Decrement and return. */
1005ef270ab1SKenneth D. Merry 		ocs_atomic_sub_return(&xport->io_pending_recursing, 1);
1006ef270ab1SKenneth D. Merry 		return;
1007ef270ab1SKenneth D. Merry 	}
1008ef270ab1SKenneth D. Merry 
1009ef270ab1SKenneth D. Merry 	do {
1010ef270ab1SKenneth D. Merry 		ocs_lock(&xport->io_pending_lock);
1011ef270ab1SKenneth D. Merry 			status = 0;
1012ef270ab1SKenneth D. Merry 			hio = NULL;
1013ef270ab1SKenneth D. Merry 			io = ocs_list_remove_head(&xport->io_pending_list);
1014ef270ab1SKenneth D. Merry 			if (io != NULL) {
1015ef270ab1SKenneth D. Merry 				if (io->io_type == OCS_IO_TYPE_ABORT) {
1016ef270ab1SKenneth D. Merry 					hio = NULL;
1017ef270ab1SKenneth D. Merry 				} else {
1018ef270ab1SKenneth D. Merry 					hio = ocs_hw_io_alloc(&ocs->hw);
1019ef270ab1SKenneth D. Merry 					if (hio == NULL) {
1020ef270ab1SKenneth D. Merry 						/*
1021ef270ab1SKenneth D. Merry 						 * No HW IO available.
1022ef270ab1SKenneth D. Merry 						 * Put IO back on the front of pending list
1023ef270ab1SKenneth D. Merry 						 */
1024ef270ab1SKenneth D. Merry 						ocs_list_add_head(&xport->io_pending_list, io);
1025ef270ab1SKenneth D. Merry 						io = NULL;
1026ef270ab1SKenneth D. Merry 					} else {
1027ef270ab1SKenneth D. Merry 						hio->eq = io->hw_priv;
1028ef270ab1SKenneth D. Merry 					}
1029ef270ab1SKenneth D. Merry 				}
1030ef270ab1SKenneth D. Merry 			}
1031ef270ab1SKenneth D. Merry 		/* Must drop the lock before dispatching the IO */
1032ef270ab1SKenneth D. Merry 		ocs_unlock(&xport->io_pending_lock);
1033ef270ab1SKenneth D. Merry 
1034ef270ab1SKenneth D. Merry 		if (io != NULL) {
1035ef270ab1SKenneth D. Merry 			count++;
1036ef270ab1SKenneth D. Merry 
1037ef270ab1SKenneth D. Merry 			/*
1038ef270ab1SKenneth D. Merry 			 * We pulled an IO off the pending list,
1039ef270ab1SKenneth D. Merry 			 * and either got an HW IO or don't need one
1040ef270ab1SKenneth D. Merry 			 */
1041ef270ab1SKenneth D. Merry 			ocs_atomic_sub_return(&xport->io_pending_count, 1);
1042ef270ab1SKenneth D. Merry 			if (hio == NULL) {
1043ef270ab1SKenneth D. Merry 				status = ocs_scsi_io_dispatch_no_hw_io(io);
1044ef270ab1SKenneth D. Merry 			} else {
1045ef270ab1SKenneth D. Merry 				status = ocs_scsi_io_dispatch_hw_io(io, hio);
1046ef270ab1SKenneth D. Merry 			}
1047ef270ab1SKenneth D. Merry 			if (status) {
1048ef270ab1SKenneth D. Merry 				/*
1049ef270ab1SKenneth D. Merry 				 * Invoke the HW callback, but do so in the separate execution context,
1050ef270ab1SKenneth D. Merry 				 * provided by the NOP mailbox completion processing context by using
1051ef270ab1SKenneth D. Merry 				 * ocs_hw_async_call()
1052ef270ab1SKenneth D. Merry 				 */
1053ef270ab1SKenneth D. Merry 				if (ocs_hw_async_call(&ocs->hw, ocs_scsi_check_pending_async_cb, io)) {
1054ef270ab1SKenneth D. Merry 					ocs_log_test(ocs, "call to ocs_hw_async_call() failed\n");
1055ef270ab1SKenneth D. Merry 				}
1056ef270ab1SKenneth D. Merry 			}
1057ef270ab1SKenneth D. Merry 		}
1058ef270ab1SKenneth D. Merry 	} while (io != NULL);
1059ef270ab1SKenneth D. Merry 
1060ef270ab1SKenneth D. Merry 	/*
1061ef270ab1SKenneth D. Merry 	 * If nothing was removed from the list,
1062ef270ab1SKenneth D. Merry 	 * we might be in a case where we need to abort an
1063ef270ab1SKenneth D. Merry 	 * active IO and the abort is on the pending list.
1064ef270ab1SKenneth D. Merry 	 * Look for an abort we can dispatch.
1065ef270ab1SKenneth D. Merry 	 */
1066ef270ab1SKenneth D. Merry 	if (count == 0 ) {
1067ef270ab1SKenneth D. Merry 		dispatch = 0;
1068ef270ab1SKenneth D. Merry 
1069ef270ab1SKenneth D. Merry 		ocs_lock(&xport->io_pending_lock);
1070ef270ab1SKenneth D. Merry 			ocs_list_foreach(&xport->io_pending_list, io) {
1071ef270ab1SKenneth D. Merry 				if (io->io_type == OCS_IO_TYPE_ABORT) {
1072ef270ab1SKenneth D. Merry 					if (io->io_to_abort->hio != NULL) {
1073ef270ab1SKenneth D. Merry 						/* This IO has a HW IO, so it is active.  Dispatch the abort. */
1074ef270ab1SKenneth D. Merry 						dispatch = 1;
1075ef270ab1SKenneth D. Merry 					} else {
1076ef270ab1SKenneth D. Merry 						/* Leave this abort on the pending list and keep looking */
1077ef270ab1SKenneth D. Merry 						dispatch = 0;
1078ef270ab1SKenneth D. Merry 					}
1079ef270ab1SKenneth D. Merry 				}
1080ef270ab1SKenneth D. Merry 				if (dispatch) {
1081ef270ab1SKenneth D. Merry 					ocs_list_remove(&xport->io_pending_list, io);
1082ef270ab1SKenneth D. Merry 					ocs_atomic_sub_return(&xport->io_pending_count, 1);
1083ef270ab1SKenneth D. Merry 					break;
1084ef270ab1SKenneth D. Merry 				}
1085ef270ab1SKenneth D. Merry 			}
1086ef270ab1SKenneth D. Merry 		ocs_unlock(&xport->io_pending_lock);
1087ef270ab1SKenneth D. Merry 
1088ef270ab1SKenneth D. Merry 		if (dispatch) {
1089ef270ab1SKenneth D. Merry 			status = ocs_scsi_io_dispatch_no_hw_io(io);
1090ef270ab1SKenneth D. Merry 			if (status) {
1091ef270ab1SKenneth D. Merry 				if (ocs_hw_async_call(&ocs->hw, ocs_scsi_check_pending_async_cb, io)) {
1092ef270ab1SKenneth D. Merry 					ocs_log_test(ocs, "call to ocs_hw_async_call() failed\n");
1093ef270ab1SKenneth D. Merry 				}
1094ef270ab1SKenneth D. Merry 			}
1095ef270ab1SKenneth D. Merry 		}
1096ef270ab1SKenneth D. Merry 	}
1097ef270ab1SKenneth D. Merry 
1098ef270ab1SKenneth D. Merry 	ocs_atomic_sub_return(&xport->io_pending_recursing, 1);
1099ef270ab1SKenneth D. Merry 	return;
1100ef270ab1SKenneth D. Merry }
1101ef270ab1SKenneth D. Merry 
1102ef270ab1SKenneth D. Merry /**
1103ef270ab1SKenneth D. Merry  * @brief Attempt to dispatch a non-abort IO
1104ef270ab1SKenneth D. Merry  *
1105ef270ab1SKenneth D. Merry  * @par Description
1106ef270ab1SKenneth D. Merry  * An IO is dispatched:
1107ef270ab1SKenneth D. Merry  * - if the pending list is not empty, add IO to pending list
1108ef270ab1SKenneth D. Merry  *   and call a function to process the pending list.
1109ef270ab1SKenneth D. Merry  * - if pending list is empty, try to allocate a HW IO. If none
1110ef270ab1SKenneth D. Merry  *   is available, place this IO at the tail of the pending IO
1111ef270ab1SKenneth D. Merry  *   list.
1112ef270ab1SKenneth D. Merry  * - if HW IO is available, attach this IO to the HW IO and
1113ef270ab1SKenneth D. Merry  *   submit it.
1114ef270ab1SKenneth D. Merry  *
1115ef270ab1SKenneth D. Merry  * @param io Pointer to IO structure.
1116ef270ab1SKenneth D. Merry  * @param cb Callback function.
1117ef270ab1SKenneth D. Merry  *
1118ef270ab1SKenneth D. Merry  * @return Returns 0 on success, a negative error code value on failure.
1119ef270ab1SKenneth D. Merry  */
1120ef270ab1SKenneth D. Merry 
1121ef270ab1SKenneth D. Merry int32_t
1122ef270ab1SKenneth D. Merry ocs_scsi_io_dispatch(ocs_io_t *io, void *cb)
1123ef270ab1SKenneth D. Merry {
1124ef270ab1SKenneth D. Merry 	ocs_hw_io_t *hio;
1125ef270ab1SKenneth D. Merry 	ocs_t *ocs = io->ocs;
1126ef270ab1SKenneth D. Merry 	ocs_xport_t *xport = ocs->xport;
1127ef270ab1SKenneth D. Merry 
1128ef270ab1SKenneth D. Merry 	ocs_assert(io->cmd_tgt || io->cmd_ini, -1);
1129ef270ab1SKenneth D. Merry 	ocs_assert((io->io_type != OCS_IO_TYPE_ABORT), -1);
1130ef270ab1SKenneth D. Merry 	io->hw_cb = cb;
1131ef270ab1SKenneth D. Merry 
1132ef270ab1SKenneth D. Merry 	/*
1133ef270ab1SKenneth D. Merry 	 * if this IO already has a HW IO, then this is either not the first phase of
1134ef270ab1SKenneth D. Merry 	 * the IO. Send it to the HW.
1135ef270ab1SKenneth D. Merry 	 */
1136ef270ab1SKenneth D. Merry 	if (io->hio != NULL) {
1137ef270ab1SKenneth D. Merry 		return ocs_scsi_io_dispatch_hw_io(io, io->hio);
1138ef270ab1SKenneth D. Merry 	}
1139ef270ab1SKenneth D. Merry 
1140ef270ab1SKenneth D. Merry 	/*
1141ef270ab1SKenneth D. Merry 	 * We don't already have a HW IO associated with the IO. First check
1142ef270ab1SKenneth D. Merry 	 * the pending list. If not empty, add IO to the tail and process the
1143ef270ab1SKenneth D. Merry 	 * pending list.
1144ef270ab1SKenneth D. Merry 	 */
1145ef270ab1SKenneth D. Merry 	ocs_lock(&xport->io_pending_lock);
1146ef270ab1SKenneth D. Merry 		if (!ocs_list_empty(&xport->io_pending_list)) {
1147ef270ab1SKenneth D. Merry 			/*
1148ef270ab1SKenneth D. Merry 			 * If this is a low latency request, the put at the front of the IO pending
1149ef270ab1SKenneth D. Merry 			 * queue, otherwise put it at the end of the queue.
1150ef270ab1SKenneth D. Merry 			 */
1151ef270ab1SKenneth D. Merry 			if (io->low_latency) {
1152ef270ab1SKenneth D. Merry 				ocs_list_add_head(&xport->io_pending_list, io);
1153ef270ab1SKenneth D. Merry 			} else {
1154ef270ab1SKenneth D. Merry 				ocs_list_add_tail(&xport->io_pending_list, io);
1155ef270ab1SKenneth D. Merry 			}
1156ef270ab1SKenneth D. Merry 			ocs_unlock(&xport->io_pending_lock);
1157ef270ab1SKenneth D. Merry 			ocs_atomic_add_return(&xport->io_pending_count, 1);
1158ef270ab1SKenneth D. Merry 			ocs_atomic_add_return(&xport->io_total_pending, 1);
1159ef270ab1SKenneth D. Merry 
1160ef270ab1SKenneth D. Merry 			/* process pending list */
1161ef270ab1SKenneth D. Merry 			ocs_scsi_check_pending(ocs);
1162ef270ab1SKenneth D. Merry 			return 0;
1163ef270ab1SKenneth D. Merry 		}
1164ef270ab1SKenneth D. Merry 	ocs_unlock(&xport->io_pending_lock);
1165ef270ab1SKenneth D. Merry 
1166ef270ab1SKenneth D. Merry 	/*
1167ef270ab1SKenneth D. Merry 	 * We don't have a HW IO associated with the IO and there's nothing
1168ef270ab1SKenneth D. Merry 	 * on the pending list. Attempt to allocate a HW IO and dispatch it.
1169ef270ab1SKenneth D. Merry 	 */
1170ef270ab1SKenneth D. Merry 	hio = ocs_hw_io_alloc(&io->ocs->hw);
1171ef270ab1SKenneth D. Merry 	if (hio == NULL) {
1172ef270ab1SKenneth D. Merry 		/* Couldn't get a HW IO. Save this IO on the pending list */
1173ef270ab1SKenneth D. Merry 		ocs_lock(&xport->io_pending_lock);
1174ef270ab1SKenneth D. Merry 			ocs_list_add_tail(&xport->io_pending_list, io);
1175ef270ab1SKenneth D. Merry 		ocs_unlock(&xport->io_pending_lock);
1176ef270ab1SKenneth D. Merry 
1177ef270ab1SKenneth D. Merry 		ocs_atomic_add_return(&xport->io_total_pending, 1);
1178ef270ab1SKenneth D. Merry 		ocs_atomic_add_return(&xport->io_pending_count, 1);
1179ef270ab1SKenneth D. Merry 		return 0;
1180ef270ab1SKenneth D. Merry 	}
1181ef270ab1SKenneth D. Merry 
1182ef270ab1SKenneth D. Merry 	/* We successfully allocated a HW IO; dispatch to HW */
1183ef270ab1SKenneth D. Merry 	return ocs_scsi_io_dispatch_hw_io(io, hio);
1184ef270ab1SKenneth D. Merry }
1185ef270ab1SKenneth D. Merry 
1186ef270ab1SKenneth D. Merry /**
1187ef270ab1SKenneth D. Merry  * @brief Attempt to dispatch an Abort IO.
1188ef270ab1SKenneth D. Merry  *
1189ef270ab1SKenneth D. Merry  * @par Description
1190ef270ab1SKenneth D. Merry  * An Abort IO is dispatched:
1191ef270ab1SKenneth D. Merry  * - if the pending list is not empty, add IO to pending list
1192ef270ab1SKenneth D. Merry  *   and call a function to process the pending list.
1193ef270ab1SKenneth D. Merry  * - if pending list is empty, send abort to the HW.
1194ef270ab1SKenneth D. Merry  *
1195ef270ab1SKenneth D. Merry  * @param io Pointer to IO structure.
1196ef270ab1SKenneth D. Merry  * @param cb Callback function.
1197ef270ab1SKenneth D. Merry  *
1198ef270ab1SKenneth D. Merry  * @return Returns 0 on success, a negative error code value on failure.
1199ef270ab1SKenneth D. Merry  */
1200ef270ab1SKenneth D. Merry 
1201ef270ab1SKenneth D. Merry int32_t
1202ef270ab1SKenneth D. Merry ocs_scsi_io_dispatch_abort(ocs_io_t *io, void *cb)
1203ef270ab1SKenneth D. Merry {
1204ef270ab1SKenneth D. Merry 	ocs_t *ocs = io->ocs;
1205ef270ab1SKenneth D. Merry 	ocs_xport_t *xport = ocs->xport;
1206ef270ab1SKenneth D. Merry 
1207ef270ab1SKenneth D. Merry 	ocs_assert((io->io_type == OCS_IO_TYPE_ABORT), -1);
1208ef270ab1SKenneth D. Merry 	io->hw_cb = cb;
1209ef270ab1SKenneth D. Merry 
1210ef270ab1SKenneth D. Merry 	/*
1211ef270ab1SKenneth D. Merry 	 * For aborts, we don't need a HW IO, but we still want to pass through
1212ef270ab1SKenneth D. Merry 	 * the pending list to preserve ordering. Thus, if the pending list is
1213ef270ab1SKenneth D. Merry 	 * not empty, add this abort to the pending list and process the pending list.
1214ef270ab1SKenneth D. Merry 	 */
1215ef270ab1SKenneth D. Merry 	ocs_lock(&xport->io_pending_lock);
1216ef270ab1SKenneth D. Merry 		if (!ocs_list_empty(&xport->io_pending_list)) {
1217ef270ab1SKenneth D. Merry 			ocs_list_add_tail(&xport->io_pending_list, io);
1218ef270ab1SKenneth D. Merry 			ocs_unlock(&xport->io_pending_lock);
1219ef270ab1SKenneth D. Merry 			ocs_atomic_add_return(&xport->io_pending_count, 1);
1220ef270ab1SKenneth D. Merry 			ocs_atomic_add_return(&xport->io_total_pending, 1);
1221ef270ab1SKenneth D. Merry 
1222ef270ab1SKenneth D. Merry 			/* process pending list */
1223ef270ab1SKenneth D. Merry 			ocs_scsi_check_pending(ocs);
1224ef270ab1SKenneth D. Merry 			return 0;
1225ef270ab1SKenneth D. Merry 		}
1226ef270ab1SKenneth D. Merry 	ocs_unlock(&xport->io_pending_lock);
1227ef270ab1SKenneth D. Merry 
1228ef270ab1SKenneth D. Merry 	/* nothing on pending list, dispatch abort */
1229ef270ab1SKenneth D. Merry 	return ocs_scsi_io_dispatch_no_hw_io(io);
1230ef270ab1SKenneth D. Merry 
1231ef270ab1SKenneth D. Merry }
1232ef270ab1SKenneth D. Merry 
1233ef270ab1SKenneth D. Merry /**
1234ef270ab1SKenneth D. Merry  * @brief Dispatch IO
1235ef270ab1SKenneth D. Merry  *
1236ef270ab1SKenneth D. Merry  * @par Description
1237ef270ab1SKenneth D. Merry  * An IO and its associated HW IO is dispatched to the HW.
1238ef270ab1SKenneth D. Merry  *
1239ef270ab1SKenneth D. Merry  * @param io Pointer to IO structure.
1240ef270ab1SKenneth D. Merry  * @param hio Pointer to HW IO structure from which IO will be
1241ef270ab1SKenneth D. Merry  * dispatched.
1242ef270ab1SKenneth D. Merry  *
1243ef270ab1SKenneth D. Merry  * @return Returns 0 on success, a negative error code value on failure.
1244ef270ab1SKenneth D. Merry  */
1245ef270ab1SKenneth D. Merry 
1246ef270ab1SKenneth D. Merry static int32_t
1247ef270ab1SKenneth D. Merry ocs_scsi_io_dispatch_hw_io(ocs_io_t *io, ocs_hw_io_t *hio)
1248ef270ab1SKenneth D. Merry {
1249ef270ab1SKenneth D. Merry 	int32_t rc;
1250ef270ab1SKenneth D. Merry 	ocs_t *ocs = io->ocs;
1251ef270ab1SKenneth D. Merry 
1252ef270ab1SKenneth D. Merry 	/* Got a HW IO; update ini/tgt_task_tag with HW IO info and dispatch */
1253ef270ab1SKenneth D. Merry 	io->hio = hio;
1254ef270ab1SKenneth D. Merry 	if (io->cmd_tgt) {
1255ef270ab1SKenneth D. Merry 		io->tgt_task_tag = hio->indicator;
1256ef270ab1SKenneth D. Merry 	} else if (io->cmd_ini) {
1257ef270ab1SKenneth D. Merry 		io->init_task_tag = hio->indicator;
1258ef270ab1SKenneth D. Merry 	}
1259ef270ab1SKenneth D. Merry 	io->hw_tag = hio->reqtag;
1260ef270ab1SKenneth D. Merry 
1261ef270ab1SKenneth D. Merry 	hio->eq = io->hw_priv;
1262ef270ab1SKenneth D. Merry 
1263ef270ab1SKenneth D. Merry 	/* Copy WQ steering */
1264ef270ab1SKenneth D. Merry 	switch(io->wq_steering) {
1265ef270ab1SKenneth D. Merry 	case OCS_SCSI_WQ_STEERING_CLASS >> OCS_SCSI_WQ_STEERING_SHIFT:
1266ef270ab1SKenneth D. Merry 		hio->wq_steering = OCS_HW_WQ_STEERING_CLASS;
1267ef270ab1SKenneth D. Merry 		break;
1268ef270ab1SKenneth D. Merry 	case OCS_SCSI_WQ_STEERING_REQUEST >> OCS_SCSI_WQ_STEERING_SHIFT:
1269ef270ab1SKenneth D. Merry 		hio->wq_steering = OCS_HW_WQ_STEERING_REQUEST;
1270ef270ab1SKenneth D. Merry 		break;
1271ef270ab1SKenneth D. Merry 	case OCS_SCSI_WQ_STEERING_CPU >> OCS_SCSI_WQ_STEERING_SHIFT:
1272ef270ab1SKenneth D. Merry 		hio->wq_steering = OCS_HW_WQ_STEERING_CPU;
1273ef270ab1SKenneth D. Merry 		break;
1274ef270ab1SKenneth D. Merry 	}
1275ef270ab1SKenneth D. Merry 
1276ef270ab1SKenneth D. Merry 	switch (io->io_type) {
1277ef270ab1SKenneth D. Merry 	case OCS_IO_TYPE_IO: {
1278ef270ab1SKenneth D. Merry 		uint32_t max_sgl;
1279ef270ab1SKenneth D. Merry 		uint32_t total_count;
1280ef270ab1SKenneth D. Merry 		uint32_t host_allocated;
1281ef270ab1SKenneth D. Merry 
1282ef270ab1SKenneth D. Merry 		ocs_hw_get(&ocs->hw, OCS_HW_N_SGL, &max_sgl);
1283ef270ab1SKenneth D. Merry 		ocs_hw_get(&ocs->hw, OCS_HW_SGL_CHAINING_HOST_ALLOCATED, &host_allocated);
1284ef270ab1SKenneth D. Merry 
1285ef270ab1SKenneth D. Merry 		/*
1286ef270ab1SKenneth D. Merry 		 * If the requested SGL is larger than the default size, then we can allocate
1287ef270ab1SKenneth D. Merry 		 * an overflow SGL.
1288ef270ab1SKenneth D. Merry 		 */
1289ef270ab1SKenneth D. Merry 		total_count = ocs_scsi_count_sgls(&io->hw_dif, io->sgl, io->sgl_count);
1290ef270ab1SKenneth D. Merry 
1291ef270ab1SKenneth D. Merry 		/*
1292ef270ab1SKenneth D. Merry 		 * Lancer requires us to allocate the chained memory area, but
1293ef270ab1SKenneth D. Merry 		 * Skyhawk must use the SGL list associated with another XRI.
1294ef270ab1SKenneth D. Merry 		 */
1295ef270ab1SKenneth D. Merry 		if (host_allocated && total_count > max_sgl) {
1296ef270ab1SKenneth D. Merry 			/* Compute count needed, the number extra plus 1 for the link sge */
1297ef270ab1SKenneth D. Merry 			uint32_t count = total_count - max_sgl + 1;
1298ef270ab1SKenneth D. Merry 			rc = ocs_dma_alloc(ocs, &io->ovfl_sgl, count*sizeof(sli4_sge_t), 64);
1299ef270ab1SKenneth D. Merry 			if (rc) {
1300ef270ab1SKenneth D. Merry 				ocs_log_err(ocs, "ocs_dma_alloc overflow sgl failed\n");
1301ef270ab1SKenneth D. Merry 				break;
1302ef270ab1SKenneth D. Merry 			}
1303ef270ab1SKenneth D. Merry 			rc = ocs_hw_io_register_sgl(&ocs->hw, io->hio, &io->ovfl_sgl, count);
1304ef270ab1SKenneth D. Merry 			if (rc) {
1305ef270ab1SKenneth D. Merry 				ocs_scsi_io_free_ovfl(io);
1306ef270ab1SKenneth D. Merry 				ocs_log_err(ocs, "ocs_hw_io_register_sgl() failed\n");
1307ef270ab1SKenneth D. Merry 				break;
1308ef270ab1SKenneth D. Merry 			}
1309ef270ab1SKenneth D. Merry 			/* EVT: update chained_io_count */
1310ef270ab1SKenneth D. Merry 			io->node->chained_io_count++;
1311ef270ab1SKenneth D. Merry 		}
1312ef270ab1SKenneth D. Merry 
1313ef270ab1SKenneth D. Merry 		rc = ocs_scsi_build_sgls(&ocs->hw, io->hio, &io->hw_dif, io->sgl, io->sgl_count, io->hio_type);
1314ef270ab1SKenneth D. Merry 		if (rc) {
1315ef270ab1SKenneth D. Merry 			ocs_scsi_io_free_ovfl(io);
1316ef270ab1SKenneth D. Merry 			break;
1317ef270ab1SKenneth D. Merry 		}
1318ef270ab1SKenneth D. Merry 
1319ef270ab1SKenneth D. Merry 		if (OCS_LOG_ENABLE_SCSI_TRACE(ocs)) {
1320ef270ab1SKenneth D. Merry 			ocs_log_sgl(io);
1321ef270ab1SKenneth D. Merry 		}
1322ef270ab1SKenneth D. Merry 
1323ef270ab1SKenneth D. Merry 		if (io->app_id) {
1324ef270ab1SKenneth D. Merry 			io->iparam.fcp_tgt.app_id = io->app_id;
1325ef270ab1SKenneth D. Merry 		}
1326ef270ab1SKenneth D. Merry 
1327ef270ab1SKenneth D. Merry 		rc = ocs_hw_io_send(&io->ocs->hw, io->hio_type, io->hio, io->wire_len, &io->iparam, &io->node->rnode,
1328ef270ab1SKenneth D. Merry 			io->hw_cb, io);
1329ef270ab1SKenneth D. Merry 		break;
1330ef270ab1SKenneth D. Merry 	}
1331ef270ab1SKenneth D. Merry 	case OCS_IO_TYPE_ELS:
1332ef270ab1SKenneth D. Merry 	case OCS_IO_TYPE_CT: {
1333ef270ab1SKenneth D. Merry 		rc = ocs_hw_srrs_send(&ocs->hw, io->hio_type, io->hio,
1334ef270ab1SKenneth D. Merry 			&io->els_req, io->wire_len,
1335ef270ab1SKenneth D. Merry 			&io->els_rsp, &io->node->rnode, &io->iparam,
1336ef270ab1SKenneth D. Merry 			io->hw_cb, io);
1337ef270ab1SKenneth D. Merry 		break;
1338ef270ab1SKenneth D. Merry 	}
1339ef270ab1SKenneth D. Merry 	case OCS_IO_TYPE_CT_RESP: {
1340ef270ab1SKenneth D. Merry 		rc = ocs_hw_srrs_send(&ocs->hw, io->hio_type, io->hio,
1341ef270ab1SKenneth D. Merry 			&io->els_rsp, io->wire_len,
1342ef270ab1SKenneth D. Merry 			NULL, &io->node->rnode, &io->iparam,
1343ef270ab1SKenneth D. Merry 			io->hw_cb, io);
1344ef270ab1SKenneth D. Merry 		break;
1345ef270ab1SKenneth D. Merry 	}
1346ef270ab1SKenneth D. Merry 	case OCS_IO_TYPE_BLS_RESP: {
1347ef270ab1SKenneth D. Merry 		/* no need to update tgt_task_tag for BLS response since the RX_ID
1348ef270ab1SKenneth D. Merry 		 * will be specified by the payload, not the XRI */
1349ef270ab1SKenneth D. Merry 		rc = ocs_hw_srrs_send(&ocs->hw, io->hio_type, io->hio,
1350ef270ab1SKenneth D. Merry 			NULL, 0, NULL, &io->node->rnode, &io->iparam, io->hw_cb, io);
1351ef270ab1SKenneth D. Merry 		break;
1352ef270ab1SKenneth D. Merry 	}
1353ef270ab1SKenneth D. Merry 	default:
1354ef270ab1SKenneth D. Merry 		scsi_io_printf(io, "Unknown IO type=%d\n", io->io_type);
1355ef270ab1SKenneth D. Merry 		rc = -1;
1356ef270ab1SKenneth D. Merry 		break;
1357ef270ab1SKenneth D. Merry 	}
1358ef270ab1SKenneth D. Merry 	return rc;
1359ef270ab1SKenneth D. Merry }
1360ef270ab1SKenneth D. Merry 
1361ef270ab1SKenneth D. Merry /**
1362ef270ab1SKenneth D. Merry  * @brief Dispatch IO
1363ef270ab1SKenneth D. Merry  *
1364ef270ab1SKenneth D. Merry  * @par Description
1365ef270ab1SKenneth D. Merry  * An IO that does require a HW IO is dispatched to the HW.
1366ef270ab1SKenneth D. Merry  *
1367ef270ab1SKenneth D. Merry  * @param io Pointer to IO structure.
1368ef270ab1SKenneth D. Merry  *
1369ef270ab1SKenneth D. Merry  * @return Returns 0 on success, or a negative error code value on failure.
1370ef270ab1SKenneth D. Merry  */
1371ef270ab1SKenneth D. Merry 
1372ef270ab1SKenneth D. Merry static int32_t
1373ef270ab1SKenneth D. Merry ocs_scsi_io_dispatch_no_hw_io(ocs_io_t *io)
1374ef270ab1SKenneth D. Merry {
1375ef270ab1SKenneth D. Merry 	int32_t rc;
1376ef270ab1SKenneth D. Merry 
1377ef270ab1SKenneth D. Merry 	switch (io->io_type) {
1378ef270ab1SKenneth D. Merry 	case OCS_IO_TYPE_ABORT: {
1379ef270ab1SKenneth D. Merry 		ocs_hw_io_t *hio_to_abort = NULL;
1380ef270ab1SKenneth D. Merry 		ocs_assert(io->io_to_abort, -1);
1381ef270ab1SKenneth D. Merry 		hio_to_abort = io->io_to_abort->hio;
1382ef270ab1SKenneth D. Merry 
1383ef270ab1SKenneth D. Merry 		if (hio_to_abort == NULL) {
1384ef270ab1SKenneth D. Merry 			/*
1385ef270ab1SKenneth D. Merry 			 * If "IO to abort" does not have an associated HW IO, immediately
1386ef270ab1SKenneth D. Merry 			 * make callback with success. The command must have been sent to
1387ef270ab1SKenneth D. Merry 			 * the backend, but the data phase has not yet started, so we don't
1388ef270ab1SKenneth D. Merry 			 * have a HW IO.
1389ef270ab1SKenneth D. Merry 			 *
1390ef270ab1SKenneth D. Merry 			 * Note: since the backend shims should be taking a reference
1391ef270ab1SKenneth D. Merry 			 * on io_to_abort, it should not be possible to have been completed
1392ef270ab1SKenneth D. Merry 			 * and freed by the backend before the abort got here.
1393ef270ab1SKenneth D. Merry 			 */
1394ef270ab1SKenneth D. Merry 			scsi_io_printf(io, "IO: " SCSI_IOFMT " not active\n",
1395ef270ab1SKenneth D. Merry 				       SCSI_IOFMT_ARGS(io->io_to_abort));
1396ef270ab1SKenneth D. Merry 			((ocs_hw_done_t)io->hw_cb)(io->hio, NULL, 0, SLI4_FC_WCQE_STATUS_SUCCESS, 0, io);
1397ef270ab1SKenneth D. Merry 			rc = 0;
1398ef270ab1SKenneth D. Merry 		} else {
1399ef270ab1SKenneth D. Merry 			/* HW IO is valid, abort it */
1400ef270ab1SKenneth D. Merry 			scsi_io_printf(io, "aborting " SCSI_IOFMT "\n", SCSI_IOFMT_ARGS(io->io_to_abort));
1401ef270ab1SKenneth D. Merry 			rc = ocs_hw_io_abort(&io->ocs->hw, hio_to_abort, io->send_abts,
1402ef270ab1SKenneth D. Merry 					      io->hw_cb, io);
1403ef270ab1SKenneth D. Merry 			if (rc) {
1404ef270ab1SKenneth D. Merry 				int status = SLI4_FC_WCQE_STATUS_SUCCESS;
1405ef270ab1SKenneth D. Merry 				if ((rc != OCS_HW_RTN_IO_NOT_ACTIVE) &&
1406ef270ab1SKenneth D. Merry 				    (rc != OCS_HW_RTN_IO_ABORT_IN_PROGRESS)) {
1407ef270ab1SKenneth D. Merry 					status = -1;
1408ef270ab1SKenneth D. Merry 					scsi_io_printf(io, "Failed to abort IO: " SCSI_IOFMT " status=%d\n",
1409ef270ab1SKenneth D. Merry 						       SCSI_IOFMT_ARGS(io->io_to_abort), rc);
1410ef270ab1SKenneth D. Merry 				}
1411ef270ab1SKenneth D. Merry 				((ocs_hw_done_t)io->hw_cb)(io->hio, NULL, 0, status, 0, io);
1412ef270ab1SKenneth D. Merry 				rc = 0;
1413ef270ab1SKenneth D. Merry 			}
1414ef270ab1SKenneth D. Merry 		}
1415ef270ab1SKenneth D. Merry 
1416ef270ab1SKenneth D. Merry 		break;
1417ef270ab1SKenneth D. Merry 	}
1418ef270ab1SKenneth D. Merry 	default:
1419ef270ab1SKenneth D. Merry 		scsi_io_printf(io, "Unknown IO type=%d\n", io->io_type);
1420ef270ab1SKenneth D. Merry 		rc = -1;
1421ef270ab1SKenneth D. Merry 		break;
1422ef270ab1SKenneth D. Merry 	}
1423ef270ab1SKenneth D. Merry 	return rc;
1424ef270ab1SKenneth D. Merry }
1425ef270ab1SKenneth D. Merry 
1426ef270ab1SKenneth D. Merry /**
1427ef270ab1SKenneth D. Merry  * @ingroup scsi_api_base
1428ef270ab1SKenneth D. Merry  * @brief Send read/write data.
1429ef270ab1SKenneth D. Merry  *
1430ef270ab1SKenneth D. Merry  * @par Description
1431ef270ab1SKenneth D. Merry  * This call is made by a target-server to initiate a SCSI read or write data phase, transferring
1432ef270ab1SKenneth D. Merry  * data between the target to the remote initiator. The payload is specified by the
1433ef270ab1SKenneth D. Merry  * scatter-gather list @c sgl of length @c sgl_count. The @c wire_len argument
1434ef270ab1SKenneth D. Merry  * specifies the payload length (independent of the scatter-gather list cumulative length).
1435ef270ab1SKenneth D. Merry  * @n @n
1436ef270ab1SKenneth D. Merry  * The @c flags argument has one bit, OCS_SCSI_LAST_DATAPHASE, which is a hint to the base
1437ef270ab1SKenneth D. Merry  * driver that it may use auto SCSI response features if the hardware supports it.
1438ef270ab1SKenneth D. Merry  * @n @n
1439ef270ab1SKenneth D. Merry  * Upon completion, the callback function @b cb is called with flags indicating that the
1440ef270ab1SKenneth D. Merry  * IO has completed (OCS_SCSI_IO_COMPL) and another data phase or response may be sent;
1441ef270ab1SKenneth D. Merry  * that the IO has completed and no response needs to be sent (OCS_SCSI_IO_COMPL_NO_RSP);
1442ef270ab1SKenneth D. Merry  * or that the IO was aborted (OCS_SCSI_IO_ABORTED).
1443ef270ab1SKenneth D. Merry  *
1444ef270ab1SKenneth D. Merry  * @param io Pointer to the IO context.
1445ef270ab1SKenneth D. Merry  * @param flags Flags controlling the sending of data.
1446ef270ab1SKenneth D. Merry  * @param dif_info Pointer to T10 DIF fields, or NULL if no DIF.
1447ef270ab1SKenneth D. Merry  * @param sgl Pointer to the payload scatter-gather list.
1448ef270ab1SKenneth D. Merry  * @param sgl_count Count of the scatter-gather list elements.
1449ef270ab1SKenneth D. Merry  * @param xwire_len Length of the payload on wire, in bytes.
1450ef270ab1SKenneth D. Merry  * @param type HW IO type.
1451ef270ab1SKenneth D. Merry  * @param enable_ar Enable auto-response if true.
1452ef270ab1SKenneth D. Merry  * @param cb Completion callback.
1453ef270ab1SKenneth D. Merry  * @param arg Application-supplied callback data.
1454ef270ab1SKenneth D. Merry  *
1455ef270ab1SKenneth D. Merry  * @return Returns 0 on success, or a negative error code value on failure.
1456ef270ab1SKenneth D. Merry  */
1457ef270ab1SKenneth D. Merry 
1458ef270ab1SKenneth D. Merry static inline int32_t
1459ef270ab1SKenneth D. Merry ocs_scsi_xfer_data(ocs_io_t *io, uint32_t flags,
1460ef270ab1SKenneth D. Merry 	ocs_scsi_dif_info_t *dif_info,
1461ef270ab1SKenneth D. Merry 	ocs_scsi_sgl_t *sgl, uint32_t sgl_count, uint32_t xwire_len,
1462ef270ab1SKenneth D. Merry 	ocs_hw_io_type_e type, int enable_ar,
1463ef270ab1SKenneth D. Merry 	ocs_scsi_io_cb_t cb, void *arg)
1464ef270ab1SKenneth D. Merry {
1465ef270ab1SKenneth D. Merry 	int32_t rc;
1466ef270ab1SKenneth D. Merry 	ocs_t *ocs;
1467ef270ab1SKenneth D. Merry 	uint32_t disable_ar_tgt_dif = FALSE;
1468ef270ab1SKenneth D. Merry 	size_t residual = 0;
1469ef270ab1SKenneth D. Merry 
1470ef270ab1SKenneth D. Merry 	if ((dif_info != NULL) && (dif_info->dif_oper == OCS_SCSI_DIF_OPER_DISABLED)) {
1471ef270ab1SKenneth D. Merry 		dif_info = NULL;
1472ef270ab1SKenneth D. Merry 	}
1473ef270ab1SKenneth D. Merry 
1474ef270ab1SKenneth D. Merry 	ocs_assert(io, -1);
1475ef270ab1SKenneth D. Merry 
1476ef270ab1SKenneth D. Merry 	if (dif_info != NULL) {
1477ef270ab1SKenneth D. Merry 		ocs_hw_get(&io->ocs->hw, OCS_HW_DISABLE_AR_TGT_DIF, &disable_ar_tgt_dif);
1478ef270ab1SKenneth D. Merry 		if (disable_ar_tgt_dif) {
1479ef270ab1SKenneth D. Merry 			enable_ar = FALSE;
1480ef270ab1SKenneth D. Merry 		}
1481ef270ab1SKenneth D. Merry 	}
1482ef270ab1SKenneth D. Merry 
1483ef270ab1SKenneth D. Merry 	io->sgl_count = sgl_count;
1484ef270ab1SKenneth D. Merry 
1485ef270ab1SKenneth D. Merry 	/* If needed, copy SGL */
1486ef270ab1SKenneth D. Merry 	if (sgl && (sgl != io->sgl)) {
1487ef270ab1SKenneth D. Merry 		ocs_assert(sgl_count <= io->sgl_allocated, -1);
1488ef270ab1SKenneth D. Merry 		ocs_memcpy(io->sgl, sgl, sgl_count*sizeof(*io->sgl));
1489ef270ab1SKenneth D. Merry 	}
1490ef270ab1SKenneth D. Merry 
1491ef270ab1SKenneth D. Merry 	ocs = io->ocs;
1492ef270ab1SKenneth D. Merry 	ocs_assert(ocs, -1);
1493ef270ab1SKenneth D. Merry 	ocs_assert(io->node, -1);
1494ef270ab1SKenneth D. Merry 
1495ef270ab1SKenneth D. Merry 	scsi_io_trace(io, "%s wire_len %d\n", (type == OCS_HW_IO_TARGET_READ) ? "send" : "recv", xwire_len);
1496ef270ab1SKenneth D. Merry 
1497ef270ab1SKenneth D. Merry 	ocs_assert(sgl, -1);
1498ef270ab1SKenneth D. Merry 	ocs_assert(sgl_count > 0, -1);
1499ef270ab1SKenneth D. Merry 	ocs_assert(io->exp_xfer_len > io->transferred, -1);
1500ef270ab1SKenneth D. Merry 
1501ef270ab1SKenneth D. Merry 	io->hio_type = type;
1502ef270ab1SKenneth D. Merry 
1503ef270ab1SKenneth D. Merry 	io->scsi_tgt_cb = cb;
1504ef270ab1SKenneth D. Merry 	io->scsi_tgt_cb_arg = arg;
1505ef270ab1SKenneth D. Merry 
1506ef270ab1SKenneth D. Merry 	rc = ocs_scsi_convert_dif_info(ocs, dif_info, &io->hw_dif);
1507ef270ab1SKenneth D. Merry 	if (rc) {
1508ef270ab1SKenneth D. Merry 		return rc;
1509ef270ab1SKenneth D. Merry 	}
1510ef270ab1SKenneth D. Merry 
1511ef270ab1SKenneth D. Merry 	/* If DIF is used, then save lba for error recovery */
1512ef270ab1SKenneth D. Merry 	if (dif_info) {
1513ef270ab1SKenneth D. Merry 		io->scsi_dif_info = *dif_info;
1514ef270ab1SKenneth D. Merry 	}
1515ef270ab1SKenneth D. Merry 
1516ef270ab1SKenneth D. Merry 	io->wire_len = MIN(xwire_len, io->exp_xfer_len - io->transferred);
1517ef270ab1SKenneth D. Merry 	residual = (xwire_len - io->wire_len);
1518ef270ab1SKenneth D. Merry 
1519ef270ab1SKenneth D. Merry 	ocs_memset(&io->iparam, 0, sizeof(io->iparam));
1520ef270ab1SKenneth D. Merry 	io->iparam.fcp_tgt.ox_id = io->init_task_tag;
1521ef270ab1SKenneth D. Merry 	io->iparam.fcp_tgt.offset = io->transferred;
1522ef270ab1SKenneth D. Merry 	io->iparam.fcp_tgt.dif_oper = io->hw_dif.dif;
1523ef270ab1SKenneth D. Merry 	io->iparam.fcp_tgt.blk_size = io->hw_dif.blk_size;
1524ef270ab1SKenneth D. Merry 	io->iparam.fcp_tgt.cs_ctl = io->cs_ctl;
1525ef270ab1SKenneth D. Merry 	io->iparam.fcp_tgt.timeout = io->timeout;
1526ef270ab1SKenneth D. Merry 
1527ef270ab1SKenneth D. Merry 	/* if this is the last data phase and there is no residual, enable
1528ef270ab1SKenneth D. Merry 	 * auto-good-response
1529ef270ab1SKenneth D. Merry 	 */
1530ef270ab1SKenneth D. Merry 	if (enable_ar && (flags & OCS_SCSI_LAST_DATAPHASE) &&
1531ef270ab1SKenneth D. Merry 		(residual == 0) && ((io->transferred + io->wire_len) == io->exp_xfer_len) && (!(flags & OCS_SCSI_NO_AUTO_RESPONSE))) {
1532ef270ab1SKenneth D. Merry 		io->iparam.fcp_tgt.flags |= SLI4_IO_AUTO_GOOD_RESPONSE;
1533ef270ab1SKenneth D. Merry 		io->auto_resp = TRUE;
1534ef270ab1SKenneth D. Merry 	} else {
1535ef270ab1SKenneth D. Merry 		io->auto_resp = FALSE;
1536ef270ab1SKenneth D. Merry 	}
1537ef270ab1SKenneth D. Merry 
1538ef270ab1SKenneth D. Merry 	/* save this transfer length */
1539ef270ab1SKenneth D. Merry 	io->xfer_req = io->wire_len;
1540ef270ab1SKenneth D. Merry 
1541ef270ab1SKenneth D. Merry 	/* Adjust the transferred count to account for overrun
1542ef270ab1SKenneth D. Merry 	 * when the residual is calculated in ocs_scsi_send_resp
1543ef270ab1SKenneth D. Merry 	 */
1544ef270ab1SKenneth D. Merry 	io->transferred += residual;
1545ef270ab1SKenneth D. Merry 
1546ef270ab1SKenneth D. Merry 	/* Adjust the SGL size if there is overrun */
1547ef270ab1SKenneth D. Merry 
1548ef270ab1SKenneth D. Merry 	if (residual) {
1549ef270ab1SKenneth D. Merry 		ocs_scsi_sgl_t  *sgl_ptr = &io->sgl[sgl_count-1];
1550ef270ab1SKenneth D. Merry 
1551ef270ab1SKenneth D. Merry 		while (residual) {
1552ef270ab1SKenneth D. Merry 			size_t len = sgl_ptr->len;
1553ef270ab1SKenneth D. Merry 			if ( len > residual) {
1554ef270ab1SKenneth D. Merry 				sgl_ptr->len = len - residual;
1555ef270ab1SKenneth D. Merry 				residual = 0;
1556ef270ab1SKenneth D. Merry 			} else {
1557ef270ab1SKenneth D. Merry 				sgl_ptr->len = 0;
1558ef270ab1SKenneth D. Merry 				residual -= len;
1559ef270ab1SKenneth D. Merry 				io->sgl_count--;
1560ef270ab1SKenneth D. Merry 			}
1561ef270ab1SKenneth D. Merry 			sgl_ptr--;
1562ef270ab1SKenneth D. Merry 		}
1563ef270ab1SKenneth D. Merry 	}
1564ef270ab1SKenneth D. Merry 
1565ef270ab1SKenneth D. Merry 	/* Set latency and WQ steering */
1566ef270ab1SKenneth D. Merry 	io->low_latency = (flags & OCS_SCSI_LOW_LATENCY) != 0;
1567ef270ab1SKenneth D. Merry 	io->wq_steering = (flags & OCS_SCSI_WQ_STEERING_MASK) >> OCS_SCSI_WQ_STEERING_SHIFT;
1568ef270ab1SKenneth D. Merry 	io->wq_class = (flags & OCS_SCSI_WQ_CLASS_MASK) >> OCS_SCSI_WQ_CLASS_SHIFT;
1569ef270ab1SKenneth D. Merry 
1570ef270ab1SKenneth D. Merry 	return ocs_scsi_io_dispatch(io, ocs_target_io_cb);
1571ef270ab1SKenneth D. Merry }
1572ef270ab1SKenneth D. Merry 
1573ef270ab1SKenneth D. Merry int32_t
1574ef270ab1SKenneth D. Merry ocs_scsi_send_rd_data(ocs_io_t *io, uint32_t flags,
1575ef270ab1SKenneth D. Merry 	ocs_scsi_dif_info_t *dif_info,
1576ef270ab1SKenneth D. Merry 	ocs_scsi_sgl_t *sgl, uint32_t sgl_count, uint32_t len,
1577ef270ab1SKenneth D. Merry 	ocs_scsi_io_cb_t cb, void *arg)
1578ef270ab1SKenneth D. Merry {
1579ef270ab1SKenneth D. Merry 	return ocs_scsi_xfer_data(io, flags, dif_info, sgl, sgl_count, len, OCS_HW_IO_TARGET_READ,
1580ef270ab1SKenneth D. Merry 				  enable_tsend_auto_resp(io->ocs), cb, arg);
1581ef270ab1SKenneth D. Merry }
1582ef270ab1SKenneth D. Merry 
1583ef270ab1SKenneth D. Merry int32_t
1584ef270ab1SKenneth D. Merry ocs_scsi_recv_wr_data(ocs_io_t *io, uint32_t flags,
1585ef270ab1SKenneth D. Merry 	ocs_scsi_dif_info_t *dif_info,
1586ef270ab1SKenneth D. Merry 	ocs_scsi_sgl_t *sgl, uint32_t sgl_count, uint32_t len,
1587ef270ab1SKenneth D. Merry 	ocs_scsi_io_cb_t cb, void *arg)
1588ef270ab1SKenneth D. Merry {
1589ef270ab1SKenneth D. Merry 	return ocs_scsi_xfer_data(io, flags, dif_info, sgl, sgl_count, len, OCS_HW_IO_TARGET_WRITE,
1590ef270ab1SKenneth D. Merry 				  enable_treceive_auto_resp(io->ocs), cb, arg);
1591ef270ab1SKenneth D. Merry }
1592ef270ab1SKenneth D. Merry 
1593ef270ab1SKenneth D. Merry /**
1594ef270ab1SKenneth D. Merry  * @ingroup scsi_api_base
1595ef270ab1SKenneth D. Merry  * @brief Free overflow SGL.
1596ef270ab1SKenneth D. Merry  *
1597ef270ab1SKenneth D. Merry  * @par Description
1598ef270ab1SKenneth D. Merry  * Free the overflow SGL if it is present.
1599ef270ab1SKenneth D. Merry  *
1600ef270ab1SKenneth D. Merry  * @param io Pointer to IO object.
1601ef270ab1SKenneth D. Merry  *
1602ef270ab1SKenneth D. Merry  * @return None.
1603ef270ab1SKenneth D. Merry  */
1604ef270ab1SKenneth D. Merry static void
1605ef270ab1SKenneth D. Merry ocs_scsi_io_free_ovfl(ocs_io_t *io) {
1606ef270ab1SKenneth D. Merry 	if (io->ovfl_sgl.size) {
1607ef270ab1SKenneth D. Merry 		ocs_dma_free(io->ocs, &io->ovfl_sgl);
1608ef270ab1SKenneth D. Merry 	}
1609ef270ab1SKenneth D. Merry }
1610ef270ab1SKenneth D. Merry 
1611ef270ab1SKenneth D. Merry /**
1612ef270ab1SKenneth D. Merry  * @ingroup scsi_api_base
1613ef270ab1SKenneth D. Merry  * @brief Send response data.
1614ef270ab1SKenneth D. Merry  *
1615ef270ab1SKenneth D. Merry  * @par Description
1616ef270ab1SKenneth D. Merry  * This function is used by a target-server to send the SCSI response data to a remote
1617ef270ab1SKenneth D. Merry  * initiator node. The target-server populates the @c ocs_scsi_cmd_resp_t
1618ef270ab1SKenneth D. Merry  * argument with scsi status, status qualifier, sense data, and response data, as
1619ef270ab1SKenneth D. Merry  * needed.
1620ef270ab1SKenneth D. Merry  * @n @n
1621ef270ab1SKenneth D. Merry  * Upon completion, the callback function @c cb is invoked. The target-server will generally
1622ef270ab1SKenneth D. Merry  * clean up its IO context resources and call ocs_scsi_io_complete().
1623ef270ab1SKenneth D. Merry  *
1624ef270ab1SKenneth D. Merry  * @param io Pointer to the IO context.
1625ef270ab1SKenneth D. Merry  * @param flags Flags to control sending of the SCSI response.
1626ef270ab1SKenneth D. Merry  * @param rsp Pointer to the response data populated by the caller.
1627ef270ab1SKenneth D. Merry  * @param cb Completion callback.
1628ef270ab1SKenneth D. Merry  * @param arg Application-specified completion callback argument.
1629ef270ab1SKenneth D. Merry 
1630ef270ab1SKenneth D. Merry  * @return Returns 0 on success, or a negative error code value on failure.
1631ef270ab1SKenneth D. Merry  */
1632ef270ab1SKenneth D. Merry int32_t
1633ef270ab1SKenneth D. Merry ocs_scsi_send_resp(ocs_io_t *io, uint32_t flags, ocs_scsi_cmd_resp_t *rsp, ocs_scsi_io_cb_t cb, void *arg)
1634ef270ab1SKenneth D. Merry {
1635ef270ab1SKenneth D. Merry 	ocs_t *ocs;
1636ef270ab1SKenneth D. Merry 	int32_t residual;
1637ef270ab1SKenneth D. Merry 	int auto_resp = TRUE;		/* Always try auto resp */
1638ef270ab1SKenneth D. Merry 	uint8_t scsi_status = 0;
1639ef270ab1SKenneth D. Merry 	uint16_t scsi_status_qualifier = 0;
1640ef270ab1SKenneth D. Merry 	uint8_t *sense_data = NULL;
1641ef270ab1SKenneth D. Merry 	uint32_t sense_data_length = 0;
1642ef270ab1SKenneth D. Merry 
1643ef270ab1SKenneth D. Merry 	ocs_assert(io, -1);
1644ef270ab1SKenneth D. Merry 
1645ef270ab1SKenneth D. Merry 	ocs = io->ocs;
1646ef270ab1SKenneth D. Merry 	ocs_assert(ocs, -1);
1647ef270ab1SKenneth D. Merry 
1648ef270ab1SKenneth D. Merry 	ocs_assert(io->node, -1);
1649ef270ab1SKenneth D. Merry 
1650ef270ab1SKenneth D. Merry 	ocs_scsi_convert_dif_info(ocs, NULL, &io->hw_dif);
1651ef270ab1SKenneth D. Merry 
1652ef270ab1SKenneth D. Merry 	if (rsp) {
1653ef270ab1SKenneth D. Merry 		scsi_status = rsp->scsi_status;
1654ef270ab1SKenneth D. Merry 		scsi_status_qualifier = rsp->scsi_status_qualifier;
1655ef270ab1SKenneth D. Merry 		sense_data = rsp->sense_data;
1656ef270ab1SKenneth D. Merry 		sense_data_length = rsp->sense_data_length;
1657ef270ab1SKenneth D. Merry 		residual = rsp->residual;
1658ef270ab1SKenneth D. Merry 	} else {
1659ef270ab1SKenneth D. Merry 		residual = io->exp_xfer_len - io->transferred;
1660ef270ab1SKenneth D. Merry 	}
1661ef270ab1SKenneth D. Merry 
1662ef270ab1SKenneth D. Merry 	io->wire_len = 0;
1663ef270ab1SKenneth D. Merry 	io->hio_type = OCS_HW_IO_TARGET_RSP;
1664ef270ab1SKenneth D. Merry 
1665ef270ab1SKenneth D. Merry 	io->scsi_tgt_cb = cb;
1666ef270ab1SKenneth D. Merry 	io->scsi_tgt_cb_arg = arg;
1667ef270ab1SKenneth D. Merry 
1668ef270ab1SKenneth D. Merry 	ocs_memset(&io->iparam, 0, sizeof(io->iparam));
1669ef270ab1SKenneth D. Merry 	io->iparam.fcp_tgt.ox_id = io->init_task_tag;
1670ef270ab1SKenneth D. Merry 	io->iparam.fcp_tgt.offset = 0;
1671ef270ab1SKenneth D. Merry 	io->iparam.fcp_tgt.cs_ctl = io->cs_ctl;
1672ef270ab1SKenneth D. Merry 	io->iparam.fcp_tgt.timeout = io->timeout;
1673ef270ab1SKenneth D. Merry 
1674ef270ab1SKenneth D. Merry 	/* Set low latency queueing request */
1675ef270ab1SKenneth D. Merry 	io->low_latency = (flags & OCS_SCSI_LOW_LATENCY) != 0;
1676ef270ab1SKenneth D. Merry 	io->wq_steering = (flags & OCS_SCSI_WQ_STEERING_MASK) >> OCS_SCSI_WQ_STEERING_SHIFT;
1677ef270ab1SKenneth D. Merry 	io->wq_class = (flags & OCS_SCSI_WQ_CLASS_MASK) >> OCS_SCSI_WQ_CLASS_SHIFT;
1678ef270ab1SKenneth D. Merry 
1679ef270ab1SKenneth D. Merry 	if ((scsi_status != 0) || residual || sense_data_length) {
1680ef270ab1SKenneth D. Merry 		fcp_rsp_iu_t *fcprsp = io->rspbuf.virt;
1681ef270ab1SKenneth D. Merry 
1682ef270ab1SKenneth D. Merry 		if (!fcprsp) {
1683ef270ab1SKenneth D. Merry 			ocs_log_err(ocs, "NULL response buffer\n");
1684ef270ab1SKenneth D. Merry 			return -1;
1685ef270ab1SKenneth D. Merry 		}
1686ef270ab1SKenneth D. Merry 
1687ef270ab1SKenneth D. Merry 		auto_resp = FALSE;
1688ef270ab1SKenneth D. Merry 
1689ef270ab1SKenneth D. Merry 		ocs_memset(fcprsp, 0, sizeof(*fcprsp));
1690ef270ab1SKenneth D. Merry 
1691ef270ab1SKenneth D. Merry 		io->wire_len += (sizeof(*fcprsp) - sizeof(fcprsp->data));
1692ef270ab1SKenneth D. Merry 
1693ef270ab1SKenneth D. Merry 		fcprsp->scsi_status = scsi_status;
1694ef270ab1SKenneth D. Merry 		*((uint16_t*)fcprsp->status_qualifier) = ocs_htobe16(scsi_status_qualifier);
1695ef270ab1SKenneth D. Merry 
1696ef270ab1SKenneth D. Merry 		/* set residual status if necessary */
1697ef270ab1SKenneth D. Merry 		if (residual != 0) {
1698ef270ab1SKenneth D. Merry 			/* FCP: if data transferred is less than the amount expected, then this is an
1699ef270ab1SKenneth D. Merry 			 * underflow.  If data transferred would have been greater than the amount expected
1700ef270ab1SKenneth D. Merry 			 * then this is an overflow
1701ef270ab1SKenneth D. Merry 			 */
1702ef270ab1SKenneth D. Merry 			if (residual > 0) {
1703ef270ab1SKenneth D. Merry 				fcprsp->flags |= FCP_RESID_UNDER;
1704ef270ab1SKenneth D. Merry 				*((uint32_t *)fcprsp->fcp_resid) = ocs_htobe32(residual);
1705ef270ab1SKenneth D. Merry 			} else {
1706ef270ab1SKenneth D. Merry 				fcprsp->flags |= FCP_RESID_OVER;
1707ef270ab1SKenneth D. Merry 				*((uint32_t *)fcprsp->fcp_resid) = ocs_htobe32(-residual);
1708ef270ab1SKenneth D. Merry 			}
1709ef270ab1SKenneth D. Merry 		}
1710ef270ab1SKenneth D. Merry 
1711ef270ab1SKenneth D. Merry 		if (sense_data && sense_data_length) {
1712ef270ab1SKenneth D. Merry 			ocs_assert(sense_data_length <= sizeof(fcprsp->data), -1);
1713ef270ab1SKenneth D. Merry 			fcprsp->flags |= FCP_SNS_LEN_VALID;
1714ef270ab1SKenneth D. Merry 			ocs_memcpy(fcprsp->data, sense_data, sense_data_length);
1715ef270ab1SKenneth D. Merry 			*((uint32_t*)fcprsp->fcp_sns_len) = ocs_htobe32(sense_data_length);
1716ef270ab1SKenneth D. Merry 			io->wire_len += sense_data_length;
1717ef270ab1SKenneth D. Merry 		}
1718ef270ab1SKenneth D. Merry 
1719ef270ab1SKenneth D. Merry 		io->sgl[0].addr = io->rspbuf.phys;
1720ef270ab1SKenneth D. Merry 		io->sgl[0].dif_addr = 0;
1721ef270ab1SKenneth D. Merry 		io->sgl[0].len = io->wire_len;
1722ef270ab1SKenneth D. Merry 		io->sgl_count = 1;
1723ef270ab1SKenneth D. Merry 	}
1724ef270ab1SKenneth D. Merry 
1725ef270ab1SKenneth D. Merry 	if (auto_resp) {
1726ef270ab1SKenneth D. Merry 		io->iparam.fcp_tgt.flags |= SLI4_IO_AUTO_GOOD_RESPONSE;
1727ef270ab1SKenneth D. Merry 	}
1728ef270ab1SKenneth D. Merry 
1729ef270ab1SKenneth D. Merry 	return ocs_scsi_io_dispatch(io, ocs_target_io_cb);
1730ef270ab1SKenneth D. Merry }
1731ef270ab1SKenneth D. Merry 
1732ef270ab1SKenneth D. Merry /**
1733ef270ab1SKenneth D. Merry  * @ingroup scsi_api_base
1734ef270ab1SKenneth D. Merry  * @brief Send TMF response data.
1735ef270ab1SKenneth D. Merry  *
1736ef270ab1SKenneth D. Merry  * @par Description
1737ef270ab1SKenneth D. Merry  * This function is used by a target-server to send SCSI TMF response data to a remote
1738ef270ab1SKenneth D. Merry  * initiator node.
1739ef270ab1SKenneth D. Merry  * Upon completion, the callback function @c cb is invoked. The target-server will generally
1740ef270ab1SKenneth D. Merry  * clean up its IO context resources and call ocs_scsi_io_complete().
1741ef270ab1SKenneth D. Merry  *
1742ef270ab1SKenneth D. Merry  * @param io Pointer to the IO context.
1743ef270ab1SKenneth D. Merry  * @param rspcode TMF response code.
1744ef270ab1SKenneth D. Merry  * @param addl_rsp_info Additional TMF response information (may be NULL for zero data).
1745ef270ab1SKenneth D. Merry  * @param cb Completion callback.
1746ef270ab1SKenneth D. Merry  * @param arg Application-specified completion callback argument.
1747ef270ab1SKenneth D. Merry  *
1748ef270ab1SKenneth D. Merry  * @return Returns 0 on success, or a negative error code value on failure.
1749ef270ab1SKenneth D. Merry  */
1750ef270ab1SKenneth D. Merry int32_t
1751ef270ab1SKenneth D. Merry ocs_scsi_send_tmf_resp(ocs_io_t *io, ocs_scsi_tmf_resp_e rspcode, uint8_t addl_rsp_info[3],
1752ef270ab1SKenneth D. Merry 		ocs_scsi_io_cb_t cb, void *arg)
1753ef270ab1SKenneth D. Merry {
1754ef270ab1SKenneth D. Merry 	int32_t rc = -1;
1755ef270ab1SKenneth D. Merry 	ocs_t *ocs = NULL;
1756ef270ab1SKenneth D. Merry 	fcp_rsp_iu_t *fcprsp = NULL;
1757ef270ab1SKenneth D. Merry 	fcp_rsp_info_t *rspinfo = NULL;
1758ef270ab1SKenneth D. Merry 	uint8_t fcp_rspcode;
1759ef270ab1SKenneth D. Merry 
1760ef270ab1SKenneth D. Merry 	ocs_assert(io, -1);
1761ef270ab1SKenneth D. Merry 	ocs_assert(io->ocs, -1);
1762ef270ab1SKenneth D. Merry 	ocs_assert(io->node, -1);
1763ef270ab1SKenneth D. Merry 
1764ef270ab1SKenneth D. Merry 	ocs = io->ocs;
1765ef270ab1SKenneth D. Merry 
1766ef270ab1SKenneth D. Merry 	io->wire_len = 0;
1767ef270ab1SKenneth D. Merry 	ocs_scsi_convert_dif_info(ocs, NULL, &io->hw_dif);
1768ef270ab1SKenneth D. Merry 
1769ef270ab1SKenneth D. Merry 	switch(rspcode) {
1770ef270ab1SKenneth D. Merry 	case OCS_SCSI_TMF_FUNCTION_COMPLETE:
1771ef270ab1SKenneth D. Merry 		fcp_rspcode = FCP_TMF_COMPLETE;
1772ef270ab1SKenneth D. Merry 		break;
1773ef270ab1SKenneth D. Merry 	case OCS_SCSI_TMF_FUNCTION_SUCCEEDED:
1774ef270ab1SKenneth D. Merry 	case OCS_SCSI_TMF_FUNCTION_IO_NOT_FOUND:
1775ef270ab1SKenneth D. Merry 		fcp_rspcode = FCP_TMF_SUCCEEDED;
1776ef270ab1SKenneth D. Merry 		break;
1777ef270ab1SKenneth D. Merry 	case OCS_SCSI_TMF_FUNCTION_REJECTED:
1778ef270ab1SKenneth D. Merry 		fcp_rspcode = FCP_TMF_REJECTED;
1779ef270ab1SKenneth D. Merry 		break;
1780ef270ab1SKenneth D. Merry 	case OCS_SCSI_TMF_INCORRECT_LOGICAL_UNIT_NUMBER:
1781ef270ab1SKenneth D. Merry 		fcp_rspcode = FCP_TMF_INCORRECT_LUN;
1782ef270ab1SKenneth D. Merry 		break;
1783ef270ab1SKenneth D. Merry 	case OCS_SCSI_TMF_SERVICE_DELIVERY:
1784ef270ab1SKenneth D. Merry 		fcp_rspcode = FCP_TMF_FAILED;
1785ef270ab1SKenneth D. Merry 		break;
1786ef270ab1SKenneth D. Merry 	default:
1787ef270ab1SKenneth D. Merry 		fcp_rspcode = FCP_TMF_REJECTED;
1788ef270ab1SKenneth D. Merry 		break;
1789ef270ab1SKenneth D. Merry 	}
1790ef270ab1SKenneth D. Merry 
1791ef270ab1SKenneth D. Merry 	io->hio_type = OCS_HW_IO_TARGET_RSP;
1792ef270ab1SKenneth D. Merry 
1793ef270ab1SKenneth D. Merry 	io->scsi_tgt_cb = cb;
1794ef270ab1SKenneth D. Merry 	io->scsi_tgt_cb_arg = arg;
1795ef270ab1SKenneth D. Merry 
1796ef270ab1SKenneth D. Merry 	if (io->tmf_cmd == OCS_SCSI_TMF_ABORT_TASK) {
1797ef270ab1SKenneth D. Merry 		rc = ocs_target_send_bls_resp(io, cb, arg);
1798ef270ab1SKenneth D. Merry 		return rc;
1799ef270ab1SKenneth D. Merry 	}
1800ef270ab1SKenneth D. Merry 
1801ef270ab1SKenneth D. Merry 	/* populate the FCP TMF response */
1802ef270ab1SKenneth D. Merry 	fcprsp = io->rspbuf.virt;
1803ef270ab1SKenneth D. Merry 	ocs_memset(fcprsp, 0, sizeof(*fcprsp));
1804ef270ab1SKenneth D. Merry 
1805ef270ab1SKenneth D. Merry 	fcprsp->flags |= FCP_RSP_LEN_VALID;
1806ef270ab1SKenneth D. Merry 
1807ef270ab1SKenneth D. Merry 	rspinfo = (fcp_rsp_info_t*) fcprsp->data;
1808ef270ab1SKenneth D. Merry 	if (addl_rsp_info != NULL) {
1809ef270ab1SKenneth D. Merry 		ocs_memcpy(rspinfo->addl_rsp_info, addl_rsp_info, sizeof(rspinfo->addl_rsp_info));
1810ef270ab1SKenneth D. Merry 	}
1811ef270ab1SKenneth D. Merry 	rspinfo->rsp_code = fcp_rspcode;
1812ef270ab1SKenneth D. Merry 
1813ef270ab1SKenneth D. Merry 	io->wire_len = sizeof(*fcprsp) - sizeof(fcprsp->data) + sizeof(*rspinfo);
1814ef270ab1SKenneth D. Merry 
1815ef270ab1SKenneth D. Merry 	*((uint32_t*)fcprsp->fcp_rsp_len) = ocs_htobe32(sizeof(*rspinfo));
1816ef270ab1SKenneth D. Merry 
1817ef270ab1SKenneth D. Merry 	io->sgl[0].addr = io->rspbuf.phys;
1818ef270ab1SKenneth D. Merry 	io->sgl[0].dif_addr = 0;
1819ef270ab1SKenneth D. Merry 	io->sgl[0].len = io->wire_len;
1820ef270ab1SKenneth D. Merry 	io->sgl_count = 1;
1821ef270ab1SKenneth D. Merry 
1822ef270ab1SKenneth D. Merry 	ocs_memset(&io->iparam, 0, sizeof(io->iparam));
1823ef270ab1SKenneth D. Merry 	io->iparam.fcp_tgt.ox_id = io->init_task_tag;
1824ef270ab1SKenneth D. Merry 	io->iparam.fcp_tgt.offset = 0;
1825ef270ab1SKenneth D. Merry 	io->iparam.fcp_tgt.cs_ctl = io->cs_ctl;
1826ef270ab1SKenneth D. Merry 	io->iparam.fcp_tgt.timeout = io->timeout;
1827ef270ab1SKenneth D. Merry 
1828ef270ab1SKenneth D. Merry 	rc = ocs_scsi_io_dispatch(io, ocs_target_io_cb);
1829ef270ab1SKenneth D. Merry 
1830ef270ab1SKenneth D. Merry 	return rc;
1831ef270ab1SKenneth D. Merry }
1832ef270ab1SKenneth D. Merry 
1833ef270ab1SKenneth D. Merry /**
1834ef270ab1SKenneth D. Merry  * @brief Process target abort callback.
1835ef270ab1SKenneth D. Merry  *
1836ef270ab1SKenneth D. Merry  * @par Description
1837ef270ab1SKenneth D. Merry  * Accepts HW abort requests.
1838ef270ab1SKenneth D. Merry  *
1839ef270ab1SKenneth D. Merry  * @param hio HW IO context.
1840ef270ab1SKenneth D. Merry  * @param rnode Remote node.
1841ef270ab1SKenneth D. Merry  * @param length Length of response data.
1842ef270ab1SKenneth D. Merry  * @param status Completion status.
1843ef270ab1SKenneth D. Merry  * @param ext_status Extended completion status.
1844ef270ab1SKenneth D. Merry  * @param app Application-specified callback data.
1845ef270ab1SKenneth D. Merry  *
1846ef270ab1SKenneth D. Merry  * @return Returns 0 on success, or a negative error code value on failure.
1847ef270ab1SKenneth D. Merry  */
1848ef270ab1SKenneth D. Merry 
1849ef270ab1SKenneth D. Merry static int32_t
1850ef270ab1SKenneth D. Merry ocs_target_abort_cb(ocs_hw_io_t *hio, ocs_remote_node_t *rnode, uint32_t length, int32_t status, uint32_t ext_status, void *app)
1851ef270ab1SKenneth D. Merry {
1852ef270ab1SKenneth D. Merry 	ocs_io_t *io = app;
1853ef270ab1SKenneth D. Merry 	ocs_t *ocs;
1854ef270ab1SKenneth D. Merry 	ocs_scsi_io_status_e scsi_status;
1855ef270ab1SKenneth D. Merry 
1856ef270ab1SKenneth D. Merry 	ocs_assert(io, -1);
1857ef270ab1SKenneth D. Merry 	ocs_assert(io->ocs, -1);
1858ef270ab1SKenneth D. Merry 
1859ef270ab1SKenneth D. Merry 	ocs = io->ocs;
1860ef270ab1SKenneth D. Merry 
1861ef270ab1SKenneth D. Merry 	if (io->abort_cb) {
1862ef270ab1SKenneth D. Merry 		ocs_scsi_io_cb_t abort_cb = io->abort_cb;
1863ef270ab1SKenneth D. Merry 		void *abort_cb_arg = io->abort_cb_arg;
1864ef270ab1SKenneth D. Merry 
1865ef270ab1SKenneth D. Merry 		io->abort_cb = NULL;
1866ef270ab1SKenneth D. Merry 		io->abort_cb_arg = NULL;
1867ef270ab1SKenneth D. Merry 
1868ef270ab1SKenneth D. Merry 		switch (status) {
1869ef270ab1SKenneth D. Merry 		case SLI4_FC_WCQE_STATUS_SUCCESS:
1870ef270ab1SKenneth D. Merry 			scsi_status = OCS_SCSI_STATUS_GOOD;
1871ef270ab1SKenneth D. Merry 			break;
1872ef270ab1SKenneth D. Merry 		case SLI4_FC_WCQE_STATUS_LOCAL_REJECT:
1873ef270ab1SKenneth D. Merry 			switch (ext_status) {
1874ef270ab1SKenneth D. Merry 			case SLI4_FC_LOCAL_REJECT_NO_XRI:
1875ef270ab1SKenneth D. Merry 				scsi_status = OCS_SCSI_STATUS_NO_IO;
1876ef270ab1SKenneth D. Merry 				break;
1877ef270ab1SKenneth D. Merry 			case SLI4_FC_LOCAL_REJECT_ABORT_IN_PROGRESS:
1878ef270ab1SKenneth D. Merry 				scsi_status = OCS_SCSI_STATUS_ABORT_IN_PROGRESS;
1879ef270ab1SKenneth D. Merry 				break;
1880ef270ab1SKenneth D. Merry 			default:
1881ef270ab1SKenneth D. Merry 				/* TODO: we have seen 0x15 (abort in progress) */
1882ef270ab1SKenneth D. Merry 				scsi_status = OCS_SCSI_STATUS_ERROR;
1883ef270ab1SKenneth D. Merry 				break;
1884ef270ab1SKenneth D. Merry 			}
1885ef270ab1SKenneth D. Merry 			break;
1886ef270ab1SKenneth D. Merry 		case SLI4_FC_WCQE_STATUS_FCP_RSP_FAILURE:
1887ef270ab1SKenneth D. Merry 			scsi_status = OCS_SCSI_STATUS_CHECK_RESPONSE;
1888ef270ab1SKenneth D. Merry 			break;
1889ef270ab1SKenneth D. Merry 		default:
1890ef270ab1SKenneth D. Merry 			scsi_status = OCS_SCSI_STATUS_ERROR;
1891ef270ab1SKenneth D. Merry 			break;
1892ef270ab1SKenneth D. Merry 		}
1893ef270ab1SKenneth D. Merry 		/* invoke callback */
1894ef270ab1SKenneth D. Merry 		abort_cb(io->io_to_abort, scsi_status, 0, abort_cb_arg);
1895ef270ab1SKenneth D. Merry 	}
1896ef270ab1SKenneth D. Merry 
1897ef270ab1SKenneth D. Merry 	ocs_assert(io != io->io_to_abort, -1);
1898ef270ab1SKenneth D. Merry 
1899ef270ab1SKenneth D. Merry 	/* done with IO to abort */
1900ef270ab1SKenneth D. Merry 	ocs_ref_put(&io->io_to_abort->ref); /* ocs_ref_get(): ocs_scsi_tgt_abort_io() */
1901ef270ab1SKenneth D. Merry 
1902ef270ab1SKenneth D. Merry 	ocs_io_free(ocs, io);
1903ef270ab1SKenneth D. Merry 
1904ef270ab1SKenneth D. Merry 	ocs_scsi_check_pending(ocs);
1905ef270ab1SKenneth D. Merry 	return 0;
1906ef270ab1SKenneth D. Merry }
1907ef270ab1SKenneth D. Merry 
1908ef270ab1SKenneth D. Merry /**
1909ef270ab1SKenneth D. Merry  * @ingroup scsi_api_base
1910ef270ab1SKenneth D. Merry  * @brief Abort a target IO.
1911ef270ab1SKenneth D. Merry  *
1912ef270ab1SKenneth D. Merry  * @par Description
1913ef270ab1SKenneth D. Merry  * This routine is called from a SCSI target-server. It initiates an abort of a
1914ef270ab1SKenneth D. Merry  * previously-issued target data phase or response request.
1915ef270ab1SKenneth D. Merry  *
1916ef270ab1SKenneth D. Merry  * @param io IO context.
1917ef270ab1SKenneth D. Merry  * @param cb SCSI target server callback.
1918ef270ab1SKenneth D. Merry  * @param arg SCSI target server supplied callback argument.
1919ef270ab1SKenneth D. Merry  *
1920ef270ab1SKenneth D. Merry  * @return Returns 0 on success, or a non-zero value on failure.
1921ef270ab1SKenneth D. Merry  */
1922ef270ab1SKenneth D. Merry int32_t
1923ef270ab1SKenneth D. Merry ocs_scsi_tgt_abort_io(ocs_io_t *io, ocs_scsi_io_cb_t cb, void *arg)
1924ef270ab1SKenneth D. Merry {
1925ef270ab1SKenneth D. Merry 	ocs_t *ocs;
1926ef270ab1SKenneth D. Merry 	ocs_xport_t *xport;
1927ef270ab1SKenneth D. Merry 	int32_t rc;
1928ef270ab1SKenneth D. Merry 
1929ef270ab1SKenneth D. Merry 	ocs_io_t *abort_io = NULL;
1930ef270ab1SKenneth D. Merry 	ocs_assert(io, -1);
1931ef270ab1SKenneth D. Merry 	ocs_assert(io->node, -1);
1932ef270ab1SKenneth D. Merry 	ocs_assert(io->ocs, -1);
1933ef270ab1SKenneth D. Merry 
1934ef270ab1SKenneth D. Merry 	ocs = io->ocs;
1935ef270ab1SKenneth D. Merry 	xport = ocs->xport;
1936ef270ab1SKenneth D. Merry 
1937ef270ab1SKenneth D. Merry 	/* take a reference on IO being aborted */
1938ef270ab1SKenneth D. Merry 	if ((ocs_ref_get_unless_zero(&io->ref) == 0)) {
1939ef270ab1SKenneth D. Merry 		/* command no longer active */
1940ef270ab1SKenneth D. Merry 		scsi_io_printf(io, "command no longer active\n");
1941ef270ab1SKenneth D. Merry 		return -1;
1942ef270ab1SKenneth D. Merry 	}
1943ef270ab1SKenneth D. Merry 
1944ef270ab1SKenneth D. Merry 	/*
1945ef270ab1SKenneth D. Merry 	 * allocate a new IO to send the abort request. Use ocs_io_alloc() directly, as
1946ef270ab1SKenneth D. Merry 	 * we need an IO object that will not fail allocation due to allocations being
1947ef270ab1SKenneth D. Merry 	 * disabled (in ocs_scsi_io_alloc())
1948ef270ab1SKenneth D. Merry 	 */
1949ef270ab1SKenneth D. Merry 	abort_io = ocs_io_alloc(ocs);
1950ef270ab1SKenneth D. Merry 	if (abort_io == NULL) {
1951ef270ab1SKenneth D. Merry 		ocs_atomic_add_return(&xport->io_alloc_failed_count, 1);
1952ef270ab1SKenneth D. Merry 		ocs_ref_put(&io->ref); /* ocs_ref_get(): same function */
1953ef270ab1SKenneth D. Merry 		return -1;
1954ef270ab1SKenneth D. Merry 	}
1955ef270ab1SKenneth D. Merry 
1956ef270ab1SKenneth D. Merry 	/* Save the target server callback and argument */
1957ef270ab1SKenneth D. Merry 	ocs_assert(abort_io->hio == NULL, -1);
1958ef270ab1SKenneth D. Merry 
1959ef270ab1SKenneth D. Merry 	/* set generic fields */
1960ef270ab1SKenneth D. Merry 	abort_io->cmd_tgt = TRUE;
1961ef270ab1SKenneth D. Merry 	abort_io->node = io->node;
1962ef270ab1SKenneth D. Merry 
1963ef270ab1SKenneth D. Merry 	/* set type and abort-specific fields */
1964ef270ab1SKenneth D. Merry 	abort_io->io_type = OCS_IO_TYPE_ABORT;
1965ef270ab1SKenneth D. Merry 	abort_io->display_name = "tgt_abort";
1966ef270ab1SKenneth D. Merry 	abort_io->io_to_abort = io;
1967ef270ab1SKenneth D. Merry 	abort_io->send_abts = FALSE;
1968ef270ab1SKenneth D. Merry 	abort_io->abort_cb = cb;
1969ef270ab1SKenneth D. Merry 	abort_io->abort_cb_arg = arg;
1970ef270ab1SKenneth D. Merry 
1971ef270ab1SKenneth D. Merry 	/* now dispatch IO */
1972ef270ab1SKenneth D. Merry 	rc = ocs_scsi_io_dispatch_abort(abort_io, ocs_target_abort_cb);
1973ef270ab1SKenneth D. Merry 	if (rc) {
1974ef270ab1SKenneth D. Merry 		ocs_ref_put(&io->ref); /* ocs_ref_get(): same function */
1975ef270ab1SKenneth D. Merry 	}
1976ef270ab1SKenneth D. Merry 	return rc;
1977ef270ab1SKenneth D. Merry }
1978ef270ab1SKenneth D. Merry 
1979ef270ab1SKenneth D. Merry /**
1980ef270ab1SKenneth D. Merry  * @brief Process target BLS response callback.
1981ef270ab1SKenneth D. Merry  *
1982ef270ab1SKenneth D. Merry  * @par Description
1983ef270ab1SKenneth D. Merry  * Accepts HW abort requests.
1984ef270ab1SKenneth D. Merry  *
1985ef270ab1SKenneth D. Merry  * @param hio HW IO context.
1986ef270ab1SKenneth D. Merry  * @param rnode Remote node.
1987ef270ab1SKenneth D. Merry  * @param length Length of response data.
1988ef270ab1SKenneth D. Merry  * @param status Completion status.
1989ef270ab1SKenneth D. Merry  * @param ext_status Extended completion status.
1990ef270ab1SKenneth D. Merry  * @param app Application-specified callback data.
1991ef270ab1SKenneth D. Merry  *
1992ef270ab1SKenneth D. Merry  * @return Returns 0 on success, or a negative error code value on failure.
1993ef270ab1SKenneth D. Merry  */
1994ef270ab1SKenneth D. Merry 
1995ef270ab1SKenneth D. Merry static int32_t
1996ef270ab1SKenneth D. Merry ocs_target_bls_resp_cb(ocs_hw_io_t *hio, ocs_remote_node_t *rnode, uint32_t length, int32_t status, uint32_t ext_status, void *app)
1997ef270ab1SKenneth D. Merry {
1998ef270ab1SKenneth D. Merry 	ocs_io_t *io = app;
1999ef270ab1SKenneth D. Merry 	ocs_t *ocs;
2000ef270ab1SKenneth D. Merry 	ocs_scsi_io_status_e bls_status;
2001ef270ab1SKenneth D. Merry 
2002ef270ab1SKenneth D. Merry 	ocs_assert(io, -1);
2003ef270ab1SKenneth D. Merry 	ocs_assert(io->ocs, -1);
2004ef270ab1SKenneth D. Merry 
2005ef270ab1SKenneth D. Merry 	ocs = io->ocs;
2006ef270ab1SKenneth D. Merry 
2007ef270ab1SKenneth D. Merry 	/* BLS isn't really a "SCSI" concept, but use SCSI status */
2008ef270ab1SKenneth D. Merry 	if (status) {
2009ef270ab1SKenneth D. Merry 		io_error_log(io, "s=%#x x=%#x\n", status, ext_status);
2010ef270ab1SKenneth D. Merry 		bls_status = OCS_SCSI_STATUS_ERROR;
2011ef270ab1SKenneth D. Merry 	} else {
2012ef270ab1SKenneth D. Merry 		bls_status = OCS_SCSI_STATUS_GOOD;
2013ef270ab1SKenneth D. Merry 	}
2014ef270ab1SKenneth D. Merry 
2015ef270ab1SKenneth D. Merry 	if (io->bls_cb) {
2016ef270ab1SKenneth D. Merry 		ocs_scsi_io_cb_t bls_cb = io->bls_cb;
2017ef270ab1SKenneth D. Merry 		void *bls_cb_arg = io->bls_cb_arg;
2018ef270ab1SKenneth D. Merry 
2019ef270ab1SKenneth D. Merry 		io->bls_cb = NULL;
2020ef270ab1SKenneth D. Merry 		io->bls_cb_arg = NULL;
2021ef270ab1SKenneth D. Merry 
2022ef270ab1SKenneth D. Merry 		/* invoke callback */
2023ef270ab1SKenneth D. Merry 		bls_cb(io, bls_status, 0, bls_cb_arg);
2024ef270ab1SKenneth D. Merry 	}
2025ef270ab1SKenneth D. Merry 
2026ef270ab1SKenneth D. Merry 	ocs_scsi_check_pending(ocs);
2027ef270ab1SKenneth D. Merry 	return 0;
2028ef270ab1SKenneth D. Merry }
2029ef270ab1SKenneth D. Merry 
2030ef270ab1SKenneth D. Merry /**
2031ef270ab1SKenneth D. Merry  * @brief Complete abort request.
2032ef270ab1SKenneth D. Merry  *
2033ef270ab1SKenneth D. Merry  * @par Description
2034ef270ab1SKenneth D. Merry  * An abort request is completed by posting a BA_ACC for the IO that requested the abort.
2035ef270ab1SKenneth D. Merry  *
2036ef270ab1SKenneth D. Merry  * @param io Pointer to the IO context.
2037ef270ab1SKenneth D. Merry  * @param cb Callback function to invoke upon completion.
2038ef270ab1SKenneth D. Merry  * @param arg Application-specified completion callback argument.
2039ef270ab1SKenneth D. Merry  *
2040ef270ab1SKenneth D. Merry  * @return Returns 0 on success, or a negative error code value on failure.
2041ef270ab1SKenneth D. Merry  */
2042ef270ab1SKenneth D. Merry 
2043ef270ab1SKenneth D. Merry static int32_t
2044ef270ab1SKenneth D. Merry ocs_target_send_bls_resp(ocs_io_t *io, ocs_scsi_io_cb_t cb, void *arg)
2045ef270ab1SKenneth D. Merry {
2046ef270ab1SKenneth D. Merry 	int32_t rc;
2047ef270ab1SKenneth D. Merry 	fc_ba_acc_payload_t *acc;
2048ef270ab1SKenneth D. Merry 
2049ef270ab1SKenneth D. Merry 	ocs_assert(io, -1);
2050ef270ab1SKenneth D. Merry 
2051ef270ab1SKenneth D. Merry 	/* fill out IO structure with everything needed to send BA_ACC */
2052ef270ab1SKenneth D. Merry 	ocs_memset(&io->iparam, 0, sizeof(io->iparam));
2053ef270ab1SKenneth D. Merry 	io->iparam.bls.ox_id = io->init_task_tag;
2054ef270ab1SKenneth D. Merry 	io->iparam.bls.rx_id = io->abort_rx_id;
2055ef270ab1SKenneth D. Merry 
2056ef270ab1SKenneth D. Merry 	acc = (void *)io->iparam.bls.payload;
2057ef270ab1SKenneth D. Merry 
2058ef270ab1SKenneth D. Merry 	ocs_memset(io->iparam.bls.payload, 0, sizeof(io->iparam.bls.payload));
2059ef270ab1SKenneth D. Merry 	acc->ox_id = io->iparam.bls.ox_id;
2060ef270ab1SKenneth D. Merry 	acc->rx_id = io->iparam.bls.rx_id;
2061ef270ab1SKenneth D. Merry 	acc->high_seq_cnt = UINT16_MAX;
2062ef270ab1SKenneth D. Merry 
2063ef270ab1SKenneth D. Merry 	/* generic io fields have already been populated */
2064ef270ab1SKenneth D. Merry 
2065ef270ab1SKenneth D. Merry 	/* set type and BLS-specific fields */
2066ef270ab1SKenneth D. Merry 	io->io_type = OCS_IO_TYPE_BLS_RESP;
2067ef270ab1SKenneth D. Merry 	io->display_name = "bls_rsp";
2068ef270ab1SKenneth D. Merry 	io->hio_type = OCS_HW_BLS_ACC;
2069ef270ab1SKenneth D. Merry 	io->bls_cb = cb;
2070ef270ab1SKenneth D. Merry 	io->bls_cb_arg = arg;
2071ef270ab1SKenneth D. Merry 
2072ef270ab1SKenneth D. Merry 	/* dispatch IO */
2073ef270ab1SKenneth D. Merry 	rc = ocs_scsi_io_dispatch(io, ocs_target_bls_resp_cb);
2074ef270ab1SKenneth D. Merry 	return rc;
2075ef270ab1SKenneth D. Merry }
2076ef270ab1SKenneth D. Merry 
2077ef270ab1SKenneth D. Merry /**
2078ef270ab1SKenneth D. Merry  * @ingroup scsi_api_base
2079ef270ab1SKenneth D. Merry  * @brief Notify the base driver that the IO is complete.
2080ef270ab1SKenneth D. Merry  *
2081ef270ab1SKenneth D. Merry  * @par Description
2082ef270ab1SKenneth D. Merry  * This function is called by a target-server to notify the base driver that an IO
2083ef270ab1SKenneth D. Merry  * has completed, allowing for the base driver to free resources.
2084ef270ab1SKenneth D. Merry  * @n
2085ef270ab1SKenneth D. Merry  * @n @b Note: This function is not called by initiator-clients.
2086ef270ab1SKenneth D. Merry  *
2087ef270ab1SKenneth D. Merry  * @param io Pointer to IO context.
2088ef270ab1SKenneth D. Merry  *
2089ef270ab1SKenneth D. Merry  * @return None.
2090ef270ab1SKenneth D. Merry  */
2091ef270ab1SKenneth D. Merry void
2092ef270ab1SKenneth D. Merry ocs_scsi_io_complete(ocs_io_t *io)
2093ef270ab1SKenneth D. Merry {
2094ef270ab1SKenneth D. Merry 	ocs_assert(io);
2095ef270ab1SKenneth D. Merry 
2096ef270ab1SKenneth D. Merry 	if (!ocs_io_busy(io)) {
2097ef270ab1SKenneth D. Merry 		ocs_log_test(io->ocs, "Got completion for non-busy io with tag 0x%x\n", io->tag);
2098ef270ab1SKenneth D. Merry 		return;
2099ef270ab1SKenneth D. Merry 	}
2100ef270ab1SKenneth D. Merry 
2101ef270ab1SKenneth D. Merry 	scsi_io_trace(io, "freeing io 0x%p %s\n", io, io->display_name);
2102ef270ab1SKenneth D. Merry 	ocs_assert(ocs_ref_read_count(&io->ref) > 0);
2103ef270ab1SKenneth D. Merry 	ocs_ref_put(&io->ref); /* ocs_ref_get(): ocs_scsi_io_alloc() */
2104ef270ab1SKenneth D. Merry }
2105ef270ab1SKenneth D. Merry 
2106ef270ab1SKenneth D. Merry /**
2107ef270ab1SKenneth D. Merry  * @brief Handle initiator IO completion.
2108ef270ab1SKenneth D. Merry  *
2109ef270ab1SKenneth D. Merry  * @par Description
2110ef270ab1SKenneth D. Merry  * This callback is made upon completion of an initiator operation (initiator read/write command).
2111ef270ab1SKenneth D. Merry  *
2112ef270ab1SKenneth D. Merry  * @param hio HW IO context.
2113ef270ab1SKenneth D. Merry  * @param rnode Remote node.
2114ef270ab1SKenneth D. Merry  * @param length Length of completion data.
2115ef270ab1SKenneth D. Merry  * @param status Completion status.
2116ef270ab1SKenneth D. Merry  * @param ext_status Extended completion status.
2117ef270ab1SKenneth D. Merry  * @param app Application-specified callback data.
2118ef270ab1SKenneth D. Merry  *
2119ef270ab1SKenneth D. Merry  * @return None.
2120ef270ab1SKenneth D. Merry  */
2121ef270ab1SKenneth D. Merry 
2122ef270ab1SKenneth D. Merry static void
2123ef270ab1SKenneth D. Merry ocs_initiator_io_cb(ocs_hw_io_t *hio, ocs_remote_node_t *rnode, uint32_t length,
2124ef270ab1SKenneth D. Merry 	int32_t status, uint32_t ext_status, void *app)
2125ef270ab1SKenneth D. Merry {
2126ef270ab1SKenneth D. Merry 	ocs_io_t *io = app;
2127ef270ab1SKenneth D. Merry 	ocs_t *ocs;
2128ef270ab1SKenneth D. Merry 	ocs_scsi_io_status_e scsi_status;
2129ef270ab1SKenneth D. Merry 
2130ef270ab1SKenneth D. Merry 	ocs_assert(io);
2131ef270ab1SKenneth D. Merry 	ocs_assert(io->scsi_ini_cb);
2132ef270ab1SKenneth D. Merry 
2133ef270ab1SKenneth D. Merry 	scsi_io_trace(io, "status x%x ext_status x%x\n", status, ext_status);
2134ef270ab1SKenneth D. Merry 
2135ef270ab1SKenneth D. Merry 	ocs = io->ocs;
2136ef270ab1SKenneth D. Merry 	ocs_assert(ocs);
2137ef270ab1SKenneth D. Merry 
2138ef270ab1SKenneth D. Merry 	ocs_scsi_io_free_ovfl(io);
2139ef270ab1SKenneth D. Merry 
2140ef270ab1SKenneth D. Merry 	/* Call target server completion */
2141ef270ab1SKenneth D. Merry 	if (io->scsi_ini_cb) {
2142ef270ab1SKenneth D. Merry 		fcp_rsp_iu_t *fcprsp = io->rspbuf.virt;
2143ef270ab1SKenneth D. Merry 		ocs_scsi_cmd_resp_t rsp;
2144ef270ab1SKenneth D. Merry 		ocs_scsi_rsp_io_cb_t cb = io->scsi_ini_cb;
2145ef270ab1SKenneth D. Merry 		uint32_t flags = 0;
2146ef270ab1SKenneth D. Merry 		uint8_t *pd = fcprsp->data;
2147ef270ab1SKenneth D. Merry 
2148ef270ab1SKenneth D. Merry 		/* Clear the callback before invoking the callback */
2149ef270ab1SKenneth D. Merry 		io->scsi_ini_cb = NULL;
2150ef270ab1SKenneth D. Merry 
2151ef270ab1SKenneth D. Merry 		ocs_memset(&rsp, 0, sizeof(rsp));
2152ef270ab1SKenneth D. Merry 
2153ef270ab1SKenneth D. Merry 		/* Unless status is FCP_RSP_FAILURE, fcprsp is not filled in */
2154ef270ab1SKenneth D. Merry 		switch (status) {
2155ef270ab1SKenneth D. Merry 		case SLI4_FC_WCQE_STATUS_SUCCESS:
2156ef270ab1SKenneth D. Merry 			scsi_status = OCS_SCSI_STATUS_GOOD;
2157ef270ab1SKenneth D. Merry 			break;
2158ef270ab1SKenneth D. Merry 		case SLI4_FC_WCQE_STATUS_FCP_RSP_FAILURE:
2159ef270ab1SKenneth D. Merry 			scsi_status = OCS_SCSI_STATUS_CHECK_RESPONSE;
2160ef270ab1SKenneth D. Merry 			rsp.scsi_status = fcprsp->scsi_status;
2161ef270ab1SKenneth D. Merry 			rsp.scsi_status_qualifier = ocs_be16toh(*((uint16_t*)fcprsp->status_qualifier));
2162ef270ab1SKenneth D. Merry 
2163ef270ab1SKenneth D. Merry 			if (fcprsp->flags & FCP_RSP_LEN_VALID) {
2164ef270ab1SKenneth D. Merry 				rsp.response_data = pd;
2165ef270ab1SKenneth D. Merry 				rsp.response_data_length = ocs_fc_getbe32(fcprsp->fcp_rsp_len);
2166ef270ab1SKenneth D. Merry 				pd += rsp.response_data_length;
2167ef270ab1SKenneth D. Merry 			}
2168ef270ab1SKenneth D. Merry 			if (fcprsp->flags & FCP_SNS_LEN_VALID) {
2169ef270ab1SKenneth D. Merry 				uint32_t sns_len = ocs_fc_getbe32(fcprsp->fcp_sns_len);
2170ef270ab1SKenneth D. Merry 				rsp.sense_data = pd;
2171ef270ab1SKenneth D. Merry 				rsp.sense_data_length = sns_len;
2172ef270ab1SKenneth D. Merry 				pd += sns_len;
2173ef270ab1SKenneth D. Merry 			}
2174ef270ab1SKenneth D. Merry 			/* Set residual */
2175ef270ab1SKenneth D. Merry 			if (fcprsp->flags & FCP_RESID_OVER) {
2176ef270ab1SKenneth D. Merry 				rsp.residual = -ocs_fc_getbe32(fcprsp->fcp_resid);
2177ef270ab1SKenneth D. Merry 				rsp.response_wire_length = length;
2178ef270ab1SKenneth D. Merry 			} else	if (fcprsp->flags & FCP_RESID_UNDER) {
2179ef270ab1SKenneth D. Merry 				rsp.residual = ocs_fc_getbe32(fcprsp->fcp_resid);
2180ef270ab1SKenneth D. Merry 				rsp.response_wire_length = length;
2181ef270ab1SKenneth D. Merry 			}
2182ef270ab1SKenneth D. Merry 
2183ef270ab1SKenneth D. Merry 			/*
2184ef270ab1SKenneth D. Merry 			 * Note: The FCP_RSP_FAILURE can be returned for initiator IOs when the total data
2185ef270ab1SKenneth D. Merry 			 * placed does not match the requested length even if the status is good. If
2186ef270ab1SKenneth D. Merry 			 * the status is all zeroes, then we have to assume that a frame(s) were
2187ef270ab1SKenneth D. Merry 			 * dropped and change the status to LOCAL_REJECT/OUT_OF_ORDER_DATA
2188ef270ab1SKenneth D. Merry 			 */
2189ef270ab1SKenneth D. Merry 			if (length != io->wire_len) {
2190ef270ab1SKenneth D. Merry 				uint32_t rsp_len = ext_status;
2191ef270ab1SKenneth D. Merry 				uint8_t *rsp_bytes = io->rspbuf.virt;
2192ef270ab1SKenneth D. Merry 				uint32_t i;
2193ef270ab1SKenneth D. Merry 				uint8_t all_zeroes = (rsp_len > 0);
2194ef270ab1SKenneth D. Merry 				/* Check if the rsp is zero */
2195ef270ab1SKenneth D. Merry 				for (i = 0; i < rsp_len; i++) {
2196ef270ab1SKenneth D. Merry 					if (rsp_bytes[i] != 0) {
2197ef270ab1SKenneth D. Merry 						all_zeroes = FALSE;
2198ef270ab1SKenneth D. Merry 						break;
2199ef270ab1SKenneth D. Merry 					}
2200ef270ab1SKenneth D. Merry 				}
2201ef270ab1SKenneth D. Merry 				if (all_zeroes) {
2202ef270ab1SKenneth D. Merry 					scsi_status = OCS_SCSI_STATUS_ERROR;
2203ef270ab1SKenneth D. Merry 					ocs_log_test(io->ocs, "[%s]" SCSI_IOFMT "local reject=0x%02x\n",
2204ef270ab1SKenneth D. Merry 						     io->node->display_name, SCSI_IOFMT_ARGS(io),
2205ef270ab1SKenneth D. Merry 						     SLI4_FC_LOCAL_REJECT_OUT_OF_ORDER_DATA);
2206ef270ab1SKenneth D. Merry 				}
2207ef270ab1SKenneth D. Merry 			}
2208ef270ab1SKenneth D. Merry 			break;
2209ef270ab1SKenneth D. Merry 		case SLI4_FC_WCQE_STATUS_LOCAL_REJECT:
2210ef270ab1SKenneth D. Merry 			if (ext_status == SLI4_FC_LOCAL_REJECT_SEQUENCE_TIMEOUT) {
2211ef270ab1SKenneth D. Merry 				scsi_status = OCS_SCSI_STATUS_COMMAND_TIMEOUT;
2212ef270ab1SKenneth D. Merry 			} else {
2213ef270ab1SKenneth D. Merry 				scsi_status = OCS_SCSI_STATUS_ERROR;
2214ef270ab1SKenneth D. Merry 			}
2215ef270ab1SKenneth D. Merry 			break;
2216ef270ab1SKenneth D. Merry 		case SLI4_FC_WCQE_STATUS_DI_ERROR:
2217ef270ab1SKenneth D. Merry 			if (ext_status & 0x01) {
2218ef270ab1SKenneth D. Merry 				scsi_status = OCS_SCSI_STATUS_DIF_GUARD_ERROR;
2219ef270ab1SKenneth D. Merry 			} else if (ext_status & 0x02) {
2220ef270ab1SKenneth D. Merry 				scsi_status = OCS_SCSI_STATUS_DIF_APP_TAG_ERROR;
2221ef270ab1SKenneth D. Merry 			} else if (ext_status & 0x04) {
2222ef270ab1SKenneth D. Merry 				scsi_status = OCS_SCSI_STATUS_DIF_REF_TAG_ERROR;
2223ef270ab1SKenneth D. Merry 			} else {
2224ef270ab1SKenneth D. Merry 				scsi_status = OCS_SCSI_STATUS_DIF_UNKNOWN_ERROR;
2225ef270ab1SKenneth D. Merry 			}
2226ef270ab1SKenneth D. Merry 			break;
2227ef270ab1SKenneth D. Merry 		default:
2228ef270ab1SKenneth D. Merry 			scsi_status = OCS_SCSI_STATUS_ERROR;
2229ef270ab1SKenneth D. Merry 			break;
2230ef270ab1SKenneth D. Merry 		}
2231ef270ab1SKenneth D. Merry 
2232ef270ab1SKenneth D. Merry 		cb(io, scsi_status, &rsp, flags, io->scsi_ini_cb_arg);
2233ef270ab1SKenneth D. Merry 	}
2234ef270ab1SKenneth D. Merry 	ocs_scsi_check_pending(ocs);
2235ef270ab1SKenneth D. Merry }
2236ef270ab1SKenneth D. Merry 
2237ef270ab1SKenneth D. Merry /**
2238ef270ab1SKenneth D. Merry  * @ingroup scsi_api_base
2239ef270ab1SKenneth D. Merry  * @brief Initiate initiator read IO.
2240ef270ab1SKenneth D. Merry  *
2241ef270ab1SKenneth D. Merry  * @par Description
2242ef270ab1SKenneth D. Merry  * This call is made by an initiator-client to send a SCSI read command. The payload
2243ef270ab1SKenneth D. Merry  * for the command is given by a scatter-gather list @c sgl for @c sgl_count
2244ef270ab1SKenneth D. Merry  * entries.
2245ef270ab1SKenneth D. Merry  * @n @n
2246ef270ab1SKenneth D. Merry  * Upon completion, the callback @b cb is invoked and passed request status.
2247ef270ab1SKenneth D. Merry  * If the command completed successfully, the callback is given SCSI response data.
2248ef270ab1SKenneth D. Merry  *
2249ef270ab1SKenneth D. Merry  * @param node Pointer to the node.
2250ef270ab1SKenneth D. Merry  * @param io Pointer to the IO context.
2251ef270ab1SKenneth D. Merry  * @param lun LUN value.
2252ef270ab1SKenneth D. Merry  * @param cdb Pointer to the CDB.
2253ef270ab1SKenneth D. Merry  * @param cdb_len Length of the CDB.
2254ef270ab1SKenneth D. Merry  * @param dif_info Pointer to the T10 DIF fields, or NULL if no DIF.
2255ef270ab1SKenneth D. Merry  * @param sgl Pointer to the scatter-gather list.
2256ef270ab1SKenneth D. Merry  * @param sgl_count Count of the scatter-gather list elements.
2257ef270ab1SKenneth D. Merry  * @param wire_len Length of the payload.
2258ef270ab1SKenneth D. Merry  * @param cb Completion callback.
2259ef270ab1SKenneth D. Merry  * @param arg Application-specified completion callback argument.
2260ef270ab1SKenneth D. Merry  *
2261ef270ab1SKenneth D. Merry  * @return Returns 0 on success, or a negative error code value on failure.
2262ef270ab1SKenneth D. Merry  */
2263ef270ab1SKenneth D. Merry int32_t
2264ef270ab1SKenneth D. Merry ocs_scsi_send_rd_io(ocs_node_t *node, ocs_io_t *io, uint64_t lun, void *cdb, uint32_t cdb_len,
2265ef270ab1SKenneth D. Merry 	ocs_scsi_dif_info_t *dif_info,
2266ef270ab1SKenneth D. Merry 	ocs_scsi_sgl_t *sgl, uint32_t sgl_count, uint32_t wire_len,
226788364968SAlexander Motin 	ocs_scsi_rsp_io_cb_t cb, void *arg, uint32_t flags)
2268ef270ab1SKenneth D. Merry {
2269ef270ab1SKenneth D. Merry 	int32_t rc;
2270ef270ab1SKenneth D. Merry 
2271ef270ab1SKenneth D. Merry 	rc = ocs_scsi_send_io(OCS_HW_IO_INITIATOR_READ, node, io, lun, 0, cdb, cdb_len, dif_info, sgl, sgl_count,
227288364968SAlexander Motin 			      wire_len, 0, cb, arg, flags);
2273ef270ab1SKenneth D. Merry 
2274ef270ab1SKenneth D. Merry 	return rc;
2275ef270ab1SKenneth D. Merry }
2276ef270ab1SKenneth D. Merry 
2277ef270ab1SKenneth D. Merry /**
2278ef270ab1SKenneth D. Merry  * @ingroup scsi_api_base
2279ef270ab1SKenneth D. Merry  * @brief Initiate initiator write IO.
2280ef270ab1SKenneth D. Merry  *
2281ef270ab1SKenneth D. Merry  * @par Description
2282ef270ab1SKenneth D. Merry  * This call is made by an initiator-client to send a SCSI write command. The payload
2283ef270ab1SKenneth D. Merry  * for the command is given by a scatter-gather list @c sgl for @c sgl_count
2284ef270ab1SKenneth D. Merry  * entries.
2285ef270ab1SKenneth D. Merry  * @n @n
2286ef270ab1SKenneth D. Merry  * Upon completion, the callback @c cb is invoked and passed request status. If the command
2287ef270ab1SKenneth D. Merry  * completed successfully, the callback is given SCSI response data.
2288ef270ab1SKenneth D. Merry  *
2289ef270ab1SKenneth D. Merry  * @param node Pointer to the node.
2290ef270ab1SKenneth D. Merry  * @param io Pointer to IO context.
2291ef270ab1SKenneth D. Merry  * @param lun LUN value.
2292ef270ab1SKenneth D. Merry  * @param cdb Pointer to the CDB.
2293ef270ab1SKenneth D. Merry  * @param cdb_len Length of the CDB.
2294ef270ab1SKenneth D. Merry  * @param dif_info Pointer to the T10 DIF fields, or NULL if no DIF.
2295ef270ab1SKenneth D. Merry  * @param sgl Pointer to the scatter-gather list.
2296ef270ab1SKenneth D. Merry  * @param sgl_count Count of the scatter-gather list elements.
2297ef270ab1SKenneth D. Merry  * @param wire_len Length of the payload.
2298ef270ab1SKenneth D. Merry  * @param cb Completion callback.
2299ef270ab1SKenneth D. Merry  * @param arg Application-specified completion callback argument.
2300ef270ab1SKenneth D. Merry  *
2301ef270ab1SKenneth D. Merry  * @return Returns 0 on success, or a negative error code value on failure.
2302ef270ab1SKenneth D. Merry  */
2303ef270ab1SKenneth D. Merry int32_t ocs_scsi_send_wr_io(ocs_node_t *node, ocs_io_t *io, uint64_t lun, void *cdb, uint32_t cdb_len,
2304ef270ab1SKenneth D. Merry 	ocs_scsi_dif_info_t *dif_info,
2305ef270ab1SKenneth D. Merry 	ocs_scsi_sgl_t *sgl, uint32_t sgl_count, uint32_t wire_len,
230688364968SAlexander Motin 	ocs_scsi_rsp_io_cb_t cb, void *arg, uint32_t flags)
2307ef270ab1SKenneth D. Merry {
2308ef270ab1SKenneth D. Merry 	int32_t rc;
2309ef270ab1SKenneth D. Merry 
2310ef270ab1SKenneth D. Merry 	rc = ocs_scsi_send_io(OCS_HW_IO_INITIATOR_WRITE, node, io, lun, 0, cdb, cdb_len, dif_info, sgl, sgl_count,
231188364968SAlexander Motin 			      wire_len, 0, cb, arg, flags);
2312ef270ab1SKenneth D. Merry 
2313ef270ab1SKenneth D. Merry 	return rc;
2314ef270ab1SKenneth D. Merry }
2315ef270ab1SKenneth D. Merry 
2316ef270ab1SKenneth D. Merry /**
2317ef270ab1SKenneth D. Merry  * @ingroup scsi_api_base
2318ef270ab1SKenneth D. Merry  * @brief Initiate initiator write IO.
2319ef270ab1SKenneth D. Merry  *
2320ef270ab1SKenneth D. Merry  * @par Description
2321ef270ab1SKenneth D. Merry  * This call is made by an initiator-client to send a SCSI write command. The payload
2322ef270ab1SKenneth D. Merry  * for the command is given by a scatter-gather list @c sgl for @c sgl_count
2323ef270ab1SKenneth D. Merry  * entries.
2324ef270ab1SKenneth D. Merry  * @n @n
2325ef270ab1SKenneth D. Merry  * Upon completion, the callback @c cb is invoked and passed request status. If the command
2326ef270ab1SKenneth D. Merry  * completed successfully, the callback is given SCSI response data.
2327ef270ab1SKenneth D. Merry  *
2328ef270ab1SKenneth D. Merry  * @param node Pointer to the node.
2329ef270ab1SKenneth D. Merry  * @param io Pointer to IO context.
2330ef270ab1SKenneth D. Merry  * @param lun LUN value.
2331ef270ab1SKenneth D. Merry  * @param cdb Pointer to the CDB.
2332ef270ab1SKenneth D. Merry  * @param cdb_len Length of the CDB.
2333ef270ab1SKenneth D. Merry  * @param dif_info Pointer to the T10 DIF fields, or NULL if no DIF.
2334ef270ab1SKenneth D. Merry  * @param sgl Pointer to the scatter-gather list.
2335ef270ab1SKenneth D. Merry  * @param sgl_count Count of the scatter-gather list elements.
2336ef270ab1SKenneth D. Merry  * @param wire_len Length of the payload.
2337ef270ab1SKenneth D. Merry  * @param first_burst Number of first burst bytes to send.
2338ef270ab1SKenneth D. Merry  * @param cb Completion callback.
2339ef270ab1SKenneth D. Merry  * @param arg Application-specified completion callback argument.
2340ef270ab1SKenneth D. Merry  *
2341ef270ab1SKenneth D. Merry  * @return Returns 0 on success, or a negative error code value on failure.
2342ef270ab1SKenneth D. Merry  */
2343ef270ab1SKenneth D. Merry int32_t
2344ef270ab1SKenneth D. Merry ocs_scsi_send_wr_io_first_burst(ocs_node_t *node, ocs_io_t *io, uint64_t lun, void *cdb, uint32_t cdb_len,
2345ef270ab1SKenneth D. Merry 	ocs_scsi_dif_info_t *dif_info,
2346ef270ab1SKenneth D. Merry 	ocs_scsi_sgl_t *sgl, uint32_t sgl_count, uint32_t wire_len, uint32_t first_burst,
234788364968SAlexander Motin 	ocs_scsi_rsp_io_cb_t cb, void *arg, uint32_t flags)
2348ef270ab1SKenneth D. Merry {
2349ef270ab1SKenneth D. Merry 	int32_t rc;
2350ef270ab1SKenneth D. Merry 
2351ef270ab1SKenneth D. Merry 	rc = ocs_scsi_send_io(OCS_HW_IO_INITIATOR_WRITE, node, io, lun, 0, cdb, cdb_len, dif_info, sgl, sgl_count,
235288364968SAlexander Motin 			      wire_len, 0, cb, arg, flags);
2353ef270ab1SKenneth D. Merry 
2354ef270ab1SKenneth D. Merry 	return rc;
2355ef270ab1SKenneth D. Merry }
2356ef270ab1SKenneth D. Merry 
2357ef270ab1SKenneth D. Merry /**
2358ef270ab1SKenneth D. Merry  * @ingroup scsi_api_base
2359ef270ab1SKenneth D. Merry  * @brief Initiate initiator SCSI command with no data.
2360ef270ab1SKenneth D. Merry  *
2361ef270ab1SKenneth D. Merry  * @par Description
2362ef270ab1SKenneth D. Merry  * This call is made by an initiator-client to send a SCSI command with no data.
2363ef270ab1SKenneth D. Merry  * @n @n
2364ef270ab1SKenneth D. Merry  * Upon completion, the callback @c cb is invoked and passed request status. If the command
2365ef270ab1SKenneth D. Merry  * completed successfully, the callback is given SCSI response data.
2366ef270ab1SKenneth D. Merry  *
2367ef270ab1SKenneth D. Merry  * @param node Pointer to the node.
2368ef270ab1SKenneth D. Merry  * @param io Pointer to the IO context.
2369ef270ab1SKenneth D. Merry  * @param lun LUN value.
2370ef270ab1SKenneth D. Merry  * @param cdb Pointer to the CDB.
2371ef270ab1SKenneth D. Merry  * @param cdb_len Length of the CDB.
2372ef270ab1SKenneth D. Merry  * @param cb Completion callback.
2373ef270ab1SKenneth D. Merry  * @param arg Application-specified completion callback argument.
2374ef270ab1SKenneth D. Merry  *
2375ef270ab1SKenneth D. Merry  * @return Returns 0 on success, or a negative error code value on failure.
2376ef270ab1SKenneth D. Merry  */
2377ef270ab1SKenneth D. Merry int32_t ocs_scsi_send_nodata_io(ocs_node_t *node, ocs_io_t *io, uint64_t lun, void *cdb, uint32_t cdb_len,
237888364968SAlexander Motin 	ocs_scsi_rsp_io_cb_t cb, void *arg, uint32_t flags)
2379ef270ab1SKenneth D. Merry {
2380ef270ab1SKenneth D. Merry 	int32_t rc;
2381ef270ab1SKenneth D. Merry 
238288364968SAlexander Motin 	rc = ocs_scsi_send_io(OCS_HW_IO_INITIATOR_NODATA, node, io, lun, 0, cdb, cdb_len, NULL, NULL, 0, 0, 0, cb, arg, flags);
2383ef270ab1SKenneth D. Merry 
2384ef270ab1SKenneth D. Merry 	return rc;
2385ef270ab1SKenneth D. Merry }
2386ef270ab1SKenneth D. Merry /**
2387ef270ab1SKenneth D. Merry  * @ingroup scsi_api_base
2388ef270ab1SKenneth D. Merry  * @brief Initiate initiator task management operation.
2389ef270ab1SKenneth D. Merry  *
2390ef270ab1SKenneth D. Merry  * @par Description
2391ef270ab1SKenneth D. Merry  * This command is used to send a SCSI task management function command. If the command
2392ef270ab1SKenneth D. Merry  * requires it (QUERY_TASK_SET for example), a payload may be associated with the command.
2393ef270ab1SKenneth D. Merry  * If no payload is required, then @c sgl_count may be zero and @c sgl is ignored.
2394ef270ab1SKenneth D. Merry  * @n @n
2395ef270ab1SKenneth D. Merry  * Upon completion @c cb is invoked with status and SCSI response data.
2396ef270ab1SKenneth D. Merry  *
2397ef270ab1SKenneth D. Merry  * @param node Pointer to the node.
2398ef270ab1SKenneth D. Merry  * @param io Pointer to the IO context.
2399ef270ab1SKenneth D. Merry  * @param io_to_abort Pointer to the IO context to abort in the
2400ef270ab1SKenneth D. Merry  * case of OCS_SCSI_TMF_ABORT_TASK. Note: this can point to the
2401ef270ab1SKenneth D. Merry  * same the same ocs_io_t as @c io, provided that @c io does not
2402ef270ab1SKenneth D. Merry  * have any outstanding work requests.
2403ef270ab1SKenneth D. Merry  * @param lun LUN value.
2404ef270ab1SKenneth D. Merry  * @param tmf Task management command.
2405ef270ab1SKenneth D. Merry  * @param sgl Pointer to the scatter-gather list.
2406ef270ab1SKenneth D. Merry  * @param sgl_count Count of the scatter-gather list elements.
2407ef270ab1SKenneth D. Merry  * @param len Length of the payload.
2408ef270ab1SKenneth D. Merry  * @param cb Completion callback.
2409ef270ab1SKenneth D. Merry  * @param arg Application-specified completion callback argument.
2410ef270ab1SKenneth D. Merry  *
2411ef270ab1SKenneth D. Merry  * @return Returns 0 on success, or a negative error code value on failure.
2412ef270ab1SKenneth D. Merry  */
2413ef270ab1SKenneth D. Merry int32_t
2414ef270ab1SKenneth D. Merry ocs_scsi_send_tmf(ocs_node_t *node, ocs_io_t *io, ocs_io_t *io_to_abort, uint64_t lun, ocs_scsi_tmf_cmd_e tmf,
2415ef270ab1SKenneth D. Merry 	ocs_scsi_sgl_t *sgl, uint32_t sgl_count, uint32_t len, ocs_scsi_rsp_io_cb_t cb, void *arg)
2416ef270ab1SKenneth D. Merry {
2417ef270ab1SKenneth D. Merry 	int32_t rc;
2418ef270ab1SKenneth D. Merry 	ocs_assert(io, -1);
2419ef270ab1SKenneth D. Merry 
2420ef270ab1SKenneth D. Merry 	if (tmf == OCS_SCSI_TMF_ABORT_TASK) {
2421ef270ab1SKenneth D. Merry 		ocs_assert(io_to_abort, -1);
2422ef270ab1SKenneth D. Merry 
2423ef270ab1SKenneth D. Merry 		/* take a reference on IO being aborted */
2424ef270ab1SKenneth D. Merry 		if ((ocs_ref_get_unless_zero(&io_to_abort->ref) == 0)) {
2425ef270ab1SKenneth D. Merry 			/* command no longer active */
2426ef270ab1SKenneth D. Merry 			scsi_io_printf(io, "command no longer active\n");
2427ef270ab1SKenneth D. Merry 			return -1;
2428ef270ab1SKenneth D. Merry 		}
2429ef270ab1SKenneth D. Merry 		/* generic io fields have already been populated */
2430ef270ab1SKenneth D. Merry 
2431ef270ab1SKenneth D. Merry 		/* abort-specific fields */
2432ef270ab1SKenneth D. Merry 		io->io_type = OCS_IO_TYPE_ABORT;
2433ef270ab1SKenneth D. Merry 		io->display_name = "abort_task";
2434ef270ab1SKenneth D. Merry 		io->io_to_abort = io_to_abort;
2435ef270ab1SKenneth D. Merry 		io->send_abts = TRUE;
2436ef270ab1SKenneth D. Merry 		io->scsi_ini_cb = cb;
2437ef270ab1SKenneth D. Merry 		io->scsi_ini_cb_arg = arg;
2438ef270ab1SKenneth D. Merry 
2439ef270ab1SKenneth D. Merry 		/* now dispatch IO */
2440ef270ab1SKenneth D. Merry 		rc = ocs_scsi_io_dispatch_abort(io, ocs_scsi_abort_io_cb);
2441ef270ab1SKenneth D. Merry 		if (rc) {
2442ef270ab1SKenneth D. Merry 			scsi_io_printf(io, "Failed to dispatch abort\n");
2443ef270ab1SKenneth D. Merry 			ocs_ref_put(&io->ref); /* ocs_ref_get(): same function */
2444ef270ab1SKenneth D. Merry 		}
2445ef270ab1SKenneth D. Merry 	} else {
2446ef270ab1SKenneth D. Merry 		io->display_name = "tmf";
2447ef270ab1SKenneth D. Merry 		rc = ocs_scsi_send_io(OCS_HW_IO_INITIATOR_READ, node, io, lun, tmf, NULL, 0, NULL,
244888364968SAlexander Motin 				      sgl, sgl_count, len, 0, cb, arg, 0);
2449ef270ab1SKenneth D. Merry 	}
2450ef270ab1SKenneth D. Merry 
2451ef270ab1SKenneth D. Merry 	return rc;
2452ef270ab1SKenneth D. Merry }
2453ef270ab1SKenneth D. Merry 
2454ef270ab1SKenneth D. Merry /**
2455ef270ab1SKenneth D. Merry  * @ingroup scsi_api_base
2456ef270ab1SKenneth D. Merry  * @brief Send an FCP IO.
2457ef270ab1SKenneth D. Merry  *
2458ef270ab1SKenneth D. Merry  * @par Description
2459ef270ab1SKenneth D. Merry  * An FCP read/write IO command, with optional task management flags, is sent to @c node.
2460ef270ab1SKenneth D. Merry  *
2461ef270ab1SKenneth D. Merry  * @param type HW IO type to send.
2462ef270ab1SKenneth D. Merry  * @param node Pointer to the node destination of the IO.
2463ef270ab1SKenneth D. Merry  * @param io Pointer to the IO context.
2464ef270ab1SKenneth D. Merry  * @param lun LUN value.
2465ef270ab1SKenneth D. Merry  * @param tmf Task management command.
2466ef270ab1SKenneth D. Merry  * @param cdb Pointer to the SCSI CDB.
2467ef270ab1SKenneth D. Merry  * @param cdb_len Length of the CDB, in bytes.
2468ef270ab1SKenneth D. Merry  * @param dif_info Pointer to the T10 DIF fields, or NULL if no DIF.
2469ef270ab1SKenneth D. Merry  * @param sgl Pointer to the scatter-gather list.
2470ef270ab1SKenneth D. Merry  * @param sgl_count Number of SGL entries in SGL.
2471ef270ab1SKenneth D. Merry  * @param wire_len Payload length, in bytes, of data on wire.
2472ef270ab1SKenneth D. Merry  * @param first_burst Number of first burst bytes to send.
2473ef270ab1SKenneth D. Merry  * @param cb Completion callback.
2474ef270ab1SKenneth D. Merry  * @param arg Application-specified completion callback argument.
2475ef270ab1SKenneth D. Merry  *
2476ef270ab1SKenneth D. Merry  * @return Returns 0 on success, or a negative error code value on failure.
2477ef270ab1SKenneth D. Merry  */
2478ef270ab1SKenneth D. Merry 
2479ef270ab1SKenneth D. Merry /* tc: could elminiate LUN, as it's part of the IO structure */
2480ef270ab1SKenneth D. Merry 
2481ef270ab1SKenneth D. Merry static int32_t ocs_scsi_send_io(ocs_hw_io_type_e type, ocs_node_t *node, ocs_io_t *io, uint64_t lun,
2482ef270ab1SKenneth D. Merry 	ocs_scsi_tmf_cmd_e tmf, uint8_t *cdb, uint32_t cdb_len,
2483ef270ab1SKenneth D. Merry 	ocs_scsi_dif_info_t *dif_info,
2484ef270ab1SKenneth D. Merry 	ocs_scsi_sgl_t *sgl, uint32_t sgl_count, uint32_t wire_len, uint32_t first_burst,
248588364968SAlexander Motin 	ocs_scsi_rsp_io_cb_t cb, void *arg, uint32_t flags)
2486ef270ab1SKenneth D. Merry {
2487ef270ab1SKenneth D. Merry 	int32_t rc;
2488ef270ab1SKenneth D. Merry 	ocs_t *ocs;
2489ef270ab1SKenneth D. Merry 	fcp_cmnd_iu_t *cmnd;
2490ef270ab1SKenneth D. Merry 	uint32_t cmnd_bytes = 0;
2491ef270ab1SKenneth D. Merry 	uint32_t *fcp_dl;
2492ef270ab1SKenneth D. Merry 	uint8_t tmf_flags = 0;
2493ef270ab1SKenneth D. Merry 
2494ef270ab1SKenneth D. Merry 	ocs_assert(io->node, -1);
2495ef270ab1SKenneth D. Merry 	ocs_assert(io->node == node, -1);
2496ef270ab1SKenneth D. Merry 	ocs_assert(io, -1);
2497ef270ab1SKenneth D. Merry 	ocs = io->ocs;
2498ef270ab1SKenneth D. Merry 	ocs_assert(cb, -1);
2499ef270ab1SKenneth D. Merry 
2500ef270ab1SKenneth D. Merry 	io->sgl_count = sgl_count;
2501ef270ab1SKenneth D. Merry 
2502ef270ab1SKenneth D. Merry 	/* Copy SGL if needed */
2503ef270ab1SKenneth D. Merry 	if (sgl != io->sgl) {
2504ef270ab1SKenneth D. Merry 		ocs_assert(sgl_count <= io->sgl_allocated, -1);
2505ef270ab1SKenneth D. Merry 		ocs_memcpy(io->sgl, sgl, sizeof(*io->sgl) * sgl_count);
2506ef270ab1SKenneth D. Merry 	}
2507ef270ab1SKenneth D. Merry 
2508ef270ab1SKenneth D. Merry 	/* save initiator and target task tags for debugging */
2509ef270ab1SKenneth D. Merry 	io->tgt_task_tag = 0xffff;
2510ef270ab1SKenneth D. Merry 
2511ef270ab1SKenneth D. Merry 	io->wire_len = wire_len;
2512ef270ab1SKenneth D. Merry 	io->hio_type = type;
2513ef270ab1SKenneth D. Merry 
2514ef270ab1SKenneth D. Merry 	if (OCS_LOG_ENABLE_SCSI_TRACE(ocs)) {
2515ef270ab1SKenneth D. Merry 		char buf[80];
2516ef270ab1SKenneth D. Merry 		ocs_textbuf_t txtbuf;
2517ef270ab1SKenneth D. Merry 		uint32_t i;
2518ef270ab1SKenneth D. Merry 
2519ef270ab1SKenneth D. Merry 		ocs_textbuf_init(ocs, &txtbuf, buf, sizeof(buf));
2520ef270ab1SKenneth D. Merry 
2521ef270ab1SKenneth D. Merry 		ocs_textbuf_printf(&txtbuf, "cdb%d: ", cdb_len);
2522ef270ab1SKenneth D. Merry 		for (i = 0; i < cdb_len; i ++) {
2523ef270ab1SKenneth D. Merry 			ocs_textbuf_printf(&txtbuf, "%02X%s", cdb[i], (i == (cdb_len-1)) ? "" : " ");
2524ef270ab1SKenneth D. Merry 		}
2525ef270ab1SKenneth D. Merry 		scsi_io_printf(io, "%s len %d, %s\n", (io->hio_type == OCS_HW_IO_INITIATOR_READ) ? "read" :
2526ef270ab1SKenneth D. Merry 			(io->hio_type == OCS_HW_IO_INITIATOR_WRITE) ? "write" : "",  io->wire_len,
2527ef270ab1SKenneth D. Merry 			ocs_textbuf_get_buffer(&txtbuf));
2528ef270ab1SKenneth D. Merry 	}
2529ef270ab1SKenneth D. Merry 
2530ef270ab1SKenneth D. Merry 	ocs_assert(io->cmdbuf.virt, -1);
2531ef270ab1SKenneth D. Merry 
2532ef270ab1SKenneth D. Merry 	cmnd = io->cmdbuf.virt;
2533ef270ab1SKenneth D. Merry 
2534ef270ab1SKenneth D. Merry 	ocs_assert(sizeof(*cmnd) <= io->cmdbuf.size, -1);
2535ef270ab1SKenneth D. Merry 
2536ef270ab1SKenneth D. Merry 	ocs_memset(cmnd, 0, sizeof(*cmnd));
2537ef270ab1SKenneth D. Merry 
2538ef270ab1SKenneth D. Merry 	/* Default FCP_CMND IU doesn't include additional CDB bytes but does include FCP_DL */
2539ef270ab1SKenneth D. Merry 	cmnd_bytes = sizeof(fcp_cmnd_iu_t) - sizeof(cmnd->fcp_cdb_and_dl) + sizeof(uint32_t);
2540ef270ab1SKenneth D. Merry 
2541ef270ab1SKenneth D. Merry 	fcp_dl = (uint32_t*)(&(cmnd->fcp_cdb_and_dl));
2542ef270ab1SKenneth D. Merry 
2543ef270ab1SKenneth D. Merry 	if (cdb) {
2544ef270ab1SKenneth D. Merry 		if (cdb_len <= 16) {
2545ef270ab1SKenneth D. Merry 			ocs_memcpy(cmnd->fcp_cdb, cdb, cdb_len);
2546ef270ab1SKenneth D. Merry 		} else {
2547ef270ab1SKenneth D. Merry 			uint32_t addl_cdb_bytes;
2548ef270ab1SKenneth D. Merry 
2549ef270ab1SKenneth D. Merry 			ocs_memcpy(cmnd->fcp_cdb, cdb, 16);
2550ef270ab1SKenneth D. Merry 			addl_cdb_bytes = cdb_len - 16;
2551ef270ab1SKenneth D. Merry 			ocs_memcpy(cmnd->fcp_cdb_and_dl, &(cdb[16]), addl_cdb_bytes);
2552ef270ab1SKenneth D. Merry 			/* additional_fcp_cdb_length is in words, not bytes */
2553ef270ab1SKenneth D. Merry 			cmnd->additional_fcp_cdb_length = (addl_cdb_bytes + 3) / 4;
2554ef270ab1SKenneth D. Merry 			fcp_dl += cmnd->additional_fcp_cdb_length;
2555ef270ab1SKenneth D. Merry 
2556ef270ab1SKenneth D. Merry 			/* Round up additional CDB bytes */
2557ef270ab1SKenneth D. Merry 			cmnd_bytes += (addl_cdb_bytes + 3) & ~0x3;
2558ef270ab1SKenneth D. Merry 		}
2559ef270ab1SKenneth D. Merry 	}
2560ef270ab1SKenneth D. Merry 
2561ef270ab1SKenneth D. Merry 	be64enc(cmnd->fcp_lun, CAM_EXTLUN_BYTE_SWIZZLE(lun));
2562ef270ab1SKenneth D. Merry 
2563ef270ab1SKenneth D. Merry 	if (node->fcp2device) {
2564ef270ab1SKenneth D. Merry 		if(ocs_get_crn(node, &cmnd->command_reference_number,
2565ef270ab1SKenneth D. Merry 					lun)) {
2566ef270ab1SKenneth D. Merry 			return -1;
2567ef270ab1SKenneth D. Merry 		}
2568ef270ab1SKenneth D. Merry 	}
256988364968SAlexander Motin 	if (flags & OCS_SCSI_CMD_HEAD_OF_QUEUE)
257088364968SAlexander Motin 		cmnd->task_attribute = FCP_TASK_ATTR_HEAD_OF_QUEUE;
257188364968SAlexander Motin 	else if (flags & OCS_SCSI_CMD_ORDERED)
257288364968SAlexander Motin 		cmnd->task_attribute = FCP_TASK_ATTR_ORDERED;
257388364968SAlexander Motin 	else if (flags & OCS_SCSI_CMD_UNTAGGED)
257488364968SAlexander Motin 		cmnd->task_attribute = FCP_TASK_ATTR_UNTAGGED;
257588364968SAlexander Motin 	else if (flags & OCS_SCSI_CMD_ACA)
257688364968SAlexander Motin 		cmnd->task_attribute = FCP_TASK_ATTR_ACA;
257788364968SAlexander Motin 	else
257888364968SAlexander Motin 		cmnd->task_attribute = FCP_TASK_ATTR_SIMPLE;
257988364968SAlexander Motin 	cmnd->command_priority = (flags & OCS_SCSI_PRIORITY_MASK) >>
258088364968SAlexander Motin 	    OCS_SCSI_PRIORITY_SHIFT;
2581ef270ab1SKenneth D. Merry 
2582ef270ab1SKenneth D. Merry 	switch (tmf) {
2583ef270ab1SKenneth D. Merry 	case OCS_SCSI_TMF_QUERY_TASK_SET:
2584ef270ab1SKenneth D. Merry 		tmf_flags = FCP_QUERY_TASK_SET;
2585ef270ab1SKenneth D. Merry 		break;
2586ef270ab1SKenneth D. Merry 	case OCS_SCSI_TMF_ABORT_TASK_SET:
2587ef270ab1SKenneth D. Merry 		tmf_flags = FCP_ABORT_TASK_SET;
2588ef270ab1SKenneth D. Merry 		break;
2589ef270ab1SKenneth D. Merry 	case OCS_SCSI_TMF_CLEAR_TASK_SET:
2590ef270ab1SKenneth D. Merry 		tmf_flags = FCP_CLEAR_TASK_SET;
2591ef270ab1SKenneth D. Merry 		break;
2592ef270ab1SKenneth D. Merry 	case OCS_SCSI_TMF_QUERY_ASYNCHRONOUS_EVENT:
2593ef270ab1SKenneth D. Merry 		tmf_flags = FCP_QUERY_ASYNCHRONOUS_EVENT;
2594ef270ab1SKenneth D. Merry 		break;
2595ef270ab1SKenneth D. Merry 	case OCS_SCSI_TMF_LOGICAL_UNIT_RESET:
2596ef270ab1SKenneth D. Merry 		tmf_flags = FCP_LOGICAL_UNIT_RESET;
2597ef270ab1SKenneth D. Merry 		break;
2598ef270ab1SKenneth D. Merry 	case OCS_SCSI_TMF_CLEAR_ACA:
2599ef270ab1SKenneth D. Merry 		tmf_flags = FCP_CLEAR_ACA;
2600ef270ab1SKenneth D. Merry 		break;
2601ef270ab1SKenneth D. Merry 	case OCS_SCSI_TMF_TARGET_RESET:
2602ef270ab1SKenneth D. Merry 		tmf_flags = FCP_TARGET_RESET;
2603ef270ab1SKenneth D. Merry 		break;
2604ef270ab1SKenneth D. Merry 	default:
2605ef270ab1SKenneth D. Merry 		tmf_flags = 0;
2606ef270ab1SKenneth D. Merry 	}
2607ef270ab1SKenneth D. Merry 	cmnd->task_management_flags = tmf_flags;
2608ef270ab1SKenneth D. Merry 
2609ef270ab1SKenneth D. Merry 	*fcp_dl = ocs_htobe32(io->wire_len);
2610ef270ab1SKenneth D. Merry 
2611ef270ab1SKenneth D. Merry 	switch (io->hio_type) {
2612ef270ab1SKenneth D. Merry 	case OCS_HW_IO_INITIATOR_READ:
2613ef270ab1SKenneth D. Merry 		cmnd->rddata = 1;
2614ef270ab1SKenneth D. Merry 		break;
2615ef270ab1SKenneth D. Merry 	case OCS_HW_IO_INITIATOR_WRITE:
2616ef270ab1SKenneth D. Merry 		cmnd->wrdata = 1;
2617ef270ab1SKenneth D. Merry 		break;
2618ef270ab1SKenneth D. Merry 	case  OCS_HW_IO_INITIATOR_NODATA:
2619ef270ab1SKenneth D. Merry 		/* sets neither */
2620ef270ab1SKenneth D. Merry 		break;
2621ef270ab1SKenneth D. Merry 	default:
2622ef270ab1SKenneth D. Merry 		ocs_log_test(ocs, "bad IO type %d\n", io->hio_type);
2623ef270ab1SKenneth D. Merry 		return -1;
2624ef270ab1SKenneth D. Merry 	}
2625ef270ab1SKenneth D. Merry 
2626ef270ab1SKenneth D. Merry 	rc = ocs_scsi_convert_dif_info(ocs, dif_info, &io->hw_dif);
2627ef270ab1SKenneth D. Merry 	if (rc) {
2628ef270ab1SKenneth D. Merry 		return rc;
2629ef270ab1SKenneth D. Merry 	}
2630ef270ab1SKenneth D. Merry 
2631ef270ab1SKenneth D. Merry 	io->scsi_ini_cb = cb;
2632ef270ab1SKenneth D. Merry 	io->scsi_ini_cb_arg = arg;
2633ef270ab1SKenneth D. Merry 
2634ef270ab1SKenneth D. Merry 	/* set command and response buffers in the iparam */
2635ef270ab1SKenneth D. Merry 	io->iparam.fcp_ini.cmnd = &io->cmdbuf;
2636ef270ab1SKenneth D. Merry 	io->iparam.fcp_ini.cmnd_size = cmnd_bytes;
2637ef270ab1SKenneth D. Merry 	io->iparam.fcp_ini.rsp = &io->rspbuf;
2638ef270ab1SKenneth D. Merry 	io->iparam.fcp_ini.flags = 0;
2639ef270ab1SKenneth D. Merry 	io->iparam.fcp_ini.dif_oper = io->hw_dif.dif;
2640ef270ab1SKenneth D. Merry 	io->iparam.fcp_ini.blk_size = io->hw_dif.blk_size;
2641ef270ab1SKenneth D. Merry 	io->iparam.fcp_ini.timeout = io->timeout;
2642ef270ab1SKenneth D. Merry 	io->iparam.fcp_ini.first_burst = first_burst;
2643ef270ab1SKenneth D. Merry 
2644ef270ab1SKenneth D. Merry 	return ocs_scsi_io_dispatch(io, ocs_initiator_io_cb);
2645ef270ab1SKenneth D. Merry }
2646ef270ab1SKenneth D. Merry 
2647ef270ab1SKenneth D. Merry /**
2648ef270ab1SKenneth D. Merry  * @ingroup scsi_api_base
2649ef270ab1SKenneth D. Merry  * @brief Callback for an aborted IO.
2650ef270ab1SKenneth D. Merry  *
2651ef270ab1SKenneth D. Merry  * @par Description
2652ef270ab1SKenneth D. Merry  * Callback function invoked upon completion of an IO abort request.
2653ef270ab1SKenneth D. Merry  *
2654ef270ab1SKenneth D. Merry  * @param hio HW IO context.
2655ef270ab1SKenneth D. Merry  * @param rnode Remote node.
2656ef270ab1SKenneth D. Merry  * @param len Response length.
2657ef270ab1SKenneth D. Merry  * @param status Completion status.
2658ef270ab1SKenneth D. Merry  * @param ext_status Extended completion status.
2659ef270ab1SKenneth D. Merry  * @param arg Application-specific callback, usually IO context.
2660ef270ab1SKenneth D. Merry 
2661ef270ab1SKenneth D. Merry  * @return Returns 0 on success, or a negative error code value on failure.
2662ef270ab1SKenneth D. Merry  */
2663ef270ab1SKenneth D. Merry 
2664ef270ab1SKenneth D. Merry static int32_t
2665ef270ab1SKenneth D. Merry ocs_scsi_abort_io_cb(struct ocs_hw_io_s *hio, ocs_remote_node_t *rnode, uint32_t len, int32_t status,
2666ef270ab1SKenneth D. Merry 	uint32_t ext_status, void *arg)
2667ef270ab1SKenneth D. Merry {
2668ef270ab1SKenneth D. Merry 	ocs_io_t *io = arg;
2669ef270ab1SKenneth D. Merry 	ocs_t *ocs;
2670ef270ab1SKenneth D. Merry 	ocs_scsi_io_status_e scsi_status = OCS_SCSI_STATUS_GOOD;
2671ef270ab1SKenneth D. Merry 
2672ef270ab1SKenneth D. Merry 	ocs_assert(io, -1);
2673ef270ab1SKenneth D. Merry 	ocs_assert(ocs_io_busy(io), -1);
2674ef270ab1SKenneth D. Merry 	ocs_assert(io->ocs, -1);
2675ef270ab1SKenneth D. Merry 	ocs_assert(io->io_to_abort, -1);
2676ef270ab1SKenneth D. Merry 	ocs = io->ocs;
2677ef270ab1SKenneth D. Merry 
2678ef270ab1SKenneth D. Merry 	ocs_log_debug(ocs, "status %d ext %d\n", status, ext_status);
2679ef270ab1SKenneth D. Merry 
2680ef270ab1SKenneth D. Merry 	/* done with IO to abort */
2681ef270ab1SKenneth D. Merry 	ocs_ref_put(&io->io_to_abort->ref); /* ocs_ref_get(): ocs_scsi_send_tmf() */
2682ef270ab1SKenneth D. Merry 
2683ef270ab1SKenneth D. Merry 	ocs_scsi_io_free_ovfl(io);
2684ef270ab1SKenneth D. Merry 
2685ef270ab1SKenneth D. Merry 	switch (status) {
2686ef270ab1SKenneth D. Merry 	case SLI4_FC_WCQE_STATUS_SUCCESS:
2687ef270ab1SKenneth D. Merry 		scsi_status = OCS_SCSI_STATUS_GOOD;
2688ef270ab1SKenneth D. Merry 		break;
2689ef270ab1SKenneth D. Merry 	case SLI4_FC_WCQE_STATUS_LOCAL_REJECT:
2690ef270ab1SKenneth D. Merry 		if (ext_status == SLI4_FC_LOCAL_REJECT_ABORT_REQUESTED) {
2691ef270ab1SKenneth D. Merry 			scsi_status = OCS_SCSI_STATUS_ABORTED;
2692ef270ab1SKenneth D. Merry 		} else if (ext_status == SLI4_FC_LOCAL_REJECT_NO_XRI) {
2693ef270ab1SKenneth D. Merry 			scsi_status = OCS_SCSI_STATUS_NO_IO;
2694ef270ab1SKenneth D. Merry 		} else if (ext_status == SLI4_FC_LOCAL_REJECT_ABORT_IN_PROGRESS) {
2695ef270ab1SKenneth D. Merry 			scsi_status = OCS_SCSI_STATUS_ABORT_IN_PROGRESS;
2696ef270ab1SKenneth D. Merry 		} else {
2697ef270ab1SKenneth D. Merry 			ocs_log_test(ocs, "Unhandled local reject 0x%x/0x%x\n", status, ext_status);
2698ef270ab1SKenneth D. Merry 			scsi_status = OCS_SCSI_STATUS_ERROR;
2699ef270ab1SKenneth D. Merry 		}
2700ef270ab1SKenneth D. Merry 		break;
2701ef270ab1SKenneth D. Merry 	default:
2702ef270ab1SKenneth D. Merry 		scsi_status = OCS_SCSI_STATUS_ERROR;
2703ef270ab1SKenneth D. Merry 		break;
2704ef270ab1SKenneth D. Merry 	}
2705ef270ab1SKenneth D. Merry 
2706ef270ab1SKenneth D. Merry 	if (io->scsi_ini_cb) {
2707ef270ab1SKenneth D. Merry 		(*io->scsi_ini_cb)(io, scsi_status, NULL, 0, io->scsi_ini_cb_arg);
2708ef270ab1SKenneth D. Merry 	} else {
2709ef270ab1SKenneth D. Merry 		ocs_scsi_io_free(io);
2710ef270ab1SKenneth D. Merry 	}
2711ef270ab1SKenneth D. Merry 
2712ef270ab1SKenneth D. Merry 	ocs_scsi_check_pending(ocs);
2713ef270ab1SKenneth D. Merry 	return 0;
2714ef270ab1SKenneth D. Merry }
2715ef270ab1SKenneth D. Merry 
2716ef270ab1SKenneth D. Merry /**
2717ef270ab1SKenneth D. Merry  * @ingroup scsi_api_base
2718ef270ab1SKenneth D. Merry  * @brief Return SCSI API integer valued property.
2719ef270ab1SKenneth D. Merry  *
2720ef270ab1SKenneth D. Merry  * @par Description
2721ef270ab1SKenneth D. Merry  * This function is called by a target-server or initiator-client to
2722ef270ab1SKenneth D. Merry  * retrieve an integer valued property.
2723ef270ab1SKenneth D. Merry  *
2724ef270ab1SKenneth D. Merry  * @param ocs Pointer to the ocs.
2725ef270ab1SKenneth D. Merry  * @param prop Property value to return.
2726ef270ab1SKenneth D. Merry  *
2727ef270ab1SKenneth D. Merry  * @return Returns a value, or 0 if invalid property was requested.
2728ef270ab1SKenneth D. Merry  */
2729ef270ab1SKenneth D. Merry uint32_t
2730ef270ab1SKenneth D. Merry ocs_scsi_get_property(ocs_t *ocs, ocs_scsi_property_e prop)
2731ef270ab1SKenneth D. Merry {
2732ef270ab1SKenneth D. Merry 	ocs_xport_t *xport = ocs->xport;
2733ef270ab1SKenneth D. Merry 	uint32_t	val;
2734ef270ab1SKenneth D. Merry 
2735ef270ab1SKenneth D. Merry 	switch (prop) {
2736ef270ab1SKenneth D. Merry 	case OCS_SCSI_MAX_SGE:
2737ef270ab1SKenneth D. Merry 		if (0 == ocs_hw_get(&ocs->hw, OCS_HW_MAX_SGE, &val)) {
2738ef270ab1SKenneth D. Merry 			return val;
2739ef270ab1SKenneth D. Merry 		}
2740ef270ab1SKenneth D. Merry 		break;
2741ef270ab1SKenneth D. Merry 	case OCS_SCSI_MAX_SGL:
2742ef270ab1SKenneth D. Merry 		if (ocs->ctrlmask & OCS_CTRLMASK_TEST_CHAINED_SGLS) {
2743ef270ab1SKenneth D. Merry 			/*
2744ef270ab1SKenneth D. Merry 			 * If chain SGL test-mode is enabled, the number of HW SGEs
2745ef270ab1SKenneth D. Merry 			 * has been limited; report back original max.
2746ef270ab1SKenneth D. Merry 			 */
2747ef270ab1SKenneth D. Merry 			return (OCS_FC_MAX_SGL);
2748ef270ab1SKenneth D. Merry 		}
2749ef270ab1SKenneth D. Merry 		if (0 == ocs_hw_get(&ocs->hw, OCS_HW_N_SGL, &val)) {
2750ef270ab1SKenneth D. Merry 			return val;
2751ef270ab1SKenneth D. Merry 		}
2752ef270ab1SKenneth D. Merry 		break;
2753ef270ab1SKenneth D. Merry 	case OCS_SCSI_MAX_IOS:
2754ef270ab1SKenneth D. Merry 		return ocs_io_pool_allocated(xport->io_pool);
2755ef270ab1SKenneth D. Merry 	case OCS_SCSI_DIF_CAPABLE:
2756ef270ab1SKenneth D. Merry 	        if (0 == ocs_hw_get(&ocs->hw, OCS_HW_DIF_CAPABLE, &val)) {
2757ef270ab1SKenneth D. Merry 	                return val;
2758ef270ab1SKenneth D. Merry 	        }
2759ef270ab1SKenneth D. Merry 		break;
2760ef270ab1SKenneth D. Merry 	case OCS_SCSI_MAX_FIRST_BURST:
2761ef270ab1SKenneth D. Merry 		return 0;
2762ef270ab1SKenneth D. Merry 	case OCS_SCSI_DIF_MULTI_SEPARATE:
2763ef270ab1SKenneth D. Merry 	        if (ocs_hw_get(&ocs->hw, OCS_HW_DIF_MULTI_SEPARATE, &val) == 0) {
2764ef270ab1SKenneth D. Merry 	                return val;
2765ef270ab1SKenneth D. Merry 	        }
2766ef270ab1SKenneth D. Merry 		break;
2767ef270ab1SKenneth D. Merry 	case OCS_SCSI_ENABLE_TASK_SET_FULL:
2768ef270ab1SKenneth D. Merry 		/* Return FALSE if we are send frame capable */
2769ef270ab1SKenneth D. Merry 		if (ocs_hw_get(&ocs->hw, OCS_HW_SEND_FRAME_CAPABLE, &val) == 0) {
2770ef270ab1SKenneth D. Merry 			return ! val;
2771ef270ab1SKenneth D. Merry 		}
2772ef270ab1SKenneth D. Merry 		break;
2773ef270ab1SKenneth D. Merry 	default:
2774ef270ab1SKenneth D. Merry 		break;
2775ef270ab1SKenneth D. Merry 	}
2776ef270ab1SKenneth D. Merry 
2777ef270ab1SKenneth D. Merry 	ocs_log_debug(ocs, "invalid property request %d\n", prop);
2778ef270ab1SKenneth D. Merry 	return 0;
2779ef270ab1SKenneth D. Merry }
2780ef270ab1SKenneth D. Merry 
2781ef270ab1SKenneth D. Merry /**
2782ef270ab1SKenneth D. Merry  * @ingroup scsi_api_base
2783ef270ab1SKenneth D. Merry  * @brief Return a property pointer.
2784ef270ab1SKenneth D. Merry  *
2785ef270ab1SKenneth D. Merry  * @par Description
2786ef270ab1SKenneth D. Merry  * This function is called by a target-server or initiator-client to
2787ef270ab1SKenneth D. Merry  * retrieve a pointer to the requested property.
2788ef270ab1SKenneth D. Merry  *
2789ef270ab1SKenneth D. Merry  * @param ocs Pointer to the ocs.
2790ef270ab1SKenneth D. Merry  * @param prop Property value to return.
2791ef270ab1SKenneth D. Merry  *
2792ef270ab1SKenneth D. Merry  * @return Returns pointer to the requested property, or NULL otherwise.
2793ef270ab1SKenneth D. Merry  */
2794ef270ab1SKenneth D. Merry void *ocs_scsi_get_property_ptr(ocs_t *ocs, ocs_scsi_property_e prop)
2795ef270ab1SKenneth D. Merry {
2796ef270ab1SKenneth D. Merry 	void *rc = NULL;
2797ef270ab1SKenneth D. Merry 
2798ef270ab1SKenneth D. Merry 	switch (prop) {
2799ef270ab1SKenneth D. Merry 	case OCS_SCSI_WWNN:
2800ef270ab1SKenneth D. Merry 		rc = ocs_hw_get_ptr(&ocs->hw, OCS_HW_WWN_NODE);
2801ef270ab1SKenneth D. Merry 		break;
2802ef270ab1SKenneth D. Merry 	case OCS_SCSI_WWPN:
2803ef270ab1SKenneth D. Merry 		rc = ocs_hw_get_ptr(&ocs->hw, OCS_HW_WWN_PORT);
2804ef270ab1SKenneth D. Merry 		break;
2805ef270ab1SKenneth D. Merry 	case OCS_SCSI_PORTNUM:
2806ef270ab1SKenneth D. Merry 		rc = ocs_hw_get_ptr(&ocs->hw, OCS_HW_PORTNUM);
2807ef270ab1SKenneth D. Merry 		break;
2808ef270ab1SKenneth D. Merry 	case OCS_SCSI_BIOS_VERSION_STRING:
2809ef270ab1SKenneth D. Merry 		rc = ocs_hw_get_ptr(&ocs->hw, OCS_HW_BIOS_VERSION_STRING);
2810ef270ab1SKenneth D. Merry 		break;
2811ef270ab1SKenneth D. Merry #if defined(OCS_ENABLE_VPD_SUPPORT)
2812ef270ab1SKenneth D. Merry 	case OCS_SCSI_SERIALNUMBER:
2813ef270ab1SKenneth D. Merry 	{
2814ef270ab1SKenneth D. Merry 		uint8_t *pvpd;
2815ef270ab1SKenneth D. Merry 		uint32_t vpd_len;
2816ef270ab1SKenneth D. Merry 
2817ef270ab1SKenneth D. Merry 		if (ocs_hw_get(&ocs->hw, OCS_HW_VPD_LEN, &vpd_len)) {
2818ef270ab1SKenneth D. Merry 			ocs_log_test(ocs, "Can't get VPD length\n");
2819ef270ab1SKenneth D. Merry 			rc = "\012sn-unknown";
2820ef270ab1SKenneth D. Merry 			break;
2821ef270ab1SKenneth D. Merry 		}
2822ef270ab1SKenneth D. Merry 
2823ef270ab1SKenneth D. Merry 		pvpd = ocs_hw_get_ptr(&ocs->hw, OCS_HW_VPD);
2824ef270ab1SKenneth D. Merry 		if (pvpd) {
2825ef270ab1SKenneth D. Merry 			rc = ocs_find_vpd(pvpd, vpd_len, "SN");
2826ef270ab1SKenneth D. Merry 		}
2827ef270ab1SKenneth D. Merry 
2828ef270ab1SKenneth D. Merry 		if (rc == NULL ||
2829ef270ab1SKenneth D. Merry 		    ocs_strlen(rc) == 0) {
2830ef270ab1SKenneth D. Merry 			/* Note: VPD is missing, using wwnn for serial number */
2831ef270ab1SKenneth D. Merry 			scsi_log(ocs, "Note: VPD is missing, using wwnn for serial number\n");
2832ef270ab1SKenneth D. Merry 			/* Use the last 32 bits of the WWN */
2833ef270ab1SKenneth D. Merry 			if ((ocs == NULL) || (ocs->domain == NULL) || (ocs->domain->sport == NULL)) {
2834ef270ab1SKenneth D. Merry 				rc = "\011(Unknown)";
2835ef270ab1SKenneth D. Merry 			} else {
2836ef270ab1SKenneth D. Merry 				rc = &ocs->domain->sport->wwnn_str[8];
2837ef270ab1SKenneth D. Merry 			}
2838ef270ab1SKenneth D. Merry 		}
2839ef270ab1SKenneth D. Merry 		break;
2840ef270ab1SKenneth D. Merry 	}
2841ef270ab1SKenneth D. Merry 	case OCS_SCSI_PARTNUMBER:
2842ef270ab1SKenneth D. Merry 	{
2843ef270ab1SKenneth D. Merry 		uint8_t *pvpd;
2844ef270ab1SKenneth D. Merry 		uint32_t vpd_len;
2845ef270ab1SKenneth D. Merry 
2846ef270ab1SKenneth D. Merry 		if (ocs_hw_get(&ocs->hw, OCS_HW_VPD_LEN, &vpd_len)) {
2847ef270ab1SKenneth D. Merry 			ocs_log_test(ocs, "Can't get VPD length\n");
2848ef270ab1SKenneth D. Merry 			rc = "\012pn-unknown";
2849ef270ab1SKenneth D. Merry 			break;
2850ef270ab1SKenneth D. Merry 		}
2851ef270ab1SKenneth D. Merry 		pvpd = ocs_hw_get_ptr(&ocs->hw, OCS_HW_VPD);
2852ef270ab1SKenneth D. Merry 		if (pvpd) {
2853ef270ab1SKenneth D. Merry 			rc = ocs_find_vpd(pvpd, vpd_len, "PN");
2854ef270ab1SKenneth D. Merry 			if (rc == NULL) {
2855ef270ab1SKenneth D. Merry 				rc = "\012pn-unknown";
2856ef270ab1SKenneth D. Merry 			}
2857ef270ab1SKenneth D. Merry 		} else {
2858ef270ab1SKenneth D. Merry 			rc = "\012pn-unknown";
2859ef270ab1SKenneth D. Merry 		}
2860ef270ab1SKenneth D. Merry 		break;
2861ef270ab1SKenneth D. Merry 	}
2862ef270ab1SKenneth D. Merry #endif
2863ef270ab1SKenneth D. Merry 	default:
2864ef270ab1SKenneth D. Merry 		break;
2865ef270ab1SKenneth D. Merry 	}
2866ef270ab1SKenneth D. Merry 
2867ef270ab1SKenneth D. Merry 	if (rc == NULL) {
2868ef270ab1SKenneth D. Merry 		ocs_log_debug(ocs, "invalid property request %d\n", prop);
2869ef270ab1SKenneth D. Merry 	}
2870ef270ab1SKenneth D. Merry 	return rc;
2871ef270ab1SKenneth D. Merry }
2872ef270ab1SKenneth D. Merry 
2873ef270ab1SKenneth D. Merry /**
2874ef270ab1SKenneth D. Merry  * @ingroup scsi_api_base
2875ef270ab1SKenneth D. Merry  * @brief Notify that delete initiator is complete.
2876ef270ab1SKenneth D. Merry  *
2877ef270ab1SKenneth D. Merry  * @par Description
2878ef270ab1SKenneth D. Merry  * Sent by the target-server to notify the base driver that the work started from
2879ef270ab1SKenneth D. Merry  * ocs_scsi_del_initiator() is now complete and that it is safe for the node to
2880ef270ab1SKenneth D. Merry  * release the rest of its resources.
2881ef270ab1SKenneth D. Merry  *
2882ef270ab1SKenneth D. Merry  * @param node Pointer to the node.
2883ef270ab1SKenneth D. Merry  *
2884ef270ab1SKenneth D. Merry  * @return None.
2885ef270ab1SKenneth D. Merry  */
2886ef270ab1SKenneth D. Merry void
2887ef270ab1SKenneth D. Merry ocs_scsi_del_initiator_complete(ocs_node_t *node)
2888ef270ab1SKenneth D. Merry {
2889ef270ab1SKenneth D. Merry 	/* Notify the node to resume */
2890ef270ab1SKenneth D. Merry 	ocs_node_post_event(node, OCS_EVT_NODE_DEL_INI_COMPLETE, NULL);
2891ef270ab1SKenneth D. Merry }
2892ef270ab1SKenneth D. Merry 
2893ef270ab1SKenneth D. Merry /**
2894ef270ab1SKenneth D. Merry  * @ingroup scsi_api_base
2895ef270ab1SKenneth D. Merry  * @brief Notify that delete target is complete.
2896ef270ab1SKenneth D. Merry  *
2897ef270ab1SKenneth D. Merry  * @par Description
2898ef270ab1SKenneth D. Merry  * Sent by the initiator-client to notify the base driver that the work started from
2899ef270ab1SKenneth D. Merry  * ocs_scsi_del_target() is now complete and that it is safe for the node to
2900ef270ab1SKenneth D. Merry  * release the rest of its resources.
2901ef270ab1SKenneth D. Merry  *
2902ef270ab1SKenneth D. Merry  * @param node Pointer to the node.
2903ef270ab1SKenneth D. Merry  *
2904ef270ab1SKenneth D. Merry  * @return None.
2905ef270ab1SKenneth D. Merry  */
2906ef270ab1SKenneth D. Merry void
2907ef270ab1SKenneth D. Merry ocs_scsi_del_target_complete(ocs_node_t *node)
2908ef270ab1SKenneth D. Merry {
2909ef270ab1SKenneth D. Merry 	/* Notify the node to resume */
2910ef270ab1SKenneth D. Merry 	ocs_node_post_event(node, OCS_EVT_NODE_DEL_TGT_COMPLETE, NULL);
2911ef270ab1SKenneth D. Merry }
2912ef270ab1SKenneth D. Merry 
2913ef270ab1SKenneth D. Merry /**
2914ef270ab1SKenneth D. Merry  * @brief Update transferred count
2915ef270ab1SKenneth D. Merry  *
2916ef270ab1SKenneth D. Merry  * @par Description
2917ef270ab1SKenneth D. Merry  * Updates io->transferred, as required when using first burst, when the amount
2918ef270ab1SKenneth D. Merry  * of first burst data processed differs from the amount of first burst
2919ef270ab1SKenneth D. Merry  * data received.
2920ef270ab1SKenneth D. Merry  *
2921ef270ab1SKenneth D. Merry  * @param io Pointer to the io object.
2922ef270ab1SKenneth D. Merry  * @param transferred Number of bytes transferred out of first burst buffers.
2923ef270ab1SKenneth D. Merry  *
2924ef270ab1SKenneth D. Merry  * @return None.
2925ef270ab1SKenneth D. Merry  */
2926ef270ab1SKenneth D. Merry void
2927ef270ab1SKenneth D. Merry ocs_scsi_update_first_burst_transferred(ocs_io_t *io, uint32_t transferred)
2928ef270ab1SKenneth D. Merry {
2929ef270ab1SKenneth D. Merry 	io->transferred = transferred;
2930ef270ab1SKenneth D. Merry }
2931ef270ab1SKenneth D. Merry 
2932ef270ab1SKenneth D. Merry /**
2933ef270ab1SKenneth D. Merry  * @brief Register bounce callback for multi-threading.
2934ef270ab1SKenneth D. Merry  *
2935ef270ab1SKenneth D. Merry  * @par Description
2936ef270ab1SKenneth D. Merry  * Register the back end bounce function.
2937ef270ab1SKenneth D. Merry  *
2938ef270ab1SKenneth D. Merry  * @param ocs Pointer to device object.
2939ef270ab1SKenneth D. Merry  * @param fctn Function pointer of bounce function.
2940ef270ab1SKenneth D. Merry  *
2941ef270ab1SKenneth D. Merry  * @return None.
2942ef270ab1SKenneth D. Merry  */
2943ef270ab1SKenneth D. Merry void
2944ef270ab1SKenneth D. Merry ocs_scsi_register_bounce(ocs_t *ocs, void(*fctn)(void(*fctn)(void *arg), void *arg, uint32_t s_id, uint32_t d_id,
2945ef270ab1SKenneth D. Merry 						 uint32_t ox_id))
2946ef270ab1SKenneth D. Merry {
2947ef270ab1SKenneth D. Merry 	ocs_hw_rtn_e rc;
2948ef270ab1SKenneth D. Merry 
2949ef270ab1SKenneth D. Merry 	rc = ocs_hw_callback(&ocs->hw, OCS_HW_CB_BOUNCE, fctn, NULL);
2950ef270ab1SKenneth D. Merry 	if (rc) {
2951ef270ab1SKenneth D. Merry 		ocs_log_test(ocs, "ocs_hw_callback(OCS_HW_CB_BOUNCE) failed: %d\n", rc);
2952ef270ab1SKenneth D. Merry 	}
2953ef270ab1SKenneth D. Merry }
2954