提交 861dc510 编写于 作者: B Bixuan Cui 提交者: Yang Yingliang

itrace: Add irqsoff trace support

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: 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>
上级 2676d5fb
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include <linux/typecheck.h> #include <linux/typecheck.h>
#include <asm/irqflags.h> #include <asm/irqflags.h>
#include <linux/itrace.h>
/* Currently trace_softirqs_on/off is used only by lockdep */ /* Currently trace_softirqs_on/off is used only by lockdep */
#ifdef CONFIG_PROVE_LOCKING #ifdef CONFIG_PROVE_LOCKING
...@@ -69,7 +70,11 @@ do { \ ...@@ -69,7 +70,11 @@ do { \
extern void stop_critical_timings(void); extern void stop_critical_timings(void);
extern void start_critical_timings(void); extern void start_critical_timings(void);
#else #else
#ifdef CONFIG_ITRACE_IRQSOFF
# define stop_critical_timings() itrace_hardirqs_ignore()
#else
# define stop_critical_timings() do { } while (0) # define stop_critical_timings() do { } while (0)
#endif
# define start_critical_timings() do { } while (0) # define start_critical_timings() do { } while (0)
#endif #endif
...@@ -135,7 +140,44 @@ do { \ ...@@ -135,7 +140,44 @@ do { \
} while (0) } 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_enable() do { raw_local_irq_enable(); } while (0)
#define local_irq_disable() do { raw_local_irq_disable(); } while (0) #define local_irq_disable() do { raw_local_irq_disable(); } while (0)
...@@ -146,7 +188,7 @@ do { \ ...@@ -146,7 +188,7 @@ do { \
#define local_irq_restore(flags) do { raw_local_irq_restore(flags); } while (0) #define local_irq_restore(flags) do { raw_local_irq_restore(flags); } while (0)
#define safe_halt() do { raw_safe_halt(); } 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) #define local_save_flags(flags) raw_local_save_flags(flags)
......
...@@ -15,6 +15,13 @@ ...@@ -15,6 +15,13 @@
#define IHANDLER_OFF 0 #define IHANDLER_OFF 0
#define IHANDLER_ON 1 #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 { struct irq_handler_info {
int irq; int irq;
char name[IRQ_NAME_LEN]; char name[IRQ_NAME_LEN];
...@@ -28,6 +35,17 @@ struct Ihandler { ...@@ -28,6 +35,17 @@ struct Ihandler {
struct irq_handler_info info[IHANDLER_INFO_NUM_MAX]; 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 #ifdef CONFIG_ITRACE_IHANDLER
extern void itrace_ihandler_entry(void); extern void itrace_ihandler_entry(void);
extern void itrace_ihandler_exit(int irq, const char *name); 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 ...@@ -44,4 +62,17 @@ static inline void __maybe_unused itrace_ihandler_exit(int irq, const char *name
}; };
#endif /* CONFIG_ITRACE_IHANDLER */ #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 */ #endif /* __LINUX_ITRACE_H */
...@@ -393,6 +393,98 @@ static const struct file_operations itrace_ihandler_proc_fops = { ...@@ -393,6 +393,98 @@ static const struct file_operations itrace_ihandler_proc_fops = {
}; };
#endif #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) static int irq_spurious_proc_show(struct seq_file *m, void *v)
{ {
struct irq_desc *desc = irq_to_desc((long) m->private); struct irq_desc *desc = irq_to_desc((long) m->private);
...@@ -558,6 +650,12 @@ void init_irq_proc(void) ...@@ -558,6 +650,12 @@ void init_irq_proc(void)
proc_create("irq/itrace_ihandler_num", 0644, NULL, proc_create("irq/itrace_ihandler_num", 0644, NULL,
&itrace_ihandler_num_proc_fops); &itrace_ihandler_num_proc_fops);
#endif #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 #ifdef CONFIG_GENERIC_IRQ_SHOW
......
...@@ -808,6 +808,17 @@ config ITRACE_IHANDLER ...@@ -808,6 +808,17 @@ config ITRACE_IHANDLER
Support dynamic disable and threshold setting. 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 #ITRACE
endif # TRACING_SUPPORT endif # TRACING_SUPPORT
......
...@@ -86,3 +86,4 @@ obj-$(CONFIG_TRACEPOINT_BENCHMARK) += trace_benchmark.o ...@@ -86,3 +86,4 @@ obj-$(CONFIG_TRACEPOINT_BENCHMARK) += trace_benchmark.o
libftrace-y := ftrace.o libftrace-y := ftrace.o
obj-$(CONFIG_ITRACE_IHANDLER) += itrace_ihandler.o obj-$(CONFIG_ITRACE_IHANDLER) += itrace_ihandler.o
obj-$(CONFIG_ITRACE_IRQSOFF) += itrace_irqsoff.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>
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;
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册