From fce9fd4b4e1a2346828f36875b7a4c5012d7864f Mon Sep 17 00:00:00 2001 From: David van Moolenbroek Date: Wed, 2 Dec 2009 11:52:26 +0000 Subject: [PATCH] Add 'getidle' CPU utilization measurement infrastructure --- include/minix/com.h | 1 + include/minix/sysinfo.h | 1 + include/minix/syslib.h | 5 +- include/minix/sysutil.h | 5 ++ kernel/arch/i386/exception.c | 1 - kernel/arch/i386/memory.c | 1 - kernel/arch/i386/proto.h | 1 - kernel/arch/i386/system.c | 1 - kernel/clock.c | 4 ++ kernel/const.h | 6 +++ kernel/glo.h | 8 +++- kernel/interrupt.c | 2 + kernel/kernel.h | 3 ++ kernel/main.c | 5 ++ kernel/proc.c | 33 ++++++++++++- kernel/proto.h | 2 + kernel/system/do_getinfo.c | 16 +++++-- kernel/utility.c | 1 - lib/sysutil/Makefile.in | 1 + lib/sysutil/getidle.c | 92 ++++++++++++++++++++++++++++++++++++ lib/sysutil/profile.c | 22 ++++----- servers/pm/misc.c | 10 +++- 22 files changed, 190 insertions(+), 31 deletions(-) create mode 100644 lib/sysutil/getidle.c diff --git a/include/minix/com.h b/include/minix/com.h index 769184194..0b95b687a 100644 --- a/include/minix/com.h +++ b/include/minix/com.h @@ -482,6 +482,7 @@ # define GET_HZ 18 /* get HZ value */ # define GET_WHOAMI 19 /* get own name and endpoint */ # define GET_RANDOMNESS_BIN 20 /* get one randomness bin */ +# define GET_IDLETSC 21 /* get cumulative idle time stamp counter */ #define I_ENDPT m7_i4 /* calling process */ #define I_VAL_PTR m7_p1 /* virtual address at caller */ #define I_VAL_LEN m7_i1 /* max length of value */ diff --git a/include/minix/sysinfo.h b/include/minix/sysinfo.h index 072c4eb4e..c0d7ed3a0 100644 --- a/include/minix/sysinfo.h +++ b/include/minix/sysinfo.h @@ -11,6 +11,7 @@ _PROTOTYPE( ssize_t getsysinfo_up, (endpoint_t who, int what, size_t size, #define SIU_LOADINFO 1 /* retrieve load info data */ #define SIU_SYSTEMHZ 2 /* retrieve system clock frequency */ +#define SIU_IDLETSC 3 /* retrieve cumulative idle timestamp count */ /* Exported system parameters. */ diff --git a/include/minix/syslib.h b/include/minix/syslib.h index 93aeaf3c0..02c03bed5 100644 --- a/include/minix/syslib.h +++ b/include/minix/syslib.h @@ -179,6 +179,7 @@ _PROTOTYPE(int sys_segctl, (int *index, u16_t *seg, vir_bytes *off, #define sys_getschedinfo(v1,v2) sys_getinfo(GET_SCHEDINFO, v1,0, v2,0) #define sys_getlocktimings(dst) sys_getinfo(GET_LOCKTIMING, dst, 0,0,0) #define sys_getprivid(nr) sys_getinfo(GET_PRIVID, 0, 0,0, nr) +#define sys_getidletsc(dst) sys_getinfo(GET_IDLETSC, dst, 0,0,0) _PROTOTYPE(int sys_getinfo, (int request, void *val_ptr, int val_len, void *val_ptr2, int val_len2) ); _PROTOTYPE(int sys_whoami, (endpoint_t *ep, char *name, int namelen)); @@ -242,9 +243,5 @@ _PROTOTYPE( int sys_cprof, (int action, int size, endpoint_t endpt, void *ctl_ptr, void *mem_ptr) ); _PROTOTYPE( int sys_profbuf, (void *ctl_ptr, void *mem_ptr) ); -/* read_tsc() and friends. */ -_PROTOTYPE( void read_tsc_64, (u64_t *t) ); -_PROTOTYPE( void read_tsc, (u32_t *hi, u32_t *lo) ); - #endif /* _SYSLIB_H */ diff --git a/include/minix/sysutil.h b/include/minix/sysutil.h index 7318bd94d..487c6b864 100644 --- a/include/minix/sysutil.h +++ b/include/minix/sysutil.h @@ -51,6 +51,7 @@ _PROTOTYPE( int getuptime2, (clock_t *ticks, time_t *boottime)); _PROTOTYPE( int tickdelay, (clock_t ticks)); _PROTOTYPE( int micro_delay_calibrate, (void)); _PROTOTYPE( u32_t sys_hz, (void)); +_PROTOTYPE( double getidle, (void)); _PROTOTYPE( void util_stacktrace, (void)); _PROTOTYPE( void util_nstrcat, (char *str, unsigned long n) ); _PROTOTYPE( void util_stacktrace_strcat, (char *)); @@ -81,5 +82,9 @@ struct util_timingdata { typedef struct util_timingdata util_timingdata_t; +/* read_tsc() and friends. */ +_PROTOTYPE( void read_tsc_64, (u64_t *t) ); +_PROTOTYPE( void read_tsc, (u32_t *hi, u32_t *lo) ); + #endif /* _MINIX_SYSUTIL_H */ diff --git a/kernel/arch/i386/exception.c b/kernel/arch/i386/exception.c index 7fc6e2cb7..98d244005 100644 --- a/kernel/arch/i386/exception.c +++ b/kernel/arch/i386/exception.c @@ -8,7 +8,6 @@ #include #include #include -#include #include "../../proc.h" #include "../../proto.h" #include "../../vm.h" diff --git a/kernel/arch/i386/memory.c b/kernel/arch/i386/memory.c index 312d6abc9..53c2ce4c7 100644 --- a/kernel/arch/i386/memory.c +++ b/kernel/arch/i386/memory.c @@ -6,7 +6,6 @@ #include #include -#include #include #include diff --git a/kernel/arch/i386/proto.h b/kernel/arch/i386/proto.h index 08828a337..4a0c42978 100644 --- a/kernel/arch/i386/proto.h +++ b/kernel/arch/i386/proto.h @@ -119,7 +119,6 @@ struct tss_s { EXTERN struct tss_s tss; -_PROTOTYPE( void prot_init, (void) ); _PROTOTYPE( void idt_init, (void) ); _PROTOTYPE( void init_codeseg, (struct segdesc_s *segdp, phys_bytes base, vir_bytes size, int privilege) ); diff --git a/kernel/arch/i386/system.c b/kernel/arch/i386/system.c index 426c53dc6..f929604f2 100644 --- a/kernel/arch/i386/system.c +++ b/kernel/arch/i386/system.c @@ -9,7 +9,6 @@ #include #include #include -#include #include #include "proto.h" diff --git a/kernel/clock.c b/kernel/clock.c index db4be94b3..488b443b3 100644 --- a/kernel/clock.c +++ b/kernel/clock.c @@ -118,6 +118,8 @@ PUBLIC int bsp_timer_int_handler(void) { unsigned ticks; + IDLE_STOP; + if(minix_panicing) return 0; @@ -228,6 +230,8 @@ PUBLIC int ap_timer_int_handler(void) int expired = 0; struct proc * p, * billp; + IDLE_STOP; + /* Update user and system accounting times. Charge the current process * for user time. If the current process is not billable, that is, if a * non-user process is running, charge the billable process for system diff --git a/kernel/const.h b/kernel/const.h index c3f1ea008..9f41136f1 100644 --- a/kernel/const.h +++ b/kernel/const.h @@ -45,6 +45,12 @@ #define lock reallock #define unlock realunlock +#ifdef CONFIG_IDLE_TSC +#define IDLE_STOP if(idle_active) { read_tsc_64(&idle_stop); idle_active = 0; } +#else +#define IDLE_STOP +#endif + /* args to intr_init() */ #define INTS_ORIG 0 /* restore interrupts */ #define INTS_MINIX 1 /* initialize interrupts for minix */ diff --git a/kernel/glo.h b/kernel/glo.h index d639f7f79..41a2560ab 100644 --- a/kernel/glo.h +++ b/kernel/glo.h @@ -1,8 +1,6 @@ #ifndef GLO_H #define GLO_H -#include - /* Global variables used in the kernel. This file contains the declarations; * storage space for the variables is allocated in table.c, because EXTERN is * defined as extern unless the _TABLE definition is seen. We rely on the @@ -66,6 +64,12 @@ EXTERN int verboseflags; EXTERN int config_no_apic; /* optionaly turn off apic */ #endif +#ifdef CONFIG_IDLE_TSC +EXTERN u64_t idle_tsc; +EXTERN u64_t idle_stop; +EXTERN int idle_active; +#endif + /* VM */ EXTERN int vm_running; EXTERN int catch_pagefaults; diff --git a/kernel/interrupt.c b/kernel/interrupt.c index c43764820..536f49a01 100644 --- a/kernel/interrupt.c +++ b/kernel/interrupt.c @@ -112,6 +112,8 @@ PUBLIC void irq_handle(int irq) { irq_hook_t * hook; + IDLE_STOP; + /* here we need not to get this IRQ until all the handlers had a say */ hw_intr_mask(irq); hook = irq_handlers[irq]; diff --git a/kernel/kernel.h b/kernel/kernel.h index 4090733cb..f3971a4b9 100644 --- a/kernel/kernel.h +++ b/kernel/kernel.h @@ -5,6 +5,8 @@ #define CONFIG_APIC /* boot verbose */ #define CONFIG_BOOT_VERBOSE +/* measure cumulative idle timestamp counter ticks */ +#undef CONFIG_IDLE_TSC /* This is the master header for the kernel. It includes some other files * and defines the principal constants. @@ -20,6 +22,7 @@ #include /* MINIX specific constants */ #include /* MINIX specific types, e.g. message */ #include /* MINIX run-time system */ +#include /* MINIX utility library functions */ #include /* watchdog timer management */ #include /* return codes and error numbers */ diff --git a/kernel/main.c b/kernel/main.c index 777e12d4d..30c040585 100644 --- a/kernel/main.c +++ b/kernel/main.c @@ -16,6 +16,7 @@ #include #include #include +#include #include "proc.h" #include "debug.h" #include "clock.h" @@ -189,6 +190,10 @@ PUBLIC void main() #endif /* SPROFILE */ cprof_procs_no = 0; /* init nr of hash table slots used */ +#ifdef CONFIG_IDLE_TSC + idle_tsc = cvu64(0); +#endif + vm_running = 0; krandom.random_sources = RANDOM_SOURCES; krandom.random_elements = RANDOM_ELEMENTS; diff --git a/kernel/proc.c b/kernel/proc.c index 2f1f9d15c..c39470937 100644 --- a/kernel/proc.c +++ b/kernel/proc.c @@ -40,6 +40,7 @@ #include #include #include +#include #include "debug.h" #include "kernel.h" @@ -50,6 +51,7 @@ * other parts of the kernel through lock_...(). The lock temporarily disables * interrupts to prevent race conditions. */ +FORWARD _PROTOTYPE( void idle, (void)); FORWARD _PROTOTYPE( int mini_send, (struct proc *caller_ptr, int dst_e, message *m_ptr, int flags)); FORWARD _PROTOTYPE( int mini_receive, (struct proc *caller_ptr, int src, @@ -123,6 +125,35 @@ PRIVATE int QueueMess(endpoint_t ep, vir_bytes msg_lin, struct proc *dst) NOREC_RETURN(queuemess, OK); } +/*===========================================================================* + * idle * + *===========================================================================*/ +PRIVATE void idle() +{ + /* This function is called whenever there is no work to do. + * Halt the CPU, and measure how many timestamp counter ticks are + * spent not doing anything. This allows test setups to measure + * the CPU utiliziation of certain workloads with high precision. + */ +#ifdef CONFIG_IDLE_TSC + u64_t idle_start; + + read_tsc_64(&idle_start); + idle_active = 1; +#endif + + halt_cpu(); + +#ifdef CONFIG_IDLE_TSC + if (idle_active) { + IDLE_STOP; + printf("Kernel: idle active after resuming CPU\n"); + } + + idle_tsc = add64(idle_tsc, sub64(idle_stop, idle_start)); +#endif +} + /*===========================================================================* * schedcheck * *===========================================================================*/ @@ -166,7 +197,7 @@ not_runnable_pick_new: proc_ptr = proc_addr(IDLE); if (priv(proc_ptr)->s_flags & BILLABLE) bill_ptr = proc_ptr; - halt_cpu(); + idle(); } check_misc_flags: diff --git a/kernel/proto.h b/kernel/proto.h index cc5241aea..3c8fb3c7f 100644 --- a/kernel/proto.h +++ b/kernel/proto.h @@ -19,6 +19,7 @@ _PROTOTYPE( void reset_timer, (struct timer *tp) ); _PROTOTYPE( void ser_dump_proc, (void) ); /* main.c */ +_PROTOTYPE( void main, (void) ); _PROTOTYPE( void prepare_shutdown, (int how) ); _PROTOTYPE( void minix_shutdown, (struct timer *tp) ); @@ -102,6 +103,7 @@ _PROTOTYPE( void stop_profile_clock, (void) ); #endif /* functions defined in architecture-dependent files. */ +_PROTOTYPE( void prot_init, (void) ); _PROTOTYPE( phys_bytes phys_copy, (phys_bytes source, phys_bytes dest, phys_bytes count) ); _PROTOTYPE( void phys_copy_fault, (void)); diff --git a/kernel/system/do_getinfo.c b/kernel/system/do_getinfo.c index ecafdc2cb..22cbcbe5c 100644 --- a/kernel/system/do_getinfo.c +++ b/kernel/system/do_getinfo.c @@ -141,18 +141,26 @@ register message *m_ptr; /* pointer to request message */ break; } #endif - case GET_IRQACTIDS: { length = sizeof(irq_actids); src_vir = (vir_bytes) irq_actids; break; } - - case GET_PRIVID: + case GET_PRIVID: { if (!isokendpt(m_ptr->I_VAL_LEN2_E, &proc_nr)) return EINVAL; return proc_addr(proc_nr)->p_priv->s_id; - + } + case GET_IDLETSC: { +#ifdef CONFIG_IDLE_TSC + length = sizeof(idle_tsc); + src_vir = (vir_bytes) &idle_tsc; + break; +#else + kprintf("do_getinfo: kernel not compiled with CONFIG_IDLE_TSC\n"); + return(EINVAL); +#endif + } default: kprintf("do_getinfo: invalid request %d\n", m_ptr->I_REQUEST); return(EINVAL); diff --git a/kernel/utility.c b/kernel/utility.c index fa31cf3fe..bc70c1419 100644 --- a/kernel/utility.c +++ b/kernel/utility.c @@ -11,7 +11,6 @@ #include #include -#include #include /*===========================================================================* diff --git a/lib/sysutil/Makefile.in b/lib/sysutil/Makefile.in index cd9f53252..c5baf71f6 100644 --- a/lib/sysutil/Makefile.in +++ b/lib/sysutil/Makefile.in @@ -10,6 +10,7 @@ libsys_FILES=" \ kputc.c \ tickdelay.c \ get_randomness.c \ + getidle.c \ getuptime.c \ getuptime2.c \ env_get_prm.c \ diff --git a/lib/sysutil/getidle.c b/lib/sysutil/getidle.c new file mode 100644 index 000000000..fdd662225 --- /dev/null +++ b/lib/sysutil/getidle.c @@ -0,0 +1,92 @@ +/* getidle.c - by David van Moolenbroek */ + +/* Usage: + * + * double idleperc; + * getidle(); + * ... + * idleperc = getidle(); + * printf("CPU usage: %lg%%\n", 100.0 - idleperc); + * + * This routine goes through PM to get the idle time, rather than making the + * sys_getinfo() call to the kernel directly. This means that it can be used + * by non-system processes as well, but it will incur some extra overhead in + * the system case. The overhead does not end up being measured, because the + * system is clearly not idle while the system calls are being made. In any + * case, for this reason, only one getidle() run is allowed at a time. + * + * Note that the kernel has to be compiled with CONFIG_IDLE_TSC support. + */ + +#define _MINIX 1 +#define _SYSTEM 1 +#include +#include +#include + +static u64_t start, idle; +static int running = 0; + +static double make_double(u64_t d) +{ +/* Convert a 64-bit fixed point value into a double. + * This whole thing should be replaced by something better eventually. + */ + double value; + int i; + + value = (double) ex64hi(d); + for (i = 0; i < sizeof(unsigned long); i += 2) + value *= 65536.0; + + value += (double) ex64lo(d); + + return value; +} + +double getidle(void) +{ + u64_t stop, idle2; + u64_t idelta, tdelta; + double ifp, tfp, rfp; + int r; + + if (!running) { + r = getsysinfo_up(PM_PROC_NR, SIU_IDLETSC, sizeof(idle), &idle); + if (r != sizeof(idle)) + return -1.0; + + running = 1; + + read_tsc_64(&start); + + return 0.0; + } + else { + read_tsc_64(&stop); + + running = 0; + + r = getsysinfo_up(PM_PROC_NR, SIU_IDLETSC, sizeof(idle2), &idle2); + if (r != sizeof(idle2)) + return -1.0; + + idelta = sub64(idle2, idle); + tdelta = sub64(stop, start); + + if (cmp64(idelta, tdelta) >= 0) + return 100.0; + + ifp = make_double(idelta); + tfp = make_double(tdelta); + + rfp = ifp / tfp * 100.0; + + if (rfp < 0.0) rfp = 0.0; + else if (rfp > 100.0) rfp = 100.0; + + return rfp; + } + + running = !running; +} diff --git a/lib/sysutil/profile.c b/lib/sysutil/profile.c index b64c03833..e8523080a 100644 --- a/lib/sysutil/profile.c +++ b/lib/sysutil/profile.c @@ -16,7 +16,7 @@ #include #include #include -#include +#include #include PRIVATE char cpath[CPROF_CPATH_MAX_LEN]; /* current call path string */ @@ -61,7 +61,7 @@ char *name; if (cprof_locked) return; else cprof_locked = 1; /* Read CPU cycle count into local variable. */ - read_tsc(&start.hi, &start.lo); + read_tsc_64(&start); /* Run init code once after system boot. */ if (init == 0) { @@ -105,8 +105,7 @@ char *name; } /* Save initial cycle count on stack. */ - cprof_stk[cprof_stk_top].start_1.hi = start.hi; - cprof_stk[cprof_stk_top].start_1.lo = start.lo; + cprof_stk[cprof_stk_top].start_1 = start; /* Check available call path len. */ if (cpath_len + strlen(name) + 1 > CPROF_CPATH_MAX_LEN) { @@ -167,8 +166,7 @@ char *name; cprof_stk[cprof_stk_top].slot = cprof_slot; /* Again save CPU cycle count on stack. */ - read_tsc(&cprof_stk[cprof_stk_top].start_2.hi, - &cprof_stk[cprof_stk_top].start_2.lo); + read_tsc_64(&cprof_stk[cprof_stk_top].start_2); cprof_locked = 0; } @@ -201,8 +199,7 @@ char *name; sub64(spent, cprof_stk[cprof_stk_top].spent_deeper)); /* Clear spent_deeper for call level we're leaving. */ - cprof_stk[cprof_stk_top].spent_deeper.lo = 0; - cprof_stk[cprof_stk_top].spent_deeper.hi = 0; + cprof_stk[cprof_stk_top].spent_deeper = cvu64(0); /* Adjust call path string and stack. */ cpath_len = cprof_stk[cprof_stk_top].cpath_len; @@ -246,12 +243,9 @@ PRIVATE void cprof_init() { for (i=0; i