diff options
author | Tejun Heo <tj@kernel.org> | 2008-11-03 06:04:37 -0500 |
---|---|---|
committer | Jeff Garzik <jgarzik@redhat.com> | 2008-12-28 22:43:21 -0500 |
commit | ece180d1cfe5fa751eaa85bf796cf28b2150af15 (patch) | |
tree | aca9d485036858ed3f1859e679473cebd3476845 /drivers/ata/libata-eh.c | |
parent | ad74e4c18d0962397314460d0da312e72c8bd02d (diff) |
libata: perform port detach in EH
ata_port_detach() first made sure EH saw ATA_PFLAG_UNLOADING and then
assumed EH context belongs to it and performed detach operation
itself. However, UNLOADING doesn't disable all of EH and this could
lead to problems including triggering WARN_ON()'s in EH path.
This patch makes port detach behave more like other EH actions such
that ata_port_detach() requests EH to detach and waits for completion.
Signed-off-by: Tejun Heo <tj@kernel.org>
Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
Diffstat (limited to 'drivers/ata/libata-eh.c')
-rw-r--r-- | drivers/ata/libata-eh.c | 32 |
1 files changed, 31 insertions, 1 deletions
diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index d673f3712bd..8147a838637 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c | |||
@@ -491,6 +491,31 @@ enum blk_eh_timer_return ata_scsi_timed_out(struct scsi_cmnd *cmd) | |||
491 | return ret; | 491 | return ret; |
492 | } | 492 | } |
493 | 493 | ||
494 | static void ata_eh_unload(struct ata_port *ap) | ||
495 | { | ||
496 | struct ata_link *link; | ||
497 | struct ata_device *dev; | ||
498 | unsigned long flags; | ||
499 | |||
500 | /* Restore SControl IPM and SPD for the next driver and | ||
501 | * disable attached devices. | ||
502 | */ | ||
503 | ata_for_each_link(link, ap, PMP_FIRST) { | ||
504 | sata_scr_write(link, SCR_CONTROL, link->saved_scontrol & 0xff0); | ||
505 | ata_for_each_dev(dev, link, ALL) | ||
506 | ata_dev_disable(dev); | ||
507 | } | ||
508 | |||
509 | /* freeze and set UNLOADED */ | ||
510 | spin_lock_irqsave(ap->lock, flags); | ||
511 | |||
512 | ata_port_freeze(ap); /* won't be thawed */ | ||
513 | ap->pflags &= ~ATA_PFLAG_EH_PENDING; /* clear pending from freeze */ | ||
514 | ap->pflags |= ATA_PFLAG_UNLOADED; | ||
515 | |||
516 | spin_unlock_irqrestore(ap->lock, flags); | ||
517 | } | ||
518 | |||
494 | /** | 519 | /** |
495 | * ata_scsi_error - SCSI layer error handler callback | 520 | * ata_scsi_error - SCSI layer error handler callback |
496 | * @host: SCSI host on which error occurred | 521 | * @host: SCSI host on which error occurred |
@@ -618,8 +643,13 @@ void ata_scsi_error(struct Scsi_Host *host) | |||
618 | /* invoke EH, skip if unloading or suspended */ | 643 | /* invoke EH, skip if unloading or suspended */ |
619 | if (!(ap->pflags & (ATA_PFLAG_UNLOADING | ATA_PFLAG_SUSPENDED))) | 644 | if (!(ap->pflags & (ATA_PFLAG_UNLOADING | ATA_PFLAG_SUSPENDED))) |
620 | ap->ops->error_handler(ap); | 645 | ap->ops->error_handler(ap); |
621 | else | 646 | else { |
647 | /* if unloading, commence suicide */ | ||
648 | if ((ap->pflags & ATA_PFLAG_UNLOADING) && | ||
649 | !(ap->pflags & ATA_PFLAG_UNLOADED)) | ||
650 | ata_eh_unload(ap); | ||
622 | ata_eh_finish(ap); | 651 | ata_eh_finish(ap); |
652 | } | ||
623 | 653 | ||
624 | /* process port suspend request */ | 654 | /* process port suspend request */ |
625 | ata_eh_handle_port_suspend(ap); | 655 | ata_eh_handle_port_suspend(ap); |