diff options
-rw-r--r-- | fs/timerfd.c | 57 | ||||
-rw-r--r-- | include/linux/hrtimer.h | 2 | ||||
-rw-r--r-- | include/linux/time.h | 6 | ||||
-rw-r--r-- | include/linux/timerfd.h | 3 | ||||
-rw-r--r-- | kernel/hrtimer.c | 36 | ||||
-rw-r--r-- | kernel/time/timekeeping.c | 15 |
6 files changed, 111 insertions, 8 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) |
diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h index 4135c88fe4fa..eda4ccde0730 100644 --- a/include/linux/hrtimer.h +++ b/include/linux/hrtimer.h | |||
@@ -155,6 +155,7 @@ enum hrtimer_base_type { | |||
155 | HRTIMER_BASE_REALTIME, | 155 | HRTIMER_BASE_REALTIME, |
156 | HRTIMER_BASE_MONOTONIC, | 156 | HRTIMER_BASE_MONOTONIC, |
157 | HRTIMER_BASE_BOOTTIME, | 157 | HRTIMER_BASE_BOOTTIME, |
158 | HRTIMER_BASE_REALTIME_COS, | ||
158 | HRTIMER_MAX_CLOCK_BASES, | 159 | HRTIMER_MAX_CLOCK_BASES, |
159 | }; | 160 | }; |
160 | 161 | ||
@@ -310,6 +311,7 @@ extern void hrtimers_resume(void); | |||
310 | extern ktime_t ktime_get(void); | 311 | extern ktime_t ktime_get(void); |
311 | extern ktime_t ktime_get_real(void); | 312 | extern ktime_t ktime_get_real(void); |
312 | extern ktime_t ktime_get_boottime(void); | 313 | extern ktime_t ktime_get_boottime(void); |
314 | extern ktime_t ktime_get_monotonic_offset(void); | ||
313 | 315 | ||
314 | DECLARE_PER_CPU(struct tick_device, tick_cpu_device); | 316 | DECLARE_PER_CPU(struct tick_device, tick_cpu_device); |
315 | 317 | ||
diff --git a/include/linux/time.h b/include/linux/time.h index b3061782dec3..a9242773eb24 100644 --- a/include/linux/time.h +++ b/include/linux/time.h | |||
@@ -302,6 +302,12 @@ struct itimerval { | |||
302 | * The IDs of various hardware clocks: | 302 | * The IDs of various hardware clocks: |
303 | */ | 303 | */ |
304 | #define CLOCK_SGI_CYCLE 10 | 304 | #define CLOCK_SGI_CYCLE 10 |
305 | |||
306 | #ifdef __KERNEL__ | ||
307 | /* This clock is not exposed to user space */ | ||
308 | #define CLOCK_REALTIME_COS 15 | ||
309 | #endif | ||
310 | |||
305 | #define MAX_CLOCKS 16 | 311 | #define MAX_CLOCKS 16 |
306 | #define CLOCKS_MASK (CLOCK_REALTIME | CLOCK_MONOTONIC) | 312 | #define CLOCKS_MASK (CLOCK_REALTIME | CLOCK_MONOTONIC) |
307 | #define CLOCKS_MONO CLOCK_MONOTONIC | 313 | #define CLOCKS_MONO CLOCK_MONOTONIC |
diff --git a/include/linux/timerfd.h b/include/linux/timerfd.h index 2d0792983f8c..e9571fc8f1a0 100644 --- a/include/linux/timerfd.h +++ b/include/linux/timerfd.h | |||
@@ -19,6 +19,7 @@ | |||
19 | * shared O_* flags. | 19 | * shared O_* flags. |
20 | */ | 20 | */ |
21 | #define TFD_TIMER_ABSTIME (1 << 0) | 21 | #define TFD_TIMER_ABSTIME (1 << 0) |
22 | #define TFD_TIMER_CANCELON_SET (1 << 1) | ||
22 | #define TFD_CLOEXEC O_CLOEXEC | 23 | #define TFD_CLOEXEC O_CLOEXEC |
23 | #define TFD_NONBLOCK O_NONBLOCK | 24 | #define TFD_NONBLOCK O_NONBLOCK |
24 | 25 | ||
@@ -26,6 +27,6 @@ | |||
26 | /* Flags for timerfd_create. */ | 27 | /* Flags for timerfd_create. */ |
27 | #define TFD_CREATE_FLAGS TFD_SHARED_FCNTL_FLAGS | 28 | #define TFD_CREATE_FLAGS TFD_SHARED_FCNTL_FLAGS |
28 | /* Flags for timerfd_settime. */ | 29 | /* Flags for timerfd_settime. */ |
29 | #define TFD_SETTIME_FLAGS TFD_TIMER_ABSTIME | 30 | #define TFD_SETTIME_FLAGS (TFD_TIMER_ABSTIME | TFD_TIMER_CANCELON_SET) |
30 | 31 | ||
31 | #endif /* _LINUX_TIMERFD_H */ | 32 | #endif /* _LINUX_TIMERFD_H */ |
diff --git a/kernel/hrtimer.c b/kernel/hrtimer.c index c145ed643bca..eabcbd781433 100644 --- a/kernel/hrtimer.c +++ b/kernel/hrtimer.c | |||
@@ -78,6 +78,11 @@ DEFINE_PER_CPU(struct hrtimer_cpu_base, hrtimer_bases) = | |||
78 | .get_time = &ktime_get_boottime, | 78 | .get_time = &ktime_get_boottime, |
79 | .resolution = KTIME_LOW_RES, | 79 | .resolution = KTIME_LOW_RES, |
80 | }, | 80 | }, |
81 | { | ||
82 | .index = CLOCK_REALTIME_COS, | ||
83 | .get_time = &ktime_get_real, | ||
84 | .resolution = KTIME_LOW_RES, | ||
85 | }, | ||
81 | } | 86 | } |
82 | }; | 87 | }; |
83 | 88 | ||
@@ -85,6 +90,7 @@ static const int hrtimer_clock_to_base_table[MAX_CLOCKS] = { | |||
85 | [CLOCK_REALTIME] = HRTIMER_BASE_REALTIME, | 90 | [CLOCK_REALTIME] = HRTIMER_BASE_REALTIME, |
86 | [CLOCK_MONOTONIC] = HRTIMER_BASE_MONOTONIC, | 91 | [CLOCK_MONOTONIC] = HRTIMER_BASE_MONOTONIC, |
87 | [CLOCK_BOOTTIME] = HRTIMER_BASE_BOOTTIME, | 92 | [CLOCK_BOOTTIME] = HRTIMER_BASE_BOOTTIME, |
93 | [CLOCK_REALTIME_COS] = HRTIMER_BASE_REALTIME_COS, | ||
88 | }; | 94 | }; |
89 | 95 | ||
90 | static inline int hrtimer_clockid_to_base(clockid_t clock_id) | 96 | static inline int hrtimer_clockid_to_base(clockid_t clock_id) |
@@ -110,6 +116,7 @@ static void hrtimer_get_softirq_time(struct hrtimer_cpu_base *base) | |||
110 | base->clock_base[HRTIMER_BASE_REALTIME].softirq_time = xtim; | 116 | base->clock_base[HRTIMER_BASE_REALTIME].softirq_time = xtim; |
111 | base->clock_base[HRTIMER_BASE_MONOTONIC].softirq_time = mono; | 117 | base->clock_base[HRTIMER_BASE_MONOTONIC].softirq_time = mono; |
112 | base->clock_base[HRTIMER_BASE_BOOTTIME].softirq_time = boot; | 118 | base->clock_base[HRTIMER_BASE_BOOTTIME].softirq_time = boot; |
119 | base->clock_base[HRTIMER_BASE_REALTIME_COS].softirq_time = xtim; | ||
113 | } | 120 | } |
114 | 121 | ||
115 | /* | 122 | /* |
@@ -479,6 +486,8 @@ static inline void debug_deactivate(struct hrtimer *timer) | |||
479 | trace_hrtimer_cancel(timer); | 486 | trace_hrtimer_cancel(timer); |
480 | } | 487 | } |
481 | 488 | ||
489 | static void hrtimer_expire_cancelable(struct hrtimer_cpu_base *cpu_base); | ||
490 | |||
482 | /* High resolution timer related functions */ | 491 | /* High resolution timer related functions */ |
483 | #ifdef CONFIG_HIGH_RES_TIMERS | 492 | #ifdef CONFIG_HIGH_RES_TIMERS |
484 | 493 | ||
@@ -715,9 +724,14 @@ static void retrigger_next_event(void *arg) | |||
715 | struct hrtimer_cpu_base *base = &__get_cpu_var(hrtimer_bases); | 724 | struct hrtimer_cpu_base *base = &__get_cpu_var(hrtimer_bases); |
716 | struct timespec realtime_offset, xtim, wtm, sleep; | 725 | struct timespec realtime_offset, xtim, wtm, sleep; |
717 | 726 | ||
718 | if (!hrtimer_hres_active()) | 727 | if (!hrtimer_hres_active()) { |
728 | raw_spin_lock(&base->lock); | ||
729 | hrtimer_expire_cancelable(base); | ||
730 | raw_spin_unlock(&base->lock); | ||
719 | return; | 731 | return; |
732 | } | ||
720 | 733 | ||
734 | /* Optimized out for !HIGH_RES */ | ||
721 | get_xtime_and_monotonic_and_sleep_offset(&xtim, &wtm, &sleep); | 735 | get_xtime_and_monotonic_and_sleep_offset(&xtim, &wtm, &sleep); |
722 | set_normalized_timespec(&realtime_offset, -wtm.tv_sec, -wtm.tv_nsec); | 736 | set_normalized_timespec(&realtime_offset, -wtm.tv_sec, -wtm.tv_nsec); |
723 | 737 | ||
@@ -727,6 +741,10 @@ static void retrigger_next_event(void *arg) | |||
727 | timespec_to_ktime(realtime_offset); | 741 | timespec_to_ktime(realtime_offset); |
728 | base->clock_base[HRTIMER_BASE_BOOTTIME].offset = | 742 | base->clock_base[HRTIMER_BASE_BOOTTIME].offset = |
729 | timespec_to_ktime(sleep); | 743 | timespec_to_ktime(sleep); |
744 | base->clock_base[HRTIMER_BASE_REALTIME_COS].offset = | ||
745 | timespec_to_ktime(realtime_offset); | ||
746 | |||
747 | hrtimer_expire_cancelable(base); | ||
730 | 748 | ||
731 | hrtimer_force_reprogram(base, 0); | 749 | hrtimer_force_reprogram(base, 0); |
732 | raw_spin_unlock(&base->lock); | 750 | raw_spin_unlock(&base->lock); |
@@ -1222,6 +1240,22 @@ static void __run_hrtimer(struct hrtimer *timer, ktime_t *now) | |||
1222 | timer->state &= ~HRTIMER_STATE_CALLBACK; | 1240 | timer->state &= ~HRTIMER_STATE_CALLBACK; |
1223 | } | 1241 | } |
1224 | 1242 | ||
1243 | static void hrtimer_expire_cancelable(struct hrtimer_cpu_base *cpu_base) | ||
1244 | { | ||
1245 | struct timerqueue_node *node; | ||
1246 | struct hrtimer_clock_base *base; | ||
1247 | ktime_t now = ktime_get_real(); | ||
1248 | |||
1249 | base = &cpu_base->clock_base[HRTIMER_BASE_REALTIME_COS]; | ||
1250 | |||
1251 | while ((node = timerqueue_getnext(&base->active))) { | ||
1252 | struct hrtimer *timer; | ||
1253 | |||
1254 | timer = container_of(node, struct hrtimer, node); | ||
1255 | __run_hrtimer(timer, &now); | ||
1256 | } | ||
1257 | } | ||
1258 | |||
1225 | #ifdef CONFIG_HIGH_RES_TIMERS | 1259 | #ifdef CONFIG_HIGH_RES_TIMERS |
1226 | 1260 | ||
1227 | /* | 1261 | /* |
diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index a61b8fa2d39a..342408cf68dd 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c | |||
@@ -1099,6 +1099,21 @@ void get_xtime_and_monotonic_and_sleep_offset(struct timespec *xtim, | |||
1099 | } | 1099 | } |
1100 | 1100 | ||
1101 | /** | 1101 | /** |
1102 | * ktime_get_monotonic_offset() - get wall_to_monotonic in ktime_t format | ||
1103 | */ | ||
1104 | ktime_t ktime_get_monotonic_offset(void) | ||
1105 | { | ||
1106 | unsigned long seq; | ||
1107 | struct timespec wtom; | ||
1108 | |||
1109 | do { | ||
1110 | seq = read_seqbegin(&xtime_lock); | ||
1111 | wtom = wall_to_monotonic; | ||
1112 | } while (read_seqretry(&xtime_lock, seq)); | ||
1113 | return timespec_to_ktime(wtom); | ||
1114 | } | ||
1115 | |||
1116 | /** | ||
1102 | * xtime_update() - advances the timekeeping infrastructure | 1117 | * xtime_update() - advances the timekeeping infrastructure |
1103 | * @ticks: number of ticks, that have elapsed since the last call. | 1118 | * @ticks: number of ticks, that have elapsed since the last call. |
1104 | * | 1119 | * |