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