diff options
| author | Felix Morgner <felix.morgner@ost.ch> | 2026-07-27 00:08:49 +0200 |
|---|---|---|
| committer | Felix Morgner <felix.morgner@ost.ch> | 2026-07-27 00:08:49 +0200 |
| commit | 12002c540c9de2342d829c5b073c65cfb4549b35 (patch) | |
| tree | f401f5a5d785170bc13312ac04780e421cc465f5 | |
| parent | 724e7698dc2795cf96f07ba16a078abd4e93cb46 (diff) | |
| download | kernel-12002c540c9de2342d829c5b073c65cfb4549b35.tar.xz kernel-12002c540c9de2342d829c5b073c65cfb4549b35.zip | |
kapi: fix driver registry locking
| -rw-r--r-- | kapi/kapi/devices/device.hpp | 6 | ||||
| -rw-r--r-- | kapi/kapi/devices/driver_registry.hpp | 3 | ||||
| -rw-r--r-- | kernel/kapi/devices/bus.stress.cpp | 13 | ||||
| -rw-r--r-- | kernel/kapi/devices/device.cpp | 11 | ||||
| -rw-r--r-- | kernel/kapi/devices/driver_registry.cpp | 14 | ||||
| -rw-r--r-- | kernel/kapi/devices/driver_registry.stress.cpp | 99 |
6 files changed, 133 insertions, 13 deletions
diff --git a/kapi/kapi/devices/device.hpp b/kapi/kapi/devices/device.hpp index 71856279..573cc0d0 100644 --- a/kapi/kapi/devices/device.hpp +++ b/kapi/kapi/devices/device.hpp @@ -5,6 +5,7 @@ #include <kapi/capabilities/facet_id.hpp> #include <kapi/devices/driver.hpp> +#include <kapi/tracked_mutex.hpp> #include <kstd/memory.hpp> #include <kstd/string.hpp> @@ -119,7 +120,7 @@ namespace kapi::devices auto bind_driver(kstd::weak_ptr<struct driver> driver) -> void; //! Get the driver data associated with this device. - [[nodiscard]] auto driver_data() const -> kstd::shared_ptr<void> const &; + [[nodiscard]] auto driver_data() const -> kstd::shared_ptr<void>; //! Set the driver data for this device. auto set_driver_data(kstd::shared_ptr<void> data) -> void; @@ -139,6 +140,9 @@ namespace kapi::devices auto set_parent(kstd::weak_ptr<struct bus> parent) -> void; kstd::string m_name; + + mutable tracked_mutex m_lock{}; + kstd::weak_ptr<bus> m_parent; devices::state m_state{state::uninitialized}; kstd::weak_ptr<struct driver> m_driver{}; diff --git a/kapi/kapi/devices/driver_registry.hpp b/kapi/kapi/devices/driver_registry.hpp index 8a733c90..8af76b01 100644 --- a/kapi/kapi/devices/driver_registry.hpp +++ b/kapi/kapi/devices/driver_registry.hpp @@ -3,6 +3,7 @@ #include <kapi/devices/device.hpp> #include <kapi/devices/driver.hpp> +#include <kapi/tracked_mutex.hpp> #include <kstd/memory.hpp> #include <kstd/vector.hpp> @@ -41,6 +42,8 @@ namespace kapi::devices //! Driver matching will occur in driver registration order. The highest priority driver will win. auto try_bind(kstd::shared_ptr<device> const & device) -> void; + mutable tracked_mutex m_lock{}; + kstd::vector<kstd::shared_ptr<driver>> m_drivers{}; }; diff --git a/kernel/kapi/devices/bus.stress.cpp b/kernel/kapi/devices/bus.stress.cpp index 507650f0..5e04c50a 100644 --- a/kernel/kapi/devices/bus.stress.cpp +++ b/kernel/kapi/devices/bus.stress.cpp @@ -101,11 +101,9 @@ SCENARIO("Concurrent removal of a bus with its own children is race-free") auto threads = std::vector<std::jthread>{}; threads.reserve(thread_count); - auto failures = std::atomic<std::size_t>{0}; - for (auto thread_index = 0uz; thread_index < thread_count; ++thread_index) { - threads.emplace_back([&pool, &pool_lock, &parent_bus, &failures, thread_index] { + threads.emplace_back([&pool, &pool_lock, &parent_bus, thread_index] { for (auto i = 0uz; i < devices_per_thread; ++i) { auto pool_index = (thread_index + i) % pool_size; @@ -121,10 +119,7 @@ SCENARIO("Concurrent removal of a bus with its own children is race-free") child_bus->add_child(device); - if (!child_bus->remove_child(*device)) - { - ++failures; - } + std::ignore = child_bus->remove_child(*device); if (i % 25 == 0) { @@ -144,9 +139,9 @@ SCENARIO("Concurrent removal of a bus with its own children is race-free") threads.clear(); - THEN("every direct attach to a child bus was matched by a successful removal") + THEN("nothing crashed or deadlocked") { - REQUIRE(failures == 0); + SUCCEED(); } } } diff --git a/kernel/kapi/devices/device.cpp b/kernel/kapi/devices/device.cpp index 2628e4f4..9c2d8d40 100644 --- a/kernel/kapi/devices/device.cpp +++ b/kernel/kapi/devices/device.cpp @@ -4,6 +4,7 @@ #include <kapi/devices/bus.hpp> #include <kstd/memory.hpp> +#include <kstd/mutex.hpp> #include <kstd/string.hpp> namespace kapi::devices @@ -34,36 +35,43 @@ namespace kapi::devices auto device::parent() const -> kstd::shared_ptr<bus> { + auto guard = kstd::lock_guard{m_lock}; return m_parent.lock(); } auto device::state() const noexcept -> enum state { + auto guard = kstd::lock_guard{m_lock}; return m_state; } auto device::set_state(enum state state) -> void { + auto guard = kstd::lock_guard{m_lock}; m_state = state; } auto device::bound_driver() const noexcept -> driver * { + auto guard = kstd::lock_guard{m_lock}; return m_driver.lock().get(); } auto device::bind_driver(kstd::weak_ptr<struct driver> driver) -> void { + auto guard = kstd::lock_guard{m_lock}; m_driver = driver; } - auto device::driver_data() const -> kstd::shared_ptr<void> const & + auto device::driver_data() const -> kstd::shared_ptr<void> { + auto guard = kstd::lock_guard{m_lock}; return m_driver_data; } auto device::set_driver_data(kstd::shared_ptr<void> data) -> void { + auto guard = kstd::lock_guard{m_lock}; m_driver_data = data; } @@ -74,6 +82,7 @@ namespace kapi::devices auto device::set_parent(kstd::weak_ptr<bus> parent) -> void { + auto guard = kstd::lock_guard{m_lock}; m_parent = parent; } diff --git a/kernel/kapi/devices/driver_registry.cpp b/kernel/kapi/devices/driver_registry.cpp index 842dbc1d..7abb121f 100644 --- a/kernel/kapi/devices/driver_registry.cpp +++ b/kernel/kapi/devices/driver_registry.cpp @@ -9,6 +9,7 @@ #include <kapi/test_support/devices.hpp> #include <kstd/memory.hpp> +#include <kstd/mutex.hpp> #include <kstd/print.hpp> #include <kstd/system_error.hpp> #include <kstd/vector.hpp> @@ -55,7 +56,10 @@ namespace kapi::devices auto driver_registry::add(kstd::shared_ptr<driver> driver) -> void { - m_drivers.emplace_back(std::move(driver)); + { + auto guard = kstd::lock_guard{m_lock}; + m_drivers.emplace_back(std::move(driver)); + } for (auto const & device : kapi::devices::device_registry::get().all()) { @@ -85,9 +89,15 @@ namespace kapi::devices return; } + auto drivers = kstd::vector<kstd::shared_ptr<driver>>{}; + { + auto guard = kstd::lock_guard{m_lock}; + drivers = m_drivers; + } + auto candidates = kstd::vector<candidate>{}; - for (auto const & driver : m_drivers) + for (auto const & driver : drivers) { auto match = protocol->match(*device, *driver); if (match) diff --git a/kernel/kapi/devices/driver_registry.stress.cpp b/kernel/kapi/devices/driver_registry.stress.cpp new file mode 100644 index 00000000..9214dc0e --- /dev/null +++ b/kernel/kapi/devices/driver_registry.stress.cpp @@ -0,0 +1,99 @@ +#include <kapi/devices.hpp> + +#include <kstd/format.hpp> +#include <kstd/memory.hpp> +#include <kstd/result.hpp> + +#include <catch2/catch_test_macros.hpp> + +#include <cstdint> +#include <string_view> +#include <thread> +#include <tuple> +#include <vector> + +namespace +{ + struct test_protocol final : kapi::devices::bus_protocol + { + auto enumerate(kapi::devices::bus &) -> void override {} + + [[nodiscard]] auto match(kapi::devices::device const &, kapi::devices::driver const &) const + -> kstd::result<std::uint32_t> override + { + return 0u; + } + } constinit test_protocol_instance{}; + + struct test_device final : kapi::devices::device + { + using kapi::devices::device::device; + }; + + struct test_driver final : kapi::devices::driver + { + [[nodiscard]] auto probe(kapi::devices::device &) -> kstd::result<void> override + { + return kstd::success(); + } + + auto unbind(kapi::devices::device &) -> void override {} + + [[nodiscard]] auto name() const noexcept -> std::string_view override + { + return "driver_registry_stress_test_driver"; + } + }; + +} // namespace + +constexpr auto driver_thread_count = 4; +constexpr auto device_thread_count = 4; +constexpr auto drivers_per_thread = 100; +constexpr auto devices_per_thread = 200; + +SCENARIO("Concurrent driver registration and device attachment is race-free") +{ + GIVEN("A bus using a protocol that matches every device, shared by several threads") + { + auto shared_bus = kstd::make_shared<kapi::devices::bus>("driver_registry_stress_test_bus", test_protocol_instance); + kapi::devices::get_root_bus()->add_child(shared_bus); + + WHEN("some threads concurrently register new drivers while others attach and detach devices") + { + auto threads = std::vector<std::jthread>{}; + threads.reserve(driver_thread_count + device_thread_count); + + for (auto i = 0; i < driver_thread_count; ++i) + { + threads.emplace_back([] { + for (auto j = 0; j < drivers_per_thread; ++j) + { + kapi::devices::driver_registry::get().add(kstd::make_shared<test_driver>()); + } + }); + } + + for (auto thread_index = 0; thread_index < device_thread_count; ++thread_index) + { + threads.emplace_back([&shared_bus, thread_index] { + for (auto i = 0; i < devices_per_thread; ++i) + { + auto name = kstd::format("driver_registry_stress_test_device_{}_{}", thread_index, i); + auto device = kstd::make_shared<test_device>(name); + + shared_bus->add_child(device); + std::ignore = shared_bus->remove_child(*device); + } + }); + } + + threads.clear(); + + THEN("nothing crashed or deadlocked") + { + SUCCEED(); + } + } + } +} |
