qla2xxx: Check + fail when npiv_vports_inuse exists in shutdown

This patch adds an check to qlt_stop_phase1() to avoid shutdown
when the base_vha contains a non-zero fc_host->npiv_vports_inuse
count.

This includes holding qla_tgt_mutex in qlt_stop_phase1() between
the fc_host->npiv_vports_inuse check + setting of tgt->tgt_stop to
avoid a possible race between qlt_lport_register() -> tcm_qla2xxx
-> tcm_qla2xxx_lport_register_npiv_cb() calling fc_vport_create().

Cc: Sawan Chandak <sawan.chandak@qlogic.com>
Cc: Quinn Tran <quinn.tran@qlogic.com>
Cc: Saurav Kashyap <saurav.kashyap@qlogic.com>
Cc: Giridhar Malavali <giridhar.malavali@qlogic.com>
Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
This commit is contained in:
Nicholas Bellinger 2014-02-19 17:50:22 -08:00
parent ddb95145a3
commit 3c231bdae1
2 changed files with 21 additions and 4 deletions

View File

@ -790,17 +790,32 @@ static inline int test_tgt_sess_count(struct qla_tgt *tgt)
} }
/* Called by tcm_qla2xxx configfs code */ /* Called by tcm_qla2xxx configfs code */
void qlt_stop_phase1(struct qla_tgt *tgt) int qlt_stop_phase1(struct qla_tgt *tgt)
{ {
struct scsi_qla_host *vha = tgt->vha; struct scsi_qla_host *vha = tgt->vha;
struct qla_hw_data *ha = tgt->ha; struct qla_hw_data *ha = tgt->ha;
unsigned long flags; unsigned long flags;
mutex_lock(&qla_tgt_mutex);
if (!vha->fc_vport) {
struct Scsi_Host *sh = vha->host;
struct fc_host_attrs *fc_host = shost_to_fc_host(sh);
bool npiv_vports;
spin_lock_irqsave(sh->host_lock, flags);
npiv_vports = (fc_host->npiv_vports_inuse);
spin_unlock_irqrestore(sh->host_lock, flags);
if (npiv_vports) {
mutex_unlock(&qla_tgt_mutex);
return -EPERM;
}
}
if (tgt->tgt_stop || tgt->tgt_stopped) { if (tgt->tgt_stop || tgt->tgt_stopped) {
ql_dbg(ql_dbg_tgt_mgt, vha, 0xf04e, ql_dbg(ql_dbg_tgt_mgt, vha, 0xf04e,
"Already in tgt->tgt_stop or tgt_stopped state\n"); "Already in tgt->tgt_stop or tgt_stopped state\n");
dump_stack(); mutex_unlock(&qla_tgt_mutex);
return; return -EPERM;
} }
ql_dbg(ql_dbg_tgt, vha, 0xe003, "Stopping target for host %ld(%p)\n", ql_dbg(ql_dbg_tgt, vha, 0xe003, "Stopping target for host %ld(%p)\n",
@ -815,6 +830,7 @@ void qlt_stop_phase1(struct qla_tgt *tgt)
qlt_clear_tgt_db(tgt, true); qlt_clear_tgt_db(tgt, true);
spin_unlock_irqrestore(&ha->hardware_lock, flags); spin_unlock_irqrestore(&ha->hardware_lock, flags);
mutex_unlock(&vha->vha_tgt.tgt_mutex); mutex_unlock(&vha->vha_tgt.tgt_mutex);
mutex_unlock(&qla_tgt_mutex);
flush_delayed_work(&tgt->sess_del_work); flush_delayed_work(&tgt->sess_del_work);
@ -841,6 +857,7 @@ void qlt_stop_phase1(struct qla_tgt *tgt)
/* Wait for sessions to clear out (just in case) */ /* Wait for sessions to clear out (just in case) */
wait_event(tgt->waitQ, test_tgt_sess_count(tgt)); wait_event(tgt->waitQ, test_tgt_sess_count(tgt));
return 0;
} }
EXPORT_SYMBOL(qlt_stop_phase1); EXPORT_SYMBOL(qlt_stop_phase1);

View File

@ -1001,7 +1001,7 @@ extern void qlt_modify_vp_config(struct scsi_qla_host *,
extern void qlt_probe_one_stage1(struct scsi_qla_host *, struct qla_hw_data *); extern void qlt_probe_one_stage1(struct scsi_qla_host *, struct qla_hw_data *);
extern int qlt_mem_alloc(struct qla_hw_data *); extern int qlt_mem_alloc(struct qla_hw_data *);
extern void qlt_mem_free(struct qla_hw_data *); extern void qlt_mem_free(struct qla_hw_data *);
extern void qlt_stop_phase1(struct qla_tgt *); extern int qlt_stop_phase1(struct qla_tgt *);
extern void qlt_stop_phase2(struct qla_tgt *); extern void qlt_stop_phase2(struct qla_tgt *);
extern irqreturn_t qla83xx_msix_atio_q(int, void *); extern irqreturn_t qla83xx_msix_atio_q(int, void *);
extern void qlt_83xx_iospace_config(struct qla_hw_data *); extern void qlt_83xx_iospace_config(struct qla_hw_data *);