1b8e80941Smrg/************************************************************************** 2b8e80941Smrg * 3b8e80941Smrg * Copyright (C) 2016 Steven Toth <stoth@kernellabs.com> 4b8e80941Smrg * Copyright (C) 2016 Zodiac Inflight Innovations 5b8e80941Smrg * All Rights Reserved. 6b8e80941Smrg * 7b8e80941Smrg * Permission is hereby granted, free of charge, to any person obtaining a 8b8e80941Smrg * copy of this software and associated documentation files (the 9b8e80941Smrg * "Software"), to deal in the Software without restriction, including 10b8e80941Smrg * without limitation the rights to use, copy, modify, merge, publish, 11b8e80941Smrg * distribute, sub license, and/or sell copies of the Software, and to 12b8e80941Smrg * permit persons to whom the Software is furnished to do so, subject to 13b8e80941Smrg * the following conditions: 14b8e80941Smrg * 15b8e80941Smrg * The above copyright notice and this permission notice (including the 16b8e80941Smrg * next paragraph) shall be included in all copies or substantial portions 17b8e80941Smrg * of the Software. 18b8e80941Smrg * 19b8e80941Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 20b8e80941Smrg * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21b8e80941Smrg * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. 22b8e80941Smrg * IN NO EVENT SHALL THE AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR 23b8e80941Smrg * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 24b8e80941Smrg * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 25b8e80941Smrg * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26b8e80941Smrg * 27b8e80941Smrg **************************************************************************/ 28b8e80941Smrg 29b8e80941Smrg#ifdef HAVE_GALLIUM_EXTRA_HUD 30b8e80941Smrg 31b8e80941Smrg/* Purpose: Reading network interface RX/TX throughput per second, 32b8e80941Smrg * displaying on the HUD. 33b8e80941Smrg */ 34b8e80941Smrg 35b8e80941Smrg#include "hud/hud_private.h" 36b8e80941Smrg#include "util/list.h" 37b8e80941Smrg#include "util/os_time.h" 38b8e80941Smrg#include "os/os_thread.h" 39b8e80941Smrg#include "util/u_memory.h" 40b8e80941Smrg#include <stdio.h> 41b8e80941Smrg#include <unistd.h> 42b8e80941Smrg#include <dirent.h> 43b8e80941Smrg#include <stdlib.h> 44b8e80941Smrg#include <unistd.h> 45b8e80941Smrg#include <inttypes.h> 46b8e80941Smrg#include <sys/types.h> 47b8e80941Smrg#include <sys/stat.h> 48b8e80941Smrg#include <sys/socket.h> 49b8e80941Smrg#include <sys/ioctl.h> 50b8e80941Smrg#include <linux/wireless.h> 51b8e80941Smrg 52b8e80941Smrgstruct nic_info 53b8e80941Smrg{ 54b8e80941Smrg struct list_head list; 55b8e80941Smrg int mode; 56b8e80941Smrg char name[64]; 57b8e80941Smrg uint64_t speedMbps; 58b8e80941Smrg int is_wireless; 59b8e80941Smrg 60b8e80941Smrg char throughput_filename[128]; 61b8e80941Smrg uint64_t last_time; 62b8e80941Smrg uint64_t last_nic_bytes; 63b8e80941Smrg}; 64b8e80941Smrg 65b8e80941Smrg/* TODO: We don't handle dynamic NIC arrival or removal. 66b8e80941Smrg * Static globals specific to this HUD category. 67b8e80941Smrg */ 68b8e80941Smrgstatic int gnic_count = 0; 69b8e80941Smrgstatic struct list_head gnic_list; 70b8e80941Smrgstatic mtx_t gnic_mutex = _MTX_INITIALIZER_NP; 71b8e80941Smrg 72b8e80941Smrgstatic struct nic_info * 73b8e80941Smrgfind_nic_by_name(const char *n, int mode) 74b8e80941Smrg{ 75b8e80941Smrg list_for_each_entry(struct nic_info, nic, &gnic_list, list) { 76b8e80941Smrg if (nic->mode != mode) 77b8e80941Smrg continue; 78b8e80941Smrg 79b8e80941Smrg if (strcasecmp(nic->name, n) == 0) 80b8e80941Smrg return nic; 81b8e80941Smrg } 82b8e80941Smrg return 0; 83b8e80941Smrg} 84b8e80941Smrg 85b8e80941Smrgstatic int 86b8e80941Smrgget_file_value(const char *fname, uint64_t *value) 87b8e80941Smrg{ 88b8e80941Smrg FILE *fh = fopen(fname, "r"); 89b8e80941Smrg if (!fh) 90b8e80941Smrg return -1; 91b8e80941Smrg if (fscanf(fh, "%" PRIu64 "", value) != 0) { 92b8e80941Smrg /* Error */ 93b8e80941Smrg } 94b8e80941Smrg fclose(fh); 95b8e80941Smrg return 0; 96b8e80941Smrg} 97b8e80941Smrg 98b8e80941Smrgstatic boolean 99b8e80941Smrgget_nic_bytes(const char *fn, uint64_t *bytes) 100b8e80941Smrg{ 101b8e80941Smrg if (get_file_value(fn, bytes) < 0) 102b8e80941Smrg return FALSE; 103b8e80941Smrg 104b8e80941Smrg return TRUE; 105b8e80941Smrg} 106b8e80941Smrg 107b8e80941Smrgstatic void 108b8e80941Smrgquery_wifi_bitrate(const struct nic_info *nic, uint64_t *bitrate) 109b8e80941Smrg{ 110b8e80941Smrg int sockfd; 111b8e80941Smrg struct iw_statistics stats; 112b8e80941Smrg struct iwreq req; 113b8e80941Smrg 114b8e80941Smrg memset(&stats, 0, sizeof(stats)); 115b8e80941Smrg memset(&req, 0, sizeof(req)); 116b8e80941Smrg 117b8e80941Smrg snprintf(req.ifr_name, sizeof(req.ifr_name), "%s", nic->name); 118b8e80941Smrg req.u.data.pointer = &stats; 119b8e80941Smrg req.u.data.flags = 1; 120b8e80941Smrg req.u.data.length = sizeof(struct iw_statistics); 121b8e80941Smrg 122b8e80941Smrg /* Any old socket will do, and a datagram socket is pretty cheap */ 123b8e80941Smrg if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { 124b8e80941Smrg fprintf(stderr, "Unable to create socket for %s\n", nic->name); 125b8e80941Smrg return; 126b8e80941Smrg } 127b8e80941Smrg 128b8e80941Smrg if (ioctl(sockfd, SIOCGIWRATE, &req) == -1) { 129b8e80941Smrg fprintf(stderr, "Error performing SIOCGIWSTATS on %s\n", nic->name); 130b8e80941Smrg close(sockfd); 131b8e80941Smrg return; 132b8e80941Smrg } 133b8e80941Smrg *bitrate = req.u.bitrate.value; 134b8e80941Smrg 135b8e80941Smrg close(sockfd); 136b8e80941Smrg} 137b8e80941Smrg 138b8e80941Smrgstatic void 139b8e80941Smrgquery_nic_rssi(const struct nic_info *nic, uint64_t *leveldBm) 140b8e80941Smrg{ 141b8e80941Smrg int sockfd; 142b8e80941Smrg struct iw_statistics stats; 143b8e80941Smrg struct iwreq req; 144b8e80941Smrg 145b8e80941Smrg memset(&stats, 0, sizeof(stats)); 146b8e80941Smrg memset(&req, 0, sizeof(req)); 147b8e80941Smrg 148b8e80941Smrg snprintf(req.ifr_name, sizeof(req.ifr_name), "%s", nic->name); 149b8e80941Smrg req.u.data.pointer = &stats; 150b8e80941Smrg req.u.data.flags = 1; 151b8e80941Smrg req.u.data.length = sizeof(struct iw_statistics); 152b8e80941Smrg 153b8e80941Smrg if (nic->mode != NIC_RSSI_DBM) 154b8e80941Smrg return; 155b8e80941Smrg 156b8e80941Smrg /* Any old socket will do, and a datagram socket is pretty cheap */ 157b8e80941Smrg if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { 158b8e80941Smrg fprintf(stderr, "Unable to create socket for %s\n", nic->name); 159b8e80941Smrg return; 160b8e80941Smrg } 161b8e80941Smrg 162b8e80941Smrg /* Perform the ioctl */ 163b8e80941Smrg if (ioctl(sockfd, SIOCGIWSTATS, &req) == -1) { 164b8e80941Smrg fprintf(stderr, "Error performing SIOCGIWSTATS on %s\n", nic->name); 165b8e80941Smrg close(sockfd); 166b8e80941Smrg return; 167b8e80941Smrg } 168b8e80941Smrg *leveldBm = ((char) stats.qual.level * -1); 169b8e80941Smrg 170b8e80941Smrg close(sockfd); 171b8e80941Smrg} 172b8e80941Smrg 173b8e80941Smrgstatic void 174b8e80941Smrgquery_nic_load(struct hud_graph *gr, struct pipe_context *pipe) 175b8e80941Smrg{ 176b8e80941Smrg /* The framework calls us at a regular but indefined period, 177b8e80941Smrg * not once per second, compensate the statistics accordingly. 178b8e80941Smrg */ 179b8e80941Smrg 180b8e80941Smrg struct nic_info *nic = gr->query_data; 181b8e80941Smrg uint64_t now = os_time_get(); 182b8e80941Smrg 183b8e80941Smrg if (nic->last_time) { 184b8e80941Smrg if (nic->last_time + gr->pane->period <= now) { 185b8e80941Smrg switch (nic->mode) { 186b8e80941Smrg case NIC_DIRECTION_RX: 187b8e80941Smrg case NIC_DIRECTION_TX: 188b8e80941Smrg { 189b8e80941Smrg uint64_t bytes; 190b8e80941Smrg get_nic_bytes(nic->throughput_filename, &bytes); 191b8e80941Smrg uint64_t nic_mbps = 192b8e80941Smrg ((bytes - nic->last_nic_bytes) / 1000000) * 8; 193b8e80941Smrg 194b8e80941Smrg float speedMbps = nic->speedMbps; 195b8e80941Smrg float periodMs = gr->pane->period / 1000.0; 196b8e80941Smrg float bits = nic_mbps; 197b8e80941Smrg float period_factor = periodMs / 1000; 198b8e80941Smrg float period_speed = speedMbps * period_factor; 199b8e80941Smrg float pct = (bits / period_speed) * 100; 200b8e80941Smrg 201b8e80941Smrg /* Scaling bps with a narrow time period into a second, 202b8e80941Smrg * potentially suffers from rounding errors at higher 203b8e80941Smrg * periods. Eg 104%. Compensate. 204b8e80941Smrg */ 205b8e80941Smrg if (pct > 100) 206b8e80941Smrg pct = 100; 207b8e80941Smrg hud_graph_add_value(gr, (uint64_t) pct); 208b8e80941Smrg 209b8e80941Smrg nic->last_nic_bytes = bytes; 210b8e80941Smrg } 211b8e80941Smrg break; 212b8e80941Smrg case NIC_RSSI_DBM: 213b8e80941Smrg { 214b8e80941Smrg uint64_t leveldBm = 0; 215b8e80941Smrg query_nic_rssi(nic, &leveldBm); 216b8e80941Smrg hud_graph_add_value(gr, leveldBm); 217b8e80941Smrg } 218b8e80941Smrg break; 219b8e80941Smrg } 220b8e80941Smrg 221b8e80941Smrg nic->last_time = now; 222b8e80941Smrg } 223b8e80941Smrg } 224b8e80941Smrg else { 225b8e80941Smrg /* initialize */ 226b8e80941Smrg switch (nic->mode) { 227b8e80941Smrg case NIC_DIRECTION_RX: 228b8e80941Smrg case NIC_DIRECTION_TX: 229b8e80941Smrg get_nic_bytes(nic->throughput_filename, &nic->last_nic_bytes); 230b8e80941Smrg break; 231b8e80941Smrg case NIC_RSSI_DBM: 232b8e80941Smrg break; 233b8e80941Smrg } 234b8e80941Smrg 235b8e80941Smrg nic->last_time = now; 236b8e80941Smrg } 237b8e80941Smrg} 238b8e80941Smrg 239b8e80941Smrg/** 240b8e80941Smrg * Create and initialize a new object for a specific network interface dev. 241b8e80941Smrg * \param pane parent context. 242b8e80941Smrg * \param nic_name logical block device name, EG. eth0. 243b8e80941Smrg * \param mode query type (NIC_DIRECTION_RX/WR/RSSI) statistics. 244b8e80941Smrg */ 245b8e80941Smrgvoid 246b8e80941Smrghud_nic_graph_install(struct hud_pane *pane, const char *nic_name, 247b8e80941Smrg unsigned int mode) 248b8e80941Smrg{ 249b8e80941Smrg struct hud_graph *gr; 250b8e80941Smrg struct nic_info *nic; 251b8e80941Smrg 252b8e80941Smrg int num_nics = hud_get_num_nics(0); 253b8e80941Smrg if (num_nics <= 0) 254b8e80941Smrg return; 255b8e80941Smrg 256b8e80941Smrg nic = find_nic_by_name(nic_name, mode); 257b8e80941Smrg if (!nic) 258b8e80941Smrg return; 259b8e80941Smrg 260b8e80941Smrg gr = CALLOC_STRUCT(hud_graph); 261b8e80941Smrg if (!gr) 262b8e80941Smrg return; 263b8e80941Smrg 264b8e80941Smrg nic->mode = mode; 265b8e80941Smrg if (nic->mode == NIC_DIRECTION_RX) { 266b8e80941Smrg snprintf(gr->name, sizeof(gr->name), "%s-rx-%"PRId64"Mbps", nic->name, 267b8e80941Smrg nic->speedMbps); 268b8e80941Smrg } 269b8e80941Smrg else if (nic->mode == NIC_DIRECTION_TX) { 270b8e80941Smrg snprintf(gr->name, sizeof(gr->name), "%s-tx-%"PRId64"Mbps", nic->name, 271b8e80941Smrg nic->speedMbps); 272b8e80941Smrg } 273b8e80941Smrg else if (nic->mode == NIC_RSSI_DBM) 274b8e80941Smrg snprintf(gr->name, sizeof(gr->name), "%s-rssi", nic->name); 275b8e80941Smrg else { 276b8e80941Smrg free(gr); 277b8e80941Smrg return; 278b8e80941Smrg } 279b8e80941Smrg 280b8e80941Smrg gr->query_data = nic; 281b8e80941Smrg gr->query_new_value = query_nic_load; 282b8e80941Smrg 283b8e80941Smrg hud_pane_add_graph(pane, gr); 284b8e80941Smrg hud_pane_set_max_value(pane, 100); 285b8e80941Smrg} 286b8e80941Smrg 287b8e80941Smrgstatic int 288b8e80941Smrgis_wireless_nic(const char *dirbase) 289b8e80941Smrg{ 290b8e80941Smrg struct stat stat_buf; 291b8e80941Smrg 292b8e80941Smrg /* Check if its a wireless card */ 293b8e80941Smrg char fn[256]; 294b8e80941Smrg snprintf(fn, sizeof(fn), "%s/wireless", dirbase); 295b8e80941Smrg if (stat(fn, &stat_buf) == 0) 296b8e80941Smrg return 1; 297b8e80941Smrg 298b8e80941Smrg return 0; 299b8e80941Smrg} 300b8e80941Smrg 301b8e80941Smrgstatic void 302b8e80941Smrgquery_nic_bitrate(struct nic_info *nic, const char *dirbase) 303b8e80941Smrg{ 304b8e80941Smrg struct stat stat_buf; 305b8e80941Smrg 306b8e80941Smrg /* Check if its a wireless card */ 307b8e80941Smrg char fn[256]; 308b8e80941Smrg snprintf(fn, sizeof(fn), "%s/wireless", dirbase); 309b8e80941Smrg if (stat(fn, &stat_buf) == 0) { 310b8e80941Smrg /* we're a wireless nic */ 311b8e80941Smrg query_wifi_bitrate(nic, &nic->speedMbps); 312b8e80941Smrg nic->speedMbps /= 1000000; 313b8e80941Smrg } 314b8e80941Smrg else { 315b8e80941Smrg /* Must be a wired nic */ 316b8e80941Smrg snprintf(fn, sizeof(fn), "%s/speed", dirbase); 317b8e80941Smrg get_file_value(fn, &nic->speedMbps); 318b8e80941Smrg } 319b8e80941Smrg} 320b8e80941Smrg 321b8e80941Smrg/** 322b8e80941Smrg * Initialize internal object arrays and display NIC HUD help. 323b8e80941Smrg * \param displayhelp true if the list of detected devices should be 324b8e80941Smrg displayed on the console. 325b8e80941Smrg * \return number of detected network interface devices. 326b8e80941Smrg */ 327b8e80941Smrgint 328b8e80941Smrghud_get_num_nics(bool displayhelp) 329b8e80941Smrg{ 330b8e80941Smrg struct dirent *dp; 331b8e80941Smrg struct stat stat_buf; 332b8e80941Smrg struct nic_info *nic; 333b8e80941Smrg char name[64]; 334b8e80941Smrg 335b8e80941Smrg /* Return the number if network interfaces. */ 336b8e80941Smrg mtx_lock(&gnic_mutex); 337b8e80941Smrg if (gnic_count) { 338b8e80941Smrg mtx_unlock(&gnic_mutex); 339b8e80941Smrg return gnic_count; 340b8e80941Smrg } 341b8e80941Smrg 342b8e80941Smrg /* Scan /sys/block, for every object type we support, create and 343b8e80941Smrg * persist an object to represent its different statistics. 344b8e80941Smrg */ 345b8e80941Smrg list_inithead(&gnic_list); 346b8e80941Smrg DIR *dir = opendir("/sys/class/net/"); 347b8e80941Smrg if (!dir) { 348b8e80941Smrg mtx_unlock(&gnic_mutex); 349b8e80941Smrg return 0; 350b8e80941Smrg } 351b8e80941Smrg 352b8e80941Smrg while ((dp = readdir(dir)) != NULL) { 353b8e80941Smrg 354b8e80941Smrg /* Avoid 'lo' and '..' and '.' */ 355b8e80941Smrg if (strlen(dp->d_name) <= 2) 356b8e80941Smrg continue; 357b8e80941Smrg 358b8e80941Smrg char basename[256]; 359b8e80941Smrg snprintf(basename, sizeof(basename), "/sys/class/net/%s", dp->d_name); 360b8e80941Smrg snprintf(name, sizeof(name), "%s/statistics/rx_bytes", basename); 361b8e80941Smrg if (stat(name, &stat_buf) < 0) 362b8e80941Smrg continue; 363b8e80941Smrg 364b8e80941Smrg if (!S_ISREG(stat_buf.st_mode)) 365b8e80941Smrg continue; /* Not a regular file */ 366b8e80941Smrg 367b8e80941Smrg int is_wireless = is_wireless_nic(basename); 368b8e80941Smrg 369b8e80941Smrg /* Add the RX object */ 370b8e80941Smrg nic = CALLOC_STRUCT(nic_info); 371b8e80941Smrg strcpy(nic->name, dp->d_name); 372b8e80941Smrg snprintf(nic->throughput_filename, sizeof(nic->throughput_filename), 373b8e80941Smrg "%s/statistics/rx_bytes", basename); 374b8e80941Smrg nic->mode = NIC_DIRECTION_RX; 375b8e80941Smrg nic->is_wireless = is_wireless; 376b8e80941Smrg query_nic_bitrate(nic, basename); 377b8e80941Smrg 378b8e80941Smrg list_addtail(&nic->list, &gnic_list); 379b8e80941Smrg gnic_count++; 380b8e80941Smrg 381b8e80941Smrg /* Add the TX object */ 382b8e80941Smrg nic = CALLOC_STRUCT(nic_info); 383b8e80941Smrg strcpy(nic->name, dp->d_name); 384b8e80941Smrg snprintf(nic->throughput_filename, 385b8e80941Smrg sizeof(nic->throughput_filename), 386b8e80941Smrg "/sys/class/net/%s/statistics/tx_bytes", dp->d_name); 387b8e80941Smrg nic->mode = NIC_DIRECTION_TX; 388b8e80941Smrg nic->is_wireless = is_wireless; 389b8e80941Smrg 390b8e80941Smrg query_nic_bitrate(nic, basename); 391b8e80941Smrg 392b8e80941Smrg list_addtail(&nic->list, &gnic_list); 393b8e80941Smrg gnic_count++; 394b8e80941Smrg 395b8e80941Smrg if (nic->is_wireless) { 396b8e80941Smrg /* RSSI Support */ 397b8e80941Smrg nic = CALLOC_STRUCT(nic_info); 398b8e80941Smrg strcpy(nic->name, dp->d_name); 399b8e80941Smrg snprintf(nic->throughput_filename, 400b8e80941Smrg sizeof(nic->throughput_filename), 401b8e80941Smrg "/sys/class/net/%s/statistics/tx_bytes", dp->d_name); 402b8e80941Smrg nic->mode = NIC_RSSI_DBM; 403b8e80941Smrg 404b8e80941Smrg query_nic_bitrate(nic, basename); 405b8e80941Smrg 406b8e80941Smrg list_addtail(&nic->list, &gnic_list); 407b8e80941Smrg gnic_count++; 408b8e80941Smrg } 409b8e80941Smrg 410b8e80941Smrg } 411b8e80941Smrg closedir(dir); 412b8e80941Smrg 413b8e80941Smrg list_for_each_entry(struct nic_info, nic, &gnic_list, list) { 414b8e80941Smrg char line[64]; 415b8e80941Smrg snprintf(line, sizeof(line), " nic-%s-%s", 416b8e80941Smrg nic->mode == NIC_DIRECTION_RX ? "rx" : 417b8e80941Smrg nic->mode == NIC_DIRECTION_TX ? "tx" : 418b8e80941Smrg nic->mode == NIC_RSSI_DBM ? "rssi" : "undefined", nic->name); 419b8e80941Smrg 420b8e80941Smrg puts(line); 421b8e80941Smrg 422b8e80941Smrg } 423b8e80941Smrg 424b8e80941Smrg mtx_unlock(&gnic_mutex); 425b8e80941Smrg return gnic_count; 426b8e80941Smrg} 427b8e80941Smrg 428b8e80941Smrg#endif /* HAVE_GALLIUM_EXTRA_HUD */ 429