pass: resmon: Add uevent-based resource monitor 66/172666/4
authorChanwoo Choi <cw00.choi@samsung.com>
Tue, 13 Feb 2018 06:31:06 +0000 (15:31 +0900)
committerChanwoo Choi <cw00.choi@samsung.com>
Fri, 16 Mar 2018 06:19:37 +0000 (15:19 +0900)
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 <cw00.choi@samsung.com>
CMakeLists.txt
packaging/pass.spec
src/pass/pass-resmon-internal.h
src/pass/pass-resmon-source.c
src/pass/pass-resmon.c
src/pass/pass-resmon.h
src/pass/pass.h

index 7d901be..f787e45 100644 (file)
@@ -97,6 +97,7 @@ SET(PKG_MODULES
        dbus-1
        gio-2.0
        gio-unix-2.0
+       libudev
        libtzplatform-config
 )
 
index 953166e..8c1ffc8 100644 (file)
@@ -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
index 659f6c5..c45e145 100644 (file)
 #ifndef __PASS_RESMON_INTERNAL__
 #define __PASS_RESMON_INTERNAL__
 
+#include <libudev.h>
+
+/*
+ * 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__ */
index 754d854..a3a0b8e 100644 (file)
@@ -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)
index d82791f..f1c85aa 100644 (file)
@@ -16,6 +16,9 @@
  * limitations under the License.
  */
 
+#include <glib-unix.h>
+#include <libudev.h>
+
 #include <pass/log.h>
 #include <pass/hal/hal.h>
 
@@ -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;
index b3fb8fc..84768b6 100644 (file)
@@ -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__ */
index 398eb1f..59b2265 100644 (file)
@@ -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;
 };
 
 /*