Altera_Forum
Honored Contributor
16 years agoNested interrupts again
Hello Linux masters,
I need to make a small patch inside uClinux kernel for NIOS2. I need to enable one specific IRQ to have absolute priority over all atomic processes running in Linux - over threads, over other ISRs, over instruction traps and so on - simply said - over any atomic section in uClinux means. I know, it is possible, but don't know exactly how to do it. Fot the first try I want to enable my specific interrupt to nest all the other possible ISRs. I have modified function process_int() as follows:asmlinkage void process_int(unsigned long vec, struct pt_regs *fp)
{
//check if special nested interrupt
if (is_nested_interrupt)
{
//process my special interrupt
}
else
{
//enable special nested interupt
is_nested_interrupt = 1;
__asm__ __volatile__(
"wrctl ienable,r0\n"
: /* No output */
: /* No input */
: /* No regs */);
local_irq_enable();
/* give the machine specific code a crack at it first */
irq_enter();
kstat_cpu(0).irqs++;
latency_cause(-99,~vec);
if (irq_list.handler) {
if ((irq_list.handler(vec, irq_list.dev_id))==IRQ_NONE)
;
} else
# ifdef DEBUG
{
printk(KERN_ERR "No interrupt handler for level %ld\n", vec);
//// asm("trap 5");
}
# else
# if 1
printk(KERN_ERR "Ignoring interrupt %ld: no handler\n", vec);
# else
panic("No interrupt handler for level %ld\n", vec);
# endif
# endif
irq_exit();
//disable special nested interrupt
local_irq_disable();
__asm__ __volatile__(
"ldw r8,0(%0)\n"
"wrctl ienable,r8\n"
: /* No output */
: "r" (&saved_ienable)
: "r8");
is_nested_interrupt = 0;
}
} There are two new global variables. "is_nested_interrupt" is used to distinguish if interrupt is nested or not. "saved_ienable" is used to hold ienable register changes made during interrupt exception handling to be sure, that at the end of non-nested interrupt there is correct value stored in ienable register prepared to catch any new interrupt source. These two variables are defined also in irq.c as follows: unsigned long saved_ienable = 0;
unsigned long is_nested_interrupt = 0; Last change needed to be done is in "clrimr" and "setimr" functions. "saved_ienable" variable is allways updated according to new required mask, but "ienable" itself is updated only in case, when we are not in interrupt, see the code: /*
* Use a zero to clean the bit.
*/
static inline void clrimr(int mask)
{
int flags;
local_irq_save(flags);
__asm__ __volatile__(
"ldw r8,0(%1)\n"
"and r8,r8,%0\n"
"stw r8,0(%1)\n"
"ldw r9,0(%2)\n"
"beq r9,r0,1f\n"
"andi r8,r8,0\n"
"1: wrctl ienable, r8\n"
: /* No output */
: "r" (mask), "r" (&saved_ienable), "r" (&is_nested_interrupt)
: "r8", "r9");
local_irq_restore(flags);
}
/*
* Use a one to set the bit.
*/
static inline void setimr(int mask)
{
int flags;
local_irq_save(flags);
__asm__ __volatile__(
"ldw r8,0(%1)\n"
"or r8,r8,%0\n"
"stw r8,0(%1)\n"
"ldw r9,0(%2)\n"
"beq r9,r0,1f\n"
"andi r8,r8,0\n"
"1: wrctl ienable, r8\n"
: /* No output */
: "r" (mask), "r" (&saved_ienable), "r" (&is_nested_interrupt)
: "r8", "r9");
local_irq_restore(flags);
} Finally the problem is, that even when ienable is set to ZERO during interrupt service routine, the Linux fails to continue somewhere inside the first interrupt routine. More preciselly I found that some first ISR is beiing called and of course "local_irq_enable()" is called to set-up PIE bit in status register, but note that at the same time ienable is set to zero. Then later during servicing this ISR some instruction trap execption is processed and some uknown time later, but very soon (because of no Linux booting console output) it somewhere hangs. I just can say, that it is almost impossible to debug where and what happened. And now the question is, if PIE=1 and ienable=0, how much is it different from the situation when PIE=0 and ienable is non-zero..??? In both cases interrupts are disabled, but in the first one the Linux during boot hangs possibly after returning from instruction trap servicing. Thank you very much for the explanation. jan