diff --git a/drivers/net/tun.c b/drivers/net/tun.c index a2c6caa..076f794 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -70,6 +70,7 @@ static int debug; #endif +#define NETDEV_LTT 4 /* the low threshold to open up the tx path */ /* Network device part of the driver */ static LIST_HEAD(tun_dev_list); @@ -86,9 +87,53 @@ static int tun_net_open(struct net_device *dev) static int tun_net_close(struct net_device *dev) { netif_stop_queue(dev); + //skb_queue_purge(&dev->blist); return 0; } +/* Batch Net device start xmit + * combine with non-batching version + * */ +static int tun_net_bxmit(struct sk_buff_head *skbs, struct net_device *dev) +{ + struct sk_buff *skb; + struct tun_struct *tun = netdev_priv(dev); + u32 qlen = skb_queue_len(&tun->readq); + + /* Drop packet if interface is not attached */ + if (!tun->attached) { + tun->stats.tx_dropped+=skb_queue_len(&dev->blist); + skb_queue_purge(&dev->blist); + return NETDEV_TX_OK; + } + + while (skb_queue_len(&dev->blist)) { + skb = __skb_dequeue(skbs); + if (!skb) + break; + skb_queue_tail(&tun->readq, skb); + } + + qlen = skb_queue_len(&tun->readq); + if (qlen >= dev->tx_queue_len) { + netif_stop_queue(dev); + tun->stats.tx_fifo_errors++; + dev->xmit_win = 1; + } else { + dev->xmit_win = dev->tx_queue_len - qlen; + } + + /* Queue packet */ + dev->trans_start = jiffies; + + /* Notify and wake up reader process */ + if (tun->flags & TUN_FASYNC) + kill_fasync(&tun->fasync, SIGIO, POLL_IN); + wake_up_interruptible(&tun->read_wait); + + return NETDEV_TX_OK; +} + /* Net device start xmit */ static int tun_net_xmit(struct sk_buff *skb, struct net_device *dev) { @@ -207,6 +252,7 @@ static void tun_net_init(struct net_device *dev) dev->tx_queue_len = TUN_READQ_SIZE; /* We prefer our own queue length */ break; } + dev->xmit_win = dev->tx_queue_len>>1; /* handwave, handwave */ } /* Character device part */ @@ -382,7 +428,13 @@ static ssize_t tun_chr_aio_read(struct kiocb *iocb, const struct iovec *iv, schedule(); continue; } - netif_wake_queue(tun->dev); + { + u32 t = skb_queue_len(&tun->readq); + if (netif_queue_stopped(tun->dev) && t < NETDEV_LTT) { + tun->dev->xmit_win = tun->dev->tx_queue_len; + netif_wake_queue(tun->dev); + } + } /** Decide whether to accept this packet. This code is designed to * behave identically to an Ethernet interface. Accept the packet if @@ -429,6 +481,7 @@ static void tun_setup(struct net_device *dev) struct tun_struct *tun = netdev_priv(dev); skb_queue_head_init(&tun->readq); + skb_queue_head_init(&dev->blist); init_waitqueue_head(&tun->read_wait); tun->owner = -1; @@ -436,6 +489,8 @@ static void tun_setup(struct net_device *dev) SET_MODULE_OWNER(dev); dev->open = tun_net_open; dev->hard_start_xmit = tun_net_xmit; + dev->hard_prep_xmit = NULL; + dev->hard_batch_xmit = tun_net_bxmit; dev->stop = tun_net_close; dev->get_stats = tun_net_stats; dev->ethtool_ops = &tun_ethtool_ops; @@ -458,7 +513,7 @@ static struct tun_struct *tun_get_by_name(const char *name) static int tun_set_iff(struct file *file, struct ifreq *ifr) { struct tun_struct *tun; - struct net_device *dev; + struct net_device *dev = NULL; int err; tun = tun_get_by_name(ifr->ifr_name); @@ -528,12 +583,15 @@ static int tun_set_iff(struct file *file, struct ifreq *ifr) } DBG(KERN_INFO "%s: tun_set_iff\n", tun->dev->name); + dev->features |= NETIF_F_BTX; if (ifr->ifr_flags & IFF_NO_PI) tun->flags |= TUN_NO_PI; - if (ifr->ifr_flags & IFF_ONE_QUEUE) + if (ifr->ifr_flags & IFF_ONE_QUEUE) { tun->flags |= TUN_ONE_QUEUE; + dev->features &= ~NETIF_F_BTX; + } file->private_data = tun; tun->attached = 1;