From: Jason Wang <jasowang@redhat.com> Date: Tue, 16 Nov 2010 05:01:31 -0500 Subject: [net] virtio_net: add link status handling Message-id: <20101116050130.3698.51989.stgit@dhcp-91-158.nay.redhat.com> Patchwork-id: 29451 O-Subject: [RHEL5.6 PATCH] virtio_net: add link status handling Bugzilla: 649573 RH-Acked-by: David S. Miller <davem@redhat.com> RH-Acked-by: Thomas Graf <tgraf@redhat.com> Bugzilla: 649573 Brew Build: https://brewweb.devel.redhat.com/taskinfo?taskID=2894155 Upstream: This patch backports the following two commits from upstream commit 9f4d26d0f3016cf8813977d624751b94465fa317 Author: Mark McLoughlin <markmc@redhat.com> Date: Mon Jan 19 17:09:49 2009 -0800 virtio_net: add link status handling Allow the host to inform us that the link is down by adding a VIRTIO_NET_F_STATUS which indicates that device status is available in virtio_net config. This is currently useful for simulating link down conditions (e.g. using proposed qemu 'set_link' monitor command) but would also be needed if we were to support device assignment via virtio. Signed-off-by: Mark McLoughlin <markmc@redhat.com> Signed-off-by: Rusty Russell <rusty@rustcorp.com.au> (added future masking) Signed-off-by: David S. Miller <davem@davemloft.net> And commit 167c25e4c5501f8b7e37f949d23652975c5a769c Author: Jason Wang <jasowang@redhat.com> Date: Wed Nov 10 14:45:41 2010 +0000 virtio-net: init link state correctly For device that supports VIRTIO_NET_F_STATUS, there's no need to assume the link is up and we need to call nerif_carrier_off() before querying device status, otherwise we may get wrong operstate after diver was loaded because the link watch event was not fired as expected. For device that does not support VIRITO_NET_F_STATUS, we could not get its status through virtnet_update_status() and what we can only do is always assuming the link is up. Acked-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Jason Wang <jasowang@redhat.com> Signed-off-by: Rusty Russell <rusty@rustcorp.com.au> Signed-off-by: David S. Miller <davem@davemloft.ne Test status: Test with my local machine. Signed-off-by: Jason Wang <jasowang@redhat.com> diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 89c9835..65cb9fe 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -43,6 +43,7 @@ struct virtnet_info struct virtio_device *vdev; struct virtqueue *rvq, *svq; struct net_device *dev; + unsigned int status; /* The skb we couldn't send because buffers were full. */ struct sk_buff *last_xmit_skb; @@ -717,6 +718,7 @@ static struct ethtool_ops virtnet_ethtool_ops = { .get_sg = ethtool_op_get_sg, .set_tso = ethtool_op_set_tso, .get_tso = ethtool_op_get_tso, + .get_link = ethtool_op_get_link, }; #define MIN_MTU 68 @@ -734,6 +736,41 @@ static void virtnet_mclist(struct net_device *dev) { } +static void virtnet_update_status(struct virtnet_info *vi) +{ + u16 v; + + if (!virtio_has_feature(vi->vdev, VIRTIO_NET_F_STATUS)) + return; + + vi->vdev->config->get(vi->vdev, + offsetof(struct virtio_net_config, status), + &v, sizeof(v)); + + /* Ignore unknown (future) status bits */ + v &= VIRTIO_NET_S_LINK_UP; + + if (vi->status == v) + return; + + vi->status = v; + + if (vi->status & VIRTIO_NET_S_LINK_UP) { + netif_carrier_on(vi->dev); + netif_wake_queue(vi->dev); + } else { + netif_carrier_off(vi->dev); + netif_stop_queue(vi->dev); + } +} + +static void virtnet_config_changed(struct virtio_device *vdev) +{ + struct virtnet_info *vi = vdev->priv; + + virtnet_update_status(vi); +} + static int virtnet_probe(struct virtio_device *vdev) { int err; @@ -843,6 +880,16 @@ static int virtnet_probe(struct virtio_device *vdev) goto unregister; } + /* Assume link up if device can't report link status, + otherwise get link status from config. */ + if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_STATUS)) { + netif_carrier_off(dev); + virtnet_update_status(vi); + } else { + vi->status = VIRTIO_NET_S_LINK_UP; + netif_carrier_on(dev); + } + pr_debug("virtnet: registered device %s\n", dev->name); return 0; @@ -913,7 +960,7 @@ static unsigned int features[] = { VIRTIO_NET_F_HOST_TSO4, VIRTIO_NET_F_HOST_UFO, VIRTIO_NET_F_HOST_TSO6, VIRTIO_NET_F_HOST_ECN, VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_TSO6, VIRTIO_NET_F_GUEST_ECN, /* We don't yet handle UFO input. */ - VIRTIO_NET_F_MRG_RXBUF, + VIRTIO_NET_F_MRG_RXBUF, VIRTIO_NET_F_STATUS, VIRTIO_F_NOTIFY_ON_EMPTY, }; @@ -925,6 +972,7 @@ static struct virtio_driver virtio_net = { .id_table = id_table, .probe = virtnet_probe, .remove = __devexit_p(virtnet_remove), + .config_changed = virtnet_config_changed, }; static int __init init(void) diff --git a/include/linux/virtio_net.h b/include/linux/virtio_net.h index 5cdd0aa..d56f84a 100644 --- a/include/linux/virtio_net.h +++ b/include/linux/virtio_net.h @@ -21,11 +21,15 @@ #define VIRTIO_NET_F_HOST_ECN 13 /* Host can handle TSO[6] w/ ECN in. */ #define VIRTIO_NET_F_HOST_UFO 14 /* Host can handle UFO in. */ #define VIRTIO_NET_F_MRG_RXBUF 15 /* Host can merge receive buffers. */ +#define VIRTIO_NET_F_STATUS 16 /* virtio_net_config.status available */ +#define VIRTIO_NET_S_LINK_UP 1 /* Link is up */ struct virtio_net_config { /* The config defining mac address (if VIRTIO_NET_F_MAC) */ __u8 mac[6]; + /* See VIRTIO_NET_F_STATUS and VIRTIO_NET_S_* above */ + __u16 status; } __attribute__((packed)); /* This is the first element of the scatter-gather list. If you don't