Commit 720ba12620ee09dce269adf4ad50958adac7bb54
1 parent
3e706399b0
Exists in
master
and in
7 other branches
[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
drivers/scsi/ahci.c
... | ... | @@ -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); |