mlx5_en_ethtool.c revision 291184
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: stable/10/sys/dev/mlx5/mlx5_en/mlx5_en_ethtool.c 291184 2015-11-23 09:32:32Z hselasky $
26 */
27
28#include "en.h"
29#include <net/sff8472.h>
30
31void
32mlx5e_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
51static int
52mlx5e_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);
200done:
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 */
212static int
213mlx5e_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 */
269static int
270mlx5e_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
314static void
315mlx5e_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 */
354static int
355mlx5e_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	}
408done:
409	PRIV_UNLOCK(priv);
410	return (error);
411}
412
413static const char *mlx5e_params_desc[] = {
414	MLX5E_PARAMS(MLX5E_STATS_DESC)
415};
416
417static const char *mlx5e_port_stats_debug_desc[] = {
418	MLX5E_PORT_STATS_DEBUG(MLX5E_STATS_DESC)
419};
420
421static int
422mlx5e_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
445void
446mlx5e_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