The fc_rport_priv structure is reference counted, so we need to ensure that the reference is increased before accessing the structure. Signed-off-by: Hannes Reinecke <hare@xxxxxxxx> --- drivers/scsi/qedf/qedf_els.c | 12 ++++++++++-- drivers/scsi/qedf/qedf_io.c | 32 ++++++++++++++++++++++---------- drivers/scsi/qedf/qedf_main.c | 5 +++++ 3 files changed, 37 insertions(+), 12 deletions(-) diff --git a/drivers/scsi/qedf/qedf_els.c b/drivers/scsi/qedf/qedf_els.c index f93379a4510c..b028c1330ed8 100644 --- a/drivers/scsi/qedf/qedf_els.c +++ b/drivers/scsi/qedf/qedf_els.c @@ -356,12 +356,18 @@ void qedf_restart_rport(struct qedf_rport *fcport) spin_unlock_irqrestore(&fcport->rport_lock, flags); rdata = fcport->rdata; - if (rdata) { + if (rdata && !kref_get_unless_zero(&rdata->kref)) { + fcport->rdata = NULL; + rdata = NULL; + } + + if (rdata && rdata->rp_state == RPORT_ST_READY) { lport = fcport->qedf->lport; port_id = rdata->ids.port_id; QEDF_ERR(&(fcport->qedf->dbg_ctx), "LOGO port_id=%x.\n", port_id); fc_rport_logoff(rdata); + kref_put(&rdata->kref, fc_rport_destroy); /* Recreate the rport and log back in */ mutex_lock(&lport->disc.disc_mutex); rdata = fc_rport_create(lport, port_id); @@ -369,8 +375,10 @@ void qedf_restart_rport(struct qedf_rport *fcport) mutex_unlock(&lport->disc.disc_mutex); fc_rport_login(rdata); fcport->rdata = rdata; - } else + } else { mutex_unlock(&lport->disc.disc_mutex); + fcport->rdata = NULL; + } } clear_bit(QEDF_RPORT_IN_RESET, &fcport->flags); } diff --git a/drivers/scsi/qedf/qedf_io.c b/drivers/scsi/qedf/qedf_io.c index 6bbc38b1b465..9086f10b24bf 100644 --- a/drivers/scsi/qedf/qedf_io.c +++ b/drivers/scsi/qedf/qedf_io.c @@ -1560,34 +1560,39 @@ int qedf_initiate_abts(struct qedf_ioreq *io_req, bool return_scsi_cmd_on_abts) goto abts_err; } + qedf = fcport->qedf; rdata = fcport->rdata; + if (!rdata || !kref_get_unless_zero(&rdata->kref)) { + QEDF_ERR(&(qedf->dbg_ctx), "stale rport\n"); + rc = 1; + goto abts_err; + } r_a_tov = rdata->r_a_tov; - qedf = fcport->qedf; lport = qedf->lport; if (lport->state != LPORT_ST_READY || !(lport->link_up)) { QEDF_ERR(&(qedf->dbg_ctx), "link is not ready\n"); rc = 1; - goto abts_err; + goto drop_kref; } if (atomic_read(&qedf->link_down_tmo_valid) > 0) { QEDF_ERR(&(qedf->dbg_ctx), "link_down_tmo active.\n"); rc = 1; - goto abts_err; + goto drop_kref; } /* Ensure room on SQ */ if (!atomic_read(&fcport->free_sqes)) { QEDF_ERR(&(qedf->dbg_ctx), "No SQ entries available\n"); rc = 1; - goto abts_err; + goto drop_kref; } if (test_bit(QEDF_RPORT_UPLOADING_CONNECTION, &fcport->flags)) { QEDF_ERR(&qedf->dbg_ctx, "fcport is uploading.\n"); - rc = 1; - goto out; + kref_put(&rdata->kref, fc_rport_destroy); + return 1; } if (!test_bit(QEDF_CMD_OUTSTANDING, &io_req->flags) || @@ -1596,8 +1601,8 @@ int qedf_initiate_abts(struct qedf_ioreq *io_req, bool return_scsi_cmd_on_abts) QEDF_ERR(&(qedf->dbg_ctx), "io_req xid=0x%x already in " "cleanup or abort processing or already " "completed.\n", io_req->xid); - rc = 1; - goto out; + kref_put(&rdata->kref, fc_rport_destroy); + return 1; } kref_get(&io_req->refcount); @@ -1632,13 +1637,14 @@ int qedf_initiate_abts(struct qedf_ioreq *io_req, bool return_scsi_cmd_on_abts) spin_unlock_irqrestore(&fcport->rport_lock, flags); return rc; +drop_kref: + kref_put(&rdata->kref, fc_rport_destroy); abts_err: /* * If the ABTS task fails to queue then we need to cleanup the * task at the firmware. */ qedf_initiate_cleanup(io_req, return_scsi_cmd_on_abts); -out: return rc; } @@ -1925,6 +1931,7 @@ static int qedf_execute_tmf(struct qedf_rport *fcport, struct scsi_cmnd *sc_cmd, unsigned long flags; struct fcoe_wqe *sqe; u16 sqe_idx; + struct fc_rport_priv *rdata = fcport->rdata; if (!sc_cmd) { QEDF_ERR(&(qedf->dbg_ctx), "invalid arg\n"); @@ -1937,8 +1944,12 @@ static int qedf_execute_tmf(struct qedf_rport *fcport, struct scsi_cmnd *sc_cmd, return FAILED; } + if (!rdata || !kref_get_unless_zero(&rdata->kref)) { + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_SCSI_TM, "stale rport\n"); + return FAILED; + } QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_SCSI_TM, "portid = 0x%x " - "tm_flags = %d\n", fcport->rdata->ids.port_id, tm_flags); + "tm_flags = %d\n", rdata->ids.port_id, tm_flags); io_req = qedf_alloc_cmd(fcport, QEDF_TASK_MGMT_CMD); if (!io_req) { @@ -2019,6 +2030,7 @@ static int qedf_execute_tmf(struct qedf_rport *fcport, struct scsi_cmnd *sc_cmd, rc = SUCCESS; } reset_tmf_err: + kref_put(&rdata->kref, fc_rport_destroy); return rc; } diff --git a/drivers/scsi/qedf/qedf_main.c b/drivers/scsi/qedf/qedf_main.c index 2bedad13f3f4..91d724678e15 100644 --- a/drivers/scsi/qedf/qedf_main.c +++ b/drivers/scsi/qedf/qedf_main.c @@ -1215,6 +1215,8 @@ static void qedf_upload_connection(struct qedf_ctx *qedf, static void qedf_cleanup_fcport(struct qedf_ctx *qedf, struct qedf_rport *fcport) { + struct fc_rport_priv *rdata = fcport->rdata; + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_CONN, "Cleaning up portid=%06x.\n", fcport->rdata->ids.port_id); @@ -1226,6 +1228,7 @@ static void qedf_cleanup_fcport(struct qedf_ctx *qedf, qedf_free_sq(qedf, fcport); fcport->rdata = NULL; fcport->qedf = NULL; + kref_put(&rdata->kref, fc_rport_destroy); } /** @@ -1301,6 +1304,8 @@ static void qedf_rport_event_handler(struct fc_lport *lport, break; } + /* Initial reference held on entry, so this can't fail */ + kref_get(&rdata->kref); fcport->rdata = rdata; fcport->rport = rport; -- 2.16.4