/*
 * DF-0245 - Per-cpu iowbytes counter underflow via thread migration.
 * [sys/kern/kern_iosched.c badjiosched(), lines 65-107]
 *
 * Bug: badjiosched() adds a thread's I/O bytes to ioscpu[mycpu].iowbytes
 * (line 80), but the per-thread accumulator td->td_iosdata.iowbytes
 * (line 79) MIGRATES with the thread across CPUs.  The decay path
 * (lines 88-90) subtracts a td->iowbytes-derived amount from
 * ioscpu[mycpu] -- which is the thread's CURRENT cpu, not the cpu(s)
 * that actually received the contribution.  If the thread accumulated
 * weight on CPU A then migrated to CPU B, the decay subtracts from B
 * which never got the addition -> ioscpu[B].iowbytes underflows
 * (size_t) to a huge value, corrupting the I/O throttle factor.
 *
 * This harness hammers bwillwrite()/bwillinode() (the only callers of
 * badjiosched) from many threads to maximize the chance of inter-call
 * CPU migration.  The underflow is timing-dependent; observation is via
 * the root sysctl 'sysctl iosched.debug=1' which kprintf's factor and
 * iowbytes.  A huge iowbytes / factor near 0 or >100 indicates the
 * accounting broke.
 *
 * Build:  cc -O2 -lpthread -o df0245_mig df0245_mig.c
 * Run:    ./df0245_mig [threads] [secs]
 *
 * Observe (root, another shell, BEFORE running): sysctl iosched.debug=1
 * then watch 'dmesg'.
 */

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>

static volatile int stop = 0;

static void *
worker(void *arg)
{
	long tid = (long)arg;
	char path[64];
	char buf[4096];
	int fd;
	snprintf(path, sizeof(path), "/tmp/df0245_io_%ld", tid);
	(void)unlink(path);
	fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0600);
	if (fd < 0) return NULL;
	memset(buf, (int)tid & 0xff, sizeof(buf));
	/* Heavy buffered-write workload -> bwillwrite() -> badjiosched().
	 * fsync occasionally to push buffers and churn accounting. */
	while (!stop) {
		for (int i = 0; i < 256; i++) {
			if (write(fd, buf, sizeof(buf)) < 0) break;
		}
		fsync(fd);
		lseek(fd, 0, SEEK_SET);
	}
	close(fd);
	unlink(path);
	return NULL;
}

int
main(int argc, char **argv)
{
	int n = (argc > 1) ? atoi(argv[1]) : 6;
	int secs = (argc > 2) ? atoi(argv[2]) : 15;
	pthread_t *th = calloc(n, sizeof(*th));

	printf("DF-0245: spawning %d writer threads for %ds "
	       "(hammer bwillwrite/badjiosched)\n", n, secs);
	printf("DF-0245: watch for ioscpu[].iowbytes underflow / wild factor "
	       "with 'sysctl iosched.debug=1' + dmesg\n");
	fflush(stdout);

	for (long i = 0; i < n; i++)
		pthread_create(&th[i], NULL, worker, (void *)i);

	sleep(secs);
	stop = 1;
	for (int i = 0; i < n; i++)
		pthread_join(th[i], NULL);

	printf("DF-0245: workload complete. Accounting underflow is confirmed "
	       "by source trace (see VERDICT.md); runtime observation is "
	       "timing-dependent on migration.\n");
	free(th);
	return 0;
}
