/drivers/scsi/ibmvscsi/ibmvfc.c
C | 4360 lines | 2988 code | 489 blank | 883 comment | 400 complexity | ba1909d24f4fc5226a48c027ece93462 MD5 | raw file
Possible License(s): LGPL-2.0, AGPL-1.0, GPL-2.0
Large files files are truncated, but you can click here to view the full file
- /*
- * ibmvfc.c -- driver for IBM Power Virtual Fibre Channel Adapter
- *
- * Written By: Brian King <brking@linux.vnet.ibm.com>, IBM Corporation
- *
- * Copyright (C) IBM Corporation, 2008
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- */
- #include <linux/module.h>
- #include <linux/moduleparam.h>
- #include <linux/dma-mapping.h>
- #include <linux/dmapool.h>
- #include <linux/delay.h>
- #include <linux/interrupt.h>
- #include <linux/kthread.h>
- #include <linux/of.h>
- #include <linux/stringify.h>
- #include <asm/firmware.h>
- #include <asm/irq.h>
- #include <asm/vio.h>
- #include <scsi/scsi.h>
- #include <scsi/scsi_cmnd.h>
- #include <scsi/scsi_host.h>
- #include <scsi/scsi_device.h>
- #include <scsi/scsi_tcq.h>
- #include <scsi/scsi_transport_fc.h>
- #include "ibmvfc.h"
- static unsigned int init_timeout = IBMVFC_INIT_TIMEOUT;
- static unsigned int default_timeout = IBMVFC_DEFAULT_TIMEOUT;
- static unsigned int max_lun = IBMVFC_MAX_LUN;
- static unsigned int max_targets = IBMVFC_MAX_TARGETS;
- static unsigned int max_requests = IBMVFC_MAX_REQUESTS_DEFAULT;
- static unsigned int disc_threads = IBMVFC_MAX_DISC_THREADS;
- static unsigned int dev_loss_tmo = IBMVFC_DEV_LOSS_TMO;
- static unsigned int ibmvfc_debug = IBMVFC_DEBUG;
- static unsigned int log_level = IBMVFC_DEFAULT_LOG_LEVEL;
- static LIST_HEAD(ibmvfc_head);
- static DEFINE_SPINLOCK(ibmvfc_driver_lock);
- static struct scsi_transport_template *ibmvfc_transport_template;
- MODULE_DESCRIPTION("IBM Virtual Fibre Channel Driver");
- MODULE_AUTHOR("Brian King <brking@linux.vnet.ibm.com>");
- MODULE_LICENSE("GPL");
- MODULE_VERSION(IBMVFC_DRIVER_VERSION);
- module_param_named(init_timeout, init_timeout, uint, S_IRUGO | S_IWUSR);
- MODULE_PARM_DESC(init_timeout, "Initialization timeout in seconds. "
- "[Default=" __stringify(IBMVFC_INIT_TIMEOUT) "]");
- module_param_named(default_timeout, default_timeout, uint, S_IRUGO | S_IWUSR);
- MODULE_PARM_DESC(default_timeout,
- "Default timeout in seconds for initialization and EH commands. "
- "[Default=" __stringify(IBMVFC_DEFAULT_TIMEOUT) "]");
- module_param_named(max_requests, max_requests, uint, S_IRUGO);
- MODULE_PARM_DESC(max_requests, "Maximum requests for this adapter. "
- "[Default=" __stringify(IBMVFC_MAX_REQUESTS_DEFAULT) "]");
- module_param_named(max_lun, max_lun, uint, S_IRUGO);
- MODULE_PARM_DESC(max_lun, "Maximum allowed LUN. "
- "[Default=" __stringify(IBMVFC_MAX_LUN) "]");
- module_param_named(max_targets, max_targets, uint, S_IRUGO);
- MODULE_PARM_DESC(max_targets, "Maximum allowed targets. "
- "[Default=" __stringify(IBMVFC_MAX_TARGETS) "]");
- module_param_named(disc_threads, disc_threads, uint, S_IRUGO);
- MODULE_PARM_DESC(disc_threads, "Number of device discovery threads to use. "
- "[Default=" __stringify(IBMVFC_MAX_DISC_THREADS) "]");
- module_param_named(debug, ibmvfc_debug, uint, S_IRUGO | S_IWUSR);
- MODULE_PARM_DESC(debug, "Enable driver debug information. "
- "[Default=" __stringify(IBMVFC_DEBUG) "]");
- module_param_named(dev_loss_tmo, dev_loss_tmo, uint, S_IRUGO | S_IWUSR);
- MODULE_PARM_DESC(dev_loss_tmo, "Maximum number of seconds that the FC "
- "transport should insulate the loss of a remote port. Once this "
- "value is exceeded, the scsi target is removed. "
- "[Default=" __stringify(IBMVFC_DEV_LOSS_TMO) "]");
- module_param_named(log_level, log_level, uint, 0);
- MODULE_PARM_DESC(log_level, "Set to 0 - 4 for increasing verbosity of device driver. "
- "[Default=" __stringify(IBMVFC_DEFAULT_LOG_LEVEL) "]");
- static const struct {
- u16 status;
- u16 error;
- u8 result;
- u8 retry;
- int log;
- char *name;
- } cmd_status [] = {
- { IBMVFC_FABRIC_MAPPED, IBMVFC_UNABLE_TO_ESTABLISH, DID_ERROR, 1, 1, "unable to establish" },
- { IBMVFC_FABRIC_MAPPED, IBMVFC_XPORT_FAULT, DID_OK, 1, 0, "transport fault" },
- { IBMVFC_FABRIC_MAPPED, IBMVFC_CMD_TIMEOUT, DID_TIME_OUT, 1, 1, "command timeout" },
- { IBMVFC_FABRIC_MAPPED, IBMVFC_ENETDOWN, DID_TRANSPORT_DISRUPTED, 1, 1, "network down" },
- { IBMVFC_FABRIC_MAPPED, IBMVFC_HW_FAILURE, DID_ERROR, 1, 1, "hardware failure" },
- { IBMVFC_FABRIC_MAPPED, IBMVFC_LINK_DOWN_ERR, DID_REQUEUE, 0, 0, "link down" },
- { IBMVFC_FABRIC_MAPPED, IBMVFC_LINK_DEAD_ERR, DID_ERROR, 0, 0, "link dead" },
- { IBMVFC_FABRIC_MAPPED, IBMVFC_UNABLE_TO_REGISTER, DID_ERROR, 1, 1, "unable to register" },
- { IBMVFC_FABRIC_MAPPED, IBMVFC_XPORT_BUSY, DID_BUS_BUSY, 1, 0, "transport busy" },
- { IBMVFC_FABRIC_MAPPED, IBMVFC_XPORT_DEAD, DID_ERROR, 0, 1, "transport dead" },
- { IBMVFC_FABRIC_MAPPED, IBMVFC_CONFIG_ERROR, DID_ERROR, 1, 1, "configuration error" },
- { IBMVFC_FABRIC_MAPPED, IBMVFC_NAME_SERVER_FAIL, DID_ERROR, 1, 1, "name server failure" },
- { IBMVFC_FABRIC_MAPPED, IBMVFC_LINK_HALTED, DID_REQUEUE, 0, 0, "link halted" },
- { IBMVFC_FABRIC_MAPPED, IBMVFC_XPORT_GENERAL, DID_OK, 1, 0, "general transport error" },
- { IBMVFC_VIOS_FAILURE, IBMVFC_CRQ_FAILURE, DID_REQUEUE, 1, 1, "CRQ failure" },
- { IBMVFC_VIOS_FAILURE, IBMVFC_SW_FAILURE, DID_ERROR, 0, 1, "software failure" },
- { IBMVFC_VIOS_FAILURE, IBMVFC_INVALID_PARAMETER, DID_ERROR, 0, 1, "invalid parameter" },
- { IBMVFC_VIOS_FAILURE, IBMVFC_MISSING_PARAMETER, DID_ERROR, 0, 1, "missing parameter" },
- { IBMVFC_VIOS_FAILURE, IBMVFC_HOST_IO_BUS, DID_ERROR, 1, 1, "host I/O bus failure" },
- { IBMVFC_VIOS_FAILURE, IBMVFC_TRANS_CANCELLED, DID_ERROR, 0, 1, "transaction cancelled" },
- { IBMVFC_VIOS_FAILURE, IBMVFC_TRANS_CANCELLED_IMPLICIT, DID_ERROR, 0, 1, "transaction cancelled implicit" },
- { IBMVFC_VIOS_FAILURE, IBMVFC_INSUFFICIENT_RESOURCE, DID_REQUEUE, 1, 1, "insufficient resources" },
- { IBMVFC_VIOS_FAILURE, IBMVFC_PLOGI_REQUIRED, DID_ERROR, 0, 1, "port login required" },
- { IBMVFC_VIOS_FAILURE, IBMVFC_COMMAND_FAILED, DID_ERROR, 1, 1, "command failed" },
- { IBMVFC_FC_FAILURE, IBMVFC_INVALID_ELS_CMD_CODE, DID_ERROR, 0, 1, "invalid ELS command code" },
- { IBMVFC_FC_FAILURE, IBMVFC_INVALID_VERSION, DID_ERROR, 0, 1, "invalid version level" },
- { IBMVFC_FC_FAILURE, IBMVFC_LOGICAL_ERROR, DID_ERROR, 1, 1, "logical error" },
- { IBMVFC_FC_FAILURE, IBMVFC_INVALID_CT_IU_SIZE, DID_ERROR, 0, 1, "invalid CT_IU size" },
- { IBMVFC_FC_FAILURE, IBMVFC_LOGICAL_BUSY, DID_REQUEUE, 1, 0, "logical busy" },
- { IBMVFC_FC_FAILURE, IBMVFC_PROTOCOL_ERROR, DID_ERROR, 1, 1, "protocol error" },
- { IBMVFC_FC_FAILURE, IBMVFC_UNABLE_TO_PERFORM_REQ, DID_ERROR, 1, 1, "unable to perform request" },
- { IBMVFC_FC_FAILURE, IBMVFC_CMD_NOT_SUPPORTED, DID_ERROR, 0, 0, "command not supported" },
- { IBMVFC_FC_FAILURE, IBMVFC_SERVER_NOT_AVAIL, DID_ERROR, 0, 1, "server not available" },
- { IBMVFC_FC_FAILURE, IBMVFC_CMD_IN_PROGRESS, DID_ERROR, 0, 1, "command already in progress" },
- { IBMVFC_FC_FAILURE, IBMVFC_VENDOR_SPECIFIC, DID_ERROR, 1, 1, "vendor specific" },
- { IBMVFC_FC_SCSI_ERROR, 0, DID_OK, 1, 0, "SCSI error" },
- };
- static void ibmvfc_npiv_login(struct ibmvfc_host *);
- static void ibmvfc_tgt_send_prli(struct ibmvfc_target *);
- static void ibmvfc_tgt_send_plogi(struct ibmvfc_target *);
- static void ibmvfc_tgt_query_target(struct ibmvfc_target *);
- static const char *unknown_error = "unknown error";
- #ifdef CONFIG_SCSI_IBMVFC_TRACE
- /**
- * ibmvfc_trc_start - Log a start trace entry
- * @evt: ibmvfc event struct
- *
- **/
- static void ibmvfc_trc_start(struct ibmvfc_event *evt)
- {
- struct ibmvfc_host *vhost = evt->vhost;
- struct ibmvfc_cmd *vfc_cmd = &evt->iu.cmd;
- struct ibmvfc_mad_common *mad = &evt->iu.mad_common;
- struct ibmvfc_trace_entry *entry;
- entry = &vhost->trace[vhost->trace_index++];
- entry->evt = evt;
- entry->time = jiffies;
- entry->fmt = evt->crq.format;
- entry->type = IBMVFC_TRC_START;
- switch (entry->fmt) {
- case IBMVFC_CMD_FORMAT:
- entry->op_code = vfc_cmd->iu.cdb[0];
- entry->scsi_id = vfc_cmd->tgt_scsi_id;
- entry->lun = scsilun_to_int(&vfc_cmd->iu.lun);
- entry->tmf_flags = vfc_cmd->iu.tmf_flags;
- entry->u.start.xfer_len = vfc_cmd->iu.xfer_len;
- break;
- case IBMVFC_MAD_FORMAT:
- entry->op_code = mad->opcode;
- break;
- default:
- break;
- };
- }
- /**
- * ibmvfc_trc_end - Log an end trace entry
- * @evt: ibmvfc event struct
- *
- **/
- static void ibmvfc_trc_end(struct ibmvfc_event *evt)
- {
- struct ibmvfc_host *vhost = evt->vhost;
- struct ibmvfc_cmd *vfc_cmd = &evt->xfer_iu->cmd;
- struct ibmvfc_mad_common *mad = &evt->xfer_iu->mad_common;
- struct ibmvfc_trace_entry *entry = &vhost->trace[vhost->trace_index++];
- entry->evt = evt;
- entry->time = jiffies;
- entry->fmt = evt->crq.format;
- entry->type = IBMVFC_TRC_END;
- switch (entry->fmt) {
- case IBMVFC_CMD_FORMAT:
- entry->op_code = vfc_cmd->iu.cdb[0];
- entry->scsi_id = vfc_cmd->tgt_scsi_id;
- entry->lun = scsilun_to_int(&vfc_cmd->iu.lun);
- entry->tmf_flags = vfc_cmd->iu.tmf_flags;
- entry->u.end.status = vfc_cmd->status;
- entry->u.end.error = vfc_cmd->error;
- entry->u.end.fcp_rsp_flags = vfc_cmd->rsp.flags;
- entry->u.end.rsp_code = vfc_cmd->rsp.data.info.rsp_code;
- entry->u.end.scsi_status = vfc_cmd->rsp.scsi_status;
- break;
- case IBMVFC_MAD_FORMAT:
- entry->op_code = mad->opcode;
- entry->u.end.status = mad->status;
- break;
- default:
- break;
- };
- }
- #else
- #define ibmvfc_trc_start(evt) do { } while (0)
- #define ibmvfc_trc_end(evt) do { } while (0)
- #endif
- /**
- * ibmvfc_get_err_index - Find the index into cmd_status for the fcp response
- * @status: status / error class
- * @error: error
- *
- * Return value:
- * index into cmd_status / -EINVAL on failure
- **/
- static int ibmvfc_get_err_index(u16 status, u16 error)
- {
- int i;
- for (i = 0; i < ARRAY_SIZE(cmd_status); i++)
- if ((cmd_status[i].status & status) == cmd_status[i].status &&
- cmd_status[i].error == error)
- return i;
- return -EINVAL;
- }
- /**
- * ibmvfc_get_cmd_error - Find the error description for the fcp response
- * @status: status / error class
- * @error: error
- *
- * Return value:
- * error description string
- **/
- static const char *ibmvfc_get_cmd_error(u16 status, u16 error)
- {
- int rc = ibmvfc_get_err_index(status, error);
- if (rc >= 0)
- return cmd_status[rc].name;
- return unknown_error;
- }
- /**
- * ibmvfc_get_err_result - Find the scsi status to return for the fcp response
- * @vfc_cmd: ibmvfc command struct
- *
- * Return value:
- * SCSI result value to return for completed command
- **/
- static int ibmvfc_get_err_result(struct ibmvfc_cmd *vfc_cmd)
- {
- int err;
- struct ibmvfc_fcp_rsp *rsp = &vfc_cmd->rsp;
- int fc_rsp_len = rsp->fcp_rsp_len;
- if ((rsp->flags & FCP_RSP_LEN_VALID) &&
- ((!fc_rsp_len && fc_rsp_len != 4 && fc_rsp_len != 8) ||
- rsp->data.info.rsp_code))
- return DID_ERROR << 16;
- err = ibmvfc_get_err_index(vfc_cmd->status, vfc_cmd->error);
- if (err >= 0)
- return rsp->scsi_status | (cmd_status[err].result << 16);
- return rsp->scsi_status | (DID_ERROR << 16);
- }
- /**
- * ibmvfc_retry_cmd - Determine if error status is retryable
- * @status: status / error class
- * @error: error
- *
- * Return value:
- * 1 if error should be retried / 0 if it should not
- **/
- static int ibmvfc_retry_cmd(u16 status, u16 error)
- {
- int rc = ibmvfc_get_err_index(status, error);
- if (rc >= 0)
- return cmd_status[rc].retry;
- return 1;
- }
- static const char *unknown_fc_explain = "unknown fc explain";
- static const struct {
- u16 fc_explain;
- char *name;
- } ls_explain [] = {
- { 0x00, "no additional explanation" },
- { 0x01, "service parameter error - options" },
- { 0x03, "service parameter error - initiator control" },
- { 0x05, "service parameter error - recipient control" },
- { 0x07, "service parameter error - received data field size" },
- { 0x09, "service parameter error - concurrent seq" },
- { 0x0B, "service parameter error - credit" },
- { 0x0D, "invalid N_Port/F_Port_Name" },
- { 0x0E, "invalid node/Fabric Name" },
- { 0x0F, "invalid common service parameters" },
- { 0x11, "invalid association header" },
- { 0x13, "association header required" },
- { 0x15, "invalid originator S_ID" },
- { 0x17, "invalid OX_ID-RX-ID combination" },
- { 0x19, "command (request) already in progress" },
- { 0x1E, "N_Port Login requested" },
- { 0x1F, "Invalid N_Port_ID" },
- };
- static const struct {
- u16 fc_explain;
- char *name;
- } gs_explain [] = {
- { 0x00, "no additional explanation" },
- { 0x01, "port identifier not registered" },
- { 0x02, "port name not registered" },
- { 0x03, "node name not registered" },
- { 0x04, "class of service not registered" },
- { 0x06, "initial process associator not registered" },
- { 0x07, "FC-4 TYPEs not registered" },
- { 0x08, "symbolic port name not registered" },
- { 0x09, "symbolic node name not registered" },
- { 0x0A, "port type not registered" },
- { 0xF0, "authorization exception" },
- { 0xF1, "authentication exception" },
- { 0xF2, "data base full" },
- { 0xF3, "data base empty" },
- { 0xF4, "processing request" },
- { 0xF5, "unable to verify connection" },
- { 0xF6, "devices not in a common zone" },
- };
- /**
- * ibmvfc_get_ls_explain - Return the FC Explain description text
- * @status: FC Explain status
- *
- * Returns:
- * error string
- **/
- static const char *ibmvfc_get_ls_explain(u16 status)
- {
- int i;
- for (i = 0; i < ARRAY_SIZE(ls_explain); i++)
- if (ls_explain[i].fc_explain == status)
- return ls_explain[i].name;
- return unknown_fc_explain;
- }
- /**
- * ibmvfc_get_gs_explain - Return the FC Explain description text
- * @status: FC Explain status
- *
- * Returns:
- * error string
- **/
- static const char *ibmvfc_get_gs_explain(u16 status)
- {
- int i;
- for (i = 0; i < ARRAY_SIZE(gs_explain); i++)
- if (gs_explain[i].fc_explain == status)
- return gs_explain[i].name;
- return unknown_fc_explain;
- }
- static const struct {
- enum ibmvfc_fc_type fc_type;
- char *name;
- } fc_type [] = {
- { IBMVFC_FABRIC_REJECT, "fabric reject" },
- { IBMVFC_PORT_REJECT, "port reject" },
- { IBMVFC_LS_REJECT, "ELS reject" },
- { IBMVFC_FABRIC_BUSY, "fabric busy" },
- { IBMVFC_PORT_BUSY, "port busy" },
- { IBMVFC_BASIC_REJECT, "basic reject" },
- };
- static const char *unknown_fc_type = "unknown fc type";
- /**
- * ibmvfc_get_fc_type - Return the FC Type description text
- * @status: FC Type error status
- *
- * Returns:
- * error string
- **/
- static const char *ibmvfc_get_fc_type(u16 status)
- {
- int i;
- for (i = 0; i < ARRAY_SIZE(fc_type); i++)
- if (fc_type[i].fc_type == status)
- return fc_type[i].name;
- return unknown_fc_type;
- }
- /**
- * ibmvfc_set_tgt_action - Set the next init action for the target
- * @tgt: ibmvfc target struct
- * @action: action to perform
- *
- **/
- static void ibmvfc_set_tgt_action(struct ibmvfc_target *tgt,
- enum ibmvfc_target_action action)
- {
- switch (tgt->action) {
- case IBMVFC_TGT_ACTION_DEL_RPORT:
- break;
- default:
- tgt->action = action;
- break;
- }
- }
- /**
- * ibmvfc_set_host_state - Set the state for the host
- * @vhost: ibmvfc host struct
- * @state: state to set host to
- *
- * Returns:
- * 0 if state changed / non-zero if not changed
- **/
- static int ibmvfc_set_host_state(struct ibmvfc_host *vhost,
- enum ibmvfc_host_state state)
- {
- int rc = 0;
- switch (vhost->state) {
- case IBMVFC_HOST_OFFLINE:
- rc = -EINVAL;
- break;
- default:
- vhost->state = state;
- break;
- };
- return rc;
- }
- /**
- * ibmvfc_set_host_action - Set the next init action for the host
- * @vhost: ibmvfc host struct
- * @action: action to perform
- *
- **/
- static void ibmvfc_set_host_action(struct ibmvfc_host *vhost,
- enum ibmvfc_host_action action)
- {
- switch (action) {
- case IBMVFC_HOST_ACTION_ALLOC_TGTS:
- if (vhost->action == IBMVFC_HOST_ACTION_INIT_WAIT)
- vhost->action = action;
- break;
- case IBMVFC_HOST_ACTION_INIT_WAIT:
- if (vhost->action == IBMVFC_HOST_ACTION_INIT)
- vhost->action = action;
- break;
- case IBMVFC_HOST_ACTION_QUERY:
- switch (vhost->action) {
- case IBMVFC_HOST_ACTION_INIT_WAIT:
- case IBMVFC_HOST_ACTION_NONE:
- case IBMVFC_HOST_ACTION_TGT_ADD:
- vhost->action = action;
- break;
- default:
- break;
- };
- break;
- case IBMVFC_HOST_ACTION_TGT_INIT:
- if (vhost->action == IBMVFC_HOST_ACTION_ALLOC_TGTS)
- vhost->action = action;
- break;
- case IBMVFC_HOST_ACTION_INIT:
- case IBMVFC_HOST_ACTION_TGT_DEL:
- case IBMVFC_HOST_ACTION_QUERY_TGTS:
- case IBMVFC_HOST_ACTION_TGT_DEL_FAILED:
- case IBMVFC_HOST_ACTION_TGT_ADD:
- case IBMVFC_HOST_ACTION_NONE:
- default:
- vhost->action = action;
- break;
- };
- }
- /**
- * ibmvfc_reinit_host - Re-start host initialization (no NPIV Login)
- * @vhost: ibmvfc host struct
- *
- * Return value:
- * nothing
- **/
- static void ibmvfc_reinit_host(struct ibmvfc_host *vhost)
- {
- if (vhost->action == IBMVFC_HOST_ACTION_NONE) {
- if (!ibmvfc_set_host_state(vhost, IBMVFC_INITIALIZING)) {
- scsi_block_requests(vhost->host);
- ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_QUERY);
- }
- } else
- vhost->reinit = 1;
- wake_up(&vhost->work_wait_q);
- }
- /**
- * ibmvfc_link_down - Handle a link down event from the adapter
- * @vhost: ibmvfc host struct
- * @state: ibmvfc host state to enter
- *
- **/
- static void ibmvfc_link_down(struct ibmvfc_host *vhost,
- enum ibmvfc_host_state state)
- {
- struct ibmvfc_target *tgt;
- ENTER;
- scsi_block_requests(vhost->host);
- list_for_each_entry(tgt, &vhost->targets, queue)
- ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
- ibmvfc_set_host_state(vhost, state);
- ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_TGT_DEL);
- vhost->events_to_log |= IBMVFC_AE_LINKDOWN;
- wake_up(&vhost->work_wait_q);
- LEAVE;
- }
- /**
- * ibmvfc_init_host - Start host initialization
- * @vhost: ibmvfc host struct
- * @relogin: is this a re-login?
- *
- * Return value:
- * nothing
- **/
- static void ibmvfc_init_host(struct ibmvfc_host *vhost, int relogin)
- {
- struct ibmvfc_target *tgt;
- if (vhost->action == IBMVFC_HOST_ACTION_INIT_WAIT) {
- if (++vhost->init_retries > IBMVFC_MAX_HOST_INIT_RETRIES) {
- dev_err(vhost->dev,
- "Host initialization retries exceeded. Taking adapter offline\n");
- ibmvfc_link_down(vhost, IBMVFC_HOST_OFFLINE);
- return;
- }
- }
- if (!ibmvfc_set_host_state(vhost, IBMVFC_INITIALIZING)) {
- if (!relogin) {
- memset(vhost->async_crq.msgs, 0, PAGE_SIZE);
- vhost->async_crq.cur = 0;
- }
- list_for_each_entry(tgt, &vhost->targets, queue)
- tgt->need_login = 1;
- scsi_block_requests(vhost->host);
- ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_INIT);
- vhost->job_step = ibmvfc_npiv_login;
- wake_up(&vhost->work_wait_q);
- }
- }
- /**
- * ibmvfc_send_crq - Send a CRQ
- * @vhost: ibmvfc host struct
- * @word1: the first 64 bits of the data
- * @word2: the second 64 bits of the data
- *
- * Return value:
- * 0 on success / other on failure
- **/
- static int ibmvfc_send_crq(struct ibmvfc_host *vhost, u64 word1, u64 word2)
- {
- struct vio_dev *vdev = to_vio_dev(vhost->dev);
- return plpar_hcall_norets(H_SEND_CRQ, vdev->unit_address, word1, word2);
- }
- /**
- * ibmvfc_send_crq_init - Send a CRQ init message
- * @vhost: ibmvfc host struct
- *
- * Return value:
- * 0 on success / other on failure
- **/
- static int ibmvfc_send_crq_init(struct ibmvfc_host *vhost)
- {
- ibmvfc_dbg(vhost, "Sending CRQ init\n");
- return ibmvfc_send_crq(vhost, 0xC001000000000000LL, 0);
- }
- /**
- * ibmvfc_send_crq_init_complete - Send a CRQ init complete message
- * @vhost: ibmvfc host struct
- *
- * Return value:
- * 0 on success / other on failure
- **/
- static int ibmvfc_send_crq_init_complete(struct ibmvfc_host *vhost)
- {
- ibmvfc_dbg(vhost, "Sending CRQ init complete\n");
- return ibmvfc_send_crq(vhost, 0xC002000000000000LL, 0);
- }
- /**
- * ibmvfc_release_crq_queue - Deallocates data and unregisters CRQ
- * @vhost: ibmvfc host struct
- *
- * Frees irq, deallocates a page for messages, unmaps dma, and unregisters
- * the crq with the hypervisor.
- **/
- static void ibmvfc_release_crq_queue(struct ibmvfc_host *vhost)
- {
- long rc;
- struct vio_dev *vdev = to_vio_dev(vhost->dev);
- struct ibmvfc_crq_queue *crq = &vhost->crq;
- ibmvfc_dbg(vhost, "Releasing CRQ\n");
- free_irq(vdev->irq, vhost);
- tasklet_kill(&vhost->tasklet);
- do {
- rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address);
- } while (rc == H_BUSY || H_IS_LONG_BUSY(rc));
- vhost->state = IBMVFC_NO_CRQ;
- dma_unmap_single(vhost->dev, crq->msg_token, PAGE_SIZE, DMA_BIDIRECTIONAL);
- free_page((unsigned long)crq->msgs);
- }
- /**
- * ibmvfc_reenable_crq_queue - reenables the CRQ
- * @vhost: ibmvfc host struct
- *
- * Return value:
- * 0 on success / other on failure
- **/
- static int ibmvfc_reenable_crq_queue(struct ibmvfc_host *vhost)
- {
- int rc;
- struct vio_dev *vdev = to_vio_dev(vhost->dev);
- /* Re-enable the CRQ */
- do {
- rc = plpar_hcall_norets(H_ENABLE_CRQ, vdev->unit_address);
- } while (rc == H_IN_PROGRESS || rc == H_BUSY || H_IS_LONG_BUSY(rc));
- if (rc)
- dev_err(vhost->dev, "Error enabling adapter (rc=%d)\n", rc);
- return rc;
- }
- /**
- * ibmvfc_reset_crq - resets a crq after a failure
- * @vhost: ibmvfc host struct
- *
- * Return value:
- * 0 on success / other on failure
- **/
- static int ibmvfc_reset_crq(struct ibmvfc_host *vhost)
- {
- int rc;
- struct vio_dev *vdev = to_vio_dev(vhost->dev);
- struct ibmvfc_crq_queue *crq = &vhost->crq;
- /* Close the CRQ */
- do {
- rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address);
- } while (rc == H_BUSY || H_IS_LONG_BUSY(rc));
- vhost->state = IBMVFC_NO_CRQ;
- ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_NONE);
- /* Clean out the queue */
- memset(crq->msgs, 0, PAGE_SIZE);
- crq->cur = 0;
- /* And re-open it again */
- rc = plpar_hcall_norets(H_REG_CRQ, vdev->unit_address,
- crq->msg_token, PAGE_SIZE);
- if (rc == H_CLOSED)
- /* Adapter is good, but other end is not ready */
- dev_warn(vhost->dev, "Partner adapter not ready\n");
- else if (rc != 0)
- dev_warn(vhost->dev, "Couldn't register crq (rc=%d)\n", rc);
- return rc;
- }
- /**
- * ibmvfc_valid_event - Determines if event is valid.
- * @pool: event_pool that contains the event
- * @evt: ibmvfc event to be checked for validity
- *
- * Return value:
- * 1 if event is valid / 0 if event is not valid
- **/
- static int ibmvfc_valid_event(struct ibmvfc_event_pool *pool,
- struct ibmvfc_event *evt)
- {
- int index = evt - pool->events;
- if (index < 0 || index >= pool->size) /* outside of bounds */
- return 0;
- if (evt != pool->events + index) /* unaligned */
- return 0;
- return 1;
- }
- /**
- * ibmvfc_free_event - Free the specified event
- * @evt: ibmvfc_event to be freed
- *
- **/
- static void ibmvfc_free_event(struct ibmvfc_event *evt)
- {
- struct ibmvfc_host *vhost = evt->vhost;
- struct ibmvfc_event_pool *pool = &vhost->pool;
- BUG_ON(!ibmvfc_valid_event(pool, evt));
- BUG_ON(atomic_inc_return(&evt->free) != 1);
- list_add_tail(&evt->queue, &vhost->free);
- }
- /**
- * ibmvfc_scsi_eh_done - EH done function for queuecommand commands
- * @evt: ibmvfc event struct
- *
- * This function does not setup any error status, that must be done
- * before this function gets called.
- **/
- static void ibmvfc_scsi_eh_done(struct ibmvfc_event *evt)
- {
- struct scsi_cmnd *cmnd = evt->cmnd;
- if (cmnd) {
- scsi_dma_unmap(cmnd);
- cmnd->scsi_done(cmnd);
- }
- if (evt->eh_comp)
- complete(evt->eh_comp);
- ibmvfc_free_event(evt);
- }
- /**
- * ibmvfc_fail_request - Fail request with specified error code
- * @evt: ibmvfc event struct
- * @error_code: error code to fail request with
- *
- * Return value:
- * none
- **/
- static void ibmvfc_fail_request(struct ibmvfc_event *evt, int error_code)
- {
- if (evt->cmnd) {
- evt->cmnd->result = (error_code << 16);
- evt->done = ibmvfc_scsi_eh_done;
- } else
- evt->xfer_iu->mad_common.status = IBMVFC_MAD_DRIVER_FAILED;
- list_del(&evt->queue);
- del_timer(&evt->timer);
- ibmvfc_trc_end(evt);
- evt->done(evt);
- }
- /**
- * ibmvfc_purge_requests - Our virtual adapter just shut down. Purge any sent requests
- * @vhost: ibmvfc host struct
- * @error_code: error code to fail requests with
- *
- * Return value:
- * none
- **/
- static void ibmvfc_purge_requests(struct ibmvfc_host *vhost, int error_code)
- {
- struct ibmvfc_event *evt, *pos;
- ibmvfc_dbg(vhost, "Purging all requests\n");
- list_for_each_entry_safe(evt, pos, &vhost->sent, queue)
- ibmvfc_fail_request(evt, error_code);
- }
- /**
- * __ibmvfc_reset_host - Reset the connection to the server (no locking)
- * @vhost: struct ibmvfc host to reset
- **/
- static void __ibmvfc_reset_host(struct ibmvfc_host *vhost)
- {
- int rc;
- scsi_block_requests(vhost->host);
- ibmvfc_purge_requests(vhost, DID_ERROR);
- if ((rc = ibmvfc_reset_crq(vhost)) ||
- (rc = ibmvfc_send_crq_init(vhost)) ||
- (rc = vio_enable_interrupts(to_vio_dev(vhost->dev)))) {
- dev_err(vhost->dev, "Error after reset rc=%d\n", rc);
- ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
- } else
- ibmvfc_link_down(vhost, IBMVFC_LINK_DOWN);
- }
- /**
- * ibmvfc_reset_host - Reset the connection to the server
- * @vhost: struct ibmvfc host to reset
- **/
- static void ibmvfc_reset_host(struct ibmvfc_host *vhost)
- {
- unsigned long flags;
- spin_lock_irqsave(vhost->host->host_lock, flags);
- __ibmvfc_reset_host(vhost);
- spin_unlock_irqrestore(vhost->host->host_lock, flags);
- }
- /**
- * ibmvfc_retry_host_init - Retry host initialization if allowed
- * @vhost: ibmvfc host struct
- *
- **/
- static void ibmvfc_retry_host_init(struct ibmvfc_host *vhost)
- {
- if (vhost->action == IBMVFC_HOST_ACTION_INIT_WAIT) {
- vhost->delay_init = 1;
- if (++vhost->init_retries > IBMVFC_MAX_HOST_INIT_RETRIES) {
- dev_err(vhost->dev,
- "Host initialization retries exceeded. Taking adapter offline\n");
- ibmvfc_link_down(vhost, IBMVFC_HOST_OFFLINE);
- } else if (vhost->init_retries == IBMVFC_MAX_HOST_INIT_RETRIES)
- __ibmvfc_reset_host(vhost);
- else
- ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_INIT);
- }
- wake_up(&vhost->work_wait_q);
- }
- /**
- * __ibmvfc_get_target - Find the specified scsi_target (no locking)
- * @starget: scsi target struct
- *
- * Return value:
- * ibmvfc_target struct / NULL if not found
- **/
- static struct ibmvfc_target *__ibmvfc_get_target(struct scsi_target *starget)
- {
- struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
- struct ibmvfc_host *vhost = shost_priv(shost);
- struct ibmvfc_target *tgt;
- list_for_each_entry(tgt, &vhost->targets, queue)
- if (tgt->target_id == starget->id) {
- kref_get(&tgt->kref);
- return tgt;
- }
- return NULL;
- }
- /**
- * ibmvfc_get_target - Find the specified scsi_target
- * @starget: scsi target struct
- *
- * Return value:
- * ibmvfc_target struct / NULL if not found
- **/
- static struct ibmvfc_target *ibmvfc_get_target(struct scsi_target *starget)
- {
- struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
- struct ibmvfc_target *tgt;
- unsigned long flags;
- spin_lock_irqsave(shost->host_lock, flags);
- tgt = __ibmvfc_get_target(starget);
- spin_unlock_irqrestore(shost->host_lock, flags);
- return tgt;
- }
- /**
- * ibmvfc_get_host_speed - Get host port speed
- * @shost: scsi host struct
- *
- * Return value:
- * none
- **/
- static void ibmvfc_get_host_speed(struct Scsi_Host *shost)
- {
- struct ibmvfc_host *vhost = shost_priv(shost);
- unsigned long flags;
- spin_lock_irqsave(shost->host_lock, flags);
- if (vhost->state == IBMVFC_ACTIVE) {
- switch (vhost->login_buf->resp.link_speed / 100) {
- case 1:
- fc_host_speed(shost) = FC_PORTSPEED_1GBIT;
- break;
- case 2:
- fc_host_speed(shost) = FC_PORTSPEED_2GBIT;
- break;
- case 4:
- fc_host_speed(shost) = FC_PORTSPEED_4GBIT;
- break;
- case 8:
- fc_host_speed(shost) = FC_PORTSPEED_8GBIT;
- break;
- case 10:
- fc_host_speed(shost) = FC_PORTSPEED_10GBIT;
- break;
- case 16:
- fc_host_speed(shost) = FC_PORTSPEED_16GBIT;
- break;
- default:
- ibmvfc_log(vhost, 3, "Unknown port speed: %lld Gbit\n",
- vhost->login_buf->resp.link_speed / 100);
- fc_host_speed(shost) = FC_PORTSPEED_UNKNOWN;
- break;
- }
- } else
- fc_host_speed(shost) = FC_PORTSPEED_UNKNOWN;
- spin_unlock_irqrestore(shost->host_lock, flags);
- }
- /**
- * ibmvfc_get_host_port_state - Get host port state
- * @shost: scsi host struct
- *
- * Return value:
- * none
- **/
- static void ibmvfc_get_host_port_state(struct Scsi_Host *shost)
- {
- struct ibmvfc_host *vhost = shost_priv(shost);
- unsigned long flags;
- spin_lock_irqsave(shost->host_lock, flags);
- switch (vhost->state) {
- case IBMVFC_INITIALIZING:
- case IBMVFC_ACTIVE:
- fc_host_port_state(shost) = FC_PORTSTATE_ONLINE;
- break;
- case IBMVFC_LINK_DOWN:
- fc_host_port_state(shost) = FC_PORTSTATE_LINKDOWN;
- break;
- case IBMVFC_LINK_DEAD:
- case IBMVFC_HOST_OFFLINE:
- fc_host_port_state(shost) = FC_PORTSTATE_OFFLINE;
- break;
- case IBMVFC_HALTED:
- fc_host_port_state(shost) = FC_PORTSTATE_BLOCKED;
- break;
- case IBMVFC_NO_CRQ:
- fc_host_port_state(shost) = FC_PORTSTATE_UNKNOWN;
- break;
- default:
- ibmvfc_log(vhost, 3, "Unknown port state: %d\n", vhost->state);
- fc_host_port_state(shost) = FC_PORTSTATE_UNKNOWN;
- break;
- }
- spin_unlock_irqrestore(shost->host_lock, flags);
- }
- /**
- * ibmvfc_set_rport_dev_loss_tmo - Set rport's device loss timeout
- * @rport: rport struct
- * @timeout: timeout value
- *
- * Return value:
- * none
- **/
- static void ibmvfc_set_rport_dev_loss_tmo(struct fc_rport *rport, u32 timeout)
- {
- if (timeout)
- rport->dev_loss_tmo = timeout;
- else
- rport->dev_loss_tmo = 1;
- }
- /**
- * ibmvfc_release_tgt - Free memory allocated for a target
- * @kref: kref struct
- *
- **/
- static void ibmvfc_release_tgt(struct kref *kref)
- {
- struct ibmvfc_target *tgt = container_of(kref, struct ibmvfc_target, kref);
- kfree(tgt);
- }
- /**
- * ibmvfc_get_starget_node_name - Get SCSI target's node name
- * @starget: scsi target struct
- *
- * Return value:
- * none
- **/
- static void ibmvfc_get_starget_node_name(struct scsi_target *starget)
- {
- struct ibmvfc_target *tgt = ibmvfc_get_target(starget);
- fc_starget_port_name(starget) = tgt ? tgt->ids.node_name : 0;
- if (tgt)
- kref_put(&tgt->kref, ibmvfc_release_tgt);
- }
- /**
- * ibmvfc_get_starget_port_name - Get SCSI target's port name
- * @starget: scsi target struct
- *
- * Return value:
- * none
- **/
- static void ibmvfc_get_starget_port_name(struct scsi_target *starget)
- {
- struct ibmvfc_target *tgt = ibmvfc_get_target(starget);
- fc_starget_port_name(starget) = tgt ? tgt->ids.port_name : 0;
- if (tgt)
- kref_put(&tgt->kref, ibmvfc_release_tgt);
- }
- /**
- * ibmvfc_get_starget_port_id - Get SCSI target's port ID
- * @starget: scsi target struct
- *
- * Return value:
- * none
- **/
- static void ibmvfc_get_starget_port_id(struct scsi_target *starget)
- {
- struct ibmvfc_target *tgt = ibmvfc_get_target(starget);
- fc_starget_port_id(starget) = tgt ? tgt->scsi_id : -1;
- if (tgt)
- kref_put(&tgt->kref, ibmvfc_release_tgt);
- }
- /**
- * ibmvfc_wait_while_resetting - Wait while the host resets
- * @vhost: ibmvfc host struct
- *
- * Return value:
- * 0 on success / other on failure
- **/
- static int ibmvfc_wait_while_resetting(struct ibmvfc_host *vhost)
- {
- long timeout = wait_event_timeout(vhost->init_wait_q,
- ((vhost->state == IBMVFC_ACTIVE ||
- vhost->state == IBMVFC_HOST_OFFLINE ||
- vhost->state == IBMVFC_LINK_DEAD) &&
- vhost->action == IBMVFC_HOST_ACTION_NONE),
- (init_timeout * HZ));
- return timeout ? 0 : -EIO;
- }
- /**
- * ibmvfc_issue_fc_host_lip - Re-initiate link initialization
- * @shost: scsi host struct
- *
- * Return value:
- * 0 on success / other on failure
- **/
- static int ibmvfc_issue_fc_host_lip(struct Scsi_Host *shost)
- {
- struct ibmvfc_host *vhost = shost_priv(shost);
- dev_err(vhost->dev, "Initiating host LIP. Resetting connection\n");
- ibmvfc_reset_host(vhost);
- return ibmvfc_wait_while_resetting(vhost);
- }
- /**
- * ibmvfc_gather_partition_info - Gather info about the LPAR
- *
- * Return value:
- * none
- **/
- static void ibmvfc_gather_partition_info(struct ibmvfc_host *vhost)
- {
- struct device_node *rootdn;
- const char *name;
- const unsigned int *num;
- rootdn = of_find_node_by_path("/");
- if (!rootdn)
- return;
- name = of_get_property(rootdn, "ibm,partition-name", NULL);
- if (name)
- strncpy(vhost->partition_name, name, sizeof(vhost->partition_name));
- num = of_get_property(rootdn, "ibm,partition-no", NULL);
- if (num)
- vhost->partition_number = *num;
- of_node_put(rootdn);
- }
- /**
- * ibmvfc_set_login_info - Setup info for NPIV login
- * @vhost: ibmvfc host struct
- *
- * Return value:
- * none
- **/
- static void ibmvfc_set_login_info(struct ibmvfc_host *vhost)
- {
- struct ibmvfc_npiv_login *login_info = &vhost->login_info;
- struct device_node *of_node = vhost->dev->archdata.of_node;
- const char *location;
- memset(login_info, 0, sizeof(*login_info));
- login_info->ostype = IBMVFC_OS_LINUX;
- login_info->max_dma_len = IBMVFC_MAX_SECTORS << 9;
- login_info->max_payload = sizeof(struct ibmvfc_fcp_cmd_iu);
- login_info->max_response = sizeof(struct ibmvfc_fcp_rsp);
- login_info->partition_num = vhost->partition_number;
- login_info->vfc_frame_version = 1;
- login_info->fcp_version = 3;
- if (vhost->client_migrated)
- login_info->flags = IBMVFC_CLIENT_MIGRATED;
- login_info->max_cmds = max_requests + IBMVFC_NUM_INTERNAL_REQ;
- login_info->capabilities = IBMVFC_CAN_MIGRATE;
- login_info->async.va = vhost->async_crq.msg_token;
- login_info->async.len = vhost->async_crq.size * sizeof(*vhost->async_crq.msgs);
- strncpy(login_info->partition_name, vhost->partition_name, IBMVFC_MAX_NAME);
- strncpy(login_info->device_name,
- dev_name(&vhost->host->shost_gendev), IBMVFC_MAX_NAME);
- location = of_get_property(of_node, "ibm,loc-code", NULL);
- location = location ? location : dev_name(vhost->dev);
- strncpy(login_info->drc_name, location, IBMVFC_MAX_NAME);
- }
- /**
- * ibmvfc_init_event_pool - Allocates and initializes the event pool for a host
- * @vhost: ibmvfc host who owns the event pool
- *
- * Returns zero on success.
- **/
- static int ibmvfc_init_event_pool(struct ibmvfc_host *vhost)
- {
- int i;
- struct ibmvfc_event_pool *pool = &vhost->pool;
- ENTER;
- pool->size = max_requests + IBMVFC_NUM_INTERNAL_REQ;
- pool->events = kcalloc(pool->size, sizeof(*pool->events), GFP_KERNEL);
- if (!pool->events)
- return -ENOMEM;
- pool->iu_storage = dma_alloc_coherent(vhost->dev,
- pool->size * sizeof(*pool->iu_storage),
- &pool->iu_token, 0);
- if (!pool->iu_storage) {
- kfree(pool->events);
- return -ENOMEM;
- }
- for (i = 0; i < pool->size; ++i) {
- struct ibmvfc_event *evt = &pool->events[i];
- atomic_set(&evt->free, 1);
- evt->crq.valid = 0x80;
- evt->crq.ioba = pool->iu_token + (sizeof(*evt->xfer_iu) * i);
- evt->xfer_iu = pool->iu_storage + i;
- evt->vhost = vhost;
- evt->ext_list = NULL;
- list_add_tail(&evt->queue, &vhost->free);
- }
- LEAVE;
- return 0;
- }
- /**
- * ibmvfc_free_event_pool - Frees memory of the event pool of a host
- * @vhost: ibmvfc host who owns the event pool
- *
- **/
- static void ibmvfc_free_event_pool(struct ibmvfc_host *vhost)
- {
- int i;
- struct ibmvfc_event_pool *pool = &vhost->pool;
- ENTER;
- for (i = 0; i < pool->size; ++i) {
- list_del(&pool->events[i].queue);
- BUG_ON(atomic_read(&pool->events[i].free) != 1);
- if (pool->events[i].ext_list)
- dma_pool_free(vhost->sg_pool,
- pool->events[i].ext_list,
- pool->events[i].ext_list_token);
- }
- kfree(pool->events);
- dma_free_coherent(vhost->dev,
- pool->size * sizeof(*pool->iu_storage),
- pool->iu_storage, pool->iu_token);
- LEAVE;
- }
- /**
- * ibmvfc_get_event - Gets the next free event in pool
- * @vhost: ibmvfc host struct
- *
- * Returns a free event from the pool.
- **/
- static struct ibmvfc_event *ibmvfc_get_event(struct ibmvfc_host *vhost)
- {
- struct ibmvfc_event *evt;
- BUG_ON(list_empty(&vhost->free));
- evt = list_entry(vhost->free.next, struct ibmvfc_event, queue);
- atomic_set(&evt->free, 0);
- list_del(&evt->queue);
- return evt;
- }
- /**
- * ibmvfc_init_event - Initialize fields in an event struct that are always
- * required.
- * @evt: The event
- * @done: Routine to call when the event is responded to
- * @format: SRP or MAD format
- **/
- static void ibmvfc_init_event(struct ibmvfc_event *evt,
- void (*done) (struct ibmvfc_event *), u8 format)
- {
- evt->cmnd = NULL;
- evt->sync_iu = NULL;
- evt->crq.format = format;
- evt->done = done;
- evt->eh_comp = NULL;
- }
- /**
- * ibmvfc_map_sg_list - Initialize scatterlist
- * @scmd: scsi command struct
- * @nseg: number of scatterlist segments
- * @md: memory descriptor list to initialize
- **/
- static void ibmvfc_map_sg_list(struct scsi_cmnd *scmd, int nseg,
- struct srp_direct_buf *md)
- {
- int i;
- struct scatterlist *sg;
- scsi_for_each_sg(scmd, sg, nseg, i) {
- md[i].va = sg_dma_address(sg);
- md[i].len = sg_dma_len(sg);
- md[i].key = 0;
- }
- }
- /**
- * ibmvfc_map_sg_data - Maps dma for a scatterlist and initializes decriptor fields
- * @scmd: Scsi_Cmnd with the scatterlist
- * @evt: ibmvfc event struct
- * @vfc_cmd: vfc_cmd that contains the memory descriptor
- * @dev: device for which to map dma memory
- *
- * Returns:
- * 0 on success / non-zero on failure
- **/
- static int ibmvfc_map_sg_data(struct scsi_cmnd *scmd,
- struct ibmvfc_event *evt,
- struct ibmvfc_cmd *vfc_cmd, struct device *dev)
- {
- int sg_mapped;
- struct srp_direct_buf *data = &vfc_cmd->ioba;
- struct ibmvfc_host *vhost = dev_get_drvdata(dev);
- sg_mapped = scsi_dma_map(scmd);
- if (!sg_mapped) {
- vfc_cmd->flags |= IBMVFC_NO_MEM_DESC;
- return 0;
- } else if (unlikely(sg_mapped < 0)) {
- if (vhost->log_level > IBMVFC_DEFAULT_LOG_LEVEL)
- scmd_printk(KERN_ERR, scmd, "Failed to map DMA buffer for command\n");
- return sg_mapped;
- }
- if (scmd->sc_data_direction == DMA_TO_DEVICE) {
- vfc_cmd->flags |= IBMVFC_WRITE;
- vfc_cmd->iu.add_cdb_len |= IBMVFC_WRDATA;
- } else {
- vfc_cmd->flags |= IBMVFC_READ;
- vfc_cmd->iu.add_cdb_len |= IBMVFC_RDDATA;
- }
- if (sg_mapped == 1) {
- ibmvfc_map_sg_list(scmd, sg_mapped, data);
- return 0;
- }
- vfc_cmd->flags |= IBMVFC_SCATTERLIST;
- if (!evt->ext_list) {
- evt->ext_list = dma_pool_alloc(vhost->sg_pool, GFP_ATOMIC,
- &evt->ext_list_token);
- if (!evt->ext_list) {
- scsi_dma_unmap(scmd);
- if (vhost->log_level > IBMVFC_DEFAULT_LOG_LEVEL)
- scmd_printk(KERN_ERR, scmd, "Can't allocate memory for scatterlist\n");
- return -ENOMEM;
- }
- }
- ibmvfc_map_sg_list(scmd, sg_mapped, evt->ext_list);
- data->va = evt->ext_list_token;
- data->len = sg_mapped * sizeof(struct srp_direct_buf);
- data->key = 0;
- return 0;
- }
- /**
- * ibmvfc_timeout - Internal command timeout handler
- * @evt: struct ibmvfc_event that timed out
- *
- * Called when an internally generated command times out
- **/
- static void ibmvfc_timeout(struct ibmvfc_event *evt)
- {
- struct ibmvfc_host *vhost = evt->vhost;
- dev_err(vhost->dev, "Command timed out (%p). Resetting connection\n", evt);
- ibmvfc_reset_host(vhost);
- }
- /**
- * ibmvfc_send_event - Transforms event to u64 array and calls send_crq()
- * @evt: event to be sent
- * @vhost: ibmvfc host struct
- * @timeout: timeout in seconds - 0 means do not time command
- *
- * Returns the value returned from ibmvfc_send_crq(). (Zero for success)
- **/
- static int ibmvfc_send_event(struct ibmvfc_event *evt,
- struct ibmvfc_host *vhost, unsigned long timeout)
- {
- u64 *crq_as_u64 = (u64 *) &evt->crq;
- int rc;
- /* Copy the IU into the transfer area */
- *evt->xfer_iu = evt->iu;
- if (evt->crq.format == IBMVFC_CMD_FORMAT)
- evt->xfer_iu->cmd.tag = (u64)evt;
- else if (evt->crq.format == IBMVFC_MAD_FORMAT)
- evt->xfer_iu->mad_common.tag = (u64)evt;
- else
- BUG();
- list_add_tail(&evt->queue, &vhost->sent);
- init_timer(&evt->timer);
- if (timeout) {
- evt->timer.data = (unsigned long) evt;
- evt->timer.expires = jiffies + (timeout * HZ);
- evt->timer.function = (void (*)(unsigned long))ibmvfc_timeout;
- add_timer(&evt->timer);
- }
- mb();
- if ((rc = ibmvfc_send_crq(vhost, crq_as_u64[0], crq_as_u64[1]))) {
- list_del(&evt->queue);
- del_timer(&evt->timer);
- /* If send_crq returns H_CLOSED, return SCSI_MLQUEUE_HOST_BUSY.
- * Firmware will send a CRQ with a transport event (0xFF) to
- * tell this client what has happened to the transport. This
- * will be handled in ibmvfc_handle_crq()
- */
- if (rc == H_CLOSED) {
- if (printk_ratelimit())
- dev_warn(vhost->dev, "Send warning. Receive queue closed, will retry.\n");
- if (evt->cmnd)
- scsi_dma_unmap(evt->cmnd);
- ibmvfc_free_event(evt);
- return SCSI_MLQUEUE_HOST_BUSY;
- }
- dev_err(vhost->dev, "Send error (rc=%d)\n", rc);
- if (evt->cmnd) {
- evt->cmnd->result = DID_ERROR << 16;
- evt->done = ibmvfc_scsi_eh_done;
- } else
- evt->xfer_iu->mad_common.status = IBMVFC_MAD_CRQ_ERROR;
- evt->done(evt);
- } else
- ibmvfc_trc_start(evt);
- return 0;
- }
- /**
- * ibmvfc_log_error - Log an error for the failed command if appropriate
- * @evt: ibmvfc event to log
- *
- **/
- static void ibmvfc_log_error(struct ibmvfc_event *evt)
- {
- struct ibmvfc_cmd *vfc_cmd = &evt->xfer_iu->cmd;
- struct ibmvfc_host *vhost = evt->vhost;
- struct ibmvfc_fcp_rsp *rsp = &vfc_cmd->rsp;
- struct scsi_cmnd *cmnd = evt->cmnd;
- const char *err = unknown_error;
- int index = ibmvfc_get_err_index(vfc_cmd->status, vfc_cmd->error);
- int logerr = 0;
- int rsp_code = 0;
- if (index >= 0) {
- logerr = cmd_status[index].log;
- err = cmd_status[index].name;
- }
- if (!logerr && (vhost->log_level <= (IBMVFC_DEFAULT_LOG_LEVEL + 1)))
- return;
- if (rsp->flags & FCP_RSP_LEN_VALID)
- rsp_code = rsp->data.info.rsp_code;
- scmd_printk(KERN_ERR, cmnd, "Command (%02X) failed: %s (%x:%x) "
- "flags: %x fcp_rsp: %x, resid=%d, scsi_status: %x\n",
- cmnd->cmnd[0], err, vfc_cmd->status, vfc_cmd->error,
- rsp->flags, rsp_code, scsi_get_resid(cmnd), rsp->scsi_status);
- }
- /**
- * ibmvfc_scsi_done - Handle responses from commands
- * @evt: ibmvfc event to be handled
- *
- * Used as a callback when sending scsi cmds.
- **/
- static void ibmvfc_scsi_done(struct ibmvfc_event *evt)
- {
- struct ibmvfc_cmd *vfc_cmd = &evt->xfer_iu->cmd;
- struct ibmvfc_fcp_rsp *rsp = &vfc_cmd->rsp;
- struct scsi_cmnd *cmnd = evt->cmnd;
- u32 rsp_len = 0;
- u32 sense_len = rsp->fcp_sense_len;
- if (cmnd) {
- if (vfc_cmd->response_flags & IBMVFC_ADAPTER_RESID_VALID)
- scsi_set_resid(cmnd, vfc_cmd->adapter_resid);
- else if (rsp->flags & FCP_RESID_UNDER)
- scsi_set_resid(cmnd, rsp->fcp_resid);
- else
- scsi_set_resid(cmnd, 0);
- if (vfc_cmd->status) {
- cmnd->result = ibmvfc_get_err_result(vfc_cmd);
- if (rsp->flags & FCP_RSP_LEN_VALID)
- rsp_len = rsp->fcp_rsp_len;
- if ((sense_len + rsp_len) > SCSI_SENSE_BUFFERSIZE)
- sense_len = SCSI_SENSE_BUFFERSIZE - rsp_len;
- if ((rsp->flags & FCP_SNS_LEN_VALID) && rsp->fcp_sense_len && rsp_len <= 8)
- memcpy(cmnd->sense_buffer, rsp->data.sense + rsp_len, sense_len);
- if ((vfc_cmd->status & IBMVFC_VIOS_FAILURE) && (vfc_cmd->error == IBMVFC_PLOGI_REQUIRED))
- ibmvfc_reinit_host(evt->vhost);
- if (!cmnd->result && (!scsi_get_resid(cmnd) || (rsp->flags & FCP_RESID_OVER)))
- cmnd->result = (DID_ERROR << 16);
- ibmvfc_log_error(evt);
- }
- if (!cmnd->result &&
- (scsi_bufflen(cmnd) - scsi_get_resid(cmnd) < cmnd->underflow))
- cmnd->result = (DID_ERROR << 16);
- scsi_dma_unmap(cmnd);
- cmnd->scsi_done(cmnd);
- }
- if (evt->eh_comp)
- complete(evt->eh_comp);
- ibmvfc_free_event(evt);
- }
- /**
- * ibmvfc_host_chkready - Check if the host can accept commands
- * @vhost: struct ibmvfc host
- *
- * Returns:
- * 1 if host can accept command / 0 if not
- **/
- static inline int ibmvfc_host_chkready(struct ibmvfc_host *vhost)
- {
- int result = 0;
- switch (vhost->state) {
- case IBMVFC_LINK_DEAD:
- case IBMVFC_HOST_OFFLINE:
- result = DID_NO_CONNECT << 16;
- break;
- case IBMVFC_NO_CRQ:
- case IBMVFC_INITIALIZING:
- case IBMVFC_HALTED:
- case IBMVFC_LINK_DOWN:
- result = DID_REQUEUE << 16;
- break;
- case IBMVFC_ACTIVE:
- result = 0;
- break;
- };
- return result;
- }
- /**
- * ibmvfc_queuecommand - The queuecommand function of the scsi template
- * @cmnd: struct scsi_cmnd to be executed
- * @done: Callback function to be called when cmnd is completed
- *
- * Returns:
- * 0 on success / other on failure
- **/
- static int ibmvfc_queuecommand(struct scsi_cmnd *cmnd,
- void (*done) (struct scsi_cmnd *))
- {
- struct ibmvfc_host *vhost = shost_priv(cmnd->device->host);
- struct fc_rport *rport = starget_to_rport(scsi_target(cmnd->device));
- struct ibmvfc_cmd *vfc_cmd;
- struct ibmvfc_event *evt;
- u8 tag[2];
- int rc;
- if (unlikely((rc = fc_remote_port_chkready(rport))) ||
- unlikely((rc = ibmvfc_host_chkready(vhost)))) {
- cmnd->result = rc;
- done(cmnd);
- return 0;
- }
- cmnd->result = (DID_OK << 16);
- evt = ibmvfc_get_event(vhost);
- ibmvfc_init_event(evt, ibmvfc_scsi_done, IBMVFC_CMD_FORMAT);
- evt->cmnd = cmnd;
- cmnd->scsi_done = done;
- vfc_cmd = &evt->iu.cmd;
- memset(vfc_cmd, 0, sizeof(*vfc_cmd));
- vfc_cmd->resp.va = (u64)evt->crq.ioba + offsetof(struct ibmvfc_cmd, rsp);
- vfc_cmd->resp.len = sizeof(vfc_cmd->rsp);
- vfc_cmd->frame_type = IBMVFC_SCSI_FCP_TYPE;
- vfc_cmd->payload_len = sizeof(vfc_cmd->iu);
- vfc_cmd->resp_len = sizeof(vfc_cmd->rsp);
- vfc_cmd->cancel_key = (unsigned long)cmnd->device->hostdata;
- vfc_cmd->tgt_scsi_id = rport->port_id;
- vfc_cmd->iu.xfer_len = scsi_bufflen(cmnd);
- int_to_scsilun(cmnd->device->lun, &vfc_cmd->iu.lun);
- memcpy(vfc_cmd->iu.cdb, cmnd->cmnd, cmnd->cmd_len);
- if (scsi_populate_tag_msg(cmnd, tag)) {
- vfc_cmd->task_tag = tag[1];
- switch (tag[0]) {
- case MSG_SIMPLE_TAG:
- vfc_cmd->iu.pri_task_attr = IBMVFC_SIMPLE_TASK;
- break;
- case MSG_HEAD_TAG:
- vfc_cmd->iu.pri_task_attr = IBMVFC_HEAD_OF_QUEUE;
- break;
- case MSG_ORDERED_TAG:
- vfc_cmd->iu.pri_task_attr = IBMVFC_ORDERED_TASK;
- break;
- };
- }
- if (likely(!(rc = ibmvfc_map_sg_data(cmnd, evt, vfc_cmd, vhost->dev))))
- return ibmvfc_send_event(evt, vhost, 0);
- ibmvfc_free_event(evt);
- if (rc == -ENOMEM)
- return SCSI_MLQUEUE_HOST_BUSY;
- if (vhost->log_level > IBMVFC_DEFAULT_LOG_LEVEL)
- scmd_printk(KERN_ERR, cmnd,
- "Failed to map DMA buffer for command. rc=%d\n", rc);
- cmnd->result = DID_ERROR << 16;
- done(cmnd);
- return 0;
- }
- /**
- * ibmvfc_sync_completion - Signal that a synchronous command has completed
- * @evt: ibmvfc event struct
- *
- **/
- static void ibmvfc_sync_completion(struct ibmvfc_event *evt)
- {
- /* copy the response back */
- if (evt->sync_iu)
- *evt->sync_iu = *evt->xfer_iu;
- complete(&evt->comp);
- }
- /**
- * ibmvfc_reset_device - Reset the device with the specified reset type
- * @sdev: scsi device to reset
- * @type: reset type
- * @desc: reset type description for log messages
- *
- * Returns:
- * 0 on success / other on failure
- **/
- static int ibmvfc_reset_device(struct scsi_device *sdev, int type, char *desc)
- {
- struct ibmvfc_host *vhost = shost_priv(sdev->host);
- struct fc_rport *rport = starget_to_rport(scsi_target(sdev));
- struct ibmvfc_cmd *tmf;
- struct ibmvfc_event *evt = NULL;
- union ibmvfc_iu rsp_iu;
- struct ibmvfc_fcp_rsp *fc_rsp = &rsp_iu.cmd.rsp;
- int rsp_rc = -EBUSY;
- unsigned long flags;
- int rsp_code = 0;
- spin_lock_irqsave(vhost->host->host_lock, flags);
- if (vhost->state == IBMVFC_ACTIVE) {
- evt = ibmvfc_get_event(vhost);
- ibmvfc_init_event(evt, ibmvfc_sync_completion, IBMVFC_CMD_FORMAT);
- tmf = &evt->iu.cmd;
- memset(tmf, 0, sizeof(*tmf));
- tmf->resp.va = (u64)evt->crq.ioba + offsetof(struct ibmvfc_cmd, rsp);
- tmf->resp.len = sizeof(tmf->rsp);
- tmf->frame_type = IBMVFC_SCSI_FCP_TYPE;
- tmf->payload_len = sizeof(tmf->iu);
- tmf->resp_len = sizeof(tmf->rsp);
- tmf->cancel_key = (unsigned long)sdev->hostdata;
- tmf->tgt_scsi_id = rport->port_id;
- int_to_scsilun(sdev->lun, &tmf->iu.lun);
- tmf->flags = (IBMVFC_NO_MEM_DESC | IBMVFC_TMF);
- tmf->iu.tmf_flags = type;
- evt->sync_iu = &rsp_iu;
- init_completion(&evt->comp);
- rsp_rc = ibmvfc_send_event(evt, vhost, default_timeout);
- }
- spin_unlock_irqrestore(vhost->host->host_lock, flags);
- if (rsp_rc != 0) {
- sdev_printk(KERN_ERR, sdev, "Failed to send %s reset event. rc=%d\n",
- desc, rsp_rc);
- return -EIO;
- }
- sdev_printk(KERN_INFO, sdev, "Resetting %s\n", desc);
- wait_for_completion(&evt->comp);
- if (rsp_iu.cmd.status) {
- if (fc_rsp->flags & FCP_RSP_LEN_VALID)
- rsp_code = fc_rsp->data.info.rsp_code;
- sdev_printk(KERN_ERR, sdev, "%s reset failed: %s (%x:%x) "
- "flags: %x fcp_rsp: %x, scsi_status: %x\n",
- desc, ibmvfc_get_cmd_error(rsp_iu.cmd.status, rsp_iu.cmd.error),
- rsp_iu.cmd.status, rsp_iu.cmd.error, fc_rsp->flags, rsp_code,
- fc_rsp->scsi_status);
- rsp_rc = -EIO;
- } else
- sdev_printk(KERN_INFO, sdev, "%s reset successful\n", desc);
- spin_lock_irqsave(vhost->host->host_lock, flags);
- ibmvfc_free_event(evt);
- spin_unlock_irqrestore(vhost->host->host_lock, flags);
- return rsp_rc;
- }
- /**
- * ibmvfc_abort_task_set - Abort outstanding commands to the device
- * @sdev: scsi device to abort commands
- *
- * This sends an Abort Task Set to the VIOS for the specified device. This does
- * NOT send any cancel to the VIOS. That must be done separately.
- *
- * Returns:
- * 0 on success / other on failure
- **/
- static int ibmvfc_abort_task_set(struct scsi_device *sdev)
- {
- struct ibmvfc_host *vhost = shost_priv(sdev->host);
- struct fc_rport *rport = starget_to_rport(scsi_target(sdev));
- struct ibmvfc_cmd *tmf;
- struct ibmvfc_event *evt, *found_evt;
- union ibmvfc_iu rsp_iu;
- struct ibmvfc_fcp_rsp *fc_rsp = &rsp_iu.cmd.rsp;
- int rsp_rc = -EBUSY;
- unsigned long flags;
- int rsp_code = 0;
- spin_lock_irqsave(vhost->host->host_lock, flags);
- found_evt = NULL;
- list_for_each_entry(evt, &vhost->sent, queue) {
- if (evt->cmnd && evt->cmnd->device == sdev) {
- found_evt = evt;
- break;
- }
- }
- if (!found_evt) {
- if (vhost->log_level > IBMVFC_DEFAULT_LOG_LEVEL)
- sdev_printk(KERN_INFO, sdev, "No events found to abort\n");
- spin_unlock_irqrestore(vhost->host->host_lock, flags);
- return 0;
- }
- if (vhost->state == IBMVFC_ACTIVE) {
- evt = ibmvfc_get_event(vhost);
- ibmvfc_init_event(evt, ibmvfc_sync_completion, IBMVFC_CMD_FORMAT);
- tmf = &evt->iu.cmd;
- memset(tmf, 0, sizeof(*tmf));
- tmf->resp.va = (u64)evt->crq.ioba + offsetof(struct ibmvfc_cmd, rsp);
- tmf->resp.len = sizeof(tmf->rsp);
- tmf->frame_type = IBMVFC_SCSI_FCP_TYPE;
- tmf->payload_len = sizeof(tmf->iu);
- tmf->resp_len = sizeof(tmf->rsp);
- tmf->cancel_key = (unsigned long)sdev->hostdata;
- tmf->tgt_scsi_id = rport->port_id;
- int_to_scsilun(sdev->lun, &tmf->iu.lun);
- tmf->flags = (IBMVFC_NO_MEM_DESC…
Large files files are truncated, but you can click here to view the full file