- Update Xen patches to 3.3-rc5 and c/s 1157.
[linux-flexiantxendom0-3.2.10.git] / drivers / xen / xenbus / xenbus_xs.c
index d1c217b..a723b8b 100644 (file)
 #include <xen/xen.h>
 #include "xenbus_comms.h"
 
+#ifdef HAVE_XEN_PLATFORM_COMPAT_H
+#include <xen/platform-compat.h>
+#endif
+
+#ifndef PF_NOFREEZE /* Old kernel (pre-2.6.6). */
+#define PF_NOFREEZE    0
+#endif
+
 struct xs_stored_msg {
        struct list_head list;
 
@@ -119,7 +127,7 @@ static DEFINE_SPINLOCK(watch_events_lock);
  * carrying out work.
  */
 static pid_t xenwatch_pid;
-static DEFINE_MUTEX(xenwatch_mutex);
+/* static */ DEFINE_MUTEX(xenwatch_mutex);
 static DECLARE_WAIT_QUEUE_HEAD(watch_events_waitq);
 
 static int get_error(const char *errorstring)
@@ -128,9 +136,8 @@ static int get_error(const char *errorstring)
 
        for (i = 0; strcmp(errorstring, xsd_errors[i].errstring) != 0; i++) {
                if (i == ARRAY_SIZE(xsd_errors) - 1) {
-                       printk(KERN_WARNING
-                              "XENBUS xen store gave: unknown error %s",
-                              errorstring);
+                       pr_warning("XENBUS xen store gave: unknown error %s",
+                                  errorstring);
                        return EINVAL;
                }
        }
@@ -196,10 +203,10 @@ static void transaction_resume(void)
 void *xenbus_dev_request_and_reply(struct xsd_sockmsg *msg)
 {
        void *ret;
-       struct xsd_sockmsg req_msg = *msg;
+       enum xsd_sockmsg_type type = msg->type;
        int err;
 
-       if (req_msg.type == XS_TRANSACTION_START)
+       if (type == XS_TRANSACTION_START)
                transaction_start();
 
        mutex_lock(&xs_state.request_mutex);
@@ -213,14 +220,15 @@ void *xenbus_dev_request_and_reply(struct xsd_sockmsg *msg)
 
        mutex_unlock(&xs_state.request_mutex);
 
-       if ((msg->type == XS_TRANSACTION_END) ||
-           ((req_msg.type == XS_TRANSACTION_START) &&
-            (msg->type == XS_ERROR)))
+       if ((type == XS_TRANSACTION_END) ||
+           ((type == XS_TRANSACTION_START) && (msg->type == XS_ERROR)))
                transaction_end();
 
        return ret;
 }
+#if !defined(CONFIG_XEN) && !defined(MODULE)
 EXPORT_SYMBOL(xenbus_dev_request_and_reply);
+#endif
 
 /* Send message to xs, get kmalloc'ed reply.  ERR_PTR() on error. */
 static void *xs_talkv(struct xenbus_transaction t,
@@ -272,9 +280,9 @@ static void *xs_talkv(struct xenbus_transaction t,
 
        if (msg.type != type) {
                if (printk_ratelimit())
-                       printk(KERN_WARNING
-                              "XENBUS unexpected type [%d], expected [%d]\n",
-                              msg.type, type);
+                       pr_warning("XENBUS unexpected type [%d],"
+                                  " expected [%d]\n",
+                                  msg.type, type);
                kfree(ret);
                return ERR_PTR(-EINVAL);
        }
@@ -331,7 +339,7 @@ static char **split(char *strings, unsigned int len, unsigned int *num)
        char *p, **ret;
 
        /* Count the strings. */
-       *num = count_strings(strings, len);
+       *num = count_strings(strings, len) + 1;
 
        /* Transfer to one big alloc for easy freeing. */
        ret = kmalloc(*num * sizeof(char *) + len, GFP_NOIO | __GFP_HIGH);
@@ -345,6 +353,7 @@ static char **split(char *strings, unsigned int len, unsigned int *num)
        strings = (char *)&ret[*num];
        for (p = strings, *num = 0; p < strings + len; p += strlen(p) + 1)
                ret[(*num)++] = p;
+       ret[*num] = strings + len;
 
        return ret;
 }
@@ -532,18 +541,18 @@ int xenbus_printf(struct xenbus_transaction t,
 {
        va_list ap;
        int ret;
-       char *buf;
+       char *printf_buffer;
 
        va_start(ap, fmt);
-       buf = kvasprintf(GFP_NOIO | __GFP_HIGH, fmt, ap);
+       printf_buffer = kvasprintf(GFP_NOIO | __GFP_HIGH, fmt, ap);
        va_end(ap);
 
-       if (!buf)
+       if (!printf_buffer)
                return -ENOMEM;
 
-       ret = xenbus_write(t, dir, node, buf);
+       ret = xenbus_write(t, dir, node, printf_buffer);
 
-       kfree(buf);
+       kfree(printf_buffer);
 
        return ret;
 }
@@ -618,6 +627,23 @@ static struct xenbus_watch *find_watch(const char *token)
        return NULL;
 }
 
+static void xs_reset_watches(void)
+{
+#ifdef MODULE
+       int err, supported = 0;
+
+       err = xenbus_scanf(XBT_NIL, "control",
+                          "platform-feature-xs_reset_watches", "%d",
+                          &supported);
+       if (err != 1 || !supported)
+               return;
+
+       err = xs_error(xs_single(XBT_NIL, XS_RESET_WATCHES, "", NULL));
+       if (err && err != -EEXIST)
+               printk(KERN_WARNING "xs_reset_watches failed: %d\n", err);
+#endif
+}
+
 /* Register callback to watch this node. */
 int register_xenbus_watch(struct xenbus_watch *watch)
 {
@@ -654,6 +680,10 @@ void unregister_xenbus_watch(struct xenbus_watch *watch)
        char token[sizeof(watch) * 2 + 1];
        int err;
 
+#if defined(CONFIG_XEN) || defined(MODULE)
+       BUG_ON(watch->flags & XBWF_new_thread);
+#endif
+
        sprintf(token, "%lX", (long)watch);
 
        down_read(&xs_state.watch_mutex);
@@ -665,9 +695,8 @@ void unregister_xenbus_watch(struct xenbus_watch *watch)
 
        err = xs_unwatch(watch->node, token);
        if (err)
-               printk(KERN_WARNING
-                      "XENBUS Failed to release watch %s: %i\n",
-                      watch->node, err);
+               pr_warning("XENBUS Failed to release watch %s: %i\n",
+                          watch->node, err);
 
        up_read(&xs_state.watch_mutex);
 
@@ -705,7 +734,9 @@ void xs_resume(void)
        struct xenbus_watch *watch;
        char token[sizeof(watch) * 2 + 1];
 
+#if !defined(CONFIG_XEN) && !defined(MODULE)
        xb_init_comms();
+#endif
 
        mutex_unlock(&xs_state.response_mutex);
        mutex_unlock(&xs_state.request_mutex);
@@ -728,11 +759,32 @@ void xs_suspend_cancel(void)
        mutex_unlock(&xs_state.transaction_mutex);
 }
 
+#if defined(CONFIG_XEN) || defined(MODULE)
+static int xenwatch_handle_callback(void *data)
+{
+       struct xs_stored_msg *msg = data;
+
+       msg->u.watch.handle->callback(msg->u.watch.handle,
+                                     (const char **)msg->u.watch.vec,
+                                     msg->u.watch.vec_size);
+
+       kfree(msg->u.watch.vec);
+       kfree(msg);
+
+       /* Kill this kthread if we were spawned just for this callback. */
+       if (current->pid != xenwatch_pid)
+               do_exit(0);
+
+       return 0;
+}
+#endif
+
 static int xenwatch_thread(void *unused)
 {
        struct list_head *ent;
        struct xs_stored_msg *msg;
 
+       current->flags |= PF_NOFREEZE;
        for (;;) {
                wait_event_interruptible(watch_events_waitq,
                                         !list_empty(&watch_events));
@@ -748,17 +800,39 @@ static int xenwatch_thread(void *unused)
                        list_del(ent);
                spin_unlock(&watch_events_lock);
 
-               if (ent != &watch_events) {
-                       msg = list_entry(ent, struct xs_stored_msg, list);
-                       msg->u.watch.handle->callback(
-                               msg->u.watch.handle,
-                               (const char **)msg->u.watch.vec,
-                               msg->u.watch.vec_size);
-                       kfree(msg->u.watch.vec);
-                       kfree(msg);
+               if (ent == &watch_events) {
+                       mutex_unlock(&xenwatch_mutex);
+                       continue;
                }
 
+               msg = list_entry(ent, struct xs_stored_msg, list);
+
+#if defined(CONFIG_XEN) || defined(MODULE)
+               /*
+                * Unlock the mutex before running an XBWF_new_thread
+                * handler. kthread_run can block which can deadlock
+                * against unregister_xenbus_watch() if we need to
+                * unregister other watches in order to make
+                * progress. This can occur on resume before the swap
+                * device is attached.
+                */
+               if (msg->u.watch.handle->flags & XBWF_new_thread) {
+                       mutex_unlock(&xenwatch_mutex);
+                       kthread_run(xenwatch_handle_callback,
+                                   msg, "xenwatch_cb");
+               } else {
+                       xenwatch_handle_callback(msg);
+                       mutex_unlock(&xenwatch_mutex);
+               }
+#else
+               msg->u.watch.handle->callback(
+                       msg->u.watch.handle,
+                       (const char **)msg->u.watch.vec,
+                       msg->u.watch.vec_size);
                mutex_unlock(&xenwatch_mutex);
+               kfree(msg->u.watch.vec);
+               kfree(msg);
+#endif
        }
 
        return 0;
@@ -858,11 +932,12 @@ static int xenbus_thread(void *unused)
 {
        int err;
 
+       current->flags |= PF_NOFREEZE;
        for (;;) {
                err = process_msg();
                if (err)
-                       printk(KERN_WARNING "XENBUS error %d while reading "
-                              "message\n", err);
+                       pr_warning("XENBUS error %d while reading "
+                                  "message\n", err);
                if (kthread_should_stop())
                        break;
        }
@@ -872,7 +947,6 @@ static int xenbus_thread(void *unused)
 
 int xs_init(void)
 {
-       int err;
        struct task_struct *task;
 
        INIT_LIST_HEAD(&xs_state.reply_list);
@@ -886,11 +960,6 @@ int xs_init(void)
        atomic_set(&xs_state.transaction_count, 0);
        init_waitqueue_head(&xs_state.transaction_wq);
 
-       /* Initialize the shared memory rings to talk to xenstored */
-       err = xb_init_comms();
-       if (err)
-               return err;
-
        task = kthread_run(xenwatch_thread, NULL, "xenwatch");
        if (IS_ERR(task))
                return PTR_ERR(task);
@@ -900,5 +969,8 @@ int xs_init(void)
        if (IS_ERR(task))
                return PTR_ERR(task);
 
+       /* shutdown watches for kexec boot */
+       xs_reset_watches();
+
        return 0;
 }