DF-5833 / eater.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 171 172 | /* * eater.c -- DF-5833 rig: pin VM free pages below v_free_reserved and hold, * then serve "drips" on stdin. Persistent across attempts. * * Usage: eater <target_free_pages> <ready_marker> <start_gate> * Commands on stdin (line-based, polled via kqueue/select): * D <bytes> -- munmap ~<bytes> of eaten memory (instant page release) * R -- re-eat until free <= target again (after a drip burst) * Q -- munmap everything and exit */ #include <sys/types.h> #include <sys/mman.h> #include <sys/sysctl.h> #include <sys/time.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <errno.h> #define CHUNK (1UL << 20) /* 1MB eat units */ #define MAXCHUNK 8192 static void *chunks[MAXCHUNK]; static int nchunks; static size_t eaten; static long target; static long free_count(void) { size_t val = 0; size_t len = sizeof(val); if (sysctlbyname("vm.stats.vm.v_free_count", &val, &len, NULL, 0) < 0) { perror("sysctl"); exit(1); } return (long)val; } /* Eat one chunk; returns 0 on success, -1 if we could not (mmap fail). */ static int eat_one(void) { if (nchunks == MAXCHUNK) return (-1); void *p = mmap(NULL, CHUNK, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); if (p == MAP_FAILED) return (-1); memset(p, 0x5a, CHUNK); chunks[nchunks++] = p; eaten += CHUNK; return (0); } int main(int argc, char **argv) { target = (argc > 1) ? atol(argv[1]) : 2000; const char *marker = (argc > 2) ? argv[2] : NULL; const char *startgate = (argc > 3) ? argv[3] : NULL; char line[128]; long fc; int i; setvbuf(stdout, NULL, _IONBF, 0); if (startgate) { while (access(startgate, F_OK) != 0) usleep(2000); } /* Phase 1: eat until free_count <= target or we self-stall. */ for (;;) { fc = free_count(); if (fc <= target) break; struct timeval t0, t1; gettimeofday(&t0, NULL); if (eat_one() != 0) { fprintf(stderr, "eater: mmap failed (free=%ld)\n", fc); break; } gettimeofday(&t1, NULL); long us = (t1.tv_sec - t0.tv_sec) * 1000000 + (t1.tv_usec - t0.tv_usec); if (us > 500000) { fprintf(stderr, "eater: stalled (%ld us) - at floor\n", us); break; } } fprintf(stdout, "EATER-READY free=%ld eaten=%zuMB chunks=%d\n", free_count(), eaten >> 20, nchunks); if (marker) { int fd = open(marker, O_CREAT | O_WRONLY | O_TRUNC, 0644); if (fd >= 0) { dprintf(fd, "ready\n"); close(fd); } } /* Phase 2: serve commands. NO hold loop -- sustained pinning * drives the system into swap exhaustion and the OOM killer. * Pressure windows are short (eat, park, crash, drip). */ int ci = 0; /* drip cursor: chunk index */ size_t off = 0; /* drip cursor: offset inside chunk */ for (;;) { fd_set rfds; struct timeval tv; FD_ZERO(&rfds); FD_SET(0, &rfds); tv.tv_sec = 0; tv.tv_usec = 50000; if (select(1, &rfds, NULL, NULL, &tv) <= 0) continue; if (fgets(line, sizeof(line), stdin) == NULL) break; if (line[0] == 'Q') break; if (line[0] == 'R') { /* re-pin: eat until free <= target again, then * re-arm the ready marker */ unlink(marker); while (free_count() > target) { if (eat_one() != 0) break; } fprintf(stdout, "RE-EATEN free=%ld chunks=%d\n", free_count(), nchunks); if (marker) { int fd = open(marker, O_CREAT | O_WRONLY | O_TRUNC, 0644); if (fd >= 0) close(fd); } continue; } if (line[0] != 'D') continue; size_t want = strtoul(line + 2, NULL, 10); size_t given = 0; while (given < want && ci < nchunks) { char *base = (char *)chunks[ci] + off; size_t piece = CHUNK - off; if (piece > 65536) piece = 65536; if (piece > (want - given)) piece = (want - given) & ~4095UL; if (piece == 0) piece = 4096; munmap(base, piece); off += piece; given += piece; if (off >= CHUNK) { off = 0; ci++; } } if (ci >= nchunks) { /* exhausted drip region: reset cursor */ ci = 0; off = 0; } fprintf(stdout, "DRIP %zu free=%ld\n", given, free_count()); } for (i = 0; i < nchunks; i++) munmap(chunks[i], CHUNK); fprintf(stdout, "EATER-DONE free=%ld\n", free_count()); return (0); } |