DF-4800 / racer.c
/* DF-4800 trigger: N processes tight-looping the VINUM_SETSTATE * (object_up) ioctl that drives revive_block() on the same subdisk. * * revive_block() (sys/dev/raid/vinum/vinumrevive.c) reads sd->revived * at entry (line 89: size/offset computation) and advances it only * AFTER the data write completes (line 187). With two or more * concurrent callers, both read the same cursor, both copy the same * block, and both advance -- the block in between is never copied. * The subdisk is then brought `up` with content that was never revived. * * The stock `vinum start` userland sleeps between ioctls (revive * interval), which is why the race needs a tight loop to show: each * child here issues back-to-back ioctls with no delay, so the * read-cursor/advance windows overlap constantly. * * Build: cc -O -o /tmp/racer /tmp/racer.c * Run (after setup): /tmp/racer 4 # 4 concurrent revivers */ #include <sys/ioccom.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <unistd.h> #include <sys/wait.h> #define L 'F' #define MAX_IOCTL_REPLY 256 struct vimsg { int index; int type; /* 1 = sd_object */ int state; /* 3 = object_up */ int op; int force; int recurse; int verify; int otherobject; int rename; long long offset; int blocksize; }; struct virep { int error; char msg[MAX_IOCTL_REPLY - sizeof(int)]; }; #define VINUM_SETSTATE_DF _IOC(IOC_IN | IOC_OUT, L, 76, MAX_IOCTL_REPLY) int main(int argc, char **argv) { int nproc = (argc > 1) ? atoi(argv[1]) : 4; int sdno = (argc > 2) ? atoi(argv[2]) : 1; int maxioctl = (argc > 3) ? atoi(argv[3]) : 0; /* 0 = until done */ int i, fd; long total = 0; for (i = 0; i < nproc; i++) { pid_t p = fork(); if (p == 0) { struct vimsg msg; struct virep *rep = (struct virep *)&msg; long count = 0; fd = open("/dev/vinum/controld", O_RDWR); if (fd < 0) { perror("controld"); _exit(1); } for (;;) { memset(&msg, 0, sizeof(msg)); msg.index = sdno; msg.type = 1; msg.state = 3; msg.blocksize = 4096; /* 4k blocks: skip = 4k */ ioctl(fd, VINUM_SETSTATE_DF, &msg); count++; if (rep->error != 35) { /* EAGAIN = keep going */ fprintf(stderr, "child %d: %ld ioctls, final error=%d msg=%s\n", getpid(), count, rep->error, rep->msg); break; } if (maxioctl && count >= maxioctl) { fprintf(stderr, "child %d: stopped after %ld ioctls (bounded)\n", getpid(), count); break; } } _exit(0); } } for (i = 0; i < nproc; i++) wait(NULL); fprintf(stderr, "all racers done (%ld)\n", total); return 0; } |