Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > media > main-src > by-pkgid > d0a35cd31c1125e2132804d68547073d > files > 3393

kernel-2.6.18-194.26.1.el5.src.rpm

From: Tomas Henzl <thenzl@redhat.com>
Date: Wed, 22 Apr 2009 15:18:50 +0300
Subject: [scsi] cciss: changes in config functions
Message-id: 49EF0B2A.9090707@redhat.com
O-Subject: [RHEL5.4 PATCH1/4] cciss changes in config functions
Bugzilla: 474392
RH-Acked-by: Mike Christie <mchristi@redhat.com>

Upstream in
6ae5ce8e8d4de666f31286808d2285aa6a50fa40
a72da29b6cbc5cf918567f2a0d76df6871e94b01

HP:
This patch removes redundant code where ever logical volumes are added or
removed. It adds 3 new functions that are called instead of having the same
code spread throughout the driver. It also removes the cciss_getgeometry
function.
It also makes the rebuild_lun_table smart enough to not rip a logical
volume out from under the OS. Without this fix if a customer is running
hpacucli to monitor their storage the driver will blindly remove and re-add
the disks whenever the utility calls the CCISS_REGNEWD ioctl. Unfortunately,
both hpacucli and ACUXE call the ioctl repeatedly. Customers have reported
IO coming to a standstill. Calling the ioctl is the problem, this patch is
the fix.

diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c
index bc3c3ef..ba63667 100644
--- a/drivers/block/cciss.c
+++ b/drivers/block/cciss.c
@@ -161,9 +161,8 @@ static int cciss_ioctl(struct inode *inode, struct file *filep,
 static int cciss_getgeo(struct block_device *bdev, struct hd_geometry *geo);
 
 static int cciss_revalidate(struct gendisk *disk);
-static int rebuild_lun_table(ctlr_info_t *h, struct gendisk *del_disk);
-static int deregister_disk(struct gendisk *disk, drive_info_struct *drv,
-			   int clear_all);
+static int rebuild_lun_table(ctlr_info_t *h, int first_time);
+static int deregister_disk(ctlr_info_t *h, int drv_index, int clear_all);
 static void cciss_read_capacity(int ctlr, int logvol, int withirq,
 			sector_t *total_size, unsigned int *block_size);
 static void cciss_read_capacity_16(int ctlr, int logvol, int withirq,
@@ -172,7 +171,6 @@ static void cciss_geometry_inquiry(int ctlr, int logvol, int withirq,
 			sector_t total_size, unsigned int block_size,
 			InquiryData_struct *inq_buff,
 			drive_info_struct *drv);
-static void cciss_getgeometry(int cntl_num);
 static void __devinit cciss_interrupt_mode(ctlr_info_t *, struct pci_dev *,
 					   __u32);
 static void start_io(ctlr_info_t *h);
@@ -933,8 +931,10 @@ static int cciss_ioctl(struct inode *inode, struct file *filep,
 			return 0;
 		}
 
+	case CCISS_REGNEWD:
+	case CCISS_DEREGDISK:
 	case CCISS_REVALIDVOLS:
-		return rebuild_lun_table(host, NULL);
+		return rebuild_lun_table(host, 0);
 
 	case CCISS_GETLUNINFO:{
 			LogvolInfo_struct luninfo;
@@ -947,11 +947,6 @@ static int cciss_ioctl(struct inode *inode, struct file *filep,
 				return -EFAULT;
 			return 0;
 		}
-	case CCISS_DEREGDISK:
-		return rebuild_lun_table(host, disk);
-
-	case CCISS_REGNEWD:
-		return rebuild_lun_table(host, NULL);
 
 	case CCISS_PASSTHRU:
 		{
@@ -1362,15 +1357,84 @@ static void cciss_softirq_done(struct request *rq)
 	spin_unlock_irqrestore(&h->lock, flags);
 }
 
+/* This function gets the unique id number of a logical drive via
+ * inquiry page 0x83.  Unique id is 16 bytes.  If the unique
+ * id cannot be had, for whatever reason, 16 bytes of 0xff
+ * are returned instead.
+ */
+static void cciss_get_uid(int ctlr, int logvol, int withirq,
+				unsigned char *uid, int buflen)
+{
+#define PAGE_83_INQ_BYTES 64
+	int rc;
+	unsigned char *buf;
+
+	if (buflen > 16)
+		buflen = 16;
+	memset(uid, 0xff, buflen);
+	buf = kzalloc(PAGE_83_INQ_BYTES, GFP_KERNEL);
+	if (!buf)
+		return;
+	memset(uid, 0, buflen);
+	if (withirq)
+		rc = sendcmd_withirq(CISS_INQUIRY, ctlr, buf,
+			PAGE_83_INQ_BYTES, 1, logvol, 0x83, TYPE_CMD);
+	else
+		rc = sendcmd(CISS_INQUIRY, ctlr, buf,
+			PAGE_83_INQ_BYTES, 1, logvol, 0x83, NULL, TYPE_CMD);
+	if (rc == IO_OK)
+		memcpy(uid, &buf[8], buflen);
+	kfree(buf);
+	return;
+}
+
+static void cciss_add_disk(ctlr_info_t *h, struct gendisk *disk,
+				int drv_index)
+{
+	disk->queue = blk_init_queue(do_cciss_request, &h->lock);
+	sprintf(disk->disk_name, "cciss/c%dd%d", h->ctlr, drv_index);
+	disk->major = h->major;
+	disk->first_minor = drv_index << NWD_SHIFT;
+	disk->fops = &cciss_fops;
+	disk->private_data = &h->drv[drv_index];
+
+	/* Set up queue information */
+	blk_queue_bounce_limit(disk->queue, h->pdev->dma_mask);
+
+	/* This is a hardware imposed limit. */
+	blk_queue_max_hw_segments(disk->queue, MAXSGENTRIES);
+
+	/* This is a limit in the driver and could be eliminated. */
+	blk_queue_max_phys_segments(disk->queue, MAXSGENTRIES);
+
+	blk_queue_max_sectors(disk->queue, h->cciss_sector_size);
+
+	blk_queue_softirq_done(disk->queue, cciss_softirq_done);
+
+	disk->queue->queuedata = h;
+
+	blk_queue_hardsect_size(disk->queue,
+				h->drv[drv_index].block_size);
+
+	/* Make sure all queue data is written out before */
+	/* setting h->drv[drv_index].queue, as setting this */
+	/* allows the interrupt handler to start the queue */
+	wmb();
+	h->drv[drv_index].queue = disk->queue;
+	add_disk(disk);
+}
+
 /* This function will check the usage_count of the drive to be updated/added.
- * If the usage_count is zero then the drive information will be updated and
- * the disk will be re-registered with the kernel.  If not then it will be
- * left alone for the next reboot.  The exception to this is disk 0 which
- * will always be left registered with the kernel since it is also the
- * controller node.  Any changes to disk 0 will show up on the next
- * reboot.
+ * If the usage_count is zero and it is a heretofore unknown drive, or,
+ * the drive's capacity, geometry, or serial number has changed,
+ * then the drive information will be updated and the disk will be
+ * re-registered with the kernel.  If these conditions don't hold,
+ * then it will be left alone for the next reboot.  The exception to this
+ * is disk 0 which will always be left registered with the kernel since it
+ * is also the controller node.  Any changes to disk 0 will show up on
+ * the next reboot.
  */
-static void cciss_update_drive_info(int ctlr, int drv_index)
+static void cciss_update_drive_info(int ctlr, int drv_index, int first_time)
 {
 	ctlr_info_t *h = hba[ctlr];
 	struct gendisk *disk;
@@ -1379,9 +1443,73 @@ static void cciss_update_drive_info(int ctlr, int drv_index)
 	sector_t total_size;
 	unsigned long flags = 0;
 	int ret = 0;
+	drive_info_struct *drvinfo;
+	int was_only_controller_node;
+
+	/* Get information about the disk and modify the driver structure */
+	inq_buff = kmalloc(sizeof(InquiryData_struct), GFP_KERNEL);
+	drvinfo = kmalloc(sizeof(*drvinfo), GFP_KERNEL);
+	if (inq_buff == NULL || drvinfo == NULL)
+		goto mem_msg;
+
+	/* See if we're trying to update the "controller node"
+	 * this will happen the when the first logical drive gets
+	 * created by ACU.
+	 */
+	was_only_controller_node = (drv_index == 0 &&
+				h->drv[0].raid_level == -1);
+
+	/* testing to see if 16-byte CDBs are already being used */
+	if (h->cciss_read == CCISS_READ_16) {
+		cciss_read_capacity_16(h->ctlr, drv_index, 1,
+			&total_size, &block_size);
+
+	} else {
+		cciss_read_capacity(ctlr, drv_index, 1,
+				    &total_size, &block_size);
+
+		/* if read_capacity returns all F's this volume is >2TB */
+		/* in size so we switch to 16-byte CDB's for all */
+		/* read/write ops */
+		if (total_size == 0xFFFFFFFFULL) {
+			cciss_read_capacity_16(ctlr, drv_index, 1,
+			&total_size, &block_size);
+			h->cciss_read = CCISS_READ_16;
+			h->cciss_write = CCISS_WRITE_16;
+		} else {
+			h->cciss_read = CCISS_READ_10;
+			h->cciss_write = CCISS_WRITE_10;
+		}
+	}
+
+	cciss_geometry_inquiry(ctlr, drv_index, 1, total_size, block_size,
+			       inq_buff, drvinfo);
+	drvinfo->block_size = block_size;
+	drvinfo->nr_blocks = total_size + 1;
+
+	cciss_get_uid(ctlr, drv_index, 1, drvinfo->uid,
+			sizeof(drvinfo->uid));
+
+	/* Is it the same disk we already know, and nothing's changed? */
+	if (h->drv[drv_index].raid_level != -1 &&
+		((memcmp(drvinfo->uid,
+				h->drv[drv_index].uid, 16) == 0) &&
+		drvinfo->block_size == h->drv[drv_index].block_size &&
+		drvinfo->nr_blocks == h->drv[drv_index].nr_blocks &&
+		drvinfo->heads == h->drv[drv_index].heads &&
+		drvinfo->sectors == h->drv[drv_index].sectors &&
+		drvinfo->cylinders == h->drv[drv_index].cylinders)) {
+			/* The disk is unchanged, nothing to update */
+			goto freeret;
+	}
+
+	/* Not the same disk, or something's changed, so we need to */
+	/* deregister it, and re-register it, if it's not in use. */
 
 	/* if the disk already exists then deregister it before proceeding */
-	if (h->drv[drv_index].raid_level != -1) {
+	/* (unless it's the first disk (for the controller node). */
+	if (h->drv[drv_index].raid_level != -1 && drv_index != 0) {
+		printk(KERN_WARNING "disk %d has changed.\n", drv_index);
 		spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags);
 		h->drv[drv_index].busy_configuring = 1;
 		spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
@@ -1389,88 +1517,38 @@ static void cciss_update_drive_info(int ctlr, int drv_index)
 		/* deregister_disk sets h->drv[drv_index].queue = NULL */
 		/* which keeps the interrupt handler from starting */
 		/* the queue. */
-		ret = deregister_disk(h->gendisk[drv_index],
-				      &h->drv[drv_index], 0);
+		ret = deregister_disk(h, drv_index, 0);
 		h->drv[drv_index].busy_configuring = 0;
 	}
 
 	/* If the disk is in use return */
 	if (ret)
-		return;
-
-	/* Get information about the disk and modify the driver structure */
-	inq_buff = kmalloc(sizeof(InquiryData_struct), GFP_KERNEL);
-	if (inq_buff == NULL)
-		goto mem_msg;
-
-	/* testing to see if 16-byte CDBs are already being used */
-	if (h->cciss_read == CCISS_READ_16) {
-		cciss_read_capacity_16(h->ctlr, drv_index, 1,
-				       &total_size, &block_size);
-		goto geo_inq;
-	}
-
-	cciss_read_capacity(ctlr, drv_index, 1, 
-			    &total_size, &block_size);
-
-	/* If read_capacity returns all F's the logical is >2TB in size */
-	/* so we switch to 16-byte CDBs for all read/write ops */
-	if (total_size == 0xFFFFFFFFULL) {
-		cciss_read_capacity_16(ctlr, drv_index, 1,
-				       &total_size, &block_size);
-		h->cciss_read = CCISS_READ_16;
-		h->cciss_write = CCISS_WRITE_16;
-	} else {
-		h->cciss_read = CCISS_READ_10;
-		h->cciss_write = CCISS_WRITE_10;
-	}
-geo_inq:
-	cciss_geometry_inquiry(ctlr, drv_index, 1, total_size, block_size,
-			       inq_buff, &h->drv[drv_index]);
+ 		goto freeret;
+  
+ 	/* Save the new information from cciss_geometry_inquiry */
+ 	/* and serial number inquiry. */
+ 	h->drv[drv_index].block_size = drvinfo->block_size;
+ 	h->drv[drv_index].nr_blocks = drvinfo->nr_blocks;
+ 	h->drv[drv_index].heads = drvinfo->heads;
+ 	h->drv[drv_index].sectors = drvinfo->sectors;
+ 	h->drv[drv_index].cylinders = drvinfo->cylinders;
+ 	h->drv[drv_index].raid_level = drvinfo->raid_level;
+ 	memcpy(h->drv[drv_index].uid, drvinfo->uid, 16);
 
 	++h->num_luns;
 	disk = h->gendisk[drv_index];
 	set_capacity(disk, h->drv[drv_index].nr_blocks);
 
-	/* if it's the controller it's already added */
-	if (drv_index) {
-		disk->queue = blk_init_queue(do_cciss_request, &h->lock);
-		sprintf(disk->disk_name, "cciss/c%dd%d", ctlr, drv_index);
-		disk->major = h->major;
-		disk->first_minor = drv_index << NWD_SHIFT;
-		disk->fops = &cciss_fops;
-		disk->private_data = &h->drv[drv_index];
+ 	/* We only add the c*d0 node once, at driver init time */
+ 	/* and never remove it. */
+ 	if (drv_index || first_time)
+ 		cciss_add_disk(h, disk, drv_index);
 
-		/* Set up queue information */
-		blk_queue_bounce_limit(disk->queue, hba[ctlr]->pdev->dma_mask);
-
-		/* This is a hardware imposed limit. */
-		blk_queue_max_hw_segments(disk->queue, MAXSGENTRIES);
-
-		/* This is a limit in the driver and could be eliminated. */
-		blk_queue_max_phys_segments(disk->queue, MAXSGENTRIES);
-
-		blk_queue_max_sectors(disk->queue, h->cciss_sector_size);
-
-		blk_queue_softirq_done(disk->queue, cciss_softirq_done);
-
-		disk->queue->queuedata = hba[ctlr];
-
-		blk_queue_hardsect_size(disk->queue,
-					hba[ctlr]->drv[drv_index].block_size);
-
-		/* Make sure all queue data is written out before */
-		/* setting h->drv[drv_index].queue, as setting this */
-		/* allows the interrupt handler to start the queue */
-		wmb();
-		h->drv[drv_index].queue = disk->queue;
-		add_disk(disk);
-	}
-
-      freeret:
+freeret:
 	kfree(inq_buff);
+	kfree(drvinfo);
 	return;
-      mem_msg:
+mem_msg:
 	printk(KERN_ERR "cciss: out of memory\n");
 	goto freeret;
 }
@@ -1480,21 +1558,97 @@ geo_inq:
  * where new drives will be added.  If the index to be returned is greater
  * than the highest_lun index for the controller then highest_lun is set
  * to this new index.  If there are no available indexes then -1 is returned.
+ * "is_controller_node" is used to know if this is a real logical drive, or 
+ * just * the controller node, which determines if this counts towards
+ * highest_lun.
  */
-static int cciss_find_free_drive_index(int ctlr)
+
+static int cciss_find_free_drive_index(int ctlr, int is_controller_node)
 {
 	int i;
 
 	for (i = 0; i < CISS_MAX_LUN; i++) {
 		if (hba[ctlr]->drv[i].raid_level == -1) {
 			if (i > hba[ctlr]->highest_lun)
-				hba[ctlr]->highest_lun = i;
+				if (!is_controller_node)
+					hba[ctlr]->highest_lun = i;
 			return i;
 		}
 	}
 	return -1;
 }
 
+/* cciss_add_gendisk finds a free hba[]->drv structure
+ * and allocates a gendisk if needed, and sets the lunid
+ * in the drvinfo structure.   It returns the index into
+ * the ->drv[] array, or -1 if none are free.
+ * is_controller_node indicates whether highest_lun should
+ * count this disk, or if it's only being added to provide
+ * a means to talk to the controller in case no logical
+ * drives have yet been configured.
+ */
+static int cciss_add_gendisk(ctlr_info_t *h, __u32 lunid, int controller_node)
+{
+	int drv_index;
+
+	drv_index = cciss_find_free_drive_index(h->ctlr, controller_node);
+	if (drv_index == -1)
+		return -1;
+	/*Check if the gendisk needs to be allocated */
+	if (!h->gendisk[drv_index]) {
+		h->gendisk[drv_index] =
+			alloc_disk(1 << NWD_SHIFT);
+		if (!h->gendisk[drv_index]) {
+			printk(KERN_ERR "cciss%d: could not "
+				"allocate a new disk %d\n",
+				h->ctlr, drv_index);
+			return -1;
+		}
+	}
+	h->drv[drv_index].LunID = lunid;
+
+	/* Don't need to mark this busy because nobody
+	 * else knows about this disk yet to contend
+	 * for access to it.
+	 */
+	h->drv[drv_index].busy_configuring = 0;
+	wmb();
+	return drv_index;
+}
+
+/* This is for the special case of a controller which
+ * has no logical drives.  In this case, we still need
+ * to register a disk so the controller can be accessed
+ * by the Array Config Utility.
+ */
+static void cciss_add_controller_node(ctlr_info_t *h)
+{
+	struct gendisk *disk;
+	int drv_index;
+
+	if (h->gendisk[0] != NULL) /* already did this? Then bail. */
+		return;
+
+	drv_index = cciss_add_gendisk(h, 0, 1);
+	if (drv_index == -1) {
+		printk(KERN_WARNING "cciss%d: could not "
+			"add disk 0.\n", h->ctlr);
+		return;
+	}
+	h->drv[drv_index].block_size = 512;
+	h->drv[drv_index].nr_blocks = 0;
+	h->drv[drv_index].heads = 0;
+	h->drv[drv_index].sectors = 0;
+	h->drv[drv_index].cylinders = 0;
+	h->drv[drv_index].raid_level = -1;
+	memset(h->drv[drv_index].uid, 0, sizeof(h->drv[drv_index].uid));
+	memset(h->drv[drv_index].vendor, 0, sizeof(h->drv[drv_index].vendor));
+	memset(h->drv[drv_index].model, 0, sizeof(h->drv[drv_index].model));
+	memset(h->drv[drv_index].rev, 0, sizeof(h->drv[drv_index].rev));
+	disk = h->gendisk[drv_index];
+	cciss_add_disk(h, disk, drv_index);
+}
+
 /* This function will add and remove logical drives from the Logical
  * drive array of the controller and maintain persistency of ordering
  * so that mount points are preserved until the next reboot.  This allows
@@ -1505,12 +1659,11 @@ static int cciss_find_free_drive_index(int ctlr)
  * del_disk	= The disk to remove if specified.  If the value given
  *		  is NULL then no disk is removed.
  */
-static int rebuild_lun_table(ctlr_info_t *h, struct gendisk *del_disk)
+static int rebuild_lun_table(ctlr_info_t *h, int first_time)
 {
 	int ctlr = h->ctlr;
 	int num_luns;
 	ReportLunData_struct *ld_buff = NULL;
-	drive_info_struct *drv = NULL;
 	int return_code;
 	int listlength = 0;
 	int i;
@@ -1519,6 +1672,9 @@ static int rebuild_lun_table(ctlr_info_t *h, struct gendisk *del_disk)
 	__u32 lunid = 0;
 	unsigned long flags;
 
+	if (!capable(CAP_SYS_RAWIO))
+		return -EPERM;
+
 	/* Set busy_configuring flag for this operation */
 	spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags);
 	if (h->busy_configuring) {
@@ -1526,100 +1682,104 @@ static int rebuild_lun_table(ctlr_info_t *h, struct gendisk *del_disk)
 		return -EBUSY;
 	}
 	h->busy_configuring = 1;
-
-	/* if del_disk is NULL then we are being called to add a new disk
-	 * and update the logical drive table.  If it is not NULL then
-	 * we will check if the disk is in use or not.
+ 	spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
+  
+ 	ld_buff = kzalloc(sizeof(ReportLunData_struct), GFP_KERNEL);
+ 	if (ld_buff == NULL)
+ 		goto mem_msg;
+ 
+ 	return_code = sendcmd_withirq(CISS_REPORT_LOG, ctlr, ld_buff,
+ 				      sizeof(ReportLunData_struct), 0,
+ 				      0, 0, TYPE_CMD);
+ 
+ 	if (return_code == IO_OK)
+ 		listlength = be32_to_cpu(*(__be32 *) ld_buff->LUNListLength);
+ 	else {	/* reading number of logical volumes failed */
+ 		printk(KERN_WARNING "cciss: report logical volume"
+ 		       " command failed\n");
+ 		listlength = 0;
+ 		goto freeret;
+ 	}
+  
+ 	num_luns = listlength / 8;	/* 8 bytes per entry */
+ 	if (num_luns > CISS_MAX_LUN) {
+ 		num_luns = CISS_MAX_LUN;
+ 		printk(KERN_WARNING "cciss: more luns configured"
+ 		       " on controller than can be handled by"
+ 		       " this driver.\n");
+ 	}
+
+	if (num_luns == 0)
+		cciss_add_controller_node(h); 
+
+ 	/* Compare controller drive array to driver's drive array
+ 	 * to see if any drives are missing on the controller due
+ 	 * to action of Array Config Utility (user deletes drive)
+ 	 * and deregister logical drives which have disappeared.
 	 */
-	if (del_disk != NULL) {
-		drv = get_drv(del_disk);
-		drv->busy_configuring = 1;
-		spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
-		return_code = deregister_disk(del_disk, drv, 1);
-		drv->busy_configuring = 0;
-		h->busy_configuring = 0;
-		return return_code;
-	} else {
-		spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
-		if (!capable(CAP_SYS_RAWIO))
-			return -EPERM;
+ 	for (i = 0; i <= h->highest_lun; i++) {
+ 		int j;
+ 		drv_found = 0;
 
-		ld_buff = kzalloc(sizeof(ReportLunData_struct), GFP_KERNEL);
-		if (ld_buff == NULL)
-			goto mem_msg;
-
-		return_code = sendcmd_withirq(CISS_REPORT_LOG, ctlr, ld_buff,
-					      sizeof(ReportLunData_struct), 0,
-					      0, 0, TYPE_CMD);
-
-		if (return_code == IO_OK) {
-			listlength =
-				be32_to_cpu(*(__u32 *) ld_buff->LUNListLength);
-		} else {	/* reading number of logical volumes failed */
-			printk(KERN_WARNING "cciss: report logical volume"
-			       " command failed\n");
-			listlength = 0;
-			goto freeret;
-		}
+		/* skip holes in the array from already deleted drives */
+		if (h->drv[i].raid_level == -1)
+			continue;
 
-		num_luns = listlength / 8;	/* 8 bytes per entry */
-		if (num_luns > CISS_MAX_LUN) {
-			num_luns = CISS_MAX_LUN;
-			printk(KERN_WARNING "cciss: more luns configured"
-			       " on controller than can be handled by"
-			       " this driver.\n");
+ 		for (j = 0; j < num_luns; j++) {
+ 			memcpy(&lunid, &ld_buff->LUN[j][0], 4);
+ 			lunid = le32_to_cpu(lunid);
+ 			if (h->drv[i].LunID == lunid) {
+ 				drv_found = 1;
+ 				break;
+ 			}
+ 		}
+ 		if (!drv_found) {
+ 			/* Deregister it from the OS, it's gone. */
+ 			spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags);
+ 			h->drv[i].busy_configuring = 1;
+ 			spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
+ 			return_code = deregister_disk(h, i, 1);
+ 			h->drv[i].busy_configuring = 0;
 		}
+ 	}
+  
+ 	/* Compare controller drive array to driver's drive array.
+ 	 * Check for updates in the drive information and any new drives
+ 	 * on the controller due to ACU adding logical drives, or changing
+ 	 * a logical drive's size, etc.  Reregister any new/changed drives
+ 	 */
+ 	for (i = 0; i < num_luns; i++) {
+ 		int j;
+  
+ 		drv_found = 0;
+  
+ 		memcpy(&lunid, &ld_buff->LUN[i][0], 4);
+ 		lunid = le32_to_cpu(lunid);
+  
+ 		/* Find if the LUN is already in the drive array
+ 		 * of the driver.  If so then update its info
+ 		 * if not in use.  If it does not exist then find
+ 		 * the first free index and add it.
+ 		 */
+ 		for (j = 0; j <= h->highest_lun; j++) {
+ 			if (h->drv[j].raid_level != -1 &&
+ 				h->drv[j].LunID == lunid) {
+ 				drv_index = j;
+ 				drv_found = 1;
+ 				break;
+  			}
+ 		}
+  
+ 		/* check if the drive was found already in the array */
+ 		if (!drv_found) {
+ 			drv_index = cciss_add_gendisk(h, lunid, 0);	
+ 			if (drv_index == -1)
+ 				goto freeret;
+		}
+		cciss_update_drive_info(ctlr, drv_index, first_time);
+ 	}		/* end for */
 
-		/* Compare controller drive array to drivers drive array.
-		 * Check for updates in the drive information and any new drives
-		 * on the controller.
-		 */
-		for (i = 0; i < num_luns; i++) {
-			int j;
-
-			drv_found = 0;
-
-			lunid = (0xff &
-				 (unsigned int)(ld_buff->LUN[i][3])) << 24;
-			lunid |= (0xff &
-				  (unsigned int)(ld_buff->LUN[i][2])) << 16;
-			lunid |= (0xff &
-				  (unsigned int)(ld_buff->LUN[i][1])) << 8;
-			lunid |= 0xff & (unsigned int)(ld_buff->LUN[i][0]);
-
-			/* Find if the LUN is already in the drive array
-			 * of the controller.  If so then update its info
-			 * if not is use.  If it does not exist then find
-			 * the first free index and add it.
-			 */
-			for (j = 0; j <= h->highest_lun; j++) {
-				if (h->drv[j].LunID == lunid) {
-					drv_index = j;
-					drv_found = 1;
-				}
-			}
-
-			/* check if the drive was found already in the array */
-			if (!drv_found) {
-				drv_index = cciss_find_free_drive_index(ctlr);
-				if (drv_index == -1)
-					goto freeret;
-
-				/*Check if the gendisk needs to be allocated */
-				if (!h->gendisk[drv_index]){
-					h->gendisk[drv_index] = alloc_disk(1 << NWD_SHIFT);
-					if (!h->gendisk[drv_index]){
-						printk(KERN_ERR "cciss: could not allocate new disk %d\n", drv_index);
-						goto mem_msg;
-					}
-				}
-			}
-			h->drv[drv_index].LunID = lunid;
-			cciss_update_drive_info(ctlr, drv_index);
-		}		/* end for */
-	}			/* end else */
-
-      freeret:
+freeret:
 	kfree(ld_buff);
 	h->busy_configuring = 0;
 	/* We return -1 here to tell the ACU that we have registered/updated
@@ -1627,8 +1787,9 @@ static int rebuild_lun_table(ctlr_info_t *h, struct gendisk *del_disk)
 	 * additional times.
 	 */
 	return -1;
-      mem_msg:
+mem_msg:
 	printk(KERN_ERR "cciss: out of memory\n");
+	h->busy_configuring = 0;
 	goto freeret;
 }
 
@@ -1647,11 +1808,15 @@ static int rebuild_lun_table(ctlr_info_t *h, struct gendisk *del_disk)
  *             the highest_lun should be left unchanged and the LunID
  *             should not be cleared.
 */
-static int deregister_disk(struct gendisk *disk, drive_info_struct *drv,
+static int deregister_disk(ctlr_info_t *h, int drv_index,
 			   int clear_all)
 {
 	int i;
-	ctlr_info_t *h = get_host(disk);
+	struct gendisk *disk;
+	drive_info_struct *drv;
+
+	disk = h->gendisk[drv_index];
+	drv = &h->drv[drv_index];
 
 	if (!capable(CAP_SYS_RAWIO))
 		return -EPERM;
@@ -1693,7 +1858,7 @@ static int deregister_disk(struct gendisk *disk, drive_info_struct *drv,
 				 * this index is used again later.
 				*/
 				for (i=0; i < CISS_MAX_LUN; i++){
-					if(h->gendisk[i] == disk){
+					if (h->gendisk[i] == disk){
 						h->gendisk[i] = NULL;
 						break;
 					}
@@ -1722,7 +1887,7 @@ static int deregister_disk(struct gendisk *disk, drive_info_struct *drv,
 		if (drv == h->drv + h->highest_lun) {
 			/* if so, find the new hightest lun */
 			int i, newhighest = -1;
-			for (i = 0; i < h->highest_lun; i++) {
+			for (i = 0; i <= h->highest_lun; i++) {
 				/* if the disk has size > 0, it is available */
 				if (h->drv[i].heads)
 					newhighest = i;
@@ -3243,134 +3408,6 @@ static int cciss_pci_init(ctlr_info_t *c, struct pci_dev *pdev)
 	return err;
 }
 
-/*
- * Gets information about the local volumes attached to the controller.
- */
-static void cciss_getgeometry(int cntl_num)
-{
-	ReportLunData_struct *ld_buff;
-	InquiryData_struct *inq_buff;
-	int return_code;
-	int i;
-	int listlength = 0;
-	__u32 lunid = 0;
-	int block_size;
-	sector_t total_size;
-
-	ld_buff = kzalloc(sizeof(ReportLunData_struct), GFP_KERNEL);
-	if (ld_buff == NULL) {
-		printk(KERN_ERR "cciss: out of memory\n");
-		return;
-	}
-	inq_buff = kzalloc(sizeof( InquiryData_struct), GFP_KERNEL);
-	if (inq_buff == NULL) {
-		printk(KERN_ERR "cciss: out of memory\n");
-		kfree(ld_buff);
-		return;
-	}
-	/* Get the firmware version */
-	return_code = sendcmd(CISS_INQUIRY, cntl_num, inq_buff,
-			      sizeof(InquiryData_struct), 0, 0, 0, NULL,
-			      TYPE_CMD);
-	if (return_code == IO_OK) {
-		hba[cntl_num]->firm_ver[0] = inq_buff->data_byte[32];
-		hba[cntl_num]->firm_ver[1] = inq_buff->data_byte[33];
-		hba[cntl_num]->firm_ver[2] = inq_buff->data_byte[34];
-		hba[cntl_num]->firm_ver[3] = inq_buff->data_byte[35];
-	} else {		/* send command failed */
-
-		printk(KERN_WARNING "cciss: unable to determine firmware"
-		       " version of controller\n");
-	}
-	/* Get the number of logical volumes */
-	return_code = sendcmd(CISS_REPORT_LOG, cntl_num, ld_buff,
-			      sizeof(ReportLunData_struct), 0, 0, 0, NULL,
-			      TYPE_CMD);
-
-	if (return_code == IO_OK) {
-#ifdef CCISS_DEBUG
-		printk("LUN Data\n--------------------------\n");
-#endif				/* CCISS_DEBUG */
-
-		listlength |=
-		    (0xff & (unsigned int)(ld_buff->LUNListLength[0])) << 24;
-		listlength |=
-		    (0xff & (unsigned int)(ld_buff->LUNListLength[1])) << 16;
-		listlength |=
-		    (0xff & (unsigned int)(ld_buff->LUNListLength[2])) << 8;
-		listlength |= 0xff & (unsigned int)(ld_buff->LUNListLength[3]);
-	} else {		/* reading number of logical volumes failed */
-
-		printk(KERN_WARNING "cciss: report logical volume"
-		       " command failed\n");
-		listlength = 0;
-	}
-	hba[cntl_num]->num_luns = listlength / 8;	// 8 bytes pre entry
-	if (hba[cntl_num]->num_luns > CISS_MAX_LUN) {
-		printk(KERN_ERR
-		       "ciss:  only %d number of logical volumes supported\n",
-		       CISS_MAX_LUN);
-		hba[cntl_num]->num_luns = CISS_MAX_LUN;
-	}
-#ifdef CCISS_DEBUG
-	printk(KERN_DEBUG "Length = %x %x %x %x = %d\n",
-	       ld_buff->LUNListLength[0], ld_buff->LUNListLength[1],
-	       ld_buff->LUNListLength[2], ld_buff->LUNListLength[3],
-	       hba[cntl_num]->num_luns);
-#endif				/* CCISS_DEBUG */
-
-	hba[cntl_num]->highest_lun = hba[cntl_num]->num_luns - 1;
-	for (i = 0; i < CISS_MAX_LUN; i++) {
-		if (i < hba[cntl_num]->num_luns) {
-			lunid = (0xff & (unsigned int)(ld_buff->LUN[i][3]))
-			    << 24;
-			lunid |= (0xff & (unsigned int)(ld_buff->LUN[i][2]))
-			    << 16;
-			lunid |= (0xff & (unsigned int)(ld_buff->LUN[i][1]))
-			    << 8;
-			lunid |= 0xff & (unsigned int)(ld_buff->LUN[i][0]);
-
-			hba[cntl_num]->drv[i].LunID = lunid;
-
-#ifdef CCISS_DEBUG
-			printk(KERN_DEBUG "LUN[%d]:  %x %x %x %x = %x\n", i,
-			       ld_buff->LUN[i][0], ld_buff->LUN[i][1],
-			       ld_buff->LUN[i][2], ld_buff->LUN[i][3],
-			       hba[cntl_num]->drv[i].LunID);
-#endif				/* CCISS_DEBUG */
-
-		/* testing to see if 16-byte CDBs are already being used */
-		if(hba[cntl_num]->cciss_read == CCISS_READ_16) {
-			cciss_read_capacity_16(cntl_num, i, 0,
-			&total_size, &block_size);
-			goto geo_inq;
-		}
-		cciss_read_capacity(cntl_num, i, 0, &total_size, &block_size);
-
-		/* If read_capacity returns all F's the volume is >2TB */
-		/* so we switch to 16-byte CDBs for all read/write ops */
-		if(total_size == 0xFFFFFFFFULL) {
-			cciss_read_capacity_16(cntl_num, i, 0,
-					       &total_size, &block_size);
-			hba[cntl_num]->cciss_read = CCISS_READ_16;
-			hba[cntl_num]->cciss_write = CCISS_WRITE_16;
-		} else {
-			hba[cntl_num]->cciss_read = CCISS_READ_10;
-			hba[cntl_num]->cciss_write = CCISS_WRITE_10;
-		}
-geo_inq:
-			cciss_geometry_inquiry(cntl_num, i, 0, total_size,
-					       block_size, inq_buff,
-					       &hba[cntl_num]->drv[i]);
-		} else {
-			/* initialize raid_level to indicate a free space */
-			hba[cntl_num]->drv[i].raid_level = -1;
-		}
-	}
-	kfree(ld_buff);
-	kfree(inq_buff);
-}
-
 /* Function to find the first free pointer into our hba[] array */
 /* Returns -1 if no free entries are left.  */
 static int alloc_cciss_hba(void)
@@ -3383,9 +3420,6 @@ static int alloc_cciss_hba(void)
 			p = kzalloc(sizeof(ctlr_info_t), GFP_KERNEL);
 			if (!p)
 				goto Enomem;
-			p->gendisk[0] = alloc_disk(1 << NWD_SHIFT);
-			if (!p->gendisk[0])
-				goto Enomem;
 			hba[i] = p;
 			return i;
 		}
@@ -3619,7 +3653,8 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
 	int i;
 	int j = 0;
 	int rc;
-	int dac;
+	int dac, return_code;
+	InquiryData_struct *inq_buff = NULL;
 
 	if (reset_devices) {
 		/* Reset the controller with a PCI power-cycle */
@@ -3730,93 +3765,45 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
 	       ((hba[i]->nr_cmds + BITS_PER_LONG -
 		 1) / BITS_PER_LONG) * sizeof(unsigned long));
 
-#ifdef CCISS_DEBUG
-	printk(KERN_DEBUG "Scanning for drives on controller cciss%d\n", i);
-#endif				/* CCISS_DEBUG */
-
-	cciss_getgeometry(i);
+	hba[i]->num_luns = 0;
+	hba[i]->highest_lun = -1;
+	for (j = 0; j < CISS_MAX_LUN; j++) {
+		hba[i]->drv[j].raid_level = -1;
+		hba[i]->drv[j].queue = NULL;
+		hba[i]->gendisk[j] = NULL;
+	}
 
 	cciss_scsi_setup(i);
 
 	/* Turn the interrupts on so we can service requests */
 	hba[i]->access.set_intr_mask(hba[i], CCISS_INTR_ON);
 
-	cciss_procinit(i);
+	/* Get the firmware version */ 
+	inq_buff = kzalloc(sizeof(InquiryData_struct), GFP_KERNEL);
+        if (inq_buff == NULL) {
+                printk(KERN_ERR "cciss: out of memory\n");
+                return ENOMEM;
+        }
 
-	hba[i]->cciss_sector_size = 2048;
+	return_code = sendcmd_withirq(CISS_INQUIRY, i, inq_buff, 
+		sizeof(InquiryData_struct), 0, 0 ,0, TYPE_CMD);
+	if (return_code == IO_OK) {
+		hba[i]->firm_ver[0] = inq_buff->data_byte[32];
+		hba[i]->firm_ver[1] = inq_buff->data_byte[33];
+		hba[i]->firm_ver[2] = inq_buff->data_byte[34];
+		hba[i]->firm_ver[3] = inq_buff->data_byte[35];
+	} else {	 /* send command failed */
+		printk(KERN_WARNING "cciss: unable to determine firmware"
+			" version of controller\n");
+	}
 
+	cciss_procinit(i);
+	hba[i]->cciss_sector_size = 2048;
 	hba[i]->busy_initializing = 0;
-
-	do {
-		drive_info_struct *drv = &(hba[i]->drv[j]);
-		struct gendisk *disk = hba[i]->gendisk[j];
-		request_queue_t *q;
-
-		/* Check if the disk was allocated already */
-		if (!disk){
-			hba[i]->gendisk[j] = alloc_disk(1 << NWD_SHIFT);
-			disk = hba[i]->gendisk[j];
-		}
-
-		/* Check that the disk was able to be allocated */
-		if (!disk) {
-			printk(KERN_ERR "cciss: unable to allocate memory for disk %d\n", j);
-			goto clean4;
-		}
-
-		q = blk_init_queue(do_cciss_request, &hba[i]->lock);
-		if (!q) {
-			printk(KERN_ERR
-			       "cciss:  unable to allocate queue for disk %d\n",
-			       j);
-			goto clean4;
-		}
-		drv->queue = q;
-
-		blk_queue_bounce_limit(q, hba[i]->pdev->dma_mask);
-
-		/* This is a hardware imposed limit. */
-		blk_queue_max_hw_segments(q, MAXSGENTRIES);
-
-		/* This is a limit in the driver and could be eliminated. */
-		blk_queue_max_phys_segments(q, MAXSGENTRIES);
-
-		blk_queue_max_sectors(q, hba[i]->cciss_sector_size);
-
-		blk_queue_softirq_done(q, cciss_softirq_done);
-
-		q->queuedata = hba[i];
-		sprintf(disk->disk_name, "cciss/c%dd%d", i, j);
-		disk->major = hba[i]->major;
-		disk->first_minor = j << NWD_SHIFT;
-		disk->fops = &cciss_fops;
-		disk->queue = q;
-		disk->private_data = drv;
-		disk->driverfs_dev = &pdev->dev;
-		/* we must register the controller even if no disks exist */
-		/* this is for the online array utilities */
-		if (!drv->heads && j)
-			continue;
-		blk_queue_hardsect_size(q, drv->block_size);
-		set_capacity(disk, drv->nr_blocks);
-		j++;
-	} while (j <= hba[i]->highest_lun);
-
-	/* Make sure all queue data is written out before */
-	/* interrupt handler, triggered by add_disk,  */
-	/* is allowed to start them. */
-	wmb();
-
-	for (j = 0; j <= hba[i]->highest_lun; j++)
-		add_disk(hba[i]->gendisk[j]);
-
-	/* we must register the controller even if no disks exist */
-	if (hba[i]->highest_lun == -1)
-		add_disk(hba[i]->gendisk[0]);
-
+	rebuild_lun_table(hba[i], 1);
 	return 1;
 
-      clean4:
+clean4:
 #ifdef CONFIG_CISS_SCSI_TAPE
 	kfree(hba[i]->scsi_rejects.complete);
 #endif
@@ -3842,7 +3829,11 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
 			blk_cleanup_queue(drv->queue);
 	}
 	pci_release_regions(pdev);
-	pci_disable_device(pdev);
+	/* This call to pci_disable_device causes the driver to be unable
+	 * to load/unload multiple times.  No reason why yet, but we are
+	 * leaving it out for now.
+	 */
+	/* pci_disable_device(pdev); */
 	pci_set_drvdata(pdev, NULL);
 	free_hba(i);
 	return -1;
diff --git a/drivers/block/cciss.h b/drivers/block/cciss.h
index 651a9e5..4f07c7d 100644
--- a/drivers/block/cciss.h
+++ b/drivers/block/cciss.h
@@ -32,13 +32,21 @@ typedef struct _drive_info_struct
 	int 	heads;
 	int	sectors;
 	int 	cylinders;
-	int	raid_level; /* set to -1 to indicate that
-			     * the drive is not in use/configured
-			    */
-	int	busy_configuring; /*This is set when the drive is being removed
-				   *to prevent it from being opened or it's queue
-				   *from being started.
-				  */
+	int	raid_level;	/* set to -1 to indicate that
+			 	 * the drive is not in use/configured
+				 */
+	int	busy_configuring;	/* This is set when the drive is being
+				    	 * removed to prevent it from being
+					 * opened or it's queue from being
+					 * started.
+					 */
+	char	vendor[9];
+	char	model[17];
+	char	rev[5];
+	BYTE	uid[16];	/* from inquiry page 0x83
+				 * not neccesarily NULL terminated
+				 */
+	struct device *dev_info;
 } drive_info_struct;
 
 #ifdef CONFIG_CISS_SCSI_TAPE