lx_kit: support multiple pending IRQs

Decoupling the scheduler execution can lead to missed interrupts
because the current implementation only handles one pending
interrupt and requires immediate processing.

This commit introduces a helper object that is used to capture
any occuring interrupts that are then handled consecutively.

Issue #4927.
This commit is contained in:
Josef Söntgen 2023-06-20 16:17:19 +02:00 committed by Norman Feske
parent caac994da8
commit 1cac134030
13 changed files with 116 additions and 47 deletions

View File

@ -31,7 +31,7 @@ int lx_emul_irq_task_function(void * data);
extern void * lx_emul_irq_task_struct;
unsigned int lx_emul_irq_last(void);
int lx_emul_pending_irq(void);
int lx_emul_irq_init(struct device_node *node, struct device_node *parent);

View File

@ -23,6 +23,8 @@
#include <util/list.h>
#include <util/xml_node.h>
#include <lx_kit/pending_irq.h>
namespace Lx_kit {
using namespace Genode;
@ -63,7 +65,7 @@ class Lx_kit::Device : List<Device>::Element
using Index = Platform::Device::Irq::Index;
Index idx;
unsigned number;
Pending_irq number;
Io_signal_handler<Irq> handler;
bool masked { true };
bool occured { false };

View File

@ -22,6 +22,7 @@
#include <lx_kit/memory.h>
#include <lx_kit/scheduler.h>
#include <lx_kit/timeout.h>
#include <lx_kit/pending_irq.h>
namespace Lx_kit {
@ -50,7 +51,6 @@ struct Lx_kit::Env
Scheduler scheduler { env.ep() };
Device_list devices { env.ep(), heap, platform };
Lx_kit::Timeout timeout { timer, scheduler };
unsigned int last_irq { 0 };
Env(Genode::Env & env) : env(env) { }
};

View File

@ -0,0 +1,34 @@
/*
* \brief Lx_kit pending interrupt utility
* \author Josef Soentgen
* \date 2023-06-22
*/
/*
* Copyright (C) 2022 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#ifndef _LX_KIT__PENDING_IRQ_H_
#define _LX_KIT__PENDING_IRQ_H_
#include <util/fifo.h>
namespace Lx_kit {
using namespace Genode;
struct Pending_irq;
}
struct Lx_kit::Pending_irq : Genode::Fifo<Lx_kit::Pending_irq>::Element
{
unsigned value;
Pending_irq(unsigned value) : value (value) { }
};
#endif /* _LX_KIT__PENDING_IRQ_H_ */

View File

@ -15,8 +15,10 @@
*/
#include <base/entrypoint.h>
#include <util/fifo.h>
#include <util/list.h>
#include <lx_kit/task.h>
#include <lx_kit/pending_irq.h>
namespace Lx_kit {
class Scheduler;
@ -45,6 +47,8 @@ class Lx_kit::Scheduler
Signal_handler<Scheduler> _execute_schedule;
Genode::Fifo<Lx_kit::Pending_irq> _pending_irqs { };
public:
Task & current();
@ -60,7 +64,13 @@ class Lx_kit::Scheduler
void execute();
void unblock_irq_handler();
void unblock_irq_handler(Pending_irq &);
template <typename FN> void pending_irq(FN const &fn)
{
_pending_irqs.dequeue([&] (Pending_irq const &pirq) {
fn(pirq.value); });
}
void unblock_time_handler();
Task & task(void * t);

View File

@ -39,7 +39,12 @@ extern "C" void lx_emul_irq_eoi(unsigned int irq)
}
extern "C" unsigned int lx_emul_irq_last()
extern "C" int lx_emul_pending_irq()
{
return Lx_kit::env().last_irq;
int pending_irq = -1;
Lx_kit::env().scheduler.pending_irq([&] (unsigned int irq) {
pending_irq = (int)irq; });
return pending_irq;
}

View File

@ -60,7 +60,7 @@ lx_emul_pci_for_each_device(void * bus, lx_emul_add_device_callback_t fn)
env().devices.for_each([&] (Device & d) {
unsigned irq = 0;
d.for_each_irq([&] (Device::Irq & i) {
if (!irq) irq = i.number; });
if (!irq) irq = i.number.value; });
d.for_pci_config([&] (Device::Pci_config & cfg) {
fn(bus, num++, d.name().string(), cfg.vendor_id, cfg.device_id,

View File

@ -30,6 +30,8 @@ namespace {
Lx_kit::Env &_env;
Lx_kit::Pending_irq _pending_irq { 0 };
public:
struct Number { unsigned value; };
@ -38,11 +40,9 @@ namespace {
void trigger_irq(Number number)
{
/*
* Mirrored from 'Lx_kit::Device::Irq::handle'
*/
_env.last_irq = number.value;
_env.scheduler.unblock_irq_handler();
_pending_irq.value = number.value;
_env.scheduler.unblock_irq_handler(_pending_irq);
_env.scheduler.schedule();
}
};

View File

@ -171,22 +171,28 @@ int lx_emul_irq_task_function(void * data)
for (;;) {
lx_emul_task_schedule(true);
local_irq_save(flags);
irq_enter();
for (;;) {
irq = lx_emul_pending_irq();
if (irq == -1)
break;
irq = dde_irq_domain ? irq_find_mapping(dde_irq_domain,
lx_emul_irq_last())
: lx_emul_irq_last();
local_irq_save(flags);
irq_enter();
if (!irq) {
ack_bad_irq(irq);
WARN_ONCE(true, "Unexpected interrupt %d received!\n", lx_emul_irq_last());
} else {
generic_handle_irq(irq);
irq = dde_irq_domain ? irq_find_mapping(dde_irq_domain,
irq)
: irq;
if (!irq) {
ack_bad_irq(irq);
WARN_ONCE(true, "Unexpected interrupt %d received!\n", irq);
} else {
generic_handle_irq(irq);
}
irq_exit();
local_irq_restore(flags);
}
irq_exit();
local_irq_restore(flags);
}
return 0;

View File

@ -53,22 +53,27 @@ int lx_emul_irq_task_function(void * data)
for (;;) {
lx_emul_task_schedule(true);
local_irq_save(flags);
irq_enter();
for (;;) {
irq = lx_emul_irq_last();
irq = lx_emul_pending_irq();
if (irq == -1)
break;
if (!irq) {
ack_bad_irq(irq);
WARN_ONCE(true, "Unexpected interrupt %d received!\n",
lx_emul_irq_last());
} else {
generic_handle_irq(irq);
lx_emul_irq_eoi(irq);
local_irq_save(flags);
irq_enter();
if (!irq) {
ack_bad_irq(irq);
WARN_ONCE(true, "Unexpected interrupt %d received!\n",
irq);
} else {
generic_handle_irq(irq);
lx_emul_irq_eoi(irq);
}
irq_exit();
local_irq_restore(flags);
}
irq_exit();
local_irq_restore(flags);
}
return 0;

View File

@ -25,7 +25,12 @@ extern "C" void lx_emul_irq_mask(unsigned int ) { }
extern "C" void lx_emul_irq_eoi(unsigned int ) { }
extern "C" unsigned int lx_emul_irq_last()
extern "C" int lx_emul_pending_irq()
{
return Lx_kit::env().last_irq;
int pending_irq = -1;
Lx_kit::env().scheduler.pending_irq([&] (unsigned int irq) {
pending_irq = (int)irq; });
return pending_irq;
}

View File

@ -56,8 +56,7 @@ void Device::Irq::handle()
if (masked)
return;
env().last_irq = number;
env().scheduler.unblock_irq_handler();
env().scheduler.unblock_irq_handler(number);
}
@ -65,7 +64,8 @@ Device::Irq::Irq(Entrypoint & ep, unsigned idx, unsigned number)
:
idx{idx},
number(number),
handler(ep, *this, &Irq::_handle) { }
handler(ep, *this, &Irq::_handle)
{ }
/************
@ -144,7 +144,7 @@ bool Device::irq_unmask(unsigned number)
bool ret = false;
for_each_irq([&] (Irq & irq) {
if (irq.number != number)
if (irq.number.value != number)
return;
ret = true;
@ -170,7 +170,7 @@ void Device::irq_mask(unsigned number)
return;
for_each_irq([&] (Irq & irq) {
if (irq.number == number) irq.masked = true; });
if (irq.number.value == number) irq.masked = true; });
}
@ -181,7 +181,7 @@ void Device::irq_ack(unsigned number)
return;
for_each_irq([&] (Irq & irq) {
if (irq.number != number || !irq.occured || !irq.session.constructed())
if (irq.number.value != number || !irq.occured || !irq.session.constructed())
return;
irq.occured = false;
irq.session->ack();

View File

@ -74,8 +74,10 @@ void Scheduler::remove(Task & task)
}
void Scheduler::unblock_irq_handler()
void Scheduler::unblock_irq_handler(Pending_irq &pirq)
{
_pending_irqs.enqueue(pirq);
for (Task * t = _present_list.first(); t; t = t->next()) {
if (t->type() == Task::IRQ_HANDLER) t->unblock();
}