Merge to Fedora kernel-2.6.18-1.2224_FC5 patched with stable patch-2.6.18.1-vs2.0...
[linux-2.6.git] / drivers / xen / xenbus / xenbus_probe.c
index a920722..2ae941f 100644 (file)
 
 #include <asm/io.h>
 #include <asm/page.h>
+#include <asm/maddr.h>
 #include <asm/pgtable.h>
 #include <asm/hypervisor.h>
+#include <asm/hypercall.h>
 #include <xen/xenbus.h>
 #include <xen/xen_proc.h>
 #include <xen/evtchn.h>
 #include <xen/features.h>
+#include <xen/hvm.h>
 
 #include "xenbus_comms.h"
 
+int xen_store_evtchn;
+struct xenstore_domain_interface *xen_store_interface;
+static unsigned long xen_store_mfn;
+
 extern struct mutex xenwatch_mutex;
 
 static BLOCKING_NOTIFIER_HEAD(xenstore_notifier_list);
 
 static void wait_for_devices(struct xenbus_driver *xendrv);
 
+static int xenbus_probe_frontend(const char *type, const char *name);
+static int xenbus_uevent_backend(struct device *dev, char **envp,
+                                int num_envp, char *buffer, int buffer_size);
+static int xenbus_probe_backend(const char *type, const char *domid);
+
+static int xenbus_dev_probe(struct device *_dev);
+static int xenbus_dev_remove(struct device *_dev);
+static void xenbus_dev_shutdown(struct device *_dev);
+
 /* If something in array of ids matches this device, return it. */
 static const struct xenbus_device_id *
 match_device(const struct xenbus_device_id *arr, struct xenbus_device *dev)
@@ -168,15 +184,17 @@ static int read_frontend_details(struct xenbus_device *xendev)
 
 
 /* Bus type for frontend drivers. */
-static int xenbus_probe_frontend(const char *type, const char *name);
 static struct xen_bus_type xenbus_frontend = {
        .root = "device",
        .levels = 2,            /* device/type/<id> */
        .get_bus_id = frontend_bus_id,
        .probe = xenbus_probe_frontend,
        .bus = {
-               .name  = "xen",
-               .match = xenbus_match,
+               .name     = "xen",
+               .match    = xenbus_match,
+               .probe    = xenbus_dev_probe,
+               .remove   = xenbus_dev_remove,
+               .shutdown = xenbus_dev_shutdown,
        },
        .dev = {
                .bus_id = "xen",
@@ -221,18 +239,18 @@ static int backend_bus_id(char bus_id[BUS_ID_SIZE], const char *nodename)
        return 0;
 }
 
-static int xenbus_uevent_backend(struct device *dev, char **envp,
-                                int num_envp, char *buffer, int buffer_size);
-static int xenbus_probe_backend(const char *type, const char *domid);
 static struct xen_bus_type xenbus_backend = {
        .root = "backend",
        .levels = 3,            /* backend/type/<frontend>/<id> */
        .get_bus_id = backend_bus_id,
        .probe = xenbus_probe_backend,
        .bus = {
-               .name  = "xen-backend",
-               .match = xenbus_match,
-               .uevent = xenbus_uevent_backend,
+               .name     = "xen-backend",
+               .match    = xenbus_match,
+               .probe    = xenbus_dev_probe,
+               .remove   = xenbus_dev_remove,
+//             .shutdown = xenbus_dev_shutdown,
+               .uevent   = xenbus_uevent_backend,
        },
        .dev = {
                .bus_id = "xen-backend",
@@ -302,8 +320,23 @@ static void otherend_changed(struct xenbus_watch *watch,
 
        state = xenbus_read_driver_state(dev->otherend);
 
-       DPRINTK("state is %d, %s, %s",
-               state, dev->otherend_watch.node, vec[XS_WATCH_PATH]);
+       DPRINTK("state is %d (%s), %s, %s", state, xenbus_strstate(state),
+               dev->otherend_watch.node, vec[XS_WATCH_PATH]);
+
+       /*
+        * Ignore xenbus transitions during shutdown. This prevents us doing
+        * work that can fail e.g., when the rootfs is gone.
+        */
+       if (system_state > SYSTEM_RUNNING) {
+               struct xen_bus_type *bus = bus;
+               bus = container_of(dev->dev.bus, struct xen_bus_type, bus);
+               /* If we're frontend, drive the state machine to Closed. */
+               /* This should cause the backend to release our resources. */
+               if ((bus == &xenbus_frontend) && (state == XenbusStateClosing))
+                       xenbus_frontend_closed(dev);
+               return;
+       }
+
        if (drv->otherend_changed)
                drv->otherend_changed(dev, state);
 }
@@ -334,7 +367,7 @@ static int xenbus_dev_probe(struct device *_dev)
        const struct xenbus_device_id *id;
        int err;
 
-       DPRINTK("");
+       DPRINTK("%s", dev->nodename);
 
        if (!drv->probe) {
                err = -ENODEV;
@@ -379,7 +412,7 @@ static int xenbus_dev_remove(struct device *_dev)
        struct xenbus_device *dev = to_xenbus_device(_dev);
        struct xenbus_driver *drv = to_xenbus_driver(_dev->driver);
 
-       DPRINTK("");
+       DPRINTK("%s", dev->nodename);
 
        free_otherend_watch(dev);
        free_otherend_details(dev);
@@ -391,6 +424,27 @@ static int xenbus_dev_remove(struct device *_dev)
        return 0;
 }
 
+static void xenbus_dev_shutdown(struct device *_dev)
+{
+       struct xenbus_device *dev = to_xenbus_device(_dev);
+       unsigned long timeout = 5*HZ;
+
+       DPRINTK("%s", dev->nodename);
+
+       get_device(&dev->dev);
+       if (dev->state != XenbusStateConnected) {
+               printk("%s: %s: %s != Connected, skipping\n", __FUNCTION__,
+                      dev->nodename, xenbus_strstate(dev->state));
+               goto out;
+       }
+       xenbus_switch_state(dev, XenbusStateClosing);
+       timeout = wait_for_completion_timeout(&dev->down, timeout);
+       if (!timeout)
+               printk("%s: %s timeout closing device\n", __FUNCTION__, dev->nodename);
+ out:
+       put_device(&dev->dev);
+}
+
 static int xenbus_register_driver_common(struct xenbus_driver *drv,
                                         struct xen_bus_type *bus)
 {
@@ -399,8 +453,6 @@ static int xenbus_register_driver_common(struct xenbus_driver *drv,
        drv->driver.name = drv->name;
        drv->driver.bus = &bus->bus;
        drv->driver.owner = drv->owner;
-       drv->driver.probe = xenbus_dev_probe;
-       drv->driver.remove = xenbus_dev_remove;
 
        mutex_lock(&xenwatch_mutex);
        ret = driver_register(&drv->driver);
@@ -508,27 +560,6 @@ static void xenbus_dev_release(struct device *dev)
                kfree(to_xenbus_device(dev));
 }
 
-/* Simplified asprintf. */
-char *kasprintf(const char *fmt, ...)
-{
-       va_list ap;
-       unsigned int len;
-       char *p, dummy[1];
-
-       va_start(ap, fmt);
-       /* FIXME: vsnprintf has a bug, NULL should work */
-       len = vsnprintf(dummy, 0, fmt, ap);
-       va_end(ap);
-
-       p = kmalloc(len + 1, GFP_KERNEL);
-       if (!p)
-               return NULL;
-       va_start(ap, fmt);
-       vsprintf(p, fmt, ap);
-       va_end(ap);
-       return p;
-}
-
 static ssize_t xendev_show_nodename(struct device *dev,
                                    struct device_attribute *attr, char *buf)
 {
@@ -575,6 +606,7 @@ static int xenbus_probe_node(struct xen_bus_type *bus,
        tmpstring += strlen(tmpstring) + 1;
        strcpy(tmpstring, type);
        xendev->devicetype = tmpstring;
+       init_completion(&xendev->down);
 
        xendev->dev.parent = &bus->dev;
        xendev->dev.bus = &bus->bus;
@@ -604,7 +636,7 @@ static int xenbus_probe_frontend(const char *type, const char *name)
        char *nodename;
        int err;
 
-       nodename = kasprintf("%s/%s/%s", xenbus_frontend.root, type, name);
+       nodename = kasprintf(GFP_KERNEL, "%s/%s/%s", xenbus_frontend.root, type, name);
        if (!nodename)
                return -ENOMEM;
 
@@ -623,7 +655,7 @@ static int xenbus_probe_backend_unit(const char *dir,
        char *nodename;
        int err;
 
-       nodename = kasprintf("%s/%s", dir, name);
+       nodename = kasprintf(GFP_KERNEL, "%s/%s", dir, name);
        if (!nodename)
                return -ENOMEM;
 
@@ -644,7 +676,7 @@ static int xenbus_probe_backend(const char *type, const char *domid)
 
        DPRINTK("");
 
-       nodename = kasprintf("%s/%s/%s", xenbus_backend.root, type, domid);
+       nodename = kasprintf(GFP_KERNEL, "%s/%s/%s", xenbus_backend.root, type, domid);
        if (!nodename)
                return -ENOMEM;
 
@@ -750,7 +782,7 @@ static void dev_changed(const char *node, struct xen_bus_type *bus)
        rootlen = strsep_len(node, '/', bus->levels);
        if (rootlen < 0)
                return;
-       root = kasprintf("%.*s", rootlen, node);
+       root = kasprintf(GFP_KERNEL, "%.*s", rootlen, node);
        if (!root)
                return;
 
@@ -840,7 +872,7 @@ static int resume_dev(struct device *dev, void *data)
                        printk(KERN_WARNING
                               "xenbus: resume %s failed: %i\n", 
                               dev->bus_id, err);
-                       return err; 
+                       return err;
                }
        }
 
@@ -852,7 +884,7 @@ static int resume_dev(struct device *dev, void *data)
                return err;
        }
 
-       return 0; 
+       return 0;
 }
 
 void xenbus_suspend(void)
@@ -928,8 +960,7 @@ static int xsd_kva_mmap(struct file *file, struct vm_area_struct *vma)
        if ((size > PAGE_SIZE) || (vma->vm_pgoff != 0))
                return -EINVAL;
 
-       if (remap_pfn_range(vma, vma->vm_start,
-                           mfn_to_pfn(xen_start_info->store_mfn),
+       if (remap_pfn_range(vma, vma->vm_start, mfn_to_pfn(xen_store_mfn),
                            size, vma->vm_page_prot))
                return -EAGAIN;
 
@@ -941,7 +972,7 @@ static int xsd_kva_read(char *page, char **start, off_t off,
 {
        int len;
 
-       len  = sprintf(page, "0x%p", mfn_to_virt(xen_start_info->store_mfn));
+       len  = sprintf(page, "0x%p", xen_store_interface);
        *eof = 1;
        return len;
 }
@@ -951,16 +982,15 @@ static int xsd_port_read(char *page, char **start, off_t off,
 {
        int len;
 
-       len  = sprintf(page, "%d", xen_start_info->store_evtchn);
+       len  = sprintf(page, "%d", xen_store_evtchn);
        *eof = 1;
        return len;
 }
 #endif
 
-
 static int __init xenbus_probe_init(void)
 {
-       int err = 0, dom0;
+       int err = 0;
        unsigned long page = 0;
 
        DPRINTK("");
@@ -975,9 +1005,7 @@ static int __init xenbus_probe_init(void)
        /*
         * Domain0 doesn't have a store_evtchn or store_mfn yet.
         */
-       dom0 = (xen_start_info->store_evtchn == 0);
-
-       if (dom0) {
+       if (is_initial_xendomain()) {
                struct evtchn_alloc_unbound alloc_unbound;
 
                /* Allocate page. */
@@ -985,7 +1013,7 @@ static int __init xenbus_probe_init(void)
                if (!page)
                        return -ENOMEM;
 
-               xen_start_info->store_mfn =
+               xen_store_mfn = xen_start_info->store_mfn =
                        pfn_to_mfn(virt_to_phys((void *)page) >>
                                   PAGE_SHIFT);
 
@@ -998,7 +1026,8 @@ static int __init xenbus_probe_init(void)
                if (err == -ENOSYS)
                        goto err;
                BUG_ON(err);
-               xen_start_info->store_evtchn = alloc_unbound.port;
+               xen_store_evtchn = xen_start_info->store_evtchn =
+                       alloc_unbound.port;
 
 #ifdef CONFIG_PROC_FS
                /* And finally publish the above info in /proc/xen */
@@ -1014,8 +1043,23 @@ static int __init xenbus_probe_init(void)
                if (xsd_port_intf)
                        xsd_port_intf->read_proc = xsd_port_read;
 #endif
-       } else
+               xen_store_interface = mfn_to_virt(xen_store_mfn);
+       } else {
                xenstored_ready = 1;
+#ifdef CONFIG_XEN
+               xen_store_evtchn = xen_start_info->store_evtchn;
+               xen_store_mfn = xen_start_info->store_mfn;
+               xen_store_interface = mfn_to_virt(xen_store_mfn);
+#else
+               xen_store_evtchn = hvm_get_parameter(HVM_PARAM_STORE_EVTCHN);
+               xen_store_mfn = hvm_get_parameter(HVM_PARAM_STORE_PFN);
+               xen_store_interface = ioremap(xen_store_mfn << PAGE_SHIFT,
+                                             PAGE_SIZE);
+#endif
+       }
+
+
+       xenbus_dev_init();
 
        /* Initialize the interface to xenstore. */
        err = xs_init();
@@ -1029,7 +1073,7 @@ static int __init xenbus_probe_init(void)
        device_register(&xenbus_frontend.dev);
        device_register(&xenbus_backend.dev);
 
-       if (!dom0)
+       if (!is_initial_xendomain())
                xenbus_probe(NULL);
 
        return 0;
@@ -1049,6 +1093,8 @@ static int __init xenbus_probe_init(void)
 
 postcore_initcall(xenbus_probe_init);
 
+MODULE_LICENSE("Dual BSD/GPL");
+
 
 static int is_disconnected_device(struct device *dev, void *data)
 {
@@ -1132,6 +1178,7 @@ static void wait_for_devices(struct xenbus_driver *xendrv)
                         print_device_status);
 }
 
+#ifndef MODULE
 static int __init boot_wait_for_devices(void)
 {
        ready_to_wait_for_devices = 1;
@@ -1140,3 +1187,4 @@ static int __init boot_wait_for_devices(void)
 }
 
 late_initcall(boot_wait_for_devices);
+#endif