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 | |
| 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>
| -rw-r--r-- | drivers/ata/libata-core.c | 23 | ||||
| -rw-r--r-- | drivers/ata/libata-eh.c | 32 | ||||
| -rw-r--r-- | include/linux/libata.h | 3 |
3 files changed, 37 insertions, 21 deletions
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 1ecc3cb0b722..837fb60a6dcc 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c | |||
| @@ -6107,8 +6107,6 @@ int ata_host_activate(struct ata_host *host, int irq, | |||
| 6107 | static void ata_port_detach(struct ata_port *ap) | 6107 | static void ata_port_detach(struct ata_port *ap) |
| 6108 | { | 6108 | { |
| 6109 | unsigned long flags; | 6109 | unsigned long flags; |
| 6110 | struct ata_link *link; | ||
| 6111 | struct ata_device *dev; | ||
| 6112 | 6110 | ||
| 6113 | if (!ap->ops->error_handler) | 6111 | if (!ap->ops->error_handler) |
| 6114 | goto skip_eh; | 6112 | goto skip_eh; |
| @@ -6116,28 +6114,15 @@ static void ata_port_detach(struct ata_port *ap) | |||
| 6116 | /* tell EH we're leaving & flush EH */ | 6114 | /* tell EH we're leaving & flush EH */ |
| 6117 | spin_lock_irqsave(ap->lock, flags); | 6115 | spin_lock_irqsave(ap->lock, flags); |
| 6118 | ap->pflags |= ATA_PFLAG_UNLOADING; | 6116 | ap->pflags |= ATA_PFLAG_UNLOADING; |
| 6117 | ata_port_schedule_eh(ap); | ||
| 6119 | spin_unlock_irqrestore(ap->lock, flags); | 6118 | spin_unlock_irqrestore(ap->lock, flags); |
| 6120 | 6119 | ||
| 6120 | /* wait till EH commits suicide */ | ||
| 6121 | ata_port_wait_eh(ap); | 6121 | ata_port_wait_eh(ap); |
| 6122 | 6122 | ||
| 6123 | /* EH is now guaranteed to see UNLOADING - EH context belongs | 6123 | /* it better be dead now */ |
| 6124 | * to us. Restore SControl and disable all existing devices. | 6124 | WARN_ON(!(ap->pflags & ATA_PFLAG_UNLOADED)); |
| 6125 | */ | ||
| 6126 | ata_for_each_link(link, ap, PMP_FIRST) { | ||
| 6127 | sata_scr_write(link, SCR_CONTROL, link->saved_scontrol & 0xff0); | ||
| 6128 | ata_for_each_dev(dev, link, ALL) | ||
| 6129 | ata_dev_disable(dev); | ||
| 6130 | } | ||
| 6131 | |||
| 6132 | /* Final freeze & EH. All in-flight commands are aborted. EH | ||
| 6133 | * will be skipped and retrials will be terminated with bad | ||
| 6134 | * target. | ||
| 6135 | */ | ||
| 6136 | spin_lock_irqsave(ap->lock, flags); | ||
| 6137 | ata_port_freeze(ap); /* won't be thawed */ | ||
| 6138 | spin_unlock_irqrestore(ap->lock, flags); | ||
| 6139 | 6125 | ||
| 6140 | ata_port_wait_eh(ap); | ||
| 6141 | cancel_rearming_delayed_work(&ap->hotplug_task); | 6126 | cancel_rearming_delayed_work(&ap->hotplug_task); |
| 6142 | 6127 | ||
| 6143 | skip_eh: | 6128 | skip_eh: |
diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index d673f3712bdc..8147a8386370 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); |
diff --git a/include/linux/libata.h b/include/linux/libata.h index 3b2a0c6444ee..3449de597eff 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h | |||
| @@ -213,10 +213,11 @@ enum { | |||
| 213 | ATA_PFLAG_FROZEN = (1 << 2), /* port is frozen */ | 213 | ATA_PFLAG_FROZEN = (1 << 2), /* port is frozen */ |
| 214 | ATA_PFLAG_RECOVERED = (1 << 3), /* recovery action performed */ | 214 | ATA_PFLAG_RECOVERED = (1 << 3), /* recovery action performed */ |
| 215 | ATA_PFLAG_LOADING = (1 << 4), /* boot/loading probe */ | 215 | ATA_PFLAG_LOADING = (1 << 4), /* boot/loading probe */ |
| 216 | ATA_PFLAG_UNLOADING = (1 << 5), /* module is unloading */ | ||
| 217 | ATA_PFLAG_SCSI_HOTPLUG = (1 << 6), /* SCSI hotplug scheduled */ | 216 | ATA_PFLAG_SCSI_HOTPLUG = (1 << 6), /* SCSI hotplug scheduled */ |
| 218 | ATA_PFLAG_INITIALIZING = (1 << 7), /* being initialized, don't touch */ | 217 | ATA_PFLAG_INITIALIZING = (1 << 7), /* being initialized, don't touch */ |
| 219 | ATA_PFLAG_RESETTING = (1 << 8), /* reset in progress */ | 218 | ATA_PFLAG_RESETTING = (1 << 8), /* reset in progress */ |
| 219 | ATA_PFLAG_UNLOADING = (1 << 9), /* driver is being unloaded */ | ||
| 220 | ATA_PFLAG_UNLOADED = (1 << 10), /* driver is unloaded */ | ||
| 220 | 221 | ||
| 221 | ATA_PFLAG_SUSPENDED = (1 << 17), /* port is suspended (power) */ | 222 | ATA_PFLAG_SUSPENDED = (1 << 17), /* port is suspended (power) */ |
| 222 | ATA_PFLAG_PM_PENDING = (1 << 18), /* PM operation pending */ | 223 | ATA_PFLAG_PM_PENDING = (1 << 18), /* PM operation pending */ |
