I figured out that you can do the following to make a smaller kernel for a specific device:
Use make menuconfig to set the Target System and Subtarget.
Run make kernel_menuconfig CONFIG_TARGET=subtarget.
Go to Machine selection > (Device) machine selection
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?
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).
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).
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.
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.