summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarkus Probst <markus.probst@posteo.de>2025-11-04 14:24:34 +0000
committerMartin K. Petersen <martin.petersen@oracle.com>2025-11-08 13:24:56 -0500
commit8c59fc1c90df7ff29565d9743f0e61180e9ee00d (patch)
tree35a8a0d0332c76fc433b398da923400df9fbafcc
parentce6d26b5330c5de4f458143e9675984e367f6a40 (diff)
scsi: ata: Stop disk on restart if ACPI power resources are found
Some embedded devices have the ability to control whether power is provided to the disks via the SATA power connector or not. ACPI power resources are usually off by default, thus making it unclear if the specific power resource will retain its state after a restart. If power resources are defined on ATA ports / devices in ACPI, we should stop the disk on SYSTEM_RESTART, to ensure the disk will not lose power while active. Add a new function, ata_acpi_dev_manage_restart(), that will be used to determine if a disk should be stopped before restarting the system. If a usable ACPI power resource has been found, it is assumed that the disk will lose power after a restart and should be stopped to avoid unclean shutdown due to power loss. Reviewed-by: Damien Le Moal <dlemoal@kernel.org> Signed-off-by: Markus Probst <markus.probst@posteo.de> Link: https://patch.msgid.link/20251104142413.322347-4-markus.probst@posteo.de Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
-rw-r--r--drivers/ata/libata-acpi.c26
-rw-r--r--drivers/ata/libata-scsi.c1
-rw-r--r--drivers/ata/libata.h2
3 files changed, 29 insertions, 0 deletions
diff --git a/drivers/ata/libata-acpi.c b/drivers/ata/libata-acpi.c
index 4782e0f22d7f..15e18d50dcc6 100644
--- a/drivers/ata/libata-acpi.c
+++ b/drivers/ata/libata-acpi.c
@@ -246,6 +246,32 @@ void ata_acpi_bind_dev(struct ata_device *dev)
}
/**
+ * ata_acpi_dev_manage_restart - if the disk should be stopped (spun down) on
+ * system restart.
+ * @dev: target ATA device
+ *
+ * RETURNS:
+ * true if the disk should be stopped, otherwise false.
+ */
+bool ata_acpi_dev_manage_restart(struct ata_device *dev)
+{
+ struct device *tdev;
+
+ /*
+ * If ATA_FLAG_ACPI_SATA is set, the acpi fwnode is attached to the
+ * ata_device instead of the ata_port.
+ */
+ if (dev->link->ap->flags & ATA_FLAG_ACPI_SATA)
+ tdev = &dev->tdev;
+ else
+ tdev = &dev->link->ap->tdev;
+
+ if (!is_acpi_device_node(tdev->fwnode))
+ return false;
+ return acpi_bus_power_manageable(ACPI_HANDLE(tdev));
+}
+
+/**
* ata_acpi_port_power_on - set the power state of the ata port to D0
* @ap: target ATA port
*
diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
index b43a3196e2be..026122bb6f2f 100644
--- a/drivers/ata/libata-scsi.c
+++ b/drivers/ata/libata-scsi.c
@@ -1095,6 +1095,7 @@ int ata_scsi_dev_config(struct scsi_device *sdev, struct queue_limits *lim,
*/
sdev->manage_runtime_start_stop = 1;
sdev->manage_shutdown = 1;
+ sdev->manage_restart = ata_acpi_dev_manage_restart(dev);
sdev->force_runtime_start_on_system_start = 1;
}
diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h
index 8cc7227f2d94..0e7ecac73680 100644
--- a/drivers/ata/libata.h
+++ b/drivers/ata/libata.h
@@ -131,6 +131,7 @@ extern void ata_acpi_set_state(struct ata_port *ap, pm_message_t state);
extern void ata_acpi_bind_port(struct ata_port *ap);
extern void ata_acpi_bind_dev(struct ata_device *dev);
extern void ata_acpi_port_power_on(struct ata_port *ap);
+extern bool ata_acpi_dev_manage_restart(struct ata_device *dev);
extern acpi_handle ata_dev_acpi_handle(struct ata_device *dev);
#else
static inline void ata_acpi_dissociate(struct ata_host *host) { }
@@ -142,6 +143,7 @@ static inline void ata_acpi_set_state(struct ata_port *ap,
static inline void ata_acpi_bind_port(struct ata_port *ap) {}
static inline void ata_acpi_bind_dev(struct ata_device *dev) {}
static inline void ata_acpi_port_power_on(struct ata_port *ap) {}
+static inline bool ata_acpi_dev_manage_restart(struct ata_device *dev) { return 0; }
#endif
/* libata-scsi.c */