From: Mike Miller This creates a new function, cciss_interrupt_mode called from cciss_pci_init. This function determines what type of interrupt vector to use, i.e., MSI, MSI-X, or IO-APIC. One noticeable difference is changing the interrupt field of the controller struct to an array of 4 unsigned ints. The Smart Array HW is capable of generating 4 distinct interrupts depending on the transport method in use during operation. These are: #define DOORBELL_INT 0 Used to notify the contoller of configuration updates. We only use this feature when in polling mode. #define PERF_MODE_INT 0 Used when the controller is in Performant Mode. #define SIMPLE_MODE_INT 2 Used when the controller is in Simple Mode (current Linux implementation). #define MEMQ_INT_MODE 3 Not used. When using IO-APIC interrupts these 4 lines are OR'ed together so when any one fires an interrupt an is generated. In MSI or MSI-X mode this hardware OR'ing is ignored. We must register for our interrupt depending on what mode the controller is running. For Linux we use SIMPLE_MODE_INT exclusively at this time. Please consider this for inclusion. Signed-off-by: Mike Miller Cc: Jens Axboe Signed-off-by: Andrew Morton --- drivers/block/cciss.c | 79 +++++++++++++++++++++++++++++++++++++++++---- drivers/block/cciss.h | 8 +++- drivers/block/cciss_scsi.c | 2 - 3 files changed, 80 insertions(+), 9 deletions(-) diff -puN drivers/block/cciss.c~cciss-adds-msi-and-msi-x-support drivers/block/cciss.c --- devel/drivers/block/cciss.c~cciss-adds-msi-and-msi-x-support 2005-12-22 05:09:26.000000000 -0800 +++ devel-akpm/drivers/block/cciss.c 2005-12-22 05:09:26.000000000 -0800 @@ -167,7 +167,7 @@ static void cciss_geometry_inquiry(int c 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, @@ -284,7 +284,7 @@ static int cciss_proc_get_info(char *buf 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); @@ -2662,6 +2662,60 @@ static int find_PCI_BAR_index(struct pci 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; + } + } +#endif /* CONFIG_PCI_MSI */ + /* if we get here we're going to use the default interrupt mode */ +default_int_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; @@ -2722,7 +2776,10 @@ static int cciss_pci_init(ctlr_info_t *c printk("board_id = %x\n", board_id); #endif /* CCISS_DEBUG */ - c->intr = pdev->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 @@ -3076,11 +3133,11 @@ static int __devinit cciss_init_one(stru /* 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); @@ -3185,7 +3242,7 @@ 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(hba[i]->major, hba[i]->devname); clean1: @@ -3226,7 +3283,15 @@ static void __devexit cciss_remove_one ( 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(hba[i]->vaddr); cciss_unregister_scsi(i); /* unhook from SCSI subsystem */ diff -puN drivers/block/cciss.h~cciss-adds-msi-and-msi-x-support drivers/block/cciss.h --- devel/drivers/block/cciss.h~cciss-adds-msi-and-msi-x-support 2005-12-22 05:09:26.000000000 -0800 +++ devel-akpm/drivers/block/cciss.h 2005-12-22 05:09:26.000000000 -0800 @@ -65,7 +65,6 @@ struct ctlr_info unsigned long io_mem_addr; unsigned long io_mem_length; CfgTable_struct __iomem *cfgtable; - unsigned int intr; int interrupts_enabled; int major; int max_commands; @@ -74,6 +73,13 @@ struct ctlr_info int num_luns; int highest_lun; int usage_count; /* number of opens all all minor devices */ +# define DOORBELL_INT 0 +# define PERF_MODE_INT 1 +# define SIMPLE_MODE_INT 2 +# define MEMQ_MODE_INT 3 + unsigned int intr[4]; + unsigned int msix_vector; + unsigned int msi_vector; // information about each logical volume drive_info_struct drv[CISS_MAX_LUN]; diff -puN drivers/block/cciss_scsi.c~cciss-adds-msi-and-msi-x-support drivers/block/cciss_scsi.c --- devel/drivers/block/cciss_scsi.c~cciss-adds-msi-and-msi-x-support 2005-12-22 05:09:26.000000000 -0800 +++ devel-akpm/drivers/block/cciss_scsi.c 2005-12-22 05:09:26.000000000 -0800 @@ -714,7 +714,7 @@ cciss_scsi_detect(int ctlr) ((struct cciss_scsi_adapter_data_t *) hba[ctlr]->scsi_ctlr)->scsi_host = (void *) sh; sh->hostdata[0] = (unsigned long) hba[ctlr]; - sh->irq = hba[ctlr]->intr; + sh->irq = hba[ctlr]->intr[SIMPLE_MODE_INT]; sh->unique_id = sh->irq; error = scsi_add_host(sh, &hba[ctlr]->pdev->dev); if (error) _