diff options
Diffstat (limited to 'kernel/hw_breakpoint.c')
| -rw-r--r-- | kernel/hw_breakpoint.c | 212 |
1 files changed, 141 insertions, 71 deletions
diff --git a/kernel/hw_breakpoint.c b/kernel/hw_breakpoint.c index cf5ee1628411..967e66143e11 100644 --- a/kernel/hw_breakpoint.c +++ b/kernel/hw_breakpoint.c | |||
| @@ -40,6 +40,7 @@ | |||
| 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/cpu.h> | ||
| 43 | #include <linux/smp.h> | 44 | #include <linux/smp.h> |
| 44 | 45 | ||
| 45 | #include <linux/hw_breakpoint.h> | 46 | #include <linux/hw_breakpoint.h> |
| @@ -52,7 +53,7 @@ | |||
| 52 | static DEFINE_PER_CPU(unsigned int, nr_cpu_bp_pinned); | 53 | static DEFINE_PER_CPU(unsigned int, nr_cpu_bp_pinned); |
| 53 | 54 | ||
| 54 | /* Number of pinned task breakpoints in a cpu */ | 55 | /* Number of pinned task breakpoints in a cpu */ |
| 55 | static DEFINE_PER_CPU(unsigned int, task_bp_pinned[HBP_NUM]); | 56 | static DEFINE_PER_CPU(unsigned int, nr_task_bp_pinned[HBP_NUM]); |
| 56 | 57 | ||
| 57 | /* Number of non-pinned cpu/task breakpoints in a cpu */ | 58 | /* Number of non-pinned cpu/task breakpoints in a cpu */ |
| 58 | static DEFINE_PER_CPU(unsigned int, nr_bp_flexible); | 59 | static DEFINE_PER_CPU(unsigned int, nr_bp_flexible); |
| @@ -73,7 +74,7 @@ static DEFINE_MUTEX(nr_bp_mutex); | |||
| 73 | static unsigned int max_task_bp_pinned(int cpu) | 74 | static unsigned int max_task_bp_pinned(int cpu) |
| 74 | { | 75 | { |
| 75 | int i; | 76 | int i; |
| 76 | unsigned int *tsk_pinned = per_cpu(task_bp_pinned, cpu); | 77 | unsigned int *tsk_pinned = per_cpu(nr_task_bp_pinned, cpu); |
| 77 | 78 | ||
| 78 | for (i = HBP_NUM -1; i >= 0; i--) { | 79 | for (i = HBP_NUM -1; i >= 0; i--) { |
| 79 | if (tsk_pinned[i] > 0) | 80 | if (tsk_pinned[i] > 0) |
| @@ -83,15 +84,51 @@ static unsigned int max_task_bp_pinned(int cpu) | |||
| 83 | return 0; | 84 | return 0; |
| 84 | } | 85 | } |
| 85 | 86 | ||
| 87 | static int task_bp_pinned(struct task_struct *tsk) | ||
| 88 | { | ||
| 89 | struct perf_event_context *ctx = tsk->perf_event_ctxp; | ||
| 90 | struct list_head *list; | ||
| 91 | struct perf_event *bp; | ||
| 92 | unsigned long flags; | ||
| 93 | int count = 0; | ||
| 94 | |||
| 95 | if (WARN_ONCE(!ctx, "No perf context for this task")) | ||
| 96 | return 0; | ||
| 97 | |||
| 98 | list = &ctx->event_list; | ||
| 99 | |||
| 100 | raw_spin_lock_irqsave(&ctx->lock, flags); | ||
| 101 | |||
| 102 | /* | ||
| 103 | * The current breakpoint counter is not included in the list | ||
| 104 | * at the open() callback time | ||
| 105 | */ | ||
| 106 | list_for_each_entry(bp, list, event_entry) { | ||
| 107 | if (bp->attr.type == PERF_TYPE_BREAKPOINT) | ||
| 108 | count++; | ||
| 109 | } | ||
| 110 | |||
| 111 | raw_spin_unlock_irqrestore(&ctx->lock, flags); | ||
| 112 | |||
| 113 | return count; | ||
| 114 | } | ||
| 115 | |||
| 86 | /* | 116 | /* |
| 87 | * Report the number of pinned/un-pinned breakpoints we have in | 117 | * Report the number of pinned/un-pinned breakpoints we have in |
| 88 | * a given cpu (cpu > -1) or in all of them (cpu = -1). | 118 | * a given cpu (cpu > -1) or in all of them (cpu = -1). |
| 89 | */ | 119 | */ |
| 90 | static void fetch_bp_busy_slots(struct bp_busy_slots *slots, int cpu) | 120 | static void |
| 121 | fetch_bp_busy_slots(struct bp_busy_slots *slots, struct perf_event *bp) | ||
| 91 | { | 122 | { |
| 123 | int cpu = bp->cpu; | ||
| 124 | struct task_struct *tsk = bp->ctx->task; | ||
| 125 | |||
| 92 | if (cpu >= 0) { | 126 | if (cpu >= 0) { |
| 93 | slots->pinned = per_cpu(nr_cpu_bp_pinned, cpu); | 127 | slots->pinned = per_cpu(nr_cpu_bp_pinned, cpu); |
| 94 | slots->pinned += max_task_bp_pinned(cpu); | 128 | if (!tsk) |
| 129 | slots->pinned += max_task_bp_pinned(cpu); | ||
| 130 | else | ||
| 131 | slots->pinned += task_bp_pinned(tsk); | ||
| 95 | slots->flexible = per_cpu(nr_bp_flexible, cpu); | 132 | slots->flexible = per_cpu(nr_bp_flexible, cpu); |
| 96 | 133 | ||
| 97 | return; | 134 | return; |
| @@ -101,7 +138,10 @@ static void fetch_bp_busy_slots(struct bp_busy_slots *slots, int cpu) | |||
| 101 | unsigned int nr; | 138 | unsigned int nr; |
| 102 | 139 | ||
| 103 | nr = per_cpu(nr_cpu_bp_pinned, cpu); | 140 | nr = per_cpu(nr_cpu_bp_pinned, cpu); |
| 104 | nr += max_task_bp_pinned(cpu); | 141 | if (!tsk) |
| 142 | nr += max_task_bp_pinned(cpu); | ||
| 143 | else | ||
| 144 | nr += task_bp_pinned(tsk); | ||
| 105 | 145 | ||
| 106 | if (nr > slots->pinned) | 146 | if (nr > slots->pinned) |
| 107 | slots->pinned = nr; | 147 | slots->pinned = nr; |
| @@ -118,35 +158,12 @@ static void fetch_bp_busy_slots(struct bp_busy_slots *slots, int cpu) | |||
| 118 | */ | 158 | */ |
| 119 | static void toggle_bp_task_slot(struct task_struct *tsk, int cpu, bool enable) | 159 | static void toggle_bp_task_slot(struct task_struct *tsk, int cpu, bool enable) |
| 120 | { | 160 | { |
| 121 | int count = 0; | ||
| 122 | struct perf_event *bp; | ||
| 123 | struct perf_event_context *ctx = tsk->perf_event_ctxp; | ||
| 124 | unsigned int *tsk_pinned; | 161 | unsigned int *tsk_pinned; |
| 125 | struct list_head *list; | 162 | int count = 0; |
| 126 | unsigned long flags; | ||
| 127 | |||
| 128 | if (WARN_ONCE(!ctx, "No perf context for this task")) | ||
| 129 | return; | ||
| 130 | |||
| 131 | list = &ctx->event_list; | ||
| 132 | |||
| 133 | spin_lock_irqsave(&ctx->lock, flags); | ||
| 134 | |||
| 135 | /* | ||
| 136 | * The current breakpoint counter is not included in the list | ||
| 137 | * at the open() callback time | ||
| 138 | */ | ||
| 139 | list_for_each_entry(bp, list, event_entry) { | ||
| 140 | if (bp->attr.type == PERF_TYPE_BREAKPOINT) | ||
| 141 | count++; | ||
| 142 | } | ||
| 143 | 163 | ||
| 144 | spin_unlock_irqrestore(&ctx->lock, flags); | 164 | count = task_bp_pinned(tsk); |
| 145 | 165 | ||
| 146 | if (WARN_ONCE(count < 0, "No breakpoint counter found in the counter list")) | 166 | tsk_pinned = per_cpu(nr_task_bp_pinned, cpu); |
| 147 | return; | ||
| 148 | |||
| 149 | tsk_pinned = per_cpu(task_bp_pinned, cpu); | ||
| 150 | if (enable) { | 167 | if (enable) { |
| 151 | tsk_pinned[count]++; | 168 | tsk_pinned[count]++; |
| 152 | if (count > 0) | 169 | if (count > 0) |
| @@ -193,7 +210,7 @@ static void toggle_bp_slot(struct perf_event *bp, bool enable) | |||
| 193 | * - If attached to a single cpu, check: | 210 | * - If attached to a single cpu, check: |
| 194 | * | 211 | * |
| 195 | * (per_cpu(nr_bp_flexible, cpu) || (per_cpu(nr_cpu_bp_pinned, cpu) | 212 | * (per_cpu(nr_bp_flexible, cpu) || (per_cpu(nr_cpu_bp_pinned, cpu) |
| 196 | * + max(per_cpu(task_bp_pinned, cpu)))) < HBP_NUM | 213 | * + max(per_cpu(nr_task_bp_pinned, cpu)))) < HBP_NUM |
| 197 | * | 214 | * |
| 198 | * -> If there are already non-pinned counters in this cpu, it means | 215 | * -> If there are already non-pinned counters in this cpu, it means |
| 199 | * there is already a free slot for them. | 216 | * there is already a free slot for them. |
| @@ -204,7 +221,7 @@ static void toggle_bp_slot(struct perf_event *bp, bool enable) | |||
| 204 | * - If attached to every cpus, check: | 221 | * - If attached to every cpus, check: |
| 205 | * | 222 | * |
| 206 | * (per_cpu(nr_bp_flexible, *) || (max(per_cpu(nr_cpu_bp_pinned, *)) | 223 | * (per_cpu(nr_bp_flexible, *) || (max(per_cpu(nr_cpu_bp_pinned, *)) |
| 207 | * + max(per_cpu(task_bp_pinned, *)))) < HBP_NUM | 224 | * + max(per_cpu(nr_task_bp_pinned, *)))) < HBP_NUM |
| 208 | * | 225 | * |
| 209 | * -> This is roughly the same, except we check the number of per cpu | 226 | * -> This is roughly the same, except we check the number of per cpu |
| 210 | * bp for every cpu and we keep the max one. Same for the per tasks | 227 | * bp for every cpu and we keep the max one. Same for the per tasks |
| @@ -216,7 +233,7 @@ static void toggle_bp_slot(struct perf_event *bp, bool enable) | |||
| 216 | * - If attached to a single cpu, check: | 233 | * - If attached to a single cpu, check: |
| 217 | * | 234 | * |
| 218 | * ((per_cpu(nr_bp_flexible, cpu) > 1) + per_cpu(nr_cpu_bp_pinned, cpu) | 235 | * ((per_cpu(nr_bp_flexible, cpu) > 1) + per_cpu(nr_cpu_bp_pinned, cpu) |
| 219 | * + max(per_cpu(task_bp_pinned, cpu))) < HBP_NUM | 236 | * + max(per_cpu(nr_task_bp_pinned, cpu))) < HBP_NUM |
| 220 | * | 237 | * |
| 221 | * -> Same checks as before. But now the nr_bp_flexible, if any, must keep | 238 | * -> Same checks as before. But now the nr_bp_flexible, if any, must keep |
| 222 | * one register at least (or they will never be fed). | 239 | * one register at least (or they will never be fed). |
| @@ -224,42 +241,74 @@ static void toggle_bp_slot(struct perf_event *bp, bool enable) | |||
| 224 | * - If attached to every cpus, check: | 241 | * - If attached to every cpus, check: |
| 225 | * | 242 | * |
| 226 | * ((per_cpu(nr_bp_flexible, *) > 1) + max(per_cpu(nr_cpu_bp_pinned, *)) | 243 | * ((per_cpu(nr_bp_flexible, *) > 1) + max(per_cpu(nr_cpu_bp_pinned, *)) |
| 227 | * + max(per_cpu(task_bp_pinned, *))) < HBP_NUM | 244 | * + max(per_cpu(nr_task_bp_pinned, *))) < HBP_NUM |
| 228 | */ | 245 | */ |
| 229 | int reserve_bp_slot(struct perf_event *bp) | 246 | static int __reserve_bp_slot(struct perf_event *bp) |
| 230 | { | 247 | { |
| 231 | struct bp_busy_slots slots = {0}; | 248 | struct bp_busy_slots slots = {0}; |
| 232 | int ret = 0; | ||
| 233 | |||
| 234 | mutex_lock(&nr_bp_mutex); | ||
| 235 | 249 | ||
| 236 | fetch_bp_busy_slots(&slots, bp->cpu); | 250 | fetch_bp_busy_slots(&slots, bp); |
| 237 | 251 | ||
| 238 | /* Flexible counters need to keep at least one slot */ | 252 | /* Flexible counters need to keep at least one slot */ |
| 239 | if (slots.pinned + (!!slots.flexible) == HBP_NUM) { | 253 | if (slots.pinned + (!!slots.flexible) == HBP_NUM) |
| 240 | ret = -ENOSPC; | 254 | return -ENOSPC; |
| 241 | goto end; | ||
| 242 | } | ||
| 243 | 255 | ||
| 244 | toggle_bp_slot(bp, true); | 256 | toggle_bp_slot(bp, true); |
| 245 | 257 | ||
| 246 | end: | 258 | return 0; |
| 259 | } | ||
| 260 | |||
| 261 | int reserve_bp_slot(struct perf_event *bp) | ||
| 262 | { | ||
| 263 | int ret; | ||
| 264 | |||
| 265 | mutex_lock(&nr_bp_mutex); | ||
| 266 | |||
| 267 | ret = __reserve_bp_slot(bp); | ||
| 268 | |||
| 247 | mutex_unlock(&nr_bp_mutex); | 269 | mutex_unlock(&nr_bp_mutex); |
| 248 | 270 | ||
| 249 | return ret; | 271 | return ret; |
| 250 | } | 272 | } |
| 251 | 273 | ||
| 274 | static void __release_bp_slot(struct perf_event *bp) | ||
| 275 | { | ||
| 276 | toggle_bp_slot(bp, false); | ||
| 277 | } | ||
| 278 | |||
| 252 | void release_bp_slot(struct perf_event *bp) | 279 | void release_bp_slot(struct perf_event *bp) |
| 253 | { | 280 | { |
| 254 | mutex_lock(&nr_bp_mutex); | 281 | mutex_lock(&nr_bp_mutex); |
| 255 | 282 | ||
| 256 | toggle_bp_slot(bp, false); | 283 | __release_bp_slot(bp); |
| 257 | 284 | ||
| 258 | mutex_unlock(&nr_bp_mutex); | 285 | mutex_unlock(&nr_bp_mutex); |
| 259 | } | 286 | } |
| 260 | 287 | ||
| 288 | /* | ||
| 289 | * Allow the kernel debugger to reserve breakpoint slots without | ||
| 290 | * taking a lock using the dbg_* variant of for the reserve and | ||
| 291 | * release breakpoint slots. | ||
| 292 | */ | ||
| 293 | int dbg_reserve_bp_slot(struct perf_event *bp) | ||
| 294 | { | ||
| 295 | if (mutex_is_locked(&nr_bp_mutex)) | ||
| 296 | return -1; | ||
| 297 | |||
| 298 | return __reserve_bp_slot(bp); | ||
| 299 | } | ||
| 300 | |||
| 301 | int dbg_release_bp_slot(struct perf_event *bp) | ||
| 302 | { | ||
| 303 | if (mutex_is_locked(&nr_bp_mutex)) | ||
| 304 | return -1; | ||
| 305 | |||
| 306 | __release_bp_slot(bp); | ||
| 261 | 307 | ||
| 262 | int __register_perf_hw_breakpoint(struct perf_event *bp) | 308 | return 0; |
| 309 | } | ||
| 310 | |||
| 311 | int register_perf_hw_breakpoint(struct perf_event *bp) | ||
| 263 | { | 312 | { |
| 264 | int ret; | 313 | int ret; |
| 265 | 314 | ||
| @@ -276,17 +325,14 @@ int __register_perf_hw_breakpoint(struct perf_event *bp) | |||
| 276 | * This is a quick hack that will be removed soon, once we remove | 325 | * This is a quick hack that will be removed soon, once we remove |
| 277 | * the tmp breakpoints from ptrace | 326 | * the tmp breakpoints from ptrace |
| 278 | */ | 327 | */ |
| 279 | if (!bp->attr.disabled || bp->callback == perf_bp_event) | 328 | if (!bp->attr.disabled || !bp->overflow_handler) |
| 280 | ret = arch_validate_hwbkpt_settings(bp, bp->ctx->task); | 329 | ret = arch_validate_hwbkpt_settings(bp, bp->ctx->task); |
| 281 | 330 | ||
| 282 | return ret; | 331 | /* if arch_validate_hwbkpt_settings() fails then release bp slot */ |
| 283 | } | 332 | if (ret) |
| 284 | 333 | release_bp_slot(bp); | |
| 285 | int register_perf_hw_breakpoint(struct perf_event *bp) | ||
| 286 | { | ||
| 287 | bp->callback = perf_bp_event; | ||
| 288 | 334 | ||
| 289 | return __register_perf_hw_breakpoint(bp); | 335 | return ret; |
| 290 | } | 336 | } |
| 291 | 337 | ||
| 292 | /** | 338 | /** |
| @@ -297,7 +343,7 @@ int register_perf_hw_breakpoint(struct perf_event *bp) | |||
| 297 | */ | 343 | */ |
| 298 | struct perf_event * | 344 | struct perf_event * |
| 299 | register_user_hw_breakpoint(struct perf_event_attr *attr, | 345 | register_user_hw_breakpoint(struct perf_event_attr *attr, |
| 300 | perf_callback_t triggered, | 346 | perf_overflow_handler_t triggered, |
| 301 | struct task_struct *tsk) | 347 | struct task_struct *tsk) |
| 302 | { | 348 | { |
| 303 | return perf_event_create_kernel_counter(attr, -1, tsk->pid, triggered); | 349 | return perf_event_create_kernel_counter(attr, -1, tsk->pid, triggered); |
| @@ -311,19 +357,40 @@ EXPORT_SYMBOL_GPL(register_user_hw_breakpoint); | |||
| 311 | * @triggered: callback to trigger when we hit the breakpoint | 357 | * @triggered: callback to trigger when we hit the breakpoint |
| 312 | * @tsk: pointer to 'task_struct' of the process to which the address belongs | 358 | * @tsk: pointer to 'task_struct' of the process to which the address belongs |
| 313 | */ | 359 | */ |
| 314 | struct perf_event * | 360 | int modify_user_hw_breakpoint(struct perf_event *bp, struct perf_event_attr *attr) |
| 315 | modify_user_hw_breakpoint(struct perf_event *bp, struct perf_event_attr *attr, | ||
| 316 | perf_callback_t triggered, | ||
| 317 | struct task_struct *tsk) | ||
| 318 | { | 361 | { |
| 319 | /* | 362 | u64 old_addr = bp->attr.bp_addr; |
| 320 | * FIXME: do it without unregistering | 363 | u64 old_len = bp->attr.bp_len; |
| 321 | * - We don't want to lose our slot | 364 | int old_type = bp->attr.bp_type; |
| 322 | * - If the new bp is incorrect, don't lose the older one | 365 | int err = 0; |
| 323 | */ | ||
| 324 | unregister_hw_breakpoint(bp); | ||
| 325 | 366 | ||
| 326 | return perf_event_create_kernel_counter(attr, -1, tsk->pid, triggered); | 367 | perf_event_disable(bp); |
| 368 | |||
| 369 | bp->attr.bp_addr = attr->bp_addr; | ||
| 370 | bp->attr.bp_type = attr->bp_type; | ||
| 371 | bp->attr.bp_len = attr->bp_len; | ||
| 372 | |||
| 373 | if (attr->disabled) | ||
| 374 | goto end; | ||
| 375 | |||
| 376 | err = arch_validate_hwbkpt_settings(bp, bp->ctx->task); | ||
| 377 | if (!err) | ||
| 378 | perf_event_enable(bp); | ||
| 379 | |||
| 380 | if (err) { | ||
| 381 | bp->attr.bp_addr = old_addr; | ||
| 382 | bp->attr.bp_type = old_type; | ||
| 383 | bp->attr.bp_len = old_len; | ||
| 384 | if (!bp->attr.disabled) | ||
| 385 | perf_event_enable(bp); | ||
| 386 | |||
| 387 | return err; | ||
| 388 | } | ||
| 389 | |||
| 390 | end: | ||
| 391 | bp->attr.disabled = attr->disabled; | ||
| 392 | |||
| 393 | return 0; | ||
| 327 | } | 394 | } |
| 328 | EXPORT_SYMBOL_GPL(modify_user_hw_breakpoint); | 395 | EXPORT_SYMBOL_GPL(modify_user_hw_breakpoint); |
| 329 | 396 | ||
| @@ -348,7 +415,7 @@ EXPORT_SYMBOL_GPL(unregister_hw_breakpoint); | |||
| 348 | */ | 415 | */ |
| 349 | struct perf_event ** | 416 | struct perf_event ** |
| 350 | register_wide_hw_breakpoint(struct perf_event_attr *attr, | 417 | register_wide_hw_breakpoint(struct perf_event_attr *attr, |
| 351 | perf_callback_t triggered) | 418 | perf_overflow_handler_t triggered) |
| 352 | { | 419 | { |
| 353 | struct perf_event **cpu_events, **pevent, *bp; | 420 | struct perf_event **cpu_events, **pevent, *bp; |
| 354 | long err; | 421 | long err; |
| @@ -358,7 +425,8 @@ register_wide_hw_breakpoint(struct perf_event_attr *attr, | |||
| 358 | if (!cpu_events) | 425 | if (!cpu_events) |
| 359 | return ERR_PTR(-ENOMEM); | 426 | return ERR_PTR(-ENOMEM); |
| 360 | 427 | ||
| 361 | for_each_possible_cpu(cpu) { | 428 | get_online_cpus(); |
| 429 | for_each_online_cpu(cpu) { | ||
| 362 | pevent = per_cpu_ptr(cpu_events, cpu); | 430 | pevent = per_cpu_ptr(cpu_events, cpu); |
| 363 | bp = perf_event_create_kernel_counter(attr, cpu, -1, triggered); | 431 | bp = perf_event_create_kernel_counter(attr, cpu, -1, triggered); |
| 364 | 432 | ||
| @@ -369,18 +437,20 @@ register_wide_hw_breakpoint(struct perf_event_attr *attr, | |||
| 369 | goto fail; | 437 | goto fail; |
| 370 | } | 438 | } |
| 371 | } | 439 | } |
| 440 | put_online_cpus(); | ||
| 372 | 441 | ||
| 373 | return cpu_events; | 442 | return cpu_events; |
| 374 | 443 | ||
| 375 | fail: | 444 | fail: |
| 376 | for_each_possible_cpu(cpu) { | 445 | for_each_online_cpu(cpu) { |
| 377 | pevent = per_cpu_ptr(cpu_events, cpu); | 446 | pevent = per_cpu_ptr(cpu_events, cpu); |
| 378 | if (IS_ERR(*pevent)) | 447 | if (IS_ERR(*pevent)) |
| 379 | break; | 448 | break; |
| 380 | unregister_hw_breakpoint(*pevent); | 449 | unregister_hw_breakpoint(*pevent); |
| 381 | } | 450 | } |
| 451 | put_online_cpus(); | ||
| 452 | |||
| 382 | free_percpu(cpu_events); | 453 | free_percpu(cpu_events); |
| 383 | /* return the error if any */ | ||
| 384 | return ERR_PTR(err); | 454 | return ERR_PTR(err); |
| 385 | } | 455 | } |
| 386 | EXPORT_SYMBOL_GPL(register_wide_hw_breakpoint); | 456 | EXPORT_SYMBOL_GPL(register_wide_hw_breakpoint); |
