pass: Add new helper function to handle the h/w resource through hal interface
authorChanwoo Choi <cw00.choi@samsung.com>
Mon, 23 Jan 2017 10:22:24 +0000 (19:22 +0900)
committerChanwoo Choi <cw00.choi@samsung.com>
Thu, 2 Feb 2017 23:43:18 +0000 (08:43 +0900)
This patch adds new helper and wrapper function to handle the hal
interface. The pass core have to call this function to control
the value of each h/w resource.

- Load the h/w resource:
int pass_get_resource(struct pass *pass);

- Following functions to handle the DVFS resource:
int pass_get_curr_governor(struct pass_resource *res, char *governor);
int pass_set_curr_governor(struct pass_resource *res, char *governor);
int pass_get_curr_freq(struct pass_resource *res);
int pass_get_min_freq(struct pass_resource *res);
int pass_set_min_freq(struct pass_resource *res, int freq);
int pass_get_max_freq(struct pass_resource *res);
int pass_set_max_freq(struct pass_resource *res, int freq);
int pass_get_up_threshold(struct pass_resource *res);
int pass_set_up_threshold(struct pass_resource *res, int up_threshold);

- Following functions to handle the CPU Hotpulg resource:
int pass_get_online_state(struct pass_resource *res, int cpu);
int pass_set_online_state(struct pass_resource *res, int cpu, int on);

- Following functions to handle the TMU resource:
int pass_get_temp(struct pass_resource *res);
int pass_get_policy(struct pass_resource *res, char *policy);

* DVFS (Dynamic Voltage & Frequency Scaling)
* TMU (Thermal Management Unit)

Change-Id: I03cbc7dc3debc787d4f4a5cb777db9f7e4bd8f3f
Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
CMakeLists.txt
src/pass/pass-hal.c [new file with mode: 0644]
src/pass/pass-hal.h [new file with mode: 0644]
src/pass/pass.h

index 14da2fafc72e2cda9f41ba92ce4711a8b3d5391a..668b86794ea99307e8d6681a75a07e5b7b606537 100644 (file)
@@ -67,6 +67,7 @@ SET(SRCS
        src/pass/pass-gov-step.c
        src/pass/pass-parser.c
        src/pass/pass-plugin.c
+       src/pass/pass-hal.c
        src/pass/pass-pmqos.c
        src/pmqos/pmqos.c
        src/pmqos/pmqos-parser.c
diff --git a/src/pass/pass-hal.c b/src/pass/pass-hal.c
new file mode 100644 (file)
index 0000000..adc213b
--- /dev/null
@@ -0,0 +1,520 @@
+/*
+ * PASS (Power Aware System Service)
+ *
+ * Copyright (c) 2012 - 2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include "pass.h"
+#include "hal/hal.h"
+#include "pass-hal.h"
+
+#include "core/common.h"
+
+static struct pass_resource_dvfs_ops *get_dvfs(struct pass_resource *res, int id)
+{
+       struct pass_resource_dvfs_ops *dvfs = NULL;
+
+       switch (id) {
+       case PASS_RESOURCE_CPU_ID:
+               dvfs = &(res->hw.cpu->dvfs);
+               break;
+       case PASS_RESOURCE_BUS_ID:
+               dvfs = &(res->hw.bus->dvfs);
+               break;
+       case PASS_RESOURCE_GPU_ID:
+               dvfs = &(res->hw.gpu->dvfs);
+               break;
+       }
+
+       return dvfs;
+}
+
+static struct pass_resource_tmu_ops *get_tmu(struct pass_resource *res, int id)
+{
+       struct pass_resource_tmu_ops *tmu = NULL;
+
+       switch (id) {
+       case PASS_RESOURCE_CPU_ID:
+               tmu = &(res->hw.cpu->tmu);
+               break;
+       case PASS_RESOURCE_BUS_ID:
+               tmu = &(res->hw.bus->tmu);
+               break;
+       case PASS_RESOURCE_GPU_ID:
+               tmu = &(res->hw.gpu->tmu);
+               break;
+       }
+
+       return tmu;
+}
+
+static struct pass_resource_hotplug_ops *get_hotplug(struct pass_resource *res,
+                                               int id)
+{
+       struct pass_resource_hotplug_ops *hotplug = NULL;
+
+       switch (id) {
+       case PASS_RESOURCE_CPU_ID:
+               hotplug = &(res->hw.cpu->hotplug);
+               break;
+       case PASS_RESOURCE_BUS_ID:
+       case PASS_RESOURCE_GPU_ID:
+               hotplug = NULL;
+               break;
+       }
+
+       return hotplug;
+}
+
+/* Get and set the current governor for DVFS resource. */
+int pass_get_curr_governor(struct pass_resource *res, char *governor)
+{
+       struct pass_resource_dvfs_ops *dvfs;
+       char *res_name;
+       int id;
+
+       if (!res || !governor)
+               return -EINVAL;
+
+       res_name = res->cdata.res_name;
+       id = res->cdata.res_type;
+
+       dvfs = get_dvfs(res, id);
+       if (!dvfs)
+               return -EINVAL;
+
+       if (!dvfs->get_curr_governor || !res_name)
+               return -EINVAL;
+
+       return dvfs->get_curr_governor(res_name, governor);
+}
+
+int pass_set_curr_governor(struct pass_resource *res, char *governor)
+{
+       struct pass_resource_dvfs_ops *dvfs;
+       char *res_name;
+       int id;
+
+       if (!res)
+               return -EINVAL;
+
+       res_name = res->cdata.res_name;
+       id = res->cdata.res_type;
+
+       dvfs = get_dvfs(res, id);
+       if (!dvfs)
+               return -EINVAL;
+
+       if (!dvfs->set_curr_governor || !res_name)
+               return -EINVAL;
+
+       return dvfs->set_curr_governor(res_name, governor);
+}
+
+/* Get and set the current/min/max frequency for DVFS resource. */
+int pass_get_curr_freq(struct pass_resource *res)
+{
+       struct pass_resource_dvfs_ops *dvfs;
+       char *res_name;
+       int id;
+
+       if (!res)
+               return -EINVAL;
+
+       res_name = res->cdata.res_name;
+       id = res->cdata.res_type;
+
+       dvfs = get_dvfs(res, id);
+       if (!dvfs)
+               return -EINVAL;
+
+       if (!dvfs->get_curr_freq || !res_name)
+               return -EINVAL;
+
+       return dvfs->get_curr_freq(res_name);
+}
+
+
+int pass_get_min_freq(struct pass_resource *res)
+{
+       struct pass_resource_dvfs_ops *dvfs;
+       char *res_name;
+       int id;
+
+       if (!res)
+               return -EINVAL;
+
+       res_name = res->cdata.res_name;
+       id = res->cdata.res_type;
+
+       dvfs = get_dvfs(res, id);
+       if (!dvfs)
+               return -EINVAL;
+
+       if (!dvfs->get_min_freq || !res_name)
+               return -EINVAL;
+
+       return dvfs->get_min_freq(res_name);
+}
+
+int pass_set_min_freq(struct pass_resource *res, int freq)
+{
+       struct pass_resource_dvfs_ops *dvfs;
+       char *res_name;
+       int id;
+
+       if (!res || freq <= 0)
+               return -EINVAL;
+
+       res_name = res->cdata.res_name;
+       id = res->cdata.res_type;
+
+       dvfs = get_dvfs(res, id);
+       if (!dvfs)
+               return -EINVAL;
+
+       if (!dvfs->set_min_freq || !res_name)
+               return -EINVAL;
+
+       return dvfs->set_min_freq(res_name, freq);
+}
+
+int pass_get_max_freq(struct pass_resource *res)
+{
+       struct pass_resource_dvfs_ops *dvfs;
+       char *res_name;
+       int id;
+
+       if (!res)
+               return -EINVAL;
+
+       res_name = res->cdata.res_name;
+       id = res->cdata.res_type;
+
+       dvfs = get_dvfs(res, id);
+       if (!dvfs)
+               return -EINVAL;
+
+       if (!dvfs->get_max_freq || !res_name)
+               return -EINVAL;
+
+       return dvfs->get_max_freq(res_name);
+}
+
+int pass_set_max_freq(struct pass_resource *res, int freq)
+{
+       struct pass_resource_dvfs_ops *dvfs;
+       char *res_name;
+       int id;
+
+       if (!res || freq <= 0)
+               return -EINVAL;
+
+       res_name = res->cdata.res_name;
+       id = res->cdata.res_type;
+
+       dvfs = get_dvfs(res, id);
+       if (!dvfs)
+               return -EINVAL;
+
+       if (!dvfs->set_max_freq || !res_name)
+               return -EINVAL;
+
+       return dvfs->set_max_freq(res_name, freq);
+}
+
+/* Get and set the up_threshold for DVFS resource. */
+int pass_get_up_threshold(struct pass_resource *res)
+{
+       struct pass_resource_dvfs_ops *dvfs;
+       char *res_name;
+       int id;
+
+       if (!res)
+               return -EINVAL;
+
+       res_name = res->cdata.res_name;
+       id = res->cdata.res_type;
+
+       dvfs = get_dvfs(res, id);
+       if (!dvfs)
+               return -EINVAL;
+
+       if (!dvfs->get_up_threshold || !res_name)
+               return -EINVAL;
+
+       return dvfs->get_up_threshold(res_name);
+}
+
+int pass_set_up_threshold(struct pass_resource *res, int up_threshold)
+{
+       struct pass_resource_dvfs_ops *dvfs;
+       char *res_name;
+       int id;
+
+       if (!res || up_threshold <= 0)
+               return -EINVAL;
+
+       res_name = res->cdata.res_name;
+       id = res->cdata.res_type;
+
+       dvfs = get_dvfs(res, id);
+       if (!dvfs)
+               return -EINVAL;
+
+       if (!dvfs->set_up_threshold || !res_name)
+               return -EINVAL;
+
+       return dvfs->set_up_threshold(res_name, up_threshold);
+}
+
+/* Get and set the online state of HOTPLUG resource (e.g., CPU). */
+int pass_get_online_state(struct pass_resource *res, int cpu)
+{
+       struct pass_resource_hotplug_ops *hotplug;
+       char *res_name;
+       int id;
+
+       if (!res)
+               return -EINVAL;
+
+       res_name = res->cdata.res_name;
+       id = res->cdata.res_type;
+
+       hotplug = get_hotplug(res, id);
+       if (!hotplug)
+               return -EINVAL;
+
+       if (!hotplug->get_online_state || !res_name)
+               return -EINVAL;
+
+       return hotplug->get_online_state(res_name, cpu);
+}
+
+int pass_set_online_state(struct pass_resource *res, int cpu, int on)
+{
+       struct pass_resource_hotplug_ops *hotplug;
+       char *res_name;
+       int id;
+
+       if (!res || on < 0)
+               return -EINVAL;
+
+       res_name = res->cdata.res_name;
+       id = res->cdata.res_type;
+
+       hotplug = get_hotplug(res, id);
+       if (!hotplug)
+               return -EINVAL;
+
+       if (!hotplug->set_online_state || !res_name)
+               return -EINVAL;
+
+       return hotplug->set_online_state(res_name, cpu, on);
+}
+
+/* Get the temperature and thermal policy for Thermal resource. */
+int pass_get_temp(struct pass_resource *res)
+{
+       struct pass_resource_tmu_ops *tmu;
+       char *res_name;
+       int id;
+
+       if (!res)
+               return -EINVAL;
+
+       res_name = res->cdata.res_name;
+       id = res->cdata.res_type;
+
+       tmu = get_tmu(res, id);
+       if (!tmu)
+               return -EINVAL;
+
+       if (!tmu->get_temp || !res_name)
+               return -EINVAL;
+
+       return tmu->get_temp(res_name);
+}
+
+int pass_get_policy(struct pass_resource *res, char *policy)
+{
+       struct pass_resource_tmu_ops *tmu;
+       char *res_name;
+       int id;
+
+       if (!res)
+               return -EINVAL;
+
+       res_name = res->cdata.res_name;
+       id = res->cdata.res_type;
+
+       tmu = get_tmu(res, id);
+       if (!tmu)
+               return -EINVAL;
+
+       if (!tmu->get_policy || !res_name)
+               return -EINVAL;
+
+       return tmu->get_policy(res_name, policy);
+}
+
+int pass_get_resource(struct pass *pass)
+{
+       struct pass_resource_info *info;
+       int i, ret;
+
+       for (i = 0; i < pass->num_resources; i++) {
+               struct pass_resource *pass_res = &pass->res[i];
+               struct pass_conf_data *cdata = &pass_res->cdata;
+               int res_type = cdata->res_type;
+               char name[10];
+
+               switch (res_type) {
+               case PASS_RESOURCE_CPU_ID:
+                       strncpy(name, PASS_RESOURCE_CPU_NAME,
+                                       strlen(PASS_RESOURCE_CPU_NAME));
+                       break;
+               case PASS_RESOURCE_BUS_ID:
+                       strncpy(name, PASS_RESOURCE_BUS_NAME,
+                                       strlen(PASS_RESOURCE_BUS_NAME));
+                       break;
+               case PASS_RESOURCE_GPU_ID:
+                       strncpy(name, PASS_RESOURCE_GPU_NAME,
+                                       strlen(PASS_RESOURCE_GPU_NAME));
+                       break;
+               default:
+                       _E("Unsupported resource type (type: %d)\n", res_type);
+                       return -EINVAL;
+               };
+
+               ret = pass_get_hw_info(name,
+                               (const struct pass_resource_info **)&info);
+               if (ret < 0) {
+                       _E("Failed to get h/w info\n");
+                       return -EINVAL;
+               }
+
+               if (!info->open || !info->close) {
+                       _E("Failed to open %s h/w device\n", name);
+                       return -EPERM;
+               }
+
+               switch (res_type) {
+               case PASS_RESOURCE_CPU_ID:
+                       ret = info->open(info,
+                               (struct pass_resource_common**)&pass_res->hw.cpu);
+                       break;
+               case PASS_RESOURCE_BUS_ID:
+                       ret = info->open(info,
+                               (struct pass_resource_common**)&pass_res->hw.bus);
+                       break;
+               case PASS_RESOURCE_GPU_ID:
+                       ret = info->open(info,
+                               (struct pass_resource_common**)&pass_res->hw.gpu);
+                       break;
+               };
+
+               if (ret < 0) {
+                       _E("Failed to open %s h/w resource\n", name);
+                       return -EINVAL;
+               }
+
+               _D("Complete the loading for %s h/w resource\n", name);
+       }
+
+       return 0;
+}
+
+/*
+ * FXIME: Following function is not standard interface.
+ * Following functions will be altered by the standard interface on later.
+ *
+ * - int pass_get_num_cpus(void)
+ * - int pass_get_cpu_stats(struct pass_policy *policy)
+ * - int64_t pass_get_time_ms(void)
+ */
+int pass_get_num_cpus(void)
+{
+       int num_cpus;
+       int ret;
+
+       ret = sys_get_int("/sys/devices/system/cpu/kernel_max", &num_cpus);
+       if (ret < 0)
+               return -EAGAIN;
+
+       /*
+        * Return the number of cpus with plus 1 because of index
+        * of first CPU is 0 (zero). (e.g., Octa cores have the '7' value
+        * of "/sys/devices/system/cpu/kernel_max").
+        */
+       return (num_cpus + 1);
+}
+
+/* Get the load_table of each resource to estimate the system load. */
+#define BUFF_MAX       255
+int pass_get_cpu_stats(struct pass_policy *policy)
+{
+       struct pass_cpu_stats *stats = policy->pass_cpu_stats;
+       char str[BUFF_MAX];
+       FILE *fp_stats = NULL;
+       int i, j, ret;
+
+       if (!policy || !policy->cluster.path_load_table || !stats) {
+               _E("invalid parameter of structure pass_cpu_stats");
+               return -EINVAL;
+       }
+
+       fp_stats = fopen(policy->cluster.path_load_table, "r");
+       if (fp_stats == NULL)
+               return -EIO;
+
+       /* Read the title and drop the buffer because it is not used */
+       if (!fgets(str, BUFF_MAX, fp_stats))
+               return -EIO;
+
+       for (i = 0; i < policy->num_pass_cpu_stats; i++) {
+               ret = fscanf(fp_stats, "%lld %d %d %d",
+                       &stats[i].time,
+                       &stats[i].freq,
+                       &stats[i].freq_new,
+                       &stats[i].nr_runnings);
+               if (ret < 0)
+                       return -EIO;
+
+               for (j = 0; j < policy->cpufreq.num_nr_cpus; j++) {
+                       ret = fscanf(fp_stats, "%d", &stats[i].load[j]);
+                       if (ret < 0)
+                               return -EIO;
+               }
+       }
+       fclose(fp_stats);
+
+       return 0;
+}
+
+/* Return current time (unit: millisecond) */
+int64_t pass_get_time_ms(void)
+{
+       struct timeval now;
+
+       gettimeofday(&now, NULL);
+
+       return (int64_t)(now.tv_sec * 1000 + now.tv_usec / 1000);
+}
diff --git a/src/pass/pass-hal.h b/src/pass/pass-hal.h
new file mode 100644 (file)
index 0000000..3ac2a5c
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * PASS (Power Aware System Service)
+ *
+ * Copyright (c) 2012 - 2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef __PASS_HAL__
+#define __PASS_HAL__
+
+#include "hal/hal.h"
+
+/* Get and the current governor. */
+int pass_get_curr_governor(struct pass_resource *res, char *governor);
+int pass_set_curr_governor(struct pass_resource *res, char *governor);
+
+/* Get the current frequency. */
+int pass_get_curr_freq(struct pass_resource *res);
+
+/* Get and set the minimum frequency. */
+int pass_get_min_freq(struct pass_resource *res);
+int pass_set_min_freq(struct pass_resource *res, int freq);
+
+/* Get and set the maximum frequency. */
+int pass_get_max_freq(struct pass_resource *res);
+int pass_set_max_freq(struct pass_resource *res, int freq);
+
+/* Get and set the up_threshold to support boosting. */
+int pass_get_up_threshold(struct pass_resource *res);
+int pass_set_up_threshold(struct pass_resource *res, int up_threshold);
+
+/* Get and set online state of cpu. */
+int pass_get_online_state(struct pass_resource *res, int cpu);
+int pass_set_online_state(struct pass_resource *res, int cpu, int on);
+
+/* Get the temperature and policy of thermal unit on specific h/w. */
+int pass_get_temp(struct pass_resource *res);
+int pass_get_policy(struct pass_resource *res, char *policy);
+
+/* Load the h/w resource. */
+int pass_get_resource(struct pass *pass);
+
+/*
+ * FXIME: Following function is not standard interface.
+ * These functions will be altered by the standard interface on later.
+ */
+int pass_get_num_cpus(void);
+int pass_get_cpu_stats(struct pass_policy *policy);
+int64_t pass_get_time_ms(void);
+
+#endif /* __PASS_HAL__ */
index 39c0ee31bf2e782794410df4ea283884535d353f..2fb755c3c24e092f9d33bdef16e91ac672d0687f 100644 (file)
@@ -264,4 +264,73 @@ struct pass_policy {
 
        struct pass_cluster_data cluster;
 };
+
+/******************************************************
+ *                   PASS resource                    *
+ ******************************************************/
+/*
+ * struct pass_conf_data - Parsed data for each h/w resource from
+ *                             configuration file (pass.conf).
+ *
+ * Mandatory:
+ * @res_type: the unique id for each h/w resource. The id can have
+ *     the one value among following defined resource id.
+ *     - 1: PASS_RESOURCE_CPU_ID
+ *     - 2: PASS_RESOURCE_BUS_ID
+ *     - 3: PASS_RESOURCE_GPU_ID
+ * @res_name: the unique name of sysfs entry.
+ *     - In case of /sys/devices/system/cpu/cpu0, res_name is 'cpu0'
+ *     - In case of /sys/devices/system/cpu/cpu4, res_name is 'cpu4'
+ *     - In case of /sys/class/devfreq/devfreq3, res_name is 'devfreq3'
+ *     - In case of /sys/class/devfreq/mali.11400000, res_name is 'mali.11400000'
+ * @path_conf_file: the path for PASS configuration file
+ * @path_load_table: the path for load_table entry from kernel
+ *
+ * Optional:
+ * @num_cpus: the number of supported cpus in the same cluster.
+ * @cpu: the index of first cpu in the same cluster.
+ */
+struct pass_conf_data {
+       unsigned int res_type;
+       char res_name[128];
+       char path_conf_file[128];
+       char path_load_table[128];
+
+       unsigned int num_cpus;
+       unsigned int cpu;
+};
+
+/*
+ * struct pass_resource - Represent the each h/w resource.
+ *
+ * @cdata: the parsed data from configuration file (pass.conf).
+ * @hw:        the resource instance of each h/w resource from pass_get_hw_info().
+ *     - If res_type of cdata is PASS_RESOURCE_CPU_ID, hw.cpu will be used.
+ *     - If res_type of cdata is PASS_RESOURCE_BUS_ID, hw.bus will be used.
+ *     - If res_type of cdata is PASS_RESOURCE_GPU_ID, hw.gpu will be used.
+ * @policy: the policy data to handle the h/w resource
+ */
+struct pass_resource {
+       struct pass_conf_data cdata;
+       struct pass_policy policy;
+
+       union {
+               struct pass_resource_cpu *cpu;
+               struct pass_resource_bus *bus;
+               struct pass_resource_gpu *gpu;
+       } hw;
+};
+
+struct pass {
+       unsigned int num_resources;
+       struct pass_resource *res;
+};
+
+#define container_of(ptr, type, member) ({                     \
+       const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
+       (type *)( (char *)__mptr - offsetof(type,member) );})
+
+#define to_pass_resource(policy)       \
+       container_of(policy, struct pass_resource, policy)
+
 #endif /* __pass__ */