/*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ #include #include #include #include #include #include #ifdef CONFIG_AVM_POWERMETER #include #include #endif/*--- #ifdef CONFIG_AVM_POWERMETER ---*/ #if defined(CONFIG_NMI_ARBITER_WORKAROUND) #include #endif/*--- #if defined(CONFIG_NMI_ARBITER_WORKAROUND) ---*/ #if defined(CONFIG_AVM_SIMPLE_PROFILING) #include #endif/*--- #if defined(CONFIG_AVM_SIMPLE_PROFILING) ---*/ #include #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32) /** * get_avenrun - get the load average array * @loads: pointer to dest load array * @offset: offset to add * @shift: shift count to shift the result left * * These values are estimates at best, so no need for locking. */ void get_avenrun(unsigned long *loads, unsigned long offset, int shift) { loads[0] = (avenrun[0] + offset) << shift; loads[1] = (avenrun[1] + offset) << shift; loads[2] = (avenrun[2] + offset) << shift; } #endif/*--- #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32) ---*/ #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0) #define task_cputime_adjusted(a,b,c) task_times(a,b, c) #endif/*--- #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0) ---*/ #if defined(CONFIG_X86) #define CPU_COUNT_SHIFT 4 #else/*--- #if defined(CONFIG_X86) ---*/ #define CPU_COUNT_SHIFT 0 #endif/*--- #else ---*//*--- #if defined(CONFIG_X86) ---*/ atomic_t avm_page_faultlink[NR_CPUS]; int avm_page_faultlink_max[NR_CPUS]; static unsigned long long gCycle_per_10sec = 250ULL * 1000ULL * 1000ULL * 10ULL; /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static unsigned long vmevents[NR_VM_EVENT_ITEMS]; static unsigned long last_pgalloc_normal; static unsigned long last_pg_fault_cnt; /*--------------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------------*/ static struct _cpu_idlecount { unsigned int last_value; unsigned long last_value_done; unsigned long long run_summary; unsigned int wait_count; unsigned int run_count; unsigned int sum_count; unsigned int show; unsigned int fullrun; unsigned int actrun; unsigned int fulldisp_jiffies; } cpu_idlecount[NR_CPUS]; #define COUNT_DIFF(act, last) ((act) - (last)) /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static inline unsigned check_10second_without_idle(unsigned long act_cycle, struct _cpu_idlecount *pcpuidle) { pcpuidle->run_summary += COUNT_DIFF(act_cycle, pcpuidle->last_value); if(pcpuidle->run_summary > gCycle_per_10sec) { pcpuidle->run_summary = 0; return 1; } return 0; } /*--------------------------------------------------------------------------------*\ * liefert ob diese CPU im IDLE-Mode \*--------------------------------------------------------------------------------*/ unsigned int avm_cpu_idle_state(int vpe) { struct _cpu_idlecount *pcpuidle = &cpu_idlecount[vpe]; return !pcpuidle->last_value_done; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ void avm_cpu_wait_start(void) { unsigned int count, tmp; struct _cpu_idlecount *pcpuidle = &cpu_idlecount[raw_smp_processor_id()]; #if defined(CONFIG_AVM_SIMPLE_PROFILING) avm_simple_profiling_enter_idle((unsigned int)avm_cpu_wait_start); #endif/*--- #if defined(CONFIG_AVM_SIMPLE_PROFILING) ---*/ count = avm_get_cycles(); if(pcpuidle->last_value) { tmp = COUNT_DIFF(count, pcpuidle->last_value) >> CPU_COUNT_SHIFT; pcpuidle->run_count += tmp; pcpuidle->sum_count += tmp; } pcpuidle->last_value = count; pcpuidle->last_value_done = 0; pcpuidle->run_summary = 0; if(unlikely(pcpuidle->fulldisp_jiffies == 0)) { pcpuidle->fulldisp_jiffies = jiffies; } } #ifdef CONFIG_AVM_POWERMETER static struct _hist_task_times { unsigned long time_last; unsigned int pid; unsigned int mark; } hist_task_times[180]; #define ARRAY_EL(a) (sizeof(a) / sizeof(a[0])) /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static struct _hist_task_times *get_histtask_by_pid(unsigned int pid, unsigned int *cache_idx) { register unsigned int i; if(*cache_idx >= ARRAY_EL(hist_task_times)) { *cache_idx = 0; } for(i = *cache_idx; i < ARRAY_EL(hist_task_times); i++) { if(hist_task_times[i].pid == pid) { *cache_idx = i + 1; hist_task_times[i].mark = 2; return &hist_task_times[i]; } } for(i = 0; i < *cache_idx; i++) { if(hist_task_times[i].pid == pid) { *cache_idx = i + 1; hist_task_times[i].mark = 2; return &hist_task_times[i]; } } /*--- Create ---*/ for(i = 0; i < ARRAY_EL(hist_task_times); i++) { if(hist_task_times[i].mark == 0) { hist_task_times[i].mark = 2; hist_task_times[i].time_last = 0; hist_task_times[i].pid = pid; return &hist_task_times[i]; } } /*--- printk("", pid); ---*/ return NULL; } struct _accumulate_output { unsigned long sum_time; unsigned long curr_time; unsigned long maxrun_time; struct task_struct *maxrun_task; unsigned int tasks; }; /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static void accumulate_time(struct _accumulate_output *ao) { struct task_struct *g, *p, *maxrun_task = current; register unsigned int i; unsigned int cache_idx = 0; register unsigned long time_sum = 0; register unsigned long maxrun_time = 0; unsigned int tsks = 0; ao->maxrun_task = current; ao->curr_time = 0; do_each_thread(g, p) { struct _hist_task_times *pht = get_histtask_by_pid(p->pid, &cache_idx); tsks++; if(pht) { unsigned long time_act; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33) unsigned long ut, st; task_cputime_adjusted(p, &ut, &st); time_act = ut + st; #else/*--- #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33) ---*/ time_act = task_stime(p) + task_utime(p); #endif/*--- #else ---*//*--- #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33) ---*/ time_sum += time_act - pht->time_last; if(maxrun_time < (time_act - pht->time_last)) { maxrun_time = (time_act - pht->time_last); maxrun_task = p; } if(unlikely(p == current)){ ao->curr_time = time_act - pht->time_last; } pht->time_last = time_act; } else { ao->tasks = tsks; ao->sum_time = 0; return; } } while_each_thread(g, p); for(i = 0; i < ARRAY_EL(hist_task_times); i++) { struct _hist_task_times *pht = &hist_task_times[i]; if(pht->mark) { pht->mark--; /*--- um geloeschte Eintraege frei zu bekommen ---*/ } } ao->maxrun_task = maxrun_task; ao->maxrun_time = maxrun_time; ao->sum_time = time_sum | 1; ao->tasks = tsks; } #endif/*--- #ifdef CONFIG_AVM_POWERMETER ---*/ /*--------------------------------------------------------------------------------------*\ * for statistic function * idle stopps * call at the end of idle in wait_irq_off-function or direct in the irq-function \*--------------------------------------------------------------------------------------*/ int avm_cpu_wait_end(void) { unsigned int cpu = raw_smp_processor_id(); unsigned int cpu_run = 0; struct _cpu_idlecount *pcpuidle = &cpu_idlecount[cpu]; if(test_and_set_bit(0, &pcpuidle->last_value_done) == 0) { int count, tmp; count = avm_get_cycles(); tmp = COUNT_DIFF(count, pcpuidle->last_value) >> CPU_COUNT_SHIFT; pcpuidle->wait_count += tmp; pcpuidle->sum_count += tmp; #if defined(CONFIG_NMI_ARBITER_WORKAROUND) { tmp = ath_workaround_nmi_wastetime(); if(pcpuidle->wait_count > tmp) { pcpuidle->wait_count -= tmp; } else { pcpuidle->wait_count = 0; } } #endif/*--- #if defined(CONFIG_NMI_ARBITER_WORKAROUND) ---*/ pcpuidle->last_value = count; return 0; } #ifdef CONFIG_AVM_POWERMETER #define LOAD_INT(x) ((x) >> FSHIFT) #define LOAD_FRAC(x) LOAD_INT(((x) & (FIXED_1-1)) * 100) if(check_10second_without_idle(avm_get_cycles(), pcpuidle)) { register unsigned long j_diff; unsigned long curr_percent = 0, max_percent = 0, sum_percent = 0, pg_fault_cnt, pg_fault_max; pcpuidle->last_value = avm_get_cycles(); if(pcpuidle->fullrun == 0) { unsigned int i; pcpuidle->actrun = 100; for(i = 0; i < NR_CPUS; i++) { struct _cpu_idlecount *pcpuidle = &cpu_idlecount[i]; cpu_run |= pcpuidle->actrun << (i * 8); } PowerManagmentRessourceInfo(powerdevice_loadrate, cpu_run); } pcpuidle->fullrun++; j_diff = COUNT_DIFF(jiffies, pcpuidle->fulldisp_jiffies); if(j_diff >= (10 * HZ)) { struct _accumulate_output ao; unsigned long avnrun[3]; unsigned long pgalloc_normal, pg_alloc_diff, sum_pages; accumulate_time(&ao); get_avenrun(avnrun, FIXED_1/200, 0); if(ao.sum_time) { /*--- Promillewert im Verhaeltnis zu allen Tasks ---*/ curr_percent = (ao.curr_time * 100) / j_diff; max_percent = (ao.maxrun_time * 100) / j_diff; sum_percent = (ao.sum_time * 100) / j_diff; } pg_fault_max = avm_page_faultlink_max[cpu]; avm_page_faultlink_max[cpu] = 0; #ifdef CONFIG_VM_EVENT_COUNTERS __all_vm_events(vmevents); #endif/*--- #ifdef CONFIG_VM_EVENT_COUNTERS ---*/ pgalloc_normal = vmevents[PGALLOC_NORMAL]; pg_fault_cnt = vmevents[PGFAULT]; pg_alloc_diff = (pgalloc_normal - last_pgalloc_normal); sum_pages = global_page_state(NR_ACTIVE_ANON)+ global_page_state(NR_INACTIVE_ANON)+ global_page_state(NR_ACTIVE_FILE)+ global_page_state(NR_INACTIVE_FILE)+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) global_page_state(NR_ISOLATED_ANON)+ global_page_state(NR_ISOLATED_FILE)+ global_page_state(NR_SHMEM)+ #endif/*--- #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) ---*/ global_page_state(NR_UNEVICTABLE)+ global_page_state(NR_FILE_DIRTY)+ global_page_state(NR_WRITEBACK)+ global_page_state(NR_UNSTABLE_NFS)+ global_page_state(NR_FREE_PAGES)+ global_page_state(NR_SLAB_RECLAIMABLE)+ global_page_state(NR_SLAB_UNRECLAIMABLE)+ global_page_state(NR_FILE_MAPPED)+ global_page_state(NR_PAGETABLE)+ global_page_state(NR_BOUNCE); printk(KERN_WARNING"system-load %d%s loadavg %lu.%lu %lu.%lu %lu.%lu - %d tasks:%lu %% curr:%s(%lu %%) max:%s(%lu %%, pid:%d) " "pgstat: sum=%lu free=%lu slab=%lu alloc=%lu/s fault=%lu/s (sleep %lu)" "\n", pcpuidle->fullrun >= 10 ? 100 : pcpuidle->fullrun, pcpuidle->fullrun >= 10 ? "%" : "", LOAD_INT(avnrun[0]), LOAD_FRAC(avnrun[0]), LOAD_INT(avnrun[1]), LOAD_FRAC(avnrun[1]), LOAD_INT(avnrun[2]), LOAD_FRAC(avnrun[2]), ao.tasks, sum_percent, current->comm, curr_percent, ao.maxrun_task->comm, max_percent, ao.maxrun_task->pid, sum_pages, global_page_state(NR_FREE_PAGES), global_page_state(NR_SLAB_UNRECLAIMABLE) + global_page_state(NR_SLAB_UNRECLAIMABLE), pg_alloc_diff / (j_diff / HZ), (pg_fault_cnt - last_pg_fault_cnt) / (j_diff / HZ), pg_fault_max ); last_pgalloc_normal = pgalloc_normal; last_pg_fault_cnt = pg_fault_cnt; pcpuidle->fullrun = 0; pcpuidle->fulldisp_jiffies = jiffies; } /*--- printk(""); ---*/ } #endif/*--- #ifdef CONFIG_AVM_POWERMETER ---*/ return 1; } /*--------------------------------------------------------------------------------*\ * trigger powermanagment to inform about idle-rate \*--------------------------------------------------------------------------------*/ void avm_cpu_wait_info(void) { unsigned int cpu = raw_smp_processor_id(); struct _cpu_idlecount *pcpuidle = &cpu_idlecount[cpu]; register unsigned int sum_count = pcpuidle->sum_count; unsigned int cpu_run = 0; if(sum_count > (1 << 29)) { #if defined(CONFIG_AVM_POWERMETER) unsigned int i; int p_idle = ((pcpuidle->wait_count >> 8) * 100) / (pcpuidle->sum_count >> 8); pcpuidle->actrun = max(0, 100 - p_idle); for(i = 0; i < NR_CPUS; i++) { struct _cpu_idlecount *pcpuidle = &cpu_idlecount[i]; cpu_run |= pcpuidle->actrun << (i * 8); } PowerManagmentRessourceInfo(powerdevice_loadrate, cpu_run); #endif/*--- #ifdef CONFIG_AVM_POWERMETER ---*/ #if defined(CONFIG_SMP) & 0 { int p_run = ((pcpuidle->run_count >> 8) * 100) / (sum_count >> 8); int p_idle = ((pcpuidle->wait_count >> 8) * 100) / (pcpuidle->sum_count >> 8); printk(KERN_INFO"[idle:cpu%u]: %u%% (run %u%%) cpu_run %x\n", cpu, p_idle, p_run, cpu_run); } #endif/*--- #if defined(CONFIG_SMP) ---*/ pcpuidle->sum_count >>= 8; pcpuidle->wait_count >>= 8; pcpuidle->run_count >>= 8; } } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ int __init avm_cpu_process_init(void) { gCycle_per_10sec = (unsigned long long)avm_get_cyclefreq() * 10ULL; return 0; } __initcall(avm_cpu_process_init);