On 10/04/2014 06:33 AM, pi-cheng.chen wrote:
Initialize struct cpufreq_pstates with initial P-state of CPUs and allocate
struct cpufreq_pstate dynamically when parsing trace file to solve the issue
caused by missing "scaling_avaialable_freqs" attr when using intel_pstate
driver.

Changes v2 to v3:
Initialize struct cpufreq_pstates with initial P-state of all CPUs and
beginning timestamp before trace acquisition and close all P-states with
ending timestamp

hanges v1 to v2:
Sort the cpufreq_pstate list when parsing events

Signed-off-by: Pi-Cheng Chen <pi-cheng.c...@linaro.org>

At the first glance it looks ok for me.

Thanks
  -- Daniel

---
  idlestat.c | 259 ++++++++++++++++++++++++++++++++++++++++++++-----------------
  idlestat.h |   7 ++
  trace.h    |   1 +
  3 files changed, 195 insertions(+), 72 deletions(-)

diff --git a/idlestat.c b/idlestat.c
index da615cb..8230067 100644
--- a/idlestat.c
+++ b/idlestat.c
@@ -536,6 +536,121 @@ static struct cpuidle_cstates *build_cstate_info(int 
nrcpus)
        return cstates;
  }

+#define TRACE_STAT_FORMAT "%*[^:]:%lf"
+
+static double get_trace_ts(void)
+{
+       FILE *f;
+       double ts;
+
+       f = fopen(TRACE_STAT_FILE, "r");
+       if (!f)
+               return -1;
+
+       while (fgets(buffer, BUFSIZE, f)) {
+               if (!strstr(buffer, "now ts"))
+                       continue;
+               if (!sscanf(buffer, TRACE_STAT_FORMAT, &ts))
+                       ts = -1;
+               break;
+       }
+       fclose(f);
+
+       return ts;
+}
+
+static void release_init_pstates(struct init_pstates *init)
+{
+       free(init->freqs);
+       free(init);
+}
+
+static struct init_pstates *build_init_pstates(void)
+{
+       struct init_pstates *init;
+       int nr_cpus, cpu;
+       unsigned int *freqs;
+
+       nr_cpus = sysconf(_SC_NPROCESSORS_CONF);
+       if (nr_cpus < 0)
+               return NULL;
+
+       init = malloc(sizeof(*init));
+       if (!init)
+               return NULL;
+
+       freqs = calloc(nr_cpus, sizeof(*freqs));
+       if (!freqs) {
+               free(init);
+               return NULL;
+       }
+       memset(freqs, 0, sizeof(*freqs) * nr_cpus);
+
+       for (cpu = 0; cpu < nr_cpus; cpu++) {
+               char *fpath;
+               unsigned int *freq = &(freqs[cpu]);
+
+               if (asprintf(&fpath, CPUFREQ_CURFREQ_PATH_FORMAT, cpu) < 0) {
+                       release_init_pstates(init);
+                       return NULL;
+               }
+               if (read_int(fpath, (int *)freq))
+                       *freq = 0;
+               free(fpath);
+       }
+       init->nr_cpus = nr_cpus;
+       init->freqs = freqs;
+
+       return init;
+}
+
+/**
+ * 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;
+}
+
  /**
   * release_pstate_info - free all P-state related structs
   * @pstates: per-cpu array of P-state statistics structs
@@ -560,14 +675,16 @@ static void release_pstate_info(struct cpufreq_pstates 
*pstates, int nrcpus)
        return;
  }

-/**
- * 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
+ * @initp: initial P-state of CPUs before trace acquistion
+ * @start_ts: timestamp when trace acquisition started
   *
   * Return: per-CPU array of structs (success) or NULL (error)
   */
-static struct cpufreq_pstates *build_pstate_info(int nrcpus)
+static struct cpufreq_pstates *build_pstate_info(int nrcpus,
+                       struct init_pstates *initp, double start_ts)
  {
        int cpu;
        struct cpufreq_pstates *pstates;
@@ -577,67 +694,28 @@ static struct cpufreq_pstates *build_pstate_info(int 
nrcpus)
                return NULL;
        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;
-               }
+       if (initp)
+               assert(initp->nr_cpus == nrcpus);

-               /* 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;
+       for (cpu = 0; cpu < nrcpus; cpu++) {
+               struct cpufreq_pstates *ps = &(pstates[cpu]);
+
+               ps->pstate = NULL;
+               ps->max = 0;
+               ps->current = -1;      /* unknown */
+               ps->idle = -1;         /* unknown */
+               ps->time_enter = 0.;
+               ps->time_exit = 0.;
+
+               if (initp && initp->freqs[cpu] > 0 && start_ts > 0) {
+                       ps->current = alloc_pstate(ps, initp->freqs[cpu]);
+                       assert(ps->current >= 0);
+                       ps->time_enter = start_ts;
+                       ps->idle = 0;
                }
-
-               /* now populate cpufreq_pstates for this CPU */
-               pstates[cpu].pstate = pstate;
-               pstates[cpu].max = nrfreq;
-               pstates[cpu].current = -1;      /* unknown */
-               pstates[cpu].idle = -1;         /* unknown */
-               pstates[cpu].time_enter = 0.;
-               pstates[cpu].time_exit = 0.;
        }

        return pstates;
-
-clean_exit:
-       release_pstate_info(pstates, nrcpus);
-       return NULL;
  }

  static int get_current_pstate(struct cpuidle_datas *datas, int cpu,
@@ -712,6 +790,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:
@@ -742,6 +823,19 @@ static void cpu_change_pstate(struct cpuidle_datas *datas, 
int cpu,
        }
  }

+static void cpu_close_all_pstate(struct cpuidle_datas *datas, double time)
+{
+       struct cpufreq_pstates *ps;
+       struct cpufreq_pstate *p;
+       int cpu, cur;
+
+       for (cpu = 0; cpu < datas->nrcpus; cpu++) {
+               cur = get_current_pstate(datas, cpu, &ps, &p);
+               if (p && !cur && time > 0)
+                       close_current_pstate(ps, time);
+       }
+}
+
  static void cpu_pstate_idle(struct cpuidle_datas *datas, int cpu, double time)
  {
        struct cpufreq_pstates *ps = &(datas->pstates[cpu]);
@@ -763,7 +857,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 +919,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 +938,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;
  }
@@ -932,7 +1023,8 @@ static int get_wakeup_irq(struct cpuidle_datas *datas, 
char *buffer, int count)
        return -1;
  }

-static struct cpuidle_datas *idlestat_load(struct program_options *options)
+static struct cpuidle_datas *idlestat_load(struct program_options *options,
+                       struct init_pstates *initp, double start_ts, double 
end_ts)
  {
        FILE *f;
        unsigned int state = 0, freq = 0, cpu = 0, nrcpus = 0;
@@ -988,14 +1080,13 @@ static struct cpuidle_datas *idlestat_load(struct 
program_options *options)
                return ptrerror("build_cstate_info: out of memory");
        }

-       datas->pstates = build_pstate_info(nrcpus);
+       datas->pstates = build_pstate_info(nrcpus, initp, start_ts);
        if (!datas->pstates) {
                free(datas->cstates);
                free(datas);
                fclose(f);
                return ptrerror("build_pstate_info: out of memory");
        }
-
        datas->nrcpus = nrcpus;

        /* read topology information */
@@ -1018,7 +1109,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;
@@ -1031,6 +1121,9 @@ static struct cpuidle_datas *idlestat_load(struct 
program_options *options)

        fclose(f);

+       /* close all p-state with end timestamp */
+       cpu_close_all_pstate(datas, end_ts);
+
        fprintf(stderr, "Log is %lf secs long with %zd events\n",
                end - begin, count);

@@ -1481,7 +1574,9 @@ int main(int argc, char *argv[], char *const envp[])
  {
        struct cpuidle_datas *datas;
        struct program_options options;
+       struct init_pstates *initp = NULL;
        int args;
+       double start_ts, end_ts;

        args = getoptions(argc, argv, &options);
        if (args <= 0)
@@ -1527,6 +1622,14 @@ int main(int argc, char *argv[], char *const envp[])
                if (idlestat_flush_trace())
                        return -1;

+               /* Get current P-state of all CPUs */
+               if (options.display & FREQUENCY_DISPLAY)
+                       initp = build_init_pstates();
+
+               /* Get timestamp before trace acquisition */
+               if (options.display & FREQUENCY_DISPLAY)
+                       start_ts = get_trace_ts();
+
                /* Start the recording */
                if (idlestat_trace_enable(true))
                        return -1;
@@ -1549,6 +1652,17 @@ int main(int argc, char *argv[], char *const envp[])
                if (idlestat_trace_enable(false))
                        return -1;

+               /* Get timestamp after trace acquisition */
+               if (options.display & FREQUENCY_DISPLAY)
+                       end_ts = get_trace_ts();
+
+               /* Emit warning messages when P-state info might be partial */
+               if (options.display & FREQUENCY_DISPLAY &&
+                       (!initp || start_ts < 0 || end_ts < 0))
+                       fprintf(stderr, "Unable to get initial P-state,"
+                               " beginning timestamp, or ending timestamp!\n"
+                               "P-state statistics data might be partial!\n");
+
                /* At this point we should have some spurious wake up
                 * at the beginning of the traces and at the end (wake
                 * up all cpus and timer expiration for the timer
@@ -1559,7 +1673,7 @@ int main(int argc, char *argv[], char *const envp[])
        }

        /* Load the idle states information */
-       datas = idlestat_load(&options);
+       datas = idlestat_load(&options, initp, start_ts, end_ts);

        if (!datas)
                return 1;
@@ -1590,6 +1704,7 @@ int main(int argc, char *argv[], char *const envp[])
                }
        }

+       release_init_pstates(initp);
        release_cpu_topo_cstates();
        release_cpu_topo_info();
        release_pstate_info(datas->pstates, datas->nrcpus);
diff --git a/idlestat.h b/idlestat.h
index 735f0fe..39ba4d7 100644
--- a/idlestat.h
+++ b/idlestat.h
@@ -41,6 +41,8 @@
        "/sys/devices/system/cpu/cpu%d/cpuidle/state%d/residency"
  #define CPUFREQ_AVFREQ_PATH_FORMAT \
        "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_available_frequencies"
+#define CPUFREQ_CURFREQ_PATH_FORMAT \
+       "/sys/devices/system/cpu/cpu%d/cpufreq/cpuinfo_cur_freq"
  #define CPUIDLE_STATENAME_PATH_FORMAT \
        "/sys/devices/system/cpu/cpu%d/cpuidle/state%d/name"

@@ -63,6 +65,11 @@ struct cpuidle_cstate {
        int target_residency; /* -1 if not available */
  };

+struct init_pstates {
+       int nr_cpus;
+       unsigned int *freqs;
+};
+
  enum IRQ_TYPE {
        HARD_IRQ = 0,
        IPI_IRQ,
diff --git a/trace.h b/trace.h
index bef6703..90b4a6a 100644
--- a/trace.h
+++ b/trace.h
@@ -33,6 +33,7 @@
  #define TRACE_EVENT_PATH TRACE_PATH "/events/enable"
  #define TRACE_FREE TRACE_PATH "/free_buffer"
  #define TRACE_FILE TRACE_PATH "/trace"
+#define TRACE_STAT_FILE TRACE_PATH "/per_cpu/cpu0/stats"
  #define TRACE_IDLE_NRHITS_PER_SEC 10000
  #define TRACE_IDLE_LENGTH 196
  #define TRACE_CPUFREQ_NRHITS_PER_SEC 100



--
 <http://www.linaro.org/> Linaro.org │ Open source software for ARM SoCs

Follow Linaro:  <http://www.facebook.com/pages/Linaro> Facebook |
<http://twitter.com/#!/linaroorg> Twitter |
<http://www.linaro.org/linaro-blog/> Blog


_______________________________________________
linaro-dev mailing list
linaro-dev@lists.linaro.org
http://lists.linaro.org/mailman/listinfo/linaro-dev

Reply via email to