2 * PASS (Power Aware System Service) - Disk Resource Driver
4 * Copyright (c) 2022 Samsung Electronics Co., Ltd.
6 * Licensed under the Apache License, Version 2.0 (the License);
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
20 * @file resource-disk.c
21 * @brief Provide disk resource driver supporting the various resource
22 * attributes. It supports the disk attributes such as disk
23 * read/write bandwidth, disk size and so on.
24 * @ingroup RESOURCE_MONITOR
29 #include <util/common.h>
30 #include <util/devices.h>
33 #include <libsyscommon/resource-manager.h>
34 #include <libsyscommon/resource-type.h>
35 #include <libsyscommon/resource-device.h>
37 #include <resource-monitor/resource-monitor.h>
44 /* Return interval of time (@p) is given in 1/100th of a second */
45 #define GET_BPS(m, n, p) (((double) ((n) - (m))) / (p) * 100)
48 unsigned long rd_sectors; /* Number of sectors read */
49 unsigned long wr_sectors; /* Number of sectors written */
50 unsigned long dc_sectors; /* Number of sectors discarded */
51 unsigned long rd_ios; /* Number of read operations issued to the device */
52 unsigned long rd_merges; /* Number of read requests merged */
53 unsigned long wr_ios; /* Number of write operations issued to the device */
54 unsigned long wr_merges; /* Number of write requests merged */
55 unsigned long dc_ios; /* Number of discard operations issued to the device */
56 unsigned long dc_merges; /* Number of discard requests merged */
57 unsigned long fl_ios; /* Number of flush requests issued to the device */
58 unsigned int rd_ticks; /* Time of read requests in queue */
59 unsigned int wr_ticks; /* Time of write requests in queue */
60 unsigned int dc_ticks; /* Time of discard requests in queue */
61 unsigned int fl_ticks; /* Time of flush requests in queue */
62 unsigned int ios_pgr; /* Number of I/Os in progress */
63 unsigned int tot_ticks; /* Number of ticks total (for this device) for I/O */
64 unsigned int rq_ticks; /* Number of ticks requests spent in queue */
71 unsigned long long uptime_cs[2];
72 struct io_stats stats[2];
77 u_int32_t write_total;
80 static int disk_get_value(struct syscommon_resman_resource *res,
81 const struct syscommon_resman_resource_attribute *attr,
84 struct disk_context *ctx;
86 if (!res || !attr || !data)
89 ctx = syscommon_resman_get_resource_privdata(res);
93 if (!ctx->device_name) {
94 _E("DISK_CTRL_DEVICE_ID is not yet initialized, res:name(%s) | attr:name(%s)id(%"PRId64")\n",
95 syscommon_resman_get_resource_name(res), syscommon_resman_get_resource_attr_name(res, attr->id), attr->id);
101 strncpy((char *)data, ctx->device_name, BUFF_MAX);
103 case DISK_ATTR_READ_PER_SEC:
104 *(double *)data = ctx->read_per_sec;
106 case DISK_ATTR_WRITE_PER_SEC:
107 *(double *)data = ctx->write_per_sec;
109 case DISK_ATTR_READ_TOTAL:
110 *(u_int64_t *)data = ctx->read_total;
112 case DISK_ATTR_WRITE_TOTAL:
113 *(u_int64_t *)data = ctx->write_total;
122 static const struct syscommon_resman_resource_attribute disk_attrs[] = {
124 .name = "DISK_ATTR_NAME",
125 .id = DISK_ATTR_NAME,
126 .type = SYSCOMMON_RESMAN_DATA_TYPE_STRING,
127 .flag = SYSCOMMON_RESMAN_RESOURCE_ATTR_FLAG_PUBLIC,
129 .get = disk_get_value,
132 .name = "DISK_ATTR_READ_PER_SEC",
133 .id = DISK_ATTR_READ_PER_SEC,
134 .type = SYSCOMMON_RESMAN_DATA_TYPE_DOUBLE,
135 .flag = SYSCOMMON_RESMAN_RESOURCE_ATTR_FLAG_PUBLIC,
137 .get = disk_get_value,
140 .name = "DISK_ATTR_WRITE_PER_SEC",
141 .id = DISK_ATTR_WRITE_PER_SEC,
142 .type = SYSCOMMON_RESMAN_DATA_TYPE_DOUBLE,
143 .flag = SYSCOMMON_RESMAN_RESOURCE_ATTR_FLAG_PUBLIC,
145 .get = disk_get_value,
148 .name = "DISK_ATTR_READ_TOTAL",
149 .id = DISK_ATTR_READ_TOTAL,
150 .type = SYSCOMMON_RESMAN_DATA_TYPE_UINT64,
151 .flag = SYSCOMMON_RESMAN_RESOURCE_ATTR_FLAG_PUBLIC,
153 .get = disk_get_value,
156 .name = "DISK_ATTR_WRITE_TOTAL",
157 .id = DISK_ATTR_WRITE_TOTAL,
158 .type = SYSCOMMON_RESMAN_DATA_TYPE_UINT64,
159 .flag = SYSCOMMON_RESMAN_RESOURCE_ATTR_FLAG_PUBLIC,
161 .get = disk_get_value,
166 static int disk_setup_device_id(struct syscommon_resman_resource *res,
167 const struct syscommon_resman_resource_control *ctrl,
170 struct disk_context *ctx;
171 const struct syscommon_resman_resource_device *device;
172 int resource_index = (int)(intptr_t)data;
177 ctx = syscommon_resman_get_resource_privdata(res);
181 device = syscommon_resman_find_resource_device(syscommon_resman_get_resource_type(res), resource_index);
183 _E("Not available resource: type: %s, index: %d\n",
184 syscommon_resman_get_resource_name(res), resource_index);
188 if (ctx->device_name)
189 free(ctx->device_name);
191 ctx->device_name = g_strdup(device->name);
192 ctx->index = resource_index;
197 static const struct syscommon_resman_resource_control disk_ctrls[] = {
199 .name = "DISK_CTRL_DEVICE_ID",
200 .id = DISK_CTRL_DEVICE_ID,
202 .set = disk_setup_device_id,
207 static int read_uptime(unsigned long long *uptime)
211 unsigned long up_sec, up_cent;
214 if ((fp = fopen("/proc/uptime", "r")) == NULL) {
216 } else if (fgets(line, sizeof(line), fp) == NULL) {
218 } else if (sscanf(line, "%lu.%lu", &up_sec, &up_cent) == 2) {
219 *uptime = (unsigned long long) up_sec * 100 +
220 (unsigned long long) up_cent;
231 static int read_disk_stats(char *device_name, struct io_stats *ios)
233 char filename[BUFF_MAX];
236 unsigned int ios_pgr, tot_ticks, rq_ticks, wr_ticks, dc_ticks, fl_ticks;
237 unsigned long rd_ios, rd_merges_or_rd_sec, wr_ios, wr_merges;
238 unsigned long rd_sec_or_wr_ios, wr_sec, rd_ticks_or_wr_sec;
239 unsigned long dc_ios, dc_merges, dc_sec, fl_ios;
241 if (!device_name || !ios)
244 snprintf(filename, BUFF_MAX, "/sys/class/block/%s/stat", device_name);
246 fp = fopen(filename, "r");
248 char errstr[BUFF_MAX];
250 strerror_r(errno, errstr, BUFF_MAX);
251 _E("failed to open block device(%s):%s\n", device_name, errstr);
255 i = fscanf(fp, "%lu %lu %lu %lu %lu %lu %lu %u %u %u %u %lu %lu %lu %u %lu %u",
256 &rd_ios, &rd_merges_or_rd_sec, &rd_sec_or_wr_ios, &rd_ticks_or_wr_sec,
257 &wr_ios, &wr_merges, &wr_sec, &wr_ticks, &ios_pgr, &tot_ticks, &rq_ticks,
258 &dc_ios, &dc_merges, &dc_sec, &dc_ticks,
261 memset(ios, 0, sizeof(*ios));
264 /* Device or partition */
265 ios->rd_ios = rd_ios;
266 ios->rd_merges = rd_merges_or_rd_sec;
267 ios->rd_sectors = rd_sec_or_wr_ios;
268 ios->rd_ticks = (unsigned int) rd_ticks_or_wr_sec;
269 ios->wr_ios = wr_ios;
270 ios->wr_merges = wr_merges;
271 ios->wr_sectors = wr_sec;
272 ios->wr_ticks = wr_ticks;
273 ios->ios_pgr = ios_pgr;
274 ios->tot_ticks = tot_ticks;
275 ios->rq_ticks = rq_ticks;
279 ios->dc_ios = dc_ios;
280 ios->dc_merges = dc_merges;
281 ios->dc_sectors = dc_sec;
282 ios->dc_ticks = dc_ticks;
287 ios->fl_ios = fl_ios;
288 ios->fl_ticks = fl_ticks;
292 /* Partition without extended statistics */
293 ios->rd_ios = rd_ios;
294 ios->rd_sectors = rd_merges_or_rd_sec;
295 ios->wr_ios = rd_sec_or_wr_ios;
296 ios->wr_sectors = rd_ticks_or_wr_sec;
304 static unsigned long long get_interval(unsigned long long prev_uptime,
305 unsigned long long curr_uptime)
307 unsigned long long interval = curr_uptime - prev_uptime;
308 return (interval == 0) ? 1 : interval;
311 static int calculate_disk_stats(struct disk_context *ctx)
313 struct io_stats *curr, *prev;
314 unsigned long long interval;
319 /* Calculate time interval in 1/100th of a second */
320 interval = get_interval(ctx->uptime_cs[PREV_IDX], ctx->uptime_cs[CURR_IDX]);
322 curr = &ctx->stats[CURR_IDX];
323 prev = &ctx->stats[PREV_IDX];
325 ctx->read_per_sec = GET_BPS(prev->rd_sectors, curr->rd_sectors, interval);
326 ctx->write_per_sec = GET_BPS(prev->wr_sectors, curr->wr_sectors, interval);
327 ctx->read_total = curr->rd_sectors;
328 ctx->write_total = curr->wr_sectors;
333 static int disk_create(struct syscommon_resman_resource *res)
335 struct disk_context *ctx;
337 ctx = calloc(1, sizeof(struct disk_context));
343 syscommon_resman_set_resource_privdata(res, ctx);
348 static void disk_delete(struct syscommon_resman_resource *res)
350 struct disk_context *ctx;
355 ctx = syscommon_resman_get_resource_privdata(res);
359 if (ctx->device_name)
360 free(ctx->device_name);
363 syscommon_resman_set_resource_privdata(res, NULL);
366 static int disk_prepare_update(struct syscommon_resman_resource *res)
368 struct disk_context *ctx;
374 ctx = syscommon_resman_get_resource_privdata(res);
378 if (!ctx->device_name) {
379 _E("%s: DISK_CTRL_DEVICE_ID is not yet initialized\n",
380 syscommon_resman_get_resource_name(res));
384 /* Backup current disk stats */
385 memcpy(&ctx->stats[PREV_IDX], &ctx->stats[CURR_IDX], sizeof(ctx->stats[CURR_IDX]));
386 ctx->uptime_cs[PREV_IDX] = ctx->uptime_cs[CURR_IDX];
388 /* Read system uptime */
389 ret = read_uptime(&(ctx->uptime_cs[CURR_IDX]));
393 /* Read disk stats */
394 ret = read_disk_stats(ctx->device_name, &ctx->stats[CURR_IDX]);
398 /* Calculate disk stats */
399 ret = calculate_disk_stats(ctx);
406 static const struct syscommon_resman_resource_driver disk_resource_driver = {
408 .type = RESOURCE_TYPE_DISK,
410 .num_attrs = ARRAY_SIZE(disk_attrs),
412 .num_ctrls = ARRAY_SIZE(disk_ctrls),
414 .create = disk_create,
415 .delete = disk_delete,
416 .prepare_update = disk_prepare_update,
419 SYSCOMMON_RESMAN_RESOURCE_DRIVER_REGISTER(&disk_resource_driver)