mirror of
https://github.com/torvalds/linux.git
synced 2024-11-12 07:01:57 +00:00
cciss: do soft reset if hard reset is broken
on driver load, if reset_devices is set, and the hard reset attempts fail, try to bring up the controller to the point that a command can be sent, and send it a soft reset command, then after the reset undo whatever driver initialization was done to get it to the point to take a command, and re-do it after the reset. This is to get kdump to work on all the "non-resettable" controllers (except 64xx controllers which can't be reset due to the potentially shared cache module.) Signed-off-by: Stephen M. Cameron <scameron@beardog.cce.hp.com> Signed-off-by: Jens Axboe <jaxboe@fusionio.com>
This commit is contained in:
parent
bf2e2e6b87
commit
5afe278114
@ -2477,6 +2477,31 @@ static int deregister_disk(ctlr_info_t *h, int drv_index,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int __devinit cciss_send_reset(ctlr_info_t *h, unsigned char *scsi3addr,
|
||||||
|
u8 reset_type)
|
||||||
|
{
|
||||||
|
CommandList_struct *c;
|
||||||
|
int return_status;
|
||||||
|
|
||||||
|
c = cmd_alloc(h);
|
||||||
|
if (!c)
|
||||||
|
return -ENOMEM;
|
||||||
|
return_status = fill_cmd(h, c, CCISS_RESET_MSG, NULL, 0, 0,
|
||||||
|
CTLR_LUNID, TYPE_MSG);
|
||||||
|
c->Request.CDB[1] = reset_type; /* fill_cmd defaults to target reset */
|
||||||
|
if (return_status != IO_OK) {
|
||||||
|
cmd_special_free(h, c);
|
||||||
|
return return_status;
|
||||||
|
}
|
||||||
|
c->waiting = NULL;
|
||||||
|
enqueue_cmd_and_start_io(h, c);
|
||||||
|
/* Don't wait for completion, the reset won't complete. Don't free
|
||||||
|
* the command either. This is the last command we will send before
|
||||||
|
* re-initializing everything, so it doesn't matter and won't leak.
|
||||||
|
*/
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int fill_cmd(ctlr_info_t *h, CommandList_struct *c, __u8 cmd, void *buff,
|
static int fill_cmd(ctlr_info_t *h, CommandList_struct *c, __u8 cmd, void *buff,
|
||||||
size_t size, __u8 page_code, unsigned char *scsi3addr,
|
size_t size, __u8 page_code, unsigned char *scsi3addr,
|
||||||
int cmd_type)
|
int cmd_type)
|
||||||
@ -3463,6 +3488,63 @@ static inline u32 process_nonindexed_cmd(ctlr_info_t *h, u32 raw_tag)
|
|||||||
return next_command(h);
|
return next_command(h);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Some controllers, like p400, will give us one interrupt
|
||||||
|
* after a soft reset, even if we turned interrupts off.
|
||||||
|
* Only need to check for this in the cciss_xxx_discard_completions
|
||||||
|
* functions.
|
||||||
|
*/
|
||||||
|
static int ignore_bogus_interrupt(ctlr_info_t *h)
|
||||||
|
{
|
||||||
|
if (likely(!reset_devices))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (likely(h->interrupts_enabled))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
dev_info(&h->pdev->dev, "Received interrupt while interrupts disabled "
|
||||||
|
"(known firmware bug.) Ignoring.\n");
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static irqreturn_t cciss_intx_discard_completions(int irq, void *dev_id)
|
||||||
|
{
|
||||||
|
ctlr_info_t *h = dev_id;
|
||||||
|
unsigned long flags;
|
||||||
|
u32 raw_tag;
|
||||||
|
|
||||||
|
if (ignore_bogus_interrupt(h))
|
||||||
|
return IRQ_NONE;
|
||||||
|
|
||||||
|
if (interrupt_not_for_us(h))
|
||||||
|
return IRQ_NONE;
|
||||||
|
spin_lock_irqsave(&h->lock, flags);
|
||||||
|
while (interrupt_pending(h)) {
|
||||||
|
raw_tag = get_next_completion(h);
|
||||||
|
while (raw_tag != FIFO_EMPTY)
|
||||||
|
raw_tag = next_command(h);
|
||||||
|
}
|
||||||
|
spin_unlock_irqrestore(&h->lock, flags);
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static irqreturn_t cciss_msix_discard_completions(int irq, void *dev_id)
|
||||||
|
{
|
||||||
|
ctlr_info_t *h = dev_id;
|
||||||
|
unsigned long flags;
|
||||||
|
u32 raw_tag;
|
||||||
|
|
||||||
|
if (ignore_bogus_interrupt(h))
|
||||||
|
return IRQ_NONE;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&h->lock, flags);
|
||||||
|
raw_tag = get_next_completion(h);
|
||||||
|
while (raw_tag != FIFO_EMPTY)
|
||||||
|
raw_tag = next_command(h);
|
||||||
|
spin_unlock_irqrestore(&h->lock, flags);
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
static irqreturn_t do_cciss_intx(int irq, void *dev_id)
|
static irqreturn_t do_cciss_intx(int irq, void *dev_id)
|
||||||
{
|
{
|
||||||
ctlr_info_t *h = dev_id;
|
ctlr_info_t *h = dev_id;
|
||||||
@ -4380,7 +4462,6 @@ static __devinit int cciss_message(struct pci_dev *pdev, unsigned char opcode, u
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define cciss_soft_reset_controller(p) cciss_message(p, 1, 0)
|
|
||||||
#define cciss_noop(p) cciss_message(p, 3, 0)
|
#define cciss_noop(p) cciss_message(p, 3, 0)
|
||||||
|
|
||||||
static int cciss_controller_hard_reset(struct pci_dev *pdev,
|
static int cciss_controller_hard_reset(struct pci_dev *pdev,
|
||||||
@ -4591,13 +4672,17 @@ static __devinit int cciss_kdump_hard_reset_controller(struct pci_dev *pdev)
|
|||||||
/* Wait for board to become not ready, then ready. */
|
/* Wait for board to become not ready, then ready. */
|
||||||
dev_info(&pdev->dev, "Waiting for board to reset.\n");
|
dev_info(&pdev->dev, "Waiting for board to reset.\n");
|
||||||
rc = cciss_wait_for_board_state(pdev, vaddr, BOARD_NOT_READY);
|
rc = cciss_wait_for_board_state(pdev, vaddr, BOARD_NOT_READY);
|
||||||
if (rc) /* Don't bail, might be E500, etc. which can't be reset */
|
if (rc) {
|
||||||
dev_warn(&pdev->dev,
|
dev_warn(&pdev->dev, "Failed waiting for board to hard reset."
|
||||||
"failed waiting for board to reset\n");
|
" Will try soft reset.\n");
|
||||||
|
rc = -ENOTSUPP; /* Not expected, but try soft reset later */
|
||||||
|
goto unmap_cfgtable;
|
||||||
|
}
|
||||||
rc = cciss_wait_for_board_state(pdev, vaddr, BOARD_READY);
|
rc = cciss_wait_for_board_state(pdev, vaddr, BOARD_READY);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_warn(&pdev->dev,
|
dev_warn(&pdev->dev,
|
||||||
"failed waiting for board to become ready\n");
|
"failed waiting for board to become ready "
|
||||||
|
"after hard reset\n");
|
||||||
goto unmap_cfgtable;
|
goto unmap_cfgtable;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4605,16 +4690,13 @@ static __devinit int cciss_kdump_hard_reset_controller(struct pci_dev *pdev)
|
|||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
goto unmap_cfgtable;
|
goto unmap_cfgtable;
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_warn(&pdev->dev, "Unable to successfully reset controller,"
|
dev_warn(&pdev->dev, "Unable to successfully hard reset "
|
||||||
" Ignoring controller.\n");
|
"controller. Will try soft reset.\n");
|
||||||
rc = -ENODEV;
|
rc = -ENOTSUPP; /* Not expected, but try soft reset later */
|
||||||
goto unmap_cfgtable;
|
|
||||||
} else {
|
} else {
|
||||||
dev_info(&pdev->dev, "board ready.\n");
|
dev_info(&pdev->dev, "Board ready after hard reset.\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
dev_info(&pdev->dev, "board ready.\n");
|
|
||||||
|
|
||||||
unmap_cfgtable:
|
unmap_cfgtable:
|
||||||
iounmap(cfgtable);
|
iounmap(cfgtable);
|
||||||
|
|
||||||
@ -4639,7 +4721,7 @@ static __devinit int cciss_init_reset_devices(struct pci_dev *pdev)
|
|||||||
* due to concerns about shared bbwc between 6402/6404 pair.
|
* due to concerns about shared bbwc between 6402/6404 pair.
|
||||||
*/
|
*/
|
||||||
if (rc == -ENOTSUPP)
|
if (rc == -ENOTSUPP)
|
||||||
return 0; /* just try to do the kdump anyhow. */
|
return rc; /* just try to do the kdump anyhow. */
|
||||||
if (rc)
|
if (rc)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
@ -4745,6 +4827,60 @@ static int cciss_request_irq(ctlr_info_t *h,
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int __devinit cciss_kdump_soft_reset(ctlr_info_t *h)
|
||||||
|
{
|
||||||
|
if (cciss_send_reset(h, CTLR_LUNID, CCISS_RESET_TYPE_CONTROLLER)) {
|
||||||
|
dev_warn(&h->pdev->dev, "Resetting array controller failed.\n");
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_info(&h->pdev->dev, "Waiting for board to soft reset.\n");
|
||||||
|
if (cciss_wait_for_board_state(h->pdev, h->vaddr, BOARD_NOT_READY)) {
|
||||||
|
dev_warn(&h->pdev->dev, "Soft reset had no effect.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_info(&h->pdev->dev, "Board reset, awaiting READY status.\n");
|
||||||
|
if (cciss_wait_for_board_state(h->pdev, h->vaddr, BOARD_READY)) {
|
||||||
|
dev_warn(&h->pdev->dev, "Board failed to become ready "
|
||||||
|
"after soft reset.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cciss_undo_allocations_after_kdump_soft_reset(ctlr_info_t *h)
|
||||||
|
{
|
||||||
|
int ctlr = h->ctlr;
|
||||||
|
|
||||||
|
free_irq(h->intr[PERF_MODE_INT], h);
|
||||||
|
#ifdef CONFIG_PCI_MSI
|
||||||
|
if (h->msix_vector)
|
||||||
|
pci_disable_msix(h->pdev);
|
||||||
|
else if (h->msi_vector)
|
||||||
|
pci_disable_msi(h->pdev);
|
||||||
|
#endif /* CONFIG_PCI_MSI */
|
||||||
|
cciss_free_sg_chain_blocks(h->cmd_sg_list, h->nr_cmds);
|
||||||
|
cciss_free_scatterlists(h);
|
||||||
|
cciss_free_cmd_pool(h);
|
||||||
|
kfree(h->blockFetchTable);
|
||||||
|
if (h->reply_pool)
|
||||||
|
pci_free_consistent(h->pdev, h->max_commands * sizeof(__u64),
|
||||||
|
h->reply_pool, h->reply_pool_dhandle);
|
||||||
|
if (h->transtable)
|
||||||
|
iounmap(h->transtable);
|
||||||
|
if (h->cfgtable)
|
||||||
|
iounmap(h->cfgtable);
|
||||||
|
if (h->vaddr)
|
||||||
|
iounmap(h->vaddr);
|
||||||
|
unregister_blkdev(h->major, h->devname);
|
||||||
|
cciss_destroy_hba_sysfs_entry(h);
|
||||||
|
pci_release_regions(h->pdev);
|
||||||
|
kfree(h);
|
||||||
|
hba[ctlr] = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This is it. Find all the controllers and register them. I really hate
|
* This is it. Find all the controllers and register them. I really hate
|
||||||
* stealing all these major device numbers.
|
* stealing all these major device numbers.
|
||||||
@ -4756,13 +4892,27 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
|
|||||||
int i;
|
int i;
|
||||||
int j = 0;
|
int j = 0;
|
||||||
int rc;
|
int rc;
|
||||||
|
int try_soft_reset = 0;
|
||||||
int dac, return_code;
|
int dac, return_code;
|
||||||
InquiryData_struct *inq_buff;
|
InquiryData_struct *inq_buff;
|
||||||
ctlr_info_t *h;
|
ctlr_info_t *h;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
rc = cciss_init_reset_devices(pdev);
|
rc = cciss_init_reset_devices(pdev);
|
||||||
if (rc)
|
if (rc) {
|
||||||
return rc;
|
if (rc != -ENOTSUPP)
|
||||||
|
return rc;
|
||||||
|
/* If the reset fails in a particular way (it has no way to do
|
||||||
|
* a proper hard reset, so returns -ENOTSUPP) we can try to do
|
||||||
|
* a soft reset once we get the controller configured up to the
|
||||||
|
* point that it can accept a command.
|
||||||
|
*/
|
||||||
|
try_soft_reset = 1;
|
||||||
|
rc = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
reinit_after_soft_reset:
|
||||||
|
|
||||||
i = alloc_cciss_hba(pdev);
|
i = alloc_cciss_hba(pdev);
|
||||||
if (i < 0)
|
if (i < 0)
|
||||||
return -1;
|
return -1;
|
||||||
@ -4852,6 +5002,62 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
|
|||||||
h->gendisk[j] = NULL;
|
h->gendisk[j] = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* At this point, the controller is ready to take commands.
|
||||||
|
* Now, if reset_devices and the hard reset didn't work, try
|
||||||
|
* the soft reset and see if that works.
|
||||||
|
*/
|
||||||
|
if (try_soft_reset) {
|
||||||
|
|
||||||
|
/* This is kind of gross. We may or may not get a completion
|
||||||
|
* from the soft reset command, and if we do, then the value
|
||||||
|
* from the fifo may or may not be valid. So, we wait 10 secs
|
||||||
|
* after the reset throwing away any completions we get during
|
||||||
|
* that time. Unregister the interrupt handler and register
|
||||||
|
* fake ones to scoop up any residual completions.
|
||||||
|
*/
|
||||||
|
spin_lock_irqsave(&h->lock, flags);
|
||||||
|
h->access.set_intr_mask(h, CCISS_INTR_OFF);
|
||||||
|
spin_unlock_irqrestore(&h->lock, flags);
|
||||||
|
free_irq(h->intr[PERF_MODE_INT], h);
|
||||||
|
rc = cciss_request_irq(h, cciss_msix_discard_completions,
|
||||||
|
cciss_intx_discard_completions);
|
||||||
|
if (rc) {
|
||||||
|
dev_warn(&h->pdev->dev, "Failed to request_irq after "
|
||||||
|
"soft reset.\n");
|
||||||
|
goto clean4;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = cciss_kdump_soft_reset(h);
|
||||||
|
if (rc) {
|
||||||
|
dev_warn(&h->pdev->dev, "Soft reset failed.\n");
|
||||||
|
goto clean4;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_info(&h->pdev->dev, "Board READY.\n");
|
||||||
|
dev_info(&h->pdev->dev,
|
||||||
|
"Waiting for stale completions to drain.\n");
|
||||||
|
h->access.set_intr_mask(h, CCISS_INTR_ON);
|
||||||
|
msleep(10000);
|
||||||
|
h->access.set_intr_mask(h, CCISS_INTR_OFF);
|
||||||
|
|
||||||
|
rc = controller_reset_failed(h->cfgtable);
|
||||||
|
if (rc)
|
||||||
|
dev_info(&h->pdev->dev,
|
||||||
|
"Soft reset appears to have failed.\n");
|
||||||
|
|
||||||
|
/* since the controller's reset, we have to go back and re-init
|
||||||
|
* everything. Easiest to just forget what we've done and do it
|
||||||
|
* all over again.
|
||||||
|
*/
|
||||||
|
cciss_undo_allocations_after_kdump_soft_reset(h);
|
||||||
|
try_soft_reset = 0;
|
||||||
|
if (rc)
|
||||||
|
/* don't go to clean4, we already unallocated */
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
goto reinit_after_soft_reset;
|
||||||
|
}
|
||||||
|
|
||||||
cciss_scsi_setup(h);
|
cciss_scsi_setup(h);
|
||||||
|
|
||||||
/* Turn the interrupts on so we can service requests */
|
/* Turn the interrupts on so we can service requests */
|
||||||
|
Loading…
Reference in New Issue
Block a user