git: 89ac48cf3ba1 - stable/13 - adaspindown: check disk power mode before sending IDLE command

From: Andriy Gapon <avg_at_FreeBSD.org>
Date: Fri, 21 Jan 2022 07:01:39 UTC
The branch stable/13 has been updated by avg:

URL: https://cgit.FreeBSD.org/src/commit/?id=89ac48cf3ba178074a638da75bbf7101576305c2

commit 89ac48cf3ba178074a638da75bbf7101576305c2
Author:     Andriy Gapon <avg@FreeBSD.org>
AuthorDate: 2021-12-24 09:02:22 +0000
Commit:     Andriy Gapon <avg@FreeBSD.org>
CommitDate: 2022-01-21 07:01:09 +0000

    adaspindown: check disk power mode before sending IDLE command
    
    If a disk is already in STANDBY mode, then setting IDLE mode can
    actually spin it up.
    
    (cherry picked from commit 15910dc0bcb526d575f8cc49efe1f98a5091c88e)
---
 sys/cam/ata/ata_da.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 47 insertions(+)

diff --git a/sys/cam/ata/ata_da.c b/sys/cam/ata/ata_da.c
index d49ef81f6009..5aa29174f71a 100644
--- a/sys/cam/ata/ata_da.c
+++ b/sys/cam/ata/ata_da.c
@@ -3576,6 +3576,7 @@ adaspindown(uint8_t cmd, int flags)
 	struct ada_softc *softc;
 	struct ccb_ataio local_ccb;
 	int error;
+	int mode;
 
 	CAM_PERIPH_FOREACH(periph, &adadriver) {
 		/* If we paniced with lock held - not recurse here. */
@@ -3591,6 +3592,52 @@ adaspindown(uint8_t cmd, int flags)
 			continue;
 		}
 
+		/*
+		 * Additionally check if we would spin up the drive instead of
+		 * spinning it down.
+		 */
+		if (cmd == ATA_IDLE_IMMEDIATE) {
+			memset(&local_ccb, 0, sizeof(local_ccb));
+			xpt_setup_ccb(&local_ccb.ccb_h, periph->path,
+			    CAM_PRIORITY_NORMAL);
+			local_ccb.ccb_h.ccb_state = ADA_CCB_DUMP;
+
+			cam_fill_ataio(&local_ccb, 0, NULL, CAM_DIR_NONE,
+			    0, NULL, 0, ada_default_timeout * 1000);
+			ata_28bit_cmd(&local_ccb, ATA_CHECK_POWER_MODE,
+			    0, 0, 0);
+			local_ccb.cmd.flags |= CAM_ATAIO_NEEDRESULT;
+
+			error = cam_periph_runccb((union ccb *)&local_ccb,
+			    adaerror, /*cam_flags*/0,
+			    /*sense_flags*/ SF_NO_RECOVERY | SF_NO_RETRY,
+			    softc->disk->d_devstat);
+			if (error != 0) {
+				xpt_print(periph->path,
+				    "Failed to read current power mode\n");
+			} else {
+				mode = local_ccb.res.sector_count;
+#ifdef DIAGNOSTIC
+				if (bootverbose) {
+					xpt_print(periph->path,
+					    "disk power mode 0x%02x\n", mode);
+				}
+#endif
+				switch (mode) {
+				case 0x00:
+				case 0x01:
+					if (bootverbose) {
+						xpt_print(periph->path,
+						    "already spun down\n");
+					}
+					cam_periph_unlock(periph);
+					continue;
+				default:
+					break;
+				}
+			}
+		}
+
 		if (bootverbose)
 			xpt_print(periph->path, "spin-down\n");