Hi, Ingo

This patch can record interrupt-off latency, preemption-off latency and
wakeup latency in a big history array, in the meanwhile, it dummies up
printks produced
 by these latency timing.

This patch adds three options: "Log interrupts-off critical section
latency", "Log non-preemptible critical section latency" and "Log wakeup
latency", they may be selected together, but just enabling one of them
will be better.

All the latency history can be viewed via /proc interface:
        
(Note, * presents CPU ID)
###interrupt-off latency history
        cat /proc/latency_log/interrupt_off_latency/CPU*

###preemption-off latency history
        cat /proc/latency_log/preemption_off_latency/CPU*

###wakeup latency history
        cat  /proc/latency_log/wakeup_latency/CPU*

Latency tracing will trace the maximum sample of three options, so
/proc/latency_trace can provide the detailed tracing for maximum sample.

diffstat:
 kernel/Makefile   |    1
 kernel/latency.c   |   96 ++++++++++++++++
 kernel/latency_log.c |  292
+++++++++++++++++++++++++++++++++++++++++++++++++++
 lib/Kconfig.debug  |   59 ++++++++++
 4 files changed, 445 insertions(+), 3 deletions(-)

--- /dev/null   2003-01-30 18:24:37.000000000 +0800
+++ linux-2.6.12/kernel/latency_log.c   2005-07-15 17:45:53.000000000
+0800
@@ -0,0 +1,292 @@
+/*
+ * kernel/latency_log.c
+ *
+ * Add support for history logs of preemption-off latency and
+ * interrupt-off latency and wakeup latency, it depends on 
+ * Real-Time Preemption Support.
+ *
+ *  Copyright (C) 2005, 2007 MontaVista Software, Inc.
+ *  Yi Yang <[EMAIL PROTECTED]>
+ *
+ */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/percpu.h>
+#include <asm/atomic.h>
+
+#define MAX_ENTRY_NUM 10240
+#define LATENCY_TYPE_NUM 3
+
+typedef struct log_data_struct {
+       atomic_t log_mode; /* 0 log, 1 don't log */
+       unsigned long min_lat;
+       unsigned long avg_lat;
+       unsigned long max_lat;
+       unsigned long long beyond_log_bound_samples;
+       unsigned long long accumulate_lat;
+       unsigned long long total_samples;
+       unsigned long long log_array[MAX_ENTRY_NUM];
+} log_data_t;
+
+enum {
+       INTERRUPT_LATENCY = 0,
+       PREEMPT_LATENCY,
+       WAKEUP_LATENCY
+};
+
+static struct proc_dir_entry * latency_log_root = NULL;
+static char * latency_log_proc_dir_root = "latency_log";
+
+static char * percpu_proc_name = "CPU";
+
+#ifdef CONFIG_INTERRUPT_OFF_LOG
+static DEFINE_PER_CPU(log_data_t, interrupt_off_log);
+static char * interrupt_off_log_proc_dir = "interrupt_off_latency";
+#endif
+
+#ifdef CONFIG_PREEMPT_OFF_LOG
+static DEFINE_PER_CPU(log_data_t, preempt_off_log);
+static char * preempt_off_log_proc_dir = "preempt_off_latency";
+#endif
+
+#ifdef CONFIG_WAKEUP_LATENCY_LOG
+static DEFINE_PER_CPU(log_data_t, wakeup_latency_log);
+static char * wakeup_latency_log_proc_dir = "wakeup_latency";
+#endif
+
+static struct proc_dir_entry *entry[LATENCY_TYPE_NUM][NR_CPUS];
+
+static inline u64 u64_div(u64 x, u64 y)
+{
+        do_div(x, y);
+        return x;
+}
+
+void latency_log(int latency_type, int cpu, unsigned long latency)
+{
+       log_data_t * my_log;
+
+
+       if ((cpu < 0) || (cpu >= NR_CPUS) || (latency_type <
INTERRUPT_LATENCY)
+               || (latency_type > WAKEUP_LATENCY) || (latency < 0)) {
+               return;
+       }
+
+       switch(latency_type) {
+#ifdef CONFIG_INTERRUPT_OFF_LOG
+       case INTERRUPT_LATENCY:
+               my_log = (log_data_t *)&per_cpu(interrupt_off_log, cpu);
+               if (atomic_read(&my_log->log_mode) == 0) {
+                       return;
+               }
+               break;
+#endif
+
+#ifdef CONFIG_PREEMPT_OFF_LOG
+       case PREEMPT_LATENCY:
+               my_log = (log_data_t *)&per_cpu(preempt_off_log, cpu);
+               if (atomic_read(&my_log->log_mode) == 0) {
+                       return;
+               }
+               break;
+#endif
+
+#ifdef CONFIG_WAKEUP_LATENCY_LOG
+       case WAKEUP_LATENCY:
+               my_log = (log_data_t *)&per_cpu(wakeup_latency_log, cpu);
+               if (atomic_read(&my_log->log_mode) == 0) {
+                       return;
+               }
+               break;
+#endif
+       default:
+               return;
+       }
+
+       if (latency >= MAX_ENTRY_NUM) {
+               my_log->beyond_log_bound_samples++;
+       }
+       else {
+               my_log->log_array[latency]++;
+       }
+
+       if (latency < my_log->min_lat) {
+               my_log->min_lat = latency;
+       }
+       else if (latency > my_log->max_lat) { 
+               my_log->max_lat = latency;
+       }
+
+       my_log->total_samples++;
+       my_log->accumulate_lat += latency;
+       my_log->avg_lat = (unsigned long)u64_div(my_log->accumulate_lat,
my_log->total_samples);
+       return;
+}
+
+static void *l_start(struct seq_file *m, loff_t * pos)
+{
+       loff_t *index_ptr = kmalloc(sizeof(loff_t), GFP_KERNEL);
+       loff_t index = *pos;
+       log_data_t *my_log = (log_data_t *) m->private;
+
+       if (index_ptr == NULL) {
+               return NULL;
+       }
+
+       if (index == 0) {
+               atomic_dec(&my_log->log_mode);
+               seq_printf(m, "#Minimum latency: %lu microseconds.\n"
+                          "#Average latency: %lu microseconds.\n"
+                          "#Maximum latency: %lu microseconds.\n"
+                          "#Total samples: %llu\n"
+                          "#There are %llu samples greater or equal than %d 
microseconds\n"
+                          "#usecs\t%16s\n"
+                          , my_log->min_lat
+                          , my_log->avg_lat
+                          , my_log->max_lat
+                          , my_log->total_samples
+                          , my_log->beyond_log_bound_samples
+                          , MAX_ENTRY_NUM, "samples");
+       }
+       if (index >= MAX_ENTRY_NUM) {
+               return NULL;
+       }
+
+       *index_ptr = index;
+       return index_ptr;
+}
+
+static void *l_next(struct seq_file *m, void *p, loff_t * pos)
+{
+       loff_t *index_ptr = p;
+       log_data_t *my_log = (log_data_t *) m->private;
+
+       if (++*pos >= MAX_ENTRY_NUM) {
+               atomic_inc(&my_log->log_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;
+       log_data_t *my_log = (log_data_t *) m->private;
+
+       seq_printf(m, "%5d\t%16llu\n", index
+               , my_log->log_array[index]);
+       return 0;
+}
+
+static struct seq_operations latency_log_seq_op = {
+       .start = l_start,
+       .next = l_next,
+       .stop = l_stop,
+       .show = l_show
+};
+
+static int latency_log_seq_open(struct inode *inode, struct file *file)
+{
+       int ret;
+       struct proc_dir_entry *entry_ptr = NULL;
+       int i, j;
+       struct seq_file *seq;
+       int break_flags = 0;
+
+       entry_ptr = PDE(file->f_dentry->d_inode);
+       for (i = 0; i < LATENCY_TYPE_NUM; i++) {
+               for (j = 0; j < NR_CPUS; j++) {
+                       if (entry[i][j] == NULL) {
+                               continue;
+                       }
+                       if (entry_ptr->low_ino == entry[i][j]->low_ino) {
+                               break_flags = 1;
+                               break;
+                       }
+               }
+               if (break_flags == 1) {
+                       break;
+               }
+       }
+       ret = seq_open(file, &latency_log_seq_op);
+       if (break_flags == 1) {
+               seq = (struct seq_file *)file->private_data;
+               seq->private = entry[i][j]->data;
+       }
+       return ret;
+}
+static struct file_operations latency_log_seq_fops = {
+       .open = latency_log_seq_open,
+       .read = seq_read,
+       .llseek = seq_lseek,
+       .release = seq_release,
+};
+
+static __init int latency_log_init(void)
+{
+       struct proc_dir_entry *tmp_parent_proc_dir;
+       char procname[64];
+       int len = 0;
+       int i = 0;
+       log_data_t *my_log;
+
+       latency_log_root = proc_mkdir(latency_log_proc_dir_root, NULL);
+
+       
+#ifdef CONFIG_INTERRUPT_OFF_LOG
+       tmp_parent_proc_dir = proc_mkdir(interrupt_off_log_proc_dir,
latency_log_root);
+       for (i = 0; i < NR_CPUS; i++) {
+               len = sprintf(procname, "%s%d", percpu_proc_name, i);
+               procname[len] = '\0';
+               entry[INTERRUPT_LATENCY][i] =
+                       create_proc_entry(procname, 0, tmp_parent_proc_dir);
+               entry[INTERRUPT_LATENCY][i]->data = (void
*)&per_cpu(interrupt_off_log, i);
+               entry[INTERRUPT_LATENCY][i]->proc_fops = &latency_log_seq_fops;
+               my_log = (log_data_t *) entry[INTERRUPT_LATENCY][i]->data;
+               atomic_set(&my_log->log_mode,1);
+               my_log->min_lat = 0xFFFFFFFFUL;
+       }
+#endif
+
+#ifdef CONFIG_PREEMPT_OFF_LOG
+       tmp_parent_proc_dir = proc_mkdir(preempt_off_log_proc_dir,
latency_log_root);
+       for (i = 0; i < NR_CPUS; i++) {
+               len = sprintf(procname, "%s%d", percpu_proc_name, i);
+               procname[len] = '\0';
+               entry[PREEMPT_LATENCY][i] =
+                       create_proc_entry(procname, 0, tmp_parent_proc_dir);
+               entry[PREEMPT_LATENCY][i]->data = (void 
*)&per_cpu(preempt_off_log,
i);
+                entry[PREEMPT_LATENCY][i]->proc_fops =
&latency_log_seq_fops;
+               my_log = (log_data_t *) entry[PREEMPT_LATENCY][i]->data;
+               atomic_set(&my_log->log_mode,1); 
+               my_log->min_lat = 0xFFFFFFFFUL;
+       }
+#endif
+
+#ifdef CONFIG_WAKEUP_LATENCY_LOG
+       tmp_parent_proc_dir = proc_mkdir(wakeup_latency_log_proc_dir,
latency_log_root);
+       for (i = 0; i < NR_CPUS; i++) {
+               len = sprintf(procname, "%s%d", percpu_proc_name, i);
+               procname[len] = '\0';
+               entry[WAKEUP_LATENCY][i] =
+                       create_proc_entry(procname, 0, tmp_parent_proc_dir);
+                entry[WAKEUP_LATENCY][i]->data = (void
*)&per_cpu(wakeup_latency_log, i);
+                entry[WAKEUP_LATENCY][i]->proc_fops =
&latency_log_seq_fops;
+               my_log = (log_data_t *) entry[WAKEUP_LATENCY][i]->data;
+               atomic_set(&my_log->log_mode,1); 
+               my_log->min_lat = 0xFFFFFFFFUL;
+       }
+#endif
+       return 0;
+
+}
+
+__initcall(latency_log_init);
--- linux-2.6.12/kernel/latency.c.orig  2005-07-15 17:45:10.000000000
+0800
+++ linux-2.6.12/kernel/latency.c       2005-07-15 17:45:53.000000000 +0800
@@ -23,6 +23,20 @@
 #include <asm/uaccess.h>
 #include <asm/unistd.h>
 
+#ifdef CONFIG_LATENCY_LOG
+enum {
+       INTERRUPT_LATENCY = 0,
+       PREEMPT_LATENCY,
+       WAKEUP_LATENCY
+};
+extern void latency_log(int latency_type, int cpu, unsigned long
latency);
+
+#ifdef CONFIG_INTERRUPT_OFF_LOG
+static inline int is_interrupt_off_timing(void);
+#endif
+
+#endif
+
 #ifdef CONFIG_PPC
 #include <asm/time.h>
 #endif
@@ -60,7 +74,12 @@ int wakeup_timing = 1;
  * Maximum preemption latency measured. Initialize to maximum,
  * we clear it after bootup.
  */
+#ifdef CONFIG_LATENCY_LOG
+static cycles_t preempt_max_latency = (cycles_t)0UL;
+#else
 static cycles_t preempt_max_latency = (cycles_t)ULONG_MAX;
+#endif
+
 static cycles_t preempt_thresh;
 
 /*
@@ -1220,6 +1239,9 @@ check_critical_timing(int cpu, struct cp
 {
        unsigned long latency, t0, t1;
        cycles_t T0, T1, T2, delta;
+#ifdef CONFIG_CRITICAL_LATENCY_LOG
+       int latency_type;
+#endif
 
        if (trace_user_triggered)
                return;
@@ -1231,8 +1253,10 @@ check_critical_timing(int cpu, struct cp
        T1 = cycles();
        delta = T1-T0;
 
+#ifndef CONFIG_CRITICAL_LATENCY_LOG
        if (!report_latency(delta))
                goto out;
+#endif
        ____trace(cpu, TRACE_FN, tr, CALLER_ADDR0, parent_eip, 0, 0, 0);
        /*
         * Update the timestamp, because the trace entry above
@@ -1247,21 +1271,29 @@ check_critical_timing(int cpu, struct cp
        if (tr->critical_sequence != max_sequence || down_trylock(&max_mutex))
                goto out;
 
+
+#ifndef CONFIG_CRITICAL_LATENCY_LOG
        if (!preempt_thresh && preempt_max_latency > delta) {
                printk("bug: updating %016Lx > %016Lx?\n",
                        preempt_max_latency, delta);
                printk("  [%016Lx %016Lx %016Lx]\n", T0, T1, T2);
        }
+#endif
 
+       latency = cycles_to_usecs(delta);
+
+#ifdef CONFIG_CRITICAL_LATENCY_LOG
+if (preempt_max_latency < delta) {
+#endif
        preempt_max_latency = delta;
        t0 = cycles_to_usecs(T0);
        t1 = cycles_to_usecs(T1);
-       latency = cycles_to_usecs(delta);
 
        tr->critical_end = parent_eip;
 
        update_max_tr(tr);
 
+#ifndef CONFIG_CRITICAL_LATENCY_LOG
        if (preempt_thresh)
                printk("(%16s-%-5d|#%d): %lu us critical section "
                        "violates %lu us threshold.\n"
@@ -1282,10 +1314,35 @@ check_critical_timing(int cpu, struct cp
        dump_stack();
        t1 = cycles_to_usecs(cycles());
        printk(" =>   dump-end timestamp %lu\n\n", t1);
+#endif
 
        max_sequence++;
 
+#ifdef CONFIG_CRITICAL_LATENCY_LOG
+}
+#endif
+
        up(&max_mutex);
+
+#ifdef CONFIG_CRITICAL_LATENCY_LOG
+       latency_type = WAKEUP_LATENCY + 1;
+#ifdef CONFIG_INTERRUPT_OFF_LOG
+       if (is_interrupt_off_timing()) {
+                       latency_type = INTERRUPT_LATENCY;
+       }
+#ifdef CONFIG_PREEMPT_OFF_LOG
+       else {
+               latency_type = PREEMPT_LATENCY;
+       }
+#endif /* CONFIG_PREEMPT_OFF_LOG */
+#else
+#ifdef CONFIG_PREEMPT_OFF_LOG
+       latency_type = PREEMPT_LATENCY;
+#endif /* CONFIG_PREEMPT_OFF_LOG */
+#endif /* CONFIG_INTERRUPT_OFF_LOG */
+       latency_log(latency_type, cpu, latency);
+#endif /* CONFIG_CRITICAL_LATENCY_LOG */
+
 out:
        tr->critical_sequence = max_sequence;
        tr->preempt_timestamp = cycles();
@@ -1293,6 +1350,7 @@ out:
        tr->trace_idx = 0;
        _trace_cmdline(cpu, tr);
        ____trace(cpu, TRACE_FN, tr, CALLER_ADDR0, parent_eip, 0, 0, 0);
+
 }
 
 void notrace touch_critical_timing(void)
@@ -1371,6 +1429,17 @@ __stop_critical_timing(unsigned long eip
 # define irqs_off_preempt_count() 0
 #endif
 
+#ifdef CONFIG_INTERRUPT_OFF_LOG
+static inline int is_interrupt_off_timing(void)
+{
+       unsigned long flags;
+
+       raw_local_save_flags(flags);
+
+       return (!irqs_off_preempt_count() && raw_irqs_disabled_flags(flags));
+} 
+#endif
+
 void notrace trace_irqs_off_lowlevel(void)
 {
        unsigned long flags;
@@ -1575,6 +1644,9 @@ check_wakeup_timing(struct cpu_trace *tr
        unsigned long latency;
        unsigned long t0, t1;
        cycles_t T0, T1, T2, delta;
+#ifdef CONFIG_WAKEUP_LATENCY_LOG
+       int cpu = _smp_processor_id();
+#endif
 
        if (trace_user_triggered)
                return;
@@ -1593,8 +1665,10 @@ check_wakeup_timing(struct cpu_trace *tr
                T0 = T1;
        delta = T1-T0;
 
+#ifndef CONFIG_WAKEUP_LATENCY_LOG
        if (!report_latency(delta))
                goto out;
+#endif
 
        ____trace(smp_processor_id(), TRACE_FN, tr, CALLER_ADDR0, parent_eip,
0, 0, 0);
        T2 = cycles();
@@ -1605,21 +1679,27 @@ check_wakeup_timing(struct cpu_trace *tr
        if (tr->critical_sequence != max_sequence || down_trylock(&max_mutex))
                goto out;
 
+#ifndef CONFIG_WAKEUP_LATENCY_LOG
        if (!preempt_thresh && preempt_max_latency > delta) {
                printk("bug2: updating %016Lx > %016Lx?\n",
                        preempt_max_latency, delta);
                printk("  [%016Lx %016Lx %016Lx]\n", T0, T1, T2);
        }
+#endif
 
+       latency = cycles_to_usecs(delta);
+
+#ifdef CONFIG_WAKEUP_LATENCY_LOG
+if (preempt_max_latency < delta) {
+#endif
        preempt_max_latency = delta;
        t0 = cycles_to_usecs(T0);
        t1 = cycles_to_usecs(T1);
-       latency = cycles_to_usecs(delta);
-
        tr->critical_end = parent_eip;
 
        update_max_tr(tr);
 
+#ifndef CONFIG_WAKEUP_LATENCY_LOG
        if (preempt_thresh)
                printk("(%16s-%-5d|#%d): %lu us wakeup latency "
                        "violates %lu us threshold.\n",
@@ -1630,12 +1710,22 @@ check_wakeup_timing(struct cpu_trace *tr
                printk("(%16s-%-5d|#%d): new %lu us maximum-latency "
                        "wakeup.\n", current->comm, current->pid,
                                _smp_processor_id(), latency);
+#endif
 
        max_sequence++;
 
+#ifdef CONFIG_WAKEUP_LATENCY_LOG
+}
+#endif
        up(&max_mutex);
+
+#ifdef CONFIG_WAKEUP_LATENCY_LOG
+       latency_log(WAKEUP_LATENCY, cpu, latency);
+#endif
+
 out:
        atomic_dec(&tr->disabled);
+
 }
 
 /*
--- linux-2.6.12/kernel/Makefile.orig   2005-07-15 17:44:56.000000000
+0800
+++ linux-2.6.12/kernel/Makefile        2005-07-15 17:45:53.000000000 +0800
@@ -13,6 +13,7 @@ obj-$(CONFIG_PREEMPT_RT) += rt.o
 
 obj-$(CONFIG_DEBUG_PREEMPT) += latency.o
 obj-$(CONFIG_LATENCY_TIMING) += latency.o
+obj-$(CONFIG_LATENCY_LOG) += latency_log.o
 
 obj-$(CONFIG_FUTEX) += futex.o
 obj-$(CONFIG_GENERIC_ISA_DMA) += dma.o
--- linux-2.6.12/lib/Kconfig.debug.orig 2005-07-15 17:45:34.000000000
+0800
+++ linux-2.6.12/lib/Kconfig.debug      2005-07-15 18:07:15.000000000 +0800
@@ -143,6 +143,21 @@ config WAKEUP_TIMING
 
              echo 0 > /proc/sys/kernel/preempt_max_latency
 
+config WAKEUP_LATENCY_LOG
+       bool "Log wakeup latency"
+       default n
+       depends on WAKEUP_TIMING
+       help
+         This option logs all the wakeup latency timing to a big histogram
+         bucket, in the meanwhile, it also dummies up printk produced by 
+         wakeup latency timing.
+
+         The wakeup latency timing histogram can be viewed via:
+
+             cat /proc/latency_log/wakeup_latency/CPU*
+
+         (Note: * presents CPU ID.)
+
 config PREEMPT_TRACE
        bool
        default y
@@ -165,6 +180,23 @@ config CRITICAL_PREEMPT_TIMING
          enabled. This option and the irqs-off timing option can be
          used together or separately.)
 
+config PREEMPT_OFF_LOG
+        bool "Log non-preemptible critical section latency"
+        default n
+        depends on CRITICAL_PREEMPT_TIMING
+        help
+          This option logs all the non-preemptible critical section
latency
+         timing to a big histogram bucket, in the meanwhile, it also 
+         dummies up printk produced by non-preemptible critical section
+         latency timing.
+
+          The non-preemptible critical section latency timing histogram
can
+         be viewed via:
+
+              cat /proc/latency_log/preempt_off_latency/CPU*
+
+          (Note: * presents CPU ID.)
+
 config CRITICAL_IRQSOFF_TIMING
        bool "Interrupts-off critical section latency timing"
        default n
@@ -181,6 +213,23 @@ config CRITICAL_IRQSOFF_TIMING
          enabled. This option and the preempt-off timing option can be
          used together or separately.)
 
+config INTERRUPT_OFF_LOG
+        bool "Log interrupts-off critical section latency"
+        default n
+        depends on CRITICAL_IRQSOFF_TIMING
+        help
+          This option logs all the interrupts-off critical section
latency 
+          timing to a big histogram bucket, in the meanwhile, it also 
+          dummies up printk produced by interrupts-off critical section
+          latency timing.
+
+          The interrupts-off critical section latency timing histogram
can 
+          be viewed via:
+
+              cat /proc/latency_log/interrupt_off_latency/CPU*
+
+          (Note: * presents CPU ID.)
+
 config CRITICAL_TIMING
        bool
        default y
@@ -191,6 +240,16 @@ config LATENCY_TIMING
        default y
        depends on WAKEUP_TIMING || CRITICAL_TIMING
 
+config CRITICAL_LATENCY_LOG
+       bool
+       default y
+       depends on PREEMPT_OFF_LOG || INTERRUPT_OFF_LOG
+
+config LATENCY_LOG
+       bool
+       default y
+       depends on WAKEUP_LATENCY_LOG || CRITICAL_LATENCY_LOG
+
 config LATENCY_TRACE
        bool "Latency tracing"
        default n


-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to