Davide, Andrew, I applied Davide's v3 patchset (sent into LKML on 25 Nov) against 2.4.24-rc3, and did various tests (all on x86). Several tests were done using the program at the foot of this mail. Various others were done by cobbling together bits of code that I haven't included here. In covering the tests I ran, and as a kind of proof of concept, I'll treat the draft man page as the test spec. I think I've found two bugs (look for the string "BUG" below), one of which is a significant deviation from expected behavior. See the comments below. Cheers, MichaelVerified. I've tried creating various combinations of CLOCK_REALTIME and CLOCK_MONOTONIC timers, both one shot (it_interval is zero) and repeating (it_interval is non-zero), and everything works as I would expect. (Verified: see the ERRORS section of the man page, below.) Disarming a timer works fine in my tests. Verified: setting both fields of new_value.it_value to zero disarms the timer. I've tested intervals down to 1 nanosec, which seem to seems to work as I would expect. (But see the BUG reported below.) Verified. Tested: after setting a CLOCK_REALTIME timer with the TFD_TIMER_ABSTIME flag to expire at some time in the past with a non-zero interval (e.g., setting 100 seconds in the past, with a 5 second interval), read() from the file descriptor returns the correct number of expirations (e.g., 20). This seems a reasonable thing to do, I suppose. However, while playing around to test this, I found what looks like a bug (see below). BUG 1: However, this test exposed what looks like a bug: if I set a CLOCK_REALTIME clock to expire in the past, with a very small interval, then the maximum number of expirations that can be returned via read seems to be limited to 32 bits, even though we have a 64-bit value for returning this information. I haven't checked the kernel source to determine where this bug is. To demonstrate the bug use the program appended to this mail, as follows: # The following starts a CLOCK_REALTIME repeating timer with # an interval of 1 microsecs (1000 nanosecs), with an initial # expiration of just under 2^32 nanoseconds in the past. # The '-a' flags says use TFD_TIMER_ABSTIME for timer_settime() calls. # The 'r' command reads from the timerfd file descriptor. $ ./timerfd_test -a -- -4290 0 0 1000 Initial setting for settime: value=1197543860.000, interval=0.000 ./timerfd_test> r <-- type this immediately Read: 4292190752 <-- nearly 2^32 expirations, as expected $ ./timerfd_test -a -- -4290 0 0 1000 Initial setting for settime: value=1197543900.000, interval=0.000 ./timerfd_test> r <-- type this after 5 secs Read: 2992244 <-- Looks like the counter rolled over See the bug described under timerfd_gettime(). BUG 2: The last sentence does not match the implementation. (Nor is it consistent with the behavior of POSIX timers. And I *think* things did work correctly in the original timerfd() implementation, but I have not gone back to check.) Suppose that we set an absolute timer to expire 100 seconds in the future. Then according to this sentence of the man page then each subsequent call to timerfd_gettime() should retrun an itimerspec structure whose it_value steadily decreases from 100 to 0 (when the timer expires). (This is the behavior in the analogous situation with POSIX timers and with setitimer()/getitimer().) However, the implementation of timerfd_gettime() always returns the "time when the timer would next expire", and this value depends on whether TFD_TIMER_ABSTIME was specified when setting the timer. Examples: $ ./timerfd_test -a -- 100 # -a means TFD_TIMER_ABSTIME Initial setting for settime: value=1197550329.140, interval=0.000 ./timerfd_test> g <-- Calls timerfd_gettime() (elapsed time= 1) Current value: value=1197550329.140, interval=0.000 $ ./timerfd_test -- 100 Initial setting for settime: value=100.295, interval=0.000 ./timerfd_test> g (elapsed time= 2) Current value: value=18208.440, interval=0.000 In both of the above examples, the "value" retrieved by timer_gettime() should be a number <= 100. BUG 2a: The bug described for timerfd_gettime() also applies for the 'curr_value' returned by a call to timerfd_settime(). Verified: the above was true in all my tests. Verified. Verified: each call to timerfd_settime() resets the "expiration count" to 0, even if a previous setting of the timer had already expired. Verified. Verified. Verified. Verified for both poll() and select(). Not verified. (No easy way to do that from userspace.) Verified. Reads from a dup(2)ed file descriptor, or a file descriptor duplicated via a fork(2) can be used to read expiration information. Verified. A timerfd file descriptor is preserved across an exceve(), and continues to generate timer expirations that can be read(2). Verified. (And obvious.) Verified. (And obvious.) (CLOCK_MONOTONIC is 1, CLOCK_REALTIME is 0) Tested: clockid == 2, clockid == -1; timerfd_create() fails with EINVAL, as expected. Currently, 'flags' must be zero. Tested: flags == 1; timerfd_create() fails with EINVAL, as expected. Not tested. Not tested. Not tested. Not tested. Tested for timerfd_gettime() and timerfd_settime(). An invalid file descriptor (i.e., a file descriptor that is not open) yields the error EBADF, as expected. Tested for timerfd_gettime() and timerfd_settime(). Passing a file descriptor that refers to an object other than a timerfd fails with the error EINVAL, as expected. Tested for timerfd_settime(). Specifying it_value.tv_nsec == 1000000000 or it_interval.tv_nsec == 1000000000 yields the error EINVAL, as expected. --- END MAN PAGE TEXT --- /* timerfd_test.c */ /* Link with -lrt */ #define _GNU_SOURCE #include <sys/syscall.h> #include <unistd.h> #include <time.h> #if defined(__i386__) #define __NR_timerfd_create 322 #define __NR_timerfd_settime 325 #define __NR_timerfd_gettime 326 #endif static int timerfd_create(int clockid, int flags) { return syscall(__NR_timerfd_create, clockid, flags); } static int timerfd_settime(int fd, int flags, struct itimerspec *new_value, struct itimerspec *curr_value) { return syscall(__NR_timerfd_settime, fd, flags, new_value, curr_value); } static int timerfd_gettime(int fd, struct itimerspec *curr_value) { return syscall(__NR_timerfd_gettime, fd, curr_value); } #define TFD_TIMER_ABSTIME (1 << 0) #define handle_error(msg) \ do { perror(msg); exit(EXIT_FAILURE); } while (0) // #include <sys/timerfd.h> #include <time.h> #include <sys/times.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <stdint.h> /* Definition of uint64_t */ static void usage(const char *pname, const char *msg) { if (msg != NULL) printf("%s", msg); fprintf(stderr, "Usage: %s [options] value-sec [value-nsec " "[intvl-sec [intvl-nsec]]]\n", pname); fprintf(stderr, "Options are:\n"); fprintf(stderr, "\t-a Use absolute timer\n"); fprintf(stderr, "\t-m Use CLOCK_MONOTONIC " "(instead of CLOCK_REALTIME)\n"); exit(EXIT_FAILURE); } /* usage */ static void display_help(void) { printf("n val-sec val-nsec intvl-sec intvl-nsec\n" " Reset timer value & interval\n"); printf("g Get timer value\n"); printf("r Read from file descriptor\n"); printf("t Print elapsed time\n"); } /* display_help */ #define MAX_LINE 1024 static void print_itimerspec(struct itimerspec *its) { printf("value=%ld.%03ld, interval=%ld.%03ld", (long) its->it_value.tv_sec, (long) its->it_value.tv_nsec / 1000000, (long) its->it_interval.tv_sec, (long) its->it_interval.tv_nsec / 1000000); } /* print_itimerspec */ int main(int argc, char *argv[]) { struct itimerspec new_value, curr_value; int fd, flags; struct timespec now; int s, opt, use_abs_timer, use_monotonic; long arg1, arg2, arg3, arg4; uint64_t exp; time_t start; char line[MAX_LINE]; int num_read, clockid; char cmd; use_abs_timer = 0; use_monotonic = 0; while ((opt = getopt(argc, argv, "am")) != -1) { switch (opt) { case 'a': use_abs_timer = 1; break; case 'm': use_monotonic = 1; break; default: usage(argv[1], NULL); break; } /* switch */ } /* while */ clockid = (use_monotonic ? CLOCK_MONOTONIC : CLOCK_REALTIME); if (optind + 1 > argc) usage(argv[0], NULL); if (clock_gettime(clockid, &now) == -1) handle_error("clock_gettime"); flags = use_abs_timer ? TFD_TIMER_ABSTIME : 0; /* Create a timer with initial expiration and interval as specified in command line */ if (use_abs_timer) { new_value.it_value.tv_sec = now.tv_sec + atoi(argv[optind]); new_value.it_value.tv_nsec = (argc > optind + 1) ? atol(argv[optind + 1]) : now.tv_nsec; } else { new_value.it_value.tv_sec = atoi(argv[optind]); new_value.it_value.tv_nsec = (argc > optind + 1) ? atol(argv[optind + 1]) : now.tv_nsec; } new_value.it_interval.tv_sec = (argc > optind + 2) ? atol(argv[optind + 2]) : 0; new_value.it_interval.tv_nsec = (argc > optind + 3) ? atol(argv[optind + 3]) : 0; fd = timerfd_create(clockid, 0); if (fd == -1) handle_error("timerfd_create"); printf("Initial setting for settime: "); print_itimerspec(&new_value); printf("\n"); s = timerfd_settime(fd, flags, &new_value, &curr_value); if (s == -1) handle_error("timerfd_settime"); start = time(NULL); for ( ; ; ) { printf("%s> ", argv[0]); fflush(stdout); if (fgets(line, MAX_LINE, stdin) == NULL) /* EOF */ exit(EXIT_SUCCESS); line[strlen(line) - 1] = '\0'; /* Remove trailing '\n' */ if (*line == '\0') continue; /* Skip blank lines */ if (line[0] == '?') display_help(); num_read = sscanf(line, " %c %ld %ld %ld %ld", &cmd, &arg1, &arg2, &arg3, &arg4); switch (cmd) { case 'n': if (num_read != 5) { printf("Wrong number of arguments\n"); continue; } if (use_abs_timer) { printf("This is an absolute timer\n"); if (clock_gettime(clockid, &now) == -1) handle_error("clock_gettime"); printf("Now: "); printf("value=%ld.%03ld", (long) now.tv_sec, (long) now.tv_nsec / 1000000); printf("\n"); new_value.it_value.tv_sec = now.tv_sec + arg1; new_value.it_value.tv_nsec = now.tv_nsec + arg2; } else { printf("This is a relative timer\n"); new_value.it_value.tv_sec = arg1; new_value.it_value.tv_nsec = arg2; } new_value.it_interval.tv_sec = arg3; new_value.it_interval.tv_nsec = arg4; printf("New setting for settime: "); print_itimerspec(&new_value); printf("\n"); s = timerfd_settime(fd, flags, &new_value, &curr_value); if (s == -1) { perror("timerfd_settime"); break; } printf("Previous setting from settime: "); print_itimerspec(&curr_value); printf("\n"); break; case 't': printf("%ld\n", (long) (time(NULL) - start)); break; case 'g': s = timerfd_gettime(fd, &curr_value); if (s == -1) handle_error("timerfd_gettime"); printf("(elapsed time=%3ld)\n", (long) (time(NULL) - start)); printf("Current value: "); print_itimerspec(&curr_value); printf("\n"); break; case 'r': s = read(fd, &exp, sizeof(uint64_t)); if (s != sizeof(uint64_t)) handle_error("read"); printf("Read: %lld\n", exp); break; } /* switch */ } /* for */ exit(EXIT_SUCCESS); } --
| Andrew Morton | -mm merge plans for 2.6.23 |
| Greg KH | [GIT PATCH] driver core patches against 2.6.24 |
| Greg KH | Re: Dual-Licensing Linux Kernel with GPL V2 and GPL V3 |
| Tomasz Kłoczko | Is it time for remove (crap) ALSA from kernel tree ? |
git: | |
| David Miller | Re: [PATCH] pkt_sched: Destroy gen estimators under rtnl_lock(). |
| David Miller | [GIT]: Networking |
| Paweł Staszewski | iproute2 action/policer question |
| Gerrit Renker | [PATCH 27/37] dccp: Integration of dynamic feature activation - part 2 (server side) |
