1e5b845dcSCatherine Sullivan // SPDX-License-Identifier: (GPL-2.0 OR MIT)
2e5b845dcSCatherine Sullivan /* Google virtual Ethernet (gve) driver
3e5b845dcSCatherine Sullivan  *
4e5b845dcSCatherine Sullivan  * Copyright (C) 2015-2019 Google, Inc.
5e5b845dcSCatherine Sullivan  */
6e5b845dcSCatherine Sullivan 
7e5b845dcSCatherine Sullivan #include <linux/rtnetlink.h>
8e5b845dcSCatherine Sullivan #include "gve.h"
9e5b845dcSCatherine Sullivan 
10e5b845dcSCatherine Sullivan static void gve_get_drvinfo(struct net_device *netdev,
11e5b845dcSCatherine Sullivan 			    struct ethtool_drvinfo *info)
12e5b845dcSCatherine Sullivan {
13e5b845dcSCatherine Sullivan 	struct gve_priv *priv = netdev_priv(netdev);
14e5b845dcSCatherine Sullivan 
15e5b845dcSCatherine Sullivan 	strlcpy(info->driver, "gve", sizeof(info->driver));
16e5b845dcSCatherine Sullivan 	strlcpy(info->version, gve_version_str, sizeof(info->version));
17e5b845dcSCatherine Sullivan 	strlcpy(info->bus_info, pci_name(priv->pdev), sizeof(info->bus_info));
18e5b845dcSCatherine Sullivan }
19e5b845dcSCatherine Sullivan 
20e5b845dcSCatherine Sullivan static void gve_set_msglevel(struct net_device *netdev, u32 value)
21e5b845dcSCatherine Sullivan {
22e5b845dcSCatherine Sullivan 	struct gve_priv *priv = netdev_priv(netdev);
23e5b845dcSCatherine Sullivan 
24e5b845dcSCatherine Sullivan 	priv->msg_enable = value;
25e5b845dcSCatherine Sullivan }
26e5b845dcSCatherine Sullivan 
27e5b845dcSCatherine Sullivan static u32 gve_get_msglevel(struct net_device *netdev)
28e5b845dcSCatherine Sullivan {
29e5b845dcSCatherine Sullivan 	struct gve_priv *priv = netdev_priv(netdev);
30e5b845dcSCatherine Sullivan 
31e5b845dcSCatherine Sullivan 	return priv->msg_enable;
32e5b845dcSCatherine Sullivan }
33e5b845dcSCatherine Sullivan 
34e5b845dcSCatherine Sullivan static const char gve_gstrings_main_stats[][ETH_GSTRING_LEN] = {
35e5b845dcSCatherine Sullivan 	"rx_packets", "tx_packets", "rx_bytes", "tx_bytes",
36e5b845dcSCatherine Sullivan 	"rx_dropped", "tx_dropped", "tx_timeouts",
37433e274bSKuo Zhao 	"rx_skb_alloc_fail", "rx_buf_alloc_fail", "rx_desc_err_dropped_pkt",
38433e274bSKuo Zhao 	"interface_up_cnt", "interface_down_cnt", "reset_cnt",
39433e274bSKuo Zhao 	"page_alloc_fail", "dma_mapping_error",
40433e274bSKuo Zhao };
41433e274bSKuo Zhao 
42433e274bSKuo Zhao static const char gve_gstrings_rx_stats[][ETH_GSTRING_LEN] = {
43433e274bSKuo Zhao 	"rx_posted_desc[%u]", "rx_completed_desc[%u]", "rx_bytes[%u]",
44433e274bSKuo Zhao 	"rx_dropped_pkt[%u]", "rx_copybreak_pkt[%u]", "rx_copied_pkt[%u]",
45433e274bSKuo Zhao };
46433e274bSKuo Zhao 
47433e274bSKuo Zhao static const char gve_gstrings_tx_stats[][ETH_GSTRING_LEN] = {
48433e274bSKuo Zhao 	"tx_posted_desc[%u]", "tx_completed_desc[%u]", "tx_bytes[%u]",
49433e274bSKuo Zhao 	"tx_wake[%u]", "tx_stop[%u]", "tx_event_counter[%u]",
50433e274bSKuo Zhao };
51433e274bSKuo Zhao 
52433e274bSKuo Zhao static const char gve_gstrings_adminq_stats[][ETH_GSTRING_LEN] = {
53433e274bSKuo Zhao 	"adminq_prod_cnt", "adminq_cmd_fail", "adminq_timeouts",
54433e274bSKuo Zhao 	"adminq_describe_device_cnt", "adminq_cfg_device_resources_cnt",
55433e274bSKuo Zhao 	"adminq_register_page_list_cnt", "adminq_unregister_page_list_cnt",
56433e274bSKuo Zhao 	"adminq_create_tx_queue_cnt", "adminq_create_rx_queue_cnt",
57433e274bSKuo Zhao 	"adminq_destroy_tx_queue_cnt", "adminq_destroy_rx_queue_cnt",
58433e274bSKuo Zhao 	"adminq_dcfg_device_resources_cnt", "adminq_set_driver_parameter_cnt",
59e5b845dcSCatherine Sullivan };
60e5b845dcSCatherine Sullivan 
61e5b845dcSCatherine Sullivan #define GVE_MAIN_STATS_LEN  ARRAY_SIZE(gve_gstrings_main_stats)
62433e274bSKuo Zhao #define GVE_ADMINQ_STATS_LEN  ARRAY_SIZE(gve_gstrings_adminq_stats)
63433e274bSKuo Zhao #define NUM_GVE_TX_CNTS	ARRAY_SIZE(gve_gstrings_tx_stats)
64433e274bSKuo Zhao #define NUM_GVE_RX_CNTS	ARRAY_SIZE(gve_gstrings_rx_stats)
65e5b845dcSCatherine Sullivan 
66e5b845dcSCatherine Sullivan static void gve_get_strings(struct net_device *netdev, u32 stringset, u8 *data)
67e5b845dcSCatherine Sullivan {
68e5b845dcSCatherine Sullivan 	struct gve_priv *priv = netdev_priv(netdev);
69e5b845dcSCatherine Sullivan 	char *s = (char *)data;
70433e274bSKuo Zhao 	int i, j;
71e5b845dcSCatherine Sullivan 
72e5b845dcSCatherine Sullivan 	if (stringset != ETH_SS_STATS)
73e5b845dcSCatherine Sullivan 		return;
74e5b845dcSCatherine Sullivan 
75e5b845dcSCatherine Sullivan 	memcpy(s, *gve_gstrings_main_stats,
76e5b845dcSCatherine Sullivan 	       sizeof(gve_gstrings_main_stats));
77e5b845dcSCatherine Sullivan 	s += sizeof(gve_gstrings_main_stats);
78e5b845dcSCatherine Sullivan 	for (i = 0; i < priv->rx_cfg.num_queues; i++) {
79433e274bSKuo Zhao 		for (j = 0; j < NUM_GVE_RX_CNTS; j++) {
80433e274bSKuo Zhao 			snprintf(s, ETH_GSTRING_LEN, gve_gstrings_rx_stats[j], i);
81e5b845dcSCatherine Sullivan 			s += ETH_GSTRING_LEN;
82433e274bSKuo Zhao 		}
83e5b845dcSCatherine Sullivan 	}
84e5b845dcSCatherine Sullivan 	for (i = 0; i < priv->tx_cfg.num_queues; i++) {
85433e274bSKuo Zhao 		for (j = 0; j < NUM_GVE_TX_CNTS; j++) {
86433e274bSKuo Zhao 			snprintf(s, ETH_GSTRING_LEN, gve_gstrings_tx_stats[j], i);
87e5b845dcSCatherine Sullivan 			s += ETH_GSTRING_LEN;
88e5b845dcSCatherine Sullivan 		}
89e5b845dcSCatherine Sullivan 	}
90e5b845dcSCatherine Sullivan 
91433e274bSKuo Zhao 	memcpy(s, *gve_gstrings_adminq_stats,
92433e274bSKuo Zhao 	       sizeof(gve_gstrings_adminq_stats));
93433e274bSKuo Zhao 	s += sizeof(gve_gstrings_adminq_stats);
94433e274bSKuo Zhao }
95433e274bSKuo Zhao 
96e5b845dcSCatherine Sullivan static int gve_get_sset_count(struct net_device *netdev, int sset)
97e5b845dcSCatherine Sullivan {
98e5b845dcSCatherine Sullivan 	struct gve_priv *priv = netdev_priv(netdev);
99e5b845dcSCatherine Sullivan 
100e5b845dcSCatherine Sullivan 	switch (sset) {
101e5b845dcSCatherine Sullivan 	case ETH_SS_STATS:
102433e274bSKuo Zhao 		return GVE_MAIN_STATS_LEN + GVE_ADMINQ_STATS_LEN +
103e5b845dcSCatherine Sullivan 		       (priv->rx_cfg.num_queues * NUM_GVE_RX_CNTS) +
104e5b845dcSCatherine Sullivan 		       (priv->tx_cfg.num_queues * NUM_GVE_TX_CNTS);
105e5b845dcSCatherine Sullivan 	default:
106e5b845dcSCatherine Sullivan 		return -EOPNOTSUPP;
107e5b845dcSCatherine Sullivan 	}
108e5b845dcSCatherine Sullivan }
109e5b845dcSCatherine Sullivan 
110e5b845dcSCatherine Sullivan static void
111e5b845dcSCatherine Sullivan gve_get_ethtool_stats(struct net_device *netdev,
112e5b845dcSCatherine Sullivan 		      struct ethtool_stats *stats, u64 *data)
113e5b845dcSCatherine Sullivan {
114433e274bSKuo Zhao 	u64 tmp_rx_pkts, tmp_rx_bytes, tmp_rx_skb_alloc_fail,	tmp_rx_buf_alloc_fail,
115433e274bSKuo Zhao 		tmp_rx_desc_err_dropped_pkt, tmp_tx_pkts, tmp_tx_bytes;
116433e274bSKuo Zhao 	u64 rx_buf_alloc_fail, rx_desc_err_dropped_pkt, rx_pkts,
117433e274bSKuo Zhao 		rx_skb_alloc_fail, rx_bytes, tx_pkts, tx_bytes;
118433e274bSKuo Zhao 	struct gve_priv *priv;
119e5b845dcSCatherine Sullivan 	unsigned int start;
120e5b845dcSCatherine Sullivan 	int ring;
121e5b845dcSCatherine Sullivan 	int i;
122e5b845dcSCatherine Sullivan 
123e5b845dcSCatherine Sullivan 	ASSERT_RTNL();
124e5b845dcSCatherine Sullivan 
125433e274bSKuo Zhao 	priv = netdev_priv(netdev);
126433e274bSKuo Zhao 	for (rx_pkts = 0, rx_bytes = 0, rx_skb_alloc_fail = 0,
127433e274bSKuo Zhao 	     rx_buf_alloc_fail = 0, rx_desc_err_dropped_pkt = 0, ring = 0;
128e5b845dcSCatherine Sullivan 	     ring < priv->rx_cfg.num_queues; ring++) {
129e5b845dcSCatherine Sullivan 		if (priv->rx) {
130e5b845dcSCatherine Sullivan 			do {
131433e274bSKuo Zhao 				struct gve_rx_ring *rx = &priv->rx[ring];
132433e274bSKuo Zhao 
1333c13ce74SCatherine Sullivan 				start =
134e5b845dcSCatherine Sullivan 				  u64_stats_fetch_begin(&priv->rx[ring].statss);
135433e274bSKuo Zhao 				tmp_rx_pkts = rx->rpackets;
136433e274bSKuo Zhao 				tmp_rx_bytes = rx->rbytes;
137433e274bSKuo Zhao 				tmp_rx_skb_alloc_fail = rx->rx_skb_alloc_fail;
138433e274bSKuo Zhao 				tmp_rx_buf_alloc_fail = rx->rx_buf_alloc_fail;
139433e274bSKuo Zhao 				tmp_rx_desc_err_dropped_pkt =
140433e274bSKuo Zhao 					rx->rx_desc_err_dropped_pkt;
141e5b845dcSCatherine Sullivan 			} while (u64_stats_fetch_retry(&priv->rx[ring].statss,
142e5b845dcSCatherine Sullivan 						       start));
143433e274bSKuo Zhao 			rx_pkts += tmp_rx_pkts;
144433e274bSKuo Zhao 			rx_bytes += tmp_rx_bytes;
145433e274bSKuo Zhao 			rx_skb_alloc_fail += tmp_rx_skb_alloc_fail;
146433e274bSKuo Zhao 			rx_buf_alloc_fail += tmp_rx_buf_alloc_fail;
147433e274bSKuo Zhao 			rx_desc_err_dropped_pkt += tmp_rx_desc_err_dropped_pkt;
148e5b845dcSCatherine Sullivan 		}
149e5b845dcSCatherine Sullivan 	}
150e5b845dcSCatherine Sullivan 	for (tx_pkts = 0, tx_bytes = 0, ring = 0;
151e5b845dcSCatherine Sullivan 	     ring < priv->tx_cfg.num_queues; ring++) {
152e5b845dcSCatherine Sullivan 		if (priv->tx) {
153e5b845dcSCatherine Sullivan 			do {
1543c13ce74SCatherine Sullivan 				start =
155e5b845dcSCatherine Sullivan 				  u64_stats_fetch_begin(&priv->tx[ring].statss);
156433e274bSKuo Zhao 				tmp_tx_pkts = priv->tx[ring].pkt_done;
157433e274bSKuo Zhao 				tmp_tx_bytes = priv->tx[ring].bytes_done;
158e5b845dcSCatherine Sullivan 			} while (u64_stats_fetch_retry(&priv->tx[ring].statss,
159e5b845dcSCatherine Sullivan 						       start));
160433e274bSKuo Zhao 			tx_pkts += tmp_tx_pkts;
161433e274bSKuo Zhao 			tx_bytes += tmp_tx_bytes;
162e5b845dcSCatherine Sullivan 		}
163e5b845dcSCatherine Sullivan 	}
164e5b845dcSCatherine Sullivan 
165e5b845dcSCatherine Sullivan 	i = 0;
166e5b845dcSCatherine Sullivan 	data[i++] = rx_pkts;
167e5b845dcSCatherine Sullivan 	data[i++] = tx_pkts;
168e5b845dcSCatherine Sullivan 	data[i++] = rx_bytes;
169e5b845dcSCatherine Sullivan 	data[i++] = tx_bytes;
170433e274bSKuo Zhao 	/* total rx dropped packets */
171433e274bSKuo Zhao 	data[i++] = rx_skb_alloc_fail + rx_buf_alloc_fail +
172433e274bSKuo Zhao 		    rx_desc_err_dropped_pkt;
173433e274bSKuo Zhao 	/* Skip tx_dropped */
174433e274bSKuo Zhao 	i++;
175433e274bSKuo Zhao 
176e5b845dcSCatherine Sullivan 	data[i++] = priv->tx_timeo_cnt;
177433e274bSKuo Zhao 	data[i++] = rx_skb_alloc_fail;
178433e274bSKuo Zhao 	data[i++] = rx_buf_alloc_fail;
179433e274bSKuo Zhao 	data[i++] = rx_desc_err_dropped_pkt;
180433e274bSKuo Zhao 	data[i++] = priv->interface_up_cnt;
181433e274bSKuo Zhao 	data[i++] = priv->interface_down_cnt;
182433e274bSKuo Zhao 	data[i++] = priv->reset_cnt;
183433e274bSKuo Zhao 	data[i++] = priv->page_alloc_fail;
184433e274bSKuo Zhao 	data[i++] = priv->dma_mapping_error;
185e5b845dcSCatherine Sullivan 	i = GVE_MAIN_STATS_LEN;
186e5b845dcSCatherine Sullivan 
187e5b845dcSCatherine Sullivan 	/* walk RX rings */
188e5b845dcSCatherine Sullivan 	if (priv->rx) {
189e5b845dcSCatherine Sullivan 		for (ring = 0; ring < priv->rx_cfg.num_queues; ring++) {
190e5b845dcSCatherine Sullivan 			struct gve_rx_ring *rx = &priv->rx[ring];
191e5b845dcSCatherine Sullivan 
192438b43bdSCatherine Sullivan 			data[i++] = rx->fill_cnt;
193433e274bSKuo Zhao 			data[i++] = rx->cnt;
194433e274bSKuo Zhao 			do {
195433e274bSKuo Zhao 				start =
196433e274bSKuo Zhao 				  u64_stats_fetch_begin(&priv->rx[ring].statss);
197433e274bSKuo Zhao 				tmp_rx_bytes = rx->rbytes;
198433e274bSKuo Zhao 				tmp_rx_skb_alloc_fail = rx->rx_skb_alloc_fail;
199433e274bSKuo Zhao 				tmp_rx_buf_alloc_fail = rx->rx_buf_alloc_fail;
200433e274bSKuo Zhao 				tmp_rx_desc_err_dropped_pkt =
201433e274bSKuo Zhao 					rx->rx_desc_err_dropped_pkt;
202433e274bSKuo Zhao 			} while (u64_stats_fetch_retry(&priv->rx[ring].statss,
203433e274bSKuo Zhao 						       start));
204433e274bSKuo Zhao 			data[i++] = tmp_rx_bytes;
205433e274bSKuo Zhao 			/* rx dropped packets */
206433e274bSKuo Zhao 			data[i++] = tmp_rx_skb_alloc_fail +
207433e274bSKuo Zhao 				tmp_rx_buf_alloc_fail +
208433e274bSKuo Zhao 				tmp_rx_desc_err_dropped_pkt;
209433e274bSKuo Zhao 			data[i++] = rx->rx_copybreak_pkt;
210433e274bSKuo Zhao 			data[i++] = rx->rx_copied_pkt;
211e5b845dcSCatherine Sullivan 		}
212e5b845dcSCatherine Sullivan 	} else {
213e5b845dcSCatherine Sullivan 		i += priv->rx_cfg.num_queues * NUM_GVE_RX_CNTS;
214e5b845dcSCatherine Sullivan 	}
215e5b845dcSCatherine Sullivan 	/* walk TX rings */
216e5b845dcSCatherine Sullivan 	if (priv->tx) {
217e5b845dcSCatherine Sullivan 		for (ring = 0; ring < priv->tx_cfg.num_queues; ring++) {
218e5b845dcSCatherine Sullivan 			struct gve_tx_ring *tx = &priv->tx[ring];
219e5b845dcSCatherine Sullivan 
220e5b845dcSCatherine Sullivan 			data[i++] = tx->req;
221e5b845dcSCatherine Sullivan 			data[i++] = tx->done;
222433e274bSKuo Zhao 			do {
223433e274bSKuo Zhao 				start =
224433e274bSKuo Zhao 				  u64_stats_fetch_begin(&priv->tx[ring].statss);
225433e274bSKuo Zhao 				tmp_tx_bytes = tx->bytes_done;
226433e274bSKuo Zhao 			} while (u64_stats_fetch_retry(&priv->tx[ring].statss,
227433e274bSKuo Zhao 						       start));
228433e274bSKuo Zhao 			data[i++] = tmp_tx_bytes;
229e5b845dcSCatherine Sullivan 			data[i++] = tx->wake_queue;
230e5b845dcSCatherine Sullivan 			data[i++] = tx->stop_queue;
231e5b845dcSCatherine Sullivan 			data[i++] = be32_to_cpu(gve_tx_load_event_counter(priv,
232e5b845dcSCatherine Sullivan 									  tx));
233e5b845dcSCatherine Sullivan 		}
234e5b845dcSCatherine Sullivan 	} else {
235e5b845dcSCatherine Sullivan 		i += priv->tx_cfg.num_queues * NUM_GVE_TX_CNTS;
236e5b845dcSCatherine Sullivan 	}
237433e274bSKuo Zhao 	/* AQ Stats */
238433e274bSKuo Zhao 	data[i++] = priv->adminq_prod_cnt;
239433e274bSKuo Zhao 	data[i++] = priv->adminq_cmd_fail;
240433e274bSKuo Zhao 	data[i++] = priv->adminq_timeouts;
241433e274bSKuo Zhao 	data[i++] = priv->adminq_describe_device_cnt;
242433e274bSKuo Zhao 	data[i++] = priv->adminq_cfg_device_resources_cnt;
243433e274bSKuo Zhao 	data[i++] = priv->adminq_register_page_list_cnt;
244433e274bSKuo Zhao 	data[i++] = priv->adminq_unregister_page_list_cnt;
245433e274bSKuo Zhao 	data[i++] = priv->adminq_create_tx_queue_cnt;
246433e274bSKuo Zhao 	data[i++] = priv->adminq_create_rx_queue_cnt;
247433e274bSKuo Zhao 	data[i++] = priv->adminq_destroy_tx_queue_cnt;
248433e274bSKuo Zhao 	data[i++] = priv->adminq_destroy_rx_queue_cnt;
249433e274bSKuo Zhao 	data[i++] = priv->adminq_dcfg_device_resources_cnt;
250433e274bSKuo Zhao 	data[i++] = priv->adminq_set_driver_parameter_cnt;
251e5b845dcSCatherine Sullivan }
252e5b845dcSCatherine Sullivan 
253e5b845dcSCatherine Sullivan static void gve_get_channels(struct net_device *netdev,
254e5b845dcSCatherine Sullivan 			     struct ethtool_channels *cmd)
255e5b845dcSCatherine Sullivan {
256e5b845dcSCatherine Sullivan 	struct gve_priv *priv = netdev_priv(netdev);
257e5b845dcSCatherine Sullivan 
258e5b845dcSCatherine Sullivan 	cmd->max_rx = priv->rx_cfg.max_queues;
259e5b845dcSCatherine Sullivan 	cmd->max_tx = priv->tx_cfg.max_queues;
260e5b845dcSCatherine Sullivan 	cmd->max_other = 0;
261e5b845dcSCatherine Sullivan 	cmd->max_combined = 0;
262e5b845dcSCatherine Sullivan 	cmd->rx_count = priv->rx_cfg.num_queues;
263e5b845dcSCatherine Sullivan 	cmd->tx_count = priv->tx_cfg.num_queues;
264e5b845dcSCatherine Sullivan 	cmd->other_count = 0;
265e5b845dcSCatherine Sullivan 	cmd->combined_count = 0;
266e5b845dcSCatherine Sullivan }
267e5b845dcSCatherine Sullivan 
268e5b845dcSCatherine Sullivan static int gve_set_channels(struct net_device *netdev,
269e5b845dcSCatherine Sullivan 			    struct ethtool_channels *cmd)
270e5b845dcSCatherine Sullivan {
271e5b845dcSCatherine Sullivan 	struct gve_priv *priv = netdev_priv(netdev);
272e5b845dcSCatherine Sullivan 	struct gve_queue_config new_tx_cfg = priv->tx_cfg;
273e5b845dcSCatherine Sullivan 	struct gve_queue_config new_rx_cfg = priv->rx_cfg;
274e5b845dcSCatherine Sullivan 	struct ethtool_channels old_settings;
275e5b845dcSCatherine Sullivan 	int new_tx = cmd->tx_count;
276e5b845dcSCatherine Sullivan 	int new_rx = cmd->rx_count;
277e5b845dcSCatherine Sullivan 
278e5b845dcSCatherine Sullivan 	gve_get_channels(netdev, &old_settings);
279e5b845dcSCatherine Sullivan 
280e5b845dcSCatherine Sullivan 	/* Changing combined is not allowed allowed */
281e5b845dcSCatherine Sullivan 	if (cmd->combined_count != old_settings.combined_count)
282e5b845dcSCatherine Sullivan 		return -EINVAL;
283e5b845dcSCatherine Sullivan 
284e5b845dcSCatherine Sullivan 	if (!new_rx || !new_tx)
285e5b845dcSCatherine Sullivan 		return -EINVAL;
286e5b845dcSCatherine Sullivan 
287e5b845dcSCatherine Sullivan 	if (!netif_carrier_ok(netdev)) {
288e5b845dcSCatherine Sullivan 		priv->tx_cfg.num_queues = new_tx;
289e5b845dcSCatherine Sullivan 		priv->rx_cfg.num_queues = new_rx;
290e5b845dcSCatherine Sullivan 		return 0;
291e5b845dcSCatherine Sullivan 	}
292e5b845dcSCatherine Sullivan 
293e5b845dcSCatherine Sullivan 	new_tx_cfg.num_queues = new_tx;
294e5b845dcSCatherine Sullivan 	new_rx_cfg.num_queues = new_rx;
295e5b845dcSCatherine Sullivan 
296e5b845dcSCatherine Sullivan 	return gve_adjust_queues(priv, new_rx_cfg, new_tx_cfg);
297e5b845dcSCatherine Sullivan }
298e5b845dcSCatherine Sullivan 
299e5b845dcSCatherine Sullivan static void gve_get_ringparam(struct net_device *netdev,
300e5b845dcSCatherine Sullivan 			      struct ethtool_ringparam *cmd)
301e5b845dcSCatherine Sullivan {
302e5b845dcSCatherine Sullivan 	struct gve_priv *priv = netdev_priv(netdev);
303e5b845dcSCatherine Sullivan 
304e5b845dcSCatherine Sullivan 	cmd->rx_max_pending = priv->rx_desc_cnt;
305e5b845dcSCatherine Sullivan 	cmd->tx_max_pending = priv->tx_desc_cnt;
306e5b845dcSCatherine Sullivan 	cmd->rx_pending = priv->rx_desc_cnt;
307e5b845dcSCatherine Sullivan 	cmd->tx_pending = priv->tx_desc_cnt;
308e5b845dcSCatherine Sullivan }
309e5b845dcSCatherine Sullivan 
310e5b845dcSCatherine Sullivan static int gve_user_reset(struct net_device *netdev, u32 *flags)
311e5b845dcSCatherine Sullivan {
312e5b845dcSCatherine Sullivan 	struct gve_priv *priv = netdev_priv(netdev);
313e5b845dcSCatherine Sullivan 
314e5b845dcSCatherine Sullivan 	if (*flags == ETH_RESET_ALL) {
315e5b845dcSCatherine Sullivan 		*flags = 0;
316e5b845dcSCatherine Sullivan 		return gve_reset(priv, true);
317e5b845dcSCatherine Sullivan 	}
318e5b845dcSCatherine Sullivan 
319e5b845dcSCatherine Sullivan 	return -EOPNOTSUPP;
320e5b845dcSCatherine Sullivan }
321e5b845dcSCatherine Sullivan 
322d5f7543cSKuo Zhao static int gve_get_tunable(struct net_device *netdev,
323d5f7543cSKuo Zhao 			   const struct ethtool_tunable *etuna, void *value)
324d5f7543cSKuo Zhao {
325d5f7543cSKuo Zhao 	struct gve_priv *priv = netdev_priv(netdev);
326d5f7543cSKuo Zhao 
327d5f7543cSKuo Zhao 	switch (etuna->id) {
328d5f7543cSKuo Zhao 	case ETHTOOL_RX_COPYBREAK:
329d5f7543cSKuo Zhao 		*(u32 *)value = priv->rx_copybreak;
330d5f7543cSKuo Zhao 		return 0;
331d5f7543cSKuo Zhao 	default:
332d5f7543cSKuo Zhao 		return -EOPNOTSUPP;
333d5f7543cSKuo Zhao 	}
334d5f7543cSKuo Zhao }
335d5f7543cSKuo Zhao 
336d5f7543cSKuo Zhao static int gve_set_tunable(struct net_device *netdev,
337433e274bSKuo Zhao 			   const struct ethtool_tunable *etuna,
338433e274bSKuo Zhao 			   const void *value)
339d5f7543cSKuo Zhao {
340d5f7543cSKuo Zhao 	struct gve_priv *priv = netdev_priv(netdev);
341d5f7543cSKuo Zhao 	u32 len;
342d5f7543cSKuo Zhao 
343d5f7543cSKuo Zhao 	switch (etuna->id) {
344d5f7543cSKuo Zhao 	case ETHTOOL_RX_COPYBREAK:
345d5f7543cSKuo Zhao 		len = *(u32 *)value;
346d5f7543cSKuo Zhao 		if (len > PAGE_SIZE / 2)
347d5f7543cSKuo Zhao 			return -EINVAL;
348d5f7543cSKuo Zhao 		priv->rx_copybreak = len;
349d5f7543cSKuo Zhao 		return 0;
350d5f7543cSKuo Zhao 	default:
351d5f7543cSKuo Zhao 		return -EOPNOTSUPP;
352d5f7543cSKuo Zhao 	}
353d5f7543cSKuo Zhao }
354d5f7543cSKuo Zhao 
355e5b845dcSCatherine Sullivan const struct ethtool_ops gve_ethtool_ops = {
356e5b845dcSCatherine Sullivan 	.get_drvinfo = gve_get_drvinfo,
357e5b845dcSCatherine Sullivan 	.get_strings = gve_get_strings,
358e5b845dcSCatherine Sullivan 	.get_sset_count = gve_get_sset_count,
359e5b845dcSCatherine Sullivan 	.get_ethtool_stats = gve_get_ethtool_stats,
360e5b845dcSCatherine Sullivan 	.set_msglevel = gve_set_msglevel,
361e5b845dcSCatherine Sullivan 	.get_msglevel = gve_get_msglevel,
362e5b845dcSCatherine Sullivan 	.set_channels = gve_set_channels,
363e5b845dcSCatherine Sullivan 	.get_channels = gve_get_channels,
364e5b845dcSCatherine Sullivan 	.get_link = ethtool_op_get_link,
365e5b845dcSCatherine Sullivan 	.get_ringparam = gve_get_ringparam,
366e5b845dcSCatherine Sullivan 	.reset = gve_user_reset,
367d5f7543cSKuo Zhao 	.get_tunable = gve_get_tunable,
368d5f7543cSKuo Zhao 	.set_tunable = gve_set_tunable,
369e5b845dcSCatherine Sullivan };
370