All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Michael Kerrisk (man-pages)" <mtk.manpages@gmail.com>
To: Thomas Gleixner <tglx@linutronix.de>
Cc: Michael Kerrisk <mtk.manpages@gmail.com>,
	linux-man <linux-man@vger.kernel.org>,
	lkml <linux-kernel@vger.kernel.org>,
	arul.jeniston@gmail.com, "devi R.K" <devi.feb27@gmail.com>,
	Marc Lehmann <debian-reportbug@plan9.de>,
	John Stultz <john.stultz@linaro.org>,
	Andrei Vagin <avagin@gmail.com>,
	Cyrill Gorcunov <gorcunov@gmail.com>
Subject: timer_settime() and ECANCELED
Date: Wed, 1 Apr 2020 11:01:18 +0200	[thread overview]
Message-ID: <CAKgNAkgiZna0yQzkdZQ92CJzjBcxX6eEu1cg24Oeu2pXRcSv8A@mail.gmail.com> (raw)

Hello Thomas, et al,

Following on from our discussion of read() on a timerfd [1], I
happened to remember a Debian bug report [2] that points out that
timer_settime() can fail with the error ECANCELED, which is both
surprising and odd (because despite the error, the timer does get
updated).

The relevant kernel code (I think, from your commit [3]) seems to be
the following in timerfd_setup():

        if (texp != 0) {
                if (flags & TFD_TIMER_ABSTIME)
                        texp = timens_ktime_to_host(clockid, texp);
                if (isalarm(ctx)) {
                        if (flags & TFD_TIMER_ABSTIME)
                                alarm_start(&ctx->t.alarm, texp);
                        else
                                alarm_start_relative(&ctx->t.alarm, texp);
                } else {
                        hrtimer_start(&ctx->t.tmr, texp, htmode);
                }

                if (timerfd_canceled(ctx))
                        return -ECANCELED;
        }

Using a small test program [4] shows the behavior. The program loops,
repeatedly calling timerfd_settime() (with a delay of a few seconds
before each call). In another terminal window, enter the following
command a few times:

    $ sudo date -s "5 seconds"       # Add 5 secs to wall-clock time

I see behavior as follows (the /sudo date -s "5 seconds"/ command was
executed before loop iterations 0, 2, and 4):

[[
$ ./timerfd_settime_ECANCELED
0
Current time is 1585729978 secs, 868510078 nsecs
Timer value is now 0 secs, 0 nsecs
timerfd_settime() succeeded
Timer value is now 9 secs, 999991977 nsecs

1
Current time is 1585729982 secs, 716339545 nsecs
Timer value is now 6 secs, 152167990 nsecs
timerfd_settime() succeeded
Timer value is now 9 secs, 999992940 nsecs

2
Current time is 1585729991 secs, 567377831 nsecs
Timer value is now 1 secs, 148959376 nsecs
timerfd_settime: Operation canceled
Timer value is now 9 secs, 999976294 nsecs

3
Current time is 1585729995 secs, 405385503 nsecs
Timer value is now 6 secs, 161989917 nsecs
timerfd_settime() succeeded
Timer value is now 9 secs, 999993317 nsecs

4
Current time is 1585730004 secs, 225036165 nsecs
Timer value is now 1 secs, 180346909 nsecs
timerfd_settime: Operation canceled
Timer value is now 9 secs, 999984345 nsecs
]]

I note from the above.

(1) If the wall-clock is changed before the first timerfd_settime()
call, the call succeeds. This is of course expected.
(2) If the wall-clock is changed after a timerfd_settime() call, then
the next timerfd_settime() call fails with ECANCELED.
(3) Even if the timerfd_settime() call fails, the timer is still updated(!).

Some questions:
(a) What is the rationale for timerfd_settime() failing with ECANCELED
in this case? (Currently, the manual page says nothing about this.)
(b) It seems at the least surprising, but more likely a bug, that
timerfd_settime() fails with ECANCELED while at the same time
successfully updating the timer value.

Your thoughts?

Thanks,

Michael

[1] https://lore.kernel.org/lkml/3cbd0919-c82a-cb21-c10f-0498433ba5d1@gmail.com/

[2] https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=947091

[3]
commit 99ee5315dac6211e972fa3f23bcc9a0343ff58c4
Author: Thomas Gleixner <tglx@linutronix.de>
Date:   Wed Apr 27 14:16:42 2011 +0200

    timerfd: Allow timers to be cancelled when clock was set

[4]
/* timerfd_settime_ECANCELED.c */
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <inttypes.h>
#include <sys/timerfd.h>

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

int
main(int argc, char *argv[])
{
    struct itimerspec ts, gts;
    struct timespec start;

    int tfd = timerfd_create(CLOCK_REALTIME, 0);
    if (tfd == -1)
        errExit("timerfd_create");

    ts.it_interval.tv_sec = 0;
    ts.it_interval.tv_nsec = 10;

    int flags = TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET;

    for (long j ; ; j++) {

        /* Inject a delay into each loop, by calling getppid()
           many times */

        for (int k = 0; k < 10000000; k++)
            getppid();

        if (j % 1 == 0)
            printf("%ld\n", j);

        /* Display the current wall-clock time */

        if (clock_gettime(CLOCK_REALTIME, &start) == -1)
            errExit("clock_gettime");
        printf("Current time is %ld secs, %ld nsecs\n",
                start.tv_sec, start.tv_nsec);

        /* Before resetting the timer, retrieve its current value
           so that after the timerfd_settime() call, we can see
           whether the the value has changed */

        if (timerfd_gettime(tfd, &gts) == -1)
            perror("timerfd_gettime");
        printf("Timer value is now %ld secs, %ld nsecs\n",
            gts.it_value.tv_sec, gts.it_value.tv_nsec);

        /* Reset the timer to now + 10 secs */

        ts.it_value.tv_sec = start.tv_sec + 10;
        ts.it_value.tv_nsec = start.tv_nsec;
        if (timerfd_settime(tfd, flags, &ts, NULL) == -1)
            perror("timerfd_settime");
        else
            printf("timerfd_settime() succeeded\n");

        /* Display the timer value once again */

        if (timerfd_gettime(tfd, &gts) == -1)
            perror("timerfd_gettime");
        printf("Timer value is now %ld secs, %ld nsecs\n",
            gts.it_value.tv_sec, gts.it_value.tv_nsec);

        printf("\n");
    }
}

             reply	other threads:[~2020-04-01  9:01 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-04-01  9:01 Michael Kerrisk (man-pages) [this message]
2020-04-01 11:16 ` timer_settime() and ECANCELED Cyrill Gorcunov
2020-04-01 17:42 ` Thomas Gleixner
2020-04-02  5:34   ` Michael Kerrisk (man-pages)
2020-04-02  8:49     ` Thomas Gleixner
2020-04-02 13:16       ` Michael Kerrisk (man-pages)
2020-04-02 13:35         ` Thomas Gleixner
2020-04-02 19:48           ` Michael Kerrisk (man-pages)
2020-04-02 20:12             ` Thomas Gleixner

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=CAKgNAkgiZna0yQzkdZQ92CJzjBcxX6eEu1cg24Oeu2pXRcSv8A@mail.gmail.com \
    --to=mtk.manpages@gmail.com \
    --cc=arul.jeniston@gmail.com \
    --cc=avagin@gmail.com \
    --cc=debian-reportbug@plan9.de \
    --cc=devi.feb27@gmail.com \
    --cc=gorcunov@gmail.com \
    --cc=john.stultz@linaro.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-man@vger.kernel.org \
    --cc=tglx@linutronix.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.