powerpc: add scaled time accounting
authorMichael Neuling <mikey@neuling.org>
Thu, 18 Oct 2007 10:06:37 +0000 (03:06 -0700)
committerLinus Torvalds <torvalds@woody.linux-foundation.org>
Thu, 18 Oct 2007 21:37:28 +0000 (14:37 -0700)
This adds POWERPC specific hooks for scaled time accounting.

POWER6 includes a SPURR register.  The SPURR is based off the PURR register
but is scaled based on CPU frequency and issue rates.  This gives a more
accurate account of the instructions used per task.  The PURR and timebase
will be constant relative to the wall clock, irrespective of the CPU
frequency.

This implementation reads the SPURR register in account_system_vtime which
is only call called on context witch and hard and soft irq entry and exit.
The percentage of user and system time is then estimated using the ratio of
these accounted by the PURR.  If the SPURR is not present, the PURR read.

An earlier implementation of this patch read the SPURR whenever the PURR
was read, which included the system call entry and exit path.
Unfortunately this showed a performance regression on lmbench runs, so was
re-implemented.

I've included the lmbench results here when run bare metal on POWER6.  1st
column is the unpatch results.  2nd column is the results using the below
patch and the 3rd is the % diff of these results from the base.  4th and
5th columns are the results and % differnce from the base using the older
patch (SPURR read in syscall entry/exit path).

                              Base        Scaled-Acct     SPURR-in-syscall
                             Result      Result  % diff    Result % diff
Simple syscall:              0.3086      0.3086  0.0000    0.3452 11.8600
Simple read:                 0.4591      0.4671  1.7425    0.5044 9.86713
Simple write:                0.4364      0.4366  0.0458    0.4731 8.40971
Simple stat:                 2.0055      2.0295  1.1967    2.0669 3.06158
Simple fstat:                0.5962      0.5876  -1.442    0.6368 6.80979
Simple open/close:           3.1283      3.1009  -0.875    3.2088 2.57328
Select on 10 fd's:           0.8554      0.8457  -1.133    0.8667 1.32101
Select on 100 fd's:          3.5292      3.6329  2.9383    3.6664 3.88756
Select on 250 fd's:          7.9097      8.1881  3.5197    8.2242 3.97613
Select on 500 fd's:          15.2659     15.836  3.7357    15.873 3.97814
Select on 10 tcp fd's:       0.9576      0.9416  -1.670    0.9752 1.83792
Select on 100 tcp fd's:      7.248       7.2254  -0.311    7.2685 0.28283
Select on 250 tcp fd's:      17.7742     17.707  -0.375    17.749 -0.1406
Select on 500 tcp fd's:      35.4258     35.25   -0.496    35.286 -0.3929
Signal handler installation: 0.6131      0.6075  -0.913    0.647  5.52927
Signal handler overhead:     2.0919      2.1078  0.7600    2.1831 4.35967
Protection fault:            0.7345      0.7478  1.8107    0.8031 9.33968
Pipe latency:                33.006      16.398  -50.31    33.475 1.42368
AF_UNIX sock stream latency: 14.5093     30.910  113.03    30.715 111.692
Process fork+exit:           219.8       222.8   1.3648    229.37 4.35623
Process fork+execve:         876.14      873.28  -0.32     868.66 -0.8533
Process fork+/bin/sh -c:     2830        2876.5  1.6431    2958   4.52296
File /var/tmp/XXX write bw:  1193497     1195536 0.1708    118657 -0.5799
Pagefaults on /var/tmp/XXX:  3.1272      3.2117  2.7020    3.2521 3.99398

Also, kernel compile times show no difference with this patch applied.

[pbadari@us.ibm.com: Avoid unnecessary PURR reading]
Signed-off-by: Michael Neuling <mikey@neuling.org>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Jay Lan <jlan@engr.sgi.com>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Badari Pulavarty <pbadari@us.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
arch/powerpc/kernel/asm-offsets.c
arch/powerpc/kernel/time.c
include/asm-powerpc/paca.h

index 0ae5d57b93681fdea060bce6c6a78dc8bcd61569..2c8e756d19a39846db6f9c986dcce7ebbd099c39 100644 (file)
@@ -141,6 +141,7 @@ int main(void)
        DEFINE(PACALPPACAPTR, offsetof(struct paca_struct, lppaca_ptr));
        DEFINE(PACAHWCPUID, offsetof(struct paca_struct, hw_cpu_id));
        DEFINE(PACA_STARTPURR, offsetof(struct paca_struct, startpurr));
+       DEFINE(PACA_STARTSPURR, offsetof(struct paca_struct, startspurr));
        DEFINE(PACA_USER_TIME, offsetof(struct paca_struct, user_time));
        DEFINE(PACA_SYSTEM_TIME, offsetof(struct paca_struct, system_time));
        DEFINE(PACA_SLBSHADOWPTR, offsetof(struct paca_struct, slb_shadow_ptr));
index 863a5d6d9b1890626f5eceac08bc45304e6c13ce..9eb3284deac4e5d4e9942b5e643ab59fede4af85 100644 (file)
@@ -211,24 +211,45 @@ static u64 read_purr(void)
        return mftb();
 }
 
+/*
+ * Read the SPURR on systems that have it, otherwise the purr
+ */
+static u64 read_spurr(u64 purr)
+{
+       if (cpu_has_feature(CPU_FTR_SPURR))
+               return mfspr(SPRN_SPURR);
+       return purr;
+}
+
 /*
  * Account time for a transition between system, hard irq
  * or soft irq state.
  */
 void account_system_vtime(struct task_struct *tsk)
 {
-       u64 now, delta;
+       u64 now, nowscaled, delta, deltascaled;
        unsigned long flags;
 
        local_irq_save(flags);
        now = read_purr();
        delta = now - get_paca()->startpurr;
        get_paca()->startpurr = now;
+       nowscaled = read_spurr(now);
+       deltascaled = nowscaled - get_paca()->startspurr;
+       get_paca()->startspurr = nowscaled;
        if (!in_interrupt()) {
+               /* deltascaled includes both user and system time.
+                * Hence scale it based on the purr ratio to estimate
+                * the system time */
+               deltascaled = deltascaled * get_paca()->system_time /
+                       (get_paca()->system_time + get_paca()->user_time);
                delta += get_paca()->system_time;
                get_paca()->system_time = 0;
        }
        account_system_time(tsk, 0, delta);
+       get_paca()->purrdelta = delta;
+       account_system_time_scaled(tsk, deltascaled);
+       get_paca()->spurrdelta = deltascaled;
        local_irq_restore(flags);
 }
 
@@ -240,11 +261,17 @@ void account_system_vtime(struct task_struct *tsk)
  */
 void account_process_vtime(struct task_struct *tsk)
 {
-       cputime_t utime;
+       cputime_t utime, utimescaled;
 
        utime = get_paca()->user_time;
        get_paca()->user_time = 0;
        account_user_time(tsk, utime);
+
+       /* Estimate the scaled utime by scaling the real utime based
+        * on the last spurr to purr ratio */
+       utimescaled = utime * get_paca()->spurrdelta / get_paca()->purrdelta;
+       get_paca()->spurrdelta = get_paca()->purrdelta = 0;
+       account_user_time_scaled(tsk, utimescaled);
 }
 
 static void account_process_time(struct pt_regs *regs)
@@ -266,6 +293,7 @@ struct cpu_purr_data {
        int     initialized;                    /* thread is running */
        u64     tb;                     /* last TB value read */
        u64     purr;                   /* last PURR value read */
+       u64     spurr;                  /* last SPURR value read */
 };
 
 /*
index fcd7b428ed0bbc01e13ee9b443b43076fbeed907..98c6bd5756b756a3147ff72a300162e4ba876adb 100644 (file)
@@ -114,6 +114,9 @@ struct paca_struct {
        u64 user_time;                  /* accumulated usermode TB ticks */
        u64 system_time;                /* accumulated system TB ticks */
        u64 startpurr;                  /* PURR/TB value snapshot */
+       u64 startspurr;                 /* SPURR value snapshot */
+       u64 purrdelta;                  /* FIXME: document */
+       u64 spurrdelta;                 /* FIXME: document */
 };
 
 extern struct paca_struct paca[];