Add network code (tested, 600Mbps for HVM guest) and block code
(untested).
It uses the kvm_pci_bus and virtio backend mechanisems, making the
driver
minimal.

Signed-off-by: Dor Laor <[EMAIL PROTECTED]>
---
 drivers/kvm/Kconfig           |   12 +++
 drivers/kvm/Makefile          |    5 +
 drivers/kvm/kvm_blk_backend.c |  162 +++++++++++++++++++++++++++++++++
 drivers/kvm/kvm_net_backend.c |  197
+++++++++++++++++++++++++++++++++++++++++
 include/linux/kvm_para.h      |    1 +
 5 files changed, 377 insertions(+), 0 deletions(-)
 create mode 100644 drivers/kvm/kvm_blk_backend.c
 create mode 100644 drivers/kvm/kvm_net_backend.c

diff --git a/drivers/kvm/Kconfig b/drivers/kvm/Kconfig
index a4de4de..67e7868 100644
--- a/drivers/kvm/Kconfig
+++ b/drivers/kvm/Kconfig
@@ -53,4 +53,16 @@ config VIRTIO_BE
        ---help---
        Paravirtual virtio backend greatly improves performance.
 
+config KVM_NET
+       tristate "KVM paravirtual network driver"
+       depends on KVM_PV && VIRTIO_BE
+       ---help---
+       Paravirtual network greatly improves performance.
+
+config KVM_BLK
+       tristate "KVM paravirtual block driver"
+       depends on KVM_PV && VIRTIO_BE
+       ---help---
+       Paravirtual block device greatly improves performance.
+
 endif # VIRTUALIZATION
diff --git a/drivers/kvm/Makefile b/drivers/kvm/Makefile
index e51dba4..cfb2fcd 100644
--- a/drivers/kvm/Makefile
+++ b/drivers/kvm/Makefile
@@ -12,3 +12,8 @@ kvm-pv-objs = kvm_pv.o kvm_pci_bus.o
 obj-$(CONFIG_KVM_PV) += kvm-pv.o
 virtio-be-objs = virtio_backend.o
 obj-$(CONFIG_VIRTIO_BE) += virtio-be.o
+kvm-net-objs = kvm_net_backend.o
+obj-$(CONFIG_KVM_NET) += kvm-net.o
+kvm-blk-objs = kvm_blk_backend.o
+obj-$(CONFIG_KVM_BLK) += kvm-blk.o
+
diff --git a/drivers/kvm/kvm_blk_backend.c
b/drivers/kvm/kvm_blk_backend.c
new file mode 100644
index 0000000..0165037
--- /dev/null
+++ b/drivers/kvm/kvm_blk_backend.c
@@ -0,0 +1,162 @@
+/*
+ * KVM virtio block device backend implementation
+ *
+ * Copyright (C) 2007, Qumranet, Inc., Dor Laor <[EMAIL PROTECTED]>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.
See
+ * the COPYING file in the top-level directory.
+ */
+
+//#define DEBUG
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/virtio.h>
+#include <linux/virtio_blk.h>
+#include <linux/genhd.h>
+#include <linux/blkdev.h>
+#include <linux/spinlock.h>
+#include <linux/kvm.h>
+#include <linux/kvm_para.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <asm/hypercall.h>
+#include <asm/io.h>
+#include "kvm_pv.h"
+#include "virtio_backend.h"
+
+static int debug = 3;
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)");
+
+#define DPRINTK(klevel, fmt, args...) \
+       if (('0' + debug) >= (int)(klevel[1])) \
+               printk(klevel "%s:%d: " fmt, \
+                       __FUNCTION__, __LINE__, ## args)
+
+static int kvm_virtblk_probe(struct virtio_device *vdev)
+{
+       struct be_virtqueue* bvq;
+       struct gendisk *disk;
+       unsigned long sectors;
+       int err, irqf;
+
+       bvq = be_new_virtqueue(vdev);
+       if (!bvq)
+               return -ENOMEM;
+
+       bvq->io_type = VIRTIO_DEVICE_INPUT | VIRTIO_DEVICE_OUTPUT;
+
+       /* Page is initially used to pass capacity. */
+       sectors = *(unsigned long *)bvq->d;
+       *(unsigned long *)bvq->d = 0;
+
+       vdev->private = disk = virtblk_probe(&bvq->vq);
+       if (IS_ERR(disk)) {
+               err = PTR_ERR(disk);
+               goto destroy;
+       }
+       set_capacity(disk, sectors);
+       blk_queue_max_hw_segments(disk->queue, NUM_DESCS-1);
+
+       //if
(virtio_devices[vdev->index].features&VIRTIO_DEVICE_F_RANDOMNESS)
+       irqf = IRQF_SAMPLE_RANDOM;
+       //else
+       //      irqf = 0;
+
+       err = request_irq(vdev->desc.irq, be_virtqueue_interrupt, irqf,
+                         disk->disk_name, bvq);
+       if (err)
+               goto unprobe;
+
+       err = hypercall(3, __NR_hypercall_register,
+                       vdev->id,
+                       virt_to_phys(bvq->d) >> PAGE_SHIFT,
+                       0);
+       if (err < 0) {
+               DPRINTK(KERN_ERR, "hypercall returned %d\n", err);
+               goto unprobe;
+       }
+
+       add_disk(disk);
+       vdev->private = disk;
+       vdev->be_queues = bvq;
+
+       return 0;
+
+unprobe:
+       virtblk_remove(disk);
+destroy:
+       be_destroy_virtqueue(bvq);
+
+       return err;
+}
+
+static int kvm_virtblk_unprobe(struct virtio_device *kdev)
+{
+       struct gendisk *disk = kdev->private;
+       struct be_virtqueue* bvq = kdev->be_queues;
+
+       if (disk)
+               virtblk_remove(disk);
+
+       if (bvq)
+               be_destroy_virtqueue(bvq);
+
+       synchronize_irq(kdev->desc.irq);
+       free_irq(kdev->desc.irq, disk);
+
+       return 0;
+}
+
+static struct virtio_bus kvmblk_probes = {
+       .vbus_probe = kvm_virtblk_probe,
+       .vbus_unprobe = kvm_virtblk_unprobe};
+
+static int __devinit kvmblk_init_one(struct pci_dev *pdev,
+                                    const struct pci_device_id *ent)
+{
+       sprintf(kvmblk_probes.name, "kvm_blk");
+       return kvmbus_init_one(pdev, ent, &kvmblk_probes,
VIRTIO_DEVICE_T_BLOCK);
+}
+
+static void __devexit kvmblk_remove_one(struct pci_dev *pdev)
+{
+       kvmbus_remove_one(pdev, &kvmblk_probes);
+}
+
+#define KVMBLK_DRIVER_NAME "paravirt_block_driver"
+#define KVMBLK_DRIVER_VERSION "1"
+#define PCI_VENDOR_ID_KVMBLK 0x5002
+#define PCI_DEVICE_ID_KVMBLK 0x1235
+
+static struct pci_device_id kvmblk_pci_tbl[] = {
+       {PCI_VENDOR_ID_KVMBLK, PCI_DEVICE_ID_KVMBLK, PCI_ANY_ID,
PCI_ANY_ID, 0, 0, 0 },
+       {0,}
+};
+MODULE_DEVICE_TABLE (pci, kvmblk_pci_tbl);
+
+
+static struct pci_driver kvmblk_pci_driver = {
+       .name           = KVMBLK_DRIVER_NAME,
+       .id_table       = kvmblk_pci_tbl,
+       .probe          = kvmblk_init_one,
+       .remove         = __devexit_p(kvmblk_remove_one),
+};
+
+int __init kvmblk_init(void)
+{
+       return pci_register_driver(&kvmblk_pci_driver);
+}
+
+static void __exit kvmblk_exit(void)
+{
+       pci_unregister_driver(&kvmblk_pci_driver);
+}
+
+module_init(kvmblk_init);
+module_exit(kvmblk_exit);
+
+MODULE_DESCRIPTION("KVM virtblk driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/kvm/kvm_net_backend.c
b/drivers/kvm/kvm_net_backend.c
new file mode 100644
index 0000000..9a6d516
--- /dev/null
+++ b/drivers/kvm/kvm_net_backend.c
@@ -0,0 +1,197 @@
+/*
+ * KVM virtio backend implementation
+ *
+ * Copyright (C) 2007, Qumranet, Inc., Dor Laor <[EMAIL PROTECTED]>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.
See
+ * the COPYING file in the top-level directory.
+ */
+
+//#define DEBUG
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/virtio.h>
+#include <linux/virtio_net.h>
+#include <linux/spinlock.h>
+#include <linux/kvm.h>
+#include <linux/kvm_para.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <asm/hypercall.h>
+#include <asm/io.h>
+#include "kvm_pv.h"
+#include "virtio_backend.h"
+
+static int debug = 3;
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)");
+
+#define DPRINTK(klevel, fmt, args...) \
+       if (('0' + debug) >= (int)(klevel[1])) \
+               printk(klevel "%s:%d: " fmt, \
+                       __FUNCTION__, __LINE__, ## args)
+
+struct be_virtqueue_pair
+{
+       struct be_virtqueue *in, *out;
+};
+
+static irqreturn_t be_virtqueue_pair_interrupt(int irq, void *_bvqp)
+{
+       struct be_virtqueue_pair *bvqp = _bvqp;
+
+       be_virtqueue_interrupt(irq, bvqp->in);
+       be_virtqueue_interrupt(irq, bvqp->out);
+
+       return IRQ_HANDLED;
+}
+
+static int kvm_virtnet_probe(struct virtio_device *vdev)
+{
+       struct net_device *dev;
+       u8 mac[ETH_ALEN];
+       int err, irqf;
+       struct be_virtqueue_pair *pair;
+
+       pair = kmalloc(sizeof(*pair), GFP_KERNEL);
+       if (!pair) {
+               err = -ENOMEM;
+               goto fail;
+       }
+
+       pair->in = be_new_virtqueue(vdev);
+       if (!pair->in) {
+               err = -ENOMEM;
+               goto free_pair;
+       }
+       pair->in->io_type = VIRTIO_DEVICE_INPUT;
+
+       pair->out = be_new_virtqueue(vdev);
+       if (!pair->out) {
+               err = -ENOMEM;
+               goto free_pair_in;
+       }
+       pair->out->io_type = VIRTIO_DEVICE_OUTPUT;
+
+       random_ether_addr(mac);
+       dev = virtnet_probe(&pair->in->vq, &pair->out->vq, &vdev->dev,
mac);
+       if (IS_ERR(dev)) {
+               err = PTR_ERR(dev);
+               goto free_pair_out;
+       }
+
+       //if
(virtio_devices[vdev->index].features&VIRTIO_DEVICE_F_RANDOMNESS)
+       irqf = IRQF_SAMPLE_RANDOM;
+       //else
+       //      irqf = 0;
+
+       err = request_irq(vdev->desc.irq,
+                         be_virtqueue_pair_interrupt, irqf, dev->name,
+                         pair);
+
+       if (err)
+               goto unprobe;
+
+       err = hypercall(3, __NR_hypercall_register,
+                       vdev->id,
+                       virt_to_phys(pair->out->d) >> PAGE_SHIFT,
+                       virt_to_phys(pair->in->d) >> PAGE_SHIFT);
+       if (err < 0) {
+               DPRINTK(KERN_ERR, "hypercall returned %d\n", err);
+               goto unprobe;
+       }
+
+       vdev->private = dev;
+       vdev->be_queues = pair;
+
+       return 0;
+
+unprobe:
+       virtnet_remove(dev);
+free_pair_out:
+       be_destroy_virtqueue(pair->out);
+free_pair_in:
+       be_destroy_virtqueue(pair->in);
+free_pair:
+       kfree(pair);
+fail:
+       return err;
+}
+
+static int kvm_virtnet_unprobe(struct virtio_device *kdev)
+{
+       struct be_virtqueue_pair *bvqp = kdev->be_queues;
+       struct net_device *dev = kdev->private;
+
+       if (dev)
+               virtnet_remove(dev);
+
+       if (bvqp) {
+               if (bvqp->in)
+                       be_destroy_virtqueue(bvqp->in);
+               if (bvqp->out)
+                       be_destroy_virtqueue(bvqp->out);
+               kfree(bvqp);
+               kdev->be_queues = NULL;
+       }
+
+       synchronize_irq(kdev->desc.irq);
+       free_irq(kdev->desc.irq, dev);
+
+       return 0;
+}
+
+static struct virtio_bus kvmnet_probes = {
+       .vbus_probe = kvm_virtnet_probe, 
+       .vbus_unprobe = kvm_virtnet_unprobe};
+
+static int __devinit kvmnet_init_one(struct pci_dev *pdev,
+                                    const struct pci_device_id *ent)
+{
+       sprintf(kvmnet_probes.name, "kvm_net");
+       return kvmbus_init_one(pdev, ent, &kvmnet_probes,
VIRTIO_DEVICE_T_NET);
+}
+
+static void __devexit kvmnet_remove_one(struct pci_dev *pdev)
+{
+       kvmbus_remove_one(pdev, &kvmnet_probes);
+}
+
+#define KVMNET_DRIVER_NAME "paravirt_network_driver"
+#define KVMNET_DRIVER_VERSION "1"
+#define PCI_VENDOR_ID_KVMNET 0x5002
+#define PCI_DEVICE_ID_KVMNET 0x1234
+
+static struct pci_device_id kvmnet_pci_tbl[] = {
+       {PCI_VENDOR_ID_KVMNET, PCI_DEVICE_ID_KVMNET, PCI_ANY_ID,
PCI_ANY_ID, 0, 0, 0 },
+       {0,}
+};
+MODULE_DEVICE_TABLE (pci, kvmnet_pci_tbl);
+
+
+static struct pci_driver kvmnet_pci_driver = {
+       .name           = KVMNET_DRIVER_NAME,
+       .id_table       = kvmnet_pci_tbl,
+       .probe          = kvmnet_init_one,
+       .remove         = __devexit_p(kvmnet_remove_one),
+};
+
+int __init kvmnet_init(void)
+{
+       return pci_register_driver(&kvmnet_pci_driver);
+}
+
+static void __exit kvmnet_exit(void)
+{
+       pci_unregister_driver(&kvmnet_pci_driver);
+}
+
+module_init(kvmnet_init);
+module_exit(kvmnet_exit);
+
+MODULE_DESCRIPTION("KVM virtnetwork driver");
+MODULE_LICENSE("GPL");
+
+
diff --git a/include/linux/kvm_para.h b/include/linux/kvm_para.h
index cd1a7da..34cf501 100644
--- a/include/linux/kvm_para.h
+++ b/include/linux/kvm_para.h
@@ -76,5 +76,6 @@ struct kvm_vcpu_para_state {
 #define __NR_hypercall_test                    0
 
 #define __NR_hypercall_notify
(KVM_KERNEL_NR_HYPERCALLS + 1)
+#define __NR_hypercall_register
(KVM_KERNEL_NR_HYPERCALLS + 2)
 
 #endif

-----
In simplicity there is elegance.
Dor Laor ;)


-------------------------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc.
Still grepping through log files to find problems?  Stop.
Now Search log events and configuration files using AJAX and a browser.
Download your FREE copy of Splunk now >>  http://get.splunk.com/
_______________________________________________
kvm-devel mailing list
kvm-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/kvm-devel

Reply via email to