Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 10d91611 authored by Nicholas Piggin's avatar Nicholas Piggin Committed by Michael Ellerman
Browse files

powerpc/64s: Reimplement book3s idle code in C



Reimplement Book3S idle code in C, moving POWER7/8/9 implementation
speific HV idle code to the powernv platform code.

Book3S assembly stubs are kept in common code and used only to save
the stack frame and non-volatile GPRs before executing architected
idle instructions, and restoring the stack and reloading GPRs then
returning to C after waking from idle.

The complex logic dealing with threads and subcores, locking, SPRs,
HMIs, timebase resync, etc., is all done in C which makes it more
maintainable.

This is not a strict translation to C code, there are some
significant differences:

- Idle wakeup no longer uses the ->cpu_restore call to reinit SPRs,
  but saves and restores them itself.

- The optimisation where EC=ESL=0 idle modes did not have to save GPRs
  or change MSR is restored, because it's now simple to do. ESL=1
  sleeps that do not lose GPRs can use this optimization too.

- KVM secondary entry and cede is now more of a call/return style
  rather than branchy. nap_state_lost is not required because KVM
  always returns via NVGPR restoring path.

- KVM secondary wakeup from offline sequence is moved entirely into
  the offline wakeup, which avoids a hwsync in the normal idle wakeup
  path.

Performance measured with context switch ping-pong on different
threads or cores, is possibly improved a small amount, 1-3% depending
on stop state and core vs thread test for shallow states. Deep states
it's in the noise compared with other latencies.

KVM improvements:

- Idle sleepers now always return to caller rather than branch out
  to KVM first.

- This allows optimisations like very fast return to caller when no
  state has been lost.

- KVM no longer requires nap_state_lost because it controls NVGPR
  save/restore itself on the way in and out.

- The heavy idle wakeup KVM request check can be moved out of the
  normal host idle code and into the not-performance-critical offline
  code.

- KVM nap code now returns from where it is called, which makes the
  flow a bit easier to follow.

Reviewed-by: default avatarGautham R. Shenoy <ego@linux.vnet.ibm.com>
Signed-off-by: default avatarNicholas Piggin <npiggin@gmail.com>
[mpe: Squash the KVM changes in]
Signed-off-by: default avatarMichael Ellerman <mpe@ellerman.id.au>
parent c1fe190c
Loading
Loading
Loading
Loading
+3 −16
Original line number Diff line number Diff line
@@ -27,10 +27,11 @@
 * the THREAD_WINKLE_BITS are set, which indicate which threads have not
 * yet woken from the winkle state.
 */
#define PNV_CORE_IDLE_LOCK_BIT			0x10000000
#define NR_PNV_CORE_IDLE_LOCK_BIT		28
#define PNV_CORE_IDLE_LOCK_BIT			(1ULL << NR_PNV_CORE_IDLE_LOCK_BIT)

#define PNV_CORE_IDLE_WINKLE_COUNT_SHIFT	16
#define PNV_CORE_IDLE_WINKLE_COUNT		0x00010000
#define PNV_CORE_IDLE_WINKLE_COUNT_ALL_BIT	0x00080000
#define PNV_CORE_IDLE_WINKLE_COUNT_BITS		0x000F0000
#define PNV_CORE_IDLE_THREAD_WINKLE_BITS_SHIFT	8
#define PNV_CORE_IDLE_THREAD_WINKLE_BITS	0x0000FF00
@@ -68,16 +69,6 @@
#define ERR_DEEP_STATE_ESL_MISMATCH	-2

#ifndef __ASSEMBLY__
/* Additional SPRs that need to be saved/restored during stop */
struct stop_sprs {
	u64 pid;
	u64 ldbar;
	u64 fscr;
	u64 hfscr;
	u64 mmcr1;
	u64 mmcr2;
	u64 mmcra;
};

#define PNV_IDLE_NAME_LEN    16
struct pnv_idle_states_t {
@@ -92,10 +83,6 @@ struct pnv_idle_states_t {

extern struct pnv_idle_states_t *pnv_idle_states;
extern int nr_pnv_idle_states;
extern u32 pnv_fastsleep_workaround_at_entry[];
extern u32 pnv_fastsleep_workaround_at_exit[];

extern u64 pnv_first_deep_stop_state;

unsigned long pnv_cpu_offline(unsigned int cpu);
int validate_psscr_val_mask(u64 *psscr_val, u64 *psscr_mask, u32 flags);
+22 −18
Original line number Diff line number Diff line
@@ -173,7 +173,6 @@ struct paca_struct {
	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 */
#ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE
	u8 pmcregs_in_use;		/* pseries puts this in lppaca */
#endif
@@ -183,23 +182,28 @@ struct paca_struct {
#endif

#ifdef CONFIG_PPC_POWERNV
	/* Per-core mask tracking idle threads and a lock bit-[L][TTTTTTTT] */
	u32 *core_idle_state_ptr;
	u8 thread_idle_state;		/* PNV_THREAD_RUNNING/NAP/SLEEP	*/
	/* Mask to indicate thread id in core */
	u8 thread_mask;
	/* PowerNV idle fields */
	/* PNV_CORE_IDLE_* bits, all siblings work on thread 0 paca */
	unsigned long idle_state;
	union {
		/* P7/P8 specific fields */
		struct {
			/* PNV_THREAD_RUNNING/NAP/SLEEP	*/
			u8 thread_idle_state;
			/* Mask to denote subcore sibling threads */
			u8 subcore_sibling_mask;
	/* Flag to request this thread not to stop */
	atomic_t dont_stop;
		};

		/* P9 specific fields */
		struct {
#ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE
			/* The PSSCR value that the kernel requested before going to stop */
			u64 requested_psscr;

	/*
	 * Save area for additional SPRs that need to be
	 * saved/restored during cpuidle stop.
	 */
	struct stop_sprs stop_sprs;
			/* Flag to request this thread not to stop */
			atomic_t dont_stop;
#endif
		};
	};
#endif

#ifdef CONFIG_PPC_BOOK3S_64
+6 −3
Original line number Diff line number Diff line
@@ -411,14 +411,17 @@ static inline unsigned long get_clean_sp(unsigned long sp, int is_32)
}
#endif

/* asm stubs */
extern unsigned long isa300_idle_stop_noloss(unsigned long psscr_val);
extern unsigned long isa300_idle_stop_mayloss(unsigned long psscr_val);
extern unsigned long isa206_idle_insn_mayloss(unsigned long type);

extern unsigned long cpuidle_disable;
enum idle_boot_override {IDLE_NO_OVERRIDE = 0, IDLE_POWERSAVE_OFF};

extern int powersave_nap;	/* set if nap mode can be used in idle loop */
extern unsigned long power7_idle_insn(unsigned long type); /* PNV_THREAD_NAP/etc*/

extern void power7_idle_type(unsigned long type);
extern unsigned long power9_idle_stop(unsigned long psscr_val);
extern unsigned long power9_offline_stop(unsigned long psscr_val);
extern void power9_idle_type(unsigned long stop_psscr_val,
			      unsigned long stop_psscr_mask);

+4 −4
Original line number Diff line number Diff line
@@ -168,6 +168,7 @@
#define PSSCR_ESL		0x00200000 /* Enable State Loss */
#define PSSCR_SD		0x00400000 /* Status Disable */
#define PSSCR_PLS	0xf000000000000000 /* Power-saving Level Status */
#define PSSCR_PLS_SHIFT	60
#define PSSCR_GUEST_VIS	0xf0000000000003ffUL /* Guest-visible PSSCR fields */
#define PSSCR_FAKE_SUSPEND	0x00000400 /* Fake-suspend bit (P9 DD2.2) */
#define PSSCR_FAKE_SUSPEND_LG	10	   /* Fake-suspend bit position */
@@ -758,10 +759,9 @@
#define	  SRR1_WAKERESET	0x00100000 /* System reset */
#define   SRR1_WAKEHDBELL	0x000c0000 /* Hypervisor doorbell on P8 */
#define	  SRR1_WAKESTATE	0x00030000 /* Powersave exit mask [46:47] */
#define	  SRR1_WS_DEEPEST	0x00030000 /* Some resources not maintained,
					  * may not be recoverable */
#define	  SRR1_WS_DEEPER	0x00020000 /* Some resources not maintained */
#define	  SRR1_WS_DEEP		0x00010000 /* All resources maintained */
#define	  SRR1_WS_HVLOSS	0x00030000 /* HV resources not maintained */
#define	  SRR1_WS_GPRLOSS	0x00020000 /* GPRs not maintained */
#define	  SRR1_WS_NOLOSS	0x00010000 /* All resources maintained */
#define   SRR1_PROGTM		0x00200000 /* TM Bad Thing */
#define   SRR1_PROGFPE		0x00100000 /* Floating Point Enabled */
#define   SRR1_PROGILL		0x00080000 /* Illegal instruction */
+0 −18
Original line number Diff line number Diff line
@@ -268,7 +268,6 @@ int main(void)
	OFFSET(ACCOUNT_USER_TIME, paca_struct, accounting.utime);
	OFFSET(ACCOUNT_SYSTEM_TIME, paca_struct, accounting.stime);
	OFFSET(PACA_TRAP_SAVE, paca_struct, trap_save);
	OFFSET(PACA_NAPSTATELOST, paca_struct, nap_state_lost);
	OFFSET(PACA_SPRG_VDSO, paca_struct, sprg_vdso);
#else /* CONFIG_PPC64 */
#ifdef CONFIG_VIRT_CPU_ACCOUNTING_NATIVE
@@ -766,23 +765,6 @@ int main(void)
	OFFSET(VCPU_TIMING_LAST_ENTER_TBL, kvm_vcpu, arch.timing_last_enter.tv32.tbl);
#endif

#ifdef CONFIG_PPC_POWERNV
	OFFSET(PACA_CORE_IDLE_STATE_PTR, paca_struct, core_idle_state_ptr);
	OFFSET(PACA_THREAD_IDLE_STATE, paca_struct, thread_idle_state);
	OFFSET(PACA_THREAD_MASK, paca_struct, thread_mask);
	OFFSET(PACA_SUBCORE_SIBLING_MASK, paca_struct, subcore_sibling_mask);
	OFFSET(PACA_REQ_PSSCR, paca_struct, requested_psscr);
	OFFSET(PACA_DONT_STOP, paca_struct, dont_stop);
#define STOP_SPR(x, f)	OFFSET(x, paca_struct, stop_sprs.f)
	STOP_SPR(STOP_PID, pid);
	STOP_SPR(STOP_LDBAR, ldbar);
	STOP_SPR(STOP_FSCR, fscr);
	STOP_SPR(STOP_HFSCR, hfscr);
	STOP_SPR(STOP_MMCR1, mmcr1);
	STOP_SPR(STOP_MMCR2, mmcr2);
	STOP_SPR(STOP_MMCRA, mmcra);
#endif

	DEFINE(PPC_DBELL_SERVER, PPC_DBELL_SERVER);
	DEFINE(PPC_DBELL_MSGTYPE, PPC_DBELL_MSGTYPE);

Loading