1b215e283SDavide Libenzi /* 2b215e283SDavide Libenzi * fs/timerfd.c 3b215e283SDavide Libenzi * 4b215e283SDavide Libenzi * Copyright (C) 2007 Davide Libenzi <davidel@xmailserver.org> 5b215e283SDavide Libenzi * 6b215e283SDavide Libenzi * 7b215e283SDavide Libenzi * Thanks to Thomas Gleixner for code reviews and useful comments. 8b215e283SDavide Libenzi * 9b215e283SDavide Libenzi */ 10b215e283SDavide Libenzi 11b215e283SDavide Libenzi #include <linux/file.h> 12b215e283SDavide Libenzi #include <linux/poll.h> 13b215e283SDavide Libenzi #include <linux/init.h> 14b215e283SDavide Libenzi #include <linux/fs.h> 15b215e283SDavide Libenzi #include <linux/sched.h> 16b215e283SDavide Libenzi #include <linux/kernel.h> 175a0e3ad6STejun Heo #include <linux/slab.h> 18b215e283SDavide Libenzi #include <linux/list.h> 19b215e283SDavide Libenzi #include <linux/spinlock.h> 20b215e283SDavide Libenzi #include <linux/time.h> 21b215e283SDavide Libenzi #include <linux/hrtimer.h> 22b215e283SDavide Libenzi #include <linux/anon_inodes.h> 23b215e283SDavide Libenzi #include <linux/timerfd.h> 2445cc2b96SAdrian Bunk #include <linux/syscalls.h> 259d94b9e2SAl Viro #include <linux/compat.h> 269ec26907SThomas Gleixner #include <linux/rcupdate.h> 27b215e283SDavide Libenzi 28b215e283SDavide Libenzi struct timerfd_ctx { 29b215e283SDavide Libenzi struct hrtimer tmr; 30b215e283SDavide Libenzi ktime_t tintv; 3199ee5315SThomas Gleixner ktime_t moffs; 32b215e283SDavide Libenzi wait_queue_head_t wqh; 334d672e7aSDavide Libenzi u64 ticks; 34b215e283SDavide Libenzi int expired; 354d672e7aSDavide Libenzi int clockid; 369ec26907SThomas Gleixner struct rcu_head rcu; 379ec26907SThomas Gleixner struct list_head clist; 3899ee5315SThomas Gleixner bool might_cancel; 39b215e283SDavide Libenzi }; 40b215e283SDavide Libenzi 419ec26907SThomas Gleixner static LIST_HEAD(cancel_list); 429ec26907SThomas Gleixner static DEFINE_SPINLOCK(cancel_lock); 439ec26907SThomas Gleixner 44b215e283SDavide Libenzi /* 45b215e283SDavide Libenzi * This gets called when the timer event triggers. We set the "expired" 46b215e283SDavide Libenzi * flag, but we do not re-arm the timer (in case it's necessary, 474d672e7aSDavide Libenzi * tintv.tv64 != 0) until the timer is accessed. 48b215e283SDavide Libenzi */ 49b215e283SDavide Libenzi static enum hrtimer_restart timerfd_tmrproc(struct hrtimer *htmr) 50b215e283SDavide Libenzi { 51b215e283SDavide Libenzi struct timerfd_ctx *ctx = container_of(htmr, struct timerfd_ctx, tmr); 52b215e283SDavide Libenzi unsigned long flags; 53b215e283SDavide Libenzi 5418963c01SDavide Libenzi spin_lock_irqsave(&ctx->wqh.lock, flags); 55b215e283SDavide Libenzi ctx->expired = 1; 564d672e7aSDavide Libenzi ctx->ticks++; 57b215e283SDavide Libenzi wake_up_locked(&ctx->wqh); 5818963c01SDavide Libenzi spin_unlock_irqrestore(&ctx->wqh.lock, flags); 59b215e283SDavide Libenzi 60b215e283SDavide Libenzi return HRTIMER_NORESTART; 61b215e283SDavide Libenzi } 62b215e283SDavide Libenzi 639ec26907SThomas Gleixner /* 649ec26907SThomas Gleixner * Called when the clock was set to cancel the timers in the cancel 651123d939SMax Asbock * list. This will wake up processes waiting on these timers. The 661123d939SMax Asbock * wake-up requires ctx->ticks to be non zero, therefore we increment 671123d939SMax Asbock * it before calling wake_up_locked(). 689ec26907SThomas Gleixner */ 699ec26907SThomas Gleixner void timerfd_clock_was_set(void) 709ec26907SThomas Gleixner { 719ec26907SThomas Gleixner ktime_t moffs = ktime_get_monotonic_offset(); 729ec26907SThomas Gleixner struct timerfd_ctx *ctx; 739ec26907SThomas Gleixner unsigned long flags; 749ec26907SThomas Gleixner 759ec26907SThomas Gleixner rcu_read_lock(); 769ec26907SThomas Gleixner list_for_each_entry_rcu(ctx, &cancel_list, clist) { 779ec26907SThomas Gleixner if (!ctx->might_cancel) 789ec26907SThomas Gleixner continue; 799ec26907SThomas Gleixner spin_lock_irqsave(&ctx->wqh.lock, flags); 809ec26907SThomas Gleixner if (ctx->moffs.tv64 != moffs.tv64) { 819ec26907SThomas Gleixner ctx->moffs.tv64 = KTIME_MAX; 821123d939SMax Asbock ctx->ticks++; 839ec26907SThomas Gleixner wake_up_locked(&ctx->wqh); 849ec26907SThomas Gleixner } 859ec26907SThomas Gleixner spin_unlock_irqrestore(&ctx->wqh.lock, flags); 869ec26907SThomas Gleixner } 879ec26907SThomas Gleixner rcu_read_unlock(); 889ec26907SThomas Gleixner } 899ec26907SThomas Gleixner 909ec26907SThomas Gleixner static void timerfd_remove_cancel(struct timerfd_ctx *ctx) 919ec26907SThomas Gleixner { 929ec26907SThomas Gleixner if (ctx->might_cancel) { 939ec26907SThomas Gleixner ctx->might_cancel = false; 949ec26907SThomas Gleixner spin_lock(&cancel_lock); 959ec26907SThomas Gleixner list_del_rcu(&ctx->clist); 969ec26907SThomas Gleixner spin_unlock(&cancel_lock); 979ec26907SThomas Gleixner } 989ec26907SThomas Gleixner } 999ec26907SThomas Gleixner 1009ec26907SThomas Gleixner static bool timerfd_canceled(struct timerfd_ctx *ctx) 1019ec26907SThomas Gleixner { 1029ec26907SThomas Gleixner if (!ctx->might_cancel || ctx->moffs.tv64 != KTIME_MAX) 1039ec26907SThomas Gleixner return false; 1049ec26907SThomas Gleixner ctx->moffs = ktime_get_monotonic_offset(); 1059ec26907SThomas Gleixner return true; 1069ec26907SThomas Gleixner } 1079ec26907SThomas Gleixner 1089ec26907SThomas Gleixner static void timerfd_setup_cancel(struct timerfd_ctx *ctx, int flags) 1099ec26907SThomas Gleixner { 1109ec26907SThomas Gleixner if (ctx->clockid == CLOCK_REALTIME && (flags & TFD_TIMER_ABSTIME) && 1119ec26907SThomas Gleixner (flags & TFD_TIMER_CANCEL_ON_SET)) { 1129ec26907SThomas Gleixner if (!ctx->might_cancel) { 1139ec26907SThomas Gleixner ctx->might_cancel = true; 1149ec26907SThomas Gleixner spin_lock(&cancel_lock); 1159ec26907SThomas Gleixner list_add_rcu(&ctx->clist, &cancel_list); 1169ec26907SThomas Gleixner spin_unlock(&cancel_lock); 1179ec26907SThomas Gleixner } 1189ec26907SThomas Gleixner } else if (ctx->might_cancel) { 1199ec26907SThomas Gleixner timerfd_remove_cancel(ctx); 1209ec26907SThomas Gleixner } 1219ec26907SThomas Gleixner } 1229ec26907SThomas Gleixner 1234d672e7aSDavide Libenzi static ktime_t timerfd_get_remaining(struct timerfd_ctx *ctx) 1244d672e7aSDavide Libenzi { 12576369470SArjan van de Ven ktime_t remaining; 1264d672e7aSDavide Libenzi 12776369470SArjan van de Ven remaining = hrtimer_expires_remaining(&ctx->tmr); 1284d672e7aSDavide Libenzi return remaining.tv64 < 0 ? ktime_set(0, 0): remaining; 1294d672e7aSDavide Libenzi } 1304d672e7aSDavide Libenzi 13199ee5315SThomas Gleixner static int timerfd_setup(struct timerfd_ctx *ctx, int flags, 132b215e283SDavide Libenzi const struct itimerspec *ktmr) 133b215e283SDavide Libenzi { 134b215e283SDavide Libenzi enum hrtimer_mode htmode; 135b215e283SDavide Libenzi ktime_t texp; 13699ee5315SThomas Gleixner int clockid = ctx->clockid; 137b215e283SDavide Libenzi 138b215e283SDavide Libenzi htmode = (flags & TFD_TIMER_ABSTIME) ? 139b215e283SDavide Libenzi HRTIMER_MODE_ABS: HRTIMER_MODE_REL; 140b215e283SDavide Libenzi 141b215e283SDavide Libenzi texp = timespec_to_ktime(ktmr->it_value); 142b215e283SDavide Libenzi ctx->expired = 0; 1434d672e7aSDavide Libenzi ctx->ticks = 0; 144b215e283SDavide Libenzi ctx->tintv = timespec_to_ktime(ktmr->it_interval); 14599ee5315SThomas Gleixner hrtimer_init(&ctx->tmr, clockid, htmode); 14676369470SArjan van de Ven hrtimer_set_expires(&ctx->tmr, texp); 147b215e283SDavide Libenzi ctx->tmr.function = timerfd_tmrproc; 14899ee5315SThomas Gleixner if (texp.tv64 != 0) { 149b215e283SDavide Libenzi hrtimer_start(&ctx->tmr, texp, htmode); 15099ee5315SThomas Gleixner if (timerfd_canceled(ctx)) 15199ee5315SThomas Gleixner return -ECANCELED; 15299ee5315SThomas Gleixner } 15399ee5315SThomas Gleixner return 0; 154b215e283SDavide Libenzi } 155b215e283SDavide Libenzi 156b215e283SDavide Libenzi static int timerfd_release(struct inode *inode, struct file *file) 157b215e283SDavide Libenzi { 158b215e283SDavide Libenzi struct timerfd_ctx *ctx = file->private_data; 159b215e283SDavide Libenzi 1609ec26907SThomas Gleixner timerfd_remove_cancel(ctx); 161b215e283SDavide Libenzi hrtimer_cancel(&ctx->tmr); 1629ec26907SThomas Gleixner kfree_rcu(ctx, rcu); 163b215e283SDavide Libenzi return 0; 164b215e283SDavide Libenzi } 165b215e283SDavide Libenzi 166b215e283SDavide Libenzi static unsigned int timerfd_poll(struct file *file, poll_table *wait) 167b215e283SDavide Libenzi { 168b215e283SDavide Libenzi struct timerfd_ctx *ctx = file->private_data; 169b215e283SDavide Libenzi unsigned int events = 0; 170b215e283SDavide Libenzi unsigned long flags; 171b215e283SDavide Libenzi 172b215e283SDavide Libenzi poll_wait(file, &ctx->wqh, wait); 173b215e283SDavide Libenzi 17418963c01SDavide Libenzi spin_lock_irqsave(&ctx->wqh.lock, flags); 1754d672e7aSDavide Libenzi if (ctx->ticks) 176b215e283SDavide Libenzi events |= POLLIN; 17718963c01SDavide Libenzi spin_unlock_irqrestore(&ctx->wqh.lock, flags); 178b215e283SDavide Libenzi 179b215e283SDavide Libenzi return events; 180b215e283SDavide Libenzi } 181b215e283SDavide Libenzi 182b215e283SDavide Libenzi static ssize_t timerfd_read(struct file *file, char __user *buf, size_t count, 183b215e283SDavide Libenzi loff_t *ppos) 184b215e283SDavide Libenzi { 185b215e283SDavide Libenzi struct timerfd_ctx *ctx = file->private_data; 186b215e283SDavide Libenzi ssize_t res; 18709828402SDavide Libenzi u64 ticks = 0; 188b215e283SDavide Libenzi 189b215e283SDavide Libenzi if (count < sizeof(ticks)) 190b215e283SDavide Libenzi return -EINVAL; 19118963c01SDavide Libenzi spin_lock_irq(&ctx->wqh.lock); 1928120a8aaSMichal Nazarewicz if (file->f_flags & O_NONBLOCK) 193b215e283SDavide Libenzi res = -EAGAIN; 1948120a8aaSMichal Nazarewicz else 1958120a8aaSMichal Nazarewicz res = wait_event_interruptible_locked_irq(ctx->wqh, ctx->ticks); 19699ee5315SThomas Gleixner 19799ee5315SThomas Gleixner /* 19899ee5315SThomas Gleixner * If clock has changed, we do not care about the 19999ee5315SThomas Gleixner * ticks and we do not rearm the timer. Userspace must 20099ee5315SThomas Gleixner * reevaluate anyway. 20199ee5315SThomas Gleixner */ 20299ee5315SThomas Gleixner if (timerfd_canceled(ctx)) { 2039ec26907SThomas Gleixner ctx->ticks = 0; 20499ee5315SThomas Gleixner ctx->expired = 0; 20599ee5315SThomas Gleixner res = -ECANCELED; 20699ee5315SThomas Gleixner } 20799ee5315SThomas Gleixner 2089ec26907SThomas Gleixner if (ctx->ticks) { 2099ec26907SThomas Gleixner ticks = ctx->ticks; 2109ec26907SThomas Gleixner 2114d672e7aSDavide Libenzi if (ctx->expired && ctx->tintv.tv64) { 212b215e283SDavide Libenzi /* 213b215e283SDavide Libenzi * If tintv.tv64 != 0, this is a periodic timer that 214b215e283SDavide Libenzi * needs to be re-armed. We avoid doing it in the timer 215b215e283SDavide Libenzi * callback to avoid DoS attacks specifying a very 216b215e283SDavide Libenzi * short timer period. 217b215e283SDavide Libenzi */ 2184d672e7aSDavide Libenzi ticks += hrtimer_forward_now(&ctx->tmr, 2194d672e7aSDavide Libenzi ctx->tintv) - 1; 220b215e283SDavide Libenzi hrtimer_restart(&ctx->tmr); 2214d672e7aSDavide Libenzi } 2224d672e7aSDavide Libenzi ctx->expired = 0; 2234d672e7aSDavide Libenzi ctx->ticks = 0; 224b215e283SDavide Libenzi } 22518963c01SDavide Libenzi spin_unlock_irq(&ctx->wqh.lock); 226b215e283SDavide Libenzi if (ticks) 22709828402SDavide Libenzi res = put_user(ticks, (u64 __user *) buf) ? -EFAULT: sizeof(ticks); 228b215e283SDavide Libenzi return res; 229b215e283SDavide Libenzi } 230b215e283SDavide Libenzi 231b215e283SDavide Libenzi static const struct file_operations timerfd_fops = { 232b215e283SDavide Libenzi .release = timerfd_release, 233b215e283SDavide Libenzi .poll = timerfd_poll, 234b215e283SDavide Libenzi .read = timerfd_read, 2356038f373SArnd Bergmann .llseek = noop_llseek, 236b215e283SDavide Libenzi }; 237b215e283SDavide Libenzi 2382903ff01SAl Viro static int timerfd_fget(int fd, struct fd *p) 239b215e283SDavide Libenzi { 2402903ff01SAl Viro struct fd f = fdget(fd); 2412903ff01SAl Viro if (!f.file) 2422903ff01SAl Viro return -EBADF; 2432903ff01SAl Viro if (f.file->f_op != &timerfd_fops) { 2442903ff01SAl Viro fdput(f); 2452903ff01SAl Viro return -EINVAL; 2464d672e7aSDavide Libenzi } 2472903ff01SAl Viro *p = f; 2482903ff01SAl Viro return 0; 2494d672e7aSDavide Libenzi } 2504d672e7aSDavide Libenzi 251836f92adSHeiko Carstens SYSCALL_DEFINE2(timerfd_create, int, clockid, int, flags) 2524d672e7aSDavide Libenzi { 2532030a42cSAl Viro int ufd; 254b215e283SDavide Libenzi struct timerfd_ctx *ctx; 255b215e283SDavide Libenzi 256e38b36f3SUlrich Drepper /* Check the TFD_* constants for consistency. */ 257e38b36f3SUlrich Drepper BUILD_BUG_ON(TFD_CLOEXEC != O_CLOEXEC); 258e38b36f3SUlrich Drepper BUILD_BUG_ON(TFD_NONBLOCK != O_NONBLOCK); 259e38b36f3SUlrich Drepper 260610d18f4SDavide Libenzi if ((flags & ~TFD_CREATE_FLAGS) || 261610d18f4SDavide Libenzi (clockid != CLOCK_MONOTONIC && 262610d18f4SDavide Libenzi clockid != CLOCK_REALTIME)) 263b215e283SDavide Libenzi return -EINVAL; 264b215e283SDavide Libenzi 2654d672e7aSDavide Libenzi ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); 266b215e283SDavide Libenzi if (!ctx) 267b215e283SDavide Libenzi return -ENOMEM; 268b215e283SDavide Libenzi 269b215e283SDavide Libenzi init_waitqueue_head(&ctx->wqh); 2704d672e7aSDavide Libenzi ctx->clockid = clockid; 2714d672e7aSDavide Libenzi hrtimer_init(&ctx->tmr, clockid, HRTIMER_MODE_ABS); 27299ee5315SThomas Gleixner ctx->moffs = ktime_get_monotonic_offset(); 273b215e283SDavide Libenzi 27411fcb6c1SUlrich Drepper ufd = anon_inode_getfd("[timerfd]", &timerfd_fops, ctx, 275628ff7c1SRoland Dreier O_RDWR | (flags & TFD_SHARED_FCNTL_FLAGS)); 2762030a42cSAl Viro if (ufd < 0) 2774d672e7aSDavide Libenzi kfree(ctx); 2784d672e7aSDavide Libenzi 2794d672e7aSDavide Libenzi return ufd; 2804d672e7aSDavide Libenzi } 2814d672e7aSDavide Libenzi 2829d94b9e2SAl Viro static int do_timerfd_settime(int ufd, int flags, 2839d94b9e2SAl Viro const struct itimerspec *new, 2849d94b9e2SAl Viro struct itimerspec *old) 2854d672e7aSDavide Libenzi { 2862903ff01SAl Viro struct fd f; 2874d672e7aSDavide Libenzi struct timerfd_ctx *ctx; 2882903ff01SAl Viro int ret; 2894d672e7aSDavide Libenzi 290610d18f4SDavide Libenzi if ((flags & ~TFD_SETTIME_FLAGS) || 2919d94b9e2SAl Viro !timespec_valid(&new->it_value) || 2929d94b9e2SAl Viro !timespec_valid(&new->it_interval)) 2934d672e7aSDavide Libenzi return -EINVAL; 2944d672e7aSDavide Libenzi 2952903ff01SAl Viro ret = timerfd_fget(ufd, &f); 2962903ff01SAl Viro if (ret) 2972903ff01SAl Viro return ret; 2982903ff01SAl Viro ctx = f.file->private_data; 2994d672e7aSDavide Libenzi 3009ec26907SThomas Gleixner timerfd_setup_cancel(ctx, flags); 3019ec26907SThomas Gleixner 302b215e283SDavide Libenzi /* 303b215e283SDavide Libenzi * We need to stop the existing timer before reprogramming 304b215e283SDavide Libenzi * it to the new values. 305b215e283SDavide Libenzi */ 306b215e283SDavide Libenzi for (;;) { 30718963c01SDavide Libenzi spin_lock_irq(&ctx->wqh.lock); 308b215e283SDavide Libenzi if (hrtimer_try_to_cancel(&ctx->tmr) >= 0) 309b215e283SDavide Libenzi break; 31018963c01SDavide Libenzi spin_unlock_irq(&ctx->wqh.lock); 311b215e283SDavide Libenzi cpu_relax(); 312b215e283SDavide Libenzi } 3134d672e7aSDavide Libenzi 3144d672e7aSDavide Libenzi /* 3154d672e7aSDavide Libenzi * If the timer is expired and it's periodic, we need to advance it 3164d672e7aSDavide Libenzi * because the caller may want to know the previous expiration time. 3174d672e7aSDavide Libenzi * We do not update "ticks" and "expired" since the timer will be 3184d672e7aSDavide Libenzi * re-programmed again in the following timerfd_setup() call. 3194d672e7aSDavide Libenzi */ 3204d672e7aSDavide Libenzi if (ctx->expired && ctx->tintv.tv64) 3214d672e7aSDavide Libenzi hrtimer_forward_now(&ctx->tmr, ctx->tintv); 3224d672e7aSDavide Libenzi 3239d94b9e2SAl Viro old->it_value = ktime_to_timespec(timerfd_get_remaining(ctx)); 3249d94b9e2SAl Viro old->it_interval = ktime_to_timespec(ctx->tintv); 3254d672e7aSDavide Libenzi 326b215e283SDavide Libenzi /* 327b215e283SDavide Libenzi * Re-program the timer to the new value ... 328b215e283SDavide Libenzi */ 3299d94b9e2SAl Viro ret = timerfd_setup(ctx, flags, new); 330b215e283SDavide Libenzi 33118963c01SDavide Libenzi spin_unlock_irq(&ctx->wqh.lock); 3322903ff01SAl Viro fdput(f); 33399ee5315SThomas Gleixner return ret; 334b215e283SDavide Libenzi } 335b215e283SDavide Libenzi 3369d94b9e2SAl Viro static int do_timerfd_gettime(int ufd, struct itimerspec *t) 3374d672e7aSDavide Libenzi { 3382903ff01SAl Viro struct fd f; 3394d672e7aSDavide Libenzi struct timerfd_ctx *ctx; 3402903ff01SAl Viro int ret = timerfd_fget(ufd, &f); 3412903ff01SAl Viro if (ret) 3422903ff01SAl Viro return ret; 3432903ff01SAl Viro ctx = f.file->private_data; 3444d672e7aSDavide Libenzi 3454d672e7aSDavide Libenzi spin_lock_irq(&ctx->wqh.lock); 3464d672e7aSDavide Libenzi if (ctx->expired && ctx->tintv.tv64) { 3474d672e7aSDavide Libenzi ctx->expired = 0; 3484d672e7aSDavide Libenzi ctx->ticks += 3494d672e7aSDavide Libenzi hrtimer_forward_now(&ctx->tmr, ctx->tintv) - 1; 3504d672e7aSDavide Libenzi hrtimer_restart(&ctx->tmr); 3514d672e7aSDavide Libenzi } 3529d94b9e2SAl Viro t->it_value = ktime_to_timespec(timerfd_get_remaining(ctx)); 3539d94b9e2SAl Viro t->it_interval = ktime_to_timespec(ctx->tintv); 3544d672e7aSDavide Libenzi spin_unlock_irq(&ctx->wqh.lock); 3552903ff01SAl Viro fdput(f); 3569d94b9e2SAl Viro return 0; 3579d94b9e2SAl Viro } 3584d672e7aSDavide Libenzi 3599d94b9e2SAl Viro SYSCALL_DEFINE4(timerfd_settime, int, ufd, int, flags, 3609d94b9e2SAl Viro const struct itimerspec __user *, utmr, 3619d94b9e2SAl Viro struct itimerspec __user *, otmr) 3629d94b9e2SAl Viro { 3639d94b9e2SAl Viro struct itimerspec new, old; 3649d94b9e2SAl Viro int ret; 3659d94b9e2SAl Viro 3669d94b9e2SAl Viro if (copy_from_user(&new, utmr, sizeof(new))) 3679d94b9e2SAl Viro return -EFAULT; 3689d94b9e2SAl Viro ret = do_timerfd_settime(ufd, flags, &new, &old); 3699d94b9e2SAl Viro if (ret) 3709d94b9e2SAl Viro return ret; 3719d94b9e2SAl Viro if (otmr && copy_to_user(otmr, &old, sizeof(old))) 3729d94b9e2SAl Viro return -EFAULT; 3739d94b9e2SAl Viro 3749d94b9e2SAl Viro return ret; 3759d94b9e2SAl Viro } 3769d94b9e2SAl Viro 3779d94b9e2SAl Viro SYSCALL_DEFINE2(timerfd_gettime, int, ufd, struct itimerspec __user *, otmr) 3789d94b9e2SAl Viro { 3799d94b9e2SAl Viro struct itimerspec kotmr; 3809d94b9e2SAl Viro int ret = do_timerfd_gettime(ufd, &kotmr); 3819d94b9e2SAl Viro if (ret) 3829d94b9e2SAl Viro return ret; 3834d672e7aSDavide Libenzi return copy_to_user(otmr, &kotmr, sizeof(kotmr)) ? -EFAULT: 0; 384b215e283SDavide Libenzi } 385b215e283SDavide Libenzi 386*0e803bafSHeiko Carstens #ifdef CONFIG_COMPAT 3879d94b9e2SAl Viro COMPAT_SYSCALL_DEFINE4(timerfd_settime, int, ufd, int, flags, 388*0e803bafSHeiko Carstens const struct compat_itimerspec __user *, utmr, 389*0e803bafSHeiko Carstens struct compat_itimerspec __user *, otmr) 3909d94b9e2SAl Viro { 3919d94b9e2SAl Viro struct itimerspec new, old; 3929d94b9e2SAl Viro int ret; 3939d94b9e2SAl Viro 3949d94b9e2SAl Viro if (get_compat_itimerspec(&new, utmr)) 3959d94b9e2SAl Viro return -EFAULT; 3969d94b9e2SAl Viro ret = do_timerfd_settime(ufd, flags, &new, &old); 3979d94b9e2SAl Viro if (ret) 3989d94b9e2SAl Viro return ret; 3999d94b9e2SAl Viro if (otmr && put_compat_itimerspec(otmr, &old)) 4009d94b9e2SAl Viro return -EFAULT; 4019d94b9e2SAl Viro return ret; 4029d94b9e2SAl Viro } 4039d94b9e2SAl Viro 4049d94b9e2SAl Viro COMPAT_SYSCALL_DEFINE2(timerfd_gettime, int, ufd, 405*0e803bafSHeiko Carstens struct compat_itimerspec __user *, otmr) 4069d94b9e2SAl Viro { 4079d94b9e2SAl Viro struct itimerspec kotmr; 4089d94b9e2SAl Viro int ret = do_timerfd_gettime(ufd, &kotmr); 4099d94b9e2SAl Viro if (ret) 4109d94b9e2SAl Viro return ret; 411*0e803bafSHeiko Carstens return put_compat_itimerspec(otmr, &kotmr) ? -EFAULT: 0; 4129d94b9e2SAl Viro } 4139d94b9e2SAl Viro #endif 414