aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTejun Heo <htejun@gmail.com>2006-05-31 05:28:13 -0400
committerTejun Heo <htejun@gmail.com>2006-05-31 05:28:13 -0400
commit720ba12620ee09dce269adf4ad50958adac7bb54 (patch)
treef31a8b3da52195610bd244baf42a5fe2e26b0a36
parent3e706399b03bd237d087d731d4b1b029e546b33d (diff)
[PATCH] libata-hp: update unload-unplug
Update unload unplug - driver unloading / PCI removal. This is done by ata_port_detach() which short-circuits EH, disables all devices and freezes the port. With this patch, EH and unloading/unplugging are properly synchronized. Signed-off-by: Tejun Heo <htejun@gmail.com>
-rw-r--r--drivers/scsi/ahci.c10
-rw-r--r--drivers/scsi/libata-core.c67
-rw-r--r--drivers/scsi/libata-eh.c8
-rw-r--r--include/linux/libata.h1
4 files changed, 71 insertions, 15 deletions
diff --git a/drivers/scsi/ahci.c b/drivers/scsi/ahci.c
index afb3805f9e95..60f455bf3696 100644
--- a/drivers/scsi/ahci.c
+++ b/drivers/scsi/ahci.c
@@ -1389,21 +1389,17 @@ static void ahci_remove_one (struct pci_dev *pdev)
1389 struct device *dev = pci_dev_to_dev(pdev); 1389 struct device *dev = pci_dev_to_dev(pdev);
1390 struct ata_host_set *host_set = dev_get_drvdata(dev); 1390 struct ata_host_set *host_set = dev_get_drvdata(dev);
1391 struct ahci_host_priv *hpriv = host_set->private_data; 1391 struct ahci_host_priv *hpriv = host_set->private_data;
1392 struct ata_port *ap;
1393 unsigned int i; 1392 unsigned int i;
1394 int have_msi; 1393 int have_msi;
1395 1394
1396 for (i = 0; i < host_set->n_ports; i++) { 1395 for (i = 0; i < host_set->n_ports; i++)
1397 ap = host_set->ports[i]; 1396 ata_port_detach(host_set->ports[i]);
1398
1399 scsi_remove_host(ap->host);
1400 }
1401 1397
1402 have_msi = hpriv->flags & AHCI_FLAG_MSI; 1398 have_msi = hpriv->flags & AHCI_FLAG_MSI;
1403 free_irq(host_set->irq, host_set); 1399 free_irq(host_set->irq, host_set);
1404 1400
1405 for (i = 0; i < host_set->n_ports; i++) { 1401 for (i = 0; i < host_set->n_ports; i++) {
1406 ap = host_set->ports[i]; 1402 struct ata_port *ap = host_set->ports[i];
1407 1403
1408 ata_scsi_release(ap->host); 1404 ata_scsi_release(ap->host);
1409 scsi_host_put(ap->host); 1405 scsi_host_put(ap->host);
diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c
index a42877e6b865..01f2c59536bc 100644
--- a/drivers/scsi/libata-core.c
+++ b/drivers/scsi/libata-core.c
@@ -5610,6 +5610,63 @@ err_free_ret:
5610} 5610}
5611 5611
5612/** 5612/**
5613 * ata_port_detach - Detach ATA port in prepration of device removal
5614 * @ap: ATA port to be detached
5615 *
5616 * Detach all ATA devices and the associated SCSI devices of @ap;
5617 * then, remove the associated SCSI host. @ap is guaranteed to
5618 * be quiescent on return from this function.
5619 *
5620 * LOCKING:
5621 * Kernel thread context (may sleep).
5622 */
5623void ata_port_detach(struct ata_port *ap)
5624{
5625 unsigned long flags;
5626 int i;
5627
5628 if (!ap->ops->error_handler)
5629 return;
5630
5631 /* tell EH we're leaving & flush EH */
5632 spin_lock_irqsave(&ap->host_set->lock, flags);
5633 ap->flags |= ATA_FLAG_UNLOADING;
5634 spin_unlock_irqrestore(&ap->host_set->lock, flags);
5635
5636 ata_port_wait_eh(ap);
5637
5638 /* EH is now guaranteed to see UNLOADING, so no new device
5639 * will be attached. Disable all existing devices.
5640 */
5641 spin_lock_irqsave(&ap->host_set->lock, flags);
5642
5643 for (i = 0; i < ATA_MAX_DEVICES; i++)
5644 ata_dev_disable(&ap->device[i]);
5645
5646 spin_unlock_irqrestore(&ap->host_set->lock, flags);
5647
5648 /* Final freeze & EH. All in-flight commands are aborted. EH
5649 * will be skipped and retrials will be terminated with bad
5650 * target.
5651 */
5652 spin_lock_irqsave(&ap->host_set->lock, flags);
5653 ata_port_freeze(ap); /* won't be thawed */
5654 spin_unlock_irqrestore(&ap->host_set->lock, flags);
5655
5656 ata_port_wait_eh(ap);
5657
5658 /* Flush hotplug task. The sequence is similar to
5659 * ata_port_flush_task().
5660 */
5661 flush_workqueue(ata_aux_wq);
5662 cancel_delayed_work(&ap->hotplug_task);
5663 flush_workqueue(ata_aux_wq);
5664
5665 /* remove the associated SCSI host */
5666 scsi_remove_host(ap->host);
5667}
5668
5669/**
5613 * ata_host_set_remove - PCI layer callback for device removal 5670 * ata_host_set_remove - PCI layer callback for device removal
5614 * @host_set: ATA host set that was removed 5671 * @host_set: ATA host set that was removed
5615 * 5672 *
@@ -5622,18 +5679,15 @@ err_free_ret:
5622 5679
5623void ata_host_set_remove(struct ata_host_set *host_set) 5680void ata_host_set_remove(struct ata_host_set *host_set)
5624{ 5681{
5625 struct ata_port *ap;
5626 unsigned int i; 5682 unsigned int i;
5627 5683
5628 for (i = 0; i < host_set->n_ports; i++) { 5684 for (i = 0; i < host_set->n_ports; i++)
5629 ap = host_set->ports[i]; 5685 ata_port_detach(host_set->ports[i]);
5630 scsi_remove_host(ap->host);
5631 }
5632 5686
5633 free_irq(host_set->irq, host_set); 5687 free_irq(host_set->irq, host_set);
5634 5688
5635 for (i = 0; i < host_set->n_ports; i++) { 5689 for (i = 0; i < host_set->n_ports; i++) {
5636 ap = host_set->ports[i]; 5690 struct ata_port *ap = host_set->ports[i];
5637 5691
5638 ata_scsi_release(ap->host); 5692 ata_scsi_release(ap->host);
5639 5693
@@ -5901,6 +5955,7 @@ EXPORT_SYMBOL_GPL(sata_deb_timing_before_fsrst);
5901EXPORT_SYMBOL_GPL(ata_std_bios_param); 5955EXPORT_SYMBOL_GPL(ata_std_bios_param);
5902EXPORT_SYMBOL_GPL(ata_std_ports); 5956EXPORT_SYMBOL_GPL(ata_std_ports);
5903EXPORT_SYMBOL_GPL(ata_device_add); 5957EXPORT_SYMBOL_GPL(ata_device_add);
5958EXPORT_SYMBOL_GPL(ata_port_detach);
5904EXPORT_SYMBOL_GPL(ata_host_set_remove); 5959EXPORT_SYMBOL_GPL(ata_host_set_remove);
5905EXPORT_SYMBOL_GPL(ata_sg_init); 5960EXPORT_SYMBOL_GPL(ata_sg_init);
5906EXPORT_SYMBOL_GPL(ata_sg_init_one); 5961EXPORT_SYMBOL_GPL(ata_sg_init_one);
diff --git a/drivers/scsi/libata-eh.c b/drivers/scsi/libata-eh.c
index 70c132bef68e..30a83a57a12f 100644
--- a/drivers/scsi/libata-eh.c
+++ b/drivers/scsi/libata-eh.c
@@ -46,6 +46,7 @@
46#include "libata.h" 46#include "libata.h"
47 47
48static void __ata_port_freeze(struct ata_port *ap); 48static void __ata_port_freeze(struct ata_port *ap);
49static void ata_eh_finish(struct ata_port *ap);
49 50
50static void ata_ering_record(struct ata_ering *ering, int is_io, 51static void ata_ering_record(struct ata_ering *ering, int is_io,
51 unsigned int err_mask) 52 unsigned int err_mask)
@@ -242,8 +243,11 @@ void ata_scsi_error(struct Scsi_Host *host)
242 243
243 spin_unlock_irqrestore(hs_lock, flags); 244 spin_unlock_irqrestore(hs_lock, flags);
244 245
245 /* invoke EH */ 246 /* invoke EH. if unloading, just finish failed qcs */
246 ap->ops->error_handler(ap); 247 if (!(ap->flags & ATA_FLAG_UNLOADING))
248 ap->ops->error_handler(ap);
249 else
250 ata_eh_finish(ap);
247 251
248 /* Exception might have happend after ->error_handler 252 /* Exception might have happend after ->error_handler
249 * recovered the port but before this point. Repeat 253 * recovered the port but before this point. Repeat
diff --git a/include/linux/libata.h b/include/linux/libata.h
index 74786c33c526..f11ba2715bef 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -649,6 +649,7 @@ extern int ata_pci_device_resume(struct pci_dev *pdev);
649extern int ata_pci_clear_simplex(struct pci_dev *pdev); 649extern int ata_pci_clear_simplex(struct pci_dev *pdev);
650#endif /* CONFIG_PCI */ 650#endif /* CONFIG_PCI */
651extern int ata_device_add(const struct ata_probe_ent *ent); 651extern int ata_device_add(const struct ata_probe_ent *ent);
652extern void ata_port_detach(struct ata_port *ap);
652extern void ata_host_set_remove(struct ata_host_set *host_set); 653extern void ata_host_set_remove(struct ata_host_set *host_set);
653extern int ata_scsi_detect(struct scsi_host_template *sht); 654extern int ata_scsi_detect(struct scsi_host_template *sht);
654extern int ata_scsi_ioctl(struct scsi_device *dev, int cmd, void __user *arg); 655extern int ata_scsi_ioctl(struct scsi_device *dev, int cmd, void __user *arg);