Commit 720ba12620ee09dce269adf4ad50958adac7bb54

Authored by Tejun Heo
1 parent 3e706399b0

[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>

Showing 4 changed files with 71 additions and 15 deletions Side-by-side Diff

... ... @@ -1389,21 +1389,17 @@
1389 1389 struct device *dev = pci_dev_to_dev(pdev);
1390 1390 struct ata_host_set *host_set = dev_get_drvdata(dev);
1391 1391 struct ahci_host_priv *hpriv = host_set->private_data;
1392   - struct ata_port *ap;
1393 1392 unsigned int i;
1394 1393 int have_msi;
1395 1394  
1396   - for (i = 0; i < host_set->n_ports; i++) {
1397   - ap = host_set->ports[i];
  1395 + for (i = 0; i < host_set->n_ports; i++)
  1396 + ata_port_detach(host_set->ports[i]);
1398 1397  
1399   - scsi_remove_host(ap->host);
1400   - }
1401   -
1402 1398 have_msi = hpriv->flags & AHCI_FLAG_MSI;
1403 1399 free_irq(host_set->irq, host_set);
1404 1400  
1405 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 1404 ata_scsi_release(ap->host);
1409 1405 scsi_host_put(ap->host);
drivers/scsi/libata-core.c
... ... @@ -5610,6 +5610,63 @@
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 5670 * ata_host_set_remove - PCI layer callback for device removal
5614 5671 * @host_set: ATA host set that was removed
5615 5672 *
5616 5673  
5617 5674  
... ... @@ -5622,18 +5679,15 @@
5622 5679  
5623 5680 void ata_host_set_remove(struct ata_host_set *host_set)
5624 5681 {
5625   - struct ata_port *ap;
5626 5682 unsigned int i;
5627 5683  
5628   - for (i = 0; i < host_set->n_ports; i++) {
5629   - ap = host_set->ports[i];
5630   - scsi_remove_host(ap->host);
5631   - }
  5684 + for (i = 0; i < host_set->n_ports; i++)
  5685 + ata_port_detach(host_set->ports[i]);
5632 5686  
5633 5687 free_irq(host_set->irq, host_set);
5634 5688  
5635 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 5692 ata_scsi_release(ap->host);
5639 5693  
... ... @@ -5901,6 +5955,7 @@
5901 5955 EXPORT_SYMBOL_GPL(ata_std_bios_param);
5902 5956 EXPORT_SYMBOL_GPL(ata_std_ports);
5903 5957 EXPORT_SYMBOL_GPL(ata_device_add);
  5958 +EXPORT_SYMBOL_GPL(ata_port_detach);
5904 5959 EXPORT_SYMBOL_GPL(ata_host_set_remove);
5905 5960 EXPORT_SYMBOL_GPL(ata_sg_init);
5906 5961 EXPORT_SYMBOL_GPL(ata_sg_init_one);
drivers/scsi/libata-eh.c
... ... @@ -46,6 +46,7 @@
46 46 #include "libata.h"
47 47  
48 48 static void __ata_port_freeze(struct ata_port *ap);
  49 +static void ata_eh_finish(struct ata_port *ap);
49 50  
50 51 static void ata_ering_record(struct ata_ering *ering, int is_io,
51 52 unsigned int err_mask)
... ... @@ -242,8 +243,11 @@
242 243  
243 244 spin_unlock_irqrestore(hs_lock, flags);
244 245  
245   - /* invoke EH */
246   - ap->ops->error_handler(ap);
  246 + /* invoke EH. if unloading, just finish failed qcs */
  247 + if (!(ap->flags & ATA_FLAG_UNLOADING))
  248 + ap->ops->error_handler(ap);
  249 + else
  250 + ata_eh_finish(ap);
247 251  
248 252 /* Exception might have happend after ->error_handler
249 253 * recovered the port but before this point. Repeat
include/linux/libata.h
... ... @@ -649,6 +649,7 @@
649 649 extern int ata_pci_clear_simplex(struct pci_dev *pdev);
650 650 #endif /* CONFIG_PCI */
651 651 extern int ata_device_add(const struct ata_probe_ent *ent);
  652 +extern void ata_port_detach(struct ata_port *ap);
652 653 extern void ata_host_set_remove(struct ata_host_set *host_set);
653 654 extern int ata_scsi_detect(struct scsi_host_template *sht);
654 655 extern int ata_scsi_ioctl(struct scsi_device *dev, int cmd, void __user *arg);