Thursday, August 16, 2012

ARM Linux Interrupts Code Flow


ARM Vector Table

vector_irq         -> Interrupt Dispatcher
vector_dabt      -> Data Abort Dispatcher
vector_pabt      -> Perfetch Abort Dispatcher
vector_und       -> Undefined Instruction Dispatcher
vector_fiq        -> Fast Interrupt Dispatcher
vector_addrexcptn   -> Address Exception Dispatcher

Once an exception occurs , one of the above functions gets called.

IRQ Exceptions:

From there ,
/* Assembly Code */
vector_irq   ->> Depending on the ARM mode call the corresponding function
irq_svc
irq_handler
arch_irq_handler_default(arch/arm/kernel) ->> get_irqnr_preamble(arch/arm/mach-omap2) -> set Soc Specific irq base
->>  get_irqnr_and_base -> Check Pending Interrupts

/* C Code */
asm_do_IRQ ->> routine called with r0 = irq number, r1 = struct pt_regs *

Data Aborts:
Control Processor Register is CP15 DFSR & DFAR Register.

vector_dabt(called from ROM code)
(
__dabt_svc | __dabt_usr
)
do_DataAbort

(
do_translation_fault |
do_page_fault |
do_sect_fault |
)


Prefetch Aborts:
Control Processor Register is CP15 IFSR & IFAR Register.

vector_pabt(called from ROM code)
(
__pabt_svc | __pabt_usr
)
do_PrefetchAbort

(
do_translation_fault |
do_page_fault |
do_sect_fault |
)

List of Instruction Faults


static struct fsr_info fsr_info[] = {
        { do_bad,               SIGBUS,  0,             "unknown 0"                     },
        { do_bad,               SIGBUS,  0,             "unknown 1"                     },
        { do_bad,               SIGBUS,  0,             "unknown 2"                     },
        { do_bad,               SIGBUS,  0,             "unknown 3"                     },
        { do_bad,               SIGBUS,  0,             "reserved translation fault"    },
        { do_translation_fault, SIGSEGV, SEGV_MAPERR,   "level 1 translation fault"     },
        { do_translation_fault, SIGSEGV, SEGV_MAPERR,   "level 2 translation fault"     },
        { do_page_fault,        SIGSEGV, SEGV_MAPERR,   "level 3 translation fault"     },
        { do_bad,               SIGBUS,  0,             "reserved access flag fault"    },
        { do_bad,               SIGSEGV, SEGV_ACCERR,   "level 1 access flag fault"     },
        { do_bad,               SIGSEGV, SEGV_ACCERR,   "level 2 access flag fault"     },
        { do_page_fault,        SIGSEGV, SEGV_ACCERR,   "level 3 access flag fault"     },
        { do_bad,               SIGBUS,  0,             "reserved permission fault"     },
        { do_bad,               SIGSEGV, SEGV_ACCERR,   "level 1 permission fault"      },
        { do_sect_fault,        SIGSEGV, SEGV_ACCERR,   "level 2 permission fault"      },
        { do_page_fault,        SIGSEGV, SEGV_ACCERR,   "level 3 permission fault"      },
        { do_bad,               SIGBUS,  0,             "synchronous external abort"    },
        { do_bad,               SIGBUS,  0,             "asynchronous external abort"   },
        { do_bad,               SIGBUS,  0,             "unknown 18"                    },
        { do_bad,               SIGBUS,  0,             "unknown 19"                    },
        { do_bad,               SIGBUS,  0,             "synchronous abort (translation table walk)" },
        { do_bad,               SIGBUS,  0,             "synchronous abort (translation table walk)" },
        { do_bad,               SIGBUS,  0,             "synchronous abort (translation table walk)" },
        { do_bad,               SIGBUS,  0,             "synchronous abort (translation table walk)" },
        { do_bad,               SIGBUS,  0,             "synchronous parity error"      },
        { do_bad,               SIGBUS,  0,             "asynchronous parity error"     },
        { do_bad,               SIGBUS,  0,             "unknown 26"                    },
        { do_bad,               SIGBUS,  0,             "unknown 27"                    },
        { do_bad,               SIGBUS,  0,             "synchronous parity error (translation table walk" },
        { do_bad,               SIGBUS,  0,             "synchronous parity error (translation table walk" },
        { do_bad,               SIGBUS,  0,             "synchronous parity error (translation table walk" },
        { do_bad,               SIGBUS,  0,             "synchronous parity error (translation table walk" },
        { do_bad,               SIGBUS,  0,             "unknown 32"                    },
        { do_bad,               SIGBUS,  BUS_ADRALN,    "alignment fault"               },
        { do_bad,               SIGBUS,  0,             "debug event"                   },
        { do_bad,               SIGBUS,  0,             "unknown 35"                    },
        { do_bad,               SIGBUS,  0,             "unknown 36"                    },
        { do_bad,               SIGBUS,  0,             "unknown 37"                    },
        { do_bad,               SIGBUS,  0,             "unknown 38"                    },
        { do_bad,               SIGBUS,  0,             "unknown 39"                    },
        { do_bad,               SIGBUS,  0,             "unknown 40"                    },
        { do_bad,               SIGBUS,  0,             "unknown 41"                    },
        { do_bad,               SIGBUS,  0,             "unknown 42"                    },
        { do_bad,               SIGBUS,  0,             "unknown 43"                    },
        { do_bad,               SIGBUS,  0,             "unknown 44"                    },
        { do_bad,               SIGBUS,  0,             "unknown 45"                    },
        { do_bad,               SIGBUS,  0,             "unknown 46"                    },
        { do_bad,               SIGBUS,  0,             "unknown 47"                    },
        { do_bad,               SIGBUS,  0,             "unknown 48"                    },
        { do_bad,               SIGBUS,  0,             "unknown 49"                    },
        { do_bad,               SIGBUS,  0,             "unknown 50"                    },
        { do_bad,               SIGBUS,  0,             "unknown 51"                    },
        { do_bad,               SIGBUS,  0,             "implementation fault (lockdown abort)" },
        { do_bad,               SIGBUS,  0,             "unknown 53"                    },
        { do_bad,               SIGBUS,  0,             "unknown 54"                    },
        { do_bad,               SIGBUS,  0,             "unknown 55"                    },
        { do_bad,               SIGBUS,  0,             "unknown 56"                    },
        { do_bad,               SIGBUS,  0,             "unknown 57"                    },
        { do_bad,               SIGBUS,  0,             "implementation fault (coprocessor abort)" },
        { do_bad,               SIGBUS,  0,             "unknown 59"                    },
        { do_bad,               SIGBUS,  0,             "unknown 60"                    },
        { do_bad,               SIGBUS,  0,             "unknown 61"                    },
        { do_bad,               SIGBUS,  0,             "unknown 62"                    },
        { do_bad,               SIGBUS,  0,             "unknown 63"                    },
};



Interprocessor Interrupts

IPIs are used to maintain synchronisation between the processors. For example, if a kernel page table entry changes, both processors must either flush their TLBs or invalidate that particular page table entry. Whichever processor changed the mapping knows to do this automatically, but the other processor does not; therefore, the processor which changed the mapping must send an IPI to the other processor to tell it to flush its TLB or invalidate the page table entry.
Using the local APIC, you can send interrupts to all processors, all processors but the one sending the interrupt, or a specific processor or logical address as well as self-interrupts. To send an IPI, write the destination APIC ID, if needed, into the high word of the ICR, then write the low word of ICR with the destination shorthand and interrupt vector set to send the IPI. Be sure to wrap these functions in spinlocks. You might want to turn off interrupts as well while sending IPIs.

31  Private nIRQ - Each CPU only sees its own legacy nIRQ pin
30  Private watchdog - Each CPU has its own watchdog timer
29  Private timer - Each CPU has its own timer
28:16  Spare - available for future private peripherals
15:0    Private inter-processor interrupts - See below

Inter-processor interrupts (IPI) can, for example, be used for interrupt-driven communication
between CPUs in an SMP system. The GIC implements IPI using 16 software interrupts. To
generate an IPI, an MPCore CPU must write to the primary GIC's Software Interrupt register
specifying an interrupt ID in the range 0 to 15 and which CPU(s) will receive the interrupt.




Technique
Recommended for
Not recommended for
a) Uncached mappings
Infrequently or sparsely accessed DMA data blocks
Simplest implementation
Correctness/reliability
Intensively accessed DMA blocks will not benefit from caching, in which case performance can suffer.
b) Set affinity
Minimizing cache line migration overheads

In complex applications, a manual procedure will be required to map the IRQ part of device drivers and user processes to CPUs.  Thorough testing will be required.
In complex applications, the enforced mapping of processes to CPUs might mean that the maximum aggregate performance is not achievable due to unbalanced CPU loading.
c) Read for ownership
Small DMA data blocks.
Can be done by OS kernel
Large DMA data blocks – must read/write completely – will cause extra memory traffic and potentially thrash L1 cache.
d) Broadcast of cache maintenance operations
Can be done by OS kernel
Currently done by Linux 2.6.28-arm1
Small DMA blocks – overhead of synchronous IPI is likely to be significant.
Performance – scheduling of IPI depends on IRQ loading –  process blocks until all CPUs have responded.

No comments:

Post a Comment