These days, if you want to do something like this, you should check out libeatmydata.

Linux has a tweakable knob called laptop_mode which is meant as an energy saving tool for laptop users on battery: it basically says “try not to touch the disk for X minutes at a time, unless you really need to, and once you do, do everything that you’ve been piling up all at once”. It’s great for laptop users, and doubly so for things like my huge laptop with two 7200RPM HDDs. Seriously.

Now, there are two things that mean you “really need to” hit the disc: reading data that isn’t already cached (duh), and the fsync() and fdatasync() calls. The latter are requests by an application to ensure that all of the data written to far has hit the disc, and they cause the disk to spin up in laptop mode.

Unfortunately, Firefox has a habit of randomly issuing fdatasync() calls. It does this as part of the SQLite backend for its various databases (especially places.sqlite), in order to avoid data corruption. Now, data corruption isn’t terribly likely on a laptop with a battery (which is essentially a built-in UPS), so this is a terrible annoyance with little benefit anyway. There has been talk about this problem, but apparently it’s still around (and the toolkit.storage.synchronous about:config property didn’t work for me). Most of the reports appear to claim that this happens while browsing, but I’ve seen it happen periodically even when Firefox is left idle. Maybe it’s caused by some extension or AJAX webapp? Who knows.

Ideally this problem would be fixed in laptop_mode itself, but that doesn’t appear to be the case and I’m not going to poke this part of my kernel just yet, so here’s a simpler userspace solution: we can preload a library into Firefox that intercepts fsync() and fdatasync() and ignores them. Even better, we can make it do that only if we’re in laptop_mode. All it takes is a little C code:

#define _GNU_SOURCE
#include <unistd.h>
#include <dlfcn.h>
#include <sys/stat.h>
#include <fcntl.h>

static int in_laptop_mode(void)
{
    char buf[2];
    int fd;
    int l;
    fd = open("/proc/sys/vm/laptop_mode", O_RDONLY);
    if (fd < 0)
        return 0;
    l = read(fd, buf, 2);
    close(fd);
    if (l == 2 && buf[0] == '0' && buf[1] == '\n')
        return 0;
    else
        return 1;
}

int fsync(int fd)
{
    static int (*_fsync)(int);
    if (!_fsync)
        _fsync = dlsym(RTLD_NEXT, "fsync");
    if (!in_laptop_mode())
        return _fsync(fd);
    else
        return 0;
}

int fdatasync(int fd)
{
    static int (*_fdatasync)(int);
    if (!_fdatasync)
        _fdatasync = dlsym(RTLD_NEXT, "fdatasync");
    if (!in_laptop_mode())
        return _fdatasync(fd);
    else
        return 0;
}

To compile and use it:

$ gcc -O2 -Wall -shared -fPIC -o killsync.so killsync.c -ldl
$ LD_PRELOAD=`pwd`/killsync.so firefox

Et voilà, firefox no longer wakes up the hard drive(s). You can confirm that the fsync/fdatasync calls no longer make it to the kernel by running strace -f -ppidof firefox``, but only in laptop_mode. I’ve been typing this blog post with this hack applied and so far HDD spinups have been few, far in between, and fairly short. I still have a few offenders to nail (mostly KDE related), but Firefox was one of the worst ones and it’s now history. PowerTOP says I’m saving 3-4W of power. Huzzah! Now I just need to get a kernel with timer stats built so I can fix my insane wakeups-from-idle per second…

2009-12-18 05:58