Commit 720ba126 authored by Tejun Heo's avatar Tejun Heo

[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: default avatarTejun Heo <htejun@gmail.com>
parent 3e706399
...@@ -1389,21 +1389,17 @@ static void ahci_remove_one (struct pci_dev *pdev) ...@@ -1389,21 +1389,17 @@ static void ahci_remove_one (struct pci_dev *pdev)
struct device *dev = pci_dev_to_dev(pdev); struct device *dev = pci_dev_to_dev(pdev);
struct ata_host_set *host_set = dev_get_drvdata(dev); struct ata_host_set *host_set = dev_get_drvdata(dev);
struct ahci_host_priv *hpriv = host_set->private_data; struct ahci_host_priv *hpriv = host_set->private_data;
struct ata_port *ap;
unsigned int i; unsigned int i;
int have_msi; int have_msi;
for (i = 0; i < host_set->n_ports; i++) { for (i = 0; i < host_set->n_ports; i++)
ap = host_set->ports[i]; ata_port_detach(host_set->ports[i]);
scsi_remove_host(ap->host);
}
have_msi = hpriv->flags & AHCI_FLAG_MSI; have_msi = hpriv->flags & AHCI_FLAG_MSI;
free_irq(host_set->irq, host_set); free_irq(host_set->irq, host_set);
for (i = 0; i < host_set->n_ports; i++) { for (i = 0; i < host_set->n_ports; i++) {
ap = host_set->ports[i]; struct ata_port *ap = host_set->ports[i];
ata_scsi_release(ap->host); ata_scsi_release(ap->host);
scsi_host_put(ap->host); scsi_host_put(ap->host);
......
...@@ -5609,6 +5609,63 @@ int ata_device_add(const struct ata_probe_ent *ent) ...@@ -5609,6 +5609,63 @@ int ata_device_add(const struct ata_probe_ent *ent)
return 0; return 0;
} }
/**
* ata_port_detach - Detach ATA port in prepration of device removal
* @ap: ATA port to be detached
*
* Detach all ATA devices and the associated SCSI devices of @ap;
* then, remove the associated SCSI host. @ap is guaranteed to
* be quiescent on return from this function.
*
* LOCKING:
* Kernel thread context (may sleep).
*/
void ata_port_detach(struct ata_port *ap)
{
unsigned long flags;
int i;
if (!ap->ops->error_handler)
return;
/* tell EH we're leaving & flush EH */
spin_lock_irqsave(&ap->host_set->lock, flags);
ap->flags |= ATA_FLAG_UNLOADING;
spin_unlock_irqrestore(&ap->host_set->lock, flags);
ata_port_wait_eh(ap);
/* EH is now guaranteed to see UNLOADING, so no new device
* will be attached. Disable all existing devices.
*/
spin_lock_irqsave(&ap->host_set->lock, flags);
for (i = 0; i < ATA_MAX_DEVICES; i++)
ata_dev_disable(&ap->device[i]);
spin_unlock_irqrestore(&ap->host_set->lock, flags);
/* Final freeze & EH. All in-flight commands are aborted. EH
* will be skipped and retrials will be terminated with bad
* target.
*/
spin_lock_irqsave(&ap->host_set->lock, flags);
ata_port_freeze(ap); /* won't be thawed */
spin_unlock_irqrestore(&ap->host_set->lock, flags);
ata_port_wait_eh(ap);
/* Flush hotplug task. The sequence is similar to
* ata_port_flush_task().
*/
flush_workqueue(ata_aux_wq);
cancel_delayed_work(&ap->hotplug_task);
flush_workqueue(ata_aux_wq);
/* remove the associated SCSI host */
scsi_remove_host(ap->host);
}
/** /**
* ata_host_set_remove - PCI layer callback for device removal * ata_host_set_remove - PCI layer callback for device removal
* @host_set: ATA host set that was removed * @host_set: ATA host set that was removed
...@@ -5622,18 +5679,15 @@ int ata_device_add(const struct ata_probe_ent *ent) ...@@ -5622,18 +5679,15 @@ int ata_device_add(const struct ata_probe_ent *ent)
void ata_host_set_remove(struct ata_host_set *host_set) void ata_host_set_remove(struct ata_host_set *host_set)
{ {
struct ata_port *ap;
unsigned int i; unsigned int i;
for (i = 0; i < host_set->n_ports; i++) { for (i = 0; i < host_set->n_ports; i++)
ap = host_set->ports[i]; ata_port_detach(host_set->ports[i]);
scsi_remove_host(ap->host);
}
free_irq(host_set->irq, host_set); free_irq(host_set->irq, host_set);
for (i = 0; i < host_set->n_ports; i++) { for (i = 0; i < host_set->n_ports; i++) {
ap = host_set->ports[i]; struct ata_port *ap = host_set->ports[i];
ata_scsi_release(ap->host); ata_scsi_release(ap->host);
...@@ -5901,6 +5955,7 @@ EXPORT_SYMBOL_GPL(sata_deb_timing_before_fsrst); ...@@ -5901,6 +5955,7 @@ EXPORT_SYMBOL_GPL(sata_deb_timing_before_fsrst);
EXPORT_SYMBOL_GPL(ata_std_bios_param); EXPORT_SYMBOL_GPL(ata_std_bios_param);
EXPORT_SYMBOL_GPL(ata_std_ports); EXPORT_SYMBOL_GPL(ata_std_ports);
EXPORT_SYMBOL_GPL(ata_device_add); EXPORT_SYMBOL_GPL(ata_device_add);
EXPORT_SYMBOL_GPL(ata_port_detach);
EXPORT_SYMBOL_GPL(ata_host_set_remove); EXPORT_SYMBOL_GPL(ata_host_set_remove);
EXPORT_SYMBOL_GPL(ata_sg_init); EXPORT_SYMBOL_GPL(ata_sg_init);
EXPORT_SYMBOL_GPL(ata_sg_init_one); EXPORT_SYMBOL_GPL(ata_sg_init_one);
......
...@@ -46,6 +46,7 @@ ...@@ -46,6 +46,7 @@
#include "libata.h" #include "libata.h"
static void __ata_port_freeze(struct ata_port *ap); static void __ata_port_freeze(struct ata_port *ap);
static void ata_eh_finish(struct ata_port *ap);
static void ata_ering_record(struct ata_ering *ering, int is_io, static void ata_ering_record(struct ata_ering *ering, int is_io,
unsigned int err_mask) unsigned int err_mask)
...@@ -242,8 +243,11 @@ void ata_scsi_error(struct Scsi_Host *host) ...@@ -242,8 +243,11 @@ void ata_scsi_error(struct Scsi_Host *host)
spin_unlock_irqrestore(hs_lock, flags); spin_unlock_irqrestore(hs_lock, flags);
/* invoke EH */ /* invoke EH. if unloading, just finish failed qcs */
ap->ops->error_handler(ap); if (!(ap->flags & ATA_FLAG_UNLOADING))
ap->ops->error_handler(ap);
else
ata_eh_finish(ap);
/* Exception might have happend after ->error_handler /* Exception might have happend after ->error_handler
* recovered the port but before this point. Repeat * recovered the port but before this point. Repeat
......
...@@ -649,6 +649,7 @@ extern int ata_pci_device_resume(struct pci_dev *pdev); ...@@ -649,6 +649,7 @@ extern int ata_pci_device_resume(struct pci_dev *pdev);
extern int ata_pci_clear_simplex(struct pci_dev *pdev); extern int ata_pci_clear_simplex(struct pci_dev *pdev);
#endif /* CONFIG_PCI */ #endif /* CONFIG_PCI */
extern int ata_device_add(const struct ata_probe_ent *ent); extern int ata_device_add(const struct ata_probe_ent *ent);
extern void ata_port_detach(struct ata_port *ap);
extern void ata_host_set_remove(struct ata_host_set *host_set); extern void ata_host_set_remove(struct ata_host_set *host_set);
extern int ata_scsi_detect(struct scsi_host_template *sht); extern int ata_scsi_detect(struct scsi_host_template *sht);
extern int ata_scsi_ioctl(struct scsi_device *dev, int cmd, void __user *arg); extern int ata_scsi_ioctl(struct scsi_device *dev, int cmd, void __user *arg);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment