1// SPDX-License-Identifier: GPL-2.0
2// Copyright (c) 2020 Facebook
3
4#include <linux/debugfs.h>
5#include <linux/ethtool.h>
6#include <linux/random.h>
7
8#include "netdevsim.h"
9
10static void
11nsim_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
22static void
23nsim_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
32static int
33nsim_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
45static 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
56static 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
67static 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}
76
77static int nsim_set_ringparam(struct net_device *dev,
78			      struct ethtool_ringparam *ring,
79			      struct kernel_ethtool_ringparam *kernel_ring,
80			      struct netlink_ext_ack *extack)
81{
82	struct netdevsim *ns = netdev_priv(dev);
83
84	ns->ethtool.ring.rx_pending = ring->rx_pending;
85	ns->ethtool.ring.rx_jumbo_pending = ring->rx_jumbo_pending;
86	ns->ethtool.ring.rx_mini_pending = ring->rx_mini_pending;
87	ns->ethtool.ring.tx_pending = ring->tx_pending;
88	return 0;
89}
90
91static void
92nsim_get_channels(struct net_device *dev, struct ethtool_channels *ch)
93{
94	struct netdevsim *ns = netdev_priv(dev);
95
96	ch->max_combined = ns->nsim_bus_dev->num_queues;
97	ch->combined_count = ns->ethtool.channels;
98}
99
100static int
101nsim_set_channels(struct net_device *dev, struct ethtool_channels *ch)
102{
103	struct netdevsim *ns = netdev_priv(dev);
104	int err;
105
106	err = netif_set_real_num_queues(dev, ch->combined_count,
107					ch->combined_count);
108	if (err)
109		return err;
110
111	ns->ethtool.channels = ch->combined_count;
112	return 0;
113}
114
115static int
116nsim_get_fecparam(struct net_device *dev, struct ethtool_fecparam *fecparam)
117{
118	struct netdevsim *ns = netdev_priv(dev);
119
120	if (ns->ethtool.get_err)
121		return -ns->ethtool.get_err;
122	memcpy(fecparam, &ns->ethtool.fec, sizeof(ns->ethtool.fec));
123	return 0;
124}
125
126static int
127nsim_set_fecparam(struct net_device *dev, struct ethtool_fecparam *fecparam)
128{
129	struct netdevsim *ns = netdev_priv(dev);
130	u32 fec;
131
132	if (ns->ethtool.set_err)
133		return -ns->ethtool.set_err;
134	memcpy(&ns->ethtool.fec, fecparam, sizeof(ns->ethtool.fec));
135	fec = fecparam->fec;
136	if (fec == ETHTOOL_FEC_AUTO)
137		fec |= ETHTOOL_FEC_OFF;
138	fec |= ETHTOOL_FEC_NONE;
139	ns->ethtool.fec.active_fec = 1 << (fls(fec) - 1);
140	return 0;
141}
142
143static int nsim_get_ts_info(struct net_device *dev,
144			    struct ethtool_ts_info *info)
145{
146	struct netdevsim *ns = netdev_priv(dev);
147
148	info->phc_index = mock_phc_index(ns->phc);
149
150	return 0;
151}
152
153static const struct ethtool_ops nsim_ethtool_ops = {
154	.supported_coalesce_params	= ETHTOOL_COALESCE_ALL_PARAMS,
155	.get_pause_stats	        = nsim_get_pause_stats,
156	.get_pauseparam		        = nsim_get_pauseparam,
157	.set_pauseparam		        = nsim_set_pauseparam,
158	.set_coalesce			= nsim_set_coalesce,
159	.get_coalesce			= nsim_get_coalesce,
160	.get_ringparam			= nsim_get_ringparam,
161	.set_ringparam			= nsim_set_ringparam,
162	.get_channels			= nsim_get_channels,
163	.set_channels			= nsim_set_channels,
164	.get_fecparam			= nsim_get_fecparam,
165	.set_fecparam			= nsim_set_fecparam,
166	.get_ts_info			= nsim_get_ts_info,
167};
168
169static void nsim_ethtool_ring_init(struct netdevsim *ns)
170{
171	ns->ethtool.ring.rx_max_pending = 4096;
172	ns->ethtool.ring.rx_jumbo_max_pending = 4096;
173	ns->ethtool.ring.rx_mini_max_pending = 4096;
174	ns->ethtool.ring.tx_max_pending = 4096;
175}
176
177void nsim_ethtool_init(struct netdevsim *ns)
178{
179	struct dentry *ethtool, *dir;
180
181	ns->netdev->ethtool_ops = &nsim_ethtool_ops;
182
183	nsim_ethtool_ring_init(ns);
184
185	ns->ethtool.fec.fec = ETHTOOL_FEC_NONE;
186	ns->ethtool.fec.active_fec = ETHTOOL_FEC_NONE;
187
188	ns->ethtool.channels = ns->nsim_bus_dev->num_queues;
189
190	ethtool = debugfs_create_dir("ethtool", ns->nsim_dev_port->ddir);
191
192	debugfs_create_u32("get_err", 0600, ethtool, &ns->ethtool.get_err);
193	debugfs_create_u32("set_err", 0600, ethtool, &ns->ethtool.set_err);
194
195	dir = debugfs_create_dir("pause", ethtool);
196	debugfs_create_bool("report_stats_rx", 0600, dir,
197			    &ns->ethtool.pauseparam.report_stats_rx);
198	debugfs_create_bool("report_stats_tx", 0600, dir,
199			    &ns->ethtool.pauseparam.report_stats_tx);
200
201	dir = debugfs_create_dir("ring", ethtool);
202	debugfs_create_u32("rx_max_pending", 0600, dir,
203			   &ns->ethtool.ring.rx_max_pending);
204	debugfs_create_u32("rx_jumbo_max_pending", 0600, dir,
205			   &ns->ethtool.ring.rx_jumbo_max_pending);
206	debugfs_create_u32("rx_mini_max_pending", 0600, dir,
207			   &ns->ethtool.ring.rx_mini_max_pending);
208	debugfs_create_u32("tx_max_pending", 0600, dir,
209			   &ns->ethtool.ring.tx_max_pending);
210}
211