diff options
author | Eric Dumazet <eric.dumazet@gmail.com> | 2010-09-26 20:35:50 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-09-28 00:30:43 -0400 |
commit | 3c97af99a5aa17feaebb4eb0f85f51ab6c055797 (patch) | |
tree | 6d14e38cc4479acf15352062974ee8e4c3cccaac /net/ipv4/ipip.c | |
parent | e985aad723d7709e6bee566bacb100d33d9b791b (diff) |
ipip: percpu stats accounting
Maintain per_cpu tx_bytes, tx_packets, rx_bytes, rx_packets.
Other seldom used fields are kept in netdev->stats structure, possibly
unsafe.
This is a preliminary work to support lockless transmit path, and
correct RX stats, that are already unsafe.
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv4/ipip.c')
-rw-r--r-- | net/ipv4/ipip.c | 127 |
1 files changed, 93 insertions, 34 deletions
diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c index babd25278106..12b6fde6f65a 100644 --- a/net/ipv4/ipip.c +++ b/net/ipv4/ipip.c | |||
@@ -131,8 +131,9 @@ struct ipip_net { | |||
131 | struct net_device *fb_tunnel_dev; | 131 | struct net_device *fb_tunnel_dev; |
132 | }; | 132 | }; |
133 | 133 | ||
134 | static void ipip_tunnel_init(struct net_device *dev); | 134 | static int ipip_tunnel_init(struct net_device *dev); |
135 | static void ipip_tunnel_setup(struct net_device *dev); | 135 | static void ipip_tunnel_setup(struct net_device *dev); |
136 | static void ipip_dev_free(struct net_device *dev); | ||
136 | 137 | ||
137 | /* | 138 | /* |
138 | * Locking : hash tables are protected by RCU and RTNL | 139 | * Locking : hash tables are protected by RCU and RTNL |
@@ -141,6 +142,34 @@ static void ipip_tunnel_setup(struct net_device *dev); | |||
141 | #define for_each_ip_tunnel_rcu(start) \ | 142 | #define for_each_ip_tunnel_rcu(start) \ |
142 | for (t = rcu_dereference(start); t; t = rcu_dereference(t->next)) | 143 | for (t = rcu_dereference(start); t; t = rcu_dereference(t->next)) |
143 | 144 | ||
145 | /* often modified stats are per cpu, other are shared (netdev->stats) */ | ||
146 | struct pcpu_tstats { | ||
147 | unsigned long rx_packets; | ||
148 | unsigned long rx_bytes; | ||
149 | unsigned long tx_packets; | ||
150 | unsigned long tx_bytes; | ||
151 | }; | ||
152 | |||
153 | static struct net_device_stats *ipip_get_stats(struct net_device *dev) | ||
154 | { | ||
155 | struct pcpu_tstats sum = { 0 }; | ||
156 | int i; | ||
157 | |||
158 | for_each_possible_cpu(i) { | ||
159 | const struct pcpu_tstats *tstats = per_cpu_ptr(dev->tstats, i); | ||
160 | |||
161 | sum.rx_packets += tstats->rx_packets; | ||
162 | sum.rx_bytes += tstats->rx_bytes; | ||
163 | sum.tx_packets += tstats->tx_packets; | ||
164 | sum.tx_bytes += tstats->tx_bytes; | ||
165 | } | ||
166 | dev->stats.rx_packets = sum.rx_packets; | ||
167 | dev->stats.rx_bytes = sum.rx_bytes; | ||
168 | dev->stats.tx_packets = sum.tx_packets; | ||
169 | dev->stats.tx_bytes = sum.tx_bytes; | ||
170 | return &dev->stats; | ||
171 | } | ||
172 | |||
144 | static struct ip_tunnel * ipip_tunnel_lookup(struct net *net, | 173 | static struct ip_tunnel * ipip_tunnel_lookup(struct net *net, |
145 | __be32 remote, __be32 local) | 174 | __be32 remote, __be32 local) |
146 | { | 175 | { |
@@ -239,7 +268,7 @@ static struct ip_tunnel * ipip_tunnel_locate(struct net *net, | |||
239 | if (parms->name[0]) | 268 | if (parms->name[0]) |
240 | strlcpy(name, parms->name, IFNAMSIZ); | 269 | strlcpy(name, parms->name, IFNAMSIZ); |
241 | else | 270 | else |
242 | sprintf(name, "tunl%%d"); | 271 | strcpy(name, "tunl%d"); |
243 | 272 | ||
244 | dev = alloc_netdev(sizeof(*t), name, ipip_tunnel_setup); | 273 | dev = alloc_netdev(sizeof(*t), name, ipip_tunnel_setup); |
245 | if (dev == NULL) | 274 | if (dev == NULL) |
@@ -255,7 +284,8 @@ static struct ip_tunnel * ipip_tunnel_locate(struct net *net, | |||
255 | nt = netdev_priv(dev); | 284 | nt = netdev_priv(dev); |
256 | nt->parms = *parms; | 285 | nt->parms = *parms; |
257 | 286 | ||
258 | ipip_tunnel_init(dev); | 287 | if (ipip_tunnel_init(dev) < 0) |
288 | goto failed_free; | ||
259 | 289 | ||
260 | if (register_netdevice(dev) < 0) | 290 | if (register_netdevice(dev) < 0) |
261 | goto failed_free; | 291 | goto failed_free; |
@@ -265,7 +295,7 @@ static struct ip_tunnel * ipip_tunnel_locate(struct net *net, | |||
265 | return nt; | 295 | return nt; |
266 | 296 | ||
267 | failed_free: | 297 | failed_free: |
268 | free_netdev(dev); | 298 | ipip_dev_free(dev); |
269 | return NULL; | 299 | return NULL; |
270 | } | 300 | } |
271 | 301 | ||
@@ -359,8 +389,10 @@ static int ipip_rcv(struct sk_buff *skb) | |||
359 | const struct iphdr *iph = ip_hdr(skb); | 389 | const struct iphdr *iph = ip_hdr(skb); |
360 | 390 | ||
361 | rcu_read_lock(); | 391 | rcu_read_lock(); |
362 | if ((tunnel = ipip_tunnel_lookup(dev_net(skb->dev), | 392 | tunnel = ipip_tunnel_lookup(dev_net(skb->dev), iph->saddr, iph->daddr); |
363 | iph->saddr, iph->daddr)) != NULL) { | 393 | if (tunnel != NULL) { |
394 | struct pcpu_tstats *tstats; | ||
395 | |||
364 | if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) { | 396 | if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) { |
365 | rcu_read_unlock(); | 397 | rcu_read_unlock(); |
366 | kfree_skb(skb); | 398 | kfree_skb(skb); |
@@ -374,7 +406,11 @@ static int ipip_rcv(struct sk_buff *skb) | |||
374 | skb->protocol = htons(ETH_P_IP); | 406 | skb->protocol = htons(ETH_P_IP); |
375 | skb->pkt_type = PACKET_HOST; | 407 | skb->pkt_type = PACKET_HOST; |
376 | 408 | ||
377 | skb_tunnel_rx(skb, tunnel->dev); | 409 | tstats = this_cpu_ptr(tunnel->dev->tstats); |
410 | tstats->rx_packets++; | ||
411 | tstats->rx_bytes += skb->len; | ||
412 | |||
413 | __skb_tunnel_rx(skb, tunnel->dev); | ||
378 | 414 | ||
379 | ipip_ecn_decapsulate(iph, skb); | 415 | ipip_ecn_decapsulate(iph, skb); |
380 | 416 | ||
@@ -397,13 +433,12 @@ static int ipip_rcv(struct sk_buff *skb) | |||
397 | static netdev_tx_t ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev) | 433 | static netdev_tx_t ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev) |
398 | { | 434 | { |
399 | struct ip_tunnel *tunnel = netdev_priv(dev); | 435 | struct ip_tunnel *tunnel = netdev_priv(dev); |
400 | struct net_device_stats *stats = &dev->stats; | 436 | struct pcpu_tstats *tstats; |
401 | struct netdev_queue *txq = netdev_get_tx_queue(dev, 0); | ||
402 | struct iphdr *tiph = &tunnel->parms.iph; | 437 | struct iphdr *tiph = &tunnel->parms.iph; |
403 | u8 tos = tunnel->parms.iph.tos; | 438 | u8 tos = tunnel->parms.iph.tos; |
404 | __be16 df = tiph->frag_off; | 439 | __be16 df = tiph->frag_off; |
405 | struct rtable *rt; /* Route to the other host */ | 440 | struct rtable *rt; /* Route to the other host */ |
406 | struct net_device *tdev; /* Device to other host */ | 441 | struct net_device *tdev; /* Device to other host */ |
407 | struct iphdr *old_iph = ip_hdr(skb); | 442 | struct iphdr *old_iph = ip_hdr(skb); |
408 | struct iphdr *iph; /* Our new IP header */ | 443 | struct iphdr *iph; /* Our new IP header */ |
409 | unsigned int max_headroom; /* The extra header space needed */ | 444 | unsigned int max_headroom; /* The extra header space needed */ |
@@ -413,13 +448,13 @@ static netdev_tx_t ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev) | |||
413 | if (skb->protocol != htons(ETH_P_IP)) | 448 | if (skb->protocol != htons(ETH_P_IP)) |
414 | goto tx_error; | 449 | goto tx_error; |
415 | 450 | ||
416 | if (tos&1) | 451 | if (tos & 1) |
417 | tos = old_iph->tos; | 452 | tos = old_iph->tos; |
418 | 453 | ||
419 | if (!dst) { | 454 | if (!dst) { |
420 | /* NBMA tunnel */ | 455 | /* NBMA tunnel */ |
421 | if ((rt = skb_rtable(skb)) == NULL) { | 456 | if ((rt = skb_rtable(skb)) == NULL) { |
422 | stats->tx_fifo_errors++; | 457 | dev->stats.tx_fifo_errors++; |
423 | goto tx_error; | 458 | goto tx_error; |
424 | } | 459 | } |
425 | if ((dst = rt->rt_gateway) == 0) | 460 | if ((dst = rt->rt_gateway) == 0) |
@@ -427,14 +462,20 @@ static netdev_tx_t ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev) | |||
427 | } | 462 | } |
428 | 463 | ||
429 | { | 464 | { |
430 | struct flowi fl = { .oif = tunnel->parms.link, | 465 | struct flowi fl = { |
431 | .nl_u = { .ip4_u = | 466 | .oif = tunnel->parms.link, |
432 | { .daddr = dst, | 467 | .nl_u = { |
433 | .saddr = tiph->saddr, | 468 | .ip4_u = { |
434 | .tos = RT_TOS(tos) } }, | 469 | .daddr = dst, |
435 | .proto = IPPROTO_IPIP }; | 470 | .saddr = tiph->saddr, |
471 | .tos = RT_TOS(tos) | ||
472 | } | ||
473 | }, | ||
474 | .proto = IPPROTO_IPIP | ||
475 | }; | ||
476 | |||
436 | if (ip_route_output_key(dev_net(dev), &rt, &fl)) { | 477 | if (ip_route_output_key(dev_net(dev), &rt, &fl)) { |
437 | stats->tx_carrier_errors++; | 478 | dev->stats.tx_carrier_errors++; |
438 | goto tx_error_icmp; | 479 | goto tx_error_icmp; |
439 | } | 480 | } |
440 | } | 481 | } |
@@ -442,7 +483,7 @@ static netdev_tx_t ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev) | |||
442 | 483 | ||
443 | if (tdev == dev) { | 484 | if (tdev == dev) { |
444 | ip_rt_put(rt); | 485 | ip_rt_put(rt); |
445 | stats->collisions++; | 486 | dev->stats.collisions++; |
446 | goto tx_error; | 487 | goto tx_error; |
447 | } | 488 | } |
448 | 489 | ||
@@ -452,7 +493,7 @@ static netdev_tx_t ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev) | |||
452 | mtu = dst_mtu(&rt->dst) - sizeof(struct iphdr); | 493 | mtu = dst_mtu(&rt->dst) - sizeof(struct iphdr); |
453 | 494 | ||
454 | if (mtu < 68) { | 495 | if (mtu < 68) { |
455 | stats->collisions++; | 496 | dev->stats.collisions++; |
456 | ip_rt_put(rt); | 497 | ip_rt_put(rt); |
457 | goto tx_error; | 498 | goto tx_error; |
458 | } | 499 | } |
@@ -488,7 +529,7 @@ static netdev_tx_t ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev) | |||
488 | struct sk_buff *new_skb = skb_realloc_headroom(skb, max_headroom); | 529 | struct sk_buff *new_skb = skb_realloc_headroom(skb, max_headroom); |
489 | if (!new_skb) { | 530 | if (!new_skb) { |
490 | ip_rt_put(rt); | 531 | ip_rt_put(rt); |
491 | txq->tx_dropped++; | 532 | dev->stats.tx_dropped++; |
492 | dev_kfree_skb(skb); | 533 | dev_kfree_skb(skb); |
493 | return NETDEV_TX_OK; | 534 | return NETDEV_TX_OK; |
494 | } | 535 | } |
@@ -525,14 +566,14 @@ static netdev_tx_t ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev) | |||
525 | iph->ttl = old_iph->ttl; | 566 | iph->ttl = old_iph->ttl; |
526 | 567 | ||
527 | nf_reset(skb); | 568 | nf_reset(skb); |
528 | 569 | tstats = this_cpu_ptr(dev->tstats); | |
529 | IPTUNNEL_XMIT(); | 570 | __IPTUNNEL_XMIT(tstats, &dev->stats); |
530 | return NETDEV_TX_OK; | 571 | return NETDEV_TX_OK; |
531 | 572 | ||
532 | tx_error_icmp: | 573 | tx_error_icmp: |
533 | dst_link_failure(skb); | 574 | dst_link_failure(skb); |
534 | tx_error: | 575 | tx_error: |
535 | stats->tx_errors++; | 576 | dev->stats.tx_errors++; |
536 | dev_kfree_skb(skb); | 577 | dev_kfree_skb(skb); |
537 | return NETDEV_TX_OK; | 578 | return NETDEV_TX_OK; |
538 | } | 579 | } |
@@ -547,13 +588,19 @@ static void ipip_tunnel_bind_dev(struct net_device *dev) | |||
547 | iph = &tunnel->parms.iph; | 588 | iph = &tunnel->parms.iph; |
548 | 589 | ||
549 | if (iph->daddr) { | 590 | if (iph->daddr) { |
550 | struct flowi fl = { .oif = tunnel->parms.link, | 591 | struct flowi fl = { |
551 | .nl_u = { .ip4_u = | 592 | .oif = tunnel->parms.link, |
552 | { .daddr = iph->daddr, | 593 | .nl_u = { |
553 | .saddr = iph->saddr, | 594 | .ip4_u = { |
554 | .tos = RT_TOS(iph->tos) } }, | 595 | .daddr = iph->daddr, |
555 | .proto = IPPROTO_IPIP }; | 596 | .saddr = iph->saddr, |
597 | .tos = RT_TOS(iph->tos) | ||
598 | } | ||
599 | }, | ||
600 | .proto = IPPROTO_IPIP | ||
601 | }; | ||
556 | struct rtable *rt; | 602 | struct rtable *rt; |
603 | |||
557 | if (!ip_route_output_key(dev_net(dev), &rt, &fl)) { | 604 | if (!ip_route_output_key(dev_net(dev), &rt, &fl)) { |
558 | tdev = rt->dst.dev; | 605 | tdev = rt->dst.dev; |
559 | ip_rt_put(rt); | 606 | ip_rt_put(rt); |
@@ -699,13 +746,19 @@ static const struct net_device_ops ipip_netdev_ops = { | |||
699 | .ndo_start_xmit = ipip_tunnel_xmit, | 746 | .ndo_start_xmit = ipip_tunnel_xmit, |
700 | .ndo_do_ioctl = ipip_tunnel_ioctl, | 747 | .ndo_do_ioctl = ipip_tunnel_ioctl, |
701 | .ndo_change_mtu = ipip_tunnel_change_mtu, | 748 | .ndo_change_mtu = ipip_tunnel_change_mtu, |
702 | 749 | .ndo_get_stats = ipip_get_stats, | |
703 | }; | 750 | }; |
704 | 751 | ||
752 | static void ipip_dev_free(struct net_device *dev) | ||
753 | { | ||
754 | free_percpu(dev->tstats); | ||
755 | free_netdev(dev); | ||
756 | } | ||
757 | |||
705 | static void ipip_tunnel_setup(struct net_device *dev) | 758 | static void ipip_tunnel_setup(struct net_device *dev) |
706 | { | 759 | { |
707 | dev->netdev_ops = &ipip_netdev_ops; | 760 | dev->netdev_ops = &ipip_netdev_ops; |
708 | dev->destructor = free_netdev; | 761 | dev->destructor = ipip_dev_free; |
709 | 762 | ||
710 | dev->type = ARPHRD_TUNNEL; | 763 | dev->type = ARPHRD_TUNNEL; |
711 | dev->hard_header_len = LL_MAX_HEADER + sizeof(struct iphdr); | 764 | dev->hard_header_len = LL_MAX_HEADER + sizeof(struct iphdr); |
@@ -717,7 +770,7 @@ static void ipip_tunnel_setup(struct net_device *dev) | |||
717 | dev->priv_flags &= ~IFF_XMIT_DST_RELEASE; | 770 | dev->priv_flags &= ~IFF_XMIT_DST_RELEASE; |
718 | } | 771 | } |
719 | 772 | ||
720 | static void ipip_tunnel_init(struct net_device *dev) | 773 | static int ipip_tunnel_init(struct net_device *dev) |
721 | { | 774 | { |
722 | struct ip_tunnel *tunnel = netdev_priv(dev); | 775 | struct ip_tunnel *tunnel = netdev_priv(dev); |
723 | 776 | ||
@@ -728,6 +781,12 @@ static void ipip_tunnel_init(struct net_device *dev) | |||
728 | memcpy(dev->broadcast, &tunnel->parms.iph.daddr, 4); | 781 | memcpy(dev->broadcast, &tunnel->parms.iph.daddr, 4); |
729 | 782 | ||
730 | ipip_tunnel_bind_dev(dev); | 783 | ipip_tunnel_bind_dev(dev); |
784 | |||
785 | dev->tstats = alloc_percpu(struct pcpu_tstats); | ||
786 | if (!dev->tstats) | ||
787 | return -ENOMEM; | ||
788 | |||
789 | return 0; | ||
731 | } | 790 | } |
732 | 791 | ||
733 | static void __net_init ipip_fb_tunnel_init(struct net_device *dev) | 792 | static void __net_init ipip_fb_tunnel_init(struct net_device *dev) |