Re: [PATCH 3/2] perf/hw_breakpoint: Remove superfluous bp->attr.disabled = 0 new attr has disabled set

From: Jiri Olsa
Date: Tue Aug 07 2018 - 10:39:54 EST


On Tue, Aug 07, 2018 at 11:10:08AM +0200, Oleg Nesterov wrote:
> On 08/07, Jiri Olsa wrote:
> >
> > On Mon, Aug 06, 2018 at 06:34:36PM +0200, Oleg Nesterov wrote:
> > > > --- a/kernel/events/hw_breakpoint.c
> > > > +++ b/kernel/events/hw_breakpoint.c
> > > > @@ -526,10 +526,9 @@ int modify_user_hw_breakpoint(struct perf_event *bp, struct perf_event_attr *att
> > > > if (err)
> > > > return err;
> > > >
> > > > - if (!attr->disabled) {
> > > > + if (!attr->disabled)
> > > > perf_event_enable(bp);
> > > > - bp->attr.disabled = 0;
> > > > - }
> > > > +
> > >
> > > Yes, but again, this still looks confusing.
> > >
> > > IMO, we should either remove "bp->attr.disabled = attr->disabled" in
> > > modify_user_hw_breakpoint_check() because bp->attr.disabled is not really
> > > used, or we should set bp->attr.disabled = 1 on failure just for consistency.
> > >
> > >
> > > Hmm... actually ptrace_get_dr7() checks ->attr.disabled, so we can hit
> > > WARN_ON(second_pass) in ptrace_write_dr7() in case when attr.disabled is
> > > falsely 0 because modify_user_hw_breakpoint_check() failed before?
> >
> > hum, I can't see how modify_user_hw_breakpoint_check could falsely set disabled
> > new attr stuff is copied once all checks passed
>
> Hmm. So modify_user_hw_breakpoint() does perf_event_disable(bp) first and afaics
> this doesn't set bp->attr.disabled = 1.
>
> If modify_user_hw_breakpoint_check() fails after that we do not update bp->attr,
> so bp->attr.disabled is still zero while this bp is actually disabled.

this seems to be cared of the 2nd pass restore logic, where the
old dr7 value will make the breakpoint to be enabled again:

bp0.attr.disabled = 0 (enabled)

ptrace_write_dr7(newdr7)
dr7 = newdr7
ptrace_modify_breakpoint(bp0)
perf_event_disable -> bp0 disabled, but bp0.attr.disabled = 0
modify_user_hw_breakpoint_check fails
return -1 -> leaving with bp0 disabled, bp0.attr.disabled = 0

dr7 = olddr7 -> bp0 bit enabled
second_pass = true
ptrace_modify_breakpoint -> attr->disabled = 0
perf_event_disable
modify_user_hw_breakpoint_check
hw_breakpoint_copy_attr
perf_event_enable -> bp0 enabled, bp0.attr.disabled = 0



but this could be problem for ptrace_set_breakpoint_addr, because
the new address modification in modify_user_hw_breakpoint_check,
can fail and leave enabled breakpoint with disabled = 0

might be enough to manualy disable it in case of error like below
(squashed with my previou change) need to test this..

jirka


---
diff --git a/kernel/events/hw_breakpoint.c b/kernel/events/hw_breakpoint.c
index fb229d9c7f3c..eff7ab716139 100644
--- a/kernel/events/hw_breakpoint.c
+++ b/kernel/events/hw_breakpoint.c
@@ -523,13 +523,14 @@ int modify_user_hw_breakpoint(struct perf_event *bp, struct perf_event_attr *att
perf_event_disable(bp);

err = modify_user_hw_breakpoint_check(bp, attr, false);
- if (err)
+ if (err) {
+ bp->attr.disabled = 1;
return err;
+ }

- if (!attr->disabled) {
+ if (!attr->disabled)
perf_event_enable(bp);
- bp->attr.disabled = 0;
- }
+
return 0;
}
EXPORT_SYMBOL_GPL(modify_user_hw_breakpoint);
--
2.17.1