Questions about kprobes implementation on SMP and preemptible kernels
Quentin Barnes
qbarnes@urbana.css.mot.com
Mon Jan 29 05:30:00 GMT 2007
On Mon, Jan 29, 2007 at 09:51:40AM +0530, Ananth N Mavinakayanahalli wrote:
>On Sun, Jan 28, 2007 at 04:31:51PM -0600, Quentin Barnes wrote:
>> The point I was raising specifically about kprobe_exceptions_notify()
>> is special. Preemption must be held disabled from the point of the
>> exception to when kprobe_exceptions_notify() is entered. If it is
>> not held disabled, kprobe_running() can return the wrong processor.
>> A very serious kprobes bug!
>>
>> The macro kprobe_running() invokes __get_cpu_var() which invokes
>> smp_processor_id() which can invoke debug_smp_processor_id(). The
>> function debug_smp_processor_id() has sanity checks to ensure that
>> the CPU thread is bound in various ways including making sure that
>> preempt_count is non-zero or that interrupts are disabled. If the
>> CPU isn't bound, debug_smp_processor_id declares a bug and logs
>> the state pointing out the offending function.
>>
>> What I think was going on was that kprobe_exceptions_notify() was
>> causing these log messages on some architectures. What I suspect
>> is that rather than fixing the real bug by ensuring that preemption
>> was properly held disabled in a continuous chain from the point of
>> the exception through to invoking kprobe_exceptions_notify(), the
>> person buried the bug by adding the calls to preempt_disable()/
>> preempt_enable() in kprobe_exceptions_notify() to defeat the checks
>> in debug_smp_processor_id() -- and the original SMP bug is still there!
>>
>> Does my explanation make sense? Is this what could have happened?
>
>It does, but you are missing the more important point. If you notice the
>placement of the notify_page_fault() (notify_die() before the page fault
>notifier was introduced) in do_page_fault(), the notifier would get
>invoked on _every_ page fault, irrespective of whether the fault was
>caused due to a faulty kprobe handler or not.
I follow that. It's a general notification mechanism which would
require that placement.
>If the page fault was due
>to a kprobe handler, we have already disabled preemption and hence we
>wouldn't have needed the preempt_(dis/en)able pair in
>kprobe_exceptions_notify().
I follow that too.
>However, if the fault was a legitimate,
>non-kprobes induced one, you'd still enter kprobes_exceptions_notify()
>and there is no way to tell if the fault happened in a preempt disabled
>section or not.
>
>I do agree with your reasoning *only if* you can ensure that the callout
>from the page fault code to kprobes_exceptions_notify() happens only on
>account of a page fault triggered by a buggy kprobe handler. In the
>absence of such a guarantee, we have the preempt_* calls.
Ah, I think I see your point now. It's very subtle.
My concern was a fault occurring due to a non-kprobes event could be
incorrectly picked up as a kprobes event because we could end up executing
on another random processor by the time we enter kprobe_exceptions_notify()
which might just have a kprobes active on it. _But that can never
happen_. Whenever a kprobe is active, preemption is always disabled
throughout the life of that kprobe on that processor which as a side-effect
takes that processor out of the pool of available processors for
scheduling. Whatever processor we might get cannot be one that can
ever return a positive result from kprobe_running(). Got it! Thanks!
In my port, to make the code behavior of kprobe_exceptions_notify()
not so subtle, I just changed it to check preemptibility before calling
kprobe_running():
====
int __kprobes kprobe_exceptions_notify(struct notifier_block *self,
unsigned long val, void *data)
{
struct die_args *args = (struct die_args *)data;
struct pt_regs *regs = args->regs;
int ret = NOTIFY_DONE;
switch (val) {
case DIE_TRANS_FAULT:
case DIE_PAGE_FAULT:
/* To be potentially processing a kprobe fault, we have
* to be non-preemptible, not in user space, and not
* executing thumb code. */
if (!preemptible() &&
regs && !user_mode(regs) && !thumb_mode(regs) &&
kprobe_running() &&
kprobe_fault_handler(regs, args->fsr)) {
ret = NOTIFY_STOP;
}
break;
default:
break;
}
return ret;
}
====
I think this algorithm is clearer than bumping "preempt_count" up
and down.
>Ananth
Quentin
More information about the Systemtap
mailing list