Commit 8b2f5ff3 authored by Swapnil Nagle's avatar Swapnil Nagle Committed by Nicholas Bellinger
Browse files

qla2xxx: cleanup cmd in qla workqueue before processing TMR



Since cmds go into qla_tgt_wq and TMRs don't, it's possible that TMR
like TASK_ABORT can be queued over the cmd for which it was meant.
To avoid this race, use a per-port list to keep track of cmds that
are enqueued to qla_tgt_wq but not yet processed. When a TMR arrives,
iterate through this list and remove any cmds that match the TMR.
This patch supports TASK_ABORT and LUN_RESET.

Cc: <stable@vger.kernel.org> # v3.18+
Signed-off-by: default avatarSwapnil Nagle <swapnil.nagle@purestorage.com>
Signed-off-by: default avatarAlexei Potashnik <alexei@purestorage.com>
Acked-by: default avatarQuinn Tran <quinn.tran@qlogic.com>
Signed-off-by: default avatarHimanshu Madhani <himanshu.madhani@qlogic.com>
Signed-off-by: default avatarNicholas Bellinger <nab@linux-iscsi.org>
parent b2032fd5
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -68,7 +68,7 @@
 * |                              |                    | 0xd101-0xd1fe	|
 * |                              |                    | 0xd214-0xd2fe	|
 * | Target Mode		  |	  0xe079       |		|
 * | Target Mode Management	  |	  0xf080       | 0xf002		|
 * | Target Mode Management	  |	  0xf083       | 0xf002		|
 * |                              |                    | 0xf046-0xf049  |
 * | Target Mode Task Management  |	  0x1000b      |		|
 * ----------------------------------------------------------------------
+5 −0
Original line number Diff line number Diff line
@@ -3579,6 +3579,11 @@ typedef struct scsi_qla_host {
	uint16_t	fcoe_fcf_idx;
	uint8_t		fcoe_vn_port_mac[6];

	/* list of commands waiting on workqueue */
	struct list_head	qla_cmd_list;
	struct list_head	qla_sess_op_cmd_list;
	spinlock_t		cmd_list_lock;

	uint32_t	vp_abort_cnt;

	struct fc_vport	*fc_vport;	/* holds fc_vport * for each vport */
+3 −0
Original line number Diff line number Diff line
@@ -3764,8 +3764,11 @@ struct scsi_qla_host *qla2x00_create_host(struct scsi_host_template *sht,
	INIT_LIST_HEAD(&vha->vp_fcports);
	INIT_LIST_HEAD(&vha->work_list);
	INIT_LIST_HEAD(&vha->list);
	INIT_LIST_HEAD(&vha->qla_cmd_list);
	INIT_LIST_HEAD(&vha->qla_sess_op_cmd_list);

	spin_lock_init(&vha->work_lock);
	spin_lock_init(&vha->cmd_list_lock);

	sprintf(vha->host_str, "%s_%ld", QLA2XXX_DRIVER_NAME, vha->host_no);
	ql_dbg(ql_dbg_init, vha, 0x0041,
+117 −6
Original line number Diff line number Diff line
@@ -1170,6 +1170,70 @@ static void qlt_24xx_retry_term_exchange(struct scsi_qla_host *vha,
	    FCP_TMF_CMPL, true);
}

static int abort_cmd_for_tag(struct scsi_qla_host *vha, uint32_t tag)
{
	struct qla_tgt_sess_op *op;
	struct qla_tgt_cmd *cmd;

	spin_lock(&vha->cmd_list_lock);

	list_for_each_entry(op, &vha->qla_sess_op_cmd_list, cmd_list) {
		if (tag == op->atio.u.isp24.exchange_addr) {
			op->aborted = true;
			spin_unlock(&vha->cmd_list_lock);
			return 1;
		}
	}

	list_for_each_entry(cmd, &vha->qla_cmd_list, cmd_list) {
		if (tag == cmd->atio.u.isp24.exchange_addr) {
			cmd->state = QLA_TGT_STATE_ABORTED;
			spin_unlock(&vha->cmd_list_lock);
			return 1;
		}
	}

	spin_unlock(&vha->cmd_list_lock);
	return 0;
}

/* drop cmds for the given lun
 * XXX only looks for cmds on the port through which lun reset was recieved
 * XXX does not go through the list of other port (which may have cmds
 *     for the same lun)
 */
static void abort_cmds_for_lun(struct scsi_qla_host *vha,
				uint32_t lun, uint8_t *s_id)
{
	struct qla_tgt_sess_op *op;
	struct qla_tgt_cmd *cmd;
	uint32_t key;

	key = sid_to_key(s_id);
	spin_lock(&vha->cmd_list_lock);
	list_for_each_entry(op, &vha->qla_sess_op_cmd_list, cmd_list) {
		uint32_t op_key;
		uint32_t op_lun;

		op_key = sid_to_key(op->atio.u.isp24.fcp_hdr.s_id);
		op_lun = scsilun_to_int(
			(struct scsi_lun *)&op->atio.u.isp24.fcp_cmnd.lun);
		if (op_key == key && op_lun == lun)
			op->aborted = true;
	}
	list_for_each_entry(cmd, &vha->qla_cmd_list, cmd_list) {
		uint32_t cmd_key;
		uint32_t cmd_lun;

		cmd_key = sid_to_key(cmd->atio.u.isp24.fcp_hdr.s_id);
		cmd_lun = scsilun_to_int(
			(struct scsi_lun *)&cmd->atio.u.isp24.fcp_cmnd.lun);
		if (cmd_key == key && cmd_lun == lun)
			cmd->state = QLA_TGT_STATE_ABORTED;
	}
	spin_unlock(&vha->cmd_list_lock);
}

/* ha->hardware_lock supposed to be held on entry */
static int __qlt_24xx_handle_abts(struct scsi_qla_host *vha,
	struct abts_recv_from_24xx *abts, struct qla_tgt_sess *sess)
@@ -1194,8 +1258,19 @@ static int __qlt_24xx_handle_abts(struct scsi_qla_host *vha,
	}
	spin_unlock(&se_sess->sess_cmd_lock);

	if (!found_lun)
	/* cmd not in LIO lists, look in qla list */
	if (!found_lun) {
		if (abort_cmd_for_tag(vha, abts->exchange_addr_to_abort)) {
			/* send TASK_ABORT response immediately */
			qlt_24xx_send_abts_resp(vha, abts, FCP_TMF_CMPL, false);
			return 0;
		} else {
			ql_dbg(ql_dbg_tgt_mgt, vha, 0xf081,
			    "unable to find cmd in driver or LIO for tag 0x%x\n",
			    abts->exchange_addr_to_abort);
			return -ENOENT;
		}
	}

	ql_dbg(ql_dbg_tgt_mgt, vha, 0xf00f,
	    "qla_target(%d): task abort (tag=%d)\n",
@@ -3264,6 +3339,13 @@ static void __qlt_do_work(struct qla_tgt_cmd *cmd)
	if (tgt->tgt_stop)
		goto out_term;

	if (cmd->state == QLA_TGT_STATE_ABORTED) {
		ql_dbg(ql_dbg_tgt_mgt, vha, 0xf082,
		    "cmd with tag %u is aborted\n",
		    cmd->atio.u.isp24.exchange_addr);
		goto out_term;
	}

	cdb = &atio->u.isp24.fcp_cmnd.cdb[0];
	cmd->se_cmd.tag = atio->u.isp24.exchange_addr;
	cmd->unpacked_lun = scsilun_to_int(
@@ -3317,6 +3399,12 @@ out_term:
static void qlt_do_work(struct work_struct *work)
{
	struct qla_tgt_cmd *cmd = container_of(work, struct qla_tgt_cmd, work);
	scsi_qla_host_t *vha = cmd->vha;
	unsigned long flags;

	spin_lock_irqsave(&vha->cmd_list_lock, flags);
	list_del(&cmd->cmd_list);
	spin_unlock_irqrestore(&vha->cmd_list_lock, flags);

	__qlt_do_work(cmd);
}
@@ -3368,6 +3456,17 @@ static void qlt_create_sess_from_atio(struct work_struct *work)
	unsigned long flags;
	uint8_t *s_id = op->atio.u.isp24.fcp_hdr.s_id;

	spin_lock_irqsave(&vha->cmd_list_lock, flags);
	list_del(&op->cmd_list);
	spin_unlock_irqrestore(&vha->cmd_list_lock, flags);

	if (op->aborted) {
		ql_dbg(ql_dbg_tgt_mgt, vha, 0xf083,
		    "sess_op with tag %u is aborted\n",
		    op->atio.u.isp24.exchange_addr);
		goto out_term;
	}

	ql_dbg(ql_dbg_tgt_mgt, vha, 0xf022,
	    "qla_target(%d): Unable to find wwn login"
	    " (s_id %x:%x:%x), trying to create it manually\n",
@@ -3440,6 +3539,11 @@ static int qlt_handle_cmd_for_atio(struct scsi_qla_host *vha,

		memcpy(&op->atio, atio, sizeof(*atio));
		op->vha = vha;

		spin_lock(&vha->cmd_list_lock);
		list_add_tail(&op->cmd_list, &vha->qla_sess_op_cmd_list);
		spin_unlock(&vha->cmd_list_lock);

		INIT_WORK(&op->work, qlt_create_sess_from_atio);
		queue_work(qla_tgt_wq, &op->work);
		return 0;
@@ -3459,6 +3563,11 @@ static int qlt_handle_cmd_for_atio(struct scsi_qla_host *vha,

	cmd->cmd_in_wq = 1;
	cmd->cmd_flags |= BIT_0;

	spin_lock(&vha->cmd_list_lock);
	list_add_tail(&cmd->cmd_list, &vha->qla_cmd_list);
	spin_unlock(&vha->cmd_list_lock);

	INIT_WORK(&cmd->work, qlt_do_work);
	queue_work(qla_tgt_wq, &cmd->work);
	return 0;
@@ -3472,6 +3581,7 @@ static int qlt_issue_task_mgmt(struct qla_tgt_sess *sess, uint32_t lun,
	struct scsi_qla_host *vha = sess->vha;
	struct qla_hw_data *ha = vha->hw;
	struct qla_tgt_mgmt_cmd *mcmd;
	struct atio_from_isp *a = (struct atio_from_isp *)iocb;
	int res;
	uint8_t tmr_func;

@@ -3512,6 +3622,7 @@ static int qlt_issue_task_mgmt(struct qla_tgt_sess *sess, uint32_t lun,
		ql_dbg(ql_dbg_tgt_tmr, vha, 0x10002,
		    "qla_target(%d): LUN_RESET received\n", sess->vha->vp_idx);
		tmr_func = TMR_LUN_RESET;
		abort_cmds_for_lun(vha, lun, a->u.isp24.fcp_hdr.s_id);
		break;

	case QLA_TGT_CLEAR_TS:
+12 −0
Original line number Diff line number Diff line
@@ -874,6 +874,8 @@ struct qla_tgt_sess_op {
	struct scsi_qla_host *vha;
	struct atio_from_isp atio;
	struct work_struct work;
	struct list_head cmd_list;
	bool aborted;
};

/*
@@ -1076,6 +1078,16 @@ static inline void qla_reverse_ini_mode(struct scsi_qla_host *ha)
		ha->host->active_mode |= MODE_INITIATOR;
}

static inline uint32_t sid_to_key(const uint8_t *s_id)
{
	uint32_t key;

	key = (((unsigned long)s_id[0] << 16) |
	       ((unsigned long)s_id[1] << 8) |
	       (unsigned long)s_id[2]);
	return key;
}

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