[AHCI] Spinning down ada(4) disks

I've stumbled upon this interesting thread while looking for information on how to replace "atacontrol spindown" with the corresponding equivalent when AHCI is used. As mentioned before if you're looking to replace "atacontrol spindown 900 ad4" the replacement would be "camcontrol idle ada4 -t 900" which means "Idle now, spin down in 900 seconds".

A very interesting read is the "AT Attachment 8 - ATA/ATAPI Command Set" specification. It helps you in mapping the various "camcontrol" idle/standby options:

From camcontrol.c:
Code:
if (strcmp(argv[1], "idle") == 0) {
                if (t == -1)
                        cmd = ATA_IDLE_IMMEDIATE;
                else
                        cmd = ATA_IDLE_CMD;
        } else if (strcmp(argv[1], "standby") == 0) {
                if (t == -1)
                        cmd = ATA_STANDBY_IMMEDIATE;
                else
                        cmd = ATA_STANDBY_CMD;
        } else {
                cmd = ATA_SLEEP;
                t = -1;
        }

The corresponding mapping (Freebsd Command vs ATA Command):

  • camcontrol idle adaX: IDLE IMMEDIATE (page 154 in the ATA8 specs): The IDLE IMMEDIATE command allows the host to immediately place the device in the Idle mode. Command completion may occur even though the device has not fully transitioned into the Idle mode. -> Bottom line: the drive will immediately be put into idle mode, the platters won't spin down.
  • camcontrol idle adaX -t XXX: IDLE (p153): The IDLE command allows the host to place the device in the Idle mode and also set the Standby timer. If the Count field is non-zero then the Standby timer shall be enabled. The value in the Count field shall be used to determine the time programmed into the Standby timer (see 4.18). If the Count field is zero then the Standby timer is disabled. -> Bottom line: Same as above but in addition the standby timer will be armed which will cause the platters to spin down XXX seconds after the last request. This is most probably what most users want!
  • camcontrol standby adaX: STANDBY IMMEDIATE (p247): This command causes the device to immediately enter the Standby mode. -> Bottom line: the platters will spin down immediately.
  • camcontrol standby adaX -t XXX: STANDBY (p246): This command causes the device to enter the Standby mode. If the Count field is non-zero then the Standby timer shall be enabled. The value in the Count field shall be used to determine the time programmed into the Standby timer (see table 27). If the Count field is zero then the Standby timer is disabled. Bottom line: same as above but in addition the standby timer will be activated.

The only question left for me is how to check whether the disks are currently spun down or active. The nice thing about "atacontrol" is that it will log both spin down and up of disks to syslog:

Code:
Aug 10 18:13:16 DrSweety-Server kernel: ad8: Idle, spin down
Aug 10 18:13:16 DrSweety-Server kernel: ad8: drive spun down.
Aug 10 20:20:09 DrSweety-Server kernel: ad8: request while spun down, starting.

So you'll get immediate feedback if something caused the disks to spin up. Obviously the nice thing is that you can then use a script etc. to immediately catch e.g. the "ps aux" output and - with a little bit of luck - catch the corresponding process which accessed the sleeping disks (e.g. a cron job which runs every 3 hours and causes the disks to wake up). Unfortunately, this is no longer possible with AHCI as - as opposed to atacontrol - it's no longer FreeBSD who tells the disks "go and sleep now". Instead, as we are now sending one of the ATA commandes mentioned above to the disk, the disk itself will decided that "it is now time to spin down as the last request was 900 seconds ago". I do not think that it will give the OS feedback about this. The only thing you can do is to regularly poll the current mode of the drive:

Code:
camcontrol cmd adaX -a "E5 00 00 00 00 00 00 00 00 00 00 00" -r -

This will send the ATA command "CHECK POWER MODE" (p86) to the drive: The CHECK POWER MODE command allows the host to determine the current power mode of the device. The
CHECK POWER MODE command shall not cause the device to change power or affect the operation of the Standby timer.
. Important is the last paragraph: you can safely issue the above command no matter if the drive is operating or in standby mode. It shouldn't cause the platters to spin up.

The output of the above command can be read as follows (p311):

Value of the next to last hex code: Description
00h: Device is in Standby mode.
40h: Device is in NV Cache Power Mode and the spindle is spun down or spinning down.
41h: device is in NV Cache Power Mode and the spindle is spun up or spinning up.
80h: Device is in Idle mode.
FFh: Device is in Active mode or Idle mode.

An example:

Code:
# sh -c 'for drive in `camcontrol devlist -v | grep ada | cut -d , -f 2 | cut -d ")" -f 1`; do camcontrol cmd $drive  -a "E5 00 00 00 00 00 00 00 00 00 00 00" -r -; done'
50 00 00 00 00 00 00 00 00 FF 00
50 00 00 00 00 40 00 00 00 00 00
50 00 00 00 00 40 00 00 00 00 00
50 00 00 00 00 40 00 00 00 00 00

-> The first disk is active whereas the last 3 are currently in standby mode.

It would probably be possible to - with a little bit of effort - emulate atacontrol's behaviour regarding the logging of spin ups/downs to syslog with CAM too by using CAM internal timers etc. But as I do not have much knowledge of the FreeBSD internals I'm unfortunately not up for the task ...

I hope this post helps whomever is seeking more information regarding the topic. If you're looking for a way to keep your disks spun down as much as possible I would suggest to first disable AHCI and use the following script at startup:

Code:
#!/bin/sh
# /usr/local/etc/rc.d/disks.sh

DEVLIST=`ls /dev/ | egrep '^ad[0-9]+$'`

for drive in ${DEVLIST}; do
  atacontrol spindown ${drive} 900
done

wait-spinup() {
  while true; do
  read l
  l=`echo $l | grep 'request while spun down'`
   if [ -n "$l" -a "`date +%H%M%S`" != "$d" ]; then
     d=`date +%H%M%S`
     for drive in ${DEVLIST}; do
       dd if=/dev/${drive} of=/dev/null count=1 2> /dev/null &
     done
     ps axo pid,user,command | mail -s "Disks spun up" root
   fi
  done
}
tail -n 0 -F /var/log/messages | wait-spinup

This will send root an email with the current process table. You may use the output of that email to figure out which process caused the disks to spun up. As soon as you have tweaked your system accordingly (e.g. I'm using one script which starts a bunch of other scripts in a row which all would cause the disks to spin up) you may then enable AHCI and use the following script (at startup) to set the standby timer:

Code:
#!/bin/sh

DEVLIST=`camcontrol devlist -v | grep ada | cut -d , -f 2 | cut -d ')' -f 1`

for drive in ${DEVLIST}; do
  /sbin/camcontrol idle ${drive} -t 900
done
 
Back
Top