/* * (C) 2010,2011 Thomas Renninger <trenn@suse.de>, Novell Inc * * Licensed under the terms of the GNU GPL License version 2. * */ #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <string.h> #include <limits.h> #include "helpers/sysfs.h" #include "helpers/helpers.h" #include "idle_monitor/cpupower-monitor.h" #define CPUIDLE_STATES_MAX 10 static cstate_t cpuidle_cstates[CPUIDLE_STATES_MAX]; struct cpuidle_monitor cpuidle_sysfs_monitor; static unsigned long long **previous_count; static unsigned long long **current_count; struct timespec start_time; static unsigned long long timediff; static int cpuidle_get_count_percent(unsigned int id, double *percent, unsigned int cpu) { unsigned long long statediff = current_count[cpu][id] - previous_count[cpu][id]; dprint("%s: - diff: %llu - percent: %f (%u)\n", cpuidle_cstates[id].name, timediff, *percent, cpu); if (timediff == 0) *percent = 0.0; else *percent = ((100.0 * statediff) / timediff); dprint("%s: - timediff: %llu - statediff: %llu - percent: %f (%u)\n", cpuidle_cstates[id].name, timediff, statediff, *percent, cpu); return 0; } static int cpuidle_start(void) { int cpu, state; clock_gettime(CLOCK_REALTIME, &start_time); for (cpu = 0; cpu < cpu_count; cpu++) { for (state = 0; state < cpuidle_sysfs_monitor.hw_states_num; state++) { previous_count[cpu][state] = sysfs_get_idlestate_time(cpu, state); dprint("CPU %d - State: %d - Val: %llu\n", cpu, state, previous_count[cpu][state]); } }; return 0; } static int cpuidle_stop(void) { int cpu, state; struct timespec end_time; clock_gettime(CLOCK_REALTIME, &end_time); timediff = timespec_diff_us(start_time, end_time); for (cpu = 0; cpu < cpu_count; cpu++) { for (state = 0; state < cpuidle_sysfs_monitor.hw_states_num; state++) { current_count[cpu][state] = sysfs_get_idlestate_time(cpu, state); dprint("CPU %d - State: %d - Val: %llu\n", cpu, state, previous_count[cpu][state]); } }; return 0; } void fix_up_intel_idle_driver_name(char *tmp, int num) { /* fix up cpuidle name for intel idle driver */ if (!strncmp(tmp, "NHM-", 4)) { switch (num) { case 1: strcpy(tmp, "C1"); break; case 2: strcpy(tmp, "C3"); break; case 3: strcpy(tmp, "C6"); break; } } else if (!strncmp(tmp, "SNB-", 4)) { switch (num) { case 1: strcpy(tmp, "C1"); break; case 2: strcpy(tmp, "C3"); break; case 3: strcpy(tmp, "C6"); break; case 4: strcpy(tmp, "C7"); break; } } else if (!strncmp(tmp, "ATM-", 4)) { switch (num) { case 1: strcpy(tmp, "C1"); break; case 2: strcpy(tmp, "C2"); break; case 3: strcpy(tmp, "C4"); break; case 4: strcpy(tmp, "C6"); break; } } } static struct cpuidle_monitor *cpuidle_register(void) { int num; char *tmp; /* Assume idle state count is the same for all CPUs */ cpuidle_sysfs_monitor.hw_states_num = sysfs_get_idlestate_count(0); if (cpuidle_sysfs_monitor.hw_states_num <= 0) return NULL; for (num = 0; num < cpuidle_sysfs_monitor.hw_states_num; num++) { tmp = sysfs_get_idlestate_name(0, num); if (tmp == NULL) continue; fix_up_intel_idle_driver_name(tmp, num); strncpy(cpuidle_cstates[num].name, tmp, CSTATE_NAME_LEN - 1); free(tmp); tmp = sysfs_get_idlestate_desc(0, num); if (tmp == NULL) continue; strncpy(cpuidle_cstates[num].desc, tmp, CSTATE_DESC_LEN - 1); free(tmp); cpuidle_cstates[num].range = RANGE_THREAD; cpuidle_cstates[num].id = num; cpuidle_cstates[num].get_count_percent = cpuidle_get_count_percent; }; /* Free this at program termination */ previous_count = malloc(sizeof(long long *) * cpu_count); current_count = malloc(sizeof(long long *) * cpu_count); for (num = 0; num < cpu_count; num++) { previous_count[num] = malloc(sizeof(long long) * cpuidle_sysfs_monitor.hw_states_num); current_count[num] = malloc(sizeof(long long) * cpuidle_sysfs_monitor.hw_states_num); } cpuidle_sysfs_monitor.name_len = strlen(cpuidle_sysfs_monitor.name); return &cpuidle_sysfs_monitor; } void cpuidle_unregister(void) { int num; for (num = 0; num < cpu_count; num++) { free(previous_count[num]); free(current_count[num]); } free(previous_count); free(current_count); } struct cpuidle_monitor cpuidle_sysfs_monitor = { .name = "Idle_Stats", .hw_states = cpuidle_cstates, .start = cpuidle_start, .stop = cpuidle_stop, .do_register = cpuidle_register, .unregister = cpuidle_unregister, .needs_root = 0, .overflow_s = UINT_MAX, };