diff options
| author | Felix Morgner <felix.morgner@ost.ch> | 2026-07-25 12:18:53 +0200 |
|---|---|---|
| committer | Felix Morgner <felix.morgner@ost.ch> | 2026-07-25 12:18:53 +0200 |
| commit | b8e2f74374f7ab8dc82f923eb7333e4226cc5fdb (patch) | |
| tree | d0c14ccf61537302212fcc74444e8e68a4940d0c | |
| parent | 9c6f14583169100b7cd70346c7dc3d13d82806aa (diff) | |
| download | kernel-b8e2f74374f7ab8dc82f923eb7333e4226cc5fdb.tar.xz kernel-b8e2f74374f7ab8dc82f923eb7333e4226cc5fdb.zip | |
kernel: make device number registry dynamic
| -rw-r--r-- | kernel/kernel/filesystem/device_number_registry.cpp | 179 | ||||
| -rw-r--r-- | kernel/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 <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 |
