lkml.org 
[lkml]   [2013]   [Sep]   [19]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
Date
From
SubjectRe: [patch 1/6] hardirq: Make hardirq bits generic
Geert,

On Wed, 18 Sep 2013, Thomas Gleixner wrote:
> The low level entry code is fiddling with preempt_count by adding
> HARDIRQ_OFFSET to it to keep track of nested interrupts. If the count
> goes to 0, it invokes do_softirq(). And you have another safety guard:
>
> ret_from_last_interrupt:
> moveq #(~ALLOWINT>>8)&0xff,%d0
> andb %sp@(PT_OFF_SR),%d0
> jne 2b
>
> That's due to the irq priority level stuff, which results in nested
> interrupts depending on the level of the serviced interrupt, right?
> And that's why you fiddle yourself with the HARDIRQ bits in the
> preempt count to prevent the core code from calling do_softirq().
>
> Though this scheme also prevents that other parts of irq_exit() are
> working correctly, because they depend on the hardirq count being
> zero, e.g. the nohz code.
>
> Needs more thoughts how to fix that w/o wasting precious bits for the
> HARDIRQ count.

So after staring a while into the m68k code I came up with the
following (untested and uncompiled) solution:

Instead of doing the HARDIRQ fiddling and the softirq handling from
the low level entry code, I provided an irq_exit_nested() variant
which has an argument to tell the core code, that it shouldn't invoke
softirq handling and the nohz exit code. That way 4 HARDIRQ bits in
preempt_count() should be sufficient and we can move them around as we
see fit without being bound by some magic asm code fiddling with it.

By modifying do_IRQ to return an indicator whether this is the last
irq in the chain, we can also simplify the asm code significantly.

Can you have a look with m68k eyes on that please? I'm sure that my
vague memory of the 68k ASM tricked me into doing something
fundamentally wrong :)

Thanks,

tglx
---
Subject: m68k: Deal with interrupt nesting in the core code
From: Thomas Gleixner <tglx@linutronix.de>
Date: Wed, 18 Sep 2013 11:56:58 +0200

m68k increments the HARDIRQ part of preempt_count in the low level
interrupt entry code, which prevents the core code from restructuring
the preempt_count layout.

This is done to prevent softirq invocation for
nested interrupts. The nesting can happen due to the interrupt
priority scheme of the 68k.

This patch removes the low level handling and moves it to the
interrupt core code by providing an irq_exit_nested() variant. The
nested argument to this function tells the core code whether to invoke
softirqs or not.

Also let do_IRQ() and the other handler variants return the nest
indicator so the low level entry code can utilize this to select
either immediate return or the expensive work functions.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
---
arch/m68k/include/asm/irq.h | 2 +-
arch/m68k/kernel/entry.S | 36 +++++++++---------------------------
arch/m68k/kernel/ints.c | 6 ------
arch/m68k/kernel/irq.c | 7 ++++---
arch/m68k/platform/68000/entry.S | 19 ++++++-------------
arch/m68k/platform/68000/ints.c | 7 +++----
arch/m68k/platform/68360/entry.S | 25 ++++++++-----------------
arch/m68k/q40/q40ints.c | 12 ++++++------
include/linux/hardirq.h | 1 +
kernel/softirq.c | 15 +++++++++++----
10 files changed, 49 insertions(+), 81 deletions(-)

Index: linux-2.6/arch/m68k/include/asm/irq.h
===================================================================
--- linux-2.6.orig/arch/m68k/include/asm/irq.h
+++ linux-2.6/arch/m68k/include/asm/irq.h
@@ -74,7 +74,7 @@ extern unsigned int irq_canonicalize(uns
#define irq_canonicalize(irq) (irq)
#endif /* !(CONFIG_M68020 || CONFIG_M68030 || CONFIG_M68040 || CONFIG_M68060) */

-asmlinkage void do_IRQ(int irq, struct pt_regs *regs);
+asmlinkage int do_IRQ(int irq, struct pt_regs *regs);
extern atomic_t irq_err_count;

#endif /* _M68K_IRQ_H_ */
Index: linux-2.6/arch/m68k/kernel/entry.S
===================================================================
--- linux-2.6.orig/arch/m68k/kernel/entry.S
+++ linux-2.6/arch/m68k/kernel/entry.S
@@ -274,9 +274,6 @@ do_delayed_trace:

ENTRY(auto_inthandler)
SAVE_ALL_INT
- GET_CURRENT(%d0)
- movel %d0,%a1
- addqb #1,%a1@(TINFO_PREEMPT+1)
| put exception # in d0
bfextu %sp@(PT_OFF_FORMATVEC){#4,#10},%d0
subw #VEC_SPUR,%d0
@@ -284,34 +281,22 @@ ENTRY(auto_inthandler)
movel %sp,%sp@-
movel %d0,%sp@- | put vector # on stack
auto_irqhandler_fixup = . + 2
- jsr do_IRQ | process the IRQ
+ jsr do_IRQ | process the IRQ, returns nest level
addql #8,%sp | pop parameters off stack

ret_from_interrupt:
- movel %curptr@(TASK_STACK),%a1
- subqb #1,%a1@(TINFO_PREEMPT+1)
- jeq ret_from_last_interrupt
-2: RESTORE_ALL
-
- ALIGN
-ret_from_last_interrupt:
- moveq #(~ALLOWINT>>8)&0xff,%d0
- andb %sp@(PT_OFF_SR),%d0
- jne 2b
-
- /* check if we need to do software interrupts */
- tstl irq_stat+CPUSTAT_SOFTIRQ_PENDING
+ /*
+ * Only the last interrupt leaving the kernel goes through the
+ * various exception return checks.
+ */
+ cmpl #0, %d0
jeq .Lret_from_exception
- pea ret_from_exception
- jra do_softirq
+ RESTORE_ALL

/* Handler for user defined interrupt vectors */

ENTRY(user_inthandler)
SAVE_ALL_INT
- GET_CURRENT(%d0)
- movel %d0,%a1
- addqb #1,%a1@(TINFO_PREEMPT+1)
| put exception # in d0
bfextu %sp@(PT_OFF_FORMATVEC){#4,#10},%d0
user_irqvec_fixup = . + 2
@@ -319,13 +304,10 @@ user_irqvec_fixup = . + 2

movel %sp,%sp@-
movel %d0,%sp@- | put vector # on stack
- jsr do_IRQ | process the IRQ
+ jsr do_IRQ | process the IRQ, returns nest level
addql #8,%sp | pop parameters off stack

- movel %curptr@(TASK_STACK),%a1
- subqb #1,%a1@(TINFO_PREEMPT+1)
- jeq ret_from_last_interrupt
- RESTORE_ALL
+ jra ret_from_interrupt

/* Handler for uninitialized and spurious interrupts */

Index: linux-2.6/arch/m68k/kernel/ints.c
===================================================================
--- linux-2.6.orig/arch/m68k/kernel/ints.c
+++ linux-2.6/arch/m68k/kernel/ints.c
@@ -58,12 +58,6 @@ void __init init_IRQ(void)
{
int i;

- /* assembly irq entry code relies on this... */
- if (HARDIRQ_MASK != 0x00ff0000) {
- extern void hardirq_mask_is_broken(void);
- hardirq_mask_is_broken();
- }
-
for (i = IRQ_AUTO_1; i <= IRQ_AUTO_7; i++)
irq_set_chip_and_handler(i, &auto_irq_chip, handle_simple_irq);

Index: linux-2.6/arch/m68k/kernel/irq.c
===================================================================
--- linux-2.6.orig/arch/m68k/kernel/irq.c
+++ linux-2.6/arch/m68k/kernel/irq.c
@@ -17,18 +17,19 @@
#include <linux/seq_file.h>
#include <asm/traps.h>

-asmlinkage void do_IRQ(int irq, struct pt_regs *regs)
+asmlinkage int do_IRQ(int irq, struct pt_regs *regs)
{
struct pt_regs *oldregs = set_irq_regs(regs);
+ int nested = regs->sr & ~ALLOWINT;

irq_enter();
generic_handle_irq(irq);
- irq_exit();
+ irq_exit_nested(nested);

set_irq_regs(oldregs);
+ return nested;
}

-
/* The number of spurious interrupts */
atomic_t irq_err_count;

Index: linux-2.6/arch/m68k/platform/68000/entry.S
===================================================================
--- linux-2.6.orig/arch/m68k/platform/68000/entry.S
+++ linux-2.6/arch/m68k/platform/68000/entry.S
@@ -217,20 +217,13 @@ inthandler:
bra ret_from_interrupt

ret_from_interrupt:
- jeq 1f
-2:
- RESTORE_ALL
-1:
- moveb %sp@(PT_OFF_SR), %d0
- and #7, %d0
- jhi 2b
-
- /* check if we need to do software interrupts */
+ /*
+ * Only the last interrupt leaving the kernel goes through the
+ * various exception return checks.
+ */
+ cmpl #0, %d0
jeq ret_from_exception
-
- pea ret_from_exception
- jra do_softirq
-
+ RESTORE_ALL

/*
* Handler for uninitialized and spurious interrupts.
Index: linux-2.6/arch/m68k/platform/68000/ints.c
===================================================================
--- linux-2.6.orig/arch/m68k/platform/68000/ints.c
+++ linux-2.6/arch/m68k/platform/68000/ints.c
@@ -74,11 +74,9 @@ asmlinkage irqreturn_t inthandler7(void)
* into one vector and look in the blasted mask register...
* This code is designed to be fast, almost constant time, not clean!
*/
-void process_int(int vec, struct pt_regs *fp)
+int process_int(int vec, struct pt_regs *fp)
{
- int irq;
- int mask;
-
+ int irq, mask, nested =fp->sr & ~ALLOWINT;
unsigned long pend = ISR;

while (pend) {
@@ -128,6 +126,7 @@ void process_int(int vec, struct pt_regs
do_IRQ(irq, fp);
pend &= ~mask;
}
+ return nested;
}

static void intc_irq_unmask(struct irq_data *d)
Index: linux-2.6/arch/m68k/platform/68360/entry.S
===================================================================
--- linux-2.6.orig/arch/m68k/platform/68360/entry.S
+++ linux-2.6/arch/m68k/platform/68360/entry.S
@@ -132,26 +132,17 @@ inthandler:

movel %sp,%sp@-
movel %d0,%sp@- /* put vector # on stack*/
- jbsr do_IRQ /* process the IRQ*/
-3: addql #8,%sp /* pop parameters off stack*/
- bra ret_from_interrupt
+ jbsr do_IRQ /* process the IRQ, returns nest level */
+ addql #8,%sp /* pop parameters off stack*/

ret_from_interrupt:
- jeq 1f
-2:
- RESTORE_ALL
-1:
- moveb %sp@(PT_OFF_SR), %d0
- and #7, %d0
- jhi 2b
- /* check if we need to do software interrupts */
-
- movel irq_stat+CPUSTAT_SOFTIRQ_PENDING,%d0
+ /*
+ * Only the last interrupt leaving the kernel goes through the
+ * various exception return checks.
+ */
+ cmpl #0, %d0
jeq ret_from_exception
-
- pea ret_from_exception
- jra do_softirq
-
+ RESTORE_ALL

/*
* Handler for uninitialized and spurious interrupts.
Index: linux-2.6/arch/m68k/q40/q40ints.c
===================================================================
--- linux-2.6.orig/arch/m68k/q40/q40ints.c
+++ linux-2.6/arch/m68k/q40/q40ints.c
@@ -202,10 +202,10 @@ static int aliased_irq=0; /* how many t


/* got interrupt, dispatch to ISA or keyboard/timer IRQs */
-static void q40_irq_handler(unsigned int irq, struct pt_regs *fp)
+static int q40_irq_handler(unsigned int irq, struct pt_regs *fp)
{
unsigned mir, mer;
- int i;
+ int i, nested = fp->sr & ~ALLOWINT;

//repeat:
mir = master_inb(IIRQ_REG);
@@ -213,14 +213,14 @@ static void q40_irq_handler(unsigned int
if ((mir & Q40_IRQ_EXT_MASK) &&
(master_inb(EIRQ_REG) & Q40_IRQ6_MASK)) {
floppy_hardint();
- return;
+ return nested;
}
#endif
switch (irq) {
case 4:
case 6:
do_IRQ(Q40_IRQ_SAMPLE, fp);
- return;
+ return nested;
}
if (mir & Q40_IRQ_FRAME_MASK) {
do_IRQ(Q40_IRQ_FRAME, fp);
@@ -277,7 +277,7 @@ static void q40_irq_handler(unsigned int
#endif
}
// used to do 'goto repeat;' here, this delayed bh processing too long
- return;
+ return nested;
}
}
if (mer && ccleirq > 0 && !aliased_irq) {
@@ -291,7 +291,7 @@ static void q40_irq_handler(unsigned int
if (mir & Q40_IRQ_KEYB_MASK)
do_IRQ(Q40_IRQ_KEYBOARD, fp);

- return;
+ return nested;
}

void q40_irq_enable(struct irq_data *data)
Index: linux-2.6/include/linux/hardirq.h
===================================================================
--- linux-2.6.orig/include/linux/hardirq.h
+++ linux-2.6/include/linux/hardirq.h
@@ -55,6 +55,7 @@ extern void irq_enter(void);
/*
* Exit irq context and process softirqs if needed:
*/
+extern void irq_exit_nested(bool nested);
extern void irq_exit(void);

#define nmi_enter() \
Index: linux-2.6/kernel/softirq.c
===================================================================
--- linux-2.6.orig/kernel/softirq.c
+++ linux-2.6/kernel/softirq.c
@@ -350,7 +350,7 @@ static inline void tick_irq_exit(void)
/*
* Exit an interrupt context. Process softirqs if needed and possible:
*/
-void irq_exit(void)
+void irq_exit_nested(bool nested)
{
#ifndef __ARCH_IRQ_EXIT_IRQS_DISABLED
local_irq_disable();
@@ -361,13 +361,20 @@ void irq_exit(void)
account_irq_exit_time(current);
trace_hardirq_exit();
sub_preempt_count(HARDIRQ_OFFSET);
- if (!in_interrupt() && local_softirq_pending())
- invoke_softirq();

- tick_irq_exit();
+ if (!nested) {
+ if (!in_interrupt() && local_softirq_pending())
+ invoke_softirq();
+ tick_irq_exit();
+ }
rcu_irq_exit();
}

+void irq_exit(void)
+{
+ irq_exit_nested(false);
+}
+
/*
* This function must run with irqs disabled!
*/

\
 
 \ /
  Last update: 2013-09-19 17:21    [W:0.099 / U:1.068 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site