X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fnet%2Floopback.c;h=2b739fd584f1ff9042d9034548a5740f84c6aed0;hb=97bf2856c6014879bd04983a3e9dfcdac1e7fe85;hp=1885bfe3c9592fd10310f8b98b1d78492804a797;hpb=5273a3df6485dc2ad6aa7ddd441b9a21970f003b;p=linux-2.6.git diff --git a/drivers/net/loopback.c b/drivers/net/loopback.c index 1885bfe3c..2b739fd58 100644 --- a/drivers/net/loopback.c +++ b/drivers/net/loopback.c @@ -7,7 +7,7 @@ * * Version: @(#)loopback.c 1.0.4b 08/16/93 * - * Authors: Ross Biro, + * Authors: Ross Biro * Fred N. van Kempen, * Donald Becker, * @@ -49,13 +49,20 @@ #include #include #include +#include #include #include #include /* For the statistics structure. */ #include /* For ARPHRD_ETHER */ #include #include +#include +struct pcpu_lstats { + unsigned long packets; + unsigned long bytes; +}; +static DEFINE_PER_CPU(struct pcpu_lstats, pcpu_lstats); #define LOOPBACK_OVERHEAD (128 + MAX_HEADER + 16 + 16) @@ -65,12 +72,13 @@ * of largesending device modulo TCP checksum, which is ignored for loopback. */ +#ifdef LOOPBACK_TSO static void emulate_large_send_offload(struct sk_buff *skb) { struct iphdr *iph = skb->nh.iph; struct tcphdr *th = (struct tcphdr*)(skb->nh.raw + (iph->ihl * 4)); unsigned int doffset = (iph->ihl + th->doff) * 4; - unsigned int mtu = skb_shinfo(skb)->tso_size + doffset; + unsigned int mtu = skb_shinfo(skb)->gso_size + doffset; unsigned int offset = 0; u32 seq = ntohl(th->seq); u16 id = ntohs(iph->id); @@ -116,6 +124,7 @@ static void emulate_large_send_offload(struct sk_buff *skb) dev_kfree_skb(skb); } +#endif /* LOOPBACK_TSO */ /* * The higher levels take care of making this non-reentrant (it's @@ -123,44 +132,82 @@ static void emulate_large_send_offload(struct sk_buff *skb) */ static int loopback_xmit(struct sk_buff *skb, struct net_device *dev) { - struct net_device_stats *stats = dev->priv; + struct pcpu_lstats *lb_stats; skb_orphan(skb); - skb->protocol=eth_type_trans(skb,dev); - skb->dev=dev; + skb->protocol = eth_type_trans(skb,dev); + skb->dev = dev; #ifndef LOOPBACK_MUST_CHECKSUM skb->ip_summed = CHECKSUM_UNNECESSARY; #endif - if (skb_shinfo(skb)->tso_size) { +#ifdef LOOPBACK_TSO + if (skb_is_gso(skb)) { BUG_ON(skb->protocol != htons(ETH_P_IP)); BUG_ON(skb->nh.iph->protocol != IPPROTO_TCP); emulate_large_send_offload(skb); return 0; } - +#endif dev->last_rx = jiffies; - if (likely(stats)) { - stats->rx_bytes+=skb->len; - stats->tx_bytes+=skb->len; - stats->rx_packets++; - stats->tx_packets++; - } + + /* it's OK to use __get_cpu_var() because BHs are off */ + lb_stats = &__get_cpu_var(pcpu_lstats); + lb_stats->bytes += skb->len; + lb_stats->packets++; netif_rx(skb); - return(0); + return 0; } +static struct net_device_stats loopback_stats; + static struct net_device_stats *get_stats(struct net_device *dev) { - return (struct net_device_stats *)dev->priv; + struct net_device_stats *stats = &loopback_stats; + unsigned long bytes = 0; + unsigned long packets = 0; + int i; + + for_each_possible_cpu(i) { + const struct pcpu_lstats *lb_stats; + + lb_stats = &per_cpu(pcpu_lstats, i); + bytes += lb_stats->bytes; + packets += lb_stats->packets; + } + stats->rx_packets = packets; + stats->tx_packets = packets; + stats->rx_bytes = bytes; + stats->tx_bytes = bytes; + return stats; } +static u32 always_on(struct net_device *dev) +{ + return 1; +} + +static const struct ethtool_ops loopback_ethtool_ops = { + .get_link = always_on, + .get_tso = ethtool_op_get_tso, + .set_tso = ethtool_op_set_tso, + .get_tx_csum = always_on, + .get_sg = always_on, + .get_rx_csum = always_on, +}; + +/* + * The loopback device is special. There is only one instance and + * it is statically allocated. Don't do this for other devices. + */ struct net_device loopback_dev = { .name = "lo", + .get_stats = &get_stats, + .priv = &loopback_stats, .mtu = (16 * 1024) + 20 + 20 + 12, .hard_start_xmit = loopback_xmit, .hard_header = eth_header, @@ -172,24 +219,21 @@ struct net_device loopback_dev = { .type = ARPHRD_LOOPBACK, /* 0x0001*/ .rebuild_header = eth_rebuild_header, .flags = IFF_LOOPBACK, - .features = NETIF_F_SG|NETIF_F_FRAGLIST - |NETIF_F_NO_CSUM|NETIF_F_HIGHDMA, + .features = NETIF_F_SG | NETIF_F_FRAGLIST +#ifdef LOOPBACK_TSO + | NETIF_F_TSO +#endif + | NETIF_F_NO_CSUM | NETIF_F_HIGHDMA + | NETIF_F_LLTX, + .ethtool_ops = &loopback_ethtool_ops, }; -/* Setup and register the of the LOOPBACK device. */ -int __init loopback_init(void) +/* Setup and register the loopback device. */ +static int __init loopback_init(void) { - struct net_device_stats *stats; - - /* Can survive without statistics */ - stats = kmalloc(sizeof(struct net_device_stats), GFP_KERNEL); - if (stats) { - memset(stats, 0, sizeof(struct net_device_stats)); - loopback_dev.priv = stats; - loopback_dev.get_stats = &get_stats; - } - return register_netdev(&loopback_dev); }; +module_init(loopback_init); + EXPORT_SYMBOL(loopback_dev);