diff options
Diffstat (limited to 'kernel/posix-timers.c')
-rw-r--r-- | kernel/posix-timers.c | 121 |
1 files changed, 84 insertions, 37 deletions
diff --git a/kernel/posix-timers.c b/kernel/posix-timers.c index 6edbb2c55c22..424c2d4265c9 100644 --- a/kernel/posix-timers.c +++ b/kernel/posix-timers.c | |||
@@ -40,38 +40,31 @@ | |||
40 | #include <linux/list.h> | 40 | #include <linux/list.h> |
41 | #include <linux/init.h> | 41 | #include <linux/init.h> |
42 | #include <linux/compiler.h> | 42 | #include <linux/compiler.h> |
43 | #include <linux/idr.h> | 43 | #include <linux/hash.h> |
44 | #include <linux/posix-clock.h> | 44 | #include <linux/posix-clock.h> |
45 | #include <linux/posix-timers.h> | 45 | #include <linux/posix-timers.h> |
46 | #include <linux/syscalls.h> | 46 | #include <linux/syscalls.h> |
47 | #include <linux/wait.h> | 47 | #include <linux/wait.h> |
48 | #include <linux/workqueue.h> | 48 | #include <linux/workqueue.h> |
49 | #include <linux/export.h> | 49 | #include <linux/export.h> |
50 | #include <linux/hashtable.h> | ||
50 | 51 | ||
51 | /* | 52 | /* |
52 | * Management arrays for POSIX timers. Timers are kept in slab memory | 53 | * Management arrays for POSIX timers. Timers are now kept in static hash table |
53 | * Timer ids are allocated by an external routine that keeps track of the | 54 | * with 512 entries. |
54 | * id and the timer. The external interface is: | 55 | * Timer ids are allocated by local routine, which selects proper hash head by |
55 | * | 56 | * key, constructed from current->signal address and per signal struct counter. |
56 | * void *idr_find(struct idr *idp, int id); to find timer_id <id> | 57 | * This keeps timer ids unique per process, but now they can intersect between |
57 | * int idr_get_new(struct idr *idp, void *ptr); to get a new id and | 58 | * processes. |
58 | * related it to <ptr> | ||
59 | * void idr_remove(struct idr *idp, int id); to release <id> | ||
60 | * void idr_init(struct idr *idp); to initialize <idp> | ||
61 | * which we supply. | ||
62 | * The idr_get_new *may* call slab for more memory so it must not be | ||
63 | * called under a spin lock. Likewise idr_remore may release memory | ||
64 | * (but it may be ok to do this under a lock...). | ||
65 | * idr_find is just a memory look up and is quite fast. A -1 return | ||
66 | * indicates that the requested id does not exist. | ||
67 | */ | 59 | */ |
68 | 60 | ||
69 | /* | 61 | /* |
70 | * Lets keep our timers in a slab cache :-) | 62 | * Lets keep our timers in a slab cache :-) |
71 | */ | 63 | */ |
72 | static struct kmem_cache *posix_timers_cache; | 64 | static struct kmem_cache *posix_timers_cache; |
73 | static struct idr posix_timers_id; | 65 | |
74 | static DEFINE_SPINLOCK(idr_lock); | 66 | static DEFINE_HASHTABLE(posix_timers_hashtable, 9); |
67 | static DEFINE_SPINLOCK(hash_lock); | ||
75 | 68 | ||
76 | /* | 69 | /* |
77 | * we assume that the new SIGEV_THREAD_ID shares no bits with the other | 70 | * we assume that the new SIGEV_THREAD_ID shares no bits with the other |
@@ -152,6 +145,56 @@ static struct k_itimer *__lock_timer(timer_t timer_id, unsigned long *flags); | |||
152 | __timr; \ | 145 | __timr; \ |
153 | }) | 146 | }) |
154 | 147 | ||
148 | static int hash(struct signal_struct *sig, unsigned int nr) | ||
149 | { | ||
150 | return hash_32(hash32_ptr(sig) ^ nr, HASH_BITS(posix_timers_hashtable)); | ||
151 | } | ||
152 | |||
153 | static struct k_itimer *__posix_timers_find(struct hlist_head *head, | ||
154 | struct signal_struct *sig, | ||
155 | timer_t id) | ||
156 | { | ||
157 | struct k_itimer *timer; | ||
158 | |||
159 | hlist_for_each_entry_rcu(timer, head, t_hash) { | ||
160 | if ((timer->it_signal == sig) && (timer->it_id == id)) | ||
161 | return timer; | ||
162 | } | ||
163 | return NULL; | ||
164 | } | ||
165 | |||
166 | static struct k_itimer *posix_timer_by_id(timer_t id) | ||
167 | { | ||
168 | struct signal_struct *sig = current->signal; | ||
169 | struct hlist_head *head = &posix_timers_hashtable[hash(sig, id)]; | ||
170 | |||
171 | return __posix_timers_find(head, sig, id); | ||
172 | } | ||
173 | |||
174 | static int posix_timer_add(struct k_itimer *timer) | ||
175 | { | ||
176 | struct signal_struct *sig = current->signal; | ||
177 | int first_free_id = sig->posix_timer_id; | ||
178 | struct hlist_head *head; | ||
179 | int ret = -ENOENT; | ||
180 | |||
181 | do { | ||
182 | spin_lock(&hash_lock); | ||
183 | head = &posix_timers_hashtable[hash(sig, sig->posix_timer_id)]; | ||
184 | if (!__posix_timers_find(head, sig, sig->posix_timer_id)) { | ||
185 | hlist_add_head_rcu(&timer->t_hash, head); | ||
186 | ret = sig->posix_timer_id; | ||
187 | } | ||
188 | if (++sig->posix_timer_id < 0) | ||
189 | sig->posix_timer_id = 0; | ||
190 | if ((sig->posix_timer_id == first_free_id) && (ret == -ENOENT)) | ||
191 | /* Loop over all possible ids completed */ | ||
192 | ret = -EAGAIN; | ||
193 | spin_unlock(&hash_lock); | ||
194 | } while (ret == -ENOENT); | ||
195 | return ret; | ||
196 | } | ||
197 | |||
155 | static inline void unlock_timer(struct k_itimer *timr, unsigned long flags) | 198 | static inline void unlock_timer(struct k_itimer *timr, unsigned long flags) |
156 | { | 199 | { |
157 | spin_unlock_irqrestore(&timr->it_lock, flags); | 200 | spin_unlock_irqrestore(&timr->it_lock, flags); |
@@ -221,6 +264,11 @@ static int posix_get_boottime(const clockid_t which_clock, struct timespec *tp) | |||
221 | return 0; | 264 | return 0; |
222 | } | 265 | } |
223 | 266 | ||
267 | static int posix_get_tai(clockid_t which_clock, struct timespec *tp) | ||
268 | { | ||
269 | timekeeping_clocktai(tp); | ||
270 | return 0; | ||
271 | } | ||
224 | 272 | ||
225 | /* | 273 | /* |
226 | * Initialize everything, well, just everything in Posix clocks/timers ;) | 274 | * Initialize everything, well, just everything in Posix clocks/timers ;) |
@@ -261,6 +309,16 @@ static __init int init_posix_timers(void) | |||
261 | .clock_getres = posix_get_coarse_res, | 309 | .clock_getres = posix_get_coarse_res, |
262 | .clock_get = posix_get_monotonic_coarse, | 310 | .clock_get = posix_get_monotonic_coarse, |
263 | }; | 311 | }; |
312 | struct k_clock clock_tai = { | ||
313 | .clock_getres = hrtimer_get_res, | ||
314 | .clock_get = posix_get_tai, | ||
315 | .nsleep = common_nsleep, | ||
316 | .nsleep_restart = hrtimer_nanosleep_restart, | ||
317 | .timer_create = common_timer_create, | ||
318 | .timer_set = common_timer_set, | ||
319 | .timer_get = common_timer_get, | ||
320 | .timer_del = common_timer_del, | ||
321 | }; | ||
264 | struct k_clock clock_boottime = { | 322 | struct k_clock clock_boottime = { |
265 | .clock_getres = hrtimer_get_res, | 323 | .clock_getres = hrtimer_get_res, |
266 | .clock_get = posix_get_boottime, | 324 | .clock_get = posix_get_boottime, |
@@ -278,11 +336,11 @@ static __init int init_posix_timers(void) | |||
278 | posix_timers_register_clock(CLOCK_REALTIME_COARSE, &clock_realtime_coarse); | 336 | posix_timers_register_clock(CLOCK_REALTIME_COARSE, &clock_realtime_coarse); |
279 | posix_timers_register_clock(CLOCK_MONOTONIC_COARSE, &clock_monotonic_coarse); | 337 | posix_timers_register_clock(CLOCK_MONOTONIC_COARSE, &clock_monotonic_coarse); |
280 | posix_timers_register_clock(CLOCK_BOOTTIME, &clock_boottime); | 338 | posix_timers_register_clock(CLOCK_BOOTTIME, &clock_boottime); |
339 | posix_timers_register_clock(CLOCK_TAI, &clock_tai); | ||
281 | 340 | ||
282 | posix_timers_cache = kmem_cache_create("posix_timers_cache", | 341 | posix_timers_cache = kmem_cache_create("posix_timers_cache", |
283 | sizeof (struct k_itimer), 0, SLAB_PANIC, | 342 | sizeof (struct k_itimer), 0, SLAB_PANIC, |
284 | NULL); | 343 | NULL); |
285 | idr_init(&posix_timers_id); | ||
286 | return 0; | 344 | return 0; |
287 | } | 345 | } |
288 | 346 | ||
@@ -504,9 +562,9 @@ static void release_posix_timer(struct k_itimer *tmr, int it_id_set) | |||
504 | { | 562 | { |
505 | if (it_id_set) { | 563 | if (it_id_set) { |
506 | unsigned long flags; | 564 | unsigned long flags; |
507 | spin_lock_irqsave(&idr_lock, flags); | 565 | spin_lock_irqsave(&hash_lock, flags); |
508 | idr_remove(&posix_timers_id, tmr->it_id); | 566 | hlist_del_rcu(&tmr->t_hash); |
509 | spin_unlock_irqrestore(&idr_lock, flags); | 567 | spin_unlock_irqrestore(&hash_lock, flags); |
510 | } | 568 | } |
511 | put_pid(tmr->it_pid); | 569 | put_pid(tmr->it_pid); |
512 | sigqueue_free(tmr->sigq); | 570 | sigqueue_free(tmr->sigq); |
@@ -552,22 +610,11 @@ SYSCALL_DEFINE3(timer_create, const clockid_t, which_clock, | |||
552 | return -EAGAIN; | 610 | return -EAGAIN; |
553 | 611 | ||
554 | spin_lock_init(&new_timer->it_lock); | 612 | spin_lock_init(&new_timer->it_lock); |
555 | 613 | new_timer_id = posix_timer_add(new_timer); | |
556 | idr_preload(GFP_KERNEL); | 614 | if (new_timer_id < 0) { |
557 | spin_lock_irq(&idr_lock); | 615 | error = new_timer_id; |
558 | error = idr_alloc(&posix_timers_id, new_timer, 0, 0, GFP_NOWAIT); | ||
559 | spin_unlock_irq(&idr_lock); | ||
560 | idr_preload_end(); | ||
561 | if (error < 0) { | ||
562 | /* | ||
563 | * Weird looking, but we return EAGAIN if the IDR is | ||
564 | * full (proper POSIX return value for this) | ||
565 | */ | ||
566 | if (error == -ENOSPC) | ||
567 | error = -EAGAIN; | ||
568 | goto out; | 616 | goto out; |
569 | } | 617 | } |
570 | new_timer_id = error; | ||
571 | 618 | ||
572 | it_id_set = IT_ID_SET; | 619 | it_id_set = IT_ID_SET; |
573 | new_timer->it_id = (timer_t) new_timer_id; | 620 | new_timer->it_id = (timer_t) new_timer_id; |
@@ -645,7 +692,7 @@ static struct k_itimer *__lock_timer(timer_t timer_id, unsigned long *flags) | |||
645 | return NULL; | 692 | return NULL; |
646 | 693 | ||
647 | rcu_read_lock(); | 694 | rcu_read_lock(); |
648 | timr = idr_find(&posix_timers_id, (int)timer_id); | 695 | timr = posix_timer_by_id(timer_id); |
649 | if (timr) { | 696 | if (timr) { |
650 | spin_lock_irqsave(&timr->it_lock, *flags); | 697 | spin_lock_irqsave(&timr->it_lock, *flags); |
651 | if (timr->it_signal == current->signal) { | 698 | if (timr->it_signal == current->signal) { |