Sync usage with man page.
[netbsd-mini2440.git] / sys / arch / xen / xenbus / xenbus_xs.c
blob142353932c937fc6138e2c93532db181fdccc1bc
1 /* $NetBSD: xenbus_xs.c,v 1.17 2008/10/29 13:53:15 cegger Exp $ */
2 /******************************************************************************
3 * xenbus_xs.c
5 * This is the kernel equivalent of the "xs" library. We don't need everything
6 * and we use xenbus_comms for communication.
8 * Copyright (C) 2005 Rusty Russell, IBM Corporation
9 *
10 * This file may be distributed separately from the Linux kernel, or
11 * incorporated into other software packages, subject to the following license:
13 * Permission is hereby granted, free of charge, to any person obtaining a copy
14 * of this source file (the "Software"), to deal in the Software without
15 * restriction, including without limitation the rights to use, copy, modify,
16 * merge, publish, distribute, sublicense, and/or sell copies of the Software,
17 * and to permit persons to whom the Software is furnished to do so, subject to
18 * the following conditions:
20 * The above copyright notice and this permission notice shall be included in
21 * all copies or substantial portions of the Software.
23 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
29 * IN THE SOFTWARE.
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: xenbus_xs.c,v 1.17 2008/10/29 13:53:15 cegger Exp $");
35 #if 0
36 #define DPRINTK(fmt, args...) \
37 printf("xenbus_xs (%s:%d) " fmt ".\n", __func__, __LINE__, ##args)
38 #else
39 #define DPRINTK(fmt, args...) ((void)0)
40 #endif
42 #include <sys/types.h>
43 #include <sys/null.h>
44 #include <sys/errno.h>
45 #include <sys/malloc.h>
46 #include <sys/systm.h>
47 #include <sys/param.h>
48 #include <sys/proc.h>
49 #include <sys/mutex.h>
50 #include <sys/kthread.h>
51 #include <sys/simplelock.h>
53 #include <machine/stdarg.h>
55 #include <xen/xen.h> /* for xendomain_is_dom0() */
56 #include <xen/xenbus.h>
57 #include "xenbus_comms.h"
59 #define streq(a, b) (strcmp((a), (b)) == 0)
61 struct xs_stored_msg {
62 SIMPLEQ_ENTRY(xs_stored_msg) msg_next;
64 struct xsd_sockmsg hdr;
66 union {
67 /* Queued replies. */
68 struct {
69 char *body;
70 } reply;
72 /* Queued watch events. */
73 struct {
74 struct xenbus_watch *handle;
75 char **vec;
76 unsigned int vec_size;
77 } watch;
78 } u;
81 struct xs_handle {
82 /* A list of replies. Currently only one will ever be outstanding. */
83 SIMPLEQ_HEAD(, xs_stored_msg) reply_list;
84 struct simplelock reply_lock;
85 kmutex_t xs_lock; /* serialize access to xenstore */
86 int suspend_spl;
90 static struct xs_handle xs_state;
92 /* List of registered watches, and a lock to protect it. */
93 static SLIST_HEAD(, xenbus_watch) watches =
94 SLIST_HEAD_INITIALIZER(watches);
95 static struct simplelock watches_lock = SIMPLELOCK_INITIALIZER;
97 /* List of pending watch callback events, and a lock to protect it. */
98 static SIMPLEQ_HEAD(, xs_stored_msg) watch_events =
99 SIMPLEQ_HEAD_INITIALIZER(watch_events);
100 static struct simplelock watch_events_lock = SIMPLELOCK_INITIALIZER;
102 static int
103 get_error(const char *errorstring)
105 unsigned int i;
107 for (i = 0; !streq(errorstring, xsd_errors[i].errstring); i++) {
108 if (i == (sizeof(xsd_errors) / sizeof(xsd_errors[0]) - 1)) {
109 printf(
110 "XENBUS xen store gave: unknown error %s",
111 errorstring);
112 return EINVAL;
115 return xsd_errors[i].errnum;
118 static void *
119 read_reply(enum xsd_sockmsg_type *type, unsigned int *len)
121 struct xs_stored_msg *msg;
122 char *body;
123 int s;
125 simple_lock(&xs_state.reply_lock);
126 s = spltty();
128 while (SIMPLEQ_EMPTY(&xs_state.reply_list)) {
129 ltsleep(&xs_state.reply_list, PRIBIO, "rplq", 0,
130 &xs_state.reply_lock);
133 msg = SIMPLEQ_FIRST(&xs_state.reply_list);
134 SIMPLEQ_REMOVE_HEAD(&xs_state.reply_list, msg_next);
136 splx(s);
137 simple_unlock(&xs_state.reply_lock);
139 *type = msg->hdr.type;
140 if (len)
141 *len = msg->hdr.len;
142 body = msg->u.reply.body;
143 DPRINTK("read_reply: type %d body %s",
144 msg->hdr.type, body);
146 free(msg, M_DEVBUF);
148 return body;
151 #if 0
152 /* Emergency write. */
153 void
154 xenbus_debug_write(const char *str, unsigned int count)
156 struct xsd_sockmsg msg = { 0 };
158 msg.type = XS_DEBUG;
159 msg.len = sizeof("print") + count + 1;
161 xb_write(&msg, sizeof(msg));
162 xb_write("print", sizeof("print"));
163 xb_write(str, count);
164 xb_write("", 1);
166 #endif
169 xenbus_dev_request_and_reply(struct xsd_sockmsg *msg, void**reply)
171 int err = 0, s;
173 s = spltty();
174 mutex_enter(&xs_state.xs_lock);
175 err = xb_write(msg, sizeof(*msg) + msg->len);
176 if (err) {
177 msg->type = XS_ERROR;
178 *reply = NULL;
179 } else {
180 *reply = read_reply(&msg->type, &msg->len);
182 mutex_exit(&xs_state.xs_lock);
183 splx(s);
185 return err;
188 /* Send message to xs, get kmalloc'ed reply. ERR_PTR() on error. */
189 static int
190 xs_talkv(struct xenbus_transaction *t,
191 enum xsd_sockmsg_type type,
192 const struct iovec *iovec,
193 unsigned int num_vecs,
194 unsigned int *len,
195 char **retbuf)
197 struct xsd_sockmsg msg;
198 unsigned int i;
199 int err, s;
200 void *ret;
202 msg.tx_id = (uint32_t)(unsigned long)t;
203 msg.req_id = 0;
204 msg.type = type;
205 msg.len = 0;
206 for (i = 0; i < num_vecs; i++)
207 msg.len += iovec[i].iov_len;
209 s = spltty();
210 mutex_enter(&xs_state.xs_lock);
212 DPRINTK("write msg");
213 err = xb_write(&msg, sizeof(msg));
214 DPRINTK("write msg err %d", err);
215 if (err) {
216 mutex_exit(&xs_state.xs_lock);
217 splx(s);
218 return (err);
221 for (i = 0; i < num_vecs; i++) {
222 DPRINTK("write iovect");
223 err = xb_write(iovec[i].iov_base, iovec[i].iov_len);
224 DPRINTK("write iovect err %d", err);
225 if (err) {
226 mutex_exit(&xs_state.xs_lock);
227 splx(s);
228 return (err);
232 DPRINTK("read");
233 ret = read_reply(&msg.type, len);
234 DPRINTK("read done");
236 mutex_exit(&xs_state.xs_lock);
237 splx(s);
239 if (msg.type == XS_ERROR) {
240 err = get_error(ret);
241 free(ret, M_DEVBUF);
242 return (err);
245 KASSERT(msg.type == type);
246 if (retbuf != NULL)
247 *retbuf = ret;
248 else
249 free(ret, M_DEVBUF);
250 return 0;
253 /* Simplified version of xs_talkv: single message. */
254 static int
255 xs_single(struct xenbus_transaction *t,
256 enum xsd_sockmsg_type type,
257 const char *string,
258 unsigned int *len,
259 char **ret)
261 struct iovec iovec;
263 /* xs_talkv only reads iovec */
264 iovec.iov_base = __UNCONST(string);
265 iovec.iov_len = strlen(string) + 1;
266 return xs_talkv(t, type, &iovec, 1, len, ret);
269 static unsigned int
270 count_strings(const char *strings, unsigned int len)
272 unsigned int num;
273 const char *p;
275 for (p = strings, num = 0; p < strings + len; p += strlen(p) + 1)
276 num++;
278 return num;
281 /* Return the path to dir with /name appended. Buffer must be kfree()'ed. */
282 static char *
283 join(const char *dir, const char *name)
285 char *buffer;
287 buffer = malloc(strlen(dir) + strlen("/") + strlen(name) + 1,
288 M_DEVBUF, M_NOWAIT);
289 if (buffer == NULL)
290 return NULL;
292 strcpy(buffer, dir);
293 if (!streq(name, "")) {
294 strcat(buffer, "/");
295 strcat(buffer, name);
298 return buffer;
301 static char **
302 split(char *strings, unsigned int len, unsigned int *num)
304 char *p, **ret;
306 /* Count the strings. */
307 *num = count_strings(strings, len);
309 /* Transfer to one big alloc for easy freeing. */
310 ret = malloc(*num * sizeof(char *) + len, M_DEVBUF, M_NOWAIT);
311 if (!ret) {
312 free(strings, M_DEVBUF);
313 return NULL;
315 memcpy(&ret[*num], strings, len);
316 free(strings, M_DEVBUF);
318 strings = (char *)&ret[*num];
319 for (p = strings, *num = 0; p < strings + len; p += strlen(p) + 1)
320 ret[(*num)++] = p;
322 return ret;
326 xenbus_directory(struct xenbus_transaction *t,
327 const char *dir, const char *node, unsigned int *num,
328 char ***retbuf)
330 char *strings, *path;
331 unsigned int len;
332 int err;
334 path = join(dir, node);
335 if (path == NULL)
336 return ENOMEM;
338 err = xs_single(t, XS_DIRECTORY, path, &len, &strings);
339 DPRINTK("xs_single %d %d", err, len);
340 free(path, M_DEVBUF);
341 if (err)
342 return err;
344 DPRINTK("xs_single strings %s", strings);
345 *retbuf = split(strings, len, num);
346 if (*retbuf == NULL)
347 return ENOMEM;
348 return 0;
351 /* Check if a path exists. Return 1 if it does. */
353 xenbus_exists(struct xenbus_transaction *t,
354 const char *dir, const char *node)
356 char **d;
357 int dir_n, err;
359 err = xenbus_directory(t, dir, node, &dir_n, &d);
360 if (err)
361 return 0;
362 free(d, M_DEVBUF);
363 return 1;
366 /* Get the value of a single file.
367 * Returns a kmalloced value: call free() on it after use.
368 * len indicates length in bytes.
371 xenbus_read(struct xenbus_transaction *t,
372 const char *dir, const char *node, unsigned int *len,
373 char **ret)
375 char *path;
376 int err;
378 path = join(dir, node);
379 if (path == NULL)
380 return ENOMEM;
382 err = xs_single(t, XS_READ, path, len, ret);
383 free(path, M_DEVBUF);
384 return err;
387 /* Read a node and convert it to unsigned long. */
389 xenbus_read_ul(struct xenbus_transaction *t,
390 const char *dir, const char *node, unsigned long *val,
391 int base)
393 char *string, *ep;
394 int err;
396 err = xenbus_read(t, dir, node, NULL, &string);
397 if (err)
398 return err;
399 *val = strtoul(string, &ep, base);
400 if (*ep != '\0') {
401 free(string, M_DEVBUF);
402 return EFTYPE;
404 free(string, M_DEVBUF);
405 return 0;
408 /* Read a node and convert it to unsigned long long. */
410 xenbus_read_ull(struct xenbus_transaction *t,
411 const char *dir, const char *node, unsigned long long *val,
412 int base)
414 char *string, *ep;
415 int err;
417 err = xenbus_read(t, dir, node, NULL, &string);
418 if (err)
419 return err;
420 *val = strtoull(string, &ep, base);
421 if (*ep != '\0') {
422 free(string, M_DEVBUF);
423 return EFTYPE;
425 free(string, M_DEVBUF);
426 return 0;
429 /* Write the value of a single file.
430 * Returns -err on failure.
433 xenbus_write(struct xenbus_transaction *t,
434 const char *dir, const char *node, const char *string)
436 const char *path;
437 struct iovec iovec[2];
438 int ret;
440 path = join(dir, node);
441 if (path == NULL)
442 return ENOMEM;
444 /* xs_talkv only reads iovec */
445 iovec[0].iov_base = __UNCONST(path);
446 iovec[0].iov_len = strlen(path) + 1;
447 iovec[1].iov_base = __UNCONST(string);
448 iovec[1].iov_len = strlen(string);
450 ret = xs_talkv(t, XS_WRITE, iovec, 2, NULL, NULL);
451 return ret;
454 /* Create a new directory. */
456 xenbus_mkdir(struct xenbus_transaction *t,
457 const char *dir, const char *node)
459 char *path;
460 int ret;
462 path = join(dir, node);
463 if (path == NULL)
464 return ENOMEM;
466 ret = xs_single(t, XS_MKDIR, path, NULL, NULL);
467 free(path, M_DEVBUF);
468 return ret;
471 /* Destroy a file or directory (directories must be empty). */
472 int xenbus_rm(struct xenbus_transaction *t, const char *dir, const char *node)
474 char *path;
475 int ret;
477 path = join(dir, node);
478 if (path == NULL)
479 return ENOMEM;
481 ret = xs_single(t, XS_RM, path, NULL, NULL);
482 free(path, M_DEVBUF);
483 return ret;
486 /* Start a transaction: changes by others will not be seen during this
487 * transaction, and changes will not be visible to others until end.
488 * MUST BE CALLED AT IPL_TTY !
490 struct xenbus_transaction *
491 xenbus_transaction_start(void)
493 char *id_str;
494 unsigned long id, err;
496 err = xs_single(NULL, XS_TRANSACTION_START, "", NULL, &id_str);
497 if (err) {
498 return NULL;
501 id = strtoul(id_str, NULL, 0);
502 free(id_str, M_DEVBUF);
504 return (struct xenbus_transaction *)id;
507 /* End a transaction.
508 * If abandon is true, transaction is discarded instead of committed.
509 * MUST BE CALLED AT IPL_TTY !
511 int xenbus_transaction_end(struct xenbus_transaction *t, int abort)
513 char abortstr[2];
514 int err;
516 if (abort)
517 strcpy(abortstr, "F");
518 else
519 strcpy(abortstr, "T");
521 err = xs_single(t, XS_TRANSACTION_END, abortstr, NULL, NULL);
523 return err;
526 /* Single read and scanf: returns -errno or num scanned. */
528 xenbus_scanf(struct xenbus_transaction *t,
529 const char *dir, const char *node, const char *fmt, ...)
531 va_list ap;
532 int ret;
533 char *val;
535 ret = xenbus_read(t, dir, node, NULL, &val);
536 if (ret)
537 return ret;
539 va_start(ap, fmt);
540 //ret = vsscanf(val, fmt, ap);
541 ret = ENXIO;
542 printf("xb_scanf format %s in %s\n", fmt, val);
543 va_end(ap);
544 free(val, M_DEVBUF);
545 return ret;
548 /* Single printf and write: returns -errno or 0. */
550 xenbus_printf(struct xenbus_transaction *t,
551 const char *dir, const char *node, const char *fmt, ...)
553 va_list ap;
554 int ret;
555 #define PRINTF_BUFFER_SIZE 4096
556 char *printf_buffer;
558 printf_buffer = malloc(PRINTF_BUFFER_SIZE, M_DEVBUF, M_NOWAIT);
559 if (printf_buffer == NULL)
560 return ENOMEM;
562 va_start(ap, fmt);
563 ret = vsnprintf(printf_buffer, PRINTF_BUFFER_SIZE, fmt, ap);
564 va_end(ap);
566 KASSERT(ret < PRINTF_BUFFER_SIZE);
567 ret = xenbus_write(t, dir, node, printf_buffer);
569 free(printf_buffer, M_DEVBUF);
571 return ret;
574 /* Takes tuples of names, scanf-style args, and void **, NULL terminated. */
576 xenbus_gather(struct xenbus_transaction *t, const char *dir, ...)
578 va_list ap;
579 const char *name;
580 int ret = 0;
582 va_start(ap, dir);
583 while (ret == 0 && (name = va_arg(ap, char *)) != NULL) {
584 const char *fmt = va_arg(ap, char *);
585 void *result = va_arg(ap, void *);
586 char *p;
588 ret = xenbus_read(t, dir, name, NULL, &p);
589 if (ret)
590 break;
591 if (fmt) {
592 // XXX if (sscanf(p, fmt, result) == 0)
593 ret = -EINVAL;
594 free(p, M_DEVBUF);
595 } else
596 *(char **)result = p;
598 va_end(ap);
599 return ret;
602 static int
603 xs_watch(const char *path, const char *token)
605 struct iovec iov[2];
607 /* xs_talkv only reads iovec */
608 iov[0].iov_base = __UNCONST(path);
609 iov[0].iov_len = strlen(path) + 1;
610 iov[1].iov_base = __UNCONST(token);
611 iov[1].iov_len = strlen(token) + 1;
613 return xs_talkv(NULL, XS_WATCH, iov, 2, NULL, NULL);
616 static int
617 xs_unwatch(const char *path, const char *token)
619 struct iovec iov[2];
621 /* xs_talkv only reads iovec */
622 iov[0].iov_base = __UNCONST(path);
623 iov[0].iov_len = strlen(path) + 1;
624 iov[1].iov_base = __UNCONST(token);
625 iov[1].iov_len = strlen(token) + 1;
627 return xs_talkv(NULL, XS_UNWATCH, iov, 2, NULL, NULL);
630 static struct xenbus_watch *
631 find_watch(const char *token)
633 struct xenbus_watch *i, *cmp;
635 cmp = (void *)strtoul(token, NULL, 16);
637 SLIST_FOREACH(i, &watches, watch_next) {
638 if (i == cmp)
639 return i;
642 return NULL;
645 /* Register callback to watch this node. */
647 register_xenbus_watch(struct xenbus_watch *watch)
649 /* Pointer in ascii is the token. */
650 char token[sizeof(watch) * 2 + 1];
651 int err;
652 int s;
654 snprintf(token, sizeof(token), "%lX", (long)watch);
656 s = spltty();
658 simple_lock(&watches_lock);
659 KASSERT(find_watch(token) == 0);
660 SLIST_INSERT_HEAD(&watches, watch, watch_next);
661 simple_unlock(&watches_lock);
663 err = xs_watch(watch->node, token);
665 /* Ignore errors due to multiple registration. */
666 if ((err != 0) && (err != EEXIST)) {
667 simple_lock(&watches_lock);
668 SLIST_REMOVE(&watches, watch, xenbus_watch, watch_next);
669 simple_unlock(&watches_lock);
672 splx(s);
674 return err;
677 void
678 unregister_xenbus_watch(struct xenbus_watch *watch)
680 struct xs_stored_msg *msg, *next_msg;
681 char token[sizeof(watch) * 2 + 1];
682 int err, s;
684 snprintf(token, sizeof(token), "%lX", (long)watch);
686 s = spltty();
688 simple_lock(&watches_lock);
689 KASSERT(find_watch(token));
690 SLIST_REMOVE(&watches, watch, xenbus_watch, watch_next);
691 simple_unlock(&watches_lock);
693 err = xs_unwatch(watch->node, token);
694 if (err)
695 printf(
696 "XENBUS Failed to release watch %s: %i\n",
697 watch->node, err);
699 splx(s);
701 /* Cancel pending watch events. */
702 simple_lock(&watch_events_lock);
703 for (msg = SIMPLEQ_FIRST(&watch_events); msg != NULL; msg = next_msg) {
704 next_msg = SIMPLEQ_NEXT(msg, msg_next);
705 if (msg->u.watch.handle != watch)
706 continue;
707 SIMPLEQ_REMOVE(&watch_events, msg, xs_stored_msg, msg_next);
708 free(msg->u.watch.vec, M_DEVBUF);
709 free(msg, M_DEVBUF);
711 simple_unlock(&watch_events_lock);
715 void
716 xs_suspend(void)
718 xs_state.suspend_spl = spltty();
721 void
722 xs_resume(void)
724 struct xenbus_watch *watch;
725 char token[sizeof(watch) * 2 + 1];
726 /* No need for watches_lock: the suspend_mutex is sufficient. */
727 SLIST_FOREACH(watch, &watches, watch_next) {
728 snprintf(token, sizeof(token), "%lX", (long)watch);
729 xs_watch(watch->node, token);
732 splx(xs_state.suspend_spl);
735 static void
736 xenwatch_thread(void *unused)
738 struct xs_stored_msg *msg;
739 int s;
741 for (;;) {
742 tsleep(&watch_events, PRIBIO, "evtsq", 0);
743 s = spltty(); /* to block IPL_CTRL */
744 while (!SIMPLEQ_EMPTY(&watch_events)) {
745 msg = SIMPLEQ_FIRST(&watch_events);
747 simple_lock(&watch_events_lock);
748 SIMPLEQ_REMOVE_HEAD(&watch_events, msg_next);
749 simple_unlock(&watch_events_lock);
750 DPRINTK("xenwatch_thread: got event");
752 msg->u.watch.handle->xbw_callback(
753 msg->u.watch.handle,
754 (const char **)msg->u.watch.vec,
755 msg->u.watch.vec_size);
756 free(msg->u.watch.vec, M_DEVBUF);
757 free(msg, M_DEVBUF);
760 splx(s);
764 static int
765 process_msg(void)
767 struct xs_stored_msg *msg;
768 char *body;
769 int err, s;
771 msg = malloc(sizeof(*msg), M_DEVBUF, M_NOWAIT);
772 if (msg == NULL)
773 return ENOMEM;
775 err = xb_read(&msg->hdr, sizeof(msg->hdr));
776 DPRINTK("xb_read hdr %d", err);
777 if (err) {
778 free(msg, M_DEVBUF);
779 return err;
782 body = malloc(msg->hdr.len + 1, M_DEVBUF, M_NOWAIT);
783 if (body == NULL) {
784 free(msg, M_DEVBUF);
785 return ENOMEM;
788 err = xb_read(body, msg->hdr.len);
789 DPRINTK("xb_read body %d", err);
790 if (err) {
791 free(body, M_DEVBUF);
792 free(msg, M_DEVBUF);
793 return err;
795 body[msg->hdr.len] = '\0';
797 if (msg->hdr.type == XS_WATCH_EVENT) {
798 DPRINTK("process_msg: XS_WATCH_EVENT");
799 msg->u.watch.vec = split(body, msg->hdr.len,
800 &msg->u.watch.vec_size);
801 if (msg->u.watch.vec == NULL) {
802 free(msg, M_DEVBUF);
803 return ENOMEM;
806 simple_lock(&watches_lock);
807 s = spltty();
808 msg->u.watch.handle = find_watch(
809 msg->u.watch.vec[XS_WATCH_TOKEN]);
810 if (msg->u.watch.handle != NULL) {
811 simple_lock(&watch_events_lock);
812 SIMPLEQ_INSERT_TAIL(&watch_events, msg, msg_next);
813 wakeup(&watch_events);
814 simple_unlock(&watch_events_lock);
815 } else {
816 free(msg->u.watch.vec, M_DEVBUF);
817 free(msg, M_DEVBUF);
819 splx(s);
820 simple_unlock(&watches_lock);
821 } else {
822 DPRINTK("process_msg: type %d body %s", msg->hdr.type, body);
824 msg->u.reply.body = body;
825 simple_lock(&xs_state.reply_lock);
826 s = spltty();
827 SIMPLEQ_INSERT_TAIL(&xs_state.reply_list, msg, msg_next);
828 splx(s);
829 simple_unlock(&xs_state.reply_lock);
830 wakeup(&xs_state.reply_list);
833 return 0;
836 static void
837 xenbus_thread(void *unused)
839 int err;
841 for (;;) {
842 err = process_msg();
843 if (err)
844 printk("XENBUS error %d while reading message\n", err);
849 xs_init(device_t dev)
851 int err;
853 SIMPLEQ_INIT(&xs_state.reply_list);
854 simple_lock_init(&xs_state.reply_lock);
855 mutex_init(&xs_state.xs_lock, MUTEX_DEFAULT, IPL_NONE);
857 err = kthread_create(PRI_NONE, 0, NULL, xenwatch_thread,
858 NULL, NULL, "xenwatch");
859 if (err) {
860 aprint_error_dev(dev, "kthread_create(xenwatch): %d\n", err);
861 return err;
864 err = kthread_create(PRI_NONE, 0, NULL, xenbus_thread,
865 NULL, NULL, "xenbus");
866 if (err) {
867 aprint_error_dev(dev, "kthread_create(xenbus): %d\n", err);
868 return err;
871 return 0;
875 * Local variables:
876 * c-file-style: "linux"
877 * indent-tabs-mode: t
878 * c-indent-level: 8
879 * c-basic-offset: 8
880 * tab-width: 8
881 * End: