diff options
Diffstat (limited to 'kernel')
| -rw-r--r-- | kernel/hw_breakpoint.c | 53 | ||||
| -rw-r--r-- | kernel/trace/trace_ksym.c | 26 |
2 files changed, 48 insertions, 31 deletions
diff --git a/kernel/hw_breakpoint.c b/kernel/hw_breakpoint.c index 974498b858fc..684b710cbb91 100644 --- a/kernel/hw_breakpoint.c +++ b/kernel/hw_breakpoint.c | |||
| @@ -40,20 +40,12 @@ | |||
| 40 | #include <linux/percpu.h> | 40 | #include <linux/percpu.h> |
| 41 | #include <linux/sched.h> | 41 | #include <linux/sched.h> |
| 42 | #include <linux/init.h> | 42 | #include <linux/init.h> |
| 43 | #include <linux/slab.h> | ||
| 43 | #include <linux/cpu.h> | 44 | #include <linux/cpu.h> |
| 44 | #include <linux/smp.h> | 45 | #include <linux/smp.h> |
| 45 | 46 | ||
| 46 | #include <linux/hw_breakpoint.h> | 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 | * Constraints data | 51 | * Constraints data |
| @@ -63,11 +55,15 @@ enum bp_type_idx { | |||
| 63 | static DEFINE_PER_CPU(unsigned int, nr_cpu_bp_pinned[TYPE_MAX]); | 55 | static DEFINE_PER_CPU(unsigned int, nr_cpu_bp_pinned[TYPE_MAX]); |
| 64 | 56 | ||
| 65 | /* Number of pinned task breakpoints in a cpu */ | 57 | /* Number of pinned task breakpoints in a cpu */ |
| 66 | static DEFINE_PER_CPU(unsigned int, nr_task_bp_pinned[TYPE_MAX][HBP_NUM]); | 58 | static DEFINE_PER_CPU(unsigned int, *nr_task_bp_pinned[TYPE_MAX]); |
| 67 | 59 | ||
| 68 | /* Number of non-pinned cpu/task breakpoints in a cpu */ | 60 | /* Number of non-pinned cpu/task breakpoints in a cpu */ |
| 69 | static DEFINE_PER_CPU(unsigned int, nr_bp_flexible[TYPE_MAX]); | 61 | static DEFINE_PER_CPU(unsigned int, nr_bp_flexible[TYPE_MAX]); |
| 70 | 62 | ||
| 63 | static int nr_slots[TYPE_MAX]; | ||
| 64 | |||
| 65 | static int constraints_initialized; | ||
| 66 | |||
| 71 | /* Gather the number of total pinned and un-pinned bp in a cpuset */ | 67 | /* Gather the number of total pinned and un-pinned bp in a cpuset */ |
| 72 | struct bp_busy_slots { | 68 | struct bp_busy_slots { |
| 73 | unsigned int pinned; | 69 | unsigned int pinned; |
| @@ -99,7 +95,7 @@ static unsigned int max_task_bp_pinned(int cpu, enum bp_type_idx type) | |||
| 99 | int i; | 95 | int i; |
| 100 | unsigned int *tsk_pinned = per_cpu(nr_task_bp_pinned[type], cpu); | 96 | unsigned int *tsk_pinned = per_cpu(nr_task_bp_pinned[type], cpu); |
| 101 | 97 | ||
| 102 | for (i = HBP_NUM -1; i >= 0; i--) { | 98 | for (i = nr_slots[type] - 1; i >= 0; i--) { |
| 103 | if (tsk_pinned[i] > 0) | 99 | if (tsk_pinned[i] > 0) |
| 104 | return i + 1; | 100 | return i + 1; |
| 105 | } | 101 | } |
| @@ -292,6 +288,10 @@ static int __reserve_bp_slot(struct perf_event *bp) | |||
| 292 | enum bp_type_idx type; | 288 | enum bp_type_idx type; |
| 293 | int weight; | 289 | int weight; |
| 294 | 290 | ||
| 291 | /* We couldn't initialize breakpoint constraints on boot */ | ||
| 292 | if (!constraints_initialized) | ||
| 293 | return -ENOMEM; | ||
| 294 | |||
| 295 | /* Basic checks */ | 295 | /* Basic checks */ |
| 296 | if (bp->attr.bp_type == HW_BREAKPOINT_EMPTY || | 296 | if (bp->attr.bp_type == HW_BREAKPOINT_EMPTY || |
| 297 | bp->attr.bp_type == HW_BREAKPOINT_INVALID) | 297 | bp->attr.bp_type == HW_BREAKPOINT_INVALID) |
| @@ -304,7 +304,7 @@ static int __reserve_bp_slot(struct perf_event *bp) | |||
| 304 | fetch_this_slot(&slots, weight); | 304 | fetch_this_slot(&slots, weight); |
| 305 | 305 | ||
| 306 | /* Flexible counters need to keep at least one slot */ | 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 | return -ENOSPC; | 308 | return -ENOSPC; |
| 309 | 309 | ||
| 310 | toggle_bp_slot(bp, true, type, weight); | 310 | toggle_bp_slot(bp, true, type, weight); |
| @@ -551,7 +551,36 @@ static struct notifier_block hw_breakpoint_exceptions_nb = { | |||
| 551 | 551 | ||
| 552 | static int __init init_hw_breakpoint(void) | 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 | return register_die_notifier(&hw_breakpoint_exceptions_nb); | 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 | core_initcall(init_hw_breakpoint); | 585 | core_initcall(init_hw_breakpoint); |
| 557 | 586 | ||
diff --git a/kernel/trace/trace_ksym.c b/kernel/trace/trace_ksym.c index d59cd6879477..8eaf00749b65 100644 --- a/kernel/trace/trace_ksym.c +++ b/kernel/trace/trace_ksym.c | |||
| @@ -34,12 +34,6 @@ | |||
| 34 | 34 | ||
| 35 | #include <asm/atomic.h> | 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 | #define KSYM_TRACER_OP_LEN 3 /* rw- */ | 37 | #define KSYM_TRACER_OP_LEN 3 /* rw- */ |
| 44 | 38 | ||
| 45 | struct trace_ksym { | 39 | struct trace_ksym { |
| @@ -53,7 +47,6 @@ struct trace_ksym { | |||
| 53 | 47 | ||
| 54 | static struct trace_array *ksym_trace_array; | 48 | static struct trace_array *ksym_trace_array; |
| 55 | 49 | ||
| 56 | static unsigned int ksym_filter_entry_count; | ||
| 57 | static unsigned int ksym_tracing_enabled; | 50 | static unsigned int ksym_tracing_enabled; |
| 58 | 51 | ||
| 59 | static HLIST_HEAD(ksym_filter_head); | 52 | static HLIST_HEAD(ksym_filter_head); |
| @@ -181,13 +174,6 @@ int process_new_ksym_entry(char *ksymname, int op, unsigned long addr) | |||
| 181 | struct trace_ksym *entry; | 174 | struct trace_ksym *entry; |
| 182 | int ret = -ENOMEM; | 175 | int ret = -ENOMEM; |
| 183 | 176 | ||
| 184 | if (ksym_filter_entry_count >= KSYM_TRACER_MAX) { | ||
| 185 | printk(KERN_ERR "ksym_tracer: Maximum limit:(%d) reached. No" | ||
| 186 | " new requests for tracing can be accepted now.\n", | ||
| 187 | KSYM_TRACER_MAX); | ||
| 188 | return -ENOSPC; | ||
| 189 | } | ||
| 190 | |||
| 191 | entry = kzalloc(sizeof(struct trace_ksym), GFP_KERNEL); | 177 | entry = kzalloc(sizeof(struct trace_ksym), GFP_KERNEL); |
| 192 | if (!entry) | 178 | if (!entry) |
| 193 | return -ENOMEM; | 179 | return -ENOMEM; |
| @@ -203,13 +189,17 @@ int process_new_ksym_entry(char *ksymname, int op, unsigned long addr) | |||
| 203 | 189 | ||
| 204 | if (IS_ERR(entry->ksym_hbp)) { | 190 | if (IS_ERR(entry->ksym_hbp)) { |
| 205 | ret = PTR_ERR(entry->ksym_hbp); | 191 | ret = PTR_ERR(entry->ksym_hbp); |
| 206 | printk(KERN_INFO "ksym_tracer request failed. Try again" | 192 | if (ret == -ENOSPC) { |
| 207 | " later!!\n"); | 193 | printk(KERN_ERR "ksym_tracer: Maximum limit reached." |
| 194 | " No new requests for tracing can be accepted now.\n"); | ||
| 195 | } else { | ||
| 196 | printk(KERN_INFO "ksym_tracer request failed. Try again" | ||
| 197 | " later!!\n"); | ||
| 198 | } | ||
| 208 | goto err; | 199 | goto err; |
| 209 | } | 200 | } |
| 210 | 201 | ||
| 211 | hlist_add_head_rcu(&(entry->ksym_hlist), &ksym_filter_head); | 202 | hlist_add_head_rcu(&(entry->ksym_hlist), &ksym_filter_head); |
| 212 | ksym_filter_entry_count++; | ||
| 213 | 203 | ||
| 214 | return 0; | 204 | return 0; |
| 215 | 205 | ||
| @@ -265,7 +255,6 @@ static void __ksym_trace_reset(void) | |||
| 265 | hlist_for_each_entry_safe(entry, node, node1, &ksym_filter_head, | 255 | hlist_for_each_entry_safe(entry, node, node1, &ksym_filter_head, |
| 266 | ksym_hlist) { | 256 | ksym_hlist) { |
| 267 | unregister_wide_hw_breakpoint(entry->ksym_hbp); | 257 | unregister_wide_hw_breakpoint(entry->ksym_hbp); |
| 268 | ksym_filter_entry_count--; | ||
| 269 | hlist_del_rcu(&(entry->ksym_hlist)); | 258 | hlist_del_rcu(&(entry->ksym_hlist)); |
| 270 | synchronize_rcu(); | 259 | synchronize_rcu(); |
| 271 | kfree(entry); | 260 | kfree(entry); |
| @@ -338,7 +327,6 @@ static ssize_t ksym_trace_filter_write(struct file *file, | |||
| 338 | goto out_unlock; | 327 | goto out_unlock; |
| 339 | } | 328 | } |
| 340 | /* Error or "symbol:---" case: drop it */ | 329 | /* Error or "symbol:---" case: drop it */ |
| 341 | ksym_filter_entry_count--; | ||
| 342 | hlist_del_rcu(&(entry->ksym_hlist)); | 330 | hlist_del_rcu(&(entry->ksym_hlist)); |
| 343 | synchronize_rcu(); | 331 | synchronize_rcu(); |
| 344 | kfree(entry); | 332 | kfree(entry); |
