Path: blob/21.2-virgl/src/gallium/auxiliary/hud/hud_nic.c
4565 views
/**************************************************************************1*2* Copyright (C) 2016 Steven Toth <[email protected]>3* Copyright (C) 2016 Zodiac Inflight Innovations4* All Rights Reserved.5*6* Permission is hereby granted, free of charge, to any person obtaining a7* copy of this software and associated documentation files (the8* "Software"), to deal in the Software without restriction, including9* without limitation the rights to use, copy, modify, merge, publish,10* distribute, sub license, and/or sell copies of the Software, and to11* permit persons to whom the Software is furnished to do so, subject to12* the following conditions:13*14* The above copyright notice and this permission notice (including the15* next paragraph) shall be included in all copies or substantial portions16* of the Software.17*18* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS19* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF20* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.21* IN NO EVENT SHALL THE AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR22* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,23* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE24* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.25*26**************************************************************************/2728#ifdef HAVE_GALLIUM_EXTRA_HUD2930/* Purpose: Reading network interface RX/TX throughput per second,31* displaying on the HUD.32*/3334#include "hud/hud_private.h"35#include "util/list.h"36#include "util/os_time.h"37#include "os/os_thread.h"38#include "util/u_memory.h"39#include "util/u_string.h"40#include <stdio.h>41#include <unistd.h>42#include <dirent.h>43#include <stdlib.h>44#include <unistd.h>45#include <inttypes.h>46#include <sys/types.h>47#include <sys/stat.h>48#include <sys/socket.h>49#include <sys/ioctl.h>50#include <linux/wireless.h>5152struct nic_info53{54struct list_head list;55int mode;56char name[64];57uint64_t speedMbps;58int is_wireless;5960char throughput_filename[128];61uint64_t last_time;62uint64_t last_nic_bytes;63};6465/* TODO: We don't handle dynamic NIC arrival or removal.66* Static globals specific to this HUD category.67*/68static int gnic_count = 0;69static struct list_head gnic_list;70static mtx_t gnic_mutex = _MTX_INITIALIZER_NP;7172static struct nic_info *73find_nic_by_name(const char *n, int mode)74{75list_for_each_entry(struct nic_info, nic, &gnic_list, list) {76if (nic->mode != mode)77continue;7879if (strcasecmp(nic->name, n) == 0)80return nic;81}82return 0;83}8485static int86get_file_value(const char *fname, uint64_t *value)87{88FILE *fh = fopen(fname, "r");89if (!fh)90return -1;91if (fscanf(fh, "%" PRIu64 "", value) != 0) {92/* Error */93}94fclose(fh);95return 0;96}9798static boolean99get_nic_bytes(const char *fn, uint64_t *bytes)100{101if (get_file_value(fn, bytes) < 0)102return FALSE;103104return TRUE;105}106107static void108query_wifi_bitrate(const struct nic_info *nic, uint64_t *bitrate)109{110int sockfd;111struct iw_statistics stats;112struct iwreq req;113114memset(&stats, 0, sizeof(stats));115memset(&req, 0, sizeof(req));116117snprintf(req.ifr_name, sizeof(req.ifr_name), "%s", nic->name);118req.u.data.pointer = &stats;119req.u.data.flags = 1;120req.u.data.length = sizeof(struct iw_statistics);121122/* Any old socket will do, and a datagram socket is pretty cheap */123if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {124fprintf(stderr, "Unable to create socket for %s\n", nic->name);125return;126}127128if (ioctl(sockfd, SIOCGIWRATE, &req) == -1) {129fprintf(stderr, "Error performing SIOCGIWSTATS on %s\n", nic->name);130close(sockfd);131return;132}133*bitrate = req.u.bitrate.value;134135close(sockfd);136}137138static void139query_nic_rssi(const struct nic_info *nic, uint64_t *leveldBm)140{141int sockfd;142struct iw_statistics stats;143struct iwreq req;144145memset(&stats, 0, sizeof(stats));146memset(&req, 0, sizeof(req));147148snprintf(req.ifr_name, sizeof(req.ifr_name), "%s", nic->name);149req.u.data.pointer = &stats;150req.u.data.flags = 1;151req.u.data.length = sizeof(struct iw_statistics);152153if (nic->mode != NIC_RSSI_DBM)154return;155156/* Any old socket will do, and a datagram socket is pretty cheap */157if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {158fprintf(stderr, "Unable to create socket for %s\n", nic->name);159return;160}161162/* Perform the ioctl */163if (ioctl(sockfd, SIOCGIWSTATS, &req) == -1) {164fprintf(stderr, "Error performing SIOCGIWSTATS on %s\n", nic->name);165close(sockfd);166return;167}168*leveldBm = ((char) stats.qual.level * -1);169170close(sockfd);171}172173static void174query_nic_load(struct hud_graph *gr, struct pipe_context *pipe)175{176/* The framework calls us at a regular but indefined period,177* not once per second, compensate the statistics accordingly.178*/179180struct nic_info *nic = gr->query_data;181uint64_t now = os_time_get();182183if (nic->last_time) {184if (nic->last_time + gr->pane->period <= now) {185switch (nic->mode) {186case NIC_DIRECTION_RX:187case NIC_DIRECTION_TX:188{189uint64_t bytes;190get_nic_bytes(nic->throughput_filename, &bytes);191uint64_t nic_mbps =192((bytes - nic->last_nic_bytes) / 1000000) * 8;193194float speedMbps = nic->speedMbps;195float periodMs = gr->pane->period / 1000.0;196float bits = nic_mbps;197float period_factor = periodMs / 1000;198float period_speed = speedMbps * period_factor;199float pct = (bits / period_speed) * 100;200201/* Scaling bps with a narrow time period into a second,202* potentially suffers from rounding errors at higher203* periods. Eg 104%. Compensate.204*/205if (pct > 100)206pct = 100;207hud_graph_add_value(gr, (uint64_t) pct);208209nic->last_nic_bytes = bytes;210}211break;212case NIC_RSSI_DBM:213{214uint64_t leveldBm = 0;215query_nic_rssi(nic, &leveldBm);216hud_graph_add_value(gr, leveldBm);217}218break;219}220221nic->last_time = now;222}223}224else {225/* initialize */226switch (nic->mode) {227case NIC_DIRECTION_RX:228case NIC_DIRECTION_TX:229get_nic_bytes(nic->throughput_filename, &nic->last_nic_bytes);230break;231case NIC_RSSI_DBM:232break;233}234235nic->last_time = now;236}237}238239/**240* Create and initialize a new object for a specific network interface dev.241* \param pane parent context.242* \param nic_name logical block device name, EG. eth0.243* \param mode query type (NIC_DIRECTION_RX/WR/RSSI) statistics.244*/245void246hud_nic_graph_install(struct hud_pane *pane, const char *nic_name,247unsigned int mode)248{249struct hud_graph *gr;250struct nic_info *nic;251252int num_nics = hud_get_num_nics(0);253if (num_nics <= 0)254return;255256nic = find_nic_by_name(nic_name, mode);257if (!nic)258return;259260gr = CALLOC_STRUCT(hud_graph);261if (!gr)262return;263264nic->mode = mode;265if (nic->mode == NIC_DIRECTION_RX) {266snprintf(gr->name, sizeof(gr->name), "%s-rx-%"PRId64"Mbps", nic->name,267nic->speedMbps);268}269else if (nic->mode == NIC_DIRECTION_TX) {270snprintf(gr->name, sizeof(gr->name), "%s-tx-%"PRId64"Mbps", nic->name,271nic->speedMbps);272}273else if (nic->mode == NIC_RSSI_DBM)274snprintf(gr->name, sizeof(gr->name), "%s-rssi", nic->name);275else {276free(gr);277return;278}279280gr->query_data = nic;281gr->query_new_value = query_nic_load;282283hud_pane_add_graph(pane, gr);284hud_pane_set_max_value(pane, 100);285}286287static int288is_wireless_nic(const char *dirbase)289{290struct stat stat_buf;291292/* Check if its a wireless card */293char fn[256];294snprintf(fn, sizeof(fn), "%s/wireless", dirbase);295if (stat(fn, &stat_buf) == 0)296return 1;297298return 0;299}300301static void302query_nic_bitrate(struct nic_info *nic, const char *dirbase)303{304struct stat stat_buf;305306/* Check if its a wireless card */307char fn[256];308snprintf(fn, sizeof(fn), "%s/wireless", dirbase);309if (stat(fn, &stat_buf) == 0) {310/* we're a wireless nic */311query_wifi_bitrate(nic, &nic->speedMbps);312nic->speedMbps /= 1000000;313}314else {315/* Must be a wired nic */316snprintf(fn, sizeof(fn), "%s/speed", dirbase);317get_file_value(fn, &nic->speedMbps);318}319}320321/**322* Initialize internal object arrays and display NIC HUD help.323* \param displayhelp true if the list of detected devices should be324displayed on the console.325* \return number of detected network interface devices.326*/327int328hud_get_num_nics(bool displayhelp)329{330struct dirent *dp;331struct stat stat_buf;332struct nic_info *nic;333char name[64];334335/* Return the number if network interfaces. */336mtx_lock(&gnic_mutex);337if (gnic_count) {338mtx_unlock(&gnic_mutex);339return gnic_count;340}341342/* Scan /sys/block, for every object type we support, create and343* persist an object to represent its different statistics.344*/345list_inithead(&gnic_list);346DIR *dir = opendir("/sys/class/net/");347if (!dir) {348mtx_unlock(&gnic_mutex);349return 0;350}351352while ((dp = readdir(dir)) != NULL) {353354/* Avoid 'lo' and '..' and '.' */355if (strlen(dp->d_name) <= 2)356continue;357358char basename[256];359snprintf(basename, sizeof(basename), "/sys/class/net/%s", dp->d_name);360snprintf(name, sizeof(name), "%s/statistics/rx_bytes", basename);361if (stat(name, &stat_buf) < 0)362continue;363364if (!S_ISREG(stat_buf.st_mode))365continue; /* Not a regular file */366367int is_wireless = is_wireless_nic(basename);368369/* Add the RX object */370nic = CALLOC_STRUCT(nic_info);371strcpy(nic->name, dp->d_name);372snprintf(nic->throughput_filename, sizeof(nic->throughput_filename),373"%s/statistics/rx_bytes", basename);374nic->mode = NIC_DIRECTION_RX;375nic->is_wireless = is_wireless;376query_nic_bitrate(nic, basename);377378list_addtail(&nic->list, &gnic_list);379gnic_count++;380381/* Add the TX object */382nic = CALLOC_STRUCT(nic_info);383strcpy(nic->name, dp->d_name);384snprintf(nic->throughput_filename,385sizeof(nic->throughput_filename),386"/sys/class/net/%s/statistics/tx_bytes", dp->d_name);387nic->mode = NIC_DIRECTION_TX;388nic->is_wireless = is_wireless;389390query_nic_bitrate(nic, basename);391392list_addtail(&nic->list, &gnic_list);393gnic_count++;394395if (nic->is_wireless) {396/* RSSI Support */397nic = CALLOC_STRUCT(nic_info);398strcpy(nic->name, dp->d_name);399snprintf(nic->throughput_filename,400sizeof(nic->throughput_filename),401"/sys/class/net/%s/statistics/tx_bytes", dp->d_name);402nic->mode = NIC_RSSI_DBM;403404query_nic_bitrate(nic, basename);405406list_addtail(&nic->list, &gnic_list);407gnic_count++;408}409410}411closedir(dir);412413list_for_each_entry(struct nic_info, nic, &gnic_list, list) {414char line[64];415snprintf(line, sizeof(line), " nic-%s-%s",416nic->mode == NIC_DIRECTION_RX ? "rx" :417nic->mode == NIC_DIRECTION_TX ? "tx" :418nic->mode == NIC_RSSI_DBM ? "rssi" : "undefined", nic->name);419420puts(line);421422}423424mtx_unlock(&gnic_mutex);425return gnic_count;426}427428#endif /* HAVE_GALLIUM_EXTRA_HUD */429430431