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:
Christian Helmuth 2024-03-25 09:43:22 +01:00
parent 56ee01bc8c
commit 044d8bca44
14 changed files with 118 additions and 226 deletions

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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 {

View File

@ -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_ */

View File

@ -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);

View File

@ -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;
}

View File

@ -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);

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.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,

View File

@ -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;

View File

@ -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;

View File

@ -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; }

View File

@ -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();
});
}

View File

@ -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();
}