aff1ce3c2a98836843d683eb8a3ad96fe01e11b4
[platform/core/system/pass.git] / src / resource / resource-disk.c
1 /*
2  * PASS (Power Aware System Service) - Disk Resource Driver
3  *
4  * Copyright (c) 2022 Samsung Electronics Co., Ltd.
5  *
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
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
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.
17  */
18
19 /**
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
25  */
26
27 #include <glib.h>
28
29 #include <util/common.h>
30 #include <util/devices.h>
31 #include <util/log.h>
32
33 #include <libsyscommon/resource-manager.h>
34 #include <libsyscommon/resource-type.h>
35 #include <libsyscommon/resource-device.h>
36
37 #include <resource-monitor/resource-monitor.h>
38
39 enum {
40         PREV_IDX = 0,
41         CURR_IDX = 1,
42 };
43
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)
46
47 struct io_stats {
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 */
65 };
66
67 struct disk_context {
68         char *device_name;
69         int index;
70
71         unsigned long long uptime_cs[2];
72         struct io_stats stats[2];
73
74         double read_per_sec;
75         double write_per_sec;
76         u_int32_t read_total;
77         u_int32_t write_total;
78 };
79
80 static int disk_get_value(struct syscommon_resman_resource *res,
81                                 const struct syscommon_resman_resource_attribute *attr,
82                                 void *data)
83 {
84         struct disk_context *ctx;
85
86         if (!res || !attr || !data)
87                 return -EINVAL;
88
89         ctx = syscommon_resman_get_resource_privdata(res);
90         if (!ctx)
91                 return -EINVAL;
92
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);
96                 return -EINVAL;
97         }
98
99         switch (attr->id) {
100         case DISK_ATTR_NAME:
101                 strncpy((char *)data, ctx->device_name, BUFF_MAX);
102                 break;
103         case DISK_ATTR_READ_PER_SEC:
104                 *(double *)data = ctx->read_per_sec;
105                 break;
106         case DISK_ATTR_WRITE_PER_SEC:
107                 *(double *)data = ctx->write_per_sec;
108                 break;
109         case DISK_ATTR_READ_TOTAL:
110                 *(u_int64_t *)data = ctx->read_total;
111                 break;
112         case DISK_ATTR_WRITE_TOTAL:
113                 *(u_int64_t *)data = ctx->write_total;
114                 break;
115         default:
116                 return -EINVAL;
117         }
118
119         return 0;
120 }
121
122 static const struct syscommon_resman_resource_attribute disk_attrs[] = {
123         {
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,
128                 .ops    = {
129                         .get = disk_get_value,
130                 },
131         }, {
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,
136                 .ops    = {
137                         .get = disk_get_value,
138                 },
139         }, {
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,
144                 .ops    = {
145                         .get = disk_get_value,
146                 },
147         }, {
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,
152                 .ops    = {
153                         .get = disk_get_value,
154                 },
155         }, {
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,
160                 .ops    = {
161                         .get = disk_get_value,
162                 },
163         },
164 };
165
166 static int disk_setup_device_id(struct syscommon_resman_resource *res,
167                                 const struct syscommon_resman_resource_control *ctrl,
168                                 const void *data)
169 {
170         struct disk_context *ctx;
171         const struct syscommon_resman_resource_device *device;
172         int resource_index = (int)(intptr_t)data;
173
174         if (!res || !ctrl)
175                 return -EINVAL;
176
177         ctx = syscommon_resman_get_resource_privdata(res);
178         if (!ctx)
179                 return -EINVAL;
180
181         device = syscommon_resman_find_resource_device(syscommon_resman_get_resource_type(res), resource_index);
182         if (!device) {
183                 _E("Not available resource: type: %s, index: %d\n",
184                                 syscommon_resman_get_resource_name(res), resource_index);
185                 return -EINVAL;
186         }
187
188         if (ctx->device_name)
189                 free(ctx->device_name);
190
191         ctx->device_name = g_strdup(device->name);
192         ctx->index = resource_index;
193
194         return 0;
195 }
196
197 static const struct syscommon_resman_resource_control disk_ctrls[] = {
198         {
199                 .name = "DISK_CTRL_DEVICE_ID",
200                 .id = DISK_CTRL_DEVICE_ID,
201                 .ops = {
202                         .set = disk_setup_device_id,
203                 },
204         },
205 };
206
207 static int read_uptime(unsigned long long *uptime)
208 {
209         FILE *fp = NULL;
210         char line[BUFF_MAX];
211         unsigned long up_sec, up_cent;
212         int ret = 0;
213
214         if ((fp = fopen("/proc/uptime", "r")) == NULL) {
215                 ret = -ENOENT;
216         } else if (fgets(line, sizeof(line), fp) == NULL) {
217                 ret = -ENOENT;
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;
221         } else {
222                 ret = -EINVAL;
223         }
224
225         if (fp != NULL)
226                 fclose(fp);
227
228         return ret;
229 }
230
231 static int read_disk_stats(char *device_name, struct io_stats *ios)
232 {
233         char filename[BUFF_MAX];
234         FILE *fp;
235         int i;
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;
240
241         if (!device_name || !ios)
242                 return -EINVAL;
243
244         snprintf(filename, BUFF_MAX, "/sys/class/block/%s/stat", device_name);
245
246         fp = fopen(filename, "r");
247         if (!fp) {
248                 char errstr[BUFF_MAX];
249
250                 strerror_r(errno, errstr, BUFF_MAX);
251                 _E("failed to open block device(%s):%s\n", device_name, errstr);
252                 return -ENOENT;
253         }
254
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,
259                    &fl_ios, &fl_ticks);
260
261         memset(ios, 0, sizeof(*ios));
262
263         if (i >= 11) {
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;
276
277                 if (i >= 15) {
278                         /* Discard I/O */
279                         ios->dc_ios     = dc_ios;
280                         ios->dc_merges  = dc_merges;
281                         ios->dc_sectors = dc_sec;
282                         ios->dc_ticks   = dc_ticks;
283                 }
284
285                 if (i >= 17) {
286                         /* Flush I/O */
287                         ios->fl_ios     = fl_ios;
288                         ios->fl_ticks   = fl_ticks;
289                 }
290         }
291         else if (i == 4) {
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;
297         }
298
299         fclose(fp);
300
301         return 0;
302 }
303
304 static unsigned long long get_interval(unsigned long long prev_uptime,
305                                         unsigned long long curr_uptime)
306 {
307         unsigned long long interval = curr_uptime - prev_uptime;
308         return (interval == 0) ? 1 : interval;
309 }
310
311 static int calculate_disk_stats(struct disk_context *ctx)
312 {
313         struct io_stats *curr, *prev;
314         unsigned long long interval;
315
316         if (!ctx)
317                 return -EINVAL;
318
319         /* Calculate time interval in 1/100th of a second */
320         interval = get_interval(ctx->uptime_cs[PREV_IDX], ctx->uptime_cs[CURR_IDX]);
321
322         curr = &ctx->stats[CURR_IDX];
323         prev = &ctx->stats[PREV_IDX];
324
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;
329
330         return 0;
331 }
332
333 static int disk_create(struct syscommon_resman_resource *res)
334 {
335         struct disk_context *ctx;
336
337         ctx = calloc(1, sizeof(struct disk_context));
338         if (!ctx)
339                 return -ENOMEM;
340
341         ctx->index = -1;
342
343         syscommon_resman_set_resource_privdata(res, ctx);
344
345         return 0;
346 }
347
348 static void disk_delete(struct syscommon_resman_resource *res)
349 {
350         struct disk_context *ctx;
351
352         if (!res)
353                 return;
354
355         ctx = syscommon_resman_get_resource_privdata(res);
356         if (!ctx)
357                 return;
358
359         if (ctx->device_name)
360                 free(ctx->device_name);
361
362         free(ctx);
363         syscommon_resman_set_resource_privdata(res, NULL);
364 }
365
366 static int disk_prepare_update(struct syscommon_resman_resource *res)
367 {
368         struct disk_context *ctx;
369         int ret;
370
371         if (!res)
372                 return -EINVAL;
373
374         ctx = syscommon_resman_get_resource_privdata(res);
375         if (!ctx)
376                 return -EINVAL;
377
378         if (!ctx->device_name) {
379                 _E("%s: DISK_CTRL_DEVICE_ID is not yet initialized\n",
380                                         syscommon_resman_get_resource_name(res));
381                 return -EINVAL;
382         }
383
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];
387
388         /* Read system uptime */
389         ret = read_uptime(&(ctx->uptime_cs[CURR_IDX]));
390         if (ret < 0)
391                 return ret;
392
393         /* Read disk stats */
394         ret = read_disk_stats(ctx->device_name, &ctx->stats[CURR_IDX]);
395         if (ret < 0)
396                 return ret;
397
398         /* Calculate disk stats */
399         ret = calculate_disk_stats(ctx);
400         if (ret < 0)
401                 return ret;
402
403         return 0;
404 }
405
406 static const struct syscommon_resman_resource_driver disk_resource_driver = {
407         .name           = "DISK",
408         .type           = RESOURCE_TYPE_DISK,
409         .attrs          = disk_attrs,
410         .num_attrs      = ARRAY_SIZE(disk_attrs),
411         .ctrls          = disk_ctrls,
412         .num_ctrls      = ARRAY_SIZE(disk_ctrls),
413         .ops            = {
414                 .create = disk_create,
415                 .delete = disk_delete,
416                 .prepare_update = disk_prepare_update,
417         },
418 };
419 SYSCOMMON_RESMAN_RESOURCE_DRIVER_REGISTER(&disk_resource_driver)