aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFrederic Weisbecker <fweisbec@gmail.com>2010-04-11 12:55:56 -0400
committerFrederic Weisbecker <fweisbec@gmail.com>2010-04-30 22:32:11 -0400
commit0102752e4c9e0655b39734550d4c35327954f7f9 (patch)
tree04c23f88e57c8e804d144434c60b91879e83648d
parentb2812d031dea86926e9c10f7714af33ac2f6b43d (diff)
hw-breakpoints: Separate constraint space for data and instruction breakpoints
There are two outstanding fashions for archs to implement hardware breakpoints. The first is to separate breakpoint address pattern definition space between data and instruction breakpoints. We then have typically distinct instruction address breakpoint registers and data address breakpoint registers, delivered with separate control registers for data and instruction breakpoints as well. This is the case of PowerPc and ARM for example. The second consists in having merged breakpoint address space definition between data and instruction breakpoint. Address registers can host either instruction or data address and the access mode for the breakpoint is defined in a control register. This is the case of x86 and Super H. This patch adds a new CONFIG_HAVE_MIXED_BREAKPOINTS_REGS config that archs can select if they belong to the second case. Those will have their slot allocation merged for instructions and data breakpoints. The others will have a separate slot tracking between data and instruction breakpoints. Signed-off-by: Frederic Weisbecker <fweisbec@gmail.com> Acked-by: Paul Mundt <lethal@linux-sh.org> Cc: Will Deacon <will.deacon@arm.com> Cc: Mahesh Salgaonkar <mahesh@linux.vnet.ibm.com> Cc: K. Prasad <prasad@linux.vnet.ibm.com> Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org> Cc: Paul Mackerras <paulus@samba.org> Cc: Ingo Molnar <mingo@elte.hu>
-rw-r--r--arch/Kconfig11
-rw-r--r--arch/sh/Kconfig1
-rw-r--r--arch/x86/Kconfig1
-rw-r--r--include/linux/hw_breakpoint.h9
-rw-r--r--kernel/hw_breakpoint.c86
5 files changed, 78 insertions, 30 deletions
diff --git a/arch/Kconfig b/arch/Kconfig
index f06010fb4838..acda512da2e2 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -137,6 +137,17 @@ config HAVE_HW_BREAKPOINT
137 bool 137 bool
138 depends on PERF_EVENTS 138 depends on PERF_EVENTS
139 139
140config HAVE_MIXED_BREAKPOINTS_REGS
141 bool
142 depends on HAVE_HW_BREAKPOINT
143 help
144 Depending on the arch implementation of hardware breakpoints,
145 some of them have separate registers for data and instruction
146 breakpoints addresses, others have mixed registers to store
147 them but define the access type in a control register.
148 Select this option if your arch implements breakpoints under the
149 latter fashion.
150
140config HAVE_USER_RETURN_NOTIFIER 151config HAVE_USER_RETURN_NOTIFIER
141 bool 152 bool
142 153
diff --git a/arch/sh/Kconfig b/arch/sh/Kconfig
index 8d90564c2bcf..e6d8ab5cfa9d 100644
--- a/arch/sh/Kconfig
+++ b/arch/sh/Kconfig
@@ -44,6 +44,7 @@ config SUPERH32
44 select HAVE_FUNCTION_GRAPH_TRACER 44 select HAVE_FUNCTION_GRAPH_TRACER
45 select HAVE_ARCH_KGDB 45 select HAVE_ARCH_KGDB
46 select HAVE_HW_BREAKPOINT 46 select HAVE_HW_BREAKPOINT
47 select HAVE_MIXED_BREAKPOINTS_REGS
47 select PERF_EVENTS if HAVE_HW_BREAKPOINT 48 select PERF_EVENTS if HAVE_HW_BREAKPOINT
48 select ARCH_HIBERNATION_POSSIBLE if MMU 49 select ARCH_HIBERNATION_POSSIBLE if MMU
49 50
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 97a95dfd1181..01177dcbe261 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -53,6 +53,7 @@ config X86
53 select HAVE_KERNEL_LZMA 53 select HAVE_KERNEL_LZMA
54 select HAVE_KERNEL_LZO 54 select HAVE_KERNEL_LZO
55 select HAVE_HW_BREAKPOINT 55 select HAVE_HW_BREAKPOINT
56 select HAVE_MIXED_BREAKPOINTS_REGS
56 select PERF_EVENTS 57 select PERF_EVENTS
57 select ANON_INODES 58 select ANON_INODES
58 select HAVE_ARCH_KMEMCHECK 59 select HAVE_ARCH_KMEMCHECK
diff --git a/include/linux/hw_breakpoint.h b/include/linux/hw_breakpoint.h
index a0aa5a9cfb0e..7e8899093098 100644
--- a/include/linux/hw_breakpoint.h
+++ b/include/linux/hw_breakpoint.h
@@ -9,9 +9,12 @@ enum {
9}; 9};
10 10
11enum { 11enum {
12 HW_BREAKPOINT_R = 1, 12 HW_BREAKPOINT_EMPTY = 0,
13 HW_BREAKPOINT_W = 2, 13 HW_BREAKPOINT_R = 1,
14 HW_BREAKPOINT_X = 4, 14 HW_BREAKPOINT_W = 2,
15 HW_BREAKPOINT_RW = HW_BREAKPOINT_R | HW_BREAKPOINT_W,
16 HW_BREAKPOINT_X = 4,
17 HW_BREAKPOINT_INVALID = HW_BREAKPOINT_RW | HW_BREAKPOINT_X,
15}; 18};
16 19
17#ifdef __KERNEL__ 20#ifdef __KERNEL__
diff --git a/kernel/hw_breakpoint.c b/kernel/hw_breakpoint.c
index 89e8a050c43a..8ead1345e33b 100644
--- a/kernel/hw_breakpoint.c
+++ b/kernel/hw_breakpoint.c
@@ -45,18 +45,28 @@
45 45
46#include <linux/hw_breakpoint.h> 46#include <linux/hw_breakpoint.h>
47 47
48enum bp_type_idx {
49 TYPE_INST = 0,
50#ifdef CONFIG_HAVE_MIXED_BREAKPOINTS_REGS
51 TYPE_DATA = 0,
52#else
53 TYPE_DATA = 1,
54#endif
55 TYPE_MAX
56};
57
48/* 58/*
49 * Constraints data 59 * Constraints data
50 */ 60 */
51 61
52/* Number of pinned cpu breakpoints in a cpu */ 62/* Number of pinned cpu breakpoints in a cpu */
53static DEFINE_PER_CPU(unsigned int, nr_cpu_bp_pinned); 63static DEFINE_PER_CPU(unsigned int, nr_cpu_bp_pinned[TYPE_MAX]);
54 64
55/* Number of pinned task breakpoints in a cpu */ 65/* Number of pinned task breakpoints in a cpu */
56static DEFINE_PER_CPU(unsigned int, nr_task_bp_pinned[HBP_NUM]); 66static DEFINE_PER_CPU(unsigned int, nr_task_bp_pinned[TYPE_MAX][HBP_NUM]);
57 67
58/* Number of non-pinned cpu/task breakpoints in a cpu */ 68/* Number of non-pinned cpu/task breakpoints in a cpu */
59static DEFINE_PER_CPU(unsigned int, nr_bp_flexible); 69static DEFINE_PER_CPU(unsigned int, nr_bp_flexible[TYPE_MAX]);
60 70
61/* Gather the number of total pinned and un-pinned bp in a cpuset */ 71/* Gather the number of total pinned and un-pinned bp in a cpuset */
62struct bp_busy_slots { 72struct bp_busy_slots {
@@ -67,14 +77,22 @@ struct bp_busy_slots {
67/* Serialize accesses to the above constraints */ 77/* Serialize accesses to the above constraints */
68static DEFINE_MUTEX(nr_bp_mutex); 78static DEFINE_MUTEX(nr_bp_mutex);
69 79
80static inline enum bp_type_idx find_slot_idx(struct perf_event *bp)
81{
82 if (bp->attr.bp_type & HW_BREAKPOINT_RW)
83 return TYPE_DATA;
84
85 return TYPE_INST;
86}
87
70/* 88/*
71 * Report the maximum number of pinned breakpoints a task 89 * Report the maximum number of pinned breakpoints a task
72 * have in this cpu 90 * have in this cpu
73 */ 91 */
74static unsigned int max_task_bp_pinned(int cpu) 92static unsigned int max_task_bp_pinned(int cpu, enum bp_type_idx type)
75{ 93{
76 int i; 94 int i;
77 unsigned int *tsk_pinned = per_cpu(nr_task_bp_pinned, cpu); 95 unsigned int *tsk_pinned = per_cpu(nr_task_bp_pinned[type], cpu);
78 96
79 for (i = HBP_NUM -1; i >= 0; i--) { 97 for (i = HBP_NUM -1; i >= 0; i--) {
80 if (tsk_pinned[i] > 0) 98 if (tsk_pinned[i] > 0)
@@ -84,7 +102,7 @@ static unsigned int max_task_bp_pinned(int cpu)
84 return 0; 102 return 0;
85} 103}
86 104
87static int task_bp_pinned(struct task_struct *tsk) 105static int task_bp_pinned(struct task_struct *tsk, enum bp_type_idx type)
88{ 106{
89 struct perf_event_context *ctx = tsk->perf_event_ctxp; 107 struct perf_event_context *ctx = tsk->perf_event_ctxp;
90 struct list_head *list; 108 struct list_head *list;
@@ -105,7 +123,8 @@ static int task_bp_pinned(struct task_struct *tsk)
105 */ 123 */
106 list_for_each_entry(bp, list, event_entry) { 124 list_for_each_entry(bp, list, event_entry) {
107 if (bp->attr.type == PERF_TYPE_BREAKPOINT) 125 if (bp->attr.type == PERF_TYPE_BREAKPOINT)
108 count++; 126 if (find_slot_idx(bp) == type)
127 count++;
109 } 128 }
110 129
111 raw_spin_unlock_irqrestore(&ctx->lock, flags); 130 raw_spin_unlock_irqrestore(&ctx->lock, flags);
@@ -118,18 +137,19 @@ static int task_bp_pinned(struct task_struct *tsk)
118 * a given cpu (cpu > -1) or in all of them (cpu = -1). 137 * a given cpu (cpu > -1) or in all of them (cpu = -1).
119 */ 138 */
120static void 139static void
121fetch_bp_busy_slots(struct bp_busy_slots *slots, struct perf_event *bp) 140fetch_bp_busy_slots(struct bp_busy_slots *slots, struct perf_event *bp,
141 enum bp_type_idx type)
122{ 142{
123 int cpu = bp->cpu; 143 int cpu = bp->cpu;
124 struct task_struct *tsk = bp->ctx->task; 144 struct task_struct *tsk = bp->ctx->task;
125 145
126 if (cpu >= 0) { 146 if (cpu >= 0) {
127 slots->pinned = per_cpu(nr_cpu_bp_pinned, cpu); 147 slots->pinned = per_cpu(nr_cpu_bp_pinned[type], cpu);
128 if (!tsk) 148 if (!tsk)
129 slots->pinned += max_task_bp_pinned(cpu); 149 slots->pinned += max_task_bp_pinned(cpu, type);
130 else 150 else
131 slots->pinned += task_bp_pinned(tsk); 151 slots->pinned += task_bp_pinned(tsk, type);
132 slots->flexible = per_cpu(nr_bp_flexible, cpu); 152 slots->flexible = per_cpu(nr_bp_flexible[type], cpu);
133 153
134 return; 154 return;
135 } 155 }
@@ -137,16 +157,16 @@ fetch_bp_busy_slots(struct bp_busy_slots *slots, struct perf_event *bp)
137 for_each_online_cpu(cpu) { 157 for_each_online_cpu(cpu) {
138 unsigned int nr; 158 unsigned int nr;
139 159
140 nr = per_cpu(nr_cpu_bp_pinned, cpu); 160 nr = per_cpu(nr_cpu_bp_pinned[type], cpu);
141 if (!tsk) 161 if (!tsk)
142 nr += max_task_bp_pinned(cpu); 162 nr += max_task_bp_pinned(cpu, type);
143 else 163 else
144 nr += task_bp_pinned(tsk); 164 nr += task_bp_pinned(tsk, type);
145 165
146 if (nr > slots->pinned) 166 if (nr > slots->pinned)
147 slots->pinned = nr; 167 slots->pinned = nr;
148 168
149 nr = per_cpu(nr_bp_flexible, cpu); 169 nr = per_cpu(nr_bp_flexible[type], cpu);
150 170
151 if (nr > slots->flexible) 171 if (nr > slots->flexible)
152 slots->flexible = nr; 172 slots->flexible = nr;
@@ -156,14 +176,15 @@ fetch_bp_busy_slots(struct bp_busy_slots *slots, struct perf_event *bp)
156/* 176/*
157 * Add a pinned breakpoint for the given task in our constraint table 177 * Add a pinned breakpoint for the given task in our constraint table
158 */ 178 */
159static void toggle_bp_task_slot(struct task_struct *tsk, int cpu, bool enable) 179static void toggle_bp_task_slot(struct task_struct *tsk, int cpu, bool enable,
180 enum bp_type_idx type)
160{ 181{
161 unsigned int *tsk_pinned; 182 unsigned int *tsk_pinned;
162 int count = 0; 183 int count = 0;
163 184
164 count = task_bp_pinned(tsk); 185 count = task_bp_pinned(tsk, type);
165 186
166 tsk_pinned = per_cpu(nr_task_bp_pinned, cpu); 187 tsk_pinned = per_cpu(nr_task_bp_pinned[type], cpu);
167 if (enable) { 188 if (enable) {
168 tsk_pinned[count]++; 189 tsk_pinned[count]++;
169 if (count > 0) 190 if (count > 0)
@@ -178,7 +199,8 @@ static void toggle_bp_task_slot(struct task_struct *tsk, int cpu, bool enable)
178/* 199/*
179 * Add/remove the given breakpoint in our constraint table 200 * Add/remove the given breakpoint in our constraint table
180 */ 201 */
181static void toggle_bp_slot(struct perf_event *bp, bool enable) 202static void
203toggle_bp_slot(struct perf_event *bp, bool enable, enum bp_type_idx type)
182{ 204{
183 int cpu = bp->cpu; 205 int cpu = bp->cpu;
184 struct task_struct *tsk = bp->ctx->task; 206 struct task_struct *tsk = bp->ctx->task;
@@ -186,20 +208,20 @@ static void toggle_bp_slot(struct perf_event *bp, bool enable)
186 /* Pinned counter task profiling */ 208 /* Pinned counter task profiling */
187 if (tsk) { 209 if (tsk) {
188 if (cpu >= 0) { 210 if (cpu >= 0) {
189 toggle_bp_task_slot(tsk, cpu, enable); 211 toggle_bp_task_slot(tsk, cpu, enable, type);
190 return; 212 return;
191 } 213 }
192 214
193 for_each_online_cpu(cpu) 215 for_each_online_cpu(cpu)
194 toggle_bp_task_slot(tsk, cpu, enable); 216 toggle_bp_task_slot(tsk, cpu, enable, type);
195 return; 217 return;
196 } 218 }
197 219
198 /* Pinned counter cpu profiling */ 220 /* Pinned counter cpu profiling */
199 if (enable) 221 if (enable)
200 per_cpu(nr_cpu_bp_pinned, bp->cpu)++; 222 per_cpu(nr_cpu_bp_pinned[type], bp->cpu)++;
201 else 223 else
202 per_cpu(nr_cpu_bp_pinned, bp->cpu)--; 224 per_cpu(nr_cpu_bp_pinned[type], bp->cpu)--;
203} 225}
204 226
205/* 227/*
@@ -246,14 +268,21 @@ static void toggle_bp_slot(struct perf_event *bp, bool enable)
246static int __reserve_bp_slot(struct perf_event *bp) 268static int __reserve_bp_slot(struct perf_event *bp)
247{ 269{
248 struct bp_busy_slots slots = {0}; 270 struct bp_busy_slots slots = {0};
271 enum bp_type_idx type;
249 272
250 fetch_bp_busy_slots(&slots, bp); 273 /* Basic checks */
274 if (bp->attr.bp_type == HW_BREAKPOINT_EMPTY ||
275 bp->attr.bp_type == HW_BREAKPOINT_INVALID)
276 return -EINVAL;
277
278 type = find_slot_idx(bp);
279 fetch_bp_busy_slots(&slots, bp, type);
251 280
252 /* Flexible counters need to keep at least one slot */ 281 /* Flexible counters need to keep at least one slot */
253 if (slots.pinned + (!!slots.flexible) == HBP_NUM) 282 if (slots.pinned + (!!slots.flexible) == HBP_NUM)
254 return -ENOSPC; 283 return -ENOSPC;
255 284
256 toggle_bp_slot(bp, true); 285 toggle_bp_slot(bp, true, type);
257 286
258 return 0; 287 return 0;
259} 288}
@@ -273,7 +302,10 @@ int reserve_bp_slot(struct perf_event *bp)
273 302
274static void __release_bp_slot(struct perf_event *bp) 303static void __release_bp_slot(struct perf_event *bp)
275{ 304{
276 toggle_bp_slot(bp, false); 305 enum bp_type_idx type;
306
307 type = find_slot_idx(bp);
308 toggle_bp_slot(bp, false, type);
277} 309}
278 310
279void release_bp_slot(struct perf_event *bp) 311void release_bp_slot(struct perf_event *bp)