1 /**************************************************************************
3 * Copyright (C) 2016 Steven Toth <stoth@kernellabs.com>
4 * Copyright (C) 2016 Zodiac Inflight Innovations
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:
15 * The above copyright notice and this permission notice (including the
16 * next paragraph) shall be included in all copies or substantial portions
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.
27 **************************************************************************/
29 #if HAVE_GALLIUM_EXTRA_HUD
31 /* Purpose: Reading network interface RX/TX throughput per second,
32 * displaying on the HUD.
35 #include "hud/hud_private.h"
36 #include "util/list.h"
37 #include "os/os_time.h"
38 #include "util/u_memory.h"
45 #include <sys/types.h>
47 #include <sys/socket.h>
48 #include <sys/ioctl.h>
49 #include <linux/wireless.h>
55 struct list_head list
;
61 char throughput_filename
[128];
63 uint64_t last_nic_bytes
;
66 /* TODO: We don't handle dynamic NIC arrival or removal.
67 * Static globals specific to this HUD category.
69 static int gnic_count
= 0;
70 static struct list_head gnic_list
;
72 static struct nic_info
*
73 find_nic_by_name(const char *n
, int mode
)
75 list_for_each_entry(struct nic_info
, nic
, &gnic_list
, list
) {
76 if (nic
->mode
!= mode
)
79 if (strcasecmp(nic
->name
, n
) == 0)
86 get_file_value(const char *fname
, uint64_t *value
)
88 FILE *fh
= fopen(fname
, "r");
91 if (fscanf(fh
, "%" PRIu64
"", value
) != 0) {
99 get_nic_bytes(const char *fn
, uint64_t *bytes
)
101 if (get_file_value(fn
, bytes
) < 0)
108 query_wifi_bitrate(const struct nic_info
*nic
, uint64_t *bitrate
)
111 struct iw_statistics stats
;
114 memset(&stats
, 0, sizeof(stats
));
115 memset(&req
, 0, sizeof(req
));
117 strcpy(req
.ifr_name
, nic
->name
);
118 req
.u
.data
.pointer
= &stats
;
119 req
.u
.data
.flags
= 1;
120 req
.u
.data
.length
= sizeof(struct iw_statistics
);
122 /* Any old socket will do, and a datagram socket is pretty cheap */
123 if ((sockfd
= socket(AF_INET
, SOCK_DGRAM
, 0)) == -1) {
124 fprintf(stderr
, "Unable to create socket for %s\n", nic
->name
);
128 if (ioctl(sockfd
, SIOCGIWRATE
, &req
) == -1) {
129 fprintf(stderr
, "Error performing SIOCGIWSTATS on %s\n", nic
->name
);
133 *bitrate
= req
.u
.bitrate
.value
;
139 query_nic_rssi(const struct nic_info
*nic
, uint64_t *leveldBm
)
142 struct iw_statistics stats
;
145 memset(&stats
, 0, sizeof(stats
));
146 memset(&req
, 0, sizeof(req
));
148 strcpy(req
.ifr_name
, nic
->name
);
149 req
.u
.data
.pointer
= &stats
;
150 req
.u
.data
.flags
= 1;
151 req
.u
.data
.length
= sizeof(struct iw_statistics
);
153 if (nic
->mode
!= NIC_RSSI_DBM
)
156 /* Any old socket will do, and a datagram socket is pretty cheap */
157 if ((sockfd
= socket(AF_INET
, SOCK_DGRAM
, 0)) == -1) {
158 fprintf(stderr
, "Unable to create socket for %s\n", nic
->name
);
162 /* Perform the ioctl */
163 if (ioctl(sockfd
, SIOCGIWSTATS
, &req
) == -1) {
164 fprintf(stderr
, "Error performing SIOCGIWSTATS on %s\n", nic
->name
);
168 *leveldBm
= ((char) stats
.qual
.level
* -1);
173 printf("NIC signal level%s is %d%s.\n",
174 (stats
.qual
.updated
& IW_QUAL_DBM
? " (in dBm)" : ""),
175 (char) stats
.qual
.level
,
176 (stats
.qual
.updated
& IW_QUAL_LEVEL_UPDATED
? " (updated)" : ""));
181 query_nic_load(struct hud_graph
*gr
)
183 /* The framework calls us at a regular but indefined period,
184 * not once per second, compensate the statistics accordingly.
187 struct nic_info
*nic
= gr
->query_data
;
188 uint64_t now
= os_time_get();
190 if (nic
->last_time
) {
191 if (nic
->last_time
+ gr
->pane
->period
<= now
) {
193 case NIC_DIRECTION_RX
:
194 case NIC_DIRECTION_TX
:
197 get_nic_bytes(nic
->throughput_filename
, &bytes
);
199 ((bytes
- nic
->last_nic_bytes
) / 1000000) * 8;
201 float speedMbps
= nic
->speedMbps
;
202 float periodMs
= gr
->pane
->period
/ 1000;
203 float bits
= nic_mbps
;
204 float period_factor
= periodMs
/ 1000;
205 float period_speed
= speedMbps
* period_factor
;
206 float pct
= (bits
/ period_speed
) * 100;
208 /* Scaling bps with a narrow time period into a second,
209 * potentially suffers from routing errors at higher
210 * periods. Eg 104%. Compensate.
214 hud_graph_add_value(gr
, (uint64_t) pct
);
216 nic
->last_nic_bytes
= bytes
;
221 uint64_t leveldBm
= 0;
222 query_nic_rssi(nic
, &leveldBm
);
223 hud_graph_add_value(gr
, leveldBm
);
228 nic
->last_time
= now
;
234 case NIC_DIRECTION_RX
:
235 case NIC_DIRECTION_TX
:
236 get_nic_bytes(nic
->throughput_filename
, &nic
->last_nic_bytes
);
242 nic
->last_time
= now
;
247 free_query_data(void *p
)
249 struct nic_info
*nic
= (struct nic_info
*) p
;
250 list_del(&nic
->list
);
255 * Create and initialize a new object for a specific network interface dev.
256 * \param pane parent context.
257 * \param nic_name logical block device name, EG. eth0.
258 * \param mode query type (NIC_DIRECTION_RX/WR/RSSI) statistics.
261 hud_nic_graph_install(struct hud_pane
*pane
, const char *nic_name
,
264 struct hud_graph
*gr
;
265 struct nic_info
*nic
;
267 int num_nics
= hud_get_num_nics(0);
272 printf("%s(%s, %s) - Creating HUD object\n", __func__
, nic_name
,
273 mode
== NIC_DIRECTION_RX
? "RX" :
274 mode
== NIC_DIRECTION_TX
? "TX" :
275 mode
== NIC_RSSI_DBM
? "RSSI" : "UNDEFINED");
278 nic
= find_nic_by_name(nic_name
, mode
);
282 gr
= CALLOC_STRUCT(hud_graph
);
287 if (nic
->mode
== NIC_DIRECTION_RX
) {
288 snprintf(gr
->name
, sizeof(gr
->name
), "%s-rx-%lldMbps", nic
->name
,
291 else if (nic
->mode
== NIC_DIRECTION_TX
) {
292 snprintf(gr
->name
, sizeof(gr
->name
), "%s-tx-%lldMbps", nic
->name
,
295 else if (nic
->mode
== NIC_RSSI_DBM
)
296 snprintf(gr
->name
, sizeof(gr
->name
), "%s-rssi", nic
->name
);
300 gr
->query_data
= nic
;
301 gr
->query_new_value
= query_nic_load
;
303 /* Don't use free() as our callback as that messes up Gallium's
304 * memory debugger. Use simple free_query_data() wrapper.
306 gr
->free_query_data
= free_query_data
;
308 hud_pane_add_graph(pane
, gr
);
309 hud_pane_set_max_value(pane
, 100);
313 is_wireless_nic(const char *dirbase
)
315 struct stat stat_buf
;
317 /* Check if its a wireless card */
319 snprintf(fn
, sizeof(fn
), "%s/wireless", dirbase
);
320 if (stat(fn
, &stat_buf
) == 0)
327 query_nic_bitrate(struct nic_info
*nic
, const char *dirbase
)
329 struct stat stat_buf
;
331 /* Check if its a wireless card */
333 snprintf(fn
, sizeof(fn
), "%s/wireless", dirbase
);
334 if (stat(fn
, &stat_buf
) == 0) {
335 /* we're a wireless nic */
336 query_wifi_bitrate(nic
, &nic
->speedMbps
);
337 nic
->speedMbps
/= 1000000;
340 /* Must be a wired nic */
341 snprintf(fn
, sizeof(fn
), "%s/speed", dirbase
);
342 get_file_value(fn
, &nic
->speedMbps
);
347 * Initialize internal object arrays and display NIC HUD help.
348 * \param displayhelp true if the list of detected devices should be
349 displayed on the console.
350 * \return number of detected network interface devices.
353 hud_get_num_nics(bool displayhelp
)
356 struct stat stat_buf
;
357 struct nic_info
*nic
;
360 /* Return the number if network interfaces. */
364 /* Scan /sys/block, for every object type we support, create and
365 * persist an object to represent its different statistics.
367 list_inithead(&gnic_list
);
368 DIR *dir
= opendir("/sys/class/net/");
372 while ((dp
= readdir(dir
)) != NULL
) {
374 /* Avoid 'lo' and '..' and '.' */
375 if (strlen(dp
->d_name
) <= 2)
379 snprintf(basename
, sizeof(basename
), "/sys/class/net/%s", dp
->d_name
);
380 snprintf(name
, sizeof(name
), "%s/statistics/rx_bytes", basename
);
381 if (stat(name
, &stat_buf
) < 0)
384 if (!S_ISREG(stat_buf
.st_mode
))
385 continue; /* Not a regular file */
387 int is_wireless
= is_wireless_nic(basename
);
389 /* Add the RX object */
390 nic
= CALLOC_STRUCT(nic_info
);
391 strcpy(nic
->name
, dp
->d_name
);
392 snprintf(nic
->throughput_filename
, sizeof(nic
->throughput_filename
),
393 "%s/statistics/rx_bytes", basename
);
394 nic
->mode
= NIC_DIRECTION_RX
;
395 nic
->is_wireless
= is_wireless
;
396 query_nic_bitrate(nic
, basename
);
398 list_addtail(&nic
->list
, &gnic_list
);
401 /* Add the TX object */
402 nic
= CALLOC_STRUCT(nic_info
);
403 strcpy(nic
->name
, dp
->d_name
);
404 snprintf(nic
->throughput_filename
,
405 sizeof(nic
->throughput_filename
),
406 "/sys/class/net/%s/statistics/tx_bytes", dp
->d_name
);
407 nic
->mode
= NIC_DIRECTION_TX
;
408 nic
->is_wireless
= is_wireless
;
410 query_nic_bitrate(nic
, basename
);
412 list_addtail(&nic
->list
, &gnic_list
);
415 if (nic
->is_wireless
) {
417 nic
= CALLOC_STRUCT(nic_info
);
418 strcpy(nic
->name
, dp
->d_name
);
419 snprintf(nic
->throughput_filename
,
420 sizeof(nic
->throughput_filename
),
421 "/sys/class/net/%s/statistics/tx_bytes", dp
->d_name
);
422 nic
->mode
= NIC_RSSI_DBM
;
424 query_nic_bitrate(nic
, basename
);
426 list_addtail(&nic
->list
, &gnic_list
);
432 list_for_each_entry(struct nic_info
, nic
, &gnic_list
, list
) {
434 snprintf(line
, sizeof(line
), " nic-%s-%s",
435 nic
->mode
== NIC_DIRECTION_RX
? "rx" :
436 nic
->mode
== NIC_DIRECTION_TX
? "tx" :
437 nic
->mode
== NIC_RSSI_DBM
? "rssi" : "undefined", nic
->name
);
446 #endif /* HAVE_GALLIUM_EXTRA_HUD */