aboutsummaryrefslogtreecommitdiff
path: root/testcases/kernel/syscalls/futex/futex_wake04.c
blob: 176dd4aebdf6b8b81a6e5b8538cbe2848b078826 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Copyright (C) 2015  Yi Zhang <wetpzy@gmail.com>
 *                     Li Wang <liwang@redhat.com>
 *
 * DESCRIPTION:
 *
 *   It is a regression test for commit:
 *   http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/
 *   commit/?id=13d60f4
 *
 *   The implementation of futex doesn't produce unique keys for futexes
 *   in shared huge pages, so threads waiting on different futexes may
 *   end up on the same wait list. This results in incorrect threads being
 *   woken by FUTEX_WAKE.
 *
 *   Needs to be run as root unless there are already enough huge pages available.
 *   In the fail case, which happens in the CentOS-6.6 kernel (2.6.32-504.8.1),
 *   the tests hangs until it times out after a 30-second wait.
 *
 */

#include <stdio.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <sys/time.h>
#include <string.h>

#include "futextest.h"
#include "futex_utils.h"
#include "lapi/mmap.h"
#include "tst_safe_stdio.h"
#include "tst_safe_pthread.h"

static futex_t *futex1, *futex2;

static struct tst_ts to;

static struct futex_test_variants variants[] = {
#if (__NR_futex != __LTP__NR_INVALID_SYSCALL)
	{ .fntype = FUTEX_FN_FUTEX, .tstype = TST_KERN_OLD_TIMESPEC, .desc = "syscall with old kernel spec"},
#endif

#if (__NR_futex_time64 != __LTP__NR_INVALID_SYSCALL)
	{ .fntype = FUTEX_FN_FUTEX64, .tstype = TST_KERN_TIMESPEC, .desc = "syscall time64 with kernel spec"},
#endif
};

static void setup(void)
{
	struct futex_test_variants *tv = &variants[tst_variant];

	tst_res(TINFO, "Testing variant: %s", tv->desc);
	futex_supported_by_kernel(tv->fntype);

	to = tst_ts_from_ns(tv->tstype, 30 * NSEC_PER_SEC);
}

static void *wait_thread1(void *arg LTP_ATTRIBUTE_UNUSED)
{
	struct futex_test_variants *tv = &variants[tst_variant];

	futex_wait(tv->fntype, futex1, *futex1, &to, 0);

	return NULL;
}

static void *wait_thread2(void *arg LTP_ATTRIBUTE_UNUSED)
{
	struct futex_test_variants *tv = &variants[tst_variant];
	int res;

	errno = 0;
	res = futex_wait(tv->fntype, futex2, *futex2, &to, 0);
	if (!res)
		tst_res(TPASS, "Hi hydra, thread2 awake!");
	else
		tst_res(TFAIL | TERRNO, "Bug: wait_thread2 did not wake after 30 secs.");

	return NULL;
}

static void wakeup_thread2(void)
{
	struct futex_test_variants *tv = &variants[tst_variant];
	void *addr;
	int hpsz, pgsz;
	pthread_t th1, th2;

	hpsz = tst_get_hugepage_size();
	tst_res(TINFO, "Hugepagesize %i", hpsz);

	/*allocate some shared memory*/
	addr = mmap(NULL, hpsz, PROT_WRITE | PROT_READ,
	            MAP_SHARED | MAP_ANONYMOUS | MAP_HUGETLB, -1, 0);

	if (addr == MAP_FAILED) {
		if (errno == ENOMEM)
			tst_brk(TCONF, "Cannot allocate hugepage, memory too fragmented?");

		tst_brk(TBROK | TERRNO, "Cannot allocate hugepage");
	}

	pgsz = getpagesize();

	/*apply the first subpage to futex1*/
	futex1 = addr;
	*futex1 = 0;
	/*apply the second subpage to futex2*/
	futex2 = (futex_t *)((char *)addr + pgsz);
	*futex2 = 0;

	/*thread1 block on futex1 first,then thread2 block on futex2*/
	SAFE_PTHREAD_CREATE(&th1, NULL, wait_thread1, NULL);
	SAFE_PTHREAD_CREATE(&th2, NULL, wait_thread2, NULL);

	while (wait_for_threads(2))
		usleep(1000);

	futex_wake(tv->fntype, futex2, 1, 0);
	SAFE_PTHREAD_JOIN(th2, NULL);
	futex_wake(tv->fntype, futex1, 1, 0);
	SAFE_PTHREAD_JOIN(th1, NULL);

	SAFE_MUNMAP(addr, hpsz);
}

static struct tst_test test = {
	.setup = setup,
	.test_all = wakeup_thread2,
	.test_variants = ARRAY_SIZE(variants),
	.needs_root = 1,
	.needs_tmpdir = 1,
	.hugepages = {1, TST_NEEDS},
};