1 /**************************************************************************
3 * Copyright (C) 2016 Steven Toth <stoth@kernellabs.com>
4 * Copyright (C) 2016 Zodiac Inflight Innovations
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:
15 * The above copyright notice and this permission notice (including the
16 * next paragraph) shall be included in all copies or substantial portions
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.
27 **************************************************************************/
29 #if HAVE_GALLIUM_EXTRA_HUD
31 /* Purpose: Reading /sys/block/<*>/stat MB/s read/write throughput per second,
32 * displaying on the HUD.
35 #include "hud/hud_private.h"
36 #include "util/list.h"
37 #include "os/os_time.h"
38 #include "util/u_memory.h"
45 #include <sys/types.h>
64 uint64_t time_in_queue
;
69 struct list_head list
;
70 int mode
; /* DISKSTAT_RD, DISKSTAT_WR */
71 char name
[64]; /* EG. sda5 */
73 char sysfs_filename
[128];
75 struct stat_s last_stat
;
78 /* TODO: We don't handle dynamic block device / partition
80 * Static globals specific to this HUD category.
82 static int gdiskstat_count
= 0;
83 static struct list_head gdiskstat_list
;
85 static struct diskstat_info
*
86 find_dsi_by_name(const char *n
, int mode
)
88 list_for_each_entry(struct diskstat_info
, dsi
, &gdiskstat_list
, list
) {
89 if (dsi
->mode
!= mode
)
91 if (strcasecmp(dsi
->name
, n
) == 0)
98 get_file_values(const char *fn
, struct stat_s
*s
)
101 FILE *fh
= fopen(fn
, "r");
106 "%" PRIu64
" %" PRIu64
" %" PRIu64
" %" PRIu64
" %" PRIu64
" %" PRIu64
107 " %" PRIu64
" %" PRIu64
" %" PRIu64
" %" PRIu64
" %" PRIu64
"",
108 &s
->r_ios
, &s
->r_merges
, &s
->r_sectors
, &s
->r_ticks
, &s
->w_ios
,
109 &s
->w_merges
, &s
->w_sectors
, &s
->w_ticks
, &s
->in_flight
, &s
->io_ticks
,
118 query_dsi_load(struct hud_graph
*gr
)
120 /* The framework calls us periodically, compensate for the
121 * calling interval accordingly when reporting per second.
123 struct diskstat_info
*dsi
= gr
->query_data
;
124 uint64_t now
= os_time_get();
126 if (dsi
->last_time
) {
127 if (dsi
->last_time
+ gr
->pane
->period
<= now
) {
129 if (get_file_values(dsi
->sysfs_filename
, &stat
) < 0)
137 dsi
->last_stat
.r_sectors
) * 512) /
138 (((float) gr
->pane
->period
/ 1000) / 1000);
143 dsi
->last_stat
.w_sectors
) * 512) /
144 (((float) gr
->pane
->period
/ 1000) / 1000);
148 hud_graph_add_value(gr
, (uint64_t) val
);
149 dsi
->last_stat
= stat
;
150 dsi
->last_time
= now
;
158 get_file_values(dsi
->sysfs_filename
, &dsi
->last_stat
);
161 dsi
->last_time
= now
;
166 * Create and initialize a new object for a specific block I/O device.
167 * \param pane parent context.
168 * \param dev_name logical block device name, EG. sda5.
169 * \param mode query read or write (DISKSTAT_RD/DISKSTAT_WR) statistics.
172 hud_diskstat_graph_install(struct hud_pane
*pane
, const char *dev_name
,
175 struct hud_graph
*gr
;
176 struct diskstat_info
*dsi
;
178 int num_devs
= hud_get_num_disks(0);
182 dsi
= find_dsi_by_name(dev_name
, mode
);
186 gr
= CALLOC_STRUCT(hud_graph
);
191 if (dsi
->mode
== DISKSTAT_RD
) {
192 snprintf(gr
->name
, sizeof(gr
->name
), "%s-Read-MB/s", dsi
->name
);
194 else if (dsi
->mode
== DISKSTAT_WR
) {
195 snprintf(gr
->name
, sizeof(gr
->name
), "%s-Write-MB/s", dsi
->name
);
200 gr
->query_data
= dsi
;
201 gr
->query_new_value
= query_dsi_load
;
203 hud_pane_add_graph(pane
, gr
);
204 hud_pane_set_max_value(pane
, 100);
208 add_object_part(const char *basename
, const char *name
, int objmode
)
210 struct diskstat_info
*dsi
= CALLOC_STRUCT(diskstat_info
);
212 strcpy(dsi
->name
, name
);
213 snprintf(dsi
->sysfs_filename
, sizeof(dsi
->sysfs_filename
), "%s/%s/stat",
216 list_addtail(&dsi
->list
, &gdiskstat_list
);
221 add_object(const char *basename
, const char *name
, int objmode
)
223 struct diskstat_info
*dsi
= CALLOC_STRUCT(diskstat_info
);
225 strcpy(dsi
->name
, name
);
226 snprintf(dsi
->sysfs_filename
, sizeof(dsi
->sysfs_filename
), "%s/stat",
229 list_addtail(&dsi
->list
, &gdiskstat_list
);
234 * Initialize internal object arrays and display block I/O HUD help.
235 * \param displayhelp true if the list of detected devices should be
236 displayed on the console.
237 * \return number of detected block I/O devices.
240 hud_get_num_disks(bool displayhelp
)
243 struct stat stat_buf
;
246 /* Return the number of block devices and partitions. */
248 return gdiskstat_count
;
250 /* Scan /sys/block, for every object type we support, create and
251 * persist an object to represent its different statistics.
253 list_inithead(&gdiskstat_list
);
254 DIR *dir
= opendir("/sys/block/");
258 while ((dp
= readdir(dir
)) != NULL
) {
260 /* Avoid 'lo' and '..' and '.' */
261 if (strlen(dp
->d_name
) <= 2)
265 snprintf(basename
, sizeof(basename
), "/sys/block/%s", dp
->d_name
);
266 snprintf(name
, sizeof(name
), "%s/stat", basename
);
267 if (stat(name
, &stat_buf
) < 0)
270 if (!S_ISREG(stat_buf
.st_mode
))
271 continue; /* Not a regular file */
273 /* Add a physical block device with R/W stats */
274 add_object(basename
, dp
->d_name
, DISKSTAT_RD
);
275 add_object(basename
, dp
->d_name
, DISKSTAT_WR
);
277 /* Add any partitions */
278 struct dirent
*dpart
;
279 DIR *pdir
= opendir(basename
);
283 while ((dpart
= readdir(pdir
)) != NULL
) {
284 /* Avoid 'lo' and '..' and '.' */
285 if (strlen(dpart
->d_name
) <= 2)
289 snprintf(p
, sizeof(p
), "%s/%s/stat", basename
, dpart
->d_name
);
290 if (stat(p
, &stat_buf
) < 0)
293 if (!S_ISREG(stat_buf
.st_mode
))
294 continue; /* Not a regular file */
296 /* Add a partition with R/W stats */
297 add_object_part(basename
, dpart
->d_name
, DISKSTAT_RD
);
298 add_object_part(basename
, dpart
->d_name
, DISKSTAT_WR
);
303 list_for_each_entry(struct diskstat_info
, dsi
, &gdiskstat_list
, list
) {
305 snprintf(line
, sizeof(line
), " diskstat-%s-%s",
306 dsi
->mode
== DISKSTAT_RD
? "rd" :
307 dsi
->mode
== DISKSTAT_WR
? "wr" : "undefined", dsi
->name
);
313 return gdiskstat_count
;
316 #endif /* HAVE_GALLIUM_EXTRA_HUD */