Commit 69ef2c69 authored by Saurav Kashyap's avatar Saurav Kashyap Committed by Martin K. Petersen

scsi: qedf: Modify abort and tmf handler to handle edge condition and flush

An I/O can be in any state when flush is called, it can be in abort,
waiting for abort, RRQ send and waiting or TMF send.

 - HZ can be different on different architecture, correctly set abort
   timeout value.

 - Flush can complete the I/Os prematurely, handle refcount for aborted
   I/Os and for which RRQ is pending.

 - Differentiate LUN/TARGET reset, as cleanup needs to be send to firmware
   accordingly.

 - Add flush mutex to sync cleanup call from abort and flush routine.

 - Clear abort/outstanding bit on timeout.
Signed-off-by: default avatarShyam Sundar <shyam.sundar@marvell.com>
Signed-off-by: default avatarChad Dupuis <cdupuis@marvell.com>
Signed-off-by: default avatarSaurav Kashyap <skashyap@marvell.com>
Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parent 5d5e5565
...@@ -49,8 +49,8 @@ ...@@ -49,8 +49,8 @@
sizeof(struct fc_frame_header)) sizeof(struct fc_frame_header))
#define QEDF_MAX_NPIV 64 #define QEDF_MAX_NPIV 64
#define QEDF_TM_TIMEOUT 10 #define QEDF_TM_TIMEOUT 10
#define QEDF_ABORT_TIMEOUT 10 #define QEDF_ABORT_TIMEOUT (10 * 1000)
#define QEDF_CLEANUP_TIMEOUT 10 #define QEDF_CLEANUP_TIMEOUT 1
#define QEDF_MAX_CDB_LEN 16 #define QEDF_MAX_CDB_LEN 16
#define UPSTREAM_REMOVE 1 #define UPSTREAM_REMOVE 1
...@@ -82,6 +82,7 @@ struct qedf_els_cb_arg { ...@@ -82,6 +82,7 @@ struct qedf_els_cb_arg {
}; };
enum qedf_ioreq_event { enum qedf_ioreq_event {
QEDF_IOREQ_EV_NONE,
QEDF_IOREQ_EV_ABORT_SUCCESS, QEDF_IOREQ_EV_ABORT_SUCCESS,
QEDF_IOREQ_EV_ABORT_FAILED, QEDF_IOREQ_EV_ABORT_FAILED,
QEDF_IOREQ_EV_SEND_RRQ, QEDF_IOREQ_EV_SEND_RRQ,
...@@ -182,7 +183,10 @@ struct qedf_rport { ...@@ -182,7 +183,10 @@ struct qedf_rport {
#define QEDF_RPORT_SESSION_READY 1 #define QEDF_RPORT_SESSION_READY 1
#define QEDF_RPORT_UPLOADING_CONNECTION 2 #define QEDF_RPORT_UPLOADING_CONNECTION 2
#define QEDF_RPORT_IN_RESET 3 #define QEDF_RPORT_IN_RESET 3
#define QEDF_RPORT_IN_LUN_RESET 4
#define QEDF_RPORT_IN_TARGET_RESET 5
unsigned long flags; unsigned long flags;
int lun_reset_lun;
unsigned long retry_delay_timestamp; unsigned long retry_delay_timestamp;
struct fc_rport *rport; struct fc_rport *rport;
struct fc_rport_priv *rdata; struct fc_rport_priv *rdata;
...@@ -395,6 +399,8 @@ struct qedf_ctx { ...@@ -395,6 +399,8 @@ struct qedf_ctx {
u8 target_resets; u8 target_resets;
u8 task_set_fulls; u8 task_set_fulls;
u8 busy; u8 busy;
/* Used for flush routine */
struct mutex flush_mutex;
}; };
struct io_bdt { struct io_bdt {
......
...@@ -201,8 +201,12 @@ static void qedf_rrq_compl(struct qedf_els_cb_arg *cb_arg) ...@@ -201,8 +201,12 @@ static void qedf_rrq_compl(struct qedf_els_cb_arg *cb_arg)
" orig xid = 0x%x, rrq_xid = 0x%x, refcount=%d\n", " orig xid = 0x%x, rrq_xid = 0x%x, refcount=%d\n",
orig_io_req, orig_io_req->xid, rrq_req->xid, refcount); orig_io_req, orig_io_req->xid, rrq_req->xid, refcount);
/* This should return the aborted io_req to the command pool */ /*
if (orig_io_req) * This should return the aborted io_req to the command pool. Note that
* we need to check the refcound in case the original request was
* flushed but we get a completion on this xid.
*/
if (orig_io_req && refcount > 0)
kref_put(&orig_io_req->refcount, qedf_release_cmd); kref_put(&orig_io_req->refcount, qedf_release_cmd);
out_free: out_free:
...@@ -229,6 +233,7 @@ int qedf_send_rrq(struct qedf_ioreq *aborted_io_req) ...@@ -229,6 +233,7 @@ int qedf_send_rrq(struct qedf_ioreq *aborted_io_req)
uint32_t sid; uint32_t sid;
uint32_t r_a_tov; uint32_t r_a_tov;
int rc; int rc;
int refcount;
if (!aborted_io_req) { if (!aborted_io_req) {
QEDF_ERR(NULL, "abort_io_req is NULL.\n"); QEDF_ERR(NULL, "abort_io_req is NULL.\n");
...@@ -237,6 +242,15 @@ int qedf_send_rrq(struct qedf_ioreq *aborted_io_req) ...@@ -237,6 +242,15 @@ int qedf_send_rrq(struct qedf_ioreq *aborted_io_req)
fcport = aborted_io_req->fcport; fcport = aborted_io_req->fcport;
if (!fcport) {
refcount = kref_read(&aborted_io_req->refcount);
QEDF_ERR(NULL,
"RRQ work was queued prior to a flush xid=0x%x, refcount=%d.\n",
aborted_io_req->xid, refcount);
kref_put(&aborted_io_req->refcount, qedf_release_cmd);
return -EINVAL;
}
/* Check that fcport is still offloaded */ /* Check that fcport is still offloaded */
if (!test_bit(QEDF_RPORT_SESSION_READY, &fcport->flags)) { if (!test_bit(QEDF_RPORT_SESSION_READY, &fcport->flags)) {
QEDF_ERR(NULL, "fcport is no longer offloaded.\n"); QEDF_ERR(NULL, "fcport is no longer offloaded.\n");
...@@ -249,6 +263,19 @@ int qedf_send_rrq(struct qedf_ioreq *aborted_io_req) ...@@ -249,6 +263,19 @@ int qedf_send_rrq(struct qedf_ioreq *aborted_io_req)
} }
qedf = fcport->qedf; qedf = fcport->qedf;
/*
* Sanity check that we can send a RRQ to make sure that refcount isn't
* 0
*/
refcount = kref_read(&aborted_io_req->refcount);
if (refcount != 1) {
QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_ELS,
"refcount for xid=%x io_req=%p refcount=%d is not 1.\n",
aborted_io_req->xid, aborted_io_req, refcount);
return -EINVAL;
}
lport = qedf->lport; lport = qedf->lport;
sid = fcport->sid; sid = fcport->sid;
r_a_tov = lport->r_a_tov; r_a_tov = lport->r_a_tov;
......
This diff is collapsed.
...@@ -615,50 +615,113 @@ static struct scsi_transport_template *qedf_fc_vport_transport_template; ...@@ -615,50 +615,113 @@ static struct scsi_transport_template *qedf_fc_vport_transport_template;
static int qedf_eh_abort(struct scsi_cmnd *sc_cmd) static int qedf_eh_abort(struct scsi_cmnd *sc_cmd)
{ {
struct fc_rport *rport = starget_to_rport(scsi_target(sc_cmd->device)); struct fc_rport *rport = starget_to_rport(scsi_target(sc_cmd->device));
struct fc_rport_libfc_priv *rp = rport->dd_data;
struct qedf_rport *fcport;
struct fc_lport *lport; struct fc_lport *lport;
struct qedf_ctx *qedf; struct qedf_ctx *qedf;
struct qedf_ioreq *io_req; struct qedf_ioreq *io_req;
struct fc_rport_libfc_priv *rp = rport->dd_data;
struct fc_rport_priv *rdata;
struct qedf_rport *fcport = NULL;
int rc = FAILED; int rc = FAILED;
int wait_count = 100;
int refcount = 0;
int rval; int rval;
int got_ref = 0;
if (fc_remote_port_chkready(rport)) {
QEDF_ERR(NULL, "rport not ready\n");
goto out;
}
lport = shost_priv(sc_cmd->device->host); lport = shost_priv(sc_cmd->device->host);
qedf = (struct qedf_ctx *)lport_priv(lport); qedf = (struct qedf_ctx *)lport_priv(lport);
if ((lport->state != LPORT_ST_READY) || !(lport->link_up)) { /* rport and tgt are allocated together, so tgt should be non-NULL */
QEDF_ERR(&(qedf->dbg_ctx), "link not ready.\n"); fcport = (struct qedf_rport *)&rp[1];
rdata = fcport->rdata;
if (!rdata || !kref_get_unless_zero(&rdata->kref)) {
QEDF_ERR(&qedf->dbg_ctx, "stale rport, sc_cmd=%p\n", sc_cmd);
rc = 1;
goto out; goto out;
} }
fcport = (struct qedf_rport *)&rp[1];
io_req = (struct qedf_ioreq *)sc_cmd->SCp.ptr; io_req = (struct qedf_ioreq *)sc_cmd->SCp.ptr;
if (!io_req) { if (!io_req) {
QEDF_ERR(&(qedf->dbg_ctx), "io_req is NULL.\n"); QEDF_ERR(&qedf->dbg_ctx,
"sc_cmd not queued with lld, sc_cmd=%p op=0x%02x, port_id=%06x\n",
sc_cmd, sc_cmd->cmnd[0],
rdata->ids.port_id);
rc = SUCCESS; rc = SUCCESS;
goto out; goto drop_rdata_kref;
} }
QEDF_ERR(&(qedf->dbg_ctx), "Aborting io_req sc_cmd=%p xid=0x%x " rval = kref_get_unless_zero(&io_req->refcount); /* ID: 005 */
"fp_idx=%d.\n", sc_cmd, io_req->xid, io_req->fp_idx); if (rval)
got_ref = 1;
/* If we got a valid io_req, confirm it belongs to this sc_cmd. */
if (!rval || io_req->sc_cmd != sc_cmd) {
QEDF_ERR(&qedf->dbg_ctx,
"Freed/Incorrect io_req, io_req->sc_cmd=%p, sc_cmd=%p, port_id=%06x, bailing out.\n",
io_req->sc_cmd, sc_cmd, rdata->ids.port_id);
goto drop_rdata_kref;
}
if (fc_remote_port_chkready(rport)) {
refcount = kref_read(&io_req->refcount);
QEDF_ERR(&qedf->dbg_ctx,
"rport not ready, io_req=%p, xid=0x%x sc_cmd=%p op=0x%02x, refcount=%d, port_id=%06x\n",
io_req, io_req->xid, sc_cmd, sc_cmd->cmnd[0],
refcount, rdata->ids.port_id);
goto drop_rdata_kref;
}
rc = fc_block_scsi_eh(sc_cmd);
if (rc)
goto drop_rdata_kref;
if (test_bit(QEDF_RPORT_UPLOADING_CONNECTION, &fcport->flags)) {
QEDF_ERR(&qedf->dbg_ctx,
"Connection uploading, xid=0x%x., port_id=%06x\n",
io_req->xid, rdata->ids.port_id);
while (io_req->sc_cmd && (wait_count != 0)) {
msleep(100);
wait_count--;
}
if (wait_count) {
QEDF_ERR(&qedf->dbg_ctx, "ABTS succeeded\n");
rc = SUCCESS;
} else {
QEDF_ERR(&qedf->dbg_ctx, "ABTS failed\n");
rc = FAILED;
}
goto drop_rdata_kref;
}
if (lport->state != LPORT_ST_READY || !(lport->link_up)) {
QEDF_ERR(&qedf->dbg_ctx, "link not ready.\n");
goto drop_rdata_kref;
}
QEDF_ERR(&qedf->dbg_ctx,
"Aborting io_req=%p sc_cmd=%p xid=0x%x fp_idx=%d, port_id=%06x.\n",
io_req, sc_cmd, io_req->xid, io_req->fp_idx,
rdata->ids.port_id);
if (qedf->stop_io_on_error) { if (qedf->stop_io_on_error) {
qedf_stop_all_io(qedf); qedf_stop_all_io(qedf);
rc = SUCCESS; rc = SUCCESS;
goto out; goto drop_rdata_kref;
} }
init_completion(&io_req->abts_done); init_completion(&io_req->abts_done);
rval = qedf_initiate_abts(io_req, true); rval = qedf_initiate_abts(io_req, true);
if (rval) { if (rval) {
QEDF_ERR(&(qedf->dbg_ctx), "Failed to queue ABTS.\n"); QEDF_ERR(&(qedf->dbg_ctx), "Failed to queue ABTS.\n");
goto out; /*
* If we fail to queue the ABTS then return this command to
* the SCSI layer as it will own and free the xid
*/
rc = SUCCESS;
qedf_scsi_done(qedf, io_req, DID_ERROR);
goto drop_rdata_kref;
} }
wait_for_completion(&io_req->abts_done); wait_for_completion(&io_req->abts_done);
...@@ -684,19 +747,27 @@ static int qedf_eh_abort(struct scsi_cmnd *sc_cmd) ...@@ -684,19 +747,27 @@ static int qedf_eh_abort(struct scsi_cmnd *sc_cmd)
QEDF_ERR(&(qedf->dbg_ctx), "ABTS failed, xid=0x%x.\n", QEDF_ERR(&(qedf->dbg_ctx), "ABTS failed, xid=0x%x.\n",
io_req->xid); io_req->xid);
drop_rdata_kref:
kref_put(&rdata->kref, fc_rport_destroy);
out: out:
if (got_ref)
kref_put(&io_req->refcount, qedf_release_cmd);
return rc; return rc;
} }
static int qedf_eh_target_reset(struct scsi_cmnd *sc_cmd) static int qedf_eh_target_reset(struct scsi_cmnd *sc_cmd)
{ {
QEDF_ERR(NULL, "TARGET RESET Issued..."); QEDF_ERR(NULL, "%d:0:%d:%lld: TARGET RESET Issued...",
sc_cmd->device->host->host_no, sc_cmd->device->id,
sc_cmd->device->lun);
return qedf_initiate_tmf(sc_cmd, FCP_TMF_TGT_RESET); return qedf_initiate_tmf(sc_cmd, FCP_TMF_TGT_RESET);
} }
static int qedf_eh_device_reset(struct scsi_cmnd *sc_cmd) static int qedf_eh_device_reset(struct scsi_cmnd *sc_cmd)
{ {
QEDF_ERR(NULL, "LUN RESET Issued...\n"); QEDF_ERR(NULL, "%d:0:%d:%lld: LUN RESET Issued... ",
sc_cmd->device->host->host_no, sc_cmd->device->id,
sc_cmd->device->lun);
return qedf_initiate_tmf(sc_cmd, FCP_TMF_LUN_RESET); return qedf_initiate_tmf(sc_cmd, FCP_TMF_LUN_RESET);
} }
...@@ -740,22 +811,6 @@ static int qedf_eh_host_reset(struct scsi_cmnd *sc_cmd) ...@@ -740,22 +811,6 @@ static int qedf_eh_host_reset(struct scsi_cmnd *sc_cmd)
{ {
struct fc_lport *lport; struct fc_lport *lport;
struct qedf_ctx *qedf; struct qedf_ctx *qedf;
struct fc_rport *rport = starget_to_rport(scsi_target(sc_cmd->device));
struct fc_rport_libfc_priv *rp = rport->dd_data;
struct qedf_rport *fcport = (struct qedf_rport *)&rp[1];
int rval;
rval = fc_remote_port_chkready(rport);
if (rval) {
QEDF_ERR(NULL, "device_reset rport not ready\n");
return FAILED;
}
if (fcport == NULL) {
QEDF_ERR(NULL, "device_reset: rport is NULL\n");
return FAILED;
}
lport = shost_priv(sc_cmd->device->host); lport = shost_priv(sc_cmd->device->host);
qedf = lport_priv(lport); qedf = lport_priv(lport);
...@@ -3007,6 +3062,7 @@ static int __qedf_probe(struct pci_dev *pdev, int mode) ...@@ -3007,6 +3062,7 @@ static int __qedf_probe(struct pci_dev *pdev, int mode)
pci_set_drvdata(pdev, qedf); pci_set_drvdata(pdev, qedf);
init_completion(&qedf->fipvlan_compl); init_completion(&qedf->fipvlan_compl);
mutex_init(&qedf->stats_mutex); mutex_init(&qedf->stats_mutex);
mutex_init(&qedf->flush_mutex);
QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_INFO, QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_INFO,
"QLogic FastLinQ FCoE Module qedf %s, " "QLogic FastLinQ FCoE Module qedf %s, "
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment