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-core.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-core.c')
-rw-r--r-- | drivers/ata/libata-core.c | 23 |
1 files changed, 4 insertions, 19 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: |