From 8a2d14faa7e56b63b129394d26ed40745907e141 Mon Sep 17 00:00:00 2001 From: Tuukka Tikkanen Date: Wed, 21 Jan 2015 00:45:39 +0200 Subject: Idlestat: P-state calculations for cores and clusters The P-state (frequency) for cores and clusters was not calculated. This patch adds recording of core/cluster frequency as the highest frequency of all non-idle cpus contained within the core/ cluster. Signed-off-by: Tuukka Tikkanen --- idlestat.c | 111 +++++++++++++++++++++++++++++++++++++++++++++++++++---------- idlestat.h | 2 +- topology.c | 32 ++++++++++++++---- topology.h | 4 +++ 4 files changed, 124 insertions(+), 25 deletions(-) diff --git a/idlestat.c b/idlestat.c index 1c1e8ac..9840a2e 100644 --- a/idlestat.c +++ b/idlestat.c @@ -531,10 +531,6 @@ static void open_current_pstate(struct cpufreq_pstates *ps, double time) static void open_next_pstate(struct cpufreq_pstates *ps, int s, double time) { ps->current = s; - if (ps->idle > 0) { - fprintf(stderr, "Warning: opening P-state on an idle CPU\n"); - return; - } open_current_pstate(ps, time); } @@ -544,11 +540,10 @@ static void close_current_pstate(struct cpufreq_pstates *ps, double time) struct cpufreq_pstate *p = &(ps->pstate[c]); double elapsed; - if (ps->idle > 0) { - fprintf(stderr, "Warning: closing P-state on an idle CPU\n"); - return; - } elapsed = (time - ps->time_enter) * USEC_PER_SEC; + if (elapsed <= 0) + return; + p->min_time = MIN(p->min_time, elapsed); p->max_time = MAX(p->max_time, elapsed); p->avg_time = AVG(p->avg_time, elapsed, p->count + 1); @@ -556,7 +551,69 @@ static void close_current_pstate(struct cpufreq_pstates *ps, double time) p->count++; } -void cpu_change_pstate(struct cpuidle_datas *datas, int cpu, +int record_group_freq(struct cpufreq_pstates *ps, double time, + unsigned int freq) +{ + int cur, next; + + cur = ps->current; + if (freq > 0) + next = alloc_pstate(ps, freq); + else + next = -1; + + if (cur == next) + return 0; /* No effective change */ + + if (cur == -1) { + /* The current pstate is -1, possibly leaving idle state */ + if (next == -1) + return 0; /* No known frequency to open, still idle */ + open_next_pstate(ps, next, time); + return 0; + } + + /* + * The group was running, update all stats and open a new state + * if needed. + */ + close_current_pstate(ps, time); + + ps->current = next; + if (next == -1) + return 0; + + open_current_pstate(ps, time); + return 0; +} + +int check_pstate_composite(struct cpuidle_datas *datas, int cpu, double time) +{ + struct cpu_core *aff_core; + struct cpu_physical *aff_cluster; + unsigned int freq; + + aff_core = cpu_to_core(cpu, datas->topo); + aff_cluster = cpu_to_cluster(cpu, datas->topo); + + freq = core_get_highest_freq(aff_core); + if (aff_core->is_ht) { + verbose_fprintf(stderr, 5, "Core %c%d: freq %9u, time %f\n", + aff_cluster->physical_id + 'A', + aff_core->core_id, + freq, time); + } + if (record_group_freq(aff_core->pstates, time, freq) == -1) + return -1; + + freq = cluster_get_highest_freq(aff_cluster); + verbose_fprintf(stderr, 5, "Cluster %c: freq %9u, time %f\n", + aff_cluster->physical_id + 'A', freq, time); + return record_group_freq(aff_cluster->pstates, time, freq); +} + + +int cpu_change_pstate(struct cpuidle_datas *datas, int cpu, unsigned int freq, double time) { struct cpufreq_pstates *ps; @@ -573,12 +630,12 @@ void cpu_change_pstate(struct cpuidle_datas *datas, int cpu, * stats unchanged */ ps->current = next; - return; + return 0; case -1: /* current pstate is -1, i.e. this is the first update */ open_next_pstate(ps, next, time); - return; + break; case 0: /* running CPU, update all stats, but skip closing current @@ -587,13 +644,16 @@ void cpu_change_pstate(struct cpuidle_datas *datas, int cpu, if (p) close_current_pstate(ps, time); open_next_pstate(ps, next, time); - return; + break; default: fprintf(stderr, "illegal pstate %d for cpu %d, exiting.\n", cur, cpu); exit(-1); } + + /* See if core or cluster highest frequency changed */ + return check_pstate_composite(datas, cpu, time); } /** @@ -644,6 +704,9 @@ static void cpu_pstate_idle(struct cpuidle_datas *datas, int cpu, double time) if (ps->current != -1) close_current_pstate(ps, time); ps->idle = 1; + + /* See if core or cluster highest frequency changed */ + assert(check_pstate_composite(datas, cpu, time) != -1); } static void cpu_pstate_running(struct cpuidle_datas *datas, int cpu, @@ -653,6 +716,9 @@ static void cpu_pstate_running(struct cpuidle_datas *datas, int cpu, ps->idle = 0; if (ps->current != -1) open_current_pstate(ps, time); + + /* See if core or cluster highest frequency changed */ + assert(check_pstate_composite(datas, cpu, time) != -1); } static int cstate_begin(double time, int state, struct cpuidle_cstates *cstates) @@ -684,12 +750,18 @@ static void cstate_end(double time, struct cpuidle_cstates *cstates) data->end = time; data->duration = data->end - data->begin; - /* That happens when precision digit in the file exceed + /* + * Duration can be < 0 when precision digit in the file exceed * 7 (eg. xxx.1000000). Ignoring the result because I don't - * find a way to fix with the sscanf used in the caller + * find a way to fix with the sscanf used in the caller. + * + * For synthetic test material, the duration may be 0. + * + * In both cases, do not record the entry, but do end the state + * regardless. */ - if (data->duration < 0) - data->duration = 0; + if (data->duration <= 0) + goto skip_entry; /* convert to us */ data->duration *= USEC_PER_SEC; @@ -717,6 +789,7 @@ static void cstate_end(double time, struct cpuidle_cstates *cstates) cstate->duration += data->duration; cstate->nrdata++; +skip_entry: /* CPU is no longer idle */ cstates->current_cstate = -1; } @@ -766,9 +839,13 @@ int store_data(double time, int state, int cpu, state = core_get_least_cstate(aff_core); if (record_cstate_event(aff_core->cstates, time, state) == -1) return -1; + aff_cluster = cpu_to_cluster(cpu, datas->topo); state = cluster_get_least_cstate(aff_cluster); - return record_cstate_event(aff_cluster->cstates, time,state); + if (record_cstate_event(aff_cluster->cstates, time,state) == -1) + return -1; + + return 0; } static void release_datas(struct cpuidle_datas *datas) diff --git a/idlestat.h b/idlestat.h index 076f6a5..966c762 100644 --- a/idlestat.h +++ b/idlestat.h @@ -191,7 +191,7 @@ struct init_pstates { extern int store_data(double time, int state, int cpu, struct cpuidle_datas *datas); extern struct cpuidle_cstates *build_cstate_info(int nrcpus); extern struct cpufreq_pstates *build_pstate_info(int nrcpus); -extern void cpu_change_pstate(struct cpuidle_datas *datas, int cpu, unsigned int freq, double time); +extern int cpu_change_pstate(struct cpuidle_datas *datas, int cpu, unsigned int freq, double time); extern int get_wakeup_irq(struct cpuidle_datas *datas, char *buffer, int count); #endif diff --git a/topology.c b/topology.c index c43536d..7e44cf2 100644 --- a/topology.c +++ b/topology.c @@ -95,6 +95,7 @@ int add_topo_info(struct cpu_topology *topo_list, struct topology_info *info) s_phy->core_num = 0; s_phy->physical_id = info->physical_id; + s_phy->pstates = build_pstate_info(1); INIT_LIST_HEAD(&s_phy->core_head); INIT_LIST_HEAD(&s_phy->cpu_enum_head); @@ -116,6 +117,7 @@ int add_topo_info(struct cpu_topology *topo_list, struct topology_info *info) s_core->cpu_num = 0; s_core->is_ht = false; s_core->core_id = info->core_id; + s_core->pstates = build_pstate_info(1); INIT_LIST_HEAD(&s_core->cpu_head); ptr = check_pos_from_head(&s_phy->core_head, s_core->core_id); @@ -206,6 +208,7 @@ void free_cpu_topology(struct list_head *head) list_for_each_entry_safe(lphysical, n, head, list_physical) { free_cpu_core_list(&lphysical->core_head); list_del(&lphysical->list_physical); + free(lphysical->pstates->pstate); free(lphysical); } } @@ -503,11 +506,13 @@ void assign_baseline_in_topo(struct cpuidle_datas *datas) struct cpu_physical, list_physical); topo_for_each_cluster(main_phy, topo) { main_phy->base_cstates = base_phy->cstates; + main_phy->base_pstates = base_phy->pstates; /* Core loop */ base_core = list_first_entry(&base_phy->core_head, struct cpu_core, list_core); cluster_for_each_core(main_core, main_phy) { main_core->base_cstates = base_core->cstates; + main_core->base_pstates = base_core->pstates; /* Cpu loop */ base_cpu = list_first_entry(&base_core->cpu_head, struct cpu_cpu, list_cpu); @@ -541,16 +546,27 @@ int dump_cpu_topo_info(struct report_ops *ops, void *report_data, int (*dump)(st sprintf(tmp, "cluster%c", s_phy->physical_id + 'A'); - if (cstate) + if (cstate) { dump(ops, s_phy->cstates, s_phy->base_cstates, tmp, report_data); + } else { + dump(ops, s_phy->pstates, s_phy->base_pstates, + tmp, report_data); + } list_for_each_entry(s_core, &s_phy->core_head, list_core) { - if (s_core->is_ht && cstate) { + if (s_core->is_ht) { sprintf(tmp, "core%d", s_core->core_id); - dump(ops, s_core->cstates, - s_core->base_cstates, - tmp, report_data); + + if (cstate) { + dump(ops, s_core->cstates, + s_core->base_cstates, + tmp, report_data); + } else { + dump(ops, s_core->pstates, + s_core->base_pstates, + tmp, report_data); + } } list_for_each_entry(s_cpu, &s_core->cpu_head, @@ -609,19 +625,21 @@ int cluster_get_highest_freq(struct cpu_physical *clust) struct cpu_cpu *cpu; int cpu_pstate_index; unsigned int cpu_freq; - unsigned int ret = ~0; + unsigned int ret = ~0U; cluster_for_each_cpu(cpu, clust) { cpu_pstate_index = cpu->pstates->current; if (cpu_pstate_index < 0) continue; + if (cpu->pstates->idle > 0) + continue; cpu_freq = cpu->pstates->pstate[cpu_pstate_index].freq; if (cpu_freq < ret) ret = cpu_freq; } /* It is possible we don't know anything near the start of trace */ - if (ret == ~0) + if (ret == ~0U) ret = 0; return ret; diff --git a/topology.h b/topology.h index 71c3422..f0cabbc 100644 --- a/topology.h +++ b/topology.h @@ -51,7 +51,9 @@ struct cpu_core { int cpu_num; bool is_ht; struct cpuidle_cstates *cstates; + struct cpufreq_pstates *pstates; struct cpuidle_cstates *base_cstates; + struct cpufreq_pstates *base_pstates; }; struct cpu_physical { @@ -61,7 +63,9 @@ struct cpu_physical { int core_num; struct list_head cpu_enum_head; struct cpuidle_cstates *cstates; + struct cpufreq_pstates *pstates; struct cpuidle_cstates *base_cstates; + struct cpufreq_pstates *base_pstates; }; struct cpu_topology { -- cgit v1.2.3