blob: f5097a98b31ee8c180a61b6c6469a7f81bf8feff [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include <err.h>
#include <inttypes.h>
#include <lib/unittest/unittest.h>
#include <platform.h>
#include <zircon/types.h>
#include <kernel/mp.h>
#include <kernel/thread.h>
#include "tests.h"
static int resume_cpu_test_thread(void* arg) {
*reinterpret_cast<uint*>(arg) = arch_curr_cpu_num();
return 0;
}
// "Unplug" online secondary (non-BOOT) cores
static zx_status_t unplug_all_cores(thread_t** leaked_threads) {
cpu_mask_t cpumask = mp_get_online_mask() & ~cpu_num_to_mask(BOOT_CPU_ID);
return mp_unplug_cpu_mask(cpumask, leaked_threads);
}
static zx_status_t hotplug_core(uint i) {
cpu_mask_t cpumask = cpu_num_to_mask(i);
return mp_hotplug_cpu_mask(cpumask);
}
static unsigned get_num_cpus_online() {
unsigned count = 0;
cpu_mask_t online = mp_get_online_mask();
while (online) {
online >>= 1;
++count;
}
return count;
}
// Unplug all cores (except for Boot core), then hotplug
// the cores one by one and make sure that we can schedule
// tasks on that core.
static bool mp_hotplug_test() {
BEGIN_TEST;
// Hotplug is only implemented for x64.
#if !defined(__x86_64__)
printf("skipping test mp_hotplug, hotplug only suported on x64\n");
END_TEST;
#endif
uint num_cores = get_num_cpus_online();
if (num_cores < 2) {
printf("skipping test mp_hotplug, not enough online cpus\n");
END_TEST;
}
thread_migrate_to_cpu(BOOT_CPU_ID);
// "Unplug" online secondary (non-BOOT) cores
thread_t* leaked_threads[SMP_MAX_CPUS] = {};
ASSERT_EQ(unplug_all_cores(leaked_threads), ZX_OK, "unplugging all cores failed");
for (uint i = 0; i < num_cores; i++) {
if (i == BOOT_CPU_ID) {
continue;
}
// hotplug this core.
ASSERT_EQ(hotplug_core(i), ZX_OK, "hotplugging core failed");
// Create a thread, affine it to the core just hotplugged
// and make sure the thread does get scheduled there.
uint running_core;
thread_t* nt = thread_create("resume-test-thread", resume_cpu_test_thread, &running_core,
DEFAULT_PRIORITY);
ASSERT_NE(nullptr, nt, "Thread create failed");
thread_set_cpu_affinity(nt, cpu_num_to_mask(i));
thread_resume(nt);
ASSERT_EQ(thread_join(nt, nullptr, ZX_TIME_INFINITE), ZX_OK, "thread join failed");
ASSERT_EQ(i, running_core, "Thread not running on hotplugged core");
}
for (unsigned i = 0; i < fbl::count_of(leaked_threads); i++) {
if (leaked_threads[i]) {
thread_forget(leaked_threads[i]);
}
}
END_TEST;
}
// The call to x86_bootstrap16_acquire() from the mp_hotplug_cpu_mask()
// fails because the PMM doesn't support allocations to low 4GB pages (ZX-978).
// Enable these tests once that issue is fixed.
// See FLK-387. (Call to x86_bootstrap16_acquire() from mp_hotplug_cpu_mask()).
// UNITTEST_START_TESTCASE(mp_hotplug_tests)
// UNITTEST("test unplug and hotplug cores one by one", mp_hotplug_test)
// UNITTEST_END_TESTCASE(mp_hotplug_tests, "hotplug",
// "Tests for unplugging and hotplugging cores");