Commit 6d9bcb62 authored by John Stultz's avatar John Stultz Committed by Linus Torvalds

timekeeping: use printk_deferred when holding timekeeping seqlock

Jiri Bohac pointed out that there are rare but potential deadlock
possibilities when calling printk while holding the timekeeping
seqlock.

This is due to printk() triggering console sem wakeup, which can
cause scheduling code to trigger hrtimers which may try to read
the time.

Specifically, as Jiri pointed out, that path is:
  printk
    vprintk_emit
      console_unlock
        up(&console_sem)
          __up
	    wake_up_process
	      try_to_wake_up
	        ttwu_do_activate
		  ttwu_activate
		    activate_task
		      enqueue_task
		        enqueue_task_fair
			  hrtick_update
			    hrtick_start_fair
			      hrtick_start_fair
			        get_time
				  ktime_get
				    --> endless loop on
				    read_seqcount_retry(&timekeeper_seq, ...)

This patch tries to avoid this issue by using printk_deferred (previously
named printk_sched) which should defer printing via a irq_work_queue.
Signed-off-by: default avatarJohn Stultz <john.stultz@linaro.org>
Reported-by: default avatarJiri Bohac <jbohac@suse.cz>
Reviewed-by: default avatarSteven Rostedt <rostedt@goodmis.org>
Cc: Jan Kara <jack@suse.cz>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent c224815d
...@@ -786,8 +786,9 @@ static long hardpps_update_freq(struct pps_normtime freq_norm) ...@@ -786,8 +786,9 @@ static long hardpps_update_freq(struct pps_normtime freq_norm)
time_status |= STA_PPSERROR; time_status |= STA_PPSERROR;
pps_errcnt++; pps_errcnt++;
pps_dec_freq_interval(); pps_dec_freq_interval();
pr_err("hardpps: PPSERROR: interval too long - %ld s\n", printk_deferred(KERN_ERR
freq_norm.sec); "hardpps: PPSERROR: interval too long - %ld s\n",
freq_norm.sec);
return 0; return 0;
} }
...@@ -800,7 +801,8 @@ static long hardpps_update_freq(struct pps_normtime freq_norm) ...@@ -800,7 +801,8 @@ static long hardpps_update_freq(struct pps_normtime freq_norm)
delta = shift_right(ftemp - pps_freq, NTP_SCALE_SHIFT); delta = shift_right(ftemp - pps_freq, NTP_SCALE_SHIFT);
pps_freq = ftemp; pps_freq = ftemp;
if (delta > PPS_MAXWANDER || delta < -PPS_MAXWANDER) { if (delta > PPS_MAXWANDER || delta < -PPS_MAXWANDER) {
pr_warning("hardpps: PPSWANDER: change=%ld\n", delta); printk_deferred(KERN_WARNING
"hardpps: PPSWANDER: change=%ld\n", delta);
time_status |= STA_PPSWANDER; time_status |= STA_PPSWANDER;
pps_stbcnt++; pps_stbcnt++;
pps_dec_freq_interval(); pps_dec_freq_interval();
...@@ -844,8 +846,9 @@ static void hardpps_update_phase(long error) ...@@ -844,8 +846,9 @@ static void hardpps_update_phase(long error)
* the time offset is updated. * the time offset is updated.
*/ */
if (jitter > (pps_jitter << PPS_POPCORN)) { if (jitter > (pps_jitter << PPS_POPCORN)) {
pr_warning("hardpps: PPSJITTER: jitter=%ld, limit=%ld\n", printk_deferred(KERN_WARNING
jitter, (pps_jitter << PPS_POPCORN)); "hardpps: PPSJITTER: jitter=%ld, limit=%ld\n",
jitter, (pps_jitter << PPS_POPCORN));
time_status |= STA_PPSJITTER; time_status |= STA_PPSJITTER;
pps_jitcnt++; pps_jitcnt++;
} else if (time_status & STA_PPSTIME) { } else if (time_status & STA_PPSTIME) {
...@@ -902,7 +905,7 @@ void __hardpps(const struct timespec *phase_ts, const struct timespec *raw_ts) ...@@ -902,7 +905,7 @@ void __hardpps(const struct timespec *phase_ts, const struct timespec *raw_ts)
time_status |= STA_PPSJITTER; time_status |= STA_PPSJITTER;
/* restart the frequency calibration interval */ /* restart the frequency calibration interval */
pps_fbase = *raw_ts; pps_fbase = *raw_ts;
pr_err("hardpps: PPSJITTER: bad pulse\n"); printk_deferred(KERN_ERR "hardpps: PPSJITTER: bad pulse\n");
return; return;
} }
......
...@@ -852,8 +852,9 @@ static void __timekeeping_inject_sleeptime(struct timekeeper *tk, ...@@ -852,8 +852,9 @@ static void __timekeeping_inject_sleeptime(struct timekeeper *tk,
struct timespec *delta) struct timespec *delta)
{ {
if (!timespec_valid_strict(delta)) { if (!timespec_valid_strict(delta)) {
printk(KERN_WARNING "__timekeeping_inject_sleeptime: Invalid " printk_deferred(KERN_WARNING
"sleep delta value!\n"); "__timekeeping_inject_sleeptime: Invalid "
"sleep delta value!\n");
return; return;
} }
tk_xtime_add(tk, delta); tk_xtime_add(tk, delta);
...@@ -1157,7 +1158,7 @@ static void timekeeping_adjust(struct timekeeper *tk, s64 offset) ...@@ -1157,7 +1158,7 @@ static void timekeeping_adjust(struct timekeeper *tk, s64 offset)
if (unlikely(tk->clock->maxadj && if (unlikely(tk->clock->maxadj &&
(tk->mult + adj > tk->clock->mult + tk->clock->maxadj))) { (tk->mult + adj > tk->clock->mult + tk->clock->maxadj))) {
printk_once(KERN_WARNING printk_deferred_once(KERN_WARNING
"Adjusting %s more than 11%% (%ld vs %ld)\n", "Adjusting %s more than 11%% (%ld vs %ld)\n",
tk->clock->name, (long)tk->mult + adj, tk->clock->name, (long)tk->mult + adj,
(long)tk->clock->mult + tk->clock->maxadj); (long)tk->clock->mult + tk->clock->maxadj);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment