Skip to content

Commit

Permalink
Merge tag 'ata-6.4-rc7' of git://git.kernel.org/pub/scm/linux/kernel/…
Browse files Browse the repository at this point in the history
…git/dlemoal/libata

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
  • Loading branch information
Linus Torvalds committed Jun 18, 2023
2 parents 1dbbfe2 + 6aa0365 commit ecbcffe
Show file tree
Hide file tree
Showing 4 changed files with 25 additions and 4 deletions.
3 changes: 2 additions & 1 deletion drivers/ata/libata-core.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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 */
Expand Down
2 changes: 1 addition & 1 deletion drivers/ata/libata-eh.c
Original file line number Diff line number Diff line change
Expand Up @@ -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])) {
Expand Down
22 changes: 21 additions & 1 deletion drivers/ata/libata-scsi.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
Expand All @@ -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));
}
2 changes: 1 addition & 1 deletion include/linux/libata.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down

0 comments on commit ecbcffe

Please sign in to comment.