]>
Commit | Line | Data |
---|---|---|
5de73ee4 SH |
1 | /* |
2 | * Copyright (C) 2017 Netronome Systems, Inc. | |
3 | * | |
4 | * This software is dual licensed under the GNU General License Version 2, | |
5 | * June 1991 as shown in the file COPYING in the top-level directory of this | |
6 | * source tree or the BSD 2-Clause License provided below. You have the | |
7 | * option to license this software under the complete terms of either license. | |
8 | * | |
9 | * The BSD 2-Clause License: | |
10 | * | |
11 | * Redistribution and use in source and binary forms, with or | |
12 | * without modification, are permitted provided that the following | |
13 | * conditions are met: | |
14 | * | |
15 | * 1. Redistributions of source code must retain the above | |
16 | * copyright notice, this list of conditions and the following | |
17 | * disclaimer. | |
18 | * | |
19 | * 2. Redistributions in binary form must reproduce the above | |
20 | * copyright notice, this list of conditions and the following | |
21 | * disclaimer in the documentation and/or other materials | |
22 | * provided with the distribution. | |
23 | * | |
24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
25 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
26 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
27 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | |
28 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | |
29 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |
30 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
31 | * SOFTWARE. | |
32 | */ | |
33 | ||
34 | #include <linux/etherdevice.h> | |
eadfa4c3 | 35 | #include <linux/io-64-nonatomic-hi-lo.h> |
5de73ee4 SH |
36 | #include <linux/lockdep.h> |
37 | #include <net/dst_metadata.h> | |
8f15df60 | 38 | #include <net/switchdev.h> |
5de73ee4 SH |
39 | |
40 | #include "nfpcore/nfp_cpp.h" | |
3238b250 | 41 | #include "nfpcore/nfp_nsp.h" |
5de73ee4 SH |
42 | #include "nfp_app.h" |
43 | #include "nfp_main.h" | |
eadfa4c3 | 44 | #include "nfp_net_ctrl.h" |
5de73ee4 | 45 | #include "nfp_net_repr.h" |
6abd224b | 46 | #include "nfp_net_sriov.h" |
5de73ee4 SH |
47 | #include "nfp_port.h" |
48 | ||
eadfa4c3 SH |
49 | static void |
50 | nfp_repr_inc_tx_stats(struct net_device *netdev, unsigned int len, | |
51 | int tx_status) | |
52 | { | |
53 | struct nfp_repr *repr = netdev_priv(netdev); | |
54 | struct nfp_repr_pcpu_stats *stats; | |
55 | ||
56 | if (unlikely(tx_status != NET_XMIT_SUCCESS && | |
57 | tx_status != NET_XMIT_CN)) { | |
58 | this_cpu_inc(repr->stats->tx_drops); | |
59 | return; | |
60 | } | |
61 | ||
62 | stats = this_cpu_ptr(repr->stats); | |
63 | u64_stats_update_begin(&stats->syncp); | |
64 | stats->tx_packets++; | |
65 | stats->tx_bytes += len; | |
66 | u64_stats_update_end(&stats->syncp); | |
67 | } | |
68 | ||
69 | void nfp_repr_inc_rx_stats(struct net_device *netdev, unsigned int len) | |
70 | { | |
71 | struct nfp_repr *repr = netdev_priv(netdev); | |
72 | struct nfp_repr_pcpu_stats *stats; | |
73 | ||
74 | stats = this_cpu_ptr(repr->stats); | |
75 | u64_stats_update_begin(&stats->syncp); | |
76 | stats->rx_packets++; | |
77 | stats->rx_bytes += len; | |
78 | u64_stats_update_end(&stats->syncp); | |
79 | } | |
80 | ||
81 | static void | |
7344bea1 | 82 | nfp_repr_phy_port_get_stats64(struct nfp_port *port, |
eadfa4c3 SH |
83 | struct rtnl_link_stats64 *stats) |
84 | { | |
7344bea1 | 85 | u8 __iomem *mem = port->eth_stats; |
eadfa4c3 | 86 | |
42d779ff PJV |
87 | stats->tx_packets = readq(mem + NFP_MAC_STATS_TX_FRAMES_TRANSMITTED_OK); |
88 | stats->tx_bytes = readq(mem + NFP_MAC_STATS_TX_OUT_OCTETS); | |
89 | stats->tx_dropped = readq(mem + NFP_MAC_STATS_TX_OUT_ERRORS); | |
eadfa4c3 | 90 | |
42d779ff PJV |
91 | stats->rx_packets = readq(mem + NFP_MAC_STATS_RX_FRAMES_RECEIVED_OK); |
92 | stats->rx_bytes = readq(mem + NFP_MAC_STATS_RX_IN_OCTETS); | |
93 | stats->rx_dropped = readq(mem + NFP_MAC_STATS_RX_IN_ERRORS); | |
eadfa4c3 SH |
94 | } |
95 | ||
96 | static void | |
ef0ec676 JK |
97 | nfp_repr_vnic_get_stats64(struct nfp_port *port, |
98 | struct rtnl_link_stats64 *stats) | |
eadfa4c3 | 99 | { |
eadfa4c3 SH |
100 | /* TX and RX stats are flipped as we are returning the stats as seen |
101 | * at the switch port corresponding to the VF. | |
102 | */ | |
ef0ec676 JK |
103 | stats->tx_packets = readq(port->vnic + NFP_NET_CFG_STATS_RX_FRAMES); |
104 | stats->tx_bytes = readq(port->vnic + NFP_NET_CFG_STATS_RX_OCTETS); | |
105 | stats->tx_dropped = readq(port->vnic + NFP_NET_CFG_STATS_RX_DISCARDS); | |
eadfa4c3 | 106 | |
ef0ec676 JK |
107 | stats->rx_packets = readq(port->vnic + NFP_NET_CFG_STATS_TX_FRAMES); |
108 | stats->rx_bytes = readq(port->vnic + NFP_NET_CFG_STATS_TX_OCTETS); | |
109 | stats->rx_dropped = readq(port->vnic + NFP_NET_CFG_STATS_TX_DISCARDS); | |
eadfa4c3 SH |
110 | } |
111 | ||
5d7c64a7 | 112 | static void |
3238b250 | 113 | nfp_repr_get_stats64(struct net_device *netdev, struct rtnl_link_stats64 *stats) |
eadfa4c3 | 114 | { |
3238b250 | 115 | struct nfp_repr *repr = netdev_priv(netdev); |
3238b250 JK |
116 | |
117 | if (WARN_ON(!repr->port)) | |
118 | return; | |
119 | ||
120 | switch (repr->port->type) { | |
121 | case NFP_PORT_PHYS_PORT: | |
7344bea1 | 122 | if (!__nfp_port_get_eth_port(repr->port)) |
3238b250 | 123 | break; |
7344bea1 | 124 | nfp_repr_phy_port_get_stats64(repr->port, stats); |
eadfa4c3 | 125 | break; |
3238b250 | 126 | case NFP_PORT_PF_PORT: |
3238b250 | 127 | case NFP_PORT_VF_PORT: |
ef0ec676 | 128 | nfp_repr_vnic_get_stats64(repr->port, stats); |
eadfa4c3 SH |
129 | default: |
130 | break; | |
131 | } | |
132 | } | |
133 | ||
5d7c64a7 | 134 | static bool |
eadfa4c3 SH |
135 | nfp_repr_has_offload_stats(const struct net_device *dev, int attr_id) |
136 | { | |
137 | switch (attr_id) { | |
138 | case IFLA_OFFLOAD_XSTATS_CPU_HIT: | |
139 | return true; | |
140 | } | |
141 | ||
142 | return false; | |
143 | } | |
144 | ||
145 | static int | |
146 | nfp_repr_get_host_stats64(const struct net_device *netdev, | |
147 | struct rtnl_link_stats64 *stats) | |
148 | { | |
149 | struct nfp_repr *repr = netdev_priv(netdev); | |
150 | int i; | |
151 | ||
152 | for_each_possible_cpu(i) { | |
153 | u64 tbytes, tpkts, tdrops, rbytes, rpkts; | |
154 | struct nfp_repr_pcpu_stats *repr_stats; | |
155 | unsigned int start; | |
156 | ||
157 | repr_stats = per_cpu_ptr(repr->stats, i); | |
158 | do { | |
159 | start = u64_stats_fetch_begin_irq(&repr_stats->syncp); | |
160 | tbytes = repr_stats->tx_bytes; | |
161 | tpkts = repr_stats->tx_packets; | |
162 | tdrops = repr_stats->tx_drops; | |
163 | rbytes = repr_stats->rx_bytes; | |
164 | rpkts = repr_stats->rx_packets; | |
165 | } while (u64_stats_fetch_retry_irq(&repr_stats->syncp, start)); | |
166 | ||
167 | stats->tx_bytes += tbytes; | |
168 | stats->tx_packets += tpkts; | |
169 | stats->tx_dropped += tdrops; | |
170 | stats->rx_bytes += rbytes; | |
171 | stats->rx_packets += rpkts; | |
172 | } | |
173 | ||
174 | return 0; | |
175 | } | |
176 | ||
5d7c64a7 JK |
177 | static int |
178 | nfp_repr_get_offload_stats(int attr_id, const struct net_device *dev, | |
179 | void *stats) | |
eadfa4c3 SH |
180 | { |
181 | switch (attr_id) { | |
182 | case IFLA_OFFLOAD_XSTATS_CPU_HIT: | |
183 | return nfp_repr_get_host_stats64(dev, stats); | |
184 | } | |
185 | ||
186 | return -EINVAL; | |
187 | } | |
188 | ||
5d7c64a7 | 189 | static netdev_tx_t nfp_repr_xmit(struct sk_buff *skb, struct net_device *netdev) |
eadfa4c3 SH |
190 | { |
191 | struct nfp_repr *repr = netdev_priv(netdev); | |
192 | unsigned int len = skb->len; | |
193 | int ret; | |
194 | ||
195 | skb_dst_drop(skb); | |
196 | dst_hold((struct dst_entry *)repr->dst); | |
197 | skb_dst_set(skb, (struct dst_entry *)repr->dst); | |
198 | skb->dev = repr->dst->u.port_info.lower_dev; | |
199 | ||
200 | ret = dev_queue_xmit(skb); | |
201 | nfp_repr_inc_tx_stats(netdev, len, ret); | |
202 | ||
203 | return ret; | |
204 | } | |
205 | ||
5d7c64a7 JK |
206 | static int nfp_repr_stop(struct net_device *netdev) |
207 | { | |
208 | struct nfp_repr *repr = netdev_priv(netdev); | |
447e9ebf | 209 | int err; |
5d7c64a7 | 210 | |
447e9ebf DM |
211 | err = nfp_app_repr_stop(repr->app, repr); |
212 | if (err) | |
213 | return err; | |
214 | ||
215 | nfp_port_configure(netdev, false); | |
216 | return 0; | |
5d7c64a7 JK |
217 | } |
218 | ||
219 | static int nfp_repr_open(struct net_device *netdev) | |
220 | { | |
221 | struct nfp_repr *repr = netdev_priv(netdev); | |
447e9ebf DM |
222 | int err; |
223 | ||
224 | err = nfp_port_configure(netdev, true); | |
225 | if (err) | |
226 | return err; | |
227 | ||
228 | err = nfp_app_repr_open(repr->app, repr); | |
229 | if (err) | |
230 | goto err_port_disable; | |
231 | ||
232 | return 0; | |
5d7c64a7 | 233 | |
447e9ebf DM |
234 | err_port_disable: |
235 | nfp_port_configure(netdev, false); | |
236 | return err; | |
5d7c64a7 JK |
237 | } |
238 | ||
39ae7eb6 | 239 | const struct net_device_ops nfp_repr_netdev_ops = { |
5d7c64a7 JK |
240 | .ndo_open = nfp_repr_open, |
241 | .ndo_stop = nfp_repr_stop, | |
242 | .ndo_start_xmit = nfp_repr_xmit, | |
243 | .ndo_get_stats64 = nfp_repr_get_stats64, | |
244 | .ndo_has_offload_stats = nfp_repr_has_offload_stats, | |
245 | .ndo_get_offload_stats = nfp_repr_get_offload_stats, | |
168c478e | 246 | .ndo_get_phys_port_name = nfp_port_get_phys_port_name, |
8a276873 | 247 | .ndo_setup_tc = nfp_port_setup_tc, |
6abd224b SH |
248 | .ndo_set_vf_mac = nfp_app_set_vf_mac, |
249 | .ndo_set_vf_vlan = nfp_app_set_vf_vlan, | |
250 | .ndo_set_vf_spoofchk = nfp_app_set_vf_spoofchk, | |
251 | .ndo_get_vf_config = nfp_app_get_vf_config, | |
252 | .ndo_set_vf_link_state = nfp_app_set_vf_link_state, | |
5d7c64a7 JK |
253 | }; |
254 | ||
5de73ee4 SH |
255 | static void nfp_repr_clean(struct nfp_repr *repr) |
256 | { | |
257 | unregister_netdev(repr->netdev); | |
1a24d4f9 | 258 | nfp_app_repr_clean(repr->app, repr->netdev); |
5de73ee4 SH |
259 | dst_release((struct dst_entry *)repr->dst); |
260 | nfp_port_free(repr->port); | |
261 | } | |
262 | ||
263 | static struct lock_class_key nfp_repr_netdev_xmit_lock_key; | |
264 | static struct lock_class_key nfp_repr_netdev_addr_lock_key; | |
265 | ||
266 | static void nfp_repr_set_lockdep_class_one(struct net_device *dev, | |
267 | struct netdev_queue *txq, | |
268 | void *_unused) | |
269 | { | |
270 | lockdep_set_class(&txq->_xmit_lock, &nfp_repr_netdev_xmit_lock_key); | |
271 | } | |
272 | ||
273 | static void nfp_repr_set_lockdep_class(struct net_device *dev) | |
274 | { | |
275 | lockdep_set_class(&dev->addr_list_lock, &nfp_repr_netdev_addr_lock_key); | |
276 | netdev_for_each_tx_queue(dev, nfp_repr_set_lockdep_class_one, NULL); | |
277 | } | |
278 | ||
279 | int nfp_repr_init(struct nfp_app *app, struct net_device *netdev, | |
5d7c64a7 JK |
280 | u32 cmsg_port_id, struct nfp_port *port, |
281 | struct net_device *pf_netdev) | |
5de73ee4 SH |
282 | { |
283 | struct nfp_repr *repr = netdev_priv(netdev); | |
284 | int err; | |
285 | ||
286 | nfp_repr_set_lockdep_class(netdev); | |
287 | ||
288 | repr->port = port; | |
289 | repr->dst = metadata_dst_alloc(0, METADATA_HW_PORT_MUX, GFP_KERNEL); | |
290 | if (!repr->dst) | |
291 | return -ENOMEM; | |
292 | repr->dst->u.port_info.port_id = cmsg_port_id; | |
293 | repr->dst->u.port_info.lower_dev = pf_netdev; | |
294 | ||
5d7c64a7 | 295 | netdev->netdev_ops = &nfp_repr_netdev_ops; |
06726f30 JK |
296 | netdev->ethtool_ops = &nfp_port_ethtool_ops; |
297 | ||
743ba5b4 DM |
298 | netdev->max_mtu = pf_netdev->max_mtu; |
299 | ||
8f15df60 | 300 | SWITCHDEV_SET_OPS(netdev, &nfp_port_switchdev_ops); |
5de73ee4 | 301 | |
8a276873 PJV |
302 | if (nfp_app_has_tc(app)) { |
303 | netdev->features |= NETIF_F_HW_TC; | |
304 | netdev->hw_features |= NETIF_F_HW_TC; | |
305 | } | |
306 | ||
1a24d4f9 | 307 | err = nfp_app_repr_init(app, netdev); |
5de73ee4 SH |
308 | if (err) |
309 | goto err_clean; | |
310 | ||
1a24d4f9 JH |
311 | err = register_netdev(netdev); |
312 | if (err) | |
313 | goto err_repr_clean; | |
314 | ||
5de73ee4 SH |
315 | return 0; |
316 | ||
1a24d4f9 JH |
317 | err_repr_clean: |
318 | nfp_app_repr_clean(app, netdev); | |
5de73ee4 SH |
319 | err_clean: |
320 | dst_release((struct dst_entry *)repr->dst); | |
321 | return err; | |
322 | } | |
323 | ||
eadfa4c3 SH |
324 | static void nfp_repr_free(struct nfp_repr *repr) |
325 | { | |
326 | free_percpu(repr->stats); | |
327 | free_netdev(repr->netdev); | |
328 | } | |
329 | ||
5de73ee4 SH |
330 | struct net_device *nfp_repr_alloc(struct nfp_app *app) |
331 | { | |
332 | struct net_device *netdev; | |
333 | struct nfp_repr *repr; | |
334 | ||
335 | netdev = alloc_etherdev(sizeof(*repr)); | |
336 | if (!netdev) | |
337 | return NULL; | |
338 | ||
339 | repr = netdev_priv(netdev); | |
340 | repr->netdev = netdev; | |
341 | repr->app = app; | |
342 | ||
eadfa4c3 SH |
343 | repr->stats = netdev_alloc_pcpu_stats(struct nfp_repr_pcpu_stats); |
344 | if (!repr->stats) | |
345 | goto err_free_netdev; | |
346 | ||
5de73ee4 | 347 | return netdev; |
eadfa4c3 SH |
348 | |
349 | err_free_netdev: | |
350 | free_netdev(netdev); | |
351 | return NULL; | |
5de73ee4 SH |
352 | } |
353 | ||
354 | static void nfp_repr_clean_and_free(struct nfp_repr *repr) | |
355 | { | |
356 | nfp_info(repr->app->cpp, "Destroying Representor(%s)\n", | |
357 | repr->netdev->name); | |
358 | nfp_repr_clean(repr); | |
eadfa4c3 | 359 | nfp_repr_free(repr); |
5de73ee4 SH |
360 | } |
361 | ||
362 | void nfp_reprs_clean_and_free(struct nfp_reprs *reprs) | |
363 | { | |
364 | unsigned int i; | |
365 | ||
366 | for (i = 0; i < reprs->num_reprs; i++) | |
367 | if (reprs->reprs[i]) | |
368 | nfp_repr_clean_and_free(netdev_priv(reprs->reprs[i])); | |
369 | ||
370 | kfree(reprs); | |
371 | } | |
372 | ||
373 | void | |
374 | nfp_reprs_clean_and_free_by_type(struct nfp_app *app, | |
375 | enum nfp_repr_type type) | |
376 | { | |
377 | struct nfp_reprs *reprs; | |
378 | ||
379 | reprs = nfp_app_reprs_set(app, type, NULL); | |
380 | if (!reprs) | |
381 | return; | |
382 | ||
383 | synchronize_rcu(); | |
384 | nfp_reprs_clean_and_free(reprs); | |
385 | } | |
386 | ||
387 | struct nfp_reprs *nfp_reprs_alloc(unsigned int num_reprs) | |
388 | { | |
389 | struct nfp_reprs *reprs; | |
390 | ||
391 | reprs = kzalloc(sizeof(*reprs) + | |
392 | num_reprs * sizeof(struct net_device *), GFP_KERNEL); | |
393 | if (!reprs) | |
394 | return NULL; | |
395 | reprs->num_reprs = num_reprs; | |
396 | ||
397 | return reprs; | |
398 | } | |
5fa27d59 DM |
399 | |
400 | int nfp_reprs_resync_phys_ports(struct nfp_app *app) | |
401 | { | |
402 | struct nfp_reprs *reprs, *old_reprs; | |
403 | struct nfp_repr *repr; | |
404 | int i; | |
405 | ||
406 | old_reprs = | |
407 | rcu_dereference_protected(app->reprs[NFP_REPR_TYPE_PHYS_PORT], | |
408 | lockdep_is_held(&app->pf->lock)); | |
409 | if (!old_reprs) | |
410 | return 0; | |
411 | ||
412 | reprs = nfp_reprs_alloc(old_reprs->num_reprs); | |
413 | if (!reprs) | |
414 | return -ENOMEM; | |
415 | ||
416 | for (i = 0; i < old_reprs->num_reprs; i++) { | |
417 | if (!old_reprs->reprs[i]) | |
418 | continue; | |
419 | ||
420 | repr = netdev_priv(old_reprs->reprs[i]); | |
421 | if (repr->port->type == NFP_PORT_INVALID) | |
422 | continue; | |
423 | ||
424 | reprs->reprs[i] = old_reprs->reprs[i]; | |
425 | } | |
426 | ||
427 | old_reprs = nfp_app_reprs_set(app, NFP_REPR_TYPE_PHYS_PORT, reprs); | |
428 | synchronize_rcu(); | |
429 | ||
430 | /* Now we free up removed representors */ | |
431 | for (i = 0; i < old_reprs->num_reprs; i++) { | |
432 | if (!old_reprs->reprs[i]) | |
433 | continue; | |
434 | ||
435 | repr = netdev_priv(old_reprs->reprs[i]); | |
436 | if (repr->port->type != NFP_PORT_INVALID) | |
437 | continue; | |
438 | ||
439 | nfp_app_repr_stop(app, repr); | |
440 | nfp_repr_clean(repr); | |
441 | } | |
442 | ||
443 | kfree(old_reprs); | |
444 | return 0; | |
445 | } |