aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTejun Heo <tj@kernel.org>2008-11-03 06:04:37 -0500
committerJeff Garzik <jgarzik@redhat.com>2008-12-28 22:43:21 -0500
commitece180d1cfe5fa751eaa85bf796cf28b2150af15 (patch)
treeaca9d485036858ed3f1859e679473cebd3476845
parentad74e4c18d0962397314460d0da312e72c8bd02d (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.c23
-rw-r--r--drivers/ata/libata-eh.c32
-rw-r--r--include/linux/libata.h3
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,
6107static void ata_port_detach(struct ata_port *ap) 6107static 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
494static 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 */