Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

hw-breakpoints: Get the number of available registers on boot dynamically

The breakpoint generic layer assumes that archs always know in advance
the static number of address registers available to host breakpoints
through the HBP_NUM macro.

However this is not true for every archs. For example Arm needs to get
this information dynamically to handle the compatiblity between
different versions.

To solve this, this patch proposes to drop the static HBP_NUM macro
and let the arch provide the number of available slots through a
new hw_breakpoint_slots() function. For archs that have
CONFIG_HAVE_MIXED_BREAKPOINTS_REGS selected, it will be called once
as the number of registers fits for instruction and data breakpoints
together.
For the others it will be called first to get the number of
instruction breakpoint registers and another time to get the
data breakpoint registers, the targeted type is given as a
parameter of hw_breakpoint_slots().

Reported-by: Will Deacon <will.deacon@arm.com>
Signed-off-by: Frederic Weisbecker <fweisbec@gmail.com>
Acked-by: Paul Mundt <lethal@linux-sh.org>
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: Jason Wessel <jason.wessel@windriver.com>
Cc: Ingo Molnar <mingo@elte.hu>

+68 -31
+5
arch/sh/include/asm/hw_breakpoint.h
··· 46 46 /* Maximum number of UBC channels */ 47 47 #define HBP_NUM 2 48 48 49 + static inline int hw_breakpoint_slots(int type) 50 + { 51 + return HBP_NUM; 52 + } 53 + 49 54 /* arch/sh/kernel/hw_breakpoint.c */ 50 55 extern int arch_check_bp_in_kernelspace(struct perf_event *bp); 51 56 extern int arch_validate_hwbkpt_settings(struct perf_event *bp);
+5
arch/x86/include/asm/hw_breakpoint.h
··· 41 41 /* Total number of available HW breakpoint registers */ 42 42 #define HBP_NUM 4 43 43 44 + static inline int hw_breakpoint_slots(int type) 45 + { 46 + return HBP_NUM; 47 + } 48 + 44 49 struct perf_event; 45 50 struct pmu; 46 51
+10
include/linux/hw_breakpoint.h
··· 17 17 HW_BREAKPOINT_INVALID = HW_BREAKPOINT_RW | HW_BREAKPOINT_X, 18 18 }; 19 19 20 + enum bp_type_idx { 21 + TYPE_INST = 0, 22 + #ifdef CONFIG_HAVE_MIXED_BREAKPOINTS_REGS 23 + TYPE_DATA = 0, 24 + #else 25 + TYPE_DATA = 1, 26 + #endif 27 + TYPE_MAX 28 + }; 29 + 20 30 #ifdef __KERNEL__ 21 31 22 32 #include <linux/perf_event.h>
+41 -12
kernel/hw_breakpoint.c
··· 40 40 #include <linux/percpu.h> 41 41 #include <linux/sched.h> 42 42 #include <linux/init.h> 43 + #include <linux/slab.h> 43 44 #include <linux/cpu.h> 44 45 #include <linux/smp.h> 45 46 46 47 #include <linux/hw_breakpoint.h> 47 48 48 - enum 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 49 58 50 /* 59 51 * Constraints data ··· 55 63 static DEFINE_PER_CPU(unsigned int, nr_cpu_bp_pinned[TYPE_MAX]); 56 64 57 65 /* Number of pinned task breakpoints in a cpu */ 58 - static DEFINE_PER_CPU(unsigned int, nr_task_bp_pinned[TYPE_MAX][HBP_NUM]); 66 + static DEFINE_PER_CPU(unsigned int, *nr_task_bp_pinned[TYPE_MAX]); 59 67 60 68 /* Number of non-pinned cpu/task breakpoints in a cpu */ 61 69 static DEFINE_PER_CPU(unsigned int, nr_bp_flexible[TYPE_MAX]); 70 + 71 + static int nr_slots[TYPE_MAX]; 72 + 73 + static int constraints_initialized; 62 74 63 75 /* Gather the number of total pinned and un-pinned bp in a cpuset */ 64 76 struct bp_busy_slots { ··· 95 99 int i; 96 100 unsigned int *tsk_pinned = per_cpu(nr_task_bp_pinned[type], cpu); 97 101 98 - for (i = HBP_NUM -1; i >= 0; i--) { 102 + for (i = nr_slots[type] - 1; i >= 0; i--) { 99 103 if (tsk_pinned[i] > 0) 100 104 return i + 1; 101 105 } ··· 288 292 enum bp_type_idx type; 289 293 int weight; 290 294 295 + /* We couldn't initialize breakpoint constraints on boot */ 296 + if (!constraints_initialized) 297 + return -ENOMEM; 298 + 291 299 /* Basic checks */ 292 300 if (bp->attr.bp_type == HW_BREAKPOINT_EMPTY || 293 301 bp->attr.bp_type == HW_BREAKPOINT_INVALID) ··· 304 304 fetch_this_slot(&slots, weight); 305 305 306 306 /* Flexible counters need to keep at least one slot */ 307 - if (slots.pinned + (!!slots.flexible) > HBP_NUM) 307 + if (slots.pinned + (!!slots.flexible) > nr_slots[type]) 308 308 return -ENOSPC; 309 309 310 310 toggle_bp_slot(bp, true, type, weight); ··· 551 551 552 552 static int __init init_hw_breakpoint(void) 553 553 { 554 + unsigned int **task_bp_pinned; 555 + int cpu, err_cpu; 556 + int i; 557 + 558 + for (i = 0; i < TYPE_MAX; i++) 559 + nr_slots[i] = hw_breakpoint_slots(i); 560 + 561 + for_each_possible_cpu(cpu) { 562 + for (i = 0; i < TYPE_MAX; i++) { 563 + task_bp_pinned = &per_cpu(nr_task_bp_pinned[i], cpu); 564 + *task_bp_pinned = kzalloc(sizeof(int) * nr_slots[i], 565 + GFP_KERNEL); 566 + if (!*task_bp_pinned) 567 + goto err_alloc; 568 + } 569 + } 570 + 571 + constraints_initialized = 1; 572 + 554 573 return register_die_notifier(&hw_breakpoint_exceptions_nb); 574 + 575 + err_alloc: 576 + for_each_possible_cpu(err_cpu) { 577 + if (err_cpu == cpu) 578 + break; 579 + for (i = 0; i < TYPE_MAX; i++) 580 + kfree(per_cpu(nr_task_bp_pinned[i], cpu)); 581 + } 582 + 583 + return -ENOMEM; 555 584 } 556 585 core_initcall(init_hw_breakpoint); 557 586
+7 -19
kernel/trace/trace_ksym.c
··· 34 34 35 35 #include <asm/atomic.h> 36 36 37 - /* 38 - * For now, let us restrict the no. of symbols traced simultaneously to number 39 - * of available hardware breakpoint registers. 40 - */ 41 - #define KSYM_TRACER_MAX HBP_NUM 42 - 43 37 #define KSYM_TRACER_OP_LEN 3 /* rw- */ 44 38 45 39 struct trace_ksym { ··· 47 53 48 54 static struct trace_array *ksym_trace_array; 49 55 50 - static unsigned int ksym_filter_entry_count; 51 56 static unsigned int ksym_tracing_enabled; 52 57 53 58 static HLIST_HEAD(ksym_filter_head); ··· 174 181 struct trace_ksym *entry; 175 182 int ret = -ENOMEM; 176 183 177 - if (ksym_filter_entry_count >= KSYM_TRACER_MAX) { 178 - printk(KERN_ERR "ksym_tracer: Maximum limit:(%d) reached. No" 179 - " new requests for tracing can be accepted now.\n", 180 - KSYM_TRACER_MAX); 181 - return -ENOSPC; 182 - } 183 - 184 184 entry = kzalloc(sizeof(struct trace_ksym), GFP_KERNEL); 185 185 if (!entry) 186 186 return -ENOMEM; ··· 189 203 190 204 if (IS_ERR(entry->ksym_hbp)) { 191 205 ret = PTR_ERR(entry->ksym_hbp); 192 - printk(KERN_INFO "ksym_tracer request failed. Try again" 193 - " later!!\n"); 206 + if (ret == -ENOSPC) { 207 + printk(KERN_ERR "ksym_tracer: Maximum limit reached." 208 + " No new requests for tracing can be accepted now.\n"); 209 + } else { 210 + printk(KERN_INFO "ksym_tracer request failed. Try again" 211 + " later!!\n"); 212 + } 194 213 goto err; 195 214 } 196 215 197 216 hlist_add_head_rcu(&(entry->ksym_hlist), &ksym_filter_head); 198 - ksym_filter_entry_count++; 199 217 200 218 return 0; 201 219 ··· 255 265 hlist_for_each_entry_safe(entry, node, node1, &ksym_filter_head, 256 266 ksym_hlist) { 257 267 unregister_wide_hw_breakpoint(entry->ksym_hbp); 258 - ksym_filter_entry_count--; 259 268 hlist_del_rcu(&(entry->ksym_hlist)); 260 269 synchronize_rcu(); 261 270 kfree(entry); ··· 327 338 goto out_unlock; 328 339 } 329 340 /* Error or "symbol:---" case: drop it */ 330 - ksym_filter_entry_count--; 331 341 hlist_del_rcu(&(entry->ksym_hlist)); 332 342 synchronize_rcu(); 333 343 kfree(entry);