aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/kernel
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2006-10-05 09:55:46 -0400
committerDavid Howells <dhowells@warthog.cambridge.redhat.com>2006-10-05 10:10:12 -0400
commit7d12e780e003f93433d49ce78cfedf4b4c52adc5 (patch)
tree6748550400445c11a306b132009f3001e3525df8 /arch/powerpc/kernel
parentda482792a6d1a3fbaaa25fae867b343fb4db3246 (diff)
IRQ: Maintain regs pointer globally rather than passing to IRQ handlers
Maintain a per-CPU global "struct pt_regs *" variable which can be used instead of passing regs around manually through all ~1800 interrupt handlers in the Linux kernel. The regs pointer is used in few places, but it potentially costs both stack space and code to pass it around. On the FRV arch, removing the regs parameter from all the genirq function results in a 20% speed up of the IRQ exit path (ie: from leaving timer_interrupt() to leaving do_IRQ()). Where appropriate, an arch may override the generic storage facility and do something different with the variable. On FRV, for instance, the address is maintained in GR28 at all times inside the kernel as part of general exception handling. Having looked over the code, it appears that the parameter may be handed down through up to twenty or so layers of functions. Consider a USB character device attached to a USB hub, attached to a USB controller that posts its interrupts through a cascaded auxiliary interrupt controller. A character device driver may want to pass regs to the sysrq handler through the input layer which adds another few layers of parameter passing. I've build this code with allyesconfig for x86_64 and i386. I've runtested the main part of the code on FRV and i386, though I can't test most of the drivers. I've also done partial conversion for powerpc and MIPS - these at least compile with minimal configurations. This will affect all archs. Mostly the changes should be relatively easy. Take do_IRQ(), store the regs pointer at the beginning, saving the old one: struct pt_regs *old_regs = set_irq_regs(regs); And put the old one back at the end: set_irq_regs(old_regs); Don't pass regs through to generic_handle_irq() or __do_IRQ(). In timer_interrupt(), this sort of change will be necessary: - update_process_times(user_mode(regs)); - profile_tick(CPU_PROFILING, regs); + update_process_times(user_mode(get_irq_regs())); + profile_tick(CPU_PROFILING); I'd like to move update_process_times()'s use of get_irq_regs() into itself, except that i386, alone of the archs, uses something other than user_mode(). Some notes on the interrupt handling in the drivers: (*) input_dev() is now gone entirely. The regs pointer is no longer stored in the input_dev struct. (*) finish_unlinks() in drivers/usb/host/ohci-q.c needs checking. It does something different depending on whether it's been supplied with a regs pointer or not. (*) Various IRQ handler function pointers have been moved to type irq_handler_t. Signed-Off-By: David Howells <dhowells@redhat.com> (cherry picked from 1b16e7ac850969f38b375e511e3fa2f474a33867 commit)
Diffstat (limited to 'arch/powerpc/kernel')
-rw-r--r--arch/powerpc/kernel/irq.c6
-rw-r--r--arch/powerpc/kernel/misc_64.S6
-rw-r--r--arch/powerpc/kernel/smp.c6
-rw-r--r--arch/powerpc/kernel/time.c6
4 files changed, 15 insertions, 9 deletions
diff --git a/arch/powerpc/kernel/irq.c b/arch/powerpc/kernel/irq.c
index c3f58f2f9f52..5deaab3090b4 100644
--- a/arch/powerpc/kernel/irq.c
+++ b/arch/powerpc/kernel/irq.c
@@ -187,6 +187,7 @@ void fixup_irqs(cpumask_t map)
187 187
188void do_IRQ(struct pt_regs *regs) 188void do_IRQ(struct pt_regs *regs)
189{ 189{
190 struct pt_regs *old_regs = set_irq_regs(regs);
190 unsigned int irq; 191 unsigned int irq;
191#ifdef CONFIG_IRQSTACKS 192#ifdef CONFIG_IRQSTACKS
192 struct thread_info *curtp, *irqtp; 193 struct thread_info *curtp, *irqtp;
@@ -230,18 +231,19 @@ void do_IRQ(struct pt_regs *regs)
230 handler = &__do_IRQ; 231 handler = &__do_IRQ;
231 irqtp->task = curtp->task; 232 irqtp->task = curtp->task;
232 irqtp->flags = 0; 233 irqtp->flags = 0;
233 call_handle_irq(irq, desc, regs, irqtp, handler); 234 call_handle_irq(irq, desc, irqtp, handler);
234 irqtp->task = NULL; 235 irqtp->task = NULL;
235 if (irqtp->flags) 236 if (irqtp->flags)
236 set_bits(irqtp->flags, &curtp->flags); 237 set_bits(irqtp->flags, &curtp->flags);
237 } else 238 } else
238#endif 239#endif
239 generic_handle_irq(irq, regs); 240 generic_handle_irq(irq);
240 } else if (irq != NO_IRQ_IGNORE) 241 } else if (irq != NO_IRQ_IGNORE)
241 /* That's not SMP safe ... but who cares ? */ 242 /* That's not SMP safe ... but who cares ? */
242 ppc_spurious_interrupts++; 243 ppc_spurious_interrupts++;
243 244
244 irq_exit(); 245 irq_exit();
246 set_irq_regs(old_regs);
245 247
246#ifdef CONFIG_PPC_ISERIES 248#ifdef CONFIG_PPC_ISERIES
247 if (get_lppaca()->int_dword.fields.decr_int) { 249 if (get_lppaca()->int_dword.fields.decr_int) {
diff --git a/arch/powerpc/kernel/misc_64.S b/arch/powerpc/kernel/misc_64.S
index 41521b30c3cd..c70e20708a1f 100644
--- a/arch/powerpc/kernel/misc_64.S
+++ b/arch/powerpc/kernel/misc_64.S
@@ -52,12 +52,12 @@ _GLOBAL(call_do_softirq)
52 blr 52 blr
53 53
54_GLOBAL(call_handle_irq) 54_GLOBAL(call_handle_irq)
55 ld r8,0(r7) 55 ld r8,0(r6)
56 mflr r0 56 mflr r0
57 std r0,16(r1) 57 std r0,16(r1)
58 mtctr r8 58 mtctr r8
59 stdu r1,THREAD_SIZE-112(r6) 59 stdu r1,THREAD_SIZE-112(r5)
60 mr r1,r6 60 mr r1,r5
61 bctrl 61 bctrl
62 ld r1,0(r1) 62 ld r1,0(r1)
63 ld r0,16(r1) 63 ld r0,16(r1)
diff --git a/arch/powerpc/kernel/smp.c b/arch/powerpc/kernel/smp.c
index 6a9bc9ce54e0..35c6309bdb76 100644
--- a/arch/powerpc/kernel/smp.c
+++ b/arch/powerpc/kernel/smp.c
@@ -115,7 +115,7 @@ void __devinit smp_generic_kick_cpu(int nr)
115} 115}
116#endif 116#endif
117 117
118void smp_message_recv(int msg, struct pt_regs *regs) 118void smp_message_recv(int msg)
119{ 119{
120 switch(msg) { 120 switch(msg) {
121 case PPC_MSG_CALL_FUNCTION: 121 case PPC_MSG_CALL_FUNCTION:
@@ -127,11 +127,11 @@ void smp_message_recv(int msg, struct pt_regs *regs)
127 break; 127 break;
128 case PPC_MSG_DEBUGGER_BREAK: 128 case PPC_MSG_DEBUGGER_BREAK:
129 if (crash_ipi_function_ptr) { 129 if (crash_ipi_function_ptr) {
130 crash_ipi_function_ptr(regs); 130 crash_ipi_function_ptr(get_irq_regs());
131 break; 131 break;
132 } 132 }
133#ifdef CONFIG_DEBUGGER 133#ifdef CONFIG_DEBUGGER
134 debugger_ipi(regs); 134 debugger_ipi(get_irq_regs());
135 break; 135 break;
136#endif /* CONFIG_DEBUGGER */ 136#endif /* CONFIG_DEBUGGER */
137 /* FALLTHROUGH */ 137 /* FALLTHROUGH */
diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c
index 85b9244a098c..d210d0a5006b 100644
--- a/arch/powerpc/kernel/time.c
+++ b/arch/powerpc/kernel/time.c
@@ -51,6 +51,7 @@
51#include <linux/rtc.h> 51#include <linux/rtc.h>
52#include <linux/jiffies.h> 52#include <linux/jiffies.h>
53#include <linux/posix-timers.h> 53#include <linux/posix-timers.h>
54#include <linux/irq.h>
54 55
55#include <asm/io.h> 56#include <asm/io.h>
56#include <asm/processor.h> 57#include <asm/processor.h>
@@ -643,6 +644,7 @@ static void iSeries_tb_recal(void)
643 */ 644 */
644void timer_interrupt(struct pt_regs * regs) 645void timer_interrupt(struct pt_regs * regs)
645{ 646{
647 struct pt_regs *old_regs;
646 int next_dec; 648 int next_dec;
647 int cpu = smp_processor_id(); 649 int cpu = smp_processor_id();
648 unsigned long ticks; 650 unsigned long ticks;
@@ -653,9 +655,10 @@ void timer_interrupt(struct pt_regs * regs)
653 do_IRQ(regs); 655 do_IRQ(regs);
654#endif 656#endif
655 657
658 old_regs = set_irq_regs(regs);
656 irq_enter(); 659 irq_enter();
657 660
658 profile_tick(CPU_PROFILING, regs); 661 profile_tick(CPU_PROFILING);
659 calculate_steal_time(); 662 calculate_steal_time();
660 663
661#ifdef CONFIG_PPC_ISERIES 664#ifdef CONFIG_PPC_ISERIES
@@ -715,6 +718,7 @@ void timer_interrupt(struct pt_regs * regs)
715#endif 718#endif
716 719
717 irq_exit(); 720 irq_exit();
721 set_irq_regs(old_regs);
718} 722}
719 723
720void wakeup_decrementer(void) 724void wakeup_decrementer(void)