aboutsummaryrefslogtreecommitdiffstats
path: root/fs/timerfd.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/timerfd.c')
-rw-r--r--fs/timerfd.c57
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 @@
26struct timerfd_ctx { 26struct 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
62static void timerfd_setup(struct timerfd_ctx *ctx, int flags, 64static 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
80static 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
82static int timerfd_release(struct inode *inode, struct file *file) 112static 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
253SYSCALL_DEFINE2(timerfd_gettime, int, ufd, struct itimerspec __user *, otmr) 298SYSCALL_DEFINE2(timerfd_gettime, int, ufd, struct itimerspec __user *, otmr)