Distributions
Ubuntu
Fedora
CentOS
中文资源站
网易开源镜像站
wniming
V2EX  ›  Linux

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

  •  
  •   wniming · 1h 4m ago · 159 views

    我让 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 的存在为啥到现在还都没有修复。

    1 replies    2026-08-31 11:42:09 +08:00
    zyp38263547
        1
    zyp38263547  
       55 mins ago   ❤️ 1
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Privacy   ·   Solana   ·   3608 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 04:37 · PVG 12:37 · LAX 21:37 · JFK 00:37
    ♥ Do have faith in what you're doing.