xref: /linux/drivers/net/netdevsim/ethtool.c (revision b0e75977c6883283b55fbbdcd64d1c7111b134f2)
1 // SPDX-License-Identifier: GPL-2.0
2 // Copyright (c) 2020 Facebook
3 
4 #include <linux/debugfs.h>
5 #include <linux/random.h>
6 #include <net/netdev_queues.h>
7 
8 #include "netdevsim.h"
9 
10 static void
11 nsim_get_pause_stats(struct net_device *dev,
12 		     struct ethtool_pause_stats *pause_stats)
13 {
14 	struct netdevsim *ns = netdev_priv(dev);
15 
16 	if (ns->ethtool.pauseparam.report_stats_rx)
17 		pause_stats->rx_pause_frames = 1;
18 	if (ns->ethtool.pauseparam.report_stats_tx)
19 		pause_stats->tx_pause_frames = 2;
20 }
21 
22 static void
23 nsim_get_pauseparam(struct net_device *dev, struct ethtool_pauseparam *pause)
24 {
25 	struct netdevsim *ns = netdev_priv(dev);
26 
27 	pause->autoneg = 0; /* We don't support ksettings, so can't pretend */
28 	pause->rx_pause = ns->ethtool.pauseparam.rx;
29 	pause->tx_pause = ns->ethtool.pauseparam.tx;
30 }
31 
32 static int
33 nsim_set_pauseparam(struct net_device *dev, struct ethtool_pauseparam *pause)
34 {
35 	struct netdevsim *ns = netdev_priv(dev);
36 
37 	if (pause->autoneg)
38 		return -EINVAL;
39 
40 	ns->ethtool.pauseparam.rx = pause->rx_pause;
41 	ns->ethtool.pauseparam.tx = pause->tx_pause;
42 	return 0;
43 }
44 
45 static int nsim_get_coalesce(struct net_device *dev,
46 			     struct ethtool_coalesce *coal,
47 			     struct kernel_ethtool_coalesce *kernel_coal,
48 			     struct netlink_ext_ack *extack)
49 {
50 	struct netdevsim *ns = netdev_priv(dev);
51 
52 	memcpy(coal, &ns->ethtool.coalesce, sizeof(ns->ethtool.coalesce));
53 	return 0;
54 }
55 
56 static int nsim_set_coalesce(struct net_device *dev,
57 			     struct ethtool_coalesce *coal,
58 			     struct kernel_ethtool_coalesce *kernel_coal,
59 			     struct netlink_ext_ack *extack)
60 {
61 	struct netdevsim *ns = netdev_priv(dev);
62 
63 	memcpy(&ns->ethtool.coalesce, coal, sizeof(ns->ethtool.coalesce));
64 	return 0;
65 }
66 
67 static void nsim_get_ringparam(struct net_device *dev,
68 			       struct ethtool_ringparam *ring,
69 			       struct kernel_ethtool_ringparam *kernel_ring,
70 			       struct netlink_ext_ack *extack)
71 {
72 	struct netdevsim *ns = netdev_priv(dev);
73 
74 	memcpy(ring, &ns->ethtool.ring, sizeof(ns->ethtool.ring));
75 	kernel_ring->hds_thresh_max = NSIM_HDS_THRESHOLD_MAX;
76 
77 	if (dev->cfg->hds_config == ETHTOOL_TCP_DATA_SPLIT_UNKNOWN)
78 		kernel_ring->tcp_data_split = ETHTOOL_TCP_DATA_SPLIT_ENABLED;
79 }
80 
81 static int nsim_set_ringparam(struct net_device *dev,
82 			      struct ethtool_ringparam *ring,
83 			      struct kernel_ethtool_ringparam *kernel_ring,
84 			      struct netlink_ext_ack *extack)
85 {
86 	struct netdevsim *ns = netdev_priv(dev);
87 
88 	ns->ethtool.ring.rx_pending = ring->rx_pending;
89 	ns->ethtool.ring.rx_jumbo_pending = ring->rx_jumbo_pending;
90 	ns->ethtool.ring.rx_mini_pending = ring->rx_mini_pending;
91 	ns->ethtool.ring.tx_pending = ring->tx_pending;
92 	return 0;
93 }
94 
95 static void
96 nsim_get_channels(struct net_device *dev, struct ethtool_channels *ch)
97 {
98 	struct netdevsim *ns = netdev_priv(dev);
99 
100 	ch->max_combined = ns->nsim_bus_dev->num_queues;
101 	ch->combined_count = ns->ethtool.channels;
102 }
103 
104 static void
105 nsim_wake_queues(struct net_device *dev)
106 {
107 	struct netdevsim *ns = netdev_priv(dev);
108 	struct netdevsim *peer;
109 
110 	synchronize_net();
111 	netif_tx_wake_all_queues(dev);
112 
113 	rcu_read_lock();
114 	peer = rcu_dereference(ns->peer);
115 	if (peer)
116 		netif_tx_wake_all_queues(peer->netdev);
117 	rcu_read_unlock();
118 }
119 
120 static int
121 nsim_set_channels(struct net_device *dev, struct ethtool_channels *ch)
122 {
123 	struct netdevsim *ns = netdev_priv(dev);
124 	int err;
125 
126 	err = netif_set_real_num_queues(dev, ch->combined_count,
127 					ch->combined_count);
128 	if (err)
129 		return err;
130 
131 	ns->ethtool.channels = ch->combined_count;
132 
133 	/* Only wake up queues if devices are linked */
134 	if (rcu_access_pointer(ns->peer))
135 		nsim_wake_queues(dev);
136 
137 	return 0;
138 }
139 
140 static int
141 nsim_get_fecparam(struct net_device *dev, struct ethtool_fecparam *fecparam)
142 {
143 	struct netdevsim *ns = netdev_priv(dev);
144 
145 	if (ns->ethtool.get_err)
146 		return -ns->ethtool.get_err;
147 	memcpy(fecparam, &ns->ethtool.fec, sizeof(ns->ethtool.fec));
148 	return 0;
149 }
150 
151 static int
152 nsim_set_fecparam(struct net_device *dev, struct ethtool_fecparam *fecparam)
153 {
154 	struct netdevsim *ns = netdev_priv(dev);
155 	u32 fec;
156 
157 	if (ns->ethtool.set_err)
158 		return -ns->ethtool.set_err;
159 	memcpy(&ns->ethtool.fec, fecparam, sizeof(ns->ethtool.fec));
160 	fec = fecparam->fec;
161 	if (fec == ETHTOOL_FEC_AUTO)
162 		fec |= ETHTOOL_FEC_OFF;
163 	fec |= ETHTOOL_FEC_NONE;
164 	ns->ethtool.fec.active_fec = 1 << (fls(fec) - 1);
165 	return 0;
166 }
167 
168 static const struct ethtool_fec_hist_range netdevsim_fec_ranges[] = {
169 	{ 0, 0},
170 	{ 1, 3},
171 	{ 4, 7},
172 	{ 0, 0}
173 };
174 
175 static void
176 nsim_get_fec_stats(struct net_device *dev, struct ethtool_fec_stats *fec_stats,
177 		   struct ethtool_fec_hist *hist)
178 {
179 	struct ethtool_fec_hist_value *values = hist->values;
180 
181 	hist->ranges = netdevsim_fec_ranges;
182 
183 	fec_stats->corrected_blocks.total = 123;
184 	fec_stats->uncorrectable_blocks.total = 4;
185 
186 	values[0].per_lane[0] = 125;
187 	values[0].per_lane[1] = 120;
188 	values[0].per_lane[2] = 100;
189 	values[0].per_lane[3] = 100;
190 	values[1].sum = 12;
191 	values[2].sum = 2;
192 	values[2].per_lane[0] = 2;
193 	values[2].per_lane[1] = 0;
194 	values[2].per_lane[2] = 0;
195 	values[2].per_lane[3] = 0;
196 }
197 
198 static int nsim_get_ts_info(struct net_device *dev,
199 			    struct kernel_ethtool_ts_info *info)
200 {
201 	struct netdevsim *ns = netdev_priv(dev);
202 
203 	info->phc_index = mock_phc_index(ns->phc);
204 
205 	return 0;
206 }
207 
208 static u32 nsim_get_rxfh_key_size(struct net_device *dev)
209 {
210 	return NETDEV_RSS_KEY_LEN;
211 }
212 
213 static u32 nsim_get_rxfh_indir_size(struct net_device *dev)
214 {
215 	return NSIM_RSS_INDIR_SIZE;
216 }
217 
218 static int nsim_get_rxfh(struct net_device *dev,
219 			 struct ethtool_rxfh_param *rxfh)
220 {
221 	struct netdevsim *ns = netdev_priv(dev);
222 	u32 i;
223 
224 	rxfh->hfunc = ETH_RSS_HASH_TOP;
225 	if (rxfh->indir) {
226 		for (i = 0; i < NSIM_RSS_INDIR_SIZE; i++)
227 			rxfh->indir[i] = ethtool_rxfh_indir_default(i, ns->ethtool.channels);
228 	}
229 	if (rxfh->key)
230 		memcpy(rxfh->key, ns->ethtool.rss_key,
231 		       sizeof(ns->ethtool.rss_key));
232 
233 	return 0;
234 }
235 
236 static int nsim_get_rxfh_fields(struct net_device *dev,
237 				struct ethtool_rxfh_fields *info)
238 {
239 	switch (info->flow_type) {
240 	case TCP_V4_FLOW:
241 	case UDP_V4_FLOW:
242 	case TCP_V6_FLOW:
243 	case UDP_V6_FLOW:
244 		info->data = RXH_IP_SRC | RXH_IP_DST |
245 			     RXH_L4_B_0_1 | RXH_L4_B_2_3;
246 		return 0;
247 	case IPV4_FLOW:
248 	case IPV6_FLOW:
249 		info->data = RXH_IP_SRC | RXH_IP_DST;
250 		return 0;
251 	default:
252 		return -EOPNOTSUPP;
253 	}
254 }
255 
256 static const struct ethtool_ops nsim_ethtool_ops = {
257 	.get_rxfh_key_size		= nsim_get_rxfh_key_size,
258 	.get_rxfh_indir_size		= nsim_get_rxfh_indir_size,
259 	.get_rxfh			= nsim_get_rxfh,
260 	.get_rxfh_fields		= nsim_get_rxfh_fields,
261 	.supported_coalesce_params	= ETHTOOL_COALESCE_ALL_PARAMS,
262 	.supported_ring_params		= ETHTOOL_RING_USE_TCP_DATA_SPLIT |
263 					  ETHTOOL_RING_USE_HDS_THRS,
264 	.op_needs_rtnl			= ETHTOOL_OP_NEEDS_RTNL_SCHANNELS,
265 	.get_pause_stats	        = nsim_get_pause_stats,
266 	.get_pauseparam		        = nsim_get_pauseparam,
267 	.set_pauseparam		        = nsim_set_pauseparam,
268 	.set_coalesce			= nsim_set_coalesce,
269 	.get_coalesce			= nsim_get_coalesce,
270 	.get_ringparam			= nsim_get_ringparam,
271 	.set_ringparam			= nsim_set_ringparam,
272 	.get_channels			= nsim_get_channels,
273 	.set_channels			= nsim_set_channels,
274 	.get_fecparam			= nsim_get_fecparam,
275 	.set_fecparam			= nsim_set_fecparam,
276 	.get_fec_stats			= nsim_get_fec_stats,
277 	.get_ts_info			= nsim_get_ts_info,
278 };
279 
280 static void nsim_ethtool_ring_init(struct netdevsim *ns)
281 {
282 	ns->ethtool.ring.rx_pending = 512;
283 	ns->ethtool.ring.rx_max_pending = 4096;
284 	ns->ethtool.ring.rx_jumbo_max_pending = 4096;
285 	ns->ethtool.ring.rx_mini_max_pending = 4096;
286 	ns->ethtool.ring.tx_pending = 512;
287 	ns->ethtool.ring.tx_max_pending = 4096;
288 }
289 
290 void nsim_ethtool_init(struct netdevsim *ns)
291 {
292 	struct dentry *ethtool, *dir;
293 
294 	ns->netdev->ethtool_ops = &nsim_ethtool_ops;
295 
296 	nsim_ethtool_ring_init(ns);
297 
298 	ns->ethtool.pauseparam.report_stats_rx = true;
299 	ns->ethtool.pauseparam.report_stats_tx = true;
300 
301 	ns->ethtool.fec.fec = ETHTOOL_FEC_NONE;
302 	ns->ethtool.fec.active_fec = ETHTOOL_FEC_NONE;
303 
304 	ns->ethtool.channels = ns->nsim_bus_dev->num_queues;
305 	netdev_rss_key_fill(ns->ethtool.rss_key, sizeof(ns->ethtool.rss_key));
306 
307 	ethtool = debugfs_create_dir("ethtool", ns->nsim_dev_port->ddir);
308 	ns->ethtool_ddir = ethtool;
309 
310 	debugfs_create_u32("get_err", 0600, ethtool, &ns->ethtool.get_err);
311 	debugfs_create_u32("set_err", 0600, ethtool, &ns->ethtool.set_err);
312 
313 	dir = debugfs_create_dir("pause", ethtool);
314 	debugfs_create_bool("report_stats_rx", 0600, dir,
315 			    &ns->ethtool.pauseparam.report_stats_rx);
316 	debugfs_create_bool("report_stats_tx", 0600, dir,
317 			    &ns->ethtool.pauseparam.report_stats_tx);
318 
319 	dir = debugfs_create_dir("ring", ethtool);
320 	debugfs_create_u32("rx_max_pending", 0600, dir,
321 			   &ns->ethtool.ring.rx_max_pending);
322 	debugfs_create_u32("rx_jumbo_max_pending", 0600, dir,
323 			   &ns->ethtool.ring.rx_jumbo_max_pending);
324 	debugfs_create_u32("rx_mini_max_pending", 0600, dir,
325 			   &ns->ethtool.ring.rx_mini_max_pending);
326 	debugfs_create_u32("tx_max_pending", 0600, dir,
327 			   &ns->ethtool.ring.tx_max_pending);
328 }
329 
330 void nsim_ethtool_fini(struct netdevsim *ns)
331 {
332 	debugfs_remove(ns->ethtool_ddir);
333 }
334