From e038674ba7b2182badbe09058840e7b77581caab Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Tue, 13 Feb 2018 15:31:06 +0900 Subject: [PATCH] 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