1331766Sken/*-
2331766Sken * Copyright (c) 2017 Broadcom. All rights reserved.
3331766Sken * The term "Broadcom" refers to Broadcom Limited and/or its subsidiaries.
4331766Sken *
5331766Sken * Redistribution and use in source and binary forms, with or without
6331766Sken * modification, are permitted provided that the following conditions are met:
7331766Sken *
8331766Sken * 1. Redistributions of source code must retain the above copyright notice,
9331766Sken *    this list of conditions and the following disclaimer.
10331766Sken *
11331766Sken * 2. Redistributions in binary form must reproduce the above copyright notice,
12331766Sken *    this list of conditions and the following disclaimer in the documentation
13331766Sken *    and/or other materials provided with the distribution.
14331766Sken *
15331766Sken * 3. Neither the name of the copyright holder nor the names of its contributors
16331766Sken *    may be used to endorse or promote products derived from this software
17331766Sken *    without specific prior written permission.
18331766Sken *
19331766Sken * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20331766Sken * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21331766Sken * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22331766Sken * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23331766Sken * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24331766Sken * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25331766Sken * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26331766Sken * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27331766Sken * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28331766Sken * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29331766Sken * POSSIBILITY OF SUCH DAMAGE.
30331766Sken *
31331766Sken * $FreeBSD: stable/11/sys/dev/ocs_fc/ocs_scsi.c 331766 2018-03-30 15:28:25Z ken $
32331766Sken */
33331766Sken
34331766Sken/**
35331766Sken * @file
36331766Sken * OCS Linux SCSI API base driver implementation.
37331766Sken */
38331766Sken
39331766Sken/**
40331766Sken * @defgroup scsi_api_base SCSI Base Target/Initiator
41331766Sken */
42331766Sken
43331766Sken
44331766Sken#include "ocs.h"
45331766Sken#include "ocs_els.h"
46331766Sken#include "ocs_scsi.h"
47331766Sken#if defined(OCS_ENABLE_VPD_SUPPORT)
48331766Sken#include "ocs_vpd.h"
49331766Sken#endif
50331766Sken#include "ocs_utils.h"
51331766Sken#include "ocs_device.h"
52331766Sken
53331766Sken#define SCSI_IOFMT "[%04x][i:%0*x t:%0*x h:%04x]"
54331766Sken#define SCSI_ITT_SIZE(ocs)	((ocs->ocs_xport == OCS_XPORT_FC) ? 4 : 8)
55331766Sken
56331766Sken#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
57331766Sken
58331766Sken#define enable_tsend_auto_resp(ocs)		((ocs->ctrlmask & OCS_CTRLMASK_XPORT_DISABLE_AUTORSP_TSEND) == 0)
59331766Sken#define enable_treceive_auto_resp(ocs)	((ocs->ctrlmask & OCS_CTRLMASK_XPORT_DISABLE_AUTORSP_TRECEIVE) == 0)
60331766Sken
61331766Sken#define scsi_io_printf(io, fmt, ...) ocs_log_info(io->ocs, "[%s]" SCSI_IOFMT fmt, \
62331766Sken	io->node->display_name, SCSI_IOFMT_ARGS(io), ##__VA_ARGS__)
63331766Sken
64331766Sken#define scsi_io_trace(io, fmt, ...) \
65331766Sken	do { \
66331766Sken		if (OCS_LOG_ENABLE_SCSI_TRACE(io->ocs)) \
67331766Sken			scsi_io_printf(io, fmt, ##__VA_ARGS__); \
68331766Sken	} while (0)
69331766Sken
70331766Sken#define scsi_log(ocs, fmt, ...) \
71331766Sken	do { \
72331766Sken		if (OCS_LOG_ENABLE_SCSI_TRACE(ocs)) \
73331766Sken			ocs_log_info(ocs, fmt, ##__VA_ARGS__); \
74331766Sken	} while (0)
75331766Sken
76331766Skenstatic int32_t ocs_target_send_bls_resp(ocs_io_t *io, ocs_scsi_io_cb_t cb, void *arg);
77331766Skenstatic int32_t ocs_scsi_abort_io_cb(struct ocs_hw_io_s *hio, ocs_remote_node_t *rnode, uint32_t len, int32_t status,
78331766Sken	uint32_t ext, void *arg);
79331766Sken
80331766Skenstatic void ocs_scsi_io_free_ovfl(ocs_io_t *io);
81331766Skenstatic uint32_t ocs_scsi_count_sgls(ocs_hw_dif_info_t *hw_dif, ocs_scsi_sgl_t *sgl, uint32_t sgl_count);
82331766Skenstatic int ocs_scsi_dif_guard_is_crc(uint8_t direction, ocs_hw_dif_info_t *dif_info);
83331766Skenstatic ocs_scsi_io_status_e ocs_scsi_dif_check_unknown(ocs_io_t *io, uint32_t length, uint32_t check_length, int is_crc);
84331766Skenstatic uint32_t ocs_scsi_dif_check_guard(ocs_hw_dif_info_t *dif_info, ocs_scsi_vaddr_len_t addrlen[],
85331766Sken	uint32_t addrlen_count, ocs_dif_t *dif, int is_crc);
86331766Skenstatic 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);
87331766Skenstatic 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);
88331766Skenstatic int32_t ocs_scsi_convert_dif_info(ocs_t *ocs, ocs_scsi_dif_info_t *scsi_dif_info,
89331766Sken	ocs_hw_dif_info_t *hw_dif_info);
90331766Skenstatic int32_t ocs_scsi_io_dispatch_hw_io(ocs_io_t *io, ocs_hw_io_t *hio);
91331766Skenstatic int32_t ocs_scsi_io_dispatch_no_hw_io(ocs_io_t *io);
92331766Skenstatic void _ocs_scsi_io_free(void *arg);
93331766Sken
94331766Sken
95331766Sken/**
96331766Sken * @ingroup scsi_api_base
97331766Sken * @brief Returns a big-endian 32-bit value given a pointer.
98331766Sken *
99331766Sken * @param p Pointer to the 32-bit big-endian location.
100331766Sken *
101331766Sken * @return Returns the byte-swapped 32-bit value.
102331766Sken */
103331766Sken
104331766Skenstatic inline uint32_t
105331766Skenocs_fc_getbe32(void *p)
106331766Sken{
107331766Sken	return ocs_be32toh(*((uint32_t*)p));
108331766Sken}
109331766Sken
110331766Sken/**
111331766Sken * @ingroup scsi_api_base
112331766Sken * @brief Enable IO allocation.
113331766Sken *
114331766Sken * @par Description
115331766Sken * The SCSI and Transport IO allocation functions are enabled. If the allocation functions
116331766Sken * are not enabled, then calls to ocs_scsi_io_alloc() (and ocs_els_io_alloc() for FC) will
117331766Sken * fail.
118331766Sken *
119331766Sken * @param node Pointer to node object.
120331766Sken *
121331766Sken * @return None.
122331766Sken */
123331766Skenvoid
124331766Skenocs_scsi_io_alloc_enable(ocs_node_t *node)
125331766Sken{
126331766Sken	ocs_assert(node != NULL);
127331766Sken	ocs_lock(&node->active_ios_lock);
128331766Sken		node->io_alloc_enabled = TRUE;
129331766Sken	ocs_unlock(&node->active_ios_lock);
130331766Sken}
131331766Sken
132331766Sken/**
133331766Sken * @ingroup scsi_api_base
134331766Sken * @brief Disable IO allocation
135331766Sken *
136331766Sken * @par Description
137331766Sken * The SCSI and Transport IO allocation functions are disabled. If the allocation functions
138331766Sken * are not enabled, then calls to ocs_scsi_io_alloc() (and ocs_els_io_alloc() for FC) will
139331766Sken * fail.
140331766Sken *
141331766Sken * @param node Pointer to node object
142331766Sken *
143331766Sken * @return None.
144331766Sken */
145331766Skenvoid
146331766Skenocs_scsi_io_alloc_disable(ocs_node_t *node)
147331766Sken{
148331766Sken	ocs_assert(node != NULL);
149331766Sken	ocs_lock(&node->active_ios_lock);
150331766Sken		node->io_alloc_enabled = FALSE;
151331766Sken	ocs_unlock(&node->active_ios_lock);
152331766Sken}
153331766Sken
154331766Sken/**
155331766Sken * @ingroup scsi_api_base
156331766Sken * @brief Allocate a SCSI IO context.
157331766Sken *
158331766Sken * @par Description
159331766Sken * A SCSI IO context is allocated and associated with a @c node. This function
160331766Sken * is called by an initiator-client when issuing SCSI commands to remote
161331766Sken * target devices. On completion, ocs_scsi_io_free() is called.
162331766Sken * @n @n
163331766Sken * The returned ocs_io_t structure has an element of type ocs_scsi_ini_io_t named
164331766Sken * "ini_io" that is declared and used by an initiator-client for private information.
165331766Sken *
166331766Sken * @param node Pointer to the associated node structure.
167331766Sken * @param role Role for IO (originator/responder).
168331766Sken *
169331766Sken * @return Returns the pointer to the IO context, or NULL.
170331766Sken *
171331766Sken */
172331766Sken
173331766Skenocs_io_t *
174331766Skenocs_scsi_io_alloc(ocs_node_t *node, ocs_scsi_io_role_e role)
175331766Sken{
176331766Sken	ocs_t *ocs;
177331766Sken	ocs_xport_t *xport;
178331766Sken	ocs_io_t *io;
179331766Sken
180331766Sken	ocs_assert(node, NULL);
181331766Sken	ocs_assert(node->ocs, NULL);
182331766Sken
183331766Sken	ocs = node->ocs;
184331766Sken	ocs_assert(ocs->xport, NULL);
185331766Sken	xport = ocs->xport;
186331766Sken
187331766Sken	ocs_lock(&node->active_ios_lock);
188331766Sken
189331766Sken		if (!node->io_alloc_enabled) {
190331766Sken			ocs_unlock(&node->active_ios_lock);
191331766Sken			return NULL;
192331766Sken		}
193331766Sken
194331766Sken		io = ocs_io_alloc(ocs);
195331766Sken		if (io == NULL) {
196331766Sken			ocs_atomic_add_return(&xport->io_alloc_failed_count, 1);
197331766Sken			ocs_unlock(&node->active_ios_lock);
198331766Sken			return NULL;
199331766Sken		}
200331766Sken
201331766Sken		/* initialize refcount */
202331766Sken		ocs_ref_init(&io->ref, _ocs_scsi_io_free, io);
203331766Sken
204331766Sken		if (io->hio != NULL) {
205331766Sken			ocs_log_err(node->ocs, "assertion failed: io->hio is not NULL\n");
206331766Sken			ocs_unlock(&node->active_ios_lock);
207331766Sken			return NULL;
208331766Sken		}
209331766Sken
210331766Sken		/* set generic fields */
211331766Sken		io->ocs = ocs;
212331766Sken		io->node = node;
213331766Sken
214331766Sken		/* set type and name */
215331766Sken		io->io_type = OCS_IO_TYPE_IO;
216331766Sken		io->display_name = "scsi_io";
217331766Sken
218331766Sken		switch (role) {
219331766Sken		case OCS_SCSI_IO_ROLE_ORIGINATOR:
220331766Sken			io->cmd_ini = TRUE;
221331766Sken			io->cmd_tgt = FALSE;
222331766Sken			break;
223331766Sken		case OCS_SCSI_IO_ROLE_RESPONDER:
224331766Sken			io->cmd_ini = FALSE;
225331766Sken			io->cmd_tgt = TRUE;
226331766Sken			break;
227331766Sken		}
228331766Sken
229331766Sken		/* Add to node's active_ios list */
230331766Sken		ocs_list_add_tail(&node->active_ios, io);
231331766Sken
232331766Sken	ocs_unlock(&node->active_ios_lock);
233331766Sken
234331766Sken	return io;
235331766Sken}
236331766Sken
237331766Sken/**
238331766Sken * @ingroup scsi_api_base
239331766Sken * @brief Free a SCSI IO context (internal).
240331766Sken *
241331766Sken * @par Description
242331766Sken * The IO context previously allocated using ocs_scsi_io_alloc()
243331766Sken * is freed. This is called from within the transport layer,
244331766Sken * when the reference count goes to zero.
245331766Sken *
246331766Sken * @param arg Pointer to the IO context.
247331766Sken *
248331766Sken * @return None.
249331766Sken */
250331766Skenstatic void
251331766Sken_ocs_scsi_io_free(void *arg)
252331766Sken{
253331766Sken	ocs_io_t *io = (ocs_io_t *)arg;
254331766Sken	ocs_t *ocs = io->ocs;
255331766Sken	ocs_node_t *node = io->node;
256331766Sken	int send_empty_event;
257331766Sken
258331766Sken	ocs_assert(io != NULL);
259331766Sken
260331766Sken	scsi_io_trace(io, "freeing io 0x%p %s\n", io, io->display_name);
261331766Sken
262331766Sken	ocs_assert(ocs_io_busy(io));
263331766Sken
264331766Sken	ocs_lock(&node->active_ios_lock);
265331766Sken		ocs_list_remove(&node->active_ios, io);
266331766Sken		send_empty_event = (!node->io_alloc_enabled) && ocs_list_empty(&node->active_ios);
267331766Sken	ocs_unlock(&node->active_ios_lock);
268331766Sken
269331766Sken	if (send_empty_event) {
270331766Sken		ocs_node_post_event(node, OCS_EVT_NODE_ACTIVE_IO_LIST_EMPTY, NULL);
271331766Sken	}
272331766Sken
273331766Sken	io->node = NULL;
274331766Sken	ocs_io_free(ocs, io);
275331766Sken
276331766Sken}
277331766Sken
278331766Sken/**
279331766Sken * @ingroup scsi_api_base
280331766Sken * @brief Free a SCSI IO context.
281331766Sken *
282331766Sken * @par Description
283331766Sken * The IO context previously allocated using ocs_scsi_io_alloc() is freed.
284331766Sken *
285331766Sken * @param io Pointer to the IO context.
286331766Sken *
287331766Sken * @return None.
288331766Sken */
289331766Skenvoid
290331766Skenocs_scsi_io_free(ocs_io_t *io)
291331766Sken{
292331766Sken	scsi_io_trace(io, "freeing io 0x%p %s\n", io, io->display_name);
293331766Sken	ocs_assert(ocs_ref_read_count(&io->ref) > 0);
294331766Sken	ocs_ref_put(&io->ref); /* ocs_ref_get(): ocs_scsi_io_alloc() */
295331766Sken}
296331766Sken
297331766Sken
298331766Sken
299331766Skenstatic int32_t
300331766Skenocs_scsi_send_io(ocs_hw_io_type_e type, ocs_node_t *node, ocs_io_t *io, uint64_t lun,
301331766Sken	ocs_scsi_tmf_cmd_e tmf, uint8_t *cdb, uint32_t cdb_len,
302331766Sken	ocs_scsi_dif_info_t *dif_info,
303331766Sken	ocs_scsi_sgl_t *sgl, uint32_t sgl_count, uint32_t wire_len, uint32_t first_burst,
304331766Sken	ocs_scsi_rsp_io_cb_t cb, void *arg);
305331766Sken
306331766Sken/**
307331766Sken * @brief Target response completion callback.
308331766Sken *
309331766Sken * @par Description
310331766Sken * Function is called upon the completion of a target IO request.
311331766Sken *
312331766Sken * @param hio Pointer to the HW IO structure.
313331766Sken * @param rnode Remote node associated with the IO that is completing.
314331766Sken * @param length Length of the response payload.
315331766Sken * @param status Completion status.
316331766Sken * @param ext_status Extended completion status.
317331766Sken * @param app Application-specific data (generally a pointer to the IO context).
318331766Sken *
319331766Sken * @return None.
320331766Sken */
321331766Sken
322331766Skenstatic void
323331766Skenocs_target_io_cb(ocs_hw_io_t *hio, ocs_remote_node_t *rnode, uint32_t length,
324331766Sken	int32_t status, uint32_t ext_status, void *app)
325331766Sken{
326331766Sken	ocs_io_t *io = app;
327331766Sken	ocs_t *ocs;
328331766Sken	ocs_scsi_io_status_e scsi_status = OCS_SCSI_STATUS_GOOD;
329331766Sken	uint16_t additional_length;
330331766Sken	uint8_t edir;
331331766Sken	uint8_t tdpv;
332331766Sken	ocs_hw_dif_info_t *dif_info = &io->hw_dif;
333331766Sken	int is_crc;
334331766Sken
335331766Sken	ocs_assert(io);
336331766Sken
337331766Sken	scsi_io_trace(io, "status x%x ext_status x%x\n", status, ext_status);
338331766Sken
339331766Sken	ocs = io->ocs;
340331766Sken	ocs_assert(ocs);
341331766Sken
342331766Sken	ocs_scsi_io_free_ovfl(io);
343331766Sken
344331766Sken	io->transferred += length;
345331766Sken
346331766Sken	/* Call target server completion */
347331766Sken	if (io->scsi_tgt_cb) {
348331766Sken		ocs_scsi_io_cb_t cb = io->scsi_tgt_cb;
349331766Sken		uint32_t flags = 0;
350331766Sken
351331766Sken		/* Clear the callback before invoking the callback */
352331766Sken		io->scsi_tgt_cb = NULL;
353331766Sken
354331766Sken		/* if status was good, and auto-good-response was set, then callback
355331766Sken		 * target-server with IO_CMPL_RSP_SENT, otherwise send IO_CMPL
356331766Sken		 */
357331766Sken		if ((status == 0) && (io->auto_resp))
358331766Sken			flags |= OCS_SCSI_IO_CMPL_RSP_SENT;
359331766Sken		else
360331766Sken			flags |= OCS_SCSI_IO_CMPL;
361331766Sken
362331766Sken		switch (status) {
363331766Sken		case SLI4_FC_WCQE_STATUS_SUCCESS:
364331766Sken			scsi_status = OCS_SCSI_STATUS_GOOD;
365331766Sken			break;
366331766Sken		case SLI4_FC_WCQE_STATUS_DI_ERROR:
367331766Sken			if (ext_status & SLI4_FC_DI_ERROR_GE) {
368331766Sken				scsi_status = OCS_SCSI_STATUS_DIF_GUARD_ERROR;
369331766Sken			} else if (ext_status & SLI4_FC_DI_ERROR_AE) {
370331766Sken				scsi_status = OCS_SCSI_STATUS_DIF_APP_TAG_ERROR;
371331766Sken			} else if (ext_status & SLI4_FC_DI_ERROR_RE) {
372331766Sken				scsi_status = OCS_SCSI_STATUS_DIF_REF_TAG_ERROR;
373331766Sken			} else {
374331766Sken				additional_length = ((ext_status >> 16) & 0xFFFF);
375331766Sken
376331766Sken				/* Capture the EDIR and TDPV bits as 0 or 1 for easier printing. */
377331766Sken				edir = !!(ext_status & SLI4_FC_DI_ERROR_EDIR);
378331766Sken				tdpv = !!(ext_status & SLI4_FC_DI_ERROR_TDPV);
379331766Sken
380331766Sken				is_crc = ocs_scsi_dif_guard_is_crc(edir, dif_info);
381331766Sken
382331766Sken				if (edir == 0) {
383331766Sken					/* For reads, we have everything in memory.  Start checking from beginning. */
384331766Sken					scsi_status = ocs_scsi_dif_check_unknown(io, 0, io->wire_len, is_crc);
385331766Sken				} else {
386331766Sken					/* For writes, use the additional length to determine where to look for the error.
387331766Sken					 * The additional_length field is set to 0 if it is not supported.
388331766Sken					 * The additional length field is valid if:
389331766Sken					 *    . additional_length is not zero
390331766Sken					 *    . Total Data Placed is valid
391331766Sken					 *    . Error Direction is RX (1)
392331766Sken					 *    . Operation is a pass thru (CRC or CKSUM on IN, and CRC or CHKSUM on OUT) (all pass-thru cases except raw)
393331766Sken					 */
394331766Sken					if ((additional_length != 0) && (tdpv != 0) &&
395331766Sken					    (dif_info->dif == SLI4_DIF_PASS_THROUGH) && (dif_info->dif_oper != OCS_HW_SGE_DIF_OP_IN_RAW_OUT_RAW) ) {
396331766Sken						scsi_status = ocs_scsi_dif_check_unknown(io, length, additional_length, is_crc);
397331766Sken					} else {
398331766Sken						/* If we can't do additional checking, then fall-back to guard error */
399331766Sken						scsi_status = OCS_SCSI_STATUS_DIF_GUARD_ERROR;
400331766Sken					}
401331766Sken				}
402331766Sken			}
403331766Sken			break;
404331766Sken		case SLI4_FC_WCQE_STATUS_LOCAL_REJECT:
405331766Sken			switch (ext_status) {
406331766Sken			case SLI4_FC_LOCAL_REJECT_INVALID_RELOFFSET:
407331766Sken			case SLI4_FC_LOCAL_REJECT_ABORT_REQUESTED:
408331766Sken				scsi_status = OCS_SCSI_STATUS_ABORTED;
409331766Sken				break;
410331766Sken			case SLI4_FC_LOCAL_REJECT_INVALID_RPI:
411331766Sken				scsi_status = OCS_SCSI_STATUS_NEXUS_LOST;
412331766Sken				break;
413331766Sken			case SLI4_FC_LOCAL_REJECT_NO_XRI:
414331766Sken				scsi_status = OCS_SCSI_STATUS_NO_IO;
415331766Sken				break;
416331766Sken			default:
417331766Sken				/* TODO: we have seen 0x0d (TX_DMA_FAILED error) */
418331766Sken				scsi_status = OCS_SCSI_STATUS_ERROR;
419331766Sken				break;
420331766Sken			}
421331766Sken			break;
422331766Sken
423331766Sken		case SLI4_FC_WCQE_STATUS_TARGET_WQE_TIMEOUT:
424331766Sken			/* target IO timed out */
425331766Sken			scsi_status = OCS_SCSI_STATUS_TIMEDOUT_AND_ABORTED;
426331766Sken			break;
427331766Sken
428331766Sken		case SLI4_FC_WCQE_STATUS_SHUTDOWN:
429331766Sken			/* Target IO cancelled by HW */
430331766Sken			scsi_status = OCS_SCSI_STATUS_SHUTDOWN;
431331766Sken			break;
432331766Sken
433331766Sken		default:
434331766Sken			scsi_status = OCS_SCSI_STATUS_ERROR;
435331766Sken			break;
436331766Sken		}
437331766Sken
438331766Sken		cb(io, scsi_status, flags, io->scsi_tgt_cb_arg);
439331766Sken
440331766Sken	}
441331766Sken	ocs_scsi_check_pending(ocs);
442331766Sken}
443331766Sken
444331766Sken/**
445331766Sken * @brief Determine if an IO is using CRC for DIF guard format.
446331766Sken *
447331766Sken * @param direction IO direction: 1 for write, 0 for read.
448331766Sken * @param dif_info Pointer to HW DIF info data.
449331766Sken *
450331766Sken * @return Returns TRUE if using CRC, FALSE if not.
451331766Sken */
452331766Skenstatic int
453331766Skenocs_scsi_dif_guard_is_crc(uint8_t direction, ocs_hw_dif_info_t *dif_info)
454331766Sken{
455331766Sken	int is_crc;
456331766Sken
457331766Sken	if (direction) {
458331766Sken		/* For writes, check if operation is "OUT_CRC" or not */
459331766Sken		switch(dif_info->dif_oper) {
460331766Sken			case OCS_HW_SGE_DIF_OP_IN_NODIF_OUT_CRC:
461331766Sken			case OCS_HW_SGE_DIF_OP_IN_CRC_OUT_CRC:
462331766Sken			case OCS_HW_SGE_DIF_OP_IN_CHKSUM_OUT_CRC:
463331766Sken				is_crc = TRUE;
464331766Sken				break;
465331766Sken			default:
466331766Sken				is_crc = FALSE;
467331766Sken				break;
468331766Sken		}
469331766Sken	} else {
470331766Sken		/* For reads, check if operation is "IN_CRC" or not */
471331766Sken		switch(dif_info->dif_oper) {
472331766Sken			case OCS_HW_SGE_DIF_OP_IN_CRC_OUT_NODIF:
473331766Sken			case OCS_HW_SGE_DIF_OP_IN_CRC_OUT_CRC:
474331766Sken			case OCS_HW_SGE_DIF_OP_IN_CRC_OUT_CHKSUM:
475331766Sken				is_crc = TRUE;
476331766Sken				break;
477331766Sken			default:
478331766Sken				is_crc = FALSE;
479331766Sken				break;
480331766Sken		}
481331766Sken	}
482331766Sken
483331766Sken	return is_crc;
484331766Sken}
485331766Sken
486331766Sken/**
487331766Sken * @brief Check a block and DIF data, computing the appropriate SCSI status
488331766Sken *
489331766Sken * @par Description
490331766Sken * This function is used to check blocks and DIF when given an unknown DIF
491331766Sken * status using the following logic:
492331766Sken *
493331766Sken * Given the address of the last good block, and a length of bytes that includes
494331766Sken * the block with the DIF error, find the bad block. If a block is found with an
495331766Sken * app_tag or ref_tag error, then return the appropriate error. No block is expected
496331766Sken * to have a block guard error since hardware "fixes" the crc. So if no block in the
497331766Sken * range of blocks has an error, then it is presumed to be a BLOCK GUARD error.
498331766Sken *
499331766Sken * @param io Pointer to the IO object.
500331766Sken * @param length Length of bytes covering the good blocks.
501331766Sken * @param check_length Length of bytes that covers the bad block.
502331766Sken * @param is_crc True if guard is using CRC format.
503331766Sken *
504331766Sken * @return Returns SCSI status.
505331766Sken */
506331766Sken
507331766Skenstatic ocs_scsi_io_status_e
508331766Skenocs_scsi_dif_check_unknown(ocs_io_t *io, uint32_t length, uint32_t check_length, int is_crc)
509331766Sken{
510331766Sken	uint32_t i;
511331766Sken	ocs_t *ocs = io->ocs;
512331766Sken	ocs_hw_dif_info_t *dif_info = &io->hw_dif;
513331766Sken	ocs_scsi_io_status_e scsi_status = OCS_SCSI_STATUS_DIF_GUARD_ERROR;
514331766Sken	uint32_t blocksize;			/* data block size */
515331766Sken	uint64_t first_check_block;		/* first block following total data placed */
516331766Sken	uint64_t last_check_block;		/* last block to check */
517331766Sken	uint32_t check_count;			/* count of blocks to check */
518331766Sken	ocs_scsi_vaddr_len_t addrlen[4];	/* address-length pairs returned from target */
519331766Sken	int32_t addrlen_count;			/* count of address-length pairs */
520331766Sken	ocs_dif_t *dif;				/* pointer to DIF block returned from target */
521331766Sken	ocs_scsi_dif_info_t scsi_dif_info = io->scsi_dif_info;
522331766Sken
523331766Sken	blocksize = ocs_hw_dif_mem_blocksize(&io->hw_dif, TRUE);
524331766Sken	first_check_block = length / blocksize;
525331766Sken	last_check_block = ((length + check_length) / blocksize);
526331766Sken	check_count = last_check_block - first_check_block;
527331766Sken
528331766Sken	ocs_log_debug(ocs, "blocksize %d first check_block %" PRId64 " last_check_block %" PRId64 " check_count %d\n",
529331766Sken		blocksize, first_check_block, last_check_block, check_count);
530331766Sken
531331766Sken	for (i = first_check_block; i < last_check_block; i++) {
532331766Sken		addrlen_count = ocs_scsi_get_block_vaddr(io, (scsi_dif_info.lba + i), addrlen, ARRAY_SIZE(addrlen), (void**) &dif);
533331766Sken		if (addrlen_count < 0) {
534331766Sken			ocs_log_test(ocs, "ocs_scsi_get_block_vaddr() failed: %d\n", addrlen_count);
535331766Sken			scsi_status = OCS_SCSI_STATUS_DIF_UNKNOWN_ERROR;
536331766Sken			break;
537331766Sken		}
538331766Sken
539331766Sken		if (! ocs_scsi_dif_check_guard(dif_info, addrlen, addrlen_count, dif, is_crc)) {
540331766Sken			ocs_log_debug(ocs, "block guard check error, lba %" PRId64 "\n", scsi_dif_info.lba + i);
541331766Sken			scsi_status = OCS_SCSI_STATUS_DIF_GUARD_ERROR;
542331766Sken			break;
543331766Sken		}
544331766Sken		if (! ocs_scsi_dif_check_app_tag(ocs, dif_info, scsi_dif_info.app_tag, dif)) {
545331766Sken			ocs_log_debug(ocs, "app tag check error, lba %" PRId64 "\n", scsi_dif_info.lba + i);
546331766Sken			scsi_status = OCS_SCSI_STATUS_DIF_APP_TAG_ERROR;
547331766Sken			break;
548331766Sken		}
549331766Sken		if (! ocs_scsi_dif_check_ref_tag(ocs, dif_info, (scsi_dif_info.ref_tag + i), dif)) {
550331766Sken			ocs_log_debug(ocs, "ref tag check error, lba %" PRId64 "\n", scsi_dif_info.lba + i);
551331766Sken			scsi_status = OCS_SCSI_STATUS_DIF_REF_TAG_ERROR;
552331766Sken			break;
553331766Sken		}
554331766Sken
555331766Sken	}
556331766Sken	return scsi_status;
557331766Sken}
558331766Sken
559331766Sken/**
560331766Sken * @brief Check the block guard of block data
561331766Sken *
562331766Sken * @par Description
563331766Sken * Using the dif_info for the transfer, check the block guard value.
564331766Sken *
565331766Sken * @param dif_info Pointer to HW DIF info data.
566331766Sken * @param addrlen Array of address length pairs.
567331766Sken * @param addrlen_count Number of entries in the addrlen[] array.
568331766Sken * @param dif Pointer to the DIF data block being checked.
569331766Sken * @param is_crc True if guard is using CRC format.
570331766Sken *
571331766Sken * @return Returns TRUE if block guard check is ok.
572331766Sken */
573331766Skenstatic uint32_t
574331766Skenocs_scsi_dif_check_guard(ocs_hw_dif_info_t *dif_info, ocs_scsi_vaddr_len_t addrlen[], uint32_t addrlen_count,
575331766Sken	ocs_dif_t *dif, int is_crc)
576331766Sken{
577331766Sken	uint16_t crc = dif_info->dif_seed;
578331766Sken	uint32_t i;
579331766Sken	uint16_t checksum;
580331766Sken
581331766Sken	if ((dif == NULL)  || !dif_info->check_guard) {
582331766Sken		return TRUE;
583331766Sken	}
584331766Sken
585331766Sken	if (is_crc) {
586331766Sken		for (i = 0; i < addrlen_count; i++) {
587331766Sken			crc = ocs_scsi_dif_calc_crc(addrlen[i].vaddr, addrlen[i].length, crc);
588331766Sken		}
589331766Sken		return (crc == ocs_be16toh(dif->crc));
590331766Sken	} else {
591331766Sken		checksum = ocs_scsi_dif_calc_checksum(addrlen, addrlen_count);
592331766Sken
593331766Sken		return (checksum == dif->crc);
594331766Sken	}
595331766Sken}
596331766Sken
597331766Sken/**
598331766Sken * @brief Check the app tag of dif data
599331766Sken *
600331766Sken * @par Description
601331766Sken * Using the dif_info for the transfer, check the app tag.
602331766Sken *
603331766Sken * @param ocs Pointer to the ocs structure for logging.
604331766Sken * @param dif_info Pointer to HW DIF info data.
605331766Sken * @param exp_app_tag The value the app tag is expected to be.
606331766Sken * @param dif Pointer to the DIF data block being checked.
607331766Sken *
608331766Sken * @return Returns TRUE if app tag check is ok.
609331766Sken */
610331766Skenstatic uint32_t
611331766Skenocs_scsi_dif_check_app_tag(ocs_t *ocs, ocs_hw_dif_info_t *dif_info, uint16_t exp_app_tag, ocs_dif_t *dif)
612331766Sken{
613331766Sken	if ((dif == NULL)  || !dif_info->check_app_tag) {
614331766Sken		return TRUE;
615331766Sken	}
616331766Sken
617331766Sken	ocs_log_debug(ocs, "expected app tag 0x%x,  actual 0x%x\n",
618331766Sken		exp_app_tag, ocs_be16toh(dif->app_tag));
619331766Sken
620331766Sken	return (exp_app_tag == ocs_be16toh(dif->app_tag));
621331766Sken}
622331766Sken
623331766Sken/**
624331766Sken * @brief Check the ref tag of dif data
625331766Sken *
626331766Sken * @par Description
627331766Sken * Using the dif_info for the transfer, check the app tag.
628331766Sken *
629331766Sken * @param ocs Pointer to the ocs structure for logging.
630331766Sken * @param dif_info Pointer to HW DIF info data.
631331766Sken * @param exp_ref_tag The value the ref tag is expected to be.
632331766Sken * @param dif Pointer to the DIF data block being checked.
633331766Sken *
634331766Sken * @return Returns TRUE if ref tag check is ok.
635331766Sken */
636331766Skenstatic uint32_t
637331766Skenocs_scsi_dif_check_ref_tag(ocs_t *ocs, ocs_hw_dif_info_t *dif_info, uint32_t exp_ref_tag, ocs_dif_t *dif)
638331766Sken{
639331766Sken	if ((dif == NULL)  || !dif_info->check_ref_tag) {
640331766Sken		return TRUE;
641331766Sken	}
642331766Sken
643331766Sken	if (exp_ref_tag != ocs_be32toh(dif->ref_tag)) {
644331766Sken		ocs_log_debug(ocs, "expected ref tag 0x%x, actual 0x%x\n",
645331766Sken			exp_ref_tag, ocs_be32toh(dif->ref_tag));
646331766Sken		return FALSE;
647331766Sken	} else {
648331766Sken		return TRUE;
649331766Sken	}
650331766Sken}
651331766Sken
652331766Sken/**
653331766Sken * @brief Return count of SGE's required for request
654331766Sken *
655331766Sken * @par Description
656331766Sken * An accurate count of SGEs is computed and returned.
657331766Sken *
658331766Sken * @param hw_dif Pointer to HW dif information.
659331766Sken * @param sgl Pointer to SGL from back end.
660331766Sken * @param sgl_count Count of SGEs in SGL.
661331766Sken *
662331766Sken * @return Count of SGEs.
663331766Sken */
664331766Skenstatic uint32_t
665331766Skenocs_scsi_count_sgls(ocs_hw_dif_info_t *hw_dif, ocs_scsi_sgl_t *sgl, uint32_t sgl_count)
666331766Sken{
667331766Sken	uint32_t count = 0;
668331766Sken	uint32_t i;
669331766Sken
670331766Sken	/* Convert DIF Information */
671331766Sken	if (hw_dif->dif_oper != OCS_HW_DIF_OPER_DISABLED) {
672331766Sken
673331766Sken		/* If we're not DIF separate, then emit a seed SGE */
674331766Sken		if (!hw_dif->dif_separate) {
675331766Sken			count++;
676331766Sken		}
677331766Sken
678331766Sken		for (i = 0; i < sgl_count; i++) {
679331766Sken			/* If DIF is enabled, and DIF is separate, then append a SEED then DIF SGE */
680331766Sken			if (hw_dif->dif_separate) {
681331766Sken				count += 2;
682331766Sken			}
683331766Sken
684331766Sken			count++;
685331766Sken		}
686331766Sken	} else {
687331766Sken		count = sgl_count;
688331766Sken	}
689331766Sken	return count;
690331766Sken}
691331766Sken
692331766Skenstatic int32_t
693331766Skenocs_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)
694331766Sken{
695331766Sken	int32_t rc;
696331766Sken	uint32_t i;
697331766Sken	ocs_t *ocs = hw->os;
698331766Sken	uint32_t blocksize = 0;
699331766Sken	uint32_t blockcount;
700331766Sken
701331766Sken	ocs_assert(hio, -1);
702331766Sken
703331766Sken	/* Initialize HW SGL */
704331766Sken	rc = ocs_hw_io_init_sges(hw, hio, type);
705331766Sken	if (rc) {
706331766Sken		ocs_log_err(ocs, "ocs_hw_io_init_sges failed: %d\n", rc);
707331766Sken		return -1;
708331766Sken	}
709331766Sken
710331766Sken	/* Convert DIF Information */
711331766Sken	if (hw_dif->dif_oper != OCS_HW_DIF_OPER_DISABLED) {
712331766Sken
713331766Sken		/* If we're not DIF separate, then emit a seed SGE */
714331766Sken		if (!hw_dif->dif_separate) {
715331766Sken			rc = ocs_hw_io_add_seed_sge(hw, hio, hw_dif);
716331766Sken			if (rc) {
717331766Sken				return rc;
718331766Sken			}
719331766Sken		}
720331766Sken
721331766Sken		/* if we are doing DIF separate, then figure out the block size so that we
722331766Sken		 * can update the ref tag in the DIF seed SGE.   Also verify that the
723331766Sken		 * the sgl lengths are all multiples of the blocksize
724331766Sken		 */
725331766Sken		if (hw_dif->dif_separate) {
726331766Sken			switch(hw_dif->blk_size) {
727331766Sken			case OCS_HW_DIF_BK_SIZE_512:	blocksize = 512; break;
728331766Sken			case OCS_HW_DIF_BK_SIZE_1024:	blocksize = 1024; break;
729331766Sken			case OCS_HW_DIF_BK_SIZE_2048:	blocksize = 2048; break;
730331766Sken			case OCS_HW_DIF_BK_SIZE_4096:	blocksize = 4096; break;
731331766Sken			case OCS_HW_DIF_BK_SIZE_520:	blocksize = 520; break;
732331766Sken			case OCS_HW_DIF_BK_SIZE_4104:	blocksize = 4104; break;
733331766Sken			default:
734331766Sken				ocs_log_test(hw->os, "Inavlid hw_dif blocksize %d\n", hw_dif->blk_size);
735331766Sken				return -1;
736331766Sken			}
737331766Sken			for (i = 0; i < sgl_count; i++) {
738331766Sken				if ((sgl[i].len % blocksize) != 0) {
739331766Sken					ocs_log_test(hw->os, "sgl[%d] len of %ld is not multiple of blocksize\n",
740331766Sken						     i, sgl[i].len);
741331766Sken					return -1;
742331766Sken				}
743331766Sken			}
744331766Sken		}
745331766Sken
746331766Sken		for (i = 0; i < sgl_count; i++) {
747331766Sken			ocs_assert(sgl[i].addr, -1);
748331766Sken			ocs_assert(sgl[i].len, -1);
749331766Sken
750331766Sken			/* If DIF is enabled, and DIF is separate, then append a SEED then DIF SGE */
751331766Sken			if (hw_dif->dif_separate) {
752331766Sken				rc = ocs_hw_io_add_seed_sge(hw, hio, hw_dif);
753331766Sken				if (rc) {
754331766Sken					return rc;
755331766Sken				}
756331766Sken				rc = ocs_hw_io_add_dif_sge(hw, hio, sgl[i].dif_addr);
757331766Sken				if (rc) {
758331766Sken					return rc;
759331766Sken				}
760331766Sken				/* Update the ref_tag for the next DIF seed SGE */
761331766Sken				blockcount = sgl[i].len / blocksize;
762331766Sken				if (hw_dif->dif_oper == OCS_HW_DIF_OPER_INSERT) {
763331766Sken					hw_dif->ref_tag_repl += blockcount;
764331766Sken				} else {
765331766Sken					hw_dif->ref_tag_cmp += blockcount;
766331766Sken				}
767331766Sken			}
768331766Sken
769331766Sken			/* Add data SGE */
770331766Sken			rc = ocs_hw_io_add_sge(hw, hio, sgl[i].addr, sgl[i].len);
771331766Sken			if (rc) {
772331766Sken				ocs_log_err(ocs, "ocs_hw_io_add_sge failed: count=%d rc=%d\n",
773331766Sken						sgl_count, rc);
774331766Sken				return rc;
775331766Sken			}
776331766Sken		}
777331766Sken	} else {
778331766Sken		for (i = 0; i < sgl_count; i++) {
779331766Sken			ocs_assert(sgl[i].addr, -1);
780331766Sken			ocs_assert(sgl[i].len, -1);
781331766Sken
782331766Sken			/* Add data SGE */
783331766Sken			rc = ocs_hw_io_add_sge(hw, hio, sgl[i].addr, sgl[i].len);
784331766Sken			if (rc) {
785331766Sken				ocs_log_err(ocs, "ocs_hw_io_add_sge failed: count=%d rc=%d\n",
786331766Sken						sgl_count, rc);
787331766Sken				return rc;
788331766Sken			}
789331766Sken
790331766Sken		}
791331766Sken	}
792331766Sken	return 0;
793331766Sken}
794331766Sken
795331766Sken/**
796331766Sken * @ingroup scsi_api_base
797331766Sken * @brief Convert SCSI API T10 DIF information into the FC HW format.
798331766Sken *
799331766Sken * @param ocs Pointer to the ocs structure for logging.
800331766Sken * @param scsi_dif_info Pointer to the SCSI API T10 DIF fields.
801331766Sken * @param hw_dif_info Pointer to the FC HW API T10 DIF fields.
802331766Sken *
803331766Sken * @return Returns 0 on success, or a negative error code value on failure.
804331766Sken */
805331766Sken
806331766Skenstatic int32_t
807331766Skenocs_scsi_convert_dif_info(ocs_t *ocs, ocs_scsi_dif_info_t *scsi_dif_info, ocs_hw_dif_info_t *hw_dif_info)
808331766Sken{
809331766Sken	uint32_t dif_seed;
810331766Sken	ocs_memset(hw_dif_info, 0, sizeof(ocs_hw_dif_info_t));
811331766Sken
812331766Sken	if (scsi_dif_info == NULL) {
813331766Sken		hw_dif_info->dif_oper = OCS_HW_DIF_OPER_DISABLED;
814331766Sken		hw_dif_info->blk_size =  OCS_HW_DIF_BK_SIZE_NA;
815331766Sken		return 0;
816331766Sken	}
817331766Sken
818331766Sken	/* Convert the DIF operation */
819331766Sken	switch(scsi_dif_info->dif_oper) {
820331766Sken	case OCS_SCSI_DIF_OPER_IN_NODIF_OUT_CRC:
821331766Sken		hw_dif_info->dif_oper = OCS_HW_SGE_DIF_OP_IN_NODIF_OUT_CRC;
822331766Sken		hw_dif_info->dif = SLI4_DIF_INSERT;
823331766Sken		break;
824331766Sken	case OCS_SCSI_DIF_OPER_IN_CRC_OUT_NODIF:
825331766Sken		hw_dif_info->dif_oper = OCS_HW_SGE_DIF_OP_IN_CRC_OUT_NODIF;
826331766Sken		hw_dif_info->dif = SLI4_DIF_STRIP;
827331766Sken		break;
828331766Sken	case OCS_SCSI_DIF_OPER_IN_NODIF_OUT_CHKSUM:
829331766Sken		hw_dif_info->dif_oper = OCS_HW_SGE_DIF_OP_IN_NODIF_OUT_CHKSUM;
830331766Sken		hw_dif_info->dif = SLI4_DIF_INSERT;
831331766Sken		break;
832331766Sken	case OCS_SCSI_DIF_OPER_IN_CHKSUM_OUT_NODIF:
833331766Sken		hw_dif_info->dif_oper = OCS_HW_SGE_DIF_OP_IN_CHKSUM_OUT_NODIF;
834331766Sken		hw_dif_info->dif = SLI4_DIF_STRIP;
835331766Sken		break;
836331766Sken	case OCS_SCSI_DIF_OPER_IN_CRC_OUT_CRC:
837331766Sken		hw_dif_info->dif_oper = OCS_HW_SGE_DIF_OP_IN_CRC_OUT_CRC;
838331766Sken		hw_dif_info->dif = SLI4_DIF_PASS_THROUGH;
839331766Sken		break;
840331766Sken	case OCS_SCSI_DIF_OPER_IN_CHKSUM_OUT_CHKSUM:
841331766Sken		hw_dif_info->dif_oper = OCS_HW_SGE_DIF_OP_IN_CHKSUM_OUT_CHKSUM;
842331766Sken		hw_dif_info->dif = SLI4_DIF_PASS_THROUGH;
843331766Sken		break;
844331766Sken	case OCS_SCSI_DIF_OPER_IN_CRC_OUT_CHKSUM:
845331766Sken		hw_dif_info->dif_oper = OCS_HW_SGE_DIF_OP_IN_CRC_OUT_CHKSUM;
846331766Sken		hw_dif_info->dif = SLI4_DIF_PASS_THROUGH;
847331766Sken		break;
848331766Sken	case OCS_SCSI_DIF_OPER_IN_CHKSUM_OUT_CRC:
849331766Sken		hw_dif_info->dif_oper = OCS_HW_SGE_DIF_OP_IN_CHKSUM_OUT_CRC;
850331766Sken		hw_dif_info->dif = SLI4_DIF_PASS_THROUGH;
851331766Sken		break;
852331766Sken	case OCS_SCSI_DIF_OPER_IN_RAW_OUT_RAW:
853331766Sken		hw_dif_info->dif_oper = OCS_HW_SGE_DIF_OP_IN_RAW_OUT_RAW;
854331766Sken		hw_dif_info->dif = SLI4_DIF_PASS_THROUGH;
855331766Sken		break;
856331766Sken	default:
857331766Sken		ocs_log_test(ocs, "unhandled SCSI DIF operation %d\n",
858331766Sken			     scsi_dif_info->dif_oper);
859331766Sken		return -1;
860331766Sken	}
861331766Sken
862331766Sken	switch(scsi_dif_info->blk_size) {
863331766Sken	case OCS_SCSI_DIF_BK_SIZE_512:
864331766Sken		hw_dif_info->blk_size = OCS_HW_DIF_BK_SIZE_512;
865331766Sken		break;
866331766Sken	case OCS_SCSI_DIF_BK_SIZE_1024:
867331766Sken		hw_dif_info->blk_size = OCS_HW_DIF_BK_SIZE_1024;
868331766Sken		break;
869331766Sken	case OCS_SCSI_DIF_BK_SIZE_2048:
870331766Sken		hw_dif_info->blk_size = OCS_HW_DIF_BK_SIZE_2048;
871331766Sken		break;
872331766Sken	case OCS_SCSI_DIF_BK_SIZE_4096:
873331766Sken		hw_dif_info->blk_size = OCS_HW_DIF_BK_SIZE_4096;
874331766Sken		break;
875331766Sken	case OCS_SCSI_DIF_BK_SIZE_520:
876331766Sken		hw_dif_info->blk_size = OCS_HW_DIF_BK_SIZE_520;
877331766Sken		break;
878331766Sken	case OCS_SCSI_DIF_BK_SIZE_4104:
879331766Sken		hw_dif_info->blk_size = OCS_HW_DIF_BK_SIZE_4104;
880331766Sken		break;
881331766Sken	default:
882331766Sken		ocs_log_test(ocs, "unhandled SCSI DIF block size %d\n",
883331766Sken			     scsi_dif_info->blk_size);
884331766Sken		return -1;
885331766Sken	}
886331766Sken
887331766Sken	/* If the operation is an INSERT the tags provided are the ones that should be
888331766Sken	 * inserted, otherwise they're the ones to be checked against. */
889331766Sken	if (hw_dif_info->dif == SLI4_DIF_INSERT ) {
890331766Sken		hw_dif_info->ref_tag_repl = scsi_dif_info->ref_tag;
891331766Sken		hw_dif_info->app_tag_repl = scsi_dif_info->app_tag;
892331766Sken	} else {
893331766Sken		hw_dif_info->ref_tag_cmp = scsi_dif_info->ref_tag;
894331766Sken		hw_dif_info->app_tag_cmp = scsi_dif_info->app_tag;
895331766Sken	}
896331766Sken
897331766Sken	hw_dif_info->check_ref_tag = scsi_dif_info->check_ref_tag;
898331766Sken	hw_dif_info->check_app_tag = scsi_dif_info->check_app_tag;
899331766Sken	hw_dif_info->check_guard = scsi_dif_info->check_guard;
900331766Sken	hw_dif_info->auto_incr_ref_tag = 1;
901331766Sken	hw_dif_info->dif_separate = scsi_dif_info->dif_separate;
902331766Sken	hw_dif_info->disable_app_ffff = scsi_dif_info->disable_app_ffff;
903331766Sken	hw_dif_info->disable_app_ref_ffff = scsi_dif_info->disable_app_ref_ffff;
904331766Sken
905331766Sken	ocs_hw_get(&ocs->hw, OCS_HW_DIF_SEED, &dif_seed);
906331766Sken	hw_dif_info->dif_seed = dif_seed;
907331766Sken
908331766Sken	return 0;
909331766Sken}
910331766Sken
911331766Sken/**
912331766Sken * @ingroup scsi_api_base
913331766Sken * @brief This function logs the SGLs for an IO.
914331766Sken *
915331766Sken * @param io Pointer to the IO context.
916331766Sken */
917331766Skenstatic void ocs_log_sgl(ocs_io_t *io)
918331766Sken{
919331766Sken	ocs_hw_io_t *hio = io->hio;
920331766Sken	sli4_sge_t *data = NULL;
921331766Sken	uint32_t *dword = NULL;
922331766Sken	uint32_t i;
923331766Sken	uint32_t n_sge;
924331766Sken
925331766Sken	scsi_io_trace(io, "def_sgl at 0x%x 0x%08x\n",
926331766Sken		      ocs_addr32_hi(hio->def_sgl.phys),
927331766Sken		      ocs_addr32_lo(hio->def_sgl.phys));
928331766Sken	n_sge = (hio->sgl == &hio->def_sgl ? hio->n_sge : hio->def_sgl_count);
929331766Sken	for (i = 0, data = hio->def_sgl.virt; i < n_sge; i++, data++) {
930331766Sken		dword = (uint32_t*)data;
931331766Sken
932331766Sken		scsi_io_trace(io, "SGL %2d 0x%08x 0x%08x 0x%08x 0x%08x\n",
933331766Sken			 i, dword[0], dword[1], dword[2], dword[3]);
934331766Sken
935331766Sken		if (dword[2] & (1U << 31)) {
936331766Sken			break;
937331766Sken		}
938331766Sken	}
939331766Sken
940331766Sken	if (hio->ovfl_sgl != NULL &&
941331766Sken		hio->sgl == hio->ovfl_sgl) {
942331766Sken		scsi_io_trace(io, "Overflow at 0x%x 0x%08x\n",
943331766Sken			      ocs_addr32_hi(hio->ovfl_sgl->phys),
944331766Sken			      ocs_addr32_lo(hio->ovfl_sgl->phys));
945331766Sken		for (i = 0, data = hio->ovfl_sgl->virt; i < hio->n_sge; i++, data++) {
946331766Sken			dword = (uint32_t*)data;
947331766Sken
948331766Sken			scsi_io_trace(io, "SGL %2d 0x%08x 0x%08x 0x%08x 0x%08x\n",
949331766Sken				 i, dword[0], dword[1], dword[2], dword[3]);
950331766Sken			if (dword[2] & (1U << 31)) {
951331766Sken				break;
952331766Sken			}
953331766Sken		}
954331766Sken	}
955331766Sken
956331766Sken}
957331766Sken
958331766Sken
959331766Sken/**
960331766Sken * @brief Check pending error asynchronous callback function.
961331766Sken *
962331766Sken * @par Description
963331766Sken * Invoke the HW callback function for a given IO. This function is called
964331766Sken * from the NOP mailbox completion context.
965331766Sken *
966331766Sken * @param hw Pointer to HW object.
967331766Sken * @param status Completion status.
968331766Sken * @param mqe Mailbox completion queue entry.
969331766Sken * @param arg General purpose argument.
970331766Sken *
971331766Sken * @return Returns 0.
972331766Sken */
973331766Skenstatic int32_t
974331766Skenocs_scsi_check_pending_async_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg)
975331766Sken{
976331766Sken	ocs_io_t *io = arg;
977331766Sken
978331766Sken	if (io != NULL) {
979331766Sken		if (io->hw_cb != NULL) {
980331766Sken			ocs_hw_done_t cb = io->hw_cb;
981331766Sken
982331766Sken			io->hw_cb = NULL;
983331766Sken			cb(io->hio, NULL, 0, SLI4_FC_WCQE_STATUS_DISPATCH_ERROR, 0, io);
984331766Sken		}
985331766Sken	}
986331766Sken	return 0;
987331766Sken}
988331766Sken
989331766Sken/**
990331766Sken * @brief Check for pending IOs to dispatch.
991331766Sken *
992331766Sken * @par Description
993331766Sken * If there are IOs on the pending list, and a HW IO is available, then
994331766Sken * dispatch the IOs.
995331766Sken *
996331766Sken * @param ocs Pointer to the OCS structure.
997331766Sken *
998331766Sken * @return None.
999331766Sken */
1000331766Sken
1001331766Skenvoid
1002331766Skenocs_scsi_check_pending(ocs_t *ocs)
1003331766Sken{
1004331766Sken	ocs_xport_t *xport = ocs->xport;
1005331766Sken	ocs_io_t *io;
1006331766Sken	ocs_hw_io_t *hio;
1007331766Sken	int32_t status;
1008331766Sken	int count = 0;
1009331766Sken	int dispatch;
1010331766Sken
1011331766Sken	/* Guard against recursion */
1012331766Sken	if (ocs_atomic_add_return(&xport->io_pending_recursing, 1)) {
1013331766Sken		/* This function is already running.  Decrement and return. */
1014331766Sken		ocs_atomic_sub_return(&xport->io_pending_recursing, 1);
1015331766Sken		return;
1016331766Sken	}
1017331766Sken
1018331766Sken	do {
1019331766Sken		ocs_lock(&xport->io_pending_lock);
1020331766Sken			status = 0;
1021331766Sken			hio = NULL;
1022331766Sken			io = ocs_list_remove_head(&xport->io_pending_list);
1023331766Sken			if (io != NULL) {
1024331766Sken				if (io->io_type == OCS_IO_TYPE_ABORT) {
1025331766Sken					hio = NULL;
1026331766Sken				} else {
1027331766Sken					hio = ocs_hw_io_alloc(&ocs->hw);
1028331766Sken					if (hio == NULL) {
1029331766Sken						/*
1030331766Sken						 * No HW IO available.
1031331766Sken						 * Put IO back on the front of pending list
1032331766Sken						 */
1033331766Sken						ocs_list_add_head(&xport->io_pending_list, io);
1034331766Sken						io = NULL;
1035331766Sken					} else {
1036331766Sken						hio->eq = io->hw_priv;
1037331766Sken					}
1038331766Sken				}
1039331766Sken			}
1040331766Sken		/* Must drop the lock before dispatching the IO */
1041331766Sken		ocs_unlock(&xport->io_pending_lock);
1042331766Sken
1043331766Sken		if (io != NULL) {
1044331766Sken			count++;
1045331766Sken
1046331766Sken			/*
1047331766Sken			 * We pulled an IO off the pending list,
1048331766Sken			 * and either got an HW IO or don't need one
1049331766Sken			 */
1050331766Sken			ocs_atomic_sub_return(&xport->io_pending_count, 1);
1051331766Sken			if (hio == NULL) {
1052331766Sken				status = ocs_scsi_io_dispatch_no_hw_io(io);
1053331766Sken			} else {
1054331766Sken				status = ocs_scsi_io_dispatch_hw_io(io, hio);
1055331766Sken			}
1056331766Sken			if (status) {
1057331766Sken				/*
1058331766Sken				 * Invoke the HW callback, but do so in the separate execution context,
1059331766Sken				 * provided by the NOP mailbox completion processing context by using
1060331766Sken				 * ocs_hw_async_call()
1061331766Sken				 */
1062331766Sken				if (ocs_hw_async_call(&ocs->hw, ocs_scsi_check_pending_async_cb, io)) {
1063331766Sken					ocs_log_test(ocs, "call to ocs_hw_async_call() failed\n");
1064331766Sken				}
1065331766Sken			}
1066331766Sken		}
1067331766Sken	} while (io != NULL);
1068331766Sken
1069331766Sken
1070331766Sken	/*
1071331766Sken	 * If nothing was removed from the list,
1072331766Sken	 * we might be in a case where we need to abort an
1073331766Sken	 * active IO and the abort is on the pending list.
1074331766Sken	 * Look for an abort we can dispatch.
1075331766Sken	 */
1076331766Sken	if (count == 0 ) {
1077331766Sken		dispatch = 0;
1078331766Sken
1079331766Sken		ocs_lock(&xport->io_pending_lock);
1080331766Sken			ocs_list_foreach(&xport->io_pending_list, io) {
1081331766Sken				if (io->io_type == OCS_IO_TYPE_ABORT) {
1082331766Sken					if (io->io_to_abort->hio != NULL) {
1083331766Sken						/* This IO has a HW IO, so it is active.  Dispatch the abort. */
1084331766Sken						dispatch = 1;
1085331766Sken					} else {
1086331766Sken						/* Leave this abort on the pending list and keep looking */
1087331766Sken						dispatch = 0;
1088331766Sken					}
1089331766Sken				}
1090331766Sken				if (dispatch) {
1091331766Sken					ocs_list_remove(&xport->io_pending_list, io);
1092331766Sken					ocs_atomic_sub_return(&xport->io_pending_count, 1);
1093331766Sken					break;
1094331766Sken				}
1095331766Sken			}
1096331766Sken		ocs_unlock(&xport->io_pending_lock);
1097331766Sken
1098331766Sken		if (dispatch) {
1099331766Sken			status = ocs_scsi_io_dispatch_no_hw_io(io);
1100331766Sken			if (status) {
1101331766Sken				if (ocs_hw_async_call(&ocs->hw, ocs_scsi_check_pending_async_cb, io)) {
1102331766Sken					ocs_log_test(ocs, "call to ocs_hw_async_call() failed\n");
1103331766Sken				}
1104331766Sken			}
1105331766Sken		}
1106331766Sken	}
1107331766Sken
1108331766Sken	ocs_atomic_sub_return(&xport->io_pending_recursing, 1);
1109331766Sken	return;
1110331766Sken}
1111331766Sken
1112331766Sken/**
1113331766Sken * @brief Attempt to dispatch a non-abort IO
1114331766Sken *
1115331766Sken * @par Description
1116331766Sken * An IO is dispatched:
1117331766Sken * - if the pending list is not empty, add IO to pending list
1118331766Sken *   and call a function to process the pending list.
1119331766Sken * - if pending list is empty, try to allocate a HW IO. If none
1120331766Sken *   is available, place this IO at the tail of the pending IO
1121331766Sken *   list.
1122331766Sken * - if HW IO is available, attach this IO to the HW IO and
1123331766Sken *   submit it.
1124331766Sken *
1125331766Sken * @param io Pointer to IO structure.
1126331766Sken * @param cb Callback function.
1127331766Sken *
1128331766Sken * @return Returns 0 on success, a negative error code value on failure.
1129331766Sken */
1130331766Sken
1131331766Skenint32_t
1132331766Skenocs_scsi_io_dispatch(ocs_io_t *io, void *cb)
1133331766Sken{
1134331766Sken	ocs_hw_io_t *hio;
1135331766Sken	ocs_t *ocs = io->ocs;
1136331766Sken	ocs_xport_t *xport = ocs->xport;
1137331766Sken
1138331766Sken	ocs_assert(io->cmd_tgt || io->cmd_ini, -1);
1139331766Sken	ocs_assert((io->io_type != OCS_IO_TYPE_ABORT), -1);
1140331766Sken	io->hw_cb = cb;
1141331766Sken
1142331766Sken	/*
1143331766Sken	 * if this IO already has a HW IO, then this is either not the first phase of
1144331766Sken	 * the IO. Send it to the HW.
1145331766Sken	 */
1146331766Sken	if (io->hio != NULL) {
1147331766Sken		return ocs_scsi_io_dispatch_hw_io(io, io->hio);
1148331766Sken	}
1149331766Sken
1150331766Sken	/*
1151331766Sken	 * We don't already have a HW IO associated with the IO. First check
1152331766Sken	 * the pending list. If not empty, add IO to the tail and process the
1153331766Sken	 * pending list.
1154331766Sken	 */
1155331766Sken	ocs_lock(&xport->io_pending_lock);
1156331766Sken		if (!ocs_list_empty(&xport->io_pending_list)) {
1157331766Sken			/*
1158331766Sken			 * If this is a low latency request, the put at the front of the IO pending
1159331766Sken			 * queue, otherwise put it at the end of the queue.
1160331766Sken			 */
1161331766Sken			if (io->low_latency) {
1162331766Sken				ocs_list_add_head(&xport->io_pending_list, io);
1163331766Sken			} else {
1164331766Sken				ocs_list_add_tail(&xport->io_pending_list, io);
1165331766Sken			}
1166331766Sken			ocs_unlock(&xport->io_pending_lock);
1167331766Sken			ocs_atomic_add_return(&xport->io_pending_count, 1);
1168331766Sken			ocs_atomic_add_return(&xport->io_total_pending, 1);
1169331766Sken
1170331766Sken			/* process pending list */
1171331766Sken			ocs_scsi_check_pending(ocs);
1172331766Sken			return 0;
1173331766Sken		}
1174331766Sken	ocs_unlock(&xport->io_pending_lock);
1175331766Sken
1176331766Sken	/*
1177331766Sken	 * We don't have a HW IO associated with the IO and there's nothing
1178331766Sken	 * on the pending list. Attempt to allocate a HW IO and dispatch it.
1179331766Sken	 */
1180331766Sken	hio = ocs_hw_io_alloc(&io->ocs->hw);
1181331766Sken	if (hio == NULL) {
1182331766Sken
1183331766Sken		/* Couldn't get a HW IO. Save this IO on the pending list */
1184331766Sken		ocs_lock(&xport->io_pending_lock);
1185331766Sken			ocs_list_add_tail(&xport->io_pending_list, io);
1186331766Sken		ocs_unlock(&xport->io_pending_lock);
1187331766Sken
1188331766Sken		ocs_atomic_add_return(&xport->io_total_pending, 1);
1189331766Sken		ocs_atomic_add_return(&xport->io_pending_count, 1);
1190331766Sken		return 0;
1191331766Sken	}
1192331766Sken
1193331766Sken	/* We successfully allocated a HW IO; dispatch to HW */
1194331766Sken	return ocs_scsi_io_dispatch_hw_io(io, hio);
1195331766Sken}
1196331766Sken
1197331766Sken/**
1198331766Sken * @brief Attempt to dispatch an Abort IO.
1199331766Sken *
1200331766Sken * @par Description
1201331766Sken * An Abort IO is dispatched:
1202331766Sken * - if the pending list is not empty, add IO to pending list
1203331766Sken *   and call a function to process the pending list.
1204331766Sken * - if pending list is empty, send abort to the HW.
1205331766Sken *
1206331766Sken * @param io Pointer to IO structure.
1207331766Sken * @param cb Callback function.
1208331766Sken *
1209331766Sken * @return Returns 0 on success, a negative error code value on failure.
1210331766Sken */
1211331766Sken
1212331766Skenint32_t
1213331766Skenocs_scsi_io_dispatch_abort(ocs_io_t *io, void *cb)
1214331766Sken{
1215331766Sken	ocs_t *ocs = io->ocs;
1216331766Sken	ocs_xport_t *xport = ocs->xport;
1217331766Sken
1218331766Sken	ocs_assert((io->io_type == OCS_IO_TYPE_ABORT), -1);
1219331766Sken	io->hw_cb = cb;
1220331766Sken
1221331766Sken	/*
1222331766Sken	 * For aborts, we don't need a HW IO, but we still want to pass through
1223331766Sken	 * the pending list to preserve ordering. Thus, if the pending list is
1224331766Sken	 * not empty, add this abort to the pending list and process the pending list.
1225331766Sken	 */
1226331766Sken	ocs_lock(&xport->io_pending_lock);
1227331766Sken		if (!ocs_list_empty(&xport->io_pending_list)) {
1228331766Sken			ocs_list_add_tail(&xport->io_pending_list, io);
1229331766Sken			ocs_unlock(&xport->io_pending_lock);
1230331766Sken			ocs_atomic_add_return(&xport->io_pending_count, 1);
1231331766Sken			ocs_atomic_add_return(&xport->io_total_pending, 1);
1232331766Sken
1233331766Sken			/* process pending list */
1234331766Sken			ocs_scsi_check_pending(ocs);
1235331766Sken			return 0;
1236331766Sken		}
1237331766Sken	ocs_unlock(&xport->io_pending_lock);
1238331766Sken
1239331766Sken	/* nothing on pending list, dispatch abort */
1240331766Sken	return ocs_scsi_io_dispatch_no_hw_io(io);
1241331766Sken
1242331766Sken}
1243331766Sken
1244331766Sken/**
1245331766Sken * @brief Dispatch IO
1246331766Sken *
1247331766Sken * @par Description
1248331766Sken * An IO and its associated HW IO is dispatched to the HW.
1249331766Sken *
1250331766Sken * @param io Pointer to IO structure.
1251331766Sken * @param hio Pointer to HW IO structure from which IO will be
1252331766Sken * dispatched.
1253331766Sken *
1254331766Sken * @return Returns 0 on success, a negative error code value on failure.
1255331766Sken */
1256331766Sken
1257331766Skenstatic int32_t
1258331766Skenocs_scsi_io_dispatch_hw_io(ocs_io_t *io, ocs_hw_io_t *hio)
1259331766Sken{
1260331766Sken	int32_t rc;
1261331766Sken	ocs_t *ocs = io->ocs;
1262331766Sken
1263331766Sken	/* Got a HW IO; update ini/tgt_task_tag with HW IO info and dispatch */
1264331766Sken	io->hio = hio;
1265331766Sken	if (io->cmd_tgt) {
1266331766Sken		io->tgt_task_tag = hio->indicator;
1267331766Sken	} else if (io->cmd_ini) {
1268331766Sken		io->init_task_tag = hio->indicator;
1269331766Sken	}
1270331766Sken	io->hw_tag = hio->reqtag;
1271331766Sken
1272331766Sken	hio->eq = io->hw_priv;
1273331766Sken
1274331766Sken	/* Copy WQ steering */
1275331766Sken	switch(io->wq_steering) {
1276331766Sken	case OCS_SCSI_WQ_STEERING_CLASS >> OCS_SCSI_WQ_STEERING_SHIFT:
1277331766Sken		hio->wq_steering = OCS_HW_WQ_STEERING_CLASS;
1278331766Sken		break;
1279331766Sken	case OCS_SCSI_WQ_STEERING_REQUEST >> OCS_SCSI_WQ_STEERING_SHIFT:
1280331766Sken		hio->wq_steering = OCS_HW_WQ_STEERING_REQUEST;
1281331766Sken		break;
1282331766Sken	case OCS_SCSI_WQ_STEERING_CPU >> OCS_SCSI_WQ_STEERING_SHIFT:
1283331766Sken		hio->wq_steering = OCS_HW_WQ_STEERING_CPU;
1284331766Sken		break;
1285331766Sken	}
1286331766Sken
1287331766Sken
1288331766Sken	switch (io->io_type) {
1289331766Sken	case OCS_IO_TYPE_IO: {
1290331766Sken		uint32_t max_sgl;
1291331766Sken		uint32_t total_count;
1292331766Sken		uint32_t host_allocated;
1293331766Sken
1294331766Sken		ocs_hw_get(&ocs->hw, OCS_HW_N_SGL, &max_sgl);
1295331766Sken		ocs_hw_get(&ocs->hw, OCS_HW_SGL_CHAINING_HOST_ALLOCATED, &host_allocated);
1296331766Sken
1297331766Sken		/*
1298331766Sken		 * If the requested SGL is larger than the default size, then we can allocate
1299331766Sken		 * an overflow SGL.
1300331766Sken		 */
1301331766Sken		total_count = ocs_scsi_count_sgls(&io->hw_dif, io->sgl, io->sgl_count);
1302331766Sken
1303331766Sken		/*
1304331766Sken		 * Lancer requires us to allocate the chained memory area, but
1305331766Sken		 * Skyhawk must use the SGL list associated with another XRI.
1306331766Sken		 */
1307331766Sken		if (host_allocated && total_count > max_sgl) {
1308331766Sken			/* Compute count needed, the number extra plus 1 for the link sge */
1309331766Sken			uint32_t count = total_count - max_sgl + 1;
1310331766Sken			rc = ocs_dma_alloc(ocs, &io->ovfl_sgl, count*sizeof(sli4_sge_t), 64);
1311331766Sken			if (rc) {
1312331766Sken				ocs_log_err(ocs, "ocs_dma_alloc overflow sgl failed\n");
1313331766Sken				break;
1314331766Sken			}
1315331766Sken			rc = ocs_hw_io_register_sgl(&ocs->hw, io->hio, &io->ovfl_sgl, count);
1316331766Sken			if (rc) {
1317331766Sken				ocs_scsi_io_free_ovfl(io);
1318331766Sken				ocs_log_err(ocs, "ocs_hw_io_register_sgl() failed\n");
1319331766Sken				break;
1320331766Sken			}
1321331766Sken			/* EVT: update chained_io_count */
1322331766Sken			io->node->chained_io_count++;
1323331766Sken		}
1324331766Sken
1325331766Sken		rc = ocs_scsi_build_sgls(&ocs->hw, io->hio, &io->hw_dif, io->sgl, io->sgl_count, io->hio_type);
1326331766Sken		if (rc) {
1327331766Sken			ocs_scsi_io_free_ovfl(io);
1328331766Sken			break;
1329331766Sken		}
1330331766Sken
1331331766Sken		if (OCS_LOG_ENABLE_SCSI_TRACE(ocs)) {
1332331766Sken			ocs_log_sgl(io);
1333331766Sken		}
1334331766Sken
1335331766Sken		if (io->app_id) {
1336331766Sken			io->iparam.fcp_tgt.app_id = io->app_id;
1337331766Sken		}
1338331766Sken
1339331766Sken		rc = ocs_hw_io_send(&io->ocs->hw, io->hio_type, io->hio, io->wire_len, &io->iparam, &io->node->rnode,
1340331766Sken			io->hw_cb, io);
1341331766Sken		break;
1342331766Sken	}
1343331766Sken	case OCS_IO_TYPE_ELS:
1344331766Sken	case OCS_IO_TYPE_CT: {
1345331766Sken		rc = ocs_hw_srrs_send(&ocs->hw, io->hio_type, io->hio,
1346331766Sken			&io->els_req, io->wire_len,
1347331766Sken			&io->els_rsp, &io->node->rnode, &io->iparam,
1348331766Sken			io->hw_cb, io);
1349331766Sken		break;
1350331766Sken	}
1351331766Sken	case OCS_IO_TYPE_CT_RESP: {
1352331766Sken		rc = ocs_hw_srrs_send(&ocs->hw, io->hio_type, io->hio,
1353331766Sken			&io->els_rsp, io->wire_len,
1354331766Sken			NULL, &io->node->rnode, &io->iparam,
1355331766Sken			io->hw_cb, io);
1356331766Sken		break;
1357331766Sken	}
1358331766Sken	case OCS_IO_TYPE_BLS_RESP: {
1359331766Sken		/* no need to update tgt_task_tag for BLS response since the RX_ID
1360331766Sken		 * will be specified by the payload, not the XRI */
1361331766Sken		rc = ocs_hw_srrs_send(&ocs->hw, io->hio_type, io->hio,
1362331766Sken			NULL, 0, NULL, &io->node->rnode, &io->iparam, io->hw_cb, io);
1363331766Sken		break;
1364331766Sken	}
1365331766Sken	default:
1366331766Sken		scsi_io_printf(io, "Unknown IO type=%d\n", io->io_type);
1367331766Sken		rc = -1;
1368331766Sken		break;
1369331766Sken	}
1370331766Sken	return rc;
1371331766Sken}
1372331766Sken
1373331766Sken/**
1374331766Sken * @brief Dispatch IO
1375331766Sken *
1376331766Sken * @par Description
1377331766Sken * An IO that does require a HW IO is dispatched to the HW.
1378331766Sken *
1379331766Sken * @param io Pointer to IO structure.
1380331766Sken *
1381331766Sken * @return Returns 0 on success, or a negative error code value on failure.
1382331766Sken */
1383331766Sken
1384331766Skenstatic int32_t
1385331766Skenocs_scsi_io_dispatch_no_hw_io(ocs_io_t *io)
1386331766Sken{
1387331766Sken	int32_t rc;
1388331766Sken
1389331766Sken	switch (io->io_type) {
1390331766Sken	case OCS_IO_TYPE_ABORT: {
1391331766Sken		ocs_hw_io_t *hio_to_abort = NULL;
1392331766Sken		ocs_assert(io->io_to_abort, -1);
1393331766Sken		hio_to_abort = io->io_to_abort->hio;
1394331766Sken
1395331766Sken		if (hio_to_abort == NULL) {
1396331766Sken			/*
1397331766Sken			 * If "IO to abort" does not have an associated HW IO, immediately
1398331766Sken			 * make callback with success. The command must have been sent to
1399331766Sken			 * the backend, but the data phase has not yet started, so we don't
1400331766Sken			 * have a HW IO.
1401331766Sken			 *
1402331766Sken			 * Note: since the backend shims should be taking a reference
1403331766Sken			 * on io_to_abort, it should not be possible to have been completed
1404331766Sken			 * and freed by the backend before the abort got here.
1405331766Sken			 */
1406331766Sken			scsi_io_printf(io, "IO: " SCSI_IOFMT " not active\n",
1407331766Sken				       SCSI_IOFMT_ARGS(io->io_to_abort));
1408331766Sken			((ocs_hw_done_t)io->hw_cb)(io->hio, NULL, 0, SLI4_FC_WCQE_STATUS_SUCCESS, 0, io);
1409331766Sken			rc = 0;
1410331766Sken		} else {
1411331766Sken			/* HW IO is valid, abort it */
1412331766Sken			scsi_io_printf(io, "aborting " SCSI_IOFMT "\n", SCSI_IOFMT_ARGS(io->io_to_abort));
1413331766Sken			rc = ocs_hw_io_abort(&io->ocs->hw, hio_to_abort, io->send_abts,
1414331766Sken					      io->hw_cb, io);
1415331766Sken			if (rc) {
1416331766Sken				int status = SLI4_FC_WCQE_STATUS_SUCCESS;
1417331766Sken				if ((rc != OCS_HW_RTN_IO_NOT_ACTIVE) &&
1418331766Sken				    (rc != OCS_HW_RTN_IO_ABORT_IN_PROGRESS)) {
1419331766Sken					status = -1;
1420331766Sken					scsi_io_printf(io, "Failed to abort IO: " SCSI_IOFMT " status=%d\n",
1421331766Sken						       SCSI_IOFMT_ARGS(io->io_to_abort), rc);
1422331766Sken				}
1423331766Sken				((ocs_hw_done_t)io->hw_cb)(io->hio, NULL, 0, status, 0, io);
1424331766Sken				rc = 0;
1425331766Sken			}
1426331766Sken		}
1427331766Sken
1428331766Sken		break;
1429331766Sken	}
1430331766Sken	default:
1431331766Sken		scsi_io_printf(io, "Unknown IO type=%d\n", io->io_type);
1432331766Sken		rc = -1;
1433331766Sken		break;
1434331766Sken	}
1435331766Sken	return rc;
1436331766Sken}
1437331766Sken
1438331766Sken/**
1439331766Sken * @ingroup scsi_api_base
1440331766Sken * @brief Send read/write data.
1441331766Sken *
1442331766Sken * @par Description
1443331766Sken * This call is made by a target-server to initiate a SCSI read or write data phase, transferring
1444331766Sken * data between the target to the remote initiator. The payload is specified by the
1445331766Sken * scatter-gather list @c sgl of length @c sgl_count. The @c wire_len argument
1446331766Sken * specifies the payload length (independent of the scatter-gather list cumulative length).
1447331766Sken * @n @n
1448331766Sken * The @c flags argument has one bit, OCS_SCSI_LAST_DATAPHASE, which is a hint to the base
1449331766Sken * driver that it may use auto SCSI response features if the hardware supports it.
1450331766Sken * @n @n
1451331766Sken * Upon completion, the callback function @b cb is called with flags indicating that the
1452331766Sken * IO has completed (OCS_SCSI_IO_COMPL) and another data phase or response may be sent;
1453331766Sken * that the IO has completed and no response needs to be sent (OCS_SCSI_IO_COMPL_NO_RSP);
1454331766Sken * or that the IO was aborted (OCS_SCSI_IO_ABORTED).
1455331766Sken *
1456331766Sken * @param io Pointer to the IO context.
1457331766Sken * @param flags Flags controlling the sending of data.
1458331766Sken * @param dif_info Pointer to T10 DIF fields, or NULL if no DIF.
1459331766Sken * @param sgl Pointer to the payload scatter-gather list.
1460331766Sken * @param sgl_count Count of the scatter-gather list elements.
1461331766Sken * @param xwire_len Length of the payload on wire, in bytes.
1462331766Sken * @param type HW IO type.
1463331766Sken * @param enable_ar Enable auto-response if true.
1464331766Sken * @param cb Completion callback.
1465331766Sken * @param arg Application-supplied callback data.
1466331766Sken *
1467331766Sken * @return Returns 0 on success, or a negative error code value on failure.
1468331766Sken */
1469331766Sken
1470331766Skenstatic inline int32_t
1471331766Skenocs_scsi_xfer_data(ocs_io_t *io, uint32_t flags,
1472331766Sken	ocs_scsi_dif_info_t *dif_info,
1473331766Sken	ocs_scsi_sgl_t *sgl, uint32_t sgl_count, uint32_t xwire_len,
1474331766Sken	ocs_hw_io_type_e type, int enable_ar,
1475331766Sken	ocs_scsi_io_cb_t cb, void *arg)
1476331766Sken{
1477331766Sken	int32_t rc;
1478331766Sken	ocs_t *ocs;
1479331766Sken	uint32_t disable_ar_tgt_dif = FALSE;
1480331766Sken	size_t residual = 0;
1481331766Sken
1482331766Sken	if ((dif_info != NULL) && (dif_info->dif_oper == OCS_SCSI_DIF_OPER_DISABLED)) {
1483331766Sken		dif_info = NULL;
1484331766Sken	}
1485331766Sken
1486331766Sken	ocs_assert(io, -1);
1487331766Sken
1488331766Sken	if (dif_info != NULL) {
1489331766Sken		ocs_hw_get(&io->ocs->hw, OCS_HW_DISABLE_AR_TGT_DIF, &disable_ar_tgt_dif);
1490331766Sken		if (disable_ar_tgt_dif) {
1491331766Sken			enable_ar = FALSE;
1492331766Sken		}
1493331766Sken	}
1494331766Sken
1495331766Sken	io->sgl_count = sgl_count;
1496331766Sken
1497331766Sken	/* If needed, copy SGL */
1498331766Sken	if (sgl && (sgl != io->sgl)) {
1499331766Sken		ocs_assert(sgl_count <= io->sgl_allocated, -1);
1500331766Sken		ocs_memcpy(io->sgl, sgl, sgl_count*sizeof(*io->sgl));
1501331766Sken	}
1502331766Sken
1503331766Sken	ocs = io->ocs;
1504331766Sken	ocs_assert(ocs, -1);
1505331766Sken	ocs_assert(io->node, -1);
1506331766Sken
1507331766Sken	scsi_io_trace(io, "%s wire_len %d\n", (type == OCS_HW_IO_TARGET_READ) ? "send" : "recv", xwire_len);
1508331766Sken
1509331766Sken	ocs_assert(sgl, -1);
1510331766Sken	ocs_assert(sgl_count > 0, -1);
1511331766Sken	ocs_assert(io->exp_xfer_len > io->transferred, -1);
1512331766Sken
1513331766Sken	io->hio_type = type;
1514331766Sken
1515331766Sken	io->scsi_tgt_cb = cb;
1516331766Sken	io->scsi_tgt_cb_arg = arg;
1517331766Sken
1518331766Sken	rc = ocs_scsi_convert_dif_info(ocs, dif_info, &io->hw_dif);
1519331766Sken	if (rc) {
1520331766Sken		return rc;
1521331766Sken	}
1522331766Sken
1523331766Sken	/* If DIF is used, then save lba for error recovery */
1524331766Sken	if (dif_info) {
1525331766Sken		io->scsi_dif_info = *dif_info;
1526331766Sken	}
1527331766Sken
1528331766Sken	io->wire_len = MIN(xwire_len, io->exp_xfer_len - io->transferred);
1529331766Sken	residual = (xwire_len - io->wire_len);
1530331766Sken
1531331766Sken	ocs_memset(&io->iparam, 0, sizeof(io->iparam));
1532331766Sken	io->iparam.fcp_tgt.ox_id = io->init_task_tag;
1533331766Sken	io->iparam.fcp_tgt.offset = io->transferred;
1534331766Sken	io->iparam.fcp_tgt.dif_oper = io->hw_dif.dif;
1535331766Sken	io->iparam.fcp_tgt.blk_size = io->hw_dif.blk_size;
1536331766Sken	io->iparam.fcp_tgt.cs_ctl = io->cs_ctl;
1537331766Sken	io->iparam.fcp_tgt.timeout = io->timeout;
1538331766Sken
1539331766Sken	/* if this is the last data phase and there is no residual, enable
1540331766Sken	 * auto-good-response
1541331766Sken	 */
1542331766Sken	if (enable_ar && (flags & OCS_SCSI_LAST_DATAPHASE) &&
1543331766Sken		(residual == 0) && ((io->transferred + io->wire_len) == io->exp_xfer_len) && (!(flags & OCS_SCSI_NO_AUTO_RESPONSE))) {
1544331766Sken		io->iparam.fcp_tgt.flags |= SLI4_IO_AUTO_GOOD_RESPONSE;
1545331766Sken		io->auto_resp = TRUE;
1546331766Sken	} else {
1547331766Sken		io->auto_resp = FALSE;
1548331766Sken	}
1549331766Sken
1550331766Sken	/* save this transfer length */
1551331766Sken	io->xfer_req = io->wire_len;
1552331766Sken
1553331766Sken	/* Adjust the transferred count to account for overrun
1554331766Sken	 * when the residual is calculated in ocs_scsi_send_resp
1555331766Sken	 */
1556331766Sken	io->transferred += residual;
1557331766Sken
1558331766Sken	/* Adjust the SGL size if there is overrun */
1559331766Sken
1560331766Sken	if (residual) {
1561331766Sken		ocs_scsi_sgl_t  *sgl_ptr = &io->sgl[sgl_count-1];
1562331766Sken
1563331766Sken		while (residual) {
1564331766Sken			size_t len = sgl_ptr->len;
1565331766Sken			if ( len > residual) {
1566331766Sken				sgl_ptr->len = len - residual;
1567331766Sken				residual = 0;
1568331766Sken			} else {
1569331766Sken				sgl_ptr->len = 0;
1570331766Sken				residual -= len;
1571331766Sken				io->sgl_count--;
1572331766Sken			}
1573331766Sken			sgl_ptr--;
1574331766Sken		}
1575331766Sken	}
1576331766Sken
1577331766Sken	/* Set latency and WQ steering */
1578331766Sken	io->low_latency = (flags & OCS_SCSI_LOW_LATENCY) != 0;
1579331766Sken	io->wq_steering = (flags & OCS_SCSI_WQ_STEERING_MASK) >> OCS_SCSI_WQ_STEERING_SHIFT;
1580331766Sken	io->wq_class = (flags & OCS_SCSI_WQ_CLASS_MASK) >> OCS_SCSI_WQ_CLASS_SHIFT;
1581331766Sken
1582331766Sken	return ocs_scsi_io_dispatch(io, ocs_target_io_cb);
1583331766Sken}
1584331766Sken
1585331766Sken
1586331766Skenint32_t
1587331766Skenocs_scsi_send_rd_data(ocs_io_t *io, uint32_t flags,
1588331766Sken	ocs_scsi_dif_info_t *dif_info,
1589331766Sken	ocs_scsi_sgl_t *sgl, uint32_t sgl_count, uint32_t len,
1590331766Sken	ocs_scsi_io_cb_t cb, void *arg)
1591331766Sken{
1592331766Sken	return ocs_scsi_xfer_data(io, flags, dif_info, sgl, sgl_count, len, OCS_HW_IO_TARGET_READ,
1593331766Sken				  enable_tsend_auto_resp(io->ocs), cb, arg);
1594331766Sken}
1595331766Sken
1596331766Skenint32_t
1597331766Skenocs_scsi_recv_wr_data(ocs_io_t *io, uint32_t flags,
1598331766Sken	ocs_scsi_dif_info_t *dif_info,
1599331766Sken	ocs_scsi_sgl_t *sgl, uint32_t sgl_count, uint32_t len,
1600331766Sken	ocs_scsi_io_cb_t cb, void *arg)
1601331766Sken{
1602331766Sken	return ocs_scsi_xfer_data(io, flags, dif_info, sgl, sgl_count, len, OCS_HW_IO_TARGET_WRITE,
1603331766Sken				  enable_treceive_auto_resp(io->ocs), cb, arg);
1604331766Sken}
1605331766Sken
1606331766Sken/**
1607331766Sken * @ingroup scsi_api_base
1608331766Sken * @brief Free overflow SGL.
1609331766Sken *
1610331766Sken * @par Description
1611331766Sken * Free the overflow SGL if it is present.
1612331766Sken *
1613331766Sken * @param io Pointer to IO object.
1614331766Sken *
1615331766Sken * @return None.
1616331766Sken */
1617331766Skenstatic void
1618331766Skenocs_scsi_io_free_ovfl(ocs_io_t *io) {
1619331766Sken	if (io->ovfl_sgl.size) {
1620331766Sken		ocs_dma_free(io->ocs, &io->ovfl_sgl);
1621331766Sken	}
1622331766Sken}
1623331766Sken
1624331766Sken/**
1625331766Sken * @ingroup scsi_api_base
1626331766Sken * @brief Send response data.
1627331766Sken *
1628331766Sken * @par Description
1629331766Sken * This function is used by a target-server to send the SCSI response data to a remote
1630331766Sken * initiator node. The target-server populates the @c ocs_scsi_cmd_resp_t
1631331766Sken * argument with scsi status, status qualifier, sense data, and response data, as
1632331766Sken * needed.
1633331766Sken * @n @n
1634331766Sken * Upon completion, the callback function @c cb is invoked. The target-server will generally
1635331766Sken * clean up its IO context resources and call ocs_scsi_io_complete().
1636331766Sken *
1637331766Sken * @param io Pointer to the IO context.
1638331766Sken * @param flags Flags to control sending of the SCSI response.
1639331766Sken * @param rsp Pointer to the response data populated by the caller.
1640331766Sken * @param cb Completion callback.
1641331766Sken * @param arg Application-specified completion callback argument.
1642331766Sken
1643331766Sken * @return Returns 0 on success, or a negative error code value on failure.
1644331766Sken */
1645331766Skenint32_t
1646331766Skenocs_scsi_send_resp(ocs_io_t *io, uint32_t flags, ocs_scsi_cmd_resp_t *rsp, ocs_scsi_io_cb_t cb, void *arg)
1647331766Sken{
1648331766Sken	ocs_t *ocs;
1649331766Sken	int32_t residual;
1650331766Sken	int auto_resp = TRUE;		/* Always try auto resp */
1651331766Sken	uint8_t scsi_status = 0;
1652331766Sken	uint16_t scsi_status_qualifier = 0;
1653331766Sken	uint8_t *sense_data = NULL;
1654331766Sken	uint32_t sense_data_length = 0;
1655331766Sken
1656331766Sken	ocs_assert(io, -1);
1657331766Sken
1658331766Sken	ocs = io->ocs;
1659331766Sken	ocs_assert(ocs, -1);
1660331766Sken
1661331766Sken	ocs_assert(io->node, -1);
1662331766Sken
1663331766Sken	ocs_scsi_convert_dif_info(ocs, NULL, &io->hw_dif);
1664331766Sken
1665331766Sken	if (rsp) {
1666331766Sken		scsi_status = rsp->scsi_status;
1667331766Sken		scsi_status_qualifier = rsp->scsi_status_qualifier;
1668331766Sken		sense_data = rsp->sense_data;
1669331766Sken		sense_data_length = rsp->sense_data_length;
1670331766Sken		residual = rsp->residual;
1671331766Sken	} else {
1672331766Sken		residual = io->exp_xfer_len - io->transferred;
1673331766Sken	}
1674331766Sken
1675331766Sken	io->wire_len = 0;
1676331766Sken	io->hio_type = OCS_HW_IO_TARGET_RSP;
1677331766Sken
1678331766Sken	io->scsi_tgt_cb = cb;
1679331766Sken	io->scsi_tgt_cb_arg = arg;
1680331766Sken
1681331766Sken	ocs_memset(&io->iparam, 0, sizeof(io->iparam));
1682331766Sken	io->iparam.fcp_tgt.ox_id = io->init_task_tag;
1683331766Sken	io->iparam.fcp_tgt.offset = 0;
1684331766Sken	io->iparam.fcp_tgt.cs_ctl = io->cs_ctl;
1685331766Sken	io->iparam.fcp_tgt.timeout = io->timeout;
1686331766Sken
1687331766Sken	/* Set low latency queueing request */
1688331766Sken	io->low_latency = (flags & OCS_SCSI_LOW_LATENCY) != 0;
1689331766Sken	io->wq_steering = (flags & OCS_SCSI_WQ_STEERING_MASK) >> OCS_SCSI_WQ_STEERING_SHIFT;
1690331766Sken	io->wq_class = (flags & OCS_SCSI_WQ_CLASS_MASK) >> OCS_SCSI_WQ_CLASS_SHIFT;
1691331766Sken
1692331766Sken	if ((scsi_status != 0) || residual || sense_data_length) {
1693331766Sken		fcp_rsp_iu_t *fcprsp = io->rspbuf.virt;
1694331766Sken
1695331766Sken		if (!fcprsp) {
1696331766Sken			ocs_log_err(ocs, "NULL response buffer\n");
1697331766Sken			return -1;
1698331766Sken		}
1699331766Sken
1700331766Sken		auto_resp = FALSE;
1701331766Sken
1702331766Sken		ocs_memset(fcprsp, 0, sizeof(*fcprsp));
1703331766Sken
1704331766Sken		io->wire_len += (sizeof(*fcprsp) - sizeof(fcprsp->data));
1705331766Sken
1706331766Sken		fcprsp->scsi_status = scsi_status;
1707331766Sken		*((uint16_t*)fcprsp->status_qualifier) = ocs_htobe16(scsi_status_qualifier);
1708331766Sken
1709331766Sken		/* set residual status if necessary */
1710331766Sken		if (residual != 0) {
1711331766Sken			/* FCP: if data transferred is less than the amount expected, then this is an
1712331766Sken			 * underflow.  If data transferred would have been greater than the amount expected
1713331766Sken			 * then this is an overflow
1714331766Sken			 */
1715331766Sken			if (residual > 0) {
1716331766Sken				fcprsp->flags |= FCP_RESID_UNDER;
1717331766Sken				*((uint32_t *)fcprsp->fcp_resid) = ocs_htobe32(residual);
1718331766Sken			} else {
1719331766Sken				fcprsp->flags |= FCP_RESID_OVER;
1720331766Sken				*((uint32_t *)fcprsp->fcp_resid) = ocs_htobe32(-residual);
1721331766Sken			}
1722331766Sken		}
1723331766Sken
1724331766Sken		if (sense_data && sense_data_length) {
1725331766Sken			ocs_assert(sense_data_length <= sizeof(fcprsp->data), -1);
1726331766Sken			fcprsp->flags |= FCP_SNS_LEN_VALID;
1727331766Sken			ocs_memcpy(fcprsp->data, sense_data, sense_data_length);
1728331766Sken			*((uint32_t*)fcprsp->fcp_sns_len) = ocs_htobe32(sense_data_length);
1729331766Sken			io->wire_len += sense_data_length;
1730331766Sken		}
1731331766Sken
1732331766Sken		io->sgl[0].addr = io->rspbuf.phys;
1733331766Sken		io->sgl[0].dif_addr = 0;
1734331766Sken		io->sgl[0].len = io->wire_len;
1735331766Sken		io->sgl_count = 1;
1736331766Sken	}
1737331766Sken
1738331766Sken	if (auto_resp) {
1739331766Sken		io->iparam.fcp_tgt.flags |= SLI4_IO_AUTO_GOOD_RESPONSE;
1740331766Sken	}
1741331766Sken
1742331766Sken	return ocs_scsi_io_dispatch(io, ocs_target_io_cb);
1743331766Sken}
1744331766Sken
1745331766Sken/**
1746331766Sken * @ingroup scsi_api_base
1747331766Sken * @brief Send TMF response data.
1748331766Sken *
1749331766Sken * @par Description
1750331766Sken * This function is used by a target-server to send SCSI TMF response data to a remote
1751331766Sken * initiator node.
1752331766Sken * Upon completion, the callback function @c cb is invoked. The target-server will generally
1753331766Sken * clean up its IO context resources and call ocs_scsi_io_complete().
1754331766Sken *
1755331766Sken * @param io Pointer to the IO context.
1756331766Sken * @param rspcode TMF response code.
1757331766Sken * @param addl_rsp_info Additional TMF response information (may be NULL for zero data).
1758331766Sken * @param cb Completion callback.
1759331766Sken * @param arg Application-specified completion callback argument.
1760331766Sken *
1761331766Sken * @return Returns 0 on success, or a negative error code value on failure.
1762331766Sken */
1763331766Skenint32_t
1764331766Skenocs_scsi_send_tmf_resp(ocs_io_t *io, ocs_scsi_tmf_resp_e rspcode, uint8_t addl_rsp_info[3],
1765331766Sken		ocs_scsi_io_cb_t cb, void *arg)
1766331766Sken{
1767331766Sken	int32_t rc = -1;
1768331766Sken	ocs_t *ocs = NULL;
1769331766Sken	fcp_rsp_iu_t *fcprsp = NULL;
1770331766Sken	fcp_rsp_info_t *rspinfo = NULL;
1771331766Sken	uint8_t fcp_rspcode;
1772331766Sken
1773331766Sken	ocs_assert(io, -1);
1774331766Sken	ocs_assert(io->ocs, -1);
1775331766Sken	ocs_assert(io->node, -1);
1776331766Sken
1777331766Sken	ocs = io->ocs;
1778331766Sken
1779331766Sken	io->wire_len = 0;
1780331766Sken	ocs_scsi_convert_dif_info(ocs, NULL, &io->hw_dif);
1781331766Sken
1782331766Sken	switch(rspcode) {
1783331766Sken	case OCS_SCSI_TMF_FUNCTION_COMPLETE:
1784331766Sken		fcp_rspcode = FCP_TMF_COMPLETE;
1785331766Sken		break;
1786331766Sken	case OCS_SCSI_TMF_FUNCTION_SUCCEEDED:
1787331766Sken	case OCS_SCSI_TMF_FUNCTION_IO_NOT_FOUND:
1788331766Sken		fcp_rspcode = FCP_TMF_SUCCEEDED;
1789331766Sken		break;
1790331766Sken	case OCS_SCSI_TMF_FUNCTION_REJECTED:
1791331766Sken		fcp_rspcode = FCP_TMF_REJECTED;
1792331766Sken		break;
1793331766Sken	case OCS_SCSI_TMF_INCORRECT_LOGICAL_UNIT_NUMBER:
1794331766Sken		fcp_rspcode = FCP_TMF_INCORRECT_LUN;
1795331766Sken		break;
1796331766Sken	case OCS_SCSI_TMF_SERVICE_DELIVERY:
1797331766Sken		fcp_rspcode = FCP_TMF_FAILED;
1798331766Sken		break;
1799331766Sken	default:
1800331766Sken		fcp_rspcode = FCP_TMF_REJECTED;
1801331766Sken		break;
1802331766Sken	}
1803331766Sken
1804331766Sken	io->hio_type = OCS_HW_IO_TARGET_RSP;
1805331766Sken
1806331766Sken	io->scsi_tgt_cb = cb;
1807331766Sken	io->scsi_tgt_cb_arg = arg;
1808331766Sken
1809331766Sken	if (io->tmf_cmd == OCS_SCSI_TMF_ABORT_TASK) {
1810331766Sken		rc = ocs_target_send_bls_resp(io, cb, arg);
1811331766Sken		return rc;
1812331766Sken	}
1813331766Sken
1814331766Sken	/* populate the FCP TMF response */
1815331766Sken	fcprsp = io->rspbuf.virt;
1816331766Sken	ocs_memset(fcprsp, 0, sizeof(*fcprsp));
1817331766Sken
1818331766Sken	fcprsp->flags |= FCP_RSP_LEN_VALID;
1819331766Sken
1820331766Sken	rspinfo = (fcp_rsp_info_t*) fcprsp->data;
1821331766Sken	if (addl_rsp_info != NULL) {
1822331766Sken		ocs_memcpy(rspinfo->addl_rsp_info, addl_rsp_info, sizeof(rspinfo->addl_rsp_info));
1823331766Sken	}
1824331766Sken	rspinfo->rsp_code = fcp_rspcode;
1825331766Sken
1826331766Sken	io->wire_len = sizeof(*fcprsp) - sizeof(fcprsp->data) + sizeof(*rspinfo);
1827331766Sken
1828331766Sken	*((uint32_t*)fcprsp->fcp_rsp_len) = ocs_htobe32(sizeof(*rspinfo));
1829331766Sken
1830331766Sken	io->sgl[0].addr = io->rspbuf.phys;
1831331766Sken	io->sgl[0].dif_addr = 0;
1832331766Sken	io->sgl[0].len = io->wire_len;
1833331766Sken	io->sgl_count = 1;
1834331766Sken
1835331766Sken	ocs_memset(&io->iparam, 0, sizeof(io->iparam));
1836331766Sken	io->iparam.fcp_tgt.ox_id = io->init_task_tag;
1837331766Sken	io->iparam.fcp_tgt.offset = 0;
1838331766Sken	io->iparam.fcp_tgt.cs_ctl = io->cs_ctl;
1839331766Sken	io->iparam.fcp_tgt.timeout = io->timeout;
1840331766Sken
1841331766Sken	rc = ocs_scsi_io_dispatch(io, ocs_target_io_cb);
1842331766Sken
1843331766Sken	return rc;
1844331766Sken}
1845331766Sken
1846331766Sken
1847331766Sken/**
1848331766Sken * @brief Process target abort callback.
1849331766Sken *
1850331766Sken * @par Description
1851331766Sken * Accepts HW abort requests.
1852331766Sken *
1853331766Sken * @param hio HW IO context.
1854331766Sken * @param rnode Remote node.
1855331766Sken * @param length Length of response data.
1856331766Sken * @param status Completion status.
1857331766Sken * @param ext_status Extended completion status.
1858331766Sken * @param app Application-specified callback data.
1859331766Sken *
1860331766Sken * @return Returns 0 on success, or a negative error code value on failure.
1861331766Sken */
1862331766Sken
1863331766Skenstatic int32_t
1864331766Skenocs_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)
1865331766Sken{
1866331766Sken	ocs_io_t *io = app;
1867331766Sken	ocs_t *ocs;
1868331766Sken	ocs_scsi_io_status_e scsi_status;
1869331766Sken
1870331766Sken	ocs_assert(io, -1);
1871331766Sken	ocs_assert(io->ocs, -1);
1872331766Sken
1873331766Sken	ocs = io->ocs;
1874331766Sken
1875331766Sken	if (io->abort_cb) {
1876331766Sken		ocs_scsi_io_cb_t abort_cb = io->abort_cb;
1877331766Sken		void *abort_cb_arg = io->abort_cb_arg;
1878331766Sken
1879331766Sken		io->abort_cb = NULL;
1880331766Sken		io->abort_cb_arg = NULL;
1881331766Sken
1882331766Sken		switch (status) {
1883331766Sken		case SLI4_FC_WCQE_STATUS_SUCCESS:
1884331766Sken			scsi_status = OCS_SCSI_STATUS_GOOD;
1885331766Sken			break;
1886331766Sken		case SLI4_FC_WCQE_STATUS_LOCAL_REJECT:
1887331766Sken			switch (ext_status) {
1888331766Sken			case SLI4_FC_LOCAL_REJECT_NO_XRI:
1889331766Sken				scsi_status = OCS_SCSI_STATUS_NO_IO;
1890331766Sken				break;
1891331766Sken			case SLI4_FC_LOCAL_REJECT_ABORT_IN_PROGRESS:
1892331766Sken				scsi_status = OCS_SCSI_STATUS_ABORT_IN_PROGRESS;
1893331766Sken				break;
1894331766Sken			default:
1895331766Sken				/* TODO: we have seen 0x15 (abort in progress) */
1896331766Sken				scsi_status = OCS_SCSI_STATUS_ERROR;
1897331766Sken				break;
1898331766Sken			}
1899331766Sken			break;
1900331766Sken		case SLI4_FC_WCQE_STATUS_FCP_RSP_FAILURE:
1901331766Sken			scsi_status = OCS_SCSI_STATUS_CHECK_RESPONSE;
1902331766Sken			break;
1903331766Sken		default:
1904331766Sken			scsi_status = OCS_SCSI_STATUS_ERROR;
1905331766Sken			break;
1906331766Sken		}
1907331766Sken		/* invoke callback */
1908331766Sken		abort_cb(io->io_to_abort, scsi_status, 0, abort_cb_arg);
1909331766Sken	}
1910331766Sken
1911331766Sken	ocs_assert(io != io->io_to_abort, -1);
1912331766Sken
1913331766Sken	/* done with IO to abort */
1914331766Sken	ocs_ref_put(&io->io_to_abort->ref); /* ocs_ref_get(): ocs_scsi_tgt_abort_io() */
1915331766Sken
1916331766Sken	ocs_io_free(ocs, io);
1917331766Sken
1918331766Sken	ocs_scsi_check_pending(ocs);
1919331766Sken	return 0;
1920331766Sken}
1921331766Sken
1922331766Sken/**
1923331766Sken * @ingroup scsi_api_base
1924331766Sken * @brief Abort a target IO.
1925331766Sken *
1926331766Sken * @par Description
1927331766Sken * This routine is called from a SCSI target-server. It initiates an abort of a
1928331766Sken * previously-issued target data phase or response request.
1929331766Sken *
1930331766Sken * @param io IO context.
1931331766Sken * @param cb SCSI target server callback.
1932331766Sken * @param arg SCSI target server supplied callback argument.
1933331766Sken *
1934331766Sken * @return Returns 0 on success, or a non-zero value on failure.
1935331766Sken */
1936331766Skenint32_t
1937331766Skenocs_scsi_tgt_abort_io(ocs_io_t *io, ocs_scsi_io_cb_t cb, void *arg)
1938331766Sken{
1939331766Sken	ocs_t *ocs;
1940331766Sken	ocs_xport_t *xport;
1941331766Sken	int32_t rc;
1942331766Sken
1943331766Sken	ocs_io_t *abort_io = NULL;
1944331766Sken	ocs_assert(io, -1);
1945331766Sken	ocs_assert(io->node, -1);
1946331766Sken	ocs_assert(io->ocs, -1);
1947331766Sken
1948331766Sken	ocs = io->ocs;
1949331766Sken	xport = ocs->xport;
1950331766Sken
1951331766Sken	/* take a reference on IO being aborted */
1952331766Sken	if ((ocs_ref_get_unless_zero(&io->ref) == 0)) {
1953331766Sken		/* command no longer active */
1954331766Sken		scsi_io_printf(io, "command no longer active\n");
1955331766Sken		return -1;
1956331766Sken	}
1957331766Sken
1958331766Sken	/*
1959331766Sken	 * allocate a new IO to send the abort request. Use ocs_io_alloc() directly, as
1960331766Sken	 * we need an IO object that will not fail allocation due to allocations being
1961331766Sken	 * disabled (in ocs_scsi_io_alloc())
1962331766Sken	 */
1963331766Sken	abort_io = ocs_io_alloc(ocs);
1964331766Sken	if (abort_io == NULL) {
1965331766Sken		ocs_atomic_add_return(&xport->io_alloc_failed_count, 1);
1966331766Sken		ocs_ref_put(&io->ref); /* ocs_ref_get(): same function */
1967331766Sken		return -1;
1968331766Sken	}
1969331766Sken
1970331766Sken	/* Save the target server callback and argument */
1971331766Sken	ocs_assert(abort_io->hio == NULL, -1);
1972331766Sken
1973331766Sken	/* set generic fields */
1974331766Sken	abort_io->cmd_tgt = TRUE;
1975331766Sken	abort_io->node = io->node;
1976331766Sken
1977331766Sken	/* set type and abort-specific fields */
1978331766Sken	abort_io->io_type = OCS_IO_TYPE_ABORT;
1979331766Sken	abort_io->display_name = "tgt_abort";
1980331766Sken	abort_io->io_to_abort = io;
1981331766Sken	abort_io->send_abts = FALSE;
1982331766Sken	abort_io->abort_cb = cb;
1983331766Sken	abort_io->abort_cb_arg = arg;
1984331766Sken
1985331766Sken	/* now dispatch IO */
1986331766Sken	rc = ocs_scsi_io_dispatch_abort(abort_io, ocs_target_abort_cb);
1987331766Sken	if (rc) {
1988331766Sken		ocs_ref_put(&io->ref); /* ocs_ref_get(): same function */
1989331766Sken	}
1990331766Sken	return rc;
1991331766Sken}
1992331766Sken
1993331766Sken/**
1994331766Sken * @brief Process target BLS response callback.
1995331766Sken *
1996331766Sken * @par Description
1997331766Sken * Accepts HW abort requests.
1998331766Sken *
1999331766Sken * @param hio HW IO context.
2000331766Sken * @param rnode Remote node.
2001331766Sken * @param length Length of response data.
2002331766Sken * @param status Completion status.
2003331766Sken * @param ext_status Extended completion status.
2004331766Sken * @param app Application-specified callback data.
2005331766Sken *
2006331766Sken * @return Returns 0 on success, or a negative error code value on failure.
2007331766Sken */
2008331766Sken
2009331766Skenstatic int32_t
2010331766Skenocs_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)
2011331766Sken{
2012331766Sken	ocs_io_t *io = app;
2013331766Sken	ocs_t *ocs;
2014331766Sken	ocs_scsi_io_status_e bls_status;
2015331766Sken
2016331766Sken	ocs_assert(io, -1);
2017331766Sken	ocs_assert(io->ocs, -1);
2018331766Sken
2019331766Sken	ocs = io->ocs;
2020331766Sken
2021331766Sken	/* BLS isn't really a "SCSI" concept, but use SCSI status */
2022331766Sken	if (status) {
2023331766Sken		io_error_log(io, "s=%#x x=%#x\n", status, ext_status);
2024331766Sken		bls_status = OCS_SCSI_STATUS_ERROR;
2025331766Sken	} else {
2026331766Sken		bls_status = OCS_SCSI_STATUS_GOOD;
2027331766Sken	}
2028331766Sken
2029331766Sken	if (io->bls_cb) {
2030331766Sken		ocs_scsi_io_cb_t bls_cb = io->bls_cb;
2031331766Sken		void *bls_cb_arg = io->bls_cb_arg;
2032331766Sken
2033331766Sken		io->bls_cb = NULL;
2034331766Sken		io->bls_cb_arg = NULL;
2035331766Sken
2036331766Sken		/* invoke callback */
2037331766Sken		bls_cb(io, bls_status, 0, bls_cb_arg);
2038331766Sken	}
2039331766Sken
2040331766Sken	ocs_scsi_check_pending(ocs);
2041331766Sken	return 0;
2042331766Sken}
2043331766Sken
2044331766Sken/**
2045331766Sken * @brief Complete abort request.
2046331766Sken *
2047331766Sken * @par Description
2048331766Sken * An abort request is completed by posting a BA_ACC for the IO that requested the abort.
2049331766Sken *
2050331766Sken * @param io Pointer to the IO context.
2051331766Sken * @param cb Callback function to invoke upon completion.
2052331766Sken * @param arg Application-specified completion callback argument.
2053331766Sken *
2054331766Sken * @return Returns 0 on success, or a negative error code value on failure.
2055331766Sken */
2056331766Sken
2057331766Skenstatic int32_t
2058331766Skenocs_target_send_bls_resp(ocs_io_t *io, ocs_scsi_io_cb_t cb, void *arg)
2059331766Sken{
2060331766Sken	int32_t rc;
2061331766Sken	fc_ba_acc_payload_t *acc;
2062331766Sken
2063331766Sken	ocs_assert(io, -1);
2064331766Sken
2065331766Sken	/* fill out IO structure with everything needed to send BA_ACC */
2066331766Sken	ocs_memset(&io->iparam, 0, sizeof(io->iparam));
2067331766Sken	io->iparam.bls.ox_id = io->init_task_tag;
2068331766Sken	io->iparam.bls.rx_id = io->abort_rx_id;
2069331766Sken
2070331766Sken	acc = (void *)io->iparam.bls.payload;
2071331766Sken
2072331766Sken	ocs_memset(io->iparam.bls.payload, 0, sizeof(io->iparam.bls.payload));
2073331766Sken	acc->ox_id = io->iparam.bls.ox_id;
2074331766Sken	acc->rx_id = io->iparam.bls.rx_id;
2075331766Sken	acc->high_seq_cnt = UINT16_MAX;
2076331766Sken
2077331766Sken	/* generic io fields have already been populated */
2078331766Sken
2079331766Sken	/* set type and BLS-specific fields */
2080331766Sken	io->io_type = OCS_IO_TYPE_BLS_RESP;
2081331766Sken	io->display_name = "bls_rsp";
2082331766Sken	io->hio_type = OCS_HW_BLS_ACC;
2083331766Sken	io->bls_cb = cb;
2084331766Sken	io->bls_cb_arg = arg;
2085331766Sken
2086331766Sken	/* dispatch IO */
2087331766Sken	rc = ocs_scsi_io_dispatch(io, ocs_target_bls_resp_cb);
2088331766Sken	return rc;
2089331766Sken}
2090331766Sken
2091331766Sken/**
2092331766Sken * @ingroup scsi_api_base
2093331766Sken * @brief Notify the base driver that the IO is complete.
2094331766Sken *
2095331766Sken * @par Description
2096331766Sken * This function is called by a target-server to notify the base driver that an IO
2097331766Sken * has completed, allowing for the base driver to free resources.
2098331766Sken * @n
2099331766Sken * @n @b Note: This function is not called by initiator-clients.
2100331766Sken *
2101331766Sken * @param io Pointer to IO context.
2102331766Sken *
2103331766Sken * @return None.
2104331766Sken */
2105331766Skenvoid
2106331766Skenocs_scsi_io_complete(ocs_io_t *io)
2107331766Sken{
2108331766Sken	ocs_assert(io);
2109331766Sken
2110331766Sken	if (!ocs_io_busy(io)) {
2111331766Sken		ocs_log_test(io->ocs, "Got completion for non-busy io with tag 0x%x\n", io->tag);
2112331766Sken		return;
2113331766Sken	}
2114331766Sken
2115331766Sken	scsi_io_trace(io, "freeing io 0x%p %s\n", io, io->display_name);
2116331766Sken	ocs_assert(ocs_ref_read_count(&io->ref) > 0);
2117331766Sken	ocs_ref_put(&io->ref); /* ocs_ref_get(): ocs_scsi_io_alloc() */
2118331766Sken}
2119331766Sken
2120331766Sken
2121331766Sken/**
2122331766Sken * @brief Handle initiator IO completion.
2123331766Sken *
2124331766Sken * @par Description
2125331766Sken * This callback is made upon completion of an initiator operation (initiator read/write command).
2126331766Sken *
2127331766Sken * @param hio HW IO context.
2128331766Sken * @param rnode Remote node.
2129331766Sken * @param length Length of completion data.
2130331766Sken * @param status Completion status.
2131331766Sken * @param ext_status Extended completion status.
2132331766Sken * @param app Application-specified callback data.
2133331766Sken *
2134331766Sken * @return None.
2135331766Sken */
2136331766Sken
2137331766Skenstatic void
2138331766Skenocs_initiator_io_cb(ocs_hw_io_t *hio, ocs_remote_node_t *rnode, uint32_t length,
2139331766Sken	int32_t status, uint32_t ext_status, void *app)
2140331766Sken{
2141331766Sken	ocs_io_t *io = app;
2142331766Sken	ocs_t *ocs;
2143331766Sken	ocs_scsi_io_status_e scsi_status;
2144331766Sken
2145331766Sken	ocs_assert(io);
2146331766Sken	ocs_assert(io->scsi_ini_cb);
2147331766Sken
2148331766Sken	scsi_io_trace(io, "status x%x ext_status x%x\n", status, ext_status);
2149331766Sken
2150331766Sken	ocs = io->ocs;
2151331766Sken	ocs_assert(ocs);
2152331766Sken
2153331766Sken	ocs_scsi_io_free_ovfl(io);
2154331766Sken
2155331766Sken	/* Call target server completion */
2156331766Sken	if (io->scsi_ini_cb) {
2157331766Sken		fcp_rsp_iu_t *fcprsp = io->rspbuf.virt;
2158331766Sken		ocs_scsi_cmd_resp_t rsp;
2159331766Sken		ocs_scsi_rsp_io_cb_t cb = io->scsi_ini_cb;
2160331766Sken		uint32_t flags = 0;
2161331766Sken		uint8_t *pd = fcprsp->data;
2162331766Sken
2163331766Sken		/* Clear the callback before invoking the callback */
2164331766Sken		io->scsi_ini_cb = NULL;
2165331766Sken
2166331766Sken		ocs_memset(&rsp, 0, sizeof(rsp));
2167331766Sken
2168331766Sken		/* Unless status is FCP_RSP_FAILURE, fcprsp is not filled in */
2169331766Sken		switch (status) {
2170331766Sken		case SLI4_FC_WCQE_STATUS_SUCCESS:
2171331766Sken			scsi_status = OCS_SCSI_STATUS_GOOD;
2172331766Sken			break;
2173331766Sken		case SLI4_FC_WCQE_STATUS_FCP_RSP_FAILURE:
2174331766Sken			scsi_status = OCS_SCSI_STATUS_CHECK_RESPONSE;
2175331766Sken			rsp.scsi_status = fcprsp->scsi_status;
2176331766Sken			rsp.scsi_status_qualifier = ocs_be16toh(*((uint16_t*)fcprsp->status_qualifier));
2177331766Sken
2178331766Sken			if (fcprsp->flags & FCP_RSP_LEN_VALID) {
2179331766Sken				rsp.response_data = pd;
2180331766Sken				rsp.response_data_length = ocs_fc_getbe32(fcprsp->fcp_rsp_len);
2181331766Sken				pd += rsp.response_data_length;
2182331766Sken			}
2183331766Sken			if (fcprsp->flags & FCP_SNS_LEN_VALID) {
2184331766Sken				uint32_t sns_len = ocs_fc_getbe32(fcprsp->fcp_sns_len);
2185331766Sken				rsp.sense_data = pd;
2186331766Sken				rsp.sense_data_length = sns_len;
2187331766Sken				pd += sns_len;
2188331766Sken			}
2189331766Sken			/* Set residual */
2190331766Sken			if (fcprsp->flags & FCP_RESID_OVER) {
2191331766Sken				rsp.residual = -ocs_fc_getbe32(fcprsp->fcp_resid);
2192331766Sken				rsp.response_wire_length = length;
2193331766Sken			} else	if (fcprsp->flags & FCP_RESID_UNDER) {
2194331766Sken				rsp.residual = ocs_fc_getbe32(fcprsp->fcp_resid);
2195331766Sken				rsp.response_wire_length = length;
2196331766Sken			}
2197331766Sken
2198331766Sken			/*
2199331766Sken			 * Note: The FCP_RSP_FAILURE can be returned for initiator IOs when the total data
2200331766Sken			 * placed does not match the requested length even if the status is good. If
2201331766Sken			 * the status is all zeroes, then we have to assume that a frame(s) were
2202331766Sken			 * dropped and change the status to LOCAL_REJECT/OUT_OF_ORDER_DATA
2203331766Sken			 */
2204331766Sken			if (length != io->wire_len) {
2205331766Sken				uint32_t rsp_len = ext_status;
2206331766Sken				uint8_t *rsp_bytes = io->rspbuf.virt;
2207331766Sken				uint32_t i;
2208331766Sken				uint8_t all_zeroes = (rsp_len > 0);
2209331766Sken				/* Check if the rsp is zero */
2210331766Sken				for (i = 0; i < rsp_len; i++) {
2211331766Sken					if (rsp_bytes[i] != 0) {
2212331766Sken						all_zeroes = FALSE;
2213331766Sken						break;
2214331766Sken					}
2215331766Sken				}
2216331766Sken				if (all_zeroes) {
2217331766Sken					scsi_status = OCS_SCSI_STATUS_ERROR;
2218331766Sken					ocs_log_test(io->ocs, "[%s]" SCSI_IOFMT "local reject=0x%02x\n",
2219331766Sken						     io->node->display_name, SCSI_IOFMT_ARGS(io),
2220331766Sken						     SLI4_FC_LOCAL_REJECT_OUT_OF_ORDER_DATA);
2221331766Sken				}
2222331766Sken			}
2223331766Sken			break;
2224331766Sken		case SLI4_FC_WCQE_STATUS_LOCAL_REJECT:
2225331766Sken			if (ext_status == SLI4_FC_LOCAL_REJECT_SEQUENCE_TIMEOUT) {
2226331766Sken				scsi_status = OCS_SCSI_STATUS_COMMAND_TIMEOUT;
2227331766Sken			} else {
2228331766Sken				scsi_status = OCS_SCSI_STATUS_ERROR;
2229331766Sken			}
2230331766Sken			break;
2231331766Sken		case SLI4_FC_WCQE_STATUS_DI_ERROR:
2232331766Sken			if (ext_status & 0x01) {
2233331766Sken				scsi_status = OCS_SCSI_STATUS_DIF_GUARD_ERROR;
2234331766Sken			} else if (ext_status & 0x02) {
2235331766Sken				scsi_status = OCS_SCSI_STATUS_DIF_APP_TAG_ERROR;
2236331766Sken			} else if (ext_status & 0x04) {
2237331766Sken				scsi_status = OCS_SCSI_STATUS_DIF_REF_TAG_ERROR;
2238331766Sken			} else {
2239331766Sken				scsi_status = OCS_SCSI_STATUS_DIF_UNKNOWN_ERROR;
2240331766Sken			}
2241331766Sken			break;
2242331766Sken		default:
2243331766Sken			scsi_status = OCS_SCSI_STATUS_ERROR;
2244331766Sken			break;
2245331766Sken		}
2246331766Sken
2247331766Sken		cb(io, scsi_status, &rsp, flags, io->scsi_ini_cb_arg);
2248331766Sken
2249331766Sken	}
2250331766Sken	ocs_scsi_check_pending(ocs);
2251331766Sken}
2252331766Sken
2253331766Sken/**
2254331766Sken * @ingroup scsi_api_base
2255331766Sken * @brief Initiate initiator read IO.
2256331766Sken *
2257331766Sken * @par Description
2258331766Sken * This call is made by an initiator-client to send a SCSI read command. The payload
2259331766Sken * for the command is given by a scatter-gather list @c sgl for @c sgl_count
2260331766Sken * entries.
2261331766Sken * @n @n
2262331766Sken * Upon completion, the callback @b cb is invoked and passed request status.
2263331766Sken * If the command completed successfully, the callback is given SCSI response data.
2264331766Sken *
2265331766Sken * @param node Pointer to the node.
2266331766Sken * @param io Pointer to the IO context.
2267331766Sken * @param lun LUN value.
2268331766Sken * @param cdb Pointer to the CDB.
2269331766Sken * @param cdb_len Length of the CDB.
2270331766Sken * @param dif_info Pointer to the T10 DIF fields, or NULL if no DIF.
2271331766Sken * @param sgl Pointer to the scatter-gather list.
2272331766Sken * @param sgl_count Count of the scatter-gather list elements.
2273331766Sken * @param wire_len Length of the payload.
2274331766Sken * @param cb Completion callback.
2275331766Sken * @param arg Application-specified completion callback argument.
2276331766Sken *
2277331766Sken * @return Returns 0 on success, or a negative error code value on failure.
2278331766Sken */
2279331766Skenint32_t
2280331766Skenocs_scsi_send_rd_io(ocs_node_t *node, ocs_io_t *io, uint64_t lun, void *cdb, uint32_t cdb_len,
2281331766Sken	ocs_scsi_dif_info_t *dif_info,
2282331766Sken	ocs_scsi_sgl_t *sgl, uint32_t sgl_count, uint32_t wire_len,
2283331766Sken	ocs_scsi_rsp_io_cb_t cb, void *arg)
2284331766Sken{
2285331766Sken	int32_t rc;
2286331766Sken
2287331766Sken	rc = ocs_scsi_send_io(OCS_HW_IO_INITIATOR_READ, node, io, lun, 0, cdb, cdb_len, dif_info, sgl, sgl_count,
2288331766Sken			      wire_len, 0, cb, arg);
2289331766Sken
2290331766Sken	return rc;
2291331766Sken}
2292331766Sken
2293331766Sken/**
2294331766Sken * @ingroup scsi_api_base
2295331766Sken * @brief Initiate initiator write IO.
2296331766Sken *
2297331766Sken * @par Description
2298331766Sken * This call is made by an initiator-client to send a SCSI write command. The payload
2299331766Sken * for the command is given by a scatter-gather list @c sgl for @c sgl_count
2300331766Sken * entries.
2301331766Sken * @n @n
2302331766Sken * Upon completion, the callback @c cb is invoked and passed request status. If the command
2303331766Sken * completed successfully, the callback is given SCSI response data.
2304331766Sken *
2305331766Sken * @param node Pointer to the node.
2306331766Sken * @param io Pointer to IO context.
2307331766Sken * @param lun LUN value.
2308331766Sken * @param cdb Pointer to the CDB.
2309331766Sken * @param cdb_len Length of the CDB.
2310331766Sken * @param dif_info Pointer to the T10 DIF fields, or NULL if no DIF.
2311331766Sken * @param sgl Pointer to the scatter-gather list.
2312331766Sken * @param sgl_count Count of the scatter-gather list elements.
2313331766Sken * @param wire_len Length of the payload.
2314331766Sken * @param cb Completion callback.
2315331766Sken * @param arg Application-specified completion callback argument.
2316331766Sken *
2317331766Sken * @return Returns 0 on success, or a negative error code value on failure.
2318331766Sken */
2319331766Skenint32_t ocs_scsi_send_wr_io(ocs_node_t *node, ocs_io_t *io, uint64_t lun, void *cdb, uint32_t cdb_len,
2320331766Sken	ocs_scsi_dif_info_t *dif_info,
2321331766Sken	ocs_scsi_sgl_t *sgl, uint32_t sgl_count, uint32_t wire_len,
2322331766Sken	ocs_scsi_rsp_io_cb_t cb, void *arg)
2323331766Sken{
2324331766Sken	int32_t rc;
2325331766Sken
2326331766Sken	rc = ocs_scsi_send_io(OCS_HW_IO_INITIATOR_WRITE, node, io, lun, 0, cdb, cdb_len, dif_info, sgl, sgl_count,
2327331766Sken			      wire_len, 0, cb, arg);
2328331766Sken
2329331766Sken	return rc;
2330331766Sken}
2331331766Sken
2332331766Sken/**
2333331766Sken * @ingroup scsi_api_base
2334331766Sken * @brief Initiate initiator write IO.
2335331766Sken *
2336331766Sken * @par Description
2337331766Sken * This call is made by an initiator-client to send a SCSI write command. The payload
2338331766Sken * for the command is given by a scatter-gather list @c sgl for @c sgl_count
2339331766Sken * entries.
2340331766Sken * @n @n
2341331766Sken * Upon completion, the callback @c cb is invoked and passed request status. If the command
2342331766Sken * completed successfully, the callback is given SCSI response data.
2343331766Sken *
2344331766Sken * @param node Pointer to the node.
2345331766Sken * @param io Pointer to IO context.
2346331766Sken * @param lun LUN value.
2347331766Sken * @param cdb Pointer to the CDB.
2348331766Sken * @param cdb_len Length of the CDB.
2349331766Sken * @param dif_info Pointer to the T10 DIF fields, or NULL if no DIF.
2350331766Sken * @param sgl Pointer to the scatter-gather list.
2351331766Sken * @param sgl_count Count of the scatter-gather list elements.
2352331766Sken * @param wire_len Length of the payload.
2353331766Sken * @param first_burst Number of first burst bytes to send.
2354331766Sken * @param cb Completion callback.
2355331766Sken * @param arg Application-specified completion callback argument.
2356331766Sken *
2357331766Sken * @return Returns 0 on success, or a negative error code value on failure.
2358331766Sken */
2359331766Skenint32_t
2360331766Skenocs_scsi_send_wr_io_first_burst(ocs_node_t *node, ocs_io_t *io, uint64_t lun, void *cdb, uint32_t cdb_len,
2361331766Sken	ocs_scsi_dif_info_t *dif_info,
2362331766Sken	ocs_scsi_sgl_t *sgl, uint32_t sgl_count, uint32_t wire_len, uint32_t first_burst,
2363331766Sken	ocs_scsi_rsp_io_cb_t cb, void *arg)
2364331766Sken{
2365331766Sken	int32_t rc;
2366331766Sken
2367331766Sken	rc = ocs_scsi_send_io(OCS_HW_IO_INITIATOR_WRITE, node, io, lun, 0, cdb, cdb_len, dif_info, sgl, sgl_count,
2368331766Sken			      wire_len, 0, cb, arg);
2369331766Sken
2370331766Sken	return rc;
2371331766Sken}
2372331766Sken
2373331766Sken/**
2374331766Sken * @ingroup scsi_api_base
2375331766Sken * @brief Initiate initiator SCSI command with no data.
2376331766Sken *
2377331766Sken * @par Description
2378331766Sken * This call is made by an initiator-client to send a SCSI command with no data.
2379331766Sken * @n @n
2380331766Sken * Upon completion, the callback @c cb is invoked and passed request status. If the command
2381331766Sken * completed successfully, the callback is given SCSI response data.
2382331766Sken *
2383331766Sken * @param node Pointer to the node.
2384331766Sken * @param io Pointer to the IO context.
2385331766Sken * @param lun LUN value.
2386331766Sken * @param cdb Pointer to the CDB.
2387331766Sken * @param cdb_len Length of the CDB.
2388331766Sken * @param cb Completion callback.
2389331766Sken * @param arg Application-specified completion callback argument.
2390331766Sken *
2391331766Sken * @return Returns 0 on success, or a negative error code value on failure.
2392331766Sken */
2393331766Skenint32_t ocs_scsi_send_nodata_io(ocs_node_t *node, ocs_io_t *io, uint64_t lun, void *cdb, uint32_t cdb_len,
2394331766Sken	ocs_scsi_rsp_io_cb_t cb, void *arg)
2395331766Sken{
2396331766Sken	int32_t rc;
2397331766Sken
2398331766Sken	rc = ocs_scsi_send_io(OCS_HW_IO_INITIATOR_NODATA, node, io, lun, 0, cdb, cdb_len, NULL, NULL, 0, 0, 0, cb, arg);
2399331766Sken
2400331766Sken	return rc;
2401331766Sken}
2402331766Sken/**
2403331766Sken * @ingroup scsi_api_base
2404331766Sken * @brief Initiate initiator task management operation.
2405331766Sken *
2406331766Sken * @par Description
2407331766Sken * This command is used to send a SCSI task management function command. If the command
2408331766Sken * requires it (QUERY_TASK_SET for example), a payload may be associated with the command.
2409331766Sken * If no payload is required, then @c sgl_count may be zero and @c sgl is ignored.
2410331766Sken * @n @n
2411331766Sken * Upon completion @c cb is invoked with status and SCSI response data.
2412331766Sken *
2413331766Sken * @param node Pointer to the node.
2414331766Sken * @param io Pointer to the IO context.
2415331766Sken * @param io_to_abort Pointer to the IO context to abort in the
2416331766Sken * case of OCS_SCSI_TMF_ABORT_TASK. Note: this can point to the
2417331766Sken * same the same ocs_io_t as @c io, provided that @c io does not
2418331766Sken * have any outstanding work requests.
2419331766Sken * @param lun LUN value.
2420331766Sken * @param tmf Task management command.
2421331766Sken * @param sgl Pointer to the scatter-gather list.
2422331766Sken * @param sgl_count Count of the scatter-gather list elements.
2423331766Sken * @param len Length of the payload.
2424331766Sken * @param cb Completion callback.
2425331766Sken * @param arg Application-specified completion callback argument.
2426331766Sken *
2427331766Sken * @return Returns 0 on success, or a negative error code value on failure.
2428331766Sken */
2429331766Skenint32_t
2430331766Skenocs_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,
2431331766Sken	ocs_scsi_sgl_t *sgl, uint32_t sgl_count, uint32_t len, ocs_scsi_rsp_io_cb_t cb, void *arg)
2432331766Sken{
2433331766Sken	int32_t rc;
2434331766Sken	ocs_assert(io, -1);
2435331766Sken
2436331766Sken	if (tmf == OCS_SCSI_TMF_ABORT_TASK) {
2437331766Sken		ocs_assert(io_to_abort, -1);
2438331766Sken
2439331766Sken		/* take a reference on IO being aborted */
2440331766Sken		if ((ocs_ref_get_unless_zero(&io_to_abort->ref) == 0)) {
2441331766Sken			/* command no longer active */
2442331766Sken			scsi_io_printf(io, "command no longer active\n");
2443331766Sken			return -1;
2444331766Sken		}
2445331766Sken		/* generic io fields have already been populated */
2446331766Sken
2447331766Sken		/* abort-specific fields */
2448331766Sken		io->io_type = OCS_IO_TYPE_ABORT;
2449331766Sken		io->display_name = "abort_task";
2450331766Sken		io->io_to_abort = io_to_abort;
2451331766Sken		io->send_abts = TRUE;
2452331766Sken		io->scsi_ini_cb = cb;
2453331766Sken		io->scsi_ini_cb_arg = arg;
2454331766Sken
2455331766Sken		/* now dispatch IO */
2456331766Sken		rc = ocs_scsi_io_dispatch_abort(io, ocs_scsi_abort_io_cb);
2457331766Sken		if (rc) {
2458331766Sken			scsi_io_printf(io, "Failed to dispatch abort\n");
2459331766Sken			ocs_ref_put(&io->ref); /* ocs_ref_get(): same function */
2460331766Sken		}
2461331766Sken	} else {
2462331766Sken		io->display_name = "tmf";
2463331766Sken		rc = ocs_scsi_send_io(OCS_HW_IO_INITIATOR_READ, node, io, lun, tmf, NULL, 0, NULL,
2464331766Sken				      sgl, sgl_count, len, 0, cb, arg);
2465331766Sken	}
2466331766Sken
2467331766Sken	return rc;
2468331766Sken}
2469331766Sken
2470331766Sken/**
2471331766Sken * @ingroup scsi_api_base
2472331766Sken * @brief Send an FCP IO.
2473331766Sken *
2474331766Sken * @par Description
2475331766Sken * An FCP read/write IO command, with optional task management flags, is sent to @c node.
2476331766Sken *
2477331766Sken * @param type HW IO type to send.
2478331766Sken * @param node Pointer to the node destination of the IO.
2479331766Sken * @param io Pointer to the IO context.
2480331766Sken * @param lun LUN value.
2481331766Sken * @param tmf Task management command.
2482331766Sken * @param cdb Pointer to the SCSI CDB.
2483331766Sken * @param cdb_len Length of the CDB, in bytes.
2484331766Sken * @param dif_info Pointer to the T10 DIF fields, or NULL if no DIF.
2485331766Sken * @param sgl Pointer to the scatter-gather list.
2486331766Sken * @param sgl_count Number of SGL entries in SGL.
2487331766Sken * @param wire_len Payload length, in bytes, of data on wire.
2488331766Sken * @param first_burst Number of first burst bytes to send.
2489331766Sken * @param cb Completion callback.
2490331766Sken * @param arg Application-specified completion callback argument.
2491331766Sken *
2492331766Sken * @return Returns 0 on success, or a negative error code value on failure.
2493331766Sken */
2494331766Sken
2495331766Sken/* tc: could elminiate LUN, as it's part of the IO structure */
2496331766Sken
2497331766Skenstatic int32_t ocs_scsi_send_io(ocs_hw_io_type_e type, ocs_node_t *node, ocs_io_t *io, uint64_t lun,
2498331766Sken	ocs_scsi_tmf_cmd_e tmf, uint8_t *cdb, uint32_t cdb_len,
2499331766Sken	ocs_scsi_dif_info_t *dif_info,
2500331766Sken	ocs_scsi_sgl_t *sgl, uint32_t sgl_count, uint32_t wire_len, uint32_t first_burst,
2501331766Sken	ocs_scsi_rsp_io_cb_t cb, void *arg)
2502331766Sken{
2503331766Sken	int32_t rc;
2504331766Sken	ocs_t *ocs;
2505331766Sken	fcp_cmnd_iu_t *cmnd;
2506331766Sken	uint32_t cmnd_bytes = 0;
2507331766Sken	uint32_t *fcp_dl;
2508331766Sken	uint8_t tmf_flags = 0;
2509331766Sken
2510331766Sken	ocs_assert(io->node, -1);
2511331766Sken	ocs_assert(io->node == node, -1);
2512331766Sken	ocs_assert(io, -1);
2513331766Sken	ocs = io->ocs;
2514331766Sken	ocs_assert(cb, -1);
2515331766Sken
2516331766Sken	io->sgl_count = sgl_count;
2517331766Sken
2518331766Sken	/* Copy SGL if needed */
2519331766Sken	if (sgl != io->sgl) {
2520331766Sken		ocs_assert(sgl_count <= io->sgl_allocated, -1);
2521331766Sken		ocs_memcpy(io->sgl, sgl, sizeof(*io->sgl) * sgl_count);
2522331766Sken	}
2523331766Sken
2524331766Sken	/* save initiator and target task tags for debugging */
2525331766Sken	io->tgt_task_tag = 0xffff;
2526331766Sken
2527331766Sken	io->wire_len = wire_len;
2528331766Sken	io->hio_type = type;
2529331766Sken
2530331766Sken	if (OCS_LOG_ENABLE_SCSI_TRACE(ocs)) {
2531331766Sken		char buf[80];
2532331766Sken		ocs_textbuf_t txtbuf;
2533331766Sken		uint32_t i;
2534331766Sken
2535331766Sken		ocs_textbuf_init(ocs, &txtbuf, buf, sizeof(buf));
2536331766Sken
2537331766Sken		ocs_textbuf_printf(&txtbuf, "cdb%d: ", cdb_len);
2538331766Sken		for (i = 0; i < cdb_len; i ++) {
2539331766Sken			ocs_textbuf_printf(&txtbuf, "%02X%s", cdb[i], (i == (cdb_len-1)) ? "" : " ");
2540331766Sken		}
2541331766Sken		scsi_io_printf(io, "%s len %d, %s\n", (io->hio_type == OCS_HW_IO_INITIATOR_READ) ? "read" :
2542331766Sken			(io->hio_type == OCS_HW_IO_INITIATOR_WRITE) ? "write" : "",  io->wire_len,
2543331766Sken			ocs_textbuf_get_buffer(&txtbuf));
2544331766Sken	}
2545331766Sken
2546331766Sken
2547331766Sken	ocs_assert(io->cmdbuf.virt, -1);
2548331766Sken
2549331766Sken	cmnd = io->cmdbuf.virt;
2550331766Sken
2551331766Sken	ocs_assert(sizeof(*cmnd) <= io->cmdbuf.size, -1);
2552331766Sken
2553331766Sken	ocs_memset(cmnd, 0, sizeof(*cmnd));
2554331766Sken
2555331766Sken	/* Default FCP_CMND IU doesn't include additional CDB bytes but does include FCP_DL */
2556331766Sken	cmnd_bytes = sizeof(fcp_cmnd_iu_t) - sizeof(cmnd->fcp_cdb_and_dl) + sizeof(uint32_t);
2557331766Sken
2558331766Sken	fcp_dl = (uint32_t*)(&(cmnd->fcp_cdb_and_dl));
2559331766Sken
2560331766Sken	if (cdb) {
2561331766Sken		if (cdb_len <= 16) {
2562331766Sken			ocs_memcpy(cmnd->fcp_cdb, cdb, cdb_len);
2563331766Sken		} else {
2564331766Sken			uint32_t addl_cdb_bytes;
2565331766Sken
2566331766Sken			ocs_memcpy(cmnd->fcp_cdb, cdb, 16);
2567331766Sken			addl_cdb_bytes = cdb_len - 16;
2568331766Sken			ocs_memcpy(cmnd->fcp_cdb_and_dl, &(cdb[16]), addl_cdb_bytes);
2569331766Sken			/* additional_fcp_cdb_length is in words, not bytes */
2570331766Sken			cmnd->additional_fcp_cdb_length = (addl_cdb_bytes + 3) / 4;
2571331766Sken			fcp_dl += cmnd->additional_fcp_cdb_length;
2572331766Sken
2573331766Sken			/* Round up additional CDB bytes */
2574331766Sken			cmnd_bytes += (addl_cdb_bytes + 3) & ~0x3;
2575331766Sken		}
2576331766Sken	}
2577331766Sken
2578331766Sken	be64enc(cmnd->fcp_lun, CAM_EXTLUN_BYTE_SWIZZLE(lun));
2579331766Sken
2580331766Sken	if (node->fcp2device) {
2581331766Sken		if(ocs_get_crn(node, &cmnd->command_reference_number,
2582331766Sken					lun)) {
2583331766Sken			return -1;
2584331766Sken		}
2585331766Sken	}
2586331766Sken
2587331766Sken	switch (tmf) {
2588331766Sken	case OCS_SCSI_TMF_QUERY_TASK_SET:
2589331766Sken		tmf_flags = FCP_QUERY_TASK_SET;
2590331766Sken		break;
2591331766Sken	case OCS_SCSI_TMF_ABORT_TASK_SET:
2592331766Sken		tmf_flags = FCP_ABORT_TASK_SET;
2593331766Sken		break;
2594331766Sken	case OCS_SCSI_TMF_CLEAR_TASK_SET:
2595331766Sken		tmf_flags = FCP_CLEAR_TASK_SET;
2596331766Sken		break;
2597331766Sken	case OCS_SCSI_TMF_QUERY_ASYNCHRONOUS_EVENT:
2598331766Sken		tmf_flags = FCP_QUERY_ASYNCHRONOUS_EVENT;
2599331766Sken		break;
2600331766Sken	case OCS_SCSI_TMF_LOGICAL_UNIT_RESET:
2601331766Sken		tmf_flags = FCP_LOGICAL_UNIT_RESET;
2602331766Sken		break;
2603331766Sken	case OCS_SCSI_TMF_CLEAR_ACA:
2604331766Sken		tmf_flags = FCP_CLEAR_ACA;
2605331766Sken		break;
2606331766Sken	case OCS_SCSI_TMF_TARGET_RESET:
2607331766Sken		tmf_flags = FCP_TARGET_RESET;
2608331766Sken		break;
2609331766Sken	default:
2610331766Sken		tmf_flags = 0;
2611331766Sken	}
2612331766Sken	cmnd->task_management_flags = tmf_flags;
2613331766Sken
2614331766Sken	*fcp_dl = ocs_htobe32(io->wire_len);
2615331766Sken
2616331766Sken	switch (io->hio_type) {
2617331766Sken	case OCS_HW_IO_INITIATOR_READ:
2618331766Sken		cmnd->rddata = 1;
2619331766Sken		break;
2620331766Sken	case OCS_HW_IO_INITIATOR_WRITE:
2621331766Sken		cmnd->wrdata = 1;
2622331766Sken		break;
2623331766Sken	case  OCS_HW_IO_INITIATOR_NODATA:
2624331766Sken		/* sets neither */
2625331766Sken		break;
2626331766Sken	default:
2627331766Sken		ocs_log_test(ocs, "bad IO type %d\n", io->hio_type);
2628331766Sken		return -1;
2629331766Sken	}
2630331766Sken
2631331766Sken	rc = ocs_scsi_convert_dif_info(ocs, dif_info, &io->hw_dif);
2632331766Sken	if (rc) {
2633331766Sken		return rc;
2634331766Sken	}
2635331766Sken
2636331766Sken	io->scsi_ini_cb = cb;
2637331766Sken	io->scsi_ini_cb_arg = arg;
2638331766Sken
2639331766Sken	/* set command and response buffers in the iparam */
2640331766Sken	io->iparam.fcp_ini.cmnd = &io->cmdbuf;
2641331766Sken	io->iparam.fcp_ini.cmnd_size = cmnd_bytes;
2642331766Sken	io->iparam.fcp_ini.rsp = &io->rspbuf;
2643331766Sken	io->iparam.fcp_ini.flags = 0;
2644331766Sken	io->iparam.fcp_ini.dif_oper = io->hw_dif.dif;
2645331766Sken	io->iparam.fcp_ini.blk_size = io->hw_dif.blk_size;
2646331766Sken	io->iparam.fcp_ini.timeout = io->timeout;
2647331766Sken	io->iparam.fcp_ini.first_burst = first_burst;
2648331766Sken
2649331766Sken	return ocs_scsi_io_dispatch(io, ocs_initiator_io_cb);
2650331766Sken}
2651331766Sken
2652331766Sken/**
2653331766Sken * @ingroup scsi_api_base
2654331766Sken * @brief Callback for an aborted IO.
2655331766Sken *
2656331766Sken * @par Description
2657331766Sken * Callback function invoked upon completion of an IO abort request.
2658331766Sken *
2659331766Sken * @param hio HW IO context.
2660331766Sken * @param rnode Remote node.
2661331766Sken * @param len Response length.
2662331766Sken * @param status Completion status.
2663331766Sken * @param ext_status Extended completion status.
2664331766Sken * @param arg Application-specific callback, usually IO context.
2665331766Sken
2666331766Sken * @return Returns 0 on success, or a negative error code value on failure.
2667331766Sken */
2668331766Sken
2669331766Skenstatic int32_t
2670331766Skenocs_scsi_abort_io_cb(struct ocs_hw_io_s *hio, ocs_remote_node_t *rnode, uint32_t len, int32_t status,
2671331766Sken	uint32_t ext_status, void *arg)
2672331766Sken{
2673331766Sken	ocs_io_t *io = arg;
2674331766Sken	ocs_t *ocs;
2675331766Sken	ocs_scsi_io_status_e scsi_status = OCS_SCSI_STATUS_GOOD;
2676331766Sken
2677331766Sken	ocs_assert(io, -1);
2678331766Sken	ocs_assert(ocs_io_busy(io), -1);
2679331766Sken	ocs_assert(io->ocs, -1);
2680331766Sken	ocs_assert(io->io_to_abort, -1);
2681331766Sken	ocs = io->ocs;
2682331766Sken
2683331766Sken	ocs_log_debug(ocs, "status %d ext %d\n", status, ext_status);
2684331766Sken
2685331766Sken	/* done with IO to abort */
2686331766Sken	ocs_ref_put(&io->io_to_abort->ref); /* ocs_ref_get(): ocs_scsi_send_tmf() */
2687331766Sken
2688331766Sken	ocs_scsi_io_free_ovfl(io);
2689331766Sken
2690331766Sken	switch (status) {
2691331766Sken	case SLI4_FC_WCQE_STATUS_SUCCESS:
2692331766Sken		scsi_status = OCS_SCSI_STATUS_GOOD;
2693331766Sken		break;
2694331766Sken	case SLI4_FC_WCQE_STATUS_LOCAL_REJECT:
2695331766Sken		if (ext_status == SLI4_FC_LOCAL_REJECT_ABORT_REQUESTED) {
2696331766Sken			scsi_status = OCS_SCSI_STATUS_ABORTED;
2697331766Sken		} else if (ext_status == SLI4_FC_LOCAL_REJECT_NO_XRI) {
2698331766Sken			scsi_status = OCS_SCSI_STATUS_NO_IO;
2699331766Sken		} else if (ext_status == SLI4_FC_LOCAL_REJECT_ABORT_IN_PROGRESS) {
2700331766Sken			scsi_status = OCS_SCSI_STATUS_ABORT_IN_PROGRESS;
2701331766Sken		} else {
2702331766Sken			ocs_log_test(ocs, "Unhandled local reject 0x%x/0x%x\n", status, ext_status);
2703331766Sken			scsi_status = OCS_SCSI_STATUS_ERROR;
2704331766Sken		}
2705331766Sken		break;
2706331766Sken	default:
2707331766Sken		scsi_status = OCS_SCSI_STATUS_ERROR;
2708331766Sken		break;
2709331766Sken	}
2710331766Sken
2711331766Sken	if (io->scsi_ini_cb) {
2712331766Sken		(*io->scsi_ini_cb)(io, scsi_status, NULL, 0, io->scsi_ini_cb_arg);
2713331766Sken	} else {
2714331766Sken		ocs_scsi_io_free(io);
2715331766Sken	}
2716331766Sken
2717331766Sken	ocs_scsi_check_pending(ocs);
2718331766Sken	return 0;
2719331766Sken}
2720331766Sken
2721331766Sken/**
2722331766Sken * @ingroup scsi_api_base
2723331766Sken * @brief Return SCSI API integer valued property.
2724331766Sken *
2725331766Sken * @par Description
2726331766Sken * This function is called by a target-server or initiator-client to
2727331766Sken * retrieve an integer valued property.
2728331766Sken *
2729331766Sken * @param ocs Pointer to the ocs.
2730331766Sken * @param prop Property value to return.
2731331766Sken *
2732331766Sken * @return Returns a value, or 0 if invalid property was requested.
2733331766Sken */
2734331766Skenuint32_t
2735331766Skenocs_scsi_get_property(ocs_t *ocs, ocs_scsi_property_e prop)
2736331766Sken{
2737331766Sken	ocs_xport_t *xport = ocs->xport;
2738331766Sken	uint32_t	val;
2739331766Sken
2740331766Sken	switch (prop) {
2741331766Sken	case OCS_SCSI_MAX_SGE:
2742331766Sken		if (0 == ocs_hw_get(&ocs->hw, OCS_HW_MAX_SGE, &val)) {
2743331766Sken			return val;
2744331766Sken		}
2745331766Sken		break;
2746331766Sken	case OCS_SCSI_MAX_SGL:
2747331766Sken		if (ocs->ctrlmask & OCS_CTRLMASK_TEST_CHAINED_SGLS) {
2748331766Sken			/*
2749331766Sken			 * If chain SGL test-mode is enabled, the number of HW SGEs
2750331766Sken			 * has been limited; report back original max.
2751331766Sken			 */
2752331766Sken			return (OCS_FC_MAX_SGL);
2753331766Sken		}
2754331766Sken		if (0 == ocs_hw_get(&ocs->hw, OCS_HW_N_SGL, &val)) {
2755331766Sken			return val;
2756331766Sken		}
2757331766Sken		break;
2758331766Sken	case OCS_SCSI_MAX_IOS:
2759331766Sken		return ocs_io_pool_allocated(xport->io_pool);
2760331766Sken	case OCS_SCSI_DIF_CAPABLE:
2761331766Sken	        if (0 == ocs_hw_get(&ocs->hw, OCS_HW_DIF_CAPABLE, &val)) {
2762331766Sken	                return val;
2763331766Sken	        }
2764331766Sken		break;
2765331766Sken	case OCS_SCSI_MAX_FIRST_BURST:
2766331766Sken		return 0;
2767331766Sken	case OCS_SCSI_DIF_MULTI_SEPARATE:
2768331766Sken	        if (ocs_hw_get(&ocs->hw, OCS_HW_DIF_MULTI_SEPARATE, &val) == 0) {
2769331766Sken	                return val;
2770331766Sken	        }
2771331766Sken		break;
2772331766Sken	case OCS_SCSI_ENABLE_TASK_SET_FULL:
2773331766Sken		/* Return FALSE if we are send frame capable */
2774331766Sken		if (ocs_hw_get(&ocs->hw, OCS_HW_SEND_FRAME_CAPABLE, &val) == 0) {
2775331766Sken			return ! val;
2776331766Sken		}
2777331766Sken		break;
2778331766Sken	default:
2779331766Sken		break;
2780331766Sken	}
2781331766Sken
2782331766Sken	ocs_log_debug(ocs, "invalid property request %d\n", prop);
2783331766Sken	return 0;
2784331766Sken}
2785331766Sken
2786331766Sken/**
2787331766Sken * @ingroup scsi_api_base
2788331766Sken * @brief Return a property pointer.
2789331766Sken *
2790331766Sken * @par Description
2791331766Sken * This function is called by a target-server or initiator-client to
2792331766Sken * retrieve a pointer to the requested property.
2793331766Sken *
2794331766Sken * @param ocs Pointer to the ocs.
2795331766Sken * @param prop Property value to return.
2796331766Sken *
2797331766Sken * @return Returns pointer to the requested property, or NULL otherwise.
2798331766Sken */
2799331766Skenvoid *ocs_scsi_get_property_ptr(ocs_t *ocs, ocs_scsi_property_e prop)
2800331766Sken{
2801331766Sken	void *rc = NULL;
2802331766Sken
2803331766Sken	switch (prop) {
2804331766Sken	case OCS_SCSI_WWNN:
2805331766Sken		rc = ocs_hw_get_ptr(&ocs->hw, OCS_HW_WWN_NODE);
2806331766Sken		break;
2807331766Sken	case OCS_SCSI_WWPN:
2808331766Sken		rc = ocs_hw_get_ptr(&ocs->hw, OCS_HW_WWN_PORT);
2809331766Sken		break;
2810331766Sken	case OCS_SCSI_PORTNUM:
2811331766Sken		rc = ocs_hw_get_ptr(&ocs->hw, OCS_HW_PORTNUM);
2812331766Sken		break;
2813331766Sken	case OCS_SCSI_BIOS_VERSION_STRING:
2814331766Sken		rc = ocs_hw_get_ptr(&ocs->hw, OCS_HW_BIOS_VERSION_STRING);
2815331766Sken		break;
2816331766Sken#if defined(OCS_ENABLE_VPD_SUPPORT)
2817331766Sken	case OCS_SCSI_SERIALNUMBER:
2818331766Sken	{
2819331766Sken		uint8_t *pvpd;
2820331766Sken		uint32_t vpd_len;
2821331766Sken
2822331766Sken		if (ocs_hw_get(&ocs->hw, OCS_HW_VPD_LEN, &vpd_len)) {
2823331766Sken			ocs_log_test(ocs, "Can't get VPD length\n");
2824331766Sken			rc = "\012sn-unknown";
2825331766Sken			break;
2826331766Sken		}
2827331766Sken
2828331766Sken		pvpd = ocs_hw_get_ptr(&ocs->hw, OCS_HW_VPD);
2829331766Sken		if (pvpd) {
2830331766Sken			rc = ocs_find_vpd(pvpd, vpd_len, "SN");
2831331766Sken		}
2832331766Sken
2833331766Sken		if (rc == NULL ||
2834331766Sken		    ocs_strlen(rc) == 0) {
2835331766Sken			/* Note: VPD is missing, using wwnn for serial number */
2836331766Sken			scsi_log(ocs, "Note: VPD is missing, using wwnn for serial number\n");
2837331766Sken			/* Use the last 32 bits of the WWN */
2838331766Sken			if ((ocs == NULL) || (ocs->domain == NULL) || (ocs->domain->sport == NULL)) {
2839331766Sken				rc = "\011(Unknown)";
2840331766Sken			} else {
2841331766Sken				rc = &ocs->domain->sport->wwnn_str[8];
2842331766Sken			}
2843331766Sken		}
2844331766Sken		break;
2845331766Sken	}
2846331766Sken	case OCS_SCSI_PARTNUMBER:
2847331766Sken	{
2848331766Sken		uint8_t *pvpd;
2849331766Sken		uint32_t vpd_len;
2850331766Sken
2851331766Sken		if (ocs_hw_get(&ocs->hw, OCS_HW_VPD_LEN, &vpd_len)) {
2852331766Sken			ocs_log_test(ocs, "Can't get VPD length\n");
2853331766Sken			rc = "\012pn-unknown";
2854331766Sken			break;
2855331766Sken		}
2856331766Sken		pvpd = ocs_hw_get_ptr(&ocs->hw, OCS_HW_VPD);
2857331766Sken		if (pvpd) {
2858331766Sken			rc = ocs_find_vpd(pvpd, vpd_len, "PN");
2859331766Sken			if (rc == NULL) {
2860331766Sken				rc = "\012pn-unknown";
2861331766Sken			}
2862331766Sken		} else {
2863331766Sken			rc = "\012pn-unknown";
2864331766Sken		}
2865331766Sken		break;
2866331766Sken	}
2867331766Sken#endif
2868331766Sken	default:
2869331766Sken		break;
2870331766Sken	}
2871331766Sken
2872331766Sken	if (rc == NULL) {
2873331766Sken		ocs_log_debug(ocs, "invalid property request %d\n", prop);
2874331766Sken	}
2875331766Sken	return rc;
2876331766Sken}
2877331766Sken
2878331766Sken/**
2879331766Sken * @ingroup scsi_api_base
2880331766Sken * @brief Notify that delete initiator is complete.
2881331766Sken *
2882331766Sken * @par Description
2883331766Sken * Sent by the target-server to notify the base driver that the work started from
2884331766Sken * ocs_scsi_del_initiator() is now complete and that it is safe for the node to
2885331766Sken * release the rest of its resources.
2886331766Sken *
2887331766Sken * @param node Pointer to the node.
2888331766Sken *
2889331766Sken * @return None.
2890331766Sken */
2891331766Skenvoid
2892331766Skenocs_scsi_del_initiator_complete(ocs_node_t *node)
2893331766Sken{
2894331766Sken	/* Notify the node to resume */
2895331766Sken	ocs_node_post_event(node, OCS_EVT_NODE_DEL_INI_COMPLETE, NULL);
2896331766Sken}
2897331766Sken
2898331766Sken
2899331766Sken/**
2900331766Sken * @ingroup scsi_api_base
2901331766Sken * @brief Notify that delete target is complete.
2902331766Sken *
2903331766Sken * @par Description
2904331766Sken * Sent by the initiator-client to notify the base driver that the work started from
2905331766Sken * ocs_scsi_del_target() is now complete and that it is safe for the node to
2906331766Sken * release the rest of its resources.
2907331766Sken *
2908331766Sken * @param node Pointer to the node.
2909331766Sken *
2910331766Sken * @return None.
2911331766Sken */
2912331766Skenvoid
2913331766Skenocs_scsi_del_target_complete(ocs_node_t *node)
2914331766Sken{
2915331766Sken	/* Notify the node to resume */
2916331766Sken	ocs_node_post_event(node, OCS_EVT_NODE_DEL_TGT_COMPLETE, NULL);
2917331766Sken}
2918331766Sken
2919331766Sken
2920331766Sken/**
2921331766Sken * @brief Update transferred count
2922331766Sken *
2923331766Sken * @par Description
2924331766Sken * Updates io->transferred, as required when using first burst, when the amount
2925331766Sken * of first burst data processed differs from the amount of first burst
2926331766Sken * data received.
2927331766Sken *
2928331766Sken * @param io Pointer to the io object.
2929331766Sken * @param transferred Number of bytes transferred out of first burst buffers.
2930331766Sken *
2931331766Sken * @return None.
2932331766Sken */
2933331766Skenvoid
2934331766Skenocs_scsi_update_first_burst_transferred(ocs_io_t *io, uint32_t transferred)
2935331766Sken{
2936331766Sken	io->transferred = transferred;
2937331766Sken}
2938331766Sken
2939331766Sken/**
2940331766Sken * @brief Register bounce callback for multi-threading.
2941331766Sken *
2942331766Sken * @par Description
2943331766Sken * Register the back end bounce function.
2944331766Sken *
2945331766Sken * @param ocs Pointer to device object.
2946331766Sken * @param fctn Function pointer of bounce function.
2947331766Sken *
2948331766Sken * @return None.
2949331766Sken */
2950331766Skenvoid
2951331766Skenocs_scsi_register_bounce(ocs_t *ocs, void(*fctn)(void(*fctn)(void *arg), void *arg, uint32_t s_id, uint32_t d_id,
2952331766Sken						 uint32_t ox_id))
2953331766Sken{
2954331766Sken	ocs_hw_rtn_e rc;
2955331766Sken
2956331766Sken	rc = ocs_hw_callback(&ocs->hw, OCS_HW_CB_BOUNCE, fctn, NULL);
2957331766Sken	if (rc) {
2958331766Sken		ocs_log_test(ocs, "ocs_hw_callback(OCS_HW_CB_BOUNCE) failed: %d\n", rc);
2959331766Sken	}
2960331766Sken}
2961