xref: /aosp_15_r20/external/mesa3d/src/gallium/auxiliary/hud/hud_diskstat.c (revision 6104692788411f58d303aa86923a9ff6ecaded22)
1 /**************************************************************************
2  *
3  * Copyright (C) 2016 Steven Toth <[email protected]>
4  * Copyright (C) 2016 Zodiac Inflight Innovations
5  * All Rights Reserved.
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a
8  * copy of this software and associated documentation files (the
9  * "Software"), to deal in the Software without restriction, including
10  * without limitation the rights to use, copy, modify, merge, publish,
11  * distribute, sub license, and/or sell copies of the Software, and to
12  * permit persons to whom the Software is furnished to do so, subject to
13  * the following conditions:
14  *
15  * The above copyright notice and this permission notice (including the
16  * next paragraph) shall be included in all copies or substantial portions
17  * of the Software.
18  *
19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
22  * IN NO EVENT SHALL THE AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR
23  * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26  *
27  **************************************************************************/
28 
29 #ifdef HAVE_GALLIUM_EXTRA_HUD
30 
31 /* Purpose: Reading /sys/block/<*>/stat MB/s read/write throughput per second,
32  * displaying on the HUD.
33  */
34 
35 #include "hud/hud_private.h"
36 #include "util/list.h"
37 #include "util/os_time.h"
38 #include "util/simple_mtx.h"
39 #include "util/u_thread.h"
40 #include "util/u_memory.h"
41 #include "util/u_string.h"
42 #include <stdio.h>
43 #include <unistd.h>
44 #include <dirent.h>
45 #include <stdlib.h>
46 #include <unistd.h>
47 #include <inttypes.h>
48 #include <sys/types.h>
49 #include <sys/stat.h>
50 #include <unistd.h>
51 
52 struct stat_s
53 {
54    /* Read */
55    uint64_t r_ios;
56    uint64_t r_merges;
57    uint64_t r_sectors;
58    uint64_t r_ticks;
59    /* Write */
60    uint64_t w_ios;
61    uint64_t w_merges;
62    uint64_t w_sectors;
63    uint64_t w_ticks;
64    /* Misc */
65    uint64_t in_flight;
66    uint64_t io_ticks;
67    uint64_t time_in_queue;
68 };
69 
70 struct diskstat_info
71 {
72    struct list_head list;
73    int mode; /* DISKSTAT_RD, DISKSTAT_WR */
74    char name[64]; /* EG. sda5 */
75 
76    char sysfs_filename[128];
77    uint64_t last_time;
78    struct stat_s last_stat;
79 };
80 
81 /* TODO: We don't handle dynamic block device / partition
82  * arrival or removal.
83  * Static globals specific to this HUD category.
84  */
85 static int gdiskstat_count = 0;
86 static struct list_head gdiskstat_list;
87 static simple_mtx_t gdiskstat_mutex = SIMPLE_MTX_INITIALIZER;
88 
89 static struct diskstat_info *
find_dsi_by_name(const char * n,int mode)90 find_dsi_by_name(const char *n, int mode)
91 {
92    list_for_each_entry(struct diskstat_info, dsi, &gdiskstat_list, list) {
93       if (dsi->mode != mode)
94          continue;
95       if (strcasecmp(dsi->name, n) == 0)
96          return dsi;
97    }
98    return 0;
99 }
100 
101 static int
get_file_values(const char * fn,struct stat_s * s)102 get_file_values(const char *fn, struct stat_s *s)
103 {
104    int ret = 0;
105    FILE *fh = fopen(fn, "r");
106    if (!fh)
107       return -1;
108 
109    ret = fscanf(fh,
110         "%" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64
111         " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 "",
112         &s->r_ios, &s->r_merges, &s->r_sectors, &s->r_ticks, &s->w_ios,
113         &s->w_merges, &s->w_sectors, &s->w_ticks, &s->in_flight, &s->io_ticks,
114         &s->time_in_queue);
115 
116    fclose(fh);
117 
118    return ret;
119 }
120 
121 static void
query_dsi_load(struct hud_graph * gr,struct pipe_context * pipe)122 query_dsi_load(struct hud_graph *gr, struct pipe_context *pipe)
123 {
124    /* The framework calls us periodically, compensate for the
125     * calling interval accordingly when reporting per second.
126     */
127    struct diskstat_info *dsi = gr->query_data;
128    uint64_t now = os_time_get();
129 
130    if (dsi->last_time) {
131       if (dsi->last_time + gr->pane->period <= now) {
132          struct stat_s stat;
133          if (get_file_values(dsi->sysfs_filename, &stat) < 0)
134             return;
135          float val = 0;
136 
137          switch (dsi->mode) {
138          case DISKSTAT_RD:
139             val =
140                ((stat.r_sectors -
141                  dsi->last_stat.r_sectors) * 512) /
142                (((float) gr->pane->period / 1000) / 1000);
143             break;
144          case DISKSTAT_WR:
145             val =
146                ((stat.w_sectors -
147                  dsi->last_stat.w_sectors) * 512) /
148                (((float) gr->pane->period / 1000) / 1000);
149             break;
150          }
151 
152          hud_graph_add_value(gr, (uint64_t) val);
153          dsi->last_stat = stat;
154          dsi->last_time = now;
155       }
156    }
157    else {
158       /* initialize */
159       switch (dsi->mode) {
160       case DISKSTAT_RD:
161       case DISKSTAT_WR:
162          get_file_values(dsi->sysfs_filename, &dsi->last_stat);
163          break;
164       }
165       dsi->last_time = now;
166    }
167 }
168 
169 /**
170   * Create and initialize a new object for a specific block I/O device.
171   * \param  pane  parent context.
172   * \param  dev_name  logical block device name, EG. sda5.
173   * \param  mode  query read or write (DISKSTAT_RD/DISKSTAT_WR) statistics.
174   */
175 void
hud_diskstat_graph_install(struct hud_pane * pane,const char * dev_name,unsigned int mode)176 hud_diskstat_graph_install(struct hud_pane *pane, const char *dev_name,
177                            unsigned int mode)
178 {
179    struct hud_graph *gr;
180    struct diskstat_info *dsi;
181 
182    int num_devs = hud_get_num_disks(0);
183    if (num_devs <= 0)
184       return;
185 
186    dsi = find_dsi_by_name(dev_name, mode);
187    if (!dsi)
188       return;
189 
190    gr = CALLOC_STRUCT(hud_graph);
191    if (!gr)
192       return;
193 
194    dsi->mode = mode;
195    if (dsi->mode == DISKSTAT_RD) {
196       snprintf(gr->name, sizeof(gr->name), "%s-Read-MB/s", dsi->name);
197    }
198    else if (dsi->mode == DISKSTAT_WR) {
199       snprintf(gr->name, sizeof(gr->name), "%s-Write-MB/s", dsi->name);
200    }
201    else {
202       free(gr);
203       return;
204    }
205 
206    gr->query_data = dsi;
207    gr->query_new_value = query_dsi_load;
208 
209    hud_pane_add_graph(pane, gr);
210    hud_pane_set_max_value(pane, 100);
211 }
212 
213 static void
add_object_part(const char * basename,const char * name,int objmode)214 add_object_part(const char *basename, const char *name, int objmode)
215 {
216    struct diskstat_info *dsi = CALLOC_STRUCT(diskstat_info);
217 
218    snprintf(dsi->name, sizeof(dsi->name), "%s", name);
219    snprintf(dsi->sysfs_filename, sizeof(dsi->sysfs_filename), "%s/%s/stat",
220       basename, name);
221    dsi->mode = objmode;
222    list_addtail(&dsi->list, &gdiskstat_list);
223    gdiskstat_count++;
224 }
225 
226 static void
add_object(const char * basename,const char * name,int objmode)227 add_object(const char *basename, const char *name, int objmode)
228 {
229    struct diskstat_info *dsi = CALLOC_STRUCT(diskstat_info);
230 
231    snprintf(dsi->name, sizeof(dsi->name), "%s", name);
232    snprintf(dsi->sysfs_filename, sizeof(dsi->sysfs_filename), "%s/stat",
233       basename);
234    dsi->mode = objmode;
235    list_addtail(&dsi->list, &gdiskstat_list);
236    gdiskstat_count++;
237 }
238 
239 /**
240   * Initialize internal object arrays and display block I/O HUD help.
241   * \param  displayhelp  true if the list of detected devices should be
242                          displayed on the console.
243   * \return  number of detected block I/O devices.
244   */
245 int
hud_get_num_disks(bool displayhelp)246 hud_get_num_disks(bool displayhelp)
247 {
248    struct dirent *dp;
249    struct stat stat_buf;
250    char name[64];
251 
252    /* Return the number of block devices and partitions. */
253    simple_mtx_lock(&gdiskstat_mutex);
254    if (gdiskstat_count) {
255       simple_mtx_unlock(&gdiskstat_mutex);
256       return gdiskstat_count;
257    }
258 
259    /* Scan /sys/block, for every object type we support, create and
260     * persist an object to represent its different statistics.
261     */
262    list_inithead(&gdiskstat_list);
263    DIR *dir = opendir("/sys/block/");
264    if (!dir) {
265       simple_mtx_unlock(&gdiskstat_mutex);
266       return 0;
267    }
268 
269    while ((dp = readdir(dir)) != NULL) {
270 
271       /* Avoid 'lo' and '..' and '.' */
272       if (strlen(dp->d_name) <= 2)
273          continue;
274 
275       char basename[256];
276       snprintf(basename, sizeof(basename), "/sys/block/%s", dp->d_name);
277       snprintf(name, sizeof(name), "%s/stat", basename);
278       if (stat(name, &stat_buf) < 0)
279          continue;
280 
281       if (!S_ISREG(stat_buf.st_mode))
282          continue;              /* Not a regular file */
283 
284       /* Add a physical block device with R/W stats */
285       add_object(basename, dp->d_name, DISKSTAT_RD);
286       add_object(basename, dp->d_name, DISKSTAT_WR);
287 
288       /* Add any partitions */
289       struct dirent *dpart;
290       DIR *pdir = opendir(basename);
291       if (!pdir) {
292          simple_mtx_unlock(&gdiskstat_mutex);
293          closedir(dir);
294          return 0;
295       }
296 
297       while ((dpart = readdir(pdir)) != NULL) {
298          /* Avoid 'lo' and '..' and '.' */
299          if (strlen(dpart->d_name) <= 2)
300             continue;
301 
302          char p[64];
303          snprintf(p, sizeof(p), "%s/%s/stat", basename, dpart->d_name);
304          if (stat(p, &stat_buf) < 0)
305             continue;
306 
307          if (!S_ISREG(stat_buf.st_mode))
308             continue;           /* Not a regular file */
309 
310          /* Add a partition with R/W stats */
311          add_object_part(basename, dpart->d_name, DISKSTAT_RD);
312          add_object_part(basename, dpart->d_name, DISKSTAT_WR);
313       }
314    }
315    closedir(dir);
316 
317    if (displayhelp) {
318       list_for_each_entry(struct diskstat_info, dsi, &gdiskstat_list, list) {
319          char line[32];
320          snprintf(line, sizeof(line), "    diskstat-%s-%s",
321                  dsi->mode == DISKSTAT_RD ? "rd" :
322                  dsi->mode == DISKSTAT_WR ? "wr" : "undefined", dsi->name);
323 
324          puts(line);
325       }
326    }
327    simple_mtx_unlock(&gdiskstat_mutex);
328 
329    return gdiskstat_count;
330 }
331 
332 #endif /* HAVE_GALLIUM_EXTRA_HUD */
333