From b03a7771c81a0d5f026250c8cd4091d9ee767fdc Mon Sep 17 00:00:00 2001 From: "Stephen M. Cameron" Date: Thu, 6 Jan 2011 14:47:48 -0600 Subject: [SCSI] hpsa: defend against zero sized buffers in passthru ioctls Signed-off-by: Stephen M. Cameron Signed-off-by: James Bottomley --- drivers/scsi/hpsa.c | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-) (limited to 'drivers/scsi/hpsa.c') diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c index 12deffccb8d..e4b5f3cda82 100644 --- a/drivers/scsi/hpsa.c +++ b/drivers/scsi/hpsa.c @@ -2433,15 +2433,17 @@ static int hpsa_passthru_ioctl(struct ctlr_info *h, void __user *argp) buff = kmalloc(iocommand.buf_size, GFP_KERNEL); if (buff == NULL) return -EFAULT; - } - if (iocommand.Request.Type.Direction == XFER_WRITE) { - /* Copy the data into the buffer we created */ - if (copy_from_user(buff, iocommand.buf, iocommand.buf_size)) { - kfree(buff); - return -EFAULT; + if (iocommand.Request.Type.Direction == XFER_WRITE) { + /* Copy the data into the buffer we created */ + if (copy_from_user(buff, iocommand.buf, + iocommand.buf_size)) { + kfree(buff); + return -EFAULT; + } + } else { + memset(buff, 0, iocommand.buf_size); } - } else - memset(buff, 0, iocommand.buf_size); + } c = cmd_special_alloc(h); if (c == NULL) { kfree(buff); @@ -2487,8 +2489,8 @@ static int hpsa_passthru_ioctl(struct ctlr_info *h, void __user *argp) cmd_special_free(h, c); return -EFAULT; } - - if (iocommand.Request.Type.Direction == XFER_READ) { + if (iocommand.Request.Type.Direction == XFER_READ && + iocommand.buf_size > 0) { /* Copy the data out of the buffer we created */ if (copy_to_user(iocommand.buf, buff, iocommand.buf_size)) { kfree(buff); @@ -2581,14 +2583,7 @@ static int hpsa_big_passthru_ioctl(struct ctlr_info *h, void __user *argp) } c->cmd_type = CMD_IOCTL_PEND; c->Header.ReplyQueue = 0; - - if (ioc->buf_size > 0) { - c->Header.SGList = sg_used; - c->Header.SGTotal = sg_used; - } else { - c->Header.SGList = 0; - c->Header.SGTotal = 0; - } + c->Header.SGList = c->Header.SGTotal = sg_used; memcpy(&c->Header.LUN, &ioc->LUN_info, sizeof(c->Header.LUN)); c->Header.Tag.lower = c->busaddr; memcpy(&c->Request, &ioc->Request, sizeof(c->Request)); @@ -2605,7 +2600,8 @@ static int hpsa_big_passthru_ioctl(struct ctlr_info *h, void __user *argp) } } hpsa_scsi_do_simple_cmd_core(h, c); - hpsa_pci_unmap(h->pdev, c, sg_used, PCI_DMA_BIDIRECTIONAL); + if (sg_used) + hpsa_pci_unmap(h->pdev, c, sg_used, PCI_DMA_BIDIRECTIONAL); check_ioctl_unit_attention(h, c); /* Copy the error information out */ memcpy(&ioc->error_info, c->err_info, sizeof(ioc->error_info)); @@ -2614,7 +2610,7 @@ static int hpsa_big_passthru_ioctl(struct ctlr_info *h, void __user *argp) status = -EFAULT; goto cleanup1; } - if (ioc->Request.Type.Direction == XFER_READ) { + if (ioc->Request.Type.Direction == XFER_READ && ioc->buf_size > 0) { /* Copy the data out of the buffer we created */ BYTE __user *ptr = ioc->buf; for (i = 0; i < sg_used; i++) { -- cgit v1.2.3-70-g09d2 From d896f3f3d129f1e2fbb4e3824242bc0dc2fb1a07 Mon Sep 17 00:00:00 2001 From: "Stephen M. Cameron" Date: Thu, 6 Jan 2011 14:47:53 -0600 Subject: [SCSI] hpsa: fixup DMA address before freeing. Some low bits might have been set by the driver, causing a message like this to come out: [ 13.288062] ------------[ cut here ]------------ [ 13.293211] WARNING: at lib/dma-debug.c:803 check_unmap+0x1a1/0x654() [ 13.300387] Hardware name: ProLiant DL180 G6 [ 13.305335] hpsa 0000:06:00.0: DMA-API: device driver tries to free DMA memory it has not allocated [device address=0x000000007f81e001] [size=640 bytes] Signed-off-by: Stephen M. Cameron Signed-off-by: James Bottomley --- drivers/scsi/hpsa.c | 2 +- drivers/scsi/hpsa_cmd.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/scsi/hpsa.c') diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c index e4b5f3cda82..e2089a3b506 100644 --- a/drivers/scsi/hpsa.c +++ b/drivers/scsi/hpsa.c @@ -2267,7 +2267,7 @@ static void cmd_special_free(struct ctlr_info *h, struct CommandList *c) pci_free_consistent(h->pdev, sizeof(*c->err_info), c->err_info, (dma_addr_t) temp64.val); pci_free_consistent(h->pdev, sizeof(*c), - c, (dma_addr_t) c->busaddr); + c, (dma_addr_t) (c->busaddr & DIRECT_LOOKUP_MASK)); } #ifdef CONFIG_COMPAT diff --git a/drivers/scsi/hpsa_cmd.h b/drivers/scsi/hpsa_cmd.h index f5c4c3cc053..7910c141115 100644 --- a/drivers/scsi/hpsa_cmd.h +++ b/drivers/scsi/hpsa_cmd.h @@ -265,6 +265,7 @@ struct ErrorInfo { #define DIRECT_LOOKUP_SHIFT 5 #define DIRECT_LOOKUP_BIT 0x10 +#define DIRECT_LOOKUP_MASK (~((1 << DIRECT_LOOKUP_SHIFT) - 1)) #define HPSA_ERROR_BIT 0x02 struct ctlr_info; /* defined in hpsa.h */ -- cgit v1.2.3-70-g09d2 From 922a9e4da34270d81e216728f929c6e11e169794 Mon Sep 17 00:00:00 2001 From: "Stephen M. Cameron" Date: Thu, 6 Jan 2011 14:47:58 -0600 Subject: [SCSI] hpsa: Remove duplicate defines of DIRECT_LOOKUP_ constants They are defined in hpsa_cmd.h Signed-off-by: Stephen M. Cameron Signed-off-by: James Bottomley --- drivers/scsi/hpsa.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/scsi/hpsa.c') diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c index e2089a3b506..b345cc47694 100644 --- a/drivers/scsi/hpsa.c +++ b/drivers/scsi/hpsa.c @@ -2863,13 +2863,11 @@ static inline void finish_cmd(struct CommandList *c, u32 raw_tag) static inline u32 hpsa_tag_contains_index(u32 tag) { -#define DIRECT_LOOKUP_BIT 0x10 return tag & DIRECT_LOOKUP_BIT; } static inline u32 hpsa_tag_to_index(u32 tag) { -#define DIRECT_LOOKUP_SHIFT 5 return tag >> DIRECT_LOOKUP_SHIFT; } -- cgit v1.2.3-70-g09d2 From fe5389c87f13c16cd77d976801c93422d0c05a49 Mon Sep 17 00:00:00 2001 From: "Stephen M. Cameron" Date: Thu, 6 Jan 2011 14:48:03 -0600 Subject: [SCSI] hpsa: fix board status waiting code After a reset, we should first wait for the board to become "not ready", and then wait for it to become "ready", instead of immediately waiting for it to become "ready", and do this waiting *after* restoring PCI config space registers. Also, only wait 10 secs for board to become "not ready" after a reset (it should quickly become not ready.) Signed-off-by: Stephen M. Cameron Signed-off-by: James Bottomley --- drivers/scsi/hpsa.c | 44 ++++++++++++++++++++++++++++++++++++-------- drivers/scsi/hpsa.h | 4 ++++ 2 files changed, 40 insertions(+), 8 deletions(-) (limited to 'drivers/scsi/hpsa.c') diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c index b345cc47694..cfd30adc3f1 100644 --- a/drivers/scsi/hpsa.c +++ b/drivers/scsi/hpsa.c @@ -173,6 +173,10 @@ static int __devinit hpsa_find_cfg_addrs(struct pci_dev *pdev, 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 int __devinit hpsa_wait_for_board_state(struct pci_dev *pdev, + void __iomem *vaddr, int wait_for_ready); +#define BOARD_NOT_READY 0 +#define BOARD_READY 1 static DEVICE_ATTR(raid_level, S_IRUGO, raid_level_show, NULL); static DEVICE_ATTR(lunid, S_IRUGO, lunid_show, NULL); @@ -3237,6 +3241,20 @@ static __devinit int hpsa_kdump_hard_reset_controller(struct pci_dev *pdev) need a little pause here */ msleep(HPSA_POST_RESET_PAUSE_MSECS); + /* Wait for board to become not ready, then ready. */ + dev_info(&pdev->dev, "Waiting for board to become ready.\n"); + rc = hpsa_wait_for_board_state(pdev, vaddr, BOARD_NOT_READY); + if (rc) + dev_warn(&pdev->dev, + "failed waiting for board to become not ready\n"); + rc = hpsa_wait_for_board_state(pdev, vaddr, BOARD_READY); + if (rc) { + dev_warn(&pdev->dev, + "failed waiting for board to become ready\n"); + goto unmap_cfgtable; + } + dev_info(&pdev->dev, "board ready.\n"); + /* 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 @@ -3432,18 +3450,28 @@ static int __devinit hpsa_pci_find_memory_BAR(struct pci_dev *pdev, return -ENODEV; } -static int __devinit hpsa_wait_for_board_ready(struct ctlr_info *h) +static int __devinit hpsa_wait_for_board_state(struct pci_dev *pdev, + void __iomem *vaddr, int wait_for_ready) { - int i; + int i, iterations; u32 scratchpad; + if (wait_for_ready) + iterations = HPSA_BOARD_READY_ITERATIONS; + else + iterations = HPSA_BOARD_NOT_READY_ITERATIONS; - for (i = 0; i < HPSA_BOARD_READY_ITERATIONS; i++) { - scratchpad = readl(h->vaddr + SA5_SCRATCHPAD_OFFSET); - if (scratchpad == HPSA_FIRMWARE_READY) - return 0; + for (i = 0; i < iterations; i++) { + scratchpad = readl(vaddr + SA5_SCRATCHPAD_OFFSET); + if (wait_for_ready) { + if (scratchpad == HPSA_FIRMWARE_READY) + return 0; + } else { + if (scratchpad != HPSA_FIRMWARE_READY) + return 0; + } msleep(HPSA_BOARD_READY_POLL_INTERVAL_MSECS); } - dev_warn(&h->pdev->dev, "board not ready, timed out.\n"); + dev_warn(&pdev->dev, "board not ready, timed out.\n"); return -ENODEV; } @@ -3635,7 +3663,7 @@ static int __devinit hpsa_pci_init(struct ctlr_info *h) err = -ENOMEM; goto err_out_free_res; } - err = hpsa_wait_for_board_ready(h); + err = hpsa_wait_for_board_state(h->pdev, h->vaddr, BOARD_READY); if (err) goto err_out_free_res; err = hpsa_find_cfgtables(h); diff --git a/drivers/scsi/hpsa.h b/drivers/scsi/hpsa.h index 19586e189f0..074d237f449 100644 --- a/drivers/scsi/hpsa.h +++ b/drivers/scsi/hpsa.h @@ -154,12 +154,16 @@ struct ctlr_info { * HPSA_BOARD_READY_ITERATIONS are derived from those. */ #define HPSA_BOARD_READY_WAIT_SECS (120) +#define HPSA_BOARD_NOT_READY_WAIT_SECS (10) #define HPSA_BOARD_READY_POLL_INTERVAL_MSECS (100) #define HPSA_BOARD_READY_POLL_INTERVAL \ ((HPSA_BOARD_READY_POLL_INTERVAL_MSECS * HZ) / 1000) #define HPSA_BOARD_READY_ITERATIONS \ ((HPSA_BOARD_READY_WAIT_SECS * 1000) / \ HPSA_BOARD_READY_POLL_INTERVAL_MSECS) +#define HPSA_BOARD_NOT_READY_ITERATIONS \ + ((HPSA_BOARD_NOT_READY_WAIT_SECS * 1000) / \ + HPSA_BOARD_READY_POLL_INTERVAL_MSECS) #define HPSA_POST_RESET_PAUSE_MSECS (3000) #define HPSA_POST_RESET_NOOP_RETRIES (12) -- cgit v1.2.3-70-g09d2 From 270d05de2b8d82df4ed19955f6c0c7400f6ffbf5 Mon Sep 17 00:00:00 2001 From: "Stephen M. Cameron" Date: Thu, 6 Jan 2011 14:48:08 -0600 Subject: [SCSI] hpsa: Use kernel provided PCI state save and restore functions and use the doorbell reset method if available (which doesn't lock up the controller if you properly save and restore all the PCI registers that you're supposed to.) Signed-off-by: Stephen M. Cameron Signed-off-by: James Bottomley --- drivers/scsi/hpsa.c | 81 ++++++++++------------------------------------------- 1 file changed, 15 insertions(+), 66 deletions(-) (limited to 'drivers/scsi/hpsa.c') diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c index cfd30adc3f1..82b94e2c745 100644 --- a/drivers/scsi/hpsa.c +++ b/drivers/scsi/hpsa.c @@ -3053,38 +3053,6 @@ static __devinit int hpsa_message(struct pci_dev *pdev, unsigned char opcode, #define hpsa_soft_reset_controller(p) hpsa_message(p, 1, 0) #define hpsa_noop(p) hpsa_message(p, 3, 0) -static __devinit int hpsa_reset_msi(struct pci_dev *pdev) -{ -/* the #defines are stolen from drivers/pci/msi.h. */ -#define msi_control_reg(base) (base + PCI_MSI_FLAGS) -#define PCI_MSIX_FLAGS_ENABLE (1 << 15) - - int pos; - u16 control = 0; - - pos = pci_find_capability(pdev, PCI_CAP_ID_MSI); - if (pos) { - pci_read_config_word(pdev, msi_control_reg(pos), &control); - if (control & PCI_MSI_FLAGS_ENABLE) { - dev_info(&pdev->dev, "resetting MSI\n"); - pci_write_config_word(pdev, msi_control_reg(pos), - control & ~PCI_MSI_FLAGS_ENABLE); - } - } - - pos = pci_find_capability(pdev, PCI_CAP_ID_MSIX); - if (pos) { - pci_read_config_word(pdev, msi_control_reg(pos), &control); - if (control & PCI_MSIX_FLAGS_ENABLE) { - dev_info(&pdev->dev, "resetting MSI-X\n"); - pci_write_config_word(pdev, msi_control_reg(pos), - control & ~PCI_MSIX_FLAGS_ENABLE); - } - } - - return 0; -} - static int hpsa_controller_hard_reset(struct pci_dev *pdev, void * __iomem vaddr, bool use_doorbell) { @@ -3140,17 +3108,17 @@ static int hpsa_controller_hard_reset(struct pci_dev *pdev, */ static __devinit int hpsa_kdump_hard_reset_controller(struct pci_dev *pdev) { - 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; + int rc; struct CfgTable __iomem *cfgtable; bool use_doorbell; u32 board_id; + u16 command_register; /* For controllers as old as the P600, this is very nearly * the same thing as @@ -3160,14 +3128,6 @@ static __devinit int hpsa_kdump_hard_reset_controller(struct pci_dev *pdev) * pci_set_power_state(pci_dev, PCI_D0); * pci_restore_state(pci_dev); * - * but we can't use these nice canned kernel routines on - * kexec, because they also check the MSI/MSI-X state in PCI - * configuration space and do the wrong thing when it is - * set/cleared. Also, the pci_save/restore_state functions - * 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. @@ -3184,9 +3144,13 @@ static __devinit int hpsa_kdump_hard_reset_controller(struct pci_dev *pdev) 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]); - + /* Save the PCI command register */ + pci_read_config_word(pdev, 4, &command_register); + /* Turn the board off. This is so that later pci_restore_state() + * won't turn the board on before the rest of config space is ready. + */ + pci_disable_device(pdev); + pci_save_state(pdev); /* find the first memory BAR, so we can find the cfg table */ rc = hpsa_pci_find_memory_BAR(pdev, &paddr); @@ -3212,30 +3176,17 @@ static __devinit int hpsa_kdump_hard_reset_controller(struct pci_dev *pdev) misc_fw_support = readl(&cfgtable->misc_fw_support); use_doorbell = misc_fw_support & MISC_FW_DOORBELL_RESET; - /* The doorbell reset seems to cause lockups on some Smart - * Arrays (e.g. P410, P410i, maybe others). Until this is - * fixed or at least isolated, avoid the doorbell reset. - */ - use_doorbell = 0; - 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 - * Registers, offsets 00h through 60h. It is important to - * restore the command register, 16-bits at offset 04h, - * last. Do not restore the configuration status register, - * 16-bits at offset 06h." Note that the offset is 2*i. - */ - for (i = 0; i < 32; i++) { - if (i == 2 || i == 3) - continue; - pci_write_config_word(pdev, 2*i, saved_config_space[i]); + pci_restore_state(pdev); + rc = pci_enable_device(pdev); + if (rc) { + dev_warn(&pdev->dev, "failed to enable device.\n"); + goto unmap_cfgtable; } - wmb(); - pci_write_config_word(pdev, 4, saved_config_space[2]); + pci_write_config_word(pdev, 4, command_register); /* Some devices (notably the HP Smart Array 5i Controller) need a little pause here */ @@ -3732,8 +3683,6 @@ static __devinit int hpsa_init_reset_devices(struct pci_dev *pdev) 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++) { -- cgit v1.2.3-70-g09d2 From 72ceeaecb748dff3d35b10d7518bed651b895f3e Mon Sep 17 00:00:00 2001 From: "Stephen M. Cameron" Date: Thu, 6 Jan 2011 14:48:13 -0600 Subject: [SCSI] hpsa: limit commands allocated on reset_devices This is to conserve memory in a memory-limited kdump scenario Signed-off-by: Stephen M. Cameron Signed-off-by: James Bottomley --- drivers/scsi/hpsa.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/scsi/hpsa.c') diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c index 82b94e2c745..688b24333a6 100644 --- a/drivers/scsi/hpsa.c +++ b/drivers/scsi/hpsa.c @@ -3470,6 +3470,11 @@ static int __devinit hpsa_find_cfgtables(struct ctlr_info *h) static void __devinit hpsa_get_max_perf_mode_cmds(struct ctlr_info *h) { h->max_commands = readl(&(h->cfgtable->MaxPerformantModeCommands)); + + /* Limit commands in memory limited kdump scenario. */ + if (reset_devices && h->max_commands > 32) + h->max_commands = 32; + if (h->max_commands < 16) { dev_warn(&h->pdev->dev, "Controller reports " "max supported commands of %d, an obvious lie. " -- cgit v1.2.3-70-g09d2 From 25c1e56a04e60af2414f8d81eda0fd10b8e7b961 Mon Sep 17 00:00:00 2001 From: "Stephen M. Cameron" Date: Thu, 6 Jan 2011 14:48:18 -0600 Subject: [SCSI] hpsa: do not reset unknown boards on reset_devices This is to prevent hpsa from resetting older boards which the cciss driver may be controlling. Signed-off-by: Stephen M. Cameron Signed-off-by: James Bottomley --- drivers/scsi/hpsa.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers/scsi/hpsa.c') diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c index 688b24333a6..5b9cd41d932 100644 --- a/drivers/scsi/hpsa.c +++ b/drivers/scsi/hpsa.c @@ -3140,7 +3140,11 @@ static __devinit int hpsa_kdump_hard_reset_controller(struct pci_dev *pdev) * 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); + rc = hpsa_lookup_board_id(pdev, &board_id); + if (rc < 0) { + dev_warn(&pdev->dev, "Not resetting device.\n"); + return -ENODEV; + } if (board_id == 0x409C0E11 || board_id == 0x409D0E11) return -ENOTSUPP; -- cgit v1.2.3-70-g09d2 From 6eaf46fdc719991a3ccda1e14b274e9adb515978 Mon Sep 17 00:00:00 2001 From: "Stephen M. Cameron" Date: Thu, 6 Jan 2011 14:48:24 -0600 Subject: [SCSI] hpsa: take the adapter lock in hpsa_wait_for_mode_change_ack Need to take the lock while accessing the register to check to see if config table changes have taken effect. Signed-off-by: Stephen M. Cameron Signed-off-by: James Bottomley --- drivers/scsi/hpsa.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'drivers/scsi/hpsa.c') diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c index 5b9cd41d932..4fb62c2aac0 100644 --- a/drivers/scsi/hpsa.c +++ b/drivers/scsi/hpsa.c @@ -3553,13 +3553,18 @@ static inline void hpsa_p600_dma_prefetch_quirk(struct ctlr_info *h) static void __devinit hpsa_wait_for_mode_change_ack(struct ctlr_info *h) { int i; + u32 doorbell_value; + unsigned long flags; /* under certain very rare conditions, this can take awhile. * (e.g.: hot replace a failed 144GB drive in a RAID 5 set right * as we enter this code.) */ for (i = 0; i < MAX_CONFIG_WAIT; i++) { - if (!(readl(h->vaddr + SA5_DOORBELL) & CFGTBL_ChangeReq)) + spin_lock_irqsave(&h->lock, flags); + doorbell_value = readl(h->vaddr + SA5_DOORBELL); + spin_unlock_irqrestore(&h->lock, flags); + if (!doorbell_value & CFGTBL_ChangeReq) break; /* delay and try again */ msleep(10); @@ -3731,6 +3736,8 @@ static int __devinit hpsa_init_one(struct pci_dev *pdev, h->busy_initializing = 1; INIT_HLIST_HEAD(&h->cmpQ); INIT_HLIST_HEAD(&h->reqQ); + spin_lock_init(&h->lock); + spin_lock_init(&h->scan_lock); rc = hpsa_pci_init(h); if (rc != 0) goto clean1; @@ -3790,8 +3797,6 @@ static int __devinit hpsa_init_one(struct pci_dev *pdev, } if (hpsa_allocate_sg_chain_blocks(h)) goto clean4; - spin_lock_init(&h->lock); - spin_lock_init(&h->scan_lock); init_waitqueue_head(&h->scan_wait_queue); h->scan_finished = 1; /* no scan currently in progress */ -- cgit v1.2.3-70-g09d2 From 02ec19c82e87e3748d326ca5e15e7ddb18c73476 Mon Sep 17 00:00:00 2001 From: "Stephen M. Cameron" Date: Thu, 6 Jan 2011 14:48:29 -0600 Subject: [SCSI] hpsa: allow driver to put controller in either simple or performant mode Signed-off-by: Stephen M. Cameron Signed-off-by: James Bottomley --- Documentation/scsi/hpsa.txt | 6 ++++++ drivers/scsi/hpsa.c | 7 +++++++ 2 files changed, 13 insertions(+) (limited to 'drivers/scsi/hpsa.c') diff --git a/Documentation/scsi/hpsa.txt b/Documentation/scsi/hpsa.txt index dca658362cb..b14e6ee00a9 100644 --- a/Documentation/scsi/hpsa.txt +++ b/Documentation/scsi/hpsa.txt @@ -28,6 +28,12 @@ boot parameter "hpsa_allow_any=1" is specified, however these are not tested nor supported by HP with this driver. For older Smart Arrays, the cciss driver should still be used. +The "hpsa_simple_mode=1" boot parameter may be used to prevent the driver from +putting the controller into "performant" mode. The difference is that with simple +mode, each command completion requires an interrupt, while with "performant mode" +(the default, and ordinarily better performing) it is possible to have multiple +command completions indicated by a single interrupt. + HPSA specific entries in /sys ----------------------------- diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c index 4fb62c2aac0..10076f1868e 100644 --- a/drivers/scsi/hpsa.c +++ b/drivers/scsi/hpsa.c @@ -74,6 +74,10 @@ static int hpsa_allow_any; module_param(hpsa_allow_any, int, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(hpsa_allow_any, "Allow hpsa driver to access unknown HP Smart Array hardware"); +static int hpsa_simple_mode; +module_param(hpsa_simple_mode, int, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(hpsa_simple_mode, + "Use 'simple mode' rather than 'performant mode'"); /* define the PCI info for the cards we can control */ static const struct pci_device_id hpsa_pci_device_id[] = { @@ -4038,6 +4042,9 @@ static __devinit void hpsa_put_ctlr_into_performant_mode(struct ctlr_info *h) { u32 trans_support; + if (hpsa_simple_mode) + return; + trans_support = readl(&(h->cfgtable->TransportSupport)); if (!(trans_support & PERFORMANT_MODE)) return; -- cgit v1.2.3-70-g09d2 From 60d3f5b068e65d424f3cf5d108fb0747dd156d00 Mon Sep 17 00:00:00 2001 From: "Stephen M. Cameron" Date: Thu, 6 Jan 2011 14:48:34 -0600 Subject: [SCSI] hpsa: use usleep_range not msleep for small sleeps Signed-off-by: Stephen M. Cameron Signed-off-by: James Bottomley --- drivers/scsi/hpsa.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/scsi/hpsa.c') diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c index 10076f1868e..ddd729e2ea8 100644 --- a/drivers/scsi/hpsa.c +++ b/drivers/scsi/hpsa.c @@ -3571,7 +3571,7 @@ static void __devinit hpsa_wait_for_mode_change_ack(struct ctlr_info *h) if (!doorbell_value & CFGTBL_ChangeReq) break; /* delay and try again */ - msleep(10); + usleep_range(10000, 20000); } } -- cgit v1.2.3-70-g09d2 From 94a136495a3fbe59b960c46fba3574b1159e8489 Mon Sep 17 00:00:00 2001 From: "Stephen M. Cameron" Date: Thu, 6 Jan 2011 14:48:39 -0600 Subject: [SCSI] hpsa: Add a per controller commands_outstanding entry in /sys Signed-off-by: Stephen M. Cameron Signed-off-by: James Bottomley --- drivers/scsi/hpsa.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'drivers/scsi/hpsa.c') diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c index ddd729e2ea8..c255f46e640 100644 --- a/drivers/scsi/hpsa.c +++ b/drivers/scsi/hpsa.c @@ -159,6 +159,8 @@ static ssize_t unique_id_show(struct device *dev, struct device_attribute *attr, char *buf); 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 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); @@ -188,6 +190,8 @@ static DEVICE_ATTR(unique_id, S_IRUGO, unique_id_show, NULL); static DEVICE_ATTR(rescan, S_IWUSR, NULL, host_store_rescan); 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 struct device_attribute *hpsa_sdev_attrs[] = { &dev_attr_raid_level, @@ -199,6 +203,7 @@ static struct device_attribute *hpsa_sdev_attrs[] = { static struct device_attribute *hpsa_shost_attrs[] = { &dev_attr_rescan, &dev_attr_firmware_revision, + &dev_attr_commands_outstanding, NULL, }; @@ -299,6 +304,15 @@ static ssize_t host_show_firmware_revision(struct device *dev, fwrev[0], fwrev[1], fwrev[2], fwrev[3]); } +static ssize_t host_show_commands_outstanding(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(dev); + struct ctlr_info *h = shost_to_hba(shost); + + return snprintf(buf, 20, "%d\n", h->commands_outstanding); +} + /* Enqueuing and dequeuing functions for cmdlists. */ static inline void addQ(struct hlist_head *list, struct CommandList *c) { -- cgit v1.2.3-70-g09d2 From c4f8a299d04bd083643ba93e982ab910219dd1f0 Mon Sep 17 00:00:00 2001 From: "Stephen M. Cameron" Date: Fri, 7 Jan 2011 10:55:43 -0600 Subject: [SCSI] hpsa: fix use of uninitialized variable in hpsa_add_msa2xxx_enclosure_device() Thanks to Scott Teel for noticing this. Signed-off-by: Stephen M. Cameron Signed-off-by: James Bottomley --- drivers/scsi/hpsa.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/scsi/hpsa.c') diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c index c255f46e640..c6c13b0c68e 100644 --- a/drivers/scsi/hpsa.c +++ b/drivers/scsi/hpsa.c @@ -1617,6 +1617,8 @@ static int add_msa2xxx_enclosure_device(struct ctlr_info *h, if (lun == 0) /* if lun is 0, then obviously we have a lun 0. */ return 0; + memset(scsi3addr, 0, 8); + scsi3addr[3] = target; if (is_hba_lunid(scsi3addr)) return 0; /* Don't add the RAID controller here. */ @@ -1631,8 +1633,6 @@ static int add_msa2xxx_enclosure_device(struct ctlr_info *h, return 0; } - memset(scsi3addr, 0, 8); - scsi3addr[3] = target; if (hpsa_update_device_info(h, scsi3addr, this_device)) return 0; (*nmsa2xxx_enclosures)++; -- cgit v1.2.3-70-g09d2 From 1d5e2ed0805bf426226b7daa54b3e60af78baa10 Mon Sep 17 00:00:00 2001 From: "Stephen M. Cameron" Date: Fri, 7 Jan 2011 10:55:48 -0600 Subject: [SCSI] hpsa: Fix problem that CMD_UNABORTABLE command status was treated as unknown Signed-off-by: Stephen M. Cameron Signed-off-by: James Bottomley --- drivers/scsi/hpsa.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/scsi/hpsa.c') diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c index c6c13b0c68e..5828bcb8296 100644 --- a/drivers/scsi/hpsa.c +++ b/drivers/scsi/hpsa.c @@ -1152,6 +1152,10 @@ static void complete_scsi_command(struct CommandList *cp, cmd->result = DID_TIME_OUT << 16; dev_warn(&h->pdev->dev, "cp %p timedout\n", cp); break; + case CMD_UNABORTABLE: + cmd->result = DID_ERROR << 16; + dev_warn(&h->pdev->dev, "Command unabortable\n"); + break; default: cmd->result = DID_ERROR << 16; dev_warn(&h->pdev->dev, "cp %p returned unknown status %x\n", @@ -1317,6 +1321,9 @@ static void hpsa_scsi_interpret_error(struct CommandList *cp) case CMD_TIMEOUT: dev_warn(d, "cp %p timed out\n", cp); break; + case CMD_UNABORTABLE: + dev_warn(d, "Command unabortable\n"); + break; default: dev_warn(d, "cp %p returned unknown status %x\n", cp, ei->CommandStatus); -- cgit v1.2.3-70-g09d2 From 938abd8449c27fc67203e1a7c350199cea1158da Mon Sep 17 00:00:00 2001 From: Vasiliy Kulikov Date: Fri, 7 Jan 2011 10:55:53 -0600 Subject: [SCSI] hpsa: avoid leaking stack contents to userland memset arg64 to zero in the passthrough ioctls to avoid leaking contents of kernel stack memory to userland via uninitialized padding fields inserted by the compiler for alignment reasons. Signed-off-by: Stephen M. Cameron Signed-off-by: James Bottomley --- drivers/scsi/hpsa.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/scsi/hpsa.c') diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c index 5828bcb8296..959eeb202d9 100644 --- a/drivers/scsi/hpsa.c +++ b/drivers/scsi/hpsa.c @@ -2310,6 +2310,7 @@ static int hpsa_ioctl32_passthru(struct scsi_device *dev, int cmd, void *arg) int err; u32 cp; + memset(&arg64, 0, sizeof(arg64)); err = 0; err |= copy_from_user(&arg64.LUN_info, &arg32->LUN_info, sizeof(arg64.LUN_info)); @@ -2346,6 +2347,7 @@ static int hpsa_ioctl32_big_passthru(struct scsi_device *dev, int err; u32 cp; + memset(&arg64, 0, sizeof(arg64)); err = 0; err |= copy_from_user(&arg64.LUN_info, &arg32->LUN_info, sizeof(arg64.LUN_info)); -- cgit v1.2.3-70-g09d2 From 9e0fc764eaec082cd2ffcf82568dfdd086935934 Mon Sep 17 00:00:00 2001 From: "Stephen M. Cameron" Date: Tue, 15 Feb 2011 15:32:48 -0600 Subject: [SCSI] hpsa: do not re-order commands in internal queues Driver's internal queues should be FIFO, not LIFO. This is a port of an almost identical patch from cciss by Jens Axboe. Signed-off-by: Stephen M. Cameron Signed-off-by: James Bottomley --- drivers/scsi/hpsa.c | 23 +++++++++++------------ drivers/scsi/hpsa.h | 4 ++-- drivers/scsi/hpsa_cmd.h | 2 +- 3 files changed, 14 insertions(+), 15 deletions(-) (limited to 'drivers/scsi/hpsa.c') diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c index 959eeb202d9..0f40de2a33d 100644 --- a/drivers/scsi/hpsa.c +++ b/drivers/scsi/hpsa.c @@ -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); diff --git a/drivers/scsi/hpsa.h b/drivers/scsi/hpsa.h index 074d237f449..e8981934236 100644 --- a/drivers/scsi/hpsa.h +++ b/drivers/scsi/hpsa.h @@ -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; diff --git a/drivers/scsi/hpsa_cmd.h b/drivers/scsi/hpsa_cmd.h index 7910c141115..785abdd0333 100644 --- a/drivers/scsi/hpsa_cmd.h +++ b/drivers/scsi/hpsa_cmd.h @@ -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; -- cgit v1.2.3-70-g09d2 From a9a3a2739a44fc05dcaba0d4d36e52dc444c294f Mon Sep 17 00:00:00 2001 From: "Stephen M. Cameron" Date: Tue, 15 Feb 2011 15:32:53 -0600 Subject: [SCSI] hpsa: make hpsa.hpsa_simple_mode=1 module parameter actually work 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. Signed-off-by: Stephen M. Cameron Signed-off-by: James Bottomley --- drivers/scsi/hpsa.c | 37 +++++++++++++++++++++++-------------- drivers/scsi/hpsa.h | 1 + 2 files changed, 24 insertions(+), 14 deletions(-) (limited to 'drivers/scsi/hpsa.c') diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c index 0f40de2a33d..66ccacfffd5 100644 --- a/drivers/scsi/hpsa.c +++ b/drivers/scsi/hpsa.c @@ -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); diff --git a/drivers/scsi/hpsa.h b/drivers/scsi/hpsa.h index e8981934236..621a1530054 100644 --- a/drivers/scsi/hpsa.h +++ b/drivers/scsi/hpsa.h @@ -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 */ -- cgit v1.2.3-70-g09d2 From 745a7a25bc0f6dc77db72656b7bc8d17b6ee8e53 Mon Sep 17 00:00:00 2001 From: "Stephen M. Cameron" Date: Tue, 15 Feb 2011 15:32:58 -0600 Subject: [SCSI] hpsa: Add transport_mode host attribute in /sys Signed-off-by: Stephen M. Cameron Signed-off-by: James Bottomley --- Documentation/scsi/hpsa.txt | 5 +++++ drivers/scsi/hpsa.c | 18 +++++++++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) (limited to 'drivers/scsi/hpsa.c') diff --git a/Documentation/scsi/hpsa.txt b/Documentation/scsi/hpsa.txt index b14e6ee00a9..880c085b951 100644 --- a/Documentation/scsi/hpsa.txt +++ b/Documentation/scsi/hpsa.txt @@ -45,6 +45,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 @@ -61,6 +62,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: ------------------------------ diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c index 66ccacfffd5..563d439c2f4 100644 --- a/drivers/scsi/hpsa.c +++ b/drivers/scsi/hpsa.c @@ -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); -- cgit v1.2.3-70-g09d2 From 960a30e7a73affcc441b9ceaff3b1b9e73e99c1f Mon Sep 17 00:00:00 2001 From: "Stephen M. Cameron" Date: Tue, 15 Feb 2011 15:33:03 -0600 Subject: [SCSI] hpsa: Inform controller we are using 32-bit tags. 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. Signed-off-by: Stephen M. Cameron Signed-off-by: James Bottomley --- drivers/scsi/hpsa.c | 24 +++++++++++++----------- drivers/scsi/hpsa_cmd.h | 1 + 2 files changed, 14 insertions(+), 11 deletions(-) (limited to 'drivers/scsi/hpsa.c') diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c index 563d439c2f4..a778cb1fd8b 100644 --- a/drivers/scsi/hpsa.c +++ b/drivers/scsi/hpsa.c @@ -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; diff --git a/drivers/scsi/hpsa_cmd.h b/drivers/scsi/hpsa_cmd.h index 785abdd0333..18464900e76 100644 --- a/drivers/scsi/hpsa_cmd.h +++ b/drivers/scsi/hpsa_cmd.h @@ -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 -- cgit v1.2.3-70-g09d2 From ba95e2ac6bfeb9af92153058a353fc47e1addc02 Mon Sep 17 00:00:00 2001 From: "Stephen M. Cameron" Date: Tue, 15 Feb 2011 15:33:08 -0600 Subject: [SCSI] hpsa: Do not attempt kdump if we detect resetting controller failed. 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. Signed-off-by: Stephen M. Cameron Signed-off-by: James Bottomley --- drivers/scsi/hpsa.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/scsi/hpsa.c') diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c index a778cb1fd8b..eb6938fe77b 100644 --- a/drivers/scsi/hpsa.c +++ b/drivers/scsi/hpsa.c @@ -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: -- cgit v1.2.3-70-g09d2 From 382be668c5a284844f9dcbb5b1cb8ffba2386d80 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 15 Feb 2011 15:33:13 -0600 Subject: [SCSI] hpsa: fix bad comparison '!' has higher precedence than '&'. CFGTBL_ChangeReq is 0x1 so the original code is equivelent to if (!doorbell_value) {... Signed-off-by: Dan Carpenter Acked-by: Stephen M. Cameron Signed-off-by: James Bottomley --- drivers/scsi/hpsa.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/scsi/hpsa.c') diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c index eb6938fe77b..c30591f84a0 100644 --- a/drivers/scsi/hpsa.c +++ b/drivers/scsi/hpsa.c @@ -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); -- cgit v1.2.3-70-g09d2 From 9143a9612277abc6e4ddced2bc54a120734834c6 Mon Sep 17 00:00:00 2001 From: "scameron@beardog.cce.hp.com" Date: Mon, 7 Mar 2011 10:44:16 -0600 Subject: [SCSI] hpsa: fix incorrect PCI IDs and add two new ones (2nd try) My first attempt was botched, got the wrong PCI Device ID (used PCI_DEVICE_ID_HP_CISSE, should have been PCI_DEVICE_ID_HP_CISSF) Signed-off-by: Stephen M. Cameron Signed-off-by: James Bottomley --- drivers/scsi/hpsa.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) (limited to 'drivers/scsi/hpsa.c') diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c index c30591f84a0..09a529e3445 100644 --- a/drivers/scsi/hpsa.c +++ b/drivers/scsi/hpsa.c @@ -89,11 +89,13 @@ 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}, + {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSF, 0x103C, 0x3350}, + {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSF, 0x103C, 0x3351}, + {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSF, 0x103C, 0x3352}, + {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSF, 0x103C, 0x3353}, + {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSF, 0x103C, 0x3354}, + {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSF, 0x103C, 0x3355}, + {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSF, 0x103C, 0x3356}, {PCI_VENDOR_ID_HP, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_STORAGE_RAID << 8, 0xffff << 8, 0}, {0,} @@ -113,11 +115,13 @@ static struct board_type products[] = { {0x3249103C, "Smart Array P812", &SA5_access}, {0x324a103C, "Smart Array P712m", &SA5_access}, {0x324b103C, "Smart Array P711m", &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}, + {0x3350103C, "Smart Array", &SA5_access}, + {0x3351103C, "Smart Array", &SA5_access}, + {0x3352103C, "Smart Array", &SA5_access}, + {0x3353103C, "Smart Array", &SA5_access}, + {0x3354103C, "Smart Array", &SA5_access}, + {0x3355103C, "Smart Array", &SA5_access}, + {0x3356103C, "Smart Array", &SA5_access}, {0xFFFF103C, "Unknown Smart Array", &SA5_access}, }; -- cgit v1.2.3-70-g09d2 From 3f5eac3a040a2ea61a575f713aabedecdd23c3f8 Mon Sep 17 00:00:00 2001 From: "Stephen M. Cameron" Date: Wed, 9 Mar 2011 17:00:01 -0600 Subject: [SCSI] hpsa: move device attributes to avoid forward declarations Signed-off-by: Stephen M. Cameron Signed-off-by: James Bottomley --- drivers/scsi/hpsa.c | 253 +++++++++++++++++++++++++--------------------------- 1 file changed, 120 insertions(+), 133 deletions(-) (limited to 'drivers/scsi/hpsa.c') diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c index 09a529e3445..dcabef4bb14 100644 --- a/drivers/scsi/hpsa.c +++ b/drivers/scsi/hpsa.c @@ -155,21 +155,7 @@ static int hpsa_eh_device_reset_handler(struct scsi_cmnd *scsicmd); static int hpsa_slave_alloc(struct scsi_device *sdev); static void hpsa_slave_destroy(struct scsi_device *sdev); -static ssize_t raid_level_show(struct device *dev, - struct device_attribute *attr, char *buf); -static ssize_t lunid_show(struct device *dev, - struct device_attribute *attr, char *buf); -static ssize_t unique_id_show(struct device *dev, - struct device_attribute *attr, char *buf); -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); static int check_for_unit_attention(struct ctlr_info *h, struct CommandList *c); static void check_ioctl_unit_attention(struct ctlr_info *h, @@ -190,53 +176,6 @@ static int __devinit hpsa_wait_for_board_state(struct pci_dev *pdev, #define BOARD_NOT_READY 0 #define BOARD_READY 1 -static DEVICE_ATTR(raid_level, S_IRUGO, raid_level_show, NULL); -static DEVICE_ATTR(lunid, S_IRUGO, lunid_show, NULL); -static DEVICE_ATTR(unique_id, S_IRUGO, unique_id_show, NULL); -static DEVICE_ATTR(rescan, S_IWUSR, NULL, host_store_rescan); -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, - &dev_attr_lunid, - &dev_attr_unique_id, - NULL, -}; - -static struct device_attribute *hpsa_shost_attrs[] = { - &dev_attr_rescan, - &dev_attr_firmware_revision, - &dev_attr_commands_outstanding, - &dev_attr_transport_mode, - NULL, -}; - -static struct scsi_host_template hpsa_driver_template = { - .module = THIS_MODULE, - .name = "hpsa", - .proc_name = "hpsa", - .queuecommand = hpsa_scsi_queue_command, - .scan_start = hpsa_scan_start, - .scan_finished = hpsa_scan_finished, - .change_queue_depth = hpsa_change_queue_depth, - .this_id = -1, - .use_clustering = ENABLE_CLUSTERING, - .eh_device_reset_handler = hpsa_eh_device_reset_handler, - .ioctl = hpsa_ioctl, - .slave_alloc = hpsa_slave_alloc, - .slave_destroy = hpsa_slave_destroy, -#ifdef CONFIG_COMPAT - .compat_ioctl = hpsa_compat_ioctl, -#endif - .sdev_attrs = hpsa_sdev_attrs, - .shost_attrs = hpsa_shost_attrs, -}; - static inline struct ctlr_info *sdev_to_hba(struct scsi_device *sdev) { unsigned long *priv = shost_priv(sdev->host); @@ -334,83 +273,11 @@ static ssize_t host_show_transport_mode(struct device *dev, "performant" : "simple"); } -/* Enqueuing and dequeuing functions for cmdlists. */ -static inline void addQ(struct list_head *list, struct CommandList *c) -{ - list_add_tail(&c->list, list); -} - -static inline u32 next_command(struct ctlr_info *h) -{ - u32 a; - - if (unlikely(!(h->transMethod & CFGTBL_Trans_Performant))) - return h->access.command_completed(h); - - if ((*(h->reply_pool_head) & 1) == (h->reply_pool_wraparound)) { - a = *(h->reply_pool_head); /* Next cmd in ring buffer */ - (h->reply_pool_head)++; - h->commands_outstanding--; - } else { - a = 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; - } - return a; -} - -/* set_performant_mode: Modify the tag for cciss performant - * set bit 0 for pull model, bits 3-1 for block fetch - * register number - */ -static void set_performant_mode(struct ctlr_info *h, struct CommandList *c) -{ - if (likely(h->transMethod & CFGTBL_Trans_Performant)) - c->busaddr |= 1 | (h->blockFetchTable[c->Header.SGList] << 1); -} - -static void enqueue_cmd_and_start_io(struct ctlr_info *h, - struct CommandList *c) -{ - unsigned long flags; - - set_performant_mode(h, c); - spin_lock_irqsave(&h->lock, flags); - addQ(&h->reqQ, c); - h->Qdepth++; - start_io(h); - spin_unlock_irqrestore(&h->lock, flags); -} - -static inline void removeQ(struct CommandList *c) -{ - if (WARN_ON(list_empty(&c->list))) - return; - list_del_init(&c->list); -} - -static inline int is_hba_lunid(unsigned char scsi3addr[]) -{ - return memcmp(scsi3addr, RAID_CTLR_LUNID, 8) == 0; -} - static inline int is_logical_dev_addr_mode(unsigned char scsi3addr[]) { return (scsi3addr[3] & 0xC0) == 0x40; } -static inline int is_scsi_rev_5(struct ctlr_info *h) -{ - if (!h->hba_inquiry_data) - return 0; - if ((h->hba_inquiry_data[2] & 0x07) == 5) - return 1; - return 0; -} - static const char *raid_label[] = { "0", "4", "1(1+0)", "5", "5+1", "ADG", "UNKNOWN" }; @@ -502,6 +369,126 @@ static ssize_t unique_id_show(struct device *dev, sn[12], sn[13], sn[14], sn[15]); } +static DEVICE_ATTR(raid_level, S_IRUGO, raid_level_show, NULL); +static DEVICE_ATTR(lunid, S_IRUGO, lunid_show, NULL); +static DEVICE_ATTR(unique_id, S_IRUGO, unique_id_show, NULL); +static DEVICE_ATTR(rescan, S_IWUSR, NULL, host_store_rescan); +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, + &dev_attr_lunid, + &dev_attr_unique_id, + NULL, +}; + +static struct device_attribute *hpsa_shost_attrs[] = { + &dev_attr_rescan, + &dev_attr_firmware_revision, + &dev_attr_commands_outstanding, + &dev_attr_transport_mode, + NULL, +}; + +static struct scsi_host_template hpsa_driver_template = { + .module = THIS_MODULE, + .name = "hpsa", + .proc_name = "hpsa", + .queuecommand = hpsa_scsi_queue_command, + .scan_start = hpsa_scan_start, + .scan_finished = hpsa_scan_finished, + .change_queue_depth = hpsa_change_queue_depth, + .this_id = -1, + .use_clustering = ENABLE_CLUSTERING, + .eh_device_reset_handler = hpsa_eh_device_reset_handler, + .ioctl = hpsa_ioctl, + .slave_alloc = hpsa_slave_alloc, + .slave_destroy = hpsa_slave_destroy, +#ifdef CONFIG_COMPAT + .compat_ioctl = hpsa_compat_ioctl, +#endif + .sdev_attrs = hpsa_sdev_attrs, + .shost_attrs = hpsa_shost_attrs, +}; + + +/* Enqueuing and dequeuing functions for cmdlists. */ +static inline void addQ(struct list_head *list, struct CommandList *c) +{ + list_add_tail(&c->list, list); +} + +static inline u32 next_command(struct ctlr_info *h) +{ + u32 a; + + if (unlikely(!(h->transMethod & CFGTBL_Trans_Performant))) + return h->access.command_completed(h); + + if ((*(h->reply_pool_head) & 1) == (h->reply_pool_wraparound)) { + a = *(h->reply_pool_head); /* Next cmd in ring buffer */ + (h->reply_pool_head)++; + h->commands_outstanding--; + } else { + a = 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; + } + return a; +} + +/* set_performant_mode: Modify the tag for cciss performant + * set bit 0 for pull model, bits 3-1 for block fetch + * register number + */ +static void set_performant_mode(struct ctlr_info *h, struct CommandList *c) +{ + if (likely(h->transMethod & CFGTBL_Trans_Performant)) + c->busaddr |= 1 | (h->blockFetchTable[c->Header.SGList] << 1); +} + +static void enqueue_cmd_and_start_io(struct ctlr_info *h, + struct CommandList *c) +{ + unsigned long flags; + + set_performant_mode(h, c); + spin_lock_irqsave(&h->lock, flags); + addQ(&h->reqQ, c); + h->Qdepth++; + start_io(h); + spin_unlock_irqrestore(&h->lock, flags); +} + +static inline void removeQ(struct CommandList *c) +{ + if (WARN_ON(list_empty(&c->list))) + return; + list_del_init(&c->list); +} + +static inline int is_hba_lunid(unsigned char scsi3addr[]) +{ + return memcmp(scsi3addr, RAID_CTLR_LUNID, 8) == 0; +} + +static inline int is_scsi_rev_5(struct ctlr_info *h) +{ + if (!h->hba_inquiry_data) + return 0; + if ((h->hba_inquiry_data[2] & 0x07) == 5) + return 1; + return 0; +} + static int hpsa_find_target_lun(struct ctlr_info *h, unsigned char scsi3addr[], int bus, int *target, int *lun) { -- cgit v1.2.3-70-g09d2 From 941b1cdae83039c99fc5c1884a98d2afd39760e5 Mon Sep 17 00:00:00 2001 From: "Stephen M. Cameron" Date: Wed, 9 Mar 2011 17:00:06 -0600 Subject: [SCSI] hpsa: export resettable host attribute This attribute, requested by Redhat, allows kexec-tools to know whether the controller can honor the reset_devices kernel parameter and actually reset the controller. For kdump to work properly it is necessary that the reset_devices parameter be honored. This attribute enables kexec-tools to warn the user if they attempt to designate a non-resettable controller as the dump device. Signed-off-by: Stephen M. Cameron Signed-off-by: James Bottomley --- Documentation/scsi/hpsa.txt | 12 ++++++++++++ drivers/scsi/hpsa.c | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) (limited to 'drivers/scsi/hpsa.c') diff --git a/Documentation/scsi/hpsa.txt b/Documentation/scsi/hpsa.txt index 880c085b951..891435a72fc 100644 --- a/Documentation/scsi/hpsa.txt +++ b/Documentation/scsi/hpsa.txt @@ -45,6 +45,7 @@ HPSA specific entries in /sys /sys/class/scsi_host/host*/rescan /sys/class/scsi_host/host*/firmware_revision + /sys/class/scsi_host/host*/resettable /sys/class/scsi_host/host*/transport_mode the host "rescan" attribute is a write only attribute. Writing to this @@ -66,6 +67,17 @@ HPSA specific entries in /sys or "simple" mode. This is controlled by the "hpsa_simple_mode" module parameter. + The "resettable" read-only attribute indicates whether a particular + controller is able to honor the "reset_devices" kernel parameter. If the + device is resettable, this file will contain a "1", otherwise, a "0". This + parameter is used by kdump, for example, to reset the controller at driver + load time to eliminate any outstanding commands on the controller and get the + controller into a known state so that the kdump initiated i/o will work right + and not be disrupted in any way by stale commands or other stale state + remaining on the controller from the previous kernel. This attribute enables + kexec tools to warn the user if they attempt to designate a device which is + unable to honor the reset_devices kernel parameter as a dump device. + HPSA specific disk attributes: ------------------------------ diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c index dcabef4bb14..415ad4fb50d 100644 --- a/drivers/scsi/hpsa.c +++ b/drivers/scsi/hpsa.c @@ -273,6 +273,44 @@ static ssize_t host_show_transport_mode(struct device *dev, "performant" : "simple"); } +/* List of controllers which cannot be reset on kexec with reset_devices */ +static u32 unresettable_controller[] = { + 0x324a103C, /* Smart Array P712m */ + 0x324b103C, /* SmartArray P711m */ + 0x3223103C, /* Smart Array P800 */ + 0x3234103C, /* Smart Array P400 */ + 0x3235103C, /* Smart Array P400i */ + 0x3211103C, /* Smart Array E200i */ + 0x3212103C, /* Smart Array E200 */ + 0x3213103C, /* Smart Array E200i */ + 0x3214103C, /* Smart Array E200i */ + 0x3215103C, /* Smart Array E200i */ + 0x3237103C, /* Smart Array E500 */ + 0x323D103C, /* Smart Array P700m */ + 0x409C0E11, /* Smart Array 6400 */ + 0x409D0E11, /* Smart Array 6400 EM */ +}; + +static int ctlr_is_resettable(struct ctlr_info *h) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(unresettable_controller); i++) + if (unresettable_controller[i] == h->board_id) + return 0; + return 1; +} + +static ssize_t host_show_resettable(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, "%d\n", ctlr_is_resettable(h)); +} + static inline int is_logical_dev_addr_mode(unsigned char scsi3addr[]) { return (scsi3addr[3] & 0xC0) == 0x40; @@ -379,6 +417,8 @@ 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 DEVICE_ATTR(resettable, S_IRUGO, + host_show_resettable, NULL); static struct device_attribute *hpsa_sdev_attrs[] = { &dev_attr_raid_level, @@ -392,6 +432,7 @@ static struct device_attribute *hpsa_shost_attrs[] = { &dev_attr_firmware_revision, &dev_attr_commands_outstanding, &dev_attr_transport_mode, + &dev_attr_resettable, NULL, }; -- cgit v1.2.3-70-g09d2