116 lines
2.6 KiB
C++
116 lines
2.6 KiB
C++
/*! \file
|
|
* \brief Guard implementation
|
|
*/
|
|
|
|
#include "guard.h"
|
|
|
|
#include "../arch/cache.h"
|
|
#include "../arch/core.h"
|
|
#include "../debug/assert.h" // IWYU pragma: keep
|
|
#include "../debug/output.h"
|
|
#include "../object/bbuffer.h"
|
|
#include "epilogues.h"
|
|
#include "../debug/copystream.h"
|
|
|
|
//! \brief The protected data for the epilogue level
|
|
static Vault global_vault;
|
|
|
|
//! \brief State of the epilogue level (per core)
|
|
struct State {
|
|
//! \brief lists of pending epilogues
|
|
BBuffer<Epilogue, 32> epilogue_queue;
|
|
//! \brief indicates whether the epilogue level is entered
|
|
bool epi_flag = false;
|
|
} __attribute__((aligned(CACHE_LINE_SIZE)));
|
|
|
|
constinit State state = {};
|
|
|
|
static inline State &get_state() { return state; }
|
|
|
|
Vault::Vault() {}
|
|
|
|
Guarded::~Guarded() { Guard::leave(); }
|
|
|
|
Guarded Guard::enter() {
|
|
assert(!get_state().epi_flag);
|
|
|
|
// Mark entry of epilogue level on this core
|
|
get_state().epi_flag = true;
|
|
|
|
return Guarded(global_vault);
|
|
}
|
|
|
|
void Guard::leave() {
|
|
// Make sure that we've performed an enter before.
|
|
assert(get_state().epi_flag);
|
|
|
|
Vault &vault = global_vault;
|
|
|
|
bool status;
|
|
while (true) {
|
|
status = Core::Interrupt::disable();
|
|
|
|
// Get item from epilogue queue (if any)
|
|
Epilogue iter;
|
|
if (!get_state().epilogue_queue.consume(iter)) {
|
|
break;
|
|
}
|
|
|
|
// Interrupts shall be restored before processing the epilogue
|
|
Core::Interrupt::restore(status);
|
|
|
|
// Process epilogue
|
|
iter(vault);
|
|
}
|
|
|
|
// leave the epilogue level this core
|
|
get_state().epi_flag = false;
|
|
|
|
// Restore previous interrupt state
|
|
Core::Interrupt::restore(status);
|
|
}
|
|
|
|
void Guard::relay(Epilogue handler) {
|
|
if (get_state().epi_flag) {
|
|
// Enqueue epilogue
|
|
bool success = get_state().epilogue_queue.produce(handler);
|
|
if (!success) DBG << "Dropped epilogue!" << endl;
|
|
} else {
|
|
// Mark entry of epilogue level on this core
|
|
get_state().epi_flag = true;
|
|
|
|
// Enable interrupts (interrupt_handler should already have sent the ACK
|
|
// IRQ via LAPIC)
|
|
Core::Interrupt::enable();
|
|
|
|
// We are, at this point, in the prologue level (E1), so this is
|
|
// allowed:
|
|
Vault &vault = global_vault;
|
|
|
|
// Process epilogue
|
|
handler(vault);
|
|
|
|
// Make sure pending epilogues are executed
|
|
while (true) {
|
|
// Disable Interrupts while accessing the epilogue queue
|
|
Core::Interrupt::disable();
|
|
Epilogue iter;
|
|
if (!get_state().epilogue_queue.consume(iter)) {
|
|
break;
|
|
}
|
|
|
|
// Interrupts shall be enabled before processing the epilogue
|
|
Core::Interrupt::enable();
|
|
|
|
// Process epilogue
|
|
iter(vault);
|
|
}
|
|
Core::Interrupt::disable();
|
|
|
|
// leave the epilogue level this core
|
|
get_state().epi_flag = false;
|
|
}
|
|
}
|
|
|
|
const Vault &Guard::unsafeConstAccess() { return global_vault; }
|