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