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