aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFelix Morgner <felix.morgner@ost.ch>2026-07-25 12:18:53 +0200
committerFelix Morgner <felix.morgner@ost.ch>2026-07-25 12:18:53 +0200
commitb8e2f74374f7ab8dc82f923eb7333e4226cc5fdb (patch)
treed0c14ccf61537302212fcc74444e8e68a4940d0c
parent9c6f14583169100b7cd70346c7dc3d13d82806aa (diff)
downloadkernel-b8e2f74374f7ab8dc82f923eb7333e4226cc5fdb.tar.xz
kernel-b8e2f74374f7ab8dc82f923eb7333e4226cc5fdb.zip
kernel: make device number registry dynamic
-rw-r--r--kernel/kernel/filesystem/device_number_registry.cpp179
-rw-r--r--kernel/kernel/filesystem/device_number_registry.hpp51
2 files changed, 168 insertions, 62 deletions
diff --git a/kernel/kernel/filesystem/device_number_registry.cpp b/kernel/kernel/filesystem/device_number_registry.cpp
index 058ae322..92f59bc5 100644
--- a/kernel/kernel/filesystem/device_number_registry.cpp
+++ b/kernel/kernel/filesystem/device_number_registry.cpp
@@ -15,17 +15,11 @@
#include <cstdint>
#include <optional>
#include <ranges>
+#include <utility>
namespace
{
constinit auto static instance = std::optional<kernel::filesystem::device_number_registry>{};
-
- struct driver_group
- {
- kapi::devices::driver const * bound_driver;
- std::uint8_t major;
- kstd::vector<kapi::devices::interface_registry::entry> published;
- };
} // namespace
namespace kernel::filesystem
@@ -42,12 +36,21 @@ namespace kernel::filesystem
device_number_registry::device_number_registry(kapi::devices::interface_registry & interface_registry)
: m_interface_registry{&interface_registry}
- {}
+ {
+ m_interface_registry->subscribe(*this);
+ }
+
+ device_number_registry::~device_number_registry()
+ {
+ m_interface_registry->unsubscribe(*this);
+ }
auto device_number_registry::resolve(kapi::filesystem::device_number number, file_type type) const
-> kstd::result<kstd::shared_ptr<kapi::devices::device>>
{
- populate();
+ // TODO: lock registry
+
+ erase_if(m_entries, [&](auto entry) { return entry.device.expired(); });
auto found =
std::ranges::find_if(m_entries, [&](auto entry) { return entry.number == number && entry.type == type; });
@@ -68,7 +71,9 @@ namespace kernel::filesystem
auto device_number_registry::number_of(kapi::devices::device const & device) const
-> kstd::result<kapi::filesystem::device_number>
{
- populate();
+ // TODO: lock registry
+
+ erase_if(m_entries, [&](auto entry) { return entry.device.expired(); });
auto found = std::ranges::find_if(m_entries, [&](auto entry) {
auto locked = entry.device.lock();
@@ -85,70 +90,124 @@ namespace kernel::filesystem
auto device_number_registry::all() const -> kstd::vector<entry>
{
- populate();
+ // TODO: lock registry
+
+ erase_if(m_entries, [&](auto entry) { return entry.device.expired(); });
return m_entries;
}
- auto device_number_registry::populate() const -> void
+ auto device_number_registry::subscribe(kstd::weak_ptr<device_number_registry_observer> observer) -> void
{
- m_entries.clear();
+ // TODO: lock registry
+
+ erase_if(m_observers, [&](auto existing) { return existing.expired(); });
- for (auto const & binding : file_type_bindings)
+ m_observers.push_back(std::move(observer));
+ }
+
+ auto device_number_registry::on_interface_published(kapi::devices::interface_id interface,
+ kapi::devices::interface_registry::entry const & published)
+ -> void
+ {
+ // Check if there even is a binding defined for the given interface.
+ auto binding =
+ std::ranges::find_if(file_type_bindings, [&](auto const & binding) { return binding.interface == interface; });
+ if (binding == std::ranges::cend(file_type_bindings))
{
- auto groups = kstd::vector<driver_group>{};
+ return;
+ }
+
+ // Check if the device is still alive (it might have died while we are being notified).
+ auto device = published.device();
+ if (!device)
+ {
+ return;
+ }
+
+ // Check if the device has a bound driver (otherwise there is no point in numbering it).
+ auto driver = device->bound_driver();
+ if (!driver)
+ {
+ return;
+ }
- for (auto const & published : m_interface_registry->all(binding.interface))
+ // Check if the driver has claimed a major number (otherwise we can't number it).
+ auto driver_major = driver->claimed_major();
+ if (!driver_major)
+ {
+ return;
+ }
+
+ // TODO: lock registry
+
+ auto next_minor = m_next_minor.find(driver);
+ auto minor = std::uint8_t{};
+ if (next_minor != m_next_minor.end())
+ {
+ minor = next_minor->second++;
+ }
+ else
+ {
+ m_next_minor.emplace(driver, std::uint8_t{1});
+ }
+
+ auto numbered = entry{
+ .number = {.major = *driver_major, .minor = minor},
+ .type = binding->posix_type,
+ .name = published.name(),
+ .device = device,
+ };
+
+ m_entries.push_back(numbered);
+
+ // TODO: drop lock here
+
+ std::ranges::for_each(m_observers, [&](auto const & weak_observer) {
+ auto observer = weak_observer.lock();
+ if (observer)
{
- auto device = published.device();
- if (!device)
- {
- continue;
- }
-
- auto bound_driver = device->bound_driver();
- if (!bound_driver)
- {
- continue;
- }
-
- auto claimed = bound_driver->claimed_major();
- if (!claimed)
- {
- continue;
- }
-
- auto found_group =
- std::ranges::find_if(groups, [&](auto const & group) { return group.bound_driver == bound_driver; });
-
- if (found_group == groups.end())
- {
- groups.push_back({.bound_driver = bound_driver, .major = *claimed, .published = {}});
- found_group = groups.end() - 1;
- }
-
- found_group->published.push_back(published);
+ observer->on_device_numbered(numbered);
}
+ });
+ }
+
+ auto device_number_registry::on_interface_withdrawn(kapi::devices::interface_id interface,
+ kapi::devices::device & device) -> void
+ {
+ // Check if there even is a binding defined for the given interface.
+ auto binding =
+ std::ranges::find_if(file_type_bindings, [&](auto const & binding) { return binding.interface == interface; });
+ if (binding == std::ranges::cend(file_type_bindings))
+ {
+ return;
+ }
+
+ // TODO: lock registry
- for (auto const & group : groups)
+ auto found = std::ranges::find_if(m_entries, [&](auto entry) {
+ auto locked = entry.device.lock();
+ return entry.type == binding->posix_type && locked.get() == &device;
+ });
+
+ if (found == std::ranges::cend(m_entries))
+ {
+ m_entries.erase(found);
+ }
+
+ auto unnumbered = *found;
+ m_entries.erase(found);
+
+ // TODO: drop lock
+
+ std::ranges::for_each(m_observers, [&](auto const & weak_observer) {
+ auto observer = weak_observer.lock();
+ if (observer)
{
- auto next_minor = decltype(kapi::filesystem::device_number::minor){};
-
- for (auto const & published : group.published)
- {
- if (auto device = published.device())
- {
- m_entries.emplace_back(kapi::filesystem::device_number{.major = group.major, .minor = next_minor}, //
- binding.posix_type, //
- published.name(), //
- published.device());
- ++next_minor;
- }
- }
+ observer->on_device_unnumbered(unnumbered);
}
- }
+ });
}
-
} // namespace kernel::filesystem
namespace kernel::tests::filesystem::device_number_registry
diff --git a/kernel/kernel/filesystem/device_number_registry.hpp b/kernel/kernel/filesystem/device_number_registry.hpp
index 0e30e226..949d7bba 100644
--- a/kernel/kernel/filesystem/device_number_registry.hpp
+++ b/kernel/kernel/filesystem/device_number_registry.hpp
@@ -6,15 +6,21 @@
#include <kapi/devices.hpp>
#include <kapi/filesystem.hpp>
+#include <kstd/flat_map.hpp>
#include <kstd/memory.hpp>
#include <kstd/result.hpp>
#include <kstd/string.hpp>
#include <kstd/vector.hpp>
+#include <cstdint>
+
namespace kernel::filesystem
{
- struct device_number_registry
+ struct device_number_registry_observer;
+
+ //! A registry to assign stable device numbers to published devices.
+ struct device_number_registry final : kapi::devices::interface_registry_observer
{
//! A single numbered device.
//!
@@ -27,8 +33,19 @@ namespace kernel::filesystem
kstd::weak_ptr<kapi::devices::device> device;
};
+ //! Construct a new device number registry, subscribing to the given interface registry.
+ //!
+ //! @param interface_registry The interface registry to back this device number registry.
explicit device_number_registry(kapi::devices::interface_registry & interface_registry);
+ //! Destroy this registry.
+ ~device_number_registry() override;
+
+ device_number_registry(device_number_registry const &) = delete;
+ device_number_registry(device_number_registry &&) = delete;
+ auto operator=(device_number_registry const &) -> device_number_registry & = delete;
+ auto operator=(device_number_registry &&) -> device_number_registry & = delete;
+
//! Get the system global instance of the registry.
[[nodiscard]] auto static get() -> device_number_registry &;
@@ -52,11 +69,41 @@ namespace kernel::filesystem
//! @return A range containing all currently registered devices numbers mappings.
[[nodiscard]] auto all() const -> kstd::vector<entry>;
+ //! Subscribe to device numbering events.
+ //!
+ //! Observers subscribed through this function are held through non-owning pointers. It is safe of an observer
+ //! subscribed through this function to be destroyed without being unsubscribed.
+ //!
+ //! @param observer The observer to notify.
+ auto subscribe(kstd::weak_ptr<device_number_registry_observer> observer) -> void;
+
private:
- auto populate() const -> void;
+ auto on_interface_published(kapi::devices::interface_id interface,
+ kapi::devices::interface_registry::entry const & entry) -> void override;
+
+ auto on_interface_withdrawn(kapi::devices::interface_id interface, kapi::devices::device & device) -> void override;
kstd::observer_ptr<kapi::devices::interface_registry> m_interface_registry;
mutable kstd::vector<entry> m_entries{};
+ kstd::flat_map<kapi::devices::driver const *, std::uint8_t> m_next_minor;
+ kstd::vector<kstd::weak_ptr<device_number_registry_observer>> m_observers{};
+ };
+
+ //! The interface for types interested in observing numbering transactions on a device number registry.
+ struct device_number_registry_observer
+ {
+ //! Enable correct destruction through base pointers.
+ virtual ~device_number_registry_observer() = default;
+
+ //! Called after a device has been assigned a device number.
+ //!
+ //! @param numbered The newly numbered entry.
+ virtual auto on_device_numbered(device_number_registry::entry const & numbered) -> void = 0;
+
+ //! Called after a device's number has been withdrawn.
+ //!
+ //! @param unnumbered The entry that is no longer numbered.
+ virtual auto on_device_unnumbered(device_number_registry::entry const & unnumbered) -> void = 0;
};
} // namespace kernel::filesystem