The Irq in Kernel Function Asm_Do_Irq() Is Different from the One I Request in Module

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

Error while raising interrupt 11 with inline asm into kernel module

This used to work on older kernel versions, but fails on later versions. The reason is that the generic IRQ handler do_IRQ() has been changed for better IRQ handling performance. Instead of using the irq_to_desc() function to get the IRQ descriptor, it reads it from the per-CPU data. The descriptor is put there during the physical device initialization. Since this pseudo device driver don't have a physical device, do_IRQ() don't find it there and returns with an error.
If we want to simulate IRQ using software interrupt, we must first write the IRQ descriptor to the per-CPU data. Unfortunately, the symbol vector_irq, the array of the IRQ descriptors in the per-CPU data, is not exported to kernel modules during kernel compilation. The only way to change it, is to recompile the whole kernel. If you think it worth the effort, you can add the line:

EXPORT_SYMBOL (vector_irq);

in the file: arch/x86/kernel/irq.c

right after all the include lines.
After compiling and booting from the newly compiled kernel, change your driver as follows:

Add an include line:

    #include <asm/hw_irq.h>

change the read function to:

static ssize_t etx_read(struct file *filp,
char __user *buf, size_t len, loff_t *off)
{
struct irq_desc *desc;

printk(KERN_INFO "Read function\n");
desc = irq_to_desc(11);
if (!desc) return -EINVAL;
__this_cpu_write(vector_irq[59], desc);
asm("int $0x3B"); // Corresponding to irq 11
return 0;
}

Simple interrupt handler: request_irq returns error code -22

You can't pass a NULL context (last parameters of the request_irq() call) when dealing with a shared interrupt line (IRQF_SHARED flag is on).

To understand why consider the following scenario: you have two identical network cards sharing the same IRQ. The same driver will pass the same interrupt handler function, the same irq number and the same description. There is no way to distinguish the two instances of the registration except via the context parameter.

Therefore, as a precaution, you can't pass a NULL context parameter if you pass the IRQF_SHARED flag.

Section mismatch in reference from a function to another function during Linux kernel build after adding #pragma GCC optimize(O0)

TL;DR: gic_smp_init() should be annotated with __init.


Update: this was fixed in kernel v5.8, here's the relevant commit.

Looks to me like you found a bug: gic_smp_init() is only called by __init gic_init_bases(), so it has no real reason to not be annotated with __init too. The function set_smp_cross_call() is annotated with __init, so callers should also be annotated with __init.

If you compile with optimizations (without the pragma) this inconsistency disappears because the compiler simply inlines the entire body of gic_smp_init() into gic_init_bases(): the call chain is then just gic_init_bases -> set_smp_cross_call and everything is fine since they are both annotated __init. However, when you disable optimizations (#pragma GCC optimize ("O0")) the compiler no longer inlines the call to gic_smp_init(), which stays on its own as an actual function, and the inconsistency reveals itself.

The authors of the module probably missed the __init annotation of set_smp_cross_call(), or the annotation was added later and nobody noticed it also needed to be "propagated" in that driver code.

There also seems to be an inconsistency between the annotations for set_smp_cross_call(): in the C files (/arch/{arm,arm64}/kernel/smp.c) it is annotated with __init, while in the header files (/arch/{arm,arm64}/include/asm/smp.h) it is not. The latter instances should probably be annotated too as the doc-comment for the __init macro suggests:

 * If the function has a prototype somewhere, you can also add
* __init between closing brace of the prototype and semicolon:
*
* extern int initialize_foobar_device(int, int, int) __init;


Related Topics



Leave a reply



Submit