mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-19 21:57:55 +00:00
dde_linux: improve handling of IRQ masking and ack
Unmasking of a pending interrupt did not lead to immediate IRQ handler execution in all cases. This commit also addresses some style concerns risen during the issue discussion. - Replace multi-boolean IRQ state by state enum - EOI and ACK should be same in DDE context - Unify x86 and ARM irqchip.c - Remove Pending_irq type - Remove dde_irq_set_wake() Fixes #5164
This commit is contained in:
parent
56ee01bc8c
commit
044d8bca44
@ -19,6 +19,7 @@ SRC_CC += lx_emul/time.cc
|
||||
SRC_CC += lx_emul/irq_flags.cc
|
||||
|
||||
SRC_C += lx_emul/clocksource.c
|
||||
SRC_C += lx_emul/irqchip.c
|
||||
SRC_C += lx_emul/shadow/fs/exec.c
|
||||
SRC_C += lx_emul/shadow/kernel/cpu.c
|
||||
SRC_C += lx_emul/shadow/kernel/exit.c
|
||||
@ -27,8 +28,8 @@ SRC_C += lx_emul/shadow/kernel/irq_work.c
|
||||
SRC_C += lx_emul/shadow/kernel/locking/spinlock.c
|
||||
SRC_C += lx_emul/shadow/kernel/pid.c
|
||||
SRC_C += lx_emul/shadow/kernel/printk/printk.c
|
||||
SRC_C += lx_emul/shadow/kernel/sched/cputime.c
|
||||
SRC_C += lx_emul/shadow/kernel/sched/core.c
|
||||
SRC_C += lx_emul/shadow/kernel/sched/cputime.c
|
||||
SRC_C += lx_emul/shadow/kernel/sched/fair.c
|
||||
SRC_C += lx_emul/shadow/kernel/sched/sched.c
|
||||
SRC_C += lx_emul/shadow/kernel/smp.c
|
||||
@ -103,7 +104,6 @@ endif
|
||||
SHADOW_INC_DIR := $(DDE_LINUX_DIR)/src/include/lx_emul/shadow/include
|
||||
SPEC_SHADOW_INC_DIR := $(DDE_LINUX_DIR)/src/include/lx_emul/shadow/arch/$(LX_ARCH)/include
|
||||
|
||||
SRC_C += lx_emul/spec/$(GEN_ARCH)/irqchip.c
|
||||
SRC_C += lx_emul/spec/$(GEN_ARCH)/start.c
|
||||
SRC_S += lx_kit/spec/$(SPEC_ARCH)/setjmp.S
|
||||
|
||||
|
@ -25,7 +25,7 @@ void lx_emul_irq_unmask(unsigned int irq);
|
||||
|
||||
void lx_emul_irq_mask(unsigned int irq);
|
||||
|
||||
void lx_emul_irq_eoi(unsigned int irq);
|
||||
void lx_emul_irq_ack(unsigned int irq);
|
||||
|
||||
int lx_emul_irq_task_function(void * data);
|
||||
|
||||
|
@ -23,7 +23,6 @@
|
||||
#include <util/list.h>
|
||||
#include <util/xml_node.h>
|
||||
|
||||
#include <lx_kit/pending_irq.h>
|
||||
|
||||
namespace Lx_kit {
|
||||
using namespace Genode;
|
||||
@ -64,18 +63,21 @@ class Lx_kit::Device : List<Device>::Element
|
||||
{
|
||||
using Index = Platform::Device::Irq::Index;
|
||||
|
||||
enum State { IDLE, PENDING, MASKED, MASKED_PENDING };
|
||||
|
||||
Index idx;
|
||||
Pending_irq number;
|
||||
unsigned number;
|
||||
Io_signal_handler<Irq> handler;
|
||||
bool masked { true };
|
||||
bool occured { false };
|
||||
State state { MASKED };
|
||||
|
||||
Constructible<Platform::Device::Irq> session {};
|
||||
|
||||
Irq(Entrypoint & ep, unsigned idx, unsigned number);
|
||||
|
||||
void _handle();
|
||||
void handle();
|
||||
void mask();
|
||||
void unmask(Platform::Device &);
|
||||
void ack();
|
||||
};
|
||||
|
||||
struct Io_port : List<Io_port>::Element
|
||||
@ -169,6 +171,7 @@ class Lx_kit::Device : List<Device>::Element
|
||||
bool irq_unmask(unsigned irq);
|
||||
void irq_mask(unsigned irq);
|
||||
void irq_ack(unsigned irq);
|
||||
int pending_irq();
|
||||
|
||||
bool read_config(unsigned reg, unsigned len, unsigned *val);
|
||||
bool write_config(unsigned reg, unsigned len, unsigned val);
|
||||
|
@ -22,7 +22,6 @@
|
||||
#include <lx_kit/memory.h>
|
||||
#include <lx_kit/scheduler.h>
|
||||
#include <lx_kit/timeout.h>
|
||||
#include <lx_kit/pending_irq.h>
|
||||
|
||||
namespace Lx_kit {
|
||||
|
||||
|
@ -1,34 +0,0 @@
|
||||
/*
|
||||
* \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_ */
|
@ -18,7 +18,6 @@
|
||||
#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,8 +44,6 @@ class Lx_kit::Scheduler
|
||||
|
||||
void _execute();
|
||||
|
||||
Genode::Fifo<Lx_kit::Pending_irq> _pending_irqs { };
|
||||
|
||||
public:
|
||||
|
||||
Task & current();
|
||||
@ -62,13 +59,7 @@ class Lx_kit::Scheduler
|
||||
|
||||
void execute();
|
||||
|
||||
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_irq_handler();
|
||||
void unblock_time_handler();
|
||||
|
||||
Task & task(void * t);
|
||||
|
@ -32,7 +32,7 @@ extern "C" void lx_emul_irq_mask(unsigned int irq)
|
||||
}
|
||||
|
||||
|
||||
extern "C" void lx_emul_irq_eoi(unsigned int irq)
|
||||
extern "C" void lx_emul_irq_ack(unsigned int irq)
|
||||
{
|
||||
Lx_kit::env().devices.for_each([&] (Lx_kit::Device & d) {
|
||||
d.irq_ack(irq); });
|
||||
@ -43,8 +43,10 @@ extern "C" int lx_emul_pending_irq()
|
||||
{
|
||||
int pending_irq = -1;
|
||||
|
||||
Lx_kit::env().scheduler.pending_irq([&] (unsigned int irq) {
|
||||
pending_irq = (int)irq; });
|
||||
Lx_kit::env().devices.for_each([&] (Lx_kit::Device & d) {
|
||||
if (pending_irq == -1)
|
||||
pending_irq = d.pending_irq();
|
||||
});
|
||||
|
||||
return pending_irq;
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
/*
|
||||
* \brief Linux DDE interrupt controller
|
||||
* \author Stefan Kalkowski
|
||||
* \author Josef Soentgen
|
||||
* \author Christian Helmuth
|
||||
* \date 2021-03-10
|
||||
*/
|
||||
|
||||
@ -19,17 +21,10 @@
|
||||
#include <linux/irqchip.h>
|
||||
#include <../kernel/irq/internals.h>
|
||||
|
||||
static int dde_irq_set_wake(struct irq_data *d, unsigned int on)
|
||||
{
|
||||
lx_emul_trace_and_stop(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void dde_irq_unmask(struct irq_data *d)
|
||||
static void dde_irq_ack(struct irq_data *d)
|
||||
{
|
||||
lx_emul_irq_eoi(d->hwirq);
|
||||
lx_emul_irq_unmask(d->hwirq);
|
||||
lx_emul_irq_ack(d->hwirq);
|
||||
}
|
||||
|
||||
|
||||
@ -39,9 +34,9 @@ static void dde_irq_mask(struct irq_data *d)
|
||||
}
|
||||
|
||||
|
||||
static void dde_irq_eoi(struct irq_data *d)
|
||||
static void dde_irq_unmask(struct irq_data *d)
|
||||
{
|
||||
lx_emul_irq_eoi(d->hwirq);
|
||||
lx_emul_irq_unmask(d->hwirq);
|
||||
}
|
||||
|
||||
|
||||
@ -55,14 +50,16 @@ static int dde_irq_set_type(struct irq_data *d, unsigned int type)
|
||||
|
||||
struct irq_chip dde_irqchip_data_chip = {
|
||||
.name = "dde-irqs",
|
||||
.irq_eoi = dde_irq_eoi,
|
||||
.irq_disable = dde_irq_mask,
|
||||
.irq_ack = dde_irq_ack,
|
||||
.irq_mask = dde_irq_mask,
|
||||
.irq_unmask = dde_irq_unmask,
|
||||
.irq_set_wake = dde_irq_set_wake,
|
||||
.irq_eoi = dde_irq_ack,
|
||||
.irq_set_type = dde_irq_set_type,
|
||||
};
|
||||
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
|
||||
static int dde_domain_translate(struct irq_domain * d,
|
||||
struct irq_fwspec * fwspec,
|
||||
@ -126,6 +123,7 @@ static const struct irq_domain_ops dde_irqchip_data_domain_ops = {
|
||||
|
||||
struct irq_domain *dde_irq_domain;
|
||||
|
||||
|
||||
int lx_emul_irq_init(struct device_node *node, struct device_node *parent)
|
||||
{
|
||||
dde_irq_domain = irq_domain_create_tree(&node->fwnode,
|
||||
@ -167,6 +165,18 @@ IRQCHIP_DECLARE(dde_gic_a9, "arm,cortex-a9-gic", lx_emul_irq_init);
|
||||
IRQCHIP_DECLARE(dde_gic_400, "arm,gic-400", lx_emul_irq_init);
|
||||
|
||||
|
||||
static int map_irq(int irq)
|
||||
{
|
||||
return dde_irq_domain ? irq_find_mapping(dde_irq_domain, irq) : irq;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static int map_irq(int irq) { return irq; }
|
||||
|
||||
#endif /* CONFIG_OF */
|
||||
|
||||
|
||||
int lx_emul_irq_task_function(void * data)
|
||||
{
|
||||
int irq;
|
||||
@ -183,9 +193,7 @@ int lx_emul_irq_task_function(void * data)
|
||||
local_irq_save(flags);
|
||||
irq_enter();
|
||||
|
||||
irq = dde_irq_domain ? irq_find_mapping(dde_irq_domain,
|
||||
irq)
|
||||
: irq;
|
||||
irq = map_irq(irq);
|
||||
|
||||
if (!irq) {
|
||||
ack_bad_irq(irq);
|
@ -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.value; });
|
||||
if (!irq) irq = i.number; });
|
||||
|
||||
d.for_pci_config([&] (Device::Pci_config & cfg) {
|
||||
fn(bus, num++, d.name().string(), cfg.vendor_id, cfg.device_id,
|
||||
|
@ -30,7 +30,7 @@ namespace {
|
||||
|
||||
Lx_kit::Env &_env;
|
||||
|
||||
Lx_kit::Pending_irq _pending_irq { 0 };
|
||||
unsigned _pending_irq = 0;
|
||||
|
||||
public:
|
||||
|
||||
@ -40,9 +40,9 @@ namespace {
|
||||
|
||||
void trigger_irq(Number number)
|
||||
{
|
||||
_pending_irq.value = number.value;
|
||||
_pending_irq = number.value;
|
||||
|
||||
_env.scheduler.unblock_irq_handler(_pending_irq);
|
||||
_env.scheduler.unblock_irq_handler();
|
||||
_env.scheduler.schedule();
|
||||
}
|
||||
};
|
||||
@ -223,9 +223,6 @@ extern "C" void lx_emul_pin_control(char const *pin_name, bool enabled)
|
||||
}
|
||||
|
||||
|
||||
extern "C" void lx_emul_backtrace(void);
|
||||
|
||||
|
||||
extern "C" int lx_emul_pin_sense(char const *pin_name)
|
||||
{
|
||||
bool result = false;
|
||||
|
@ -1,110 +0,0 @@
|
||||
/*
|
||||
* \brief Linux DDE x86 interrupt controller
|
||||
* \author Josef Soentgen
|
||||
* \date 2022-01-20
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2022 Genode Labs GmbH
|
||||
*
|
||||
* This file is distributed under the terms of the GNU General Public License
|
||||
* version 2.
|
||||
*/
|
||||
|
||||
#include <lx_emul/debug.h>
|
||||
#include <lx_emul/irq.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/irqchip.h>
|
||||
#include <../kernel/irq/internals.h>
|
||||
|
||||
|
||||
static void dde_irq_unmask(struct irq_data *d)
|
||||
{
|
||||
lx_emul_irq_unmask(d->hwirq);
|
||||
}
|
||||
|
||||
|
||||
static void dde_irq_mask(struct irq_data *d)
|
||||
{
|
||||
lx_emul_irq_mask(d->hwirq);
|
||||
}
|
||||
|
||||
|
||||
int lx_emul_irq_init(struct device_node *node, struct device_node *parent)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
struct irq_chip dde_irqchip_data_chip = {
|
||||
.name = "dde-irqs",
|
||||
.irq_mask = dde_irq_mask,
|
||||
.irq_disable = dde_irq_mask,
|
||||
.irq_unmask = dde_irq_unmask,
|
||||
.irq_mask_ack = dde_irq_mask,
|
||||
};
|
||||
|
||||
|
||||
int lx_emul_irq_task_function(void * data)
|
||||
{
|
||||
unsigned long flags;
|
||||
int irq;
|
||||
|
||||
for (;;) {
|
||||
lx_emul_task_schedule(true);
|
||||
|
||||
for (;;) {
|
||||
|
||||
irq = lx_emul_pending_irq();
|
||||
if (irq == -1)
|
||||
break;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
struct task_struct irq_task = {
|
||||
.__state = 0,
|
||||
.usage = REFCOUNT_INIT(2),
|
||||
.flags = PF_KTHREAD,
|
||||
.prio = MAX_PRIO - 20,
|
||||
.static_prio = MAX_PRIO - 20,
|
||||
.normal_prio = MAX_PRIO - 20,
|
||||
.policy = SCHED_NORMAL,
|
||||
.cpus_ptr = &irq_task.cpus_mask,
|
||||
.cpus_mask = CPU_MASK_ALL,
|
||||
.nr_cpus_allowed = 1,
|
||||
.mm = NULL,
|
||||
.active_mm = NULL,
|
||||
.tasks = LIST_HEAD_INIT(irq_task.tasks),
|
||||
.real_parent = &irq_task,
|
||||
.parent = &irq_task,
|
||||
.children = LIST_HEAD_INIT(irq_task.children),
|
||||
.sibling = LIST_HEAD_INIT(irq_task.sibling),
|
||||
.group_leader = &irq_task,
|
||||
.comm = "kirqd",
|
||||
.thread = INIT_THREAD,
|
||||
.pending = {
|
||||
.list = LIST_HEAD_INIT(irq_task.pending.list),
|
||||
.signal = {{0}}
|
||||
},
|
||||
.blocked = {{0}},
|
||||
};
|
||||
void * lx_emul_irq_task_struct = &irq_task;
|
@ -22,15 +22,7 @@ extern "C" void lx_emul_irq_unmask(unsigned int ) { }
|
||||
extern "C" void lx_emul_irq_mask(unsigned int ) { }
|
||||
|
||||
|
||||
extern "C" void lx_emul_irq_eoi(unsigned int ) { }
|
||||
extern "C" void lx_emul_irq_ack(unsigned int ) { }
|
||||
|
||||
|
||||
extern "C" int lx_emul_pending_irq()
|
||||
{
|
||||
int pending_irq = -1;
|
||||
|
||||
Lx_kit::env().scheduler.pending_irq([&] (unsigned int irq) {
|
||||
pending_irq = (int)irq; });
|
||||
|
||||
return pending_irq;
|
||||
}
|
||||
extern "C" int lx_emul_pending_irq() { return -1; }
|
||||
|
@ -38,25 +38,65 @@ bool Device::Io_port::match(uint16_t addr)
|
||||
}
|
||||
|
||||
|
||||
/****************
|
||||
** Device::Irq**
|
||||
****************/
|
||||
/*****************
|
||||
** Device::Irq **
|
||||
*****************/
|
||||
|
||||
void Device::Irq::_handle()
|
||||
{
|
||||
handle();
|
||||
switch (state) {
|
||||
case IDLE: state = PENDING; break;
|
||||
case PENDING: state = PENDING; break;
|
||||
case MASKED: state = MASKED_PENDING; break;
|
||||
case MASKED_PENDING: state = MASKED_PENDING; break;
|
||||
}
|
||||
|
||||
env().scheduler.unblock_irq_handler();
|
||||
env().scheduler.schedule();
|
||||
}
|
||||
|
||||
|
||||
void Device::Irq::handle()
|
||||
void Device::Irq::ack()
|
||||
{
|
||||
occured = true;
|
||||
if (session.constructed())
|
||||
session->ack();
|
||||
|
||||
if (masked)
|
||||
return;
|
||||
switch (state) {
|
||||
case IDLE: state = IDLE; break;
|
||||
case PENDING: state = IDLE; break;
|
||||
case MASKED: state = MASKED; break;
|
||||
case MASKED_PENDING: state = MASKED; break;
|
||||
}
|
||||
}
|
||||
|
||||
env().scheduler.unblock_irq_handler(number);
|
||||
|
||||
void Device::Irq::mask()
|
||||
{
|
||||
switch (state) {
|
||||
case IDLE: state = MASKED; break;
|
||||
case MASKED: state = MASKED; break;
|
||||
case PENDING: state = MASKED_PENDING; break;
|
||||
case MASKED_PENDING: state = MASKED_PENDING; break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Device::Irq::unmask(Platform::Device &dev)
|
||||
{
|
||||
if (!session.constructed()) {
|
||||
session.construct(dev, idx);
|
||||
session->sigh_omit_initial_signal(handler);
|
||||
session->ack();
|
||||
}
|
||||
|
||||
switch (state) {
|
||||
case IDLE: state = IDLE; break;
|
||||
case MASKED: state = IDLE; break;
|
||||
case PENDING: state = PENDING; break;
|
||||
case MASKED_PENDING: state = PENDING; break;
|
||||
}
|
||||
|
||||
env().scheduler.unblock_irq_handler();
|
||||
}
|
||||
|
||||
|
||||
@ -139,25 +179,33 @@ void * Device::io_mem_local_addr(addr_t phys_addr, size_t size)
|
||||
}
|
||||
|
||||
|
||||
int Device::pending_irq()
|
||||
{
|
||||
if (!_pdev.constructed())
|
||||
return -1;
|
||||
|
||||
int result = -1;
|
||||
|
||||
for_each_irq([&] (Irq & irq) {
|
||||
if (result == -1 && irq.state == Irq::PENDING)
|
||||
result = irq.number;
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
bool Device::irq_unmask(unsigned number)
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
for_each_irq([&] (Irq & irq) {
|
||||
if (irq.number.value != number)
|
||||
if (irq.number != number)
|
||||
return;
|
||||
|
||||
ret = true;
|
||||
enable();
|
||||
|
||||
if (!irq.session.constructed()) {
|
||||
irq.session.construct(*_pdev, irq.idx);
|
||||
irq.session->sigh_omit_initial_signal(irq.handler);
|
||||
irq.session->ack();
|
||||
}
|
||||
|
||||
irq.masked = false;
|
||||
if (irq.occured) irq.handle();
|
||||
irq.unmask(*_pdev);
|
||||
});
|
||||
|
||||
return ret;
|
||||
@ -170,8 +218,7 @@ void Device::irq_mask(unsigned number)
|
||||
return;
|
||||
|
||||
for_each_irq([&] (Irq & irq) {
|
||||
if (irq.number.value == number) irq.masked = true; });
|
||||
|
||||
if (irq.number == number) irq.mask(); });
|
||||
}
|
||||
|
||||
|
||||
@ -181,10 +228,9 @@ void Device::irq_ack(unsigned number)
|
||||
return;
|
||||
|
||||
for_each_irq([&] (Irq & irq) {
|
||||
if (irq.number.value != number || !irq.occured || !irq.session.constructed())
|
||||
if (irq.number != number)
|
||||
return;
|
||||
irq.occured = false;
|
||||
irq.session->ack();
|
||||
irq.ack();
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -74,10 +74,8 @@ void Scheduler::remove(Task & task)
|
||||
}
|
||||
|
||||
|
||||
void Scheduler::unblock_irq_handler(Pending_irq &pirq)
|
||||
void Scheduler::unblock_irq_handler()
|
||||
{
|
||||
_pending_irqs.enqueue(pirq);
|
||||
|
||||
for (Task * t = _present_list.first(); t; t = t->next()) {
|
||||
if (t->type() == Task::IRQ_HANDLER) t->unblock();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user