diff options
author | Andrea Bastoni <bastoni@cs.unc.edu> | 2011-08-27 09:43:54 -0400 |
---|---|---|
committer | Andrea Bastoni <bastoni@cs.unc.edu> | 2011-08-27 10:06:11 -0400 |
commit | 7b1bb388bc879ffcc6c69b567816d5c354afe42b (patch) | |
tree | 5a217fdfb0b5e5a327bdcd624506337c1ae1fe32 /kernel/time/posix-clock.c | |
parent | 7d754596756240fa918b94cd0c3011c77a638987 (diff) | |
parent | 02f8c6aee8df3cdc935e9bdd4f2d020306035dbe (diff) |
Merge 'Linux v3.0' into Litmus
Some notes:
* Litmus^RT scheduling class is the topmost scheduling class
(above stop_sched_class).
* scheduler_ipi() function (e.g., in smp_reschedule_interrupt())
may increase IPI latencies.
* Added path into schedule() to quickly re-evaluate scheduling
decision without becoming preemptive again. This used to be
a standard path before the removal of BKL.
Conflicts:
Makefile
arch/arm/kernel/calls.S
arch/arm/kernel/smp.c
arch/x86/include/asm/unistd_32.h
arch/x86/kernel/smp.c
arch/x86/kernel/syscall_table_32.S
include/linux/hrtimer.h
kernel/printk.c
kernel/sched.c
kernel/sched_fair.c
Diffstat (limited to 'kernel/time/posix-clock.c')
-rw-r--r-- | kernel/time/posix-clock.c | 445 |
1 files changed, 445 insertions, 0 deletions
diff --git a/kernel/time/posix-clock.c b/kernel/time/posix-clock.c new file mode 100644 index 000000000000..c340ca658f37 --- /dev/null +++ b/kernel/time/posix-clock.c | |||
@@ -0,0 +1,445 @@ | |||
1 | /* | ||
2 | * posix-clock.c - support for dynamic clock devices | ||
3 | * | ||
4 | * Copyright (C) 2010 OMICRON electronics GmbH | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
19 | */ | ||
20 | #include <linux/device.h> | ||
21 | #include <linux/file.h> | ||
22 | #include <linux/posix-clock.h> | ||
23 | #include <linux/slab.h> | ||
24 | #include <linux/syscalls.h> | ||
25 | #include <linux/uaccess.h> | ||
26 | |||
27 | static void delete_clock(struct kref *kref); | ||
28 | |||
29 | /* | ||
30 | * Returns NULL if the posix_clock instance attached to 'fp' is old and stale. | ||
31 | */ | ||
32 | static struct posix_clock *get_posix_clock(struct file *fp) | ||
33 | { | ||
34 | struct posix_clock *clk = fp->private_data; | ||
35 | |||
36 | down_read(&clk->rwsem); | ||
37 | |||
38 | if (!clk->zombie) | ||
39 | return clk; | ||
40 | |||
41 | up_read(&clk->rwsem); | ||
42 | |||
43 | return NULL; | ||
44 | } | ||
45 | |||
46 | static void put_posix_clock(struct posix_clock *clk) | ||
47 | { | ||
48 | up_read(&clk->rwsem); | ||
49 | } | ||
50 | |||
51 | static ssize_t posix_clock_read(struct file *fp, char __user *buf, | ||
52 | size_t count, loff_t *ppos) | ||
53 | { | ||
54 | struct posix_clock *clk = get_posix_clock(fp); | ||
55 | int err = -EINVAL; | ||
56 | |||
57 | if (!clk) | ||
58 | return -ENODEV; | ||
59 | |||
60 | if (clk->ops.read) | ||
61 | err = clk->ops.read(clk, fp->f_flags, buf, count); | ||
62 | |||
63 | put_posix_clock(clk); | ||
64 | |||
65 | return err; | ||
66 | } | ||
67 | |||
68 | static unsigned int posix_clock_poll(struct file *fp, poll_table *wait) | ||
69 | { | ||
70 | struct posix_clock *clk = get_posix_clock(fp); | ||
71 | int result = 0; | ||
72 | |||
73 | if (!clk) | ||
74 | return -ENODEV; | ||
75 | |||
76 | if (clk->ops.poll) | ||
77 | result = clk->ops.poll(clk, fp, wait); | ||
78 | |||
79 | put_posix_clock(clk); | ||
80 | |||
81 | return result; | ||
82 | } | ||
83 | |||
84 | static int posix_clock_fasync(int fd, struct file *fp, int on) | ||
85 | { | ||
86 | struct posix_clock *clk = get_posix_clock(fp); | ||
87 | int err = 0; | ||
88 | |||
89 | if (!clk) | ||
90 | return -ENODEV; | ||
91 | |||
92 | if (clk->ops.fasync) | ||
93 | err = clk->ops.fasync(clk, fd, fp, on); | ||
94 | |||
95 | put_posix_clock(clk); | ||
96 | |||
97 | return err; | ||
98 | } | ||
99 | |||
100 | static int posix_clock_mmap(struct file *fp, struct vm_area_struct *vma) | ||
101 | { | ||
102 | struct posix_clock *clk = get_posix_clock(fp); | ||
103 | int err = -ENODEV; | ||
104 | |||
105 | if (!clk) | ||
106 | return -ENODEV; | ||
107 | |||
108 | if (clk->ops.mmap) | ||
109 | err = clk->ops.mmap(clk, vma); | ||
110 | |||
111 | put_posix_clock(clk); | ||
112 | |||
113 | return err; | ||
114 | } | ||
115 | |||
116 | static long posix_clock_ioctl(struct file *fp, | ||
117 | unsigned int cmd, unsigned long arg) | ||
118 | { | ||
119 | struct posix_clock *clk = get_posix_clock(fp); | ||
120 | int err = -ENOTTY; | ||
121 | |||
122 | if (!clk) | ||
123 | return -ENODEV; | ||
124 | |||
125 | if (clk->ops.ioctl) | ||
126 | err = clk->ops.ioctl(clk, cmd, arg); | ||
127 | |||
128 | put_posix_clock(clk); | ||
129 | |||
130 | return err; | ||
131 | } | ||
132 | |||
133 | #ifdef CONFIG_COMPAT | ||
134 | static long posix_clock_compat_ioctl(struct file *fp, | ||
135 | unsigned int cmd, unsigned long arg) | ||
136 | { | ||
137 | struct posix_clock *clk = get_posix_clock(fp); | ||
138 | int err = -ENOTTY; | ||
139 | |||
140 | if (!clk) | ||
141 | return -ENODEV; | ||
142 | |||
143 | if (clk->ops.ioctl) | ||
144 | err = clk->ops.ioctl(clk, cmd, arg); | ||
145 | |||
146 | put_posix_clock(clk); | ||
147 | |||
148 | return err; | ||
149 | } | ||
150 | #endif | ||
151 | |||
152 | static int posix_clock_open(struct inode *inode, struct file *fp) | ||
153 | { | ||
154 | int err; | ||
155 | struct posix_clock *clk = | ||
156 | container_of(inode->i_cdev, struct posix_clock, cdev); | ||
157 | |||
158 | down_read(&clk->rwsem); | ||
159 | |||
160 | if (clk->zombie) { | ||
161 | err = -ENODEV; | ||
162 | goto out; | ||
163 | } | ||
164 | if (clk->ops.open) | ||
165 | err = clk->ops.open(clk, fp->f_mode); | ||
166 | else | ||
167 | err = 0; | ||
168 | |||
169 | if (!err) { | ||
170 | kref_get(&clk->kref); | ||
171 | fp->private_data = clk; | ||
172 | } | ||
173 | out: | ||
174 | up_read(&clk->rwsem); | ||
175 | return err; | ||
176 | } | ||
177 | |||
178 | static int posix_clock_release(struct inode *inode, struct file *fp) | ||
179 | { | ||
180 | struct posix_clock *clk = fp->private_data; | ||
181 | int err = 0; | ||
182 | |||
183 | if (clk->ops.release) | ||
184 | err = clk->ops.release(clk); | ||
185 | |||
186 | kref_put(&clk->kref, delete_clock); | ||
187 | |||
188 | fp->private_data = NULL; | ||
189 | |||
190 | return err; | ||
191 | } | ||
192 | |||
193 | static const struct file_operations posix_clock_file_operations = { | ||
194 | .owner = THIS_MODULE, | ||
195 | .llseek = no_llseek, | ||
196 | .read = posix_clock_read, | ||
197 | .poll = posix_clock_poll, | ||
198 | .unlocked_ioctl = posix_clock_ioctl, | ||
199 | .open = posix_clock_open, | ||
200 | .release = posix_clock_release, | ||
201 | .fasync = posix_clock_fasync, | ||
202 | .mmap = posix_clock_mmap, | ||
203 | #ifdef CONFIG_COMPAT | ||
204 | .compat_ioctl = posix_clock_compat_ioctl, | ||
205 | #endif | ||
206 | }; | ||
207 | |||
208 | int posix_clock_register(struct posix_clock *clk, dev_t devid) | ||
209 | { | ||
210 | int err; | ||
211 | |||
212 | kref_init(&clk->kref); | ||
213 | init_rwsem(&clk->rwsem); | ||
214 | |||
215 | cdev_init(&clk->cdev, &posix_clock_file_operations); | ||
216 | clk->cdev.owner = clk->ops.owner; | ||
217 | err = cdev_add(&clk->cdev, devid, 1); | ||
218 | |||
219 | return err; | ||
220 | } | ||
221 | EXPORT_SYMBOL_GPL(posix_clock_register); | ||
222 | |||
223 | static void delete_clock(struct kref *kref) | ||
224 | { | ||
225 | struct posix_clock *clk = container_of(kref, struct posix_clock, kref); | ||
226 | |||
227 | if (clk->release) | ||
228 | clk->release(clk); | ||
229 | } | ||
230 | |||
231 | void posix_clock_unregister(struct posix_clock *clk) | ||
232 | { | ||
233 | cdev_del(&clk->cdev); | ||
234 | |||
235 | down_write(&clk->rwsem); | ||
236 | clk->zombie = true; | ||
237 | up_write(&clk->rwsem); | ||
238 | |||
239 | kref_put(&clk->kref, delete_clock); | ||
240 | } | ||
241 | EXPORT_SYMBOL_GPL(posix_clock_unregister); | ||
242 | |||
243 | struct posix_clock_desc { | ||
244 | struct file *fp; | ||
245 | struct posix_clock *clk; | ||
246 | }; | ||
247 | |||
248 | static int get_clock_desc(const clockid_t id, struct posix_clock_desc *cd) | ||
249 | { | ||
250 | struct file *fp = fget(CLOCKID_TO_FD(id)); | ||
251 | int err = -EINVAL; | ||
252 | |||
253 | if (!fp) | ||
254 | return err; | ||
255 | |||
256 | if (fp->f_op->open != posix_clock_open || !fp->private_data) | ||
257 | goto out; | ||
258 | |||
259 | cd->fp = fp; | ||
260 | cd->clk = get_posix_clock(fp); | ||
261 | |||
262 | err = cd->clk ? 0 : -ENODEV; | ||
263 | out: | ||
264 | if (err) | ||
265 | fput(fp); | ||
266 | return err; | ||
267 | } | ||
268 | |||
269 | static void put_clock_desc(struct posix_clock_desc *cd) | ||
270 | { | ||
271 | put_posix_clock(cd->clk); | ||
272 | fput(cd->fp); | ||
273 | } | ||
274 | |||
275 | static int pc_clock_adjtime(clockid_t id, struct timex *tx) | ||
276 | { | ||
277 | struct posix_clock_desc cd; | ||
278 | int err; | ||
279 | |||
280 | err = get_clock_desc(id, &cd); | ||
281 | if (err) | ||
282 | return err; | ||
283 | |||
284 | if ((cd.fp->f_mode & FMODE_WRITE) == 0) { | ||
285 | err = -EACCES; | ||
286 | goto out; | ||
287 | } | ||
288 | |||
289 | if (cd.clk->ops.clock_adjtime) | ||
290 | err = cd.clk->ops.clock_adjtime(cd.clk, tx); | ||
291 | else | ||
292 | err = -EOPNOTSUPP; | ||
293 | out: | ||
294 | put_clock_desc(&cd); | ||
295 | |||
296 | return err; | ||
297 | } | ||
298 | |||
299 | static int pc_clock_gettime(clockid_t id, struct timespec *ts) | ||
300 | { | ||
301 | struct posix_clock_desc cd; | ||
302 | int err; | ||
303 | |||
304 | err = get_clock_desc(id, &cd); | ||
305 | if (err) | ||
306 | return err; | ||
307 | |||
308 | if (cd.clk->ops.clock_gettime) | ||
309 | err = cd.clk->ops.clock_gettime(cd.clk, ts); | ||
310 | else | ||
311 | err = -EOPNOTSUPP; | ||
312 | |||
313 | put_clock_desc(&cd); | ||
314 | |||
315 | return err; | ||
316 | } | ||
317 | |||
318 | static int pc_clock_getres(clockid_t id, struct timespec *ts) | ||
319 | { | ||
320 | struct posix_clock_desc cd; | ||
321 | int err; | ||
322 | |||
323 | err = get_clock_desc(id, &cd); | ||
324 | if (err) | ||
325 | return err; | ||
326 | |||
327 | if (cd.clk->ops.clock_getres) | ||
328 | err = cd.clk->ops.clock_getres(cd.clk, ts); | ||
329 | else | ||
330 | err = -EOPNOTSUPP; | ||
331 | |||
332 | put_clock_desc(&cd); | ||
333 | |||
334 | return err; | ||
335 | } | ||
336 | |||
337 | static int pc_clock_settime(clockid_t id, const struct timespec *ts) | ||
338 | { | ||
339 | struct posix_clock_desc cd; | ||
340 | int err; | ||
341 | |||
342 | err = get_clock_desc(id, &cd); | ||
343 | if (err) | ||
344 | return err; | ||
345 | |||
346 | if ((cd.fp->f_mode & FMODE_WRITE) == 0) { | ||
347 | err = -EACCES; | ||
348 | goto out; | ||
349 | } | ||
350 | |||
351 | if (cd.clk->ops.clock_settime) | ||
352 | err = cd.clk->ops.clock_settime(cd.clk, ts); | ||
353 | else | ||
354 | err = -EOPNOTSUPP; | ||
355 | out: | ||
356 | put_clock_desc(&cd); | ||
357 | |||
358 | return err; | ||
359 | } | ||
360 | |||
361 | static int pc_timer_create(struct k_itimer *kit) | ||
362 | { | ||
363 | clockid_t id = kit->it_clock; | ||
364 | struct posix_clock_desc cd; | ||
365 | int err; | ||
366 | |||
367 | err = get_clock_desc(id, &cd); | ||
368 | if (err) | ||
369 | return err; | ||
370 | |||
371 | if (cd.clk->ops.timer_create) | ||
372 | err = cd.clk->ops.timer_create(cd.clk, kit); | ||
373 | else | ||
374 | err = -EOPNOTSUPP; | ||
375 | |||
376 | put_clock_desc(&cd); | ||
377 | |||
378 | return err; | ||
379 | } | ||
380 | |||
381 | static int pc_timer_delete(struct k_itimer *kit) | ||
382 | { | ||
383 | clockid_t id = kit->it_clock; | ||
384 | struct posix_clock_desc cd; | ||
385 | int err; | ||
386 | |||
387 | err = get_clock_desc(id, &cd); | ||
388 | if (err) | ||
389 | return err; | ||
390 | |||
391 | if (cd.clk->ops.timer_delete) | ||
392 | err = cd.clk->ops.timer_delete(cd.clk, kit); | ||
393 | else | ||
394 | err = -EOPNOTSUPP; | ||
395 | |||
396 | put_clock_desc(&cd); | ||
397 | |||
398 | return err; | ||
399 | } | ||
400 | |||
401 | static void pc_timer_gettime(struct k_itimer *kit, struct itimerspec *ts) | ||
402 | { | ||
403 | clockid_t id = kit->it_clock; | ||
404 | struct posix_clock_desc cd; | ||
405 | |||
406 | if (get_clock_desc(id, &cd)) | ||
407 | return; | ||
408 | |||
409 | if (cd.clk->ops.timer_gettime) | ||
410 | cd.clk->ops.timer_gettime(cd.clk, kit, ts); | ||
411 | |||
412 | put_clock_desc(&cd); | ||
413 | } | ||
414 | |||
415 | static int pc_timer_settime(struct k_itimer *kit, int flags, | ||
416 | struct itimerspec *ts, struct itimerspec *old) | ||
417 | { | ||
418 | clockid_t id = kit->it_clock; | ||
419 | struct posix_clock_desc cd; | ||
420 | int err; | ||
421 | |||
422 | err = get_clock_desc(id, &cd); | ||
423 | if (err) | ||
424 | return err; | ||
425 | |||
426 | if (cd.clk->ops.timer_settime) | ||
427 | err = cd.clk->ops.timer_settime(cd.clk, kit, flags, ts, old); | ||
428 | else | ||
429 | err = -EOPNOTSUPP; | ||
430 | |||
431 | put_clock_desc(&cd); | ||
432 | |||
433 | return err; | ||
434 | } | ||
435 | |||
436 | struct k_clock clock_posix_dynamic = { | ||
437 | .clock_getres = pc_clock_getres, | ||
438 | .clock_set = pc_clock_settime, | ||
439 | .clock_get = pc_clock_gettime, | ||
440 | .clock_adj = pc_clock_adjtime, | ||
441 | .timer_create = pc_timer_create, | ||
442 | .timer_set = pc_timer_settime, | ||
443 | .timer_del = pc_timer_delete, | ||
444 | .timer_get = pc_timer_gettime, | ||
445 | }; | ||