diff options
Diffstat (limited to 'fs/timerfd.c')
-rw-r--r-- | fs/timerfd.c | 57 |
1 files changed, 51 insertions, 6 deletions
diff --git a/fs/timerfd.c b/fs/timerfd.c index 8c4fc1425b3e..7e14c9e7c4ee 100644 --- a/fs/timerfd.c +++ b/fs/timerfd.c | |||
@@ -26,10 +26,12 @@ | |||
26 | struct timerfd_ctx { | 26 | struct timerfd_ctx { |
27 | struct hrtimer tmr; | 27 | struct hrtimer tmr; |
28 | ktime_t tintv; | 28 | ktime_t tintv; |
29 | ktime_t moffs; | ||
29 | wait_queue_head_t wqh; | 30 | wait_queue_head_t wqh; |
30 | u64 ticks; | 31 | u64 ticks; |
31 | int expired; | 32 | int expired; |
32 | int clockid; | 33 | int clockid; |
34 | bool might_cancel; | ||
33 | }; | 35 | }; |
34 | 36 | ||
35 | /* | 37 | /* |
@@ -59,24 +61,52 @@ static ktime_t timerfd_get_remaining(struct timerfd_ctx *ctx) | |||
59 | return remaining.tv64 < 0 ? ktime_set(0, 0): remaining; | 61 | return remaining.tv64 < 0 ? ktime_set(0, 0): remaining; |
60 | } | 62 | } |
61 | 63 | ||
62 | static void timerfd_setup(struct timerfd_ctx *ctx, int flags, | 64 | static bool timerfd_canceled(struct timerfd_ctx *ctx) |
63 | const struct itimerspec *ktmr) | 65 | { |
66 | ktime_t moffs; | ||
67 | |||
68 | if (!ctx->might_cancel) | ||
69 | return false; | ||
70 | |||
71 | moffs = ktime_get_monotonic_offset(); | ||
72 | |||
73 | if (moffs.tv64 == ctx->moffs.tv64) | ||
74 | return false; | ||
75 | |||
76 | ctx->moffs = moffs; | ||
77 | return true; | ||
78 | } | ||
79 | |||
80 | static int timerfd_setup(struct timerfd_ctx *ctx, int flags, | ||
81 | const struct itimerspec *ktmr) | ||
64 | { | 82 | { |
65 | enum hrtimer_mode htmode; | 83 | enum hrtimer_mode htmode; |
66 | ktime_t texp; | 84 | ktime_t texp; |
85 | int clockid = ctx->clockid; | ||
67 | 86 | ||
68 | htmode = (flags & TFD_TIMER_ABSTIME) ? | 87 | htmode = (flags & TFD_TIMER_ABSTIME) ? |
69 | HRTIMER_MODE_ABS: HRTIMER_MODE_REL; | 88 | HRTIMER_MODE_ABS: HRTIMER_MODE_REL; |
70 | 89 | ||
90 | ctx->might_cancel = false; | ||
91 | if (htmode == HRTIMER_MODE_ABS && ctx->clockid == CLOCK_REALTIME && | ||
92 | (flags & TFD_TIMER_CANCELON_SET)) { | ||
93 | clockid = CLOCK_REALTIME_COS; | ||
94 | ctx->might_cancel = true; | ||
95 | } | ||
96 | |||
71 | texp = timespec_to_ktime(ktmr->it_value); | 97 | texp = timespec_to_ktime(ktmr->it_value); |
72 | ctx->expired = 0; | 98 | ctx->expired = 0; |
73 | ctx->ticks = 0; | 99 | ctx->ticks = 0; |
74 | ctx->tintv = timespec_to_ktime(ktmr->it_interval); | 100 | ctx->tintv = timespec_to_ktime(ktmr->it_interval); |
75 | hrtimer_init(&ctx->tmr, ctx->clockid, htmode); | 101 | hrtimer_init(&ctx->tmr, clockid, htmode); |
76 | hrtimer_set_expires(&ctx->tmr, texp); | 102 | hrtimer_set_expires(&ctx->tmr, texp); |
77 | ctx->tmr.function = timerfd_tmrproc; | 103 | ctx->tmr.function = timerfd_tmrproc; |
78 | if (texp.tv64 != 0) | 104 | if (texp.tv64 != 0) { |
79 | hrtimer_start(&ctx->tmr, texp, htmode); | 105 | hrtimer_start(&ctx->tmr, texp, htmode); |
106 | if (timerfd_canceled(ctx)) | ||
107 | return -ECANCELED; | ||
108 | } | ||
109 | return 0; | ||
80 | } | 110 | } |
81 | 111 | ||
82 | static int timerfd_release(struct inode *inode, struct file *file) | 112 | static int timerfd_release(struct inode *inode, struct file *file) |
@@ -118,8 +148,21 @@ static ssize_t timerfd_read(struct file *file, char __user *buf, size_t count, | |||
118 | res = -EAGAIN; | 148 | res = -EAGAIN; |
119 | else | 149 | else |
120 | res = wait_event_interruptible_locked_irq(ctx->wqh, ctx->ticks); | 150 | res = wait_event_interruptible_locked_irq(ctx->wqh, ctx->ticks); |
151 | |||
121 | if (ctx->ticks) { | 152 | if (ctx->ticks) { |
122 | ticks = ctx->ticks; | 153 | ticks = ctx->ticks; |
154 | |||
155 | /* | ||
156 | * If clock has changed, we do not care about the | ||
157 | * ticks and we do not rearm the timer. Userspace must | ||
158 | * reevaluate anyway. | ||
159 | */ | ||
160 | if (timerfd_canceled(ctx)) { | ||
161 | ticks = 0; | ||
162 | ctx->expired = 0; | ||
163 | res = -ECANCELED; | ||
164 | } | ||
165 | |||
123 | if (ctx->expired && ctx->tintv.tv64) { | 166 | if (ctx->expired && ctx->tintv.tv64) { |
124 | /* | 167 | /* |
125 | * If tintv.tv64 != 0, this is a periodic timer that | 168 | * If tintv.tv64 != 0, this is a periodic timer that |
@@ -183,6 +226,7 @@ SYSCALL_DEFINE2(timerfd_create, int, clockid, int, flags) | |||
183 | init_waitqueue_head(&ctx->wqh); | 226 | init_waitqueue_head(&ctx->wqh); |
184 | ctx->clockid = clockid; | 227 | ctx->clockid = clockid; |
185 | hrtimer_init(&ctx->tmr, clockid, HRTIMER_MODE_ABS); | 228 | hrtimer_init(&ctx->tmr, clockid, HRTIMER_MODE_ABS); |
229 | ctx->moffs = ktime_get_monotonic_offset(); | ||
186 | 230 | ||
187 | ufd = anon_inode_getfd("[timerfd]", &timerfd_fops, ctx, | 231 | ufd = anon_inode_getfd("[timerfd]", &timerfd_fops, ctx, |
188 | O_RDWR | (flags & TFD_SHARED_FCNTL_FLAGS)); | 232 | O_RDWR | (flags & TFD_SHARED_FCNTL_FLAGS)); |
@@ -199,6 +243,7 @@ SYSCALL_DEFINE4(timerfd_settime, int, ufd, int, flags, | |||
199 | struct file *file; | 243 | struct file *file; |
200 | struct timerfd_ctx *ctx; | 244 | struct timerfd_ctx *ctx; |
201 | struct itimerspec ktmr, kotmr; | 245 | struct itimerspec ktmr, kotmr; |
246 | int ret; | ||
202 | 247 | ||
203 | if (copy_from_user(&ktmr, utmr, sizeof(ktmr))) | 248 | if (copy_from_user(&ktmr, utmr, sizeof(ktmr))) |
204 | return -EFAULT; | 249 | return -EFAULT; |
@@ -240,14 +285,14 @@ SYSCALL_DEFINE4(timerfd_settime, int, ufd, int, flags, | |||
240 | /* | 285 | /* |
241 | * Re-program the timer to the new value ... | 286 | * Re-program the timer to the new value ... |
242 | */ | 287 | */ |
243 | timerfd_setup(ctx, flags, &ktmr); | 288 | ret = timerfd_setup(ctx, flags, &ktmr); |
244 | 289 | ||
245 | spin_unlock_irq(&ctx->wqh.lock); | 290 | spin_unlock_irq(&ctx->wqh.lock); |
246 | fput(file); | 291 | fput(file); |
247 | if (otmr && copy_to_user(otmr, &kotmr, sizeof(kotmr))) | 292 | if (otmr && copy_to_user(otmr, &kotmr, sizeof(kotmr))) |
248 | return -EFAULT; | 293 | return -EFAULT; |
249 | 294 | ||
250 | return 0; | 295 | return ret; |
251 | } | 296 | } |
252 | 297 | ||
253 | SYSCALL_DEFINE2(timerfd_gettime, int, ufd, struct itimerspec __user *, otmr) | 298 | SYSCALL_DEFINE2(timerfd_gettime, int, ufd, struct itimerspec __user *, otmr) |