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