diff --git a/Makefile b/Makefile
index f659d3085121..7cc99a424a85 100644
--- a/Makefile
+++ b/Makefile
@@ -527,6 +527,7 @@ XZ		= xz
 ZSTD		= zstd
 
 PAHOLE_FLAGS	= $(shell PAHOLE=$(PAHOLE) $(srctree)/scripts/pahole-flags.sh)
+EXTRA_PAHOLE_FLAGS = $(shell PAHOLE=$(PAHOLE) $(srctree)/scripts/pahole-flags.sh extra)
 
 CHECKFLAGS     := -D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__ \
 		  -Wbitwise -Wno-return-void -Wno-unknown-attribute $(CF)
@@ -614,7 +615,7 @@ export KBUILD_RUSTFLAGS RUSTFLAGS_KERNEL RUSTFLAGS_MODULE
 export KBUILD_AFLAGS AFLAGS_KERNEL AFLAGS_MODULE
 export KBUILD_AFLAGS_MODULE KBUILD_CFLAGS_MODULE KBUILD_RUSTFLAGS_MODULE KBUILD_LDFLAGS_MODULE
 export KBUILD_AFLAGS_KERNEL KBUILD_CFLAGS_KERNEL KBUILD_RUSTFLAGS_KERNEL
-export PAHOLE_FLAGS
+export PAHOLE_FLAGS EXTRA_PAHOLE_FLAGS
 
 # Files to ignore in find ... statements
 
diff --git a/kernel/bpf/Makefile b/kernel/bpf/Makefile
index 341c94f208f4..8e3ee5c98f23 100644
--- a/kernel/bpf/Makefile
+++ b/kernel/bpf/Makefile
@@ -43,3 +43,7 @@ obj-$(CONFIG_BPF_PRELOAD) += preload/
 obj-$(CONFIG_BPF_SYSCALL) += relo_core.o
 $(obj)/relo_core.o: $(srctree)/tools/lib/bpf/relo_core.c FORCE
 	$(call if_changed_rule,cc_o_c)
+
+ifeq ($(CONFIG_DEBUG_INFO_BTF_VARS),m)
+obj-m = vmlinux_btf_extra.o
+endif
diff --git a/kernel/bpf/vmlinux_btf_extra.c b/kernel/bpf/vmlinux_btf_extra.c
new file mode 100644
index 000000000000..9aa682287a1e
--- /dev/null
+++ b/kernel/bpf/vmlinux_btf_extra.c
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2022, Oracle and/or its affiliates. */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+/* Dummy module used as a container for extra vmlinux BTF information;
+ * to be used if vmlinux BTF size is a concern.
+ */
+static int __init vmlinux_btf_extra_init(void)
+{
+	return 0;
+}
+module_init(vmlinux_btf_extra_init);
+
+static void __exit vmlinux_btf_extra_exit(void)
+{
+	return;
+}
+module_exit(vmlinux_btf_extra_exit);
+
+MODULE_LICENSE("GPL v2");
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index f473f7d8a0a2..3c7df82a37db 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -364,6 +364,15 @@ config DEBUG_INFO_BTF_MODULES
 	help
 	  Generate compact split BTF type information for kernel modules.
 
+config DEBUG_INFO_BTF_VARS
+	tristate "Encode kernel variables in BTF"
+	depends on DEBUG_INFO_BTF && PAHOLE_VERSION >= 124
+	help
+	   Decide whether pahole emits variable definitions for all
+	   variables.  If 'm', variables are stored in vmlinux-btf-extra
+	   module, which has BTF for variables only (it is deduplicated
+	   with vmlinux BTF).
+
 config MODULE_ALLOW_BTF_MISMATCH
 	bool "Allow loading modules with non-matching BTF type info"
 	depends on DEBUG_INFO_BTF_MODULES
diff --git a/scripts/Makefile.modfinal b/scripts/Makefile.modfinal
index 9a1fa6aa30fe..210d7869c14f 100644
--- a/scripts/Makefile.modfinal
+++ b/scripts/Makefile.modfinal
@@ -43,6 +43,12 @@ quiet_cmd_btf_ko = BTF [M] $@
 		printf "Skipping BTF generation for %s due to unavailability of vmlinux\n" $@ 1>&2; \
 	elif [ -n "$(CONFIG_RUST)" ] && $(srctree)/scripts/is_rust_module.sh $@; then 		\
 		printf "Skipping BTF generation for %s because it's a Rust module\n" $@ 1>&2; \
+	elif [ $@ == "kernel/bpf/vmlinux_btf_extra.ko" ]; then		\
+		LLVM_OBJCOPY="$(OBJCOPY)" $(PAHOLE) -J			\
+		    --btf_encode_detached=$@.btf --btf_base vmlinux	\
+		    $(EXTRA_PAHOLE_FLAGS) vmlinux;			\
+		$(OBJCOPY) --add-section .BTF=$(@).btf			\
+		    --set-section-flags .BTF=alloc,readonly $(@);	\
 	else								\
 		LLVM_OBJCOPY="$(OBJCOPY)" $(PAHOLE) -J $(PAHOLE_FLAGS) --btf_base vmlinux $@; \
 		$(RESOLVE_BTFIDS) -b vmlinux $@; 			\
diff --git a/scripts/pahole-flags.sh b/scripts/pahole-flags.sh
index 0d99ef17e4a5..44bc714fe926 100755
--- a/scripts/pahole-flags.sh
+++ b/scripts/pahole-flags.sh
@@ -1,6 +1,16 @@
 #!/bin/sh
 # SPDX-License-Identifier: GPL-2.0
 
+config_value() {
+	awk -v name=$1 -F '=' '$1 == name { print $2 }' include/config/auto.conf
+}
+
+# target is set to "extra" for vmlinux_btf_extra module encoding flags.
+# If CONFIG_DEBUG_INFO_BTF_VARS is 'm', we encode variables in
+# the module, otherwise if 'y' encode them in vmlinux BTF directly.
+
+target=$1
+
 extra_paholeopt=
 
 if ! [ -x "$(command -v ${PAHOLE})" ]; then
@@ -20,4 +30,32 @@ if [ "${pahole_ver}" -ge "122" ]; then
 	extra_paholeopt="${extra_paholeopt} -j"
 fi
 
+case $(config_value CONFIG_DEBUG_INFO_BTF_VARS) in
+y)
+	# variables are encoded in core vmlinux BTF; nothing is encoded
+	# in vmlinux_btf_extra module BTF.
+	if [[ $target == "extra" ]]; then
+		extra_paholeopt=""
+	else
+		extra_paholeopt="${extra_paholeopt} --encode_all_btf_vars"
+	fi
+	;;
+m)
+	# global variables are encoded in vmlinux_btf_extra; per-CPU
+	# variables are still found in vmlinux BTF.
+	if [[ $target == "extra" ]]; then
+		extra_paholeopt="--encode_all_btf_vars"
+	else
+		extra_paholeopt="${extra_paholeopt}"
+	fi
+	;;
+*)
+	# nothing is encoded in vmlinux_btf_extra; no global variables
+	# are encoded in core vmlinux BTF.
+	if [[ $target == "extra" ]]; then
+		extra_paholeopt=""
+	fi
+	;;
+esac
+
 echo ${extra_paholeopt}
