Page MenuHome GnuPG

keyboxd is active when idle (unnecessary power draw)
Open, NormalPublic

Description

On a system doing nothing else, where keyboxd has been started, it remains active apparently indefinitely, potentially even after the user logs out, if they have no process reaping set up.

As long as keyboxd is running, it wakes up roughly every 4 or 5 seconds, does some minor calculation, and then goes back to sleep again. Every minute, it spins up a thread and tries to talk to itself over its socket, i think to see whether it is still successfully listening. This connection itself causes another thread to spin up to handle it. These threads chat briefly to each other, and then terminate.

All this activity (the ~4s wakeups plus the ~1m pair of threads) is enough to indicate to a tool like powertop that keyboxd is one of the main userspace consumers of power on an otherwise idle system.

On a Linux system, if a scheduled wakeup is needed because of a known future event, the wakeup can be scheduled safely with the select() timeout -- any intervening events that happen in the meantime will interrupt the sleep safely.

Additionally, if there is a concern about a listening socket somehow slipping out of the processes' control, any modern linux system supports an inotify-style callback infrastructure that can be mixed into the select() loop to provide an indication if the socket path changes somehow. it should even be possible to set up inotify on the parent directory of a socket, or all ancestors going all the way back to the root of the filesystem, if you really want to be thorough.

With no unnecessary wakeups scheduled, and without spawning threads every minute, an idle system could subside into a low-power CPU state and wait for necessary activity before spinning up the cores again. The current activity of keyboxd makes that harder to do.

All of this only matters to people who care about energy cost, the ecological impact of extra power draw, or battery life, of course. And it's still a trivial amount of activity compared to, say, mining bitcoin ☺

I see two possible ways to resolve this concern, but there are probably others:

  • implement correct select() loop timing logic and use inotify() (on platforms that have it) to detect changes to the listening socket. On platforms that don't support this, you might still need to wake up regularly to check on the socket (or you could decide that platforms that don't have inotify or the equivalent simply don't get to do this verification).
  • the whole keyboxd process could simply terminate after a relatively short amount of idle time (1 minute?), releasing the listening socket and handling its accept() queue before terminating. If autolaunching is working as expected, then the replacement keyboxd will simply be spun up when needed.

The latter approach has the added advantage of not leaving a lingering process too long after logout, in systems where there is no post-logout reaping.

PS note, this is very similar to the never-resolved T1805, but with a different daemon. As the number of daemons with this kind of CPU footprint increases, the worse GnuPG as a whole looks to someone struggling with power management issues.

Details

Version
2.4.7

Event Timeline

dkg renamed this task from keyboxd is remarkably active when idle (unnecessary power draw) to keyboxd is active when idle (unnecessary power draw).Sun, Feb 9, 5:42 AM
werner triaged this task as Normal priority.Mon, Feb 10, 9:40 AM
werner edited projects, added Feature Request; removed keyboxd, Bug Report.
werner added subscribers: gniibe, werner.

inotify is already used used on Linux to check for a lost homedir. The once-in-a-minute check should be the same as with the other daemons and has proved to be very useful. The whole thing has been discussed over and over again a long time ago and - as with other system daemon - we agreed on scheduling at the full second.

Checking the code shows that at some point we must have lost this sync feature. Needs more investigation.

This is the old code from gnupg-2.0/agent/gpg-agent.c:

/* Create a timeout event if needed.  To help with power saving
   we syncronize the ticks to the next full second.  */
if (!time_ev)
  {
    pth_time_t nexttick;

    nexttick = pth_timeout (TIMERTICK_INTERVAL, 0);
    if (nexttick.tv_usec > 10)  /* Use a 10 usec threshhold.  */
      {
        nexttick.tv_sec++;
        nexttick.tv_usec = 0;
      }
    time_ev = pth_event (PTH_EVENT_TIME, nexttick);
  }

with the migration to nPth this was changed in 2.2 to:

      npth_clock_gettime (&curtime);
      if (!(npth_timercmp (&curtime, &abstime, <)))
        {
	  /* Timeout.  */
	  handle_tick ();
	  npth_clock_gettime (&abstime);
	  abstime.tv_sec += TIMERTICK_INTERVAL;
       }
      npth_timersub (&abstime, &curtime, &timeout);

See rG7a7a59782766a8bde0c3e7156d14bb2b0e4a3951

I'm glad that inotify is already in use, that's a reasonable thing on the Linux platform.

Can you give an example of how the once-a-minute loopback test has been useful, specifically?

What about the every ~4s wakeups? are those also obligatory? even waking up on the same wall-clock second means that an idle system is going to have to spin up cores that would otherwise be idle indefinitely, and if all the daemons wake at the same time, there might be many cores spun up simultaneously. They could all be left in low-power states if the unnecessary wakeup didn't happen at all.

If the intent is to synchronize all daemon wakeups on the wall-clock second, maybe it would be good to have some sort of test to make sure that it keeps that property.