aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorHaavard Skinnemoen <hskinnemoen@atmel.com>2007-11-27 07:50:45 -0500
committerHaavard Skinnemoen <hskinnemoen@atmel.com>2008-01-25 02:31:39 -0500
commit13b54a50525a9685065684e1e11258d27dd27bdf (patch)
tree5cc2dbceb4efff1810e5425617ce9bc850e1e8cb /arch
parent6ea6dd93c9454cc9521134f907bc970d09f460e4 (diff)
[AVR32] Enable debugging only when needed
Keep track of processes being debugged (including the kernel itself) and turn the OCD system on and off as appropriate. Since enabling debugging turns off some optimizations in the CPU core, this fixes the issue that enabling KProbes support or simply running a program under gdbserver will reduce system performance significantly until the next reboot. The CPU performance will still be reduced for all processes while a process is being debugged, but this is a lot better than reducing the performance forever. Signed-off-by: Haavard Skinnemoen <hskinnemoen@atmel.com>
Diffstat (limited to 'arch')
-rw-r--r--arch/avr32/kernel/Makefile2
-rw-r--r--arch/avr32/kernel/kprobes.c5
-rw-r--r--arch/avr32/kernel/ocd.c163
-rw-r--r--arch/avr32/kernel/process.c5
-rw-r--r--arch/avr32/kernel/ptrace.c5
5 files changed, 171 insertions, 9 deletions
diff --git a/arch/avr32/kernel/Makefile b/arch/avr32/kernel/Makefile
index 2d6d48f35f69..bc224a4e39fe 100644
--- a/arch/avr32/kernel/Makefile
+++ b/arch/avr32/kernel/Makefile
@@ -6,7 +6,7 @@ extra-y := head.o vmlinux.lds
6 6
7obj-$(CONFIG_SUBARCH_AVR32B) += entry-avr32b.o 7obj-$(CONFIG_SUBARCH_AVR32B) += entry-avr32b.o
8obj-y += syscall_table.o syscall-stubs.o irq.o 8obj-y += syscall_table.o syscall-stubs.o irq.o
9obj-y += setup.o traps.o semaphore.o ptrace.o 9obj-y += setup.o traps.o semaphore.o ocd.o ptrace.o
10obj-y += signal.o sys_avr32.o process.o time.o 10obj-y += signal.o sys_avr32.o process.o time.o
11obj-y += init_task.o switch_to.o cpu.o 11obj-y += init_task.o switch_to.o cpu.o
12obj-$(CONFIG_MODULES) += module.o avr32_ksyms.o 12obj-$(CONFIG_MODULES) += module.o avr32_ksyms.o
diff --git a/arch/avr32/kernel/kprobes.c b/arch/avr32/kernel/kprobes.c
index 799ba89b07a8..f820e9f25520 100644
--- a/arch/avr32/kernel/kprobes.c
+++ b/arch/avr32/kernel/kprobes.c
@@ -48,6 +48,7 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
48void __kprobes arch_arm_kprobe(struct kprobe *p) 48void __kprobes arch_arm_kprobe(struct kprobe *p)
49{ 49{
50 pr_debug("arming kprobe at %p\n", p->addr); 50 pr_debug("arming kprobe at %p\n", p->addr);
51 ocd_enable(NULL);
51 *p->addr = BREAKPOINT_INSTRUCTION; 52 *p->addr = BREAKPOINT_INSTRUCTION;
52 flush_icache_range((unsigned long)p->addr, 53 flush_icache_range((unsigned long)p->addr,
53 (unsigned long)p->addr + sizeof(kprobe_opcode_t)); 54 (unsigned long)p->addr + sizeof(kprobe_opcode_t));
@@ -56,6 +57,7 @@ void __kprobes arch_arm_kprobe(struct kprobe *p)
56void __kprobes arch_disarm_kprobe(struct kprobe *p) 57void __kprobes arch_disarm_kprobe(struct kprobe *p)
57{ 58{
58 pr_debug("disarming kprobe at %p\n", p->addr); 59 pr_debug("disarming kprobe at %p\n", p->addr);
60 ocd_disable(NULL);
59 *p->addr = p->opcode; 61 *p->addr = p->opcode;
60 flush_icache_range((unsigned long)p->addr, 62 flush_icache_range((unsigned long)p->addr,
61 (unsigned long)p->addr + sizeof(kprobe_opcode_t)); 63 (unsigned long)p->addr + sizeof(kprobe_opcode_t));
@@ -260,9 +262,6 @@ int __kprobes longjmp_break_handler(struct kprobe *p, struct pt_regs *regs)
260 262
261int __init arch_init_kprobes(void) 263int __init arch_init_kprobes(void)
262{ 264{
263 printk("KPROBES: Enabling monitor mode (MM|DBE)...\n");
264 ocd_write(DC, (1 << OCD_DC_MM_BIT) | (1 << OCD_DC_DBE_BIT));
265
266 /* TODO: Register kretprobe trampoline */ 265 /* TODO: Register kretprobe trampoline */
267 return 0; 266 return 0;
268} 267}
diff --git a/arch/avr32/kernel/ocd.c b/arch/avr32/kernel/ocd.c
new file mode 100644
index 000000000000..c4f023294d75
--- /dev/null
+++ b/arch/avr32/kernel/ocd.c
@@ -0,0 +1,163 @@
1/*
2 * Copyright (C) 2007 Atmel Corporation
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 */
8#include <linux/init.h>
9#include <linux/sched.h>
10#include <linux/spinlock.h>
11
12#include <asm/ocd.h>
13
14static long ocd_count;
15static spinlock_t ocd_lock;
16
17/**
18 * ocd_enable - enable on-chip debugging
19 * @child: task to be debugged
20 *
21 * If @child is non-NULL, ocd_enable() first checks if debugging has
22 * already been enabled for @child, and if it has, does nothing.
23 *
24 * If @child is NULL (e.g. when debugging the kernel), or debugging
25 * has not already been enabled for it, ocd_enable() increments the
26 * reference count and enables the debugging hardware.
27 */
28void ocd_enable(struct task_struct *child)
29{
30 u32 dc;
31
32 if (child)
33 pr_debug("ocd_enable: child=%s [%u]\n",
34 child->comm, child->pid);
35 else
36 pr_debug("ocd_enable (no child)\n");
37
38 if (!child || !test_and_set_tsk_thread_flag(child, TIF_DEBUG)) {
39 spin_lock(&ocd_lock);
40 ocd_count++;
41 dc = ocd_read(DC);
42 dc |= (1 << OCD_DC_MM_BIT) | (1 << OCD_DC_DBE_BIT);
43 ocd_write(DC, dc);
44 spin_unlock(&ocd_lock);
45 }
46}
47
48/**
49 * ocd_disable - disable on-chip debugging
50 * @child: task that was being debugged, but isn't anymore
51 *
52 * If @child is non-NULL, ocd_disable() checks if debugging is enabled
53 * for @child, and if it isn't, does nothing.
54 *
55 * If @child is NULL (e.g. when debugging the kernel), or debugging is
56 * enabled, ocd_disable() decrements the reference count, and if it
57 * reaches zero, disables the debugging hardware.
58 */
59void ocd_disable(struct task_struct *child)
60{
61 u32 dc;
62
63 if (!child)
64 pr_debug("ocd_disable (no child)\n");
65 else if (test_tsk_thread_flag(child, TIF_DEBUG))
66 pr_debug("ocd_disable: child=%s [%u]\n",
67 child->comm, child->pid);
68
69 if (!child || test_and_clear_tsk_thread_flag(child, TIF_DEBUG)) {
70 spin_lock(&ocd_lock);
71 ocd_count--;
72
73 WARN_ON(ocd_count < 0);
74
75 if (ocd_count <= 0) {
76 dc = ocd_read(DC);
77 dc &= ~((1 << OCD_DC_MM_BIT) | (1 << OCD_DC_DBE_BIT));
78 ocd_write(DC, dc);
79 }
80 spin_unlock(&ocd_lock);
81 }
82}
83
84#ifdef CONFIG_DEBUG_FS
85#include <linux/debugfs.h>
86#include <linux/module.h>
87
88static struct dentry *ocd_debugfs_root;
89static struct dentry *ocd_debugfs_DC;
90static struct dentry *ocd_debugfs_DS;
91static struct dentry *ocd_debugfs_count;
92
93static u64 ocd_DC_get(void *data)
94{
95 return ocd_read(DC);
96}
97static void ocd_DC_set(void *data, u64 val)
98{
99 ocd_write(DC, val);
100}
101DEFINE_SIMPLE_ATTRIBUTE(fops_DC, ocd_DC_get, ocd_DC_set, "0x%08llx\n");
102
103static u64 ocd_DS_get(void *data)
104{
105 return ocd_read(DS);
106}
107DEFINE_SIMPLE_ATTRIBUTE(fops_DS, ocd_DS_get, NULL, "0x%08llx\n");
108
109static u64 ocd_count_get(void *data)
110{
111 return ocd_count;
112}
113DEFINE_SIMPLE_ATTRIBUTE(fops_count, ocd_count_get, NULL, "%lld\n");
114
115static void ocd_debugfs_init(void)
116{
117 struct dentry *root;
118
119 root = debugfs_create_dir("ocd", NULL);
120 if (IS_ERR(root) || !root)
121 goto err_root;
122 ocd_debugfs_root = root;
123
124 ocd_debugfs_DC = debugfs_create_file("DC", S_IRUSR | S_IWUSR,
125 root, NULL, &fops_DC);
126 if (!ocd_debugfs_DC)
127 goto err_DC;
128
129 ocd_debugfs_DS = debugfs_create_file("DS", S_IRUSR, root,
130 NULL, &fops_DS);
131 if (!ocd_debugfs_DS)
132 goto err_DS;
133
134 ocd_debugfs_count = debugfs_create_file("count", S_IRUSR, root,
135 NULL, &fops_count);
136 if (!ocd_debugfs_count)
137 goto err_count;
138
139 return;
140
141err_count:
142 debugfs_remove(ocd_debugfs_DS);
143err_DS:
144 debugfs_remove(ocd_debugfs_DC);
145err_DC:
146 debugfs_remove(ocd_debugfs_root);
147err_root:
148 printk(KERN_WARNING "OCD: Failed to create debugfs entries\n");
149}
150#else
151static inline void ocd_debugfs_init(void)
152{
153
154}
155#endif
156
157static int __init ocd_init(void)
158{
159 spin_lock_init(&ocd_lock);
160 ocd_debugfs_init();
161 return 0;
162}
163arch_initcall(ocd_init);
diff --git a/arch/avr32/kernel/process.c b/arch/avr32/kernel/process.c
index 9d6dac8af7a2..eaaa69bbdc38 100644
--- a/arch/avr32/kernel/process.c
+++ b/arch/avr32/kernel/process.c
@@ -103,7 +103,7 @@ EXPORT_SYMBOL(kernel_thread);
103 */ 103 */
104void exit_thread(void) 104void exit_thread(void)
105{ 105{
106 /* nothing to do */ 106 ocd_disable(current);
107} 107}
108 108
109void flush_thread(void) 109void flush_thread(void)
@@ -345,6 +345,9 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long usp,
345 p->thread.cpu_context.ksp = (unsigned long)childregs; 345 p->thread.cpu_context.ksp = (unsigned long)childregs;
346 p->thread.cpu_context.pc = (unsigned long)ret_from_fork; 346 p->thread.cpu_context.pc = (unsigned long)ret_from_fork;
347 347
348 if ((clone_flags & CLONE_PTRACE) && test_thread_flag(TIF_DEBUG))
349 ocd_enable(p);
350
348 return 0; 351 return 0;
349} 352}
350 353
diff --git a/arch/avr32/kernel/ptrace.c b/arch/avr32/kernel/ptrace.c
index 002369e44093..1fed38fcf594 100644
--- a/arch/avr32/kernel/ptrace.c
+++ b/arch/avr32/kernel/ptrace.c
@@ -58,6 +58,7 @@ void ptrace_disable(struct task_struct *child)
58{ 58{
59 clear_tsk_thread_flag(child, TIF_SINGLE_STEP); 59 clear_tsk_thread_flag(child, TIF_SINGLE_STEP);
60 clear_tsk_thread_flag(child, TIF_BREAKPOINT); 60 clear_tsk_thread_flag(child, TIF_BREAKPOINT);
61 ocd_disable(child);
61} 62}
62 63
63/* 64/*
@@ -144,10 +145,6 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)
144{ 145{
145 int ret; 146 int ret;
146 147
147 pr_debug("ptrace: Enabling monitor mode...\n");
148 ocd_write(DC, ocd_read(DC) | (1 << OCD_DC_MM_BIT)
149 | (1 << OCD_DC_DBE_BIT));
150
151 switch (request) { 148 switch (request) {
152 /* Read the word at location addr in the child process */ 149 /* Read the word at location addr in the child process */
153 case PTRACE_PEEKTEXT: 150 case PTRACE_PEEKTEXT: