mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	[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
					
				
					 4 changed files with 71 additions and 15 deletions
				
			
		| 
						 | 
					@ -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 a new issue