[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


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.