← Back to team overview

ubuntu-boot team mailing list archive

The (unnecessary) overhead of initramfs

 

Our boot time target is 10s, that's from the boot loader to a fully
logged in desktop (through auto-login).  To reach that time, a budget is
given to the identifiable parts of the boot sequence.

This week I've been concentrating on the kernel and initramfs, the
target budget is 2s.  After this time, the root filesystem must be
mounted and the init daemon started.


Now, there's a strong push from some camps that the initramfs isn't
needed; and that we can build storage drivers and filesystem drivers
into the kernel.

While this is fine for bespoke hardware images, we *do* need the
initramfs for the default case because we have to support the widest
possible hardware configuration out of the box.  Our boot performance
targets are for the default case, so they include the initramfs.

Note that this support for the widest possible *hardware* configuration
is different from support for software configuration.  Another win for
the initramfs is that it can be configured to support additional
software steps such as RAID, LVM, Encrypted root filesystem, Live CD
(casper), NFS boot, etc.

That being said, such software configuration is an option.  We aim to
support a user who moves their drive from one hardware configuration to
another; but it's ok for a user who moves the software image from a
single drive to software RAID, or encrypted root, to need to regenerate
their initramfs.


So, on with the bootcharts (these are all from an SSD-based Dell Mini
10v).  Since we're interested in the kernel and initramfs only, the only
time we're interested in is when the init daemon is started.

Due to the curious nature of init, and that one init process execs
another, this isn't *quite* as simple as looking up that process.

Instead look for when the "sreadahead" service is started.


max-karmic-20090728-1.png is pretty much a default install of Karmic α3,
as you can see the time for the kernel and initramfs is a whopping 4s!
Double our budget.

You'll notice that the framebuffer driver, fbcon, and usplash are all
being started inside the initramfs.  What happens if we take these out?


Everything added to the initramfs has more than an O(1) time penalty.
Firstly there's the simple size of the binaries and files necessary to
support things.

These increase the size of the initramfs, so increase the loading time
for the boot loader to load it into memory.  And remember, the boot
loader uses BIOS calls to load things, no DMA or speed advantage - every
byte hurts.

The initramfs is compressed, so every byte added now needs to be
decompressed into kernel memory.  This doubles the hurt.

And then the initramfs needs to be unpacked from the cpio image into the
ramfs, now the hurt is trebled!


The initramfs has one job to do: mount the root filesystem.  Adding
anything else results in a significant performance penalty.

The reason cited for doing things in the initramfs is that they need to
be done very quickly after boot.  These boot graphs show the irony of
that.

usplash started in the initramfs (-1.png) starts 3.2s into the boot.  If
we strip it all out, and start it first thing after the initramfs is
loaded, it can be started 2.5s into the boot!


The initramfs scripts spend a lot of wasted time and effort doing things
that really don't need to be done:

In the "framebuffer" init-top script:

  modprobe -q intel_agp
  modprobe -q i915
  modprobe -q ${FB} ${OPTS}

The initramfs is force-loading the framebuffer console, and framebuffer
drivers.  It also spends quite a bit of effort figuring out *which*
framebuffer it might want to load.

(The possible value of FB is specified on the command-line, or
matroxfb_base)

Ironically, intel_agp, i915 and matroxfb_base (along with the other
framebuffer drivers I randomly checked) *ALL* already specify PCI
modalias rules in the modules.

In other words, they will be automatically loaded by udev *anyway*

So why are we spending the effort to figure it out when it'll happen by
magic by just deleting the script and leaving the module there.


Another reason to not put things like this into the initramfs is that
the initramfs is *optional*.  If you're hardcoding anything, that should
be setting off alarm bells.

In this case, consider the following line in the "kernelextras" hook:

  force_load fbcon

and in the "framebuffer" init-top script:

  modprobe -q fbcon

What happens if you don't boot with an initramfs?  What loads fbcon?  It
turns out that *nothing* does!

Force-loading fbcon like this in the initramfs actually hides a bug
(that it's not loaded otherwise)


I'd also removed all the console and keymap related scripts when
removing usplash.  The original justification for some of these was that
it was impossible to set the console font while usplash was running.

In other words, putting usplash in the initramfs introduced yet more
bugs, which required even more expensive workarounds!

You might need your keymap to enter the passphrase, but that's an
*optional* feature.  Those who enabled encrypted root should
automatically get these hooks and scripts, those who don't do not need
them.


Continuing in the vein of hardcoding things, if you now read the thermal
hook, alarm bells should be going off.

I'm partly to blame for this one as well.

We force-load the fan and thermal modules.  Who will load them if we
skip the initramfs?  Turns out, udev does.  So if udev automatically
loads them anyway, *WHY* do we force-load them?

(Also worth pointing out that they're built in to the Ubuntu kernel, not
 modules!)


Finally we have a bunch of things in the initramfs like dmsetup, fuse
and ntfs-3g intended to support edge-case filesystems.  Again, this is a
software change; it's *ok* to require people to update their initramfs
if they change to or from these.

Thus they should only go into the initramfs if they're going to be used!

(And in the case of fuse, who loads the fuse module if the initramfs
 is skipped)


What's the result of removing all this stuff?

max-karmic-20090729-4.png

Down from 4s to 2.5s!  We're only 0.5s shy of the budget, with no
significant loss of functionality in the default install.


So what about that last 0.5s?  Well, scripts/local needs a bit of a
study.  There's a lot of stuff in there to figure out the root
filesystem type, and loop waiting for it.

But we're using udev, which already probes for all this stuff
*including* UUIDs and LABELs.

Why don't we just ship a udev rule that matches on the root filesystem,
and then continues the init script to mount it?


Another thing to look at is max-karmic-20090729-4.svg - this is the
kernel initcall boot graph.  As you can see, the populate_rootfs()
function takes up 0.4s on its own - if we made this an asynchronous call
it could happen in the background while the rest of the kernel
initialises.

(I've tried this and keep getting thwarted so far, but am working on it)


I'd also from a pure-kernel POV say that isapnp_init could probably be
an async call as well.


There's also still the question of everything that initramfs-tools drags
in by itself (many modules, etc.) without hooks.

I think from an initramfs POV what I'd like to see/do is:

 - everything should be a hook, mkinitramfs without a hook should
   generate a near-empty initramfs

 - hooks should be trivially configurable so that they (*and their
   accompanying scripts*) are only included if the software
   configuration requires it


I'm also looking at dracut as an alternative to initramfs-tools, in case
it will be better suited to this kind of bare approach.

Scott
-- 
Scott James Remnant
scott@xxxxxxxxxxxxx

Attachment: max-karmic-20090728-1.png
Description: PNG image

Attachment: max-karmic-20090729-4.png
Description: PNG image

Attachment: max-karmic-20090729-4.svg
Description: image/svg

Attachment: signature.asc
Description: This is a digitally signed message part


Follow ups