--- /dev/null
+/*
+ * PASS (Power Aware System Service) - System Resource Driver
+ *
+ * Copyright (c) 2022 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.
+ */
+
+/**
+ * @file resource-system.c
+ * @brief TBD
+ * @ingroup TBD
+ */
+
+#include <glib.h>
+
+#include <hal/hal-power.h>
+
+#include <util/common.h>
+#include <util/log.h>
+#include <util/resource.h>
+#include <util/kernel.h>
+
+#include <tmonitor/tmonitor.h>
+
+struct system_resouce_data {
+ struct cpu_stat prev_avg;
+ struct cpu_stat curr_avg;
+
+ int num_possible_cpus;
+ int num_online_cpus;
+ struct cpu_stat *prev_cpus;
+ struct cpu_stat *curr_cpus;
+};
+
+static double __calculate_cpu_util(int64_t id, struct cpu_stat *prev,
+ struct cpu_stat *curr)
+{
+ struct cpu_stat diff;
+ int total;
+ double util;
+
+ diff.user = curr->user - prev->user;
+ diff.system = curr->system - prev->system;
+ diff.nice = curr->nice - prev->nice;
+ diff.idle = curr->idle - prev->idle;
+
+ total = (double)(diff.user + diff.system + diff.nice + diff.idle);
+
+ switch (id) {
+ case SYSTEM_CPU_UTIL:
+ case SYSTEM_PER_CPU_UTIL:
+ util = (double)((diff.user + diff.system + diff.nice) * 100) / total;
+ break;
+ case SYSTEM_CPU_USER_UTIL:
+ case SYSTEM_PER_CPU_USER_UTIL:
+ util = (double)((diff.user) * 100) / total;
+ break;
+ case SYSTEM_CPU_SYS_UTIL:
+ case SYSTEM_PER_CPU_SYS_UTIL:
+ util = (double)((diff.system + diff.nice) * 100) / total;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return util;
+}
+
+static int system_get_avg_cpu_util(const struct resource *res,
+ const struct resource_attribute *attr,
+ void **data)
+{
+ struct system_resouce_data *sysdata = (struct system_resouce_data *)res->priv;
+ double util;
+
+ if (!res || !attr || !data)
+ return -EINVAL;
+
+ util = __calculate_cpu_util(attr->id, &sysdata->prev_avg, &sysdata->curr_avg);
+ if (util < 0) {
+ _W("failed to calculate average cpu util (%s.%d: %s)\n",
+ res->name, res->index, attr->name);
+ util = 0;
+ }
+ *data = (void *)(intptr_t)util;
+
+ return 0;
+}
+
+static int system_get_per_cpu_util(const struct resource *res,
+ const struct resource_attribute *attr,
+ void **data)
+{
+ struct system_resouce_data *sysdata = (struct system_resouce_data *)res->priv;
+ struct array_value *array;
+ double *utils;
+ int i;
+
+ if (!res || !attr || !data)
+ return -EINVAL;
+ array = calloc(1, sizeof(*array));
+ if (!array)
+ return -ENOMEM;
+
+ array->type = DATA_TYPE_DOUBLE;
+ array->length = sysdata->num_possible_cpus;
+ array->data = calloc(sysdata->num_possible_cpus, sizeof(double));
+ if (!array->data) {
+ free(array);
+ return -ENOMEM;
+ }
+ utils = (double *)array->data;
+
+ for (i = 0; i < sysdata->num_possible_cpus; i++) {
+ utils[i] = __calculate_cpu_util(attr->id,
+ &sysdata->prev_cpus[i],
+ &sysdata->curr_cpus[i]);
+ if (utils[i] < 0) {
+ _W("failed to calculate per-cpu util (%s.%d: %s)\n",
+ res->name, res->index, attr->name);
+ utils[i] = 0;
+ }
+ }
+ *data = (void *)array;
+
+ return 0;
+}
+
+static int system_get_cpu_num(const struct resource *res,
+ const struct resource_attribute *attr,
+ void **data)
+{
+ int cpu_num;
+
+ if (!res || !attr || !data)
+ return -EINVAL;
+
+ switch (attr->id) {
+ case SYSTEM_POSSIBLE_CPU:
+ cpu_num = kernel_get_online_cpu_num();
+ break;
+ case SYSTEM_ONLINE_CPU:
+ cpu_num = kernel_get_possible_cpu_num();
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ *data = (void *)(intptr_t)cpu_num;
+
+ return 0;
+}
+
+static const struct resource_attribute system_attrs[] = {
+ {
+ .name = "SYSTEM_CPU_UTIL",
+ .id = SYSTEM_CPU_UTIL,
+ .type = DATA_TYPE_DOUBLE,
+ .ops = {
+ .get = system_get_avg_cpu_util,
+ }
+ }, {
+ .name = "SYSTEM_CPU_USER_UTIL",
+ .id = SYSTEM_CPU_USER_UTIL,
+ .type = DATA_TYPE_DOUBLE,
+ .ops = {
+ .get = system_get_avg_cpu_util,
+ }
+ }, {
+ .name = "SYSTEM_CPU_SYS_UTIL",
+ .id = SYSTEM_CPU_SYS_UTIL,
+ .type = DATA_TYPE_DOUBLE,
+ .ops = {
+ .get = system_get_avg_cpu_util,
+ }
+ }, {
+ .name = "SYSTEM_PER_CPU_UTIL",
+ .id = SYSTEM_PER_CPU_UTIL,
+ .type = DATA_TYPE_ARRAY,
+ .ops = {
+ .get = system_get_per_cpu_util,
+ }
+ }, {
+ .name = "SYSTEM_PER_CPU_USER_UTIL",
+ .id = SYSTEM_PER_CPU_USER_UTIL,
+ .type = DATA_TYPE_ARRAY,
+ .ops = {
+ .get = system_get_per_cpu_util,
+ }
+ }, {
+ .name = "SYSTEM_PER_CPU_SYS_UTIL",
+ .id = SYSTEM_PER_CPU_SYS_UTIL,
+ .type = DATA_TYPE_ARRAY,
+ .ops = {
+ .get = system_get_per_cpu_util,
+ }
+ }, {
+ .name = "SYSTEM_POSSIBLE_CPU",
+ .id = SYSTEM_POSSIBLE_CPU,
+ .type = DATA_TYPE_INT,
+ .ops = {
+ .get = system_get_cpu_num,
+ }
+ }, {
+ .name = "SYSTEM_ONLINE_CPU",
+ .id = SYSTEM_ONLINE_CPU,
+ .type = DATA_TYPE_INT,
+ .ops = {
+ .get = system_get_cpu_num,
+ }
+ },
+};
+
+static int system_driver_init(struct resource *res)
+{
+ struct system_resouce_data *sysdata;
+ int ret;
+
+ sysdata = calloc(1, sizeof(struct system_resouce_data));
+ if (!sysdata)
+ return -ENOMEM;
+ res->priv = (void *)sysdata;
+
+ ret = kernel_get_possible_cpu_num();
+ if (ret < 0) {
+ _I("failed to get possible cpu on system driver (%s.%d)\n",
+ res->name, res->index);
+ goto err;
+ }
+ sysdata->num_possible_cpus = ret;
+
+ sysdata->prev_cpus = calloc(sysdata->num_possible_cpus,
+ sizeof(struct cpu_stat));
+ if (!sysdata->prev_cpus) {
+ _I("failed to allocate memory of prev_cpus (%s.%d)\n",
+ res->name, res->index);
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ sysdata->curr_cpus = calloc(sysdata->num_possible_cpus,
+ sizeof(struct cpu_stat));
+ if (!sysdata->curr_cpus) {
+ _I("failed to allocate memory of curr_cpus (%s.%d)\n",
+ res->name, res->index);
+ ret = -ENOMEM;
+ goto err_prev_cpus;
+ }
+
+ return 0;
+
+err_prev_cpus:
+ free(sysdata->prev_cpus);
+err:
+ sysdata->num_possible_cpus = 0;
+ free(sysdata);
+ sysdata = NULL;
+
+ return ret;
+}
+
+static void system_driver_exit(struct resource *res)
+{
+ struct system_resouce_data *sysdata
+ = (struct system_resouce_data *)res->priv;
+
+ free(sysdata->prev_cpus);
+ free(sysdata->curr_cpus);
+ free(sysdata);
+ res->priv = NULL;
+}
+
+static int system_driver_prepare_update(struct resource *res)
+{
+ struct system_resouce_data *sysdata
+ = (struct system_resouce_data *)res->priv;
+ int ret;
+
+ /* Get the average cpu utilization of all cpus */
+ memcpy(&sysdata->prev_avg, &sysdata->curr_avg, sizeof(sysdata->prev_avg));
+ ret = kernel_get_total_cpu_stat(&sysdata->curr_avg);
+ if (ret < 0) {
+ _I("failed to calculate average cpu util (%s:%d)\n",
+ res->name, res->index);
+ return ret;
+ }
+
+ /* Get the per-cpu utilization */
+ memcpy(sysdata->prev_cpus, sysdata->curr_cpus,
+ sizeof(struct cpu_stat) * sysdata->num_possible_cpus);
+ ret = kernel_get_per_cpu_stat(sysdata->curr_cpus,
+ sysdata->num_possible_cpus,
+ &sysdata->num_online_cpus);
+ if (ret < 0) {
+ _I("failed to calculate per-cpu util (%s:%d)\n",
+ res->name, res->index);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct resource_driver system_resource_driver = {
+ .name = "SYSTEM",
+ .type = RESOURCE_TYPE_SYSTEM,
+ .attrs = system_attrs,
+ .num_attrs = ARRAY_SIZE(system_attrs),
+ .flags = RESOURCE_DRIVER_NO_DEVICE,
+ .ops = {
+ .init = system_driver_init,
+ .exit = system_driver_exit,
+ .prepare_update = system_driver_prepare_update,
+ },
+};
+RESOURCE_DRIVER_REGISTER(&system_resource_driver)