Skip to content

Commit

Permalink
[POWERPC] Fix missed hardware breakpoints across multiple threads
Browse files Browse the repository at this point in the history
There is a bug in the powerpc DABR (data access breakpoint) handling,
which can result in us missing breakpoints if several threads are trying
to break on the same address.

The circumstances are that do_page_fault() calls do_dabr(), this clears
the DABR (sets it to 0) and sets up the signal which will report to
userspace that the DABR was hit. The do_signal() code will restore the DABR
value on the way out to userspace.

If we reschedule before calling do_signal(), __switch_to() will check the
cached DABR value and compare it to the new thread's value, if they match
we don't set the DABR in hardware.

So if two threads have the same DABR value, and we schedule from one to
the other after taking the interrupt for the first thread hitting the DABR,
the second thread will run without the DABR set in hardware.

The cleanest fix is to move the cache update into set_dabr(), that way we
can't forget to do it.

Reported-by: Jan Kratochvil <jan.kratochvil@redhat.com>
Signed-off-by: Michael Ellerman <michael@ellerman.id.au>
Signed-off-by: Paul Mackerras <paulus@samba.org>
  • Loading branch information
Michael Ellerman authored and Paul Mackerras committed Mar 28, 2008
1 parent 5c29934 commit a2ceff5
Showing 1 changed file with 5 additions and 5 deletions.
10 changes: 5 additions & 5 deletions arch/powerpc/kernel/process.c
Original file line number Diff line number Diff line change
Expand Up @@ -241,8 +241,12 @@ void discard_lazy_cpu_state(void)
}
#endif /* CONFIG_SMP */

static DEFINE_PER_CPU(unsigned long, current_dabr);

int set_dabr(unsigned long dabr)
{
__get_cpu_var(current_dabr) = dabr;

#ifdef CONFIG_PPC_MERGE /* XXX for now */
if (ppc_md.set_dabr)
return ppc_md.set_dabr(dabr);
Expand All @@ -259,8 +263,6 @@ int set_dabr(unsigned long dabr)
DEFINE_PER_CPU(struct cpu_usage, cpu_usage_array);
#endif

static DEFINE_PER_CPU(unsigned long, current_dabr);

struct task_struct *__switch_to(struct task_struct *prev,
struct task_struct *new)
{
Expand Down Expand Up @@ -325,10 +327,8 @@ struct task_struct *__switch_to(struct task_struct *prev,

#endif /* CONFIG_SMP */

if (unlikely(__get_cpu_var(current_dabr) != new->thread.dabr)) {
if (unlikely(__get_cpu_var(current_dabr) != new->thread.dabr))
set_dabr(new->thread.dabr);
__get_cpu_var(current_dabr) = new->thread.dabr;
}

new_thread = &new->thread;
old_thread = &current->thread;
Expand Down

0 comments on commit a2ceff5

Please sign in to comment.