1f08c3bdfSopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
2f08c3bdfSopenharmony_ci/*
3f08c3bdfSopenharmony_ci * Copyright (C) 2015  Yi Zhang <wetpzy@gmail.com>
4f08c3bdfSopenharmony_ci *                     Li Wang <liwang@redhat.com>
5f08c3bdfSopenharmony_ci *
6f08c3bdfSopenharmony_ci * DESCRIPTION:
7f08c3bdfSopenharmony_ci *
8f08c3bdfSopenharmony_ci *   It is a regression test for commit:
9f08c3bdfSopenharmony_ci *   http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/
10f08c3bdfSopenharmony_ci *   commit/?id=13d60f4
11f08c3bdfSopenharmony_ci *
12f08c3bdfSopenharmony_ci *   The implementation of futex doesn't produce unique keys for futexes
13f08c3bdfSopenharmony_ci *   in shared huge pages, so threads waiting on different futexes may
14f08c3bdfSopenharmony_ci *   end up on the same wait list. This results in incorrect threads being
15f08c3bdfSopenharmony_ci *   woken by FUTEX_WAKE.
16f08c3bdfSopenharmony_ci *
17f08c3bdfSopenharmony_ci *   Needs to be run as root unless there are already enough huge pages available.
18f08c3bdfSopenharmony_ci *   In the fail case, which happens in the CentOS-6.6 kernel (2.6.32-504.8.1),
19f08c3bdfSopenharmony_ci *   the tests hangs until it times out after a 30-second wait.
20f08c3bdfSopenharmony_ci *
21f08c3bdfSopenharmony_ci */
22f08c3bdfSopenharmony_ci
23f08c3bdfSopenharmony_ci#include <stdio.h>
24f08c3bdfSopenharmony_ci#include <fcntl.h>
25f08c3bdfSopenharmony_ci#include <sys/time.h>
26f08c3bdfSopenharmony_ci#include <string.h>
27f08c3bdfSopenharmony_ci
28f08c3bdfSopenharmony_ci#include "futextest.h"
29f08c3bdfSopenharmony_ci#include "futex_utils.h"
30f08c3bdfSopenharmony_ci#include "lapi/mmap.h"
31f08c3bdfSopenharmony_ci#include "tst_safe_stdio.h"
32f08c3bdfSopenharmony_ci#include "tst_safe_pthread.h"
33f08c3bdfSopenharmony_ci
34f08c3bdfSopenharmony_cistatic futex_t *futex1, *futex2;
35f08c3bdfSopenharmony_ci
36f08c3bdfSopenharmony_cistatic struct tst_ts to;
37f08c3bdfSopenharmony_ci
38f08c3bdfSopenharmony_cistatic struct futex_test_variants variants[] = {
39f08c3bdfSopenharmony_ci#if (__NR_futex != __LTP__NR_INVALID_SYSCALL)
40f08c3bdfSopenharmony_ci	{ .fntype = FUTEX_FN_FUTEX, .tstype = TST_KERN_OLD_TIMESPEC, .desc = "syscall with old kernel spec"},
41f08c3bdfSopenharmony_ci#endif
42f08c3bdfSopenharmony_ci
43f08c3bdfSopenharmony_ci#if (__NR_futex_time64 != __LTP__NR_INVALID_SYSCALL)
44f08c3bdfSopenharmony_ci	{ .fntype = FUTEX_FN_FUTEX64, .tstype = TST_KERN_TIMESPEC, .desc = "syscall time64 with kernel spec"},
45f08c3bdfSopenharmony_ci#endif
46f08c3bdfSopenharmony_ci};
47f08c3bdfSopenharmony_ci
48f08c3bdfSopenharmony_cistatic void setup(void)
49f08c3bdfSopenharmony_ci{
50f08c3bdfSopenharmony_ci	struct futex_test_variants *tv = &variants[tst_variant];
51f08c3bdfSopenharmony_ci
52f08c3bdfSopenharmony_ci	tst_res(TINFO, "Testing variant: %s", tv->desc);
53f08c3bdfSopenharmony_ci	futex_supported_by_kernel(tv->fntype);
54f08c3bdfSopenharmony_ci
55f08c3bdfSopenharmony_ci	to = tst_ts_from_ns(tv->tstype, 30 * NSEC_PER_SEC);
56f08c3bdfSopenharmony_ci}
57f08c3bdfSopenharmony_ci
58f08c3bdfSopenharmony_cistatic void *wait_thread1(void *arg LTP_ATTRIBUTE_UNUSED)
59f08c3bdfSopenharmony_ci{
60f08c3bdfSopenharmony_ci	struct futex_test_variants *tv = &variants[tst_variant];
61f08c3bdfSopenharmony_ci
62f08c3bdfSopenharmony_ci	futex_wait(tv->fntype, futex1, *futex1, &to, 0);
63f08c3bdfSopenharmony_ci
64f08c3bdfSopenharmony_ci	return NULL;
65f08c3bdfSopenharmony_ci}
66f08c3bdfSopenharmony_ci
67f08c3bdfSopenharmony_cistatic void *wait_thread2(void *arg LTP_ATTRIBUTE_UNUSED)
68f08c3bdfSopenharmony_ci{
69f08c3bdfSopenharmony_ci	struct futex_test_variants *tv = &variants[tst_variant];
70f08c3bdfSopenharmony_ci	int res;
71f08c3bdfSopenharmony_ci
72f08c3bdfSopenharmony_ci	errno = 0;
73f08c3bdfSopenharmony_ci	res = futex_wait(tv->fntype, futex2, *futex2, &to, 0);
74f08c3bdfSopenharmony_ci	if (!res)
75f08c3bdfSopenharmony_ci		tst_res(TPASS, "Hi hydra, thread2 awake!");
76f08c3bdfSopenharmony_ci	else
77f08c3bdfSopenharmony_ci		tst_res(TFAIL | TERRNO, "Bug: wait_thread2 did not wake after 30 secs.");
78f08c3bdfSopenharmony_ci
79f08c3bdfSopenharmony_ci	return NULL;
80f08c3bdfSopenharmony_ci}
81f08c3bdfSopenharmony_ci
82f08c3bdfSopenharmony_cistatic void wakeup_thread2(void)
83f08c3bdfSopenharmony_ci{
84f08c3bdfSopenharmony_ci	struct futex_test_variants *tv = &variants[tst_variant];
85f08c3bdfSopenharmony_ci	void *addr;
86f08c3bdfSopenharmony_ci	int hpsz, pgsz;
87f08c3bdfSopenharmony_ci	pthread_t th1, th2;
88f08c3bdfSopenharmony_ci
89f08c3bdfSopenharmony_ci	hpsz = tst_get_hugepage_size();
90f08c3bdfSopenharmony_ci	tst_res(TINFO, "Hugepagesize %i", hpsz);
91f08c3bdfSopenharmony_ci
92f08c3bdfSopenharmony_ci	/*allocate some shared memory*/
93f08c3bdfSopenharmony_ci	addr = mmap(NULL, hpsz, PROT_WRITE | PROT_READ,
94f08c3bdfSopenharmony_ci	            MAP_SHARED | MAP_ANONYMOUS | MAP_HUGETLB, -1, 0);
95f08c3bdfSopenharmony_ci
96f08c3bdfSopenharmony_ci	if (addr == MAP_FAILED) {
97f08c3bdfSopenharmony_ci		if (errno == ENOMEM)
98f08c3bdfSopenharmony_ci			tst_brk(TCONF, "Cannot allocate hugepage, memory too fragmented?");
99f08c3bdfSopenharmony_ci
100f08c3bdfSopenharmony_ci		tst_brk(TBROK | TERRNO, "Cannot allocate hugepage");
101f08c3bdfSopenharmony_ci	}
102f08c3bdfSopenharmony_ci
103f08c3bdfSopenharmony_ci	pgsz = getpagesize();
104f08c3bdfSopenharmony_ci
105f08c3bdfSopenharmony_ci	/*apply the first subpage to futex1*/
106f08c3bdfSopenharmony_ci	futex1 = addr;
107f08c3bdfSopenharmony_ci	*futex1 = 0;
108f08c3bdfSopenharmony_ci	/*apply the second subpage to futex2*/
109f08c3bdfSopenharmony_ci	futex2 = (futex_t *)((char *)addr + pgsz);
110f08c3bdfSopenharmony_ci	*futex2 = 0;
111f08c3bdfSopenharmony_ci
112f08c3bdfSopenharmony_ci	/*thread1 block on futex1 first,then thread2 block on futex2*/
113f08c3bdfSopenharmony_ci	SAFE_PTHREAD_CREATE(&th1, NULL, wait_thread1, NULL);
114f08c3bdfSopenharmony_ci	SAFE_PTHREAD_CREATE(&th2, NULL, wait_thread2, NULL);
115f08c3bdfSopenharmony_ci
116f08c3bdfSopenharmony_ci	while (wait_for_threads(2))
117f08c3bdfSopenharmony_ci		usleep(1000);
118f08c3bdfSopenharmony_ci
119f08c3bdfSopenharmony_ci	futex_wake(tv->fntype, futex2, 1, 0);
120f08c3bdfSopenharmony_ci	SAFE_PTHREAD_JOIN(th2, NULL);
121f08c3bdfSopenharmony_ci	futex_wake(tv->fntype, futex1, 1, 0);
122f08c3bdfSopenharmony_ci	SAFE_PTHREAD_JOIN(th1, NULL);
123f08c3bdfSopenharmony_ci
124f08c3bdfSopenharmony_ci	SAFE_MUNMAP(addr, hpsz);
125f08c3bdfSopenharmony_ci}
126f08c3bdfSopenharmony_ci
127f08c3bdfSopenharmony_cistatic struct tst_test test = {
128f08c3bdfSopenharmony_ci	.setup = setup,
129f08c3bdfSopenharmony_ci	.test_all = wakeup_thread2,
130f08c3bdfSopenharmony_ci	.test_variants = ARRAY_SIZE(variants),
131f08c3bdfSopenharmony_ci	.needs_root = 1,
132f08c3bdfSopenharmony_ci	.needs_tmpdir = 1,
133f08c3bdfSopenharmony_ci	.hugepages = {1, TST_NEEDS},
134f08c3bdfSopenharmony_ci};
135