Root/fs/timerfd.c

1/*
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
11#include <linux/file.h>
12#include <linux/poll.h>
13#include <linux/init.h>
14#include <linux/fs.h>
15#include <linux/sched.h>
16#include <linux/kernel.h>
17#include <linux/slab.h>
18#include <linux/list.h>
19#include <linux/spinlock.h>
20#include <linux/time.h>
21#include <linux/hrtimer.h>
22#include <linux/anon_inodes.h>
23#include <linux/timerfd.h>
24#include <linux/syscalls.h>
25
26struct timerfd_ctx {
27    struct hrtimer tmr;
28    ktime_t tintv;
29    wait_queue_head_t wqh;
30    u64 ticks;
31    int expired;
32    int clockid;
33};
34
35/*
36 * This gets called when the timer event triggers. We set the "expired"
37 * flag, but we do not re-arm the timer (in case it's necessary,
38 * tintv.tv64 != 0) until the timer is accessed.
39 */
40static enum hrtimer_restart timerfd_tmrproc(struct hrtimer *htmr)
41{
42    struct timerfd_ctx *ctx = container_of(htmr, struct timerfd_ctx, tmr);
43    unsigned long flags;
44
45    spin_lock_irqsave(&ctx->wqh.lock, flags);
46    ctx->expired = 1;
47    ctx->ticks++;
48    wake_up_locked(&ctx->wqh);
49    spin_unlock_irqrestore(&ctx->wqh.lock, flags);
50
51    return HRTIMER_NORESTART;
52}
53
54static ktime_t timerfd_get_remaining(struct timerfd_ctx *ctx)
55{
56    ktime_t remaining;
57
58    remaining = hrtimer_expires_remaining(&ctx->tmr);
59    return remaining.tv64 < 0 ? ktime_set(0, 0): remaining;
60}
61
62static void timerfd_setup(struct timerfd_ctx *ctx, int flags,
63              const struct itimerspec *ktmr)
64{
65    enum hrtimer_mode htmode;
66    ktime_t texp;
67
68    htmode = (flags & TFD_TIMER_ABSTIME) ?
69        HRTIMER_MODE_ABS: HRTIMER_MODE_REL;
70
71    texp = timespec_to_ktime(ktmr->it_value);
72    ctx->expired = 0;
73    ctx->ticks = 0;
74    ctx->tintv = timespec_to_ktime(ktmr->it_interval);
75    hrtimer_init(&ctx->tmr, ctx->clockid, htmode);
76    hrtimer_set_expires(&ctx->tmr, texp);
77    ctx->tmr.function = timerfd_tmrproc;
78    if (texp.tv64 != 0)
79        hrtimer_start(&ctx->tmr, texp, htmode);
80}
81
82static int timerfd_release(struct inode *inode, struct file *file)
83{
84    struct timerfd_ctx *ctx = file->private_data;
85
86    hrtimer_cancel(&ctx->tmr);
87    kfree(ctx);
88    return 0;
89}
90
91static unsigned int timerfd_poll(struct file *file, poll_table *wait)
92{
93    struct timerfd_ctx *ctx = file->private_data;
94    unsigned int events = 0;
95    unsigned long flags;
96
97    poll_wait(file, &ctx->wqh, wait);
98
99    spin_lock_irqsave(&ctx->wqh.lock, flags);
100    if (ctx->ticks)
101        events |= POLLIN;
102    spin_unlock_irqrestore(&ctx->wqh.lock, flags);
103
104    return events;
105}
106
107static ssize_t timerfd_read(struct file *file, char __user *buf, size_t count,
108                loff_t *ppos)
109{
110    struct timerfd_ctx *ctx = file->private_data;
111    ssize_t res;
112    u64 ticks = 0;
113    DECLARE_WAITQUEUE(wait, current);
114
115    if (count < sizeof(ticks))
116        return -EINVAL;
117    spin_lock_irq(&ctx->wqh.lock);
118    res = -EAGAIN;
119    if (!ctx->ticks && !(file->f_flags & O_NONBLOCK)) {
120        __add_wait_queue(&ctx->wqh, &wait);
121        for (res = 0;;) {
122            set_current_state(TASK_INTERRUPTIBLE);
123            if (ctx->ticks) {
124                res = 0;
125                break;
126            }
127            if (signal_pending(current)) {
128                res = -ERESTARTSYS;
129                break;
130            }
131            spin_unlock_irq(&ctx->wqh.lock);
132            schedule();
133            spin_lock_irq(&ctx->wqh.lock);
134        }
135        __remove_wait_queue(&ctx->wqh, &wait);
136        __set_current_state(TASK_RUNNING);
137    }
138    if (ctx->ticks) {
139        ticks = ctx->ticks;
140        if (ctx->expired && ctx->tintv.tv64) {
141            /*
142             * If tintv.tv64 != 0, this is a periodic timer that
143             * needs to be re-armed. We avoid doing it in the timer
144             * callback to avoid DoS attacks specifying a very
145             * short timer period.
146             */
147            ticks += hrtimer_forward_now(&ctx->tmr,
148                             ctx->tintv) - 1;
149            hrtimer_restart(&ctx->tmr);
150        }
151        ctx->expired = 0;
152        ctx->ticks = 0;
153    }
154    spin_unlock_irq(&ctx->wqh.lock);
155    if (ticks)
156        res = put_user(ticks, (u64 __user *) buf) ? -EFAULT: sizeof(ticks);
157    return res;
158}
159
160static const struct file_operations timerfd_fops = {
161    .release = timerfd_release,
162    .poll = timerfd_poll,
163    .read = timerfd_read,
164};
165
166static struct file *timerfd_fget(int fd)
167{
168    struct file *file;
169
170    file = fget(fd);
171    if (!file)
172        return ERR_PTR(-EBADF);
173    if (file->f_op != &timerfd_fops) {
174        fput(file);
175        return ERR_PTR(-EINVAL);
176    }
177
178    return file;
179}
180
181SYSCALL_DEFINE2(timerfd_create, int, clockid, int, flags)
182{
183    int ufd;
184    struct timerfd_ctx *ctx;
185
186    /* Check the TFD_* constants for consistency. */
187    BUILD_BUG_ON(TFD_CLOEXEC != O_CLOEXEC);
188    BUILD_BUG_ON(TFD_NONBLOCK != O_NONBLOCK);
189
190    if ((flags & ~TFD_CREATE_FLAGS) ||
191        (clockid != CLOCK_MONOTONIC &&
192         clockid != CLOCK_REALTIME))
193        return -EINVAL;
194
195    ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
196    if (!ctx)
197        return -ENOMEM;
198
199    init_waitqueue_head(&ctx->wqh);
200    ctx->clockid = clockid;
201    hrtimer_init(&ctx->tmr, clockid, HRTIMER_MODE_ABS);
202
203    ufd = anon_inode_getfd("[timerfd]", &timerfd_fops, ctx,
204                   O_RDWR | (flags & TFD_SHARED_FCNTL_FLAGS));
205    if (ufd < 0)
206        kfree(ctx);
207
208    return ufd;
209}
210
211SYSCALL_DEFINE4(timerfd_settime, int, ufd, int, flags,
212        const struct itimerspec __user *, utmr,
213        struct itimerspec __user *, otmr)
214{
215    struct file *file;
216    struct timerfd_ctx *ctx;
217    struct itimerspec ktmr, kotmr;
218
219    if (copy_from_user(&ktmr, utmr, sizeof(ktmr)))
220        return -EFAULT;
221
222    if ((flags & ~TFD_SETTIME_FLAGS) ||
223        !timespec_valid(&ktmr.it_value) ||
224        !timespec_valid(&ktmr.it_interval))
225        return -EINVAL;
226
227    file = timerfd_fget(ufd);
228    if (IS_ERR(file))
229        return PTR_ERR(file);
230    ctx = file->private_data;
231
232    /*
233     * We need to stop the existing timer before reprogramming
234     * it to the new values.
235     */
236    for (;;) {
237        spin_lock_irq(&ctx->wqh.lock);
238        if (hrtimer_try_to_cancel(&ctx->tmr) >= 0)
239            break;
240        spin_unlock_irq(&ctx->wqh.lock);
241        cpu_relax();
242    }
243
244    /*
245     * If the timer is expired and it's periodic, we need to advance it
246     * because the caller may want to know the previous expiration time.
247     * We do not update "ticks" and "expired" since the timer will be
248     * re-programmed again in the following timerfd_setup() call.
249     */
250    if (ctx->expired && ctx->tintv.tv64)
251        hrtimer_forward_now(&ctx->tmr, ctx->tintv);
252
253    kotmr.it_value = ktime_to_timespec(timerfd_get_remaining(ctx));
254    kotmr.it_interval = ktime_to_timespec(ctx->tintv);
255
256    /*
257     * Re-program the timer to the new value ...
258     */
259    timerfd_setup(ctx, flags, &ktmr);
260
261    spin_unlock_irq(&ctx->wqh.lock);
262    fput(file);
263    if (otmr && copy_to_user(otmr, &kotmr, sizeof(kotmr)))
264        return -EFAULT;
265
266    return 0;
267}
268
269SYSCALL_DEFINE2(timerfd_gettime, int, ufd, struct itimerspec __user *, otmr)
270{
271    struct file *file;
272    struct timerfd_ctx *ctx;
273    struct itimerspec kotmr;
274
275    file = timerfd_fget(ufd);
276    if (IS_ERR(file))
277        return PTR_ERR(file);
278    ctx = file->private_data;
279
280    spin_lock_irq(&ctx->wqh.lock);
281    if (ctx->expired && ctx->tintv.tv64) {
282        ctx->expired = 0;
283        ctx->ticks +=
284            hrtimer_forward_now(&ctx->tmr, ctx->tintv) - 1;
285        hrtimer_restart(&ctx->tmr);
286    }
287    kotmr.it_value = ktime_to_timespec(timerfd_get_remaining(ctx));
288    kotmr.it_interval = ktime_to_timespec(ctx->tintv);
289    spin_unlock_irq(&ctx->wqh.lock);
290    fput(file);
291
292    return copy_to_user(otmr, &kotmr, sizeof(kotmr)) ? -EFAULT: 0;
293}
294
295

Archive Download this file



interactive