Blob Blame History Raw
From: Shung-Hsi Yu <shung-hsi.yu@suse.com>
Date: Fri, 22 Apr 2022 14:07:34 +0800
Subject: bpf: selftests: adapt bpf_iter_task_vma to get_inode_dev()
Patch-mainline: Never, work-around for SUSE-specific patch
References: bsc#927455 bsc#1198585

We carry two patches to allow differentiating btrfs subvolumes through dev_t,
namely:

  patches.suse/vfs-add-super_operations-get_inode_dev
  patches.suse/btrfs-provide-super_operations-get_inode_dev

Which cause the BPF iterator task_vma test to fail when running on btrfs
because /proc/[PID]/maps exported dev_t (obtained through get_inode_dev()) is
different from what the BPF program gets with file->f_inode->i_sb->s_dev for
files in subvolumes.

This patch workaround the difference by treating inodes associated with btrfs
differently (determined through struct super_operations*), replicating
btrfs_get_inode_dev() with BPF helpers.

In order to replicate btrfs_get_inode_dev(), definition of btrfs-related
structs is needed, this is solved by hacking the build system to generate
header file from btrfs BTF instead of vmlinux, which shouldn't make much
difference for testing.

Signed-off-by: Shung-Hsi Yu <shung-hsi.yu@suse.com>
---
 tools/testing/selftests/bpf/Makefile                  |   15 ++++++++++++++-
 tools/testing/selftests/bpf/progs/bpf_iter_task_vma.c |   13 ++++++++++++-
 2 files changed, 26 insertions(+), 2 deletions(-)

--- a/tools/testing/selftests/bpf/Makefile
+++ b/tools/testing/selftests/bpf/Makefile
@@ -138,6 +138,14 @@ VMLINUX_BTF ?= $(abspath $(firstword $(w
 ifeq ($(VMLINUX_BTF),)
 $(error Cannot find a vmlinux for VMLINUX_BTF at any of "$(VMLINUX_BTF_PATHS)")
 endif
+BTRFS_BTF_PATHS ?= $(if $(O),$(O)/fs/btrfs/btrfs.ko)				\
+		     $(if $(KBUILD_OUTPUT),$(KBUILD_OUTPUT)/fs/btrfs/btrfs.ko)	\
+		     ../../../../fs/btrfs/btrfs.ko				\
+		     /sys/kernel/btf/btrfs
+BTRFS_BTF ?= $(abspath $(firstword $(wildcard $(BTRFS_BTF_PATHS))))
+ifeq ($(BTRFS_BTF),)
+$(error Cannot find a btrfs.ko for BTRFS_BTF at any of "$(BTRFS_BTF_PATHS)")
+endif
 
 # Define simple and short `make test_progs`, `make test_sysctl`, etc targets
 # to build individual tests.
@@ -242,7 +250,12 @@ endif
 $(INCLUDE_DIR)/vmlinux.h: $(VMLINUX_BTF) $(BPFTOOL) | $(INCLUDE_DIR)
 ifeq ($(VMLINUX_H),)
 	$(call msg,GEN,,$@)
-	$(Q)$(BPFTOOL) btf dump file $(VMLINUX_BTF) format c > $@
+	# This is a lie, we're dumping BTF of the btrfs modules because we need
+	# definition of btrfs-related definitions (see bsc#1198585). But since
+	# the header file generated also contains vmlinux definiton (more
+	# specifically, it is a superset of vmlinux definition), it can be used
+	# in places where vmlinux.h is expected.
+	$(Q)$(BPFTOOL) btf dump --base-btf $(VMLINUX_BTF) file $(BTRFS_BTF) format c > $@
 else
 	$(call msg,CP,,$@)
 	$(Q)cp "$(VMLINUX_H)" $@
--- a/tools/testing/selftests/bpf/progs/bpf_iter_task_vma.c
+++ b/tools/testing/selftests/bpf/progs/bpf_iter_task_vma.c
@@ -1,6 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0
 /* Copyright (c) 2020 Facebook */
 #include "bpf_iter.h"
+#include <bpf/bpf_core_read.h>
 #include <bpf/bpf_helpers.h>
 
 char _license[] SEC("license") = "GPL";
@@ -17,6 +18,9 @@ char _license[] SEC("license") = "GPL";
 #define MAJOR(dev)	((unsigned int) ((dev) >> MINORBITS))
 #define MINOR(dev)	((unsigned int) ((dev) & MINORMASK))
 
+/* libbpf will fill-in the address of the symbol */
+extern const void btrfs_super_ops __ksym;
+
 #define D_PATH_BUF_SIZE 1024
 char d_path_buf[D_PATH_BUF_SIZE] = {};
 __u32 pid = 0;
@@ -42,7 +46,14 @@ SEC("iter/task_vma") int proc_maps(struc
 	BPF_SEQ_PRINTF(seq, "%08llx-%08llx %s ", vma->vm_start, vma->vm_end, perm_str);
 
 	if (file) {
-		__u32 dev = file->f_inode->i_sb->s_dev;
+		__u32 dev;
+		if (file->f_inode->i_sb->s_op == &btrfs_super_ops) {
+			/* BTRFS is treated specially, see bsc#1198585 */
+			struct btrfs_inode *inode = container_of(file->f_inode, struct btrfs_inode, vfs_inode);
+			dev = BPF_CORE_READ(inode, root, anon_dev);
+		} else {
+			dev = file->f_inode->i_sb->s_dev;
+		}
 
 		bpf_d_path(&file->f_path, d_path_buf, D_PATH_BUF_SIZE);