2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
13 * Copyright (c) 2012 by Delphix. All rights reserved.
27 #include <sys/types.h>
30 #include <sys/socket.h>
31 #include <sys/devpoll.h>
36 * This file implements some simple tests to verify the behavior of the
37 * poll system call and the DP_POLL ioctl on /dev/poll.
41 * Several customers recently ran into an issue where threads in grizzly
42 * (java http server implementation) would randomly wake up from a java
43 * call to select against a java.nio.channels.Selector with no events ready
44 * but well before the specified timeout expired. The
45 * java.nio.channels.Selector select logic is implemented via /dev/poll.
46 * The selector opens /dev/poll, writes the file descriptors it wants to
47 * select on to the file descritpor, and then issues a DP_POLL ioctl to
48 * wait for events to be ready.
50 * The DP_POLL ioctl arguments include a relative timeout in milliseconds,
51 * according to man poll.7d the ioctl should block until events are ready,
52 * the timeout expires, or a signal was received. In this case we noticed
53 * that DP_POLL was returning before the timeout expired despite no events
54 * being ready and no signal being delivered.
56 * Using dtrace we discovered that DP_POLL was returning in cases where the
57 * system time was changed and the thread calling DP_POLL was woken up as
58 * a result of the process forking. The DP_POLL logic was in a loop
59 * checking if events were ready and then calling cv_waituntil_sig to
60 * block. cv_waituntil_sig will return -1 if the system time has changed,
61 * causing the DP_POLL to complete prematurely.
63 * Looking at the code it turns out the same problem exists in
64 * the implementation for poll.2 as well.
68 * The fix changes dpioctl and poll_common to use cv_relwaituntil_sig
69 * rather then cv_waituntil_sig. cv_reltimedwait_sig expects a
70 * relative timeout rather then an absolute timeout, so we avoid the
75 * The test verifies that changing the date does not wake up threads
76 * blocked processing a poll request or a DP_POLL ioctl. The test spawns
77 * one thread that changes the date and forks (to force the threads to
78 * wakeup from cv_reltimedwait_sig) every two seconds. The test spawns
79 * a second thread that issues poll / DP_POLL on an fd set that will
80 * never have events ready and verifies that it does not return until
81 * the specified timeout expires.
85 * The maximum amount of skew in seconds allowed between the
86 * expected an actual time that a test takes.
90 static pthread_mutex_t exitLock
= PTHREAD_MUTEX_INITIALIZER
;
91 static pthread_cond_t exitCond
= PTHREAD_COND_INITIALIZER
;
92 static int terminated
= 0;
95 * Set via -d to enable debug logging
100 debug_log(const char *format
, ...)
108 (void) printf("DEBUG: ");
110 va_start(args
, format
);
111 (void) vprintf(format
, args
);
116 test_start(const char *testName
, const char *format
, ...)
120 (void) printf("TEST STARTING %s: ", testName
);
122 va_start(args
, format
);
123 (void) vprintf(format
, args
);
125 (void) fflush(stdout
);
129 test_failed(const char *testName
, const char *format
, ...)
133 (void) printf("TEST FAILED %s: ", testName
);
135 va_start(args
, format
);
136 (void) vprintf(format
, args
);
143 test_passed(const char *testName
)
145 (void) printf("TEST PASS: %s\n", testName
);
146 (void) fflush(stdout
);
150 check_time(time_t elapsed
, time_t expected
)
152 time_t diff
= expected
- elapsed
;
155 * We may take slightly more or less time then expected,
156 * we allow for a small fudge factor if things completed
157 * before we expect them to.
159 return (elapsed
>= expected
|| diff
<= TIME_DRIFT
);
163 poll_wrapper(pollfd_t
*fds
, nfds_t nfds
, int timeout
, time_t *elapsed
)
166 time_t start
= time(NULL
);
168 debug_log("POLL start: (0x%p, %d, %d)\n", fds
, nfds
, timeout
);
170 ret
= poll(fds
, nfds
, timeout
);
172 *elapsed
= time(NULL
) - start
;
174 debug_log("POLL end: (0x%p, %d, %d) returns %d (elapse=%d)\n",
175 fds
, nfds
, timeout
, ret
, (*elapsed
));
181 dppoll(int pollfd
, pollfd_t
*fds
, nfds_t nfds
, int timeout
, time_t *elapsed
)
185 time_t start
= time(NULL
);
189 arg
.dp_timeout
= timeout
;
191 debug_log("DP_POLL start: (0x%p, %d, %d)\n", fds
, nfds
, timeout
);
193 ret
= ioctl(pollfd
, DP_POLL
, &arg
);
195 *elapsed
= time(NULL
) - start
;
197 debug_log("DP_POLL end: (0x%p, %d, %d) returns %d (elapse=%d)\n",
198 fds
, arg
.dp_nfds
, arg
.dp_timeout
, ret
, (*elapsed
));
204 clear_fd(const char *testName
, int pollfd
, int testfd
)
210 fd
.events
= POLLREMOVE
;
213 ret
= write(pollfd
, &fd
, sizeof (pollfd_t
));
215 if (ret
!= sizeof (pollfd_t
)) {
217 test_failed(testName
, "Failed to clear fd %d: %s",
218 testfd
, strerror(ret
));
222 test_failed(testName
, "Failed to clear fd %d: %d", testfd
, ret
);
227 * TEST: poll with no FDs set, verify we wait the appropriate amount of time.
230 poll_no_fd_test(void)
232 const char *testName
= __func__
;
237 test_start(testName
, "poll for %d sec with no fds\n", timeout
);
239 ret
= poll_wrapper(NULL
, 0, timeout
* 1000, &elapsed
);
242 test_failed(testName
, "POLL returns %d (expected 0)\n", ret
);
245 if (!check_time(elapsed
, timeout
)) {
246 test_failed(testName
, "took %d (expected %d)\n",
250 test_passed(testName
);
254 * TEST: POLL with a valid FD set, verify that we wait the appropriate amount
258 poll_with_fds_test(int testfd
)
260 const char *testName
= __func__
;
270 test_start(testName
, "poll for %d sec with fds\n", timeout
);
272 ret
= poll_wrapper(&fd
, 1, timeout
* 1000, &elapsed
);
275 test_failed(testName
, "POLL returns %d (expected 0)\n", ret
);
278 if (!check_time(elapsed
, timeout
)) {
279 test_failed(testName
, "took %d (expected %d)\n",
283 test_passed(testName
);
287 * TEST: DP_POLL with no FDs set, verify we wait the appropriate
291 dev_poll_no_fd_test(int pollfd
)
293 const char *testName
= __func__
;
298 test_start(testName
, "poll for %d sec with no fds\n", timeout
);
300 ret
= dppoll(pollfd
, NULL
, 0, timeout
* 1000, &elapsed
);
303 test_failed(testName
, "DP_POLL returns %d (expected 0)\n", ret
);
306 if (!check_time(elapsed
, timeout
)) {
307 test_failed(testName
, "took %d (expected %d)\n",
311 test_passed(testName
);
315 * TEST: DP_POLL with a valid FD set, verify that we wait
316 * the appropriate amount of time.
319 dev_poll_with_fds_test(int pollfd
, int testfd
)
321 const char *testName
= __func__
;
327 test_start(testName
, "poll for %d sec with fds\n", timeout
);
330 * Clear the FD in case it's already in the cached set
332 clear_fd(testName
, pollfd
, testfd
);
335 * Add the FD with POLLIN
338 fds
[0].events
= POLLIN
;
341 ret
= write(pollfd
, fds
, sizeof (pollfd_t
));
343 if (ret
!= sizeof (pollfd_t
)) {
345 test_failed(testName
, "Failed to set fds: %s",
349 test_failed(testName
, "Failed to set fds: %d", ret
);
352 ret
= dppoll(pollfd
, fds
, 5, timeout
* 1000, &elapsed
);
355 test_failed(testName
, "DP_POLL returns %d (expected 0)\n", ret
);
358 if (!check_time(elapsed
, timeout
)) {
359 test_failed(testName
, "took %d (expected %d)\n",
363 clear_fd(testName
, pollfd
, testfd
);
365 test_passed(testName
);
370 poll_thread(void *data
)
375 pollfd
= open("/dev/poll", O_RDWR
);
378 perror("Failed to open /dev/poll: ");
383 * Create a dummy FD that will never have POLLIN set
385 testfd
= socket(PF_INET
, SOCK_STREAM
, 0);
388 poll_with_fds_test(testfd
);
390 dev_poll_no_fd_test(pollfd
);
391 dev_poll_with_fds_test(pollfd
, testfd
);
393 (void) close(testfd
);
394 (void) close(pollfd
);
401 * This function causes any threads blocked in cv_timedwait_sig_hires
402 * to wakeup, which allows us to test how dpioctl handles spurious
411 * Forking will force all of the threads to be woken up so
412 * they can be moved to a well known state.
417 perror("Fork failed: ");
419 } else if (child
== 0) {
426 result
= waitpid(child
, &status
, 0);
428 if (result
== -1 && errno
!= EINTR
) {
429 (void) printf("Waitpid for %ld failed: %s\n",
430 child
, strerror(errno
));
433 } while (result
!= child
);
436 (void) printf("Child pid %ld failed: %d\n",
444 * This function changes the system time, which has the side
445 * effect of updating timechanged in the kernel.
453 ret
= gettimeofday(&tp
, NULL
);
457 ret
= settimeofday(&tp
, NULL
);
462 * The helper thread runs in a loop changing the time and
463 * forcing wakeups every 2 seconds.
467 helper_thread(void *data
)
470 struct timespec ts
= {2, 0};
472 debug_log("Helper thread started ...\n");
476 (void) pthread_mutex_lock(&exitLock
);
477 (void) pthread_cond_reltimedwait_np(&exitCond
, &exitLock
, &ts
);
479 (void) pthread_mutex_unlock(&exitLock
);
487 debug_log("Time changed and force wakeup issued\n");
490 debug_log("Helper thread exiting ...\n");
499 (void) pthread_mutex_lock(&exitLock
);
501 (void) pthread_cond_broadcast(&exitCond
);
502 (void) pthread_mutex_unlock(&exitLock
);
508 pthread_t pollThread
;
509 pthread_t helperThread
;
512 ret
= pthread_create(&helperThread
, NULL
, helper_thread
, NULL
);
515 (void) printf("Failed to create date thread: %s",
520 ret
= pthread_create(&pollThread
, NULL
, poll_thread
, NULL
);
523 (void) printf("Failed to create poll thread: %s",
528 (void) pthread_join(pollThread
, NULL
);
530 (void) pthread_join(helperThread
, NULL
);
534 main(int argc
, char * const argv
[])
538 while ((c
= getopt(argc
, argv
, "d")) != -1) {
549 * We need to be root to change the system time
551 if (getuid() != 0 && geteuid() != 0) {
552 (void) printf("%s must be run as root\n", argv
[0]);