Kernel: Wait 10 seconds to find root device

If root device is initialized asynchronously it may not be ready when
main initialization thread is finished. This patch adds searching for
root device every 500 ms for 10 seconds.
This commit is contained in:
Bananymous 2024-11-21 15:45:48 +02:00
parent 658ea68d04
commit 8054f6c618
2 changed files with 97 additions and 46 deletions

View File

@ -83,5 +83,5 @@ command_line_enter_msg:
command_line: command_line:
# 100 character command line # 100 character command line
command_line_buffer: command_line_buffer:
.ascii "root=/dev/sda2" .ascii "root=UUID=9C87D2AF-566A-4517-971A-57BA86EEA88D"
.skip 100 - 28 .skip 100 - (. - command_line_buffer)

View File

@ -6,6 +6,7 @@
#include <kernel/FS/VirtualFileSystem.h> #include <kernel/FS/VirtualFileSystem.h>
#include <kernel/Lock/LockGuard.h> #include <kernel/Lock/LockGuard.h>
#include <kernel/Storage/Partition.h> #include <kernel/Storage/Partition.h>
#include <kernel/Timer/Timer.h>
#include <fcntl.h> #include <fcntl.h>
@ -14,55 +15,105 @@ namespace Kernel
static BAN::RefPtr<VirtualFileSystem> s_instance; static BAN::RefPtr<VirtualFileSystem> s_instance;
static BAN::ErrorOr<BAN::RefPtr<BlockDevice>> find_partition_by_uuid(BAN::StringView uuid)
{
ASSERT(uuid.size() == 36);
BAN::RefPtr<BlockDevice> result;
DevFileSystem::get().for_each_inode(
[&result, uuid](BAN::RefPtr<Inode> inode) -> BAN::Iteration
{
if (!inode->is_device())
return BAN::Iteration::Continue;
if (!static_cast<Device*>(inode.ptr())->is_partition())
return BAN::Iteration::Continue;
auto* partition = static_cast<Partition*>(inode.ptr());
if (partition->uuid() != uuid)
return BAN::Iteration::Continue;
result = partition;
return BAN::Iteration::Break;
}
);
if (!result)
return BAN::Error::from_errno(ENOENT);
return result;
}
static BAN::ErrorOr<BAN::RefPtr<BlockDevice>> find_block_device_by_name(BAN::StringView name)
{
auto device_inode = TRY(DevFileSystem::get().root_inode()->find_inode(name));
if (!device_inode->mode().ifblk())
return BAN::Error::from_errno(ENOTBLK);
return BAN::RefPtr<BlockDevice>(static_cast<BlockDevice*>(device_inode.ptr()));
}
static BAN::RefPtr<BlockDevice> find_root_device(BAN::StringView root_path)
{
enum class RootType
{
PartitionUUID,
BlockDeviceName,
};
BAN::StringView entry;
RootType type;
if (root_path.size() >= 5 && root_path.substring(0, 5) == "UUID="_sv)
{
entry = root_path.substring(5);
if (entry.size() != 36)
panic("Invalid UUID '{}'", entry);
type = RootType::PartitionUUID;
}
else if (root_path.size() >= 5 && root_path.substring(0, 5) == "/dev/"_sv)
{
entry = root_path.substring(5);
if (entry.empty() || entry.contains('/'))
panic("Invalid root path '{}'", root_path);
type = RootType::BlockDeviceName;
}
else
{
panic("Unsupported root path format '{}'", root_path);
}
constexpr size_t timeout_ms = 10'000;
constexpr size_t sleep_ms = 500;
for (size_t i = 0; i < timeout_ms / sleep_ms; i++)
{
BAN::ErrorOr<BAN::RefPtr<BlockDevice>> ret = BAN::Error::from_errno(EINVAL);
switch (type)
{
case RootType::PartitionUUID:
ret = find_partition_by_uuid(entry);
break;
case RootType::BlockDeviceName:
ret = find_block_device_by_name(entry);
break;
}
if (!ret.is_error())
return ret.release_value();
if (ret.error().get_error_code() != ENOENT)
panic("could not open root device '{}': {}", root_path, ret.error());
SystemTimer::get().sleep_ms(sleep_ms);
}
panic("could not find root device '{}' after {} ms", root_path, timeout_ms);
}
void VirtualFileSystem::initialize(BAN::StringView root_path) void VirtualFileSystem::initialize(BAN::StringView root_path)
{ {
ASSERT(!s_instance); ASSERT(!s_instance);
s_instance = MUST(BAN::RefPtr<VirtualFileSystem>::create()); s_instance = MUST(BAN::RefPtr<VirtualFileSystem>::create());
BAN::RefPtr<BlockDevice> root_device; auto root_device = find_root_device(root_path);
if (root_path.size() >= 5 && root_path.substring(0, 5) == "UUID="_sv) ASSERT(root_device);
{
auto uuid = root_path.substring(5);
if (uuid.size() != 36)
panic("Invalid UUID specified for root '{}'", uuid);
BAN::RefPtr<Partition> root_partition;
DevFileSystem::get().for_each_inode(
[&root_partition, uuid](BAN::RefPtr<Inode> inode) -> BAN::Iteration
{
if (!inode->is_device())
return BAN::Iteration::Continue;
if (!static_cast<Device*>(inode.ptr())->is_partition())
return BAN::Iteration::Continue;
auto* partition = static_cast<Partition*>(inode.ptr());
if (partition->uuid() != uuid)
return BAN::Iteration::Continue;
root_partition = partition;
return BAN::Iteration::Break;
}
);
if (!root_partition)
panic("Could not find partition with UUID '{}'", uuid);
root_device = root_partition;
}
else if (root_path.size() >= 5 && root_path.substring(0, 5) == "/dev/"_sv)
{
auto device_name = root_path.substring(5);
auto device_result = DevFileSystem::get().root_inode()->find_inode(device_name);
if (device_result.is_error())
panic("Could not open root device '{}': {}", root_path, device_result.error());
auto device_inode = device_result.release_value();
if (!device_inode->mode().ifblk())
panic("Root inode '{}' is not an block device", root_path);
root_device = static_cast<BlockDevice*>(device_inode.ptr());
}
else
{
panic("Unknown root path format '{}' specified", root_path);
}
auto filesystem_result = FileSystem::from_block_device(root_device); auto filesystem_result = FileSystem::from_block_device(root_device);
if (filesystem_result.is_error()) if (filesystem_result.is_error())