/*
 * memhog.c - Aggressive unprivileged memory-pressure driver for DF-2463.
 *
 * Allocates and touches anonymous memory until malloc() starts failing,
 * then keeps touching the resident set so the VM pager cannot reclaim it.
 * Goal: drive vm.stats.vm.v_free_count toward vm.v_free_min so that kernel
 * kmalloc(..., M_NOWAIT) (e.g. the iscsi pdu_zone) starts returning NULL.
 *
 * Build: cc -O2 -o memhog memhog.c
 * Run:   ./memhog [chunkMB]
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define MAXCHUNKS 4096

int main(int argc, char **argv) {
    size_t chunk = (argc > 1 ? (size_t)atoi(argv[1]) : 8) * 1024 * 1024;
    char *blocks[MAXCHUNKS];
    int n = 0, round;
    /* Phase 1: grab as much memory as possible */
    while (n < MAXCHUNKS) {
        char *p = malloc(chunk);
        if (p == NULL) break;
        memset(p, 0xab, chunk);   /* touch every page -> resident */
        blocks[n++] = p;
        if ((n % 16) == 0)
            fprintf(stderr, "memhog: %d chunks = %lu MB\n", n, (unsigned long)(n * (chunk >> 20)));
    }
    fprintf(stderr, "memhog: exhausted malloc at %d chunks (%lu MB); churning\n",
            n, (unsigned long)(n * (chunk >> 20)));
    fflush(stderr);
    /* Phase 2: keep churning to defeat the pager (swap is off in run.sh) */
    for (round = 0; ; round++) {
        for (int i = 0; i < n; i++)
            memset(blocks[i], (round & 0xff), chunk);
        usleep(100000);
    }
    return 0;
}
