aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Gleixner <tglx@linutronix.de>2009-08-28 14:25:24 -0400
committerThomas Gleixner <tglx@linutronix.de>2009-08-28 14:25:24 -0400
commit7285dd7fd375763bfb8ab1ac9cf3f1206f503c16 (patch)
tree42f809ab3616cc3d93d655acccfc2d54e9f6d0e4
parent90cba64a5f672a239f43ec5cb9a11b806887331e (diff)
clocksource: Resolve cpu hotplug dead lock with TSC unstable
Martin Schwidefsky analyzed it: To register a clocksource the clocksource_mutex is acquired and if necessary timekeeping_notify is called to install the clocksource as the timekeeper clock. timekeeping_notify uses stop_machine which needs to take cpu_add_remove_lock mutex. Starting a new cpu is done with the cpu_add_remove_lock mutex held. native_cpu_up checks the tsc of the new cpu and if the tsc is no good clocksource_change_rating is called. Which needs the clocksource_mutex and the deadlock is complete. The solution is to replace the TSC via the clocksource watchdog mechanism. Mark the TSC as unstable and schedule the watchdog work so it gets removed in the watchdog thread context. Signed-off-by: Thomas Gleixner <tglx@linutronix.de> LKML-Reference: <new-submission> Cc: Martin Schwidefsky <schwidefsky@de.ibm.com> Cc: John Stultz <johnstul@us.ibm.com>
-rw-r--r--arch/x86/kernel/tsc.c8
-rw-r--r--include/linux/clocksource.h1
-rw-r--r--kernel/time/clocksource.c33
3 files changed, 36 insertions, 6 deletions
diff --git a/arch/x86/kernel/tsc.c b/arch/x86/kernel/tsc.c
index 968425422c46..fc3672a303d6 100644
--- a/arch/x86/kernel/tsc.c
+++ b/arch/x86/kernel/tsc.c
@@ -767,12 +767,14 @@ void mark_tsc_unstable(char *reason)
767{ 767{
768 if (!tsc_unstable) { 768 if (!tsc_unstable) {
769 tsc_unstable = 1; 769 tsc_unstable = 1;
770 printk("Marking TSC unstable due to %s\n", reason); 770 printk(KERN_INFO "Marking TSC unstable due to %s\n", reason);
771 /* Change only the rating, when not registered */ 771 /* Change only the rating, when not registered */
772 if (clocksource_tsc.mult) 772 if (clocksource_tsc.mult)
773 clocksource_change_rating(&clocksource_tsc, 0); 773 clocksource_mark_unstable(&clocksource_tsc);
774 else 774 else {
775 clocksource_tsc.flags |= CLOCK_SOURCE_UNSTABLE;
775 clocksource_tsc.rating = 0; 776 clocksource_tsc.rating = 0;
777 }
776 } 778 }
777} 779}
778 780
diff --git a/include/linux/clocksource.h b/include/linux/clocksource.h
index 9ea40ff26f0e..83d2fbd81b93 100644
--- a/include/linux/clocksource.h
+++ b/include/linux/clocksource.h
@@ -277,6 +277,7 @@ extern struct clocksource* clocksource_get_next(void);
277extern void clocksource_change_rating(struct clocksource *cs, int rating); 277extern void clocksource_change_rating(struct clocksource *cs, int rating);
278extern void clocksource_resume(void); 278extern void clocksource_resume(void);
279extern struct clocksource * __init __weak clocksource_default_clock(void); 279extern struct clocksource * __init __weak clocksource_default_clock(void);
280extern void clocksource_mark_unstable(struct clocksource *cs);
280 281
281#ifdef CONFIG_GENERIC_TIME_VSYSCALL 282#ifdef CONFIG_GENERIC_TIME_VSYSCALL
282extern void update_vsyscall(struct timespec *ts, struct clocksource *c); 283extern void update_vsyscall(struct timespec *ts, struct clocksource *c);
diff --git a/kernel/time/clocksource.c b/kernel/time/clocksource.c
index e0c86ad6e9fb..a0af4ffcb6e5 100644
--- a/kernel/time/clocksource.c
+++ b/kernel/time/clocksource.c
@@ -149,15 +149,42 @@ static void clocksource_watchdog_work(struct work_struct *work)
149 kthread_run(clocksource_watchdog_kthread, NULL, "kwatchdog"); 149 kthread_run(clocksource_watchdog_kthread, NULL, "kwatchdog");
150} 150}
151 151
152static void clocksource_unstable(struct clocksource *cs, int64_t delta) 152static void __clocksource_unstable(struct clocksource *cs)
153{ 153{
154 printk(KERN_WARNING "Clocksource %s unstable (delta = %Ld ns)\n",
155 cs->name, delta);
156 cs->flags &= ~(CLOCK_SOURCE_VALID_FOR_HRES | CLOCK_SOURCE_WATCHDOG); 154 cs->flags &= ~(CLOCK_SOURCE_VALID_FOR_HRES | CLOCK_SOURCE_WATCHDOG);
157 cs->flags |= CLOCK_SOURCE_UNSTABLE; 155 cs->flags |= CLOCK_SOURCE_UNSTABLE;
158 schedule_work(&watchdog_work); 156 schedule_work(&watchdog_work);
159} 157}
160 158
159static void clocksource_unstable(struct clocksource *cs, int64_t delta)
160{
161 printk(KERN_WARNING "Clocksource %s unstable (delta = %Ld ns)\n",
162 cs->name, delta);
163 __clocksource_unstable(cs);
164}
165
166/**
167 * clocksource_mark_unstable - mark clocksource unstable via watchdog
168 * @cs: clocksource to be marked unstable
169 *
170 * This function is called instead of clocksource_change_rating from
171 * cpu hotplug code to avoid a deadlock between the clocksource mutex
172 * and the cpu hotplug mutex. It defers the update of the clocksource
173 * to the watchdog thread.
174 */
175void clocksource_mark_unstable(struct clocksource *cs)
176{
177 unsigned long flags;
178
179 spin_lock_irqsave(&watchdog_lock, flags);
180 if (!(cs->flags & CLOCK_SOURCE_UNSTABLE)) {
181 if (list_empty(&cs->wd_list))
182 list_add(&cs->wd_list, &watchdog_list);
183 __clocksource_unstable(cs);
184 }
185 spin_unlock_irqrestore(&watchdog_lock, flags);
186}
187
161static void clocksource_watchdog(unsigned long data) 188static void clocksource_watchdog(unsigned long data)
162{ 189{
163 struct clocksource *cs; 190 struct clocksource *cs;