// SPDX-License-Identifier: GPL-2.0-or-later /* * 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
*/
#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
**/ staticint 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
**/ staticconstchar *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 * @vhost: ibmvfc host struct * @vfc_cmd: ibmvfc command struct * * Return value: * SCSI result value to return for completed command
**/ staticint ibmvfc_get_err_result(struct ibmvfc_host *vhost, struct ibmvfc_cmd *vfc_cmd)
{ int err; struct ibmvfc_fcp_rsp *rsp = ibmvfc_get_fcp_rsp(vhost, vfc_cmd); int fc_rsp_len = be32_to_cpu(rsp->fcp_rsp_len);
/** * 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
**/ staticint 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;
}
staticconstchar *unknown_fc_explain = "unknown fc explain";
staticconststruct {
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
**/ staticconstchar *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
**/ staticconstchar *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;
staticconstchar *unknown_fc_type = "unknown fc type";
/** * ibmvfc_get_fc_type - Return the FC Type description text * @status: FC Type error status * * Returns: * error string
**/ staticconstchar *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 * * Returns: * 0 if action changed / non-zero if not changed
**/ staticint ibmvfc_set_tgt_action(struct ibmvfc_target *tgt, enum ibmvfc_target_action action)
{ int rc = -EINVAL;
switch (tgt->action) { case IBMVFC_TGT_ACTION_LOGOUT_RPORT: if (action == IBMVFC_TGT_ACTION_LOGOUT_RPORT_WAIT ||
action == IBMVFC_TGT_ACTION_DEL_RPORT) {
tgt->action = action;
rc = 0;
} break; case IBMVFC_TGT_ACTION_LOGOUT_RPORT_WAIT: if (action == IBMVFC_TGT_ACTION_DEL_RPORT ||
action == IBMVFC_TGT_ACTION_DEL_AND_LOGOUT_RPORT) {
tgt->action = action;
rc = 0;
} break; case IBMVFC_TGT_ACTION_LOGOUT_DELETED_RPORT: if (action == IBMVFC_TGT_ACTION_LOGOUT_RPORT) {
tgt->action = action;
rc = 0;
} break; case IBMVFC_TGT_ACTION_DEL_AND_LOGOUT_RPORT: if (action == IBMVFC_TGT_ACTION_LOGOUT_DELETED_RPORT) {
tgt->action = action;
rc = 0;
} break; case IBMVFC_TGT_ACTION_DEL_RPORT: if (action == IBMVFC_TGT_ACTION_DELETED_RPORT) {
tgt->action = action;
rc = 0;
} break; case IBMVFC_TGT_ACTION_DELETED_RPORT: break; default:
tgt->action = action;
rc = 0; break;
}
if (action >= IBMVFC_TGT_ACTION_LOGOUT_RPORT)
tgt->add_rport = 0;
return rc;
}
/** * 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
**/ staticint ibmvfc_set_host_state(struct ibmvfc_host *vhost, enum ibmvfc_host_state state)
{ int rc = 0;
/** * ibmvfc_set_host_action - Set the next init action for the host * @vhost: ibmvfc host struct * @action: action to perform *
**/ staticvoid 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_LOGO_WAIT: if (vhost->action == IBMVFC_HOST_ACTION_LOGO)
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_DEL_FAILED:
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_REENABLE: case IBMVFC_HOST_ACTION_RESET:
vhost->action = action; break; case IBMVFC_HOST_ACTION_INIT: case IBMVFC_HOST_ACTION_TGT_DEL: case IBMVFC_HOST_ACTION_LOGO: case IBMVFC_HOST_ACTION_QUERY_TGTS: case IBMVFC_HOST_ACTION_TGT_DEL_FAILED: case IBMVFC_HOST_ACTION_NONE: default: switch (vhost->action) { case IBMVFC_HOST_ACTION_RESET: case IBMVFC_HOST_ACTION_REENABLE: break; default:
vhost->action = action; break;
} break;
}
}
/** * ibmvfc_del_tgt - Schedule cleanup and removal of the target * @tgt: ibmvfc target struct
**/ staticvoid ibmvfc_del_tgt(struct ibmvfc_target *tgt)
{ if (!ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_LOGOUT_RPORT)) {
tgt->job_step = ibmvfc_tgt_implicit_logout_and_del;
tgt->init_retries = 0;
}
wake_up(&tgt->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 *
**/ staticvoid ibmvfc_link_down(struct ibmvfc_host *vhost, enum ibmvfc_host_state state)
{ struct ibmvfc_target *tgt;
/** * 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
**/ staticint 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_init_event_pool - Allocates and initializes the event pool for a host * @vhost: ibmvfc host who owns the event pool * @queue: ibmvfc queue struct * * Returns zero on success.
**/ staticint ibmvfc_init_event_pool(struct ibmvfc_host *vhost, struct ibmvfc_queue *queue)
{ int i; struct ibmvfc_event_pool *pool = &queue->evt_pool;
/** * ibmvfc_free_event_pool - Frees memory of the event pool of a host * @vhost: ibmvfc host who owns the event pool * @queue: ibmvfc queue struct *
**/ staticvoid ibmvfc_free_event_pool(struct ibmvfc_host *vhost, struct ibmvfc_queue *queue)
{ int i; struct ibmvfc_event_pool *pool = &queue->evt_pool;
ENTER; for (i = 0; i < pool->size; ++i) {
list_del(&pool->events[i].queue_list);
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);
}
/* Clean out the queue */
memset(crq->msgs.crq, 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"); elseif (rc != 0)
dev_warn(vhost->dev, "Couldn't register crq (rc=%d)\n", 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
**/ staticint 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 *
**/ staticvoid ibmvfc_free_event(struct ibmvfc_event *evt)
{ struct ibmvfc_event_pool *pool = &evt->queue->evt_pool; unsignedlong flags;
/** * 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.
**/ staticvoid ibmvfc_scsi_eh_done(struct ibmvfc_event *evt)
{ struct scsi_cmnd *cmnd = evt->cmnd;
if (cmnd) {
scsi_dma_unmap(cmnd);
scsi_done(cmnd);
}
ibmvfc_free_event(evt);
}
/** * ibmvfc_complete_purge - Complete failed command list * @purge_list: list head of failed commands * * This function runs completions on commands to fail as a result of a * host reset or platform migration.
**/ staticvoid ibmvfc_complete_purge(struct list_head *purge_list)
{ struct ibmvfc_event *evt, *pos;
/** * ibmvfc_fail_request - Fail request with specified error code * @evt: ibmvfc event struct * @error_code: error code to fail request with * * Return value: * none
**/ staticvoid ibmvfc_fail_request(struct ibmvfc_event *evt, int error_code)
{ /* * Anything we are failing should still be active. Otherwise, it * implies we already got a response for the command and are doing * something bad like double completing it.
*/
BUG_ON(!atomic_dec_and_test(&evt->active)); if (evt->cmnd) {
evt->cmnd->result = (error_code << 16);
evt->done = ibmvfc_scsi_eh_done;
} else
evt->xfer_iu->mad_common.status = cpu_to_be16(IBMVFC_MAD_DRIVER_FAILED);
timer_delete(&evt->timer);
}
/** * 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
**/ staticvoid ibmvfc_purge_requests(struct ibmvfc_host *vhost, int error_code)
{ struct ibmvfc_event *evt, *pos; struct ibmvfc_queue *queues = vhost->scsi_scrqs.scrqs; unsignedlong flags; int hwqs = 0; int i;
if (vhost->using_channels)
hwqs = vhost->scsi_scrqs.active_queues;
for (i = 0; i < hwqs; i++) {
spin_lock_irqsave(queues[i].q_lock, flags);
spin_lock(&queues[i].l_lock);
list_for_each_entry_safe(evt, pos, &queues[i].sent, queue_list)
ibmvfc_fail_request(evt, error_code);
list_splice_init(&queues[i].sent, &vhost->purge);
spin_unlock(&queues[i].l_lock);
spin_unlock_irqrestore(queues[i].q_lock, flags);
}
}
/** * ibmvfc_hard_reset_host - Reset the connection to the server by breaking the CRQ * @vhost: struct ibmvfc host to reset
**/ staticvoid ibmvfc_hard_reset_host(struct ibmvfc_host *vhost)
{
ibmvfc_purge_requests(vhost, DID_ERROR);
ibmvfc_link_down(vhost, IBMVFC_LINK_DOWN);
ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_RESET);
}
/** * __ibmvfc_reset_host - Reset the connection to the server (no locking) * @vhost: struct ibmvfc host to reset
**/ staticvoid __ibmvfc_reset_host(struct ibmvfc_host *vhost)
{ if (vhost->logged_in && vhost->action != IBMVFC_HOST_ACTION_LOGO_WAIT &&
!ibmvfc_set_host_state(vhost, IBMVFC_INITIALIZING)) {
scsi_block_requests(vhost->host);
ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_LOGO);
vhost->job_step = ibmvfc_npiv_logout;
wake_up(&vhost->work_wait_q);
} else
ibmvfc_hard_reset_host(vhost);
}
/** * ibmvfc_reset_host - Reset the connection to the server * @vhost: ibmvfc host struct
**/ staticvoid ibmvfc_reset_host(struct ibmvfc_host *vhost)
{ unsignedlong flags;
/** * __ibmvfc_get_event - Gets the next free event in pool * @queue: ibmvfc queue struct * @reserved: event is for a reserved management command * * Returns a free event from the pool.
**/ staticstruct ibmvfc_event *__ibmvfc_get_event(struct ibmvfc_queue *queue, int reserved)
{ struct ibmvfc_event *evt = NULL; unsignedlong flags;
/** * ibmvfc_locked_done - Calls evt completion with host_lock held * @evt: ibmvfc evt to complete * * All non-scsi command completion callbacks have the expectation that the * host_lock is held. This callback is used by ibmvfc_init_event to wrap a * MAD evt with the host_lock.
**/ staticvoid ibmvfc_locked_done(struct ibmvfc_event *evt)
{ unsignedlong flags;
/** * ibmvfc_timeout - Internal command timeout handler * @t: struct ibmvfc_event that timed out * * Called when an internally generated command times out
**/ staticvoid ibmvfc_timeout(struct timer_list *t)
{ struct ibmvfc_event *evt = timer_container_of(evt, t, timer); 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)
**/ staticint ibmvfc_send_event(struct ibmvfc_event *evt, struct ibmvfc_host *vhost, unsignedlong timeout)
{
__be64 *crq_as_u64 = (__be64 *) &evt->crq; unsignedlong flags; 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 = cpu_to_be64((u64)evt); elseif (evt->crq.format == IBMVFC_MAD_FORMAT)
evt->xfer_iu->mad_common.tag = cpu_to_be64((u64)evt); else
BUG();
if (rc) {
atomic_set(&evt->active, 0);
list_del(&evt->queue_list);
spin_unlock_irqrestore(&evt->queue->l_lock, flags);
timer_delete(&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;
}
/** * ibmvfc_host_chkready - Check if the host can accept commands * @vhost: struct ibmvfc host * * Returns: * 1 if host can accept command / 0 if not
**/ staticinlineint 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;
}
/** * ibmvfc_sync_completion - Signal that a synchronous command has completed * @evt: ibmvfc event struct *
**/ staticvoid ibmvfc_sync_completion(struct ibmvfc_event *evt)
{ /* copy the response back */ if (evt->sync_iu)
*evt->sync_iu = *evt->xfer_iu;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.