1179 lines
32 KiB
C
1179 lines
32 KiB
C
/*
|
|
* kernel/trace/latency_hist.c
|
|
*
|
|
* Add support for histograms of preemption-off latency and
|
|
* interrupt-off latency and wakeup latency, it depends on
|
|
* Real-Time Preemption Support.
|
|
*
|
|
* Copyright (C) 2005 MontaVista Software, Inc.
|
|
* Yi Yang <yyang@ch.mvista.com>
|
|
*
|
|
* Converted to work with the new latency tracer.
|
|
* Copyright (C) 2008 Red Hat, Inc.
|
|
* Steven Rostedt <srostedt@redhat.com>
|
|
*
|
|
*/
|
|
#include <linux/module.h>
|
|
#include <linux/debugfs.h>
|
|
#include <linux/seq_file.h>
|
|
#include <linux/percpu.h>
|
|
#include <linux/kallsyms.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/sched/rt.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/atomic.h>
|
|
#include <asm/div64.h>
|
|
|
|
#include "trace.h"
|
|
#include <trace/events/sched.h>
|
|
|
|
#define NSECS_PER_USECS 1000L
|
|
|
|
#define CREATE_TRACE_POINTS
|
|
#include <trace/events/hist.h>
|
|
|
|
enum {
|
|
IRQSOFF_LATENCY = 0,
|
|
PREEMPTOFF_LATENCY,
|
|
PREEMPTIRQSOFF_LATENCY,
|
|
WAKEUP_LATENCY,
|
|
WAKEUP_LATENCY_SHAREDPRIO,
|
|
MISSED_TIMER_OFFSETS,
|
|
TIMERANDWAKEUP_LATENCY,
|
|
MAX_LATENCY_TYPE,
|
|
};
|
|
|
|
#define MAX_ENTRY_NUM 10240
|
|
|
|
struct hist_data {
|
|
atomic_t hist_mode; /* 0 log, 1 don't log */
|
|
long offset; /* set it to MAX_ENTRY_NUM/2 for a bipolar scale */
|
|
long min_lat;
|
|
long max_lat;
|
|
unsigned long long below_hist_bound_samples;
|
|
unsigned long long above_hist_bound_samples;
|
|
long long accumulate_lat;
|
|
unsigned long long total_samples;
|
|
unsigned long long hist_array[MAX_ENTRY_NUM];
|
|
};
|
|
|
|
struct enable_data {
|
|
int latency_type;
|
|
int enabled;
|
|
};
|
|
|
|
static char *latency_hist_dir_root = "latency_hist";
|
|
|
|
#ifdef CONFIG_INTERRUPT_OFF_HIST
|
|
static DEFINE_PER_CPU(struct hist_data, irqsoff_hist);
|
|
static char *irqsoff_hist_dir = "irqsoff";
|
|
static DEFINE_PER_CPU(cycles_t, hist_irqsoff_start);
|
|
static DEFINE_PER_CPU(int, hist_irqsoff_counting);
|
|
#endif
|
|
|
|
#ifdef CONFIG_PREEMPT_OFF_HIST
|
|
static DEFINE_PER_CPU(struct hist_data, preemptoff_hist);
|
|
static char *preemptoff_hist_dir = "preemptoff";
|
|
static DEFINE_PER_CPU(cycles_t, hist_preemptoff_start);
|
|
static DEFINE_PER_CPU(int, hist_preemptoff_counting);
|
|
#endif
|
|
|
|
#if defined(CONFIG_PREEMPT_OFF_HIST) && defined(CONFIG_INTERRUPT_OFF_HIST)
|
|
static DEFINE_PER_CPU(struct hist_data, preemptirqsoff_hist);
|
|
static char *preemptirqsoff_hist_dir = "preemptirqsoff";
|
|
static DEFINE_PER_CPU(cycles_t, hist_preemptirqsoff_start);
|
|
static DEFINE_PER_CPU(int, hist_preemptirqsoff_counting);
|
|
#endif
|
|
|
|
#if defined(CONFIG_PREEMPT_OFF_HIST) || defined(CONFIG_INTERRUPT_OFF_HIST)
|
|
static notrace void probe_preemptirqsoff_hist(void *v, int reason, int start);
|
|
static struct enable_data preemptirqsoff_enabled_data = {
|
|
.latency_type = PREEMPTIRQSOFF_LATENCY,
|
|
.enabled = 0,
|
|
};
|
|
#endif
|
|
|
|
#if defined(CONFIG_WAKEUP_LATENCY_HIST) || \
|
|
defined(CONFIG_MISSED_TIMER_OFFSETS_HIST)
|
|
struct maxlatproc_data {
|
|
char comm[FIELD_SIZEOF(struct task_struct, comm)];
|
|
char current_comm[FIELD_SIZEOF(struct task_struct, comm)];
|
|
int pid;
|
|
int current_pid;
|
|
int prio;
|
|
int current_prio;
|
|
long latency;
|
|
long timeroffset;
|
|
cycle_t timestamp;
|
|
};
|
|
#endif
|
|
|
|
#ifdef CONFIG_WAKEUP_LATENCY_HIST
|
|
static DEFINE_PER_CPU(struct hist_data, wakeup_latency_hist);
|
|
static DEFINE_PER_CPU(struct hist_data, wakeup_latency_hist_sharedprio);
|
|
static char *wakeup_latency_hist_dir = "wakeup";
|
|
static char *wakeup_latency_hist_dir_sharedprio = "sharedprio";
|
|
static notrace void probe_wakeup_latency_hist_start(void *v,
|
|
struct task_struct *p, int success);
|
|
static notrace void probe_wakeup_latency_hist_stop(void *v,
|
|
struct task_struct *prev, struct task_struct *next);
|
|
static notrace void probe_sched_migrate_task(void *,
|
|
struct task_struct *task, int cpu);
|
|
static struct enable_data wakeup_latency_enabled_data = {
|
|
.latency_type = WAKEUP_LATENCY,
|
|
.enabled = 0,
|
|
};
|
|
static DEFINE_PER_CPU(struct maxlatproc_data, wakeup_maxlatproc);
|
|
static DEFINE_PER_CPU(struct maxlatproc_data, wakeup_maxlatproc_sharedprio);
|
|
static DEFINE_PER_CPU(struct task_struct *, wakeup_task);
|
|
static DEFINE_PER_CPU(int, wakeup_sharedprio);
|
|
static unsigned long wakeup_pid;
|
|
#endif
|
|
|
|
#ifdef CONFIG_MISSED_TIMER_OFFSETS_HIST
|
|
static DEFINE_PER_CPU(struct hist_data, missed_timer_offsets);
|
|
static char *missed_timer_offsets_dir = "missed_timer_offsets";
|
|
static notrace void probe_hrtimer_interrupt(void *v, int cpu,
|
|
long long offset, struct task_struct *curr, struct task_struct *task);
|
|
static struct enable_data missed_timer_offsets_enabled_data = {
|
|
.latency_type = MISSED_TIMER_OFFSETS,
|
|
.enabled = 0,
|
|
};
|
|
static DEFINE_PER_CPU(struct maxlatproc_data, missed_timer_offsets_maxlatproc);
|
|
static unsigned long missed_timer_offsets_pid;
|
|
#endif
|
|
|
|
#if defined(CONFIG_WAKEUP_LATENCY_HIST) && \
|
|
defined(CONFIG_MISSED_TIMER_OFFSETS_HIST)
|
|
static DEFINE_PER_CPU(struct hist_data, timerandwakeup_latency_hist);
|
|
static char *timerandwakeup_latency_hist_dir = "timerandwakeup";
|
|
static struct enable_data timerandwakeup_enabled_data = {
|
|
.latency_type = TIMERANDWAKEUP_LATENCY,
|
|
.enabled = 0,
|
|
};
|
|
static DEFINE_PER_CPU(struct maxlatproc_data, timerandwakeup_maxlatproc);
|
|
#endif
|
|
|
|
void notrace latency_hist(int latency_type, int cpu, long latency,
|
|
long timeroffset, cycle_t stop,
|
|
struct task_struct *p)
|
|
{
|
|
struct hist_data *my_hist;
|
|
#if defined(CONFIG_WAKEUP_LATENCY_HIST) || \
|
|
defined(CONFIG_MISSED_TIMER_OFFSETS_HIST)
|
|
struct maxlatproc_data *mp = NULL;
|
|
#endif
|
|
|
|
if (!cpu_possible(cpu) || latency_type < 0 ||
|
|
latency_type >= MAX_LATENCY_TYPE)
|
|
return;
|
|
|
|
switch (latency_type) {
|
|
#ifdef CONFIG_INTERRUPT_OFF_HIST
|
|
case IRQSOFF_LATENCY:
|
|
my_hist = &per_cpu(irqsoff_hist, cpu);
|
|
break;
|
|
#endif
|
|
#ifdef CONFIG_PREEMPT_OFF_HIST
|
|
case PREEMPTOFF_LATENCY:
|
|
my_hist = &per_cpu(preemptoff_hist, cpu);
|
|
break;
|
|
#endif
|
|
#if defined(CONFIG_PREEMPT_OFF_HIST) && defined(CONFIG_INTERRUPT_OFF_HIST)
|
|
case PREEMPTIRQSOFF_LATENCY:
|
|
my_hist = &per_cpu(preemptirqsoff_hist, cpu);
|
|
break;
|
|
#endif
|
|
#ifdef CONFIG_WAKEUP_LATENCY_HIST
|
|
case WAKEUP_LATENCY:
|
|
my_hist = &per_cpu(wakeup_latency_hist, cpu);
|
|
mp = &per_cpu(wakeup_maxlatproc, cpu);
|
|
break;
|
|
case WAKEUP_LATENCY_SHAREDPRIO:
|
|
my_hist = &per_cpu(wakeup_latency_hist_sharedprio, cpu);
|
|
mp = &per_cpu(wakeup_maxlatproc_sharedprio, cpu);
|
|
break;
|
|
#endif
|
|
#ifdef CONFIG_MISSED_TIMER_OFFSETS_HIST
|
|
case MISSED_TIMER_OFFSETS:
|
|
my_hist = &per_cpu(missed_timer_offsets, cpu);
|
|
mp = &per_cpu(missed_timer_offsets_maxlatproc, cpu);
|
|
break;
|
|
#endif
|
|
#if defined(CONFIG_WAKEUP_LATENCY_HIST) && \
|
|
defined(CONFIG_MISSED_TIMER_OFFSETS_HIST)
|
|
case TIMERANDWAKEUP_LATENCY:
|
|
my_hist = &per_cpu(timerandwakeup_latency_hist, cpu);
|
|
mp = &per_cpu(timerandwakeup_maxlatproc, cpu);
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
return;
|
|
}
|
|
|
|
latency += my_hist->offset;
|
|
|
|
if (atomic_read(&my_hist->hist_mode) == 0)
|
|
return;
|
|
|
|
if (latency < 0 || latency >= MAX_ENTRY_NUM) {
|
|
if (latency < 0)
|
|
my_hist->below_hist_bound_samples++;
|
|
else
|
|
my_hist->above_hist_bound_samples++;
|
|
} else
|
|
my_hist->hist_array[latency]++;
|
|
|
|
if (unlikely(latency > my_hist->max_lat ||
|
|
my_hist->min_lat == LONG_MAX)) {
|
|
#if defined(CONFIG_WAKEUP_LATENCY_HIST) || \
|
|
defined(CONFIG_MISSED_TIMER_OFFSETS_HIST)
|
|
if (latency_type == WAKEUP_LATENCY ||
|
|
latency_type == WAKEUP_LATENCY_SHAREDPRIO ||
|
|
latency_type == MISSED_TIMER_OFFSETS ||
|
|
latency_type == TIMERANDWAKEUP_LATENCY) {
|
|
strncpy(mp->comm, p->comm, sizeof(mp->comm));
|
|
strncpy(mp->current_comm, current->comm,
|
|
sizeof(mp->current_comm));
|
|
mp->pid = task_pid_nr(p);
|
|
mp->current_pid = task_pid_nr(current);
|
|
mp->prio = p->prio;
|
|
mp->current_prio = current->prio;
|
|
mp->latency = latency;
|
|
mp->timeroffset = timeroffset;
|
|
mp->timestamp = stop;
|
|
}
|
|
#endif
|
|
my_hist->max_lat = latency;
|
|
}
|
|
if (unlikely(latency < my_hist->min_lat))
|
|
my_hist->min_lat = latency;
|
|
my_hist->total_samples++;
|
|
my_hist->accumulate_lat += latency;
|
|
}
|
|
|
|
static void *l_start(struct seq_file *m, loff_t *pos)
|
|
{
|
|
loff_t *index_ptr = NULL;
|
|
loff_t index = *pos;
|
|
struct hist_data *my_hist = m->private;
|
|
|
|
if (index == 0) {
|
|
char minstr[32], avgstr[32], maxstr[32];
|
|
|
|
atomic_dec(&my_hist->hist_mode);
|
|
|
|
if (likely(my_hist->total_samples)) {
|
|
long avg = (long) div64_s64(my_hist->accumulate_lat,
|
|
my_hist->total_samples);
|
|
snprintf(minstr, sizeof(minstr), "%ld",
|
|
my_hist->min_lat - my_hist->offset);
|
|
snprintf(avgstr, sizeof(avgstr), "%ld",
|
|
avg - my_hist->offset);
|
|
snprintf(maxstr, sizeof(maxstr), "%ld",
|
|
my_hist->max_lat - my_hist->offset);
|
|
} else {
|
|
strcpy(minstr, "<undef>");
|
|
strcpy(avgstr, minstr);
|
|
strcpy(maxstr, minstr);
|
|
}
|
|
|
|
seq_printf(m, "#Minimum latency: %s microseconds\n"
|
|
"#Average latency: %s microseconds\n"
|
|
"#Maximum latency: %s microseconds\n"
|
|
"#Total samples: %llu\n"
|
|
"#There are %llu samples lower than %ld"
|
|
" microseconds.\n"
|
|
"#There are %llu samples greater or equal"
|
|
" than %ld microseconds.\n"
|
|
"#usecs\t%16s\n",
|
|
minstr, avgstr, maxstr,
|
|
my_hist->total_samples,
|
|
my_hist->below_hist_bound_samples,
|
|
-my_hist->offset,
|
|
my_hist->above_hist_bound_samples,
|
|
MAX_ENTRY_NUM - my_hist->offset,
|
|
"samples");
|
|
}
|
|
if (index < MAX_ENTRY_NUM) {
|
|
index_ptr = kmalloc(sizeof(loff_t), GFP_KERNEL);
|
|
if (index_ptr)
|
|
*index_ptr = index;
|
|
}
|
|
|
|
return index_ptr;
|
|
}
|
|
|
|
static void *l_next(struct seq_file *m, void *p, loff_t *pos)
|
|
{
|
|
loff_t *index_ptr = p;
|
|
struct hist_data *my_hist = m->private;
|
|
|
|
if (++*pos >= MAX_ENTRY_NUM) {
|
|
atomic_inc(&my_hist->hist_mode);
|
|
return NULL;
|
|
}
|
|
*index_ptr = *pos;
|
|
return index_ptr;
|
|
}
|
|
|
|
static void l_stop(struct seq_file *m, void *p)
|
|
{
|
|
kfree(p);
|
|
}
|
|
|
|
static int l_show(struct seq_file *m, void *p)
|
|
{
|
|
int index = *(loff_t *) p;
|
|
struct hist_data *my_hist = m->private;
|
|
|
|
seq_printf(m, "%6ld\t%16llu\n", index - my_hist->offset,
|
|
my_hist->hist_array[index]);
|
|
return 0;
|
|
}
|
|
|
|
static const struct seq_operations latency_hist_seq_op = {
|
|
.start = l_start,
|
|
.next = l_next,
|
|
.stop = l_stop,
|
|
.show = l_show
|
|
};
|
|
|
|
static int latency_hist_open(struct inode *inode, struct file *file)
|
|
{
|
|
int ret;
|
|
|
|
ret = seq_open(file, &latency_hist_seq_op);
|
|
if (!ret) {
|
|
struct seq_file *seq = file->private_data;
|
|
seq->private = inode->i_private;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static const struct file_operations latency_hist_fops = {
|
|
.open = latency_hist_open,
|
|
.read = seq_read,
|
|
.llseek = seq_lseek,
|
|
.release = seq_release,
|
|
};
|
|
|
|
#if defined(CONFIG_WAKEUP_LATENCY_HIST) || \
|
|
defined(CONFIG_MISSED_TIMER_OFFSETS_HIST)
|
|
static void clear_maxlatprocdata(struct maxlatproc_data *mp)
|
|
{
|
|
mp->comm[0] = mp->current_comm[0] = '\0';
|
|
mp->prio = mp->current_prio = mp->pid = mp->current_pid =
|
|
mp->latency = mp->timeroffset = -1;
|
|
mp->timestamp = 0;
|
|
}
|
|
#endif
|
|
|
|
static void hist_reset(struct hist_data *hist)
|
|
{
|
|
atomic_dec(&hist->hist_mode);
|
|
|
|
memset(hist->hist_array, 0, sizeof(hist->hist_array));
|
|
hist->below_hist_bound_samples = 0ULL;
|
|
hist->above_hist_bound_samples = 0ULL;
|
|
hist->min_lat = LONG_MAX;
|
|
hist->max_lat = LONG_MIN;
|
|
hist->total_samples = 0ULL;
|
|
hist->accumulate_lat = 0LL;
|
|
|
|
atomic_inc(&hist->hist_mode);
|
|
}
|
|
|
|
static ssize_t
|
|
latency_hist_reset(struct file *file, const char __user *a,
|
|
size_t size, loff_t *off)
|
|
{
|
|
int cpu;
|
|
struct hist_data *hist = NULL;
|
|
#if defined(CONFIG_WAKEUP_LATENCY_HIST) || \
|
|
defined(CONFIG_MISSED_TIMER_OFFSETS_HIST)
|
|
struct maxlatproc_data *mp = NULL;
|
|
#endif
|
|
off_t latency_type = (off_t) file->private_data;
|
|
|
|
for_each_online_cpu(cpu) {
|
|
|
|
switch (latency_type) {
|
|
#ifdef CONFIG_PREEMPT_OFF_HIST
|
|
case PREEMPTOFF_LATENCY:
|
|
hist = &per_cpu(preemptoff_hist, cpu);
|
|
break;
|
|
#endif
|
|
#ifdef CONFIG_INTERRUPT_OFF_HIST
|
|
case IRQSOFF_LATENCY:
|
|
hist = &per_cpu(irqsoff_hist, cpu);
|
|
break;
|
|
#endif
|
|
#if defined(CONFIG_INTERRUPT_OFF_HIST) && defined(CONFIG_PREEMPT_OFF_HIST)
|
|
case PREEMPTIRQSOFF_LATENCY:
|
|
hist = &per_cpu(preemptirqsoff_hist, cpu);
|
|
break;
|
|
#endif
|
|
#ifdef CONFIG_WAKEUP_LATENCY_HIST
|
|
case WAKEUP_LATENCY:
|
|
hist = &per_cpu(wakeup_latency_hist, cpu);
|
|
mp = &per_cpu(wakeup_maxlatproc, cpu);
|
|
break;
|
|
case WAKEUP_LATENCY_SHAREDPRIO:
|
|
hist = &per_cpu(wakeup_latency_hist_sharedprio, cpu);
|
|
mp = &per_cpu(wakeup_maxlatproc_sharedprio, cpu);
|
|
break;
|
|
#endif
|
|
#ifdef CONFIG_MISSED_TIMER_OFFSETS_HIST
|
|
case MISSED_TIMER_OFFSETS:
|
|
hist = &per_cpu(missed_timer_offsets, cpu);
|
|
mp = &per_cpu(missed_timer_offsets_maxlatproc, cpu);
|
|
break;
|
|
#endif
|
|
#if defined(CONFIG_WAKEUP_LATENCY_HIST) && \
|
|
defined(CONFIG_MISSED_TIMER_OFFSETS_HIST)
|
|
case TIMERANDWAKEUP_LATENCY:
|
|
hist = &per_cpu(timerandwakeup_latency_hist, cpu);
|
|
mp = &per_cpu(timerandwakeup_maxlatproc, cpu);
|
|
break;
|
|
#endif
|
|
}
|
|
|
|
hist_reset(hist);
|
|
#if defined(CONFIG_WAKEUP_LATENCY_HIST) || \
|
|
defined(CONFIG_MISSED_TIMER_OFFSETS_HIST)
|
|
if (latency_type == WAKEUP_LATENCY ||
|
|
latency_type == WAKEUP_LATENCY_SHAREDPRIO ||
|
|
latency_type == MISSED_TIMER_OFFSETS ||
|
|
latency_type == TIMERANDWAKEUP_LATENCY)
|
|
clear_maxlatprocdata(mp);
|
|
#endif
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
#if defined(CONFIG_WAKEUP_LATENCY_HIST) || \
|
|
defined(CONFIG_MISSED_TIMER_OFFSETS_HIST)
|
|
static ssize_t
|
|
show_pid(struct file *file, char __user *ubuf, size_t cnt, loff_t *ppos)
|
|
{
|
|
char buf[64];
|
|
int r;
|
|
unsigned long *this_pid = file->private_data;
|
|
|
|
r = snprintf(buf, sizeof(buf), "%lu\n", *this_pid);
|
|
return simple_read_from_buffer(ubuf, cnt, ppos, buf, r);
|
|
}
|
|
|
|
static ssize_t do_pid(struct file *file, const char __user *ubuf,
|
|
size_t cnt, loff_t *ppos)
|
|
{
|
|
char buf[64];
|
|
unsigned long pid;
|
|
unsigned long *this_pid = file->private_data;
|
|
|
|
if (cnt >= sizeof(buf))
|
|
return -EINVAL;
|
|
|
|
if (copy_from_user(&buf, ubuf, cnt))
|
|
return -EFAULT;
|
|
|
|
buf[cnt] = '\0';
|
|
|
|
if (kstrtoul(buf, 10, &pid))
|
|
return -EINVAL;
|
|
|
|
*this_pid = pid;
|
|
|
|
return cnt;
|
|
}
|
|
#endif
|
|
|
|
#if defined(CONFIG_WAKEUP_LATENCY_HIST) || \
|
|
defined(CONFIG_MISSED_TIMER_OFFSETS_HIST)
|
|
static ssize_t
|
|
show_maxlatproc(struct file *file, char __user *ubuf, size_t cnt, loff_t *ppos)
|
|
{
|
|
int r;
|
|
struct maxlatproc_data *mp = file->private_data;
|
|
int strmaxlen = (TASK_COMM_LEN * 2) + (8 * 8);
|
|
unsigned long long t;
|
|
unsigned long usecs, secs;
|
|
char *buf;
|
|
|
|
if (mp->pid == -1 || mp->current_pid == -1) {
|
|
buf = "(none)\n";
|
|
return simple_read_from_buffer(ubuf, cnt, ppos, buf,
|
|
strlen(buf));
|
|
}
|
|
|
|
buf = kmalloc(strmaxlen, GFP_KERNEL);
|
|
if (buf == NULL)
|
|
return -ENOMEM;
|
|
|
|
t = ns2usecs(mp->timestamp);
|
|
usecs = do_div(t, USEC_PER_SEC);
|
|
secs = (unsigned long) t;
|
|
r = snprintf(buf, strmaxlen,
|
|
"%d %d %ld (%ld) %s <- %d %d %s %lu.%06lu\n", mp->pid,
|
|
MAX_RT_PRIO-1 - mp->prio, mp->latency, mp->timeroffset, mp->comm,
|
|
mp->current_pid, MAX_RT_PRIO-1 - mp->current_prio, mp->current_comm,
|
|
secs, usecs);
|
|
r = simple_read_from_buffer(ubuf, cnt, ppos, buf, r);
|
|
kfree(buf);
|
|
return r;
|
|
}
|
|
#endif
|
|
|
|
static ssize_t
|
|
show_enable(struct file *file, char __user *ubuf, size_t cnt, loff_t *ppos)
|
|
{
|
|
char buf[64];
|
|
struct enable_data *ed = file->private_data;
|
|
int r;
|
|
|
|
r = snprintf(buf, sizeof(buf), "%d\n", ed->enabled);
|
|
return simple_read_from_buffer(ubuf, cnt, ppos, buf, r);
|
|
}
|
|
|
|
static ssize_t
|
|
do_enable(struct file *file, const char __user *ubuf, size_t cnt, loff_t *ppos)
|
|
{
|
|
char buf[64];
|
|
long enable;
|
|
struct enable_data *ed = file->private_data;
|
|
|
|
if (cnt >= sizeof(buf))
|
|
return -EINVAL;
|
|
|
|
if (copy_from_user(&buf, ubuf, cnt))
|
|
return -EFAULT;
|
|
|
|
buf[cnt] = 0;
|
|
|
|
if (kstrtoul(buf, 10, &enable))
|
|
return -EINVAL;
|
|
|
|
if ((enable && ed->enabled) || (!enable && !ed->enabled))
|
|
return cnt;
|
|
|
|
if (enable) {
|
|
int ret;
|
|
|
|
switch (ed->latency_type) {
|
|
#if defined(CONFIG_INTERRUPT_OFF_HIST) || defined(CONFIG_PREEMPT_OFF_HIST)
|
|
case PREEMPTIRQSOFF_LATENCY:
|
|
ret = register_trace_preemptirqsoff_hist(
|
|
probe_preemptirqsoff_hist, NULL);
|
|
if (ret) {
|
|
pr_info("wakeup trace: Couldn't assign "
|
|
"probe_preemptirqsoff_hist "
|
|
"to trace_preemptirqsoff_hist\n");
|
|
return ret;
|
|
}
|
|
break;
|
|
#endif
|
|
#ifdef CONFIG_WAKEUP_LATENCY_HIST
|
|
case WAKEUP_LATENCY:
|
|
ret = register_trace_sched_wakeup(
|
|
probe_wakeup_latency_hist_start, NULL);
|
|
if (ret) {
|
|
pr_info("wakeup trace: Couldn't assign "
|
|
"probe_wakeup_latency_hist_start "
|
|
"to trace_sched_wakeup\n");
|
|
return ret;
|
|
}
|
|
ret = register_trace_sched_wakeup_new(
|
|
probe_wakeup_latency_hist_start, NULL);
|
|
if (ret) {
|
|
pr_info("wakeup trace: Couldn't assign "
|
|
"probe_wakeup_latency_hist_start "
|
|
"to trace_sched_wakeup_new\n");
|
|
unregister_trace_sched_wakeup(
|
|
probe_wakeup_latency_hist_start, NULL);
|
|
return ret;
|
|
}
|
|
ret = register_trace_sched_switch(
|
|
probe_wakeup_latency_hist_stop, NULL);
|
|
if (ret) {
|
|
pr_info("wakeup trace: Couldn't assign "
|
|
"probe_wakeup_latency_hist_stop "
|
|
"to trace_sched_switch\n");
|
|
unregister_trace_sched_wakeup(
|
|
probe_wakeup_latency_hist_start, NULL);
|
|
unregister_trace_sched_wakeup_new(
|
|
probe_wakeup_latency_hist_start, NULL);
|
|
return ret;
|
|
}
|
|
ret = register_trace_sched_migrate_task(
|
|
probe_sched_migrate_task, NULL);
|
|
if (ret) {
|
|
pr_info("wakeup trace: Couldn't assign "
|
|
"probe_sched_migrate_task "
|
|
"to trace_sched_migrate_task\n");
|
|
unregister_trace_sched_wakeup(
|
|
probe_wakeup_latency_hist_start, NULL);
|
|
unregister_trace_sched_wakeup_new(
|
|
probe_wakeup_latency_hist_start, NULL);
|
|
unregister_trace_sched_switch(
|
|
probe_wakeup_latency_hist_stop, NULL);
|
|
return ret;
|
|
}
|
|
break;
|
|
#endif
|
|
#ifdef CONFIG_MISSED_TIMER_OFFSETS_HIST
|
|
case MISSED_TIMER_OFFSETS:
|
|
ret = register_trace_hrtimer_interrupt(
|
|
probe_hrtimer_interrupt, NULL);
|
|
if (ret) {
|
|
pr_info("wakeup trace: Couldn't assign "
|
|
"probe_hrtimer_interrupt "
|
|
"to trace_hrtimer_interrupt\n");
|
|
return ret;
|
|
}
|
|
break;
|
|
#endif
|
|
#if defined(CONFIG_WAKEUP_LATENCY_HIST) && \
|
|
defined(CONFIG_MISSED_TIMER_OFFSETS_HIST)
|
|
case TIMERANDWAKEUP_LATENCY:
|
|
if (!wakeup_latency_enabled_data.enabled ||
|
|
!missed_timer_offsets_enabled_data.enabled)
|
|
return -EINVAL;
|
|
break;
|
|
#endif
|
|
default:
|
|
break;
|
|
}
|
|
} else {
|
|
switch (ed->latency_type) {
|
|
#if defined(CONFIG_INTERRUPT_OFF_HIST) || defined(CONFIG_PREEMPT_OFF_HIST)
|
|
case PREEMPTIRQSOFF_LATENCY:
|
|
{
|
|
int cpu;
|
|
|
|
unregister_trace_preemptirqsoff_hist(
|
|
probe_preemptirqsoff_hist, NULL);
|
|
for_each_online_cpu(cpu) {
|
|
#ifdef CONFIG_INTERRUPT_OFF_HIST
|
|
per_cpu(hist_irqsoff_counting,
|
|
cpu) = 0;
|
|
#endif
|
|
#ifdef CONFIG_PREEMPT_OFF_HIST
|
|
per_cpu(hist_preemptoff_counting,
|
|
cpu) = 0;
|
|
#endif
|
|
#if defined(CONFIG_INTERRUPT_OFF_HIST) && defined(CONFIG_PREEMPT_OFF_HIST)
|
|
per_cpu(hist_preemptirqsoff_counting,
|
|
cpu) = 0;
|
|
#endif
|
|
}
|
|
}
|
|
break;
|
|
#endif
|
|
#ifdef CONFIG_WAKEUP_LATENCY_HIST
|
|
case WAKEUP_LATENCY:
|
|
{
|
|
int cpu;
|
|
|
|
unregister_trace_sched_wakeup(
|
|
probe_wakeup_latency_hist_start, NULL);
|
|
unregister_trace_sched_wakeup_new(
|
|
probe_wakeup_latency_hist_start, NULL);
|
|
unregister_trace_sched_switch(
|
|
probe_wakeup_latency_hist_stop, NULL);
|
|
unregister_trace_sched_migrate_task(
|
|
probe_sched_migrate_task, NULL);
|
|
|
|
for_each_online_cpu(cpu) {
|
|
per_cpu(wakeup_task, cpu) = NULL;
|
|
per_cpu(wakeup_sharedprio, cpu) = 0;
|
|
}
|
|
}
|
|
#ifdef CONFIG_MISSED_TIMER_OFFSETS_HIST
|
|
timerandwakeup_enabled_data.enabled = 0;
|
|
#endif
|
|
break;
|
|
#endif
|
|
#ifdef CONFIG_MISSED_TIMER_OFFSETS_HIST
|
|
case MISSED_TIMER_OFFSETS:
|
|
unregister_trace_hrtimer_interrupt(
|
|
probe_hrtimer_interrupt, NULL);
|
|
#ifdef CONFIG_WAKEUP_LATENCY_HIST
|
|
timerandwakeup_enabled_data.enabled = 0;
|
|
#endif
|
|
break;
|
|
#endif
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
ed->enabled = enable;
|
|
return cnt;
|
|
}
|
|
|
|
static const struct file_operations latency_hist_reset_fops = {
|
|
.open = tracing_open_generic,
|
|
.write = latency_hist_reset,
|
|
};
|
|
|
|
static const struct file_operations enable_fops = {
|
|
.open = tracing_open_generic,
|
|
.read = show_enable,
|
|
.write = do_enable,
|
|
};
|
|
|
|
#if defined(CONFIG_WAKEUP_LATENCY_HIST) || \
|
|
defined(CONFIG_MISSED_TIMER_OFFSETS_HIST)
|
|
static const struct file_operations pid_fops = {
|
|
.open = tracing_open_generic,
|
|
.read = show_pid,
|
|
.write = do_pid,
|
|
};
|
|
|
|
static const struct file_operations maxlatproc_fops = {
|
|
.open = tracing_open_generic,
|
|
.read = show_maxlatproc,
|
|
};
|
|
#endif
|
|
|
|
#if defined(CONFIG_INTERRUPT_OFF_HIST) || defined(CONFIG_PREEMPT_OFF_HIST)
|
|
static notrace void probe_preemptirqsoff_hist(void *v, int reason,
|
|
int starthist)
|
|
{
|
|
int cpu = raw_smp_processor_id();
|
|
int time_set = 0;
|
|
|
|
if (starthist) {
|
|
cycle_t uninitialized_var(start);
|
|
|
|
if (!preempt_count() && !irqs_disabled())
|
|
return;
|
|
|
|
#ifdef CONFIG_INTERRUPT_OFF_HIST
|
|
if ((reason == IRQS_OFF || reason == TRACE_START) &&
|
|
!per_cpu(hist_irqsoff_counting, cpu)) {
|
|
per_cpu(hist_irqsoff_counting, cpu) = 1;
|
|
start = ftrace_now(cpu);
|
|
time_set++;
|
|
per_cpu(hist_irqsoff_start, cpu) = start;
|
|
}
|
|
#endif
|
|
|
|
#ifdef CONFIG_PREEMPT_OFF_HIST
|
|
if ((reason == PREEMPT_OFF || reason == TRACE_START) &&
|
|
!per_cpu(hist_preemptoff_counting, cpu)) {
|
|
per_cpu(hist_preemptoff_counting, cpu) = 1;
|
|
if (!(time_set++))
|
|
start = ftrace_now(cpu);
|
|
per_cpu(hist_preemptoff_start, cpu) = start;
|
|
}
|
|
#endif
|
|
|
|
#if defined(CONFIG_INTERRUPT_OFF_HIST) && defined(CONFIG_PREEMPT_OFF_HIST)
|
|
if (per_cpu(hist_irqsoff_counting, cpu) &&
|
|
per_cpu(hist_preemptoff_counting, cpu) &&
|
|
!per_cpu(hist_preemptirqsoff_counting, cpu)) {
|
|
per_cpu(hist_preemptirqsoff_counting, cpu) = 1;
|
|
if (!time_set)
|
|
start = ftrace_now(cpu);
|
|
per_cpu(hist_preemptirqsoff_start, cpu) = start;
|
|
}
|
|
#endif
|
|
} else {
|
|
cycle_t uninitialized_var(stop);
|
|
|
|
#ifdef CONFIG_INTERRUPT_OFF_HIST
|
|
if ((reason == IRQS_ON || reason == TRACE_STOP) &&
|
|
per_cpu(hist_irqsoff_counting, cpu)) {
|
|
cycle_t start = per_cpu(hist_irqsoff_start, cpu);
|
|
|
|
stop = ftrace_now(cpu);
|
|
time_set++;
|
|
if (start) {
|
|
long latency = ((long) (stop - start)) /
|
|
NSECS_PER_USECS;
|
|
|
|
latency_hist(IRQSOFF_LATENCY, cpu, latency, 0,
|
|
stop, NULL);
|
|
}
|
|
per_cpu(hist_irqsoff_counting, cpu) = 0;
|
|
}
|
|
#endif
|
|
|
|
#ifdef CONFIG_PREEMPT_OFF_HIST
|
|
if ((reason == PREEMPT_ON || reason == TRACE_STOP) &&
|
|
per_cpu(hist_preemptoff_counting, cpu)) {
|
|
cycle_t start = per_cpu(hist_preemptoff_start, cpu);
|
|
|
|
if (!(time_set++))
|
|
stop = ftrace_now(cpu);
|
|
if (start) {
|
|
long latency = ((long) (stop - start)) /
|
|
NSECS_PER_USECS;
|
|
|
|
latency_hist(PREEMPTOFF_LATENCY, cpu, latency,
|
|
0, stop, NULL);
|
|
}
|
|
per_cpu(hist_preemptoff_counting, cpu) = 0;
|
|
}
|
|
#endif
|
|
|
|
#if defined(CONFIG_INTERRUPT_OFF_HIST) && defined(CONFIG_PREEMPT_OFF_HIST)
|
|
if ((!per_cpu(hist_irqsoff_counting, cpu) ||
|
|
!per_cpu(hist_preemptoff_counting, cpu)) &&
|
|
per_cpu(hist_preemptirqsoff_counting, cpu)) {
|
|
cycle_t start = per_cpu(hist_preemptirqsoff_start, cpu);
|
|
|
|
if (!time_set)
|
|
stop = ftrace_now(cpu);
|
|
if (start) {
|
|
long latency = ((long) (stop - start)) /
|
|
NSECS_PER_USECS;
|
|
|
|
latency_hist(PREEMPTIRQSOFF_LATENCY, cpu,
|
|
latency, 0, stop, NULL);
|
|
}
|
|
per_cpu(hist_preemptirqsoff_counting, cpu) = 0;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef CONFIG_WAKEUP_LATENCY_HIST
|
|
static DEFINE_RAW_SPINLOCK(wakeup_lock);
|
|
static notrace void probe_sched_migrate_task(void *v, struct task_struct *task,
|
|
int cpu)
|
|
{
|
|
int old_cpu = task_cpu(task);
|
|
|
|
if (cpu != old_cpu) {
|
|
unsigned long flags;
|
|
struct task_struct *cpu_wakeup_task;
|
|
|
|
raw_spin_lock_irqsave(&wakeup_lock, flags);
|
|
|
|
cpu_wakeup_task = per_cpu(wakeup_task, old_cpu);
|
|
if (task == cpu_wakeup_task) {
|
|
put_task_struct(cpu_wakeup_task);
|
|
per_cpu(wakeup_task, old_cpu) = NULL;
|
|
cpu_wakeup_task = per_cpu(wakeup_task, cpu) = task;
|
|
get_task_struct(cpu_wakeup_task);
|
|
}
|
|
|
|
raw_spin_unlock_irqrestore(&wakeup_lock, flags);
|
|
}
|
|
}
|
|
|
|
static notrace void probe_wakeup_latency_hist_start(void *v,
|
|
struct task_struct *p, int success)
|
|
{
|
|
unsigned long flags;
|
|
struct task_struct *curr = current;
|
|
int cpu = task_cpu(p);
|
|
struct task_struct *cpu_wakeup_task;
|
|
|
|
raw_spin_lock_irqsave(&wakeup_lock, flags);
|
|
|
|
cpu_wakeup_task = per_cpu(wakeup_task, cpu);
|
|
|
|
if (wakeup_pid) {
|
|
if ((cpu_wakeup_task && p->prio == cpu_wakeup_task->prio) ||
|
|
p->prio == curr->prio)
|
|
per_cpu(wakeup_sharedprio, cpu) = 1;
|
|
if (likely(wakeup_pid != task_pid_nr(p)))
|
|
goto out;
|
|
} else {
|
|
if (likely(!rt_task(p)) ||
|
|
(cpu_wakeup_task && p->prio > cpu_wakeup_task->prio) ||
|
|
p->prio > curr->prio)
|
|
goto out;
|
|
if ((cpu_wakeup_task && p->prio == cpu_wakeup_task->prio) ||
|
|
p->prio == curr->prio)
|
|
per_cpu(wakeup_sharedprio, cpu) = 1;
|
|
}
|
|
|
|
if (cpu_wakeup_task)
|
|
put_task_struct(cpu_wakeup_task);
|
|
cpu_wakeup_task = per_cpu(wakeup_task, cpu) = p;
|
|
get_task_struct(cpu_wakeup_task);
|
|
cpu_wakeup_task->preempt_timestamp_hist =
|
|
ftrace_now(raw_smp_processor_id());
|
|
out:
|
|
raw_spin_unlock_irqrestore(&wakeup_lock, flags);
|
|
}
|
|
|
|
static notrace void probe_wakeup_latency_hist_stop(void *v,
|
|
struct task_struct *prev, struct task_struct *next)
|
|
{
|
|
unsigned long flags;
|
|
int cpu = task_cpu(next);
|
|
long latency;
|
|
cycle_t stop;
|
|
struct task_struct *cpu_wakeup_task;
|
|
|
|
raw_spin_lock_irqsave(&wakeup_lock, flags);
|
|
|
|
cpu_wakeup_task = per_cpu(wakeup_task, cpu);
|
|
|
|
if (cpu_wakeup_task == NULL)
|
|
goto out;
|
|
|
|
/* Already running? */
|
|
if (unlikely(current == cpu_wakeup_task))
|
|
goto out_reset;
|
|
|
|
if (next != cpu_wakeup_task) {
|
|
if (next->prio < cpu_wakeup_task->prio)
|
|
goto out_reset;
|
|
|
|
if (next->prio == cpu_wakeup_task->prio)
|
|
per_cpu(wakeup_sharedprio, cpu) = 1;
|
|
|
|
goto out;
|
|
}
|
|
|
|
if (current->prio == cpu_wakeup_task->prio)
|
|
per_cpu(wakeup_sharedprio, cpu) = 1;
|
|
|
|
/*
|
|
* The task we are waiting for is about to be switched to.
|
|
* Calculate latency and store it in histogram.
|
|
*/
|
|
stop = ftrace_now(raw_smp_processor_id());
|
|
|
|
latency = ((long) (stop - next->preempt_timestamp_hist)) /
|
|
NSECS_PER_USECS;
|
|
|
|
if (per_cpu(wakeup_sharedprio, cpu)) {
|
|
latency_hist(WAKEUP_LATENCY_SHAREDPRIO, cpu, latency, 0, stop,
|
|
next);
|
|
per_cpu(wakeup_sharedprio, cpu) = 0;
|
|
} else {
|
|
latency_hist(WAKEUP_LATENCY, cpu, latency, 0, stop, next);
|
|
#ifdef CONFIG_MISSED_TIMER_OFFSETS_HIST
|
|
if (timerandwakeup_enabled_data.enabled) {
|
|
latency_hist(TIMERANDWAKEUP_LATENCY, cpu,
|
|
next->timer_offset + latency, next->timer_offset,
|
|
stop, next);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
out_reset:
|
|
#ifdef CONFIG_MISSED_TIMER_OFFSETS_HIST
|
|
next->timer_offset = 0;
|
|
#endif
|
|
put_task_struct(cpu_wakeup_task);
|
|
per_cpu(wakeup_task, cpu) = NULL;
|
|
out:
|
|
raw_spin_unlock_irqrestore(&wakeup_lock, flags);
|
|
}
|
|
#endif
|
|
|
|
#ifdef CONFIG_MISSED_TIMER_OFFSETS_HIST
|
|
static notrace void probe_hrtimer_interrupt(void *v, int cpu,
|
|
long long latency_ns, struct task_struct *curr,
|
|
struct task_struct *task)
|
|
{
|
|
if (latency_ns <= 0 && task != NULL && rt_task(task) &&
|
|
(task->prio < curr->prio ||
|
|
(task->prio == curr->prio &&
|
|
!cpumask_test_cpu(cpu, &task->cpus_allowed)))) {
|
|
long latency;
|
|
cycle_t now;
|
|
|
|
if (missed_timer_offsets_pid) {
|
|
if (likely(missed_timer_offsets_pid !=
|
|
task_pid_nr(task)))
|
|
return;
|
|
}
|
|
|
|
now = ftrace_now(cpu);
|
|
latency = (long) div_s64(-latency_ns, NSECS_PER_USECS);
|
|
latency_hist(MISSED_TIMER_OFFSETS, cpu, latency, latency, now,
|
|
task);
|
|
#ifdef CONFIG_WAKEUP_LATENCY_HIST
|
|
task->timer_offset = latency;
|
|
#endif
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static __init int latency_hist_init(void)
|
|
{
|
|
struct dentry *latency_hist_root = NULL;
|
|
struct dentry *dentry;
|
|
#ifdef CONFIG_WAKEUP_LATENCY_HIST
|
|
struct dentry *dentry_sharedprio;
|
|
#endif
|
|
struct dentry *entry;
|
|
struct dentry *enable_root;
|
|
int i = 0;
|
|
struct hist_data *my_hist;
|
|
char name[64];
|
|
char *cpufmt = "CPU%d";
|
|
#if defined(CONFIG_WAKEUP_LATENCY_HIST) || \
|
|
defined(CONFIG_MISSED_TIMER_OFFSETS_HIST)
|
|
char *cpufmt_maxlatproc = "max_latency-CPU%d";
|
|
struct maxlatproc_data *mp = NULL;
|
|
#endif
|
|
|
|
dentry = tracing_init_dentry();
|
|
latency_hist_root = debugfs_create_dir(latency_hist_dir_root, dentry);
|
|
enable_root = debugfs_create_dir("enable", latency_hist_root);
|
|
|
|
#ifdef CONFIG_INTERRUPT_OFF_HIST
|
|
dentry = debugfs_create_dir(irqsoff_hist_dir, latency_hist_root);
|
|
for_each_possible_cpu(i) {
|
|
sprintf(name, cpufmt, i);
|
|
entry = debugfs_create_file(name, 0444, dentry,
|
|
&per_cpu(irqsoff_hist, i), &latency_hist_fops);
|
|
my_hist = &per_cpu(irqsoff_hist, i);
|
|
atomic_set(&my_hist->hist_mode, 1);
|
|
my_hist->min_lat = LONG_MAX;
|
|
}
|
|
entry = debugfs_create_file("reset", 0644, dentry,
|
|
(void *)IRQSOFF_LATENCY, &latency_hist_reset_fops);
|
|
#endif
|
|
|
|
#ifdef CONFIG_PREEMPT_OFF_HIST
|
|
dentry = debugfs_create_dir(preemptoff_hist_dir,
|
|
latency_hist_root);
|
|
for_each_possible_cpu(i) {
|
|
sprintf(name, cpufmt, i);
|
|
entry = debugfs_create_file(name, 0444, dentry,
|
|
&per_cpu(preemptoff_hist, i), &latency_hist_fops);
|
|
my_hist = &per_cpu(preemptoff_hist, i);
|
|
atomic_set(&my_hist->hist_mode, 1);
|
|
my_hist->min_lat = LONG_MAX;
|
|
}
|
|
entry = debugfs_create_file("reset", 0644, dentry,
|
|
(void *)PREEMPTOFF_LATENCY, &latency_hist_reset_fops);
|
|
#endif
|
|
|
|
#if defined(CONFIG_INTERRUPT_OFF_HIST) && defined(CONFIG_PREEMPT_OFF_HIST)
|
|
dentry = debugfs_create_dir(preemptirqsoff_hist_dir,
|
|
latency_hist_root);
|
|
for_each_possible_cpu(i) {
|
|
sprintf(name, cpufmt, i);
|
|
entry = debugfs_create_file(name, 0444, dentry,
|
|
&per_cpu(preemptirqsoff_hist, i), &latency_hist_fops);
|
|
my_hist = &per_cpu(preemptirqsoff_hist, i);
|
|
atomic_set(&my_hist->hist_mode, 1);
|
|
my_hist->min_lat = LONG_MAX;
|
|
}
|
|
entry = debugfs_create_file("reset", 0644, dentry,
|
|
(void *)PREEMPTIRQSOFF_LATENCY, &latency_hist_reset_fops);
|
|
#endif
|
|
|
|
#if defined(CONFIG_INTERRUPT_OFF_HIST) || defined(CONFIG_PREEMPT_OFF_HIST)
|
|
entry = debugfs_create_file("preemptirqsoff", 0644,
|
|
enable_root, (void *)&preemptirqsoff_enabled_data,
|
|
&enable_fops);
|
|
#endif
|
|
|
|
#ifdef CONFIG_WAKEUP_LATENCY_HIST
|
|
dentry = debugfs_create_dir(wakeup_latency_hist_dir,
|
|
latency_hist_root);
|
|
dentry_sharedprio = debugfs_create_dir(
|
|
wakeup_latency_hist_dir_sharedprio, dentry);
|
|
for_each_possible_cpu(i) {
|
|
sprintf(name, cpufmt, i);
|
|
|
|
entry = debugfs_create_file(name, 0444, dentry,
|
|
&per_cpu(wakeup_latency_hist, i),
|
|
&latency_hist_fops);
|
|
my_hist = &per_cpu(wakeup_latency_hist, i);
|
|
atomic_set(&my_hist->hist_mode, 1);
|
|
my_hist->min_lat = LONG_MAX;
|
|
|
|
entry = debugfs_create_file(name, 0444, dentry_sharedprio,
|
|
&per_cpu(wakeup_latency_hist_sharedprio, i),
|
|
&latency_hist_fops);
|
|
my_hist = &per_cpu(wakeup_latency_hist_sharedprio, i);
|
|
atomic_set(&my_hist->hist_mode, 1);
|
|
my_hist->min_lat = LONG_MAX;
|
|
|
|
sprintf(name, cpufmt_maxlatproc, i);
|
|
|
|
mp = &per_cpu(wakeup_maxlatproc, i);
|
|
entry = debugfs_create_file(name, 0444, dentry, mp,
|
|
&maxlatproc_fops);
|
|
clear_maxlatprocdata(mp);
|
|
|
|
mp = &per_cpu(wakeup_maxlatproc_sharedprio, i);
|
|
entry = debugfs_create_file(name, 0444, dentry_sharedprio, mp,
|
|
&maxlatproc_fops);
|
|
clear_maxlatprocdata(mp);
|
|
}
|
|
entry = debugfs_create_file("pid", 0644, dentry,
|
|
(void *)&wakeup_pid, &pid_fops);
|
|
entry = debugfs_create_file("reset", 0644, dentry,
|
|
(void *)WAKEUP_LATENCY, &latency_hist_reset_fops);
|
|
entry = debugfs_create_file("reset", 0644, dentry_sharedprio,
|
|
(void *)WAKEUP_LATENCY_SHAREDPRIO, &latency_hist_reset_fops);
|
|
entry = debugfs_create_file("wakeup", 0644,
|
|
enable_root, (void *)&wakeup_latency_enabled_data,
|
|
&enable_fops);
|
|
#endif
|
|
|
|
#ifdef CONFIG_MISSED_TIMER_OFFSETS_HIST
|
|
dentry = debugfs_create_dir(missed_timer_offsets_dir,
|
|
latency_hist_root);
|
|
for_each_possible_cpu(i) {
|
|
sprintf(name, cpufmt, i);
|
|
entry = debugfs_create_file(name, 0444, dentry,
|
|
&per_cpu(missed_timer_offsets, i), &latency_hist_fops);
|
|
my_hist = &per_cpu(missed_timer_offsets, i);
|
|
atomic_set(&my_hist->hist_mode, 1);
|
|
my_hist->min_lat = LONG_MAX;
|
|
|
|
sprintf(name, cpufmt_maxlatproc, i);
|
|
mp = &per_cpu(missed_timer_offsets_maxlatproc, i);
|
|
entry = debugfs_create_file(name, 0444, dentry, mp,
|
|
&maxlatproc_fops);
|
|
clear_maxlatprocdata(mp);
|
|
}
|
|
entry = debugfs_create_file("pid", 0644, dentry,
|
|
(void *)&missed_timer_offsets_pid, &pid_fops);
|
|
entry = debugfs_create_file("reset", 0644, dentry,
|
|
(void *)MISSED_TIMER_OFFSETS, &latency_hist_reset_fops);
|
|
entry = debugfs_create_file("missed_timer_offsets", 0644,
|
|
enable_root, (void *)&missed_timer_offsets_enabled_data,
|
|
&enable_fops);
|
|
#endif
|
|
|
|
#if defined(CONFIG_WAKEUP_LATENCY_HIST) && \
|
|
defined(CONFIG_MISSED_TIMER_OFFSETS_HIST)
|
|
dentry = debugfs_create_dir(timerandwakeup_latency_hist_dir,
|
|
latency_hist_root);
|
|
for_each_possible_cpu(i) {
|
|
sprintf(name, cpufmt, i);
|
|
entry = debugfs_create_file(name, 0444, dentry,
|
|
&per_cpu(timerandwakeup_latency_hist, i),
|
|
&latency_hist_fops);
|
|
my_hist = &per_cpu(timerandwakeup_latency_hist, i);
|
|
atomic_set(&my_hist->hist_mode, 1);
|
|
my_hist->min_lat = LONG_MAX;
|
|
|
|
sprintf(name, cpufmt_maxlatproc, i);
|
|
mp = &per_cpu(timerandwakeup_maxlatproc, i);
|
|
entry = debugfs_create_file(name, 0444, dentry, mp,
|
|
&maxlatproc_fops);
|
|
clear_maxlatprocdata(mp);
|
|
}
|
|
entry = debugfs_create_file("reset", 0644, dentry,
|
|
(void *)TIMERANDWAKEUP_LATENCY, &latency_hist_reset_fops);
|
|
entry = debugfs_create_file("timerandwakeup", 0644,
|
|
enable_root, (void *)&timerandwakeup_enabled_data,
|
|
&enable_fops);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
device_initcall(latency_hist_init);
|