aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6
diff options
context:
space:
mode:
authorEric Dumazet <eric.dumazet@gmail.com>2010-09-27 23:23:34 -0400
committerDavid S. Miller <davem@davemloft.net>2010-09-29 16:25:52 -0400
commit8560f2266b36adb43238f1f9fd13958dd031901c (patch)
treeda92b380a566f5e2daba65678b874f0f2c4df3cb /net/ipv6
parent153f0943382e9ae0bff7caa110a1a4656088d0d4 (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.c93
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
78static void ip6_tnl_dev_init(struct net_device *dev); 78static int ip6_tnl_dev_init(struct net_device *dev);
79static void ip6_tnl_dev_setup(struct net_device *dev); 79static void ip6_tnl_dev_setup(struct net_device *dev);
80 80
81static int ip6_tnl_net_id __read_mostly; 81static 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) */
92struct pcpu_tstats {
93 unsigned long rx_packets;
94 unsigned long rx_bytes;
95 unsigned long tx_packets;
96 unsigned long tx_bytes;
97};
98
99static 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
247static 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
266failed_free: 302failed_free:
267 free_netdev(dev); 303 ip6_dev_free(dev);
268failed: 304failed:
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
1303static const struct net_device_ops ip6_tnl_netdev_ops = { 1347static 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 = {
1318static void ip6_tnl_dev_setup(struct net_device *dev) 1364static 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
1337static inline void 1383static inline int
1338ip6_tnl_dev_init_gen(struct net_device *dev) 1384ip6_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
1350static void ip6_tnl_dev_init(struct net_device *dev) 1401static 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
1364static void __net_init ip6_fb_tnl_dev_init(struct net_device *dev) 1419static 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
1376static struct xfrm6_tunnel ip4ip6_handler __read_mostly = { 1435static 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
1430err_register: 1491err_register:
1431 free_netdev(ip6n->fb_tnl_dev); 1492 ip6_dev_free(ip6n->fb_tnl_dev);
1432err_alloc_dev: 1493err_alloc_dev:
1433 return err; 1494 return err;
1434} 1495}