Kernel: Add timeout to ACHI commands

ACHI commands can now fail from timeouts.
This commit is contained in:
Bananymous 2023-10-24 11:56:25 +03:00
parent fb801044ec
commit 2d0da93ac4
2 changed files with 26 additions and 13 deletions

View File

@ -30,7 +30,8 @@ namespace Kernel
BAN::Optional<uint32_t> find_free_command_slot();
void handle_irq();
void block_until_irq();
BAN::ErrorOr<void> block_until_command_completed(uint32_t command_slot);
private:
BAN::RefPtr<AHCIController> m_controller;
@ -41,8 +42,6 @@ namespace Kernel
// TODO: can we read straight to user buffer?
BAN::UniqPtr<DMARegion> m_data_dma_region;
volatile bool m_has_got_irq { false };
friend class AHCIController;
};

View File

@ -1,3 +1,4 @@
#include <kernel/Scheduler.h>
#include <kernel/Storage/ATA/AHCI/Controller.h>
#include <kernel/Storage/ATA/AHCI/Device.h>
#include <kernel/Storage/ATA/ATADefinitions.h>
@ -40,7 +41,7 @@ namespace Kernel
m_port->ie = 0xFFFFFFFF;
TRY(read_identify_data());
TRY(detail::ATABaseDevice::initialize({ (const uint16_t*)m_data_dma_region->vaddr(), m_data_dma_region->size() }));
TRY(detail::ATABaseDevice::initialize({ (const uint16_t*)m_data_dma_region->vaddr(), m_data_dma_region->size() / sizeof(uint16_t) }));
return {};
}
@ -120,8 +121,7 @@ namespace Kernel
m_port->ci = 1 << slot.value();
// FIXME: timeout
do { block_until_irq(); } while (m_port->ci & (1 << slot.value()));
TRY(block_until_command_completed(slot.value()));
return {};
}
@ -145,17 +145,32 @@ namespace Kernel
void AHCIDevice::handle_irq()
{
ASSERT(!m_has_got_irq);
uint16_t err = m_port->serr & 0xFFFF;
if (err)
print_error(err);
m_has_got_irq = true;
}
void AHCIDevice::block_until_irq()
BAN::ErrorOr<void> AHCIDevice::block_until_command_completed(uint32_t command_slot)
{
while (!__sync_bool_compare_and_swap(&m_has_got_irq, true, false))
__builtin_ia32_pause();
static constexpr uint64_t total_timeout_ms = 5000;
static constexpr uint64_t poll_timeout_ms = 10;
auto start_time = SystemTimer::get().ms_since_boot();
while (start_time + poll_timeout_ms < SystemTimer::get().ns_since_boot())
if (!(m_port->ci & (1 << command_slot)))
return {};
// FIXME: This should actually block once semaphores support blocking with timeout.
// This doesn't allow scheduler to go properly idle.
while (start_time + total_timeout_ms < SystemTimer::get().ns_since_boot())
{
Scheduler::get().reschedule();
if (!(m_port->ci & (1 << command_slot)))
return {};
}
return BAN::Error::from_errno(ETIMEDOUT);
}
BAN::ErrorOr<void> AHCIDevice::read_sectors_impl(uint64_t lba, uint64_t sector_count, BAN::ByteSpan buffer)
@ -260,8 +275,7 @@ namespace Kernel
m_port->ci = 1 << slot.value();
// FIXME: timeout
do { block_until_irq(); } while (m_port->ci & (1 << slot.value()));
TRY(block_until_command_completed(slot.value()));
return {};
}