Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mesa
Path: blob/21.2-virgl/src/gallium/auxiliary/hud/hud_nic.c
4565 views
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 "os/os_thread.h"
39
#include "util/u_memory.h"
40
#include "util/u_string.h"
41
#include <stdio.h>
42
#include <unistd.h>
43
#include <dirent.h>
44
#include <stdlib.h>
45
#include <unistd.h>
46
#include <inttypes.h>
47
#include <sys/types.h>
48
#include <sys/stat.h>
49
#include <sys/socket.h>
50
#include <sys/ioctl.h>
51
#include <linux/wireless.h>
52
53
struct nic_info
54
{
55
struct list_head list;
56
int mode;
57
char name[64];
58
uint64_t speedMbps;
59
int is_wireless;
60
61
char throughput_filename[128];
62
uint64_t last_time;
63
uint64_t last_nic_bytes;
64
};
65
66
/* TODO: We don't handle dynamic NIC arrival or removal.
67
* Static globals specific to this HUD category.
68
*/
69
static int gnic_count = 0;
70
static struct list_head gnic_list;
71
static mtx_t gnic_mutex = _MTX_INITIALIZER_NP;
72
73
static struct nic_info *
74
find_nic_by_name(const char *n, int mode)
75
{
76
list_for_each_entry(struct nic_info, nic, &gnic_list, list) {
77
if (nic->mode != mode)
78
continue;
79
80
if (strcasecmp(nic->name, n) == 0)
81
return nic;
82
}
83
return 0;
84
}
85
86
static int
87
get_file_value(const char *fname, uint64_t *value)
88
{
89
FILE *fh = fopen(fname, "r");
90
if (!fh)
91
return -1;
92
if (fscanf(fh, "%" PRIu64 "", value) != 0) {
93
/* Error */
94
}
95
fclose(fh);
96
return 0;
97
}
98
99
static boolean
100
get_nic_bytes(const char *fn, uint64_t *bytes)
101
{
102
if (get_file_value(fn, bytes) < 0)
103
return FALSE;
104
105
return TRUE;
106
}
107
108
static void
109
query_wifi_bitrate(const struct nic_info *nic, uint64_t *bitrate)
110
{
111
int sockfd;
112
struct iw_statistics stats;
113
struct iwreq req;
114
115
memset(&stats, 0, sizeof(stats));
116
memset(&req, 0, sizeof(req));
117
118
snprintf(req.ifr_name, sizeof(req.ifr_name), "%s", nic->name);
119
req.u.data.pointer = &stats;
120
req.u.data.flags = 1;
121
req.u.data.length = sizeof(struct iw_statistics);
122
123
/* Any old socket will do, and a datagram socket is pretty cheap */
124
if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
125
fprintf(stderr, "Unable to create socket for %s\n", nic->name);
126
return;
127
}
128
129
if (ioctl(sockfd, SIOCGIWRATE, &req) == -1) {
130
fprintf(stderr, "Error performing SIOCGIWSTATS on %s\n", nic->name);
131
close(sockfd);
132
return;
133
}
134
*bitrate = req.u.bitrate.value;
135
136
close(sockfd);
137
}
138
139
static void
140
query_nic_rssi(const struct nic_info *nic, uint64_t *leveldBm)
141
{
142
int sockfd;
143
struct iw_statistics stats;
144
struct iwreq req;
145
146
memset(&stats, 0, sizeof(stats));
147
memset(&req, 0, sizeof(req));
148
149
snprintf(req.ifr_name, sizeof(req.ifr_name), "%s", nic->name);
150
req.u.data.pointer = &stats;
151
req.u.data.flags = 1;
152
req.u.data.length = sizeof(struct iw_statistics);
153
154
if (nic->mode != NIC_RSSI_DBM)
155
return;
156
157
/* Any old socket will do, and a datagram socket is pretty cheap */
158
if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
159
fprintf(stderr, "Unable to create socket for %s\n", nic->name);
160
return;
161
}
162
163
/* Perform the ioctl */
164
if (ioctl(sockfd, SIOCGIWSTATS, &req) == -1) {
165
fprintf(stderr, "Error performing SIOCGIWSTATS on %s\n", nic->name);
166
close(sockfd);
167
return;
168
}
169
*leveldBm = ((char) stats.qual.level * -1);
170
171
close(sockfd);
172
}
173
174
static void
175
query_nic_load(struct hud_graph *gr, struct pipe_context *pipe)
176
{
177
/* The framework calls us at a regular but indefined period,
178
* not once per second, compensate the statistics accordingly.
179
*/
180
181
struct nic_info *nic = gr->query_data;
182
uint64_t now = os_time_get();
183
184
if (nic->last_time) {
185
if (nic->last_time + gr->pane->period <= now) {
186
switch (nic->mode) {
187
case NIC_DIRECTION_RX:
188
case NIC_DIRECTION_TX:
189
{
190
uint64_t bytes;
191
get_nic_bytes(nic->throughput_filename, &bytes);
192
uint64_t nic_mbps =
193
((bytes - nic->last_nic_bytes) / 1000000) * 8;
194
195
float speedMbps = nic->speedMbps;
196
float periodMs = gr->pane->period / 1000.0;
197
float bits = nic_mbps;
198
float period_factor = periodMs / 1000;
199
float period_speed = speedMbps * period_factor;
200
float pct = (bits / period_speed) * 100;
201
202
/* Scaling bps with a narrow time period into a second,
203
* potentially suffers from rounding errors at higher
204
* periods. Eg 104%. Compensate.
205
*/
206
if (pct > 100)
207
pct = 100;
208
hud_graph_add_value(gr, (uint64_t) pct);
209
210
nic->last_nic_bytes = bytes;
211
}
212
break;
213
case NIC_RSSI_DBM:
214
{
215
uint64_t leveldBm = 0;
216
query_nic_rssi(nic, &leveldBm);
217
hud_graph_add_value(gr, leveldBm);
218
}
219
break;
220
}
221
222
nic->last_time = now;
223
}
224
}
225
else {
226
/* initialize */
227
switch (nic->mode) {
228
case NIC_DIRECTION_RX:
229
case NIC_DIRECTION_TX:
230
get_nic_bytes(nic->throughput_filename, &nic->last_nic_bytes);
231
break;
232
case NIC_RSSI_DBM:
233
break;
234
}
235
236
nic->last_time = now;
237
}
238
}
239
240
/**
241
* Create and initialize a new object for a specific network interface dev.
242
* \param pane parent context.
243
* \param nic_name logical block device name, EG. eth0.
244
* \param mode query type (NIC_DIRECTION_RX/WR/RSSI) statistics.
245
*/
246
void
247
hud_nic_graph_install(struct hud_pane *pane, const char *nic_name,
248
unsigned int mode)
249
{
250
struct hud_graph *gr;
251
struct nic_info *nic;
252
253
int num_nics = hud_get_num_nics(0);
254
if (num_nics <= 0)
255
return;
256
257
nic = find_nic_by_name(nic_name, mode);
258
if (!nic)
259
return;
260
261
gr = CALLOC_STRUCT(hud_graph);
262
if (!gr)
263
return;
264
265
nic->mode = mode;
266
if (nic->mode == NIC_DIRECTION_RX) {
267
snprintf(gr->name, sizeof(gr->name), "%s-rx-%"PRId64"Mbps", nic->name,
268
nic->speedMbps);
269
}
270
else if (nic->mode == NIC_DIRECTION_TX) {
271
snprintf(gr->name, sizeof(gr->name), "%s-tx-%"PRId64"Mbps", nic->name,
272
nic->speedMbps);
273
}
274
else if (nic->mode == NIC_RSSI_DBM)
275
snprintf(gr->name, sizeof(gr->name), "%s-rssi", nic->name);
276
else {
277
free(gr);
278
return;
279
}
280
281
gr->query_data = nic;
282
gr->query_new_value = query_nic_load;
283
284
hud_pane_add_graph(pane, gr);
285
hud_pane_set_max_value(pane, 100);
286
}
287
288
static int
289
is_wireless_nic(const char *dirbase)
290
{
291
struct stat stat_buf;
292
293
/* Check if its a wireless card */
294
char fn[256];
295
snprintf(fn, sizeof(fn), "%s/wireless", dirbase);
296
if (stat(fn, &stat_buf) == 0)
297
return 1;
298
299
return 0;
300
}
301
302
static void
303
query_nic_bitrate(struct nic_info *nic, const char *dirbase)
304
{
305
struct stat stat_buf;
306
307
/* Check if its a wireless card */
308
char fn[256];
309
snprintf(fn, sizeof(fn), "%s/wireless", dirbase);
310
if (stat(fn, &stat_buf) == 0) {
311
/* we're a wireless nic */
312
query_wifi_bitrate(nic, &nic->speedMbps);
313
nic->speedMbps /= 1000000;
314
}
315
else {
316
/* Must be a wired nic */
317
snprintf(fn, sizeof(fn), "%s/speed", dirbase);
318
get_file_value(fn, &nic->speedMbps);
319
}
320
}
321
322
/**
323
* Initialize internal object arrays and display NIC HUD help.
324
* \param displayhelp true if the list of detected devices should be
325
displayed on the console.
326
* \return number of detected network interface devices.
327
*/
328
int
329
hud_get_num_nics(bool displayhelp)
330
{
331
struct dirent *dp;
332
struct stat stat_buf;
333
struct nic_info *nic;
334
char name[64];
335
336
/* Return the number if network interfaces. */
337
mtx_lock(&gnic_mutex);
338
if (gnic_count) {
339
mtx_unlock(&gnic_mutex);
340
return gnic_count;
341
}
342
343
/* Scan /sys/block, for every object type we support, create and
344
* persist an object to represent its different statistics.
345
*/
346
list_inithead(&gnic_list);
347
DIR *dir = opendir("/sys/class/net/");
348
if (!dir) {
349
mtx_unlock(&gnic_mutex);
350
return 0;
351
}
352
353
while ((dp = readdir(dir)) != NULL) {
354
355
/* Avoid 'lo' and '..' and '.' */
356
if (strlen(dp->d_name) <= 2)
357
continue;
358
359
char basename[256];
360
snprintf(basename, sizeof(basename), "/sys/class/net/%s", dp->d_name);
361
snprintf(name, sizeof(name), "%s/statistics/rx_bytes", basename);
362
if (stat(name, &stat_buf) < 0)
363
continue;
364
365
if (!S_ISREG(stat_buf.st_mode))
366
continue; /* Not a regular file */
367
368
int is_wireless = is_wireless_nic(basename);
369
370
/* Add the RX object */
371
nic = CALLOC_STRUCT(nic_info);
372
strcpy(nic->name, dp->d_name);
373
snprintf(nic->throughput_filename, sizeof(nic->throughput_filename),
374
"%s/statistics/rx_bytes", basename);
375
nic->mode = NIC_DIRECTION_RX;
376
nic->is_wireless = is_wireless;
377
query_nic_bitrate(nic, basename);
378
379
list_addtail(&nic->list, &gnic_list);
380
gnic_count++;
381
382
/* Add the TX object */
383
nic = CALLOC_STRUCT(nic_info);
384
strcpy(nic->name, dp->d_name);
385
snprintf(nic->throughput_filename,
386
sizeof(nic->throughput_filename),
387
"/sys/class/net/%s/statistics/tx_bytes", dp->d_name);
388
nic->mode = NIC_DIRECTION_TX;
389
nic->is_wireless = is_wireless;
390
391
query_nic_bitrate(nic, basename);
392
393
list_addtail(&nic->list, &gnic_list);
394
gnic_count++;
395
396
if (nic->is_wireless) {
397
/* RSSI Support */
398
nic = CALLOC_STRUCT(nic_info);
399
strcpy(nic->name, dp->d_name);
400
snprintf(nic->throughput_filename,
401
sizeof(nic->throughput_filename),
402
"/sys/class/net/%s/statistics/tx_bytes", dp->d_name);
403
nic->mode = NIC_RSSI_DBM;
404
405
query_nic_bitrate(nic, basename);
406
407
list_addtail(&nic->list, &gnic_list);
408
gnic_count++;
409
}
410
411
}
412
closedir(dir);
413
414
list_for_each_entry(struct nic_info, nic, &gnic_list, list) {
415
char line[64];
416
snprintf(line, sizeof(line), " nic-%s-%s",
417
nic->mode == NIC_DIRECTION_RX ? "rx" :
418
nic->mode == NIC_DIRECTION_TX ? "tx" :
419
nic->mode == NIC_RSSI_DBM ? "rssi" : "undefined", nic->name);
420
421
puts(line);
422
423
}
424
425
mtx_unlock(&gnic_mutex);
426
return gnic_count;
427
}
428
429
#endif /* HAVE_GALLIUM_EXTRA_HUD */
430
431