diff options
| author | Bjoern B. Brandenburg <bbb@cs.unc.edu> | 2009-04-22 23:01:44 -0400 |
|---|---|---|
| committer | Bjoern B. Brandenburg <bbb@cs.unc.edu> | 2009-04-22 23:01:44 -0400 |
| commit | b5c4ababa76ce94cfa276f620b81d7d0f1d8ebf1 (patch) | |
| tree | bc8c705a7b1b8e11c6789cecb5f6d9c3be09dad0 | |
| parent | fc757f4223edfab215d6e49cee479a09ecc17962 (diff) | |
hrtimer: add support for starting timers on remote CPUs
This depends on the ability to send the special 'pull_timers' IPI
even with IRQs disabled.
| -rw-r--r-- | include/linux/hrtimer.h | 16 | ||||
| -rw-r--r-- | include/linux/smp.h | 6 | ||||
| -rw-r--r-- | kernel/hrtimer.c | 67 |
3 files changed, 89 insertions, 0 deletions
diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h index 7a9398e197..4f45c83151 100644 --- a/include/linux/hrtimer.h +++ b/include/linux/hrtimer.h | |||
| @@ -200,6 +200,19 @@ struct hrtimer_cpu_base { | |||
| 200 | struct list_head cb_pending; | 200 | struct list_head cb_pending; |
| 201 | unsigned long nr_events; | 201 | unsigned long nr_events; |
| 202 | #endif | 202 | #endif |
| 203 | struct list_head to_pull; | ||
| 204 | }; | ||
| 205 | |||
| 206 | #define HRTIMER_START_ON_INACTIVE 0 | ||
| 207 | #define HRTIMER_START_ON_QUEUED 1 | ||
| 208 | #define HRTIMER_START_ON_PROCESSED 2 | ||
| 209 | |||
| 210 | struct hrtimer_start_on_info { | ||
| 211 | struct list_head list; | ||
| 212 | struct hrtimer* timer; | ||
| 213 | ktime_t time; | ||
| 214 | enum hrtimer_mode mode; | ||
| 215 | atomic_t state; | ||
| 203 | }; | 216 | }; |
| 204 | 217 | ||
| 205 | #ifdef CONFIG_HIGH_RES_TIMERS | 218 | #ifdef CONFIG_HIGH_RES_TIMERS |
| @@ -262,6 +275,9 @@ extern void hrtimer_init(struct hrtimer *timer, clockid_t which_clock, | |||
| 262 | /* Basic timer operations: */ | 275 | /* Basic timer operations: */ |
| 263 | extern int hrtimer_start(struct hrtimer *timer, ktime_t tim, | 276 | extern int hrtimer_start(struct hrtimer *timer, ktime_t tim, |
| 264 | const enum hrtimer_mode mode); | 277 | const enum hrtimer_mode mode); |
| 278 | extern int hrtimer_start_on(int cpu, struct hrtimer_start_on_info* info, | ||
| 279 | struct hrtimer *timer, ktime_t time, | ||
| 280 | const enum hrtimer_mode mode); | ||
| 265 | extern int hrtimer_cancel(struct hrtimer *timer); | 281 | extern int hrtimer_cancel(struct hrtimer *timer); |
| 266 | extern int hrtimer_try_to_cancel(struct hrtimer *timer); | 282 | extern int hrtimer_try_to_cancel(struct hrtimer *timer); |
| 267 | 283 | ||
diff --git a/include/linux/smp.h b/include/linux/smp.h index c25e66bcec..84df090ce4 100644 --- a/include/linux/smp.h +++ b/include/linux/smp.h | |||
| @@ -35,6 +35,12 @@ extern void smp_send_reschedule(int cpu); | |||
| 35 | 35 | ||
| 36 | 36 | ||
| 37 | /* | 37 | /* |
| 38 | * sends a 'pull timers' event to another CPU: | ||
| 39 | */ | ||
| 40 | extern void smp_send_pull_timers(int cpu); | ||
| 41 | |||
| 42 | |||
| 43 | /* | ||
| 38 | * Prepare machine for booting other CPUs. | 44 | * Prepare machine for booting other CPUs. |
| 39 | */ | 45 | */ |
| 40 | extern void smp_prepare_cpus(unsigned int max_cpus); | 46 | extern void smp_prepare_cpus(unsigned int max_cpus); |
diff --git a/kernel/hrtimer.c b/kernel/hrtimer.c index f994bb8065..c47baab445 100644 --- a/kernel/hrtimer.c +++ b/kernel/hrtimer.c | |||
| @@ -813,6 +813,73 @@ remove_hrtimer(struct hrtimer *timer, struct hrtimer_clock_base *base) | |||
| 813 | return 0; | 813 | return 0; |
| 814 | } | 814 | } |
| 815 | 815 | ||
| 816 | void hrtimer_pull(void) | ||
| 817 | { | ||
| 818 | struct hrtimer_cpu_base* base = &__get_cpu_var(hrtimer_bases); | ||
| 819 | struct hrtimer_start_on_info* info; | ||
| 820 | struct list_head *pos, *safe, list; | ||
| 821 | |||
| 822 | spin_lock(&base->lock); | ||
| 823 | list_replace_init(&base->to_pull, &list); | ||
| 824 | spin_unlock(&base->lock); | ||
| 825 | |||
| 826 | list_for_each_safe(pos, safe, &list) { | ||
| 827 | info = list_entry(pos, struct hrtimer_start_on_info, list); | ||
| 828 | TRACE("pulled timer 0x%x\n", info->timer); | ||
| 829 | list_del(pos); | ||
| 830 | hrtimer_start(info->timer, info->time, info->mode); | ||
| 831 | mb(); | ||
| 832 | atomic_set(&info->state, HRTIMER_START_ON_PROCESSED); | ||
| 833 | } | ||
| 834 | } | ||
| 835 | |||
| 836 | int hrtimer_start_on(int cpu, struct hrtimer_start_on_info* info, | ||
| 837 | struct hrtimer *timer, ktime_t time, | ||
| 838 | const enum hrtimer_mode mode) | ||
| 839 | { | ||
| 840 | unsigned long flags; | ||
| 841 | struct hrtimer_cpu_base* base; | ||
| 842 | int in_use = 0, was_empty; | ||
| 843 | |||
| 844 | /* serialize access to info through the timer base */ | ||
| 845 | lock_hrtimer_base(timer, &flags); | ||
| 846 | |||
| 847 | in_use = atomic_read(&info->state) != HRTIMER_START_ON_INACTIVE; | ||
| 848 | if (!in_use) { | ||
| 849 | INIT_LIST_HEAD(&info->list); | ||
| 850 | info->timer = timer; | ||
| 851 | info->time = time; | ||
| 852 | info->mode = mode; | ||
| 853 | /* mark as in use */ | ||
| 854 | atomic_set(&info->state, HRTIMER_START_ON_QUEUED); | ||
| 855 | } | ||
| 856 | |||
| 857 | unlock_hrtimer_base(timer, &flags); | ||
| 858 | |||
| 859 | if (!in_use) { | ||
| 860 | /* initiate pull */ | ||
| 861 | preempt_disable(); | ||
| 862 | if (cpu == smp_processor_id()) { | ||
| 863 | /* start timer locally */ | ||
| 864 | hrtimer_start(info->timer, info->time, info->mode); | ||
| 865 | atomic_set(&info->state, HRTIMER_START_ON_PROCESSED); | ||
| 866 | } else { | ||
| 867 | base = &per_cpu(hrtimer_bases, cpu); | ||
| 868 | spin_lock_irqsave(&base->lock, flags); | ||
| 869 | was_empty = list_empty(&base->to_pull); | ||
| 870 | list_add(&info->list, &base->to_pull); | ||
| 871 | spin_unlock_irqrestore(&base->lock, flags); | ||
| 872 | if (was_empty) | ||
| 873 | /* only send IPI if other no else | ||
| 874 | * has done so already | ||
| 875 | */ | ||
| 876 | smp_send_pull_timers(cpu); | ||
| 877 | } | ||
| 878 | preempt_enable(); | ||
| 879 | } | ||
| 880 | return in_use; | ||
| 881 | } | ||
| 882 | |||
| 816 | /** | 883 | /** |
| 817 | * hrtimer_start - (re)start an relative timer on the current CPU | 884 | * hrtimer_start - (re)start an relative timer on the current CPU |
| 818 | * @timer: the timer to be added | 885 | * @timer: the timer to be added |
