Work around the halt/wakeup race in barrier_halt_wait() (issue #13)

There is an unavoidable race between one core halting after decrementing
the barrier count and another core sending it the wakeup NMI. This can
only occur if the core sending the wakeup is running at many times the
speed of the core halting, but it has been observed on an Intel Icelake
mobile processor.
This commit is contained in:
Martin Whitaker
2022-03-08 23:14:26 +00:00
parent 0076e63885
commit f6da06b117
2 changed files with 48 additions and 5 deletions

View File

@@ -66,10 +66,29 @@ void barrier_halt_wait(barrier_t *barrier)
local_flag_t *waiting_flags = local_flags(barrier->flag_num);
int my_cpu = smp_my_cpu_num();
waiting_flags[my_cpu].flag = true;
if (__sync_sub_and_fetch(&barrier->count, 1) != 0) {
__asm__ __volatile__ ("hlt");
return;
}
//
// There is a small window of opportunity for the wakeup signal to arrive
// between us decrementing the barrier count and halting. So code the
// following in assembler, both to ensure the window of opportunity is as
// small as possible, and also to allow us to detect and skip over the
// halt in the interrupt handler.
//
// if (__sync_sub_and_fetch(&barrier->count, 1) != 0) {
// __asm__ __volatile__ ("hlt");
// return;
// }
//
__asm__ goto ("\t"
"lock decl %0 \n\t"
"je 0f \n\t"
"hlt \n\t"
"jmp %l[end] \n"
"0: \n"
: /* no outputs */
: "m" (barrier->count)
: /* no clobbers */
: end
);
// Last one here, so reset the barrier and wake the others.
barrier->count = barrier->num_threads;
__sync_synchronize();
@@ -80,4 +99,6 @@ void barrier_halt_wait(barrier_t *barrier)
smp_send_nmi(cpu_num);
}
}
end:
return;
}