xenstore.c revision 181624
1181624Skmacy/******************************************************************************
2181624Skmacy * xenbus_xs.c
3181624Skmacy *
4181624Skmacy * This is the kernel equivalent of the "xs" library.  We don't need everything
5181624Skmacy * and we use xenbus_comms for communication.
6181624Skmacy *
7181624Skmacy * Copyright (C) 2005 Rusty Russell, IBM Corporation
8181624Skmacy *
9181624Skmacy * This file may be distributed separately from the Linux kernel, or
10181624Skmacy * incorporated into other software packages, subject to the following license:
11181624Skmacy *
12181624Skmacy * Permission is hereby granted, free of charge, to any person obtaining a copy
13181624Skmacy * of this source file (the "Software"), to deal in the Software without
14181624Skmacy * restriction, including without limitation the rights to use, copy, modify,
15181624Skmacy * merge, publish, distribute, sublicense, and/or sell copies of the Software,
16181624Skmacy * and to permit persons to whom the Software is furnished to do so, subject to
17181624Skmacy * the following conditions:
18181624Skmacy *
19181624Skmacy * The above copyright notice and this permission notice shall be included in
20181624Skmacy * all copies or substantial portions of the Software.
21181624Skmacy *
22181624Skmacy * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23181624Skmacy * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24181624Skmacy * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25181624Skmacy * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26181624Skmacy * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27181624Skmacy * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
28181624Skmacy * IN THE SOFTWARE.
29181624Skmacy */
30181624Skmacy
31181624Skmacy
32181624Skmacy#include <sys/cdefs.h>
33181624Skmacy__FBSDID("$FreeBSD: head/sys/xen/xenbus/xenbus_xs.c 181624 2008-08-12 07:36:56Z kmacy $");
34181624Skmacy
35181624Skmacy#include <sys/param.h>
36181624Skmacy#include <sys/types.h>
37181624Skmacy#include <sys/cdefs.h>
38181624Skmacy#include <sys/unistd.h>
39181624Skmacy#include <sys/errno.h>
40181624Skmacy#include <sys/uio.h>
41181624Skmacy#include <sys/kernel.h>
42181624Skmacy#include <sys/time.h>
43181624Skmacy#include <sys/lock.h>
44181624Skmacy#include <sys/mutex.h>
45181624Skmacy#include <sys/sx.h>
46181624Skmacy#include <sys/sema.h>
47181624Skmacy#include <sys/syslog.h>
48181624Skmacy#include <sys/malloc.h>
49181624Skmacy#include <sys/libkern.h>
50181624Skmacy#include <sys/systm.h>
51181624Skmacy#include <sys/proc.h>
52181624Skmacy#include <sys/kthread.h>
53181624Skmacy
54181624Skmacy#include <machine/xen/hypervisor.h>
55181624Skmacy#include <machine/xen/xenbus.h>
56181624Skmacy#include <machine/stdarg.h>
57181624Skmacy
58181624Skmacy#include <xen/xenbus/xenbus_comms.h>
59181624Skmacyint xs_process_msg(void);
60181624Skmacy
61181624Skmacy#define kmalloc(size, unused) malloc(size, M_DEVBUF, M_WAITOK)
62181624Skmacy#define BUG_ON        PANIC_IF
63181624Skmacy#define DEFINE_SPINLOCK(lock) struct mtx lock
64181624Skmacy#define spin_lock     mtx_lock
65181624Skmacy#define spin_unlock   mtx_unlock
66181624Skmacy#define u32           uint32_t
67181624Skmacy#define list_del(head, ent)      TAILQ_REMOVE(head, ent, list)
68181624Skmacy#define simple_strtoul strtoul
69181624Skmacy#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
70181624Skmacy#define list_empty    TAILQ_EMPTY
71181624Skmacy
72181624Skmacy#define streq(a, b) (strcmp((a), (b)) == 0)
73181624Skmacy
74181624Skmacystruct kvec {
75181624Skmacy		const void *iov_base;
76181624Skmacy		size_t      iov_len;
77181624Skmacy};
78181624Skmacy
79181624Skmacystruct xs_stored_msg {
80181624Skmacy		TAILQ_ENTRY(xs_stored_msg) list;
81181624Skmacy
82181624Skmacy		struct xsd_sockmsg hdr;
83181624Skmacy
84181624Skmacy		union {
85181624Skmacy				/* Queued replies. */
86181624Skmacy				struct {
87181624Skmacy						char *body;
88181624Skmacy				} reply;
89181624Skmacy
90181624Skmacy				/* Queued watch events. */
91181624Skmacy				struct {
92181624Skmacy						struct xenbus_watch *handle;
93181624Skmacy						char **vec;
94181624Skmacy						unsigned int vec_size;
95181624Skmacy				} watch;
96181624Skmacy		} u;
97181624Skmacy};
98181624Skmacy
99181624Skmacystruct xs_handle {
100181624Skmacy		/* A list of replies. Currently only one will ever be outstanding. */
101181624Skmacy		TAILQ_HEAD(xs_handle_list, xs_stored_msg) reply_list;
102181624Skmacy		spinlock_t reply_lock;
103181624Skmacy		int reply_waitq;
104181624Skmacy
105181624Skmacy		/* One request at a time. */
106181624Skmacy		struct sx request_mutex;
107181624Skmacy
108181624Skmacy		/* Protect transactions against save/restore. */
109181624Skmacy		struct rw_semaphore suspend_mutex;
110181624Skmacy};
111181624Skmacy
112181624Skmacystatic struct xs_handle xs_state;
113181624Skmacy
114181624Skmacy/* List of registered watches, and a lock to protect it. */
115181624Skmacystatic LIST_HEAD(watch_list_head, xenbus_watch) watches;
116181624Skmacystatic DEFINE_SPINLOCK(watches_lock);
117181624Skmacy/* List of pending watch callback events, and a lock to protect it. */
118181624Skmacystatic TAILQ_HEAD(event_list_head, xs_stored_msg) watch_events;
119181624Skmacystatic DEFINE_SPINLOCK(watch_events_lock);
120181624Skmacy/*
121181624Skmacy * Details of the xenwatch callback kernel thread. The thread waits on the
122181624Skmacy * watch_events_waitq for work to do (queued on watch_events list). When it
123181624Skmacy * wakes up it acquires the xenwatch_mutex before reading the list and
124181624Skmacy * carrying out work.
125181624Skmacy */
126181624Skmacystatic pid_t xenwatch_pid;
127181624Skmacystruct sx xenwatch_mutex;
128181624Skmacystatic int watch_events_waitq;
129181624Skmacy
130181624Skmacystatic int get_error(const char *errorstring)
131181624Skmacy{
132181624Skmacy		unsigned int i;
133181624Skmacy
134181624Skmacy		for (i = 0; !streq(errorstring, xsd_errors[i].errstring); i++) {
135181624Skmacy				if (i == ARRAY_SIZE(xsd_errors) - 1) {
136181624Skmacy						log(LOG_WARNING, "XENBUS xen store gave: unknown error %s",
137181624Skmacy							   errorstring);
138181624Skmacy						return EINVAL;
139181624Skmacy				}
140181624Skmacy		}
141181624Skmacy		return xsd_errors[i].errnum;
142181624Skmacy}
143181624Skmacy
144181624Skmacyextern int scheduler_running;
145181624Skmacy
146181624Skmacystatic void *read_reply(enum xsd_sockmsg_type *type, unsigned int *len)
147181624Skmacy{
148181624Skmacy		struct xs_stored_msg *msg;
149181624Skmacy		char *body;
150181624Skmacy		int i;
151181624Skmacy
152181624Skmacy		if (scheduler_running == 0) {
153181624Skmacy				/*
154181624Skmacy				 * Give other domain time to run :-/
155181624Skmacy				 */
156181624Skmacy				for (i = 0; i < 10000; i++)
157181624Skmacy						HYPERVISOR_yield();
158181624Skmacy				xs_process_msg();
159181624Skmacy		}
160181624Skmacy
161181624Skmacy		spin_lock(&xs_state.reply_lock);
162181624Skmacy
163181624Skmacy		while (list_empty(&xs_state.reply_list)) {
164181624Skmacy				spin_unlock(&xs_state.reply_lock);
165181624Skmacy				wait_event_interruptible(&xs_state.reply_waitq,
166181624Skmacy										 !list_empty(&xs_state.reply_list));
167181624Skmacy				spin_lock(&xs_state.reply_lock);
168181624Skmacy		}
169181624Skmacy
170181624Skmacy		msg = TAILQ_FIRST(&xs_state.reply_list);
171181624Skmacy		list_del(&xs_state.reply_list, msg);
172181624Skmacy
173181624Skmacy		spin_unlock(&xs_state.reply_lock);
174181624Skmacy
175181624Skmacy		*type = msg->hdr.type;
176181624Skmacy		if (len)
177181624Skmacy				*len = msg->hdr.len;
178181624Skmacy		body = msg->u.reply.body;
179181624Skmacy
180181624Skmacy		kfree(msg);
181181624Skmacy
182181624Skmacy		return body;
183181624Skmacy}
184181624Skmacy
185181624Skmacy#if 0
186181624Skmacy/* Emergency write. UNUSED*/
187181624Skmacyvoid xenbus_debug_write(const char *str, unsigned int count)
188181624Skmacy{
189181624Skmacy		struct xsd_sockmsg msg = { 0 };
190181624Skmacy
191181624Skmacy		msg.type = XS_DEBUG;
192181624Skmacy		msg.len = sizeof("print") + count + 1;
193181624Skmacy
194181624Skmacy		sx_xlock(&xs_state.request_mutex);
195181624Skmacy		xb_write(&msg, sizeof(msg));
196181624Skmacy		xb_write("print", sizeof("print"));
197181624Skmacy		xb_write(str, count);
198181624Skmacy		xb_write("", 1);
199181624Skmacy		sx_xunlock(&xs_state.request_mutex);
200181624Skmacy}
201181624Skmacy
202181624Skmacy#endif
203181624Skmacyvoid *xenbus_dev_request_and_reply(struct xsd_sockmsg *msg)
204181624Skmacy{
205181624Skmacy		void *ret;
206181624Skmacy		struct xsd_sockmsg req_msg = *msg;
207181624Skmacy		int err;
208181624Skmacy
209181624Skmacy		if (req_msg.type == XS_TRANSACTION_START)
210181624Skmacy				down_read(&xs_state.suspend_mutex);
211181624Skmacy
212181624Skmacy		sx_xlock(&xs_state.request_mutex);
213181624Skmacy
214181624Skmacy		err = xb_write(msg, sizeof(*msg) + msg->len);
215181624Skmacy		if (err) {
216181624Skmacy				msg->type = XS_ERROR;
217181624Skmacy				ret = ERR_PTR(err);
218181624Skmacy		} else {
219181624Skmacy				ret = read_reply(&msg->type, &msg->len);
220181624Skmacy		}
221181624Skmacy
222181624Skmacy		sx_xunlock(&xs_state.request_mutex);
223181624Skmacy
224181624Skmacy		if ((msg->type == XS_TRANSACTION_END) ||
225181624Skmacy			((req_msg.type == XS_TRANSACTION_START) &&
226181624Skmacy			 (msg->type == XS_ERROR)))
227181624Skmacy				up_read(&xs_state.suspend_mutex);
228181624Skmacy
229181624Skmacy		return ret;
230181624Skmacy}
231181624Skmacy
232181624Skmacy/* Send message to xs, get kmalloc'ed reply.  ERR_PTR() on error. */
233181624Skmacystatic void *xs_talkv(struct xenbus_transaction t,
234181624Skmacy					  enum xsd_sockmsg_type type,
235181624Skmacy					  const struct kvec *iovec,
236181624Skmacy					  unsigned int num_vecs,
237181624Skmacy					  unsigned int *len)
238181624Skmacy{
239181624Skmacy		struct xsd_sockmsg msg;
240181624Skmacy		void *ret = NULL;
241181624Skmacy		unsigned int i;
242181624Skmacy		int err;
243181624Skmacy
244181624Skmacy		msg.tx_id = t.id;
245181624Skmacy		msg.req_id = 0;
246181624Skmacy		msg.type = type;
247181624Skmacy		msg.len = 0;
248181624Skmacy		for (i = 0; i < num_vecs; i++)
249181624Skmacy				msg.len += iovec[i].iov_len;
250181624Skmacy
251181624Skmacy		sx_xlock(&xs_state.request_mutex);
252181624Skmacy
253181624Skmacy		err = xb_write(&msg, sizeof(msg));
254181624Skmacy		if (err) {
255181624Skmacy				sx_xunlock(&xs_state.request_mutex);
256181624Skmacy				return ERR_PTR(err);
257181624Skmacy		}
258181624Skmacy
259181624Skmacy		for (i = 0; i < num_vecs; i++) {
260181624Skmacy				err = xb_write(iovec[i].iov_base, iovec[i].iov_len);;
261181624Skmacy				if (err) {
262181624Skmacy						sx_xunlock(&xs_state.request_mutex);
263181624Skmacy						return ERR_PTR(err);
264181624Skmacy				}
265181624Skmacy		}
266181624Skmacy
267181624Skmacy		ret = read_reply(&msg.type, len);
268181624Skmacy
269181624Skmacy		sx_xunlock(&xs_state.request_mutex);
270181624Skmacy
271181624Skmacy		if (IS_ERR(ret))
272181624Skmacy				return ret;
273181624Skmacy
274181624Skmacy		if (msg.type == XS_ERROR) {
275181624Skmacy				err = get_error(ret);
276181624Skmacy				kfree(ret);
277181624Skmacy				return ERR_PTR(-err);
278181624Skmacy		}
279181624Skmacy
280181624Skmacy		BUG_ON(msg.type != type);
281181624Skmacy		return ret;
282181624Skmacy}
283181624Skmacy
284181624Skmacy/* Simplified version of xs_talkv: single message. */
285181624Skmacystatic void *xs_single(struct xenbus_transaction t,
286181624Skmacy					   enum xsd_sockmsg_type type,
287181624Skmacy					   const char *string,
288181624Skmacy					   unsigned int *len)
289181624Skmacy{
290181624Skmacy		struct kvec iovec;
291181624Skmacy
292181624Skmacy		iovec.iov_base = (const void *)string;
293181624Skmacy		iovec.iov_len = strlen(string) + 1;
294181624Skmacy		return xs_talkv(t, type, &iovec, 1, len);
295181624Skmacy}
296181624Skmacy
297181624Skmacy/* Many commands only need an ack, don't care what it says. */
298181624Skmacystatic int xs_error(char *reply)
299181624Skmacy{
300181624Skmacy		if (IS_ERR(reply))
301181624Skmacy				return PTR_ERR(reply);
302181624Skmacy		kfree(reply);
303181624Skmacy		return 0;
304181624Skmacy}
305181624Skmacy
306181624Skmacystatic unsigned int count_strings(const char *strings, unsigned int len)
307181624Skmacy{
308181624Skmacy		unsigned int num;
309181624Skmacy		const char *p;
310181624Skmacy
311181624Skmacy		for (p = strings, num = 0; p < strings + len; p += strlen(p) + 1)
312181624Skmacy				num++;
313181624Skmacy
314181624Skmacy		return num;
315181624Skmacy}
316181624Skmacy
317181624Skmacy/* Return the path to dir with /name appended. Buffer must be kfree()'ed. */
318181624Skmacystatic char *join(const char *dir, const char *name)
319181624Skmacy{
320181624Skmacy		char *buffer;
321181624Skmacy
322181624Skmacy		buffer = kmalloc(strlen(dir) + strlen("/") + strlen(name) + 1,
323181624Skmacy						 GFP_KERNEL);
324181624Skmacy		if (buffer == NULL)
325181624Skmacy				return ERR_PTR(-ENOMEM);
326181624Skmacy
327181624Skmacy		strcpy(buffer, dir);
328181624Skmacy		if (!streq(name, "")) {
329181624Skmacy				strcat(buffer, "/");
330181624Skmacy				strcat(buffer, name);
331181624Skmacy		}
332181624Skmacy
333181624Skmacy		return buffer;
334181624Skmacy}
335181624Skmacy
336181624Skmacystatic char **split(char *strings, unsigned int len, unsigned int *num)
337181624Skmacy{
338181624Skmacy		char *p, **ret;
339181624Skmacy
340181624Skmacy		/* Count the strings. */
341181624Skmacy		*num = count_strings(strings, len);
342181624Skmacy
343181624Skmacy		/* Transfer to one big alloc for easy freeing. */
344181624Skmacy		ret = kmalloc(*num * sizeof(char *) + len, GFP_KERNEL);
345181624Skmacy		if (!ret) {
346181624Skmacy				kfree(strings);
347181624Skmacy				return ERR_PTR(-ENOMEM);
348181624Skmacy		}
349181624Skmacy		memcpy(&ret[*num], strings, len);
350181624Skmacy		kfree(strings);
351181624Skmacy
352181624Skmacy		strings = (char *)&ret[*num];
353181624Skmacy		for (p = strings, *num = 0; p < strings + len; p += strlen(p) + 1)
354181624Skmacy				ret[(*num)++] = p;
355181624Skmacy
356181624Skmacy		return ret;
357181624Skmacy}
358181624Skmacy
359181624Skmacychar **xenbus_directory(struct xenbus_transaction t,
360181624Skmacy						const char *dir, const char *node, unsigned int *num)
361181624Skmacy{
362181624Skmacy		char *strings, *path;
363181624Skmacy		unsigned int len;
364181624Skmacy
365181624Skmacy		path = join(dir, node);
366181624Skmacy		if (IS_ERR(path))
367181624Skmacy				return (char **)path;
368181624Skmacy
369181624Skmacy		strings = xs_single(t, XS_DIRECTORY, path, &len);
370181624Skmacy		kfree(path);
371181624Skmacy		if (IS_ERR(strings))
372181624Skmacy				return (char **)strings;
373181624Skmacy
374181624Skmacy		return split(strings, len, num);
375181624Skmacy}
376181624SkmacyEXPORT_SYMBOL(xenbus_directory);
377181624Skmacy
378181624Skmacy/* Check if a path exists. Return 1 if it does. */
379181624Skmacyint xenbus_exists(struct xenbus_transaction t,
380181624Skmacy				  const char *dir, const char *node)
381181624Skmacy{
382181624Skmacy		char **d;
383181624Skmacy		int dir_n;
384181624Skmacy
385181624Skmacy		d = xenbus_directory(t, dir, node, &dir_n);
386181624Skmacy		if (IS_ERR(d))
387181624Skmacy				return 0;
388181624Skmacy		kfree(d);
389181624Skmacy		return 1;
390181624Skmacy}
391181624SkmacyEXPORT_SYMBOL(xenbus_exists);
392181624Skmacy
393181624Skmacy/* Get the value of a single file.
394181624Skmacy * Returns a kmalloced value: call free() on it after use.
395181624Skmacy * len indicates length in bytes.
396181624Skmacy */
397181624Skmacyvoid *xenbus_read(struct xenbus_transaction t,
398181624Skmacy				  const char *dir, const char *node, unsigned int *len)
399181624Skmacy{
400181624Skmacy		char *path;
401181624Skmacy		void *ret;
402181624Skmacy
403181624Skmacy		path = join(dir, node);
404181624Skmacy		if (IS_ERR(path))
405181624Skmacy				return (void *)path;
406181624Skmacy
407181624Skmacy		ret = xs_single(t, XS_READ, path, len);
408181624Skmacy		kfree(path);
409181624Skmacy		return ret;
410181624Skmacy}
411181624SkmacyEXPORT_SYMBOL(xenbus_read);
412181624Skmacy
413181624Skmacy/* Write the value of a single file.
414181624Skmacy * Returns -err on failure.
415181624Skmacy */
416181624Skmacyint xenbus_write(struct xenbus_transaction t,
417181624Skmacy				 const char *dir, const char *node, const char *string)
418181624Skmacy{
419181624Skmacy		char *path;
420181624Skmacy		struct kvec iovec[2];
421181624Skmacy		int ret;
422181624Skmacy
423181624Skmacy		path = join(dir, node);
424181624Skmacy		if (IS_ERR(path))
425181624Skmacy				return PTR_ERR(path);
426181624Skmacy
427181624Skmacy		iovec[0].iov_base = path;
428181624Skmacy		iovec[0].iov_len = strlen(path) + 1;
429181624Skmacy		iovec[1].iov_base = string;
430181624Skmacy		iovec[1].iov_len = strlen(string);
431181624Skmacy
432181624Skmacy		ret = xs_error(xs_talkv(t, XS_WRITE, iovec, ARRAY_SIZE(iovec), NULL));
433181624Skmacy		kfree(path);
434181624Skmacy		return ret;
435181624Skmacy}
436181624SkmacyEXPORT_SYMBOL(xenbus_write);
437181624Skmacy
438181624Skmacy/* Create a new directory. */
439181624Skmacyint xenbus_mkdir(struct xenbus_transaction t,
440181624Skmacy				 const char *dir, const char *node)
441181624Skmacy{
442181624Skmacy		char *path;
443181624Skmacy		int ret;
444181624Skmacy
445181624Skmacy		path = join(dir, node);
446181624Skmacy		if (IS_ERR(path))
447181624Skmacy				return PTR_ERR(path);
448181624Skmacy
449181624Skmacy		ret = xs_error(xs_single(t, XS_MKDIR, path, NULL));
450181624Skmacy		kfree(path);
451181624Skmacy		return ret;
452181624Skmacy}
453181624SkmacyEXPORT_SYMBOL(xenbus_mkdir);
454181624Skmacy
455181624Skmacy/* Destroy a file or directory (directories must be empty). */
456181624Skmacyint xenbus_rm(struct xenbus_transaction t, const char *dir, const char *node)
457181624Skmacy{
458181624Skmacy		char *path;
459181624Skmacy		int ret;
460181624Skmacy
461181624Skmacy		path = join(dir, node);
462181624Skmacy		if (IS_ERR(path))
463181624Skmacy				return PTR_ERR(path);
464181624Skmacy
465181624Skmacy		ret = xs_error(xs_single(t, XS_RM, path, NULL));
466181624Skmacy		kfree(path);
467181624Skmacy		return ret;
468181624Skmacy}
469181624SkmacyEXPORT_SYMBOL(xenbus_rm);
470181624Skmacy
471181624Skmacy/* Start a transaction: changes by others will not be seen during this
472181624Skmacy * transaction, and changes will not be visible to others until end.
473181624Skmacy */
474181624Skmacyint xenbus_transaction_start(struct xenbus_transaction *t)
475181624Skmacy{
476181624Skmacy		char *id_str;
477181624Skmacy
478181624Skmacy		down_read(&xs_state.suspend_mutex);
479181624Skmacy
480181624Skmacy		id_str = xs_single(XBT_NIL, XS_TRANSACTION_START, "", NULL);
481181624Skmacy		if (IS_ERR(id_str)) {
482181624Skmacy				up_read(&xs_state.suspend_mutex);
483181624Skmacy				return PTR_ERR(id_str);
484181624Skmacy		}
485181624Skmacy
486181624Skmacy		t->id = simple_strtoul(id_str, NULL, 0);
487181624Skmacy		kfree(id_str);
488181624Skmacy
489181624Skmacy		return 0;
490181624Skmacy}
491181624SkmacyEXPORT_SYMBOL(xenbus_transaction_start);
492181624Skmacy
493181624Skmacy/* End a transaction.
494181624Skmacy * If abandon is true, transaction is discarded instead of committed.
495181624Skmacy */
496181624Skmacyint xenbus_transaction_end(struct xenbus_transaction t, int abort)
497181624Skmacy{
498181624Skmacy		char abortstr[2];
499181624Skmacy		int err;
500181624Skmacy
501181624Skmacy		if (abort)
502181624Skmacy				strcpy(abortstr, "F");
503181624Skmacy		else
504181624Skmacy				strcpy(abortstr, "T");
505181624Skmacy
506181624Skmacy		err = xs_error(xs_single(t, XS_TRANSACTION_END, abortstr, NULL));
507181624Skmacy
508181624Skmacy		up_read(&xs_state.suspend_mutex);
509181624Skmacy
510181624Skmacy		return err;
511181624Skmacy}
512181624SkmacyEXPORT_SYMBOL(xenbus_transaction_end);
513181624Skmacy
514181624Skmacy/* Single read and scanf: returns -errno or num scanned. */
515181624Skmacyint xenbus_scanf(struct xenbus_transaction t,
516181624Skmacy				 const char *dir, const char *node, const char *fmt, ...)
517181624Skmacy{
518181624Skmacy		va_list ap;
519181624Skmacy		int ret;
520181624Skmacy		char *val;
521181624Skmacy
522181624Skmacy		val = xenbus_read(t, dir, node, NULL);
523181624Skmacy		if (IS_ERR(val))
524181624Skmacy				return PTR_ERR(val);
525181624Skmacy
526181624Skmacy		va_start(ap, fmt);
527181624Skmacy		ret = vsscanf(val, fmt, ap);
528181624Skmacy		va_end(ap);
529181624Skmacy		kfree(val);
530181624Skmacy		/* Distinctive errno. */
531181624Skmacy		if (ret == 0)
532181624Skmacy				return -ERANGE;
533181624Skmacy		return ret;
534181624Skmacy}
535181624SkmacyEXPORT_SYMBOL(xenbus_scanf);
536181624Skmacy
537181624Skmacy/* Single printf and write: returns -errno or 0. */
538181624Skmacyint xenbus_printf(struct xenbus_transaction t,
539181624Skmacy				  const char *dir, const char *node, const char *fmt, ...)
540181624Skmacy{
541181624Skmacy		va_list ap;
542181624Skmacy		int ret;
543181624Skmacy#define PRINTF_BUFFER_SIZE 4096
544181624Skmacy		char *printf_buffer;
545181624Skmacy
546181624Skmacy		printf_buffer = kmalloc(PRINTF_BUFFER_SIZE, GFP_KERNEL);
547181624Skmacy		if (printf_buffer == NULL)
548181624Skmacy				return -ENOMEM;
549181624Skmacy
550181624Skmacy		va_start(ap, fmt);
551181624Skmacy		ret = vsnprintf(printf_buffer, PRINTF_BUFFER_SIZE, fmt, ap);
552181624Skmacy		va_end(ap);
553181624Skmacy
554181624Skmacy		BUG_ON(ret > PRINTF_BUFFER_SIZE-1);
555181624Skmacy		ret = xenbus_write(t, dir, node, printf_buffer);
556181624Skmacy
557181624Skmacy		kfree(printf_buffer);
558181624Skmacy
559181624Skmacy		return ret;
560181624Skmacy}
561181624SkmacyEXPORT_SYMBOL(xenbus_printf);
562181624Skmacy
563181624Skmacy/* Takes tuples of names, scanf-style args, and void **, NULL terminated. */
564181624Skmacyint xenbus_gather(struct xenbus_transaction t, const char *dir, ...)
565181624Skmacy{
566181624Skmacy		va_list ap;
567181624Skmacy		const char *name;
568181624Skmacy		int ret = 0;
569181624Skmacy
570181624Skmacy		va_start(ap, dir);
571181624Skmacy		while (ret == 0 && (name = va_arg(ap, char *)) != NULL) {
572181624Skmacy				const char *fmt = va_arg(ap, char *);
573181624Skmacy				void *result = va_arg(ap, void *);
574181624Skmacy				char *p;
575181624Skmacy
576181624Skmacy				p = xenbus_read(t, dir, name, NULL);
577181624Skmacy				if (IS_ERR(p)) {
578181624Skmacy						ret = PTR_ERR(p);
579181624Skmacy						break;
580181624Skmacy				}
581181624Skmacy				if (fmt) {
582181624Skmacy						if (sscanf(p, fmt, result) == 0)
583181624Skmacy								ret = -EINVAL;
584181624Skmacy						kfree(p);
585181624Skmacy				} else
586181624Skmacy						*(char **)result = p;
587181624Skmacy		}
588181624Skmacy		va_end(ap);
589181624Skmacy		return ret;
590181624Skmacy}
591181624SkmacyEXPORT_SYMBOL(xenbus_gather);
592181624Skmacy
593181624Skmacystatic int xs_watch(const char *path, const char *token)
594181624Skmacy{
595181624Skmacy		struct kvec iov[2];
596181624Skmacy
597181624Skmacy		iov[0].iov_base = path;
598181624Skmacy		iov[0].iov_len = strlen(path) + 1;
599181624Skmacy		iov[1].iov_base = token;
600181624Skmacy		iov[1].iov_len = strlen(token) + 1;
601181624Skmacy
602181624Skmacy		return xs_error(xs_talkv(XBT_NIL, XS_WATCH, iov,
603181624Skmacy								 ARRAY_SIZE(iov), NULL));
604181624Skmacy}
605181624Skmacy
606181624Skmacystatic int xs_unwatch(const char *path, const char *token)
607181624Skmacy{
608181624Skmacy		struct kvec iov[2];
609181624Skmacy
610181624Skmacy		iov[0].iov_base = path;
611181624Skmacy		iov[0].iov_len = strlen(path) + 1;
612181624Skmacy		iov[1].iov_base = token;
613181624Skmacy		iov[1].iov_len = strlen(token) + 1;
614181624Skmacy
615181624Skmacy		return xs_error(xs_talkv(XBT_NIL, XS_UNWATCH, iov,
616181624Skmacy								 ARRAY_SIZE(iov), NULL));
617181624Skmacy}
618181624Skmacy
619181624Skmacystatic struct xenbus_watch *find_watch(const char *token)
620181624Skmacy{
621181624Skmacy		struct xenbus_watch *i, *cmp;
622181624Skmacy
623181624Skmacy		cmp = (void *)simple_strtoul(token, NULL, 16);
624181624Skmacy
625181624Skmacy		LIST_FOREACH(i, &watches, list)
626181624Skmacy				if (i == cmp)
627181624Skmacy						return i;
628181624Skmacy
629181624Skmacy		return NULL;
630181624Skmacy}
631181624Skmacy
632181624Skmacy/* Register callback to watch this node. */
633181624Skmacyint register_xenbus_watch(struct xenbus_watch *watch)
634181624Skmacy{
635181624Skmacy		/* Pointer in ascii is the token. */
636181624Skmacy		char token[sizeof(watch) * 2 + 1];
637181624Skmacy		int err;
638181624Skmacy
639181624Skmacy		sprintf(token, "%lX", (long)watch);
640181624Skmacy
641181624Skmacy		down_read(&xs_state.suspend_mutex);
642181624Skmacy
643181624Skmacy		spin_lock(&watches_lock);
644181624Skmacy		BUG_ON(find_watch(token) != NULL);
645181624Skmacy		LIST_INSERT_HEAD(&watches, watch, list);
646181624Skmacy		spin_unlock(&watches_lock);
647181624Skmacy
648181624Skmacy		err = xs_watch(watch->node, token);
649181624Skmacy
650181624Skmacy		/* Ignore errors due to multiple registration. */
651181624Skmacy		if ((err != 0) && (err != -EEXIST)) {
652181624Skmacy				spin_lock(&watches_lock);
653181624Skmacy				LIST_REMOVE(watch, list);
654181624Skmacy				spin_unlock(&watches_lock);
655181624Skmacy		}
656181624Skmacy
657181624Skmacy		up_read(&xs_state.suspend_mutex);
658181624Skmacy
659181624Skmacy		return err;
660181624Skmacy}
661181624SkmacyEXPORT_SYMBOL(register_xenbus_watch);
662181624Skmacy
663181624Skmacyvoid unregister_xenbus_watch(struct xenbus_watch *watch)
664181624Skmacy{
665181624Skmacy		struct xs_stored_msg *msg, *tmp;
666181624Skmacy		char token[sizeof(watch) * 2 + 1];
667181624Skmacy		int err;
668181624Skmacy
669181624Skmacy		sprintf(token, "%lX", (long)watch);
670181624Skmacy
671181624Skmacy		down_read(&xs_state.suspend_mutex);
672181624Skmacy
673181624Skmacy		spin_lock(&watches_lock);
674181624Skmacy		BUG_ON(!find_watch(token));
675181624Skmacy		LIST_REMOVE(watch, list);
676181624Skmacy		spin_unlock(&watches_lock);
677181624Skmacy
678181624Skmacy		err = xs_unwatch(watch->node, token);
679181624Skmacy		if (err)
680181624Skmacy				log(LOG_WARNING, "XENBUS Failed to release watch %s: %i\n",
681181624Skmacy					   watch->node, err);
682181624Skmacy
683181624Skmacy		up_read(&xs_state.suspend_mutex);
684181624Skmacy
685181624Skmacy		/* Cancel pending watch events. */
686181624Skmacy		spin_lock(&watch_events_lock);
687181624Skmacy		TAILQ_FOREACH_SAFE(msg, &watch_events, list, tmp) {
688181624Skmacy				if (msg->u.watch.handle != watch)
689181624Skmacy						continue;
690181624Skmacy				list_del(&watch_events, msg);
691181624Skmacy				kfree(msg->u.watch.vec);
692181624Skmacy				kfree(msg);
693181624Skmacy		}
694181624Skmacy		spin_unlock(&watch_events_lock);
695181624Skmacy
696181624Skmacy		/* Flush any currently-executing callback, unless we are it. :-) */
697181624Skmacy		if (curproc->p_pid != xenwatch_pid) {
698181624Skmacy				sx_xlock(&xenwatch_mutex);
699181624Skmacy				sx_xunlock(&xenwatch_mutex);
700181624Skmacy		}
701181624Skmacy}
702181624SkmacyEXPORT_SYMBOL(unregister_xenbus_watch);
703181624Skmacy
704181624Skmacyvoid xs_suspend(void)
705181624Skmacy{
706181624Skmacy		down_write(&xs_state.suspend_mutex);
707181624Skmacy		sx_xlock(&xs_state.request_mutex);
708181624Skmacy}
709181624Skmacy
710181624Skmacyvoid xs_resume(void)
711181624Skmacy{
712181624Skmacy		struct xenbus_watch *watch;
713181624Skmacy		char token[sizeof(watch) * 2 + 1];
714181624Skmacy
715181624Skmacy		sx_xunlock(&xs_state.request_mutex);
716181624Skmacy
717181624Skmacy		/* No need for watches_lock: the suspend_mutex is sufficient. */
718181624Skmacy		LIST_FOREACH(watch, &watches, list) {
719181624Skmacy				sprintf(token, "%lX", (long)watch);
720181624Skmacy				xs_watch(watch->node, token);
721181624Skmacy		}
722181624Skmacy
723181624Skmacy		up_write(&xs_state.suspend_mutex);
724181624Skmacy}
725181624Skmacy
726181624Skmacystatic void xenwatch_thread(void *unused)
727181624Skmacy{
728181624Skmacy		struct xs_stored_msg *msg;
729181624Skmacy
730181624Skmacy		for (;;) {
731181624Skmacy				wait_event_interruptible(&watch_events_waitq,
732181624Skmacy										 !list_empty(&watch_events));
733181624Skmacy
734181624Skmacy				sx_xlock(&xenwatch_mutex);
735181624Skmacy
736181624Skmacy				spin_lock(&watch_events_lock);
737181624Skmacy				msg = TAILQ_FIRST(&watch_events);
738181624Skmacy				if (msg)
739181624Skmacy						list_del(&watch_events, msg);
740181624Skmacy				spin_unlock(&watch_events_lock);
741181624Skmacy
742181624Skmacy				if (msg != NULL) {
743181624Skmacy
744181624Skmacy						msg->u.watch.handle->callback(
745181624Skmacy								msg->u.watch.handle,
746181624Skmacy								(const char **)msg->u.watch.vec,
747181624Skmacy								msg->u.watch.vec_size);
748181624Skmacy						kfree(msg->u.watch.vec);
749181624Skmacy						kfree(msg);
750181624Skmacy				}
751181624Skmacy
752181624Skmacy				sx_xunlock(&xenwatch_mutex);
753181624Skmacy		}
754181624Skmacy}
755181624Skmacy
756181624Skmacyint xs_process_msg(void)
757181624Skmacy{
758181624Skmacy		struct xs_stored_msg *msg;
759181624Skmacy		char *body;
760181624Skmacy		int err;
761181624Skmacy
762181624Skmacy		msg = kmalloc(sizeof(*msg), GFP_KERNEL);
763181624Skmacy		if (msg == NULL)
764181624Skmacy				return -ENOMEM;
765181624Skmacy
766181624Skmacy		err = xb_read(&msg->hdr, sizeof(msg->hdr));
767181624Skmacy		if (err) {
768181624Skmacy				kfree(msg);
769181624Skmacy				return err;
770181624Skmacy		}
771181624Skmacy
772181624Skmacy		body = kmalloc(msg->hdr.len + 1, GFP_KERNEL);
773181624Skmacy		if (body == NULL) {
774181624Skmacy				kfree(msg);
775181624Skmacy				return -ENOMEM;
776181624Skmacy		}
777181624Skmacy
778181624Skmacy		err = xb_read(body, msg->hdr.len);
779181624Skmacy		if (err) {
780181624Skmacy				kfree(body);
781181624Skmacy				kfree(msg);
782181624Skmacy				return err;
783181624Skmacy		}
784181624Skmacy		body[msg->hdr.len] = '\0';
785181624Skmacy
786181624Skmacy		if (msg->hdr.type == XS_WATCH_EVENT) {
787181624Skmacy				msg->u.watch.vec = split(body, msg->hdr.len,
788181624Skmacy										 &msg->u.watch.vec_size);
789181624Skmacy				if (IS_ERR(msg->u.watch.vec)) {
790181624Skmacy						kfree(msg);
791181624Skmacy						return PTR_ERR(msg->u.watch.vec);
792181624Skmacy				}
793181624Skmacy
794181624Skmacy				spin_lock(&watches_lock);
795181624Skmacy				msg->u.watch.handle = find_watch(
796181624Skmacy						msg->u.watch.vec[XS_WATCH_TOKEN]);
797181624Skmacy				if (msg->u.watch.handle != NULL) {
798181624Skmacy						spin_lock(&watch_events_lock);
799181624Skmacy						TAILQ_INSERT_TAIL(&watch_events, msg, list);
800181624Skmacy						wakeup(&watch_events_waitq);
801181624Skmacy						spin_unlock(&watch_events_lock);
802181624Skmacy				} else {
803181624Skmacy						kfree(msg->u.watch.vec);
804181624Skmacy						kfree(msg);
805181624Skmacy				}
806181624Skmacy				spin_unlock(&watches_lock);
807181624Skmacy		} else {
808181624Skmacy				msg->u.reply.body = body;
809181624Skmacy				spin_lock(&xs_state.reply_lock);
810181624Skmacy				TAILQ_INSERT_TAIL(&xs_state.reply_list, msg, list);
811181624Skmacy				spin_unlock(&xs_state.reply_lock);
812181624Skmacy				wakeup(&xs_state.reply_waitq);
813181624Skmacy		}
814181624Skmacy
815181624Skmacy		return 0;
816181624Skmacy}
817181624Skmacy
818181624Skmacystatic void xenbus_thread(void *unused)
819181624Skmacy{
820181624Skmacy		int err;
821181624Skmacy
822181624Skmacy		for (;;) {
823181624Skmacy				err = xs_process_msg();
824181624Skmacy				if (err)
825181624Skmacy						printf("XENBUS error %d while reading "
826181624Skmacy							   "message\n", err);
827181624Skmacy		}
828181624Skmacy}
829181624Skmacy
830181624Skmacyint xs_init(void)
831181624Skmacy{
832181624Skmacy		int err;
833181624Skmacy		struct proc *p;
834181624Skmacy
835181624Skmacy		TAILQ_INIT(&xs_state.reply_list);
836181624Skmacy		TAILQ_INIT(&watch_events);
837181624Skmacy		mtx_init(&xs_state.reply_lock, "state reply", NULL, MTX_DEF);
838181624Skmacy		sema_init(&xs_state.suspend_mutex, 1, "xenstore suspend");
839181624Skmacy		sx_init(&xenwatch_mutex, "xenwatch");
840181624Skmacy		sx_init(&xs_state.request_mutex, "xenstore request");
841181624Skmacy
842181624Skmacy#if 0
843181624Skmacy		mtx_init(&xs_state.suspend_mutex, "xenstore suspend", NULL, MTX_DEF);
844181624Skmacy		sema_init(&xs_state.request_mutex, 1, "xenstore request");
845181624Skmacy		sema_init(&xenwatch_mutex, 1, "xenwatch");
846181624Skmacy#endif
847181624Skmacy		mtx_init(&watches_lock, "watches", NULL, MTX_DEF);
848181624Skmacy		mtx_init(&watch_events_lock, "watch events", NULL, MTX_DEF);
849181624Skmacy
850181624Skmacy		/* Initialize the shared memory rings to talk to xenstored */
851181624Skmacy		err = xb_init_comms();
852181624Skmacy		if (err)
853181624Skmacy				return err;
854181624Skmacy
855181624Skmacy		err = kproc_create(xenwatch_thread, NULL, &p,
856181624Skmacy							 RFHIGHPID, 0, "xenwatch");
857181624Skmacy		if (err)
858181624Skmacy				return err;
859181624Skmacy		xenwatch_pid = p->p_pid;
860181624Skmacy
861181624Skmacy		err = kproc_create(xenbus_thread, NULL, NULL,
862181624Skmacy							 RFHIGHPID, 0, "xenbus");
863181624Skmacy
864181624Skmacy		return err;
865181624Skmacy}
866181624Skmacy
867181624Skmacy
868181624Skmacy/*
869181624Skmacy * Local variables:
870181624Skmacy *  c-file-style: "bsd"
871181624Skmacy *  indent-tabs-mode: t
872181624Skmacy *  c-indent-level: 4
873181624Skmacy *  c-basic-offset: 8
874181624Skmacy *  tab-width: 4
875181624Skmacy * End:
876181624Skmacy */
877