| Date: Wed, 26 Jun 2013 15:28:11 -0400 |
| From: Steven Rostedt <rostedt@goodmis.org> |
| Subject: rt,ntp: Move call to schedule_delayed_work() to helper thread |
| |
| The ntp code for notify_cmos_timer() is called from a hard interrupt |
| context. schedule_delayed_work() under PREEMPT_RT_FULL calls spinlocks |
| that have been converted to mutexes, thus calling schedule_delayed_work() |
| from interrupt is not safe. |
| |
| Add a helper thread that does the call to schedule_delayed_work and wake |
| up that thread instead of calling schedule_delayed_work() directly. |
| This is only for CONFIG_PREEMPT_RT_FULL, otherwise the code still calls |
| schedule_delayed_work() directly in irq context. |
| |
| Note: There's a few places in the kernel that do this. Perhaps the RT |
| code should have a dedicated thread that does the checks. Just register |
| a notifier on boot up for your check and wake up the thread when |
| needed. This will be a todo. |
| |
| Signed-off-by: Steven Rostedt <rostedt@goodmis.org> |
| |
| --- |
| kernel/time/ntp.c | 43 +++++++++++++++++++++++++++++++++++++++++++ |
| 1 file changed, 43 insertions(+) |
| |
| --- a/kernel/time/ntp.c |
| +++ b/kernel/time/ntp.c |
| @@ -10,6 +10,7 @@ |
| #include <linux/workqueue.h> |
| #include <linux/hrtimer.h> |
| #include <linux/jiffies.h> |
| +#include <linux/kthread.h> |
| #include <linux/math64.h> |
| #include <linux/timex.h> |
| #include <linux/time.h> |
| @@ -562,10 +563,52 @@ static void sync_cmos_clock(struct work_ |
| &sync_cmos_work, timespec64_to_jiffies(&next)); |
| } |
| |
| +#ifdef CONFIG_PREEMPT_RT_FULL |
| +/* |
| + * RT can not call schedule_delayed_work from real interrupt context. |
| + * Need to make a thread to do the real work. |
| + */ |
| +static struct task_struct *cmos_delay_thread; |
| +static bool do_cmos_delay; |
| + |
| +static int run_cmos_delay(void *ignore) |
| +{ |
| + while (!kthread_should_stop()) { |
| + set_current_state(TASK_INTERRUPTIBLE); |
| + if (do_cmos_delay) { |
| + do_cmos_delay = false; |
| + queue_delayed_work(system_power_efficient_wq, |
| + &sync_cmos_work, 0); |
| + } |
| + schedule(); |
| + } |
| + __set_current_state(TASK_RUNNING); |
| + return 0; |
| +} |
| + |
| +void ntp_notify_cmos_timer(void) |
| +{ |
| + do_cmos_delay = true; |
| + /* Make visible before waking up process */ |
| + smp_wmb(); |
| + wake_up_process(cmos_delay_thread); |
| +} |
| + |
| +static __init int create_cmos_delay_thread(void) |
| +{ |
| + cmos_delay_thread = kthread_run(run_cmos_delay, NULL, "kcmosdelayd"); |
| + BUG_ON(!cmos_delay_thread); |
| + return 0; |
| +} |
| +early_initcall(create_cmos_delay_thread); |
| + |
| +#else |
| + |
| void ntp_notify_cmos_timer(void) |
| { |
| queue_delayed_work(system_power_efficient_wq, &sync_cmos_work, 0); |
| } |
| +#endif /* CONFIG_PREEMPT_RT_FULL */ |
| |
| #else |
| void ntp_notify_cmos_timer(void) { } |