DF-3250 / fh_race.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 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 | /* * DF-3250 fhopen racer - two pinned processes race fhopen() of the SAME * (cold) ext2 inode, driving VFS_FHTOVP -> ext2_vget -> ext2_ihashget/ * ext2_alloc_vnode directly with no namei in between. * * When the loser's ext2fs_inode_hash_lock check lands after the winner's * unlock, ext2_ihashins returns EBUSY and the handler at ext2_vfsops.c * 1313-1320 double-frees the inode -> KKASSERT bmap panic. * * usage: fh_race <dir-with-rNNNNN> <nfiles> <seconds> * (must run as root: getfh/fhopen are RESTRICTEDROOT) */ #include <sys/types.h> #include <sys/mman.h> #include <sys/wait.h> #include <sys/usched.h> #include <sys/mount.h> #include <sys/file.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <time.h> #include <errno.h> struct shared { volatile uint64_t narrate; volatile uint64_t ndone; volatile uint32_t stop; }; static inline void cpu_pause(void) { __asm__ volatile("pause"); } static inline uint64_t rdtsc(void) { unsigned int lo, hi; __asm__ volatile("rdtsc" : "=a"(lo), "=d"(hi)); return ((uint64_t)hi << 32) | lo; } int main(int argc, char **argv) { const char *dir; long nfiles; long secs; int id, fd, i; uint64_t k; time_t t0; struct shared *s; struct fhandle *fhs; char path[512]; int lockfd; if (argc < 4) { fprintf(stderr, "usage: %s <dir> <nfiles> <seconds>\n", argv[0]); return (2); } dir = argv[1]; nfiles = atol(argv[2]); secs = atol(argv[3]); /* * No flock: a wedged (D-state) partner would hold it forever and * block respawns; the supervisor enforces single-instance instead. */ lockfd = open("/tmp/df3250.lock", O_CREAT | O_RDWR, 0644); (void)lockfd; s = mmap(NULL, sizeof(*s), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0); memset((void *)s, 0, sizeof(*s)); fhs = malloc(sizeof(struct fhandle) * nfiles); /* phase A: collect file handles */ for (i = 0; i < nfiles; i++) { snprintf(path, sizeof(path), "%s/r%05d", dir, i); if (getfh(path, &fhs[i]) != 0) { fprintf(stderr, "getfh(%s): %s\n", path, strerror(errno)); return (1); } } fprintf(stderr, "collected %ld file handles\n", nfiles); id = fork() != 0; { int cpu = id; if (usched_set(getpid(), USCHED_SET_CPU, &cpu, sizeof(cpu)) != 0) fprintf(stderr, "id=%d pin FAILED\n", id); } t0 = time(NULL); for (k = 0; ; k++) { uint64_t idx; uint64_t deadline; if (s->stop) break; if ((uint64_t)(time(NULL) - t0) > (uint64_t)secs) { s->stop = 1; break; } /* * Barriers with a stall deadline: if the partner wedged * (e.g. permanently blocked in the kernel on the broken * ext2fs_inode_hash_lock lost-wakeup), exit so the * supervisor can respawn the pair. */ __sync_add_and_fetch(&s->narrate, 1); deadline = rdtsc() + 12000000000ULL; /* ~4s @3GHz */ while (s->narrate < 2 * (k + 1)) { if (rdtsc() > deadline) goto stalled; cpu_pause(); } /* phase sweep 0..~250us in ~80ns steps */ if (id) { uint64_t spin = (k % 3000ULL) * 250ULL; uint64_t tend = rdtsc() + spin; while (rdtsc() < tend) cpu_pause(); } idx = k % (uint64_t)nfiles; fd = fhopen(&fhs[idx], O_RDONLY); if (fd >= 0) close(fd); __sync_add_and_fetch(&s->ndone, 1); deadline = rdtsc() + 12000000000ULL; /* ~4s @3GHz */ while (s->ndone < 2 * (k + 1)) { if (rdtsc() > deadline) goto stalled; cpu_pause(); } if (id && (k % 50000) == 0) fprintf(stderr, "attempts=%llu elapsed=%ld\n", (unsigned long long)k, (long)(time(NULL) - t0)); continue; stalled: fprintf(stderr, "id=%d: partner stalled at round %llu " "(lost-wakeup wedge?), exiting for respawn\n", id, (unsigned long long)k); break; } if (id) { int st; wait(&st); fprintf(stderr, "total attempts ~%llu\n", (unsigned long long)k); } return (0); } |