提交 99ee5315 编写于 作者: T Thomas Gleixner

timerfd: Allow timers to be cancelled when clock was set

Some applications must be aware of clock realtime being set
backward. A simple example is a clock applet which arms a timer for
the next minute display. If clock realtime is set backward then the
applet displays a stale time for the amount of time which the clock
was set backwards. Due to that applications poll the time because we
don't have an interface.

Extend the timerfd interface by adding a flag which puts the timer
onto a different internal realtime clock. All timers on this clock are
expired whenever the clock was set.

The timerfd core records the monotonic offset when the timer is
created. When the timer is armed, then the current offset is compared
to the previous recorded offset. When it has changed, then
timerfd_settime returns -ECANCELED. When a timer is read the offset is
compared and if it changed -ECANCELED returned to user space. Periodic
timers are not rearmed in the cancelation case.
Signed-off-by: NThomas Gleixner <tglx@linutronix.de>
Acked-by: NJohn Stultz <johnstul@us.ibm.com>
Cc: Chris Friesen <chris.friesen@genband.com>
Tested-by: NKay Sievers <kay.sievers@vrfy.org>
Cc: "Kirill A. Shutemov" <kirill@shutemov.name>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Davide Libenzi <davidel@xmailserver.org>
Reviewed-by: NAlexander Shishkin <virtuoso@slind.org>
Link: http://lkml.kernel.org/r/%3Calpine.LFD.2.02.1104271359580.3323%40ionos%3ESigned-off-by: NThomas Gleixner <tglx@linutronix.de>
上级 b12a03ce
...@@ -26,10 +26,12 @@ ...@@ -26,10 +26,12 @@
struct timerfd_ctx { struct timerfd_ctx {
struct hrtimer tmr; struct hrtimer tmr;
ktime_t tintv; ktime_t tintv;
ktime_t moffs;
wait_queue_head_t wqh; wait_queue_head_t wqh;
u64 ticks; u64 ticks;
int expired; int expired;
int clockid; int clockid;
bool might_cancel;
}; };
/* /*
...@@ -59,24 +61,52 @@ static ktime_t timerfd_get_remaining(struct timerfd_ctx *ctx) ...@@ -59,24 +61,52 @@ static ktime_t timerfd_get_remaining(struct timerfd_ctx *ctx)
return remaining.tv64 < 0 ? ktime_set(0, 0): remaining; return remaining.tv64 < 0 ? ktime_set(0, 0): remaining;
} }
static void timerfd_setup(struct timerfd_ctx *ctx, int flags, static bool timerfd_canceled(struct timerfd_ctx *ctx)
{
ktime_t moffs;
if (!ctx->might_cancel)
return false;
moffs = ktime_get_monotonic_offset();
if (moffs.tv64 == ctx->moffs.tv64)
return false;
ctx->moffs = moffs;
return true;
}
static int timerfd_setup(struct timerfd_ctx *ctx, int flags,
const struct itimerspec *ktmr) const struct itimerspec *ktmr)
{ {
enum hrtimer_mode htmode; enum hrtimer_mode htmode;
ktime_t texp; ktime_t texp;
int clockid = ctx->clockid;
htmode = (flags & TFD_TIMER_ABSTIME) ? htmode = (flags & TFD_TIMER_ABSTIME) ?
HRTIMER_MODE_ABS: HRTIMER_MODE_REL; HRTIMER_MODE_ABS: HRTIMER_MODE_REL;
ctx->might_cancel = false;
if (htmode == HRTIMER_MODE_ABS && ctx->clockid == CLOCK_REALTIME &&
(flags & TFD_TIMER_CANCELON_SET)) {
clockid = CLOCK_REALTIME_COS;
ctx->might_cancel = true;
}
texp = timespec_to_ktime(ktmr->it_value); texp = timespec_to_ktime(ktmr->it_value);
ctx->expired = 0; ctx->expired = 0;
ctx->ticks = 0; ctx->ticks = 0;
ctx->tintv = timespec_to_ktime(ktmr->it_interval); ctx->tintv = timespec_to_ktime(ktmr->it_interval);
hrtimer_init(&ctx->tmr, ctx->clockid, htmode); hrtimer_init(&ctx->tmr, clockid, htmode);
hrtimer_set_expires(&ctx->tmr, texp); hrtimer_set_expires(&ctx->tmr, texp);
ctx->tmr.function = timerfd_tmrproc; ctx->tmr.function = timerfd_tmrproc;
if (texp.tv64 != 0) if (texp.tv64 != 0) {
hrtimer_start(&ctx->tmr, texp, htmode); hrtimer_start(&ctx->tmr, texp, htmode);
if (timerfd_canceled(ctx))
return -ECANCELED;
}
return 0;
} }
static int timerfd_release(struct inode *inode, struct file *file) static int timerfd_release(struct inode *inode, struct file *file)
...@@ -118,8 +148,21 @@ static ssize_t timerfd_read(struct file *file, char __user *buf, size_t count, ...@@ -118,8 +148,21 @@ static ssize_t timerfd_read(struct file *file, char __user *buf, size_t count,
res = -EAGAIN; res = -EAGAIN;
else else
res = wait_event_interruptible_locked_irq(ctx->wqh, ctx->ticks); res = wait_event_interruptible_locked_irq(ctx->wqh, ctx->ticks);
if (ctx->ticks) { if (ctx->ticks) {
ticks = ctx->ticks; ticks = ctx->ticks;
/*
* If clock has changed, we do not care about the
* ticks and we do not rearm the timer. Userspace must
* reevaluate anyway.
*/
if (timerfd_canceled(ctx)) {
ticks = 0;
ctx->expired = 0;
res = -ECANCELED;
}
if (ctx->expired && ctx->tintv.tv64) { if (ctx->expired && ctx->tintv.tv64) {
/* /*
* If tintv.tv64 != 0, this is a periodic timer that * If tintv.tv64 != 0, this is a periodic timer that
...@@ -183,6 +226,7 @@ SYSCALL_DEFINE2(timerfd_create, int, clockid, int, flags) ...@@ -183,6 +226,7 @@ SYSCALL_DEFINE2(timerfd_create, int, clockid, int, flags)
init_waitqueue_head(&ctx->wqh); init_waitqueue_head(&ctx->wqh);
ctx->clockid = clockid; ctx->clockid = clockid;
hrtimer_init(&ctx->tmr, clockid, HRTIMER_MODE_ABS); hrtimer_init(&ctx->tmr, clockid, HRTIMER_MODE_ABS);
ctx->moffs = ktime_get_monotonic_offset();
ufd = anon_inode_getfd("[timerfd]", &timerfd_fops, ctx, ufd = anon_inode_getfd("[timerfd]", &timerfd_fops, ctx,
O_RDWR | (flags & TFD_SHARED_FCNTL_FLAGS)); O_RDWR | (flags & TFD_SHARED_FCNTL_FLAGS));
...@@ -199,6 +243,7 @@ SYSCALL_DEFINE4(timerfd_settime, int, ufd, int, flags, ...@@ -199,6 +243,7 @@ SYSCALL_DEFINE4(timerfd_settime, int, ufd, int, flags,
struct file *file; struct file *file;
struct timerfd_ctx *ctx; struct timerfd_ctx *ctx;
struct itimerspec ktmr, kotmr; struct itimerspec ktmr, kotmr;
int ret;
if (copy_from_user(&ktmr, utmr, sizeof(ktmr))) if (copy_from_user(&ktmr, utmr, sizeof(ktmr)))
return -EFAULT; return -EFAULT;
...@@ -240,14 +285,14 @@ SYSCALL_DEFINE4(timerfd_settime, int, ufd, int, flags, ...@@ -240,14 +285,14 @@ SYSCALL_DEFINE4(timerfd_settime, int, ufd, int, flags,
/* /*
* Re-program the timer to the new value ... * Re-program the timer to the new value ...
*/ */
timerfd_setup(ctx, flags, &ktmr); ret = timerfd_setup(ctx, flags, &ktmr);
spin_unlock_irq(&ctx->wqh.lock); spin_unlock_irq(&ctx->wqh.lock);
fput(file); fput(file);
if (otmr && copy_to_user(otmr, &kotmr, sizeof(kotmr))) if (otmr && copy_to_user(otmr, &kotmr, sizeof(kotmr)))
return -EFAULT; return -EFAULT;
return 0; return ret;
} }
SYSCALL_DEFINE2(timerfd_gettime, int, ufd, struct itimerspec __user *, otmr) SYSCALL_DEFINE2(timerfd_gettime, int, ufd, struct itimerspec __user *, otmr)
......
...@@ -155,6 +155,7 @@ enum hrtimer_base_type { ...@@ -155,6 +155,7 @@ enum hrtimer_base_type {
HRTIMER_BASE_REALTIME, HRTIMER_BASE_REALTIME,
HRTIMER_BASE_MONOTONIC, HRTIMER_BASE_MONOTONIC,
HRTIMER_BASE_BOOTTIME, HRTIMER_BASE_BOOTTIME,
HRTIMER_BASE_REALTIME_COS,
HRTIMER_MAX_CLOCK_BASES, HRTIMER_MAX_CLOCK_BASES,
}; };
...@@ -310,6 +311,7 @@ extern void hrtimers_resume(void); ...@@ -310,6 +311,7 @@ extern void hrtimers_resume(void);
extern ktime_t ktime_get(void); extern ktime_t ktime_get(void);
extern ktime_t ktime_get_real(void); extern ktime_t ktime_get_real(void);
extern ktime_t ktime_get_boottime(void); extern ktime_t ktime_get_boottime(void);
extern ktime_t ktime_get_monotonic_offset(void);
DECLARE_PER_CPU(struct tick_device, tick_cpu_device); DECLARE_PER_CPU(struct tick_device, tick_cpu_device);
......
...@@ -302,6 +302,12 @@ struct itimerval { ...@@ -302,6 +302,12 @@ struct itimerval {
* The IDs of various hardware clocks: * The IDs of various hardware clocks:
*/ */
#define CLOCK_SGI_CYCLE 10 #define CLOCK_SGI_CYCLE 10
#ifdef __KERNEL__
/* This clock is not exposed to user space */
#define CLOCK_REALTIME_COS 15
#endif
#define MAX_CLOCKS 16 #define MAX_CLOCKS 16
#define CLOCKS_MASK (CLOCK_REALTIME | CLOCK_MONOTONIC) #define CLOCKS_MASK (CLOCK_REALTIME | CLOCK_MONOTONIC)
#define CLOCKS_MONO CLOCK_MONOTONIC #define CLOCKS_MONO CLOCK_MONOTONIC
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
* shared O_* flags. * shared O_* flags.
*/ */
#define TFD_TIMER_ABSTIME (1 << 0) #define TFD_TIMER_ABSTIME (1 << 0)
#define TFD_TIMER_CANCELON_SET (1 << 1)
#define TFD_CLOEXEC O_CLOEXEC #define TFD_CLOEXEC O_CLOEXEC
#define TFD_NONBLOCK O_NONBLOCK #define TFD_NONBLOCK O_NONBLOCK
...@@ -26,6 +27,6 @@ ...@@ -26,6 +27,6 @@
/* Flags for timerfd_create. */ /* Flags for timerfd_create. */
#define TFD_CREATE_FLAGS TFD_SHARED_FCNTL_FLAGS #define TFD_CREATE_FLAGS TFD_SHARED_FCNTL_FLAGS
/* Flags for timerfd_settime. */ /* Flags for timerfd_settime. */
#define TFD_SETTIME_FLAGS TFD_TIMER_ABSTIME #define TFD_SETTIME_FLAGS (TFD_TIMER_ABSTIME | TFD_TIMER_CANCELON_SET)
#endif /* _LINUX_TIMERFD_H */ #endif /* _LINUX_TIMERFD_H */
...@@ -78,6 +78,11 @@ DEFINE_PER_CPU(struct hrtimer_cpu_base, hrtimer_bases) = ...@@ -78,6 +78,11 @@ DEFINE_PER_CPU(struct hrtimer_cpu_base, hrtimer_bases) =
.get_time = &ktime_get_boottime, .get_time = &ktime_get_boottime,
.resolution = KTIME_LOW_RES, .resolution = KTIME_LOW_RES,
}, },
{
.index = CLOCK_REALTIME_COS,
.get_time = &ktime_get_real,
.resolution = KTIME_LOW_RES,
},
} }
}; };
...@@ -85,6 +90,7 @@ static const int hrtimer_clock_to_base_table[MAX_CLOCKS] = { ...@@ -85,6 +90,7 @@ static const int hrtimer_clock_to_base_table[MAX_CLOCKS] = {
[CLOCK_REALTIME] = HRTIMER_BASE_REALTIME, [CLOCK_REALTIME] = HRTIMER_BASE_REALTIME,
[CLOCK_MONOTONIC] = HRTIMER_BASE_MONOTONIC, [CLOCK_MONOTONIC] = HRTIMER_BASE_MONOTONIC,
[CLOCK_BOOTTIME] = HRTIMER_BASE_BOOTTIME, [CLOCK_BOOTTIME] = HRTIMER_BASE_BOOTTIME,
[CLOCK_REALTIME_COS] = HRTIMER_BASE_REALTIME_COS,
}; };
static inline int hrtimer_clockid_to_base(clockid_t clock_id) static inline int hrtimer_clockid_to_base(clockid_t clock_id)
...@@ -110,6 +116,7 @@ static void hrtimer_get_softirq_time(struct hrtimer_cpu_base *base) ...@@ -110,6 +116,7 @@ static void hrtimer_get_softirq_time(struct hrtimer_cpu_base *base)
base->clock_base[HRTIMER_BASE_REALTIME].softirq_time = xtim; base->clock_base[HRTIMER_BASE_REALTIME].softirq_time = xtim;
base->clock_base[HRTIMER_BASE_MONOTONIC].softirq_time = mono; base->clock_base[HRTIMER_BASE_MONOTONIC].softirq_time = mono;
base->clock_base[HRTIMER_BASE_BOOTTIME].softirq_time = boot; base->clock_base[HRTIMER_BASE_BOOTTIME].softirq_time = boot;
base->clock_base[HRTIMER_BASE_REALTIME_COS].softirq_time = xtim;
} }
/* /*
...@@ -479,6 +486,8 @@ static inline void debug_deactivate(struct hrtimer *timer) ...@@ -479,6 +486,8 @@ static inline void debug_deactivate(struct hrtimer *timer)
trace_hrtimer_cancel(timer); trace_hrtimer_cancel(timer);
} }
static void hrtimer_expire_cancelable(struct hrtimer_cpu_base *cpu_base);
/* High resolution timer related functions */ /* High resolution timer related functions */
#ifdef CONFIG_HIGH_RES_TIMERS #ifdef CONFIG_HIGH_RES_TIMERS
...@@ -715,9 +724,14 @@ static void retrigger_next_event(void *arg) ...@@ -715,9 +724,14 @@ static void retrigger_next_event(void *arg)
struct hrtimer_cpu_base *base = &__get_cpu_var(hrtimer_bases); struct hrtimer_cpu_base *base = &__get_cpu_var(hrtimer_bases);
struct timespec realtime_offset, xtim, wtm, sleep; struct timespec realtime_offset, xtim, wtm, sleep;
if (!hrtimer_hres_active()) if (!hrtimer_hres_active()) {
raw_spin_lock(&base->lock);
hrtimer_expire_cancelable(base);
raw_spin_unlock(&base->lock);
return; return;
}
/* Optimized out for !HIGH_RES */
get_xtime_and_monotonic_and_sleep_offset(&xtim, &wtm, &sleep); get_xtime_and_monotonic_and_sleep_offset(&xtim, &wtm, &sleep);
set_normalized_timespec(&realtime_offset, -wtm.tv_sec, -wtm.tv_nsec); set_normalized_timespec(&realtime_offset, -wtm.tv_sec, -wtm.tv_nsec);
...@@ -727,6 +741,10 @@ static void retrigger_next_event(void *arg) ...@@ -727,6 +741,10 @@ static void retrigger_next_event(void *arg)
timespec_to_ktime(realtime_offset); timespec_to_ktime(realtime_offset);
base->clock_base[HRTIMER_BASE_BOOTTIME].offset = base->clock_base[HRTIMER_BASE_BOOTTIME].offset =
timespec_to_ktime(sleep); timespec_to_ktime(sleep);
base->clock_base[HRTIMER_BASE_REALTIME_COS].offset =
timespec_to_ktime(realtime_offset);
hrtimer_expire_cancelable(base);
hrtimer_force_reprogram(base, 0); hrtimer_force_reprogram(base, 0);
raw_spin_unlock(&base->lock); raw_spin_unlock(&base->lock);
...@@ -1222,6 +1240,22 @@ static void __run_hrtimer(struct hrtimer *timer, ktime_t *now) ...@@ -1222,6 +1240,22 @@ static void __run_hrtimer(struct hrtimer *timer, ktime_t *now)
timer->state &= ~HRTIMER_STATE_CALLBACK; timer->state &= ~HRTIMER_STATE_CALLBACK;
} }
static void hrtimer_expire_cancelable(struct hrtimer_cpu_base *cpu_base)
{
struct timerqueue_node *node;
struct hrtimer_clock_base *base;
ktime_t now = ktime_get_real();
base = &cpu_base->clock_base[HRTIMER_BASE_REALTIME_COS];
while ((node = timerqueue_getnext(&base->active))) {
struct hrtimer *timer;
timer = container_of(node, struct hrtimer, node);
__run_hrtimer(timer, &now);
}
}
#ifdef CONFIG_HIGH_RES_TIMERS #ifdef CONFIG_HIGH_RES_TIMERS
/* /*
......
...@@ -1098,6 +1098,21 @@ void get_xtime_and_monotonic_and_sleep_offset(struct timespec *xtim, ...@@ -1098,6 +1098,21 @@ void get_xtime_and_monotonic_and_sleep_offset(struct timespec *xtim,
} while (read_seqretry(&xtime_lock, seq)); } while (read_seqretry(&xtime_lock, seq));
} }
/**
* ktime_get_monotonic_offset() - get wall_to_monotonic in ktime_t format
*/
ktime_t ktime_get_monotonic_offset(void)
{
unsigned long seq;
struct timespec wtom;
do {
seq = read_seqbegin(&xtime_lock);
wtom = wall_to_monotonic;
} while (read_seqretry(&xtime_lock, seq));
return timespec_to_ktime(wtom);
}
/** /**
* xtime_update() - advances the timekeeping infrastructure * xtime_update() - advances the timekeeping infrastructure
* @ticks: number of ticks, that have elapsed since the last call. * @ticks: number of ticks, that have elapsed since the last call.
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册