diff options
author | Steven Toth <[email protected]> | 2016-09-28 12:58:00 -0600 |
---|---|---|
committer | Brian Paul <[email protected]> | 2016-09-28 16:18:05 -0600 |
commit | 8c60bcb4c317026e017a8ecffe303fd4e7f0db33 (patch) | |
tree | a69adc68dd1f442409ff70b07b5f592157c154d7 /src/gallium/auxiliary/hud/hud_diskstat.c | |
parent | 29783c0887b3afd345e15e4e6910c04185219812 (diff) |
gallium/hud: Add support for block I/O, network I/O and lmsensor stats
V8: Feedback based on peer review
convert if block into a switch
Constify some func args
V7: Increase precision when measuring lmsensors volts
Flatten patch series.
V6: Feedback based on peer review
Simplify sensor initialization (arg passing).
Constify some func args
V5: Feedback based on peer review
Convert sprintf to snprintf
Convert char * to const char *
int arg converted to bool
Func changes to take a filename vs a larger struct.
Omit the space between '*' and the param name.
V4: Merged with master as of 2016/9/27 6pm
V3: Flatten the entire patchset ready for the ML
V2: Additional seperate patches based on feedback
a) configure.ac: Add a comment related to libsensors
b) HUD: Disable Block/NIC I/O stats by default.
Implement configuration option --enable-gallium-extra-hud=yes
and enable both statistics when this option is enabled.
c) Configure.ac: Minor cleanup to user visible configuration settings
d) Configure.ac: HUD stats - build system improvements
Move the -lsensors out of a deeper Makefile, bring it into the configure.ac.
Also, rename a compiler directive to more closely follow the standard.
V1: Initial release to the ML
Three new features:
1. Disk/block I/O device read/write stats MB/ps.
2. Network Interface RX/TX transfer statistics as a percentage
of the overall NIC speed.
3. lmsensor power, voltage and temperature sensors.
The lmsensor changes makes a dependency on libsensors so support
for the change is opt out by default.
Signed-off-by: Steven Toth <[email protected]>
Reviewed-by: Brian Paul <[email protected]>
Diffstat (limited to 'src/gallium/auxiliary/hud/hud_diskstat.c')
-rw-r--r-- | src/gallium/auxiliary/hud/hud_diskstat.c | 337 |
1 files changed, 337 insertions, 0 deletions
diff --git a/src/gallium/auxiliary/hud/hud_diskstat.c b/src/gallium/auxiliary/hud/hud_diskstat.c new file mode 100644 index 00000000000..a2290cc428e --- /dev/null +++ b/src/gallium/auxiliary/hud/hud_diskstat.c @@ -0,0 +1,337 @@ +/************************************************************************** + * + * Copyright (C) 2016 Steven Toth <[email protected]> + * Copyright (C) 2016 Zodiac Inflight Innovations + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +#if HAVE_GALLIUM_EXTRA_HUD + +/* Purpose: Reading /sys/block/<*>/stat MB/s read/write throughput per second, + * displaying on the HUD. + */ + +#include "hud/hud_private.h" +#include "util/list.h" +#include "os/os_time.h" +#include "util/u_memory.h" +#include <stdio.h> +#include <unistd.h> +#include <dirent.h> +#include <stdlib.h> +#include <unistd.h> +#include <inttypes.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> + +#define LOCAL_DEBUG 0 + +struct stat_s +{ + /* Read */ + uint64_t r_ios; + uint64_t r_merges; + uint64_t r_sectors; + uint64_t r_ticks; + /* Write */ + uint64_t w_ios; + uint64_t w_merges; + uint64_t w_sectors; + uint64_t w_ticks; + /* Misc */ + uint64_t in_flight; + uint64_t io_ticks; + uint64_t time_in_queue; +}; + +struct diskstat_info +{ + struct list_head list; + int mode; /* DISKSTAT_RD, DISKSTAT_WR */ + char name[64]; /* EG. sda5 */ + + char sysfs_filename[128]; + uint64_t last_time; + struct stat_s last_stat; +}; + +/* TODO: We don't handle dynamic block device / partition + * arrival or removal. + * Static globals specific to this HUD category. + */ +static int gdiskstat_count = 0; +static struct list_head gdiskstat_list; + +static struct diskstat_info * +find_dsi_by_name(const char *n, int mode) +{ + list_for_each_entry(struct diskstat_info, dsi, &gdiskstat_list, list) { + if (dsi->mode != mode) + continue; + if (strcasecmp(dsi->name, n) == 0) + return dsi; + } + return 0; +} + +static int +get_file_values(const char *fn, struct stat_s *s) +{ + int ret = 0; + FILE *fh = fopen(fn, "r"); + if (!fh) + return -1; + + ret = fscanf(fh, + "%" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 + " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 "", + &s->r_ios, &s->r_merges, &s->r_sectors, &s->r_ticks, &s->w_ios, + &s->w_merges, &s->w_sectors, &s->w_ticks, &s->in_flight, &s->io_ticks, + &s->time_in_queue); + + fclose(fh); + + return ret; +} + +static void +query_dsi_load(struct hud_graph *gr) +{ + /* The framework calls us periodically, compensate for the + * calling interval accordingly when reporting per second. + */ + struct diskstat_info *dsi = gr->query_data; + uint64_t now = os_time_get(); + + if (dsi->last_time) { + if (dsi->last_time + gr->pane->period <= now) { + struct stat_s stat; + if (get_file_values(dsi->sysfs_filename, &stat) < 0) + return; + float val = 0; + + switch (dsi->mode) { + case DISKSTAT_RD: + val = + ((stat.r_sectors - + dsi->last_stat.r_sectors) * 512) / + (((float) gr->pane->period / 1000) / 1000); + break; + case DISKSTAT_WR: + val = + ((stat.w_sectors - + dsi->last_stat.w_sectors) * 512) / + (((float) gr->pane->period / 1000) / 1000); + break; + } + + hud_graph_add_value(gr, (uint64_t) val); + dsi->last_stat = stat; + dsi->last_time = now; + } + } + else { + /* initialize */ + switch (dsi->mode) { + case DISKSTAT_RD: + case DISKSTAT_WR: + get_file_values(dsi->sysfs_filename, &dsi->last_stat); + break; + } + dsi->last_time = now; + } +} + +static void +free_query_data(void *p) +{ + struct diskstat_info *nic = (struct diskstat_info *) p; + list_del(&nic->list); + FREE(nic); +} + +/** + * Create and initialize a new object for a specific block I/O device. + * \param pane parent context. + * \param dev_name logical block device name, EG. sda5. + * \param mode query read or write (DISKSTAT_RD/DISKSTAT_WR) statistics. + */ +void +hud_diskstat_graph_install(struct hud_pane *pane, const char *dev_name, + unsigned int mode) +{ + struct hud_graph *gr; + struct diskstat_info *dsi; + + int num_devs = hud_get_num_disks(0); + if (num_devs <= 0) + return; + +#if LOCAL_DEBUG + printf("%s(%s, %s) - Creating HUD object\n", __func__, dev_name, + mode == DISKSTAT_RD ? "RD" : + mode == DISKSTAT_WR ? "WR" : "UNDEFINED"); +#endif + + dsi = find_dsi_by_name(dev_name, mode); + if (!dsi) + return; + + gr = CALLOC_STRUCT(hud_graph); + if (!gr) + return; + + dsi->mode = mode; + if (dsi->mode == DISKSTAT_RD) { + snprintf(gr->name, sizeof(gr->name), "%s-Read-MB/s", dsi->name); + } + else if (dsi->mode == DISKSTAT_WR) { + snprintf(gr->name, sizeof(gr->name), "%s-Write-MB/s", dsi->name); + } + else + return; + + gr->query_data = dsi; + gr->query_new_value = query_dsi_load; + + /* Don't use free() as our callback as that messes up Gallium's + * memory debugger. Use simple free_query_data() wrapper. + */ + gr->free_query_data = free_query_data; + + hud_pane_add_graph(pane, gr); + hud_pane_set_max_value(pane, 100); +} + +static void +add_object_part(const char *basename, const char *name, int objmode) +{ + struct diskstat_info *dsi = CALLOC_STRUCT(diskstat_info); + + strcpy(dsi->name, name); + snprintf(dsi->sysfs_filename, sizeof(dsi->sysfs_filename), "%s/%s/stat", + basename, name); + dsi->mode = objmode; + list_addtail(&dsi->list, &gdiskstat_list); + gdiskstat_count++; +} + +static void +add_object(const char *basename, const char *name, int objmode) +{ + struct diskstat_info *dsi = CALLOC_STRUCT(diskstat_info); + + strcpy(dsi->name, name); + snprintf(dsi->sysfs_filename, sizeof(dsi->sysfs_filename), "%s/stat", + basename); + dsi->mode = objmode; + list_addtail(&dsi->list, &gdiskstat_list); + gdiskstat_count++; +} + +/** + * Initialize internal object arrays and display block I/O HUD help. + * \param displayhelp true if the list of detected devices should be + displayed on the console. + * \return number of detected block I/O devices. + */ +int +hud_get_num_disks(bool displayhelp) +{ + struct dirent *dp; + struct stat stat_buf; + char name[64]; + + /* Return the number of block devices and partitions. */ + if (gdiskstat_count) + return gdiskstat_count; + + /* Scan /sys/block, for every object type we support, create and + * persist an object to represent its different statistics. + */ + list_inithead(&gdiskstat_list); + DIR *dir = opendir("/sys/block/"); + if (!dir) + return 0; + + while ((dp = readdir(dir)) != NULL) { + + /* Avoid 'lo' and '..' and '.' */ + if (strlen(dp->d_name) <= 2) + continue; + + char basename[256]; + snprintf(basename, sizeof(basename), "/sys/block/%s", dp->d_name); + snprintf(name, sizeof(name), "%s/stat", basename); + if (stat(name, &stat_buf) < 0) + continue; + + if (!S_ISREG(stat_buf.st_mode)) + continue; /* Not a regular file */ + + /* Add a physical block device with R/W stats */ + add_object(basename, dp->d_name, DISKSTAT_RD); + add_object(basename, dp->d_name, DISKSTAT_WR); + + /* Add any partitions */ + struct dirent *dpart; + DIR *pdir = opendir(basename); + if (!pdir) + return 0; + + while ((dpart = readdir(pdir)) != NULL) { + /* Avoid 'lo' and '..' and '.' */ + if (strlen(dpart->d_name) <= 2) + continue; + + char p[64]; + snprintf(p, sizeof(p), "%s/%s/stat", basename, dpart->d_name); + if (stat(p, &stat_buf) < 0) + continue; + + if (!S_ISREG(stat_buf.st_mode)) + continue; /* Not a regular file */ + + /* Add a partition with R/W stats */ + add_object_part(basename, dpart->d_name, DISKSTAT_RD); + add_object_part(basename, dpart->d_name, DISKSTAT_WR); + } + } + + if (displayhelp) { + list_for_each_entry(struct diskstat_info, dsi, &gdiskstat_list, list) { + char line[32]; + snprintf(line, sizeof(line), " diskstat-%s-%s", + dsi->mode == DISKSTAT_RD ? "rd" : + dsi->mode == DISKSTAT_WR ? "wr" : "undefined", dsi->name); + + puts(line); + } + } + + return gdiskstat_count; +} + +#endif /* HAVE_GALLIUM_EXTRA_HUD */ |