/* bnx2fc_io.c: QLogic Linux FCoE offload driver. * IO manager and SCSI IO processing. * * Copyright (c) 2008-2013 Broadcom Corporation * Copyright (c) 2014-2016 QLogic Corporation * Copyright (c) 2016-2017 Cavium Inc. * * 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. * * Written by: Bhanu Prakash Gollapudi (bprakash@broadcom.com)
*/
spin_lock_bh(&tgt->tgt_lock); if (test_and_clear_bit(BNX2FC_FLAG_ISSUE_RRQ, &io_req->req_flags)) {
clear_bit(BNX2FC_FLAG_RETIRE_OXID, &io_req->req_flags); /* * ideally we should hold the io_req until RRQ complets, * and release io_req from timeout hold.
*/
spin_unlock_bh(&tgt->tgt_lock);
bnx2fc_send_rrq(io_req); return;
} if (test_and_clear_bit(BNX2FC_FLAG_RETIRE_OXID, &io_req->req_flags)) {
BNX2FC_IO_DBG(io_req, "IO ready for reuse now\n"); goto done;
}
switch (cmd_type) { case BNX2FC_SCSI_CMD: if (test_and_clear_bit(BNX2FC_FLAG_EH_ABORT,
&io_req->req_flags)) { /* Handle eh_abort timeout */
BNX2FC_IO_DBG(io_req, "eh_abort timed out\n");
complete(&io_req->abts_done);
} elseif (test_bit(BNX2FC_FLAG_ISSUE_ABTS,
&io_req->req_flags)) { /* Handle internally generated ABTS timeout */
BNX2FC_IO_DBG(io_req, "ABTS timed out refcnt = %d\n",
kref_read(&io_req->refcount)); if (!(test_and_set_bit(BNX2FC_FLAG_ABTS_DONE,
&io_req->req_flags))) { /* * Cleanup and return original command to * mid-layer.
*/
bnx2fc_initiate_cleanup(io_req);
kref_put(&io_req->refcount, bnx2fc_cmd_release);
spin_unlock_bh(&tgt->tgt_lock);
return;
} else {
BNX2FC_IO_DBG(io_req, "IO already in " "ABTS processing\n");
}
} break; case BNX2FC_ELS:
if (test_bit(BNX2FC_FLAG_ISSUE_ABTS, &io_req->req_flags)) {
BNX2FC_IO_DBG(io_req, "ABTS for ELS timed out\n");
if (!test_and_set_bit(BNX2FC_FLAG_ABTS_DONE,
&io_req->req_flags)) {
kref_put(&io_req->refcount, bnx2fc_cmd_release);
spin_unlock_bh(&tgt->tgt_lock);
return;
}
} else { /* * Handle ELS timeout. * tgt_lock is used to sync compl path and timeout * path. If els compl path is processing this IO, we * have nothing to do here, just release the timer hold
*/
BNX2FC_IO_DBG(io_req, "ELS timed out\n"); if (test_and_set_bit(BNX2FC_FLAG_ELS_DONE,
&io_req->req_flags)) goto done;
/* Indicate the cb_func that this ELS is timed out */
set_bit(BNX2FC_FLAG_ELS_TIMEOUT, &io_req->req_flags);
done: /* release the cmd that was held when timer was set */
kref_put(&io_req->refcount, bnx2fc_cmd_release);
spin_unlock_bh(&tgt->tgt_lock);
}
staticvoid bnx2fc_scsi_done(struct bnx2fc_cmd *io_req, int err_code)
{ /* Called with host lock held */ struct scsi_cmnd *sc_cmd = io_req->sc_cmd;
/* * active_cmd_queue may have other command types as well, * and during flush operation, we want to error back only * scsi commands.
*/ if (io_req->cmd_type != BNX2FC_SCSI_CMD) return;
BNX2FC_IO_DBG(io_req, "scsi_done. err_code = 0x%x\n", err_code); if (test_bit(BNX2FC_FLAG_CMD_LOST, &io_req->req_flags)) { /* Do not call scsi done for this IO */ return;
}
for (i = 0; i < arr_sz; i++) {
INIT_LIST_HEAD(&cmgr->free_list[i]);
spin_lock_init(&cmgr->free_list_lock[i]);
}
/* * Pre-allocated pool of bnx2fc_cmds. * Last entry in the free list array is the free list * of slow path requests.
*/
xid = BNX2FC_MIN_XID;
num_pri_ios = num_ios - hba->elstm_xids; for (i = 0; i < num_ios; i++) {
io_req = kzalloc(sizeof(*io_req), GFP_KERNEL);
if (!io_req) {
printk(KERN_ERR PFX "failed to alloc io_req\n"); goto mem_err;
}
io_req->xid = xid++; if (i < num_pri_ios)
list_add_tail(&io_req->link,
&cmgr->free_list[io_req->xid %
num_possible_cpus()]); else
list_add_tail(&io_req->link,
&cmgr->free_list[num_possible_cpus()]);
io_req++;
}
/* Allocate pool of io_bdts - one for each bnx2fc_cmd */
mem_size = num_ios * sizeof(struct io_bdt *);
cmgr->io_bdt_pool = kzalloc(mem_size, GFP_KERNEL); if (!cmgr->io_bdt_pool) {
printk(KERN_ERR PFX "failed to alloc io_bdt_pool\n"); goto mem_err;
}
mem_size = sizeof(struct io_bdt); for (i = 0; i < num_ios; i++) {
cmgr->io_bdt_pool[i] = kmalloc(mem_size, GFP_KERNEL); if (!cmgr->io_bdt_pool[i]) {
printk(KERN_ERR PFX "failed to alloc " "io_bdt_pool[%d]\n", i); goto mem_err;
}
}
/* Allocate an map fcoe_bdt_ctx structures */
bd_tbl_sz = BNX2FC_MAX_BDS_PER_CMD * sizeof(struct fcoe_bd_ctx); for (i = 0; i < num_ios; i++) {
bdt_info = cmgr->io_bdt_pool[i];
bdt_info->bd_tbl = dma_alloc_coherent(&hba->pcidev->dev,
bd_tbl_sz,
&bdt_info->bd_tbl_dma,
GFP_KERNEL); if (!bdt_info->bd_tbl) {
printk(KERN_ERR PFX "failed to alloc " "bdt_tbl[%d]\n", i); goto mem_err;
}
}
/* Bind io_bdt for this io_req */ /* Have a static link between io_req and io_bdt_pool */
bd_tbl = io_req->bd_tbl = cmd_mgr->io_bdt_pool[xid];
bd_tbl->io_req = io_req;
/* Hold the io_req against deletion */
kref_init(&io_req->refcount); return io_req;
}
/* Bind io_bdt for this io_req */ /* Have a static link between io_req and io_bdt_pool */
bd_tbl = io_req->bd_tbl = cmd_mgr->io_bdt_pool[xid];
bd_tbl->io_req = io_req;
/* Hold the io_req against deletion */
kref_init(&io_req->refcount); return io_req;
}
if (io_req->cmd_type == BNX2FC_SCSI_CMD)
index = io_req->xid % num_possible_cpus(); else
index = RESERVE_FREE_LIST_INDEX;
spin_lock_bh(&cmd_mgr->free_list_lock[index]); if (io_req->cmd_type != BNX2FC_SCSI_CMD)
bnx2fc_free_mp_resc(io_req);
cmd_mgr->cmds[io_req->xid] = NULL; /* Delete IO from retire queue */
list_del_init(&io_req->link); /* Add it to the free list */
list_add(&io_req->link,
&cmd_mgr->free_list[index]);
atomic_dec(&io_req->tgt->num_active_ios);
spin_unlock_bh(&cmd_mgr->free_list_lock[index]);
/* * MP buffer is either a task mgmt command or an ELS. * So the assumption is that it consumes a single bd * entry in the bd table
*/
mp_resp_bd = mp_req->mp_resp_bd;
addr = mp_req->resp_buf_dma;
mp_resp_bd->buf_addr_lo = (u32)addr & 0xffffffff;
mp_resp_bd->buf_addr_hi = (u32)((u64)addr >> 32);
mp_resp_bd->buf_len = CNIC_PAGE_SIZE;
mp_resp_bd->flags = 0;
if (rport == NULL) {
printk(KERN_ERR PFX "device_reset: rport is NULL\n");
rc = FAILED; goto tmf_err;
}
rp = rport->dd_data;
rc = fc_block_rport(rport); if (rc) return rc;
if (lport->state != LPORT_ST_READY || !(lport->link_up)) {
printk(KERN_ERR PFX "device_reset: link is not ready\n");
rc = FAILED; goto tmf_err;
} /* rport and tgt are allocated together, so tgt should be non-NULL */
tgt = (struct bnx2fc_rport *)&rp[1];
/* Fill FC header */
fc_hdr = &(abts_req->req_fc_hdr);
/* Obtain oxid and rxid for the original exchange to be aborted */
fc_hdr->fh_ox_id = htons(io_req->xid);
fc_hdr->fh_rx_id = htons(io_req->task->rxwr_txrd.var_ctx.rx_id);
/* Initialize task context for this IO request */
task_page = (struct fcoe_task_ctx_entry *)
interface->hba->task_ctx[task_idx];
task = &(task_page[index]);
bnx2fc_init_mp_task(abts_io_req, task);
/* * ABTS task is a temporary task that will be cleaned up * irrespective of ABTS response. We need to start the timer * for the original exchange, as the CQE is posted for the original * IO request. * * Timer for ABTS is started only when it is originated by a * TM request. For the ABTS issued as part of ULP timeout, * scsi-ml maintains the timers.
*/
/* if (test_bit(BNX2FC_FLAG_ISSUE_ABTS, &io_req->req_flags))*/
bnx2fc_cmd_timer_set(io_req, 2 * r_a_tov);
/* Set flag that cleanup request is pending with the firmware */
set_bit(BNX2FC_FLAG_ISSUE_CLEANUP_REQ, &io_req->req_flags);
/* Ring doorbell */
bnx2fc_ring_doorbell(tgt);
cleanup_err: return rc;
}
/** * bnx2fc_eh_target_reset: Reset a target * * @sc_cmd: SCSI command * * Set from SCSI host template to send task mgmt command to the target * and wait for the response
*/ int bnx2fc_eh_target_reset(struct scsi_cmnd *sc_cmd)
{ struct fc_rport *rport = starget_to_rport(scsi_target(sc_cmd->device)); struct fc_lport *lport = shost_priv(rport_to_shost(rport));
/** * bnx2fc_eh_device_reset - Reset a single LUN * * @sc_cmd: SCSI command * * Set from SCSI host template to send task mgmt command to the target * and wait for the response
*/ int bnx2fc_eh_device_reset(struct scsi_cmnd *sc_cmd)
{ struct fc_rport *rport = starget_to_rport(scsi_target(sc_cmd->device)); struct fc_lport *lport = shost_priv(rport_to_shost(rport));
/* * Can't wait forever on cleanup response lest we let the SCSI error * handler wait forever
*/
time_left = wait_for_completion_timeout(&io_req->cleanup_done,
BNX2FC_FW_TIMEOUT); if (!time_left) {
BNX2FC_IO_DBG(io_req, "%s(): Wait for cleanup timed out.\n",
__func__);
/* * Put the extra reference to the SCSI command since it would * not have been returned in this case.
*/
kref_put(&io_req->refcount, bnx2fc_cmd_release);
}
lport = shost_priv(sc_cmd->device->host); if ((lport->state != LPORT_ST_READY) || !(lport->link_up)) {
printk(KERN_ERR PFX "eh_abort: link not ready\n"); return FAILED;
}
tgt = (struct bnx2fc_rport *)&rp[1];
BNX2FC_TGT_DBG(tgt, "Entered bnx2fc_eh_abort\n");
spin_lock_bh(&tgt->tgt_lock);
io_req = bnx2fc_priv(sc_cmd)->io_req; if (!io_req) { /* Command might have just completed */
printk(KERN_ERR PFX "eh_abort: io_req is NULL\n");
spin_unlock_bh(&tgt->tgt_lock); return SUCCESS;
}
BNX2FC_IO_DBG(io_req, "eh_abort - refcnt = %d\n",
kref_read(&io_req->refcount));
/* Hold IO request across abort processing */
kref_get(&io_req->refcount);
BUG_ON(tgt != io_req->tgt);
/* Remove the io_req from the active_q. */ /* * Task Mgmt functions (LUN RESET & TGT RESET) will not * issue an ABTS on this particular IO req, as the * io_req is no longer in the active_q.
*/ if (tgt->flush_in_prog) {
printk(KERN_ERR PFX "eh_abort: io_req (xid = 0x%x) " "flush in progress\n", io_req->xid);
kref_put(&io_req->refcount, bnx2fc_cmd_release);
spin_unlock_bh(&tgt->tgt_lock); return SUCCESS;
}
if (io_req->on_active_queue == 0) {
printk(KERN_ERR PFX "eh_abort: io_req (xid = 0x%x) " "not on active_q\n", io_req->xid); /* * The IO is still with the FW. * Return failure and let SCSI-ml retry eh_abort.
*/
spin_unlock_bh(&tgt->tgt_lock); return FAILED;
}
/* * Only eh_abort processing will remove the IO from * active_cmd_q before processing the request. this is * done to avoid race conditions between IOs aborted * as part of task management completion and eh_abort * processing
*/
list_del_init(&io_req->link);
io_req->on_active_queue = 0; /* Move IO req to retire queue */
list_add_tail(&io_req->link, &tgt->io_retire_queue);
if (test_and_set_bit(BNX2FC_FLAG_ISSUE_ABTS, &io_req->req_flags)) {
printk(KERN_ERR PFX "eh_abort: io_req (xid = 0x%x) " "already in abts processing\n", io_req->xid); if (cancel_delayed_work(&io_req->timeout_work))
kref_put(&io_req->refcount,
bnx2fc_cmd_release); /* drop timer hold */ /* * We don't want to hold off the upper layer timer so simply * cleanup the command and return that I/O was successfully * aborted.
*/
bnx2fc_abts_cleanup(io_req); /* This only occurs when an task abort was requested while ABTS is in progress. Setting the IO_CLEANUP flag will skip the RRQ process in the case when the fw generated SCSI_CMD cmpl was a result from the ABTS request rather than the CLEANUP
request */
set_bit(BNX2FC_FLAG_IO_CLEANUP, &io_req->req_flags);
rc = FAILED; goto done;
}
/* Cancel the current timer running on this io_req */ if (cancel_delayed_work(&io_req->timeout_work))
kref_put(&io_req->refcount,
bnx2fc_cmd_release); /* drop timer hold */
set_bit(BNX2FC_FLAG_EH_ABORT, &io_req->req_flags);
io_req->wait_for_abts_comp = 1;
rc = bnx2fc_initiate_abts(io_req); if (rc == FAILED) {
io_req->wait_for_cleanup_comp = 1;
bnx2fc_initiate_cleanup(io_req);
spin_unlock_bh(&tgt->tgt_lock);
wait_for_completion(&io_req->cleanup_done);
spin_lock_bh(&tgt->tgt_lock);
io_req->wait_for_cleanup_comp = 0; goto done;
}
spin_unlock_bh(&tgt->tgt_lock);
/* Wait 2 * RA_TOV + 1 to be sure timeout function hasn't fired */
time_left = wait_for_completion_timeout(&io_req->abts_done,
msecs_to_jiffies(2 * rp->r_a_tov + 1)); if (time_left)
BNX2FC_IO_DBG(io_req, "Timed out in eh_abort waiting for abts_done");
spin_lock_bh(&tgt->tgt_lock);
io_req->wait_for_abts_comp = 0; if (test_bit(BNX2FC_FLAG_IO_COMPL, &io_req->req_flags)) {
BNX2FC_IO_DBG(io_req, "IO completed in a different context\n");
rc = SUCCESS;
} elseif (!(test_and_set_bit(BNX2FC_FLAG_ABTS_DONE,
&io_req->req_flags))) { /* Let the scsi-ml try to recover this command */
printk(KERN_ERR PFX "abort failed, xid = 0x%x\n",
io_req->xid); /* * Cleanup firmware residuals before returning control back * to SCSI ML.
*/
rc = bnx2fc_abts_cleanup(io_req); goto done;
} else { /* * We come here even when there was a race condition * between timeout and abts completion, and abts * completion happens just in time.
*/
BNX2FC_IO_DBG(io_req, "abort succeeded\n");
rc = SUCCESS;
bnx2fc_scsi_done(io_req, DID_ABORT);
kref_put(&io_req->refcount, bnx2fc_cmd_release);
}
done: /* release the reference taken in eh_abort */
kref_put(&io_req->refcount, bnx2fc_cmd_release);
spin_unlock_bh(&tgt->tgt_lock); return rc;
}
if (rc)
printk(KERN_ERR PFX "clnup_compl: Unable to send SRR" " IO will abort\n");
seq_clnp_req->cb_arg = NULL;
kref_put(&orig_io_req->refcount, bnx2fc_cmd_release);
free_cb_arg:
kfree(cb_arg); return;
}
void bnx2fc_process_cleanup_compl(struct bnx2fc_cmd *io_req, struct fcoe_task_ctx_entry *task,
u8 num_rq)
{
BNX2FC_IO_DBG(io_req, "Entered process_cleanup_compl " "refcnt = %d, cmd_type = %d\n",
kref_read(&io_req->refcount), io_req->cmd_type); /* * Test whether there is a cleanup request pending. If not just * exit.
*/ if (!test_and_clear_bit(BNX2FC_FLAG_ISSUE_CLEANUP_REQ,
&io_req->req_flags)) return; /* * If we receive a cleanup completion for this request then the * firmware will not give us an abort completion for this request * so clear any ABTS pending flags.
*/ if (test_bit(BNX2FC_FLAG_ISSUE_ABTS, &io_req->req_flags) &&
!test_bit(BNX2FC_FLAG_ABTS_DONE, &io_req->req_flags)) {
set_bit(BNX2FC_FLAG_ABTS_DONE, &io_req->req_flags); if (io_req->wait_for_abts_comp)
complete(&io_req->abts_done);
}
bnx2fc_scsi_done(io_req, DID_ERROR);
kref_put(&io_req->refcount, bnx2fc_cmd_release); if (io_req->wait_for_cleanup_comp)
complete(&io_req->cleanup_done);
}
if (test_and_set_bit(BNX2FC_FLAG_ABTS_DONE,
&io_req->req_flags)) {
BNX2FC_IO_DBG(io_req, "Timer context finished processing" " this io\n"); return;
}
/* * If we receive an ABTS completion here then we will not receive * a cleanup completion so clear any cleanup pending flags.
*/ if (test_bit(BNX2FC_FLAG_ISSUE_CLEANUP_REQ, &io_req->req_flags)) {
clear_bit(BNX2FC_FLAG_ISSUE_CLEANUP_REQ, &io_req->req_flags); if (io_req->wait_for_cleanup_comp)
complete(&io_req->cleanup_done);
}
/* Do not issue RRQ as this IO is already cleanedup */ if (test_and_set_bit(BNX2FC_FLAG_IO_CLEANUP,
&io_req->req_flags)) goto io_compl;
/* * For ABTS issued due to SCSI eh_abort_handler, timeout * values are maintained by scsi-ml itself. Cancel timeout * in case ABTS issued as part of task management function * or due to FW error.
*/ if (test_bit(BNX2FC_FLAG_ISSUE_ABTS, &io_req->req_flags)) if (cancel_delayed_work(&io_req->timeout_work))
kref_put(&io_req->refcount,
bnx2fc_cmd_release); /* drop timer hold */
switch (r_ctl) { case FC_RCTL_BA_ACC: /* * Dont release this cmd yet. It will be relesed * after we get RRQ response
*/
BNX2FC_IO_DBG(io_req, "ABTS response - ACC Send RRQ\n");
issue_rrq = 1; break;
if (issue_rrq) {
BNX2FC_IO_DBG(io_req, "Issue RRQ after R_A_TOV\n");
set_bit(BNX2FC_FLAG_ISSUE_RRQ, &io_req->req_flags);
}
set_bit(BNX2FC_FLAG_RETIRE_OXID, &io_req->req_flags);
bnx2fc_cmd_timer_set(io_req, r_a_tov);
io_compl: if (io_req->wait_for_abts_comp) { if (test_and_clear_bit(BNX2FC_FLAG_EH_ABORT,
&io_req->req_flags))
complete(&io_req->abts_done);
} else { /* * We end up here when ABTS is issued as * in asynchronous context, i.e., as part * of task management completion, or * when FW error is received or when the * ABTS is issued when the IO is timed * out.
*/
/* called with tgt_lock held */
BNX2FC_IO_DBG(io_req, "Entered bnx2fc_lun_reset_cmpl\n"); /* * Walk thru the active_ios queue and ABORT the IO * that matches with the LUN that was reset
*/
list_for_each_entry_safe(cmd, tmp, &tgt->active_cmd_queue, link) {
BNX2FC_TGT_DBG(tgt, "LUN RST cmpl: scan for pending IOs\n"); if (!cmd->sc_cmd) continue;
lun = cmd->sc_cmd->device->lun; if (lun == tm_req->tm_lun) { /* Initiate ABTS on this cmd */ if (!test_and_set_bit(BNX2FC_FLAG_ISSUE_ABTS,
&cmd->req_flags)) { /* cancel the IO timeout */ if (cancel_delayed_work(&io_req->timeout_work))
kref_put(&io_req->refcount,
bnx2fc_cmd_release); /* timer hold */
rc = bnx2fc_initiate_abts(cmd); /* abts shouldn't fail in this context */
WARN_ON(rc != SUCCESS);
} else
printk(KERN_ERR PFX "lun_rst: abts already in" " progress for this IO 0x%x\n",
cmd->xid);
}
}
}
/* called with tgt_lock held */
BNX2FC_IO_DBG(io_req, "Entered bnx2fc_tgt_reset_cmpl\n"); /* * Walk thru the active_ios queue and ABORT the IO * that matches with the LUN that was reset
*/
list_for_each_entry_safe(cmd, tmp, &tgt->active_cmd_queue, link) {
BNX2FC_TGT_DBG(tgt, "TGT RST cmpl: scan for pending IOs\n"); /* Initiate ABTS */ if (!test_and_set_bit(BNX2FC_FLAG_ISSUE_ABTS,
&cmd->req_flags)) { /* cancel the IO timeout */ if (cancel_delayed_work(&io_req->timeout_work))
kref_put(&io_req->refcount,
bnx2fc_cmd_release); /* timer hold */
rc = bnx2fc_initiate_abts(cmd); /* abts shouldn't fail in this context */
WARN_ON(rc != SUCCESS);
} else
printk(KERN_ERR PFX "tgt_rst: abts already in progress" " for this IO 0x%x\n", cmd->xid);
}
}
printk(KERN_ERR PFX "Command not on active_cmd_queue!\n"); return;
}
kref_put(&io_req->refcount, bnx2fc_cmd_release); if (io_req->wait_for_abts_comp) {
BNX2FC_IO_DBG(io_req, "tm_compl - wake up the waiter\n");
complete(&io_req->abts_done);
}
}
staticint bnx2fc_split_bd(struct bnx2fc_cmd *io_req, u64 addr, int sg_len, int bd_index)
{ struct fcoe_bd_ctx *bd = io_req->bd_tbl->bd_tbl; int frag_size, sg_frags;
/* * Return the command to ML if BD count exceeds the max number * that can be handled by FW.
*/ if (bd_count > BNX2FC_FW_MAX_BDS_PER_CMD) {
pr_err("bd_count = %d exceeded FW supported max BD(255), task_id = 0x%x\n",
bd_count, io_req->xid); return -ENOMEM;
}
/* * Use dma_unmap_sg directly to ensure we're using the correct * dev struct off of pcidev.
*/ if (io_req->bd_tbl->bd_valid && sc && scsi_sg_count(sc)) {
dma_unmap_sg(&hba->pcidev->dev, scsi_sglist(sc),
scsi_sg_count(sc), sc->sc_data_direction);
io_req->bd_tbl->bd_valid = 0;
}
}
/* Fetch fcp_rsp_info and fcp_sns_info if available */ if (num_rq) {
/* * We do not anticipate num_rq >1, as the linux defined * SCSI_SENSE_BUFFERSIZE is 96 bytes + 8 bytes of FCP_RSP_INFO * 256 bytes of single rq buffer is good enough to hold this.
*/
if (rq_buff_len > num_rq * BNX2FC_RQ_BUF_SZ) { /* Invalid sense sense length. */
printk(KERN_ERR PFX "invalid sns length %d\n",
rq_buff_len); /* reset rq_buff_len */
rq_buff_len = num_rq * BNX2FC_RQ_BUF_SZ;
}
/* fetch fcp_rsp_code */ if ((fcp_rsp_len == 4) || (fcp_rsp_len == 8)) { /* Only for task management function */
io_req->fcp_rsp_code = rq_data[3];
BNX2FC_IO_DBG(io_req, "fcp_rsp_code = %d\n",
io_req->fcp_rsp_code);
}
/* fetch sense data */
rq_data += fcp_rsp_len;
if (fcp_sns_len > SCSI_SENSE_BUFFERSIZE) {
printk(KERN_ERR PFX "Truncating sense buffer\n");
fcp_sns_len = SCSI_SENSE_BUFFERSIZE;
}
memset(sc_cmd->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE); if (fcp_sns_len)
memcpy(sc_cmd->sense_buffer, rq_data, fcp_sns_len);
}
}
/** * bnx2fc_queuecommand - Queuecommand function of the scsi template * * @host: The Scsi_Host the command was issued to * @sc_cmd: struct scsi_cmnd to be executed * * This is the IO strategy routine, called by SCSI-ML
**/ int bnx2fc_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *sc_cmd)
{ struct fc_lport *lport = shost_priv(host); struct fc_rport *rport = starget_to_rport(scsi_target(sc_cmd->device)); struct fc_rport_libfc_priv *rp = rport->dd_data; struct bnx2fc_rport *tgt; struct bnx2fc_cmd *io_req; int rc = 0; int rval;
/* rport and tgt are allocated together, so tgt should be non-NULL */
tgt = (struct bnx2fc_rport *)&rp[1];
if (!test_bit(BNX2FC_FLAG_SESSION_READY, &tgt->flags)) { /* * Session is not offloaded yet. Let SCSI-ml retry * the command.
*/
rc = SCSI_MLQUEUE_TARGET_BUSY; goto exit_qcmd;
} if (tgt->retry_delay_timestamp) { if (time_after(jiffies, tgt->retry_delay_timestamp)) {
tgt->retry_delay_timestamp = 0;
} else { /* If retry_delay timer is active, flow off the ML */
rc = SCSI_MLQUEUE_TARGET_BUSY; goto exit_qcmd;
}
}
if (test_and_set_bit(BNX2FC_FLAG_IO_COMPL, &io_req->req_flags)) { /* we will not receive ABTS response for this IO */
BNX2FC_IO_DBG(io_req, "Timer context finished processing " "this scsi cmd\n"); if (test_and_clear_bit(BNX2FC_FLAG_IO_CLEANUP,
&io_req->req_flags)) {
BNX2FC_IO_DBG(io_req, "Actual completion after cleanup request cleaning up\n");
bnx2fc_process_cleanup_compl(io_req, task, num_rq);
} return;
}
/* Cancel the timeout_work, as we received IO completion */ if (cancel_delayed_work(&io_req->timeout_work))
kref_put(&io_req->refcount,
bnx2fc_cmd_release); /* drop timer hold */
sc_cmd = io_req->sc_cmd; if (sc_cmd == NULL) {
printk(KERN_ERR PFX "scsi_cmd_compl - sc_cmd is NULL\n"); return;
}
/* Fetch fcp_rsp from task context and perform cmd completion */
fcp_rsp = (struct fcoe_fcp_rsp_payload *)
&(task->rxwr_only.union_ctx.comp_info.fcp_rsp.payload);
/* parse fcp_rsp and obtain sense data from RQ if available */
bnx2fc_parse_fcp_rsp(io_req, fcp_rsp, num_rq, rq_data);
if (!bnx2fc_priv(sc_cmd)->io_req) {
printk(KERN_ERR PFX "io_req is NULL\n"); return;
}
if (io_req->on_active_queue) {
list_del_init(&io_req->link);
io_req->on_active_queue = 0; /* Move IO req to retire queue */
list_add_tail(&io_req->link, &tgt->io_retire_queue);
} else { /* This should not happen, but could have been pulled * by bnx2fc_flush_active_ios(), or during a race * between command abort and (late) completion.
*/
BNX2FC_IO_DBG(io_req, "xid not on active_cmd_queue\n"); if (io_req->wait_for_abts_comp) if (test_and_clear_bit(BNX2FC_FLAG_EH_ABORT,
&io_req->req_flags))
complete(&io_req->abts_done);
}
/* Build buffer descriptor list for firmware from sg list */ if (bnx2fc_build_bd_list_from_sg(io_req)) {
printk(KERN_ERR PFX "BD list creation failed\n");
kref_put(&io_req->refcount, bnx2fc_cmd_release); return -EAGAIN;
}
task_idx = xid / BNX2FC_TASKS_PER_PAGE;
index = xid % BNX2FC_TASKS_PER_PAGE;
/* Initialize task context for this IO request */
task_page = (struct fcoe_task_ctx_entry *) hba->task_ctx[task_idx];
task = &(task_page[index]);
bnx2fc_init_task(io_req, task);
if (tgt->flush_in_prog) {
printk(KERN_ERR PFX "Flush in progress..Host Busy\n");
kref_put(&io_req->refcount, bnx2fc_cmd_release); return -EAGAIN;
}
if (!test_bit(BNX2FC_FLAG_SESSION_READY, &tgt->flags)) {
printk(KERN_ERR PFX "Session not ready...post_io\n");
kref_put(&io_req->refcount, bnx2fc_cmd_release); return -EAGAIN;
}
/* Time IO req */ if (tgt->io_timeout)
bnx2fc_cmd_timer_set(io_req, BNX2FC_IO_TIMEOUT); /* Obtain free SQ entry */
bnx2fc_add_2_sq(tgt, xid);
/* Enqueue the io_req to active_cmd_queue */
io_req->on_active_queue = 1; /* move io_req from pending_queue to active_queue */
list_add_tail(&io_req->link, &tgt->active_cmd_queue);
/* Ring doorbell */
bnx2fc_ring_doorbell(tgt); return 0;
}
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.