diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2011-05-23 14:30:28 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-05-23 14:30:28 -0400 |
commit | 30cb6d5f2eb24d15d20139d5ceefaccc68734bd7 (patch) | |
tree | 773c5a98645e4b945343caddcfe5af365566ccc5 /fs | |
parent | 4867faab1e3eb8cc3f74e390357615d9b8e8cda6 (diff) | |
parent | 68fa61c026057a39d6ccb850aa8785043afbee02 (diff) |
Merge branch 'timers-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip
* 'timers-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip:
hrtimers: Reorder clock bases
hrtimers: Avoid touching inactive timer bases
hrtimers: Make struct hrtimer_cpu_base layout less stupid
timerfd: Manage cancelable timers in timerfd
clockevents: Move C3 stop test outside lock
alarmtimer: Drop device refcount after rtc_open()
alarmtimer: Check return value of class_find_device()
timerfd: Allow timers to be cancelled when clock was set
hrtimers: Prepare for cancel on clock was set timers
Diffstat (limited to 'fs')
-rw-r--r-- | fs/timerfd.c | 102 |
1 files changed, 95 insertions, 7 deletions
diff --git a/fs/timerfd.c b/fs/timerfd.c index 8c4fc1425b3e..f67acbdda5e8 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,63 @@ 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. | ||
65 | */ | ||
66 | void timerfd_clock_was_set(void) | ||
67 | { | ||
68 | ktime_t moffs = ktime_get_monotonic_offset(); | ||
69 | struct timerfd_ctx *ctx; | ||
70 | unsigned long flags; | ||
71 | |||
72 | rcu_read_lock(); | ||
73 | list_for_each_entry_rcu(ctx, &cancel_list, clist) { | ||
74 | if (!ctx->might_cancel) | ||
75 | continue; | ||
76 | spin_lock_irqsave(&ctx->wqh.lock, flags); | ||
77 | if (ctx->moffs.tv64 != moffs.tv64) { | ||
78 | ctx->moffs.tv64 = KTIME_MAX; | ||
79 | wake_up_locked(&ctx->wqh); | ||
80 | } | ||
81 | spin_unlock_irqrestore(&ctx->wqh.lock, flags); | ||
82 | } | ||
83 | rcu_read_unlock(); | ||
84 | } | ||
85 | |||
86 | static void timerfd_remove_cancel(struct timerfd_ctx *ctx) | ||
87 | { | ||
88 | if (ctx->might_cancel) { | ||
89 | ctx->might_cancel = false; | ||
90 | spin_lock(&cancel_lock); | ||
91 | list_del_rcu(&ctx->clist); | ||
92 | spin_unlock(&cancel_lock); | ||
93 | } | ||
94 | } | ||
95 | |||
96 | static bool timerfd_canceled(struct timerfd_ctx *ctx) | ||
97 | { | ||
98 | if (!ctx->might_cancel || ctx->moffs.tv64 != KTIME_MAX) | ||
99 | return false; | ||
100 | ctx->moffs = ktime_get_monotonic_offset(); | ||
101 | return true; | ||
102 | } | ||
103 | |||
104 | static void timerfd_setup_cancel(struct timerfd_ctx *ctx, int flags) | ||
105 | { | ||
106 | if (ctx->clockid == CLOCK_REALTIME && (flags & TFD_TIMER_ABSTIME) && | ||
107 | (flags & TFD_TIMER_CANCEL_ON_SET)) { | ||
108 | if (!ctx->might_cancel) { | ||
109 | ctx->might_cancel = true; | ||
110 | spin_lock(&cancel_lock); | ||
111 | list_add_rcu(&ctx->clist, &cancel_list); | ||
112 | spin_unlock(&cancel_lock); | ||
113 | } | ||
114 | } else if (ctx->might_cancel) { | ||
115 | timerfd_remove_cancel(ctx); | ||
116 | } | ||
117 | } | ||
118 | |||
54 | static ktime_t timerfd_get_remaining(struct timerfd_ctx *ctx) | 119 | static ktime_t timerfd_get_remaining(struct timerfd_ctx *ctx) |
55 | { | 120 | { |
56 | ktime_t remaining; | 121 | ktime_t remaining; |
@@ -59,11 +124,12 @@ static ktime_t timerfd_get_remaining(struct timerfd_ctx *ctx) | |||
59 | return remaining.tv64 < 0 ? ktime_set(0, 0): remaining; | 124 | return remaining.tv64 < 0 ? ktime_set(0, 0): remaining; |
60 | } | 125 | } |
61 | 126 | ||
62 | static void timerfd_setup(struct timerfd_ctx *ctx, int flags, | 127 | static int timerfd_setup(struct timerfd_ctx *ctx, int flags, |
63 | const struct itimerspec *ktmr) | 128 | const struct itimerspec *ktmr) |
64 | { | 129 | { |
65 | enum hrtimer_mode htmode; | 130 | enum hrtimer_mode htmode; |
66 | ktime_t texp; | 131 | ktime_t texp; |
132 | int clockid = ctx->clockid; | ||
67 | 133 | ||
68 | htmode = (flags & TFD_TIMER_ABSTIME) ? | 134 | htmode = (flags & TFD_TIMER_ABSTIME) ? |
69 | HRTIMER_MODE_ABS: HRTIMER_MODE_REL; | 135 | HRTIMER_MODE_ABS: HRTIMER_MODE_REL; |
@@ -72,19 +138,24 @@ static void timerfd_setup(struct timerfd_ctx *ctx, int flags, | |||
72 | ctx->expired = 0; | 138 | ctx->expired = 0; |
73 | ctx->ticks = 0; | 139 | ctx->ticks = 0; |
74 | ctx->tintv = timespec_to_ktime(ktmr->it_interval); | 140 | ctx->tintv = timespec_to_ktime(ktmr->it_interval); |
75 | hrtimer_init(&ctx->tmr, ctx->clockid, htmode); | 141 | hrtimer_init(&ctx->tmr, clockid, htmode); |
76 | hrtimer_set_expires(&ctx->tmr, texp); | 142 | hrtimer_set_expires(&ctx->tmr, texp); |
77 | ctx->tmr.function = timerfd_tmrproc; | 143 | ctx->tmr.function = timerfd_tmrproc; |
78 | if (texp.tv64 != 0) | 144 | if (texp.tv64 != 0) { |
79 | hrtimer_start(&ctx->tmr, texp, htmode); | 145 | hrtimer_start(&ctx->tmr, texp, htmode); |
146 | if (timerfd_canceled(ctx)) | ||
147 | return -ECANCELED; | ||
148 | } | ||
149 | return 0; | ||
80 | } | 150 | } |
81 | 151 | ||
82 | static int timerfd_release(struct inode *inode, struct file *file) | 152 | static int timerfd_release(struct inode *inode, struct file *file) |
83 | { | 153 | { |
84 | struct timerfd_ctx *ctx = file->private_data; | 154 | struct timerfd_ctx *ctx = file->private_data; |
85 | 155 | ||
156 | timerfd_remove_cancel(ctx); | ||
86 | hrtimer_cancel(&ctx->tmr); | 157 | hrtimer_cancel(&ctx->tmr); |
87 | kfree(ctx); | 158 | kfree_rcu(ctx, rcu); |
88 | return 0; | 159 | return 0; |
89 | } | 160 | } |
90 | 161 | ||
@@ -118,8 +189,21 @@ static ssize_t timerfd_read(struct file *file, char __user *buf, size_t count, | |||
118 | res = -EAGAIN; | 189 | res = -EAGAIN; |
119 | else | 190 | else |
120 | res = wait_event_interruptible_locked_irq(ctx->wqh, ctx->ticks); | 191 | res = wait_event_interruptible_locked_irq(ctx->wqh, ctx->ticks); |
192 | |||
193 | /* | ||
194 | * If clock has changed, we do not care about the | ||
195 | * ticks and we do not rearm the timer. Userspace must | ||
196 | * reevaluate anyway. | ||
197 | */ | ||
198 | if (timerfd_canceled(ctx)) { | ||
199 | ctx->ticks = 0; | ||
200 | ctx->expired = 0; | ||
201 | res = -ECANCELED; | ||
202 | } | ||
203 | |||
121 | if (ctx->ticks) { | 204 | if (ctx->ticks) { |
122 | ticks = ctx->ticks; | 205 | ticks = ctx->ticks; |
206 | |||
123 | if (ctx->expired && ctx->tintv.tv64) { | 207 | if (ctx->expired && ctx->tintv.tv64) { |
124 | /* | 208 | /* |
125 | * If tintv.tv64 != 0, this is a periodic timer that | 209 | * If tintv.tv64 != 0, this is a periodic timer that |
@@ -183,6 +267,7 @@ SYSCALL_DEFINE2(timerfd_create, int, clockid, int, flags) | |||
183 | init_waitqueue_head(&ctx->wqh); | 267 | init_waitqueue_head(&ctx->wqh); |
184 | ctx->clockid = clockid; | 268 | ctx->clockid = clockid; |
185 | hrtimer_init(&ctx->tmr, clockid, HRTIMER_MODE_ABS); | 269 | hrtimer_init(&ctx->tmr, clockid, HRTIMER_MODE_ABS); |
270 | ctx->moffs = ktime_get_monotonic_offset(); | ||
186 | 271 | ||
187 | ufd = anon_inode_getfd("[timerfd]", &timerfd_fops, ctx, | 272 | ufd = anon_inode_getfd("[timerfd]", &timerfd_fops, ctx, |
188 | O_RDWR | (flags & TFD_SHARED_FCNTL_FLAGS)); | 273 | O_RDWR | (flags & TFD_SHARED_FCNTL_FLAGS)); |
@@ -199,6 +284,7 @@ SYSCALL_DEFINE4(timerfd_settime, int, ufd, int, flags, | |||
199 | struct file *file; | 284 | struct file *file; |
200 | struct timerfd_ctx *ctx; | 285 | struct timerfd_ctx *ctx; |
201 | struct itimerspec ktmr, kotmr; | 286 | struct itimerspec ktmr, kotmr; |
287 | int ret; | ||
202 | 288 | ||
203 | if (copy_from_user(&ktmr, utmr, sizeof(ktmr))) | 289 | if (copy_from_user(&ktmr, utmr, sizeof(ktmr))) |
204 | return -EFAULT; | 290 | return -EFAULT; |
@@ -213,6 +299,8 @@ SYSCALL_DEFINE4(timerfd_settime, int, ufd, int, flags, | |||
213 | return PTR_ERR(file); | 299 | return PTR_ERR(file); |
214 | ctx = file->private_data; | 300 | ctx = file->private_data; |
215 | 301 | ||
302 | timerfd_setup_cancel(ctx, flags); | ||
303 | |||
216 | /* | 304 | /* |
217 | * We need to stop the existing timer before reprogramming | 305 | * We need to stop the existing timer before reprogramming |
218 | * it to the new values. | 306 | * it to the new values. |
@@ -240,14 +328,14 @@ SYSCALL_DEFINE4(timerfd_settime, int, ufd, int, flags, | |||
240 | /* | 328 | /* |
241 | * Re-program the timer to the new value ... | 329 | * Re-program the timer to the new value ... |
242 | */ | 330 | */ |
243 | timerfd_setup(ctx, flags, &ktmr); | 331 | ret = timerfd_setup(ctx, flags, &ktmr); |
244 | 332 | ||
245 | spin_unlock_irq(&ctx->wqh.lock); | 333 | spin_unlock_irq(&ctx->wqh.lock); |
246 | fput(file); | 334 | fput(file); |
247 | if (otmr && copy_to_user(otmr, &kotmr, sizeof(kotmr))) | 335 | if (otmr && copy_to_user(otmr, &kotmr, sizeof(kotmr))) |
248 | return -EFAULT; | 336 | return -EFAULT; |
249 | 337 | ||
250 | return 0; | 338 | return ret; |
251 | } | 339 | } |
252 | 340 | ||
253 | SYSCALL_DEFINE2(timerfd_gettime, int, ufd, struct itimerspec __user *, otmr) | 341 | SYSCALL_DEFINE2(timerfd_gettime, int, ufd, struct itimerspec __user *, otmr) |