timerfd read only gets single byte?

From: Michael Kerrisk
Date: Tue Jul 17 2007 - 04:11:39 EST


Hi Davide,

While writing a test program to incorporate into the timerfd.2 man page, I
think I've found a bug. It looks like only the least significant byte of
ticks is being returned from read(2), even though I am providing a 4 byte
buffer.

The test program takes 3 command line arguments:

1) seconds for the initial expiration
2) seconds for the timer interval
3) number of timer expirations to catch before terminating

I tried running this program and suspending it for a few minutes, to see if
I could get a large overrun value. When I do this on 2.6.22-rc4 (the built
kernel I have to hand), I see the following:

============
$ ./timerfd_demo 1 1 500
0.000: timer started
1.005: read: 1; total=1
2.005: read: 1; total=2
3.005: read: 1; total=3
4.005: read: 1; total=4
5.006: read: 1; total=5
^Z
[1]+ Stopped ./timerfd_demo 1 1 500
$ date
Tue Jul 17 09:18:11 CEST 2007
$ date
Tue Jul 17 09:23:40 CEST 2007
$ fg
./timerfd_demo 1 1 500
339.769: read: 78; total=83
340.004: read: 1; total=84
341.004: read: 1; total=85
^C
==============

The after bringing the program back into the foreground, I would have
expected to get an overrun count of 334 or thereabouts, but it looks as
though I'm only getting the least significant byte from read(2).

Cheers,

Michael


/* Link with -lrt */

#define _GNU_SOURCE
#include <sys/syscall.h>
#include <unistd.h>
#include <time.h>
#if defined(__i386__)
#define __NR_timerfd 322
#endif

static int
timerfd(int ufd, int clockid, int flags, struct itimerspec *utmr) {
return syscall(__NR_timerfd, ufd, clockid, flags, utmr);
}

#define TFD_TIMER_ABSTIME (1 << 0)

////////////////////////////////////////////////////////////

// #include <sys/timerfd.h>
#include <time.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h> /* Definition of uint32_t */

#define die(msg) do { perror(msg); exit(EXIT_FAILURE); } while (0)

static void
print_elapsed_time(void)
{
static struct timespec start;
struct timespec curr;
static int first_call = 1;
int secs, nsecs;

if (first_call) {
first_call = 0;
if (clock_gettime(CLOCK_MONOTONIC, &start) == -1)
die("clock_gettime");
}

if (clock_gettime(CLOCK_MONOTONIC, &curr) == -1)
die("clock_gettime");

secs = curr.tv_sec - start.tv_sec;
nsecs = curr.tv_nsec - start.tv_nsec;
if (nsecs < 0) {
secs--;
nsecs += 1000000000;
}
printf("%d.%03d: ", secs, (nsecs + 500000) / 1000000);
}

int
main(int argc, char *argv[])
{
struct itimerspec utmr;
int max_exp, tot_exp, tfd;
struct timespec now;
uint32_t exp;
ssize_t s;

if ((argc != 2) && (argc != 4)) {
fprintf(stderr, "%s init-secs [interval-secs max-exp]\n",
argv[0]);
exit(EXIT_FAILURE);
}

if (clock_gettime(CLOCK_REALTIME, &now) == -1)
die("clock_gettime");

/* Create a CLOCK_REALTIME absolute timer with initial
expiration and interval as specified in command line */

utmr.it_value.tv_sec = now.tv_sec + atoi(argv[1]);
utmr.it_value.tv_nsec = now.tv_nsec;
if (argc == 2) {
utmr.it_interval.tv_sec = 0;
max_exp = 1;
} else {
utmr.it_interval.tv_sec = atoi(argv[2]);
max_exp = atoi(argv[3]);
}
utmr.it_interval.tv_nsec = 0;

tfd = timerfd(-1, CLOCK_REALTIME, TFD_TIMER_ABSTIME, &utmr);
if (tfd == -1)
die("timerfd");

print_elapsed_time();
printf("timer started\n");

exp = 0; // ????? Without this initialization, the results from
// read() are strange; it appears that read() is only
// returning one byte of tick information, not four.
for (tot_exp = 0; tot_exp < max_exp;) {
s = read(tfd, &exp, sizeof(uint32_t));
if (s != sizeof(uint32_t))
die("read");

tot_exp += exp;
print_elapsed_time();
printf("read: %u; total=%d\n", exp, tot_exp);
}

exit(EXIT_SUCCESS);
}

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/