From: Bixuan Cui cuibixuan@huawei.com
ascend inclusion category: feature bugzilla: NA CVE: NA
---------------------------------------------
Itrace (Interrupt Tracing) is a lightweight interrupt tracing tool. It supports the following functions: * Ihandler(Interrupt Handler) tracer can trace and calculate the time consumed of the hardware irq function and list the name, number and duration of the interrupt. * Ihandler supports dynamic disable and threshold setting.
Signed-off-by: Bixuan Cui cuibixuan@huawei.com Reviewed-by: Ding Tianhong dingtianhong@huawei.com Reviewed-by: Hanjun Guo guohanjun@huawei.com Signed-off-by: Yang Yingliang yangyingliang@huawei.com --- include/linux/itrace.h | 47 ++++++++++ kernel/irq/handle.c | 3 + kernel/irq/proc.c | 100 +++++++++++++++++++++ kernel/trace/Kconfig | 22 +++++ kernel/trace/Makefile | 2 + kernel/trace/itrace_ihandler.c | 154 +++++++++++++++++++++++++++++++++ 6 files changed, 328 insertions(+) create mode 100644 include/linux/itrace.h create mode 100644 kernel/trace/itrace_ihandler.c
diff --git a/include/linux/itrace.h b/include/linux/itrace.h new file mode 100644 index 0000000000000..d7916581e03f2 --- /dev/null +++ b/include/linux/itrace.h @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright(c) 2021 Huawei Technologies Co., Ltd + * Author: Bixuan Cui cuibixuan@huawei.com + */ + +#ifndef __LINUX_ITRACE_H +#define __LINUX_ITRACE_H + +#define IRQ_NAME_LEN 20 +#define IHANDLER_INFO_NUM_MIN 1 +#define IHANDLER_INFO_NUM_MAX 30 +#define IHANDLER_INFO_CT_MAX 99999 +#define IHANDLER_THRESHOLD_MAX 10000000 +#define IHANDLER_OFF 0 +#define IHANDLER_ON 1 + +struct irq_handler_info { + int irq; + char name[IRQ_NAME_LEN]; + u64 t_max; + unsigned int ct; +}; + +struct Ihandler { + int front; + int num; + struct irq_handler_info info[IHANDLER_INFO_NUM_MAX]; +}; + +#ifdef CONFIG_ITRACE_IHANDLER +extern void itrace_ihandler_entry(void); +extern void itrace_ihandler_exit(int irq, const char *name); +extern void itrace_ihandler_set(u64 set); +extern void itrace_ihandler_get(struct Ihandler *ih, int cpu); +extern void itrace_ihandler_num_set(int set); +extern int itrace_ihandler_num_get(void); +#else +static inline void __maybe_unused itrace_ihandler_entry(void) +{ +}; +static inline void __maybe_unused itrace_ihandler_exit(int irq, const char *name) +{ +}; +#endif /* CONFIG_ITRACE_IHANDLER */ + +#endif /* __LINUX_ITRACE_H */ diff --git a/kernel/irq/handle.c b/kernel/irq/handle.c index 38554bc353755..95caa394df603 100644 --- a/kernel/irq/handle.c +++ b/kernel/irq/handle.c @@ -13,6 +13,7 @@ #include <linux/sched.h> #include <linux/interrupt.h> #include <linux/kernel_stat.h> +#include <linux/itrace.h>
#include <trace/events/irq.h>
@@ -146,7 +147,9 @@ irqreturn_t __handle_irq_event_percpu(struct irq_desc *desc, unsigned int *flags irqreturn_t res;
trace_irq_handler_entry(irq, action); + itrace_ihandler_entry(); res = action->handler(irq, action->dev_id); + itrace_ihandler_exit(irq, action->name); trace_irq_handler_exit(irq, action, res);
if (WARN_ONCE(!irqs_disabled(),"irq %u handler %pF enabled interrupts\n", diff --git a/kernel/irq/proc.c b/kernel/irq/proc.c index e8c655b7a4308..1d5b4b0154720 100644 --- a/kernel/irq/proc.c +++ b/kernel/irq/proc.c @@ -12,6 +12,7 @@ #include <linux/interrupt.h> #include <linux/kernel_stat.h> #include <linux/mutex.h> +#include <linux/itrace.h>
#include "internals.h"
@@ -299,6 +300,99 @@ static int irq_node_proc_show(struct seq_file *m, void *v) } #endif
+#ifdef CONFIG_ITRACE_IHANDLER +static int itrace_ihandler_num_show(struct seq_file *m, void *v) +{ + seq_printf(m, "%d\n", itrace_ihandler_num_get()); + + return 0; +} + +static ssize_t itrace_ihandler_num_write(struct file *file, + const char __user *buffer, size_t count, loff_t *ppos) +{ + int ret; + int num; + + ret = kstrtoint_from_user(buffer, count, 10, &num); + if (ret) + return ret; + + if (num > IHANDLER_INFO_NUM_MAX || num < IHANDLER_INFO_NUM_MIN) + return -EINVAL; + + itrace_ihandler_num_set(num); + + return count; +} + +static int itrace_ihandler_num_open(struct inode *inode, struct file *file) +{ + return single_open(file, itrace_ihandler_num_show, PDE_DATA(inode)); +} + +static const struct file_operations itrace_ihandler_num_proc_fops = { + .open = itrace_ihandler_num_open, + .read = seq_read, + .release = single_release, + .write = itrace_ihandler_num_write, +}; + +static int itrace_ihandler_show(struct seq_file *m, void *v) +{ + unsigned int i, j; + struct Ihandler ihandler; + int online_cpus = num_online_cpus(); + + for (i = 0; i < online_cpus; i++) { + itrace_ihandler_get(&ihandler, i); + + /* print nothing while num is 0 */ + if (ihandler.num == 0) + return 0; + + seq_printf(m, "[irq_handler CPU%d]:\n", i); + for (j = 0; j < ihandler.num; j++) + seq_printf(m, " irq:%d name:%s max_time:%llu(us) count:%u\n", + ihandler.info[j].irq, ihandler.info[j].name, + ihandler.info[j].t_max / NSEC_PER_USEC, + ihandler.info[j].ct); + } + + return 0; +} + +static ssize_t itrace_ihandler_write(struct file *file, + const char __user *buffer, size_t count, loff_t *ppos) +{ + int ret; + u64 val; + + ret = kstrtoull_from_user(buffer, count, 10, &val); + if (ret) + return ret; + + if (val > IHANDLER_THRESHOLD_MAX) + return -EINVAL; + + itrace_ihandler_set(val); + + return count; +} + +static int itrace_ihandler_open(struct inode *inode, struct file *file) +{ + return single_open(file, itrace_ihandler_show, PDE_DATA(inode)); +} + +static const struct file_operations itrace_ihandler_proc_fops = { + .open = itrace_ihandler_open, + .read = seq_read, + .release = single_release, + .write = itrace_ihandler_write, +}; +#endif + static int irq_spurious_proc_show(struct seq_file *m, void *v) { struct irq_desc *desc = irq_to_desc((long) m->private); @@ -458,6 +552,12 @@ void init_irq_proc(void) */ for_each_irq_desc(irq, desc) register_irq_proc(irq, desc); +#ifdef CONFIG_ITRACE_IHANDLER + proc_create("irq/itrace_ihandler", 0644, NULL, + &itrace_ihandler_proc_fops); + proc_create("irq/itrace_ihandler_num", 0644, NULL, + &itrace_ihandler_num_proc_fops); +#endif }
#ifdef CONFIG_GENERIC_IRQ_SHOW diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig index e656d1e232da4..5dd2e37f1aae0 100644 --- a/kernel/trace/Kconfig +++ b/kernel/trace/Kconfig @@ -788,5 +788,27 @@ config GCOV_PROFILE_FTRACE
endif # FTRACE
+menuconfig ITRACE + bool "Itrace Support" + help + Itrace(Interrupt Tracing) is a lightweight interrupt tracing + tool. + It can trace the areas that interrupt handler function and + disable interrupts. + +if ITRACE + +config ITRACE_IHANDLER + bool "Support for tracing the interrupt handler function" + default n + help + Ihandler(Interrupt Handler) tracer can trace and calculate + the time consumed of the hardware irq function and list the + name, number and duration of the interrupt. + + Support dynamic disable and threshold setting. + +endif #ITRACE + endif # TRACING_SUPPORT
diff --git a/kernel/trace/Makefile b/kernel/trace/Makefile index f81dadbc7c4ac..231ce3c22ea4e 100644 --- a/kernel/trace/Makefile +++ b/kernel/trace/Makefile @@ -84,3 +84,5 @@ obj-$(CONFIG_UPROBE_EVENTS) += trace_uprobe.o obj-$(CONFIG_TRACEPOINT_BENCHMARK) += trace_benchmark.o
libftrace-y := ftrace.o + +obj-$(CONFIG_ITRACE_IHANDLER) += itrace_ihandler.o diff --git a/kernel/trace/itrace_ihandler.c b/kernel/trace/itrace_ihandler.c new file mode 100644 index 0000000000000..906e2e0b1a0f8 --- /dev/null +++ b/kernel/trace/itrace_ihandler.c @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright(c) 2021 Huawei Technologies Co., Ltd + * Author: Bixuan Cui cuibixuan@huawei.com + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/timer.h> +#include <linux/jiffies.h> +#include <linux/sched/clock.h> +#include <linux/itrace.h> +#include <linux/time64.h> + +static u64 threshold_value; +static int ihandler_info_num = 5; +static int ihandler_enable; + +static DEFINE_PER_CPU(struct Ihandler, ihandler); +static DEFINE_PER_CPU(u64, ihandler_entry); + +/* Per-cpu variable to prevent redundant calls when already entry handler */ +static DEFINE_PER_CPU(int, ihandle_flag); + +void itrace_ihandler_entry(void) +{ + if (ihandler_enable == IHANDLER_OFF) + return; + + if (!this_cpu_read(ihandle_flag)) { + this_cpu_write(ihandle_flag, 1); + + this_cpu_write(ihandler_entry, sched_clock()); + } +} + +static void itrace_insert_diff(int cpu, u64 diff, int irq, const char *name) +{ + int j, index, find = 0; + char *ihandler_name; + unsigned int ct; + u64 t_max; + int front = per_cpu(ihandler, cpu).front; + int num = per_cpu(ihandler, cpu).num; + + for (j = 0; j < num; j++) { + index = (front + j) % num; + if (per_cpu(ihandler, cpu).info[index].irq == irq) { + find = 1; + break; + } + } + + if (find != 0) { + t_max = per_cpu(ihandler, cpu).info[index].t_max; + ct = per_cpu(ihandler, cpu).info[index].ct + 1; + + per_cpu(ihandler, cpu).info[index].t_max = diff > t_max ? + diff : t_max; + per_cpu(ihandler, cpu).info[index].ct = ct > + IHANDLER_INFO_CT_MAX ? IHANDLER_INFO_CT_MAX : ct; + } else { + num = num + 1; + ihandler_name = per_cpu(ihandler, cpu).info[front].name; + + per_cpu(ihandler, cpu).info[front].irq = irq; + strncpy(ihandler_name, name, IRQ_NAME_LEN - 1); + ihandler_name[IRQ_NAME_LEN - 1] = '\0'; + per_cpu(ihandler, cpu).info[front].t_max = diff; + per_cpu(ihandler, cpu).front = (front + 1) % + ihandler_info_num; + per_cpu(ihandler, cpu).num = num > ihandler_info_num ? + ihandler_info_num : num; + per_cpu(ihandler, cpu).info[front].ct += 1; + + } +} + +void itrace_ihandler_exit(int irq, const char *name) +{ + u64 diff; + int cpu; + + if (ihandler_enable == IHANDLER_OFF) + return; + + if (this_cpu_read(ihandle_flag)) { + cpu = smp_processor_id(); + diff = sched_clock() - per_cpu(ihandler_entry, cpu); + + if (diff > threshold_value) + itrace_insert_diff(cpu, diff, irq, name); + + this_cpu_write(ihandle_flag, 0); + } +} + +void itrace_ihandler_set(u64 set) +{ + unsigned int i, j; + int online_cpus = num_online_cpus(); + + /* disable tracer and update threshold_value first */ + ihandler_enable = IHANDLER_OFF; + threshold_value = set * NSEC_PER_USEC; + + for (i = 0; i < online_cpus; i++) { + + per_cpu(ihandler, i).front = 0; + per_cpu(ihandler, i).num = 0; + for (j = 0; j < IHANDLER_INFO_NUM_MAX; j++) { + per_cpu(ihandler, i).info[j].irq = 0; + per_cpu(ihandler, i).info[j].name[0] = '\0'; + per_cpu(ihandler, i).info[j].t_max = 0; + per_cpu(ihandler, i).info[j].ct = 0; + } + + /* enable tracer */ + if (set != 0) { + per_cpu(ihandle_flag, i) = 0; + ihandler_enable = IHANDLER_ON; + } + } +} + +void itrace_ihandler_get(struct Ihandler *ih, int cpu) +{ + unsigned int j; + char *ihandler_name; + + for (j = 0; j < ihandler_info_num; j++) { + ihandler_name = per_cpu(ihandler, cpu).info[j].name; + + ih->num = per_cpu(ihandler, cpu).num; + ih->info[j].irq = per_cpu(ihandler, cpu).info[j].irq; + strncpy(ih->info[j].name, ihandler_name, IRQ_NAME_LEN); + ih->info[j].t_max = per_cpu(ihandler, cpu).info[j].t_max; + ih->info[j].ct = per_cpu(ihandler, cpu).info[j].ct; + } +} + +void itrace_ihandler_num_set(int set) +{ + ihandler_info_num = set; + + /* clear ihandler of per cpu while reset info_num */ + itrace_ihandler_set(threshold_value / NSEC_PER_USEC); +} + +int itrace_ihandler_num_get(void) +{ + return ihandler_info_num; +}
From: Bixuan Cui cuibixuan@huawei.com
ascend inclusion category: feature bugzilla: NA CVE: NA
---------------------------------------------
Add Irqsoff tracer into itrace to trace the areas that disable interrupts Irqsoff supports dynamic disable and threshold setting.
Signed-off-by: Bixuan Cui cuibixuan@huawei.com Reviewed-by: Ding Tianhong dingtianhong@huawei.com Reviewed-by: Hanjun Guo guohanjun@huawei.com Signed-off-by: Yang Yingliang yangyingliang@huawei.com --- include/linux/irqflags.h | 46 +++++++++++- include/linux/itrace.h | 31 ++++++++ kernel/irq/proc.c | 98 +++++++++++++++++++++++++ kernel/trace/Kconfig | 11 +++ kernel/trace/Makefile | 1 + kernel/trace/itrace_irqsoff.c | 132 ++++++++++++++++++++++++++++++++++ 6 files changed, 317 insertions(+), 2 deletions(-) create mode 100644 kernel/trace/itrace_irqsoff.c
diff --git a/include/linux/irqflags.h b/include/linux/irqflags.h index 21619c92c3770..18f674deeb726 100644 --- a/include/linux/irqflags.h +++ b/include/linux/irqflags.h @@ -14,6 +14,7 @@
#include <linux/typecheck.h> #include <asm/irqflags.h> +#include <linux/itrace.h>
/* Currently trace_softirqs_on/off is used only by lockdep */ #ifdef CONFIG_PROVE_LOCKING @@ -69,7 +70,11 @@ do { \ extern void stop_critical_timings(void); extern void start_critical_timings(void); #else +#ifdef CONFIG_ITRACE_IRQSOFF +# define stop_critical_timings() itrace_hardirqs_ignore() +#else # define stop_critical_timings() do { } while (0) +#endif # define start_critical_timings() do { } while (0) #endif
@@ -135,7 +140,44 @@ do { \ } while (0)
-#else /* !CONFIG_TRACE_IRQFLAGS */ +#elif defined(CONFIG_ITRACE_IRQSOFF) /* CONFIG_ITRACE_IRQSOFF */ + +#define local_irq_enable() \ + do { \ + itrace_hardirqs_on(); \ + raw_local_irq_enable(); \ + } while (0) + +#define local_irq_disable() \ + do { \ + raw_local_irq_disable(); \ + itrace_hardirqs_off(); \ + } while (0) + +#define local_irq_save(flags) \ + do { \ + raw_local_irq_save(flags); \ + itrace_hardirqs_off(); \ + } while (0) + +#define local_irq_restore(flags) \ + do { \ + if (raw_irqs_disabled_flags(flags)) { \ + raw_local_irq_restore(flags); \ + itrace_hardirqs_off(); \ + } else { \ + itrace_hardirqs_on(); \ + raw_local_irq_restore(flags); \ + } \ + } while (0) + +#define safe_halt() \ + do { \ + itrace_hardirqs_on(); \ + raw_safe_halt(); \ + } while (0) + +#else
#define local_irq_enable() do { raw_local_irq_enable(); } while (0) #define local_irq_disable() do { raw_local_irq_disable(); } while (0) @@ -146,7 +188,7 @@ do { \ #define local_irq_restore(flags) do { raw_local_irq_restore(flags); } while (0) #define safe_halt() do { raw_safe_halt(); } while (0)
-#endif /* CONFIG_TRACE_IRQFLAGS */ +#endif /* CONFIG_TRACE_IRQFLAGS && CONFIG_ITRACE_IRQFLAGS */
#define local_save_flags(flags) raw_local_save_flags(flags)
diff --git a/include/linux/itrace.h b/include/linux/itrace.h index d7916581e03f2..aba6396857b38 100644 --- a/include/linux/itrace.h +++ b/include/linux/itrace.h @@ -15,6 +15,13 @@ #define IHANDLER_OFF 0 #define IHANDLER_ON 1
+#define CALLER_FUNC_LEN 50 +#define IRQSOFF_INFO_NUM_MIN 1 +#define IRQSOFF_INFO_NUM_MAX 30 +#define IRQSOFF_THRESHOLD_MAX 10000000 +#define IRQSOFF_OFF 0 +#define IRQSOFF_ON 1 + struct irq_handler_info { int irq; char name[IRQ_NAME_LEN]; @@ -28,6 +35,17 @@ struct Ihandler { struct irq_handler_info info[IHANDLER_INFO_NUM_MAX]; };
+struct irqsoff_info { + u64 t_max; + char caller[CALLER_FUNC_LEN]; +}; + +struct Irqsoff { + int front; + int num; + struct irqsoff_info info[IRQSOFF_INFO_NUM_MAX]; +}; + #ifdef CONFIG_ITRACE_IHANDLER extern void itrace_ihandler_entry(void); extern void itrace_ihandler_exit(int irq, const char *name); @@ -44,4 +62,17 @@ static inline void __maybe_unused itrace_ihandler_exit(int irq, const char *name }; #endif /* CONFIG_ITRACE_IHANDLER */
+#ifdef CONFIG_ITRACE_IRQSOFF +extern void itrace_hardirqs_on(void); +extern void itrace_hardirqs_off(void); +extern void itrace_hardirqs_ignore(void); +extern void itrace_irqsoff_set(u64 set); +extern void itrace_irqsoff_get(struct Irqsoff *is, int cpu); +extern void itrace_irqsoff_num_set(int set); +extern int itrace_irqsoff_num_get(void); +#else +# define itrace_hardirqs_on() do { } while (0) +# define itrace_hardirqs_on() do { } while (0) +#endif /* CONFIG_ITRACE_IRQSOFF */ + #endif /* __LINUX_ITRACE_H */ diff --git a/kernel/irq/proc.c b/kernel/irq/proc.c index 1d5b4b0154720..1d50b5b855d21 100644 --- a/kernel/irq/proc.c +++ b/kernel/irq/proc.c @@ -393,6 +393,98 @@ static const struct file_operations itrace_ihandler_proc_fops = { }; #endif
+#ifdef CONFIG_ITRACE_IRQSOFF +static int itrace_irqsoff_num_show(struct seq_file *m, void *v) +{ + seq_printf(m, "%d\n", itrace_irqsoff_num_get()); + + return 0; +} + +static ssize_t itrace_irqsoff_num_write(struct file *file, + const char __user *buffer, size_t count, loff_t *ppos) +{ + int ret; + int num; + + ret = kstrtoint_from_user(buffer, count, 10, &num); + if (ret) + return ret; + + if (num > IRQSOFF_INFO_NUM_MAX || num < IRQSOFF_INFO_NUM_MIN) + return -EINVAL; + + itrace_irqsoff_num_set(num); + + return count; +} + +static int itrace_irqsoff_num_open(struct inode *inode, struct file *file) +{ + return single_open(file, itrace_irqsoff_num_show, PDE_DATA(inode)); +} + +static const struct file_operations itrace_irqsoff_num_proc_fops = { + .open = itrace_irqsoff_num_open, + .read = seq_read, + .release = single_release, + .write = itrace_irqsoff_num_write, +}; + +static int itrace_irqsoff_show(struct seq_file *m, void *v) +{ + unsigned int i, j; + int online_cpus = num_online_cpus(); + struct Irqsoff irqsoff; + + for (i = 0; i < online_cpus; i++) { + itrace_irqsoff_get(&irqsoff, i); + + /* print nothing while num is 0 */ + if (irqsoff.num == 0) + continue; + + seq_printf(m, "[irqsoff CPU%d]:\n", i); + for (j = 0; j < irqsoff.num; j++) + seq_printf(m, " max_time:%llu(us) caller:%s\n", + irqsoff.info[j].t_max / NSEC_PER_USEC, + irqsoff.info[j].caller); + } + + return 0; +} + +static ssize_t itrace_irqsoff_write(struct file *file, + const char __user *buffer, size_t count, loff_t *ppos) +{ + int ret; + u64 val; + + ret = kstrtoull_from_user(buffer, count, 10, &val); + if (ret) + return ret; + + if (val > IRQSOFF_THRESHOLD_MAX) + return -EINVAL; + + itrace_irqsoff_set(val); + + return count; +} + +static int itrace_irqsoff_open(struct inode *inode, struct file *file) +{ + return single_open(file, itrace_irqsoff_show, PDE_DATA(inode)); +} + +static const struct file_operations itrace_irqsoff_proc_fops = { + .open = itrace_irqsoff_open, + .read = seq_read, + .release = single_release, + .write = itrace_irqsoff_write, +}; +#endif + static int irq_spurious_proc_show(struct seq_file *m, void *v) { struct irq_desc *desc = irq_to_desc((long) m->private); @@ -558,6 +650,12 @@ void init_irq_proc(void) proc_create("irq/itrace_ihandler_num", 0644, NULL, &itrace_ihandler_num_proc_fops); #endif +#ifdef CONFIG_ITRACE_IRQSOFF + proc_create("irq/itrace_irqsoff", 0644, NULL, + &itrace_irqsoff_proc_fops); + proc_create("irq/itrace_irqsoff_num", 0644, NULL, + &itrace_irqsoff_num_proc_fops); +#endif }
#ifdef CONFIG_GENERIC_IRQ_SHOW diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig index 5dd2e37f1aae0..36d4ded6ae74b 100644 --- a/kernel/trace/Kconfig +++ b/kernel/trace/Kconfig @@ -808,6 +808,17 @@ config ITRACE_IHANDLER
Support dynamic disable and threshold setting.
+config ITRACE_IRQSOFF + bool "Support for tracing the irqsoff" + depends on !TRACE_IRQFLAGS && !IRQSOFF_TRACER && !PREEMPT_TRACER + default n + help + Irqsoff tracer is a lightweight tracing tool. It can trace + the areas that disable interrupts and saves the trace with + the latency. + + Support dynamic disable and threshold setting. + endif #ITRACE
endif # TRACING_SUPPORT diff --git a/kernel/trace/Makefile b/kernel/trace/Makefile index 231ce3c22ea4e..c0e8ed93adf28 100644 --- a/kernel/trace/Makefile +++ b/kernel/trace/Makefile @@ -86,3 +86,4 @@ obj-$(CONFIG_TRACEPOINT_BENCHMARK) += trace_benchmark.o libftrace-y := ftrace.o
obj-$(CONFIG_ITRACE_IHANDLER) += itrace_ihandler.o +obj-$(CONFIG_ITRACE_IRQSOFF) += itrace_irqsoff.o diff --git a/kernel/trace/itrace_irqsoff.c b/kernel/trace/itrace_irqsoff.c new file mode 100644 index 0000000000000..15444755de712 --- /dev/null +++ b/kernel/trace/itrace_irqsoff.c @@ -0,0 +1,132 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright(c) 2021 Huawei Technologies Co., Ltd + * Author: Bixuan Cui cuibixuan@huawei.com + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/timer.h> +#include <linux/jiffies.h> +#include <linux/sched/clock.h> +#include <linux/itrace.h> + +static u64 threshold_value; +static int irqsoff_info_num = 3; +static int irqsoff_enable; + +static DEFINE_PER_CPU(struct Irqsoff, irqsoff); +static DEFINE_PER_CPU(u64, irqsoff_off); + +/* Per-cpu variable to prevent redundant calls when IRQs already off */ +static DEFINE_PER_CPU(u64, irqsoff_flag); + +void itrace_hardirqs_on(void) +{ + u64 diff; + int front, cpu, num; + char *caller; + + if (irqsoff_enable == IRQSOFF_OFF) + return; + + if (this_cpu_read(irqsoff_flag)) { + cpu = smp_processor_id(); + diff = sched_clock() - per_cpu(irqsoff_off, cpu); + + if (diff > threshold_value) { + front = per_cpu(irqsoff, cpu).front; + num = per_cpu(irqsoff, cpu).num + 1; + caller = per_cpu(irqsoff, cpu).info[front].caller; + + per_cpu(irqsoff, cpu).info[front].t_max = diff; + snprintf(caller, CALLER_FUNC_LEN, "%pS", + __builtin_return_address(0)); + + per_cpu(irqsoff, cpu).front = (front + 1) % + irqsoff_info_num; + per_cpu(irqsoff, cpu).num = num > irqsoff_info_num ? + irqsoff_info_num : num; + } + + this_cpu_write(irqsoff_flag, 0); + } +} +EXPORT_SYMBOL(itrace_hardirqs_on); + +void itrace_hardirqs_off(void) +{ + if (irqsoff_enable == IRQSOFF_OFF) + return; + + if (!this_cpu_read(irqsoff_flag)) { + this_cpu_write(irqsoff_flag, 1); + + this_cpu_write(irqsoff_off, sched_clock()); + } +} +EXPORT_SYMBOL(itrace_hardirqs_off); + +void itrace_hardirqs_ignore(void) +{ + if (irqsoff_enable == IRQSOFF_OFF) + return; + + if (this_cpu_read(irqsoff_flag)) + this_cpu_write(irqsoff_flag, 0); +} +EXPORT_SYMBOL(itrace_hardirqs_ignore); + +void itrace_irqsoff_set(u64 set) +{ + unsigned int i, j; + int online_cpus = num_online_cpus(); + + /* disable tracer and update threshold_value first */ + irqsoff_enable = IRQSOFF_OFF; + threshold_value = set * NSEC_PER_USEC; + + for (i = 0; i < online_cpus; i++) { + + per_cpu(irqsoff, i).front = 0; + per_cpu(irqsoff, i).num = 0; + for (j = 0; j < IRQSOFF_INFO_NUM_MAX; j++) { + per_cpu(irqsoff, i).info[j].t_max = 0; + per_cpu(irqsoff, i).info[j].caller[0] = '\0'; + } + + /* enable tracer */ + if (set != 0) { + per_cpu(irqsoff_flag, i) = 0; + irqsoff_enable = IRQSOFF_ON; + } + } +} + +void itrace_irqsoff_get(struct Irqsoff *is, int cpu) +{ + unsigned int j; + char *caller; + + for (j = 0; j < irqsoff_info_num; j++) { + caller = per_cpu(irqsoff, cpu).info[j].caller; + + is->num = per_cpu(irqsoff, cpu).num; + is->info[j].t_max = per_cpu(irqsoff, cpu).info[j].t_max; + strncpy(is->info[j].caller, caller, CALLER_FUNC_LEN); + } +} + +void itrace_irqsoff_num_set(int set) +{ + irqsoff_info_num = set; + + /* clear irqsoff.num while reset info_num */ + itrace_irqsoff_set(threshold_value / NSEC_PER_USEC); +} + +int itrace_irqsoff_num_get(void) +{ + return irqsoff_info_num; +}
From: Bixuan Cui cuibixuan@huawei.com
ascend inclusion category: feature bugzilla: NA CVE: NA
---------------------------------------------
Add documentation for itrace.
Signed-off-by: Bixuan Cui cuibixuan@huawei.com Reviewed-by: Ding Tianhong dingtianhong@huawei.com Reviewed-by: Hanjun Guo guohanjun@huawei.com Signed-off-by: Yang Yingliang yangyingliang@huawei.com --- Documentation/trace/index.rst | 1 + Documentation/trace/itrace.rst | 109 +++++++++++++++++++++++++++++++++ 2 files changed, 110 insertions(+) create mode 100644 Documentation/trace/itrace.rst
diff --git a/Documentation/trace/index.rst b/Documentation/trace/index.rst index 306997941ba1b..4500ef4cdaad5 100644 --- a/Documentation/trace/index.rst +++ b/Documentation/trace/index.rst @@ -22,3 +22,4 @@ Linux Tracing Technologies hwlat_detector intel_th stm + itrace diff --git a/Documentation/trace/itrace.rst b/Documentation/trace/itrace.rst new file mode 100644 index 0000000000000..4986133e9a22a --- /dev/null +++ b/Documentation/trace/itrace.rst @@ -0,0 +1,109 @@ +.. SPDX-License-Identifier: GPL-2.0 + +============= +Interrupts Tracing +============= + +:Author: Bixuan Cui + +1. Introduction +=============== + +Itrace (Interrupt Tracing) is a lightweight interrupt tracing tool. It supports +the following functions: + +* Ihandler(Interrupt Handler) tracer can trace and calculate the time consumed of + the hardware irq function and list the name, number and duration of the interrupt. + +* Irqsoff tracer can trace the areas that disable interrupts. + +* Ihandler and Irqsoff support dynamic disable and threshold setting. + +2. Using Ihandler Tracing +====================== + +2.1 Enable Ihandler Tracing +--------------------------------- + +Echo threshold val to /proc/irq/itrace_ihandler to enable ihandler tracer. The +threshold value ranges from 1 to 10000000, in microsecond. For example:: + + # echo 20 > /proc/irq/itrace_ihandler + +Cat /proc/irq/itrace_ihandler to get the message of ihandler. For example:: + + # echo 1 > /proc/irq/itrace_ihandler + # cat /proc/irq/itrace_ihandler + [irq_handler CPU0]: + irq:14 name:uart-pl011 max_time:8(us) count:278 + irq:31 name:ufshcd max_time:44(us) count:1684 + irq:565 name:mailbox-1-lp-rx-acp max_time:6(us) count:10 + irq:602 name:devdrv-functional_c max_time:1(us) count:1 + +Note: Only the captured information is displayed. Five lines for each cpu + by default. + +2.2 Disable Ihandler Tracing +--------------------------------- + +Echo 0 to /proc/irq/itrace_ihandler to disable ihandler tracer. For example:: + + # echo 0 > /proc/irq/itrace_ihandler + +2.3 Set the number of displayed +--------------------------------- + +Echo num to /proc/irq/itrace_ihandler_num to set the number of displayed. The +num value ranges from 1 to 30. For example:: + + # echo 20 > /proc/irq/itrace_ihandler_num + +3. Using Irqsoff Tracing +====================== + +3.1 Enable Irqsoff Tracing +--------------------------------- + +Echo threshold val to /proc/irq/itrace_irqsoff to enable irqsoff tracer. The +threshold value ranges from 1 to 10000000, in microsecond. For example:: + + # echo 20 > /proc/irq/itrace_irqsoff + +Cat /proc/irq/itrace_irqsoff to get the message of irqsoff. For example:: + + # echo 1 > /proc/irq/itrace_irqsoff + # cat /proc/irq/itrace_irqsoff + [irqsoff CPU0]: + max_time:3(us) caller:__do_softirq+0x94/0x328 + max_time:4(us) caller:__do_softirq+0x94/0x328 + max_time:4(us) caller:__do_softirq+0x94/0x328 + [irqsoff CPU1]: + max_time:2(us) caller:finish_task_switch+0x6c/0x1e0 + max_time:1(us) caller:__arm64_sys_setpgid+0xd0/0x1d8 + max_time:2(us) caller:pagevec_lru_move_fn+0x18c/0x1c8 + [irqsoff CPU2]: + max_time:4(us) caller:__do_softirq+0x94/0x328 + max_time:6(us) caller:__do_softirq+0x94/0x328 + max_time:2(us) caller:rcu_idle_exit+0xa4/0xd8 + [irqsoff CPU3]: + max_time:3(us) caller:rcu_idle_exit+0xa4/0xd8 + max_time:1(us) caller:__do_softirq+0x94/0x328 + max_time:2(us) caller:osal_spin_unlock_irqrestore+0x5c/0x80 [drv_osal] + +Note: Only the captured information is displayed. Three lines for each cpu + by default. + +3.2 Disable Irqsoff Tracing +--------------------------------- + +Echo 0 to /proc/irq/itrace_irqsoff to disable irqsoff tracer. For example:: + + # echo 0 > /proc/irq/itrace_irqsoff + +3.3 Set the number of displayed +--------------------------------- + +Echo num to /proc/irq/itrace_irqsoff_num to set the number of displayed. +The num value ranges from 1 to 30. For example:: + + # echo 20 > /proc/irq/itrace_irqsoff_num
From: Bixuan Cui cuibixuan@huawei.com
ascend inclusion category: feature bugzilla: NA CVE: NA
---------------------------------------------
set default value of CONFIG_ITRACE.
Signed-off-by: Bixuan Cui cuibixuan@huawei.com Reviewed-by: Ding Tianhong dingtianhong@huawei.com Reviewed-by: Hanjun Guo guohanjun@huawei.com Signed-off-by: Yang Yingliang yangyingliang@huawei.com --- arch/arm64/configs/hulk_defconfig | 1 + 1 file changed, 1 insertion(+)
diff --git a/arch/arm64/configs/hulk_defconfig b/arch/arm64/configs/hulk_defconfig index f207fbffb87d2..976074747f12d 100644 --- a/arch/arm64/configs/hulk_defconfig +++ b/arch/arm64/configs/hulk_defconfig @@ -4826,6 +4826,7 @@ CONFIG_TEE=m CONFIG_UACCE=y # CONFIG_WD_DUMMY_DEV is not set # CONFIG_SLIMBUS is not set +# CONFIG_ITRACE is not set
# # File systems