debug: hard lockup detect
authorjianxin.pan <jianxin.pan@amlogic.com>
Sat, 20 Jan 2018 07:24:50 +0000 (15:24 +0800)
committerJianxin Pan <jianxin.pan@amlogic.com>
Thu, 10 May 2018 02:39:28 +0000 (19:39 -0700)
PD#165796: detect dead lock in isr and dead lock with irq disabled

Change-Id: I0aace28fd29e5aeb8c514f6ab1700ac1248f836c
Signed-off-by: jianxin.pan <jianxin.pan@amlogic.com>
17 files changed:
MAINTAINERS
arch/arm64/configs/meson64_defconfig
arch/arm64/include/asm/irqflags.h
drivers/amlogic/Kconfig
drivers/amlogic/Makefile
drivers/amlogic/debug/Kconfig [new file with mode: 0644]
drivers/amlogic/debug/Makefile [new file with mode: 0644]
drivers/amlogic/debug/debug_lockup.c [new file with mode: 0644]
drivers/amlogic/debug/irqflags_debug.h [new file with mode: 0644]
drivers/amlogic/watchdog/meson_wdt.c
include/linux/amlogic/debug_lockup.h [new file with mode: 0644]
kernel/irq/chip.c
kernel/irq/handle.c
kernel/softirq.c
kernel/sysctl.c
kernel/watchdog.c
kernel/watchdog_hld.c

index b35f547..a818a8a 100644 (file)
@@ -14446,3 +14446,6 @@ M:      Jihong Sui <jihong.sui@amlogic.com>
 F:      include/uapi/linux/dvb/aml_demod.h
 F:      drivers/amlogic/media/aml_demod/*
 
+AMLOGIC DEBUG
+M: Jianxin Pan <jianxin.pan@amlogic.com>
+F:  drivers/amlogic/debug/
index 2634bb1..26b4bdc 100644 (file)
@@ -345,6 +345,8 @@ CONFIG_AMLOGIC_DDR_BANDWIDTH=y
 CONFIG_AMLOGIC_TEE=y
 CONFIG_AMLOGIC_GPIO_IRQ=y
 CONFIG_AMLOGIC_ATV_DEMOD=y
+CONFIG_AMLOGIC_DEBUG=y
+CONFIG_AMLOGIC_DEBUG_LOCKUP=y
 CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
 CONFIG_DEVTMPFS=y
 CONFIG_DEVTMPFS_MOUNT=y
index 8c58128..81db6c9 100644 (file)
@@ -20,6 +20,9 @@
 
 #include <asm/ptrace.h>
 
+#ifdef CONFIG_AMLOGIC_DEBUG_LOCKUP
+#include <../drivers/amlogic/debug/irqflags_debug.h>
+#else
 /*
  * CPU interrupt mask handling.
  */
@@ -112,3 +115,4 @@ static inline int arch_irqs_disabled_flags(unsigned long flags)
 
 #endif
 #endif
+#endif
index 01a6155..1c66995 100644 (file)
@@ -128,5 +128,6 @@ source "drivers/amlogic/irqchip/Kconfig"
 
 source "drivers/amlogic/atv_demod/Kconfig"
 
+source "drivers/amlogic/debug/Kconfig"
 endmenu
 endif
index 1979244..fcc0523 100644 (file)
@@ -118,3 +118,5 @@ obj-$(CONFIG_AMLOGIC_LEDRING) += ledring/
 obj-$(CONFIG_AMLOGIC_GPIO_IRQ) += irqchip/
 
 obj-$(CONFIG_AMLOGIC_ATV_DEMOD) += atv_demod/
+
+obj-$(CONFIG_AMLOGIC_DEBUG) += debug/
diff --git a/drivers/amlogic/debug/Kconfig b/drivers/amlogic/debug/Kconfig
new file mode 100644 (file)
index 0000000..34af209
--- /dev/null
@@ -0,0 +1,15 @@
+menuconfig AMLOGIC_DEBUG
+       bool "AMLOGIC kernel debug"
+       default n
+       help
+               Amlogic kernel debug
+
+config AMLOGIC_DEBUG_LOCKUP
+       bool "Amlogic Kernel Lockup debug"
+       depends on AMLOGIC_DEBUG
+       depends on HARDLOCKUP_DETECTOR
+       default n
+       help
+               Debug lockup in isr and deaklock whit irq disabled.
+               When enable this config, Watchdog should be disabled.
+
diff --git a/drivers/amlogic/debug/Makefile b/drivers/amlogic/debug/Makefile
new file mode 100644 (file)
index 0000000..2f07bf9
--- /dev/null
@@ -0,0 +1 @@
+obj-$(CONFIG_AMLOGIC_DEBUG_LOCKUP)     += debug_lockup.o
diff --git a/drivers/amlogic/debug/debug_lockup.c b/drivers/amlogic/debug/debug_lockup.c
new file mode 100644 (file)
index 0000000..b4b29ae
--- /dev/null
@@ -0,0 +1,356 @@
+/*
+ * drivers/amlogic/debug/debug_lockup.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <linux/stacktrace.h>
+#include <linux/export.h>
+#include <linux/types.h>
+#include <linux/smp.h>
+#include <linux/irqflags.h>
+#include <linux/sched.h>
+#include "../kernel/sched/sched.h"
+#include <linux/moduleparam.h>
+#include <linux/debugfs.h>
+#include <linux/module.h>
+#include <linux/uaccess.h>
+
+/*isr trace*/
+#define ns2ms                  (1000 * 1000)
+#define LONG_ISR               (500 * ns2ms)
+#define LONG_SIRQ              (500 * ns2ms)
+#define CHK_WINDOW             (1000 * ns2ms)
+#define IRQ_CNT                        256
+#define CCCNT_WARN             1000
+#define CPU                    8
+static unsigned long t_base[IRQ_CNT];
+static unsigned long t_isr[IRQ_CNT];
+static unsigned long t_total[IRQ_CNT];
+static unsigned int cnt_total[IRQ_CNT];
+static void *cpu_action[CPU];
+static int cpu_irq[CPU] = {0};
+static void *cpu_sirq[CPU] = {NULL};
+
+/*irq disable trace*/
+#define LONG_IRQDIS            (1000 * 1000000)        /*500 ms*/
+#define OUT_WIN                        (500 * 1000000) /*500 ms*/
+#define LONG_IDLE              (5000000000)    /*5 sec*/
+#define ENTRY                  10
+static unsigned long           t_i_d[CPU];
+static int                     irq_flg;
+static struct stack_trace      irq_trace[CPU];
+static unsigned long           t_entrys[CPU][ENTRY];
+static unsigned long           t_idle[CPU] = { 0 };
+static unsigned long           t_d_out;
+
+static unsigned long isr_thr = LONG_ISR;
+core_param(isr_thr, isr_thr, ulong, 0644);
+static unsigned long irq_dis_thr = LONG_IRQDIS;
+core_param(irq_dis_thr, irq_dis_thr, ulong, 0644);
+static unsigned long sirq_thr = LONG_SIRQ;
+core_param(sirq_thr, sirq_thr, ulong, 0644);
+static int irq_check_en = 1;
+core_param(irq_check_en, irq_check_en, int, 0644);
+static int isr_check_en = 1;
+core_param(isr_check_en, isr_check_en, int, 0644);
+static unsigned long out_thr = OUT_WIN;
+core_param(out_thr, out_thr, ulong, 0644);
+
+void notrace isr_in_hook(unsigned int cpu, unsigned long *tin,
+       unsigned int irq, void *act)
+{
+       if (irq >= IRQ_CNT || !isr_check_en || oops_in_progress)
+               return;
+       cpu_irq[cpu] = irq;
+       cpu_action[cpu] = act;
+       *tin = sched_clock();
+       if (*tin >= CHK_WINDOW + t_base[irq]) {
+               t_base[irq]     = *tin;
+               t_isr[irq]      = 0;
+               t_total[irq]    = 0;
+               cnt_total[irq]  = 0;
+       }
+}
+
+void notrace isr_out_hook(unsigned int cpu, unsigned long tin, unsigned int irq)
+{
+       unsigned long tout;
+       unsigned int ratio = 0;
+
+       if (!isr_check_en || oops_in_progress)
+               return;
+       if (irq >= IRQ_CNT || cpu_irq[cpu] <= 0)
+               return;
+       cpu_irq[cpu] = 0;
+       tout = sched_clock();
+       t_isr[irq] += (tout > tin) ? (tout-tin) : 0;
+       t_total[irq] = (tout-t_base[irq]);
+       cnt_total[irq]++;
+
+       if (tout > isr_thr + tin)
+               pr_err("ISR_Long___ERR. irq:%d  tout-tin:%ld ms\n",
+                       irq, (tout - tin) / ns2ms);
+
+       if (t_total[irq] < CHK_WINDOW)
+               return;
+
+       ratio = t_isr[irq] * 100 / t_total[irq];
+       if (ratio >= 35) {
+               pr_err("IRQRatio___ERR.irq:%d ratio:%d\n", irq, (int)ratio);
+               pr_err("t_isr:%d  t_total:%d, cnt:%d\n",
+                       (int)(t_isr[irq] / ns2ms),
+                       (int)(t_total[irq] / ns2ms),
+                       cnt_total[irq]);
+       }
+       t_base[irq] = sched_clock();
+       t_isr[irq] = 0;
+       t_total[irq] = 0;
+       cnt_total[irq] = 0;
+       cpu_action[cpu] = NULL;
+}
+
+void notrace sirq_in_hook(unsigned int cpu, unsigned long *tin, void *p)
+{
+       cpu_sirq[cpu] = p;
+       *tin = sched_clock();
+}
+void notrace sirq_out_hook(unsigned int cpu, unsigned long tin, void *p)
+{
+       unsigned long tout = sched_clock();
+
+       if (cpu_sirq[cpu] && tin && (tout > tin + sirq_thr) &&
+               !oops_in_progress) {
+               pr_err("SIRQLong___ERR. sirq:%p  tout-tin:%ld ms\n",
+                       p, (tout - tin) / ns2ms);
+       }
+       cpu_sirq[cpu] = NULL;
+}
+
+void notrace irq_trace_start(unsigned long flags)
+{
+       unsigned int cpu;
+       int softirq = 0;
+
+       if (!irq_flg  || !irq_check_en || oops_in_progress)
+               return;
+       if (arch_irqs_disabled_flags(flags))
+               return;
+
+       cpu = get_cpu();
+       put_cpu();
+       softirq =  task_thread_info(current)->preempt_count & SOFTIRQ_MASK;
+       if ((t_idle[cpu] && !softirq) || t_i_d[cpu] || cpu_is_offline(cpu) ||
+               (softirq_count() && !cpu_sirq[cpu]))
+               return;
+
+       memset(&irq_trace[cpu], 0, sizeof(irq_trace[cpu]));
+       memset(&t_entrys[cpu][0], 0, sizeof(t_entrys[cpu][0])*ENTRY);
+       irq_trace[cpu].entries = &t_entrys[cpu][0];
+       irq_trace[cpu].max_entries = ENTRY;
+       irq_trace[cpu].skip = 2;
+       irq_trace[cpu].nr_entries = 0;
+       t_i_d[cpu] = sched_clock();
+       save_stack_trace(&irq_trace[cpu]);
+}
+EXPORT_SYMBOL(irq_trace_start);
+
+void notrace irq_trace_stop(unsigned long flag)
+{
+       unsigned long t_i_e, t;
+       unsigned int cpu;
+       int softirq = 0;
+       static int out_cnt;
+
+       if (!irq_check_en ||  !irq_flg || oops_in_progress)
+               return;
+
+       if (arch_irqs_disabled_flags(flag))
+               return;
+
+       cpu = get_cpu();
+       put_cpu();
+       if (!t_i_d[cpu] ||
+               !arch_irqs_disabled_flags(arch_local_save_flags())) {
+               t_i_d[cpu] = 0;
+               return;
+       }
+
+       t_i_e = sched_clock();
+       if (t_i_e <  t_i_d[cpu]) {
+               t_i_d[cpu] = 0;
+               return;
+       }
+
+       t = (t_i_e - t_i_d[cpu]);
+       softirq =  task_thread_info(current)->preempt_count & SOFTIRQ_MASK;
+
+       if (!(t_idle[cpu] && !softirq) && (t > irq_dis_thr) && t_i_d[cpu] &&
+               !(softirq_count() && !cpu_sirq[cpu])) {
+               out_cnt++;
+               if (t_i_e >= t_d_out + out_thr) {
+                       t_d_out = t_i_e;
+                       pr_err("\n\nDisIRQ___ERR:%ld ms <%ld %ld> %d:\n",
+                               t / ns2ms, t_i_e / ns2ms, t_i_d[cpu] / ns2ms,
+                               out_cnt);
+                       print_stack_trace(&irq_trace[cpu], 0);
+                       dump_stack();
+               }
+       }
+       t_i_d[cpu] = 0;
+}
+EXPORT_SYMBOL(irq_trace_stop);
+
+void __attribute__((weak)) lockup_hook(int cpu)
+{
+}
+
+void  notrace arch_cpu_idle_enter(void)
+{
+       int cpu;
+
+       if ((!irq_check_en ||  !irq_flg) && !isr_check_en)
+               return;
+       cpu = get_cpu();
+       put_cpu();
+       t_idle[cpu] = local_clock();
+}
+void  notrace arch_cpu_idle_exit(void)
+{
+       int cpu;
+
+       if ((!irq_check_en ||  !irq_flg) && !isr_check_en)
+               return;
+       cpu = get_cpu();
+       put_cpu();
+       t_idle[cpu] = 0;
+}
+
+void pr_lockup_info(int c)
+{
+       int cpu;
+       int virq = irq_check_en;
+       int visr = isr_check_en;
+
+       irq_flg = 0;
+       irq_check_en = 0;
+       isr_check_en = 0;
+       console_loglevel = 7;
+       pr_err("\n\n\nHARDLOCKUP____ERR.CPU[%d] <irqen:%d isren%d>START\n",
+               c, virq, visr);
+       for_each_online_cpu(cpu) {
+               unsigned long t_cur = sched_clock();
+               struct task_struct *p = (cpu_rq(cpu)->curr);
+               int preempt = task_thread_info(p)->preempt_count;
+
+               pr_err("\ndump_cpu[%d] irq:%3d preempt:%x  %s\n",
+                       cpu, cpu_irq[cpu], preempt,      p->comm);
+               if (preempt & HARDIRQ_MASK)
+                       pr_err("IRQ %pf, %x\n", cpu_action[cpu],
+                       (unsigned int)(preempt & HARDIRQ_MASK));
+               if (preempt & SOFTIRQ_MASK)
+                       pr_err("SotIRQ %pf, %x\n", cpu_sirq[cpu],
+                       (unsigned int)(preempt & SOFTIRQ_MASK));
+
+               if (t_i_d[cpu]) {
+                       pr_err("IRQ____ERR[%d]. <%ld %ld>.\n",
+                               cpu,  t_i_d[cpu] / ns2ms,
+                               (t_cur-t_i_d[cpu]) / ns2ms);
+                       print_stack_trace(&irq_trace[cpu], 0);
+               }
+               if (t_idle[cpu] && (t_idle[cpu] > LONG_IDLE + t_cur)) {
+                       pr_err("CPU[%d] IdleLong____ERR:%ld ms <%ld %ld>\n",
+                               cpu, (t_cur - t_idle[cpu]) / ns2ms,
+                               t_cur / ns2ms, t_idle[cpu] / ns2ms);
+               }
+               dump_cpu_task(cpu);
+               lockup_hook(cpu);
+       }
+       pr_err("\nHARDLOCKUP____ERR.END\n\n");
+}
+
+
+
+static struct dentry *debug_lockup;
+#define debug_fs(x)                                    \
+static ssize_t x##_write(struct file *file, const char __user *userbuf,\
+                                  size_t count, loff_t *ppos)  \
+{                                                      \
+       char buf[20];                                   \
+       unsigned long val;                                      \
+       int ret;                                                \
+       count = min_t(size_t, count, (sizeof(buf)-1));          \
+       if (copy_from_user(buf, userbuf, count))                \
+               return -EFAULT;                         \
+       buf[count] = 0;                                 \
+       ret = sscanf(buf, "%ld", &val);                 \
+       x = (typeof(x))val;                             \
+       if (irq_check_en || isr_check_en)                       \
+               aml_wdt_disable_dbg();                  \
+       pr_info("%s:%ld\n", __func__, (unsigned long)x);        \
+       return count;                                   \
+}                                                      \
+static ssize_t x##_read(struct file *file, char __user *userbuf,       \
+                                size_t count, loff_t *ppos)    \
+{                                                      \
+       char buf[20];                                   \
+       unsigned long val;                                      \
+       ssize_t len;                                    \
+       val = (unsigned long)x;                         \
+       len = snprintf(buf, sizeof(buf), "%ld\n", val);         \
+       pr_info("%s:%ld\n", __func__, val);                     \
+       return simple_read_from_buffer(userbuf, count, ppos, buf, len);\
+}                                                      \
+static const struct file_operations x##_debug_ops = {          \
+       .open           = simple_open,                  \
+       .read           = x##_read,                     \
+       .write          = x##_write,                    \
+}
+debug_fs(isr_thr);
+debug_fs(irq_dis_thr);
+debug_fs(sirq_thr);
+debug_fs(irq_check_en);
+debug_fs(isr_check_en);
+debug_fs(out_thr);
+
+static int __init debug_lockup_init(void)
+{
+       debug_lockup = debugfs_create_dir("lockup", NULL);
+       if (IS_ERR_OR_NULL(debug_lockup)) {
+               pr_warn("failed to create debug_lockup\n");
+               debug_lockup = NULL;
+               return -1;
+       }
+       debugfs_create_file("isr_thr", S_IFREG | 0664,
+                           debug_lockup, NULL, &isr_thr_debug_ops);
+       debugfs_create_file("irq_dis_thr", S_IFREG | 0664,
+                           debug_lockup, NULL, &irq_dis_thr_debug_ops);
+       debugfs_create_file("sirq_thr", S_IFREG | 0664,
+                           debug_lockup, NULL, &sirq_thr_debug_ops);
+       debugfs_create_file("out_thr", S_IFREG | 0664,
+                           debug_lockup, NULL, &out_thr_debug_ops);
+       debugfs_create_file("isr_check_en", S_IFREG | 0664,
+                           debug_lockup, NULL, &isr_check_en_debug_ops);
+       debugfs_create_file("irq_check_en", S_IFREG | 0664,
+                           debug_lockup, NULL, &irq_check_en_debug_ops);
+       if (irq_check_en || isr_check_en)
+               aml_wdt_disable_dbg();
+       irq_flg = 1;
+       return 0;
+}
+late_initcall(debug_lockup_init);
+
+MODULE_DESCRIPTION("Amlogic debug lockup module");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jianxin Pan <jianxin.pan@amlogic.com>");
diff --git a/drivers/amlogic/debug/irqflags_debug.h b/drivers/amlogic/debug/irqflags_debug.h
new file mode 100644 (file)
index 0000000..3e06fa7
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * drivers/amlogic/debug/irqflags_debug.h
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#ifndef __ASM_IRQFLAGS_DEBUG_H
+#define __ASM_IRQFLAGS_DEBUG_H
+
+#ifdef __KERNEL__
+
+/*
+ * CPU interrupt mask handling.
+ */
+#include <linux/amlogic/debug_lockup.h>
+
+static inline unsigned long arch_local_irq_save(void)
+{
+       unsigned long flags;
+
+       asm volatile(
+               "mrs    %0, daif                // arch_local_irq_save\n"
+               "msr    daifset, #2"
+               : "=r" (flags)
+               :
+               : "memory");
+        irq_trace_start(flags);
+       return flags;
+}
+
+static inline void arch_local_irq_enable(void)
+{
+       irq_trace_stop(0);
+
+       asm volatile(
+               "msr    daifclr, #2             // arch_local_irq_enable"
+               :
+               :
+               : "memory");
+}
+
+static inline void arch_local_irq_disable(void)
+{
+#if 0
+       asm volatile(
+               "msr    daifset, #2             // arch_local_irq_disable"
+               :
+               :
+               : "memory");
+#endif
+       arch_local_irq_save();
+}
+
+#define local_fiq_enable()     asm("msr        daifclr, #1" : : : "memory")
+#define local_fiq_disable()    asm("msr        daifset, #1" : : : "memory")
+
+#define local_async_enable()   asm("msr        daifclr, #4" : : : "memory")
+#define local_async_disable()  asm("msr        daifset, #4" : : : "memory")
+
+/*
+ * Save the current interrupt enable state.
+ */
+static inline unsigned long arch_local_save_flags(void)
+{
+       unsigned long flags;
+
+       asm volatile(
+               "mrs    %0, daif                // arch_local_save_flags"
+               : "=r" (flags)
+               :
+               : "memory");
+       return flags;
+}
+
+/*
+ * restore saved IRQ state
+ */
+static inline void arch_local_irq_restore(unsigned long flags)
+{
+       irq_trace_stop(flags);
+       asm volatile(
+               "msr    daif, %0                // arch_local_irq_restore"
+       :
+       : "r" (flags)
+       : "memory");
+}
+
+static inline int arch_irqs_disabled_flags(unsigned long flags)
+{
+       return flags & PSR_I_BIT;
+}
+
+/*
+ * save and restore debug state
+ */
+#define local_dbg_save(flags)                                          \
+       do {                                                            \
+               typecheck(unsigned long, flags);                        \
+               asm volatile(                                           \
+               "mrs    %0, daif                // local_dbg_save\n"    \
+               "msr    daifset, #8"                                    \
+               : "=r" (flags) : : "memory");                           \
+       } while (0)
+
+#define local_dbg_restore(flags)                                       \
+       do {                                                            \
+               typecheck(unsigned long, flags);                        \
+               asm volatile(                                           \
+               "msr    daif, %0                // local_dbg_restore\n" \
+               : : "r" (flags) : "memory");                            \
+       } while (0)
+
+#endif
+#endif
index c119e66..4da2517 100644 (file)
@@ -56,7 +56,6 @@ struct aml_wdt_dev {
        struct notifier_block reboot_notifier;
 };
 
-
 static void aml_update_bits(void __iomem  *reg, unsigned int mask,
                                                        unsigned int val)
 {
@@ -295,6 +294,24 @@ static struct notifier_block aml_wdt_reboot_notifier = {
        .notifier_call = aml_wtd_reboot_notify,
 };
 
+#ifdef CONFIG_AMLOGIC_DEBUG_LOCKUP
+/* HARDLOCKUP safe window: watchdog_thresh * 2 * /5 *3 *2 = 24 second*/
+#define HARDLOCKUP_WIN  30
+struct aml_wdt_dev *g_awdt;
+void aml_wdt_disable_dbg(void)
+{
+       static int flg;
+       int cnt;
+
+       if (!g_awdt || flg)
+               return;
+       cnt = readl(g_awdt->reg_base + TCNT) & 0xffff;
+       if (cnt < HARDLOCKUP_WIN * g_awdt->one_second)
+               cnt = HARDLOCKUP_WIN * g_awdt->one_second;
+       set_watchdog_cnt(g_awdt, cnt);
+}
+#endif
+
 static int aml_wdt_probe(struct platform_device *pdev)
 {
        struct watchdog_device *aml_wdt;
@@ -340,7 +357,9 @@ static int aml_wdt_probe(struct platform_device *pdev)
        register_pm_notifier(&wdev->pm_notifier);
        register_reboot_notifier(&wdev->reboot_notifier);
        dev_info(wdev->dev, "AML Watchdog Timer probed done\n");
-
+#ifdef CONFIG_AMLOGIC_DEBUG_LOCKUP
+       g_awdt = wdev;
+#endif
        return 0;
 }
 
diff --git a/include/linux/amlogic/debug_lockup.h b/include/linux/amlogic/debug_lockup.h
new file mode 100644 (file)
index 0000000..25f746b
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef __debug_lockup_h_
+#define __debug_lockup_h_
+
+
+void irq_trace_stop(unsigned long flag);
+void irq_trace_start(unsigned long flag);
+void pr_lockup_info(int cpu);
+void lockup_hook(int cpu);
+void isr_in_hook(unsigned int c, unsigned long *t, unsigned int i, void *a);
+void isr_out_hook(unsigned int cpu, unsigned long tin, unsigned int irq);
+void irq_trace_en(int en);
+void sirq_in_hook(unsigned int cpu, unsigned long *tin, void *p);
+void sirq_out_hook(unsigned int cpu, unsigned long tin, void *p);
+void aml_wdt_disable_dbg(void);
+#endif
index f30110e..0b4c21c 100644 (file)
@@ -764,9 +764,19 @@ void handle_percpu_devid_irq(struct irq_desc *desc)
                chip->irq_ack(&desc->irq_data);
 
        if (likely(action)) {
+
+#ifdef CONFIG_AMLOGIC_DEBUG_LOCKUP
+               unsigned long tin;
+               unsigned int cpu = smp_processor_id();
+
+               isr_in_hook(cpu, &tin, irq, action->handler);
+#endif
                trace_irq_handler_entry(irq, action);
                res = action->handler(irq, raw_cpu_ptr(action->percpu_dev_id));
                trace_irq_handler_exit(irq, action, res);
+#ifdef CONFIG_AMLOGIC_DEBUG_LOCKUP
+               isr_out_hook(cpu, tin, irq);
+#endif
        } else {
                unsigned int cpu = smp_processor_id();
                bool enabled = cpumask_test_cpu(cpu, desc->percpu_enabled);
index d3f2490..063553f 100644 (file)
@@ -141,9 +141,18 @@ irqreturn_t __handle_irq_event_percpu(struct irq_desc *desc, unsigned int *flags
        for_each_action_of_desc(desc, action) {
                irqreturn_t res;
 
+#ifdef CONFIG_AMLOGIC_DEBUG_LOCKUP
+               unsigned long tin;
+               unsigned int cpu = smp_processor_id();
+
+               isr_in_hook(cpu, &tin, irq, action->handler);
+#endif
                trace_irq_handler_entry(irq, action);
                res = action->handler(irq, action->dev_id);
                trace_irq_handler_exit(irq, action, res);
+#ifdef CONFIG_AMLOGIC_DEBUG_LOCKUP
+               isr_out_hook(cpu, tin, irq);
+#endif
 
                if (WARN_ONCE(!irqs_disabled(),"irq %u handler %pF enabled interrupts\n",
                              irq, action->handler))
index 744fa61..8c832fb 100644 (file)
@@ -247,6 +247,10 @@ asmlinkage __visible void __softirq_entry __do_softirq(void)
        bool in_hardirq;
        __u32 pending;
        int softirq_bit;
+#ifdef CONFIG_AMLOGIC_DEBUG_LOCKUP
+       int cpu;
+       unsigned long tin;
+#endif
 
        /*
         * Mask out PF_MEMALLOC s current task context is borrowed for the
@@ -281,7 +285,14 @@ restart:
                kstat_incr_softirqs_this_cpu(vec_nr);
 
                trace_softirq_entry(vec_nr);
+#ifdef CONFIG_AMLOGIC_DEBUG_LOCKUP
+               cpu = smp_processor_id();
+               sirq_in_hook(cpu, &tin, (void *)h->action);
+#endif
                h->action(h);
+#ifdef CONFIG_AMLOGIC_DEBUG_LOCKUP
+               sirq_out_hook(cpu, tin, (void *)h->action);
+#endif
                trace_softirq_exit(vec_nr);
                if (unlikely(prev_count != preempt_count())) {
                        pr_err("huh, entered softirq %u %s %p with preempt_count %08x, exited with %08x?\n",
index 3d10a47..a21e2c4 100644 (file)
@@ -969,7 +969,7 @@ static struct ctl_table kern_table[] = {
                .extra1         = &zero,
                .extra2         = &one,
        },
-#ifdef CONFIG_HARDLOCKUP_DETECTOR
+#if defined(CONFIG_HARDLOCKUP_DETECTOR)
        {
                .procname       = "hardlockup_panic",
                .data           = &hardlockup_panic,
index 0b3638c..9df5ac2 100644 (file)
@@ -26,6 +26,8 @@
 #include <linux/kvm_para.h>
 #include <linux/kthread.h>
 
+#include <linux/amlogic/debug_lockup.h>
+
 static DEFINE_MUTEX(watchdog_proc_mutex);
 
 #if defined(CONFIG_HAVE_NMI_WATCHDOG) || defined(CONFIG_HARDLOCKUP_DETECTOR)
@@ -241,6 +243,22 @@ static void watchdog_interrupt_count(void)
        __this_cpu_inc(hrtimer_interrupts);
 }
 
+unsigned long watchdog_get_interrupt_count_cpu(int cpu)
+{
+       return per_cpu(hrtimer_interrupts, cpu);
+}
+/*
+ * These two functions are mostly architecture specific
+ * defining them as weak here.
+ */
+int __weak watchdog_nmi_enable(unsigned int cpu)
+{
+       return 0;
+}
+void __weak watchdog_nmi_disable(unsigned int cpu)
+{
+}
+
 static int watchdog_enable_all_cpus(void);
 static void watchdog_disable_all_cpus(void);
 
index ac528e1..9a42b8b 100644 (file)
@@ -129,6 +129,9 @@ static void watchdog_overflow_callback(struct perf_event *event,
                                !test_and_set_bit(0, &hardlockup_allcpu_dumped))
                        trigger_allbutself_cpu_backtrace();
 
+#ifdef CONFIG_AMLOGIC_DEBUG_LOCKUP
+               pr_lockup_info(next_cpu);
+#endif
                if (hardlockup_panic)
                        nmi_panic(regs, "Hard LOCKUP");
 
@@ -198,6 +201,9 @@ void watchdog_check_hardlockup_other_cpu(void)
                if (per_cpu(hard_watchdog_warn, next_cpu) == true)
                        return;
 
+#ifdef CONFIG_AMLOGIC_DEBUG_LOCKUP
+               pr_lockup_info(next_cpu);
+#endif
                if (hardlockup_panic)
                        panic("Watchdog detected hard LOCKUP on cpu %u",
                                next_cpu);