1219820Sjeff/*
2272407Shselasky * Copyright (c) 2007, 2014 Mellanox Technologies. All rights reserved.
3219820Sjeff *
4219820Sjeff * This software is available to you under a choice of one of two
5219820Sjeff * licenses.  You may choose to be licensed under the terms of the GNU
6219820Sjeff * General Public License (GPL) Version 2, available from the file
7219820Sjeff * COPYING in the main directory of this source tree, or the
8219820Sjeff * OpenIB.org BSD license below:
9219820Sjeff *
10219820Sjeff *     Redistribution and use in source and binary forms, with or
11219820Sjeff *     without modification, are permitted provided that the following
12219820Sjeff *     conditions are met:
13219820Sjeff *
14219820Sjeff *      - Redistributions of source code must retain the above
15219820Sjeff *        copyright notice, this list of conditions and the following
16219820Sjeff *        disclaimer.
17219820Sjeff *
18219820Sjeff *      - Redistributions in binary form must reproduce the above
19219820Sjeff *        copyright notice, this list of conditions and the following
20219820Sjeff *        disclaimer in the documentation and/or other materials
21219820Sjeff *        provided with the distribution.
22219820Sjeff *
23219820Sjeff * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24219820Sjeff * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25219820Sjeff * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26219820Sjeff * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
27219820Sjeff * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
28219820Sjeff * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
29219820Sjeff * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30219820Sjeff * SOFTWARE.
31219820Sjeff *
32219820Sjeff */
33219820Sjeff
34219820Sjeff#include <linux/kernel.h>
35219820Sjeff#include <linux/ethtool.h>
36219820Sjeff#include <linux/netdevice.h>
37272407Shselasky#include <linux/mlx4/driver.h>
38272407Shselasky#include <linux/in.h>
39272407Shselasky#include <net/ip.h>
40272407Shselasky#include <linux/bitmap.h>
41219820Sjeff
42219820Sjeff#include "mlx4_en.h"
43219820Sjeff#include "en_port.h"
44219820Sjeff
45272407Shselasky#define EN_ETHTOOL_QP_ATTACH (1ull << 63)
46219820Sjeff
47272407Shselaskyunion mlx4_ethtool_flow_union {
48272407Shselasky	struct ethtool_tcpip4_spec		tcp_ip4_spec;
49272407Shselasky	struct ethtool_tcpip4_spec		udp_ip4_spec;
50272407Shselasky	struct ethtool_tcpip4_spec		sctp_ip4_spec;
51272407Shselasky	struct ethtool_ah_espip4_spec		ah_ip4_spec;
52272407Shselasky	struct ethtool_ah_espip4_spec		esp_ip4_spec;
53272407Shselasky	struct ethtool_usrip4_spec		usr_ip4_spec;
54272407Shselasky	struct ethhdr				ether_spec;
55272407Shselasky	__u8					hdata[52];
56272407Shselasky};
57219820Sjeff
58272407Shselaskystruct mlx4_ethtool_flow_ext {
59272407Shselasky	__u8		padding[2];
60272407Shselasky	unsigned char	h_dest[ETH_ALEN];
61272407Shselasky	__be16		vlan_etype;
62272407Shselasky	__be16		vlan_tci;
63272407Shselasky	__be32		data[2];
64272407Shselasky};
65219820Sjeff
66272407Shselaskystruct mlx4_ethtool_rx_flow_spec {
67272407Shselasky	__u32		flow_type;
68272407Shselasky	union mlx4_ethtool_flow_union h_u;
69272407Shselasky	struct mlx4_ethtool_flow_ext h_ext;
70272407Shselasky	union mlx4_ethtool_flow_union m_u;
71272407Shselasky	struct mlx4_ethtool_flow_ext m_ext;
72272407Shselasky	__u64		ring_cookie;
73272407Shselasky	__u32		location;
74272407Shselasky};
75219820Sjeff
76272407Shselaskystruct mlx4_ethtool_rxnfc {
77272407Shselasky	__u32				cmd;
78272407Shselasky	__u32				flow_type;
79272407Shselasky	__u64				data;
80272407Shselasky	struct mlx4_ethtool_rx_flow_spec	fs;
81272407Shselasky	__u32				rule_cnt;
82272407Shselasky	__u32				rule_locs[0];
83272407Shselasky};
84272407Shselasky
85272407Shselasky#ifndef FLOW_MAC_EXT
86272407Shselasky#define	FLOW_MAC_EXT	0x40000000
87272407Shselasky#endif
88272407Shselasky
89219820Sjeffstatic void
90219820Sjeffmlx4_en_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *drvinfo)
91219820Sjeff{
92219820Sjeff	struct mlx4_en_priv *priv = netdev_priv(dev);
93219820Sjeff	struct mlx4_en_dev *mdev = priv->mdev;
94219820Sjeff
95272407Shselasky	strlcpy(drvinfo->driver, DRV_NAME, sizeof(drvinfo->driver));
96272407Shselasky	strlcpy(drvinfo->version, DRV_VERSION " (" DRV_RELDATE ")",
97272407Shselasky		sizeof(drvinfo->version));
98272407Shselasky	snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version),
99272407Shselasky		"%d.%d.%d",
100219820Sjeff		(u16) (mdev->dev->caps.fw_ver >> 32),
101219820Sjeff		(u16) ((mdev->dev->caps.fw_ver >> 16) & 0xffff),
102219820Sjeff		(u16) (mdev->dev->caps.fw_ver & 0xffff));
103272407Shselasky	strlcpy(drvinfo->bus_info, pci_name(mdev->dev->pdev),
104272407Shselasky		sizeof(drvinfo->bus_info));
105219820Sjeff	drvinfo->n_stats = 0;
106219820Sjeff	drvinfo->regdump_len = 0;
107219820Sjeff	drvinfo->eedump_len = 0;
108219820Sjeff}
109219820Sjeff
110272407Shselaskystatic const char main_strings[][ETH_GSTRING_LEN] = {
111272407Shselasky	/* packet statistics */
112272407Shselasky	"rx_packets",
113272407Shselasky	"rx_bytes",
114272407Shselasky	"rx_multicast_packets",
115272407Shselasky	"rx_broadcast_packets",
116272407Shselasky	"rx_errors",
117272407Shselasky	"rx_dropped",
118272407Shselasky	"rx_length_errors",
119272407Shselasky	"rx_over_errors",
120272407Shselasky	"rx_crc_errors",
121272407Shselasky	"rx_jabbers",
122272407Shselasky	"rx_in_range_length_error",
123272407Shselasky	"rx_out_range_length_error",
124272407Shselasky	"rx_lt_64_bytes_packets",
125272407Shselasky	"rx_127_bytes_packets",
126272407Shselasky	"rx_255_bytes_packets",
127272407Shselasky	"rx_511_bytes_packets",
128272407Shselasky	"rx_1023_bytes_packets",
129272407Shselasky	"rx_1518_bytes_packets",
130272407Shselasky	"rx_1522_bytes_packets",
131272407Shselasky	"rx_1548_bytes_packets",
132272407Shselasky	"rx_gt_1548_bytes_packets",
133272407Shselasky	"tx_packets",
134272407Shselasky	"tx_bytes",
135272407Shselasky	"tx_multicast_packets",
136272407Shselasky	"tx_broadcast_packets",
137272407Shselasky	"tx_errors",
138272407Shselasky	"tx_dropped",
139272407Shselasky	"tx_lt_64_bytes_packets",
140272407Shselasky	"tx_127_bytes_packets",
141272407Shselasky	"tx_255_bytes_packets",
142272407Shselasky	"tx_511_bytes_packets",
143272407Shselasky	"tx_1023_bytes_packets",
144272407Shselasky	"tx_1518_bytes_packets",
145272407Shselasky	"tx_1522_bytes_packets",
146272407Shselasky	"tx_1548_bytes_packets",
147272407Shselasky	"tx_gt_1548_bytes_packets",
148272407Shselasky	"rx_prio_0_packets", "rx_prio_0_bytes",
149272407Shselasky	"rx_prio_1_packets", "rx_prio_1_bytes",
150272407Shselasky	"rx_prio_2_packets", "rx_prio_2_bytes",
151272407Shselasky	"rx_prio_3_packets", "rx_prio_3_bytes",
152272407Shselasky	"rx_prio_4_packets", "rx_prio_4_bytes",
153272407Shselasky	"rx_prio_5_packets", "rx_prio_5_bytes",
154272407Shselasky	"rx_prio_6_packets", "rx_prio_6_bytes",
155272407Shselasky	"rx_prio_7_packets", "rx_prio_7_bytes",
156272407Shselasky	"rx_novlan_packets", "rx_novlan_bytes",
157272407Shselasky	"tx_prio_0_packets", "tx_prio_0_bytes",
158272407Shselasky	"tx_prio_1_packets", "tx_prio_1_bytes",
159272407Shselasky	"tx_prio_2_packets", "tx_prio_2_bytes",
160272407Shselasky	"tx_prio_3_packets", "tx_prio_3_bytes",
161272407Shselasky	"tx_prio_4_packets", "tx_prio_4_bytes",
162272407Shselasky	"tx_prio_5_packets", "tx_prio_5_bytes",
163272407Shselasky	"tx_prio_6_packets", "tx_prio_6_bytes",
164272407Shselasky	"tx_prio_7_packets", "tx_prio_7_bytes",
165272407Shselasky	"tx_novlan_packets", "tx_novlan_bytes",
166219820Sjeff
167272407Shselasky	/* flow control statistics */
168272407Shselasky	"rx_pause_prio_0", "rx_pause_duration_prio_0",
169272407Shselasky	"rx_pause_transition_prio_0", "tx_pause_prio_0",
170272407Shselasky	"tx_pause_duration_prio_0", "tx_pause_transition_prio_0",
171272407Shselasky	"rx_pause_prio_1", "rx_pause_duration_prio_1",
172272407Shselasky	"rx_pause_transition_prio_1", "tx_pause_prio_1",
173272407Shselasky	"tx_pause_duration_prio_1", "tx_pause_transition_prio_1",
174272407Shselasky	"rx_pause_prio_2", "rx_pause_duration_prio_2",
175272407Shselasky	"rx_pause_transition_prio_2", "tx_pause_prio_2",
176272407Shselasky	"tx_pause_duration_prio_2", "tx_pause_transition_prio_2",
177272407Shselasky	"rx_pause_prio_3", "rx_pause_duration_prio_3",
178272407Shselasky	"rx_pause_transition_prio_3", "tx_pause_prio_3",
179272407Shselasky	"tx_pause_duration_prio_3", "tx_pause_transition_prio_3",
180272407Shselasky	"rx_pause_prio_4", "rx_pause_duration_prio_4",
181272407Shselasky	"rx_pause_transition_prio_4", "tx_pause_prio_4",
182272407Shselasky	"tx_pause_duration_prio_4", "tx_pause_transition_prio_4",
183272407Shselasky	"rx_pause_prio_5", "rx_pause_duration_prio_5",
184272407Shselasky	"rx_pause_transition_prio_5", "tx_pause_prio_5",
185272407Shselasky	"tx_pause_duration_prio_5", "tx_pause_transition_prio_5",
186272407Shselasky	"rx_pause_prio_6", "rx_pause_duration_prio_6",
187272407Shselasky	"rx_pause_transition_prio_6", "tx_pause_prio_6",
188272407Shselasky	"tx_pause_duration_prio_6", "tx_pause_transition_prio_6",
189272407Shselasky	"rx_pause_prio_7", "rx_pause_duration_prio_7",
190272407Shselasky	"rx_pause_transition_prio_7", "tx_pause_prio_7",
191272407Shselasky	"tx_pause_duration_prio_7", "tx_pause_transition_prio_7",
192219820Sjeff
193272407Shselasky	/* VF statistics */
194272407Shselasky	"rx_packets",
195272407Shselasky	"rx_bytes",
196272407Shselasky	"rx_multicast_packets",
197272407Shselasky	"rx_broadcast_packets",
198272407Shselasky	"rx_errors",
199272407Shselasky	"rx_dropped",
200272407Shselasky	"tx_packets",
201272407Shselasky	"tx_bytes",
202272407Shselasky	"tx_multicast_packets",
203272407Shselasky	"tx_broadcast_packets",
204272407Shselasky	"tx_errors",
205219820Sjeff
206272407Shselasky	/* VPort statistics */
207272407Shselasky	"vport_rx_unicast_packets",
208272407Shselasky	"vport_rx_unicast_bytes",
209272407Shselasky	"vport_rx_multicast_packets",
210272407Shselasky	"vport_rx_multicast_bytes",
211272407Shselasky	"vport_rx_broadcast_packets",
212272407Shselasky	"vport_rx_broadcast_bytes",
213272407Shselasky	"vport_rx_dropped",
214272407Shselasky	"vport_rx_errors",
215272407Shselasky	"vport_tx_unicast_packets",
216272407Shselasky	"vport_tx_unicast_bytes",
217272407Shselasky	"vport_tx_multicast_packets",
218272407Shselasky	"vport_tx_multicast_bytes",
219272407Shselasky	"vport_tx_broadcast_packets",
220272407Shselasky	"vport_tx_broadcast_bytes",
221272407Shselasky	"vport_tx_errors",
222219820Sjeff
223219820Sjeff	/* port statistics */
224272407Shselasky	"tx_tso_packets",
225272407Shselasky	"tx_queue_stopped", "tx_wake_queue", "tx_timeout", "rx_alloc_failed",
226219820Sjeff	"rx_csum_good", "rx_csum_none", "tx_chksum_offload",
227219820Sjeff};
228219820Sjeff
229219820Sjeffstatic const char mlx4_en_test_names[][ETH_GSTRING_LEN]= {
230272407Shselasky	"Interrupt Test",
231219820Sjeff	"Link Test",
232219820Sjeff	"Speed Test",
233219820Sjeff	"Register Test",
234219820Sjeff	"Loopback Test",
235219820Sjeff};
236219820Sjeff
237219820Sjeffstatic u32 mlx4_en_get_msglevel(struct net_device *dev)
238219820Sjeff{
239219820Sjeff	return ((struct mlx4_en_priv *) netdev_priv(dev))->msg_enable;
240219820Sjeff}
241219820Sjeff
242219820Sjeffstatic void mlx4_en_set_msglevel(struct net_device *dev, u32 val)
243219820Sjeff{
244219820Sjeff	((struct mlx4_en_priv *) netdev_priv(dev))->msg_enable = val;
245219820Sjeff}
246219820Sjeff
247219820Sjeffstatic void mlx4_en_get_wol(struct net_device *netdev,
248219820Sjeff			    struct ethtool_wolinfo *wol)
249219820Sjeff{
250272407Shselasky	struct mlx4_en_priv *priv = netdev_priv(netdev);
251272407Shselasky	int err = 0;
252272407Shselasky	u64 config = 0;
253272407Shselasky	u64 mask;
254219820Sjeff
255272407Shselasky	if ((priv->port < 1) || (priv->port > 2)) {
256272407Shselasky		en_err(priv, "Failed to get WoL information\n");
257272407Shselasky		return;
258272407Shselasky	}
259272407Shselasky
260272407Shselasky	mask = (priv->port == 1) ? MLX4_DEV_CAP_FLAG_WOL_PORT1 :
261272407Shselasky		MLX4_DEV_CAP_FLAG_WOL_PORT2;
262272407Shselasky
263272407Shselasky	if (!(priv->mdev->dev->caps.flags & mask)) {
264272407Shselasky		wol->supported = 0;
265272407Shselasky		wol->wolopts = 0;
266272407Shselasky		return;
267272407Shselasky	}
268272407Shselasky
269272407Shselasky	err = mlx4_wol_read(priv->mdev->dev, &config, priv->port);
270272407Shselasky	if (err) {
271272407Shselasky		en_err(priv, "Failed to get WoL information\n");
272272407Shselasky		return;
273272407Shselasky	}
274272407Shselasky
275272407Shselasky	if (config & MLX4_EN_WOL_MAGIC)
276272407Shselasky		wol->supported = WAKE_MAGIC;
277272407Shselasky	else
278272407Shselasky		wol->supported = 0;
279272407Shselasky
280272407Shselasky	if (config & MLX4_EN_WOL_ENABLED)
281272407Shselasky		wol->wolopts = WAKE_MAGIC;
282272407Shselasky	else
283272407Shselasky		wol->wolopts = 0;
284219820Sjeff}
285219820Sjeff
286272407Shselaskystatic int mlx4_en_set_wol(struct net_device *netdev,
287272407Shselasky			    struct ethtool_wolinfo *wol)
288219820Sjeff{
289272407Shselasky	struct mlx4_en_priv *priv = netdev_priv(netdev);
290272407Shselasky	u64 config = 0;
291272407Shselasky	int err = 0;
292272407Shselasky	u64 mask;
293272407Shselasky
294272407Shselasky	if ((priv->port < 1) || (priv->port > 2))
295272407Shselasky		return -EOPNOTSUPP;
296272407Shselasky
297272407Shselasky	mask = (priv->port == 1) ? MLX4_DEV_CAP_FLAG_WOL_PORT1 :
298272407Shselasky		MLX4_DEV_CAP_FLAG_WOL_PORT2;
299272407Shselasky
300272407Shselasky	if (!(priv->mdev->dev->caps.flags & mask))
301272407Shselasky		return -EOPNOTSUPP;
302272407Shselasky
303272407Shselasky	if (wol->supported & ~WAKE_MAGIC)
304272407Shselasky		return -EINVAL;
305272407Shselasky
306272407Shselasky	err = mlx4_wol_read(priv->mdev->dev, &config, priv->port);
307272407Shselasky	if (err) {
308272407Shselasky		en_err(priv, "Failed to get WoL info, unable to modify\n");
309272407Shselasky		return err;
310272407Shselasky	}
311272407Shselasky
312272407Shselasky	if (wol->wolopts & WAKE_MAGIC) {
313272407Shselasky		config |= MLX4_EN_WOL_DO_MODIFY | MLX4_EN_WOL_ENABLED |
314272407Shselasky				MLX4_EN_WOL_MAGIC;
315272407Shselasky	} else {
316272407Shselasky		config &= ~(MLX4_EN_WOL_ENABLED | MLX4_EN_WOL_MAGIC);
317272407Shselasky		config |= MLX4_EN_WOL_DO_MODIFY;
318272407Shselasky	}
319272407Shselasky
320272407Shselasky	err = mlx4_wol_write(priv->mdev->dev, config, priv->port);
321272407Shselasky	if (err)
322272407Shselasky		en_err(priv, "Failed to set WoL information\n");
323272407Shselasky
324272407Shselasky	return err;
325272407Shselasky}
326272407Shselasky
327272407Shselaskystruct bitmap_sim_iterator {
328272407Shselasky	bool advance_array;
329272407Shselasky	unsigned long *stats_bitmap;
330272407Shselasky	unsigned int count;
331272407Shselasky	unsigned int j;
332272407Shselasky};
333272407Shselasky
334272407Shselaskystatic inline void bitmap_sim_iterator_init(struct bitmap_sim_iterator *h,
335272407Shselasky					    unsigned long *stats_bitmap,
336272407Shselasky					    int count)
337272407Shselasky{
338272407Shselasky	h->j = 0;
339272407Shselasky	h->advance_array = !bitmap_empty(stats_bitmap, count);
340272407Shselasky	h->count = h->advance_array ? bitmap_weight(stats_bitmap, count)
341272407Shselasky		: count;
342272407Shselasky	h->stats_bitmap = stats_bitmap;
343272407Shselasky}
344272407Shselasky
345272407Shselaskystatic inline int bitmap_sim_iterator_test(struct bitmap_sim_iterator *h)
346272407Shselasky{
347272407Shselasky	return !h->advance_array ? 1 : test_bit(h->j, h->stats_bitmap);
348272407Shselasky}
349272407Shselasky
350272407Shselaskystatic inline int bitmap_sim_iterator_inc(struct bitmap_sim_iterator *h)
351272407Shselasky{
352272407Shselasky	return h->j++;
353272407Shselasky}
354272407Shselasky
355272407Shselaskystatic inline unsigned int bitmap_sim_iterator_count(
356272407Shselasky		struct bitmap_sim_iterator *h)
357272407Shselasky{
358272407Shselasky	return h->count;
359272407Shselasky}
360272407Shselasky
361272407Shselaskyint mlx4_en_get_sset_count(struct net_device *dev, int sset)
362272407Shselasky{
363219820Sjeff	struct mlx4_en_priv *priv = netdev_priv(dev);
364272407Shselasky	struct bitmap_sim_iterator it;
365219820Sjeff
366272407Shselasky	int num_of_stats = NUM_ALL_STATS -
367272407Shselasky		((priv->mdev->dev->caps.flags2 &
368272407Shselasky		 MLX4_DEV_CAP_FLAG2_FLOWSTATS_EN) ? 0 : NUM_FLOW_STATS);
369272407Shselasky
370272407Shselasky	bitmap_sim_iterator_init(&it, priv->stats_bitmap, num_of_stats);
371272407Shselasky
372219820Sjeff	switch (sset) {
373219820Sjeff	case ETH_SS_STATS:
374272407Shselasky		return bitmap_sim_iterator_count(&it) +
375272407Shselasky			(priv->tx_ring_num * 2) +
376272407Shselasky#ifdef LL_EXTENDED_STATS
377272407Shselasky			(priv->rx_ring_num * 5);
378272407Shselasky#else
379272407Shselasky			(priv->rx_ring_num * 2);
380272407Shselasky#endif
381219820Sjeff	case ETH_SS_TEST:
382272407Shselasky		return MLX4_EN_NUM_SELF_TEST - !(priv->mdev->dev->caps.flags
383272407Shselasky					& MLX4_DEV_CAP_FLAG_UC_LOOPBACK) * 2;
384219820Sjeff	default:
385219820Sjeff		return -EOPNOTSUPP;
386219820Sjeff	}
387219820Sjeff}
388219820Sjeff
389272407Shselaskyvoid mlx4_en_get_ethtool_stats(struct net_device *dev,
390272407Shselasky		struct ethtool_stats *stats, u64 *data)
391219820Sjeff{
392219820Sjeff	struct mlx4_en_priv *priv = netdev_priv(dev);
393219820Sjeff	int index = 0;
394219820Sjeff	int i;
395272407Shselasky	struct bitmap_sim_iterator it;
396219820Sjeff
397272407Shselasky	int num_of_stats = NUM_ALL_STATS -
398272407Shselasky		((priv->mdev->dev->caps.flags2 &
399272407Shselasky		 MLX4_DEV_CAP_FLAG2_FLOWSTATS_EN) ? 0 : NUM_FLOW_STATS);
400272407Shselasky
401272407Shselasky	bitmap_sim_iterator_init(&it, priv->stats_bitmap, num_of_stats);
402272407Shselasky
403272407Shselasky	if (!data || !priv->port_up)
404272407Shselasky		return;
405272407Shselasky
406219820Sjeff	spin_lock_bh(&priv->stats_lock);
407219820Sjeff
408272407Shselasky	for (i = 0; i < NUM_PKT_STATS; i++,
409272407Shselasky			bitmap_sim_iterator_inc(&it))
410272407Shselasky		if (bitmap_sim_iterator_test(&it))
411272407Shselasky			data[index++] =
412272407Shselasky				((unsigned long *)&priv->pkstats)[i];
413272407Shselasky	for (i = 0; i < NUM_FLOW_STATS; i++,
414272407Shselasky			bitmap_sim_iterator_inc(&it))
415272407Shselasky		if (priv->mdev->dev->caps.flags2 &
416272407Shselasky		    MLX4_DEV_CAP_FLAG2_FLOWSTATS_EN)
417272407Shselasky			if (bitmap_sim_iterator_test(&it))
418272407Shselasky				data[index++] =
419272407Shselasky					((u64 *)&priv->flowstats)[i];
420272407Shselasky	for (i = 0; i < NUM_VF_STATS; i++,
421272407Shselasky			bitmap_sim_iterator_inc(&it))
422272407Shselasky		if (bitmap_sim_iterator_test(&it))
423272407Shselasky			data[index++] =
424272407Shselasky				((unsigned long *)&priv->vf_stats)[i];
425272407Shselasky	for (i = 0; i < NUM_VPORT_STATS; i++,
426272407Shselasky			bitmap_sim_iterator_inc(&it))
427272407Shselasky		if (bitmap_sim_iterator_test(&it))
428272407Shselasky			data[index++] =
429272407Shselasky				((unsigned long *)&priv->vport_stats)[i];
430272407Shselasky	for (i = 0; i < NUM_PORT_STATS; i++,
431272407Shselasky			bitmap_sim_iterator_inc(&it))
432272407Shselasky		if (bitmap_sim_iterator_test(&it))
433272407Shselasky			data[index++] =
434272407Shselasky				((unsigned long *)&priv->port_stats)[i];
435219820Sjeff
436219820Sjeff	for (i = 0; i < priv->tx_ring_num; i++) {
437272407Shselasky		data[index++] = priv->tx_ring[i]->packets;
438272407Shselasky		data[index++] = priv->tx_ring[i]->bytes;
439219820Sjeff	}
440219820Sjeff	for (i = 0; i < priv->rx_ring_num; i++) {
441272407Shselasky		data[index++] = priv->rx_ring[i]->packets;
442272407Shselasky		data[index++] = priv->rx_ring[i]->bytes;
443272407Shselasky#ifdef LL_EXTENDED_STATS
444272407Shselasky		data[index++] = priv->rx_ring[i]->yields;
445272407Shselasky		data[index++] = priv->rx_ring[i]->misses;
446272407Shselasky		data[index++] = priv->rx_ring[i]->cleaned;
447272407Shselasky#endif
448219820Sjeff	}
449219820Sjeff	spin_unlock_bh(&priv->stats_lock);
450219820Sjeff
451219820Sjeff}
452219820Sjeff
453272407Shselaskyvoid mlx4_en_restore_ethtool_stats(struct mlx4_en_priv *priv, u64 *data)
454272407Shselasky{
455272407Shselasky	int index = 0;
456272407Shselasky	int i;
457272407Shselasky	struct bitmap_sim_iterator it;
458272407Shselasky
459272407Shselasky	int num_of_stats = NUM_ALL_STATS -
460272407Shselasky		((priv->mdev->dev->caps.flags2 &
461272407Shselasky		 MLX4_DEV_CAP_FLAG2_FLOWSTATS_EN) ? 0 : NUM_FLOW_STATS);
462272407Shselasky
463272407Shselasky	bitmap_sim_iterator_init(&it, priv->stats_bitmap, num_of_stats);
464272407Shselasky
465272407Shselasky	if (!data || !priv->port_up)
466272407Shselasky		return;
467272407Shselasky
468272407Shselasky	spin_lock_bh(&priv->stats_lock);
469272407Shselasky
470272407Shselasky	for (i = 0; i < NUM_PKT_STATS; i++,
471272407Shselasky	      bitmap_sim_iterator_inc(&it))
472272407Shselasky			if (bitmap_sim_iterator_test(&it))
473272407Shselasky				((unsigned long *)&priv->pkstats)[i] =
474272407Shselasky					data[index++];
475272407Shselasky	for (i = 0; i < NUM_FLOW_STATS; i++,
476272407Shselasky	      bitmap_sim_iterator_inc(&it))
477272407Shselasky		if (priv->mdev->dev->caps.flags2 &
478272407Shselasky		    MLX4_DEV_CAP_FLAG2_FLOWSTATS_EN)
479272407Shselasky			if (bitmap_sim_iterator_test(&it))
480272407Shselasky				((u64 *)&priv->flowstats)[i] =
481272407Shselasky					 data[index++];
482272407Shselasky	for (i = 0; i < NUM_VF_STATS; i++,
483272407Shselasky	      bitmap_sim_iterator_inc(&it))
484272407Shselasky			if (bitmap_sim_iterator_test(&it))
485272407Shselasky				((unsigned long *)&priv->vf_stats)[i] =
486272407Shselasky					data[index++];
487272407Shselasky	for (i = 0; i < NUM_VPORT_STATS; i++,
488272407Shselasky	      bitmap_sim_iterator_inc(&it))
489272407Shselasky			if (bitmap_sim_iterator_test(&it))
490272407Shselasky				((unsigned long *)&priv->vport_stats)[i] =
491272407Shselasky					data[index++];
492272407Shselasky	for (i = 0; i < NUM_PORT_STATS; i++,
493272407Shselasky	      bitmap_sim_iterator_inc(&it))
494272407Shselasky			if (bitmap_sim_iterator_test(&it))
495272407Shselasky				((unsigned long *)&priv->port_stats)[i] =
496272407Shselasky					data[index++];
497272407Shselasky
498272407Shselasky	for (i = 0; i < priv->tx_ring_num; i++) {
499272407Shselasky		priv->tx_ring[i]->packets = data[index++];
500272407Shselasky		priv->tx_ring[i]->bytes = data[index++];
501272407Shselasky	}
502272407Shselasky	for (i = 0; i < priv->rx_ring_num; i++) {
503272407Shselasky		priv->rx_ring[i]->packets = data[index++];
504272407Shselasky		priv->rx_ring[i]->bytes = data[index++];
505272407Shselasky	}
506272407Shselasky	spin_unlock_bh(&priv->stats_lock);
507272407Shselasky}
508272407Shselasky
509219820Sjeffstatic void mlx4_en_self_test(struct net_device *dev,
510219820Sjeff			      struct ethtool_test *etest, u64 *buf)
511219820Sjeff{
512219820Sjeff	mlx4_en_ex_selftest(dev, &etest->flags, buf);
513219820Sjeff}
514219820Sjeff
515219820Sjeffstatic void mlx4_en_get_strings(struct net_device *dev,
516219820Sjeff				uint32_t stringset, uint8_t *data)
517219820Sjeff{
518219820Sjeff	struct mlx4_en_priv *priv = netdev_priv(dev);
519219820Sjeff	int index = 0;
520272407Shselasky	int i, k;
521272407Shselasky	struct bitmap_sim_iterator it;
522219820Sjeff
523272407Shselasky	int num_of_stats = NUM_ALL_STATS -
524272407Shselasky		((priv->mdev->dev->caps.flags2 &
525272407Shselasky		 MLX4_DEV_CAP_FLAG2_FLOWSTATS_EN) ? 0 : NUM_FLOW_STATS);
526272407Shselasky
527272407Shselasky	bitmap_sim_iterator_init(&it, priv->stats_bitmap, num_of_stats);
528272407Shselasky
529219820Sjeff	switch (stringset) {
530219820Sjeff	case ETH_SS_TEST:
531219820Sjeff		for (i = 0; i < MLX4_EN_NUM_SELF_TEST - 2; i++)
532219820Sjeff			strcpy(data + i * ETH_GSTRING_LEN, mlx4_en_test_names[i]);
533272407Shselasky		if (priv->mdev->dev->caps.flags & MLX4_DEV_CAP_FLAG_UC_LOOPBACK)
534219820Sjeff			for (; i < MLX4_EN_NUM_SELF_TEST; i++)
535219820Sjeff				strcpy(data + i * ETH_GSTRING_LEN, mlx4_en_test_names[i]);
536219820Sjeff		break;
537219820Sjeff
538219820Sjeff	case ETH_SS_STATS:
539219820Sjeff		/* Add main counters */
540272407Shselasky		for (i = 0; i < NUM_PKT_STATS; i++,
541272407Shselasky		     bitmap_sim_iterator_inc(&it))
542272407Shselasky			if (bitmap_sim_iterator_test(&it))
543272407Shselasky				strcpy(data + (index++) * ETH_GSTRING_LEN,
544272407Shselasky				       main_strings[i]);
545272407Shselasky
546272407Shselasky		for (k = 0; k < NUM_FLOW_STATS; k++,
547272407Shselasky		     bitmap_sim_iterator_inc(&it))
548272407Shselasky			if (priv->mdev->dev->caps.flags2 &
549272407Shselasky			    MLX4_DEV_CAP_FLAG2_FLOWSTATS_EN)
550272407Shselasky				if (bitmap_sim_iterator_test(&it))
551272407Shselasky					strcpy(data + (index++) *
552272407Shselasky					       ETH_GSTRING_LEN,
553272407Shselasky						main_strings[i + k]);
554272407Shselasky
555272407Shselasky		for (; (i + k) < num_of_stats; i++,
556272407Shselasky		     bitmap_sim_iterator_inc(&it))
557272407Shselasky			if (bitmap_sim_iterator_test(&it))
558272407Shselasky				strcpy(data + (index++) * ETH_GSTRING_LEN,
559272407Shselasky				       main_strings[i + k]);
560272407Shselasky
561219820Sjeff		for (i = 0; i < priv->tx_ring_num; i++) {
562219820Sjeff			sprintf(data + (index++) * ETH_GSTRING_LEN,
563219820Sjeff				"tx%d_packets", i);
564219820Sjeff			sprintf(data + (index++) * ETH_GSTRING_LEN,
565219820Sjeff				"tx%d_bytes", i);
566219820Sjeff		}
567219820Sjeff		for (i = 0; i < priv->rx_ring_num; i++) {
568219820Sjeff			sprintf(data + (index++) * ETH_GSTRING_LEN,
569219820Sjeff				"rx%d_packets", i);
570219820Sjeff			sprintf(data + (index++) * ETH_GSTRING_LEN,
571219820Sjeff				"rx%d_bytes", i);
572272407Shselasky#ifdef LL_EXTENDED_STATS
573272407Shselasky			sprintf(data + (index++) * ETH_GSTRING_LEN,
574272407Shselasky				"rx%d_napi_yield", i);
575272407Shselasky			sprintf(data + (index++) * ETH_GSTRING_LEN,
576272407Shselasky				"rx%d_misses", i);
577272407Shselasky			sprintf(data + (index++) * ETH_GSTRING_LEN,
578272407Shselasky				"rx%d_cleaned", i);
579272407Shselasky#endif
580219820Sjeff		}
581219820Sjeff		break;
582219820Sjeff	}
583219820Sjeff}
584219820Sjeff
585272407Shselaskystatic u32 mlx4_en_autoneg_get(struct net_device *dev)
586272407Shselasky{
587272407Shselasky	struct mlx4_en_priv *priv = netdev_priv(dev);
588272407Shselasky	struct mlx4_en_dev *mdev = priv->mdev;
589272407Shselasky	u32 autoneg = AUTONEG_DISABLE;
590272407Shselasky
591272407Shselasky	if ((mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_ETH_BACKPL_AN_REP) &&
592272407Shselasky	    priv->port_state.autoneg) {
593272407Shselasky		autoneg = AUTONEG_ENABLE;
594272407Shselasky	}
595272407Shselasky
596272407Shselasky	return autoneg;
597272407Shselasky}
598272407Shselasky
599219820Sjeffstatic int mlx4_en_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
600219820Sjeff{
601219820Sjeff	struct mlx4_en_priv *priv = netdev_priv(dev);
602219820Sjeff	int trans_type;
603219820Sjeff
604272407Shselasky	/* SUPPORTED_1000baseT_Half isn't supported */
605272407Shselasky	cmd->supported = SUPPORTED_1000baseT_Full
606272407Shselasky			|SUPPORTED_10000baseT_Full;
607219820Sjeff
608272407Shselasky	cmd->advertising = ADVERTISED_1000baseT_Full
609272407Shselasky			  |ADVERTISED_10000baseT_Full;
610272407Shselasky
611272407Shselasky	cmd->supported |= SUPPORTED_1000baseKX_Full
612272407Shselasky			|SUPPORTED_10000baseKX4_Full
613272407Shselasky			|SUPPORTED_10000baseKR_Full
614272407Shselasky			|SUPPORTED_10000baseR_FEC
615272407Shselasky			|SUPPORTED_40000baseKR4_Full
616272407Shselasky			|SUPPORTED_40000baseCR4_Full
617272407Shselasky			|SUPPORTED_40000baseSR4_Full
618272407Shselasky			|SUPPORTED_40000baseLR4_Full;
619272407Shselasky
620272407Shselasky	/* ADVERTISED_1000baseT_Half isn't advertised */
621272407Shselasky	cmd->advertising |= ADVERTISED_1000baseKX_Full
622272407Shselasky			  |ADVERTISED_10000baseKX4_Full
623272407Shselasky			  |ADVERTISED_10000baseKR_Full
624272407Shselasky			  |ADVERTISED_10000baseR_FEC
625272407Shselasky			  |ADVERTISED_40000baseKR4_Full
626272407Shselasky			  |ADVERTISED_40000baseCR4_Full
627272407Shselasky			  |ADVERTISED_40000baseSR4_Full
628272407Shselasky			  |ADVERTISED_40000baseLR4_Full;
629272407Shselasky
630219820Sjeff	if (mlx4_en_QUERY_PORT(priv->mdev, priv->port))
631219820Sjeff		return -ENOMEM;
632219820Sjeff
633272407Shselasky	cmd->autoneg = mlx4_en_autoneg_get(dev);
634272407Shselasky	if (cmd->autoneg == AUTONEG_ENABLE) {
635272407Shselasky		cmd->supported |= SUPPORTED_Autoneg;
636272407Shselasky		cmd->advertising |= ADVERTISED_Autoneg;
637272407Shselasky	}
638272407Shselasky
639219820Sjeff	trans_type = priv->port_state.transciver;
640219820Sjeff	if (netif_carrier_ok(dev)) {
641272407Shselasky		ethtool_cmd_speed_set(cmd, priv->port_state.link_speed);
642219820Sjeff		cmd->duplex = DUPLEX_FULL;
643219820Sjeff	} else {
644272407Shselasky		ethtool_cmd_speed_set(cmd, -1);
645219820Sjeff		cmd->duplex = -1;
646219820Sjeff	}
647219820Sjeff
648219820Sjeff	if (trans_type > 0 && trans_type <= 0xC) {
649219820Sjeff		cmd->port = PORT_FIBRE;
650219820Sjeff		cmd->transceiver = XCVR_EXTERNAL;
651219820Sjeff		cmd->supported |= SUPPORTED_FIBRE;
652219820Sjeff		cmd->advertising |= ADVERTISED_FIBRE;
653219820Sjeff	} else if (trans_type == 0x80 || trans_type == 0) {
654219820Sjeff		cmd->port = PORT_TP;
655219820Sjeff		cmd->transceiver = XCVR_INTERNAL;
656219820Sjeff		cmd->supported |= SUPPORTED_TP;
657219820Sjeff		cmd->advertising |= ADVERTISED_TP;
658219820Sjeff	} else  {
659219820Sjeff		cmd->port = -1;
660219820Sjeff		cmd->transceiver = -1;
661219820Sjeff	}
662219820Sjeff	return 0;
663219820Sjeff}
664219820Sjeff
665272407Shselaskystatic const char *mlx4_en_duplex_to_string(int duplex)
666272407Shselasky{
667272407Shselasky	switch (duplex) {
668272407Shselasky	case DUPLEX_FULL:
669272407Shselasky		return "FULL";
670272407Shselasky	case DUPLEX_HALF:
671272407Shselasky		return "HALF";
672272407Shselasky	default:
673272407Shselasky		break;
674272407Shselasky	}
675272407Shselasky	return "UNKNOWN";
676272407Shselasky}
677272407Shselasky
678219820Sjeffstatic int mlx4_en_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
679219820Sjeff{
680272407Shselasky	struct mlx4_en_priv *priv = netdev_priv(dev);
681272407Shselasky	struct mlx4_en_port_state *port_state = &priv->port_state;
682219820Sjeff
683272407Shselasky	if ((cmd->autoneg != port_state->autoneg) ||
684272407Shselasky	    (ethtool_cmd_speed(cmd) != port_state->link_speed) ||
685272407Shselasky	    (cmd->duplex != DUPLEX_FULL)) {
686272407Shselasky		en_info(priv, "Changing port state properties (auto-negotiation"
687272407Shselasky			      " , speed/duplex) is not supported. Current:"
688272407Shselasky			      " auto-negotiation=%d speed/duplex=%d/%s\n",
689272407Shselasky			      port_state->autoneg, port_state->link_speed,
690272407Shselasky			      mlx4_en_duplex_to_string(DUPLEX_FULL));
691272407Shselasky		return -EOPNOTSUPP;
692272407Shselasky	}
693272407Shselasky
694272407Shselasky	/* User provided same port state properties that are currently set.
695272407Shselasky	 * Nothing to change
696272407Shselasky	 */
697219820Sjeff	return 0;
698219820Sjeff}
699219820Sjeff
700219820Sjeffstatic int mlx4_en_get_coalesce(struct net_device *dev,
701219820Sjeff			      struct ethtool_coalesce *coal)
702219820Sjeff{
703219820Sjeff	struct mlx4_en_priv *priv = netdev_priv(dev);
704219820Sjeff
705272407Shselasky	coal->tx_coalesce_usecs = priv->tx_usecs;
706272407Shselasky	coal->tx_max_coalesced_frames = priv->tx_frames;
707219820Sjeff	coal->rx_coalesce_usecs = priv->rx_usecs;
708219820Sjeff	coal->rx_max_coalesced_frames = priv->rx_frames;
709219820Sjeff
710219820Sjeff	coal->pkt_rate_low = priv->pkt_rate_low;
711219820Sjeff	coal->rx_coalesce_usecs_low = priv->rx_usecs_low;
712219820Sjeff	coal->pkt_rate_high = priv->pkt_rate_high;
713219820Sjeff	coal->rx_coalesce_usecs_high = priv->rx_usecs_high;
714219820Sjeff	coal->rate_sample_interval = priv->sample_interval;
715219820Sjeff	coal->use_adaptive_rx_coalesce = priv->adaptive_rx_coal;
716219820Sjeff	return 0;
717219820Sjeff}
718219820Sjeff
719219820Sjeffstatic int mlx4_en_set_coalesce(struct net_device *dev,
720219820Sjeff			      struct ethtool_coalesce *coal)
721219820Sjeff{
722219820Sjeff	struct mlx4_en_priv *priv = netdev_priv(dev);
723219820Sjeff	int err, i;
724219820Sjeff
725219820Sjeff	priv->rx_frames = (coal->rx_max_coalesced_frames ==
726219820Sjeff			   MLX4_EN_AUTO_CONF) ?
727219820Sjeff				MLX4_EN_RX_COAL_TARGET /
728219820Sjeff				priv->dev->mtu + 1 :
729219820Sjeff				coal->rx_max_coalesced_frames;
730219820Sjeff	priv->rx_usecs = (coal->rx_coalesce_usecs ==
731219820Sjeff			  MLX4_EN_AUTO_CONF) ?
732219820Sjeff				MLX4_EN_RX_COAL_TIME :
733219820Sjeff				coal->rx_coalesce_usecs;
734219820Sjeff
735272407Shselasky	/* Setting TX coalescing parameters */
736272407Shselasky	if (coal->tx_coalesce_usecs != priv->tx_usecs ||
737272407Shselasky	    coal->tx_max_coalesced_frames != priv->tx_frames) {
738272407Shselasky		priv->tx_usecs = coal->tx_coalesce_usecs;
739272407Shselasky		priv->tx_frames = coal->tx_max_coalesced_frames;
740272407Shselasky		if (priv->port_up) {
741272407Shselasky			for (i = 0; i < priv->tx_ring_num; i++) {
742272407Shselasky				priv->tx_cq[i]->moder_cnt = priv->tx_frames;
743272407Shselasky				priv->tx_cq[i]->moder_time = priv->tx_usecs;
744272407Shselasky				if (mlx4_en_set_cq_moder(priv, priv->tx_cq[i]))
745272407Shselasky					en_warn(priv, "Failed changing moderation for TX cq %d\n", i);
746272407Shselasky			}
747272407Shselasky		}
748272407Shselasky	}
749272407Shselasky
750219820Sjeff	/* Set adaptive coalescing params */
751219820Sjeff	priv->pkt_rate_low = coal->pkt_rate_low;
752219820Sjeff	priv->rx_usecs_low = coal->rx_coalesce_usecs_low;
753219820Sjeff	priv->pkt_rate_high = coal->pkt_rate_high;
754219820Sjeff	priv->rx_usecs_high = coal->rx_coalesce_usecs_high;
755219820Sjeff	priv->sample_interval = coal->rate_sample_interval;
756219820Sjeff	priv->adaptive_rx_coal = coal->use_adaptive_rx_coalesce;
757219820Sjeff	if (priv->adaptive_rx_coal)
758219820Sjeff		return 0;
759219820Sjeff
760272407Shselasky	if (priv->port_up) {
761272407Shselasky		for (i = 0; i < priv->rx_ring_num; i++) {
762272407Shselasky			priv->rx_cq[i]->moder_cnt = priv->rx_frames;
763272407Shselasky			priv->rx_cq[i]->moder_time = priv->rx_usecs;
764272407Shselasky			priv->last_moder_time[i] = MLX4_EN_AUTO_CONF;
765272407Shselasky				err = mlx4_en_set_cq_moder(priv, priv->rx_cq[i]);
766272407Shselasky			if (err)
767272407Shselasky				return err;
768272407Shselasky		}
769219820Sjeff	}
770272407Shselasky
771219820Sjeff	return 0;
772219820Sjeff}
773219820Sjeff
774219820Sjeffstatic int mlx4_en_set_pauseparam(struct net_device *dev,
775219820Sjeff				struct ethtool_pauseparam *pause)
776219820Sjeff{
777219820Sjeff	struct mlx4_en_priv *priv = netdev_priv(dev);
778219820Sjeff	struct mlx4_en_dev *mdev = priv->mdev;
779219820Sjeff	int err;
780219820Sjeff
781272407Shselasky	if (pause->autoneg)
782272407Shselasky		return -EOPNOTSUPP;
783272407Shselasky
784219820Sjeff	priv->prof->tx_pause = pause->tx_pause != 0;
785219820Sjeff	priv->prof->rx_pause = pause->rx_pause != 0;
786219820Sjeff	err = mlx4_SET_PORT_general(mdev->dev, priv->port,
787272407Shselasky				    priv->rx_skb_size + ETH_FCS_LEN,
788219820Sjeff				    priv->prof->tx_pause,
789219820Sjeff				    priv->prof->tx_ppp,
790219820Sjeff				    priv->prof->rx_pause,
791219820Sjeff				    priv->prof->rx_ppp);
792219820Sjeff	if (err)
793219820Sjeff		en_err(priv, "Failed setting pause params\n");
794219820Sjeff
795219820Sjeff	return err;
796219820Sjeff}
797219820Sjeff
798219820Sjeffstatic void mlx4_en_get_pauseparam(struct net_device *dev,
799219820Sjeff				 struct ethtool_pauseparam *pause)
800219820Sjeff{
801219820Sjeff	struct mlx4_en_priv *priv = netdev_priv(dev);
802219820Sjeff
803219820Sjeff	pause->tx_pause = priv->prof->tx_pause;
804219820Sjeff	pause->rx_pause = priv->prof->rx_pause;
805272407Shselasky	pause->autoneg = mlx4_en_autoneg_get(dev);
806219820Sjeff}
807219820Sjeff
808272407Shselasky/* rtnl lock must be taken before calling */
809272407Shselaskyint mlx4_en_pre_config(struct mlx4_en_priv *priv)
810272407Shselasky{
811272407Shselasky#ifdef CONFIG_RFS_ACCEL
812272407Shselasky	struct cpu_rmap *rmap;
813272407Shselasky
814272407Shselasky	if (!priv->dev->rx_cpu_rmap)
815272407Shselasky		return 0;
816272407Shselasky
817272407Shselasky	/* Disable RFS events
818272407Shselasky	 * Must have all RFS jobs flushed before freeing resources
819272407Shselasky	 */
820272407Shselasky	rmap = priv->dev->rx_cpu_rmap;
821272407Shselasky	priv->dev->rx_cpu_rmap = NULL;
822272407Shselasky
823272407Shselasky	rtnl_unlock();
824272407Shselasky	free_irq_cpu_rmap(rmap);
825272407Shselasky	rtnl_lock();
826272407Shselasky
827272407Shselasky	if (priv->dev->rx_cpu_rmap)
828272407Shselasky		return -EBUSY; /* another configuration completed while lock
829272407Shselasky				* was free
830272407Shselasky				*/
831272407Shselasky
832272407Shselasky	/* Make sure all currently running filter_work are being processed
833272407Shselasky	 * Other work will return immediatly because of disable_rfs
834272407Shselasky	 */
835272407Shselasky	flush_workqueue(priv->mdev->workqueue);
836272407Shselasky
837272407Shselasky#endif
838272407Shselasky
839272407Shselasky	return 0;
840272407Shselasky}
841272407Shselasky
842219820Sjeffstatic int mlx4_en_set_ringparam(struct net_device *dev,
843219820Sjeff				 struct ethtool_ringparam *param)
844219820Sjeff{
845219820Sjeff	struct mlx4_en_priv *priv = netdev_priv(dev);
846219820Sjeff	struct mlx4_en_dev *mdev = priv->mdev;
847219820Sjeff	u32 rx_size, tx_size;
848219820Sjeff	int port_up = 0;
849219820Sjeff	int err = 0;
850272407Shselasky	int i, n_stats;
851272407Shselasky	u64 *data = NULL;
852219820Sjeff
853272407Shselasky	if (!priv->port_up)
854272407Shselasky		return -ENOMEM;
855272407Shselasky
856219820Sjeff	if (param->rx_jumbo_pending || param->rx_mini_pending)
857219820Sjeff		return -EINVAL;
858219820Sjeff
859219820Sjeff	rx_size = roundup_pow_of_two(param->rx_pending);
860219820Sjeff	rx_size = max_t(u32, rx_size, MLX4_EN_MIN_RX_SIZE);
861219820Sjeff	rx_size = min_t(u32, rx_size, MLX4_EN_MAX_RX_SIZE);
862219820Sjeff	tx_size = roundup_pow_of_two(param->tx_pending);
863219820Sjeff	tx_size = max_t(u32, tx_size, MLX4_EN_MIN_TX_SIZE);
864219820Sjeff	tx_size = min_t(u32, tx_size, MLX4_EN_MAX_TX_SIZE);
865219820Sjeff
866272407Shselasky	if (rx_size == (priv->port_up ? priv->rx_ring[0]->actual_size :
867272407Shselasky					priv->rx_ring[0]->size) &&
868272407Shselasky	    tx_size == priv->tx_ring[0]->size)
869219820Sjeff		return 0;
870272407Shselasky	err = mlx4_en_pre_config(priv);
871272407Shselasky	if (err)
872272407Shselasky		return err;
873219820Sjeff
874219820Sjeff	mutex_lock(&mdev->state_lock);
875219820Sjeff	if (priv->port_up) {
876219820Sjeff		port_up = 1;
877219820Sjeff		mlx4_en_stop_port(dev);
878219820Sjeff	}
879219820Sjeff
880272407Shselasky	/* Cache port statistics */
881272407Shselasky	n_stats = mlx4_en_get_sset_count(dev, ETH_SS_STATS);
882272407Shselasky	if (n_stats > 0) {
883272407Shselasky		data = kmalloc(n_stats * sizeof(u64), GFP_KERNEL);
884272407Shselasky		if (data)
885272407Shselasky			mlx4_en_get_ethtool_stats(dev, NULL, data);
886272407Shselasky	}
887272407Shselasky
888219820Sjeff	mlx4_en_free_resources(priv);
889219820Sjeff
890219820Sjeff	priv->prof->tx_ring_size = tx_size;
891219820Sjeff	priv->prof->rx_ring_size = rx_size;
892219820Sjeff
893219820Sjeff	err = mlx4_en_alloc_resources(priv);
894219820Sjeff	if (err) {
895219820Sjeff		en_err(priv, "Failed reallocating port resources\n");
896219820Sjeff		goto out;
897219820Sjeff	}
898272407Shselasky
899272407Shselasky	/* Restore port statistics */
900272407Shselasky	if (n_stats > 0 && data)
901272407Shselasky		mlx4_en_restore_ethtool_stats(priv, data);
902272407Shselasky
903219820Sjeff	if (port_up) {
904219820Sjeff		err = mlx4_en_start_port(dev);
905272407Shselasky		if (err) {
906219820Sjeff			en_err(priv, "Failed starting port\n");
907272407Shselasky			goto out;
908272407Shselasky		}
909219820Sjeff
910272407Shselasky		for (i = 0; i < priv->rx_ring_num; i++) {
911272407Shselasky			priv->rx_cq[i]->moder_cnt = priv->rx_frames;
912272407Shselasky			priv->rx_cq[i]->moder_time = priv->rx_usecs;
913272407Shselasky			priv->last_moder_time[i] = MLX4_EN_AUTO_CONF;
914272407Shselasky			err = mlx4_en_set_cq_moder(priv, priv->rx_cq[i]);
915272407Shselasky			if (err)
916272407Shselasky				goto out;
917272407Shselasky		}
918257867Salfred	}
919257867Salfred
920219820Sjeffout:
921272407Shselasky	kfree(data);
922219820Sjeff	mutex_unlock(&mdev->state_lock);
923219820Sjeff	return err;
924219820Sjeff}
925219820Sjeff
926219820Sjeffstatic void mlx4_en_get_ringparam(struct net_device *dev,
927219820Sjeff				  struct ethtool_ringparam *param)
928219820Sjeff{
929219820Sjeff	struct mlx4_en_priv *priv = netdev_priv(dev);
930219820Sjeff
931272407Shselasky	if (!priv->port_up)
932272407Shselasky		return;
933272407Shselasky
934219820Sjeff	memset(param, 0, sizeof(*param));
935219820Sjeff	param->rx_max_pending = MLX4_EN_MAX_RX_SIZE;
936219820Sjeff	param->tx_max_pending = MLX4_EN_MAX_TX_SIZE;
937219820Sjeff	param->rx_pending = priv->port_up ?
938272407Shselasky		priv->rx_ring[0]->actual_size : priv->rx_ring[0]->size;
939272407Shselasky	param->tx_pending = priv->tx_ring[0]->size;
940219820Sjeff}
941219820Sjeff
942272407Shselaskystatic u32 mlx4_en_get_rxfh_indir_size(struct net_device *dev)
943272407Shselasky{
944272407Shselasky	struct mlx4_en_priv *priv = netdev_priv(dev);
945272407Shselasky
946272407Shselasky	return priv->rx_ring_num;
947272407Shselasky}
948272407Shselasky
949272407Shselaskystatic int mlx4_en_get_rxfh_indir(struct net_device *dev, u32 *ring_index)
950272407Shselasky{
951272407Shselasky	struct mlx4_en_priv *priv = netdev_priv(dev);
952272407Shselasky	struct mlx4_en_rss_map *rss_map = &priv->rss_map;
953272407Shselasky	int rss_rings;
954272407Shselasky	size_t n = priv->rx_ring_num;
955272407Shselasky	int err = 0;
956272407Shselasky
957272407Shselasky	rss_rings = priv->prof->rss_rings ?: priv->rx_ring_num;
958272407Shselasky	rss_rings = 1 << ilog2(rss_rings);
959272407Shselasky
960272407Shselasky	while (n--) {
961272407Shselasky		ring_index[n] = rss_map->qps[n % rss_rings].qpn -
962272407Shselasky			rss_map->base_qpn;
963272407Shselasky	}
964272407Shselasky
965272407Shselasky	return err;
966272407Shselasky}
967272407Shselasky
968272407Shselaskystatic int mlx4_en_set_rxfh_indir(struct net_device *dev,
969272407Shselasky		const u32 *ring_index)
970272407Shselasky{
971272407Shselasky	struct mlx4_en_priv *priv = netdev_priv(dev);
972272407Shselasky	struct mlx4_en_dev *mdev = priv->mdev;
973272407Shselasky	int port_up = 0;
974272407Shselasky	int err = 0;
975272407Shselasky	int i;
976272407Shselasky	int rss_rings = 0;
977272407Shselasky
978272407Shselasky	/* Calculate RSS table size and make sure flows are spread evenly
979272407Shselasky	 * between rings
980272407Shselasky	 */
981272407Shselasky	for (i = 0; i < priv->rx_ring_num; i++) {
982272407Shselasky		if (i > 0 && !ring_index[i] && !rss_rings)
983272407Shselasky			rss_rings = i;
984272407Shselasky
985272407Shselasky		if (ring_index[i] != (i % (rss_rings ?: priv->rx_ring_num)))
986272407Shselasky			return -EINVAL;
987272407Shselasky	}
988272407Shselasky
989272407Shselasky	if (!rss_rings)
990272407Shselasky		rss_rings = priv->rx_ring_num;
991272407Shselasky
992272407Shselasky	/* RSS table size must be an order of 2 */
993272407Shselasky	if (!is_power_of_2(rss_rings))
994272407Shselasky		return -EINVAL;
995272407Shselasky
996272407Shselasky	mutex_lock(&mdev->state_lock);
997272407Shselasky	if (priv->port_up) {
998272407Shselasky		port_up = 1;
999272407Shselasky		mlx4_en_stop_port(dev);
1000272407Shselasky	}
1001272407Shselasky
1002272407Shselasky	priv->prof->rss_rings = rss_rings;
1003272407Shselasky
1004272407Shselasky	if (port_up) {
1005272407Shselasky		err = mlx4_en_start_port(dev);
1006272407Shselasky		if (err)
1007272407Shselasky			en_err(priv, "Failed starting port\n");
1008272407Shselasky	}
1009272407Shselasky
1010272407Shselasky	mutex_unlock(&mdev->state_lock);
1011272407Shselasky	return err;
1012272407Shselasky}
1013272407Shselasky
1014272407Shselasky#define all_zeros_or_all_ones(field)		\
1015272407Shselasky	((field) == 0 || (field) == (__force typeof(field))-1)
1016272407Shselasky
1017272407Shselaskystatic int mlx4_en_validate_flow(struct net_device *dev,
1018272407Shselasky				 struct mlx4_ethtool_rxnfc *cmd)
1019272407Shselasky{
1020272407Shselasky	struct ethtool_usrip4_spec *l3_mask;
1021272407Shselasky	struct ethtool_tcpip4_spec *l4_mask;
1022272407Shselasky	struct ethhdr *eth_mask;
1023272407Shselasky
1024272407Shselasky	if (cmd->fs.location >= MAX_NUM_OF_FS_RULES)
1025272407Shselasky		return -EINVAL;
1026272407Shselasky
1027272407Shselasky	if (cmd->fs.flow_type & FLOW_MAC_EXT) {
1028272407Shselasky		/* dest mac mask must be ff:ff:ff:ff:ff:ff */
1029272407Shselasky		if (!is_broadcast_ether_addr(cmd->fs.m_ext.h_dest))
1030272407Shselasky			return -EINVAL;
1031272407Shselasky	}
1032272407Shselasky
1033272407Shselasky	switch (cmd->fs.flow_type & ~(FLOW_EXT | FLOW_MAC_EXT)) {
1034272407Shselasky	case TCP_V4_FLOW:
1035272407Shselasky	case UDP_V4_FLOW:
1036272407Shselasky		if (cmd->fs.m_u.tcp_ip4_spec.tos)
1037272407Shselasky			return -EINVAL;
1038272407Shselasky		l4_mask = &cmd->fs.m_u.tcp_ip4_spec;
1039272407Shselasky		/* don't allow mask which isn't all 0 or 1 */
1040272407Shselasky		if (!all_zeros_or_all_ones(l4_mask->ip4src) ||
1041272407Shselasky		    !all_zeros_or_all_ones(l4_mask->ip4dst) ||
1042272407Shselasky		    !all_zeros_or_all_ones(l4_mask->psrc) ||
1043272407Shselasky		    !all_zeros_or_all_ones(l4_mask->pdst))
1044272407Shselasky			return -EINVAL;
1045272407Shselasky		break;
1046272407Shselasky	case IP_USER_FLOW:
1047272407Shselasky		l3_mask = &cmd->fs.m_u.usr_ip4_spec;
1048272407Shselasky		if (l3_mask->l4_4_bytes || l3_mask->tos || l3_mask->proto ||
1049272407Shselasky		    cmd->fs.h_u.usr_ip4_spec.ip_ver != ETH_RX_NFC_IP4 ||
1050272407Shselasky		    (!l3_mask->ip4src && !l3_mask->ip4dst) ||
1051272407Shselasky		    !all_zeros_or_all_ones(l3_mask->ip4src) ||
1052272407Shselasky		    !all_zeros_or_all_ones(l3_mask->ip4dst))
1053272407Shselasky			return -EINVAL;
1054272407Shselasky		break;
1055272407Shselasky	case ETHER_FLOW:
1056272407Shselasky		eth_mask = &cmd->fs.m_u.ether_spec;
1057272407Shselasky		/* source mac mask must not be set */
1058272407Shselasky		if (!is_zero_ether_addr(eth_mask->h_source))
1059272407Shselasky			return -EINVAL;
1060272407Shselasky
1061272407Shselasky		/* dest mac mask must be ff:ff:ff:ff:ff:ff */
1062272407Shselasky		if (!is_broadcast_ether_addr(eth_mask->h_dest))
1063272407Shselasky			return -EINVAL;
1064272407Shselasky
1065272407Shselasky		if (!all_zeros_or_all_ones(eth_mask->h_proto))
1066272407Shselasky			return -EINVAL;
1067272407Shselasky		break;
1068272407Shselasky	default:
1069272407Shselasky		return -EINVAL;
1070272407Shselasky	}
1071272407Shselasky
1072272407Shselasky	if ((cmd->fs.flow_type & FLOW_EXT)) {
1073272407Shselasky		if (cmd->fs.m_ext.vlan_etype ||
1074272407Shselasky		    !(cmd->fs.m_ext.vlan_tci == 0 ||
1075272407Shselasky		      cmd->fs.m_ext.vlan_tci == cpu_to_be16(0xfff)))
1076272407Shselasky			return -EINVAL;
1077272407Shselasky		if (cmd->fs.m_ext.vlan_tci) {
1078272407Shselasky			if (be16_to_cpu(cmd->fs.h_ext.vlan_tci) <
1079272407Shselasky			    VLAN_MIN_VALUE ||
1080272407Shselasky			    be16_to_cpu(cmd->fs.h_ext.vlan_tci) >
1081272407Shselasky			    VLAN_MAX_VALUE)
1082272407Shselasky				return -EINVAL;
1083272407Shselasky		}
1084272407Shselasky	}
1085272407Shselasky
1086272407Shselasky	return 0;
1087272407Shselasky}
1088272407Shselasky
1089272407Shselaskystatic int mlx4_en_ethtool_add_mac_rule(struct mlx4_ethtool_rxnfc *cmd,
1090272407Shselasky					struct list_head *rule_list_h,
1091272407Shselasky					struct mlx4_spec_list *spec_l2,
1092272407Shselasky					unsigned char *mac)
1093272407Shselasky{
1094272407Shselasky	int err = 0;
1095272407Shselasky	__be64 mac_msk = cpu_to_be64(MLX4_MAC_MASK << 16);
1096272407Shselasky
1097272407Shselasky	spec_l2->id = MLX4_NET_TRANS_RULE_ID_ETH;
1098272407Shselasky	memcpy(spec_l2->eth.dst_mac_msk, &mac_msk, ETH_ALEN);
1099272407Shselasky	memcpy(spec_l2->eth.dst_mac, mac, ETH_ALEN);
1100272407Shselasky
1101272407Shselasky	if ((cmd->fs.flow_type & FLOW_EXT) && cmd->fs.m_ext.vlan_tci) {
1102272407Shselasky		spec_l2->eth.vlan_id = cmd->fs.h_ext.vlan_tci;
1103272407Shselasky		spec_l2->eth.vlan_id_msk = cpu_to_be16(0xfff);
1104272407Shselasky	}
1105272407Shselasky
1106272407Shselasky	list_add_tail(&spec_l2->list, rule_list_h);
1107272407Shselasky
1108272407Shselasky	return err;
1109272407Shselasky}
1110272407Shselasky
1111272407Shselaskystatic int mlx4_en_ethtool_add_mac_rule_by_ipv4(struct mlx4_en_priv *priv,
1112272407Shselasky						struct mlx4_ethtool_rxnfc *cmd,
1113272407Shselasky						struct list_head *rule_list_h,
1114272407Shselasky						struct mlx4_spec_list *spec_l2,
1115272407Shselasky						__be32 ipv4_dst)
1116272407Shselasky{
1117272407Shselasky	unsigned char mac[ETH_ALEN];
1118272407Shselasky
1119272407Shselasky	if (!ipv4_is_multicast(ipv4_dst)) {
1120272407Shselasky		if (cmd->fs.flow_type & FLOW_MAC_EXT)
1121272407Shselasky			memcpy(&mac, cmd->fs.h_ext.h_dest, ETH_ALEN);
1122272407Shselasky		else
1123272407Shselasky			memcpy(&mac, priv->dev->dev_addr, ETH_ALEN);
1124272407Shselasky	} else {
1125272407Shselasky		ip_eth_mc_map(ipv4_dst, mac);
1126272407Shselasky	}
1127272407Shselasky
1128272407Shselasky	return mlx4_en_ethtool_add_mac_rule(cmd, rule_list_h, spec_l2, &mac[0]);
1129272407Shselasky}
1130272407Shselasky
1131272407Shselaskystatic int add_ip_rule(struct mlx4_en_priv *priv,
1132272407Shselasky				struct mlx4_ethtool_rxnfc *cmd,
1133272407Shselasky				struct list_head *list_h)
1134272407Shselasky{
1135272407Shselasky	struct mlx4_spec_list *spec_l2 = NULL;
1136272407Shselasky	struct mlx4_spec_list *spec_l3 = NULL;
1137272407Shselasky	struct ethtool_usrip4_spec *l3_mask = &cmd->fs.m_u.usr_ip4_spec;
1138272407Shselasky
1139272407Shselasky	spec_l3 = kzalloc(sizeof(*spec_l3), GFP_KERNEL);
1140272407Shselasky	spec_l2 = kzalloc(sizeof(*spec_l2), GFP_KERNEL);
1141272407Shselasky	if (!spec_l2 || !spec_l3) {
1142272407Shselasky		en_err(priv, "Fail to alloc ethtool rule.\n");
1143272407Shselasky		kfree(spec_l2);
1144272407Shselasky		kfree(spec_l3);
1145272407Shselasky		return -ENOMEM;
1146272407Shselasky	}
1147272407Shselasky
1148272407Shselasky	mlx4_en_ethtool_add_mac_rule_by_ipv4(priv, cmd, list_h, spec_l2,
1149272407Shselasky					     cmd->fs.h_u.
1150272407Shselasky					     usr_ip4_spec.ip4dst);
1151272407Shselasky	spec_l3->id = MLX4_NET_TRANS_RULE_ID_IPV4;
1152272407Shselasky	spec_l3->ipv4.src_ip = cmd->fs.h_u.usr_ip4_spec.ip4src;
1153272407Shselasky	if (l3_mask->ip4src)
1154272407Shselasky		spec_l3->ipv4.src_ip_msk = MLX4_BE_WORD_MASK;
1155272407Shselasky	spec_l3->ipv4.dst_ip = cmd->fs.h_u.usr_ip4_spec.ip4dst;
1156272407Shselasky	if (l3_mask->ip4dst)
1157272407Shselasky		spec_l3->ipv4.dst_ip_msk = MLX4_BE_WORD_MASK;
1158272407Shselasky	list_add_tail(&spec_l3->list, list_h);
1159272407Shselasky
1160272407Shselasky	return 0;
1161272407Shselasky}
1162272407Shselasky
1163272407Shselaskystatic int add_tcp_udp_rule(struct mlx4_en_priv *priv,
1164272407Shselasky			     struct mlx4_ethtool_rxnfc *cmd,
1165272407Shselasky			     struct list_head *list_h, int proto)
1166272407Shselasky{
1167272407Shselasky	struct mlx4_spec_list *spec_l2 = NULL;
1168272407Shselasky	struct mlx4_spec_list *spec_l3 = NULL;
1169272407Shselasky	struct mlx4_spec_list *spec_l4 = NULL;
1170272407Shselasky	struct ethtool_tcpip4_spec *l4_mask = &cmd->fs.m_u.tcp_ip4_spec;
1171272407Shselasky
1172272407Shselasky	spec_l2 = kzalloc(sizeof(*spec_l2), GFP_KERNEL);
1173272407Shselasky	spec_l3 = kzalloc(sizeof(*spec_l3), GFP_KERNEL);
1174272407Shselasky	spec_l4 = kzalloc(sizeof(*spec_l4), GFP_KERNEL);
1175272407Shselasky	if (!spec_l2 || !spec_l3 || !spec_l4) {
1176272407Shselasky		en_err(priv, "Fail to alloc ethtool rule.\n");
1177272407Shselasky		kfree(spec_l2);
1178272407Shselasky		kfree(spec_l3);
1179272407Shselasky		kfree(spec_l4);
1180272407Shselasky		return -ENOMEM;
1181272407Shselasky	}
1182272407Shselasky
1183272407Shselasky	spec_l3->id = MLX4_NET_TRANS_RULE_ID_IPV4;
1184272407Shselasky
1185272407Shselasky	if (proto == TCP_V4_FLOW) {
1186272407Shselasky		mlx4_en_ethtool_add_mac_rule_by_ipv4(priv, cmd, list_h,
1187272407Shselasky						     spec_l2,
1188272407Shselasky						     cmd->fs.h_u.
1189272407Shselasky						     tcp_ip4_spec.ip4dst);
1190272407Shselasky		spec_l4->id = MLX4_NET_TRANS_RULE_ID_TCP;
1191272407Shselasky		spec_l3->ipv4.src_ip = cmd->fs.h_u.tcp_ip4_spec.ip4src;
1192272407Shselasky		spec_l3->ipv4.dst_ip = cmd->fs.h_u.tcp_ip4_spec.ip4dst;
1193272407Shselasky		spec_l4->tcp_udp.src_port = cmd->fs.h_u.tcp_ip4_spec.psrc;
1194272407Shselasky		spec_l4->tcp_udp.dst_port = cmd->fs.h_u.tcp_ip4_spec.pdst;
1195272407Shselasky	} else {
1196272407Shselasky		mlx4_en_ethtool_add_mac_rule_by_ipv4(priv, cmd, list_h,
1197272407Shselasky						     spec_l2,
1198272407Shselasky						     cmd->fs.h_u.
1199272407Shselasky						     udp_ip4_spec.ip4dst);
1200272407Shselasky		spec_l4->id = MLX4_NET_TRANS_RULE_ID_UDP;
1201272407Shselasky		spec_l3->ipv4.src_ip = cmd->fs.h_u.udp_ip4_spec.ip4src;
1202272407Shselasky		spec_l3->ipv4.dst_ip = cmd->fs.h_u.udp_ip4_spec.ip4dst;
1203272407Shselasky		spec_l4->tcp_udp.src_port = cmd->fs.h_u.udp_ip4_spec.psrc;
1204272407Shselasky		spec_l4->tcp_udp.dst_port = cmd->fs.h_u.udp_ip4_spec.pdst;
1205272407Shselasky	}
1206272407Shselasky
1207272407Shselasky	if (l4_mask->ip4src)
1208272407Shselasky		spec_l3->ipv4.src_ip_msk = MLX4_BE_WORD_MASK;
1209272407Shselasky	if (l4_mask->ip4dst)
1210272407Shselasky		spec_l3->ipv4.dst_ip_msk = MLX4_BE_WORD_MASK;
1211272407Shselasky
1212272407Shselasky	if (l4_mask->psrc)
1213272407Shselasky		spec_l4->tcp_udp.src_port_msk = MLX4_BE_SHORT_MASK;
1214272407Shselasky	if (l4_mask->pdst)
1215272407Shselasky		spec_l4->tcp_udp.dst_port_msk = MLX4_BE_SHORT_MASK;
1216272407Shselasky
1217272407Shselasky	list_add_tail(&spec_l3->list, list_h);
1218272407Shselasky	list_add_tail(&spec_l4->list, list_h);
1219272407Shselasky
1220272407Shselasky	return 0;
1221272407Shselasky}
1222272407Shselasky
1223272407Shselaskystatic int mlx4_en_ethtool_to_net_trans_rule(struct net_device *dev,
1224272407Shselasky					     struct mlx4_ethtool_rxnfc *cmd,
1225272407Shselasky					     struct list_head *rule_list_h)
1226272407Shselasky{
1227272407Shselasky	int err;
1228272407Shselasky	struct ethhdr *eth_spec;
1229272407Shselasky	struct mlx4_spec_list *spec_l2;
1230272407Shselasky	struct mlx4_en_priv *priv = netdev_priv(dev);
1231272407Shselasky
1232272407Shselasky	err = mlx4_en_validate_flow(dev, cmd);
1233272407Shselasky	if (err)
1234272407Shselasky		return err;
1235272407Shselasky
1236272407Shselasky	switch (cmd->fs.flow_type & ~(FLOW_EXT | FLOW_MAC_EXT)) {
1237272407Shselasky	case ETHER_FLOW:
1238272407Shselasky		spec_l2 = kzalloc(sizeof(*spec_l2), GFP_KERNEL);
1239272407Shselasky		if (!spec_l2)
1240272407Shselasky			return -ENOMEM;
1241272407Shselasky
1242272407Shselasky		eth_spec = &cmd->fs.h_u.ether_spec;
1243272407Shselasky		mlx4_en_ethtool_add_mac_rule(cmd, rule_list_h, spec_l2, &eth_spec->h_dest[0]);
1244272407Shselasky		spec_l2->eth.ether_type = eth_spec->h_proto;
1245272407Shselasky		if (eth_spec->h_proto)
1246272407Shselasky			spec_l2->eth.ether_type_enable = 1;
1247272407Shselasky		break;
1248272407Shselasky	case IP_USER_FLOW:
1249272407Shselasky		err = add_ip_rule(priv, cmd, rule_list_h);
1250272407Shselasky		break;
1251272407Shselasky	case TCP_V4_FLOW:
1252272407Shselasky		err = add_tcp_udp_rule(priv, cmd, rule_list_h, TCP_V4_FLOW);
1253272407Shselasky		break;
1254272407Shselasky	case UDP_V4_FLOW:
1255272407Shselasky		err = add_tcp_udp_rule(priv, cmd, rule_list_h, UDP_V4_FLOW);
1256272407Shselasky		break;
1257272407Shselasky	}
1258272407Shselasky
1259272407Shselasky	return err;
1260272407Shselasky}
1261272407Shselasky
1262272407Shselaskystatic int mlx4_en_flow_replace(struct net_device *dev,
1263272407Shselasky				struct mlx4_ethtool_rxnfc *cmd)
1264272407Shselasky{
1265272407Shselasky	int err;
1266272407Shselasky	struct mlx4_en_priv *priv = netdev_priv(dev);
1267272407Shselasky	struct mlx4_en_dev *mdev = priv->mdev;
1268272407Shselasky	struct ethtool_flow_id *loc_rule;
1269272407Shselasky	struct mlx4_spec_list *spec, *tmp_spec;
1270272407Shselasky	u32 qpn;
1271272407Shselasky	u64 reg_id;
1272272407Shselasky
1273272407Shselasky	struct mlx4_net_trans_rule rule = {
1274272407Shselasky		.queue_mode = MLX4_NET_TRANS_Q_FIFO,
1275272407Shselasky		.exclusive = 0,
1276272407Shselasky		.allow_loopback = 1,
1277272407Shselasky		.promisc_mode = MLX4_FS_REGULAR,
1278272407Shselasky	};
1279272407Shselasky
1280272407Shselasky	rule.port = priv->port;
1281272407Shselasky	rule.priority = MLX4_DOMAIN_ETHTOOL | cmd->fs.location;
1282272407Shselasky	INIT_LIST_HEAD(&rule.list);
1283272407Shselasky
1284272407Shselasky	/* Allow direct QP attaches if the EN_ETHTOOL_QP_ATTACH flag is set */
1285272407Shselasky	if (cmd->fs.ring_cookie == RX_CLS_FLOW_DISC)
1286272407Shselasky		qpn = priv->drop_qp.qpn;
1287272407Shselasky	else if (cmd->fs.ring_cookie & EN_ETHTOOL_QP_ATTACH) {
1288272407Shselasky		qpn = cmd->fs.ring_cookie & (EN_ETHTOOL_QP_ATTACH - 1);
1289272407Shselasky	} else {
1290272407Shselasky		if (cmd->fs.ring_cookie >= priv->rx_ring_num) {
1291272407Shselasky			en_warn(priv, "rxnfc: RX ring (%llu) doesn't exist.\n",
1292272407Shselasky				cmd->fs.ring_cookie);
1293272407Shselasky			return -EINVAL;
1294272407Shselasky		}
1295272407Shselasky		qpn = priv->rss_map.qps[cmd->fs.ring_cookie].qpn;
1296272407Shselasky		if (!qpn) {
1297272407Shselasky			en_warn(priv, "rxnfc: RX ring (%llu) is inactive.\n",
1298272407Shselasky				cmd->fs.ring_cookie);
1299272407Shselasky			return -EINVAL;
1300272407Shselasky		}
1301272407Shselasky	}
1302272407Shselasky	rule.qpn = qpn;
1303272407Shselasky	err = mlx4_en_ethtool_to_net_trans_rule(dev, cmd, &rule.list);
1304272407Shselasky	if (err)
1305272407Shselasky		goto out_free_list;
1306272407Shselasky
1307272407Shselasky	mutex_lock(&mdev->state_lock);
1308272407Shselasky	loc_rule = &priv->ethtool_rules[cmd->fs.location];
1309272407Shselasky	if (loc_rule->id) {
1310272407Shselasky		err = mlx4_flow_detach(priv->mdev->dev, loc_rule->id);
1311272407Shselasky		if (err) {
1312272407Shselasky			en_err(priv, "Fail to detach network rule at location %d. registration id = %llx\n",
1313272407Shselasky			       cmd->fs.location, loc_rule->id);
1314272407Shselasky			goto unlock;
1315272407Shselasky		}
1316272407Shselasky		loc_rule->id = 0;
1317272407Shselasky		memset(&loc_rule->flow_spec, 0,
1318272407Shselasky		       sizeof(struct ethtool_rx_flow_spec));
1319272407Shselasky		list_del(&loc_rule->list);
1320272407Shselasky	}
1321272407Shselasky	err = mlx4_flow_attach(priv->mdev->dev, &rule, &reg_id);
1322272407Shselasky	if (err) {
1323272407Shselasky		en_err(priv, "Fail to attach network rule at location %d.\n",
1324272407Shselasky		       cmd->fs.location);
1325272407Shselasky		goto unlock;
1326272407Shselasky	}
1327272407Shselasky	loc_rule->id = reg_id;
1328272407Shselasky	memcpy(&loc_rule->flow_spec, &cmd->fs,
1329272407Shselasky	       sizeof(struct ethtool_rx_flow_spec));
1330272407Shselasky	list_add_tail(&loc_rule->list, &priv->ethtool_list);
1331272407Shselasky
1332272407Shselaskyunlock:
1333272407Shselasky	mutex_unlock(&mdev->state_lock);
1334272407Shselaskyout_free_list:
1335272407Shselasky	list_for_each_entry_safe(spec, tmp_spec, &rule.list, list) {
1336272407Shselasky		list_del(&spec->list);
1337272407Shselasky		kfree(spec);
1338272407Shselasky	}
1339272407Shselasky	return err;
1340272407Shselasky}
1341272407Shselasky
1342272407Shselaskystatic int mlx4_en_flow_detach(struct net_device *dev,
1343272407Shselasky			       struct mlx4_ethtool_rxnfc *cmd)
1344272407Shselasky{
1345272407Shselasky	int err = 0;
1346272407Shselasky	struct ethtool_flow_id *rule;
1347272407Shselasky	struct mlx4_en_priv *priv = netdev_priv(dev);
1348272407Shselasky	struct mlx4_en_dev *mdev = priv->mdev;
1349272407Shselasky
1350272407Shselasky	if (cmd->fs.location >= MAX_NUM_OF_FS_RULES)
1351272407Shselasky		return -EINVAL;
1352272407Shselasky
1353272407Shselasky	mutex_lock(&mdev->state_lock);
1354272407Shselasky	rule = &priv->ethtool_rules[cmd->fs.location];
1355272407Shselasky	if (!rule->id) {
1356272407Shselasky		err =  -ENOENT;
1357272407Shselasky		goto out;
1358272407Shselasky	}
1359272407Shselasky
1360272407Shselasky	err = mlx4_flow_detach(priv->mdev->dev, rule->id);
1361272407Shselasky	if (err) {
1362272407Shselasky		en_err(priv, "Fail to detach network rule at location %d. registration id = 0x%llx\n",
1363272407Shselasky		       cmd->fs.location, rule->id);
1364272407Shselasky		goto out;
1365272407Shselasky	}
1366272407Shselasky	rule->id = 0;
1367272407Shselasky	memset(&rule->flow_spec, 0, sizeof(struct ethtool_rx_flow_spec));
1368272407Shselasky
1369272407Shselasky	list_del(&rule->list);
1370272407Shselaskyout:
1371272407Shselasky	mutex_unlock(&mdev->state_lock);
1372272407Shselasky	return err;
1373272407Shselasky
1374272407Shselasky}
1375272407Shselasky
1376272407Shselaskystatic int mlx4_en_get_flow(struct net_device *dev, struct mlx4_ethtool_rxnfc *cmd,
1377272407Shselasky			    int loc)
1378272407Shselasky{
1379272407Shselasky	int err = 0;
1380272407Shselasky	struct ethtool_flow_id *rule;
1381272407Shselasky	struct mlx4_en_priv *priv = netdev_priv(dev);
1382272407Shselasky
1383272407Shselasky	if (loc < 0 || loc >= MAX_NUM_OF_FS_RULES)
1384272407Shselasky		return -EINVAL;
1385272407Shselasky
1386272407Shselasky	rule = &priv->ethtool_rules[loc];
1387272407Shselasky	if (rule->id)
1388272407Shselasky		memcpy(&cmd->fs, &rule->flow_spec,
1389272407Shselasky		       sizeof(struct ethtool_rx_flow_spec));
1390272407Shselasky	else
1391272407Shselasky		err = -ENOENT;
1392272407Shselasky
1393272407Shselasky	return err;
1394272407Shselasky}
1395272407Shselasky
1396272407Shselaskystatic int mlx4_en_get_num_flows(struct mlx4_en_priv *priv)
1397272407Shselasky{
1398272407Shselasky
1399272407Shselasky	int i, res = 0;
1400272407Shselasky	for (i = 0; i < MAX_NUM_OF_FS_RULES; i++) {
1401272407Shselasky		if (priv->ethtool_rules[i].id)
1402272407Shselasky			res++;
1403272407Shselasky	}
1404272407Shselasky	return res;
1405272407Shselasky
1406272407Shselasky}
1407272407Shselasky
1408272407Shselaskystatic int mlx4_en_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *c,
1409272407Shselasky			     u32 *rule_locs)
1410272407Shselasky{
1411272407Shselasky	struct mlx4_en_priv *priv = netdev_priv(dev);
1412272407Shselasky	struct mlx4_en_dev *mdev = priv->mdev;
1413272407Shselasky	int err = 0;
1414272407Shselasky	int i = 0, priority = 0;
1415272407Shselasky	struct mlx4_ethtool_rxnfc *cmd = (struct mlx4_ethtool_rxnfc *)c;
1416272407Shselasky
1417272407Shselasky	if ((cmd->cmd == ETHTOOL_GRXCLSRLCNT ||
1418272407Shselasky	     cmd->cmd == ETHTOOL_GRXCLSRULE ||
1419272407Shselasky	     cmd->cmd == ETHTOOL_GRXCLSRLALL) &&
1420272407Shselasky	    (mdev->dev->caps.steering_mode !=
1421272407Shselasky	     MLX4_STEERING_MODE_DEVICE_MANAGED || !priv->port_up))
1422272407Shselasky		return -EINVAL;
1423272407Shselasky
1424272407Shselasky	switch (cmd->cmd) {
1425272407Shselasky	case ETHTOOL_GRXRINGS:
1426272407Shselasky		cmd->data = priv->rx_ring_num;
1427272407Shselasky		break;
1428272407Shselasky	case ETHTOOL_GRXCLSRLCNT:
1429272407Shselasky		cmd->rule_cnt = mlx4_en_get_num_flows(priv);
1430272407Shselasky		break;
1431272407Shselasky	case ETHTOOL_GRXCLSRULE:
1432272407Shselasky		err = mlx4_en_get_flow(dev, cmd, cmd->fs.location);
1433272407Shselasky		break;
1434272407Shselasky	case ETHTOOL_GRXCLSRLALL:
1435272407Shselasky		while ((!err || err == -ENOENT) && priority < cmd->rule_cnt) {
1436272407Shselasky			err = mlx4_en_get_flow(dev, cmd, i);
1437272407Shselasky			if (!err)
1438272407Shselasky				rule_locs[priority++] = i;
1439272407Shselasky			i++;
1440272407Shselasky		}
1441272407Shselasky		err = 0;
1442272407Shselasky		break;
1443272407Shselasky	default:
1444272407Shselasky		err = -EOPNOTSUPP;
1445272407Shselasky		break;
1446272407Shselasky	}
1447272407Shselasky
1448272407Shselasky	return err;
1449272407Shselasky}
1450272407Shselasky
1451272407Shselaskystatic int mlx4_en_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *c)
1452272407Shselasky{
1453272407Shselasky	int err = 0;
1454272407Shselasky	struct mlx4_en_priv *priv = netdev_priv(dev);
1455272407Shselasky	struct mlx4_en_dev *mdev = priv->mdev;
1456272407Shselasky	struct mlx4_ethtool_rxnfc *cmd = (struct mlx4_ethtool_rxnfc *)c;
1457272407Shselasky
1458272407Shselasky	if (mdev->dev->caps.steering_mode !=
1459272407Shselasky	    MLX4_STEERING_MODE_DEVICE_MANAGED || !priv->port_up)
1460272407Shselasky		return -EINVAL;
1461272407Shselasky
1462272407Shselasky	switch (cmd->cmd) {
1463272407Shselasky	case ETHTOOL_SRXCLSRLINS:
1464272407Shselasky		err = mlx4_en_flow_replace(dev, cmd);
1465272407Shselasky		break;
1466272407Shselasky	case ETHTOOL_SRXCLSRLDEL:
1467272407Shselasky		err = mlx4_en_flow_detach(dev, cmd);
1468272407Shselasky		break;
1469272407Shselasky	default:
1470272407Shselasky		en_warn(priv, "Unsupported ethtool command. (%d)\n", cmd->cmd);
1471272407Shselasky		return -EINVAL;
1472272407Shselasky	}
1473272407Shselasky
1474272407Shselasky	return err;
1475272407Shselasky}
1476272407Shselasky
1477272407Shselaskystatic void mlx4_en_get_channels(struct net_device *dev,
1478272407Shselasky				 struct ethtool_channels *channel)
1479272407Shselasky{
1480272407Shselasky	struct mlx4_en_priv *priv = netdev_priv(dev);
1481272407Shselasky
1482272407Shselasky	memset(channel, 0, sizeof(*channel));
1483272407Shselasky
1484272407Shselasky	channel->max_rx = MAX_RX_RINGS;
1485272407Shselasky	channel->max_tx = MLX4_EN_MAX_TX_RING_P_UP;
1486272407Shselasky
1487272407Shselasky	channel->rx_count = priv->rx_ring_num;
1488272407Shselasky	channel->tx_count = priv->tx_ring_num / MLX4_EN_NUM_UP;
1489272407Shselasky}
1490272407Shselasky
1491272407Shselaskystatic int mlx4_en_set_channels(struct net_device *dev,
1492272407Shselasky				struct ethtool_channels *channel)
1493272407Shselasky{
1494272407Shselasky	struct mlx4_en_priv *priv = netdev_priv(dev);
1495272407Shselasky	struct mlx4_en_dev *mdev = priv->mdev;
1496272407Shselasky	int port_up = 0;
1497272407Shselasky	int i;
1498272407Shselasky	int err = 0;
1499272407Shselasky
1500272407Shselasky	if (channel->other_count || channel->combined_count ||
1501272407Shselasky	    channel->tx_count > MLX4_EN_MAX_TX_RING_P_UP ||
1502272407Shselasky	    channel->rx_count > MAX_RX_RINGS ||
1503272407Shselasky	    !channel->tx_count || !channel->rx_count)
1504272407Shselasky		return -EINVAL;
1505272407Shselasky
1506272407Shselasky	err = mlx4_en_pre_config(priv);
1507272407Shselasky	if (err)
1508272407Shselasky		return err;
1509272407Shselasky
1510272407Shselasky	mutex_lock(&mdev->state_lock);
1511272407Shselasky	if (priv->port_up) {
1512272407Shselasky		port_up = 1;
1513272407Shselasky		mlx4_en_stop_port(dev);
1514272407Shselasky	}
1515272407Shselasky
1516272407Shselasky	mlx4_en_free_resources(priv);
1517272407Shselasky
1518272407Shselasky	priv->num_tx_rings_p_up = channel->tx_count;
1519272407Shselasky	priv->tx_ring_num = channel->tx_count * MLX4_EN_NUM_UP;
1520272407Shselasky	priv->rx_ring_num = channel->rx_count;
1521272407Shselasky
1522272407Shselasky	err = mlx4_en_alloc_resources(priv);
1523272407Shselasky	if (err) {
1524272407Shselasky		en_err(priv, "Failed reallocating port resources\n");
1525272407Shselasky		goto out;
1526272407Shselasky	}
1527272407Shselasky
1528272407Shselasky	netif_set_real_num_tx_queues(dev, priv->tx_ring_num);
1529272407Shselasky	netif_set_real_num_rx_queues(dev, priv->rx_ring_num);
1530272407Shselasky
1531272407Shselasky	mlx4_en_setup_tc(dev, MLX4_EN_NUM_UP);
1532272407Shselasky
1533272407Shselasky	en_warn(priv, "Using %d TX rings\n", priv->tx_ring_num);
1534272407Shselasky	en_warn(priv, "Using %d RX rings\n", priv->rx_ring_num);
1535272407Shselasky
1536272407Shselasky	if (port_up) {
1537272407Shselasky		err = mlx4_en_start_port(dev);
1538272407Shselasky		if (err)
1539272407Shselasky			en_err(priv, "Failed starting port\n");
1540272407Shselasky
1541272407Shselasky		for (i = 0; i < priv->rx_ring_num; i++) {
1542272407Shselasky			priv->rx_cq[i]->moder_cnt = priv->rx_frames;
1543272407Shselasky			priv->rx_cq[i]->moder_time = priv->rx_usecs;
1544272407Shselasky			priv->last_moder_time[i] = MLX4_EN_AUTO_CONF;
1545272407Shselasky			err = mlx4_en_set_cq_moder(priv, priv->rx_cq[i]);
1546272407Shselasky			if (err)
1547272407Shselasky				goto out;
1548272407Shselasky		}
1549272407Shselasky	}
1550272407Shselasky
1551272407Shselaskyout:
1552272407Shselasky	mutex_unlock(&mdev->state_lock);
1553272407Shselasky	return err;
1554272407Shselasky}
1555272407Shselasky
1556272407Shselaskystatic int mlx4_en_get_ts_info(struct net_device *dev,
1557272407Shselasky			       struct ethtool_ts_info *info)
1558272407Shselasky{
1559272407Shselasky	struct mlx4_en_priv *priv = netdev_priv(dev);
1560272407Shselasky	struct mlx4_en_dev *mdev = priv->mdev;
1561272407Shselasky	int ret;
1562272407Shselasky
1563272407Shselasky	ret = ethtool_op_get_ts_info(dev, info);
1564272407Shselasky	if (ret)
1565272407Shselasky		return ret;
1566272407Shselasky
1567272407Shselasky	if (mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_TS) {
1568272407Shselasky		info->so_timestamping |=
1569272407Shselasky			SOF_TIMESTAMPING_TX_HARDWARE |
1570272407Shselasky			SOF_TIMESTAMPING_RX_HARDWARE |
1571272407Shselasky			SOF_TIMESTAMPING_RAW_HARDWARE;
1572272407Shselasky
1573272407Shselasky		info->tx_types =
1574272407Shselasky			(1 << HWTSTAMP_TX_OFF) |
1575272407Shselasky			(1 << HWTSTAMP_TX_ON);
1576272407Shselasky
1577272407Shselasky		info->rx_filters =
1578272407Shselasky			(1 << HWTSTAMP_FILTER_NONE) |
1579272407Shselasky			(1 << HWTSTAMP_FILTER_ALL);
1580272407Shselasky	}
1581272407Shselasky
1582272407Shselasky	return ret;
1583272407Shselasky}
1584272407Shselasky
1585219820Sjeffconst struct ethtool_ops mlx4_en_ethtool_ops = {
1586219820Sjeff	.get_drvinfo = mlx4_en_get_drvinfo,
1587219820Sjeff	.get_settings = mlx4_en_get_settings,
1588219820Sjeff	.set_settings = mlx4_en_set_settings,
1589219820Sjeff	.get_link = ethtool_op_get_link,
1590219820Sjeff	.get_strings = mlx4_en_get_strings,
1591219820Sjeff	.get_sset_count = mlx4_en_get_sset_count,
1592219820Sjeff	.get_ethtool_stats = mlx4_en_get_ethtool_stats,
1593219820Sjeff	.self_test = mlx4_en_self_test,
1594219820Sjeff	.get_wol = mlx4_en_get_wol,
1595220016Sjeff	.set_wol = mlx4_en_set_wol,
1596219820Sjeff	.get_msglevel = mlx4_en_get_msglevel,
1597219820Sjeff	.set_msglevel = mlx4_en_set_msglevel,
1598219820Sjeff	.get_coalesce = mlx4_en_get_coalesce,
1599219820Sjeff	.set_coalesce = mlx4_en_set_coalesce,
1600219820Sjeff	.get_pauseparam = mlx4_en_get_pauseparam,
1601219820Sjeff	.set_pauseparam = mlx4_en_set_pauseparam,
1602219820Sjeff	.get_ringparam = mlx4_en_get_ringparam,
1603219820Sjeff	.set_ringparam = mlx4_en_set_ringparam,
1604272407Shselasky	.get_rxnfc = mlx4_en_get_rxnfc,
1605272407Shselasky	.set_rxnfc = mlx4_en_set_rxnfc,
1606272407Shselasky	.get_rxfh_indir_size = mlx4_en_get_rxfh_indir_size,
1607272407Shselasky	.get_rxfh_indir = mlx4_en_get_rxfh_indir,
1608272407Shselasky	.set_rxfh_indir = mlx4_en_set_rxfh_indir,
1609272407Shselasky	.get_channels = mlx4_en_get_channels,
1610272407Shselasky	.set_channels = mlx4_en_set_channels,
1611272407Shselasky	.get_ts_info = mlx4_en_get_ts_info,
1612219820Sjeff};
1613219820Sjeff
1614219820Sjeff
1615219820Sjeff
1616219820Sjeff
1617219820Sjeff
1618