返回 AI 资讯
社区讨论V2EX 技术

发现了一个 btrfs 的 bug,有没有 v 友一起来看下?

我让 Codex 帮我写了一个可以稳定触发这个 bug 的程序: define GNU SOURCE include include include include include include include include include define READ SIZE 4096UL define TEST FILE SIZE (1UL 1024 1024) define MAX TRIES 100UL / Reproduce the Btrfs direct-IO checksum false fail…

内容摘要

我让 Codex 帮我写了一个可以稳定触发这个 bug 的程序: #define _GNU_SOURCE #include <errno.h> #include <fcntl.h> #include <pthread.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <stdatomic.h> #include <sys/mman.h> #include <unistd.h> #define READ_SIZE 4096UL #define TEST_FILE_SIZE (1UL * 1024 * 1024) #define MAX_TRIES 100UL /* * Reproduce the Btrfs direct-IO checksum false failure: * one thread overwrites the O_DIRECT read buffer while read() is in flight. */ static atomic_int stop_scribble; static void fill_buffer(char *buf, size_t len, uint64_t *seed) { size_t i; for (i = 0; i < len; i++) { *seed ^= *seed << 13; *seed ^= *seed >> 7; *seed ^= *seed << 17; buf[i] = (char)*seed; } } static int create_test_file(void) { char path[] = "./btrfs-read-repro.XXXXXX"; char buf[READ_SIZE]; uint64_t seed = 0x123456789abcdef0ULL; size_t done; int fd; int direct_fd; fd = mkstemp(path); if (fd < 0) { perror("mkstemp"); return -1; } for (done = 0; done < TEST_FILE_SIZE; done += sizeof(buf)) { ssize_t ret; fill_buffer(buf, sizeof(buf), &seed); ret = write(fd, buf, sizeof(buf)); if (ret != (ssize_t)sizeof(buf)) { if (ret < 0) { perror("write"); } else { fprintf(stderr, "short write: %zd\n", ret); } goto fail; } } if (fsync(fd) < 0) { perror("fsync"); goto fail; } (void)posix_fadvise(fd, 0, 0, POSIX_FADV_DONTNEED); close(fd); direct_fd = open(path, O_RDONLY | O_DIRECT | O_CLOEXEC); if (direct_fd < 0) { perror(path); unlink(path); return -1; } unlink(path); return direct_fd; fail: close(fd); unlink(path); return -1; } static void *scribble_thread(void *opaque) { char *buf = opaque; uint64_t v = 0x5a5a5a5a5a5a5a5aULL; while (!atomic_load_explicit(&stop_scribble, memory_order_relaxed)) { size_t off; for (off = 0; off < READ_SIZE; off += sizeof(v)) { *(volatile uint64_t *)(buf + off) = v; } v += 0x0101010101010101ULL; } return NULL; } int main(void) { pthread_t scribbler; char *buf; int fd; int reproduced = 0; fd = create_test_file(); if (fd < 0) { return 1; } buf = mmap(NULL, READ_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (buf == MAP_FAILED) { perror("mmap"); close(fd); return 1; } if (pthread_create(&scribbler, NULL, scribble_thread, buf) != 0) { perror("pthread_create"); munmap(buf, READ_SIZE); close(fd); return 1; } for (unsigned long n = 0; n < MAX_TRIES; n++) { ssize_t ret; if (lseek(fd, 0, SEEK_SET) < 0) { perror("lseek"); break; } ret = read(fd, buf, READ_SIZE); if (ret < 0 && errno == EIO) { fprintf(stderr, "reproduced: read returned EIO at try %lu\n", n); reproduced = 1; break; } if (ret != (ssize_t)READ_SIZE) { fprintf(stderr, "unexpected read result: %zd errno=%d\n", ret, errno); break; } } atomic_store_explicit(&stop_scribble, 1, memory_order_relaxed); pthread_join(scribbler, NULL); munmap(buf, READ_SIZE); close(fd); if (!reproduced) { fprintf(stderr, "not reproduced after %lu tries\n", MAX_TRIES); return 1; } return 0; } 只需要当前目录是 btrfs ,运行这个程序就能直接触发下面这个报错: root@develop:~/test# gcc btrfs_read_repro.c -pthread -o btrfs_read_repro root@develop:~/test# root@develop:~/test# ./btrfs_read_repro reproduced: read returned EIO at try 1 root@develop:~/test# root@develop:~/test# dmesg [12297.261256] BTRFS warning (device nvme1n1p1): csum failed root 336 ino 1110132 off 0 csum 0x588c3802 expected csum 0x84efe040 mirror 1 [12297.261263] BTRFS error (device nvme1n1p1): bdev /dev/nvme1n1p1 errs: wr 0, rd 0, flush 0, corrupt 36926, gen 0 [12297.261267] BTRFS warning (device nvme1n1p1): direct IO failed ino 1110132 op 0x8800 offset 0x0 len 4096 err no 10 root@develop:~/test# 我用最新的 7.3-rc1 内核测试仍然有这个报错,不知道上游是否知道有这个 bug ,这个 bug 用 5.10 内核都能复现,如果上游知道这个 bug 的存在为啥到现在还都没有修复。

资讯来源

V2EX

原文链接

打开原文