blob: b4e9e81448c22fc37479acbd67b6e76b5d200c68 [file] [log] [blame]
Davide Libenzib215e282007-05-10 22:23:16 -07001/*
2 * fs/timerfd.c
3 *
4 * Copyright (C) 2007 Davide Libenzi <davidel@xmailserver.org>
5 *
6 *
7 * Thanks to Thomas Gleixner for code reviews and useful comments.
8 *
9 */
10
Todd Poynord16e3ab2013-05-08 15:50:50 -070011#include <linux/alarmtimer.h>
Davide Libenzib215e282007-05-10 22:23:16 -070012#include <linux/file.h>
13#include <linux/poll.h>
14#include <linux/init.h>
15#include <linux/fs.h>
16#include <linux/sched.h>
17#include <linux/kernel.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090018#include <linux/slab.h>
Davide Libenzib215e282007-05-10 22:23:16 -070019#include <linux/list.h>
20#include <linux/spinlock.h>
21#include <linux/time.h>
22#include <linux/hrtimer.h>
23#include <linux/anon_inodes.h>
24#include <linux/timerfd.h>
Adrian Bunk45cc2b92008-04-29 00:58:59 -070025#include <linux/syscalls.h>
Thomas Gleixner9ec26902011-05-20 16:18:50 +020026#include <linux/rcupdate.h>
Davide Libenzib215e282007-05-10 22:23:16 -070027
28struct timerfd_ctx {
Todd Poynord16e3ab2013-05-08 15:50:50 -070029 union {
30 struct hrtimer tmr;
31 struct alarm alarm;
32 } t;
Davide Libenzib215e282007-05-10 22:23:16 -070033 ktime_t tintv;
Thomas Gleixner99ee5312011-04-27 14:16:42 +020034 ktime_t moffs;
Davide Libenzib215e282007-05-10 22:23:16 -070035 wait_queue_head_t wqh;
Davide Libenzi4d672e72008-02-04 22:27:26 -080036 u64 ticks;
Davide Libenzib215e282007-05-10 22:23:16 -070037 int expired;
Davide Libenzi4d672e72008-02-04 22:27:26 -080038 int clockid;
Thomas Gleixner9ec26902011-05-20 16:18:50 +020039 struct rcu_head rcu;
40 struct list_head clist;
Thomas Gleixner80941492017-08-22 10:37:48 +080041 spinlock_t cancel_lock;
Thomas Gleixner99ee5312011-04-27 14:16:42 +020042 bool might_cancel;
Davide Libenzib215e282007-05-10 22:23:16 -070043};
44
Thomas Gleixner9ec26902011-05-20 16:18:50 +020045static LIST_HEAD(cancel_list);
46static DEFINE_SPINLOCK(cancel_lock);
47
Todd Poynord16e3ab2013-05-08 15:50:50 -070048static inline bool isalarm(struct timerfd_ctx *ctx)
49{
50 return ctx->clockid == CLOCK_REALTIME_ALARM ||
51 ctx->clockid == CLOCK_BOOTTIME_ALARM;
52}
53
Davide Libenzib215e282007-05-10 22:23:16 -070054/*
55 * This gets called when the timer event triggers. We set the "expired"
56 * flag, but we do not re-arm the timer (in case it's necessary,
Davide Libenzi4d672e72008-02-04 22:27:26 -080057 * tintv.tv64 != 0) until the timer is accessed.
Davide Libenzib215e282007-05-10 22:23:16 -070058 */
Todd Poynord16e3ab2013-05-08 15:50:50 -070059static void timerfd_triggered(struct timerfd_ctx *ctx)
Davide Libenzib215e282007-05-10 22:23:16 -070060{
Davide Libenzib215e282007-05-10 22:23:16 -070061 unsigned long flags;
62
Davide Libenzi18963c02007-05-18 12:02:33 -070063 spin_lock_irqsave(&ctx->wqh.lock, flags);
Davide Libenzib215e282007-05-10 22:23:16 -070064 ctx->expired = 1;
Davide Libenzi4d672e72008-02-04 22:27:26 -080065 ctx->ticks++;
Davide Libenzib215e282007-05-10 22:23:16 -070066 wake_up_locked(&ctx->wqh);
Davide Libenzi18963c02007-05-18 12:02:33 -070067 spin_unlock_irqrestore(&ctx->wqh.lock, flags);
Todd Poynord16e3ab2013-05-08 15:50:50 -070068}
Davide Libenzib215e282007-05-10 22:23:16 -070069
Todd Poynord16e3ab2013-05-08 15:50:50 -070070static 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);
Davide Libenzib215e282007-05-10 22:23:16 -070075 return HRTIMER_NORESTART;
76}
77
Todd Poynord16e3ab2013-05-08 15:50:50 -070078static 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
Thomas Gleixner9ec26902011-05-20 16:18:50 +020087/*
88 * Called when the clock was set to cancel the timers in the cancel
Max Asbock1123d932011-06-13 10:18:32 -070089 * list. This will wake up processes waiting on these timers. The
90 * wake-up requires ctx->ticks to be non zero, therefore we increment
91 * it before calling wake_up_locked().
Thomas Gleixner9ec26902011-05-20 16:18:50 +020092 */
93void timerfd_clock_was_set(void)
94{
95 ktime_t moffs = ktime_get_monotonic_offset();
96 struct timerfd_ctx *ctx;
97 unsigned long flags;
98
99 rcu_read_lock();
100 list_for_each_entry_rcu(ctx, &cancel_list, clist) {
101 if (!ctx->might_cancel)
102 continue;
103 spin_lock_irqsave(&ctx->wqh.lock, flags);
104 if (ctx->moffs.tv64 != moffs.tv64) {
105 ctx->moffs.tv64 = KTIME_MAX;
Max Asbock1123d932011-06-13 10:18:32 -0700106 ctx->ticks++;
Thomas Gleixner9ec26902011-05-20 16:18:50 +0200107 wake_up_locked(&ctx->wqh);
108 }
109 spin_unlock_irqrestore(&ctx->wqh.lock, flags);
110 }
111 rcu_read_unlock();
112}
113
Thomas Gleixner80941492017-08-22 10:37:48 +0800114static void __timerfd_remove_cancel(struct timerfd_ctx *ctx)
Thomas Gleixner9ec26902011-05-20 16:18:50 +0200115{
116 if (ctx->might_cancel) {
117 ctx->might_cancel = false;
118 spin_lock(&cancel_lock);
119 list_del_rcu(&ctx->clist);
120 spin_unlock(&cancel_lock);
121 }
122}
123
Thomas Gleixner80941492017-08-22 10:37:48 +0800124static void timerfd_remove_cancel(struct timerfd_ctx *ctx)
125{
126 spin_lock(&ctx->cancel_lock);
127 __timerfd_remove_cancel(ctx);
128 spin_unlock(&ctx->cancel_lock);
129}
130
Thomas Gleixner9ec26902011-05-20 16:18:50 +0200131static bool timerfd_canceled(struct timerfd_ctx *ctx)
132{
133 if (!ctx->might_cancel || ctx->moffs.tv64 != KTIME_MAX)
134 return false;
135 ctx->moffs = ktime_get_monotonic_offset();
136 return true;
137}
138
139static void timerfd_setup_cancel(struct timerfd_ctx *ctx, int flags)
140{
Thomas Gleixner80941492017-08-22 10:37:48 +0800141 spin_lock(&ctx->cancel_lock);
Todd Poynord16e3ab2013-05-08 15:50:50 -0700142 if ((ctx->clockid == CLOCK_REALTIME ||
143 ctx->clockid == CLOCK_REALTIME_ALARM) &&
144 (flags & TFD_TIMER_ABSTIME) && (flags & TFD_TIMER_CANCEL_ON_SET)) {
Thomas Gleixner9ec26902011-05-20 16:18:50 +0200145 if (!ctx->might_cancel) {
146 ctx->might_cancel = true;
147 spin_lock(&cancel_lock);
148 list_add_rcu(&ctx->clist, &cancel_list);
149 spin_unlock(&cancel_lock);
150 }
Thomas Gleixner80941492017-08-22 10:37:48 +0800151 } else {
152 __timerfd_remove_cancel(ctx);
Thomas Gleixner9ec26902011-05-20 16:18:50 +0200153 }
Thomas Gleixner80941492017-08-22 10:37:48 +0800154 spin_unlock(&ctx->cancel_lock);
Thomas Gleixner9ec26902011-05-20 16:18:50 +0200155}
156
Davide Libenzi4d672e72008-02-04 22:27:26 -0800157static ktime_t timerfd_get_remaining(struct timerfd_ctx *ctx)
158{
Arjan van de Ven76369472008-09-01 15:00:14 -0700159 ktime_t remaining;
Davide Libenzi4d672e72008-02-04 22:27:26 -0800160
Todd Poynord16e3ab2013-05-08 15:50:50 -0700161 if (isalarm(ctx))
162 remaining = alarm_expires_remaining(&ctx->t.alarm);
163 else
164 remaining = hrtimer_expires_remaining(&ctx->t.tmr);
165
Davide Libenzi4d672e72008-02-04 22:27:26 -0800166 return remaining.tv64 < 0 ? ktime_set(0, 0): remaining;
167}
168
Thomas Gleixner99ee5312011-04-27 14:16:42 +0200169static int timerfd_setup(struct timerfd_ctx *ctx, int flags,
170 const struct itimerspec *ktmr)
Davide Libenzib215e282007-05-10 22:23:16 -0700171{
172 enum hrtimer_mode htmode;
173 ktime_t texp;
Thomas Gleixner99ee5312011-04-27 14:16:42 +0200174 int clockid = ctx->clockid;
Davide Libenzib215e282007-05-10 22:23:16 -0700175
176 htmode = (flags & TFD_TIMER_ABSTIME) ?
177 HRTIMER_MODE_ABS: HRTIMER_MODE_REL;
178
179 texp = timespec_to_ktime(ktmr->it_value);
180 ctx->expired = 0;
Davide Libenzi4d672e72008-02-04 22:27:26 -0800181 ctx->ticks = 0;
Davide Libenzib215e282007-05-10 22:23:16 -0700182 ctx->tintv = timespec_to_ktime(ktmr->it_interval);
Todd Poynord16e3ab2013-05-08 15:50:50 -0700183
184 if (isalarm(ctx)) {
185 alarm_init(&ctx->t.alarm,
186 ctx->clockid == CLOCK_REALTIME_ALARM ?
187 ALARM_REALTIME : ALARM_BOOTTIME,
188 timerfd_alarmproc);
189 } else {
190 hrtimer_init(&ctx->t.tmr, clockid, htmode);
191 hrtimer_set_expires(&ctx->t.tmr, texp);
192 ctx->t.tmr.function = timerfd_tmrproc;
193 }
194
Thomas Gleixner99ee5312011-04-27 14:16:42 +0200195 if (texp.tv64 != 0) {
Todd Poynord16e3ab2013-05-08 15:50:50 -0700196 if (isalarm(ctx)) {
197 if (flags & TFD_TIMER_ABSTIME)
198 alarm_start(&ctx->t.alarm, texp);
199 else
200 alarm_start_relative(&ctx->t.alarm, texp);
201 } else {
202 hrtimer_start(&ctx->t.tmr, texp, htmode);
203 }
204
Thomas Gleixner99ee5312011-04-27 14:16:42 +0200205 if (timerfd_canceled(ctx))
206 return -ECANCELED;
207 }
208 return 0;
Davide Libenzib215e282007-05-10 22:23:16 -0700209}
210
211static int timerfd_release(struct inode *inode, struct file *file)
212{
213 struct timerfd_ctx *ctx = file->private_data;
214
Thomas Gleixner9ec26902011-05-20 16:18:50 +0200215 timerfd_remove_cancel(ctx);
Todd Poynord16e3ab2013-05-08 15:50:50 -0700216
217 if (isalarm(ctx))
218 alarm_cancel(&ctx->t.alarm);
219 else
220 hrtimer_cancel(&ctx->t.tmr);
Thomas Gleixner9ec26902011-05-20 16:18:50 +0200221 kfree_rcu(ctx, rcu);
Davide Libenzib215e282007-05-10 22:23:16 -0700222 return 0;
223}
224
225static unsigned int timerfd_poll(struct file *file, poll_table *wait)
226{
227 struct timerfd_ctx *ctx = file->private_data;
228 unsigned int events = 0;
229 unsigned long flags;
230
231 poll_wait(file, &ctx->wqh, wait);
232
Davide Libenzi18963c02007-05-18 12:02:33 -0700233 spin_lock_irqsave(&ctx->wqh.lock, flags);
Davide Libenzi4d672e72008-02-04 22:27:26 -0800234 if (ctx->ticks)
Davide Libenzib215e282007-05-10 22:23:16 -0700235 events |= POLLIN;
Davide Libenzi18963c02007-05-18 12:02:33 -0700236 spin_unlock_irqrestore(&ctx->wqh.lock, flags);
Davide Libenzib215e282007-05-10 22:23:16 -0700237
238 return events;
239}
240
241static ssize_t timerfd_read(struct file *file, char __user *buf, size_t count,
242 loff_t *ppos)
243{
244 struct timerfd_ctx *ctx = file->private_data;
245 ssize_t res;
Davide Libenzi09828402007-07-26 10:41:07 -0700246 u64 ticks = 0;
Davide Libenzib215e282007-05-10 22:23:16 -0700247
248 if (count < sizeof(ticks))
249 return -EINVAL;
Davide Libenzi18963c02007-05-18 12:02:33 -0700250 spin_lock_irq(&ctx->wqh.lock);
Michal Nazarewicz8120a8a2010-05-05 12:53:12 +0200251 if (file->f_flags & O_NONBLOCK)
252 res = -EAGAIN;
253 else
254 res = wait_event_interruptible_locked_irq(ctx->wqh, ctx->ticks);
Thomas Gleixner99ee5312011-04-27 14:16:42 +0200255
Thomas Gleixner9ec26902011-05-20 16:18:50 +0200256 /*
257 * If clock has changed, we do not care about the
258 * ticks and we do not rearm the timer. Userspace must
259 * reevaluate anyway.
260 */
261 if (timerfd_canceled(ctx)) {
262 ctx->ticks = 0;
263 ctx->expired = 0;
264 res = -ECANCELED;
265 }
266
Davide Libenzi4d672e72008-02-04 22:27:26 -0800267 if (ctx->ticks) {
268 ticks = ctx->ticks;
Thomas Gleixner99ee5312011-04-27 14:16:42 +0200269
Davide Libenzi4d672e72008-02-04 22:27:26 -0800270 if (ctx->expired && ctx->tintv.tv64) {
Davide Libenzib215e282007-05-10 22:23:16 -0700271 /*
272 * If tintv.tv64 != 0, this is a periodic timer that
273 * needs to be re-armed. We avoid doing it in the timer
274 * callback to avoid DoS attacks specifying a very
275 * short timer period.
276 */
Todd Poynord16e3ab2013-05-08 15:50:50 -0700277 if (isalarm(ctx)) {
278 ticks += alarm_forward_now(
279 &ctx->t.alarm, ctx->tintv) - 1;
280 alarm_restart(&ctx->t.alarm);
281 } else {
282 ticks += hrtimer_forward_now(&ctx->t.tmr,
283 ctx->tintv) - 1;
284 hrtimer_restart(&ctx->t.tmr);
285 }
Davide Libenzi4d672e72008-02-04 22:27:26 -0800286 }
287 ctx->expired = 0;
288 ctx->ticks = 0;
Davide Libenzib215e282007-05-10 22:23:16 -0700289 }
Davide Libenzi18963c02007-05-18 12:02:33 -0700290 spin_unlock_irq(&ctx->wqh.lock);
Davide Libenzib215e282007-05-10 22:23:16 -0700291 if (ticks)
Davide Libenzi09828402007-07-26 10:41:07 -0700292 res = put_user(ticks, (u64 __user *) buf) ? -EFAULT: sizeof(ticks);
Davide Libenzib215e282007-05-10 22:23:16 -0700293 return res;
294}
295
296static const struct file_operations timerfd_fops = {
297 .release = timerfd_release,
298 .poll = timerfd_poll,
299 .read = timerfd_read,
Arnd Bergmann6038f372010-08-15 18:52:59 +0200300 .llseek = noop_llseek,
Davide Libenzib215e282007-05-10 22:23:16 -0700301};
302
Al Virof19b2c42012-08-26 21:32:02 -0400303static struct file *timerfd_fget(int fd, int *fput_needed)
Davide Libenzib215e282007-05-10 22:23:16 -0700304{
Davide Libenzi4d672e72008-02-04 22:27:26 -0800305 struct file *file;
306
Al Virof19b2c42012-08-26 21:32:02 -0400307 file = fget_light(fd, fput_needed);
Davide Libenzi4d672e72008-02-04 22:27:26 -0800308 if (!file)
309 return ERR_PTR(-EBADF);
310 if (file->f_op != &timerfd_fops) {
Al Virof19b2c42012-08-26 21:32:02 -0400311 fput_light(file, *fput_needed);
Davide Libenzi4d672e72008-02-04 22:27:26 -0800312 return ERR_PTR(-EINVAL);
313 }
314
315 return file;
316}
317
Heiko Carstens836f92a2009-01-14 14:14:33 +0100318SYSCALL_DEFINE2(timerfd_create, int, clockid, int, flags)
Davide Libenzi4d672e72008-02-04 22:27:26 -0800319{
Al Viro2030a422008-02-23 06:46:49 -0500320 int ufd;
Davide Libenzib215e282007-05-10 22:23:16 -0700321 struct timerfd_ctx *ctx;
Davide Libenzi4d672e72008-02-04 22:27:26 -0800322
Ulrich Dreppere38b36f2008-07-23 21:29:42 -0700323 /* Check the TFD_* constants for consistency. */
324 BUILD_BUG_ON(TFD_CLOEXEC != O_CLOEXEC);
325 BUILD_BUG_ON(TFD_NONBLOCK != O_NONBLOCK);
326
Davide Libenzi610d18f2009-02-18 14:48:18 -0800327 if ((flags & ~TFD_CREATE_FLAGS) ||
328 (clockid != CLOCK_MONOTONIC &&
Todd Poynord16e3ab2013-05-08 15:50:50 -0700329 clockid != CLOCK_REALTIME &&
330 clockid != CLOCK_REALTIME_ALARM &&
Greg Hackmann74db9602013-12-13 13:02:31 -0800331 clockid != CLOCK_BOOTTIME &&
Todd Poynord16e3ab2013-05-08 15:50:50 -0700332 clockid != CLOCK_BOOTTIME_ALARM))
Davide Libenzi4d672e72008-02-04 22:27:26 -0800333 return -EINVAL;
334
335 ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
336 if (!ctx)
337 return -ENOMEM;
338
339 init_waitqueue_head(&ctx->wqh);
Thomas Gleixner80941492017-08-22 10:37:48 +0800340 spin_lock_init(&ctx->cancel_lock);
Davide Libenzi4d672e72008-02-04 22:27:26 -0800341 ctx->clockid = clockid;
Todd Poynord16e3ab2013-05-08 15:50:50 -0700342
343 if (isalarm(ctx))
344 alarm_init(&ctx->t.alarm,
345 ctx->clockid == CLOCK_REALTIME_ALARM ?
346 ALARM_REALTIME : ALARM_BOOTTIME,
347 timerfd_alarmproc);
348 else
349 hrtimer_init(&ctx->t.tmr, clockid, HRTIMER_MODE_ABS);
350
Thomas Gleixner99ee5312011-04-27 14:16:42 +0200351 ctx->moffs = ktime_get_monotonic_offset();
Davide Libenzi4d672e72008-02-04 22:27:26 -0800352
Ulrich Drepper11fcb6c2008-07-23 21:29:26 -0700353 ufd = anon_inode_getfd("[timerfd]", &timerfd_fops, ctx,
Roland Dreier628ff7c2009-12-18 09:41:24 -0800354 O_RDWR | (flags & TFD_SHARED_FCNTL_FLAGS));
Al Viro2030a422008-02-23 06:46:49 -0500355 if (ufd < 0)
Davide Libenzi4d672e72008-02-04 22:27:26 -0800356 kfree(ctx);
Davide Libenzi4d672e72008-02-04 22:27:26 -0800357
358 return ufd;
359}
360
Heiko Carstens836f92a2009-01-14 14:14:33 +0100361SYSCALL_DEFINE4(timerfd_settime, int, ufd, int, flags,
362 const struct itimerspec __user *, utmr,
363 struct itimerspec __user *, otmr)
Davide Libenzi4d672e72008-02-04 22:27:26 -0800364{
365 struct file *file;
366 struct timerfd_ctx *ctx;
367 struct itimerspec ktmr, kotmr;
Al Virof19b2c42012-08-26 21:32:02 -0400368 int ret, fput_needed;
Davide Libenzib215e282007-05-10 22:23:16 -0700369
370 if (copy_from_user(&ktmr, utmr, sizeof(ktmr)))
371 return -EFAULT;
372
Davide Libenzi610d18f2009-02-18 14:48:18 -0800373 if ((flags & ~TFD_SETTIME_FLAGS) ||
374 !timespec_valid(&ktmr.it_value) ||
Davide Libenzib215e282007-05-10 22:23:16 -0700375 !timespec_valid(&ktmr.it_interval))
376 return -EINVAL;
377
Al Virof19b2c42012-08-26 21:32:02 -0400378 file = timerfd_fget(ufd, &fput_needed);
Davide Libenzi4d672e72008-02-04 22:27:26 -0800379 if (IS_ERR(file))
380 return PTR_ERR(file);
381 ctx = file->private_data;
Davide Libenzib215e282007-05-10 22:23:16 -0700382
Thomas Gleixner9ec26902011-05-20 16:18:50 +0200383 timerfd_setup_cancel(ctx, flags);
384
Davide Libenzi4d672e72008-02-04 22:27:26 -0800385 /*
386 * We need to stop the existing timer before reprogramming
387 * it to the new values.
388 */
389 for (;;) {
390 spin_lock_irq(&ctx->wqh.lock);
Todd Poynord16e3ab2013-05-08 15:50:50 -0700391
392 if (isalarm(ctx)) {
393 if (alarm_try_to_cancel(&ctx->t.alarm) >= 0)
394 break;
395 } else {
396 if (hrtimer_try_to_cancel(&ctx->t.tmr) >= 0)
397 break;
398 }
Davide Libenzi18963c02007-05-18 12:02:33 -0700399 spin_unlock_irq(&ctx->wqh.lock);
Davide Libenzi4d672e72008-02-04 22:27:26 -0800400 cpu_relax();
Davide Libenzib215e282007-05-10 22:23:16 -0700401 }
402
Davide Libenzi4d672e72008-02-04 22:27:26 -0800403 /*
404 * If the timer is expired and it's periodic, we need to advance it
405 * because the caller may want to know the previous expiration time.
406 * We do not update "ticks" and "expired" since the timer will be
407 * re-programmed again in the following timerfd_setup() call.
408 */
Todd Poynord16e3ab2013-05-08 15:50:50 -0700409 if (ctx->expired && ctx->tintv.tv64) {
410 if (isalarm(ctx))
411 alarm_forward_now(&ctx->t.alarm, ctx->tintv);
412 else
413 hrtimer_forward_now(&ctx->t.tmr, ctx->tintv);
414 }
Davide Libenzib215e282007-05-10 22:23:16 -0700415
Davide Libenzi4d672e72008-02-04 22:27:26 -0800416 kotmr.it_value = ktime_to_timespec(timerfd_get_remaining(ctx));
417 kotmr.it_interval = ktime_to_timespec(ctx->tintv);
418
419 /*
420 * Re-program the timer to the new value ...
421 */
Thomas Gleixner99ee5312011-04-27 14:16:42 +0200422 ret = timerfd_setup(ctx, flags, &ktmr);
Davide Libenzi4d672e72008-02-04 22:27:26 -0800423
424 spin_unlock_irq(&ctx->wqh.lock);
Al Virof19b2c42012-08-26 21:32:02 -0400425 fput_light(file, fput_needed);
Davide Libenzi4d672e72008-02-04 22:27:26 -0800426 if (otmr && copy_to_user(otmr, &kotmr, sizeof(kotmr)))
427 return -EFAULT;
428
Thomas Gleixner99ee5312011-04-27 14:16:42 +0200429 return ret;
Davide Libenzi4d672e72008-02-04 22:27:26 -0800430}
431
Heiko Carstensd4e82042009-01-14 14:14:34 +0100432SYSCALL_DEFINE2(timerfd_gettime, int, ufd, struct itimerspec __user *, otmr)
Davide Libenzi4d672e72008-02-04 22:27:26 -0800433{
434 struct file *file;
435 struct timerfd_ctx *ctx;
436 struct itimerspec kotmr;
Al Virof19b2c42012-08-26 21:32:02 -0400437 int fput_needed;
Davide Libenzi4d672e72008-02-04 22:27:26 -0800438
Al Virof19b2c42012-08-26 21:32:02 -0400439 file = timerfd_fget(ufd, &fput_needed);
Davide Libenzi4d672e72008-02-04 22:27:26 -0800440 if (IS_ERR(file))
441 return PTR_ERR(file);
442 ctx = file->private_data;
443
444 spin_lock_irq(&ctx->wqh.lock);
445 if (ctx->expired && ctx->tintv.tv64) {
446 ctx->expired = 0;
Todd Poynord16e3ab2013-05-08 15:50:50 -0700447
448 if (isalarm(ctx)) {
449 ctx->ticks +=
450 alarm_forward_now(
451 &ctx->t.alarm, ctx->tintv) - 1;
452 alarm_restart(&ctx->t.alarm);
453 } else {
454 ctx->ticks +=
455 hrtimer_forward_now(&ctx->t.tmr, ctx->tintv)
456 - 1;
457 hrtimer_restart(&ctx->t.tmr);
458 }
Davide Libenzi4d672e72008-02-04 22:27:26 -0800459 }
460 kotmr.it_value = ktime_to_timespec(timerfd_get_remaining(ctx));
461 kotmr.it_interval = ktime_to_timespec(ctx->tintv);
462 spin_unlock_irq(&ctx->wqh.lock);
Al Virof19b2c42012-08-26 21:32:02 -0400463 fput_light(file, fput_needed);
Davide Libenzi4d672e72008-02-04 22:27:26 -0800464
465 return copy_to_user(otmr, &kotmr, sizeof(kotmr)) ? -EFAULT: 0;
Davide Libenzib215e282007-05-10 22:23:16 -0700466}
467