Commit | Line | Data |
---|---|---|
e5b845dc CS |
1 | // SPDX-License-Identifier: (GPL-2.0 OR MIT) |
2 | /* Google virtual Ethernet (gve) driver | |
3 | * | |
4 | * Copyright (C) 2015-2019 Google, Inc. | |
5 | */ | |
6 | ||
7 | #include <linux/rtnetlink.h> | |
8 | #include "gve.h" | |
9 | ||
10 | static void gve_get_drvinfo(struct net_device *netdev, | |
11 | struct ethtool_drvinfo *info) | |
12 | { | |
13 | struct gve_priv *priv = netdev_priv(netdev); | |
14 | ||
15 | strlcpy(info->driver, "gve", sizeof(info->driver)); | |
16 | strlcpy(info->version, gve_version_str, sizeof(info->version)); | |
17 | strlcpy(info->bus_info, pci_name(priv->pdev), sizeof(info->bus_info)); | |
18 | } | |
19 | ||
20 | static void gve_set_msglevel(struct net_device *netdev, u32 value) | |
21 | { | |
22 | struct gve_priv *priv = netdev_priv(netdev); | |
23 | ||
24 | priv->msg_enable = value; | |
25 | } | |
26 | ||
27 | static u32 gve_get_msglevel(struct net_device *netdev) | |
28 | { | |
29 | struct gve_priv *priv = netdev_priv(netdev); | |
30 | ||
31 | return priv->msg_enable; | |
32 | } | |
33 | ||
34 | static const char gve_gstrings_main_stats[][ETH_GSTRING_LEN] = { | |
35 | "rx_packets", "tx_packets", "rx_bytes", "tx_bytes", | |
36 | "rx_dropped", "tx_dropped", "tx_timeouts", | |
37 | }; | |
38 | ||
39 | #define GVE_MAIN_STATS_LEN ARRAY_SIZE(gve_gstrings_main_stats) | |
40 | #define NUM_GVE_TX_CNTS 5 | |
41 | #define NUM_GVE_RX_CNTS 2 | |
42 | ||
43 | static void gve_get_strings(struct net_device *netdev, u32 stringset, u8 *data) | |
44 | { | |
45 | struct gve_priv *priv = netdev_priv(netdev); | |
46 | char *s = (char *)data; | |
47 | int i; | |
48 | ||
49 | if (stringset != ETH_SS_STATS) | |
50 | return; | |
51 | ||
52 | memcpy(s, *gve_gstrings_main_stats, | |
53 | sizeof(gve_gstrings_main_stats)); | |
54 | s += sizeof(gve_gstrings_main_stats); | |
55 | for (i = 0; i < priv->rx_cfg.num_queues; i++) { | |
56 | snprintf(s, ETH_GSTRING_LEN, "rx_desc_cnt[%u]", i); | |
57 | s += ETH_GSTRING_LEN; | |
58 | snprintf(s, ETH_GSTRING_LEN, "rx_desc_fill_cnt[%u]", i); | |
59 | s += ETH_GSTRING_LEN; | |
60 | } | |
61 | for (i = 0; i < priv->tx_cfg.num_queues; i++) { | |
62 | snprintf(s, ETH_GSTRING_LEN, "tx_req[%u]", i); | |
63 | s += ETH_GSTRING_LEN; | |
64 | snprintf(s, ETH_GSTRING_LEN, "tx_done[%u]", i); | |
65 | s += ETH_GSTRING_LEN; | |
66 | snprintf(s, ETH_GSTRING_LEN, "tx_wake[%u]", i); | |
67 | s += ETH_GSTRING_LEN; | |
68 | snprintf(s, ETH_GSTRING_LEN, "tx_stop[%u]", i); | |
69 | s += ETH_GSTRING_LEN; | |
70 | snprintf(s, ETH_GSTRING_LEN, "tx_event_counter[%u]", i); | |
71 | s += ETH_GSTRING_LEN; | |
72 | } | |
73 | } | |
74 | ||
75 | static int gve_get_sset_count(struct net_device *netdev, int sset) | |
76 | { | |
77 | struct gve_priv *priv = netdev_priv(netdev); | |
78 | ||
79 | switch (sset) { | |
80 | case ETH_SS_STATS: | |
81 | return GVE_MAIN_STATS_LEN + | |
82 | (priv->rx_cfg.num_queues * NUM_GVE_RX_CNTS) + | |
83 | (priv->tx_cfg.num_queues * NUM_GVE_TX_CNTS); | |
84 | default: | |
85 | return -EOPNOTSUPP; | |
86 | } | |
87 | } | |
88 | ||
89 | static void | |
90 | gve_get_ethtool_stats(struct net_device *netdev, | |
91 | struct ethtool_stats *stats, u64 *data) | |
92 | { | |
93 | struct gve_priv *priv = netdev_priv(netdev); | |
94 | u64 rx_pkts, rx_bytes, tx_pkts, tx_bytes; | |
95 | unsigned int start; | |
96 | int ring; | |
97 | int i; | |
98 | ||
99 | ASSERT_RTNL(); | |
100 | ||
101 | for (rx_pkts = 0, rx_bytes = 0, ring = 0; | |
102 | ring < priv->rx_cfg.num_queues; ring++) { | |
103 | if (priv->rx) { | |
104 | do { | |
3c13ce74 CS |
105 | start = |
106 | u64_stats_fetch_begin(&priv->rx[ring].statss); | |
e5b845dc CS |
107 | rx_pkts += priv->rx[ring].rpackets; |
108 | rx_bytes += priv->rx[ring].rbytes; | |
109 | } while (u64_stats_fetch_retry(&priv->rx[ring].statss, | |
110 | start)); | |
111 | } | |
112 | } | |
113 | for (tx_pkts = 0, tx_bytes = 0, ring = 0; | |
114 | ring < priv->tx_cfg.num_queues; ring++) { | |
115 | if (priv->tx) { | |
116 | do { | |
3c13ce74 CS |
117 | start = |
118 | u64_stats_fetch_begin(&priv->tx[ring].statss); | |
e5b845dc CS |
119 | tx_pkts += priv->tx[ring].pkt_done; |
120 | tx_bytes += priv->tx[ring].bytes_done; | |
121 | } while (u64_stats_fetch_retry(&priv->tx[ring].statss, | |
122 | start)); | |
123 | } | |
124 | } | |
125 | ||
126 | i = 0; | |
127 | data[i++] = rx_pkts; | |
128 | data[i++] = tx_pkts; | |
129 | data[i++] = rx_bytes; | |
130 | data[i++] = tx_bytes; | |
131 | /* Skip rx_dropped and tx_dropped */ | |
132 | i += 2; | |
133 | data[i++] = priv->tx_timeo_cnt; | |
134 | i = GVE_MAIN_STATS_LEN; | |
135 | ||
136 | /* walk RX rings */ | |
137 | if (priv->rx) { | |
138 | for (ring = 0; ring < priv->rx_cfg.num_queues; ring++) { | |
139 | struct gve_rx_ring *rx = &priv->rx[ring]; | |
140 | ||
438b43bd CS |
141 | data[i++] = rx->cnt; |
142 | data[i++] = rx->fill_cnt; | |
e5b845dc CS |
143 | } |
144 | } else { | |
145 | i += priv->rx_cfg.num_queues * NUM_GVE_RX_CNTS; | |
146 | } | |
147 | /* walk TX rings */ | |
148 | if (priv->tx) { | |
149 | for (ring = 0; ring < priv->tx_cfg.num_queues; ring++) { | |
150 | struct gve_tx_ring *tx = &priv->tx[ring]; | |
151 | ||
152 | data[i++] = tx->req; | |
153 | data[i++] = tx->done; | |
154 | data[i++] = tx->wake_queue; | |
155 | data[i++] = tx->stop_queue; | |
156 | data[i++] = be32_to_cpu(gve_tx_load_event_counter(priv, | |
157 | tx)); | |
158 | } | |
159 | } else { | |
160 | i += priv->tx_cfg.num_queues * NUM_GVE_TX_CNTS; | |
161 | } | |
162 | } | |
163 | ||
164 | static void gve_get_channels(struct net_device *netdev, | |
165 | struct ethtool_channels *cmd) | |
166 | { | |
167 | struct gve_priv *priv = netdev_priv(netdev); | |
168 | ||
169 | cmd->max_rx = priv->rx_cfg.max_queues; | |
170 | cmd->max_tx = priv->tx_cfg.max_queues; | |
171 | cmd->max_other = 0; | |
172 | cmd->max_combined = 0; | |
173 | cmd->rx_count = priv->rx_cfg.num_queues; | |
174 | cmd->tx_count = priv->tx_cfg.num_queues; | |
175 | cmd->other_count = 0; | |
176 | cmd->combined_count = 0; | |
177 | } | |
178 | ||
179 | static int gve_set_channels(struct net_device *netdev, | |
180 | struct ethtool_channels *cmd) | |
181 | { | |
182 | struct gve_priv *priv = netdev_priv(netdev); | |
183 | struct gve_queue_config new_tx_cfg = priv->tx_cfg; | |
184 | struct gve_queue_config new_rx_cfg = priv->rx_cfg; | |
185 | struct ethtool_channels old_settings; | |
186 | int new_tx = cmd->tx_count; | |
187 | int new_rx = cmd->rx_count; | |
188 | ||
189 | gve_get_channels(netdev, &old_settings); | |
190 | ||
191 | /* Changing combined is not allowed allowed */ | |
192 | if (cmd->combined_count != old_settings.combined_count) | |
193 | return -EINVAL; | |
194 | ||
195 | if (!new_rx || !new_tx) | |
196 | return -EINVAL; | |
197 | ||
198 | if (!netif_carrier_ok(netdev)) { | |
199 | priv->tx_cfg.num_queues = new_tx; | |
200 | priv->rx_cfg.num_queues = new_rx; | |
201 | return 0; | |
202 | } | |
203 | ||
204 | new_tx_cfg.num_queues = new_tx; | |
205 | new_rx_cfg.num_queues = new_rx; | |
206 | ||
207 | return gve_adjust_queues(priv, new_rx_cfg, new_tx_cfg); | |
208 | } | |
209 | ||
210 | static void gve_get_ringparam(struct net_device *netdev, | |
211 | struct ethtool_ringparam *cmd) | |
212 | { | |
213 | struct gve_priv *priv = netdev_priv(netdev); | |
214 | ||
215 | cmd->rx_max_pending = priv->rx_desc_cnt; | |
216 | cmd->tx_max_pending = priv->tx_desc_cnt; | |
217 | cmd->rx_pending = priv->rx_desc_cnt; | |
218 | cmd->tx_pending = priv->tx_desc_cnt; | |
219 | } | |
220 | ||
221 | static int gve_user_reset(struct net_device *netdev, u32 *flags) | |
222 | { | |
223 | struct gve_priv *priv = netdev_priv(netdev); | |
224 | ||
225 | if (*flags == ETH_RESET_ALL) { | |
226 | *flags = 0; | |
227 | return gve_reset(priv, true); | |
228 | } | |
229 | ||
230 | return -EOPNOTSUPP; | |
231 | } | |
232 | ||
233 | const struct ethtool_ops gve_ethtool_ops = { | |
234 | .get_drvinfo = gve_get_drvinfo, | |
235 | .get_strings = gve_get_strings, | |
236 | .get_sset_count = gve_get_sset_count, | |
237 | .get_ethtool_stats = gve_get_ethtool_stats, | |
238 | .set_msglevel = gve_set_msglevel, | |
239 | .get_msglevel = gve_get_msglevel, | |
240 | .set_channels = gve_set_channels, | |
241 | .get_channels = gve_get_channels, | |
242 | .get_link = ethtool_op_get_link, | |
243 | .get_ringparam = gve_get_ringparam, | |
244 | .reset = gve_user_reset, | |
245 | }; |