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