Commit ecbcffe3 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull ata fix from Damien Le Moal:

 - Avoid deadlocks on resume from sleep by delaying scsi rescan until
   the scsi device is also fully resumed.

* tag 'ata-6.4-rc7' of git://git.kernel.org/pub/scm/linux/kernel/git/dlemoal/libata:
  ata: libata-scsi: Avoid deadlock on rescan after device resume
parents 1dbbfe25 6aa0365a
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -5348,7 +5348,7 @@ struct ata_port *ata_port_alloc(struct ata_host *host)

	mutex_init(&ap->scsi_scan_mutex);
	INIT_DELAYED_WORK(&ap->hotplug_task, ata_scsi_hotplug);
	INIT_WORK(&ap->scsi_rescan_task, ata_scsi_dev_rescan);
	INIT_DELAYED_WORK(&ap->scsi_rescan_task, ata_scsi_dev_rescan);
	INIT_LIST_HEAD(&ap->eh_done_q);
	init_waitqueue_head(&ap->eh_wait_q);
	init_completion(&ap->park_req_pending);
@@ -5954,6 +5954,7 @@ static void ata_port_detach(struct ata_port *ap)
	WARN_ON(!(ap->pflags & ATA_PFLAG_UNLOADED));

	cancel_delayed_work_sync(&ap->hotplug_task);
	cancel_delayed_work_sync(&ap->scsi_rescan_task);

 skip_eh:
	/* clean up zpodd on port removal */
+1 −1
Original line number Diff line number Diff line
@@ -2984,7 +2984,7 @@ static int ata_eh_revalidate_and_attach(struct ata_link *link,
			ehc->i.flags |= ATA_EHI_SETMODE;

			/* schedule the scsi_rescan_device() here */
			schedule_work(&(ap->scsi_rescan_task));
			schedule_delayed_work(&ap->scsi_rescan_task, 0);
		} else if (dev->class == ATA_DEV_UNKNOWN &&
			   ehc->tries[dev->devno] &&
			   ata_class_enabled(ehc->classes[dev->devno])) {
+21 −1
Original line number Diff line number Diff line
@@ -4597,10 +4597,11 @@ int ata_scsi_user_scan(struct Scsi_Host *shost, unsigned int channel,
void ata_scsi_dev_rescan(struct work_struct *work)
{
	struct ata_port *ap =
		container_of(work, struct ata_port, scsi_rescan_task);
		container_of(work, struct ata_port, scsi_rescan_task.work);
	struct ata_link *link;
	struct ata_device *dev;
	unsigned long flags;
	bool delay_rescan = false;

	mutex_lock(&ap->scsi_scan_mutex);
	spin_lock_irqsave(ap->lock, flags);
@@ -4614,6 +4615,21 @@ void ata_scsi_dev_rescan(struct work_struct *work)
			if (scsi_device_get(sdev))
				continue;

			/*
			 * If the rescan work was scheduled because of a resume
			 * event, the port is already fully resumed, but the
			 * SCSI device may not yet be fully resumed. In such
			 * case, executing scsi_rescan_device() may cause a
			 * deadlock with the PM code on device_lock(). Prevent
			 * this by giving up and retrying rescan after a short
			 * delay.
			 */
			delay_rescan = sdev->sdev_gendev.power.is_suspended;
			if (delay_rescan) {
				scsi_device_put(sdev);
				break;
			}

			spin_unlock_irqrestore(ap->lock, flags);
			scsi_rescan_device(&(sdev->sdev_gendev));
			scsi_device_put(sdev);
@@ -4623,4 +4639,8 @@ void ata_scsi_dev_rescan(struct work_struct *work)

	spin_unlock_irqrestore(ap->lock, flags);
	mutex_unlock(&ap->scsi_scan_mutex);

	if (delay_rescan)
		schedule_delayed_work(&ap->scsi_rescan_task,
				      msecs_to_jiffies(5));
}
+1 −1
Original line number Diff line number Diff line
@@ -836,7 +836,7 @@ struct ata_port {

	struct mutex		scsi_scan_mutex;
	struct delayed_work	hotplug_task;
	struct work_struct	scsi_rescan_task;
	struct delayed_work	scsi_rescan_task;

	unsigned int		hsm_task_state;