[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [PATCH 6/9] Linux support for vdevice bus
Subject: Linux support for vdevice bus This patch provides the Linux implementation of the vdevice bus. FIXME: currently it does not support save/restore of the domain: it should call stop before shutting down, and remap shares afterwards before calling reconnect. This depends on exactly what we do with shared pages on restore. diff -r 520f3bf7d3f0 linux-2.6-xen-sparse/drivers/xen/Makefile --- a/linux-2.6-xen-sparse/drivers/xen/Makefile Fri Jun 2 05:22:39 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/Makefile Fri Jun 2 17:04:48 2006 @@ -8,6 +8,7 @@ obj-y += balloon/ obj-y += privcmd/ obj-y += xenbus/ +obj-y += vdevice/ obj-y += xenshare.o obj-$(CONFIG_XEN_BLKDEV_BACKEND) += blkback/ diff -r 520f3bf7d3f0 linux-2.6-xen-sparse/drivers/xen/vdevice/Makefile --- /dev/null Fri Jun 2 05:22:39 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/vdevice/Makefile Fri Jun 2 17:04:48 2006 @@ -0,0 +1,1 @@ +obj-y := vdevice.o diff -r 520f3bf7d3f0 linux-2.6-xen-sparse/drivers/xen/vdevice/vdevice.c --- /dev/null Fri Jun 2 05:22:39 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/vdevice/vdevice.c Fri Jun 2 17:04:48 2006 @@ -0,0 +1,286 @@ +#define DEBUG + +#include <linux/init.h> +#include <linux/err.h> +#include <linux/kernel.h> +#include <linux/device.h> +#include <linux/vdevice.h> +#include <linux/page-flags.h> +#include <linux/vmalloc.h> +#include <linux/workqueue.h> +#include <xen/evtchn.h> +#include <asm/page.h> +#include <xen/interface/share.h> + +static struct work_struct vdevice_add; +static struct xen_share *vdevice_share; +static struct vdevice_desc *vdevices; +static int vdevice_change_counter = 1; +static struct device **devices_installed; + +static ssize_t show_ref(struct bus_type *bus, char *buf) +{ + return sprintf(buf, "0x%lx\n", xen_start_info->vdevice_share); +} +static BUS_ATTR(share_ref, 0444, show_ref, NULL); + +static ssize_t type_show(struct device *_dev, + struct device_attribute *attr, char *buf) +{ + struct vdevice *dev = container_of(_dev, struct vdevice, dev); + return sprintf(buf, "%i", dev->id.type); +} +static ssize_t features_show(struct device *_dev, + struct device_attribute *attr, char *buf) +{ + struct vdevice *dev = container_of(_dev, struct vdevice, dev); + return sprintf(buf, "%i", dev->id.features); +} +static ssize_t share_ref_show(struct device *_dev, + struct device_attribute *attr, char *buf) +{ + struct vdevice *dev = container_of(_dev, struct vdevice, dev); + return sprintf(buf, "%li", + (long)vdevices[dev->vdevice_index].shared_ref); +} +static ssize_t status_show(struct device *_dev, + struct device_attribute *attr, char *buf) +{ + struct vdevice *dev = container_of(_dev, struct vdevice, dev); + return sprintf(buf, "%i", vdevices[dev->vdevice_index].status); +} +static ssize_t status_store(struct device *_dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct vdevice *dev = container_of(_dev, struct vdevice, dev); + if (sscanf(buf, "%i", &vdevices[dev->vdevice_index].status) != 1) + return -EINVAL; + return count; +} +static struct device_attribute vdevice_dev_attrs[] = { + __ATTR_RO(type), + __ATTR_RO(features), + __ATTR_RO(share_ref), + __ATTR(status, 0644, status_show, status_store), + __ATTR_NULL +}; + +static int vdevice_match(struct device *_dev, struct device_driver *_drv) +{ + const struct vdevice_id *i; + struct vdevice *dev = container_of(_dev, struct vdevice, dev); + struct vdevice_driver *drv = container_of(_drv, struct vdevice_driver, + driver); + + for (i = drv->ids; i->type != 0; i++) { + if (dev->id.type == i->type && + (dev->id.features & i->features) == i->features) + return 1; + } + return 0; +} + +struct vdevice_bus { + struct bus_type bus; + struct vdevice dev; +}; + +static struct vdevice_bus vd_bus = { + .bus = { + .name = "vdevice", + .match = vdevice_match, + .dev_attrs = vdevice_dev_attrs, + }, + .dev.dev = { + .parent = NULL, + .bus_id = "vdevice", + } +}; + +static int vdevice_dev_probe(struct device *_dev) +{ + int ret; + struct vdevice *dev = container_of(_dev, struct vdevice, dev); + struct vdevice_driver *drv = container_of(dev->dev.driver, + struct vdevice_driver, driver); + struct vdevice_desc *me = &vdevices[dev->vdevice_index]; + + me->status |= VDEVICE_S_DRIVER; + + /* We only set this up when we actually probe, as userspace + * drivers don't want this. Previous probe might have failed, + * so we could already have it mapped. */ + if (!dev->share) { + dev->share = xen_share_get(me->shared_ref, me->nr_pages); + if (IS_ERR(dev->share)) { + printk(KERN_ERR + "vdevice: failed mapping %u@%li for %i/%i\n", + me->nr_pages, (long)me->shared_ref, + dev->id.type, dev->id.features); + me->status |= VDEVICE_S_FAILED; + ret = PTR_ERR(dev->share); + dev->share = NULL; + return ret; + } + me->status |= VDEVICE_S_MAPPED; + } + + ret = drv->probe(dev, &dev->id); + if (ret == 0) + me->status |= VDEVICE_S_DRIVER_OK; + return ret; +} + +static int vdevice_dev_remove(struct device *_dev) +{ + struct vdevice *dev = container_of(_dev, struct vdevice, dev); + struct vdevice_driver *drv = container_of(dev->dev.driver, + struct vdevice_driver, driver); + + if (drv && drv->remove) + drv->remove(dev); + if (dev->share) + xen_share_put(dev->share); + put_device(_dev); + return 0; +} + +int register_vdevice_driver(struct vdevice_driver *drv) +{ + drv->driver.bus = &vd_bus.bus; + drv->driver.name = drv->name; + drv->driver.owner = drv->owner; + drv->driver.probe = vdevice_dev_probe; + drv->driver.remove = vdevice_dev_remove; + + return driver_register(&drv->driver); +} +EXPORT_SYMBOL_GPL(register_vdevice_driver); + +void unregister_vdevice_driver(struct vdevice_driver *drv) +{ + driver_unregister(&drv->driver); +} +EXPORT_SYMBOL_GPL(unregister_vdevice_driver); + +static share_ref_t new_shared_page(void) +{ + dom0_op_t op = { .cmd = DOM0_CREATESHAREDPAGES, + .interface_version = DOM0_INTERFACE_VERSION, + .u.createsharedpages.num = 1 }; + + return HYPERVISOR_dom0_op(&op); +} + +static void release_vdevice(struct device *_dev) +{ + struct vdevice *dev = container_of(_dev, struct vdevice, dev); + + devices_installed[dev->vdevice_index] = NULL; + kfree(dev); +} + +static void add_vdevice(unsigned int num) +{ + struct vdevice *new; + + vdevices[num].status = VDEVICE_S_ACKNOWLEDGE; + new = kmalloc(sizeof(struct vdevice), GFP_KERNEL); + if (!new) { + printk(KERN_EMERG "Could not allocate vdevice %u\n", num); + vdevices[num].status |= VDEVICE_S_FAILED; + return; + } + + new->vdevice_index = num; + new->id = vdevices[num].id; + new->private = NULL; + memset(&new->dev, 0, sizeof(new->dev)); + new->dev.parent = &vd_bus.dev.dev; + new->dev.bus = &vd_bus.bus; + new->dev.release = release_vdevice; + sprintf(new->dev.bus_id, "%u", num); + new->share = NULL; + if (device_register(&new->dev) != 0) { + printk(KERN_EMERG "Could not register vdevice %u\n", num); + vdevices[num].status |= VDEVICE_S_FAILED; + kfree(new); + } + + devices_installed[num] = &new->dev; +} + +static void vdevice_work(void *unused) +{ + unsigned int i; + + /* Something changed: look for differences. */ + for (i = 0; i < PAGE_SIZE / sizeof(struct vdevice_desc); i++) { + char name[20]; + struct device *dev; + + sprintf(name, "%i", i); + dev = devices_installed[i]; + if (vdevices[i].id.type != 0 && !dev) + add_vdevice(i); + else if (dev && vdevices[i].id.type == 0) + device_unregister(dev); + } + + /* Re-arm trigger */ + vdevice_change_counter = 1; + + /* Acknowledge. */ + HYPERVISOR_share(XEN_SHARE_trigger, xen_start_info->vdevice_share, + 0, 0, 0); +} + +static void vdevice_handler(struct xen_share_handler *h) +{ + schedule_work(&vdevice_add); +} +static struct xen_share_handler handler = { + .handler = vdevice_handler, +}; + +static int __init vdevice_init(void) +{ + int err; + + if (!xen_start_info->vdevice_share) { + /* We could be dom0, in which case we can create it. */ + xen_start_info->vdevice_share = new_shared_page(); + if (IS_ERR_VALUE(xen_start_info->vdevice_share)) { + printk(KERN_INFO "Vdevice bus not found\n"); + xen_start_info->vdevice_share = 0; + return 0; + } + } + printk(KERN_INFO "vdevice bus found at 0x%lx\n", xen_start_info->vdevice_share); + + vdevice_share = xen_share_get(xen_start_info->vdevice_share, 1); + BUG_ON(IS_ERR(vdevice_share)); + vdevices = vdevice_share->addr; + + /* Allocate space for the same number of devices as can fit + * on the vdevices page */ + devices_installed = kcalloc(sizeof(struct device*), + PAGE_SIZE / sizeof(struct vdevice_desc), + GFP_KERNEL); + BUG_ON(!devices_installed); + + bus_register(&vd_bus.bus); + device_register(&vd_bus.dev.dev); + bus_create_file(&vd_bus.bus, &bus_attr_share_ref); + + /* Scan bus once for existing devices before setting up interrupt */ + vdevice_work(NULL); + + INIT_WORK(&vdevice_add, vdevice_work, NULL); + xen_share_add_handler(vdevice_share, &handler); + err = xen_share_watch(vdevice_share, 1, &vdevice_change_counter); + BUG_ON(err<0); + + return 0; +} +postcore_initcall(vdevice_init); diff -r 520f3bf7d3f0 linux-2.6-xen-sparse/include/linux/vdevice.h --- /dev/null Fri Jun 2 05:22:39 2006 +++ b/linux-2.6-xen-sparse/include/linux/vdevice.h Fri Jun 2 17:04:48 2006 @@ -0,0 +1,40 @@ +#ifndef _LINUX_VDEVICE_H_ +#define _LINUX_VDEVICE_H_ + +#include <linux/device.h> +#include <xen/interface/io/vdevice.h> +#include <xen/interface/share.h> +#include <asm/share.h> + +struct vdevice { + /* Unique busid */ + int vdevice_index; + + /* Shared region for this device. */ + struct xen_share *share; + + struct device dev; + struct vdevice_id id; + + /* Driver can hang data off here. */ + void *private; +}; + +struct vdevice_driver { + /* I can drive the following type of device(s) */ + char *name; + struct module *owner; + const struct vdevice_id *ids; + int (*probe)(struct vdevice *dev, const struct vdevice_id *id); + void (*remove)(struct vdevice *dev); + + void (*stop)(struct vdevice *dev); + int (*reconnect)(struct vdevice *dev); + + struct device_driver driver; +}; + +extern int register_vdevice_driver(struct vdevice_driver *drv); +extern void unregister_vdevice_driver(struct vdevice_driver *drv); + +#endif /* _LINUX_VDEVICE_H_ */ -- ccontrol: http://ccontrol.ozlabs.org _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxxxxxxxx http://lists.xensource.com/xen-devel
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |