diff --git a/tools/include/uapi/linux/xfs.h b/tools/include/uapi/linux/xfs.h
index 2c4c61d5ba5391aebef71f7274c18d24b80f2296..a0d37e411ee185a239d98c20caaa169b017d4575 100644
--- a/tools/include/uapi/linux/xfs.h
+++ b/tools/include/uapi/linux/xfs.h
@@ -9,9 +9,11 @@
 
 struct xfs_writable_file {
 	const unsigned char *name;
+	unsigned int clear_f_mode; /* can be cleared from file->f_mode */
 	unsigned int f_mode; /* can be set into file->f_mode */
 	long long i_size; /* file size */
-	long long prev_pos; /* ra->prev_pos */
+	long long prev_pos; /* ra->prev_pos page index */
+	long long pos; /* iocb->ki_pos page index */
 };
 
 #endif /* _UAPI_LINUX_XFS_H */
diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile
index 1ba656a8ed656147f502aefa02ca09035eb67457..8d2737285f185db96874537975889876ccf95224 100644
--- a/tools/testing/selftests/bpf/Makefile
+++ b/tools/testing/selftests/bpf/Makefile
@@ -36,7 +36,7 @@ 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_skb_cgroup_id_kern.o test_set_xfs_file.o test_clear_xfs_file.o
 
 # Order correspond to 'make run_tests' order
 TEST_PROGS := test_kmod.sh \
diff --git a/tools/testing/selftests/bpf/test_clear_xfs_file.c b/tools/testing/selftests/bpf/test_clear_xfs_file.c
new file mode 100644
index 0000000000000000000000000000000000000000..167ee6320723219ea690280d94cf86d7400e6e1b
--- /dev/null
+++ b/tools/testing/selftests/bpf/test_clear_xfs_file.c
@@ -0,0 +1,43 @@
+// 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, clear_f_mode: %u, 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;
+		/* blk_xxx */
+		} else if (len == 15) {
+			if (file->prev_pos == file->pos)
+				file->clear_f_mode |= FMODE_RANDOM;
+		}
+		bpf_trace_printk(fmt, sizeof(fmt), name, file->clear_f_mode,
+				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 d0bc971d93bf96c14822264d34f36be9b2d84e53..247c42be029b21bbc7f0fe0b7179129a265d1eef 100644
--- a/tools/testing/selftests/bpf/test_xfs_file.c
+++ b/tools/testing/selftests/bpf/test_xfs_file.c
@@ -18,17 +18,22 @@
 
 int main(int argc, char *argv[])
 {
-	const char *file = "./test_set_xfs_file.o";
+	const char *set_file = "./test_set_xfs_file.o";
+	const char *clear_file = "./test_clear_xfs_file.o";
+	const char *file = set_file;
 	struct bpf_object *obj;
 	int efd, err, prog_fd;
 	int delay = SLEEP_SECS;
 	char *endptr, *str;
 
-	if (argc == 2) {
-		str = argv[1];
+	if (argc == 3) {
+		str = argv[2];
 		delay = strtol(str, &endptr, 10);
 	}
 
+	if (argc >= 2 && !strcmp("clear", argv[1]))
+		file = clear_file;
+
 	err = bpf_prog_load(file, BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE, &obj,
 			&prog_fd);
 	if (err) {