From b8e2f74374f7ab8dc82f923eb7333e4226cc5fdb Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Sat, 25 Jul 2026 12:18:53 +0200 Subject: kernel: make device number registry dynamic --- .../kernel/filesystem/device_number_registry.cpp | 179 ++++++++++++++------- .../kernel/filesystem/device_number_registry.hpp | 51 +++++- 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 #include #include +#include namespace { constinit auto static instance = std::optional{}; - - struct driver_group - { - kapi::devices::driver const * bound_driver; - std::uint8_t major; - kstd::vector 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> { - 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 { - 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 { - 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 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{}; + 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 #include +#include #include #include #include #include +#include + 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 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; + //! 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 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 m_interface_registry; mutable kstd::vector m_entries{}; + kstd::flat_map m_next_minor; + kstd::vector> 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 -- cgit v1.2.3