diff options
Diffstat (limited to 'kernel/hw_breakpoint.c')
| -rw-r--r-- | kernel/hw_breakpoint.c | 58 | 
1 files changed, 47 insertions, 11 deletions
| diff --git a/kernel/hw_breakpoint.c b/kernel/hw_breakpoint.c index 50dbd599958..967e66143e1 100644 --- a/kernel/hw_breakpoint.c +++ b/kernel/hw_breakpoint.c | |||
| @@ -243,38 +243,70 @@ static void toggle_bp_slot(struct perf_event *bp, bool enable) | |||
| 243 | * ((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, *)) | 
| 244 | * + max(per_cpu(nr_task_bp_pinned, *))) < HBP_NUM | 244 | * + max(per_cpu(nr_task_bp_pinned, *))) < HBP_NUM | 
| 245 | */ | 245 | */ | 
| 246 | int reserve_bp_slot(struct perf_event *bp) | 246 | static int __reserve_bp_slot(struct perf_event *bp) | 
| 247 | { | 247 | { | 
| 248 | struct bp_busy_slots slots = {0}; | 248 | struct bp_busy_slots slots = {0}; | 
| 249 | int ret = 0; | ||
| 250 | |||
| 251 | mutex_lock(&nr_bp_mutex); | ||
| 252 | 249 | ||
| 253 | fetch_bp_busy_slots(&slots, bp); | 250 | fetch_bp_busy_slots(&slots, bp); | 
| 254 | 251 | ||
| 255 | /* Flexible counters need to keep at least one slot */ | 252 | /* Flexible counters need to keep at least one slot */ | 
| 256 | if (slots.pinned + (!!slots.flexible) == HBP_NUM) { | 253 | if (slots.pinned + (!!slots.flexible) == HBP_NUM) | 
| 257 | ret = -ENOSPC; | 254 | return -ENOSPC; | 
| 258 | goto end; | ||
| 259 | } | ||
| 260 | 255 | ||
| 261 | toggle_bp_slot(bp, true); | 256 | toggle_bp_slot(bp, true); | 
| 262 | 257 | ||
| 263 | 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 | |||
| 264 | mutex_unlock(&nr_bp_mutex); | 269 | mutex_unlock(&nr_bp_mutex); | 
| 265 | 270 | ||
| 266 | return ret; | 271 | return ret; | 
| 267 | } | 272 | } | 
| 268 | 273 | ||
| 274 | static void __release_bp_slot(struct perf_event *bp) | ||
| 275 | { | ||
| 276 | toggle_bp_slot(bp, false); | ||
| 277 | } | ||
| 278 | |||
| 269 | void release_bp_slot(struct perf_event *bp) | 279 | void release_bp_slot(struct perf_event *bp) | 
| 270 | { | 280 | { | 
| 271 | mutex_lock(&nr_bp_mutex); | 281 | mutex_lock(&nr_bp_mutex); | 
| 272 | 282 | ||
| 273 | toggle_bp_slot(bp, false); | 283 | __release_bp_slot(bp); | 
| 274 | 284 | ||
| 275 | mutex_unlock(&nr_bp_mutex); | 285 | mutex_unlock(&nr_bp_mutex); | 
| 276 | } | 286 | } | 
| 277 | 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); | ||
| 307 | |||
| 308 | return 0; | ||
| 309 | } | ||
| 278 | 310 | ||
| 279 | int register_perf_hw_breakpoint(struct perf_event *bp) | 311 | int register_perf_hw_breakpoint(struct perf_event *bp) | 
| 280 | { | 312 | { | 
| @@ -296,6 +328,10 @@ int register_perf_hw_breakpoint(struct perf_event *bp) | |||
| 296 | if (!bp->attr.disabled || !bp->overflow_handler) | 328 | if (!bp->attr.disabled || !bp->overflow_handler) | 
| 297 | ret = arch_validate_hwbkpt_settings(bp, bp->ctx->task); | 329 | ret = arch_validate_hwbkpt_settings(bp, bp->ctx->task); | 
| 298 | 330 | ||
| 331 | /* if arch_validate_hwbkpt_settings() fails then release bp slot */ | ||
| 332 | if (ret) | ||
| 333 | release_bp_slot(bp); | ||
| 334 | |||
| 299 | return ret; | 335 | return ret; | 
| 300 | } | 336 | } | 
| 301 | 337 | ||
| @@ -324,8 +360,8 @@ EXPORT_SYMBOL_GPL(register_user_hw_breakpoint); | |||
| 324 | int modify_user_hw_breakpoint(struct perf_event *bp, struct perf_event_attr *attr) | 360 | int modify_user_hw_breakpoint(struct perf_event *bp, struct perf_event_attr *attr) | 
| 325 | { | 361 | { | 
| 326 | u64 old_addr = bp->attr.bp_addr; | 362 | u64 old_addr = bp->attr.bp_addr; | 
| 363 | u64 old_len = bp->attr.bp_len; | ||
| 327 | int old_type = bp->attr.bp_type; | 364 | int old_type = bp->attr.bp_type; | 
| 328 | int old_len = bp->attr.bp_len; | ||
| 329 | int err = 0; | 365 | int err = 0; | 
| 330 | 366 | ||
| 331 | perf_event_disable(bp); | 367 | perf_event_disable(bp); | 
