xref: /netbsd/sys/arch/xen/xenbus/xenbus_xs.c (revision 1fc12f99)
1 /* $NetBSD: xenbus_xs.c,v 1.28 2022/09/01 16:25:18 bouyer Exp $ */
2 /******************************************************************************
3  * xenbus_xs.c
4  *
5  * This is the kernel equivalent of the "xs" library.  We don't need everything
6  * and we use xenbus_comms for communication.
7  *
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:
12  *
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:
19  *
20  * The above copyright notice and this permission notice shall be included in
21  * all copies or substantial portions of the Software.
22  *
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.
30  */
31 
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: xenbus_xs.c,v 1.28 2022/09/01 16:25:18 bouyer Exp $");
34 
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
41 
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 
52 #include <xen/xen.h>	/* for xendomain_is_dom0() */
53 #include <xen/xenbus.h>
54 #include "xenbus_comms.h"
55 
56 #define streq(a, b) (strcmp((a), (b)) == 0)
57 
58 struct xs_stored_msg {
59 	SIMPLEQ_ENTRY(xs_stored_msg) msg_next;
60 
61 	struct xsd_sockmsg hdr;
62 
63 	union {
64 		/* Queued replies. */
65 		struct {
66 			char *body;
67 		} reply;
68 
69 		/* Queued watch events. */
70 		struct {
71 			struct xenbus_watch *handle;
72 			char **vec;
73 			unsigned int vec_size;
74 		} watch;
75 	} u;
76 };
77 
78 struct xs_handle {
79 	/* A list of replies. Currently only one will ever be outstanding. */
80 	SIMPLEQ_HEAD(, xs_stored_msg) reply_list;
81 	kmutex_t reply_lock;
82 	kcondvar_t reply_cv;
83 	kmutex_t xs_lock; /* serialize access to xenstore */
84 	int suspend_spl;
85 
86 };
87 
88 static struct xs_handle xs_state;
89 
90 /* List of registered watches, and a lock to protect it. */
91 static SLIST_HEAD(, xenbus_watch) watches;
92 static kmutex_t watches_lock;
93 
94 /* List of pending watch callback events, and a lock to protect it. */
95 static SIMPLEQ_HEAD(, xs_stored_msg) watch_events;
96 static kmutex_t watch_events_lock;
97 static kcondvar_t watch_cv;
98 
99 static int
get_error(const char * errorstring)100 get_error(const char *errorstring)
101 {
102 	unsigned int i;
103 
104 	for (i = 0; !streq(errorstring, xsd_errors[i].errstring); i++) {
105 		if (i == (sizeof(xsd_errors) / sizeof(xsd_errors[0]) - 1)) {
106 			printf(
107 			       "XENBUS xen store gave: unknown error %s",
108 			       errorstring);
109 			return EINVAL;
110 		}
111 	}
112 	return xsd_errors[i].errnum;
113 }
114 
115 static void *
read_reply(enum xsd_sockmsg_type * type,unsigned int * len)116 read_reply(enum xsd_sockmsg_type *type, unsigned int *len)
117 {
118 	struct xs_stored_msg *msg;
119 	char *body;
120 
121 	mutex_enter(&xs_state.reply_lock);
122 	while (SIMPLEQ_EMPTY(&xs_state.reply_list)) {
123 		cv_wait(&xs_state.reply_cv, &xs_state.reply_lock);
124 	}
125 	msg = SIMPLEQ_FIRST(&xs_state.reply_list);
126 	SIMPLEQ_REMOVE_HEAD(&xs_state.reply_list, msg_next);
127 	mutex_exit(&xs_state.reply_lock);
128 
129 	*type = msg->hdr.type;
130 	if (len)
131 		*len = msg->hdr.len;
132 	body = msg->u.reply.body;
133 	DPRINTK("read_reply: type %d body %s",
134 	    msg->hdr.type, body);
135 
136 	free(msg, M_DEVBUF);
137 
138 	return body;
139 }
140 
141 #if 0
142 /* Emergency write. */
143 void
144 xenbus_debug_write(const char *str, unsigned int count)
145 {
146 	struct xsd_sockmsg msg = { 0 };
147 
148 	msg.type = XS_DEBUG;
149 	msg.len = sizeof("print") + count + 1;
150 
151 	xb_write(&msg, sizeof(msg));
152 	xb_write("print", sizeof("print"));
153 	xb_write(str, count);
154 	xb_write("", 1);
155 }
156 #endif
157 
158 int
xenbus_dev_request_and_reply(struct xsd_sockmsg * msg,void ** reply)159 xenbus_dev_request_and_reply(struct xsd_sockmsg *msg, void**reply)
160 {
161 	int err = 0;
162 
163 	mutex_enter(&xs_state.xs_lock);
164 	err = xb_write(msg, sizeof(*msg) + msg->len);
165 	if (err) {
166 		msg->type = XS_ERROR;
167 		*reply = NULL;
168 	} else {
169 		*reply = read_reply(&msg->type, &msg->len);
170 	}
171 	mutex_exit(&xs_state.xs_lock);
172 
173 	return err;
174 }
175 
176 void
xenbus_dev_reply_free(struct xsd_sockmsg * msg,void * reply)177 xenbus_dev_reply_free(struct xsd_sockmsg *msg, void *reply)
178 {
179 	free(reply, M_DEVBUF);
180 }
181 
182 /* Send message to xs, get kmalloc'ed reply.  ERR_PTR() on error. */
183 static int
xs_talkv(struct xenbus_transaction * t,enum xsd_sockmsg_type type,const struct iovec * iovec,unsigned int num_vecs,unsigned int * len,char ** retbuf)184 xs_talkv(struct xenbus_transaction *t,
185 		      enum xsd_sockmsg_type type,
186 		      const struct iovec *iovec,
187 		      unsigned int num_vecs,
188 		      unsigned int *len,
189 		      char **retbuf)
190 {
191 	struct xsd_sockmsg msg;
192 	unsigned int i;
193 	int err;
194 	void *ret;
195 
196 	msg.tx_id = (uint32_t)(unsigned long)t;
197 	msg.req_id = 0;
198 	msg.type = type;
199 	msg.len = 0;
200 	for (i = 0; i < num_vecs; i++)
201 		msg.len += iovec[i].iov_len;
202 
203 	mutex_enter(&xs_state.xs_lock);
204 
205 	DPRINTK("write msg");
206 	err = xb_write(&msg, sizeof(msg));
207 	DPRINTK("write msg err %d", err);
208 	if (err) {
209 		mutex_exit(&xs_state.xs_lock);
210 		return (err);
211 	}
212 
213 	for (i = 0; i < num_vecs; i++) {
214 		DPRINTK("write iovect");
215 		err = xb_write(iovec[i].iov_base, iovec[i].iov_len);
216 		DPRINTK("write iovect err %d", err);
217 		if (err) {
218 			mutex_exit(&xs_state.xs_lock);
219 			return (err);
220 		}
221 	}
222 
223 	DPRINTK("read");
224 	ret = read_reply(&msg.type, len);
225 	DPRINTK("read done");
226 
227 	mutex_exit(&xs_state.xs_lock);
228 
229 	if (msg.type == XS_ERROR) {
230 		err = get_error(ret);
231 		free(ret, M_DEVBUF);
232 		return (err);
233 	}
234 
235 	KASSERT(msg.type == type);
236 	if (retbuf != NULL)
237 		*retbuf = ret;
238 	else
239 		free(ret, M_DEVBUF);
240 	return 0;
241 }
242 
243 /* Simplified version of xs_talkv: single message. */
244 static int
xs_single(struct xenbus_transaction * t,enum xsd_sockmsg_type type,const char * string,unsigned int * len,char ** ret)245 xs_single(struct xenbus_transaction *t,
246 		       enum xsd_sockmsg_type type,
247 		       const char *string,
248 		       unsigned int *len,
249 		       char **ret)
250 {
251 	struct iovec iovec;
252 
253 	/* xs_talkv only reads iovec */
254 	iovec.iov_base = __UNCONST(string);
255 	iovec.iov_len = strlen(string) + 1;
256 	return xs_talkv(t, type, &iovec, 1, len, ret);
257 }
258 
259 static unsigned int
count_strings(const char * strings,unsigned int len)260 count_strings(const char *strings, unsigned int len)
261 {
262 	unsigned int num;
263 	const char *p;
264 
265 	for (p = strings, num = 0; p < strings + len; p += strlen(p) + 1)
266 		num++;
267 
268 	return num;
269 }
270 
271 /* Return the path to dir with /name appended. Buffer must be kfree()'ed. */
272 static char *
join(const char * dir,const char * name)273 join(const char *dir, const char *name)
274 {
275 	char *buffer;
276 
277 	buffer = malloc(strlen(dir) + strlen("/") + strlen(name) + 1,
278 			 M_DEVBUF, M_NOWAIT);
279 	if (buffer == NULL)
280 		return NULL;
281 
282 	strcpy(buffer, dir);
283 	if (!streq(name, "")) {
284 		strcat(buffer, "/");
285 		strcat(buffer, name);
286 	}
287 
288 	return buffer;
289 }
290 
291 static char **
split(char * strings,unsigned int len,unsigned int * num)292 split(char *strings, unsigned int len, unsigned int *num)
293 {
294 	char *p, **ret;
295 
296 	/* Count the strings. */
297 	*num = count_strings(strings, len);
298 
299 	/* Transfer to one big alloc for easy freeing. */
300 	ret = malloc(*num * sizeof(char *) + len, M_DEVBUF, M_NOWAIT);
301 	if (!ret) {
302 		free(strings, M_DEVBUF);
303 		return NULL;
304 	}
305 	memcpy(&ret[*num], strings, len);
306 	free(strings, M_DEVBUF);
307 
308 	strings = (char *)&ret[*num];
309 	for (p = strings, *num = 0; p < strings + len; p += strlen(p) + 1)
310 		ret[(*num)++] = p;
311 
312 	return ret;
313 }
314 
315 int
xenbus_directory(struct xenbus_transaction * t,const char * dir,const char * node,unsigned int * num,char *** retbuf)316 xenbus_directory(struct xenbus_transaction *t,
317 			const char *dir, const char *node, unsigned int *num,
318 			char ***retbuf)
319 {
320 	char *strings, *path;
321 	unsigned int len;
322 	int err;
323 
324 	path = join(dir, node);
325 	if (path == NULL)
326 		return ENOMEM;
327 
328 	err = xs_single(t, XS_DIRECTORY, path, &len, &strings);
329 	DPRINTK("xs_single %d %d", err, len);
330 	free(path, M_DEVBUF);
331 	if (err)
332 		return err;
333 
334 	DPRINTK("xs_single strings %s", strings);
335 	*retbuf = split(strings, len, num);
336 	if (*retbuf == NULL)
337 		return ENOMEM;
338 	return 0;
339 }
340 
341 void
xenbus_directory_free(unsigned int num,char ** dir)342 xenbus_directory_free(unsigned int num, char **dir)
343 {
344 	free(dir, M_DEVBUF);
345 }
346 
347 /* Check if a path exists. Return 1 if it does. */
348 int
xenbus_exists(struct xenbus_transaction * t,const char * dir,const char * node)349 xenbus_exists(struct xenbus_transaction *t,
350 		  const char *dir, const char *node)
351 {
352 	char **d;
353 	int dir_n, err;
354 
355 	err = xenbus_directory(t, dir, node, &dir_n, &d);
356 	if (err)
357 		return 0;
358 	free(d, M_DEVBUF);
359 	return 1;
360 }
361 
362 /* Get the value of a single file.
363  * Returns a kmalloced value: call free() on it after use.
364  * len indicates length in bytes.
365  */
366 int
xenbus_read(struct xenbus_transaction * t,const char * dir,const char * node,char * buffer,size_t bufsz)367 xenbus_read(struct xenbus_transaction *t,
368 		  const char *dir, const char *node,
369 		  char *buffer, size_t bufsz)
370 {
371 	char *path;
372 	int err;
373 	char *ret;
374 	unsigned int len;
375 
376 	path = join(dir, node);
377 	if (path == NULL)
378 		return ENOMEM;
379 
380 	err = xs_single(t, XS_READ, path, &len, &ret);
381 
382 	if (err == 0) {
383 		if (len + 1 <= bufsz) {
384 			strncpy(buffer, ret, bufsz);
385 		} else {
386 			err = ENAMETOOLONG;
387 		}
388 		free(ret, M_DEVBUF);
389 	}
390 
391 	free(path, M_DEVBUF);
392 	return err;
393 }
394 
395 /* Read a node and convert it to unsigned long. */
396 int
xenbus_read_ul(struct xenbus_transaction * t,const char * dir,const char * node,unsigned long * val,int base)397 xenbus_read_ul(struct xenbus_transaction *t,
398 		  const char *dir, const char *node, unsigned long *val,
399 		  int base)
400 {
401 	char string[32], *ep;
402 	int err;
403 
404 	err = xenbus_read(t, dir, node, string, sizeof(string));
405 	if (err)
406 		return err;
407 	*val = strtoul(string, &ep, base);
408 	if (*ep != '\0') {
409 		return EFTYPE;
410 	}
411 	return 0;
412 }
413 
414 /* Read a node and convert it to unsigned long long. */
415 int
xenbus_read_ull(struct xenbus_transaction * t,const char * dir,const char * node,unsigned long long * val,int base)416 xenbus_read_ull(struct xenbus_transaction *t,
417 		  const char *dir, const char *node, unsigned long long *val,
418 		  int base)
419 {
420 	char string[32], *ep;
421 	int err;
422 
423 	err = xenbus_read(t, dir, node, string, sizeof(string));
424 	if (err)
425 		return err;
426 	*val = strtoull(string, &ep, base);
427 	if (*ep != '\0') {
428 		return EFTYPE;
429 	}
430 	return 0;
431 }
432 
433 /* Write the value of a single file.
434  * Returns -err on failure.
435  */
436 int
xenbus_write(struct xenbus_transaction * t,const char * dir,const char * node,const char * string)437 xenbus_write(struct xenbus_transaction *t,
438 		 const char *dir, const char *node, const char *string)
439 {
440 	const char *path;
441 	struct iovec iovec[2];
442 	int ret;
443 
444 	path = join(dir, node);
445 	if (path == NULL)
446 		return ENOMEM;
447 
448 	/* xs_talkv only reads iovec */
449 	iovec[0].iov_base = __UNCONST(path);
450 	iovec[0].iov_len = strlen(path) + 1;
451 	iovec[1].iov_base = __UNCONST(string);
452 	iovec[1].iov_len = strlen(string);
453 
454 	ret = xs_talkv(t, XS_WRITE, iovec, 2, NULL, NULL);
455 	return ret;
456 }
457 
458 /* Create a new directory. */
459 int
xenbus_mkdir(struct xenbus_transaction * t,const char * dir,const char * node)460 xenbus_mkdir(struct xenbus_transaction *t,
461 		 const char *dir, const char *node)
462 {
463 	char *path;
464 	int ret;
465 
466 	path = join(dir, node);
467 	if (path == NULL)
468 		return ENOMEM;
469 
470 	ret = xs_single(t, XS_MKDIR, path, NULL, NULL);
471 	free(path, M_DEVBUF);
472 	return ret;
473 }
474 
475 /* Destroy a file or directory (directories must be empty). */
xenbus_rm(struct xenbus_transaction * t,const char * dir,const char * node)476 int xenbus_rm(struct xenbus_transaction *t, const char *dir, const char *node)
477 {
478 	char *path;
479 	int ret;
480 
481 	path = join(dir, node);
482 	if (path == NULL)
483 		return ENOMEM;
484 
485 	ret = xs_single(t, XS_RM, path, NULL, NULL);
486 	free(path, M_DEVBUF);
487 	return ret;
488 }
489 
490 /* Start a transaction: changes by others will not be seen during this
491  * transaction, and changes will not be visible to others until end.
492  * MUST BE CALLED AT IPL_TTY !
493  */
494 struct xenbus_transaction *
xenbus_transaction_start(void)495 xenbus_transaction_start(void)
496 {
497 	char *id_str;
498 	unsigned long id, err;
499 
500 	err = xs_single(NULL, XS_TRANSACTION_START, "", NULL, &id_str);
501 	if (err) {
502 		return NULL;
503 	}
504 
505 	id = strtoul(id_str, NULL, 0);
506 	free(id_str, M_DEVBUF);
507 
508 	return (struct xenbus_transaction *)id;
509 }
510 
511 /* End a transaction.
512  * If abandon is true, transaction is discarded instead of committed.
513  * MUST BE CALLED AT IPL_TTY !
514  */
xenbus_transaction_end(struct xenbus_transaction * t,int abort)515 int xenbus_transaction_end(struct xenbus_transaction *t, int abort)
516 {
517 	char abortstr[2];
518 	int err;
519 
520 	if (abort)
521 		strcpy(abortstr, "F");
522 	else
523 		strcpy(abortstr, "T");
524 
525 	err = xs_single(t, XS_TRANSACTION_END, abortstr, NULL, NULL);
526 
527 	return err;
528 }
529 
530 /* Single printf and write: returns -errno or 0. */
531 int
xenbus_printf(struct xenbus_transaction * t,const char * dir,const char * node,const char * fmt,...)532 xenbus_printf(struct xenbus_transaction *t,
533 		  const char *dir, const char *node, const char *fmt, ...)
534 {
535 	va_list ap;
536 	int ret;
537 #define PRINTF_BUFFER_SIZE 4096
538 	char *printf_buffer;
539 
540 	printf_buffer = malloc(PRINTF_BUFFER_SIZE, M_DEVBUF, M_NOWAIT);
541 	if (printf_buffer == NULL)
542 		return ENOMEM;
543 
544 	va_start(ap, fmt);
545 	ret = vsnprintf(printf_buffer, PRINTF_BUFFER_SIZE, fmt, ap);
546 	va_end(ap);
547 
548 	KASSERT(ret < PRINTF_BUFFER_SIZE);
549 	ret = xenbus_write(t, dir, node, printf_buffer);
550 
551 	free(printf_buffer, M_DEVBUF);
552 
553 	return ret;
554 }
555 
556 static int
xs_watch(const char * path,const char * token)557 xs_watch(const char *path, const char *token)
558 {
559 	struct iovec iov[2];
560 
561 	/* xs_talkv only reads iovec */
562 	iov[0].iov_base = __UNCONST(path);
563 	iov[0].iov_len = strlen(path) + 1;
564 	iov[1].iov_base = __UNCONST(token);
565 	iov[1].iov_len = strlen(token) + 1;
566 
567 	return xs_talkv(NULL, XS_WATCH, iov, 2, NULL, NULL);
568 }
569 
570 static int
xs_unwatch(const char * path,const char * token)571 xs_unwatch(const char *path, const char *token)
572 {
573 	struct iovec iov[2];
574 
575 	/* xs_talkv only reads iovec */
576 	iov[0].iov_base = __UNCONST(path);
577 	iov[0].iov_len = strlen(path) + 1;
578 	iov[1].iov_base = __UNCONST(token);
579 	iov[1].iov_len = strlen(token) + 1;
580 
581 	return xs_talkv(NULL, XS_UNWATCH, iov, 2, NULL, NULL);
582 }
583 
584 static struct xenbus_watch *
find_watch(const char * token)585 find_watch(const char *token)
586 {
587 	struct xenbus_watch *i, *cmp;
588 
589 	cmp = (void *)strtoul(token, NULL, 16);
590 
591 	SLIST_FOREACH(i, &watches, watch_next) {
592 		if (i == cmp)
593 			return i;
594 	}
595 
596 	return NULL;
597 }
598 
599 /* Register callback to watch this node. */
600 int
register_xenbus_watch(struct xenbus_watch * watch)601 register_xenbus_watch(struct xenbus_watch *watch)
602 {
603 	/* Pointer in ascii is the token. */
604 	char token[sizeof(watch) * 2 + 1];
605 	int err;
606 
607 	snprintf(token, sizeof(token), "%lX", (long)watch);
608 
609 	mutex_enter(&watches_lock);
610 	KASSERT(find_watch(token) == 0);
611 	SLIST_INSERT_HEAD(&watches, watch, watch_next);
612 	mutex_exit(&watches_lock);
613 
614 	err = xs_watch(watch->node, token);
615 
616 	/* Ignore errors due to multiple registration. */
617 	if ((err != 0) && (err != EEXIST)) {
618 		mutex_enter(&watches_lock);
619 		SLIST_REMOVE(&watches, watch, xenbus_watch, watch_next);
620 		mutex_exit(&watches_lock);
621 	}
622 	return err;
623 }
624 
625 void
unregister_xenbus_watch(struct xenbus_watch * watch)626 unregister_xenbus_watch(struct xenbus_watch *watch)
627 {
628 	SIMPLEQ_HEAD(, xs_stored_msg) gclist;
629 	struct xs_stored_msg *msg, *next_msg;
630 	char token[sizeof(watch) * 2 + 1];
631 	int err;
632 
633 	snprintf(token, sizeof(token), "%lX", (long)watch);
634 
635 	mutex_enter(&watches_lock);
636 	KASSERT(find_watch(token));
637 	SLIST_REMOVE(&watches, watch, xenbus_watch, watch_next);
638 	mutex_exit(&watches_lock);
639 
640 	err = xs_unwatch(watch->node, token);
641 	if (err) {
642 		printf(
643 		       "XENBUS Failed to release watch %s: %i\n",
644 		       watch->node, err);
645 	}
646 
647 	/* Cancel pending watch events. */
648 	SIMPLEQ_INIT(&gclist);
649 	mutex_enter(&watch_events_lock);
650 	for (msg = SIMPLEQ_FIRST(&watch_events); msg != NULL; msg = next_msg) {
651 		next_msg = SIMPLEQ_NEXT(msg, msg_next);
652 		if (msg->u.watch.handle != watch)
653 			continue;
654 		SIMPLEQ_REMOVE(&watch_events, msg, xs_stored_msg, msg_next);
655 		SIMPLEQ_INSERT_TAIL(&gclist, msg, msg_next);
656 	}
657 	mutex_exit(&watch_events_lock);
658 
659 	while ((msg = SIMPLEQ_FIRST(&gclist)) != NULL) {
660 		SIMPLEQ_REMOVE(&gclist, msg, xs_stored_msg, msg_next);
661 		free(msg->u.watch.vec, M_DEVBUF);
662 		free(msg, M_DEVBUF);
663 	}
664 }
665 
666 void
xs_suspend(void)667 xs_suspend(void)
668 {
669 	xs_state.suspend_spl = spltty();
670 }
671 
672 void
xs_resume(void)673 xs_resume(void)
674 {
675 	struct xenbus_watch *watch;
676 	char token[sizeof(watch) * 2 + 1];
677 	/* No need for watches_lock: the suspend_mutex is sufficient. */
678 	SLIST_FOREACH(watch, &watches, watch_next) {
679 		snprintf(token, sizeof(token), "%lX", (long)watch);
680 		xs_watch(watch->node, token);
681 	}
682 
683 	splx(xs_state.suspend_spl);
684 }
685 
686 static void
xenwatch_thread(void * unused)687 xenwatch_thread(void *unused)
688 {
689 	SIMPLEQ_HEAD(, xs_stored_msg) events_to_proces;
690 	struct xs_stored_msg *msg;
691 
692 	SIMPLEQ_INIT(&events_to_proces);
693 	for (;;) {
694 		mutex_enter(&watch_events_lock);
695 		while (SIMPLEQ_EMPTY(&watch_events))
696 			cv_wait(&watch_cv, &watch_events_lock);
697 		SIMPLEQ_CONCAT(&events_to_proces, &watch_events);
698 		mutex_exit(&watch_events_lock);
699 
700 		DPRINTK("xenwatch_thread: processing events");
701 
702 		while ((msg = SIMPLEQ_FIRST(&events_to_proces)) != NULL) {
703 			DPRINTK("xenwatch_thread: got event");
704 			SIMPLEQ_REMOVE_HEAD(&events_to_proces, msg_next);
705 			msg->u.watch.handle->xbw_callback(
706 				msg->u.watch.handle,
707 				(void *)msg->u.watch.vec,
708 				msg->u.watch.vec_size);
709 			free(msg->u.watch.vec, M_DEVBUF);
710 			free(msg, M_DEVBUF);
711 		}
712 	}
713 }
714 
715 static int
process_msg(void)716 process_msg(void)
717 {
718 	struct xs_stored_msg *msg, *s_msg;
719 	char *body;
720 	int err;
721 
722 	msg = malloc(sizeof(*msg), M_DEVBUF, M_WAITOK);
723 	if (msg == NULL)
724 		return ENOMEM;
725 
726 	err = xb_read(&msg->hdr, sizeof(msg->hdr));
727 	DPRINTK("xb_read hdr %d", err);
728 	if (err) {
729 		free(msg, M_DEVBUF);
730 		return err;
731 	}
732 
733 	body = malloc(msg->hdr.len + 1, M_DEVBUF, M_NOWAIT);
734 	if (body == NULL) {
735 		free(msg, M_DEVBUF);
736 		return ENOMEM;
737 	}
738 
739 	err = xb_read(body, msg->hdr.len);
740 	DPRINTK("xb_read body %d", err);
741 	if (err) {
742 		free(body, M_DEVBUF);
743 		free(msg, M_DEVBUF);
744 		return err;
745 	}
746 	body[msg->hdr.len] = '\0';
747 
748 	if (msg->hdr.type == XS_WATCH_EVENT) {
749 		bool found, repeated;
750 
751 		DPRINTK("process_msg: XS_WATCH_EVENT");
752 		msg->u.watch.vec = split(body, msg->hdr.len,
753 					 &msg->u.watch.vec_size);
754 		if (msg->u.watch.vec == NULL) {
755 			free(msg, M_DEVBUF);
756 			return ENOMEM;
757 		}
758 
759 		mutex_enter(&watches_lock);
760 		msg->u.watch.handle = find_watch(
761 		    msg->u.watch.vec[XS_WATCH_TOKEN]);
762 		found = (msg->u.watch.handle != NULL);
763 		repeated = false;
764 		if (found) {
765 			mutex_enter(&watch_events_lock);
766 			/* Don't add duplicate events to the queue of pending watches */
767 			SIMPLEQ_FOREACH(s_msg, &watch_events, msg_next) {
768 				if (s_msg->u.watch.handle == msg->u.watch.handle) {
769 					repeated = true;
770 					break;
771 				}
772 			}
773 			if (!repeated) {
774 				SIMPLEQ_INSERT_TAIL(&watch_events, msg, msg_next);
775 				cv_broadcast(&watch_cv);
776 			}
777 			mutex_exit(&watch_events_lock);
778 		}
779 		mutex_exit(&watches_lock);
780 		if (!found || repeated) {
781 			free(msg->u.watch.vec, M_DEVBUF);
782 			free(msg, M_DEVBUF);
783 		}
784 	} else {
785 		DPRINTK("process_msg: type %d body %s", msg->hdr.type, body);
786 
787 		msg->u.reply.body = body;
788 		mutex_enter(&xs_state.reply_lock);
789 		SIMPLEQ_INSERT_TAIL(&xs_state.reply_list, msg, msg_next);
790 		cv_broadcast(&xs_state.reply_cv);
791 		mutex_exit(&xs_state.reply_lock);
792 	}
793 
794 	return 0;
795 }
796 
797 static void
xenbus_thread(void * unused)798 xenbus_thread(void *unused)
799 {
800 	int err;
801 
802 	for (;;) {
803 		err = process_msg();
804 		if (err)
805 			printk("XENBUS error %d while reading message\n", err);
806 	}
807 }
808 
809 int
xs_init(device_t dev)810 xs_init(device_t dev)
811 {
812 	int err;
813 
814 	SLIST_INIT(&watches);
815 	mutex_init(&watches_lock, MUTEX_DEFAULT, IPL_TTY);
816 
817 	SIMPLEQ_INIT(&watch_events);
818 	mutex_init(&watch_events_lock, MUTEX_DEFAULT, IPL_TTY);
819 	cv_init(&watch_cv, "evtsq");
820 
821 	SIMPLEQ_INIT(&xs_state.reply_list);
822 	mutex_init(&xs_state.xs_lock, MUTEX_DEFAULT, IPL_NONE);
823 	mutex_init(&xs_state.reply_lock, MUTEX_DEFAULT, IPL_TTY);
824 	cv_init(&xs_state.reply_cv, "rplq");
825 
826 	err = kthread_create(PRI_NONE, KTHREAD_MPSAFE, NULL, xenwatch_thread,
827 	    NULL, NULL, "xenwatch");
828 	if (err) {
829 		aprint_error_dev(dev, "kthread_create(xenwatch): %d\n", err);
830 		return err;
831 	}
832 
833 	err = kthread_create(PRI_NONE, KTHREAD_MPSAFE, NULL, xenbus_thread,
834 	    NULL, NULL, "xenbus");
835 	if (err) {
836 		aprint_error_dev(dev, "kthread_create(xenbus): %d\n", err);
837 		return err;
838 	}
839 
840 	return 0;
841 }
842 
843 /*
844  * Local variables:
845  *  c-file-style: "linux"
846  *  indent-tabs-mode: t
847  *  c-indent-level: 8
848  *  c-basic-offset: 8
849  *  tab-width: 8
850  * End:
851  */
852