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