powerpc: Rework lazy-interrupt handling
PATCH v3 1/1 powerpc: Rework lazy-interrupt handling
The current implementation of lazy interrupts handling has some issues that this tries to address.
Except on iSeries, we don't do the various workarounds we need to do on re-enable when returning from an interrupt, which can do an implicit re-enable, and thus we may still lose or get delayed decrementer or doorbell interrupts.
The current scheme also makes it much harder to handle the external "edge" interrupts provided by some BookE processors when using the EPR facility (External Proxy) and the Freescale Hypervisor.
We also hard mask on decrementer interrupts which is sub-optimal.
This is an attempt at fixing it all in one go by reworking the way we do the lazy interrupt disabling.
The base idea is to replace the "hard_enabled" field with a "irq_happened" field in which we store a bit mask of what interrupt occurred while soft-disabled.
When re-enabling, either via arch_local_irq_restore() or when returning from an interrupt, we can now decide what to do by testing bits in that field. We then implement re-emitting of the lost interrupts via either a re-use of the existing exception frame (exception exit case) or via the creation of a new one from assembly code (arch_local_irq_enable), without the need to trigger a fake one using set_dec() or similar.
In addition, this adds a few refinements:
- We no longer hard disable decrementer interrupts that occur
while soft-disabled. We now simply bump the decrementer back to max (on BookS) or leave it stopped (on BookE) and continue with hard interrupts enabled, which means that we'll potentially get better sample quality from performance monitor interrupts.
- Timer, decrementer and doorbell interrupts now hard-enable
shortly after removing the source of the interrupt, which means they no longer run entirely hard disabled. Again, this will improve perf sample quality.
- On Book3E 64-bit, we now make the performance monitor interrupt
act as an NMI like Book3S (the necessary C code for that to work appear to already be present in the FSL perf code, notably calling nmi_enter instead of irq_enter).
There are additional refinements that we can do on top of this patch:
- We could remove the ps3 workaround from arch_local_irq_enable(),
I believe that it should no longer be necessary
- We could make "masked" decrementer interrupts act as NMIs when doing
timer-based perf sampling to improve the sample quality.
- There are additional simplifications of the exception entry/exit path
that I've spotted along the way, such as merging fast_exception_return with the normal code path.
This patch needs a LOT more testing & review than it had so far !!!
Not-signed-off-by-yet: Benjamin Herrenschmidt <[email protected]>
---
v2:
- Add hard-enable to decrementer, timer and doorbells
- Fix CR clobber in masked irq handling on BookE
- Make embedded perf interrupt act as an NMI
- Add a PACA_HAPPENED_EE_EDGE for use by FSL if they want to retrigger an interrupt without preventing hard-enable
v3:
- Fix or vs. ori bug on Book3E
- Fix enabling of interrupts for some exceptions on Book3E
--- arch/powerpc/include/asm/exception-64s.h | 21 ++- arch/powerpc/include/asm/hw_irq.h | 51 +++++- arch/powerpc/include/asm/irqflags.h | 13 +- arch/powerpc/include/asm/paca.h | 2 +- arch/powerpc/kernel/asm-offsets.c | 2 +- arch/powerpc/kernel/dbell.c | 12 ++ arch/powerpc/kernel/entry_64.S | 96 ++++++----- arch/powerpc/kernel/exceptions-64e.S | 209 ++++++++++++++++------- arch/powerpc/kernel/exceptions-64s.S | 90 ++++++---- arch/powerpc/kernel/head_64.S | 9 - arch/powerpc/kernel/idle_book3e.S | 8 +- arch/powerpc/kernel/idle_power4.S | 17 ++- arch/powerpc/kernel/idle_power7.S | 20 ++- arch/powerpc/kernel/irq.c | 187 ++++++++++++++------ arch/powerpc/kernel/time.c | 15 ++- arch/powerpc/platforms/iseries/Makefile | 2 +- arch/powerpc/platforms/iseries/exception.S | 11 +- arch/powerpc/platforms/iseries/misc.S | 26 --- arch/powerpc/platforms/pseries/processor_idle.c | 24 +++- 19 files changed, 539 insertions(+), 276 deletions(-) delete mode 100644 arch/powerpc/platforms/iseries/misc.S
arch/powerpc/include/asm/exception-64s.h +12 -9
@@ -232,23 +232,24 @@ label##_hv: \ EXCEPTION_PROLOG_PSERIES(PACA_EXGEN, label##_common, \ EXC_HV, KVMTEST, vec) -#define __SOFTEN_TEST (h) \ +#define __SOFTEN_TEST (h, vec) \ lbz r10,PACASOFTIRQEN(r13); \ cmpwi r10,0; \ + li r10,(vec)>>8; \ beq masked_##h##interrupt -#define _SOFTEN_TEST (h) __SOFTEN_TEST(h) +#define _SOFTEN_TEST (h, vec) __SOFTEN_TEST(h, vec)#define SOFTEN_TEST_PR (vec) \ KVMTEST_PR(vec); \ - _SOFTEN_TEST(EXC_STD) + _SOFTEN_TEST(EXC_STD, vec)#define SOFTEN_TEST_HV (vec) \ KVMTEST(vec); \ - _SOFTEN_TEST(EXC_HV) + _SOFTEN_TEST(EXC_HV, vec)#define SOFTEN_TEST_HV_201 (vec) \ KVMTEST(vec); \ - _SOFTEN_TEST(EXC_STD) + _SOFTEN_TEST(EXC_STD, vec)#define __MASKABLE_EXCEPTION_PSERIES (vec, label, h, extra) \ HMT_MEDIUM; \
@@ -276,9 +277,9 @@ label##_hv: \#define DISABLE_INTS \ li r11,0; \ stb r11,PACASOFTIRQEN(r13); \ -BEGIN_FW_FTR_SECTION; \ - stb r11,PACAHARDIRQEN(r13); \ -END_FW_FTR_SECTION_IFCLR(FW_FEATURE_ISERIES); \ + lbz r11,PACAIRQHAPPENED(r13); \ + ori r11,r11,PACA_HAPPENED; \ + stb r11,PACAIRQHAPPENED(r13); \ TRACE_DISABLE_INTS; \ BEGIN_FW_FTR_SECTION; \ mfmsr r10; \
@@ -289,7 +290,9 @@ END_FW_FTR_SECTION_IFSET(FW_FEATURE_ISERIES)#define DISABLE_INTS \ li r11,0; \ stb r11,PACASOFTIRQEN(r13); \ - stb r11,PACAHARDIRQEN(r13); \ + lbz r11,PACAIRQHAPPENED(r13); \ + ori r11,r11,PACA_HAPPENED; \ + stb r11,PACAIRQHAPPENED(r13); \ TRACE_DISABLE_INTS#endif /* CONFIG_PPC_ISERIES */
arch/powerpc/include/asm/hw_irq.h +45 -6
79 lines not on this page · raw message
arch/powerpc/include/asm/irqflags.h +6 -7
@@ -47,16 +47,15 @@b skip; \ 95: TRACE_WITH_FRAME_BUFFER(.trace_hardirqs_on) \ li en,1; -#define TRACE_AND_RESTORE_IRQ (en) \ - TRACE_AND_RESTORE_IRQ_PARTIAL(en,96f); \ - stb en,PACASOFTIRQEN(r13); \ -96:#else #define TRACE_ENABLE_INTS #define TRACE_DISABLE_INTS -#define TRACE_AND_RESTORE_IRQ_PARTIAL (en,skip) -#define TRACE_AND_RESTORE_IRQ (en) \ - stb en,PACASOFTIRQEN(r13) +#define TRACE_AND_RESTORE_IRQ_PARTIAL (en,skip) \ + cmpdi en,0; \ + bne 95f; \ + stb en,PACASOFTIRQEN(r13); \ + b skip; \ +95:#endif #endif
arch/powerpc/include/asm/paca.h +1 -1
@@ -132,7 +132,7 @@ struct paca_struct {u64 saved_msr;/* MSR saved here by enter_rtas */ u16 trap_save;/* Used when bad stack is encountered */ u8 soft_enabled;/* irq soft-enable flag */ -u8 hard_enabled;/* set if irqs are enabled in MSR */ +u8 irq_happened;/* irq happened while soft-disabled */ u8 io_sync;/* writel() needs spin_unlock sync */ u8 irq_work_pending;/* IRQ_WORK interrupt while soft-disable */ u8 nap_state_lost;/* NV GPR values lost in power7_idle */
arch/powerpc/kernel/asm-offsets.c +1 -1
@@ -147,7 +147,7 @@ int main(void)DEFINE (PACAKBASE , offsetof(struct paca_struct , kernelbase));DEFINE (PACAKMSR , offsetof(struct paca_struct , kernel_msr));DEFINE (PACASOFTIRQEN , offsetof(struct paca_struct , soft_enabled)); -DEFINE (PACAHARDIRQEN , offsetof(struct paca_struct , hard_enabled)); +DEFINE (PACAIRQHAPPENED , offsetof(struct paca_struct , irq_happened)); DEFINE(PACACONTEXTID, offsetof(struct paca_struct, context.id));#ifdef CONFIG_PPC_MM_SLICES DEFINE(PACALOWSLICESPSIZE, offsetof(struct paca_struct,
arch/powerpc/kernel/dbell.c +12 -0
@@ -37,6 +37,18 @@ void doorbell_exception(struct pt_regs *regs)irq_enter (); +#ifdef CONFIG_PPC64 +/* Let's hard enable interrupts now that we have reset +* the DEC (or acked it on BookE) +* +* We skip that if there's a pending EE "level" interrupt +* as an optimization +*/ +get_paca ()->irq_happened &= ~PACA_HAPPENED ; +if (!(get_paca ()->irq_happened &PACA_HAPPENED_EE )) +__hard_irq_enable (); +#endif /* CONFIG_PPC64 */ +smp_ipi_demux ();irq_exit ();
arch/powerpc/kernel/entry_64.S +50 -46
149 lines not on this page · raw message
arch/powerpc/kernel/exceptions-64e.S +146 -63
349 lines not on this page · raw message
arch/powerpc/kernel/exceptions-64s.S +55 -35
126 lines not on this page · raw message
arch/powerpc/kernel/head_64.S +0 -9
@@ -564,7 +564,6 @@ _GLOBAL(pmac_secondary_start) */ li r0,0 stb r0,PACASOFTIRQEN(r13) - stb r0,PACAHARDIRQEN(r13) /* Create a temp kernel stack for use before relocation is on. */ ld r1,PACAEMERGSP(r13)
@@ -621,13 +620,8 @@ __secondary_start: #ifdef CONFIG_PPC_ISERIES BEGIN_FW_FTR_SECTION ori r4,r4,MSR_EE - li r8,1 - stb r8,PACAHARDIRQEN(r13) END_FW_FTR_SECTION_IFSET(FW_FEATURE_ISERIES) #endif -BEGIN_FW_FTR_SECTION - stb r7,PACAHARDIRQEN(r13) -END_FW_FTR_SECTION_IFCLR(FW_FEATURE_ISERIES) stb r7,PACASOFTIRQEN(r13) mtspr SPRN_SRR0,r3
@@ -782,11 +776,8 @@ BEGIN_FW_FTR_SECTION mfmsr r5 ori r5,r5,MSR_EE /* Hard Enabled on iSeries*/ mtmsrd r5 - li r5,1 END_FW_FTR_SECTION_IFSET(FW_FEATURE_ISERIES) #endif - stb r5,PACAHARDIRQEN(r13) /* Hard Disabled on others */ - bl .start_kernel /* Not reached */
arch/powerpc/kernel/idle_book3e.S +3 -5
@@ -32,11 +32,11 @@ _GLOBAL(book3e_idle) * since we may otherwise lose it (doorbells etc...). We know * that since PACAHARDIRQEN will have been cleared in that case. */ - lbz r3,PACAHARDIRQEN(r13) + lbz r3,PACAIRQHAPPENED(r13) cmpwi cr0,r3,0 - beqlr + bnelr - /* Now we are going to mark ourselves as soft and hard enables in + /* Now we are going to mark ourselves as soft and hard enabled in * order to be able to take interrupts while asleep. We inform lockdep * of that. We don't actually turn interrupts on just yet tho. */
@@ -46,7 +46,6 @@ _GLOBAL(book3e_idle) #endif li r0,1 stb r0,PACASOFTIRQEN(r13) - stb r0,PACAHARDIRQEN(r13) /* Interrupts will make use return to LR, so get something we want * in there
@@ -59,7 +58,6 @@ _GLOBAL(book3e_idle) /* Mark them off again in the PACA as well */ li r0,0 stb r0,PACASOFTIRQEN(r13) - stb r0,PACAHARDIRQEN(r13) /* Tell lockdep about it */ #ifdef CONFIG_TRACE_IRQFLAGS
arch/powerpc/kernel/idle_power4.S +15 -2
@@ -29,14 +29,27 @@ END_FTR_SECTION_IFCLR(CPU_FTR_CAN_NAP) cmpwi 0,r4,0 beqlr - /* Go to NAP now */ + /* Hard disable */ mfmsr r7 rldicl r0,r7,48,1 rotldi r0,r0,16 mtmsrd r0,1 /* hard-disable interrupts */ + + /* Check if something happened while soft-disabled */ + lbz r0,PACAIRQHAPPENED(r13) + cmpwi cr0,r0,0 + bnelr + + /* + * Here we mark ourselves soft-enabled. We should probably + * tell lockdep about it, but the interrupt will re-disable + * immediately so it shouldn't be a big issue. If it becomes + * one, then we should implement things the way we do on + * book3e. + */ li r0,1 stb r0,PACASOFTIRQEN(r13) /* we'll hard-enable shortly */ - stb r0,PACAHARDIRQEN(r13) + BEGIN_FTR_SECTION DSSALL sync
arch/powerpc/kernel/idle_power7.S +17 -3
@@ -1,5 +1,5 @@ /* - * This file contains the power_save function for 970-family CPUs. + * This file contains the power_save function for Power7 CPUs * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License
@@ -51,9 +51,23 @@ _GLOBAL(power7_idle) rldicl r9,r9,48,1 rotldi r9,r9,16 mtmsrd r9,1 /* hard-disable interrupts */ - li r0,0 + + /* Check if something happened while soft-disabled */ + lbz r0,PACAIRQHAPPENED(r13) + cmpwi cr0,r0,0 + beq 1f + addi r1,r1,INT_FRAME_SIZE + ld r0,16(r1) + mtlr r0 + blr + + /* + * Here we mark ourselves soft-diasbled (we should already be + * actually...). The interrupt is only going to happen after + * we return to the caller and it does a local_irq_enable() + */ +1: li r0,0 stb r0,PACASOFTIRQEN(r13) /* we'll hard-enable shortly */ - stb r0,PACAHARDIRQEN(r13) stb r0,PACA_NAPSTATELOST(r13) /* Continue saving state */
arch/powerpc/kernel/irq.c +133 -54
242 lines not on this page · raw message
arch/powerpc/kernel/time.c +12 -3
@@ -259,7 +259,6 @@ void accumulate_stolen_time(void)u64 sst, ust;u8 save_soft_enabled = local_paca->soft_enabled; -u8 save_hard_enabled = local_paca->hard_enabled;/* We are called early in the exception entry, before * soft/hard_enabled are sync'ed to the expected state
@@ -268,7 +267,6 @@ void accumulate_stolen_time(void)* complain */ local_paca->soft_enabled =0 ; - local_paca->hard_enabled =0 ; sst =scan_dispatch_log (local_paca->starttime_user); ust =scan_dispatch_log (local_paca->starttime);
@@ -277,7 +275,6 @@ void accumulate_stolen_time(void) local_paca->stolen_time += ust + sst; local_paca->soft_enabled = save_soft_enabled; - local_paca->hard_enabled = save_hard_enabled; }static inline u64 calculate_stolen_time (u64 stop_tb)
@@ -589,6 +586,18 @@ void timer_interrupt(struct pt_regs * regs)do_IRQ (regs);#endif +#ifdef CONFIG_PPC64 +/* Let's hard enable interrupts now that we have reset +* the DEC (or acked it on BookE) +* +* We skip that if there's a pending EE "level" interrupt +* as an optimization +*/ +get_paca ()->irq_happened &= ~PACA_HAPPENED ; +if (!(get_paca ()->irq_happened &PACA_HAPPENED_EE )) +__hard_irq_enable (); +#endif /* CONFIG_PPC64 */ + old_regs =set_irq_regs (regs);irq_enter ();
arch/powerpc/platforms/iseries/Makefile +1 -1
@@ -2,7 +2,7 @@ ccflags-y := -mno-minimal-toc obj-y += exception.o obj-y += hvlog.o hvlpconfig.o lpardata.o setup.o dt.o mf.o lpevents.o \ - hvcall.o proc.o htab.o iommu.o misc.o irq.o + hvcall.o proc.o htab.o iommu.o irq.o obj-$(CONFIG_PCI) += pci.o obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_VIOPATH) += viopath.o vio.o
arch/powerpc/platforms/iseries/exception.S +8 -3
@@ -32,6 +32,7 @@ #include <asm/ptrace.h> #include <asm/cputable.h> #include <asm/mmu.h> +#include <asm/hw_irq.h> #include "exception.h"
@@ -261,16 +262,20 @@ system_call_iSeries: decrementer_iSeries_masked: /* We may not have a valid TOC pointer in here. */ - li r11,1 + li r11,PACA_HAPPENED_DEC ld r12,PACALPPACAPTR(r13) stb r11,LPPACADECRINT(r12) li r12,-1 clrldi r12,r12,33 /* set DEC to 0x7fffffff */ mtspr SPRN_DEC,r12 - /* fall through */ + b 1f hardware_interrupt_iSeries_masked: - mtcrf 0x80,r9 /* Restore regs */ + li r11,PACA_HAPPENED_EE +1: mtcrf 0x80,r9 /* Restore regs */ + lbz r10,PACAIRQHAPPENED(r13) + or r11,r10,r11 + stb r11,PACAIRQHAPPENED(r13) ld r12,PACALPPACAPTR(r13) ld r11,LPPACASRR0(r12) ld r12,LPPACASRR1(r12)
arch/powerpc/platforms/iseries/misc.S deleted +0 -26
@@ -1,26 +0,0 @@ -/* - * This file contains miscellaneous low-level functions. - * Copyright (C) 1995-2005 IBM Corp - * - * Largely rewritten by Cort Dougan ([email protected]) - * and Paul Mackerras. - * Adapted for iSeries by Mike Corrigan ([email protected]) - * PPC64 updates by Dave Engebretsen ([email protected]) - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - */ - -#include <asm/processor.h> -#include <asm/asm-offsets.h> -#include <asm/ppc_asm.h> - - .text - -/* Handle pending interrupts in interrupt context */ -_GLOBAL(iseries_handle_interrupts) - li r0,0x5555 - sc - blr
arch/powerpc/platforms/pseries/processor_idle.c +22 -2
@@ -96,6 +96,25 @@ out:return index; } +static int check_and_cede_processor (void ) +{ +/* +* Interrupts are soft-disabled at this point, +* but not hard disabled. So an interrupt might have +* occurred before entering NAP, and would be potentially +* lost (edge events, decrementer events, etc...) unless +* we first hard disable then check. +* +* We must use the low level __hard_irq_disable() and not +* hard_irq_disable() as the later will set a bit in +* paca->irq_happened (to force re-enable later) which we +* don't need nor want here. +*/ +__hard_irq_disable (); +if (get_paca ()->irq_happened ==0 ) +cede_processor (); +} +static int dedicated_cede_loop (struct cpuidle_device *dev,struct cpuidle_driver *drv,int index)
@@ -108,7 +127,8 @@ static int dedicated_cede_loop(struct cpuidle_device *dev,ppc64_runlatch_off ();HMT_medium (); -cede_processor (); + +check_and_cede_processor ();get_lppaca ()->donate_dedicated_cpu =0 ; dev->last_residency =
@@ -132,7 +152,7 @@ static int shared_cede_loop(struct cpuidle_device *dev,* processor. When returning here, external interrupts * are enabled. */ -cede_processor (); +check_and_cede_processor (); dev->last_residency = (int )idle_loop_epilog (in_purr, kt_before);
Quoted 7 lines · Benjamin Herrenschmidt
The current implementation of lazy interrupts handling has some issues that this tries to address.
Except on iSeries, we don't do the various workarounds we need to do on re-enable when returning from an interrupt, which can do an implicit re-enable, and thus we may still lose or get delayed decrementer or doorbell interrupts.
.../...
So we were still losing doorbells on BookE due to a st00pid thinko on my part :-) New patch following, hopefully that one's good !
Cheers,
Ben.