Kernel: Use a WaitQueue in PATAChannel

Instead of waking up repeatedly to check if a disk operation has
finished, use a WaitQueue and wake it up in the IRQ handler.

This simplifies the device driver a bit, and makes it more responsive
as well :^)
This commit is contained in:
Andreas Kling 2019-12-01 12:47:53 +01:00
parent 9ed272ce98
commit 8b129476b1
Notes: sideshowbarker 2024-07-19 11:00:36 +09:00
3 changed files with 17 additions and 40 deletions

View File

@ -97,6 +97,8 @@ PATAChannel::PATAChannel(ChannelType type, bool force_pio)
, m_io_base((type == ChannelType::Primary ? 0x1F0 : 0x170))
, m_control_base((type == ChannelType::Primary ? 0x3f6 : 0x376))
{
disable_irq();
m_dma_enabled.resource() = true;
ProcFS::add_sys_bool("ide_dma", m_dma_enabled);
@ -144,20 +146,12 @@ static void print_ide_status(u8 status)
(status & ATA_SR_ERR) != 0);
}
bool PATAChannel::wait_for_irq()
void PATAChannel::wait_for_irq()
{
#ifdef PATA_DEBUG
kprintf("PATAChannel: waiting for IRQ %d...\n", irq_number());
#endif
while (!m_interrupted) {
// FIXME: Put this process into a Blocked state instead, it's stupid to wake up just to check a flag.
Scheduler::yield();
}
#ifdef PATA_DEBUG
kprintf("PATAChannel: received IRQ %d!\n", irq_number());
#endif
return true;
cli();
enable_irq();
current->wait_on(m_irq_queue);
disable_irq();
}
void PATAChannel::handle_irq()
@ -173,7 +167,7 @@ void PATAChannel::handle_irq()
#ifdef PATA_DEBUG
kprintf("PATAChannel: interrupt: DRQ=%u BSY=%u DRDY=%u\n", (status & ATA_SR_DRQ) != 0, (status & ATA_SR_BSY) != 0, (status & ATA_SR_DRDY) != 0);
#endif
m_interrupted = true;
m_irq_queue.wake_all();
}
static void io_delay()
@ -186,8 +180,6 @@ void PATAChannel::detect_disks()
{
// There are only two possible disks connected to a channel
for (auto i = 0; i < 2; i++) {
enable_irq();
IO::out8(m_io_base + ATA_REG_HDDEVSEL, 0xA0 | (i << 4)); // First, we need to select the drive itself
// Apparently these need to be 0 before sending IDENTIFY?!
@ -257,8 +249,6 @@ bool PATAChannel::ata_read_sectors_with_dma(u32 lba, u16 count, u8* outbuf, bool
current->pid(), lba, count, outbuf);
#endif
disable_irq();
m_prdt.offset = m_dma_buffer_page->paddr();
m_prdt.size = 512 * count;
@ -276,9 +266,6 @@ bool PATAChannel::ata_read_sectors_with_dma(u32 lba, u16 count, u8* outbuf, bool
// Set transfer direction
IO::out8(m_bus_master_base, 0x8);
m_interrupted = false;
enable_irq();
while (IO::in8(m_io_base + ATA_REG_STATUS) & ATA_SR_BSY)
;
@ -315,7 +302,6 @@ bool PATAChannel::ata_read_sectors_with_dma(u32 lba, u16 count, u8* outbuf, bool
IO::out8(m_bus_master_base, 0x9);
wait_for_irq();
disable_irq();
if (m_device_error)
return false;
@ -336,8 +322,6 @@ bool PATAChannel::ata_write_sectors_with_dma(u32 lba, u16 count, const u8* inbuf
current->pid(), lba, count, inbuf);
#endif
disable_irq();
m_prdt.offset = m_dma_buffer_page->paddr();
m_prdt.size = 512 * count;
@ -354,9 +338,6 @@ bool PATAChannel::ata_write_sectors_with_dma(u32 lba, u16 count, const u8* inbuf
// Turn on "Interrupt" and "Error" flag. The error flag should be cleared by hardware.
IO::out8(m_bus_master_base + 2, IO::in8(m_bus_master_base + 2) | 0x6);
m_interrupted = false;
enable_irq();
while (IO::in8(m_io_base + ATA_REG_STATUS) & ATA_SR_BSY)
;
@ -393,7 +374,6 @@ bool PATAChannel::ata_write_sectors_with_dma(u32 lba, u16 count, const u8* inbuf
IO::out8(m_bus_master_base, 0x1);
wait_for_irq();
disable_irq();
if (m_device_error)
return false;
@ -415,7 +395,6 @@ bool PATAChannel::ata_read_sectors(u32 start_sector, u16 count, u8* outbuf, bool
start_sector,
outbuf);
#endif
disable_irq();
while (IO::in8(m_io_base + ATA_REG_STATUS) & ATA_SR_BSY)
;
@ -439,8 +418,6 @@ bool PATAChannel::ata_read_sectors(u32 start_sector, u16 count, u8* outbuf, bool
;
IO::out8(m_io_base + ATA_REG_COMMAND, ATA_CMD_READ_PIO);
m_interrupted = false;
enable_irq();
wait_for_irq();
if (m_device_error)
@ -475,7 +452,6 @@ bool PATAChannel::ata_write_sectors(u32 start_sector, u16 count, const u8* inbuf
count,
start_sector);
#endif
disable_irq();
while (IO::in8(m_io_base + ATA_REG_STATUS) & ATA_SR_BSY)
;
@ -512,19 +488,13 @@ bool PATAChannel::ata_write_sectors(u32 start_sector, u16 count, const u8* inbuf
kprintf("PATAChannel: Writing 512 bytes (part %d) (status=%b), inbuf=%p...\n", i, status, inbuf + (512 * i));
#endif
disable_irq();
IO::repeated_out16(m_io_base + ATA_REG_DATA, inbuf + (512 * i), 256);
m_interrupted = false;
enable_irq();
wait_for_irq();
status = IO::in8(m_io_base + ATA_REG_STATUS);
ASSERT(!(status & ATA_SR_BSY));
}
disable_irq();
IO::out8(m_io_base + ATA_REG_COMMAND, ATA_CMD_CACHE_FLUSH);
m_interrupted = false;
enable_irq();
wait_for_irq();
u8 status = IO::in8(m_io_base + ATA_REG_STATUS);
ASSERT(!(status & ATA_SR_BSY));

View File

@ -17,6 +17,7 @@
#include <Kernel/PCI.h>
#include <Kernel/VM/PhysicalAddress.h>
#include <Kernel/VM/PhysicalPage.h>
#include <Kernel/WaitQueue.h>
struct PhysicalRegionDescriptor {
PhysicalAddress offset;
@ -49,7 +50,7 @@ private:
void initialize(bool force_pio);
void detect_disks();
bool wait_for_irq();
void wait_for_irq();
bool ata_read_sectors_with_dma(u32, u16, u8*, bool);
bool ata_write_sectors_with_dma(u32, u16, const u8*, bool);
bool ata_read_sectors(u32, u16, u8*, bool);
@ -60,7 +61,8 @@ private:
u16 m_io_base { 0x1F0 };
u16 m_control_base { 0 };
volatile u8 m_device_error { 0 };
volatile bool m_interrupted { false };
WaitQueue m_irq_queue;
PCI::Address m_pci_address;
PhysicalRegionDescriptor m_prdt;

View File

@ -322,6 +322,11 @@ public:
return block<ConditionBlocker>(state_string, move(condition));
}
void wait_on(WaitQueue& queue)
{
(void)block<WaitQueueBlocker>(queue);
}
void unblock();
// Tell this thread to unblock if needed,