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