Commit | Line | Data |
---|---|---|
d1a890fa SB |
1 | /* |
2 | * Linux driver for VMware's vmxnet3 ethernet NIC. | |
3 | * | |
123db31d | 4 | * Copyright (C) 2008-2020, VMware, Inc. All Rights Reserved. |
d1a890fa SB |
5 | * |
6 | * This program is free software; you can redistribute it and/or modify it | |
7 | * under the terms of the GNU General Public License as published by the | |
8 | * Free Software Foundation; version 2 of the License and no later version. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, but | |
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | |
13 | * NON INFRINGEMENT. See the GNU General Public License for more | |
14 | * details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License | |
17 | * along with this program; if not, write to the Free Software | |
18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
19 | * | |
20 | * The full GNU General Public License is included in this distribution in | |
21 | * the file called "COPYING". | |
22 | * | |
190af10f | 23 | * Maintained by: pv-drivers@vmware.com |
d1a890fa SB |
24 | * |
25 | */ | |
26 | ||
27 | ||
28 | #include "vmxnet3_int.h" | |
29 | ||
30 | struct vmxnet3_stat_desc { | |
31 | char desc[ETH_GSTRING_LEN]; | |
32 | int offset; | |
33 | }; | |
34 | ||
35 | ||
d1a890fa SB |
36 | /* per tq stats maintained by the device */ |
37 | static const struct vmxnet3_stat_desc | |
38 | vmxnet3_tq_dev_stats[] = { | |
39 | /* description, offset */ | |
76d39dae SB |
40 | { "Tx Queue#", 0 }, |
41 | { " TSO pkts tx", offsetof(struct UPT1_TxStats, TSOPktsTxOK) }, | |
42 | { " TSO bytes tx", offsetof(struct UPT1_TxStats, TSOBytesTxOK) }, | |
43 | { " ucast pkts tx", offsetof(struct UPT1_TxStats, ucastPktsTxOK) }, | |
44 | { " ucast bytes tx", offsetof(struct UPT1_TxStats, ucastBytesTxOK) }, | |
45 | { " mcast pkts tx", offsetof(struct UPT1_TxStats, mcastPktsTxOK) }, | |
46 | { " mcast bytes tx", offsetof(struct UPT1_TxStats, mcastBytesTxOK) }, | |
47 | { " bcast pkts tx", offsetof(struct UPT1_TxStats, bcastPktsTxOK) }, | |
48 | { " bcast bytes tx", offsetof(struct UPT1_TxStats, bcastBytesTxOK) }, | |
49 | { " pkts tx err", offsetof(struct UPT1_TxStats, pktsTxError) }, | |
50 | { " pkts tx discard", offsetof(struct UPT1_TxStats, pktsTxDiscard) }, | |
d1a890fa SB |
51 | }; |
52 | ||
53 | /* per tq stats maintained by the driver */ | |
54 | static const struct vmxnet3_stat_desc | |
55 | vmxnet3_tq_driver_stats[] = { | |
56 | /* description, offset */ | |
76d39dae SB |
57 | {" drv dropped tx total", offsetof(struct vmxnet3_tq_driver_stats, |
58 | drop_total) }, | |
59 | { " too many frags", offsetof(struct vmxnet3_tq_driver_stats, | |
60 | drop_too_many_frags) }, | |
61 | { " giant hdr", offsetof(struct vmxnet3_tq_driver_stats, | |
62 | drop_oversized_hdr) }, | |
63 | { " hdr err", offsetof(struct vmxnet3_tq_driver_stats, | |
64 | drop_hdr_inspect_err) }, | |
65 | { " tso", offsetof(struct vmxnet3_tq_driver_stats, | |
66 | drop_tso) }, | |
67 | { " ring full", offsetof(struct vmxnet3_tq_driver_stats, | |
68 | tx_ring_full) }, | |
69 | { " pkts linearized", offsetof(struct vmxnet3_tq_driver_stats, | |
70 | linearized) }, | |
71 | { " hdr cloned", offsetof(struct vmxnet3_tq_driver_stats, | |
72 | copy_skb_header) }, | |
73 | { " giant hdr", offsetof(struct vmxnet3_tq_driver_stats, | |
74 | oversized_hdr) }, | |
d1a890fa SB |
75 | }; |
76 | ||
77 | /* per rq stats maintained by the device */ | |
78 | static const struct vmxnet3_stat_desc | |
79 | vmxnet3_rq_dev_stats[] = { | |
76d39dae SB |
80 | { "Rx Queue#", 0 }, |
81 | { " LRO pkts rx", offsetof(struct UPT1_RxStats, LROPktsRxOK) }, | |
82 | { " LRO byte rx", offsetof(struct UPT1_RxStats, LROBytesRxOK) }, | |
83 | { " ucast pkts rx", offsetof(struct UPT1_RxStats, ucastPktsRxOK) }, | |
84 | { " ucast bytes rx", offsetof(struct UPT1_RxStats, ucastBytesRxOK) }, | |
85 | { " mcast pkts rx", offsetof(struct UPT1_RxStats, mcastPktsRxOK) }, | |
86 | { " mcast bytes rx", offsetof(struct UPT1_RxStats, mcastBytesRxOK) }, | |
87 | { " bcast pkts rx", offsetof(struct UPT1_RxStats, bcastPktsRxOK) }, | |
88 | { " bcast bytes rx", offsetof(struct UPT1_RxStats, bcastBytesRxOK) }, | |
89 | { " pkts rx OOB", offsetof(struct UPT1_RxStats, pktsRxOutOfBuf) }, | |
90 | { " pkts rx err", offsetof(struct UPT1_RxStats, pktsRxError) }, | |
d1a890fa SB |
91 | }; |
92 | ||
93 | /* per rq stats maintained by the driver */ | |
94 | static const struct vmxnet3_stat_desc | |
95 | vmxnet3_rq_driver_stats[] = { | |
96 | /* description, offset */ | |
76d39dae SB |
97 | { " drv dropped rx total", offsetof(struct vmxnet3_rq_driver_stats, |
98 | drop_total) }, | |
99 | { " err", offsetof(struct vmxnet3_rq_driver_stats, | |
100 | drop_err) }, | |
101 | { " fcs", offsetof(struct vmxnet3_rq_driver_stats, | |
102 | drop_fcs) }, | |
103 | { " rx buf alloc fail", offsetof(struct vmxnet3_rq_driver_stats, | |
104 | rx_buf_alloc_failure) }, | |
d1a890fa SB |
105 | }; |
106 | ||
7887456e | 107 | /* global stats maintained by the driver */ |
d1a890fa SB |
108 | static const struct vmxnet3_stat_desc |
109 | vmxnet3_global_stats[] = { | |
110 | /* description, offset */ | |
76d39dae | 111 | { "tx timeout count", offsetof(struct vmxnet3_adapter, |
d1a890fa SB |
112 | tx_timeout_count) } |
113 | }; | |
114 | ||
115 | ||
bc1f4470 | 116 | void |
95305f6c | 117 | vmxnet3_get_stats64(struct net_device *netdev, |
118 | struct rtnl_link_stats64 *stats) | |
d1a890fa SB |
119 | { |
120 | struct vmxnet3_adapter *adapter; | |
121 | struct vmxnet3_tq_driver_stats *drvTxStats; | |
122 | struct vmxnet3_rq_driver_stats *drvRxStats; | |
123 | struct UPT1_TxStats *devTxStats; | |
124 | struct UPT1_RxStats *devRxStats; | |
83d0feff | 125 | unsigned long flags; |
09c5088e | 126 | int i; |
d1a890fa SB |
127 | |
128 | adapter = netdev_priv(netdev); | |
129 | ||
130 | /* Collect the dev stats into the shared area */ | |
83d0feff | 131 | spin_lock_irqsave(&adapter->cmd_lock, flags); |
d1a890fa | 132 | VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, VMXNET3_CMD_GET_STATS); |
83d0feff | 133 | spin_unlock_irqrestore(&adapter->cmd_lock, flags); |
d1a890fa | 134 | |
09c5088e SB |
135 | for (i = 0; i < adapter->num_tx_queues; i++) { |
136 | devTxStats = &adapter->tqd_start[i].stats; | |
137 | drvTxStats = &adapter->tx_queue[i].stats; | |
95305f6c | 138 | stats->tx_packets += devTxStats->ucastPktsTxOK + |
139 | devTxStats->mcastPktsTxOK + | |
140 | devTxStats->bcastPktsTxOK; | |
141 | stats->tx_bytes += devTxStats->ucastBytesTxOK + | |
142 | devTxStats->mcastBytesTxOK + | |
143 | devTxStats->bcastBytesTxOK; | |
144 | stats->tx_errors += devTxStats->pktsTxError; | |
145 | stats->tx_dropped += drvTxStats->drop_total; | |
09c5088e | 146 | } |
d1a890fa | 147 | |
09c5088e SB |
148 | for (i = 0; i < adapter->num_rx_queues; i++) { |
149 | devRxStats = &adapter->rqd_start[i].stats; | |
150 | drvRxStats = &adapter->rx_queue[i].stats; | |
95305f6c | 151 | stats->rx_packets += devRxStats->ucastPktsRxOK + |
152 | devRxStats->mcastPktsRxOK + | |
153 | devRxStats->bcastPktsRxOK; | |
d1a890fa | 154 | |
95305f6c | 155 | stats->rx_bytes += devRxStats->ucastBytesRxOK + |
156 | devRxStats->mcastBytesRxOK + | |
157 | devRxStats->bcastBytesRxOK; | |
d1a890fa | 158 | |
95305f6c | 159 | stats->rx_errors += devRxStats->pktsRxError; |
160 | stats->rx_dropped += drvRxStats->drop_total; | |
161 | stats->multicast += devRxStats->mcastPktsRxOK; | |
09c5088e | 162 | } |
d1a890fa SB |
163 | } |
164 | ||
165 | static int | |
166 | vmxnet3_get_sset_count(struct net_device *netdev, int sset) | |
167 | { | |
76d39dae | 168 | struct vmxnet3_adapter *adapter = netdev_priv(netdev); |
d1a890fa SB |
169 | switch (sset) { |
170 | case ETH_SS_STATS: | |
76d39dae SB |
171 | return (ARRAY_SIZE(vmxnet3_tq_dev_stats) + |
172 | ARRAY_SIZE(vmxnet3_tq_driver_stats)) * | |
173 | adapter->num_tx_queues + | |
174 | (ARRAY_SIZE(vmxnet3_rq_dev_stats) + | |
175 | ARRAY_SIZE(vmxnet3_rq_driver_stats)) * | |
176 | adapter->num_rx_queues + | |
d1a890fa SB |
177 | ARRAY_SIZE(vmxnet3_global_stats); |
178 | default: | |
179 | return -EOPNOTSUPP; | |
180 | } | |
181 | } | |
182 | ||
183 | ||
b6bd9b54 SK |
184 | /* This is a version 2 of the vmxnet3 ethtool_regs which goes hand in hand with |
185 | * the version 2 of the vmxnet3 support for ethtool(8) --register-dump. | |
186 | * Therefore, if any registers are added, removed or modified, then a version | |
187 | * bump and a corresponding change in the vmxnet3 support for ethtool(8) | |
188 | * --register-dump would be required. | |
189 | */ | |
d1a890fa SB |
190 | static int |
191 | vmxnet3_get_regs_len(struct net_device *netdev) | |
192 | { | |
76d39dae | 193 | struct vmxnet3_adapter *adapter = netdev_priv(netdev); |
b6bd9b54 SK |
194 | |
195 | return ((9 /* BAR1 registers */ + | |
196 | (1 + adapter->intr.num_intrs) + | |
197 | (1 + adapter->num_tx_queues * 17 /* Tx queue registers */) + | |
198 | (1 + adapter->num_rx_queues * 23 /* Rx queue registers */)) * | |
199 | sizeof(u32)); | |
d1a890fa SB |
200 | } |
201 | ||
202 | ||
203 | static void | |
204 | vmxnet3_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo) | |
205 | { | |
206 | struct vmxnet3_adapter *adapter = netdev_priv(netdev); | |
207 | ||
208 | strlcpy(drvinfo->driver, vmxnet3_driver_name, sizeof(drvinfo->driver)); | |
d1a890fa SB |
209 | |
210 | strlcpy(drvinfo->version, VMXNET3_DRIVER_VERSION_REPORT, | |
211 | sizeof(drvinfo->version)); | |
d1a890fa SB |
212 | |
213 | strlcpy(drvinfo->bus_info, pci_name(adapter->pdev), | |
7826d43f | 214 | sizeof(drvinfo->bus_info)); |
d1a890fa SB |
215 | } |
216 | ||
217 | ||
218 | static void | |
219 | vmxnet3_get_strings(struct net_device *netdev, u32 stringset, u8 *buf) | |
220 | { | |
76d39dae | 221 | struct vmxnet3_adapter *adapter = netdev_priv(netdev); |
d1a890fa | 222 | if (stringset == ETH_SS_STATS) { |
76d39dae SB |
223 | int i, j; |
224 | for (j = 0; j < adapter->num_tx_queues; j++) { | |
225 | for (i = 0; i < ARRAY_SIZE(vmxnet3_tq_dev_stats); i++) { | |
226 | memcpy(buf, vmxnet3_tq_dev_stats[i].desc, | |
227 | ETH_GSTRING_LEN); | |
228 | buf += ETH_GSTRING_LEN; | |
229 | } | |
230 | for (i = 0; i < ARRAY_SIZE(vmxnet3_tq_driver_stats); | |
231 | i++) { | |
232 | memcpy(buf, vmxnet3_tq_driver_stats[i].desc, | |
233 | ETH_GSTRING_LEN); | |
234 | buf += ETH_GSTRING_LEN; | |
235 | } | |
d1a890fa | 236 | } |
76d39dae SB |
237 | |
238 | for (j = 0; j < adapter->num_rx_queues; j++) { | |
239 | for (i = 0; i < ARRAY_SIZE(vmxnet3_rq_dev_stats); i++) { | |
240 | memcpy(buf, vmxnet3_rq_dev_stats[i].desc, | |
241 | ETH_GSTRING_LEN); | |
242 | buf += ETH_GSTRING_LEN; | |
243 | } | |
244 | for (i = 0; i < ARRAY_SIZE(vmxnet3_rq_driver_stats); | |
245 | i++) { | |
246 | memcpy(buf, vmxnet3_rq_driver_stats[i].desc, | |
247 | ETH_GSTRING_LEN); | |
248 | buf += ETH_GSTRING_LEN; | |
249 | } | |
d1a890fa | 250 | } |
76d39dae | 251 | |
d1a890fa SB |
252 | for (i = 0; i < ARRAY_SIZE(vmxnet3_global_stats); i++) { |
253 | memcpy(buf, vmxnet3_global_stats[i].desc, | |
254 | ETH_GSTRING_LEN); | |
255 | buf += ETH_GSTRING_LEN; | |
256 | } | |
257 | } | |
258 | } | |
259 | ||
3dd7400b RD |
260 | netdev_features_t vmxnet3_fix_features(struct net_device *netdev, |
261 | netdev_features_t features) | |
262 | { | |
263 | /* If Rx checksum is disabled, then LRO should also be disabled */ | |
264 | if (!(features & NETIF_F_RXCSUM)) | |
265 | features &= ~NETIF_F_LRO; | |
266 | ||
267 | return features; | |
268 | } | |
269 | ||
dacce2be RD |
270 | static void vmxnet3_enable_encap_offloads(struct net_device *netdev) |
271 | { | |
272 | struct vmxnet3_adapter *adapter = netdev_priv(netdev); | |
273 | ||
274 | if (VMXNET3_VERSION_GE_4(adapter)) { | |
275 | netdev->hw_enc_features |= NETIF_F_SG | NETIF_F_RXCSUM | | |
276 | NETIF_F_HW_CSUM | NETIF_F_HW_VLAN_CTAG_TX | | |
277 | NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_TSO | NETIF_F_TSO6 | | |
278 | NETIF_F_LRO | NETIF_F_GSO_UDP_TUNNEL | | |
279 | NETIF_F_GSO_UDP_TUNNEL_CSUM; | |
280 | } | |
281 | } | |
282 | ||
283 | static void vmxnet3_disable_encap_offloads(struct net_device *netdev) | |
284 | { | |
285 | struct vmxnet3_adapter *adapter = netdev_priv(netdev); | |
286 | ||
287 | if (VMXNET3_VERSION_GE_4(adapter)) { | |
288 | netdev->hw_enc_features &= ~(NETIF_F_SG | NETIF_F_RXCSUM | | |
289 | NETIF_F_HW_CSUM | NETIF_F_HW_VLAN_CTAG_TX | | |
290 | NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_TSO | NETIF_F_TSO6 | | |
291 | NETIF_F_LRO | NETIF_F_GSO_UDP_TUNNEL | | |
292 | NETIF_F_GSO_UDP_TUNNEL_CSUM); | |
293 | } | |
294 | } | |
295 | ||
c8f44aff | 296 | int vmxnet3_set_features(struct net_device *netdev, netdev_features_t features) |
d92be4b1 | 297 | { |
d1a890fa | 298 | struct vmxnet3_adapter *adapter = netdev_priv(netdev); |
83d0feff | 299 | unsigned long flags; |
c8f44aff | 300 | netdev_features_t changed = features ^ netdev->features; |
dacce2be RD |
301 | netdev_features_t tun_offload_mask = NETIF_F_GSO_UDP_TUNNEL | |
302 | NETIF_F_GSO_UDP_TUNNEL_CSUM; | |
303 | u8 udp_tun_enabled = (netdev->features & tun_offload_mask) != 0; | |
d1a890fa | 304 | |
f646968f | 305 | if (changed & (NETIF_F_RXCSUM | NETIF_F_LRO | |
dacce2be | 306 | NETIF_F_HW_VLAN_CTAG_RX | tun_offload_mask)) { |
a0d2730c MM |
307 | if (features & NETIF_F_RXCSUM) |
308 | adapter->shared->devRead.misc.uptFeatures |= | |
309 | UPT1_F_RXCSUM; | |
310 | else | |
311 | adapter->shared->devRead.misc.uptFeatures &= | |
312 | ~UPT1_F_RXCSUM; | |
d1a890fa | 313 | |
7887456e | 314 | /* update hardware LRO capability accordingly */ |
a0d2730c | 315 | if (features & NETIF_F_LRO) |
ca802447 | 316 | adapter->shared->devRead.misc.uptFeatures |= |
3843e515 | 317 | UPT1_F_LRO; |
d1a890fa SB |
318 | else |
319 | adapter->shared->devRead.misc.uptFeatures &= | |
3843e515 | 320 | ~UPT1_F_LRO; |
a0d2730c | 321 | |
f646968f | 322 | if (features & NETIF_F_HW_VLAN_CTAG_RX) |
72e85c45 JG |
323 | adapter->shared->devRead.misc.uptFeatures |= |
324 | UPT1_F_RXVLAN; | |
325 | else | |
326 | adapter->shared->devRead.misc.uptFeatures &= | |
327 | ~UPT1_F_RXVLAN; | |
328 | ||
dacce2be RD |
329 | if ((features & tun_offload_mask) != 0 && !udp_tun_enabled) { |
330 | vmxnet3_enable_encap_offloads(netdev); | |
331 | adapter->shared->devRead.misc.uptFeatures |= | |
332 | UPT1_F_RXINNEROFLD; | |
333 | } else if ((features & tun_offload_mask) == 0 && | |
334 | udp_tun_enabled) { | |
335 | vmxnet3_disable_encap_offloads(netdev); | |
336 | adapter->shared->devRead.misc.uptFeatures &= | |
337 | ~UPT1_F_RXINNEROFLD; | |
338 | } | |
339 | ||
83d0feff | 340 | spin_lock_irqsave(&adapter->cmd_lock, flags); |
d1a890fa SB |
341 | VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, |
342 | VMXNET3_CMD_UPDATE_FEATURE); | |
83d0feff | 343 | spin_unlock_irqrestore(&adapter->cmd_lock, flags); |
d1a890fa SB |
344 | } |
345 | return 0; | |
346 | } | |
347 | ||
348 | static void | |
349 | vmxnet3_get_ethtool_stats(struct net_device *netdev, | |
350 | struct ethtool_stats *stats, u64 *buf) | |
351 | { | |
352 | struct vmxnet3_adapter *adapter = netdev_priv(netdev); | |
83d0feff | 353 | unsigned long flags; |
d1a890fa SB |
354 | u8 *base; |
355 | int i; | |
09c5088e | 356 | int j = 0; |
d1a890fa | 357 | |
83d0feff | 358 | spin_lock_irqsave(&adapter->cmd_lock, flags); |
d1a890fa | 359 | VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, VMXNET3_CMD_GET_STATS); |
83d0feff | 360 | spin_unlock_irqrestore(&adapter->cmd_lock, flags); |
d1a890fa SB |
361 | |
362 | /* this does assume each counter is 64-bit wide */ | |
76d39dae SB |
363 | for (j = 0; j < adapter->num_tx_queues; j++) { |
364 | base = (u8 *)&adapter->tqd_start[j].stats; | |
365 | *buf++ = (u64)j; | |
366 | for (i = 1; i < ARRAY_SIZE(vmxnet3_tq_dev_stats); i++) | |
367 | *buf++ = *(u64 *)(base + | |
368 | vmxnet3_tq_dev_stats[i].offset); | |
369 | ||
370 | base = (u8 *)&adapter->tx_queue[j].stats; | |
371 | for (i = 0; i < ARRAY_SIZE(vmxnet3_tq_driver_stats); i++) | |
372 | *buf++ = *(u64 *)(base + | |
373 | vmxnet3_tq_driver_stats[i].offset); | |
374 | } | |
d1a890fa | 375 | |
331b9ab8 | 376 | for (j = 0; j < adapter->num_rx_queues; j++) { |
76d39dae SB |
377 | base = (u8 *)&adapter->rqd_start[j].stats; |
378 | *buf++ = (u64) j; | |
379 | for (i = 1; i < ARRAY_SIZE(vmxnet3_rq_dev_stats); i++) | |
380 | *buf++ = *(u64 *)(base + | |
381 | vmxnet3_rq_dev_stats[i].offset); | |
382 | ||
383 | base = (u8 *)&adapter->rx_queue[j].stats; | |
384 | for (i = 0; i < ARRAY_SIZE(vmxnet3_rq_driver_stats); i++) | |
385 | *buf++ = *(u64 *)(base + | |
386 | vmxnet3_rq_driver_stats[i].offset); | |
387 | } | |
d1a890fa SB |
388 | |
389 | base = (u8 *)adapter; | |
390 | for (i = 0; i < ARRAY_SIZE(vmxnet3_global_stats); i++) | |
391 | *buf++ = *(u64 *)(base + vmxnet3_global_stats[i].offset); | |
392 | } | |
393 | ||
394 | ||
b6bd9b54 SK |
395 | /* This is a version 2 of the vmxnet3 ethtool_regs which goes hand in hand with |
396 | * the version 2 of the vmxnet3 support for ethtool(8) --register-dump. | |
397 | * Therefore, if any registers are added, removed or modified, then a version | |
398 | * bump and a corresponding change in the vmxnet3 support for ethtool(8) | |
399 | * --register-dump would be required. | |
400 | */ | |
d1a890fa SB |
401 | static void |
402 | vmxnet3_get_regs(struct net_device *netdev, struct ethtool_regs *regs, void *p) | |
403 | { | |
404 | struct vmxnet3_adapter *adapter = netdev_priv(netdev); | |
405 | u32 *buf = p; | |
76d39dae | 406 | int i = 0, j = 0; |
d1a890fa SB |
407 | |
408 | memset(p, 0, vmxnet3_get_regs_len(netdev)); | |
409 | ||
b6bd9b54 | 410 | regs->version = 2; |
d1a890fa SB |
411 | |
412 | /* Update vmxnet3_get_regs_len if we want to dump more registers */ | |
413 | ||
b6bd9b54 SK |
414 | buf[j++] = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_VRRS); |
415 | buf[j++] = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_UVRS); | |
416 | buf[j++] = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_DSAL); | |
417 | buf[j++] = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_DSAH); | |
418 | buf[j++] = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_CMD); | |
419 | buf[j++] = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_MACL); | |
420 | buf[j++] = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_MACH); | |
421 | buf[j++] = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_ICR); | |
422 | buf[j++] = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_ECR); | |
423 | ||
424 | buf[j++] = adapter->intr.num_intrs; | |
425 | for (i = 0; i < adapter->intr.num_intrs; i++) { | |
426 | buf[j++] = VMXNET3_READ_BAR0_REG(adapter, VMXNET3_REG_IMR | |
427 | + i * VMXNET3_REG_ALIGN); | |
428 | } | |
76d39dae | 429 | |
b6bd9b54 SK |
430 | buf[j++] = adapter->num_tx_queues; |
431 | for (i = 0; i < adapter->num_tx_queues; i++) { | |
432 | struct vmxnet3_tx_queue *tq = &adapter->tx_queue[i]; | |
433 | ||
434 | buf[j++] = VMXNET3_READ_BAR0_REG(adapter, VMXNET3_REG_TXPROD + | |
435 | i * VMXNET3_REG_ALIGN); | |
436 | ||
437 | buf[j++] = VMXNET3_GET_ADDR_LO(tq->tx_ring.basePA); | |
438 | buf[j++] = VMXNET3_GET_ADDR_HI(tq->tx_ring.basePA); | |
439 | buf[j++] = tq->tx_ring.size; | |
440 | buf[j++] = tq->tx_ring.next2fill; | |
441 | buf[j++] = tq->tx_ring.next2comp; | |
442 | buf[j++] = tq->tx_ring.gen; | |
443 | ||
444 | buf[j++] = VMXNET3_GET_ADDR_LO(tq->data_ring.basePA); | |
445 | buf[j++] = VMXNET3_GET_ADDR_HI(tq->data_ring.basePA); | |
446 | buf[j++] = tq->data_ring.size; | |
3c8b3efc | 447 | buf[j++] = tq->txdata_desc_size; |
b6bd9b54 SK |
448 | |
449 | buf[j++] = VMXNET3_GET_ADDR_LO(tq->comp_ring.basePA); | |
450 | buf[j++] = VMXNET3_GET_ADDR_HI(tq->comp_ring.basePA); | |
451 | buf[j++] = tq->comp_ring.size; | |
452 | buf[j++] = tq->comp_ring.next2proc; | |
453 | buf[j++] = tq->comp_ring.gen; | |
454 | ||
455 | buf[j++] = tq->stopped; | |
76d39dae SB |
456 | } |
457 | ||
b6bd9b54 | 458 | buf[j++] = adapter->num_rx_queues; |
76d39dae | 459 | for (i = 0; i < adapter->num_rx_queues; i++) { |
b6bd9b54 SK |
460 | struct vmxnet3_rx_queue *rq = &adapter->rx_queue[i]; |
461 | ||
462 | buf[j++] = VMXNET3_READ_BAR0_REG(adapter, VMXNET3_REG_RXPROD + | |
463 | i * VMXNET3_REG_ALIGN); | |
464 | buf[j++] = VMXNET3_READ_BAR0_REG(adapter, VMXNET3_REG_RXPROD2 + | |
465 | i * VMXNET3_REG_ALIGN); | |
466 | ||
467 | buf[j++] = VMXNET3_GET_ADDR_LO(rq->rx_ring[0].basePA); | |
468 | buf[j++] = VMXNET3_GET_ADDR_HI(rq->rx_ring[0].basePA); | |
469 | buf[j++] = rq->rx_ring[0].size; | |
470 | buf[j++] = rq->rx_ring[0].next2fill; | |
471 | buf[j++] = rq->rx_ring[0].next2comp; | |
472 | buf[j++] = rq->rx_ring[0].gen; | |
473 | ||
474 | buf[j++] = VMXNET3_GET_ADDR_LO(rq->rx_ring[1].basePA); | |
475 | buf[j++] = VMXNET3_GET_ADDR_HI(rq->rx_ring[1].basePA); | |
476 | buf[j++] = rq->rx_ring[1].size; | |
477 | buf[j++] = rq->rx_ring[1].next2fill; | |
478 | buf[j++] = rq->rx_ring[1].next2comp; | |
479 | buf[j++] = rq->rx_ring[1].gen; | |
480 | ||
50a5ce3e SK |
481 | buf[j++] = VMXNET3_GET_ADDR_LO(rq->data_ring.basePA); |
482 | buf[j++] = VMXNET3_GET_ADDR_HI(rq->data_ring.basePA); | |
483 | buf[j++] = rq->rx_ring[0].size; | |
484 | buf[j++] = rq->data_ring.desc_size; | |
76d39dae | 485 | |
b6bd9b54 SK |
486 | buf[j++] = VMXNET3_GET_ADDR_LO(rq->comp_ring.basePA); |
487 | buf[j++] = VMXNET3_GET_ADDR_HI(rq->comp_ring.basePA); | |
488 | buf[j++] = rq->comp_ring.size; | |
489 | buf[j++] = rq->comp_ring.next2proc; | |
490 | buf[j++] = rq->comp_ring.gen; | |
491 | } | |
d1a890fa SB |
492 | } |
493 | ||
494 | ||
495 | static void | |
496 | vmxnet3_get_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) | |
497 | { | |
498 | struct vmxnet3_adapter *adapter = netdev_priv(netdev); | |
499 | ||
500 | wol->supported = WAKE_UCAST | WAKE_ARP | WAKE_MAGIC; | |
501 | wol->wolopts = adapter->wol; | |
502 | } | |
503 | ||
504 | ||
505 | static int | |
506 | vmxnet3_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) | |
507 | { | |
508 | struct vmxnet3_adapter *adapter = netdev_priv(netdev); | |
509 | ||
510 | if (wol->wolopts & (WAKE_PHY | WAKE_MCAST | WAKE_BCAST | | |
511 | WAKE_MAGICSECURE)) { | |
512 | return -EOPNOTSUPP; | |
513 | } | |
514 | ||
515 | adapter->wol = wol->wolopts; | |
516 | ||
517 | device_set_wakeup_enable(&adapter->pdev->dev, adapter->wol); | |
518 | ||
519 | return 0; | |
520 | } | |
521 | ||
522 | ||
523 | static int | |
3426bd72 PR |
524 | vmxnet3_get_link_ksettings(struct net_device *netdev, |
525 | struct ethtool_link_ksettings *ecmd) | |
d1a890fa SB |
526 | { |
527 | struct vmxnet3_adapter *adapter = netdev_priv(netdev); | |
528 | ||
3426bd72 PR |
529 | ethtool_link_ksettings_zero_link_mode(ecmd, supported); |
530 | ethtool_link_ksettings_add_link_mode(ecmd, supported, 10000baseT_Full); | |
531 | ethtool_link_ksettings_add_link_mode(ecmd, supported, 1000baseT_Full); | |
532 | ethtool_link_ksettings_add_link_mode(ecmd, supported, TP); | |
533 | ethtool_link_ksettings_zero_link_mode(ecmd, advertising); | |
534 | ethtool_link_ksettings_add_link_mode(ecmd, advertising, TP); | |
535 | ecmd->base.port = PORT_TP; | |
d1a890fa SB |
536 | |
537 | if (adapter->link_speed) { | |
3426bd72 PR |
538 | ecmd->base.speed = adapter->link_speed; |
539 | ecmd->base.duplex = DUPLEX_FULL; | |
d1a890fa | 540 | } else { |
3426bd72 PR |
541 | ecmd->base.speed = SPEED_UNKNOWN; |
542 | ecmd->base.duplex = DUPLEX_UNKNOWN; | |
d1a890fa SB |
543 | } |
544 | return 0; | |
545 | } | |
546 | ||
547 | ||
548 | static void | |
549 | vmxnet3_get_ringparam(struct net_device *netdev, | |
550 | struct ethtool_ringparam *param) | |
551 | { | |
552 | struct vmxnet3_adapter *adapter = netdev_priv(netdev); | |
553 | ||
554 | param->rx_max_pending = VMXNET3_RX_RING_MAX_SIZE; | |
555 | param->tx_max_pending = VMXNET3_TX_RING_MAX_SIZE; | |
50a5ce3e SK |
556 | param->rx_mini_max_pending = VMXNET3_VERSION_GE_3(adapter) ? |
557 | VMXNET3_RXDATA_DESC_MAX_SIZE : 0; | |
53831aa1 | 558 | param->rx_jumbo_max_pending = VMXNET3_RX_RING2_MAX_SIZE; |
d1a890fa | 559 | |
f00e2b0a NH |
560 | param->rx_pending = adapter->rx_ring_size; |
561 | param->tx_pending = adapter->tx_ring_size; | |
50a5ce3e SK |
562 | param->rx_mini_pending = VMXNET3_VERSION_GE_3(adapter) ? |
563 | adapter->rxdata_desc_size : 0; | |
53831aa1 | 564 | param->rx_jumbo_pending = adapter->rx_ring2_size; |
d1a890fa SB |
565 | } |
566 | ||
567 | ||
568 | static int | |
569 | vmxnet3_set_ringparam(struct net_device *netdev, | |
570 | struct ethtool_ringparam *param) | |
571 | { | |
572 | struct vmxnet3_adapter *adapter = netdev_priv(netdev); | |
53831aa1 | 573 | u32 new_tx_ring_size, new_rx_ring_size, new_rx_ring2_size; |
50a5ce3e | 574 | u16 new_rxdata_desc_size; |
d1a890fa SB |
575 | u32 sz; |
576 | int err = 0; | |
577 | ||
578 | if (param->tx_pending == 0 || param->tx_pending > | |
579 | VMXNET3_TX_RING_MAX_SIZE) | |
580 | return -EINVAL; | |
581 | ||
582 | if (param->rx_pending == 0 || param->rx_pending > | |
583 | VMXNET3_RX_RING_MAX_SIZE) | |
584 | return -EINVAL; | |
585 | ||
53831aa1 SK |
586 | if (param->rx_jumbo_pending == 0 || |
587 | param->rx_jumbo_pending > VMXNET3_RX_RING2_MAX_SIZE) | |
588 | return -EINVAL; | |
589 | ||
e4fabf2b BD |
590 | /* if adapter not yet initialized, do nothing */ |
591 | if (adapter->rx_buf_per_pkt == 0) { | |
592 | netdev_err(netdev, "adapter not completely initialized, " | |
593 | "ring size cannot be changed yet\n"); | |
594 | return -EOPNOTSUPP; | |
595 | } | |
d1a890fa | 596 | |
50a5ce3e | 597 | if (VMXNET3_VERSION_GE_3(adapter)) { |
4a4a52d4 | 598 | if (param->rx_mini_pending > VMXNET3_RXDATA_DESC_MAX_SIZE) |
50a5ce3e | 599 | return -EINVAL; |
50a5ce3e SK |
600 | } else if (param->rx_mini_pending != 0) { |
601 | return -EINVAL; | |
602 | } | |
603 | ||
d1a890fa SB |
604 | /* round it up to a multiple of VMXNET3_RING_SIZE_ALIGN */ |
605 | new_tx_ring_size = (param->tx_pending + VMXNET3_RING_SIZE_MASK) & | |
606 | ~VMXNET3_RING_SIZE_MASK; | |
607 | new_tx_ring_size = min_t(u32, new_tx_ring_size, | |
608 | VMXNET3_TX_RING_MAX_SIZE); | |
609 | if (new_tx_ring_size > VMXNET3_TX_RING_MAX_SIZE || (new_tx_ring_size % | |
610 | VMXNET3_RING_SIZE_ALIGN) != 0) | |
611 | return -EINVAL; | |
612 | ||
613 | /* ring0 has to be a multiple of | |
614 | * rx_buf_per_pkt * VMXNET3_RING_SIZE_ALIGN | |
615 | */ | |
616 | sz = adapter->rx_buf_per_pkt * VMXNET3_RING_SIZE_ALIGN; | |
617 | new_rx_ring_size = (param->rx_pending + sz - 1) / sz * sz; | |
618 | new_rx_ring_size = min_t(u32, new_rx_ring_size, | |
619 | VMXNET3_RX_RING_MAX_SIZE / sz * sz); | |
620 | if (new_rx_ring_size > VMXNET3_RX_RING_MAX_SIZE || (new_rx_ring_size % | |
621 | sz) != 0) | |
622 | return -EINVAL; | |
623 | ||
53831aa1 SK |
624 | /* ring2 has to be a multiple of VMXNET3_RING_SIZE_ALIGN */ |
625 | new_rx_ring2_size = (param->rx_jumbo_pending + VMXNET3_RING_SIZE_MASK) & | |
626 | ~VMXNET3_RING_SIZE_MASK; | |
627 | new_rx_ring2_size = min_t(u32, new_rx_ring2_size, | |
628 | VMXNET3_RX_RING2_MAX_SIZE); | |
629 | ||
50a5ce3e SK |
630 | /* rx data ring buffer size has to be a multiple of |
631 | * VMXNET3_RXDATA_DESC_SIZE_ALIGN | |
632 | */ | |
633 | new_rxdata_desc_size = | |
634 | (param->rx_mini_pending + VMXNET3_RXDATA_DESC_SIZE_MASK) & | |
635 | ~VMXNET3_RXDATA_DESC_SIZE_MASK; | |
636 | new_rxdata_desc_size = min_t(u16, new_rxdata_desc_size, | |
637 | VMXNET3_RXDATA_DESC_MAX_SIZE); | |
638 | ||
53831aa1 SK |
639 | if (new_tx_ring_size == adapter->tx_ring_size && |
640 | new_rx_ring_size == adapter->rx_ring_size && | |
50a5ce3e SK |
641 | new_rx_ring2_size == adapter->rx_ring2_size && |
642 | new_rxdata_desc_size == adapter->rxdata_desc_size) { | |
d1a890fa SB |
643 | return 0; |
644 | } | |
645 | ||
646 | /* | |
647 | * Reset_work may be in the middle of resetting the device, wait for its | |
648 | * completion. | |
649 | */ | |
650 | while (test_and_set_bit(VMXNET3_STATE_BIT_RESETTING, &adapter->state)) | |
93c65d13 | 651 | usleep_range(1000, 2000); |
d1a890fa SB |
652 | |
653 | if (netif_running(netdev)) { | |
654 | vmxnet3_quiesce_dev(adapter); | |
655 | vmxnet3_reset_dev(adapter); | |
656 | ||
657 | /* recreate the rx queue and the tx queue based on the | |
658 | * new sizes */ | |
09c5088e SB |
659 | vmxnet3_tq_destroy_all(adapter); |
660 | vmxnet3_rq_destroy_all(adapter); | |
d1a890fa SB |
661 | |
662 | err = vmxnet3_create_queues(adapter, new_tx_ring_size, | |
3c8b3efc | 663 | new_rx_ring_size, new_rx_ring2_size, |
50a5ce3e SK |
664 | adapter->txdata_desc_size, |
665 | new_rxdata_desc_size); | |
d1a890fa SB |
666 | if (err) { |
667 | /* failed, most likely because of OOM, try default | |
668 | * size */ | |
204a6e65 SH |
669 | netdev_err(netdev, "failed to apply new sizes, " |
670 | "try the default ones\n"); | |
f00e2b0a | 671 | new_rx_ring_size = VMXNET3_DEF_RX_RING_SIZE; |
53831aa1 | 672 | new_rx_ring2_size = VMXNET3_DEF_RX_RING2_SIZE; |
f00e2b0a | 673 | new_tx_ring_size = VMXNET3_DEF_TX_RING_SIZE; |
50a5ce3e SK |
674 | new_rxdata_desc_size = VMXNET3_VERSION_GE_3(adapter) ? |
675 | VMXNET3_DEF_RXDATA_DESC_SIZE : 0; | |
676 | ||
d1a890fa | 677 | err = vmxnet3_create_queues(adapter, |
f00e2b0a NH |
678 | new_tx_ring_size, |
679 | new_rx_ring_size, | |
3c8b3efc | 680 | new_rx_ring2_size, |
50a5ce3e SK |
681 | adapter->txdata_desc_size, |
682 | new_rxdata_desc_size); | |
d1a890fa | 683 | if (err) { |
204a6e65 SH |
684 | netdev_err(netdev, "failed to create queues " |
685 | "with default sizes. Closing it\n"); | |
d1a890fa SB |
686 | goto out; |
687 | } | |
688 | } | |
689 | ||
690 | err = vmxnet3_activate_dev(adapter); | |
691 | if (err) | |
204a6e65 SH |
692 | netdev_err(netdev, "failed to re-activate, error %d." |
693 | " Closing it\n", err); | |
d1a890fa | 694 | } |
f00e2b0a NH |
695 | adapter->tx_ring_size = new_tx_ring_size; |
696 | adapter->rx_ring_size = new_rx_ring_size; | |
53831aa1 | 697 | adapter->rx_ring2_size = new_rx_ring2_size; |
50a5ce3e | 698 | adapter->rxdata_desc_size = new_rxdata_desc_size; |
d1a890fa SB |
699 | |
700 | out: | |
701 | clear_bit(VMXNET3_STATE_BIT_RESETTING, &adapter->state); | |
702 | if (err) | |
703 | vmxnet3_force_close(adapter); | |
704 | ||
705 | return err; | |
706 | } | |
707 | ||
d3a8a9e5 RD |
708 | static int |
709 | vmxnet3_get_rss_hash_opts(struct vmxnet3_adapter *adapter, | |
710 | struct ethtool_rxnfc *info) | |
711 | { | |
712 | enum Vmxnet3_RSSField rss_fields; | |
713 | ||
714 | if (netif_running(adapter->netdev)) { | |
715 | unsigned long flags; | |
716 | ||
717 | spin_lock_irqsave(&adapter->cmd_lock, flags); | |
718 | ||
719 | VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, | |
720 | VMXNET3_CMD_GET_RSS_FIELDS); | |
721 | rss_fields = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_CMD); | |
722 | spin_unlock_irqrestore(&adapter->cmd_lock, flags); | |
723 | } else { | |
724 | rss_fields = adapter->rss_fields; | |
725 | } | |
726 | ||
727 | info->data = 0; | |
728 | ||
729 | /* Report default options for RSS on vmxnet3 */ | |
730 | switch (info->flow_type) { | |
731 | case TCP_V4_FLOW: | |
732 | case TCP_V6_FLOW: | |
733 | info->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3 | | |
734 | RXH_IP_SRC | RXH_IP_DST; | |
735 | break; | |
736 | case UDP_V4_FLOW: | |
737 | if (rss_fields & VMXNET3_RSS_FIELDS_UDPIP4) | |
738 | info->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; | |
739 | info->data |= RXH_IP_SRC | RXH_IP_DST; | |
740 | break; | |
741 | case AH_ESP_V4_FLOW: | |
742 | case AH_V4_FLOW: | |
743 | case ESP_V4_FLOW: | |
744 | if (rss_fields & VMXNET3_RSS_FIELDS_ESPIP4) | |
745 | info->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; | |
df561f66 | 746 | fallthrough; |
d3a8a9e5 RD |
747 | case SCTP_V4_FLOW: |
748 | case IPV4_FLOW: | |
749 | info->data |= RXH_IP_SRC | RXH_IP_DST; | |
750 | break; | |
751 | case UDP_V6_FLOW: | |
752 | if (rss_fields & VMXNET3_RSS_FIELDS_UDPIP6) | |
753 | info->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; | |
754 | info->data |= RXH_IP_SRC | RXH_IP_DST; | |
755 | break; | |
756 | case AH_ESP_V6_FLOW: | |
757 | case AH_V6_FLOW: | |
758 | case ESP_V6_FLOW: | |
759 | case SCTP_V6_FLOW: | |
760 | case IPV6_FLOW: | |
761 | info->data |= RXH_IP_SRC | RXH_IP_DST; | |
762 | break; | |
763 | default: | |
764 | return -EINVAL; | |
765 | } | |
766 | ||
767 | return 0; | |
768 | } | |
769 | ||
770 | static int | |
771 | vmxnet3_set_rss_hash_opt(struct net_device *netdev, | |
772 | struct vmxnet3_adapter *adapter, | |
773 | struct ethtool_rxnfc *nfc) | |
774 | { | |
775 | enum Vmxnet3_RSSField rss_fields = adapter->rss_fields; | |
776 | ||
777 | /* RSS does not support anything other than hashing | |
778 | * to queues on src and dst IPs and ports | |
779 | */ | |
780 | if (nfc->data & ~(RXH_IP_SRC | RXH_IP_DST | | |
781 | RXH_L4_B_0_1 | RXH_L4_B_2_3)) | |
782 | return -EINVAL; | |
783 | ||
784 | switch (nfc->flow_type) { | |
785 | case TCP_V4_FLOW: | |
786 | case TCP_V6_FLOW: | |
787 | if (!(nfc->data & RXH_IP_SRC) || | |
788 | !(nfc->data & RXH_IP_DST) || | |
789 | !(nfc->data & RXH_L4_B_0_1) || | |
790 | !(nfc->data & RXH_L4_B_2_3)) | |
791 | return -EINVAL; | |
792 | break; | |
793 | case UDP_V4_FLOW: | |
794 | if (!(nfc->data & RXH_IP_SRC) || | |
795 | !(nfc->data & RXH_IP_DST)) | |
796 | return -EINVAL; | |
797 | switch (nfc->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) { | |
798 | case 0: | |
799 | rss_fields &= ~VMXNET3_RSS_FIELDS_UDPIP4; | |
800 | break; | |
801 | case (RXH_L4_B_0_1 | RXH_L4_B_2_3): | |
802 | rss_fields |= VMXNET3_RSS_FIELDS_UDPIP4; | |
803 | break; | |
804 | default: | |
805 | return -EINVAL; | |
806 | } | |
807 | break; | |
808 | case UDP_V6_FLOW: | |
809 | if (!(nfc->data & RXH_IP_SRC) || | |
810 | !(nfc->data & RXH_IP_DST)) | |
811 | return -EINVAL; | |
812 | switch (nfc->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) { | |
813 | case 0: | |
814 | rss_fields &= ~VMXNET3_RSS_FIELDS_UDPIP6; | |
815 | break; | |
816 | case (RXH_L4_B_0_1 | RXH_L4_B_2_3): | |
817 | rss_fields |= VMXNET3_RSS_FIELDS_UDPIP6; | |
818 | break; | |
819 | default: | |
820 | return -EINVAL; | |
821 | } | |
822 | break; | |
823 | case ESP_V4_FLOW: | |
824 | case AH_V4_FLOW: | |
825 | case AH_ESP_V4_FLOW: | |
826 | if (!(nfc->data & RXH_IP_SRC) || | |
827 | !(nfc->data & RXH_IP_DST)) | |
828 | return -EINVAL; | |
829 | switch (nfc->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) { | |
830 | case 0: | |
831 | rss_fields &= ~VMXNET3_RSS_FIELDS_ESPIP4; | |
832 | break; | |
833 | case (RXH_L4_B_0_1 | RXH_L4_B_2_3): | |
834 | rss_fields |= VMXNET3_RSS_FIELDS_ESPIP4; | |
835 | break; | |
836 | default: | |
837 | return -EINVAL; | |
838 | } | |
839 | break; | |
840 | case ESP_V6_FLOW: | |
841 | case AH_V6_FLOW: | |
842 | case AH_ESP_V6_FLOW: | |
843 | case SCTP_V4_FLOW: | |
844 | case SCTP_V6_FLOW: | |
845 | if (!(nfc->data & RXH_IP_SRC) || | |
846 | !(nfc->data & RXH_IP_DST) || | |
847 | (nfc->data & RXH_L4_B_0_1) || | |
848 | (nfc->data & RXH_L4_B_2_3)) | |
849 | return -EINVAL; | |
850 | break; | |
851 | default: | |
852 | return -EINVAL; | |
853 | } | |
854 | ||
855 | /* if we changed something we need to update flags */ | |
856 | if (rss_fields != adapter->rss_fields) { | |
857 | adapter->default_rss_fields = false; | |
858 | if (netif_running(netdev)) { | |
859 | struct Vmxnet3_DriverShared *shared = adapter->shared; | |
860 | union Vmxnet3_CmdInfo *cmdInfo = &shared->cu.cmdInfo; | |
861 | unsigned long flags; | |
862 | ||
863 | spin_lock_irqsave(&adapter->cmd_lock, flags); | |
864 | cmdInfo->setRssFields = rss_fields; | |
865 | VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, | |
866 | VMXNET3_CMD_SET_RSS_FIELDS); | |
867 | ||
868 | /* Not all requested RSS may get applied, so get and | |
869 | * cache what was actually applied. | |
870 | */ | |
871 | VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, | |
872 | VMXNET3_CMD_GET_RSS_FIELDS); | |
873 | adapter->rss_fields = | |
874 | VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_CMD); | |
875 | spin_unlock_irqrestore(&adapter->cmd_lock, flags); | |
876 | } else { | |
877 | /* When the device is activated, we will try to apply | |
878 | * these rules and cache the applied value later. | |
879 | */ | |
880 | adapter->rss_fields = rss_fields; | |
881 | } | |
882 | } | |
883 | return 0; | |
884 | } | |
d1a890fa | 885 | |
09c5088e SB |
886 | static int |
887 | vmxnet3_get_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *info, | |
815c7db5 | 888 | u32 *rules) |
09c5088e SB |
889 | { |
890 | struct vmxnet3_adapter *adapter = netdev_priv(netdev); | |
d3a8a9e5 RD |
891 | int err = 0; |
892 | ||
09c5088e SB |
893 | switch (info->cmd) { |
894 | case ETHTOOL_GRXRINGS: | |
895 | info->data = adapter->num_rx_queues; | |
d3a8a9e5 RD |
896 | break; |
897 | case ETHTOOL_GRXFH: | |
898 | if (!VMXNET3_VERSION_GE_4(adapter)) { | |
899 | err = -EOPNOTSUPP; | |
900 | break; | |
901 | } | |
11e877b2 RD |
902 | #ifdef VMXNET3_RSS |
903 | if (!adapter->rss) { | |
904 | err = -EOPNOTSUPP; | |
905 | break; | |
906 | } | |
907 | #endif | |
d3a8a9e5 RD |
908 | err = vmxnet3_get_rss_hash_opts(adapter, info); |
909 | break; | |
910 | default: | |
911 | err = -EOPNOTSUPP; | |
912 | break; | |
09c5088e | 913 | } |
d3a8a9e5 RD |
914 | |
915 | return err; | |
916 | } | |
917 | ||
918 | static int | |
919 | vmxnet3_set_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *info) | |
920 | { | |
921 | struct vmxnet3_adapter *adapter = netdev_priv(netdev); | |
922 | int err = 0; | |
923 | ||
924 | if (!VMXNET3_VERSION_GE_4(adapter)) { | |
925 | err = -EOPNOTSUPP; | |
926 | goto done; | |
927 | } | |
11e877b2 RD |
928 | #ifdef VMXNET3_RSS |
929 | if (!adapter->rss) { | |
930 | err = -EOPNOTSUPP; | |
931 | goto done; | |
932 | } | |
933 | #endif | |
d3a8a9e5 RD |
934 | |
935 | switch (info->cmd) { | |
936 | case ETHTOOL_SRXFH: | |
937 | err = vmxnet3_set_rss_hash_opt(netdev, adapter, info); | |
938 | break; | |
939 | default: | |
940 | err = -EOPNOTSUPP; | |
941 | break; | |
942 | } | |
943 | ||
944 | done: | |
945 | return err; | |
09c5088e SB |
946 | } |
947 | ||
e9248fbd | 948 | #ifdef VMXNET3_RSS |
7850f63f BH |
949 | static u32 |
950 | vmxnet3_get_rss_indir_size(struct net_device *netdev) | |
951 | { | |
952 | struct vmxnet3_adapter *adapter = netdev_priv(netdev); | |
953 | struct UPT1_RSSConf *rssConf = adapter->rss_conf; | |
954 | ||
955 | return rssConf->indTableSize; | |
956 | } | |
957 | ||
09c5088e | 958 | static int |
892311f6 | 959 | vmxnet3_get_rss(struct net_device *netdev, u32 *p, u8 *key, u8 *hfunc) |
09c5088e SB |
960 | { |
961 | struct vmxnet3_adapter *adapter = netdev_priv(netdev); | |
962 | struct UPT1_RSSConf *rssConf = adapter->rss_conf; | |
7850f63f | 963 | unsigned int n = rssConf->indTableSize; |
09c5088e | 964 | |
892311f6 EP |
965 | if (hfunc) |
966 | *hfunc = ETH_RSS_HASH_TOP; | |
967 | if (!p) | |
968 | return 0; | |
3e1c6846 JJB |
969 | if (n > UPT1_RSS_MAX_IND_TABLE_SIZE) |
970 | return 0; | |
09c5088e | 971 | while (n--) |
7850f63f | 972 | p[n] = rssConf->indTable[n]; |
09c5088e SB |
973 | return 0; |
974 | ||
975 | } | |
976 | ||
977 | static int | |
892311f6 EP |
978 | vmxnet3_set_rss(struct net_device *netdev, const u32 *p, const u8 *key, |
979 | const u8 hfunc) | |
09c5088e SB |
980 | { |
981 | unsigned int i; | |
83d0feff | 982 | unsigned long flags; |
09c5088e SB |
983 | struct vmxnet3_adapter *adapter = netdev_priv(netdev); |
984 | struct UPT1_RSSConf *rssConf = adapter->rss_conf; | |
985 | ||
892311f6 EP |
986 | /* We do not allow change in unsupported parameters */ |
987 | if (key || | |
988 | (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP)) | |
989 | return -EOPNOTSUPP; | |
990 | if (!p) | |
991 | return 0; | |
09c5088e | 992 | for (i = 0; i < rssConf->indTableSize; i++) |
7850f63f | 993 | rssConf->indTable[i] = p[i]; |
09c5088e | 994 | |
83d0feff | 995 | spin_lock_irqsave(&adapter->cmd_lock, flags); |
09c5088e SB |
996 | VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, |
997 | VMXNET3_CMD_UPDATE_RSSIDT); | |
83d0feff | 998 | spin_unlock_irqrestore(&adapter->cmd_lock, flags); |
09c5088e SB |
999 | |
1000 | return 0; | |
1001 | ||
1002 | } | |
e9248fbd | 1003 | #endif |
09c5088e | 1004 | |
4edef40e SK |
1005 | static int |
1006 | vmxnet3_get_coalesce(struct net_device *netdev, struct ethtool_coalesce *ec) | |
1007 | { | |
1008 | struct vmxnet3_adapter *adapter = netdev_priv(netdev); | |
1009 | ||
1010 | if (!VMXNET3_VERSION_GE_3(adapter)) | |
1011 | return -EOPNOTSUPP; | |
1012 | ||
1013 | switch (adapter->coal_conf->coalMode) { | |
1014 | case VMXNET3_COALESCE_DISABLED: | |
1015 | /* struct ethtool_coalesce is already initialized to 0 */ | |
1016 | break; | |
1017 | case VMXNET3_COALESCE_ADAPT: | |
1018 | ec->use_adaptive_rx_coalesce = true; | |
1019 | break; | |
1020 | case VMXNET3_COALESCE_STATIC: | |
1021 | ec->tx_max_coalesced_frames = | |
1022 | adapter->coal_conf->coalPara.coalStatic.tx_comp_depth; | |
1023 | ec->rx_max_coalesced_frames = | |
1024 | adapter->coal_conf->coalPara.coalStatic.rx_depth; | |
1025 | break; | |
1026 | case VMXNET3_COALESCE_RBC: { | |
1027 | u32 rbc_rate; | |
1028 | ||
1029 | rbc_rate = adapter->coal_conf->coalPara.coalRbc.rbc_rate; | |
1030 | ec->rx_coalesce_usecs = VMXNET3_COAL_RBC_USECS(rbc_rate); | |
1031 | } | |
1032 | break; | |
1033 | default: | |
1034 | return -EOPNOTSUPP; | |
1035 | } | |
1036 | ||
1037 | return 0; | |
1038 | } | |
1039 | ||
1040 | static int | |
1041 | vmxnet3_set_coalesce(struct net_device *netdev, struct ethtool_coalesce *ec) | |
1042 | { | |
1043 | struct vmxnet3_adapter *adapter = netdev_priv(netdev); | |
1044 | struct Vmxnet3_DriverShared *shared = adapter->shared; | |
1045 | union Vmxnet3_CmdInfo *cmdInfo = &shared->cu.cmdInfo; | |
1046 | unsigned long flags; | |
1047 | ||
1048 | if (!VMXNET3_VERSION_GE_3(adapter)) | |
1049 | return -EOPNOTSUPP; | |
1050 | ||
4edef40e SK |
1051 | if ((ec->rx_coalesce_usecs == 0) && |
1052 | (ec->use_adaptive_rx_coalesce == 0) && | |
1053 | (ec->tx_max_coalesced_frames == 0) && | |
1054 | (ec->rx_max_coalesced_frames == 0)) { | |
1055 | memset(adapter->coal_conf, 0, sizeof(*adapter->coal_conf)); | |
1056 | adapter->coal_conf->coalMode = VMXNET3_COALESCE_DISABLED; | |
1057 | goto done; | |
1058 | } | |
1059 | ||
1060 | if (ec->rx_coalesce_usecs != 0) { | |
1061 | u32 rbc_rate; | |
1062 | ||
1063 | if ((ec->use_adaptive_rx_coalesce != 0) || | |
1064 | (ec->tx_max_coalesced_frames != 0) || | |
1065 | (ec->rx_max_coalesced_frames != 0)) { | |
1066 | return -EINVAL; | |
1067 | } | |
1068 | ||
1069 | rbc_rate = VMXNET3_COAL_RBC_RATE(ec->rx_coalesce_usecs); | |
1070 | if (rbc_rate < VMXNET3_COAL_RBC_MIN_RATE || | |
1071 | rbc_rate > VMXNET3_COAL_RBC_MAX_RATE) { | |
1072 | return -EINVAL; | |
1073 | } | |
1074 | ||
1075 | memset(adapter->coal_conf, 0, sizeof(*adapter->coal_conf)); | |
1076 | adapter->coal_conf->coalMode = VMXNET3_COALESCE_RBC; | |
1077 | adapter->coal_conf->coalPara.coalRbc.rbc_rate = rbc_rate; | |
1078 | goto done; | |
1079 | } | |
1080 | ||
1081 | if (ec->use_adaptive_rx_coalesce != 0) { | |
1082 | if ((ec->rx_coalesce_usecs != 0) || | |
1083 | (ec->tx_max_coalesced_frames != 0) || | |
1084 | (ec->rx_max_coalesced_frames != 0)) { | |
1085 | return -EINVAL; | |
1086 | } | |
1087 | memset(adapter->coal_conf, 0, sizeof(*adapter->coal_conf)); | |
1088 | adapter->coal_conf->coalMode = VMXNET3_COALESCE_ADAPT; | |
1089 | goto done; | |
1090 | } | |
1091 | ||
1092 | if ((ec->tx_max_coalesced_frames != 0) || | |
1093 | (ec->rx_max_coalesced_frames != 0)) { | |
1094 | if ((ec->rx_coalesce_usecs != 0) || | |
1095 | (ec->use_adaptive_rx_coalesce != 0)) { | |
1096 | return -EINVAL; | |
1097 | } | |
1098 | ||
1099 | if ((ec->tx_max_coalesced_frames > | |
1100 | VMXNET3_COAL_STATIC_MAX_DEPTH) || | |
1101 | (ec->rx_max_coalesced_frames > | |
1102 | VMXNET3_COAL_STATIC_MAX_DEPTH)) { | |
1103 | return -EINVAL; | |
1104 | } | |
1105 | ||
1106 | memset(adapter->coal_conf, 0, sizeof(*adapter->coal_conf)); | |
1107 | adapter->coal_conf->coalMode = VMXNET3_COALESCE_STATIC; | |
1108 | ||
1109 | adapter->coal_conf->coalPara.coalStatic.tx_comp_depth = | |
1110 | (ec->tx_max_coalesced_frames ? | |
1111 | ec->tx_max_coalesced_frames : | |
1112 | VMXNET3_COAL_STATIC_DEFAULT_DEPTH); | |
1113 | ||
1114 | adapter->coal_conf->coalPara.coalStatic.rx_depth = | |
1115 | (ec->rx_max_coalesced_frames ? | |
1116 | ec->rx_max_coalesced_frames : | |
1117 | VMXNET3_COAL_STATIC_DEFAULT_DEPTH); | |
1118 | ||
1119 | adapter->coal_conf->coalPara.coalStatic.tx_depth = | |
1120 | VMXNET3_COAL_STATIC_DEFAULT_DEPTH; | |
1121 | goto done; | |
1122 | } | |
1123 | ||
1124 | done: | |
1125 | adapter->default_coal_mode = false; | |
1126 | if (netif_running(netdev)) { | |
1127 | spin_lock_irqsave(&adapter->cmd_lock, flags); | |
1128 | cmdInfo->varConf.confVer = 1; | |
1129 | cmdInfo->varConf.confLen = | |
1130 | cpu_to_le32(sizeof(*adapter->coal_conf)); | |
1131 | cmdInfo->varConf.confPA = cpu_to_le64(adapter->coal_conf_pa); | |
1132 | VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, | |
1133 | VMXNET3_CMD_SET_COALESCE); | |
1134 | spin_unlock_irqrestore(&adapter->cmd_lock, flags); | |
1135 | } | |
1136 | ||
1137 | return 0; | |
1138 | } | |
1139 | ||
c8b88efc | 1140 | static const struct ethtool_ops vmxnet3_ethtool_ops = { |
0f3883b4 JK |
1141 | .supported_coalesce_params = ETHTOOL_COALESCE_RX_USECS | |
1142 | ETHTOOL_COALESCE_MAX_FRAMES | | |
1143 | ETHTOOL_COALESCE_USE_ADAPTIVE_RX, | |
d1a890fa SB |
1144 | .get_drvinfo = vmxnet3_get_drvinfo, |
1145 | .get_regs_len = vmxnet3_get_regs_len, | |
1146 | .get_regs = vmxnet3_get_regs, | |
1147 | .get_wol = vmxnet3_get_wol, | |
1148 | .set_wol = vmxnet3_set_wol, | |
1149 | .get_link = ethtool_op_get_link, | |
4edef40e SK |
1150 | .get_coalesce = vmxnet3_get_coalesce, |
1151 | .set_coalesce = vmxnet3_set_coalesce, | |
d1a890fa | 1152 | .get_strings = vmxnet3_get_strings, |
d1a890fa SB |
1153 | .get_sset_count = vmxnet3_get_sset_count, |
1154 | .get_ethtool_stats = vmxnet3_get_ethtool_stats, | |
1155 | .get_ringparam = vmxnet3_get_ringparam, | |
1156 | .set_ringparam = vmxnet3_set_ringparam, | |
09c5088e | 1157 | .get_rxnfc = vmxnet3_get_rxnfc, |
d3a8a9e5 | 1158 | .set_rxnfc = vmxnet3_set_rxnfc, |
e9248fbd | 1159 | #ifdef VMXNET3_RSS |
7850f63f | 1160 | .get_rxfh_indir_size = vmxnet3_get_rss_indir_size, |
fe62d001 BH |
1161 | .get_rxfh = vmxnet3_get_rss, |
1162 | .set_rxfh = vmxnet3_set_rss, | |
e9248fbd | 1163 | #endif |
3426bd72 | 1164 | .get_link_ksettings = vmxnet3_get_link_ksettings, |
d1a890fa SB |
1165 | }; |
1166 | ||
1167 | void vmxnet3_set_ethtool_ops(struct net_device *netdev) | |
1168 | { | |
7ad24ea4 | 1169 | netdev->ethtool_ops = &vmxnet3_ethtool_ops; |
d1a890fa | 1170 | } |