diff --git a/tools/include/uapi/linux/xfs.h b/tools/include/uapi/linux/xfs.h
index a0d37e411ee185a239d98c20caaa169b017d4575..1409b45affd347a1a7536c8d75644952535a404c 100644
--- a/tools/include/uapi/linux/xfs.h
+++ b/tools/include/uapi/linux/xfs.h
@@ -6,6 +6,7 @@
 
 #define FMODE_RANDOM	(0x1000)
 #define FMODE_WILLNEED	(0x400000)
+#define FMODE_SPC_READAHEAD	(0x800000)
 
 struct xfs_writable_file {
 	const unsigned char *name;
diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile
index 8d2737285f185db96874537975889876ccf95224..46b1d5b864f5a5c904c92a0c394d3050808e602d 100644
--- a/tools/testing/selftests/bpf/Makefile
+++ b/tools/testing/selftests/bpf/Makefile
@@ -36,7 +36,8 @@ TEST_GEN_FILES = test_pkt_access.o test_xdp.o test_l4lb.o test_tcp_estats.o test
 	test_get_stack_rawtp.o test_sockmap_kern.o test_sockhash_kern.o \
 	test_lwt_seg6local.o sendmsg4_prog.o sendmsg6_prog.o test_lirc_mode2_kern.o \
 	get_cgroup_id_kern.o socket_cookie_prog.o test_select_reuseport_kern.o \
-	test_skb_cgroup_id_kern.o test_set_xfs_file.o test_clear_xfs_file.o
+	test_skb_cgroup_id_kern.o test_set_xfs_file.o test_clear_xfs_file.o \
+	test_spec_readahead_xfs_file.o
 
 # Order correspond to 'make run_tests' order
 TEST_PROGS := test_kmod.sh \
diff --git a/tools/testing/selftests/bpf/test_spec_readahead_xfs_file.c b/tools/testing/selftests/bpf/test_spec_readahead_xfs_file.c
new file mode 100644
index 0000000000000000000000000000000000000000..ff8794a14cdcd98fb6de6ee090ba9ab6257077ce
--- /dev/null
+++ b/tools/testing/selftests/bpf/test_spec_readahead_xfs_file.c
@@ -0,0 +1,39 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/bpf.h>
+#include "bpf_helpers.h"
+#include <string.h>
+#include <linux/xfs.h>
+
+/* from /sys/kernel/debug/tracing/events/xfs/xfs_read_file */
+struct xfs_read_buffer_args {
+	struct xfs_writable_file *file;
+};
+
+SEC("tracepoint/xfs/xfs_file_read")
+int bpf_prog1(struct xfs_read_buffer_args *ctx)
+{
+	char fmt[] = "name: %s, set f_mode: %u\n";
+	struct xfs_writable_file *file = ctx->file;
+	char name[64] = {};
+	char *tmp;
+	unsigned long i_size;
+	int len;
+
+	bpf_probe_read(&tmp, 8, &(file->name));
+	len = bpf_probe_read_str(name, 64, tmp);
+	bpf_probe_read(&i_size, 8, &(file->i_size));
+
+	if (!strncmp("blk_", name, 4)) {
+		/* blk_xxx.meta or blk_xxx with size < 2M */
+		if (len == 27 || (len == 15 && i_size <= 2 * 1024 * 1024))
+			file->f_mode |= FMODE_WILLNEED;
+		else if (len == 15) /* blk_xxx */
+			file->f_mode |= FMODE_SPC_READAHEAD;
+		bpf_trace_printk(fmt, sizeof(fmt), name, file->f_mode);
+	}
+	return 0;
+}
+
+char _license[] SEC("license") = "GPL";
+__u32 _version SEC("version") = 1;
diff --git a/tools/testing/selftests/bpf/test_xfs_file.c b/tools/testing/selftests/bpf/test_xfs_file.c
index 247c42be029b21bbc7f0fe0b7179129a265d1eef..89e79d959677ca1475fe40e94032ce793f6d50ba 100644
--- a/tools/testing/selftests/bpf/test_xfs_file.c
+++ b/tools/testing/selftests/bpf/test_xfs_file.c
@@ -20,6 +20,7 @@ int main(int argc, char *argv[])
 {
 	const char *set_file = "./test_set_xfs_file.o";
 	const char *clear_file = "./test_clear_xfs_file.o";
+	const char *spec_readahead_file = "./test_spec_readahead_xfs_file.o";
 	const char *file = set_file;
 	struct bpf_object *obj;
 	int efd, err, prog_fd;
@@ -31,8 +32,12 @@ int main(int argc, char *argv[])
 		delay = strtol(str, &endptr, 10);
 	}
 
-	if (argc >= 2 && !strcmp("clear", argv[1]))
-		file = clear_file;
+	if (argc >= 2) {
+		if (!strcmp("clear", argv[1]))
+			file = clear_file;
+		if (!strcmp("spec_readahead", argv[1]))
+			file = spec_readahead_file;
+	}
 
 	err = bpf_prog_load(file, BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE, &obj,
 			&prog_fd);