Kernel/VirtIO: Ensure proper error propagation in core methods

Simplify core methods in the VirtIO bus handling code by ensuring proper
error propagation. This makes initialization of queues, handling changes
in device configuration, and other core patterns more readable as well.

It also allows us to remove the obnoxious pattern of checking for
boolean "success" and if we get false answer then returning an actual
errno code.
This commit is contained in:
Liav A 2023-09-23 00:44:33 +03:00 committed by Andrew Kaster
parent 6ee6a2534d
commit 7718842829
12 changed files with 56 additions and 81 deletions

View file

@ -25,16 +25,14 @@ UNMAP_AFTER_INIT ErrorOr<void> Console::initialize_virtio_resources()
{ {
TRY(Device::initialize_virtio_resources()); TRY(Device::initialize_virtio_resources());
auto const* cfg = TRY(transport_entity().get_config(VirtIO::ConfigurationType::Device)); auto const* cfg = TRY(transport_entity().get_config(VirtIO::ConfigurationType::Device));
bool success = negotiate_features([&](u64 supported_features) { TRY(negotiate_features([&](u64 supported_features) {
u64 negotiated = 0; u64 negotiated = 0;
if (is_feature_set(supported_features, VIRTIO_CONSOLE_F_SIZE)) if (is_feature_set(supported_features, VIRTIO_CONSOLE_F_SIZE))
dbgln("VirtIO::Console: Console size is not yet supported!"); dbgln("VirtIO::Console: Console size is not yet supported!");
if (is_feature_set(supported_features, VIRTIO_CONSOLE_F_MULTIPORT)) if (is_feature_set(supported_features, VIRTIO_CONSOLE_F_MULTIPORT))
negotiated |= VIRTIO_CONSOLE_F_MULTIPORT; negotiated |= VIRTIO_CONSOLE_F_MULTIPORT;
return negotiated; return negotiated;
}); }));
if (!success)
return Error::from_errno(EIO);
u32 max_nr_ports = 0; u32 max_nr_ports = 0;
u16 cols = 0, rows = 0; u16 cols = 0, rows = 0;
transport_entity().read_config_atomic([&]() { transport_entity().read_config_atomic([&]() {
@ -49,9 +47,7 @@ UNMAP_AFTER_INIT ErrorOr<void> Console::initialize_virtio_resources()
}); });
dbgln("VirtIO::Console: cols: {}, rows: {}, max nr ports {}", cols, rows, max_nr_ports); dbgln("VirtIO::Console: cols: {}, rows: {}, max nr ports {}", cols, rows, max_nr_ports);
// Base receiveq/transmitq for port0 + optional control queues and 2 per every additional port // Base receiveq/transmitq for port0 + optional control queues and 2 per every additional port
success = setup_queues(2 + max_nr_ports > 0 ? 2 + 2 * max_nr_ports : 0); TRY(setup_queues(2 + max_nr_ports > 0 ? 2 + 2 * max_nr_ports : 0));
if (!success)
return Error::from_errno(EIO);
finish_init(); finish_init();
if (is_feature_accepted(VIRTIO_CONSOLE_F_MULTIPORT)) { if (is_feature_accepted(VIRTIO_CONSOLE_F_MULTIPORT)) {
@ -70,10 +66,10 @@ UNMAP_AFTER_INIT Console::Console(NonnullOwnPtr<TransportEntity> transport_entit
{ {
} }
bool Console::handle_device_config_change() ErrorOr<void> Console::handle_device_config_change()
{ {
dbgln("VirtIO::Console: Handle device config change"); dbgln("VirtIO::Console: Handle device config change");
return true; return {};
} }
void Console::handle_queue_update(u16 queue_index) void Console::handle_queue_update(u16 queue_index)

View file

@ -59,7 +59,7 @@ private:
constexpr static size_t CONTROL_MESSAGE_SIZE = sizeof(ControlMessage); constexpr static size_t CONTROL_MESSAGE_SIZE = sizeof(ControlMessage);
constexpr static size_t CONTROL_BUFFER_SIZE = CONTROL_MESSAGE_SIZE * 32; constexpr static size_t CONTROL_BUFFER_SIZE = CONTROL_MESSAGE_SIZE * 32;
virtual bool handle_device_config_change() override; virtual ErrorOr<void> handle_device_config_change() override;
virtual void handle_queue_update(u16 queue_index) override; virtual void handle_queue_update(u16 queue_index) override;
Vector<LockRefPtr<ConsolePort>> m_ports; Vector<LockRefPtr<ConsolePort>> m_ports;

View file

@ -38,7 +38,7 @@ void Device::set_status_bit(u8 status_bit)
m_transport_entity->set_status_bits({}, m_status); m_transport_entity->set_status_bits({}, m_status);
} }
bool Device::accept_device_features(u64 device_features, u64 accepted_features) ErrorOr<void> Device::accept_device_features(u64 device_features, u64 accepted_features)
{ {
VERIFY(!m_did_accept_features); VERIFY(!m_did_accept_features);
m_did_accept_features = true; m_did_accept_features = true;
@ -70,28 +70,24 @@ bool Device::accept_device_features(u64 device_features, u64 accepted_features)
if (!(m_status & DEVICE_STATUS_FEATURES_OK)) { if (!(m_status & DEVICE_STATUS_FEATURES_OK)) {
set_status_bit(DEVICE_STATUS_FAILED); set_status_bit(DEVICE_STATUS_FAILED);
dbgln("{}: Features not accepted by host!", m_class_name); dbgln("{}: Features not accepted by host!", m_class_name);
return false; return Error::from_errno(EIO);
} }
m_accepted_features = accepted_features; m_accepted_features = accepted_features;
dbgln_if(VIRTIO_DEBUG, "{}: Features accepted by host", m_class_name); dbgln_if(VIRTIO_DEBUG, "{}: Features accepted by host", m_class_name);
return true; return {};
} }
bool Device::setup_queue(u16 queue_index) ErrorOr<void> Device::setup_queue(u16 queue_index)
{ {
auto queue_or_error = m_transport_entity->setup_queue({}, queue_index); auto queue = TRY(m_transport_entity->setup_queue({}, queue_index));
if (queue_or_error.is_error())
return false;
auto queue = queue_or_error.release_value();
dbgln_if(VIRTIO_DEBUG, "{}: Queue[{}] configured with size: {}", m_class_name, queue_index, queue->size()); dbgln_if(VIRTIO_DEBUG, "{}: Queue[{}] configured with size: {}", m_class_name, queue_index, queue->size());
m_queues.append(move(queue)); TRY(m_queues.try_append(move(queue)));
return true; return {};
} }
bool Device::setup_queues(u16 requested_queue_count) ErrorOr<void> Device::setup_queues(u16 requested_queue_count)
{ {
VERIFY(!m_did_setup_queues); VERIFY(!m_did_setup_queues);
m_did_setup_queues = true; m_did_setup_queues = true;
@ -103,7 +99,7 @@ bool Device::setup_queues(u16 requested_queue_count)
m_queue_count = maximum_queue_count; m_queue_count = maximum_queue_count;
} else if (requested_queue_count > maximum_queue_count) { } else if (requested_queue_count > maximum_queue_count) {
dbgln("{}: {} queues requested but only {} available!", m_class_name, m_queue_count, maximum_queue_count); dbgln("{}: {} queues requested but only {} available!", m_class_name, m_queue_count, maximum_queue_count);
return false; return Error::from_errno(ENXIO);
} else { } else {
m_queue_count = requested_queue_count; m_queue_count = requested_queue_count;
} }
@ -113,15 +109,13 @@ bool Device::setup_queues(u16 requested_queue_count)
} }
dbgln_if(VIRTIO_DEBUG, "{}: Setting up {} queues", m_class_name, m_queue_count); dbgln_if(VIRTIO_DEBUG, "{}: Setting up {} queues", m_class_name, m_queue_count);
for (u16 i = 0; i < m_queue_count; i++) { for (u16 i = 0; i < m_queue_count; i++)
if (!setup_queue(i)) TRY(setup_queue(i));
return false;
} // NOTE: Queues can only be activated *after* all others queues were also configured
for (u16 i = 0; i < m_queue_count; i++) { // Queues can only be activated *after* all others queues were also configured for (u16 i = 0; i < m_queue_count; i++)
if (!m_transport_entity->activate_queue({}, i)) TRY(m_transport_entity->activate_queue({}, i));
return false; return {};
}
return true;
} }
void Device::finish_init() void Device::finish_init()
@ -143,7 +137,7 @@ bool Device::handle_irq(Badge<TransportInterruptHandler>)
} }
if (isr_type & DEVICE_CONFIG_INTERRUPT) { if (isr_type & DEVICE_CONFIG_INTERRUPT) {
dbgln_if(VIRTIO_DEBUG, "{}: VirtIO Device config interrupt!", class_name()); dbgln_if(VIRTIO_DEBUG, "{}: VirtIO Device config interrupt!", class_name());
if (!handle_device_config_change()) { if (handle_device_config_change().is_error()) {
set_status_bit(DEVICE_STATUS_FAILED); set_status_bit(DEVICE_STATUS_FAILED);
dbgln("{}: Failed to handle device config change!", class_name()); dbgln("{}: Failed to handle device config change!", class_name());
} }

View file

@ -35,7 +35,7 @@ protected:
void mask_status_bits(u8 status_mask); void mask_status_bits(u8 status_mask);
void set_status_bit(u8); void set_status_bit(u8);
bool setup_queues(u16 requested_queue_count = 0); ErrorOr<void> setup_queues(u16 requested_queue_count = 0);
void finish_init(); void finish_init();
Queue& get_queue(u16 queue_index) Queue& get_queue(u16 queue_index)
@ -51,7 +51,7 @@ protected:
} }
template<typename F> template<typename F>
bool negotiate_features(F f) ErrorOr<void> negotiate_features(F f)
{ {
u64 device_features = m_transport_entity->get_device_features(); u64 device_features = m_transport_entity->get_device_features();
u64 accept_features = f(device_features); u64 accept_features = f(device_features);
@ -72,16 +72,16 @@ protected:
void supply_chain_and_notify(u16 queue_index, QueueChain& chain); void supply_chain_and_notify(u16 queue_index, QueueChain& chain);
virtual bool handle_device_config_change() = 0; virtual ErrorOr<void> handle_device_config_change() = 0;
virtual void handle_queue_update(u16 queue_index) = 0; virtual void handle_queue_update(u16 queue_index) = 0;
TransportEntity& transport_entity() { return *m_transport_entity; } TransportEntity& transport_entity() { return *m_transport_entity; }
private: private:
bool accept_device_features(u64 device_features, u64 accepted_features); ErrorOr<void> accept_device_features(u64 device_features, u64 accepted_features);
bool setup_queue(u16 queue_index); ErrorOr<void> setup_queue(u16 queue_index);
bool activate_queue(u16 queue_index); ErrorOr<void> activate_queue(u16 queue_index);
void notify_queue(u16 queue_index); void notify_queue(u16 queue_index);
Vector<NonnullOwnPtr<Queue>> m_queues; Vector<NonnullOwnPtr<Queue>> m_queues;

View file

@ -19,14 +19,10 @@ UNMAP_AFTER_INIT NonnullLockRefPtr<RNG> RNG::must_create_for_pci_instance(PCI::D
UNMAP_AFTER_INIT ErrorOr<void> RNG::initialize_virtio_resources() UNMAP_AFTER_INIT ErrorOr<void> RNG::initialize_virtio_resources()
{ {
TRY(Device::initialize_virtio_resources()); TRY(Device::initialize_virtio_resources());
bool success = negotiate_features([&](auto) { TRY(negotiate_features([&](auto) {
return 0; return 0;
}); }));
if (!success) TRY(setup_queues(1));
return Error::from_errno(EIO);
success = setup_queues(1);
if (!success)
return Error::from_errno(EIO);
finish_init(); finish_init();
m_entropy_buffer = TRY(MM.allocate_contiguous_kernel_region(PAGE_SIZE, "VirtIO::RNG"sv, Memory::Region::Access::ReadWrite)); m_entropy_buffer = TRY(MM.allocate_contiguous_kernel_region(PAGE_SIZE, "VirtIO::RNG"sv, Memory::Region::Access::ReadWrite));
memset(m_entropy_buffer->vaddr().as_ptr(), 0, m_entropy_buffer->size()); memset(m_entropy_buffer->vaddr().as_ptr(), 0, m_entropy_buffer->size());
@ -39,9 +35,9 @@ UNMAP_AFTER_INIT RNG::RNG(NonnullOwnPtr<TransportEntity> transport_entity)
{ {
} }
bool RNG::handle_device_config_change() ErrorOr<void> RNG::handle_device_config_change()
{ {
return false; // Device has no config return Error::from_errno(EIO); // Device has no config
} }
void RNG::handle_queue_update(u16 queue_index) void RNG::handle_queue_update(u16 queue_index)

View file

@ -27,7 +27,7 @@ public:
private: private:
virtual StringView class_name() const override { return "VirtIORNG"sv; } virtual StringView class_name() const override { return "VirtIORNG"sv; }
explicit RNG(NonnullOwnPtr<TransportEntity>); explicit RNG(NonnullOwnPtr<TransportEntity>);
virtual bool handle_device_config_change() override; virtual ErrorOr<void> handle_device_config_change() override;
virtual void handle_queue_update(u16 queue_index) override; virtual void handle_queue_update(u16 queue_index) override;
void request_entropy_from_host(); void request_entropy_from_host();

View file

@ -135,16 +135,16 @@ void TransportEntity::notify_queue(Badge<VirtIO::Device>, NotifyQueueDescriptor
config_write16(*m_notify_cfg, descriptor.possible_notify_offset * m_notify_multiplier, descriptor.queue_index); config_write16(*m_notify_cfg, descriptor.possible_notify_offset * m_notify_multiplier, descriptor.queue_index);
} }
bool TransportEntity::activate_queue(Badge<VirtIO::Device>, u16 queue_index) ErrorOr<void> TransportEntity::activate_queue(Badge<VirtIO::Device>, u16 queue_index)
{ {
if (!m_common_cfg) if (!m_common_cfg)
return false; return Error::from_errno(ENXIO);
config_write16(*m_common_cfg, COMMON_CFG_QUEUE_SELECT, queue_index); config_write16(*m_common_cfg, COMMON_CFG_QUEUE_SELECT, queue_index);
config_write16(*m_common_cfg, COMMON_CFG_QUEUE_ENABLE, true); config_write16(*m_common_cfg, COMMON_CFG_QUEUE_ENABLE, true);
dbgln_if(VIRTIO_DEBUG, "Queue[{}] activated", queue_index); dbgln_if(VIRTIO_DEBUG, "Queue[{}] activated", queue_index);
return true; return {};
} }
u64 TransportEntity::get_device_features() u64 TransportEntity::get_device_features()

View file

@ -30,7 +30,7 @@ public:
u16 possible_notify_offset; u16 possible_notify_offset;
}; };
void notify_queue(Badge<VirtIO::Device>, NotifyQueueDescriptor); void notify_queue(Badge<VirtIO::Device>, NotifyQueueDescriptor);
bool activate_queue(Badge<VirtIO::Device>, u16 queue_index); ErrorOr<void> activate_queue(Badge<VirtIO::Device>, u16 queue_index);
ErrorOr<NonnullOwnPtr<Queue>> setup_queue(Badge<VirtIO::Device>, u16 queue_index); ErrorOr<NonnullOwnPtr<Queue>> setup_queue(Badge<VirtIO::Device>, u16 queue_index);
void set_status_bits(Badge<VirtIO::Device>, u8 status_bits); void set_status_bits(Badge<VirtIO::Device>, u8 status_bits);
void reset_device(Badge<VirtIO::Device>); void reset_device(Badge<VirtIO::Device>);

View file

@ -150,7 +150,7 @@ ErrorOr<void> VirtIOGraphicsAdapter::initialize_virtio_resources()
TRY(VirtIO::Device::initialize_virtio_resources()); TRY(VirtIO::Device::initialize_virtio_resources());
auto* config = TRY(transport_entity().get_config(VirtIO::ConfigurationType::Device)); auto* config = TRY(transport_entity().get_config(VirtIO::ConfigurationType::Device));
m_device_configuration = config; m_device_configuration = config;
bool success = negotiate_features([&](u64 supported_features) { TRY(negotiate_features([&](u64 supported_features) {
u64 negotiated = 0; u64 negotiated = 0;
if (is_feature_set(supported_features, VIRTIO_GPU_F_VIRGL)) { if (is_feature_set(supported_features, VIRTIO_GPU_F_VIRGL)) {
dbgln_if(VIRTIO_DEBUG, "VirtIO::GraphicsAdapter: VirGL is available, enabling"); dbgln_if(VIRTIO_DEBUG, "VirtIO::GraphicsAdapter: VirGL is available, enabling");
@ -160,21 +160,17 @@ ErrorOr<void> VirtIOGraphicsAdapter::initialize_virtio_resources()
if (is_feature_set(supported_features, VIRTIO_GPU_F_EDID)) if (is_feature_set(supported_features, VIRTIO_GPU_F_EDID))
negotiated |= VIRTIO_GPU_F_EDID; negotiated |= VIRTIO_GPU_F_EDID;
return negotiated; return negotiated;
}));
transport_entity().read_config_atomic([&]() {
m_num_scanouts = transport_entity().config_read32(*config, DEVICE_NUM_SCANOUTS);
}); });
if (success) { dbgln_if(VIRTIO_DEBUG, "VirtIO::GraphicsAdapter: num_scanouts: {}", m_num_scanouts);
transport_entity().read_config_atomic([&]() { TRY(setup_queues(2)); // CONTROLQ + CURSORQ
m_num_scanouts = transport_entity().config_read32(*config, DEVICE_NUM_SCANOUTS);
});
dbgln_if(VIRTIO_DEBUG, "VirtIO::GraphicsAdapter: num_scanouts: {}", m_num_scanouts);
success = setup_queues(2); // CONTROLQ + CURSORQ
}
if (!success)
return Error::from_errno(EIO);
finish_init(); finish_init();
return {}; return {};
} }
bool VirtIOGraphicsAdapter::handle_device_config_change() ErrorOr<void> VirtIOGraphicsAdapter::handle_device_config_change()
{ {
auto events = get_pending_events(); auto events = get_pending_events();
if (events & VIRTIO_GPU_EVENT_DISPLAY) { if (events & VIRTIO_GPU_EVENT_DISPLAY) {
@ -184,9 +180,9 @@ bool VirtIOGraphicsAdapter::handle_device_config_change()
} }
if (events & ~VIRTIO_GPU_EVENT_DISPLAY) { if (events & ~VIRTIO_GPU_EVENT_DISPLAY) {
dbgln("VirtIO::GraphicsAdapter: Got unknown device config change event: {:#x}", events); dbgln("VirtIO::GraphicsAdapter: Got unknown device config change event: {:#x}", events);
return false; return Error::from_errno(EIO);
} }
return true; return {};
} }
void VirtIOGraphicsAdapter::handle_queue_update(u16) void VirtIOGraphicsAdapter::handle_queue_update(u16)

View file

@ -69,7 +69,7 @@ private:
ErrorOr<void> initialize_adapter(); ErrorOr<void> initialize_adapter();
virtual bool handle_device_config_change() override; virtual ErrorOr<void> handle_device_config_change() override;
virtual void handle_queue_update(u16 queue_index) override; virtual void handle_queue_update(u16 queue_index) override;
u32 get_pending_events(); u32 get_pending_events();
void clear_pending_events(u32 event_bitmask); void clear_pending_events(u32 event_bitmask);

View file

@ -127,7 +127,7 @@ UNMAP_AFTER_INIT ErrorOr<void> VirtIONetworkAdapter::initialize_virtio_resources
TRY(Device::initialize_virtio_resources()); TRY(Device::initialize_virtio_resources());
m_device_config = TRY(transport_entity().get_config(VirtIO::ConfigurationType::Device)); m_device_config = TRY(transport_entity().get_config(VirtIO::ConfigurationType::Device));
bool success = negotiate_features([&](u64 supported_features) { TRY(negotiate_features([&](u64 supported_features) {
u64 negotiated = 0; u64 negotiated = 0;
if (is_feature_set(supported_features, VIRTIO_NET_F_STATUS)) if (is_feature_set(supported_features, VIRTIO_NET_F_STATUS))
negotiated |= VIRTIO_NET_F_STATUS; negotiated |= VIRTIO_NET_F_STATUS;
@ -138,17 +138,10 @@ UNMAP_AFTER_INIT ErrorOr<void> VirtIONetworkAdapter::initialize_virtio_resources
if (is_feature_set(supported_features, VIRTIO_NET_F_MTU)) if (is_feature_set(supported_features, VIRTIO_NET_F_MTU))
negotiated |= VIRTIO_NET_F_MTU; negotiated |= VIRTIO_NET_F_MTU;
return negotiated; return negotiated;
}); }));
if (!success)
return Error::from_errno(EIO);
success = handle_device_config_change(); TRY(handle_device_config_change());
if (!success) TRY(setup_queues(2)); // receive & transmit
return Error::from_errno(EIO);
success = setup_queues(2); // receive & transmit
if (!success)
return Error::from_errno(EIO);
finish_init(); finish_init();
@ -168,7 +161,7 @@ UNMAP_AFTER_INIT ErrorOr<void> VirtIONetworkAdapter::initialize_virtio_resources
return {}; return {};
} }
bool VirtIONetworkAdapter::handle_device_config_change() ErrorOr<void> VirtIONetworkAdapter::handle_device_config_change()
{ {
dbgln_if(VIRTIO_DEBUG, "VirtIONetworkAdapter: handle_device_config_change"); dbgln_if(VIRTIO_DEBUG, "VirtIONetworkAdapter: handle_device_config_change");
transport_entity().read_config_atomic([&]() { transport_entity().read_config_atomic([&]() {
@ -196,7 +189,7 @@ bool VirtIONetworkAdapter::handle_device_config_change()
m_link_duplex = duplex == 0x01; m_link_duplex = duplex == 0x01;
} }
}); });
return true; return {};
} }
void VirtIONetworkAdapter::handle_queue_update(u16 queue_index) void VirtIONetworkAdapter::handle_queue_update(u16 queue_index)

View file

@ -37,7 +37,7 @@ private:
explicit VirtIONetworkAdapter(StringView interface_name, NonnullOwnPtr<VirtIO::TransportEntity>); explicit VirtIONetworkAdapter(StringView interface_name, NonnullOwnPtr<VirtIO::TransportEntity>);
// VirtIO::Device // VirtIO::Device
virtual bool handle_device_config_change() override; virtual ErrorOr<void> handle_device_config_change() override;
virtual void handle_queue_update(u16 queue_index) override; virtual void handle_queue_update(u16 queue_index) override;
// NetworkAdapter // NetworkAdapter