Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

kbuild: Build kernel module BTFs if BTF is enabled and pahole supports it

Detect if pahole supports split BTF generation, and generate BTF for each
selected kernel module, if it does. This is exposed to Makefiles and C code as
CONFIG_DEBUG_INFO_BTF_MODULES flag.

Kernel module BTF has to be re-generated if either vmlinux's BTF changes or
module's .ko changes. To achieve that, I needed a helper similar to
if_changed, but that would allow to filter out vmlinux from the list of
updated dependencies for .ko building. I've put it next to the only place that
uses and needs it, but it might be a better idea to just add it along the
other if_changed variants into scripts/Kbuild.include.

Each kernel module's BTF deduplication is pretty fast, as it does only
incremental BTF deduplication on top of already deduplicated vmlinux BTF. To
show the added build time, I've first ran make only just built kernel (to
establish the baseline) and then forced only BTF re-generation, without
regenerating .ko files. The build was performed with -j60 parallelization on
56-core machine. The final time also includes bzImage building, so it's not
a pure BTF overhead.

$ time make -j60
...
make -j60 27.65s user 10.96s system 782% cpu 4.933 total
$ touch ~/linux-build/default/vmlinux && time make -j60
...
make -j60 123.69s user 27.85s system 1566% cpu 9.675 total

So 4.6 seconds real time, with noticeable part spent in compressed vmlinux and
bzImage building.

To show size savings, I've built my kernel configuration with about 700 kernel
modules with full BTF per each kernel module (without deduplicating against
vmlinux) and with split BTF against deduplicated vmlinux (approach in this
patch). Below are top 10 modules with biggest BTF sizes. And total size of BTF
data across all kernel modules.

It shows that split BTF "compresses" 115MB down to 5MB total. And the biggest
kernel modules get a downsize from 500-570KB down to 200-300KB.

FULL BTF
========

$ for f in $(find . -name '*.ko'); do size -A -d $f | grep BTF | awk '{print $2}'; done | awk '{ s += $1 } END { print s }'
115710691

$ for f in $(find . -name '*.ko'); do printf "%s %d\n" $f $(size -A -d $f | grep BTF | awk '{print $2}'); done | sort -nr -k2 | head -n10
./drivers/gpu/drm/i915/i915.ko 570570
./drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.ko 520240
./drivers/gpu/drm/radeon/radeon.ko 503849
./drivers/infiniband/hw/mlx5/mlx5_ib.ko 491777
./fs/xfs/xfs.ko 411544
./drivers/net/ethernet/intel/i40e/i40e.ko 403904
./drivers/net/ethernet/broadcom/bnx2x/bnx2x.ko 398754
./drivers/infiniband/core/ib_core.ko 397224
./fs/cifs/cifs.ko 386249
./fs/nfsd/nfsd.ko 379738

SPLIT BTF
=========

$ for f in $(find . -name '*.ko'); do size -A -d $f | grep BTF | awk '{print $2}'; done | awk '{ s += $1 } END { print s }'
5194047

$ for f in $(find . -name '*.ko'); do printf "%s %d\n" $f $(size -A -d $f | grep BTF | awk '{print $2}'); done | sort -nr -k2 | head -n10
./drivers/gpu/drm/i915/i915.ko 293206
./drivers/gpu/drm/radeon/radeon.ko 282103
./fs/xfs/xfs.ko 222150
./drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.ko 198503
./drivers/infiniband/hw/mlx5/mlx5_ib.ko 198356
./drivers/net/ethernet/broadcom/bnx2x/bnx2x.ko 113444
./fs/cifs/cifs.ko 109379
./arch/x86/kvm/kvm.ko 100225
./drivers/gpu/drm/drm.ko 94827
./drivers/infiniband/core/ib_core.ko 91188

Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Link: https://lore.kernel.org/bpf/20201110011932.3201430-4-andrii@kernel.org

authored by

Andrii Nakryiko and committed by
Alexei Starovoitov
5f9ae91f 53297220

+27 -2
+9
lib/Kconfig.debug
··· 274 274 Turning this on expects presence of pahole tool, which will convert 275 275 DWARF type info into equivalent deduplicated BTF type info. 276 276 277 + config PAHOLE_HAS_SPLIT_BTF 278 + def_bool $(success, test `$(PAHOLE) --version | sed -E 's/v([0-9]+)\.([0-9]+)/\1\2/'` -ge "119") 279 + 280 + config DEBUG_INFO_BTF_MODULES 281 + def_bool y 282 + depends on DEBUG_INFO_BTF && MODULES && PAHOLE_HAS_SPLIT_BTF 283 + help 284 + Generate compact split BTF type information for kernel modules. 285 + 277 286 config GDB_SCRIPTS 278 287 bool "Provide GDB scripts for kernel debugging" 279 288 help
+18 -2
scripts/Makefile.modfinal
··· 6 6 PHONY := __modfinal 7 7 __modfinal: 8 8 9 + include include/config/auto.conf 9 10 include $(srctree)/scripts/Kbuild.include 10 11 11 12 # for c_flags ··· 37 36 -T scripts/module.lds -o $@ $(filter %.o, $^); \ 38 37 $(if $(ARCH_POSTLINK), $(MAKE) -f $(ARCH_POSTLINK) $@, true) 39 38 40 - $(modules): %.ko: %.o %.mod.o scripts/module.lds FORCE 41 - +$(call if_changed,ld_ko_o) 39 + quiet_cmd_btf_ko = BTF [M] $@ 40 + cmd_btf_ko = LLVM_OBJCOPY=$(OBJCOPY) $(PAHOLE) -J --btf_base vmlinux $@ 41 + 42 + # Same as newer-prereqs, but allows to exclude specified extra dependencies 43 + newer_prereqs_except = $(filter-out $(PHONY) $(1),$?) 44 + 45 + # Same as if_changed, but allows to exclude specified extra dependencies 46 + if_changed_except = $(if $(call newer_prereqs_except,$(2))$(cmd-check), \ 47 + $(cmd); \ 48 + printf '%s\n' 'cmd_$@ := $(make-cmd)' > $(dot-target).cmd, @:) 49 + 50 + # Re-generate module BTFs if either module's .ko or vmlinux changed 51 + $(modules): %.ko: %.o %.mod.o scripts/module.lds vmlinux FORCE 52 + +$(call if_changed_except,ld_ko_o,vmlinux) 53 + ifdef CONFIG_DEBUG_INFO_BTF_MODULES 54 + +$(if $(newer-prereqs),$(call cmd,btf_ko)) 55 + endif 42 56 43 57 targets += $(modules) $(modules:.ko=.mod.o) 44 58