1843e1988Sjohnlev /* 2843e1988Sjohnlev * CDDL HEADER START 3843e1988Sjohnlev * 4843e1988Sjohnlev * The contents of this file are subject to the terms of the 5843e1988Sjohnlev * Common Development and Distribution License (the "License"). 6843e1988Sjohnlev * You may not use this file except in compliance with the License. 7843e1988Sjohnlev * 8843e1988Sjohnlev * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9843e1988Sjohnlev * or http://www.opensolaris.org/os/licensing. 10843e1988Sjohnlev * See the License for the specific language governing permissions 11843e1988Sjohnlev * and limitations under the License. 12843e1988Sjohnlev * 13843e1988Sjohnlev * When distributing Covered Code, include this CDDL HEADER in each 14843e1988Sjohnlev * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15843e1988Sjohnlev * If applicable, add the following below this CDDL HEADER, with the 16843e1988Sjohnlev * fields enclosed by brackets "[]" replaced with your own identifying 17843e1988Sjohnlev * information: Portions Copyright [yyyy] [name of copyright owner] 18843e1988Sjohnlev * 19843e1988Sjohnlev * CDDL HEADER END 20843e1988Sjohnlev */ 21843e1988Sjohnlev 22843e1988Sjohnlev /* 237f0b8309SEdward Pilatowicz * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24843e1988Sjohnlev * Use is subject to license terms. 25843e1988Sjohnlev */ 26843e1988Sjohnlev 27843e1988Sjohnlev /* 28843e1988Sjohnlev * 29843e1988Sjohnlev * xenbus_xs.c 30843e1988Sjohnlev * 31843e1988Sjohnlev * This is the kernel equivalent of the "xs" library. We don't need everything 32843e1988Sjohnlev * and we use xenbus_comms for communication. 33843e1988Sjohnlev * 34843e1988Sjohnlev * Copyright (C) 2005 Rusty Russell, IBM Corporation 35843e1988Sjohnlev * 36843e1988Sjohnlev * This file may be distributed separately from the Linux kernel, or 37843e1988Sjohnlev * incorporated into other software packages, subject to the following license: 38843e1988Sjohnlev * 39843e1988Sjohnlev * Permission is hereby granted, free of charge, to any person obtaining a copy 40843e1988Sjohnlev * of this source file (the "Software"), to deal in the Software without 41843e1988Sjohnlev * restriction, including without limitation the rights to use, copy, modify, 42843e1988Sjohnlev * merge, publish, distribute, sublicense, and/or sell copies of the Software, 43843e1988Sjohnlev * and to permit persons to whom the Software is furnished to do so, subject to 44843e1988Sjohnlev * the following conditions: 45843e1988Sjohnlev * 46843e1988Sjohnlev * The above copyright notice and this permission notice shall be included in 47843e1988Sjohnlev * all copies or substantial portions of the Software. 48843e1988Sjohnlev * 49843e1988Sjohnlev * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 50843e1988Sjohnlev * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 51843e1988Sjohnlev * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 52843e1988Sjohnlev * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 53843e1988Sjohnlev * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 54843e1988Sjohnlev * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 55843e1988Sjohnlev * IN THE SOFTWARE. 56843e1988Sjohnlev */ 57843e1988Sjohnlev 58843e1988Sjohnlev /* 59843e1988Sjohnlev * NOTE: To future maintainers of the Solaris version of this file: 60843e1988Sjohnlev * I found the Linux version of this code to be very disgusting in 61843e1988Sjohnlev * overloading pointers and error codes into void * return values. 62843e1988Sjohnlev * The main difference you will find is that all such usage is changed 63843e1988Sjohnlev * to pass pointers to void* to be filled in with return values and 64843e1988Sjohnlev * the functions return error codes. 65843e1988Sjohnlev */ 66843e1988Sjohnlev 67843e1988Sjohnlev #include <sys/errno.h> 68843e1988Sjohnlev #include <sys/types.h> 69843e1988Sjohnlev #include <sys/sysmacros.h> 70843e1988Sjohnlev #include <sys/uio.h> 71843e1988Sjohnlev #include <sys/mutex.h> 72843e1988Sjohnlev #include <sys/condvar.h> 73843e1988Sjohnlev #include <sys/rwlock.h> 74843e1988Sjohnlev #include <sys/disp.h> 75843e1988Sjohnlev #include <sys/ddi.h> 76843e1988Sjohnlev #include <sys/sunddi.h> 77843e1988Sjohnlev #include <sys/avintr.h> 78843e1988Sjohnlev #include <sys/cmn_err.h> 79551bc2a6Smrj #include <sys/mach_mmu.h> 80843e1988Sjohnlev #include <util/sscanf.h> 81843e1988Sjohnlev #define _XSD_ERRORS_DEFINED 82551bc2a6Smrj #ifdef XPV_HVM_DRIVER 83551bc2a6Smrj #include <sys/xpv_support.h> 84551bc2a6Smrj #endif 85843e1988Sjohnlev #include <sys/hypervisor.h> 86ab4a9bebSjohnlev #include <sys/taskq.h> 87ab4a9bebSjohnlev #include <sys/sdt.h> 88843e1988Sjohnlev #include <xen/sys/xenbus_impl.h> 89843e1988Sjohnlev #include <xen/sys/xenbus_comms.h> 90843e1988Sjohnlev #include <xen/sys/xendev.h> 91843e1988Sjohnlev #include <xen/public/io/xs_wire.h> 92843e1988Sjohnlev 93843e1988Sjohnlev #define streq(a, b) (strcmp((a), (b)) == 0) 94843e1988Sjohnlev 95843e1988Sjohnlev #define list_empty(list) (list_head(list) == NULL) 96843e1988Sjohnlev 97843e1988Sjohnlev struct xs_stored_msg { 98349b53ddSStuart Maybee list_node_t list; 99843e1988Sjohnlev 100843e1988Sjohnlev struct xsd_sockmsg hdr; 101843e1988Sjohnlev 102843e1988Sjohnlev union { 103843e1988Sjohnlev /* Queued replies. */ 104843e1988Sjohnlev struct { 105843e1988Sjohnlev char *body; 106843e1988Sjohnlev } reply; 107843e1988Sjohnlev 108843e1988Sjohnlev /* Queued watch events. */ 109843e1988Sjohnlev struct { 110843e1988Sjohnlev struct xenbus_watch *handle; 111843e1988Sjohnlev char **vec; 112843e1988Sjohnlev unsigned int vec_size; 113843e1988Sjohnlev } watch; 114843e1988Sjohnlev } un; 115843e1988Sjohnlev }; 116843e1988Sjohnlev 117843e1988Sjohnlev static struct xs_handle { 118843e1988Sjohnlev /* A list of replies. Currently only one will ever be outstanding. */ 119843e1988Sjohnlev list_t reply_list; 120843e1988Sjohnlev kmutex_t reply_lock; 121843e1988Sjohnlev kcondvar_t reply_cv; 122843e1988Sjohnlev 123843e1988Sjohnlev /* One request at a time. */ 124843e1988Sjohnlev kmutex_t request_mutex; 125843e1988Sjohnlev 126843e1988Sjohnlev /* Protect transactions against save/restore. */ 127843e1988Sjohnlev krwlock_t suspend_lock; 128843e1988Sjohnlev } xs_state; 129843e1988Sjohnlev 130843e1988Sjohnlev static int last_req_id; 131843e1988Sjohnlev 132843e1988Sjohnlev /* 133843e1988Sjohnlev * List of clients wanting a xenstore up notification, and a lock to protect it 134843e1988Sjohnlev */ 135843e1988Sjohnlev static boolean_t xenstore_up; 136843e1988Sjohnlev static list_t notify_list; 137843e1988Sjohnlev static kmutex_t notify_list_lock; 138843e1988Sjohnlev static taskq_t *xenbus_taskq; 139843e1988Sjohnlev 140843e1988Sjohnlev /* List of registered watches, and a lock to protect it. */ 141843e1988Sjohnlev static list_t watches; 142843e1988Sjohnlev static kmutex_t watches_lock; 143843e1988Sjohnlev 144843e1988Sjohnlev /* List of pending watch callback events, and a lock to protect it. */ 145843e1988Sjohnlev static list_t watch_events; 146843e1988Sjohnlev static kmutex_t watch_events_lock; 147843e1988Sjohnlev 148843e1988Sjohnlev /* 149843e1988Sjohnlev * Details of the xenwatch callback kernel thread. The thread waits on the 150843e1988Sjohnlev * watch_events_cv for work to do (queued on watch_events list). When it 151843e1988Sjohnlev * wakes up it acquires the xenwatch_mutex before reading the list and 152843e1988Sjohnlev * carrying out work. 153843e1988Sjohnlev */ 154843e1988Sjohnlev static kmutex_t xenwatch_mutex; 155843e1988Sjohnlev static kcondvar_t watch_events_cv; 156843e1988Sjohnlev 157843e1988Sjohnlev static int process_msg(void); 158843e1988Sjohnlev 159843e1988Sjohnlev static int 160843e1988Sjohnlev get_error(const char *errorstring) 161843e1988Sjohnlev { 162843e1988Sjohnlev unsigned int i; 163843e1988Sjohnlev 164843e1988Sjohnlev for (i = 0; !streq(errorstring, xsd_errors[i].errstring); i++) { 165843e1988Sjohnlev if (i == (sizeof (xsd_errors) / sizeof (xsd_errors[0])) - 1) { 166843e1988Sjohnlev cmn_err(CE_WARN, 167843e1988Sjohnlev "XENBUS xen store gave: unknown error %s", 168843e1988Sjohnlev errorstring); 169843e1988Sjohnlev return (EINVAL); 170843e1988Sjohnlev } 171843e1988Sjohnlev } 172843e1988Sjohnlev return (xsd_errors[i].errnum); 173843e1988Sjohnlev } 174843e1988Sjohnlev 175843e1988Sjohnlev /* 176843e1988Sjohnlev * Read a synchronous reply from xenstore. Since we can return early before 177843e1988Sjohnlev * reading a relevant reply, we discard any messages not matching the request 178843e1988Sjohnlev * ID. Caller must free returned message on success. 179843e1988Sjohnlev */ 180843e1988Sjohnlev static int 181843e1988Sjohnlev read_reply(struct xsd_sockmsg *req_hdr, struct xs_stored_msg **reply) 182843e1988Sjohnlev { 183843e1988Sjohnlev extern int do_polled_io; 184843e1988Sjohnlev 185843e1988Sjohnlev mutex_enter(&xs_state.reply_lock); 186843e1988Sjohnlev 187843e1988Sjohnlev for (;;) { 188843e1988Sjohnlev while (list_empty(&xs_state.reply_list)) { 189843e1988Sjohnlev if (interrupts_unleashed && !do_polled_io) { 190843e1988Sjohnlev if (cv_wait_sig(&xs_state.reply_cv, 191843e1988Sjohnlev &xs_state.reply_lock) == 0) { 192843e1988Sjohnlev mutex_exit(&xs_state.reply_lock); 193843e1988Sjohnlev *reply = NULL; 194843e1988Sjohnlev return (EINTR); 195843e1988Sjohnlev } 196843e1988Sjohnlev } else { /* polled mode needed for early probes */ 197843e1988Sjohnlev mutex_exit(&xs_state.reply_lock); 198843e1988Sjohnlev (void) HYPERVISOR_yield(); 199843e1988Sjohnlev (void) process_msg(); 200843e1988Sjohnlev mutex_enter(&xs_state.reply_lock); 201843e1988Sjohnlev } 202843e1988Sjohnlev } 203843e1988Sjohnlev 204843e1988Sjohnlev *reply = list_head(&xs_state.reply_list); 205843e1988Sjohnlev list_remove(&xs_state.reply_list, *reply); 206843e1988Sjohnlev 207843e1988Sjohnlev if ((*reply)->hdr.req_id == req_hdr->req_id) 208843e1988Sjohnlev break; 209843e1988Sjohnlev } 210843e1988Sjohnlev 211843e1988Sjohnlev mutex_exit(&xs_state.reply_lock); 212843e1988Sjohnlev return (0); 213843e1988Sjohnlev } 214843e1988Sjohnlev 215843e1988Sjohnlev /* Emergency write. */ 216843e1988Sjohnlev void 217843e1988Sjohnlev xenbus_debug_write(const char *str, unsigned int count) 218843e1988Sjohnlev { 219843e1988Sjohnlev struct xsd_sockmsg msg = { 0 }; 220843e1988Sjohnlev 221843e1988Sjohnlev msg.type = XS_DEBUG; 222843e1988Sjohnlev msg.len = sizeof ("print") + count + 1; 223843e1988Sjohnlev 224843e1988Sjohnlev mutex_enter(&xs_state.request_mutex); 225843e1988Sjohnlev (void) xb_write(&msg, sizeof (msg)); 226843e1988Sjohnlev (void) xb_write("print", sizeof ("print")); 227843e1988Sjohnlev (void) xb_write(str, count); 228843e1988Sjohnlev (void) xb_write("", 1); 229843e1988Sjohnlev mutex_exit(&xs_state.request_mutex); 230843e1988Sjohnlev } 231843e1988Sjohnlev 232843e1988Sjohnlev /* 233843e1988Sjohnlev * This is pretty unpleasant. First off, there's the horrible logic around 234843e1988Sjohnlev * suspend_lock and transactions. Also, we can be interrupted either before we 235843e1988Sjohnlev * write a message, or before we receive a reply. A client that wants to 236843e1988Sjohnlev * survive this can't know which case happened. Luckily all clients don't care 237843e1988Sjohnlev * about signals currently, and the alternative (a hard wait on a userspace 238843e1988Sjohnlev * daemon) isn't exactly preferable. Caller must free 'reply' on success. 239843e1988Sjohnlev */ 240843e1988Sjohnlev int 241843e1988Sjohnlev xenbus_dev_request_and_reply(struct xsd_sockmsg *msg, void **reply) 242843e1988Sjohnlev { 243843e1988Sjohnlev struct xsd_sockmsg req_msg = *msg; 244843e1988Sjohnlev struct xs_stored_msg *reply_msg = NULL; 245843e1988Sjohnlev int err; 246843e1988Sjohnlev 247843e1988Sjohnlev if (req_msg.type == XS_TRANSACTION_START) 248843e1988Sjohnlev rw_enter(&xs_state.suspend_lock, RW_READER); 249843e1988Sjohnlev 250843e1988Sjohnlev mutex_enter(&xs_state.request_mutex); 251843e1988Sjohnlev 252843e1988Sjohnlev msg->req_id = last_req_id++; 253843e1988Sjohnlev 254843e1988Sjohnlev err = xb_write(msg, sizeof (*msg) + msg->len); 255843e1988Sjohnlev if (err) { 256843e1988Sjohnlev if (req_msg.type == XS_TRANSACTION_START) 257843e1988Sjohnlev rw_exit(&xs_state.suspend_lock); 258843e1988Sjohnlev msg->type = XS_ERROR; 259843e1988Sjohnlev *reply = NULL; 260843e1988Sjohnlev goto out; 261843e1988Sjohnlev } 262843e1988Sjohnlev 263843e1988Sjohnlev err = read_reply(msg, &reply_msg); 264843e1988Sjohnlev 265843e1988Sjohnlev if (err) { 266843e1988Sjohnlev if (msg->type == XS_TRANSACTION_START) 267843e1988Sjohnlev rw_exit(&xs_state.suspend_lock); 268843e1988Sjohnlev *reply = NULL; 269843e1988Sjohnlev goto out; 270843e1988Sjohnlev } 271843e1988Sjohnlev 272843e1988Sjohnlev *reply = reply_msg->un.reply.body; 273843e1988Sjohnlev *msg = reply_msg->hdr; 274843e1988Sjohnlev 275843e1988Sjohnlev if (reply_msg->hdr.type == XS_TRANSACTION_END) 276843e1988Sjohnlev rw_exit(&xs_state.suspend_lock); 277843e1988Sjohnlev 278843e1988Sjohnlev out: 279843e1988Sjohnlev if (reply_msg != NULL) 280843e1988Sjohnlev kmem_free(reply_msg, sizeof (*reply_msg)); 281843e1988Sjohnlev 282843e1988Sjohnlev mutex_exit(&xs_state.request_mutex); 283843e1988Sjohnlev return (err); 284843e1988Sjohnlev } 285843e1988Sjohnlev 286843e1988Sjohnlev /* 287843e1988Sjohnlev * Send message to xs, return errcode, rval filled in with pointer 288843e1988Sjohnlev * to kmem_alloc'ed reply. 289843e1988Sjohnlev */ 290843e1988Sjohnlev static int 291843e1988Sjohnlev xs_talkv(xenbus_transaction_t t, 292843e1988Sjohnlev enum xsd_sockmsg_type type, 293843e1988Sjohnlev const iovec_t *iovec, 294843e1988Sjohnlev unsigned int num_vecs, 295843e1988Sjohnlev void **rval, 296843e1988Sjohnlev unsigned int *len) 297843e1988Sjohnlev { 298843e1988Sjohnlev struct xsd_sockmsg msg; 299843e1988Sjohnlev struct xs_stored_msg *reply_msg; 300843e1988Sjohnlev char *reply; 301843e1988Sjohnlev unsigned int i; 302843e1988Sjohnlev int err; 303843e1988Sjohnlev 304843e1988Sjohnlev msg.tx_id = (uint32_t)(unsigned long)t; 305843e1988Sjohnlev msg.type = type; 306843e1988Sjohnlev msg.len = 0; 307843e1988Sjohnlev for (i = 0; i < num_vecs; i++) 308843e1988Sjohnlev msg.len += iovec[i].iov_len; 309843e1988Sjohnlev 310843e1988Sjohnlev mutex_enter(&xs_state.request_mutex); 311843e1988Sjohnlev 312843e1988Sjohnlev msg.req_id = last_req_id++; 313843e1988Sjohnlev 314843e1988Sjohnlev err = xb_write(&msg, sizeof (msg)); 315843e1988Sjohnlev if (err) { 316843e1988Sjohnlev mutex_exit(&xs_state.request_mutex); 317843e1988Sjohnlev return (err); 318843e1988Sjohnlev } 319843e1988Sjohnlev 320843e1988Sjohnlev for (i = 0; i < num_vecs; i++) { 321843e1988Sjohnlev err = xb_write(iovec[i].iov_base, iovec[i].iov_len); 322843e1988Sjohnlev if (err) { 323843e1988Sjohnlev mutex_exit(&xs_state.request_mutex); 324843e1988Sjohnlev return (err); 325843e1988Sjohnlev } 326843e1988Sjohnlev } 327843e1988Sjohnlev 328843e1988Sjohnlev err = read_reply(&msg, &reply_msg); 329843e1988Sjohnlev 330843e1988Sjohnlev mutex_exit(&xs_state.request_mutex); 331843e1988Sjohnlev 332843e1988Sjohnlev if (err) 333843e1988Sjohnlev return (err); 334843e1988Sjohnlev 335843e1988Sjohnlev reply = reply_msg->un.reply.body; 336843e1988Sjohnlev 337843e1988Sjohnlev if (reply_msg->hdr.type == XS_ERROR) { 338843e1988Sjohnlev err = get_error(reply); 339843e1988Sjohnlev kmem_free(reply, reply_msg->hdr.len + 1); 340843e1988Sjohnlev goto out; 341843e1988Sjohnlev } 342843e1988Sjohnlev 343843e1988Sjohnlev if (len != NULL) 344843e1988Sjohnlev *len = reply_msg->hdr.len + 1; 345843e1988Sjohnlev 346843e1988Sjohnlev ASSERT(reply_msg->hdr.type == type); 347843e1988Sjohnlev 348843e1988Sjohnlev if (rval != NULL) 349843e1988Sjohnlev *rval = reply; 350843e1988Sjohnlev else 351843e1988Sjohnlev kmem_free(reply, reply_msg->hdr.len + 1); 352843e1988Sjohnlev 353843e1988Sjohnlev out: 354843e1988Sjohnlev kmem_free(reply_msg, sizeof (*reply_msg)); 355843e1988Sjohnlev return (err); 356843e1988Sjohnlev } 357843e1988Sjohnlev 358843e1988Sjohnlev /* Simplified version of xs_talkv: single message. */ 359843e1988Sjohnlev static int 360843e1988Sjohnlev xs_single(xenbus_transaction_t t, 361843e1988Sjohnlev enum xsd_sockmsg_type type, 362843e1988Sjohnlev const char *string, void **ret, 363843e1988Sjohnlev unsigned int *len) 364843e1988Sjohnlev { 365843e1988Sjohnlev iovec_t iovec; 366843e1988Sjohnlev 367843e1988Sjohnlev iovec.iov_base = (char *)string; 368843e1988Sjohnlev iovec.iov_len = strlen(string) + 1; 369843e1988Sjohnlev return (xs_talkv(t, type, &iovec, 1, ret, len)); 370843e1988Sjohnlev } 371843e1988Sjohnlev 372843e1988Sjohnlev static unsigned int 373843e1988Sjohnlev count_strings(const char *strings, unsigned int len) 374843e1988Sjohnlev { 375843e1988Sjohnlev unsigned int num; 376843e1988Sjohnlev const char *p; 377843e1988Sjohnlev 378843e1988Sjohnlev for (p = strings, num = 0; p < strings + len; p += strlen(p) + 1) 379843e1988Sjohnlev num++; 380843e1988Sjohnlev 381843e1988Sjohnlev return (num); 382843e1988Sjohnlev } 383843e1988Sjohnlev 384843e1988Sjohnlev /* Return the path to dir with /name appended. Buffer must be kmem_free()'ed */ 385843e1988Sjohnlev static char * 386843e1988Sjohnlev join(const char *dir, const char *name) 387843e1988Sjohnlev { 388843e1988Sjohnlev char *buffer; 389843e1988Sjohnlev size_t slashlen; 390843e1988Sjohnlev 391843e1988Sjohnlev slashlen = streq(name, "") ? 0 : 1; 392843e1988Sjohnlev buffer = kmem_alloc(strlen(dir) + slashlen + strlen(name) + 1, 393843e1988Sjohnlev KM_SLEEP); 394843e1988Sjohnlev 395843e1988Sjohnlev (void) strcpy(buffer, dir); 396843e1988Sjohnlev if (slashlen != 0) { 397843e1988Sjohnlev (void) strcat(buffer, "/"); 398843e1988Sjohnlev (void) strcat(buffer, name); 399843e1988Sjohnlev } 400843e1988Sjohnlev return (buffer); 401843e1988Sjohnlev } 402843e1988Sjohnlev 403843e1988Sjohnlev static char ** 404843e1988Sjohnlev split(char *strings, unsigned int len, unsigned int *num) 405843e1988Sjohnlev { 406843e1988Sjohnlev char *p, **ret; 407843e1988Sjohnlev 408843e1988Sjohnlev /* Count the strings. */ 409843e1988Sjohnlev if ((*num = count_strings(strings, len - 1)) == 0) 410843e1988Sjohnlev return (NULL); 411843e1988Sjohnlev 412843e1988Sjohnlev /* Transfer to one big alloc for easy freeing. */ 413843e1988Sjohnlev ret = kmem_alloc(*num * sizeof (char *) + (len - 1), KM_SLEEP); 414843e1988Sjohnlev (void) memcpy(&ret[*num], strings, len - 1); 415843e1988Sjohnlev kmem_free(strings, len); 416843e1988Sjohnlev 417843e1988Sjohnlev strings = (char *)&ret[*num]; 418843e1988Sjohnlev for (p = strings, *num = 0; p < strings + (len - 1); 419843e1988Sjohnlev p += strlen(p) + 1) { 420843e1988Sjohnlev ret[(*num)++] = p; 421843e1988Sjohnlev } 422843e1988Sjohnlev 423843e1988Sjohnlev return (ret); 424843e1988Sjohnlev } 425843e1988Sjohnlev 426843e1988Sjohnlev char ** 427843e1988Sjohnlev xenbus_directory(xenbus_transaction_t t, 428843e1988Sjohnlev const char *dir, const char *node, unsigned int *num) 429843e1988Sjohnlev { 430843e1988Sjohnlev char *strings, *path; 431843e1988Sjohnlev unsigned int len; 432843e1988Sjohnlev int err; 433843e1988Sjohnlev 434843e1988Sjohnlev path = join(dir, node); 435843e1988Sjohnlev err = xs_single(t, XS_DIRECTORY, path, (void **)&strings, &len); 436843e1988Sjohnlev kmem_free(path, strlen(path) + 1); 437843e1988Sjohnlev if (err != 0 || strings == NULL) { 438843e1988Sjohnlev /* sigh, we lose error code info here */ 439843e1988Sjohnlev *num = 0; 440843e1988Sjohnlev return (NULL); 441843e1988Sjohnlev } 442843e1988Sjohnlev 443843e1988Sjohnlev return (split(strings, len, num)); 444843e1988Sjohnlev } 445843e1988Sjohnlev 4467f0b8309SEdward Pilatowicz /* Check if a path exists. */ 4477f0b8309SEdward Pilatowicz boolean_t 4487f0b8309SEdward Pilatowicz xenbus_exists(const char *dir, const char *node) 4497f0b8309SEdward Pilatowicz { 4507f0b8309SEdward Pilatowicz void *p; 4517f0b8309SEdward Pilatowicz uint_t n; 4527f0b8309SEdward Pilatowicz 4537f0b8309SEdward Pilatowicz if (xenbus_read(XBT_NULL, dir, node, &p, &n) != 0) 4547f0b8309SEdward Pilatowicz return (B_FALSE); 4557f0b8309SEdward Pilatowicz kmem_free(p, n); 4567f0b8309SEdward Pilatowicz return (B_TRUE); 4577f0b8309SEdward Pilatowicz } 4587f0b8309SEdward Pilatowicz 4597f0b8309SEdward Pilatowicz /* Check if a directory path exists. */ 4607f0b8309SEdward Pilatowicz boolean_t 4617f0b8309SEdward Pilatowicz xenbus_exists_dir(const char *dir, const char *node) 462843e1988Sjohnlev { 463843e1988Sjohnlev char **d; 464843e1988Sjohnlev unsigned int dir_n; 465843e1988Sjohnlev int i, len; 466843e1988Sjohnlev 4677f0b8309SEdward Pilatowicz d = xenbus_directory(XBT_NULL, dir, node, &dir_n); 468843e1988Sjohnlev if (d == NULL) 4697f0b8309SEdward Pilatowicz return (B_FALSE); 470843e1988Sjohnlev for (i = 0, len = 0; i < dir_n; i++) 471843e1988Sjohnlev len += strlen(d[i]) + 1 + sizeof (char *); 472843e1988Sjohnlev kmem_free(d, len); 4737f0b8309SEdward Pilatowicz return (B_TRUE); 474843e1988Sjohnlev } 475843e1988Sjohnlev 476843e1988Sjohnlev /* 477843e1988Sjohnlev * Get the value of a single file. 478843e1988Sjohnlev * Returns a kmem_alloced value in retp: call kmem_free() on it after use. 479843e1988Sjohnlev * len indicates length in bytes. 480843e1988Sjohnlev */ 481843e1988Sjohnlev int 482843e1988Sjohnlev xenbus_read(xenbus_transaction_t t, 483843e1988Sjohnlev const char *dir, const char *node, void **retp, unsigned int *len) 484843e1988Sjohnlev { 485843e1988Sjohnlev char *path; 486843e1988Sjohnlev int err; 487843e1988Sjohnlev 488843e1988Sjohnlev path = join(dir, node); 489843e1988Sjohnlev err = xs_single(t, XS_READ, path, retp, len); 490843e1988Sjohnlev kmem_free(path, strlen(path) + 1); 491843e1988Sjohnlev return (err); 492843e1988Sjohnlev } 493843e1988Sjohnlev 4947f0b8309SEdward Pilatowicz int 4957f0b8309SEdward Pilatowicz xenbus_read_str(const char *dir, const char *node, char **retp) 4967f0b8309SEdward Pilatowicz { 4977f0b8309SEdward Pilatowicz uint_t n; 4987f0b8309SEdward Pilatowicz int err; 4997f0b8309SEdward Pilatowicz char *str; 5007f0b8309SEdward Pilatowicz 5017f0b8309SEdward Pilatowicz /* 5027f0b8309SEdward Pilatowicz * Since we access the xenbus value immediatly we can't be 5037f0b8309SEdward Pilatowicz * part of a transaction. 5047f0b8309SEdward Pilatowicz */ 5057f0b8309SEdward Pilatowicz if ((err = xenbus_read(XBT_NULL, dir, node, (void **)&str, &n)) != 0) 5067f0b8309SEdward Pilatowicz return (err); 5077f0b8309SEdward Pilatowicz ASSERT((str != NULL) && (n > 0)); 5087f0b8309SEdward Pilatowicz 5097f0b8309SEdward Pilatowicz /* 5107f0b8309SEdward Pilatowicz * Why bother with this? Because xenbus is truly annoying in the 5117f0b8309SEdward Pilatowicz * fact that when it returns a string, it doesn't guarantee that 5127f0b8309SEdward Pilatowicz * the memory that holds the string is of size strlen() + 1. 5137f0b8309SEdward Pilatowicz * This forces callers to keep track of the size of the memory 5147f0b8309SEdward Pilatowicz * containing the string. Ugh. We'll work around this by 5157f0b8309SEdward Pilatowicz * re-allocate strings to always be of size strlen() + 1. 5167f0b8309SEdward Pilatowicz */ 5177f0b8309SEdward Pilatowicz *retp = strdup(str); 5187f0b8309SEdward Pilatowicz kmem_free(str, n); 5197f0b8309SEdward Pilatowicz return (0); 5207f0b8309SEdward Pilatowicz } 5217f0b8309SEdward Pilatowicz 522843e1988Sjohnlev /* 523843e1988Sjohnlev * Write the value of a single file. 524843e1988Sjohnlev * Returns err on failure. 525843e1988Sjohnlev */ 526843e1988Sjohnlev int 527843e1988Sjohnlev xenbus_write(xenbus_transaction_t t, 528843e1988Sjohnlev const char *dir, const char *node, const char *string) 529843e1988Sjohnlev { 530843e1988Sjohnlev char *path; 531843e1988Sjohnlev iovec_t iovec[2]; 532843e1988Sjohnlev int ret; 533843e1988Sjohnlev 534843e1988Sjohnlev path = join(dir, node); 535843e1988Sjohnlev 536843e1988Sjohnlev iovec[0].iov_base = (void *)path; 537843e1988Sjohnlev iovec[0].iov_len = strlen(path) + 1; 538843e1988Sjohnlev iovec[1].iov_base = (void *)string; 539843e1988Sjohnlev iovec[1].iov_len = strlen(string); 540843e1988Sjohnlev 541843e1988Sjohnlev ret = xs_talkv(t, XS_WRITE, iovec, 2, NULL, NULL); 542843e1988Sjohnlev kmem_free(path, iovec[0].iov_len); 543843e1988Sjohnlev return (ret); 544843e1988Sjohnlev } 545843e1988Sjohnlev 546843e1988Sjohnlev /* Create a new directory. */ 547843e1988Sjohnlev int 548843e1988Sjohnlev xenbus_mkdir(xenbus_transaction_t t, const char *dir, const char *node) 549843e1988Sjohnlev { 550843e1988Sjohnlev char *path; 551843e1988Sjohnlev int ret; 552843e1988Sjohnlev 553843e1988Sjohnlev path = join(dir, node); 554843e1988Sjohnlev ret = xs_single(t, XS_MKDIR, path, NULL, NULL); 555843e1988Sjohnlev kmem_free(path, strlen(path) + 1); 556843e1988Sjohnlev return (ret); 557843e1988Sjohnlev } 558843e1988Sjohnlev 559843e1988Sjohnlev /* Destroy a file or directory (directories must be empty). */ 560843e1988Sjohnlev int 561843e1988Sjohnlev xenbus_rm(xenbus_transaction_t t, const char *dir, const char *node) 562843e1988Sjohnlev { 563843e1988Sjohnlev char *path; 564843e1988Sjohnlev int ret; 565843e1988Sjohnlev 566843e1988Sjohnlev path = join(dir, node); 567843e1988Sjohnlev ret = xs_single(t, XS_RM, path, NULL, NULL); 568843e1988Sjohnlev kmem_free(path, strlen(path) + 1); 569843e1988Sjohnlev return (ret); 570843e1988Sjohnlev } 571843e1988Sjohnlev 572843e1988Sjohnlev /* 573843e1988Sjohnlev * Start a transaction: changes by others will not be seen during this 574843e1988Sjohnlev * transaction, and changes will not be visible to others until end. 575843e1988Sjohnlev */ 576843e1988Sjohnlev int 577843e1988Sjohnlev xenbus_transaction_start(xenbus_transaction_t *t) 578843e1988Sjohnlev { 579843e1988Sjohnlev void *id_str; 580843e1988Sjohnlev unsigned long id; 581843e1988Sjohnlev int err; 582843e1988Sjohnlev unsigned int len; 583843e1988Sjohnlev 584843e1988Sjohnlev rw_enter(&xs_state.suspend_lock, RW_READER); 585843e1988Sjohnlev 586843e1988Sjohnlev err = xs_single(XBT_NULL, XS_TRANSACTION_START, "", &id_str, &len); 587843e1988Sjohnlev if (err) { 588843e1988Sjohnlev rw_exit(&xs_state.suspend_lock); 589843e1988Sjohnlev return (err); 590843e1988Sjohnlev } 591843e1988Sjohnlev 592843e1988Sjohnlev (void) ddi_strtoul((char *)id_str, NULL, 0, &id); 593843e1988Sjohnlev *t = (xenbus_transaction_t)id; 594843e1988Sjohnlev kmem_free(id_str, len); 595843e1988Sjohnlev 596843e1988Sjohnlev return (0); 597843e1988Sjohnlev } 598843e1988Sjohnlev 599843e1988Sjohnlev /* 600843e1988Sjohnlev * End a transaction. 601843e1988Sjohnlev * If abandon is true, transaction is discarded instead of committed. 602843e1988Sjohnlev */ 603843e1988Sjohnlev int 604843e1988Sjohnlev xenbus_transaction_end(xenbus_transaction_t t, int abort) 605843e1988Sjohnlev { 606843e1988Sjohnlev char abortstr[2]; 607843e1988Sjohnlev int err; 608843e1988Sjohnlev 609843e1988Sjohnlev if (abort) 610843e1988Sjohnlev (void) strcpy(abortstr, "F"); 611843e1988Sjohnlev else 612843e1988Sjohnlev (void) strcpy(abortstr, "T"); 613843e1988Sjohnlev 614843e1988Sjohnlev err = xs_single(t, XS_TRANSACTION_END, abortstr, NULL, NULL); 615843e1988Sjohnlev 616843e1988Sjohnlev rw_exit(&xs_state.suspend_lock); 617843e1988Sjohnlev 618843e1988Sjohnlev return (err); 619843e1988Sjohnlev } 620843e1988Sjohnlev 621843e1988Sjohnlev /* 622843e1988Sjohnlev * Single read and scanf: returns errno or 0. This can only handle a single 623843e1988Sjohnlev * conversion specifier. 624843e1988Sjohnlev */ 625843e1988Sjohnlev /* SCANFLIKE4 */ 626843e1988Sjohnlev int 627843e1988Sjohnlev xenbus_scanf(xenbus_transaction_t t, 628843e1988Sjohnlev const char *dir, const char *node, const char *fmt, ...) 629843e1988Sjohnlev { 630843e1988Sjohnlev va_list ap; 631843e1988Sjohnlev int ret; 632843e1988Sjohnlev char *val; 633843e1988Sjohnlev unsigned int len; 634843e1988Sjohnlev 635843e1988Sjohnlev ret = xenbus_read(t, dir, node, (void **)&val, &len); 636843e1988Sjohnlev if (ret) 637843e1988Sjohnlev return (ret); 638843e1988Sjohnlev 639843e1988Sjohnlev va_start(ap, fmt); 640843e1988Sjohnlev if (vsscanf(val, fmt, ap) != 1) 641843e1988Sjohnlev ret = ERANGE; 642843e1988Sjohnlev va_end(ap); 643843e1988Sjohnlev kmem_free(val, len); 644843e1988Sjohnlev return (ret); 645843e1988Sjohnlev } 646843e1988Sjohnlev 647843e1988Sjohnlev /* Single printf and write: returns errno or 0. */ 648843e1988Sjohnlev /* PRINTFLIKE4 */ 649843e1988Sjohnlev int 650843e1988Sjohnlev xenbus_printf(xenbus_transaction_t t, 651843e1988Sjohnlev const char *dir, const char *node, const char *fmt, ...) 652843e1988Sjohnlev { 653843e1988Sjohnlev va_list ap; 654843e1988Sjohnlev int ret; 655843e1988Sjohnlev #define PRINTF_BUFFER_SIZE 4096 656843e1988Sjohnlev char *printf_buffer; 657843e1988Sjohnlev 658843e1988Sjohnlev printf_buffer = kmem_alloc(PRINTF_BUFFER_SIZE, KM_SLEEP); 659843e1988Sjohnlev 660843e1988Sjohnlev va_start(ap, fmt); 661843e1988Sjohnlev ret = vsnprintf(printf_buffer, PRINTF_BUFFER_SIZE, fmt, ap); 662843e1988Sjohnlev va_end(ap); 663843e1988Sjohnlev 664843e1988Sjohnlev ASSERT(ret <= PRINTF_BUFFER_SIZE-1); 665843e1988Sjohnlev ret = xenbus_write(t, dir, node, printf_buffer); 666843e1988Sjohnlev 667843e1988Sjohnlev kmem_free(printf_buffer, PRINTF_BUFFER_SIZE); 668843e1988Sjohnlev 669843e1988Sjohnlev return (ret); 670843e1988Sjohnlev } 671843e1988Sjohnlev 672843e1988Sjohnlev 673843e1988Sjohnlev /* Takes tuples of names, scanf-style args, and void **, NULL terminated. */ 674843e1988Sjohnlev int 675843e1988Sjohnlev xenbus_gather(xenbus_transaction_t t, const char *dir, ...) 676843e1988Sjohnlev { 677843e1988Sjohnlev va_list ap; 678843e1988Sjohnlev const char *name; 679843e1988Sjohnlev int ret = 0; 680843e1988Sjohnlev unsigned int len; 681843e1988Sjohnlev 682843e1988Sjohnlev va_start(ap, dir); 683843e1988Sjohnlev while (ret == 0 && (name = va_arg(ap, char *)) != NULL) { 684843e1988Sjohnlev const char *fmt = va_arg(ap, char *); 685843e1988Sjohnlev void *result = va_arg(ap, void *); 686843e1988Sjohnlev char *p; 687843e1988Sjohnlev 688843e1988Sjohnlev ret = xenbus_read(t, dir, name, (void **)&p, &len); 689843e1988Sjohnlev if (ret) 690843e1988Sjohnlev break; 691843e1988Sjohnlev if (fmt) { 692843e1988Sjohnlev ASSERT(result != NULL); 693843e1988Sjohnlev if (sscanf(p, fmt, result) != 1) 694843e1988Sjohnlev ret = EINVAL; 695843e1988Sjohnlev kmem_free(p, len); 696843e1988Sjohnlev } else 697843e1988Sjohnlev *(char **)result = p; 698843e1988Sjohnlev } 699843e1988Sjohnlev va_end(ap); 700843e1988Sjohnlev return (ret); 701843e1988Sjohnlev } 702843e1988Sjohnlev 703843e1988Sjohnlev static int 704843e1988Sjohnlev xs_watch(const char *path, const char *token) 705843e1988Sjohnlev { 706843e1988Sjohnlev iovec_t iov[2]; 707843e1988Sjohnlev 708843e1988Sjohnlev iov[0].iov_base = (void *)path; 709843e1988Sjohnlev iov[0].iov_len = strlen(path) + 1; 710843e1988Sjohnlev iov[1].iov_base = (void *)token; 711843e1988Sjohnlev iov[1].iov_len = strlen(token) + 1; 712843e1988Sjohnlev 713843e1988Sjohnlev return (xs_talkv(XBT_NULL, XS_WATCH, iov, 2, NULL, NULL)); 714843e1988Sjohnlev } 715843e1988Sjohnlev 716843e1988Sjohnlev static int 717843e1988Sjohnlev xs_unwatch(const char *path, const char *token) 718843e1988Sjohnlev { 719843e1988Sjohnlev iovec_t iov[2]; 720843e1988Sjohnlev 721843e1988Sjohnlev iov[0].iov_base = (char *)path; 722843e1988Sjohnlev iov[0].iov_len = strlen(path) + 1; 723843e1988Sjohnlev iov[1].iov_base = (char *)token; 724843e1988Sjohnlev iov[1].iov_len = strlen(token) + 1; 725843e1988Sjohnlev 726843e1988Sjohnlev return (xs_talkv(XBT_NULL, XS_UNWATCH, iov, 2, NULL, NULL)); 727843e1988Sjohnlev } 728843e1988Sjohnlev 729843e1988Sjohnlev static struct xenbus_watch * 730843e1988Sjohnlev find_watch(const char *token) 731843e1988Sjohnlev { 732843e1988Sjohnlev struct xenbus_watch *i, *cmp; 733843e1988Sjohnlev 734843e1988Sjohnlev (void) ddi_strtoul(token, NULL, 16, (unsigned long *)&cmp); 735843e1988Sjohnlev 736843e1988Sjohnlev for (i = list_head(&watches); i != NULL; i = list_next(&watches, i)) 737843e1988Sjohnlev if (i == cmp) 738843e1988Sjohnlev break; 739843e1988Sjohnlev 740843e1988Sjohnlev return (i); 741843e1988Sjohnlev } 742843e1988Sjohnlev 743843e1988Sjohnlev /* Register a xenstore state notify callback */ 744843e1988Sjohnlev int 745843e1988Sjohnlev xs_register_xenbus_callback(void (*callback)(int)) 746843e1988Sjohnlev { 747843e1988Sjohnlev struct xenbus_notify *xbn, *xnp; 748843e1988Sjohnlev 749843e1988Sjohnlev xbn = kmem_alloc(sizeof (struct xenbus_notify), KM_SLEEP); 750843e1988Sjohnlev xbn->notify_func = callback; 751843e1988Sjohnlev mutex_enter(¬ify_list_lock); 752843e1988Sjohnlev /* 753843e1988Sjohnlev * Make sure not already on the list 754843e1988Sjohnlev */ 755843e1988Sjohnlev xnp = list_head(¬ify_list); 756843e1988Sjohnlev for (; xnp != NULL; xnp = list_next(¬ify_list, xnp)) { 757843e1988Sjohnlev if (xnp->notify_func == callback) { 758843e1988Sjohnlev kmem_free(xbn, sizeof (struct xenbus_notify)); 759843e1988Sjohnlev mutex_exit(¬ify_list_lock); 760843e1988Sjohnlev return (EEXIST); 761843e1988Sjohnlev } 762843e1988Sjohnlev } 763843e1988Sjohnlev xnp = xbn; 764843e1988Sjohnlev list_insert_tail(¬ify_list, xbn); 765843e1988Sjohnlev done: 766843e1988Sjohnlev if (xenstore_up) 767843e1988Sjohnlev xnp->notify_func(XENSTORE_UP); 768843e1988Sjohnlev mutex_exit(¬ify_list_lock); 769843e1988Sjohnlev return (0); 770843e1988Sjohnlev } 771843e1988Sjohnlev 772843e1988Sjohnlev /* 773843e1988Sjohnlev * Notify clients of xenstore state 774843e1988Sjohnlev */ 775843e1988Sjohnlev static void 776843e1988Sjohnlev do_notify_callbacks(void *arg) 777843e1988Sjohnlev { 778843e1988Sjohnlev struct xenbus_notify *xnp; 779843e1988Sjohnlev 780843e1988Sjohnlev mutex_enter(¬ify_list_lock); 781843e1988Sjohnlev xnp = list_head(¬ify_list); 782843e1988Sjohnlev for (; xnp != NULL; xnp = list_next(¬ify_list, xnp)) { 783843e1988Sjohnlev xnp->notify_func((int)((uintptr_t)arg)); 784843e1988Sjohnlev } 785843e1988Sjohnlev mutex_exit(¬ify_list_lock); 786843e1988Sjohnlev } 787843e1988Sjohnlev 788843e1988Sjohnlev void 789843e1988Sjohnlev xs_notify_xenstore_up(void) 790843e1988Sjohnlev { 791843e1988Sjohnlev xenstore_up = B_TRUE; 792843e1988Sjohnlev (void) taskq_dispatch(xenbus_taskq, do_notify_callbacks, 793843e1988Sjohnlev (void *)XENSTORE_UP, 0); 794843e1988Sjohnlev } 795843e1988Sjohnlev 796843e1988Sjohnlev void 797843e1988Sjohnlev xs_notify_xenstore_down(void) 798843e1988Sjohnlev { 799843e1988Sjohnlev xenstore_up = B_FALSE; 800843e1988Sjohnlev (void) taskq_dispatch(xenbus_taskq, do_notify_callbacks, 801843e1988Sjohnlev (void *)XENSTORE_DOWN, 0); 802843e1988Sjohnlev } 803843e1988Sjohnlev 804843e1988Sjohnlev /* Register callback to watch this node. */ 805843e1988Sjohnlev int 806843e1988Sjohnlev register_xenbus_watch(struct xenbus_watch *watch) 807843e1988Sjohnlev { 808843e1988Sjohnlev /* Pointer in ascii is the token. */ 809843e1988Sjohnlev char token[sizeof (watch) * 2 + 1]; 810843e1988Sjohnlev int err; 811843e1988Sjohnlev 812843e1988Sjohnlev ASSERT(xenstore_up); 813843e1988Sjohnlev (void) snprintf(token, sizeof (token), "%lX", (long)watch); 814843e1988Sjohnlev 815843e1988Sjohnlev rw_enter(&xs_state.suspend_lock, RW_READER); 816843e1988Sjohnlev 817843e1988Sjohnlev mutex_enter(&watches_lock); 818843e1988Sjohnlev /* 819843e1988Sjohnlev * May be re-registering a watch if xenstore daemon was restarted 820843e1988Sjohnlev */ 821843e1988Sjohnlev if (find_watch(token) == NULL) 822843e1988Sjohnlev list_insert_tail(&watches, watch); 823843e1988Sjohnlev mutex_exit(&watches_lock); 824843e1988Sjohnlev 825ab4a9bebSjohnlev DTRACE_XPV3(xenbus__register__watch, const char *, watch->node, 826ab4a9bebSjohnlev uintptr_t, watch->callback, struct xenbus_watch *, watch); 827ab4a9bebSjohnlev 828843e1988Sjohnlev err = xs_watch(watch->node, token); 829843e1988Sjohnlev 830843e1988Sjohnlev /* Ignore errors due to multiple registration. */ 831843e1988Sjohnlev if ((err != 0) && (err != EEXIST)) { 832843e1988Sjohnlev mutex_enter(&watches_lock); 833843e1988Sjohnlev list_remove(&watches, watch); 834843e1988Sjohnlev mutex_exit(&watches_lock); 835843e1988Sjohnlev } 836843e1988Sjohnlev 837843e1988Sjohnlev rw_exit(&xs_state.suspend_lock); 838843e1988Sjohnlev 839843e1988Sjohnlev return (err); 840843e1988Sjohnlev } 841843e1988Sjohnlev 842843e1988Sjohnlev static void 843843e1988Sjohnlev free_stored_msg(struct xs_stored_msg *msg) 844843e1988Sjohnlev { 845843e1988Sjohnlev int i, len = 0; 846843e1988Sjohnlev 847843e1988Sjohnlev for (i = 0; i < msg->un.watch.vec_size; i++) 848843e1988Sjohnlev len += strlen(msg->un.watch.vec[i]) + 1 + sizeof (char *); 849843e1988Sjohnlev kmem_free(msg->un.watch.vec, len); 850843e1988Sjohnlev kmem_free(msg, sizeof (*msg)); 851843e1988Sjohnlev } 852843e1988Sjohnlev 853843e1988Sjohnlev void 854843e1988Sjohnlev unregister_xenbus_watch(struct xenbus_watch *watch) 855843e1988Sjohnlev { 856843e1988Sjohnlev struct xs_stored_msg *msg; 857843e1988Sjohnlev char token[sizeof (watch) * 2 + 1]; 858843e1988Sjohnlev int err; 859843e1988Sjohnlev 860843e1988Sjohnlev (void) snprintf(token, sizeof (token), "%lX", (long)watch); 861843e1988Sjohnlev 862843e1988Sjohnlev rw_enter(&xs_state.suspend_lock, RW_READER); 863843e1988Sjohnlev 864843e1988Sjohnlev mutex_enter(&watches_lock); 865843e1988Sjohnlev ASSERT(find_watch(token)); 866843e1988Sjohnlev list_remove(&watches, watch); 867843e1988Sjohnlev mutex_exit(&watches_lock); 868843e1988Sjohnlev 869ab4a9bebSjohnlev DTRACE_XPV3(xenbus__unregister__watch, const char *, watch->node, 870ab4a9bebSjohnlev uintptr_t, watch->callback, struct xenbus_watch *, watch); 871ab4a9bebSjohnlev 872843e1988Sjohnlev err = xs_unwatch(watch->node, token); 873843e1988Sjohnlev if (err) 874843e1988Sjohnlev cmn_err(CE_WARN, "XENBUS Failed to release watch %s: %d", 875843e1988Sjohnlev watch->node, err); 876843e1988Sjohnlev 877843e1988Sjohnlev rw_exit(&xs_state.suspend_lock); 878843e1988Sjohnlev 879843e1988Sjohnlev /* Cancel pending watch events. */ 880843e1988Sjohnlev mutex_enter(&watch_events_lock); 881843e1988Sjohnlev msg = list_head(&watch_events); 882843e1988Sjohnlev 883843e1988Sjohnlev while (msg != NULL) { 884843e1988Sjohnlev struct xs_stored_msg *tmp = list_next(&watch_events, msg); 885843e1988Sjohnlev if (msg->un.watch.handle == watch) { 886843e1988Sjohnlev list_remove(&watch_events, msg); 887843e1988Sjohnlev free_stored_msg(msg); 888843e1988Sjohnlev } 889843e1988Sjohnlev msg = tmp; 890843e1988Sjohnlev } 891843e1988Sjohnlev 892843e1988Sjohnlev mutex_exit(&watch_events_lock); 893843e1988Sjohnlev 894843e1988Sjohnlev /* Flush any currently-executing callback, unless we are it. :-) */ 895843e1988Sjohnlev if (mutex_owner(&xenwatch_mutex) != curthread) { 896843e1988Sjohnlev mutex_enter(&xenwatch_mutex); 897843e1988Sjohnlev mutex_exit(&xenwatch_mutex); 898843e1988Sjohnlev } 899843e1988Sjohnlev } 900843e1988Sjohnlev 901843e1988Sjohnlev void 902843e1988Sjohnlev xenbus_suspend(void) 903843e1988Sjohnlev { 904843e1988Sjohnlev rw_enter(&xs_state.suspend_lock, RW_WRITER); 905843e1988Sjohnlev mutex_enter(&xs_state.request_mutex); 906843e1988Sjohnlev 907843e1988Sjohnlev xb_suspend(); 908843e1988Sjohnlev } 909843e1988Sjohnlev 910843e1988Sjohnlev void 911843e1988Sjohnlev xenbus_resume(void) 912843e1988Sjohnlev { 913843e1988Sjohnlev struct xenbus_watch *watch; 914843e1988Sjohnlev char token[sizeof (watch) * 2 + 1]; 915843e1988Sjohnlev 916843e1988Sjohnlev mutex_exit(&xs_state.request_mutex); 917843e1988Sjohnlev 918843e1988Sjohnlev xb_init(); 919843e1988Sjohnlev xb_setup_intr(); 920843e1988Sjohnlev 921843e1988Sjohnlev /* No need for watches_lock: the suspend_lock is sufficient. */ 922843e1988Sjohnlev for (watch = list_head(&watches); watch != NULL; 923843e1988Sjohnlev watch = list_next(&watches, watch)) { 924843e1988Sjohnlev (void) snprintf(token, sizeof (token), "%lX", (long)watch); 925843e1988Sjohnlev (void) xs_watch(watch->node, token); 926843e1988Sjohnlev } 927843e1988Sjohnlev 928843e1988Sjohnlev rw_exit(&xs_state.suspend_lock); 929843e1988Sjohnlev } 930843e1988Sjohnlev 931843e1988Sjohnlev static void 932843e1988Sjohnlev xenwatch_thread(void) 933843e1988Sjohnlev { 934843e1988Sjohnlev struct xs_stored_msg *msg; 935ab4a9bebSjohnlev struct xenbus_watch *watch; 936843e1988Sjohnlev 937843e1988Sjohnlev for (;;) { 938843e1988Sjohnlev mutex_enter(&watch_events_lock); 939843e1988Sjohnlev while (list_empty(&watch_events)) 940843e1988Sjohnlev cv_wait(&watch_events_cv, &watch_events_lock); 941843e1988Sjohnlev msg = list_head(&watch_events); 9421d03c31eSjohnlev ASSERT(msg != NULL); 943843e1988Sjohnlev list_remove(&watch_events, msg); 944ab4a9bebSjohnlev watch = msg->un.watch.handle; 945843e1988Sjohnlev mutex_exit(&watch_events_lock); 946843e1988Sjohnlev 9471d03c31eSjohnlev mutex_enter(&xenwatch_mutex); 948ab4a9bebSjohnlev 949ab4a9bebSjohnlev DTRACE_XPV4(xenbus__fire__watch, 950ab4a9bebSjohnlev const char *, watch->node, 951ab4a9bebSjohnlev uintptr_t, watch->callback, 952ab4a9bebSjohnlev struct xenbus_watch *, watch, 953ab4a9bebSjohnlev const char *, msg->un.watch.vec[XS_WATCH_PATH]); 954ab4a9bebSjohnlev 955ab4a9bebSjohnlev watch->callback(watch, (const char **)msg->un.watch.vec, 956ab4a9bebSjohnlev msg->un.watch.vec_size); 957ab4a9bebSjohnlev 958843e1988Sjohnlev free_stored_msg(msg); 959843e1988Sjohnlev mutex_exit(&xenwatch_mutex); 960843e1988Sjohnlev } 961843e1988Sjohnlev } 962843e1988Sjohnlev 963843e1988Sjohnlev static int 964843e1988Sjohnlev process_msg(void) 965843e1988Sjohnlev { 966843e1988Sjohnlev struct xs_stored_msg *msg; 967843e1988Sjohnlev char *body; 968843e1988Sjohnlev int err, mlen; 969843e1988Sjohnlev 970843e1988Sjohnlev msg = kmem_alloc(sizeof (*msg), KM_SLEEP); 971843e1988Sjohnlev 972843e1988Sjohnlev err = xb_read(&msg->hdr, sizeof (msg->hdr)); 973843e1988Sjohnlev if (err) { 974843e1988Sjohnlev kmem_free(msg, sizeof (*msg)); 975843e1988Sjohnlev return (err); 976843e1988Sjohnlev } 977843e1988Sjohnlev 978843e1988Sjohnlev mlen = msg->hdr.len + 1; 979843e1988Sjohnlev body = kmem_alloc(mlen, KM_SLEEP); 980843e1988Sjohnlev 981843e1988Sjohnlev err = xb_read(body, msg->hdr.len); 982843e1988Sjohnlev if (err) { 983843e1988Sjohnlev kmem_free(body, mlen); 984843e1988Sjohnlev kmem_free(msg, sizeof (*msg)); 985843e1988Sjohnlev return (err); 986843e1988Sjohnlev } 987843e1988Sjohnlev 988843e1988Sjohnlev body[mlen - 1] = '\0'; 989843e1988Sjohnlev 990843e1988Sjohnlev if (msg->hdr.type == XS_WATCH_EVENT) { 991ab4a9bebSjohnlev const char *token; 992843e1988Sjohnlev msg->un.watch.vec = split(body, msg->hdr.len + 1, 993843e1988Sjohnlev &msg->un.watch.vec_size); 994843e1988Sjohnlev if (msg->un.watch.vec == NULL) { 995843e1988Sjohnlev kmem_free(msg, sizeof (*msg)); 996843e1988Sjohnlev return (EIO); 997843e1988Sjohnlev } 998843e1988Sjohnlev 999843e1988Sjohnlev mutex_enter(&watches_lock); 1000ab4a9bebSjohnlev token = msg->un.watch.vec[XS_WATCH_TOKEN]; 1001ab4a9bebSjohnlev if ((msg->un.watch.handle = find_watch(token)) != NULL) { 1002843e1988Sjohnlev mutex_enter(&watch_events_lock); 1003ab4a9bebSjohnlev 1004ab4a9bebSjohnlev DTRACE_XPV4(xenbus__enqueue__watch, 1005ab4a9bebSjohnlev const char *, msg->un.watch.handle->node, 1006ab4a9bebSjohnlev uintptr_t, msg->un.watch.handle->callback, 1007ab4a9bebSjohnlev struct xenbus_watch *, msg->un.watch.handle, 1008ab4a9bebSjohnlev const char *, msg->un.watch.vec[XS_WATCH_PATH]); 1009ab4a9bebSjohnlev 1010843e1988Sjohnlev list_insert_tail(&watch_events, msg); 1011843e1988Sjohnlev cv_broadcast(&watch_events_cv); 1012843e1988Sjohnlev mutex_exit(&watch_events_lock); 1013843e1988Sjohnlev } else { 1014843e1988Sjohnlev free_stored_msg(msg); 1015843e1988Sjohnlev } 1016843e1988Sjohnlev mutex_exit(&watches_lock); 1017843e1988Sjohnlev } else { 1018843e1988Sjohnlev msg->un.reply.body = body; 1019843e1988Sjohnlev mutex_enter(&xs_state.reply_lock); 1020843e1988Sjohnlev list_insert_tail(&xs_state.reply_list, msg); 1021843e1988Sjohnlev mutex_exit(&xs_state.reply_lock); 1022843e1988Sjohnlev cv_signal(&xs_state.reply_cv); 1023843e1988Sjohnlev } 1024843e1988Sjohnlev 1025843e1988Sjohnlev return (0); 1026843e1988Sjohnlev } 1027843e1988Sjohnlev 1028843e1988Sjohnlev static void 1029843e1988Sjohnlev xenbus_thread(void) 1030843e1988Sjohnlev { 1031843e1988Sjohnlev int err; 1032843e1988Sjohnlev 1033*fc621ef0SJohn Levon /* 1034*fc621ef0SJohn Levon * We have to wait for interrupts to be ready, so we don't clash 1035*fc621ef0SJohn Levon * with the polled-IO code in read_reply(). 1036*fc621ef0SJohn Levon */ 1037*fc621ef0SJohn Levon while (!interrupts_unleashed) 1038*fc621ef0SJohn Levon delay(10); 1039*fc621ef0SJohn Levon 1040*fc621ef0SJohn Levon for (;;) { 1041843e1988Sjohnlev err = process_msg(); 1042843e1988Sjohnlev if (err) 1043843e1988Sjohnlev cmn_err(CE_WARN, "XENBUS error %d while reading " 1044843e1988Sjohnlev "message", err); 1045843e1988Sjohnlev } 1046843e1988Sjohnlev } 1047843e1988Sjohnlev 1048843e1988Sjohnlev /* 1049843e1988Sjohnlev * When setting up xenbus, dom0 and domU have to take different paths, which 1050843e1988Sjohnlev * makes this code a little confusing. For dom0: 1051843e1988Sjohnlev * 1052843e1988Sjohnlev * xs_early_init - mutex init only 1053843e1988Sjohnlev * xs_dom0_init - called on xenbus dev attach: set up our xenstore page and 1054843e1988Sjohnlev * event channel; start xenbus threads for responding to interrupts. 1055843e1988Sjohnlev * 1056843e1988Sjohnlev * And for domU: 1057843e1988Sjohnlev * 1058843e1988Sjohnlev * xs_early_init - mutex init; set up our xenstore page and event channel 1059843e1988Sjohnlev * xs_domu_init - installation of IRQ handler; start xenbus threads. 1060843e1988Sjohnlev * 1061843e1988Sjohnlev * We need an early init on domU so we can use xenbus in polled mode to 1062843e1988Sjohnlev * discover devices, VCPUs etc. 1063843e1988Sjohnlev * 1064843e1988Sjohnlev * On resume, we use xb_init() and xb_setup_intr() to restore xenbus to a 1065843e1988Sjohnlev * working state. 1066843e1988Sjohnlev */ 1067843e1988Sjohnlev 1068843e1988Sjohnlev void 1069843e1988Sjohnlev xs_early_init(void) 1070843e1988Sjohnlev { 1071843e1988Sjohnlev list_create(&xs_state.reply_list, sizeof (struct xs_stored_msg), 1072843e1988Sjohnlev offsetof(struct xs_stored_msg, list)); 1073843e1988Sjohnlev list_create(&watch_events, sizeof (struct xs_stored_msg), 1074843e1988Sjohnlev offsetof(struct xs_stored_msg, list)); 1075843e1988Sjohnlev list_create(&watches, sizeof (struct xenbus_watch), 1076843e1988Sjohnlev offsetof(struct xenbus_watch, list)); 1077843e1988Sjohnlev list_create(¬ify_list, sizeof (struct xenbus_notify), 1078843e1988Sjohnlev offsetof(struct xenbus_notify, list)); 1079843e1988Sjohnlev mutex_init(&xs_state.reply_lock, NULL, MUTEX_DEFAULT, NULL); 1080843e1988Sjohnlev mutex_init(&xs_state.request_mutex, NULL, MUTEX_DEFAULT, NULL); 1081843e1988Sjohnlev mutex_init(¬ify_list_lock, NULL, MUTEX_DEFAULT, NULL); 1082843e1988Sjohnlev rw_init(&xs_state.suspend_lock, NULL, RW_DEFAULT, NULL); 1083843e1988Sjohnlev cv_init(&xs_state.reply_cv, NULL, CV_DEFAULT, NULL); 1084843e1988Sjohnlev 1085843e1988Sjohnlev if (DOMAIN_IS_INITDOMAIN(xen_info)) 1086843e1988Sjohnlev return; 1087843e1988Sjohnlev 1088843e1988Sjohnlev xb_init(); 1089843e1988Sjohnlev xenstore_up = B_TRUE; 1090843e1988Sjohnlev } 1091843e1988Sjohnlev 1092843e1988Sjohnlev static void 1093843e1988Sjohnlev xs_thread_init(void) 1094843e1988Sjohnlev { 1095843e1988Sjohnlev (void) thread_create(NULL, 0, xenwatch_thread, NULL, 0, &p0, 1096843e1988Sjohnlev TS_RUN, minclsyspri); 1097843e1988Sjohnlev (void) thread_create(NULL, 0, xenbus_thread, NULL, 0, &p0, 1098843e1988Sjohnlev TS_RUN, minclsyspri); 1099843e1988Sjohnlev xenbus_taskq = taskq_create("xenbus_taskq", 1, 1100843e1988Sjohnlev maxclsyspri - 1, 1, 1, TASKQ_PREPOPULATE); 1101843e1988Sjohnlev ASSERT(xenbus_taskq != NULL); 1102843e1988Sjohnlev } 1103843e1988Sjohnlev 1104843e1988Sjohnlev void 1105843e1988Sjohnlev xs_domu_init(void) 1106843e1988Sjohnlev { 1107843e1988Sjohnlev if (DOMAIN_IS_INITDOMAIN(xen_info)) 1108843e1988Sjohnlev return; 1109843e1988Sjohnlev 1110843e1988Sjohnlev /* 1111843e1988Sjohnlev * Add interrupt handler for xenbus now, must wait till after 1112843e1988Sjohnlev * psm module is loaded. All use of xenbus is in polled mode 1113843e1988Sjohnlev * until xs_init is called since it is what kicks off the xs 1114843e1988Sjohnlev * server threads. 1115843e1988Sjohnlev */ 1116843e1988Sjohnlev xs_thread_init(); 1117843e1988Sjohnlev xb_setup_intr(); 1118843e1988Sjohnlev } 1119843e1988Sjohnlev 1120843e1988Sjohnlev 1121843e1988Sjohnlev void 1122843e1988Sjohnlev xs_dom0_init(void) 1123843e1988Sjohnlev { 1124843e1988Sjohnlev static boolean_t initialized = B_FALSE; 1125843e1988Sjohnlev 1126843e1988Sjohnlev ASSERT(DOMAIN_IS_INITDOMAIN(xen_info)); 1127843e1988Sjohnlev 1128843e1988Sjohnlev /* 1129843e1988Sjohnlev * The xenbus driver might be re-attaching. 1130843e1988Sjohnlev */ 1131843e1988Sjohnlev if (initialized) 1132843e1988Sjohnlev return; 1133843e1988Sjohnlev 1134843e1988Sjohnlev xb_init(); 1135843e1988Sjohnlev xs_thread_init(); 1136843e1988Sjohnlev xb_setup_intr(); 1137843e1988Sjohnlev 1138843e1988Sjohnlev initialized = B_TRUE; 1139843e1988Sjohnlev } 1140