diff options
author | Eric Dumazet <eric.dumazet@gmail.com> | 2010-09-27 23:23:34 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-09-29 16:25:52 -0400 |
commit | 8560f2266b36adb43238f1f9fd13958dd031901c (patch) | |
tree | da92b380a566f5e2daba65678b874f0f2c4df3cb /net/ipv6 | |
parent | 153f0943382e9ae0bff7caa110a1a4656088d0d4 (diff) |
ip6tnl: 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/ipv6')
-rw-r--r-- | net/ipv6/ip6_tunnel.c | 93 |
1 files changed, 77 insertions, 16 deletions
diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index f6d9f683543e..8be3c452af90 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c | |||
@@ -75,7 +75,7 @@ MODULE_LICENSE("GPL"); | |||
75 | (addr)->s6_addr32[2] ^ (addr)->s6_addr32[3]) & \ | 75 | (addr)->s6_addr32[2] ^ (addr)->s6_addr32[3]) & \ |
76 | (HASH_SIZE - 1)) | 76 | (HASH_SIZE - 1)) |
77 | 77 | ||
78 | static void ip6_tnl_dev_init(struct net_device *dev); | 78 | static int ip6_tnl_dev_init(struct net_device *dev); |
79 | static void ip6_tnl_dev_setup(struct net_device *dev); | 79 | static void ip6_tnl_dev_setup(struct net_device *dev); |
80 | 80 | ||
81 | static int ip6_tnl_net_id __read_mostly; | 81 | static int ip6_tnl_net_id __read_mostly; |
@@ -88,6 +88,34 @@ struct ip6_tnl_net { | |||
88 | struct ip6_tnl __rcu **tnls[2]; | 88 | struct ip6_tnl __rcu **tnls[2]; |
89 | }; | 89 | }; |
90 | 90 | ||
91 | /* often modified stats are per cpu, other are shared (netdev->stats) */ | ||
92 | struct pcpu_tstats { | ||
93 | unsigned long rx_packets; | ||
94 | unsigned long rx_bytes; | ||
95 | unsigned long tx_packets; | ||
96 | unsigned long tx_bytes; | ||
97 | }; | ||
98 | |||
99 | static struct net_device_stats *ip6_get_stats(struct net_device *dev) | ||
100 | { | ||
101 | struct pcpu_tstats sum = { 0 }; | ||
102 | int i; | ||
103 | |||
104 | for_each_possible_cpu(i) { | ||
105 | const struct pcpu_tstats *tstats = per_cpu_ptr(dev->tstats, i); | ||
106 | |||
107 | sum.rx_packets += tstats->rx_packets; | ||
108 | sum.rx_bytes += tstats->rx_bytes; | ||
109 | sum.tx_packets += tstats->tx_packets; | ||
110 | sum.tx_bytes += tstats->tx_bytes; | ||
111 | } | ||
112 | dev->stats.rx_packets = sum.rx_packets; | ||
113 | dev->stats.rx_bytes = sum.rx_bytes; | ||
114 | dev->stats.tx_packets = sum.tx_packets; | ||
115 | dev->stats.tx_bytes = sum.tx_bytes; | ||
116 | return &dev->stats; | ||
117 | } | ||
118 | |||
91 | /* | 119 | /* |
92 | * Locking : hash tables are protected by RCU and RTNL | 120 | * Locking : hash tables are protected by RCU and RTNL |
93 | */ | 121 | */ |
@@ -216,6 +244,12 @@ ip6_tnl_unlink(struct ip6_tnl_net *ip6n, struct ip6_tnl *t) | |||
216 | } | 244 | } |
217 | } | 245 | } |
218 | 246 | ||
247 | static void ip6_dev_free(struct net_device *dev) | ||
248 | { | ||
249 | free_percpu(dev->tstats); | ||
250 | free_netdev(dev); | ||
251 | } | ||
252 | |||
219 | /** | 253 | /** |
220 | * ip6_tnl_create() - create a new tunnel | 254 | * ip6_tnl_create() - create a new tunnel |
221 | * @p: tunnel parameters | 255 | * @p: tunnel parameters |
@@ -254,7 +288,9 @@ static struct ip6_tnl *ip6_tnl_create(struct net *net, struct ip6_tnl_parm *p) | |||
254 | 288 | ||
255 | t = netdev_priv(dev); | 289 | t = netdev_priv(dev); |
256 | t->parms = *p; | 290 | t->parms = *p; |
257 | ip6_tnl_dev_init(dev); | 291 | err = ip6_tnl_dev_init(dev); |
292 | if (err < 0) | ||
293 | goto failed_free; | ||
258 | 294 | ||
259 | if ((err = register_netdevice(dev)) < 0) | 295 | if ((err = register_netdevice(dev)) < 0) |
260 | goto failed_free; | 296 | goto failed_free; |
@@ -264,7 +300,7 @@ static struct ip6_tnl *ip6_tnl_create(struct net *net, struct ip6_tnl_parm *p) | |||
264 | return t; | 300 | return t; |
265 | 301 | ||
266 | failed_free: | 302 | failed_free: |
267 | free_netdev(dev); | 303 | ip6_dev_free(dev); |
268 | failed: | 304 | failed: |
269 | return NULL; | 305 | return NULL; |
270 | } | 306 | } |
@@ -700,6 +736,8 @@ static int ip6_tnl_rcv(struct sk_buff *skb, __u16 protocol, | |||
700 | 736 | ||
701 | if ((t = ip6_tnl_lookup(dev_net(skb->dev), &ipv6h->saddr, | 737 | if ((t = ip6_tnl_lookup(dev_net(skb->dev), &ipv6h->saddr, |
702 | &ipv6h->daddr)) != NULL) { | 738 | &ipv6h->daddr)) != NULL) { |
739 | struct pcpu_tstats *tstats; | ||
740 | |||
703 | if (t->parms.proto != ipproto && t->parms.proto != 0) { | 741 | if (t->parms.proto != ipproto && t->parms.proto != 0) { |
704 | rcu_read_unlock(); | 742 | rcu_read_unlock(); |
705 | goto discard; | 743 | goto discard; |
@@ -722,7 +760,11 @@ static int ip6_tnl_rcv(struct sk_buff *skb, __u16 protocol, | |||
722 | skb->pkt_type = PACKET_HOST; | 760 | skb->pkt_type = PACKET_HOST; |
723 | memset(skb->cb, 0, sizeof(struct inet6_skb_parm)); | 761 | memset(skb->cb, 0, sizeof(struct inet6_skb_parm)); |
724 | 762 | ||
725 | skb_tunnel_rx(skb, t->dev); | 763 | tstats = this_cpu_ptr(t->dev->tstats); |
764 | tstats->rx_packets++; | ||
765 | tstats->rx_bytes += skb->len; | ||
766 | |||
767 | __skb_tunnel_rx(skb, t->dev); | ||
726 | 768 | ||
727 | dscp_ecn_decapsulate(t, ipv6h, skb); | 769 | dscp_ecn_decapsulate(t, ipv6h, skb); |
728 | 770 | ||
@@ -935,8 +977,10 @@ static int ip6_tnl_xmit2(struct sk_buff *skb, | |||
935 | err = ip6_local_out(skb); | 977 | err = ip6_local_out(skb); |
936 | 978 | ||
937 | if (net_xmit_eval(err) == 0) { | 979 | if (net_xmit_eval(err) == 0) { |
938 | stats->tx_bytes += pkt_len; | 980 | struct pcpu_tstats *tstats = this_cpu_ptr(t->dev->tstats); |
939 | stats->tx_packets++; | 981 | |
982 | tstats->tx_bytes += pkt_len; | ||
983 | tstats->tx_packets++; | ||
940 | } else { | 984 | } else { |
941 | stats->tx_errors++; | 985 | stats->tx_errors++; |
942 | stats->tx_aborted_errors++; | 986 | stats->tx_aborted_errors++; |
@@ -1301,12 +1345,14 @@ ip6_tnl_change_mtu(struct net_device *dev, int new_mtu) | |||
1301 | 1345 | ||
1302 | 1346 | ||
1303 | static const struct net_device_ops ip6_tnl_netdev_ops = { | 1347 | static const struct net_device_ops ip6_tnl_netdev_ops = { |
1304 | .ndo_uninit = ip6_tnl_dev_uninit, | 1348 | .ndo_uninit = ip6_tnl_dev_uninit, |
1305 | .ndo_start_xmit = ip6_tnl_xmit, | 1349 | .ndo_start_xmit = ip6_tnl_xmit, |
1306 | .ndo_do_ioctl = ip6_tnl_ioctl, | 1350 | .ndo_do_ioctl = ip6_tnl_ioctl, |
1307 | .ndo_change_mtu = ip6_tnl_change_mtu, | 1351 | .ndo_change_mtu = ip6_tnl_change_mtu, |
1352 | .ndo_get_stats = ip6_get_stats, | ||
1308 | }; | 1353 | }; |
1309 | 1354 | ||
1355 | |||
1310 | /** | 1356 | /** |
1311 | * ip6_tnl_dev_setup - setup virtual tunnel device | 1357 | * ip6_tnl_dev_setup - setup virtual tunnel device |
1312 | * @dev: virtual device associated with tunnel | 1358 | * @dev: virtual device associated with tunnel |
@@ -1318,7 +1364,7 @@ static const struct net_device_ops ip6_tnl_netdev_ops = { | |||
1318 | static void ip6_tnl_dev_setup(struct net_device *dev) | 1364 | static void ip6_tnl_dev_setup(struct net_device *dev) |
1319 | { | 1365 | { |
1320 | dev->netdev_ops = &ip6_tnl_netdev_ops; | 1366 | dev->netdev_ops = &ip6_tnl_netdev_ops; |
1321 | dev->destructor = free_netdev; | 1367 | dev->destructor = ip6_dev_free; |
1322 | 1368 | ||
1323 | dev->type = ARPHRD_TUNNEL6; | 1369 | dev->type = ARPHRD_TUNNEL6; |
1324 | dev->hard_header_len = LL_MAX_HEADER + sizeof (struct ipv6hdr); | 1370 | dev->hard_header_len = LL_MAX_HEADER + sizeof (struct ipv6hdr); |
@@ -1334,12 +1380,17 @@ static void ip6_tnl_dev_setup(struct net_device *dev) | |||
1334 | * @dev: virtual device associated with tunnel | 1380 | * @dev: virtual device associated with tunnel |
1335 | **/ | 1381 | **/ |
1336 | 1382 | ||
1337 | static inline void | 1383 | static inline int |
1338 | ip6_tnl_dev_init_gen(struct net_device *dev) | 1384 | ip6_tnl_dev_init_gen(struct net_device *dev) |
1339 | { | 1385 | { |
1340 | struct ip6_tnl *t = netdev_priv(dev); | 1386 | struct ip6_tnl *t = netdev_priv(dev); |
1387 | |||
1341 | t->dev = dev; | 1388 | t->dev = dev; |
1342 | strcpy(t->parms.name, dev->name); | 1389 | strcpy(t->parms.name, dev->name); |
1390 | dev->tstats = alloc_percpu(struct pcpu_tstats); | ||
1391 | if (!dev->tstats) | ||
1392 | return -ENOMEM; | ||
1393 | return 0; | ||
1343 | } | 1394 | } |
1344 | 1395 | ||
1345 | /** | 1396 | /** |
@@ -1347,11 +1398,15 @@ ip6_tnl_dev_init_gen(struct net_device *dev) | |||
1347 | * @dev: virtual device associated with tunnel | 1398 | * @dev: virtual device associated with tunnel |
1348 | **/ | 1399 | **/ |
1349 | 1400 | ||
1350 | static void ip6_tnl_dev_init(struct net_device *dev) | 1401 | static int ip6_tnl_dev_init(struct net_device *dev) |
1351 | { | 1402 | { |
1352 | struct ip6_tnl *t = netdev_priv(dev); | 1403 | struct ip6_tnl *t = netdev_priv(dev); |
1353 | ip6_tnl_dev_init_gen(dev); | 1404 | int err = ip6_tnl_dev_init_gen(dev); |
1405 | |||
1406 | if (err) | ||
1407 | return err; | ||
1354 | ip6_tnl_link_config(t); | 1408 | ip6_tnl_link_config(t); |
1409 | return 0; | ||
1355 | } | 1410 | } |
1356 | 1411 | ||
1357 | /** | 1412 | /** |
@@ -1361,16 +1416,20 @@ static void ip6_tnl_dev_init(struct net_device *dev) | |||
1361 | * Return: 0 | 1416 | * Return: 0 |
1362 | **/ | 1417 | **/ |
1363 | 1418 | ||
1364 | static void __net_init ip6_fb_tnl_dev_init(struct net_device *dev) | 1419 | static int __net_init ip6_fb_tnl_dev_init(struct net_device *dev) |
1365 | { | 1420 | { |
1366 | struct ip6_tnl *t = netdev_priv(dev); | 1421 | struct ip6_tnl *t = netdev_priv(dev); |
1367 | struct net *net = dev_net(dev); | 1422 | struct net *net = dev_net(dev); |
1368 | struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id); | 1423 | struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id); |
1424 | int err = ip6_tnl_dev_init_gen(dev); | ||
1425 | |||
1426 | if (err) | ||
1427 | return err; | ||
1369 | 1428 | ||
1370 | ip6_tnl_dev_init_gen(dev); | ||
1371 | t->parms.proto = IPPROTO_IPV6; | 1429 | t->parms.proto = IPPROTO_IPV6; |
1372 | dev_hold(dev); | 1430 | dev_hold(dev); |
1373 | rcu_assign_pointer(ip6n->tnls_wc[0], t); | 1431 | rcu_assign_pointer(ip6n->tnls_wc[0], t); |
1432 | return 0; | ||
1374 | } | 1433 | } |
1375 | 1434 | ||
1376 | static struct xfrm6_tunnel ip4ip6_handler __read_mostly = { | 1435 | static struct xfrm6_tunnel ip4ip6_handler __read_mostly = { |
@@ -1420,7 +1479,9 @@ static int __net_init ip6_tnl_init_net(struct net *net) | |||
1420 | goto err_alloc_dev; | 1479 | goto err_alloc_dev; |
1421 | dev_net_set(ip6n->fb_tnl_dev, net); | 1480 | dev_net_set(ip6n->fb_tnl_dev, net); |
1422 | 1481 | ||
1423 | ip6_fb_tnl_dev_init(ip6n->fb_tnl_dev); | 1482 | err = ip6_fb_tnl_dev_init(ip6n->fb_tnl_dev); |
1483 | if (err < 0) | ||
1484 | goto err_register; | ||
1424 | 1485 | ||
1425 | err = register_netdev(ip6n->fb_tnl_dev); | 1486 | err = register_netdev(ip6n->fb_tnl_dev); |
1426 | if (err < 0) | 1487 | if (err < 0) |
@@ -1428,7 +1489,7 @@ static int __net_init ip6_tnl_init_net(struct net *net) | |||
1428 | return 0; | 1489 | return 0; |
1429 | 1490 | ||
1430 | err_register: | 1491 | err_register: |
1431 | free_netdev(ip6n->fb_tnl_dev); | 1492 | ip6_dev_free(ip6n->fb_tnl_dev); |
1432 | err_alloc_dev: | 1493 | err_alloc_dev: |
1433 | return err; | 1494 | return err; |
1434 | } | 1495 | } |