Sophie

Sophie

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

kernel-2.6.18-194.11.1.el5.src.rpm

From: Tomas Henzl <thenzl@redhat.com>
Subject: Re: [PATCH RHEL-5] RHEL 5.1 cciss driver updates - REPOST
Date: Fri, 22 Jun 2007 15:45:55 +0200
Bugzilla: 222852
Message-Id: <467BD293.4000105@redhat.com>
Changelog: [scsi] cciss driver updates

https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=222852

These patches are from Mike Miller <mike.miller@hp.com> and they are in
the upstream.

The patch was tested by me on a x86-64 16TB machine and on a smaller one
-i686.
For testing I used the programs verify-data and iozone.

Tomas

Patch 1 adds the support to fire up on any HP RAID class device that
has a valid cciss signature.

Patch 3 changes our open to test for drv->heads like we do in other
places in the driver. Mostly for consistency.

Patch 4 removes the no longer used revalidate_allvol function. It was
replaced by rebuild_lun_table.

Patch 5 adds the support for a large number of logical volumes. We will
have hardware that is supposed to support up 1024 lv's. Testing shows we
may not be able to register that many, however. This patch sets max
number of luns to 256.

Patch 6 adds support for logical volumes larger than 2TB in size. If
read_capacitry returns all F's we switch to 16-byte CDBs for all
read/write ops. I don't see the value in trying to determine if the LBA
is past the 2TB 10-byte limit.

Patch 9 is a version change.

diff -puN drivers/block/cciss.c~cciss_ref_driver_for_lx2619-rc2 drivers/block/cciss.c
--- linux-2.6.18.x86_64/drivers/block/cciss.c~cciss_ref_driver_for_lx2619-rc2	2007-03-30 16:08:32.000000000 -0500
+++ linux-2.6.18.x86_64-root/drivers/block/cciss.c	2007-03-30 16:08:32.000000000 -0500
@@ -83,6 +83,8 @@ static const struct pci_device_id cciss_
 	{PCI_VENDOR_ID_HP,     PCI_DEVICE_ID_HP_CISSD,     0x103C, 0x3214},
 	{PCI_VENDOR_ID_HP,     PCI_DEVICE_ID_HP_CISSD,     0x103C, 0x3215},
 	{PCI_VENDOR_ID_HP,     PCI_DEVICE_ID_HP_CISSC,     0x103C, 0x3237},
+	{PCI_VENDOR_ID_HP,     PCI_ANY_ID,      PCI_ANY_ID, PCI_ANY_ID,
+		PCI_CLASS_STORAGE_RAID << 8, 0xffff << 8, 0},
 	{0,}
 };
 
@@ -113,6 +115,7 @@ static struct board_type products[] = {
 	{0x3214103C, "Smart Array E200i", &SA5_access, 120},
 	{0x3215103C, "Smart Array E200i", &SA5_access, 120},
 	{0x3237103C, "Smart Array E500", &SA5_access, 512},
+	{0xFFFF103C, "Unknown Smart Array", &SA5_access},
 };
 
 /* How long to wait (in milliseconds) for board to go into simple mode */
@@ -2882,13 +2885,6 @@ static int cciss_pci_init(ctlr_info_t *c
 			break;
 		}
 	}
-	if (i == ARRAY_SIZE(products)) {
-		printk(KERN_WARNING "cciss: Sorry, I don't know how"
-		       " to access the Smart Array controller %08lx\n",
-		       (unsigned long)board_id);
-		err = -ENODEV;
-		goto err_out_free_res;
-	}
 	if ((readb(&c->cfgtable->Signature[0]) != 'C') ||
 	    (readb(&c->cfgtable->Signature[1]) != 'I') ||
 	    (readb(&c->cfgtable->Signature[2]) != 'S') ||
@@ -2897,6 +2893,26 @@ static int cciss_pci_init(ctlr_info_t *c
 		err = -ENODEV;
 		goto err_out_free_res;
 	}
+	/* We didn't find the controller in our list. We know the
+	 * signature is valid. If it's an HP device let's try to
+	 * bind to the device and fire it up. Otherwise we bail.
+	 */
+	if (i == ARRAY_SIZE(products)) {
+		if (subsystem_vendor_id == PCI_VENDOR_ID_HP) {
+			c->product_name = products[i-1].product_name;
+			c->access = *(products[i-1].access);
+			printk(KERN_WARNING "cciss: This is an unknown "
+				"Smart Array controller.\n"
+				"cciss: Please update to the latest driver "
+				"available from www.hp.com.\n");
+		} else {
+			printk(KERN_WARNING "cciss: Sorry, I don't know how"
+				" to access the Smart Array controller %08lx\n"
+					, (unsigned long)board_id);
+			err = -ENODEV;
+			goto err_out_free_res;
+		}
+	}
 #ifdef CONFIG_X86
 	{
 		/* Need to enable prefetch in the SCSI core for 6400 in x86 */
_

diff -puN drivers/block/cciss.c~cciss_open_fix_for_lx2619-rc4 drivers/block/cciss.c
--- linux-2.6.18.x86_64/drivers/block/cciss.c~cciss_open_fix_for_lx2619-rc4	2007-03-30 16:09:29.000000000 -0500
+++ linux-2.6.18.x86_64-root/drivers/block/cciss.c	2007-03-30 16:09:29.000000000 -0500
@@ -494,7 +494,7 @@ static int cciss_open(struct inode *inod
 	 * but I'm already using way to many device nodes to claim another one
 	 * for "raw controller".
 	 */
-	if (drv->nr_blocks == 0) {
+	if (drv->heads == 0) {
 		if (iminor(inode) != 0) {	/* not node 0? */
 			/* if not node 0 make sure it is a partition = 0 */
 			if (iminor(inode) & 0x0f) {
_

diff -puN drivers/block/cciss.c~cciss_rm_revalidate_allvol_for_lx2619-rc4 drivers/block/cciss.c
--- linux-2.6.18.x86_64/drivers/block/cciss.c~cciss_rm_revalidate_allvol_for_lx2619-rc4	2007-03-30 16:09:39.000000000 -0500
+++ linux-2.6.18.x86_64-root/drivers/block/cciss.c	2007-03-30 16:09:39.000000000 -0500
@@ -141,7 +141,6 @@ static int cciss_ioctl(struct inode *ino
 		       unsigned int cmd, unsigned long arg);
 static int cciss_getgeo(struct block_device *bdev, struct hd_geometry *geo);
 
-static int revalidate_allvol(ctlr_info_t *host);
 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,
@@ -857,9 +856,7 @@ static int cciss_ioctl(struct inode *ino
 		}
 
 	case CCISS_REVALIDVOLS:
-		if (bdev != bdev->bd_contains || drv != host->drv)
-			return -ENXIO;
-		return revalidate_allvol(host);
+		return rebuild_lun_table(host, NULL);
 
 	case CCISS_GETLUNINFO:{
 			LogvolInfo_struct luninfo;
@@ -1159,75 +1156,6 @@ static int cciss_ioctl(struct inode *ino
 	}
 }
 
-/*
- * revalidate_allvol is for online array config utilities.  After a
- * utility reconfigures the drives in the array, it can use this function
- * (through an ioctl) to make the driver zap any previous disk structs for
- * that controller and get new ones.
- *
- * Right now I'm using the getgeometry() function to do this, but this
- * function should probably be finer grained and allow you to revalidate one
- * particular logical volume (instead of all of them on a particular
- * controller).
- */
-static int revalidate_allvol(ctlr_info_t *host)
-{
-	int ctlr = host->ctlr, i;
-	unsigned long flags;
-
-	spin_lock_irqsave(CCISS_LOCK(ctlr), flags);
-	if (host->usage_count > 1) {
-		spin_unlock_irqrestore(CCISS_LOCK(ctlr), flags);
-		printk(KERN_WARNING "cciss: Device busy for volume"
-		       " revalidation (usage=%d)\n", host->usage_count);
-		return -EBUSY;
-	}
-	host->usage_count++;
-	spin_unlock_irqrestore(CCISS_LOCK(ctlr), flags);
-
-	for (i = 0; i < NWD; i++) {
-		struct gendisk *disk = host->gendisk[i];
-		if (disk) {
-			request_queue_t *q = disk->queue;
-
-			if (disk->flags & GENHD_FL_UP)
-				del_gendisk(disk);
-			if (q)
-				blk_cleanup_queue(q);
-		}
-	}
-
-	/*
-	 * Set the partition and block size structures for all volumes
-	 * on this controller to zero.  We will reread all of this data
-	 */
-	memset(host->drv, 0, sizeof(drive_info_struct)
-	       * CISS_MAX_LUN);
-	/*
-	 * Tell the array controller not to give us any interrupts while
-	 * we check the new geometry.  Then turn interrupts back on when
-	 * we're done.
-	 */
-	host->access.set_intr_mask(host, CCISS_INTR_OFF);
-	cciss_getgeometry(ctlr);
-	host->access.set_intr_mask(host, CCISS_INTR_ON);
-
-	/* Loop through each real device */
-	for (i = 0; i < NWD; i++) {
-		struct gendisk *disk = host->gendisk[i];
-		drive_info_struct *drv = &(host->drv[i]);
-		/* we must register the controller even if no disks exist */
-		/* this is for the online array utilities */
-		if (!drv->heads && i)
-			continue;
-		blk_queue_hardsect_size(drv->queue, drv->block_size);
-		set_capacity(disk, drv->nr_blocks);
-		add_disk(disk);
-	}
-	host->usage_count--;
-	return 0;
-}
-
 static inline void complete_buffers(struct bio *bio, int status)
 {
 	while (bio) {
_

diff -puN drivers/block/cciss.c~cciss_gt_16_ld_for_lx2619-rc4 drivers/block/cciss.c
--- linux-2.6.18.x86_64/drivers/block/cciss.c~cciss_gt_16_ld_for_lx2619-rc4	2007-03-30 16:10:02.000000000 -0500
+++ linux-2.6.18.x86_64-root/drivers/block/cciss.c	2007-03-30 16:10:02.000000000 -0500
@@ -1310,6 +1310,11 @@ static void cciss_update_drive_info(int 
 	/* 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];
 
 		/* Set up queue information */
 		disk->queue->backing_dev_info.ra_pages = READ_AHEAD;
@@ -1389,11 +1394,6 @@ static int rebuild_lun_table(ctlr_info_t
 
 	/* Set busy_configuring flag for this operation */
 	spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags);
-	if (h->num_luns >= CISS_MAX_LUN) {
-		spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
-		return -EINVAL;
-	}
-
 	if (h->busy_configuring) {
 		spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
 		return -EBUSY;
@@ -1426,17 +1426,8 @@ static int rebuild_lun_table(ctlr_info_t
 					      0, 0, TYPE_CMD);
 
 		if (return_code == IO_OK) {
-			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]);
+			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");
@@ -1487,6 +1478,14 @@ static int rebuild_lun_table(ctlr_info_t
 				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);
@@ -1524,6 +1523,7 @@ static int rebuild_lun_table(ctlr_info_t
 static int deregister_disk(struct gendisk *disk, drive_info_struct *drv,
 			   int clear_all)
 {
+	int i;
 	ctlr_info_t *h = get_host(disk);
 
 	if (!capable(CAP_SYS_RAWIO))
@@ -1547,9 +1547,35 @@ static int deregister_disk(struct gendis
 				del_gendisk(disk);
 			if (q) {
 				blk_cleanup_queue(q);
+				/* Set drv->queue to NULL so that we do not try
+				 * to call blk_start_queue on this queue in the
+				 * interrupt handler
+				 */
 				drv->queue = NULL;
 			}
+			/* If clear_all is set then we are deleting the logical
+			 * drive, not just refreshing its info.  For drives
+			 * other than disk 0 we will call put_disk.  We do not
+			 * do this for disk 0 as we need it to be able to
+			 * configure the controller.
+			*/
+			if (clear_all){
+				/* This isn't pretty, but we need to find the
+				 * disk in our array and NULL our the pointer.
+				 * This is so that we will call alloc_disk if
+				 * this index is used again later.
+				*/
+				for (i=0; i < CISS_MAX_LUN; i++){
+					if(h->gendisk[i] == disk){
+						h->gendisk[i] = NULL;
+						break;
+					}
+				}
+				put_disk(disk);
+			}
 		}
+	} else {
+		set_capacity(disk, 0);
 	}
 
 	--h->num_luns;
@@ -3032,13 +3058,7 @@ static void cciss_getgeometry(int cntl_n
 /* Returns -1 if no free entries are left.  */
 static int alloc_cciss_hba(void)
 {
-	struct gendisk *disk[NWD];
-	int i, n;
-	for (n = 0; n < NWD; n++) {
-		disk[n] = alloc_disk(1 << NWD_SHIFT);
-		if (!disk[n])
-			goto out;
-	}
+	int i;
 
 	for (i = 0; i < MAX_CTLR; i++) {
 		if (!hba[i]) {
@@ -3046,20 +3066,18 @@ static int alloc_cciss_hba(void)
 			p = kzalloc(sizeof(ctlr_info_t), GFP_KERNEL);
 			if (!p)
 				goto Enomem;
-			for (n = 0; n < NWD; n++)
-				p->gendisk[n] = disk[n];
+			p->gendisk[0] = alloc_disk(1 << NWD_SHIFT);
+			if (!p->gendisk[0])
+				goto Enomem;
 			hba[i] = p;
 			return i;
 		}
 	}
 	printk(KERN_WARNING "cciss: This driver supports a maximum"
 	       " of %d controllers.\n", MAX_CTLR);
-	goto out;
-      Enomem:
+	return -1;
+Enomem:
 	printk(KERN_ERR "cciss: out of memory.\n");
-      out:
-	while (n--)
-		put_disk(disk[n]);
 	return -1;
 }
 
@@ -3069,7 +3087,7 @@ static void free_hba(int i)
 	int n;
 
 	hba[i] = NULL;
-	for (n = 0; n < NWD; n++)
+	for (n = 0; n < CISS_MAX_LUN; n++)
 		put_disk(p->gendisk[n]);
 	kfree(p);
 }
@@ -3082,9 +3100,8 @@ static void free_hba(int i)
 static int __devinit cciss_init_one(struct pci_dev *pdev,
 				    const struct pci_device_id *ent)
 {
-	request_queue_t *q;
 	int i;
-	int j;
+	int j = 0;
 	int rc;
 	int dac;
 
@@ -3196,16 +3213,29 @@ static int __devinit cciss_init_one(stru
 
 	hba[i]->busy_initializing = 0;
 
-	for (j = 0; j < NWD; j++) {	/* mfm */
+	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);
-			break;
+			goto clean4;
 		}
 		drv->queue = q;
 
@@ -3237,7 +3267,8 @@ static int __devinit cciss_init_one(stru
 		blk_queue_hardsect_size(q, drv->block_size);
 		set_capacity(disk, drv->nr_blocks);
 		add_disk(disk);
-	}
+		j++;
+	} while (j <= hba[i]->highest_lun);
 
 	return 1;
 
@@ -3260,6 +3291,15 @@ static int __devinit cciss_init_one(stru
 	unregister_blkdev(hba[i]->major, hba[i]->devname);
       clean1:
 	hba[i]->busy_initializing = 0;
+	/* cleanup any queues that may have been initialized */
+	for (j=0; j <= hba[i]->highest_lun; j++){
+		drive_info_struct *drv = &(hba[i]->drv[j]);
+		if (drv->queue)
+			blk_cleanup_queue(drv->queue);
+	}
+	pci_release_regions(pdev);
+	pci_disable_device(pdev);
+	pci_set_drvdata(pdev, NULL);
 	free_hba(i);
 	return -1;
 }
@@ -3307,7 +3347,7 @@ static void __devexit cciss_remove_one(s
 	remove_proc_entry(hba[i]->devname, proc_cciss);
 
 	/* remove it from the disk list */
-	for (j = 0; j < NWD; j++) {
+	for (j = 0; j < CISS_MAX_LUN; j++) {
 		struct gendisk *disk = hba[i]->gendisk[j];
 		if (disk) {
 			request_queue_t *q = disk->queue;
diff -puN drivers/block/cciss_cmd.h~cciss_gt_16_ld_for_lx2619-rc4 drivers/block/cciss_cmd.h
--- linux-2.6.18.x86_64/drivers/block/cciss_cmd.h~cciss_gt_16_ld_for_lx2619-rc4	2007-03-30 16:10:02.000000000 -0500
+++ linux-2.6.18.x86_64-root/drivers/block/cciss_cmd.h	2007-03-30 16:10:26.000000000 -0500
@@ -89,7 +89,7 @@ typedef union _u64bit
 //###########################################################################
 //STRUCTURES
 //###########################################################################
-#define CISS_MAX_LUN	16	
+#define CISS_MAX_LUN		512
 #define CISS_MAX_PHYS_LUN	1024
 // SCSI-3 Cmmands 
 
diff -puN drivers/block/cciss.h~cciss_gt_16_ld_for_lx2619-rc4 drivers/block/cciss.h
--- linux-2.6.18.x86_64/drivers/block/cciss.h~cciss_gt_16_ld_for_lx2619-rc4	2007-03-30 16:10:02.000000000 -0500
+++ linux-2.6.18.x86_64-root/drivers/block/cciss.h	2007-03-30 16:10:02.000000000 -0500
@@ -6,7 +6,6 @@
 #include "cciss_cmd.h"
 
 
-#define NWD		16
 #define NWD_SHIFT	4
 #define MAX_PART	(1 << NWD_SHIFT)
 
@@ -109,7 +108,7 @@ struct ctlr_info 
 	int			next_to_run;
 
 	// Disk structures we need to pass back
-	struct gendisk   *gendisk[NWD];
+	struct gendisk   *gendisk[CISS_MAX_LUN];
 #ifdef CONFIG_CISS_SCSI_TAPE
 	void *scsi_ctlr; /* ptr to structure containing scsi related stuff */
 	/* list of block side commands the scsi error handling sucked up */
diff -puN include/linux/cciss_ioctl.h~cciss_gt_16_ld_for_lx2619-rc4 include/linux/cciss_ioctl.h
--- linux-2.6.18.x86_64/include/linux/cciss_ioctl.h~cciss_gt_16_ld_for_lx2619-rc4	2007-03-30 16:10:02.000000000 -0500
+++ linux-2.6.18.x86_64-root/include/linux/cciss_ioctl.h	2007-03-30 16:10:02.000000000 -0500
@@ -80,7 +80,7 @@ typedef __u32 DriverVer_type;
 #define HWORD __u16
 #define DWORD __u32
 
-#define CISS_MAX_LUN	16	
+#define CISS_MAX_LUN	512
 
 #define LEVEL2LUN   1   // index into Target(x) structure, due to byte swapping
 #define LEVEL3LUN   0
_

diff -puN drivers/block/cciss.c~cciss_2tb_support drivers/block/cciss.c
--- linux-2.6.18.x86_64/drivers/block/cciss.c~cciss_2tb_support	2007-03-30 16:10:42.000000000 -0500
+++ linux-2.6.18.x86_64-root/drivers/block/cciss.c	2007-03-30 16:27:01.000000000 -0500
@@ -145,15 +145,14 @@ static int cciss_revalidate(struct gendi
 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 void cciss_read_capacity(int ctlr, int logvol, ReadCapdata_struct *buf,
-				int withirq, unsigned int *total_size,
-				unsigned int *block_size);
+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,
+			sector_t *total_size, unsigned int *block_size);
 static void cciss_geometry_inquiry(int ctlr, int logvol, int withirq,
-				   unsigned int total_size,
-				   unsigned int block_size,
-				   InquiryData_struct *inq_buff,
-				   drive_info_struct *drv);
+			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);
@@ -1269,10 +1268,9 @@ static void cciss_update_drive_info(int 
 {
 	ctlr_info_t *h = hba[ctlr];
 	struct gendisk *disk;
-	ReadCapdata_struct *size_buff = NULL;
 	InquiryData_struct *inq_buff = NULL;
 	unsigned int block_size;
-	unsigned int total_size;
+	sector_t total_size;
 	unsigned long flags = 0;
 	int ret = 0;
 
@@ -1291,17 +1289,23 @@ static void cciss_update_drive_info(int 
 		return;
 
 	/* Get information about the disk and modify the driver structure */
-	size_buff = kmalloc(sizeof(ReadCapdata_struct), GFP_KERNEL);
-	if (size_buff == NULL)
-		goto mem_msg;
 	inq_buff = kmalloc(sizeof(InquiryData_struct), GFP_KERNEL);
 	if (inq_buff == NULL)
 		goto mem_msg;
 
-	cciss_read_capacity(ctlr, drv_index, size_buff, 1,
-			    &total_size, &block_size);
-	cciss_geometry_inquiry(ctlr, drv_index, 1, total_size, block_size,
-			       inq_buff, &h->drv[drv_index]);
+	cciss_read_capacity(ctlr, drv_index, 1, &total_size, &block_size);
+
+	/* If read_capacity returns all F's the logical is >2TB
+	 * 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;
+	}
 
 	++h->num_luns;
 	disk = h->gendisk[drv_index];
@@ -1340,7 +1344,6 @@ static void cciss_update_drive_info(int 
 	}
 
       freeret:
-	kfree(size_buff);
 	kfree(inq_buff);
 	return;
       mem_msg:
@@ -1686,6 +1689,22 @@ static int fill_cmd(CommandList_struct *
 			c->Request.Timeout = 0;
 			c->Request.CDB[0] = cmd;
 			break;
+		case CCISS_READ_CAPACITY_16:
+			c->Header.LUN.LogDev.VolId = h->drv[log_unit].LunID;
+			c->Header.LUN.LogDev.Mode = 1;
+			c->Request.CDBLen = 16;
+			c->Request.Type.Attribute = ATTR_SIMPLE;
+			c->Request.Type.Direction = XFER_READ;
+			c->Request.Timeout = 0;
+			c->Request.CDB[0] = cmd;
+			c->Request.CDB[1] = 0x10;
+			c->Request.CDB[10] = (size >> 24) & 0xFF;
+			c->Request.CDB[11] = (size >> 16) & 0xFF;
+			c->Request.CDB[12] = (size >> 8) & 0xFF;
+			c->Request.CDB[13] = size & 0xFF;
+			c->Request.Timeout = 0;
+			c->Request.CDB[0] = cmd;
+			break;
 		case CCISS_CACHE_FLUSH:
 			c->Request.CDBLen = 12;
 			c->Request.Type.Attribute = ATTR_SIMPLE;
@@ -1719,6 +1738,7 @@ static int fill_cmd(CommandList_struct *
 			memset(&c->Request.CDB[0], 0, sizeof(c->Request.CDB));
 			c->Request.CDB[0] = cmd;	/* reset */
 			c->Request.CDB[1] = 0x04;	/* reset a LUN */
+			break;
 		case 3:	/* No-Op message */
 			c->Request.CDBLen = 1;
 			c->Request.Type.Attribute = ATTR_SIMPLE;
@@ -1862,12 +1882,14 @@ static int sendcmd_withirq(__u8 cmd,
 }
 
 static void cciss_geometry_inquiry(int ctlr, int logvol,
-				   int withirq, unsigned int total_size,
+				   int withirq, sector_t total_size,
 				   unsigned int block_size,
 				   InquiryData_struct *inq_buff,
 				   drive_info_struct *drv)
 {
 	int return_code;
+	unsigned long t, rem;
+
 	memset(inq_buff, 0, sizeof(InquiryData_struct));
 	if (withirq)
 		return_code = sendcmd_withirq(CISS_INQUIRY, ctlr,
@@ -1882,25 +1904,25 @@ static void cciss_geometry_inquiry(int c
 			printk(KERN_WARNING
 			       "cciss: reading geometry failed, volume "
 			       "does not support reading geometry\n");
-			drv->block_size = block_size;
-			drv->nr_blocks = total_size;
 			drv->heads = 255;
-			drv->sectors = 32;	// Sectors per track
-			drv->cylinders = total_size / 255 / 32;
+			drv->sectors = 32;	/* Sectors per track */
+			drv->raid_level = RAID_UNKNOWN;
 		} else {
-			unsigned int t;
-
-			drv->block_size = block_size;
-			drv->nr_blocks = total_size;
 			drv->heads = inq_buff->data_byte[6];
 			drv->sectors = inq_buff->data_byte[7];
 			drv->cylinders = (inq_buff->data_byte[4] & 0xff) << 8;
 			drv->cylinders += inq_buff->data_byte[5];
 			drv->raid_level = inq_buff->data_byte[8];
-			t = drv->heads * drv->sectors;
-			if (t > 1) {
-				drv->cylinders = total_size / t;
-			}
+		}
+		drv->block_size = block_size;
+		drv->nr_blocks = total_size + 1;
+		t = drv->heads * drv->sectors;
+		if(t > 1) {
+			sector_t real_size = total_size + 1;
+			rem = sector_div(total_size, t);
+			if(rem)
+				real_size++;
+			drv->cylinders = real_size;
 		}
 	} else {		/* Get geometry failed */
 		printk(KERN_WARNING "cciss: reading geometry failed\n");
@@ -1910,31 +1932,73 @@ static void cciss_geometry_inquiry(int c
 }
 
 static void
-cciss_read_capacity(int ctlr, int logvol, ReadCapdata_struct *buf,
-		    int withirq, unsigned int *total_size,
-		    unsigned int *block_size)
+cciss_read_capacity(int ctlr, int logvol, int withirq, sector_t *total_size,
+				unsigned int *block_size)
 {
+	ReadCapdata_struct *buf;
 	int return_code;
-	memset(buf, 0, sizeof(*buf));
+	buf = kmalloc(sizeof(ReadCapdata_struct), GFP_KERNEL);
+	if (buf == NULL) {
+		printk(KERN_WARNING "cciss: out of memory\n");
+		return;
+	}
+	memset(buf, 0, sizeof(ReadCapdata_struct));
+
 	if (withirq)
 		return_code = sendcmd_withirq(CCISS_READ_CAPACITY,
-					      ctlr, buf, sizeof(*buf), 1,
-					      logvol, 0, TYPE_CMD);
+			ctlr, buf, sizeof(ReadCapdata_struct),
+				1, logvol, 0, TYPE_CMD);
 	else
 		return_code = sendcmd(CCISS_READ_CAPACITY,
-				      ctlr, buf, sizeof(*buf), 1, logvol, 0,
-				      NULL, TYPE_CMD);
+			ctlr, buf, sizeof(ReadCapdata_struct),
+				1, logvol, 0, NULL, TYPE_CMD);
+	if (return_code == IO_OK) {
+		*total_size = be32_to_cpu(*(__u32 *) buf->total_size);
+		*block_size = be32_to_cpu(*(__u32 *) buf->block_size);
+	} else { /* read capacity command failed */
+		printk(KERN_WARNING "cciss: read capacity failed\n");
+		*total_size = 0;
+		*block_size = BLOCK_SIZE;
+	}
+	if (*total_size != (__u32) 0)
+		printk(KERN_INFO "      blocks= %lld block_size= %d\n",
+						*total_size+1, *block_size);
+	kfree(buf);
+	return;
+}
+
+static void
+cciss_read_capacity_16(int ctlr, int logvol, int withirq, sector_t *total_size, 				unsigned int *block_size)
+{
+	ReadCapdata_struct_16 *buf;
+	int return_code;
+	buf = kmalloc(sizeof(ReadCapdata_struct_16), GFP_KERNEL);
+	if (buf == NULL) {
+		printk(KERN_WARNING "cciss: out of memory\n");
+		return;
+	}
+	memset(buf, 0, sizeof(ReadCapdata_struct_16));
+	if (withirq) {
+		return_code = sendcmd_withirq(CCISS_READ_CAPACITY_16,
+			ctlr, buf, sizeof(ReadCapdata_struct_16),
+				1, logvol, 0, TYPE_CMD);
+	}
+	else {
+		return_code = sendcmd(CCISS_READ_CAPACITY_16,
+			ctlr, buf, sizeof(ReadCapdata_struct_16),
+				1, logvol, 0, NULL, TYPE_CMD);
+	}
 	if (return_code == IO_OK) {
-		*total_size =
-		    be32_to_cpu(*((__be32 *) & buf->total_size[0])) + 1;
-		*block_size = be32_to_cpu(*((__be32 *) & buf->block_size[0]));
-	} else {		/* read capacity command failed */
+		*total_size = be64_to_cpu(*(__u64 *) buf->total_size);
+		*block_size = be32_to_cpu(*(__u32 *) buf->block_size);
+	} else { /* read capacity command failed */
 		printk(KERN_WARNING "cciss: read capacity failed\n");
 		*total_size = 0;
 		*block_size = BLOCK_SIZE;
 	}
-	printk(KERN_INFO "      blocks= %u block_size= %d\n",
-	       *total_size, *block_size);
+	printk(KERN_INFO "      blocks= %lld block_size= %d\n",
+		*total_size+1, *block_size);
+	kfree(buf);
 	return;
 }
 
@@ -1945,8 +2009,7 @@ static int cciss_revalidate(struct gendi
 	int logvol;
 	int FOUND = 0;
 	unsigned int block_size;
-	unsigned int total_size;
-	ReadCapdata_struct *size_buff = NULL;
+	sector_t total_size;
 	InquiryData_struct *inq_buff = NULL;
 
 	for (logvol = 0; logvol < CISS_MAX_LUN; logvol++) {
@@ -1959,27 +2022,22 @@ static int cciss_revalidate(struct gendi
 	if (!FOUND)
 		return 1;
 
-	size_buff = kmalloc(sizeof(ReadCapdata_struct), GFP_KERNEL);
-	if (size_buff == NULL) {
-		printk(KERN_WARNING "cciss: out of memory\n");
-		return 1;
-	}
-	inq_buff = kmalloc(sizeof(InquiryData_struct), GFP_KERNEL);
 	if (inq_buff == NULL) {
 		printk(KERN_WARNING "cciss: out of memory\n");
-		kfree(size_buff);
 		return 1;
 	}
-
-	cciss_read_capacity(h->ctlr, logvol, size_buff, 1, &total_size,
-			    &block_size);
+	if (h->cciss_read == CCISS_READ_10) {
+		cciss_read_capacity(h->ctlr, logvol, 1,
+					&total_size, &block_size);
+	} else {
+		cciss_read_capacity_16(h->ctlr, logvol, 1,
+					&total_size, &block_size);
+	}
 	cciss_geometry_inquiry(h->ctlr, logvol, 1, total_size, block_size,
-			       inq_buff, drv);
-
+					inq_buff, drv);
 	blk_queue_hardsect_size(drv->queue, drv->block_size);
 	set_capacity(disk, drv->nr_blocks);
 
-	kfree(size_buff);
 	kfree(inq_buff);
 	return 0;
 }
@@ -2388,7 +2446,8 @@ static void do_cciss_request(request_que
 {
 	ctlr_info_t *h = q->queuedata;
 	CommandList_struct *c;
-	int start_blk, seg;
+	sector_t start_blk;
+	int seg;
 	struct request *creq;
 	u64bit temp64;
 	struct scatterlist tmp_sg[MAXSGENTRIES];
@@ -2435,7 +2494,7 @@ static void do_cciss_request(request_que
 	    (rq_data_dir(creq) == READ) ? XFER_READ : XFER_WRITE;
 	c->Request.Timeout = 0;	// Don't time out
 	c->Request.CDB[0] =
-	    (rq_data_dir(creq) == READ) ? CCISS_READ : CCISS_WRITE;
+	    (rq_data_dir(creq) == READ) ? h->cciss_read : h->cciss_write;
 	start_blk = creq->sector;
 #ifdef CCISS_DEBUG
 	printk(KERN_DEBUG "ciss: sector =%d nr_sectors=%d\n", (int)creq->sector,
@@ -2469,15 +2528,34 @@ static void do_cciss_request(request_que
 #endif				/* CCISS_DEBUG */
 
 	c->Header.SGList = c->Header.SGTotal = seg;
-	c->Request.CDB[1] = 0;
-	c->Request.CDB[2] = (start_blk >> 24) & 0xff;	//MSB
-	c->Request.CDB[3] = (start_blk >> 16) & 0xff;
-	c->Request.CDB[4] = (start_blk >> 8) & 0xff;
-	c->Request.CDB[5] = start_blk & 0xff;
-	c->Request.CDB[6] = 0;	// (sect >> 24) & 0xff; MSB
-	c->Request.CDB[7] = (creq->nr_sectors >> 8) & 0xff;
-	c->Request.CDB[8] = creq->nr_sectors & 0xff;
-	c->Request.CDB[9] = c->Request.CDB[11] = c->Request.CDB[12] = 0;
+
+	if(h->cciss_read == CCISS_READ_10) {
+		c->Request.CDB[1] = 0;
+		c->Request.CDB[2] = (start_blk >> 24) & 0xff;   //MSB
+		c->Request.CDB[3] = (start_blk >> 16) & 0xff;
+		c->Request.CDB[4] = (start_blk >> 8) & 0xff;
+		c->Request.CDB[5] = start_blk & 0xff;
+		c->Request.CDB[6] = 0;  // (sect >> 24) & 0xff; MSB
+		c->Request.CDB[7] = (creq->nr_sectors >> 8) & 0xff;
+		c->Request.CDB[8] = creq->nr_sectors & 0xff;
+		c->Request.CDB[9] = c->Request.CDB[11] = c->Request.CDB[12] = 0;
+	} else {
+		c->Request.CDBLen = 16;
+		c->Request.CDB[1]= 0;
+		c->Request.CDB[2]= (start_blk >> 56) & 0xff;    //MSB
+		c->Request.CDB[3]= (start_blk >> 48) & 0xff;
+		c->Request.CDB[4]= (start_blk >> 40) & 0xff;
+		c->Request.CDB[5]= (start_blk >> 32) & 0xff;
+		c->Request.CDB[6]= (start_blk >> 24) & 0xff;
+		c->Request.CDB[7]= (start_blk >> 16) & 0xff;
+		c->Request.CDB[8]= (start_blk >>  8) & 0xff;
+		c->Request.CDB[9]= start_blk & 0xff;
+		c->Request.CDB[10]= (creq->nr_sectors >>  24) & 0xff;
+		c->Request.CDB[11]= (creq->nr_sectors >>  16) & 0xff;
+		c->Request.CDB[12]= (creq->nr_sectors >>  8) & 0xff;
+		c->Request.CDB[13]= creq->nr_sectors & 0xff;
+		c->Request.CDB[14] = c->Request.CDB[15] = 0;
+	}
 
 	spin_lock_irq(q->queue_lock);
 
@@ -2941,31 +3019,23 @@ static int cciss_pci_init(ctlr_info_t *c
 static void cciss_getgeometry(int cntl_num)
 {
 	ReportLunData_struct *ld_buff;
-	ReadCapdata_struct *size_buff;
 	InquiryData_struct *inq_buff;
 	int return_code;
 	int i;
 	int listlength = 0;
 	__u32 lunid = 0;
 	int block_size;
-	int total_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;
 	}
-	size_buff = kmalloc(sizeof(ReadCapdata_struct), GFP_KERNEL);
-	if (size_buff == NULL) {
-		printk(KERN_ERR "cciss: out of memory\n");
-		kfree(ld_buff);
-		return;
-	}
-	inq_buff = kmalloc(sizeof(InquiryData_struct), GFP_KERNEL);
+	inq_buff = kzalloc(sizeof( InquiryData_struct), GFP_KERNEL);
 	if (inq_buff == NULL) {
 		printk(KERN_ERR "cciss: out of memory\n");
 		kfree(ld_buff);
-		kfree(size_buff);
 		return;
 	}
 	/* Get the firmware version */
@@ -3020,7 +3090,6 @@ static void cciss_getgeometry(int cntl_n
 #endif				/* CCISS_DEBUG */
 
 	hba[cntl_num]->highest_lun = hba[cntl_num]->num_luns - 1;
-//      for(i=0; i<  hba[cntl_num]->num_luns; i++)
 	for (i = 0; i < CISS_MAX_LUN; i++) {
 		if (i < hba[cntl_num]->num_luns) {
 			lunid = (0xff & (unsigned int)(ld_buff->LUN[i][3]))
@@ -3039,8 +3108,26 @@ static void cciss_getgeometry(int cntl_n
 			       ld_buff->LUN[i][2], ld_buff->LUN[i][3],
 			       hba[cntl_num]->drv[i].LunID);
 #endif				/* CCISS_DEBUG */
-			cciss_read_capacity(cntl_num, i, size_buff, 0,
+
+		/* 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 */
+		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]);
@@ -3050,7 +3137,6 @@ static void cciss_getgeometry(int cntl_n
 		}
 	}
 	kfree(ld_buff);
-	kfree(size_buff);
 	kfree(inq_buff);
 }
 
diff -puN drivers/block/cciss_cmd.h~cciss_2tb_support drivers/block/cciss_cmd.h
--- linux-2.6.18.x86_64/drivers/block/cciss_cmd.h~cciss_2tb_support	2007-03-30 16:10:42.000000000 -0500
+++ linux-2.6.18.x86_64-root/drivers/block/cciss_cmd.h	2007-03-30 16:10:42.000000000 -0500
@@ -119,11 +119,34 @@ typedef struct _ReadCapdata_struct
   BYTE block_size[4];	// Size of blocks in bytes
 } ReadCapdata_struct;
 
-// 12 byte commands not implemented in firmware yet. 
-// #define CCISS_READ 	0xa8	// Read(12)
-// #define CCISS_WRITE	0xaa	// Write(12)
- #define CCISS_READ   0x28    // Read(10)
- #define CCISS_WRITE  0x2a    // Write(10)
+#define CCISS_READ_CAPACITY_16 0x9e /* Read Capacity 16 */
+
+/* service action to differentiate a 16 byte read capacity from
+   other commands that use the 0x9e SCSI op code */
+
+#define CCISS_READ_CAPACITY_16_SERVICE_ACT 0x10
+
+typedef struct _ReadCapdata_struct_16
+{
+	BYTE total_size[8];   /* Total size in blocks */
+	BYTE block_size[4];   /* Size of blocks in bytes */
+	BYTE prot_en:1;       /* protection enable bit */
+	BYTE rto_en:1;        /* reference tag own enable bit */
+	BYTE reserved:6;      /* reserved bits */
+	BYTE reserved2[18];   /* reserved bytes per spec */
+} ReadCapdata_struct_16;
+
+/* Define the supported read/write commands for cciss based controllers */
+
+#define CCISS_READ_10   0x28    /* Read(10)  */
+#define CCISS_WRITE_10  0x2a    /* Write(10) */
+#define CCISS_READ_16   0x88    /* Read(16)  */
+#define CCISS_WRITE_16  0x8a    /* Write(16) */
+
+/* Define the CDB lengths supported by cciss based controllers */
+
+#define CDB_LEN10	10
+#define CDB_LEN16	16
 
 // BMIC commands 
 #define BMIC_READ 0x26
diff -puN drivers/block/cciss.h~cciss_2tb_support drivers/block/cciss.h
--- linux-2.6.18.x86_64/drivers/block/cciss.h~cciss_2tb_support	2007-03-30 16:10:42.000000000 -0500
+++ linux-2.6.18.x86_64-root/drivers/block/cciss.h	2007-03-30 16:10:42.000000000 -0500
@@ -76,6 +76,8 @@ struct ctlr_info 
 	unsigned int intr[4];
 	unsigned int msix_vector;
 	unsigned int msi_vector;
+	BYTE	cciss_read;
+	BYTE	cciss_write;
 
 	// information about each logical volume
 	drive_info_struct drv[CISS_MAX_LUN];
_

diff -puN drivers/block/cciss.c~cciss_3616_for_rh drivers/block/cciss.c
--- linux-2.6.18.x86_64/drivers/block/cciss.c~cciss_3616_for_rh	2007-03-30 16:27:26.000000000 -0500
+++ linux-2.6.18.x86_64-root/drivers/block/cciss.c	2007-03-30 16:27:26.000000000 -0500
@@ -47,15 +47,15 @@
 #include <linux/completion.h>
 
 #define CCISS_DRIVER_VERSION(maj,min,submin) ((maj<<16)|(min<<8)|(submin))
-#define DRIVER_NAME "HP CISS Driver (v 3.6.14-RH1)"
-#define DRIVER_VERSION CCISS_DRIVER_VERSION(3,6,14)
+#define DRIVER_NAME "HP CISS Driver (v 3.6.16-RH1)"
+#define DRIVER_VERSION CCISS_DRIVER_VERSION(3,6,16)
 
 /* Embedded module documentation macros - see modules.h */
 MODULE_AUTHOR("Hewlett-Packard Company");
-MODULE_DESCRIPTION("Driver for HP Controller SA5xxx SA6xxx version 3.6.14-RH1");
+MODULE_DESCRIPTION("Driver for HP Controller SA5xxx SA6xxx version 3.6.16-RH1");
 MODULE_SUPPORTED_DEVICE("HP SA5i SA5i+ SA532 SA5300 SA5312 SA641 SA642 SA6400"
 			" SA6i P600 P800 P400 P400i E200 E200i E500");
-MODULE_VERSION("3.6.14-RH1");
+MODULE_VERSION("3.6.16-RH1");
 MODULE_LICENSE("GPL");
 
 #include "cciss_cmd.h"
_