upgrade to fedora-2.6.12-1.1398.FC4 + vserver 2.0.rc7
[linux-2.6.git] / drivers / block / cciss.c
index 0185655..0916a7a 100644 (file)
@@ -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
  *
  */
 
 #include <linux/completion.h>
 
 #define CCISS_DRIVER_VERSION(maj,min,submin) ((maj<<16)|(min<<8)|(submin))
-#define DRIVER_NAME "HP CISS Driver (v 2.6.4)"
-#define DRIVER_VERSION CCISS_DRIVER_VERSION(2,6,4)
+#define DRIVER_NAME "HP CISS Driver (v 2.6.6)"
+#define DRIVER_VERSION CCISS_DRIVER_VERSION(2,6,6)
 
 /* Embedded module documentation macros - see modules.h */
 MODULE_AUTHOR("Hewlett-Packard Company");
-MODULE_DESCRIPTION("Driver for HP Controller SA5xxx SA6xxx version 2.6.4");
-MODULE_VERSION("2.6.4");
+MODULE_DESCRIPTION("Driver for HP Controller SA5xxx SA6xxx version 2.6.6");
 MODULE_SUPPORTED_DEVICE("HP SA5i SA5i+ SA532 SA5300 SA5312 SA641 SA642 SA6400"
-                       " SA6i P600");
+                       " SA6i P600 P800 E400");
+MODULE_VERSION("2.6.6");
 MODULE_LICENSE("GPL");
 
 #include "cciss_cmd.h"
@@ -62,7 +62,7 @@ MODULE_LICENSE("GPL");
 #include <linux/cciss_ioctl.h>
 
 /* 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,
@@ -83,6 +83,10 @@ const struct pci_device_id cciss_pci_device_id[] = {
                0x0E11, 0x4091, 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_CISSB,
+               0x103c, 0x3223, 0, 0, 0},
+       { PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSB,
+               0x103c, 0x3231, 0, 0, 0},
        {0,}
 };
 MODULE_DEVICE_TABLE(pci, cciss_pci_device_id);
@@ -104,6 +108,8 @@ static struct board_type products[] = {
        { 0x409D0E11, "Smart Array 6400 EM", &SA5_access},
        { 0x40910E11, "Smart Array 6i", &SA5_access},
        { 0x3225103C, "Smart Array P600", &SA5_access},
+       { 0x3223103C, "Smart Array P800", &SA5_access},
+       { 0x3231103C, "Smart Array E400", &SA5_access},
 };
 
 /* How long to wait (in millesconds) for board to go into simple mode */
@@ -115,7 +121,11 @@ static struct board_type products[] = {
 
 #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 */
 
@@ -147,11 +157,18 @@ 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,
+#ifdef CONFIG_COMPAT
+       .compat_ioctl   = cciss_compat_ioctl,
+#endif
        .revalidate_disk= cciss_revalidate,
 };
 
@@ -288,7 +305,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) {
@@ -478,80 +495,50 @@ static int cciss_release(struct inode *inode, struct file *filep)
 }
 
 #ifdef CONFIG_COMPAT
-/* for AMD 64 bit kernel compatibility with 32-bit userland ioctls */
-extern long sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg);
-extern int
-register_ioctl32_conversion(unsigned int cmd, int (*handler)(unsigned int,
-      unsigned int, unsigned long, struct file *));
-extern int unregister_ioctl32_conversion(unsigned int cmd);
-
-static int cciss_ioctl32_passthru(unsigned int fd, unsigned cmd, unsigned long arg, struct file *file);
-static int cciss_ioctl32_big_passthru(unsigned int fd, unsigned cmd, unsigned long arg,
-       struct file *file);
-
-typedef int (*handler_type) (unsigned int, unsigned int, unsigned long, struct file *);
-
-static struct ioctl32_map {
-       unsigned int cmd;
-       handler_type handler;
-       int registered;
-} cciss_ioctl32_map[] = {
-       { CCISS_GETPCIINFO,     (handler_type) sys_ioctl, 0 },
-       { CCISS_GETINTINFO,     (handler_type) sys_ioctl, 0 },
-       { CCISS_SETINTINFO,     (handler_type) sys_ioctl, 0 },
-       { CCISS_GETNODENAME,    (handler_type) sys_ioctl, 0 },
-       { CCISS_SETNODENAME,    (handler_type) sys_ioctl, 0 },
-       { CCISS_GETHEARTBEAT,   (handler_type) sys_ioctl, 0 },
-       { CCISS_GETBUSTYPES,    (handler_type) sys_ioctl, 0 },
-       { CCISS_GETFIRMVER,     (handler_type) sys_ioctl, 0 },
-       { CCISS_GETDRIVVER,     (handler_type) sys_ioctl, 0 },
-       { CCISS_REVALIDVOLS,    (handler_type) sys_ioctl, 0 },
-       { CCISS_PASSTHRU32,     cciss_ioctl32_passthru, 0 },
-       { CCISS_DEREGDISK,      (handler_type) sys_ioctl, 0 },
-       { CCISS_REGNEWDISK,     (handler_type) sys_ioctl, 0 },
-       { CCISS_REGNEWD,        (handler_type) sys_ioctl, 0 },
-       { CCISS_RESCANDISK,     (handler_type) sys_ioctl, 0 },
-       { CCISS_GETLUNINFO,     (handler_type) sys_ioctl, 0 },
-       { CCISS_BIG_PASSTHRU32, cciss_ioctl32_big_passthru, 0 },
-};
-#define NCCISS_IOCTL32_ENTRIES (sizeof(cciss_ioctl32_map) / sizeof(cciss_ioctl32_map[0]))
-static void register_cciss_ioctl32(void)
+
+static int do_ioctl(struct file *f, unsigned cmd, unsigned long arg)
 {
-       int i, rc;
-
-       for (i=0; i < NCCISS_IOCTL32_ENTRIES; i++) {
-               rc = register_ioctl32_conversion(
-                       cciss_ioctl32_map[i].cmd,
-                       cciss_ioctl32_map[i].handler);
-               if (rc != 0) {
-                       printk(KERN_WARNING "cciss: failed to register "
-                               "32 bit compatible ioctl 0x%08x\n",
-                               cciss_ioctl32_map[i].cmd);
-                       cciss_ioctl32_map[i].registered = 0;
-               } else
-                       cciss_ioctl32_map[i].registered = 1;
-       }
+       int ret;
+       lock_kernel();
+       ret = cciss_ioctl(f->f_dentry->d_inode, f, cmd, arg);
+       unlock_kernel();
+       return ret;
 }
-static void unregister_cciss_ioctl32(void)
+
+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)
 {
-       int i, rc;
+       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);
 
-       for (i=0; i < NCCISS_IOCTL32_ENTRIES; i++) {
-               if (!cciss_ioctl32_map[i].registered)
-                       continue;
-               rc = unregister_ioctl32_conversion(
-                       cciss_ioctl32_map[i].cmd);
-               if (rc == 0) {
-                       cciss_ioctl32_map[i].registered = 0;
-                       continue;
-               }
-               printk(KERN_WARNING "cciss: failed to unregister "
-                       "32 bit compatible ioctl 0x%08x\n",
-                       cciss_ioctl32_map[i].cmd);
+       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;
        }
 }
-int cciss_ioctl32_passthru(unsigned int fd, unsigned cmd, unsigned long arg,
-       struct file *file)
+
+static int cciss_ioctl32_passthru(struct file *f, unsigned cmd, unsigned long arg)
 {
        IOCTL32_Command_struct __user *arg32 =
                (IOCTL32_Command_struct __user *) arg;
@@ -572,7 +559,7 @@ int cciss_ioctl32_passthru(unsigned int fd, unsigned cmd, unsigned long arg,
        if (err)
                return -EFAULT;
 
-       err = sys_ioctl(fd, CCISS_PASSTHRU, (unsigned long) p);
+       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));
@@ -581,8 +568,7 @@ int cciss_ioctl32_passthru(unsigned int fd, unsigned cmd, unsigned long arg,
        return err;
 }
 
-int cciss_ioctl32_big_passthru(unsigned int fd, unsigned cmd, unsigned long arg,
-       struct file *file)
+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;
@@ -604,7 +590,7 @@ int cciss_ioctl32_big_passthru(unsigned int fd, unsigned cmd, unsigned long arg,
        if (err)
                 return -EFAULT;
 
-       err = sys_ioctl(fd, CCISS_BIG_PASSTHRU, (unsigned long) p);
+       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));
@@ -612,9 +598,6 @@ int cciss_ioctl32_big_passthru(unsigned int fd, unsigned cmd, unsigned long arg,
                return -EFAULT;
        return err;
 }
-#else
-static inline void register_cciss_ioctl32(void) {}
-static inline void unregister_cciss_ioctl32(void) {}
 #endif
 /*
  * ioctl 
@@ -2108,6 +2091,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;
 
@@ -2196,6 +2182,9 @@ queue:
 full:
        blk_stop_queue(q);
 startio:
+       /* We will already have the driver lock here so not need
+        * to lock it.
+       */
        start_io(h);
 }
 
@@ -2205,7 +2194,8 @@ static irqreturn_t do_cciss_intr(int irq, void *dev_id, struct pt_regs *regs)
        CommandList_struct *c;
        unsigned long flags;
        __u32 a, a1;
-
+       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))
@@ -2252,13 +2242,50 @@ 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 < NWD; j++){
+               int curr_queue = (start_queue + j) % NWD;
+               /* 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->gendisk[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) % NWD;
+                               goto cleanup;
+                       } else {
+                               h->next_to_run = curr_queue;
+                               goto cleanup;
+       }
+               } else {
+                       curr_queue = (curr_queue + 1) % NWD;
+               }
+       }
+
+cleanup:
        spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
        return IRQ_HANDLED;
 }
+
 /* 
  *  We cannot read the structure directly, for portablity we must use 
  *   the io functions.
@@ -2672,7 +2699,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");
@@ -2704,13 +2731,14 @@ 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);
        if (cciss_pci_init(hba[i], pdev) != 0)
                goto clean1;
@@ -2729,11 +2757,24 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
                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 = MAJOR_NR + 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);
@@ -2804,7 +2845,7 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
 
                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;
@@ -2833,7 +2874,7 @@ clean4:
                        hba[i]->errinfo_pool_dhandle);
        free_irq(hba[i]->intr, 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]);
        free_hba(i);
@@ -2875,7 +2916,7 @@ static void __devexit cciss_remove_one (struct pci_dev *pdev)
        pci_set_drvdata(pdev, NULL);
        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 */
@@ -2906,7 +2947,7 @@ 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");
 
@@ -2914,17 +2955,10 @@ int __init cciss_init(void)
        return pci_module_init(&cciss_pci_driver);
 }
 
-static int __init init_cciss_module(void)
-{
-       register_cciss_ioctl32();
-       return ( cciss_init());
-}
-
-static void __exit cleanup_cciss_module(void)
+static void __exit cciss_cleanup(void)
 {
        int i;
 
-       unregister_cciss_ioctl32();
        pci_unregister_driver(&cciss_pci_driver);
        /* double check that all controller entrys have been removed */
        for (i=0; i< MAX_CTLR; i++) 
@@ -2939,5 +2973,5 @@ static void __exit cleanup_cciss_module(void)
        remove_proc_entry("cciss", proc_root_driver);
 }
 
-module_init(init_cciss_module);
-module_exit(cleanup_cciss_module);
+module_init(cciss_init);
+module_exit(cciss_cleanup);