101e04c3fSmrg/************************************************************************** 201e04c3fSmrg * 301e04c3fSmrg * Copyright (C) 2016 Steven Toth <stoth@kernellabs.com> 401e04c3fSmrg * Copyright (C) 2016 Zodiac Inflight Innovations 501e04c3fSmrg * All Rights Reserved. 601e04c3fSmrg * 701e04c3fSmrg * Permission is hereby granted, free of charge, to any person obtaining a 801e04c3fSmrg * copy of this software and associated documentation files (the 901e04c3fSmrg * "Software"), to deal in the Software without restriction, including 1001e04c3fSmrg * without limitation the rights to use, copy, modify, merge, publish, 1101e04c3fSmrg * distribute, sub license, and/or sell copies of the Software, and to 1201e04c3fSmrg * permit persons to whom the Software is furnished to do so, subject to 1301e04c3fSmrg * the following conditions: 1401e04c3fSmrg * 1501e04c3fSmrg * The above copyright notice and this permission notice (including the 1601e04c3fSmrg * next paragraph) shall be included in all copies or substantial portions 1701e04c3fSmrg * of the Software. 1801e04c3fSmrg * 1901e04c3fSmrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 2001e04c3fSmrg * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 2101e04c3fSmrg * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. 2201e04c3fSmrg * IN NO EVENT SHALL THE AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR 2301e04c3fSmrg * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 2401e04c3fSmrg * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 2501e04c3fSmrg * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 2601e04c3fSmrg * 2701e04c3fSmrg **************************************************************************/ 2801e04c3fSmrg 2901e04c3fSmrg#ifdef HAVE_GALLIUM_EXTRA_HUD 3001e04c3fSmrg 3101e04c3fSmrg/* Purpose: Reading network interface RX/TX throughput per second, 3201e04c3fSmrg * displaying on the HUD. 3301e04c3fSmrg */ 3401e04c3fSmrg 3501e04c3fSmrg#include "hud/hud_private.h" 3601e04c3fSmrg#include "util/list.h" 3701e04c3fSmrg#include "util/os_time.h" 3801e04c3fSmrg#include "os/os_thread.h" 3901e04c3fSmrg#include "util/u_memory.h" 407ec681f3Smrg#include "util/u_string.h" 4101e04c3fSmrg#include <stdio.h> 4201e04c3fSmrg#include <unistd.h> 4301e04c3fSmrg#include <dirent.h> 4401e04c3fSmrg#include <stdlib.h> 4501e04c3fSmrg#include <unistd.h> 4601e04c3fSmrg#include <inttypes.h> 4701e04c3fSmrg#include <sys/types.h> 4801e04c3fSmrg#include <sys/stat.h> 4901e04c3fSmrg#include <sys/socket.h> 5001e04c3fSmrg#include <sys/ioctl.h> 5101e04c3fSmrg#include <linux/wireless.h> 5201e04c3fSmrg 5301e04c3fSmrgstruct nic_info 5401e04c3fSmrg{ 5501e04c3fSmrg struct list_head list; 5601e04c3fSmrg int mode; 5701e04c3fSmrg char name[64]; 5801e04c3fSmrg uint64_t speedMbps; 5901e04c3fSmrg int is_wireless; 6001e04c3fSmrg 6101e04c3fSmrg char throughput_filename[128]; 6201e04c3fSmrg uint64_t last_time; 6301e04c3fSmrg uint64_t last_nic_bytes; 6401e04c3fSmrg}; 6501e04c3fSmrg 6601e04c3fSmrg/* TODO: We don't handle dynamic NIC arrival or removal. 6701e04c3fSmrg * Static globals specific to this HUD category. 6801e04c3fSmrg */ 6901e04c3fSmrgstatic int gnic_count = 0; 7001e04c3fSmrgstatic struct list_head gnic_list; 7101e04c3fSmrgstatic mtx_t gnic_mutex = _MTX_INITIALIZER_NP; 7201e04c3fSmrg 7301e04c3fSmrgstatic struct nic_info * 7401e04c3fSmrgfind_nic_by_name(const char *n, int mode) 7501e04c3fSmrg{ 7601e04c3fSmrg list_for_each_entry(struct nic_info, nic, &gnic_list, list) { 7701e04c3fSmrg if (nic->mode != mode) 7801e04c3fSmrg continue; 7901e04c3fSmrg 8001e04c3fSmrg if (strcasecmp(nic->name, n) == 0) 8101e04c3fSmrg return nic; 8201e04c3fSmrg } 8301e04c3fSmrg return 0; 8401e04c3fSmrg} 8501e04c3fSmrg 8601e04c3fSmrgstatic int 8701e04c3fSmrgget_file_value(const char *fname, uint64_t *value) 8801e04c3fSmrg{ 8901e04c3fSmrg FILE *fh = fopen(fname, "r"); 9001e04c3fSmrg if (!fh) 9101e04c3fSmrg return -1; 9201e04c3fSmrg if (fscanf(fh, "%" PRIu64 "", value) != 0) { 9301e04c3fSmrg /* Error */ 9401e04c3fSmrg } 9501e04c3fSmrg fclose(fh); 9601e04c3fSmrg return 0; 9701e04c3fSmrg} 9801e04c3fSmrg 9901e04c3fSmrgstatic boolean 10001e04c3fSmrgget_nic_bytes(const char *fn, uint64_t *bytes) 10101e04c3fSmrg{ 10201e04c3fSmrg if (get_file_value(fn, bytes) < 0) 10301e04c3fSmrg return FALSE; 10401e04c3fSmrg 10501e04c3fSmrg return TRUE; 10601e04c3fSmrg} 10701e04c3fSmrg 10801e04c3fSmrgstatic void 10901e04c3fSmrgquery_wifi_bitrate(const struct nic_info *nic, uint64_t *bitrate) 11001e04c3fSmrg{ 11101e04c3fSmrg int sockfd; 11201e04c3fSmrg struct iw_statistics stats; 11301e04c3fSmrg struct iwreq req; 11401e04c3fSmrg 11501e04c3fSmrg memset(&stats, 0, sizeof(stats)); 11601e04c3fSmrg memset(&req, 0, sizeof(req)); 11701e04c3fSmrg 118361fc4cbSmaya snprintf(req.ifr_name, sizeof(req.ifr_name), "%s", nic->name); 11901e04c3fSmrg req.u.data.pointer = &stats; 12001e04c3fSmrg req.u.data.flags = 1; 12101e04c3fSmrg req.u.data.length = sizeof(struct iw_statistics); 12201e04c3fSmrg 12301e04c3fSmrg /* Any old socket will do, and a datagram socket is pretty cheap */ 12401e04c3fSmrg if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { 12501e04c3fSmrg fprintf(stderr, "Unable to create socket for %s\n", nic->name); 12601e04c3fSmrg return; 12701e04c3fSmrg } 12801e04c3fSmrg 12901e04c3fSmrg if (ioctl(sockfd, SIOCGIWRATE, &req) == -1) { 13001e04c3fSmrg fprintf(stderr, "Error performing SIOCGIWSTATS on %s\n", nic->name); 13101e04c3fSmrg close(sockfd); 13201e04c3fSmrg return; 13301e04c3fSmrg } 13401e04c3fSmrg *bitrate = req.u.bitrate.value; 13501e04c3fSmrg 13601e04c3fSmrg close(sockfd); 13701e04c3fSmrg} 13801e04c3fSmrg 13901e04c3fSmrgstatic void 14001e04c3fSmrgquery_nic_rssi(const struct nic_info *nic, uint64_t *leveldBm) 14101e04c3fSmrg{ 14201e04c3fSmrg int sockfd; 14301e04c3fSmrg struct iw_statistics stats; 14401e04c3fSmrg struct iwreq req; 14501e04c3fSmrg 14601e04c3fSmrg memset(&stats, 0, sizeof(stats)); 14701e04c3fSmrg memset(&req, 0, sizeof(req)); 14801e04c3fSmrg 149361fc4cbSmaya snprintf(req.ifr_name, sizeof(req.ifr_name), "%s", nic->name); 15001e04c3fSmrg req.u.data.pointer = &stats; 15101e04c3fSmrg req.u.data.flags = 1; 15201e04c3fSmrg req.u.data.length = sizeof(struct iw_statistics); 15301e04c3fSmrg 15401e04c3fSmrg if (nic->mode != NIC_RSSI_DBM) 15501e04c3fSmrg return; 15601e04c3fSmrg 15701e04c3fSmrg /* Any old socket will do, and a datagram socket is pretty cheap */ 15801e04c3fSmrg if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { 15901e04c3fSmrg fprintf(stderr, "Unable to create socket for %s\n", nic->name); 16001e04c3fSmrg return; 16101e04c3fSmrg } 16201e04c3fSmrg 16301e04c3fSmrg /* Perform the ioctl */ 16401e04c3fSmrg if (ioctl(sockfd, SIOCGIWSTATS, &req) == -1) { 16501e04c3fSmrg fprintf(stderr, "Error performing SIOCGIWSTATS on %s\n", nic->name); 16601e04c3fSmrg close(sockfd); 16701e04c3fSmrg return; 16801e04c3fSmrg } 16901e04c3fSmrg *leveldBm = ((char) stats.qual.level * -1); 17001e04c3fSmrg 17101e04c3fSmrg close(sockfd); 17201e04c3fSmrg} 17301e04c3fSmrg 17401e04c3fSmrgstatic void 17501e04c3fSmrgquery_nic_load(struct hud_graph *gr, struct pipe_context *pipe) 17601e04c3fSmrg{ 17701e04c3fSmrg /* The framework calls us at a regular but indefined period, 17801e04c3fSmrg * not once per second, compensate the statistics accordingly. 17901e04c3fSmrg */ 18001e04c3fSmrg 18101e04c3fSmrg struct nic_info *nic = gr->query_data; 18201e04c3fSmrg uint64_t now = os_time_get(); 18301e04c3fSmrg 18401e04c3fSmrg if (nic->last_time) { 18501e04c3fSmrg if (nic->last_time + gr->pane->period <= now) { 18601e04c3fSmrg switch (nic->mode) { 18701e04c3fSmrg case NIC_DIRECTION_RX: 18801e04c3fSmrg case NIC_DIRECTION_TX: 18901e04c3fSmrg { 19001e04c3fSmrg uint64_t bytes; 19101e04c3fSmrg get_nic_bytes(nic->throughput_filename, &bytes); 19201e04c3fSmrg uint64_t nic_mbps = 19301e04c3fSmrg ((bytes - nic->last_nic_bytes) / 1000000) * 8; 19401e04c3fSmrg 19501e04c3fSmrg float speedMbps = nic->speedMbps; 196361fc4cbSmaya float periodMs = gr->pane->period / 1000.0; 19701e04c3fSmrg float bits = nic_mbps; 19801e04c3fSmrg float period_factor = periodMs / 1000; 19901e04c3fSmrg float period_speed = speedMbps * period_factor; 20001e04c3fSmrg float pct = (bits / period_speed) * 100; 20101e04c3fSmrg 20201e04c3fSmrg /* Scaling bps with a narrow time period into a second, 203361fc4cbSmaya * potentially suffers from rounding errors at higher 20401e04c3fSmrg * periods. Eg 104%. Compensate. 20501e04c3fSmrg */ 20601e04c3fSmrg if (pct > 100) 20701e04c3fSmrg pct = 100; 20801e04c3fSmrg hud_graph_add_value(gr, (uint64_t) pct); 20901e04c3fSmrg 21001e04c3fSmrg nic->last_nic_bytes = bytes; 21101e04c3fSmrg } 21201e04c3fSmrg break; 21301e04c3fSmrg case NIC_RSSI_DBM: 21401e04c3fSmrg { 21501e04c3fSmrg uint64_t leveldBm = 0; 21601e04c3fSmrg query_nic_rssi(nic, &leveldBm); 21701e04c3fSmrg hud_graph_add_value(gr, leveldBm); 21801e04c3fSmrg } 21901e04c3fSmrg break; 22001e04c3fSmrg } 22101e04c3fSmrg 22201e04c3fSmrg nic->last_time = now; 22301e04c3fSmrg } 22401e04c3fSmrg } 22501e04c3fSmrg else { 22601e04c3fSmrg /* initialize */ 22701e04c3fSmrg switch (nic->mode) { 22801e04c3fSmrg case NIC_DIRECTION_RX: 22901e04c3fSmrg case NIC_DIRECTION_TX: 23001e04c3fSmrg get_nic_bytes(nic->throughput_filename, &nic->last_nic_bytes); 23101e04c3fSmrg break; 23201e04c3fSmrg case NIC_RSSI_DBM: 23301e04c3fSmrg break; 23401e04c3fSmrg } 23501e04c3fSmrg 23601e04c3fSmrg nic->last_time = now; 23701e04c3fSmrg } 23801e04c3fSmrg} 23901e04c3fSmrg 24001e04c3fSmrg/** 24101e04c3fSmrg * Create and initialize a new object for a specific network interface dev. 24201e04c3fSmrg * \param pane parent context. 24301e04c3fSmrg * \param nic_name logical block device name, EG. eth0. 24401e04c3fSmrg * \param mode query type (NIC_DIRECTION_RX/WR/RSSI) statistics. 24501e04c3fSmrg */ 24601e04c3fSmrgvoid 24701e04c3fSmrghud_nic_graph_install(struct hud_pane *pane, const char *nic_name, 24801e04c3fSmrg unsigned int mode) 24901e04c3fSmrg{ 25001e04c3fSmrg struct hud_graph *gr; 25101e04c3fSmrg struct nic_info *nic; 25201e04c3fSmrg 25301e04c3fSmrg int num_nics = hud_get_num_nics(0); 25401e04c3fSmrg if (num_nics <= 0) 25501e04c3fSmrg return; 25601e04c3fSmrg 25701e04c3fSmrg nic = find_nic_by_name(nic_name, mode); 25801e04c3fSmrg if (!nic) 25901e04c3fSmrg return; 26001e04c3fSmrg 26101e04c3fSmrg gr = CALLOC_STRUCT(hud_graph); 26201e04c3fSmrg if (!gr) 26301e04c3fSmrg return; 26401e04c3fSmrg 26501e04c3fSmrg nic->mode = mode; 26601e04c3fSmrg if (nic->mode == NIC_DIRECTION_RX) { 26701e04c3fSmrg snprintf(gr->name, sizeof(gr->name), "%s-rx-%"PRId64"Mbps", nic->name, 26801e04c3fSmrg nic->speedMbps); 26901e04c3fSmrg } 27001e04c3fSmrg else if (nic->mode == NIC_DIRECTION_TX) { 27101e04c3fSmrg snprintf(gr->name, sizeof(gr->name), "%s-tx-%"PRId64"Mbps", nic->name, 27201e04c3fSmrg nic->speedMbps); 27301e04c3fSmrg } 27401e04c3fSmrg else if (nic->mode == NIC_RSSI_DBM) 27501e04c3fSmrg snprintf(gr->name, sizeof(gr->name), "%s-rssi", nic->name); 276361fc4cbSmaya else { 277361fc4cbSmaya free(gr); 27801e04c3fSmrg return; 279361fc4cbSmaya } 28001e04c3fSmrg 28101e04c3fSmrg gr->query_data = nic; 28201e04c3fSmrg gr->query_new_value = query_nic_load; 28301e04c3fSmrg 28401e04c3fSmrg hud_pane_add_graph(pane, gr); 28501e04c3fSmrg hud_pane_set_max_value(pane, 100); 28601e04c3fSmrg} 28701e04c3fSmrg 28801e04c3fSmrgstatic int 28901e04c3fSmrgis_wireless_nic(const char *dirbase) 29001e04c3fSmrg{ 29101e04c3fSmrg struct stat stat_buf; 29201e04c3fSmrg 29301e04c3fSmrg /* Check if its a wireless card */ 29401e04c3fSmrg char fn[256]; 29501e04c3fSmrg snprintf(fn, sizeof(fn), "%s/wireless", dirbase); 29601e04c3fSmrg if (stat(fn, &stat_buf) == 0) 29701e04c3fSmrg return 1; 29801e04c3fSmrg 29901e04c3fSmrg return 0; 30001e04c3fSmrg} 30101e04c3fSmrg 30201e04c3fSmrgstatic void 30301e04c3fSmrgquery_nic_bitrate(struct nic_info *nic, const char *dirbase) 30401e04c3fSmrg{ 30501e04c3fSmrg struct stat stat_buf; 30601e04c3fSmrg 30701e04c3fSmrg /* Check if its a wireless card */ 30801e04c3fSmrg char fn[256]; 30901e04c3fSmrg snprintf(fn, sizeof(fn), "%s/wireless", dirbase); 31001e04c3fSmrg if (stat(fn, &stat_buf) == 0) { 31101e04c3fSmrg /* we're a wireless nic */ 31201e04c3fSmrg query_wifi_bitrate(nic, &nic->speedMbps); 31301e04c3fSmrg nic->speedMbps /= 1000000; 31401e04c3fSmrg } 31501e04c3fSmrg else { 31601e04c3fSmrg /* Must be a wired nic */ 31701e04c3fSmrg snprintf(fn, sizeof(fn), "%s/speed", dirbase); 31801e04c3fSmrg get_file_value(fn, &nic->speedMbps); 31901e04c3fSmrg } 32001e04c3fSmrg} 32101e04c3fSmrg 32201e04c3fSmrg/** 32301e04c3fSmrg * Initialize internal object arrays and display NIC HUD help. 32401e04c3fSmrg * \param displayhelp true if the list of detected devices should be 32501e04c3fSmrg displayed on the console. 32601e04c3fSmrg * \return number of detected network interface devices. 32701e04c3fSmrg */ 32801e04c3fSmrgint 32901e04c3fSmrghud_get_num_nics(bool displayhelp) 33001e04c3fSmrg{ 33101e04c3fSmrg struct dirent *dp; 33201e04c3fSmrg struct stat stat_buf; 33301e04c3fSmrg struct nic_info *nic; 33401e04c3fSmrg char name[64]; 33501e04c3fSmrg 33601e04c3fSmrg /* Return the number if network interfaces. */ 33701e04c3fSmrg mtx_lock(&gnic_mutex); 33801e04c3fSmrg if (gnic_count) { 33901e04c3fSmrg mtx_unlock(&gnic_mutex); 34001e04c3fSmrg return gnic_count; 34101e04c3fSmrg } 34201e04c3fSmrg 34301e04c3fSmrg /* Scan /sys/block, for every object type we support, create and 34401e04c3fSmrg * persist an object to represent its different statistics. 34501e04c3fSmrg */ 34601e04c3fSmrg list_inithead(&gnic_list); 34701e04c3fSmrg DIR *dir = opendir("/sys/class/net/"); 34801e04c3fSmrg if (!dir) { 34901e04c3fSmrg mtx_unlock(&gnic_mutex); 35001e04c3fSmrg return 0; 35101e04c3fSmrg } 35201e04c3fSmrg 35301e04c3fSmrg while ((dp = readdir(dir)) != NULL) { 35401e04c3fSmrg 35501e04c3fSmrg /* Avoid 'lo' and '..' and '.' */ 35601e04c3fSmrg if (strlen(dp->d_name) <= 2) 35701e04c3fSmrg continue; 35801e04c3fSmrg 35901e04c3fSmrg char basename[256]; 36001e04c3fSmrg snprintf(basename, sizeof(basename), "/sys/class/net/%s", dp->d_name); 36101e04c3fSmrg snprintf(name, sizeof(name), "%s/statistics/rx_bytes", basename); 36201e04c3fSmrg if (stat(name, &stat_buf) < 0) 36301e04c3fSmrg continue; 36401e04c3fSmrg 36501e04c3fSmrg if (!S_ISREG(stat_buf.st_mode)) 36601e04c3fSmrg continue; /* Not a regular file */ 36701e04c3fSmrg 36801e04c3fSmrg int is_wireless = is_wireless_nic(basename); 36901e04c3fSmrg 37001e04c3fSmrg /* Add the RX object */ 37101e04c3fSmrg nic = CALLOC_STRUCT(nic_info); 37201e04c3fSmrg strcpy(nic->name, dp->d_name); 37301e04c3fSmrg snprintf(nic->throughput_filename, sizeof(nic->throughput_filename), 37401e04c3fSmrg "%s/statistics/rx_bytes", basename); 37501e04c3fSmrg nic->mode = NIC_DIRECTION_RX; 37601e04c3fSmrg nic->is_wireless = is_wireless; 37701e04c3fSmrg query_nic_bitrate(nic, basename); 37801e04c3fSmrg 37901e04c3fSmrg list_addtail(&nic->list, &gnic_list); 38001e04c3fSmrg gnic_count++; 38101e04c3fSmrg 38201e04c3fSmrg /* Add the TX object */ 38301e04c3fSmrg nic = CALLOC_STRUCT(nic_info); 38401e04c3fSmrg strcpy(nic->name, dp->d_name); 38501e04c3fSmrg snprintf(nic->throughput_filename, 38601e04c3fSmrg sizeof(nic->throughput_filename), 38701e04c3fSmrg "/sys/class/net/%s/statistics/tx_bytes", dp->d_name); 38801e04c3fSmrg nic->mode = NIC_DIRECTION_TX; 38901e04c3fSmrg nic->is_wireless = is_wireless; 39001e04c3fSmrg 39101e04c3fSmrg query_nic_bitrate(nic, basename); 39201e04c3fSmrg 39301e04c3fSmrg list_addtail(&nic->list, &gnic_list); 39401e04c3fSmrg gnic_count++; 39501e04c3fSmrg 39601e04c3fSmrg if (nic->is_wireless) { 39701e04c3fSmrg /* RSSI Support */ 39801e04c3fSmrg nic = CALLOC_STRUCT(nic_info); 39901e04c3fSmrg strcpy(nic->name, dp->d_name); 40001e04c3fSmrg snprintf(nic->throughput_filename, 40101e04c3fSmrg sizeof(nic->throughput_filename), 40201e04c3fSmrg "/sys/class/net/%s/statistics/tx_bytes", dp->d_name); 40301e04c3fSmrg nic->mode = NIC_RSSI_DBM; 40401e04c3fSmrg 40501e04c3fSmrg query_nic_bitrate(nic, basename); 40601e04c3fSmrg 40701e04c3fSmrg list_addtail(&nic->list, &gnic_list); 40801e04c3fSmrg gnic_count++; 40901e04c3fSmrg } 41001e04c3fSmrg 41101e04c3fSmrg } 41201e04c3fSmrg closedir(dir); 41301e04c3fSmrg 41401e04c3fSmrg list_for_each_entry(struct nic_info, nic, &gnic_list, list) { 41501e04c3fSmrg char line[64]; 41601e04c3fSmrg snprintf(line, sizeof(line), " nic-%s-%s", 41701e04c3fSmrg nic->mode == NIC_DIRECTION_RX ? "rx" : 41801e04c3fSmrg nic->mode == NIC_DIRECTION_TX ? "tx" : 41901e04c3fSmrg nic->mode == NIC_RSSI_DBM ? "rssi" : "undefined", nic->name); 42001e04c3fSmrg 42101e04c3fSmrg puts(line); 42201e04c3fSmrg 42301e04c3fSmrg } 42401e04c3fSmrg 42501e04c3fSmrg mtx_unlock(&gnic_mutex); 42601e04c3fSmrg return gnic_count; 42701e04c3fSmrg} 42801e04c3fSmrg 42901e04c3fSmrg#endif /* HAVE_GALLIUM_EXTRA_HUD */ 430