18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * fs/timerfd.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2007 Davide Libenzi <davidel@xmailserver.org> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Thanks to Thomas Gleixner for code reviews and useful comments. 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/alarmtimer.h> 138c2ecf20Sopenharmony_ci#include <linux/file.h> 148c2ecf20Sopenharmony_ci#include <linux/poll.h> 158c2ecf20Sopenharmony_ci#include <linux/init.h> 168c2ecf20Sopenharmony_ci#include <linux/fs.h> 178c2ecf20Sopenharmony_ci#include <linux/sched.h> 188c2ecf20Sopenharmony_ci#include <linux/kernel.h> 198c2ecf20Sopenharmony_ci#include <linux/slab.h> 208c2ecf20Sopenharmony_ci#include <linux/list.h> 218c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 228c2ecf20Sopenharmony_ci#include <linux/time.h> 238c2ecf20Sopenharmony_ci#include <linux/hrtimer.h> 248c2ecf20Sopenharmony_ci#include <linux/anon_inodes.h> 258c2ecf20Sopenharmony_ci#include <linux/timerfd.h> 268c2ecf20Sopenharmony_ci#include <linux/syscalls.h> 278c2ecf20Sopenharmony_ci#include <linux/compat.h> 288c2ecf20Sopenharmony_ci#include <linux/rcupdate.h> 298c2ecf20Sopenharmony_ci#include <linux/time_namespace.h> 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistruct timerfd_ctx { 328c2ecf20Sopenharmony_ci union { 338c2ecf20Sopenharmony_ci struct hrtimer tmr; 348c2ecf20Sopenharmony_ci struct alarm alarm; 358c2ecf20Sopenharmony_ci } t; 368c2ecf20Sopenharmony_ci ktime_t tintv; 378c2ecf20Sopenharmony_ci ktime_t moffs; 388c2ecf20Sopenharmony_ci wait_queue_head_t wqh; 398c2ecf20Sopenharmony_ci u64 ticks; 408c2ecf20Sopenharmony_ci int clockid; 418c2ecf20Sopenharmony_ci short unsigned expired; 428c2ecf20Sopenharmony_ci short unsigned settime_flags; /* to show in fdinfo */ 438c2ecf20Sopenharmony_ci struct rcu_head rcu; 448c2ecf20Sopenharmony_ci struct list_head clist; 458c2ecf20Sopenharmony_ci spinlock_t cancel_lock; 468c2ecf20Sopenharmony_ci bool might_cancel; 478c2ecf20Sopenharmony_ci}; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic LIST_HEAD(cancel_list); 508c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(cancel_lock); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic inline bool isalarm(struct timerfd_ctx *ctx) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci return ctx->clockid == CLOCK_REALTIME_ALARM || 558c2ecf20Sopenharmony_ci ctx->clockid == CLOCK_BOOTTIME_ALARM; 568c2ecf20Sopenharmony_ci} 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci/* 598c2ecf20Sopenharmony_ci * This gets called when the timer event triggers. We set the "expired" 608c2ecf20Sopenharmony_ci * flag, but we do not re-arm the timer (in case it's necessary, 618c2ecf20Sopenharmony_ci * tintv != 0) until the timer is accessed. 628c2ecf20Sopenharmony_ci */ 638c2ecf20Sopenharmony_cistatic void timerfd_triggered(struct timerfd_ctx *ctx) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci unsigned long flags; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci spin_lock_irqsave(&ctx->wqh.lock, flags); 688c2ecf20Sopenharmony_ci ctx->expired = 1; 698c2ecf20Sopenharmony_ci ctx->ticks++; 708c2ecf20Sopenharmony_ci wake_up_locked_poll(&ctx->wqh, EPOLLIN); 718c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ctx->wqh.lock, flags); 728c2ecf20Sopenharmony_ci} 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic enum hrtimer_restart timerfd_tmrproc(struct hrtimer *htmr) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci struct timerfd_ctx *ctx = container_of(htmr, struct timerfd_ctx, 778c2ecf20Sopenharmony_ci t.tmr); 788c2ecf20Sopenharmony_ci timerfd_triggered(ctx); 798c2ecf20Sopenharmony_ci return HRTIMER_NORESTART; 808c2ecf20Sopenharmony_ci} 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic enum alarmtimer_restart timerfd_alarmproc(struct alarm *alarm, 838c2ecf20Sopenharmony_ci ktime_t now) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci struct timerfd_ctx *ctx = container_of(alarm, struct timerfd_ctx, 868c2ecf20Sopenharmony_ci t.alarm); 878c2ecf20Sopenharmony_ci timerfd_triggered(ctx); 888c2ecf20Sopenharmony_ci return ALARMTIMER_NORESTART; 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci/* 928c2ecf20Sopenharmony_ci * Called when the clock was set to cancel the timers in the cancel 938c2ecf20Sopenharmony_ci * list. This will wake up processes waiting on these timers. The 948c2ecf20Sopenharmony_ci * wake-up requires ctx->ticks to be non zero, therefore we increment 958c2ecf20Sopenharmony_ci * it before calling wake_up_locked(). 968c2ecf20Sopenharmony_ci */ 978c2ecf20Sopenharmony_civoid timerfd_clock_was_set(void) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci ktime_t moffs = ktime_mono_to_real(0); 1008c2ecf20Sopenharmony_ci struct timerfd_ctx *ctx; 1018c2ecf20Sopenharmony_ci unsigned long flags; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci rcu_read_lock(); 1048c2ecf20Sopenharmony_ci list_for_each_entry_rcu(ctx, &cancel_list, clist) { 1058c2ecf20Sopenharmony_ci if (!ctx->might_cancel) 1068c2ecf20Sopenharmony_ci continue; 1078c2ecf20Sopenharmony_ci spin_lock_irqsave(&ctx->wqh.lock, flags); 1088c2ecf20Sopenharmony_ci if (ctx->moffs != moffs) { 1098c2ecf20Sopenharmony_ci ctx->moffs = KTIME_MAX; 1108c2ecf20Sopenharmony_ci ctx->ticks++; 1118c2ecf20Sopenharmony_ci wake_up_locked_poll(&ctx->wqh, EPOLLIN); 1128c2ecf20Sopenharmony_ci } 1138c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ctx->wqh.lock, flags); 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci rcu_read_unlock(); 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic void __timerfd_remove_cancel(struct timerfd_ctx *ctx) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci if (ctx->might_cancel) { 1218c2ecf20Sopenharmony_ci ctx->might_cancel = false; 1228c2ecf20Sopenharmony_ci spin_lock(&cancel_lock); 1238c2ecf20Sopenharmony_ci list_del_rcu(&ctx->clist); 1248c2ecf20Sopenharmony_ci spin_unlock(&cancel_lock); 1258c2ecf20Sopenharmony_ci } 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic void timerfd_remove_cancel(struct timerfd_ctx *ctx) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci spin_lock(&ctx->cancel_lock); 1318c2ecf20Sopenharmony_ci __timerfd_remove_cancel(ctx); 1328c2ecf20Sopenharmony_ci spin_unlock(&ctx->cancel_lock); 1338c2ecf20Sopenharmony_ci} 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_cistatic bool timerfd_canceled(struct timerfd_ctx *ctx) 1368c2ecf20Sopenharmony_ci{ 1378c2ecf20Sopenharmony_ci if (!ctx->might_cancel || ctx->moffs != KTIME_MAX) 1388c2ecf20Sopenharmony_ci return false; 1398c2ecf20Sopenharmony_ci ctx->moffs = ktime_mono_to_real(0); 1408c2ecf20Sopenharmony_ci return true; 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistatic void timerfd_setup_cancel(struct timerfd_ctx *ctx, int flags) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci spin_lock(&ctx->cancel_lock); 1468c2ecf20Sopenharmony_ci if ((ctx->clockid == CLOCK_REALTIME || 1478c2ecf20Sopenharmony_ci ctx->clockid == CLOCK_REALTIME_ALARM) && 1488c2ecf20Sopenharmony_ci (flags & TFD_TIMER_ABSTIME) && (flags & TFD_TIMER_CANCEL_ON_SET)) { 1498c2ecf20Sopenharmony_ci if (!ctx->might_cancel) { 1508c2ecf20Sopenharmony_ci ctx->might_cancel = true; 1518c2ecf20Sopenharmony_ci spin_lock(&cancel_lock); 1528c2ecf20Sopenharmony_ci list_add_rcu(&ctx->clist, &cancel_list); 1538c2ecf20Sopenharmony_ci spin_unlock(&cancel_lock); 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci } else { 1568c2ecf20Sopenharmony_ci __timerfd_remove_cancel(ctx); 1578c2ecf20Sopenharmony_ci } 1588c2ecf20Sopenharmony_ci spin_unlock(&ctx->cancel_lock); 1598c2ecf20Sopenharmony_ci} 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cistatic ktime_t timerfd_get_remaining(struct timerfd_ctx *ctx) 1628c2ecf20Sopenharmony_ci{ 1638c2ecf20Sopenharmony_ci ktime_t remaining; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci if (isalarm(ctx)) 1668c2ecf20Sopenharmony_ci remaining = alarm_expires_remaining(&ctx->t.alarm); 1678c2ecf20Sopenharmony_ci else 1688c2ecf20Sopenharmony_ci remaining = hrtimer_expires_remaining_adjusted(&ctx->t.tmr); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci return remaining < 0 ? 0: remaining; 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistatic int timerfd_setup(struct timerfd_ctx *ctx, int flags, 1748c2ecf20Sopenharmony_ci const struct itimerspec64 *ktmr) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci enum hrtimer_mode htmode; 1778c2ecf20Sopenharmony_ci ktime_t texp; 1788c2ecf20Sopenharmony_ci int clockid = ctx->clockid; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci htmode = (flags & TFD_TIMER_ABSTIME) ? 1818c2ecf20Sopenharmony_ci HRTIMER_MODE_ABS: HRTIMER_MODE_REL; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci texp = timespec64_to_ktime(ktmr->it_value); 1848c2ecf20Sopenharmony_ci ctx->expired = 0; 1858c2ecf20Sopenharmony_ci ctx->ticks = 0; 1868c2ecf20Sopenharmony_ci ctx->tintv = timespec64_to_ktime(ktmr->it_interval); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci if (isalarm(ctx)) { 1898c2ecf20Sopenharmony_ci alarm_init(&ctx->t.alarm, 1908c2ecf20Sopenharmony_ci ctx->clockid == CLOCK_REALTIME_ALARM ? 1918c2ecf20Sopenharmony_ci ALARM_REALTIME : ALARM_BOOTTIME, 1928c2ecf20Sopenharmony_ci timerfd_alarmproc); 1938c2ecf20Sopenharmony_ci } else { 1948c2ecf20Sopenharmony_ci hrtimer_init(&ctx->t.tmr, clockid, htmode); 1958c2ecf20Sopenharmony_ci hrtimer_set_expires(&ctx->t.tmr, texp); 1968c2ecf20Sopenharmony_ci ctx->t.tmr.function = timerfd_tmrproc; 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci if (texp != 0) { 2008c2ecf20Sopenharmony_ci if (flags & TFD_TIMER_ABSTIME) 2018c2ecf20Sopenharmony_ci texp = timens_ktime_to_host(clockid, texp); 2028c2ecf20Sopenharmony_ci if (isalarm(ctx)) { 2038c2ecf20Sopenharmony_ci if (flags & TFD_TIMER_ABSTIME) 2048c2ecf20Sopenharmony_ci alarm_start(&ctx->t.alarm, texp); 2058c2ecf20Sopenharmony_ci else 2068c2ecf20Sopenharmony_ci alarm_start_relative(&ctx->t.alarm, texp); 2078c2ecf20Sopenharmony_ci } else { 2088c2ecf20Sopenharmony_ci hrtimer_start(&ctx->t.tmr, texp, htmode); 2098c2ecf20Sopenharmony_ci } 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci if (timerfd_canceled(ctx)) 2128c2ecf20Sopenharmony_ci return -ECANCELED; 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci ctx->settime_flags = flags & TFD_SETTIME_FLAGS; 2168c2ecf20Sopenharmony_ci return 0; 2178c2ecf20Sopenharmony_ci} 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cistatic int timerfd_release(struct inode *inode, struct file *file) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci struct timerfd_ctx *ctx = file->private_data; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci timerfd_remove_cancel(ctx); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci if (isalarm(ctx)) 2268c2ecf20Sopenharmony_ci alarm_cancel(&ctx->t.alarm); 2278c2ecf20Sopenharmony_ci else 2288c2ecf20Sopenharmony_ci hrtimer_cancel(&ctx->t.tmr); 2298c2ecf20Sopenharmony_ci kfree_rcu(ctx, rcu); 2308c2ecf20Sopenharmony_ci return 0; 2318c2ecf20Sopenharmony_ci} 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_cistatic __poll_t timerfd_poll(struct file *file, poll_table *wait) 2348c2ecf20Sopenharmony_ci{ 2358c2ecf20Sopenharmony_ci struct timerfd_ctx *ctx = file->private_data; 2368c2ecf20Sopenharmony_ci __poll_t events = 0; 2378c2ecf20Sopenharmony_ci unsigned long flags; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci poll_wait(file, &ctx->wqh, wait); 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci spin_lock_irqsave(&ctx->wqh.lock, flags); 2428c2ecf20Sopenharmony_ci if (ctx->ticks) 2438c2ecf20Sopenharmony_ci events |= EPOLLIN; 2448c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ctx->wqh.lock, flags); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci return events; 2478c2ecf20Sopenharmony_ci} 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_cistatic ssize_t timerfd_read(struct file *file, char __user *buf, size_t count, 2508c2ecf20Sopenharmony_ci loff_t *ppos) 2518c2ecf20Sopenharmony_ci{ 2528c2ecf20Sopenharmony_ci struct timerfd_ctx *ctx = file->private_data; 2538c2ecf20Sopenharmony_ci ssize_t res; 2548c2ecf20Sopenharmony_ci u64 ticks = 0; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci if (count < sizeof(ticks)) 2578c2ecf20Sopenharmony_ci return -EINVAL; 2588c2ecf20Sopenharmony_ci spin_lock_irq(&ctx->wqh.lock); 2598c2ecf20Sopenharmony_ci if (file->f_flags & O_NONBLOCK) 2608c2ecf20Sopenharmony_ci res = -EAGAIN; 2618c2ecf20Sopenharmony_ci else 2628c2ecf20Sopenharmony_ci res = wait_event_interruptible_locked_irq(ctx->wqh, ctx->ticks); 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci /* 2658c2ecf20Sopenharmony_ci * If clock has changed, we do not care about the 2668c2ecf20Sopenharmony_ci * ticks and we do not rearm the timer. Userspace must 2678c2ecf20Sopenharmony_ci * reevaluate anyway. 2688c2ecf20Sopenharmony_ci */ 2698c2ecf20Sopenharmony_ci if (timerfd_canceled(ctx)) { 2708c2ecf20Sopenharmony_ci ctx->ticks = 0; 2718c2ecf20Sopenharmony_ci ctx->expired = 0; 2728c2ecf20Sopenharmony_ci res = -ECANCELED; 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci if (ctx->ticks) { 2768c2ecf20Sopenharmony_ci ticks = ctx->ticks; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci if (ctx->expired && ctx->tintv) { 2798c2ecf20Sopenharmony_ci /* 2808c2ecf20Sopenharmony_ci * If tintv != 0, this is a periodic timer that 2818c2ecf20Sopenharmony_ci * needs to be re-armed. We avoid doing it in the timer 2828c2ecf20Sopenharmony_ci * callback to avoid DoS attacks specifying a very 2838c2ecf20Sopenharmony_ci * short timer period. 2848c2ecf20Sopenharmony_ci */ 2858c2ecf20Sopenharmony_ci if (isalarm(ctx)) { 2868c2ecf20Sopenharmony_ci ticks += alarm_forward_now( 2878c2ecf20Sopenharmony_ci &ctx->t.alarm, ctx->tintv) - 1; 2888c2ecf20Sopenharmony_ci alarm_restart(&ctx->t.alarm); 2898c2ecf20Sopenharmony_ci } else { 2908c2ecf20Sopenharmony_ci ticks += hrtimer_forward_now(&ctx->t.tmr, 2918c2ecf20Sopenharmony_ci ctx->tintv) - 1; 2928c2ecf20Sopenharmony_ci hrtimer_restart(&ctx->t.tmr); 2938c2ecf20Sopenharmony_ci } 2948c2ecf20Sopenharmony_ci } 2958c2ecf20Sopenharmony_ci ctx->expired = 0; 2968c2ecf20Sopenharmony_ci ctx->ticks = 0; 2978c2ecf20Sopenharmony_ci } 2988c2ecf20Sopenharmony_ci spin_unlock_irq(&ctx->wqh.lock); 2998c2ecf20Sopenharmony_ci if (ticks) 3008c2ecf20Sopenharmony_ci res = put_user(ticks, (u64 __user *) buf) ? -EFAULT: sizeof(ticks); 3018c2ecf20Sopenharmony_ci return res; 3028c2ecf20Sopenharmony_ci} 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci#ifdef CONFIG_PROC_FS 3058c2ecf20Sopenharmony_cistatic void timerfd_show(struct seq_file *m, struct file *file) 3068c2ecf20Sopenharmony_ci{ 3078c2ecf20Sopenharmony_ci struct timerfd_ctx *ctx = file->private_data; 3088c2ecf20Sopenharmony_ci struct timespec64 value, interval; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci spin_lock_irq(&ctx->wqh.lock); 3118c2ecf20Sopenharmony_ci value = ktime_to_timespec64(timerfd_get_remaining(ctx)); 3128c2ecf20Sopenharmony_ci interval = ktime_to_timespec64(ctx->tintv); 3138c2ecf20Sopenharmony_ci spin_unlock_irq(&ctx->wqh.lock); 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci seq_printf(m, 3168c2ecf20Sopenharmony_ci "clockid: %d\n" 3178c2ecf20Sopenharmony_ci "ticks: %llu\n" 3188c2ecf20Sopenharmony_ci "settime flags: 0%o\n" 3198c2ecf20Sopenharmony_ci "it_value: (%llu, %llu)\n" 3208c2ecf20Sopenharmony_ci "it_interval: (%llu, %llu)\n", 3218c2ecf20Sopenharmony_ci ctx->clockid, 3228c2ecf20Sopenharmony_ci (unsigned long long)ctx->ticks, 3238c2ecf20Sopenharmony_ci ctx->settime_flags, 3248c2ecf20Sopenharmony_ci (unsigned long long)value.tv_sec, 3258c2ecf20Sopenharmony_ci (unsigned long long)value.tv_nsec, 3268c2ecf20Sopenharmony_ci (unsigned long long)interval.tv_sec, 3278c2ecf20Sopenharmony_ci (unsigned long long)interval.tv_nsec); 3288c2ecf20Sopenharmony_ci} 3298c2ecf20Sopenharmony_ci#else 3308c2ecf20Sopenharmony_ci#define timerfd_show NULL 3318c2ecf20Sopenharmony_ci#endif 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci#ifdef CONFIG_CHECKPOINT_RESTORE 3348c2ecf20Sopenharmony_cistatic long timerfd_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 3358c2ecf20Sopenharmony_ci{ 3368c2ecf20Sopenharmony_ci struct timerfd_ctx *ctx = file->private_data; 3378c2ecf20Sopenharmony_ci int ret = 0; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci switch (cmd) { 3408c2ecf20Sopenharmony_ci case TFD_IOC_SET_TICKS: { 3418c2ecf20Sopenharmony_ci u64 ticks; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci if (copy_from_user(&ticks, (u64 __user *)arg, sizeof(ticks))) 3448c2ecf20Sopenharmony_ci return -EFAULT; 3458c2ecf20Sopenharmony_ci if (!ticks) 3468c2ecf20Sopenharmony_ci return -EINVAL; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci spin_lock_irq(&ctx->wqh.lock); 3498c2ecf20Sopenharmony_ci if (!timerfd_canceled(ctx)) { 3508c2ecf20Sopenharmony_ci ctx->ticks = ticks; 3518c2ecf20Sopenharmony_ci wake_up_locked_poll(&ctx->wqh, EPOLLIN); 3528c2ecf20Sopenharmony_ci } else 3538c2ecf20Sopenharmony_ci ret = -ECANCELED; 3548c2ecf20Sopenharmony_ci spin_unlock_irq(&ctx->wqh.lock); 3558c2ecf20Sopenharmony_ci break; 3568c2ecf20Sopenharmony_ci } 3578c2ecf20Sopenharmony_ci default: 3588c2ecf20Sopenharmony_ci ret = -ENOTTY; 3598c2ecf20Sopenharmony_ci break; 3608c2ecf20Sopenharmony_ci } 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci return ret; 3638c2ecf20Sopenharmony_ci} 3648c2ecf20Sopenharmony_ci#else 3658c2ecf20Sopenharmony_ci#define timerfd_ioctl NULL 3668c2ecf20Sopenharmony_ci#endif 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_cistatic const struct file_operations timerfd_fops = { 3698c2ecf20Sopenharmony_ci .release = timerfd_release, 3708c2ecf20Sopenharmony_ci .poll = timerfd_poll, 3718c2ecf20Sopenharmony_ci .read = timerfd_read, 3728c2ecf20Sopenharmony_ci .llseek = noop_llseek, 3738c2ecf20Sopenharmony_ci .show_fdinfo = timerfd_show, 3748c2ecf20Sopenharmony_ci .unlocked_ioctl = timerfd_ioctl, 3758c2ecf20Sopenharmony_ci}; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_cistatic int timerfd_fget(int fd, struct fd *p) 3788c2ecf20Sopenharmony_ci{ 3798c2ecf20Sopenharmony_ci struct fd f = fdget(fd); 3808c2ecf20Sopenharmony_ci if (!f.file) 3818c2ecf20Sopenharmony_ci return -EBADF; 3828c2ecf20Sopenharmony_ci if (f.file->f_op != &timerfd_fops) { 3838c2ecf20Sopenharmony_ci fdput(f); 3848c2ecf20Sopenharmony_ci return -EINVAL; 3858c2ecf20Sopenharmony_ci } 3868c2ecf20Sopenharmony_ci *p = f; 3878c2ecf20Sopenharmony_ci return 0; 3888c2ecf20Sopenharmony_ci} 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ciSYSCALL_DEFINE2(timerfd_create, int, clockid, int, flags) 3918c2ecf20Sopenharmony_ci{ 3928c2ecf20Sopenharmony_ci int ufd; 3938c2ecf20Sopenharmony_ci struct timerfd_ctx *ctx; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci /* Check the TFD_* constants for consistency. */ 3968c2ecf20Sopenharmony_ci BUILD_BUG_ON(TFD_CLOEXEC != O_CLOEXEC); 3978c2ecf20Sopenharmony_ci BUILD_BUG_ON(TFD_NONBLOCK != O_NONBLOCK); 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci if ((flags & ~TFD_CREATE_FLAGS) || 4008c2ecf20Sopenharmony_ci (clockid != CLOCK_MONOTONIC && 4018c2ecf20Sopenharmony_ci clockid != CLOCK_REALTIME && 4028c2ecf20Sopenharmony_ci clockid != CLOCK_REALTIME_ALARM && 4038c2ecf20Sopenharmony_ci clockid != CLOCK_BOOTTIME && 4048c2ecf20Sopenharmony_ci clockid != CLOCK_BOOTTIME_ALARM)) 4058c2ecf20Sopenharmony_ci return -EINVAL; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci if ((clockid == CLOCK_REALTIME_ALARM || 4088c2ecf20Sopenharmony_ci clockid == CLOCK_BOOTTIME_ALARM) && 4098c2ecf20Sopenharmony_ci !capable(CAP_WAKE_ALARM)) 4108c2ecf20Sopenharmony_ci return -EPERM; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); 4138c2ecf20Sopenharmony_ci if (!ctx) 4148c2ecf20Sopenharmony_ci return -ENOMEM; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci init_waitqueue_head(&ctx->wqh); 4178c2ecf20Sopenharmony_ci spin_lock_init(&ctx->cancel_lock); 4188c2ecf20Sopenharmony_ci ctx->clockid = clockid; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci if (isalarm(ctx)) 4218c2ecf20Sopenharmony_ci alarm_init(&ctx->t.alarm, 4228c2ecf20Sopenharmony_ci ctx->clockid == CLOCK_REALTIME_ALARM ? 4238c2ecf20Sopenharmony_ci ALARM_REALTIME : ALARM_BOOTTIME, 4248c2ecf20Sopenharmony_ci timerfd_alarmproc); 4258c2ecf20Sopenharmony_ci else 4268c2ecf20Sopenharmony_ci hrtimer_init(&ctx->t.tmr, clockid, HRTIMER_MODE_ABS); 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci ctx->moffs = ktime_mono_to_real(0); 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci ufd = anon_inode_getfd("[timerfd]", &timerfd_fops, ctx, 4318c2ecf20Sopenharmony_ci O_RDWR | (flags & TFD_SHARED_FCNTL_FLAGS)); 4328c2ecf20Sopenharmony_ci if (ufd < 0) 4338c2ecf20Sopenharmony_ci kfree(ctx); 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci return ufd; 4368c2ecf20Sopenharmony_ci} 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_cistatic int do_timerfd_settime(int ufd, int flags, 4398c2ecf20Sopenharmony_ci const struct itimerspec64 *new, 4408c2ecf20Sopenharmony_ci struct itimerspec64 *old) 4418c2ecf20Sopenharmony_ci{ 4428c2ecf20Sopenharmony_ci struct fd f; 4438c2ecf20Sopenharmony_ci struct timerfd_ctx *ctx; 4448c2ecf20Sopenharmony_ci int ret; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci if ((flags & ~TFD_SETTIME_FLAGS) || 4478c2ecf20Sopenharmony_ci !itimerspec64_valid(new)) 4488c2ecf20Sopenharmony_ci return -EINVAL; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci ret = timerfd_fget(ufd, &f); 4518c2ecf20Sopenharmony_ci if (ret) 4528c2ecf20Sopenharmony_ci return ret; 4538c2ecf20Sopenharmony_ci ctx = f.file->private_data; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci if (isalarm(ctx) && !capable(CAP_WAKE_ALARM)) { 4568c2ecf20Sopenharmony_ci fdput(f); 4578c2ecf20Sopenharmony_ci return -EPERM; 4588c2ecf20Sopenharmony_ci } 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci timerfd_setup_cancel(ctx, flags); 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci /* 4638c2ecf20Sopenharmony_ci * We need to stop the existing timer before reprogramming 4648c2ecf20Sopenharmony_ci * it to the new values. 4658c2ecf20Sopenharmony_ci */ 4668c2ecf20Sopenharmony_ci for (;;) { 4678c2ecf20Sopenharmony_ci spin_lock_irq(&ctx->wqh.lock); 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci if (isalarm(ctx)) { 4708c2ecf20Sopenharmony_ci if (alarm_try_to_cancel(&ctx->t.alarm) >= 0) 4718c2ecf20Sopenharmony_ci break; 4728c2ecf20Sopenharmony_ci } else { 4738c2ecf20Sopenharmony_ci if (hrtimer_try_to_cancel(&ctx->t.tmr) >= 0) 4748c2ecf20Sopenharmony_ci break; 4758c2ecf20Sopenharmony_ci } 4768c2ecf20Sopenharmony_ci spin_unlock_irq(&ctx->wqh.lock); 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci if (isalarm(ctx)) 4798c2ecf20Sopenharmony_ci hrtimer_cancel_wait_running(&ctx->t.alarm.timer); 4808c2ecf20Sopenharmony_ci else 4818c2ecf20Sopenharmony_ci hrtimer_cancel_wait_running(&ctx->t.tmr); 4828c2ecf20Sopenharmony_ci } 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci /* 4858c2ecf20Sopenharmony_ci * If the timer is expired and it's periodic, we need to advance it 4868c2ecf20Sopenharmony_ci * because the caller may want to know the previous expiration time. 4878c2ecf20Sopenharmony_ci * We do not update "ticks" and "expired" since the timer will be 4888c2ecf20Sopenharmony_ci * re-programmed again in the following timerfd_setup() call. 4898c2ecf20Sopenharmony_ci */ 4908c2ecf20Sopenharmony_ci if (ctx->expired && ctx->tintv) { 4918c2ecf20Sopenharmony_ci if (isalarm(ctx)) 4928c2ecf20Sopenharmony_ci alarm_forward_now(&ctx->t.alarm, ctx->tintv); 4938c2ecf20Sopenharmony_ci else 4948c2ecf20Sopenharmony_ci hrtimer_forward_now(&ctx->t.tmr, ctx->tintv); 4958c2ecf20Sopenharmony_ci } 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci old->it_value = ktime_to_timespec64(timerfd_get_remaining(ctx)); 4988c2ecf20Sopenharmony_ci old->it_interval = ktime_to_timespec64(ctx->tintv); 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci /* 5018c2ecf20Sopenharmony_ci * Re-program the timer to the new value ... 5028c2ecf20Sopenharmony_ci */ 5038c2ecf20Sopenharmony_ci ret = timerfd_setup(ctx, flags, new); 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci spin_unlock_irq(&ctx->wqh.lock); 5068c2ecf20Sopenharmony_ci fdput(f); 5078c2ecf20Sopenharmony_ci return ret; 5088c2ecf20Sopenharmony_ci} 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_cistatic int do_timerfd_gettime(int ufd, struct itimerspec64 *t) 5118c2ecf20Sopenharmony_ci{ 5128c2ecf20Sopenharmony_ci struct fd f; 5138c2ecf20Sopenharmony_ci struct timerfd_ctx *ctx; 5148c2ecf20Sopenharmony_ci int ret = timerfd_fget(ufd, &f); 5158c2ecf20Sopenharmony_ci if (ret) 5168c2ecf20Sopenharmony_ci return ret; 5178c2ecf20Sopenharmony_ci ctx = f.file->private_data; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci spin_lock_irq(&ctx->wqh.lock); 5208c2ecf20Sopenharmony_ci if (ctx->expired && ctx->tintv) { 5218c2ecf20Sopenharmony_ci ctx->expired = 0; 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci if (isalarm(ctx)) { 5248c2ecf20Sopenharmony_ci ctx->ticks += 5258c2ecf20Sopenharmony_ci alarm_forward_now( 5268c2ecf20Sopenharmony_ci &ctx->t.alarm, ctx->tintv) - 1; 5278c2ecf20Sopenharmony_ci alarm_restart(&ctx->t.alarm); 5288c2ecf20Sopenharmony_ci } else { 5298c2ecf20Sopenharmony_ci ctx->ticks += 5308c2ecf20Sopenharmony_ci hrtimer_forward_now(&ctx->t.tmr, ctx->tintv) 5318c2ecf20Sopenharmony_ci - 1; 5328c2ecf20Sopenharmony_ci hrtimer_restart(&ctx->t.tmr); 5338c2ecf20Sopenharmony_ci } 5348c2ecf20Sopenharmony_ci } 5358c2ecf20Sopenharmony_ci t->it_value = ktime_to_timespec64(timerfd_get_remaining(ctx)); 5368c2ecf20Sopenharmony_ci t->it_interval = ktime_to_timespec64(ctx->tintv); 5378c2ecf20Sopenharmony_ci spin_unlock_irq(&ctx->wqh.lock); 5388c2ecf20Sopenharmony_ci fdput(f); 5398c2ecf20Sopenharmony_ci return 0; 5408c2ecf20Sopenharmony_ci} 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ciSYSCALL_DEFINE4(timerfd_settime, int, ufd, int, flags, 5438c2ecf20Sopenharmony_ci const struct __kernel_itimerspec __user *, utmr, 5448c2ecf20Sopenharmony_ci struct __kernel_itimerspec __user *, otmr) 5458c2ecf20Sopenharmony_ci{ 5468c2ecf20Sopenharmony_ci struct itimerspec64 new, old; 5478c2ecf20Sopenharmony_ci int ret; 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci if (get_itimerspec64(&new, utmr)) 5508c2ecf20Sopenharmony_ci return -EFAULT; 5518c2ecf20Sopenharmony_ci ret = do_timerfd_settime(ufd, flags, &new, &old); 5528c2ecf20Sopenharmony_ci if (ret) 5538c2ecf20Sopenharmony_ci return ret; 5548c2ecf20Sopenharmony_ci if (otmr && put_itimerspec64(&old, otmr)) 5558c2ecf20Sopenharmony_ci return -EFAULT; 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci return ret; 5588c2ecf20Sopenharmony_ci} 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ciSYSCALL_DEFINE2(timerfd_gettime, int, ufd, struct __kernel_itimerspec __user *, otmr) 5618c2ecf20Sopenharmony_ci{ 5628c2ecf20Sopenharmony_ci struct itimerspec64 kotmr; 5638c2ecf20Sopenharmony_ci int ret = do_timerfd_gettime(ufd, &kotmr); 5648c2ecf20Sopenharmony_ci if (ret) 5658c2ecf20Sopenharmony_ci return ret; 5668c2ecf20Sopenharmony_ci return put_itimerspec64(&kotmr, otmr) ? -EFAULT : 0; 5678c2ecf20Sopenharmony_ci} 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT_32BIT_TIME 5708c2ecf20Sopenharmony_ciSYSCALL_DEFINE4(timerfd_settime32, int, ufd, int, flags, 5718c2ecf20Sopenharmony_ci const struct old_itimerspec32 __user *, utmr, 5728c2ecf20Sopenharmony_ci struct old_itimerspec32 __user *, otmr) 5738c2ecf20Sopenharmony_ci{ 5748c2ecf20Sopenharmony_ci struct itimerspec64 new, old; 5758c2ecf20Sopenharmony_ci int ret; 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci if (get_old_itimerspec32(&new, utmr)) 5788c2ecf20Sopenharmony_ci return -EFAULT; 5798c2ecf20Sopenharmony_ci ret = do_timerfd_settime(ufd, flags, &new, &old); 5808c2ecf20Sopenharmony_ci if (ret) 5818c2ecf20Sopenharmony_ci return ret; 5828c2ecf20Sopenharmony_ci if (otmr && put_old_itimerspec32(&old, otmr)) 5838c2ecf20Sopenharmony_ci return -EFAULT; 5848c2ecf20Sopenharmony_ci return ret; 5858c2ecf20Sopenharmony_ci} 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ciSYSCALL_DEFINE2(timerfd_gettime32, int, ufd, 5888c2ecf20Sopenharmony_ci struct old_itimerspec32 __user *, otmr) 5898c2ecf20Sopenharmony_ci{ 5908c2ecf20Sopenharmony_ci struct itimerspec64 kotmr; 5918c2ecf20Sopenharmony_ci int ret = do_timerfd_gettime(ufd, &kotmr); 5928c2ecf20Sopenharmony_ci if (ret) 5938c2ecf20Sopenharmony_ci return ret; 5948c2ecf20Sopenharmony_ci return put_old_itimerspec32(&kotmr, otmr) ? -EFAULT : 0; 5958c2ecf20Sopenharmony_ci} 5968c2ecf20Sopenharmony_ci#endif 597