aboutsummaryrefslogtreecommitdiffstats
path: root/fs/timerfd.c
diff options
context:
space:
mode:
authorTodd Poynor <toddpoynor@google.com>2013-05-15 17:38:12 -0400
committerJohn Stultz <john.stultz@linaro.org>2013-05-29 15:57:34 -0400
commit11ffa9d6065f344a9bd769a2452f26f2f671e5f8 (patch)
tree8c790f1f8203d797b0b77ca0e4e52da2536796ef /fs/timerfd.c
parent6cffe00f7d4e24679eae6b7aae4caaf915288256 (diff)
timerfd: Add alarm timers
Add support for clocks CLOCK_REALTIME_ALARM and CLOCK_BOOTTIME_ALARM, thereby enabling wakeup alarm timers via file descriptors. Signed-off-by: Todd Poynor <toddpoynor@google.com> Signed-off-by: John Stultz <john.stultz@linaro.org>
Diffstat (limited to 'fs/timerfd.c')
-rw-r--r--fs/timerfd.c131
1 files changed, 108 insertions, 23 deletions
diff --git a/fs/timerfd.c b/fs/timerfd.c
index 32b644f03690..929312180dd0 100644
--- a/fs/timerfd.c
+++ b/fs/timerfd.c
@@ -8,6 +8,7 @@
8 * 8 *
9 */ 9 */
10 10
11#include <linux/alarmtimer.h>
11#include <linux/file.h> 12#include <linux/file.h>
12#include <linux/poll.h> 13#include <linux/poll.h>
13#include <linux/init.h> 14#include <linux/init.h>
@@ -26,7 +27,10 @@
26#include <linux/rcupdate.h> 27#include <linux/rcupdate.h>
27 28
28struct timerfd_ctx { 29struct timerfd_ctx {
29 struct hrtimer tmr; 30 union {
31 struct hrtimer tmr;
32 struct alarm alarm;
33 } t;
30 ktime_t tintv; 34 ktime_t tintv;
31 ktime_t moffs; 35 ktime_t moffs;
32 wait_queue_head_t wqh; 36 wait_queue_head_t wqh;
@@ -41,14 +45,19 @@ struct timerfd_ctx {
41static LIST_HEAD(cancel_list); 45static LIST_HEAD(cancel_list);
42static DEFINE_SPINLOCK(cancel_lock); 46static DEFINE_SPINLOCK(cancel_lock);
43 47
48static inline bool isalarm(struct timerfd_ctx *ctx)
49{
50 return ctx->clockid == CLOCK_REALTIME_ALARM ||
51 ctx->clockid == CLOCK_BOOTTIME_ALARM;
52}
53
44/* 54/*
45 * This gets called when the timer event triggers. We set the "expired" 55 * This gets called when the timer event triggers. We set the "expired"
46 * flag, but we do not re-arm the timer (in case it's necessary, 56 * flag, but we do not re-arm the timer (in case it's necessary,
47 * tintv.tv64 != 0) until the timer is accessed. 57 * tintv.tv64 != 0) until the timer is accessed.
48 */ 58 */
49static enum hrtimer_restart timerfd_tmrproc(struct hrtimer *htmr) 59static void timerfd_triggered(struct timerfd_ctx *ctx)
50{ 60{
51 struct timerfd_ctx *ctx = container_of(htmr, struct timerfd_ctx, tmr);
52 unsigned long flags; 61 unsigned long flags;
53 62
54 spin_lock_irqsave(&ctx->wqh.lock, flags); 63 spin_lock_irqsave(&ctx->wqh.lock, flags);
@@ -56,10 +65,25 @@ static enum hrtimer_restart timerfd_tmrproc(struct hrtimer *htmr)
56 ctx->ticks++; 65 ctx->ticks++;
57 wake_up_locked(&ctx->wqh); 66 wake_up_locked(&ctx->wqh);
58 spin_unlock_irqrestore(&ctx->wqh.lock, flags); 67 spin_unlock_irqrestore(&ctx->wqh.lock, flags);
68}
59 69
70static enum hrtimer_restart timerfd_tmrproc(struct hrtimer *htmr)
71{
72 struct timerfd_ctx *ctx = container_of(htmr, struct timerfd_ctx,
73 t.tmr);
74 timerfd_triggered(ctx);
60 return HRTIMER_NORESTART; 75 return HRTIMER_NORESTART;
61} 76}
62 77
78static enum alarmtimer_restart timerfd_alarmproc(struct alarm *alarm,
79 ktime_t now)
80{
81 struct timerfd_ctx *ctx = container_of(alarm, struct timerfd_ctx,
82 t.alarm);
83 timerfd_triggered(ctx);
84 return ALARMTIMER_NORESTART;
85}
86
63/* 87/*
64 * Called when the clock was set to cancel the timers in the cancel 88 * Called when the clock was set to cancel the timers in the cancel
65 * list. This will wake up processes waiting on these timers. The 89 * list. This will wake up processes waiting on these timers. The
@@ -107,8 +131,9 @@ static bool timerfd_canceled(struct timerfd_ctx *ctx)
107 131
108static void timerfd_setup_cancel(struct timerfd_ctx *ctx, int flags) 132static void timerfd_setup_cancel(struct timerfd_ctx *ctx, int flags)
109{ 133{
110 if (ctx->clockid == CLOCK_REALTIME && (flags & TFD_TIMER_ABSTIME) && 134 if ((ctx->clockid == CLOCK_REALTIME ||
111 (flags & TFD_TIMER_CANCEL_ON_SET)) { 135 ctx->clockid == CLOCK_REALTIME_ALARM) &&
136 (flags & TFD_TIMER_ABSTIME) && (flags & TFD_TIMER_CANCEL_ON_SET)) {
112 if (!ctx->might_cancel) { 137 if (!ctx->might_cancel) {
113 ctx->might_cancel = true; 138 ctx->might_cancel = true;
114 spin_lock(&cancel_lock); 139 spin_lock(&cancel_lock);
@@ -124,7 +149,11 @@ static ktime_t timerfd_get_remaining(struct timerfd_ctx *ctx)
124{ 149{
125 ktime_t remaining; 150 ktime_t remaining;
126 151
127 remaining = hrtimer_expires_remaining(&ctx->tmr); 152 if (isalarm(ctx))
153 remaining = alarm_expires_remaining(&ctx->t.alarm);
154 else
155 remaining = hrtimer_expires_remaining(&ctx->t.tmr);
156
128 return remaining.tv64 < 0 ? ktime_set(0, 0): remaining; 157 return remaining.tv64 < 0 ? ktime_set(0, 0): remaining;
129} 158}
130 159
@@ -142,11 +171,28 @@ static int timerfd_setup(struct timerfd_ctx *ctx, int flags,
142 ctx->expired = 0; 171 ctx->expired = 0;
143 ctx->ticks = 0; 172 ctx->ticks = 0;
144 ctx->tintv = timespec_to_ktime(ktmr->it_interval); 173 ctx->tintv = timespec_to_ktime(ktmr->it_interval);
145 hrtimer_init(&ctx->tmr, clockid, htmode); 174
146 hrtimer_set_expires(&ctx->tmr, texp); 175 if (isalarm(ctx)) {
147 ctx->tmr.function = timerfd_tmrproc; 176 alarm_init(&ctx->t.alarm,
177 ctx->clockid == CLOCK_REALTIME_ALARM ?
178 ALARM_REALTIME : ALARM_BOOTTIME,
179 timerfd_alarmproc);
180 } else {
181 hrtimer_init(&ctx->t.tmr, clockid, htmode);
182 hrtimer_set_expires(&ctx->t.tmr, texp);
183 ctx->t.tmr.function = timerfd_tmrproc;
184 }
185
148 if (texp.tv64 != 0) { 186 if (texp.tv64 != 0) {
149 hrtimer_start(&ctx->tmr, texp, htmode); 187 if (isalarm(ctx)) {
188 if (flags & TFD_TIMER_ABSTIME)
189 alarm_start(&ctx->t.alarm, texp);
190 else
191 alarm_start_relative(&ctx->t.alarm, texp);
192 } else {
193 hrtimer_start(&ctx->t.tmr, texp, htmode);
194 }
195
150 if (timerfd_canceled(ctx)) 196 if (timerfd_canceled(ctx))
151 return -ECANCELED; 197 return -ECANCELED;
152 } 198 }
@@ -158,7 +204,11 @@ static int timerfd_release(struct inode *inode, struct file *file)
158 struct timerfd_ctx *ctx = file->private_data; 204 struct timerfd_ctx *ctx = file->private_data;
159 205
160 timerfd_remove_cancel(ctx); 206 timerfd_remove_cancel(ctx);
161 hrtimer_cancel(&ctx->tmr); 207
208 if (isalarm(ctx))
209 alarm_cancel(&ctx->t.alarm);
210 else
211 hrtimer_cancel(&ctx->t.tmr);
162 kfree_rcu(ctx, rcu); 212 kfree_rcu(ctx, rcu);
163 return 0; 213 return 0;
164} 214}
@@ -215,9 +265,15 @@ static ssize_t timerfd_read(struct file *file, char __user *buf, size_t count,
215 * callback to avoid DoS attacks specifying a very 265 * callback to avoid DoS attacks specifying a very
216 * short timer period. 266 * short timer period.
217 */ 267 */
218 ticks += hrtimer_forward_now(&ctx->tmr, 268 if (isalarm(ctx)) {
219 ctx->tintv) - 1; 269 ticks += alarm_forward_now(
220 hrtimer_restart(&ctx->tmr); 270 &ctx->t.alarm, ctx->tintv) - 1;
271 alarm_restart(&ctx->t.alarm);
272 } else {
273 ticks += hrtimer_forward_now(&ctx->t.tmr,
274 ctx->tintv) - 1;
275 hrtimer_restart(&ctx->t.tmr);
276 }
221 } 277 }
222 ctx->expired = 0; 278 ctx->expired = 0;
223 ctx->ticks = 0; 279 ctx->ticks = 0;
@@ -259,7 +315,9 @@ SYSCALL_DEFINE2(timerfd_create, int, clockid, int, flags)
259 315
260 if ((flags & ~TFD_CREATE_FLAGS) || 316 if ((flags & ~TFD_CREATE_FLAGS) ||
261 (clockid != CLOCK_MONOTONIC && 317 (clockid != CLOCK_MONOTONIC &&
262 clockid != CLOCK_REALTIME)) 318 clockid != CLOCK_REALTIME &&
319 clockid != CLOCK_REALTIME_ALARM &&
320 clockid != CLOCK_BOOTTIME_ALARM))
263 return -EINVAL; 321 return -EINVAL;
264 322
265 ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); 323 ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
@@ -268,7 +326,15 @@ SYSCALL_DEFINE2(timerfd_create, int, clockid, int, flags)
268 326
269 init_waitqueue_head(&ctx->wqh); 327 init_waitqueue_head(&ctx->wqh);
270 ctx->clockid = clockid; 328 ctx->clockid = clockid;
271 hrtimer_init(&ctx->tmr, clockid, HRTIMER_MODE_ABS); 329
330 if (isalarm(ctx))
331 alarm_init(&ctx->t.alarm,
332 ctx->clockid == CLOCK_REALTIME_ALARM ?
333 ALARM_REALTIME : ALARM_BOOTTIME,
334 timerfd_alarmproc);
335 else
336 hrtimer_init(&ctx->t.tmr, clockid, HRTIMER_MODE_ABS);
337
272 ctx->moffs = ktime_get_monotonic_offset(); 338 ctx->moffs = ktime_get_monotonic_offset();
273 339
274 ufd = anon_inode_getfd("[timerfd]", &timerfd_fops, ctx, 340 ufd = anon_inode_getfd("[timerfd]", &timerfd_fops, ctx,
@@ -305,8 +371,14 @@ static int do_timerfd_settime(int ufd, int flags,
305 */ 371 */
306 for (;;) { 372 for (;;) {
307 spin_lock_irq(&ctx->wqh.lock); 373 spin_lock_irq(&ctx->wqh.lock);
308 if (hrtimer_try_to_cancel(&ctx->tmr) >= 0) 374
309 break; 375 if (isalarm(ctx)) {
376 if (alarm_try_to_cancel(&ctx->t.alarm) >= 0)
377 break;
378 } else {
379 if (hrtimer_try_to_cancel(&ctx->t.tmr) >= 0)
380 break;
381 }
310 spin_unlock_irq(&ctx->wqh.lock); 382 spin_unlock_irq(&ctx->wqh.lock);
311 cpu_relax(); 383 cpu_relax();
312 } 384 }
@@ -317,8 +389,12 @@ static int do_timerfd_settime(int ufd, int flags,
317 * We do not update "ticks" and "expired" since the timer will be 389 * We do not update "ticks" and "expired" since the timer will be
318 * re-programmed again in the following timerfd_setup() call. 390 * re-programmed again in the following timerfd_setup() call.
319 */ 391 */
320 if (ctx->expired && ctx->tintv.tv64) 392 if (ctx->expired && ctx->tintv.tv64) {
321 hrtimer_forward_now(&ctx->tmr, ctx->tintv); 393 if (isalarm(ctx))
394 alarm_forward_now(&ctx->t.alarm, ctx->tintv);
395 else
396 hrtimer_forward_now(&ctx->t.tmr, ctx->tintv);
397 }
322 398
323 old->it_value = ktime_to_timespec(timerfd_get_remaining(ctx)); 399 old->it_value = ktime_to_timespec(timerfd_get_remaining(ctx));
324 old->it_interval = ktime_to_timespec(ctx->tintv); 400 old->it_interval = ktime_to_timespec(ctx->tintv);
@@ -345,9 +421,18 @@ static int do_timerfd_gettime(int ufd, struct itimerspec *t)
345 spin_lock_irq(&ctx->wqh.lock); 421 spin_lock_irq(&ctx->wqh.lock);
346 if (ctx->expired && ctx->tintv.tv64) { 422 if (ctx->expired && ctx->tintv.tv64) {
347 ctx->expired = 0; 423 ctx->expired = 0;
348 ctx->ticks += 424
349 hrtimer_forward_now(&ctx->tmr, ctx->tintv) - 1; 425 if (isalarm(ctx)) {
350 hrtimer_restart(&ctx->tmr); 426 ctx->ticks +=
427 alarm_forward_now(
428 &ctx->t.alarm, ctx->tintv) - 1;
429 alarm_restart(&ctx->t.alarm);
430 } else {
431 ctx->ticks +=
432 hrtimer_forward_now(&ctx->t.tmr, ctx->tintv)
433 - 1;
434 hrtimer_restart(&ctx->t.tmr);
435 }
351 } 436 }
352 t->it_value = ktime_to_timespec(timerfd_get_remaining(ctx)); 437 t->it_value = ktime_to_timespec(timerfd_get_remaining(ctx));
353 t->it_interval = ktime_to_timespec(ctx->tintv); 438 t->it_interval = ktime_to_timespec(ctx->tintv);