aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/net/loopback.c32
1 files changed, 28 insertions, 4 deletions
diff --git a/drivers/net/loopback.c b/drivers/net/loopback.c
index 4b6f7b2abea5..f3018bb7570d 100644
--- a/drivers/net/loopback.c
+++ b/drivers/net/loopback.c
@@ -62,7 +62,6 @@ struct pcpu_lstats {
62 unsigned long packets; 62 unsigned long packets;
63 unsigned long bytes; 63 unsigned long bytes;
64}; 64};
65static DEFINE_PER_CPU(struct pcpu_lstats, pcpu_lstats);
66 65
67#define LOOPBACK_OVERHEAD (128 + MAX_HEADER + 16 + 16) 66#define LOOPBACK_OVERHEAD (128 + MAX_HEADER + 16 + 16)
68 67
@@ -134,7 +133,7 @@ static void emulate_large_send_offload(struct sk_buff *skb)
134 */ 133 */
135static int loopback_xmit(struct sk_buff *skb, struct net_device *dev) 134static int loopback_xmit(struct sk_buff *skb, struct net_device *dev)
136{ 135{
137 struct pcpu_lstats *lb_stats; 136 struct pcpu_lstats *pcpu_lstats, *lb_stats;
138 137
139 skb_orphan(skb); 138 skb_orphan(skb);
140 139
@@ -155,7 +154,8 @@ static int loopback_xmit(struct sk_buff *skb, struct net_device *dev)
155 dev->last_rx = jiffies; 154 dev->last_rx = jiffies;
156 155
157 /* it's OK to use __get_cpu_var() because BHs are off */ 156 /* it's OK to use __get_cpu_var() because BHs are off */
158 lb_stats = &__get_cpu_var(pcpu_lstats); 157 pcpu_lstats = netdev_priv(dev);
158 lb_stats = per_cpu_ptr(pcpu_lstats, smp_processor_id());
159 lb_stats->bytes += skb->len; 159 lb_stats->bytes += skb->len;
160 lb_stats->packets++; 160 lb_stats->packets++;
161 161
@@ -166,15 +166,17 @@ static int loopback_xmit(struct sk_buff *skb, struct net_device *dev)
166 166
167static struct net_device_stats *get_stats(struct net_device *dev) 167static struct net_device_stats *get_stats(struct net_device *dev)
168{ 168{
169 const struct pcpu_lstats *pcpu_lstats;
169 struct net_device_stats *stats = &dev->stats; 170 struct net_device_stats *stats = &dev->stats;
170 unsigned long bytes = 0; 171 unsigned long bytes = 0;
171 unsigned long packets = 0; 172 unsigned long packets = 0;
172 int i; 173 int i;
173 174
175 pcpu_lstats = netdev_priv(dev);
174 for_each_possible_cpu(i) { 176 for_each_possible_cpu(i) {
175 const struct pcpu_lstats *lb_stats; 177 const struct pcpu_lstats *lb_stats;
176 178
177 lb_stats = &per_cpu(pcpu_lstats, i); 179 lb_stats = per_cpu_ptr(pcpu_lstats, i);
178 bytes += lb_stats->bytes; 180 bytes += lb_stats->bytes;
179 packets += lb_stats->packets; 181 packets += lb_stats->packets;
180 } 182 }
@@ -198,6 +200,26 @@ static const struct ethtool_ops loopback_ethtool_ops = {
198 .get_rx_csum = always_on, 200 .get_rx_csum = always_on,
199}; 201};
200 202
203static int loopback_dev_init(struct net_device *dev)
204{
205 struct pcpu_lstats *lstats;
206
207 lstats = alloc_percpu(struct pcpu_lstats);
208 if (!lstats)
209 return -ENOMEM;
210
211 dev->priv = lstats;
212 return 0;
213}
214
215static void loopback_dev_free(struct net_device *dev)
216{
217 struct pcpu_lstats *lstats = netdev_priv(dev);
218
219 free_percpu(lstats);
220 free_netdev(dev);
221}
222
201/* 223/*
202 * The loopback device is special. There is only one instance and 224 * The loopback device is special. There is only one instance and
203 * it is statically allocated. Don't do this for other devices. 225 * it is statically allocated. Don't do this for other devices.
@@ -225,6 +247,8 @@ static void loopback_setup(struct net_device *dev)
225 | NETIF_F_LLTX 247 | NETIF_F_LLTX
226 | NETIF_F_NETNS_LOCAL, 248 | NETIF_F_NETNS_LOCAL,
227 dev->ethtool_ops = &loopback_ethtool_ops; 249 dev->ethtool_ops = &loopback_ethtool_ops;
250 dev->init = loopback_dev_init;
251 dev->destructor = loopback_dev_free;
228} 252}
229 253
230/* Setup and register the loopback device. */ 254/* Setup and register the loopback device. */