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

Commit cf9efce0 authored by Paul Mackerras's avatar Paul Mackerras Committed by Benjamin Herrenschmidt
Browse files

powerpc: Account time using timebase rather than PURR



Currently, when CONFIG_VIRT_CPU_ACCOUNTING is enabled, we use the
PURR register for measuring the user and system time used by
processes, as well as other related times such as hardirq and
softirq times.  This turns out to be quite confusing for users
because it means that a program will often be measured as taking
less time when run on a multi-threaded processor (SMT2 or SMT4 mode)
than it does when run on a single-threaded processor (ST mode), even
though the program takes longer to finish.  The discrepancy is
accounted for as stolen time, which is also confusing, particularly
when there are no other partitions running.

This changes the accounting to use the timebase instead, meaning that
the reported user and system times are the actual number of real-time
seconds that the program was executing on the processor thread,
regardless of which SMT mode the processor is in.  Thus a program will
generally show greater user and system times when run on a
multi-threaded processor than on a single-threaded processor.

On pSeries systems on POWER5 or later processors, we measure the
stolen time (time when this partition wasn't running) using the
hypervisor dispatch trace log.  We check for new entries in the
log on every entry from user mode and on every transition from
kernel process context to soft or hard IRQ context (i.e. when
account_system_vtime() gets called).  So that we can correctly
distinguish time stolen from user time and time stolen from system
time, without having to check the log on every exit to user mode,
we store separate timestamps for exit to user mode and entry from
user mode.

On systems that have a SPURR (POWER6 and POWER7), we read the SPURR
in account_system_vtime() (as before), and then apportion the SPURR
ticks since the last time we read it between scaled user time and
scaled system time according to the relative proportions of user
time and system time over the same interval.  This avoids having to
read the SPURR on every kernel entry and exit.  On systems that have
PURR but not SPURR (i.e., POWER5), we do the same using the PURR
rather than the SPURR.

This disables the DTL user interface in /sys/debug/kernel/powerpc/dtl
for now since it conflicts with the use of the dispatch trace log
by the time accounting code.

Signed-off-by: default avatarPaul Mackerras <paulus@samba.org>
Signed-off-by: default avatarBenjamin Herrenschmidt <benh@kernel.crashing.org>
parent 93c22703
Loading
Loading
Loading
Loading
+2 −1
Original line number Original line Diff line number Diff line
@@ -137,7 +137,8 @@
	li	r10,0;							   \
	li	r10,0;							   \
	ld	r11,exception_marker@toc(r2);				   \
	ld	r11,exception_marker@toc(r2);				   \
	std	r10,RESULT(r1);		/* clear regs->result		*/ \
	std	r10,RESULT(r1);		/* clear regs->result		*/ \
	std	r11,STACK_FRAME_OVERHEAD-16(r1); /* mark the frame	*/
	std	r11,STACK_FRAME_OVERHEAD-16(r1); /* mark the frame	*/ \
	ACCOUNT_STOLEN_TIME


/*
/*
 * Exception vectors.
 * Exception vectors.
+19 −0
Original line number Original line Diff line number Diff line
@@ -172,6 +172,25 @@ struct slb_shadow {


extern struct slb_shadow slb_shadow[];
extern struct slb_shadow slb_shadow[];


/*
 * Layout of entries in the hypervisor's dispatch trace log buffer.
 */
struct dtl_entry {
	u8	dispatch_reason;
	u8	preempt_reason;
	u16	processor_id;
	u32	enqueue_to_dispatch_time;
	u32	ready_to_enqueue_time;
	u32	waiting_to_ready_time;
	u64	timebase;
	u64	fault_addr;
	u64	srr0;
	u64	srr1;
};

#define DISPATCH_LOG_BYTES	4096	/* bytes per cpu */
#define N_DISPATCH_LOG		(DISPATCH_LOG_BYTES / sizeof(struct dtl_entry))

#endif /* CONFIG_PPC_BOOK3S */
#endif /* CONFIG_PPC_BOOK3S */
#endif /* __KERNEL__ */
#endif /* __KERNEL__ */
#endif /* _ASM_POWERPC_LPPACA_H */
#endif /* _ASM_POWERPC_LPPACA_H */
+9 −1
Original line number Original line Diff line number Diff line
@@ -85,6 +85,8 @@ struct paca_struct {
	u8 kexec_state;		/* set when kexec down has irqs off */
	u8 kexec_state;		/* set when kexec down has irqs off */
#ifdef CONFIG_PPC_STD_MMU_64
#ifdef CONFIG_PPC_STD_MMU_64
	struct slb_shadow *slb_shadow_ptr;
	struct slb_shadow *slb_shadow_ptr;
	struct dtl_entry *dispatch_log;
	struct dtl_entry *dispatch_log_end;


	/*
	/*
	 * Now, starting in cacheline 2, the exception save areas
	 * Now, starting in cacheline 2, the exception save areas
@@ -134,8 +136,14 @@ struct paca_struct {
	/* Stuff for accurate time accounting */
	/* Stuff for accurate time accounting */
	u64 user_time;			/* accumulated usermode TB ticks */
	u64 user_time;			/* accumulated usermode TB ticks */
	u64 system_time;		/* accumulated system TB ticks */
	u64 system_time;		/* accumulated system TB ticks */
	u64 startpurr;			/* PURR/TB value snapshot */
	u64 user_time_scaled;		/* accumulated usermode SPURR ticks */
	u64 starttime;			/* TB value snapshot */
	u64 starttime_user;		/* TB value on exit to usermode */
	u64 startspurr;			/* SPURR value snapshot */
	u64 startspurr;			/* SPURR value snapshot */
	u64 utime_sspurr;		/* ->user_time when ->startspurr set */
	u64 stolen_time;		/* TB ticks taken by hypervisor */
	u64 dtl_ridx;			/* read index in dispatch log */
	struct dtl_entry *dtl_curr;	/* pointer corresponding to dtl_ridx */


#ifdef CONFIG_KVM_BOOK3S_HANDLER
#ifdef CONFIG_KVM_BOOK3S_HANDLER
	/* We use this to store guest state in */
	/* We use this to store guest state in */
+31 −19
Original line number Original line Diff line number Diff line
@@ -9,6 +9,7 @@
#include <asm/asm-compat.h>
#include <asm/asm-compat.h>
#include <asm/processor.h>
#include <asm/processor.h>
#include <asm/ppc-opcode.h>
#include <asm/ppc-opcode.h>
#include <asm/firmware.h>


#ifndef __ASSEMBLY__
#ifndef __ASSEMBLY__
#error __FILE__ should only be used in assembler files
#error __FILE__ should only be used in assembler files
@@ -26,17 +27,13 @@
#ifndef CONFIG_VIRT_CPU_ACCOUNTING
#ifndef CONFIG_VIRT_CPU_ACCOUNTING
#define ACCOUNT_CPU_USER_ENTRY(ra, rb)
#define ACCOUNT_CPU_USER_ENTRY(ra, rb)
#define ACCOUNT_CPU_USER_EXIT(ra, rb)
#define ACCOUNT_CPU_USER_EXIT(ra, rb)
#define ACCOUNT_STOLEN_TIME
#else
#else
#define ACCOUNT_CPU_USER_ENTRY(ra, rb)					\
#define ACCOUNT_CPU_USER_ENTRY(ra, rb)					\
	beq	2f;			/* if from kernel mode */	\
	beq	2f;			/* if from kernel mode */	\
BEGIN_FTR_SECTION;							\
	MFTB(ra);			/* get timebase */		\
	mfspr	ra,SPRN_PURR;		/* get processor util. reg */	\
	ld	rb,PACA_STARTTIME_USER(r13);				\
END_FTR_SECTION_IFSET(CPU_FTR_PURR);					\
	std	ra,PACA_STARTTIME(r13);					\
BEGIN_FTR_SECTION;							\
	MFTB(ra);			/* or get TB if no PURR */	\
END_FTR_SECTION_IFCLR(CPU_FTR_PURR);					\
	ld	rb,PACA_STARTPURR(r13);					\
	std	ra,PACA_STARTPURR(r13);					\
	subf	rb,rb,ra;		/* subtract start value */	\
	subf	rb,rb,ra;		/* subtract start value */	\
	ld	ra,PACA_USER_TIME(r13);					\
	ld	ra,PACA_USER_TIME(r13);					\
	add	ra,ra,rb;		/* add on to user time */	\
	add	ra,ra,rb;		/* add on to user time */	\
@@ -44,19 +41,34 @@ END_FTR_SECTION_IFCLR(CPU_FTR_PURR); \
2:
2:


#define ACCOUNT_CPU_USER_EXIT(ra, rb)					\
#define ACCOUNT_CPU_USER_EXIT(ra, rb)					\
BEGIN_FTR_SECTION;							\
	MFTB(ra);			/* get timebase */		\
	mfspr	ra,SPRN_PURR;		/* get processor util. reg */	\
	ld	rb,PACA_STARTTIME(r13);					\
END_FTR_SECTION_IFSET(CPU_FTR_PURR);					\
	std	ra,PACA_STARTTIME_USER(r13);				\
BEGIN_FTR_SECTION;							\
	MFTB(ra);			/* or get TB if no PURR */	\
END_FTR_SECTION_IFCLR(CPU_FTR_PURR);					\
	ld	rb,PACA_STARTPURR(r13);					\
	std	ra,PACA_STARTPURR(r13);					\
	subf	rb,rb,ra;		/* subtract start value */	\
	subf	rb,rb,ra;		/* subtract start value */	\
	ld	ra,PACA_SYSTEM_TIME(r13);				\
	ld	ra,PACA_SYSTEM_TIME(r13);				\
	add	ra,ra,rb;		/* add on to user time */	\
	add	ra,ra,rb;		/* add on to system time */	\
	std	ra,PACA_SYSTEM_TIME(r13);
	std	ra,PACA_SYSTEM_TIME(r13)
#endif

#ifdef CONFIG_PPC_SPLPAR
#define ACCOUNT_STOLEN_TIME						\
BEGIN_FW_FTR_SECTION;							\
	beq	33f;							\
	/* from user - see if there are any DTL entries to process */	\
	ld	r10,PACALPPACAPTR(r13);	/* get ptr to VPA */		\
	ld	r11,PACA_DTL_RIDX(r13);	/* get log read index */	\
	ld	r10,LPPACA_DTLIDX(r10);	/* get log write index */	\
	cmpd	cr1,r11,r10;						\
	beq+	cr1,33f;						\
	bl	.accumulate_stolen_time;				\
33:									\
END_FW_FTR_SECTION_IFSET(FW_FEATURE_SPLPAR)

#else  /* CONFIG_PPC_SPLPAR */
#define ACCOUNT_STOLEN_TIME

#endif /* CONFIG_PPC_SPLPAR */

#endif /* CONFIG_VIRT_CPU_ACCOUNTING */


/*
/*
 * Macros for storing registers into and loading registers from
 * Macros for storing registers into and loading registers from
+0 −5
Original line number Original line Diff line number Diff line
@@ -34,7 +34,6 @@ extern void to_tm(int tim, struct rtc_time * tm);
extern void GregorianDay(struct rtc_time *tm);
extern void GregorianDay(struct rtc_time *tm);


extern void generic_calibrate_decr(void);
extern void generic_calibrate_decr(void);
extern void snapshot_timebase(void);


extern void set_dec_cpu6(unsigned int val);
extern void set_dec_cpu6(unsigned int val);


@@ -212,12 +211,8 @@ struct cpu_usage {
DECLARE_PER_CPU(struct cpu_usage, cpu_usage_array);
DECLARE_PER_CPU(struct cpu_usage, cpu_usage_array);


#if defined(CONFIG_VIRT_CPU_ACCOUNTING)
#if defined(CONFIG_VIRT_CPU_ACCOUNTING)
extern void calculate_steal_time(void);
extern void snapshot_timebases(void);
#define account_process_vtime(tsk)		account_process_tick(tsk, 0)
#define account_process_vtime(tsk)		account_process_tick(tsk, 0)
#else
#else
#define calculate_steal_time()			do { } while (0)
#define snapshot_timebases()			do { } while (0)
#define account_process_vtime(tsk)		do { } while (0)
#define account_process_vtime(tsk)		do { } while (0)
#endif
#endif


Loading