aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Gleixner <tglx@linutronix.de>2011-04-27 08:16:42 -0400
committerThomas Gleixner <tglx@linutronix.de>2011-05-02 15:39:15 -0400
commit99ee5315dac6211e972fa3f23bcc9a0343ff58c4 (patch)
tree6663d6ceaabcb9bac03193e2781cdbe6a139f70c
parentb12a03ce4880bd13786a98db6de494a3e0123129 (diff)
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: Thomas Gleixner <tglx@linutronix.de> Acked-by: John Stultz <johnstul@us.ibm.com> Cc: Chris Friesen <chris.friesen@genband.com> Tested-by: Kay 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: Alexander Shishkin <virtuoso@slind.org> Link: http://lkml.kernel.org/r/%3Calpine.LFD.2.02.1104271359580.3323%40ionos%3E Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
-rw-r--r--fs/timerfd.c57
-rw-r--r--include/linux/hrtimer.h2
-rw-r--r--include/linux/time.h6
-rw-r--r--include/linux/timerfd.h3
-rw-r--r--kernel/hrtimer.c36
-rw-r--r--kernel/time/timekeeping.c15
6 files changed, 111 insertions, 8 deletions
diff --git a/fs/timerfd.c b/fs/timerfd.c
index 8c4fc1425b3e..7e14c9e7c4ee 100644
--- a/fs/timerfd.c
+++ b/fs/timerfd.c
@@ -26,10 +26,12 @@
26struct timerfd_ctx { 26struct timerfd_ctx {
27 struct hrtimer tmr; 27 struct hrtimer tmr;
28 ktime_t tintv; 28 ktime_t tintv;
29 ktime_t moffs;
29 wait_queue_head_t wqh; 30 wait_queue_head_t wqh;
30 u64 ticks; 31 u64 ticks;
31 int expired; 32 int expired;
32 int clockid; 33 int clockid;
34 bool might_cancel;
33}; 35};
34 36
35/* 37/*
@@ -59,24 +61,52 @@ static ktime_t timerfd_get_remaining(struct timerfd_ctx *ctx)
59 return remaining.tv64 < 0 ? ktime_set(0, 0): remaining; 61 return remaining.tv64 < 0 ? ktime_set(0, 0): remaining;
60} 62}
61 63
62static void timerfd_setup(struct timerfd_ctx *ctx, int flags, 64static bool timerfd_canceled(struct timerfd_ctx *ctx)
63 const struct itimerspec *ktmr) 65{
66 ktime_t moffs;
67
68 if (!ctx->might_cancel)
69 return false;
70
71 moffs = ktime_get_monotonic_offset();
72
73 if (moffs.tv64 == ctx->moffs.tv64)
74 return false;
75
76 ctx->moffs = moffs;
77 return true;
78}
79
80static int timerfd_setup(struct timerfd_ctx *ctx, int flags,
81 const struct itimerspec *ktmr)
64{ 82{
65 enum hrtimer_mode htmode; 83 enum hrtimer_mode htmode;
66 ktime_t texp; 84 ktime_t texp;
85 int clockid = ctx->clockid;
67 86
68 htmode = (flags & TFD_TIMER_ABSTIME) ? 87 htmode = (flags & TFD_TIMER_ABSTIME) ?
69 HRTIMER_MODE_ABS: HRTIMER_MODE_REL; 88 HRTIMER_MODE_ABS: HRTIMER_MODE_REL;
70 89
90 ctx->might_cancel = false;
91 if (htmode == HRTIMER_MODE_ABS && ctx->clockid == CLOCK_REALTIME &&
92 (flags & TFD_TIMER_CANCELON_SET)) {
93 clockid = CLOCK_REALTIME_COS;
94 ctx->might_cancel = true;
95 }
96
71 texp = timespec_to_ktime(ktmr->it_value); 97 texp = timespec_to_ktime(ktmr->it_value);
72 ctx->expired = 0; 98 ctx->expired = 0;
73 ctx->ticks = 0; 99 ctx->ticks = 0;
74 ctx->tintv = timespec_to_ktime(ktmr->it_interval); 100 ctx->tintv = timespec_to_ktime(ktmr->it_interval);
75 hrtimer_init(&ctx->tmr, ctx->clockid, htmode); 101 hrtimer_init(&ctx->tmr, clockid, htmode);
76 hrtimer_set_expires(&ctx->tmr, texp); 102 hrtimer_set_expires(&ctx->tmr, texp);
77 ctx->tmr.function = timerfd_tmrproc; 103 ctx->tmr.function = timerfd_tmrproc;
78 if (texp.tv64 != 0) 104 if (texp.tv64 != 0) {
79 hrtimer_start(&ctx->tmr, texp, htmode); 105 hrtimer_start(&ctx->tmr, texp, htmode);
106 if (timerfd_canceled(ctx))
107 return -ECANCELED;
108 }
109 return 0;
80} 110}
81 111
82static int timerfd_release(struct inode *inode, struct file *file) 112static 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 res = -EAGAIN; 148 res = -EAGAIN;
119 else 149 else
120 res = wait_event_interruptible_locked_irq(ctx->wqh, ctx->ticks); 150 res = wait_event_interruptible_locked_irq(ctx->wqh, ctx->ticks);
151
121 if (ctx->ticks) { 152 if (ctx->ticks) {
122 ticks = ctx->ticks; 153 ticks = ctx->ticks;
154
155 /*
156 * If clock has changed, we do not care about the
157 * ticks and we do not rearm the timer. Userspace must
158 * reevaluate anyway.
159 */
160 if (timerfd_canceled(ctx)) {
161 ticks = 0;
162 ctx->expired = 0;
163 res = -ECANCELED;
164 }
165
123 if (ctx->expired && ctx->tintv.tv64) { 166 if (ctx->expired && ctx->tintv.tv64) {
124 /* 167 /*
125 * If tintv.tv64 != 0, this is a periodic timer that 168 * If tintv.tv64 != 0, this is a periodic timer that
@@ -183,6 +226,7 @@ SYSCALL_DEFINE2(timerfd_create, int, clockid, int, flags)
183 init_waitqueue_head(&ctx->wqh); 226 init_waitqueue_head(&ctx->wqh);
184 ctx->clockid = clockid; 227 ctx->clockid = clockid;
185 hrtimer_init(&ctx->tmr, clockid, HRTIMER_MODE_ABS); 228 hrtimer_init(&ctx->tmr, clockid, HRTIMER_MODE_ABS);
229 ctx->moffs = ktime_get_monotonic_offset();
186 230
187 ufd = anon_inode_getfd("[timerfd]", &timerfd_fops, ctx, 231 ufd = anon_inode_getfd("[timerfd]", &timerfd_fops, ctx,
188 O_RDWR | (flags & TFD_SHARED_FCNTL_FLAGS)); 232 O_RDWR | (flags & TFD_SHARED_FCNTL_FLAGS));
@@ -199,6 +243,7 @@ SYSCALL_DEFINE4(timerfd_settime, int, ufd, int, flags,
199 struct file *file; 243 struct file *file;
200 struct timerfd_ctx *ctx; 244 struct timerfd_ctx *ctx;
201 struct itimerspec ktmr, kotmr; 245 struct itimerspec ktmr, kotmr;
246 int ret;
202 247
203 if (copy_from_user(&ktmr, utmr, sizeof(ktmr))) 248 if (copy_from_user(&ktmr, utmr, sizeof(ktmr)))
204 return -EFAULT; 249 return -EFAULT;
@@ -240,14 +285,14 @@ SYSCALL_DEFINE4(timerfd_settime, int, ufd, int, flags,
240 /* 285 /*
241 * Re-program the timer to the new value ... 286 * Re-program the timer to the new value ...
242 */ 287 */
243 timerfd_setup(ctx, flags, &ktmr); 288 ret = timerfd_setup(ctx, flags, &ktmr);
244 289
245 spin_unlock_irq(&ctx->wqh.lock); 290 spin_unlock_irq(&ctx->wqh.lock);
246 fput(file); 291 fput(file);
247 if (otmr && copy_to_user(otmr, &kotmr, sizeof(kotmr))) 292 if (otmr && copy_to_user(otmr, &kotmr, sizeof(kotmr)))
248 return -EFAULT; 293 return -EFAULT;
249 294
250 return 0; 295 return ret;
251} 296}
252 297
253SYSCALL_DEFINE2(timerfd_gettime, int, ufd, struct itimerspec __user *, otmr) 298SYSCALL_DEFINE2(timerfd_gettime, int, ufd, struct itimerspec __user *, otmr)
diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h
index 4135c88fe4fa..eda4ccde0730 100644
--- a/include/linux/hrtimer.h
+++ b/include/linux/hrtimer.h
@@ -155,6 +155,7 @@ enum hrtimer_base_type {
155 HRTIMER_BASE_REALTIME, 155 HRTIMER_BASE_REALTIME,
156 HRTIMER_BASE_MONOTONIC, 156 HRTIMER_BASE_MONOTONIC,
157 HRTIMER_BASE_BOOTTIME, 157 HRTIMER_BASE_BOOTTIME,
158 HRTIMER_BASE_REALTIME_COS,
158 HRTIMER_MAX_CLOCK_BASES, 159 HRTIMER_MAX_CLOCK_BASES,
159}; 160};
160 161
@@ -310,6 +311,7 @@ extern void hrtimers_resume(void);
310extern ktime_t ktime_get(void); 311extern ktime_t ktime_get(void);
311extern ktime_t ktime_get_real(void); 312extern ktime_t ktime_get_real(void);
312extern ktime_t ktime_get_boottime(void); 313extern ktime_t ktime_get_boottime(void);
314extern ktime_t ktime_get_monotonic_offset(void);
313 315
314DECLARE_PER_CPU(struct tick_device, tick_cpu_device); 316DECLARE_PER_CPU(struct tick_device, tick_cpu_device);
315 317
diff --git a/include/linux/time.h b/include/linux/time.h
index b3061782dec3..a9242773eb24 100644
--- a/include/linux/time.h
+++ b/include/linux/time.h
@@ -302,6 +302,12 @@ struct itimerval {
302 * The IDs of various hardware clocks: 302 * The IDs of various hardware clocks:
303 */ 303 */
304#define CLOCK_SGI_CYCLE 10 304#define CLOCK_SGI_CYCLE 10
305
306#ifdef __KERNEL__
307/* This clock is not exposed to user space */
308#define CLOCK_REALTIME_COS 15
309#endif
310
305#define MAX_CLOCKS 16 311#define MAX_CLOCKS 16
306#define CLOCKS_MASK (CLOCK_REALTIME | CLOCK_MONOTONIC) 312#define CLOCKS_MASK (CLOCK_REALTIME | CLOCK_MONOTONIC)
307#define CLOCKS_MONO CLOCK_MONOTONIC 313#define CLOCKS_MONO CLOCK_MONOTONIC
diff --git a/include/linux/timerfd.h b/include/linux/timerfd.h
index 2d0792983f8c..e9571fc8f1a0 100644
--- a/include/linux/timerfd.h
+++ b/include/linux/timerfd.h
@@ -19,6 +19,7 @@
19 * shared O_* flags. 19 * shared O_* flags.
20 */ 20 */
21#define TFD_TIMER_ABSTIME (1 << 0) 21#define TFD_TIMER_ABSTIME (1 << 0)
22#define TFD_TIMER_CANCELON_SET (1 << 1)
22#define TFD_CLOEXEC O_CLOEXEC 23#define TFD_CLOEXEC O_CLOEXEC
23#define TFD_NONBLOCK O_NONBLOCK 24#define TFD_NONBLOCK O_NONBLOCK
24 25
@@ -26,6 +27,6 @@
26/* Flags for timerfd_create. */ 27/* Flags for timerfd_create. */
27#define TFD_CREATE_FLAGS TFD_SHARED_FCNTL_FLAGS 28#define TFD_CREATE_FLAGS TFD_SHARED_FCNTL_FLAGS
28/* Flags for timerfd_settime. */ 29/* Flags for timerfd_settime. */
29#define TFD_SETTIME_FLAGS TFD_TIMER_ABSTIME 30#define TFD_SETTIME_FLAGS (TFD_TIMER_ABSTIME | TFD_TIMER_CANCELON_SET)
30 31
31#endif /* _LINUX_TIMERFD_H */ 32#endif /* _LINUX_TIMERFD_H */
diff --git a/kernel/hrtimer.c b/kernel/hrtimer.c
index c145ed643bca..eabcbd781433 100644
--- a/kernel/hrtimer.c
+++ b/kernel/hrtimer.c
@@ -78,6 +78,11 @@ DEFINE_PER_CPU(struct hrtimer_cpu_base, hrtimer_bases) =
78 .get_time = &ktime_get_boottime, 78 .get_time = &ktime_get_boottime,
79 .resolution = KTIME_LOW_RES, 79 .resolution = KTIME_LOW_RES,
80 }, 80 },
81 {
82 .index = CLOCK_REALTIME_COS,
83 .get_time = &ktime_get_real,
84 .resolution = KTIME_LOW_RES,
85 },
81 } 86 }
82}; 87};
83 88
@@ -85,6 +90,7 @@ static const int hrtimer_clock_to_base_table[MAX_CLOCKS] = {
85 [CLOCK_REALTIME] = HRTIMER_BASE_REALTIME, 90 [CLOCK_REALTIME] = HRTIMER_BASE_REALTIME,
86 [CLOCK_MONOTONIC] = HRTIMER_BASE_MONOTONIC, 91 [CLOCK_MONOTONIC] = HRTIMER_BASE_MONOTONIC,
87 [CLOCK_BOOTTIME] = HRTIMER_BASE_BOOTTIME, 92 [CLOCK_BOOTTIME] = HRTIMER_BASE_BOOTTIME,
93 [CLOCK_REALTIME_COS] = HRTIMER_BASE_REALTIME_COS,
88}; 94};
89 95
90static inline int hrtimer_clockid_to_base(clockid_t clock_id) 96static 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 base->clock_base[HRTIMER_BASE_REALTIME].softirq_time = xtim; 116 base->clock_base[HRTIMER_BASE_REALTIME].softirq_time = xtim;
111 base->clock_base[HRTIMER_BASE_MONOTONIC].softirq_time = mono; 117 base->clock_base[HRTIMER_BASE_MONOTONIC].softirq_time = mono;
112 base->clock_base[HRTIMER_BASE_BOOTTIME].softirq_time = boot; 118 base->clock_base[HRTIMER_BASE_BOOTTIME].softirq_time = boot;
119 base->clock_base[HRTIMER_BASE_REALTIME_COS].softirq_time = xtim;
113} 120}
114 121
115/* 122/*
@@ -479,6 +486,8 @@ static inline void debug_deactivate(struct hrtimer *timer)
479 trace_hrtimer_cancel(timer); 486 trace_hrtimer_cancel(timer);
480} 487}
481 488
489static void hrtimer_expire_cancelable(struct hrtimer_cpu_base *cpu_base);
490
482/* High resolution timer related functions */ 491/* High resolution timer related functions */
483#ifdef CONFIG_HIGH_RES_TIMERS 492#ifdef CONFIG_HIGH_RES_TIMERS
484 493
@@ -715,9 +724,14 @@ static void retrigger_next_event(void *arg)
715 struct hrtimer_cpu_base *base = &__get_cpu_var(hrtimer_bases); 724 struct hrtimer_cpu_base *base = &__get_cpu_var(hrtimer_bases);
716 struct timespec realtime_offset, xtim, wtm, sleep; 725 struct timespec realtime_offset, xtim, wtm, sleep;
717 726
718 if (!hrtimer_hres_active()) 727 if (!hrtimer_hres_active()) {
728 raw_spin_lock(&base->lock);
729 hrtimer_expire_cancelable(base);
730 raw_spin_unlock(&base->lock);
719 return; 731 return;
732 }
720 733
734 /* Optimized out for !HIGH_RES */
721 get_xtime_and_monotonic_and_sleep_offset(&xtim, &wtm, &sleep); 735 get_xtime_and_monotonic_and_sleep_offset(&xtim, &wtm, &sleep);
722 set_normalized_timespec(&realtime_offset, -wtm.tv_sec, -wtm.tv_nsec); 736 set_normalized_timespec(&realtime_offset, -wtm.tv_sec, -wtm.tv_nsec);
723 737
@@ -727,6 +741,10 @@ static void retrigger_next_event(void *arg)
727 timespec_to_ktime(realtime_offset); 741 timespec_to_ktime(realtime_offset);
728 base->clock_base[HRTIMER_BASE_BOOTTIME].offset = 742 base->clock_base[HRTIMER_BASE_BOOTTIME].offset =
729 timespec_to_ktime(sleep); 743 timespec_to_ktime(sleep);
744 base->clock_base[HRTIMER_BASE_REALTIME_COS].offset =
745 timespec_to_ktime(realtime_offset);
746
747 hrtimer_expire_cancelable(base);
730 748
731 hrtimer_force_reprogram(base, 0); 749 hrtimer_force_reprogram(base, 0);
732 raw_spin_unlock(&base->lock); 750 raw_spin_unlock(&base->lock);
@@ -1222,6 +1240,22 @@ static void __run_hrtimer(struct hrtimer *timer, ktime_t *now)
1222 timer->state &= ~HRTIMER_STATE_CALLBACK; 1240 timer->state &= ~HRTIMER_STATE_CALLBACK;
1223} 1241}
1224 1242
1243static void hrtimer_expire_cancelable(struct hrtimer_cpu_base *cpu_base)
1244{
1245 struct timerqueue_node *node;
1246 struct hrtimer_clock_base *base;
1247 ktime_t now = ktime_get_real();
1248
1249 base = &cpu_base->clock_base[HRTIMER_BASE_REALTIME_COS];
1250
1251 while ((node = timerqueue_getnext(&base->active))) {
1252 struct hrtimer *timer;
1253
1254 timer = container_of(node, struct hrtimer, node);
1255 __run_hrtimer(timer, &now);
1256 }
1257}
1258
1225#ifdef CONFIG_HIGH_RES_TIMERS 1259#ifdef CONFIG_HIGH_RES_TIMERS
1226 1260
1227/* 1261/*
diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c
index a61b8fa2d39a..342408cf68dd 100644
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -1099,6 +1099,21 @@ void get_xtime_and_monotonic_and_sleep_offset(struct timespec *xtim,
1099} 1099}
1100 1100
1101/** 1101/**
1102 * ktime_get_monotonic_offset() - get wall_to_monotonic in ktime_t format
1103 */
1104ktime_t ktime_get_monotonic_offset(void)
1105{
1106 unsigned long seq;
1107 struct timespec wtom;
1108
1109 do {
1110 seq = read_seqbegin(&xtime_lock);
1111 wtom = wall_to_monotonic;
1112 } while (read_seqretry(&xtime_lock, seq));
1113 return timespec_to_ktime(wtom);
1114}
1115
1116/**
1102 * xtime_update() - advances the timekeeping infrastructure 1117 * xtime_update() - advances the timekeeping infrastructure
1103 * @ticks: number of ticks, that have elapsed since the last call. 1118 * @ticks: number of ticks, that have elapsed since the last call.
1104 * 1119 *