Commit | Line | Data |
---|---|---|
f13bbc2f NP |
1 | /** |
2 | * Copyright 2013 Cisco Systems, Inc. All rights reserved. | |
3 | * | |
4 | * This program is free software; you may redistribute it and/or modify | |
5 | * it under the terms of the GNU General Public License as published by | |
6 | * the Free Software Foundation; version 2 of the License. | |
7 | * | |
8 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
9 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
10 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
11 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | |
12 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | |
13 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |
14 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
15 | * SOFTWARE. | |
16 | * | |
17 | */ | |
18 | ||
19 | #include <linux/netdevice.h> | |
20 | #include <linux/ethtool.h> | |
21 | ||
22 | #include "enic_res.h" | |
23 | #include "enic.h" | |
24 | #include "enic_dev.h" | |
3762ff8f | 25 | #include "enic_clsf.h" |
f13bbc2f NP |
26 | |
27 | struct enic_stat { | |
28 | char name[ETH_GSTRING_LEN]; | |
29 | unsigned int index; | |
30 | }; | |
31 | ||
32 | #define ENIC_TX_STAT(stat) { \ | |
33 | .name = #stat, \ | |
34 | .index = offsetof(struct vnic_tx_stats, stat) / sizeof(u64) \ | |
35 | } | |
36 | ||
37 | #define ENIC_RX_STAT(stat) { \ | |
38 | .name = #stat, \ | |
39 | .index = offsetof(struct vnic_rx_stats, stat) / sizeof(u64) \ | |
40 | } | |
41 | ||
42 | static const struct enic_stat enic_tx_stats[] = { | |
43 | ENIC_TX_STAT(tx_frames_ok), | |
44 | ENIC_TX_STAT(tx_unicast_frames_ok), | |
45 | ENIC_TX_STAT(tx_multicast_frames_ok), | |
46 | ENIC_TX_STAT(tx_broadcast_frames_ok), | |
47 | ENIC_TX_STAT(tx_bytes_ok), | |
48 | ENIC_TX_STAT(tx_unicast_bytes_ok), | |
49 | ENIC_TX_STAT(tx_multicast_bytes_ok), | |
50 | ENIC_TX_STAT(tx_broadcast_bytes_ok), | |
51 | ENIC_TX_STAT(tx_drops), | |
52 | ENIC_TX_STAT(tx_errors), | |
53 | ENIC_TX_STAT(tx_tso), | |
54 | }; | |
55 | ||
56 | static const struct enic_stat enic_rx_stats[] = { | |
57 | ENIC_RX_STAT(rx_frames_ok), | |
58 | ENIC_RX_STAT(rx_frames_total), | |
59 | ENIC_RX_STAT(rx_unicast_frames_ok), | |
60 | ENIC_RX_STAT(rx_multicast_frames_ok), | |
61 | ENIC_RX_STAT(rx_broadcast_frames_ok), | |
62 | ENIC_RX_STAT(rx_bytes_ok), | |
63 | ENIC_RX_STAT(rx_unicast_bytes_ok), | |
64 | ENIC_RX_STAT(rx_multicast_bytes_ok), | |
65 | ENIC_RX_STAT(rx_broadcast_bytes_ok), | |
66 | ENIC_RX_STAT(rx_drop), | |
67 | ENIC_RX_STAT(rx_no_bufs), | |
68 | ENIC_RX_STAT(rx_errors), | |
69 | ENIC_RX_STAT(rx_rss), | |
70 | ENIC_RX_STAT(rx_crc_errors), | |
71 | ENIC_RX_STAT(rx_frames_64), | |
72 | ENIC_RX_STAT(rx_frames_127), | |
73 | ENIC_RX_STAT(rx_frames_255), | |
74 | ENIC_RX_STAT(rx_frames_511), | |
75 | ENIC_RX_STAT(rx_frames_1023), | |
76 | ENIC_RX_STAT(rx_frames_1518), | |
77 | ENIC_RX_STAT(rx_frames_to_max), | |
78 | }; | |
79 | ||
80 | static const unsigned int enic_n_tx_stats = ARRAY_SIZE(enic_tx_stats); | |
81 | static const unsigned int enic_n_rx_stats = ARRAY_SIZE(enic_rx_stats); | |
82 | ||
7c2ce6e6 SS |
83 | void enic_intr_coal_set_rx(struct enic *enic, u32 timer) |
84 | { | |
85 | int i; | |
86 | int intr; | |
87 | ||
88 | for (i = 0; i < enic->rq_count; i++) { | |
89 | intr = enic_msix_rq_intr(enic, i); | |
90 | vnic_intr_coalescing_timer_set(&enic->intr[intr], timer); | |
91 | } | |
92 | } | |
93 | ||
f13bbc2f NP |
94 | static int enic_get_settings(struct net_device *netdev, |
95 | struct ethtool_cmd *ecmd) | |
96 | { | |
97 | struct enic *enic = netdev_priv(netdev); | |
98 | ||
99 | ecmd->supported = (SUPPORTED_10000baseT_Full | SUPPORTED_FIBRE); | |
100 | ecmd->advertising = (ADVERTISED_10000baseT_Full | ADVERTISED_FIBRE); | |
101 | ecmd->port = PORT_FIBRE; | |
102 | ecmd->transceiver = XCVR_EXTERNAL; | |
103 | ||
104 | if (netif_carrier_ok(netdev)) { | |
105 | ethtool_cmd_speed_set(ecmd, vnic_dev_port_speed(enic->vdev)); | |
106 | ecmd->duplex = DUPLEX_FULL; | |
107 | } else { | |
537fae01 JP |
108 | ethtool_cmd_speed_set(ecmd, SPEED_UNKNOWN); |
109 | ecmd->duplex = DUPLEX_UNKNOWN; | |
f13bbc2f NP |
110 | } |
111 | ||
112 | ecmd->autoneg = AUTONEG_DISABLE; | |
113 | ||
114 | return 0; | |
115 | } | |
116 | ||
117 | static void enic_get_drvinfo(struct net_device *netdev, | |
118 | struct ethtool_drvinfo *drvinfo) | |
119 | { | |
120 | struct enic *enic = netdev_priv(netdev); | |
121 | struct vnic_devcmd_fw_info *fw_info; | |
122 | ||
123 | enic_dev_fw_info(enic, &fw_info); | |
124 | ||
125 | strlcpy(drvinfo->driver, DRV_NAME, sizeof(drvinfo->driver)); | |
126 | strlcpy(drvinfo->version, DRV_VERSION, sizeof(drvinfo->version)); | |
127 | strlcpy(drvinfo->fw_version, fw_info->fw_version, | |
128 | sizeof(drvinfo->fw_version)); | |
129 | strlcpy(drvinfo->bus_info, pci_name(enic->pdev), | |
130 | sizeof(drvinfo->bus_info)); | |
131 | } | |
132 | ||
133 | static void enic_get_strings(struct net_device *netdev, u32 stringset, | |
134 | u8 *data) | |
135 | { | |
136 | unsigned int i; | |
137 | ||
138 | switch (stringset) { | |
139 | case ETH_SS_STATS: | |
140 | for (i = 0; i < enic_n_tx_stats; i++) { | |
141 | memcpy(data, enic_tx_stats[i].name, ETH_GSTRING_LEN); | |
142 | data += ETH_GSTRING_LEN; | |
143 | } | |
144 | for (i = 0; i < enic_n_rx_stats; i++) { | |
145 | memcpy(data, enic_rx_stats[i].name, ETH_GSTRING_LEN); | |
146 | data += ETH_GSTRING_LEN; | |
147 | } | |
148 | break; | |
149 | } | |
150 | } | |
151 | ||
152 | static int enic_get_sset_count(struct net_device *netdev, int sset) | |
153 | { | |
154 | switch (sset) { | |
155 | case ETH_SS_STATS: | |
156 | return enic_n_tx_stats + enic_n_rx_stats; | |
157 | default: | |
158 | return -EOPNOTSUPP; | |
159 | } | |
160 | } | |
161 | ||
162 | static void enic_get_ethtool_stats(struct net_device *netdev, | |
163 | struct ethtool_stats *stats, u64 *data) | |
164 | { | |
165 | struct enic *enic = netdev_priv(netdev); | |
166 | struct vnic_stats *vstats; | |
167 | unsigned int i; | |
168 | ||
169 | enic_dev_stats_dump(enic, &vstats); | |
170 | ||
171 | for (i = 0; i < enic_n_tx_stats; i++) | |
172 | *(data++) = ((u64 *)&vstats->tx)[enic_tx_stats[i].index]; | |
173 | for (i = 0; i < enic_n_rx_stats; i++) | |
174 | *(data++) = ((u64 *)&vstats->rx)[enic_rx_stats[i].index]; | |
175 | } | |
176 | ||
177 | static u32 enic_get_msglevel(struct net_device *netdev) | |
178 | { | |
179 | struct enic *enic = netdev_priv(netdev); | |
180 | return enic->msg_enable; | |
181 | } | |
182 | ||
183 | static void enic_set_msglevel(struct net_device *netdev, u32 value) | |
184 | { | |
185 | struct enic *enic = netdev_priv(netdev); | |
186 | enic->msg_enable = value; | |
187 | } | |
188 | ||
189 | static int enic_get_coalesce(struct net_device *netdev, | |
190 | struct ethtool_coalesce *ecmd) | |
191 | { | |
192 | struct enic *enic = netdev_priv(netdev); | |
7c2ce6e6 | 193 | struct enic_rx_coal *rxcoal = &enic->rx_coalesce_setting; |
f13bbc2f NP |
194 | |
195 | ecmd->tx_coalesce_usecs = enic->tx_coalesce_usecs; | |
196 | ecmd->rx_coalesce_usecs = enic->rx_coalesce_usecs; | |
7c2ce6e6 SS |
197 | if (rxcoal->use_adaptive_rx_coalesce) |
198 | ecmd->use_adaptive_rx_coalesce = 1; | |
199 | ecmd->rx_coalesce_usecs_low = rxcoal->small_pkt_range_start; | |
200 | ecmd->rx_coalesce_usecs_high = rxcoal->range_end; | |
f13bbc2f NP |
201 | |
202 | return 0; | |
203 | } | |
204 | ||
205 | static int enic_set_coalesce(struct net_device *netdev, | |
206 | struct ethtool_coalesce *ecmd) | |
207 | { | |
208 | struct enic *enic = netdev_priv(netdev); | |
209 | u32 tx_coalesce_usecs; | |
210 | u32 rx_coalesce_usecs; | |
7c2ce6e6 SS |
211 | u32 rx_coalesce_usecs_low; |
212 | u32 rx_coalesce_usecs_high; | |
213 | u32 coalesce_usecs_max; | |
f13bbc2f | 214 | unsigned int i, intr; |
7c2ce6e6 | 215 | struct enic_rx_coal *rxcoal = &enic->rx_coalesce_setting; |
f13bbc2f | 216 | |
7c2ce6e6 | 217 | coalesce_usecs_max = vnic_dev_get_intr_coal_timer_max(enic->vdev); |
f13bbc2f | 218 | tx_coalesce_usecs = min_t(u32, ecmd->tx_coalesce_usecs, |
7c2ce6e6 | 219 | coalesce_usecs_max); |
f13bbc2f | 220 | rx_coalesce_usecs = min_t(u32, ecmd->rx_coalesce_usecs, |
7c2ce6e6 SS |
221 | coalesce_usecs_max); |
222 | ||
223 | rx_coalesce_usecs_low = min_t(u32, ecmd->rx_coalesce_usecs_low, | |
224 | coalesce_usecs_max); | |
225 | rx_coalesce_usecs_high = min_t(u32, ecmd->rx_coalesce_usecs_high, | |
226 | coalesce_usecs_max); | |
f13bbc2f NP |
227 | |
228 | switch (vnic_dev_get_intr_mode(enic->vdev)) { | |
229 | case VNIC_DEV_INTR_MODE_INTX: | |
230 | if (tx_coalesce_usecs != rx_coalesce_usecs) | |
231 | return -EINVAL; | |
7c2ce6e6 SS |
232 | if (ecmd->use_adaptive_rx_coalesce || |
233 | ecmd->rx_coalesce_usecs_low || | |
234 | ecmd->rx_coalesce_usecs_high) | |
a16a3361 | 235 | return -EINVAL; |
f13bbc2f NP |
236 | |
237 | intr = enic_legacy_io_intr(); | |
238 | vnic_intr_coalescing_timer_set(&enic->intr[intr], | |
239 | tx_coalesce_usecs); | |
240 | break; | |
241 | case VNIC_DEV_INTR_MODE_MSI: | |
242 | if (tx_coalesce_usecs != rx_coalesce_usecs) | |
243 | return -EINVAL; | |
7c2ce6e6 SS |
244 | if (ecmd->use_adaptive_rx_coalesce || |
245 | ecmd->rx_coalesce_usecs_low || | |
246 | ecmd->rx_coalesce_usecs_high) | |
a16a3361 | 247 | return -EINVAL; |
f13bbc2f NP |
248 | |
249 | vnic_intr_coalescing_timer_set(&enic->intr[0], | |
250 | tx_coalesce_usecs); | |
251 | break; | |
252 | case VNIC_DEV_INTR_MODE_MSIX: | |
a16a3361 GV |
253 | if (ecmd->rx_coalesce_usecs_high && |
254 | (rx_coalesce_usecs_high < | |
255 | rx_coalesce_usecs_low + ENIC_AIC_LARGE_PKT_DIFF)) | |
256 | return -EINVAL; | |
257 | ||
f13bbc2f NP |
258 | for (i = 0; i < enic->wq_count; i++) { |
259 | intr = enic_msix_wq_intr(enic, i); | |
260 | vnic_intr_coalescing_timer_set(&enic->intr[intr], | |
261 | tx_coalesce_usecs); | |
262 | } | |
263 | ||
a16a3361 GV |
264 | rxcoal->use_adaptive_rx_coalesce = |
265 | !!ecmd->use_adaptive_rx_coalesce; | |
266 | if (!rxcoal->use_adaptive_rx_coalesce) | |
267 | enic_intr_coal_set_rx(enic, rx_coalesce_usecs); | |
f13bbc2f | 268 | |
7c2ce6e6 | 269 | if (ecmd->rx_coalesce_usecs_high) { |
7c2ce6e6 SS |
270 | rxcoal->range_end = rx_coalesce_usecs_high; |
271 | rxcoal->small_pkt_range_start = rx_coalesce_usecs_low; | |
272 | rxcoal->large_pkt_range_start = rx_coalesce_usecs_low + | |
273 | ENIC_AIC_LARGE_PKT_DIFF; | |
274 | } | |
f13bbc2f NP |
275 | break; |
276 | default: | |
277 | break; | |
278 | } | |
279 | ||
280 | enic->tx_coalesce_usecs = tx_coalesce_usecs; | |
281 | enic->rx_coalesce_usecs = rx_coalesce_usecs; | |
282 | ||
283 | return 0; | |
284 | } | |
285 | ||
3762ff8f GV |
286 | static int enic_grxclsrlall(struct enic *enic, struct ethtool_rxnfc *cmd, |
287 | u32 *rule_locs) | |
288 | { | |
289 | int j, ret = 0, cnt = 0; | |
290 | ||
291 | cmd->data = enic->rfs_h.max - enic->rfs_h.free; | |
292 | for (j = 0; j < (1 << ENIC_RFS_FLW_BITSHIFT); j++) { | |
293 | struct hlist_head *hhead; | |
294 | struct hlist_node *tmp; | |
295 | struct enic_rfs_fltr_node *n; | |
296 | ||
297 | hhead = &enic->rfs_h.ht_head[j]; | |
298 | hlist_for_each_entry_safe(n, tmp, hhead, node) { | |
299 | if (cnt == cmd->rule_cnt) | |
300 | return -EMSGSIZE; | |
301 | rule_locs[cnt] = n->fltr_id; | |
302 | cnt++; | |
303 | } | |
304 | } | |
305 | cmd->rule_cnt = cnt; | |
306 | ||
307 | return ret; | |
308 | } | |
309 | ||
310 | static int enic_grxclsrule(struct enic *enic, struct ethtool_rxnfc *cmd) | |
311 | { | |
312 | struct ethtool_rx_flow_spec *fsp = | |
313 | (struct ethtool_rx_flow_spec *)&cmd->fs; | |
314 | struct enic_rfs_fltr_node *n; | |
315 | ||
316 | n = htbl_fltr_search(enic, (u16)fsp->location); | |
317 | if (!n) | |
318 | return -EINVAL; | |
319 | switch (n->keys.ip_proto) { | |
320 | case IPPROTO_TCP: | |
321 | fsp->flow_type = TCP_V4_FLOW; | |
322 | break; | |
323 | case IPPROTO_UDP: | |
324 | fsp->flow_type = UDP_V4_FLOW; | |
325 | break; | |
326 | default: | |
327 | return -EINVAL; | |
328 | break; | |
329 | } | |
330 | ||
331 | fsp->h_u.tcp_ip4_spec.ip4src = n->keys.src; | |
332 | fsp->m_u.tcp_ip4_spec.ip4src = (__u32)~0; | |
333 | ||
334 | fsp->h_u.tcp_ip4_spec.ip4dst = n->keys.dst; | |
335 | fsp->m_u.tcp_ip4_spec.ip4dst = (__u32)~0; | |
336 | ||
337 | fsp->h_u.tcp_ip4_spec.psrc = n->keys.port16[0]; | |
338 | fsp->m_u.tcp_ip4_spec.psrc = (__u16)~0; | |
339 | ||
340 | fsp->h_u.tcp_ip4_spec.pdst = n->keys.port16[1]; | |
341 | fsp->m_u.tcp_ip4_spec.pdst = (__u16)~0; | |
342 | ||
343 | fsp->ring_cookie = n->rq_id; | |
344 | ||
345 | return 0; | |
346 | } | |
347 | ||
348 | static int enic_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd, | |
349 | u32 *rule_locs) | |
350 | { | |
351 | struct enic *enic = netdev_priv(dev); | |
352 | int ret = 0; | |
353 | ||
354 | switch (cmd->cmd) { | |
355 | case ETHTOOL_GRXRINGS: | |
356 | cmd->data = enic->rq_count; | |
357 | break; | |
358 | case ETHTOOL_GRXCLSRLCNT: | |
359 | spin_lock_bh(&enic->rfs_h.lock); | |
360 | cmd->rule_cnt = enic->rfs_h.max - enic->rfs_h.free; | |
361 | cmd->data = enic->rfs_h.max; | |
362 | spin_unlock_bh(&enic->rfs_h.lock); | |
363 | break; | |
364 | case ETHTOOL_GRXCLSRLALL: | |
365 | spin_lock_bh(&enic->rfs_h.lock); | |
366 | ret = enic_grxclsrlall(enic, cmd, rule_locs); | |
367 | spin_unlock_bh(&enic->rfs_h.lock); | |
368 | break; | |
369 | case ETHTOOL_GRXCLSRULE: | |
370 | spin_lock_bh(&enic->rfs_h.lock); | |
371 | ret = enic_grxclsrule(enic, cmd); | |
372 | spin_unlock_bh(&enic->rfs_h.lock); | |
373 | break; | |
374 | default: | |
375 | ret = -EOPNOTSUPP; | |
376 | break; | |
377 | } | |
378 | ||
379 | return ret; | |
380 | } | |
381 | ||
f13bbc2f NP |
382 | static const struct ethtool_ops enic_ethtool_ops = { |
383 | .get_settings = enic_get_settings, | |
384 | .get_drvinfo = enic_get_drvinfo, | |
385 | .get_msglevel = enic_get_msglevel, | |
386 | .set_msglevel = enic_set_msglevel, | |
387 | .get_link = ethtool_op_get_link, | |
388 | .get_strings = enic_get_strings, | |
389 | .get_sset_count = enic_get_sset_count, | |
390 | .get_ethtool_stats = enic_get_ethtool_stats, | |
391 | .get_coalesce = enic_get_coalesce, | |
392 | .set_coalesce = enic_set_coalesce, | |
3762ff8f | 393 | .get_rxnfc = enic_get_rxnfc, |
f13bbc2f NP |
394 | }; |
395 | ||
396 | void enic_set_ethtool_ops(struct net_device *netdev) | |
397 | { | |
7ad24ea4 | 398 | netdev->ethtool_ops = &enic_ethtool_ops; |
f13bbc2f | 399 | } |