[Solved] Building a smaller kernel for a specific device

I figured out that you can do the following to make a smaller kernel for a specific device:

  1. Use make menuconfig to set the Target System and Subtarget.

  2. Run make kernel_menuconfig CONFIG_TARGET=subtarget.

  3. Go to Machine selection > (Device) machine selection

  4. Exclude all devices other than your specific device, exit and save.

This will edit the subtarget config file, such as target/linux/ar71xx/tiny/config-default. To see what changes were made, run git diff (output pasted below). I noticed that this procedure disables a lot of settings, so I'm wondering: is this a safe technique? For example, this seems to have disabled serial port access, but I'm not sure if my device has an ar933x serial port in the first place.

I'm surprised that I haven't found tutorials that mention this technique, so I'm wondering: does this actually work properly sometimes or always? Or did I just make a kernel that will brick my device or have some other problems?

Thanks!

diff --git a/target/linux/ar71xx/tiny/config-default b/target/linux/ar71xx/tiny/config-default
index 2e9031a24c..69b38acc5e 100644
--- a/target/linux/ar71xx/tiny/config-default
+++ b/target/linux/ar71xx/tiny/config-default
@@ -1,72 +1,9 @@
-CONFIG_ATH79_DEV_AP9X_PCI=y
-CONFIG_ATH79_DEV_DSA=y
 CONFIG_ATH79_DEV_ETH=y
 CONFIG_ATH79_DEV_GPIO_BUTTONS=y
 CONFIG_ATH79_DEV_LEDS_GPIO=y
 CONFIG_ATH79_DEV_M25P80=y
-# CONFIG_ATH79_DEV_NFC is not set
 CONFIG_ATH79_DEV_SPI=y
 CONFIG_ATH79_DEV_USB=y
 CONFIG_ATH79_DEV_WMAC=y
-CONFIG_ATH79_MACH_DIR_600_A1=y
-CONFIG_ATH79_MACH_DIR_615_C1=y
-CONFIG_ATH79_MACH_DIR_615_I1=y
-CONFIG_ATH79_MACH_NBG460N=y
-CONFIG_ATH79_MACH_TEW_632BRP=y
-CONFIG_ATH79_MACH_TEW_712BR=y
-CONFIG_ATH79_MACH_TL_MR11U=y
-CONFIG_ATH79_MACH_TL_MR13U=y
-CONFIG_ATH79_MACH_TL_MR3020=y
-CONFIG_ATH79_MACH_TL_MR3X20=y
-CONFIG_ATH79_MACH_TL_WA701ND_V2=y
-CONFIG_ATH79_MACH_TL_WA7210N_V2=y
-CONFIG_ATH79_MACH_TL_WA801ND_V3=y
-CONFIG_ATH79_MACH_TL_WA830RE_V2=y
-CONFIG_ATH79_MACH_TL_WA850RE_V2=y
-CONFIG_ATH79_MACH_TL_WA855RE_V1=y
-CONFIG_ATH79_MACH_TL_WA901ND=y
-CONFIG_ATH79_MACH_TL_WA901ND_V2=y
-CONFIG_ATH79_MACH_TL_WA901ND_V4=y
-CONFIG_ATH79_MACH_TL_WAX50RE=y
-CONFIG_ATH79_MACH_TL_WDR3320_V2=y
-CONFIG_ATH79_MACH_TL_WDR3500=y
-CONFIG_ATH79_MACH_TL_WR1041N_V2=y
-CONFIG_ATH79_MACH_TL_WR703N=y
-CONFIG_ATH79_MACH_TL_WR720N_V3=y
-CONFIG_ATH79_MACH_TL_WR741ND=y
-CONFIG_ATH79_MACH_TL_WR741ND_V4=y
-CONFIG_ATH79_MACH_TL_WR802N_V1=y
-CONFIG_ATH79_MACH_TL_WR802N_V2=y
-CONFIG_ATH79_MACH_TL_WR840N_V2=y
-CONFIG_ATH79_MACH_TL_WR841N_V1=y
-CONFIG_ATH79_MACH_TL_WR841N_V8=y
 CONFIG_ATH79_MACH_TL_WR841N_V9=y
-CONFIG_ATH79_MACH_TL_WR940N_V4=y
-CONFIG_ATH79_MACH_TL_WR941ND=y
-CONFIG_ATH79_MACH_TL_WR941ND_V6=y
-CONFIG_ATH79_MACH_WHR_HP_G300N=y
-CONFIG_ATH79_MACH_WLAE_AG300N=y
-CONFIG_ATH79_MACH_WNR2000=y
-CONFIG_ATH79_MACH_WNR2000_V3=y
-CONFIG_ATH79_MACH_WNR2000_V4=y
-CONFIG_ATH79_MACH_WP543=y
-CONFIG_ATH79_MACH_WPE72=y
-CONFIG_ATH79_NVRAM=y
-CONFIG_ATH79_PCI_ATH9K_FIXUP=y
-CONFIG_BLK_MQ_PCI=y
-CONFIG_HW_HAS_PCI=y
-CONFIG_MYLOADER=y
-CONFIG_PCI=y
-CONFIG_PCI_AR724X=y
-CONFIG_PCI_DISABLE_COMMON_QUIRKS=y
-CONFIG_PCI_DOMAINS=y
-CONFIG_SERIAL_AR933X=y
-CONFIG_SERIAL_AR933X_CONSOLE=y
-CONFIG_SERIAL_AR933X_NR_UARTS=2
-CONFIG_SOC_AR71XX=y
-CONFIG_SOC_AR724X=y
-CONFIG_SOC_AR913X=y
-CONFIG_SOC_AR933X=y
-CONFIG_SOC_AR934X=y
 CONFIG_SOC_QCA953X=y
-CONFIG_SOC_QCA956X=y
1 Like

Changing the kernel configuration is a relatively risky step into (mostly-) untested territory, but it does exactly what it says it does - you just need to know (very well) what you do and why it's safe to do for your device (and have a failsafe recovery available).

It should be relatively safe to exclude machine support from the kernel as long as you do not deselect Required SoC feature support.

As already written, you should have working hardware access / debrick setup ready before trying such a kernel.

Thanks for the quick replies. I was trying to get a sense of how risky (or time wasting) this is. For example:

  • If the config-default/Kconfig files were not written with this technique in mind, then this would be risky.
  • If those files were written assuming that this technique would not be used, then this would be risky.
  • If no one else does uses this technique, then this would be risky.

For example, CONFIG_ATH79_DEV_DSA was disabled in tiny/config-default. If that is actually necessary for my device (and it should have been listed in the Kconfig.openwrt for my device), but no one ever noticed because no one ever tried this technique, then that would be bad.

Perhaps I'll be a little bit more conservative and just edit tiny/config-default to disable CONFIG_ATH79_MACH_* and CONFIG_SOC_* for other devices, without touching the other settings.

Yes, I've reviewed the debrick process ahead of time. Thanks for the reminders.

FWIW, I tried manually editing tiny/config-default to disable CONFIG_ATH79_MACH_* and CONFIG_SOC_* for other devices, without touching the other settings. The resulting kernel was the same as using the UI in make kernel_menuconfig CONFIG_TARGET=subtarget, which disables a lot more settings, as shown in my original post. (I determined this by comparing the embedded ikconfig in the kernels).

I guess the kernel build process probably uses Kconfig.openwrt to override parts of tiny/config-default.

Once you confirm that your kernel boots and does not cripple any functions, I'd be curious to know how much smaller the compressed kernel is on disk, as well as if the RAM consumption has notably decreased.

At least disabling CONFIG_SOC_* becomes moot after switching to ath79, it's only ar71xx which (needs to) include mach files for all devices of the given sub-target (ath79 appends only the necessary DTS).

It worked.

I used make kernel_menuconfig CONFIG_TARGET=subtarget to make a device-specific kernel for only the TP-Link TL-WR841N/ND v9. It booted and seems to work fine.

Kernel size on disk

Looks like it saves ~44K for the compressed kernel:

File Not device-specific (bytes) Device-specific (bytes) Savings (bytes)
openwrt-ar71xx-tiny-uImage-lzma.bin 1197836 1153022 44,814
openwrt-ar71xx-tiny-vmlinux.bin 3761116 3597716 163,400
openwrt-ar71xx-tiny-vmlinux.elf 3766240 3602788 163,452
openwrt-ar71xx-tiny-vmlinux.lzma 1245184 1179648 65,536
openwrt-ar71xx-tiny-vmlinux-lzma.elf 1207956 1163144 44,812

RAM consumption

It's hard to say how much RAM consumption is saved because in addition to the device-specific kernel, I made several other build changes at the same time:

  • Went from OpenWrt 18.06.1 to 18.06.2.
  • Removed 8250 serial drivers (subtarget: CONFIG_SERIAL_8250). This also has the effect of disabling the persistent /sbin/askfirst /usr/libexec/login.sh process. (Probably an alternative way to prevent askfirst from running is by commenting out the askconsole line in /etc/inittab.)
  • Removed early printk (subtarget: CONFIG_EARLY_PRINTK).
  • Used a (default) squashfs block size of 256K instead of (commonly used) 1024K which should free up 2,304K (3 * 1024K - 3 * 256K), if I understand correctly.
  • Disabled swap (CONFIG_KERNEL_SWAP).

The new result is:

root@OpenWrt:~# free
             total       used       free     shared    buffers     cached
Mem:         28624      19844       8780         76       2084       6024
-/+ buffers/cache:      11736      16888
Swap:            0          0          0

The previous result was:

root@OpenWrt:~# free
             total       used       free     shared    buffers     cached
Mem:         28452      22480       5972         72       2200       4256
-/+ buffers/cache:      16024      12428
Swap:            0          0          0

My guess is that the majority of the RAM savings is just due to using a (default) squashfs block size of 256K.

BTW, I stop uhttpd unless I'm actively using the web interface. I also always build my kernel with these (well-known) settings disabled: CONFIG_KERNEL_PRINTK, CONFIG_KERNEL_CRASHLOG, CONFIG_KERNEL_DEBUG_INFO, CONFIG_KERNEL_DEBUG_KERNEL, CONFIG_KERNEL_MAGIC_SYSRQ, CONFIG_KERNEL_ELF_CORE, CONFIG_KERNEL_PRINTK_TIME.

@TokenBucket If your problem is solved, please consider marking this topic as [Solved]. (Click the pencil behind the topic...)

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.