summary refs log tree commit diff
path: root/drivers/scsi/qla2xxx/qla_bsg.c
diff options
context:
space:
mode:
authorSarang Radke <sarang.radke@qlogic.com>2010-03-19 17:03:59 -0700
committerJames Bottomley <James.Bottomley@suse.de>2010-04-11 09:45:50 -0500
commit09ff701a177b116c6c15b6e501e58fbfb306b424 (patch)
treefd99933ea29dbc36fc6636f5278d237dbee89b96 /drivers/scsi/qla2xxx/qla_bsg.c
parent6e98016ca077c5c751167bfdb1a3a2a3bee581cf (diff)
downloadlinux-09ff701a177b116c6c15b6e501e58fbfb306b424.tar.gz
[SCSI] qla2xxx: Add APEX support.
Allows priority setting for FCP_CMNDs.

Signed-off-by: Giridhar Malavali <giridhar.malavali@qlogic.com>
Signed-off-by: James Bottomley <James.Bottomley@suse.de>
Diffstat (limited to 'drivers/scsi/qla2xxx/qla_bsg.c')
-rw-r--r--drivers/scsi/qla2xxx/qla_bsg.c163
1 files changed, 163 insertions, 0 deletions
diff --git a/drivers/scsi/qla2xxx/qla_bsg.c b/drivers/scsi/qla2xxx/qla_bsg.c
index c20292fde720..3c3a86ca6cbd 100644
--- a/drivers/scsi/qla2xxx/qla_bsg.c
+++ b/drivers/scsi/qla2xxx/qla_bsg.c
@@ -35,6 +35,166 @@ done:
 	return sp;
 }
 
+int
+qla24xx_fcp_prio_cfg_valid(struct qla_fcp_prio_cfg *pri_cfg, uint8_t flag)
+{
+	int i, ret, num_valid;
+	uint8_t *bcode;
+	struct qla_fcp_prio_entry *pri_entry;
+
+	ret = 1;
+	num_valid = 0;
+	bcode = (uint8_t *)pri_cfg;
+
+	if (bcode[0x0] != 'H' || bcode[0x1] != 'Q' || bcode[0x2] != 'O' ||
+			bcode[0x3] != 'S') {
+		return 0;
+	}
+	if (flag != 1)
+		return ret;
+
+	pri_entry = &pri_cfg->entry[0];
+	for (i = 0; i < pri_cfg->num_entries; i++) {
+		if (pri_entry->flags & FCP_PRIO_ENTRY_TAG_VALID)
+			num_valid++;
+		pri_entry++;
+	}
+
+	if (num_valid == 0)
+		ret = 0;
+
+	return ret;
+}
+
+static int
+qla24xx_proc_fcp_prio_cfg_cmd(struct fc_bsg_job *bsg_job)
+{
+	struct Scsi_Host *host = bsg_job->shost;
+	scsi_qla_host_t *vha = shost_priv(host);
+	struct qla_hw_data *ha = vha->hw;
+	int ret = 0;
+	uint32_t len;
+	uint32_t oper;
+
+	bsg_job->reply->reply_payload_rcv_len = 0;
+
+	if (test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags) ||
+		test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags) ||
+		test_bit(ISP_ABORT_RETRY, &vha->dpc_flags)) {
+		ret = -EBUSY;
+		goto exit_fcp_prio_cfg;
+	}
+
+	/* Get the sub command */
+	oper = bsg_job->request->rqst_data.h_vendor.vendor_cmd[1];
+
+	/* Only set config is allowed if config memory is not allocated */
+	if (!ha->fcp_prio_cfg && (oper != QLFC_FCP_PRIO_SET_CONFIG)) {
+		ret = -EINVAL;
+		goto exit_fcp_prio_cfg;
+	}
+	switch (oper) {
+	case QLFC_FCP_PRIO_DISABLE:
+		if (ha->flags.fcp_prio_enabled) {
+			ha->flags.fcp_prio_enabled = 0;
+			ha->fcp_prio_cfg->attributes &=
+				~FCP_PRIO_ATTR_ENABLE;
+			qla24xx_update_all_fcp_prio(vha);
+			bsg_job->reply->result = DID_OK;
+		} else {
+			ret = -EINVAL;
+			bsg_job->reply->result = (DID_ERROR << 16);
+			goto exit_fcp_prio_cfg;
+		}
+		break;
+
+	case QLFC_FCP_PRIO_ENABLE:
+		if (!ha->flags.fcp_prio_enabled) {
+			if (ha->fcp_prio_cfg) {
+				ha->flags.fcp_prio_enabled = 1;
+				ha->fcp_prio_cfg->attributes |=
+				    FCP_PRIO_ATTR_ENABLE;
+				qla24xx_update_all_fcp_prio(vha);
+				bsg_job->reply->result = DID_OK;
+			} else {
+				ret = -EINVAL;
+				bsg_job->reply->result = (DID_ERROR << 16);
+				goto exit_fcp_prio_cfg;
+			}
+		}
+		break;
+
+	case QLFC_FCP_PRIO_GET_CONFIG:
+		len = bsg_job->reply_payload.payload_len;
+		if (!len || len > FCP_PRIO_CFG_SIZE) {
+			ret = -EINVAL;
+			bsg_job->reply->result = (DID_ERROR << 16);
+			goto exit_fcp_prio_cfg;
+		}
+
+		bsg_job->reply->result = DID_OK;
+		bsg_job->reply->reply_payload_rcv_len =
+			sg_copy_from_buffer(
+			bsg_job->reply_payload.sg_list,
+			bsg_job->reply_payload.sg_cnt, ha->fcp_prio_cfg,
+			len);
+
+		break;
+
+	case QLFC_FCP_PRIO_SET_CONFIG:
+		len = bsg_job->request_payload.payload_len;
+		if (!len || len > FCP_PRIO_CFG_SIZE) {
+			bsg_job->reply->result = (DID_ERROR << 16);
+			ret = -EINVAL;
+			goto exit_fcp_prio_cfg;
+		}
+
+		if (!ha->fcp_prio_cfg) {
+			ha->fcp_prio_cfg = vmalloc(FCP_PRIO_CFG_SIZE);
+			if (!ha->fcp_prio_cfg) {
+				qla_printk(KERN_WARNING, ha,
+					"Unable to allocate memory "
+					"for fcp prio config data (%x).\n",
+					FCP_PRIO_CFG_SIZE);
+				bsg_job->reply->result = (DID_ERROR << 16);
+				ret = -ENOMEM;
+				goto exit_fcp_prio_cfg;
+			}
+		}
+
+		memset(ha->fcp_prio_cfg, 0, FCP_PRIO_CFG_SIZE);
+		sg_copy_to_buffer(bsg_job->request_payload.sg_list,
+		bsg_job->request_payload.sg_cnt, ha->fcp_prio_cfg,
+			FCP_PRIO_CFG_SIZE);
+
+		/* validate fcp priority data */
+		if (!qla24xx_fcp_prio_cfg_valid(
+			(struct qla_fcp_prio_cfg *)
+			ha->fcp_prio_cfg, 1)) {
+			bsg_job->reply->result = (DID_ERROR << 16);
+			ret = -EINVAL;
+			/* If buffer was invalidatic int
+			 * fcp_prio_cfg is of no use
+			 */
+			vfree(ha->fcp_prio_cfg);
+			ha->fcp_prio_cfg = NULL;
+			goto exit_fcp_prio_cfg;
+		}
+
+		ha->flags.fcp_prio_enabled = 0;
+		if (ha->fcp_prio_cfg->attributes & FCP_PRIO_ATTR_ENABLE)
+			ha->flags.fcp_prio_enabled = 1;
+		qla24xx_update_all_fcp_prio(vha);
+		bsg_job->reply->result = DID_OK;
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+exit_fcp_prio_cfg:
+	bsg_job->job_done(bsg_job);
+	return ret;
+}
 static int
 qla2x00_process_els(struct fc_bsg_job *bsg_job)
 {
@@ -948,6 +1108,9 @@ qla2x00_process_vendor_specific(struct fc_bsg_job *bsg_job)
 	case QL_VND_IIDMA:
 		return qla24xx_iidma(bsg_job);
 
+	case QL_VND_FCP_PRIO_CFG_CMD:
+		return qla24xx_proc_fcp_prio_cfg_cmd(bsg_job);
+
 	default:
 		bsg_job->reply->result = (DID_ERROR << 16);
 		bsg_job->job_done(bsg_job);