When using intel_pstate driver, "scaling_available_freqs" attr is not exported to sysfs. It causes assertion of idlestat due to memory of struct cpufreq_pstate was not allocated.
Allocate struct cpufreq_pstate dynamically when getting frequency information from trace file instead of parsing available frequencies from sysfs
Changes v1 to v2: Sort the cpufreq_pstate list when parsing events
Signed-off-by: Pi-Cheng Chen pi-cheng.chen@linaro.org --- idlestat.c | 118 +++++++++++++++++++++++++++++-------------------------------- 1 file changed, 56 insertions(+), 62 deletions(-)
diff --git a/idlestat.c b/idlestat.c index da615cb..d03f12e 100644 --- a/idlestat.c +++ b/idlestat.c @@ -561,8 +561,8 @@ static void release_pstate_info(struct cpufreq_pstates *pstates, int nrcpus) }
/** - * build_pstate_info - parse cpufreq sysfs entries and build per-CPU - * structs to maintain statistics of P-state transitions + * build_pstate_info - allocate and initialize per-CPU structs to + * maintain statistics of P-state transitions * @nrcpus: number of CPUs * * Return: per-CPU array of structs (success) or NULL (error) @@ -578,55 +578,8 @@ static struct cpufreq_pstates *build_pstate_info(int nrcpus) memset(pstates, 0, sizeof(*pstates) * nrcpus);
for (cpu = 0; cpu < nrcpus; cpu++) { - struct cpufreq_pstate *pstate; - int nrfreq; - char *fpath, *freq, line[256]; - FILE *sc_av_freq; - - if (asprintf(&fpath, CPUFREQ_AVFREQ_PATH_FORMAT, cpu) < 0) - goto clean_exit; - - /* read scaling_available_frequencies for the CPU */ - sc_av_freq = fopen(fpath, "r"); - free(fpath); - if (!sc_av_freq) { - fprintf(stderr, "warning: P-states not supported for " - "CPU%d\n", cpu); - continue; - } - freq = fgets(line, sizeof(line)/sizeof(line[0]), sc_av_freq); - fclose(sc_av_freq); - if (!freq) { - /* unlikely to be here, but just in case... */ - fprintf(stderr, "warning: P-state info not found for " - "CPU%d\n", cpu); - continue; - } - - /* tokenize line and populate each frequency */ - nrfreq = 0; - pstate = NULL; - while ((freq = strtok(freq, "\n ")) != NULL) { - struct cpufreq_pstate *tmp = realloc(pstate, sizeof(*pstate) * (nrfreq+1)); - if (!tmp) - goto clean_exit; - pstate = tmp; - - /* initialize pstate record */ - pstate[nrfreq].id = nrfreq; - pstate[nrfreq].freq = atol(freq); - pstate[nrfreq].count = 0; - pstate[nrfreq].min_time = DBL_MAX; - pstate[nrfreq].max_time = 0.; - pstate[nrfreq].avg_time = 0.; - pstate[nrfreq].duration = 0.; - nrfreq++; - freq = NULL; - } - - /* now populate cpufreq_pstates for this CPU */ - pstates[cpu].pstate = pstate; - pstates[cpu].max = nrfreq; + pstates[cpu].pstate = NULL; + pstates[cpu].max = 0; pstates[cpu].current = -1; /* unknown */ pstates[cpu].idle = -1; /* unknown */ pstates[cpu].time_enter = 0.; @@ -634,10 +587,52 @@ static struct cpufreq_pstates *build_pstate_info(int nrcpus) }
return pstates; +}
-clean_exit: - release_pstate_info(pstates, nrcpus); - return NULL; +/** + * alloc_pstate - allocate, sort, and initialize pstate struct + * to maintain statistics of P-state transitions + * @pstates: per-CPU P-state statistics struct + * @freq: frequency for which the newly pstate is allocated + * + * Return: the index of the newly allocated pstate struct + */ +static int alloc_pstate(struct cpufreq_pstates *pstates, unsigned int freq) +{ + struct cpufreq_pstate *pstate, *tmp; + int nrfreq, i, next = 0; + + pstate = pstates->pstate; + nrfreq = pstates->max; + + tmp = realloc(pstate, sizeof(*pstate) * (nrfreq + 1)); + if (!tmp) { + perror("realloc pstate"); + return -1; + } + pstate = tmp; + pstates->pstate = tmp; + pstates->max = nrfreq + 1; + + for (i = 0; i < nrfreq && freq <= pstate[i].freq; i++) + ; + + next = i; + for (i = nrfreq; i > next && i > 0; i--) { + pstate[i] = pstate[i - 1]; + pstate[i].id = i; + pstates->current = (pstates->current == i - 1)? i : pstates->current; + } + + pstate[next].id = next; + pstate[next].freq = freq; + pstate[next].count = 0; + pstate[next].min_time = DBL_MAX; + pstate[next].max_time = 0; + pstate[next].avg_time = 0; + pstate[next].duration = 0; + + return next; }
static int get_current_pstate(struct cpuidle_datas *datas, int cpu, @@ -712,6 +707,9 @@ static void cpu_change_pstate(struct cpuidle_datas *datas, int cpu,
cur = get_current_pstate(datas, cpu, &ps, &p); next = freq_to_pstate_index(ps, freq); + if (next < 0) + next = alloc_pstate(ps, freq); + assert(next >= 0);
switch (cur) { case 1: @@ -763,7 +761,6 @@ static int store_data(double time, int state, int cpu, struct cpuidle_datas *datas, int count) { struct cpuidle_cstates *cstates = &datas->cstates[cpu]; - struct cpufreq_pstate *pstate = datas->pstates[cpu].pstate; struct cpuidle_cstate *cstate; struct cpuidle_data *data, *tmp; int nrdata, last_cstate = cstates->last_cstate; @@ -826,9 +823,8 @@ static int store_data(double time, int state, int cpu, /* need indication if CPU is idle or not */ cstates->last_cstate = -1;
- /* update P-state stats if supported */ - if (pstate) - cpu_pstate_running(datas, cpu, time); + /* update P-state stats */ + cpu_pstate_running(datas, cpu, time);
return 0; } @@ -846,9 +842,8 @@ static int store_data(double time, int state, int cpu, cstates->cstate_max = MAX(cstates->cstate_max, state); cstates->last_cstate = state; cstates->wakeirq = NULL; - /* update P-state stats if supported */ - if (pstate) - cpu_pstate_idle(datas, cpu, time); + /* update P-state stats*/ + cpu_pstate_idle(datas, cpu, time);
return 0; } @@ -1018,7 +1013,6 @@ static struct cpuidle_datas *idlestat_load(struct program_options *options) } else if (strstr(buffer, "cpu_frequency")) { assert(sscanf(buffer, TRACE_FORMAT, &time, &freq, &cpu) == 3); - assert(datas->pstates[cpu].pstate != NULL); cpu_change_pstate(datas, cpu, freq, time); count++; continue;