What Is Chained Irq in Linux, When Are They Need to Used

Side effects on re enabling an IRQ line from within an interrupt handler

Because say, if the interrupts were OFF and then you acquire a spin_lock_irq and then spin_unlock_irq. This spin_unlock_irq will enable the interrupts back again. But you had started with the state of the system where interrupts were OFF and now they are ON. This is undesirable.

So, what you are doing using spin_lock_irqsave is to *save/preserve * the state of IRQ (ON or OFF) and when you do a spin_lock_irqrestore, you return to the IRQ state that was set before you acquired the lock.

From http://www.linuxjournal.com/article/5833:

With a call to spin_unlock_irqrestore(), interrupts are restored to
the state when the lock was acquired.

Risk:
When the interrupts are supposed to be disabled and you enable it and there is an IRQ handler that was running (with IRQ disabled) could lead to nesting of interrupts and too much of nesting can crash your system (for eg a network IRQ that interrupts thousands of times within a second).


From the kernel source:

spin_lock_irq

326 static inline void spin_lock_irq(spinlock_t *lock)
327 {
328 raw_spin_lock_irq(&lock->rlock);
329 }

spin_lock_irqsave

331 #define spin_lock_irqsave(lock, flags)                          \
332 do { \
333 raw_spin_lock_irqsave(spinlock_check(lock), flags); \
334 } while (0)

Here you are first checking the state of IRQ and preserving it.

290 static inline raw_spinlock_t *spinlock_check(spinlock_t *lock)
291 {
292 return &lock->rlock;
293 }

And similar source you can find for spin_unlock_irq & spin_unlock_irqrestore

Interrupt handling in linux. Interrupts sharing

The kernel function request_irq calls setup_irq (in kernel/irq/manage.c). They are chained in the order they were associated with the IRQ. That is, each ISR is added to the end of the list.

The irq in kernel function asm_do_IRQ() is different from the one I request in module

This observation is likely due to the mapping between physical and virtual IRQ numbers. The numbers seen in your driver are virtual IRQ numbers, valid only when using the generic linux interrupt handling subsystem. The interrupt number in asm_do_IRQ will be the physical interrupt number provided by the interrupt fabric of the core.

I believe the OMAP processors support interrupts on GPIO pins. The way this is usually implemented is to allocate a single IRQ line for a bank of GPIO inputs, say 32 bits. When an interrupt occurs on any of the GPIOs, that IRQ line will activate. This is likely the number 62 on your processor. If you look in the manual for your processor, you should see that IRQ 62 corresponds to an interrupt on a GPIO bank.

Now, the linux GPIO subsystem will allow you to allocate an interrupt handler to any of the GPIOs, providing you with a mapping from a linux irq number to a physical irq number. The linux irq number in your case is 196. The GPIO subsystem is configured to handle all GPIO interrupts (say interrupt 62), read the GPIO register to determine which of the GPIO bits in a bank could have generated an interrupt, and then calls out the interrupt handler you've assigned with request_irq.

Here's a basic flow of control for a GPIO interrupt:

  1. A change occurs on an interrupt in a GPIO bank. IRQ 62 is raised.
  2. asm_do_IRQ runs on IRQ 62. The GPIO subsystem has been registered to handle IRQ 62 by the platform init code.
  3. The GPIO subsystem reads the GPIO registers and determines that GPIO bit X has caused the interrupt. It calculates the mapping from bit X to the linux virtual IRQ number, in this case 196.
  4. The GPIO interrupt handler then calls the generic_handle_irq function with 196, which calls your interrupt handler.

There is usually a static mapping defined by the platform between virtual IRQ numbers and physical IRQ numbers. To see this mapping,

  • enable CONFIG_VIRQ_DEBUG on kernels older than linux-3.4, or
  • enable CONFIG_IRQ_DOMAIN_DEBUG on newer kernels.

Then have a look to irq_domain_mapping debugfs file. E.g. on PowerPC:

# mount -t debugfs none /sys/kernel/debug
# cat /sys/kernel/debug/irq_domain_mapping
irq hwirq chip name chip data domain name
16 0x00009 IPIC 0xcf801c80 /soc8347@e0000000/pic@700
18 0x00012 IPIC 0xcf801c80 /soc8347@e0000000/pic@700
19 0x0000e IPIC 0xcf801c80 /soc8347@e0000000/pic@700
20 0x0000f IPIC 0xcf801c80 /soc8347@e0000000/pic@700
21 0x00010 IPIC 0xcf801c80 /soc8347@e0000000/pic@700
77 0x0004d IPIC 0xcf801c80 /soc8347@e0000000/pic@700

Doing interrupt chaining assignment

I'm not using it anymore, but just wanted to play again with probably the assembler I gave my first steps in assembly:

.186
.MODEL TINY, C

.code
ORG 100h

Entry:
; Install handler
push ds
xor cx, cx
mov ds, cx
mov ax, ds:[8*4]
mov dx, ds:[8*4+2]
cli
mov ds:[8*4], OFFSET InterruptHandler
mov ds:[8*4+2], cs
pop ds
mov word ptr [OldIntVect], ax
mov word ptr [OldIntVect+2], dx
sti

; Wait for the user to press a key. In the meantime you should see lots of wildcards!
xor ax, ax
int 16h

; Restore original handler
mov ax, word ptr [OldIntVect]
mov dx, word ptr [OldIntVect+2]
push ds
xor cx, cx
mov ds, cx
cli
mov ds:[8*4], ax
mov ds:[8*4+2], dx
sti
pop ds

; Exit to DOS
int 20h

PROC MyHandler

mov ah, 0Eh
mov al, '*'
int 10h
ret
ENDP


InterruptHandler:
pushf
call cs:[OldIntVect]
cmp [busy], 0
jne ExitHandler ; If jumps then the timer was faster than the time it takes for MyHandler to complete

mov cs:[busy], 1
pusha
call MyHandler ; Other options are using a pointer to function or just inlining the code here.
popa
mov cs:[busy], 0

ExitHandler:
iret

OldIntVect dd ?
busy db ?

END Entry

Tested under WinXP (32-bit):

>tasm timer.asm
Turbo Assembler Version 1.01 Copyright (c) 1988, 1989 Borland International

Assembling file: TIMER.ASM
Error messages: None
Warning messages: None
Remaining memory: 481k


>tlink /t timer.obj
Turbo Link Version 3.0 Copyright (c) 1987, 1990 Borland International

>timer
***************************

But this of course is only valid for a DOS environment (DOSBox, Windows 32-bit versions, etc.) and with some tweaks for a bootloader at most.

Anyway, thanks for the beautiful time you just gave me reviving all this :P

What does interrupt service routines of an interrupt handler mean?

Not knowing the context, I can understand the text in two different ways:

first

In most modern operating systems the actual interrupt handler is a function in the operating system which does some operations, calls a function in a device driver and then performs some more operations.

Both the words "interrupt service routine" and "interrupt handler" might be used for the function in the operating system or for the function in the device driver.

The book may name the operating system's function "interrupt handler" and the function in the driver "interrupt service routine". In this case the book describes how different device drivers handling the same interrupt work.

second

When an interrupt occurs, the "interrupt service routine" is started. The book names the function itself "interrupt handler" and each time the function is started, the book speaks about one "interrupt service routine".

In this case the book describes what exactly happens if an interrupt occurs multiple times and the "interrupt service routine" has not finished before the next interrupt happens.

Startup code for -- linux IRQ interrupt hander for ARM

The actual interrupt handling is set up by linux/arch/arm/kernel/entry-armv.S. There is then a long chain of code involved in decoding and running interrupt handlers.

The actual request_irq is generic code, which sets up a "descriptor", irq_desc, defined in linux/include/linux/irqdesc.h.

The actual handling of "which interrupt is which" is configured in the specific setup for the board. I'm giving an example of an omap2/omap3 board here (randomly chosen because I have worked with those boards, but not in Linux):
linux/arch/arm/mach-omap2/irq.c

I hope this helps.



Related Topics



Leave a reply



Submit