hpsa: Feb 15, 2011 updates
Subsystems (3)
cover hpsa: Feb 15, 2011 updates
The following series fixes a problem that commands were unintentionally re-ordered within the driver, and another problem that the hpsa_simple_mode module paramter didn't actually work. It adds a host attribute in /sys to know which mode (simple or performant) the controller is in. It informs the controller that we're only using 32-bit tags (needed for a couple of new controllers to work properly). Finally, we shouldn't proceed with kdumping if a controller cannot be reset, as there may be outstanding commands which interfere with kdump's i/o.
---
Dan Carpenter (1):
hpsa: fix bad comparison
Stephen M. Cameron (5):
hpsa: do not re-order commands in internal queues
hpsa: make hpsa.hpsa_simple_mode=1 module parameter actually work
hpsa: Add transport_mode host attribute in /sys
hpsa: Inform controller we are using 32-bit tags.
hpsa: Do not attempt kdump if we detect resetting controller failed.
Documentation/scsi/hpsa.txt | 5 ++ drivers/scsi/hpsa.c | 104 +++++++++++++++++++++++++++---------------- drivers/scsi/hpsa.h | 5 +- drivers/scsi/hpsa_cmd.h | 3 + 4 files changed, 75 insertions(+), 42 deletions(-)
Signature · 1 line
-- steve
PATCH 1/6 hpsa: do not re-order commands in internal queues
From: Stephen M. Cameron <[email protected]>
Driver's internal queues should be FIFO, not LIFO.
This is a port of an almost identical patch from cciss by Jens Axboe.
--- drivers/scsi/hpsa.c | 23 +++++++++++------------ drivers/scsi/hpsa.h | 4 ++-- drivers/scsi/hpsa_cmd.h | 2 +- 3 files changed, 14 insertions(+), 15 deletions(-)
drivers/scsi/hpsa.c +11 -12
@@ -314,9 +314,9 @@ static ssize_t host_show_commands_outstanding(struct device *dev, }/* Enqueuing and dequeuing functions for cmdlists. */ -static inline void addQ (struct hlist_head *list,struct CommandList *c) +static inline void addQ (struct list_head *list,struct CommandList *c) { -hlist_add_head (&c->list, list); +list_add_tail (&c->list, list); }static inline u32 next_command (struct ctlr_info *h)
@@ -366,9 +366,9 @@ static void enqueue_cmd_and_start_io(struct ctlr_info *h,static inline void removeQ (struct CommandList *c) { -if (WARN_ON (hlist_unhashed (&c->list))) +if (WARN_ON (list_empty (&c->list)))return ; -hlist_del_init (&c->list); +list_del_init (&c->list); }static inline int is_hba_lunid (unsigned char scsi3addr[])
@@ -2228,7 +2228,7 @@ static struct CommandList *cmd_alloc(struct ctlr_info *h) c->cmdindex = i; -INIT_HLIST_NODE (&c->list); +INIT_LIST_HEAD (&c->list); c->busaddr = (u32 ) cmd_dma_handle; temp64.val = (u64 ) err_dma_handle; c->ErrDesc.Addr.lower = temp64.val32.lower;
@@ -2266,7 +2266,7 @@ static struct CommandList *cmd_special_alloc(struct ctlr_info *h) }memset (c->err_info,0 ,sizeof (*c->err_info)); -INIT_HLIST_NODE (&c->list); +INIT_LIST_HEAD (&c->list); c->busaddr = (u32 ) cmd_dma_handle; temp64.val = (u64 ) err_dma_handle; c->ErrDesc.Addr.lower = temp64.val32.lower;
@@ -2837,8 +2837,8 @@ static void start_io(struct ctlr_info *h) {struct CommandList *c; -while (!hlist_empty (&h->reqQ)) { - c = hlist_entry(h->reqQ.first, struct CommandList, list); +while (!list_empty (&h->reqQ)) { + c = list_entry(h->reqQ.next, struct CommandList, list);/* can't do anything if fifo is full */ if ((h->access.fifo_full (h))) {dev_warn (&h->pdev->dev,"fifo full\n" );
@@ -2929,10 +2929,9 @@ static inline u32 process_nonindexed_cmd(struct ctlr_info *h, {u32 tag;struct CommandList *c =NULL ; -struct hlist_node *tmp; tag =hpsa_tag_discard_error_bits (raw_tag); -hlist_for_each_entry (c, tmp, &h->cmpQ, list) { +list_for_each_entry (c, &h->cmpQ, list) {if ((c->busaddr &0xFFFFFFE0 ) == (tag &0xFFFFFFE0 )) {finish_cmd (c, raw_tag);return next_command (h);
@@ -3761,8 +3760,8 @@ static int __devinit hpsa_init_one(struct pci_dev *pdev, h->pdev = pdev; h->busy_initializing =1 ; -INIT_HLIST_HEAD (&h->cmpQ); -INIT_HLIST_HEAD (&h->reqQ); +INIT_LIST_HEAD (&h->cmpQ); +INIT_LIST_HEAD (&h->reqQ);spin_lock_init (&h->lock);spin_lock_init (&h->scan_lock); rc =hpsa_pci_init (h);
drivers/scsi/hpsa.h +2 -2
@@ -75,8 +75,8 @@ struct ctlr_info {struct access_method access;/* queue and queue Info */ -struct hlist_head reqQ; -struct hlist_head cmpQ; +struct list_head reqQ; +struct list_head cmpQ;unsigned int Qdepth;unsigned int maxQsinceinit;unsigned int maxSG;
drivers/scsi/hpsa_cmd.h +1 -1
@@ -292,7 +292,7 @@ struct CommandList {struct ctlr_info *h;int cmd_type;long cmdindex; -struct hlist_node list; +struct list_head list;struct request *rq;struct completion *waiting;void *scsi_cmd;
PATCH 2/6 hpsa: make hpsa.hpsa_simple_mode=1 module parameter actually work
In reply to Stephen M. CameronFrom: Stephen M. Cameron <[email protected]>
It's not enough to simple avoid putting the board into performant mode, as we have to set up the interrupts differently, etc. When I originally tested this module parameter, I tested it incorrectly without realizing it, and the driver was running in performant mode the whole time unbeknownst to me.
--- drivers/scsi/hpsa.c | 37 +++++++++++++++++++++++-------------- drivers/scsi/hpsa.h | 1 + 2 files changed, 24 insertions(+), 14 deletions(-)
drivers/scsi/hpsa.c +23 -14
@@ -1186,7 +1186,7 @@ static int hpsa_scsi_detect(struct ctlr_info *h) sh->sg_tablesize = h->maxsgentries; h->scsi_host = sh; sh->hostdata[0 ] = (unsigned long ) h; - sh->irq = h->intr[PERF_MODE_INT ]; + sh->irq = h->intr[h->intr_mode]; sh->unique_id = sh->irq; error =scsi_add_host (sh, &h->pdev->dev);if (error)
@@ -2902,10 +2902,14 @@ static inline u32 hpsa_tag_to_index(u32 tag)return tag >>DIRECT_LOOKUP_SHIFT ; } -static inline u32 hpsa_tag_discard_error_bits (u32 tag) + +static inline u32 hpsa_tag_discard_error_bits (struct ctlr_info *h,u32 tag) { -#define HPSA_ERROR_BITS 0x03 -return tag & ~HPSA_ERROR_BITS ; +#define HPSA_PERF_ERROR_BITS ((1 << DIRECT_LOOKUP_SHIFT) - 1) +#define HPSA_SIMPLE_ERROR_BITS 0x03 +if (unlikely (h->transMethod != CFGTBL_Trans_Performant)) +return tag & ~HPSA_SIMPLE_ERROR_BITS ; +return tag & ~HPSA_PERF_ERROR_BITS ; }/* process completion of an indexed ("direct lookup") command */
@@ -2930,7 +2934,7 @@ static inline u32 process_nonindexed_cmd(struct ctlr_info *h,u32 tag;struct CommandList *c =NULL ; - tag =hpsa_tag_discard_error_bits (raw_tag); + tag =hpsa_tag_discard_error_bits (h, raw_tag);list_for_each_entry (c, &h->cmpQ, list) {if ((c->busaddr &0xFFFFFFE0 ) == (tag &0xFFFFFFE0 )) {finish_cmd (c, raw_tag);
@@ -2981,7 +2985,10 @@ static irqreturn_t do_hpsa_intr_msi(int irq, void *dev_id)return IRQ_HANDLED ; } -/* Send a message CDB to the firmware. */ +/* Send a message CDB to the firmware. Careful, this only works +* in simple mode, not performant mode due to the tag lookup. +* We only ever use this immediately after a controller reset. +*/ static __devinit int hpsa_message (struct pci_dev *pdev,unsigned char opcode,unsigned char type) {
@@ -3047,7 +3054,7 @@ static __devinit int hpsa_message(struct pci_dev *pdev, unsigned char opcode,for (i =0 ; i <HPSA_MSG_SEND_RETRY_LIMIT ; i++) { tag =readl (vaddr +SA5_REPLY_PORT_OFFSET ); -if (hpsa_tag_discard_error_bits (tag) == paddr32) +if ((tag & ~HPSA_SIMPLE_ERROR_BITS ) == paddr32)break ;msleep (HPSA_MSG_SEND_RETRY_INTERVAL_MSECS ); }
@@ -3379,7 +3386,7 @@ static void __devinit hpsa_interrupt_mode(struct ctlr_info *h) default_int_mode:#endif /* CONFIG_PCI_MSI */ /* if we get here we're going to use the default interrupt mode */ - h->intr[PERF_MODE_INT ] = h->pdev->irq; + h->intr[h->intr_mode] = h->pdev->irq; }static int __devinit hpsa_lookup_board_id (struct pci_dev *pdev,u32 *board_id)
@@ -3760,6 +3767,8 @@ static int __devinit hpsa_init_one(struct pci_dev *pdev, h->pdev = pdev; h->busy_initializing =1 ; + h->intr_mode = hpsa_simple_mode ?SIMPLE_MODE_INT :PERF_MODE_INT ; +printk (KERN_WARNING "hpsa_simple_mode is %d\n" , hpsa_simple_mode);INIT_LIST_HEAD (&h->cmpQ);INIT_LIST_HEAD (&h->reqQ);spin_lock_init (&h->lock);
@@ -3790,20 +3799,20 @@ static int __devinit hpsa_init_one(struct pci_dev *pdev, h->access.set_intr_mask (h,HPSA_INTR_OFF );if (h->msix_vector || h->msi_vector) - rc =request_irq (h->intr[PERF_MODE_INT ], do_hpsa_intr_msi, + rc =request_irq (h->intr[h->intr_mode], do_hpsa_intr_msi,IRQF_DISABLED , h->devname, h);else - rc =request_irq (h->intr[PERF_MODE_INT ], do_hpsa_intr_intx, + rc =request_irq (h->intr[h->intr_mode], 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); + h->intr[h->intr_mode], h->devname); goto clean2; }dev_info (&pdev->dev,"%s: <0x%x> at IRQ %d%s using DAC\n" , h->devname, pdev->device, - h->intr[PERF_MODE_INT ], dac ?"" :" not" ); + h->intr[h->intr_mode], dac ?"" :" not" ); h->cmd_pool_bits = kmalloc(((h->nr_cmds + BITS_PER_LONG -
@@ -3854,7 +3863,7 @@ clean4: h->nr_cmds *sizeof (struct ErrorInfo ), h->errinfo_pool, h->errinfo_pool_dhandle); -free_irq (h->intr[PERF_MODE_INT ], h); +free_irq (h->intr[h->intr_mode], h); clean2: clean1: h->busy_initializing =0 ;
@@ -3898,7 +3907,7 @@ static void hpsa_shutdown(struct pci_dev *pdev)*/ hpsa_flush_cache (h); h->access.set_intr_mask (h,HPSA_INTR_OFF ); -free_irq (h->intr[PERF_MODE_INT ], h); +free_irq (h->intr[h->intr_mode], h);#ifdef CONFIG_PCI_MSI if (h->msix_vector)pci_disable_msix (h->pdev);
drivers/scsi/hpsa.h +1 -0
@@ -72,6 +72,7 @@ struct ctlr_info {unsigned int intr[4 ];unsigned int msix_vector;unsigned int msi_vector; +int intr_mode;/* either PERF_MODE_INT or SIMPLE_MODE_INT */ struct access_method access;/* queue and queue Info */
PATCH 3/6 hpsa: Add transport_mode host attribute in /sys
In reply to Stephen M. CameronFrom: Stephen M. Cameron <[email protected]>
--- Documentation/scsi/hpsa.txt | 5 +++++ drivers/scsi/hpsa.c | 18 +++++++++++++++++- 2 files changed, 22 insertions(+), 1 deletions(-)
Documentation/scsi/hpsa.txt +5 -0
@@ -39,6 +39,7 @@ HPSA specific entries in /sys /sys/class/scsi_host/host*/rescan /sys/class/scsi_host/host*/firmware_revision + /sys/class/scsi_host/host*/transport_mode the host "rescan" attribute is a write only attribute. Writing to this attribute will cause the driver to scan for new, changed, or removed devices
@@ -55,6 +56,10 @@ HPSA specific entries in /sys root@host:/sys/class/scsi_host/host4# cat firmware_revision 7.14 + The transport_mode indicates whether the controller is in "performant" + or "simple" mode. This is controlled by the "hpsa_simple_mode" module + parameter. + HPSA specific disk attributes: ------------------------------
drivers/scsi/hpsa.c +17 -1
@@ -161,6 +161,8 @@ static ssize_t host_show_firmware_revision(struct device *dev, struct device_attribute *attr, char *buf);static ssize_t host_show_commands_outstanding (struct device *dev,struct device_attribute *attr,char *buf); +static ssize_t host_show_transport_mode (struct device *dev, +struct device_attribute *attr,char *buf);static void hpsa_update_scsi_devices (struct ctlr_info *h,int hostno);static ssize_t host_store_rescan (struct device *dev,struct device_attribute *attr,const char *buf,size_t count);
@@ -192,6 +194,8 @@ static DEVICE_ATTR(firmware_revision, S_IRUGO, host_show_firmware_revision, NULL); static DEVICE_ATTR(commands_outstanding, S_IRUGO, host_show_commands_outstanding, NULL); +static DEVICE_ATTR(transport_mode, S_IRUGO, + host_show_transport_mode, NULL);static struct device_attribute *hpsa_sdev_attrs[] = { &dev_attr_raid_level,
@@ -204,6 +208,7 @@ static struct device_attribute *hpsa_shost_attrs[] = { &dev_attr_rescan, &dev_attr_firmware_revision, &dev_attr_commands_outstanding, + &dev_attr_transport_mode, NULL, };
@@ -313,6 +318,18 @@ static ssize_t host_show_commands_outstanding(struct device *dev,return snprintf (buf,20 ,"%d\n" , h->commands_outstanding); } +static ssize_t host_show_transport_mode (struct device *dev, +struct device_attribute *attr,char *buf) +{ +struct ctlr_info *h; +struct Scsi_Host *shost =class_to_shost (dev); + + h =shost_to_hba (shost); +return snprintf (buf,20 ,"%s\n" , + h->transMethod == CFGTBL_Trans_Performant ? +"performant" :"simple" ); +} +/* Enqueuing and dequeuing functions for cmdlists. */ static inline void addQ (struct list_head *list,struct CommandList *c) {
@@ -3768,7 +3785,6 @@ static int __devinit hpsa_init_one(struct pci_dev *pdev, h->pdev = pdev; h->busy_initializing =1 ; h->intr_mode = hpsa_simple_mode ?SIMPLE_MODE_INT :PERF_MODE_INT ; -printk (KERN_WARNING "hpsa_simple_mode is %d\n" , hpsa_simple_mode);INIT_LIST_HEAD (&h->cmpQ);INIT_LIST_HEAD (&h->reqQ);spin_lock_init (&h->lock);
PATCH 4/6 hpsa: Inform controller we are using 32-bit tags.
In reply to Stephen M. CameronFrom: Stephen M. Cameron <[email protected]>
Controller will transfer only 32-bits on completion if it knows we are only using 32-bit tags. Also, some newer controllers apparently (and erroneously) require that we only use 32-bit tags, and that we inform the controller of this.
--- drivers/scsi/hpsa.c | 24 +++++++++++++----------- drivers/scsi/hpsa_cmd.h | 1 + 2 files changed, 14 insertions(+), 11 deletions(-)
drivers/scsi/hpsa.c +13 -11
@@ -326,7 +326,7 @@ static ssize_t host_show_transport_mode(struct device *dev, h =shost_to_hba (shost);return snprintf (buf,20 ,"%s\n" , - h->transMethod == CFGTBL_Trans_Performant ? + h->transMethod & CFGTBL_Trans_Performant ?"performant" :"simple" ); }
@@ -340,7 +340,7 @@ static inline u32 next_command(struct ctlr_info *h) {u32 a; -if (unlikely (h->transMethod != CFGTBL_Trans_Performant)) +if (unlikely (!(h->transMethod & CFGTBL_Trans_Performant)))return h->access.command_completed (h);if ((*(h->reply_pool_head) &1 ) == (h->reply_pool_wraparound)) {
@@ -364,7 +364,7 @@ static inline u32 next_command(struct ctlr_info *h)*/ static void set_performant_mode (struct ctlr_info *h,struct CommandList *c) { -if (likely (h->transMethod == CFGTBL_Trans_Performant)) +if (likely (h->transMethod & CFGTBL_Trans_Performant)) c->busaddr |=1 | (h->blockFetchTable[c->Header.SGList] <<1 ); }
@@ -2924,7 +2924,7 @@ static inline u32 hpsa_tag_discard_error_bits(struct ctlr_info *h, u32 tag) {#define HPSA_PERF_ERROR_BITS ((1 << DIRECT_LOOKUP_SHIFT) - 1)#define HPSA_SIMPLE_ERROR_BITS 0x03 -if (unlikely (h->transMethod != CFGTBL_Trans_Performant)) +if (unlikely (!(h->transMethod & CFGTBL_Trans_Performant)))return tag & ~HPSA_SIMPLE_ERROR_BITS ;return tag & ~HPSA_PERF_ERROR_BITS ; }
@@ -3640,6 +3640,7 @@ static int __devinit hpsa_enter_simple_mode(struct ctlr_info *h) "unable to get board into simple mode\n");return -ENODEV ; } + h->transMethod = CFGTBL_Trans_Simple;return 0 ; }
@@ -4025,7 +4026,8 @@ static void calc_bucket_map(int bucket[], int num_buckets, } } -static __devinit void hpsa_enter_performant_mode (struct ctlr_info *h) +static __devinit void hpsa_enter_performant_mode (struct ctlr_info *h, +u32 use_short_tags) {int i;unsigned long register_value;
@@ -4073,7 +4075,7 @@ static __devinit void hpsa_enter_performant_mode(struct ctlr_info *h)writel (0 , &h->transtable->RepQCtrAddrHigh32);writel (h->reply_pool_dhandle, &h->transtable->RepQAddr0Low32);writel (0 , &h->transtable->RepQAddr0High32); -writel (CFGTBL_Trans_Performant, +writel (CFGTBL_Trans_Performant | use_short_tags, &(h->cfgtable->HostWrite.TransportRequest));writel (CFGTBL_ChangeReq, h->vaddr +SA5_DOORBELL );hpsa_wait_for_mode_change_ack (h);
@@ -4083,6 +4085,9 @@ static __devinit void hpsa_enter_performant_mode(struct ctlr_info *h) " performant mode\n");return ; } +/* Change the access methods to the performant access methods */ + h->access = SA5_performant_access; + h->transMethod = CFGTBL_Trans_Performant; }static __devinit void hpsa_put_ctlr_into_performant_mode (struct ctlr_info *h)
@@ -4111,11 +4116,8 @@ static __devinit void hpsa_put_ctlr_into_performant_mode(struct ctlr_info *h) || (h->blockFetchTable == NULL)) goto clean_up; -hpsa_enter_performant_mode (h); - -/* Change the access methods to the performant access methods */ - h->access = SA5_performant_access; - h->transMethod = CFGTBL_Trans_Performant; +hpsa_enter_performant_mode (h, + trans_support & CFGTBL_Trans_use_short_tags);return ;
drivers/scsi/hpsa_cmd.h +1 -0
@@ -104,6 +104,7 @@#define CFGTBL_Trans_Simple 0x00000002l#define CFGTBL_Trans_Performant 0x00000004l +#define CFGTBL_Trans_use_short_tags 0x20000000l#define CFGTBL_BusType_Ultra2 0x00000001l#define CFGTBL_BusType_Ultra3 0x00000002l
PATCH 5/6 hpsa: Do not attempt kdump if we detect resetting controller failed.
In reply to Stephen M. CameronFrom: Stephen M. Cameron <[email protected]>
We can get completions left over from before the attempted reset which will interfere with the kdump. Better to just not make the attempt in that case.
--- drivers/scsi/hpsa.c | 6 +++--- 1 files changed, 3 insertions(+), 3 deletions(-)
drivers/scsi/hpsa.c +3 -3
@@ -3264,13 +3264,13 @@ static __devinit int hpsa_kdump_hard_reset_controller(struct pci_dev *pdev)* 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. +* In those cases, don't try to proceed, as it generally doesn't work. */ active_transport =readl (&cfgtable->TransportActive);if (active_transport &PERFORMANT_MODE ) {dev_warn (&pdev->dev,"Unable to successfully reset controller," -" proceeding anyway.\n" ); - rc = -ENOTSUPP ; +" Ignoring controller.\n" ); + rc = -ENODEV ; } unmap_cfgtable:
PATCH 6/6 hpsa: fix bad comparison
In reply to Stephen M. CameronFrom: Dan Carpenter <[email protected]>
'!' has higher precedence than '&'. CFGTBL_ChangeReq is 0x1 so the original code is equivelent to if (!doorbell_value) {...
--- drivers/scsi/hpsa.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)
drivers/scsi/hpsa.c +1 -1
@@ -3614,7 +3614,7 @@ static void __devinit hpsa_wait_for_mode_change_ack(struct ctlr_info *h)spin_lock_irqsave (&h->lock, flags); doorbell_value =readl (h->vaddr +SA5_DOORBELL );spin_unlock_irqrestore (&h->lock, flags); -if (!doorbell_value & CFGTBL_ChangeReq) +if (!(doorbell_value & CFGTBL_ChangeReq))break ;/* delay and try again */ usleep_range (10000 ,20000 );