[Mesa-dev] [PATCH] HUD: Add support for block I/O, network I/O and lmsensor stats

Karol Herbst karolherbst at gmail.com
Mon Sep 12 19:22:31 UTC 2016


Hey,

nice work regarding the lmsensor bits. But I think it makes sense to
also wire the power things in, cause we actually expose them within
nouveau. Others might want or actually do the same as well.

Many thanks

2016-09-12 20:33 GMT+02:00 Steven Toth <stoth at kernellabs.com>:
> 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
> so the change is opt out by default.
>
> Signed-off-by: Steven Toth <stoth at kernellabs.com>
> ---
>  configure.ac                                 |  20 ++
>  src/gallium/auxiliary/Makefile.am            |   4 +
>  src/gallium/auxiliary/Makefile.sources       |   3 +
>  src/gallium/auxiliary/hud/hud_context.c      |  67 ++++
>  src/gallium/auxiliary/hud/hud_diskstat.c     | 331 ++++++++++++++++++++
>  src/gallium/auxiliary/hud/hud_nic.c          | 437 +++++++++++++++++++++++++++
>  src/gallium/auxiliary/hud/hud_private.h      |  23 ++
>  src/gallium/auxiliary/hud/hud_sensors_temp.c | 374 +++++++++++++++++++++++
>  src/gallium/include/pipe/p_defines.h         |   4 +
>  9 files changed, 1263 insertions(+)
>  create mode 100644 src/gallium/auxiliary/hud/hud_diskstat.c
>  create mode 100644 src/gallium/auxiliary/hud/hud_nic.c
>  create mode 100644 src/gallium/auxiliary/hud/hud_sensors_temp.c
>
> diff --git a/configure.ac b/configure.ac
> index a413a3a..ef4671a 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -91,6 +91,7 @@ XCBGLX_REQUIRED=1.8.1
>  XSHMFENCE_REQUIRED=1.1
>  XVMC_REQUIRED=1.0.6
>  PYTHON_MAKO_REQUIRED=0.8.0
> +LIBSENSORS_REQUIRED=4.0.0
>
>  dnl Check for progs
>  AC_PROG_CPP
> @@ -871,6 +872,17 @@ AC_ARG_ENABLE([dri],
>      [enable_dri="$enableval"],
>      [enable_dri=yes])
>
> +#PKG_CHECK_MODULES([LIBSENSORS], [libsensors >= $LIBSENSORS_REQUIRED], [enable_lmsensors=yes], [enable_lmsensors=no])
> +AC_ARG_ENABLE([lmsensors],
> +    [AS_HELP_STRING([--enable-lmsensors],
> +        [enable HUD lmsensor support @<:@default=disabled@:>@])],
> +    [enable_lmsensors="$enableval"],
> +    [enable_lmsensors=no])
> +AM_CONDITIONAL(HAVE_LMSENSORS, test "x$enable_lmsensors" = xyes)
> +if test "x$enable_lmsensors" = xyes ; then
> +    DEFINES="${DEFINES} -DHAVE_LMSENSORS=1"
> +fi
> +
>  case "$host_os" in
>  linux*)
>      dri3_default=yes
> @@ -1122,6 +1134,7 @@ AM_CONDITIONAL(HAVE_DRISW_KMS, test "x$have_drisw_kms" = xyes )
>  AM_CONDITIONAL(HAVE_DRI2, test "x$enable_dri" = xyes -a "x$dri_platform" = xdrm -a "x$have_libdrm" = xyes )
>  AM_CONDITIONAL(HAVE_DRI3, test "x$enable_dri3" = xyes -a "x$dri_platform" = xdrm -a "x$have_libdrm" = xyes )
>  AM_CONDITIONAL(HAVE_APPLEDRI, test "x$enable_dri" = xyes -a "x$dri_platform" = xapple )
> +AM_CONDITIONAL(HAVE_LMSENSORS, test "x$enable_lmsensors" = xyes )
>
>  AC_ARG_ENABLE([shared-glapi],
>      [AS_HELP_STRING([--enable-shared-glapi],
> @@ -2876,6 +2889,13 @@ else
>      echo "        Gallium:         no"
>  fi
>
> +echo ""
> +if test "x$enable_lmsensors" != xyes; then
> +    echo "        lmsensors:       no"
> +else
> +    echo "        lmsensors:       yes"
> +fi
> +
>  dnl Shader cache
>  echo ""
>  echo "        Shader cache:    $enable_shader_cache"
> diff --git a/src/gallium/auxiliary/Makefile.am b/src/gallium/auxiliary/Makefile.am
> index d971a2b..4e77c9d 100644
> --- a/src/gallium/auxiliary/Makefile.am
> +++ b/src/gallium/auxiliary/Makefile.am
> @@ -34,6 +34,10 @@ libgallium_la_SOURCES += \
>
>  endif
>
> +if HAVE_LMSENSORS
> +libgallium_la_LDFLAGS = -lsensors
> +endif
> +
>  MKDIR_GEN = $(AM_V_at)$(MKDIR_P) $(@D)
>  PYTHON_GEN =  $(AM_V_GEN)$(PYTHON2) $(PYTHON_FLAGS)
>
> diff --git a/src/gallium/auxiliary/Makefile.sources b/src/gallium/auxiliary/Makefile.sources
> index f8954c9..650a403 100644
> --- a/src/gallium/auxiliary/Makefile.sources
> +++ b/src/gallium/auxiliary/Makefile.sources
> @@ -62,6 +62,9 @@ C_SOURCES := \
>         hud/hud_context.c \
>         hud/hud_context.h \
>         hud/hud_cpu.c \
> +       hud/hud_nic.c \
> +       hud/hud_diskstat.c \
> +       hud/hud_sensors_temp.c \
>         hud/hud_driver_query.c \
>         hud/hud_fps.c \
>         hud/hud_private.h \
> diff --git a/src/gallium/auxiliary/hud/hud_context.c b/src/gallium/auxiliary/hud/hud_context.c
> index f1a1cee..a90103b 100644
> --- a/src/gallium/auxiliary/hud/hud_context.c
> +++ b/src/gallium/auxiliary/hud/hud_context.c
> @@ -257,6 +257,10 @@ number_to_human_readable(uint64_t num, uint64_t max_value,
>     static const char *hz_units[] =
>        {" Hz", " KHz", " MHz", " GHz"};
>     static const char *percent_units[] = {"%"};
> +   static const char *dbm_units[] = {" (-dBm)"};
> +   static const char *temperature_units[] = {" C"};
> +   static const char *volt_units[] = {" V"};
> +   static const char *amp_units[] = {" mA", " A"};
>
>     const char **units;
>     unsigned max_unit;
> @@ -269,6 +273,22 @@ number_to_human_readable(uint64_t num, uint64_t max_value,
>        max_unit = ARRAY_SIZE(time_units)-1;
>        units = time_units;
>        break;
> +   case PIPE_DRIVER_QUERY_TYPE_VOLTS:
> +      max_unit = ARRAY_SIZE(volt_units)-1;
> +      units = volt_units;
> +      break;
> +   case PIPE_DRIVER_QUERY_TYPE_AMPS:
> +      max_unit = ARRAY_SIZE(amp_units)-1;
> +      units = amp_units;
> +      break;
> +   case PIPE_DRIVER_QUERY_TYPE_DBM:
> +      max_unit = ARRAY_SIZE(dbm_units)-1;
> +      units = dbm_units;
> +      break;
> +   case PIPE_DRIVER_QUERY_TYPE_TEMPERATURE:
> +      max_unit = ARRAY_SIZE(temperature_units)-1;
> +      units = temperature_units;
> +      break;
>     case PIPE_DRIVER_QUERY_TYPE_PERCENTAGE:
>        max_unit = ARRAY_SIZE(percent_units)-1;
>        units = percent_units;
> @@ -993,6 +1013,7 @@ hud_parse_env_var(struct hud_context *hud, const char *env)
>        }
>
>        /* Add a graph. */
> +      char arg_name[64];
>        /* IF YOU CHANGE THIS, UPDATE print_help! */
>        if (strcmp(name, "fps") == 0) {
>           hud_fps_graph_install(pane);
> @@ -1003,6 +1024,46 @@ hud_parse_env_var(struct hud_context *hud, const char *env)
>        else if (sscanf(name, "cpu%u%s", &i, s) == 1) {
>           hud_cpu_graph_install(pane, i);
>        }
> +      else if (sscanf(name, "nic-rx-%s", arg_name) == 1) {
> +         hud_nic_graph_install(pane, arg_name, NIC_DIRECTION_RX);
> +      }
> +      else if (sscanf(name, "nic-tx-%s", arg_name) == 1) {
> +         hud_nic_graph_install(pane, arg_name, NIC_DIRECTION_TX);
> +      }
> +      else if (sscanf(name, "nic-rssi-%s", arg_name) == 1) {
> +         hud_nic_graph_install(pane, arg_name, NIC_RSSI_DBM);
> +         pane->type = PIPE_DRIVER_QUERY_TYPE_DBM;
> +      }
> +      else if (sscanf(name, "diskstat-rd-%s", arg_name) == 1) {
> +         hud_diskstat_graph_install(pane, arg_name, DISKSTAT_RD);
> +         pane->type = PIPE_DRIVER_QUERY_TYPE_BYTES;
> +      }
> +      else if (sscanf(name, "diskstat-wr-%s", arg_name) == 1) {
> +         hud_diskstat_graph_install(pane, arg_name, DISKSTAT_WR);
> +         pane->type = PIPE_DRIVER_QUERY_TYPE_BYTES;
> +      }
> +#if HAVE_LMSENSORS
> +      else if (sscanf(name, "sensors_temp_cu-%s", arg_name) == 1) {
> +         hud_sensors_temp_graph_install(pane, &name[16],
> +                                        SENSORS_TEMP_CURRENT);
> +         pane->type = PIPE_DRIVER_QUERY_TYPE_TEMPERATURE;
> +      }
> +      else if (sscanf(name, "sensors_temp_cr-%s", arg_name) == 1) {
> +         hud_sensors_temp_graph_install(pane, &name[16],
> +                                        SENSORS_TEMP_CRITICAL);
> +         pane->type = PIPE_DRIVER_QUERY_TYPE_TEMPERATURE;
> +      }
> +      else if (sscanf(name, "sensors_volt_cu-%s", arg_name) == 1) {
> +         hud_sensors_temp_graph_install(pane, &name[16],
> +                                        SENSORS_VOLTAGE_CURRENT);
> +         pane->type = PIPE_DRIVER_QUERY_TYPE_VOLTS;
> +      }
> +      else if (sscanf(name, "sensors_curr_cu-%s", arg_name) == 1) {
> +         hud_sensors_temp_graph_install(pane, &name[16],
> +                                        SENSORS_CURRENT_CURRENT);
> +         pane->type = PIPE_DRIVER_QUERY_TYPE_AMPS;
> +      }
> +#endif
>        else if (strcmp(name, "samples-passed") == 0 &&
>                 has_occlusion_query(hud->pipe->screen)) {
>           hud_pipe_query_install(&hud->batch_query, pane, hud->pipe,
> @@ -1212,6 +1273,12 @@ print_help(struct pipe_screen *screen)
>        puts("    cs-invocations");
>     }
>
> +   hud_get_num_disks(1);
> +   hud_get_num_nics(1);
> +#if HAVE_LMSENSORS
> +   hud_get_num_sensors(1);
> +#endif
> +
>     if (screen->get_driver_query_info){
>        boolean skipping = false;
>        struct pipe_driver_query_info info;
> diff --git a/src/gallium/auxiliary/hud/hud_diskstat.c b/src/gallium/auxiliary/hud/hud_diskstat.c
> new file mode 100644
> index 0000000..aee4964
> --- /dev/null
> +++ b/src/gallium/auxiliary/hud/hud_diskstat.c
> @@ -0,0 +1,331 @@
> +/**************************************************************************
> + *
> + * Copyright (C) 2016 Steven Toth <stoth at kernellabs.com>
> + * 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.
> + *
> + **************************************************************************/
> +
> +/* 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(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(struct diskstat_info *dsi, struct stat_s *s)
> +{
> +   int ret = 0;
> +   FILE *fh = fopen(dsi->sysfs_filename, "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, &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, &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, 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) {
> +      sprintf(gr->name, "%s-Read-MB/s", dsi->name);
> +   }
> +   else if (dsi->mode == DISKSTAT_WR) {
> +      sprintf(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(char *basename, char *name, int objmode)
> +{
> +   struct diskstat_info *dsi = CALLOC_STRUCT(diskstat_info);
> +
> +   strcpy(dsi->name, name);
> +   sprintf(dsi->sysfs_filename, "%s/%s/stat", basename, name);
> +   dsi->mode = objmode;
> +   list_addtail(&dsi->list, &gdiskstat_list);
> +   gdiskstat_count++;
> +}
> +
> +static void
> +add_object(char *basename, char *name, int objmode)
> +{
> +   struct diskstat_info *dsi = CALLOC_STRUCT(diskstat_info);
> +
> +   strcpy(dsi->name, name);
> +   sprintf(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(int 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];
> +      sprintf(basename, "/sys/block/%s", dp->d_name);
> +      sprintf(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];
> +         sprintf(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];
> +         sprintf(line, "    diskstat-%s-%s",
> +                 dsi->mode == DISKSTAT_RD ? "rd" :
> +                 dsi->mode == DISKSTAT_WR ? "wr" : "undefined", dsi->name);
> +
> +         puts(line);
> +      }
> +   }
> +
> +   return gdiskstat_count;
> +}
> diff --git a/src/gallium/auxiliary/hud/hud_nic.c b/src/gallium/auxiliary/hud/hud_nic.c
> new file mode 100644
> index 0000000..d31f797
> --- /dev/null
> +++ b/src/gallium/auxiliary/hud/hud_nic.c
> @@ -0,0 +1,437 @@
> +/**************************************************************************
> + *
> + * Copyright (C) 2016 Steven Toth <stoth at kernellabs.com>
> + * 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.
> + *
> + **************************************************************************/
> +
> +/* Purpose: Reading network interface RX/TX 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 <sys/socket.h>
> +#include <sys/ioctl.h>
> +#include <linux/wireless.h>
> +
> +#define LOCAL_DEBUG 0
> +
> +struct nic_info
> +{
> +   struct list_head list;
> +   int mode;
> +   char name[64];
> +   uint64_t speedMbps;
> +   int is_wireless;
> +
> +   char throughput_filename[128];
> +   uint64_t last_time;
> +   uint64_t last_nic_bytes;
> +};
> +
> +/* TODO: We don't handle dynamic NIC arrival or removal.
> + * Static globals specific to this HUD category.
> + */
> +static int gnic_count = 0;
> +static struct list_head gnic_list;
> +
> +static struct nic_info *
> +find_nic_by_name(char *n, int mode)
> +{
> +   list_for_each_entry(struct nic_info, nic, &gnic_list, list) {
> +      if (nic->mode != mode)
> +         continue;
> +
> +      if (strcasecmp(nic->name, n) == 0)
> +         return nic;
> +   }
> +   return 0;
> +}
> +
> +static int
> +get_file_value(char *fname, uint64_t * value)
> +{
> +   FILE *fh = fopen(fname, "r");
> +   if (!fh)
> +      return -1;
> +   if (fscanf(fh, "%" PRIu64 "", value) != 0) {
> +      /* Error */
> +   }
> +   fclose(fh);
> +   return 0;
> +}
> +
> +static boolean
> +get_nic_bytes(struct nic_info *nic, uint64_t * bytes)
> +{
> +   if (get_file_value(nic->throughput_filename, bytes) < 0)
> +      return FALSE;
> +
> +   return TRUE;
> +}
> +
> +static void
> +query_wifi_bitrate(struct nic_info *nic, uint64_t * bitrate)
> +{
> +   int sockfd;
> +   struct iw_statistics stats;
> +   struct iwreq req;
> +
> +   memset(&stats, 0, sizeof(stats));
> +   memset(&req, 0, sizeof(req));
> +
> +   strcpy(req.ifr_name, nic->name);
> +   req.u.data.pointer = &stats;
> +   req.u.data.flags = 1;
> +   req.u.data.length = sizeof(struct iw_statistics);
> +
> +   /* Any old socket will do, and a datagram socket is pretty cheap */
> +   if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
> +      fprintf(stderr, "Unable to create socket for %s\n", nic->name);
> +      return;
> +   }
> +
> +   if (ioctl(sockfd, SIOCGIWRATE, &req) == -1) {
> +      fprintf(stderr, "Error performing SIOCGIWSTATS on %s\n", nic->name);
> +      close(sockfd);
> +      return;
> +   }
> +   *bitrate = req.u.bitrate.value;
> +
> +   close(sockfd);
> +}
> +
> +static void
> +query_nic_rssi(struct nic_info *nic, uint64_t * leveldBm)
> +{
> +   int sockfd;
> +   struct iw_statistics stats;
> +   struct iwreq req;
> +
> +   memset(&stats, 0, sizeof(stats));
> +   memset(&req, 0, sizeof(req));
> +
> +   strcpy(req.ifr_name, nic->name);
> +   req.u.data.pointer = &stats;
> +   req.u.data.flags = 1;
> +   req.u.data.length = sizeof(struct iw_statistics);
> +
> +   if (nic->mode != NIC_RSSI_DBM)
> +      return;
> +
> +   /* Any old socket will do, and a datagram socket is pretty cheap */
> +   if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
> +      fprintf(stderr, "Unable to create socket for %s\n", nic->name);
> +      return;
> +   }
> +
> +   /* Perform the ioctl */
> +   if (ioctl(sockfd, SIOCGIWSTATS, &req) == -1) {
> +      fprintf(stderr, "Error performing SIOCGIWSTATS on %s\n", nic->name);
> +      close(sockfd);
> +      return;
> +   }
> +   *leveldBm = ((char) stats.qual.level * -1);
> +
> +   close(sockfd);
> +
> +#if LOCAL_DEBUG
> +   printf("NIC signal level%s is %d%s.\n",
> +          (stats.qual.updated & IW_QUAL_DBM ? " (in dBm)" : ""),
> +          (char) stats.qual.level,
> +          (stats.qual.updated & IW_QUAL_LEVEL_UPDATED ? " (updated)" : ""));
> +#endif
> +}
> +
> +static void
> +query_nic_load(struct hud_graph *gr)
> +{
> +   /* The framework calls us at a regular but indefined period,
> +    * not once per second, compensate the statistics accordingly.
> +    */
> +
> +   struct nic_info *nic = gr->query_data;
> +   uint64_t now = os_time_get();
> +
> +   if (nic->last_time) {
> +      if (nic->last_time + gr->pane->period <= now) {
> +         switch (nic->mode) {
> +         case NIC_DIRECTION_RX:
> +         case NIC_DIRECTION_TX:
> +            {
> +               uint64_t bytes;
> +               get_nic_bytes(nic, &bytes);
> +               uint64_t nic_mbps =
> +                  ((bytes - nic->last_nic_bytes) / 1000000) * 8;
> +
> +               float speedMbps = nic->speedMbps;
> +               float periodMs = gr->pane->period / 1000;
> +               float bits = nic_mbps;
> +               float period_factor = periodMs / 1000;
> +               float period_speed = speedMbps * period_factor;
> +               float pct = (bits / period_speed) * 100;
> +
> +               /* Scaling bps with a narrow time period into a second,
> +                * potentially suffers from routing errors at higher
> +                * periods. Eg 104%. Compensate.
> +                */
> +               if (pct > 100)
> +                  pct = 100;
> +               hud_graph_add_value(gr, (uint64_t) pct);
> +
> +               nic->last_nic_bytes = bytes;
> +            }
> +            break;
> +         case NIC_RSSI_DBM:
> +            {
> +               uint64_t leveldBm = 0;
> +               query_nic_rssi(nic, &leveldBm);
> +               hud_graph_add_value(gr, leveldBm);
> +            }
> +            break;
> +         }
> +
> +         nic->last_time = now;
> +      }
> +   }
> +   else {
> +      /* initialize */
> +      switch (nic->mode) {
> +      case NIC_DIRECTION_RX:
> +      case NIC_DIRECTION_TX:
> +         get_nic_bytes(nic, &nic->last_nic_bytes);
> +         break;
> +      case NIC_RSSI_DBM:
> +         break;
> +      }
> +
> +      nic->last_time = now;
> +   }
> +}
> +
> +static void
> +free_query_data(void *p)
> +{
> +   struct nic_info *nic = (struct nic_info *) p;
> +   list_del(&nic->list);
> +   FREE(nic);
> +}
> +
> +/**
> +  * Create and initialize a new object for a specific network interface dev.
> +  * \param  pane  parent context.
> +  * \param  nic_name  logical block device name, EG. eth0.
> +  * \param  mode  query type (NIC_DIRECTION_RX/WR/RSSI) statistics.
> +  */
> +void
> +hud_nic_graph_install(struct hud_pane *pane, char *nic_name,
> +                      unsigned int mode)
> +{
> +   struct hud_graph *gr;
> +   struct nic_info *nic;
> +
> +   int num_nics = hud_get_num_nics(0);
> +   if (num_nics <= 0)
> +      return;
> +
> +#if LOCAL_DEBUG
> +   printf("%s(%s, %s) - Creating HUD object\n", __func__, nic_name,
> +          mode == NIC_DIRECTION_RX ? "RX" :
> +          mode == NIC_DIRECTION_TX ? "TX" :
> +          mode == NIC_RSSI_DBM ? "RSSI" : "UNDEFINED");
> +#endif
> +
> +   nic = find_nic_by_name(nic_name, mode);
> +   if (!nic)
> +      return;
> +
> +   gr = CALLOC_STRUCT(hud_graph);
> +   if (!gr)
> +      return;
> +
> +   nic->mode = mode;
> +   if (nic->mode == NIC_DIRECTION_RX) {
> +      sprintf(gr->name, "%s-rx-%lldMbps", nic->name, nic->speedMbps);
> +   }
> +   else if (nic->mode == NIC_DIRECTION_TX) {
> +      sprintf(gr->name, "%s-tx-%lldMbps", nic->name, nic->speedMbps);
> +   }
> +   else if (nic->mode == NIC_RSSI_DBM)
> +      sprintf(gr->name, "%s-rssi", nic->name);
> +   else
> +      return;
> +
> +   gr->query_data = nic;
> +   gr->query_new_value = query_nic_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 int
> +is_wireless_nic(char *dirbase)
> +{
> +   struct stat stat_buf;
> +
> +   /* Check if its a wireless card */
> +   char fn[256];
> +   sprintf(fn, "%s/wireless", dirbase);
> +   if (stat(fn, &stat_buf) == 0)
> +      return 1;
> +
> +   return 0;
> +}
> +
> +static void
> +query_nic_bitrate(struct nic_info *nic, char *dirbase)
> +{
> +   struct stat stat_buf;
> +
> +   /* Check if its a wireless card */
> +   char fn[256];
> +   sprintf(fn, "%s/wireless", dirbase);
> +   if (stat(fn, &stat_buf) == 0) {
> +      /* we're a wireless nic */
> +      query_wifi_bitrate(nic, &nic->speedMbps);
> +      nic->speedMbps /= 1000000;
> +   }
> +   else {
> +      /* Must be a wired nic */
> +      sprintf(fn, "%s/speed", dirbase);
> +      get_file_value(fn, &nic->speedMbps);
> +   }
> +}
> +
> +/**
> +  * Initialize internal object arrays and display NIC HUD help.
> +  * \param  displayhelp  true if the list of detected devices should be
> +                         displayed on the console.
> +  * \return  number of detected network interface devices.
> +  */
> +int
> +hud_get_num_nics(int displayhelp)
> +{
> +   struct dirent *dp;
> +   struct stat stat_buf;
> +   struct nic_info *nic;
> +   char name[64];
> +
> +   /* Return the number if network interfaces. */
> +   if (gnic_count)
> +      return gnic_count;
> +
> +   /* Scan /sys/block, for every object type we support, create and
> +    * persist an object to represent its different statistics.
> +    */
> +   list_inithead(&gnic_list);
> +   DIR *dir = opendir("/sys/class/net/");
> +   if (!dir)
> +      return 0;
> +
> +   while ((dp = readdir(dir)) != NULL) {
> +
> +      /* Avoid 'lo' and '..' and '.' */
> +      if (strlen(dp->d_name) <= 2)
> +         continue;
> +
> +      char basename[256];
> +      sprintf(basename, "/sys/class/net/%s", dp->d_name);
> +      sprintf(name, "%s/statistics/rx_bytes", basename);
> +      if (stat(name, &stat_buf) < 0)
> +         continue;
> +
> +      if (!S_ISREG(stat_buf.st_mode))
> +         continue;              /* Not a regular file */
> +
> +      int is_wireless = is_wireless_nic(basename);
> +
> +      /* Add the RX object */
> +      nic = CALLOC_STRUCT(nic_info);
> +      strcpy(nic->name, dp->d_name);
> +      sprintf(nic->throughput_filename, "%s/statistics/rx_bytes", basename);
> +      nic->mode = NIC_DIRECTION_RX;
> +      nic->is_wireless = is_wireless;
> +      query_nic_bitrate(nic, basename);
> +
> +      list_addtail(&nic->list, &gnic_list);
> +      gnic_count++;
> +
> +      /* Add the TX object */
> +      nic = CALLOC_STRUCT(nic_info);
> +      strcpy(nic->name, dp->d_name);
> +      sprintf(nic->throughput_filename,
> +              "/sys/class/net/%s/statistics/tx_bytes", dp->d_name);
> +      nic->mode = NIC_DIRECTION_TX;
> +      nic->is_wireless = is_wireless;
> +
> +      query_nic_bitrate(nic, basename);
> +
> +      list_addtail(&nic->list, &gnic_list);
> +      gnic_count++;
> +
> +      if (nic->is_wireless) {
> +         /* RSSI Support */
> +         nic = CALLOC_STRUCT(nic_info);
> +         strcpy(nic->name, dp->d_name);
> +         sprintf(nic->throughput_filename,
> +                 "/sys/class/net/%s/statistics/tx_bytes", dp->d_name);
> +         nic->mode = NIC_RSSI_DBM;
> +
> +         query_nic_bitrate(nic, basename);
> +
> +         list_addtail(&nic->list, &gnic_list);
> +         gnic_count++;
> +      }
> +
> +   }
> +
> +   list_for_each_entry(struct nic_info, nic, &gnic_list, list) {
> +      char line[64];
> +      sprintf(line, "    nic-%s-%s",
> +              nic->mode == NIC_DIRECTION_RX ? "rx" :
> +              nic->mode == NIC_DIRECTION_TX ? "tx" :
> +              nic->mode == NIC_RSSI_DBM ? "rssi" : "undefined", nic->name);
> +
> +      puts(line);
> +
> +   }
> +
> +   return gnic_count;
> +}
> diff --git a/src/gallium/auxiliary/hud/hud_private.h b/src/gallium/auxiliary/hud/hud_private.h
> index 2104c27..5e9da0c 100644
> --- a/src/gallium/auxiliary/hud/hud_private.h
> +++ b/src/gallium/auxiliary/hud/hud_private.h
> @@ -103,4 +103,27 @@ boolean hud_driver_query_install(struct hud_batch_query_context **pbq,
>  void hud_batch_query_update(struct hud_batch_query_context *bq);
>  void hud_batch_query_cleanup(struct hud_batch_query_context **pbq);
>
> +int hud_get_num_nics(int displayhelp);
> +#define NIC_DIRECTION_RX 1
> +#define NIC_DIRECTION_TX 2
> +#define NIC_RSSI_DBM     3
> +void hud_nic_graph_install(struct hud_pane *pane, char *nic_index,
> +                           unsigned int mode);
> +
> +int hud_get_num_disks(int displayhelp);
> +#define DISKSTAT_RD 1
> +#define DISKSTAT_WR 2
> +void hud_diskstat_graph_install(struct hud_pane *pane, char *dev_name,
> +                                unsigned int mode);
> +
> +#if HAVE_LMSENSORS
> +int hud_get_num_sensors(int displayhelp);
> +#define SENSORS_TEMP_CURRENT     1
> +#define SENSORS_TEMP_CRITICAL    2
> +#define SENSORS_VOLTAGE_CURRENT  3
> +#define SENSORS_CURRENT_CURRENT  4
> +void hud_sensors_temp_graph_install(struct hud_pane *pane, char *dev_name,
> +                                    unsigned int mode);
> +#endif
> +
>  #endif
> diff --git a/src/gallium/auxiliary/hud/hud_sensors_temp.c b/src/gallium/auxiliary/hud/hud_sensors_temp.c
> new file mode 100644
> index 0000000..1a06079
> --- /dev/null
> +++ b/src/gallium/auxiliary/hud/hud_sensors_temp.c
> @@ -0,0 +1,374 @@
> +/**************************************************************************
> + *
> + * Copyright (C) 2016 Steven Toth <stoth at kernellabs.com>
> + * 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_LMSENSORS
> +/* Purpose: Extract lm-sensors data, expose temperature, power, voltage. */
> +
> +#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>
> +#include <sensors/sensors.h>
> +
> +#define LOCAL_DEBUG 0
> +
> +/* TODO: We don't handle dynamic sensor discovery / arrival or removal.
> + * Static globals specific to this HUD category.
> + */
> +static int gsensors_temp_count = 0;
> +static struct list_head gsensors_temp_list;
> +
> +struct sensors_temp_info
> +{
> +   struct list_head list;
> +
> +   /* Combined chip and feature name, human readable. */
> +   char name[64];
> +
> +   /* The type of measurement, critical or current. */
> +   unsigned int mode;
> +
> +   uint64_t last_time;
> +
> +   char chipname[64];
> +   char featurename[128];
> +
> +   sensors_chip_name *chip;
> +   const sensors_feature *feature;
> +   double current, min, max, critical;
> +};
> +
> +static double
> +get_value(const sensors_chip_name * name, const sensors_subfeature * sub)
> +{
> +   double val;
> +   int err;
> +
> +   err = sensors_get_value(name, sub->number, &val);
> +   if (err) {
> +      fprintf(stderr, "ERROR: Can't get value of subfeature %s\n", sub->name);
> +      val = 0;
> +   }
> +   return val;
> +}
> +
> +static void
> +get_sensor_values(struct sensors_temp_info *sti)
> +{
> +   const sensors_subfeature *sf;
> +
> +   if (sti->mode == SENSORS_VOLTAGE_CURRENT) {
> +      sf = sensors_get_subfeature(sti->chip, sti->feature,
> +                                  SENSORS_SUBFEATURE_IN_INPUT);
> +      if (sf)
> +         sti->current = get_value(sti->chip, sf);
> +   }
> +
> +   if (sti->mode == SENSORS_CURRENT_CURRENT) {
> +      sf = sensors_get_subfeature(sti->chip, sti->feature,
> +                                  SENSORS_SUBFEATURE_CURR_INPUT);
> +      if (sf) {
> +         /* Sensors API returns in AMPs, even though driver is reporting mA,
> +          * convert back to mA */
> +         sti->current = get_value(sti->chip, sf) * 1000;
> +      }
> +   }
> +
> +   if (sti->mode == SENSORS_TEMP_CURRENT) {
> +      sf = sensors_get_subfeature(sti->chip, sti->feature,
> +                                  SENSORS_SUBFEATURE_TEMP_INPUT);
> +      if (sf)
> +         sti->current = get_value(sti->chip, sf);
> +   }
> +
> +   if (sti->mode == SENSORS_TEMP_CRITICAL) {
> +      sf = sensors_get_subfeature(sti->chip, sti->feature,
> +                                  SENSORS_SUBFEATURE_TEMP_CRIT);
> +      if (sf)
> +         sti->critical = get_value(sti->chip, sf);
> +   }
> +
> +   sf = sensors_get_subfeature(sti->chip, sti->feature,
> +                               SENSORS_SUBFEATURE_TEMP_MIN);
> +   if (sf)
> +      sti->min = get_value(sti->chip, sf);
> +
> +   sf = sensors_get_subfeature(sti->chip, sti->feature,
> +                               SENSORS_SUBFEATURE_TEMP_MAX);
> +   if (sf)
> +      sti->max = get_value(sti->chip, sf);
> +#if LOCAL_DEBUG
> +   printf("%s.%s.current = %.1f\n", sti->chipname, sti->featurename,
> +          sti->current);
> +   printf("%s.%s.critical = %.1f\n", sti->chipname, sti->featurename,
> +          sti->critical);
> +#endif
> +}
> +
> +static struct sensors_temp_info *
> +find_sti_by_name(char *n, unsigned int mode)
> +{
> +   list_for_each_entry(struct sensors_temp_info, sti, &gsensors_temp_list, list) {
> +      if (sti->mode != mode)
> +         continue;
> +      if (strcasecmp(sti->name, n) == 0)
> +         return sti;
> +   }
> +   return 0;
> +}
> +
> +static void
> +query_sti_load(struct hud_graph *gr)
> +{
> +   struct sensors_temp_info *sti = gr->query_data;
> +   uint64_t now = os_time_get();
> +
> +   if (sti->last_time) {
> +      if (sti->last_time + gr->pane->period <= now) {
> +         get_sensor_values(sti);
> +
> +         switch (sti->mode) {
> +         case SENSORS_TEMP_CURRENT:
> +            hud_graph_add_value(gr, (uint64_t) sti->current);
> +            break;
> +         case SENSORS_TEMP_CRITICAL:
> +            hud_graph_add_value(gr, (uint64_t) sti->critical);
> +            break;
> +         case SENSORS_VOLTAGE_CURRENT:
> +            hud_graph_add_value(gr, (uint64_t) sti->current);
> +            break;
> +         case SENSORS_CURRENT_CURRENT:
> +            hud_graph_add_value(gr, (uint64_t) sti->current);
> +            break;
> +         }
> +
> +         sti->last_time = now;
> +      }
> +   }
> +   else {
> +      /* initialize */
> +      get_sensor_values(sti);
> +      sti->last_time = now;
> +   }
> +}
> +
> +static void
> +free_query_data(void *p)
> +{
> +   struct sensors_temp_info *sti = (struct sensors_temp_info *) p;
> +   list_del(&sti->list);
> +   if (sti->chip)
> +      sensors_free_chip_name(sti->chip);
> +   FREE(sti);
> +   sensors_cleanup();
> +}
> +
> +/**
> +  * Create and initialize a new object for a specific sensor interface dev.
> +  * \param  pane  parent context.
> +  * \param  dev_name  device name, EG. 'coretemp-isa-0000.Core 1'
> +  * \param  mode  query type (NIC_DIRECTION_RX/WR/RSSI) statistics.
> +  */
> +void
> +hud_sensors_temp_graph_install(struct hud_pane *pane, char *dev_name,
> +                               unsigned int mode)
> +{
> +   struct hud_graph *gr;
> +   struct sensors_temp_info *sti;
> +
> +   int num_devs = hud_get_num_sensors(0);
> +   if (num_devs <= 0)
> +      return;
> +#if LOCAL_DEBUG
> +   printf("%s(%s, %s) - Creating HUD object\n", __func__, dev_name,
> +          mode == SENSORS_VOLTAGE_CURRENT ? "VOLTS" :
> +          mode == SENSORS_CURRENT_CURRENT ? "AMPS" :
> +          mode == SENSORS_TEMP_CURRENT ? "CU" :
> +          mode == SENSORS_TEMP_CRITICAL ? "CR" : "UNDEFINED");
> +#endif
> +
> +   sti = find_sti_by_name(dev_name, mode);
> +   if (!sti)
> +      return;
> +
> +   gr = CALLOC_STRUCT(hud_graph);
> +   if (!gr)
> +      return;
> +
> +   sprintf(gr->name, "%.6s..%s (%s)",
> +           sti->chipname,
> +           sti->featurename,
> +           sti->mode == SENSORS_VOLTAGE_CURRENT ? "Volts" :
> +           sti->mode == SENSORS_CURRENT_CURRENT ? "Amps" :
> +           sti->mode == SENSORS_TEMP_CURRENT ? "Curr" :
> +           sti->mode == SENSORS_TEMP_CRITICAL ? "Crit" : "Unkn");
> +
> +   gr->query_data = sti;
> +   gr->query_new_value = query_sti_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);
> +   switch (sti->mode) {
> +   case SENSORS_TEMP_CURRENT:
> +   case SENSORS_TEMP_CRITICAL:
> +      hud_pane_set_max_value(pane, 120);
> +      break;
> +   case SENSORS_VOLTAGE_CURRENT:
> +      hud_pane_set_max_value(pane, 12);
> +      break;
> +   case SENSORS_CURRENT_CURRENT:
> +      hud_pane_set_max_value(pane, 5000);
> +      break;
> +   }
> +}
> +
> +static void
> +create_object(char *chipname, char *featurename,
> +             const sensors_chip_name * chip, const sensors_feature * feature,
> +             int mode)
> +{
> +#if LOCAL_DEBUG
> +   printf("%03d: %s.%s\n", gsensors_temp_count, chipname, featurename);
> +#endif
> +   struct sensors_temp_info *sti = CALLOC_STRUCT(sensors_temp_info);
> +
> +   sti->mode = mode;
> +   sti->chip = (sensors_chip_name *) chip;
> +   sti->feature = feature;
> +   strcpy(sti->chipname, chipname);
> +   strcpy(sti->featurename, featurename);
> +   sprintf(sti->name, "%s.%s", sti->chipname, sti->featurename);
> +
> +   list_addtail(&sti->list, &gsensors_temp_list);
> +   gsensors_temp_count++;
> +}
> +
> +static void
> +build_sensor_list(void)
> +{
> +   const sensors_chip_name *chip;
> +   const sensors_chip_name *match = 0;
> +   const sensors_feature *feature;
> +   int chip_nr = 0;
> +
> +   char name[256];
> +   while ((chip = sensors_get_detected_chips(match, &chip_nr))) {
> +      sensors_snprintf_chip_name(name, sizeof(name), chip);
> +
> +      /* Get all features and filter accordingly. */
> +      int fnr = 0;
> +      while ((feature = sensors_get_features(chip, &fnr))) {
> +         char *featurename = sensors_get_label(chip, feature);
> +         if (!featurename)
> +            continue;
> +
> +         /* Create a 'current' and 'critical' object pair.
> +          * Ignore sensor if its not temperature based.
> +          */
> +         if (feature->type == SENSORS_FEATURE_TEMP) {
> +            create_object(name, featurename, chip, feature,
> +                          SENSORS_TEMP_CURRENT);
> +            create_object(name, featurename, chip, feature,
> +                          SENSORS_TEMP_CRITICAL);
> +         }
> +         if (feature->type == SENSORS_FEATURE_IN) {
> +            create_object(name, featurename, chip, feature,
> +                          SENSORS_VOLTAGE_CURRENT);
> +         }
> +         if (feature->type == SENSORS_FEATURE_CURR) {
> +            create_object(name, featurename, chip, feature,
> +                          SENSORS_CURRENT_CURRENT);
> +         }
> +         free(featurename);
> +      }
> +   }
> +}
> +
> +/**
> +  * Initialize internal object arrays and display lmsensors HUD help.
> +  * \param  displayhelp  true if the list of detected devices should be
> +                         displayed on the console.
> +  * \return  number of detected lmsensor devices.
> +  */
> +int
> +hud_get_num_sensors(int displayhelp)
> +{
> +   /* Return the number of sensors detected. */
> +   if (gsensors_temp_count)
> +      return gsensors_temp_count;
> +
> +   int ret = sensors_init(NULL);
> +   if (ret)
> +      return 0;
> +
> +   list_inithead(&gsensors_temp_list);
> +
> +   /* Scan /sys/block, for every object type we support, create and
> +    * persist an object to represent its different statistics.
> +    */
> +   build_sensor_list();
> +
> +   if (displayhelp) {
> +      list_for_each_entry(struct sensors_temp_info, sti, &gsensors_temp_list, list) {
> +         char line[64];
> +         switch (sti->mode) {
> +         case SENSORS_TEMP_CURRENT:
> +            sprintf(line, "    sensors_temp_cu-%s", sti->name);
> +            break;
> +         case SENSORS_TEMP_CRITICAL:
> +            sprintf(line, "    sensors_temp_cr-%s", sti->name);
> +            break;
> +         case SENSORS_VOLTAGE_CURRENT:
> +            sprintf(line, "    sensors_volt_cu-%s", sti->name);
> +            break;
> +         case SENSORS_CURRENT_CURRENT:
> +            sprintf(line, "    sensors_curr_cu-%s", sti->name);
> +            break;
> +         }
> +
> +         puts(line);
> +      }
> +   }
> +
> +   return gsensors_temp_count;
> +}
> +
> +#endif /* HAVE_LMSENSORS */
> diff --git a/src/gallium/include/pipe/p_defines.h b/src/gallium/include/pipe/p_defines.h
> index 88aa050..317a7c3 100644
> --- a/src/gallium/include/pipe/p_defines.h
> +++ b/src/gallium/include/pipe/p_defines.h
> @@ -965,6 +965,10 @@ enum pipe_driver_query_type
>     PIPE_DRIVER_QUERY_TYPE_BYTES,
>     PIPE_DRIVER_QUERY_TYPE_MICROSECONDS,
>     PIPE_DRIVER_QUERY_TYPE_HZ,
> +   PIPE_DRIVER_QUERY_TYPE_DBM,
> +   PIPE_DRIVER_QUERY_TYPE_TEMPERATURE,
> +   PIPE_DRIVER_QUERY_TYPE_VOLTS,
> +   PIPE_DRIVER_QUERY_TYPE_AMPS,
>  };
>
>  /* Whether an average value per frame or a cumulative value should be
> --
> 2.7.4
>
> _______________________________________________
> mesa-dev mailing list
> mesa-dev at lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/mesa-dev


More information about the mesa-dev mailing list