Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > fc11cd6e1c513a17304da94a5390f3cd > files > 3571

kernel-2.6.18-194.11.1.el5.src.rpm

From: Marcus Barrow <mbarrow@redhat.com>
Subject: [Bug 260701][QLogic][RHEL 5.1 bug] regression, 	nvram/vpd updates produce soft lockups and system hangs.
Date: Tue, 28 Aug 2007 15:14:15 -0400
Bugzilla: 260701
Message-Id: <20070828191415.3104.35369.sendpatchset@shell.boston.redhat.com>
Changelog: [scsi] qla2xxx: nvram/vpd updates produce soft lockups and system hangs

BZ 260701

Updates to the nvram / vpd data on the HBA can cause software lockups
and hangs / nmi watchdog. 

This has been observed in recent testing at QLogic. This was not found
earlier due to a bug in user space libraries which failed to perform
proper updateing. 

Regards,
Marcus Barrow



---
 drivers/scsi/qla2xxx/qla_attr.c |   38 +++++++++++++++++++++++---------------
 drivers/scsi/qla2xxx/qla_def.h  |    4 ++++
 drivers/scsi/qla2xxx/qla_init.c |   19 +++++++++++--------
 drivers/scsi/qla2xxx/qla_os.c   |   14 ++++++++++++++
 4 files changed, 52 insertions(+), 23 deletions(-)

diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c
index 8081b63..aa232a5 100644
--- a/drivers/scsi/qla2xxx/qla_attr.c
+++ b/drivers/scsi/qla2xxx/qla_attr.c
@@ -86,18 +86,20 @@ qla2x00_sysfs_read_nvram(struct kobject
 {
 	struct scsi_qla_host *ha = to_qla_host(dev_to_shost(container_of(kobj,
 	    struct device, kobj)));
-	unsigned long	flags;
+	int		size = ha->nvram_size;
+	char		*nvram_cache = ha->nvram;
 
-	if (!capable(CAP_SYS_ADMIN) || off != 0)
+	if (!capable(CAP_SYS_ADMIN) || off > size || count == 0)
 		return 0;
+	if (off + count > size) {
+		size -= off;
+		count = size;
+	}
 
-	/* Read NVRAM. */
-	spin_lock_irqsave(&ha->hardware_lock, flags);
-	ha->isp_ops.read_nvram(ha, (uint8_t *)buf, ha->nvram_base,
-	    ha->nvram_size);
-	spin_unlock_irqrestore(&ha->hardware_lock, flags);
+	/* Read NVRAM data from cache. */
+	memcpy(buf, &nvram_cache[off], count);
 
-	return ha->nvram_size;
+	return count;
 }
 
 static ssize_t
@@ -138,6 +140,8 @@ qla2x00_sysfs_write_nvram(struct kobject
 	/* Write NVRAM. */
 	spin_lock_irqsave(&ha->hardware_lock, flags);
 	ha->isp_ops.write_nvram(ha, (uint8_t *)buf, ha->nvram_base, count);
+	ha->isp_ops.read_nvram(ha, (uint8_t *)&ha->nvram, ha->nvram_base,
+	    count);
 	spin_unlock_irqrestore(&ha->hardware_lock, flags);
 
 	set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
@@ -291,17 +295,20 @@ qla2x00_sysfs_read_vpd(struct kobject *k
 {
 	struct scsi_qla_host *ha = to_qla_host(dev_to_shost(container_of(kobj,
 	    struct device, kobj)));
-	unsigned long flags;
+	int           size = ha->vpd_size;
+	char          *vpd_cache = ha->vpd;
 
-	if (!capable(CAP_SYS_ADMIN) || off != 0)
+	if (!capable(CAP_SYS_ADMIN) || off > size || count == 0)
 		return 0;
+	if (off + count > size) {
+		size -= off;
+		count = size;
+	}
 
-	/* Read NVRAM. */
-	spin_lock_irqsave(&ha->hardware_lock, flags);
-	ha->isp_ops.read_nvram(ha, (uint8_t *)buf, ha->vpd_base, ha->vpd_size);
-	spin_unlock_irqrestore(&ha->hardware_lock, flags);
+	/* Read NVRAM data from cache. */
+	memcpy(buf, &vpd_cache[off], count);
 
-	return ha->vpd_size;
+	return count;
 }
 
 static ssize_t
@@ -318,6 +325,7 @@ qla2x00_sysfs_write_vpd(struct kobject *
 	/* Write NVRAM. */
 	spin_lock_irqsave(&ha->hardware_lock, flags);
 	ha->isp_ops.write_nvram(ha, (uint8_t *)buf, ha->vpd_base, count);
+	ha->isp_ops.read_nvram(ha, (uint8_t *)ha->vpd, ha->vpd_base, count);
 	spin_unlock_irqrestore(&ha->hardware_lock, flags);
 
 	return count;
diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h
index 7c364e0..bb3931a 100644
--- a/drivers/scsi/qla2xxx/qla_def.h
+++ b/drivers/scsi/qla2xxx/qla_def.h
@@ -2272,10 +2272,14 @@ typedef struct scsi_qla_host {
 	uint8_t		serial2;
 
 	/* NVRAM configuration data */
+#define MAX_NVRAM_SIZE	4096
+#define VPD_OFFSET	MAX_NVRAM_SIZE / 2
 	uint16_t	nvram_size;
 	uint16_t	nvram_base;
+	void		*nvram;
 	uint16_t	vpd_size;
 	uint16_t	vpd_base;
+	void		*vpd;
 
 	uint16_t	loop_reset_delay;
 	uint8_t		retry_count;
diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c
index 98c01cd..d137210 100644
--- a/drivers/scsi/qla2xxx/qla_init.c
+++ b/drivers/scsi/qla2xxx/qla_init.c
@@ -1413,8 +1413,8 @@ qla2x00_nvram_config(scsi_qla_host_t *ha
 	uint16_t        cnt;
 	uint8_t         *dptr1, *dptr2;
 	init_cb_t       *icb = ha->init_cb;
-	nvram_t         *nv = (nvram_t *)ha->request_ring;
-	uint8_t         *ptr = (uint8_t *)ha->request_ring;
+	nvram_t         *nv = ha->nvram;
+	uint8_t         *ptr = ha->nvram;
 	struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
 
 	/* Determine NVRAM starting address. */
@@ -1430,8 +1430,7 @@ qla2x00_nvram_config(scsi_qla_host_t *ha
 		chksum += *ptr++;
 
 	DEBUG5(printk("scsi(%ld): Contents of NVRAM\n", ha->host_no));
-	DEBUG5(qla2x00_dump_buffer((uint8_t *)ha->request_ring,
-	    ha->nvram_size));
+	DEBUG5(qla2x00_dump_buffer((uint8_t *)nv, ha->nvram_size));
 
 	/* Bad NVRAM data, set defaults parameters. */
 	if (chksum || nv->id[0] != 'I' || nv->id[1] != 'S' ||
@@ -3301,7 +3300,7 @@ qla24xx_nvram_config(scsi_qla_host_t *ha
 	uint16_t cnt;
 
 	icb = (struct init_cb_24xx *)ha->init_cb;
-	nv = (struct nvram_24xx *)ha->request_ring;
+	nv = ha->nvram;
 
 	/* Determine NVRAM starting address. */
 	ha->nvram_size = sizeof(struct nvram_24xx);
@@ -3313,7 +3312,12 @@ qla24xx_nvram_config(scsi_qla_host_t *ha
 		ha->vpd_base = FA_NVRAM_VPD1_ADDR;
 	}
 
-	/* Get NVRAM data and calculate checksum. */
+	/* Get VPD data into cache */
+	ha->vpd = ha->nvram + VPD_OFFSET;
+	ha->isp_ops.read_nvram(ha, (uint8_t *)ha->vpd,
+	    ha->nvram_base - FA_NVRAM_FUNC0_ADDR, FA_NVRAM_VPD_SIZE * 4);
+
+	/* Get NVRAM data into cache and calculate checksum. */
 	dptr = (uint32_t *)nv;
 	ha->isp_ops.read_nvram(ha, (uint8_t *)dptr, ha->nvram_base,
 	    ha->nvram_size);
@@ -3321,8 +3325,7 @@ qla24xx_nvram_config(scsi_qla_host_t *ha
 		chksum += le32_to_cpu(*dptr++);
 
 	DEBUG5(printk("scsi(%ld): Contents of NVRAM\n", ha->host_no));
-	DEBUG5(qla2x00_dump_buffer((uint8_t *)ha->request_ring,
-	    ha->nvram_size));
+	DEBUG5(qla2x00_dump_buffer((uint8_t *)nv, ha->nvram_size));
 
 	/* Bad NVRAM data, set defaults parameters. */
 	if (chksum || nv->id[0] != 'I' || nv->id[1] != 'S' || nv->id[2] != 'P'
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index 6388091..5919a59 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -2041,6 +2041,19 @@ qla2x00_mem_alloc(scsi_qla_host_t *ha)
 			}
 		}
 
+		/* Get memory for cached NVRAM */
+		ha->nvram = kzalloc(MAX_NVRAM_SIZE, GFP_KERNEL);
+		if (ha->nvram == NULL) {
+			/* error */
+			qla_printk(KERN_WARNING, ha,
+			    "Memory Allocation failed - nvram cache\n");
+
+			qla2x00_mem_free(ha);
+			msleep(100);
+
+			continue;
+		}
+
 		/* Done all allocations without any error. */
 		status = 0;
 
@@ -2152,6 +2165,7 @@ qla2x00_mem_free(scsi_qla_host_t *ha)
 	ha->fw_dump_reading = 0;
 
 	vfree(ha->optrom_buffer);
+	kfree(ha->nvram);
 }
 
 /*
-- 
1.4.4.1