DF-2628 / drainfill.c
/* * DF-2628 drain filler: single process, no threads. Writes 256K chunks * to <dir>/drainfile in one tight loop until write() fails, then keeps * retrying (each retry re-runs the enospace precheck, keeping the * per-tick cache hot while attempting to consume any space that appears). * Prints progress on stderr. * * usage: drainfill <dir> */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <fcntl.h> #include <unistd.h> #include <time.h> #include <sys/stat.h> #include <signal.h> #include <stdlib.h> static void bye(int sig __unused) { _exit(0); } int main(int argc, char **argv) { char path[512], buf[262144]; int fd; long long bw = 0, last = 0; time_t t0 = time(NULL); if (argc < 2) { fprintf(stderr, "usage: %s <dir> [seconds]\n", argv[0]); return 64; } if (argc >= 3) { signal(SIGALRM, bye); alarm(atoi(argv[2])); } snprintf(path, sizeof(path), "%s/drainfile", argv[1]); { /* incompressible data: hammer2 auto-compresses (lz4) and * does not allocate for all-zero data; random-ish content * forces real allocation. */ size_t i; uint32_t st = 0x12345678u ^ (uint32_t)(bw >> 9); for (i = 0; i < sizeof(buf); i += 4) { st ^= st << 13; st ^= st >> 17; st ^= st << 5; ((uint32_t *)buf)[i / 4] = st; } } fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0644); if (fd < 0) { perror(path); return 1; } for (;;) { ssize_t r = write(fd, buf, sizeof(buf)); if (r < 0) { if (errno == ENOSPC) { fprintf(stderr, "ENOSPC at %lld bytes " "(%lds), retry-loop\n", bw, (long)(time(NULL) - t0)); /* keep retrying: consumes space that may * reappear (bulkfree) */ usleep(2000); continue; } perror("write"); return 1; } bw += r; if (r < (ssize_t)sizeof(buf)) { fprintf(stderr, "short write %zd at %lld\n", r, bw); } if (bw - last > (32LL << 20)) { last = bw; fprintf(stderr, "drained %lld MB (%lds)\n", bw >> 20, (long)(time(NULL) - t0)); } } return 0; } |