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 "os/os_thread.h"
39 #include "util/u_memory.h"
46 #include <sys/types.h>
65 uint64_t time_in_queue
;
70 struct list_head list
;
71 int mode
; /* DISKSTAT_RD, DISKSTAT_WR */
72 char name
[64]; /* EG. sda5 */
74 char sysfs_filename
[128];
76 struct stat_s last_stat
;
79 /* TODO: We don't handle dynamic block device / partition
81 * Static globals specific to this HUD category.
83 static int gdiskstat_count
= 0;
84 static struct list_head gdiskstat_list
;
85 static mtx_t gdiskstat_mutex
= _MTX_INITIALIZER_NP
;
87 static struct diskstat_info
*
88 find_dsi_by_name(const char *n
, int mode
)
90 list_for_each_entry(struct diskstat_info
, dsi
, &gdiskstat_list
, list
) {
91 if (dsi
->mode
!= mode
)
93 if (strcasecmp(dsi
->name
, n
) == 0)
100 get_file_values(const char *fn
, struct stat_s
*s
)
103 FILE *fh
= fopen(fn
, "r");
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
,
120 query_dsi_load(struct hud_graph
*gr
)
122 /* The framework calls us periodically, compensate for the
123 * calling interval accordingly when reporting per second.
125 struct diskstat_info
*dsi
= gr
->query_data
;
126 uint64_t now
= os_time_get();
128 if (dsi
->last_time
) {
129 if (dsi
->last_time
+ gr
->pane
->period
<= now
) {
131 if (get_file_values(dsi
->sysfs_filename
, &stat
) < 0)
139 dsi
->last_stat
.r_sectors
) * 512) /
140 (((float) gr
->pane
->period
/ 1000) / 1000);
145 dsi
->last_stat
.w_sectors
) * 512) /
146 (((float) gr
->pane
->period
/ 1000) / 1000);
150 hud_graph_add_value(gr
, (uint64_t) val
);
151 dsi
->last_stat
= stat
;
152 dsi
->last_time
= now
;
160 get_file_values(dsi
->sysfs_filename
, &dsi
->last_stat
);
163 dsi
->last_time
= now
;
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.
174 hud_diskstat_graph_install(struct hud_pane
*pane
, const char *dev_name
,
177 struct hud_graph
*gr
;
178 struct diskstat_info
*dsi
;
180 int num_devs
= hud_get_num_disks(0);
184 dsi
= find_dsi_by_name(dev_name
, mode
);
188 gr
= CALLOC_STRUCT(hud_graph
);
193 if (dsi
->mode
== DISKSTAT_RD
) {
194 snprintf(gr
->name
, sizeof(gr
->name
), "%s-Read-MB/s", dsi
->name
);
196 else if (dsi
->mode
== DISKSTAT_WR
) {
197 snprintf(gr
->name
, sizeof(gr
->name
), "%s-Write-MB/s", dsi
->name
);
202 gr
->query_data
= dsi
;
203 gr
->query_new_value
= query_dsi_load
;
205 hud_pane_add_graph(pane
, gr
);
206 hud_pane_set_max_value(pane
, 100);
210 add_object_part(const char *basename
, const char *name
, int objmode
)
212 struct diskstat_info
*dsi
= CALLOC_STRUCT(diskstat_info
);
214 strcpy(dsi
->name
, name
);
215 snprintf(dsi
->sysfs_filename
, sizeof(dsi
->sysfs_filename
), "%s/%s/stat",
218 list_addtail(&dsi
->list
, &gdiskstat_list
);
223 add_object(const char *basename
, const char *name
, int objmode
)
225 struct diskstat_info
*dsi
= CALLOC_STRUCT(diskstat_info
);
227 strcpy(dsi
->name
, name
);
228 snprintf(dsi
->sysfs_filename
, sizeof(dsi
->sysfs_filename
), "%s/stat",
231 list_addtail(&dsi
->list
, &gdiskstat_list
);
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.
242 hud_get_num_disks(bool displayhelp
)
245 struct stat stat_buf
;
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
;
255 /* Scan /sys/block, for every object type we support, create and
256 * persist an object to represent its different statistics.
258 list_inithead(&gdiskstat_list
);
259 DIR *dir
= opendir("/sys/block/");
261 mtx_unlock(&gdiskstat_mutex
);
265 while ((dp
= readdir(dir
)) != NULL
) {
267 /* Avoid 'lo' and '..' and '.' */
268 if (strlen(dp
->d_name
) <= 2)
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)
277 if (!S_ISREG(stat_buf
.st_mode
))
278 continue; /* Not a regular file */
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
);
284 /* Add any partitions */
285 struct dirent
*dpart
;
286 DIR *pdir
= opendir(basename
);
288 mtx_unlock(&gdiskstat_mutex
);
293 while ((dpart
= readdir(pdir
)) != NULL
) {
294 /* Avoid 'lo' and '..' and '.' */
295 if (strlen(dpart
->d_name
) <= 2)
299 snprintf(p
, sizeof(p
), "%s/%s/stat", basename
, dpart
->d_name
);
300 if (stat(p
, &stat_buf
) < 0)
303 if (!S_ISREG(stat_buf
.st_mode
))
304 continue; /* Not a regular file */
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
);
314 list_for_each_entry(struct diskstat_info
, dsi
, &gdiskstat_list
, list
) {
316 snprintf(line
, sizeof(line
), " diskstat-%s-%s",
317 dsi
->mode
== DISKSTAT_RD
? "rd" :
318 dsi
->mode
== DISKSTAT_WR
? "wr" : "undefined", dsi
->name
);
323 mtx_unlock(&gdiskstat_mutex
);
325 return gdiskstat_count
;
328 #endif /* HAVE_GALLIUM_EXTRA_HUD */