diff options
author | stephen hemminger <shemminger@vyatta.com> | 2010-03-02 08:32:09 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-03-17 00:23:19 -0400 |
commit | 14bb4789833a2e2610f30e2d3e1451701ac96ec1 (patch) | |
tree | 992a40f1c885d22f6bce27d270e5839b1d8d121b | |
parent | 0a9627f2649a02bea165cfd529d7bcb625c2fcad (diff) |
bridge: per-cpu packet statistics (v3)
The shared packet statistics are a potential source of slow down
on bridged traffic. Convert to per-cpu array, but only keep those
statistics which change per-packet.
Signed-off-by: Stephen Hemminger <shemminger@vyatta.com>
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | net/bridge/br_device.c | 43 | ||||
-rw-r--r-- | net/bridge/br_if.c | 6 | ||||
-rw-r--r-- | net/bridge/br_input.c | 6 | ||||
-rw-r--r-- | net/bridge/br_private.h | 8 |
4 files changed, 57 insertions, 6 deletions
diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index 90a9024e5c1e..5b8a6e73b02f 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c | |||
@@ -26,11 +26,12 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev) | |||
26 | const unsigned char *dest = skb->data; | 26 | const unsigned char *dest = skb->data; |
27 | struct net_bridge_fdb_entry *dst; | 27 | struct net_bridge_fdb_entry *dst; |
28 | struct net_bridge_mdb_entry *mdst; | 28 | struct net_bridge_mdb_entry *mdst; |
29 | struct br_cpu_netstats *brstats = this_cpu_ptr(br->stats); | ||
29 | 30 | ||
30 | BR_INPUT_SKB_CB(skb)->brdev = dev; | 31 | brstats->tx_packets++; |
32 | brstats->tx_bytes += skb->len; | ||
31 | 33 | ||
32 | dev->stats.tx_packets++; | 34 | BR_INPUT_SKB_CB(skb)->brdev = dev; |
33 | dev->stats.tx_bytes += skb->len; | ||
34 | 35 | ||
35 | skb_reset_mac_header(skb); | 36 | skb_reset_mac_header(skb); |
36 | skb_pull(skb, ETH_HLEN); | 37 | skb_pull(skb, ETH_HLEN); |
@@ -81,6 +82,31 @@ static int br_dev_stop(struct net_device *dev) | |||
81 | return 0; | 82 | return 0; |
82 | } | 83 | } |
83 | 84 | ||
85 | static struct net_device_stats *br_get_stats(struct net_device *dev) | ||
86 | { | ||
87 | struct net_bridge *br = netdev_priv(dev); | ||
88 | struct net_device_stats *stats = &dev->stats; | ||
89 | struct br_cpu_netstats sum = { 0 }; | ||
90 | unsigned int cpu; | ||
91 | |||
92 | for_each_possible_cpu(cpu) { | ||
93 | const struct br_cpu_netstats *bstats | ||
94 | = per_cpu_ptr(br->stats, cpu); | ||
95 | |||
96 | sum.tx_bytes += bstats->tx_bytes; | ||
97 | sum.tx_packets += bstats->tx_packets; | ||
98 | sum.rx_bytes += bstats->rx_bytes; | ||
99 | sum.rx_packets += bstats->rx_packets; | ||
100 | } | ||
101 | |||
102 | stats->tx_bytes = sum.tx_bytes; | ||
103 | stats->tx_packets = sum.tx_packets; | ||
104 | stats->rx_bytes = sum.rx_bytes; | ||
105 | stats->rx_packets = sum.rx_packets; | ||
106 | |||
107 | return stats; | ||
108 | } | ||
109 | |||
84 | static int br_change_mtu(struct net_device *dev, int new_mtu) | 110 | static int br_change_mtu(struct net_device *dev, int new_mtu) |
85 | { | 111 | { |
86 | struct net_bridge *br = netdev_priv(dev); | 112 | struct net_bridge *br = netdev_priv(dev); |
@@ -180,19 +206,28 @@ static const struct net_device_ops br_netdev_ops = { | |||
180 | .ndo_open = br_dev_open, | 206 | .ndo_open = br_dev_open, |
181 | .ndo_stop = br_dev_stop, | 207 | .ndo_stop = br_dev_stop, |
182 | .ndo_start_xmit = br_dev_xmit, | 208 | .ndo_start_xmit = br_dev_xmit, |
209 | .ndo_get_stats = br_get_stats, | ||
183 | .ndo_set_mac_address = br_set_mac_address, | 210 | .ndo_set_mac_address = br_set_mac_address, |
184 | .ndo_set_multicast_list = br_dev_set_multicast_list, | 211 | .ndo_set_multicast_list = br_dev_set_multicast_list, |
185 | .ndo_change_mtu = br_change_mtu, | 212 | .ndo_change_mtu = br_change_mtu, |
186 | .ndo_do_ioctl = br_dev_ioctl, | 213 | .ndo_do_ioctl = br_dev_ioctl, |
187 | }; | 214 | }; |
188 | 215 | ||
216 | static void br_dev_free(struct net_device *dev) | ||
217 | { | ||
218 | struct net_bridge *br = netdev_priv(dev); | ||
219 | |||
220 | free_percpu(br->stats); | ||
221 | free_netdev(dev); | ||
222 | } | ||
223 | |||
189 | void br_dev_setup(struct net_device *dev) | 224 | void br_dev_setup(struct net_device *dev) |
190 | { | 225 | { |
191 | random_ether_addr(dev->dev_addr); | 226 | random_ether_addr(dev->dev_addr); |
192 | ether_setup(dev); | 227 | ether_setup(dev); |
193 | 228 | ||
194 | dev->netdev_ops = &br_netdev_ops; | 229 | dev->netdev_ops = &br_netdev_ops; |
195 | dev->destructor = free_netdev; | 230 | dev->destructor = br_dev_free; |
196 | SET_ETHTOOL_OPS(dev, &br_ethtool_ops); | 231 | SET_ETHTOOL_OPS(dev, &br_ethtool_ops); |
197 | dev->tx_queue_len = 0; | 232 | dev->tx_queue_len = 0; |
198 | dev->priv_flags = IFF_EBRIDGE; | 233 | dev->priv_flags = IFF_EBRIDGE; |
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index b6a3872f5681..b7cdd2e98050 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c | |||
@@ -185,6 +185,12 @@ static struct net_device *new_bridge_dev(struct net *net, const char *name) | |||
185 | br = netdev_priv(dev); | 185 | br = netdev_priv(dev); |
186 | br->dev = dev; | 186 | br->dev = dev; |
187 | 187 | ||
188 | br->stats = alloc_percpu(struct br_cpu_netstats); | ||
189 | if (!br->stats) { | ||
190 | free_netdev(dev); | ||
191 | return NULL; | ||
192 | } | ||
193 | |||
188 | spin_lock_init(&br->lock); | 194 | spin_lock_init(&br->lock); |
189 | INIT_LIST_HEAD(&br->port_list); | 195 | INIT_LIST_HEAD(&br->port_list); |
190 | spin_lock_init(&br->hash_lock); | 196 | spin_lock_init(&br->hash_lock); |
diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c index d74d570fc848..333dfb7c5886 100644 --- a/net/bridge/br_input.c +++ b/net/bridge/br_input.c | |||
@@ -23,9 +23,11 @@ const u8 br_group_address[ETH_ALEN] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 }; | |||
23 | static int br_pass_frame_up(struct sk_buff *skb) | 23 | static int br_pass_frame_up(struct sk_buff *skb) |
24 | { | 24 | { |
25 | struct net_device *indev, *brdev = BR_INPUT_SKB_CB(skb)->brdev; | 25 | struct net_device *indev, *brdev = BR_INPUT_SKB_CB(skb)->brdev; |
26 | struct net_bridge *br = netdev_priv(brdev); | ||
27 | struct br_cpu_netstats *brstats = this_cpu_ptr(br->stats); | ||
26 | 28 | ||
27 | brdev->stats.rx_packets++; | 29 | brstats->rx_packets++; |
28 | brdev->stats.rx_bytes += skb->len; | 30 | brstats->rx_bytes += skb->len; |
29 | 31 | ||
30 | indev = skb->dev; | 32 | indev = skb->dev; |
31 | skb->dev = brdev; | 33 | skb->dev = brdev; |
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 846d7d1e2075..791d4ab0fd4d 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h | |||
@@ -135,6 +135,14 @@ struct net_bridge | |||
135 | spinlock_t lock; | 135 | spinlock_t lock; |
136 | struct list_head port_list; | 136 | struct list_head port_list; |
137 | struct net_device *dev; | 137 | struct net_device *dev; |
138 | |||
139 | struct br_cpu_netstats __percpu { | ||
140 | unsigned long rx_packets; | ||
141 | unsigned long rx_bytes; | ||
142 | unsigned long tx_packets; | ||
143 | unsigned long tx_bytes; | ||
144 | } *stats; | ||
145 | |||
138 | spinlock_t hash_lock; | 146 | spinlock_t hash_lock; |
139 | struct hlist_head hash[BR_HASH_SIZE]; | 147 | struct hlist_head hash[BR_HASH_SIZE]; |
140 | unsigned long feature_mask; | 148 | unsigned long feature_mask; |