1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright Collabora Ltd., 2021
4  *
5  * futex cmp requeue test by André Almeida <andrealmeid@collabora.com>
6  */
7 
8 #include <pthread.h>
9 #include <sys/shm.h>
10 #include <sys/mman.h>
11 #include <fcntl.h>
12 #include "logging.h"
13 #include "futextest.h"
14 
15 #define TEST_NAME "futex-wait"
16 #define timeout_ns  30000000
17 #define WAKE_WAIT_US 10000
18 #define SHM_PATH "futex_shm_file"
19 
20 void *futex;
21 
22 void usage(char *prog)
23 {
24 	printf("Usage: %s\n", prog);
25 	printf("  -c	Use color\n");
26 	printf("  -h	Display this help message\n");
27 	printf("  -v L	Verbosity level: %d=QUIET %d=CRITICAL %d=INFO\n",
28 	       VQUIET, VCRITICAL, VINFO);
29 }
30 
31 static void *waiterfn(void *arg)
32 {
33 	struct timespec to;
34 	unsigned int flags = 0;
35 
36 	if (arg)
37 		flags = *((unsigned int *) arg);
38 
39 	to.tv_sec = 0;
40 	to.tv_nsec = timeout_ns;
41 
42 	if (futex_wait(futex, 0, &to, flags))
43 		printf("waiter failed errno %d\n", errno);
44 
45 	return NULL;
46 }
47 
48 int main(int argc, char *argv[])
49 {
50 	int res, ret = RET_PASS, fd, c, shm_id;
51 	u_int32_t f_private = 0, *shared_data;
52 	unsigned int flags = FUTEX_PRIVATE_FLAG;
53 	pthread_t waiter;
54 	void *shm;
55 
56 	futex = &f_private;
57 
58 	while ((c = getopt(argc, argv, "cht:v:")) != -1) {
59 		switch (c) {
60 		case 'c':
61 			log_color(1);
62 			break;
63 		case 'h':
64 			usage(basename(argv[0]));
65 			exit(0);
66 		case 'v':
67 			log_verbosity(atoi(optarg));
68 			break;
69 		default:
70 			usage(basename(argv[0]));
71 			exit(1);
72 		}
73 	}
74 
75 	ksft_print_header();
76 	ksft_set_plan(3);
77 	ksft_print_msg("%s: Test futex_wait\n", basename(argv[0]));
78 
79 	/* Testing a private futex */
80 	info("Calling private futex_wait on futex: %p\n", futex);
81 	if (pthread_create(&waiter, NULL, waiterfn, (void *) &flags))
82 		error("pthread_create failed\n", errno);
83 
84 	usleep(WAKE_WAIT_US);
85 
86 	info("Calling private futex_wake on futex: %p\n", futex);
87 	res = futex_wake(futex, 1, FUTEX_PRIVATE_FLAG);
88 	if (res != 1) {
89 		ksft_test_result_fail("futex_wake private returned: %d %s\n",
90 				      errno, strerror(errno));
91 		ret = RET_FAIL;
92 	} else {
93 		ksft_test_result_pass("futex_wake private succeeds\n");
94 	}
95 
96 	/* Testing an anon page shared memory */
97 	shm_id = shmget(IPC_PRIVATE, 4096, IPC_CREAT | 0666);
98 	if (shm_id < 0) {
99 		perror("shmget");
100 		exit(1);
101 	}
102 
103 	shared_data = shmat(shm_id, NULL, 0);
104 
105 	*shared_data = 0;
106 	futex = shared_data;
107 
108 	info("Calling shared (page anon) futex_wait on futex: %p\n", futex);
109 	if (pthread_create(&waiter, NULL, waiterfn, NULL))
110 		error("pthread_create failed\n", errno);
111 
112 	usleep(WAKE_WAIT_US);
113 
114 	info("Calling shared (page anon) futex_wake on futex: %p\n", futex);
115 	res = futex_wake(futex, 1, 0);
116 	if (res != 1) {
117 		ksft_test_result_fail("futex_wake shared (page anon) returned: %d %s\n",
118 				      errno, strerror(errno));
119 		ret = RET_FAIL;
120 	} else {
121 		ksft_test_result_pass("futex_wake shared (page anon) succeeds\n");
122 	}
123 
124 
125 	/* Testing a file backed shared memory */
126 	fd = open(SHM_PATH, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
127 	if (fd < 0) {
128 		perror("open");
129 		exit(1);
130 	}
131 
132 	if (ftruncate(fd, sizeof(f_private))) {
133 		perror("ftruncate");
134 		exit(1);
135 	}
136 
137 	shm = mmap(NULL, sizeof(f_private), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
138 	if (shm == MAP_FAILED) {
139 		perror("mmap");
140 		exit(1);
141 	}
142 
143 	memcpy(shm, &f_private, sizeof(f_private));
144 
145 	futex = shm;
146 
147 	info("Calling shared (file backed) futex_wait on futex: %p\n", futex);
148 	if (pthread_create(&waiter, NULL, waiterfn, NULL))
149 		error("pthread_create failed\n", errno);
150 
151 	usleep(WAKE_WAIT_US);
152 
153 	info("Calling shared (file backed) futex_wake on futex: %p\n", futex);
154 	res = futex_wake(shm, 1, 0);
155 	if (res != 1) {
156 		ksft_test_result_fail("futex_wake shared (file backed) returned: %d %s\n",
157 				      errno, strerror(errno));
158 		ret = RET_FAIL;
159 	} else {
160 		ksft_test_result_pass("futex_wake shared (file backed) succeeds\n");
161 	}
162 
163 	/* Freeing resources */
164 	shmdt(shared_data);
165 	munmap(shm, sizeof(f_private));
166 	remove(SHM_PATH);
167 	close(fd);
168 
169 	ksft_print_cnts();
170 	return ret;
171 }
172