#include #include #include #include #include #include #if defined(CONFIG_AVM_POWERMETER) #include #include #endif/*--- #if defined(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 defined(CONFIG_AVM_ENHANCED) #include #endif/*--- #if defined(CONFIG_AVM_ENHANCED) ---*/ #if KERNEL_VERSION(2, 6, 32) > LINUX_VERSION_CODE /** * 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 KERNEL_VERSION(3, 8, 0) > LINUX_VERSION_CODE #define task_cputime_adjusted(a, b, c) task_times(a, b, c) #endif #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]; /** * die Normierung erfolgt entsprechend des Zeitfensters */ #if defined(__get_last_unaligned_info) static const char *norm_ai(unsigned long *ai, unsigned int sec) { if (sec >= 60 * 60) { *ai *= 60 * 60; return "h"; } if (sec >= 60) { *ai *= 60; return "min"; } return "s"; } #endif /** */ static char *print_ai_info(char *txt, int txtlen __maybe_unused, unsigned long sec __maybe_unused, int user __maybe_unused) { #if defined(__get_last_unaligned_info) static unsigned long last_ai_user; static unsigned long last_ai_sys; const char *comm, *post_fix; unsigned long ai_diff; unsigned long ai_count; unsigned long last_pc; unsigned long *last_ai_count = user ? &last_ai_user : &last_ai_sys; comm = get_last_unaligned_info(&ai_count, &last_pc, user); ai_diff = ai_count - *last_ai_count; if (ai_diff == 0) { txt[0] = 0; return txt; } *last_ai_count = ai_count; post_fix = norm_ai(&ai_diff, sec); if (sec == 0) sec = 1; if (user) { snprintf(txt, txtlen, " ai_user:%lu.%02lu/%s 0x%08lx %s", ai_diff / sec, ((ai_diff * 100) / sec) % 100, post_fix, last_pc, comm); } else { snprintf(txt, txtlen, " ai_sys:%lu.%02lu/%s %pS %s", ai_diff / sec, ((ai_diff * 100) / sec) % 100, post_fix, (void *)last_pc, comm); } #else txt[0] = 0; #endif/*--- #if defined(__get_last_unaligned_info) ---*/ return txt; } #define COUNT_DIFF(act, last) ((act) - (last)) /** */ static inline unsigned int 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 long count, tmp; unsigned int cpu = get_cpu(); struct _cpu_idlecount *pcpuidle = &cpu_idlecount[cpu]; put_cpu(); #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]; /** */ static struct _hist_task_times *get_histtask_by_pid(unsigned int pid, unsigned int *cache_idx) { register unsigned int i; if (*cache_idx >= ARRAY_SIZE(hist_task_times)) { *cache_idx = 0; } for (i = *cache_idx; i < ARRAY_SIZE(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_SIZE(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 KERNEL_VERSION(2, 6, 33) <= LINUX_VERSION_CODE unsigned long ut, st; task_cputime_adjusted(p, &ut, &st); time_act = ut + st; #else time_act = task_stime(p) + task_utime(p); #endif 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_SIZE(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 = get_cpu(); unsigned int cpu_run = 0; struct _cpu_idlecount *pcpuidle = &cpu_idlecount[cpu]; char txt[2][64]; put_cpu(); if (test_and_set_bit(0, &pcpuidle->last_value_done) == 0) { unsigned long 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 < num_possible_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 KERNEL_VERSION(2, 6, 32) <= LINUX_VERSION_CODE global_page_state(NR_ISOLATED_ANON)+ global_page_state(NR_ISOLATED_FILE)+ global_page_state(NR_SHMEM)+ #endif 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); pr_warn("[%x]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%s%s (sleep %lu)"\ "\n", cpu, 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_RECLAIMABLE) + global_page_state(NR_SLAB_UNRECLAIMABLE), pg_alloc_diff / (j_diff / HZ), (pg_fault_cnt - last_pg_fault_cnt) / (j_diff / HZ), print_ai_info(txt[0], sizeof(txt[0]), j_diff / HZ, 0), print_ai_info(txt[1], sizeof(txt[1]), j_diff / HZ, 1), 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 = get_cpu(); struct _cpu_idlecount *pcpuidle = &cpu_idlecount[cpu]; register unsigned int sum_count = pcpuidle->sum_count; unsigned int cpu_run = 0; put_cpu(); 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 < num_possible_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); pr_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; } early_initcall(avm_cpu_process_init);