This post is obsolete

This blog post is quite old, and a lot has changed since then. Meanwhile, I have changed employer, blogging platform, software stack, infrastructure, interests, and more.
You will probably find more recent and relevant information about the topics discussed here elsewhere.
Still, this content is provided here for historical reasons, but please don’t expect it to be current or authoritative at this point.
Thanks for stopping by!


OpenSolaris Home Server Scripting 2: Setting Up Power Management

Power Management Scripting

Last week, we looked at how essential scripting is for administering home servers (one of the 7 tips for home server bliss) and we wrote us a little script for enabling automatic snapshots.

Another thing that you’ll almost certainly want to do on your OpenSolaris home server is enabling power management. This will ensure your server spends as little power as possible when idle, while still being powerful when needed.

There’s a whole community group devoted to power management (no link, opensolaris.org no longer exists) and it’s a good idea to check out their FAQ (no link, opensolaris.org no longer exists) and check in on their discussions (no link, opensolaris.org no longer exists).

How To Configure Power Management in OpenSolaris

To enable power management on OpenSolaris, we need to do two things:

  • Enable CPU power management: This will tell the system to switch the CPU into a lower power mode when idle.

  • Enable disk power management: This tells the disks to switch to a lower power mode when there’s no activity for a specified amount of time.

Both can be configured by editing the power.conf(4) (no link, sun.com no longer exists) file and then calling pmconfig(1M) (no link, sun.com no longer exists) to make the changes go live.

Let’s write ourselves a script that does all of the configuration automatically, so we just need to call it and all of the system will be optimized for lower power consumption:

Manipulating the power.conf File

We’ll assume full control of the relevant settings in power.conf. Whatever was written there before, after we’re done with it, power management will be enabled. Likewise, if we decide to shut off power management, all settings that concern CPU and disk power management will be gone.

This makes our script more simple: We can create a function that rips off any CPU and disk PM settings, then either write back the result (to disable PM) or attach our own settings (to enable it).

(CPU PM is enabled by default on a fresh install of OpenSolaris 2009.06, but it’s good to not make that assumption in case someone did change power.conf after all or when switching PM back on after having switched it explicitly off.)

Here’s the piece of code that produces a “clean” version of power.conf, without any CPU or disk power management options:

1
2
3
4
5
6
7
function filter_power {
    # Purge power.conf from any custom settings
    cat $POWERCONF | \
        grep -v cpupm | \
        grep -v cpu-threshold | \
        grep -v device-thresholds
}

It makes sense to put it into its own function, as we’ll use it for both setting up and tearing down power management.

The Proper Use Of Temporary Files

We’ll use a temporary file to construct our new version of power.conf for two reasons:

  • We can exchange the old version with a new one by using pfexec mv and don’t need to run the whole script with full privileges (which would be needed to make any power.conf bits work).

  • We’ll learn how to properly use temporary files in shell scripts.

In fact, Chris Gerhard recently wrote a blog post about how to properly set up temporary files so they clean up after themselves automatically. This is such a good practice that I want to emphasize it here by making it part of our power management script.

So here’s the piece to clean up any CPU and disk power settings from power.conf and make the settings live:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# Create a temporary file name, make sure it's gone when we are.
trap '${TMPFILE:+rm ${TMPFILE}}' EXIT
US=$(basename $0)
TMPFILE=$(/usr/bin/mktemp /tmp/${US}.XXXXXX)

function unconfigure_power {
    # Get a clean version of power.conf
    filter_power >$TMPFILE

    # Copy back the temporary file into power.conf
    pfexec mv $TMPFILE /etc/power.conf && unset TMPFILE

    # Activate the new power.conf file
    pfexec pmconfig
}

Taking Care Of Ownership And Permissions

But wait! Since we’re exchanging the system’s power.conf with one of our own, we need to make sure to set the file owners and permissions properly after the exchange. This is a job for another function:

1
2
3
4
5
# Adjust owner and permissions for power.conf
function powerconf_perms {
    pfexec chown root:sys power.conf
    pfexec chmod 644 power.conf
}

Of course we’ll need to call this in the function above after the pfexec mv bit.

Detecting the Disks in the System

Now comes the tricky part: Each disk in the system needs its own entry with its device path in power.conf. But what’s the best way to detect how many disks there are in the system and what their device paths are?

It turns out that format(1M) (no link, sun.com no longer exists) is our friend, because the first thing it does after starting it is to present us with a list of all disks, and their device paths.

Very well, but there’s one caveat: format expects us to use it interactively, but we want to use it in a script. If we called format inside our script, it would wait for the user to navigate its menus by listening on STDIN, which we really don’t want!

After some tweaking, I found out that you can send an empty input stream into format through echo, then it will automatically terminate itself after giving us its list of drives. The reason for this is most probably the EOF that STDIN will return to format after the (empty) input has been processed and thus, format will have no option but to terminate.

Here’s the line that gives us all of the disks in the system:

1
disks=$( echo | $PFFORMAT | grep pci )

We use grep to filter out just the lines with the device paths.

Putting it All Together

Now that we have all the pieces, let’s put together our power management configuration script. We’ll abstract away all pfexec commands and the power.conf file itself into a block of constants at the beginning to make the code slightly cleaner:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
#!/bin/ksh93
#
# powersave
#
# Setup power saving parameters
#

# Useful constants
POWERCONF="/etc/power.conf"
PFCHOWN="pfexec chown"
PFCHMOD="pfexec chmod"
PFMV="pfexec mv"
PFPMCONFIG="pfexec pmconfig"
PFFORMAT="pfexec format"

# Create a temporary file name, make sure it's gone when we are.
trap '${TMPFILE:+rm ${TMPFILE}}' EXIT
US=$(/usr/bin/basename $0)
TMPFILE=$(mktemp /tmp/${US}.XXXXXX)

# Get parameters
CMD=$1

# Adjust owner and permissions for power.conf
function powerconf_perms {
    $PFCHOWN root:sys $POWERCONF
    $PFCHMOD 644 $POWERCONF
}

function filter_power {
    # Purge power.conf from any custom settings
    cat $POWERCONF | \
        grep -v cpupm | \
        grep -v cpu-threshold | \
        grep -v device-thresholds
}

function unconfigure_power {
    # Get a clean version of power.conf
    filter_power >$TMPFILE

    # Copy back the temporary file into power.conf
    $PFMV $TMPFILE $POWERCONF && unset TMPFILE

    # Adjust permissions and owner
    powerconf_perms

    # Activate the new power.conf file
    $PFPMCONFIG
}

function configure_power {
    # Get a clean config to start with.
    filter_power >$TMPFILE

    # Enable CPU power management
    echo "cpupm enable" >>$TMPFILE
    echo "cpu-threshold 1s" >>$TMPFILE

    # Get a list of hard disk devices in the system by asking format
    disks=$( echo | $PFFORMAT | grep pci )

    for i in $disks ; do
        echo "device-thresholds $i  5m" >>$TMPFILE
    done

    # Copy the new power.conf back
    $PFMV $TMPFILE $POWERCONF && unset TMPFILE

    # Adjust permissions and owner
    powerconf_perms

    # Activate the new power.conf file
    $PFPMCONFIG
}

# Show a help message.
function show_help {
    echo "Usage: $0 command"
    echo "Supported commands are:"
    echo "  on"
    echo "  off"
}

#
# Main program.
#
# We support different subcommands. Switch to the corresponding subroutine,
# then exit.
#
case $CMD in
        'on' )
                configure_power
        ;;
        'off' )
                unconfigure_power
        ;;
        'help' | * )
                show_help
        ;;
esac

exit 0

Conclusion

I hear the following question quite often: “How can I set up power management on my server?”. Now we have a script that does exactly that. We also learned a bit about the handling of temporary files and found a trick for getting a list of all of the disks that are available in the system.

You can tweak the script to your needs of course. Maybe you want to change the times after which CPU or disk PM kicks in. Or maybe you want to preserve the original power.conf file just in case before meddling with it. Or maybe you don’t want to power manage all of the disks in your system but only some. Feel free to enhance the script and contribute your changes in the comments section of this post.

You can easily observe the effects of CPU power management with the powertop(1M) (no link, opensolaris.org no longer exists) utility.

Observing the effects of disk power management is trickier: The setting is just forwarded to the disk and it’s up to the drive to decide what to do after the idle time is up. Some disks will spin down, some merely spin slower and some won’t do anything at all. Using a watt-meter and some patience may be the only way to find out what really happens.

Also, it may be difficult to achieve the idle state for your boot drives due to different system activities writing to log files etc. This is why separating data pools from the root pool is a good idea. Some even separate /var into a USB stick or other solid state media because of this.

Your Turn

What’s your experience with OpenSolaris power management? Do you have ideas on how to improve this script? Have you measured the effects of disk power management on your server or did you find a way to programmatically verify that it works? Share your views in the comments section below!

Getting More Out of This Blog

If you found this article useful, then add Constant Thinking to your feed reader and get more Technology Thoughts delivered right to your desktop!

Update: Gregor suggested in the comments to use mktemp with it’s path as the GNU and the Solaris version differ slightly. He also suggested to use basename to strip the script name when generating the temporary file. I just added the mods to the above code sample. Thanks!

Update (18/03/2010): My personal scripting role-model Chris pointed out in his latest blog post “Don’t keep opening those files” that writing to a file in a loop over and over again incurs the overhead of opening and closing the destination file multiple times which is not optimal from a performance point of view.

Here’s an alternative version of the part that creates the disk entries for power.conf and avoids the loop altogether, opening the destination file just one time:

1
2
3
4
5
6
# Get a list of hard disk devices in the system by asking format
# Then construct the device-thresholds lines out of it.
disks=$( echo | $PFFORMAT | grep pci ) 
echo $disks | \
        sed -e 's/\([^ ]* *\)/device-thresholds \1      5m\n/g' \
        >>$TMPFILE

This should scale ok for arbitrary numbers of disks, say in an X4540 or better.

Thanks, Chris!


Comments

Commenting is currently not available, because I’d like to avoid cookies on this site. I may or may not endeavor into building my own commenting system at some time, who knows?

Meanwhile, please use the Contact form to send me your comments.

Thank you!


Welcome

This is the blog of Constantin Gonzalez, a Solutions Architect at Amazon Web Services, with more than 25 years of IT experience.

The views expressed in this blog are my own and do not necessarily reflect the views of my current or previous employers.


Copyright © 2022 – Constantin Gonzalez – Some rights reserved.
By using this site you agree to not hold the author responsible for anything related to this site. See Site Info/Imprint for details and our information policy.

This site was built using Pelican, which is written in Python, using a homegrown theme that borrows heavily from Elegant. It is hosted on Amazon S3 and distributed through Amazon CloudFront.