• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**************************************************************************
2  *
3  * Copyright (C) 2016 Steven Toth <stoth@kernellabs.com>
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 "os/os_thread.h"
39 #include "util/u_memory.h"
40 #include "util/u_string.h"
41 #include <stdio.h>
42 #include <unistd.h>
43 #include <dirent.h>
44 #include <stdlib.h>
45 #include <unistd.h>
46 #include <inttypes.h>
47 #include <sys/types.h>
48 #include <sys/stat.h>
49 #include <unistd.h>
50 
51 struct stat_s
52 {
53    /* Read */
54    uint64_t r_ios;
55    uint64_t r_merges;
56    uint64_t r_sectors;
57    uint64_t r_ticks;
58    /* Write */
59    uint64_t w_ios;
60    uint64_t w_merges;
61    uint64_t w_sectors;
62    uint64_t w_ticks;
63    /* Misc */
64    uint64_t in_flight;
65    uint64_t io_ticks;
66    uint64_t time_in_queue;
67 };
68 
69 struct diskstat_info
70 {
71    struct list_head list;
72    int mode; /* DISKSTAT_RD, DISKSTAT_WR */
73    char name[64]; /* EG. sda5 */
74 
75    char sysfs_filename[128];
76    uint64_t last_time;
77    struct stat_s last_stat;
78 };
79 
80 /* TODO: We don't handle dynamic block device / partition
81  * arrival or removal.
82  * Static globals specific to this HUD category.
83  */
84 static int gdiskstat_count = 0;
85 static struct list_head gdiskstat_list;
86 static mtx_t gdiskstat_mutex = _MTX_INITIALIZER_NP;
87 
88 static struct diskstat_info *
find_dsi_by_name(const char * n,int mode)89 find_dsi_by_name(const char *n, int mode)
90 {
91    list_for_each_entry(struct diskstat_info, dsi, &gdiskstat_list, list) {
92       if (dsi->mode != mode)
93          continue;
94       if (strcasecmp(dsi->name, n) == 0)
95          return dsi;
96    }
97    return 0;
98 }
99 
100 static int
get_file_values(const char * fn,struct stat_s * s)101 get_file_values(const char *fn, struct stat_s *s)
102 {
103    int ret = 0;
104    FILE *fh = fopen(fn, "r");
105    if (!fh)
106       return -1;
107 
108    ret = fscanf(fh,
109         "%" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64
110         " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 "",
111         &s->r_ios, &s->r_merges, &s->r_sectors, &s->r_ticks, &s->w_ios,
112         &s->w_merges, &s->w_sectors, &s->w_ticks, &s->in_flight, &s->io_ticks,
113         &s->time_in_queue);
114 
115    fclose(fh);
116 
117    return ret;
118 }
119 
120 static void
query_dsi_load(struct hud_graph * gr,struct pipe_context * pipe)121 query_dsi_load(struct hud_graph *gr, struct pipe_context *pipe)
122 {
123    /* The framework calls us periodically, compensate for the
124     * calling interval accordingly when reporting per second.
125     */
126    struct diskstat_info *dsi = gr->query_data;
127    uint64_t now = os_time_get();
128 
129    if (dsi->last_time) {
130       if (dsi->last_time + gr->pane->period <= now) {
131          struct stat_s stat;
132          if (get_file_values(dsi->sysfs_filename, &stat) < 0)
133             return;
134          float val = 0;
135 
136          switch (dsi->mode) {
137          case DISKSTAT_RD:
138             val =
139                ((stat.r_sectors -
140                  dsi->last_stat.r_sectors) * 512) /
141                (((float) gr->pane->period / 1000) / 1000);
142             break;
143          case DISKSTAT_WR:
144             val =
145                ((stat.w_sectors -
146                  dsi->last_stat.w_sectors) * 512) /
147                (((float) gr->pane->period / 1000) / 1000);
148             break;
149          }
150 
151          hud_graph_add_value(gr, (uint64_t) val);
152          dsi->last_stat = stat;
153          dsi->last_time = now;
154       }
155    }
156    else {
157       /* initialize */
158       switch (dsi->mode) {
159       case DISKSTAT_RD:
160       case DISKSTAT_WR:
161          get_file_values(dsi->sysfs_filename, &dsi->last_stat);
162          break;
163       }
164       dsi->last_time = now;
165    }
166 }
167 
168 /**
169   * Create and initialize a new object for a specific block I/O device.
170   * \param  pane  parent context.
171   * \param  dev_name  logical block device name, EG. sda5.
172   * \param  mode  query read or write (DISKSTAT_RD/DISKSTAT_WR) statistics.
173   */
174 void
hud_diskstat_graph_install(struct hud_pane * pane,const char * dev_name,unsigned int mode)175 hud_diskstat_graph_install(struct hud_pane *pane, const char *dev_name,
176                            unsigned int mode)
177 {
178    struct hud_graph *gr;
179    struct diskstat_info *dsi;
180 
181    int num_devs = hud_get_num_disks(0);
182    if (num_devs <= 0)
183       return;
184 
185    dsi = find_dsi_by_name(dev_name, mode);
186    if (!dsi)
187       return;
188 
189    gr = CALLOC_STRUCT(hud_graph);
190    if (!gr)
191       return;
192 
193    dsi->mode = mode;
194    if (dsi->mode == DISKSTAT_RD) {
195       snprintf(gr->name, sizeof(gr->name), "%s-Read-MB/s", dsi->name);
196    }
197    else if (dsi->mode == DISKSTAT_WR) {
198       snprintf(gr->name, sizeof(gr->name), "%s-Write-MB/s", dsi->name);
199    }
200    else {
201       free(gr);
202       return;
203    }
204 
205    gr->query_data = dsi;
206    gr->query_new_value = query_dsi_load;
207 
208    hud_pane_add_graph(pane, gr);
209    hud_pane_set_max_value(pane, 100);
210 }
211 
212 static void
add_object_part(const char * basename,const char * name,int objmode)213 add_object_part(const char *basename, const char *name, int objmode)
214 {
215    struct diskstat_info *dsi = CALLOC_STRUCT(diskstat_info);
216 
217    snprintf(dsi->name, sizeof(dsi->name), "%s", name);
218    snprintf(dsi->sysfs_filename, sizeof(dsi->sysfs_filename), "%s/%s/stat",
219       basename, name);
220    dsi->mode = objmode;
221    list_addtail(&dsi->list, &gdiskstat_list);
222    gdiskstat_count++;
223 }
224 
225 static void
add_object(const char * basename,const char * name,int objmode)226 add_object(const char *basename, const char *name, int objmode)
227 {
228    struct diskstat_info *dsi = CALLOC_STRUCT(diskstat_info);
229 
230    snprintf(dsi->name, sizeof(dsi->name), "%s", name);
231    snprintf(dsi->sysfs_filename, sizeof(dsi->sysfs_filename), "%s/stat",
232       basename);
233    dsi->mode = objmode;
234    list_addtail(&dsi->list, &gdiskstat_list);
235    gdiskstat_count++;
236 }
237 
238 /**
239   * Initialize internal object arrays and display block I/O HUD help.
240   * \param  displayhelp  true if the list of detected devices should be
241                          displayed on the console.
242   * \return  number of detected block I/O devices.
243   */
244 int
hud_get_num_disks(bool displayhelp)245 hud_get_num_disks(bool displayhelp)
246 {
247    struct dirent *dp;
248    struct stat stat_buf;
249    char name[64];
250 
251    /* Return the number of block devices and partitions. */
252    mtx_lock(&gdiskstat_mutex);
253    if (gdiskstat_count) {
254       mtx_unlock(&gdiskstat_mutex);
255       return gdiskstat_count;
256    }
257 
258    /* Scan /sys/block, for every object type we support, create and
259     * persist an object to represent its different statistics.
260     */
261    list_inithead(&gdiskstat_list);
262    DIR *dir = opendir("/sys/block/");
263    if (!dir) {
264       mtx_unlock(&gdiskstat_mutex);
265       return 0;
266    }
267 
268    while ((dp = readdir(dir)) != NULL) {
269 
270       /* Avoid 'lo' and '..' and '.' */
271       if (strlen(dp->d_name) <= 2)
272          continue;
273 
274       char basename[256];
275       snprintf(basename, sizeof(basename), "/sys/block/%s", dp->d_name);
276       snprintf(name, sizeof(name), "%s/stat", basename);
277       if (stat(name, &stat_buf) < 0)
278          continue;
279 
280       if (!S_ISREG(stat_buf.st_mode))
281          continue;              /* Not a regular file */
282 
283       /* Add a physical block device with R/W stats */
284       add_object(basename, dp->d_name, DISKSTAT_RD);
285       add_object(basename, dp->d_name, DISKSTAT_WR);
286 
287       /* Add any partitions */
288       struct dirent *dpart;
289       DIR *pdir = opendir(basename);
290       if (!pdir) {
291          mtx_unlock(&gdiskstat_mutex);
292          closedir(dir);
293          return 0;
294       }
295 
296       while ((dpart = readdir(pdir)) != NULL) {
297          /* Avoid 'lo' and '..' and '.' */
298          if (strlen(dpart->d_name) <= 2)
299             continue;
300 
301          char p[64];
302          snprintf(p, sizeof(p), "%s/%s/stat", basename, dpart->d_name);
303          if (stat(p, &stat_buf) < 0)
304             continue;
305 
306          if (!S_ISREG(stat_buf.st_mode))
307             continue;           /* Not a regular file */
308 
309          /* Add a partition with R/W stats */
310          add_object_part(basename, dpart->d_name, DISKSTAT_RD);
311          add_object_part(basename, dpart->d_name, DISKSTAT_WR);
312       }
313    }
314    closedir(dir);
315 
316    if (displayhelp) {
317       list_for_each_entry(struct diskstat_info, dsi, &gdiskstat_list, list) {
318          char line[32];
319          snprintf(line, sizeof(line), "    diskstat-%s-%s",
320                  dsi->mode == DISKSTAT_RD ? "rd" :
321                  dsi->mode == DISKSTAT_WR ? "wr" : "undefined", dsi->name);
322 
323          puts(line);
324       }
325    }
326    mtx_unlock(&gdiskstat_mutex);
327 
328    return gdiskstat_count;
329 }
330 
331 #endif /* HAVE_GALLIUM_EXTRA_HUD */
332