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
|