Commit f93a20541134fa767e8dc4eb32e956d30b9f6b92

Authored by Frederic Weisbecker
1 parent 0102752e4c

hw-breakpoints: Handle breakpoint weight in allocation constraints

Depending on their nature and on what an arch supports, breakpoints
may consume more than one address register. For example a simple
absolute address match usually only requires one address register.
But an address range match may consume two registers.

Currently our slot allocation constraints, that tend to reflect the
limited arch's resources, always consider that a breakpoint consumes
one slot.

Then provide a way for archs to tell us the weight of a breakpoint
through a new hw_breakpoint_weight() helper. This weight will be
computed against the generic allocation constraints instead of
a constant value.

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>

Showing 1 changed file with 45 additions and 18 deletions Side-by-side Diff

kernel/hw_breakpoint.c
... ... @@ -77,6 +77,11 @@
77 77 /* Serialize accesses to the above constraints */
78 78 static DEFINE_MUTEX(nr_bp_mutex);
79 79  
  80 +__weak int hw_breakpoint_weight(struct perf_event *bp)
  81 +{
  82 + return 1;
  83 +}
  84 +
80 85 static inline enum bp_type_idx find_slot_idx(struct perf_event *bp)
81 86 {
82 87 if (bp->attr.bp_type & HW_BREAKPOINT_RW)
... ... @@ -124,7 +129,7 @@
124 129 list_for_each_entry(bp, list, event_entry) {
125 130 if (bp->attr.type == PERF_TYPE_BREAKPOINT)
126 131 if (find_slot_idx(bp) == type)
127   - count++;
  132 + count += hw_breakpoint_weight(bp);
128 133 }
129 134  
130 135 raw_spin_unlock_irqrestore(&ctx->lock, flags);
131 136  
132 137  
133 138  
134 139  
135 140  
... ... @@ -174,25 +179,40 @@
174 179 }
175 180  
176 181 /*
  182 + * For now, continue to consider flexible as pinned, until we can
  183 + * ensure no flexible event can ever be scheduled before a pinned event
  184 + * in a same cpu.
  185 + */
  186 +static void
  187 +fetch_this_slot(struct bp_busy_slots *slots, int weight)
  188 +{
  189 + slots->pinned += weight;
  190 +}
  191 +
  192 +/*
177 193 * Add a pinned breakpoint for the given task in our constraint table
178 194 */
179 195 static void toggle_bp_task_slot(struct task_struct *tsk, int cpu, bool enable,
180   - enum bp_type_idx type)
  196 + enum bp_type_idx type, int weight)
181 197 {
182 198 unsigned int *tsk_pinned;
183   - int count = 0;
  199 + int old_count = 0;
  200 + int old_idx = 0;
  201 + int idx = 0;
184 202  
185   - count = task_bp_pinned(tsk, type);
  203 + old_count = task_bp_pinned(tsk, type);
  204 + old_idx = old_count - 1;
  205 + idx = old_idx + weight;
186 206  
187 207 tsk_pinned = per_cpu(nr_task_bp_pinned[type], cpu);
188 208 if (enable) {
189   - tsk_pinned[count]++;
190   - if (count > 0)
191   - tsk_pinned[count-1]--;
  209 + tsk_pinned[idx]++;
  210 + if (old_count > 0)
  211 + tsk_pinned[old_idx]--;
192 212 } else {
193   - tsk_pinned[count]--;
194   - if (count > 0)
195   - tsk_pinned[count-1]++;
  213 + tsk_pinned[idx]--;
  214 + if (old_count > 0)
  215 + tsk_pinned[old_idx]++;
196 216 }
197 217 }
198 218  
... ... @@ -200,7 +220,8 @@
200 220 * Add/remove the given breakpoint in our constraint table
201 221 */
202 222 static void
203   -toggle_bp_slot(struct perf_event *bp, bool enable, enum bp_type_idx type)
  223 +toggle_bp_slot(struct perf_event *bp, bool enable, enum bp_type_idx type,
  224 + int weight)
204 225 {
205 226 int cpu = bp->cpu;
206 227 struct task_struct *tsk = bp->ctx->task;
207 228  
208 229  
209 230  
... ... @@ -208,20 +229,20 @@
208 229 /* Pinned counter task profiling */
209 230 if (tsk) {
210 231 if (cpu >= 0) {
211   - toggle_bp_task_slot(tsk, cpu, enable, type);
  232 + toggle_bp_task_slot(tsk, cpu, enable, type, weight);
212 233 return;
213 234 }
214 235  
215 236 for_each_online_cpu(cpu)
216   - toggle_bp_task_slot(tsk, cpu, enable, type);
  237 + toggle_bp_task_slot(tsk, cpu, enable, type, weight);
217 238 return;
218 239 }
219 240  
220 241 /* Pinned counter cpu profiling */
221 242 if (enable)
222   - per_cpu(nr_cpu_bp_pinned[type], bp->cpu)++;
  243 + per_cpu(nr_cpu_bp_pinned[type], bp->cpu) += weight;
223 244 else
224   - per_cpu(nr_cpu_bp_pinned[type], bp->cpu)--;
  245 + per_cpu(nr_cpu_bp_pinned[type], bp->cpu) -= weight;
225 246 }
226 247  
227 248 /*
... ... @@ -269,6 +290,7 @@
269 290 {
270 291 struct bp_busy_slots slots = {0};
271 292 enum bp_type_idx type;
  293 + int weight;
272 294  
273 295 /* Basic checks */
274 296 if (bp->attr.bp_type == HW_BREAKPOINT_EMPTY ||
275 297  
276 298  
277 299  
... ... @@ -276,13 +298,16 @@
276 298 return -EINVAL;
277 299  
278 300 type = find_slot_idx(bp);
  301 + weight = hw_breakpoint_weight(bp);
  302 +
279 303 fetch_bp_busy_slots(&slots, bp, type);
  304 + fetch_this_slot(&slots, weight);
280 305  
281 306 /* Flexible counters need to keep at least one slot */
282   - if (slots.pinned + (!!slots.flexible) == HBP_NUM)
  307 + if (slots.pinned + (!!slots.flexible) > HBP_NUM)
283 308 return -ENOSPC;
284 309  
285   - toggle_bp_slot(bp, true, type);
  310 + toggle_bp_slot(bp, true, type, weight);
286 311  
287 312 return 0;
288 313 }
289 314  
... ... @@ -303,9 +328,11 @@
303 328 static void __release_bp_slot(struct perf_event *bp)
304 329 {
305 330 enum bp_type_idx type;
  331 + int weight;
306 332  
307 333 type = find_slot_idx(bp);
308   - toggle_bp_slot(bp, false, type);
  334 + weight = hw_breakpoint_weight(bp);
  335 + toggle_bp_slot(bp, false, type, weight);
309 336 }
310 337  
311 338 void release_bp_slot(struct perf_event *bp)