aboutsummaryrefslogtreecommitdiffstats
path: root/fs/timerfd.c
diff options
context:
space:
mode:
authorGlenn Elliott <gelliott@cs.unc.edu>2012-03-04 19:47:13 -0500
committerGlenn Elliott <gelliott@cs.unc.edu>2012-03-04 19:47:13 -0500
commitc71c03bda1e86c9d5198c5d83f712e695c4f2a1e (patch)
treeecb166cb3e2b7e2adb3b5e292245fefd23381ac8 /fs/timerfd.c
parentea53c912f8a86a8567697115b6a0d8152beee5c8 (diff)
parent6a00f206debf8a5c8899055726ad127dbeeed098 (diff)
Merge branch 'mpi-master' into wip-k-fmlpwip-k-fmlp
Conflicts: litmus/sched_cedf.c
Diffstat (limited to 'fs/timerfd.c')
-rw-r--r--fs/timerfd.c106
1 files changed, 99 insertions, 7 deletions
diff --git a/fs/timerfd.c b/fs/timerfd.c
index b86ab8eff79a..dffeb3795af1 100644
--- a/fs/timerfd.c
+++ b/fs/timerfd.c
@@ -22,16 +22,24 @@
22#include <linux/anon_inodes.h> 22#include <linux/anon_inodes.h>
23#include <linux/timerfd.h> 23#include <linux/timerfd.h>
24#include <linux/syscalls.h> 24#include <linux/syscalls.h>
25#include <linux/rcupdate.h>
25 26
26struct timerfd_ctx { 27struct timerfd_ctx {
27 struct hrtimer tmr; 28 struct hrtimer tmr;
28 ktime_t tintv; 29 ktime_t tintv;
30 ktime_t moffs;
29 wait_queue_head_t wqh; 31 wait_queue_head_t wqh;
30 u64 ticks; 32 u64 ticks;
31 int expired; 33 int expired;
32 int clockid; 34 int clockid;
35 struct rcu_head rcu;
36 struct list_head clist;
37 bool might_cancel;
33}; 38};
34 39
40static LIST_HEAD(cancel_list);
41static DEFINE_SPINLOCK(cancel_lock);
42
35/* 43/*
36 * This gets called when the timer event triggers. We set the "expired" 44 * 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, 45 * flag, but we do not re-arm the timer (in case it's necessary,
@@ -51,6 +59,66 @@ static enum hrtimer_restart timerfd_tmrproc(struct hrtimer *htmr)
51 return HRTIMER_NORESTART; 59 return HRTIMER_NORESTART;
52} 60}
53 61
62/*
63 * Called when the clock was set to cancel the timers in the cancel
64 * list. This will wake up processes waiting on these timers. The
65 * wake-up requires ctx->ticks to be non zero, therefore we increment
66 * it before calling wake_up_locked().
67 */
68void timerfd_clock_was_set(void)
69{
70 ktime_t moffs = ktime_get_monotonic_offset();
71 struct timerfd_ctx *ctx;
72 unsigned long flags;
73
74 rcu_read_lock();
75 list_for_each_entry_rcu(ctx, &cancel_list, clist) {
76 if (!ctx->might_cancel)
77 continue;
78 spin_lock_irqsave(&ctx->wqh.lock, flags);
79 if (ctx->moffs.tv64 != moffs.tv64) {
80 ctx->moffs.tv64 = KTIME_MAX;
81 ctx->ticks++;
82 wake_up_locked(&ctx->wqh);
83 }
84 spin_unlock_irqrestore(&ctx->wqh.lock, flags);
85 }
86 rcu_read_unlock();
87}
88
89static void timerfd_remove_cancel(struct timerfd_ctx *ctx)
90{
91 if (ctx->might_cancel) {
92 ctx->might_cancel = false;
93 spin_lock(&cancel_lock);
94 list_del_rcu(&ctx->clist);
95 spin_unlock(&cancel_lock);
96 }
97}
98
99static bool timerfd_canceled(struct timerfd_ctx *ctx)
100{
101 if (!ctx->might_cancel || ctx->moffs.tv64 != KTIME_MAX)
102 return false;
103 ctx->moffs = ktime_get_monotonic_offset();
104 return true;
105}
106
107static void timerfd_setup_cancel(struct timerfd_ctx *ctx, int flags)
108{
109 if (ctx->clockid == CLOCK_REALTIME && (flags & TFD_TIMER_ABSTIME) &&
110 (flags & TFD_TIMER_CANCEL_ON_SET)) {
111 if (!ctx->might_cancel) {
112 ctx->might_cancel = true;
113 spin_lock(&cancel_lock);
114 list_add_rcu(&ctx->clist, &cancel_list);
115 spin_unlock(&cancel_lock);
116 }
117 } else if (ctx->might_cancel) {
118 timerfd_remove_cancel(ctx);
119 }
120}
121
54static ktime_t timerfd_get_remaining(struct timerfd_ctx *ctx) 122static ktime_t timerfd_get_remaining(struct timerfd_ctx *ctx)
55{ 123{
56 ktime_t remaining; 124 ktime_t remaining;
@@ -59,11 +127,12 @@ static ktime_t timerfd_get_remaining(struct timerfd_ctx *ctx)
59 return remaining.tv64 < 0 ? ktime_set(0, 0): remaining; 127 return remaining.tv64 < 0 ? ktime_set(0, 0): remaining;
60} 128}
61 129
62static void timerfd_setup(struct timerfd_ctx *ctx, int flags, 130static int timerfd_setup(struct timerfd_ctx *ctx, int flags,
63 const struct itimerspec *ktmr) 131 const struct itimerspec *ktmr)
64{ 132{
65 enum hrtimer_mode htmode; 133 enum hrtimer_mode htmode;
66 ktime_t texp; 134 ktime_t texp;
135 int clockid = ctx->clockid;
67 136
68 htmode = (flags & TFD_TIMER_ABSTIME) ? 137 htmode = (flags & TFD_TIMER_ABSTIME) ?
69 HRTIMER_MODE_ABS: HRTIMER_MODE_REL; 138 HRTIMER_MODE_ABS: HRTIMER_MODE_REL;
@@ -72,19 +141,24 @@ static void timerfd_setup(struct timerfd_ctx *ctx, int flags,
72 ctx->expired = 0; 141 ctx->expired = 0;
73 ctx->ticks = 0; 142 ctx->ticks = 0;
74 ctx->tintv = timespec_to_ktime(ktmr->it_interval); 143 ctx->tintv = timespec_to_ktime(ktmr->it_interval);
75 hrtimer_init(&ctx->tmr, ctx->clockid, htmode); 144 hrtimer_init(&ctx->tmr, clockid, htmode);
76 hrtimer_set_expires(&ctx->tmr, texp); 145 hrtimer_set_expires(&ctx->tmr, texp);
77 ctx->tmr.function = timerfd_tmrproc; 146 ctx->tmr.function = timerfd_tmrproc;
78 if (texp.tv64 != 0) 147 if (texp.tv64 != 0) {
79 hrtimer_start(&ctx->tmr, texp, htmode); 148 hrtimer_start(&ctx->tmr, texp, htmode);
149 if (timerfd_canceled(ctx))
150 return -ECANCELED;
151 }
152 return 0;
80} 153}
81 154
82static int timerfd_release(struct inode *inode, struct file *file) 155static int timerfd_release(struct inode *inode, struct file *file)
83{ 156{
84 struct timerfd_ctx *ctx = file->private_data; 157 struct timerfd_ctx *ctx = file->private_data;
85 158
159 timerfd_remove_cancel(ctx);
86 hrtimer_cancel(&ctx->tmr); 160 hrtimer_cancel(&ctx->tmr);
87 kfree(ctx); 161 kfree_rcu(ctx, rcu);
88 return 0; 162 return 0;
89} 163}
90 164
@@ -118,8 +192,21 @@ static ssize_t timerfd_read(struct file *file, char __user *buf, size_t count,
118 res = -EAGAIN; 192 res = -EAGAIN;
119 else 193 else
120 res = wait_event_interruptible_locked_irq(ctx->wqh, ctx->ticks); 194 res = wait_event_interruptible_locked_irq(ctx->wqh, ctx->ticks);
195
196 /*
197 * If clock has changed, we do not care about the
198 * ticks and we do not rearm the timer. Userspace must
199 * reevaluate anyway.
200 */
201 if (timerfd_canceled(ctx)) {
202 ctx->ticks = 0;
203 ctx->expired = 0;
204 res = -ECANCELED;
205 }
206
121 if (ctx->ticks) { 207 if (ctx->ticks) {
122 ticks = ctx->ticks; 208 ticks = ctx->ticks;
209
123 if (ctx->expired && ctx->tintv.tv64) { 210 if (ctx->expired && ctx->tintv.tv64) {
124 /* 211 /*
125 * If tintv.tv64 != 0, this is a periodic timer that 212 * If tintv.tv64 != 0, this is a periodic timer that
@@ -144,6 +231,7 @@ static const struct file_operations timerfd_fops = {
144 .release = timerfd_release, 231 .release = timerfd_release,
145 .poll = timerfd_poll, 232 .poll = timerfd_poll,
146 .read = timerfd_read, 233 .read = timerfd_read,
234 .llseek = noop_llseek,
147}; 235};
148 236
149static struct file *timerfd_fget(int fd) 237static struct file *timerfd_fget(int fd)
@@ -182,6 +270,7 @@ SYSCALL_DEFINE2(timerfd_create, int, clockid, int, flags)
182 init_waitqueue_head(&ctx->wqh); 270 init_waitqueue_head(&ctx->wqh);
183 ctx->clockid = clockid; 271 ctx->clockid = clockid;
184 hrtimer_init(&ctx->tmr, clockid, HRTIMER_MODE_ABS); 272 hrtimer_init(&ctx->tmr, clockid, HRTIMER_MODE_ABS);
273 ctx->moffs = ktime_get_monotonic_offset();
185 274
186 ufd = anon_inode_getfd("[timerfd]", &timerfd_fops, ctx, 275 ufd = anon_inode_getfd("[timerfd]", &timerfd_fops, ctx,
187 O_RDWR | (flags & TFD_SHARED_FCNTL_FLAGS)); 276 O_RDWR | (flags & TFD_SHARED_FCNTL_FLAGS));
@@ -198,6 +287,7 @@ SYSCALL_DEFINE4(timerfd_settime, int, ufd, int, flags,
198 struct file *file; 287 struct file *file;
199 struct timerfd_ctx *ctx; 288 struct timerfd_ctx *ctx;
200 struct itimerspec ktmr, kotmr; 289 struct itimerspec ktmr, kotmr;
290 int ret;
201 291
202 if (copy_from_user(&ktmr, utmr, sizeof(ktmr))) 292 if (copy_from_user(&ktmr, utmr, sizeof(ktmr)))
203 return -EFAULT; 293 return -EFAULT;
@@ -212,6 +302,8 @@ SYSCALL_DEFINE4(timerfd_settime, int, ufd, int, flags,
212 return PTR_ERR(file); 302 return PTR_ERR(file);
213 ctx = file->private_data; 303 ctx = file->private_data;
214 304
305 timerfd_setup_cancel(ctx, flags);
306
215 /* 307 /*
216 * We need to stop the existing timer before reprogramming 308 * We need to stop the existing timer before reprogramming
217 * it to the new values. 309 * it to the new values.
@@ -239,14 +331,14 @@ SYSCALL_DEFINE4(timerfd_settime, int, ufd, int, flags,
239 /* 331 /*
240 * Re-program the timer to the new value ... 332 * Re-program the timer to the new value ...
241 */ 333 */
242 timerfd_setup(ctx, flags, &ktmr); 334 ret = timerfd_setup(ctx, flags, &ktmr);
243 335
244 spin_unlock_irq(&ctx->wqh.lock); 336 spin_unlock_irq(&ctx->wqh.lock);
245 fput(file); 337 fput(file);
246 if (otmr && copy_to_user(otmr, &kotmr, sizeof(kotmr))) 338 if (otmr && copy_to_user(otmr, &kotmr, sizeof(kotmr)))
247 return -EFAULT; 339 return -EFAULT;
248 340
249 return 0; 341 return ret;
250} 342}
251 343
252SYSCALL_DEFINE2(timerfd_gettime, int, ufd, struct itimerspec __user *, otmr) 344SYSCALL_DEFINE2(timerfd_gettime, int, ufd, struct itimerspec __user *, otmr)