1 /*
2 * ethtool.c: Linux ethernet device configuration tool.
3 *
4 * Copyright (C) 1998 David S. Miller ([email protected])
5 * Portions Copyright 2001 Sun Microsystems
6 * Kernel 2.4 update Copyright 2001 Jeff Garzik <[email protected]>
7 * Wake-on-LAN,natsemi,misc support by Tim Hockin <[email protected]>
8 * Portions Copyright 2002 Intel
9 * Portions Copyright (C) Sun Microsystems 2008
10 * do_test support by Eli Kupermann <[email protected]>
11 * ETHTOOL_PHYS_ID support by Chris Leech <[email protected]>
12 * e1000 support by Scott Feldman <[email protected]>
13 * e100 support by Wen Tao <[email protected]>
14 * ixgb support by Nicholas Nunley <[email protected]>
15 * amd8111e support by Reeja John <[email protected]>
16 * long arguments by Andi Kleen.
17 * SMSC LAN911x support by Steve Glendinning <[email protected]>
18 * Rx Network Flow Control configuration support <[email protected]>
19 * Various features by Ben Hutchings <[email protected]>;
20 * Copyright 2009, 2010 Solarflare Communications
21 * MDI-X set support by Jesse Brandeburg <[email protected]>
22 * Copyright 2012 Intel Corporation
23 * vmxnet3 support by Shrikrishna Khare <[email protected]>
24 * Various features by Ben Hutchings <[email protected]>;
25 * Copyright 2008-2010, 2013-2016 Ben Hutchings
26 * QSFP+/QSFP28 DOM support by Vidya Sagar Ravipati <[email protected]>
27 *
28 * TODO:
29 * * show settings for all devices
30 */
31
32 #include "internal.h"
33 #include <string.h>
34 #include <strings.h>
35 #include <stdlib.h>
36 #include <sys/stat.h>
37 #include <stdio.h>
38 #include <stddef.h>
39 #include <stdbool.h>
40 #include <errno.h>
41 #include <sys/utsname.h>
42 #include <limits.h>
43 #include <ctype.h>
44 #include <inttypes.h>
45
46 #include <sys/socket.h>
47 #include <netinet/in.h>
48 #include <arpa/inet.h>
49
50 #include <linux/ioctl.h>
51 #include <linux/sockios.h>
52 #include <linux/netlink.h>
53
54 #include "common.h"
55 #include "netlink/extapi.h"
56
57 #ifndef MAX_ADDR_LEN
58 #define MAX_ADDR_LEN 32
59 #endif
60
61 #define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
62
63 static void exit_bad_args(void) __attribute__((noreturn));
64
exit_bad_args(void)65 static void exit_bad_args(void)
66 {
67 fprintf(stderr,
68 "ethtool: bad command line argument(s)\n"
69 "For more information run ethtool -h\n");
70 exit(1);
71 }
72
73 static void exit_nlonly_param(const char *name) __attribute__((noreturn));
74
exit_nlonly_param(const char * name)75 static void exit_nlonly_param(const char *name)
76 {
77 fprintf(stderr,
78 "ethtool: parameter '%s' can be used only with netlink\n",
79 name);
80 exit(1);
81 }
82
83 typedef enum {
84 CMDL_NONE,
85 CMDL_BOOL,
86 CMDL_S32,
87 CMDL_U8,
88 CMDL_U16,
89 CMDL_U32,
90 CMDL_U64,
91 CMDL_BE16,
92 CMDL_IP4,
93 CMDL_STR,
94 CMDL_FLAG,
95 CMDL_MAC,
96 } cmdline_type_t;
97
98 struct cmdline_info {
99 const char *name;
100 cmdline_type_t type;
101 /* Points to int (BOOL), s32, u16, u32 (U32/FLAG/IP4), u64,
102 * char * (STR) or u8[6] (MAC). For FLAG, the value accumulates
103 * all flags to be set. */
104 void *wanted_val;
105 void *ioctl_val;
106 /* For FLAG, the flag value to be set/cleared */
107 u32 flag_val;
108 /* For FLAG, points to u32 and accumulates all flags seen.
109 * For anything else, points to int and is set if the option is
110 * seen. */
111 void *seen_val;
112 };
113
114 struct feature_def {
115 char name[ETH_GSTRING_LEN];
116 int off_flag_index; /* index in off_flag_def; negative if none match */
117 };
118
119 struct feature_defs {
120 size_t n_features;
121 /* Number of features each offload flag is associated with */
122 unsigned int off_flag_matched[OFF_FLAG_DEF_SIZE];
123 /* Name and offload flag index for each feature */
124 struct feature_def def[0];
125 };
126
127 #define FEATURE_BITS_TO_BLOCKS(n_bits) DIV_ROUND_UP(n_bits, 32U)
128 #define FEATURE_WORD(blocks, index, field) ((blocks)[(index) / 32U].field)
129 #define FEATURE_FIELD_FLAG(index) (1U << (index) % 32U)
130 #define FEATURE_BIT_SET(blocks, index, field) \
131 (FEATURE_WORD(blocks, index, field) |= FEATURE_FIELD_FLAG(index))
132 #define FEATURE_BIT_CLEAR(blocks, index, field) \
133 (FEATURE_WORD(blocks, index, filed) &= ~FEATURE_FIELD_FLAG(index))
134 #define FEATURE_BIT_IS_SET(blocks, index, field) \
135 (FEATURE_WORD(blocks, index, field) & FEATURE_FIELD_FLAG(index))
136
137 static long long
get_int_range(char * str,int base,long long min,long long max)138 get_int_range(char *str, int base, long long min, long long max)
139 {
140 long long v;
141 char *endp;
142
143 if (!str)
144 exit_bad_args();
145 errno = 0;
146 v = strtoll(str, &endp, base);
147 if (errno || *endp || v < min || v > max)
148 exit_bad_args();
149 return v;
150 }
151
152 static unsigned long long
get_uint_range(char * str,int base,unsigned long long max)153 get_uint_range(char *str, int base, unsigned long long max)
154 {
155 unsigned long long v;
156 char *endp;
157
158 if (!str)
159 exit_bad_args();
160 errno = 0;
161 v = strtoull(str, &endp, base);
162 if (errno || *endp || v > max)
163 exit_bad_args();
164 return v;
165 }
166
get_int(char * str,int base)167 static int get_int(char *str, int base)
168 {
169 return get_int_range(str, base, INT_MIN, INT_MAX);
170 }
171
get_u32(char * str,int base)172 static u32 get_u32(char *str, int base)
173 {
174 return get_uint_range(str, base, 0xffffffff);
175 }
176
get_mac_addr(char * src,unsigned char * dest)177 static void get_mac_addr(char *src, unsigned char *dest)
178 {
179 int count;
180 int i;
181 int buf[ETH_ALEN];
182
183 count = sscanf(src, "%2x:%2x:%2x:%2x:%2x:%2x",
184 &buf[0], &buf[1], &buf[2], &buf[3], &buf[4], &buf[5]);
185 if (count != ETH_ALEN)
186 exit_bad_args();
187
188 for (i = 0; i < count; i++)
189 dest[i] = buf[i];
190 }
191
parse_hex_u32_bitmap(const char * s,unsigned int nbits,u32 * result)192 static int parse_hex_u32_bitmap(const char *s,
193 unsigned int nbits, u32 *result)
194 {
195 const unsigned int nwords = __KERNEL_DIV_ROUND_UP(nbits, 32);
196 size_t slen = strlen(s);
197 size_t i;
198
199 /* ignore optional '0x' prefix */
200 if ((slen > 2) && (strncasecmp(s, "0x", 2) == 0)) {
201 slen -= 2;
202 s += 2;
203 }
204
205 if (slen > 8 * nwords) /* up to 2 digits per byte */
206 return -1;
207
208 memset(result, 0, 4 * nwords);
209 for (i = 0; i < slen; ++i) {
210 const unsigned int shift = (slen - 1 - i) * 4;
211 u32 *dest = &result[shift / 32];
212 u32 nibble;
213
214 if ('a' <= s[i] && s[i] <= 'f')
215 nibble = 0xa + (s[i] - 'a');
216 else if ('A' <= s[i] && s[i] <= 'F')
217 nibble = 0xa + (s[i] - 'A');
218 else if ('0' <= s[i] && s[i] <= '9')
219 nibble = (s[i] - '0');
220 else
221 return -1;
222
223 *dest |= (nibble << (shift % 32));
224 }
225
226 return 0;
227 }
228
parse_generic_cmdline(struct cmd_context * ctx,int * changed,struct cmdline_info * info,unsigned int n_info)229 static void parse_generic_cmdline(struct cmd_context *ctx,
230 int *changed,
231 struct cmdline_info *info,
232 unsigned int n_info)
233 {
234 unsigned int argc = ctx->argc;
235 char **argp = ctx->argp;
236 unsigned int i, idx;
237 int found;
238
239 for (i = 0; i < argc; i++) {
240 found = 0;
241 for (idx = 0; idx < n_info; idx++) {
242 if (!strcmp(info[idx].name, argp[i])) {
243 found = 1;
244 *changed = 1;
245 if (info[idx].type != CMDL_FLAG &&
246 info[idx].seen_val)
247 *(int *)info[idx].seen_val = 1;
248 i += 1;
249 if (i >= argc)
250 exit_bad_args();
251 switch (info[idx].type) {
252 case CMDL_BOOL: {
253 int *p = info[idx].wanted_val;
254 if (!strcmp(argp[i], "on"))
255 *p = 1;
256 else if (!strcmp(argp[i], "off"))
257 *p = 0;
258 else
259 exit_bad_args();
260 break;
261 }
262 case CMDL_S32: {
263 s32 *p = info[idx].wanted_val;
264 *p = get_int_range(argp[i], 0,
265 -0x80000000LL,
266 0x7fffffff);
267 break;
268 }
269 case CMDL_U8: {
270 u8 *p = info[idx].wanted_val;
271 *p = get_uint_range(argp[i], 0, 0xff);
272 break;
273 }
274 case CMDL_U16: {
275 u16 *p = info[idx].wanted_val;
276 *p = get_uint_range(argp[i], 0, 0xffff);
277 break;
278 }
279 case CMDL_U32: {
280 u32 *p = info[idx].wanted_val;
281 *p = get_uint_range(argp[i], 0,
282 0xffffffff);
283 break;
284 }
285 case CMDL_U64: {
286 u64 *p = info[idx].wanted_val;
287 *p = get_uint_range(
288 argp[i], 0,
289 0xffffffffffffffffLL);
290 break;
291 }
292 case CMDL_BE16: {
293 u16 *p = info[idx].wanted_val;
294 *p = cpu_to_be16(
295 get_uint_range(argp[i], 0,
296 0xffff));
297 break;
298 }
299 case CMDL_IP4: {
300 u32 *p = info[idx].wanted_val;
301 struct in_addr in;
302 if (!inet_pton(AF_INET, argp[i], &in))
303 exit_bad_args();
304 *p = in.s_addr;
305 break;
306 }
307 case CMDL_MAC:
308 get_mac_addr(argp[i],
309 info[idx].wanted_val);
310 break;
311 case CMDL_FLAG: {
312 u32 *p;
313 p = info[idx].seen_val;
314 *p |= info[idx].flag_val;
315 if (!strcmp(argp[i], "on")) {
316 p = info[idx].wanted_val;
317 *p |= info[idx].flag_val;
318 } else if (strcmp(argp[i], "off")) {
319 exit_bad_args();
320 }
321 break;
322 }
323 case CMDL_STR: {
324 char **s = info[idx].wanted_val;
325 *s = strdup(argp[i]);
326 break;
327 }
328 default:
329 exit_bad_args();
330 }
331 break;
332 }
333 }
334 if (!found)
335 exit_bad_args();
336 }
337 }
338
flag_to_cmdline_info(const char * name,u32 value,u32 * wanted,u32 * mask,struct cmdline_info * cli)339 static void flag_to_cmdline_info(const char *name, u32 value,
340 u32 *wanted, u32 *mask,
341 struct cmdline_info *cli)
342 {
343 memset(cli, 0, sizeof(*cli));
344 cli->name = name;
345 cli->type = CMDL_FLAG;
346 cli->flag_val = value;
347 cli->wanted_val = wanted;
348 cli->seen_val = mask;
349 }
350
rxflow_str_to_type(const char * str)351 static int rxflow_str_to_type(const char *str)
352 {
353 int flow_type = 0;
354
355 if (!strcmp(str, "tcp4"))
356 flow_type = TCP_V4_FLOW;
357 else if (!strcmp(str, "udp4"))
358 flow_type = UDP_V4_FLOW;
359 else if (!strcmp(str, "ah4") || !strcmp(str, "esp4"))
360 flow_type = AH_ESP_V4_FLOW;
361 else if (!strcmp(str, "sctp4"))
362 flow_type = SCTP_V4_FLOW;
363 else if (!strcmp(str, "tcp6"))
364 flow_type = TCP_V6_FLOW;
365 else if (!strcmp(str, "udp6"))
366 flow_type = UDP_V6_FLOW;
367 else if (!strcmp(str, "ah6") || !strcmp(str, "esp6"))
368 flow_type = AH_ESP_V6_FLOW;
369 else if (!strcmp(str, "sctp6"))
370 flow_type = SCTP_V6_FLOW;
371 else if (!strcmp(str, "ether"))
372 flow_type = ETHER_FLOW;
373
374 return flow_type;
375 }
376
do_version(struct cmd_context * ctx __maybe_unused)377 static int do_version(struct cmd_context *ctx __maybe_unused)
378 {
379 fprintf(stdout,
380 PACKAGE " version " VERSION
381 #ifndef ETHTOOL_ENABLE_PRETTY_DUMP
382 " (pretty dumps disabled)"
383 #endif
384 "\n");
385 return 0;
386 }
387
388 /* link mode routines */
389
390 static ETHTOOL_DECLARE_LINK_MODE_MASK(all_advertised_modes);
391 static ETHTOOL_DECLARE_LINK_MODE_MASK(all_advertised_flags);
392
init_global_link_mode_masks(void)393 static void init_global_link_mode_masks(void)
394 {
395 static const enum ethtool_link_mode_bit_indices
396 all_advertised_modes_bits[] = {
397 ETHTOOL_LINK_MODE_10baseT_Half_BIT,
398 ETHTOOL_LINK_MODE_10baseT_Full_BIT,
399 ETHTOOL_LINK_MODE_100baseT_Half_BIT,
400 ETHTOOL_LINK_MODE_100baseT_Full_BIT,
401 ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
402 ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
403 ETHTOOL_LINK_MODE_10000baseT_Full_BIT,
404 ETHTOOL_LINK_MODE_2500baseX_Full_BIT,
405 ETHTOOL_LINK_MODE_1000baseKX_Full_BIT,
406 ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT,
407 ETHTOOL_LINK_MODE_10000baseKR_Full_BIT,
408 ETHTOOL_LINK_MODE_10000baseR_FEC_BIT,
409 ETHTOOL_LINK_MODE_20000baseMLD2_Full_BIT,
410 ETHTOOL_LINK_MODE_20000baseKR2_Full_BIT,
411 ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT,
412 ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT,
413 ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT,
414 ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT,
415 ETHTOOL_LINK_MODE_56000baseKR4_Full_BIT,
416 ETHTOOL_LINK_MODE_56000baseCR4_Full_BIT,
417 ETHTOOL_LINK_MODE_56000baseSR4_Full_BIT,
418 ETHTOOL_LINK_MODE_56000baseLR4_Full_BIT,
419 ETHTOOL_LINK_MODE_25000baseCR_Full_BIT,
420 ETHTOOL_LINK_MODE_25000baseKR_Full_BIT,
421 ETHTOOL_LINK_MODE_25000baseSR_Full_BIT,
422 ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT,
423 ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT,
424 ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT,
425 ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT,
426 ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT,
427 ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT,
428 ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT,
429 ETHTOOL_LINK_MODE_1000baseX_Full_BIT,
430 ETHTOOL_LINK_MODE_10000baseCR_Full_BIT,
431 ETHTOOL_LINK_MODE_10000baseSR_Full_BIT,
432 ETHTOOL_LINK_MODE_10000baseLR_Full_BIT,
433 ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT,
434 ETHTOOL_LINK_MODE_10000baseER_Full_BIT,
435 ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
436 ETHTOOL_LINK_MODE_5000baseT_Full_BIT,
437 ETHTOOL_LINK_MODE_50000baseKR_Full_BIT,
438 ETHTOOL_LINK_MODE_50000baseSR_Full_BIT,
439 ETHTOOL_LINK_MODE_50000baseCR_Full_BIT,
440 ETHTOOL_LINK_MODE_50000baseLR_ER_FR_Full_BIT,
441 ETHTOOL_LINK_MODE_50000baseDR_Full_BIT,
442 ETHTOOL_LINK_MODE_100000baseKR2_Full_BIT,
443 ETHTOOL_LINK_MODE_100000baseSR2_Full_BIT,
444 ETHTOOL_LINK_MODE_100000baseCR2_Full_BIT,
445 ETHTOOL_LINK_MODE_100000baseLR2_ER2_FR2_Full_BIT,
446 ETHTOOL_LINK_MODE_100000baseDR2_Full_BIT,
447 ETHTOOL_LINK_MODE_200000baseKR4_Full_BIT,
448 ETHTOOL_LINK_MODE_200000baseSR4_Full_BIT,
449 ETHTOOL_LINK_MODE_200000baseLR4_ER4_FR4_Full_BIT,
450 ETHTOOL_LINK_MODE_200000baseDR4_Full_BIT,
451 ETHTOOL_LINK_MODE_200000baseCR4_Full_BIT,
452 ETHTOOL_LINK_MODE_100baseT1_Full_BIT,
453 ETHTOOL_LINK_MODE_1000baseT1_Full_BIT,
454 ETHTOOL_LINK_MODE_400000baseKR8_Full_BIT,
455 ETHTOOL_LINK_MODE_400000baseSR8_Full_BIT,
456 ETHTOOL_LINK_MODE_400000baseLR8_ER8_FR8_Full_BIT,
457 ETHTOOL_LINK_MODE_400000baseDR8_Full_BIT,
458 ETHTOOL_LINK_MODE_400000baseCR8_Full_BIT,
459 ETHTOOL_LINK_MODE_100000baseKR_Full_BIT,
460 ETHTOOL_LINK_MODE_100000baseSR_Full_BIT,
461 ETHTOOL_LINK_MODE_100000baseLR_ER_FR_Full_BIT,
462 ETHTOOL_LINK_MODE_100000baseCR_Full_BIT,
463 ETHTOOL_LINK_MODE_100000baseDR_Full_BIT,
464 ETHTOOL_LINK_MODE_200000baseKR2_Full_BIT,
465 ETHTOOL_LINK_MODE_200000baseSR2_Full_BIT,
466 ETHTOOL_LINK_MODE_200000baseLR2_ER2_FR2_Full_BIT,
467 ETHTOOL_LINK_MODE_200000baseDR2_Full_BIT,
468 ETHTOOL_LINK_MODE_200000baseCR2_Full_BIT,
469 ETHTOOL_LINK_MODE_400000baseKR4_Full_BIT,
470 ETHTOOL_LINK_MODE_400000baseSR4_Full_BIT,
471 ETHTOOL_LINK_MODE_400000baseLR4_ER4_FR4_Full_BIT,
472 ETHTOOL_LINK_MODE_400000baseDR4_Full_BIT,
473 ETHTOOL_LINK_MODE_400000baseCR4_Full_BIT,
474 ETHTOOL_LINK_MODE_100baseFX_Half_BIT,
475 ETHTOOL_LINK_MODE_100baseFX_Full_BIT,
476 ETHTOOL_LINK_MODE_10baseT1L_Full_BIT,
477 ETHTOOL_LINK_MODE_800000baseCR8_Full_BIT,
478 ETHTOOL_LINK_MODE_800000baseKR8_Full_BIT,
479 ETHTOOL_LINK_MODE_800000baseDR8_Full_BIT,
480 ETHTOOL_LINK_MODE_800000baseDR8_2_Full_BIT,
481 ETHTOOL_LINK_MODE_800000baseSR8_Full_BIT,
482 ETHTOOL_LINK_MODE_800000baseVR8_Full_BIT,
483 ETHTOOL_LINK_MODE_10baseT1S_Full_BIT,
484 ETHTOOL_LINK_MODE_10baseT1S_Half_BIT,
485 ETHTOOL_LINK_MODE_10baseT1S_P2MP_Half_BIT,
486 };
487 static const enum ethtool_link_mode_bit_indices
488 additional_advertised_flags_bits[] = {
489 ETHTOOL_LINK_MODE_Autoneg_BIT,
490 ETHTOOL_LINK_MODE_TP_BIT,
491 ETHTOOL_LINK_MODE_AUI_BIT,
492 ETHTOOL_LINK_MODE_MII_BIT,
493 ETHTOOL_LINK_MODE_FIBRE_BIT,
494 ETHTOOL_LINK_MODE_BNC_BIT,
495 ETHTOOL_LINK_MODE_Pause_BIT,
496 ETHTOOL_LINK_MODE_Asym_Pause_BIT,
497 ETHTOOL_LINK_MODE_Backplane_BIT,
498 ETHTOOL_LINK_MODE_FEC_NONE_BIT,
499 ETHTOOL_LINK_MODE_FEC_RS_BIT,
500 ETHTOOL_LINK_MODE_FEC_BASER_BIT,
501 ETHTOOL_LINK_MODE_FEC_LLRS_BIT,
502 };
503 unsigned int i;
504
505 ethtool_link_mode_zero(all_advertised_modes);
506 ethtool_link_mode_zero(all_advertised_flags);
507 for (i = 0; i < ARRAY_SIZE(all_advertised_modes_bits); ++i) {
508 ethtool_link_mode_set_bit(all_advertised_modes_bits[i],
509 all_advertised_modes);
510 ethtool_link_mode_set_bit(all_advertised_modes_bits[i],
511 all_advertised_flags);
512 }
513
514 for (i = 0; i < ARRAY_SIZE(additional_advertised_flags_bits); ++i) {
515 ethtool_link_mode_set_bit(
516 additional_advertised_flags_bits[i],
517 all_advertised_flags);
518 }
519 }
520
521 static void dump_link_caps(const char *prefix, const char *an_prefix,
522 const u32 *mask, int link_mode_only);
523
dump_supported(const struct ethtool_link_usettings * link_usettings)524 static void dump_supported(const struct ethtool_link_usettings *link_usettings)
525 {
526 fprintf(stdout, " Supported ports: [ ");
527 if (ethtool_link_mode_test_bit(
528 ETHTOOL_LINK_MODE_TP_BIT,
529 link_usettings->link_modes.supported))
530 fprintf(stdout, "TP ");
531 if (ethtool_link_mode_test_bit(
532 ETHTOOL_LINK_MODE_AUI_BIT,
533 link_usettings->link_modes.supported))
534 fprintf(stdout, "AUI ");
535 if (ethtool_link_mode_test_bit(
536 ETHTOOL_LINK_MODE_BNC_BIT,
537 link_usettings->link_modes.supported))
538 fprintf(stdout, "BNC ");
539 if (ethtool_link_mode_test_bit(
540 ETHTOOL_LINK_MODE_MII_BIT,
541 link_usettings->link_modes.supported))
542 fprintf(stdout, "MII ");
543 if (ethtool_link_mode_test_bit(
544 ETHTOOL_LINK_MODE_FIBRE_BIT,
545 link_usettings->link_modes.supported))
546 fprintf(stdout, "FIBRE ");
547 if (ethtool_link_mode_test_bit(
548 ETHTOOL_LINK_MODE_Backplane_BIT,
549 link_usettings->link_modes.supported))
550 fprintf(stdout, "Backplane ");
551 fprintf(stdout, "]\n");
552
553 dump_link_caps("Supported", "Supports",
554 link_usettings->link_modes.supported, 0);
555 }
556
557 /* Print link capability flags (supported, advertised or lp_advertised).
558 * Assumes that the corresponding SUPPORTED and ADVERTISED flags are equal.
559 */
dump_link_caps(const char * prefix,const char * an_prefix,const u32 * mask,int link_mode_only)560 static void dump_link_caps(const char *prefix, const char *an_prefix,
561 const u32 *mask, int link_mode_only)
562 {
563 static const struct {
564 int same_line; /* print on same line as previous */
565 unsigned int bit_index;
566 const char *name;
567 } mode_defs[] = {
568 { 0, ETHTOOL_LINK_MODE_10baseT_Half_BIT,
569 "10baseT/Half" },
570 { 1, ETHTOOL_LINK_MODE_10baseT_Full_BIT,
571 "10baseT/Full" },
572 { 0, ETHTOOL_LINK_MODE_100baseT_Half_BIT,
573 "100baseT/Half" },
574 { 1, ETHTOOL_LINK_MODE_100baseT_Full_BIT,
575 "100baseT/Full" },
576 { 0, ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
577 "1000baseT/Half" },
578 { 1, ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
579 "1000baseT/Full" },
580 { 0, ETHTOOL_LINK_MODE_10000baseT_Full_BIT,
581 "10000baseT/Full" },
582 { 0, ETHTOOL_LINK_MODE_2500baseX_Full_BIT,
583 "2500baseX/Full" },
584 { 0, ETHTOOL_LINK_MODE_1000baseKX_Full_BIT,
585 "1000baseKX/Full" },
586 { 0, ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT,
587 "10000baseKX4/Full" },
588 { 0, ETHTOOL_LINK_MODE_10000baseKR_Full_BIT,
589 "10000baseKR/Full" },
590 { 0, ETHTOOL_LINK_MODE_10000baseR_FEC_BIT,
591 "10000baseR_FEC" },
592 { 0, ETHTOOL_LINK_MODE_20000baseMLD2_Full_BIT,
593 "20000baseMLD2/Full" },
594 { 0, ETHTOOL_LINK_MODE_20000baseKR2_Full_BIT,
595 "20000baseKR2/Full" },
596 { 0, ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT,
597 "40000baseKR4/Full" },
598 { 0, ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT,
599 "40000baseCR4/Full" },
600 { 0, ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT,
601 "40000baseSR4/Full" },
602 { 0, ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT,
603 "40000baseLR4/Full" },
604 { 0, ETHTOOL_LINK_MODE_56000baseKR4_Full_BIT,
605 "56000baseKR4/Full" },
606 { 0, ETHTOOL_LINK_MODE_56000baseCR4_Full_BIT,
607 "56000baseCR4/Full" },
608 { 0, ETHTOOL_LINK_MODE_56000baseSR4_Full_BIT,
609 "56000baseSR4/Full" },
610 { 0, ETHTOOL_LINK_MODE_56000baseLR4_Full_BIT,
611 "56000baseLR4/Full" },
612 { 0, ETHTOOL_LINK_MODE_25000baseCR_Full_BIT,
613 "25000baseCR/Full" },
614 { 0, ETHTOOL_LINK_MODE_25000baseKR_Full_BIT,
615 "25000baseKR/Full" },
616 { 0, ETHTOOL_LINK_MODE_25000baseSR_Full_BIT,
617 "25000baseSR/Full" },
618 { 0, ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT,
619 "50000baseCR2/Full" },
620 { 0, ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT,
621 "50000baseKR2/Full" },
622 { 0, ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT,
623 "100000baseKR4/Full" },
624 { 0, ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT,
625 "100000baseSR4/Full" },
626 { 0, ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT,
627 "100000baseCR4/Full" },
628 { 0, ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT,
629 "100000baseLR4_ER4/Full" },
630 { 0, ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT,
631 "50000baseSR2/Full" },
632 { 0, ETHTOOL_LINK_MODE_1000baseX_Full_BIT,
633 "1000baseX/Full" },
634 { 0, ETHTOOL_LINK_MODE_10000baseCR_Full_BIT,
635 "10000baseCR/Full" },
636 { 0, ETHTOOL_LINK_MODE_10000baseSR_Full_BIT,
637 "10000baseSR/Full" },
638 { 0, ETHTOOL_LINK_MODE_10000baseLR_Full_BIT,
639 "10000baseLR/Full" },
640 { 0, ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT,
641 "10000baseLRM/Full" },
642 { 0, ETHTOOL_LINK_MODE_10000baseER_Full_BIT,
643 "10000baseER/Full" },
644 { 0, ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
645 "2500baseT/Full" },
646 { 0, ETHTOOL_LINK_MODE_5000baseT_Full_BIT,
647 "5000baseT/Full" },
648 { 0, ETHTOOL_LINK_MODE_50000baseKR_Full_BIT,
649 "50000baseKR/Full" },
650 { 0, ETHTOOL_LINK_MODE_50000baseSR_Full_BIT,
651 "50000baseSR/Full" },
652 { 0, ETHTOOL_LINK_MODE_50000baseCR_Full_BIT,
653 "50000baseCR/Full" },
654 { 0, ETHTOOL_LINK_MODE_50000baseLR_ER_FR_Full_BIT,
655 "50000baseLR_ER_FR/Full" },
656 { 0, ETHTOOL_LINK_MODE_50000baseDR_Full_BIT,
657 "50000baseDR/Full" },
658 { 0, ETHTOOL_LINK_MODE_100000baseKR2_Full_BIT,
659 "100000baseKR2/Full" },
660 { 0, ETHTOOL_LINK_MODE_100000baseSR2_Full_BIT,
661 "100000baseSR2/Full" },
662 { 0, ETHTOOL_LINK_MODE_100000baseCR2_Full_BIT,
663 "100000baseCR2/Full" },
664 { 0, ETHTOOL_LINK_MODE_100000baseLR2_ER2_FR2_Full_BIT,
665 "100000baseLR2_ER2_FR2/Full" },
666 { 0, ETHTOOL_LINK_MODE_100000baseDR2_Full_BIT,
667 "100000baseDR2/Full" },
668 { 0, ETHTOOL_LINK_MODE_200000baseKR4_Full_BIT,
669 "200000baseKR4/Full" },
670 { 0, ETHTOOL_LINK_MODE_200000baseSR4_Full_BIT,
671 "200000baseSR4/Full" },
672 { 0, ETHTOOL_LINK_MODE_200000baseLR4_ER4_FR4_Full_BIT,
673 "200000baseLR4_ER4_FR4/Full" },
674 { 0, ETHTOOL_LINK_MODE_200000baseDR4_Full_BIT,
675 "200000baseDR4/Full" },
676 { 0, ETHTOOL_LINK_MODE_200000baseCR4_Full_BIT,
677 "200000baseCR4/Full" },
678 { 0, ETHTOOL_LINK_MODE_100baseT1_Full_BIT,
679 "100baseT1/Full" },
680 { 0, ETHTOOL_LINK_MODE_1000baseT1_Full_BIT,
681 "1000baseT1/Full" },
682 { 0, ETHTOOL_LINK_MODE_400000baseKR8_Full_BIT,
683 "400000baseKR8/Full" },
684 { 0, ETHTOOL_LINK_MODE_400000baseSR8_Full_BIT,
685 "400000baseSR8/Full" },
686 { 0, ETHTOOL_LINK_MODE_400000baseLR8_ER8_FR8_Full_BIT,
687 "400000baseLR8_ER8_FR8/Full" },
688 { 0, ETHTOOL_LINK_MODE_400000baseDR8_Full_BIT,
689 "400000baseDR8/Full" },
690 { 0, ETHTOOL_LINK_MODE_400000baseCR8_Full_BIT,
691 "400000baseCR8/Full" },
692 { 0, ETHTOOL_LINK_MODE_100000baseKR_Full_BIT,
693 "100000baseKR/Full" },
694 { 0, ETHTOOL_LINK_MODE_100000baseSR_Full_BIT,
695 "100000baseSR/Full" },
696 { 0, ETHTOOL_LINK_MODE_100000baseLR_ER_FR_Full_BIT,
697 "100000baseLR_ER_FR/Full" },
698 { 0, ETHTOOL_LINK_MODE_100000baseDR_Full_BIT,
699 "100000baseDR/Full" },
700 { 0, ETHTOOL_LINK_MODE_100000baseCR_Full_BIT,
701 "100000baseCR/Full" },
702 { 0, ETHTOOL_LINK_MODE_200000baseKR2_Full_BIT,
703 "200000baseKR2/Full" },
704 { 0, ETHTOOL_LINK_MODE_200000baseSR2_Full_BIT,
705 "200000baseSR2/Full" },
706 { 0, ETHTOOL_LINK_MODE_200000baseLR2_ER2_FR2_Full_BIT,
707 "200000baseLR2_ER2_FR2/Full" },
708 { 0, ETHTOOL_LINK_MODE_200000baseDR2_Full_BIT,
709 "200000baseDR2/Full" },
710 { 0, ETHTOOL_LINK_MODE_200000baseCR2_Full_BIT,
711 "200000baseCR2/Full" },
712 { 0, ETHTOOL_LINK_MODE_400000baseKR4_Full_BIT,
713 "400000baseKR4/Full" },
714 { 0, ETHTOOL_LINK_MODE_400000baseSR4_Full_BIT,
715 "400000baseSR4/Full" },
716 { 0, ETHTOOL_LINK_MODE_400000baseLR4_ER4_FR4_Full_BIT,
717 "400000baseLR4_ER4_FR4/Full" },
718 { 0, ETHTOOL_LINK_MODE_400000baseDR4_Full_BIT,
719 "400000baseDR4/Full" },
720 { 0, ETHTOOL_LINK_MODE_400000baseCR4_Full_BIT,
721 "400000baseCR4/Full" },
722 { 0, ETHTOOL_LINK_MODE_100baseFX_Half_BIT,
723 "100baseFX/Half" },
724 { 1, ETHTOOL_LINK_MODE_100baseFX_Full_BIT,
725 "100baseFX/Full" },
726 { 0, ETHTOOL_LINK_MODE_10baseT1L_Full_BIT,
727 "10baseT1L/Full" },
728 { 0, ETHTOOL_LINK_MODE_800000baseCR8_Full_BIT,
729 "800000baseCR8/Full" },
730 { 0, ETHTOOL_LINK_MODE_800000baseKR8_Full_BIT,
731 "800000baseKR8/Full" },
732 { 0, ETHTOOL_LINK_MODE_800000baseDR8_Full_BIT,
733 "800000baseDR8/Full" },
734 { 0, ETHTOOL_LINK_MODE_800000baseDR8_2_Full_BIT,
735 "800000baseDR8_2/Full" },
736 { 0, ETHTOOL_LINK_MODE_800000baseSR8_Full_BIT,
737 "800000baseSR8/Full" },
738 { 0, ETHTOOL_LINK_MODE_800000baseVR8_Full_BIT,
739 "800000baseVR8/Full" },
740 { 0, ETHTOOL_LINK_MODE_10baseT1S_Full_BIT,
741 "10baseT1S/Full" },
742 { 1, ETHTOOL_LINK_MODE_10baseT1S_Half_BIT,
743 "10baseT1S/Half" },
744 { 0, ETHTOOL_LINK_MODE_10baseT1S_P2MP_Half_BIT,
745 "10baseT1S/Half" },
746 };
747 int indent;
748 int did1, new_line_pend;
749 int fecreported = 0;
750 unsigned int i;
751
752 /* Indent just like the separate functions used to */
753 indent = strlen(prefix) + 14;
754 if (indent < 24)
755 indent = 24;
756
757 fprintf(stdout, " %s link modes:%*s", prefix,
758 indent - (int)strlen(prefix) - 12, "");
759 did1 = 0;
760 new_line_pend = 0;
761 for (i = 0; i < ARRAY_SIZE(mode_defs); i++) {
762 if (did1 && !mode_defs[i].same_line)
763 new_line_pend = 1;
764 if (ethtool_link_mode_test_bit(mode_defs[i].bit_index,
765 mask)) {
766 if (new_line_pend) {
767 fprintf(stdout, "\n");
768 fprintf(stdout, " %*s", indent, "");
769 new_line_pend = 0;
770 }
771 did1++;
772 fprintf(stdout, "%s ", mode_defs[i].name);
773 }
774 }
775 if (did1 == 0)
776 fprintf(stdout, "Not reported");
777 fprintf(stdout, "\n");
778
779 if (!link_mode_only) {
780 fprintf(stdout, " %s pause frame use: ", prefix);
781 if (ethtool_link_mode_test_bit(
782 ETHTOOL_LINK_MODE_Pause_BIT, mask)) {
783 fprintf(stdout, "Symmetric");
784 if (ethtool_link_mode_test_bit(
785 ETHTOOL_LINK_MODE_Asym_Pause_BIT, mask))
786 fprintf(stdout, " Receive-only");
787 fprintf(stdout, "\n");
788 } else {
789 if (ethtool_link_mode_test_bit(
790 ETHTOOL_LINK_MODE_Asym_Pause_BIT, mask))
791 fprintf(stdout, "Transmit-only\n");
792 else
793 fprintf(stdout, "No\n");
794 }
795
796 fprintf(stdout, " %s auto-negotiation: ", an_prefix);
797 if (ethtool_link_mode_test_bit(
798 ETHTOOL_LINK_MODE_Autoneg_BIT, mask))
799 fprintf(stdout, "Yes\n");
800 else
801 fprintf(stdout, "No\n");
802
803 fprintf(stdout, " %s FEC modes:", prefix);
804 if (ethtool_link_mode_test_bit(ETHTOOL_LINK_MODE_FEC_NONE_BIT,
805 mask)) {
806 fprintf(stdout, " None");
807 fecreported = 1;
808 }
809 if (ethtool_link_mode_test_bit(ETHTOOL_LINK_MODE_FEC_BASER_BIT,
810 mask)) {
811 fprintf(stdout, " BaseR");
812 fecreported = 1;
813 }
814 if (ethtool_link_mode_test_bit(ETHTOOL_LINK_MODE_FEC_RS_BIT,
815 mask)) {
816 fprintf(stdout, " RS");
817 fecreported = 1;
818 }
819 if (ethtool_link_mode_test_bit(ETHTOOL_LINK_MODE_FEC_LLRS_BIT,
820 mask)) {
821 fprintf(stdout, " LLRS");
822 fecreported = 1;
823 }
824
825 if (!fecreported)
826 fprintf(stdout, " Not reported");
827 fprintf(stdout, "\n");
828 }
829 }
830
831 static int
dump_link_usettings(const struct ethtool_link_usettings * link_usettings)832 dump_link_usettings(const struct ethtool_link_usettings *link_usettings)
833 {
834 dump_supported(link_usettings);
835 dump_link_caps("Advertised", "Advertised",
836 link_usettings->link_modes.advertising, 0);
837 if (!ethtool_link_mode_is_empty(
838 link_usettings->link_modes.lp_advertising))
839 dump_link_caps("Link partner advertised",
840 "Link partner advertised",
841 link_usettings->link_modes.lp_advertising, 0);
842
843 fprintf(stdout, " Speed: ");
844 if (link_usettings->base.speed == 0
845 || link_usettings->base.speed == (u16)(-1)
846 || link_usettings->base.speed == (u32)(-1))
847 fprintf(stdout, "Unknown!\n");
848 else
849 fprintf(stdout, "%uMb/s\n", link_usettings->base.speed);
850
851 fprintf(stdout, " Duplex: ");
852 switch (link_usettings->base.duplex) {
853 case DUPLEX_HALF:
854 fprintf(stdout, "Half\n");
855 break;
856 case DUPLEX_FULL:
857 fprintf(stdout, "Full\n");
858 break;
859 default:
860 fprintf(stdout, "Unknown! (%i)\n", link_usettings->base.duplex);
861 break;
862 };
863
864 fprintf(stdout, " Port: ");
865 switch (link_usettings->base.port) {
866 case PORT_TP:
867 fprintf(stdout, "Twisted Pair\n");
868 break;
869 case PORT_AUI:
870 fprintf(stdout, "AUI\n");
871 break;
872 case PORT_BNC:
873 fprintf(stdout, "BNC\n");
874 break;
875 case PORT_MII:
876 fprintf(stdout, "MII\n");
877 break;
878 case PORT_FIBRE:
879 fprintf(stdout, "FIBRE\n");
880 break;
881 case PORT_DA:
882 fprintf(stdout, "Direct Attach Copper\n");
883 break;
884 case PORT_NONE:
885 fprintf(stdout, "None\n");
886 break;
887 case PORT_OTHER:
888 fprintf(stdout, "Other\n");
889 break;
890 default:
891 fprintf(stdout, "Unknown! (%i)\n", link_usettings->base.port);
892 break;
893 };
894
895 fprintf(stdout, " PHYAD: %d\n", link_usettings->base.phy_address);
896 fprintf(stdout, " Transceiver: ");
897 switch (link_usettings->deprecated.transceiver) {
898 case XCVR_INTERNAL:
899 fprintf(stdout, "internal\n");
900 break;
901 case XCVR_EXTERNAL:
902 fprintf(stdout, "external\n");
903 break;
904 default:
905 fprintf(stdout, "Unknown!\n");
906 break;
907 };
908
909 fprintf(stdout, " Auto-negotiation: %s\n",
910 (link_usettings->base.autoneg == AUTONEG_DISABLE) ?
911 "off" : "on");
912
913 if (link_usettings->base.port == PORT_TP)
914 dump_mdix(link_usettings->base.eth_tp_mdix,
915 link_usettings->base.eth_tp_mdix_ctrl);
916
917 return 0;
918 }
919
dump_drvinfo(struct ethtool_drvinfo * info)920 static int dump_drvinfo(struct ethtool_drvinfo *info)
921 {
922 fprintf(stdout,
923 "driver: %.*s\n"
924 "version: %.*s\n"
925 "firmware-version: %.*s\n"
926 "expansion-rom-version: %.*s\n"
927 "bus-info: %.*s\n"
928 "supports-statistics: %s\n"
929 "supports-test: %s\n"
930 "supports-eeprom-access: %s\n"
931 "supports-register-dump: %s\n"
932 "supports-priv-flags: %s\n",
933 (int)sizeof(info->driver), info->driver,
934 (int)sizeof(info->version), info->version,
935 (int)sizeof(info->fw_version), info->fw_version,
936 (int)sizeof(info->erom_version), info->erom_version,
937 (int)sizeof(info->bus_info), info->bus_info,
938 info->n_stats ? "yes" : "no",
939 info->testinfo_len ? "yes" : "no",
940 info->eedump_len ? "yes" : "no",
941 info->regdump_len ? "yes" : "no",
942 info->n_priv_flags ? "yes" : "no");
943
944 return 0;
945 }
946
parse_wolopts(char * optstr,u32 * data)947 static int parse_wolopts(char *optstr, u32 *data)
948 {
949 *data = 0;
950 while (*optstr) {
951 switch (*optstr) {
952 case 'p':
953 *data |= WAKE_PHY;
954 break;
955 case 'u':
956 *data |= WAKE_UCAST;
957 break;
958 case 'm':
959 *data |= WAKE_MCAST;
960 break;
961 case 'b':
962 *data |= WAKE_BCAST;
963 break;
964 case 'a':
965 *data |= WAKE_ARP;
966 break;
967 case 'g':
968 *data |= WAKE_MAGIC;
969 break;
970 case 's':
971 *data |= WAKE_MAGICSECURE;
972 break;
973 case 'f':
974 *data |= WAKE_FILTER;
975 break;
976 case 'd':
977 *data = 0;
978 break;
979 default:
980 return -1;
981 }
982 optstr++;
983 }
984 return 0;
985 }
986
parse_rxfhashopts(char * optstr,u32 * data)987 static int parse_rxfhashopts(char *optstr, u32 *data)
988 {
989 *data = 0;
990 while (*optstr) {
991 switch (*optstr) {
992 case 'm':
993 *data |= RXH_L2DA;
994 break;
995 case 'v':
996 *data |= RXH_VLAN;
997 break;
998 case 't':
999 *data |= RXH_L3_PROTO;
1000 break;
1001 case 's':
1002 *data |= RXH_IP_SRC;
1003 break;
1004 case 'd':
1005 *data |= RXH_IP_DST;
1006 break;
1007 case 'f':
1008 *data |= RXH_L4_B_0_1;
1009 break;
1010 case 'n':
1011 *data |= RXH_L4_B_2_3;
1012 break;
1013 case 'r':
1014 *data |= RXH_DISCARD;
1015 break;
1016 default:
1017 return -1;
1018 }
1019 optstr++;
1020 }
1021 return 0;
1022 }
1023
unparse_rxfhashopts(u64 opts)1024 static char *unparse_rxfhashopts(u64 opts)
1025 {
1026 static char buf[300];
1027
1028 memset(buf, 0, sizeof(buf));
1029
1030 if (opts) {
1031 if (opts & RXH_L2DA)
1032 strcat(buf, "L2DA\n");
1033 if (opts & RXH_VLAN)
1034 strcat(buf, "VLAN tag\n");
1035 if (opts & RXH_L3_PROTO)
1036 strcat(buf, "L3 proto\n");
1037 if (opts & RXH_IP_SRC)
1038 strcat(buf, "IP SA\n");
1039 if (opts & RXH_IP_DST)
1040 strcat(buf, "IP DA\n");
1041 if (opts & RXH_L4_B_0_1)
1042 strcat(buf, "L4 bytes 0 & 1 [TCP/UDP src port]\n");
1043 if (opts & RXH_L4_B_2_3)
1044 strcat(buf, "L4 bytes 2 & 3 [TCP/UDP dst port]\n");
1045 } else {
1046 sprintf(buf, "None");
1047 }
1048
1049 return buf;
1050 }
1051
convert_string_to_hashkey(char * rss_hkey,u32 key_size,const char * rss_hkey_string)1052 static int convert_string_to_hashkey(char *rss_hkey, u32 key_size,
1053 const char *rss_hkey_string)
1054 {
1055 u32 i = 0;
1056 int hex_byte, len;
1057
1058 do {
1059 if (i > (key_size - 1)) {
1060 fprintf(stderr,
1061 "Key is too long for device (%u > %u)\n",
1062 i + 1, key_size);
1063 goto err;
1064 }
1065
1066 if (sscanf(rss_hkey_string, "%2x%n", &hex_byte, &len) < 1 ||
1067 len != 2) {
1068 fprintf(stderr, "Invalid RSS hash key format\n");
1069 goto err;
1070 }
1071
1072 rss_hkey[i++] = hex_byte;
1073 rss_hkey_string += 2;
1074
1075 if (*rss_hkey_string == ':') {
1076 rss_hkey_string++;
1077 } else if (*rss_hkey_string != '\0') {
1078 fprintf(stderr, "Invalid RSS hash key format\n");
1079 goto err;
1080 }
1081
1082 } while (*rss_hkey_string);
1083
1084 if (i != key_size) {
1085 fprintf(stderr, "Key is too short for device (%u < %u)\n",
1086 i, key_size);
1087 goto err;
1088 }
1089
1090 return 0;
1091 err:
1092 return 2;
1093 }
1094
parse_hkey(char ** rss_hkey,u32 key_size,const char * rss_hkey_string)1095 static int parse_hkey(char **rss_hkey, u32 key_size,
1096 const char *rss_hkey_string)
1097 {
1098 if (!key_size) {
1099 fprintf(stderr,
1100 "Cannot set RX flow hash configuration:\n"
1101 " Hash key setting not supported\n");
1102 return 1;
1103 }
1104
1105 *rss_hkey = malloc(key_size);
1106 if (!(*rss_hkey)) {
1107 perror("Cannot allocate memory for RSS hash key");
1108 return 1;
1109 }
1110
1111 if (convert_string_to_hashkey(*rss_hkey, key_size,
1112 rss_hkey_string)) {
1113 free(*rss_hkey);
1114 *rss_hkey = NULL;
1115 return 2;
1116 }
1117 return 0;
1118 }
1119
1120 #ifdef ETHTOOL_ENABLE_PRETTY_DUMP
1121 static const struct {
1122 const char *name;
1123 int (*func)(struct ethtool_drvinfo *info, struct ethtool_regs *regs);
1124
1125 } driver_list[] = {
1126 { "8139cp", realtek_dump_regs },
1127 { "8139too", realtek_dump_regs },
1128 { "r8169", realtek_dump_regs },
1129 { "de2104x", de2104x_dump_regs },
1130 { "e1000", e1000_dump_regs },
1131 { "e1000e", e1000_dump_regs },
1132 { "igb", igb_dump_regs },
1133 { "ixgb", ixgb_dump_regs },
1134 { "ixgbe", ixgbe_dump_regs },
1135 { "ixgbevf", ixgbevf_dump_regs },
1136 { "natsemi", natsemi_dump_regs },
1137 { "e100", e100_dump_regs },
1138 { "amd8111e", amd8111e_dump_regs },
1139 { "pcnet32", pcnet32_dump_regs },
1140 { "fec_8xx", fec_8xx_dump_regs },
1141 { "ibm_emac", ibm_emac_dump_regs },
1142 { "tg3", tg3_dump_regs },
1143 { "skge", skge_dump_regs },
1144 { "sky2", sky2_dump_regs },
1145 { "vioc", vioc_dump_regs },
1146 { "smsc911x", smsc911x_dump_regs },
1147 { "at76c50x-usb", at76c50x_usb_dump_regs },
1148 { "sfc", sfc_dump_regs },
1149 { "st_mac100", st_mac100_dump_regs },
1150 { "st_gmac", st_gmac_dump_regs },
1151 { "et131x", et131x_dump_regs },
1152 { "altera_tse", altera_tse_dump_regs },
1153 { "vmxnet3", vmxnet3_dump_regs },
1154 { "fjes", fjes_dump_regs },
1155 { "lan78xx", lan78xx_dump_regs },
1156 { "dsa", dsa_dump_regs },
1157 { "fec", fec_dump_regs },
1158 { "igc", igc_dump_regs },
1159 { "bnxt_en", bnxt_dump_regs },
1160 { "cpsw-switch", cpsw_dump_regs },
1161 { "lan743x", lan743x_dump_regs },
1162 { "fsl_enetc", fsl_enetc_dump_regs },
1163 { "fsl_enetc_vf", fsl_enetc_dump_regs },
1164 { "hns3", hns3_dump_regs },
1165 };
1166 #endif
1167
dump_hex(FILE * file,const u8 * data,int len,int offset)1168 void dump_hex(FILE *file, const u8 *data, int len, int offset)
1169 {
1170 int i;
1171
1172 fprintf(file, "Offset\t\tValues\n");
1173 fprintf(file, "------\t\t------");
1174 for (i = 0; i < len; i++) {
1175 if (i % 16 == 0)
1176 fprintf(file, "\n0x%04x:\t\t", i + offset);
1177 fprintf(file, "%02x ", data[i]);
1178 }
1179 fprintf(file, "\n");
1180 }
1181
dump_regs(int gregs_dump_raw,int gregs_dump_hex,struct ethtool_drvinfo * info,struct ethtool_regs * regs)1182 static int dump_regs(int gregs_dump_raw, int gregs_dump_hex,
1183 struct ethtool_drvinfo *info, struct ethtool_regs *regs)
1184 {
1185 if (gregs_dump_raw) {
1186 fwrite(regs->data, regs->len, 1, stdout);
1187 goto nested;
1188 }
1189
1190 #ifdef ETHTOOL_ENABLE_PRETTY_DUMP
1191 if (!gregs_dump_hex) {
1192 unsigned int i;
1193
1194 for (i = 0; i < ARRAY_SIZE(driver_list); i++)
1195 if (!strncmp(driver_list[i].name, info->driver,
1196 ETHTOOL_BUSINFO_LEN)) {
1197 if (driver_list[i].func(info, regs) == 0)
1198 goto nested;
1199 /* This version (or some other
1200 * variation in the dump format) is
1201 * not handled; fall back to hex
1202 */
1203 break;
1204 }
1205 }
1206 #endif
1207
1208 dump_hex(stdout, regs->data, regs->len, 0);
1209
1210 nested:
1211 /* Recurse dump if some drvinfo and regs structures are nested */
1212 if (info->regdump_len > regs->len + sizeof(*info) + sizeof(*regs)) {
1213 info = (struct ethtool_drvinfo *)(®s->data[0] + regs->len);
1214 regs = (struct ethtool_regs *)(®s->data[0] + regs->len + sizeof(*info));
1215
1216 return dump_regs(gregs_dump_raw, gregs_dump_hex, info, regs);
1217 }
1218
1219 return 0;
1220 }
1221
dump_eeprom(int geeprom_dump_raw,struct ethtool_drvinfo * info __maybe_unused,struct ethtool_eeprom * ee)1222 static int dump_eeprom(int geeprom_dump_raw,
1223 struct ethtool_drvinfo *info __maybe_unused,
1224 struct ethtool_eeprom *ee)
1225 {
1226 if (geeprom_dump_raw) {
1227 fwrite(ee->data, 1, ee->len, stdout);
1228 return 0;
1229 }
1230 #ifdef ETHTOOL_ENABLE_PRETTY_DUMP
1231 if (!strncmp("natsemi", info->driver, ETHTOOL_BUSINFO_LEN)) {
1232 return natsemi_dump_eeprom(info, ee);
1233 } else if (!strncmp("tg3", info->driver, ETHTOOL_BUSINFO_LEN)) {
1234 return tg3_dump_eeprom(info, ee);
1235 }
1236 #endif
1237 dump_hex(stdout, ee->data, ee->len, ee->offset);
1238
1239 return 0;
1240 }
1241
dump_test(struct ethtool_test * test,struct ethtool_gstrings * strings)1242 static int dump_test(struct ethtool_test *test,
1243 struct ethtool_gstrings *strings)
1244 {
1245 unsigned int i;
1246 int rc;
1247
1248 rc = test->flags & ETH_TEST_FL_FAILED;
1249 fprintf(stdout, "The test result is %s\n", rc ? "FAIL" : "PASS");
1250
1251 if (test->flags & ETH_TEST_FL_EXTERNAL_LB)
1252 fprintf(stdout, "External loopback test was %sexecuted\n",
1253 (test->flags & ETH_TEST_FL_EXTERNAL_LB_DONE) ?
1254 "" : "not ");
1255
1256 if (strings->len)
1257 fprintf(stdout, "The test extra info:\n");
1258
1259 for (i = 0; i < strings->len; i++) {
1260 fprintf(stdout, "%s\t %d\n",
1261 (char *)(strings->data + i * ETH_GSTRING_LEN),
1262 (u32) test->data[i]);
1263 }
1264
1265 fprintf(stdout, "\n");
1266 return rc;
1267 }
1268
dump_pause(const struct ethtool_pauseparam * epause,u32 advertising,u32 lp_advertising)1269 static int dump_pause(const struct ethtool_pauseparam *epause,
1270 u32 advertising, u32 lp_advertising)
1271 {
1272 fprintf(stdout,
1273 "Autonegotiate: %s\n"
1274 "RX: %s\n"
1275 "TX: %s\n",
1276 epause->autoneg ? "on" : "off",
1277 epause->rx_pause ? "on" : "off",
1278 epause->tx_pause ? "on" : "off");
1279
1280 if (lp_advertising) {
1281 int an_rx = 0, an_tx = 0;
1282
1283 /* Work out negotiated pause frame usage per
1284 * IEEE 802.3-2005 table 28B-3.
1285 */
1286 if (advertising & lp_advertising & ADVERTISED_Pause) {
1287 an_tx = 1;
1288 an_rx = 1;
1289 } else if (advertising & lp_advertising &
1290 ADVERTISED_Asym_Pause) {
1291 if (advertising & ADVERTISED_Pause)
1292 an_rx = 1;
1293 else if (lp_advertising & ADVERTISED_Pause)
1294 an_tx = 1;
1295 }
1296
1297 fprintf(stdout,
1298 "RX negotiated: %s\n"
1299 "TX negotiated: %s\n",
1300 an_rx ? "on" : "off",
1301 an_tx ? "on" : "off");
1302 }
1303
1304 fprintf(stdout, "\n");
1305 return 0;
1306 }
1307
dump_ring(const struct ethtool_ringparam * ering)1308 static int dump_ring(const struct ethtool_ringparam *ering)
1309 {
1310 fprintf(stdout,
1311 "Pre-set maximums:\n"
1312 "RX: %u\n"
1313 "RX Mini: %u\n"
1314 "RX Jumbo: %u\n"
1315 "TX: %u\n",
1316 ering->rx_max_pending,
1317 ering->rx_mini_max_pending,
1318 ering->rx_jumbo_max_pending,
1319 ering->tx_max_pending);
1320
1321 fprintf(stdout,
1322 "Current hardware settings:\n"
1323 "RX: %u\n"
1324 "RX Mini: %u\n"
1325 "RX Jumbo: %u\n"
1326 "TX: %u\n",
1327 ering->rx_pending,
1328 ering->rx_mini_pending,
1329 ering->rx_jumbo_pending,
1330 ering->tx_pending);
1331
1332 fprintf(stdout, "\n");
1333 return 0;
1334 }
1335
dump_channels(const struct ethtool_channels * echannels)1336 static int dump_channels(const struct ethtool_channels *echannels)
1337 {
1338 fprintf(stdout,
1339 "Pre-set maximums:\n"
1340 "RX: %u\n"
1341 "TX: %u\n"
1342 "Other: %u\n"
1343 "Combined: %u\n",
1344 echannels->max_rx, echannels->max_tx,
1345 echannels->max_other,
1346 echannels->max_combined);
1347
1348 fprintf(stdout,
1349 "Current hardware settings:\n"
1350 "RX: %u\n"
1351 "TX: %u\n"
1352 "Other: %u\n"
1353 "Combined: %u\n",
1354 echannels->rx_count, echannels->tx_count,
1355 echannels->other_count,
1356 echannels->combined_count);
1357
1358 fprintf(stdout, "\n");
1359 return 0;
1360 }
1361
dump_coalesce(const struct ethtool_coalesce * ecoal)1362 static int dump_coalesce(const struct ethtool_coalesce *ecoal)
1363 {
1364 fprintf(stdout, "Adaptive RX: %s TX: %s\n",
1365 ecoal->use_adaptive_rx_coalesce ? "on" : "off",
1366 ecoal->use_adaptive_tx_coalesce ? "on" : "off");
1367
1368 fprintf(stdout,
1369 "stats-block-usecs: %u\n"
1370 "sample-interval: %u\n"
1371 "pkt-rate-low: %u\n"
1372 "pkt-rate-high: %u\n"
1373 "\n"
1374 "rx-usecs: %u\n"
1375 "rx-frames: %u\n"
1376 "rx-usecs-irq: %u\n"
1377 "rx-frames-irq: %u\n"
1378 "\n"
1379 "tx-usecs: %u\n"
1380 "tx-frames: %u\n"
1381 "tx-usecs-irq: %u\n"
1382 "tx-frames-irq: %u\n"
1383 "\n"
1384 "rx-usecs-low: %u\n"
1385 "rx-frames-low: %u\n"
1386 "tx-usecs-low: %u\n"
1387 "tx-frames-low: %u\n"
1388 "\n"
1389 "rx-usecs-high: %u\n"
1390 "rx-frames-high: %u\n"
1391 "tx-usecs-high: %u\n"
1392 "tx-frames-high: %u\n"
1393 "\n",
1394 ecoal->stats_block_coalesce_usecs,
1395 ecoal->rate_sample_interval,
1396 ecoal->pkt_rate_low,
1397 ecoal->pkt_rate_high,
1398
1399 ecoal->rx_coalesce_usecs,
1400 ecoal->rx_max_coalesced_frames,
1401 ecoal->rx_coalesce_usecs_irq,
1402 ecoal->rx_max_coalesced_frames_irq,
1403
1404 ecoal->tx_coalesce_usecs,
1405 ecoal->tx_max_coalesced_frames,
1406 ecoal->tx_coalesce_usecs_irq,
1407 ecoal->tx_max_coalesced_frames_irq,
1408
1409 ecoal->rx_coalesce_usecs_low,
1410 ecoal->rx_max_coalesced_frames_low,
1411 ecoal->tx_coalesce_usecs_low,
1412 ecoal->tx_max_coalesced_frames_low,
1413
1414 ecoal->rx_coalesce_usecs_high,
1415 ecoal->rx_max_coalesced_frames_high,
1416 ecoal->tx_coalesce_usecs_high,
1417 ecoal->tx_max_coalesced_frames_high);
1418
1419 return 0;
1420 }
1421
dump_per_queue_coalesce(struct ethtool_per_queue_op * per_queue_opt,__u32 * queue_mask,int n_queues)1422 void dump_per_queue_coalesce(struct ethtool_per_queue_op *per_queue_opt,
1423 __u32 *queue_mask, int n_queues)
1424 {
1425 struct ethtool_coalesce *ecoal;
1426 int i, idx = 0;
1427
1428 ecoal = (struct ethtool_coalesce *)(per_queue_opt + 1);
1429 for (i = 0; i < __KERNEL_DIV_ROUND_UP(MAX_NUM_QUEUE, 32); i++) {
1430 int queue = i * 32;
1431 __u32 mask = queue_mask[i];
1432
1433 while (mask > 0) {
1434 if (mask & 0x1) {
1435 fprintf(stdout, "Queue: %d\n", queue);
1436 dump_coalesce(ecoal + idx);
1437 idx++;
1438 }
1439 mask = mask >> 1;
1440 queue++;
1441 }
1442 if (idx == n_queues)
1443 break;
1444 }
1445 }
1446
1447 struct feature_state {
1448 u32 off_flags;
1449 struct ethtool_gfeatures features;
1450 };
1451
dump_one_feature(const char * indent,const char * name,const struct feature_state * state,const struct feature_state * ref_state,u32 index)1452 static void dump_one_feature(const char *indent, const char *name,
1453 const struct feature_state *state,
1454 const struct feature_state *ref_state,
1455 u32 index)
1456 {
1457 if (ref_state &&
1458 !(FEATURE_BIT_IS_SET(state->features.features, index, active) ^
1459 FEATURE_BIT_IS_SET(ref_state->features.features, index, active)))
1460 return;
1461
1462 printf("%s%s: %s%s\n",
1463 indent, name,
1464 FEATURE_BIT_IS_SET(state->features.features, index, active) ?
1465 "on" : "off",
1466 (!FEATURE_BIT_IS_SET(state->features.features, index, available)
1467 || FEATURE_BIT_IS_SET(state->features.features, index,
1468 never_changed))
1469 ? " [fixed]"
1470 : (FEATURE_BIT_IS_SET(state->features.features, index, requested)
1471 ^ FEATURE_BIT_IS_SET(state->features.features, index, active))
1472 ? (FEATURE_BIT_IS_SET(state->features.features, index, requested)
1473 ? " [requested on]" : " [requested off]")
1474 : "");
1475 }
1476
linux_version_code(void)1477 static unsigned int linux_version_code(void)
1478 {
1479 struct utsname utsname;
1480 unsigned version, patchlevel, sublevel = 0;
1481
1482 if (uname(&utsname))
1483 return -1;
1484 if (sscanf(utsname.release, "%u.%u.%u", &version, &patchlevel, &sublevel) < 2)
1485 return -1;
1486 return KERNEL_VERSION(version, patchlevel, sublevel);
1487 }
1488
dump_features(const struct feature_defs * defs,const struct feature_state * state,const struct feature_state * ref_state)1489 static void dump_features(const struct feature_defs *defs,
1490 const struct feature_state *state,
1491 const struct feature_state *ref_state)
1492 {
1493 unsigned int kernel_ver = linux_version_code();
1494 unsigned int i, j;
1495 int indent;
1496 u32 value;
1497
1498 for (i = 0; i < OFF_FLAG_DEF_SIZE; i++) {
1499 /* Don't show features whose state is unknown on this
1500 * kernel version
1501 */
1502 if (defs->off_flag_matched[i] == 0 &&
1503 ((off_flag_def[i].get_cmd == 0 &&
1504 kernel_ver < off_flag_def[i].min_kernel_ver) ||
1505 (off_flag_def[i].get_cmd == ETHTOOL_GUFO &&
1506 kernel_ver >= KERNEL_VERSION(4, 14, 0))))
1507 continue;
1508
1509 value = off_flag_def[i].value;
1510
1511 /* If this offload flag matches exactly one generic
1512 * feature then it's redundant to show the flag and
1513 * feature states separately. Otherwise, show the
1514 * flag state first.
1515 */
1516 if (defs->off_flag_matched[i] != 1 &&
1517 (!ref_state ||
1518 (state->off_flags ^ ref_state->off_flags) & value)) {
1519 printf("%s: %s\n",
1520 off_flag_def[i].long_name,
1521 (state->off_flags & value) ? "on" : "off");
1522 indent = 1;
1523 } else {
1524 indent = 0;
1525 }
1526
1527 /* Show matching features */
1528 for (j = 0; j < defs->n_features; j++) {
1529 if (defs->def[j].off_flag_index != (int)i)
1530 continue;
1531 if (defs->off_flag_matched[i] != 1)
1532 /* Show all matching feature states */
1533 dump_one_feature(indent ? "\t" : "",
1534 defs->def[j].name,
1535 state, ref_state, j);
1536 else
1537 /* Show full state with the old flag name */
1538 dump_one_feature("", off_flag_def[i].long_name,
1539 state, ref_state, j);
1540 }
1541 }
1542
1543 /* Show all unmatched features that have non-null names */
1544 for (j = 0; j < defs->n_features; j++)
1545 if (defs->def[j].off_flag_index < 0 && defs->def[j].name[0])
1546 dump_one_feature("", defs->def[j].name,
1547 state, ref_state, j);
1548 }
1549
dump_rxfhash(int fhash,u64 val)1550 static int dump_rxfhash(int fhash, u64 val)
1551 {
1552 switch (fhash & ~FLOW_RSS) {
1553 case TCP_V4_FLOW:
1554 fprintf(stdout, "TCP over IPV4 flows");
1555 break;
1556 case UDP_V4_FLOW:
1557 fprintf(stdout, "UDP over IPV4 flows");
1558 break;
1559 case SCTP_V4_FLOW:
1560 fprintf(stdout, "SCTP over IPV4 flows");
1561 break;
1562 case AH_ESP_V4_FLOW:
1563 case AH_V4_FLOW:
1564 case ESP_V4_FLOW:
1565 fprintf(stdout, "IPSEC AH/ESP over IPV4 flows");
1566 break;
1567 case TCP_V6_FLOW:
1568 fprintf(stdout, "TCP over IPV6 flows");
1569 break;
1570 case UDP_V6_FLOW:
1571 fprintf(stdout, "UDP over IPV6 flows");
1572 break;
1573 case SCTP_V6_FLOW:
1574 fprintf(stdout, "SCTP over IPV6 flows");
1575 break;
1576 case AH_ESP_V6_FLOW:
1577 case AH_V6_FLOW:
1578 case ESP_V6_FLOW:
1579 fprintf(stdout, "IPSEC AH/ESP over IPV6 flows");
1580 break;
1581 default:
1582 break;
1583 }
1584
1585 if (val & RXH_DISCARD) {
1586 fprintf(stdout, " - All matching flows discarded on RX\n");
1587 return 0;
1588 }
1589 fprintf(stdout, " use these fields for computing Hash flow key:\n");
1590
1591 fprintf(stdout, "%s\n", unparse_rxfhashopts(val));
1592
1593 return 0;
1594 }
1595
dump_eeecmd(struct ethtool_eee * ep)1596 static void dump_eeecmd(struct ethtool_eee *ep)
1597 {
1598 ETHTOOL_DECLARE_LINK_MODE_MASK(link_mode);
1599
1600 fprintf(stdout, " EEE status: ");
1601 if (!ep->supported) {
1602 fprintf(stdout, "not supported\n");
1603 return;
1604 } else if (!ep->eee_enabled) {
1605 fprintf(stdout, "disabled\n");
1606 } else {
1607 fprintf(stdout, "enabled - ");
1608 if (ep->eee_active)
1609 fprintf(stdout, "active\n");
1610 else
1611 fprintf(stdout, "inactive\n");
1612 }
1613
1614 fprintf(stdout, " Tx LPI:");
1615 if (ep->tx_lpi_enabled)
1616 fprintf(stdout, " %d (us)\n", ep->tx_lpi_timer);
1617 else
1618 fprintf(stdout, " disabled\n");
1619
1620 ethtool_link_mode_zero(link_mode);
1621
1622 link_mode[0] = ep->supported;
1623 dump_link_caps("Supported EEE", "", link_mode, 1);
1624
1625 link_mode[0] = ep->advertised;
1626 dump_link_caps("Advertised EEE", "", link_mode, 1);
1627
1628 link_mode[0] = ep->lp_advertised;
1629 dump_link_caps("Link partner advertised EEE", "", link_mode, 1);
1630 }
1631
dump_fec(u32 fec)1632 static void dump_fec(u32 fec)
1633 {
1634 if (fec & ETHTOOL_FEC_NONE)
1635 fprintf(stdout, " None");
1636 if (fec & ETHTOOL_FEC_AUTO)
1637 fprintf(stdout, " Auto");
1638 if (fec & ETHTOOL_FEC_OFF)
1639 fprintf(stdout, " Off");
1640 if (fec & ETHTOOL_FEC_BASER)
1641 fprintf(stdout, " BaseR");
1642 if (fec & ETHTOOL_FEC_RS)
1643 fprintf(stdout, " RS");
1644 if (fec & ETHTOOL_FEC_LLRS)
1645 fprintf(stdout, " LLRS");
1646 }
1647
1648 #define N_SOTS 7
1649
1650 static char *so_timestamping_labels[N_SOTS] = {
1651 "hardware-transmit (SOF_TIMESTAMPING_TX_HARDWARE)",
1652 "software-transmit (SOF_TIMESTAMPING_TX_SOFTWARE)",
1653 "hardware-receive (SOF_TIMESTAMPING_RX_HARDWARE)",
1654 "software-receive (SOF_TIMESTAMPING_RX_SOFTWARE)",
1655 "software-system-clock (SOF_TIMESTAMPING_SOFTWARE)",
1656 "hardware-legacy-clock (SOF_TIMESTAMPING_SYS_HARDWARE)",
1657 "hardware-raw-clock (SOF_TIMESTAMPING_RAW_HARDWARE)",
1658 };
1659
1660 #define N_TX_TYPES (HWTSTAMP_TX_ONESTEP_SYNC + 1)
1661
1662 static char *tx_type_labels[N_TX_TYPES] = {
1663 "off (HWTSTAMP_TX_OFF)",
1664 "on (HWTSTAMP_TX_ON)",
1665 "one-step-sync (HWTSTAMP_TX_ONESTEP_SYNC)",
1666 };
1667
1668 #define N_RX_FILTERS (HWTSTAMP_FILTER_NTP_ALL + 1)
1669
1670 static char *rx_filter_labels[N_RX_FILTERS] = {
1671 "none (HWTSTAMP_FILTER_NONE)",
1672 "all (HWTSTAMP_FILTER_ALL)",
1673 "some (HWTSTAMP_FILTER_SOME)",
1674 "ptpv1-l4-event (HWTSTAMP_FILTER_PTP_V1_L4_EVENT)",
1675 "ptpv1-l4-sync (HWTSTAMP_FILTER_PTP_V1_L4_SYNC)",
1676 "ptpv1-l4-delay-req (HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ)",
1677 "ptpv2-l4-event (HWTSTAMP_FILTER_PTP_V2_L4_EVENT)",
1678 "ptpv2-l4-sync (HWTSTAMP_FILTER_PTP_V2_L4_SYNC)",
1679 "ptpv2-l4-delay-req (HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ)",
1680 "ptpv2-l2-event (HWTSTAMP_FILTER_PTP_V2_L2_EVENT)",
1681 "ptpv2-l2-sync (HWTSTAMP_FILTER_PTP_V2_L2_SYNC)",
1682 "ptpv2-l2-delay-req (HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ)",
1683 "ptpv2-event (HWTSTAMP_FILTER_PTP_V2_EVENT)",
1684 "ptpv2-sync (HWTSTAMP_FILTER_PTP_V2_SYNC)",
1685 "ptpv2-delay-req (HWTSTAMP_FILTER_PTP_V2_DELAY_REQ)",
1686 "ntp-all (HWTSTAMP_FILTER_NTP_ALL)",
1687 };
1688
dump_tsinfo(const struct ethtool_ts_info * info)1689 static int dump_tsinfo(const struct ethtool_ts_info *info)
1690 {
1691 int i;
1692
1693 fprintf(stdout, "Capabilities:\n");
1694
1695 for (i = 0; i < N_SOTS; i++) {
1696 if (info->so_timestamping & (1 << i))
1697 fprintf(stdout, "\t%s\n", so_timestamping_labels[i]);
1698 }
1699
1700 fprintf(stdout, "PTP Hardware Clock: ");
1701
1702 if (info->phc_index < 0)
1703 fprintf(stdout, "none\n");
1704 else
1705 fprintf(stdout, "%d\n", info->phc_index);
1706
1707 fprintf(stdout, "Hardware Transmit Timestamp Modes:");
1708
1709 if (!info->tx_types)
1710 fprintf(stdout, " none\n");
1711 else
1712 fprintf(stdout, "\n");
1713
1714 for (i = 0; i < N_TX_TYPES; i++) {
1715 if (info->tx_types & (1 << i))
1716 fprintf(stdout, "\t%s\n", tx_type_labels[i]);
1717 }
1718
1719 fprintf(stdout, "Hardware Receive Filter Modes:");
1720
1721 if (!info->rx_filters)
1722 fprintf(stdout, " none\n");
1723 else
1724 fprintf(stdout, "\n");
1725
1726 for (i = 0; i < N_RX_FILTERS; i++) {
1727 if (info->rx_filters & (1 << i))
1728 fprintf(stdout, "\t%s\n", rx_filter_labels[i]);
1729 }
1730
1731 return 0;
1732 }
1733
1734 static struct ethtool_gstrings *
get_stringset(struct cmd_context * ctx,enum ethtool_stringset set_id,ptrdiff_t drvinfo_offset,int null_terminate)1735 get_stringset(struct cmd_context *ctx, enum ethtool_stringset set_id,
1736 ptrdiff_t drvinfo_offset, int null_terminate)
1737 {
1738 struct {
1739 struct ethtool_sset_info hdr;
1740 u32 buf[1];
1741 } sset_info;
1742 struct ethtool_drvinfo drvinfo;
1743 u32 len, i;
1744 struct ethtool_gstrings *strings;
1745
1746 sset_info.hdr.cmd = ETHTOOL_GSSET_INFO;
1747 sset_info.hdr.reserved = 0;
1748 sset_info.hdr.sset_mask = 1ULL << set_id;
1749 if (send_ioctl(ctx, &sset_info) == 0) {
1750 const u32 *sset_lengths = sset_info.hdr.data;
1751
1752 len = sset_info.hdr.sset_mask ? sset_lengths[0] : 0;
1753 } else if (errno == EOPNOTSUPP && drvinfo_offset != 0) {
1754 /* Fallback for old kernel versions */
1755 drvinfo.cmd = ETHTOOL_GDRVINFO;
1756 if (send_ioctl(ctx, &drvinfo))
1757 return NULL;
1758 len = *(u32 *)((char *)&drvinfo + drvinfo_offset);
1759 } else {
1760 return NULL;
1761 }
1762
1763 strings = calloc(1, sizeof(*strings) + len * ETH_GSTRING_LEN);
1764 if (!strings)
1765 return NULL;
1766
1767 strings->cmd = ETHTOOL_GSTRINGS;
1768 strings->string_set = set_id;
1769 strings->len = len;
1770 if (len != 0 && send_ioctl(ctx, strings)) {
1771 free(strings);
1772 return NULL;
1773 }
1774
1775 if (null_terminate)
1776 for (i = 0; i < len; i++)
1777 strings->data[(i + 1) * ETH_GSTRING_LEN - 1] = 0;
1778
1779 return strings;
1780 }
1781
get_feature_defs(struct cmd_context * ctx)1782 static struct feature_defs *get_feature_defs(struct cmd_context *ctx)
1783 {
1784 struct ethtool_gstrings *names;
1785 struct feature_defs *defs;
1786 unsigned int i, j;
1787 u32 n_features;
1788
1789 names = get_stringset(ctx, ETH_SS_FEATURES, 0, 1);
1790 if (names) {
1791 n_features = names->len;
1792 } else if (errno == EOPNOTSUPP || errno == EINVAL) {
1793 /* Kernel doesn't support named features; not an error */
1794 n_features = 0;
1795 } else if (errno == EPERM) {
1796 /* Kernel bug: ETHTOOL_GSSET_INFO was privileged.
1797 * Work around it. */
1798 n_features = 0;
1799 } else {
1800 return NULL;
1801 }
1802
1803 defs = malloc(sizeof(*defs) + sizeof(defs->def[0]) * n_features);
1804 if (!defs) {
1805 free(names);
1806 return NULL;
1807 }
1808
1809 defs->n_features = n_features;
1810 memset(defs->off_flag_matched, 0, sizeof(defs->off_flag_matched));
1811
1812 /* Copy out feature names and find those associated with legacy flags */
1813 for (i = 0; i < defs->n_features; i++) {
1814 memcpy(defs->def[i].name, names->data + i * ETH_GSTRING_LEN,
1815 ETH_GSTRING_LEN);
1816 defs->def[i].off_flag_index = -1;
1817
1818 for (j = 0;
1819 j < OFF_FLAG_DEF_SIZE &&
1820 defs->def[i].off_flag_index < 0;
1821 j++) {
1822 const char *pattern =
1823 off_flag_def[j].kernel_name;
1824 const char *name = defs->def[i].name;
1825 for (;;) {
1826 if (*pattern == '*') {
1827 /* There is only one wildcard; so
1828 * switch to a suffix comparison */
1829 size_t pattern_len =
1830 strlen(pattern + 1);
1831 size_t name_len = strlen(name);
1832 if (name_len < pattern_len)
1833 break; /* name is too short */
1834 name += name_len - pattern_len;
1835 ++pattern;
1836 } else if (*pattern != *name) {
1837 break; /* mismatch */
1838 } else if (*pattern == 0) {
1839 defs->def[i].off_flag_index = j;
1840 defs->off_flag_matched[j]++;
1841 break;
1842 } else {
1843 ++name;
1844 ++pattern;
1845 }
1846 }
1847 }
1848 }
1849
1850 free(names);
1851 return defs;
1852 }
1853
do_gdrv(struct cmd_context * ctx)1854 static int do_gdrv(struct cmd_context *ctx)
1855 {
1856 int err;
1857 struct ethtool_drvinfo drvinfo;
1858
1859 if (ctx->argc != 0)
1860 exit_bad_args();
1861
1862 drvinfo.cmd = ETHTOOL_GDRVINFO;
1863 err = send_ioctl(ctx, &drvinfo);
1864 if (err < 0) {
1865 perror("Cannot get driver information");
1866 return 71;
1867 }
1868 return dump_drvinfo(&drvinfo);
1869 }
1870
do_gpause(struct cmd_context * ctx)1871 static int do_gpause(struct cmd_context *ctx)
1872 {
1873 struct ethtool_pauseparam epause;
1874 struct ethtool_cmd ecmd;
1875 int err;
1876
1877 if (ctx->argc != 0)
1878 exit_bad_args();
1879
1880 fprintf(stdout, "Pause parameters for %s:\n", ctx->devname);
1881
1882 epause.cmd = ETHTOOL_GPAUSEPARAM;
1883 err = send_ioctl(ctx, &epause);
1884 if (err) {
1885 perror("Cannot get device pause settings");
1886 return 76;
1887 }
1888
1889 if (epause.autoneg) {
1890 ecmd.cmd = ETHTOOL_GSET;
1891 err = send_ioctl(ctx, &ecmd);
1892 if (err) {
1893 perror("Cannot get device settings");
1894 return 1;
1895 }
1896 dump_pause(&epause, ecmd.advertising, ecmd.lp_advertising);
1897 } else {
1898 dump_pause(&epause, 0, 0);
1899 }
1900
1901 return 0;
1902 }
1903
do_generic_set1(struct cmdline_info * info,int * changed_out)1904 static void do_generic_set1(struct cmdline_info *info, int *changed_out)
1905 {
1906 int wanted, *v1, *v2;
1907
1908 v1 = info->wanted_val;
1909 wanted = *v1;
1910
1911 if (wanted < 0)
1912 return;
1913
1914 v2 = info->ioctl_val;
1915 if (wanted == *v2) {
1916 fprintf(stderr, "%s unmodified, ignoring\n", info->name);
1917 } else {
1918 *v2 = wanted;
1919 *changed_out = 1;
1920 }
1921 }
1922
do_generic_set(struct cmdline_info * info,unsigned int n_info,int * changed_out)1923 static void do_generic_set(struct cmdline_info *info,
1924 unsigned int n_info,
1925 int *changed_out)
1926 {
1927 unsigned int i;
1928
1929 for (i = 0; i < n_info; i++)
1930 do_generic_set1(&info[i], changed_out);
1931 }
1932
do_spause(struct cmd_context * ctx)1933 static int do_spause(struct cmd_context *ctx)
1934 {
1935 struct ethtool_pauseparam epause;
1936 int gpause_changed = 0;
1937 int pause_autoneg_wanted = -1;
1938 int pause_rx_wanted = -1;
1939 int pause_tx_wanted = -1;
1940 struct cmdline_info cmdline_pause[] = {
1941 {
1942 .name = "autoneg",
1943 .type = CMDL_BOOL,
1944 .wanted_val = &pause_autoneg_wanted,
1945 .ioctl_val = &epause.autoneg,
1946 },
1947 {
1948 .name = "rx",
1949 .type = CMDL_BOOL,
1950 .wanted_val = &pause_rx_wanted,
1951 .ioctl_val = &epause.rx_pause,
1952 },
1953 {
1954 .name = "tx",
1955 .type = CMDL_BOOL,
1956 .wanted_val = &pause_tx_wanted,
1957 .ioctl_val = &epause.tx_pause,
1958 },
1959 };
1960 int err, changed = 0;
1961
1962 parse_generic_cmdline(ctx, &gpause_changed,
1963 cmdline_pause, ARRAY_SIZE(cmdline_pause));
1964
1965 epause.cmd = ETHTOOL_GPAUSEPARAM;
1966 err = send_ioctl(ctx, &epause);
1967 if (err) {
1968 perror("Cannot get device pause settings");
1969 return 77;
1970 }
1971
1972 do_generic_set(cmdline_pause, ARRAY_SIZE(cmdline_pause), &changed);
1973
1974 if (!changed) {
1975 fprintf(stderr, "no pause parameters changed, aborting\n");
1976 return 78;
1977 }
1978
1979 epause.cmd = ETHTOOL_SPAUSEPARAM;
1980 err = send_ioctl(ctx, &epause);
1981 if (err) {
1982 perror("Cannot set device pause parameters");
1983 return 79;
1984 }
1985
1986 return 0;
1987 }
1988
do_sring(struct cmd_context * ctx)1989 static int do_sring(struct cmd_context *ctx)
1990 {
1991 struct ethtool_ringparam ering;
1992 int gring_changed = 0;
1993 s32 ring_rx_wanted = -1;
1994 s32 ring_rx_mini_wanted = -1;
1995 s32 ring_rx_jumbo_wanted = -1;
1996 s32 ring_tx_wanted = -1;
1997 struct cmdline_info cmdline_ring[] = {
1998 {
1999 .name = "rx",
2000 .type = CMDL_S32,
2001 .wanted_val = &ring_rx_wanted,
2002 .ioctl_val = &ering.rx_pending,
2003 },
2004 {
2005 .name = "rx-mini",
2006 .type = CMDL_S32,
2007 .wanted_val = &ring_rx_mini_wanted,
2008 .ioctl_val = &ering.rx_mini_pending,
2009 },
2010 {
2011 .name = "rx-jumbo",
2012 .type = CMDL_S32,
2013 .wanted_val = &ring_rx_jumbo_wanted,
2014 .ioctl_val = &ering.rx_jumbo_pending,
2015 },
2016 {
2017 .name = "tx",
2018 .type = CMDL_S32,
2019 .wanted_val = &ring_tx_wanted,
2020 .ioctl_val = &ering.tx_pending,
2021 },
2022 };
2023 int err, changed = 0;
2024
2025 parse_generic_cmdline(ctx, &gring_changed,
2026 cmdline_ring, ARRAY_SIZE(cmdline_ring));
2027
2028 ering.cmd = ETHTOOL_GRINGPARAM;
2029 err = send_ioctl(ctx, &ering);
2030 if (err) {
2031 perror("Cannot get device ring settings");
2032 return 76;
2033 }
2034
2035 do_generic_set(cmdline_ring, ARRAY_SIZE(cmdline_ring), &changed);
2036
2037 if (!changed) {
2038 fprintf(stderr, "no ring parameters changed, aborting\n");
2039 return 80;
2040 }
2041
2042 ering.cmd = ETHTOOL_SRINGPARAM;
2043 err = send_ioctl(ctx, &ering);
2044 if (err) {
2045 perror("Cannot set device ring parameters");
2046 return 81;
2047 }
2048
2049 return 0;
2050 }
2051
do_gring(struct cmd_context * ctx)2052 static int do_gring(struct cmd_context *ctx)
2053 {
2054 struct ethtool_ringparam ering;
2055 int err;
2056
2057 if (ctx->argc != 0)
2058 exit_bad_args();
2059
2060 fprintf(stdout, "Ring parameters for %s:\n", ctx->devname);
2061
2062 ering.cmd = ETHTOOL_GRINGPARAM;
2063 err = send_ioctl(ctx, &ering);
2064 if (err == 0) {
2065 err = dump_ring(&ering);
2066 if (err)
2067 return err;
2068 } else {
2069 perror("Cannot get device ring settings");
2070 return 76;
2071 }
2072
2073 return 0;
2074 }
2075
do_schannels(struct cmd_context * ctx)2076 static int do_schannels(struct cmd_context *ctx)
2077 {
2078 struct ethtool_channels echannels;
2079 int gchannels_changed;
2080 s32 channels_rx_wanted = -1;
2081 s32 channels_tx_wanted = -1;
2082 s32 channels_other_wanted = -1;
2083 s32 channels_combined_wanted = -1;
2084 struct cmdline_info cmdline_channels[] = {
2085 {
2086 .name = "rx",
2087 .type = CMDL_S32,
2088 .wanted_val = &channels_rx_wanted,
2089 .ioctl_val = &echannels.rx_count,
2090 },
2091 {
2092 .name = "tx",
2093 .type = CMDL_S32,
2094 .wanted_val = &channels_tx_wanted,
2095 .ioctl_val = &echannels.tx_count,
2096 },
2097 {
2098 .name = "other",
2099 .type = CMDL_S32,
2100 .wanted_val = &channels_other_wanted,
2101 .ioctl_val = &echannels.other_count,
2102 },
2103 {
2104 .name = "combined",
2105 .type = CMDL_S32,
2106 .wanted_val = &channels_combined_wanted,
2107 .ioctl_val = &echannels.combined_count,
2108 },
2109 };
2110 int err, changed = 0;
2111
2112 parse_generic_cmdline(ctx, &gchannels_changed,
2113 cmdline_channels, ARRAY_SIZE(cmdline_channels));
2114
2115 echannels.cmd = ETHTOOL_GCHANNELS;
2116 err = send_ioctl(ctx, &echannels);
2117 if (err) {
2118 perror("Cannot get device channel parameters");
2119 return 1;
2120 }
2121
2122 do_generic_set(cmdline_channels, ARRAY_SIZE(cmdline_channels),
2123 &changed);
2124
2125 if (!changed) {
2126 fprintf(stderr, "no channel parameters changed.\n");
2127 fprintf(stderr, "current values: rx %u tx %u other %u"
2128 " combined %u\n", echannels.rx_count,
2129 echannels.tx_count, echannels.other_count,
2130 echannels.combined_count);
2131 return 0;
2132 }
2133
2134 echannels.cmd = ETHTOOL_SCHANNELS;
2135 err = send_ioctl(ctx, &echannels);
2136 if (err) {
2137 perror("Cannot set device channel parameters");
2138 return 1;
2139 }
2140
2141 return 0;
2142 }
2143
do_gchannels(struct cmd_context * ctx)2144 static int do_gchannels(struct cmd_context *ctx)
2145 {
2146 struct ethtool_channels echannels;
2147 int err;
2148
2149 if (ctx->argc != 0)
2150 exit_bad_args();
2151
2152 fprintf(stdout, "Channel parameters for %s:\n", ctx->devname);
2153
2154 echannels.cmd = ETHTOOL_GCHANNELS;
2155 err = send_ioctl(ctx, &echannels);
2156 if (err == 0) {
2157 err = dump_channels(&echannels);
2158 if (err)
2159 return err;
2160 } else {
2161 perror("Cannot get device channel parameters");
2162 return 1;
2163 }
2164 return 0;
2165
2166 }
2167
do_gcoalesce(struct cmd_context * ctx)2168 static int do_gcoalesce(struct cmd_context *ctx)
2169 {
2170 struct ethtool_coalesce ecoal = {};
2171 int err;
2172
2173 if (ctx->argc != 0)
2174 exit_bad_args();
2175
2176 fprintf(stdout, "Coalesce parameters for %s:\n", ctx->devname);
2177
2178 ecoal.cmd = ETHTOOL_GCOALESCE;
2179 err = send_ioctl(ctx, &ecoal);
2180 if (err == 0) {
2181 err = dump_coalesce(&ecoal);
2182 if (err)
2183 return err;
2184 } else {
2185 perror("Cannot get device coalesce settings");
2186 return 82;
2187 }
2188
2189 return 0;
2190 }
2191
2192 #define DECLARE_COALESCE_OPTION_VARS() \
2193 s32 coal_stats_wanted = -1; \
2194 int coal_adaptive_rx_wanted = -1; \
2195 int coal_adaptive_tx_wanted = -1; \
2196 s32 coal_sample_rate_wanted = -1; \
2197 s32 coal_pkt_rate_low_wanted = -1; \
2198 s32 coal_pkt_rate_high_wanted = -1; \
2199 s32 coal_rx_usec_wanted = -1; \
2200 s32 coal_rx_frames_wanted = -1; \
2201 s32 coal_rx_usec_irq_wanted = -1; \
2202 s32 coal_rx_frames_irq_wanted = -1; \
2203 s32 coal_tx_usec_wanted = -1; \
2204 s32 coal_tx_frames_wanted = -1; \
2205 s32 coal_tx_usec_irq_wanted = -1; \
2206 s32 coal_tx_frames_irq_wanted = -1; \
2207 s32 coal_rx_usec_low_wanted = -1; \
2208 s32 coal_rx_frames_low_wanted = -1; \
2209 s32 coal_tx_usec_low_wanted = -1; \
2210 s32 coal_tx_frames_low_wanted = -1; \
2211 s32 coal_rx_usec_high_wanted = -1; \
2212 s32 coal_rx_frames_high_wanted = -1; \
2213 s32 coal_tx_usec_high_wanted = -1; \
2214 s32 coal_tx_frames_high_wanted = -1
2215
2216 #define COALESCE_CMDLINE_INFO(__ecoal) \
2217 { \
2218 { \
2219 .name = "adaptive-rx", \
2220 .type = CMDL_BOOL, \
2221 .wanted_val = &coal_adaptive_rx_wanted, \
2222 .ioctl_val = &__ecoal.use_adaptive_rx_coalesce, \
2223 }, \
2224 { \
2225 .name = "adaptive-tx", \
2226 .type = CMDL_BOOL, \
2227 .wanted_val = &coal_adaptive_tx_wanted, \
2228 .ioctl_val = &__ecoal.use_adaptive_tx_coalesce, \
2229 }, \
2230 { \
2231 .name = "sample-interval", \
2232 .type = CMDL_S32, \
2233 .wanted_val = &coal_sample_rate_wanted, \
2234 .ioctl_val = &__ecoal.rate_sample_interval, \
2235 }, \
2236 { \
2237 .name = "stats-block-usecs", \
2238 .type = CMDL_S32, \
2239 .wanted_val = &coal_stats_wanted, \
2240 .ioctl_val = &__ecoal.stats_block_coalesce_usecs, \
2241 }, \
2242 { \
2243 .name = "pkt-rate-low", \
2244 .type = CMDL_S32, \
2245 .wanted_val = &coal_pkt_rate_low_wanted, \
2246 .ioctl_val = &__ecoal.pkt_rate_low, \
2247 }, \
2248 { \
2249 .name = "pkt-rate-high", \
2250 .type = CMDL_S32, \
2251 .wanted_val = &coal_pkt_rate_high_wanted, \
2252 .ioctl_val = &__ecoal.pkt_rate_high, \
2253 }, \
2254 { \
2255 .name = "rx-usecs", \
2256 .type = CMDL_S32, \
2257 .wanted_val = &coal_rx_usec_wanted, \
2258 .ioctl_val = &__ecoal.rx_coalesce_usecs, \
2259 }, \
2260 { \
2261 .name = "rx-frames", \
2262 .type = CMDL_S32, \
2263 .wanted_val = &coal_rx_frames_wanted, \
2264 .ioctl_val = &__ecoal.rx_max_coalesced_frames, \
2265 }, \
2266 { \
2267 .name = "rx-usecs-irq", \
2268 .type = CMDL_S32, \
2269 .wanted_val = &coal_rx_usec_irq_wanted, \
2270 .ioctl_val = &__ecoal.rx_coalesce_usecs_irq, \
2271 }, \
2272 { \
2273 .name = "rx-frames-irq", \
2274 .type = CMDL_S32, \
2275 .wanted_val = &coal_rx_frames_irq_wanted, \
2276 .ioctl_val = &__ecoal.rx_max_coalesced_frames_irq, \
2277 }, \
2278 { \
2279 .name = "tx-usecs", \
2280 .type = CMDL_S32, \
2281 .wanted_val = &coal_tx_usec_wanted, \
2282 .ioctl_val = &__ecoal.tx_coalesce_usecs, \
2283 }, \
2284 { \
2285 .name = "tx-frames", \
2286 .type = CMDL_S32, \
2287 .wanted_val = &coal_tx_frames_wanted, \
2288 .ioctl_val = &__ecoal.tx_max_coalesced_frames, \
2289 }, \
2290 { \
2291 .name = "tx-usecs-irq", \
2292 .type = CMDL_S32, \
2293 .wanted_val = &coal_tx_usec_irq_wanted, \
2294 .ioctl_val = &__ecoal.tx_coalesce_usecs_irq, \
2295 }, \
2296 { \
2297 .name = "tx-frames-irq", \
2298 .type = CMDL_S32, \
2299 .wanted_val = &coal_tx_frames_irq_wanted, \
2300 .ioctl_val = &__ecoal.tx_max_coalesced_frames_irq, \
2301 }, \
2302 { \
2303 .name = "rx-usecs-low", \
2304 .type = CMDL_S32, \
2305 .wanted_val = &coal_rx_usec_low_wanted, \
2306 .ioctl_val = &__ecoal.rx_coalesce_usecs_low, \
2307 }, \
2308 { \
2309 .name = "rx-frames-low", \
2310 .type = CMDL_S32, \
2311 .wanted_val = &coal_rx_frames_low_wanted, \
2312 .ioctl_val = &__ecoal.rx_max_coalesced_frames_low, \
2313 }, \
2314 { \
2315 .name = "tx-usecs-low", \
2316 .type = CMDL_S32, \
2317 .wanted_val = &coal_tx_usec_low_wanted, \
2318 .ioctl_val = &__ecoal.tx_coalesce_usecs_low, \
2319 }, \
2320 { \
2321 .name = "tx-frames-low", \
2322 .type = CMDL_S32, \
2323 .wanted_val = &coal_tx_frames_low_wanted, \
2324 .ioctl_val = &__ecoal.tx_max_coalesced_frames_low, \
2325 }, \
2326 { \
2327 .name = "rx-usecs-high", \
2328 .type = CMDL_S32, \
2329 .wanted_val = &coal_rx_usec_high_wanted, \
2330 .ioctl_val = &__ecoal.rx_coalesce_usecs_high, \
2331 }, \
2332 { \
2333 .name = "rx-frames-high", \
2334 .type = CMDL_S32, \
2335 .wanted_val = &coal_rx_frames_high_wanted, \
2336 .ioctl_val = &__ecoal.rx_max_coalesced_frames_high,\
2337 }, \
2338 { \
2339 .name = "tx-usecs-high", \
2340 .type = CMDL_S32, \
2341 .wanted_val = &coal_tx_usec_high_wanted, \
2342 .ioctl_val = &__ecoal.tx_coalesce_usecs_high, \
2343 }, \
2344 { \
2345 .name = "tx-frames-high", \
2346 .type = CMDL_S32, \
2347 .wanted_val = &coal_tx_frames_high_wanted, \
2348 .ioctl_val = &__ecoal.tx_max_coalesced_frames_high,\
2349 }, \
2350 }
2351
do_scoalesce(struct cmd_context * ctx)2352 static int do_scoalesce(struct cmd_context *ctx)
2353 {
2354 struct ethtool_coalesce ecoal;
2355 int gcoalesce_changed = 0;
2356 DECLARE_COALESCE_OPTION_VARS();
2357 struct cmdline_info cmdline_coalesce[] = COALESCE_CMDLINE_INFO(ecoal);
2358 int err, changed = 0;
2359
2360 parse_generic_cmdline(ctx, &gcoalesce_changed,
2361 cmdline_coalesce, ARRAY_SIZE(cmdline_coalesce));
2362
2363 ecoal.cmd = ETHTOOL_GCOALESCE;
2364 err = send_ioctl(ctx, &ecoal);
2365 if (err) {
2366 perror("Cannot get device coalesce settings");
2367 return 76;
2368 }
2369
2370 do_generic_set(cmdline_coalesce, ARRAY_SIZE(cmdline_coalesce),
2371 &changed);
2372
2373 if (!changed) {
2374 fprintf(stderr, "no coalesce parameters changed, aborting\n");
2375 return 80;
2376 }
2377
2378 ecoal.cmd = ETHTOOL_SCOALESCE;
2379 err = send_ioctl(ctx, &ecoal);
2380 if (err) {
2381 perror("Cannot set device coalesce parameters");
2382 return 81;
2383 }
2384
2385 return 0;
2386 }
2387
2388 static struct feature_state *
get_features(struct cmd_context * ctx,const struct feature_defs * defs)2389 get_features(struct cmd_context *ctx, const struct feature_defs *defs)
2390 {
2391 struct feature_state *state;
2392 struct ethtool_value eval;
2393 int err, allfail = 1;
2394 u32 value;
2395 int i;
2396
2397 state = malloc(sizeof(*state) +
2398 FEATURE_BITS_TO_BLOCKS(defs->n_features) *
2399 sizeof(state->features.features[0]));
2400 if (!state)
2401 return NULL;
2402
2403 state->off_flags = 0;
2404
2405 for (i = 0; i < OFF_FLAG_DEF_SIZE; i++) {
2406 value = off_flag_def[i].value;
2407 if (!off_flag_def[i].get_cmd)
2408 continue;
2409 eval.cmd = off_flag_def[i].get_cmd;
2410 err = send_ioctl(ctx, &eval);
2411 if (err) {
2412 if (errno == EOPNOTSUPP &&
2413 off_flag_def[i].get_cmd == ETHTOOL_GUFO)
2414 continue;
2415
2416 fprintf(stderr,
2417 "Cannot get device %s settings: %m\n",
2418 off_flag_def[i].long_name);
2419 } else {
2420 if (eval.data)
2421 state->off_flags |= value;
2422 allfail = 0;
2423 }
2424 }
2425
2426 eval.cmd = ETHTOOL_GFLAGS;
2427 err = send_ioctl(ctx, &eval);
2428 if (err) {
2429 perror("Cannot get device flags");
2430 } else {
2431 state->off_flags |= eval.data & ETH_FLAG_EXT_MASK;
2432 allfail = 0;
2433 }
2434
2435 if (defs->n_features) {
2436 state->features.cmd = ETHTOOL_GFEATURES;
2437 state->features.size = FEATURE_BITS_TO_BLOCKS(defs->n_features);
2438 err = send_ioctl(ctx, &state->features);
2439 if (err)
2440 perror("Cannot get device generic features");
2441 else
2442 allfail = 0;
2443 }
2444
2445 if (allfail) {
2446 free(state);
2447 return NULL;
2448 }
2449
2450 return state;
2451 }
2452
do_gfeatures(struct cmd_context * ctx)2453 static int do_gfeatures(struct cmd_context *ctx)
2454 {
2455 struct feature_defs *defs;
2456 struct feature_state *features;
2457
2458 if (ctx->argc != 0)
2459 exit_bad_args();
2460
2461 defs = get_feature_defs(ctx);
2462 if (!defs) {
2463 perror("Cannot get device feature names");
2464 return 1;
2465 }
2466
2467 fprintf(stdout, "Features for %s:\n", ctx->devname);
2468
2469 features = get_features(ctx, defs);
2470 if (!features) {
2471 fprintf(stdout, "no feature info available\n");
2472 free(defs);
2473 return 1;
2474 }
2475
2476 dump_features(defs, features, NULL);
2477 free(features);
2478 free(defs);
2479 return 0;
2480 }
2481
do_sfeatures(struct cmd_context * ctx)2482 static int do_sfeatures(struct cmd_context *ctx)
2483 {
2484 struct feature_defs *defs;
2485 int any_changed = 0, any_mismatch = 0;
2486 u32 off_flags_wanted = 0;
2487 u32 off_flags_mask = 0;
2488 struct ethtool_sfeatures *efeatures = NULL;
2489 struct feature_state *old_state = NULL;
2490 struct feature_state *new_state = NULL;
2491 struct cmdline_info *cmdline_features;
2492 struct ethtool_value eval;
2493 unsigned int i, j;
2494 int err, rc;
2495
2496 defs = get_feature_defs(ctx);
2497 if (!defs) {
2498 perror("Cannot get device feature names");
2499 return 1;
2500 }
2501 if (defs->n_features) {
2502 efeatures = malloc(sizeof(*efeatures) +
2503 FEATURE_BITS_TO_BLOCKS(defs->n_features) *
2504 sizeof(efeatures->features[0]));
2505 if (!efeatures) {
2506 perror("Cannot parse arguments");
2507 rc = 1;
2508 goto err;
2509 }
2510 efeatures->cmd = ETHTOOL_SFEATURES;
2511 efeatures->size = FEATURE_BITS_TO_BLOCKS(defs->n_features);
2512 memset(efeatures->features, 0,
2513 FEATURE_BITS_TO_BLOCKS(defs->n_features) *
2514 sizeof(efeatures->features[0]));
2515 }
2516
2517 /* Generate cmdline_info for legacy flags and kernel-named
2518 * features, and parse our arguments.
2519 */
2520 cmdline_features = calloc(2 * OFF_FLAG_DEF_SIZE + defs->n_features,
2521 sizeof(cmdline_features[0]));
2522 if (!cmdline_features) {
2523 perror("Cannot parse arguments");
2524 rc = 1;
2525 goto err;
2526 }
2527 j = 0;
2528 for (i = 0; i < OFF_FLAG_DEF_SIZE; i++) {
2529 flag_to_cmdline_info(off_flag_def[i].short_name,
2530 off_flag_def[i].value,
2531 &off_flags_wanted, &off_flags_mask,
2532 &cmdline_features[j++]);
2533 flag_to_cmdline_info(off_flag_def[i].long_name,
2534 off_flag_def[i].value,
2535 &off_flags_wanted, &off_flags_mask,
2536 &cmdline_features[j++]);
2537 }
2538 for (i = 0; i < defs->n_features; i++)
2539 flag_to_cmdline_info(
2540 defs->def[i].name, FEATURE_FIELD_FLAG(i),
2541 &FEATURE_WORD(efeatures->features, i, requested),
2542 &FEATURE_WORD(efeatures->features, i, valid),
2543 &cmdline_features[j++]);
2544 parse_generic_cmdline(ctx, &any_changed, cmdline_features,
2545 2 * OFF_FLAG_DEF_SIZE + defs->n_features);
2546 free(cmdline_features);
2547
2548 if (!any_changed) {
2549 fprintf(stdout, "no features changed\n");
2550 rc = 0;
2551 goto err;
2552 }
2553
2554 old_state = get_features(ctx, defs);
2555 if (!old_state) {
2556 rc = 1;
2557 goto err;
2558 }
2559
2560 if (efeatures) {
2561 /* For each offload that the user specified, update any
2562 * related features that the user did not specify and that
2563 * are not fixed. Warn if all related features are fixed.
2564 */
2565 for (i = 0; i < OFF_FLAG_DEF_SIZE; i++) {
2566 int fixed = 1;
2567
2568 if (!(off_flags_mask & off_flag_def[i].value))
2569 continue;
2570
2571 for (j = 0; j < defs->n_features; j++) {
2572 if (defs->def[j].off_flag_index != (int)i ||
2573 !FEATURE_BIT_IS_SET(
2574 old_state->features.features,
2575 j, available) ||
2576 FEATURE_BIT_IS_SET(
2577 old_state->features.features,
2578 j, never_changed))
2579 continue;
2580
2581 fixed = 0;
2582 if (!FEATURE_BIT_IS_SET(efeatures->features,
2583 j, valid)) {
2584 FEATURE_BIT_SET(efeatures->features,
2585 j, valid);
2586 if (off_flags_wanted &
2587 off_flag_def[i].value)
2588 FEATURE_BIT_SET(
2589 efeatures->features,
2590 j, requested);
2591 }
2592 }
2593
2594 if (fixed)
2595 fprintf(stderr, "Cannot change %s\n",
2596 off_flag_def[i].long_name);
2597 }
2598
2599 err = send_ioctl(ctx, efeatures);
2600 if (err < 0) {
2601 perror("Cannot set device feature settings");
2602 rc = 1;
2603 goto err;
2604 }
2605 } else {
2606 for (i = 0; i < OFF_FLAG_DEF_SIZE; i++) {
2607 if (!off_flag_def[i].set_cmd)
2608 continue;
2609 if (off_flags_mask & off_flag_def[i].value) {
2610 eval.cmd = off_flag_def[i].set_cmd;
2611 eval.data = !!(off_flags_wanted &
2612 off_flag_def[i].value);
2613 err = send_ioctl(ctx, &eval);
2614 if (err) {
2615 fprintf(stderr,
2616 "Cannot set device %s settings: %m\n",
2617 off_flag_def[i].long_name);
2618 rc = 1;
2619 goto err;
2620 }
2621 }
2622 }
2623
2624 if (off_flags_mask & ETH_FLAG_EXT_MASK) {
2625 eval.cmd = ETHTOOL_SFLAGS;
2626 eval.data = (old_state->off_flags & ~off_flags_mask &
2627 ETH_FLAG_EXT_MASK);
2628 eval.data |= off_flags_wanted & ETH_FLAG_EXT_MASK;
2629
2630 err = send_ioctl(ctx, &eval);
2631 if (err) {
2632 perror("Cannot set device flag settings");
2633 rc = 92;
2634 goto err;
2635 }
2636 }
2637 }
2638
2639 /* Compare new state with requested state */
2640 new_state = get_features(ctx, defs);
2641 if (!new_state) {
2642 rc = 1;
2643 goto err;
2644 }
2645 any_changed = new_state->off_flags != old_state->off_flags;
2646 any_mismatch = (new_state->off_flags !=
2647 ((old_state->off_flags & ~off_flags_mask) |
2648 off_flags_wanted));
2649 for (i = 0; i < FEATURE_BITS_TO_BLOCKS(defs->n_features); i++) {
2650 if (new_state->features.features[i].active !=
2651 old_state->features.features[i].active)
2652 any_changed = 1;
2653 if (new_state->features.features[i].active !=
2654 ((old_state->features.features[i].active &
2655 ~efeatures->features[i].valid) |
2656 efeatures->features[i].requested))
2657 any_mismatch = 1;
2658 }
2659 if (any_mismatch) {
2660 if (!any_changed) {
2661 fprintf(stderr,
2662 "Could not change any device features\n");
2663 rc = 1;
2664 goto err;
2665 }
2666 printf("Actual changes:\n");
2667 dump_features(defs, new_state, old_state);
2668 }
2669
2670 rc = 0;
2671
2672 err:
2673 free(new_state);
2674 free(old_state);
2675 free(defs);
2676 free(efeatures);
2677
2678 return rc;
2679 }
2680
2681 static struct ethtool_link_usettings *
do_ioctl_glinksettings(struct cmd_context * ctx)2682 do_ioctl_glinksettings(struct cmd_context *ctx)
2683 {
2684 int err;
2685 struct {
2686 struct ethtool_link_settings req;
2687 __u32 link_mode_data[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32];
2688 } ecmd;
2689 struct ethtool_link_usettings *link_usettings;
2690 unsigned int u32_offs;
2691
2692 /* Handshake with kernel to determine number of words for link
2693 * mode bitmaps. When requested number of bitmap words is not
2694 * the one expected by kernel, the latter returns the integer
2695 * opposite of what it is expecting. We request length 0 below
2696 * (aka. invalid bitmap length) to get this info.
2697 */
2698 memset(&ecmd, 0, sizeof(ecmd));
2699 ecmd.req.cmd = ETHTOOL_GLINKSETTINGS;
2700 err = send_ioctl(ctx, &ecmd);
2701 if (err < 0)
2702 return NULL;
2703
2704 /* see above: we expect a strictly negative value from kernel.
2705 */
2706 if (ecmd.req.link_mode_masks_nwords >= 0
2707 || ecmd.req.cmd != ETHTOOL_GLINKSETTINGS)
2708 return NULL;
2709
2710 /* got the real ecmd.req.link_mode_masks_nwords,
2711 * now send the real request
2712 */
2713 ecmd.req.cmd = ETHTOOL_GLINKSETTINGS;
2714 ecmd.req.link_mode_masks_nwords = -ecmd.req.link_mode_masks_nwords;
2715 err = send_ioctl(ctx, &ecmd);
2716 if (err < 0)
2717 return NULL;
2718
2719 if (ecmd.req.link_mode_masks_nwords <= 0
2720 || ecmd.req.cmd != ETHTOOL_GLINKSETTINGS)
2721 return NULL;
2722
2723 /* Convert to usettings struct */
2724 link_usettings = calloc(1, sizeof(*link_usettings));
2725 if (link_usettings == NULL)
2726 return NULL;
2727
2728 memcpy(&link_usettings->base, &ecmd.req, sizeof(link_usettings->base));
2729 link_usettings->deprecated.transceiver = ecmd.req.transceiver;
2730
2731 /* copy link mode bitmaps */
2732 u32_offs = 0;
2733 memcpy(link_usettings->link_modes.supported,
2734 &ecmd.link_mode_data[u32_offs],
2735 4 * ecmd.req.link_mode_masks_nwords);
2736
2737 u32_offs += ecmd.req.link_mode_masks_nwords;
2738 memcpy(link_usettings->link_modes.advertising,
2739 &ecmd.link_mode_data[u32_offs],
2740 4 * ecmd.req.link_mode_masks_nwords);
2741
2742 u32_offs += ecmd.req.link_mode_masks_nwords;
2743 memcpy(link_usettings->link_modes.lp_advertising,
2744 &ecmd.link_mode_data[u32_offs],
2745 4 * ecmd.req.link_mode_masks_nwords);
2746
2747 return link_usettings;
2748 }
2749
2750 static int
do_ioctl_slinksettings(struct cmd_context * ctx,const struct ethtool_link_usettings * link_usettings)2751 do_ioctl_slinksettings(struct cmd_context *ctx,
2752 const struct ethtool_link_usettings *link_usettings)
2753 {
2754 struct {
2755 struct ethtool_link_settings req;
2756 __u32 link_mode_data[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32];
2757 } ecmd;
2758 unsigned int u32_offs;
2759
2760 /* refuse to send ETHTOOL_SLINKSETTINGS ioctl if
2761 * link_usettings was retrieved with ETHTOOL_GSET
2762 */
2763 if (link_usettings->base.cmd != ETHTOOL_GLINKSETTINGS)
2764 return -1;
2765
2766 /* refuse to send ETHTOOL_SLINKSETTINGS ioctl if deprecated fields
2767 * were set
2768 */
2769 if (link_usettings->deprecated.transceiver)
2770 return -1;
2771
2772 if (link_usettings->base.link_mode_masks_nwords <= 0)
2773 return -1;
2774
2775 memcpy(&ecmd.req, &link_usettings->base, sizeof(ecmd.req));
2776 ecmd.req.cmd = ETHTOOL_SLINKSETTINGS;
2777
2778 /* copy link mode bitmaps */
2779 u32_offs = 0;
2780 memcpy(&ecmd.link_mode_data[u32_offs],
2781 link_usettings->link_modes.supported,
2782 4 * ecmd.req.link_mode_masks_nwords);
2783
2784 u32_offs += ecmd.req.link_mode_masks_nwords;
2785 memcpy(&ecmd.link_mode_data[u32_offs],
2786 link_usettings->link_modes.advertising,
2787 4 * ecmd.req.link_mode_masks_nwords);
2788
2789 u32_offs += ecmd.req.link_mode_masks_nwords;
2790 memcpy(&ecmd.link_mode_data[u32_offs],
2791 link_usettings->link_modes.lp_advertising,
2792 4 * ecmd.req.link_mode_masks_nwords);
2793
2794 return send_ioctl(ctx, &ecmd);
2795 }
2796
2797 static struct ethtool_link_usettings *
do_ioctl_gset(struct cmd_context * ctx)2798 do_ioctl_gset(struct cmd_context *ctx)
2799 {
2800 int err;
2801 struct ethtool_cmd ecmd;
2802 struct ethtool_link_usettings *link_usettings;
2803
2804 memset(&ecmd, 0, sizeof(ecmd));
2805 ecmd.cmd = ETHTOOL_GSET;
2806 err = send_ioctl(ctx, &ecmd);
2807 if (err < 0)
2808 return NULL;
2809
2810 link_usettings = calloc(1, sizeof(*link_usettings));
2811 if (link_usettings == NULL)
2812 return NULL;
2813
2814 /* remember that ETHTOOL_GSET was used */
2815 link_usettings->base.cmd = ETHTOOL_GSET;
2816
2817 link_usettings->base.link_mode_masks_nwords = 1;
2818 link_usettings->link_modes.supported[0] = ecmd.supported;
2819 link_usettings->link_modes.advertising[0] = ecmd.advertising;
2820 link_usettings->link_modes.lp_advertising[0] = ecmd.lp_advertising;
2821 link_usettings->base.speed = ethtool_cmd_speed(&ecmd);
2822 link_usettings->base.duplex = ecmd.duplex;
2823 link_usettings->base.port = ecmd.port;
2824 link_usettings->base.phy_address = ecmd.phy_address;
2825 link_usettings->deprecated.transceiver = ecmd.transceiver;
2826 link_usettings->base.autoneg = ecmd.autoneg;
2827 link_usettings->base.mdio_support = ecmd.mdio_support;
2828 /* ignored (fully deprecated): maxrxpkt, maxtxpkt */
2829 link_usettings->base.eth_tp_mdix = ecmd.eth_tp_mdix;
2830 link_usettings->base.eth_tp_mdix_ctrl = ecmd.eth_tp_mdix_ctrl;
2831
2832 return link_usettings;
2833 }
2834
ethtool_link_mode_is_backward_compatible(const u32 * mask)2835 static bool ethtool_link_mode_is_backward_compatible(const u32 *mask)
2836 {
2837 unsigned int i;
2838
2839 for (i = 1; i < ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32; ++i)
2840 if (mask[i])
2841 return false;
2842
2843 return true;
2844 }
2845
2846 static int
do_ioctl_sset(struct cmd_context * ctx,const struct ethtool_link_usettings * link_usettings)2847 do_ioctl_sset(struct cmd_context *ctx,
2848 const struct ethtool_link_usettings *link_usettings)
2849 {
2850 struct ethtool_cmd ecmd;
2851
2852 /* refuse to send ETHTOOL_SSET ioctl if link_usettings was
2853 * retrieved with ETHTOOL_GLINKSETTINGS
2854 */
2855 if (link_usettings->base.cmd != ETHTOOL_GSET)
2856 return -1;
2857
2858 if (link_usettings->base.link_mode_masks_nwords <= 0)
2859 return -1;
2860
2861 /* refuse to sset if any bit > 31 is set */
2862 if (!ethtool_link_mode_is_backward_compatible(
2863 link_usettings->link_modes.supported))
2864 return -1;
2865 if (!ethtool_link_mode_is_backward_compatible(
2866 link_usettings->link_modes.advertising))
2867 return -1;
2868 if (!ethtool_link_mode_is_backward_compatible(
2869 link_usettings->link_modes.lp_advertising))
2870 return -1;
2871
2872 memset(&ecmd, 0, sizeof(ecmd));
2873 ecmd.cmd = ETHTOOL_SSET;
2874
2875 ecmd.supported = link_usettings->link_modes.supported[0];
2876 ecmd.advertising = link_usettings->link_modes.advertising[0];
2877 ecmd.lp_advertising = link_usettings->link_modes.lp_advertising[0];
2878 ethtool_cmd_speed_set(&ecmd, link_usettings->base.speed);
2879 ecmd.duplex = link_usettings->base.duplex;
2880 ecmd.port = link_usettings->base.port;
2881 ecmd.phy_address = link_usettings->base.phy_address;
2882 ecmd.transceiver = link_usettings->deprecated.transceiver;
2883 ecmd.autoneg = link_usettings->base.autoneg;
2884 ecmd.mdio_support = link_usettings->base.mdio_support;
2885 /* ignored (fully deprecated): maxrxpkt, maxtxpkt */
2886 ecmd.eth_tp_mdix = link_usettings->base.eth_tp_mdix;
2887 ecmd.eth_tp_mdix_ctrl = link_usettings->base.eth_tp_mdix_ctrl;
2888 return send_ioctl(ctx, &ecmd);
2889 }
2890
do_gset(struct cmd_context * ctx)2891 static int do_gset(struct cmd_context *ctx)
2892 {
2893 int err;
2894 struct ethtool_link_usettings *link_usettings;
2895 struct ethtool_wolinfo wolinfo;
2896 struct ethtool_value edata;
2897 int allfail = 1;
2898
2899 if (ctx->argc != 0)
2900 exit_bad_args();
2901
2902 fprintf(stdout, "Settings for %s:\n", ctx->devname);
2903
2904 link_usettings = do_ioctl_glinksettings(ctx);
2905 if (link_usettings == NULL)
2906 link_usettings = do_ioctl_gset(ctx);
2907 if (link_usettings != NULL) {
2908 err = dump_link_usettings(link_usettings);
2909 free(link_usettings);
2910 if (err)
2911 return err;
2912 allfail = 0;
2913 } else if (errno != EOPNOTSUPP) {
2914 perror("Cannot get device settings");
2915 }
2916
2917 wolinfo.cmd = ETHTOOL_GWOL;
2918 err = send_ioctl(ctx, &wolinfo);
2919 if (err == 0) {
2920 err = dump_wol(&wolinfo);
2921 if (err)
2922 return err;
2923 allfail = 0;
2924 } else if (errno != EOPNOTSUPP) {
2925 perror("Cannot get wake-on-lan settings");
2926 }
2927
2928 edata.cmd = ETHTOOL_GMSGLVL;
2929 err = send_ioctl(ctx, &edata);
2930 if (err == 0) {
2931 fprintf(stdout, " Current message level: 0x%08x (%d)\n"
2932 " ",
2933 edata.data, edata.data);
2934 print_flags(flags_msglvl, n_flags_msglvl, edata.data);
2935 fprintf(stdout, "\n");
2936 allfail = 0;
2937 } else if (errno != EOPNOTSUPP) {
2938 perror("Cannot get message level");
2939 }
2940
2941 edata.cmd = ETHTOOL_GLINK;
2942 err = send_ioctl(ctx, &edata);
2943 if (err == 0) {
2944 fprintf(stdout, " Link detected: %s\n",
2945 edata.data ? "yes":"no");
2946 allfail = 0;
2947 } else if (errno != EOPNOTSUPP) {
2948 perror("Cannot get link status");
2949 }
2950
2951 if (allfail) {
2952 fprintf(stdout, "No data available\n");
2953 return 75;
2954 }
2955 return 0;
2956 }
2957
do_sset(struct cmd_context * ctx)2958 static int do_sset(struct cmd_context *ctx)
2959 {
2960 int speed_wanted = -1;
2961 int duplex_wanted = -1;
2962 int port_wanted = -1;
2963 int mdix_wanted = -1;
2964 int autoneg_wanted = -1;
2965 int phyad_wanted = -1;
2966 int xcvr_wanted = -1;
2967 u32 *full_advertising_wanted = NULL;
2968 u32 *advertising_wanted = NULL;
2969 ETHTOOL_DECLARE_LINK_MODE_MASK(mask_full_advertising_wanted);
2970 ETHTOOL_DECLARE_LINK_MODE_MASK(mask_advertising_wanted);
2971 int gset_changed = 0; /* did anything in GSET change? */
2972 u32 wol_wanted = 0;
2973 int wol_change = 0;
2974 u8 sopass_wanted[SOPASS_MAX];
2975 int sopass_change = 0;
2976 int gwol_changed = 0; /* did anything in GWOL change? */
2977 int msglvl_changed = 0;
2978 u32 msglvl_wanted = 0;
2979 u32 msglvl_mask = 0;
2980 struct cmdline_info cmdline_msglvl[n_flags_msglvl];
2981 unsigned int argc = ctx->argc;
2982 char **argp = ctx->argp;
2983 unsigned int i;
2984 int err = 0;
2985
2986 for (i = 0; i < n_flags_msglvl; i++)
2987 flag_to_cmdline_info(flags_msglvl[i].name,
2988 flags_msglvl[i].value,
2989 &msglvl_wanted, &msglvl_mask,
2990 &cmdline_msglvl[i]);
2991
2992 for (i = 0; i < argc; i++) {
2993 if (!strcmp(argp[i], "speed")) {
2994 gset_changed = 1;
2995 i += 1;
2996 if (i >= argc)
2997 exit_bad_args();
2998 speed_wanted = get_int(argp[i], 10);
2999 } else if (!strcmp(argp[i], "duplex")) {
3000 gset_changed = 1;
3001 i += 1;
3002 if (i >= argc)
3003 exit_bad_args();
3004 if (!strcmp(argp[i], "half"))
3005 duplex_wanted = DUPLEX_HALF;
3006 else if (!strcmp(argp[i], "full"))
3007 duplex_wanted = DUPLEX_FULL;
3008 else
3009 exit_bad_args();
3010 } else if (!strcmp(argp[i], "port")) {
3011 gset_changed = 1;
3012 i += 1;
3013 if (i >= argc)
3014 exit_bad_args();
3015 if (!strcmp(argp[i], "tp"))
3016 port_wanted = PORT_TP;
3017 else if (!strcmp(argp[i], "aui"))
3018 port_wanted = PORT_AUI;
3019 else if (!strcmp(argp[i], "bnc"))
3020 port_wanted = PORT_BNC;
3021 else if (!strcmp(argp[i], "mii"))
3022 port_wanted = PORT_MII;
3023 else if (!strcmp(argp[i], "fibre"))
3024 port_wanted = PORT_FIBRE;
3025 else
3026 exit_bad_args();
3027 } else if (!strcmp(argp[i], "mdix")) {
3028 gset_changed = 1;
3029 i += 1;
3030 if (i >= argc)
3031 exit_bad_args();
3032 if (!strcmp(argp[i], "auto"))
3033 mdix_wanted = ETH_TP_MDI_AUTO;
3034 else if (!strcmp(argp[i], "on"))
3035 mdix_wanted = ETH_TP_MDI_X;
3036 else if (!strcmp(argp[i], "off"))
3037 mdix_wanted = ETH_TP_MDI;
3038 else
3039 exit_bad_args();
3040 } else if (!strcmp(argp[i], "autoneg")) {
3041 i += 1;
3042 if (i >= argc)
3043 exit_bad_args();
3044 if (!strcmp(argp[i], "on")) {
3045 gset_changed = 1;
3046 autoneg_wanted = AUTONEG_ENABLE;
3047 } else if (!strcmp(argp[i], "off")) {
3048 gset_changed = 1;
3049 autoneg_wanted = AUTONEG_DISABLE;
3050 } else {
3051 exit_bad_args();
3052 }
3053 } else if (!strcmp(argp[i], "advertise")) {
3054 gset_changed = 1;
3055 i += 1;
3056 if (i >= argc)
3057 exit_bad_args();
3058 if (parse_hex_u32_bitmap(
3059 argp[i],
3060 ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NBITS,
3061 mask_full_advertising_wanted))
3062 exit_bad_args();
3063 full_advertising_wanted = mask_full_advertising_wanted;
3064 } else if (!strcmp(argp[i], "phyad")) {
3065 gset_changed = 1;
3066 i += 1;
3067 if (i >= argc)
3068 exit_bad_args();
3069 phyad_wanted = get_int(argp[i], 0);
3070 } else if (!strcmp(argp[i], "xcvr")) {
3071 gset_changed = 1;
3072 i += 1;
3073 if (i >= argc)
3074 exit_bad_args();
3075 if (!strcmp(argp[i], "internal"))
3076 xcvr_wanted = XCVR_INTERNAL;
3077 else if (!strcmp(argp[i], "external"))
3078 xcvr_wanted = XCVR_EXTERNAL;
3079 else
3080 exit_bad_args();
3081 } else if (!strcmp(argp[i], "wol")) {
3082 gwol_changed = 1;
3083 i++;
3084 if (i >= argc)
3085 exit_bad_args();
3086 if (parse_wolopts(argp[i], &wol_wanted) < 0)
3087 exit_bad_args();
3088 wol_change = 1;
3089 } else if (!strcmp(argp[i], "sopass")) {
3090 gwol_changed = 1;
3091 i++;
3092 if (i >= argc)
3093 exit_bad_args();
3094 get_mac_addr(argp[i], sopass_wanted);
3095 sopass_change = 1;
3096 } else if (!strcmp(argp[i], "msglvl")) {
3097 i++;
3098 if (i >= argc)
3099 exit_bad_args();
3100 if (isdigit((unsigned char)argp[i][0])) {
3101 msglvl_changed = 1;
3102 msglvl_mask = ~0;
3103 msglvl_wanted =
3104 get_uint_range(argp[i], 0,
3105 0xffffffff);
3106 } else {
3107 ctx->argc -= i;
3108 ctx->argp += i;
3109 parse_generic_cmdline(
3110 ctx, &msglvl_changed,
3111 cmdline_msglvl,
3112 ARRAY_SIZE(cmdline_msglvl));
3113 break;
3114 }
3115 } else if (!strcmp(argp[i], "master-slave")) {
3116 exit_nlonly_param(argp[i]);
3117 } else {
3118 exit_bad_args();
3119 }
3120 }
3121
3122 if (full_advertising_wanted == NULL) {
3123 /* User didn't supply a full advertisement bitfield:
3124 * construct one from the specified speed and duplex.
3125 */
3126 int adv_bit = -1;
3127
3128 if (speed_wanted == SPEED_10 && duplex_wanted == DUPLEX_HALF)
3129 adv_bit = ETHTOOL_LINK_MODE_10baseT_Half_BIT;
3130 else if (speed_wanted == SPEED_10 &&
3131 duplex_wanted == DUPLEX_FULL)
3132 adv_bit = ETHTOOL_LINK_MODE_10baseT_Full_BIT;
3133 else if (speed_wanted == SPEED_100 &&
3134 duplex_wanted == DUPLEX_HALF)
3135 adv_bit = ETHTOOL_LINK_MODE_100baseT_Half_BIT;
3136 else if (speed_wanted == SPEED_100 &&
3137 duplex_wanted == DUPLEX_FULL)
3138 adv_bit = ETHTOOL_LINK_MODE_100baseT_Full_BIT;
3139 else if (speed_wanted == SPEED_1000 &&
3140 duplex_wanted == DUPLEX_HALF)
3141 adv_bit = ETHTOOL_LINK_MODE_1000baseT_Half_BIT;
3142 else if (speed_wanted == SPEED_1000 &&
3143 duplex_wanted == DUPLEX_FULL)
3144 adv_bit = ETHTOOL_LINK_MODE_1000baseT_Full_BIT;
3145 else if (speed_wanted == SPEED_2500 &&
3146 duplex_wanted == DUPLEX_FULL)
3147 adv_bit = ETHTOOL_LINK_MODE_2500baseX_Full_BIT;
3148 else if (speed_wanted == SPEED_10000 &&
3149 duplex_wanted == DUPLEX_FULL)
3150 adv_bit = ETHTOOL_LINK_MODE_10000baseT_Full_BIT;
3151
3152 if (adv_bit >= 0) {
3153 advertising_wanted = mask_advertising_wanted;
3154 ethtool_link_mode_zero(advertising_wanted);
3155 ethtool_link_mode_set_bit(
3156 adv_bit, advertising_wanted);
3157 }
3158 /* otherwise: auto negotiate without forcing,
3159 * all supported speed will be assigned below
3160 */
3161 }
3162
3163 if (gset_changed) {
3164 struct ethtool_link_usettings *link_usettings;
3165
3166 link_usettings = do_ioctl_glinksettings(ctx);
3167 if (link_usettings == NULL)
3168 link_usettings = do_ioctl_gset(ctx);
3169 else
3170 memset(&link_usettings->deprecated, 0,
3171 sizeof(link_usettings->deprecated));
3172 if (link_usettings == NULL) {
3173 perror("Cannot get current device settings");
3174 err = -1;
3175 } else {
3176 /* Change everything the user specified. */
3177 if (speed_wanted != -1)
3178 link_usettings->base.speed = speed_wanted;
3179 if (duplex_wanted != -1)
3180 link_usettings->base.duplex = duplex_wanted;
3181 if (port_wanted != -1)
3182 link_usettings->base.port = port_wanted;
3183 if (mdix_wanted != -1) {
3184 /* check driver supports MDI-X */
3185 if (link_usettings->base.eth_tp_mdix_ctrl
3186 != ETH_TP_MDI_INVALID)
3187 link_usettings->base.eth_tp_mdix_ctrl
3188 = mdix_wanted;
3189 else
3190 fprintf(stderr,
3191 "setting MDI not supported\n");
3192 }
3193 if (autoneg_wanted != -1)
3194 link_usettings->base.autoneg = autoneg_wanted;
3195 if (phyad_wanted != -1)
3196 link_usettings->base.phy_address = phyad_wanted;
3197 if (xcvr_wanted != -1)
3198 link_usettings->deprecated.transceiver
3199 = xcvr_wanted;
3200 /* XXX If the user specified speed or duplex
3201 * then we should mask the advertised modes
3202 * accordingly. For now, warn that we aren't
3203 * doing that.
3204 */
3205 if ((speed_wanted != -1 || duplex_wanted != -1) &&
3206 link_usettings->base.autoneg &&
3207 advertising_wanted == NULL) {
3208 fprintf(stderr, "Cannot advertise");
3209 if (speed_wanted >= 0)
3210 fprintf(stderr, " speed %d",
3211 speed_wanted);
3212 if (duplex_wanted >= 0)
3213 fprintf(stderr, " duplex %s",
3214 duplex_wanted ?
3215 "full" : "half");
3216 fprintf(stderr, "\n");
3217 }
3218 if (autoneg_wanted == AUTONEG_ENABLE &&
3219 advertising_wanted == NULL &&
3220 full_advertising_wanted == NULL) {
3221 unsigned int i;
3222
3223 /* Auto negotiation enabled, but with
3224 * unspecified speed and duplex: enable all
3225 * supported speeds and duplexes.
3226 */
3227 ethtool_link_mode_for_each_u32(i) {
3228 u32 sup = link_usettings->link_modes.supported[i];
3229 u32 *adv = link_usettings->link_modes.advertising + i;
3230
3231 *adv = ((*adv & ~all_advertised_modes[i])
3232 | (sup & all_advertised_modes[i]));
3233 }
3234
3235 /* If driver supports unknown flags, we cannot
3236 * be sure that we enable all link modes.
3237 */
3238 ethtool_link_mode_for_each_u32(i) {
3239 u32 sup = link_usettings->link_modes.supported[i];
3240
3241 if ((sup & all_advertised_flags[i]) != sup) {
3242 fprintf(stderr, "Driver supports one or more unknown flags\n");
3243 break;
3244 }
3245 }
3246 } else if (advertising_wanted != NULL) {
3247 unsigned int i;
3248
3249 /* Enable all requested modes */
3250 ethtool_link_mode_for_each_u32(i) {
3251 u32 *adv = link_usettings->link_modes.advertising + i;
3252
3253 *adv = ((*adv & ~all_advertised_modes[i])
3254 | advertising_wanted[i]);
3255 }
3256 } else if (full_advertising_wanted != NULL) {
3257 ethtool_link_mode_copy(
3258 link_usettings->link_modes.advertising,
3259 full_advertising_wanted);
3260 }
3261
3262 /* Try to perform the update. */
3263 if (link_usettings->base.cmd == ETHTOOL_GLINKSETTINGS)
3264 err = do_ioctl_slinksettings(ctx,
3265 link_usettings);
3266 else
3267 err = do_ioctl_sset(ctx, link_usettings);
3268 free(link_usettings);
3269 if (err < 0)
3270 perror("Cannot set new settings");
3271 }
3272 if (err < 0) {
3273 if (speed_wanted != -1)
3274 fprintf(stderr, " not setting speed\n");
3275 if (duplex_wanted != -1)
3276 fprintf(stderr, " not setting duplex\n");
3277 if (port_wanted != -1)
3278 fprintf(stderr, " not setting port\n");
3279 if (autoneg_wanted != -1)
3280 fprintf(stderr, " not setting autoneg\n");
3281 if (phyad_wanted != -1)
3282 fprintf(stderr, " not setting phy_address\n");
3283 if (xcvr_wanted != -1)
3284 fprintf(stderr, " not setting transceiver\n");
3285 if (mdix_wanted != -1)
3286 fprintf(stderr, " not setting mdix\n");
3287 }
3288 }
3289
3290 if (gwol_changed) {
3291 struct ethtool_wolinfo wol;
3292
3293 wol.cmd = ETHTOOL_GWOL;
3294 err = send_ioctl(ctx, &wol);
3295 if (err < 0) {
3296 perror("Cannot get current wake-on-lan settings");
3297 } else {
3298 /* Change everything the user specified. */
3299 if (wol_change)
3300 wol.wolopts = wol_wanted;
3301 if (sopass_change) {
3302 int i;
3303 for (i = 0; i < SOPASS_MAX; i++)
3304 wol.sopass[i] = sopass_wanted[i];
3305 }
3306
3307 /* Try to perform the update. */
3308 wol.cmd = ETHTOOL_SWOL;
3309 err = send_ioctl(ctx, &wol);
3310 if (err < 0)
3311 perror("Cannot set new wake-on-lan settings");
3312 }
3313 if (err < 0) {
3314 if (wol_change)
3315 fprintf(stderr, " not setting wol\n");
3316 if (sopass_change)
3317 fprintf(stderr, " not setting sopass\n");
3318 }
3319 }
3320
3321 if (msglvl_changed) {
3322 struct ethtool_value edata;
3323
3324 edata.cmd = ETHTOOL_GMSGLVL;
3325 err = send_ioctl(ctx, &edata);
3326 if (err < 0) {
3327 perror("Cannot get msglvl");
3328 } else {
3329 edata.cmd = ETHTOOL_SMSGLVL;
3330 edata.data = ((edata.data & ~msglvl_mask) |
3331 msglvl_wanted);
3332 err = send_ioctl(ctx, &edata);
3333 if (err < 0)
3334 perror("Cannot set new msglvl");
3335 }
3336 }
3337
3338 return 0;
3339 }
3340
do_gregs(struct cmd_context * ctx)3341 static int do_gregs(struct cmd_context *ctx)
3342 {
3343 int gregs_changed = 0;
3344 int gregs_dump_raw = 0;
3345 int gregs_dump_hex = 0;
3346 char *gregs_dump_file = NULL;
3347 struct cmdline_info cmdline_gregs[] = {
3348 {
3349 .name = "raw",
3350 .type = CMDL_BOOL,
3351 .wanted_val = &gregs_dump_raw,
3352 },
3353 {
3354 .name = "hex",
3355 .type = CMDL_BOOL,
3356 .wanted_val = &gregs_dump_hex,
3357 },
3358 {
3359 .name = "file",
3360 .type = CMDL_STR,
3361 .wanted_val = &gregs_dump_file,
3362 },
3363 };
3364 int err;
3365 struct ethtool_drvinfo drvinfo;
3366 struct ethtool_regs *regs;
3367
3368 parse_generic_cmdline(ctx, &gregs_changed,
3369 cmdline_gregs, ARRAY_SIZE(cmdline_gregs));
3370
3371 drvinfo.cmd = ETHTOOL_GDRVINFO;
3372 err = send_ioctl(ctx, &drvinfo);
3373 if (err < 0) {
3374 perror("Cannot get driver information");
3375 return 72;
3376 }
3377
3378 regs = calloc(1, sizeof(*regs)+drvinfo.regdump_len);
3379 if (!regs) {
3380 perror("Cannot allocate memory for register dump");
3381 return 73;
3382 }
3383 regs->cmd = ETHTOOL_GREGS;
3384 regs->len = drvinfo.regdump_len;
3385 err = send_ioctl(ctx, regs);
3386 if (err < 0) {
3387 perror("Cannot get register dump");
3388 free(regs);
3389 return 74;
3390 }
3391
3392 if (!gregs_dump_raw && gregs_dump_file != NULL) {
3393 /* overwrite reg values from file dump */
3394 FILE *f = fopen(gregs_dump_file, "r");
3395 struct ethtool_regs *nregs;
3396 struct stat st;
3397 size_t nread;
3398
3399 if (!f || fstat(fileno(f), &st) < 0) {
3400 fprintf(stderr, "Can't open '%s': %s\n",
3401 gregs_dump_file, strerror(errno));
3402 if (f)
3403 fclose(f);
3404 free(regs);
3405 return 75;
3406 }
3407
3408 nregs = realloc(regs, sizeof(*regs) + st.st_size);
3409 if (!nregs) {
3410 perror("Cannot allocate memory for register dump");
3411 free(regs); /* was not freed by realloc */
3412 return 73;
3413 }
3414 regs = nregs;
3415 regs->len = st.st_size;
3416 nread = fread(regs->data, regs->len, 1, f);
3417 fclose(f);
3418 if (nread != 1) {
3419 free(regs);
3420 return 75;
3421 }
3422 }
3423
3424 if (dump_regs(gregs_dump_raw, gregs_dump_hex,
3425 &drvinfo, regs) < 0) {
3426 fprintf(stderr, "Cannot dump registers\n");
3427 free(regs);
3428 return 75;
3429 }
3430 free(regs);
3431
3432 return 0;
3433 }
3434
do_nway_rst(struct cmd_context * ctx)3435 static int do_nway_rst(struct cmd_context *ctx)
3436 {
3437 struct ethtool_value edata;
3438 int err;
3439
3440 if (ctx->argc != 0)
3441 exit_bad_args();
3442
3443 edata.cmd = ETHTOOL_NWAY_RST;
3444 err = send_ioctl(ctx, &edata);
3445 if (err < 0)
3446 perror("Cannot restart autonegotiation");
3447
3448 return err;
3449 }
3450
do_geeprom(struct cmd_context * ctx)3451 static int do_geeprom(struct cmd_context *ctx)
3452 {
3453 int geeprom_changed = 0;
3454 int geeprom_dump_raw = 0;
3455 u32 geeprom_offset = 0;
3456 u32 geeprom_length = 0;
3457 int geeprom_length_seen = 0;
3458 struct cmdline_info cmdline_geeprom[] = {
3459 {
3460 .name = "offset",
3461 .type = CMDL_U32,
3462 .wanted_val = &geeprom_offset,
3463 },
3464 {
3465 .name = "length",
3466 .type = CMDL_U32,
3467 .wanted_val = &geeprom_length,
3468 .seen_val = &geeprom_length_seen,
3469 },
3470 {
3471 .name = "raw",
3472 .type = CMDL_BOOL,
3473 .wanted_val = &geeprom_dump_raw,
3474 },
3475 };
3476 int err;
3477 struct ethtool_drvinfo drvinfo;
3478 struct ethtool_eeprom *eeprom;
3479
3480 parse_generic_cmdline(ctx, &geeprom_changed,
3481 cmdline_geeprom, ARRAY_SIZE(cmdline_geeprom));
3482
3483 drvinfo.cmd = ETHTOOL_GDRVINFO;
3484 err = send_ioctl(ctx, &drvinfo);
3485 if (err < 0) {
3486 perror("Cannot get driver information");
3487 return 74;
3488 }
3489
3490 if (!geeprom_length_seen)
3491 geeprom_length = drvinfo.eedump_len;
3492
3493 if (drvinfo.eedump_len < geeprom_offset + geeprom_length)
3494 geeprom_length = drvinfo.eedump_len - geeprom_offset;
3495
3496 eeprom = calloc(1, sizeof(*eeprom)+geeprom_length);
3497 if (!eeprom) {
3498 perror("Cannot allocate memory for EEPROM data");
3499 return 75;
3500 }
3501 eeprom->cmd = ETHTOOL_GEEPROM;
3502 eeprom->len = geeprom_length;
3503 eeprom->offset = geeprom_offset;
3504 err = send_ioctl(ctx, eeprom);
3505 if (err < 0) {
3506 perror("Cannot get EEPROM data");
3507 free(eeprom);
3508 return 74;
3509 }
3510 err = dump_eeprom(geeprom_dump_raw, &drvinfo, eeprom);
3511 free(eeprom);
3512
3513 return err;
3514 }
3515
do_seeprom(struct cmd_context * ctx)3516 static int do_seeprom(struct cmd_context *ctx)
3517 {
3518 int seeprom_changed = 0;
3519 u32 seeprom_magic = 0;
3520 u32 seeprom_length = 0;
3521 u32 seeprom_offset = 0;
3522 u8 seeprom_value = 0;
3523 int seeprom_length_seen = 0;
3524 int seeprom_value_seen = 0;
3525 struct cmdline_info cmdline_seeprom[] = {
3526 {
3527 .name = "magic",
3528 .type = CMDL_U32,
3529 .wanted_val = &seeprom_magic,
3530 },
3531 {
3532 .name = "offset",
3533 .type = CMDL_U32,
3534 .wanted_val = &seeprom_offset,
3535 },
3536 {
3537 .name = "length",
3538 .type = CMDL_U32,
3539 .wanted_val = &seeprom_length,
3540 .seen_val = &seeprom_length_seen,
3541 },
3542 {
3543 .name = "value",
3544 .type = CMDL_U8,
3545 .wanted_val = &seeprom_value,
3546 .seen_val = &seeprom_value_seen,
3547 },
3548 };
3549 int err;
3550 struct ethtool_drvinfo drvinfo;
3551 struct ethtool_eeprom *eeprom;
3552
3553 parse_generic_cmdline(ctx, &seeprom_changed,
3554 cmdline_seeprom, ARRAY_SIZE(cmdline_seeprom));
3555
3556 drvinfo.cmd = ETHTOOL_GDRVINFO;
3557 err = send_ioctl(ctx, &drvinfo);
3558 if (err < 0) {
3559 perror("Cannot get driver information");
3560 return 74;
3561 }
3562
3563 if (seeprom_value_seen && !seeprom_length_seen)
3564 seeprom_length = 1;
3565 else if (!seeprom_length_seen)
3566 seeprom_length = drvinfo.eedump_len;
3567
3568 if (seeprom_value_seen && (seeprom_length != 1)) {
3569 fprintf(stderr, "value requires length 1\n");
3570 return 1;
3571 }
3572
3573 if (drvinfo.eedump_len < seeprom_offset + seeprom_length) {
3574 fprintf(stderr, "offset & length out of bounds\n");
3575 return 1;
3576 }
3577
3578 eeprom = calloc(1, sizeof(*eeprom)+seeprom_length);
3579 if (!eeprom) {
3580 perror("Cannot allocate memory for EEPROM data");
3581 return 75;
3582 }
3583
3584 eeprom->cmd = ETHTOOL_SEEPROM;
3585 eeprom->len = seeprom_length;
3586 eeprom->offset = seeprom_offset;
3587 eeprom->magic = seeprom_magic;
3588 eeprom->data[0] = seeprom_value;
3589
3590 /* Multi-byte write: read input from stdin */
3591 if (!seeprom_value_seen) {
3592 if (fread(eeprom->data, eeprom->len, 1, stdin) != 1) {
3593 fprintf(stderr, "not enough data from stdin\n");
3594 free(eeprom);
3595 return 75;
3596 }
3597 if ((fgetc(stdin) != EOF) || !feof(stdin)) {
3598 fprintf(stderr, "too much data from stdin\n");
3599 free(eeprom);
3600 return 75;
3601 }
3602 }
3603
3604 err = send_ioctl(ctx, eeprom);
3605 if (err < 0) {
3606 perror("Cannot set EEPROM data");
3607 err = 87;
3608 }
3609 free(eeprom);
3610
3611 return err;
3612 }
3613
do_test(struct cmd_context * ctx)3614 static int do_test(struct cmd_context *ctx)
3615 {
3616 enum {
3617 ONLINE = 0,
3618 OFFLINE,
3619 EXTERNAL_LB,
3620 } test_type;
3621 int err;
3622 struct ethtool_test *test;
3623 struct ethtool_gstrings *strings;
3624
3625 if (ctx->argc > 1)
3626 exit_bad_args();
3627 if (ctx->argc == 1) {
3628 if (!strcmp(ctx->argp[0], "online"))
3629 test_type = ONLINE;
3630 else if (!strcmp(*ctx->argp, "offline"))
3631 test_type = OFFLINE;
3632 else if (!strcmp(*ctx->argp, "external_lb"))
3633 test_type = EXTERNAL_LB;
3634 else
3635 exit_bad_args();
3636 } else {
3637 test_type = OFFLINE;
3638 }
3639
3640 strings = get_stringset(ctx, ETH_SS_TEST,
3641 offsetof(struct ethtool_drvinfo, testinfo_len),
3642 1);
3643 if (!strings) {
3644 perror("Cannot get strings");
3645 return 74;
3646 }
3647
3648 test = calloc(1, sizeof(*test) + strings->len * sizeof(u64));
3649 if (!test) {
3650 perror("Cannot allocate memory for test info");
3651 free(strings);
3652 return 73;
3653 }
3654 memset(test->data, 0, strings->len * sizeof(u64));
3655 test->cmd = ETHTOOL_TEST;
3656 test->len = strings->len;
3657 if (test_type == EXTERNAL_LB)
3658 test->flags = (ETH_TEST_FL_OFFLINE | ETH_TEST_FL_EXTERNAL_LB);
3659 else if (test_type == OFFLINE)
3660 test->flags = ETH_TEST_FL_OFFLINE;
3661 else
3662 test->flags = 0;
3663 err = send_ioctl(ctx, test);
3664 if (err < 0) {
3665 perror("Cannot test");
3666 free(test);
3667 free(strings);
3668 return 74;
3669 }
3670
3671 err = dump_test(test, strings);
3672 free(test);
3673 free(strings);
3674
3675 return err;
3676 }
3677
do_phys_id(struct cmd_context * ctx)3678 static int do_phys_id(struct cmd_context *ctx)
3679 {
3680 int err;
3681 struct ethtool_value edata;
3682 int phys_id_time;
3683
3684 if (ctx->argc > 1)
3685 exit_bad_args();
3686 if (ctx->argc == 1)
3687 phys_id_time = get_int(*ctx->argp, 0);
3688 else
3689 phys_id_time = 0;
3690
3691 edata.cmd = ETHTOOL_PHYS_ID;
3692 edata.data = phys_id_time;
3693 err = send_ioctl(ctx, &edata);
3694 if (err < 0)
3695 perror("Cannot identify NIC");
3696
3697 return err;
3698 }
3699
do_gstats(struct cmd_context * ctx,int cmd,int stringset,const char * name)3700 static int do_gstats(struct cmd_context *ctx, int cmd, int stringset,
3701 const char *name)
3702 {
3703 struct ethtool_gstrings *strings;
3704 struct ethtool_stats *stats;
3705 unsigned int n_stats, sz_stats, i;
3706 int err;
3707
3708 if (ctx->argc != 0)
3709 exit_bad_args();
3710
3711 strings = get_stringset(ctx, stringset,
3712 offsetof(struct ethtool_drvinfo, n_stats),
3713 0);
3714 if (!strings) {
3715 perror("Cannot get stats strings information");
3716 return 96;
3717 }
3718
3719 n_stats = strings->len;
3720 if (n_stats < 1) {
3721 fprintf(stderr, "no stats available\n");
3722 free(strings);
3723 return 94;
3724 }
3725
3726 sz_stats = n_stats * sizeof(u64);
3727
3728 stats = calloc(1, sz_stats + sizeof(struct ethtool_stats));
3729 if (!stats) {
3730 fprintf(stderr, "no memory available\n");
3731 free(strings);
3732 return 95;
3733 }
3734
3735 stats->cmd = cmd;
3736 stats->n_stats = n_stats;
3737 err = send_ioctl(ctx, stats);
3738 if (err < 0) {
3739 perror("Cannot get stats information");
3740 free(strings);
3741 free(stats);
3742 return 97;
3743 }
3744
3745 /* todo - pretty-print the strings per-driver */
3746 fprintf(stdout, "%s statistics:\n", name);
3747 for (i = 0; i < n_stats; i++) {
3748 fprintf(stdout, " %.*s: %llu\n",
3749 ETH_GSTRING_LEN,
3750 &strings->data[i * ETH_GSTRING_LEN],
3751 stats->data[i]);
3752 }
3753 free(strings);
3754 free(stats);
3755
3756 return 0;
3757 }
3758
do_gnicstats(struct cmd_context * ctx)3759 static int do_gnicstats(struct cmd_context *ctx)
3760 {
3761 return do_gstats(ctx, ETHTOOL_GSTATS, ETH_SS_STATS, "NIC");
3762 }
3763
do_gphystats(struct cmd_context * ctx)3764 static int do_gphystats(struct cmd_context *ctx)
3765 {
3766 return do_gstats(ctx, ETHTOOL_GPHYSTATS, ETH_SS_PHY_STATS, "PHY");
3767 }
3768
3769 static int do_srxntuple(struct cmd_context *ctx,
3770 struct ethtool_rx_flow_spec *rx_rule_fs);
3771
do_srxclass(struct cmd_context * ctx)3772 static int do_srxclass(struct cmd_context *ctx)
3773 {
3774 int err;
3775
3776 if (ctx->argc < 2)
3777 exit_bad_args();
3778
3779 if (!strcmp(ctx->argp[0], "rx-flow-hash")) {
3780 int rx_fhash_set;
3781 u32 rx_fhash_val;
3782 struct ethtool_rxnfc nfccmd;
3783 bool flow_rss = false;
3784
3785 if (ctx->argc == 5) {
3786 if (strcmp(ctx->argp[3], "context"))
3787 exit_bad_args();
3788 flow_rss = true;
3789 nfccmd.rss_context = get_u32(ctx->argp[4], 0);
3790 } else if (ctx->argc != 3) {
3791 exit_bad_args();
3792 }
3793 rx_fhash_set = rxflow_str_to_type(ctx->argp[1]);
3794 if (!rx_fhash_set)
3795 exit_bad_args();
3796 if (parse_rxfhashopts(ctx->argp[2], &rx_fhash_val) < 0)
3797 exit_bad_args();
3798
3799 nfccmd.cmd = ETHTOOL_SRXFH;
3800 nfccmd.flow_type = rx_fhash_set;
3801 nfccmd.data = rx_fhash_val;
3802 if (flow_rss)
3803 nfccmd.flow_type |= FLOW_RSS;
3804
3805 err = send_ioctl(ctx, &nfccmd);
3806 if (err < 0)
3807 perror("Cannot change RX network flow hashing options");
3808 } else if (!strcmp(ctx->argp[0], "flow-type")) {
3809 struct ethtool_rx_flow_spec rx_rule_fs;
3810 __u32 rss_context = 0;
3811
3812 ctx->argc--;
3813 ctx->argp++;
3814 if (rxclass_parse_ruleopts(ctx, &rx_rule_fs, &rss_context) < 0)
3815 exit_bad_args();
3816
3817 /* attempt to add rule via N-tuple specifier */
3818 err = do_srxntuple(ctx, &rx_rule_fs);
3819 if (!err)
3820 return 0;
3821
3822 /* attempt to add rule via network flow classifier */
3823 err = rxclass_rule_ins(ctx, &rx_rule_fs, rss_context);
3824 if (err < 0) {
3825 fprintf(stderr, "Cannot insert"
3826 " classification rule\n");
3827 return 1;
3828 }
3829 } else if (!strcmp(ctx->argp[0], "delete")) {
3830 int rx_class_rule_del =
3831 get_uint_range(ctx->argp[1], 0, INT_MAX);
3832
3833 err = rxclass_rule_del(ctx, rx_class_rule_del);
3834
3835 if (err < 0) {
3836 fprintf(stderr, "Cannot delete"
3837 " classification rule\n");
3838 return 1;
3839 }
3840 } else {
3841 exit_bad_args();
3842 }
3843
3844 return 0;
3845 }
3846
do_grxclass(struct cmd_context * ctx)3847 static int do_grxclass(struct cmd_context *ctx)
3848 {
3849 struct ethtool_rxnfc nfccmd;
3850 int err;
3851
3852 if (ctx->argc > 0 && !strcmp(ctx->argp[0], "rx-flow-hash")) {
3853 int rx_fhash_get;
3854 bool flow_rss = false;
3855
3856 if (ctx->argc == 4) {
3857 if (strcmp(ctx->argp[2], "context"))
3858 exit_bad_args();
3859 flow_rss = true;
3860 nfccmd.rss_context = get_u32(ctx->argp[3], 0);
3861 } else if (ctx->argc != 2) {
3862 exit_bad_args();
3863 }
3864
3865 rx_fhash_get = rxflow_str_to_type(ctx->argp[1]);
3866 if (!rx_fhash_get)
3867 exit_bad_args();
3868
3869 nfccmd.cmd = ETHTOOL_GRXFH;
3870 nfccmd.flow_type = rx_fhash_get;
3871 if (flow_rss)
3872 nfccmd.flow_type |= FLOW_RSS;
3873 err = send_ioctl(ctx, &nfccmd);
3874 if (err < 0) {
3875 perror("Cannot get RX network flow hashing options");
3876 } else {
3877 if (flow_rss)
3878 fprintf(stdout, "For RSS context %u:\n",
3879 nfccmd.rss_context);
3880 dump_rxfhash(rx_fhash_get, nfccmd.data);
3881 }
3882 } else if (ctx->argc == 2 && !strcmp(ctx->argp[0], "rule")) {
3883 int rx_class_rule_get =
3884 get_uint_range(ctx->argp[1], 0, INT_MAX);
3885
3886 err = rxclass_rule_get(ctx, rx_class_rule_get);
3887 if (err < 0)
3888 fprintf(stderr, "Cannot get RX classification rule\n");
3889 } else if (ctx->argc == 0) {
3890 nfccmd.cmd = ETHTOOL_GRXRINGS;
3891 err = send_ioctl(ctx, &nfccmd);
3892 if (err < 0)
3893 perror("Cannot get RX rings");
3894 else
3895 fprintf(stdout, "%d RX rings available\n",
3896 (int)nfccmd.data);
3897
3898 err = rxclass_rule_getall(ctx);
3899 if (err < 0)
3900 fprintf(stderr, "RX classification rule retrieval failed\n");
3901
3902 } else {
3903 exit_bad_args();
3904 }
3905
3906 return err ? 1 : 0;
3907 }
3908
do_grxfhindir(struct cmd_context * ctx,struct ethtool_rxnfc * ring_count)3909 static int do_grxfhindir(struct cmd_context *ctx,
3910 struct ethtool_rxnfc *ring_count)
3911 {
3912 struct ethtool_rxfh_indir indir_head;
3913 struct ethtool_rxfh_indir *indir;
3914 int err;
3915
3916 indir_head.cmd = ETHTOOL_GRXFHINDIR;
3917 indir_head.size = 0;
3918 err = send_ioctl(ctx, &indir_head);
3919 if (err < 0) {
3920 perror("Cannot get RX flow hash indirection table size");
3921 return 1;
3922 }
3923
3924 indir = malloc(sizeof(*indir) +
3925 indir_head.size * sizeof(*indir->ring_index));
3926 if (!indir) {
3927 perror("Cannot allocate memory for indirection table");
3928 return 1;
3929 }
3930
3931 indir->cmd = ETHTOOL_GRXFHINDIR;
3932 indir->size = indir_head.size;
3933 err = send_ioctl(ctx, indir);
3934 if (err < 0) {
3935 perror("Cannot get RX flow hash indirection table");
3936 free(indir);
3937 return 1;
3938 }
3939
3940 print_indir_table(ctx, ring_count->data, indir->size,
3941 indir->ring_index);
3942
3943 free(indir);
3944 return 0;
3945 }
3946
do_grxfh(struct cmd_context * ctx)3947 static int do_grxfh(struct cmd_context *ctx)
3948 {
3949 struct ethtool_gstrings *hfuncs = NULL;
3950 struct ethtool_rxfh rss_head = {0};
3951 struct ethtool_rxnfc ring_count;
3952 struct ethtool_rxfh *rss;
3953 u32 rss_context = 0;
3954 u32 i, indir_bytes;
3955 unsigned int arg_num = 0;
3956 u8 *hkey;
3957 int err;
3958
3959 while (arg_num < ctx->argc) {
3960 if (!strcmp(ctx->argp[arg_num], "context")) {
3961 ++arg_num;
3962 rss_context = get_int_range(ctx->argp[arg_num], 0, 1,
3963 ETH_RXFH_CONTEXT_ALLOC - 1);
3964 ++arg_num;
3965 } else {
3966 exit_bad_args();
3967 }
3968 }
3969
3970 ring_count.cmd = ETHTOOL_GRXRINGS;
3971 err = send_ioctl(ctx, &ring_count);
3972 if (err < 0) {
3973 perror("Cannot get RX ring count");
3974 return 1;
3975 }
3976
3977 rss_head.cmd = ETHTOOL_GRSSH;
3978 rss_head.rss_context = rss_context;
3979 err = send_ioctl(ctx, &rss_head);
3980 if (err < 0 && errno == EOPNOTSUPP && !rss_context) {
3981 return do_grxfhindir(ctx, &ring_count);
3982 } else if (err < 0) {
3983 perror("Cannot get RX flow hash indir size and/or key size");
3984 return 1;
3985 }
3986
3987 rss = calloc(1, sizeof(*rss) +
3988 rss_head.indir_size * sizeof(rss_head.rss_config[0]) +
3989 rss_head.key_size);
3990 if (!rss) {
3991 perror("Cannot allocate memory for RX flow hash config");
3992 return 1;
3993 }
3994
3995 rss->cmd = ETHTOOL_GRSSH;
3996 rss->rss_context = rss_context;
3997 rss->indir_size = rss_head.indir_size;
3998 rss->key_size = rss_head.key_size;
3999 err = send_ioctl(ctx, rss);
4000 if (err < 0) {
4001 perror("Cannot get RX flow hash configuration");
4002 free(rss);
4003 return 1;
4004 }
4005
4006 print_indir_table(ctx, ring_count.data, rss->indir_size,
4007 rss->rss_config);
4008
4009 indir_bytes = rss->indir_size * sizeof(rss->rss_config[0]);
4010 hkey = ((u8 *)rss->rss_config + indir_bytes);
4011
4012 print_rss_hkey(hkey, rss->key_size);
4013
4014 printf("RSS hash function:\n");
4015 if (!rss->hfunc) {
4016 printf(" Operation not supported\n");
4017 goto out;
4018 }
4019
4020 hfuncs = get_stringset(ctx, ETH_SS_RSS_HASH_FUNCS, 0, 1);
4021 if (!hfuncs) {
4022 perror("Cannot get hash functions names");
4023 free(rss);
4024 return 1;
4025 }
4026
4027 for (i = 0; i < hfuncs->len; i++)
4028 printf(" %s: %s\n",
4029 (const char *)hfuncs->data + i * ETH_GSTRING_LEN,
4030 (rss->hfunc & (1 << i)) ? "on" : "off");
4031
4032 out:
4033 free(hfuncs);
4034 free(rss);
4035 return 0;
4036 }
4037
fill_indir_table(u32 * indir_size,u32 * indir,int rxfhindir_default,int rxfhindir_start,int rxfhindir_equal,char ** rxfhindir_weight,u32 num_weights)4038 static int fill_indir_table(u32 *indir_size, u32 *indir, int rxfhindir_default,
4039 int rxfhindir_start, int rxfhindir_equal,
4040 char **rxfhindir_weight, u32 num_weights)
4041 {
4042 u32 i;
4043
4044 if (rxfhindir_equal) {
4045 for (i = 0; i < *indir_size; i++)
4046 indir[i] = rxfhindir_start + (i % rxfhindir_equal);
4047 } else if (rxfhindir_weight) {
4048 u32 j, weight, sum = 0, partial = 0;
4049
4050 for (j = 0; j < num_weights; j++) {
4051 weight = get_u32(rxfhindir_weight[j], 0);
4052 sum += weight;
4053 }
4054
4055 if (sum == 0) {
4056 fprintf(stderr,
4057 "At least one weight must be non-zero\n");
4058 return 2;
4059 }
4060
4061 if (sum > *indir_size) {
4062 fprintf(stderr,
4063 "Total weight exceeds the size of the "
4064 "indirection table\n");
4065 return 2;
4066 }
4067
4068 j = -1;
4069 for (i = 0; i < *indir_size; i++) {
4070 while (i >= (*indir_size) * partial / sum) {
4071 j += 1;
4072 weight = get_u32(rxfhindir_weight[j], 0);
4073 partial += weight;
4074 }
4075 indir[i] = rxfhindir_start + j;
4076 }
4077 } else if (rxfhindir_default) {
4078 /* "*indir_size == 0" ==> reset indir to default */
4079 *indir_size = 0;
4080 } else {
4081 *indir_size = ETH_RXFH_INDIR_NO_CHANGE;
4082 }
4083
4084 return 0;
4085 }
4086
do_srxfhindir(struct cmd_context * ctx,int rxfhindir_default,int rxfhindir_start,int rxfhindir_equal,char ** rxfhindir_weight,u32 num_weights)4087 static int do_srxfhindir(struct cmd_context *ctx, int rxfhindir_default,
4088 int rxfhindir_start, int rxfhindir_equal,
4089 char **rxfhindir_weight, u32 num_weights)
4090 {
4091 struct ethtool_rxfh_indir indir_head;
4092 struct ethtool_rxfh_indir *indir;
4093 int err;
4094
4095 indir_head.cmd = ETHTOOL_GRXFHINDIR;
4096 indir_head.size = 0;
4097 err = send_ioctl(ctx, &indir_head);
4098 if (err < 0) {
4099 perror("Cannot get RX flow hash indirection table size");
4100 return 1;
4101 }
4102
4103 indir = malloc(sizeof(*indir) +
4104 indir_head.size * sizeof(*indir->ring_index));
4105
4106 if (!indir) {
4107 perror("Cannot allocate memory for indirection table");
4108 return 1;
4109 }
4110
4111 indir->cmd = ETHTOOL_SRXFHINDIR;
4112 indir->size = indir_head.size;
4113
4114 if (fill_indir_table(&indir->size, indir->ring_index,
4115 rxfhindir_default, rxfhindir_start,
4116 rxfhindir_equal, rxfhindir_weight, num_weights)) {
4117 free(indir);
4118 return 1;
4119 }
4120
4121 err = send_ioctl(ctx, indir);
4122 if (err < 0) {
4123 perror("Cannot set RX flow hash indirection table");
4124 free(indir);
4125 return 1;
4126 }
4127
4128 free(indir);
4129 return 0;
4130 }
4131
do_srxfh(struct cmd_context * ctx)4132 static int do_srxfh(struct cmd_context *ctx)
4133 {
4134 struct ethtool_rxfh rss_head = {0};
4135 struct ethtool_rxfh *rss = NULL;
4136 struct ethtool_rxnfc ring_count;
4137 int rxfhindir_equal = 0, rxfhindir_default = 0, rxfhindir_start = 0;
4138 struct ethtool_gstrings *hfuncs = NULL;
4139 char **rxfhindir_weight = NULL;
4140 char *rxfhindir_key = NULL;
4141 char *req_hfunc_name = NULL;
4142 char *hfunc_name = NULL;
4143 char *hkey = NULL;
4144 int err = 0;
4145 unsigned int i;
4146 u32 arg_num = 0, indir_bytes = 0;
4147 u32 req_hfunc = 0;
4148 u32 entry_size = sizeof(rss_head.rss_config[0]);
4149 u32 num_weights = 0;
4150 u32 rss_context = 0;
4151 int delete = 0;
4152
4153 if (ctx->argc < 1)
4154 exit_bad_args();
4155
4156 while (arg_num < ctx->argc) {
4157 if (!strcmp(ctx->argp[arg_num], "equal")) {
4158 ++arg_num;
4159 rxfhindir_equal = get_int_range(ctx->argp[arg_num],
4160 0, 1, INT_MAX);
4161 ++arg_num;
4162 } else if (!strcmp(ctx->argp[arg_num], "start")) {
4163 ++arg_num;
4164 rxfhindir_start = get_int_range(ctx->argp[arg_num],
4165 0, 0, INT_MAX);
4166 ++arg_num;
4167 } else if (!strcmp(ctx->argp[arg_num], "weight")) {
4168 ++arg_num;
4169 rxfhindir_weight = ctx->argp + arg_num;
4170 while (arg_num < ctx->argc &&
4171 isdigit((unsigned char)ctx->argp[arg_num][0])) {
4172 ++arg_num;
4173 ++num_weights;
4174 }
4175 if (!num_weights)
4176 exit_bad_args();
4177 } else if (!strcmp(ctx->argp[arg_num], "hkey")) {
4178 ++arg_num;
4179 rxfhindir_key = ctx->argp[arg_num];
4180 if (!rxfhindir_key)
4181 exit_bad_args();
4182 ++arg_num;
4183 } else if (!strcmp(ctx->argp[arg_num], "default")) {
4184 ++arg_num;
4185 rxfhindir_default = 1;
4186 } else if (!strcmp(ctx->argp[arg_num], "hfunc")) {
4187 ++arg_num;
4188 req_hfunc_name = ctx->argp[arg_num];
4189 if (!req_hfunc_name)
4190 exit_bad_args();
4191 ++arg_num;
4192 } else if (!strcmp(ctx->argp[arg_num], "context")) {
4193 ++arg_num;
4194 if(!strcmp(ctx->argp[arg_num], "new"))
4195 rss_context = ETH_RXFH_CONTEXT_ALLOC;
4196 else
4197 rss_context = get_int_range(
4198 ctx->argp[arg_num], 0, 1,
4199 ETH_RXFH_CONTEXT_ALLOC - 1);
4200 ++arg_num;
4201 } else if (!strcmp(ctx->argp[arg_num], "delete")) {
4202 ++arg_num;
4203 delete = 1;
4204 } else {
4205 exit_bad_args();
4206 }
4207 }
4208
4209 if (rxfhindir_equal && rxfhindir_weight) {
4210 fprintf(stderr,
4211 "Equal and weight options are mutually exclusive\n");
4212 return 1;
4213 }
4214
4215 if (rxfhindir_equal && rxfhindir_default) {
4216 fprintf(stderr,
4217 "Equal and default options are mutually exclusive\n");
4218 return 1;
4219 }
4220
4221 if (rxfhindir_weight && rxfhindir_default) {
4222 fprintf(stderr,
4223 "Weight and default options are mutually exclusive\n");
4224 return 1;
4225 }
4226
4227 if (rxfhindir_start && rxfhindir_default) {
4228 fprintf(stderr,
4229 "Start and default options are mutually exclusive\n");
4230 return 1;
4231 }
4232
4233 if (rxfhindir_start && !(rxfhindir_equal || rxfhindir_weight)) {
4234 fprintf(stderr,
4235 "Start must be used with equal or weight options\n");
4236 return 1;
4237 }
4238
4239 if (rxfhindir_default && rss_context) {
4240 fprintf(stderr,
4241 "Default and context options are mutually exclusive\n");
4242 return 1;
4243 }
4244
4245 if (delete && !rss_context) {
4246 fprintf(stderr, "Delete option requires context option\n");
4247 return 1;
4248 }
4249
4250 if (delete && rxfhindir_weight) {
4251 fprintf(stderr,
4252 "Delete and weight options are mutually exclusive\n");
4253 return 1;
4254 }
4255
4256 if (delete && rxfhindir_equal) {
4257 fprintf(stderr,
4258 "Delete and equal options are mutually exclusive\n");
4259 return 1;
4260 }
4261
4262 if (delete && rxfhindir_default) {
4263 fprintf(stderr,
4264 "Delete and default options are mutually exclusive\n");
4265 return 1;
4266 }
4267
4268 if (delete && rxfhindir_key) {
4269 fprintf(stderr,
4270 "Delete and hkey options are mutually exclusive\n");
4271 return 1;
4272 }
4273
4274 ring_count.cmd = ETHTOOL_GRXRINGS;
4275 err = send_ioctl(ctx, &ring_count);
4276 if (err < 0) {
4277 perror("Cannot get RX ring count");
4278 return 1;
4279 }
4280
4281 rss_head.cmd = ETHTOOL_GRSSH;
4282 err = send_ioctl(ctx, &rss_head);
4283 if (err < 0 && errno == EOPNOTSUPP && !rxfhindir_key &&
4284 !req_hfunc_name && !rss_context) {
4285 return do_srxfhindir(ctx, rxfhindir_default, rxfhindir_start,
4286 rxfhindir_equal, rxfhindir_weight,
4287 num_weights);
4288 } else if (err < 0) {
4289 perror("Cannot get RX flow hash indir size and key size");
4290 return 1;
4291 }
4292
4293 if (rxfhindir_key) {
4294 err = parse_hkey(&hkey, rss_head.key_size,
4295 rxfhindir_key);
4296 if (err)
4297 return err;
4298 }
4299
4300 if (rxfhindir_equal || rxfhindir_weight)
4301 indir_bytes = rss_head.indir_size * entry_size;
4302
4303 if (rss_head.hfunc && req_hfunc_name) {
4304 hfuncs = get_stringset(ctx, ETH_SS_RSS_HASH_FUNCS, 0, 1);
4305 if (!hfuncs) {
4306 perror("Cannot get hash functions names");
4307 err = 1;
4308 goto free;
4309 }
4310
4311 for (i = 0; i < hfuncs->len && !req_hfunc ; i++) {
4312 hfunc_name = (char *)(hfuncs->data +
4313 i * ETH_GSTRING_LEN);
4314 if (!strncmp(hfunc_name, req_hfunc_name,
4315 ETH_GSTRING_LEN))
4316 req_hfunc = (u32)1 << i;
4317 }
4318
4319 if (!req_hfunc) {
4320 fprintf(stderr,
4321 "Unknown hash function: %s\n", req_hfunc_name);
4322 err = 1;
4323 goto free;
4324 }
4325 }
4326
4327 rss = calloc(1, sizeof(*rss) + indir_bytes + rss_head.key_size);
4328 if (!rss) {
4329 perror("Cannot allocate memory for RX flow hash config");
4330 err = 1;
4331 goto free;
4332 }
4333 rss->cmd = ETHTOOL_SRSSH;
4334 rss->rss_context = rss_context;
4335 rss->hfunc = req_hfunc;
4336 if (delete) {
4337 rss->indir_size = rss->key_size = 0;
4338 } else {
4339 rss->indir_size = rss_head.indir_size;
4340 rss->key_size = rss_head.key_size;
4341 if (fill_indir_table(&rss->indir_size, rss->rss_config,
4342 rxfhindir_default, rxfhindir_start,
4343 rxfhindir_equal, rxfhindir_weight,
4344 num_weights)) {
4345 err = 1;
4346 goto free;
4347 }
4348 }
4349
4350 if (hkey)
4351 memcpy((char *)rss->rss_config + indir_bytes,
4352 hkey, rss->key_size);
4353 else
4354 rss->key_size = 0;
4355
4356 err = send_ioctl(ctx, rss);
4357 if (err < 0) {
4358 perror("Cannot set RX flow hash configuration");
4359 err = 1;
4360 } else if (rss_context == ETH_RXFH_CONTEXT_ALLOC) {
4361 printf("New RSS context is %d\n", rss->rss_context);
4362 }
4363
4364 free:
4365 free(hkey);
4366 free(rss);
4367 free(hfuncs);
4368 return err;
4369 }
4370
do_flash(struct cmd_context * ctx)4371 static int do_flash(struct cmd_context *ctx)
4372 {
4373 char *flash_file;
4374 int flash_region;
4375 struct ethtool_flash efl;
4376 int err;
4377
4378 if (ctx->argc < 1 || ctx->argc > 2)
4379 exit_bad_args();
4380 flash_file = ctx->argp[0];
4381 if (ctx->argc == 2) {
4382 flash_region = strtol(ctx->argp[1], NULL, 0);
4383 if (flash_region < 0)
4384 exit_bad_args();
4385 } else {
4386 flash_region = -1;
4387 }
4388
4389 if (strlen(flash_file) > ETHTOOL_FLASH_MAX_FILENAME - 1) {
4390 fprintf(stdout, "Filename too long\n");
4391 return 99;
4392 }
4393
4394 efl.cmd = ETHTOOL_FLASHDEV;
4395 strcpy(efl.data, flash_file);
4396
4397 if (flash_region < 0)
4398 efl.region = ETHTOOL_FLASH_ALL_REGIONS;
4399 else
4400 efl.region = flash_region;
4401
4402 err = send_ioctl(ctx, &efl);
4403 if (err < 0)
4404 perror("Flashing failed");
4405
4406 return err;
4407 }
4408
do_permaddr(struct cmd_context * ctx)4409 static int do_permaddr(struct cmd_context *ctx)
4410 {
4411 unsigned int i;
4412 int err;
4413 struct ethtool_perm_addr *epaddr;
4414
4415 epaddr = malloc(sizeof(struct ethtool_perm_addr) + MAX_ADDR_LEN);
4416 if (!epaddr) {
4417 perror("Cannot allocate memory for operation");
4418 return 1;
4419 }
4420
4421 epaddr->cmd = ETHTOOL_GPERMADDR;
4422 epaddr->size = MAX_ADDR_LEN;
4423
4424 err = send_ioctl(ctx, epaddr);
4425 if (err < 0)
4426 perror("Cannot read permanent address");
4427 else {
4428 printf("Permanent address:");
4429 for (i = 0; i < epaddr->size; i++)
4430 printf("%c%02x", (i == 0) ? ' ' : ':',
4431 epaddr->data[i]);
4432 printf("\n");
4433 }
4434 free(epaddr);
4435
4436 return err;
4437 }
4438
flow_type_is_ntuple_supported(__u32 flow_type)4439 static bool flow_type_is_ntuple_supported(__u32 flow_type)
4440 {
4441 switch (flow_type) {
4442 case TCP_V4_FLOW:
4443 case UDP_V4_FLOW:
4444 case SCTP_V4_FLOW:
4445 case AH_V4_FLOW:
4446 case ESP_V4_FLOW:
4447 case IPV4_USER_FLOW:
4448 case ETHER_FLOW:
4449 return true;
4450 default:
4451 return false;
4452 }
4453 }
4454
flow_spec_to_ntuple(struct ethtool_rx_flow_spec * fsp,struct ethtool_rx_ntuple_flow_spec * ntuple)4455 static int flow_spec_to_ntuple(struct ethtool_rx_flow_spec *fsp,
4456 struct ethtool_rx_ntuple_flow_spec *ntuple)
4457 {
4458 size_t i;
4459
4460 /* verify location is not specified */
4461 if (fsp->location != RX_CLS_LOC_ANY)
4462 return -1;
4463
4464 /* destination MAC address in L3/L4 rules is not supported by ntuple */
4465 if (fsp->flow_type & FLOW_MAC_EXT)
4466 return -1;
4467
4468 /* verify ring cookie can transfer to action */
4469 if (fsp->ring_cookie > INT_MAX && fsp->ring_cookie < (u64)(-2))
4470 return -1;
4471
4472 /* verify only one field is setting data field */
4473 if ((fsp->flow_type & FLOW_EXT) &&
4474 (fsp->m_ext.data[0] || fsp->m_ext.data[1]) &&
4475 fsp->m_ext.vlan_etype)
4476 return -1;
4477
4478 /* IPv6 flow types are not supported by ntuple */
4479 if (!flow_type_is_ntuple_supported(fsp->flow_type & ~FLOW_EXT))
4480 return -1;
4481
4482 /* Set entire ntuple to ~0 to guarantee all masks are set */
4483 memset(ntuple, ~0, sizeof(*ntuple));
4484
4485 /* set non-filter values */
4486 ntuple->flow_type = fsp->flow_type;
4487 ntuple->action = fsp->ring_cookie;
4488
4489 /*
4490 * Copy over header union, they are identical in layout however
4491 * the ntuple union contains additional padding on the end
4492 */
4493 memcpy(&ntuple->h_u, &fsp->h_u, sizeof(fsp->h_u));
4494
4495 /*
4496 * The same rule mentioned above applies to the mask union. However,
4497 * in addition we need to invert the mask bits to match the ntuple
4498 * mask which is 1 for masked, versus 0 for masked as seen in nfc.
4499 */
4500 memcpy(&ntuple->m_u, &fsp->m_u, sizeof(fsp->m_u));
4501 for (i = 0; i < sizeof(fsp->m_u); i++)
4502 ntuple->m_u.hdata[i] ^= 0xFF;
4503
4504 /* copy extended fields */
4505 if (fsp->flow_type & FLOW_EXT) {
4506 ntuple->vlan_tag =
4507 ntohs(fsp->h_ext.vlan_tci);
4508 ntuple->vlan_tag_mask =
4509 ~ntohs(fsp->m_ext.vlan_tci);
4510 if (fsp->m_ext.vlan_etype) {
4511 /*
4512 * vlan_etype and user data are mutually exclusive
4513 * in ntuple configuration as they occupy the same
4514 * space.
4515 */
4516 if (fsp->m_ext.data[0] || fsp->m_ext.data[1])
4517 return -1;
4518 ntuple->data =
4519 ntohl(fsp->h_ext.vlan_etype);
4520 ntuple->data_mask =
4521 ~(u64)ntohl(fsp->m_ext.vlan_etype);
4522 } else {
4523 ntuple->data =
4524 (u64)ntohl(fsp->h_ext.data[0]) << 32;
4525 ntuple->data |=
4526 (u64)ntohl(fsp->h_ext.data[1]);
4527 ntuple->data_mask =
4528 (u64)ntohl(~fsp->m_ext.data[0]) << 32;
4529 ntuple->data_mask |=
4530 (u64)ntohl(~fsp->m_ext.data[1]);
4531 }
4532 }
4533
4534 /* Mask out the extended bit, because ntuple does not know it! */
4535 ntuple->flow_type &= ~FLOW_EXT;
4536
4537 return 0;
4538 }
4539
do_srxntuple(struct cmd_context * ctx,struct ethtool_rx_flow_spec * rx_rule_fs)4540 static int do_srxntuple(struct cmd_context *ctx,
4541 struct ethtool_rx_flow_spec *rx_rule_fs)
4542 {
4543 struct ethtool_rx_ntuple ntuplecmd;
4544 struct ethtool_value eval;
4545 int err;
4546
4547 /* attempt to convert the flow classifier to an ntuple classifier */
4548 err = flow_spec_to_ntuple(rx_rule_fs, &ntuplecmd.fs);
4549 if (err)
4550 return -1;
4551
4552 /*
4553 * Check to see if the flag is set for N-tuple, this allows
4554 * us to avoid the possible EINVAL response for the N-tuple
4555 * flag not being set on the device
4556 */
4557 eval.cmd = ETHTOOL_GFLAGS;
4558 err = send_ioctl(ctx, &eval);
4559 if (err || !(eval.data & ETH_FLAG_NTUPLE))
4560 return -1;
4561
4562 /* send rule via N-tuple */
4563 ntuplecmd.cmd = ETHTOOL_SRXNTUPLE;
4564 err = send_ioctl(ctx, &ntuplecmd);
4565
4566 /*
4567 * Display error only if response is something other than op not
4568 * supported. It is possible that the interface uses the network
4569 * flow classifier interface instead of N-tuple.
4570 */
4571 if (err < 0) {
4572 if (errno != EOPNOTSUPP)
4573 perror("Cannot add new rule via N-tuple");
4574 return -1;
4575 }
4576
4577 return 0;
4578 }
4579
do_writefwdump(struct ethtool_dump * dump,const char * dump_file)4580 static int do_writefwdump(struct ethtool_dump *dump, const char *dump_file)
4581 {
4582 int err = 0;
4583 FILE *f;
4584 size_t bytes;
4585
4586 f = fopen(dump_file, "wb+");
4587
4588 if (!f) {
4589 fprintf(stderr, "Can't open file %s: %s\n",
4590 dump_file, strerror(errno));
4591 return 1;
4592 }
4593 bytes = fwrite(dump->data, 1, dump->len, f);
4594 if (bytes != dump->len) {
4595 fprintf(stderr, "Can not write all of dump data\n");
4596 err = 1;
4597 }
4598 if (fclose(f)) {
4599 fprintf(stderr, "Can't close file %s: %s\n",
4600 dump_file, strerror(errno));
4601 err = 1;
4602 }
4603 return err;
4604 }
4605
do_getfwdump(struct cmd_context * ctx)4606 static int do_getfwdump(struct cmd_context *ctx)
4607 {
4608 u32 dump_flag;
4609 char *dump_file;
4610 int err;
4611 struct ethtool_dump edata;
4612 struct ethtool_dump *data;
4613
4614 if (ctx->argc == 2 && !strcmp(ctx->argp[0], "data")) {
4615 dump_flag = ETHTOOL_GET_DUMP_DATA;
4616 dump_file = ctx->argp[1];
4617 } else if (ctx->argc == 0) {
4618 dump_flag = 0;
4619 dump_file = NULL;
4620 } else {
4621 exit_bad_args();
4622 }
4623
4624 edata.cmd = ETHTOOL_GET_DUMP_FLAG;
4625
4626 err = send_ioctl(ctx, &edata);
4627 if (err < 0) {
4628 perror("Can not get dump level");
4629 return 1;
4630 }
4631 if (dump_flag != ETHTOOL_GET_DUMP_DATA) {
4632 fprintf(stdout, "flag: %u, version: %u, length: %u\n",
4633 edata.flag, edata.version, edata.len);
4634 return 0;
4635 }
4636 data = calloc(1, offsetof(struct ethtool_dump, data) + edata.len);
4637 if (!data) {
4638 perror("Can not allocate enough memory");
4639 return 1;
4640 }
4641 data->cmd = ETHTOOL_GET_DUMP_DATA;
4642 data->len = edata.len;
4643 err = send_ioctl(ctx, data);
4644 if (err < 0) {
4645 perror("Can not get dump data");
4646 err = 1;
4647 goto free;
4648 }
4649 err = do_writefwdump(data, dump_file);
4650 free:
4651 free(data);
4652 return err;
4653 }
4654
do_setfwdump(struct cmd_context * ctx)4655 static int do_setfwdump(struct cmd_context *ctx)
4656 {
4657 u32 dump_flag;
4658 int err;
4659 struct ethtool_dump dump;
4660
4661 if (ctx->argc != 1)
4662 exit_bad_args();
4663 dump_flag = get_u32(ctx->argp[0], 0);
4664
4665 dump.cmd = ETHTOOL_SET_DUMP;
4666 dump.flag = dump_flag;
4667 err = send_ioctl(ctx, &dump);
4668 if (err < 0) {
4669 perror("Can not set dump level");
4670 return 1;
4671 }
4672 return 0;
4673 }
4674
do_gprivflags(struct cmd_context * ctx)4675 static int do_gprivflags(struct cmd_context *ctx)
4676 {
4677 struct ethtool_gstrings *strings;
4678 struct ethtool_value flags;
4679 unsigned int i;
4680 int max_len = 0, cur_len, rc;
4681
4682 if (ctx->argc != 0)
4683 exit_bad_args();
4684
4685 strings = get_stringset(ctx, ETH_SS_PRIV_FLAGS,
4686 offsetof(struct ethtool_drvinfo, n_priv_flags),
4687 1);
4688 if (!strings) {
4689 perror("Cannot get private flag names");
4690 return 1;
4691 }
4692 if (strings->len == 0) {
4693 fprintf(stderr, "No private flags defined\n");
4694 rc = 1;
4695 goto err;
4696 }
4697 if (strings->len > 32) {
4698 /* ETHTOOL_GPFLAGS can only cover 32 flags */
4699 fprintf(stderr, "Only showing first 32 private flags\n");
4700 strings->len = 32;
4701 }
4702
4703 flags.cmd = ETHTOOL_GPFLAGS;
4704 if (send_ioctl(ctx, &flags)) {
4705 perror("Cannot get private flags");
4706 rc = 1;
4707 goto err;
4708 }
4709
4710 /* Find longest string and align all strings accordingly */
4711 for (i = 0; i < strings->len; i++) {
4712 cur_len = strlen((const char *)strings->data +
4713 i * ETH_GSTRING_LEN);
4714 if (cur_len > max_len)
4715 max_len = cur_len;
4716 }
4717
4718 printf("Private flags for %s:\n", ctx->devname);
4719 for (i = 0; i < strings->len; i++)
4720 printf("%-*s: %s\n",
4721 max_len,
4722 (const char *)strings->data + i * ETH_GSTRING_LEN,
4723 (flags.data & (1U << i)) ? "on" : "off");
4724
4725 rc = 0;
4726
4727 err:
4728 free(strings);
4729 return rc;
4730 }
4731
do_sprivflags(struct cmd_context * ctx)4732 static int do_sprivflags(struct cmd_context *ctx)
4733 {
4734 struct ethtool_gstrings *strings;
4735 struct cmdline_info *cmdline;
4736 struct ethtool_value flags;
4737 u32 wanted_flags = 0, seen_flags = 0;
4738 int any_changed, rc;
4739 unsigned int i;
4740
4741 strings = get_stringset(ctx, ETH_SS_PRIV_FLAGS,
4742 offsetof(struct ethtool_drvinfo, n_priv_flags),
4743 1);
4744 if (!strings) {
4745 perror("Cannot get private flag names");
4746 return 1;
4747 }
4748 if (strings->len == 0) {
4749 fprintf(stderr, "No private flags defined\n");
4750 rc = 1;
4751 goto err;
4752 }
4753 if (strings->len > 32) {
4754 /* ETHTOOL_{G,S}PFLAGS can only cover 32 flags */
4755 fprintf(stderr, "Only setting first 32 private flags\n");
4756 strings->len = 32;
4757 }
4758
4759 cmdline = calloc(strings->len, sizeof(*cmdline));
4760 if (!cmdline) {
4761 perror("Cannot parse arguments");
4762 rc = 1;
4763 goto err;
4764 }
4765 for (i = 0; i < strings->len; i++) {
4766 cmdline[i].name = ((const char *)strings->data +
4767 i * ETH_GSTRING_LEN);
4768 cmdline[i].type = CMDL_FLAG;
4769 cmdline[i].wanted_val = &wanted_flags;
4770 cmdline[i].flag_val = 1U << i;
4771 cmdline[i].seen_val = &seen_flags;
4772 }
4773 parse_generic_cmdline(ctx, &any_changed, cmdline, strings->len);
4774 free(cmdline);
4775
4776 flags.cmd = ETHTOOL_GPFLAGS;
4777 if (send_ioctl(ctx, &flags)) {
4778 perror("Cannot get private flags");
4779 rc = 1;
4780 goto err;
4781 }
4782
4783 flags.cmd = ETHTOOL_SPFLAGS;
4784 flags.data = (flags.data & ~seen_flags) | wanted_flags;
4785 if (send_ioctl(ctx, &flags)) {
4786 perror("Cannot set private flags");
4787 rc = 1;
4788 goto err;
4789 }
4790
4791 rc = 0;
4792 err:
4793 free(strings);
4794 return rc;
4795 }
4796
do_tsinfo(struct cmd_context * ctx)4797 static int do_tsinfo(struct cmd_context *ctx)
4798 {
4799 struct ethtool_ts_info info;
4800
4801 if (ctx->argc != 0)
4802 exit_bad_args();
4803
4804 fprintf(stdout, "Time stamping parameters for %s:\n", ctx->devname);
4805 info.cmd = ETHTOOL_GET_TS_INFO;
4806 if (send_ioctl(ctx, &info)) {
4807 perror("Cannot get device time stamping settings");
4808 return -1;
4809 }
4810 dump_tsinfo(&info);
4811 return 0;
4812 }
4813
do_getmodule(struct cmd_context * ctx)4814 static int do_getmodule(struct cmd_context *ctx)
4815 {
4816 struct ethtool_modinfo modinfo;
4817 struct ethtool_eeprom *eeprom;
4818 u32 geeprom_offset = 0;
4819 u32 geeprom_length = 0;
4820 int geeprom_changed = 0;
4821 int geeprom_dump_raw = 0;
4822 int geeprom_dump_hex = 0;
4823 int geeprom_length_seen = 0;
4824 int err;
4825
4826 struct cmdline_info cmdline_geeprom[] = {
4827 {
4828 .name = "offset",
4829 .type = CMDL_U32,
4830 .wanted_val = &geeprom_offset,
4831 },
4832 {
4833 .name = "length",
4834 .type = CMDL_U32,
4835 .wanted_val = &geeprom_length,
4836 .seen_val = &geeprom_length_seen,
4837 },
4838 {
4839 .name = "raw",
4840 .type = CMDL_BOOL,
4841 .wanted_val = &geeprom_dump_raw,
4842 },
4843 {
4844 .name = "hex",
4845 .type = CMDL_BOOL,
4846 .wanted_val = &geeprom_dump_hex,
4847 },
4848 };
4849
4850 parse_generic_cmdline(ctx, &geeprom_changed,
4851 cmdline_geeprom, ARRAY_SIZE(cmdline_geeprom));
4852
4853 if (geeprom_dump_raw && geeprom_dump_hex) {
4854 printf("Hex and raw dump cannot be specified together\n");
4855 return 1;
4856 }
4857
4858 modinfo.cmd = ETHTOOL_GMODULEINFO;
4859 err = send_ioctl(ctx, &modinfo);
4860 if (err < 0) {
4861 perror("Cannot get module EEPROM information");
4862 return 1;
4863 }
4864
4865 if (!geeprom_length_seen)
4866 geeprom_length = modinfo.eeprom_len;
4867
4868 if (modinfo.eeprom_len < geeprom_offset + geeprom_length)
4869 geeprom_length = modinfo.eeprom_len - geeprom_offset;
4870
4871 eeprom = calloc(1, sizeof(*eeprom)+geeprom_length);
4872 if (!eeprom) {
4873 perror("Cannot allocate memory for Module EEPROM data");
4874 return 1;
4875 }
4876
4877 eeprom->cmd = ETHTOOL_GMODULEEEPROM;
4878 eeprom->len = geeprom_length;
4879 eeprom->offset = geeprom_offset;
4880 err = send_ioctl(ctx, eeprom);
4881 if (err < 0) {
4882 int saved_errno = errno;
4883
4884 perror("Cannot get Module EEPROM data");
4885 if (saved_errno == ENODEV || saved_errno == EIO ||
4886 saved_errno == ENXIO)
4887 fprintf(stderr, "SFP module not in cage?\n");
4888 free(eeprom);
4889 return 1;
4890 }
4891
4892 /*
4893 * SFF-8079 EEPROM layout contains the memory available at A0 address on
4894 * the PHY EEPROM.
4895 * SFF-8472 defines a virtual extension of the EEPROM, where the
4896 * microcontroller on the SFP/SFP+ generates a page at the A2 address,
4897 * which contains data relative to optical diagnostics.
4898 * The current kernel implementation returns a blob, which contains:
4899 * - ETH_MODULE_SFF_8079 => The A0 page only.
4900 * - ETH_MODULE_SFF_8472 => The A0 and A2 page concatenated.
4901 */
4902 if (geeprom_dump_raw) {
4903 fwrite(eeprom->data, 1, eeprom->len, stdout);
4904 } else {
4905 if (eeprom->offset != 0 ||
4906 (eeprom->len != modinfo.eeprom_len)) {
4907 geeprom_dump_hex = 1;
4908 } else if (!geeprom_dump_hex) {
4909 switch (modinfo.type) {
4910 #ifdef ETHTOOL_ENABLE_PRETTY_DUMP
4911 case ETH_MODULE_SFF_8079:
4912 sff8079_show_all_ioctl(eeprom->data);
4913 break;
4914 case ETH_MODULE_SFF_8472:
4915 sff8079_show_all_ioctl(eeprom->data);
4916 sff8472_show_all(eeprom->data);
4917 break;
4918 case ETH_MODULE_SFF_8436:
4919 case ETH_MODULE_SFF_8636:
4920 sff8636_show_all_ioctl(eeprom->data,
4921 modinfo.eeprom_len);
4922 break;
4923 #endif
4924 default:
4925 geeprom_dump_hex = 1;
4926 break;
4927 }
4928 }
4929 if (geeprom_dump_hex)
4930 dump_hex(stdout, eeprom->data,
4931 eeprom->len, eeprom->offset);
4932 }
4933
4934 free(eeprom);
4935
4936 return 0;
4937 }
4938
do_geee(struct cmd_context * ctx)4939 static int do_geee(struct cmd_context *ctx)
4940 {
4941 struct ethtool_eee eeecmd;
4942
4943 if (ctx->argc != 0)
4944 exit_bad_args();
4945
4946 eeecmd.cmd = ETHTOOL_GEEE;
4947 if (send_ioctl(ctx, &eeecmd)) {
4948 perror("Cannot get EEE settings");
4949 return 1;
4950 }
4951
4952 fprintf(stdout, "EEE Settings for %s:\n", ctx->devname);
4953 dump_eeecmd(&eeecmd);
4954
4955 return 0;
4956 }
4957
do_seee(struct cmd_context * ctx)4958 static int do_seee(struct cmd_context *ctx)
4959 {
4960 int adv_c = -1, lpi_c = -1, lpi_time_c = -1, eee_c = -1;
4961 int change = -1, change2 = 0;
4962 struct ethtool_eee eeecmd;
4963 struct cmdline_info cmdline_eee[] = {
4964 {
4965 .name = "advertise",
4966 .type = CMDL_U32,
4967 .wanted_val = &adv_c,
4968 .ioctl_val = &eeecmd.advertised,
4969 },
4970 {
4971 .name = "tx-lpi",
4972 .type = CMDL_BOOL,
4973 .wanted_val = &lpi_c,
4974 .ioctl_val = &eeecmd.tx_lpi_enabled,
4975 },
4976 {
4977 .name = "tx-timer",
4978 .type = CMDL_U32,
4979 .wanted_val = &lpi_time_c,
4980 .ioctl_val = &eeecmd.tx_lpi_timer,
4981 },
4982 {
4983 .name = "eee",
4984 .type = CMDL_BOOL,
4985 .wanted_val = &eee_c,
4986 .ioctl_val = &eeecmd.eee_enabled,
4987 },
4988 };
4989
4990 if (ctx->argc == 0)
4991 exit_bad_args();
4992
4993 parse_generic_cmdline(ctx, &change, cmdline_eee,
4994 ARRAY_SIZE(cmdline_eee));
4995
4996 eeecmd.cmd = ETHTOOL_GEEE;
4997 if (send_ioctl(ctx, &eeecmd)) {
4998 perror("Cannot get EEE settings");
4999 return 1;
5000 }
5001
5002 do_generic_set(cmdline_eee, ARRAY_SIZE(cmdline_eee), &change2);
5003
5004 if (change2) {
5005 eeecmd.cmd = ETHTOOL_SEEE;
5006 if (send_ioctl(ctx, &eeecmd)) {
5007 perror("Cannot set EEE settings");
5008 return 1;
5009 }
5010 }
5011
5012 return 0;
5013 }
5014
5015 /* copy of net/ethtool/common.c */
5016 char
5017 tunable_strings[__ETHTOOL_TUNABLE_COUNT][ETH_GSTRING_LEN] = {
5018 [ETHTOOL_ID_UNSPEC] = "Unspec",
5019 [ETHTOOL_RX_COPYBREAK] = "rx-copybreak",
5020 [ETHTOOL_TX_COPYBREAK] = "tx-copybreak",
5021 [ETHTOOL_TX_COPYBREAK_BUF_SIZE] = "tx-buf-size",
5022 [ETHTOOL_PFC_PREVENTION_TOUT] = "pfc-prevention-tout",
5023 };
5024
5025 union ethtool_tunable_info_val {
5026 uint8_t u8;
5027 uint16_t u16;
5028 uint32_t u32;
5029 uint64_t u64;
5030 int8_t s8;
5031 int16_t s16;
5032 int32_t s32;
5033 int64_t s64;
5034 };
5035
5036 struct ethtool_tunable_info {
5037 enum tunable_id t_id;
5038 enum tunable_type_id t_type_id;
5039 size_t size;
5040 cmdline_type_t type;
5041 union ethtool_tunable_info_val wanted;
5042 int seen;
5043 };
5044
5045 static struct ethtool_tunable_info tunables_info[] = {
5046 { .t_id = ETHTOOL_RX_COPYBREAK,
5047 .t_type_id = ETHTOOL_TUNABLE_U32,
5048 .size = sizeof(u32),
5049 .type = CMDL_U32,
5050 },
5051 { .t_id = ETHTOOL_TX_COPYBREAK,
5052 .t_type_id = ETHTOOL_TUNABLE_U32,
5053 .size = sizeof(u32),
5054 .type = CMDL_U32,
5055 },
5056 { .t_id = ETHTOOL_PFC_PREVENTION_TOUT,
5057 .t_type_id = ETHTOOL_TUNABLE_U16,
5058 .size = sizeof(u16),
5059 .type = CMDL_U16,
5060 },
5061 { .t_id = ETHTOOL_TX_COPYBREAK_BUF_SIZE,
5062 .t_type_id = ETHTOOL_TUNABLE_U32,
5063 .size = sizeof(u32),
5064 .type = CMDL_U32,
5065 },
5066 };
5067 #define TUNABLES_INFO_SIZE ARRAY_SIZE(tunables_info)
5068
do_stunable(struct cmd_context * ctx)5069 static int do_stunable(struct cmd_context *ctx)
5070 {
5071 struct cmdline_info cmdline_tunable[TUNABLES_INFO_SIZE];
5072 struct ethtool_tunable_info *tinfo = tunables_info;
5073 int changed = 0;
5074 unsigned int i;
5075
5076 for (i = 0; i < TUNABLES_INFO_SIZE; i++) {
5077 cmdline_tunable[i].name = tunable_strings[tinfo[i].t_id];
5078 cmdline_tunable[i].type = tinfo[i].type;
5079 cmdline_tunable[i].wanted_val = &tinfo[i].wanted;
5080 cmdline_tunable[i].seen_val = &tinfo[i].seen;
5081 }
5082
5083 parse_generic_cmdline(ctx, &changed, cmdline_tunable, TUNABLES_INFO_SIZE);
5084 if (!changed)
5085 exit_bad_args();
5086
5087 for (i = 0; i < TUNABLES_INFO_SIZE; i++) {
5088 struct ethtool_tunable *tuna;
5089 size_t size;
5090 int ret;
5091
5092 if (!tinfo[i].seen)
5093 continue;
5094
5095 size = sizeof(*tuna) + tinfo[i].size;
5096 tuna = calloc(1, size);
5097 if (!tuna) {
5098 perror(tunable_strings[tinfo[i].t_id]);
5099 return 1;
5100 }
5101 tuna->cmd = ETHTOOL_STUNABLE;
5102 tuna->id = tinfo[i].t_id;
5103 tuna->type_id = tinfo[i].t_type_id;
5104 tuna->len = tinfo[i].size;
5105 memcpy(tuna->data, &tinfo[i].wanted, tuna->len);
5106 ret = send_ioctl(ctx, tuna);
5107 if (ret) {
5108 perror(tunable_strings[tuna->id]);
5109 free(tuna);
5110 return ret;
5111 }
5112 free(tuna);
5113 }
5114 return 0;
5115 }
5116
print_tunable(struct ethtool_tunable * tuna)5117 static void print_tunable(struct ethtool_tunable *tuna)
5118 {
5119 char *name = tunable_strings[tuna->id];
5120 union ethtool_tunable_info_val *val;
5121
5122 val = (union ethtool_tunable_info_val *)tuna->data;
5123 switch (tuna->type_id) {
5124 case ETHTOOL_TUNABLE_U8:
5125 fprintf(stdout, "%s: %" PRIu8 "\n", name, val->u8);
5126 break;
5127 case ETHTOOL_TUNABLE_U16:
5128 fprintf(stdout, "%s: %" PRIu16 "\n", name, val->u16);
5129 break;
5130 case ETHTOOL_TUNABLE_U32:
5131 fprintf(stdout, "%s: %" PRIu32 "\n", name, val->u32);
5132 break;
5133 case ETHTOOL_TUNABLE_U64:
5134 fprintf(stdout, "%s: %" PRIu64 "\n", name, val->u64);
5135 break;
5136 case ETHTOOL_TUNABLE_S8:
5137 fprintf(stdout, "%s: %" PRId8 "\n", name, val->s8);
5138 break;
5139 case ETHTOOL_TUNABLE_S16:
5140 fprintf(stdout, "%s: %" PRId16 "\n", name, val->s16);
5141 break;
5142 case ETHTOOL_TUNABLE_S32:
5143 fprintf(stdout, "%s: %" PRId32 "\n", name, val->s32);
5144 break;
5145 case ETHTOOL_TUNABLE_S64:
5146 fprintf(stdout, "%s: %" PRId64 "\n", name, val->s64);
5147 break;
5148 default:
5149 fprintf(stdout, "%s: Unknown format\n", name);
5150 }
5151 }
5152
do_gtunable(struct cmd_context * ctx)5153 static int do_gtunable(struct cmd_context *ctx)
5154 {
5155 struct ethtool_tunable_info *tinfo = tunables_info;
5156 char **argp = ctx->argp;
5157 unsigned int argc = ctx->argc;
5158 unsigned int i, j;
5159
5160 if (argc < 1)
5161 exit_bad_args();
5162
5163 for (i = 0; i < argc; i++) {
5164 int valid = 0;
5165
5166 for (j = 0; j < TUNABLES_INFO_SIZE; j++) {
5167 char *ts = tunable_strings[tinfo[j].t_id];
5168 struct ethtool_tunable *tuna;
5169 int ret;
5170
5171 if (strcmp(argp[i], ts))
5172 continue;
5173 valid = 1;
5174
5175 tuna = calloc(1, sizeof(*tuna) + tinfo[j].size);
5176 if (!tuna) {
5177 perror(ts);
5178 return 1;
5179 }
5180 tuna->cmd = ETHTOOL_GTUNABLE;
5181 tuna->id = tinfo[j].t_id;
5182 tuna->type_id = tinfo[j].t_type_id;
5183 tuna->len = tinfo[j].size;
5184 ret = send_ioctl(ctx, tuna);
5185 if (ret) {
5186 fprintf(stderr, "%s: Cannot get tunable\n", ts);
5187 free(tuna);
5188 return ret;
5189 }
5190 print_tunable(tuna);
5191 free(tuna);
5192 }
5193 if (!valid)
5194 exit_bad_args();
5195 }
5196 return 0;
5197 }
5198
do_get_phy_tunable(struct cmd_context * ctx)5199 static int do_get_phy_tunable(struct cmd_context *ctx)
5200 {
5201 unsigned int argc = ctx->argc;
5202 char **argp = ctx->argp;
5203
5204 if (argc < 1)
5205 exit_bad_args();
5206
5207 if (!strcmp(argp[0], "downshift")) {
5208 struct {
5209 struct ethtool_tunable ds;
5210 u8 count;
5211 } cont;
5212
5213 cont.ds.cmd = ETHTOOL_PHY_GTUNABLE;
5214 cont.ds.id = ETHTOOL_PHY_DOWNSHIFT;
5215 cont.ds.type_id = ETHTOOL_TUNABLE_U8;
5216 cont.ds.len = 1;
5217 if (send_ioctl(ctx, &cont.ds) < 0) {
5218 perror("Cannot Get PHY downshift count");
5219 return 87;
5220 }
5221 if (cont.count)
5222 fprintf(stdout, "Downshift count: %d\n", cont.count);
5223 else
5224 fprintf(stdout, "Downshift disabled\n");
5225 } else if (!strcmp(argp[0], "fast-link-down")) {
5226 struct {
5227 struct ethtool_tunable fld;
5228 u8 msecs;
5229 } cont;
5230
5231 cont.fld.cmd = ETHTOOL_PHY_GTUNABLE;
5232 cont.fld.id = ETHTOOL_PHY_FAST_LINK_DOWN;
5233 cont.fld.type_id = ETHTOOL_TUNABLE_U8;
5234 cont.fld.len = 1;
5235 if (send_ioctl(ctx, &cont.fld) < 0) {
5236 perror("Cannot Get PHY Fast Link Down value");
5237 return 87;
5238 }
5239
5240 if (cont.msecs == ETHTOOL_PHY_FAST_LINK_DOWN_ON)
5241 fprintf(stdout, "Fast Link Down enabled\n");
5242 else if (cont.msecs == ETHTOOL_PHY_FAST_LINK_DOWN_OFF)
5243 fprintf(stdout, "Fast Link Down disabled\n");
5244 else
5245 fprintf(stdout, "Fast Link Down enabled, %d msecs\n",
5246 cont.msecs);
5247 } else if (!strcmp(argp[0], "energy-detect-power-down")) {
5248 struct {
5249 struct ethtool_tunable ds;
5250 u16 msecs;
5251 } cont;
5252
5253 cont.ds.cmd = ETHTOOL_PHY_GTUNABLE;
5254 cont.ds.id = ETHTOOL_PHY_EDPD;
5255 cont.ds.type_id = ETHTOOL_TUNABLE_U16;
5256 cont.ds.len = 2;
5257 if (send_ioctl(ctx, &cont.ds) < 0) {
5258 perror("Cannot Get PHY Energy Detect Power Down value");
5259 return 87;
5260 }
5261
5262 if (cont.msecs == ETHTOOL_PHY_EDPD_DISABLE)
5263 fprintf(stdout, "Energy Detect Power Down: disabled\n");
5264 else if (cont.msecs == ETHTOOL_PHY_EDPD_NO_TX)
5265 fprintf(stdout,
5266 "Energy Detect Power Down: enabled, TX disabled\n");
5267 else
5268 fprintf(stdout,
5269 "Energy Detect Power Down: enabled, TX %u msecs\n",
5270 cont.msecs);
5271 } else {
5272 exit_bad_args();
5273 }
5274
5275 return 0;
5276 }
5277
parse_reset(char * val,__u32 bitset,char * arg,__u32 * data)5278 static __u32 parse_reset(char *val, __u32 bitset, char *arg, __u32 *data)
5279 {
5280 __u32 bitval = 0;
5281 int i;
5282
5283 /* Check for component match */
5284 for (i = 0; val[i] != '\0'; i++)
5285 if (arg[i] != val[i])
5286 return 0;
5287
5288 /* Check if component has -shared specified or not */
5289 if (arg[i] == '\0')
5290 bitval = bitset;
5291 else if (!strcmp(arg+i, "-shared"))
5292 bitval = bitset << ETH_RESET_SHARED_SHIFT;
5293
5294 if (bitval) {
5295 *data |= bitval;
5296 return 1;
5297 }
5298 return 0;
5299 }
5300
do_reset(struct cmd_context * ctx)5301 static int do_reset(struct cmd_context *ctx)
5302 {
5303 struct ethtool_value resetinfo;
5304 __u32 data;
5305 unsigned int argc = ctx->argc;
5306 char **argp = ctx->argp;
5307 unsigned int i;
5308
5309 if (argc == 0)
5310 exit_bad_args();
5311
5312 data = 0;
5313
5314 for (i = 0; i < argc; i++) {
5315 if (!strcmp(argp[i], "flags")) {
5316 __u32 flags;
5317
5318 i++;
5319 if (i >= argc)
5320 exit_bad_args();
5321 flags = strtoul(argp[i], NULL, 0);
5322 if (flags == 0)
5323 exit_bad_args();
5324 else
5325 data |= flags;
5326 } else if (parse_reset("mgmt", ETH_RESET_MGMT,
5327 argp[i], &data)) {
5328 } else if (parse_reset("irq", ETH_RESET_IRQ,
5329 argp[i], &data)) {
5330 } else if (parse_reset("dma", ETH_RESET_DMA,
5331 argp[i], &data)) {
5332 } else if (parse_reset("filter", ETH_RESET_FILTER,
5333 argp[i], &data)) {
5334 } else if (parse_reset("offload", ETH_RESET_OFFLOAD,
5335 argp[i], &data)) {
5336 } else if (parse_reset("mac", ETH_RESET_MAC,
5337 argp[i], &data)) {
5338 } else if (parse_reset("phy", ETH_RESET_PHY,
5339 argp[i], &data)) {
5340 } else if (parse_reset("ram", ETH_RESET_RAM,
5341 argp[i], &data)) {
5342 } else if (parse_reset("ap", ETH_RESET_AP,
5343 argp[i], &data)) {
5344 } else if (!strcmp(argp[i], "dedicated")) {
5345 data |= ETH_RESET_DEDICATED;
5346 } else if (!strcmp(argp[i], "all")) {
5347 data |= ETH_RESET_ALL;
5348 } else {
5349 exit_bad_args();
5350 }
5351 }
5352
5353 resetinfo.cmd = ETHTOOL_RESET;
5354 resetinfo.data = data;
5355 fprintf(stdout, "ETHTOOL_RESET 0x%x\n", resetinfo.data);
5356
5357 if (send_ioctl(ctx, &resetinfo)) {
5358 perror("Cannot issue ETHTOOL_RESET");
5359 return 1;
5360 }
5361
5362 fprintf(stdout, "Components reset: 0x%x\n", data & ~resetinfo.data);
5363 if (resetinfo.data)
5364 fprintf(stdout, "Components not reset: 0x%x\n", resetinfo.data);
5365
5366 return 0;
5367 }
5368
parse_named_bool(struct cmd_context * ctx,const char * name,u8 * on)5369 static int parse_named_bool(struct cmd_context *ctx, const char *name, u8 *on)
5370 {
5371 if (ctx->argc < 2)
5372 return 0;
5373
5374 if (strcmp(*ctx->argp, name))
5375 return 0;
5376
5377 if (!strcmp(*(ctx->argp + 1), "on")) {
5378 *on = 1;
5379 } else if (!strcmp(*(ctx->argp + 1), "off")) {
5380 *on = 0;
5381 } else {
5382 fprintf(stderr, "Invalid boolean\n");
5383 exit_bad_args();
5384 }
5385
5386 ctx->argc -= 2;
5387 ctx->argp += 2;
5388
5389 return 1;
5390 }
5391
parse_named_uint(struct cmd_context * ctx,const char * name,unsigned long long * val,unsigned long long max)5392 static int parse_named_uint(struct cmd_context *ctx,
5393 const char *name,
5394 unsigned long long *val,
5395 unsigned long long max)
5396 {
5397 if (ctx->argc < 2)
5398 return 0;
5399
5400 if (strcmp(*ctx->argp, name))
5401 return 0;
5402
5403 *val = get_uint_range(*(ctx->argp + 1), 0, max);
5404
5405 ctx->argc -= 2;
5406 ctx->argp += 2;
5407
5408 return 1;
5409 }
5410
parse_named_u8(struct cmd_context * ctx,const char * name,u8 * val)5411 static int parse_named_u8(struct cmd_context *ctx, const char *name, u8 *val)
5412 {
5413 unsigned long long val1;
5414 int ret;
5415
5416 ret = parse_named_uint(ctx, name, &val1, 0xff);
5417 if (ret)
5418 *val = val1;
5419
5420 return ret;
5421 }
5422
parse_named_u16(struct cmd_context * ctx,const char * name,u16 * val)5423 static int parse_named_u16(struct cmd_context *ctx, const char *name, u16 *val)
5424 {
5425 unsigned long long val1;
5426 int ret;
5427
5428 ret = parse_named_uint(ctx, name, &val1, 0xffff);
5429 if (ret)
5430 *val = val1;
5431
5432 return ret;
5433 }
5434
do_set_phy_tunable(struct cmd_context * ctx)5435 static int do_set_phy_tunable(struct cmd_context *ctx)
5436 {
5437 int err = 0;
5438 u8 ds_cnt = DOWNSHIFT_DEV_DEFAULT_COUNT;
5439 u8 ds_changed = 0, ds_has_cnt = 0, ds_enable = 0;
5440 u8 fld_changed = 0, fld_enable = 0;
5441 u8 fld_msecs = ETHTOOL_PHY_FAST_LINK_DOWN_ON;
5442 u8 edpd_changed = 0, edpd_enable = 0;
5443 u16 edpd_tx_interval = ETHTOOL_PHY_EDPD_DFLT_TX_MSECS;
5444
5445 /* Parse arguments */
5446 if (parse_named_bool(ctx, "downshift", &ds_enable)) {
5447 ds_changed = 1;
5448 ds_has_cnt = parse_named_u8(ctx, "count", &ds_cnt);
5449 } else if (parse_named_bool(ctx, "fast-link-down", &fld_enable)) {
5450 fld_changed = 1;
5451 if (fld_enable)
5452 parse_named_u8(ctx, "msecs", &fld_msecs);
5453 } else if (parse_named_bool(ctx, "energy-detect-power-down",
5454 &edpd_enable)) {
5455 edpd_changed = 1;
5456 if (edpd_enable)
5457 parse_named_u16(ctx, "msecs", &edpd_tx_interval);
5458 } else {
5459 exit_bad_args();
5460 }
5461
5462 /* Validate parameters */
5463 if (ds_changed) {
5464 if (!ds_enable && ds_has_cnt) {
5465 fprintf(stderr, "'count' may not be set when downshift "
5466 "is off.\n");
5467 exit_bad_args();
5468 }
5469
5470 if (ds_enable && ds_has_cnt && ds_cnt == 0) {
5471 fprintf(stderr, "'count' may not be zero.\n");
5472 exit_bad_args();
5473 }
5474
5475 if (!ds_enable)
5476 ds_cnt = DOWNSHIFT_DEV_DISABLE;
5477 } else if (fld_changed) {
5478 if (!fld_enable)
5479 fld_msecs = ETHTOOL_PHY_FAST_LINK_DOWN_OFF;
5480 else if (fld_msecs == ETHTOOL_PHY_FAST_LINK_DOWN_OFF)
5481 exit_bad_args();
5482 } else if (edpd_changed) {
5483 if (!edpd_enable)
5484 edpd_tx_interval = ETHTOOL_PHY_EDPD_DISABLE;
5485 else if (edpd_tx_interval == 0)
5486 edpd_tx_interval = ETHTOOL_PHY_EDPD_NO_TX;
5487 else if (edpd_tx_interval > ETHTOOL_PHY_EDPD_NO_TX) {
5488 fprintf(stderr, "'msecs' max value is %d.\n",
5489 (ETHTOOL_PHY_EDPD_NO_TX - 1));
5490 exit_bad_args();
5491 }
5492 }
5493
5494 /* Do it */
5495 if (ds_changed) {
5496 struct {
5497 struct ethtool_tunable ds;
5498 u8 count;
5499 } cont;
5500
5501 cont.ds.cmd = ETHTOOL_PHY_STUNABLE;
5502 cont.ds.id = ETHTOOL_PHY_DOWNSHIFT;
5503 cont.ds.type_id = ETHTOOL_TUNABLE_U8;
5504 cont.ds.len = 1;
5505 cont.count = ds_cnt;
5506 err = send_ioctl(ctx, &cont.ds);
5507 if (err < 0) {
5508 perror("Cannot Set PHY downshift count");
5509 err = 87;
5510 }
5511 } else if (fld_changed) {
5512 struct {
5513 struct ethtool_tunable fld;
5514 u8 msecs;
5515 } cont;
5516
5517 cont.fld.cmd = ETHTOOL_PHY_STUNABLE;
5518 cont.fld.id = ETHTOOL_PHY_FAST_LINK_DOWN;
5519 cont.fld.type_id = ETHTOOL_TUNABLE_U8;
5520 cont.fld.len = 1;
5521 cont.msecs = fld_msecs;
5522 err = send_ioctl(ctx, &cont.fld);
5523 if (err < 0) {
5524 perror("Cannot Set PHY Fast Link Down value");
5525 err = 87;
5526 }
5527 } else if (edpd_changed) {
5528 struct {
5529 struct ethtool_tunable fld;
5530 u16 msecs;
5531 } cont;
5532
5533 cont.fld.cmd = ETHTOOL_PHY_STUNABLE;
5534 cont.fld.id = ETHTOOL_PHY_EDPD;
5535 cont.fld.type_id = ETHTOOL_TUNABLE_U16;
5536 cont.fld.len = 2;
5537 cont.msecs = edpd_tx_interval;
5538 err = send_ioctl(ctx, &cont.fld);
5539 if (err < 0) {
5540 perror("Cannot Set PHY Energy Detect Power Down");
5541 err = 87;
5542 }
5543 }
5544
5545 return err;
5546 }
5547
fecmode_str_to_type(const char * str)5548 static int fecmode_str_to_type(const char *str)
5549 {
5550 if (!strcasecmp(str, "auto"))
5551 return ETHTOOL_FEC_AUTO;
5552 if (!strcasecmp(str, "off"))
5553 return ETHTOOL_FEC_OFF;
5554 if (!strcasecmp(str, "rs"))
5555 return ETHTOOL_FEC_RS;
5556 if (!strcasecmp(str, "baser"))
5557 return ETHTOOL_FEC_BASER;
5558 if (!strcasecmp(str, "llrs"))
5559 return ETHTOOL_FEC_LLRS;
5560 return 0;
5561 }
5562
do_gfec(struct cmd_context * ctx)5563 static int do_gfec(struct cmd_context *ctx)
5564 {
5565 struct ethtool_fecparam feccmd = { 0 };
5566 int rv;
5567
5568 if (ctx->argc != 0)
5569 exit_bad_args();
5570
5571 feccmd.cmd = ETHTOOL_GFECPARAM;
5572 rv = send_ioctl(ctx, &feccmd);
5573 if (rv != 0) {
5574 perror("Cannot get FEC settings");
5575 return rv;
5576 }
5577
5578 fprintf(stdout, "FEC parameters for %s:\n", ctx->devname);
5579 fprintf(stdout, "Supported/Configured FEC encodings:");
5580 dump_fec(feccmd.fec);
5581 fprintf(stdout, "\n");
5582
5583 fprintf(stdout, "Active FEC encoding:");
5584 dump_fec(feccmd.active_fec);
5585 fprintf(stdout, "\n");
5586
5587 return 0;
5588 }
5589
do_sfec(struct cmd_context * ctx)5590 static int do_sfec(struct cmd_context *ctx)
5591 {
5592 enum { ARG_NONE, ARG_ENCODING } state = ARG_NONE;
5593 struct ethtool_fecparam feccmd;
5594 int fecmode = 0, newmode;
5595 unsigned int i;
5596 int rv;
5597
5598 for (i = 0; i < ctx->argc; i++) {
5599 if (!strcmp(ctx->argp[i], "encoding")) {
5600 state = ARG_ENCODING;
5601 continue;
5602 }
5603 if (state == ARG_ENCODING) {
5604 newmode = fecmode_str_to_type(ctx->argp[i]);
5605 if (!newmode)
5606 exit_bad_args();
5607 fecmode |= newmode;
5608 continue;
5609 }
5610 exit_bad_args();
5611 }
5612
5613 if (!fecmode)
5614 exit_bad_args();
5615
5616 feccmd.cmd = ETHTOOL_SFECPARAM;
5617 feccmd.fec = fecmode;
5618 rv = send_ioctl(ctx, &feccmd);
5619 if (rv != 0) {
5620 perror("Cannot set FEC settings");
5621 return rv;
5622 }
5623
5624 return 0;
5625 }
5626
5627 static int do_perqueue(struct cmd_context *ctx);
5628
5629 #ifndef TEST_ETHTOOL
send_ioctl(struct cmd_context * ctx,void * cmd)5630 int send_ioctl(struct cmd_context *ctx, void *cmd)
5631 {
5632 ctx->ifr.ifr_data = cmd;
5633 return ioctl(ctx->fd, SIOCETHTOOL, &ctx->ifr);
5634 }
5635 #endif
5636
5637 static int show_usage(struct cmd_context *ctx);
5638
5639 struct option {
5640 const char *opts;
5641 bool no_dev;
5642 bool json;
5643 int (*func)(struct cmd_context *);
5644 nl_chk_t nlchk;
5645 nl_func_t nlfunc;
5646 const char *help;
5647 const char *xhelp;
5648 };
5649
5650 static const struct option args[] = {
5651 {
5652 /* "default" entry when no switch is used */
5653 .opts = "",
5654 .func = do_gset,
5655 .nlfunc = nl_gset,
5656 .help = "Display standard information about device",
5657 },
5658 {
5659 .opts = "-s|--change",
5660 .func = do_sset,
5661 .nlfunc = nl_sset,
5662 .help = "Change generic options",
5663 .xhelp = " [ speed %d ]\n"
5664 " [ lanes %d ]\n"
5665 " [ duplex half|full ]\n"
5666 " [ port tp|aui|bnc|mii|fibre|da ]\n"
5667 " [ mdix auto|on|off ]\n"
5668 " [ autoneg on|off ]\n"
5669 " [ advertise %x[/%x] | mode on|off ... [--] ]\n"
5670 " [ phyad %d ]\n"
5671 " [ xcvr internal|external ]\n"
5672 " [ wol %d[/%d] | p|u|m|b|a|g|s|f|d... ]\n"
5673 " [ sopass %x:%x:%x:%x:%x:%x ]\n"
5674 " [ msglvl %d[/%d] | type on|off ... [--] ]\n"
5675 " [ master-slave preferred-master|preferred-slave|forced-master|forced-slave ]\n"
5676 },
5677 {
5678 .opts = "-a|--show-pause",
5679 .json = true,
5680 .func = do_gpause,
5681 .nlfunc = nl_gpause,
5682 .help = "Show pause options",
5683 .xhelp = " [ --src aggregate | emac | pmac ]\n"
5684 },
5685 {
5686 .opts = "-A|--pause",
5687 .func = do_spause,
5688 .nlfunc = nl_spause,
5689 .help = "Set pause options",
5690 .xhelp = " [ autoneg on|off ]\n"
5691 " [ rx on|off ]\n"
5692 " [ tx on|off ]\n"
5693 },
5694 {
5695 .opts = "-c|--show-coalesce",
5696 .json = true,
5697 .func = do_gcoalesce,
5698 .nlfunc = nl_gcoalesce,
5699 .help = "Show coalesce options"
5700 },
5701 {
5702 .opts = "-C|--coalesce",
5703 .func = do_scoalesce,
5704 .nlfunc = nl_scoalesce,
5705 .help = "Set coalesce options",
5706 .xhelp = " [adaptive-rx on|off]\n"
5707 " [adaptive-tx on|off]\n"
5708 " [rx-usecs N]\n"
5709 " [rx-frames N]\n"
5710 " [rx-usecs-irq N]\n"
5711 " [rx-frames-irq N]\n"
5712 " [tx-usecs N]\n"
5713 " [tx-frames N]\n"
5714 " [tx-usecs-irq N]\n"
5715 " [tx-frames-irq N]\n"
5716 " [stats-block-usecs N]\n"
5717 " [pkt-rate-low N]\n"
5718 " [rx-usecs-low N]\n"
5719 " [rx-frames-low N]\n"
5720 " [tx-usecs-low N]\n"
5721 " [tx-frames-low N]\n"
5722 " [pkt-rate-high N]\n"
5723 " [rx-usecs-high N]\n"
5724 " [rx-frames-high N]\n"
5725 " [tx-usecs-high N]\n"
5726 " [tx-frames-high N]\n"
5727 " [sample-interval N]\n"
5728 " [cqe-mode-rx on|off]\n"
5729 " [cqe-mode-tx on|off]\n"
5730 " [tx-aggr-max-bytes N]\n"
5731 " [tx-aggr-max-frames N]\n"
5732 " [tx-aggr-time-usecs N]\n"
5733 },
5734 {
5735 .opts = "-g|--show-ring",
5736 .json = true,
5737 .func = do_gring,
5738 .nlfunc = nl_gring,
5739 .help = "Query RX/TX ring parameters"
5740 },
5741 {
5742 .opts = "-G|--set-ring",
5743 .func = do_sring,
5744 .nlfunc = nl_sring,
5745 .help = "Set RX/TX ring parameters",
5746 .xhelp = " [ rx N ]\n"
5747 " [ rx-mini N ]\n"
5748 " [ rx-jumbo N ]\n"
5749 " [ tx N ]\n"
5750 " [ rx-buf-len N ]\n"
5751 " [ cqe-size N ]\n"
5752 " [ tx-push on|off ]\n"
5753 " [ rx-push on|off ]\n"
5754 " [ tx-push-buf-len N]\n"
5755 },
5756 {
5757 .opts = "-k|--show-features|--show-offload",
5758 .json = true,
5759 .func = do_gfeatures,
5760 .nlfunc = nl_gfeatures,
5761 .help = "Get state of protocol offload and other features"
5762 },
5763 {
5764 .opts = "-K|--features|--offload",
5765 .func = do_sfeatures,
5766 .nlfunc = nl_sfeatures,
5767 .help = "Set protocol offload and other features",
5768 .xhelp = " FEATURE on|off ...\n"
5769 },
5770 {
5771 .opts = "-i|--driver",
5772 .func = do_gdrv,
5773 .help = "Show driver information"
5774 },
5775 {
5776 .opts = "-d|--register-dump",
5777 .func = do_gregs,
5778 .help = "Do a register dump",
5779 .xhelp = " [ raw on|off ]\n"
5780 " [ file FILENAME ]\n"
5781 },
5782 {
5783 .opts = "-e|--eeprom-dump",
5784 .func = do_geeprom,
5785 .help = "Do a EEPROM dump",
5786 .xhelp = " [ raw on|off ]\n"
5787 " [ offset N ]\n"
5788 " [ length N ]\n"
5789 },
5790 {
5791 .opts = "-E|--change-eeprom",
5792 .func = do_seeprom,
5793 .help = "Change bytes in device EEPROM",
5794 .xhelp = " [ magic N ]\n"
5795 " [ offset N ]\n"
5796 " [ length N ]\n"
5797 " [ value N ]\n"
5798 },
5799 {
5800 .opts = "-r|--negotiate",
5801 .func = do_nway_rst,
5802 .help = "Restart N-WAY negotiation"
5803 },
5804 {
5805 .opts = "-p|--identify",
5806 .func = do_phys_id,
5807 .help = "Show visible port identification (e.g. blinking)",
5808 .xhelp = " [ TIME-IN-SECONDS ]\n"
5809 },
5810 {
5811 .opts = "-t|--test",
5812 .func = do_test,
5813 .help = "Execute adapter self test",
5814 .xhelp = " [ online | offline | external_lb ]\n"
5815 },
5816 {
5817 .opts = "-S|--statistics",
5818 .json = true,
5819 .func = do_gnicstats,
5820 .nlchk = nl_gstats_chk,
5821 .nlfunc = nl_gstats,
5822 .help = "Show adapter statistics",
5823 .xhelp = " [ --all-groups | --groups [eth-phy] [eth-mac] [eth-ctrl] [rmon] ]\n"
5824 " [ --src aggregate | emac | pmac ]\n"
5825 },
5826 {
5827 .opts = "--phy-statistics",
5828 .func = do_gphystats,
5829 .help = "Show phy statistics"
5830 },
5831 {
5832 .opts = "-n|-u|--show-nfc|--show-ntuple",
5833 .func = do_grxclass,
5834 .help = "Show Rx network flow classification options or rules",
5835 .xhelp = " [ rx-flow-hash tcp4|udp4|ah4|esp4|sctp4|"
5836 "tcp6|udp6|ah6|esp6|sctp6 [context %d] |\n"
5837 " rule %d ]\n"
5838 },
5839 {
5840 .opts = "-N|-U|--config-nfc|--config-ntuple",
5841 .func = do_srxclass,
5842 .help = "Configure Rx network flow classification options or rules",
5843 .xhelp = " rx-flow-hash tcp4|udp4|ah4|esp4|sctp4|"
5844 "tcp6|udp6|ah6|esp6|sctp6 m|v|t|s|d|f|n|r... [context %d] |\n"
5845 " flow-type ether|ip4|tcp4|udp4|sctp4|ah4|esp4|"
5846 "ip6|tcp6|udp6|ah6|esp6|sctp6\n"
5847 " [ src %x:%x:%x:%x:%x:%x [m %x:%x:%x:%x:%x:%x] ]\n"
5848 " [ dst %x:%x:%x:%x:%x:%x [m %x:%x:%x:%x:%x:%x] ]\n"
5849 " [ proto %d [m %x] ]\n"
5850 " [ src-ip IP-ADDRESS [m IP-ADDRESS] ]\n"
5851 " [ dst-ip IP-ADDRESS [m IP-ADDRESS] ]\n"
5852 " [ tos %d [m %x] ]\n"
5853 " [ tclass %d [m %x] ]\n"
5854 " [ l4proto %d [m %x] ]\n"
5855 " [ src-port %d [m %x] ]\n"
5856 " [ dst-port %d [m %x] ]\n"
5857 " [ spi %d [m %x] ]\n"
5858 " [ vlan-etype %x [m %x] ]\n"
5859 " [ vlan %x [m %x] ]\n"
5860 " [ user-def %x [m %x] ]\n"
5861 " [ dst-mac %x:%x:%x:%x:%x:%x [m %x:%x:%x:%x:%x:%x] ]\n"
5862 " [ action %d ] | [ vf %d queue %d ]\n"
5863 " [ context %d ]\n"
5864 " [ loc %d ] |\n"
5865 " delete %d\n"
5866 },
5867 {
5868 .opts = "-T|--show-time-stamping",
5869 .func = do_tsinfo,
5870 .nlfunc = nl_tsinfo,
5871 .help = "Show time stamping capabilities"
5872 },
5873 {
5874 .opts = "-x|--show-rxfh-indir|--show-rxfh",
5875 .json = true,
5876 .func = do_grxfh,
5877 .nlfunc = nl_grss,
5878 .help = "Show Rx flow hash indirection table and/or RSS hash key",
5879 .xhelp = " [ context %d ]\n"
5880 },
5881 {
5882 .opts = "-X|--set-rxfh-indir|--rxfh",
5883 .func = do_srxfh,
5884 .help = "Set Rx flow hash indirection table and/or RSS hash key",
5885 .xhelp = " [ context %d|new ]\n"
5886 " [ equal N | weight W0 W1 ... | default ]\n"
5887 " [ hkey %x:%x:%x:%x:%x:.... ]\n"
5888 " [ hfunc FUNC ]\n"
5889 " [ delete ]\n"
5890 },
5891 {
5892 .opts = "-f|--flash",
5893 .func = do_flash,
5894 .help = "Flash firmware image from the specified file to a region on the device",
5895 .xhelp = " FILENAME [ REGION-NUMBER-TO-FLASH ]\n"
5896 },
5897 {
5898 .opts = "-P|--show-permaddr",
5899 .func = do_permaddr,
5900 .nlfunc = nl_permaddr,
5901 .help = "Show permanent hardware address"
5902 },
5903 {
5904 .opts = "-w|--get-dump",
5905 .func = do_getfwdump,
5906 .help = "Get dump flag, data",
5907 .xhelp = " [ data FILENAME ]\n"
5908 },
5909 {
5910 .opts = "-W|--set-dump",
5911 .func = do_setfwdump,
5912 .help = "Set dump flag of the device",
5913 .xhelp = " N\n"
5914 },
5915 {
5916 .opts = "-l|--show-channels",
5917 .func = do_gchannels,
5918 .nlfunc = nl_gchannels,
5919 .help = "Query Channels"
5920 },
5921 {
5922 .opts = "-L|--set-channels",
5923 .func = do_schannels,
5924 .nlfunc = nl_schannels,
5925 .help = "Set Channels",
5926 .xhelp = " [ rx N ]\n"
5927 " [ tx N ]\n"
5928 " [ other N ]\n"
5929 " [ combined N ]\n"
5930 },
5931 {
5932 .opts = "--show-priv-flags",
5933 .func = do_gprivflags,
5934 .nlfunc = nl_gprivflags,
5935 .help = "Query private flags"
5936 },
5937 {
5938 .opts = "--set-priv-flags",
5939 .func = do_sprivflags,
5940 .nlfunc = nl_sprivflags,
5941 .help = "Set private flags",
5942 .xhelp = " FLAG on|off ...\n"
5943 },
5944 {
5945 .opts = "-m|--dump-module-eeprom|--module-info",
5946 .func = do_getmodule,
5947 .nlfunc = nl_getmodule,
5948 .help = "Query/Decode Module EEPROM information and optical diagnostics if available",
5949 .xhelp = " [ raw on|off ]\n"
5950 " [ hex on|off ]\n"
5951 " [ offset N ]\n"
5952 " [ length N ]\n"
5953 " [ page N ]\n"
5954 " [ bank N ]\n"
5955 " [ i2c N ]\n"
5956 },
5957 {
5958 .opts = "--show-eee",
5959 .func = do_geee,
5960 .nlfunc = nl_geee,
5961 .help = "Show EEE settings",
5962 },
5963 {
5964 .opts = "--set-eee",
5965 .func = do_seee,
5966 .nlfunc = nl_seee,
5967 .help = "Set EEE settings",
5968 .xhelp = " [ eee on|off ]\n"
5969 " [ advertise %x ]\n"
5970 " [ tx-lpi on|off ]\n"
5971 " [ tx-timer %d ]\n"
5972 },
5973 {
5974 .opts = "--set-phy-tunable",
5975 .func = do_set_phy_tunable,
5976 .help = "Set PHY tunable",
5977 .xhelp = " [ downshift on|off [count N] ]\n"
5978 " [ fast-link-down on|off [msecs N] ]\n"
5979 " [ energy-detect-power-down on|off [msecs N] ]\n"
5980 },
5981 {
5982 .opts = "--get-phy-tunable",
5983 .func = do_get_phy_tunable,
5984 .help = "Get PHY tunable",
5985 .xhelp = " [ downshift ]\n"
5986 " [ fast-link-down ]\n"
5987 " [ energy-detect-power-down ]\n"
5988 },
5989 {
5990 .opts = "--get-tunable",
5991 .func = do_gtunable,
5992 .help = "Get tunable",
5993 .xhelp = " [ rx-copybreak ]\n"
5994 " [ tx-copybreak ]\n"
5995 " [ tx-buf-size ]\n"
5996 " [ pfc-prevention-tout ]\n"
5997 },
5998 {
5999 .opts = "--set-tunable",
6000 .func = do_stunable,
6001 .help = "Set tunable",
6002 .xhelp = " [ rx-copybreak N ]\n"
6003 " [ tx-copybreak N ]\n"
6004 " [ tx-buf-size N ]\n"
6005 " [ pfc-prevention-tout N ]\n"
6006 },
6007 {
6008 .opts = "--reset",
6009 .func = do_reset,
6010 .help = "Reset components",
6011 .xhelp = " [ flags %x ]\n"
6012 " [ mgmt ]\n"
6013 " [ mgmt-shared ]\n"
6014 " [ irq ]\n"
6015 " [ irq-shared ]\n"
6016 " [ dma ]\n"
6017 " [ dma-shared ]\n"
6018 " [ filter ]\n"
6019 " [ filter-shared ]\n"
6020 " [ offload ]\n"
6021 " [ offload-shared ]\n"
6022 " [ mac ]\n"
6023 " [ mac-shared ]\n"
6024 " [ phy ]\n"
6025 " [ phy-shared ]\n"
6026 " [ ram ]\n"
6027 " [ ram-shared ]\n"
6028 " [ ap ]\n"
6029 " [ ap-shared ]\n"
6030 " [ dedicated ]\n"
6031 " [ all ]\n"
6032 },
6033 {
6034 .opts = "--show-fec",
6035 .json = true,
6036 .func = do_gfec,
6037 .nlfunc = nl_gfec,
6038 .help = "Show FEC settings",
6039 },
6040 {
6041 .opts = "--set-fec",
6042 .func = do_sfec,
6043 .nlfunc = nl_sfec,
6044 .help = "Set FEC settings",
6045 .xhelp = " [ encoding auto|off|rs|baser|llrs [...] ]\n"
6046 },
6047 {
6048 .opts = "-Q|--per-queue",
6049 .func = do_perqueue,
6050 .help = "Apply per-queue command. ",
6051 .xhelp = "The supported sub commands include --show-coalesce, --coalesce"
6052 " [queue_mask %x] SUB_COMMAND\n",
6053 },
6054 {
6055 .opts = "--cable-test",
6056 .json = true,
6057 .nlfunc = nl_cable_test,
6058 .help = "Perform a cable test",
6059 },
6060 {
6061 .opts = "--cable-test-tdr",
6062 .json = true,
6063 .nlfunc = nl_cable_test_tdr,
6064 .help = "Print cable test time domain reflectrometery data",
6065 .xhelp = " [ first N ]\n"
6066 " [ last N ]\n"
6067 " [ step N ]\n"
6068 " [ pair N ]\n"
6069 },
6070 {
6071 .opts = "--show-tunnels",
6072 .nlfunc = nl_gtunnels,
6073 .help = "Show NIC tunnel offload information",
6074 },
6075 {
6076 .opts = "--show-module",
6077 .json = true,
6078 .nlfunc = nl_gmodule,
6079 .help = "Show transceiver module settings",
6080 },
6081 {
6082 .opts = "--set-module",
6083 .nlfunc = nl_smodule,
6084 .help = "Set transceiver module settings",
6085 .xhelp = " [ power-mode-policy high|auto ]\n"
6086 },
6087 {
6088 .opts = "--get-plca-cfg",
6089 .nlfunc = nl_plca_get_cfg,
6090 .help = "Get PLCA configuration",
6091 },
6092 {
6093 .opts = "--set-plca-cfg",
6094 .nlfunc = nl_plca_set_cfg,
6095 .help = "Set PLCA configuration",
6096 .xhelp = " [ enable on|off ]\n"
6097 " [ node-id N ]\n"
6098 " [ node-cnt N ]\n"
6099 " [ to-tmr N ]\n"
6100 " [ burst-cnt N ]\n"
6101 " [ burst-tmr N ]\n"
6102 },
6103 {
6104 .opts = "--get-plca-status",
6105 .nlfunc = nl_plca_get_status,
6106 .help = "Get PLCA status information",
6107 },
6108 {
6109 .opts = "--show-mm",
6110 .json = true,
6111 .nlfunc = nl_get_mm,
6112 .help = "Show MAC merge layer state",
6113 },
6114 {
6115 .opts = "--set-mm",
6116 .nlfunc = nl_set_mm,
6117 .help = "Set MAC merge layer parameters",
6118 " [ verify-enabled on|off ]\n"
6119 " [ verify-time N ]\n"
6120 " [ tx-enabled on|off ]\n"
6121 " [ pmac-enabled on|off ]\n"
6122 " [ tx-min-frag-size 60-252 ]\n"
6123 },
6124 {
6125 .opts = "--show-pse",
6126 .json = true,
6127 .nlfunc = nl_gpse,
6128 .help = "Show settings for Power Sourcing Equipment",
6129 },
6130 {
6131 .opts = "--set-pse",
6132 .nlfunc = nl_spse,
6133 .help = "Set Power Sourcing Equipment settings",
6134 .xhelp = " [ podl-pse-admin-control enable|disable ]\n"
6135 },
6136 {
6137 .opts = "-h|--help",
6138 .no_dev = true,
6139 .func = show_usage,
6140 .help = "Show this help"
6141 },
6142 {
6143 .opts = "--version",
6144 .no_dev = true,
6145 .func = do_version,
6146 .help = "Show version number"
6147 },
6148 {}
6149 };
6150
show_usage(struct cmd_context * ctx __maybe_unused)6151 static int show_usage(struct cmd_context *ctx __maybe_unused)
6152 {
6153 int i;
6154
6155 /* ethtool -h */
6156 fprintf(stdout, PACKAGE " version " VERSION "\n");
6157 fprintf(stdout, "Usage:\n");
6158 for (i = 0; args[i].opts; i++) {
6159 fputs(" ethtool [ FLAGS ] ", stdout);
6160 fprintf(stdout, "%s %s\t%s\n",
6161 args[i].opts,
6162 args[i].no_dev ? "\t" : "DEVNAME",
6163 args[i].help);
6164 if (args[i].xhelp)
6165 fputs(args[i].xhelp, stdout);
6166 }
6167 nl_monitor_usage();
6168 fprintf(stdout, "\n");
6169 fprintf(stdout, "FLAGS:\n");
6170 fprintf(stdout, " --debug MASK turn on debugging messages\n");
6171 fprintf(stdout, " --json enable JSON output format (not supported by all commands)\n");
6172 fprintf(stdout, " -I|--include-statistics request device statistics related to the command (not supported by all commands)\n");
6173
6174 return 0;
6175 }
6176
find_option(char * arg)6177 static int find_option(char *arg)
6178 {
6179 const char *opt;
6180 size_t len;
6181 int k;
6182
6183 for (k = 1; args[k].opts; k++) {
6184 opt = args[k].opts;
6185 for (;;) {
6186 len = strcspn(opt, "|");
6187 if (strncmp(arg, opt, len) == 0 && arg[len] == 0)
6188 return k;
6189
6190 if (opt[len] == 0)
6191 break;
6192 opt += len + 1;
6193 }
6194 }
6195
6196 return -1;
6197 }
6198
6199 #define MAX(x, y) (x > y ? x : y)
6200
find_max_num_queues(struct cmd_context * ctx)6201 static int find_max_num_queues(struct cmd_context *ctx)
6202 {
6203 struct ethtool_channels echannels;
6204
6205 echannels.cmd = ETHTOOL_GCHANNELS;
6206 if (send_ioctl(ctx, &echannels))
6207 return -1;
6208
6209 return MAX(echannels.rx_count, echannels.tx_count) +
6210 echannels.combined_count;
6211 }
6212
6213 static struct ethtool_per_queue_op *
get_per_queue_coalesce(struct cmd_context * ctx,__u32 * queue_mask,int n_queues)6214 get_per_queue_coalesce(struct cmd_context *ctx, __u32 *queue_mask, int n_queues)
6215 {
6216 struct ethtool_per_queue_op *per_queue_opt;
6217
6218 per_queue_opt = malloc(sizeof(*per_queue_opt) + n_queues *
6219 sizeof(struct ethtool_coalesce));
6220 if (!per_queue_opt)
6221 return NULL;
6222
6223 memcpy(per_queue_opt->queue_mask, queue_mask,
6224 __KERNEL_DIV_ROUND_UP(MAX_NUM_QUEUE, 32) * sizeof(__u32));
6225 per_queue_opt->cmd = ETHTOOL_PERQUEUE;
6226 per_queue_opt->sub_command = ETHTOOL_GCOALESCE;
6227 if (send_ioctl(ctx, per_queue_opt)) {
6228 free(per_queue_opt);
6229 perror("Cannot get device per queue parameters");
6230 return NULL;
6231 }
6232
6233 return per_queue_opt;
6234 }
6235
set_per_queue_coalesce(struct cmd_context * ctx,struct ethtool_per_queue_op * per_queue_opt,int n_queues)6236 static void set_per_queue_coalesce(struct cmd_context *ctx,
6237 struct ethtool_per_queue_op *per_queue_opt,
6238 int n_queues)
6239 {
6240 struct ethtool_coalesce ecoal;
6241 DECLARE_COALESCE_OPTION_VARS();
6242 struct cmdline_info cmdline_coalesce[] = COALESCE_CMDLINE_INFO(ecoal);
6243 __u32 *queue_mask = per_queue_opt->queue_mask;
6244 struct ethtool_coalesce *ecoal_q;
6245 int gcoalesce_changed = 0;
6246 int i, idx = 0;
6247
6248 parse_generic_cmdline(ctx, &gcoalesce_changed,
6249 cmdline_coalesce, ARRAY_SIZE(cmdline_coalesce));
6250
6251 ecoal_q = (struct ethtool_coalesce *)(per_queue_opt + 1);
6252 for (i = 0; i < __KERNEL_DIV_ROUND_UP(MAX_NUM_QUEUE, 32); i++) {
6253 int queue = i * 32;
6254 __u32 mask = queue_mask[i];
6255
6256 while (mask > 0) {
6257 if (mask & 0x1) {
6258 int changed = 0;
6259
6260 memcpy(&ecoal, ecoal_q + idx,
6261 sizeof(struct ethtool_coalesce));
6262 do_generic_set(cmdline_coalesce,
6263 ARRAY_SIZE(cmdline_coalesce),
6264 &changed);
6265 if (!changed)
6266 fprintf(stderr,
6267 "Queue %d, no coalesce parameters changed\n",
6268 queue);
6269 memcpy(ecoal_q + idx, &ecoal,
6270 sizeof(struct ethtool_coalesce));
6271 idx++;
6272 }
6273 mask = mask >> 1;
6274 queue++;
6275 }
6276 if (idx == n_queues)
6277 break;
6278 }
6279
6280 per_queue_opt->cmd = ETHTOOL_PERQUEUE;
6281 per_queue_opt->sub_command = ETHTOOL_SCOALESCE;
6282
6283 if (send_ioctl(ctx, per_queue_opt))
6284 perror("Cannot set device per queue parameters");
6285 }
6286
do_perqueue(struct cmd_context * ctx)6287 static int do_perqueue(struct cmd_context *ctx)
6288 {
6289 struct ethtool_per_queue_op *per_queue_opt;
6290 __u32 queue_mask[__KERNEL_DIV_ROUND_UP(MAX_NUM_QUEUE, 32)] = {0};
6291 int i, n_queues = 0;
6292
6293 if (ctx->argc == 0)
6294 exit_bad_args();
6295
6296 /*
6297 * The sub commands will be applied to
6298 * all queues if no queue_mask set
6299 */
6300 if (strncmp(*ctx->argp, "queue_mask", 11)) {
6301 n_queues = find_max_num_queues(ctx);
6302 if (n_queues < 0) {
6303 perror("Cannot get number of queues");
6304 return -EFAULT;
6305 } else if (n_queues > MAX_NUM_QUEUE) {
6306 n_queues = MAX_NUM_QUEUE;
6307 }
6308 for (i = 0; i < n_queues / 32; i++)
6309 queue_mask[i] = ~0;
6310 if (n_queues % 32)
6311 queue_mask[i] = (1 << (n_queues - i * 32)) - 1;
6312 fprintf(stdout,
6313 "The sub commands will be applied to all %d queues\n",
6314 n_queues);
6315 } else {
6316 if (ctx->argc <= 2)
6317 exit_bad_args();
6318 ctx->argc--;
6319 ctx->argp++;
6320 if (parse_hex_u32_bitmap(*ctx->argp, MAX_NUM_QUEUE,
6321 queue_mask)) {
6322 fprintf(stdout, "Invalid queue mask\n");
6323 return -1;
6324 }
6325 for (i = 0; i < __KERNEL_DIV_ROUND_UP(MAX_NUM_QUEUE, 32); i++) {
6326 __u32 mask = queue_mask[i];
6327
6328 while (mask > 0) {
6329 if (mask & 0x1)
6330 n_queues++;
6331 mask = mask >> 1;
6332 }
6333 }
6334 ctx->argc--;
6335 ctx->argp++;
6336 }
6337
6338 i = find_option(ctx->argp[0]);
6339 if (i < 0)
6340 exit_bad_args();
6341
6342 if (strstr(args[i].opts, "--show-coalesce") != NULL) {
6343 per_queue_opt = get_per_queue_coalesce(ctx, queue_mask,
6344 n_queues);
6345 if (per_queue_opt == NULL) {
6346 perror("Cannot get device per queue parameters");
6347 return -EFAULT;
6348 }
6349 dump_per_queue_coalesce(per_queue_opt, queue_mask, n_queues);
6350 free(per_queue_opt);
6351 } else if (strstr(args[i].opts, "--coalesce") != NULL) {
6352 ctx->argc--;
6353 ctx->argp++;
6354 per_queue_opt = get_per_queue_coalesce(ctx, queue_mask,
6355 n_queues);
6356 if (per_queue_opt == NULL) {
6357 perror("Cannot get device per queue parameters");
6358 return -EFAULT;
6359 }
6360 set_per_queue_coalesce(ctx, per_queue_opt, n_queues);
6361 free(per_queue_opt);
6362 } else {
6363 perror("The subcommand is not supported yet");
6364 return -EOPNOTSUPP;
6365 }
6366
6367 return 0;
6368 }
6369
ioctl_init(struct cmd_context * ctx,bool no_dev)6370 static int ioctl_init(struct cmd_context *ctx, bool no_dev)
6371 {
6372 if (no_dev) {
6373 ctx->fd = -1;
6374 return 0;
6375 }
6376 if (strlen(ctx->devname) >= IFNAMSIZ) {
6377 fprintf(stderr, "Device name longer than %u characters\n",
6378 IFNAMSIZ - 1);
6379 exit_bad_args();
6380 }
6381
6382 /* Setup our control structures. */
6383 memset(&ctx->ifr, 0, sizeof(ctx->ifr));
6384 strcpy(ctx->ifr.ifr_name, ctx->devname);
6385
6386 /* Open control socket. */
6387 ctx->fd = socket(AF_INET, SOCK_DGRAM, 0);
6388 if (ctx->fd < 0)
6389 ctx->fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);
6390 if (ctx->fd < 0) {
6391 perror("Cannot get control socket");
6392 return 70;
6393 }
6394
6395 return 0;
6396 }
6397
main(int argc,char ** argp)6398 int main(int argc, char **argp)
6399 {
6400 struct cmd_context ctx = {};
6401 int ret;
6402 int k;
6403
6404 init_global_link_mode_masks();
6405
6406 if (argc < 2)
6407 exit_bad_args();
6408
6409 /* Skip command name */
6410 argp++;
6411 argc--;
6412
6413 while (true) {
6414 if (*argp && !strcmp(*argp, "--debug")) {
6415 char *eptr;
6416
6417 if (argc < 2)
6418 exit_bad_args();
6419 ctx.debug = strtoul(argp[1], &eptr, 0);
6420 if (!argp[1][0] || *eptr)
6421 exit_bad_args();
6422
6423 argp += 2;
6424 argc -= 2;
6425 continue;
6426 }
6427 if (*argp && !strcmp(*argp, "--json")) {
6428 ctx.json = true;
6429 argp += 1;
6430 argc -= 1;
6431 continue;
6432 }
6433 if (*argp && (!strcmp(*argp, "--include-statistics") ||
6434 !strcmp(*argp, "-I"))) {
6435 ctx.show_stats = true;
6436 argp += 1;
6437 argc -= 1;
6438 continue;
6439 }
6440 break;
6441 }
6442 if (*argp && !strcmp(*argp, "--monitor")) {
6443 ctx.argp = ++argp;
6444 ctx.argc = --argc;
6445 ret = nl_monitor(&ctx);
6446 return ret ? 1 : 0;
6447 }
6448
6449 /* First argument must be either a valid option or a device
6450 * name to get settings for (which we don't expect to begin
6451 * with '-').
6452 */
6453 if (!*argp)
6454 exit_bad_args();
6455
6456 k = find_option(*argp);
6457 if (k > 0) {
6458 argp++;
6459 argc--;
6460 } else {
6461 if ((*argp)[0] == '-')
6462 exit_bad_args();
6463 k = 0;
6464 }
6465
6466 if (!args[k].no_dev) {
6467 ctx.devname = *argp++;
6468 argc--;
6469
6470 if (!ctx.devname)
6471 exit_bad_args();
6472 }
6473 if (ctx.json && !args[k].json)
6474 exit_bad_args();
6475 ctx.argc = argc;
6476 ctx.argp = argp;
6477 netlink_run_handler(&ctx, args[k].nlchk, args[k].nlfunc, !args[k].func);
6478
6479 if (ctx.json) /* no IOCTL command supports JSON output */
6480 exit_bad_args();
6481
6482 ret = ioctl_init(&ctx, args[k].no_dev);
6483 if (ret)
6484 return ret;
6485
6486 return args[k].func(&ctx);
6487 }
6488