Fix interrupt worker quality.

This commit is contained in:
Jonas 'Sortie' Termansen 2014-09-14 20:05:34 +02:00
parent 76577e69f1
commit 741e48e688
4 changed files with 89 additions and 79 deletions

View File

@ -366,9 +366,8 @@ static void Clock__FireTimer(void* timer_ptr)
timer->clock->UnlockClock(); timer->clock->UnlockClock();
} }
static void Clock__FireTimer_InterruptWorker(void* timer_ptr_ptr, size_t) static void Clock__FireTimer_InterruptWorker(void* timer_ptr, void*, size_t)
{ {
void* timer_ptr = *((void**) timer_ptr_ptr);
Clock__FireTimer(timer_ptr); Clock__FireTimer(timer_ptr);
} }
@ -389,8 +388,7 @@ void Clock::FireTimer(Timer* timer)
else else
{ {
timer->flags |= TIMER_FIRING; timer->flags |= TIMER_FIRING;
Interrupt::ScheduleWork(Clock__FireTimer_InterruptWorker, &timer, Interrupt::ScheduleWork(Clock__FireTimer_InterruptWorker, timer, NULL, 0);
sizeof(timer));
} }
} }

View File

@ -115,8 +115,10 @@ void Init();
void InitWorker(); void InitWorker();
void WorkerThread(void* user); void WorkerThread(void* user);
typedef void(*WorkHandler)(void* payload, size_t payloadsize); bool ScheduleWork(void (*handler)(void*, void*, size_t),
bool ScheduleWork(WorkHandler handler, void* payload, size_t payloadsize); void* handler_context,
void* payload,
size_t payload_size);
} // namespace Interrupt } // namespace Interrupt
} // namespace Sortix } // namespace Sortix

View File

@ -37,115 +37,127 @@
#include <sortix/kernel/syscall.h> #include <sortix/kernel/syscall.h>
#include <sortix/kernel/thread.h> #include <sortix/kernel/thread.h>
// TODO: The interrupt worker isn't a reliable design.
namespace Sortix { namespace Sortix {
namespace Interrupt { namespace Interrupt {
// TODO: This implementation is a bit hacky and can be optimized. unsigned char* queue;
volatile size_t queue_offset;
volatile size_t queue_used;
const size_t QUEUE_SIZE = 4096;
uint8_t* queue; struct worker_package
uint8_t* storage;
volatile size_t queueoffset;
volatile size_t queueused;
size_t queuesize;
struct Package
{ {
size_t size; size_t payload_size;
size_t payloadoffset; void (*handler)(void*, void*, size_t);
size_t payloadsize; void* handler_context;
WorkHandler handler;
uint8_t payload[0];
}; };
void InitWorker() void InitWorker()
{ {
const size_t QUEUE_SIZE = 4UL*1024UL; queue = new unsigned char[QUEUE_SIZE];
static_assert(QUEUE_SIZE % sizeof(Package) == 0, "QUEUE_SIZE must be a multiple of the package size");
queue = new uint8_t[QUEUE_SIZE];
if ( !queue ) if ( !queue )
Panic("Can't allocate interrupt worker queue"); Panic("Can't allocate interrupt worker queue");
storage = new uint8_t[QUEUE_SIZE]; queue_offset = 0;
if ( !storage ) queue_used = 0;
Panic("Can't allocate interrupt worker storage");
queuesize = QUEUE_SIZE;
queueoffset = 0;
queueused = 0;
} }
static void WriteToQueue(const void* src, size_t size) static void WriteToQueue(const void* src, size_t size)
{ {
const uint8_t* buf = (const uint8_t*) src; assert(size <= QUEUE_SIZE - queue_used);
size_t writeat = (queueoffset + queueused) % queuesize; const unsigned char* input = (const unsigned char*) src;
size_t available = queuesize - writeat; for ( size_t i = 0; i < size; i++ )
size_t count = available < size ? available : size; {
memcpy(queue + writeat, buf, count); size_t index = (queue_offset + queue_used + i) % QUEUE_SIZE;
queueused += count; queue[index] = input[i];
if ( count < size ) }
WriteToQueue(buf + count, size - count); queue_used += size;
} }
static void ReadFromQueue(void* dest, size_t size) static void ReadFromQueue(void* dst, size_t size)
{ {
uint8_t* buf = (uint8_t*) dest; assert(size <= queue_used);
size_t available = queuesize - queueoffset; unsigned char* output = (unsigned char*) dst;
size_t count = available < size ? available : size; for ( size_t i = 0; i < size; i++ )
memcpy(buf, queue + queueoffset, count); {
queueused -= count; size_t index = (queue_offset + i) % QUEUE_SIZE;
queueoffset = (queueoffset + count) % queuesize; output[i] = queue[index];
if ( count < size ) }
ReadFromQueue(buf + count, size - count); queue_offset = (queue_offset + size) % QUEUE_SIZE;
queue_used -= size;
} }
static Package* PopPackage(uint8_t** payloadp, Package* /*prev*/) static bool PopPackage(struct worker_package* package,
unsigned char* payload,
size_t payload_size)
{ {
Package* package = NULL; bool interrupts_was_enabled = Interrupt::SetEnabled(false);
uint8_t* payload = NULL; if ( !queue_used )
Interrupt::Disable(); {
Interrupt::SetEnabled(interrupts_was_enabled);
return false;
}
if ( !queueused )
goto out;
package = (Package*) storage;
ReadFromQueue(package, sizeof(*package)); ReadFromQueue(package, sizeof(*package));
payload = storage + sizeof(*package); if ( !(package->payload_size <= payload_size) )
ReadFromQueue(payload, package->payloadsize); {
*payloadp = payload; Interrupt::SetEnabled(interrupts_was_enabled);
assert(package->payload_size <= payload_size);
queue_offset = (queue_offset + package->payload_size) % QUEUE_SIZE;
return false;
}
out: ReadFromQueue(payload, package->payload_size);
Interrupt::Enable();
return package; Interrupt::SetEnabled(interrupts_was_enabled);
return true;
} }
void WorkerThread(void* /*user*/) void WorkerThread(void* /*user*/)
{ {
assert(Interrupt::IsEnabled()); assert(Interrupt::IsEnabled());
uint8_t* payload = NULL;
Package* package = NULL; struct worker_package package;
size_t storage_size = QUEUE_SIZE;
unsigned char* storage = new unsigned char[storage_size];
if ( !storage )
Panic("Can't allocate interrupt worker storage");
while ( true ) while ( true )
{ {
package = PopPackage(&payload, package); if ( !PopPackage(&package, storage, storage_size) )
if ( !package ) { Scheduler::Yield(); continue; } {
size_t payloadsize = package->payloadsize; Scheduler::Yield();
package->handler(payload, payloadsize); continue;
}
unsigned char* payload = storage;
size_t payload_size = package.payload_size;
assert(package.handler);
package.handler(package.handler_context, payload, payload_size);
} }
} }
bool ScheduleWork(WorkHandler handler, void* payload, size_t payloadsize) bool ScheduleWork(void (*handler)(void*, void*, size_t),
void* handler_context,
void* payload,
size_t payload_size)
{ {
assert(!Interrupt::IsEnabled()); assert(!Interrupt::IsEnabled());
Package package; assert(handler);
package.size = sizeof(package) + payloadsize; assert(payload || !payload_size);
package.payloadoffset = 0; // Currently unused
package.payloadsize = payloadsize;
package.handler = handler;
size_t queuefreespace = queuesize - queueused; struct worker_package package;
if ( queuefreespace < package.size ) package.payload_size = payload_size;
package.handler = handler;
package.handler_context = handler_context;
if ( QUEUE_SIZE - queue_used < sizeof(package) + payload_size )
return false; return false;
WriteToQueue(&package, sizeof(package)); WriteToQueue(&package, sizeof(package));
WriteToQueue(payload, payloadsize); WriteToQueue(payload, payload_size);
return true; return true;
} }

View File

@ -89,15 +89,14 @@ PS2Keyboard::~PS2Keyboard()
struct PS2KeyboardWork struct PS2KeyboardWork
{ {
PS2Keyboard* kb;
uint8_t scancode; uint8_t scancode;
}; };
static void PS2Keyboard__InterruptWork(void* payload, size_t size) static void PS2Keyboard__InterruptWork(void* kb_ptr, void* payload, size_t size)
{ {
assert(size == sizeof(PS2KeyboardWork)); assert(size == sizeof(PS2KeyboardWork));
PS2KeyboardWork* work = (PS2KeyboardWork*) payload; PS2KeyboardWork* work = (PS2KeyboardWork*) payload;
work->kb->InterruptWork(work->scancode); ((PS2Keyboard*) kb_ptr)->InterruptWork(work->scancode);
} }
void PS2Keyboard::OnInterrupt(CPU::InterruptRegisters* regs) void PS2Keyboard::OnInterrupt(CPU::InterruptRegisters* regs)
@ -117,9 +116,8 @@ void PS2Keyboard::OnInterrupt(CPU::InterruptRegisters* regs)
Debugger::Run(); Debugger::Run();
} }
PS2KeyboardWork work; PS2KeyboardWork work;
work.kb = this;
work.scancode = scancode; work.scancode = scancode;
Interrupt::ScheduleWork(PS2Keyboard__InterruptWork, &work, sizeof(work)); Interrupt::ScheduleWork(PS2Keyboard__InterruptWork, this, &work, sizeof(work));
} }
void PS2Keyboard::InterruptWork(uint8_t scancode) void PS2Keyboard::InterruptWork(uint8_t scancode)