/*
 * race5462 -- DF-5462 racer: hammer ptopen() (cam_extend_get) on stable
 * /dev/pt0../dev/ptN while the ptsim bus grows past every 8-unit
 * boundary, forcing cam_extend_set() to kfree() the array the racer may
 * be reading.
 *
 * Usage: race5462 <nunits> <nproc> <seconds>
 * Each child process races ONE unit (unit = child index % nunits) with
 * open()/close() so every open exercises cam_extend_get(ptperiphs, unit).
 */
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <sys/ptio.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>

struct stats {
	volatile unsigned long opens;
	volatile unsigned long ebusy;
	volatile unsigned long enxio;
	volatile unsigned long other;
	volatile long last_errno;
};

static struct stats *st;
static volatile sig_atomic_t run_flag = 1;
static int consfd = -1;
static int my_idx;

static void
heartbeat(int sig)
{
	run_flag = 0;
	if (consfd >= 0 && st != NULL)
		dprintf(consfd, "[RACER] child %d done: opens=%lu enxio=%lu "
		    "other=%lu last_errno=%ld\n", my_idx,
		    st[my_idx].opens, st[my_idx].enxio, st[my_idx].other,
		    st[my_idx].last_errno);
}

static void
on_alarm(int sig)
{
	heartbeat(sig);
}

int
main(int argc, char **argv)
{
	int nunits, nproc, secs;
	int i, pid0;
	char path[64];

	if (argc != 4) {
		fprintf(stderr, "usage: %s <nunits> <nproc> <seconds>\n",
		    argv[0]);
		return (2);
	}
	nunits = atoi(argv[1]);
	nproc = atoi(argv[2]);
	secs = atoi(argv[3]);

	consfd = open("/dev/console", O_WRONLY);
	st = mmap(NULL, sizeof(*st) * nproc, PROT_READ | PROT_WRITE,
	    MAP_SHARED | MAP_ANON, -1, 0);
	if (st == MAP_FAILED) {
		perror("mmap");
		return (2);
	}

	signal(SIGALRM, on_alarm);
	pid0 = 0;
	for (i = 0; i < nproc; i++) {
		int pid = fork();
		if (pid < 0) {
			perror("fork");
			return (2);
		}
		if (pid == 0) {
			int unit = i % nunits;
			my_idx = i;
			signal(SIGALRM, on_alarm);
			alarm(secs);
			snprintf(path, sizeof(path), "/dev/pt%d", unit);
			while (run_flag) {
				int fd = open(path, O_RDWR);
				if (fd >= 0) {
					int t = 1;
					ioctl(fd, PTIOCGETTIMEOUT, &t);
					close(fd);
					st[i].opens++;
				} else if (errno == EBUSY) {
					st[i].ebusy++;
				} else if (errno == ENXIO) {
					st[i].enxio++;
				} else {
					st[i].other++;
					st[i].last_errno = errno;
				}
			}
			_exit(0);
		}
		if (i == 0)
			pid0 = pid;
	}

	/* parent: wait, then aggregate */
	for (i = 0; i < nproc; i++)
		wait(NULL);
	{
		unsigned long o = 0, b = 0, n = 0, g = 0;
		long le = 0;
		for (i = 0; i < nproc; i++) {
			o += st[i].opens; b += st[i].ebusy;
			n += st[i].enxio; g += st[i].other;
			if (st[i].last_errno)
				le = st[i].last_errno;
		}
		printf("RACER-DONE opens=%lu ebusy=%lu enxio=%lu other=%lu "
		    "last_other_errno=%ld\n", o, b, n, g, le);
		if (g != 0)
			printf("RACER-ANOMALY unexpected errno observed\n");
	}
	return (0);
}
