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

1 小时 26 分钟前
 wniming

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

193 次点击
所在节点    Linux
1 条回复
zyp38263547
1 小时 17 分钟前

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1238347

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX