/*
 * df2712_storm.c -- unprivileged scheduler-pressure storm for DF-2712.
 *
 * Usage: df2712_storm <nprocs> <seconds> [pin_victim]
 *
 *   nprocs processes, each single-threaded, hammering sched_yield().
 *   Every yield drives bsd4_release_curproc -> bsd4_setrunqueue (fresh
 *   lwp_rebal_ticks) -> bsd4_acquire_curproc -> bsd4_select_curproc ->
 *   bsd4_chooseproc_locked_cache_coherent under bsd4_spin, which calls
 *   bsd4_kick_helper (up to queue_checks times per scan) -- the spin-held
 *   lwkt_send_ipiq()/wakeup() path at usched_bsd4.c:1590-1615.
 *
 *   No affinity syscalls are needed: chooseproc_cc kicks any candidate
 *   homed on a non-sibling cpu that is batchy and inside its rebal
 *   window (usched_bsd4.c:1483-1520).  A uniform yield storm of N
 *   processes spread over 6 cpus satisfies all three constantly.
 *
 *   pin_victim (optional, requires privilege on this kernel: usched_set
 *   USCHED_SET_CPU needs SYSCAP_NOSCHED_CPUSET = uid 0, kern_usched.c:235):
 *   if given, 5/6 of the processes pin themselves to cpu `pin_victim`
 *   (perpetual kick target) and 1/6 spread over the other cpus (kickers).
 *
 * Exits 0 printing "STORM-DONE ..." on survival.
 */
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/usched.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <errno.h>
#include <sched.h>

static double
now(void)
{
	struct timespec ts;
	clock_gettime(CLOCK_MONOTONIC, &ts);
	return (ts.tv_sec + ts.tv_nsec / 1e9);
}

int
main(int argc, char **argv)
{
	long nprocs = 200, secs = 60;
	int pin_victim = -1;
	long i, ok = 0, bad = 0;
	int ncpu = 1;
	size_t sz = sizeof(ncpu);
	double t0;

	if (argc > 1) nprocs = atol(argv[1]);
	if (argc > 2) secs = atol(argv[2]);
	if (argc > 3) pin_victim = atoi(argv[3]);
	sysctlbyname("hw.ncpu", &ncpu, &sz, NULL, 0);

	t0 = now();
	for (i = 0; i < nprocs; ++i) {
		pid_t pid = fork();
		if (pid == 0) {
			double deadline = secs;
			volatile unsigned long n = 0;
			/* optional privilege-assisted concentration pinning */
			if (pin_victim >= 0) {
				int c = ((i % 6) == 5) ? (i % (ncpu - 1))
							: pin_victim % ncpu;
				if (usched_set(0, USCHED_SET_CPU, &c,
					       sizeof(c)) < 0)
					; /* unpriv: stay unpinned */
			}
			while (now() - t0 < deadline)
				n += sched_yield();
			_exit(0);
		}
		if (pid < 0) {
			perror("fork");
			break;
		}
	}
	for (i = 0; i < nprocs; ++i) {
		int st = 0;
		if (wait(&st) < 0) {
			if (errno == EINTR) { --i; continue; }
			break;
		}
		if (WIFEXITED(st) && WEXITSTATUS(st) == 0)
			++ok;
		else
			++bad;
	}
	printf("STORM-DONE n=%ld ok=%ld bad=%ld secs=%ld pin=%d\n",
	    nprocs, ok, bad, secs, pin_victim);
	return (bad != 0);
}
