kernel: fix atomic operations

Atomic operations were previously broken. RISC-V breaks atomic
operations into multiple instructions, and there is some state kept
between those instructions. This state is not visible, and is internal
to the processor.

We were ignoring this state, which resulted in inconsistencies
throughout the system. The following sequence was possible:

1. A thread loads a value, and obtains the `reservation set`
2. A context switch happens on the following instruction
3. A different thread loads the value, modifies it, and continues
4. A third thread (or a different process) loads a value and obtains the
   `reservation set`
5. The original thread runs, sees the `reservation set` is held, and
   updates the value, clobbering what the second thread did in #3

A workaround for this is to perform a dummy `sc` as part of the context
switch. This invalidates any atomic operations that may be pending.

Signed-off-by: Sean Cross <sean@xobs.io>
This commit is contained in:
Sean Cross 2023-02-02 23:06:32 +08:00
parent 935b1a17a4
commit dc097e32d8
7 changed files with 20 additions and 0 deletions

View file

@ -154,6 +154,11 @@ _xous_resume_context:
move sp, a0
LOAD x1, 0*REGBYTES(sp)
// Clear any reservations, invalidating any atomic operations
#ifndef SKIP_MULTICORE
sc.w zero, x1, 0*REGBYTES(sp)
#endif
// Skip SP for now
LOAD x3, 2*REGBYTES(sp)
LOAD x4, 3*REGBYTES(sp)
@ -206,6 +211,21 @@ _xous_syscall_return_result:
csrw sepc, t0
LOAD x1, 0*REGBYTES(sp)
// An atomic operation is broken up into multiple instructions.
// A program first issues a `lr` instruction, which basically
// raises a flag saying that an atomic operation is now live. It
// then issues an `sc` instruction, and if the flag is still held
// it succeeds and clears the flag.
//
// This flag is a bit of state within the processor, and the
// RISC-V spec doesn't say how to access it. However, it is only
// one bit, and we can clear the bit by performing a dummy `sc`
// here. This will cause a failure in any atomic operation in the
// program we're switching to.
#ifndef SKIP_MULTICORE
sc.w zero, x1, 0*REGBYTES(sp)
#endif
// Skip SP for now
LOAD x3, 2*REGBYTES(sp)
LOAD x4, 3*REGBYTES(sp)