HDD Power-Up in Standby: prevent from spinning up

The linux kernel spins up the drive. Take a look at these lines from drivers/ata/libata-core.c (kernel source code):

if (!tried_spinup && (id[2] == 0x37c8 || id[2] == 0x738c)) {
    tried_spinup = 1;
    /*
     * Drive powered-up in standby mode, and requires a specific
     * SET_FEATURES spin-up subcommand before it will accept
     * anything other than the original IDENTIFY command.
     */
    err_mask = ata_dev_set_feature(dev, SETFEATURES_SPINUP, 0);
    if (err_mask && id[2] != 0x738c) {
        rc = -EIO;
        reason = "SPINUP failed";
        goto err_out;
    }
    /*
     * If the drive initially returned incomplete IDENTIFY info,
     * we now must reissue the IDENTIFY command.
     */
    if (id[2] == 0x37c8)
        goto retry;
}

If you comment these lines and recompile the kernel, the drives will not spin up. Then you'll need a command to spin them up, for example when hdparm disables PUIS it spins up the drive. Take a look at this link.

That's all I know about PUIS.

Edit: I just noticed that your drive spins up before the grub screen: this means the motherboard is spinning up the drive. You can try disabling the corresponding sata port in your BIOS/UEFI configuration, if it allows such a thing, and try again. If it's working, the drive will remain still until the kernel spins it up, after the grub screen and before the user login prompt, and you can find in dmesg

ataX.00: failed to IDENTIFY (SPINUP failed, err_mask=0x4)
ataX.00: revalidation failed (errno=-5)
ataX: SATA link up 6.0 Gbps (SStatus 133 SControl 300)

At this point, if you hack the kernel, the drive will not spin up at all like I described earlier.

Edit 2: I found a better command to spin up the disk:

sg_sat_set_features --feature=7 /dev/sdX

it's part of the sg3_utils package, requires root privileges, but spins up the disk nicely. Post updated on the arch linux forum, that's my final solution for now. A small summary of that post:

  • if your PUIS enabled disk spins up before boot loader screen, try disabling the corresponding sata port, or try a PCI-ex sata controller card
  • recompile the kernel to disable the command that spins up the disks in PUIS state
  • use sg_sat_set_feature to spin up the disk
  • rescan sata port to gain access to partitions

Edit 3: Some kind soul wrote a patch, on the archlinux forum: https://bbs.archlinux.org/viewtopic.php?pid=1855326#p1855326

Transcription:

If we can't avoid patching libata, might as well disable PUIS drives on boot to get rid of endless error messages. The downside is we've to tell the kernel to re-enable them on request, since userspace tools like sg_sat* expect an entry inside /dev.

Take a look at my tentative patch for that feature. I hope someone will consider sparing his time to rework it to kernel standards and propose it upstream. I wrote the patch against clean v4.19.56.

Remember to set "libata.spinup_control=0" kernel parameter in the bootloader after recompiling the module and rebuilding your initramfs image!

Then you should

echo 1 > /sys/module/libata/parameters/spinup_control

and issue a rescan to the drive you want to spin up.

echo '- - -' > devices/pci0000:00/0000:00:1f.2/ata4/host3/scsi_host/host3/scan

--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -171,6 +171,10 @@ static int atapi_an;
 module_param(atapi_an, int, 0444);
 MODULE_PARM_DESC(atapi_an, "Enable ATAPI AN media presence notification (0=0ff [default], 1=on)");

+static int spinup_control = 1;
+module_param(spinup_control, int, 0644);
+MODULE_PARM_DESC(spinup_control, "Spin up standby drives (0=PUIS drives disabled, 1=standby drives can spin up [default])");
+
 MODULE_AUTHOR("Jeff Garzik");
 MODULE_DESCRIPTION("Library module for ATA devices");
 MODULE_LICENSE("GPL");
@@ -1978,28 +1982,40 @@ retry:
            goto err_out;
    }

-   if (!tried_spinup && (id[2] == 0x37c8 || id[2] == 0x738c)) {
+   /*
+    * My drives indicate with 0x738c that media is ready when PUIS
+    * is enabled, in conflict with the relevant standards.
+    * The compliant behavior therefore prevents spun-up and ready
+    * drives from being recognized on reboot.
+    * I had no choice but to remove "|| id[2] == 0x738c))".
+    */
+   if (!tried_spinup && (id[2] == 0x37c8)) {
        tried_spinup = 1;
        /*
         * Drive powered-up in standby mode, and requires a specific
         * SET_FEATURES spin-up subcommand before it will accept
         * anything other than the original IDENTIFY command.
         */
-       err_mask = ata_dev_set_feature(dev, SETFEATURES_SPINUP, 0);
-       if (err_mask && id[2] != 0x738c) {
-           rc = -EIO;
-           reason = "SPINUP failed";
-           goto err_out;
-       }
-       /*
-        * If the drive initially returned incomplete IDENTIFY info,
-        * we now must reissue the IDENTIFY command.
-        */
-       if (id[2] == 0x37c8)
+       if (spinup_control) {
+           err_mask = ata_dev_set_feature(dev, SETFEATURES_SPINUP, 0);
+           if (err_mask) {
+               rc = -EIO;
+               reason = "SPINUP failed";
+               goto err_out;
+           }
+           /*
+            * If the drive initially returned incomplete IDENTIFY info,
+            * we now must reissue the IDENTIFY command.
+            */
            goto retry;
+       } else {
+           dev->horkage |= ATA_HORKAGE_DISABLE;
+           ata_dev_notice(dev, "horkage modified (drive powered-up in standby)\n");
+       }
    }

-   if ((flags & ATA_READID_POSTRESET) &&
+   if (spinup_control && (flags & ATA_READID_POSTRESET) &&
        (class == ATA_DEV_ATA || class == ATA_DEV_ZAC)) {
        /*
         * The exact sequence expected by certain pre-ATA4 drives is:

Credit to az12shareart, who registerd to arch linux forum just to write this, I think.


I searched for the exact thing a few years ago, for Linux Mint and an old HD that was only used for occasional data storage too.

The solutions I found (don't have the links handy anymore) were the same that it looks like you found: a few hard drives might have a jumper setting that should cause the drive to stay sleeping / not spin up at boot time. But it did not work, my results were the exact same as yours, it still spins up at boot. I didn't find any fixes for that, for all I know it was the BIOS/GRUB/linux, separately or working together, or just the HD itself that wasn't listening.

I did some "hot plug"/"hot swap" testing, connecting the power to a (SATA) hard drive while the computer was up & running. It generated some log entries (dmesg & /var/log/syslog) and worked successfully. Then when done with the drive (sync, unmount, sleep/hdparm -y) unplugging the power again. Worked! But it apparently needs a compatible motherboard & OS, so YMMV.

However, pulling the power plug to use the drive isn't very convenient or easy, so I wired up a double-pole single-throw switch - DPST, Wikipedia has a diagram - has 4 terminals, for the 2 separate power wires (12V & 5V?), to keep them separate and turn them both on/off at the same time. Connecting it to the HD's power, I can turn on & off the drive whenever needed.

Update:

Hot swapping used to work on Linux Mint 14/15/16, but for some reason it quit working on 17 & up, I'm guessing some kernel change stopped it. Now hot swapping a hard drive on only appears to work, but the drive reads as corrupted, only a power-on reboot gets it working successfully. Maybe there's an easy way to get it working again, or some recompiled kernel is required with some special switches...?

Update 2

Hot swapping is apparently working again for Ubuntu 16.04 (Mint 18 should work too).