Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3c231bda authored by Nicholas Bellinger's avatar Nicholas Bellinger
Browse files

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: default avatarNicholas Bellinger <nab@linux-iscsi.org>
parent ddb95145
Loading
Loading
Loading
Loading
+20 −3
Original line number Original line Diff line number Diff line
@@ -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);


+1 −1
Original line number Original line Diff line number Diff line
@@ -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 *);