hpsa driver updates June 16, 2010
Subsystems (2)
cover hpsa driver updates June 16, 2010
The following series implements support for new controllers, allows the "hpsa_allow_any=1" boot parameter to enable the driver to work with old Compaq branded controllers, fixes a problem with the hard-reset of newer controllers which was preventing kdump from working (except 640x, p800), provides separate interrupt handler functions for intx and msi/msix boards for efficiency on the msi/msix supporting boards, and prevents the the driver from attempting to allocate a negative amount of memory in the event the controller claims to support zero commands (guard against buggy firmware.)
---
Stephen M. Cameron (9):
hpsa: add new controllers
hpsa: Make "hpsa_allow_any=1" boot param enable Compaq Smart Arrays.
hpsa: make hpsa_find_memory_BAR not require the per HBA structure.
hpsa: factor out hpsa_find_cfg_addrs.
hpsa: factor out the code to reset controllers on driver load
hpsa: Fix hard reset code.
hpsa: forbid hard reset of 640x boards
hpsa: separate intx and msi/msix interrupt handlers
hpsa: sanitize max commands
drivers/scsi/hpsa.c | 346 ++++++++++++++++++++++++++++++++++++----------- drivers/scsi/hpsa_cmd.h | 4 + 2 files changed, 266 insertions(+), 84 deletions(-)
Signature · 34 lines
This series of patches depends on a bunch of patches that were sent to linux-scsi back in May, but do not currently appear to be in anyone's tree so far that I can see. The patches below are the ones the above patches depend on. (If it would be better to resend all the patches at once, I can do that.) 1. 2010-06-11 [PATCH] hpsa: fix use of uninitialized variable in linux-scs Stephen M. Cameron ... 7. 2010-05-27 [PATCH 24/24] hpsa: add entry to MAINTAINERS linux-scs Stephen M. Cameron 8. 2010-05-27 [PATCH 23/24] hpsa: fix block fetch table problem. linux-scs Stephen M. Cameron 9. 2010-05-27 [PATCH 22/24] hpsa: expose controller firmware revisi linux-scs Stephen M. Cameron 10. 2010-05-27 [PATCH 21/24] hpsa: Add hpsa.txt to Documentation/scs linux-scs Stephen M. Cameron 11. 2010-05-27 [PATCH 20/24] hpsa: remove unused firm_ver member of linux-scs Stephen M. Cameron 12. 2010-05-27 [PATCH 19/24] hpsa: factor out hpsa_enter_performant_ linux-scs Stephen M. Cameron 13. 2010-05-27 [PATCH 18/24] hpsa: remove unused variable trans_offs linux-scs Stephen M. Cameron 14. 2010-05-27 [PATCH 17/24] hpsa: factor out hpsa_wait_for_mode_cha linux-scs Stephen M. Cameron 15. 2010-05-27 [PATCH 16/24] hpsa: mark hpsa_mark_hpsa_put_ctlr_into linux-scs Stephen M. Cameron 16. 2010-05-27 [PATCH 15/24] hpsa: clean up debug ifdefs linux-scs Stephen M. Cameron 17. 2010-05-27 [PATCH 14/24] hpsa: check that simple mode is support linux-scs Stephen M. Cameron 18. 2010-05-27 [PATCH 13/24] hpsa: factor out hpsa_enter_simple_mode linux-scs Stephen M. Cameron 19. 2010-05-27 [PATCH 12/24] hpsa: factor out hpsa_p600_dma_prefetch linux-scs Stephen M. Cameron 20. 2010-05-27 [PATCH 11/24] hpsa: factor out hpsa_enable_scsi_prefe linux-scs Stephen M. Cameron 21. 2010-05-27 [PATCH 10/24] hpsa: factor out hpsa-CISS-signature-pr linux-scs Stephen M. Cameron 22. 2010-05-27 [PATCH 09/24] hpsa: hpsa factor out hpsa_find_board_p linux-scs Stephen M. Cameron 23. 2010-05-27 [PATCH 08/24] hpsa: fix leak of ioremapped memory linux-scs Stephen M. Cameron 24. 2010-05-27 [PATCH 07/24] hpsa: factor out hpsa_find_cfgtables linux-scs Stephen M. Cameron 25. 2010-05-27 [PATCH 06/24] hpsa: factor out hpsa_wait_for_board_re linux-scs Stephen M. Cameron 26. 2010-05-27 [PATCH 05/24] hpsa: factor out hpsa_find_memory_BAR linux-scs Stephen M. Cameron 27. 2010-05-27 [PATCH 04/24] hpsa: remove redundant board_id paramet linux-scs Stephen M. Cameron 28. 2010-05-27 [PATCH 03/24] hpsa: factor out hpsa_board_disabled linux-scs Stephen M. Cameron 29. 2010-05-27 [PATCH 02/24] hpsa: factor out hpsa_lookup_board_id linux-scs Stephen M. Cameron 30. 2010-05-27 [PATCH 01/24] hpsa: save pdev pointer in per hba stru linux-scs Stephen M. Cameron -- steve
PATCH 1/9 hpsa: add new controllers
From: Stephen M. Cameron <[email protected]>
hpsa: add new controllers
--- drivers/scsi/hpsa.c | 10 ++++++++++ 1 files changed, 10 insertions(+), 0 deletions(-)
drivers/scsi/hpsa.c +10 -0
@@ -86,6 +86,11 @@ static const struct pci_device_id hpsa_pci_device_id[] = { {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSE, 0x103C, 0x324a}, {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSE, 0x103C, 0x324b}, {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSE, 0x103C, 0x3233}, + {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSE, 0x103C, 0x3250}, + {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSE, 0x103C, 0x3251}, + {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSE, 0x103C, 0x3252}, + {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSE, 0x103C, 0x3253}, + {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSE, 0x103C, 0x3254}, #define PCI_DEVICE_ID_HP_CISSF 0x333f {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSF, 0x103C, 0x333F}, {PCI_VENDOR_ID_HP, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
@@ -109,6 +114,11 @@ static struct board_type products[] = { {0x324b103C, "Smart Array P711m", &SA5_access}, {0x3233103C, "StorageWorks P1210m", &SA5_access}, {0x333F103C, "StorageWorks P1210m", &SA5_access}, + {0x3250103C, "Smart Array", &SA5_access}, + {0x3250113C, "Smart Array", &SA5_access}, + {0x3250123C, "Smart Array", &SA5_access}, + {0x3250133C, "Smart Array", &SA5_access}, + {0x3250143C, "Smart Array", &SA5_access}, {0xFFFF103C, "Unknown Smart Array", &SA5_access}, };
PATCH 2/9 hpsa: Make "hpsa_allow_any=1" boot param enable Compaq Smart Arrays.
In reply to Stephen M. CameronFrom: Stephen M. Cameron <[email protected]>
hpsa: Make "hpsa_allow_any=1" boot param enable Compaq Smart Arrays. We were previously only accepting HP boards.
--- drivers/scsi/hpsa.c | 6 +++++- 1 files changed, 5 insertions(+), 1 deletions(-)
drivers/scsi/hpsa.c +5 -1
@@ -95,6 +95,8 @@ static const struct pci_device_id hpsa_pci_device_id[] = { {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSF, 0x103C, 0x333F}, {PCI_VENDOR_ID_HP, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_STORAGE_RAID << 8, 0xffff << 8, 0}, + {PCI_VENDOR_ID_COMPAQ, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, + PCI_CLASS_STORAGE_RAID << 8, 0xffff << 8, 0}, {0,} };
@@ -3293,7 +3295,9 @@ static int __devinit hpsa_lookup_board_id(struct pci_dev *pdev, u32 *board_id)if (*board_id == products[i].board_id)return i; -if (subsystem_vendor_id !=PCI_VENDOR_ID_HP || !hpsa_allow_any) { +if ((subsystem_vendor_id !=PCI_VENDOR_ID_HP && + subsystem_vendor_id !=PCI_VENDOR_ID_COMPAQ ) || + !hpsa_allow_any) {dev_warn (&pdev->dev,"unrecognized board ID: " "0x%08x, ignoring.\n" , *board_id);return -ENODEV ;
PATCH 3/9 hpsa: make hpsa_find_memory_BAR not require the per HBA structure.
In reply to Stephen M. CameronFrom: Stephen M. Cameron <[email protected]>
Rationale for this is that in order to fix the hard reset code used by kdump, we need to use this function before we even have the per HBA structure.
--- drivers/scsi/hpsa.c | 12 ++++++------ 1 files changed, 6 insertions(+), 6 deletions(-)
drivers/scsi/hpsa.c +6 -6
@@ -3313,20 +3313,20 @@ static inline bool hpsa_board_disabled(struct pci_dev *pdev)return ((command &PCI_COMMAND_MEMORY ) ==0 ); } -static int __devinit hpsa_pci_find_memory_BAR (struct ctlr_info *h, +static int __devinit hpsa_pci_find_memory_BAR (struct pci_dev *pdev,unsigned long *memory_bar) {int i;for (i =0 ; i <DEVICE_COUNT_RESOURCE ; i++) -if (pci_resource_flags (h->pdev, i) &IORESOURCE_MEM ) { +if (pci_resource_flags (pdev, i) &IORESOURCE_MEM ) {/* addressing mode bits already removed */ - *memory_bar =pci_resource_start (h->pdev, i); -dev_dbg (&h->pdev->dev,"memory BAR = %lx\n" , + *memory_bar =pci_resource_start (pdev, i); +dev_dbg (&pdev->dev,"memory BAR = %lx\n" , *memory_bar);return 0 ; } -dev_warn (&h->pdev->dev,"no memory BAR found\n" ); +dev_warn (&pdev->dev,"no memory BAR found\n" );return -ENODEV ; }
@@ -3503,7 +3503,7 @@ static int __devinit hpsa_pci_init(struct ctlr_info *h)return err; }hpsa_interrupt_mode (h); - err =hpsa_pci_find_memory_BAR (h, &h->paddr); + err =hpsa_pci_find_memory_BAR (h->pdev, &h->paddr);if (err) goto err_out_free_res; h->vaddr =remap_pci_mem (h->paddr,0x250 );
PATCH 4/9 hpsa: factor out hpsa_find_cfg_addrs.
In reply to Stephen M. CameronFrom: Stephen M. Cameron <[email protected]>
Rationale for this is that I will also need to use this code in fixing kdump host reset code prior to having the hba structure.
--- drivers/scsi/hpsa.c | 34 ++++++++++++++++++++++------------ 1 files changed, 22 insertions(+), 12 deletions(-)
drivers/scsi/hpsa.c +22 -12
@@ -3345,29 +3345,39 @@ static int __devinit hpsa_wait_for_board_ready(struct ctlr_info *h)return -ENODEV ; } +static int __devinit hpsa_find_cfg_addrs (struct pci_dev *pdev, +void __iomem *vaddr,u32 *cfg_base_addr,u64 *cfg_base_addr_index, +u64 *cfg_offset) +{ + *cfg_base_addr =readl (vaddr +SA5_CTCFG_OFFSET ); + *cfg_offset =readl (vaddr +SA5_CTMEM_OFFSET ); + *cfg_base_addr &= (u32 )0x0000ffff ; + *cfg_base_addr_index =find_PCI_BAR_index (pdev, *cfg_base_addr); +if (*cfg_base_addr_index ==-1 ) { +dev_warn (&pdev->dev,"cannot find cfg_base_addr_index\n" ); +return -ENODEV ; + } +return 0 ; +} +static int __devinit hpsa_find_cfgtables (struct ctlr_info *h) {u64 cfg_offset;u32 cfg_base_addr;u64 cfg_base_addr_index;u32 trans_offset; +int rc; -/* get the address index number */ - cfg_base_addr =readl (h->vaddr +SA5_CTCFG_OFFSET ); - cfg_base_addr &= (u32 )0x0000ffff ; - cfg_base_addr_index =find_PCI_BAR_index (h->pdev, cfg_base_addr); -if (cfg_base_addr_index ==-1 ) { -dev_warn (&h->pdev->dev,"cannot find cfg_base_addr_index\n" ); -return -ENODEV ; - } - cfg_offset =readl (h->vaddr +SA5_CTMEM_OFFSET ); + rc =hpsa_find_cfg_addrs (h->pdev, h->vaddr, &cfg_base_addr, + &cfg_base_addr_index, &cfg_offset); +if (rc) +return rc; h->cfgtable =remap_pci_mem (pci_resource_start (h->pdev, - cfg_base_addr_index) + cfg_offset, -sizeof (h->cfgtable)); + cfg_base_addr_index) + cfg_offset,sizeof (*h->cfgtable));if (!h->cfgtable)return -ENOMEM ;/* Find performant mode table. */ - trans_offset =readl (&(h->cfgtable->TransMethodOffset)); + trans_offset =readl (&h->cfgtable->TransMethodOffset); h->transtable =remap_pci_mem (pci_resource_start (h->pdev, cfg_base_addr_index)+cfg_offset+trans_offset,sizeof (*h->transtable));
PATCH 5/9 hpsa: factor out the code to reset controllers on driver load
In reply to Stephen M. CameronFrom: Stephen M. Cameron <[email protected]>
hpsa: factor out the code to reset controllers on driver load for kdump support
--- drivers/scsi/hpsa.c | 49 ++++++++++++++++++++++++++++++------------------- 1 files changed, 30 insertions(+), 19 deletions(-)
drivers/scsi/hpsa.c +30 -19
@@ -3571,33 +3571,44 @@ static void __devinit hpsa_hba_inquiry(struct ctlr_info *h) } } +static __devinit int hpsa_init_reset_devices (struct pci_dev *pdev) +{ +int i; + +if (!reset_devices) +return 0 ; + +/* Reset the controller with a PCI power-cycle */ +if (hpsa_hard_reset_controller (pdev) ||hpsa_reset_msi (pdev)) +return -ENODEV ; + +/* Some devices (notably the HP Smart Array 5i Controller) +need a little pause here */ +msleep (HPSA_POST_RESET_PAUSE_MSECS ); + +/* Now try to get the controller to respond to a no-op */ +for (i =0 ; i <HPSA_POST_RESET_NOOP_RETRIES ; i++) { +if (hpsa_noop (pdev) ==0 ) +break ; +else +dev_warn (&pdev->dev,"no-op failed%s\n" , + (i <11 ?"; re-trying" :"" )); + } +return 0 ; +} +static int __devinit hpsa_init_one (struct pci_dev *pdev,const struct pci_device_id *ent) { -int i, rc; -int dac; +int dac, rc;struct ctlr_info *h;if (number_of_controllers ==0 ) printk(KERN_INFO DRIVER_NAME "\n"); -if (reset_devices) { -/* Reset the controller with a PCI power-cycle */ -if (hpsa_hard_reset_controller (pdev) ||hpsa_reset_msi (pdev)) -return -ENODEV ; - -/* Some devices (notably the HP Smart Array 5i Controller) -need a little pause here */ -msleep (HPSA_POST_RESET_PAUSE_MSECS ); -/* Now try to get the controller to respond to a no-op */ -for (i =0 ; i <HPSA_POST_RESET_NOOP_RETRIES ; i++) { -if (hpsa_noop (pdev) ==0 ) -break ; -else -dev_warn (&pdev->dev,"no-op failed%s\n" , - (i <11 ?"; re-trying" :"" )); - } - } + rc =hpsa_init_reset_devices (pdev); +if (rc) +return rc;/* Command structures must be aligned on a 32-byte boundary because * the 5 lower bits of the address are used by the hardware. and by
PATCH 6/9 hpsa: Fix hard reset code.
In reply to Stephen M. CameronFrom: Stephen M. Cameron <[email protected]>
hpsa: Fix hard reset code.
Smart Array controllers newer than the P600 do not honor the PCI power state method of resetting the controllers. Instead, in these cases we can get them to reset via the "doorbell" register.
This escaped notice until we began using "performant" mode because the fact that the controllers did not reset did not normally impede subsequent operation, and so things generally appeared to "work". Once the performant mode code was added, if the controller does not reset, it remains in performant mode. The code immediately after the reset presumes the controller is in "simple" mode (which previously, it had remained in simple mode the whole time). If the controller remains in performant mode any code which presumes it is in simple mode will not work. So the reset needs to be fixed.
Unfortunately there are some controllers which cannot be reset by either method. (eg. p800). We detect these cases by noticing that the controller seems to remain in performant mode even after a reset has been attempted. In those case, we proceed anyway, as if the reset has happened (and skip the step of waiting for the controller to become ready -- which is expecting it to be in "simple" mode.) To sum up, we try to do a better job of resetting the controller if "reset_devices" is set, and if it doesn't work, we print a message and try to continue anyway.
--- drivers/scsi/hpsa.c | 174 ++++++++++++++++++++++++++++++++++++----------- drivers/scsi/hpsa_cmd.h | 4 + 2 files changed, 137 insertions(+), 41 deletions(-)
drivers/scsi/hpsa.c +133 -41
@@ -174,6 +174,11 @@ static void calc_bucket_map(int *bucket, int num_buckets, int nsgs, int *bucket_map);static __devinit void hpsa_put_ctlr_into_performant_mode (struct ctlr_info *h);static inline u32 next_command (struct ctlr_info *h); +static int __devinit hpsa_find_cfg_addrs (struct pci_dev *pdev, +void __iomem *vaddr,u32 *cfg_base_addr,u64 *cfg_base_addr_index, +u64 *cfg_offset); +static int __devinit hpsa_pci_find_memory_BAR (struct pci_dev *pdev, +unsigned long *memory_bar); static DEVICE_ATTR(raid_level, S_IRUGO, raid_level_show, NULL); static DEVICE_ATTR(lunid, S_IRUGO, lunid_show, NULL);
@@ -3078,17 +3083,75 @@ static __devinit int hpsa_reset_msi(struct pci_dev *pdev)return 0 ; } +static int hpsa_controller_hard_reset (struct pci_dev *pdev, +void *__iomem vaddr,bool use_doorbell) +{ +u16 pmcsr; +int pos; + +if (use_doorbell) { +/* For everything after the P600, the PCI power state method +* of resetting the controller doesn't work, so we have this +* other way using the doorbell register. +*/ +dev_info (&pdev->dev,"using doorbell to reset controller\n" ); +writel (DOORBELL_CTLR_RESET , vaddr +SA5_DOORBELL ); +msleep (1000 ); + }else {/* Try to do it the PCI power state way */ + +/* Quoting from the Open CISS Specification: "The Power +* Management Control/Status Register (CSR) controls the power +* state of the device. The normal operating state is D0, +* CSR=00h. The software off state is D3, CSR=03h. To reset +* the controller, place the interface device in D3 then to D0, +* this causes a secondary PCI reset which will reset the +* controller." */ + + pos =pci_find_capability (pdev,PCI_CAP_ID_PM ); +if (pos ==0 ) { +dev_err (&pdev->dev, +"hpsa_reset_controller: " +"PCI PM not supported\n" ); +return -ENODEV ; + } +dev_info (&pdev->dev,"using PCI PM to reset controller\n" ); +/* enter the D3hot power management state */ +pci_read_config_word (pdev, pos +PCI_PM_CTRL , &pmcsr); + pmcsr &= ~PCI_PM_CTRL_STATE_MASK ; + pmcsr |= PCI_D3hot; +pci_write_config_word (pdev, pos +PCI_PM_CTRL , pmcsr); + +msleep (500 ); + +/* enter the D0 power management state */ + pmcsr &= ~PCI_PM_CTRL_STATE_MASK ; + pmcsr |=PCI_D0 ; +pci_write_config_word (pdev, pos +PCI_PM_CTRL , pmcsr); + +msleep (500 ); + } +return 0 ; +} +/* This does a hard reset of the controller using PCI power management -* states. +* states or the using the doorbell register. */ -static __devinit int hpsa_hard_reset_controller (struct pci_dev *pdev) +static __devinit int hpsa_kdump_hard_reset_controller (struct pci_dev *pdev) { -u16 pmcsr, saved_config_space[32 ]; -int i, pos; +u16 saved_config_space[32 ]; +u64 cfg_offset; +u32 cfg_base_addr; +u64 cfg_base_addr_index; +void __iomem *vaddr; +unsigned long paddr; +u32 misc_fw_support, active_transport; +int rc, i; +struct CfgTable __iomem *cfgtable; +bool use_doorbell; -dev_info (&pdev->dev,"using PCI PM to reset controller\n" ); -/* This is very nearly the same thing as +/* For controllers as old as the P600, this is very nearly +* the same thing as * * pci_save_state(pci_dev); * pci_set_power_state(pci_dev, PCI_D3hot);
@@ -3102,41 +3165,42 @@ static __devinit int hpsa_hard_reset_controller(struct pci_dev *pdev)* violate the ordering requirements for restoring the * configuration space from the CCISS document (see the * comment below). So we roll our own .... +* +* For controllers newer than the P600, the pci power state +* method of resetting doesn't work so we have another way +* using the doorbell register. */ -for (i =0 ; i <32 ; i++)pci_read_config_word (pdev,2 *i, &saved_config_space[i]); - pos =pci_find_capability (pdev,PCI_CAP_ID_PM ); -if (pos ==0 ) { -dev_err (&pdev->dev, -"hpsa_reset_controller: PCI PM not supported\n" ); -return -ENODEV ; - } - -/* Quoting from the Open CISS Specification: "The Power -* Management Control/Status Register (CSR) controls the power -* state of the device. The normal operating state is D0, -* CSR=00h. The software off state is D3, CSR=03h. To reset -* the controller, place the interface device in D3 then to -* D0, this causes a secondary PCI reset which will reset the -* controller." -*/ -/* enter the D3hot power management state */ -pci_read_config_word (pdev, pos +PCI_PM_CTRL , &pmcsr); - pmcsr &= ~PCI_PM_CTRL_STATE_MASK ; - pmcsr |= PCI_D3hot; -pci_write_config_word (pdev, pos +PCI_PM_CTRL , pmcsr); +/* find the first memory BAR, so we can find the cfg table */ + rc =hpsa_pci_find_memory_BAR (pdev, &paddr); +if (rc) +return rc; + vaddr =remap_pci_mem (paddr,0x250 ); +if (!vaddr) +return -ENOMEM ; -msleep (500 ); +/* find cfgtable in order to check if reset via doorbell is supported */ + rc =hpsa_find_cfg_addrs (pdev, vaddr, &cfg_base_addr, + &cfg_base_addr_index, &cfg_offset); +if (rc) + goto unmap_vaddr; + cfgtable =remap_pci_mem (pci_resource_start (pdev, + cfg_base_addr_index) + cfg_offset,sizeof (*cfgtable)); +if (!cfgtable) { + rc = -ENOMEM ; + goto unmap_vaddr; + } -/* enter the D0 power management state */ - pmcsr &= ~PCI_PM_CTRL_STATE_MASK ; - pmcsr |=PCI_D0 ; -pci_write_config_word (pdev, pos +PCI_PM_CTRL , pmcsr); +/* If reset via doorbell register is supported, use that. */ + misc_fw_support =readl (&cfgtable->misc_fw_support); + use_doorbell = misc_fw_support &MISC_FW_DOORBELL_RESET ; -msleep (500 ); + rc =hpsa_controller_hard_reset (pdev, vaddr, use_doorbell); +if (rc) + goto unmap_cfgtable;/* Restore the PCI configuration space. The Open CISS * Specification says, "Restore the PCI Configuration
@@ -3153,7 +3217,29 @@ static __devinit int hpsa_hard_reset_controller(struct pci_dev *pdev)wmb ();pci_write_config_word (pdev,4 , saved_config_space[2 ]); -return 0 ; +/* Some devices (notably the HP Smart Array 5i Controller) +need a little pause here */ +msleep (HPSA_POST_RESET_PAUSE_MSECS ); + +/* Controller should be in simple mode at this point. If it's not, +* It means we're on one of those controllers which doesn't support +* the doorbell reset method and on which the PCI power management reset +* method doesn't work (P800, for example.) +* In those cases, pretend the reset worked and hope for the best. +*/ + active_transport =readl (&cfgtable->TransportActive); +if (active_transport &PERFORMANT_MODE ) { +dev_warn (&pdev->dev,"Unable to successfully reset controller," +" proceeding anyway.\n" ); + rc = -ENOTSUPP ; + } + +unmap_cfgtable: +iounmap (cfgtable); + +unmap_vaddr: +iounmap (vaddr); +return rc; }/*
@@ -3573,18 +3659,24 @@ static void __devinit hpsa_hba_inquiry(struct ctlr_info *h)static __devinit int hpsa_init_reset_devices (struct pci_dev *pdev) { -int i; +int rc, i;if (!reset_devices)return 0 ; -/* Reset the controller with a PCI power-cycle */ -if (hpsa_hard_reset_controller (pdev) ||hpsa_reset_msi (pdev)) -return -ENODEV ; +/* Reset the controller with a PCI power-cycle or via doorbell */ + rc =hpsa_kdump_hard_reset_controller (pdev); -/* Some devices (notably the HP Smart Array 5i Controller) -need a little pause here */ -msleep (HPSA_POST_RESET_PAUSE_MSECS ); +/* -ENOTSUPP here means we cannot reset the controller +* but it's already (and still) up and running in +* "performant mode". +*/ +if (rc == -ENOTSUPP ) +return 0 ;/* just try to do the kdump anyhow. */ +if (rc) +return -ENODEV ; +if (hpsa_reset_msi (pdev)) +return -ENODEV ;/* Now try to get the controller to respond to a no-op */ for (i =0 ; i <HPSA_POST_RESET_NOOP_RETRIES ; i++) {
drivers/scsi/hpsa_cmd.h +4 -0
@@ -100,6 +100,7 @@/* Configuration Table */ #define CFGTBL_ChangeReq 0x00000001l#define CFGTBL_AccCmds 0x00000001l +#define DOORBELL_CTLR_RESET 0x00000004l#define CFGTBL_Trans_Simple 0x00000002l#define CFGTBL_Trans_Performant 0x00000004l
@@ -339,6 +340,9 @@ struct CfgTable {u32 MaxPhysicalDevices;u32 MaxPhysicalDrivesPerLogicalUnit;u32 MaxPerformantModeCommands; +u8 reserved[0x78 -0x58 ]; +u32 misc_fw_support;/* offset 0x78 */ +#define MISC_FW_DOORBELL_RESET (0x02) };#define NUM_BLOCKFETCH_ENTRIES 8
PATCH 7/9 hpsa: forbid hard reset of 640x boards
In reply to Stephen M. CameronFrom: Stephen M. Cameron <[email protected]>
hpsa: forbid hard reset of 640x boards
The 6402/6404 are two PCI devices -- two Smart Array controllers -- that fit into one slot. It is possible to reset them independently, however, they share a battery backed cache module. One of the pair controls the cache and the 2nd one access the cache through the first one. If you reset the one controlling the cache, the other one will not be a happy camper. So we just forbid resetting this conjoined mess.
--- drivers/scsi/hpsa.c | 18 ++++++++++++++++-- 1 files changed, 16 insertions(+), 2 deletions(-)
drivers/scsi/hpsa.c +16 -2
@@ -179,6 +179,7 @@ static int __devinit hpsa_find_cfg_addrs(struct pci_dev *pdev, u64 *cfg_offset);static int __devinit hpsa_pci_find_memory_BAR (struct pci_dev *pdev,unsigned long *memory_bar); +static int __devinit hpsa_lookup_board_id (struct pci_dev *pdev,u32 *board_id); static DEVICE_ATTR(raid_level, S_IRUGO, raid_level_show, NULL); static DEVICE_ATTR(lunid, S_IRUGO, lunid_show, NULL);
@@ -3148,7 +3149,7 @@ static __devinit int hpsa_kdump_hard_reset_controller(struct pci_dev *pdev)int rc, i;struct CfgTable __iomem *cfgtable;bool use_doorbell; - +u32 board_id;/* For controllers as old as the P600, this is very nearly * the same thing as
@@ -3170,6 +3171,18 @@ static __devinit int hpsa_kdump_hard_reset_controller(struct pci_dev *pdev)* method of resetting doesn't work so we have another way * using the doorbell register. */ + +/* Exclude 640x boards. These are two pci devices in one slot +* which share a battery backed cache module. One controls the +* cache, the other accesses the cache through the one that controls +* it. If we reset the one controlling the cache, the other will +* likely not be happy. Just forbid resetting this conjoined mess. +* The 640x isn't really supported by hpsa anyway. +*/ +hpsa_lookup_board_id (pdev, &board_id); +if (board_id ==0x409C0E11 || board_id ==0x409D0E11 ) +return -ENOTSUPP ; +for (i =0 ; i <32 ; i++)pci_read_config_word (pdev,2 *i, &saved_config_space[i]);
@@ -3669,7 +3682,8 @@ static __devinit int hpsa_init_reset_devices(struct pci_dev *pdev)/* -ENOTSUPP here means we cannot reset the controller * but it's already (and still) up and running in -* "performant mode". +* "performant mode". Or, it might be 640x, which can't reset +* due to concerns about shared bbwc between 6402/6404 pair. */ if (rc == -ENOTSUPP )return 0 ;/* just try to do the kdump anyhow. */
PATCH 8/9 hpsa: separate intx and msi/msix interrupt handlers
In reply to Stephen M. CameronFrom: Stephen M. Cameron <[email protected]>
hpsa: separate intx and msi/msix interrupt handlers There are things which need to be done in the intx interrupt handler which do not need to be done in the msi/msix interrupt handler, like checking that the interrupt is actually for us, and checking that the interrupt pending bit on the hardware is set (which we weren't previously doing at all, which means old controllers wouldn't work), so it makes sense to separate these into two functions.
--- drivers/scsi/hpsa.c | 39 ++++++++++++++++++++++++++++++++------- 1 files changed, 32 insertions(+), 7 deletions(-)
drivers/scsi/hpsa.c +32 -7
@@ -126,7 +126,8 @@ static struct board_type products[] = {static int number_of_controllers; -static irqreturn_t do_hpsa_intr (int irq,void *dev_id); +static irqreturn_t do_hpsa_intr_intx (int irq,void *dev_id); +static irqreturn_t do_hpsa_intr_msi (int irq,void *dev_id);static int hpsa_ioctl (struct scsi_device *dev,int cmd,void *arg);static void start_io (struct ctlr_info *h);
@@ -2858,9 +2859,8 @@ static inline bool interrupt_pending(struct ctlr_info *h)static inline long interrupt_not_for_us (struct ctlr_info *h) { -return !(h->msi_vector || h->msix_vector) && - ((h->access.intr_pending (h) ==0 ) || - (h->interrupts_enabled ==0 )); +return (h->access.intr_pending (h) ==0 ) || + (h->interrupts_enabled ==0 ); } static inline int bad_tag(struct ctlr_info *h, u32 tag_index,
@@ -2934,7 +2934,7 @@ static inline u32 process_nonindexed_cmd(struct ctlr_info *h,return next_command (h); } -static irqreturn_t do_hpsa_intr (int irq,void *dev_id) +static irqreturn_t do_hpsa_intr_intx (int irq,void *dev_id) {struct ctlr_info *h = dev_id;unsigned long flags;
@@ -2943,6 +2943,26 @@ static irqreturn_t do_hpsa_intr(int irq, void *dev_id)if (interrupt_not_for_us (h))return IRQ_NONE ;spin_lock_irqsave (&h->lock, flags); +while (interrupt_pending (h)) { + raw_tag =get_next_completion (h); +while (raw_tag !=FIFO_EMPTY ) { +if (hpsa_tag_contains_index (raw_tag)) + raw_tag =process_indexed_cmd (h, raw_tag); +else + raw_tag =process_nonindexed_cmd (h, raw_tag); + } + } +spin_unlock_irqrestore (&h->lock, flags); +return IRQ_HANDLED ; +} + +static irqreturn_t do_hpsa_intr_msi (int irq,void *dev_id) +{ +struct ctlr_info *h = dev_id; +unsigned long flags; +u32 raw_tag; + +spin_lock_irqsave (&h->lock, flags); raw_tag =get_next_completion (h);while (raw_tag !=FIFO_EMPTY ) {if (hpsa_tag_contains_index (raw_tag))
@@ -3754,8 +3774,13 @@ static int __devinit hpsa_init_one(struct pci_dev *pdev,/* make sure the board interrupts are off */ h->access.set_intr_mask (h,HPSA_INTR_OFF ); - rc =request_irq (h->intr[PERF_MODE_INT ], do_hpsa_intr, -IRQF_DISABLED , h->devname, h); + +if (h->msix_vector || h->msi_vector) + rc =request_irq (h->intr[PERF_MODE_INT ], do_hpsa_intr_msi, +IRQF_DISABLED , h->devname, h); +else + rc =request_irq (h->intr[PERF_MODE_INT ], do_hpsa_intr_intx, +IRQF_DISABLED , h->devname, h);if (rc) {dev_err (&pdev->dev,"unable to get irq %d for %s\n" , h->intr[PERF_MODE_INT ], h->devname);
PATCH 9/9 hpsa: sanitize max commands
In reply to Stephen M. CameronFrom: Stephen M. Cameron <[email protected]>
hpsa: sanitize max commands
Some controllers might try to tell us they support 0 commands in performant mode. This is a lie told by buggy firmware. We have to be wary of this lest we try to allocate a negative number of command blocks, which will be treated as unsigned, and get an out of memory condition.
--- drivers/scsi/hpsa.c | 16 ++++++++++++++-- 1 files changed, 14 insertions(+), 2 deletions(-)
drivers/scsi/hpsa.c +14 -2
@@ -3505,13 +3505,25 @@ static int __devinit hpsa_find_cfgtables(struct ctlr_info *h)return 0 ; } +static void __devinit hpsa_get_max_perf_mode_cmds (struct ctlr_info *h) +{ + h->max_commands =readl (&(h->cfgtable->MaxPerformantModeCommands)); +if (h->max_commands <16 ) { +dev_warn (&h->pdev->dev,"Controller reports " +"max supported commands of %d, an obvious lie. " +"Using 16. Ensure that firmware is up to date.\n" , + h->max_commands); + h->max_commands =16 ; + } +} +/* Interrogate the hardware for some limits: * max commands, max SG elements without chaining, and with chaining, * SG chain block size, etc. */ static void __devinit hpsa_find_board_params (struct ctlr_info *h) { - h->max_commands =readl (&(h->cfgtable->MaxPerformantModeCommands)); +hpsa_get_max_perf_mode_cmds (h); h->nr_cmds = h->max_commands -4 ;/* Allow room for some ioctls */ h->maxsgentries =readl (&(h->cfgtable->MaxScatterGatherElements));/*
@@ -4056,7 +4068,7 @@ static __devinit void hpsa_put_ctlr_into_performant_mode(struct ctlr_info *h)if (!(trans_support &PERFORMANT_MODE ))return ; - h->max_commands =readl (&(h->cfgtable->MaxPerformantModeCommands)); +hpsa_get_max_perf_mode_cmds (h); h->max_sg_entries =32 ;/* Performant mode ring buffer and supporting data structures */ h->reply_pool_size = h->max_commands *sizeof (u64);