From: Tomas Hruby Date: Wed, 10 Feb 2010 15:36:54 +0000 (+0000) Subject: Time accounting based on TSC X-Git-Tag: v3.1.7~292 X-Git-Url: http://zhaoyanbai.com/repos/%22http:/www.isc.org/icons/doxygen.png?a=commitdiff_plain;h=1b56fdb33cb7b0289aa7f54bb0d0a3b919cee072;p=minix.git Time accounting based on TSC - as thre are still KERNEL and IDLE entries, time accounting for kernel and idle time works the same as for any other process - everytime we stop accounting for the currently running process, kernel or idle, we read the TSC counter and increment the p_cycles entry. - the process cycles inherently include some of the kernel cycles as we can stop accounting for the process only after we save its context and we start accounting just before we restore its context - this assumes that the system does not scale the CPU frequency which will be true for ... long time ;-) --- diff --git a/kernel/arch/i386/apic_asm.S b/kernel/arch/i386/apic_asm.S index 28c53b414..4025b56db 100644 --- a/kernel/arch/i386/apic_asm.S +++ b/kernel/arch/i386/apic_asm.S @@ -36,12 +36,16 @@ TEST_INT_IN_KERNEL(4, 0f) ;\ \ SAVE_PROCESS_CTX(0) ;\ + push %ebp ;\ + call cycles_accounting_stop ;\ + add $4, %esp ;\ movl $0, %ebp /* for stack trace */ ;\ APIC_IRQ_HANDLER(irq) ;\ jmp restart ;\ \ 0: \ pusha ;\ + call cycles_accounting_stop_idle ;\ APIC_IRQ_HANDLER(irq) ;\ popa ;\ iret ; @@ -142,12 +146,16 @@ apic_hwint15: TEST_INT_IN_KERNEL(4, 0f) ;\ \ SAVE_PROCESS_CTX(0) ;\ + push %ebp ;\ + call cycles_accounting_stop ;\ + add $4, %esp ;\ movl $0, %ebp /* for stack trace */ ;\ LAPIC_INTR_HANDLER(func) ;\ jmp restart ;\ \ 0: \ pusha ;\ + call cycles_accounting_stop_idle ;\ LAPIC_INTR_HANDLER(func) ;\ popa ;\ iret ; diff --git a/kernel/arch/i386/clock.c b/kernel/arch/i386/clock.c index 377312ae7..cbc48d02b 100644 --- a/kernel/arch/i386/clock.c +++ b/kernel/arch/i386/clock.c @@ -7,6 +7,9 @@ #include "../../kernel.h" #include "../../clock.h" +#include "../../proc.h" +#include + #ifdef CONFIG_APIC #include "apic.h" @@ -22,6 +25,9 @@ #define TIMER_FREQ 1193182 /* clock frequency for timer in PC and AT */ #define TIMER_COUNT(freq) (TIMER_FREQ/(freq)) /* initial value for counter*/ +/* FIXME make it cpu local! */ +PRIVATE u64_t tsc_ctr_switch; /* when did we switched time accounting */ + PRIVATE irq_hook_t pic_timer_hook; /* interrupt handler hook */ /*===========================================================================* @@ -119,3 +125,22 @@ PUBLIC int arch_register_local_timer_handler(irq_handler_t handler) return 0; } + +PUBLIC void cycles_accounting_init(void) +{ + read_tsc_64(&tsc_ctr_switch); +} + +PUBLIC void cycles_accounting_stop(struct proc * p) +{ + u64_t tsc; + + read_tsc_64(&tsc); + p->p_cycles = add64(p->p_cycles, sub64(tsc, tsc_ctr_switch)); + tsc_ctr_switch = tsc; +} + +PUBLIC void cycles_accounting_stop_idle(void) +{ + cycles_accounting_stop(proc_addr(IDLE)); +} diff --git a/kernel/arch/i386/mpx386.S b/kernel/arch/i386/mpx386.S index ed3a7c2b8..539948d0b 100644 --- a/kernel/arch/i386/mpx386.S +++ b/kernel/arch/i386/mpx386.S @@ -253,6 +253,9 @@ csinit: TEST_INT_IN_KERNEL(4, 0f) ;\ \ SAVE_PROCESS_CTX(0) ;\ + push %ebp ;\ + call cycles_accounting_stop ;\ + add $4, %esp ;\ movl $0, %ebp /* for stack trace */ ;\ PIC_IRQ_HANDLER(irq) ;\ movb $END_OF_INT, %al ;\ @@ -261,6 +264,7 @@ csinit: \ 0: \ pusha ;\ + call cycles_accounting_stop_idle ;\ PIC_IRQ_HANDLER(irq) ;\ movb $END_OF_INT, %al ;\ outb $INT_CTL /* reenable interrupts in master pic */ ;\ @@ -316,6 +320,9 @@ hwint07: TEST_INT_IN_KERNEL(4, 0f) ;\ \ SAVE_PROCESS_CTX(0) ;\ + push %ebp ;\ + call cycles_accounting_stop ;\ + add $4, %esp ;\ movl $0, %ebp /* for stack trace */ ;\ PIC_IRQ_HANDLER(irq) ;\ movb $END_OF_INT, %al ;\ @@ -325,6 +332,7 @@ hwint07: \ 0: \ pusha ;\ + call cycles_accounting_stop_idle ;\ PIC_IRQ_HANDLER(irq) ;\ movb $END_OF_INT, %al ;\ outb $INT_CTL /* reenable interrupts in master pic */ ;\ @@ -395,6 +403,11 @@ ipc_entry: push %eax push %ecx + /* stop user process cycles */ + push %ebp + call cycles_accounting_stop + add $4, %esp + /* for stack trace */ movl $0, %ebp @@ -420,9 +433,6 @@ kernel_call_entry: /* save the pointer to the current process */ push %ebp - /* for stack trace */ - movl $0, %ebp - /* * pass the syscall arguments from userspace to the handler. * SAVE_PROCESS_CTX() does not clobber these registers, they are still @@ -430,6 +440,14 @@ kernel_call_entry: */ push %eax + /* stop user process cycles */ + push %ebp + call cycles_accounting_stop + add $4, %esp + + /* for stack trace */ + movl $0, %ebp + call kernel_call /* restore the current process pointer and save the return value */ @@ -458,6 +476,11 @@ exception_entry_from_user: SAVE_PROCESS_CTX(8) + /* stop user process cycles */ + push %ebp + call cycles_accounting_stop + add $4, %esp + /* for stack trace clear %ebp */ movl $0, %ebp @@ -604,6 +627,10 @@ copr_not_available: jnz 0f /* jump if FPU is already initialized */ orw $MF_FPU_INITIALIZED, (%ebx) fninit + /* stop user process cycles */ + push %ebp + call cycles_accounting_stop + add $4, %esp jmp copr_return 0: /* load FPU context for current process */ mov %ss:FP_SAVE_AREA_P(%ebp), %eax diff --git a/kernel/arch/i386/system.c b/kernel/arch/i386/system.c index ee1979379..d54dc63a4 100644 --- a/kernel/arch/i386/system.c +++ b/kernel/arch/i386/system.c @@ -325,10 +325,10 @@ PRIVATE void printslot(struct proc *pp, int level) COL - kprintf("%d: %s %d prio %d/%d time %d/%d cr3 0x%lx rts %s misc %s", + kprintf("%d: %s %d prio %d/%d time %d/%d cycles 0x%x%08x cr3 0x%lx rts %s misc %s", proc_nr(pp), pp->p_name, pp->p_endpoint, pp->p_priority, pp->p_max_priority, pp->p_user_time, - pp->p_sys_time, pp->p_seg.p_cr3, + pp->p_sys_time, pp->p_cycles.hi, pp->p_cycles.lo, pp->p_seg.p_cr3, rtsflagstr(pp->p_rts_flags), miscflagstr(pp->p_misc_flags)); if(pp->p_rts_flags & RTS_SENDING) { diff --git a/kernel/clock.c b/kernel/clock.c index 7ffe414de..fd57a3e2f 100644 --- a/kernel/clock.c +++ b/kernel/clock.c @@ -78,8 +78,6 @@ PUBLIC int bsp_timer_int_handler(void) { unsigned ticks; - IDLE_STOP; - if(minix_panicing) return 0; @@ -192,8 +190,6 @@ PUBLIC int ap_timer_int_handler(void) int expired = 0; struct proc * p, * billp; - IDLE_STOP; - #ifdef CONFIG_WATCHDOG /* * we need to know whether local timer ticks are happening or whether diff --git a/kernel/const.h b/kernel/const.h index d650bb88e..3f40ef9bb 100644 --- a/kernel/const.h +++ b/kernel/const.h @@ -25,12 +25,6 @@ #define unset_sys_bit(map,bit) \ ( MAP_CHUNK(map.chunk,bit) &= ~(1 << CHUNK_OFFSET(bit) ) -#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 3e48708aa..85e0b0321 100644 --- a/kernel/glo.h +++ b/kernel/glo.h @@ -58,12 +58,6 @@ 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 - EXTERN unsigned cpu_hz[CONFIG_MAX_CPUS]; #define cpu_set_freq(cpu, freq) do {cpu_hz[cpu] = freq;} while (0) diff --git a/kernel/interrupt.c b/kernel/interrupt.c index ca4311f46..33380b5ef 100644 --- a/kernel/interrupt.c +++ b/kernel/interrupt.c @@ -111,8 +111,6 @@ 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 97809c1d2..b1ccf6097 100644 --- a/kernel/kernel.h +++ b/kernel/kernel.h @@ -13,8 +13,6 @@ /* We only support 1 cpu now */ #define CONFIG_MAX_CPUS 1 #define cpuid 0 -/* 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. diff --git a/kernel/main.c b/kernel/main.c index d1f82a311..ddd8c284c 100644 --- a/kernel/main.c +++ b/kernel/main.c @@ -218,10 +218,6 @@ 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; @@ -255,6 +251,8 @@ PUBLIC void main() FIXME("PROC check enabled"); #endif + cycles_accounting_init(); + restart(); NOT_REACHABLE; } diff --git a/kernel/proc.c b/kernel/proc.c index 5e957767f..57022e81a 100644 --- a/kernel/proc.c +++ b/kernel/proc.c @@ -119,30 +119,21 @@ PRIVATE int QueueMess(endpoint_t ep, vir_bytes msg_lin, struct proc *dst) /*===========================================================================* * idle * *===========================================================================*/ -PRIVATE void idle() +PRIVATE void idle(void) { /* 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 + /* start accounting for the idle time */ + cycles_accounting_stop(proc_addr(KERNEL)); 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 + /* + * end of accounting for the idle task does not happen here, the kernel + * is handling stuff for quite a while before it gets back here! + */ } /*===========================================================================* @@ -274,6 +265,7 @@ check_misc_flags: #endif proc_ptr = arch_finish_schedcheck(); + cycles_accounting_stop(proc_addr(KERNEL)); NOREC_RETURN(schedch, proc_ptr); } diff --git a/kernel/proc.h b/kernel/proc.h index 467bfbe8b..a144a4cb0 100644 --- a/kernel/proc.h +++ b/kernel/proc.h @@ -40,6 +40,8 @@ struct proc { clock_t p_virt_left; /* number of ticks left on virtual timer */ clock_t p_prof_left; /* number of ticks left on profile timer */ + u64_t p_cycles; /* how many cycles did the process use */ + struct proc *p_nextready; /* pointer to next ready process */ struct proc *p_caller_q; /* head of list of procs wishing to send */ struct proc *p_q_link; /* link to next proc wishing to send */ diff --git a/kernel/proto.h b/kernel/proto.h index 1aafbc76f..1d03c6f26 100644 --- a/kernel/proto.h +++ b/kernel/proto.h @@ -18,6 +18,18 @@ _PROTOTYPE( void set_timer, (struct timer *tp, clock_t t, tmr_func_t f) ); _PROTOTYPE( void reset_timer, (struct timer *tp) ); _PROTOTYPE( void ser_dump_proc, (void) ); +_PROTOTYPE( void cycles_accounting_init, (void) ); +/* + * This functions start and stop accounting for process, kernel or idle cycles. + * It inherently have to account for some kernel cycles for process too, + * therefore it should be called asap after trapping to kernel and as late as + * possible before returning to userspace. These function is architecture + * dependent + */ +_PROTOTYPE( void cycles_accounting_stop, (struct proc * p) ); +/* this is a wrapper to make calling it from assembly easier */ +_PROTOTYPE( void cycles_accounting_stop_idle, (void) ); + /* main.c */ _PROTOTYPE( void main, (void) ); _PROTOTYPE( void prepare_shutdown, (int how) ); diff --git a/kernel/system/do_getinfo.c b/kernel/system/do_getinfo.c index 98400404c..4aea7020b 100644 --- a/kernel/system/do_getinfo.c +++ b/kernel/system/do_getinfo.c @@ -152,14 +152,12 @@ PUBLIC int do_getinfo(struct proc * caller, message * m_ptr) break; } case GET_IDLETSC: { -#ifdef CONFIG_IDLE_TSC - length = sizeof(idle_tsc); - src_vir = (vir_bytes) &idle_tsc; + struct proc * idl; + + idl = proc_addr(IDLE); + length = sizeof(idl->p_cycles); + src_vir = (vir_bytes) &idl->p_cycles; break; -#else - kprintf("do_getinfo: kernel not compiled with CONFIG_IDLE_TSC\n"); - return(EINVAL); -#endif } case GET_AOUTHEADER: { int hdrindex, index = m_ptr->I_VAL_LEN2_E;