DF-0245 / df0245_mig.c
/* * DF-0245 - Per-cpu iowbytes counter underflow via thread migration. * [sys/kern/kern_iosched.c badjiosched(), lines 65-107] * * Bug: badjiosched() adds a thread's I/O bytes to ioscpu[mycpu].iowbytes * (line 80), but the per-thread accumulator td->td_iosdata.iowbytes * (line 79) MIGRATES with the thread across CPUs. The decay path * (lines 88-90) subtracts a td->iowbytes-derived amount from * ioscpu[mycpu] -- which is the thread's CURRENT cpu, not the cpu(s) * that actually received the contribution. If the thread accumulated * weight on CPU A then migrated to CPU B, the decay subtracts from B * which never got the addition -> ioscpu[B].iowbytes underflows * (size_t) to a huge value, corrupting the I/O throttle factor. * * This harness hammers bwillwrite()/bwillinode() (the only callers of * badjiosched) from many threads to maximize the chance of inter-call * CPU migration. The underflow is timing-dependent; observation is via * the root sysctl 'sysctl iosched.debug=1' which kprintf's factor and * iowbytes. A huge iowbytes / factor near 0 or >100 indicates the * accounting broke. * * Build: cc -O2 -lpthread -o df0245_mig df0245_mig.c * Run: ./df0245_mig [threads] [secs] * * Observe (root, another shell, BEFORE running): sysctl iosched.debug=1 * then watch 'dmesg'. */ #include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <sys/stat.h> static volatile int stop = 0; static void * worker(void *arg) { long tid = (long)arg; char path[64]; char buf[4096]; int fd; snprintf(path, sizeof(path), "/tmp/df0245_io_%ld", tid); (void)unlink(path); fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0600); if (fd < 0) return NULL; memset(buf, (int)tid & 0xff, sizeof(buf)); /* Heavy buffered-write workload -> bwillwrite() -> badjiosched(). * fsync occasionally to push buffers and churn accounting. */ while (!stop) { for (int i = 0; i < 256; i++) { if (write(fd, buf, sizeof(buf)) < 0) break; } fsync(fd); lseek(fd, 0, SEEK_SET); } close(fd); unlink(path); return NULL; } int main(int argc, char **argv) { int n = (argc > 1) ? atoi(argv[1]) : 6; int secs = (argc > 2) ? atoi(argv[2]) : 15; pthread_t *th = calloc(n, sizeof(*th)); printf("DF-0245: spawning %d writer threads for %ds " "(hammer bwillwrite/badjiosched)\n", n, secs); printf("DF-0245: watch for ioscpu[].iowbytes underflow / wild factor " "with 'sysctl iosched.debug=1' + dmesg\n"); fflush(stdout); for (long i = 0; i < n; i++) pthread_create(&th[i], NULL, worker, (void *)i); sleep(secs); stop = 1; for (int i = 0; i < n; i++) pthread_join(th[i], NULL); printf("DF-0245: workload complete. Accounting underflow is confirmed " "by source trace (see VERDICT.md); runtime observation is " "timing-dependent on migration.\n"); free(th); return 0; } |