xref: /freebsd/sys/dev/mlx5/mlx5_en/mlx5_en_ethtool.c (revision fafb1ee7bdc5d8a7d07cd03b2fb0bbb76f7a9d7c)
1 /*-
2  * Copyright (c) 2015 Mellanox Technologies. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS `AS IS' AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23  * SUCH DAMAGE.
24  *
25  * $FreeBSD$
26  */
27 
28 #include "en.h"
29 #include <net/sff8472.h>
30 
31 void
32 mlx5e_create_stats(struct sysctl_ctx_list *ctx,
33     struct sysctl_oid_list *parent, const char *buffer,
34     const char **desc, unsigned num, u64 * arg)
35 {
36 	struct sysctl_oid *node;
37 	unsigned x;
38 
39 	sysctl_ctx_init(ctx);
40 
41 	node = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO,
42 	    buffer, CTLFLAG_RD, NULL, "Statistics");
43 	if (node == NULL)
44 		return;
45 	for (x = 0; x != num; x++) {
46 		SYSCTL_ADD_UQUAD(ctx, SYSCTL_CHILDREN(node), OID_AUTO,
47 		    desc[2 * x], CTLFLAG_RD, arg + x, desc[2 * x + 1]);
48 	}
49 }
50 
51 static int
52 mlx5e_ethtool_handler(SYSCTL_HANDLER_ARGS)
53 {
54 	struct mlx5e_priv *priv = arg1;
55 	uint64_t value;
56 	int was_opened;
57 	int error;
58 
59 	PRIV_LOCK(priv);
60 	value = priv->params_ethtool.arg[arg2];
61 	if (req != NULL) {
62 		error = sysctl_handle_64(oidp, &value, 0, req);
63 		if (error || req->newptr == NULL ||
64 		    value == priv->params_ethtool.arg[arg2])
65 			goto done;
66 
67 		/* assign new value */
68 		priv->params_ethtool.arg[arg2] = value;
69 	} else {
70 		error = 0;
71 	}
72 	/* check if device is gone */
73 	if (priv->gone) {
74 		error = ENXIO;
75 		goto done;
76 	}
77 	/* import RX coal time */
78 	if (priv->params_ethtool.rx_coalesce_usecs < 1)
79 		priv->params_ethtool.rx_coalesce_usecs = 0;
80 	else if (priv->params_ethtool.rx_coalesce_usecs >
81 	    MLX5E_FLD_MAX(cqc, cq_period)) {
82 		priv->params_ethtool.rx_coalesce_usecs =
83 		    MLX5E_FLD_MAX(cqc, cq_period);
84 	}
85 	priv->params.rx_cq_moderation_usec = priv->params_ethtool.rx_coalesce_usecs;
86 
87 	/* import RX coal pkts */
88 	if (priv->params_ethtool.rx_coalesce_pkts < 1)
89 		priv->params_ethtool.rx_coalesce_pkts = 0;
90 	else if (priv->params_ethtool.rx_coalesce_pkts >
91 	    MLX5E_FLD_MAX(cqc, cq_max_count)) {
92 		priv->params_ethtool.rx_coalesce_pkts =
93 		    MLX5E_FLD_MAX(cqc, cq_max_count);
94 	}
95 	priv->params.rx_cq_moderation_pkts = priv->params_ethtool.rx_coalesce_pkts;
96 
97 	/* import TX coal time */
98 	if (priv->params_ethtool.tx_coalesce_usecs < 1)
99 		priv->params_ethtool.tx_coalesce_usecs = 0;
100 	else if (priv->params_ethtool.tx_coalesce_usecs >
101 	    MLX5E_FLD_MAX(cqc, cq_period)) {
102 		priv->params_ethtool.tx_coalesce_usecs =
103 		    MLX5E_FLD_MAX(cqc, cq_period);
104 	}
105 	priv->params.tx_cq_moderation_usec = priv->params_ethtool.tx_coalesce_usecs;
106 
107 	/* import TX coal pkts */
108 	if (priv->params_ethtool.tx_coalesce_pkts < 1)
109 		priv->params_ethtool.tx_coalesce_pkts = 0;
110 	else if (priv->params_ethtool.tx_coalesce_pkts >
111 	    MLX5E_FLD_MAX(cqc, cq_max_count)) {
112 		priv->params_ethtool.tx_coalesce_pkts = MLX5E_FLD_MAX(cqc, cq_max_count);
113 	}
114 	priv->params.tx_cq_moderation_pkts = priv->params_ethtool.tx_coalesce_pkts;
115 
116 	if (&priv->params_ethtool.arg[arg2] == &priv->params_ethtool.rx_pauseframe_control ||
117 	    &priv->params_ethtool.arg[arg2] == &priv->params_ethtool.tx_pauseframe_control) {
118 		/* range check parameters */
119 		priv->params_ethtool.rx_pauseframe_control =
120 		    priv->params_ethtool.rx_pauseframe_control ? 1 : 0;
121 		priv->params_ethtool.tx_pauseframe_control =
122 		    priv->params_ethtool.tx_pauseframe_control ? 1 : 0;
123 
124 		/* update firmware */
125 		error = -mlx5_set_port_pause(priv->mdev, 1,
126 		    priv->params_ethtool.rx_pauseframe_control,
127 		    priv->params_ethtool.tx_pauseframe_control);
128 		goto done;
129 	}
130 
131 	was_opened = test_bit(MLX5E_STATE_OPENED, &priv->state);
132 	if (was_opened) {
133 		u64 *xarg = priv->params_ethtool.arg + arg2;
134 
135 		if (xarg == &priv->params_ethtool.tx_coalesce_pkts ||
136 		    xarg == &priv->params_ethtool.rx_coalesce_pkts ||
137 		    xarg == &priv->params_ethtool.tx_coalesce_usecs ||
138 		    xarg == &priv->params_ethtool.rx_coalesce_usecs) {
139 			/* avoid downing and upping the network interface */
140 			error = mlx5e_refresh_channel_params(priv);
141 			goto done;
142 		}
143 		mlx5e_close_locked(priv->ifp);
144 	}
145 	/* import TX queue size */
146 	if (priv->params_ethtool.tx_queue_size <
147 	    (1 << MLX5E_PARAMS_MINIMUM_LOG_SQ_SIZE)) {
148 		priv->params_ethtool.tx_queue_size =
149 		    (1 << MLX5E_PARAMS_MINIMUM_LOG_SQ_SIZE);
150 	} else if (priv->params_ethtool.tx_queue_size >
151 	    priv->params_ethtool.tx_queue_size_max) {
152 		priv->params_ethtool.tx_queue_size =
153 		    priv->params_ethtool.tx_queue_size_max;
154 	}
155 	priv->params.log_sq_size =
156 	    order_base_2(priv->params_ethtool.tx_queue_size);
157 
158 	/* import RX queue size */
159 	if (priv->params_ethtool.rx_queue_size <
160 	    (1 << MLX5E_PARAMS_MINIMUM_LOG_RQ_SIZE)) {
161 		priv->params_ethtool.rx_queue_size =
162 		    (1 << MLX5E_PARAMS_MINIMUM_LOG_RQ_SIZE);
163 	} else if (priv->params_ethtool.rx_queue_size >
164 	    priv->params_ethtool.rx_queue_size_max) {
165 		priv->params_ethtool.rx_queue_size =
166 		    priv->params_ethtool.rx_queue_size_max;
167 	}
168 	priv->params.log_rq_size =
169 	    order_base_2(priv->params_ethtool.rx_queue_size);
170 
171 	priv->params.min_rx_wqes = min_t (u16,
172 	          priv->params_ethtool.rx_queue_size - 1,
173 	          MLX5E_PARAMS_DEFAULT_MIN_RX_WQES);
174 
175 	/* import number of channels */
176 	if (priv->params_ethtool.channels < 1)
177 		priv->params_ethtool.channels = 1;
178 	else if (priv->params_ethtool.channels >
179 	    (u64) priv->mdev->priv.eq_table.num_comp_vectors) {
180 		priv->params_ethtool.channels =
181 		    (u64) priv->mdev->priv.eq_table.num_comp_vectors;
182 	}
183 	priv->params.num_channels = priv->params_ethtool.channels;
184 
185 	/* import RX mode */
186 	if (priv->params_ethtool.rx_coalesce_mode != 0)
187 		priv->params_ethtool.rx_coalesce_mode = 1;
188 	priv->params.rx_cq_moderation_mode = priv->params_ethtool.rx_coalesce_mode;
189 
190 	/* import TX mode */
191 	if (priv->params_ethtool.tx_coalesce_mode != 0)
192 		priv->params_ethtool.tx_coalesce_mode = 1;
193 	priv->params.tx_cq_moderation_mode = priv->params_ethtool.tx_coalesce_mode;
194 
195 	/* we always agree to turn off HW LRO - but not always to turn on */
196 	if (priv->params_ethtool.hw_lro) {
197 		if (priv->params_ethtool.hw_lro != 1) {
198 			priv->params_ethtool.hw_lro = priv->params.hw_lro_en;
199 			error = EINVAL;
200 			goto done;
201 		}
202 		if (priv->ifp->if_capenable & IFCAP_LRO)
203 			priv->params.hw_lro_en = !!MLX5_CAP_ETH(priv->mdev, lro_cap);
204 		else {
205 			/* set the correct (0) value to params_ethtool.hw_lro, issue a warning and return error */
206 			priv->params_ethtool.hw_lro = 0;
207 			error = EINVAL;
208 			if_printf(priv->ifp, "Can't set HW_LRO to a device with LRO turned off");
209 			goto done;
210 		}
211 	} else {
212 		priv->params.hw_lro_en = false;
213 	}
214 
215 	if (&priv->params_ethtool.arg[arg2] ==
216 	    &priv->params_ethtool.cqe_zipping) {
217 		if (priv->params_ethtool.cqe_zipping &&
218 		    MLX5_CAP_GEN(priv->mdev, cqe_compression)) {
219 			priv->params.cqe_zipping_en = true;
220 			priv->params_ethtool.cqe_zipping = 1;
221 		} else {
222 			priv->params.cqe_zipping_en = false;
223 			priv->params_ethtool.cqe_zipping = 0;
224 		}
225 	}
226 
227 	if (was_opened)
228 		mlx5e_open_locked(priv->ifp);
229 done:
230 	PRIV_UNLOCK(priv);
231 	return (error);
232 }
233 
234 /*
235  * Read the first three bytes of the eeprom in order to get the needed info
236  * for the whole reading.
237  * Byte 0 - Identifier byte
238  * Byte 1 - Revision byte
239  * Byte 2 - Status byte
240  */
241 static int
242 mlx5e_get_eeprom_info(struct mlx5e_priv *priv, struct mlx5e_eeprom *eeprom)
243 {
244 	struct mlx5_core_dev *dev = priv->mdev;
245 	u32 data = 0;
246 	int size_read = 0;
247 	int ret;
248 
249 	ret = mlx5_query_module_num(dev, &eeprom->module_num);
250 	if (ret) {
251 		if_printf(priv->ifp, "%s:%d: Failed query module error=%d\n",
252 		    __func__, __LINE__, ret);
253 		return (ret);
254 	}
255 
256 	/* Read the first three bytes to get Identifier, Revision and Status */
257 	ret = mlx5_query_eeprom(dev, eeprom->i2c_addr, eeprom->page_num,
258 	    eeprom->device_addr, MLX5E_EEPROM_INFO_BYTES, eeprom->module_num, &data,
259 	    &size_read);
260 	if (ret) {
261 		if_printf(priv->ifp, "%s:%d: Failed query eeprom module error=0x%x\n",
262 		    __func__, __LINE__, ret);
263 		return (ret);
264 	}
265 
266 	switch (data & MLX5_EEPROM_IDENTIFIER_BYTE_MASK) {
267 	case SFF_8024_ID_QSFP:
268 		eeprom->type = MLX5E_ETH_MODULE_SFF_8436;
269 		eeprom->len = MLX5E_ETH_MODULE_SFF_8436_LEN;
270 		break;
271 	case SFF_8024_ID_QSFPPLUS:
272 	case SFF_8024_ID_QSFP28:
273 		if ((data & MLX5_EEPROM_IDENTIFIER_BYTE_MASK) == SFF_8024_ID_QSFP28 ||
274 		    ((data & MLX5_EEPROM_REVISION_ID_BYTE_MASK) >> 8) >= 0x3) {
275 			eeprom->type = MLX5E_ETH_MODULE_SFF_8636;
276 			eeprom->len = MLX5E_ETH_MODULE_SFF_8636_LEN;
277 		} else {
278 			eeprom->type = MLX5E_ETH_MODULE_SFF_8436;
279 			eeprom->len = MLX5E_ETH_MODULE_SFF_8436_LEN;
280 		}
281 		if ((data & MLX5_EEPROM_PAGE_3_VALID_BIT_MASK) == 0)
282 			eeprom->page_valid = 1;
283 		break;
284 	case SFF_8024_ID_SFP:
285 		eeprom->type = MLX5E_ETH_MODULE_SFF_8472;
286 		eeprom->len = MLX5E_ETH_MODULE_SFF_8472_LEN;
287 		break;
288 	default:
289 		if_printf(priv->ifp, "%s:%d: Not recognized cable type = 0x%x(%s)\n",
290 		    __func__, __LINE__, data & MLX5_EEPROM_IDENTIFIER_BYTE_MASK,
291 		    sff_8024_id[data & MLX5_EEPROM_IDENTIFIER_BYTE_MASK]);
292 		return (EINVAL);
293 	}
294 	return (0);
295 }
296 
297 /* Read both low and high pages of the eeprom */
298 static int
299 mlx5e_get_eeprom(struct mlx5e_priv *priv, struct mlx5e_eeprom *ee)
300 {
301 	struct mlx5_core_dev *dev = priv->mdev;
302 	int size_read = 0;
303 	int ret;
304 
305 	if (ee->len == 0)
306 		return (EINVAL);
307 
308 	/* Read low page of the eeprom */
309 	while (ee->device_addr < ee->len) {
310 		ret = mlx5_query_eeprom(dev, ee->i2c_addr, ee->page_num, ee->device_addr,
311 		    ee->len - ee->device_addr, ee->module_num,
312 		    ee->data + (ee->device_addr / 4), &size_read);
313 		if (ret) {
314 			if_printf(priv->ifp, "%s:%d: Failed reading eeprom, "
315 			    "error = 0x%02x\n", __func__, __LINE__, ret);
316 			return (ret);
317 		}
318 		ee->device_addr += size_read;
319 	}
320 
321 	/* Read high page of the eeprom */
322 	if (ee->page_valid) {
323 		ee->device_addr = MLX5E_EEPROM_HIGH_PAGE_OFFSET;
324 		ee->page_num = MLX5E_EEPROM_HIGH_PAGE;
325 		size_read = 0;
326 		while (ee->device_addr < MLX5E_EEPROM_PAGE_LENGTH) {
327 			ret = mlx5_query_eeprom(dev, ee->i2c_addr, ee->page_num,
328 			    ee->device_addr, MLX5E_EEPROM_PAGE_LENGTH - ee->device_addr,
329 			    ee->module_num, ee->data + (ee->len / 4) +
330 			    ((ee->device_addr - MLX5E_EEPROM_HIGH_PAGE_OFFSET) / 4),
331 			    &size_read);
332 			if (ret) {
333 				if_printf(priv->ifp, "%s:%d: Failed reading eeprom, "
334 				    "error = 0x%02x\n", __func__, __LINE__, ret);
335 				return (ret);
336 			}
337 			ee->device_addr += size_read;
338 		}
339 	}
340 	return (0);
341 }
342 
343 static void
344 mlx5e_print_eeprom(struct mlx5e_eeprom *eeprom)
345 {
346 	int row;
347 	int index_in_row;
348 	int byte_to_write = 0;
349 	int line_length = 16;
350 
351 	printf("\nOffset\t\tValues\n");
352 	printf("------\t\t------");
353 	while (byte_to_write < eeprom->len) {
354 		printf("\n0x%04X\t\t", byte_to_write);
355 		for (index_in_row = 0; index_in_row < line_length; index_in_row++) {
356 			printf("%02X ", ((u8 *)eeprom->data)[byte_to_write]);
357 			byte_to_write++;
358 		}
359 	}
360 
361 	if (eeprom->page_valid) {
362 		row = MLX5E_EEPROM_HIGH_PAGE_OFFSET;
363 		printf("\n\nUpper Page 0x03\n");
364 		printf("\nOffset\t\tValues\n");
365 		printf("------\t\t------");
366 		while (row < MLX5E_EEPROM_PAGE_LENGTH) {
367 			printf("\n0x%04X\t\t", row);
368 			for (index_in_row = 0; index_in_row < line_length; index_in_row++) {
369 				printf("%02X ", ((u8 *)eeprom->data)[byte_to_write]);
370 				byte_to_write++;
371 				row++;
372 			}
373 		}
374 	}
375 }
376 
377 /*
378  * Read cable EEPROM module information by first inspecting the first
379  * three bytes to get the initial information for a whole reading.
380  * Information will be printed to dmesg.
381  */
382 static int
383 mlx5e_read_eeprom(SYSCTL_HANDLER_ARGS)
384 {
385 	struct mlx5e_priv *priv = arg1;
386 	struct mlx5e_eeprom eeprom;
387 	int error;
388 	int result = 0;
389 
390 	PRIV_LOCK(priv);
391 	error = sysctl_handle_int(oidp, &result, 0, req);
392 	if (error || !req->newptr)
393 		goto done;
394 
395 	/* Check if device is gone */
396 	if (priv->gone) {
397 		error = ENXIO;
398 		goto done;
399 	}
400 
401 	if (result == 1) {
402 		eeprom.i2c_addr = MLX5E_I2C_ADDR_LOW;
403 		eeprom.device_addr = 0;
404 		eeprom.page_num = MLX5E_EEPROM_LOW_PAGE;
405 		eeprom.page_valid = 0;
406 
407 		/* Read three first bytes to get important info */
408 		error = mlx5e_get_eeprom_info(priv, &eeprom);
409 		if (error) {
410 			if_printf(priv->ifp, "%s:%d: Failed reading eeprom's "
411 			    "initial information\n", __func__, __LINE__);
412 			error = 0;
413 			goto done;
414 		}
415 		/*
416 		 * Allocate needed length buffer and additional space for
417 		 * page 0x03
418 		 */
419 		eeprom.data = malloc(eeprom.len + MLX5E_EEPROM_PAGE_LENGTH,
420 		    M_MLX5EN, M_WAITOK | M_ZERO);
421 
422 		/* Read the whole eeprom information */
423 		error = mlx5e_get_eeprom(priv, &eeprom);
424 		if (error) {
425 			if_printf(priv->ifp, "%s:%d: Failed reading eeprom\n",
426 			    __func__, __LINE__);
427 			error = 0;
428 			/*
429 			 * Continue printing partial information in case of
430 			 * an error
431 			 */
432 		}
433 		mlx5e_print_eeprom(&eeprom);
434 		free(eeprom.data, M_MLX5EN);
435 	}
436 done:
437 	PRIV_UNLOCK(priv);
438 	return (error);
439 }
440 
441 static const char *mlx5e_params_desc[] = {
442 	MLX5E_PARAMS(MLX5E_STATS_DESC)
443 };
444 
445 static const char *mlx5e_port_stats_debug_desc[] = {
446 	MLX5E_PORT_STATS_DEBUG(MLX5E_STATS_DESC)
447 };
448 
449 static int
450 mlx5e_ethtool_debug_stats(SYSCTL_HANDLER_ARGS)
451 {
452 	struct mlx5e_priv *priv = arg1;
453 	int error;
454 	int sys_debug;
455 
456 	sys_debug = priv->sysctl_debug;
457 	error = sysctl_handle_int(oidp, &priv->sysctl_debug, 0, req);
458 	if (error || !req->newptr)
459 		return (error);
460 	priv->sysctl_debug = !!priv->sysctl_debug;
461 	if (sys_debug == priv->sysctl_debug)
462 		return (error);
463 	if (priv->sysctl_debug)
464 		mlx5e_create_stats(&priv->stats.port_stats_debug.ctx,
465 		    SYSCTL_CHILDREN(priv->sysctl_ifnet), "debug_stats",
466 		    mlx5e_port_stats_debug_desc, MLX5E_PORT_STATS_DEBUG_NUM,
467 		    priv->stats.port_stats_debug.arg);
468 	else
469 		sysctl_ctx_free(&priv->stats.port_stats_debug.ctx);
470 	return (error);
471 }
472 
473 void
474 mlx5e_create_ethtool(struct mlx5e_priv *priv)
475 {
476 	struct sysctl_oid *node;
477 	const char *pnameunit;
478 	unsigned x;
479 
480 	/* set some defaults */
481 	priv->params_ethtool.tx_queue_size_max = 1 << MLX5E_PARAMS_MAXIMUM_LOG_SQ_SIZE;
482 	priv->params_ethtool.rx_queue_size_max = 1 << MLX5E_PARAMS_MAXIMUM_LOG_RQ_SIZE;
483 	priv->params_ethtool.tx_queue_size = 1 << priv->params.log_sq_size;
484 	priv->params_ethtool.rx_queue_size = 1 << priv->params.log_rq_size;
485 	priv->params_ethtool.channels = priv->params.num_channels;
486 	priv->params_ethtool.coalesce_pkts_max = MLX5E_FLD_MAX(cqc, cq_max_count);
487 	priv->params_ethtool.coalesce_usecs_max = MLX5E_FLD_MAX(cqc, cq_period);
488 	priv->params_ethtool.rx_coalesce_mode = priv->params.rx_cq_moderation_mode;
489 	priv->params_ethtool.rx_coalesce_usecs = priv->params.rx_cq_moderation_usec;
490 	priv->params_ethtool.rx_coalesce_pkts = priv->params.rx_cq_moderation_pkts;
491 	priv->params_ethtool.tx_coalesce_mode = priv->params.tx_cq_moderation_mode;
492 	priv->params_ethtool.tx_coalesce_usecs = priv->params.tx_cq_moderation_usec;
493 	priv->params_ethtool.tx_coalesce_pkts = priv->params.tx_cq_moderation_pkts;
494 	priv->params_ethtool.hw_lro = priv->params.hw_lro_en;
495 	priv->params_ethtool.cqe_zipping = priv->params.cqe_zipping_en;
496 
497 	/* create root node */
498 	node = SYSCTL_ADD_NODE(&priv->sysctl_ctx,
499 	    SYSCTL_CHILDREN(priv->sysctl_ifnet), OID_AUTO,
500 	    "conf", CTLFLAG_RW, NULL, "Configuration");
501 	if (node == NULL)
502 		return;
503 	for (x = 0; x != MLX5E_PARAMS_NUM; x++) {
504 		/* check for read-only parameter */
505 		if (strstr(mlx5e_params_desc[2 * x], "_max") != NULL) {
506 			SYSCTL_ADD_PROC(&priv->sysctl_ctx, SYSCTL_CHILDREN(node), OID_AUTO,
507 			    mlx5e_params_desc[2 * x], CTLTYPE_U64 | CTLFLAG_RD |
508 			    CTLFLAG_MPSAFE, priv, x, &mlx5e_ethtool_handler, "QU",
509 			    mlx5e_params_desc[2 * x + 1]);
510 		} else {
511 #if (__FreeBSD_version < 1100000)
512 			char path[64];
513 #endif
514 			/*
515 			 * NOTE: In FreeBSD-11 and newer the
516 			 * CTLFLAG_RWTUN flag will take care of
517 			 * loading default sysctl value from the
518 			 * kernel environment, if any:
519 			 */
520 			SYSCTL_ADD_PROC(&priv->sysctl_ctx, SYSCTL_CHILDREN(node), OID_AUTO,
521 			    mlx5e_params_desc[2 * x], CTLTYPE_U64 | CTLFLAG_RWTUN |
522 			    CTLFLAG_MPSAFE, priv, x, &mlx5e_ethtool_handler, "QU",
523 			    mlx5e_params_desc[2 * x + 1]);
524 
525 #if (__FreeBSD_version < 1100000)
526 			/* compute path for sysctl */
527 			snprintf(path, sizeof(path), "dev.mce.%d.conf.%s",
528 			    device_get_unit(priv->mdev->pdev->dev.bsddev),
529 			    mlx5e_params_desc[2 * x]);
530 
531 			/* try to fetch tunable, if any */
532 			if (TUNABLE_QUAD_FETCH(path, &priv->params_ethtool.arg[x]))
533 				mlx5e_ethtool_handler(NULL, priv, x, NULL);
534 #endif
535 		}
536 	}
537 
538 	SYSCTL_ADD_PROC(&priv->sysctl_ctx, SYSCTL_CHILDREN(node), OID_AUTO,
539 	    "debug_stats", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, priv,
540 	    0, &mlx5e_ethtool_debug_stats, "I", "Extended debug statistics");
541 
542 	pnameunit = device_get_nameunit(priv->mdev->pdev->dev.bsddev);
543 
544 	SYSCTL_ADD_STRING(&priv->sysctl_ctx, SYSCTL_CHILDREN(node),
545 	    OID_AUTO, "device_name", CTLFLAG_RD,
546 	    __DECONST(void *, pnameunit), 0,
547 	    "PCI device name");
548 
549 	/* EEPROM support */
550 	SYSCTL_ADD_PROC(&priv->sysctl_ctx, SYSCTL_CHILDREN(node), OID_AUTO, "eeprom_info",
551 	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, priv, 0,
552 	    mlx5e_read_eeprom, "I", "EEPROM information");
553 }
554