diff options
| author | Arjan van de Ven <arjan@linux.intel.com> | 2008-01-25 15:08:34 -0500 |
|---|---|---|
| committer | Ingo Molnar <mingo@elte.hu> | 2008-01-25 15:08:34 -0500 |
| commit | 9745512ce79de686df354dc70a8d1a74d801892d (patch) | |
| tree | 9b64e2b2e6d2ae534beef136922082f21701c7b9 | |
| parent | 326587b840785c60f5dc18557235a23bafefd620 (diff) | |
sched: latencytop support
LatencyTOP kernel infrastructure; it measures latencies in the
scheduler and tracks it system wide and per process.
Signed-off-by: Arjan van de Ven <arjan@linux.intel.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
| -rw-r--r-- | arch/x86/kernel/stacktrace.c | 27 | ||||
| -rw-r--r-- | fs/proc/base.c | 78 | ||||
| -rw-r--r-- | include/linux/latencytop.h | 44 | ||||
| -rw-r--r-- | include/linux/sched.h | 5 | ||||
| -rw-r--r-- | include/linux/stacktrace.h | 3 | ||||
| -rw-r--r-- | kernel/Makefile | 1 | ||||
| -rw-r--r-- | kernel/fork.c | 1 | ||||
| -rw-r--r-- | kernel/latencytop.c | 239 | ||||
| -rw-r--r-- | kernel/sched_fair.c | 8 | ||||
| -rw-r--r-- | kernel/sysctl.c | 10 | ||||
| -rw-r--r-- | lib/Kconfig.debug | 14 |
11 files changed, 429 insertions, 1 deletions
diff --git a/arch/x86/kernel/stacktrace.c b/arch/x86/kernel/stacktrace.c index 6fa6cf036c70..55771fd7e545 100644 --- a/arch/x86/kernel/stacktrace.c +++ b/arch/x86/kernel/stacktrace.c | |||
| @@ -33,6 +33,19 @@ static void save_stack_address(void *data, unsigned long addr) | |||
| 33 | trace->entries[trace->nr_entries++] = addr; | 33 | trace->entries[trace->nr_entries++] = addr; |
| 34 | } | 34 | } |
| 35 | 35 | ||
| 36 | static void save_stack_address_nosched(void *data, unsigned long addr) | ||
| 37 | { | ||
| 38 | struct stack_trace *trace = (struct stack_trace *)data; | ||
| 39 | if (in_sched_functions(addr)) | ||
| 40 | return; | ||
| 41 | if (trace->skip > 0) { | ||
| 42 | trace->skip--; | ||
| 43 | return; | ||
| 44 | } | ||
| 45 | if (trace->nr_entries < trace->max_entries) | ||
| 46 | trace->entries[trace->nr_entries++] = addr; | ||
| 47 | } | ||
| 48 | |||
| 36 | static const struct stacktrace_ops save_stack_ops = { | 49 | static const struct stacktrace_ops save_stack_ops = { |
| 37 | .warning = save_stack_warning, | 50 | .warning = save_stack_warning, |
| 38 | .warning_symbol = save_stack_warning_symbol, | 51 | .warning_symbol = save_stack_warning_symbol, |
| @@ -40,6 +53,13 @@ static const struct stacktrace_ops save_stack_ops = { | |||
| 40 | .address = save_stack_address, | 53 | .address = save_stack_address, |
| 41 | }; | 54 | }; |
| 42 | 55 | ||
| 56 | static const struct stacktrace_ops save_stack_ops_nosched = { | ||
| 57 | .warning = save_stack_warning, | ||
| 58 | .warning_symbol = save_stack_warning_symbol, | ||
| 59 | .stack = save_stack_stack, | ||
| 60 | .address = save_stack_address_nosched, | ||
| 61 | }; | ||
| 62 | |||
| 43 | /* | 63 | /* |
| 44 | * Save stack-backtrace addresses into a stack_trace buffer. | 64 | * Save stack-backtrace addresses into a stack_trace buffer. |
| 45 | */ | 65 | */ |
| @@ -50,3 +70,10 @@ void save_stack_trace(struct stack_trace *trace) | |||
| 50 | trace->entries[trace->nr_entries++] = ULONG_MAX; | 70 | trace->entries[trace->nr_entries++] = ULONG_MAX; |
| 51 | } | 71 | } |
| 52 | EXPORT_SYMBOL(save_stack_trace); | 72 | EXPORT_SYMBOL(save_stack_trace); |
| 73 | |||
| 74 | void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) | ||
| 75 | { | ||
| 76 | dump_trace(tsk, NULL, NULL, &save_stack_ops_nosched, trace); | ||
| 77 | if (trace->nr_entries < trace->max_entries) | ||
| 78 | trace->entries[trace->nr_entries++] = ULONG_MAX; | ||
| 79 | } | ||
diff --git a/fs/proc/base.c b/fs/proc/base.c index 7411bfb0b7cc..91fa8e6ce8ad 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c | |||
| @@ -310,6 +310,77 @@ static int proc_pid_schedstat(struct task_struct *task, char *buffer) | |||
| 310 | } | 310 | } |
| 311 | #endif | 311 | #endif |
| 312 | 312 | ||
| 313 | #ifdef CONFIG_LATENCYTOP | ||
| 314 | static int lstats_show_proc(struct seq_file *m, void *v) | ||
| 315 | { | ||
| 316 | int i; | ||
| 317 | struct task_struct *task = m->private; | ||
| 318 | seq_puts(m, "Latency Top version : v0.1\n"); | ||
| 319 | |||
| 320 | for (i = 0; i < 32; i++) { | ||
| 321 | if (task->latency_record[i].backtrace[0]) { | ||
| 322 | int q; | ||
| 323 | seq_printf(m, "%i %li %li ", | ||
| 324 | task->latency_record[i].count, | ||
| 325 | task->latency_record[i].time, | ||
| 326 | task->latency_record[i].max); | ||
| 327 | for (q = 0; q < LT_BACKTRACEDEPTH; q++) { | ||
| 328 | char sym[KSYM_NAME_LEN]; | ||
| 329 | char *c; | ||
| 330 | if (!task->latency_record[i].backtrace[q]) | ||
| 331 | break; | ||
| 332 | if (task->latency_record[i].backtrace[q] == ULONG_MAX) | ||
| 333 | break; | ||
| 334 | sprint_symbol(sym, task->latency_record[i].backtrace[q]); | ||
| 335 | c = strchr(sym, '+'); | ||
| 336 | if (c) | ||
| 337 | *c = 0; | ||
| 338 | seq_printf(m, "%s ", sym); | ||
| 339 | } | ||
| 340 | seq_printf(m, "\n"); | ||
| 341 | } | ||
| 342 | |||
| 343 | } | ||
| 344 | return 0; | ||
| 345 | } | ||
| 346 | |||
| 347 | static int lstats_open(struct inode *inode, struct file *file) | ||
| 348 | { | ||
| 349 | int ret; | ||
| 350 | struct seq_file *m; | ||
| 351 | struct task_struct *task = get_proc_task(inode); | ||
| 352 | |||
| 353 | ret = single_open(file, lstats_show_proc, NULL); | ||
| 354 | if (!ret) { | ||
| 355 | m = file->private_data; | ||
| 356 | m->private = task; | ||
| 357 | } | ||
| 358 | return ret; | ||
| 359 | } | ||
| 360 | |||
| 361 | static ssize_t lstats_write(struct file *file, const char __user *buf, | ||
| 362 | size_t count, loff_t *offs) | ||
| 363 | { | ||
| 364 | struct seq_file *m; | ||
| 365 | struct task_struct *task; | ||
| 366 | |||
| 367 | m = file->private_data; | ||
| 368 | task = m->private; | ||
| 369 | clear_all_latency_tracing(task); | ||
| 370 | |||
| 371 | return count; | ||
| 372 | } | ||
| 373 | |||
| 374 | static const struct file_operations proc_lstats_operations = { | ||
| 375 | .open = lstats_open, | ||
| 376 | .read = seq_read, | ||
| 377 | .write = lstats_write, | ||
| 378 | .llseek = seq_lseek, | ||
| 379 | .release = single_release, | ||
| 380 | }; | ||
| 381 | |||
| 382 | #endif | ||
| 383 | |||
| 313 | /* The badness from the OOM killer */ | 384 | /* The badness from the OOM killer */ |
| 314 | unsigned long badness(struct task_struct *p, unsigned long uptime); | 385 | unsigned long badness(struct task_struct *p, unsigned long uptime); |
| 315 | static int proc_oom_score(struct task_struct *task, char *buffer) | 386 | static int proc_oom_score(struct task_struct *task, char *buffer) |
| @@ -1020,6 +1091,7 @@ static const struct file_operations proc_fault_inject_operations = { | |||
| 1020 | }; | 1091 | }; |
| 1021 | #endif | 1092 | #endif |
| 1022 | 1093 | ||
| 1094 | |||
| 1023 | #ifdef CONFIG_SCHED_DEBUG | 1095 | #ifdef CONFIG_SCHED_DEBUG |
| 1024 | /* | 1096 | /* |
| 1025 | * Print out various scheduling related per-task fields: | 1097 | * Print out various scheduling related per-task fields: |
| @@ -2230,6 +2302,9 @@ static const struct pid_entry tgid_base_stuff[] = { | |||
| 2230 | #ifdef CONFIG_SCHEDSTATS | 2302 | #ifdef CONFIG_SCHEDSTATS |
| 2231 | INF("schedstat", S_IRUGO, pid_schedstat), | 2303 | INF("schedstat", S_IRUGO, pid_schedstat), |
| 2232 | #endif | 2304 | #endif |
| 2305 | #ifdef CONFIG_LATENCYTOP | ||
| 2306 | REG("latency", S_IRUGO, lstats), | ||
| 2307 | #endif | ||
| 2233 | #ifdef CONFIG_PROC_PID_CPUSET | 2308 | #ifdef CONFIG_PROC_PID_CPUSET |
| 2234 | REG("cpuset", S_IRUGO, cpuset), | 2309 | REG("cpuset", S_IRUGO, cpuset), |
| 2235 | #endif | 2310 | #endif |
| @@ -2555,6 +2630,9 @@ static const struct pid_entry tid_base_stuff[] = { | |||
| 2555 | #ifdef CONFIG_SCHEDSTATS | 2630 | #ifdef CONFIG_SCHEDSTATS |
| 2556 | INF("schedstat", S_IRUGO, pid_schedstat), | 2631 | INF("schedstat", S_IRUGO, pid_schedstat), |
| 2557 | #endif | 2632 | #endif |
| 2633 | #ifdef CONFIG_LATENCYTOP | ||
| 2634 | REG("latency", S_IRUGO, lstats), | ||
| 2635 | #endif | ||
| 2558 | #ifdef CONFIG_PROC_PID_CPUSET | 2636 | #ifdef CONFIG_PROC_PID_CPUSET |
| 2559 | REG("cpuset", S_IRUGO, cpuset), | 2637 | REG("cpuset", S_IRUGO, cpuset), |
| 2560 | #endif | 2638 | #endif |
diff --git a/include/linux/latencytop.h b/include/linux/latencytop.h new file mode 100644 index 000000000000..901c2d6377a8 --- /dev/null +++ b/include/linux/latencytop.h | |||
| @@ -0,0 +1,44 @@ | |||
| 1 | /* | ||
| 2 | * latencytop.h: Infrastructure for displaying latency | ||
| 3 | * | ||
| 4 | * (C) Copyright 2008 Intel Corporation | ||
| 5 | * Author: Arjan van de Ven <arjan@linux.intel.com> | ||
| 6 | * | ||
| 7 | */ | ||
| 8 | |||
| 9 | #ifndef _INCLUDE_GUARD_LATENCYTOP_H_ | ||
| 10 | #define _INCLUDE_GUARD_LATENCYTOP_H_ | ||
| 11 | |||
| 12 | #ifdef CONFIG_LATENCYTOP | ||
| 13 | |||
| 14 | #define LT_SAVECOUNT 32 | ||
| 15 | #define LT_BACKTRACEDEPTH 12 | ||
| 16 | |||
| 17 | struct latency_record { | ||
| 18 | unsigned long backtrace[LT_BACKTRACEDEPTH]; | ||
| 19 | unsigned int count; | ||
| 20 | unsigned long time; | ||
| 21 | unsigned long max; | ||
| 22 | }; | ||
| 23 | |||
| 24 | |||
| 25 | struct task_struct; | ||
| 26 | |||
| 27 | void account_scheduler_latency(struct task_struct *task, int usecs, int inter); | ||
| 28 | |||
| 29 | void clear_all_latency_tracing(struct task_struct *p); | ||
| 30 | |||
| 31 | #else | ||
| 32 | |||
| 33 | static inline void | ||
| 34 | account_scheduler_latency(struct task_struct *task, int usecs, int inter) | ||
| 35 | { | ||
| 36 | } | ||
| 37 | |||
| 38 | static inline void clear_all_latency_tracing(struct task_struct *p) | ||
| 39 | { | ||
| 40 | } | ||
| 41 | |||
| 42 | #endif | ||
| 43 | |||
| 44 | #endif | ||
diff --git a/include/linux/sched.h b/include/linux/sched.h index acadcab89ef9..dfc76e172f3f 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h | |||
| @@ -88,6 +88,7 @@ struct sched_param { | |||
| 88 | #include <linux/hrtimer.h> | 88 | #include <linux/hrtimer.h> |
| 89 | #include <linux/task_io_accounting.h> | 89 | #include <linux/task_io_accounting.h> |
| 90 | #include <linux/kobject.h> | 90 | #include <linux/kobject.h> |
| 91 | #include <linux/latencytop.h> | ||
| 91 | 92 | ||
| 92 | #include <asm/processor.h> | 93 | #include <asm/processor.h> |
| 93 | 94 | ||
| @@ -1220,6 +1221,10 @@ struct task_struct { | |||
| 1220 | int make_it_fail; | 1221 | int make_it_fail; |
| 1221 | #endif | 1222 | #endif |
| 1222 | struct prop_local_single dirties; | 1223 | struct prop_local_single dirties; |
| 1224 | #ifdef CONFIG_LATENCYTOP | ||
| 1225 | int latency_record_count; | ||
| 1226 | struct latency_record latency_record[LT_SAVECOUNT]; | ||
| 1227 | #endif | ||
| 1223 | }; | 1228 | }; |
| 1224 | 1229 | ||
| 1225 | /* | 1230 | /* |
diff --git a/include/linux/stacktrace.h b/include/linux/stacktrace.h index e7fa657d0c49..5da9794b2d78 100644 --- a/include/linux/stacktrace.h +++ b/include/linux/stacktrace.h | |||
| @@ -9,10 +9,13 @@ struct stack_trace { | |||
| 9 | }; | 9 | }; |
| 10 | 10 | ||
| 11 | extern void save_stack_trace(struct stack_trace *trace); | 11 | extern void save_stack_trace(struct stack_trace *trace); |
| 12 | extern void save_stack_trace_tsk(struct task_struct *tsk, | ||
| 13 | struct stack_trace *trace); | ||
| 12 | 14 | ||
| 13 | extern void print_stack_trace(struct stack_trace *trace, int spaces); | 15 | extern void print_stack_trace(struct stack_trace *trace, int spaces); |
| 14 | #else | 16 | #else |
| 15 | # define save_stack_trace(trace) do { } while (0) | 17 | # define save_stack_trace(trace) do { } while (0) |
| 18 | # define save_stack_trace_tsk(tsk, trace) do { } while (0) | ||
| 16 | # define print_stack_trace(trace, spaces) do { } while (0) | 19 | # define print_stack_trace(trace, spaces) do { } while (0) |
| 17 | #endif | 20 | #endif |
| 18 | 21 | ||
diff --git a/kernel/Makefile b/kernel/Makefile index 68755cd9a7e4..390d42146267 100644 --- a/kernel/Makefile +++ b/kernel/Makefile | |||
| @@ -62,6 +62,7 @@ obj-$(CONFIG_SYSCTL) += utsname_sysctl.o | |||
| 62 | obj-$(CONFIG_TASK_DELAY_ACCT) += delayacct.o | 62 | obj-$(CONFIG_TASK_DELAY_ACCT) += delayacct.o |
| 63 | obj-$(CONFIG_TASKSTATS) += taskstats.o tsacct.o | 63 | obj-$(CONFIG_TASKSTATS) += taskstats.o tsacct.o |
| 64 | obj-$(CONFIG_MARKERS) += marker.o | 64 | obj-$(CONFIG_MARKERS) += marker.o |
| 65 | obj-$(CONFIG_LATENCYTOP) += latencytop.o | ||
| 65 | 66 | ||
| 66 | ifneq ($(CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER),y) | 67 | ifneq ($(CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER),y) |
| 67 | # According to Alan Modra <alan@linuxcare.com.au>, the -fno-omit-frame-pointer is | 68 | # According to Alan Modra <alan@linuxcare.com.au>, the -fno-omit-frame-pointer is |
diff --git a/kernel/fork.c b/kernel/fork.c index 0c969f4fade0..39d22b3357de 100644 --- a/kernel/fork.c +++ b/kernel/fork.c | |||
| @@ -1205,6 +1205,7 @@ static struct task_struct *copy_process(unsigned long clone_flags, | |||
| 1205 | #ifdef TIF_SYSCALL_EMU | 1205 | #ifdef TIF_SYSCALL_EMU |
| 1206 | clear_tsk_thread_flag(p, TIF_SYSCALL_EMU); | 1206 | clear_tsk_thread_flag(p, TIF_SYSCALL_EMU); |
| 1207 | #endif | 1207 | #endif |
| 1208 | clear_all_latency_tracing(p); | ||
| 1208 | 1209 | ||
| 1209 | /* Our parent execution domain becomes current domain | 1210 | /* Our parent execution domain becomes current domain |
| 1210 | These must match for thread signalling to apply */ | 1211 | These must match for thread signalling to apply */ |
diff --git a/kernel/latencytop.c b/kernel/latencytop.c new file mode 100644 index 000000000000..b4e3c85abe74 --- /dev/null +++ b/kernel/latencytop.c | |||
| @@ -0,0 +1,239 @@ | |||
| 1 | /* | ||
| 2 | * latencytop.c: Latency display infrastructure | ||
| 3 | * | ||
| 4 | * (C) Copyright 2008 Intel Corporation | ||
| 5 | * Author: Arjan van de Ven <arjan@linux.intel.com> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or | ||
| 8 | * modify it under the terms of the GNU General Public License | ||
| 9 | * as published by the Free Software Foundation; version 2 | ||
| 10 | * of the License. | ||
| 11 | */ | ||
| 12 | #include <linux/latencytop.h> | ||
| 13 | #include <linux/kallsyms.h> | ||
| 14 | #include <linux/seq_file.h> | ||
| 15 | #include <linux/notifier.h> | ||
| 16 | #include <linux/spinlock.h> | ||
| 17 | #include <linux/proc_fs.h> | ||
| 18 | #include <linux/module.h> | ||
| 19 | #include <linux/sched.h> | ||
| 20 | #include <linux/list.h> | ||
| 21 | #include <linux/slab.h> | ||
| 22 | #include <linux/stacktrace.h> | ||
| 23 | |||
| 24 | static DEFINE_SPINLOCK(latency_lock); | ||
| 25 | |||
| 26 | #define MAXLR 128 | ||
| 27 | static struct latency_record latency_record[MAXLR]; | ||
| 28 | |||
| 29 | int latencytop_enabled; | ||
| 30 | |||
| 31 | void clear_all_latency_tracing(struct task_struct *p) | ||
| 32 | { | ||
| 33 | unsigned long flags; | ||
| 34 | |||
| 35 | if (!latencytop_enabled) | ||
| 36 | return; | ||
| 37 | |||
| 38 | spin_lock_irqsave(&latency_lock, flags); | ||
| 39 | memset(&p->latency_record, 0, sizeof(p->latency_record)); | ||
| 40 | p->latency_record_count = 0; | ||
| 41 | spin_unlock_irqrestore(&latency_lock, flags); | ||
| 42 | } | ||
| 43 | |||
| 44 | static void clear_global_latency_tracing(void) | ||
| 45 | { | ||
| 46 | unsigned long flags; | ||
| 47 | |||
| 48 | spin_lock_irqsave(&latency_lock, flags); | ||
| 49 | memset(&latency_record, 0, sizeof(latency_record)); | ||
| 50 | spin_unlock_irqrestore(&latency_lock, flags); | ||
| 51 | } | ||
| 52 | |||
| 53 | static void __sched | ||
| 54 | account_global_scheduler_latency(struct task_struct *tsk, struct latency_record *lat) | ||
| 55 | { | ||
| 56 | int firstnonnull = MAXLR + 1; | ||
| 57 | int i; | ||
| 58 | |||
| 59 | if (!latencytop_enabled) | ||
| 60 | return; | ||
| 61 | |||
| 62 | /* skip kernel threads for now */ | ||
| 63 | if (!tsk->mm) | ||
| 64 | return; | ||
| 65 | |||
| 66 | for (i = 0; i < MAXLR; i++) { | ||
| 67 | int q; | ||
| 68 | int same = 1; | ||
| 69 | /* Nothing stored: */ | ||
| 70 | if (!latency_record[i].backtrace[0]) { | ||
| 71 | if (firstnonnull > i) | ||
| 72 | firstnonnull = i; | ||
| 73 | continue; | ||
| 74 | } | ||
| 75 | for (q = 0 ; q < LT_BACKTRACEDEPTH ; q++) { | ||
| 76 | if (latency_record[i].backtrace[q] != | ||
| 77 | lat->backtrace[q]) | ||
| 78 | same = 0; | ||
| 79 | if (same && lat->backtrace[q] == 0) | ||
| 80 | break; | ||
| 81 | if (same && lat->backtrace[q] == ULONG_MAX) | ||
| 82 | break; | ||
| 83 | } | ||
| 84 | if (same) { | ||
| 85 | latency_record[i].count++; | ||
| 86 | latency_record[i].time += lat->time; | ||
| 87 | if (lat->time > latency_record[i].max) | ||
| 88 | latency_record[i].max = lat->time; | ||
| 89 | return; | ||
| 90 | } | ||
| 91 | } | ||
| 92 | |||
| 93 | i = firstnonnull; | ||
| 94 | if (i >= MAXLR - 1) | ||
| 95 | return; | ||
| 96 | |||
| 97 | /* Allocted a new one: */ | ||
| 98 | memcpy(&latency_record[i], lat, sizeof(struct latency_record)); | ||
| 99 | } | ||
| 100 | |||
| 101 | static inline void store_stacktrace(struct task_struct *tsk, struct latency_record *lat) | ||
| 102 | { | ||
| 103 | struct stack_trace trace; | ||
| 104 | |||
| 105 | memset(&trace, 0, sizeof(trace)); | ||
| 106 | trace.max_entries = LT_BACKTRACEDEPTH; | ||
| 107 | trace.entries = &lat->backtrace[0]; | ||
| 108 | trace.skip = 0; | ||
| 109 | save_stack_trace_tsk(tsk, &trace); | ||
| 110 | } | ||
| 111 | |||
| 112 | void __sched | ||
| 113 | account_scheduler_latency(struct task_struct *tsk, int usecs, int inter) | ||
| 114 | { | ||
| 115 | unsigned long flags; | ||
| 116 | int i, q; | ||
| 117 | struct latency_record lat; | ||
| 118 | |||
| 119 | if (!latencytop_enabled) | ||
| 120 | return; | ||
| 121 | |||
| 122 | /* Long interruptible waits are generally user requested... */ | ||
| 123 | if (inter && usecs > 5000) | ||
| 124 | return; | ||
| 125 | |||
| 126 | memset(&lat, 0, sizeof(lat)); | ||
| 127 | lat.count = 1; | ||
| 128 | lat.time = usecs; | ||
| 129 | lat.max = usecs; | ||
| 130 | store_stacktrace(tsk, &lat); | ||
| 131 | |||
| 132 | spin_lock_irqsave(&latency_lock, flags); | ||
| 133 | |||
| 134 | account_global_scheduler_latency(tsk, &lat); | ||
| 135 | |||
| 136 | /* | ||
| 137 | * short term hack; if we're > 32 we stop; future we recycle: | ||
| 138 | */ | ||
| 139 | tsk->latency_record_count++; | ||
| 140 | if (tsk->latency_record_count >= LT_SAVECOUNT) | ||
| 141 | goto out_unlock; | ||
| 142 | |||
| 143 | for (i = 0; i < LT_SAVECOUNT ; i++) { | ||
| 144 | struct latency_record *mylat; | ||
| 145 | int same = 1; | ||
| 146 | mylat = &tsk->latency_record[i]; | ||
| 147 | for (q = 0 ; q < LT_BACKTRACEDEPTH ; q++) { | ||
| 148 | if (mylat->backtrace[q] != | ||
| 149 | lat.backtrace[q]) | ||
| 150 | same = 0; | ||
| 151 | if (same && lat.backtrace[q] == 0) | ||
| 152 | break; | ||
| 153 | if (same && lat.backtrace[q] == ULONG_MAX) | ||
| 154 | break; | ||
| 155 | } | ||
| 156 | if (same) { | ||
| 157 | mylat->count++; | ||
| 158 | mylat->time += lat.time; | ||
| 159 | if (lat.time > mylat->max) | ||
| 160 | mylat->max = lat.time; | ||
| 161 | goto out_unlock; | ||
| 162 | } | ||
| 163 | } | ||
| 164 | |||
| 165 | /* Allocated a new one: */ | ||
| 166 | i = tsk->latency_record_count; | ||
| 167 | memcpy(&tsk->latency_record[i], &lat, sizeof(struct latency_record)); | ||
| 168 | |||
| 169 | out_unlock: | ||
| 170 | spin_unlock_irqrestore(&latency_lock, flags); | ||
| 171 | } | ||
| 172 | |||
| 173 | static int lstats_show(struct seq_file *m, void *v) | ||
| 174 | { | ||
| 175 | int i; | ||
| 176 | |||
| 177 | seq_puts(m, "Latency Top version : v0.1\n"); | ||
| 178 | |||
| 179 | for (i = 0; i < MAXLR; i++) { | ||
| 180 | if (latency_record[i].backtrace[0]) { | ||
| 181 | int q; | ||
| 182 | seq_printf(m, "%i %li %li ", | ||
| 183 | latency_record[i].count, | ||
| 184 | latency_record[i].time, | ||
| 185 | latency_record[i].max); | ||
| 186 | for (q = 0; q < LT_BACKTRACEDEPTH; q++) { | ||
| 187 | char sym[KSYM_NAME_LEN]; | ||
| 188 | char *c; | ||
| 189 | if (!latency_record[i].backtrace[q]) | ||
| 190 | break; | ||
| 191 | if (latency_record[i].backtrace[q] == ULONG_MAX) | ||
| 192 | break; | ||
| 193 | sprint_symbol(sym, latency_record[i].backtrace[q]); | ||
| 194 | c = strchr(sym, '+'); | ||
| 195 | if (c) | ||
| 196 | *c = 0; | ||
| 197 | seq_printf(m, "%s ", sym); | ||
| 198 | } | ||
| 199 | seq_printf(m, "\n"); | ||
| 200 | } | ||
| 201 | } | ||
| 202 | return 0; | ||
| 203 | } | ||
| 204 | |||
| 205 | static ssize_t | ||
| 206 | lstats_write(struct file *file, const char __user *buf, size_t count, | ||
| 207 | loff_t *offs) | ||
| 208 | { | ||
| 209 | clear_global_latency_tracing(); | ||
| 210 | |||
| 211 | return count; | ||
| 212 | } | ||
| 213 | |||
| 214 | static int lstats_open(struct inode *inode, struct file *filp) | ||
| 215 | { | ||
| 216 | return single_open(filp, lstats_show, NULL); | ||
| 217 | } | ||
| 218 | |||
| 219 | static struct file_operations lstats_fops = { | ||
| 220 | .open = lstats_open, | ||
| 221 | .read = seq_read, | ||
| 222 | .write = lstats_write, | ||
| 223 | .llseek = seq_lseek, | ||
| 224 | .release = single_release, | ||
| 225 | }; | ||
| 226 | |||
| 227 | static int __init init_lstats_procfs(void) | ||
| 228 | { | ||
| 229 | struct proc_dir_entry *pe; | ||
| 230 | |||
| 231 | pe = create_proc_entry("latency_stats", 0644, NULL); | ||
| 232 | if (!pe) | ||
| 233 | return -ENOMEM; | ||
| 234 | |||
| 235 | pe->proc_fops = &lstats_fops; | ||
| 236 | |||
| 237 | return 0; | ||
| 238 | } | ||
| 239 | __initcall(init_lstats_procfs); | ||
diff --git a/kernel/sched_fair.c b/kernel/sched_fair.c index 3dab1ff83c4f..1b3b40ad7c54 100644 --- a/kernel/sched_fair.c +++ b/kernel/sched_fair.c | |||
| @@ -20,6 +20,8 @@ | |||
| 20 | * Copyright (C) 2007 Red Hat, Inc., Peter Zijlstra <pzijlstr@redhat.com> | 20 | * Copyright (C) 2007 Red Hat, Inc., Peter Zijlstra <pzijlstr@redhat.com> |
| 21 | */ | 21 | */ |
| 22 | 22 | ||
| 23 | #include <linux/latencytop.h> | ||
| 24 | |||
| 23 | /* | 25 | /* |
| 24 | * Targeted preemption latency for CPU-bound tasks: | 26 | * Targeted preemption latency for CPU-bound tasks: |
| 25 | * (default: 20ms * (1 + ilog(ncpus)), units: nanoseconds) | 27 | * (default: 20ms * (1 + ilog(ncpus)), units: nanoseconds) |
| @@ -434,6 +436,7 @@ static void enqueue_sleeper(struct cfs_rq *cfs_rq, struct sched_entity *se) | |||
| 434 | #ifdef CONFIG_SCHEDSTATS | 436 | #ifdef CONFIG_SCHEDSTATS |
| 435 | if (se->sleep_start) { | 437 | if (se->sleep_start) { |
| 436 | u64 delta = rq_of(cfs_rq)->clock - se->sleep_start; | 438 | u64 delta = rq_of(cfs_rq)->clock - se->sleep_start; |
| 439 | struct task_struct *tsk = task_of(se); | ||
| 437 | 440 | ||
| 438 | if ((s64)delta < 0) | 441 | if ((s64)delta < 0) |
| 439 | delta = 0; | 442 | delta = 0; |
| @@ -443,9 +446,12 @@ static void enqueue_sleeper(struct cfs_rq *cfs_rq, struct sched_entity *se) | |||
| 443 | 446 | ||
| 444 | se->sleep_start = 0; | 447 | se->sleep_start = 0; |
| 445 | se->sum_sleep_runtime += delta; | 448 | se->sum_sleep_runtime += delta; |
| 449 | |||
| 450 | account_scheduler_latency(tsk, delta >> 10, 1); | ||
| 446 | } | 451 | } |
| 447 | if (se->block_start) { | 452 | if (se->block_start) { |
| 448 | u64 delta = rq_of(cfs_rq)->clock - se->block_start; | 453 | u64 delta = rq_of(cfs_rq)->clock - se->block_start; |
| 454 | struct task_struct *tsk = task_of(se); | ||
| 449 | 455 | ||
| 450 | if ((s64)delta < 0) | 456 | if ((s64)delta < 0) |
| 451 | delta = 0; | 457 | delta = 0; |
| @@ -462,11 +468,11 @@ static void enqueue_sleeper(struct cfs_rq *cfs_rq, struct sched_entity *se) | |||
| 462 | * time that the task spent sleeping: | 468 | * time that the task spent sleeping: |
| 463 | */ | 469 | */ |
| 464 | if (unlikely(prof_on == SLEEP_PROFILING)) { | 470 | if (unlikely(prof_on == SLEEP_PROFILING)) { |
| 465 | struct task_struct *tsk = task_of(se); | ||
| 466 | 471 | ||
| 467 | profile_hits(SLEEP_PROFILING, (void *)get_wchan(tsk), | 472 | profile_hits(SLEEP_PROFILING, (void *)get_wchan(tsk), |
| 468 | delta >> 20); | 473 | delta >> 20); |
| 469 | } | 474 | } |
| 475 | account_scheduler_latency(tsk, delta >> 10, 0); | ||
| 470 | } | 476 | } |
| 471 | #endif | 477 | #endif |
| 472 | } | 478 | } |
diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 3afbd25f43eb..5418ef61e16e 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c | |||
| @@ -81,6 +81,7 @@ extern int compat_log; | |||
| 81 | extern int maps_protect; | 81 | extern int maps_protect; |
| 82 | extern int sysctl_stat_interval; | 82 | extern int sysctl_stat_interval; |
| 83 | extern int audit_argv_kb; | 83 | extern int audit_argv_kb; |
| 84 | extern int latencytop_enabled; | ||
| 84 | 85 | ||
| 85 | /* Constants used for minimum and maximum */ | 86 | /* Constants used for minimum and maximum */ |
| 86 | #ifdef CONFIG_DETECT_SOFTLOCKUP | 87 | #ifdef CONFIG_DETECT_SOFTLOCKUP |
| @@ -416,6 +417,15 @@ static struct ctl_table kern_table[] = { | |||
| 416 | .proc_handler = &proc_dointvec_taint, | 417 | .proc_handler = &proc_dointvec_taint, |
| 417 | }, | 418 | }, |
| 418 | #endif | 419 | #endif |
| 420 | #ifdef CONFIG_LATENCYTOP | ||
| 421 | { | ||
| 422 | .procname = "latencytop", | ||
| 423 | .data = &latencytop_enabled, | ||
| 424 | .maxlen = sizeof(int), | ||
| 425 | .mode = 0644, | ||
| 426 | .proc_handler = &proc_dointvec, | ||
| 427 | }, | ||
| 428 | #endif | ||
| 419 | #ifdef CONFIG_SECURITY_CAPABILITIES | 429 | #ifdef CONFIG_SECURITY_CAPABILITIES |
| 420 | { | 430 | { |
| 421 | .procname = "cap-bound", | 431 | .procname = "cap-bound", |
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index a60109307d32..14fb355e3caa 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug | |||
| @@ -517,4 +517,18 @@ config FAULT_INJECTION_STACKTRACE_FILTER | |||
| 517 | help | 517 | help |
| 518 | Provide stacktrace filter for fault-injection capabilities | 518 | Provide stacktrace filter for fault-injection capabilities |
| 519 | 519 | ||
| 520 | config LATENCYTOP | ||
| 521 | bool "Latency measuring infrastructure" | ||
| 522 | select FRAME_POINTER if !MIPS | ||
| 523 | select KALLSYMS | ||
| 524 | select KALLSYMS_ALL | ||
| 525 | select STACKTRACE | ||
| 526 | select SCHEDSTATS | ||
| 527 | select SCHED_DEBUG | ||
| 528 | depends on X86 || X86_64 | ||
| 529 | help | ||
| 530 | Enable this option if you want to use the LatencyTOP tool | ||
| 531 | to find out which userspace is blocking on what kernel operations. | ||
| 532 | |||
| 533 | |||
| 520 | source "samples/Kconfig" | 534 | source "samples/Kconfig" |
