diff options
author | Glenn Elliott <gelliott@cs.unc.edu> | 2012-03-04 19:47:13 -0500 |
---|---|---|
committer | Glenn Elliott <gelliott@cs.unc.edu> | 2012-03-04 19:47:13 -0500 |
commit | c71c03bda1e86c9d5198c5d83f712e695c4f2a1e (patch) | |
tree | ecb166cb3e2b7e2adb3b5e292245fefd23381ac8 /fs/timerfd.c | |
parent | ea53c912f8a86a8567697115b6a0d8152beee5c8 (diff) | |
parent | 6a00f206debf8a5c8899055726ad127dbeeed098 (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.c | 106 |
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 | ||
26 | struct timerfd_ctx { | 27 | struct 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 | ||
40 | static LIST_HEAD(cancel_list); | ||
41 | static 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 | */ | ||
68 | void 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 | |||
89 | static 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 | |||
99 | static 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 | |||
107 | static 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 | |||
54 | static ktime_t timerfd_get_remaining(struct timerfd_ctx *ctx) | 122 | static 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 | ||
62 | static void timerfd_setup(struct timerfd_ctx *ctx, int flags, | 130 | static 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 | ||
82 | static int timerfd_release(struct inode *inode, struct file *file) | 155 | static 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 | ||
149 | static struct file *timerfd_fget(int fd) | 237 | static 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 | ||
252 | SYSCALL_DEFINE2(timerfd_gettime, int, ufd, struct itimerspec __user *, otmr) | 344 | SYSCALL_DEFINE2(timerfd_gettime, int, ufd, struct itimerspec __user *, otmr) |