*
* Version: @(#)loopback.c 1.0.4b 08/16/93
*
- * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Authors: Ross Biro
* Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
* Donald Becker, <becker@scyld.com>
*
#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>
+static DEFINE_PER_CPU(struct net_device_stats, loopback_stats);
#define LOOPBACK_OVERHEAD (128 + MAX_HEADER + 16 + 16)
* 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;
dev_kfree_skb(skb);
}
+#endif /* LOOPBACK_TSO */
/*
* The higher levels take care of making this non-reentrant (it's
*/
static int loopback_xmit(struct sk_buff *skb, struct net_device *dev)
{
- struct net_device_stats *stats = dev->priv;
+ struct net_device_stats *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
+#ifdef LOOPBACK_TSO
if (skb_shinfo(skb)->tso_size) {
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++;
- }
+
+ lb_stats = &per_cpu(loopback_stats, get_cpu());
+ lb_stats->rx_bytes += skb->len;
+ lb_stats->tx_bytes = lb_stats->rx_bytes;
+ lb_stats->rx_packets++;
+ lb_stats->tx_packets = lb_stats->rx_packets;
+ put_cpu();
netif_rx(skb);
static struct net_device_stats *get_stats(struct net_device *dev)
{
- return (struct net_device_stats *)dev->priv;
+ struct net_device_stats *stats = dev->priv;
+ int i;
+
+ if (!stats) {
+ return NULL;
+ }
+
+ memset(stats, 0, sizeof(struct net_device_stats));
+
+ for_each_possible_cpu(i) {
+ struct net_device_stats *lb_stats;
+
+ lb_stats = &per_cpu(loopback_stats, i);
+ stats->rx_bytes += lb_stats->rx_bytes;
+ stats->tx_bytes += lb_stats->tx_bytes;
+ stats->rx_packets += lb_stats->rx_packets;
+ stats->tx_packets += lb_stats->tx_packets;
+ }
+
+ return stats;
+}
+
+static u32 loopback_get_link(struct net_device *dev)
+{
+ return 1;
}
+static struct ethtool_ops loopback_ethtool_ops = {
+ .get_link = loopback_get_link,
+ .get_tso = ethtool_op_get_tso,
+ .set_tso = ethtool_op_set_tso,
+};
+
struct net_device loopback_dev = {
.name = "lo",
.mtu = (16 * 1024) + 20 + 20 + 12,
.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. */
+/* Setup and register the loopback device. */
int __init loopback_init(void)
{
struct net_device_stats *stats;