Optimizing Boot Speed for Linux Embedded Products

Optimizing Boot Speed for Linux Embedded ProductsFor more content, you can join the Linux system knowledge base package (knowledge base + driver videos + Q&A + projects):Optimizing Boot Speed for Linux Embedded Products

📢 Boot speed is an important performance indicator for embedded products. A faster boot speed provides a better user experience and can save energy in some cases, as it allows for direct shutdown instead of sleep mode. Optimizing boot speed can enhance product competitiveness. For certain systems, boot speed is a strict requirement.

1. Observation Methods

Before optimization, it is essential to fully understand the current system’s boot timing.

1.1, printk time

Enable the following option in the <span>kernel</span> configuration:

kernel hacking --->
[*] Show timing information on printks

This will prepend timestamps to the kernel’s <span>log</span>.

Note: This method is primarily used to measure the time taken at various stages during the kernel boot process.

1.2, initcall_debug

Add parameters to the <span>kernel</span> <span>cmdline</span> by modifying the file:<span>/<board>/configs/env-<kernelversion>.cfg</span>

initcall_debug=1
setargs_nand=setenv bootargs console=${console} earlyprintk=${earlyprintk} root=${nand_root} initcall_debug=${initcall_debug} init=${init}

Once enabled, the boot process will print each initcall function call and its duration.

Note: This method is primarily used to measure the time taken by kernel initcalls.

1.3, bootgraph

A tool included in the kernel source (<span>scripts/bootgraph.pl</span>) can be used to analyze boot time.

  • <span>kernel</span> must be compiled with the <span>CONFIG_PRINTK_TIME</span> option

  • Add <span>initcall_debug=1</span> to the <span>kernel cmdline</span>

  • After the system boots, execute <span>dmesg | perl $(Kernel_DIR)/scripts/bootgraph.pl > output.svg</span>

  • Use an <span>SVG</span> viewer (such as <span>Inkscape</span>, <span>Gimp</span>, <span>Firefox</span>, etc.) to view the output file <span>output.svg</span>

Note: This method is primarily used to measure the time taken at various stages during the kernel boot process.

1.4, bootchart

<span>bootchart</span> is an open-source tool for analyzing the performance of the Linux boot process. It automatically collects information about <span>CPU</span> usage, processes, etc., during the system boot and displays the analysis results graphically, which can guide the optimization of the system boot process.

  • Modify the <span>kernel cmdline</span>. Change the file <span><board>/configs/env-<kernel-version>.cfg</span> to set <span>init</span> to <span>init=/sbin/bootchartd</span>.

  • Collect information. <span>bootchartd</span> will gather data from <span>/proc/stat</span>, <span>/proc/diskstat</span>, and <span>/proc/[pid]/stat</span><code><span> and save it as </span><code><span>bootchart.tgz</span>.

  • Convert the image. Use the <span>pybootchartgui.py</span> tool on a PC to convert <span>bootchart.tgz</span> to <span>bootchart.png</span> for easier analysis.

Note: This method is primarily used to measure the time taken from mounting the file system to starting the main application.

1.5, GPIO + Oscilloscope

Add code to manipulate GPIO at appropriate points, and use an oscilloscope to capture waveforms to obtain the duration of each stage.

Note: This method can be used to measure the time taken at each stage during the entire boot process.

1.6, grabserial

<span>Grabserial</span> is a tool written in <span>Python</span> by <span>Tim Bird</span> that captures serial output and adds timestamps to each line of received information. It can be downloaded from: https://github.com/tbird20d/grabserial

Documentation: http://elinux.org/Grabserial

Common usage:

sudo grabserial -v -S -d /dev/ttyUSB0 -e 30 -t

To reset the timestamp at a specific string, use the -m parameter:

sudo grabserial -v -S -d /dev/ttyUSB0 -e 30 -t -m "Starting kernel"
  • -v shows parameter information

  • -s skips serial port checks

  • -d specifies the serial port, e.g., /dev/ttyUSB0

  • -e specifies the duration, e.g., capturing 30 seconds of serial records

  • -t adds timestamps

  • -m resets the timestamp when matching a specified string, starting from 0

2. Kernel Optimization Methods

  • Optimization is an ongoing process. It is necessary to choose optimization methods based on objectives, considering both optimization effects and difficulties.

  • Optimizations need to be targeted. Due to differences in CPU counts and frequencies, flash types and sizes, kernel/rootfs compression types and sizes, required functions, main applications, etc., optimizations must be specific.

Generally speaking, kernel boot time is significant and requires deeper optimization.

2.1, Kernel Compression Method

Compare the boot times and flash usage of different compression methods and choose one that fits the actual situation.

2.2, Load Location

The kernel image can be self-extracted by the kernel or decompressed by U-Boot. In the case of self-extraction, if the compressed kernel and the decompressed kernel address conflict, it will first copy itself to a safe location before decompressing to prevent self-overwriting. This will incur additional copying time. For example, for a kernel running at address 0x80008000, the bootloader can load it to 0x81008000, but other locations are also possible.

2.3, Kernel Trimming

Trimming the kernel accelerates boot time in two ways: it reduces size, thus decreasing loading and decompression time; and it reduces the amount of initialization content during kernel startup. Trimming should be based on the actual product situation, removing unnecessary functions and modules.

2.4, Pre-setting LPJ Value

LPJ, or loops_per_jiffy, is calculated at each boot, but if no modifications are made, this value is the same every time. It can be directly provided to skip the calculation.

As shown in the log below, if there is a skipped message, the lpj is calculated using the timer and does not need recalibration.

[0.019918] Calibrating delay loop(skipped), value calculated using timer frequency..48.00BogoMIPS(lpj=240000)

If there is no skipped message, you can add lpj=XXX to the cmdline for pre-setting.

2.5, Initcall Optimization

Set initcall_debug=1 in the cmdline to print the order and duration of all initcall calls during the kernel initialization process. Specifically, modify the tina/target/allwinner//configs/env-.cfg file, adding a line “initcall_debug=1″, and then append ” initcall_debug=${initcall_debug}” after “setargs_*”, as shown below.

setargs_nand=setenv bootargs console=${console} console=tty0 root=${nand_root} init=${init} loglevel=${loglevel} partitions=${partitions}
initcall_debug=${initcall_debug}

After adding this, the kernel boot will print similar messages, allowing for in-depth optimization of initcalls that take a long time.

[0.021772] initcall sunxi_pinctrl_init+0x0/0x44 returned 0 after 9765 usecs
[0.067694] initcall param_sysfs_init+0x0/0x198 returned 0 after 29296 usecs
[0.070240] initcall genhd_device_init+0x0/0x88 returned 0 after 9765 usecs
[0.080405] initcall init_scsi+0x0/0x90 returned 0 after 9765 usecs
[0.090384] initcall mmc_init+0x0/0x84 returned 0 after 9765 usecs

2.6, Parallel Kernel Initcall Modules

Kernel initcalls have many levels, and the most time-consuming during startup are the initcalls of various modules. For multi-core solutions, consider executing module initcalls in parallel to save time. Currently, the kernel’s do_initcalls executes them one by one in order, but it can be modified to create new kernel threads for execution.

2.7, Reducing PTY/TTY Count

After adding initcall printing, it was found that the initialization of pty/tty took a lot of time. Reducing their count can shorten the init time.

initcall pty_init+0x0/0x3c4 returned 0 after 239627 usecs
initcall chr_dev_init+0x0/0xdc returned 0 after 36581 usecs

2.8, Kernel Modules

Consider the constraints on boot speed. The optimization of kernel <span>modules</span> mainly has two points:

  • For modules that must be loaded, compile them directly into the kernel.

  • For non-essential functions, compile them as modules.

For example, if an application requires network connectivity on the main interface, the boot speed can be measured by the appearance of the main interface. In this case, consider compiling the display into the kernel and the Wi-Fi as a module, loading it dynamically when needed.

Leave a Comment