xref: /freebsd/sys/dev/mlx5/mlx5_en/mlx5_en_ethtool.c (revision c6a33c8e88c5684876e670c8189d03ad25108d8a)
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 	error = sysctl_handle_64(oidp, &value, 0, req);
62 	if (error || req->newptr == NULL ||
63 	    value == priv->params_ethtool.arg[arg2])
64 		goto done;
65 
66 	/* assign new value */
67 	priv->params_ethtool.arg[arg2] = value;
68 
69 	/* check if device is gone */
70 	if (priv->gone) {
71 		error = ENXIO;
72 		goto done;
73 	}
74 
75 	if (&priv->params_ethtool.arg[arg2] == &priv->params_ethtool.rx_pauseframe_control ||
76 	    &priv->params_ethtool.arg[arg2] == &priv->params_ethtool.tx_pauseframe_control) {
77 		/* range check parameters */
78 		priv->params_ethtool.rx_pauseframe_control =
79 		    priv->params_ethtool.rx_pauseframe_control ? 1 : 0;
80 		priv->params_ethtool.tx_pauseframe_control =
81 		    priv->params_ethtool.tx_pauseframe_control ? 1 : 0;
82 
83 		/* update firmware */
84 		error = -mlx5_set_port_pause(priv->mdev, 1,
85 		    priv->params_ethtool.rx_pauseframe_control,
86 		    priv->params_ethtool.tx_pauseframe_control);
87 		goto done;
88 	}
89 
90 	was_opened = test_bit(MLX5E_STATE_OPENED, &priv->state);
91 	if (was_opened)
92 		mlx5e_close_locked(priv->ifp);
93 
94 	/* import TX queue size */
95 	if (priv->params_ethtool.tx_queue_size <
96 	    (1 << MLX5E_PARAMS_MINIMUM_LOG_SQ_SIZE)) {
97 		priv->params_ethtool.tx_queue_size =
98 		    (1 << MLX5E_PARAMS_MINIMUM_LOG_SQ_SIZE);
99 	} else if (priv->params_ethtool.tx_queue_size >
100 	    priv->params_ethtool.tx_queue_size_max) {
101 		priv->params_ethtool.tx_queue_size =
102 		    priv->params_ethtool.tx_queue_size_max;
103 	}
104 	priv->params.log_sq_size =
105 	    order_base_2(priv->params_ethtool.tx_queue_size);
106 
107 	/* import RX queue size */
108 	if (priv->params_ethtool.rx_queue_size <
109 	    (1 << MLX5E_PARAMS_MINIMUM_LOG_RQ_SIZE)) {
110 		priv->params_ethtool.rx_queue_size =
111 		    (1 << MLX5E_PARAMS_MINIMUM_LOG_RQ_SIZE);
112 	} else if (priv->params_ethtool.rx_queue_size >
113 	    priv->params_ethtool.rx_queue_size_max) {
114 		priv->params_ethtool.rx_queue_size =
115 		    priv->params_ethtool.rx_queue_size_max;
116 	}
117 	priv->params.log_rq_size =
118 	    order_base_2(priv->params_ethtool.rx_queue_size);
119 
120 	priv->params.min_rx_wqes = min_t (u16,
121 	          priv->params_ethtool.rx_queue_size - 1,
122 	          MLX5E_PARAMS_DEFAULT_MIN_RX_WQES);
123 
124 	/* import number of channels */
125 	if (priv->params_ethtool.channels < 1)
126 		priv->params_ethtool.channels = 1;
127 	else if (priv->params_ethtool.channels >
128 	    (u64) priv->mdev->priv.eq_table.num_comp_vectors) {
129 		priv->params_ethtool.channels =
130 		    (u64) priv->mdev->priv.eq_table.num_comp_vectors;
131 	}
132 	priv->params.num_channels = priv->params_ethtool.channels;
133 
134 	/* import RX mode */
135 	if (priv->params_ethtool.rx_coalesce_mode != 0)
136 		priv->params_ethtool.rx_coalesce_mode = 1;
137 	priv->params.rx_cq_moderation_mode = priv->params_ethtool.rx_coalesce_mode;
138 
139 	/* import TX mode */
140 	if (priv->params_ethtool.tx_coalesce_mode != 0)
141 		priv->params_ethtool.tx_coalesce_mode = 1;
142 	priv->params.tx_cq_moderation_mode = priv->params_ethtool.tx_coalesce_mode;
143 
144 	/* import RX coal time */
145 	if (priv->params_ethtool.rx_coalesce_usecs < 1)
146 		priv->params_ethtool.rx_coalesce_usecs = 0;
147 	else if (priv->params_ethtool.rx_coalesce_usecs >
148 	    MLX5E_FLD_MAX(cqc, cq_period)) {
149 		priv->params_ethtool.rx_coalesce_usecs =
150 		    MLX5E_FLD_MAX(cqc, cq_period);
151 	}
152 	priv->params.rx_cq_moderation_usec = priv->params_ethtool.rx_coalesce_usecs;
153 
154 	/* import RX coal pkts */
155 	if (priv->params_ethtool.rx_coalesce_pkts < 1)
156 		priv->params_ethtool.rx_coalesce_pkts = 0;
157 	else if (priv->params_ethtool.rx_coalesce_pkts >
158 	    MLX5E_FLD_MAX(cqc, cq_max_count)) {
159 		priv->params_ethtool.rx_coalesce_pkts =
160 		    MLX5E_FLD_MAX(cqc, cq_max_count);
161 	}
162 	priv->params.rx_cq_moderation_pkts = priv->params_ethtool.rx_coalesce_pkts;
163 
164 	/* import TX coal time */
165 	if (priv->params_ethtool.tx_coalesce_usecs < 1)
166 		priv->params_ethtool.tx_coalesce_usecs = 0;
167 	else if (priv->params_ethtool.tx_coalesce_usecs >
168 	    MLX5E_FLD_MAX(cqc, cq_period)) {
169 		priv->params_ethtool.tx_coalesce_usecs =
170 		    MLX5E_FLD_MAX(cqc, cq_period);
171 	}
172 	priv->params.tx_cq_moderation_usec = priv->params_ethtool.tx_coalesce_usecs;
173 
174 	/* import TX coal pkts */
175 	if (priv->params_ethtool.tx_coalesce_pkts < 1)
176 		priv->params_ethtool.tx_coalesce_pkts = 0;
177 	else if (priv->params_ethtool.tx_coalesce_pkts >
178 	    MLX5E_FLD_MAX(cqc, cq_max_count)) {
179 		priv->params_ethtool.tx_coalesce_pkts = MLX5E_FLD_MAX(cqc, cq_max_count);
180 	}
181 	priv->params.tx_cq_moderation_pkts = priv->params_ethtool.tx_coalesce_pkts;
182 
183 	/* we always agree to turn off HW LRO - but not always to turn on */
184 	if (priv->params_ethtool.hw_lro) {
185 		if (priv->params_ethtool.hw_lro != 1) {
186 			priv->params_ethtool.hw_lro = priv->params.hw_lro_en;
187 			error = EINVAL;
188 			goto done;
189 		}
190 		if (priv->ifp->if_capenable & IFCAP_LRO)
191 			priv->params.hw_lro_en = !!MLX5_CAP_ETH(priv->mdev, lro_cap);
192 		else {
193 			/* set the correct (0) value to params_ethtool.hw_lro, issue a warning and return error */
194 			priv->params_ethtool.hw_lro = 0;
195 			error = EINVAL;
196 			if_printf(priv->ifp, "Can't set HW_LRO to a device with LRO turned off");
197 			goto done;
198 		}
199 	} else {
200 		priv->params.hw_lro_en = false;
201 	}
202 
203 	if (was_opened)
204 		mlx5e_open_locked(priv->ifp);
205 done:
206 	PRIV_UNLOCK(priv);
207 	return (error);
208 }
209 
210 /*
211  * Read the first three bytes of the eeprom in order to get the needed info
212  * for the whole reading.
213  * Byte 0 - Identifier byte
214  * Byte 1 - Revision byte
215  * Byte 2 - Status byte
216  */
217 static int
218 mlx5e_get_eeprom_info(struct mlx5e_priv *priv, struct mlx5e_eeprom *eeprom)
219 {
220 	struct mlx5_core_dev *dev = priv->mdev;
221 	u32 data = 0;
222 	int size_read = 0;
223 	int ret;
224 
225 	ret = mlx5_query_module_num(dev, &eeprom->module_num);
226 	if (ret) {
227 		if_printf(priv->ifp, "%s:%d: Failed query module error=%d\n",
228 		    __func__, __LINE__, ret);
229 		return (ret);
230 	}
231 
232 	/* Read the first three bytes to get Identifier, Revision and Status */
233 	ret = mlx5_query_eeprom(dev, eeprom->i2c_addr, eeprom->page_num,
234 	    eeprom->device_addr, MLX5E_EEPROM_INFO_BYTES, eeprom->module_num, &data,
235 	    &size_read);
236 	if (ret) {
237 		if_printf(priv->ifp, "%s:%d: Failed query eeprom module error=0x%x\n",
238 		    __func__, __LINE__, ret);
239 		return (ret);
240 	}
241 
242 	switch (data & MLX5_EEPROM_IDENTIFIER_BYTE_MASK) {
243 	case SFF_8024_ID_QSFP:
244 		eeprom->type = MLX5E_ETH_MODULE_SFF_8436;
245 		eeprom->len = MLX5E_ETH_MODULE_SFF_8436_LEN;
246 		break;
247 	case SFF_8024_ID_QSFPPLUS:
248 	case SFF_8024_ID_QSFP28:
249 		if ((data & MLX5_EEPROM_IDENTIFIER_BYTE_MASK) == SFF_8024_ID_QSFP28 ||
250 		    ((data & MLX5_EEPROM_REVISION_ID_BYTE_MASK) >> 8) >= 0x3) {
251 			eeprom->type = MLX5E_ETH_MODULE_SFF_8636;
252 			eeprom->len = MLX5E_ETH_MODULE_SFF_8636_LEN;
253 		} else {
254 			eeprom->type = MLX5E_ETH_MODULE_SFF_8436;
255 			eeprom->len = MLX5E_ETH_MODULE_SFF_8436_LEN;
256 		}
257 		if ((data & MLX5_EEPROM_PAGE_3_VALID_BIT_MASK) == 0)
258 			eeprom->page_valid = 1;
259 		break;
260 	case SFF_8024_ID_SFP:
261 		eeprom->type = MLX5E_ETH_MODULE_SFF_8472;
262 		eeprom->len = MLX5E_ETH_MODULE_SFF_8472_LEN;
263 		break;
264 	default:
265 		if_printf(priv->ifp, "%s:%d: Not recognized cable type = 0x%x(%s)\n",
266 		    __func__, __LINE__, data & MLX5_EEPROM_IDENTIFIER_BYTE_MASK,
267 		    sff_8024_id[data & MLX5_EEPROM_IDENTIFIER_BYTE_MASK]);
268 		return (EINVAL);
269 	}
270 	return (0);
271 }
272 
273 /* Read both low and high pages of the eeprom */
274 static int
275 mlx5e_get_eeprom(struct mlx5e_priv *priv, struct mlx5e_eeprom *ee)
276 {
277 	struct mlx5_core_dev *dev = priv->mdev;
278 	int size_read = 0;
279 	int ret;
280 
281 	if (ee->len == 0)
282 		return (EINVAL);
283 
284 	/* Read low page of the eeprom */
285 	while (ee->device_addr < ee->len) {
286 		ret = mlx5_query_eeprom(dev, ee->i2c_addr, ee->page_num, ee->device_addr,
287 		    ee->len - ee->device_addr, ee->module_num,
288 		    ee->data + (ee->device_addr / 4), &size_read);
289 		if (ret) {
290 			if_printf(priv->ifp, "%s:%d: Failed reading eeprom, "
291 			    "error = 0x%02x\n", __func__, __LINE__, ret);
292 			return (ret);
293 		}
294 		ee->device_addr += size_read;
295 	}
296 
297 	/* Read high page of the eeprom */
298 	if (ee->page_valid) {
299 		ee->device_addr = MLX5E_EEPROM_HIGH_PAGE_OFFSET;
300 		ee->page_num = MLX5E_EEPROM_HIGH_PAGE;
301 		size_read = 0;
302 		while (ee->device_addr < MLX5E_EEPROM_PAGE_LENGTH) {
303 			ret = mlx5_query_eeprom(dev, ee->i2c_addr, ee->page_num,
304 			    ee->device_addr, MLX5E_EEPROM_PAGE_LENGTH - ee->device_addr,
305 			    ee->module_num, ee->data + (ee->len / 4) +
306 			    ((ee->device_addr - MLX5E_EEPROM_HIGH_PAGE_OFFSET) / 4),
307 			    &size_read);
308 			if (ret) {
309 				if_printf(priv->ifp, "%s:%d: Failed reading eeprom, "
310 				    "error = 0x%02x\n", __func__, __LINE__, ret);
311 				return (ret);
312 			}
313 			ee->device_addr += size_read;
314 		}
315 	}
316 	return (0);
317 }
318 
319 static void
320 mlx5e_print_eeprom(struct mlx5e_eeprom *eeprom)
321 {
322 	int i, j = 0;
323 	int row = 0;
324 
325 	printf("\nOffset\t\tValues\n");
326 	printf("------\t\t------\n");
327 	while (row < eeprom->len) {
328 		printf("0x%04x\t\t", row);
329 		for (i = 0; i < 16; i++) {
330 			printf("%02x ", ((u8 *)eeprom->data)[j]);
331 			j++;
332 			row++;
333 		}
334 		printf("\n");
335 	}
336 
337 	if (eeprom->page_valid) {
338 		row = MLX5E_EEPROM_HIGH_PAGE_OFFSET;
339 		printf("\nUpper Page 0x03\n");
340 		printf("\nOffset\t\tValues\n");
341 		printf("------\t\t------\n");
342 		while (row < MLX5E_EEPROM_PAGE_LENGTH) {
343 			printf("0x%04x\t\t", row);
344 			for (i = 0; i < 16; i++) {
345 				printf("%02x ", ((u8 *)eeprom->data)[j]);
346 				j++;
347 				row++;
348 			}
349 			printf("\n");
350 		}
351 	}
352 }
353 
354 /*
355  * Read cable EEPROM module information by first inspecting the first
356  * three bytes to get the initial information for a whole reading.
357  * Information will be printed to dmesg.
358  */
359 static int
360 mlx5e_read_eeprom(SYSCTL_HANDLER_ARGS)
361 {
362 	struct mlx5e_priv *priv = arg1;
363 	struct mlx5e_eeprom eeprom;
364 	int error;
365 	int result = 0;
366 
367 	PRIV_LOCK(priv);
368 	error = sysctl_handle_int(oidp, &result, 0, req);
369 	if (error || !req->newptr)
370 		goto done;
371 
372 	/* Check if device is gone */
373 	if (priv->gone) {
374 		error = ENXIO;
375 		goto done;
376 	}
377 
378 	if (result == 1) {
379 		eeprom.i2c_addr = MLX5E_I2C_ADDR_LOW;
380 		eeprom.device_addr = 0;
381 		eeprom.page_num = MLX5E_EEPROM_LOW_PAGE;
382 		eeprom.page_valid = 0;
383 
384 		/* Read three first bytes to get important info */
385 		error = mlx5e_get_eeprom_info(priv, &eeprom);
386 		if (error) {
387 			if_printf(priv->ifp, "%s:%d: Failed reading eeprom's "
388 			    "initial information\n", __func__, __LINE__);
389 			error = 0;
390 			goto done;
391 		}
392 		/*
393 		 * Allocate needed length buffer and additional space for
394 		 * page 0x03
395 		 */
396 		eeprom.data = malloc(eeprom.len + MLX5E_EEPROM_PAGE_LENGTH,
397 		    M_MLX5EN, M_WAITOK | M_ZERO);
398 
399 		/* Read the whole eeprom information */
400 		error = mlx5e_get_eeprom(priv, &eeprom);
401 		if (error) {
402 			if_printf(priv->ifp, "%s:%d: Failed reading eeprom\n",
403 			    __func__, __LINE__);
404 			error = 0;
405 			/*
406 			 * Continue printing partial information in case of
407 			 * an error
408 			 */
409 		}
410 		mlx5e_print_eeprom(&eeprom);
411 		free(eeprom.data, M_MLX5EN);
412 	}
413 done:
414 	PRIV_UNLOCK(priv);
415 	return (error);
416 }
417 
418 static const char *mlx5e_params_desc[] = {
419 	MLX5E_PARAMS(MLX5E_STATS_DESC)
420 };
421 
422 static const char *mlx5e_port_stats_debug_desc[] = {
423 	MLX5E_PORT_STATS_DEBUG(MLX5E_STATS_DESC)
424 };
425 
426 static int
427 mlx5e_ethtool_debug_stats(SYSCTL_HANDLER_ARGS)
428 {
429 	struct mlx5e_priv *priv = arg1;
430 	int error;
431 	int sys_debug;
432 
433 	sys_debug = priv->sysctl_debug;
434 	error = sysctl_handle_int(oidp, &priv->sysctl_debug, 0, req);
435 	if (error || !req->newptr)
436 		return (error);
437 	priv->sysctl_debug = !!priv->sysctl_debug;
438 	if (sys_debug == priv->sysctl_debug)
439 		return (error);
440 	if (priv->sysctl_debug)
441 		mlx5e_create_stats(&priv->stats.port_stats_debug.ctx,
442 		    SYSCTL_CHILDREN(priv->sysctl_ifnet), "debug_stats",
443 		    mlx5e_port_stats_debug_desc, MLX5E_PORT_STATS_DEBUG_NUM,
444 		    priv->stats.port_stats_debug.arg);
445 	else
446 		sysctl_ctx_free(&priv->stats.port_stats_debug.ctx);
447 	return (error);
448 }
449 
450 void
451 mlx5e_create_ethtool(struct mlx5e_priv *priv)
452 {
453 	struct sysctl_oid *node;
454 	const char *pnameunit;
455 	unsigned x;
456 
457 	/* set some defaults */
458 	priv->params_ethtool.tx_queue_size_max = 1 << MLX5E_PARAMS_MAXIMUM_LOG_SQ_SIZE;
459 	priv->params_ethtool.rx_queue_size_max = 1 << MLX5E_PARAMS_MAXIMUM_LOG_RQ_SIZE;
460 	priv->params_ethtool.tx_queue_size = 1 << priv->params.log_sq_size;
461 	priv->params_ethtool.rx_queue_size = 1 << priv->params.log_rq_size;
462 	priv->params_ethtool.channels = priv->params.num_channels;
463 	priv->params_ethtool.coalesce_pkts_max = MLX5E_FLD_MAX(cqc, cq_max_count);
464 	priv->params_ethtool.coalesce_usecs_max = MLX5E_FLD_MAX(cqc, cq_period);
465 	priv->params_ethtool.rx_coalesce_mode = priv->params.rx_cq_moderation_mode;
466 	priv->params_ethtool.rx_coalesce_usecs = priv->params.rx_cq_moderation_usec;
467 	priv->params_ethtool.rx_coalesce_pkts = priv->params.rx_cq_moderation_pkts;
468 	priv->params_ethtool.tx_coalesce_mode = priv->params.tx_cq_moderation_mode;
469 	priv->params_ethtool.tx_coalesce_usecs = priv->params.tx_cq_moderation_usec;
470 	priv->params_ethtool.tx_coalesce_pkts = priv->params.tx_cq_moderation_pkts;
471 	priv->params_ethtool.hw_lro = priv->params.hw_lro_en;
472 
473 	/* create root node */
474 	node = SYSCTL_ADD_NODE(&priv->sysctl_ctx,
475 	    SYSCTL_CHILDREN(priv->sysctl_ifnet), OID_AUTO,
476 	    "conf", CTLFLAG_RW, NULL, "Configuration");
477 	if (node == NULL)
478 		return;
479 	for (x = 0; x != MLX5E_PARAMS_NUM; x++) {
480 		/* check for read-only parameter */
481 		if (strstr(mlx5e_params_desc[2 * x], "_max") != NULL) {
482 			SYSCTL_ADD_PROC(&priv->sysctl_ctx, SYSCTL_CHILDREN(node), OID_AUTO,
483 			    mlx5e_params_desc[2 * x], CTLTYPE_U64 | CTLFLAG_RD |
484 			    CTLFLAG_MPSAFE, priv, x, &mlx5e_ethtool_handler, "QU",
485 			    mlx5e_params_desc[2 * x + 1]);
486 		} else {
487 			SYSCTL_ADD_PROC(&priv->sysctl_ctx, SYSCTL_CHILDREN(node), OID_AUTO,
488 			    mlx5e_params_desc[2 * x], CTLTYPE_U64 | CTLFLAG_RWTUN |
489 			    CTLFLAG_MPSAFE, priv, x, &mlx5e_ethtool_handler, "QU",
490 			    mlx5e_params_desc[2 * x + 1]);
491 		}
492 	}
493 
494 	SYSCTL_ADD_PROC(&priv->sysctl_ctx, SYSCTL_CHILDREN(node), OID_AUTO,
495 	    "debug_stats", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, priv,
496 	    0, &mlx5e_ethtool_debug_stats, "I", "Extended debug statistics");
497 
498 	pnameunit = device_get_nameunit(priv->mdev->pdev->dev.bsddev);
499 
500 	SYSCTL_ADD_STRING(&priv->sysctl_ctx, SYSCTL_CHILDREN(node),
501 	    OID_AUTO, "device_name", CTLFLAG_RD,
502 	    __DECONST(void *, pnameunit), 0,
503 	    "PCI device name");
504 
505 	/* EEPROM support */
506 	SYSCTL_ADD_PROC(&priv->sysctl_ctx, SYSCTL_CHILDREN(node), OID_AUTO, "eeprom_info",
507 	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, priv, 0,
508 	    mlx5e_read_eeprom, "I", "EEPROM information");
509 }
510