提交 2676d5fb 编写于 作者: B Bixuan Cui 提交者: Yang Yingliang

itrace: Add interrupts trace support

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: NBixuan Cui <cuibixuan@huawei.com>
Reviewed-by: NDing Tianhong <dingtianhong@huawei.com>
Reviewed-by: NHanjun Guo <guohanjun@huawei.com>
Signed-off-by: NYang Yingliang <yangyingliang@huawei.com>
上级 66dfb5b1
/* 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 */
......@@ -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",
......
......@@ -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
......
......@@ -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
......@@ -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
// 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;
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册