]> Zhao Yanbai Git Server - minix.git/commitdiff
Time accounting based on TSC
authorTomas Hruby <tom@minix3.org>
Wed, 10 Feb 2010 15:36:54 +0000 (15:36 +0000)
committerTomas Hruby <tom@minix3.org>
Wed, 10 Feb 2010 15:36:54 +0000 (15:36 +0000)
- 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 ;-)

14 files changed:
kernel/arch/i386/apic_asm.S
kernel/arch/i386/clock.c
kernel/arch/i386/mpx386.S
kernel/arch/i386/system.c
kernel/clock.c
kernel/const.h
kernel/glo.h
kernel/interrupt.c
kernel/kernel.h
kernel/main.c
kernel/proc.c
kernel/proc.h
kernel/proto.h
kernel/system/do_getinfo.c

index 28c53b414c719d4815a0cdc299af4b0c8cc2357d..4025b56db3d71e66726f5d0ccde9048e115ea81f 100644 (file)
        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                                                            ;
index 377312ae77d4dc8c93a18635e63f09be57380fff..cbc48d02b3507b8ef78c24991cf639c22f02e04a 100644 (file)
@@ -7,6 +7,9 @@
 #include "../../kernel.h"
 
 #include "../../clock.h"
+#include "../../proc.h"
+#include <minix/u64.h>
+
 
 #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));
+}
index ed3a7c2b8909b9aaedc859e2f956cee8bbdec598..539948d0ba9f54e52e431a5c0fd7c10e6108aac3 100644 (file)
@@ -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
index ee197937935bde74a447d4ff30bcdc44b8c60cfc..d54dc63a43cdd3f1288644d3ae76cd89c3248543 100644 (file)
@@ -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) {
index 7ffe414decea0474ae8bffb6977deb4a567cfb63..fd57a3e2fe0ce9785968a4cb9f90dcf0f0742382 100644 (file)
@@ -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
index d650bb88e84ff5aaad7734e2dc933eeeb47bcf58..3f40ef9bb04276f17ed99418c94f858f93e54f71 100644 (file)
 #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 */
index 3e48708aa50907ad0ea82b3d12f9854eb038b0ba..85e0b0321c5de373eb3ba3d555edc49af6c60b78 100644 (file)
@@ -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)
index ca4311f46aa868b5e63f71de3b5ffda8bf972087..33380b5ef61c5f3a24f48913e22d31ae8b4c2b00 100644 (file)
@@ -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];
index 97809c1d208a3ec89b46f437ec732ffe1eb3eca4..b1ccf6097a70ad699dbba7805aad07a86b577b37 100644 (file)
@@ -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.
index d1f82a3113a20d44ab05091bb06d282d6ce0472e..ddd8c284c07f85b9c52187bff0483c345c42fbd2 100644 (file)
@@ -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;
 }
index 5e957767f88abe9b882c33e2166f5a6fa663dd6a..57022e81a60631abed765be3245b76eef4b84ce1 100644 (file)
@@ -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);
 }
index 467bfbe8b94b5bef452d49da6652e80414e7315f..a144a4cb088dbe3ab429fa866a786f378f1b26f1 100644 (file)
@@ -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 */
index 1aafbc76fc6d032840e290706e81c77395ed6f0f..1d03c6f2601ad3a3043d7fab1b7afddb78e7774c 100644 (file)
@@ -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)                           );
index 98400404c40f7490cbce30a1850f67d0cb7f3256..4aea7020bfe1b66e0a5d176c681652ce59a77a63 100644 (file)
@@ -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;