From patchwork Sun May 31 18:59:01 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Michael S. Tsirkin" X-Patchwork-Id: 27131 Received: from vger.kernel.org (vger.kernel.org [209.132.176.167]) by demeter.kernel.org (8.14.2/8.14.2) with ESMTP id n4VIxIef006375 for ; Sun, 31 May 2009 18:59:18 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751889AbZEaS7N (ORCPT ); Sun, 31 May 2009 14:59:13 -0400 Received: (majordomo@vger.kernel.org) by vger.kernel.org id S1751614AbZEaS7N (ORCPT ); Sun, 31 May 2009 14:59:13 -0400 Received: from mx2.redhat.com ([66.187.237.31]:43309 "EHLO mx2.redhat.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751798AbZEaS7N (ORCPT ); Sun, 31 May 2009 14:59:13 -0400 Received: from int-mx2.corp.redhat.com (int-mx2.corp.redhat.com [172.16.27.26]) by mx2.redhat.com (8.13.8/8.13.8) with ESMTP id n4VIxEwa016073; Sun, 31 May 2009 14:59:14 -0400 Received: from ns3.rdu.redhat.com (ns3.rdu.redhat.com [10.11.255.199]) by int-mx2.corp.redhat.com (8.13.1/8.13.1) with ESMTP id n4VIxDuV019009; Sun, 31 May 2009 14:59:14 -0400 Received: from redhat.com (vpn-10-98.str.redhat.com [10.32.10.98]) by ns3.rdu.redhat.com (8.13.8/8.13.8) with ESMTP id n4VIxBAS019726; Sun, 31 May 2009 14:59:11 -0400 Date: Sun, 31 May 2009 21:59:01 +0300 From: "Michael S. Tsirkin" To: Gregory Haskins , kvm@vger.kernel.org, avi@redhat.com, mtosatti@redhat.com Subject: [PATCH 1/3] virt-core: binding together drivers and hypervisors Message-ID: <20090531185901.GB10043@redhat.com> References: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: User-Agent: Mutt/1.5.17+20080114 (2008-01-14) X-Scanned-By: MIMEDefang 2.58 on 172.16.27.26 Sender: kvm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org This adds support for virt-core: generic glue making it possible to implement kernel-level accelerators (drivers) which are independent of a hypervisor, that is can in theory work on top of either kvm or lguest. Each driver and hypervisor registers with core, and then when user adds a virtual device, drivers can find and interfact with hypervisors through the virt_dev object. Users add devices by making a hypervisor-specific system call (e.g. ioctl) the result of which is a file descriptor controlling the device. Signed-off-by: Michael S. Tsirkin --- arch/x86/kvm/Kconfig | 1 + drivers/Makefile | 1 + drivers/virt/Kconfig | 5 ++ drivers/virt/Makefile | 1 + drivers/virt/virt_core.c | 111 ++++++++++++++++++++++++++++++++++++++++++++++ include/linux/virt.h | 94 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 213 insertions(+), 0 deletions(-) create mode 100644 drivers/virt/Kconfig create mode 100644 drivers/virt/Makefile create mode 100644 drivers/virt/virt_core.c create mode 100644 include/linux/virt.h diff --git a/arch/x86/kvm/Kconfig b/arch/x86/kvm/Kconfig index a58504e..693b807 100644 --- a/arch/x86/kvm/Kconfig +++ b/arch/x86/kvm/Kconfig @@ -71,6 +71,7 @@ config KVM_TRACE # OK, it's a little counter-intuitive to do this, but it puts it neatly under # the virtualization menu. +source drivers/virt/Kconfig source drivers/lguest/Kconfig source drivers/virtio/Kconfig diff --git a/drivers/Makefile b/drivers/Makefile index 1266ead..9f14b08 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -104,6 +104,7 @@ obj-$(CONFIG_HID) += hid/ obj-$(CONFIG_PPC_PS3) += ps3/ obj-$(CONFIG_OF) += of/ obj-$(CONFIG_SSB) += ssb/ +obj-$(CONFIG_VIRT_CORE) += virt/ obj-$(CONFIG_VIRTIO) += virtio/ obj-$(CONFIG_STAGING) += staging/ obj-y += platform/ diff --git a/drivers/virt/Kconfig b/drivers/virt/Kconfig new file mode 100644 index 0000000..ace7b2e --- /dev/null +++ b/drivers/virt/Kconfig @@ -0,0 +1,5 @@ +config VIRT_CORE + tristate "Generic virtual device binding support" + ---help--- + Core support for binding kernel drivers to virtual devices. + Make sure to also select any drivers you wish to use. diff --git a/drivers/virt/Makefile b/drivers/virt/Makefile new file mode 100644 index 0000000..7a77047 --- /dev/null +++ b/drivers/virt/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_VIRT_CORE) += virt_core.o diff --git a/drivers/virt/virt_core.c b/drivers/virt/virt_core.c new file mode 100644 index 0000000..89e1f53 --- /dev/null +++ b/drivers/virt/virt_core.c @@ -0,0 +1,111 @@ +/* + * Virt core support: this is the glue that binds together drivers for virtual devices + * and hypervisors. + * + * Copyright (c) 2009 Red Hat Inc. + * + * Author: Michael S. Tsirkin + */ +#include +#include + +#include +#include +#include +#include + +#include + +/* virt bus implementation */ +static LIST_HEAD(driver_list); +static LIST_HEAD(hypervisor_list); +static DEFINE_MUTEX(driver_lock); +static DEFINE_MUTEX(hypervisor_lock); + +static int virt_close(struct inode *inode, struct file *filp) +{ + struct virt_dev *dev = filp->private_data; + dev->driver->device_remove(dev->driver, dev); + dev->hypervisor->put(dev->hypervisor); + kfree(dev); + return 0; +} + +void virt_driver_register(struct virt_driver *driver, + struct file_operations *fops) +{ + BUG_ON(fops->release); + BUG_ON(fops->open); + driver->f_ops = fops; + fops->release = virt_close; + mutex_lock(&driver_lock); + list_add(&driver->list, &driver_list); + mutex_unlock(&driver_lock); +} +EXPORT_SYMBOL_GPL(virt_driver_register); + +void virt_driver_unregister(struct virt_driver *driver) +{ + mutex_lock(&driver_lock); + list_del(&driver->list); + mutex_unlock(&driver_lock); +} +EXPORT_SYMBOL_GPL(virt_driver_unregister); + +void virt_hypervisor_register(struct virt_hypervisor *hypervisor) +{ + mutex_lock(&hypervisor_lock); + list_add(&hypervisor->list, &hypervisor_list); + mutex_unlock(&hypervisor_lock); +} +EXPORT_SYMBOL_GPL(virt_hypervisor_register); + +void virt_hypervisor_unregister(struct virt_hypervisor *hypervisor) +{ + mutex_lock(&hypervisor_lock); + list_del(&hypervisor->list); + mutex_unlock(&hypervisor_lock); +} +EXPORT_SYMBOL_GPL(virt_hypervisor_unregister); + +int virt_device_create(struct virt_hypervisor *hypervisor, int flags, + const u8 *id, int id_len) +{ + struct virt_dev *dev = kmalloc(sizeof *dev, GFP_KERNEL); + struct virt_driver *driver; + const struct file_operations *fops; + int ret; + if (!dev) + return -ENOMEM; + dev->hypervisor = hypervisor; + hypervisor->get(hypervisor); + + mutex_lock(&driver_lock); + ret = -ENODEV; + list_for_each_entry(driver, &driver_list, list) { + ret = driver->device_probe(driver, dev, id, id_len); + if (ret != -ENODEV) + break; + } + mutex_unlock(&driver_lock); + if (ret) + goto err_probe; + + fops = fops_get(driver->f_ops); + if (!fops) { + ret = -ENOMEM; + goto err_file; + } + + ret = anon_inode_getfd(driver->name, fops, dev, flags); + if (ret < 0) + goto err_file; + return ret; +err_file: + driver->device_remove(driver, dev); +err_probe: + hypervisor->put(hypervisor); + kfree(dev); + return ret; +} +EXPORT_SYMBOL_GPL(virt_device_create); diff --git a/include/linux/virt.h b/include/linux/virt.h new file mode 100644 index 0000000..ecc0aef --- /dev/null +++ b/include/linux/virt.h @@ -0,0 +1,94 @@ +#ifndef LINUX_VIRT_H +#define LINUX_VIRT_H +/* Virt core support. + * Each driver and hypervisor register with core, and then drivers can find + * and interfact with hypervisors through the virt_dev object. + */ + +#include +#include +#include + +struct virt_dev; + +struct virt_hypervisor { + /* Fields set by virt core. */ + struct list_head list; + /* Fields set by hypervisor. */ + /* Operations supported by this hypervisor. */ + /* core uses reference counting to make sure hypervisor does not + * go away while there are devices attached to it. */ + void (*get)(struct virt_hypervisor *); + void (*put)(struct virt_hypervisor *); + /* Optional interrupt injection support */ + int (*set_irq)(struct virt_hypervisor *, + int source, int irq, int level); +}; + +struct virt_driver { + /* Fields set by virt core. */ + struct list_head list; + const struct file_operations *f_ops; + /* Fields set by driver. */ + const char *name; + /* Check whether this driver wants this device, + * and bind driver to device if yes. + * Return -ENODEV if driver does not match this device, + * 0 on success, any other value is an error. */ + int (*device_probe)(struct virt_driver *, struct virt_dev *, + const void *id, int id_len); + /* Called on removal of all devices where this driver's probe + * returned success */ + void (*device_remove)(struct virt_driver *, struct virt_dev *); +}; + +/* Device objects are allocated and freed by virt core. */ +struct virt_dev { + struct virt_driver *driver; + void *driver_ctx; + struct virt_hypervisor *hypervisor; +}; + +/* Utility function for file_operations in drivers. */ +static inline struct virt_dev *virt_dev_get(struct file *file) +{ + return file->private_data; +} + +#ifdef CONFIG_VIRT_CORE_MODULE +void virt_driver_register(struct virt_driver *, struct file_operations *); +void virt_driver_unregister(struct virt_driver *); + +void virt_hypervisor_register(struct virt_hypervisor *); +void virt_hypervisor_unregister(struct virt_hypervisor *); + + +/* Users add devices by making a hypervisor-specific system call (e.g. ioctl) + * the result of which is a file descriptor controlling the device. + * Hypervisors implement this by calling virt_device_create to get struct file + * and install it in file descriptor table. Device is later released by + * closing the file descriptor */ +/* Associate a file descriptor with the device and return it. + * Returns a value < 0 on failure. */ +int virt_device_create(struct virt_hypervisor *, int flags, + const u8 *id, int id_len); +#else +static inline +void virt_driver_register(struct virt_driver *d, struct file_operations *o) {} +static inline +void virt_driver_unregister(struct virt_driver *d) {} + +static inline +void virt_hypervisor_register(struct virt_hypervisor *h) {} +static inline +void virt_hypervisor_unregister(struct virt_hypervisor *h) {} + +static inline +struct file *virt_device_create(struct virt_hypervisor *h, int flags, + const u8 *id, int id_len) +{ + return ERR_PTR(-ENOTTY); +} +#endif + +#endif