* Introduce a (private) flag B_NO_HANDLED_INFO for install_io_interrupt_handler.
It causes the interrupt handler to be inserted at the very end of the list instead of at the top. It is intended to be used as a workaround when a interrupt handler cannot know if it actually handled the interrupt. This should never be used by native drivers. Also if we know that the result is not valid because of this flag we won't disable the vector in case we count many unhandled interrupts as those numbers are then unreliable. * Moved B_NO_LOCK_VECTOR to be a private flag as well. * Made the interrupt handler list a simple manually maintaned singly linked list instead of the doubly linked one used with insque and remque as it greatly simplifies things for such an easy use case and is more compact. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@29241 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -51,7 +51,6 @@ typedef int32 (*interrupt_handler)(void *data);
|
||||
|
||||
/* Flags that can be passed to install_io_interrupt_handler() */
|
||||
#define B_NO_ENABLE_COUNTER 1
|
||||
#define B_NO_LOCK_VECTOR 2
|
||||
|
||||
|
||||
/* timer interrupts services */
|
||||
|
||||
@@ -12,6 +12,10 @@
|
||||
#include <KernelExport.h>
|
||||
#include <arch/int.h>
|
||||
|
||||
// private install_io_interrupt_handler() flags
|
||||
#define B_NO_LOCK_VECTOR 0x100
|
||||
#define B_NO_HANDLED_INFO 0x200
|
||||
|
||||
struct kernel_args;
|
||||
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#include <arch/int.h>
|
||||
#include <arch/cpu.h>
|
||||
#include <int.h>
|
||||
|
||||
#include "hpet.h"
|
||||
|
||||
|
||||
+59
-26
@@ -33,14 +33,14 @@
|
||||
|
||||
struct io_handler {
|
||||
struct io_handler *next;
|
||||
struct io_handler *prev;
|
||||
interrupt_handler func;
|
||||
void *data;
|
||||
bool use_enable_counter;
|
||||
bool no_handled_info;
|
||||
};
|
||||
|
||||
struct io_vector {
|
||||
struct io_handler handler_list;
|
||||
struct io_handler * handler_list;
|
||||
spinlock vector_lock;
|
||||
int32 enable_count;
|
||||
bool no_lock_vector;
|
||||
@@ -67,18 +67,16 @@ dump_int_statistics(int argc, char **argv)
|
||||
&& sVectors[i].enable_count == 0
|
||||
&& sVectors[i].handled_count == 0
|
||||
&& sVectors[i].unhandled_count == 0
|
||||
&& sVectors[i].handler_list.next == &sVectors[i].handler_list)
|
||||
&& sVectors[i].handler_list == NULL)
|
||||
continue;
|
||||
|
||||
kprintf("int %3d, enabled %ld, handled %8lld, unhandled %8lld%s%s\n",
|
||||
i, sVectors[i].enable_count, sVectors[i].handled_count,
|
||||
sVectors[i].unhandled_count,
|
||||
B_SPINLOCK_IS_LOCKED(&sVectors[i].vector_lock) ? ", ACTIVE" : "",
|
||||
sVectors[i].handler_list.next == &sVectors[i].handler_list
|
||||
? ", no handler" : "");
|
||||
sVectors[i].handler_list == NULL ? ", no handler" : "");
|
||||
|
||||
for (io = sVectors[i].handler_list.next;
|
||||
io != &sVectors[i].handler_list; io = io->next) {
|
||||
for (io = sVectors[i].handler_list; io != NULL; io = io->next) {
|
||||
const char *symbol, *imageName;
|
||||
bool exactMatch;
|
||||
|
||||
@@ -134,7 +132,7 @@ int_init_post_vm(kernel_args *args)
|
||||
sVectors[i].trigger_count = 0;
|
||||
sVectors[i].ignored_count = 0;
|
||||
#endif
|
||||
initque(&sVectors[i].handler_list); /* initialize handler queue */
|
||||
sVectors[i].handler_list = NULL;
|
||||
}
|
||||
|
||||
#if DEBUG_INTERRUPTS
|
||||
@@ -168,13 +166,15 @@ int_io_interrupt_handler(int vector, bool levelTriggered)
|
||||
if (!sVectors[vector].no_lock_vector)
|
||||
acquire_spinlock(&sVectors[vector].vector_lock);
|
||||
|
||||
#if !DEBUG_INTERRUPTS
|
||||
// The list can be empty at this place
|
||||
if (sVectors[vector].handler_list.next == &sVectors[vector].handler_list) {
|
||||
if (sVectors[vector].handler_list == NULL) {
|
||||
dprintf("unhandled io interrupt %d\n", vector);
|
||||
if (!sVectors[vector].no_lock_vector)
|
||||
release_spinlock(&sVectors[vector].vector_lock);
|
||||
return B_UNHANDLED_INTERRUPT;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* For level-triggered interrupts, we actually handle the return
|
||||
* value (ie. B_HANDLED_INTERRUPT) to decide wether or not we
|
||||
@@ -185,9 +185,7 @@ int_io_interrupt_handler(int vector, bool levelTriggered)
|
||||
* whatever the driver thought would be useful (ie. B_INVOKE_SCHEDULER)
|
||||
*/
|
||||
|
||||
for (io = sVectors[vector].handler_list.next;
|
||||
io != &sVectors[vector].handler_list;
|
||||
io = io->next) {
|
||||
for (io = sVectors[vector].handler_list; io != NULL; io = io->next) {
|
||||
status = io->func(io->data);
|
||||
|
||||
if (levelTriggered && status != B_UNHANDLED_INTERRUPT)
|
||||
@@ -210,16 +208,27 @@ int_io_interrupt_handler(int vector, bool levelTriggered)
|
||||
|
||||
if (sVectors[vector].trigger_count > 10000) {
|
||||
if (sVectors[vector].ignored_count > 9900) {
|
||||
if (sVectors[vector].handler_list.next == NULL
|
||||
|| sVectors[vector].handler_list.next->next == NULL) {
|
||||
// this interrupt vector is not shared, disable it
|
||||
sVectors[vector].enable_count = -100;
|
||||
arch_int_disable_io_interrupt(vector);
|
||||
dprintf("Disabling unhandled io interrupt %d\n", vector);
|
||||
struct io_handler *last = sVectors[vector].handler_list;
|
||||
while (last && last->next)
|
||||
last = last->next;
|
||||
|
||||
if (last != NULL && last->no_handled_info) {
|
||||
// we have an interrupt handler installed that does not
|
||||
// know whether or not it has actually handled the interrupt,
|
||||
// so this unhandled count is inaccurate and we can't just
|
||||
// disable
|
||||
} else {
|
||||
// this is a shared interrupt vector, we cannot just disable it
|
||||
dprintf("More than 99%% interrupts of vector %d are unhandled\n",
|
||||
vector);
|
||||
if (sVectors[vector].handler_list == NULL
|
||||
|| sVectors[vector].handler_list->next == NULL) {
|
||||
// this interrupt vector is not shared, disable it
|
||||
sVectors[vector].enable_count = -100;
|
||||
arch_int_disable_io_interrupt(vector);
|
||||
dprintf("Disabling unhandled io interrupt %d\n", vector);
|
||||
} else {
|
||||
// this is a shared interrupt vector, we cannot just disable it
|
||||
dprintf("More than 99%% interrupts of vector %d are unhandled\n",
|
||||
vector);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -290,13 +299,33 @@ install_io_interrupt_handler(long vector, interrupt_handler handler, void *data,
|
||||
io->func = handler;
|
||||
io->data = data;
|
||||
io->use_enable_counter = (flags & B_NO_ENABLE_COUNTER) == 0;
|
||||
io->no_handled_info = (flags & B_NO_HANDLED_INFO) == 0;
|
||||
|
||||
// Disable the interrupts, get the spinlock for this irq only
|
||||
// and then insert the handler
|
||||
state = disable_interrupts();
|
||||
acquire_spinlock(&sVectors[vector].vector_lock);
|
||||
|
||||
insque(io, &sVectors[vector].handler_list);
|
||||
if ((flags & B_NO_HANDLED_INFO) != 0
|
||||
&& sVectors[vector].handler_list != NULL) {
|
||||
// The driver registering this interrupt handler doesn't know
|
||||
// whether or not it actually handled the interrupt after the
|
||||
// handler returns. This is incompatible with shared interrupts
|
||||
// as we'd potentially steal interrupts from other handlers
|
||||
// resulting in interrupt storms. Therefore we enqueue this interrupt
|
||||
// handler as the very last one, meaning all other handlers will
|
||||
// get their go at any interrupt first.
|
||||
struct io_handler *last = sVectors[vector].handler_list;
|
||||
while (last->next)
|
||||
last = last->next;
|
||||
|
||||
io->next = NULL;
|
||||
last->next = io;
|
||||
} else {
|
||||
// A normal interrupt handler, just add it to the head of the list.
|
||||
io->next = sVectors[vector].handler_list;
|
||||
sVectors[vector].handler_list = io;
|
||||
}
|
||||
|
||||
// If B_NO_ENABLE_COUNTER is set, we're being asked to not alter
|
||||
// whether the interrupt should be enabled or not
|
||||
@@ -328,6 +357,7 @@ remove_io_interrupt_handler(long vector, interrupt_handler handler, void *data)
|
||||
{
|
||||
status_t status = B_BAD_VALUE;
|
||||
struct io_handler *io = NULL;
|
||||
struct io_handler *last = NULL;
|
||||
cpu_status state;
|
||||
|
||||
if (vector < 0 || vector >= NUM_IO_VECTORS)
|
||||
@@ -341,12 +371,13 @@ remove_io_interrupt_handler(long vector, interrupt_handler handler, void *data)
|
||||
* We go forward through the list but this means we start with the
|
||||
* most recently added handlers.
|
||||
*/
|
||||
for (io = sVectors[vector].handler_list.next;
|
||||
io != &sVectors[vector].handler_list;
|
||||
io = io->next) {
|
||||
for (io = sVectors[vector].handler_list; io != NULL; io = io->next) {
|
||||
/* we have to match both function and data */
|
||||
if (io->func == handler && io->data == data) {
|
||||
remque(io);
|
||||
if (last != NULL)
|
||||
last->next = io->next;
|
||||
else
|
||||
sVectors[vector].handler_list = io->next;
|
||||
|
||||
// Check if we need to disable the interrupt
|
||||
if (io->use_enable_counter && --sVectors[vector].enable_count == 0)
|
||||
@@ -355,6 +386,8 @@ remove_io_interrupt_handler(long vector, interrupt_handler handler, void *data)
|
||||
status = B_OK;
|
||||
break;
|
||||
}
|
||||
|
||||
last = io;
|
||||
}
|
||||
|
||||
release_spinlock(&sVectors[vector].vector_lock);
|
||||
|
||||
Reference in New Issue
Block a user