From: Chanwoo Choi Date: Mon, 25 Jul 2022 03:44:02 +0000 (+0900) Subject: resource: Add disk and network resource driver X-Git-Tag: submit/tizen/20220805.011932^0 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=b3308132cbfbc2de1b74c07842bbba18a12c0063;p=platform%2Fcore%2Fsystem%2Fpass.git resource: Add disk and network resource driver 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 --- diff --git a/CMakeLists.txt b/CMakeLists.txt index 71ab2c3..aada2f3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/lib/resource-monitor/resource-monitor.h b/lib/resource-monitor/resource-monitor.h index 08d7d11..1a97cb6 100644 --- a/lib/resource-monitor/resource-monitor.h +++ b/lib/resource-monitor/resource-monitor.h @@ -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 index 0000000..3493241 --- /dev/null +++ b/src/resource/resource-disk.c @@ -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 + +#include +#include +#include + +#include + +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 index 0000000..cfc4887 --- /dev/null +++ b/src/resource/resource-network.c @@ -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 + +#include +#include +#include + +#include + +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)