WARNING - OLD ARCHIVES

This is an archived copy of the Xen.org mailing list, which we have preserved to ensure that existing links to archives are not broken. The live archive, which contains the latest emails, can be found at http://lists.xen.org/
   
 
 
Xen 
 
Home Products Support Community News
 
   
 

xen-devel

[Xen-devel] [PATCH RFC 2/3] Virtio draft III: example net driver

Net driver using virtio

TODO:
        1) GSO.
        2) Checksum options.
        3) Big packets.

Signed-off-by: Rusty Russell <rusty@xxxxxxxxxxxxxxx>
---
 drivers/net/Makefile       |    2 
 drivers/net/virtio_net.c   |  277 ++++++++++++++++++++++++++++++++++++++++++++
 include/linux/virtio_net.h |   12 +
 3 files changed, 290 insertions(+), 1 deletion(-)

===================================================================
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -37,7 +37,7 @@ obj-$(CONFIG_CASSINI) += cassini.o
 
 obj-$(CONFIG_MACE) += mace.o
 obj-$(CONFIG_BMAC) += bmac.o
-
+obj-y += virtio_net.o
 obj-$(CONFIG_DGRS) += dgrs.o
 obj-$(CONFIG_VORTEX) += 3c59x.o
 obj-$(CONFIG_TYPHOON) += typhoon.o
===================================================================
--- /dev/null
+++ b/drivers/net/virtio_net.c
@@ -0,0 +1,277 @@
+/* A simple network driver using virtio.
+ *
+ * Copyright 2007 Rusty Russell <rusty@xxxxxxxxxxxxxxx> IBM Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+//#define DEBUG
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/module.h>
+#include <linux/virtio.h>
+#include <linux/scatterlist.h>
+
+/* FIXME: Make dynamic */
+#define MAX_PACKET_LEN (ETH_HLEN+ETH_DATA_LEN)
+
+#define SKB_ID(skb) (*(unsigned long *)(skb)->cb)
+
+struct virtnet_info
+{
+       struct virtio_device *vdev;
+       struct net_device *ndev;
+
+       /* Number of input buffers, and max we've ever had. */
+       unsigned int num, max;
+
+       /* Receive & send queues. */
+       struct sk_buff_head recv;
+       struct sk_buff_head send;
+};
+
+static bool skb_xmit_done(struct virtio_device *vdev)
+{
+       struct virtnet_info *vi = vdev->priv;
+
+       /* In case we were waiting for output buffers. */
+       netif_wake_queue(vi->ndev);
+       return true;
+}
+
+static void receive_skb(struct net_device *dev, struct sk_buff *skb,
+                       unsigned len)
+{
+       if (unlikely(len < ETH_HLEN)) {
+               pr_debug("%s: short packet %i\n", dev->name, len);
+               dev->stats.rx_length_errors++;
+               dev_kfree_skb(skb);
+               return;
+       }
+       BUG_ON(len > MAX_PACKET_LEN);
+
+       skb_trim(skb, len);
+       skb->protocol = eth_type_trans(skb, dev);
+       pr_debug("Receiving skb proto 0x%04x len %i type %i\n",
+                ntohs(skb->protocol), skb->len, skb->pkt_type);
+       dev->stats.rx_bytes += skb->len;
+       dev->stats.rx_packets++;
+       netif_rx(skb);
+}
+
+static void try_fill_recv(struct virtnet_info *vi)
+{
+       struct sk_buff *skb;
+       struct scatterlist sg[MAX_SKB_FRAGS];
+       unsigned long sgnum, id;
+
+       for (;;) {
+               skb = netdev_alloc_skb(vi->ndev, MAX_PACKET_LEN);
+               if (unlikely(!skb))
+                       break;
+
+               skb_put(skb, MAX_PACKET_LEN);
+               sgnum = skb_to_sgvec(skb, sg, 0, skb->len);
+               skb_queue_head(&vi->recv, skb);
+
+               id = vi->vdev->ops->add_inbuf(vi->vdev, sg, sgnum, skb);
+               if (IS_ERR_VALUE(id)) {
+                       skb_unlink(skb, &vi->recv);
+                       kfree_skb(skb);
+                       break;
+               }
+               vi->num++;
+       }
+       if (unlikely(vi->num > vi->max))
+               vi->max = vi->num;
+       vi->vdev->ops->sync(vi->vdev, VIRTIO_IN);
+}
+
+static bool skb_recv_done(struct virtio_device *vdev)
+{
+       struct virtnet_info *vi = vdev->priv;
+
+       netif_rx_schedule(vi->ndev);
+       /* Suppress further interrupts. */
+       return false;
+}
+
+static int virtnet_poll(struct net_device *dev, int *budget)
+{
+       struct virtnet_info *vi = netdev_priv(dev);
+       struct sk_buff *skb = NULL;
+       unsigned int len, received = 0;
+
+again:
+       while (received < dev->quota &&
+              (skb = vi->vdev->ops->get_inbuf(vi->vdev, &len)) != NULL) {
+               __skb_unlink(skb, &vi->recv);
+               receive_skb(vi->ndev, skb, len);
+               vi->num--;
+               received++;
+       }
+
+        dev->quota -= received;
+        *budget -= received;
+
+       /* FIXME: If we oom and completely run out of inbufs, we need
+        * to start a timer trying to fill more. */
+       if (vi->num < vi->max / 2)
+               try_fill_recv(vi);
+
+       /* Still more work to do? */
+       if (skb)
+               return 1; /* not done */
+
+       netif_rx_complete(dev);
+       if (unlikely(!vi->vdev->ops->restart_in(vi->vdev))
+           && netif_rx_reschedule(dev, received))
+               goto again;
+
+       return 0;
+}
+
+static void free_old_xmit_skbs(struct virtnet_info *vi)
+{
+       struct sk_buff *skb;
+       unsigned int len;
+
+       while ((skb = vi->vdev->ops->get_outbuf(vi->vdev, &len)) != NULL) {
+               pr_debug("Sent skb %p\n", skb);
+               __skb_unlink(skb, &vi->send);
+               vi->ndev->stats.tx_bytes += len;
+               vi->ndev->stats.tx_packets++;
+               kfree_skb(skb);
+       }
+}
+
+static int start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+       struct virtnet_info *vi = netdev_priv(dev);
+       unsigned long num, id;
+       struct scatterlist sg[MAX_SKB_FRAGS];
+       const unsigned char *dest = ((struct ethhdr *)skb->data)->h_dest;
+
+       pr_debug("%s: xmit %p %02x:%02x:%02x:%02x:%02x:%02x\n",
+                dev->name, skb,
+                dest[0], dest[1], dest[2], dest[3], dest[4], dest[5]);
+
+       free_old_xmit_skbs(vi);
+
+       num = skb_to_sgvec(skb, sg, 0, skb->len);
+       __skb_queue_head(&vi->send, skb);
+       id = vi->vdev->ops->add_outbuf(vi->vdev, sg, num, skb);
+       if (IS_ERR_VALUE(id)) {
+               pr_debug("%s: virtio not prepared to send\n", dev->name);
+               skb_unlink(skb, &vi->send);
+               netif_stop_queue(dev);
+               return NETDEV_TX_BUSY;
+       }
+       SKB_ID(skb) = id;
+       vi->vdev->ops->sync(vi->vdev, VIRTIO_OUT);
+
+       return 0;
+}
+
+static int virtnet_open(struct net_device *dev)
+{
+       struct virtnet_info *vi = netdev_priv(dev);
+
+       try_fill_recv(vi);
+
+       /* If we didn't even get one input buffer, we're useless. */
+       if (vi->num == 0)
+               return -ENOMEM;
+
+       return 0;
+}
+
+static int virtnet_close(struct net_device *dev)
+{
+       struct virtnet_info *vi = netdev_priv(dev);
+       struct sk_buff *skb;
+
+       /* networking core has neutered skb_xmit_done/skb_recv_done, so don't
+        * worry about races vs. get_buf(). */
+       while ((skb = __skb_dequeue(&vi->recv)) != NULL) {
+               vi->vdev->ops->detach_inbuf(vi->vdev, SKB_ID(skb));
+               kfree_skb(skb);
+               vi->num--;
+       }
+       while ((skb = __skb_dequeue(&vi->send)) != NULL) {
+               vi->vdev->ops->detach_outbuf(vi->vdev, SKB_ID(skb));
+               kfree_skb(skb);
+       }
+       BUG_ON(vi->num != 0);
+       return 0;
+}
+
+static struct virtio_driver_ops virtnet_ops = {
+       .in = skb_recv_done,
+       .out = skb_xmit_done,
+};
+
+struct net_device *virtnet_probe(struct virtio_device *vdev,
+                                const u8 mac[ETH_ALEN])
+{
+       int err;
+       struct net_device *dev;
+       struct virtnet_info *vi;
+
+       dev = alloc_etherdev(sizeof(struct virtnet_info));
+       if (!dev)
+               return ERR_PTR(-ENOMEM);
+
+       SET_MODULE_OWNER(dev);
+
+       ether_setup(dev);
+       memcpy(dev->dev_addr, mac, ETH_ALEN);
+       dev->open = virtnet_open;
+       dev->stop = virtnet_close;
+       dev->poll = virtnet_poll;
+       dev->hard_start_xmit = start_xmit;
+       dev->weight = 16;
+       SET_NETDEV_DEV(dev, vdev->dev);
+
+       vi = netdev_priv(dev);
+       vi->vdev = vdev;
+       vi->ndev = dev;
+       vdev->priv = vi;
+       vdev->driver_ops = &virtnet_ops;
+       skb_queue_head_init(&vi->recv);
+       skb_queue_head_init(&vi->send);
+
+       err = register_netdev(dev);
+       if (err) {
+               pr_debug("virtio_net: registering device failed\n");
+               goto free;
+       }
+       pr_debug("virtnet: registered device %s\n", dev->name);
+       return dev;
+
+free:
+       free_netdev(dev);
+       return ERR_PTR(err);
+}
+EXPORT_SYMBOL_GPL(virtnet_probe);
+
+void virtnet_remove(struct net_device *dev)
+{
+       unregister_netdev(dev);
+       free_netdev(dev);
+}
+EXPORT_SYMBOL_GPL(virtnet_remove);
+
+MODULE_DESCRIPTION("Virtio network driver");
+MODULE_LICENSE("GPL");
===================================================================
--- /dev/null
+++ b/include/linux/virtio_net.h
@@ -0,0 +1,12 @@
+#ifndef _LINUX_VIRTIO_NET_H
+#define _LINUX_VIRTIO_NET_H
+#include <linux/types.h>
+#include <linux/etherdevice.h>
+struct net_device;
+struct virtio_device;
+
+struct net_device *virtnet_probe(struct virtio_device *vdev,
+                                const u8 mac[ETH_ALEN]);
+void virtnet_remove(struct net_device *dev);
+
+#endif /* _LINUX_VIRTIO_NET_H */



_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel

<Prev in Thread] Current Thread [Next in Thread>