* limitations under the License.
*/
+#include <glib-unix.h>
+#include <libudev.h>
+
#include <pass/log.h>
#include <pass/hal/hal.h>
#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[] = {
[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)
}
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;
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);
/* 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;
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);
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);
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;
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;
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;