diff --git a/arch/powerpc/include/asm/pgtable-ppc64.h b/arch/powerpc/include/asm/pgtable-ppc64.h index 2b09cd5..6ed08ab 100644 --- a/arch/powerpc/include/asm/pgtable-ppc64.h +++ b/arch/powerpc/include/asm/pgtable-ppc64.h @@ -228,8 +228,11 @@ static inline unsigned long pte_update(struct mm_struct *mm, assert_pte_locked(mm, addr); #ifdef CONFIG_PPC_STD_MMU_64 - if (old & _PAGE_HASHPTE) + if (old & _PAGE_HASHPTE) { + preempt_disable(); hpte_need_flush(mm, addr, ptep, old, huge); + preempt_enable(); + } #endif return old; diff --git a/arch/powerpc/kernel/rtasd.c b/arch/powerpc/kernel/rtasd.c index 049dbec..7980ec0 100644 --- a/arch/powerpc/kernel/rtasd.c +++ b/arch/powerpc/kernel/rtasd.c @@ -412,7 +412,8 @@ static void rtas_event_scan(struct work_struct *w) get_online_cpus(); - cpu = cpumask_next(smp_processor_id(), cpu_online_mask); + /* raw_ OK because just using CPU as starting point. */ + cpu = cpumask_next(raw_smp_processor_id(), cpu_online_mask); if (cpu >= nr_cpu_ids) { cpu = cpumask_first(cpu_online_mask); diff --git a/include/linux/preempt.h b/include/linux/preempt.h index 2e681d9..9b20772 100644 --- a/include/linux/preempt.h +++ b/include/linux/preempt.h @@ -27,15 +27,19 @@ asmlinkage void preempt_schedule(void); +void add_preempt_debug_entry(const char *file, const char *func, int line, int polarity); + #define preempt_disable() \ do { \ inc_preempt_count(); \ + add_preempt_debug_entry(__FILE__, __func__, __LINE__, 1); \ barrier(); \ } while (0) #define preempt_enable_no_resched() \ do { \ barrier(); \ + add_preempt_debug_entry(__FILE__, __func__, __LINE__, -1); \ dec_preempt_count(); \ } while (0) diff --git a/include/linux/sched.h b/include/linux/sched.h index d747f94..6eed80c 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1190,6 +1190,19 @@ enum perf_event_task_context { perf_nr_task_contexts, }; +#ifdef CONFIG_DEBUG_PREEMPT + +#define PREEMPT_DEBUG_NENTRIES 128 +struct preempt_debug_entry { + const char *file; + const char *func; + int line; + int preempt_cnt; + int preempt_polarity; +}; + +#endif /* #ifdef CONFIG_DEBUG_PREEMPT */ + struct task_struct { volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */ void *stack; @@ -1243,6 +1256,10 @@ struct task_struct { #ifdef CONFIG_RCU_BOOST struct rt_mutex *rcu_boost_mutex; #endif /* #ifdef CONFIG_RCU_BOOST */ +#ifdef CONFIG_DEBUG_PREEMPT + int preempt_debug_entry_index; + struct preempt_debug_entry preempt_debug_log[PREEMPT_DEBUG_NENTRIES]; +#endif /* #ifdef CONFIG_DEBUG_PREEMPT */ #if defined(CONFIG_SCHEDSTATS) || defined(CONFIG_TASK_DELAY_ACCT) struct sched_info sched_info; diff --git a/kernel/sched.c b/kernel/sched.c index 18d38e4..56150aa 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -3802,14 +3802,98 @@ notrace unsigned long get_parent_ip(unsigned long addr) #if defined(CONFIG_PREEMPT) && (defined(CONFIG_DEBUG_PREEMPT) || \ defined(CONFIG_PREEMPT_TRACER)) +#ifdef CONFIG_DEBUG_PREEMPT + +static DEFINE_PER_CPU(struct preempt_debug_entry[PREEMPT_DEBUG_NENTRIES], preempt_debug_log); + +void add_preempt_debug_entry(const char *file, const char *func, int line, int polarity) +{ + int idx = current->preempt_debug_entry_index; + struct preempt_debug_entry *p = ¤t->preempt_debug_log[idx]; + + p->file = file; + p->func = func; + p->line = line; + p->preempt_cnt = preempt_count(); + p->preempt_polarity = polarity; + idx++; + if (idx < PREEMPT_DEBUG_NENTRIES) + current->preempt_debug_entry_index = idx; + else + current->preempt_debug_entry_index = 0; +} +EXPORT_SYMBOL_GPL(add_preempt_debug_entry); + +static void dump_preempt_debug_log(void) +{ + int idx; + unsigned long flags; + int endidx; + struct preempt_debug_entry *p; + struct preempt_debug_entry *q; + + local_irq_save(flags); + idx = current->preempt_debug_entry_index; + p = ¤t->preempt_debug_log[idx]; + q = &__get_cpu_var(preempt_debug_log)[0]; + endidx = idx - 1; + if (endidx < 0) + endidx = PREEMPT_DEBUG_NENTRIES - 1; + for (;;) { + *q = *p; + if (idx == endidx) + break; + if (++idx >= PREEMPT_DEBUG_NENTRIES) + p++; + else { + idx = 0; + p = ¤t->preempt_debug_log[idx]; + } + q++; + } + printk(KERN_ALERT "Dumping preempt_debug_log:\n"); + for (idx = 0; idx < PREEMPT_DEBUG_NENTRIES; idx++) { + int before, after; + + q = &__get_cpu_var(preempt_debug_log)[idx]; + if (p->preempt_polarity < 0) { + before = p->preempt_cnt; + after = before - 1; + } else { + after = p->preempt_cnt; + before = after - 1; + } + if (p->file != NULL) + printk(KERN_ALERT "%d %s:%d %s() %x->%x\n", idx, p->file, p->line, p->func, before, after); + } + local_irq_restore(flags); + printk(KERN_ALERT "End of preempt_debug_log.\n"); +} + +#else /* #ifdef CONFIG_DEBUG_PREEMPT */ + +void add_preempt_debug_entry(const char *file, const char *func, int line, int polarity) +{ +} + +static void dump_preempt_debug_log(void) +{ +} + +EXPORT_SYMBOL_GPL(add_preempt_debug_entry); + +#endif /* #else #ifdef CONFIG_DEBUG_PREEMPT */ + void __kprobes add_preempt_count(int val) { #ifdef CONFIG_DEBUG_PREEMPT /* * Underflow? */ - if (DEBUG_LOCKS_WARN_ON((preempt_count() < 0))) + if (DEBUG_LOCKS_WARN_ON((preempt_count() < 0))) { + dump_preempt_debug_log(); return; + } #endif preempt_count() += val; #ifdef CONFIG_DEBUG_PREEMPT @@ -3830,8 +3914,10 @@ void __kprobes sub_preempt_count(int val) /* * Underflow? */ - if (DEBUG_LOCKS_WARN_ON(val > preempt_count())) + if (DEBUG_LOCKS_WARN_ON(val > preempt_count())) { + dump_preempt_debug_log(); return; + } /* * Is the spinlock portion underflowing? */