/*
 * DF-2615 PoC — one kdmsg_msg (the LNK_PING allocated in
 * kdmsg_iocom_uninit, kern_dmsg.c:280-281) leaks per hammer2
 * mount/umount cycle when the mount was created with
 * cluster_fd < 0 (no iocom threads ever created, so nothing
 * drains iocom->msgq and EXITNOACC is never set).
 *
 * On the instrumented kernel each cycle prints:
 *   DF2615_LEAK: 1 kdmsg_msg(s) queued at iocom_uninit exit
 *
 * Note: stock mount_hammer2(8) usually supplies cluster_fd >= 0
 * (it auto-starts the hammer2 service daemon and connects to it),
 * which creates the iocom threads; in that configuration the write
 * thread sets EXITNOACC and the PING is drained.  The leak path is
 * taken whenever the mount proceeds without a cluster connection
 * (daemon not running / connect failed -> mount_hammer2 passes -1,
 * or any direct mount(2) caller).
 *
 * build: cc -O -I/usr/src/sys -o df2615_trigger df2615_trigger.c
 * run:   (root) ./df2615_trigger [cycles]
 */
#include <sys/param.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <vfs/hammer2/hammer2_mount.h>
#include <err.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define MOUNTPT	"/mnt/h2leak"
#define VOLUME	"/dev/vn0@testvol"

int
main(int argc, char **argv)
{
	struct hammer2_mount_info info;
	int n = (argc > 1) ? atoi(argv[1]) : 20;
	int i;

	if (mkdir(MOUNTPT, 0755) < 0 && errno != EEXIST)
		err(1, "mkdir " MOUNTPT);

	memset(&info, 0, sizeof(info));
	info.volume = VOLUME;
	info.hflags = 0;
	info.cluster_fd = -1;		/* no cluster -> no iocom threads */

	for (i = 0; i < n; i++) {
		if (mount("hammer2", MOUNTPT, 0, &info) < 0)
			err(1, "mount cycle %d", i);
		if (unmount(MOUNTPT, 0) < 0)
			err(1, "unmount cycle %d", i);
	}
	printf("CYCLES_DONE=%d (cluster_fd=-1, no iocom threads)\n", n);
	return (0);
}
