Skip to content
This repository was archived by the owner on Nov 8, 2023. It is now read-only.

Commit 0c76106

Browse files
damien-lemoalmartinkpetersen
authored andcommitted
scsi: sd: Fix TCG OPAL unlock on system resume
Commit 3cc2ffe ("scsi: sd: Differentiate system and runtime start/stop management") introduced the manage_system_start_stop scsi_device flag to allow libata to indicate to the SCSI disk driver that nothing should be done when resuming a disk on system resume. This change turned the execution of sd_resume() into a no-op for ATA devices on system resume. While this solved deadlock issues during device resume, this change also wrongly removed the execution of opal_unlock_from_suspend(). As a result, devices with TCG OPAL locking enabled remain locked and inaccessible after a system resume from sleep. To fix this issue, introduce the SCSI driver resume method and implement it with the sd_resume() function calling opal_unlock_from_suspend(). The former sd_resume() function is renamed to sd_resume_common() and modified to call the new sd_resume() function. For non-ATA devices, this result in no functional changes. In order for libata to explicitly execute sd_resume() when a device is resumed during system restart, the function scsi_resume_device() is introduced. libata calls this function from the revalidation work executed on devie resume, a state that is indicated with the new device flag ATA_DFLAG_RESUMING. Doing so, locked TCG OPAL enabled devices are unlocked on resume, allowing normal operation. Fixes: 3cc2ffe ("scsi: sd: Differentiate system and runtime start/stop management") Link: https://bugzilla.kernel.org/show_bug.cgi?id=218538 Cc: stable@vger.kernel.org Signed-off-by: Damien Le Moal <dlemoal@kernel.org> Link: https://lore.kernel.org/r/20240319071209.1179257-1-dlemoal@kernel.org Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
1 parent 27f58c0 commit 0c76106

File tree

7 files changed

+69
-5
lines changed

7 files changed

+69
-5
lines changed

drivers/ata/libata-eh.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -712,8 +712,10 @@ void ata_scsi_port_error_handler(struct Scsi_Host *host, struct ata_port *ap)
712712
ehc->saved_ncq_enabled |= 1 << devno;
713713

714714
/* If we are resuming, wake up the device */
715-
if (ap->pflags & ATA_PFLAG_RESUMING)
715+
if (ap->pflags & ATA_PFLAG_RESUMING) {
716+
dev->flags |= ATA_DFLAG_RESUMING;
716717
ehc->i.dev_action[devno] |= ATA_EH_SET_ACTIVE;
718+
}
717719
}
718720
}
719721

@@ -3169,6 +3171,7 @@ static int ata_eh_revalidate_and_attach(struct ata_link *link,
31693171
return 0;
31703172

31713173
err:
3174+
dev->flags &= ~ATA_DFLAG_RESUMING;
31723175
*r_failed_dev = dev;
31733176
return rc;
31743177
}

drivers/ata/libata-scsi.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4730,6 +4730,7 @@ void ata_scsi_dev_rescan(struct work_struct *work)
47304730
struct ata_link *link;
47314731
struct ata_device *dev;
47324732
unsigned long flags;
4733+
bool do_resume;
47334734
int ret = 0;
47344735

47354736
mutex_lock(&ap->scsi_scan_mutex);
@@ -4751,7 +4752,15 @@ void ata_scsi_dev_rescan(struct work_struct *work)
47514752
if (scsi_device_get(sdev))
47524753
continue;
47534754

4755+
do_resume = dev->flags & ATA_DFLAG_RESUMING;
4756+
47544757
spin_unlock_irqrestore(ap->lock, flags);
4758+
if (do_resume) {
4759+
ret = scsi_resume_device(sdev);
4760+
if (ret == -EWOULDBLOCK)
4761+
goto unlock;
4762+
dev->flags &= ~ATA_DFLAG_RESUMING;
4763+
}
47554764
ret = scsi_rescan_device(sdev);
47564765
scsi_device_put(sdev);
47574766
spin_lock_irqsave(ap->lock, flags);

drivers/scsi/scsi_scan.c

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1642,6 +1642,40 @@ int scsi_add_device(struct Scsi_Host *host, uint channel,
16421642
}
16431643
EXPORT_SYMBOL(scsi_add_device);
16441644

1645+
int scsi_resume_device(struct scsi_device *sdev)
1646+
{
1647+
struct device *dev = &sdev->sdev_gendev;
1648+
int ret = 0;
1649+
1650+
device_lock(dev);
1651+
1652+
/*
1653+
* Bail out if the device or its queue are not running. Otherwise,
1654+
* the rescan may block waiting for commands to be executed, with us
1655+
* holding the device lock. This can result in a potential deadlock
1656+
* in the power management core code when system resume is on-going.
1657+
*/
1658+
if (sdev->sdev_state != SDEV_RUNNING ||
1659+
blk_queue_pm_only(sdev->request_queue)) {
1660+
ret = -EWOULDBLOCK;
1661+
goto unlock;
1662+
}
1663+
1664+
if (dev->driver && try_module_get(dev->driver->owner)) {
1665+
struct scsi_driver *drv = to_scsi_driver(dev->driver);
1666+
1667+
if (drv->resume)
1668+
ret = drv->resume(dev);
1669+
module_put(dev->driver->owner);
1670+
}
1671+
1672+
unlock:
1673+
device_unlock(dev);
1674+
1675+
return ret;
1676+
}
1677+
EXPORT_SYMBOL(scsi_resume_device);
1678+
16451679
int scsi_rescan_device(struct scsi_device *sdev)
16461680
{
16471681
struct device *dev = &sdev->sdev_gendev;

drivers/scsi/sd.c

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4108,7 +4108,21 @@ static int sd_suspend_runtime(struct device *dev)
41084108
return sd_suspend_common(dev, true);
41094109
}
41104110

4111-
static int sd_resume(struct device *dev, bool runtime)
4111+
static int sd_resume(struct device *dev)
4112+
{
4113+
struct scsi_disk *sdkp = dev_get_drvdata(dev);
4114+
4115+
sd_printk(KERN_NOTICE, sdkp, "Starting disk\n");
4116+
4117+
if (opal_unlock_from_suspend(sdkp->opal_dev)) {
4118+
sd_printk(KERN_NOTICE, sdkp, "OPAL unlock failed\n");
4119+
return -EIO;
4120+
}
4121+
4122+
return 0;
4123+
}
4124+
4125+
static int sd_resume_common(struct device *dev, bool runtime)
41124126
{
41134127
struct scsi_disk *sdkp = dev_get_drvdata(dev);
41144128
int ret;
@@ -4124,7 +4138,7 @@ static int sd_resume(struct device *dev, bool runtime)
41244138
sd_printk(KERN_NOTICE, sdkp, "Starting disk\n");
41254139
ret = sd_start_stop_device(sdkp, 1);
41264140
if (!ret) {
4127-
opal_unlock_from_suspend(sdkp->opal_dev);
4141+
sd_resume(dev);
41284142
sdkp->suspended = false;
41294143
}
41304144

@@ -4143,7 +4157,7 @@ static int sd_resume_system(struct device *dev)
41434157
return 0;
41444158
}
41454159

4146-
return sd_resume(dev, false);
4160+
return sd_resume_common(dev, false);
41474161
}
41484162

41494163
static int sd_resume_runtime(struct device *dev)
@@ -4170,7 +4184,7 @@ static int sd_resume_runtime(struct device *dev)
41704184
"Failed to clear sense data\n");
41714185
}
41724186

4173-
return sd_resume(dev, true);
4187+
return sd_resume_common(dev, true);
41744188
}
41754189

41764190
static const struct dev_pm_ops sd_pm_ops = {
@@ -4193,6 +4207,7 @@ static struct scsi_driver sd_template = {
41934207
.pm = &sd_pm_ops,
41944208
},
41954209
.rescan = sd_rescan,
4210+
.resume = sd_resume,
41964211
.init_command = sd_init_command,
41974212
.uninit_command = sd_uninit_command,
41984213
.done = sd_done,

include/linux/libata.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ enum {
107107

108108
ATA_DFLAG_NCQ_PRIO_ENABLED = (1 << 20), /* Priority cmds sent to dev */
109109
ATA_DFLAG_CDL_ENABLED = (1 << 21), /* cmd duration limits is enabled */
110+
ATA_DFLAG_RESUMING = (1 << 22), /* Device is resuming */
110111
ATA_DFLAG_DETACH = (1 << 24),
111112
ATA_DFLAG_DETACHED = (1 << 25),
112113
ATA_DFLAG_DA = (1 << 26), /* device supports Device Attention */

include/scsi/scsi_driver.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ struct request;
1212
struct scsi_driver {
1313
struct device_driver gendrv;
1414

15+
int (*resume)(struct device *);
1516
void (*rescan)(struct device *);
1617
blk_status_t (*init_command)(struct scsi_cmnd *);
1718
void (*uninit_command)(struct scsi_cmnd *);

include/scsi/scsi_host.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -767,6 +767,7 @@ scsi_template_proc_dir(const struct scsi_host_template *sht);
767767
#define scsi_template_proc_dir(sht) NULL
768768
#endif
769769
extern void scsi_scan_host(struct Scsi_Host *);
770+
extern int scsi_resume_device(struct scsi_device *sdev);
770771
extern int scsi_rescan_device(struct scsi_device *sdev);
771772
extern void scsi_remove_host(struct Scsi_Host *);
772773
extern struct Scsi_Host *scsi_host_get(struct Scsi_Host *);

0 commit comments

Comments
 (0)