Loading drivers/soc/qcom/minidump_log.c +166 −48 Original line number Diff line number Diff line Loading @@ -4,6 +4,7 @@ */ #include <linux/cache.h> #include <linux/freezer.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> Loading @@ -18,6 +19,7 @@ #include <linux/mm.h> #include <linux/ratelimit.h> #include <linux/sched/task.h> #include <linux/suspend.h> #include <linux/vmalloc.h> #ifdef CONFIG_QCOM_DYN_MINIDUMP_STACK Loading @@ -38,11 +40,20 @@ struct md_stack_cpu_data { static int md_current_stack_init __read_mostly; static DEFINE_PER_CPU_SHARED_ALIGNED(struct md_stack_cpu_data, md_stack_data); struct md_suspend_context_data { int task_mdno; int stack_mdidx[STACK_NUM_PAGES]; struct md_region stack_mdr[STACK_NUM_PAGES]; struct md_region task_mdr; bool init; }; static struct md_suspend_context_data md_suspend_context; #endif static bool is_vmap_stack __read_mostly; static void __init register_log_buf(void) { char **log_bufp; Loading @@ -64,8 +75,7 @@ static void __init register_log_buf(void) pr_err("Failed to add logbuf in Minidump\n"); } static int register_stack_entry(struct md_region *ksp_entry, u64 sp, u64 size, u32 cpu) static int register_stack_entry(struct md_region *ksp_entry, u64 sp, u64 size) { struct page *sp_page; int entry; Loading @@ -81,7 +91,8 @@ static int register_stack_entry(struct md_region *ksp_entry, u64 sp, u64 size, entry = msm_minidump_add_region(ksp_entry); if (entry < 0) pr_err("Failed to add stack of cpu %d in Minidump\n", cpu); pr_err("Failed to add stack of entry %s in Minidump\n", ksp_entry->name); return entry; } Loading Loading @@ -173,15 +184,14 @@ void dump_stack_minidump(u64 sp) for (i = 0; i < copy_pages; i++) { scnprintf(ksp_entry.name, sizeof(ksp_entry.name), "KSTACK%d_%d", cpu, i); (void)register_stack_entry(&ksp_entry, sp, PAGE_SIZE, cpu); (void)register_stack_entry(&ksp_entry, sp, PAGE_SIZE); sp += PAGE_SIZE; } } else { sp &= ~(THREAD_SIZE - 1); scnprintf(ksp_entry.name, sizeof(ksp_entry.name), "KSTACK%d", cpu); (void)register_stack_entry(&ksp_entry, sp, THREAD_SIZE, cpu); (void)register_stack_entry(&ksp_entry, sp, THREAD_SIZE); } scnprintf(ktsk_entry.name, sizeof(ktsk_entry.name), "KTASK%d", cpu); Loading @@ -194,7 +204,7 @@ void dump_stack_minidump(u64 sp) #ifdef CONFIG_QCOM_DYN_MINIDUMP_STACK static void update_stack_entry(struct md_region *ksp_entry, u64 sp, int mdno, u32 cpu) int mdno) { struct page *sp_page; Loading @@ -207,72 +217,72 @@ static void update_stack_entry(struct md_region *ksp_entry, u64 sp, } if (msm_minidump_update_region(mdno, ksp_entry) < 0) { pr_err_ratelimited( "Failed to update cpu[%d] current stack in minidump\n", cpu); "Failed to update stack entry %s in minidump\n", ksp_entry->name); } } static void register_vmapped_stack(struct md_stack_cpu_data *md_stack_cpu_d, u64 sp, u32 cpu, bool update) static void register_vmapped_stack(struct md_region *mdr, int *mdno, u64 sp, char *name_str, bool update) { struct md_region *mdr; int *mdno; int i; sp &= ~(PAGE_SIZE - 1); for (i = 0; i < STACK_NUM_PAGES; i++) { mdr = md_stack_cpu_d->stack_mdr + i; mdno = md_stack_cpu_d->stack_mdidx + i; if (unlikely(!update)) { scnprintf(mdr->name, sizeof(mdr->name), "KSTACK%d_%d", cpu, i); *mdno = register_stack_entry(mdr, sp, PAGE_SIZE, cpu); scnprintf(mdr->name, sizeof(mdr->name), "%s_%d", name_str, i); *mdno = register_stack_entry(mdr, sp, PAGE_SIZE); } else { update_stack_entry(mdr, sp, *mdno, cpu); update_stack_entry(mdr, sp, *mdno); } sp += PAGE_SIZE; mdr++; mdno++; } } static void register_normal_stack(struct md_stack_cpu_data *md_stack_cpu_d, u64 sp, u32 cpu, bool update) static void register_normal_stack(struct md_region *mdr, int *mdno, u64 sp, char *name_str, bool update) { struct md_region *mdr; mdr = md_stack_cpu_d->stack_mdr; sp &= ~(THREAD_SIZE - 1); if (unlikely(!update)) { scnprintf(mdr->name, sizeof(mdr->name), "KSTACK%d", cpu); *md_stack_cpu_d->stack_mdidx = register_stack_entry( mdr, sp, THREAD_SIZE, cpu); scnprintf(mdr->name, sizeof(mdr->name), name_str); *mdno = register_stack_entry(mdr, sp, THREAD_SIZE); } else { update_stack_entry(mdr, sp, *md_stack_cpu_d->stack_mdidx, cpu); update_stack_entry(mdr, sp, *mdno); } } static void update_md_stack(u32 cpu, u64 sp) static void update_md_stack(struct md_region *stack_mdr, int *stack_mdno, u64 sp) { struct md_stack_cpu_data *md_stack_cpu_d = &per_cpu(md_stack_data, cpu); int *mdno; unsigned int i; if (is_idle_task(current) || !md_current_stack_init) return; int *mdno; if (likely(is_vmap_stack)) { for (i = 0; i < STACK_NUM_PAGES; i++) { mdno = md_stack_cpu_d->stack_mdidx + i; mdno = stack_mdno + i; if (unlikely(*mdno < 0)) return; } register_vmapped_stack(md_stack_cpu_d, sp, cpu, true); register_vmapped_stack(stack_mdr, stack_mdno, sp, NULL, true); } else { if (unlikely(*md_stack_cpu_d->stack_mdidx < 0)) if (unlikely(*stack_mdno < 0)) return; register_normal_stack(md_stack_cpu_d, sp, cpu, true); register_normal_stack(stack_mdr, stack_mdno, sp, NULL, true); } } static void update_md_cpu_stack(u32 cpu, u64 sp) { struct md_stack_cpu_data *md_stack_cpu_d = &per_cpu(md_stack_data, cpu); if (is_idle_task(current) || !md_current_stack_init) return; update_md_stack(md_stack_cpu_d->stack_mdr, md_stack_cpu_d->stack_mdidx, sp); } void md_current_stack_notifer(void *ignore, bool preempt, Loading @@ -281,7 +291,7 @@ void md_current_stack_notifer(void *ignore, bool preempt, u32 cpu = task_cpu(next); u64 sp = (u64)next->stack; update_md_stack(cpu, sp); update_md_cpu_stack(cpu, sp); } void md_current_stack_ipi_handler(void *data) Loading @@ -296,7 +306,45 @@ void md_current_stack_ipi_handler(void *data) stack_vm_area = task_stack_vm_area(current); sp = (u64)stack_vm_area->addr; } update_md_stack(cpu, sp); update_md_cpu_stack(cpu, sp); } static void update_md_current_task(struct md_region *mdr, int mdno) { mdr->virt_addr = (u64)current; mdr->phys_addr = virt_to_phys((uintptr_t *)current); if (msm_minidump_update_region(mdno, mdr) < 0) pr_err("Failed to update %s current task in minidump\n", mdr->name); } static void update_md_suspend_current_stack(void) { u64 sp = current_stack_pointer; struct vm_struct *stack_vm_area; if (likely(is_vmap_stack)) { stack_vm_area = task_stack_vm_area(current); sp = (u64)stack_vm_area->addr; } update_md_stack(md_suspend_context.stack_mdr, md_suspend_context.stack_mdidx, sp); } static void update_md_suspend_current_task(void) { if (unlikely(md_suspend_context.task_mdno < 0)) return; update_md_current_task(&md_suspend_context.task_mdr, md_suspend_context.task_mdno); } static void update_md_suspend_currents(void) { if (!md_suspend_context.init) return; update_md_suspend_current_stack(); update_md_suspend_current_task(); } static void register_current_stack(void) Loading @@ -305,6 +353,7 @@ static void register_current_stack(void) u64 sp = current_stack_pointer; struct md_stack_cpu_data *md_stack_cpu_d; struct vm_struct *stack_vm_area; char name_str[MAX_NAME_LENGTH]; /* * Since stacks are now allocated with vmalloc, the translation to Loading @@ -324,16 +373,85 @@ static void register_current_stack(void) * once system up and running, let the cpu update its currents. */ md_stack_cpu_d = &per_cpu(md_stack_data, cpu); scnprintf(name_str, sizeof(name_str), "KSTACK%d", cpu); if (is_vmap_stack) register_vmapped_stack(md_stack_cpu_d, sp, cpu, false); register_vmapped_stack(md_stack_cpu_d->stack_mdr, md_stack_cpu_d->stack_mdidx, sp, name_str, false); else register_normal_stack(md_stack_cpu_d, sp, cpu, false); register_normal_stack(md_stack_cpu_d->stack_mdr, md_stack_cpu_d->stack_mdidx, sp, name_str, false); } register_trace_sched_switch(md_current_stack_notifer, NULL); md_current_stack_init = 1; smp_call_function(md_current_stack_ipi_handler, NULL, 1); } static void register_suspend_stack(void) { char name_str[MAX_NAME_LENGTH]; u64 sp = current_stack_pointer; struct vm_struct *stack_vm_area = task_stack_vm_area(current); scnprintf(name_str, sizeof(name_str), "KSUSPSTK"); if (is_vmap_stack) { sp = (u64)stack_vm_area->addr; register_vmapped_stack(md_suspend_context.stack_mdr, md_suspend_context.stack_mdidx, sp, name_str, false); } else { register_normal_stack(md_suspend_context.stack_mdr, md_suspend_context.stack_mdidx, sp, name_str, false); } } static void register_current_task(struct md_region *mdr, int *mdno, char *name_str) { scnprintf(mdr->name, sizeof(mdr->name), name_str); mdr->virt_addr = (u64)current; mdr->phys_addr = virt_to_phys((uintptr_t *)current); mdr->size = sizeof(struct task_struct); *mdno = msm_minidump_add_region(mdr); if (*mdno < 0) pr_err("Failed to add current task %s in Minidump\n", mdr->name); } static void register_suspend_current_task(void) { char name_str[MAX_NAME_LENGTH]; scnprintf(name_str, sizeof(name_str), "KSUSPTASK"); register_current_task(&md_suspend_context.task_mdr, &md_suspend_context.task_mdno, name_str); } static int minidump_pm_notifier(struct notifier_block *nb, unsigned long event, void *unused) { switch (event) { case PM_SUSPEND_PREPARE: update_md_suspend_currents(); break; } return NOTIFY_DONE; } static struct notifier_block minidump_pm_nb = { .notifier_call = minidump_pm_notifier, }; static void register_suspend_context(void) { register_suspend_stack(); register_suspend_current_task(); register_pm_notifier(&minidump_pm_nb); md_suspend_context.init = true; } #endif #ifdef CONFIG_ARM64 Loading @@ -356,15 +474,14 @@ static void register_irq_stack(void) sizeof(irq_sp_entry.name), "KISTACK%d_%d", cpu, i); register_stack_entry(&irq_sp_entry, sp, PAGE_SIZE, cpu); PAGE_SIZE); sp += PAGE_SIZE; } } else { sp = irq_stack_base; scnprintf(irq_sp_entry.name, sizeof(irq_sp_entry.name), "KISTACK%d", cpu); register_stack_entry(&irq_sp_entry, sp, IRQ_STACK_SIZE, cpu); register_stack_entry(&irq_sp_entry, sp, IRQ_STACK_SIZE); } } } Loading @@ -379,6 +496,7 @@ static int __init msm_minidump_log_init(void) register_irq_stack(); #ifdef CONFIG_QCOM_DYN_MINIDUMP_STACK register_current_stack(); register_suspend_context(); #endif register_log_buf(); return 0; Loading drivers/soc/qcom/qcom_soc_wdt.c +8 −0 Original line number Diff line number Diff line Loading @@ -105,6 +105,13 @@ static int qcom_soc_wdt_probe(struct platform_device *pdev) return qcom_wdt_register(pdev, wdog_dd, "msm-watchdog"); } static const struct dev_pm_ops qcom_soc_dev_pm_ops = { #ifdef CONFIG_PM_SLEEP .suspend_late = qcom_wdt_pet_suspend, .resume_early = qcom_wdt_pet_resume, #endif }; static const struct of_device_id qcom_soc_match_table[] = { { .compatible = "qcom,msm-watchdog" }, {} Loading @@ -115,6 +122,7 @@ static struct platform_driver qcom_soc_wdt_driver = { .remove = qcom_wdt_remove, .driver = { .name = "msm_watchdog", .pm = &qcom_soc_dev_pm_ops, .of_match_table = qcom_soc_match_table, }, }; Loading drivers/soc/qcom/qcom_wdt_core.c +54 −5 Original line number Diff line number Diff line Loading @@ -89,6 +89,45 @@ static void qcom_wdt_resume(void) wdog_data->last_pet = sched_clock(); return; } int qcom_wdt_pet_suspend(struct device *dev) { struct msm_watchdog_data *wdog_dd = (struct msm_watchdog_data *)dev_get_drvdata(dev); if (!wdog_dd) return 0; wdog_dd->ops->reset_wdt(wdog_dd); wdog_dd->last_pet = sched_clock(); spin_lock(&wdog_dd->freeze_lock); wdog_dd->freeze_in_progress = true; spin_unlock(&wdog_dd->freeze_lock); del_timer_sync(&wdog_dd->pet_timer); return 0; } EXPORT_SYMBOL(qcom_wdt_pet_suspend); int qcom_wdt_pet_resume(struct device *dev) { struct msm_watchdog_data *wdog_dd = (struct msm_watchdog_data *)dev_get_drvdata(dev); unsigned long delay_time = 0; if (!wdog_dd) return 0; delay_time = msecs_to_jiffies(wdog_dd->pet_time); wdog_dd->ops->reset_wdt(wdog_dd); wdog_dd->last_pet = sched_clock(); spin_lock(&wdog_dd->freeze_lock); wdog_dd->pet_timer.expires = jiffies + delay_time; add_timer(&wdog_dd->pet_timer); wdog_dd->freeze_in_progress = false; spin_unlock(&wdog_dd->freeze_lock); return 0; } EXPORT_SYMBOL(qcom_wdt_pet_resume); #endif static struct syscore_ops qcom_wdt_syscore_ops = { Loading Loading @@ -339,8 +378,13 @@ static __ref int qcom_wdt_kthread(void *arg) /* Check again before scheduling * Could have been changed on other cpu */ if (!kthread_should_stop()) mod_timer(&wdog_dd->pet_timer, jiffies + delay_time); if (!kthread_should_stop()) { spin_lock(&wdog_dd->freeze_lock); if (!wdog_dd->freeze_in_progress) mod_timer(&wdog_dd->pet_timer, jiffies + delay_time); spin_unlock(&wdog_dd->freeze_lock); } } return 0; } Loading Loading @@ -444,6 +488,9 @@ static irqreturn_t qcom_wdt_bark_handler(int irq, void *dev_id) if (wdog_dd->do_ipi_ping) qcom_wdt_dump_cpu_alive_mask(wdog_dd); if (wdog_dd->freeze_in_progress) dev_info(wdog_dd->dev, "Suspend in progress\n"); qcom_wdt_trigger_bite(); return IRQ_HANDLED; Loading Loading @@ -474,7 +521,7 @@ static int qcom_wdt_init(struct msm_watchdog_data *wdog_dd, ret = devm_request_irq(wdog_dd->dev, wdog_dd->bark_irq, qcom_wdt_bark_handler, IRQF_TRIGGER_RISING, IRQF_TRIGGER_RISING | IRQF_NO_SUSPEND, "apps_wdog_bark", wdog_dd); if (ret) { dev_err(wdog_dd->dev, "failed to request bark irq\n"); Loading @@ -495,6 +542,8 @@ static int qcom_wdt_init(struct msm_watchdog_data *wdog_dd, wdog_dd->timer_expired = false; wdog_dd->user_pet_complete = true; wdog_dd->user_pet_enabled = false; spin_lock_init(&wdog_dd->freeze_lock); wdog_dd->freeze_in_progress = false; wake_up_process(wdog_dd->watchdog_task); timer_setup(&wdog_dd->pet_timer, qcom_wdt_pet_task_wakeup, 0); wdog_dd->pet_timer.expires = jiffies + delay_time; Loading drivers/virt/haven/hh_virt_wdt.c +8 −0 Original line number Diff line number Diff line Loading @@ -261,6 +261,13 @@ static int hh_wdt_probe(struct platform_device *pdev) return qcom_wdt_register(pdev, wdog_dd, "hh-watchdog"); } static const struct dev_pm_ops hh_wdt_dev_pm_ops = { #ifdef CONFIG_PM_SLEEP .suspend_late = qcom_wdt_pet_suspend, .resume_early = qcom_wdt_pet_resume, #endif }; static const struct of_device_id hh_wdt_match_table[] = { { .compatible = "qcom,hh-watchdog" }, {} Loading @@ -271,6 +278,7 @@ static struct platform_driver hh_wdt_driver = { .remove = qcom_wdt_remove, .driver = { .name = "hh-watchdog", .pm = &hh_wdt_dev_pm_ops, .of_match_table = hh_wdt_match_table, }, }; Loading include/soc/qcom/watchdog.h +4 −0 Original line number Diff line number Diff line Loading @@ -123,12 +123,16 @@ struct msm_watchdog_data { unsigned long long ping_start[NR_CPUS]; unsigned long long ping_end[NR_CPUS]; int cpu_idle_pc_state[NR_CPUS]; bool freeze_in_progress; spinlock_t freeze_lock; }; extern void qcom_wdt_trigger_bite(void); int qcom_wdt_register(struct platform_device *pdev, struct msm_watchdog_data *wdog_dd, char *wdog_dd_name); int qcom_wdt_pet_suspend(struct device *dev); int qcom_wdt_pet_resume(struct device *dev); int qcom_wdt_remove(struct platform_device *pdev); #else static inline void qcom_wdt_trigger_bite(void) { } Loading Loading
drivers/soc/qcom/minidump_log.c +166 −48 Original line number Diff line number Diff line Loading @@ -4,6 +4,7 @@ */ #include <linux/cache.h> #include <linux/freezer.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> Loading @@ -18,6 +19,7 @@ #include <linux/mm.h> #include <linux/ratelimit.h> #include <linux/sched/task.h> #include <linux/suspend.h> #include <linux/vmalloc.h> #ifdef CONFIG_QCOM_DYN_MINIDUMP_STACK Loading @@ -38,11 +40,20 @@ struct md_stack_cpu_data { static int md_current_stack_init __read_mostly; static DEFINE_PER_CPU_SHARED_ALIGNED(struct md_stack_cpu_data, md_stack_data); struct md_suspend_context_data { int task_mdno; int stack_mdidx[STACK_NUM_PAGES]; struct md_region stack_mdr[STACK_NUM_PAGES]; struct md_region task_mdr; bool init; }; static struct md_suspend_context_data md_suspend_context; #endif static bool is_vmap_stack __read_mostly; static void __init register_log_buf(void) { char **log_bufp; Loading @@ -64,8 +75,7 @@ static void __init register_log_buf(void) pr_err("Failed to add logbuf in Minidump\n"); } static int register_stack_entry(struct md_region *ksp_entry, u64 sp, u64 size, u32 cpu) static int register_stack_entry(struct md_region *ksp_entry, u64 sp, u64 size) { struct page *sp_page; int entry; Loading @@ -81,7 +91,8 @@ static int register_stack_entry(struct md_region *ksp_entry, u64 sp, u64 size, entry = msm_minidump_add_region(ksp_entry); if (entry < 0) pr_err("Failed to add stack of cpu %d in Minidump\n", cpu); pr_err("Failed to add stack of entry %s in Minidump\n", ksp_entry->name); return entry; } Loading Loading @@ -173,15 +184,14 @@ void dump_stack_minidump(u64 sp) for (i = 0; i < copy_pages; i++) { scnprintf(ksp_entry.name, sizeof(ksp_entry.name), "KSTACK%d_%d", cpu, i); (void)register_stack_entry(&ksp_entry, sp, PAGE_SIZE, cpu); (void)register_stack_entry(&ksp_entry, sp, PAGE_SIZE); sp += PAGE_SIZE; } } else { sp &= ~(THREAD_SIZE - 1); scnprintf(ksp_entry.name, sizeof(ksp_entry.name), "KSTACK%d", cpu); (void)register_stack_entry(&ksp_entry, sp, THREAD_SIZE, cpu); (void)register_stack_entry(&ksp_entry, sp, THREAD_SIZE); } scnprintf(ktsk_entry.name, sizeof(ktsk_entry.name), "KTASK%d", cpu); Loading @@ -194,7 +204,7 @@ void dump_stack_minidump(u64 sp) #ifdef CONFIG_QCOM_DYN_MINIDUMP_STACK static void update_stack_entry(struct md_region *ksp_entry, u64 sp, int mdno, u32 cpu) int mdno) { struct page *sp_page; Loading @@ -207,72 +217,72 @@ static void update_stack_entry(struct md_region *ksp_entry, u64 sp, } if (msm_minidump_update_region(mdno, ksp_entry) < 0) { pr_err_ratelimited( "Failed to update cpu[%d] current stack in minidump\n", cpu); "Failed to update stack entry %s in minidump\n", ksp_entry->name); } } static void register_vmapped_stack(struct md_stack_cpu_data *md_stack_cpu_d, u64 sp, u32 cpu, bool update) static void register_vmapped_stack(struct md_region *mdr, int *mdno, u64 sp, char *name_str, bool update) { struct md_region *mdr; int *mdno; int i; sp &= ~(PAGE_SIZE - 1); for (i = 0; i < STACK_NUM_PAGES; i++) { mdr = md_stack_cpu_d->stack_mdr + i; mdno = md_stack_cpu_d->stack_mdidx + i; if (unlikely(!update)) { scnprintf(mdr->name, sizeof(mdr->name), "KSTACK%d_%d", cpu, i); *mdno = register_stack_entry(mdr, sp, PAGE_SIZE, cpu); scnprintf(mdr->name, sizeof(mdr->name), "%s_%d", name_str, i); *mdno = register_stack_entry(mdr, sp, PAGE_SIZE); } else { update_stack_entry(mdr, sp, *mdno, cpu); update_stack_entry(mdr, sp, *mdno); } sp += PAGE_SIZE; mdr++; mdno++; } } static void register_normal_stack(struct md_stack_cpu_data *md_stack_cpu_d, u64 sp, u32 cpu, bool update) static void register_normal_stack(struct md_region *mdr, int *mdno, u64 sp, char *name_str, bool update) { struct md_region *mdr; mdr = md_stack_cpu_d->stack_mdr; sp &= ~(THREAD_SIZE - 1); if (unlikely(!update)) { scnprintf(mdr->name, sizeof(mdr->name), "KSTACK%d", cpu); *md_stack_cpu_d->stack_mdidx = register_stack_entry( mdr, sp, THREAD_SIZE, cpu); scnprintf(mdr->name, sizeof(mdr->name), name_str); *mdno = register_stack_entry(mdr, sp, THREAD_SIZE); } else { update_stack_entry(mdr, sp, *md_stack_cpu_d->stack_mdidx, cpu); update_stack_entry(mdr, sp, *mdno); } } static void update_md_stack(u32 cpu, u64 sp) static void update_md_stack(struct md_region *stack_mdr, int *stack_mdno, u64 sp) { struct md_stack_cpu_data *md_stack_cpu_d = &per_cpu(md_stack_data, cpu); int *mdno; unsigned int i; if (is_idle_task(current) || !md_current_stack_init) return; int *mdno; if (likely(is_vmap_stack)) { for (i = 0; i < STACK_NUM_PAGES; i++) { mdno = md_stack_cpu_d->stack_mdidx + i; mdno = stack_mdno + i; if (unlikely(*mdno < 0)) return; } register_vmapped_stack(md_stack_cpu_d, sp, cpu, true); register_vmapped_stack(stack_mdr, stack_mdno, sp, NULL, true); } else { if (unlikely(*md_stack_cpu_d->stack_mdidx < 0)) if (unlikely(*stack_mdno < 0)) return; register_normal_stack(md_stack_cpu_d, sp, cpu, true); register_normal_stack(stack_mdr, stack_mdno, sp, NULL, true); } } static void update_md_cpu_stack(u32 cpu, u64 sp) { struct md_stack_cpu_data *md_stack_cpu_d = &per_cpu(md_stack_data, cpu); if (is_idle_task(current) || !md_current_stack_init) return; update_md_stack(md_stack_cpu_d->stack_mdr, md_stack_cpu_d->stack_mdidx, sp); } void md_current_stack_notifer(void *ignore, bool preempt, Loading @@ -281,7 +291,7 @@ void md_current_stack_notifer(void *ignore, bool preempt, u32 cpu = task_cpu(next); u64 sp = (u64)next->stack; update_md_stack(cpu, sp); update_md_cpu_stack(cpu, sp); } void md_current_stack_ipi_handler(void *data) Loading @@ -296,7 +306,45 @@ void md_current_stack_ipi_handler(void *data) stack_vm_area = task_stack_vm_area(current); sp = (u64)stack_vm_area->addr; } update_md_stack(cpu, sp); update_md_cpu_stack(cpu, sp); } static void update_md_current_task(struct md_region *mdr, int mdno) { mdr->virt_addr = (u64)current; mdr->phys_addr = virt_to_phys((uintptr_t *)current); if (msm_minidump_update_region(mdno, mdr) < 0) pr_err("Failed to update %s current task in minidump\n", mdr->name); } static void update_md_suspend_current_stack(void) { u64 sp = current_stack_pointer; struct vm_struct *stack_vm_area; if (likely(is_vmap_stack)) { stack_vm_area = task_stack_vm_area(current); sp = (u64)stack_vm_area->addr; } update_md_stack(md_suspend_context.stack_mdr, md_suspend_context.stack_mdidx, sp); } static void update_md_suspend_current_task(void) { if (unlikely(md_suspend_context.task_mdno < 0)) return; update_md_current_task(&md_suspend_context.task_mdr, md_suspend_context.task_mdno); } static void update_md_suspend_currents(void) { if (!md_suspend_context.init) return; update_md_suspend_current_stack(); update_md_suspend_current_task(); } static void register_current_stack(void) Loading @@ -305,6 +353,7 @@ static void register_current_stack(void) u64 sp = current_stack_pointer; struct md_stack_cpu_data *md_stack_cpu_d; struct vm_struct *stack_vm_area; char name_str[MAX_NAME_LENGTH]; /* * Since stacks are now allocated with vmalloc, the translation to Loading @@ -324,16 +373,85 @@ static void register_current_stack(void) * once system up and running, let the cpu update its currents. */ md_stack_cpu_d = &per_cpu(md_stack_data, cpu); scnprintf(name_str, sizeof(name_str), "KSTACK%d", cpu); if (is_vmap_stack) register_vmapped_stack(md_stack_cpu_d, sp, cpu, false); register_vmapped_stack(md_stack_cpu_d->stack_mdr, md_stack_cpu_d->stack_mdidx, sp, name_str, false); else register_normal_stack(md_stack_cpu_d, sp, cpu, false); register_normal_stack(md_stack_cpu_d->stack_mdr, md_stack_cpu_d->stack_mdidx, sp, name_str, false); } register_trace_sched_switch(md_current_stack_notifer, NULL); md_current_stack_init = 1; smp_call_function(md_current_stack_ipi_handler, NULL, 1); } static void register_suspend_stack(void) { char name_str[MAX_NAME_LENGTH]; u64 sp = current_stack_pointer; struct vm_struct *stack_vm_area = task_stack_vm_area(current); scnprintf(name_str, sizeof(name_str), "KSUSPSTK"); if (is_vmap_stack) { sp = (u64)stack_vm_area->addr; register_vmapped_stack(md_suspend_context.stack_mdr, md_suspend_context.stack_mdidx, sp, name_str, false); } else { register_normal_stack(md_suspend_context.stack_mdr, md_suspend_context.stack_mdidx, sp, name_str, false); } } static void register_current_task(struct md_region *mdr, int *mdno, char *name_str) { scnprintf(mdr->name, sizeof(mdr->name), name_str); mdr->virt_addr = (u64)current; mdr->phys_addr = virt_to_phys((uintptr_t *)current); mdr->size = sizeof(struct task_struct); *mdno = msm_minidump_add_region(mdr); if (*mdno < 0) pr_err("Failed to add current task %s in Minidump\n", mdr->name); } static void register_suspend_current_task(void) { char name_str[MAX_NAME_LENGTH]; scnprintf(name_str, sizeof(name_str), "KSUSPTASK"); register_current_task(&md_suspend_context.task_mdr, &md_suspend_context.task_mdno, name_str); } static int minidump_pm_notifier(struct notifier_block *nb, unsigned long event, void *unused) { switch (event) { case PM_SUSPEND_PREPARE: update_md_suspend_currents(); break; } return NOTIFY_DONE; } static struct notifier_block minidump_pm_nb = { .notifier_call = minidump_pm_notifier, }; static void register_suspend_context(void) { register_suspend_stack(); register_suspend_current_task(); register_pm_notifier(&minidump_pm_nb); md_suspend_context.init = true; } #endif #ifdef CONFIG_ARM64 Loading @@ -356,15 +474,14 @@ static void register_irq_stack(void) sizeof(irq_sp_entry.name), "KISTACK%d_%d", cpu, i); register_stack_entry(&irq_sp_entry, sp, PAGE_SIZE, cpu); PAGE_SIZE); sp += PAGE_SIZE; } } else { sp = irq_stack_base; scnprintf(irq_sp_entry.name, sizeof(irq_sp_entry.name), "KISTACK%d", cpu); register_stack_entry(&irq_sp_entry, sp, IRQ_STACK_SIZE, cpu); register_stack_entry(&irq_sp_entry, sp, IRQ_STACK_SIZE); } } } Loading @@ -379,6 +496,7 @@ static int __init msm_minidump_log_init(void) register_irq_stack(); #ifdef CONFIG_QCOM_DYN_MINIDUMP_STACK register_current_stack(); register_suspend_context(); #endif register_log_buf(); return 0; Loading
drivers/soc/qcom/qcom_soc_wdt.c +8 −0 Original line number Diff line number Diff line Loading @@ -105,6 +105,13 @@ static int qcom_soc_wdt_probe(struct platform_device *pdev) return qcom_wdt_register(pdev, wdog_dd, "msm-watchdog"); } static const struct dev_pm_ops qcom_soc_dev_pm_ops = { #ifdef CONFIG_PM_SLEEP .suspend_late = qcom_wdt_pet_suspend, .resume_early = qcom_wdt_pet_resume, #endif }; static const struct of_device_id qcom_soc_match_table[] = { { .compatible = "qcom,msm-watchdog" }, {} Loading @@ -115,6 +122,7 @@ static struct platform_driver qcom_soc_wdt_driver = { .remove = qcom_wdt_remove, .driver = { .name = "msm_watchdog", .pm = &qcom_soc_dev_pm_ops, .of_match_table = qcom_soc_match_table, }, }; Loading
drivers/soc/qcom/qcom_wdt_core.c +54 −5 Original line number Diff line number Diff line Loading @@ -89,6 +89,45 @@ static void qcom_wdt_resume(void) wdog_data->last_pet = sched_clock(); return; } int qcom_wdt_pet_suspend(struct device *dev) { struct msm_watchdog_data *wdog_dd = (struct msm_watchdog_data *)dev_get_drvdata(dev); if (!wdog_dd) return 0; wdog_dd->ops->reset_wdt(wdog_dd); wdog_dd->last_pet = sched_clock(); spin_lock(&wdog_dd->freeze_lock); wdog_dd->freeze_in_progress = true; spin_unlock(&wdog_dd->freeze_lock); del_timer_sync(&wdog_dd->pet_timer); return 0; } EXPORT_SYMBOL(qcom_wdt_pet_suspend); int qcom_wdt_pet_resume(struct device *dev) { struct msm_watchdog_data *wdog_dd = (struct msm_watchdog_data *)dev_get_drvdata(dev); unsigned long delay_time = 0; if (!wdog_dd) return 0; delay_time = msecs_to_jiffies(wdog_dd->pet_time); wdog_dd->ops->reset_wdt(wdog_dd); wdog_dd->last_pet = sched_clock(); spin_lock(&wdog_dd->freeze_lock); wdog_dd->pet_timer.expires = jiffies + delay_time; add_timer(&wdog_dd->pet_timer); wdog_dd->freeze_in_progress = false; spin_unlock(&wdog_dd->freeze_lock); return 0; } EXPORT_SYMBOL(qcom_wdt_pet_resume); #endif static struct syscore_ops qcom_wdt_syscore_ops = { Loading Loading @@ -339,8 +378,13 @@ static __ref int qcom_wdt_kthread(void *arg) /* Check again before scheduling * Could have been changed on other cpu */ if (!kthread_should_stop()) mod_timer(&wdog_dd->pet_timer, jiffies + delay_time); if (!kthread_should_stop()) { spin_lock(&wdog_dd->freeze_lock); if (!wdog_dd->freeze_in_progress) mod_timer(&wdog_dd->pet_timer, jiffies + delay_time); spin_unlock(&wdog_dd->freeze_lock); } } return 0; } Loading Loading @@ -444,6 +488,9 @@ static irqreturn_t qcom_wdt_bark_handler(int irq, void *dev_id) if (wdog_dd->do_ipi_ping) qcom_wdt_dump_cpu_alive_mask(wdog_dd); if (wdog_dd->freeze_in_progress) dev_info(wdog_dd->dev, "Suspend in progress\n"); qcom_wdt_trigger_bite(); return IRQ_HANDLED; Loading Loading @@ -474,7 +521,7 @@ static int qcom_wdt_init(struct msm_watchdog_data *wdog_dd, ret = devm_request_irq(wdog_dd->dev, wdog_dd->bark_irq, qcom_wdt_bark_handler, IRQF_TRIGGER_RISING, IRQF_TRIGGER_RISING | IRQF_NO_SUSPEND, "apps_wdog_bark", wdog_dd); if (ret) { dev_err(wdog_dd->dev, "failed to request bark irq\n"); Loading @@ -495,6 +542,8 @@ static int qcom_wdt_init(struct msm_watchdog_data *wdog_dd, wdog_dd->timer_expired = false; wdog_dd->user_pet_complete = true; wdog_dd->user_pet_enabled = false; spin_lock_init(&wdog_dd->freeze_lock); wdog_dd->freeze_in_progress = false; wake_up_process(wdog_dd->watchdog_task); timer_setup(&wdog_dd->pet_timer, qcom_wdt_pet_task_wakeup, 0); wdog_dd->pet_timer.expires = jiffies + delay_time; Loading
drivers/virt/haven/hh_virt_wdt.c +8 −0 Original line number Diff line number Diff line Loading @@ -261,6 +261,13 @@ static int hh_wdt_probe(struct platform_device *pdev) return qcom_wdt_register(pdev, wdog_dd, "hh-watchdog"); } static const struct dev_pm_ops hh_wdt_dev_pm_ops = { #ifdef CONFIG_PM_SLEEP .suspend_late = qcom_wdt_pet_suspend, .resume_early = qcom_wdt_pet_resume, #endif }; static const struct of_device_id hh_wdt_match_table[] = { { .compatible = "qcom,hh-watchdog" }, {} Loading @@ -271,6 +278,7 @@ static struct platform_driver hh_wdt_driver = { .remove = qcom_wdt_remove, .driver = { .name = "hh-watchdog", .pm = &hh_wdt_dev_pm_ops, .of_match_table = hh_wdt_match_table, }, }; Loading
include/soc/qcom/watchdog.h +4 −0 Original line number Diff line number Diff line Loading @@ -123,12 +123,16 @@ struct msm_watchdog_data { unsigned long long ping_start[NR_CPUS]; unsigned long long ping_end[NR_CPUS]; int cpu_idle_pc_state[NR_CPUS]; bool freeze_in_progress; spinlock_t freeze_lock; }; extern void qcom_wdt_trigger_bite(void); int qcom_wdt_register(struct platform_device *pdev, struct msm_watchdog_data *wdog_dd, char *wdog_dd_name); int qcom_wdt_pet_suspend(struct device *dev); int qcom_wdt_pet_resume(struct device *dev); int qcom_wdt_remove(struct platform_device *pdev); #else static inline void qcom_wdt_trigger_bite(void) { } Loading