blob: 39d01e60135d517393c50b9831b5d45269e1ed1a [file] [log] [blame]
// Copyright 2017 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 "kernel/percpu.h"
#include <debug.h>
#include <lib/counters.h>
#include <lib/system-topology.h>
#include <arch/ops.h>
#include <fbl/alloc_checker.h>
#include <ffl/string.h>
#include <kernel/align.h>
#include <kernel/cpu_distance_map.h>
#include <kernel/lockdep.h>
#include <ktl/algorithm.h>
#include <ktl/unique_ptr.h>
#include <lk/init.h>
#include <lockdep/lockdep.h>
#include <ktl/enforce.h>
decltype(percpu::boot_processor_) percpu::boot_processor_{};
percpu* percpu::secondary_processors_{nullptr};
percpu* percpu::boot_index_[1]{percpu::boot_processor_.GetAddressUnchecked()};
percpu** percpu::processor_index_{percpu::boot_index_};
size_t percpu::processor_count_{1};
percpu::percpu(cpu_num_t cpu_num) {
scheduler.this_cpu_ = cpu_num;
idle_power_thread.this_cpu_ = cpu_num;
#if WITH_LOCK_DEP
// Initialize the lockdep tracking state for irq context.
auto* state = reinterpret_cast<lockdep::ThreadLockState*>(&lock_state);
lockdep::SystemInitThreadLockState(state);
#endif
counters = CounterArena().CpuData(cpu_num);
}
void percpu::InitializeBoot() {
boot_processor_.Initialize(0);
// Install a pointer to the boot CPU's high-level percpu struct in its low-level percpu struct.
arch_setup_percpu(0, &boot_processor_.Get());
}
void percpu::InitializeSecondariesBegin(uint32_t /*init_level*/) {
processor_count_ = CpuDistanceMap::Get().cpu_count();
DEBUG_ASSERT(processor_count_ != 0);
const size_t index_size = sizeof(percpu*) * processor_count_;
processor_index_ = static_cast<percpu**>(memalign(MAX_CACHE_LINE, index_size));
processor_index_[0] = &boot_processor_.Get();
static_assert((MAX_CACHE_LINE % alignof(struct percpu)) == 0);
const size_t bytes = sizeof(percpu) * (processor_count_ - 1);
if (bytes) {
secondary_processors_ = static_cast<percpu*>(memalign(MAX_CACHE_LINE, bytes));
// TODO: Remove the need to zero memory by fully initializing all of percpu
// members in the constructor / default initializers.
memset(secondary_processors_, 0, bytes);
// Construct the secondary percpu instances and add them to the index.
for (cpu_num_t i = 1; i < processor_count_; i++) {
processor_index_[i] = &secondary_processors_[i - 1];
new (&secondary_processors_[i - 1]) percpu{i};
}
}
// Compute the performance scale of each CPU.
{
fbl::AllocChecker checker;
ktl::unique_ptr<uint8_t[]> performance_class{new (&checker) uint8_t[processor_count_]};
if (checker.check()) {
uint8_t max_performance_class = 0;
for (cpu_num_t i = 0; i < processor_count_; i++) {
performance_class[i] = system_topology::GetPerformanceClass(i);
max_performance_class = ktl::max(performance_class[i], max_performance_class);
}
dprintf(INFO, "CPU performance scales:\n");
for (cpu_num_t i = 0; i < processor_count_; i++) {
const SchedPerformanceScale scale =
ffl::FromRatio(performance_class[i] + 1, max_performance_class + 1);
processor_index_[i]->scheduler.InitializePerformanceScale(scale);
CpuSearchSet::SetPerfScale(i, scale.raw_value());
dprintf(INFO, "CPU %2u: %s\n", i, Format(scale).c_str());
}
} else {
dprintf(INFO, "Failed to allocate temp buffer, using default performance for all CPUs\n");
}
}
// Determine the clusters before initializing the CPU search sets.
CpuSearchSet::AutoCluster(processor_count_);
CpuSearchSet::DumpClusters();
// Initialize the search set for each CPU.
dprintf(INFO, "CPU search order:\n");
for (cpu_num_t i = 0; i < processor_count_; i++) {
processor_index_[i]->search_set.Initialize(i, processor_count_);
processor_index_[i]->search_set.Dump();
const size_t cluster = processor_index_[i]->search_set.cluster();
processor_index_[i]->scheduler.cluster_ = cluster;
}
}
void percpu::InitializeSecondaryFinish() {
const cpu_num_t cpu_num = arch_curr_cpu_num();
DEBUG_ASSERT(cpu_num < processor_count());
arch_setup_percpu(cpu_num, processor_index_[cpu_num]);
}
// Allocate secondary percpu instances before booting other processors, after
// vm and system topology are initialized.
LK_INIT_HOOK(percpu_heap_init, percpu::InitializeSecondariesBegin, LK_INIT_LEVEL_KERNEL)