From 2c17d2da8c9ef2c5be5077d3995041791e38094d Mon Sep 17 00:00:00 2001 From: "Stephen M. Cameron" Date: Tue, 1 May 2012 11:42:30 -0500 Subject: [SCSI] hpsa: do not read from controller unnecessarily in completion code MSI/MSI-X interrupts can't race the DMA completion they are communicating so no need to read from controller to flush the DMA to the host if MSI or MSI-X interrupts are being used. Signed-off-by: Stephen M. Cameron Signed-off-by: James Bottomley --- drivers/scsi/hpsa.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/scsi/hpsa.h') diff --git a/drivers/scsi/hpsa.h b/drivers/scsi/hpsa.h index 7b28d54fa878..48f78123d1e6 100644 --- a/drivers/scsi/hpsa.h +++ b/drivers/scsi/hpsa.h @@ -258,12 +258,12 @@ static unsigned long SA5_performant_completed(struct ctlr_info *h) { unsigned long register_value = FIFO_EMPTY; - /* flush the controller write of the reply queue by reading - * outbound doorbell status register. - */ - register_value = readl(h->vaddr + SA5_OUTDB_STATUS); /* msi auto clears the interrupt pending bit. */ if (!(h->msi_vector || h->msix_vector)) { + /* flush the controller write of the reply queue by reading + * outbound doorbell status register. + */ + register_value = readl(h->vaddr + SA5_OUTDB_STATUS); writel(SA5_OUTDB_CLEAR_PERF_BIT, h->vaddr + SA5_OUTDB_CLEAR); /* Do a read in order to flush the write to the controller * (as per spec.) -- cgit From 75167d2cc7654f57b90497fe90b1f0ae946c22a6 Mon Sep 17 00:00:00 2001 From: "Stephen M. Cameron" Date: Tue, 1 May 2012 11:42:51 -0500 Subject: [SCSI] hpsa: add abort error handler function Signed-off-by: Stephen M. Cameron Signed-off-by: James Bottomley --- drivers/scsi/hpsa.h | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'drivers/scsi/hpsa.h') diff --git a/drivers/scsi/hpsa.h b/drivers/scsi/hpsa.h index 48f78123d1e6..d8aa95c43f4d 100644 --- a/drivers/scsi/hpsa.h +++ b/drivers/scsi/hpsa.h @@ -125,6 +125,27 @@ struct ctlr_info { u64 last_heartbeat_timestamp; u32 lockup_detected; struct list_head lockup_list; + u32 TMFSupportFlags; /* cache what task mgmt funcs are supported. */ +#define HPSATMF_BITS_SUPPORTED (1 << 0) +#define HPSATMF_PHYS_LUN_RESET (1 << 1) +#define HPSATMF_PHYS_NEX_RESET (1 << 2) +#define HPSATMF_PHYS_TASK_ABORT (1 << 3) +#define HPSATMF_PHYS_TSET_ABORT (1 << 4) +#define HPSATMF_PHYS_CLEAR_ACA (1 << 5) +#define HPSATMF_PHYS_CLEAR_TSET (1 << 6) +#define HPSATMF_PHYS_QRY_TASK (1 << 7) +#define HPSATMF_PHYS_QRY_TSET (1 << 8) +#define HPSATMF_PHYS_QRY_ASYNC (1 << 9) +#define HPSATMF_MASK_SUPPORTED (1 << 16) +#define HPSATMF_LOG_LUN_RESET (1 << 17) +#define HPSATMF_LOG_NEX_RESET (1 << 18) +#define HPSATMF_LOG_TASK_ABORT (1 << 19) +#define HPSATMF_LOG_TSET_ABORT (1 << 20) +#define HPSATMF_LOG_CLEAR_ACA (1 << 21) +#define HPSATMF_LOG_CLEAR_TSET (1 << 22) +#define HPSATMF_LOG_QRY_TASK (1 << 23) +#define HPSATMF_LOG_QRY_TSET (1 << 24) +#define HPSATMF_LOG_QRY_ASYNC (1 << 25) }; #define HPSA_ABORT_MSG 0 #define HPSA_DEVICE_RESET_MSG 1 -- cgit From 254f796b9f22b1944c64caabc356a56caaa2facd Mon Sep 17 00:00:00 2001 From: Matt Gates Date: Tue, 1 May 2012 11:43:06 -0500 Subject: [SCSI] hpsa: use multiple reply queues Smart Arrays can support multiple reply queues onto which command completions may be deposited. It can help performance quite a bit to arrange for command completions to be processed on the same CPU from which they were submitted to increase the likelihood of cache hits. Signed-off-by: Matt Gates Signed-off-by: Stephen M. Cameron Signed-off-by: James Bottomley --- drivers/scsi/hpsa.h | 40 +++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 15 deletions(-) (limited to 'drivers/scsi/hpsa.h') diff --git a/drivers/scsi/hpsa.h b/drivers/scsi/hpsa.h index d8aa95c43f4d..486a7c099246 100644 --- a/drivers/scsi/hpsa.h +++ b/drivers/scsi/hpsa.h @@ -34,7 +34,7 @@ struct access_method { void (*set_intr_mask)(struct ctlr_info *h, unsigned long val); unsigned long (*fifo_full)(struct ctlr_info *h); bool (*intr_pending)(struct ctlr_info *h); - unsigned long (*command_completed)(struct ctlr_info *h); + unsigned long (*command_completed)(struct ctlr_info *h, u8 q); }; struct hpsa_scsi_dev_t { @@ -48,6 +48,13 @@ struct hpsa_scsi_dev_t { unsigned char raid_level; /* from inquiry page 0xC1 */ }; +struct reply_pool { + u64 *head; + size_t size; + u8 wraparound; + u32 current_entry; +}; + struct ctlr_info { int ctlr; char devname[8]; @@ -68,7 +75,7 @@ struct ctlr_info { # define DOORBELL_INT 1 # define SIMPLE_MODE_INT 2 # define MEMQ_MODE_INT 3 - unsigned int intr[4]; + unsigned int intr[MAX_REPLY_QUEUES]; unsigned int msix_vector; unsigned int msi_vector; int intr_mode; /* either PERF_MODE_INT or SIMPLE_MODE_INT */ @@ -111,13 +118,13 @@ struct ctlr_info { unsigned long transMethod; /* - * Performant mode completion buffer + * Performant mode completion buffers */ u64 *reply_pool; - dma_addr_t reply_pool_dhandle; - u64 *reply_pool_head; size_t reply_pool_size; - unsigned char reply_pool_wraparound; + struct reply_pool reply_queue[MAX_REPLY_QUEUES]; + u8 nreply_queues; + dma_addr_t reply_pool_dhandle; u32 *blockFetchTable; unsigned char *hba_inquiry_data; u64 last_intr_timestamp; @@ -125,6 +132,8 @@ struct ctlr_info { u64 last_heartbeat_timestamp; u32 lockup_detected; struct list_head lockup_list; + /* Address of h->q[x] is passed to intr handler to know which queue */ + u8 q[MAX_REPLY_QUEUES]; u32 TMFSupportFlags; /* cache what task mgmt funcs are supported. */ #define HPSATMF_BITS_SUPPORTED (1 << 0) #define HPSATMF_PHYS_LUN_RESET (1 << 1) @@ -275,8 +284,9 @@ static void SA5_performant_intr_mask(struct ctlr_info *h, unsigned long val) } } -static unsigned long SA5_performant_completed(struct ctlr_info *h) +static unsigned long SA5_performant_completed(struct ctlr_info *h, u8 q) { + struct reply_pool *rq = &h->reply_queue[q]; unsigned long register_value = FIFO_EMPTY; /* msi auto clears the interrupt pending bit. */ @@ -292,19 +302,18 @@ static unsigned long SA5_performant_completed(struct ctlr_info *h) register_value = readl(h->vaddr + SA5_OUTDB_STATUS); } - if ((*(h->reply_pool_head) & 1) == (h->reply_pool_wraparound)) { - register_value = *(h->reply_pool_head); - (h->reply_pool_head)++; + if ((rq->head[rq->current_entry] & 1) == rq->wraparound) { + register_value = rq->head[rq->current_entry]; + rq->current_entry++; h->commands_outstanding--; } else { register_value = FIFO_EMPTY; } /* Check for wraparound */ - if (h->reply_pool_head == (h->reply_pool + h->max_commands)) { - h->reply_pool_head = h->reply_pool; - h->reply_pool_wraparound ^= 1; + if (rq->current_entry == h->max_commands) { + rq->current_entry = 0; + rq->wraparound ^= 1; } - return register_value; } @@ -324,7 +333,8 @@ static unsigned long SA5_fifo_full(struct ctlr_info *h) * returns value read from hardware. * returns FIFO_EMPTY if there is nothing to read */ -static unsigned long SA5_completed(struct ctlr_info *h) +static unsigned long SA5_completed(struct ctlr_info *h, + __attribute__((unused)) u8 q) { unsigned long register_value = readl(h->vaddr + SA5_REPLY_PORT_OFFSET); -- cgit From e16a33adc0e59aa96a483fd2923d77e674f013c1 Mon Sep 17 00:00:00 2001 From: Matt Gates Date: Tue, 1 May 2012 11:43:11 -0500 Subject: [SCSI] hpsa: refine interrupt handler locking for greater concurrency Use spinlocks with finer granularity in the submission and completion paths to allow concurrent execution for multiple reply queues. In particular, do not hold a spin lock while submitting a request to the device, nor during most of the interrupt handler. Signed-off-by: Matt Gates Signed-off-by: Stephen M. Cameron Signed-off-by: James Bottomley --- drivers/scsi/hpsa.h | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'drivers/scsi/hpsa.h') diff --git a/drivers/scsi/hpsa.h b/drivers/scsi/hpsa.h index 486a7c099246..79c36aaa2a37 100644 --- a/drivers/scsi/hpsa.h +++ b/drivers/scsi/hpsa.h @@ -246,9 +246,6 @@ static void SA5_submit_command(struct ctlr_info *h, c->Header.Tag.lower); writel(c->busaddr, h->vaddr + SA5_REQUEST_PORT_OFFSET); (void) readl(h->vaddr + SA5_SCRATCHPAD_OFFSET); - h->commands_outstanding++; - if (h->commands_outstanding > h->max_outstanding) - h->max_outstanding = h->commands_outstanding; } /* @@ -287,7 +284,7 @@ static void SA5_performant_intr_mask(struct ctlr_info *h, unsigned long val) static unsigned long SA5_performant_completed(struct ctlr_info *h, u8 q) { struct reply_pool *rq = &h->reply_queue[q]; - unsigned long register_value = FIFO_EMPTY; + unsigned long flags, register_value = FIFO_EMPTY; /* msi auto clears the interrupt pending bit. */ if (!(h->msi_vector || h->msix_vector)) { @@ -305,7 +302,9 @@ static unsigned long SA5_performant_completed(struct ctlr_info *h, u8 q) if ((rq->head[rq->current_entry] & 1) == rq->wraparound) { register_value = rq->head[rq->current_entry]; rq->current_entry++; + spin_lock_irqsave(&h->lock, flags); h->commands_outstanding--; + spin_unlock_irqrestore(&h->lock, flags); } else { register_value = FIFO_EMPTY; } @@ -338,9 +337,13 @@ static unsigned long SA5_completed(struct ctlr_info *h, { unsigned long register_value = readl(h->vaddr + SA5_REPLY_PORT_OFFSET); + unsigned long flags; - if (register_value != FIFO_EMPTY) + if (register_value != FIFO_EMPTY) { + spin_lock_irqsave(&h->lock, flags); h->commands_outstanding--; + spin_unlock_irqrestore(&h->lock, flags); + } #ifdef HPSA_DEBUG if (register_value != FIFO_EMPTY) -- cgit From 21334ea9086c31db38e76152a1e31001a0ed288a Mon Sep 17 00:00:00 2001 From: "Stephen M. Cameron" Date: Tue, 1 May 2012 11:43:37 -0500 Subject: [SCSI] hpsa: removed unused member maxQsinceinit Signed-off-by: Stephen M. Cameron Signed-off-by: James Bottomley --- drivers/scsi/hpsa.h | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/scsi/hpsa.h') diff --git a/drivers/scsi/hpsa.h b/drivers/scsi/hpsa.h index 79c36aaa2a37..fb51ef7d48cc 100644 --- a/drivers/scsi/hpsa.h +++ b/drivers/scsi/hpsa.h @@ -85,7 +85,6 @@ struct ctlr_info { struct list_head reqQ; struct list_head cmpQ; unsigned int Qdepth; - unsigned int maxQsinceinit; unsigned int maxSG; spinlock_t lock; int maxsgentries; -- cgit From e85c59746957fd6e3595d02cf614370056b5816e Mon Sep 17 00:00:00 2001 From: "Stephen M. Cameron" Date: Tue, 1 May 2012 11:43:42 -0500 Subject: [SCSI] hpsa: dial down lockup detection during firmware flash Dial back the aggressiveness of the controller lockup detection thread. Currently it will declare the controller to be locked up if it goes for 10 seconds with no interrupts and no change in the heartbeat register. Dial back this to 30 seconds with no heartbeat change, and also snoop the ioctl path and if a firmware flash command is detected, dial it back further to 4 minutes until the firmware flash command completes. The reason for this is that during the firmware flash operation, the controller apparently doesn't update the heartbeat register as frequently as it is supposed to, and we can get a false positive. Signed-off-by: Stephen M. Cameron Signed-off-by: James Bottomley --- drivers/scsi/hpsa.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/scsi/hpsa.h') diff --git a/drivers/scsi/hpsa.h b/drivers/scsi/hpsa.h index fb51ef7d48cc..981647989bfd 100644 --- a/drivers/scsi/hpsa.h +++ b/drivers/scsi/hpsa.h @@ -129,6 +129,8 @@ struct ctlr_info { u64 last_intr_timestamp; u32 last_heartbeat; u64 last_heartbeat_timestamp; + u32 heartbeat_sample_interval; + atomic_t firmware_flash_in_progress; u32 lockup_detected; struct list_head lockup_list; /* Address of h->q[x] is passed to intr handler to know which queue */ -- cgit