DF-4563 / stall.c
/* * DF-4563 minimal trigger: vn(4) close-path teardown race. * * Loop: clone a vn unit via /dev/vn, attach it swap-backed, detach it * (arms VNF_DESTROY on clone units), open the unit a second time, then * race two close() calls on the two descriptors. * * On the stock kernel this wedges the losing close in uninterruptible * sleep (often for minutes, hanging unrelated devfs operations too) and * then panics with: * panic: assertion "(sr->flags & SRF_PUTAWAY) == 0" failed in sysref_put * release_dev() -> devfs_destroy_dev_worker() -> devfs_destroy_related_worker() */ #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <fcntl.h> #include <time.h> #include <pthread.h> #include <sys/ioctl.h> #include <sys/stat.h> #include <sys/vnioctl.h> #include <string.h> #include <unistd.h> static volatile int barrier; static int fdA, fdB; static struct timespec tA0, tA1, tB0, tB1; static void *closerA(void *x){ while(!barrier); clock_gettime(CLOCK_MONOTONIC,&tA0); close(fdA); clock_gettime(CLOCK_MONOTONIC,&tA1); return NULL; } static void *closerB(void *x){ while(!barrier); clock_gettime(CLOCK_MONOTONIC,&tB0); close(fdB); clock_gettime(CLOCK_MONOTONIC,&tB1); return NULL; } static double ts(struct timespec *a, struct timespec *b){ return (b->tv_sec-a->tv_sec)+1e-9*(b->tv_nsec-a->tv_nsec); } int main(int argc, char **argv) { struct vn_ioctl vio; struct stat st, st2; char path[64]; int n = argc > 1 ? atoi(argv[1]) : 500; double worst = 0; int stalls = 0; pthread_t ta, tb; int i, u; for (i = 0; i < n; i++) { int fd1 = open("/dev/vn", O_RDWR); /* clone */ if (fd1 < 0 || fstat(fd1, &st) < 0) { printf("clone fail\n"); break; } path[0] = 0; for (u = 4; u < 64; u++) { /* find our node */ snprintf(path, sizeof(path), "/dev/vn%d", u); if (stat(path, &st2) < 0) continue; if (st2.st_rdev == st.st_rdev) break; path[0] = 0; } if (!path[0]) { close(fd1); continue; } bzero(&vio, sizeof(vio)); vio.vn_file = NULL; /* swap-backed */ vio.vn_size = 16; if (ioctl(fd1, VNIOCATTACH, &vio) < 0) { close(fd1); continue; } bzero(&vio, sizeof(vio)); if (ioctl(fd1, VNIOCDETACH, &vio) < 0) { close(fd1); continue; } int fd2 = open(path, O_RDWR); if (fd2 < 0) { close(fd1); continue; } fdA = fd1; fdB = fd2; barrier = 0; pthread_create(&ta, NULL, closerA, NULL); pthread_create(&tb, NULL, closerB, NULL); barrier = 1; pthread_join(ta, NULL); pthread_join(tb, NULL); double d = ts(&tA0, &tA1), e = ts(&tB0, &tB1); if (d > worst) worst = d; if (e > worst) worst = e; if (d > 1.0 || e > 1.0) { stalls++; printf("iter %d: closeA=%.3fs closeB=%.3fs\n", i, d, e); fflush(stdout); } if ((i % 100) == 0) { printf("i=%d worst=%.3f stalls=%d\n", i, worst, stalls); fflush(stdout); } } printf("DONE n=%d worst_close=%.3fs stalls>1s=%d\n", n, worst, stalls); return 0; } |