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 | |
| 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>
| -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 | ||||
| -rw-r--r-- | include/linux/libata.h | 1 |
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 | */ | ||
| 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 |
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); | |||
| 649 | extern int ata_pci_clear_simplex(struct pci_dev *pdev); | 649 | extern int ata_pci_clear_simplex(struct pci_dev *pdev); |
| 650 | #endif /* CONFIG_PCI */ | 650 | #endif /* CONFIG_PCI */ |
| 651 | extern int ata_device_add(const struct ata_probe_ent *ent); | 651 | extern int ata_device_add(const struct ata_probe_ent *ent); |
| 652 | extern void ata_port_detach(struct ata_port *ap); | ||
| 652 | extern void ata_host_set_remove(struct ata_host_set *host_set); | 653 | extern void ata_host_set_remove(struct ata_host_set *host_set); |
| 653 | extern int ata_scsi_detect(struct scsi_host_template *sht); | 654 | extern int ata_scsi_detect(struct scsi_host_template *sht); |
| 654 | extern int ata_scsi_ioctl(struct scsi_device *dev, int cmd, void __user *arg); | 655 | extern int ata_scsi_ioctl(struct scsi_device *dev, int cmd, void __user *arg); |
