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