// SPDX-License-Identifier: GPL-2.0
#include <stdio.h>
#include <sys/mman.h>
#include <unistd.h>

#include "utils.h"

/* This must match the huge page & THP size */
#define SIZE	(16 * 1024 * 1024)

static int test_body(void)
{
	void *addr;
	char *p;

	addr = (void *)0xa0000000;

	p = mmap(addr, SIZE, PROT_READ | PROT_WRITE,
		 MAP_HUGETLB | MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
	if (p != MAP_FAILED) {
		/*
		 * Typically the mmap will fail because no huge pages are
		 * allocated on the system. But if there are huge pages
		 * allocated the mmap will succeed. That's fine too, we just
		 * munmap here before continuing.  munmap() length of
		 * MAP_HUGETLB memory must be hugepage aligned.
		 */
		if (munmap(addr, SIZE)) {
			perror("munmap");
			return 1;
		}
	}

	p = mmap(addr, SIZE, PROT_READ | PROT_WRITE,
		 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
	if (p == MAP_FAILED) {
		printf("Mapping failed @ %p\n", addr);
		perror("mmap");
		return 1;
	}

	/*
	 * Either a user or kernel access is sufficient to trigger the bug.
	 * A kernel access is easier to spot & debug, as it will trigger the
	 * softlockup or RCU stall detectors, and when the system is kicked
	 * into xmon we get a backtrace in the kernel.
	 *
	 * A good option is:
	 *  getcwd(p, SIZE);
	 *
	 * For the purposes of this testcase it's preferable to spin in
	 * userspace, so the harness can kill us if we get stuck. That way we
	 * see a test failure rather than a dead system.
	 */
	*p = 0xf;

	munmap(addr, SIZE);

	return 0;
}

static int test_main(void)
{
	int i;

	/* 10,000 because it's a "bunch", and completes reasonably quickly */
	for (i = 0; i < 10000; i++)
		if (test_body())
			return 1;

	return 0;
}

int main(void)
{
	return test_harness(test_main, "hugetlb_vs_thp");
}