Skip to content

Commit

Permalink
qla2xxx: Delete session if initiator is gone from FW
Browse files Browse the repository at this point in the history
1. Initiator A is logged in with fc_id(1)/loop_id(1)
2. Initiator A re-logs in with fc_id(2)/loop_id(2)
3. Part of old session deletion async logoout for 1/1 is queued
4. Initiator B logs in with fc_id(1)/loop_id(1), starts
   passing data and creates session.
5. Async logo from 3 is processed by DPC and sent to FW

Now initiator B has the session but is logged out from FW.

This condition is detected first with CTIO error 29 at which
point we should delete current session. During session
deletion we will send LOGO to initiator to force re-login.

Under rare circumstances initiator might be logged out of FW,
not have driver session, but still think it's logged in.
E.g. the above sequence plus session deletion due to re-config.
Incoming commands will fail to create local session because
initiator is not found in FW. In this case we also issue LOGO
to initiator to force him re-login.

Finally this patch fixes exchange leak when commands where
received in logged out state. In this case loop_id must be
set to FFFF when corresponding exchange is terminated. The
patch modifies exchange termination to always use FFFF,
since in certain scenarios it's impossible to tell whether
command was received in logged in or logged out state.

Signed-off-by: Alexei Potashnik <alexei@purestorage.com>
Acked-by: Quinn Tran <quinn.tran@qlogic.com>
Signed-off-by: Himanshu Madhani <himanshu.madhani@qlogic.com>
Reviewed-by: Hannes Reinecke <hare@suse.com>
Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
  • Loading branch information
Alexei Potashnik authored and Nicholas Bellinger committed Jan 7, 2016
1 parent 6eb5471 commit 71cdc07
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 16 deletions.
2 changes: 2 additions & 0 deletions drivers/scsi/qla2xxx/qla_def.h
Original file line number Diff line number Diff line change
Expand Up @@ -3652,6 +3652,8 @@ typedef struct scsi_qla_host {
atomic_t generation_tick;
/* Time when global fcport update has been scheduled */
int total_fcport_update_gen;
/* List of pending LOGOs, protected by tgt_mutex */
struct list_head logo_list;

uint32_t vp_abort_cnt;

Expand Down
1 change: 1 addition & 0 deletions drivers/scsi/qla2xxx/qla_os.c
Original file line number Diff line number Diff line change
Expand Up @@ -3920,6 +3920,7 @@ struct scsi_qla_host *qla2x00_create_host(struct scsi_host_template *sht,
INIT_LIST_HEAD(&vha->list);
INIT_LIST_HEAD(&vha->qla_cmd_list);
INIT_LIST_HEAD(&vha->qla_sess_op_cmd_list);
INIT_LIST_HEAD(&vha->logo_list);

spin_lock_init(&vha->work_lock);
spin_lock_init(&vha->cmd_list_lock);
Expand Down
108 changes: 92 additions & 16 deletions drivers/scsi/qla2xxx/qla_target.c
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,52 @@ void qlt_response_pkt_all_vps(struct scsi_qla_host *vha, response_t *pkt)

}

typedef struct {
/* These fields must be initialized by the caller */
port_id_t id;
/*
* number of cmds dropped while we were waiting for
* initiator to ack LOGO initialize to 1 if LOGO is
* triggered by a command, otherwise, to 0
*/
int cmd_count;

/* These fields are used by callee */
struct list_head list;
} qlt_port_logo_t;

static void
qlt_send_first_logo(struct scsi_qla_host *vha, qlt_port_logo_t *logo)
{
qlt_port_logo_t *tmp;
int res;

mutex_lock(&vha->vha_tgt.tgt_mutex);

list_for_each_entry(tmp, &vha->logo_list, list) {
if (tmp->id.b24 == logo->id.b24) {
tmp->cmd_count += logo->cmd_count;
mutex_unlock(&vha->vha_tgt.tgt_mutex);
return;
}
}

list_add_tail(&logo->list, &vha->logo_list);

mutex_unlock(&vha->vha_tgt.tgt_mutex);

res = qla24xx_els_dcmd_iocb(vha, ELS_DCMD_LOGO, logo->id);

mutex_lock(&vha->vha_tgt.tgt_mutex);
list_del(&logo->list);
mutex_unlock(&vha->vha_tgt.tgt_mutex);

dev_info(&vha->hw->pdev->dev,
"Finished LOGO to %02x:%02x:%02x, dropped %d cmds, res = %#x\n",
logo->id.b.domain, logo->id.b.area, logo->id.b.al_pa,
logo->cmd_count, res);
}

static void qlt_free_session_done(struct work_struct *work)
{
struct qla_tgt_sess *sess = container_of(work, struct qla_tgt_sess,
Expand All @@ -402,14 +448,21 @@ static void qlt_free_session_done(struct work_struct *work)

ql_dbg(ql_dbg_tgt_mgt, vha, 0xf084,
"%s: se_sess %p / sess %p from port %8phC loop_id %#04x"
" s_id %02x:%02x:%02x logout %d keep %d plogi %d\n",
" s_id %02x:%02x:%02x logout %d keep %d plogi %d els_logo %d\n",
__func__, sess->se_sess, sess, sess->port_name, sess->loop_id,
sess->s_id.b.domain, sess->s_id.b.area, sess->s_id.b.al_pa,
sess->logout_on_delete, sess->keep_nport_handle,
sess->plogi_ack_needed);
sess->plogi_ack_needed, sess->send_els_logo);

BUG_ON(!tgt);

if (sess->send_els_logo) {
qlt_port_logo_t logo;
logo.id = sess->s_id;
logo.cmd_count = 0;
qlt_send_first_logo(vha, &logo);
}

if (sess->logout_on_delete) {
int rc;

Expand Down Expand Up @@ -636,12 +689,12 @@ static int qla24xx_get_loop_id(struct scsi_qla_host *vha, const uint8_t *s_id,
ql_dbg(ql_dbg_tgt_mgt, vha, 0xf045,
"qla_target(%d): get_id_list() failed: %x\n",
vha->vp_idx, rc);
res = -1;
res = -EBUSY;
goto out_free_id_list;
}

id_iter = (char *)gid_list;
res = -1;
res = -ENOENT;
for (i = 0; i < entries; i++) {
struct gid_list_info *gid = (struct gid_list_info *)id_iter;
if ((gid->al_pa == s_id[2]) &&
Expand Down Expand Up @@ -2968,7 +3021,7 @@ static int __qlt_send_term_exchange(struct scsi_qla_host *vha,

ctio24 = (struct ctio7_to_24xx *)pkt;
ctio24->entry_type = CTIO_TYPE7;
ctio24->nport_handle = cmd ? cmd->loop_id : CTIO7_NHANDLE_UNRECOGNIZED;
ctio24->nport_handle = CTIO7_NHANDLE_UNRECOGNIZED;
ctio24->timeout = cpu_to_le16(QLA_TGT_TIMEOUT);
ctio24->vp_index = vha->vp_idx;
ctio24->initiator_id[0] = atio->u.isp24.fcp_hdr.s_id[2];
Expand Down Expand Up @@ -3404,13 +3457,26 @@ static void qlt_do_ctio_completion(struct scsi_qla_host *vha, uint32_t handle,

case CTIO_PORT_LOGGED_OUT:
case CTIO_PORT_UNAVAILABLE:
{
bool logged_out = (status & 0xFFFF);
ql_dbg(ql_dbg_tgt_mgt, vha, 0xf059,
"qla_target(%d): CTIO with PORT LOGGED "
"OUT (29) or PORT UNAVAILABLE (28) status %x "
"qla_target(%d): CTIO with %s status %x "
"received (state %x, se_cmd %p)\n", vha->vp_idx,
(logged_out == CTIO_PORT_LOGGED_OUT) ?
"PORT LOGGED OUT" : "PORT UNAVAILABLE",
status, cmd->state, se_cmd);
break;

if (logged_out && cmd->sess) {
/*
* Session is already logged out, but we need
* to notify initiator, who's not aware of this
*/
cmd->sess->logout_on_delete = 0;
cmd->sess->send_els_logo = 1;
qlt_schedule_sess_for_deletion(cmd->sess, true);
}
break;
}
case CTIO_SRR_RECEIVED:
ql_dbg(ql_dbg_tgt_mgt, vha, 0xf05a,
"qla_target(%d): CTIO with SRR_RECEIVED"
Expand Down Expand Up @@ -3698,10 +3764,8 @@ static void qlt_create_sess_from_atio(struct work_struct *work)
goto out_term;
}

mutex_lock(&vha->vha_tgt.tgt_mutex);
sess = qlt_make_local_sess(vha, s_id);
/* sess has an extra creation ref. */
mutex_unlock(&vha->vha_tgt.tgt_mutex);

if (!sess)
goto out_term;
Expand Down Expand Up @@ -5541,12 +5605,16 @@ static struct qla_tgt_sess *qlt_make_local_sess(struct scsi_qla_host *vha,
int rc, global_resets;
uint16_t loop_id = 0;

mutex_lock(&vha->vha_tgt.tgt_mutex);

retry:
global_resets =
atomic_read(&vha->vha_tgt.qla_tgt->tgt_global_resets_count);

rc = qla24xx_get_loop_id(vha, s_id, &loop_id);
if (rc != 0) {
mutex_unlock(&vha->vha_tgt.tgt_mutex);

if ((s_id[0] == 0xFF) &&
(s_id[1] == 0xFC)) {
/*
Expand All @@ -5557,17 +5625,27 @@ static struct qla_tgt_sess *qlt_make_local_sess(struct scsi_qla_host *vha,
"Unable to find initiator with S_ID %x:%x:%x",
s_id[0], s_id[1], s_id[2]);
} else
ql_dbg(ql_dbg_tgt_mgt, vha, 0xf071,
ql_log(ql_log_info, vha, 0xf071,
"qla_target(%d): Unable to find "
"initiator with S_ID %x:%x:%x",
vha->vp_idx, s_id[0], s_id[1],
s_id[2]);

if (rc == -ENOENT) {
qlt_port_logo_t logo;
sid_to_portid(s_id, &logo.id);
logo.cmd_count = 1;
qlt_send_first_logo(vha, &logo);
}

return NULL;
}

fcport = qlt_get_port_database(vha, loop_id);
if (!fcport)
if (!fcport) {
mutex_unlock(&vha->vha_tgt.tgt_mutex);
return NULL;
}

if (global_resets !=
atomic_read(&vha->vha_tgt.qla_tgt->tgt_global_resets_count)) {
Expand All @@ -5582,6 +5660,8 @@ static struct qla_tgt_sess *qlt_make_local_sess(struct scsi_qla_host *vha,

sess = qlt_create_sess(vha, fcport, true);

mutex_unlock(&vha->vha_tgt.tgt_mutex);

kfree(fcport);
return sess;
}
Expand Down Expand Up @@ -5611,10 +5691,8 @@ static void qlt_abort_work(struct qla_tgt *tgt,
if (!sess) {
spin_unlock_irqrestore(&ha->hardware_lock, flags);

mutex_lock(&vha->vha_tgt.tgt_mutex);
sess = qlt_make_local_sess(vha, s_id);
/* sess has got an extra creation ref */
mutex_unlock(&vha->vha_tgt.tgt_mutex);

spin_lock_irqsave(&ha->hardware_lock, flags);
if (!sess)
Expand Down Expand Up @@ -5670,10 +5748,8 @@ static void qlt_tmr_work(struct qla_tgt *tgt,
if (!sess) {
spin_unlock_irqrestore(&ha->hardware_lock, flags);

mutex_lock(&vha->vha_tgt.tgt_mutex);
sess = qlt_make_local_sess(vha, s_id);
/* sess has got an extra creation ref */
mutex_unlock(&vha->vha_tgt.tgt_mutex);

spin_lock_irqsave(&ha->hardware_lock, flags);
if (!sess)
Expand Down
9 changes: 9 additions & 0 deletions drivers/scsi/qla2xxx/qla_target.h
Original file line number Diff line number Diff line change
Expand Up @@ -909,6 +909,7 @@ struct qla_tgt_sess {
unsigned int logout_on_delete:1;
unsigned int plogi_ack_needed:1;
unsigned int keep_nport_handle:1;
unsigned int send_els_logo:1;

unsigned char logout_completed;

Expand Down Expand Up @@ -1120,6 +1121,14 @@ static inline uint32_t sid_to_key(const uint8_t *s_id)
return key;
}

static inline void sid_to_portid(const uint8_t *s_id, port_id_t *p)
{
memset(p, 0, sizeof(*p));
p->b.domain = s_id[0];
p->b.area = s_id[1];
p->b.al_pa = s_id[2];
}

/*
* Exported symbols from qla_target.c LLD logic used by qla2xxx code..
*/
Expand Down

0 comments on commit 71cdc07

Please sign in to comment.