resource: Add disk and network resource driver 92/278692/4 submit/tizen/20220805.011932
authorChanwoo Choi <cw00.choi@samsung.com>
Mon, 25 Jul 2022 03:44:02 +0000 (12:44 +0900)
committerChanwoo Choi <cw00.choi@samsung.com>
Tue, 2 Aug 2022 02:32:49 +0000 (11:32 +0900)
Add disk and network resource driver to provide monitoring data.

1. Add disk resource driver to provide the disk bandwidth
   and usage with following resource attributes:
- DISK_ATTR_NAME indicates the disk device name
- DISK_ATTR_READ_PER_SEC indicates read bandwidth of specific block like zram0
- DISK_ATTR_WRITE_PER_SEC indicates write bandwidth of specific block like mmcblk0
- DISK_ATTR_READ_TOTAL indicates read kB size of specific block
- DISK_ATTR_WRITE_TOTAL indicates write kB size of specific block

The detailed description[1] of data are from /proc/diskstats and
/sys/class/block/[block name]/stats:
[1] https://www.kernel.org/doc/Documentation/ABI/testing/procfs-diskstats

2. Add skeleton of network resource driver with resource name attribute.
- NETWORK_ATTR_NAME indicates the network device name

Change-Id: Ib440ab628ad1edc5a24378b9c18b80b7c9a8367e
Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
CMakeLists.txt
lib/resource-monitor/resource-monitor.h
src/resource/resource-disk.c [new file with mode: 0644]
src/resource/resource-network.c [new file with mode: 0644]

index 71ab2c3..aada2f3 100644 (file)
@@ -49,6 +49,8 @@ SET(SRCS
        src/resource/resource-battery.c
        src/resource/resource-process.c
        src/resource/resource-process-group.c
+       src/resource/resource-disk.c
+       src/resource/resource-network.c
        src/monitor/monitor.c
        src/monitor/monitor-thread.c
        src/monitor/monitor-command.c
index 08d7d11..1a97cb6 100644 (file)
@@ -42,6 +42,8 @@ extern "C" {
 #define RESOURCE_TYPE_DISPLAY          7
 #define RESOURCE_TYPE_SYSTEM           8
 #define RESOURCE_TYPE_PROCESS_GROUP    9
+#define RESOURCE_TYPE_DISK             10
+#define RESOURCE_TYPE_NETWORK          11
 #define RESOURCE_TYPE_NONSTANDARD      99
 
 /**
@@ -149,6 +151,20 @@ extern "C" {
 
 #define PROCESS_GROUP_CTRL_ROOT_PID            BIT(0)
 
+/* Disk Resource */
+#define DISK_ATTR_NAME                         BIT(0)  /* DATA_TYPE_STRING */
+#define DISK_ATTR_READ_PER_SEC                 BIT(1)  /* DATA_TYPE_DOUBLE */
+#define DISK_ATTR_WRITE_PER_SEC                        BIT(2)  /* DATA_TYPE_DOUBLE */
+#define DISK_ATTR_READ_TOTAL                   BIT(3)  /* DATA_TYPE_UINT64 */
+#define DISK_ATTR_WRITE_TOTAL                  BIT(4)  /* DATA_TYPE_UINT64 */
+
+#define DISK_CTRL_DEVICE_ID                    BIT(0)
+
+/* Network Resource */
+#define NETWORK_ATTR_NAME                      BIT(0)  /* DATA_TYPE_STRING */
+
+#define NETWORK_CTRL_DEVICE_ID                 BIT(0)
+
 /**
  * @brief Initialize the resource monitor
  * @return @c positive integer as resource monitor id on success, otherwise a negative error value
diff --git a/src/resource/resource-disk.c b/src/resource/resource-disk.c
new file mode 100644 (file)
index 0000000..3493241
--- /dev/null
@@ -0,0 +1,410 @@
+/*
+ * PASS (Power Aware System Service) - Disk 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-disk.c
+ * @brief      Provide disk resource driver supporting the various resource
+ *             attributes. It supports the disk attributes such as disk
+ *             read/write bandwidth, disk size and so on.
+ * @ingroup    RESOURCE_MONITOR
+ */
+
+#include <glib.h>
+
+#include <util/common.h>
+#include <util/log.h>
+#include <util/resource.h>
+
+#include <resource-monitor/resource-monitor.h>
+
+enum {
+       PREV_IDX = 0,
+       CURR_IDX = 1,
+};
+
+/* Return interval of time (@p) is given in 1/100th of a second */
+#define GET_BPS(m, n, p)       (((double) ((n) - (m))) / (p) * 100)
+
+struct io_stats {
+       unsigned long rd_sectors;       /* Number of sectors read */
+       unsigned long wr_sectors;       /* Number of sectors written */
+       unsigned long dc_sectors;       /* Number of sectors discarded */
+       unsigned long rd_ios;           /* Number of read operations issued to the device */
+       unsigned long rd_merges;        /* Number of read requests merged */
+       unsigned long wr_ios;           /* Number of write operations issued to the device */
+       unsigned long wr_merges;        /* Number of write requests merged */
+       unsigned long dc_ios;           /* Number of discard operations issued to the device */
+       unsigned long dc_merges;        /* Number of discard requests merged */
+       unsigned long fl_ios;           /* Number of flush requests issued to the device */
+       unsigned int  rd_ticks;         /* Time of read requests in queue */
+       unsigned int  wr_ticks;         /* Time of write requests in queue */
+       unsigned int  dc_ticks;         /* Time of discard requests in queue */
+       unsigned int  fl_ticks;         /* Time of flush requests in queue */
+       unsigned int  ios_pgr;          /* Number of I/Os in progress */
+       unsigned int  tot_ticks;        /* Number of ticks total (for this device) for I/O */
+       unsigned int  rq_ticks;         /* Number of ticks requests spent in queue */
+};
+
+struct disk_context {
+       char *device_name;
+       int index;
+
+       unsigned long long uptime_cs[2];
+       struct io_stats stats[2];
+
+       double read_per_sec;
+       double write_per_sec;
+       u_int32_t read_total;
+       u_int32_t write_total;
+};
+
+static int disk_get_value(struct resource *res,
+                               const struct resource_attribute *attr,
+                               void *data)
+{
+       struct disk_context *ctx;
+
+       if (!res || !attr || !data)
+               return -EINVAL;
+
+       ctx = get_resource_privdata(res);
+       if (!ctx)
+               return -EINVAL;
+
+       if (!ctx->device_name) {
+               _E("DISK_CTRL_DEVICE_ID is not yet initialized, res:name(%s)id(%d) | attr:name(%s)id(%"PRId64")\n",
+                                       get_resource_name(res), get_resource_id(res),
+                                       get_resource_attr_name(res, attr->id), attr->id);
+               return -EINVAL;
+       }
+
+       switch (attr->id) {
+       case DISK_ATTR_NAME:
+               strncpy((char *)data, ctx->device_name, BUFF_MAX);
+               break;
+       case DISK_ATTR_READ_PER_SEC:
+               *(double *)data = ctx->read_per_sec;
+               break;
+       case DISK_ATTR_WRITE_PER_SEC:
+               *(double *)data = ctx->write_per_sec;
+               break;
+       case DISK_ATTR_READ_TOTAL:
+               *(u_int64_t *)data = ctx->read_total;
+               break;
+       case DISK_ATTR_WRITE_TOTAL:
+               *(u_int64_t *)data = ctx->write_total;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static const struct resource_attribute disk_attrs[] = {
+       {
+               .name   = "DISK_ATTR_NAME",
+               .id     = DISK_ATTR_NAME,
+               .type   = DATA_TYPE_STRING,
+               .ops    = {
+                       .get = disk_get_value,
+               },
+       }, {
+               .name   = "DISK_ATTR_READ_PER_SEC",
+               .id     = DISK_ATTR_READ_PER_SEC,
+               .type   = DATA_TYPE_DOUBLE,
+               .ops    = {
+                       .get = disk_get_value,
+               },
+       }, {
+               .name   = "DISK_ATTR_WRITE_PER_SEC",
+               .id     = DISK_ATTR_WRITE_PER_SEC,
+               .type   = DATA_TYPE_DOUBLE,
+               .ops    = {
+                       .get = disk_get_value,
+               },
+       }, {
+               .name   = "DISK_ATTR_READ_TOTAL",
+               .id     = DISK_ATTR_READ_TOTAL,
+               .type   = DATA_TYPE_UINT64,
+               .ops    = {
+                       .get = disk_get_value,
+               },
+       }, {
+               .name   = "DISK_ATTR_WRITE_TOTAL",
+               .id     = DISK_ATTR_WRITE_TOTAL,
+               .type   = DATA_TYPE_UINT64,
+               .ops    = {
+                       .get = disk_get_value,
+               },
+       },
+};
+
+static int disk_setup_device_id(struct resource *res,
+                               const struct resource_control *ctrl,
+                               const void *data)
+{
+       struct disk_context *ctx;
+       const struct resource_device *device;
+       int resource_index = (int)(intptr_t)data;
+
+       if (!res || !ctrl)
+               return -EINVAL;
+
+       ctx = get_resource_privdata(res);
+       if (!ctx)
+               return -EINVAL;
+
+       device = find_resource_device(get_resource_type(res), resource_index);
+       if (!device) {
+               _E("Not available resource: type: %s, index: %d\n",
+                               get_resource_name(res), resource_index);
+               return -EINVAL;
+       }
+
+       if (ctx->device_name)
+               free(ctx->device_name);
+
+       ctx->device_name = g_strdup(device->name);
+       ctx->index = resource_index;
+
+       return 0;
+}
+
+static const struct resource_control disk_ctrls[] = {
+       {
+               .name = "DISK_CTRL_DEVICE_ID",
+               .id = DISK_CTRL_DEVICE_ID,
+               .ops = {
+                       .set = disk_setup_device_id,
+               },
+       },
+};
+
+static int read_uptime(unsigned long long *uptime)
+{
+       FILE *fp = NULL;
+       char line[BUFF_MAX];
+       unsigned long up_sec, up_cent;
+       int ret = 0;
+
+       if ((fp = fopen("/proc/uptime", "r")) == NULL) {
+               ret = -ENOENT;
+       } else if (fgets(line, sizeof(line), fp) == NULL) {
+               ret = -ENOENT;
+       } else if (sscanf(line, "%lu.%lu", &up_sec, &up_cent) == 2) {
+               *uptime = (unsigned long long) up_sec * 100 +
+                         (unsigned long long) up_cent;
+       } else {
+               ret = -EINVAL;
+       }
+
+       if (fp != NULL)
+               fclose(fp);
+
+       return ret;
+}
+
+static int read_disk_stats(char *device_name, struct io_stats *ios)
+{
+       char filename[BUFF_MAX];
+       FILE *fp;
+       int i;
+       unsigned int ios_pgr, tot_ticks, rq_ticks, wr_ticks, dc_ticks, fl_ticks;
+       unsigned long rd_ios, rd_merges_or_rd_sec, wr_ios, wr_merges;
+       unsigned long rd_sec_or_wr_ios, wr_sec, rd_ticks_or_wr_sec;
+       unsigned long dc_ios, dc_merges, dc_sec, fl_ios;
+
+       if (!device_name || !ios)
+               return -EINVAL;
+
+       snprintf(filename, BUFF_MAX, "/sys/class/block/%s/stat", device_name);
+
+       if (access(filename, F_OK) == -1) {
+               _E("There is no block device(%s)\n", device_name);
+               return -ENOENT;
+       } else if ((fp = fopen(filename, "r")) == NULL) {
+               _E("failed to open block device(%s)\n", device_name);
+               return -ENOENT;
+       }
+
+       i = fscanf(fp, "%lu %lu %lu %lu %lu %lu %lu %u %u %u %u %lu %lu %lu %u %lu %u",
+                  &rd_ios, &rd_merges_or_rd_sec, &rd_sec_or_wr_ios, &rd_ticks_or_wr_sec,
+                  &wr_ios, &wr_merges, &wr_sec, &wr_ticks, &ios_pgr, &tot_ticks, &rq_ticks,
+                  &dc_ios, &dc_merges, &dc_sec, &dc_ticks,
+                  &fl_ios, &fl_ticks);
+
+       memset(ios, 0, sizeof(*ios));
+
+       if (i >= 11) {
+               /* Device or partition */
+               ios->rd_ios     = rd_ios;
+               ios->rd_merges  = rd_merges_or_rd_sec;
+               ios->rd_sectors = rd_sec_or_wr_ios;
+               ios->rd_ticks   = (unsigned int) rd_ticks_or_wr_sec;
+               ios->wr_ios     = wr_ios;
+               ios->wr_merges  = wr_merges;
+               ios->wr_sectors = wr_sec;
+               ios->wr_ticks   = wr_ticks;
+               ios->ios_pgr    = ios_pgr;
+               ios->tot_ticks  = tot_ticks;
+               ios->rq_ticks   = rq_ticks;
+
+               if (i >= 15) {
+                       /* Discard I/O */
+                       ios->dc_ios     = dc_ios;
+                       ios->dc_merges  = dc_merges;
+                       ios->dc_sectors = dc_sec;
+                       ios->dc_ticks   = dc_ticks;
+               }
+
+               if (i >= 17) {
+                       /* Flush I/O */
+                       ios->fl_ios     = fl_ios;
+                       ios->fl_ticks   = fl_ticks;
+               }
+       }
+       else if (i == 4) {
+               /* Partition without extended statistics */
+               ios->rd_ios     = rd_ios;
+               ios->rd_sectors = rd_merges_or_rd_sec;
+               ios->wr_ios     = rd_sec_or_wr_ios;
+               ios->wr_sectors = rd_ticks_or_wr_sec;
+       }
+
+       fclose(fp);
+
+       return 0;
+}
+
+static unsigned long long get_interval(unsigned long long prev_uptime,
+                                       unsigned long long curr_uptime)
+{
+       unsigned long long interval = curr_uptime - prev_uptime;
+       return (interval == 0) ? 1 : interval;
+}
+
+static int calculate_disk_stats(struct disk_context *ctx)
+{
+       struct io_stats *curr, *prev;
+       unsigned long long interval;
+
+       if (!ctx)
+               return -EINVAL;
+
+       /* Calculate time interval in 1/100th of a second */
+       interval = get_interval(ctx->uptime_cs[PREV_IDX], ctx->uptime_cs[CURR_IDX]);
+
+       curr = &ctx->stats[CURR_IDX];
+       prev = &ctx->stats[PREV_IDX];
+
+       ctx->read_per_sec = GET_BPS(prev->rd_sectors, curr->rd_sectors, interval);
+       ctx->write_per_sec = GET_BPS(prev->wr_sectors, curr->wr_sectors, interval);
+       ctx->read_total = curr->rd_sectors;
+       ctx->write_total = curr->wr_sectors;
+
+       return 0;
+}
+
+static int disk_init(struct resource *res)
+{
+       struct disk_context *ctx;
+
+       ctx = calloc(1, sizeof(struct disk_context));
+       if (!ctx)
+               return -ENOMEM;
+
+       ctx->index = -1;
+
+       set_resource_privdata(res, ctx);
+
+       return 0;
+}
+
+static void disk_exit(struct resource *res)
+{
+       struct disk_context *ctx;
+
+       if (!res)
+               return;
+
+       ctx = get_resource_privdata(res);
+       if (!ctx)
+               return;
+
+       if (ctx->device_name)
+               free(ctx->device_name);
+
+       free(ctx);
+       set_resource_privdata(res, NULL);
+}
+
+static int disk_prepare_update(struct resource *res)
+{
+       struct disk_context *ctx;
+       int ret;
+
+       if (!res)
+               return -EINVAL;
+
+       ctx = get_resource_privdata(res);
+       if (!ctx)
+               return -EINVAL;
+
+       if (!ctx->device_name) {
+               _E("%s: DISK_CTRL_DEVICE_ID is not yet initialized\n",
+                                       get_resource_name(res));
+               return -EINVAL;
+       }
+
+       /* Backup current disk stats */
+       memcpy(&ctx->stats[PREV_IDX], &ctx->stats[CURR_IDX], sizeof(ctx->stats[CURR_IDX]));
+       ctx->uptime_cs[PREV_IDX] = ctx->uptime_cs[CURR_IDX];
+
+       /* Read system uptime */
+       ret = read_uptime(&(ctx->uptime_cs[CURR_IDX]));
+       if (ret < 0)
+               return ret;
+
+       /* Read disk stats */
+       ret = read_disk_stats(ctx->device_name, &ctx->stats[CURR_IDX]);
+       if (ret < 0)
+               return ret;
+
+       /* Calculate disk stats */
+       ret = calculate_disk_stats(ctx);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+static const struct resource_driver disk_resource_driver = {
+       .name           = "DISK",
+       .type           = RESOURCE_TYPE_DISK,
+       .attrs          = disk_attrs,
+       .num_attrs      = ARRAY_SIZE(disk_attrs),
+       .ctrls          = disk_ctrls,
+       .num_ctrls      = ARRAY_SIZE(disk_ctrls),
+       .ops            = {
+               .init = disk_init,
+               .exit = disk_exit,
+               .prepare_update = disk_prepare_update,
+       },
+};
+RESOURCE_DRIVER_REGISTER(&disk_resource_driver)
diff --git a/src/resource/resource-network.c b/src/resource/resource-network.c
new file mode 100644 (file)
index 0000000..cfc4887
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ * PASS (Power Aware System Service) - Network 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-network.c
+ * @brief      Provide network resource driver supporting the various resource
+ *             attributes. It supports the network attributes such as network
+ *             bandwidth and so on.
+ * @ingroup    RESOURCE_MONITOR
+ */
+
+#include <glib.h>
+
+#include <util/common.h>
+#include <util/log.h>
+#include <util/resource.h>
+
+#include <resource-monitor/resource-monitor.h>
+
+struct network_context {
+       char *device_name;
+       int index;
+};
+
+static int network_get_name(struct resource *res,
+                               const struct resource_attribute *attr,
+                               void *data)
+{
+       struct network_context *ctx;
+       char *buf = (char *)data;
+
+       if (!res || !attr || !data)
+               return -EINVAL;
+
+       ctx = get_resource_privdata(res);
+       if (!ctx)
+               return -EINVAL;
+
+       if (!ctx->device_name) {
+               _E("%s: NETWORK_CTRL_DEVICE_ID is not yet initialized\n",
+                               get_resource_name(res));
+               return -EINVAL;
+       }
+
+       strncpy(buf, ctx->device_name, BUFF_MAX);
+
+       return 0;
+}
+
+static const struct resource_attribute network_attrs[] = {
+       {
+               .name   = "NETWORK_ATTR_NAME",
+               .id     = NETWORK_ATTR_NAME,
+               .type   = DATA_TYPE_STRING,
+               .ops    = {
+                       .get = network_get_name,
+               },
+       },
+};
+
+static int network_setup_device_id(struct resource *res,
+                               const struct resource_control *ctrl,
+                               const void *data)
+{
+       struct network_context *ctx;
+       const struct resource_device *device;
+       int resource_index = (int)(intptr_t)data;
+
+       if (!res || !ctrl)
+               return -EINVAL;
+
+       ctx = get_resource_privdata(res);
+       if (!ctx)
+               return -EINVAL;
+
+       device = find_resource_device(get_resource_type(res), resource_index);
+       if (!device) {
+               _E("Not available resource: type: %s, index: %d\n",
+                               get_resource_name(res), resource_index);
+               return -EINVAL;
+       }
+
+       if (ctx->device_name)
+               free(ctx->device_name);
+
+       ctx->device_name = g_strdup(device->name);
+       ctx->index = resource_index;
+
+       return 0;
+}
+
+static const struct resource_control network_ctrls[] = {
+       {
+               .name = "NETWORK_CTRL_DEVICE_ID",
+               .id = NETWORK_CTRL_DEVICE_ID,
+               .ops = {
+                       .set = network_setup_device_id,
+               },
+       },
+};
+
+static int network_init(struct resource *res)
+{
+       struct network_context *ctx;
+
+       ctx = calloc(1, sizeof(struct network_context));
+       if (!ctx)
+               return -ENOMEM;
+
+       ctx->index = -1;
+
+       set_resource_privdata(res, ctx);
+
+       return 0;
+}
+
+static void network_exit(struct resource *res)
+{
+       struct network_context *ctx;
+
+       if (!res)
+               return;
+
+       ctx = get_resource_privdata(res);
+       if (!ctx)
+               return;
+
+       if (ctx->device_name)
+               free(ctx->device_name);
+
+       free(ctx);
+       set_resource_privdata(res, NULL);
+}
+
+static const struct resource_driver network_resource_driver = {
+       .name           = "NETWORK",
+       .type           = RESOURCE_TYPE_NETWORK,
+       .attrs          = network_attrs,
+       .num_attrs      = ARRAY_SIZE(network_attrs),
+       .ctrls          = network_ctrls,
+       .num_ctrls      = ARRAY_SIZE(network_ctrls),
+       .ops            = {
+               .init = network_init,
+               .exit = network_exit,
+       },
+};
+RESOURCE_DRIVER_REGISTER(&network_resource_driver)