diff options
| -rw-r--r-- | kernel/time/clocksource.c | 50 |
1 files changed, 34 insertions, 16 deletions
diff --git a/kernel/time/clocksource.c b/kernel/time/clocksource.c index 0e974cface0b..c3d2b94723dc 100644 --- a/kernel/time/clocksource.c +++ b/kernel/time/clocksource.c | |||
| @@ -119,6 +119,16 @@ static DEFINE_SPINLOCK(watchdog_lock); | |||
| 119 | static int watchdog_running; | 119 | static int watchdog_running; |
| 120 | static atomic_t watchdog_reset_pending; | 120 | static atomic_t watchdog_reset_pending; |
| 121 | 121 | ||
| 122 | static void inline clocksource_watchdog_lock(unsigned long *flags) | ||
| 123 | { | ||
| 124 | spin_lock_irqsave(&watchdog_lock, *flags); | ||
| 125 | } | ||
| 126 | |||
| 127 | static void inline clocksource_watchdog_unlock(unsigned long *flags) | ||
| 128 | { | ||
| 129 | spin_unlock_irqrestore(&watchdog_lock, *flags); | ||
| 130 | } | ||
| 131 | |||
| 122 | static int clocksource_watchdog_kthread(void *data); | 132 | static int clocksource_watchdog_kthread(void *data); |
| 123 | static void __clocksource_change_rating(struct clocksource *cs, int rating); | 133 | static void __clocksource_change_rating(struct clocksource *cs, int rating); |
| 124 | 134 | ||
| @@ -142,6 +152,9 @@ static void __clocksource_unstable(struct clocksource *cs) | |||
| 142 | cs->flags &= ~(CLOCK_SOURCE_VALID_FOR_HRES | CLOCK_SOURCE_WATCHDOG); | 152 | cs->flags &= ~(CLOCK_SOURCE_VALID_FOR_HRES | CLOCK_SOURCE_WATCHDOG); |
| 143 | cs->flags |= CLOCK_SOURCE_UNSTABLE; | 153 | cs->flags |= CLOCK_SOURCE_UNSTABLE; |
| 144 | 154 | ||
| 155 | if (list_empty(&cs->list)) | ||
| 156 | return; | ||
| 157 | |||
| 145 | if (cs->mark_unstable) | 158 | if (cs->mark_unstable) |
| 146 | cs->mark_unstable(cs); | 159 | cs->mark_unstable(cs); |
| 147 | 160 | ||
| @@ -164,7 +177,7 @@ void clocksource_mark_unstable(struct clocksource *cs) | |||
| 164 | 177 | ||
| 165 | spin_lock_irqsave(&watchdog_lock, flags); | 178 | spin_lock_irqsave(&watchdog_lock, flags); |
| 166 | if (!(cs->flags & CLOCK_SOURCE_UNSTABLE)) { | 179 | if (!(cs->flags & CLOCK_SOURCE_UNSTABLE)) { |
| 167 | if (list_empty(&cs->wd_list)) | 180 | if (!list_empty(&cs->list) && list_empty(&cs->wd_list)) |
| 168 | list_add(&cs->wd_list, &watchdog_list); | 181 | list_add(&cs->wd_list, &watchdog_list); |
| 169 | __clocksource_unstable(cs); | 182 | __clocksource_unstable(cs); |
| 170 | } | 183 | } |
| @@ -319,9 +332,6 @@ static void clocksource_resume_watchdog(void) | |||
| 319 | 332 | ||
| 320 | static void clocksource_enqueue_watchdog(struct clocksource *cs) | 333 | static void clocksource_enqueue_watchdog(struct clocksource *cs) |
| 321 | { | 334 | { |
| 322 | unsigned long flags; | ||
| 323 | |||
| 324 | spin_lock_irqsave(&watchdog_lock, flags); | ||
| 325 | if (cs->flags & CLOCK_SOURCE_MUST_VERIFY) { | 335 | if (cs->flags & CLOCK_SOURCE_MUST_VERIFY) { |
| 326 | /* cs is a clocksource to be watched. */ | 336 | /* cs is a clocksource to be watched. */ |
| 327 | list_add(&cs->wd_list, &watchdog_list); | 337 | list_add(&cs->wd_list, &watchdog_list); |
| @@ -331,7 +341,6 @@ static void clocksource_enqueue_watchdog(struct clocksource *cs) | |||
| 331 | if (cs->flags & CLOCK_SOURCE_IS_CONTINUOUS) | 341 | if (cs->flags & CLOCK_SOURCE_IS_CONTINUOUS) |
| 332 | cs->flags |= CLOCK_SOURCE_VALID_FOR_HRES; | 342 | cs->flags |= CLOCK_SOURCE_VALID_FOR_HRES; |
| 333 | } | 343 | } |
| 334 | spin_unlock_irqrestore(&watchdog_lock, flags); | ||
| 335 | } | 344 | } |
| 336 | 345 | ||
| 337 | static void clocksource_select_watchdog(bool fallback) | 346 | static void clocksource_select_watchdog(bool fallback) |
| @@ -373,9 +382,6 @@ static void clocksource_select_watchdog(bool fallback) | |||
| 373 | 382 | ||
| 374 | static void clocksource_dequeue_watchdog(struct clocksource *cs) | 383 | static void clocksource_dequeue_watchdog(struct clocksource *cs) |
| 375 | { | 384 | { |
| 376 | unsigned long flags; | ||
| 377 | |||
| 378 | spin_lock_irqsave(&watchdog_lock, flags); | ||
| 379 | if (cs != watchdog) { | 385 | if (cs != watchdog) { |
| 380 | if (cs->flags & CLOCK_SOURCE_MUST_VERIFY) { | 386 | if (cs->flags & CLOCK_SOURCE_MUST_VERIFY) { |
| 381 | /* cs is a watched clocksource. */ | 387 | /* cs is a watched clocksource. */ |
| @@ -384,21 +390,19 @@ static void clocksource_dequeue_watchdog(struct clocksource *cs) | |||
| 384 | clocksource_stop_watchdog(); | 390 | clocksource_stop_watchdog(); |
| 385 | } | 391 | } |
| 386 | } | 392 | } |
| 387 | spin_unlock_irqrestore(&watchdog_lock, flags); | ||
| 388 | } | 393 | } |
| 389 | 394 | ||
| 390 | static int __clocksource_watchdog_kthread(void) | 395 | static int __clocksource_watchdog_kthread(void) |
| 391 | { | 396 | { |
| 392 | struct clocksource *cs, *tmp; | 397 | struct clocksource *cs, *tmp; |
| 393 | unsigned long flags; | 398 | unsigned long flags; |
| 394 | LIST_HEAD(unstable); | ||
| 395 | int select = 0; | 399 | int select = 0; |
| 396 | 400 | ||
| 397 | spin_lock_irqsave(&watchdog_lock, flags); | 401 | spin_lock_irqsave(&watchdog_lock, flags); |
| 398 | list_for_each_entry_safe(cs, tmp, &watchdog_list, wd_list) { | 402 | list_for_each_entry_safe(cs, tmp, &watchdog_list, wd_list) { |
| 399 | if (cs->flags & CLOCK_SOURCE_UNSTABLE) { | 403 | if (cs->flags & CLOCK_SOURCE_UNSTABLE) { |
| 400 | list_del_init(&cs->wd_list); | 404 | list_del_init(&cs->wd_list); |
| 401 | list_add(&cs->wd_list, &unstable); | 405 | __clocksource_change_rating(cs, 0); |
| 402 | select = 1; | 406 | select = 1; |
| 403 | } | 407 | } |
| 404 | if (cs->flags & CLOCK_SOURCE_RESELECT) { | 408 | if (cs->flags & CLOCK_SOURCE_RESELECT) { |
| @@ -410,11 +414,6 @@ static int __clocksource_watchdog_kthread(void) | |||
| 410 | clocksource_stop_watchdog(); | 414 | clocksource_stop_watchdog(); |
| 411 | spin_unlock_irqrestore(&watchdog_lock, flags); | 415 | spin_unlock_irqrestore(&watchdog_lock, flags); |
| 412 | 416 | ||
| 413 | /* Needs to be done outside of watchdog lock */ | ||
| 414 | list_for_each_entry_safe(cs, tmp, &unstable, wd_list) { | ||
| 415 | list_del_init(&cs->wd_list); | ||
| 416 | __clocksource_change_rating(cs, 0); | ||
| 417 | } | ||
| 418 | return select; | 417 | return select; |
| 419 | } | 418 | } |
| 420 | 419 | ||
| @@ -447,6 +446,9 @@ static inline int __clocksource_watchdog_kthread(void) { return 0; } | |||
| 447 | static bool clocksource_is_watchdog(struct clocksource *cs) { return false; } | 446 | static bool clocksource_is_watchdog(struct clocksource *cs) { return false; } |
| 448 | void clocksource_mark_unstable(struct clocksource *cs) { } | 447 | void clocksource_mark_unstable(struct clocksource *cs) { } |
| 449 | 448 | ||
| 449 | static void inline clocksource_watchdog_lock(unsigned long *flags) { } | ||
| 450 | static void inline clocksource_watchdog_unlock(unsigned long *flags) { } | ||
| 451 | |||
| 450 | #endif /* CONFIG_CLOCKSOURCE_WATCHDOG */ | 452 | #endif /* CONFIG_CLOCKSOURCE_WATCHDOG */ |
| 451 | 453 | ||
| 452 | /** | 454 | /** |
| @@ -779,14 +781,19 @@ EXPORT_SYMBOL_GPL(__clocksource_update_freq_scale); | |||
| 779 | */ | 781 | */ |
| 780 | int __clocksource_register_scale(struct clocksource *cs, u32 scale, u32 freq) | 782 | int __clocksource_register_scale(struct clocksource *cs, u32 scale, u32 freq) |
| 781 | { | 783 | { |
| 784 | unsigned long flags; | ||
| 782 | 785 | ||
| 783 | /* Initialize mult/shift and max_idle_ns */ | 786 | /* Initialize mult/shift and max_idle_ns */ |
| 784 | __clocksource_update_freq_scale(cs, scale, freq); | 787 | __clocksource_update_freq_scale(cs, scale, freq); |
| 785 | 788 | ||
| 786 | /* Add clocksource to the clocksource list */ | 789 | /* Add clocksource to the clocksource list */ |
| 787 | mutex_lock(&clocksource_mutex); | 790 | mutex_lock(&clocksource_mutex); |
| 791 | |||
| 792 | clocksource_watchdog_lock(&flags); | ||
| 788 | clocksource_enqueue(cs); | 793 | clocksource_enqueue(cs); |
| 789 | clocksource_enqueue_watchdog(cs); | 794 | clocksource_enqueue_watchdog(cs); |
| 795 | clocksource_watchdog_unlock(&flags); | ||
| 796 | |||
| 790 | clocksource_select(); | 797 | clocksource_select(); |
| 791 | clocksource_select_watchdog(false); | 798 | clocksource_select_watchdog(false); |
| 792 | mutex_unlock(&clocksource_mutex); | 799 | mutex_unlock(&clocksource_mutex); |
| @@ -808,8 +815,13 @@ static void __clocksource_change_rating(struct clocksource *cs, int rating) | |||
| 808 | */ | 815 | */ |
| 809 | void clocksource_change_rating(struct clocksource *cs, int rating) | 816 | void clocksource_change_rating(struct clocksource *cs, int rating) |
| 810 | { | 817 | { |
| 818 | unsigned long flags; | ||
| 819 | |||
| 811 | mutex_lock(&clocksource_mutex); | 820 | mutex_lock(&clocksource_mutex); |
| 821 | clocksource_watchdog_lock(&flags); | ||
| 812 | __clocksource_change_rating(cs, rating); | 822 | __clocksource_change_rating(cs, rating); |
| 823 | clocksource_watchdog_unlock(&flags); | ||
| 824 | |||
| 813 | clocksource_select(); | 825 | clocksource_select(); |
| 814 | clocksource_select_watchdog(false); | 826 | clocksource_select_watchdog(false); |
| 815 | mutex_unlock(&clocksource_mutex); | 827 | mutex_unlock(&clocksource_mutex); |
| @@ -821,6 +833,8 @@ EXPORT_SYMBOL(clocksource_change_rating); | |||
| 821 | */ | 833 | */ |
| 822 | static int clocksource_unbind(struct clocksource *cs) | 834 | static int clocksource_unbind(struct clocksource *cs) |
| 823 | { | 835 | { |
| 836 | unsigned long flags; | ||
| 837 | |||
| 824 | if (clocksource_is_watchdog(cs)) { | 838 | if (clocksource_is_watchdog(cs)) { |
| 825 | /* Select and try to install a replacement watchdog. */ | 839 | /* Select and try to install a replacement watchdog. */ |
| 826 | clocksource_select_watchdog(true); | 840 | clocksource_select_watchdog(true); |
| @@ -834,8 +848,12 @@ static int clocksource_unbind(struct clocksource *cs) | |||
| 834 | if (curr_clocksource == cs) | 848 | if (curr_clocksource == cs) |
| 835 | return -EBUSY; | 849 | return -EBUSY; |
| 836 | } | 850 | } |
| 851 | |||
| 852 | clocksource_watchdog_lock(&flags); | ||
| 837 | clocksource_dequeue_watchdog(cs); | 853 | clocksource_dequeue_watchdog(cs); |
| 838 | list_del_init(&cs->list); | 854 | list_del_init(&cs->list); |
| 855 | clocksource_watchdog_unlock(&flags); | ||
| 856 | |||
| 839 | return 0; | 857 | return 0; |
| 840 | } | 858 | } |
| 841 | 859 | ||
