diff options
author | Tejun Heo <htejun@gmail.com> | 2006-05-31 05:28:13 -0400 |
---|---|---|
committer | Tejun Heo <htejun@gmail.com> | 2006-05-31 05:28:13 -0400 |
commit | 720ba12620ee09dce269adf4ad50958adac7bb54 (patch) | |
tree | f31a8b3da52195610bd244baf42a5fe2e26b0a36 /drivers | |
parent | 3e706399b03bd237d087d731d4b1b029e546b33d (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>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/scsi/ahci.c | 10 | ||||
-rw-r--r-- | drivers/scsi/libata-core.c | 67 | ||||
-rw-r--r-- | drivers/scsi/libata-eh.c | 8 |
3 files changed, 70 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 | */ | ||
5623 | void 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 | ||
5623 | void ata_host_set_remove(struct ata_host_set *host_set) | 5680 | void 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); | |||
5901 | EXPORT_SYMBOL_GPL(ata_std_bios_param); | 5955 | EXPORT_SYMBOL_GPL(ata_std_bios_param); |
5902 | EXPORT_SYMBOL_GPL(ata_std_ports); | 5956 | EXPORT_SYMBOL_GPL(ata_std_ports); |
5903 | EXPORT_SYMBOL_GPL(ata_device_add); | 5957 | EXPORT_SYMBOL_GPL(ata_device_add); |
5958 | EXPORT_SYMBOL_GPL(ata_port_detach); | ||
5904 | EXPORT_SYMBOL_GPL(ata_host_set_remove); | 5959 | EXPORT_SYMBOL_GPL(ata_host_set_remove); |
5905 | EXPORT_SYMBOL_GPL(ata_sg_init); | 5960 | EXPORT_SYMBOL_GPL(ata_sg_init); |
5906 | EXPORT_SYMBOL_GPL(ata_sg_init_one); | 5961 | EXPORT_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 | ||
48 | static void __ata_port_freeze(struct ata_port *ap); | 48 | static void __ata_port_freeze(struct ata_port *ap); |
49 | static void ata_eh_finish(struct ata_port *ap); | ||
49 | 50 | ||
50 | static void ata_ering_record(struct ata_ering *ering, int is_io, | 51 | static 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 |