diff options
Diffstat (limited to 'kernel/hw_breakpoint.c')
| -rw-r--r-- | kernel/hw_breakpoint.c | 78 |
1 files changed, 41 insertions, 37 deletions
diff --git a/kernel/hw_breakpoint.c b/kernel/hw_breakpoint.c index 71ed3ce29e12..d71a987fd2bf 100644 --- a/kernel/hw_breakpoint.c +++ b/kernel/hw_breakpoint.c | |||
| @@ -41,6 +41,7 @@ | |||
| 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/slab.h> |
| 44 | #include <linux/list.h> | ||
| 44 | #include <linux/cpu.h> | 45 | #include <linux/cpu.h> |
| 45 | #include <linux/smp.h> | 46 | #include <linux/smp.h> |
| 46 | 47 | ||
| @@ -62,6 +63,9 @@ static DEFINE_PER_CPU(unsigned int, nr_bp_flexible[TYPE_MAX]); | |||
| 62 | 63 | ||
| 63 | static int nr_slots[TYPE_MAX]; | 64 | static int nr_slots[TYPE_MAX]; |
| 64 | 65 | ||
| 66 | /* Keep track of the breakpoints attached to tasks */ | ||
| 67 | static LIST_HEAD(bp_task_head); | ||
| 68 | |||
| 65 | static int constraints_initialized; | 69 | static int constraints_initialized; |
| 66 | 70 | ||
| 67 | /* 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 */ |
| @@ -103,33 +107,21 @@ static unsigned int max_task_bp_pinned(int cpu, enum bp_type_idx type) | |||
| 103 | return 0; | 107 | return 0; |
| 104 | } | 108 | } |
| 105 | 109 | ||
| 106 | static int task_bp_pinned(struct task_struct *tsk, enum bp_type_idx type) | 110 | /* |
| 111 | * Count the number of breakpoints of the same type and same task. | ||
| 112 | * The given event must be not on the list. | ||
| 113 | */ | ||
| 114 | static int task_bp_pinned(struct perf_event *bp, enum bp_type_idx type) | ||
| 107 | { | 115 | { |
| 108 | struct perf_event_context *ctx = tsk->perf_event_ctxp; | 116 | struct perf_event_context *ctx = bp->ctx; |
| 109 | struct list_head *list; | 117 | struct perf_event *iter; |
| 110 | struct perf_event *bp; | ||
| 111 | unsigned long flags; | ||
| 112 | int count = 0; | 118 | int count = 0; |
| 113 | 119 | ||
| 114 | if (WARN_ONCE(!ctx, "No perf context for this task")) | 120 | list_for_each_entry(iter, &bp_task_head, hw.bp_list) { |
| 115 | return 0; | 121 | if (iter->ctx == ctx && find_slot_idx(iter) == type) |
| 116 | 122 | count += hw_breakpoint_weight(iter); | |
| 117 | list = &ctx->event_list; | ||
| 118 | |||
| 119 | raw_spin_lock_irqsave(&ctx->lock, flags); | ||
| 120 | |||
| 121 | /* | ||
| 122 | * The current breakpoint counter is not included in the list | ||
| 123 | * at the open() callback time | ||
| 124 | */ | ||
| 125 | list_for_each_entry(bp, list, event_entry) { | ||
| 126 | if (bp->attr.type == PERF_TYPE_BREAKPOINT) | ||
| 127 | if (find_slot_idx(bp) == type) | ||
| 128 | count += hw_breakpoint_weight(bp); | ||
| 129 | } | 123 | } |
| 130 | 124 | ||
| 131 | raw_spin_unlock_irqrestore(&ctx->lock, flags); | ||
| 132 | |||
| 133 | return count; | 125 | return count; |
| 134 | } | 126 | } |
| 135 | 127 | ||
| @@ -149,7 +141,7 @@ fetch_bp_busy_slots(struct bp_busy_slots *slots, struct perf_event *bp, | |||
| 149 | if (!tsk) | 141 | if (!tsk) |
| 150 | slots->pinned += max_task_bp_pinned(cpu, type); | 142 | slots->pinned += max_task_bp_pinned(cpu, type); |
| 151 | else | 143 | else |
| 152 | slots->pinned += task_bp_pinned(tsk, type); | 144 | slots->pinned += task_bp_pinned(bp, type); |
| 153 | slots->flexible = per_cpu(nr_bp_flexible[type], cpu); | 145 | slots->flexible = per_cpu(nr_bp_flexible[type], cpu); |
| 154 | 146 | ||
| 155 | return; | 147 | return; |
| @@ -162,7 +154,7 @@ fetch_bp_busy_slots(struct bp_busy_slots *slots, struct perf_event *bp, | |||
| 162 | if (!tsk) | 154 | if (!tsk) |
| 163 | nr += max_task_bp_pinned(cpu, type); | 155 | nr += max_task_bp_pinned(cpu, type); |
| 164 | else | 156 | else |
| 165 | nr += task_bp_pinned(tsk, type); | 157 | nr += task_bp_pinned(bp, type); |
| 166 | 158 | ||
| 167 | if (nr > slots->pinned) | 159 | if (nr > slots->pinned) |
| 168 | slots->pinned = nr; | 160 | slots->pinned = nr; |
| @@ -188,7 +180,7 @@ fetch_this_slot(struct bp_busy_slots *slots, int weight) | |||
| 188 | /* | 180 | /* |
| 189 | * Add a pinned breakpoint for the given task in our constraint table | 181 | * Add a pinned breakpoint for the given task in our constraint table |
| 190 | */ | 182 | */ |
| 191 | static void toggle_bp_task_slot(struct task_struct *tsk, int cpu, bool enable, | 183 | static void toggle_bp_task_slot(struct perf_event *bp, int cpu, bool enable, |
| 192 | enum bp_type_idx type, int weight) | 184 | enum bp_type_idx type, int weight) |
| 193 | { | 185 | { |
| 194 | unsigned int *tsk_pinned; | 186 | unsigned int *tsk_pinned; |
| @@ -196,10 +188,11 @@ static void toggle_bp_task_slot(struct task_struct *tsk, int cpu, bool enable, | |||
| 196 | int old_idx = 0; | 188 | int old_idx = 0; |
| 197 | int idx = 0; | 189 | int idx = 0; |
| 198 | 190 | ||
| 199 | old_count = task_bp_pinned(tsk, type); | 191 | old_count = task_bp_pinned(bp, type); |
| 200 | old_idx = old_count - 1; | 192 | old_idx = old_count - 1; |
| 201 | idx = old_idx + weight; | 193 | idx = old_idx + weight; |
| 202 | 194 | ||
| 195 | /* tsk_pinned[n] is the number of tasks having n breakpoints */ | ||
| 203 | tsk_pinned = per_cpu(nr_task_bp_pinned[type], cpu); | 196 | tsk_pinned = per_cpu(nr_task_bp_pinned[type], cpu); |
| 204 | if (enable) { | 197 | if (enable) { |
| 205 | tsk_pinned[idx]++; | 198 | tsk_pinned[idx]++; |
| @@ -222,23 +215,30 @@ toggle_bp_slot(struct perf_event *bp, bool enable, enum bp_type_idx type, | |||
| 222 | int cpu = bp->cpu; | 215 | int cpu = bp->cpu; |
| 223 | struct task_struct *tsk = bp->ctx->task; | 216 | struct task_struct *tsk = bp->ctx->task; |
| 224 | 217 | ||
| 218 | /* Pinned counter cpu profiling */ | ||
| 219 | if (!tsk) { | ||
| 220 | |||
| 221 | if (enable) | ||
| 222 | per_cpu(nr_cpu_bp_pinned[type], bp->cpu) += weight; | ||
| 223 | else | ||
| 224 | per_cpu(nr_cpu_bp_pinned[type], bp->cpu) -= weight; | ||
| 225 | return; | ||
| 226 | } | ||
| 227 | |||
| 225 | /* Pinned counter task profiling */ | 228 | /* Pinned counter task profiling */ |
| 226 | if (tsk) { | ||
| 227 | if (cpu >= 0) { | ||
| 228 | toggle_bp_task_slot(tsk, cpu, enable, type, weight); | ||
| 229 | return; | ||
| 230 | } | ||
| 231 | 229 | ||
| 230 | if (!enable) | ||
| 231 | list_del(&bp->hw.bp_list); | ||
| 232 | |||
| 233 | if (cpu >= 0) { | ||
| 234 | toggle_bp_task_slot(bp, cpu, enable, type, weight); | ||
| 235 | } else { | ||
| 232 | for_each_online_cpu(cpu) | 236 | for_each_online_cpu(cpu) |
| 233 | toggle_bp_task_slot(tsk, cpu, enable, type, weight); | 237 | toggle_bp_task_slot(bp, cpu, enable, type, weight); |
| 234 | return; | ||
| 235 | } | 238 | } |
| 236 | 239 | ||
| 237 | /* Pinned counter cpu profiling */ | ||
| 238 | if (enable) | 240 | if (enable) |
| 239 | per_cpu(nr_cpu_bp_pinned[type], bp->cpu) += weight; | 241 | list_add_tail(&bp->hw.bp_list, &bp_task_head); |
| 240 | else | ||
| 241 | per_cpu(nr_cpu_bp_pinned[type], bp->cpu) -= weight; | ||
| 242 | } | 242 | } |
| 243 | 243 | ||
| 244 | /* | 244 | /* |
| @@ -312,6 +312,10 @@ static int __reserve_bp_slot(struct perf_event *bp) | |||
| 312 | weight = hw_breakpoint_weight(bp); | 312 | weight = hw_breakpoint_weight(bp); |
| 313 | 313 | ||
| 314 | fetch_bp_busy_slots(&slots, bp, type); | 314 | fetch_bp_busy_slots(&slots, bp, type); |
| 315 | /* | ||
| 316 | * Simulate the addition of this breakpoint to the constraints | ||
| 317 | * and see the result. | ||
| 318 | */ | ||
| 315 | fetch_this_slot(&slots, weight); | 319 | fetch_this_slot(&slots, weight); |
| 316 | 320 | ||
| 317 | /* Flexible counters need to keep at least one slot */ | 321 | /* Flexible counters need to keep at least one slot */ |
