virtio: use callback on empty in virtio_net
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>
This commit is contained in:
parent
14c998f034
commit
363f15149c
|
@ -44,6 +44,7 @@ struct virtnet_info
|
||||||
/* The skb we couldn't send because buffers were full. */
|
/* The skb we couldn't send because buffers were full. */
|
||||||
struct sk_buff *last_xmit_skb;
|
struct sk_buff *last_xmit_skb;
|
||||||
|
|
||||||
|
/* If we need to free in a timer, this is it. */
|
||||||
struct timer_list xmit_free_timer;
|
struct timer_list xmit_free_timer;
|
||||||
|
|
||||||
/* Number of input buffers, and max we've ever had. */
|
/* Number of input buffers, and max we've ever had. */
|
||||||
|
@ -51,6 +52,7 @@ struct virtnet_info
|
||||||
|
|
||||||
/* For cleaning up after transmission. */
|
/* For cleaning up after transmission. */
|
||||||
struct tasklet_struct tasklet;
|
struct tasklet_struct tasklet;
|
||||||
|
bool free_in_tasklet;
|
||||||
|
|
||||||
/* Receive & send queues. */
|
/* Receive & send queues. */
|
||||||
struct sk_buff_head recv;
|
struct sk_buff_head recv;
|
||||||
|
@ -74,7 +76,7 @@ static void skb_xmit_done(struct virtqueue *svq)
|
||||||
/* Suppress further interrupts. */
|
/* Suppress further interrupts. */
|
||||||
svq->vq_ops->disable_cb(svq);
|
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);
|
netif_wake_queue(vi->dev);
|
||||||
|
|
||||||
/* Make sure we re-xmit last_xmit_skb: if there are no more packets
|
/* Make sure we re-xmit last_xmit_skb: if there are no more packets
|
||||||
|
@ -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)
|
static void xmit_free(unsigned long data)
|
||||||
{
|
{
|
||||||
struct virtnet_info *vi = (void *)data;
|
struct virtnet_info *vi = (void *)data;
|
||||||
|
@ -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;
|
num = skb_to_sgvec(skb, sg+1, 0, skb->len) + 1;
|
||||||
|
|
||||||
err = vi->svq->vq_ops->add_buf(vi->svq, sg, num, 0, skb);
|
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));
|
mod_timer(&vi->xmit_free_timer, jiffies + (HZ/10));
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
|
@ -317,6 +321,8 @@ static void xmit_tasklet(unsigned long data)
|
||||||
vi->svq->vq_ops->kick(vi->svq);
|
vi->svq->vq_ops->kick(vi->svq);
|
||||||
vi->last_xmit_skb = NULL;
|
vi->last_xmit_skb = NULL;
|
||||||
}
|
}
|
||||||
|
if (vi->free_in_tasklet)
|
||||||
|
free_old_xmit_skbs(vi);
|
||||||
netif_tx_unlock_bh(vi->dev);
|
netif_tx_unlock_bh(vi->dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -457,6 +463,10 @@ static int virtnet_probe(struct virtio_device *vdev)
|
||||||
vi->vdev = vdev;
|
vi->vdev = vdev;
|
||||||
vdev->priv = vi;
|
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. */
|
/* We expect two virtqueues, receive then send. */
|
||||||
vi->rvq = vdev->config->find_vq(vdev, 0, skb_recv_done);
|
vi->rvq = vdev->config->find_vq(vdev, 0, skb_recv_done);
|
||||||
if (IS_ERR(vi->rvq)) {
|
if (IS_ERR(vi->rvq)) {
|
||||||
|
@ -476,7 +486,8 @@ static int virtnet_probe(struct virtio_device *vdev)
|
||||||
|
|
||||||
tasklet_init(&vi->tasklet, xmit_tasklet, (unsigned long)vi);
|
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);
|
err = register_netdev(dev);
|
||||||
if (err) {
|
if (err) {
|
||||||
|
@ -515,7 +526,8 @@ static void virtnet_remove(struct virtio_device *vdev)
|
||||||
/* Stop all the virtqueues. */
|
/* Stop all the virtqueues. */
|
||||||
vdev->config->reset(vdev);
|
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. */
|
/* Free our skbs in send and recv queues, if any. */
|
||||||
while ((skb = __skb_dequeue(&vi->recv)) != NULL) {
|
while ((skb = __skb_dequeue(&vi->recv)) != NULL) {
|
||||||
|
@ -540,7 +552,7 @@ static struct virtio_device_id id_table[] = {
|
||||||
static unsigned int features[] = {
|
static unsigned int features[] = {
|
||||||
VIRTIO_NET_F_CSUM, VIRTIO_NET_F_GSO, VIRTIO_NET_F_MAC,
|
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_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 = {
|
static struct virtio_driver virtio_net = {
|
||||||
|
|
Loading…
Reference in New Issue