hw-breakpoints: Modify breakpoints without unregistering them
Currently, when ptrace needs to modify a breakpoint, like disabling it, changing its address, type or len, it calls modify_user_hw_breakpoint(). This latter will perform the heavy and racy task of unregistering the old breakpoint and registering a new one. This is racy as someone else might steal the reserved breakpoint slot under us, which is undesired as the breakpoint is only supposed to be modified, sometimes in the middle of a debugging workflow. We don't want our slot to be stolen in the middle. So instead of unregistering/registering the breakpoint, just disable it while we modify its breakpoint fields and re-enable it after if necessary. Signed-off-by: Frederic Weisbecker <fweisbec@gmail.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Prasad <prasad@linux.vnet.ibm.com> LKML-Reference: <1260347148-5519-1-git-send-regression-fweisbec@gmail.com> Signed-off-by: Ingo Molnar <mingo@elte.hu>
This commit is contained in:

committed by
Ingo Molnar

parent
c937fe20cb
commit
44234adcdc
@@ -320,18 +320,40 @@ EXPORT_SYMBOL_GPL(register_user_hw_breakpoint);
|
||||
* @triggered: callback to trigger when we hit the breakpoint
|
||||
* @tsk: pointer to 'task_struct' of the process to which the address belongs
|
||||
*/
|
||||
struct perf_event *
|
||||
modify_user_hw_breakpoint(struct perf_event *bp, struct perf_event_attr *attr)
|
||||
int modify_user_hw_breakpoint(struct perf_event *bp, struct perf_event_attr *attr)
|
||||
{
|
||||
/*
|
||||
* FIXME: do it without unregistering
|
||||
* - We don't want to lose our slot
|
||||
* - If the new bp is incorrect, don't lose the older one
|
||||
*/
|
||||
unregister_hw_breakpoint(bp);
|
||||
u64 old_addr = bp->attr.bp_addr;
|
||||
int old_type = bp->attr.bp_type;
|
||||
int old_len = bp->attr.bp_len;
|
||||
int err = 0;
|
||||
|
||||
return perf_event_create_kernel_counter(attr, -1, bp->ctx->task->pid,
|
||||
bp->overflow_handler);
|
||||
perf_event_disable(bp);
|
||||
|
||||
bp->attr.bp_addr = attr->bp_addr;
|
||||
bp->attr.bp_type = attr->bp_type;
|
||||
bp->attr.bp_len = attr->bp_len;
|
||||
|
||||
if (attr->disabled)
|
||||
goto end;
|
||||
|
||||
err = arch_validate_hwbkpt_settings(bp, bp->ctx->task);
|
||||
if (!err)
|
||||
perf_event_enable(bp);
|
||||
|
||||
if (err) {
|
||||
bp->attr.bp_addr = old_addr;
|
||||
bp->attr.bp_type = old_type;
|
||||
bp->attr.bp_len = old_len;
|
||||
if (!bp->attr.disabled)
|
||||
perf_event_enable(bp);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
end:
|
||||
bp->attr.disabled = attr->disabled;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(modify_user_hw_breakpoint);
|
||||
|
||||
|
Reference in New Issue
Block a user