X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fblock%2Fcciss.c;h=ff900aa3f0e52a094efbdfb19e6d2187a13b50c1;hb=43bc926fffd92024b46cafaf7350d669ba9ca884;hp=0d45b81e19aee582ba818eda04f1d4f85fb939fc;hpb=9213980e6a70d8473e0ffd4b39ab5b6caaba9ff5;p=linux-2.6.git diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index 0d45b81e1..ff900aa3f 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c @@ -1,6 +1,6 @@ /* * Disk Array driver for HP SA 5xxx and 6xxx Controllers - * Copyright 2000, 2002 Hewlett-Packard Development Company, L.P. + * Copyright 2000, 2006 Hewlett-Packard Development Company, L.P. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,7 +16,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * - * Questions/Comments/Bugfixes to Cciss-discuss@lists.sourceforge.net + * Questions/Comments/Bugfixes to iss_storagedev@hp.com * */ @@ -37,30 +37,34 @@ #include #include #include +#include +#include #include #include +#include #include #include #include #define CCISS_DRIVER_VERSION(maj,min,submin) ((maj<<16)|(min<<8)|(submin)) -#define DRIVER_NAME "Compaq CISS Driver (v 2.6.2)" -#define DRIVER_VERSION CCISS_DRIVER_VERSION(2,6,2) +#define DRIVER_NAME "HP CISS Driver (v 2.6.10)" +#define DRIVER_VERSION CCISS_DRIVER_VERSION(2,6,10) /* Embedded module documentation macros - see modules.h */ MODULE_AUTHOR("Hewlett-Packard Company"); -MODULE_DESCRIPTION("Driver for HP Controller SA5xxx SA6xxx version 2.6.2"); +MODULE_DESCRIPTION("Driver for HP Controller SA5xxx SA6xxx version 2.6.10"); MODULE_SUPPORTED_DEVICE("HP SA5i SA5i+ SA532 SA5300 SA5312 SA641 SA642 SA6400" - " SA6i"); + " SA6i P600 P800 P400 P400i E200 E200i"); MODULE_LICENSE("GPL"); +MODULE_VERSION("2.6.8"); #include "cciss_cmd.h" #include "cciss.h" #include /* define the PCI info for the cards we can control */ -const struct pci_device_id cciss_pci_device_id[] = { +static const struct pci_device_id cciss_pci_device_id[] = { { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_COMPAQ_CISS, 0x0E11, 0x4070, 0, 0, 0}, { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_COMPAQ_CISSB, @@ -79,15 +83,29 @@ const struct pci_device_id cciss_pci_device_id[] = { 0x0E11, 0x409D, 0, 0, 0}, { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_COMPAQ_CISSC, 0x0E11, 0x4091, 0, 0, 0}, - { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_COMPAQ_CISSC, - 0x0E11, 0x409E, 0, 0, 0}, - { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_COMPAQ_CISSC, - 0x103C, 0x3211, 0, 0, 0}, + { PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSA, + 0x103C, 0x3225, 0, 0, 0}, + { PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSC, + 0x103c, 0x3223, 0, 0, 0}, + { PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSC, + 0x103c, 0x3234, 0, 0, 0}, + { PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSC, + 0x103c, 0x3235, 0, 0, 0}, + { PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSD, + 0x103c, 0x3211, 0, 0, 0}, + { PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSD, + 0x103c, 0x3212, 0, 0, 0}, + { PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSD, + 0x103c, 0x3213, 0, 0, 0}, + { PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSD, + 0x103c, 0x3214, 0, 0, 0}, + { PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSD, + 0x103c, 0x3215, 0, 0, 0}, {0,} }; MODULE_DEVICE_TABLE(pci, cciss_pci_device_id); -#define NR_PRODUCTS (sizeof(products)/sizeof(struct board_type)) +#define NR_PRODUCTS ARRAY_SIZE(products) /* board_id = Subsystem Device ID & Vendor ID * product = Marketing Name for the board @@ -103,8 +121,15 @@ static struct board_type products[] = { { 0x409C0E11, "Smart Array 6400", &SA5_access}, { 0x409D0E11, "Smart Array 6400 EM", &SA5_access}, { 0x40910E11, "Smart Array 6i", &SA5_access}, - { 0x409E0E11, "Smart Array 6422", &SA5_access}, - { 0x3211103C, "Smart Array V100", &SA5_access}, + { 0x3225103C, "Smart Array P600", &SA5_access}, + { 0x3223103C, "Smart Array P800", &SA5_access}, + { 0x3234103C, "Smart Array P400", &SA5_access}, + { 0x3235103C, "Smart Array P400i", &SA5_access}, + { 0x3211103C, "Smart Array E200i", &SA5_access}, + { 0x3212103C, "Smart Array E200", &SA5_access}, + { 0x3213103C, "Smart Array E200i", &SA5_access}, + { 0x3214103C, "Smart Array E200i", &SA5_access}, + { 0x3215103C, "Smart Array E200i", &SA5_access}, }; /* How long to wait (in millesconds) for board to go into simple mode */ @@ -114,31 +139,46 @@ static struct board_type products[] = { /*define how many times we will try a command because of bus resets */ #define MAX_CMD_RETRIES 3 -#define READ_AHEAD 256 +#define READ_AHEAD 1024 #define NR_CMDS 384 /* #commands that can be outstanding */ -#define MAX_CTLR 8 +#define MAX_CTLR 32 + +/* Originally cciss driver only supports 8 major numbers */ +#define MAX_CTLR_ORIG 8 -#define CCISS_DMA_MASK 0xFFFFFFFF /* 32 bit DMA */ static ctlr_info_t *hba[MAX_CTLR]; static void do_cciss_request(request_queue_t *q); +static irqreturn_t do_cciss_intr(int irq, void *dev_id, struct pt_regs *regs); static int cciss_open(struct inode *inode, struct file *filep); static int cciss_release(struct inode *inode, struct file *filep); static int cciss_ioctl(struct inode *inode, struct file *filep, 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 deregister_disk(struct gendisk *disk); -static int register_new_disk(ctlr_info_t *h); +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_geometry_inquiry(int ctlr, int logvol, + int withirq, unsigned int 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); static int sendcmd( __u8 cmd, int ctlr, void *buff, size_t size, unsigned int use_unit_num, unsigned int log_unit, __u8 page_code, unsigned char *scsi3addr, int cmd_type); +static int sendcmd_withirq(__u8 cmd, int ctlr, void *buff, size_t size, + unsigned int use_unit_num, unsigned int log_unit, __u8 page_code, + int cmd_type); + +static void fail_all_cmds(unsigned long ctlr); #ifdef CONFIG_PROC_FS static int cciss_proc_get_info(char *buffer, char **start, off_t offset, @@ -148,11 +188,19 @@ static void cciss_procinit(int i); static void cciss_procinit(int i) {} #endif /* CONFIG_PROC_FS */ +#ifdef CONFIG_COMPAT +static long cciss_compat_ioctl(struct file *f, unsigned cmd, unsigned long arg); +#endif + static struct block_device_operations cciss_fops = { .owner = THIS_MODULE, .open = cciss_open, .release = cciss_release, .ioctl = cciss_ioctl, + .getgeo = cciss_getgeo, +#ifdef CONFIG_COMPAT + .compat_ioctl = cciss_compat_ioctl, +#endif .revalidate_disk= cciss_revalidate, }; @@ -184,17 +232,18 @@ static inline CommandList_struct *removeQ(CommandList_struct **Qptr, } return c; } -#ifdef CONFIG_PROC_FS #include "cciss_scsi.c" /* For SCSI tape support */ +#ifdef CONFIG_PROC_FS + /* * Report information about this controller. */ -#define ENG_GIG 1048576000 +#define ENG_GIG 1000000000 #define ENG_GIG_FACTOR (ENG_GIG/512) #define RAID_UNKNOWN 6 -static const char *raid_label[] = {"0","4","1(0+1)","5","5+1","ADG", +static const char *raid_label[] = {"0","4","1(1+0)","5","5+1","ADG", "UNKNOWN"}; static struct proc_dir_entry *proc_cciss; @@ -208,7 +257,7 @@ static int cciss_proc_get_info(char *buffer, char **start, off_t offset, ctlr_info_t *h = (ctlr_info_t*)data; drive_info_struct *drv; unsigned long flags; - unsigned int vol_sz, vol_sz_frac; + sector_t vol_sz, vol_sz_frac; ctlr = h->ctlr; @@ -237,7 +286,7 @@ static int cciss_proc_get_info(char *buffer, char **start, off_t offset, h->product_name, (unsigned long)h->board_id, h->firm_ver[0], h->firm_ver[1], h->firm_ver[2], h->firm_ver[3], - (unsigned int)h->intr, + (unsigned int)h->intr[SIMPLE_MODE_INT], h->num_luns, h->Qdepth, h->commands_outstanding, h->maxQsinceinit, h->max_outstanding, h->maxSG); @@ -245,32 +294,21 @@ static int cciss_proc_get_info(char *buffer, char **start, off_t offset, pos += size; len += size; cciss_proc_tape_report(ctlr, buffer, &pos, &len); for(i=0; i<=h->highest_lun; i++) { - sector_t tmp; drv = &h->drv[i]; - if (drv->block_size == 0) + if (drv->heads == 0) continue; - vol_sz = drv->nr_blocks; - sector_div(vol_sz, ENG_GIG_FACTOR); - - /* - * Awkwardly do this: - * vol_sz_frac = - * (drv->nr_blocks%ENG_GIG_FACTOR)*100/ENG_GIG_FACTOR; - */ - tmp = drv->nr_blocks; - vol_sz_frac = sector_div(tmp, ENG_GIG_FACTOR); - - /* Now, vol_sz_frac = (drv->nr_blocks%ENG_GIG_FACTOR) */ + vol_sz = drv->nr_blocks; + vol_sz_frac = sector_div(vol_sz, ENG_GIG_FACTOR); vol_sz_frac *= 100; sector_div(vol_sz_frac, ENG_GIG_FACTOR); if (drv->raid_level > 5) drv->raid_level = RAID_UNKNOWN; size = sprintf(buffer+len, "cciss/c%dd%d:" - "\t%4d.%02dGB\tRAID %s\n", - ctlr, i, vol_sz,vol_sz_frac, + "\t%4u.%02uGB\tRAID %s\n", + ctlr, i, (int)vol_sz, (int)vol_sz_frac, raid_label[drv->raid_level]); pos += size; len += size; } @@ -299,7 +337,7 @@ cciss_proc_write(struct file *file, const char __user *buffer, if (copy_from_user(cmd, buffer, count)) return -EFAULT; cmd[count] = '\0'; len = strlen(cmd); // above 3 lines ensure safety - if (cmd[len-1] == '\n') + if (len && cmd[len-1] == '\n') cmd[--len] = '\0'; # ifdef CONFIG_CISS_SCSI_TAPE if (strcmp("engage scsi", cmd)==0) { @@ -356,6 +394,8 @@ static CommandList_struct * cmd_alloc(ctlr_info_t *h, int get_from_pool) return NULL; memset(c, 0, sizeof(CommandList_struct)); + c->cmdindex = -1; + c->err_info = (ErrorInfo_struct *)pci_alloc_consistent( h->pdev, sizeof(ErrorInfo_struct), &err_dma_handle); @@ -386,6 +426,8 @@ static CommandList_struct * cmd_alloc(ctlr_info_t *h, int get_from_pool) err_dma_handle = h->errinfo_pool_dhandle + i*sizeof(ErrorInfo_struct); h->nr_allocs++; + + c->cmdindex = i; } c->busaddr = (__u32) cmd_dma_handle; @@ -446,15 +488,26 @@ static int cciss_open(struct inode *inode, struct file *filep) printk(KERN_DEBUG "cciss_open %s\n", inode->i_bdev->bd_disk->disk_name); #endif /* CCISS_DEBUG */ + if (host->busy_initializing || drv->busy_configuring) + return -EBUSY; /* * Root is allowed to open raw volume zero even if it's not configured - * so array config can still work. I don't think I really like this, + * so array config can still work. Root is also allowed to open any + * volume that has a LUN ID, so it can issue IOCTL to reread the + * disk information. I don't think I really like this * but I'm already using way to many device nodes to claim another one * for "raw controller". */ if (drv->nr_blocks == 0) { - if (iminor(inode) != 0) + if (iminor(inode) != 0) { /* not node 0? */ + /* if not node 0 make sure it is a partition = 0 */ + if (iminor(inode) & 0x0f) { return -ENXIO; + /* if it is, make sure we have a LUN ID */ + } else if (drv->LunID == 0) { + return -ENXIO; + } + } if (!capable(CAP_SYS_ADMIN)) return -EPERM; } @@ -479,6 +532,125 @@ static int cciss_release(struct inode *inode, struct file *filep) return 0; } +#ifdef CONFIG_COMPAT + +static int do_ioctl(struct file *f, unsigned cmd, unsigned long arg) +{ + int ret; + lock_kernel(); + ret = cciss_ioctl(f->f_dentry->d_inode, f, cmd, arg); + unlock_kernel(); + return ret; +} + +static int cciss_ioctl32_passthru(struct file *f, unsigned cmd, unsigned long arg); +static int cciss_ioctl32_big_passthru(struct file *f, unsigned cmd, unsigned long arg); + +static long cciss_compat_ioctl(struct file *f, unsigned cmd, unsigned long arg) +{ + switch (cmd) { + case CCISS_GETPCIINFO: + case CCISS_GETINTINFO: + case CCISS_SETINTINFO: + case CCISS_GETNODENAME: + case CCISS_SETNODENAME: + case CCISS_GETHEARTBEAT: + case CCISS_GETBUSTYPES: + case CCISS_GETFIRMVER: + case CCISS_GETDRIVVER: + case CCISS_REVALIDVOLS: + case CCISS_DEREGDISK: + case CCISS_REGNEWDISK: + case CCISS_REGNEWD: + case CCISS_RESCANDISK: + case CCISS_GETLUNINFO: + return do_ioctl(f, cmd, arg); + + case CCISS_PASSTHRU32: + return cciss_ioctl32_passthru(f, cmd, arg); + case CCISS_BIG_PASSTHRU32: + return cciss_ioctl32_big_passthru(f, cmd, arg); + + default: + return -ENOIOCTLCMD; + } +} + +static int cciss_ioctl32_passthru(struct file *f, unsigned cmd, unsigned long arg) +{ + IOCTL32_Command_struct __user *arg32 = + (IOCTL32_Command_struct __user *) arg; + IOCTL_Command_struct arg64; + IOCTL_Command_struct __user *p = compat_alloc_user_space(sizeof(arg64)); + int err; + u32 cp; + + err = 0; + err |= copy_from_user(&arg64.LUN_info, &arg32->LUN_info, sizeof(arg64.LUN_info)); + err |= copy_from_user(&arg64.Request, &arg32->Request, sizeof(arg64.Request)); + err |= copy_from_user(&arg64.error_info, &arg32->error_info, sizeof(arg64.error_info)); + err |= get_user(arg64.buf_size, &arg32->buf_size); + err |= get_user(cp, &arg32->buf); + arg64.buf = compat_ptr(cp); + err |= copy_to_user(p, &arg64, sizeof(arg64)); + + if (err) + return -EFAULT; + + err = do_ioctl(f, CCISS_PASSTHRU, (unsigned long) p); + if (err) + return err; + err |= copy_in_user(&arg32->error_info, &p->error_info, sizeof(arg32->error_info)); + if (err) + return -EFAULT; + return err; +} + +static int cciss_ioctl32_big_passthru(struct file *file, unsigned cmd, unsigned long arg) +{ + BIG_IOCTL32_Command_struct __user *arg32 = + (BIG_IOCTL32_Command_struct __user *) arg; + BIG_IOCTL_Command_struct arg64; + BIG_IOCTL_Command_struct __user *p = compat_alloc_user_space(sizeof(arg64)); + int err; + u32 cp; + + err = 0; + err |= copy_from_user(&arg64.LUN_info, &arg32->LUN_info, sizeof(arg64.LUN_info)); + err |= copy_from_user(&arg64.Request, &arg32->Request, sizeof(arg64.Request)); + err |= copy_from_user(&arg64.error_info, &arg32->error_info, sizeof(arg64.error_info)); + err |= get_user(arg64.buf_size, &arg32->buf_size); + err |= get_user(arg64.malloc_size, &arg32->malloc_size); + err |= get_user(cp, &arg32->buf); + arg64.buf = compat_ptr(cp); + err |= copy_to_user(p, &arg64, sizeof(arg64)); + + if (err) + return -EFAULT; + + err = do_ioctl(file, CCISS_BIG_PASSTHRU, (unsigned long) p); + if (err) + return err; + err |= copy_in_user(&arg32->error_info, &p->error_info, sizeof(arg32->error_info)); + if (err) + return -EFAULT; + return err; +} +#endif + +static int cciss_getgeo(struct block_device *bdev, struct hd_geometry *geo) +{ + drive_info_struct *drv = get_drv(bdev->bd_disk); + + if (!drv->cylinders) + return -ENXIO; + + geo->heads = drv->heads; + geo->sectors = drv->sectors; + geo->cylinders = drv->cylinders; + return 0; +} + /* * ioctl */ @@ -497,26 +669,12 @@ static int cciss_ioctl(struct inode *inode, struct file *filep, #endif /* CCISS_DEBUG */ switch(cmd) { - case HDIO_GETGEO: - { - struct hd_geometry driver_geo; - if (drv->cylinders) { - driver_geo.heads = drv->heads; - driver_geo.sectors = drv->sectors; - driver_geo.cylinders = drv->cylinders; - } else - return -ENXIO; - driver_geo.start= get_start_sect(inode->i_bdev); - if (copy_to_user(argp, &driver_geo, sizeof(struct hd_geometry))) - return -EFAULT; - return(0); - } - case CCISS_GETPCIINFO: { cciss_pci_info_struct pciinfo; if (!arg) return -EINVAL; + pciinfo.domain = pci_domain_nr(host->pdev->bus); pciinfo.bus = host->pdev->bus->number; pciinfo.dev_fn = host->pdev->devfn; pciinfo.board_id = host->board_id; @@ -664,28 +822,20 @@ static int cciss_ioctl(struct inode *inode, struct file *filep, case CCISS_GETLUNINFO: { LogvolInfo_struct luninfo; - int i; luninfo.LunID = drv->LunID; luninfo.num_opens = drv->usage_count; luninfo.num_parts = 0; - /* count partitions 1 to 15 with sizes > 0 */ - for(i=1; i part[i]) - continue; - if (disk->part[i]->nr_sects != 0) - luninfo.num_parts++; - } if (copy_to_user(argp, &luninfo, sizeof(LogvolInfo_struct))) return -EFAULT; return(0); } case CCISS_DEREGDISK: - return deregister_disk(disk); + return rebuild_lun_table(host, disk); case CCISS_REGNEWD: - return register_new_disk(host); + return rebuild_lun_table(host, NULL); case CCISS_PASSTHRU: { @@ -726,6 +876,8 @@ static int cciss_ioctl(struct inode *inode, struct file *filep, kfree(buff); return -EFAULT; } + } else { + memset(buff, 0, iocommand.buf_size); } if ((c = cmd_alloc(host , 0)) == NULL) { @@ -845,13 +997,11 @@ static int cciss_ioctl(struct inode *inode, struct file *filep, status = -EINVAL; goto cleanup1; } - buff = (unsigned char **) kmalloc(MAXSGENTRIES * - sizeof(char *), GFP_KERNEL); + buff = kzalloc(MAXSGENTRIES * sizeof(char *), GFP_KERNEL); if (!buff) { status = -ENOMEM; goto cleanup1; } - memset(buff, 0, MAXSGENTRIES); buff_size = (int *) kmalloc(MAXSGENTRIES * sizeof(int), GFP_KERNEL); if (!buff_size) { @@ -868,10 +1018,13 @@ static int cciss_ioctl(struct inode *inode, struct file *filep, status = -ENOMEM; goto cleanup1; } - if (ioc->Request.Type.Direction == XFER_WRITE && - copy_from_user(buff[sg_used], data_ptr, sz)) { + if (ioc->Request.Type.Direction == XFER_WRITE) { + if (copy_from_user(buff[sg_used], data_ptr, sz)) { status = -ENOMEM; - goto cleanup1; + goto cleanup1; + } + } else { + memset(buff[sg_used], 0, sz); } left -= sz; data_ptr += sz; @@ -946,29 +1099,19 @@ static int cciss_ioctl(struct inode *inode, struct file *filep, cleanup1: if (buff) { for(i=0; iprivate_data; - set_capacity(disk, drv->nr_blocks); - return 0; -} - /* * revalidate_allvol is for online array config utilities. After a * utility reconfigures the drives in the array, it can use this function @@ -997,8 +1140,14 @@ static int revalidate_allvol(ctlr_info_t *host) for(i=0; i< NWD; i++) { struct gendisk *disk = host->gendisk[i]; - if (disk->flags & GENHD_FL_UP) - del_gendisk(disk); + if (disk) { + request_queue_t *q = disk->queue; + + if (disk->flags & GENHD_FL_UP) + del_gendisk(disk); + if (q) + blk_cleanup_queue(q); + } } /* @@ -1020,9 +1169,11 @@ static int revalidate_allvol(ctlr_info_t *host) for (i = 0; i < NWD; i++) { struct gendisk *disk = host->gendisk[i]; drive_info_struct *drv = &(host->drv[i]); - if (!drv->nr_blocks) + /* 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(host->queue, drv->block_size); + blk_queue_hardsect_size(drv->queue, drv->block_size); set_capacity(disk, drv->nr_blocks); add_disk(disk); } @@ -1030,48 +1181,376 @@ static int revalidate_allvol(ctlr_info_t *host) return 0; } -static int deregister_disk(struct gendisk *disk) +static inline void complete_buffers(struct bio *bio, int status) { + while (bio) { + struct bio *xbh = bio->bi_next; + int nr_sectors = bio_sectors(bio); + + bio->bi_next = NULL; + blk_finished_io(len); + bio_endio(bio, nr_sectors << 9, status ? 0 : -EIO); + bio = xbh; + } + +} + +static void cciss_softirq_done(struct request *rq) +{ + CommandList_struct *cmd = rq->completion_data; + ctlr_info_t *h = hba[cmd->ctlr]; unsigned long flags; - ctlr_info_t *h = get_host(disk); - drive_info_struct *drv = get_drv(disk); + u64bit temp64; + int i, ddir; + + if (cmd->Request.Type.Direction == XFER_READ) + ddir = PCI_DMA_FROMDEVICE; + else + ddir = PCI_DMA_TODEVICE; + + /* command did not need to be retried */ + /* unmap the DMA mapping for all the scatter gather elements */ + for(i=0; iHeader.SGList; i++) { + temp64.val32.lower = cmd->SG[i].Addr.lower; + temp64.val32.upper = cmd->SG[i].Addr.upper; + pci_unmap_page(h->pdev, temp64.val, cmd->SG[i].Len, ddir); + } + + complete_buffers(rq->bio, rq->errors); + +#ifdef CCISS_DEBUG + printk("Done with %p\n", rq); +#endif /* CCISS_DEBUG */ + + spin_lock_irqsave(&h->lock, flags); + end_that_request_last(rq, rq->errors); + cmd_free(h, cmd,1); + spin_unlock_irqrestore(&h->lock, flags); +} + +/* 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. +*/ +static void cciss_update_drive_info(int ctlr, int drv_index) + { + 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; + unsigned long flags = 0; + int ret = 0; + + /* if the disk already exists then deregister it before proceeding*/ + if (h->drv[drv_index].raid_level != -1){ + spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags); + h->drv[drv_index].busy_configuring = 1; + spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags); + ret = deregister_disk(h->gendisk[drv_index], + &h->drv[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 sturcture */ + 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]); + + ++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); + + /* Set up queue information */ + disk->queue->backing_dev_info.ra_pages = READ_AHEAD; + 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, 512); + + 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); + + h->drv[drv_index].queue = disk->queue; + add_disk(disk); + } + +freeret: + kfree(size_buff); + kfree(inq_buff); + return; +mem_msg: + printk(KERN_ERR "cciss: out of memory\n"); + goto freeret; +} + +/* This function will find the first index of the controllers drive array + * that has a -1 for the raid_level and will return that index. This is + * 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. +*/ +static int cciss_find_free_drive_index(int ctlr) +{ + 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; + return i; + } + } + return -1; +} + +/* This function will add and remove logical drives from the Logical + * drive array of the controller and maintain persistancy of ordering + * so that mount points are preserved until the next reboot. This allows + * for the removal of logical drives in the middle of the drive array + * without a re-ordering of those drives. + * INPUT + * h = The controller to perform the operations on + * 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) +{ 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; + int drv_found; + int drv_index = 0; + __u32 lunid = 0; + unsigned long flags; + + /* 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; + } + 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. + */ + 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; + + 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 |= (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; + 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"); + } + + /* 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; + + } + h->drv[drv_index].LunID = lunid; + cciss_update_drive_info(ctlr, drv_index); + } /* end for */ + } /* end else */ + +freeret: + kfree(ld_buff); + h->busy_configuring = 0; + /* We return -1 here to tell the ACU that we have registered/updated + * all of the drives that we can and to keep it from calling us + * additional times. + */ + return -1; +mem_msg: + printk(KERN_ERR "cciss: out of memory\n"); + goto freeret; +} + +/* This function will deregister the disk and it's queue from the + * kernel. It must be called with the controller lock held and the + * drv structures busy_configuring flag set. It's parameters are: + * + * disk = This is the disk to be deregistered + * drv = This is the drive_info_struct associated with the disk to be + * deregistered. It contains information about the disk used + * by the driver. + * clear_all = This flag determines whether or not the disk information + * is going to be completely cleared out and the highest_lun + * reset. Sometimes we want to clear out information about + * the disk in preperation for re-adding it. In this case + * 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, + int clear_all) +{ + ctlr_info_t *h = get_host(disk); if (!capable(CAP_SYS_RAWIO)) return -EPERM; - spin_lock_irqsave(CCISS_LOCK(ctlr), flags); /* make sure logical volume is NOT is use */ - if( drv->usage_count > 1) { - spin_unlock_irqrestore(CCISS_LOCK(ctlr), flags); + if(clear_all || (h->gendisk[0] == disk)) { + if (drv->usage_count > 1) return -EBUSY; } - drv->usage_count++; - spin_unlock_irqrestore(CCISS_LOCK(ctlr), flags); + else + if( drv->usage_count > 0 ) + return -EBUSY; + + /* invalidate the devices and deregister the disk. If it is disk + * zero do not deregister it but just zero out it's values. This + * allows us to delete disk zero but keep the controller registered. + */ + if (h->gendisk[0] != disk){ + if (disk) { + request_queue_t *q = disk->queue; + if (disk->flags & GENHD_FL_UP) + del_gendisk(disk); + if (q) { + blk_cleanup_queue(q); + drv->queue = NULL; + } + } + } + + --h->num_luns; + /* zero out the disk size info */ + drv->nr_blocks = 0; + drv->block_size = 0; + drv->heads = 0; + drv->sectors = 0; + drv->cylinders = 0; + drv->raid_level = -1; /* This can be used as a flag variable to + * indicate that this element of the drive + * array is free. + */ - /* invalidate the devices and deregister the disk */ - if (disk->flags & GENHD_FL_UP) - del_gendisk(disk); + if (clear_all){ /* check to see if it was the last disk */ if (drv == h->drv + h->highest_lun) { /* if so, find the new hightest lun */ int i, newhighest =-1; for(i=0; ihighest_lun; i++) { /* if the disk has size > 0, it is available */ - if (h->drv[i].nr_blocks) + if (h->drv[i].heads) newhighest = i; } h->highest_lun = newhighest; - } - --h->num_luns; - /* zero out the disk size info */ - drv->nr_blocks = 0; - drv->block_size = 0; - drv->cylinders = 0; + drv->LunID = 0; + } return(0); } + static int fill_cmd(CommandList_struct *c, __u8 cmd, int ctlr, void *buff, size_t size, unsigned int use_unit_num, /* 0: address the controller, @@ -1166,6 +1645,24 @@ static int fill_cmd(CommandList_struct *c, __u8 cmd, int ctlr, void *buff, } } else if (cmd_type == TYPE_MSG) { switch (cmd) { + case 0: /* ABORT message */ + c->Request.CDBLen = 12; + c->Request.Type.Attribute = ATTR_SIMPLE; + c->Request.Type.Direction = XFER_WRITE; + c->Request.Timeout = 0; + c->Request.CDB[0] = cmd; /* abort */ + c->Request.CDB[1] = 0; /* abort a command */ + /* buff contains the tag of the command to abort */ + memcpy(&c->Request.CDB[4], buff, 8); + break; + case 1: /* RESET message */ + c->Request.CDBLen = 12; + c->Request.Type.Attribute = ATTR_SIMPLE; + c->Request.Type.Direction = XFER_WRITE; + c->Request.Timeout = 0; + memset(&c->Request.CDB[0], 0, sizeof(c->Request.CDB)); + c->Request.CDB[0] = cmd; /* reset */ + c->Request.CDB[1] = 0x04; /* reset a LUN */ case 3: /* No-Op message */ c->Request.CDBLen = 1; c->Request.Type.Attribute = ATTR_SIMPLE; @@ -1307,8 +1804,10 @@ case CMD_HARDWARE_ERR: } } /* unlock the buffers from DMA */ + buff_dma_handle.val32.lower = c->SG[0].Addr.lower; + buff_dma_handle.val32.upper = c->SG[0].Addr.upper; pci_unmap_single( h->pdev, (dma_addr_t) buff_dma_handle.val, - size, PCI_DMA_BIDIRECTIONAL); + c->SG[0].Len, PCI_DMA_BIDIRECTIONAL); cmd_free(h, c, 0); return(return_status); @@ -1337,21 +1836,22 @@ static void cciss_geometry_inquiry(int ctlr, int logvol, drv->sectors = 32; // Sectors per track drv->cylinders = total_size / 255 / 32; } 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; + } } } else { /* Get geometry failed */ - printk(KERN_WARNING "cciss: reading geometry failed, " - "continuing with default 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; + printk(KERN_WARNING "cciss: reading geometry failed\n"); } printk(KERN_INFO " heads= %d, sectors= %d, cylinders= %d\n\n", drv->heads, drv->sectors, drv->cylinders); @@ -1369,8 +1869,8 @@ cciss_read_capacity(int ctlr, int logvol, ReadCapdata_struct *buf, return_code = sendcmd(CCISS_READ_CAPACITY, ctlr, buf, sizeof(*buf), 1, logvol, 0, NULL, TYPE_CMD); if (return_code == IO_OK) { - *total_size = be32_to_cpu(*((__u32 *) &buf->total_size[0]))+1; - *block_size = be32_to_cpu(*((__u32 *) &buf->block_size[0])); + *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 */ printk(KERN_WARNING "cciss: read capacity failed\n"); *total_size = 0; @@ -1380,161 +1880,53 @@ cciss_read_capacity(int ctlr, int logvol, ReadCapdata_struct *buf, *total_size, *block_size); return; } -static int register_new_disk(ctlr_info_t *h) + +static int cciss_revalidate(struct gendisk *disk) { - struct gendisk *disk; - int ctlr = h->ctlr; - int i; - int num_luns; + ctlr_info_t *h = get_host(disk); + drive_info_struct *drv = get_drv(disk); int logvol; - int new_lun_found = 0; - int new_lun_index = 0; - int free_index_found = 0; - int free_index = 0; - ReportLunData_struct *ld_buff = NULL; - ReadCapdata_struct *size_buff = NULL; - InquiryData_struct *inq_buff = NULL; - int return_code; - int listlength = 0; - __u32 lunid = 0; + int FOUND=0; unsigned int block_size; unsigned int total_size; + ReadCapdata_struct *size_buff = NULL; + InquiryData_struct *inq_buff = NULL; + + for(logvol=0; logvol < CISS_MAX_LUN; logvol++) + { + if(h->drv[logvol].LunID == drv->LunID) { + FOUND=1; + break; + } + } + + if (!FOUND) return 1; - if (!capable(CAP_SYS_RAWIO)) - return -EPERM; - /* if we have no space in our disk array left to add anything */ - if( h->num_luns >= CISS_MAX_LUN) - return -EINVAL; - - ld_buff = kmalloc(sizeof(ReportLunData_struct), GFP_KERNEL); - if (ld_buff == NULL) - goto mem_msg; - memset(ld_buff, 0, sizeof(ReportLunData_struct)); size_buff = kmalloc(sizeof( ReadCapdata_struct), GFP_KERNEL); if (size_buff == NULL) - goto mem_msg; + { + printk(KERN_WARNING "cciss: out of memory\n"); + return 1; + } inq_buff = kmalloc(sizeof( InquiryData_struct), GFP_KERNEL); if (inq_buff == NULL) - goto mem_msg; - - return_code = sendcmd_withirq(CISS_REPORT_LOG, ctlr, ld_buff, - sizeof(ReportLunData_struct), 0, 0, 0, TYPE_CMD); + { + printk(KERN_WARNING "cciss: out of memory\n"); + kfree(size_buff); + return 1; + } - if( return_code == IO_OK) - { - - // printk("LUN Data\n--------------------------\n"); + cciss_read_capacity(h->ctlr, logvol, size_buff, 1, &total_size, &block_size); + cciss_geometry_inquiry(h->ctlr, logvol, 1, total_size, block_size, inq_buff, drv); - 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; - goto free_err; - } - num_luns = listlength / 8; // 8 bytes pre entry - if (num_luns > CISS_MAX_LUN) - { - 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], num_luns); -#endif - for(i=0; i< num_luns; i++) - { - int j; - int lunID_found = 0; + blk_queue_hardsect_size(drv->queue, drv->block_size); + set_capacity(disk, drv->nr_blocks); - 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]); - - /* check to see if this is a new lun */ - for(j=0; j <= h->highest_lun; j++) - { -#ifdef CCISS_DEBUG - printk("Checking %d %x against %x\n", j,h->drv[j].LunID, - lunid); -#endif /* CCISS_DEBUG */ - if (h->drv[j].LunID == lunid) - { - lunID_found = 1; - break; - } - - } - if( lunID_found == 1) - continue; - else - { /* It is the new lun we have been looking for */ -#ifdef CCISS_DEBUG - printk("new lun found at %d\n", i); -#endif /* CCISS_DEBUG */ - new_lun_index = i; - new_lun_found = 1; - break; - } - } - if (!new_lun_found) - { - printk(KERN_WARNING "cciss: New Logical Volume not found\n"); - goto free_err; - } - /* Now find the free index */ - for(i=0; i drv[i].LunID == 0) - { -#ifdef CCISS_DEBUG - printk("free index found at %d\n", i); -#endif /* CCISS_DEBUG */ - free_index_found = 1; - free_index = i; - break; - } - } - if (!free_index_found) - { - printk(KERN_WARNING "cciss: unable to find free slot for disk\n"); - goto free_err; - } - - logvol = free_index; - h->drv[logvol].LunID = lunid; - /* there could be gaps in lun numbers, track hightest */ - if(h->highest_lun < lunid) - h->highest_lun = logvol; - cciss_read_capacity(ctlr, logvol, size_buff, 1, - &total_size, &block_size); - cciss_geometry_inquiry(ctlr, logvol, 1, total_size, block_size, - inq_buff, &h->drv[logvol]); - h->drv[logvol].usage_count = 0; - ++h->num_luns; - /* setup partitions per disk */ - disk = h->gendisk[logvol]; - set_capacity(disk, h->drv[logvol].nr_blocks); - add_disk(disk); -freeret: - kfree(ld_buff); kfree(size_buff); kfree(inq_buff); - return (logvol); -mem_msg: - printk(KERN_ERR "cciss: out of memory\n"); -free_err: - logvol = -1; - goto freeret; + return 0; } + /* * Wait polling for a command to complete. * The memory mapped FIFO is polled for the completion. @@ -1549,15 +1941,60 @@ static unsigned long pollcomplete(int ctlr) for (i = 20 * HZ; i > 0; i--) { done = hba[ctlr]->access.command_completed(hba[ctlr]); - if (done == FIFO_EMPTY) { - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(1); - } else + if (done == FIFO_EMPTY) + schedule_timeout_uninterruptible(1); + else return (done); } /* Invalid address to tell caller we ran out of time */ return 1; } + +static int add_sendcmd_reject(__u8 cmd, int ctlr, unsigned long complete) +{ + /* We get in here if sendcmd() is polling for completions + and gets some command back that it wasn't expecting -- + something other than that which it just sent down. + Ordinarily, that shouldn't happen, but it can happen when + the scsi tape stuff gets into error handling mode, and + starts using sendcmd() to try to abort commands and + reset tape drives. In that case, sendcmd may pick up + completions of commands that were sent to logical drives + through the block i/o system, or cciss ioctls completing, etc. + In that case, we need to save those completions for later + processing by the interrupt handler. + */ + +#ifdef CONFIG_CISS_SCSI_TAPE + struct sendcmd_reject_list *srl = &hba[ctlr]->scsi_rejects; + + /* If it's not the scsi tape stuff doing error handling, (abort */ + /* or reset) then we don't expect anything weird. */ + if (cmd != CCISS_RESET_MSG && cmd != CCISS_ABORT_MSG) { +#endif + printk( KERN_WARNING "cciss cciss%d: SendCmd " + "Invalid command list address returned! (%lx)\n", + ctlr, complete); + /* not much we can do. */ +#ifdef CONFIG_CISS_SCSI_TAPE + return 1; + } + + /* We've sent down an abort or reset, but something else + has completed */ + if (srl->ncompletions >= (NR_CMDS + 2)) { + /* Uh oh. No room to save it for later... */ + printk(KERN_WARNING "cciss%d: Sendcmd: Invalid command addr, " + "reject list overflow, command lost!\n", ctlr); + return 1; + } + /* Save it for later */ + srl->complete[srl->ncompletions] = complete; + srl->ncompletions++; +#endif + return 0; +} + /* * Send a command to the controller, and wait for it to complete. * Only used at init time. @@ -1580,7 +2017,7 @@ static int sendcmd( unsigned long complete; ctlr_info_t *info_p= hba[ctlr]; u64bit buff_dma_handle; - int status; + int status, done = 0; if ((c = cmd_alloc(info_p, 1)) == NULL) { printk(KERN_WARNING "cciss: unable to get memory"); @@ -1602,7 +2039,9 @@ resend_cmd1: info_p->access.set_intr_mask(info_p, CCISS_INTR_OFF); /* Make sure there is room in the command FIFO */ - /* Actually it should be completely empty at this time. */ + /* Actually it should be completely empty at this time */ + /* unless we are in here doing error handling for the scsi */ + /* tape side of the driver. */ for (i = 200000; i > 0; i--) { /* if fifo isn't full go */ @@ -1619,13 +2058,25 @@ resend_cmd1: * Send the cmd */ info_p->access.submit_command(info_p, c); - complete = pollcomplete(ctlr); + done = 0; + do { + complete = pollcomplete(ctlr); #ifdef CCISS_DEBUG - printk(KERN_DEBUG "cciss: command completed\n"); + printk(KERN_DEBUG "cciss: command completed\n"); #endif /* CCISS_DEBUG */ - if (complete != 1) { + if (complete == 1) { + printk( KERN_WARNING + "cciss cciss%d: SendCmd Timeout out, " + "No command list address returned!\n", + ctlr); + status = IO_ERROR; + done = 1; + break; + } + + /* This will need to change for direct lookup completions */ if ( (complete & CISS_ERROR_BIT) && (complete & ~CISS_ERROR_BIT) == c->busaddr) { @@ -1665,6 +2116,10 @@ resend_cmd1: status = IO_ERROR; goto cleanup1; } + } else if (c->err_info->CommandStatus == CMD_UNABORTABLE) { + printk(KERN_WARNING "cciss%d: command could not be aborted.\n", ctlr); + status = IO_ERROR; + goto cleanup1; } printk(KERN_WARNING "ciss ciss%d: sendcmd" " Error %x \n", ctlr, @@ -1679,38 +2134,40 @@ resend_cmd1: goto cleanup1; } } + /* This will need changing for direct lookup completions */ if (complete != c->busaddr) { - printk( KERN_WARNING "cciss cciss%d: SendCmd " - "Invalid command list address returned! (%lx)\n", - ctlr, complete); - status = IO_ERROR; - goto cleanup1; - } - } else { - printk( KERN_WARNING - "cciss cciss%d: SendCmd Timeout out, " - "No command list address returned!\n", - ctlr); - status = IO_ERROR; - } + if (add_sendcmd_reject(cmd, ctlr, complete) != 0) { + BUG(); /* we are pretty much hosed if we get here. */ + } + continue; + } else + done = 1; + } while (!done); cleanup1: /* unlock the data buffer from DMA */ + buff_dma_handle.val32.lower = c->SG[0].Addr.lower; + buff_dma_handle.val32.upper = c->SG[0].Addr.upper; pci_unmap_single(info_p->pdev, (dma_addr_t) buff_dma_handle.val, - size, PCI_DMA_BIDIRECTIONAL); + c->SG[0].Len, PCI_DMA_BIDIRECTIONAL); +#ifdef CONFIG_CISS_SCSI_TAPE + /* if we saved some commands for later, process them now. */ + if (info_p->scsi_rejects.ncompletions > 0) + do_cciss_intr(0, info_p, NULL); +#endif cmd_free(info_p, c, 1); return (status); } /* * Map (physical) PCI mem into (virtual) kernel space */ -static ulong remap_pci_mem(ulong base, ulong size) +static void __iomem *remap_pci_mem(ulong base, ulong size) { ulong page_base = ((ulong) base) & PAGE_MASK; ulong page_offs = ((ulong) base) - page_base; - ulong page_remapped = (ulong) ioremap(page_base, page_offs+size); + void __iomem *page_remapped = ioremap(page_base, page_offs+size); - return (ulong) (page_remapped ? (page_remapped + page_offs) : 0UL); + return page_remapped ? (page_remapped + page_offs) : NULL; } /* @@ -1729,7 +2186,7 @@ static void start_io( ctlr_info_t *h) break; } - /* Get the frist entry from the Request Q */ + /* Get the first entry from the Request Q */ removeQ(&(h->reqQ), c); h->Qdepth--; @@ -1740,20 +2197,6 @@ static void start_io( ctlr_info_t *h) addQ (&(h->cmpQ), c); } } - -static inline void complete_buffers(struct bio *bio, int status) -{ - while (bio) { - struct bio *xbh = bio->bi_next; - int nr_sectors = bio_sectors(bio); - - bio->bi_next = NULL; - blk_finished_io(len); - bio_endio(bio, nr_sectors << 9, status ? 0 : -EIO); - bio = xbh; - } - -} /* Assumes that CCISS_LOCK(h->ctlr) is held. */ /* Zeros out the error record and then resends the command back */ /* to the controller */ @@ -1770,16 +2213,16 @@ static inline void resend_cciss_cmd( ctlr_info_t *h, CommandList_struct *c) start_io(h); } + /* checks the status of the job and calls complete buffers to mark all - * buffers for the completed job. + * buffers for the completed job. Note that this function does not need + * to hold the hba/queue lock. */ static inline void complete_command( ctlr_info_t *h, CommandList_struct *cmd, int timeout) { int status = 1; - int i; int retry_cmd = 0; - u64bit temp64; if (timeout) status = 0; @@ -1887,24 +2330,11 @@ static inline void complete_command( ctlr_info_t *h, CommandList_struct *cmd, resend_cciss_cmd(h,cmd); return; } - /* command did not need to be retried */ - /* unmap the DMA mapping for all the scatter gather elements */ - for(i=0; iHeader.SGList; i++) { - temp64.val32.lower = cmd->SG[i].Addr.lower; - temp64.val32.upper = cmd->SG[i].Addr.upper; - pci_unmap_page(hba[cmd->ctlr]->pdev, - temp64.val, cmd->SG[i].Len, - (cmd->Request.Type.Direction == XFER_READ) ? - PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE); - } - complete_buffers(cmd->rq->bio, status); -#ifdef CCISS_DEBUG - printk("Done with %p\n", cmd->rq); -#endif /* CCISS_DEBUG */ - - end_that_request_last(cmd->rq); - cmd_free(h,cmd,1); + cmd->rq->completion_data = cmd; + cmd->rq->errors = status; + blk_add_trace_rq(cmd->rq->q, cmd->rq, BLK_TA_COMPLETE); + blk_complete_request(cmd->rq); } /* @@ -1921,6 +2351,9 @@ static void do_cciss_request(request_queue_t *q) drive_info_struct *drv; int i, dir; + /* We call start_io here in case there is a command waiting on the + * queue that has not been sent. + */ if (blk_queue_plugged(q)) goto startio; @@ -1929,8 +2362,7 @@ queue: if (!creq) goto startio; - if (creq->nr_phys_segments > MAXSGENTRIES) - BUG(); + BUG_ON(creq->nr_phys_segments > MAXSGENTRIES); if (( c = cmd_alloc(h, 1)) == NULL) goto full; @@ -1945,7 +2377,11 @@ queue: /* fill in the request */ drv = creq->rq_disk->private_data; c->Header.ReplyQueue = 0; // unused in simple mode - c->Header.Tag.lower = c->busaddr; // use the physical address the cmd block for tag + /* got command from pool, so use the command block index instead */ + /* for direct lookups. */ + /* The first 2 bits are reserved for controller error reporting. */ + c->Header.Tag.lower = (c->cmdindex << 3); + c->Header.Tag.lower |= 0x04; /* flag for direct lookup. */ c->Header.LUN.LogDev.VolId= drv->LunID; c->Header.LUN.LogDev.Mode = 1; c->Request.CDBLen = 10; // 12 byte commands not in FW yet; @@ -2009,35 +2445,88 @@ queue: full: blk_stop_queue(q); startio: + /* We will already have the driver lock here so not need + * to lock it. + */ start_io(h); } +static inline unsigned long get_next_completion(ctlr_info_t *h) +{ +#ifdef CONFIG_CISS_SCSI_TAPE + /* Any rejects from sendcmd() lying around? Process them first */ + if (h->scsi_rejects.ncompletions == 0) + return h->access.command_completed(h); + else { + struct sendcmd_reject_list *srl; + int n; + srl = &h->scsi_rejects; + n = --srl->ncompletions; + /* printk("cciss%d: processing saved reject\n", h->ctlr); */ + printk("p"); + return srl->complete[n]; + } +#else + return h->access.command_completed(h); +#endif +} + +static inline int interrupt_pending(ctlr_info_t *h) +{ +#ifdef CONFIG_CISS_SCSI_TAPE + return ( h->access.intr_pending(h) + || (h->scsi_rejects.ncompletions > 0)); +#else + return h->access.intr_pending(h); +#endif +} + +static inline long interrupt_not_for_us(ctlr_info_t *h) +{ +#ifdef CONFIG_CISS_SCSI_TAPE + return (((h->access.intr_pending(h) == 0) || + (h->interrupts_enabled == 0)) + && (h->scsi_rejects.ncompletions == 0)); +#else + return (((h->access.intr_pending(h) == 0) || + (h->interrupts_enabled == 0))); +#endif +} + static irqreturn_t do_cciss_intr(int irq, void *dev_id, struct pt_regs *regs) { ctlr_info_t *h = dev_id; CommandList_struct *c; unsigned long flags; - __u32 a, a1; - + __u32 a, a1, a2; + int j; + int start_queue = h->next_to_run; - /* Is this interrupt for us? */ - if (( h->access.intr_pending(h) == 0) || (h->interrupts_enabled == 0)) + if (interrupt_not_for_us(h)) return IRQ_NONE; - /* * If there are completed commands in the completion queue, * we had better do something about it. */ spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags); - while( h->access.intr_pending(h)) - { - while((a = h->access.command_completed(h)) != FIFO_EMPTY) - { + while (interrupt_pending(h)) { + while((a = get_next_completion(h)) != FIFO_EMPTY) { a1 = a; + if ((a & 0x04)) { + a2 = (a >> 3); + if (a2 >= NR_CMDS) { + printk(KERN_WARNING "cciss: controller cciss%d failed, stopping.\n", h->ctlr); + fail_all_cmds(h->ctlr); + return IRQ_HANDLED; + } + + c = h->cmd_pool + a2; + a = c->busaddr; + + } else { a &= ~3; - if ((c = h->cmpQ) == NULL) - { - printk(KERN_WARNING "cciss: Completion of %08lx ignored\n", (unsigned long)a1); + if ((c = h->cmpQ) == NULL) { + printk(KERN_WARNING "cciss: Completion of %08x ignored\n", a1); continue; } while(c->busaddr != a) { @@ -2045,6 +2534,7 @@ static irqreturn_t do_cciss_intr(int irq, void *dev_id, struct pt_regs *regs) if (c == h->cmpQ) break; } + } /* * If we've found the command, take it off the * completion Q and free it @@ -2065,10 +2555,46 @@ static irqreturn_t do_cciss_intr(int irq, void *dev_id, struct pt_regs *regs) } } - /* - * See if we can queue up some more IO + /* check to see if we have maxed out the number of commands that can + * be placed on the queue. If so then exit. We do this check here + * in case the interrupt we serviced was from an ioctl and did not + * free any new commands. */ - blk_start_queue(h->queue); + if ((find_first_zero_bit(h->cmd_pool_bits, NR_CMDS)) == NR_CMDS) + goto cleanup; + + /* We have room on the queue for more commands. Now we need to queue + * them up. We will also keep track of the next queue to run so + * that every queue gets a chance to be started first. + */ + for (j=0; j < h->highest_lun + 1; j++){ + int curr_queue = (start_queue + j) % (h->highest_lun + 1); + /* make sure the disk has been added and the drive is real + * because this can be called from the middle of init_one. + */ + if(!(h->drv[curr_queue].queue) || + !(h->drv[curr_queue].heads)) + continue; + blk_start_queue(h->gendisk[curr_queue]->queue); + + /* check to see if we have maxed out the number of commands + * that can be placed on the queue. + */ + if ((find_first_zero_bit(h->cmd_pool_bits, NR_CMDS)) == NR_CMDS) + { + if (curr_queue == start_queue){ + h->next_to_run = (start_queue + 1) % (h->highest_lun + 1); + goto cleanup; + } else { + h->next_to_run = curr_queue; + goto cleanup; + } + } else { + curr_queue = (curr_queue + 1) % (h->highest_lun + 1); + } + } + +cleanup: spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags); return IRQ_HANDLED; } @@ -2157,10 +2683,63 @@ static int find_PCI_BAR_index(struct pci_dev *pdev, return -1; } +/* If MSI/MSI-X is supported by the kernel we will try to enable it on + * controllers that are capable. If not, we use IO-APIC mode. + */ + +static void __devinit cciss_interrupt_mode(ctlr_info_t *c, struct pci_dev *pdev, __u32 board_id) +{ +#ifdef CONFIG_PCI_MSI + int err; + struct msix_entry cciss_msix_entries[4] = {{0,0}, {0,1}, + {0,2}, {0,3}}; + + /* Some boards advertise MSI but don't really support it */ + if ((board_id == 0x40700E11) || + (board_id == 0x40800E11) || + (board_id == 0x40820E11) || + (board_id == 0x40830E11)) + goto default_int_mode; + + if (pci_find_capability(pdev, PCI_CAP_ID_MSIX)) { + err = pci_enable_msix(pdev, cciss_msix_entries, 4); + if (!err) { + c->intr[0] = cciss_msix_entries[0].vector; + c->intr[1] = cciss_msix_entries[1].vector; + c->intr[2] = cciss_msix_entries[2].vector; + c->intr[3] = cciss_msix_entries[3].vector; + c->msix_vector = 1; + return; + } + if (err > 0) { + printk(KERN_WARNING "cciss: only %d MSI-X vectors " + "available\n", err); + } else { + printk(KERN_WARNING "cciss: MSI-X init failed %d\n", + err); + } + } + if (pci_find_capability(pdev, PCI_CAP_ID_MSI)) { + if (!pci_enable_msi(pdev)) { + c->intr[SIMPLE_MODE_INT] = pdev->irq; + c->msi_vector = 1; + return; + } else { + printk(KERN_WARNING "cciss: MSI init failed\n"); + c->intr[SIMPLE_MODE_INT] = pdev->irq; + return; + } + } +default_int_mode: +#endif /* CONFIG_PCI_MSI */ + /* if we get here we're going to use the default interrupt mode */ + c->intr[SIMPLE_MODE_INT] = pdev->irq; + return; +} + static int cciss_pci_init(ctlr_info_t *c, struct pci_dev *pdev) { ushort subsystem_vendor_id, subsystem_device_id, command; - unchar irq = pdev->irq; __u32 board_id, scratchpad = 0; __u64 cfg_offset; __u32 cfg_base_addr; @@ -2181,11 +2760,6 @@ static int cciss_pci_init(ctlr_info_t *c, struct pci_dev *pdev) printk(KERN_ERR "cciss: Unable to Enable PCI device\n"); return( -1); } - if (pci_set_dma_mask(pdev, CCISS_DMA_MASK ) != 0) - { - printk(KERN_ERR "cciss: Unable to set DMA mask\n"); - return(-1); - } subsystem_vendor_id = pdev->subsystem_vendor; subsystem_device_id = pdev->subsystem_device; @@ -2219,11 +2793,14 @@ static int cciss_pci_init(ctlr_info_t *c, struct pci_dev *pdev) #ifdef CCISS_DEBUG printk("command = %x\n", command); - printk("irq = %x\n", irq); + printk("irq = %x\n", pdev->irq); printk("board_id = %x\n", board_id); #endif /* CCISS_DEBUG */ - c->intr = irq; +/* If the kernel supports MSI/MSI-X we will try to enable that functionality, + * else we use the IO-APIC interrupt assigned to us by system ROM. + */ + cciss_interrupt_mode(c, pdev, board_id); /* * Memory base addr is first addr , the second points to the config @@ -2271,13 +2848,13 @@ static int cciss_pci_init(ctlr_info_t *c, struct pci_dev *pdev) #ifdef CCISS_DEBUG printk("cfg offset = %x\n", cfg_offset); #endif /* CCISS_DEBUG */ - c->cfgtable = (CfgTable_struct *) - remap_pci_mem(pci_resource_start(pdev, cfg_base_addr_index) - + cfg_offset, sizeof(CfgTable_struct)); + c->cfgtable = remap_pci_mem(pci_resource_start(pdev, + cfg_base_addr_index) + cfg_offset, + sizeof(CfgTable_struct)); c->board_id = board_id; #ifdef CCISS_DEBUG - print_cfg_table(c->cfgtable); + print_cfg_table(c->cfgtable); #endif /* CCISS_DEBUG */ for(i=0; ihighest_lun = hba[cntl_num]->num_luns-1; - for(i=0; i< hba[cntl_num]->num_luns; i++) +// for(i=0; i< hba[cntl_num]->num_luns; i++) + for(i=0; i < CISS_MAX_LUN; i++) { - - 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; + 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; @@ -2447,13 +3027,18 @@ static void cciss_getgeometry(int cntl_num) #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); + 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 */ cciss_read_capacity(cntl_num, i, size_buff, 0, &total_size, &block_size); - cciss_geometry_inquiry(cntl_num, i, 0, total_size, block_size, - inq_buff, &hba[cntl_num]->drv[i]); + 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(size_buff); @@ -2475,10 +3060,9 @@ static int alloc_cciss_hba(void) for(i=0; i< MAX_CTLR; i++) { if (!hba[i]) { ctlr_info_t *p; - p = kmalloc(sizeof(ctlr_info_t), GFP_KERNEL); + p = kzalloc(sizeof(ctlr_info_t), GFP_KERNEL); if (!p) goto Enomem; - memset(p, 0, sizeof(ctlr_info_t)); for (n = 0; n < NWD; n++) p->gendisk[n] = disk[n]; hba[i] = p; @@ -2486,7 +3070,7 @@ static int alloc_cciss_hba(void) } } printk(KERN_WARNING "cciss: This driver supports a maximum" - " of 8 controllers.\n"); + " of %d controllers.\n", MAX_CTLR); goto out; Enomem: printk(KERN_ERR "cciss: out of memory.\n"); @@ -2518,14 +3102,18 @@ static int __devinit cciss_init_one(struct pci_dev *pdev, request_queue_t *q; int i; int j; + int rc; printk(KERN_DEBUG "cciss: Device 0x%x has been found at" " bus %d dev %d func %d\n", pdev->device, pdev->bus->number, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); i = alloc_cciss_hba(); - if( i < 0 ) + if(i < 0) return (-1); + + hba[i]->busy_initializing = 1; + if (cciss_pci_init(hba[i], pdev) != 0) goto clean1; @@ -2534,28 +3122,41 @@ static int __devinit cciss_init_one(struct pci_dev *pdev, hba[i]->pdev = pdev; /* configure PCI DMA stuff */ - if (!pci_set_dma_mask(pdev, 0xffffffffffffffffULL)) + if (!pci_set_dma_mask(pdev, DMA_64BIT_MASK)) printk("cciss: using DAC cycles\n"); - else if (!pci_set_dma_mask(pdev, 0xffffffff)) + else if (!pci_set_dma_mask(pdev, DMA_32BIT_MASK)) printk("cciss: not using DAC cycles\n"); else { printk("cciss: no suitable DMA available\n"); goto clean1; } - if (register_blkdev(COMPAQ_CISS_MAJOR+i, hba[i]->devname)) { - printk(KERN_ERR "cciss: Unable to register device %s\n", - hba[i]->devname); + /* + * register with the major number, or get a dynamic major number + * by passing 0 as argument. This is done for greater than + * 8 controller support. + */ + if (i < MAX_CTLR_ORIG) + hba[i]->major = COMPAQ_CISS_MAJOR + i; + rc = register_blkdev(hba[i]->major, hba[i]->devname); + if(rc == -EBUSY || rc == -EINVAL) { + printk(KERN_ERR + "cciss: Unable to get major number %d for %s " + "on hba %d\n", hba[i]->major, hba[i]->devname, i); goto clean1; } + else { + if (i >= MAX_CTLR_ORIG) + hba[i]->major = rc; + } /* make sure the board interrupts are off */ hba[i]->access.set_intr_mask(hba[i], CCISS_INTR_OFF); - if( request_irq(hba[i]->intr, do_cciss_intr, + if( request_irq(hba[i]->intr[SIMPLE_MODE_INT], do_cciss_intr, SA_INTERRUPT | SA_SHIRQ | SA_SAMPLE_RANDOM, hba[i]->devname, hba[i])) { printk(KERN_ERR "cciss: Unable to get irq %d for %s\n", - hba[i]->intr, hba[i]->devname); + hba[i]->intr[SIMPLE_MODE_INT], hba[i]->devname); goto clean2; } hba[i]->cmd_pool_bits = kmalloc(((NR_CMDS+BITS_PER_LONG-1)/BITS_PER_LONG)*sizeof(unsigned long), GFP_KERNEL); @@ -2571,15 +3172,16 @@ static int __devinit cciss_init_one(struct pci_dev *pdev, printk( KERN_ERR "cciss: out of memory"); goto clean4; } - - spin_lock_init(&hba[i]->lock); - q = blk_init_queue(do_cciss_request, &hba[i]->lock); - if (!q) +#ifdef CONFIG_CISS_SCSI_TAPE + hba[i]->scsi_rejects.complete = + kmalloc(sizeof(hba[i]->scsi_rejects.complete[0]) * + (NR_CMDS + 5), GFP_KERNEL); + if (hba[i]->scsi_rejects.complete == NULL) { + printk( KERN_ERR "cciss: out of memory"); goto clean4; - - q->backing_dev_info.ra_pages = READ_AHEAD; - hba[i]->queue = q; - q->queuedata = hba[i]; + } +#endif + spin_lock_init(&hba[i]->lock); /* Initialize the pdev driver private data. have it point to hba[i]. */ @@ -2600,40 +3202,58 @@ static int __devinit cciss_init_one(struct pci_dev *pdev, hba[i]->access.set_intr_mask(hba[i], CCISS_INTR_ON); cciss_procinit(i); + hba[i]->busy_initializing = 0; - blk_queue_bounce_limit(q, hba[i]->pdev->dma_mask); + for(j=0; j < NWD; j++) { /* mfm */ + drive_info_struct *drv = &(hba[i]->drv[j]); + struct gendisk *disk = hba[i]->gendisk[j]; - /* This is a hardware imposed limit. */ - blk_queue_max_hw_segments(q, MAXSGENTRIES); + 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; + } + drv->queue = q; - /* This is a limit in the driver and could be eliminated. */ - blk_queue_max_phys_segments(q, MAXSGENTRIES); + q->backing_dev_info.ra_pages = READ_AHEAD; + blk_queue_bounce_limit(q, hba[i]->pdev->dma_mask); - blk_queue_max_sectors(q, 512); + /* 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); - for(j=0; jdrv[j]); - struct gendisk *disk = hba[i]->gendisk[j]; + blk_queue_max_sectors(q, 512); + + blk_queue_softirq_done(q, cciss_softirq_done); + q->queuedata = hba[i]; sprintf(disk->disk_name, "cciss/c%dd%d", i, j); sprintf(disk->devfs_name, "cciss/host%d/target%d", i, j); - disk->major = COMPAQ_CISS_MAJOR + i; + disk->major = hba[i]->major; disk->first_minor = j << NWD_SHIFT; disk->fops = &cciss_fops; - disk->queue = hba[i]->queue; + disk->queue = q; disk->private_data = drv; - if( !(drv->nr_blocks)) + /* 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(hba[i]->queue, drv->block_size); + blk_queue_hardsect_size(q, drv->block_size); set_capacity(disk, drv->nr_blocks); add_disk(disk); } + return(1); clean4: - if(hba[i]->cmd_pool_bits) - kfree(hba[i]->cmd_pool_bits); +#ifdef CONFIG_CISS_SCSI_TAPE + kfree(hba[i]->scsi_rejects.complete); +#endif + kfree(hba[i]->cmd_pool_bits); if(hba[i]->cmd_pool) pci_free_consistent(hba[i]->pdev, NR_CMDS * sizeof(CommandList_struct), @@ -2643,11 +3263,12 @@ clean4: NR_CMDS * sizeof( ErrorInfo_struct), hba[i]->errinfo_pool, hba[i]->errinfo_pool_dhandle); - free_irq(hba[i]->intr, hba[i]); + free_irq(hba[i]->intr[SIMPLE_MODE_INT], hba[i]); clean2: - unregister_blkdev(COMPAQ_CISS_MAJOR+i, hba[i]->devname); + unregister_blkdev(hba[i]->major, hba[i]->devname); clean1: release_io_mem(hba[i]); + hba[i]->busy_initializing = 0; free_hba(i); return(-1); } @@ -2683,26 +3304,42 @@ static void __devexit cciss_remove_one (struct pci_dev *pdev) printk(KERN_WARNING "Error Flushing cache on controller %d\n", i); } - free_irq(hba[i]->intr, hba[i]); + free_irq(hba[i]->intr[2], hba[i]); + +#ifdef CONFIG_PCI_MSI + if (hba[i]->msix_vector) + pci_disable_msix(hba[i]->pdev); + else if (hba[i]->msi_vector) + pci_disable_msi(hba[i]->pdev); +#endif /* CONFIG_PCI_MSI */ + pci_set_drvdata(pdev, NULL); - iounmap((void*)hba[i]->vaddr); + iounmap(hba[i]->vaddr); cciss_unregister_scsi(i); /* unhook from SCSI subsystem */ - unregister_blkdev(COMPAQ_CISS_MAJOR+i, hba[i]->devname); + unregister_blkdev(hba[i]->major, hba[i]->devname); remove_proc_entry(hba[i]->devname, proc_cciss); /* remove it from the disk list */ for (j = 0; j < NWD; j++) { struct gendisk *disk = hba[i]->gendisk[j]; - if (disk->flags & GENHD_FL_UP) - del_gendisk(disk); + if (disk) { + request_queue_t *q = disk->queue; + + if (disk->flags & GENHD_FL_UP) + del_gendisk(disk); + if (q) + blk_cleanup_queue(q); + } } - blk_cleanup_queue(hba[i]->queue); pci_free_consistent(hba[i]->pdev, NR_CMDS * sizeof(CommandList_struct), hba[i]->cmd_pool, hba[i]->cmd_pool_dhandle); pci_free_consistent(hba[i]->pdev, NR_CMDS * sizeof( ErrorInfo_struct), hba[i]->errinfo_pool, hba[i]->errinfo_pool_dhandle); kfree(hba[i]->cmd_pool_bits); +#ifdef CONFIG_CISS_SCSI_TAPE + kfree(hba[i]->scsi_rejects.complete); +#endif release_io_mem(hba[i]); free_hba(i); } @@ -2718,20 +3355,15 @@ static struct pci_driver cciss_pci_driver = { * This is it. Register the PCI driver information for the cards we control * the OS will call our registered routines when it finds one of our cards. */ -int __init cciss_init(void) +static int __init cciss_init(void) { printk(KERN_INFO DRIVER_NAME "\n"); /* Register for our PCI devices */ - return pci_module_init(&cciss_pci_driver); + return pci_register_driver(&cciss_pci_driver); } -static int __init init_cciss_module(void) -{ - return ( cciss_init()); -} - -static void __exit cleanup_cciss_module(void) +static void __exit cciss_cleanup(void) { int i; @@ -2749,5 +3381,43 @@ static void __exit cleanup_cciss_module(void) remove_proc_entry("cciss", proc_root_driver); } -module_init(init_cciss_module); -module_exit(cleanup_cciss_module); +static void fail_all_cmds(unsigned long ctlr) +{ + /* If we get here, the board is apparently dead. */ + ctlr_info_t *h = hba[ctlr]; + CommandList_struct *c; + unsigned long flags; + + printk(KERN_WARNING "cciss%d: controller not responding.\n", h->ctlr); + h->alive = 0; /* the controller apparently died... */ + + spin_lock_irqsave(CCISS_LOCK(ctlr), flags); + + pci_disable_device(h->pdev); /* Make sure it is really dead. */ + + /* move everything off the request queue onto the completed queue */ + while( (c = h->reqQ) != NULL ) { + removeQ(&(h->reqQ), c); + h->Qdepth--; + addQ (&(h->cmpQ), c); + } + + /* Now, fail everything on the completed queue with a HW error */ + while( (c = h->cmpQ) != NULL ) { + removeQ(&h->cmpQ, c); + c->err_info->CommandStatus = CMD_HARDWARE_ERR; + if (c->cmd_type == CMD_RWREQ) { + complete_command(h, c, 0); + } else if (c->cmd_type == CMD_IOCTL_PEND) + complete(c->waiting); +#ifdef CONFIG_CISS_SCSI_TAPE + else if (c->cmd_type == CMD_SCSI) + complete_scsi_command(c, 0, 0); +#endif + } + spin_unlock_irqrestore(CCISS_LOCK(ctlr), flags); + return; +} + +module_init(cciss_init); +module_exit(cciss_cleanup);