pass: resmon: Add new Resource Monitor (resmon) module 46/170446/3
authorChanwoo Choi <cw00.choi@samsung.com>
Fri, 2 Feb 2018 05:14:28 +0000 (14:14 +0900)
committerChanwoo Choi <cw00.choi@samsung.com>
Thu, 15 Mar 2018 04:45:08 +0000 (13:45 +0900)
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 <cw00.choi@samsung.com>
CMakeLists.txt
include/pass/common.h
src/pass/pass-resmon-internal.h [new file with mode: 0644]
src/pass/pass-resmon.c [new file with mode: 0644]
src/pass/pass-resmon.h [new file with mode: 0644]
src/pass/pass.c
src/pass/pass.h

index 6a40a6a..c13750f 100644 (file)
@@ -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
index 46c7490..9de28a3 100644 (file)
@@ -26,6 +26,9 @@
 #include <stdbool.h>
 #include <unistd.h>
 
+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 (file)
index 0000000..659f6c5
--- /dev/null
@@ -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 (file)
index 0000000..702b1de
--- /dev/null
@@ -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 <pass/log.h>
+#include <pass/hal/hal.h>
+
+#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 (file)
index 0000000..a046854
--- /dev/null
@@ -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__ */
index a2d7a0d..f93d27a 100644 (file)
@@ -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");
index 55f4fbb..58fcf20 100644 (file)
@@ -24,6 +24,7 @@
 #include <stdio.h>
 #include <glib.h>
 
+#include <pass/common.h>
 #include <pass/log.h>
 
 #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;
 };