Blame SOURCES/0036-scsi-scsi-qla2xxx-Add-support-for-multiple-fwdump-te.patch

3c6e85
From f42a165a0f0fd533b8bdd3adf94ee567bb669765 Mon Sep 17 00:00:00 2001
3c6e85
From: Himanshu Madhani <hmadhani@redhat.com>
3c6e85
Date: Thu, 1 Aug 2019 15:54:56 -0400
3c6e85
Subject: [PATCH 036/124] [scsi] scsi: qla2xxx: Add support for multiple fwdump
3c6e85
 templates/segments
3c6e85
3c6e85
Message-id: <20190801155618.12650-37-hmadhani@redhat.com>
3c6e85
Patchwork-id: 267812
3c6e85
O-Subject: [RHEL 7.8 e-stor PATCH 036/118] scsi: qla2xxx: Add support for multiple fwdump templates/segments
3c6e85
Bugzilla: 1729270
3c6e85
RH-Acked-by: Jarod Wilson <jarod@redhat.com>
3c6e85
RH-Acked-by: Tony Camuso <tcamuso@redhat.com>
3c6e85
3c6e85
From: Joe Carnuccio <joe.carnuccio@cavium.com>
3c6e85
3c6e85
Bugzilla 1729270
3c6e85
3c6e85
This patch adds multipe firmware dump template and segments support for
3c6e85
ISP27XX/28XX.
3c6e85
3c6e85
Signed-off-by: Joe Carnuccio <joe.carnuccio@cavium.com>
3c6e85
Signed-off-by: Himanshu Madhani <hmadhani@marvell.com>
3c6e85
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
3c6e85
(cherry picked from commit a28d9e4ef99729d7e4db31d2dfeaf00755be4ab7)
3c6e85
Signed-off-by: Himanshu Madhani <hmadhani@redhat.com>
3c6e85
Signed-off-by: Jan Stancek <jstancek@redhat.com>
3c6e85
---
3c6e85
 drivers/scsi/qla2xxx/qla_bsg.c  |   3 +-
3c6e85
 drivers/scsi/qla2xxx/qla_def.h  |   9 +-
3c6e85
 drivers/scsi/qla2xxx/qla_gbl.h  |   2 +-
3c6e85
 drivers/scsi/qla2xxx/qla_init.c | 408 +++++++++++++++++++++++-----------------
3c6e85
 drivers/scsi/qla2xxx/qla_os.c   |  15 +-
3c6e85
 drivers/scsi/qla2xxx/qla_sup.c  |   2 +
3c6e85
 drivers/scsi/qla2xxx/qla_tmpl.c |  89 +++++----
3c6e85
 7 files changed, 304 insertions(+), 224 deletions(-)
3c6e85
3c6e85
diff --git a/drivers/scsi/qla2xxx/qla_bsg.c b/drivers/scsi/qla2xxx/qla_bsg.c
3c6e85
index f7cfb36e635e..b6eb4c6677bf 100644
3c6e85
--- a/drivers/scsi/qla2xxx/qla_bsg.c
3c6e85
+++ b/drivers/scsi/qla2xxx/qla_bsg.c
3c6e85
@@ -80,8 +80,7 @@ qla24xx_fcp_prio_cfg_valid(scsi_qla_host_t *vha,
3c6e85
 		return 0;
3c6e85
 	}
3c6e85
 
3c6e85
-	if (bcode[0] != 'H' || bcode[1] != 'Q' || bcode[2] != 'O' ||
3c6e85
-			bcode[3] != 'S') {
3c6e85
+	if (memcmp(bcode, "HQOS", 4)) {
3c6e85
 		/* Invalid FCP priority data header*/
3c6e85
 		ql_dbg(ql_dbg_user, vha, 0x7052,
3c6e85
 		    "Invalid FCP Priority data header. bcode=0x%x.\n",
3c6e85
diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h
3c6e85
index 15b146db7f71..ed5283e50b32 100644
3c6e85
--- a/drivers/scsi/qla2xxx/qla_def.h
3c6e85
+++ b/drivers/scsi/qla2xxx/qla_def.h
3c6e85
@@ -4037,9 +4037,11 @@ struct qla_hw_data {
3c6e85
 	uint8_t		pep_version[3];
3c6e85
 
3c6e85
 	/* Firmware dump template */
3c6e85
-	void		*fw_dump_template;
3c6e85
-	uint32_t	fw_dump_template_len;
3c6e85
-	/* Firmware dump information. */
3c6e85
+	struct fwdt {
3c6e85
+		void *template;
3c6e85
+		ulong length;
3c6e85
+		ulong dump_size;
3c6e85
+	} fwdt[2];
3c6e85
 	struct qla2xxx_fw_dump *fw_dump;
3c6e85
 	uint32_t	fw_dump_len;
3c6e85
 	bool		fw_dumped;
3c6e85
@@ -4082,7 +4084,6 @@ struct qla_hw_data {
3c6e85
 	uint16_t	product_id[4];
3c6e85
 
3c6e85
 	uint8_t		model_number[16+1];
3c6e85
-#define BINZERO		"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
3c6e85
 	char		model_desc[80];
3c6e85
 	uint8_t		adapter_id[16+1];
3c6e85
 
3c6e85
diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h
3c6e85
index 3e4424ecd6ec..fcb85c15c703 100644
3c6e85
--- a/drivers/scsi/qla2xxx/qla_gbl.h
3c6e85
+++ b/drivers/scsi/qla2xxx/qla_gbl.h
3c6e85
@@ -610,7 +610,7 @@ extern void qla82xx_fw_dump(scsi_qla_host_t *, int);
3c6e85
 extern void qla8044_fw_dump(scsi_qla_host_t *, int);
3c6e85
 
3c6e85
 extern void qla27xx_fwdump(scsi_qla_host_t *, int);
3c6e85
-extern ulong qla27xx_fwdt_calculate_dump_size(struct scsi_qla_host *);
3c6e85
+extern ulong qla27xx_fwdt_calculate_dump_size(struct scsi_qla_host *, void *);
3c6e85
 extern int qla27xx_fwdt_template_valid(void *);
3c6e85
 extern ulong qla27xx_fwdt_template_size(void *);
3c6e85
 
3c6e85
diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c
3c6e85
index 05c8a6befa4b..9fa3c3322809 100644
3c6e85
--- a/drivers/scsi/qla2xxx/qla_init.c
3c6e85
+++ b/drivers/scsi/qla2xxx/qla_init.c
3c6e85
@@ -3089,12 +3089,15 @@ eft_err:
3c6e85
 void
3c6e85
 qla2x00_alloc_fw_dump(scsi_qla_host_t *vha)
3c6e85
 {
3c6e85
+	int rval;
3c6e85
 	uint32_t dump_size, fixed_size, mem_size, req_q_size, rsp_q_size,
3c6e85
 	    eft_size, fce_size, mq_size;
3c6e85
 	struct qla_hw_data *ha = vha->hw;
3c6e85
 	struct req_que *req = ha->req_q_map[0];
3c6e85
 	struct rsp_que *rsp = ha->rsp_q_map[0];
3c6e85
 	struct qla2xxx_fw_dump *fw_dump;
3c6e85
+	dma_addr_t tc_dma;
3c6e85
+	void *tc;
3c6e85
 
3c6e85
 	dump_size = fixed_size = mem_size = eft_size = fce_size = mq_size = 0;
3c6e85
 	req_q_size = rsp_q_size = 0;
3c6e85
@@ -3139,20 +3142,51 @@ qla2x00_alloc_fw_dump(scsi_qla_host_t *vha)
3c6e85
 
3c6e85
 		fce_size = sizeof(struct qla2xxx_fce_chain) + FCE_SIZE;
3c6e85
 try_eft:
3c6e85
+		if (ha->eft)
3c6e85
+			dma_free_coherent(&ha->pdev->dev,
3c6e85
+			    EFT_SIZE, ha->eft, ha->eft_dma);
3c6e85
+
3c6e85
+		/* Allocate memory for Extended Trace Buffer. */
3c6e85
+		tc = dma_alloc_coherent(&ha->pdev->dev, EFT_SIZE, &tc_dma,
3c6e85
+					 GFP_KERNEL);
3c6e85
+		if (!tc) {
3c6e85
+			ql_log(ql_log_warn, vha, 0x00c1,
3c6e85
+			    "Unable to allocate (%d KB) for EFT.\n",
3c6e85
+			    EFT_SIZE / 1024);
3c6e85
+			goto allocate;
3c6e85
+		}
3c6e85
+
3c6e85
+		rval = qla2x00_enable_eft_trace(vha, tc_dma, EFT_NUM_BUFFERS);
3c6e85
+		if (rval) {
3c6e85
+			ql_log(ql_log_warn, vha, 0x00c2,
3c6e85
+			    "Unable to initialize EFT (%d).\n", rval);
3c6e85
+			dma_free_coherent(&ha->pdev->dev, EFT_SIZE, tc,
3c6e85
+			    tc_dma);
3c6e85
+		}
3c6e85
 		ql_dbg(ql_dbg_init, vha, 0x00c3,
3c6e85
 		    "Allocated (%d KB) EFT ...\n", EFT_SIZE / 1024);
3c6e85
 		eft_size = EFT_SIZE;
3c6e85
 	}
3c6e85
 
3c6e85
 	if (IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
3c6e85
-		if (!ha->fw_dump_template) {
3c6e85
-			ql_log(ql_log_warn, vha, 0x00ba,
3c6e85
-			    "Failed missing fwdump template\n");
3c6e85
-			return;
3c6e85
+		struct fwdt *fwdt = ha->fwdt;
3c6e85
+		uint j;
3c6e85
+
3c6e85
+		for (j = 0; j < 2; j++, fwdt++) {
3c6e85
+			if (!fwdt->template) {
3c6e85
+				ql_log(ql_log_warn, vha, 0x00ba,
3c6e85
+				    "-> fwdt%u no template\n", j);
3c6e85
+				continue;
3c6e85
+			}
3c6e85
+			ql_dbg(ql_dbg_init, vha, 0x00fa,
3c6e85
+			    "-> fwdt%u calculating fwdump size...\n", j);
3c6e85
+			fwdt->dump_size = qla27xx_fwdt_calculate_dump_size(
3c6e85
+			    vha, fwdt->template);
3c6e85
+			ql_dbg(ql_dbg_init, vha, 0x00fa,
3c6e85
+			    "-> fwdt%u calculated fwdump size = %#lx bytes\n",
3c6e85
+			    j, fwdt->dump_size);
3c6e85
+			dump_size += fwdt->dump_size;
3c6e85
 		}
3c6e85
-		dump_size = qla27xx_fwdt_calculate_dump_size(vha);
3c6e85
-		ql_dbg(ql_dbg_init, vha, 0x00fa,
3c6e85
-		    "-> allocating fwdump (%x bytes)...\n", dump_size);
3c6e85
 		goto allocate;
3c6e85
 	}
3c6e85
 
3c6e85
@@ -4284,11 +4318,14 @@ qla2x00_set_model_info(scsi_qla_host_t *vha, uint8_t *model, size_t len,
3c6e85
 {
3c6e85
 	char *st, *en;
3c6e85
 	uint16_t index;
3c6e85
+	uint64_t zero[2] = { 0 };
3c6e85
 	struct qla_hw_data *ha = vha->hw;
3c6e85
 	int use_tbl = !IS_QLA24XX_TYPE(ha) && !IS_QLA25XX(ha) &&
3c6e85
 	    !IS_CNA_CAPABLE(ha) && !IS_QLA2031(ha);
3c6e85
 
3c6e85
-	if (memcmp(model, BINZERO, len) != 0) {
3c6e85
+	if (len > sizeof(zero))
3c6e85
+		len = sizeof(zero);
3c6e85
+	if (memcmp(model, &zero, len) != 0) {
3c6e85
 		strncpy(ha->model_number, model, len);
3c6e85
 		st = en = ha->model_number;
3c6e85
 		en += len - 1;
3c6e85
@@ -4392,8 +4429,8 @@ qla2x00_nvram_config(scsi_qla_host_t *vha)
3c6e85
 	    nv, ha->nvram_size);
3c6e85
 
3c6e85
 	/* Bad NVRAM data, set defaults parameters. */
3c6e85
-	if (chksum || nv->id[0] != 'I' || nv->id[1] != 'S' ||
3c6e85
-	    nv->id[2] != 'P' || nv->id[3] != ' ' || nv->nvram_version < 1) {
3c6e85
+	if (chksum || memcmp("ISP ", nv->id, sizeof(nv->id)) ||
3c6e85
+	    nv->nvram_version < 1) {
3c6e85
 		/* Reset NVRAM data. */
3c6e85
 		ql_log(ql_log_warn, vha, 0x0064,
3c6e85
 		    "Inconsistent NVRAM "
3c6e85
@@ -7000,9 +7037,8 @@ qla24xx_nvram_config(scsi_qla_host_t *vha)
3c6e85
 	    nv, ha->nvram_size);
3c6e85
 
3c6e85
 	/* Bad NVRAM data, set defaults parameters. */
3c6e85
-	if (chksum || nv->id[0] != 'I' || nv->id[1] != 'S' || nv->id[2] != 'P'
3c6e85
-	    || nv->id[3] != ' ' ||
3c6e85
-	    nv->nvram_version < cpu_to_le16(ICB_VERSION)) {
3c6e85
+	if (chksum || memcmp("ISP ", nv->id, sizeof(nv->id)) ||
3c6e85
+	    le16_to_cpu(nv->nvram_version) < ICB_VERSION) {
3c6e85
 		/* Reset NVRAM data. */
3c6e85
 		ql_log(ql_log_warn, vha, 0x006b,
3c6e85
 		    "Inconsistent NVRAM detected: checksum=0x%x id=%c "
3c6e85
@@ -7318,14 +7354,16 @@ static int
3c6e85
 qla24xx_load_risc_flash(scsi_qla_host_t *vha, uint32_t *srisc_addr,
3c6e85
     uint32_t faddr)
3c6e85
 {
3c6e85
-	int	rval = QLA_SUCCESS;
3c6e85
-	int	segments, fragment;
3c6e85
-	uint32_t *dcode, dlen;
3c6e85
-	uint32_t risc_addr;
3c6e85
-	uint32_t risc_size;
3c6e85
-	uint32_t i;
3c6e85
+	int rval;
3c6e85
+	uint templates, segments, fragment;
3c6e85
+	ulong i;
3c6e85
+	uint j;
3c6e85
+	ulong dlen;
3c6e85
+	uint32_t *dcode;
3c6e85
+	uint32_t risc_addr, risc_size, risc_attr = 0;
3c6e85
 	struct qla_hw_data *ha = vha->hw;
3c6e85
 	struct req_que *req = ha->req_q_map[0];
3c6e85
+	struct fwdt *fwdt = ha->fwdt;
3c6e85
 
3c6e85
 	ql_dbg(ql_dbg_init, vha, 0x008b,
3c6e85
 	    "FW: Loading firmware from flash (%x).\n", faddr);
3c6e85
@@ -7343,34 +7381,36 @@ qla24xx_load_risc_flash(scsi_qla_host_t *vha, uint32_t *srisc_addr,
3c6e85
 		return QLA_FUNCTION_FAILED;
3c6e85
 	}
3c6e85
 
3c6e85
-	while (segments && rval == QLA_SUCCESS) {
3c6e85
-		/* Read segment's load information. */
3c6e85
-		qla24xx_read_flash_data(vha, dcode, faddr, 4);
3c6e85
-
3c6e85
+	dcode = (void *)req->ring;
3c6e85
+	*srisc_addr = 0;
3c6e85
+	segments = FA_RISC_CODE_SEGMENTS;
3c6e85
+	for (j = 0; j < segments; j++) {
3c6e85
+		ql_dbg(ql_dbg_init, vha, 0x008d,
3c6e85
+		    "-> Loading segment %u...\n", j);
3c6e85
+		qla24xx_read_flash_data(vha, dcode, faddr, 10);
3c6e85
 		risc_addr = be32_to_cpu(dcode[2]);
3c6e85
-		*srisc_addr = *srisc_addr == 0 ? risc_addr : *srisc_addr;
3c6e85
 		risc_size = be32_to_cpu(dcode[3]);
3c6e85
+		if (!*srisc_addr) {
3c6e85
+			*srisc_addr = risc_addr;
3c6e85
+			risc_attr = be32_to_cpu(dcode[9]);
3c6e85
+		}
3c6e85
 
3c6e85
-		fragment = 0;
3c6e85
-		while (risc_size > 0 && rval == QLA_SUCCESS) {
3c6e85
-			dlen = (uint32_t)(ha->fw_transfer_size >> 2);
3c6e85
+		dlen = ha->fw_transfer_size >> 2;
3c6e85
+		for (fragment = 0; risc_size; fragment++) {
3c6e85
 			if (dlen > risc_size)
3c6e85
 				dlen = risc_size;
3c6e85
 
3c6e85
 			ql_dbg(ql_dbg_init, vha, 0x008e,
3c6e85
-			    "Loading risc segment@ risc addr %x "
3c6e85
-			    "number of dwords 0x%x offset 0x%x.\n",
3c6e85
-			    risc_addr, dlen, faddr);
3c6e85
-
3c6e85
+			    "-> Loading fragment %u: %#x <- %#x (%#lx dwords)...\n",
3c6e85
+			    fragment, risc_addr, faddr, dlen);
3c6e85
 			qla24xx_read_flash_data(vha, dcode, faddr, dlen);
3c6e85
 			for (i = 0; i < dlen; i++)
3c6e85
 				dcode[i] = swab32(dcode[i]);
3c6e85
 
3c6e85
-			rval = qla2x00_load_ram(vha, req->dma, risc_addr,
3c6e85
-			    dlen);
3c6e85
+			rval = qla2x00_load_ram(vha, req->dma, risc_addr, dlen);
3c6e85
 			if (rval) {
3c6e85
 				ql_log(ql_log_fatal, vha, 0x008f,
3c6e85
-				    "Failed to load segment %d of firmware.\n",
3c6e85
+				    "-> Failed load firmware fragment %u.\n",
3c6e85
 				    fragment);
3c6e85
 				return QLA_FUNCTION_FAILED;
3c6e85
 			}
3c6e85
@@ -7378,72 +7418,83 @@ qla24xx_load_risc_flash(scsi_qla_host_t *vha, uint32_t *srisc_addr,
3c6e85
 			faddr += dlen;
3c6e85
 			risc_addr += dlen;
3c6e85
 			risc_size -= dlen;
3c6e85
-			fragment++;
3c6e85
 		}
3c6e85
-
3c6e85
-		/* Next segment. */
3c6e85
-		segments--;
3c6e85
 	}
3c6e85
 
3c6e85
 	if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha))
3c6e85
-		return rval;
3c6e85
+		return QLA_SUCCESS;
3c6e85
 
3c6e85
-	if (ha->fw_dump_template)
3c6e85
-		vfree(ha->fw_dump_template);
3c6e85
-	ha->fw_dump_template = NULL;
3c6e85
-	ha->fw_dump_template_len = 0;
3c6e85
-
3c6e85
-	ql_dbg(ql_dbg_init, vha, 0x0161,
3c6e85
-	    "Loading fwdump template from %x\n", faddr);
3c6e85
-	qla24xx_read_flash_data(vha, dcode, faddr, 7);
3c6e85
-	risc_size = be32_to_cpu(dcode[2]);
3c6e85
-	ql_dbg(ql_dbg_init, vha, 0x0162,
3c6e85
-	    "-> array size %x dwords\n", risc_size);
3c6e85
-	if (risc_size == 0 || risc_size == ~0)
3c6e85
-		goto failed;
3c6e85
+	templates = (risc_attr & BIT_9) ? 2 : 1;
3c6e85
+	ql_dbg(ql_dbg_init, vha, 0x0160, "-> templates = %u\n", templates);
3c6e85
+	for (j = 0; j < templates; j++, fwdt++) {
3c6e85
+		if (fwdt->template)
3c6e85
+			vfree(fwdt->template);
3c6e85
+		fwdt->template = NULL;
3c6e85
+		fwdt->length = 0;
3c6e85
+
3c6e85
+		qla24xx_read_flash_data(vha, dcode, faddr, 7);
3c6e85
+		risc_size = be32_to_cpu(dcode[2]);
3c6e85
+		ql_dbg(ql_dbg_init, vha, 0x0161,
3c6e85
+		    "-> fwdt%u template array at %#x (%#x dwords)\n",
3c6e85
+		    j, faddr, risc_size);
3c6e85
+		if (!risc_size || !~risc_size) {
3c6e85
+			ql_dbg(ql_dbg_init, vha, 0x0162,
3c6e85
+			    "-> fwdt%u failed to read array\n", j);
3c6e85
+			goto failed;
3c6e85
+		}
3c6e85
 
3c6e85
-	dlen = (risc_size - 8) * sizeof(*dcode);
3c6e85
-	ql_dbg(ql_dbg_init, vha, 0x0163,
3c6e85
-	    "-> template allocating %x bytes...\n", dlen);
3c6e85
-	ha->fw_dump_template = vmalloc(dlen);
3c6e85
-	if (!ha->fw_dump_template) {
3c6e85
-		ql_log(ql_log_warn, vha, 0x0164,
3c6e85
-		    "Failed fwdump template allocate %x bytes.\n", risc_size);
3c6e85
-		goto failed;
3c6e85
-	}
3c6e85
+		/* skip header and ignore checksum */
3c6e85
+		faddr += 7;
3c6e85
+		risc_size -= 8;
3c6e85
+
3c6e85
+		ql_dbg(ql_dbg_init, vha, 0x0163,
3c6e85
+		    "-> fwdt%u template allocate template %#x words...\n",
3c6e85
+		    j, risc_size);
3c6e85
+		fwdt->template = vmalloc(risc_size * sizeof(*dcode));
3c6e85
+		if (!fwdt->template) {
3c6e85
+			ql_log(ql_log_warn, vha, 0x0164,
3c6e85
+			    "-> fwdt%u failed allocate template.\n", j);
3c6e85
+			goto failed;
3c6e85
+		}
3c6e85
 
3c6e85
-	faddr += 7;
3c6e85
-	risc_size -= 8;
3c6e85
-	dcode = ha->fw_dump_template;
3c6e85
-	qla24xx_read_flash_data(vha, dcode, faddr, risc_size);
3c6e85
-	for (i = 0; i < risc_size; i++)
3c6e85
-		dcode[i] = le32_to_cpu(dcode[i]);
3c6e85
+		dcode = fwdt->template;
3c6e85
+		qla24xx_read_flash_data(vha, dcode, faddr, risc_size);
3c6e85
+		for (i = 0; i < risc_size; i++)
3c6e85
+			dcode[i] = le32_to_cpu(dcode[i]);
3c6e85
 
3c6e85
-	if (!qla27xx_fwdt_template_valid(dcode)) {
3c6e85
-		ql_log(ql_log_warn, vha, 0x0165,
3c6e85
-		    "Failed fwdump template validate\n");
3c6e85
-		goto failed;
3c6e85
-	}
3c6e85
+		if (!qla27xx_fwdt_template_valid(dcode)) {
3c6e85
+			ql_log(ql_log_warn, vha, 0x0165,
3c6e85
+			    "-> fwdt%u failed template validate\n", j);
3c6e85
+			goto failed;
3c6e85
+		}
3c6e85
 
3c6e85
-	dlen = qla27xx_fwdt_template_size(dcode);
3c6e85
-	ql_dbg(ql_dbg_init, vha, 0x0166,
3c6e85
-	    "-> template size %x bytes\n", dlen);
3c6e85
-	if (dlen > risc_size * sizeof(*dcode)) {
3c6e85
-		ql_log(ql_log_warn, vha, 0x0167,
3c6e85
-		    "Failed fwdump template exceeds array by %zx bytes\n",
3c6e85
-		    (size_t)(dlen - risc_size * sizeof(*dcode)));
3c6e85
-		goto failed;
3c6e85
+		dlen = qla27xx_fwdt_template_size(dcode);
3c6e85
+		ql_dbg(ql_dbg_init, vha, 0x0166,
3c6e85
+		    "-> fwdt%u template size %#lx bytes (%#lx words)\n",
3c6e85
+		    j, dlen, dlen / sizeof(*dcode));
3c6e85
+		if (dlen > risc_size * sizeof(*dcode)) {
3c6e85
+			ql_log(ql_log_warn, vha, 0x0167,
3c6e85
+			    "-> fwdt%u template exceeds array (%-lu bytes)\n",
3c6e85
+			    j, dlen - risc_size * sizeof(*dcode));
3c6e85
+			goto failed;
3c6e85
+		}
3c6e85
+
3c6e85
+		fwdt->length = dlen;
3c6e85
+		ql_dbg(ql_dbg_init, vha, 0x0168,
3c6e85
+		    "-> fwdt%u loaded template ok\n", j);
3c6e85
+
3c6e85
+		faddr += risc_size + 1;
3c6e85
 	}
3c6e85
-	ha->fw_dump_template_len = dlen;
3c6e85
-	return rval;
3c6e85
+
3c6e85
+	return QLA_SUCCESS;
3c6e85
 
3c6e85
 failed:
3c6e85
-	ql_log(ql_log_warn, vha, 0x016d, "Failed fwdump template\n");
3c6e85
-	if (ha->fw_dump_template)
3c6e85
-		vfree(ha->fw_dump_template);
3c6e85
-	ha->fw_dump_template = NULL;
3c6e85
-	ha->fw_dump_template_len = 0;
3c6e85
-	return rval;
3c6e85
+	if (fwdt->template)
3c6e85
+		vfree(fwdt->template);
3c6e85
+	fwdt->template = NULL;
3c6e85
+	fwdt->length = 0;
3c6e85
+
3c6e85
+	return QLA_SUCCESS;
3c6e85
 }
3c6e85
 
3c6e85
 #define QLA_FW_URL "http://ldriver.qlogic.com/firmware/"
3c6e85
@@ -7551,31 +7602,31 @@ static int
3c6e85
 qla24xx_load_risc_blob(scsi_qla_host_t *vha, uint32_t *srisc_addr)
3c6e85
 {
3c6e85
 	int	rval;
3c6e85
-	int	segments, fragment;
3c6e85
-	uint32_t *dcode, dlen;
3c6e85
-	uint32_t risc_addr;
3c6e85
-	uint32_t risc_size;
3c6e85
-	uint32_t i;
3c6e85
+	uint templates, segments, fragment;
3c6e85
+	uint32_t *dcode;
3c6e85
+	ulong dlen;
3c6e85
+	uint32_t risc_addr, risc_size, risc_attr = 0;
3c6e85
+	ulong i;
3c6e85
+	uint j;
3c6e85
 	struct fw_blob *blob;
3c6e85
 	uint32_t *fwcode;
3c6e85
-	uint32_t fwclen;
3c6e85
 	struct qla_hw_data *ha = vha->hw;
3c6e85
 	struct req_que *req = ha->req_q_map[0];
3c6e85
+	struct fwdt *fwdt = ha->fwdt;
3c6e85
+
3c6e85
+	ql_dbg(ql_dbg_init, vha, 0x0090,
3c6e85
+	    "-> FW: Loading via request-firmware.\n");
3c6e85
 
3c6e85
-	/* Load firmware blob. */
3c6e85
 	blob = qla2x00_request_firmware(vha);
3c6e85
 	if (!blob) {
3c6e85
-		ql_log(ql_log_warn, vha, 0x0090,
3c6e85
-		    "Firmware image unavailable.\n");
3c6e85
-		ql_log(ql_log_warn, vha, 0x0091,
3c6e85
-		    "Firmware images can be retrieved from: "
3c6e85
-		    QLA_FW_URL ".\n");
3c6e85
+		ql_log(ql_log_warn, vha, 0x0092,
3c6e85
+		    "-> Firmware file not found.\n");
3c6e85
 
3c6e85
 		return QLA_FUNCTION_FAILED;
3c6e85
 	}
3c6e85
 
3c6e85
 	fwcode = (void *)blob->fw->data;
3c6e85
-	dcode = fwcode;
3c6e85
+	dcode = fwcode + 4;
3c6e85
 	if (qla24xx_risc_firmware_invalid(dcode)) {
3c6e85
 		ql_log(ql_log_fatal, vha, 0x0093,
3c6e85
 		    "Unable to verify integrity of firmware image (%Zd).\n",
3c6e85
@@ -7597,38 +7648,39 @@ qla24xx_load_risc_blob(scsi_qla_host_t *vha, uint32_t *srisc_addr)
3c6e85
 		return QLA_FUNCTION_FAILED;
3c6e85
 	}
3c6e85
 
3c6e85
-	while (segments && rval == QLA_SUCCESS) {
3c6e85
+	dcode = (void *)req->ring;
3c6e85
+	*srisc_addr = 0;
3c6e85
+	segments = FA_RISC_CODE_SEGMENTS;
3c6e85
+	for (j = 0; j < segments; j++) {
3c6e85
+		ql_dbg(ql_dbg_init, vha, 0x0096,
3c6e85
+		    "-> Loading segment %u...\n", j);
3c6e85
 		risc_addr = be32_to_cpu(fwcode[2]);
3c6e85
-		*srisc_addr = *srisc_addr == 0 ? risc_addr : *srisc_addr;
3c6e85
 		risc_size = be32_to_cpu(fwcode[3]);
3c6e85
 
3c6e85
-		/* Validate firmware image size. */
3c6e85
-		fwclen += risc_size * sizeof(uint32_t);
3c6e85
-		if (blob->fw->size < fwclen) {
3c6e85
-			ql_log(ql_log_fatal, vha, 0x0096,
3c6e85
-			    "Unable to verify integrity of firmware image "
3c6e85
-			    "(%Zd).\n", blob->fw->size);
3c6e85
-			return QLA_FUNCTION_FAILED;
3c6e85
+		if (!*srisc_addr) {
3c6e85
+			*srisc_addr = risc_addr;
3c6e85
+			risc_attr = be32_to_cpu(fwcode[9]);
3c6e85
 		}
3c6e85
 
3c6e85
-		fragment = 0;
3c6e85
-		while (risc_size > 0 && rval == QLA_SUCCESS) {
3c6e85
+		dlen = ha->fw_transfer_size >> 2;
3c6e85
+		for (fragment = 0; risc_size; fragment++) {
3c6e85
 			dlen = (uint32_t)(ha->fw_transfer_size >> 2);
3c6e85
 			if (dlen > risc_size)
3c6e85
 				dlen = risc_size;
3c6e85
 
3c6e85
 			ql_dbg(ql_dbg_init, vha, 0x0097,
3c6e85
-			    "Loading risc segment@ risc addr %x "
3c6e85
-			    "number of dwords 0x%x.\n", risc_addr, dlen);
3c6e85
+			    "-> Loading fragment %u: %#x <- %#x (%#lx words)...\n",
3c6e85
+			    fragment, risc_addr,
3c6e85
+			    (uint32_t)(fwcode - (typeof(fwcode))blob->fw->data),
3c6e85
+			    dlen);
3c6e85
 
3c6e85
 			for (i = 0; i < dlen; i++)
3c6e85
 				dcode[i] = swab32(fwcode[i]);
3c6e85
 
3c6e85
-			rval = qla2x00_load_ram(vha, req->dma, risc_addr,
3c6e85
-			    dlen);
3c6e85
+			rval = qla2x00_load_ram(vha, req->dma, risc_addr, dlen);
3c6e85
 			if (rval) {
3c6e85
 				ql_log(ql_log_fatal, vha, 0x0098,
3c6e85
-				    "Failed to load segment %d of firmware.\n",
3c6e85
+				    "-> Failed load firmware fragment %u.\n",
3c6e85
 				    fragment);
3c6e85
 				return QLA_FUNCTION_FAILED;
3c6e85
 			}
3c6e85
@@ -7636,71 +7688,82 @@ qla24xx_load_risc_blob(scsi_qla_host_t *vha, uint32_t *srisc_addr)
3c6e85
 			fwcode += dlen;
3c6e85
 			risc_addr += dlen;
3c6e85
 			risc_size -= dlen;
3c6e85
-			fragment++;
3c6e85
 		}
3c6e85
-
3c6e85
-		/* Next segment. */
3c6e85
-		segments--;
3c6e85
 	}
3c6e85
 
3c6e85
 	if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha))
3c6e85
-		return rval;
3c6e85
+		return QLA_SUCCESS;
3c6e85
 
3c6e85
-	if (ha->fw_dump_template)
3c6e85
-		vfree(ha->fw_dump_template);
3c6e85
-	ha->fw_dump_template = NULL;
3c6e85
-	ha->fw_dump_template_len = 0;
3c6e85
-
3c6e85
-	ql_dbg(ql_dbg_init, vha, 0x171,
3c6e85
-	    "Loading fwdump template from %lx\n",
3c6e85
-	    (void *)fwcode - (void *)blob->fw->data);
3c6e85
-	risc_size = be32_to_cpu(fwcode[2]);
3c6e85
-	ql_dbg(ql_dbg_init, vha, 0x172,
3c6e85
-	    "-> array size %x dwords\n", risc_size);
3c6e85
-	if (risc_size == 0 || risc_size == ~0)
3c6e85
-		goto failed;
3c6e85
+	templates = (risc_attr & BIT_9) ? 2 : 1;
3c6e85
+	ql_dbg(ql_dbg_init, vha, 0x0170, "-> templates = %u\n", templates);
3c6e85
+	for (j = 0; j < templates; j++, fwdt++) {
3c6e85
+		if (fwdt->template)
3c6e85
+			vfree(fwdt->template);
3c6e85
+		fwdt->template = NULL;
3c6e85
+		fwdt->length = 0;
3c6e85
+
3c6e85
+		risc_size = be32_to_cpu(fwcode[2]);
3c6e85
+		ql_dbg(ql_dbg_init, vha, 0x0171,
3c6e85
+		    "-> fwdt%u template array at %#x (%#x dwords)\n",
3c6e85
+		    j, (uint32_t)((void *)fwcode - (void *)blob->fw->data),
3c6e85
+		    risc_size);
3c6e85
+		if (!risc_size || !~risc_size) {
3c6e85
+			ql_dbg(ql_dbg_init, vha, 0x0172,
3c6e85
+			    "-> fwdt%u failed to read array\n", j);
3c6e85
+			goto failed;
3c6e85
+		}
3c6e85
 
3c6e85
-	dlen = (risc_size - 8) * sizeof(*fwcode);
3c6e85
-	ql_dbg(ql_dbg_init, vha, 0x0173,
3c6e85
-	    "-> template allocating %x bytes...\n", dlen);
3c6e85
-	ha->fw_dump_template = vmalloc(dlen);
3c6e85
-	if (!ha->fw_dump_template) {
3c6e85
-		ql_log(ql_log_warn, vha, 0x0174,
3c6e85
-		    "Failed fwdump template allocate %x bytes.\n", risc_size);
3c6e85
-		goto failed;
3c6e85
-	}
3c6e85
+		/* skip header and ignore checksum */
3c6e85
+		fwcode += 7;
3c6e85
+		risc_size -= 8;
3c6e85
+
3c6e85
+		ql_dbg(ql_dbg_init, vha, 0x0173,
3c6e85
+		    "-> fwdt%u template allocate template %#x words...\n",
3c6e85
+		    j, risc_size);
3c6e85
+		fwdt->template = vmalloc(risc_size * sizeof(*dcode));
3c6e85
+		if (!fwdt->template) {
3c6e85
+			ql_log(ql_log_warn, vha, 0x0174,
3c6e85
+			    "-> fwdt%u failed allocate template.\n", j);
3c6e85
+			goto failed;
3c6e85
+		}
3c6e85
 
3c6e85
-	fwcode += 7;
3c6e85
-	risc_size -= 8;
3c6e85
-	dcode = ha->fw_dump_template;
3c6e85
-	for (i = 0; i < risc_size; i++)
3c6e85
-		dcode[i] = le32_to_cpu(fwcode[i]);
3c6e85
+		dcode = fwdt->template;
3c6e85
+		for (i = 0; i < risc_size; i++)
3c6e85
+			dcode[i] = le32_to_cpu(fwcode[i]);
3c6e85
 
3c6e85
-	if (!qla27xx_fwdt_template_valid(dcode)) {
3c6e85
-		ql_log(ql_log_warn, vha, 0x0175,
3c6e85
-		    "Failed fwdump template validate\n");
3c6e85
-		goto failed;
3c6e85
-	}
3c6e85
+		if (!qla27xx_fwdt_template_valid(dcode)) {
3c6e85
+			ql_log(ql_log_warn, vha, 0x0175,
3c6e85
+			    "-> fwdt%u failed template validate\n", j);
3c6e85
+			goto failed;
3c6e85
+		}
3c6e85
 
3c6e85
-	dlen = qla27xx_fwdt_template_size(dcode);
3c6e85
-	ql_dbg(ql_dbg_init, vha, 0x0176,
3c6e85
-	    "-> template size %x bytes\n", dlen);
3c6e85
-	if (dlen > risc_size * sizeof(*fwcode)) {
3c6e85
-		ql_log(ql_log_warn, vha, 0x0177,
3c6e85
-		    "Failed fwdump template exceeds array by %zx bytes\n",
3c6e85
-		    (size_t)(dlen - risc_size * sizeof(*fwcode)));
3c6e85
-		goto failed;
3c6e85
+		dlen = qla27xx_fwdt_template_size(dcode);
3c6e85
+		ql_dbg(ql_dbg_init, vha, 0x0176,
3c6e85
+		    "-> fwdt%u template size %#lx bytes (%#lx words)\n",
3c6e85
+		    j, dlen, dlen / sizeof(*dcode));
3c6e85
+		if (dlen > risc_size * sizeof(*dcode)) {
3c6e85
+			ql_log(ql_log_warn, vha, 0x0177,
3c6e85
+			    "-> fwdt%u template exceeds array (%-lu bytes)\n",
3c6e85
+			    j, dlen - risc_size * sizeof(*dcode));
3c6e85
+			goto failed;
3c6e85
+		}
3c6e85
+
3c6e85
+		fwdt->length = dlen;
3c6e85
+		ql_dbg(ql_dbg_init, vha, 0x0178,
3c6e85
+		    "-> fwdt%u loaded template ok\n", j);
3c6e85
+
3c6e85
+		fwcode += risc_size + 1;
3c6e85
 	}
3c6e85
-	ha->fw_dump_template_len = dlen;
3c6e85
-	return rval;
3c6e85
+
3c6e85
+	return QLA_SUCCESS;
3c6e85
 
3c6e85
 failed:
3c6e85
-	ql_log(ql_log_warn, vha, 0x017d, "Failed fwdump template\n");
3c6e85
-	if (ha->fw_dump_template)
3c6e85
-		vfree(ha->fw_dump_template);
3c6e85
-	ha->fw_dump_template = NULL;
3c6e85
-	ha->fw_dump_template_len = 0;
3c6e85
-	return rval;
3c6e85
+	if (fwdt->template)
3c6e85
+		vfree(fwdt->template);
3c6e85
+	fwdt->template = NULL;
3c6e85
+	fwdt->length = 0;
3c6e85
+
3c6e85
+	return QLA_SUCCESS;
3c6e85
 }
3c6e85
 
3c6e85
 int
3c6e85
@@ -7967,9 +8030,8 @@ qla81xx_nvram_config(scsi_qla_host_t *vha)
3c6e85
 	    nv, ha->nvram_size);
3c6e85
 
3c6e85
 	/* Bad NVRAM data, set defaults parameters. */
3c6e85
-	if (chksum || nv->id[0] != 'I' || nv->id[1] != 'S' || nv->id[2] != 'P'
3c6e85
-	    || nv->id[3] != ' ' ||
3c6e85
-	    nv->nvram_version < cpu_to_le16(ICB_VERSION)) {
3c6e85
+	if (chksum || memcmp("ISP ", nv->id, sizeof(nv->id)) ||
3c6e85
+	    le16_to_cpu(nv->nvram_version) < ICB_VERSION) {
3c6e85
 		/* Reset NVRAM data. */
3c6e85
 		ql_log(ql_log_info, vha, 0x0073,
3c6e85
 		    "Inconsistent NVRAM detected: checksum=0x%x id=%c "
3c6e85
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
3c6e85
index 8c567a82752b..354977d41097 100644
3c6e85
--- a/drivers/scsi/qla2xxx/qla_os.c
3c6e85
+++ b/drivers/scsi/qla2xxx/qla_os.c
3c6e85
@@ -4734,6 +4734,9 @@ qla2x00_free_exchoffld_buffer(struct qla_hw_data *ha)
3c6e85
 static void
3c6e85
 qla2x00_free_fw_dump(struct qla_hw_data *ha)
3c6e85
 {
3c6e85
+	struct fwdt *fwdt = ha->fwdt;
3c6e85
+	uint j;
3c6e85
+
3c6e85
 	if (ha->fce)
3c6e85
 		dma_free_coherent(&ha->pdev->dev, FCE_SIZE, ha->fce,
3c6e85
 			ha->fce_dma);
3c6e85
@@ -4745,9 +4748,6 @@ qla2x00_free_fw_dump(struct qla_hw_data *ha)
3c6e85
 	if (ha->fw_dump)
3c6e85
 		vfree(ha->fw_dump);
3c6e85
 
3c6e85
-	if (ha->fw_dump_template)
3c6e85
-		vfree(ha->fw_dump_template);
3c6e85
-
3c6e85
 	ha->fce = NULL;
3c6e85
 	ha->fce_dma = 0;
3c6e85
 	ha->eft = NULL;
3c6e85
@@ -4757,8 +4757,13 @@ qla2x00_free_fw_dump(struct qla_hw_data *ha)
3c6e85
 	ha->fw_dump_reading = 0;
3c6e85
 	ha->fw_dump = NULL;
3c6e85
 	ha->fw_dump_len = 0;
3c6e85
-	ha->fw_dump_template = NULL;
3c6e85
-	ha->fw_dump_template_len = 0;
3c6e85
+
3c6e85
+	for (j = 0; j < 2; j++, fwdt++) {
3c6e85
+		if (fwdt->template)
3c6e85
+			vfree(fwdt->template);
3c6e85
+		fwdt->template = NULL;
3c6e85
+		fwdt->length = 0;
3c6e85
+	}
3c6e85
 }
3c6e85
 
3c6e85
 /*
3c6e85
diff --git a/drivers/scsi/qla2xxx/qla_sup.c b/drivers/scsi/qla2xxx/qla_sup.c
3c6e85
index a062cecb3980..5c0c62f1f010 100644
3c6e85
--- a/drivers/scsi/qla2xxx/qla_sup.c
3c6e85
+++ b/drivers/scsi/qla2xxx/qla_sup.c
3c6e85
@@ -2633,6 +2633,8 @@ qla25xx_read_optrom_data(struct scsi_qla_host *vha, uint8_t *buf,
3c6e85
 		goto slow_read;
3c6e85
 
3c6e85
 try_fast:
3c6e85
+	if (offset & 0xff)
3c6e85
+		goto slow_read;
3c6e85
 	optrom = dma_alloc_coherent(&ha->pdev->dev, OPTROM_BURST_SIZE,
3c6e85
 	    &optrom_dma, GFP_KERNEL);
3c6e85
 	if (!optrom) {
3c6e85
diff --git a/drivers/scsi/qla2xxx/qla_tmpl.c b/drivers/scsi/qla2xxx/qla_tmpl.c
3c6e85
index aab4aed88138..97525263b457 100644
3c6e85
--- a/drivers/scsi/qla2xxx/qla_tmpl.c
3c6e85
+++ b/drivers/scsi/qla2xxx/qla_tmpl.c
3c6e85
@@ -38,7 +38,6 @@ qla27xx_insert32(uint32_t value, void *buf, ulong *len)
3c6e85
 static inline void
3c6e85
 qla27xx_insertbuf(void *mem, ulong size, void *buf, ulong *len)
3c6e85
 {
3c6e85
-
3c6e85
 	if (buf && mem && size) {
3c6e85
 		buf += *len;
3c6e85
 		memcpy(buf, mem, size);
3c6e85
@@ -855,23 +854,11 @@ qla27xx_walk_template(struct scsi_qla_host *vha,
3c6e85
 
3c6e85
 	if (count)
3c6e85
 		ql_dbg(ql_dbg_misc, vha, 0xd018,
3c6e85
-		    "%s: entry residual count (%lx)\n", __func__, count);
3c6e85
+		    "%s: entry count residual=+%lu\n", __func__, count);
3c6e85
 
3c6e85
 	if (ent)
3c6e85
 		ql_dbg(ql_dbg_misc, vha, 0xd019,
3c6e85
-		    "%s: missing end entry (%lx)\n", __func__, count);
3c6e85
-
3c6e85
-	if (buf && *len != vha->hw->fw_dump_len)
3c6e85
-		ql_dbg(ql_dbg_misc, vha, 0xd01b,
3c6e85
-		    "%s: length=%#lx residual=%+ld\n",
3c6e85
-		    __func__, *len, vha->hw->fw_dump_len - *len);
3c6e85
-
3c6e85
-	if (buf) {
3c6e85
-		ql_log(ql_log_warn, vha, 0xd015,
3c6e85
-		    "Firmware dump saved to temp buffer (%lu/%p)\n",
3c6e85
-		    vha->host_no, vha->hw->fw_dump);
3c6e85
-		qla2x00_post_uevent_work(vha, QLA_UEVENT_CODE_FW_DUMP);
3c6e85
-	}
3c6e85
+		    "%s: missing end entry\n", __func__);
3c6e85
 }
3c6e85
 
3c6e85
 static void
3c6e85
@@ -894,8 +881,8 @@ qla27xx_driver_info(struct qla27xx_fwdt_template *tmp)
3c6e85
 }
3c6e85
 
3c6e85
 static void
3c6e85
-qla27xx_firmware_info(struct qla27xx_fwdt_template *tmp,
3c6e85
-	struct scsi_qla_host *vha)
3c6e85
+qla27xx_firmware_info(struct scsi_qla_host *vha,
3c6e85
+    struct qla27xx_fwdt_template *tmp)
3c6e85
 {
3c6e85
 	tmp->firmware_version[0] = vha->hw->fw_major_version;
3c6e85
 	tmp->firmware_version[1] = vha->hw->fw_minor_version;
3c6e85
@@ -912,7 +899,7 @@ ql27xx_edit_template(struct scsi_qla_host *vha,
3c6e85
 {
3c6e85
 	qla27xx_time_stamp(tmp);
3c6e85
 	qla27xx_driver_info(tmp);
3c6e85
-	qla27xx_firmware_info(tmp, vha);
3c6e85
+	qla27xx_firmware_info(vha, tmp);
3c6e85
 }
3c6e85
 
3c6e85
 static inline uint32_t
3c6e85
@@ -943,26 +930,26 @@ qla27xx_verify_template_header(struct qla27xx_fwdt_template *tmp)
3c6e85
 	return le32_to_cpu(tmp->template_type) == TEMPLATE_TYPE_FWDUMP;
3c6e85
 }
3c6e85
 
3c6e85
-static void
3c6e85
-qla27xx_execute_fwdt_template(struct scsi_qla_host *vha)
3c6e85
+static ulong
3c6e85
+qla27xx_execute_fwdt_template(struct scsi_qla_host *vha,
3c6e85
+    struct qla27xx_fwdt_template *tmp, void *buf)
3c6e85
 {
3c6e85
-	struct qla27xx_fwdt_template *tmp = vha->hw->fw_dump_template;
3c6e85
-	ulong len;
3c6e85
+	ulong len = 0;
3c6e85
 
3c6e85
 	if (qla27xx_fwdt_template_valid(tmp)) {
3c6e85
 		len = tmp->template_size;
3c6e85
-		tmp = memcpy(vha->hw->fw_dump, tmp, len);
3c6e85
+		tmp = memcpy(buf, tmp, len);
3c6e85
 		ql27xx_edit_template(vha, tmp);
3c6e85
-		qla27xx_walk_template(vha, tmp, tmp, &len;;
3c6e85
-		vha->hw->fw_dump_len = len;
3c6e85
-		vha->hw->fw_dumped = 1;
3c6e85
+		qla27xx_walk_template(vha, tmp, buf, &len;;
3c6e85
 	}
3c6e85
+
3c6e85
+	return len;
3c6e85
 }
3c6e85
 
3c6e85
 ulong
3c6e85
-qla27xx_fwdt_calculate_dump_size(struct scsi_qla_host *vha)
3c6e85
+qla27xx_fwdt_calculate_dump_size(struct scsi_qla_host *vha, void *p)
3c6e85
 {
3c6e85
-	struct qla27xx_fwdt_template *tmp = vha->hw->fw_dump_template;
3c6e85
+	struct qla27xx_fwdt_template *tmp = p;
3c6e85
 	ulong len = 0;
3c6e85
 
3c6e85
 	if (qla27xx_fwdt_template_valid(tmp)) {
3c6e85
@@ -1012,17 +999,41 @@ qla27xx_fwdump(scsi_qla_host_t *vha, int hardware_locked)
3c6e85
 		spin_lock_irqsave(&vha->hw->hardware_lock, flags);
3c6e85
 #endif
3c6e85
 
3c6e85
-	if (!vha->hw->fw_dump)
3c6e85
-		ql_log(ql_log_warn, vha, 0xd01e, "fwdump buffer missing.\n");
3c6e85
-	else if (!vha->hw->fw_dump_template)
3c6e85
-		ql_log(ql_log_warn, vha, 0xd01f, "fwdump template missing.\n");
3c6e85
-	else if (vha->hw->fw_dumped)
3c6e85
-		ql_log(ql_log_warn, vha, 0xd300,
3c6e85
-		    "Firmware has been previously dumped (%p),"
3c6e85
-		    " -- ignoring request\n", vha->hw->fw_dump);
3c6e85
-	else {
3c6e85
-		QLA_FW_STOPPED(vha->hw);
3c6e85
-		qla27xx_execute_fwdt_template(vha);
3c6e85
+	if (!vha->hw->fw_dump) {
3c6e85
+		ql_log(ql_log_warn, vha, 0xd01e, "-> fwdump no buffer\n");
3c6e85
+	} else if (vha->hw->fw_dumped) {
3c6e85
+		ql_log(ql_log_warn, vha, 0xd01f,
3c6e85
+		    "-> Firmware already dumped (%p) -- ignoring request\n",
3c6e85
+		    vha->hw->fw_dump);
3c6e85
+	} else {
3c6e85
+		struct fwdt *fwdt = vha->hw->fwdt;
3c6e85
+		uint j;
3c6e85
+		ulong len;
3c6e85
+		void *buf = vha->hw->fw_dump;
3c6e85
+
3c6e85
+		for (j = 0; j < 2; j++, fwdt++, buf += len) {
3c6e85
+			ql_log(ql_log_warn, vha, 0xd011,
3c6e85
+			    "-> fwdt%u running...\n", j);
3c6e85
+			if (!fwdt->template) {
3c6e85
+				ql_log(ql_log_warn, vha, 0xd012,
3c6e85
+				    "-> fwdt%u no template\n", j);
3c6e85
+				break;
3c6e85
+			}
3c6e85
+			len = qla27xx_execute_fwdt_template(vha,
3c6e85
+			    fwdt->template, buf);
3c6e85
+			if (len != fwdt->dump_size) {
3c6e85
+				ql_log(ql_log_warn, vha, 0xd013,
3c6e85
+				    "-> fwdt%u fwdump residual=%+ld\n",
3c6e85
+				    j, fwdt->dump_size - len);
3c6e85
+			}
3c6e85
+		}
3c6e85
+		vha->hw->fw_dump_len = buf - (void *)vha->hw->fw_dump;
3c6e85
+		vha->hw->fw_dumped = 1;
3c6e85
+
3c6e85
+		ql_log(ql_log_warn, vha, 0xd015,
3c6e85
+		    "-> Firmware dump saved to buffer (%lu/%p) <%lx>\n",
3c6e85
+		    vha->host_no, vha->hw->fw_dump, vha->hw->fw_dump_cap_flags);
3c6e85
+		qla2x00_post_uevent_work(vha, QLA_UEVENT_CODE_FW_DUMP);
3c6e85
 	}
3c6e85
 
3c6e85
 #ifndef __CHECKER__
3c6e85
-- 
3c6e85
2.13.6
3c6e85