Skip to content

Commit

Permalink
virtio: use callback on empty in virtio_net
Browse files Browse the repository at this point in the history
virtio_net uses a timer to free old transmitted packets, rather than
leaving callbacks enabled all the time.  If the host promises to
always notify us when the transmit ring is empty, we can free packets
at that point and avoid the timer.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
  • Loading branch information
Rusty Russell authored and Jeff Garzik committed Jun 10, 2008
1 parent 14c998f commit 363f151
Showing 1 changed file with 17 additions and 5 deletions.
22 changes: 17 additions & 5 deletions drivers/net/virtio_net.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,15 @@ struct virtnet_info
/* The skb we couldn't send because buffers were full. */
struct sk_buff *last_xmit_skb;

/* If we need to free in a timer, this is it. */
struct timer_list xmit_free_timer;

/* Number of input buffers, and max we've ever had. */
unsigned int num, max;

/* For cleaning up after transmission. */
struct tasklet_struct tasklet;
bool free_in_tasklet;

/* Receive & send queues. */
struct sk_buff_head recv;
Expand All @@ -74,7 +76,7 @@ static void skb_xmit_done(struct virtqueue *svq)
/* Suppress further interrupts. */
svq->vq_ops->disable_cb(svq);

/* We were waiting for more output buffers. */
/* We were probably waiting for more output buffers. */
netif_wake_queue(vi->dev);

/* Make sure we re-xmit last_xmit_skb: if there are no more packets
Expand Down Expand Up @@ -242,6 +244,8 @@ static void free_old_xmit_skbs(struct virtnet_info *vi)
}
}

/* If the virtio transport doesn't always notify us when all in-flight packets
* are consumed, we fall back to using this function on a timer to free them. */
static void xmit_free(unsigned long data)
{
struct virtnet_info *vi = (void *)data;
Expand Down Expand Up @@ -302,7 +306,7 @@ static int xmit_skb(struct virtnet_info *vi, struct sk_buff *skb)
num = skb_to_sgvec(skb, sg+1, 0, skb->len) + 1;

err = vi->svq->vq_ops->add_buf(vi->svq, sg, num, 0, skb);
if (!err)
if (!err && !vi->free_in_tasklet)
mod_timer(&vi->xmit_free_timer, jiffies + (HZ/10));

return err;
Expand All @@ -317,6 +321,8 @@ static void xmit_tasklet(unsigned long data)
vi->svq->vq_ops->kick(vi->svq);
vi->last_xmit_skb = NULL;
}
if (vi->free_in_tasklet)
free_old_xmit_skbs(vi);
netif_tx_unlock_bh(vi->dev);
}

Expand Down Expand Up @@ -457,6 +463,10 @@ static int virtnet_probe(struct virtio_device *vdev)
vi->vdev = vdev;
vdev->priv = vi;

/* If they give us a callback when all buffers are done, we don't need
* the timer. */
vi->free_in_tasklet = virtio_has_feature(vdev,VIRTIO_F_NOTIFY_ON_EMPTY);

/* We expect two virtqueues, receive then send. */
vi->rvq = vdev->config->find_vq(vdev, 0, skb_recv_done);
if (IS_ERR(vi->rvq)) {
Expand All @@ -476,7 +486,8 @@ static int virtnet_probe(struct virtio_device *vdev)

tasklet_init(&vi->tasklet, xmit_tasklet, (unsigned long)vi);

setup_timer(&vi->xmit_free_timer, xmit_free, (unsigned long)vi);
if (!vi->free_in_tasklet)
setup_timer(&vi->xmit_free_timer, xmit_free, (unsigned long)vi);

err = register_netdev(dev);
if (err) {
Expand Down Expand Up @@ -515,7 +526,8 @@ static void virtnet_remove(struct virtio_device *vdev)
/* Stop all the virtqueues. */
vdev->config->reset(vdev);

del_timer_sync(&vi->xmit_free_timer);
if (!vi->free_in_tasklet)
del_timer_sync(&vi->xmit_free_timer);

/* Free our skbs in send and recv queues, if any. */
while ((skb = __skb_dequeue(&vi->recv)) != NULL) {
Expand All @@ -540,7 +552,7 @@ static struct virtio_device_id id_table[] = {
static unsigned int features[] = {
VIRTIO_NET_F_CSUM, VIRTIO_NET_F_GSO, VIRTIO_NET_F_MAC,
VIRTIO_NET_F_HOST_TSO4, VIRTIO_NET_F_HOST_UFO, VIRTIO_NET_F_HOST_TSO6,
VIRTIO_NET_F_HOST_ECN,
VIRTIO_NET_F_HOST_ECN, VIRTIO_F_NOTIFY_ON_EMPTY,
};

static struct virtio_driver virtio_net = {
Expand Down

0 comments on commit 363f151

Please sign in to comment.