Blame view
drivers/net/loopback.c
5.36 KB
1da177e4c
|
1 2 3 4 5 6 7 8 9 |
/* * INET An implementation of the TCP/IP protocol suite for the LINUX * operating system. INET is implemented using the BSD Socket * interface as the means of communication with the user level. * * Pseudo-driver for the loopback interface. * * Version: @(#)loopback.c 1.0.4b 08/16/93 * |
02c30a84e
|
10 |
* Authors: Ross Biro |
1da177e4c
|
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
* Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> * Donald Becker, <becker@scyld.com> * * Alan Cox : Fixed oddments for NET3.014 * Alan Cox : Rejig for NET3.029 snap #3 * Alan Cox : Fixed NET3.029 bugs and sped up * Larry McVoy : Tiny tweak to double performance * Alan Cox : Backed out LMV's tweak - the linux mm * can't take it... * Michael Griffith: Don't bother computing the checksums * on packets received on the loopback * interface. * Alexey Kuznetsov: Potential hang under some extreme * cases removed. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ #include <linux/kernel.h> #include <linux/jiffies.h> #include <linux/module.h> #include <linux/interrupt.h> #include <linux/fs.h> #include <linux/types.h> #include <linux/string.h> #include <linux/socket.h> #include <linux/errno.h> #include <linux/fcntl.h> #include <linux/in.h> #include <linux/init.h> #include <asm/system.h> #include <asm/uaccess.h> #include <asm/io.h> #include <linux/inet.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> #include <linux/skbuff.h> #include <linux/ethtool.h> #include <net/sock.h> #include <net/checksum.h> #include <linux/if_ether.h> /* For the statistics structure. */ #include <linux/if_arp.h> /* For ARPHRD_ETHER */ #include <linux/ip.h> #include <linux/tcp.h> #include <linux/percpu.h> |
2774c7aba
|
60 |
#include <net/net_namespace.h> |
5eaa0bd81
|
61 |
#include <linux/u64_stats_sync.h> |
1da177e4c
|
62 |
|
5175c3786
|
63 |
struct pcpu_lstats { |
5eaa0bd81
|
64 65 66 |
u64 packets; u64 bytes; struct u64_stats_sync syncp; |
5175c3786
|
67 |
}; |
1da177e4c
|
68 |
|
1da177e4c
|
69 70 71 72 |
/* * The higher levels take care of making this non-reentrant (it's * called with bh's disabled). */ |
61357325f
|
73 74 |
static netdev_tx_t loopback_xmit(struct sk_buff *skb, struct net_device *dev) |
1da177e4c
|
75 |
{ |
47d742752
|
76 |
struct pcpu_lstats *lb_stats; |
7eebb0b28
|
77 |
int len; |
1da177e4c
|
78 79 |
skb_orphan(skb); |
7eebb0b28
|
80 |
skb->protocol = eth_type_trans(skb, dev); |
1da177e4c
|
81 |
|
9e0db4b12
|
82 |
/* it's OK to use per_cpu_ptr() because BHs are off */ |
a7855c78a
|
83 |
lb_stats = this_cpu_ptr(dev->lstats); |
1da177e4c
|
84 |
|
7eebb0b28
|
85 86 |
len = skb->len; if (likely(netif_rx(skb) == NET_RX_SUCCESS)) { |
5eaa0bd81
|
87 |
u64_stats_update_begin(&lb_stats->syncp); |
7eebb0b28
|
88 89 |
lb_stats->bytes += len; lb_stats->packets++; |
5eaa0bd81
|
90 |
u64_stats_update_end(&lb_stats->syncp); |
caf586e5f
|
91 |
} |
1da177e4c
|
92 |
|
6ed106549
|
93 |
return NETDEV_TX_OK; |
1da177e4c
|
94 |
} |
28172739f
|
95 96 |
static struct rtnl_link_stats64 *loopback_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats) |
1da177e4c
|
97 |
{ |
6b10de38f
|
98 99 |
u64 bytes = 0; u64 packets = 0; |
1da177e4c
|
100 |
int i; |
0fed48463
|
101 |
for_each_possible_cpu(i) { |
5175c3786
|
102 |
const struct pcpu_lstats *lb_stats; |
5eaa0bd81
|
103 104 |
u64 tbytes, tpackets; unsigned int start; |
1da177e4c
|
105 |
|
a7855c78a
|
106 |
lb_stats = per_cpu_ptr(dev->lstats, i); |
5eaa0bd81
|
107 108 109 110 111 |
do { start = u64_stats_fetch_begin(&lb_stats->syncp); tbytes = lb_stats->bytes; tpackets = lb_stats->packets; } while (u64_stats_fetch_retry(&lb_stats->syncp, start)); |
5eaa0bd81
|
112 113 |
bytes += tbytes; packets += tpackets; |
1da177e4c
|
114 |
} |
5175c3786
|
115 116 |
stats->rx_packets = packets; stats->tx_packets = packets; |
7eebb0b28
|
117 118 |
stats->rx_bytes = bytes; stats->tx_bytes = bytes; |
1da177e4c
|
119 120 |
return stats; } |
7fa6b0668
|
121 |
static u32 always_on(struct net_device *dev) |
1da177e4c
|
122 123 124 |
{ return 1; } |
7282d491e
|
125 |
static const struct ethtool_ops loopback_ethtool_ops = { |
7fa6b0668
|
126 |
.get_link = always_on, |
1da177e4c
|
127 |
}; |
5f6d88b91
|
128 129 |
static int loopback_dev_init(struct net_device *dev) { |
a7855c78a
|
130 131 |
dev->lstats = alloc_percpu(struct pcpu_lstats); if (!dev->lstats) |
5f6d88b91
|
132 |
return -ENOMEM; |
5f6d88b91
|
133 134 135 136 137 |
return 0; } static void loopback_dev_free(struct net_device *dev) { |
a7855c78a
|
138 |
free_percpu(dev->lstats); |
5f6d88b91
|
139 140 |
free_netdev(dev); } |
c02373bf2
|
141 142 |
static const struct net_device_ops loopback_ops = { .ndo_init = loopback_dev_init, |
008298231
|
143 |
.ndo_start_xmit= loopback_xmit, |
6b10de38f
|
144 |
.ndo_get_stats64 = loopback_get_stats64, |
c02373bf2
|
145 |
}; |
7fa6b0668
|
146 |
/* |
9e0db4b12
|
147 148 |
* The loopback device is special. There is only one instance * per network namespace. |
7fa6b0668
|
149 |
*/ |
854d8363f
|
150 151 |
static void loopback_setup(struct net_device *dev) { |
854d8363f
|
152 |
dev->mtu = (16 * 1024) + 20 + 20 + 12; |
854d8363f
|
153 154 155 156 |
dev->hard_header_len = ETH_HLEN; /* 14 */ dev->addr_len = ETH_ALEN; /* 6 */ dev->tx_queue_len = 0; dev->type = ARPHRD_LOOPBACK; /* 0x0001*/ |
854d8363f
|
157 |
dev->flags = IFF_LOOPBACK; |
93f154b59
|
158 |
dev->priv_flags &= ~IFF_XMIT_DST_RELEASE; |
cf0bdefd4
|
159 |
dev->hw_features = NETIF_F_ALL_TSO | NETIF_F_UFO; |
854d8363f
|
160 |
dev->features = NETIF_F_SG | NETIF_F_FRAGLIST |
cf0bdefd4
|
161 162 |
| NETIF_F_ALL_TSO | NETIF_F_UFO |
854d8363f
|
163 |
| NETIF_F_NO_CSUM |
cf0bdefd4
|
164 |
| NETIF_F_RXCSUM |
854d8363f
|
165 166 |
| NETIF_F_HIGHDMA | NETIF_F_LLTX |
0553c891f
|
167 |
| NETIF_F_NETNS_LOCAL |
eed2a12f1
|
168 169 |
| NETIF_F_VLAN_CHALLENGED | NETIF_F_LOOPBACK; |
854d8363f
|
170 |
dev->ethtool_ops = &loopback_ethtool_ops; |
3b04ddde0
|
171 |
dev->header_ops = ð_header_ops; |
c02373bf2
|
172 173 |
dev->netdev_ops = &loopback_ops; dev->destructor = loopback_dev_free; |
854d8363f
|
174 |
} |
de3cb747f
|
175 |
|
227836495
|
176 |
/* Setup and register the loopback device. */ |
4665079cb
|
177 |
static __net_init int loopback_net_init(struct net *net) |
1da177e4c
|
178 |
{ |
854d8363f
|
179 180 181 182 183 184 185 |
struct net_device *dev; int err; err = -ENOMEM; dev = alloc_netdev(0, "lo", loopback_setup); if (!dev) goto out; |
aeed9e82c
|
186 |
|
c346dca10
|
187 |
dev_net_set(dev, net); |
854d8363f
|
188 |
err = register_netdev(dev); |
aeed9e82c
|
189 |
if (err) |
854d8363f
|
190 |
goto out_free_netdev; |
aeed9e82c
|
191 |
|
2774c7aba
|
192 |
net->loopback_dev = dev; |
9d6dda32c
|
193 |
return 0; |
854d8363f
|
194 |
|
1da177e4c
|
195 |
|
854d8363f
|
196 197 |
out_free_netdev: free_netdev(dev); |
9d6dda32c
|
198 |
out: |
09ad9bc75
|
199 |
if (net_eq(net, &init_net)) |
9d6dda32c
|
200 201 202 |
panic("loopback: Failed to register netdevice: %d ", err); return err; |
854d8363f
|
203 |
} |
505d4f73d
|
204 205 |
/* Registered in net/core/dev.c */ struct pernet_operations __net_initdata loopback_net_ops = { |
2774c7aba
|
206 |
.init = loopback_net_init, |
2774c7aba
|
207 |
}; |