From 7628402b070ca215943a3b78586764d5a07bdc5d Mon Sep 17 00:00:00 2001 From: Peter Wemm Date: Thu, 29 Nov 2007 06:34:30 +0000 Subject: [PATCH] Move the shared cp_time array (counts %sys, %user, %idle etc) to the per-cpu area. cp_time[] goes away and a new function creates a merged cp_time-like array for things like linprocfs, sysctl etc. The atomic ops for updating cp_time[] in statclock go away, and the scope of the thread lock is reduced. sysctl kern.cp_time returns a backwards compatible cp_time[] array. A new kern.cp_times sysctl returns the individual per-cpu stats. I have pending changes to make top and vmstat optionally show per-cpu stats. I'm very aware that there are something like 5 or 6 other versions "out there" for doing this - but none were handy when I needed them. I did merge my changes with John Baldwin's, and ended up replacing a few chunks of my stuff with his, and stealing some other code. Reviewed by: jhb Partly obtained from: jhb --- sys/compat/linprocfs/linprocfs.c | 21 +++++++-- sys/kern/kern_clock.c | 81 ++++++++++++++++++++++++++++---- sys/sys/pcpu.h | 2 + sys/sys/resource.h | 2 +- 4 files changed, 91 insertions(+), 15 deletions(-) diff --git a/sys/compat/linprocfs/linprocfs.c b/sys/compat/linprocfs/linprocfs.c index c23ec322a3bf..5ba83a3ff71f 100644 --- a/sys/compat/linprocfs/linprocfs.c +++ b/sys/compat/linprocfs/linprocfs.c @@ -374,19 +374,28 @@ linprocfs_domtab(PFS_FILL_ARGS) static int linprocfs_dostat(PFS_FILL_ARGS) { + struct pcpu *pcpu; + long cp_time[CPUSTATES]; + long *cp; int i; + read_cpu_time(cp_time); sbuf_printf(sb, "cpu %ld %ld %ld %ld\n", T2J(cp_time[CP_USER]), T2J(cp_time[CP_NICE]), T2J(cp_time[CP_SYS] /*+ cp_time[CP_INTR]*/), T2J(cp_time[CP_IDLE])); - for (i = 0; i < mp_ncpus; ++i) + for (i = 0; i <= mp_maxid; ++i) { + if (CPU_ABSENT(i)) + continue; + pcpu = pcpu_find(i); + cp = pcpu->pc_cp_time; sbuf_printf(sb, "cpu%d %ld %ld %ld %ld\n", i, - T2J(cp_time[CP_USER]) / mp_ncpus, - T2J(cp_time[CP_NICE]) / mp_ncpus, - T2J(cp_time[CP_SYS]) / mp_ncpus, - T2J(cp_time[CP_IDLE]) / mp_ncpus); + T2J(cp[CP_USER]), + T2J(cp[CP_NICE]), + T2J(cp[CP_SYS] /*+ cp[CP_INTR]*/), + T2J(cp[CP_IDLE])); + } sbuf_printf(sb, "disk 0 0 0 0\n" "page %u %u\n" @@ -410,9 +419,11 @@ linprocfs_dostat(PFS_FILL_ARGS) static int linprocfs_douptime(PFS_FILL_ARGS) { + long cp_time[CPUSTATES]; struct timeval tv; getmicrouptime(&tv); + read_cpu_time(cp_time); sbuf_printf(sb, "%lld.%02ld %ld.%02ld\n", (long long)tv.tv_sec, tv.tv_usec / 10000, T2S(cp_time[CP_IDLE]), T2J(cp_time[CP_IDLE]) % 100); diff --git a/sys/kern/kern_clock.c b/sys/kern/kern_clock.c index 506b5a2333d9..54b7b8f985b0 100644 --- a/sys/kern/kern_clock.c +++ b/sys/kern/kern_clock.c @@ -81,9 +81,6 @@ extern void hardclock_device_poll(void); static void initclocks(void *dummy); SYSINIT(clocks, SI_SUB_CLOCKS, SI_ORDER_FIRST, initclocks, NULL) -/* Some of these don't belong here, but it's easiest to concentrate them. */ -long cp_time[CPUSTATES]; - /* Spin-lock protecting profiling statistics. */ static struct mtx time_lock; @@ -91,10 +88,14 @@ static int sysctl_kern_cp_time(SYSCTL_HANDLER_ARGS) { int error; + long cp_time[CPUSTATES]; #ifdef SCTL_MASK32 int i; unsigned int cp_time32[CPUSTATES]; +#endif + read_cpu_time(cp_time); +#ifdef SCTL_MASK32 if (req->flags & SCTL_MASK32) { if (!req->oldptr) return SYSCTL_OUT(req, 0, sizeof(cp_time32)); @@ -114,6 +115,66 @@ sysctl_kern_cp_time(SYSCTL_HANDLER_ARGS) SYSCTL_PROC(_kern, OID_AUTO, cp_time, CTLTYPE_LONG|CTLFLAG_RD, 0,0, sysctl_kern_cp_time, "LU", "CPU time statistics"); +static long empty[CPUSTATES]; + +static int +sysctl_kern_cp_times(SYSCTL_HANDLER_ARGS) +{ + struct pcpu *pcpu; + int error; + int i, c; + long *cp_time; +#ifdef SCTL_MASK32 + unsigned int cp_time32[CPUSTATES]; +#endif + + if (!req->oldptr) { +#ifdef SCTL_MASK32 + if (req->flags & SCTL_MASK32) + return SYSCTL_OUT(req, 0, sizeof(cp_time32) * (mp_maxid + 1)); + else +#endif + return SYSCTL_OUT(req, 0, sizeof(long) * CPUSTATES * (mp_maxid + 1)); + } + for (error = 0, c = 0; error == 0 && c <= mp_maxid; c++) { + if (!CPU_ABSENT(c)) { + pcpu = pcpu_find(c); + cp_time = pcpu->pc_cp_time; + } else { + cp_time = empty; + } +#ifdef SCTL_MASK32 + if (req->flags & SCTL_MASK32) { + for (i = 0; i < CPUSTATES; i++) + cp_time32[i] = (unsigned int)cp_time[i]; + error = SYSCTL_OUT(req, cp_time32, sizeof(cp_time32)); + } else +#endif + error = SYSCTL_OUT(req, cp_time, sizeof(long) * CPUSTATES); + } + return error; +} + +SYSCTL_PROC(_kern, OID_AUTO, cp_times, CTLTYPE_LONG|CTLFLAG_RD, + 0,0, sysctl_kern_cp_times, "LU", "per-CPU time statistics"); + +void +read_cpu_time(long *cp_time) +{ + struct pcpu *pc; + int i, j; + + /* Sum up global cp_time[]. */ + bzero(cp_time, sizeof(long) * CPUSTATES); + for (i = 0; i <= mp_maxid; i++) { + if (CPU_ABSENT(i)) + continue; + pc = pcpu_find(i); + for (j = 0; j < CPUSTATES; j++) + cp_time[j] += pc->pc_cp_time[j]; + } +} + #ifdef SW_WATCHDOG #include @@ -405,11 +466,12 @@ statclock(int usermode) struct thread *td; struct proc *p; long rss; + long *cp_time; td = curthread; p = td->td_proc; - thread_lock_flags(td, MTX_QUIET); + cp_time = (long *)PCPU_PTR(cp_time); if (usermode) { /* * Charge the time as appropriate. @@ -420,9 +482,9 @@ statclock(int usermode) #endif td->td_uticks++; if (p->p_nice > NZERO) - atomic_add_long(&cp_time[CP_NICE], 1); + cp_time[CP_NICE]++; else - atomic_add_long(&cp_time[CP_USER], 1); + cp_time[CP_USER]++; } else { /* * Came from kernel mode, so we were: @@ -439,7 +501,7 @@ statclock(int usermode) if ((td->td_pflags & TDP_ITHREAD) || td->td_intr_nesting_level >= 2) { td->td_iticks++; - atomic_add_long(&cp_time[CP_INTR], 1); + cp_time[CP_INTR]++; } else { #ifdef KSE if (p->p_flag & P_SA) @@ -448,9 +510,9 @@ statclock(int usermode) td->td_pticks++; td->td_sticks++; if (!TD_IS_IDLETHREAD(td)) - atomic_add_long(&cp_time[CP_SYS], 1); + cp_time[CP_SYS]++; else - atomic_add_long(&cp_time[CP_IDLE], 1); + cp_time[CP_IDLE]++; } } @@ -466,6 +528,7 @@ statclock(int usermode) ru->ru_maxrss = rss; CTR4(KTR_SCHED, "statclock: %p(%s) prio %d stathz %d", td, td->td_name, td->td_priority, (stathz)?stathz:hz); + thread_lock_flags(td, MTX_QUIET); sched_clock(td); thread_unlock(td); } diff --git a/sys/sys/pcpu.h b/sys/sys/pcpu.h index 8757fe63968b..168bf722ccd8 100644 --- a/sys/sys/pcpu.h +++ b/sys/sys/pcpu.h @@ -43,6 +43,7 @@ #include #include +#include #include struct pcb; @@ -82,6 +83,7 @@ struct pcpu { #endif PCPU_MD_FIELDS; struct vmmeter pc_cnt; /* VM stats counters */ + long pc_cp_time[CPUSTATES]; struct device *pc_device; /* diff --git a/sys/sys/resource.h b/sys/sys/resource.h index c1b16f431f58..d01b0bd67f4c 100644 --- a/sys/sys/resource.h +++ b/sys/sys/resource.h @@ -154,7 +154,7 @@ struct loadavg { #ifdef _KERNEL extern struct loadavg averunnable; -extern long cp_time[CPUSTATES]; +void read_cpu_time(long *cp_time); /* Writes array of CPUSTATES */ #else