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