resource: Operate based on resource id
[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(int resource_id,
81                                 const struct syscommon_resman_resource_attribute *attr,
82                                 void *data)
83 {
84         struct disk_context *ctx;
85
86         if (resource_id < 0 || !attr || !data)
87                 return -EINVAL;
88
89         ctx = syscommon_resman_get_resource_privdata(resource_id);
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(resource_id),
96                                         syscommon_resman_get_resource_attr_name(resource_id, attr->id), attr->id);
97                 return -EINVAL;
98         }
99
100         switch (attr->id) {
101         case DISK_ATTR_NAME:
102                 strncpy((char *)data, ctx->device_name, BUFF_MAX);
103                 break;
104         case DISK_ATTR_READ_PER_SEC:
105                 *(double *)data = ctx->read_per_sec;
106                 break;
107         case DISK_ATTR_WRITE_PER_SEC:
108                 *(double *)data = ctx->write_per_sec;
109                 break;
110         case DISK_ATTR_READ_TOTAL:
111                 *(u_int64_t *)data = ctx->read_total;
112                 break;
113         case DISK_ATTR_WRITE_TOTAL:
114                 *(u_int64_t *)data = ctx->write_total;
115                 break;
116         default:
117                 return -EINVAL;
118         }
119
120         return 0;
121 }
122
123 static const struct syscommon_resman_resource_attribute disk_attrs[] = {
124         {
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,
129                 .ops    = {
130                         .get = disk_get_value,
131                 },
132         }, {
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,
137                 .ops    = {
138                         .get = disk_get_value,
139                 },
140         }, {
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,
145                 .ops    = {
146                         .get = disk_get_value,
147                 },
148         }, {
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,
153                 .ops    = {
154                         .get = disk_get_value,
155                 },
156         }, {
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,
161                 .ops    = {
162                         .get = disk_get_value,
163                 },
164         },
165 };
166
167 static int disk_setup_device_id(int resource_id,
168                                 const struct syscommon_resman_resource_control *ctrl,
169                                 const void *data)
170 {
171         struct disk_context *ctx;
172         const struct syscommon_resman_resource_device *device;
173         int resource_index = (int)(intptr_t)data;
174
175         if (resource_id < 0 || !ctrl)
176                 return -EINVAL;
177
178         ctx = syscommon_resman_get_resource_privdata(resource_id);
179         if (!ctx)
180                 return -EINVAL;
181
182         device = syscommon_resman_find_resource_device(syscommon_resman_get_resource_type(resource_id), resource_index);
183         if (!device) {
184                 _E("Not available resource: type: %s, index: %d\n",
185                                 syscommon_resman_get_resource_name(resource_id), resource_index);
186                 return -EINVAL;
187         }
188
189         if (ctx->device_name)
190                 free(ctx->device_name);
191
192         ctx->device_name = g_strdup(device->name);
193         ctx->index = resource_index;
194
195         return 0;
196 }
197
198 static const struct syscommon_resman_resource_control disk_ctrls[] = {
199         {
200                 .name = "DISK_CTRL_DEVICE_ID",
201                 .id = DISK_CTRL_DEVICE_ID,
202                 .ops = {
203                         .set = disk_setup_device_id,
204                 },
205         },
206 };
207
208 static int read_uptime(unsigned long long *uptime)
209 {
210         FILE *fp = NULL;
211         char line[BUFF_MAX];
212         unsigned long up_sec, up_cent;
213         int ret = 0;
214
215         if ((fp = fopen("/proc/uptime", "r")) == NULL) {
216                 ret = -ENOENT;
217         } else if (fgets(line, sizeof(line), fp) == NULL) {
218                 ret = -ENOENT;
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;
222         } else {
223                 ret = -EINVAL;
224         }
225
226         if (fp != NULL)
227                 fclose(fp);
228
229         return ret;
230 }
231
232 static int read_disk_stats(char *device_name, struct io_stats *ios)
233 {
234         char filename[BUFF_MAX];
235         FILE *fp;
236         int i;
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;
241
242         if (!device_name || !ios)
243                 return -EINVAL;
244
245         snprintf(filename, BUFF_MAX, "/sys/class/block/%s/stat", device_name);
246
247         fp = fopen(filename, "r");
248         if (!fp) {
249                 char errstr[BUFF_MAX];
250
251                 strerror_r(errno, errstr, BUFF_MAX);
252                 _E("failed to open block device(%s):%s\n", device_name, errstr);
253                 return -ENOENT;
254         }
255
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,
260                    &fl_ios, &fl_ticks);
261
262         memset(ios, 0, sizeof(*ios));
263
264         if (i >= 11) {
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;
277
278                 if (i >= 15) {
279                         /* Discard I/O */
280                         ios->dc_ios     = dc_ios;
281                         ios->dc_merges  = dc_merges;
282                         ios->dc_sectors = dc_sec;
283                         ios->dc_ticks   = dc_ticks;
284                 }
285
286                 if (i >= 17) {
287                         /* Flush I/O */
288                         ios->fl_ios     = fl_ios;
289                         ios->fl_ticks   = fl_ticks;
290                 }
291         }
292         else if (i == 4) {
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;
298         }
299
300         fclose(fp);
301
302         return 0;
303 }
304
305 static unsigned long long get_interval(unsigned long long prev_uptime,
306                                         unsigned long long curr_uptime)
307 {
308         unsigned long long interval = curr_uptime - prev_uptime;
309         return (interval == 0) ? 1 : interval;
310 }
311
312 static int calculate_disk_stats(struct disk_context *ctx)
313 {
314         struct io_stats *curr, *prev;
315         unsigned long long interval;
316
317         if (!ctx)
318                 return -EINVAL;
319
320         /* Calculate time interval in 1/100th of a second */
321         interval = get_interval(ctx->uptime_cs[PREV_IDX], ctx->uptime_cs[CURR_IDX]);
322
323         curr = &ctx->stats[CURR_IDX];
324         prev = &ctx->stats[PREV_IDX];
325
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;
330
331         return 0;
332 }
333
334 static int disk_create(int resource_id)
335 {
336         struct disk_context *ctx;
337
338         ctx = calloc(1, sizeof(struct disk_context));
339         if (!ctx)
340                 return -ENOMEM;
341
342         ctx->index = -1;
343
344         syscommon_resman_set_resource_privdata(resource_id, ctx);
345
346         return 0;
347 }
348
349 static void disk_delete(int resource_id)
350 {
351         struct disk_context *ctx;
352
353         if (resource_id < 0)
354                 return;
355
356         ctx = syscommon_resman_get_resource_privdata(resource_id);
357         if (!ctx)
358                 return;
359
360         if (ctx->device_name)
361                 free(ctx->device_name);
362
363         free(ctx);
364         syscommon_resman_set_resource_privdata(resource_id, NULL);
365 }
366
367 static int disk_prepare_update(int resource_id)
368 {
369         struct disk_context *ctx;
370         int ret;
371
372         if (resource_id < 0)
373                 return -EINVAL;
374
375         ctx = syscommon_resman_get_resource_privdata(resource_id);
376         if (!ctx)
377                 return -EINVAL;
378
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));
382                 return -EINVAL;
383         }
384
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];
388
389         /* Read system uptime */
390         ret = read_uptime(&(ctx->uptime_cs[CURR_IDX]));
391         if (ret < 0)
392                 return ret;
393
394         /* Read disk stats */
395         ret = read_disk_stats(ctx->device_name, &ctx->stats[CURR_IDX]);
396         if (ret < 0)
397                 return ret;
398
399         /* Calculate disk stats */
400         ret = calculate_disk_stats(ctx);
401         if (ret < 0)
402                 return ret;
403
404         return 0;
405 }
406
407 static const struct syscommon_resman_resource_driver disk_resource_driver = {
408         .name           = "DISK",
409         .type           = RESOURCE_TYPE_DISK,
410         .attrs          = disk_attrs,
411         .num_attrs      = ARRAY_SIZE(disk_attrs),
412         .ctrls          = disk_ctrls,
413         .num_ctrls      = ARRAY_SIZE(disk_ctrls),
414         .ops            = {
415                 .create = disk_create,
416                 .delete = disk_delete,
417                 .prepare_update = disk_prepare_update,
418         },
419 };
420 SYSCOMMON_RESMAN_RESOURCE_DRIVER_REGISTER(&disk_resource_driver)