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(int resource_id,
81 const struct syscommon_resman_resource_attribute *attr,
84 struct disk_context *ctx;
86 if (resource_id < 0 || !attr || !data)
89 ctx = syscommon_resman_get_resource_privdata(resource_id);
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(resource_id),
96 syscommon_resman_get_resource_attr_name(resource_id, attr->id), attr->id);
102 strncpy((char *)data, ctx->device_name, BUFF_MAX);
104 case DISK_ATTR_READ_PER_SEC:
105 *(double *)data = ctx->read_per_sec;
107 case DISK_ATTR_WRITE_PER_SEC:
108 *(double *)data = ctx->write_per_sec;
110 case DISK_ATTR_READ_TOTAL:
111 *(u_int64_t *)data = ctx->read_total;
113 case DISK_ATTR_WRITE_TOTAL:
114 *(u_int64_t *)data = ctx->write_total;
123 static const struct syscommon_resman_resource_attribute disk_attrs[] = {
125 .name = "DISK_ATTR_NAME",
126 .id = DISK_ATTR_NAME,
127 .type = SYSCOMMON_RESMAN_DATA_TYPE_STRING,
128 .flag = SYSCOMMON_RESMAN_RESOURCE_ATTR_FLAG_PUBLIC,
130 .get = disk_get_value,
133 .name = "DISK_ATTR_READ_PER_SEC",
134 .id = DISK_ATTR_READ_PER_SEC,
135 .type = SYSCOMMON_RESMAN_DATA_TYPE_DOUBLE,
136 .flag = SYSCOMMON_RESMAN_RESOURCE_ATTR_FLAG_PUBLIC,
138 .get = disk_get_value,
141 .name = "DISK_ATTR_WRITE_PER_SEC",
142 .id = DISK_ATTR_WRITE_PER_SEC,
143 .type = SYSCOMMON_RESMAN_DATA_TYPE_DOUBLE,
144 .flag = SYSCOMMON_RESMAN_RESOURCE_ATTR_FLAG_PUBLIC,
146 .get = disk_get_value,
149 .name = "DISK_ATTR_READ_TOTAL",
150 .id = DISK_ATTR_READ_TOTAL,
151 .type = SYSCOMMON_RESMAN_DATA_TYPE_UINT64,
152 .flag = SYSCOMMON_RESMAN_RESOURCE_ATTR_FLAG_PUBLIC,
154 .get = disk_get_value,
157 .name = "DISK_ATTR_WRITE_TOTAL",
158 .id = DISK_ATTR_WRITE_TOTAL,
159 .type = SYSCOMMON_RESMAN_DATA_TYPE_UINT64,
160 .flag = SYSCOMMON_RESMAN_RESOURCE_ATTR_FLAG_PUBLIC,
162 .get = disk_get_value,
167 static int disk_setup_device_id(int resource_id,
168 const struct syscommon_resman_resource_control *ctrl,
171 struct disk_context *ctx;
172 const struct syscommon_resman_resource_device *device;
173 int resource_index = (int)(intptr_t)data;
175 if (resource_id < 0 || !ctrl)
178 ctx = syscommon_resman_get_resource_privdata(resource_id);
182 device = syscommon_resman_find_resource_device(syscommon_resman_get_resource_type(resource_id), resource_index);
184 _E("Not available resource: type: %s, index: %d\n",
185 syscommon_resman_get_resource_name(resource_id), resource_index);
189 if (ctx->device_name)
190 free(ctx->device_name);
192 ctx->device_name = g_strdup(device->name);
193 ctx->index = resource_index;
198 static const struct syscommon_resman_resource_control disk_ctrls[] = {
200 .name = "DISK_CTRL_DEVICE_ID",
201 .id = DISK_CTRL_DEVICE_ID,
203 .set = disk_setup_device_id,
208 static int read_uptime(unsigned long long *uptime)
212 unsigned long up_sec, up_cent;
215 if ((fp = fopen("/proc/uptime", "r")) == NULL) {
217 } else if (fgets(line, sizeof(line), fp) == NULL) {
219 } else if (sscanf(line, "%lu.%lu", &up_sec, &up_cent) == 2) {
220 *uptime = (unsigned long long) up_sec * 100 +
221 (unsigned long long) up_cent;
232 static int read_disk_stats(char *device_name, struct io_stats *ios)
234 char filename[BUFF_MAX];
237 unsigned int ios_pgr, tot_ticks, rq_ticks, wr_ticks, dc_ticks, fl_ticks;
238 unsigned long rd_ios, rd_merges_or_rd_sec, wr_ios, wr_merges;
239 unsigned long rd_sec_or_wr_ios, wr_sec, rd_ticks_or_wr_sec;
240 unsigned long dc_ios, dc_merges, dc_sec, fl_ios;
242 if (!device_name || !ios)
245 snprintf(filename, BUFF_MAX, "/sys/class/block/%s/stat", device_name);
247 fp = fopen(filename, "r");
249 char errstr[BUFF_MAX];
251 strerror_r(errno, errstr, BUFF_MAX);
252 _E("failed to open block device(%s):%s\n", device_name, errstr);
256 i = fscanf(fp, "%lu %lu %lu %lu %lu %lu %lu %u %u %u %u %lu %lu %lu %u %lu %u",
257 &rd_ios, &rd_merges_or_rd_sec, &rd_sec_or_wr_ios, &rd_ticks_or_wr_sec,
258 &wr_ios, &wr_merges, &wr_sec, &wr_ticks, &ios_pgr, &tot_ticks, &rq_ticks,
259 &dc_ios, &dc_merges, &dc_sec, &dc_ticks,
262 memset(ios, 0, sizeof(*ios));
265 /* Device or partition */
266 ios->rd_ios = rd_ios;
267 ios->rd_merges = rd_merges_or_rd_sec;
268 ios->rd_sectors = rd_sec_or_wr_ios;
269 ios->rd_ticks = (unsigned int) rd_ticks_or_wr_sec;
270 ios->wr_ios = wr_ios;
271 ios->wr_merges = wr_merges;
272 ios->wr_sectors = wr_sec;
273 ios->wr_ticks = wr_ticks;
274 ios->ios_pgr = ios_pgr;
275 ios->tot_ticks = tot_ticks;
276 ios->rq_ticks = rq_ticks;
280 ios->dc_ios = dc_ios;
281 ios->dc_merges = dc_merges;
282 ios->dc_sectors = dc_sec;
283 ios->dc_ticks = dc_ticks;
288 ios->fl_ios = fl_ios;
289 ios->fl_ticks = fl_ticks;
293 /* Partition without extended statistics */
294 ios->rd_ios = rd_ios;
295 ios->rd_sectors = rd_merges_or_rd_sec;
296 ios->wr_ios = rd_sec_or_wr_ios;
297 ios->wr_sectors = rd_ticks_or_wr_sec;
305 static unsigned long long get_interval(unsigned long long prev_uptime,
306 unsigned long long curr_uptime)
308 unsigned long long interval = curr_uptime - prev_uptime;
309 return (interval == 0) ? 1 : interval;
312 static int calculate_disk_stats(struct disk_context *ctx)
314 struct io_stats *curr, *prev;
315 unsigned long long interval;
320 /* Calculate time interval in 1/100th of a second */
321 interval = get_interval(ctx->uptime_cs[PREV_IDX], ctx->uptime_cs[CURR_IDX]);
323 curr = &ctx->stats[CURR_IDX];
324 prev = &ctx->stats[PREV_IDX];
326 ctx->read_per_sec = GET_BPS(prev->rd_sectors, curr->rd_sectors, interval);
327 ctx->write_per_sec = GET_BPS(prev->wr_sectors, curr->wr_sectors, interval);
328 ctx->read_total = curr->rd_sectors;
329 ctx->write_total = curr->wr_sectors;
334 static int disk_create(int resource_id)
336 struct disk_context *ctx;
338 ctx = calloc(1, sizeof(struct disk_context));
344 syscommon_resman_set_resource_privdata(resource_id, ctx);
349 static void disk_delete(int resource_id)
351 struct disk_context *ctx;
356 ctx = syscommon_resman_get_resource_privdata(resource_id);
360 if (ctx->device_name)
361 free(ctx->device_name);
364 syscommon_resman_set_resource_privdata(resource_id, NULL);
367 static int disk_prepare_update(int resource_id)
369 struct disk_context *ctx;
375 ctx = syscommon_resman_get_resource_privdata(resource_id);
379 if (!ctx->device_name) {
380 _E("%s: DISK_CTRL_DEVICE_ID is not yet initialized\n",
381 syscommon_resman_get_resource_name(resource_id));
385 /* Backup current disk stats */
386 memcpy(&ctx->stats[PREV_IDX], &ctx->stats[CURR_IDX], sizeof(ctx->stats[CURR_IDX]));
387 ctx->uptime_cs[PREV_IDX] = ctx->uptime_cs[CURR_IDX];
389 /* Read system uptime */
390 ret = read_uptime(&(ctx->uptime_cs[CURR_IDX]));
394 /* Read disk stats */
395 ret = read_disk_stats(ctx->device_name, &ctx->stats[CURR_IDX]);
399 /* Calculate disk stats */
400 ret = calculate_disk_stats(ctx);
407 static const struct syscommon_resman_resource_driver disk_resource_driver = {
409 .type = RESOURCE_TYPE_DISK,
411 .num_attrs = ARRAY_SIZE(disk_attrs),
413 .num_ctrls = ARRAY_SIZE(disk_ctrls),
415 .create = disk_create,
416 .delete = disk_delete,
417 .prepare_update = disk_prepare_update,
420 SYSCOMMON_RESMAN_RESOURCE_DRIVER_REGISTER(&disk_resource_driver)