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
[email protected]
https://lists.sourceforge.net/lists/listinfo/kvm-devel