mirror of https://gitee.com/openkylin/linux.git
[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>
This commit is contained in:
parent
3e706399b0
commit
720ba12620
|
@ -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)
|
||||||
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)
|
||||||
|
|
||||||
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);
|
||||||
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 @@
|
||||||
#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)
|
||||||
|
|
||||||
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);
|
||||||
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);
|
||||||
|
|
Loading…
Reference in New Issue