Changeset View
Standalone View
sys/dev/ocs_fc/ocs_cam.c
- This file was added.
Property | Old Value | New Value |
---|---|---|
svn:eol-style | null | native \ No newline at end of property |
svn:keywords | null | FreeBSD=%H \ No newline at end of property |
svn:mime-type | null | text/plain \ No newline at end of property |
/*- | |||||
* Copyright (c) 2017 Broadcom. All rights reserved. | |||||
* The term "Broadcom" refers to Broadcom Limited and/or its subsidiaries. | |||||
* | |||||
* Redistribution and use in source and binary forms, with or without | |||||
* modification, are permitted provided that the following conditions are met: | |||||
* | |||||
* 1. Redistributions of source code must retain the above copyright notice, | |||||
* this list of conditions and the following disclaimer. | |||||
* | |||||
* 2. Redistributions in binary form must reproduce the above copyright notice, | |||||
* this list of conditions and the following disclaimer in the documentation | |||||
* and/or other materials provided with the distribution. | |||||
* | |||||
* 3. Neither the name of the copyright holder nor the names of its contributors | |||||
* may be used to endorse or promote products derived from this software | |||||
* without specific prior written permission. | |||||
* | |||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE | |||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |||||
* POSSIBILITY OF SUCH DAMAGE. | |||||
* | |||||
*/ | |||||
/** | |||||
* @defgroup scsi_api_target SCSI Target API | |||||
* @defgroup scsi_api_initiator SCSI Initiator API | |||||
* @defgroup cam_api Common Access Method (CAM) API | |||||
* @defgroup cam_io CAM IO | |||||
*/ | |||||
/** | |||||
* @file | |||||
* Provides CAM functionality. | |||||
*/ | |||||
#include "ocs.h" | |||||
#include "ocs_scsi.h" | |||||
#include "ocs_device.h" | |||||
/* Default IO timeout value for initiators is 30 seconds */ | |||||
#define OCS_CAM_IO_TIMEOUT 30 | |||||
typedef struct { | |||||
ocs_scsi_sgl_t *sgl; | |||||
uint32_t sgl_max; | |||||
uint32_t sgl_count; | |||||
int32_t rc; | |||||
} ocs_dmamap_load_arg_t; | |||||
static void ocs_action(struct cam_sim *, union ccb *); | |||||
static void ocs_poll(struct cam_sim *); | |||||
static ocs_tgt_resource_t *ocs_tgt_resource_get(struct ocs_softc *, | |||||
struct ccb_hdr *, uint32_t *); | |||||
static int32_t ocs_tgt_resource_abort(struct ocs_softc *, ocs_tgt_resource_t *); | |||||
static uint32_t ocs_abort_initiator_io(struct ocs_softc *ocs, union ccb *accb); | |||||
static void ocs_abort_inot(struct ocs_softc *ocs, union ccb *ccb); | |||||
static void ocs_abort_atio(struct ocs_softc *ocs, union ccb *ccb); | |||||
static int32_t ocs_target_tmf_cb(ocs_io_t *, ocs_scsi_io_status_e, uint32_t, void *); | |||||
static int32_t ocs_io_abort_cb(ocs_io_t *, ocs_scsi_io_status_e, uint32_t, void *); | |||||
static int32_t ocs_task_set_full_or_busy(ocs_io_t *io); | |||||
static int32_t ocs_initiator_tmf_cb(ocs_io_t *, ocs_scsi_io_status_e, | |||||
ocs_scsi_cmd_resp_t *, uint32_t, void *); | |||||
static inline ocs_io_t *ocs_scsi_find_io(struct ocs_softc *ocs, uint32_t tag) | |||||
{ | |||||
return ocs_io_get_instance(ocs, tag); | |||||
} | |||||
static inline void ocs_target_io_free(ocs_io_t *io) | |||||
{ | |||||
io->tgt_io.state = OCS_CAM_IO_FREE; | |||||
io->tgt_io.flags = 0; | |||||
io->tgt_io.app = NULL; | |||||
ocs_scsi_io_complete(io); | |||||
if(io->ocs->io_in_use != 0) | |||||
atomic_subtract_acq_32(&io->ocs->io_in_use, 1); | |||||
} | |||||
static int32_t | |||||
ocs_cam_attach(ocs_t *ocs) | |||||
{ | |||||
struct cam_devq *devq = NULL; | |||||
struct cam_sim *sim = NULL; | |||||
struct cam_path *path = NULL; | |||||
uint32_t i = 0; | |||||
uint32_t max_io = ocs_scsi_get_property(ocs, OCS_SCSI_MAX_IOS); | |||||
if (ocs->sim != NULL) { | |||||
/* Already initialized */ | |||||
return 0; | |||||
} | |||||
if (NULL == (devq = cam_simq_alloc(max_io))) { | |||||
device_printf(ocs->dev, "Can't allocate SIMQ\n"); | |||||
return -1; | |||||
} | |||||
if (mtx_initialized(&ocs->sim_lock) == 0) { | |||||
mtx_init(&ocs->sim_lock, "ocs_sim_lock", NULL, MTX_DEF); | |||||
} | |||||
if (NULL == (sim = cam_sim_alloc(ocs_action, ocs_poll, | |||||
device_get_name(ocs->dev), ocs, | |||||
device_get_unit(ocs->dev), &ocs->sim_lock, | |||||
max_io, max_io, devq))) { | |||||
cam_simq_free(devq); | |||||
device_printf(ocs->dev, "Can't allocate SIM\n"); | |||||
return -1; | |||||
} | |||||
ocs->targ_rsrc_wildcard.enabled = FALSE; | |||||
STAILQ_INIT(&ocs->targ_rsrc_wildcard.atio); | |||||
STAILQ_INIT(&ocs->targ_rsrc_wildcard.inot); | |||||
for (i = 0; i < OCS_MAX_LUN; i++) { | |||||
ocs->targ_rsrc[i].enabled = FALSE; | |||||
STAILQ_INIT(&ocs->targ_rsrc[i].atio); | |||||
STAILQ_INIT(&ocs->targ_rsrc[i].inot); | |||||
} | |||||
mtx_lock(&ocs->sim_lock); | |||||
if (CAM_SUCCESS != xpt_bus_register(sim, ocs->dev, 0)) { | |||||
device_printf(ocs->dev, "Can't register bus %d\n", 0); | |||||
mtx_unlock(&ocs->sim_lock); | |||||
cam_sim_free(sim, TRUE); | |||||
return -1; | |||||
} | |||||
if (CAM_REQ_CMP != xpt_create_path(&path, NULL, cam_sim_path(sim), | |||||
CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD)) { | |||||
device_printf(ocs->dev, "Can't create path\n"); | |||||
xpt_bus_deregister(cam_sim_path(sim)); | |||||
mtx_unlock(&ocs->sim_lock); | |||||
cam_sim_free(sim, TRUE); | |||||
return -1; | |||||
} | |||||
mtx_unlock(&ocs->sim_lock); | |||||
ocs->sim = sim; | |||||
ocs->path = path; | |||||
ocs->io_high_watermark = max_io; | |||||
ocs->io_in_use = 0; | |||||
return 0; | |||||
} | |||||
static int32_t | |||||
ocs_cam_detach(ocs_t *ocs) | |||||
{ | |||||
if (ocs->sim) { | |||||
mtx_lock(&ocs->sim_lock); | |||||
ocs_tgt_resource_abort(ocs, &ocs->targ_rsrc_wildcard); | |||||
if (ocs->path) { | |||||
xpt_async(AC_LOST_DEVICE, ocs->path, NULL); | |||||
xpt_free_path(ocs->path); | |||||
ocs->path = NULL; | |||||
} | |||||
xpt_bus_deregister(cam_sim_path(ocs->sim)); | |||||
cam_sim_free(ocs->sim, TRUE); | |||||
ocs->sim = NULL; | |||||
mtx_unlock(&ocs->sim_lock); | |||||
} | |||||
if (mtx_initialized(&ocs->sim_lock)) | |||||
mtx_destroy(&ocs->sim_lock); | |||||
return 0; | |||||
} | |||||
/*************************************************************************** | |||||
* Functions required by SCSI base driver API | |||||
*/ | |||||
/** | |||||
* @ingroup scsi_api_target | |||||
* @brief Attach driver to the BSD SCSI layer (a.k.a CAM) | |||||
* | |||||
* Allocates + initializes CAM related resources and attaches to the CAM | |||||
* | |||||
* @param ocs the driver instance's software context | |||||
* | |||||
* @return 0 on success, non-zero otherwise | |||||
*/ | |||||
int32_t | |||||
ocs_scsi_tgt_new_device(ocs_t *ocs) | |||||
{ | |||||
ocs->enable_task_set_full = ocs_scsi_get_property(ocs, | |||||
OCS_SCSI_ENABLE_TASK_SET_FULL); | |||||
ocs_log_debug(ocs, "task set full processing is %s\n", | |||||
ocs->enable_task_set_full ? "enabled" : "disabled"); | |||||
return ocs_cam_attach(ocs); | |||||
} | |||||
/** | |||||
* @ingroup scsi_api_target | |||||
* @brief Tears down target members of ocs structure. | |||||
* | |||||
* Called by OS code when device is removed. | |||||
* | |||||
* @param ocs pointer to ocs | |||||
* | |||||
* @return returns 0 for success, a negative error code value for failure. | |||||
*/ | |||||
int32_t | |||||
ocs_scsi_tgt_del_device(ocs_t *ocs) | |||||
{ | |||||
return ocs_cam_detach(ocs); | |||||
} | |||||
/** | |||||
* @ingroup scsi_api_target | |||||
* @brief accept new domain notification | |||||
* | |||||
* Called by base drive when new domain is discovered. A target-server | |||||
* will use this call to prepare for new remote node notifications | |||||
* arising from ocs_scsi_new_initiator(). | |||||
* | |||||
* The domain context has an element <b>ocs_scsi_tgt_domain_t tgt_domain</b> | |||||
* which is declared by the target-server code and is used for target-server | |||||
* private data. | |||||
* | |||||
* This function will only be called if the base-driver has been enabled for | |||||
* target capability. | |||||
* | |||||
* Note that this call is made to target-server backends, | |||||
* the ocs_scsi_ini_new_domain() function is called to initiator-client backends. | |||||
* | |||||
* @param domain pointer to domain | |||||
* | |||||
* @return returns 0 for success, a negative error code value for failure. | |||||
*/ | |||||
int32_t | |||||
ocs_scsi_tgt_new_domain(ocs_domain_t *domain) | |||||
{ | |||||
return 0; | |||||
} | |||||
/** | |||||
* @ingroup scsi_api_target | |||||
* @brief accept domain lost notification | |||||
* | |||||
* Called by base-driver when a domain goes away. A target-server will | |||||
* use this call to clean up all domain scoped resources. | |||||
* | |||||
* Note that this call is made to target-server backends, | |||||
* the ocs_scsi_ini_del_domain() function is called to initiator-client backends. | |||||
* | |||||
* @param domain pointer to domain | |||||
* | |||||
* @return returns 0 for success, a negative error code value for failure. | |||||
*/ | |||||
void | |||||
ocs_scsi_tgt_del_domain(ocs_domain_t *domain) | |||||
{ | |||||
} | |||||
/** | |||||
* @ingroup scsi_api_target | |||||
* @brief accept new sli port (sport) notification | |||||
* | |||||
* Called by base drive when new sport is discovered. A target-server | |||||
* will use this call to prepare for new remote node notifications | |||||
* arising from ocs_scsi_new_initiator(). | |||||
* | |||||
* The domain context has an element <b>ocs_scsi_tgt_sport_t tgt_sport</b> | |||||
* which is declared by the target-server code and is used for | |||||
* target-server private data. | |||||
* | |||||
* This function will only be called if the base-driver has been enabled for | |||||
* target capability. | |||||
* | |||||
* Note that this call is made to target-server backends, | |||||
* the ocs_scsi_tgt_new_domain() is called to initiator-client backends. | |||||
* | |||||
* @param sport pointer to SLI port | |||||
* | |||||
* @return returns 0 for success, a negative error code value for failure. | |||||
*/ | |||||
int32_t | |||||
ocs_scsi_tgt_new_sport(ocs_sport_t *sport) | |||||
{ | |||||
return 0; | |||||
} | |||||
/** | |||||
* @ingroup scsi_api_target | |||||
* @brief accept SLI port gone notification | |||||
* | |||||
* Called by base-driver when a sport goes away. A target-server will | |||||
* use this call to clean up all sport scoped resources. | |||||
* | |||||
* Note that this call is made to target-server backends, | |||||
* the ocs_scsi_ini_del_sport() is called to initiator-client backends. | |||||
* | |||||
* @param sport pointer to SLI port | |||||
* | |||||
* @return returns 0 for success, a negative error code value for failure. | |||||
*/ | |||||
void | |||||
ocs_scsi_tgt_del_sport(ocs_sport_t *sport) | |||||
{ | |||||
} | |||||
/** | |||||
* @ingroup scsi_api_target | |||||
* @brief receive notification of a new SCSI initiator node | |||||
* | |||||
* Sent by base driver to notify a target-server of the presense of a new | |||||
* remote initiator. The target-server may use this call to prepare for | |||||
* inbound IO from this node. | |||||
* | |||||
* The ocs_node_t structure has and elment of type ocs_scsi_tgt_node_t named | |||||
* tgt_node that is declared and used by a target-server for private | |||||
* information. | |||||
* | |||||
* This function is only called if the target capability is enabled in driver. | |||||
* | |||||
* @param node pointer to new remote initiator node | |||||
* | |||||
* @return returns 0 for success, a negative error code value for failure. | |||||
* | |||||
* @note | |||||
*/ | |||||
int32_t | |||||
ocs_scsi_new_initiator(ocs_node_t *node) | |||||
{ | |||||
ocs_t *ocs = node->ocs; | |||||
struct ac_contract ac; | |||||
struct ac_device_changed *adc; | |||||
/* | |||||
* Update the IO watermark by decrementing it by the | |||||
* number of IOs reserved for each initiator. | |||||
*/ | |||||
atomic_subtract_acq_32(&ocs->io_high_watermark, OCS_RSVD_INI_IO); | |||||
ac.contract_number = AC_CONTRACT_DEV_CHG; | |||||
adc = (struct ac_device_changed *) ac.contract_data; | |||||
adc->wwpn = ocs_node_get_wwpn(node); | |||||
adc->port = node->rnode.fc_id; | |||||
adc->target = node->instance_index; | |||||
adc->arrived = 1; | |||||
xpt_async(AC_CONTRACT, ocs->path, &ac); | |||||
return 0; | |||||
} | |||||
/** | |||||
* @ingroup scsi_api_target | |||||
* @brief validate new initiator | |||||
* | |||||
* Sent by base driver to validate a remote initiatiator. The target-server | |||||
* returns TRUE if this initiator should be accepted. | |||||
* | |||||
* This function is only called if the target capability is enabled in driver. | |||||
* | |||||
* @param node pointer to remote initiator node to validate | |||||
* | |||||
* @return TRUE if initiator should be accepted, FALSE if it should be rejected | |||||
* | |||||
* @note | |||||
*/ | |||||
int32_t | |||||
ocs_scsi_validate_initiator(ocs_node_t *node) | |||||
{ | |||||
return 1; | |||||
} | |||||
/** | |||||
* @ingroup scsi_api_target | |||||
* @brief Delete a SCSI initiator node | |||||
* | |||||
* Sent by base driver to notify a target-server that a remote initiator | |||||
* is now gone. The base driver will have terminated all outstanding IOs | |||||
* and the target-server will receive appropriate completions. | |||||
* | |||||
* This function is only called if the base driver is enabled for | |||||
* target capability. | |||||
* | |||||
* @param node pointer node being deleted | |||||
* @param reason Reason why initiator is gone. | |||||
* | |||||
* @return OCS_SCSI_CALL_COMPLETE to indicate that all work was completed | |||||
* | |||||
* @note | |||||
*/ | |||||
int32_t | |||||
ocs_scsi_del_initiator(ocs_node_t *node, ocs_scsi_del_initiator_reason_e reason) | |||||
{ | |||||
ocs_t *ocs = node->ocs; | |||||
struct ac_contract ac; | |||||
struct ac_device_changed *adc; | |||||
ac.contract_number = AC_CONTRACT_DEV_CHG; | |||||
adc = (struct ac_device_changed *) ac.contract_data; | |||||
mav: I suppose here you should notify CAM about initiator gone via xpt_async(AC_CONTRACT), same as… | |||||
adc->wwpn = ocs_node_get_wwpn(node); | |||||
adc->port = node->rnode.fc_id; | |||||
adc->target = node->instance_index; | |||||
adc->arrived = 0; | |||||
xpt_async(AC_CONTRACT, ocs->path, &ac); | |||||
if (reason == OCS_SCSI_INITIATOR_MISSING) { | |||||
return OCS_SCSI_CALL_COMPLETE; | |||||
} | |||||
/* | |||||
* Update the IO watermark by incrementing it by the | |||||
* number of IOs reserved for each initiator. | |||||
*/ | |||||
atomic_add_acq_32(&ocs->io_high_watermark, OCS_RSVD_INI_IO); | |||||
return OCS_SCSI_CALL_COMPLETE; | |||||
} | |||||
/** | |||||
* @ingroup scsi_api_target | |||||
* @brief receive FCP SCSI Command | |||||
* | |||||
* Called by the base driver when a new SCSI command has been received. The | |||||
* target-server will process the command, and issue data and/or response phase | |||||
* requests to the base driver. | |||||
* | |||||
* The IO context (ocs_io_t) structure has and element of type | |||||
* ocs_scsi_tgt_io_t named tgt_io that is declared and used by | |||||
* a target-server for private information. | |||||
* | |||||
* @param io pointer to IO context | |||||
* @param lun LUN for this IO | |||||
* @param cdb pointer to SCSI CDB | |||||
* @param cdb_len length of CDB in bytes | |||||
* @param flags command flags | |||||
* | |||||
* @return returns 0 for success, a negative error code value for failure. | |||||
*/ | |||||
int32_t ocs_scsi_recv_cmd(ocs_io_t *io, uint64_t lun, uint8_t *cdb, | |||||
uint32_t cdb_len, uint32_t flags) | |||||
{ | |||||
ocs_t *ocs = io->ocs; | |||||
struct ccb_accept_tio *atio = NULL; | |||||
ocs_node_t *node = io->node; | |||||
ocs_tgt_resource_t *trsrc = NULL; | |||||
int32_t rc = -1; | |||||
atomic_add_acq_32(&ocs->io_in_use, 1); | |||||
/* set target io timeout */ | |||||
io->timeout = ocs->target_io_timer_sec; | |||||
if (ocs->enable_task_set_full && | |||||
(ocs->io_in_use >= ocs->io_high_watermark)) { | |||||
return ocs_task_set_full_or_busy(io); | |||||
} else { | |||||
atomic_store_rel_32(&io->node->tgt_node.busy_sent, FALSE); | |||||
} | |||||
if ((lun < OCS_MAX_LUN) && ocs->targ_rsrc[lun].enabled) { | |||||
trsrc = &ocs->targ_rsrc[lun]; | |||||
} else if (ocs->targ_rsrc_wildcard.enabled) { | |||||
trsrc = &ocs->targ_rsrc_wildcard; | |||||
} | |||||
if (trsrc) { | |||||
atio = (struct ccb_accept_tio *)STAILQ_FIRST(&trsrc->atio); | |||||
} | |||||
if (atio) { | |||||
STAILQ_REMOVE_HEAD(&trsrc->atio, sim_links.stqe); | |||||
atio->ccb_h.status = CAM_CDB_RECVD; | |||||
atio->ccb_h.target_lun = lun; | |||||
atio->sense_len = 0; | |||||
atio->init_id = node->instance_index; | |||||
atio->tag_id = io->tag; | |||||
atio->ccb_h.ccb_io_ptr = io; | |||||
if (flags & OCS_SCSI_CMD_SIMPLE) | |||||
atio->tag_action = MSG_SIMPLE_Q_TAG; | |||||
else if (flags & FCP_TASK_ATTR_HEAD_OF_QUEUE) | |||||
atio->tag_action = MSG_HEAD_OF_Q_TAG; | |||||
else if (flags & FCP_TASK_ATTR_ORDERED) | |||||
atio->tag_action = MSG_ORDERED_Q_TAG; | |||||
else | |||||
atio->tag_action = 0; | |||||
atio->cdb_len = cdb_len; | |||||
ocs_memcpy(atio->cdb_io.cdb_bytes, cdb, cdb_len); | |||||
io->tgt_io.flags = 0; | |||||
io->tgt_io.state = OCS_CAM_IO_COMMAND; | |||||
xpt_done((union ccb *)atio); | |||||
rc = 0; | |||||
} else { | |||||
device_printf( | |||||
ocs->dev, "%s: no ATIO for LUN %lx (en=%s) OX_ID %#x\n", | |||||
__func__, lun, | |||||
trsrc ? (trsrc->enabled ? "T" : "F") : "X", | |||||
be16toh(io->init_task_tag)); | |||||
io->tgt_io.state = OCS_CAM_IO_MAX; | |||||
ocs_target_io_free(io); | |||||
} | |||||
return rc; | |||||
} | |||||
/** | |||||
* @ingroup scsi_api_target | |||||
* @brief receive FCP SCSI Command with first burst data. | |||||
* | |||||
* Receive a new FCP SCSI command from the base driver with first burst data. | |||||
* | |||||
* @param io pointer to IO context | |||||
* @param lun LUN for this IO | |||||
* @param cdb pointer to SCSI CDB | |||||
* @param cdb_len length of CDB in bytes | |||||
* @param flags command flags | |||||
* @param first_burst_buffers first burst buffers | |||||
* @param first_burst_buffer_count The number of bytes received in the first burst | |||||
* | |||||
* @return returns 0 for success, a negative error code value for failure. | |||||
*/ | |||||
int32_t ocs_scsi_recv_cmd_first_burst(ocs_io_t *io, uint64_t lun, uint8_t *cdb, | |||||
uint32_t cdb_len, uint32_t flags, | |||||
ocs_dma_t first_burst_buffers[], | |||||
uint32_t first_burst_buffer_count) | |||||
{ | |||||
return -1; | |||||
} | |||||
/** | |||||
* @ingroup scsi_api_target | |||||
* @brief receive a TMF command IO | |||||
* | |||||
* Called by the base driver when a SCSI TMF command has been received. The | |||||
* target-server will process the command, aborting commands as needed, and post | |||||
* a response using ocs_scsi_send_resp() | |||||
* | |||||
* The IO context (ocs_io_t) structure has and element of type ocs_scsi_tgt_io_t named | |||||
* tgt_io that is declared and used by a target-server for private information. | |||||
* | |||||
* If the target-server walks the nodes active_ios linked list, and starts IO | |||||
* abort processing, the code <b>must</b> be sure not to abort the IO passed into the | |||||
* ocs_scsi_recv_tmf() command. | |||||
* | |||||
* @param tmfio pointer to IO context | |||||
* @param lun logical unit value | |||||
* @param cmd command request | |||||
* @param abortio pointer to IO object to abort for TASK_ABORT (NULL for all other TMF) | |||||
* @param flags flags | |||||
* | |||||
* @return returns 0 for success, a negative error code value for failure. | |||||
*/ | |||||
int32_t ocs_scsi_recv_tmf(ocs_io_t *tmfio, uint64_t lun, ocs_scsi_tmf_cmd_e cmd, | |||||
ocs_io_t *abortio, uint32_t flags) | |||||
{ | |||||
ocs_t *ocs = tmfio->ocs; | |||||
ocs_node_t *node = tmfio->node; | |||||
ocs_tgt_resource_t *trsrc = NULL; | |||||
struct ccb_immediate_notify *inot = NULL; | |||||
int32_t rc = -1; | |||||
if ((lun < OCS_MAX_LUN) && ocs->targ_rsrc[lun].enabled) { | |||||
Done Inline ActionsIn case of request handled by wildcard LUN it would be good to set inot->ccb_h.target_id/target_lun to real values to let CAM/CTL know what is the real target. CTL restore original wildcard values when resubmit INOT back. mav: In case of request handled by wildcard LUN it would be good to set inot->ccb_h. | |||||
trsrc = &ocs->targ_rsrc[lun]; | |||||
} else if (ocs->targ_rsrc_wildcard.enabled) { | |||||
trsrc = &ocs->targ_rsrc_wildcard; | |||||
} | |||||
device_printf(tmfio->ocs->dev, "%s: io=%p cmd=%#x LU=%lx en=%s\n", | |||||
__func__, tmfio, cmd, lun, | |||||
trsrc ? (trsrc->enabled ? "T" : "F") : "X"); | |||||
if (trsrc) { | |||||
inot = (struct ccb_immediate_notify *)STAILQ_FIRST(&trsrc->inot); | |||||
} | |||||
if (!inot) { | |||||
device_printf( | |||||
ocs->dev, "%s: no INOT for LUN %lx (en=%s) OX_ID %#x\n", | |||||
__func__, lun, trsrc ? (trsrc->enabled ? "T" : "F") : "X", | |||||
be16toh(tmfio->init_task_tag)); | |||||
if (abortio) { | |||||
ocs_scsi_io_complete(abortio); | |||||
} | |||||
ocs_scsi_io_complete(tmfio); | |||||
goto ocs_scsi_recv_tmf_out; | |||||
} | |||||
Done Inline Actionsinot->arg seems not set here. Modern FreeBSD supports it too. mav: inot->arg seems not set here. Modern FreeBSD supports it too. | |||||
tmfio->tgt_io.app = abortio; | |||||
STAILQ_REMOVE_HEAD(&trsrc->inot, sim_links.stqe); | |||||
inot->tag_id = tmfio->tag; | |||||
inot->seq_id = tmfio->tag; | |||||
if ((lun < OCS_MAX_LUN) && ocs->targ_rsrc[lun].enabled) { | |||||
inot->initiator_id = node->instance_index; | |||||
} else { | |||||
inot->initiator_id = CAM_TARGET_WILDCARD; | |||||
} | |||||
inot->ccb_h.status = CAM_MESSAGE_RECV; | |||||
inot->ccb_h.target_lun = lun; | |||||
switch (cmd) { | |||||
case OCS_SCSI_TMF_ABORT_TASK: | |||||
inot->arg = MSG_ABORT_TASK; | |||||
inot->seq_id = abortio->tag; | |||||
device_printf(ocs->dev, "%s: ABTS IO.%#x st=%#x\n", | |||||
__func__, abortio->tag, abortio->tgt_io.state); | |||||
abortio->tgt_io.flags |= OCS_CAM_IO_F_ABORT_RECV; | |||||
abortio->tgt_io.flags |= OCS_CAM_IO_F_ABORT_NOTIFY; | |||||
break; | |||||
case OCS_SCSI_TMF_QUERY_TASK_SET: | |||||
device_printf(ocs->dev, | |||||
"%s: OCS_SCSI_TMF_QUERY_TASK_SET not supported\n", | |||||
__func__); | |||||
STAILQ_INSERT_TAIL(&trsrc->inot, &inot->ccb_h, sim_links.stqe); | |||||
ocs_scsi_io_complete(tmfio); | |||||
goto ocs_scsi_recv_tmf_out; | |||||
break; | |||||
case OCS_SCSI_TMF_ABORT_TASK_SET: | |||||
inot->arg = MSG_ABORT_TASK_SET; | |||||
break; | |||||
case OCS_SCSI_TMF_CLEAR_TASK_SET: | |||||
inot->arg = MSG_CLEAR_TASK_SET; | |||||
break; | |||||
case OCS_SCSI_TMF_QUERY_ASYNCHRONOUS_EVENT: | |||||
inot->arg = MSG_QUERY_ASYNC_EVENT; | |||||
break; | |||||
case OCS_SCSI_TMF_LOGICAL_UNIT_RESET: | |||||
inot->arg = MSG_LOGICAL_UNIT_RESET; | |||||
break; | |||||
case OCS_SCSI_TMF_CLEAR_ACA: | |||||
inot->arg = MSG_CLEAR_ACA; | |||||
break; | |||||
case OCS_SCSI_TMF_TARGET_RESET: | |||||
inot->arg = MSG_TARGET_RESET; | |||||
break; | |||||
default: | |||||
device_printf(ocs->dev, "%s: unsupported TMF %#x\n", | |||||
__func__, cmd); | |||||
STAILQ_INSERT_TAIL(&trsrc->inot, &inot->ccb_h, sim_links.stqe); | |||||
goto ocs_scsi_recv_tmf_out; | |||||
} | |||||
rc = 0; | |||||
xpt_print(inot->ccb_h.path, "%s: func=%#x stat=%#x id=%#x lun=%#x" | |||||
" flags=%#x tag=%#x seq=%#x ini=%#x arg=%#x\n", | |||||
__func__, inot->ccb_h.func_code, inot->ccb_h.status, | |||||
inot->ccb_h.target_id, | |||||
(unsigned int)inot->ccb_h.target_lun, inot->ccb_h.flags, | |||||
inot->tag_id, inot->seq_id, inot->initiator_id, | |||||
inot->arg); | |||||
xpt_done((union ccb *)inot); | |||||
if (abortio) { | |||||
abortio->tgt_io.flags |= OCS_CAM_IO_F_ABORT_DEV; | |||||
rc = ocs_scsi_tgt_abort_io(abortio, ocs_io_abort_cb, tmfio); | |||||
} | |||||
ocs_scsi_recv_tmf_out: | |||||
return rc; | |||||
} | |||||
/** | |||||
* @ingroup scsi_api_initiator | |||||
* @brief Initializes any initiator fields on the ocs structure. | |||||
* | |||||
* Called by OS initialization code when a new device is discovered. | |||||
* | |||||
* @param ocs pointer to ocs | |||||
* | |||||
* @return returns 0 for success, a negative error code value for failure. | |||||
*/ | |||||
int32_t | |||||
ocs_scsi_ini_new_device(ocs_t *ocs) | |||||
{ | |||||
return ocs_cam_attach(ocs); | |||||
} | |||||
/** | |||||
* @ingroup scsi_api_initiator | |||||
* @brief Tears down initiator members of ocs structure. | |||||
* | |||||
* Called by OS code when device is removed. | |||||
* | |||||
* @param ocs pointer to ocs | |||||
* | |||||
* @return returns 0 for success, a negative error code value for failure. | |||||
*/ | |||||
int32_t | |||||
ocs_scsi_ini_del_device(ocs_t *ocs) | |||||
{ | |||||
return ocs_cam_detach(ocs); | |||||
} | |||||
/** | |||||
* @ingroup scsi_api_initiator | |||||
* @brief accept new domain notification | |||||
* | |||||
* Called by base drive when new domain is discovered. An initiator-client | |||||
* will accept this call to prepare for new remote node notifications | |||||
* arising from ocs_scsi_new_target(). | |||||
* | |||||
* The domain context has the element <b>ocs_scsi_ini_domain_t ini_domain</b> | |||||
* which is declared by the initiator-client code and is used for | |||||
* initiator-client private data. | |||||
* | |||||
* This function will only be called if the base-driver has been enabled for | |||||
* initiator capability. | |||||
* | |||||
* Note that this call is made to initiator-client backends, | |||||
* the ocs_scsi_tgt_new_domain() function is called to target-server backends. | |||||
* | |||||
* @param domain pointer to domain | |||||
* | |||||
* @return returns 0 for success, a negative error code value for failure. | |||||
*/ | |||||
int32_t | |||||
ocs_scsi_ini_new_domain(ocs_domain_t *domain) | |||||
{ | |||||
return 0; | |||||
} | |||||
/** | |||||
* @ingroup scsi_api_initiator | |||||
* @brief accept domain lost notification | |||||
* | |||||
* Called by base-driver when a domain goes away. An initiator-client will | |||||
* use this call to clean up all domain scoped resources. | |||||
* | |||||
* This function will only be called if the base-driver has been enabled for | |||||
* initiator capability. | |||||
* | |||||
* Note that this call is made to initiator-client backends, | |||||
* the ocs_scsi_tgt_del_domain() function is called to target-server backends. | |||||
* | |||||
* @param domain pointer to domain | |||||
* | |||||
* @return returns 0 for success, a negative error code value for failure. | |||||
*/ | |||||
void | |||||
ocs_scsi_ini_del_domain(ocs_domain_t *domain) | |||||
{ | |||||
} | |||||
/** | |||||
* @ingroup scsi_api_initiator | |||||
* @brief accept new sli port notification | |||||
* | |||||
* Called by base drive when new sli port (sport) is discovered. | |||||
* A target-server will use this call to prepare for new remote node | |||||
* notifications arising from ocs_scsi_new_initiator(). | |||||
* | |||||
* This function will only be called if the base-driver has been enabled for | |||||
* target capability. | |||||
* | |||||
* Note that this call is made to target-server backends, | |||||
* the ocs_scsi_ini_new_sport() function is called to initiator-client backends. | |||||
* | |||||
Not Done Inline ActionsThis and above/below looks like leaked debugging. mav: This and above/below looks like leaked debugging. | |||||
* @param sport pointer to sport | |||||
* | |||||
* @return returns 0 for success, a negative error code value for failure. | |||||
*/ | |||||
int32_t | |||||
ocs_scsi_ini_new_sport(ocs_sport_t *sport) | |||||
{ | |||||
return 0; | |||||
} | |||||
/** | |||||
* @ingroup scsi_api_initiator | |||||
* @brief accept sli port gone notification | |||||
* | |||||
* Called by base-driver when a sport goes away. A target-server will | |||||
* use this call to clean up all sport scoped resources. | |||||
* | |||||
* Note that this call is made to target-server backends, | |||||
* the ocs_scsi_ini_del_sport() function is called to initiator-client backends. | |||||
* | |||||
* @param sport pointer to SLI port | |||||
* | |||||
* @return returns 0 for success, a negative error code value for failure. | |||||
*/ | |||||
void | |||||
ocs_scsi_ini_del_sport(ocs_sport_t *sport) | |||||
{ | |||||
} | |||||
/** | |||||
* @ingroup scsi_api_initiator | |||||
* @brief receive notification of a new SCSI target node | |||||
* | |||||
* Sent by base driver to notify an initiator-client of the presense of a new | |||||
* remote target. The initiator-server may use this call to prepare for | |||||
* inbound IO from this node. | |||||
* | |||||
* This function is only called if the base driver is enabled for | |||||
* initiator capability. | |||||
* | |||||
* @param node pointer to new remote initiator node | |||||
* | |||||
* @return none | |||||
* | |||||
* @note | |||||
*/ | |||||
int32_t | |||||
ocs_scsi_new_target(ocs_node_t *node) | |||||
{ | |||||
struct ocs_softc *ocs = node->ocs; | |||||
union ccb *ccb = NULL; | |||||
if (NULL == (ccb = xpt_alloc_ccb_nowait())) { | |||||
device_printf(ocs->dev, "%s: ccb allocation failed\n", __func__); | |||||
return -1; | |||||
} | |||||
if (CAM_REQ_CMP != xpt_create_path(&ccb->ccb_h.path, xpt_periph, | |||||
cam_sim_path(ocs->sim), | |||||
node->instance_index, CAM_LUN_WILDCARD)) { | |||||
device_printf( | |||||
ocs->dev, "%s: target path creation failed\n", __func__); | |||||
xpt_free_ccb(ccb); | |||||
return -1; | |||||
} | |||||
xpt_rescan(ccb); | |||||
return 0; | |||||
} | |||||
/** | |||||
* @ingroup scsi_api_initiator | |||||
* @brief Delete a SCSI target node | |||||
* | |||||
* Sent by base driver to notify a initiator-client that a remote target | |||||
* is now gone. The base driver will have terminated all outstanding IOs | |||||
* and the initiator-client will receive appropriate completions. | |||||
* | |||||
* The ocs_node_t structure has and elment of type ocs_scsi_ini_node_t named | |||||
* ini_node that is declared and used by a target-server for private | |||||
* information. | |||||
* | |||||
* This function is only called if the base driver is enabled for | |||||
* initiator capability. | |||||
* | |||||
* @param node pointer node being deleted | |||||
* @param reason reason for deleting the target | |||||
* | |||||
* @return Returns OCS_SCSI_CALL_ASYNC if target delete is queued for async | |||||
* completion and OCS_SCSI_CALL_COMPLETE if call completed or error. | |||||
* | |||||
* @note | |||||
*/ | |||||
int32_t | |||||
ocs_scsi_del_target(ocs_node_t *node, ocs_scsi_del_target_reason_e reason) | |||||
{ | |||||
struct ocs_softc *ocs = node->ocs; | |||||
struct cam_path *cpath = NULL; | |||||
if (!ocs->sim) { | |||||
device_printf(ocs->dev, "%s: calling with NULL sim\n", __func__); | |||||
return OCS_SCSI_CALL_COMPLETE; | |||||
} | |||||
if (CAM_REQ_CMP == xpt_create_path(&cpath, NULL, cam_sim_path(ocs->sim), | |||||
node->instance_index, CAM_LUN_WILDCARD)) { | |||||
xpt_async(AC_LOST_DEVICE, cpath, NULL); | |||||
xpt_free_path(cpath); | |||||
} | |||||
return OCS_SCSI_CALL_COMPLETE; | |||||
} | |||||
/** | |||||
* @brief Initialize SCSI IO | |||||
* | |||||
* Initialize SCSI IO, this function is called once per IO during IO pool | |||||
* allocation so that the target server may initialize any of its own private | |||||
* data. | |||||
* | |||||
* @param io pointer to SCSI IO object | |||||
* | |||||
* @return returns 0 for success, a negative error code value for failure. | |||||
*/ | |||||
int32_t | |||||
ocs_scsi_tgt_io_init(ocs_io_t *io) | |||||
{ | |||||
return 0; | |||||
} | |||||
/** | |||||
* @brief Uninitialize SCSI IO | |||||
* | |||||
* Uninitialize target server private data in a SCSI io object | |||||
* | |||||
* @param io pointer to SCSI IO object | |||||
* | |||||
* @return returns 0 for success, a negative error code value for failure. | |||||
*/ | |||||
int32_t | |||||
ocs_scsi_tgt_io_exit(ocs_io_t *io) | |||||
{ | |||||
return 0; | |||||
} | |||||
/** | |||||
* @brief Initialize SCSI IO | |||||
* | |||||
* Initialize SCSI IO, this function is called once per IO during IO pool | |||||
* allocation so that the initiator client may initialize any of its own private | |||||
* data. | |||||
* | |||||
* @param io pointer to SCSI IO object | |||||
* | |||||
* @return returns 0 for success, a negative error code value for failure. | |||||
*/ | |||||
int32_t | |||||
ocs_scsi_ini_io_init(ocs_io_t *io) | |||||
{ | |||||
return 0; | |||||
} | |||||
/** | |||||
* @brief Uninitialize SCSI IO | |||||
* | |||||
* Uninitialize initiator client private data in a SCSI io object | |||||
* | |||||
* @param io pointer to SCSI IO object | |||||
* | |||||
* @return returns 0 for success, a negative error code value for failure. | |||||
*/ | |||||
int32_t | |||||
ocs_scsi_ini_io_exit(ocs_io_t *io) | |||||
{ | |||||
return 0; | |||||
} | |||||
/* | |||||
* End of functions required by SCSI base driver API | |||||
***************************************************************************/ | |||||
static __inline void | |||||
ocs_set_ccb_status(union ccb *ccb, cam_status status) | |||||
{ | |||||
ccb->ccb_h.status &= ~CAM_STATUS_MASK; | |||||
ccb->ccb_h.status |= status; | |||||
} | |||||
static int32_t | |||||
ocs_task_set_full_or_busy_cb(ocs_io_t *io, ocs_scsi_io_status_e scsi_status, | |||||
uint32_t flags, void *arg) | |||||
{ | |||||
ocs_target_io_free(io); | |||||
return 0; | |||||
} | |||||
/** | |||||
* @brief send SCSI task set full or busy status | |||||
* | |||||
* A SCSI task set full or busy response is sent depending on whether | |||||
* another IO is already active on the LUN. | |||||
* | |||||
* @param io pointer to IO context | |||||
* | |||||
* @return returns 0 for success, a negative error code value for failure. | |||||
*/ | |||||
static int32_t | |||||
ocs_task_set_full_or_busy(ocs_io_t *io) | |||||
{ | |||||
ocs_scsi_cmd_resp_t rsp = { 0 }; | |||||
ocs_t *ocs = io->ocs; | |||||
/* | |||||
* If there is another command for the LUN, then send task set full, | |||||
* if this is the first one, then send the busy status. | |||||
* | |||||
* if 'busy sent' is FALSE, set it to TRUE and send BUSY | |||||
* otherwise send FULL | |||||
*/ | |||||
if (atomic_cmpset_acq_32(&io->node->tgt_node.busy_sent, FALSE, TRUE)) { | |||||
rsp.scsi_status = SCSI_STATUS_BUSY; /* Busy */ | |||||
printf("%s: busy [%s] tag=%x iiu=%d ihw=%d\n", __func__, | |||||
io->node->display_name, io->tag, | |||||
io->ocs->io_in_use, io->ocs->io_high_watermark); | |||||
} else { | |||||
rsp.scsi_status = SCSI_STATUS_TASK_SET_FULL; /* Task set full */ | |||||
printf("%s: full tag=%x iiu=%d\n", __func__, io->tag, | |||||
io->ocs->io_in_use); | |||||
} | |||||
/* Log a message here indicating a busy or task set full state */ | |||||
if (OCS_LOG_ENABLE_Q_FULL_BUSY_MSG(ocs)) { | |||||
/* Log Task Set Full */ | |||||
if (rsp.scsi_status == SCSI_STATUS_TASK_SET_FULL) { | |||||
/* Task Set Full Message */ | |||||
ocs_log_info(ocs, "OCS CAM TASK SET FULL. Tasks >= %d\n", | |||||
ocs->io_high_watermark); | |||||
} | |||||
else if (rsp.scsi_status == SCSI_STATUS_BUSY) { | |||||
/* Log Busy Message */ | |||||
ocs_log_info(ocs, "OCS CAM SCSI BUSY\n"); | |||||
} | |||||
} | |||||
/* Send the response */ | |||||
return | |||||
ocs_scsi_send_resp(io, 0, &rsp, ocs_task_set_full_or_busy_cb, NULL); | |||||
} | |||||
/** | |||||
* @ingroup cam_io | |||||
* @brief Process target IO completions | |||||
* | |||||
* @param io | |||||
* @param scsi_status did the IO complete successfully | |||||
* @param flags | |||||
* @param arg application specific pointer provided in the call to ocs_target_io() | |||||
* | |||||
* @todo | |||||
*/ | |||||
static int32_t ocs_scsi_target_io_cb(ocs_io_t *io, | |||||
ocs_scsi_io_status_e scsi_status, | |||||
uint32_t flags, void *arg) | |||||
{ | |||||
union ccb *ccb = arg; | |||||
struct ccb_scsiio *csio = &ccb->csio; | |||||
struct ocs_softc *ocs = csio->ccb_h.ccb_ocs_ptr; | |||||
uint32_t cam_dir = ccb->ccb_h.flags & CAM_DIR_MASK; | |||||
uint32_t io_is_done = | |||||
(ccb->ccb_h.flags & CAM_SEND_STATUS) == CAM_SEND_STATUS; | |||||
ccb->ccb_h.status &= ~CAM_SIM_QUEUED; | |||||
if (CAM_DIR_NONE != cam_dir) { | |||||
bus_dmasync_op_t op; | |||||
if (CAM_DIR_IN == cam_dir) { | |||||
op = BUS_DMASYNC_POSTREAD; | |||||
} else { | |||||
op = BUS_DMASYNC_POSTWRITE; | |||||
} | |||||
/* Synchronize the DMA memory with the CPU and free the mapping */ | |||||
bus_dmamap_sync(ocs->buf_dmat, io->tgt_io.dmap, op); | |||||
if (io->tgt_io.flags & OCS_CAM_IO_F_DMAPPED) { | |||||
bus_dmamap_unload(ocs->buf_dmat, io->tgt_io.dmap); | |||||
} | |||||
} | |||||
if (io->tgt_io.sendresp) { | |||||
io->tgt_io.sendresp = 0; | |||||
ocs_scsi_cmd_resp_t resp = { 0 }; | |||||
io->tgt_io.state = OCS_CAM_IO_RESP; | |||||
resp.scsi_status = scsi_status; | |||||
if (ccb->ccb_h.flags & CAM_SEND_SENSE) { | |||||
resp.sense_data = (uint8_t *)&csio->sense_data; | |||||
resp.sense_data_length = csio->sense_len; | |||||
} | |||||
resp.residual = io->exp_xfer_len - io->transferred; | |||||
return ocs_scsi_send_resp(io, 0, &resp, ocs_scsi_target_io_cb, ccb); | |||||
} | |||||
switch (scsi_status) { | |||||
case OCS_SCSI_STATUS_GOOD: | |||||
ocs_set_ccb_status(ccb, CAM_REQ_CMP); | |||||
break; | |||||
case OCS_SCSI_STATUS_ABORTED: | |||||
ocs_set_ccb_status(ccb, CAM_REQ_ABORTED); | |||||
break; | |||||
default: | |||||
ocs_set_ccb_status(ccb, CAM_REQ_CMP_ERR); | |||||
} | |||||
if (io_is_done) { | |||||
if ((io->tgt_io.flags & OCS_CAM_IO_F_ABORT_NOTIFY) == 0) { | |||||
ocs_target_io_free(io); | |||||
} | |||||
} else { | |||||
io->tgt_io.state = OCS_CAM_IO_DATA_DONE; | |||||
/*device_printf(ocs->dev, "%s: CTIO state=%d tag=%#x\n", | |||||
__func__, io->tgt_io.state, io->tag);*/ | |||||
} | |||||
xpt_done(ccb); | |||||
return 0; | |||||
} | |||||
/** | |||||
* @note 1. Since the CCB is assigned to the ocs_io_t on an XPT_CONT_TARGET_IO | |||||
* action, if an initiator aborts a command prior to the SIM receiving | |||||
* a CTIO, the IO's CCB will be NULL. | |||||
*/ | |||||
static int32_t | |||||
ocs_io_abort_cb(ocs_io_t *io, ocs_scsi_io_status_e scsi_status, uint32_t flags, void *arg) | |||||
{ | |||||
struct ocs_softc *ocs = NULL; | |||||
ocs_io_t *tmfio = arg; | |||||
ocs_scsi_tmf_resp_e tmf_resp = OCS_SCSI_TMF_FUNCTION_COMPLETE; | |||||
int32_t rc = 0; | |||||
ocs = io->ocs; | |||||
io->tgt_io.flags &= ~OCS_CAM_IO_F_ABORT_DEV; | |||||
/* A good status indicates the IO was aborted and will be completed in | |||||
* the IO's completion handler. Handle the other cases here. */ | |||||
switch (scsi_status) { | |||||
case OCS_SCSI_STATUS_GOOD: | |||||
break; | |||||
case OCS_SCSI_STATUS_NO_IO: | |||||
break; | |||||
default: | |||||
device_printf(ocs->dev, "%s: unhandled status %d\n", | |||||
__func__, scsi_status); | |||||
tmf_resp = OCS_SCSI_TMF_FUNCTION_REJECTED; | |||||
rc = -1; | |||||
} | |||||
ocs_scsi_send_tmf_resp(tmfio, tmf_resp, NULL, ocs_target_tmf_cb, NULL); | |||||
return rc; | |||||
} | |||||
/** | |||||
* @ingroup cam_io | |||||
* @brief Process initiator IO completions | |||||
* | |||||
* @param io | |||||
* @param scsi_status did the IO complete successfully | |||||
* @param rsp pointer to response buffer | |||||
* @param flags | |||||
* @param arg application specific pointer provided in the call to ocs_target_io() | |||||
* | |||||
* @todo | |||||
*/ | |||||
static int32_t ocs_scsi_initiator_io_cb(ocs_io_t *io, | |||||
ocs_scsi_io_status_e scsi_status, | |||||
ocs_scsi_cmd_resp_t *rsp, | |||||
uint32_t flags, void *arg) | |||||
{ | |||||
union ccb *ccb = arg; | |||||
struct ccb_scsiio *csio = &ccb->csio; | |||||
struct ocs_softc *ocs = csio->ccb_h.ccb_ocs_ptr; | |||||
uint32_t cam_dir = ccb->ccb_h.flags & CAM_DIR_MASK; | |||||
cam_status ccb_status= CAM_REQ_CMP_ERR; | |||||
if (CAM_DIR_NONE != cam_dir) { | |||||
bus_dmasync_op_t op; | |||||
if (CAM_DIR_IN == cam_dir) { | |||||
op = BUS_DMASYNC_POSTREAD; | |||||
} else { | |||||
op = BUS_DMASYNC_POSTWRITE; | |||||
} | |||||
/* Synchronize the DMA memory with the CPU and free the mapping */ | |||||
bus_dmamap_sync(ocs->buf_dmat, io->tgt_io.dmap, op); | |||||
if (io->tgt_io.flags & OCS_CAM_IO_F_DMAPPED) { | |||||
bus_dmamap_unload(ocs->buf_dmat, io->tgt_io.dmap); | |||||
} | |||||
} | |||||
if (scsi_status == OCS_SCSI_STATUS_CHECK_RESPONSE) { | |||||
csio->scsi_status = rsp->scsi_status; | |||||
if (SCSI_STATUS_OK != rsp->scsi_status) { | |||||
ccb_status = CAM_SCSI_STATUS_ERROR; | |||||
} | |||||
csio->resid = rsp->residual; | |||||
if (rsp->residual > 0) { | |||||
uint32_t length = rsp->response_wire_length; | |||||
/* underflow */ | |||||
if (csio->dxfer_len == (length + csio->resid)) { | |||||
ccb_status = CAM_REQ_CMP; | |||||
} | |||||
} else if (rsp->residual < 0) { | |||||
ccb_status = CAM_DATA_RUN_ERR; | |||||
} | |||||
if ((rsp->sense_data_length) && | |||||
!(ccb->ccb_h.flags & (CAM_SENSE_PHYS | CAM_SENSE_PTR))) { | |||||
uint32_t sense_len = 0; | |||||
ccb->ccb_h.status |= CAM_AUTOSNS_VALID; | |||||
if (rsp->sense_data_length < csio->sense_len) { | |||||
csio->sense_resid = | |||||
csio->sense_len - rsp->sense_data_length; | |||||
sense_len = rsp->sense_data_length; | |||||
} else { | |||||
csio->sense_resid = 0; | |||||
sense_len = csio->sense_len; | |||||
} | |||||
ocs_memcpy(&csio->sense_data, rsp->sense_data, sense_len); | |||||
} | |||||
} else if (scsi_status != OCS_SCSI_STATUS_GOOD) { | |||||
ccb_status = CAM_REQ_CMP_ERR; | |||||
} else { | |||||
ccb_status = CAM_REQ_CMP; | |||||
} | |||||
ocs_set_ccb_status(ccb, ccb_status); | |||||
ocs_scsi_io_free(io); | |||||
csio->ccb_h.ccb_io_ptr = NULL; | |||||
csio->ccb_h.ccb_ocs_ptr = NULL; | |||||
ccb->ccb_h.status &= ~CAM_SIM_QUEUED; | |||||
xpt_done(ccb); | |||||
return 0; | |||||
} | |||||
/** | |||||
* @brief Load scatter-gather list entries into an IO | |||||
* | |||||
* This routine relies on the driver instance's software context pointer and | |||||
* the IO object pointer having been already assigned to hooks in the CCB. | |||||
* Although the routine does not return success/fail, callers can look at the | |||||
* n_sge member to determine if the mapping failed (0 on failure). | |||||
* | |||||
* @param arg pointer to the CAM ccb for this IO | |||||
* @param seg DMA address/length pairs | |||||
* @param nseg number of DMA address/length pairs | |||||
* @param error any errors while mapping the IO | |||||
*/ | |||||
static void | |||||
ocs_scsi_dmamap_load(void *arg, bus_dma_segment_t *seg, int nseg, int error) | |||||
{ | |||||
ocs_dmamap_load_arg_t *sglarg = (ocs_dmamap_load_arg_t*) arg; | |||||
if (error) { | |||||
printf("%s: seg=%p nseg=%d error=%d\n", | |||||
__func__, seg, nseg, error); | |||||
sglarg->rc = -1; | |||||
} else { | |||||
uint32_t i = 0; | |||||
uint32_t c = 0; | |||||
if ((sglarg->sgl_count + nseg) > sglarg->sgl_max) { | |||||
printf("%s: sgl_count=%d nseg=%d max=%d\n", __func__, | |||||
sglarg->sgl_count, nseg, sglarg->sgl_max); | |||||
sglarg->rc = -2; | |||||
return; | |||||
} | |||||
for (i = 0, c = sglarg->sgl_count; i < nseg; i++, c++) { | |||||
sglarg->sgl[c].addr = seg[i].ds_addr; | |||||
sglarg->sgl[c].len = seg[i].ds_len; | |||||
} | |||||
sglarg->sgl_count = c; | |||||
sglarg->rc = 0; | |||||
} | |||||
} | |||||
/** | |||||
* @brief Build a scatter-gather list from a CAM CCB | |||||
* | |||||
* @param ocs the driver instance's software context | |||||
* @param ccb pointer to the CCB | |||||
* @param io pointer to the previously allocated IO object | |||||
* @param sgl pointer to SGL | |||||
* @param sgl_max number of entries in sgl | |||||
* | |||||
* @return 0 on success, non-zero otherwise | |||||
*/ | |||||
static int32_t | |||||
ocs_build_scsi_sgl(struct ocs_softc *ocs, union ccb *ccb, ocs_io_t *io, | |||||
ocs_scsi_sgl_t *sgl, uint32_t sgl_max) | |||||
{ | |||||
ocs_dmamap_load_arg_t dmaarg; | |||||
int32_t err = 0; | |||||
if (!ocs || !ccb || !io || !sgl) { | |||||
printf("%s: bad param o=%p c=%p i=%p s=%p\n", __func__, | |||||
ocs, ccb, io, sgl); | |||||
return -1; | |||||
} | |||||
io->tgt_io.flags &= ~OCS_CAM_IO_F_DMAPPED; | |||||
dmaarg.sgl = sgl; | |||||
dmaarg.sgl_count = 0; | |||||
dmaarg.sgl_max = sgl_max; | |||||
dmaarg.rc = 0; | |||||
err = bus_dmamap_load_ccb(ocs->buf_dmat, io->tgt_io.dmap, ccb, | |||||
ocs_scsi_dmamap_load, &dmaarg, 0); | |||||
if (err || dmaarg.rc) { | |||||
device_printf( | |||||
ocs->dev, "%s: bus_dmamap_load_ccb error (%d %d)\n", | |||||
__func__, err, dmaarg.rc); | |||||
return -1; | |||||
} | |||||
io->tgt_io.flags |= OCS_CAM_IO_F_DMAPPED; | |||||
return dmaarg.sgl_count; | |||||
} | |||||
/** | |||||
* @ingroup cam_io | |||||
* @brief Send a target IO | |||||
* | |||||
* @param ocs the driver instance's software context | |||||
* @param ccb pointer to the CCB | |||||
* | |||||
* @return 0 on success, non-zero otherwise | |||||
*/ | |||||
static int32_t | |||||
ocs_target_io(struct ocs_softc *ocs, union ccb *ccb) | |||||
{ | |||||
struct ccb_scsiio *csio = &ccb->csio; | |||||
ocs_io_t *io = NULL; | |||||
uint32_t cam_dir = ccb->ccb_h.flags & CAM_DIR_MASK; | |||||
bool sendstatus = ccb->ccb_h.flags & CAM_SEND_STATUS; | |||||
uint32_t xferlen = csio->dxfer_len; | |||||
int32_t rc = 0; | |||||
io = ocs_scsi_find_io(ocs, csio->tag_id); | |||||
if (io == NULL) { | |||||
ocs_set_ccb_status(ccb, CAM_REQ_CMP_ERR); | |||||
panic("bad tag value"); | |||||
return -1; | |||||
} | |||||
/* Received an ABORT TASK for this IO */ | |||||
if (io->tgt_io.flags & OCS_CAM_IO_F_ABORT_RECV) { | |||||
/*device_printf(ocs->dev, | |||||
"%s: XPT_CONT_TARGET_IO state=%d tag=%#x xid=%#x flags=%#x\n", | |||||
__func__, io->tgt_io.state, io->tag, io->init_task_tag, | |||||
io->tgt_io.flags);*/ | |||||
io->tgt_io.flags |= OCS_CAM_IO_F_ABORT_CAM; | |||||
if (ccb->ccb_h.flags & CAM_SEND_STATUS) { | |||||
ocs_target_io_free(io); | |||||
} | |||||
ocs_set_ccb_status(ccb, CAM_REQ_ABORTED); | |||||
return -1; | |||||
} | |||||
io->tgt_io.app = ccb; | |||||
ocs_set_ccb_status(ccb, CAM_REQ_INPROG); | |||||
ccb->ccb_h.status |= CAM_SIM_QUEUED; | |||||
csio->ccb_h.ccb_ocs_ptr = ocs; | |||||
csio->ccb_h.ccb_io_ptr = io; | |||||
if ((sendstatus && (xferlen == 0))) { | |||||
ocs_scsi_cmd_resp_t resp = { 0 }; | |||||
ocs_assert(ccb->ccb_h.flags & CAM_SEND_STATUS, -1); | |||||
io->tgt_io.state = OCS_CAM_IO_RESP; | |||||
resp.scsi_status = csio->scsi_status; | |||||
if (ccb->ccb_h.flags & CAM_SEND_SENSE) { | |||||
resp.sense_data = (uint8_t *)&csio->sense_data; | |||||
resp.sense_data_length = csio->sense_len; | |||||
} | |||||
resp.residual = io->exp_xfer_len - io->transferred; | |||||
rc = ocs_scsi_send_resp(io, 0, &resp, ocs_scsi_target_io_cb, ccb); | |||||
} else if (xferlen != 0) { | |||||
ocs_scsi_sgl_t sgl[OCS_FC_MAX_SGL]; | |||||
int32_t sgl_count = 0; | |||||
io->tgt_io.state = OCS_CAM_IO_DATA; | |||||
if (sendstatus) | |||||
io->tgt_io.sendresp = 1; | |||||
sgl_count = ocs_build_scsi_sgl(ocs, ccb, io, sgl, ARRAY_SIZE(sgl)); | |||||
if (sgl_count > 0) { | |||||
if (cam_dir == CAM_DIR_IN) { | |||||
rc = ocs_scsi_send_rd_data(io, 0, NULL, sgl, | |||||
sgl_count, csio->dxfer_len, | |||||
ocs_scsi_target_io_cb, ccb); | |||||
} else if (cam_dir == CAM_DIR_OUT) { | |||||
rc = ocs_scsi_recv_wr_data(io, 0, NULL, sgl, | |||||
sgl_count, csio->dxfer_len, | |||||
ocs_scsi_target_io_cb, ccb); | |||||
} else { | |||||
device_printf(ocs->dev, "%s:" | |||||
" unknown CAM direction %#x\n", | |||||
__func__, cam_dir); | |||||
rc = -1; | |||||
} | |||||
} else { | |||||
device_printf(ocs->dev, "%s: building SGL failed\n", | |||||
__func__); | |||||
rc = -1; | |||||
} | |||||
} else { | |||||
device_printf(ocs->dev, "%s: Wrong value xfer and sendstatus" | |||||
" are 0 \n", __func__); | |||||
rc = -1; | |||||
} | |||||
if (rc) { | |||||
ocs_set_ccb_status(ccb, CAM_REQ_CMP_ERR); | |||||
ccb->ccb_h.status &= ~CAM_SIM_QUEUED; | |||||
io->tgt_io.state = OCS_CAM_IO_DATA_DONE; | |||||
device_printf(ocs->dev, "%s: CTIO state=%d tag=%#x\n", | |||||
__func__, io->tgt_io.state, io->tag); | |||||
if ((sendstatus && (xferlen == 0))) { | |||||
ocs_target_io_free(io); | |||||
} | |||||
} | |||||
return rc; | |||||
} | |||||
static int32_t | |||||
ocs_target_tmf_cb(ocs_io_t *io, ocs_scsi_io_status_e scsi_status, uint32_t flags, | |||||
void *arg) | |||||
{ | |||||
/*device_printf(io->ocs->dev, "%s: tag=%x io=%p s=%#x\n", | |||||
__func__, io->tag, io, scsi_status);*/ | |||||
ocs_scsi_io_complete(io); | |||||
return 0; | |||||
} | |||||
/** | |||||
* @ingroup cam_io | |||||
* @brief Send an initiator IO | |||||
* | |||||
* @param ocs the driver instance's software context | |||||
* @param ccb pointer to the CCB | |||||
* | |||||
* @return 0 on success, non-zero otherwise | |||||
*/ | |||||
static int32_t | |||||
ocs_initiator_io(struct ocs_softc *ocs, union ccb *ccb) | |||||
{ | |||||
int32_t rc; | |||||
struct ccb_scsiio *csio = &ccb->csio; | |||||
struct ccb_hdr *ccb_h = &csio->ccb_h; | |||||
ocs_node_t *node = NULL; | |||||
ocs_io_t *io = NULL; | |||||
ocs_scsi_sgl_t sgl[OCS_FC_MAX_SGL]; | |||||
int32_t sgl_count; | |||||
if (ccb_h->target_id > OCS_MAX_TARGETS) | |||||
panic("%s target id[%d] out of range\n", __func__, | |||||
ccb_h->target_id); | |||||
node = ocs_node_get_instance(ocs, ccb_h->target_id); | |||||
if (node == NULL) { | |||||
device_printf(ocs->dev, "%s: no device %d\n", __func__, | |||||
ccb_h->target_id); | |||||
return CAM_DEV_NOT_THERE; | |||||
} | |||||
if (!node->targ) { | |||||
device_printf(ocs->dev, "%s: not target device %d\n", __func__, | |||||
ccb_h->target_id); | |||||
return CAM_DEV_NOT_THERE; | |||||
} | |||||
io = ocs_scsi_io_alloc(node, OCS_SCSI_IO_ROLE_ORIGINATOR); | |||||
if (io == NULL) { | |||||
device_printf(ocs->dev, "%s: unable to alloc IO\n", __func__); | |||||
return -1; | |||||
} | |||||
/* eventhough this is INI, use target structure as ocs_build_scsi_sgl | |||||
* only references the tgt_io part of an ocs_io_t */ | |||||
io->tgt_io.app = ccb; | |||||
csio->ccb_h.ccb_ocs_ptr = ocs; | |||||
csio->ccb_h.ccb_io_ptr = io; | |||||
sgl_count = ocs_build_scsi_sgl(ocs, ccb, io, sgl, ARRAY_SIZE(sgl)); | |||||
if (sgl_count < 0) { | |||||
ocs_scsi_io_free(io); | |||||
device_printf(ocs->dev, "%s: building SGL failed\n", __func__); | |||||
return -1; | |||||
} | |||||
if (ccb->ccb_h.timeout == CAM_TIME_INFINITY) { | |||||
io->timeout = 0; | |||||
} else if (ccb->ccb_h.timeout == CAM_TIME_DEFAULT) { | |||||
io->timeout = OCS_CAM_IO_TIMEOUT; | |||||
} else { | |||||
io->timeout = ccb->ccb_h.timeout; | |||||
} | |||||
switch (ccb->ccb_h.flags & CAM_DIR_MASK) { | |||||
case CAM_DIR_NONE: | |||||
rc = ocs_scsi_send_nodata_io(node, io, ccb_h->target_lun, | |||||
ccb->ccb_h.flags & CAM_CDB_POINTER ? | |||||
csio->cdb_io.cdb_ptr: csio->cdb_io.cdb_bytes, | |||||
csio->cdb_len, | |||||
ocs_scsi_initiator_io_cb, ccb); | |||||
break; | |||||
case CAM_DIR_IN: | |||||
rc = ocs_scsi_send_rd_io(node, io, ccb_h->target_lun, | |||||
ccb->ccb_h.flags & CAM_CDB_POINTER ? | |||||
csio->cdb_io.cdb_ptr: csio->cdb_io.cdb_bytes, | |||||
csio->cdb_len, | |||||
NULL, | |||||
sgl, sgl_count, csio->dxfer_len, | |||||
ocs_scsi_initiator_io_cb, ccb); | |||||
break; | |||||
case CAM_DIR_OUT: | |||||
rc = ocs_scsi_send_wr_io(node, io, ccb_h->target_lun, | |||||
ccb->ccb_h.flags & CAM_CDB_POINTER ? | |||||
csio->cdb_io.cdb_ptr: csio->cdb_io.cdb_bytes, | |||||
csio->cdb_len, | |||||
NULL, | |||||
sgl, sgl_count, csio->dxfer_len, | |||||
ocs_scsi_initiator_io_cb, ccb); | |||||
break; | |||||
default: | |||||
panic("%s invalid data direction %08x\n", __func__, | |||||
ccb->ccb_h.flags); | |||||
break; | |||||
} | |||||
return rc; | |||||
} | |||||
/** | |||||
* @ingroup cam_api | |||||
* @brief Process CAM actions | |||||
* | |||||
* The driver supplies this routine to the CAM during intialization and | |||||
* is the main entry point for processing CAM Control Blocks (CCB) | |||||
* | |||||
* @param sim pointer to the SCSI Interface Module | |||||
* @param ccb CAM control block | |||||
* | |||||
* @todo | |||||
* - populate path inquiry data via info retrieved from SLI port | |||||
*/ | |||||
static void | |||||
ocs_action(struct cam_sim *sim, union ccb *ccb) | |||||
{ | |||||
struct ocs_softc *ocs = (struct ocs_softc *)cam_sim_softc(sim); | |||||
struct ccb_hdr *ccb_h = &ccb->ccb_h; | |||||
int32_t rc; | |||||
switch (ccb_h->func_code) { | |||||
case XPT_SCSI_IO: | |||||
if ((ccb->ccb_h.flags & CAM_CDB_POINTER) != 0) { | |||||
if ((ccb->ccb_h.flags & CAM_CDB_PHYS) != 0) { | |||||
ccb->ccb_h.status = CAM_REQ_INVALID; | |||||
xpt_done(ccb); | |||||
break; | |||||
} | |||||
} | |||||
rc = ocs_initiator_io(ocs, ccb); | |||||
if (0 == rc) { | |||||
ocs_set_ccb_status(ccb, CAM_REQ_INPROG | CAM_SIM_QUEUED); | |||||
break; | |||||
} else { | |||||
ccb->ccb_h.status &= ~CAM_SIM_QUEUED; | |||||
if (rc > 0) { | |||||
ocs_set_ccb_status(ccb, rc); | |||||
} else { | |||||
ocs_set_ccb_status(ccb, CAM_SEL_TIMEOUT); | |||||
} | |||||
} | |||||
xpt_done(ccb); | |||||
break; | |||||
case XPT_PATH_INQ: | |||||
{ | |||||
struct ccb_pathinq *cpi = &ccb->cpi; | |||||
struct ccb_pathinq_settings_fc *fc = &cpi->xport_specific.fc; | |||||
uint64_t wwn = 0; | |||||
ocs_xport_stats_t value; | |||||
cpi->version_num = 1; | |||||
cpi->protocol = PROTO_SCSI; | |||||
cpi->protocol_version = SCSI_REV_SPC; | |||||
if (ocs->ocs_xport == OCS_XPORT_FC) { | |||||
cpi->transport = XPORT_FC; | |||||
} else { | |||||
cpi->transport = XPORT_UNKNOWN; | |||||
} | |||||
cpi->transport_version = 0; | |||||
/* Set the transport parameters of the SIM */ | |||||
ocs_xport_status(ocs->xport, OCS_XPORT_LINK_SPEED, &value); | |||||
fc->bitrate = value.value * 1000; /* speed in Mbps */ | |||||
wwn = *((uint64_t *)ocs_scsi_get_property_ptr(ocs, OCS_SCSI_WWPN)); | |||||
fc->wwpn = be64toh(wwn); | |||||
wwn = *((uint64_t *)ocs_scsi_get_property_ptr(ocs, OCS_SCSI_WWNN)); | |||||
fc->wwnn = be64toh(wwn); | |||||
if (ocs->domain && ocs->domain->attached) { | |||||
fc->port = ocs->domain->sport->fc_id; | |||||
} | |||||
if (ocs->config_tgt) { | |||||
cpi->target_sprt = | |||||
PIT_PROCESSOR | PIT_DISCONNECT | PIT_TERM_IO; | |||||
} | |||||
cpi->hba_misc = PIM_NOBUSRESET | PIM_UNMAPPED; | |||||
cpi->hba_misc |= PIM_EXTLUNS | PIM_NOSCAN; | |||||
cpi->hba_inquiry = PI_TAG_ABLE; | |||||
cpi->max_target = OCS_MAX_TARGETS; | |||||
cpi->initiator_id = cpi->max_target + 1; | |||||
if (!ocs->enable_ini) { | |||||
cpi->hba_misc |= PIM_NOINITIATOR; | |||||
} | |||||
cpi->max_lun = OCS_MAX_LUN; | |||||
cpi->bus_id = cam_sim_bus(sim); | |||||
/* Need to supply a base transfer speed prior to linking up | |||||
* Worst case, this would be FC 1Gbps */ | |||||
cpi->base_transfer_speed = 1 * 1000 * 1000; | |||||
/* Calculate the max IO supported | |||||
* Worst case would be an OS page per SGL entry */ | |||||
cpi->maxio = PAGE_SIZE * | |||||
(ocs_scsi_get_property(ocs, OCS_SCSI_MAX_SGL) - 1); | |||||
strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); | |||||
strncpy(cpi->hba_vid, "Emulex", HBA_IDLEN); | |||||
strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN); | |||||
cpi->unit_number = cam_sim_unit(sim); | |||||
cpi->ccb_h.status = CAM_REQ_CMP; | |||||
xpt_done(ccb); | |||||
break; | |||||
} | |||||
case XPT_GET_TRAN_SETTINGS: | |||||
{ | |||||
struct ccb_trans_settings *cts = &ccb->cts; | |||||
struct ccb_trans_settings_scsi *scsi = &cts->proto_specific.scsi; | |||||
struct ccb_trans_settings_fc *fc = &cts->xport_specific.fc; | |||||
ocs_xport_stats_t value; | |||||
ocs_node_t *fnode = NULL; | |||||
Done Inline ActionsIt may be not critical, but I would set those three independently of initiator role. I don't know whether your hardware and driver support live role change, but at least with isp(4) driver it works fine. I guess CTL CAM target frontend won't be happy if initiator_id change live. mav: It may be not critical, but I would set those three independently of initiator role. I don't… | |||||
Not Done Inline ActionsAs of now, we are not supporting live role change. ram: As of now, we are not supporting live role change. | |||||
if (ocs->ocs_xport != OCS_XPORT_FC) { | |||||
ocs_set_ccb_status(ccb, CAM_REQ_INVALID); | |||||
xpt_done(ccb); | |||||
break; | |||||
} | |||||
fnode = ocs_node_get_instance(ocs, cts->ccb_h.target_id); | |||||
if (fnode == NULL) { | |||||
ocs_set_ccb_status(ccb, CAM_DEV_NOT_THERE); | |||||
xpt_done(ccb); | |||||
break; | |||||
} | |||||
cts->protocol = PROTO_SCSI; | |||||
Done Inline ActionsI have feeling that here must be -1 in case of buffer not aligned to page boundary. mav: I have feeling that here must be -1 in case of buffer not aligned to page boundary. | |||||
cts->protocol_version = SCSI_REV_SPC2; | |||||
cts->transport = XPORT_FC; | |||||
cts->transport_version = 2; | |||||
scsi->valid = CTS_SCSI_VALID_TQ; | |||||
scsi->flags = CTS_SCSI_FLAGS_TAG_ENB; | |||||
/* speed in Mbps */ | |||||
ocs_xport_status(ocs->xport, OCS_XPORT_LINK_SPEED, &value); | |||||
fc->bitrate = value.value * 1000; | |||||
fc->wwpn = ocs_node_get_wwpn(fnode); | |||||
fc->wwnn = ocs_node_get_wwnn(fnode); | |||||
fc->port = fnode->rnode.fc_id; | |||||
fc->valid = CTS_FC_VALID_SPEED | | |||||
CTS_FC_VALID_WWPN | | |||||
CTS_FC_VALID_WWNN | | |||||
CTS_FC_VALID_PORT; | |||||
ocs_set_ccb_status(ccb, CAM_REQ_CMP); | |||||
xpt_done(ccb); | |||||
break; | |||||
} | |||||
case XPT_SET_TRAN_SETTINGS: | |||||
ocs_set_ccb_status(ccb, CAM_REQ_CMP); | |||||
xpt_done(ccb); | |||||
break; | |||||
case XPT_CALC_GEOMETRY: | |||||
cam_calc_geometry(&ccb->ccg, TRUE); | |||||
xpt_done(ccb); | |||||
break; | |||||
case XPT_GET_SIM_KNOB: | |||||
{ | |||||
struct ccb_sim_knob *knob = &ccb->knob; | |||||
uint64_t wwn = 0; | |||||
if (ocs->ocs_xport != OCS_XPORT_FC) { | |||||
ocs_set_ccb_status(ccb, CAM_REQ_INVALID); | |||||
xpt_done(ccb); | |||||
break; | |||||
Done Inline ActionsThis seems reporting HBA port WWNs, while XPT_GET_TRAN_SETTINGS should report properties of specific target port. HBA port WWNs should be reported via XPT_PATH_INQ cpi->xport_specific.fc.* fields instead. mav: This seems reporting HBA port WWNs, while XPT_GET_TRAN_SETTINGS should report properties of… | |||||
} | |||||
wwn = *((uint64_t *)ocs_scsi_get_property_ptr(ocs, | |||||
OCS_SCSI_WWNN)); | |||||
knob->xport_specific.fc.wwnn = be64toh(wwn); | |||||
wwn = *((uint64_t *)ocs_scsi_get_property_ptr(ocs, | |||||
OCS_SCSI_WWPN)); | |||||
knob->xport_specific.fc.wwpn = be64toh(wwn); | |||||
knob->xport_specific.fc.role = ocs->role; | |||||
knob->xport_specific.fc.valid = KNOB_VALID_ADDRESS | | |||||
KNOB_VALID_ROLE; | |||||
ocs_set_ccb_status(ccb, CAM_REQ_CMP); | |||||
xpt_done(ccb); | |||||
break; | |||||
} | |||||
case XPT_SET_SIM_KNOB: | |||||
{ | |||||
struct ccb_sim_knob *knob = &ccb->knob; | |||||
bool role_changed = FALSE; | |||||
if (ocs->ocs_xport != OCS_XPORT_FC) { | |||||
ocs_set_ccb_status(ccb, CAM_REQ_INVALID); | |||||
xpt_done(ccb); | |||||
break; | |||||
} | |||||
if (knob->xport_specific.fc.valid & KNOB_VALID_ADDRESS) { | |||||
device_printf(ocs->dev, | |||||
"%s: XPT_SET_SIM_KNOB wwnn=%zx wwpn=%zx\n", | |||||
__func__, | |||||
knob->xport_specific.fc.wwnn, | |||||
knob->xport_specific.fc.wwpn); | |||||
} | |||||
if (knob->xport_specific.fc.valid & KNOB_VALID_ROLE) { | |||||
switch (knob->xport_specific.fc.role) { | |||||
case KNOB_ROLE_NONE: | |||||
if (ocs->role != KNOB_ROLE_NONE) { | |||||
ocs->enable_ini = FALSE; | |||||
ocs->enable_tgt = FALSE; | |||||
role_changed = TRUE; | |||||
} | |||||
break; | |||||
case KNOB_ROLE_TARGET: | |||||
if (ocs->role != KNOB_ROLE_TARGET) { | |||||
ocs->enable_ini = FALSE; | |||||
ocs->enable_tgt = TRUE; | |||||
role_changed = TRUE; | |||||
} | |||||
break; | |||||
case KNOB_ROLE_INITIATOR: | |||||
if (ocs->role != KNOB_ROLE_INITIATOR) { | |||||
ocs->enable_ini = TRUE; | |||||
ocs->enable_tgt = FALSE; | |||||
role_changed = TRUE; | |||||
} | |||||
break; | |||||
case KNOB_ROLE_BOTH: | |||||
if (ocs->role != KNOB_ROLE_BOTH) { | |||||
ocs->enable_ini = TRUE; | |||||
ocs->enable_tgt = TRUE; | |||||
role_changed = TRUE; | |||||
} | |||||
break; | |||||
default: | |||||
device_printf(ocs->dev, | |||||
"%s: XPT_SET_SIM_KNOB unsupported role: %d\n", | |||||
__func__, knob->xport_specific.fc.role); | |||||
} | |||||
} | |||||
if (role_changed) { | |||||
device_printf(ocs->dev, | |||||
"XPT_SET_SIM_KNOB old_role: %d new_role: %d\n", | |||||
ocs->role, knob->xport_specific.fc.role); | |||||
ocs->role = knob->xport_specific.fc.role; | |||||
rc = ocs_xport_control(ocs->xport, OCS_XPORT_PORT_OFFLINE); | |||||
if (rc) { | |||||
ocs_log_debug(ocs, "port offline failed : %d\n", rc); | |||||
} | |||||
rc = ocs_xport_control(ocs->xport, OCS_XPORT_PORT_ONLINE); | |||||
if (rc) { | |||||
ocs_log_debug(ocs, "port online failed : %d\n", rc); | |||||
} | |||||
} | |||||
ocs_set_ccb_status(ccb, CAM_REQ_CMP); | |||||
xpt_done(ccb); | |||||
break; | |||||
} | |||||
case XPT_ABORT: | |||||
{ | |||||
union ccb *accb = ccb->cab.abort_ccb; | |||||
switch (accb->ccb_h.func_code) { | |||||
case XPT_ACCEPT_TARGET_IO: | |||||
ocs_abort_atio(ocs, ccb); | |||||
break; | |||||
case XPT_IMMEDIATE_NOTIFY: | |||||
ocs_abort_inot(ocs, ccb); | |||||
break; | |||||
case XPT_SCSI_IO: | |||||
rc = ocs_abort_initiator_io(ocs, accb); | |||||
if (rc) { | |||||
ccb->ccb_h.status = CAM_UA_ABORT; | |||||
} else { | |||||
ccb->ccb_h.status = CAM_REQ_CMP; | |||||
} | |||||
break; | |||||
default: | |||||
printf("abort of unknown func %#x\n", | |||||
accb->ccb_h.func_code); | |||||
ccb->ccb_h.status = CAM_REQ_INVALID; | |||||
break; | |||||
} | |||||
break; | |||||
} | |||||
case XPT_RESET_BUS: | |||||
if (ocs_xport_control(ocs->xport, OCS_XPORT_PORT_OFFLINE) == 0) { | |||||
ocs_xport_control(ocs->xport, OCS_XPORT_PORT_ONLINE); | |||||
ocs_set_ccb_status(ccb, CAM_REQ_CMP); | |||||
} else { | |||||
ocs_set_ccb_status(ccb, CAM_REQ_CMP_ERR); | |||||
} | |||||
xpt_done(ccb); | |||||
break; | |||||
case XPT_RESET_DEV: | |||||
{ | |||||
ocs_node_t *node = NULL; | |||||
ocs_io_t *io = NULL; | |||||
int32_t rc = 0; | |||||
node = ocs_node_get_instance(ocs, ccb_h->target_id); | |||||
if (node == NULL) { | |||||
device_printf(ocs->dev, "%s: no device %d\n", | |||||
__func__, ccb_h->target_id); | |||||
ocs_set_ccb_status(ccb, CAM_DEV_NOT_THERE); | |||||
xpt_done(ccb); | |||||
break; | |||||
} | |||||
io = ocs_scsi_io_alloc(node, OCS_SCSI_IO_ROLE_ORIGINATOR); | |||||
if (io == NULL) { | |||||
device_printf(ocs->dev, "%s: unable to alloc IO\n", | |||||
__func__); | |||||
ocs_set_ccb_status(ccb, CAM_REQ_CMP_ERR); | |||||
xpt_done(ccb); | |||||
break; | |||||
Done Inline ActionsIt would be nice to add XPT_ABORT support for XPT_SCSI_IO too. While CAM does not use it now, I hope it may start at some point. Besides I find it useful for testing target implementation. mav: It would be nice to add XPT_ABORT support for XPT_SCSI_IO too. While CAM does not use it now… | |||||
} | |||||
rc = ocs_scsi_send_tmf(node, io, NULL, ccb_h->target_lun, | |||||
OCS_SCSI_TMF_LOGICAL_UNIT_RESET, | |||||
NULL, 0, 0, /* sgl, sgl_count, length */ | |||||
ocs_initiator_tmf_cb, NULL/*arg*/); | |||||
if (rc) { | |||||
ocs_set_ccb_status(ccb, CAM_REQ_CMP_ERR); | |||||
} else { | |||||
ocs_set_ccb_status(ccb, CAM_REQ_CMP); | |||||
} | |||||
if (node->fcp2device) { | |||||
ocs_reset_crn(node, ccb_h->target_lun); | |||||
} | |||||
xpt_done(ccb); | |||||
break; | |||||
} | |||||
case XPT_EN_LUN: /* target support */ | |||||
{ | |||||
ocs_tgt_resource_t *trsrc = NULL; | |||||
uint32_t status = 0; | |||||
device_printf(ocs->dev, "XPT_EN_LUN %sable %d:%d\n", | |||||
ccb->cel.enable ? "en" : "dis", | |||||
ccb->ccb_h.target_id, | |||||
(unsigned int)ccb->ccb_h.target_lun); | |||||
trsrc = ocs_tgt_resource_get(ocs, &ccb->ccb_h, &status); | |||||
if (trsrc) { | |||||
trsrc->enabled = ccb->cel.enable; | |||||
/* Abort all ATIO/INOT on LUN disable */ | |||||
if (trsrc->enabled == FALSE) { | |||||
ocs_tgt_resource_abort(ocs, trsrc); | |||||
} else { | |||||
trsrc->once = FALSE; | |||||
} | |||||
status = CAM_REQ_CMP; | |||||
} | |||||
ocs_set_ccb_status(ccb, status); | |||||
xpt_done(ccb); | |||||
break; | |||||
} | |||||
/* | |||||
* The flow of target IOs in CAM is: | |||||
* - CAM supplies a number of CCBs to the driver used for received | |||||
* commands. | |||||
* - when the driver receives a command, it copies the relevant | |||||
* information to the CCB and returns it to the CAM using xpt_done() | |||||
* - after the target server processes the request, it creates | |||||
* a new CCB containing information on how to continue the IO and | |||||
* passes that to the driver | |||||
* - the driver processes the "continue IO" (a.k.a CTIO) CCB | |||||
* - once the IO completes, the driver returns the CTIO to the CAM | |||||
* using xpt_done() | |||||
*/ | |||||
case XPT_ACCEPT_TARGET_IO: /* used to inform upper layer of | |||||
received CDB (a.k.a. ATIO) */ | |||||
case XPT_IMMEDIATE_NOTIFY: /* used to inform upper layer of other | |||||
event (a.k.a. INOT) */ | |||||
{ | |||||
ocs_tgt_resource_t *trsrc = NULL; | |||||
uint32_t status = 0; | |||||
/*printf("XPT_%s %p\n", ccb_h->func_code == XPT_ACCEPT_TARGET_IO ? | |||||
"ACCEPT_TARGET_IO" : "IMMEDIATE_NOTIFY", ccb);*/ | |||||
trsrc = ocs_tgt_resource_get(ocs, &ccb->ccb_h, &status); | |||||
if (trsrc == NULL) { | |||||
ocs_set_ccb_status(ccb, CAM_DEV_NOT_THERE); | |||||
xpt_done(ccb); | |||||
break; | |||||
} | |||||
Not Done Inline ActionsIt would be nice if code fit into 80 columns when possible. mav: It would be nice if code fit into 80 columns when possible. | |||||
Not Done Inline ActionsI will submit the indentation changes in the next update. ram: I will submit the indentation changes in the next update. | |||||
if (trsrc->once == FALSE) { | |||||
trsrc->once = TRUE; | |||||
} | |||||
if (XPT_ACCEPT_TARGET_IO == ccb->ccb_h.func_code) { | |||||
struct ccb_accept_tio *atio = NULL; | |||||
atio = (struct ccb_accept_tio *)ccb; | |||||
atio->init_id = 0x0badbeef; | |||||
atio->tag_id = 0xdeadc0de; | |||||
STAILQ_INSERT_TAIL(&trsrc->atio, &ccb->ccb_h, | |||||
sim_links.stqe); | |||||
} else { | |||||
STAILQ_INSERT_TAIL(&trsrc->inot, &ccb->ccb_h, | |||||
sim_links.stqe); | |||||
} | |||||
ccb->ccb_h.ccb_io_ptr = NULL; | |||||
ccb->ccb_h.ccb_ocs_ptr = ocs; | |||||
ocs_set_ccb_status(ccb, CAM_REQ_INPROG); | |||||
/* | |||||
* These actions give resources to the target driver. | |||||
* If we didn't return here, this function would call | |||||
* xpt_done(), signaling to the upper layers that an | |||||
* IO or other event had arrived. | |||||
*/ | |||||
break; | |||||
} | |||||
case XPT_NOTIFY_ACKNOWLEDGE: | |||||
{ | |||||
ocs_io_t *io = NULL; | |||||
ocs_io_t *abortio = NULL; | |||||
/* Get the IO reference for this tag */ | |||||
io = ocs_scsi_find_io(ocs, ccb->cna2.tag_id); | |||||
if (io == NULL) { | |||||
device_printf(ocs->dev, | |||||
"%s: XPT_NOTIFY_ACKNOWLEDGE no IO with tag %#x\n", | |||||
__func__, ccb->cna2.tag_id); | |||||
ocs_set_ccb_status(ccb, CAM_REQ_CMP_ERR); | |||||
xpt_done(ccb); | |||||
break; | |||||
} | |||||
abortio = io->tgt_io.app; | |||||
if (abortio) { | |||||
abortio->tgt_io.flags &= ~OCS_CAM_IO_F_ABORT_NOTIFY; | |||||
device_printf(ocs->dev, | |||||
"%s: XPT_NOTIFY_ACK state=%d tag=%#x xid=%#x" | |||||
" flags=%#x\n", __func__, abortio->tgt_io.state, | |||||
abortio->tag, abortio->init_task_tag, | |||||
abortio->tgt_io.flags); | |||||
/* TMF response was sent in abort callback */ | |||||
} else { | |||||
ocs_scsi_send_tmf_resp(io, | |||||
OCS_SCSI_TMF_FUNCTION_COMPLETE, | |||||
NULL, ocs_target_tmf_cb, NULL); | |||||
} | |||||
ocs_set_ccb_status(ccb, CAM_REQ_CMP); | |||||
xpt_done(ccb); | |||||
break; | |||||
} | |||||
case XPT_CONT_TARGET_IO: /* continue target IO, sending data/response (a.k.a. CTIO) */ | |||||
if (ocs_target_io(ocs, ccb)) { | |||||
device_printf(ocs->dev, | |||||
"XPT_CONT_TARGET_IO failed flags=%x tag=%#x\n", | |||||
ccb->ccb_h.flags, ccb->csio.tag_id); | |||||
ocs_set_ccb_status(ccb, CAM_REQ_INVALID); | |||||
xpt_done(ccb); | |||||
} | |||||
break; | |||||
default: | |||||
device_printf(ocs->dev, "unhandled func_code = %#x\n", | |||||
ccb_h->func_code); | |||||
ccb_h->status = CAM_REQ_INVALID; | |||||
xpt_done(ccb); | |||||
break; | |||||
} | |||||
} | |||||
/** | |||||
* @ingroup cam_api | |||||
* @brief Process events | |||||
* | |||||
* @param sim pointer to the SCSI Interface Module | |||||
* | |||||
*/ | |||||
static void | |||||
ocs_poll(struct cam_sim *sim) | |||||
{ | |||||
printf("%s\n", __func__); | |||||
} | |||||
static int32_t | |||||
ocs_initiator_tmf_cb(ocs_io_t *io, ocs_scsi_io_status_e scsi_status, | |||||
ocs_scsi_cmd_resp_t *rsp, uint32_t flags, void *arg) | |||||
{ | |||||
int32_t rc = 0; | |||||
switch (scsi_status) { | |||||
case OCS_SCSI_STATUS_GOOD: | |||||
case OCS_SCSI_STATUS_NO_IO: | |||||
break; | |||||
case OCS_SCSI_STATUS_CHECK_RESPONSE: | |||||
if (rsp->response_data_length == 0) { | |||||
ocs_log_test(io->ocs, "check response without data?!?\n"); | |||||
rc = -1; | |||||
break; | |||||
} | |||||
if (rsp->response_data[3] != 0) { | |||||
ocs_log_test(io->ocs, "TMF status %08x\n", | |||||
be32toh(*((uint32_t *)rsp->response_data))); | |||||
rc = -1; | |||||
break; | |||||
} | |||||
break; | |||||
default: | |||||
ocs_log_test(io->ocs, "status=%#x\n", scsi_status); | |||||
rc = -1; | |||||
} | |||||
ocs_scsi_io_free(io); | |||||
return rc; | |||||
} | |||||
/** | |||||
* @brief lookup target resource structure | |||||
* | |||||
* Arbitrarily support | |||||
* - wildcard target ID + LU | |||||
* - 0 target ID + non-wildcard LU | |||||
* | |||||
* @param ocs the driver instance's software context | |||||
* @param ccb_h pointer to the CCB header | |||||
* @param status returned status value | |||||
* | |||||
* @return pointer to the target resource, NULL if none available (e.g. if LU | |||||
* is not enabled) | |||||
*/ | |||||
static ocs_tgt_resource_t *ocs_tgt_resource_get(struct ocs_softc *ocs, | |||||
struct ccb_hdr *ccb_h, uint32_t *status) | |||||
{ | |||||
target_id_t tid = ccb_h->target_id; | |||||
lun_id_t lun = ccb_h->target_lun; | |||||
if (CAM_TARGET_WILDCARD == tid) { | |||||
if (CAM_LUN_WILDCARD != lun) { | |||||
*status = CAM_LUN_INVALID; | |||||
return NULL; | |||||
} | |||||
return &ocs->targ_rsrc_wildcard; | |||||
} else { | |||||
if (lun < OCS_MAX_LUN) { | |||||
return &ocs->targ_rsrc[lun]; | |||||
} else { | |||||
*status = CAM_LUN_INVALID; | |||||
return NULL; | |||||
} | |||||
} | |||||
} | |||||
static int32_t | |||||
ocs_tgt_resource_abort(struct ocs_softc *ocs, ocs_tgt_resource_t *trsrc) | |||||
{ | |||||
Not Done Inline ActionsI suppose here should be called ocs_hw_flush() to allow kernel dumping on FC storage. mav: I suppose here should be called ocs_hw_flush() to allow kernel dumping on FC storage. | |||||
union ccb *ccb = NULL; | |||||
uint32_t count; | |||||
count = 0; | |||||
do { | |||||
ccb = (union ccb *)STAILQ_FIRST(&trsrc->atio); | |||||
if (ccb) { | |||||
STAILQ_REMOVE_HEAD(&trsrc->atio, sim_links.stqe); | |||||
ccb->ccb_h.status = CAM_REQ_ABORTED; | |||||
xpt_done(ccb); | |||||
count++; | |||||
} | |||||
} while (ccb); | |||||
count = 0; | |||||
do { | |||||
ccb = (union ccb *)STAILQ_FIRST(&trsrc->inot); | |||||
if (ccb) { | |||||
STAILQ_REMOVE_HEAD(&trsrc->inot, sim_links.stqe); | |||||
ccb->ccb_h.status = CAM_REQ_ABORTED; | |||||
xpt_done(ccb); | |||||
count++; | |||||
} | |||||
} while (ccb); | |||||
return 0; | |||||
} | |||||
static void | |||||
ocs_abort_atio(struct ocs_softc *ocs, union ccb *ccb) | |||||
{ | |||||
ocs_io_t *aio = NULL; | |||||
ocs_tgt_resource_t *trsrc = NULL; | |||||
uint32_t status = CAM_REQ_INVALID; | |||||
struct ccb_hdr *cur = NULL; | |||||
union ccb *accb = ccb->cab.abort_ccb; | |||||
trsrc = ocs_tgt_resource_get(ocs, &accb->ccb_h, &status); | |||||
if (trsrc != NULL) { | |||||
STAILQ_FOREACH(cur, &trsrc->atio, sim_links.stqe) { | |||||
if (cur != &ccb->ccb_h) | |||||
continue; | |||||
STAILQ_REMOVE(&trsrc->atio, cur, ccb_hdr, | |||||
sim_links.stqe); | |||||
accb->ccb_h.status = CAM_REQ_ABORTED; | |||||
xpt_done(accb); | |||||
ocs_set_ccb_status(ccb, CAM_REQ_CMP); | |||||
return; | |||||
} | |||||
} | |||||
/* if the ATIO has a valid IO pointer, CAM is telling | |||||
* the driver that the ATIO (which represents the entire | |||||
* exchange) has been aborted. */ | |||||
aio = accb->ccb_h.ccb_io_ptr; | |||||
if (aio == NULL) { | |||||
ccb->ccb_h.status = CAM_UA_ABORT; | |||||
return; | |||||
} | |||||
device_printf(ocs->dev, | |||||
"%s: XPT_ABORT ATIO state=%d tag=%#x" | |||||
" xid=%#x flags=%#x\n", __func__, | |||||
aio->tgt_io.state, aio->tag, | |||||
aio->init_task_tag, aio->tgt_io.flags); | |||||
/* Expectations are: | |||||
* - abort task was received | |||||
* - already aborted IO in the DEVICE | |||||
* - already received NOTIFY ACKNOWLEDGE */ | |||||
if ((aio->tgt_io.flags & OCS_CAM_IO_F_ABORT_RECV) == 0) | |||||
device_printf(ocs->dev, "%s: hinky state\n", __func__); | |||||
aio->tgt_io.flags |= OCS_CAM_IO_F_ABORT_CAM; | |||||
ocs_target_io_free(aio); | |||||
ocs_set_ccb_status(ccb, CAM_REQ_CMP); | |||||
return; | |||||
} | |||||
static void | |||||
ocs_abort_inot(struct ocs_softc *ocs, union ccb *ccb) | |||||
{ | |||||
ocs_tgt_resource_t *trsrc = NULL; | |||||
uint32_t status = CAM_REQ_INVALID; | |||||
struct ccb_hdr *cur = NULL; | |||||
union ccb *accb = ccb->cab.abort_ccb; | |||||
trsrc = ocs_tgt_resource_get(ocs, &accb->ccb_h, &status); | |||||
if (trsrc) { | |||||
STAILQ_FOREACH(cur, &trsrc->inot, sim_links.stqe) { | |||||
if (cur != &accb->ccb_h) | |||||
continue; | |||||
STAILQ_REMOVE(&trsrc->inot, cur, ccb_hdr, | |||||
sim_links.stqe); | |||||
accb->ccb_h.status = CAM_REQ_ABORTED; | |||||
xpt_done(accb); | |||||
ocs_set_ccb_status(ccb, CAM_REQ_CMP); | |||||
return; | |||||
} | |||||
} | |||||
ocs_set_ccb_status(ccb, CAM_UA_ABORT); | |||||
return; | |||||
} | |||||
static uint32_t | |||||
ocs_abort_initiator_io(struct ocs_softc *ocs, union ccb *accb) | |||||
{ | |||||
ocs_node_t *node = NULL; | |||||
ocs_io_t *io = NULL; | |||||
int32_t rc = 0; | |||||
struct ccb_scsiio *csio = &accb->csio; | |||||
node = ocs_node_get_instance(ocs, accb->ccb_h.target_id); | |||||
Not Done Inline ActionsThis is overly verbose for normal operation. mav: This is overly verbose for normal operation. | |||||
if (node == NULL) { | |||||
device_printf(ocs->dev, "%s: no device %d\n", | |||||
__func__, accb->ccb_h.target_id); | |||||
ocs_set_ccb_status(accb, CAM_DEV_NOT_THERE); | |||||
xpt_done(accb); | |||||
return (-1); | |||||
} | |||||
io = ocs_scsi_io_alloc(node, OCS_SCSI_IO_ROLE_ORIGINATOR); | |||||
if (io == NULL) { | |||||
device_printf(ocs->dev, | |||||
"%s: unable to alloc IO\n", __func__); | |||||
ocs_set_ccb_status(accb, CAM_REQ_CMP_ERR); | |||||
xpt_done(accb); | |||||
return (-1); | |||||
} | |||||
rc = ocs_scsi_send_tmf(node, io, | |||||
(ocs_io_t *)csio->ccb_h.ccb_io_ptr, | |||||
accb->ccb_h.target_lun, | |||||
OCS_SCSI_TMF_ABORT_TASK, | |||||
NULL, 0, 0, | |||||
ocs_initiator_tmf_cb, NULL/*arg*/); | |||||
return rc; | |||||
} | |||||
void | |||||
ocs_scsi_ini_ddump(ocs_textbuf_t *textbuf, ocs_scsi_ddump_type_e type, void *obj) | |||||
{ | |||||
switch(type) { | |||||
case OCS_SCSI_DDUMP_DEVICE: { | |||||
//ocs_t *ocs = obj; | |||||
break; | |||||
} | |||||
case OCS_SCSI_DDUMP_DOMAIN: { | |||||
//ocs_domain_t *domain = obj; | |||||
break; | |||||
} | |||||
case OCS_SCSI_DDUMP_SPORT: { | |||||
//ocs_sport_t *sport = obj; | |||||
break; | |||||
} | |||||
case OCS_SCSI_DDUMP_NODE: { | |||||
//ocs_node_t *node = obj; | |||||
break; | |||||
} | |||||
case OCS_SCSI_DDUMP_IO: { | |||||
//ocs_io_t *io = obj; | |||||
break; | |||||
} | |||||
default: { | |||||
break; | |||||
} | |||||
} | |||||
} | |||||
void | |||||
ocs_scsi_tgt_ddump(ocs_textbuf_t *textbuf, ocs_scsi_ddump_type_e type, void *obj) | |||||
{ | |||||
switch(type) { | |||||
case OCS_SCSI_DDUMP_DEVICE: { | |||||
//ocs_t *ocs = obj; | |||||
break; | |||||
} | |||||
case OCS_SCSI_DDUMP_DOMAIN: { | |||||
//ocs_domain_t *domain = obj; | |||||
break; | |||||
} | |||||
case OCS_SCSI_DDUMP_SPORT: { | |||||
//ocs_sport_t *sport = obj; | |||||
break; | |||||
} | |||||
case OCS_SCSI_DDUMP_NODE: { | |||||
//ocs_node_t *node = obj; | |||||
break; | |||||
} | |||||
case OCS_SCSI_DDUMP_IO: { | |||||
ocs_io_t *io = obj; | |||||
char *state_str = NULL; | |||||
switch (io->tgt_io.state) { | |||||
case OCS_CAM_IO_FREE: | |||||
state_str = "FREE"; | |||||
break; | |||||
case OCS_CAM_IO_COMMAND: | |||||
state_str = "COMMAND"; | |||||
break; | |||||
case OCS_CAM_IO_DATA: | |||||
state_str = "DATA"; | |||||
break; | |||||
case OCS_CAM_IO_DATA_DONE: | |||||
state_str = "DATA_DONE"; | |||||
break; | |||||
case OCS_CAM_IO_RESP: | |||||
state_str = "RESP"; | |||||
break; | |||||
default: | |||||
state_str = "xxx BAD xxx"; | |||||
} | |||||
ocs_ddump_value(textbuf, "cam_st", "%s", state_str); | |||||
if (io->tgt_io.app) { | |||||
ocs_ddump_value(textbuf, "cam_flags", "%#x", | |||||
((union ccb *)(io->tgt_io.app))->ccb_h.flags); | |||||
ocs_ddump_value(textbuf, "cam_status", "%#x", | |||||
((union ccb *)(io->tgt_io.app))->ccb_h.status); | |||||
} | |||||
break; | |||||
} | |||||
default: { | |||||
break; | |||||
} | |||||
} | |||||
} | |||||
int32_t ocs_scsi_get_block_vaddr(ocs_io_t *io, uint64_t blocknumber, | |||||
ocs_scsi_vaddr_len_t addrlen[], | |||||
uint32_t max_addrlen, void **dif_vaddr) | |||||
{ | |||||
return -1; | |||||
} | |||||
uint32_t | |||||
ocs_get_crn(ocs_node_t *node, uint8_t *crn, uint64_t lun) | |||||
{ | |||||
uint32_t idx; | |||||
struct ocs_lun_crn *lcrn = NULL; | |||||
idx = lun % OCS_MAX_LUN; | |||||
lcrn = node->ini_node.lun_crn[idx]; | |||||
if (lcrn == NULL) { | |||||
lcrn = ocs_malloc(node->ocs, sizeof(struct ocs_lun_crn), | |||||
M_ZERO|M_NOWAIT); | |||||
if (lcrn == NULL) { | |||||
return (1); | |||||
} | |||||
lcrn->lun = lun; | |||||
node->ini_node.lun_crn[idx] = lcrn; | |||||
} | |||||
if (lcrn->lun != lun) { | |||||
return (1); | |||||
} | |||||
if (lcrn->crnseed == 0) | |||||
lcrn->crnseed = 1; | |||||
*crn = lcrn->crnseed++; | |||||
return (0); | |||||
} | |||||
void | |||||
ocs_del_crn(ocs_node_t *node) | |||||
{ | |||||
uint32_t i; | |||||
struct ocs_lun_crn *lcrn = NULL; | |||||
for(i = 0; i < OCS_MAX_LUN; i++) { | |||||
lcrn = node->ini_node.lun_crn[i]; | |||||
if (lcrn) { | |||||
ocs_free(node->ocs, lcrn, sizeof(*lcrn)); | |||||
} | |||||
} | |||||
return; | |||||
} | |||||
void | |||||
ocs_reset_crn(ocs_node_t *node, uint64_t lun) | |||||
{ | |||||
uint32_t idx; | |||||
struct ocs_lun_crn *lcrn = NULL; | |||||
idx = lun % OCS_MAX_LUN; | |||||
lcrn = node->ini_node.lun_crn[idx]; | |||||
if (lcrn) | |||||
lcrn->crnseed = 0; | |||||
return; | |||||
} |
I suppose here you should notify CAM about initiator gone via xpt_async(AC_CONTRACT), same as above it should be notified for initiator arrival. It allows CTL to clean/update LUN state after initiator gone and/or returned.