OpenCores
URL https://opencores.org/ocsvn/test_project/test_project/trunk

Subversion Repositories test_project

[/] [test_project/] [trunk/] [linux_sd_driver/] [drivers/] [net/] [loopback.c] - Blame information for rev 62

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 62 marcus.erl
/*
2
 * INET         An implementation of the TCP/IP protocol suite for the LINUX
3
 *              operating system.  INET is implemented using the  BSD Socket
4
 *              interface as the means of communication with the user level.
5
 *
6
 *              Pseudo-driver for the loopback interface.
7
 *
8
 * Version:     @(#)loopback.c  1.0.4b  08/16/93
9
 *
10
 * Authors:     Ross Biro
11
 *              Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
12
 *              Donald Becker, <becker@scyld.com>
13
 *
14
 *              Alan Cox        :       Fixed oddments for NET3.014
15
 *              Alan Cox        :       Rejig for NET3.029 snap #3
16
 *              Alan Cox        :       Fixed NET3.029 bugs and sped up
17
 *              Larry McVoy     :       Tiny tweak to double performance
18
 *              Alan Cox        :       Backed out LMV's tweak - the linux mm
19
 *                                      can't take it...
20
 *              Michael Griffith:       Don't bother computing the checksums
21
 *                                      on packets received on the loopback
22
 *                                      interface.
23
 *              Alexey Kuznetsov:       Potential hang under some extreme
24
 *                                      cases removed.
25
 *
26
 *              This program is free software; you can redistribute it and/or
27
 *              modify it under the terms of the GNU General Public License
28
 *              as published by the Free Software Foundation; either version
29
 *              2 of the License, or (at your option) any later version.
30
 */
31
#include <linux/kernel.h>
32
#include <linux/jiffies.h>
33
#include <linux/module.h>
34
#include <linux/interrupt.h>
35
#include <linux/fs.h>
36
#include <linux/types.h>
37
#include <linux/string.h>
38
#include <linux/socket.h>
39
#include <linux/errno.h>
40
#include <linux/fcntl.h>
41
#include <linux/in.h>
42
#include <linux/init.h>
43
 
44
#include <asm/system.h>
45
#include <asm/uaccess.h>
46
#include <asm/io.h>
47
 
48
#include <linux/inet.h>
49
#include <linux/netdevice.h>
50
#include <linux/etherdevice.h>
51
#include <linux/skbuff.h>
52
#include <linux/ethtool.h>
53
#include <net/sock.h>
54
#include <net/checksum.h>
55
#include <linux/if_ether.h>     /* For the statistics structure. */
56
#include <linux/if_arp.h>       /* For ARPHRD_ETHER */
57
#include <linux/ip.h>
58
#include <linux/tcp.h>
59
#include <linux/percpu.h>
60
#include <net/net_namespace.h>
61
 
62
struct pcpu_lstats {
63
        unsigned long packets;
64
        unsigned long bytes;
65
};
66
 
67
#define LOOPBACK_OVERHEAD (128 + MAX_HEADER + 16 + 16)
68
 
69
/* KISS: just allocate small chunks and copy bits.
70
 *
71
 * So, in fact, this is documentation, explaining what we expect
72
 * of largesending device modulo TCP checksum, which is ignored for loopback.
73
 */
74
 
75
#ifdef LOOPBACK_TSO
76
static void emulate_large_send_offload(struct sk_buff *skb)
77
{
78
        struct iphdr *iph = ip_hdr(skb);
79
        struct tcphdr *th = (struct tcphdr *)(skb_network_header(skb) +
80
                                              (iph->ihl * 4));
81
        unsigned int doffset = (iph->ihl + th->doff) * 4;
82
        unsigned int mtu = skb_shinfo(skb)->gso_size + doffset;
83
        unsigned int offset = 0;
84
        u32 seq = ntohl(th->seq);
85
        u16 id  = ntohs(iph->id);
86
 
87
        while (offset + doffset < skb->len) {
88
                unsigned int frag_size = min(mtu, skb->len - offset) - doffset;
89
                struct sk_buff *nskb = alloc_skb(mtu + 32, GFP_ATOMIC);
90
 
91
                if (!nskb)
92
                        break;
93
                skb_reserve(nskb, 32);
94
                skb_set_mac_header(nskb, -ETH_HLEN);
95
                skb_reset_network_header(nskb);
96
                iph = ip_hdr(nskb);
97
                skb_copy_to_linear_data(nskb, skb_network_header(skb),
98
                                        doffset);
99
                if (skb_copy_bits(skb,
100
                                  doffset + offset,
101
                                  nskb->data + doffset,
102
                                  frag_size))
103
                        BUG();
104
                skb_put(nskb, doffset + frag_size);
105
                nskb->ip_summed = CHECKSUM_UNNECESSARY;
106
                nskb->dev = skb->dev;
107
                nskb->priority = skb->priority;
108
                nskb->protocol = skb->protocol;
109
                nskb->dst = dst_clone(skb->dst);
110
                memcpy(nskb->cb, skb->cb, sizeof(skb->cb));
111
                nskb->pkt_type = skb->pkt_type;
112
 
113
                th = (struct tcphdr *)(skb_network_header(nskb) + iph->ihl * 4);
114
                iph->tot_len = htons(frag_size + doffset);
115
                iph->id = htons(id);
116
                iph->check = 0;
117
                iph->check = ip_fast_csum((unsigned char *) iph, iph->ihl);
118
                th->seq = htonl(seq);
119
                if (offset + doffset + frag_size < skb->len)
120
                        th->fin = th->psh = 0;
121
                netif_rx(nskb);
122
                offset += frag_size;
123
                seq += frag_size;
124
                id++;
125
        }
126
 
127
        dev_kfree_skb(skb);
128
}
129
#endif /* LOOPBACK_TSO */
130
 
131
/*
132
 * The higher levels take care of making this non-reentrant (it's
133
 * called with bh's disabled).
134
 */
135
static int loopback_xmit(struct sk_buff *skb, struct net_device *dev)
136
{
137
        struct pcpu_lstats *pcpu_lstats, *lb_stats;
138
 
139
        skb_orphan(skb);
140
 
141
        skb->protocol = eth_type_trans(skb,dev);
142
#ifndef LOOPBACK_MUST_CHECKSUM
143
        skb->ip_summed = CHECKSUM_UNNECESSARY;
144
#endif
145
 
146
#ifdef LOOPBACK_TSO
147
        if (skb_is_gso(skb)) {
148
                BUG_ON(skb->protocol != htons(ETH_P_IP));
149
                BUG_ON(ip_hdr(skb)->protocol != IPPROTO_TCP);
150
 
151
                emulate_large_send_offload(skb);
152
                return 0;
153
        }
154
#endif
155
        dev->last_rx = jiffies;
156
 
157
        /* it's OK to use per_cpu_ptr() because BHs are off */
158
        pcpu_lstats = netdev_priv(dev);
159
        lb_stats = per_cpu_ptr(pcpu_lstats, smp_processor_id());
160
        lb_stats->bytes += skb->len;
161
        lb_stats->packets++;
162
 
163
        netif_rx(skb);
164
 
165
        return 0;
166
}
167
 
168
static struct net_device_stats *get_stats(struct net_device *dev)
169
{
170
        const struct pcpu_lstats *pcpu_lstats;
171
        struct net_device_stats *stats = &dev->stats;
172
        unsigned long bytes = 0;
173
        unsigned long packets = 0;
174
        int i;
175
 
176
        pcpu_lstats = netdev_priv(dev);
177
        for_each_possible_cpu(i) {
178
                const struct pcpu_lstats *lb_stats;
179
 
180
                lb_stats = per_cpu_ptr(pcpu_lstats, i);
181
                bytes   += lb_stats->bytes;
182
                packets += lb_stats->packets;
183
        }
184
        stats->rx_packets = packets;
185
        stats->tx_packets = packets;
186
        stats->rx_bytes = bytes;
187
        stats->tx_bytes = bytes;
188
        return stats;
189
}
190
 
191
static u32 always_on(struct net_device *dev)
192
{
193
        return 1;
194
}
195
 
196
static const struct ethtool_ops loopback_ethtool_ops = {
197
        .get_link               = always_on,
198
        .set_tso                = ethtool_op_set_tso,
199
        .get_tx_csum            = always_on,
200
        .get_sg                 = always_on,
201
        .get_rx_csum            = always_on,
202
};
203
 
204
static int loopback_dev_init(struct net_device *dev)
205
{
206
        struct pcpu_lstats *lstats;
207
 
208
        lstats = alloc_percpu(struct pcpu_lstats);
209
        if (!lstats)
210
                return -ENOMEM;
211
 
212
        dev->priv = lstats;
213
        return 0;
214
}
215
 
216
static void loopback_dev_free(struct net_device *dev)
217
{
218
        struct pcpu_lstats *lstats = netdev_priv(dev);
219
 
220
        free_percpu(lstats);
221
        free_netdev(dev);
222
}
223
 
224
/*
225
 * The loopback device is special. There is only one instance
226
 * per network namespace.
227
 */
228
static void loopback_setup(struct net_device *dev)
229
{
230
        dev->get_stats          = &get_stats;
231
        dev->mtu                = (16 * 1024) + 20 + 20 + 12;
232
        dev->hard_start_xmit    = loopback_xmit;
233
        dev->hard_header_len    = ETH_HLEN;     /* 14   */
234
        dev->addr_len           = ETH_ALEN;     /* 6    */
235
        dev->tx_queue_len       = 0;
236
        dev->type               = ARPHRD_LOOPBACK;      /* 0x0001*/
237
        dev->flags              = IFF_LOOPBACK;
238
        dev->features           = NETIF_F_SG | NETIF_F_FRAGLIST
239
#ifdef LOOPBACK_TSO
240
                | NETIF_F_TSO
241
#endif
242
                | NETIF_F_NO_CSUM
243
                | NETIF_F_HIGHDMA
244
                | NETIF_F_LLTX
245
                | NETIF_F_NETNS_LOCAL;
246
        dev->ethtool_ops        = &loopback_ethtool_ops;
247
        dev->header_ops         = &eth_header_ops;
248
        dev->init = loopback_dev_init;
249
        dev->destructor = loopback_dev_free;
250
}
251
 
252
/* Setup and register the loopback device. */
253
static __net_init int loopback_net_init(struct net *net)
254
{
255
        struct net_device *dev;
256
        int err;
257
 
258
        err = -ENOMEM;
259
        dev = alloc_netdev(0, "lo", loopback_setup);
260
        if (!dev)
261
                goto out;
262
 
263
        dev->nd_net = net;
264
        err = register_netdev(dev);
265
        if (err)
266
                goto out_free_netdev;
267
 
268
        net->loopback_dev = dev;
269
        return 0;
270
 
271
 
272
out_free_netdev:
273
        free_netdev(dev);
274
out:
275
        if (net == &init_net)
276
                panic("loopback: Failed to register netdevice: %d\n", err);
277
        return err;
278
}
279
 
280
static __net_exit void loopback_net_exit(struct net *net)
281
{
282
        struct net_device *dev = net->loopback_dev;
283
 
284
        unregister_netdev(dev);
285
}
286
 
287
static struct pernet_operations __net_initdata loopback_net_ops = {
288
       .init = loopback_net_init,
289
       .exit = loopback_net_exit,
290
};
291
 
292
static int __init loopback_init(void)
293
{
294
        return register_pernet_device(&loopback_net_ops);
295
}
296
 
297
/* Loopback is special. It should be initialized before any other network
298
 * device and network subsystem.
299
 */
300
fs_initcall(loopback_init);

powered by: WebSVN 2.1.0

© copyright 1999-2025 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.