hw: re-implement kernel mutex

* Rename Kernel::Lock into Kernel::Mutex
* Replace Guard object by template function that expects
  lambda to handle re-entrance by same cpu

Ref genodelabs/genode#5425
This commit is contained in:
Stefan Kalkowski 2025-01-24 13:22:12 +01:00 committed by Christian Helmuth
parent c2cee1a885
commit 98032a2605
10 changed files with 152 additions and 141 deletions

View File

@ -7,7 +7,7 @@
# add C++ sources
SRC_CC += spec/arm/cortex_a15_cpu.cc
SRC_CC += kernel/cpu_mp.cc
SRC_CC += spec/arm/kernel/lock.cc
SRC_CC += spec/arm/kernel/mutex.cc
# include less specific configuration
include $(call select_from_repositories,lib/mk/spec/arm_v7/core-hw.inc)

View File

@ -9,7 +9,7 @@ SRC_CC += spec/arm/cortex_a9_board.cc
SRC_CC += spec/arm/cortex_a9_cpu.cc
SRC_CC += spec/arm/cortex_a9_global_timer.cc
SRC_CC += spec/arm/gicv2.cc
SRC_CC += spec/arm/kernel/lock.cc
SRC_CC += spec/arm/kernel/mutex.cc
SRC_CC += kernel/vm_thread_off.cc
SRC_CC += kernel/cpu_mp.cc

View File

@ -10,7 +10,7 @@ LIBS += syscall-hw
# add C++ sources
SRC_CC += kernel/cpu_mp.cc
SRC_CC += spec/arm/generic_timer.cc
SRC_CC += spec/arm/kernel/lock.cc
SRC_CC += spec/arm/kernel/mutex.cc
SRC_CC += spec/arm/kernel/thread_caches.cc
SRC_CC += spec/arm/platform_support.cc
SRC_CC += spec/arm_v8/cpu.cc

View File

@ -18,11 +18,11 @@ SRC_S += spec/x86_64/exception_vector.s
# add C++ sources
SRC_CC += kernel/cpu_mp.cc
SRC_CC += kernel/mutex.cc
SRC_CC += kernel/vm_thread_on.cc
SRC_CC += spec/x86_64/virtualization/kernel/vm.cc
SRC_CC += spec/x86_64/virtualization/kernel/svm.cc
SRC_CC += spec/x86_64/virtualization/kernel/vmx.cc
SRC_CC += kernel/lock.cc
SRC_CC += spec/x86_64/pic.cc
SRC_CC += spec/x86_64/timer.cc
SRC_CC += spec/x86_64/kernel/thread_exception.cc

View File

@ -1,47 +0,0 @@
/*
* \brief Kernel lock
* \author Martin Stein
* \author Stefan Kalkowski
* \date 2012-11-30
*/
/*
* Copyright (C) 2012-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _CORE__SPEC__SMP__KERNEL__LOCK_H_
#define _CORE__SPEC__SMP__KERNEL__LOCK_H_
namespace Kernel { class Lock; }
class Kernel::Lock
{
private:
enum { INVALID = ~0U };
enum State { UNLOCKED, LOCKED };
int volatile _locked { UNLOCKED };
unsigned volatile _current_cpu { INVALID };
public:
void lock();
void unlock();
struct Guard
{
Lock &_lock;
explicit Guard(Lock &lock) : _lock(lock) { _lock.lock(); }
~Guard() { _lock.unlock(); }
};
};
#endif /* _CORE__SPEC__SMP__KERNEL__LOCK_H_ */

View File

@ -15,7 +15,7 @@
/* core includes */
#include <map_local.h>
#include <kernel/cpu.h>
#include <kernel/lock.h>
#include <kernel/mutex.h>
#include <kernel/main.h>
#include <platform_pd.h>
#include <platform_thread.h>
@ -39,7 +39,7 @@ class Kernel::Main
static Main *_instance;
Lock _data_lock { };
Mutex _mutex { };
Cpu_pool _cpu_pool { };
Irq::Pool _user_irq_pool { };
Board::Address_space_id_allocator _addr_space_id_alloc { };
@ -65,12 +65,14 @@ void Kernel::Main::_handle_kernel_entry()
{
Cpu::Context * context;
{
Lock::Guard guard(_data_lock);
context =
&_cpu_pool.cpu(Cpu::executing_id()).handle_exception_and_schedule();
}
_mutex.execute_exclusive(
[&] () {
Cpu &cpu = _cpu_pool.cpu(Cpu::executing_id());
context = &cpu.handle_exception_and_schedule();
},
[&] () {
Genode::error("Cpu ", Cpu::executing_id(), " re-entered lock.",
"Kernel exception?!"); });
context->proceed();
}
@ -86,7 +88,7 @@ void Kernel::main_initialize_and_handle_kernel_entry()
{
using Boot_info = Hw::Boot_info<Board::Boot_info>;
static Lock init_lock;
static Mutex init_mutex;
static volatile unsigned nr_of_initialized_cpus { 0 };
static volatile bool kernel_initialized { false };
@ -99,35 +101,34 @@ void Kernel::main_initialize_and_handle_kernel_entry()
* Let the first CPU create a Main object and initialize the static
* reference to it.
*/
{
Lock::Guard guard(init_lock);
static Main instance;
Main::_instance = &instance;
}
init_mutex.execute_exclusive(
[&] () {
static Main instance;
Main::_instance = &instance; },
[&] () {
Genode::error("recursive call of ", __func__); });
/* the CPU resumed if the kernel is already initialized */
if (kernel_initialized) {
{
Lock::Guard guard(Main::_instance->_data_lock);
Main::_instance->_mutex.execute_exclusive(
[&] () {
if (nr_of_initialized_cpus == nr_of_cpus) {
nr_of_initialized_cpus = 0;
if (nr_of_initialized_cpus == nr_of_cpus) {
nr_of_initialized_cpus = 0;
Main::_instance->_serial.init();
Main::_instance->_global_irq_ctrl.init();
}
Main::_instance->_serial.init();
Main::_instance->_global_irq_ctrl.init();
}
nr_of_initialized_cpus = nr_of_initialized_cpus + 1;
nr_of_initialized_cpus = nr_of_initialized_cpus + 1;
Main::_instance->_cpu_pool.cpu(Cpu::executing_id()).reinit_cpu();
Main::_instance->_cpu_pool.cpu(Cpu::executing_id()).reinit_cpu();
if (nr_of_initialized_cpus == nr_of_cpus) {
Genode::raw("kernel resumed");
}
}
if (nr_of_initialized_cpus == nr_of_cpus)
Genode::raw("kernel resumed");
},
[&] () {
Genode::error("recursive call of ", __func__); });
while (nr_of_initialized_cpus < nr_of_cpus) { }
@ -136,20 +137,21 @@ void Kernel::main_initialize_and_handle_kernel_entry()
return;
}
{
/**
* Let each CPU initialize its corresponding CPU object in the
* CPU pool.
*/
Lock::Guard guard(Main::_instance->_data_lock);
Main::_instance->_cpu_pool.initialize_executing_cpu(
Main::_instance->_addr_space_id_alloc,
Main::_instance->_user_irq_pool,
Main::_instance->_core_platform_pd.kernel_pd(),
Main::_instance->_global_irq_ctrl);
Main::_instance->_mutex.execute_exclusive(
[&] () {
/**
* Let each CPU initialize its corresponding CPU object in the
* CPU pool.
*/
Main::_instance->_cpu_pool.initialize_executing_cpu(
Main::_instance->_addr_space_id_alloc,
Main::_instance->_user_irq_pool,
Main::_instance->_core_platform_pd.kernel_pd(),
Main::_instance->_global_irq_ctrl);
nr_of_initialized_cpus = nr_of_initialized_cpus + 1;
};
nr_of_initialized_cpus = nr_of_initialized_cpus + 1; },
[&] () {
Genode::error("recursive call of ", __func__); });
/**
* Let all CPUs block until each CPU object in the CPU pool has been
@ -161,29 +163,30 @@ void Kernel::main_initialize_and_handle_kernel_entry()
* Let the primary CPU initialize the core main thread and finish
* initialization of the boot info.
*/
{
Lock::Guard guard(Main::_instance->_data_lock);
Main::_instance->_mutex.execute_exclusive(
[&] () {
if (Cpu::executing_id() == Main::_instance->_cpu_pool.primary_cpu().id()) {
Main::_instance->_cpu_pool.for_each_cpu([&] (Kernel::Cpu &cpu) {
boot_info.kernel_irqs.add(cpu.timer().interrupt_id());
});
boot_info.kernel_irqs.add((unsigned)Board::Pic::IPI);
if (Cpu::executing_id() == Main::_instance->_cpu_pool.primary_cpu().id()) {
Main::_instance->_cpu_pool.for_each_cpu([&] (Kernel::Cpu &cpu) {
boot_info.kernel_irqs.add(cpu.timer().interrupt_id());
});
boot_info.kernel_irqs.add((unsigned)Board::Pic::IPI);
Main::_instance->_core_main_thread.construct(
Main::_instance->_addr_space_id_alloc,
Main::_instance->_user_irq_pool,
Main::_instance->_cpu_pool,
Main::_instance->_core_platform_pd.kernel_pd());
Main::_instance->_core_main_thread.construct(
Main::_instance->_addr_space_id_alloc,
Main::_instance->_user_irq_pool,
Main::_instance->_cpu_pool,
Main::_instance->_core_platform_pd.kernel_pd());
boot_info.core_main_thread_utcb =
(addr_t)Main::_instance->_core_main_thread->utcb();
boot_info.core_main_thread_utcb =
(addr_t)Main::_instance->_core_main_thread->utcb();
Genode::log("");
Genode::log("kernel initialized");
kernel_initialized = true;
}
}
Genode::log("");
Genode::log("kernel initialized");
kernel_initialized = true;
}
},
[&] () {
Genode::error("recursive call of ", __func__); });
/**
* Let secondary CPUs block until the primary CPU has initialized the

View File

@ -1,11 +1,11 @@
/*
* \brief Kernel lock for multi-processor systems
* \brief Kernel mutex
* \author Stefan Kalkowski
* \date 2018-11-20
* \date 2024-11-22
*/
/*
* Copyright (C) 2019 Genode Labs GmbH
* Copyright (C) 2024 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
@ -17,25 +17,21 @@
/* base-hw core includes */
#include <kernel/cpu.h>
#include <kernel/lock.h>
#include <kernel/mutex.h>
void Kernel::Lock::lock()
bool Kernel::Mutex::_lock()
{
/* check for the lock holder being the same cpu */
if (_current_cpu == Cpu::executing_id()) {
/* at least print an error message */
Genode::raw("Cpu ", _current_cpu,
" error: re-entered lock. Kernel exception?!");
}
while (!Genode::cmpxchg((volatile int*)&_locked, UNLOCKED, LOCKED)) { ; }
while (!Genode::cmpxchg((volatile int*)&_locked, UNLOCKED, LOCKED))
if (_current_cpu == Cpu::executing_id())
return false;
_current_cpu = Cpu::executing_id();
return true;
}
void Kernel::Lock::unlock()
void Kernel::Mutex::_unlock()
{
_current_cpu = INVALID;

View File

@ -0,0 +1,62 @@
/*
* \brief Kernel mutex
* \author Stefan Kalkowski
* \date 2024-11-22
*/
/*
* Copyright (C) 2024 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _CORE__KERNEL__MUTEX_H_
#define _CORE__KERNEL__MUTEX_H_
namespace Kernel { class Mutex; }
class Kernel::Mutex
{
private:
enum { INVALID = ~0U };
enum State { UNLOCKED, LOCKED };
State volatile _locked { UNLOCKED };
unsigned volatile _current_cpu { INVALID };
bool _lock();
void _unlock();
public:
/**
* Execute exclusively some critical section with lambda 'fn',
* if the critical section is called recursively by the same cpu
* lambda 'reentered' is called instead.
*/
void execute_exclusive(auto const &fn,
auto const &reentered)
{
/*
* If the lock cannot get acquired, it is already taken by this cpu.
* That means implicitely that most probably some machine exception
* during kernel execution forced the cpu to re-enter this critical
* section.
*/
if (!_lock()) {
reentered();
/* block forever */
while (!_lock()) ;
}
fn();
_unlock();
}
};
#endif /* _CORE__KERNEL__MUTEX_H_ */

View File

@ -74,7 +74,7 @@ class Hw::Address_space : public Core::Address_space
using Table = Hw::Page_table;
using Array = Table::Allocator::Array<DEFAULT_TRANSLATION_TABLE_MAX>;
Mutex _mutex { }; /* table lock */
Genode::Mutex _mutex { }; /* table lock */
Table &_tt; /* table virt addr */
addr_t _tt_phys; /* table phys addr */
Array *_tt_array = nullptr;

View File

@ -1,11 +1,11 @@
/*
* \brief Kernel lock for multi-processor systems
* \brief Kernel mutex
* \author Stefan Kalkowski
* \date 2018-11-20
*/
/*
* Copyright (C) 2019 Genode Labs GmbH
* Copyright (C) 2019-2024 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
@ -17,24 +17,21 @@
/* base-hw core includes */
#include <kernel/cpu.h>
#include <kernel/lock.h>
#include <kernel/mutex.h>
void Kernel::Lock::lock()
bool Kernel::Mutex::_lock()
{
/* check for the lock holder being the same cpu */
if (_current_cpu == Cpu::executing_id()) {
/* at least print an error message */
Genode::raw("Cpu ", _current_cpu,
" error: re-entered lock. Kernel exception?!");
}
if (_current_cpu == Cpu::executing_id())
return false;
Cpu::wait_for_xchg(&_locked, LOCKED, UNLOCKED);
Cpu::wait_for_xchg((volatile int*)&_locked, LOCKED, UNLOCKED);
_current_cpu = Cpu::executing_id();
return true;
}
void Kernel::Lock::unlock()
void Kernel::Mutex::_unlock()
{
_current_cpu = INVALID;