From 204e36c7c18aa76dfd5c0746993968dfd6f57518 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Fri, 2 Feb 2018 14:14:28 +0900 Subject: [PATCH 01/16] pass: resmon: Add new Resource Monitor (resmon) module PASS required the resource monitoring in order to decide the current system status. Even if it was required, PASS had not supported the separate resource monitor module. Add Resource Monitor (resmon) module to support the resource monitoring. Other modules in PASS are able to use resmon in order to monitor/collect the resource data such as frequency, utilization, file/memory's usage and so on. Resource-Monitor provides two monitoring method as following: - Timer-based resource monitor like polling method. There are both oneshot and periodic timer and it requires the interval as the mandatory. - Uevent-based resource monitor like interrupt method. It notifies the result of monitoring when receiving the uevent. But, uevent-based resource monitor will be implemented on later. [Function description] - Timer-based resource monitor int pass_resmon_update_timer_interval() int pass_resmon_register_timer() int pass_resmon_unregister_timer() Change-Id: I4ec40f570fa0083a0fc22754174e63033cd99836 Signed-off-by: Chanwoo Choi --- CMakeLists.txt | 1 + include/pass/common.h | 3 + src/pass/pass-resmon-internal.h | 55 ++++++ src/pass/pass-resmon.c | 364 ++++++++++++++++++++++++++++++++++++++++ src/pass/pass-resmon.h | 83 +++++++++ src/pass/pass.c | 25 ++- src/pass/pass.h | 18 ++ 7 files changed, 545 insertions(+), 4 deletions(-) create mode 100644 src/pass/pass-resmon-internal.h create mode 100644 src/pass/pass-resmon.c create mode 100644 src/pass/pass-resmon.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 6a40a6a..c13750f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -69,6 +69,7 @@ SET(SRCS src/pass/pass-parser.c src/pass/pass-hal.c src/pass/pass-rescon.c + src/pass/pass-resmon.c src/pass/pass-pmqos.c src/pmqos/pmqos.c src/pmqos/pmqos-parser.c diff --git a/include/pass/common.h b/include/pass/common.h index 46c7490..9de28a3 100644 --- a/include/pass/common.h +++ b/include/pass/common.h @@ -26,6 +26,9 @@ #include #include +typedef unsigned int uint32; +typedef unsigned long long uint64; + #define ARRAY_SIZE(name) (sizeof(name)/sizeof(name[0])) /* diff --git a/src/pass/pass-resmon-internal.h b/src/pass/pass-resmon-internal.h new file mode 100644 index 0000000..659f6c5 --- /dev/null +++ b/src/pass/pass-resmon-internal.h @@ -0,0 +1,55 @@ +/* + * PASS (Power Aware System Service) Internal hearder file for only Resource Monitor + * + * Copyright (c) 2018 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_RESMON_INTERNAL__ +#define __PASS_RESMON_INTERNAL__ + +struct resmon; + +struct resmon_ops { + int (*init)(struct resmon *monitor); + int (*exit)(struct resmon *monitor); + + /* Callback of timer-based resource monitor */ + int (*timer_handler)(struct resmon *monitor, void *result); +}; + +struct resmon { + struct pass_resmon *resmon; + + /* Resource Monitor's source type */ + enum resmon_src_type src_type; + + /* + * User callback function when following event happen: + * - In case of timer-baed resmon, the timer is expired. + * - In case of uevent-baed resmon, receive the required uevent. + */ + int (*user_func)(void *result, void *user_data); + void *user_data; + void *result; + + struct resmon_ops *ops; + + /* Private data of timer-based resource-monitor */ + enum resmon_timer_type timer_type; + unsigned int timer_interval; + guint timer_id; +}; + +#endif /* __PASS_RESMON_INTERNAL__ */ diff --git a/src/pass/pass-resmon.c b/src/pass/pass-resmon.c new file mode 100644 index 0000000..702b1de --- /dev/null +++ b/src/pass/pass-resmon.c @@ -0,0 +1,364 @@ +/* + * PASS (Power Aware System Service) Resource Monitor + * + * Copyright (c) 2018 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 +#include + +#include "pass.h" +#include "pass-hal.h" +#include "pass-resmon.h" +#include "pass-resmon-internal.h" + +uint64 available_resmon_timer[] = { + [PASS_RESOURCE_UNKNOWN] = 0, + [PASS_RESOURCE_CPU_ID] = RESMON_SRC_THERMAL + | RESMON_SRC_CPUHP, + [PASS_RESOURCE_BUS_ID] = 0, + [PASS_RESOURCE_GPU_ID] = RESMON_SRC_THERMAL, + [PASS_RESOURCE_MEMORY_ID] = 0, + [PASS_RESOURCE_NONSTANDARD_ID] = 0 +}; + +static bool resmon_is_supported(unsigned int res_type, + enum resmon_src_type src_type) +{ + return !!(available_resmon_timer[res_type] & src_type); +} + +static bool resmon_is_created(struct pass_resmon *resmon, + enum resmon_src_type src_type) +{ + return !!(resmon->timer_state & src_type); +} + +static gint __compare_monitor_type(gconstpointer data, gconstpointer input) +{ + struct resmon *monitor = (struct resmon *)data; + enum resmon_src_type *src_type = (enum resmon_src_type *)input; + + if (monitor->src_type == *src_type) + return 0; + return -1; +} + +static struct resmon *resmon_find_monitor(struct pass_resmon *resmon, + enum resmon_src_type src_type) +{ + GList *node = g_list_find_custom(resmon->timer_list, &src_type, + __compare_monitor_type); + if (!node) + return NULL; + return (struct resmon *)node->data; +} + +static gboolean resmon_timer_func(gpointer data); +static int resmon_timer_add(struct resmon *monitor, unsigned int interval) +{ + struct pass_resmon *resmon = monitor->resmon; + int ret; + + /* Add new timer-based resmon to timer_list if it's not created */ + if (!resmon_is_created(resmon, monitor->src_type)) { + resmon->timer_list = + g_list_append(resmon->timer_list, + (gpointer)monitor); + resmon->timer_state |= monitor->src_type; + + if (monitor->ops && monitor->ops->init) { + ret = monitor->ops->init(monitor); + if (ret < 0) + return ret; + } + /* + * Remove the registered timer-based resmon in order to update interval + * of timer-based resmon if it's already created. + */ + } else { + g_source_remove(monitor->timer_id); + } + + /* Add timer-based resmon with new interval to update the interval */ + monitor->timer_id = g_timeout_add((guint)interval, + (GSourceFunc)resmon_timer_func, + (gpointer)monitor); + if (!monitor->timer_id) { + resmon->timer_list = g_list_remove(resmon->timer_list, + (gpointer)monitor); + resmon->timer_state &= ~monitor->src_type; + return -EPERM; + } + + return 0; +} + +static int resmon_timer_delete(struct resmon *monitor) +{ + struct pass_resmon *resmon = monitor->resmon; + int ret; + + g_source_remove(monitor->timer_id); + + if (monitor->ops && monitor->ops->exit) { + ret = monitor->ops->exit(monitor); + if (ret < 0) + return ret; + } + + resmon->timer_state &= ~(monitor->src_type); + resmon->timer_list = g_list_remove(resmon->timer_list, + (gpointer)monitor); + + return 0; +} + +static gboolean resmon_timer_func(gpointer data) +{ + struct resmon *monitor = data; + struct pass_resmon *resmon = monitor->resmon; + struct pass_resource *res + = container_of(resmon, struct pass_resource, resmon);; + int ret = 0; + + if (!monitor->result) + return false; + + /* Collect resource data according to enum resmon_src_type */ + if (monitor->ops && monitor->ops->timer_handler) { + ret = monitor->ops->timer_handler(monitor, monitor->result); + if (ret < 0) { + _E("failed to invoke timer_handler " \ + "(res_name:%s, src_type: 0x%x)\n", + res->config_data.res_name, monitor->src_type); + return false; + } + } + + /* Pass collected resource data (result) to resmon's user */ + if (monitor->user_func) { + ret = (monitor->user_func)(monitor->result, monitor->user_data); + if (ret < 0) { + _E("failed to invoke user_func " \ + "(res_name:%s, src_type: 0x%x)\n", + res->config_data.res_name, monitor->src_type); + return false; + } + } + + /* If monitor has oneshot mode, delete the timer-based resmon */ + switch (monitor->timer_type) { + case RESMON_TIMER_ONESHOT: + ret = resmon_timer_delete(monitor); + if (ret < 0) { + _E("failed to delete timer " \ + "(res_name:%s, src_type: 0x%x)\n", + res->config_data.res_name, monitor->src_type); + return false; + } + + free(monitor); + monitor = NULL; + break; + case RESMON_TIMER_PERIODIC: + break; + default: + return false; + } + + return true; +} + +/* + * pass_resmon_register_timer - Register timer-based resource monitor + * + * @res: the instance of struct pass_resource. + * @src_type: the type of resource monitor among 'enum resmon_src_type'. + * @timer_type: the type of timer among 'enum resmon_timer_type'. + * @timer_interval: the interval of timer (unit: millisecond). + * @user_func: the callback function when timer is expired. + * @user_data: the passed user user_data. + */ +int pass_resmon_register_timer(struct pass_resource *res, + enum resmon_src_type src_type, + enum resmon_timer_type timer_type, + unsigned int timer_interval, + int (*user_func)(void *result, void *user_data), + void *user_data) +{ + struct pass_resmon *resmon; + struct resmon *monitor; + int res_type; + int ret; + + if (!res || src_type == 0 || timer_type == 0 || user_func == NULL) + return -EINVAL; + + resmon = &res->resmon; + + /* Check whether the resource monitor is supported or not */ + res_type = res->config_data.res_type; + if (!resmon_is_supported(res_type, src_type)) { + _E("invalid value (res_type: %d, src_type: 0x%x)\n", + res_type, src_type); + return -EINVAL; + } + + /* Prevent the monitoring of already required type */ + if (resmon_is_created(resmon, src_type)) { + _E("timer is already created (res_type: %d, src_type: 0x%x)\n", + res_type, src_type); + return -EBUSY; + } + + /* Allocate the memory for resource monitor */ + monitor = calloc(1, sizeof(struct resmon)); + if (!monitor) + return -ENOMEM; + + monitor->resmon = resmon; + monitor->src_type = src_type; + monitor->timer_type = timer_type; + monitor->timer_interval = timer_interval; + monitor->user_func = user_func; + monitor->user_data = user_data; + + /* TODO: Get instance of struct resmon_ops accoring to resmon_src_type */ + + /* Add timer-based resource monitor */ + ret = resmon_timer_add(monitor, timer_interval); + if (ret < 0) { + _E("failed to add monitor (res_type: %d, src_type: 0x%x)\n", + res_type, src_type); + goto err; + } + + return 0; + +err: + free(monitor); + monitor = NULL; + + return ret; +} + +/* + * pass_resmon_unregister_timer - Unregister timer-based resource monitor + * + * @res: the instance of struct pass_resource + * @src_type: the type of resource monitor among 'enum resmon_src_type'. + */ +int pass_resmon_unregister_timer(struct pass_resource *res, + enum resmon_src_type src_type) +{ + struct resmon *monitor; + int ret; + + if (!res || src_type == 0) + return -EINVAL; + + monitor = resmon_find_monitor(&res->resmon, src_type); + if (!monitor) { + _E("failed to find monitor (res_type: %d, src_type: 0x%x)\n", + res->config_data.res_type, src_type); + return -EINVAL; + } + + /* Delete timer-based resource monitor */ + ret = resmon_timer_delete(monitor); + if (ret < 0) { + _E("failed to delete monitor (res_type: %d, src_type: 0x%x)\n", + res->config_data.res_type, src_type); + } + + /* Free the memory of resource monitor */ + free(monitor); + monitor = NULL; + + return 0; +} + +/* + * pass_resmon_update_interval - Update the period of timer + * + * @res: the instance of struct pass_resource. + * @src_type: the type of resource monitor among 'enum resmon_src_type'. + * @timer_interval: the interval of timer (unit: millisecond). + */ +int pass_resmon_update_timer_interval(struct pass_resource *res, + enum resmon_src_type src_type, + int timer_interval) +{ + struct resmon *monitor; + int ret; + + if (!res) + return -EINVAL; + + /* Find registered timer-based resmon */ + monitor = resmon_find_monitor(&res->resmon, src_type); + if (!monitor) { + _E("failed to find monitor (res_type:%d, src_type: 0x%x)\n", + res->config_data.res_type, src_type); + return -EINVAL; + } + + /* Update new interval of timer-based resmon */ + ret = resmon_timer_add(monitor, timer_interval); + if (ret < 0) + return -EINVAL; + + return 0; +} + +int pass_resmon_init(struct pass_resource *res) +{ + struct pass_resmon *resmon; + + if (!res) + return -EINVAL; + if (res->resmon.state == PASS_ON) + return -EINVAL; + + resmon = &res->resmon; + + resmon->timer_list = NULL; + resmon->timer_state = 0; + + resmon->state = PASS_ON; + + return 0; +} + +int pass_resmon_exit(struct pass_resource *res) +{ + struct pass_resmon *resmon; + + if (!res) + return -EINVAL; + if (res->resmon.state == PASS_OFF) + return -EINVAL; + + resmon = &res->resmon; + + g_list_free(resmon->timer_list); + resmon->timer_list = NULL; + resmon->timer_state = 0; + + resmon->state = PASS_OFF; + + return 0; +} diff --git a/src/pass/pass-resmon.h b/src/pass/pass-resmon.h new file mode 100644 index 0000000..a046854 --- /dev/null +++ b/src/pass/pass-resmon.h @@ -0,0 +1,83 @@ +/* + * PASS (Power Aware System Service) Header file of Resource Monitor + * + * Copyright (c) 2018 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_RESMON__ +#define __PASS_RESMON__ + +/* + * enum resmon_timer_type - Represents the type of timer-based resource monitor. + * - RESMON_TIMER_PERIODIC: Periodic timer with the regular interval. + * - RESMON_TIMER_ONESHOT: Oneshot timer after the interval. + */ +enum resmon_timer_type { + RESMON_TIMER_PERIODIC = 1, + RESMON_TIMER_ONESHOT, +}; + +/* + * enum resmon_src_type - Represents the source type of resource-monitor. + * - RESMON_THERMAL: Monitor the 'thermal' info of h/w resource. + * - RESMON_CPUHP: Monitor the 'CPU h/w resource' as following: + * CPU utilization/frequency/the number of running tasks. But this type + * is using nonstandard linux kernel interface. It should be replaced with + * the linux kernel standard interface. + */ +enum resmon_src_type { + RESMON_SRC_UNKNOWN = 0x0, + RESMON_SRC_THERMAL = 0x1, + RESMON_SRC_CPUHP = 0x2, +}; + +/* + * pass_resmon_register_timer - Register timer-based resource monitor. + * + * @res: the instance of struct pass_resource. + * @src_type: the type of resource monitor among 'enum resmon_src_type'. + * @timer_type: the type of timer among 'enum resmon_timer_type'. + * @timer_interval: the interval of timer (unit: millisecond). + * @user_func: the callback function when timer is expired. + * @user_data: the passed user user_data. + */ +int pass_resmon_register_timer(struct pass_resource *res, + enum resmon_src_type src_type, + enum resmon_timer_type timer_type, + unsigned int timer_interval, + int (*user_func)(void *result, void *user_data), + void *user_data); + +/* + * pass_resmon_unregister_timer - Unregister timer-based resource monitor. + * + * @res: the instance of struct pass_resource. + * @src_type: the type of resource monitor among 'enum resmon_src_type'. + */ +int pass_resmon_unregister_timer(struct pass_resource *res, + enum resmon_src_type src_type); + +/* + * pass_resmon_update_timer_interval - Update interval of timer-based monitor. + * + * @res: the instance of struct pass_resource. + * @src_type: the type of resource monitor among 'enum resmon_src_type'. + * @timer_interval: the interval of timer (unit: millisecond). + */ +int pass_resmon_update_timer_interval(struct pass_resource *res, + enum resmon_src_type src_type, + int timer_interval); + +#endif /* __PASS_RESMON__ */ diff --git a/src/pass/pass.c b/src/pass/pass.c index a2d7a0d..f93d27a 100644 --- a/src/pass/pass.c +++ b/src/pass/pass.c @@ -40,6 +40,8 @@ extern int pass_rescon_init(struct pass_resource *res); extern int pass_rescon_exit(struct pass_resource *res); +extern int pass_resmon_init(struct pass_resource *res); +extern int pass_resmon_exit(struct pass_resource *res); extern int pass_cpuhp_init(struct pass_resource *res); extern int pass_cpuhp_exit(struct pass_resource *res); extern int pass_pmqos_init(struct pass_resource *res); @@ -135,8 +137,8 @@ static int pass_init_resource(struct pass_resource *res) res->config_data.gov_timeout; /* - * Have to initialize the ResCon (Resource-Controller) before calling - * the init function of modules. Because there are ordering dependency. + * Have to initialize ResCon (Resource-Controller) and ResMon + * (Resource Monitor) before calling init() function of modules. */ ret = pass_rescon_init(res); if (ret < 0) { @@ -144,6 +146,12 @@ static int pass_init_resource(struct pass_resource *res) return ret; } + ret = pass_resmon_init(res); + if (ret < 0) { + _E("cannot initialize PASS Resource Monitor"); + goto err_resmon; + } + ret = pass_cpuhp_init(res); if (ret < 0) { _E("cannot initialize PASS CPUHP"); @@ -162,6 +170,9 @@ err_pmqos: if (pass_cpuhp_exit(res) < 0) _E("cannot exit PASS CPUHP"); err_cpuhp: + if (pass_resmon_exit(res) < 0) + _E("cannot exit PASS Resource Monitor"); +err_resmon: if (pass_rescon_exit(res) < 0) _E("cannot exit PASS Resource-Controller"); @@ -188,9 +199,15 @@ static int pass_exit_resource(struct pass_resource *res) } /* - * Have to exit the ResCon (Resource-Controller) after called - * the exit function of modules. Because there are ordering dependency. + * Have to exit ResCon (Resource-Controller) and ResMon + * (Resource Monitor) after called exit() function of modules. */ + ret = pass_resmon_exit(res); + if (ret < 0) { + _E("cannot exit PASS Resource Monitor"); + return ret; + } + ret = pass_rescon_exit(res); if (ret < 0) { _E("cannot exit PASS Resource-Controller"); diff --git a/src/pass/pass.h b/src/pass/pass.h index 55f4fbb..58fcf20 100644 --- a/src/pass/pass.h +++ b/src/pass/pass.h @@ -24,6 +24,7 @@ #include #include +#include #include #define BUFF_MAX 255 @@ -204,6 +205,20 @@ struct pass_rescon { }; /* + * struct pass_resmon - Represent ResMon (Resource Monitor) module + * + * @state: the state of ResMon (either enabled or disabled). + * @timer_list: the list of required timer-based resource monitor. + * @timer_state: the state of all timer-based resource-monitor. + */ +struct pass_resmon { + enum pass_state state; + + GList *timer_list; + uint64 timer_state; +}; + +/* * struct pass_pmqos - Represent PMQoS module * * @state: the state of PMQoS (either enabled or disabled). @@ -393,6 +408,7 @@ struct pass_resource_init_data { * - If res_type of cdata is PASS_RESOURCE_NONSTANDARD_ID, * hal.nonstandard will be used. * @rescon: represents ResCon (Resource Controller) module. + * @resmon: represents ResMon (Resource Monitor) module. * @cpuhp: represents CPUHP (CPU Hotplug Manager) module. * @pmqos: represents PMQoS module. */ @@ -411,6 +427,8 @@ struct pass_resource { } hal; struct pass_rescon rescon; + struct pass_resmon resmon; + struct pass_cpuhp cpuhp; struct pass_pmqos pmqos; }; -- 2.7.4 From ca5fa7b87cf90c843c2a44ac2720360661f27204 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Tue, 13 Feb 2018 16:17:16 +0900 Subject: [PATCH 02/16] pass: resmon: Add resmon source's skeleton code The RESMON (resource monitor) module will monitor the various h/w resource such as CPU, GPU, Memory, Network, Filesystem and so on. Each h/w resource requires the common monitoring API like timer hanlding function. Add resmon source's skeleton code. RESMON makes the following two files to implement ths resource monitor: - pass-resmon.c : Implment pass_resmon_init/exit() and common monitoring API which are used for all resource monitoring. It contains the exported API of both timer-based and uevent-baed resource monitor. - pass-resmon-impl.c : Implement the h/w resource monitoring through HAL (Hardware Abstract Layer). Each resource-monitor would collect the data of h/w resource with their own method. For example, some resource-monitor just reads the value from fixed sysfs interface. On the other hand, other resource-monitor parses the multiple data and then make the meaningfull data from raw data. Change-Id: I1a83c8ead16e6cba48e5ce191d55b5bde497afae Signed-off-by: Chanwoo Choi --- CMakeLists.txt | 1 + src/pass/pass-resmon-source.c | 79 +++++++++++++++++++++++++++++++++++++++++++ src/pass/pass-resmon.c | 9 ++++- 3 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 src/pass/pass-resmon-source.c diff --git a/CMakeLists.txt b/CMakeLists.txt index c13750f..7d901be 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -70,6 +70,7 @@ SET(SRCS src/pass/pass-hal.c src/pass/pass-rescon.c src/pass/pass-resmon.c + src/pass/pass-resmon-source.c src/pass/pass-pmqos.c src/pmqos/pmqos.c src/pmqos/pmqos-parser.c diff --git a/src/pass/pass-resmon-source.c b/src/pass/pass-resmon-source.c new file mode 100644 index 0000000..270f5f8 --- /dev/null +++ b/src/pass/pass-resmon-source.c @@ -0,0 +1,79 @@ +/* + * PASS (Power Aware System Service) Resource Monitor's source implementation + * + * Copyright (c) 2018 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 + +#include "pass.h" +#include "pass-resmon.h" +#include "pass-resmon-internal.h" + +/* RESMON_SRC_THERMAL */ +static int resmon_thermal_init(struct resmon *monitor) +{ + return 0; +} + +static int resmon_thermal_exit(struct resmon *monitor) +{ + return 0; +} + +static int resmon_thermal_timer_handler(struct resmon *monitor, void *result) +{ + return 0; +} + +/* RESMON_SRC_CPUHP */ +static int resmon_cpuhp_init(struct resmon *monitor) +{ + return 0; +} + +static int resmon_cpuhp_exit(struct resmon *monitor) +{ + return 0; +} + +static int resmon_cpuhp_timer_handler(struct resmon *monitor, void *result) +{ + return 0; +} + +struct resmon_ops thermal_src_ops = { + .init = resmon_thermal_init, + .exit = resmon_thermal_exit, + .timer_handler = resmon_thermal_timer_handler, +}; + +struct resmon_ops cpuhp_src_ops = { + .init = resmon_cpuhp_init, + .exit = resmon_cpuhp_exit, + .timer_handler = resmon_cpuhp_timer_handler, +}; + +struct resmon_ops *resmon_get_ops(enum resmon_src_type src_type) +{ + switch (src_type) { + case RESMON_SRC_THERMAL: + return &thermal_src_ops; + case RESMON_SRC_CPUHP: + return &cpuhp_src_ops;; + default: + return NULL; + } +} diff --git a/src/pass/pass-resmon.c b/src/pass/pass-resmon.c index 702b1de..d82791f 100644 --- a/src/pass/pass-resmon.c +++ b/src/pass/pass-resmon.c @@ -24,6 +24,8 @@ #include "pass-resmon.h" #include "pass-resmon-internal.h" +extern struct resmon_ops *resmon_get_ops(enum resmon_src_type src_type); + uint64 available_resmon_timer[] = { [PASS_RESOURCE_UNKNOWN] = 0, [PASS_RESOURCE_CPU_ID] = RESMON_SRC_THERMAL @@ -236,7 +238,12 @@ int pass_resmon_register_timer(struct pass_resource *res, monitor->user_func = user_func; monitor->user_data = user_data; - /* TODO: Get instance of struct resmon_ops accoring to resmon_src_type */ + /* Get instance of struct resmon_ops accoring to resmon_src_type */ + monitor->ops = resmon_get_ops(src_type); + if (!monitor->ops) { + ret = -EINVAL; + goto err; + } /* Add timer-based resource monitor */ ret = resmon_timer_add(monitor, timer_interval); -- 2.7.4 From 161d9c9ca5547148e9a1efba988df38df6c206a7 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Tue, 13 Feb 2018 16:00:39 +0900 Subject: [PATCH 03/16] pass: resmon: Add timer-based RESMON_SRC_CPUHP source RESMON_SRC_CPUHP indicates the CPU resource monitoring for CPUHP (CPU Hotplug Manager) module. RESMON_CPUHP contains the CPU's information. They are used to decide the current system status on CPUHP. Unfortunately, it is nonstandard linux kernel interface which is developed for only CPUHP module. It should be replaced with standard linux kernel interface on later. [Description of RESMON_CPUHP resource-monitor] - The kind of resource data : time (time unit is millisecond) : old CPU frequency (Minimum/Maximum is different accroding to h/w board) : new CPU frequency (Minimum/Maximum is different accroding to h/w board) : the number of nr_running tasks in the same cluster (100 means the one task) : each CPU's utilization (Minimum is 0 and maximum is 100) - Path of RESMON_SRC_CPUHP monitoring : /sys/kernel/debug/cpufreq/cpu0/load_table - Result of RESMON_SRC_CPUHP monitoring $ cat /sys/kernel/debug/cpufreq/cpu0/load_table Time(ms) Old Freq(Hz) New Freq(Hz) NR_running CPU0 CPU1 CPU2 CPU3 3417690 500000 500000 300 10 4 0 0 3417770 500000 500000 182 10 4 0 0 Change-Id: Idcb3384e507f08aac282e0f0629a5666d7e0dd75 Signed-off-by: Chanwoo Choi --- src/pass/pass-resmon-source.c | 84 +++++++++++++++++++++++++++++++++++++++++++ src/pass/pass-resmon.h | 21 +++++++++++ 2 files changed, 105 insertions(+) diff --git a/src/pass/pass-resmon-source.c b/src/pass/pass-resmon-source.c index 270f5f8..9c8efcb 100644 --- a/src/pass/pass-resmon-source.c +++ b/src/pass/pass-resmon-source.c @@ -16,6 +16,8 @@ * limitations under the License. */ +#include + #include #include "pass.h" @@ -39,18 +41,100 @@ static int resmon_thermal_timer_handler(struct resmon *monitor, void *result) } /* RESMON_SRC_CPUHP */ +#define RESMON_SRC_CPUHP_COUNT 20 static int resmon_cpuhp_init(struct resmon *monitor) { + struct resmon_result_src_cpuhp *result; + int i; + + if (monitor->result) + return 0; + + result = calloc(RESMON_SRC_CPUHP_COUNT, sizeof(*result)); + + for (i = 0; i < RESMON_SRC_CPUHP_COUNT; i++) { + result[i].load = calloc(RESMON_SRC_CPUHP_COUNT, + sizeof(unsigned int)); + result[i].nr_running = calloc(RESMON_SRC_CPUHP_COUNT, + sizeof(unsigned int)); + result[i].runnable_load = calloc(RESMON_SRC_CPUHP_COUNT, + sizeof(unsigned int)); + } + + monitor->result = result; + return 0; } static int resmon_cpuhp_exit(struct resmon *monitor) { + struct resmon_result_src_cpuhp *result; + int i; + + if (!monitor->result) + return 0; + + result = (struct resmon_result_src_cpuhp *)monitor->result; + if (!result) + return -ENOMEM; + + for (i = 0; i < RESMON_SRC_CPUHP_COUNT; i++) { + free(result[i].load); + free(result[i].nr_running); + free(result[i].runnable_load); + } + + free(monitor->result); + monitor->result = NULL; + return 0; } static int resmon_cpuhp_timer_handler(struct resmon *monitor, void *result) { + struct pass_resmon *resmon = monitor->resmon; + struct pass_resource *res = + container_of(resmon, struct pass_resource, resmon); + struct resmon_result_src_cpuhp *cpuhp_result = result; + char str[BUFF_MAX]; + int i, j, ret; + FILE *fp = NULL; + + if (!cpuhp_result) + return -ENOMEM; + + fp = fopen(res->config_data.path_load_table, "r"); + if (!fp) + return -EIO; + + /* Read the title and drop the buffer because it is not used */ + if (!fgets(str, BUFF_MAX, fp)) { + fclose(fp); + return -EIO; + } + + for (i = 0; i < RESMON_SRC_CPUHP_COUNT; i++) { + ret = fscanf(fp, "%" SCNd64 "%u %u %u", + &cpuhp_result[i].time, + &cpuhp_result[i].freq, + &cpuhp_result[i].freq_new, + &cpuhp_result[i].nr_runnings); + if (ret < 0) { + fclose(fp); + return -EIO; + } + + for (j = 0; j < res->config_data.num_cpus; j++) { + ret = fscanf(fp, "%u", &cpuhp_result[i].load[j]); + if (ret < 0) { + fclose(fp); + return -EIO; + } + } + } + + fclose(fp); + return 0; } diff --git a/src/pass/pass-resmon.h b/src/pass/pass-resmon.h index a046854..52fdb4e 100644 --- a/src/pass/pass-resmon.h +++ b/src/pass/pass-resmon.h @@ -36,6 +36,9 @@ enum resmon_timer_type { * CPU utilization/frequency/the number of running tasks. But this type * is using nonstandard linux kernel interface. It should be replaced with * the linux kernel standard interface. + * + * Description of result format of resource monitor's source type: + * - RESMON_SRC_CPUHP : struct resmon_result_src_cpuhp */ enum resmon_src_type { RESMON_SRC_UNKNOWN = 0x0, @@ -43,6 +46,24 @@ enum resmon_src_type { RESMON_SRC_CPUHP = 0x2, }; +/* Result of RESMON_SRC_CPUHP's resource monitor */ +struct resmon_result_src_cpuhp { + int64_t time; + unsigned int freq; + unsigned int freq_new; + unsigned int nr_runnings; + + unsigned int *load; + unsigned int *nr_running; + unsigned int *runnable_load; + + unsigned int num_busy_cpu; + unsigned int avg_load; + unsigned int avg_runnable_load; + unsigned int avg_thread_load; + unsigned int avg_thread_runnable_load; +}; + /* * pass_resmon_register_timer - Register timer-based resource monitor. * -- 2.7.4 From 2fabc28a84d2920d516c5e65ff3bc093c6f3442d Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Tue, 13 Feb 2018 22:34:01 +0900 Subject: [PATCH 04/16] pass: resmon: Add timer-based RESMON_SRC_THERMAL source RESMON_SRC_THERMAL indicates the thermal monitoring for h/w resource such as CPU, GPU and so on. It is used to decide the current system status like 'Low, Normal, High, Critical and Danger according to temperature of h/w resource'. Also, other subsystems and user-processes can use the temperature information. It is standard Linux Kernel interface which provides the thermal infomation through Thermal framework of Linux Kernel. [Description of RESMON_SRC_THERMAL resource-monitor] - The kind of resource data : temperature of h/w resource (unit is centigrade) : (e.g., 47000 means 47.0 degrees centigrade) - Path of RESMON_SRC_THERMAL monitoring : cat /sys/class/thermal/thermal_zone[X]/temp - Result of RESMON_SRC_THERMAL monitoring $ cat /sys/class/thermal/thermal_zone0/temp 47000 For exmaple on TM2 board, : thermal_zone0 for ARM big core on TM2 : thermal_zone2 for ARM GPU on TM2 : thermal_zone3 for ARM LITTLE core on TM2 Change-Id: Ia6272621caea45a3c117d4d5bb9512ccafb9947a Signed-off-by: Chanwoo Choi --- src/pass/pass-resmon-source.c | 34 ++++++++++++++++++++++++++++++++++ src/pass/pass-resmon.h | 6 ++++++ 2 files changed, 40 insertions(+) diff --git a/src/pass/pass-resmon-source.c b/src/pass/pass-resmon-source.c index 9c8efcb..754d854 100644 --- a/src/pass/pass-resmon-source.c +++ b/src/pass/pass-resmon-source.c @@ -21,22 +21,56 @@ #include #include "pass.h" +#include "pass-hal.h" #include "pass-resmon.h" #include "pass-resmon-internal.h" /* RESMON_SRC_THERMAL */ static int resmon_thermal_init(struct resmon *monitor) { + struct resmon_result_src_thermal *result; + + if (monitor->result) + return 0; + + result = calloc(1, sizeof(*result)); + if (!result) + return -ENOMEM; + + monitor->result = result; + return 0; } static int resmon_thermal_exit(struct resmon *monitor) { + if (!monitor->result) + return 0; + + free(monitor->result); + monitor->result = NULL; + return 0; } static int resmon_thermal_timer_handler(struct resmon *monitor, void *result) { + struct pass_resmon *resmon = monitor->resmon; + struct pass_resource *res = + container_of(resmon, struct pass_resource, resmon); + struct resmon_result_src_thermal *thermal_result = result; + int temp; + + if (!thermal_result) + return -ENOMEM; + + /* Get temperature of h/w resource */ + temp = pass_get_temp(res); + if (temp < 0) + return temp; + + thermal_result->temp = temp; + return 0; } diff --git a/src/pass/pass-resmon.h b/src/pass/pass-resmon.h index 52fdb4e..b3fb8fc 100644 --- a/src/pass/pass-resmon.h +++ b/src/pass/pass-resmon.h @@ -38,6 +38,7 @@ enum resmon_timer_type { * the linux kernel standard interface. * * Description of result format of resource monitor's source type: + * - RESMON_SRC_THERMAL: struct resmon_result_src_thermal * - RESMON_SRC_CPUHP : struct resmon_result_src_cpuhp */ enum resmon_src_type { @@ -46,6 +47,11 @@ enum resmon_src_type { RESMON_SRC_CPUHP = 0x2, }; +/* Result of RESMON_SRC_THERMAL resource monitor */ +struct resmon_result_src_thermal { + int temp; +}; + /* Result of RESMON_SRC_CPUHP's resource monitor */ struct resmon_result_src_cpuhp { int64_t time; -- 2.7.4 From eac3132aeb702e90dfd56d772d8a65501586b12d Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Mon, 12 Feb 2018 14:06:54 +0900 Subject: [PATCH 05/16] pass: cpuhp: Replace legacy monitoring with RESMON (Resource Monitor) PASS CPUHP (CPU Hotplug Manager) module requires the CPU resource monitoring in order to check the current system status and then decide the optimal amount of CPU resource. It makes the balance between the ondemand peformanace and required low-power consumption. But, CPUHP used the legacy and too tightly coupled monitoring code. So, replace the legacy method for resource monitoring with RESMON (Resource Monitor). CPUHP will use the RESMON_CPUHP type instead of old method. Change-Id: I1dbbafceeb1c747383fdbfe0aa33c3e84fe02a68 Signed-off-by: Chanwoo Choi --- src/pass/pass-cpuhp-radiation.c | 14 +++-- src/pass/pass-cpuhp-step.c | 12 ++-- src/pass/pass-cpuhp.c | 129 ++++++++++++++-------------------------- src/pass/pass-hal.c | 59 ------------------ src/pass/pass-hal.h | 5 -- src/pass/pass-parser.c | 3 - src/pass/pass.h | 26 -------- 7 files changed, 60 insertions(+), 188 deletions(-) diff --git a/src/pass/pass-cpuhp-radiation.c b/src/pass/pass-cpuhp-radiation.c index 649385b..8010c39 100644 --- a/src/pass/pass-cpuhp-radiation.c +++ b/src/pass/pass-cpuhp-radiation.c @@ -21,11 +21,15 @@ #include #include "pass.h" +#include "pass-resmon.h" + +#define PASS_CPU_STATS_MAX_COUNT 20 /* * pass_cpuhp_radiation_governor - Check cpu state and determine the amount of resource * - * @stats: structure for getting cpu frequency state from kerncel + * @res: the instance of struct pass_resource. + * @result: the result of resource monitoring. * * This function check periodically current following state of system * and then determine whether pass level up or down according to pass @@ -34,11 +38,11 @@ * - the number of nr_running * - the number of busy_cpu */ -int pass_cpuhp_radiation_governor(struct pass_resource *res) +int pass_cpuhp_radiation_governor(struct pass_resource *res, void *result) { struct pass_level *levels = res->config_data.levels; struct pass_cpuhp *cpuhp = &res->cpuhp; - struct pass_cpu_stats *cpu_stats = cpuhp->pass_cpu_stats; + struct resmon_result_src_cpuhp *cpu_stats = result; int up_threshold = cpuhp->up_threshold; int down_threshold = cpuhp->down_threshold; int num_pass_gov = 0; @@ -58,7 +62,7 @@ int pass_cpuhp_radiation_governor(struct pass_resource *res) int j; int64_t time; - for (i = 0; i < cpuhp->num_pass_cpu_stats; i++) { + for (i = 0; i < PASS_CPU_STATS_MAX_COUNT; i++) { time = cpu_stats[i].time; freq = cpu_stats[i].freq; nr_running = cpu_stats[i].nr_runnings; @@ -192,7 +196,7 @@ int pass_cpuhp_radiation_governor(struct pass_resource *res) /* if (level == res->prev_level) { - for (i = num_pass_gov; i < cpuhp->num_pass_cpu_stats; i++) { + for (i = num_pass_gov; i < PASS_CPU_STATS_MAX_COUNT; i++) { time = cpu_stats[i].time; freq = cpu_stats[i].freq; nr_running = cpu_stats[i].nr_runnings; diff --git a/src/pass/pass-cpuhp-step.c b/src/pass/pass-cpuhp-step.c index 606d217..a86a030 100644 --- a/src/pass/pass-cpuhp-step.c +++ b/src/pass/pass-cpuhp-step.c @@ -21,11 +21,15 @@ #include #include "pass.h" +#include "pass-resmon.h" + +#define PASS_CPU_STATS_MAX_COUNT 20 /* * pass_cpuhp_step_governor - Check cpu state and determine the amount of resource * - * @stats: structure for getting cpu frequency state from kerncel + * @res: the instance of struct pass_resource. + * @result: the result of resource monitoring. * * This function check periodically current following state of system * and then determine whether pass level up or down according to pass @@ -34,11 +38,11 @@ * - the number of nr_running * - the number of busy_cpu */ -int pass_cpuhp_step_governor(struct pass_resource *res) +int pass_cpuhp_step_governor(struct pass_resource *res, void *result) { struct pass_level *levels = res->config_data.levels; struct pass_cpuhp *cpuhp = &res->cpuhp; - struct pass_cpu_stats *cpu_stats = cpuhp->pass_cpu_stats; + struct resmon_result_src_cpuhp *cpu_stats = result; int up_threshold = cpuhp->up_threshold; int down_threshold = cpuhp->down_threshold; int num_pass_gov = 0; @@ -55,7 +59,7 @@ int pass_cpuhp_step_governor(struct pass_resource *res) int j; int64_t time; - for (i = 0; i < cpuhp->num_pass_cpu_stats; i++) { + for (i = 0; i < PASS_CPU_STATS_MAX_COUNT; i++) { time = cpu_stats[i].time; freq = cpu_stats[i].freq; nr_running = cpu_stats[i].nr_runnings; diff --git a/src/pass/pass-cpuhp.c b/src/pass/pass-cpuhp.c index 38174a4..7954ef8 100644 --- a/src/pass/pass-cpuhp.c +++ b/src/pass/pass-cpuhp.c @@ -1,5 +1,5 @@ /* - * PASS (Power Aware System Service) Governor + * PASS (Power Aware System Service) CPUHP (CPU Hotplug Manager) * * Copyright (c) 2012 - 2018 Samsung Electronics Co., Ltd. * @@ -25,11 +25,11 @@ #include #include "pass.h" -#include "pass-hal.h" #include "pass-rescon.h" +#include "pass-resmon.h" -extern int pass_cpuhp_step_governor(struct pass_resource *res); -extern int pass_cpuhp_radiation_governor(struct pass_resource *res); +extern int pass_cpuhp_step_governor(struct pass_resource *res, void *result); +extern int pass_cpuhp_radiation_governor(struct pass_resource *res, void *result); static int cpuhp_governor_update(struct pass_resource *, enum pass_state); #define PASS_DEFAULT_CPU_THRESHOLD 20 @@ -44,7 +44,7 @@ struct pass_cpuhp_governor { int (*init)(struct pass_resource *res); int (*exit)(struct pass_resource *res); int (*update)(struct pass_resource *res, enum pass_state state); - int (*governor)(struct pass_resource *res); + int (*governor)(struct pass_resource *res, void *result); }; static bool is_enabled(struct pass_cpuhp *cpuhp) @@ -102,12 +102,12 @@ static struct pass_hotplug* cpuhp_get_hotplug(struct pass_resource *res, return NULL; } -static void cpuhp_calculate_busy_cpu(struct pass_resource *res) +static void cpuhp_calculate_busy_cpu(struct pass_resource *res, void *result) { struct pass_cpuhp *cpuhp = &res->cpuhp; struct pass_level *levels = res->config_data.levels; struct pass_resource_config_data *config_data = &res->config_data; - struct pass_cpu_stats *stats = cpuhp->pass_cpu_stats; + struct resmon_result_src_cpuhp *stats = result; unsigned int level = res->rescon.curr_level; unsigned int cpu_threshold = 0; unsigned int busy_cpu; @@ -122,7 +122,7 @@ static void cpuhp_calculate_busy_cpu(struct pass_resource *res) limit_min_cpu = levels[level].limit_min_cpu; - for (i = 0; i < cpuhp->num_pass_cpu_stats; i++) { + for (i = 0; i < PASS_CPU_STATS_MAX_COUNT; i++) { cur_freq = stats[i].freq; nr_runnings = stats[i].nr_runnings; @@ -164,14 +164,13 @@ static void cpuhp_calculate_busy_cpu(struct pass_resource *res) } } -static gboolean cpuhp_governor_timer_cb(gpointer data) +static int cpuhp_timer_func(void *result, void *user_data) { - struct pass_resource *res = (struct pass_resource *)data; + struct pass_resource *res = user_data; struct pass_level *levels; struct pass_cpuhp *cpuhp; - static int count = 0; double curr_gov_timeout, next_gov_timeout; - int level, ret; + int level; if (!res) { _E("cannot call the governor timeout callback\n"); @@ -181,34 +180,15 @@ static gboolean cpuhp_governor_timer_cb(gpointer data) levels = res->config_data.levels; cpuhp = &res->cpuhp; - /* - * Collect data related to system state - * - the current frequency - * - the number of nr_running - * - the resource utilization - */ - ret = pass_get_cpu_stats(res); - if (ret < 0) { - if (count++ < PASS_CPU_STATS_MAX_COUNT) - return TRUE; - - count = 0; - - _E("cannot read the 'pass_cpu_stats' sysfs entry"); - cpuhp_governor_update(res, PASS_OFF); - - return FALSE; - } - /* Calculate the number of busy cpu */ - cpuhp_calculate_busy_cpu(res); + cpuhp_calculate_busy_cpu(res, result); /* Store current governor timeout */ curr_gov_timeout = levels[res->rescon.curr_level].gov_timeout; /* Determine the amount of proper resource */ if (cpuhp->governor->governor) { - level = cpuhp->governor->governor(res); + level = cpuhp->governor->governor(res, result); pass_rescon_set_level(res, level); } else { @@ -228,11 +208,9 @@ static gboolean cpuhp_governor_timer_cb(gpointer data) _I("Change the period of governor timer from %fs to %fs\n", curr_gov_timeout, next_gov_timeout); - g_source_remove(cpuhp->gov_timeout_id); - cpuhp->gov_timeout_id = g_timeout_add( - (guint)(next_gov_timeout * 1000), - cpuhp_governor_timer_cb, - (gpointer)res); + + pass_resmon_update_timer_interval(res, RESMON_SRC_CPUHP, + next_gov_timeout * 1000); } return TRUE; @@ -241,6 +219,7 @@ static gboolean cpuhp_governor_timer_cb(gpointer data) static void cpuhp_governor_start(struct pass_resource *res) { struct pass_cpuhp *cpuhp = &res->cpuhp; + int ret; if (!cpuhp->governor) { _E("cannot start PASS governor"); @@ -252,19 +231,15 @@ static void cpuhp_governor_start(struct pass_resource *res) return; } - /* Add callback function for each governor timeout */ - if (res->config_data.gov_timeout > 0) { - cpuhp->gov_timeout_id = g_timeout_add( - (guint)(res->config_data.gov_timeout * 1000), - (GSourceFunc)cpuhp_governor_timer_cb, - (gpointer)res); - if (!cpuhp->gov_timeout_id) { - _E("cannot add core timer for governor"); - cpuhp_governor_update(res, PASS_OFF); - return; - } - } else { - cpuhp->gov_timeout_id = 0; + /* Register the resource-monitor for CPUHP module */ + ret = pass_resmon_register_timer(res, RESMON_SRC_CPUHP, + RESMON_TIMER_PERIODIC, + res->config_data.gov_timeout * 1000, + cpuhp_timer_func, + res); + if (ret < 0) { + _E("failed to register timer for CPUHP\n"); + return; } /* @@ -279,14 +254,11 @@ static void cpuhp_governor_start(struct pass_resource *res) /* Set PASS state as PASS_ON */ cpuhp->state = PASS_ON; - - _I("Start governor for '%s' resource", res->config_data.res_name); } static void cpuhp_governor_stop(struct pass_resource *res) { struct pass_cpuhp *cpuhp = &res->cpuhp; - struct pass_resource_config_data *config_data = &res->config_data; if (!cpuhp->governor) { _E("cannot stop PASS governor"); @@ -300,15 +272,11 @@ static void cpuhp_governor_stop(struct pass_resource *res) cpuhp_hotplug_stop(res); - if (cpuhp->gov_timeout_id) { - g_source_remove(cpuhp->gov_timeout_id); - cpuhp->gov_timeout_id = 0; - } + /* Unregister the resource-monitor for CPUHP module */ + pass_resmon_unregister_timer(res, RESMON_SRC_CPUHP); /* Set PASS state as PASS_OFF */ cpuhp->state = PASS_OFF; - - _I("Stop governor for '%s' resource", config_data->res_name); } static int cpuhp_governor_init(struct pass_resource *res) @@ -333,7 +301,6 @@ static int cpuhp_governor_init(struct pass_resource *res) static int cpuhp_governor_exit(struct pass_resource *res) { struct pass_cpuhp *cpuhp = &res->cpuhp; - int i; /* * Stop timer and @@ -341,14 +308,6 @@ static int cpuhp_governor_exit(struct pass_resource *res) */ cpuhp_governor_update(res, PASS_OFF); - /* Free allocated memory */ - for (i = 0; i < cpuhp->num_pass_cpu_stats; i++) { - free(cpuhp->pass_cpu_stats[i].load); - free(cpuhp->pass_cpu_stats[i].nr_running); - free(cpuhp->pass_cpu_stats[i].runnable_load); - } - free(cpuhp->pass_cpu_stats); - if (cpuhp->hotplug) free(cpuhp->hotplug->sequence); @@ -356,7 +315,6 @@ static int cpuhp_governor_exit(struct pass_resource *res) cpuhp->pass_cpu_threshold = 0; cpuhp->up_threshold = 0; cpuhp->down_threshold = 0; - cpuhp->num_pass_cpu_stats = 0; cpuhp->governor = NULL; return 0; @@ -452,6 +410,21 @@ int pass_cpuhp_init(struct pass_resource *res) if (!res) return -EINVAL; + /* + * FIXME: The h/w resource configuration specifies the gov_type. + * When gov_type is either PASS_GOV_STEP(2) or PASS_GOV_RADIATION(3), + * CPUHP should be enabled. On later, each PASS module have to define + * the separate property in configuration which indicates either this + * module is used or not. + */ + switch (res->config_data.gov_type) { + case PASS_GOV_STEP: + case PASS_GOV_RADIATION: + break; + default: + return 0; + }; + cpuhp = &res->cpuhp; if (!cpuhp->pass_cpu_threshold) @@ -463,27 +436,11 @@ int pass_cpuhp_init(struct pass_resource *res) if (!cpuhp->down_threshold) cpuhp->down_threshold = PASS_DEFAULT_LEVEL_DOWN_THRESHOLD; - if (!cpuhp->num_pass_cpu_stats) - cpuhp->num_pass_cpu_stats = PASS_CPU_STATS_MAX_COUNT; - for (i = 0; i < res->config_data.num_levels; i++) if (max_freq < res->config_data.levels[i].limit_max_freq) max_freq = res->config_data.levels[i].limit_max_freq; cpuhp->max_freq = max_freq; - /* Allocate memory according to the number of data and cpu */ - cpuhp->pass_cpu_stats = calloc(cpuhp->num_pass_cpu_stats, - sizeof(struct pass_cpu_stats)); - - for (i = 0; i < cpuhp->num_pass_cpu_stats; i++) { - cpuhp->pass_cpu_stats[i].load = - calloc(res->config_data.num_cpus, sizeof(unsigned int)); - cpuhp->pass_cpu_stats[i].nr_running = - calloc(res->config_data.num_cpus, sizeof(unsigned int)); - cpuhp->pass_cpu_stats[i].runnable_load = - calloc(res->config_data.num_cpus, sizeof(unsigned int)); - } - /* Get the instance of CPU Hotplug's policy */ cpuhp->hotplug = cpuhp_get_hotplug(res, res->config_data.gov_type); if (cpuhp->hotplug) { diff --git a/src/pass/pass-hal.c b/src/pass/pass-hal.c index 73eeb17..3694326 100644 --- a/src/pass/pass-hal.c +++ b/src/pass/pass-hal.c @@ -849,62 +849,3 @@ int pass_put_resource(struct pass_resource *res) return 0; } -/* - * FXIME: Following function is not standard interface. - * Following functions will be altered by the standard interface on later. - * - * - int pass_get_cpu_stats(struct pass_resource *res) - * - int64_t pass_get_time_ms(void) - */ - -/* Get the load_table of each resource to estimate the system load. */ -int pass_get_cpu_stats(struct pass_resource *res) -{ - struct pass_cpu_stats *stats; - char str[BUFF_MAX]; - FILE *fp_stats = NULL; - int i, j, ret; - - if (!res) - return -EINVAL; - stats = res->cpuhp.pass_cpu_stats; - - if (!stats) { - _E("invalid parameter of structure pass_cpu_stats"); - return -EINVAL; - } - - fp_stats = fopen(res->config_data.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)) { - fclose(fp_stats); - return -EIO; - } - - for (i = 0; i < res->cpuhp.num_pass_cpu_stats; i++) { - ret = fscanf(fp_stats, "%" SCNd64 "%u %u %u", - &stats[i].time, - &stats[i].freq, - &stats[i].freq_new, - &stats[i].nr_runnings); - if (ret < 0) { - fclose(fp_stats); - return -EIO; - } - - for (j = 0; j < res->config_data.num_cpus; j++) { - ret = fscanf(fp_stats, "%u", &stats[i].load[j]); - if (ret < 0) { - fclose(fp_stats); - return -EIO; - } - } - } - - fclose(fp_stats); - - return 0; -} diff --git a/src/pass/pass-hal.h b/src/pass/pass-hal.h index 8d1fd6f..da47a49 100644 --- a/src/pass/pass-hal.h +++ b/src/pass/pass-hal.h @@ -75,11 +75,6 @@ int pass_set_online_min_num(struct pass_resource *res, int num); /* Get and set the maximum number of online CPUs */ int pass_get_online_max_num(struct pass_resource *res); int pass_set_online_max_num(struct pass_resource *res, int num); -/* - * FXIME: Following function is not standard interface. - * These functions will be altered by the standard interface on later. - */ -int pass_get_cpu_stats(struct pass_resource *res); /*** * Functions for Memory h/w resource diff --git a/src/pass/pass-parser.c b/src/pass/pass-parser.c index 911a188..37b4595 100644 --- a/src/pass/pass-parser.c +++ b/src/pass/pass-parser.c @@ -250,8 +250,6 @@ static int parse_core(struct parse_result *result, void *user_data) else if (MATCH(result->name, "pass_init_level")) res->rescon.init_level = atoi(result->value); - else if (MATCH(result->name, "pass_num_cpu_stats")) - res->cpuhp.num_pass_cpu_stats = atoi(result->value); else if (MATCH(result->name, "pass_cpu_threshold")) res->cpuhp.pass_cpu_threshold = atoi(result->value); else if (MATCH(result->name, "pass_up_threshold")) @@ -567,7 +565,6 @@ int pass_get_each_resource_config(struct pass_resource *res, char *path) res->rescon.max_level = 0; /* Initialize the CPUHP's data */ - cpuhp->num_pass_cpu_stats = 0; cpuhp->pass_cpu_threshold = 0; cpuhp->up_threshold = 0; cpuhp->down_threshold = 0; diff --git a/src/pass/pass.h b/src/pass/pass.h index 58fcf20..9c42ce3 100644 --- a/src/pass/pass.h +++ b/src/pass/pass.h @@ -63,25 +63,6 @@ enum pass_state { PASS_ON = 1, }; -struct pass_cpu_stats { - const char *path; - - int64_t time; - unsigned int freq; - unsigned int freq_new; - unsigned int nr_runnings; - - unsigned int *load; - unsigned int *nr_running; - unsigned int *runnable_load; - - unsigned int num_busy_cpu; - unsigned int avg_load; - unsigned int avg_runnable_load; - unsigned int avg_thread_load; - unsigned int avg_thread_runnable_load; -}; - struct pass_hotplug { char name[BUFF_MAX]; unsigned int num_cpus; @@ -252,11 +233,8 @@ struct pass_pmqos { * @pass_cpu_threshold: * @up_threshold: the threshold used when increasing the level * @down_threshold: the threshold used when decreasing the level - * @num_pass_cpu_stats: the number of history for CPU utilization/nr_runnings - * @pass_cpu_stats: the list of CPU utilization/nr_runnings * @last_time: the updated last time of cpu_stats * @governor: the instance of CPUHP Policy governor. - * @gov_timeout_id: the periodic timer id * @max_freq: the maximum frequency among levels * * @hotplug: the instance of CPU Hotplug governor (will be deprecated). @@ -270,13 +248,9 @@ struct pass_cpuhp { unsigned int up_threshold; unsigned int down_threshold; - int num_pass_cpu_stats; - struct pass_cpu_stats *pass_cpu_stats; - int64_t last_time; struct pass_cpuhp_governor *governor; - guint gov_timeout_id; unsigned int max_freq; -- 2.7.4 From 3c97b99d939b0766f9744f0cefe651308a78746d Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Thu, 15 Mar 2018 10:00:11 +0900 Subject: [PATCH 06/16] pass: rescon: Set default pass level when initializing RESCON Before developed the RESCON (resource controller), CPUHP (cpu hotplug manager) controls the level directly. CPUHP decides the next level and then ask them to RESCON in order to control the h/w resource. So, set default pass level when initializing RESCON in pass_resmon_init() instead of CPUHP. Change-Id: I0a8f86b7a781d4cf13e021f9a29537738a8fd70a Signed-off-by: Chanwoo Choi --- src/pass/pass-cpuhp.c | 10 ---------- src/pass/pass-rescon.c | 10 ++++++++++ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/pass/pass-cpuhp.c b/src/pass/pass-cpuhp.c index 7954ef8..df10e96 100644 --- a/src/pass/pass-cpuhp.c +++ b/src/pass/pass-cpuhp.c @@ -242,16 +242,6 @@ static void cpuhp_governor_start(struct pass_resource *res) return; } - /* - * Set default pass level when starting pass - * - default pass level according to res->init_level - */ - res->rescon.curr_level = -1; - if (res->rescon.init_level > res->rescon.max_level) - res->rescon.init_level = res->rescon.max_level; - - pass_rescon_set_level(res, res->rescon.init_level); - /* Set PASS state as PASS_ON */ cpuhp->state = PASS_ON; } diff --git a/src/pass/pass-rescon.c b/src/pass/pass-rescon.c index 2deefa8..f5b0e56 100644 --- a/src/pass/pass-rescon.c +++ b/src/pass/pass-rescon.c @@ -238,6 +238,16 @@ int pass_rescon_init(struct pass_resource *res) return ret; } + /* + * Set default pass level when starting pass + * - default pass level according to res->init_level + */ + res->rescon.curr_level = -1; + if (res->rescon.init_level > res->rescon.max_level) + res->rescon.init_level = res->rescon.max_level; + + pass_rescon_set_level(res, res->rescon.init_level); + rescon->state = PASS_ON; return 0; -- 2.7.4 From 3e49c76563cae271e9073675f3da0f5394c28fee Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Mon, 12 Feb 2018 15:12:12 +0900 Subject: [PATCH 07/16] pass: Initialize only supported module according to h/w resource PASS handlse the h/w resource such as CPU, BUS, GPU, Memory and so on. Also, PASS provides the multiple module such as CPUHP, PMQoS, Rescon, Resmon and Parser. All modules provided by PASS are not used for each h/w resource. Each h/w resource must need to have the supported module list and then initialize/exit the module according to the defined their own list. Change-Id: I0453a7cb247fcd52fdd2f18953d7df1d1406114e Signed-off-by: Chanwoo Choi --- src/pass/pass.c | 130 +++++++++++++++++++++++++++++++++++++++----------------- src/pass/pass.h | 19 +++++++++ 2 files changed, 111 insertions(+), 38 deletions(-) diff --git a/src/pass/pass.c b/src/pass/pass.c index f93d27a..b599787 100644 --- a/src/pass/pass.c +++ b/src/pass/pass.c @@ -38,6 +38,33 @@ #define DBUS_CORE_PATH "/Org/Tizen/System/Pass/Core" #define PASS_CONF_PATH "/etc/pass/pass.conf" +static uint64 supported_module[] = { + [PASS_RESOURCE_UNKNOWN] = 0, + [PASS_RESOURCE_CPU_ID] = PASS_MODULE_PARSER + | PASS_MODULE_RESCON + | PASS_MODULE_RESMON + | PASS_MODULE_PMQOS + | PASS_MODULE_CPUHP, + + [PASS_RESOURCE_BUS_ID] = PASS_MODULE_PARSER + | PASS_MODULE_RESCON + | PASS_MODULE_RESMON + | PASS_MODULE_PMQOS, + + [PASS_RESOURCE_GPU_ID] = PASS_MODULE_PARSER + | PASS_MODULE_RESCON + | PASS_MODULE_RESMON + | PASS_MODULE_PMQOS, + + [PASS_RESOURCE_MEMORY_ID] = PASS_MODULE_PARSER + | PASS_MODULE_RESCON + | PASS_MODULE_RESMON + | PASS_MODULE_PMQOS, + + [PASS_RESOURCE_NONSTANDARD_ID] = PASS_MODULE_PARSER + | PASS_MODULE_RESCON, +}; + extern int pass_rescon_init(struct pass_resource *res); extern int pass_rescon_exit(struct pass_resource *res); extern int pass_resmon_init(struct pass_resource *res); @@ -113,6 +140,13 @@ static struct pass_gdbus_signal_info g_gdbus_signal_infos[] = { /****************************************************** * PASS interface (Init/Exit) * ******************************************************/ + +static bool is_supported_module(struct pass_resource *res, + enum pass_module_type type) +{ + return !!(supported_module[res->config_data.res_type] & type); +} + static int pass_init_resource(struct pass_resource *res) { int ret; @@ -140,41 +174,53 @@ static int pass_init_resource(struct pass_resource *res) * Have to initialize ResCon (Resource-Controller) and ResMon * (Resource Monitor) before calling init() function of modules. */ - ret = pass_rescon_init(res); - if (ret < 0) { - _E("cannot initialize PASS Resource-Controller"); - return ret; + + if (is_supported_module(res, PASS_MODULE_RESCON)) { + ret = pass_rescon_init(res); + if (ret < 0) { + _E("cannot initialize PASS Resource-Controller"); + return ret; + } } - ret = pass_resmon_init(res); - if (ret < 0) { - _E("cannot initialize PASS Resource Monitor"); - goto err_resmon; + if (is_supported_module(res, PASS_MODULE_RESMON)) { + ret = pass_resmon_init(res); + if (ret < 0) { + _E("cannot initialize PASS Resource Monitor"); + goto err_resmon; + } } - ret = pass_cpuhp_init(res); - if (ret < 0) { - _E("cannot initialize PASS CPUHP"); - goto err_cpuhp; + if (is_supported_module(res, PASS_MODULE_CPUHP)) { + ret = pass_cpuhp_init(res); + if (ret < 0) { + _E("cannot initialize PASS CPUHP"); + goto err_cpuhp; + } } - ret = pass_pmqos_init(res); - if (ret < 0) { - _E("cannot initialize PASS PMQoS"); - goto err_pmqos; + if (is_supported_module(res, PASS_MODULE_PMQOS)) { + ret = pass_pmqos_init(res); + if (ret < 0) { + _E("cannot initialize PASS PMQoS"); + goto err_pmqos; + } } return 0; err_pmqos: - if (pass_cpuhp_exit(res) < 0) - _E("cannot exit PASS CPUHP"); + if (is_supported_module(res, PASS_MODULE_CPUHP)) + if (pass_cpuhp_exit(res) < 0) + _E("cannot exit PASS CPUHP"); err_cpuhp: - if (pass_resmon_exit(res) < 0) - _E("cannot exit PASS Resource Monitor"); + if (is_supported_module(res, PASS_MODULE_RESMON)) + if (pass_resmon_exit(res) < 0) + _E("cannot exit PASS Resource Monitor"); err_resmon: - if (pass_rescon_exit(res) < 0) - _E("cannot exit PASS Resource-Controller"); + if (is_supported_module(res, PASS_MODULE_RESCON)) + if (pass_rescon_exit(res) < 0) + _E("cannot exit PASS Resource-Controller"); return ret; } @@ -186,32 +232,40 @@ static int pass_exit_resource(struct pass_resource *res) /* Put configuration of each resource from pass-resource*.conf */ pass_put_each_resource_config(res); - ret = pass_cpuhp_exit(res); - if (ret < 0) { - _E("cannot exit PASS CPUHP"); - return -1; + if (is_supported_module(res, PASS_MODULE_CPUHP)) { + ret = pass_cpuhp_exit(res); + if (ret < 0) { + _E("cannot exit PASS CPUHP"); + return ret; + } } - ret = pass_pmqos_exit(res); - if (ret < 0) { - _E("cannot exit PASS PMQoS"); - return ret; + if (is_supported_module(res, PASS_MODULE_PMQOS)) { + ret = pass_pmqos_exit(res); + if (ret < 0) { + _E("cannot exit PASS PMQoS"); + return ret; + } } /* * Have to exit ResCon (Resource-Controller) and ResMon * (Resource Monitor) after called exit() function of modules. */ - ret = pass_resmon_exit(res); - if (ret < 0) { - _E("cannot exit PASS Resource Monitor"); - return ret; + if (is_supported_module(res, PASS_MODULE_RESMON)) { + ret = pass_resmon_exit(res); + if (ret < 0) { + _E("cannot exit PASS Resource Monitor"); + return ret; + } } - ret = pass_rescon_exit(res); - if (ret < 0) { - _E("cannot exit PASS Resource-Controller"); - return ret; + if (is_supported_module(res, PASS_MODULE_RESCON)) { + ret = pass_rescon_exit(res); + if (ret < 0) { + _E("cannot exit PASS Resource-Controller"); + return ret; + } } return 0; diff --git a/src/pass/pass.h b/src/pass/pass.h index 9c42ce3..398eb1f 100644 --- a/src/pass/pass.h +++ b/src/pass/pass.h @@ -34,6 +34,25 @@ struct pass_resource; struct pass_cpuhp_governor; /* + * enum pass_module_type - Define supported modules of PASS + * + * PASS_MODULE_PARSER: Represents the parser module + * PASS_MODULE_RESCON: Represents the Resource Monitor module + * PASS_MODULE_RESMON: Represents the Resource Controller module + * PASS_MODULE_PMQOS: Represents the Scenario-based PMQoS module + * PASS_MODULE_CPUHP: Represents the CPU Hotplug Manager module + */ +enum pass_module_type { + PASS_MODULE_UNKNOWN = 0x0, + + PASS_MODULE_PARSER = 0x1, + PASS_MODULE_RESCON = 0x2, + PASS_MODULE_RESMON = 0x4, + PASS_MODULE_PMQOS = 0x8, + PASS_MODULE_CPUHP = 0x10, +}; + +/* * PASS Governor type * * --------------------------------------------------------------------------- -- 2.7.4 From e038674ba7b2182badbe09058840e7b77581caab Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Tue, 13 Feb 2018 15:31:06 +0900 Subject: [PATCH 08/16] pass: resmon: Add uevent-based resource monitor Resource-Monitor provides two monitoring method as following: - Timer-based resource monitor like polling method. There are both oneshot and periodic timer and it requires the interval as the mandatory. - Uevent-based resource monitor like interrupt method. It notifies the result of monitoring when receiving the uevent from Kernel. Add the support of uevent-based resource monitor which uses the libudev.h library in order to handle the uevent from kernel. [Function description] - Uevent-based resource monitor int pass_resmon_register_uevent() int pass_resmon_unregister_uevent() Change-Id: I3c9362298f5e096600989d5aaa2c6720346b9c5c Signed-off-by: Chanwoo Choi --- CMakeLists.txt | 1 + packaging/pass.spec | 1 + src/pass/pass-resmon-internal.h | 26 +++ src/pass/pass-resmon-source.c | 6 + src/pass/pass-resmon.c | 396 +++++++++++++++++++++++++++++++++++++++- src/pass/pass-resmon.h | 22 +++ src/pass/pass.h | 5 + 7 files changed, 449 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7d901be..f787e45 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -97,6 +97,7 @@ SET(PKG_MODULES dbus-1 gio-2.0 gio-unix-2.0 + libudev libtzplatform-config ) diff --git a/packaging/pass.spec b/packaging/pass.spec index 953166e..8c1ffc8 100644 --- a/packaging/pass.spec +++ b/packaging/pass.spec @@ -25,6 +25,7 @@ BuildRequires: pkgconfig(gio-2.0) BuildRequires: pkgconfig(gio-unix-2.0) BuildRequires: pkgconfig(glib-2.0) BuildRequires: pkgconfig(eventsystem) +BuildRequires: pkgconfig(libudev) BuildRequires: pkgconfig(libtzplatform-config) %description diff --git a/src/pass/pass-resmon-internal.h b/src/pass/pass-resmon-internal.h index 659f6c5..c45e145 100644 --- a/src/pass/pass-resmon-internal.h +++ b/src/pass/pass-resmon-internal.h @@ -19,6 +19,19 @@ #ifndef __PASS_RESMON_INTERNAL__ #define __PASS_RESMON_INTERNAL__ +#include + +/* + * enum resmon_type - Represents the type of resource-monitor. + * - RESMON_TIMER: Timer-based resource-monitor type. (Polling method) + * - RESMON_UEVENT: Uevent-based resource-monitor type. (Interrupt method) + */ +enum resmon_type { + RESMON_UNKNOWN = 0x0, + RESMON_TIMER = 0x1, + RESMON_UEVENT = 0x2, +}; + struct resmon; struct resmon_ops { @@ -27,11 +40,20 @@ struct resmon_ops { /* Callback of timer-based resource monitor */ int (*timer_handler)(struct resmon *monitor, void *result); + + /* Callback of uevent-based resource monitor */ + int (*uevent_handler)(struct resmon *monitor, void *result, + struct udev_device *dev); + const char *uevent_subsystem; + const char *uevent_devtype; }; struct resmon { struct pass_resmon *resmon; + /* Resource Monitor's type is either timer or uevent. */ + enum resmon_type type; + /* Resource Monitor's source type */ enum resmon_src_type src_type; @@ -50,6 +72,10 @@ struct resmon { enum resmon_timer_type timer_type; unsigned int timer_interval; guint timer_id; + + /* Private data of uevent-based resource-monitor */ + struct udev_monitor *udev_monitor; + guint udev_moniotr_fd; }; #endif /* __PASS_RESMON_INTERNAL__ */ diff --git a/src/pass/pass-resmon-source.c b/src/pass/pass-resmon-source.c index 754d854..a3a0b8e 100644 --- a/src/pass/pass-resmon-source.c +++ b/src/pass/pass-resmon-source.c @@ -176,12 +176,18 @@ struct resmon_ops thermal_src_ops = { .init = resmon_thermal_init, .exit = resmon_thermal_exit, .timer_handler = resmon_thermal_timer_handler, + .uevent_handler = NULL, + .uevent_subsystem = NULL, + .uevent_devtype = NULL, }; struct resmon_ops cpuhp_src_ops = { .init = resmon_cpuhp_init, .exit = resmon_cpuhp_exit, .timer_handler = resmon_cpuhp_timer_handler, + .uevent_handler = NULL, + .uevent_subsystem = NULL, + .uevent_devtype = NULL, }; struct resmon_ops *resmon_get_ops(enum resmon_src_type src_type) diff --git a/src/pass/pass-resmon.c b/src/pass/pass-resmon.c index d82791f..f1c85aa 100644 --- a/src/pass/pass-resmon.c +++ b/src/pass/pass-resmon.c @@ -16,6 +16,9 @@ * limitations under the License. */ +#include +#include + #include #include @@ -24,6 +27,9 @@ #include "pass-resmon.h" #include "pass-resmon-internal.h" +static struct udev *g_udev = NULL; +static int g_udev_count = 0; + extern struct resmon_ops *resmon_get_ops(enum resmon_src_type src_type); uint64 available_resmon_timer[] = { @@ -36,16 +42,41 @@ uint64 available_resmon_timer[] = { [PASS_RESOURCE_NONSTANDARD_ID] = 0 }; +uint64 available_resmon_uevent[] = { + [PASS_RESOURCE_UNKNOWN] = 0, + [PASS_RESOURCE_CPU_ID] = RESMON_SRC_THERMAL, + [PASS_RESOURCE_BUS_ID] = 0, + [PASS_RESOURCE_GPU_ID] = RESMON_SRC_THERMAL, + [PASS_RESOURCE_MEMORY_ID] = 0, + [PASS_RESOURCE_NONSTANDARD_ID] = 0 +}; + static bool resmon_is_supported(unsigned int res_type, + enum resmon_type type, enum resmon_src_type src_type) { - return !!(available_resmon_timer[res_type] & src_type); + switch (type) { + case RESMON_TIMER: + return !!(available_resmon_timer[res_type] & src_type); + case RESMON_UEVENT: + return !!(available_resmon_uevent[res_type] & src_type); + default: + return false; + } } static bool resmon_is_created(struct pass_resmon *resmon, + enum resmon_type type, enum resmon_src_type src_type) { - return !!(resmon->timer_state & src_type); + switch (type) { + case RESMON_TIMER: + return !!(resmon->timer_state & src_type); + case RESMON_UEVENT: + return !!(resmon->uevent_state & src_type); + default: + return false; + } } static gint __compare_monitor_type(gconstpointer data, gconstpointer input) @@ -59,10 +90,24 @@ static gint __compare_monitor_type(gconstpointer data, gconstpointer input) } static struct resmon *resmon_find_monitor(struct pass_resmon *resmon, + enum resmon_type type, enum resmon_src_type src_type) { - GList *node = g_list_find_custom(resmon->timer_list, &src_type, + GList *node; + + switch (type) { + case RESMON_TIMER: + node = g_list_find_custom(resmon->timer_list, &src_type, + __compare_monitor_type); + break; + case RESMON_UEVENT: + node = g_list_find_custom(resmon->uevent_list, &src_type, __compare_monitor_type); + break; + default: + return NULL; + } + if (!node) return NULL; return (struct resmon *)node->data; @@ -75,7 +120,7 @@ static int resmon_timer_add(struct resmon *monitor, unsigned int interval) int ret; /* Add new timer-based resmon to timer_list if it's not created */ - if (!resmon_is_created(resmon, monitor->src_type)) { + if (!resmon_is_created(resmon, RESMON_TIMER, monitor->src_type)) { resmon->timer_list = g_list_append(resmon->timer_list, (gpointer)monitor); @@ -213,14 +258,14 @@ int pass_resmon_register_timer(struct pass_resource *res, /* Check whether the resource monitor is supported or not */ res_type = res->config_data.res_type; - if (!resmon_is_supported(res_type, src_type)) { + if (!resmon_is_supported(res_type, RESMON_TIMER, src_type)) { _E("invalid value (res_type: %d, src_type: 0x%x)\n", res_type, src_type); return -EINVAL; } /* Prevent the monitoring of already required type */ - if (resmon_is_created(resmon, src_type)) { + if (resmon_is_created(resmon, RESMON_TIMER, src_type)) { _E("timer is already created (res_type: %d, src_type: 0x%x)\n", res_type, src_type); return -EBUSY; @@ -277,7 +322,7 @@ int pass_resmon_unregister_timer(struct pass_resource *res, if (!res || src_type == 0) return -EINVAL; - monitor = resmon_find_monitor(&res->resmon, src_type); + monitor = resmon_find_monitor(&res->resmon, RESMON_TIMER, src_type); if (!monitor) { _E("failed to find monitor (res_type: %d, src_type: 0x%x)\n", res->config_data.res_type, src_type); @@ -316,7 +361,7 @@ int pass_resmon_update_timer_interval(struct pass_resource *res, return -EINVAL; /* Find registered timer-based resmon */ - monitor = resmon_find_monitor(&res->resmon, src_type); + monitor = resmon_find_monitor(&res->resmon, RESMON_TIMER, src_type); if (!monitor) { _E("failed to find monitor (res_type:%d, src_type: 0x%x)\n", res->config_data.res_type, src_type); @@ -331,6 +376,316 @@ int pass_resmon_update_timer_interval(struct pass_resource *res, return 0; } +static gboolean resmon_uevent_func(gint fd, GIOCondition cond, void *data) +{ + struct resmon *monitor = data; + struct pass_resmon *resmon = monitor->resmon; + struct pass_resource *res + = container_of(resmon, struct pass_resource, resmon);; + struct udev_device *dev; + int ret; + + dev = udev_monitor_receive_device(monitor->udev_monitor); + if (!dev) + return G_SOURCE_CONTINUE; + + dev = udev_device_ref(dev); + if (!dev) { + _E("failed to refer uevent device " \ + "(res_name:%s, src_type: 0x%x)\n", + res->config_data.res_name, monitor->src_type); + return G_SOURCE_CONTINUE; + } + + /* Collect resource data according to enum resmon_src_type */ + if (monitor->ops && monitor->ops->uevent_handler) { + ret = monitor->ops->uevent_handler(monitor, monitor->result, dev); + if (ret < 0) { + _E("failed to invoke uevent_handler " \ + "(res_name:%s, src_type: 0x%x)\n", + res->config_data.res_name, monitor->src_type); + return G_SOURCE_CONTINUE; + } + } + + /* Pass collected resource data (result) to resmon's user */ + if (monitor->user_func) { + ret = (monitor->user_func)(monitor->result, monitor->user_data); + if (ret < 0) { + _E("failed to invoke user_func " \ + "(res_name:%s, src_type: 0x%x)\n", + res->config_data.res_name, monitor->src_type); + return G_SOURCE_CONTINUE; + } + } + + udev_device_unref(dev); + + return G_SOURCE_CONTINUE; +} + +#define UDEV_MONITOR_SIZE (128 * 1024) +static int resmon_uevent_add(struct resmon *monitor) +{ + struct udev_monitor *udev_monitor; + struct pass_resmon *resmon = monitor->resmon; + struct pass_resource *res + = container_of(resmon, struct pass_resource, resmon);; + guint gfd; + int ret; + int fd; + + /* Initialize the udev-monitor instance and set buffer size */ + udev_monitor = udev_monitor_new_from_netlink(g_udev, "kernel"); + if (!udev_monitor) { + _E("failed to create the udev monitor " \ + "(res_name:%s, src_type: 0x%x)\n", + res->config_data.res_name, monitor->src_type); + return -EINVAL; + } + + /* + * At least, either uevent_subsystem or uevet_devtype should be not NULL + */ + if (!monitor->ops->uevent_subsystem && !monitor->ops->uevent_devtype) { + _E("failed to add filter due to subsystem/devtype are NULL " \ + "(res_name:%s, src_type: 0x%x)\n", + res->config_data.res_name, monitor->src_type); + return -EINVAL; + } + + /* Update the kernel's subsystem and devtype for filtering */ + ret = udev_monitor_filter_add_match_subsystem_devtype( + udev_monitor, + monitor->ops->uevent_subsystem, + monitor->ops->uevent_devtype); + if (ret < 0) { + _E("failed to add filter with subsystem and devtype " \ + "(res_name:%s, src_type: 0x%x)\n", + res->config_data.res_name, monitor->src_type); + goto err_udev_monitor; + } + + ret = udev_monitor_filter_update(udev_monitor); + if (ret < 0) { + _E("failed to update filter with subsystem(%s) & devtype(%s) " \ + "(res_name:%s, src_type: 0x%x)\n", + monitor->ops->uevent_subsystem, + monitor->ops->uevent_devtype, + res->config_data.res_name, monitor->src_type); + goto err_udev_monitor; + } + + /* Register callback to subscribe the event */ + fd = udev_monitor_get_fd(udev_monitor); + if (fd <= 0) { + _E("failed to get file descriptor of udev monitor " \ + "(res_name:%s, src_type: 0x%x)\n", + res->config_data.res_name, monitor->src_type); + ret = -EINVAL; + goto err_udev_monitor; + } + + gfd = g_unix_fd_add(fd, G_IO_IN, resmon_uevent_func, monitor); + if (gfd <= 0) { + _E("failed to add uevent-based callback function " \ + "(res_name:%s, src_type: 0x%x)\n", + res->config_data.res_name, monitor->src_type); + ret = -EINVAL; + goto err_udev_monitor; + } + + /* + * Invoke the .init of each RESMON_SRC_* source + * before enabling the udev monitoring. + */ + if (monitor->ops && monitor->ops->init) { + ret = monitor->ops->init(monitor); + if (ret < 0) { + _E("failed to invoke .init of resmon source " \ + "(res_name:%s, src_type: 0x%x)\n", + res->config_data.res_name, monitor->src_type); + return ret; + } + } + + /* Bind udev-monitor to enable the udev monitoring */ + ret = udev_monitor_enable_receiving(udev_monitor); + if (ret < 0) { + _E("failed to bind udev monitor for receving the event " \ + "(res_name:%s, src_type: 0x%x)\n", + res->config_data.res_name, monitor->src_type); + ret = -EAGAIN; + goto err_udev_monitor_fd; + } + + /* Add new uevent-based resmon to uevent_list */ + resmon->uevent_list = g_list_append(resmon->uevent_list, + (gpointer)monitor); + resmon->uevent_state |= monitor->src_type; + + monitor->udev_monitor = udev_monitor;; + monitor->udev_moniotr_fd = gfd; + + return 0; + +err_udev_monitor_fd: + g_source_remove(gfd); + gfd = -1; +err_udev_monitor: + udev_monitor_unref(udev_monitor); + udev_monitor = NULL; + + return ret; +} + +static int resmon_uevent_delete(struct resmon *monitor) +{ + struct pass_resmon *resmon = monitor->resmon; + struct pass_resource *res + = container_of(resmon, struct pass_resource, resmon);; + int ret; + + /* Invoke the .exit of each RESMON_SRC_* source */ + if (monitor->ops && monitor->ops->exit) { + ret = monitor->ops->exit(monitor); + if (ret < 0) { + _E("failed to invoke .exit of resmon source " \ + "(res_name:%s, src_type: 0x%x)\n", + res->config_data.res_name, monitor->src_type); + return ret; + } + } + + udev_monitor_unref(monitor->udev_monitor); + g_source_remove(monitor->udev_moniotr_fd); + monitor->udev_monitor = NULL; + monitor->udev_moniotr_fd = -1; + + /* Delete the uevent-based resmon from uevent_list */ + resmon->uevent_state &= ~(monitor->src_type); + resmon->uevent_list = g_list_remove(resmon->uevent_list, + (gpointer)monitor); + + return 0; +} + +/* + * pass_resmon_register_uevent - Register uevent-based resource monitor + * + * @res: the instance of struct pass_resource. + * @src_type: the type of resource monitor among 'enum resmon_src_type'. + * @user_func: the callback function when timer is expired. + * @user_data: the passed user user_data. + */ +int pass_resmon_register_uevent(struct pass_resource *res, + enum resmon_src_type src_type, + int (*user_func)(void *result, void *user_data), + void *user_data) +{ + struct pass_resmon *resmon; + struct resmon *monitor; + int res_type; + int ret; + + if (!res || src_type == 0 || user_func == NULL) { + _E("invalid parameter\n"); + return -EINVAL; + } + + resmon = &res->resmon; + + /* Check whether the resource monitor is supported or not */ + res_type = res->config_data.res_type; + if (!resmon_is_supported(res_type, RESMON_UEVENT, src_type)) { + _E("src_type is not supported (res_name: %s, src_type: 0x%x)\n", + res->config_data.res_name, src_type); + return -EINVAL; + } + + /* Prevent the monitoring of already required type */ + if (resmon_is_created(resmon, RESMON_UEVENT, src_type)) { + _E("uevent-based resmon is already created "\ + "(res_name: %s, src_type: 0x%x)\n", + res->config_data.res_name, src_type); + return -EBUSY; + } + + /* Allocate the memory for resource monitor */ + monitor = calloc(1, sizeof(struct resmon)); + if (!monitor) + return -ENOMEM; + + monitor->resmon = resmon; + monitor->src_type = src_type; + monitor->user_func = user_func; + monitor->user_data = user_data; + + /* Get instance of struct resmon_ops accoring to resmon_src_type */ + monitor->ops = resmon_get_ops(src_type); + if (!monitor->ops) { + _E("failed to get resmon_ops (res_name: %s, type: 0x%x)\n", + res->config_data.res_name, src_type); + ret = -EINVAL; + goto err; + } + + /* Add uevent-based resource monitor */ + ret = resmon_uevent_add(monitor); + if (ret < 0) { + _E("failed to add monitor (res_name: %s, type: 0x%x)\n", + res->config_data.res_name, src_type); + goto err; + } + + return 0; + +err: + free(monitor); + monitor = NULL; + + return ret; +} + +/* + * pass_resmon_unregister_uevent - Unregister uevent-based resource monitor + * + * @res: the instance of struct pass_resource + * @src_type: the type of resource monitor among 'enum resmon_src_type'. + */ +int pass_resmon_unregister_uevent(struct pass_resource *res, + enum resmon_src_type src_type) +{ + struct resmon *monitor; + int ret; + + if (!res || src_type == 0) { + _E("invalid parameter\n"); + return -EINVAL; + } + + monitor = resmon_find_monitor(&res->resmon, RESMON_UEVENT, src_type); + if (!monitor) { + _E("failed to find monitor (res_name: %d, type: 0x%x)\n", + res->config_data.res_name, src_type); + return -EINVAL; + } + + /* Delete uevent-based resource monitor */ + ret = resmon_uevent_delete(monitor); + if (ret < 0) { + _E("failed to delete monitor (res_name: %d, type: 0x%x)\n", + res->config_data.res_name, src_type); + } + + /* Free the memory of resource monitor */ + free(monitor); + monitor = NULL; + + return 0; + +} + int pass_resmon_init(struct pass_resource *res) { struct pass_resmon *resmon; @@ -345,6 +700,21 @@ int pass_resmon_init(struct pass_resource *res) resmon->timer_list = NULL; resmon->timer_state = 0; + /* Initialize the uevent-related variables */ + resmon->uevent_list = NULL; + resmon->uevent_state = 0; + + if (g_udev_count == 0) { + g_udev = udev_new(); + if (!g_udev) { + _E("failed to create udev for uevent-based monitor\n"); + return -ENOMEM; + } + } else { + g_udev = udev_ref(g_udev); + } + g_udev_count++; + resmon->state = PASS_ON; return 0; @@ -365,6 +735,16 @@ int pass_resmon_exit(struct pass_resource *res) resmon->timer_list = NULL; resmon->timer_state = 0; + /* Free the uevent-related variables */ + g_list_free(resmon->uevent_list); + resmon->uevent_list = NULL; + resmon->uevent_state = 0; + + udev_unref(g_udev); + g_udev_count--; + if (g_udev_count == 0) + g_udev = NULL; + resmon->state = PASS_OFF; return 0; diff --git a/src/pass/pass-resmon.h b/src/pass/pass-resmon.h index b3fb8fc..84768b6 100644 --- a/src/pass/pass-resmon.h +++ b/src/pass/pass-resmon.h @@ -107,4 +107,26 @@ int pass_resmon_update_timer_interval(struct pass_resource *res, enum resmon_src_type src_type, int timer_interval); +/* + * pass_resmon_register_uevent - Register uevent-based resource monitor + * + * @res: the instance of struct pass_resource. + * @src_type: the type of resource monitor among 'enum resmon_src_type'. + * @func: the callback function when receive the uevent. + * @user_data: the passed user user_data. + */ +int pass_resmon_register_uevent(struct pass_resource *res, + enum resmon_src_type src_type, + int (*func)(void *result, void *user_data), + void *user_data); + +/* + * pass_resmon_unregister_uevent - Unregister uevent-based resource monitor + * + * @res: the instance of struct pass_resource + * @src_type: the type of resource monitor among 'enum resmon_src_type'. + */ +int pass_resmon_unregister_uevent(struct pass_resource *res, + enum resmon_src_type src_type); + #endif /* __PASS_RESMON__ */ diff --git a/src/pass/pass.h b/src/pass/pass.h index 398eb1f..59b2265 100644 --- a/src/pass/pass.h +++ b/src/pass/pass.h @@ -210,12 +210,17 @@ struct pass_rescon { * @state: the state of ResMon (either enabled or disabled). * @timer_list: the list of required timer-based resource monitor. * @timer_state: the state of all timer-based resource-monitor. + * @uevent_list: the list of required uevent-based resource monitor. + * @uevent_state: the state of all uevent-based resource-monitor. */ struct pass_resmon { enum pass_state state; GList *timer_list; uint64 timer_state; + + GList *uevent_list; + uint64 uevent_state; }; /* -- 2.7.4 From cbc70905a1583f9e3751f64124eb89c64733a97c Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Thu, 8 Mar 2018 20:00:30 +0900 Subject: [PATCH 09/16] pass: resmon: Add uevent-based RESMON_SRC_THERMAL source RESMON_SRC_THERMAL indicates the thermal monitoring for h/w resource such as CPU, GPU and so on. The uevent-based RESMON_SRC_THERMAL uses the uevent from kernel. When receving the uevent, RESMON_SRC_THERMAL reads the temperature of h/w resource and then pass it to consumer of resource monitor. It is standard Linux Kernel interface which provides the thermal information through thermal framework of Linux Kernel. [Description of RESMON_SRC_THERMAL resource-monitor] - The kind of resource data : temperature of h/w resource (unit is centigrade) : (e.g., 47000 means 47.0 degrees centigrade) - The 'subsystem' name for uevent-based RESMON_SRC_THERMAL : "thermal" - Path of RESMON_SRC_THERMAL monitoring : cat /sys/class/thermal/thermal_zone[X]/temp - Result of RESMON_SRC_THERMAL monitoring $ cat /sys/class/thermal/thermal_zone0/temp 47000 For exmaple on TM2 board, : thermal_zone0 for ARM big core on TM2 : thermal_zone2 for ARM GPU on TM2 : thermal_zone3 for ARM LITTLE core on TM2 Change-Id: Ib66abc11b16c1e17084bc9b89e06c4fb81e6f897 Signed-off-by: Chanwoo Choi --- src/pass/pass-resmon-source.c | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/src/pass/pass-resmon-source.c b/src/pass/pass-resmon-source.c index a3a0b8e..5da41bd 100644 --- a/src/pass/pass-resmon-source.c +++ b/src/pass/pass-resmon-source.c @@ -74,6 +74,28 @@ static int resmon_thermal_timer_handler(struct resmon *monitor, void *result) return 0; } +static int resmon_thermal_uevent_handler(struct resmon *monitor, void *result, + struct udev_device *dev) +{ + struct pass_resmon *resmon = monitor->resmon; + struct pass_resource *res = + container_of(resmon, struct pass_resource, resmon); + struct resmon_result_src_thermal *thermal_result = result; + int temp; + + if (!thermal_result) + return -ENOMEM; + + /* Get temperature of h/w resource */ + temp = pass_get_temp(res); + if (temp < 0) + return temp; + + thermal_result->temp = temp; + + return 0; +} + /* RESMON_SRC_CPUHP */ #define RESMON_SRC_CPUHP_COUNT 20 static int resmon_cpuhp_init(struct resmon *monitor) @@ -176,8 +198,8 @@ struct resmon_ops thermal_src_ops = { .init = resmon_thermal_init, .exit = resmon_thermal_exit, .timer_handler = resmon_thermal_timer_handler, - .uevent_handler = NULL, - .uevent_subsystem = NULL, + .uevent_handler = resmon_thermal_uevent_handler, + .uevent_subsystem = "thermal", .uevent_devtype = NULL, }; -- 2.7.4 From 92cf50aea8cbab234d79284b0a6aeec55607bef6 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Fri, 16 Mar 2018 13:56:48 +0900 Subject: [PATCH 10/16] pass: cpuhp: Fix error when exiting the CPUHP module If resource configuration doesn't specify the use of CPUHP, pass_cpuhp_init() have to be skipped. But, the commit eac3132aeb70 ("pass: cpuhp: Replace legacy monitoring with RESMON (Resource Monitor)") didn't handle it rightly. If CPU h/w resource doesn't use the CPUHP governor (radiation or step cpuhp governor), have to skip the exit sequence of CPUHP. Change-Id: I2c97873eebfa1e441807b615637786ebda5ed8b8 Fixes: eac3132aeb70 ("pass: cpuhp: Replace legacy monitoring with RESMON (Resource Monitor) Signed-off-by: Chanwoo Choi --- src/pass/pass-cpuhp.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/pass/pass-cpuhp.c b/src/pass/pass-cpuhp.c index df10e96..5d55091 100644 --- a/src/pass/pass-cpuhp.c +++ b/src/pass/pass-cpuhp.c @@ -467,6 +467,21 @@ int pass_cpuhp_exit(struct pass_resource *res) if (!res) return -EINVAL; + /* + * FIXME: The h/w resource configuration specifies the gov_type. + * When gov_type is either PASS_GOV_STEP(2) or PASS_GOV_RADIATION(3), + * CPUHP should be enabled. On later, each PASS module have to define + * the separate property in configuration which indicates either this + * module is used or not. + */ + switch (res->config_data.gov_type) { + case PASS_GOV_STEP: + case PASS_GOV_RADIATION: + break; + default: + return 0; + }; + cpuhp = &res->cpuhp; if (!cpuhp->governor || !cpuhp->governor->exit) -- 2.7.4 From 877e72971faca1735dbac8914035488a5b10b86b Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Fri, 30 Mar 2018 15:52:57 +0900 Subject: [PATCH 11/16] pass: resmon: Save updated timer interval RESMON provides the update function of 'timer interval' on the runtime. Should save the updated 'timer interval' value to 'struct resmon'. Change-Id: Ice3deb34889343dfbcb555cb31fcfff37d6cf2a6 Signed-off-by: Chanwoo Choi --- src/pass/pass-resmon.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/pass/pass-resmon.c b/src/pass/pass-resmon.c index f1c85aa..9736870 100644 --- a/src/pass/pass-resmon.c +++ b/src/pass/pass-resmon.c @@ -150,6 +150,8 @@ static int resmon_timer_add(struct resmon *monitor, unsigned int interval) return -EPERM; } + monitor->timer_interval = interval; + return 0; } @@ -368,6 +370,9 @@ int pass_resmon_update_timer_interval(struct pass_resource *res, return -EINVAL; } + if (monitor->timer_interval == timer_interval) + return 0; + /* Update new interval of timer-based resmon */ ret = resmon_timer_add(monitor, timer_interval); if (ret < 0) -- 2.7.4 From 67b78e48c3faaf7841f0022a4b485f7afcd2c084 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Wed, 28 Mar 2018 21:31:59 +0900 Subject: [PATCH 12/16] pmqos: Clean-up code and remove unnecessary function Clean-up code and just remove unnecessary get_methods_from_conf(). Change-Id: I158603675172f32d01151b7269a73ba69d002258 Signed-off-by: Chanwoo Choi --- src/pmqos/pmqos.c | 126 ++++++++++++++++++++++-------------------------------- 1 file changed, 52 insertions(+), 74 deletions(-) diff --git a/src/pmqos/pmqos.c b/src/pmqos/pmqos.c index b2a04d6..6a6a89c 100644 --- a/src/pmqos/pmqos.c +++ b/src/pmqos/pmqos.c @@ -1,7 +1,7 @@ /* * PASS (Power Aware System Service) * - * Copyright (c) 2012 - 2013 Samsung Electronics Co., Ltd. + * Copyright (c) 2012 - 2018 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. @@ -43,8 +43,7 @@ #define DBUS_PMQOS_PATH "/Org/Tizen/System/Pass/Pmqos" static SystemPassPmqos *g_gdbus_instance = NULL; -static struct pmqos_scenario *g_pmqos_scenarios = NULL; -static bool g_initialized = false; +static struct pmqos_scenario *g_pmqos = NULL; struct pmqos_cpu { char name[NAME_MAX]; @@ -89,7 +88,7 @@ static gboolean dbus_cb_pmqos_start(SystemPassPmqos *obj, int ret = 0; int booting_done = 1; - if (g_initialized) + if (g_pmqos) _I("PASS PMQoS is already started\n"); else ret = pmqos_init_done(&booting_done, NULL); @@ -107,7 +106,7 @@ static gboolean dbus_cb_pmqos_start(SystemPassPmqos *obj, static gboolean dbus_cb_pmqos_stop(SystemPassPmqos *obj, GDBusMethodInvocation *invoc, gpointer user_data) { - if (!g_initialized) + if (!g_pmqos) _I("PASS PMQoS is already stopped\n"); else pmqos_free(); @@ -128,17 +127,17 @@ static gboolean dbus_cb_pmqos_legacy_scenario(SystemPassPmqos *obj, int i, ret = 0; gboolean ret_out = TRUE; - if (!g_initialized) { + if (!g_pmqos) { _E("PASS PMQoS is stopped\n"); ret_out = FALSE; goto out; } else { name_from = g_dbus_method_invocation_get_method_name(invoc); ret = -ENOTSUP; - if (g_pmqos_scenarios != NULL) { - for (i = 0; i < g_pmqos_scenarios->num; i++) { - name = g_pmqos_scenarios->list[i].name; - support = g_pmqos_scenarios->list[i].support; + if (g_pmqos) { + for (i = 0; i < g_pmqos->num; i++) { + name = g_pmqos->list[i].name; + support = g_pmqos->list[i].support; if (!strncmp(name, name_from, strlen(name)) && support) { ret = 0; @@ -224,9 +223,9 @@ static int set_pmqos(const char *name, int val) char scenario[100]; if (val) - _D("Set pm scenario : [Lock ]%s", name); + _D("Set pm scenario : Lock \'%s\'", name); else - _D("Set pm scenario : [Unlock]%s", name); + _D("Set pm scenario : Unlock \'%s\'", name); snprintf(scenario, sizeof(scenario), "%s%s", name, (val ? "Lock" : "Unlock")); device_notify(DEVICE_NOTIFIER_PMQOS, (void *)scenario); @@ -392,79 +391,56 @@ err: return ret; } -static int get_methods_from_conf(const char *path, - struct pmqos_scenario **pmqos_scenarios) +static int pmqos_init_done(void *data, void *user_data) { - struct pmqos_scenario *scenarios; - int i, ret, count = 0; + int *booting_done; + int ret; + int i; + + /* + * As a callback function for the booting-done event, it is notified + * with the result of the booting_finished() function by using the + * first argument, void *data. When it is successfully booted up, an int + * value, 1 is passed with the first argument. + */ + booting_done = (int *)data; + if (!(*booting_done)) + return -EINVAL; - scenarios = malloc(sizeof(*scenarios)); - if (scenarios == NULL) + if (g_pmqos) + return 0; + + g_pmqos = calloc(1, sizeof(*g_pmqos)); + if (!g_pmqos) return -ENOMEM; - /* get pmqos table from conf */ - ret = pmqos_get_scenario(path, scenarios); + ret = pmqos_get_scenario(PMQOS_CONF_PATH, g_pmqos); if (ret < 0) { - pmqos_put_scenario(scenarios); + _E("failed to get PMQoS scenario\n"); return ret; } - /* if do not support scenario */ - if (!scenarios->support) - return 0; - - /* if do not have scenarios */ - if (scenarios->num <= 0) - return 0; + if (!g_pmqos->support || g_pmqos->num <= 0) { + ret = pmqos_put_scenario(g_pmqos); + if (ret < 0) + _E("failed to put PMQoS scenario\n"); + return ret; + } - /* set edbus_methods structure */ - for (i = 0; i < scenarios->num; ++i) { - /* if this scenario does not support */ - if (!scenarios->list[i].support) - continue; + for (i = 0; i < g_pmqos->num; ++i) + if (g_pmqos->list[i].support) + _I("Support \'%s\' scenario", g_pmqos->list[i].name); - count++; - _I("support [%s] scenario", scenarios->list[i].name); - } /* * Set maximum timeout for the sceanrio by using the parsed data from * pass-pmqos.conf. But, if there is no setting from pass-pmqos.conf * pmqos uses the default timeout value (3000 millisecond). */ - if (scenarios->max_timeout_ms) - unlock_max_timeout_ms = scenarios->max_timeout_ms; + if (g_pmqos->max_timeout_ms) + unlock_max_timeout_ms = g_pmqos->max_timeout_ms; else unlock_max_timeout_ms = DEFAULT_PMQOS_TIMER; - ret = count; - *pmqos_scenarios = scenarios; - - return ret; -} - -static int pmqos_init_done(void *data, void *user_data) -{ - int *booting_done; - int size; - - /* - * As a callback function for the booting-done event, it is notified - * with the result of the booting_finished() function by using the - * first argument, void *data. When it is successfully booted up, an int - * value, 1 is passed with the first argument. - */ - booting_done = (int *)data; - if (!(*booting_done)) - return -EINVAL; - - size = get_methods_from_conf(PMQOS_CONF_PATH, &g_pmqos_scenarios); - if (size < 0) { - _E("failed to load configuration file(%s)", PMQOS_CONF_PATH); - return -EINVAL; - } - - g_initialized = true; - return 0; } @@ -476,26 +452,28 @@ static void pmqos_init(void *data) static void pmqos_free(void) { - /* Assign 0 to static variables */ + int ret; + memset(&unlock_timer_start_st, 0, sizeof(struct timespec)); memset(&unlock_timer_end_st, 0, sizeof(struct timespec)); memset(&unlock_timer_owner, 0, sizeof(struct pmqos_cpu)); - /* Clean up pmqos_head */ if (pmqos_head) { g_list_free_full(pmqos_head, free); pmqos_head = NULL; } - free(g_pmqos_scenarios); - g_pmqos_scenarios = NULL; + ret = pmqos_put_scenario(g_pmqos); + if (ret < 0) + _E("failed to put PMQoS scenario\n"); + + free(g_pmqos); + g_pmqos = NULL; if (g_unlock_timeout_id) { g_source_remove(g_unlock_timeout_id); g_unlock_timeout_id = 0; } - - g_initialized = false; } static void pmqos_exit(void *data) @@ -520,7 +498,7 @@ static int pmqos_probe(void *data) int ret = 0; g_gdbus_instance = pass_gdbus_get_instance_pmqos(); - if (g_gdbus_instance == NULL) { + if (!g_gdbus_instance) { _E("cannot get a dbus instance for the %s interface\n", DBUS_PMQOS_INTERFACE); return -ENOSYS; -- 2.7.4 From 51762660ab87bf1b2eabe0f20546638f2560a7e2 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Thu, 22 Mar 2018 17:02:45 +0900 Subject: [PATCH 13/16] core: device-notifier: Remove unused notifier definition DEVICE_NOTIFIER_POWER_OFF notifier definition is not used. Remove it. Change-Id: I30ab609c55aa0f43b8b1a4fefcb6db2a716161a5 Signed-off-by: Chanwoo Choi --- include/pass/device-notifier.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/pass/device-notifier.h b/include/pass/device-notifier.h index 77c571f..f045abc 100644 --- a/include/pass/device-notifier.h +++ b/include/pass/device-notifier.h @@ -25,7 +25,6 @@ enum device_notifier_type { DEVICE_NOTIFIER_BOOTING_DONE, DEVICE_NOTIFIER_PMQOS, - DEVICE_NOTIFIER_POWEROFF, DEVICE_NOTIFIER_MAX, }; -- 2.7.4 From fedc0928b15f6ac1917d9be4186078e1730feb26 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Thu, 22 Mar 2018 18:31:31 +0900 Subject: [PATCH 14/16] core: device-notifier: Add new DEVICE_NOTIFIER_THERMAL DEVICE_NOTIFIER_THERMAL is used to notify the thermal level. Change-Id: I46e19f32b347ce70626876be5f8e8f7993795879 Signed-off-by: Chanwoo Choi --- include/pass/device-notifier.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/pass/device-notifier.h b/include/pass/device-notifier.h index f045abc..d95a6db 100644 --- a/include/pass/device-notifier.h +++ b/include/pass/device-notifier.h @@ -25,6 +25,7 @@ enum device_notifier_type { DEVICE_NOTIFIER_BOOTING_DONE, DEVICE_NOTIFIER_PMQOS, + DEVICE_NOTIFIER_THERMAL, DEVICE_NOTIFIER_MAX, }; -- 2.7.4 From f770e15a4e52ac4c7b4b6aedcf977a7a32ee1fbd Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Tue, 20 Mar 2018 16:59:34 +0900 Subject: [PATCH 15/16] core: gdbus: Add new gdbus function to send broadcast signal Add new following new function in order to send broadcast signal through d-bus interface: - int pass_gdbus_send_broadcast_signal(char *path, char *interface, char *method, GVariant *arg) Change-Id: I4e08a984f234fb4557e94f446e2d0aea86afc768 Signed-off-by: Chanwoo Choi --- include/pass/gdbus-util.h | 4 ++++ src/core/gdbus-util.c | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/include/pass/gdbus-util.h b/include/pass/gdbus-util.h index 306e859..f6be0b9 100644 --- a/include/pass/gdbus-util.h +++ b/include/pass/gdbus-util.h @@ -19,6 +19,7 @@ #ifndef __GDBUS_UTIL_H__ #define __GDBUS_UTIL_H__ +#include #include #include @@ -51,6 +52,9 @@ int pass_gdbus_connect_signal(gpointer instance, int num_signals, struct pass_gdbus_signal_info *signal_infos); void pass_gdbus_disconnect_signal(gpointer instance, int num_signals, struct pass_gdbus_signal_info *signal_infos); +int pass_gdbus_send_broadcast_signal(char *path, char *interface, + char *method, GVariant *arg); + SystemPassCore *pass_gdbus_get_instance_core(void); void pass_gdbus_put_instance_core(SystemPassCore **instance); SystemPassPmqos *pass_gdbus_get_instance_pmqos(void); diff --git a/src/core/gdbus-util.c b/src/core/gdbus-util.c index 42d21b8..6a672db 100644 --- a/src/core/gdbus-util.c +++ b/src/core/gdbus-util.c @@ -188,6 +188,45 @@ void pass_gdbus_disconnect_signal(gpointer instance, int num_signals, } } +int pass_gdbus_send_broadcast_signal(char *path, char *interface, char *method, + GVariant *arg) +{ + GDBusMessage *message = NULL; + GError *err = NULL; + int ret; + + if (!path || !interface || !method || !arg) { + _E("invalid parameter\n"); + return -EINVAL; + } + + if (!g_dbus_sys_conn) { + _E("cannot get the dbus connection to system message bus\n"); + return -ENOSYS; + } + + /* Make the dbus message to send broadcast signal */ + message = g_dbus_message_new_signal(path, interface, method); + if (!message) { + _E("failed to allocate new %s.%s signal", interface, method); + return -EPERM; + } + g_dbus_message_set_body(message, arg); + + /* Send broadcast signal */ + ret = g_dbus_connection_send_message(g_dbus_sys_conn, message, + G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, &err); + if (!ret) { + _E("failed to broadcast [%s][%d]", method, arg); + g_object_unref(message); + return -ECOMM; + } + + g_object_unref(message); + + return 0; +} + static void put_instance(gpointer *instance) { g_object_unref(*instance); -- 2.7.4 From bc00af81b9b9376069e66fbd9eb68f6988a83240 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Tue, 20 Mar 2018 15:29:31 +0900 Subject: [PATCH 16/16] scripts: Add pass-thermal.conf configuration Thermal Monitor is able to define the multiple scenario indicating the thermal status. Thermal Monitor uses the defined scenarios when reporting the current system status according to raw temperature. pass-thermal.conf contains the supported scenarios of Themral Monitor. It will be located in "/etc/pass/pass-thermal.conf" Change-Id: I98e4a109b47f1a4d398dfbf1cc4cdb08efa596e7 Signed-off-by: Chanwoo Choi --- CMakeLists.txt | 1 + packaging/pass.spec | 1 + scripts/pass-thermal.conf | 25 +++++++++++++++++++++++++ 3 files changed, 27 insertions(+) create mode 100644 scripts/pass-thermal.conf diff --git a/CMakeLists.txt b/CMakeLists.txt index f787e45..26cab5a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -156,6 +156,7 @@ TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${pkgs_LDFLAGS} "-ldl" "-lm") INSTALL(TARGETS ${PROJECT_NAME} DESTINATION bin) INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/scripts/${PROJECT_NAME}.conf DESTINATION /etc/dbus-1/system.d) INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/scripts/pass-pmqos.conf DESTINATION /etc/pass) +INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/scripts/pass-thermal.conf DESTINATION /etc/pass) CONFIGURE_FILE(${PROJECT_NAME}.pc.in ${PROJECT_NAME}.pc @ONLY) INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.pc DESTINATION ${LIB_INSTALL_DIR}/pkgconfig) diff --git a/packaging/pass.spec b/packaging/pass.spec index 8c1ffc8..f274516 100644 --- a/packaging/pass.spec +++ b/packaging/pass.spec @@ -86,6 +86,7 @@ systemctl daemon-reload %license LICENSE %config %{_sysconfdir}/dbus-1/system.d/%{daemon_name}.conf %config %{_sysconfdir}/pass/pass-pmqos.conf +%config %{_sysconfdir}/pass/pass-thermal.conf %{_bindir}/%{daemon_name} %{_unitdir}/multi-user.target.wants/%{daemon_name}.service %{_unitdir}/%{daemon_name}.service diff --git a/scripts/pass-thermal.conf b/scripts/pass-thermal.conf new file mode 100644 index 0000000..ad0f02d --- /dev/null +++ b/scripts/pass-thermal.conf @@ -0,0 +1,25 @@ +[thermal] +# set to "yes" thermal_support (Default value is no) +# set thermal_number_of_scenario to be tested +thermal_support=yes +thermal_number_of_scenario=4 + +# describe the scenario section as follows +#[Scenario0] +#name=ReleaseAction +#support=yes +[thermal.scenario0] +name=ReleaseAction +support=yes + +[thermal.scenario1] +name=WarningAction +support=yes + +[thermal.scenario2] +name=LimitAction +support=yes + +[thermal.scenario3] +name=ShutdownAction +support=yes -- 2.7.4