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" _