DF-2568 / race_flush_v5.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 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 | /* * DF-2568 — v5: focused fsync-vs-unlink race on a single file. * * Theory: fsync(file) dispatches a flush xop for the file's inode (hashes to * thread group based on FILE inode). unlink(file) dispatches an unlink xop via * the PARENT directory (hashes to thread group based on DIR inode). Different * groups = different threads = can run concurrently. * * The flush xop locks the file's chain. The unlink xop looks up the file's * chain in the parent dir and tries to lock it -> BLOCKS. When the flush hits * flush_core:662 (unlock chain), the unlink grabs it, calls chain_delete * (sets chain->parent = NULL). The flush then sees chain->parent != parent at * :681, returns retry=1, and the retry loop at :405 derefs NULL. * * Tight loop per worker: write+fsync+unlink+recreate. Many workers + syncers. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <signal.h> #include <sys/wait.h> #include <sys/stat.h> #include <errno.h> static volatile sig_atomic_t stop = 0; static void on_alarm(int s){ (void)s;stop=1; } #define BLK 8192 static void racer(int id, const char *dir, long iters) { char path[512], buf[BLK]; long i; memset(buf, (char)(id + 0x42), sizeof(buf)); for (i = 0; i < iters && !stop; i++) { int fd, b; snprintf(path, sizeof(path), "%s/r%d", dir, id); /* create + write (dirty chains) */ fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0666); if (fd < 0) { /* file might be being unlinked by us; retry */ continue; } for (b = 0; b < 6; b++) { buf[0] = (char)(id ^ i ^ b); write(fd, buf, sizeof(buf)); } /* fsync: dispatch flush xop for this file's inode */ fsync(fd); close(fd); /* unlink: dispatch unlink xop via parent dir inode. * This races with any concurrent flush of this chain. */ unlink(path); } _exit(0); } /* Syncer: global sync to flush the entire topology */ static void syncer(int id, long iters) { long i; for (i = 0; i < iters && !stop; i++) sync(); _exit(0); } /* Renamer: churn topology via rename (creates chain moves) */ static void renamer(int id, const char *dir, long iters) { char p1[512], p2[512]; long i; for (i = 0; i < iters && !stop; i++) { snprintf(p1, sizeof(p1), "%s/m%d_a", dir, id); snprintf(p2, sizeof(p2), "%s/m%d_b", dir, id); /* create p1 if not exists */ int fd = open(p1, O_CREAT | O_TRUNC | O_RDWR, 0666); if (fd >= 0) { write(fd, p1, sizeof(p1)); close(fd); } rename(p1, p2); /* swap back */ rename(p2, p1); unlink(p1); unlink(p2); } _exit(0); } int main(int argc, char **argv) { const char *dir = NULL; int nracers = 20, nsyncers = 6, nrenamers = 4; long iters = 50000000; int timeout = 0, opt; while ((opt = getopt(argc, argv, "d:r:s:R:i:t:")) != -1) { switch (opt) { case 'd': dir = optarg; break; case 'r': nracers = atoi(optarg); break; case 's': nsyncers = atoi(optarg); break; case 'R': nrenamers = atoi(optarg); break; case 'i': iters = atol(optarg); break; case 't': timeout = atoi(optarg); break; default: fprintf(stderr,"usage: %s -d dir [-r racers] [-s syncers] [-R renamers] [-i iters] [-t sec]\n",argv[0]); return 2; } } if (!dir) { fprintf(stderr,"%s: need -d dir\n",argv[0]); return 2; } fprintf(stderr,"DF-2568 v5: dir=%s racers=%d syncers=%d renamers=%d iters=%ld timeout=%d\n", dir, nracers, nsyncers, nrenamers, iters, timeout); signal(SIGALRM, on_alarm); if (timeout > 0) alarm((unsigned)timeout); int nkids = nracers + nsyncers + nrenamers; pid_t *kids = calloc(nkids, sizeof(pid_t)); if (!kids) { perror("calloc"); return 1; } int k, ki = 0; for (k = 0; k < nracers; k++) { pid_t p = fork(); if (p < 0) { perror("fork"); return 1; } if (p == 0) racer(k, dir, iters); kids[ki++] = p; } for (k = 0; k < nsyncers; k++) { pid_t p = fork(); if (p < 0) { perror("fork"); return 1; } if (p == 0) syncer(k, iters); kids[ki++] = p; } for (k = 0; k < nrenamers; k++) { pid_t p = fork(); if (p < 0) { perror("fork"); return 1; } if (p == 0) renamer(k, dir, iters); kids[ki++] = p; } for (k = 0; k < nkids; k++) if (kids[k] > 0) waitpid(kids[k], NULL, 0); fprintf(stderr, "DF-2568 v5: done (no panic)\n"); return 0; } |