aboutsummaryrefslogtreecommitdiffstats
path: root/arch/avr32/kernel/ocd.c
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/avr32/kernel/ocd.c
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/avr32/kernel/ocd.c')
-rw-r--r--arch/avr32/kernel/ocd.c163
1 files changed, 163 insertions, 0 deletions
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);