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