loopback.c 5.8 KB
Newer Older
L
Linus Torvalds 已提交
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
 *
10
 * Authors:	Ross Biro
L
Linus Torvalds 已提交
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>
60
#include <net/net_namespace.h>
61
#include <linux/u64_stats_sync.h>
L
Linus Torvalds 已提交
62

63
struct pcpu_lstats {
64 65 66 67
	u64			packets;
	u64			bytes;
	struct u64_stats_sync	syncp;
	unsigned long		drops;
68
};
L
Linus Torvalds 已提交
69 70 71 72 73

/*
 * The higher levels take care of making this non-reentrant (it's
 * called with bh's disabled).
 */
74 75
static netdev_tx_t loopback_xmit(struct sk_buff *skb,
				 struct net_device *dev)
L
Linus Torvalds 已提交
76
{
77 78
	struct pcpu_lstats __percpu *pcpu_lstats;
	struct pcpu_lstats *lb_stats;
E
Eric Dumazet 已提交
79
	int len;
L
Linus Torvalds 已提交
80 81 82

	skb_orphan(skb);

E
Eric Dumazet 已提交
83
	skb->protocol = eth_type_trans(skb, dev);
L
Linus Torvalds 已提交
84

85
	/* it's OK to use per_cpu_ptr() because BHs are off */
86
	pcpu_lstats = (void __percpu __force *)dev->ml_priv;
87
	lb_stats = this_cpu_ptr(pcpu_lstats);
L
Linus Torvalds 已提交
88

E
Eric Dumazet 已提交
89 90
	len = skb->len;
	if (likely(netif_rx(skb) == NET_RX_SUCCESS)) {
91
		u64_stats_update_begin(&lb_stats->syncp);
E
Eric Dumazet 已提交
92 93
		lb_stats->bytes += len;
		lb_stats->packets++;
94
		u64_stats_update_end(&lb_stats->syncp);
E
Eric Dumazet 已提交
95 96
	} else
		lb_stats->drops++;
L
Linus Torvalds 已提交
97

98
	return NETDEV_TX_OK;
L
Linus Torvalds 已提交
99 100
}

101 102
static struct rtnl_link_stats64 *loopback_get_stats64(struct net_device *dev,
						      struct rtnl_link_stats64 *stats)
L
Linus Torvalds 已提交
103
{
104
	const struct pcpu_lstats __percpu *pcpu_lstats;
105 106 107
	u64 bytes = 0;
	u64 packets = 0;
	u64 drops = 0;
L
Linus Torvalds 已提交
108 109
	int i;

110
	pcpu_lstats = (void __percpu __force *)dev->ml_priv;
111
	for_each_possible_cpu(i) {
112
		const struct pcpu_lstats *lb_stats;
113 114
		u64 tbytes, tpackets;
		unsigned int start;
L
Linus Torvalds 已提交
115

116
		lb_stats = per_cpu_ptr(pcpu_lstats, i);
117 118 119 120 121
		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));
E
Eric Dumazet 已提交
122
		drops   += lb_stats->drops;
123 124
		bytes   += tbytes;
		packets += tpackets;
L
Linus Torvalds 已提交
125
	}
126 127
	stats->rx_packets = packets;
	stats->tx_packets = packets;
E
Eric Dumazet 已提交
128 129 130 131
	stats->rx_dropped = drops;
	stats->rx_errors  = drops;
	stats->rx_bytes   = bytes;
	stats->tx_bytes   = bytes;
L
Linus Torvalds 已提交
132 133 134
	return stats;
}

135
static u32 always_on(struct net_device *dev)
L
Linus Torvalds 已提交
136 137 138 139
{
	return 1;
}

140
static const struct ethtool_ops loopback_ethtool_ops = {
141
	.get_link		= always_on,
L
Linus Torvalds 已提交
142
	.set_tso		= ethtool_op_set_tso,
143 144 145
	.get_tx_csum		= always_on,
	.get_sg			= always_on,
	.get_rx_csum		= always_on,
L
Linus Torvalds 已提交
146 147
};

148 149
static int loopback_dev_init(struct net_device *dev)
{
150
	struct pcpu_lstats __percpu *lstats;
151 152 153 154 155

	lstats = alloc_percpu(struct pcpu_lstats);
	if (!lstats)
		return -ENOMEM;

156
	dev->ml_priv = (void __force *)lstats;
157 158 159 160 161
	return 0;
}

static void loopback_dev_free(struct net_device *dev)
{
162 163
	struct pcpu_lstats __percpu *lstats =
		(void __percpu __force *)dev->ml_priv;
164 165 166 167 168

	free_percpu(lstats);
	free_netdev(dev);
}

169 170
static const struct net_device_ops loopback_ops = {
	.ndo_init      = loopback_dev_init,
171
	.ndo_start_xmit= loopback_xmit,
172
	.ndo_get_stats64 = loopback_get_stats64,
173 174
};

175
/*
176 177
 * The loopback device is special. There is only one instance
 * per network namespace.
178
 */
179 180 181 182 183 184 185 186
static void loopback_setup(struct net_device *dev)
{
	dev->mtu		= (16 * 1024) + 20 + 20 + 12;
	dev->hard_header_len	= ETH_HLEN;	/* 14	*/
	dev->addr_len		= ETH_ALEN;	/* 6	*/
	dev->tx_queue_len	= 0;
	dev->type		= ARPHRD_LOOPBACK;	/* 0x0001*/
	dev->flags		= IFF_LOOPBACK;
187
	dev->priv_flags	       &= ~IFF_XMIT_DST_RELEASE;
188 189 190 191 192
	dev->features 		= NETIF_F_SG | NETIF_F_FRAGLIST
		| NETIF_F_TSO
		| NETIF_F_NO_CSUM
		| NETIF_F_HIGHDMA
		| NETIF_F_LLTX
193
		| NETIF_F_NETNS_LOCAL;
194
	dev->ethtool_ops	= &loopback_ethtool_ops;
195
	dev->header_ops		= &eth_header_ops;
196 197
	dev->netdev_ops		= &loopback_ops;
	dev->destructor		= loopback_dev_free;
198
}
199

200
/* Setup and register the loopback device. */
201
static __net_init int loopback_net_init(struct net *net)
L
Linus Torvalds 已提交
202
{
203 204 205 206 207 208 209
	struct net_device *dev;
	int err;

	err = -ENOMEM;
	dev = alloc_netdev(0, "lo", loopback_setup);
	if (!dev)
		goto out;
210

211
	dev_net_set(dev, net);
212
	err = register_netdev(dev);
213
	if (err)
214
		goto out_free_netdev;
215

216
	net->loopback_dev = dev;
217
	return 0;
218

L
Linus Torvalds 已提交
219

220 221
out_free_netdev:
	free_netdev(dev);
222
out:
O
Octavian Purdila 已提交
223
	if (net_eq(net, &init_net))
224 225
		panic("loopback: Failed to register netdevice: %d\n", err);
	return err;
226 227
}

228 229
/* Registered in net/core/dev.c */
struct pernet_operations __net_initdata loopback_net_ops = {
230 231
       .init = loopback_net_init,
};