Halt CPU cores that are not active in a test.

This saves a lot of power when performing sequential tests.
This commit is contained in:
Martin Whitaker 2022-02-12 16:16:56 +00:00
parent de32bc5412
commit 2bf1623733
6 changed files with 60 additions and 6 deletions

View File

@ -91,6 +91,7 @@ cpu_state_t cpu_state[MAX_CPUS];
bool enable_temperature = false;
bool enable_trace = false;
bool enable_halt = true;
bool pause_at_start = true;
@ -113,6 +114,8 @@ static void parse_option(const char *option, const char *params)
}
} else if (strncmp(option, "nopause", 8) == 0) {
pause_at_start = false;
} else if (strncmp(option, "nohalt", 7) == 0) {
enable_halt = false;
} else if (strncmp(option, "smp", 4) == 0) {
smp_enabled = true;
}

View File

@ -38,6 +38,7 @@ extern cpu_state_t cpu_state[MAX_CPUS];
extern bool enable_temperature;
extern bool enable_trace;
extern bool enable_halt;
extern bool pause_at_start;

View File

@ -24,6 +24,8 @@
// Constants
//------------------------------------------------------------------------------
#define HLT_OPCODE 0xf4
#ifdef __x86_64__
#define REG_PREFIX 'r'
#define REG_DIGITS 16
@ -115,12 +117,17 @@ void interrupt(struct trap_regs *trap_regs)
#endif
}
#if REPORT_PARITY_ERRORS
if (trap_regs->vect == 2) {
uint8_t *pc = (uint8_t *)trap_regs->ip;
if (pc[-1] == HLT_OPCODE) {
// Assume this is a wakeup signal sent via IPI.
return;
}
#if REPORT_PARITY_ERRORS
parity_error();
return;
}
#endif
}
spin_lock(error_mutex);

View File

@ -71,6 +71,8 @@ static uintptr_t high_load_addr;
static barrier_t *start_barrier = NULL;
static spinlock_t *halt_mutex = NULL;
static volatile bool start_run = false;
static volatile bool start_pass = false;
static volatile bool start_test = false;
@ -82,6 +84,8 @@ static volatile int window_num = 0;
static volatile uintptr_t window_start = 0;
static volatile uintptr_t window_end = 0;
static volatile bool halt_if_inactive = false;
static volatile int test_stage = 0;
//------------------------------------------------------------------------------
@ -247,6 +251,7 @@ static void global_init(void)
start_barrier = smp_alloc_barrier(1);
run_barrier = smp_alloc_barrier(1);
halt_mutex = smp_alloc_mutex();
error_mutex = smp_alloc_mutex();
start_run = true;
@ -254,7 +259,7 @@ static void global_init(void)
restart = false;
}
static void setup_vm_map(uintptr_t win_start, uintptr_t win_end)
static size_t setup_vm_map(uintptr_t win_start, uintptr_t win_end)
{
vm_map_size = 0;
@ -266,11 +271,12 @@ static void setup_vm_map(uintptr_t win_start, uintptr_t win_end)
win_end = pm_limit_upper;
}
if (win_start >= win_end) {
return;
return 0;
}
// Now initialise the virtual memory map with the intersection
// of the window and the physical memory segments.
size_t num_mapped_pages = 0;
for (int i = 0; i < pm_map_size; i++) {
uintptr_t seg_start = pm_map[i].start;
uintptr_t seg_end = pm_map[i].end;
@ -281,12 +287,14 @@ static void setup_vm_map(uintptr_t win_start, uintptr_t win_end)
seg_end = win_end;
}
if (seg_start < seg_end && seg_start < win_end && seg_end > win_start) {
num_mapped_pages += seg_end - seg_start;
vm_map[vm_map_size].pm_base_addr = seg_start;
vm_map[vm_map_size].start = first_word_mapping(seg_start);
vm_map[vm_map_size].end = last_word_mapping(seg_end - 1, sizeof(testword_t));
vm_map_size++;
}
}
return num_mapped_pages;
}
static void test_all_windows(int my_cpu)
@ -365,11 +373,24 @@ static void test_all_windows(int my_cpu)
window_start = window_end;
window_end += VM_WINDOW_SIZE;
}
setup_vm_map(window_start, window_end);
size_t num_mapped_pages = setup_vm_map(window_start, window_end);
// There is a significant overhead in restarting halted CPU cores, so only enable
// halting if the memory present in the window is a reasonable size.
halt_if_inactive = enable_halt && num_enabled_cpus > num_active_cpus && num_mapped_pages > PAGE_C(16,MB);
}
BARRIER;
if (!i_am_active) {
// Guard against a race between halting and being woken up.
spin_lock(halt_mutex);
if (halt_if_inactive) {
cpu_state[my_cpu] = CPU_STATE_HALTED;
spin_unlock(halt_mutex);
__asm__ __volatile__ ("hlt");
cpu_state[my_cpu] = CPU_STATE_RUNNING;
} else {
spin_unlock(halt_mutex);
}
continue;
}
@ -394,6 +415,16 @@ static void test_all_windows(int my_cpu)
}
if (i_am_master) {
if (!dummy_run && halt_if_inactive) {
spin_lock(halt_mutex);
halt_if_inactive = false;
spin_unlock(halt_mutex);
for (int cpu_num = 0; cpu_num < num_available_cpus; cpu_num++) {
if (cpu_state[cpu_num] == CPU_STATE_HALTED) {
smp_send_nmi(cpu_num);
}
}
}
window_num++;
}
} while (window_end < pm_map[pm_map_size - 1].end);

View File

@ -740,6 +740,11 @@ int smp_start(cpu_state_t cpu_state[MAX_CPUS])
#endif
}
bool smp_send_nmi(int cpu_num)
{
return send_ipi(cpu_num_to_apic_id[cpu_num], 0, 0, APIC_DELMODE_NMI, 0, 200);
}
int smp_my_cpu_num(void)
{
return num_available_cpus > 1 ? apic_id_to_cpu_num[my_apic_id()] : 0;

View File

@ -26,7 +26,8 @@
typedef enum __attribute__ ((packed)) {
CPU_STATE_DISABLED = 0,
CPU_STATE_ENABLED = 1,
CPU_STATE_RUNNING = 2
CPU_STATE_RUNNING = 2,
CPU_STATE_HALTED = 3
} cpu_state_t;
/*
@ -55,6 +56,12 @@ void smp_init(bool smp_enable);
*/
int smp_start(cpu_state_t cpu_state[MAX_CPUS]);
/*
* Sends a non-maskable interrupt to the CPU core whose ordinal number
* is cpu_num.
*/
bool smp_send_nmi(int cpu_num);
/*
* Returns the ordinal number of the calling CPU core.
*/