Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

[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
Loading
Loading
Loading
Loading
+3 −7
Original line number Diff line number Diff line
@@ -1389,21 +1389,17 @@ static void ahci_remove_one (struct pci_dev *pdev)
	struct device *dev = pci_dev_to_dev(pdev);
	struct ata_host_set *host_set = dev_get_drvdata(dev);
	struct ahci_host_priv *hpriv = host_set->private_data;
	struct ata_port *ap;
	unsigned int i;
	int have_msi;

	for (i = 0; i < host_set->n_ports; i++) {
		ap = host_set->ports[i];

		scsi_remove_host(ap->host);
	}
	for (i = 0; i < host_set->n_ports; i++)
		ata_port_detach(host_set->ports[i]);

	have_msi = hpriv->flags & AHCI_FLAG_MSI;
	free_irq(host_set->irq, host_set);

	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);
		scsi_host_put(ap->host);
+61 −6
Original line number Diff line number Diff line
@@ -5609,6 +5609,63 @@ int ata_device_add(const struct ata_probe_ent *ent)
	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
 *	@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)
{
	struct ata_port *ap;
	unsigned int i;

	for (i = 0; i < host_set->n_ports; i++) {
		ap = host_set->ports[i];
		scsi_remove_host(ap->host);
	}
	for (i = 0; i < host_set->n_ports; i++)
		ata_port_detach(host_set->ports[i]);

	free_irq(host_set->irq, host_set);

	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);

@@ -5901,6 +5955,7 @@ EXPORT_SYMBOL_GPL(sata_deb_timing_before_fsrst);
EXPORT_SYMBOL_GPL(ata_std_bios_param);
EXPORT_SYMBOL_GPL(ata_std_ports);
EXPORT_SYMBOL_GPL(ata_device_add);
EXPORT_SYMBOL_GPL(ata_port_detach);
EXPORT_SYMBOL_GPL(ata_host_set_remove);
EXPORT_SYMBOL_GPL(ata_sg_init);
EXPORT_SYMBOL_GPL(ata_sg_init_one);
+6 −2
Original line number Diff line number Diff line
@@ -46,6 +46,7 @@
#include "libata.h"

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,
			     unsigned int err_mask)
@@ -242,8 +243,11 @@ void ata_scsi_error(struct Scsi_Host *host)

		spin_unlock_irqrestore(hs_lock, flags);

		/* invoke EH */
		/* invoke EH.  if unloading, just finish failed qcs */
		if (!(ap->flags & ATA_FLAG_UNLOADING))
			ap->ops->error_handler(ap);
		else
			ata_eh_finish(ap);

		/* Exception might have happend after ->error_handler
		 * recovered the port but before this point.  Repeat
+1 −0
Original line number Diff line number Diff line
@@ -649,6 +649,7 @@ extern int ata_pci_device_resume(struct pci_dev *pdev);
extern int ata_pci_clear_simplex(struct pci_dev *pdev);
#endif /* CONFIG_PCI */
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 int ata_scsi_detect(struct scsi_host_template *sht);
extern int ata_scsi_ioctl(struct scsi_device *dev, int cmd, void __user *arg);