xref: /minix/minix/tests/test76.c (revision 7f5f010b)
1 /* Tests for interrupting VFS calls - by D.C. van Moolenbroek */
2 /* This test needs to be run as root; otherwise, openpty() won't work. */
3 #include <stdio.h>
4 #include <sys/time.h>
5 #include <sys/wait.h>
6 #include <sys/socket.h>
7 #include <sys/utsname.h>
8 #include <sys/syslimits.h>
9 #include <netinet/in.h>
10 #include <signal.h>
11 #include <unistd.h>
12 #include <fcntl.h>
13 #include <errno.h>
14 #include <util.h>
15 
16 #define ITERATIONS 1
17 
18 #include "common.h"
19 
20 /*
21  * This signal handler does nothing.  It just needs to be triggered, so that
22  * PM will tell VFS to unpause this process.
23  */
24 static void dummy_handler(int sig)
25 {
26 	/* Nothing. */
27 }
28 
29 /*
30  * Interrupt a select(2) call.
31  */
32 static void
33 test76a(void)
34 {
35 	struct sigaction act, oact;
36 	struct itimerval it;
37 	struct sockaddr_in sin;
38 	struct timeval tv;
39 	fd_set set;
40 	int tfd[2], pfd[2], sfd, maxfd;
41 
42 	subtest = 1;
43 
44 	act.sa_handler = dummy_handler;
45 	sigfillset(&act.sa_mask);
46 	act.sa_flags = 0;
47 	if (sigaction(SIGALRM, &act, &oact) < 0) e(1);
48 
49 	memset(&it, 0, sizeof(it));
50 	it.it_value.tv_sec = 0;
51 	it.it_value.tv_usec = 10000;
52 	if (setitimer(ITIMER_REAL, &it, NULL) < 0) e(2);
53 
54 	/* First try without any file descriptors. */
55 	tv.tv_sec = 1;
56 	tv.tv_usec = 0;
57 	if (select(0, NULL, NULL, NULL, &tv) >= 0) e(3);
58 	if (errno != EINTR) e(4);
59 
60 	/* Then try with different types of file descriptors, all blocking. */
61 	if (openpty(&tfd[0], &tfd[1], NULL, NULL, NULL) < 0) e(5);
62 
63 	FD_ZERO(&set);
64 	FD_SET(tfd[0], &set); /* reading from the PTY master should block */
65 	maxfd = tfd[0];
66 
67 	if (pipe(pfd) < 0) e(6);
68 	FD_SET(pfd[0], &set); /* reading from an empty pipe should block */
69 	if (maxfd < pfd[0]) maxfd = pfd[0];
70 
71 	if ((sfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) e(7);
72 
73 	memset(&sin, 0, sizeof(sin));
74 	sin.sin_family = AF_INET;
75 	/* Binding to an arbitrary port is fine. */
76 	if (bind(sfd, (struct sockaddr *)&sin, sizeof(sin)) < 0) e(8);
77 
78 	if (listen(sfd, 1) < 0) e(9);
79 
80 	FD_SET(sfd, &set); /* reading from a listening socket should block */
81 	if (maxfd < sfd) maxfd = sfd;
82 
83 	memset(&it, 0, sizeof(it));
84 	it.it_value.tv_sec = 0;
85 	it.it_value.tv_usec = 100000;
86 	if (setitimer(ITIMER_REAL, &it, NULL) < 0) e(10);
87 
88 	tv.tv_sec = 1;
89 	tv.tv_usec = 0;
90 	if (select(maxfd + 1, &set, NULL, NULL, &tv) >= 0) e(11);
91 	if (errno != EINTR) e(12);
92 
93 	if (close(tfd[0]) < 0) e(13);
94 	if (close(tfd[1]) < 0) e(14);
95 	if (close(pfd[0]) < 0) e(15);
96 	if (close(pfd[1]) < 0) e(16);
97 	if (close(sfd) < 0) e(17);
98 
99 	if (sigaction(SIGUSR1, &oact, NULL) < 0) e(18);
100 }
101 
102 /*
103  * Interrupt reads and writes to a pipe.  POSIX states that if the operation
104  * was partially successful, the number of bytes written so far should be
105  * returned; otherwise, the we should get the normal EINTR.
106  */
107 static void
108 test76b(void)
109 {
110 	struct sigaction act, oact;
111 	struct itimerval it;
112 	char *buf;
113 	int pfd[2];
114 
115 	subtest = 2;
116 
117 	if ((buf = malloc(PIPE_BUF * 2)) == NULL) e(1);
118 
119 	if (pipe(pfd) < 0) e(2);
120 
121 	act.sa_handler = dummy_handler;
122 	sigfillset(&act.sa_mask);
123 	act.sa_flags = 0;
124 	if (sigaction(SIGALRM, &act, &oact) < 0) e(3);
125 
126 	memset(&it, 0, sizeof(it));
127 	it.it_value.tv_sec = 0;
128 	it.it_value.tv_usec = 10000;
129 	if (setitimer(ITIMER_REAL, &it, NULL) < 0) e(4);
130 
131 	/*
132 	 * This write is too large for the pipe, so it will block until the
133 	 * signal arrives.  When being interrupted, it should return the pipe
134 	 * size, as that is the part that has been filled successfully so far.
135 	 */
136 	if (write(pfd[1], buf, PIPE_BUF * 2) != PIPE_BUF) e(5);
137 
138 	/*
139 	 * Since the write partially succeeded, we should be able to read all
140 	 * we wrote so far, without blocking.
141 	 */
142 	if (read(pfd[0], buf, PIPE_BUF) != PIPE_BUF) e(6);
143 
144 	/* We should now be able to fill the pipe up to its full size again. */
145 	if (write(pfd[1], buf, PIPE_BUF) != PIPE_BUF) e(7);
146 
147 	memset(&it, 0, sizeof(it));
148 	it.it_value.tv_sec = 0;
149 	it.it_value.tv_usec = 10000;
150 	if (setitimer(ITIMER_REAL, &it, NULL) < 0) e(8);
151 
152 	/* Now interrupt a write attempt on a full pipe. */
153 	if (write(pfd[1], buf, 1) >= 0) e(9);
154 	if (errno != EINTR) e(10);
155 
156 	/* Empty the pipe again. */
157 	if (read(pfd[0], buf, PIPE_BUF) != PIPE_BUF) e(11);
158 
159 	memset(&it, 0, sizeof(it));
160 	it.it_value.tv_sec = 0;
161 	it.it_value.tv_usec = 10000;
162 	if (setitimer(ITIMER_REAL, &it, NULL) < 0) e(12);
163 
164 	/* Now interrupt a read on an empty pipe. */
165 	if (read(pfd[0], buf, PIPE_BUF) >= 0) e(13);
166 	if (errno != EINTR) e(14);
167 
168 	if (close(pfd[0]) < 0) e(15);
169 	if (close(pfd[1]) < 0) e(16);
170 
171 	if (sigaction(SIGUSR1, &oact, NULL) < 0) e(17);
172 
173 	free(buf);
174 }
175 
176 /*
177  * Interrupt an ioctl(2) call.  We use an alarm to interrupt an accept(3) call
178  * on a TCP socket - the accept procedure is (currently) implemented using
179  * ioctl(2) calls.
180  */
181 static void
182 test76c(void)
183 {
184 	struct sigaction act, oact;
185 	struct itimerval it;
186 	struct sockaddr_in sin;
187 	socklen_t len;
188 	int sfd;
189 
190 	subtest = 3;
191 
192 	if ((sfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) e(1);
193 
194 	memset(&sin, 0, sizeof(sin));
195 	sin.sin_family = AF_INET;
196 	/* Binding to an arbitrary port is fine. */
197 	if (bind(sfd, (struct sockaddr *)&sin, sizeof(sin)) < 0) e(2);
198 
199 	if (listen(sfd, 1) < 0) e(3);
200 
201 	act.sa_handler = dummy_handler;
202 	sigfillset(&act.sa_mask);
203 	act.sa_flags = 0;
204 	if (sigaction(SIGALRM, &act, &oact) < 0) e(4);
205 
206 	memset(&it, 0, sizeof(it));
207 	it.it_value.tv_sec = 0;
208 	it.it_value.tv_usec = 10000;
209 	if (setitimer(ITIMER_REAL, &it, NULL) < 0) e(5);
210 
211 	/* This will block until the timer fires. */
212 	len = sizeof(sin);
213 	if (accept(sfd, (struct sockaddr *)&sin, &len) >= 0) e(6);
214 	if (errno != EINTR) e(7);
215 
216 	if (close(sfd) < 0) e(8);
217 
218 	if (sigaction(SIGUSR1, &oact, NULL) < 0) e(9);
219 }
220 
221 /*
222  * Try to trigger semi-concurrent processing of normal system calls and
223  * postponed PM requests for a single process within VFS.
224  */
225 static void
226 test76d(void)
227 {
228 	struct utsname name;
229 	struct sigaction act, oact;
230 	struct itimerval it;
231 	int r, fd, pfd[2], count, status;
232 	time_t stime, etime, runtime = 30 /*seconds*/;
233 	char buf[3], *pbuf;
234 
235 	subtest = 4;
236 
237 	/* This test would kill wimpy platforms such as ARM. */
238 	if (uname(&name) < 0) e(1);
239 	if (!strcmp(name.machine, "arm")) return;
240 
241 	act.sa_handler = dummy_handler;
242 	sigfillset(&act.sa_mask);
243 	act.sa_flags = 0;
244 	if (sigaction(SIGALRM, &act, &oact) < 0) e(2);
245 
246 	if (pipe(pfd) < 0) e(3);
247 
248 	/* Pre-fill the pipe. */
249 	if ((pbuf = malloc(PIPE_BUF - 1)) == NULL) e(4);
250 
251 	if (write(pfd[1], pbuf, PIPE_BUF - 1) != PIPE_BUF - 1) e(5);
252 
253 	free(pbuf);
254 
255 	switch (fork()) {
256 	case 0:
257 		if (close(pfd[1]) < 0) e(6);
258 
259 		/* Read from the pipe, but more slowly than the writer. */
260 		while ((r = read(pfd[0], buf, 2)) != 0)
261 			if (r < 0) e(7);
262 
263 		exit(0);
264 	case -1:
265 		e(8);
266 	default:
267 		break;
268 	}
269 
270 	switch (fork()) {
271 	case 0:
272 		if (close(pfd[0]) < 0) e(9);
273 
274 		time(&stime);
275 
276 		/* Start an alarm mayhem. */
277 		it.it_value.tv_sec = 0;
278 		it.it_value.tv_usec = 1;
279 		it.it_interval.tv_sec = 0;
280 		it.it_interval.tv_usec = 1;
281 		if (setitimer(ITIMER_REAL, &it, NULL) < 0) e(10);
282 
283 		/*
284 		 * Then start writing to the pipe, in such a way that the
285 		 * write operation will be suspended in every so many cases.
286 		 */
287 		do {
288 			if (write(pfd[1], buf, 3) < 0 && errno != EINTR)
289 				e(11);
290 
291 			time(&etime);
292 		} while ((int)(etime - stime) < runtime);
293 
294 		exit(0);
295 	case -1:
296 		e(12);
297 	default:
298 		break;
299 	}
300 
301 	if (close(pfd[0]) < 0) e(13);
302 	if (close(pfd[1]) < 0) e(14);
303 
304 	/*
305 	 * First give the two processes a while to run regularly.  Then start
306 	 * creating additional noise to keep the VFS worker threads busy.
307 	 */
308 	runtime /= 2;
309 
310 	sleep(runtime);
311 
312 	/*
313 	 * As of writing, VFS has less than 20 worker threads. Create more
314 	 * processes than that.
315 	 */
316 	for (count = 2; count < 20; count++) {
317 		switch (fork()) {
318 		case 0:
319 			time(&stime);
320 
321 			do {
322 				/*
323 				 * Opening a character device blocks the
324 				 * calling thread, hopefully causing work to be
325 				 * queued.  Sadly, in practice, the high
326 				 * priorities of system processes prevent this
327 				 * case from occurring frequently.  It works
328 				 * better with a driver that has a priority
329 				 * below that of of user processes.
330 				 */
331 				if ((fd = open("/dev/null", O_WRONLY)) < 0)
332 					e(15);
333 
334 				close(fd);
335 
336 				time(&etime);
337 			} while ((int)(etime - stime) < runtime);
338 
339 			exit(0);
340 		case -1:
341 			e(16);
342 		default:
343 			break;
344 		}
345 	}
346 
347 	/* Wait for all children to shut down. */
348 	while (count-- > 0) {
349 		if (wait(&status) <= 0) e(17);
350 		if (!WIFEXITED(status)) e(18);
351 		if (WEXITSTATUS(status) != 0) e(19);
352 	}
353 
354 	if (sigaction(SIGUSR1, &oact, NULL) < 0) e(20);
355 }
356 
357 /*
358  * Try to get a nonblocking select(2) call to be interrupted by a signal.
359  * In the future, VFS should prevent this from happening at all; for now, we
360  * just want to make sure it does not result in disaster when it does happen.
361  */
362 static void
363 test76e(void)
364 {
365 	struct utsname name;
366 	struct sigaction act, oact;
367 	struct itimerval it;
368 	struct timeval tv;
369 	fd_set set;
370 	int tfd[2], left;
371 
372 	subtest = 5;
373 
374 	/* This test would kill wimpy platforms such as ARM. */
375 	if (uname(&name) < 0) e(1);
376 	if (!strcmp(name.machine, "arm")) return;
377 
378 	if (openpty(&tfd[0], &tfd[1], NULL, NULL, NULL) < 0) e(2);
379 
380 	act.sa_handler = dummy_handler;
381 	sigfillset(&act.sa_mask);
382 	act.sa_flags = 0;
383 	if (sigaction(SIGALRM, &act, &oact) < 0) e(3);
384 
385 	/*
386 	 * Start an alarm mayhem.  We have to try to get a signal in between
387 	 * VFS sending a select request to TTY, and TTY replying to VFS with
388 	 * initial results.
389 	 */
390 	it.it_value.tv_sec = 0;
391 	it.it_value.tv_usec = 1;
392 	it.it_interval.tv_sec = 0;
393 	it.it_interval.tv_usec = 1;
394 	if (setitimer(ITIMER_REAL, &it, NULL) < 0) e(4);
395 
396 	/*
397 	 * Now issue nonblocking selects until we get interrupted, or until
398 	 * we have gone through a hardcoded maximum of attempts.
399 	 */
400 	left = 100000;
401 	do {
402 		if (--left == 0) break;
403 
404 		FD_ZERO(&set);
405 		FD_SET(tfd[0], &set); /* reading from master should block */
406 
407 		tv.tv_sec = 0;
408 		tv.tv_usec = 0;
409 	} while (select(2, &set, NULL, NULL, &tv) >= 0);
410 
411 	if (left > 0 && errno != EINTR) e(5);
412 
413 	it.it_value.tv_sec = 0;
414 	it.it_value.tv_usec = 0;
415 	if (setitimer(ITIMER_REAL, &it, NULL) < 0) e(6);
416 
417 	/* The call failed, so the set must be unmodified. */
418 	if (left > 0 && !FD_SET(tfd[0], &set)) e(7);
419 
420 	if (close(tfd[0]) < 0) e(8);
421 	if (close(tfd[1]) < 0) e(9);
422 
423 	if (sigaction(SIGUSR1, &oact, NULL) < 0) e(10);
424 }
425 
426 int
427 main(int argc, char **argv)
428 {
429 	int i, m;
430 
431 	start(76);
432 
433 	if (argc == 2)
434 		m = atoi(argv[1]);
435 	else
436 		m = 0xFF;
437 
438 	for (i = 0; i < ITERATIONS; i++) {
439 		if (m & 0x01) test76a();
440 		if (m & 0x02) test76b();
441 		if (m & 0x04) test76c();
442 		if (m & 0x08) test76d();
443 		if (m & 0x10) test76e();
444 	}
445 
446 	quit();
447 }
448