xref: /haiku/src/system/kernel/int.cpp (revision 1345706a9ff6ad0dc041339a02d4259998b0765d)
1 /*
2  * Copyright 2002-2010, Axel Dörfler, axeld@pinc-software.de.
3  * Distributed under the terms of the MIT License.
4  *
5  * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
6  * Distributed under the terms of the NewOS License.
7  */
8 
9 
10 #include <int.h>
11 
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 
16 #include <arch/debug_console.h>
17 #include <arch/int.h>
18 #include <boot/kernel_args.h>
19 #include <elf.h>
20 #include <util/kqueue.h>
21 #include <smp.h>
22 
23 #include "kernel_debug_config.h"
24 
25 
26 //#define TRACE_INT
27 #ifdef TRACE_INT
28 #	define TRACE(x) dprintf x
29 #else
30 #	define TRACE(x) ;
31 #endif
32 
33 
34 struct io_handler {
35 	struct io_handler	*next;
36 	interrupt_handler	func;
37 	void				*data;
38 	bool				use_enable_counter;
39 	bool				no_handled_info;
40 #if DEBUG_INTERRUPTS
41 	int64				handled_count;
42 #endif
43 };
44 
45 struct io_vector {
46 	struct io_handler	*handler_list;
47 	spinlock			vector_lock;
48 	int32				enable_count;
49 	bool				no_lock_vector;
50 #if DEBUG_INTERRUPTS
51 	int64				handled_count;
52 	int64				unhandled_count;
53 	int					trigger_count;
54 	int					ignored_count;
55 #endif
56 };
57 
58 static struct io_vector sVectors[NUM_IO_VECTORS];
59 
60 
61 #if DEBUG_INTERRUPTS
62 static int
63 dump_int_statistics(int argc, char **argv)
64 {
65 	int i;
66 	for (i = 0; i < NUM_IO_VECTORS; i++) {
67 		struct io_handler *io;
68 
69 		if (!B_SPINLOCK_IS_LOCKED(&sVectors[i].vector_lock)
70 			&& sVectors[i].enable_count == 0
71 			&& sVectors[i].handled_count == 0
72 			&& sVectors[i].unhandled_count == 0
73 			&& sVectors[i].handler_list == NULL)
74 			continue;
75 
76 		kprintf("int %3d, enabled %ld, handled %8lld, unhandled %8lld%s%s\n",
77 			i, sVectors[i].enable_count, sVectors[i].handled_count,
78 			sVectors[i].unhandled_count,
79 			B_SPINLOCK_IS_LOCKED(&sVectors[i].vector_lock) ? ", ACTIVE" : "",
80 			sVectors[i].handler_list == NULL ? ", no handler" : "");
81 
82 		for (io = sVectors[i].handler_list; io != NULL; io = io->next) {
83 			const char *symbol, *imageName;
84 			bool exactMatch;
85 
86 			status_t error = elf_debug_lookup_symbol_address((addr_t)io->func,
87 				NULL, &symbol, &imageName, &exactMatch);
88 			if (error == B_OK && exactMatch) {
89 				if (strchr(imageName, '/') != NULL)
90 					imageName = strrchr(imageName, '/') + 1;
91 
92 				int length = 4 + strlen(imageName);
93 				kprintf("   %s:%-*s (%p)", imageName, 45 - length, symbol,
94 					io->func);
95 			} else
96 				kprintf("\t\t\t\t\t   func %p", io->func);
97 
98 			kprintf(", data %p, handled ", io->data);
99 			if (io->no_handled_info)
100 				kprintf("<unknown>\n");
101 			else
102 				kprintf("%8lld\n", io->handled_count);
103 		}
104 
105 		kprintf("\n");
106 	}
107 	return 0;
108 }
109 #endif
110 
111 
112 //	#pragma mark - private kernel API
113 
114 
115 bool
116 interrupts_enabled(void)
117 {
118 	return arch_int_are_interrupts_enabled();
119 }
120 
121 
122 status_t
123 int_init(kernel_args* args)
124 {
125 	TRACE(("init_int_handlers: entry\n"));
126 
127 	return arch_int_init(args);
128 }
129 
130 
131 status_t
132 int_init_post_vm(kernel_args* args)
133 {
134 	int i;
135 
136 	/* initialize the vector list */
137 	for (i = 0; i < NUM_IO_VECTORS; i++) {
138 		B_INITIALIZE_SPINLOCK(&sVectors[i].vector_lock);
139 		sVectors[i].enable_count = 0;
140 		sVectors[i].no_lock_vector = false;
141 #if DEBUG_INTERRUPTS
142 		sVectors[i].handled_count = 0;
143 		sVectors[i].unhandled_count = 0;
144 		sVectors[i].trigger_count = 0;
145 		sVectors[i].ignored_count = 0;
146 #endif
147 		sVectors[i].handler_list = NULL;
148 	}
149 
150 #if DEBUG_INTERRUPTS
151 	add_debugger_command("ints", &dump_int_statistics,
152 		"list interrupt statistics");
153 #endif
154 
155 	return arch_int_init_post_vm(args);
156 }
157 
158 
159 status_t
160 int_init_io(kernel_args* args)
161 {
162 	return arch_int_init_io(args);
163 }
164 
165 
166 status_t
167 int_init_post_device_manager(kernel_args* args)
168 {
169 	arch_debug_install_interrupt_handlers();
170 
171 	return arch_int_init_post_device_manager(args);
172 }
173 
174 
175 /*!	Actually process an interrupt via the handlers registered for that
176 	vector (IRQ).
177 */
178 int
179 int_io_interrupt_handler(int vector, bool levelTriggered)
180 {
181 	int status = B_UNHANDLED_INTERRUPT;
182 	struct io_handler* io;
183 	bool handled = false;
184 
185 	if (!sVectors[vector].no_lock_vector)
186 		acquire_spinlock(&sVectors[vector].vector_lock);
187 
188 #if !DEBUG_INTERRUPTS
189 	// The list can be empty at this place
190 	if (sVectors[vector].handler_list == NULL) {
191 		dprintf("unhandled io interrupt %d\n", vector);
192 		if (!sVectors[vector].no_lock_vector)
193 			release_spinlock(&sVectors[vector].vector_lock);
194 		return B_UNHANDLED_INTERRUPT;
195 	}
196 #endif
197 
198 	// For level-triggered interrupts, we actually handle the return
199 	// value (ie. B_HANDLED_INTERRUPT) to decide wether or not we
200 	// want to call another interrupt handler.
201 	// For edge-triggered interrupts, however, we always need to call
202 	// all handlers, as multiple interrupts cannot be identified. We
203 	// still make sure the return code of this function will issue
204 	// whatever the driver thought would be useful.
205 
206 	for (io = sVectors[vector].handler_list; io != NULL; io = io->next) {
207 		status = io->func(io->data);
208 
209 #if DEBUG_INTERRUPTS
210 		if (status != B_UNHANDLED_INTERRUPT)
211 			io->handled_count++;
212 #endif
213 		if (levelTriggered && status != B_UNHANDLED_INTERRUPT)
214 			break;
215 
216 		if (status == B_HANDLED_INTERRUPT || status == B_INVOKE_SCHEDULER)
217 			handled = true;
218 	}
219 
220 #if DEBUG_INTERRUPTS
221 	sVectors[vector].trigger_count++;
222 	if (status != B_UNHANDLED_INTERRUPT || handled) {
223 		sVectors[vector].handled_count++;
224 	} else {
225 		sVectors[vector].unhandled_count++;
226 		sVectors[vector].ignored_count++;
227 	}
228 
229 	if (sVectors[vector].trigger_count > 10000) {
230 		if (sVectors[vector].ignored_count > 9900) {
231 			struct io_handler *last = sVectors[vector].handler_list;
232 			while (last && last->next)
233 				last = last->next;
234 
235 			if (last != NULL && last->no_handled_info) {
236 				// we have an interrupt handler installed that does not
237 				// know whether or not it has actually handled the interrupt,
238 				// so this unhandled count is inaccurate and we can't just
239 				// disable
240 			} else {
241 				if (sVectors[vector].handler_list == NULL
242 					|| sVectors[vector].handler_list->next == NULL) {
243 					// this interrupt vector is not shared, disable it
244 					sVectors[vector].enable_count = -100;
245 					arch_int_disable_io_interrupt(vector);
246 					dprintf("Disabling unhandled io interrupt %d\n", vector);
247 				} else {
248 					// this is a shared interrupt vector, we cannot just disable it
249 					dprintf("More than 99%% interrupts of vector %d are unhandled\n",
250 						vector);
251 				}
252 			}
253 		}
254 
255 		sVectors[vector].trigger_count = 0;
256 		sVectors[vector].ignored_count = 0;
257 	}
258 #endif
259 
260 	if (!sVectors[vector].no_lock_vector)
261 		release_spinlock(&sVectors[vector].vector_lock);
262 
263 	if (levelTriggered)
264 		return status;
265 
266 	// edge triggered return value
267 
268 	if (handled)
269 		return B_HANDLED_INTERRUPT;
270 
271 	return B_UNHANDLED_INTERRUPT;
272 }
273 
274 
275 //	#pragma mark - public API
276 
277 
278 #undef disable_interrupts
279 #undef restore_interrupts
280 
281 
282 cpu_status
283 disable_interrupts(void)
284 {
285 	return arch_int_disable_interrupts();
286 }
287 
288 
289 void
290 restore_interrupts(cpu_status status)
291 {
292 	arch_int_restore_interrupts(status);
293 }
294 
295 
296 /*!	Install a handler to be called when an interrupt is triggered
297 	for the given interrupt number with \a data as the argument.
298 */
299 status_t
300 install_io_interrupt_handler(long vector, interrupt_handler handler, void *data,
301 	ulong flags)
302 {
303 	struct io_handler *io = NULL;
304 	cpu_status state;
305 
306 	if (vector < 0 || vector >= NUM_IO_VECTORS)
307 		return B_BAD_VALUE;
308 
309 	io = (struct io_handler *)malloc(sizeof(struct io_handler));
310 	if (io == NULL)
311 		return B_NO_MEMORY;
312 
313 	arch_debug_remove_interrupt_handler(vector);
314 		// There might be a temporary debug interrupt installed on this
315 		// vector that should be removed now.
316 
317 	io->func = handler;
318 	io->data = data;
319 	io->use_enable_counter = (flags & B_NO_ENABLE_COUNTER) == 0;
320 	io->no_handled_info = (flags & B_NO_HANDLED_INFO) != 0;
321 #if DEBUG_INTERRUPTS
322 	io->handled_count = 0LL;
323 #endif
324 
325 	// Disable the interrupts, get the spinlock for this irq only
326 	// and then insert the handler
327 	state = disable_interrupts();
328 	acquire_spinlock(&sVectors[vector].vector_lock);
329 
330 	if ((flags & B_NO_HANDLED_INFO) != 0
331 		&& sVectors[vector].handler_list != NULL) {
332 		// The driver registering this interrupt handler doesn't know
333 		// whether or not it actually handled the interrupt after the
334 		// handler returns. This is incompatible with shared interrupts
335 		// as we'd potentially steal interrupts from other handlers
336 		// resulting in interrupt storms. Therefore we enqueue this interrupt
337 		// handler as the very last one, meaning all other handlers will
338 		// get their go at any interrupt first.
339 		struct io_handler *last = sVectors[vector].handler_list;
340 		while (last->next)
341 			last = last->next;
342 
343 		io->next = NULL;
344 		last->next = io;
345 	} else {
346 		// A normal interrupt handler, just add it to the head of the list.
347 		io->next = sVectors[vector].handler_list;
348 		sVectors[vector].handler_list = io;
349 	}
350 
351 	// If B_NO_ENABLE_COUNTER is set, we're being asked to not alter
352 	// whether the interrupt should be enabled or not
353 	if (io->use_enable_counter) {
354 		if (sVectors[vector].enable_count++ == 0)
355 			arch_int_enable_io_interrupt(vector);
356 	}
357 
358 	// If B_NO_LOCK_VECTOR is specified this is a vector that is not supposed
359 	// to have multiple handlers and does not require locking of the vector
360 	// when entering the handler. For example this is used by internally
361 	// registered interrupt handlers like for handling local APIC interrupts
362 	// that may run concurently on multiple CPUs. Locking with a spinlock
363 	// would in that case defeat the purpose as it would serialize calling the
364 	// handlers in parallel on different CPUs.
365 	if (flags & B_NO_LOCK_VECTOR)
366 		sVectors[vector].no_lock_vector = true;
367 
368 	release_spinlock(&sVectors[vector].vector_lock);
369 	restore_interrupts(state);
370 
371 	return B_OK;
372 }
373 
374 
375 /*!	Remove a previously installed interrupt handler */
376 status_t
377 remove_io_interrupt_handler(long vector, interrupt_handler handler, void *data)
378 {
379 	status_t status = B_BAD_VALUE;
380 	struct io_handler *io = NULL;
381 	struct io_handler *last = NULL;
382 	cpu_status state;
383 
384 	if (vector < 0 || vector >= NUM_IO_VECTORS)
385 		return B_BAD_VALUE;
386 
387 	/* lock the structures down so it is not modified while we search */
388 	state = disable_interrupts();
389 	acquire_spinlock(&sVectors[vector].vector_lock);
390 
391 	/* loop through the available handlers and try to find a match.
392 	 * We go forward through the list but this means we start with the
393 	 * most recently added handlers.
394 	 */
395 	for (io = sVectors[vector].handler_list; io != NULL; io = io->next) {
396 		/* we have to match both function and data */
397 		if (io->func == handler && io->data == data) {
398 			if (last != NULL)
399 				last->next = io->next;
400 			else
401 				sVectors[vector].handler_list = io->next;
402 
403 			// Check if we need to disable the interrupt
404 			if (io->use_enable_counter && --sVectors[vector].enable_count == 0)
405 				arch_int_disable_io_interrupt(vector);
406 
407 			status = B_OK;
408 			break;
409 		}
410 
411 		last = io;
412 	}
413 
414 	release_spinlock(&sVectors[vector].vector_lock);
415 	restore_interrupts(state);
416 
417 	// if the handler could be found and removed, we still have to free it
418 	if (status == B_OK)
419 		free(io);
420 
421 	return status;
422 }
423 
424