From owner-svn-src-head@FreeBSD.ORG Tue Sep 30 17:14:13 2014 Return-Path: Delivered-To: svn-src-head@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [8.8.178.115]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTPS id 07DB7B2; Tue, 30 Sep 2014 17:14:13 +0000 (UTC) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id DD01BC0D; Tue, 30 Sep 2014 17:14:12 +0000 (UTC) Received: from svn.freebsd.org ([127.0.1.70]) by svn.freebsd.org (8.14.9/8.14.9) with ESMTP id s8UHECd3007293; Tue, 30 Sep 2014 17:14:12 GMT (envelope-from royger@FreeBSD.org) Received: (from royger@localhost) by svn.freebsd.org (8.14.9/8.14.9/Submit) id s8UHECCa007289; Tue, 30 Sep 2014 17:14:12 GMT (envelope-from royger@FreeBSD.org) Message-Id: <201409301714.s8UHECCa007289@svn.freebsd.org> X-Authentication-Warning: svn.freebsd.org: royger set sender to royger@FreeBSD.org using -f From: Roger Pau Monné Date: Tue, 30 Sep 2014 17:14:12 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r272314 - in head/sys: conf dev/xen/xenstore xen/xenstore X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-head@freebsd.org X-Mailman-Version: 2.1.18-1 Precedence: list List-Id: SVN commit messages for the src tree for head/-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 30 Sep 2014 17:14:13 -0000 Author: royger Date: Tue Sep 30 17:14:11 2014 New Revision: 272314 URL: http://svnweb.freebsd.org/changeset/base/272314 Log: xen: move xenstore devices Move xenstore related devices (xenstore.c and xenstore_dev.c) from xen/xenstore to dev/xen/xenstore. This is just code motion, no functional changes. Sponsored by: Citrix Systems R&D Added: head/sys/dev/xen/xenstore/ head/sys/dev/xen/xenstore/xenstore.c - copied unchanged from r272308, head/sys/xen/xenstore/xenstore.c head/sys/dev/xen/xenstore/xenstore_dev.c - copied unchanged from r272308, head/sys/xen/xenstore/xenstore_dev.c Deleted: head/sys/xen/xenstore/xenstore.c head/sys/xen/xenstore/xenstore_dev.c Modified: head/sys/conf/files Modified: head/sys/conf/files ============================================================================== --- head/sys/conf/files Tue Sep 30 16:55:19 2014 (r272313) +++ head/sys/conf/files Tue Sep 30 17:14:11 2014 (r272314) @@ -2638,6 +2638,8 @@ dev/xen/netfront/netfront.c optional xen dev/xen/xenpci/xenpci.c optional xenpci dev/xen/timer/timer.c optional xen | xenhvm dev/xen/pvcpu/pvcpu.c optional xen | xenhvm +dev/xen/xenstore/xenstore.c optional xen | xenhvm +dev/xen/xenstore/xenstore_dev.c optional xen | xenhvm dev/xl/if_xl.c optional xl pci dev/xl/xlphy.c optional xl pci fs/autofs/autofs.c optional autofs @@ -3976,8 +3978,6 @@ xen/xenbus/xenbusb_if.m optional xen | xen/xenbus/xenbusb.c optional xen | xenhvm xen/xenbus/xenbusb_front.c optional xen | xenhvm xen/xenbus/xenbusb_back.c optional xen | xenhvm -xen/xenstore/xenstore.c optional xen | xenhvm -xen/xenstore/xenstore_dev.c optional xen | xenhvm xdr/xdr.c optional krpc | nfslockd | nfsclient | nfsserver | nfscl | nfsd xdr/xdr_array.c optional krpc | nfslockd | nfsclient | nfsserver | nfscl | nfsd xdr/xdr_mbuf.c optional krpc | nfslockd | nfsclient | nfsserver | nfscl | nfsd Copied: head/sys/dev/xen/xenstore/xenstore.c (from r272308, head/sys/xen/xenstore/xenstore.c) ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/sys/dev/xen/xenstore/xenstore.c Tue Sep 30 17:14:11 2014 (r272314, copy of r272308, head/sys/xen/xenstore/xenstore.c) @@ -0,0 +1,1640 @@ +/****************************************************************************** + * xenstore.c + * + * Low-level kernel interface to the XenStore. + * + * Copyright (C) 2005 Rusty Russell, IBM Corporation + * Copyright (C) 2009,2010 Spectra Logic Corporation + * + * This file may be distributed separately from the Linux kernel, or + * incorporated into other software packages, subject to the following license: + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this source file (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include + +/** + * \file xenstore.c + * \brief XenStore interface + * + * The XenStore interface is a simple storage system that is a means of + * communicating state and configuration data between the Xen Domain 0 + * and the various guest domains. All configuration data other than + * a small amount of essential information required during the early + * boot process of launching a Xen aware guest, is managed using the + * XenStore. + * + * The XenStore is ASCII string based, and has a structure and semantics + * similar to a filesystem. There are files and directories, the directories + * able to contain files or other directories. The depth of the hierachy + * is only limited by the XenStore's maximum path length. + * + * The communication channel between the XenStore service and other + * domains is via two, guest specific, ring buffers in a shared memory + * area. One ring buffer is used for communicating in each direction. + * The grant table references for this shared memory are given to the + * guest either via the xen_start_info structure for a fully para- + * virtualized guest, or via HVM hypercalls for a hardware virtualized + * guest. + * + * The XenStore communication relies on an event channel and thus + * interrupts. For this reason, the attachment of the XenStore + * relies on an interrupt driven configuration hook to hold off + * boot processing until communication with the XenStore service + * can be established. + * + * Several Xen services depend on the XenStore, most notably the + * XenBus used to discover and manage Xen devices. These services + * are implemented as NewBus child attachments to a bus exported + * by this XenStore driver. + */ + +static struct xs_watch *find_watch(const char *token); + +MALLOC_DEFINE(M_XENSTORE, "xenstore", "XenStore data and results"); + +/** + * Pointer to shared memory communication structures allowing us + * to communicate with the XenStore service. + * + * When operating in full PV mode, this pointer is set early in kernel + * startup from within xen_machdep.c. In HVM mode, we use hypercalls + * to get the guest frame number for the shared page and then map it + * into kva. See xs_init() for details. + */ +struct xenstore_domain_interface *xen_store; + +/*-------------------------- Private Data Structures ------------------------*/ + +/** + * Structure capturing messages received from the XenStore service. + */ +struct xs_stored_msg { + TAILQ_ENTRY(xs_stored_msg) list; + + struct xsd_sockmsg hdr; + + union { + /* Queued replies. */ + struct { + char *body; + } reply; + + /* Queued watch events. */ + struct { + struct xs_watch *handle; + const char **vec; + u_int vec_size; + } watch; + } u; +}; +TAILQ_HEAD(xs_stored_msg_list, xs_stored_msg); + +/** + * Container for all XenStore related state. + */ +struct xs_softc { + /** Newbus device for the XenStore. */ + device_t xs_dev; + + /** + * Lock serializing access to ring producer/consumer + * indexes. Use of this lock guarantees that wakeups + * of blocking readers/writers are not missed due to + * races with the XenStore service. + */ + struct mtx ring_lock; + + /* + * Mutex used to insure exclusive access to the outgoing + * communication ring. We use a lock type that can be + * held while sleeping so that xs_write() can block waiting + * for space in the ring to free up, without allowing another + * writer to come in and corrupt a partial message write. + */ + struct sx request_mutex; + + /** + * A list of replies to our requests. + * + * The reply list is filled by xs_rcv_thread(). It + * is consumed by the context that issued the request + * to which a reply is made. The requester blocks in + * xs_read_reply(). + * + * /note Only one requesting context can be active at a time. + * This is guaranteed by the request_mutex and insures + * that the requester sees replies matching the order + * of its requests. + */ + struct xs_stored_msg_list reply_list; + + /** Lock protecting the reply list. */ + struct mtx reply_lock; + + /** + * List of registered watches. + */ + struct xs_watch_list registered_watches; + + /** Lock protecting the registered watches list. */ + struct mtx registered_watches_lock; + + /** + * List of pending watch callback events. + */ + struct xs_stored_msg_list watch_events; + + /** Lock protecting the watch calback list. */ + struct mtx watch_events_lock; + + /** + * Sleepable lock used to prevent VM suspension while a + * xenstore transaction is outstanding. + * + * Each active transaction holds a shared lock on the + * suspend mutex. Our suspend method blocks waiting + * to acquire an exclusive lock. This guarantees that + * suspend processing will only proceed once all active + * transactions have been retired. + */ + struct sx suspend_mutex; + + /** + * The processid of the xenwatch thread. + */ + pid_t xenwatch_pid; + + /** + * Sleepable mutex used to gate the execution of XenStore + * watch event callbacks. + * + * xenwatch_thread holds an exclusive lock on this mutex + * while delivering event callbacks, and xenstore_unregister_watch() + * uses an exclusive lock of this mutex to guarantee that no + * callbacks of the just unregistered watch are pending + * before returning to its caller. + */ + struct sx xenwatch_mutex; + + /** + * The HVM guest pseudo-physical frame number. This is Xen's mapping + * of the true machine frame number into our "physical address space". + */ + unsigned long gpfn; + + /** + * The event channel for communicating with the + * XenStore service. + */ + int evtchn; + + /** Handle for XenStore interrupts. */ + xen_intr_handle_t xen_intr_handle; + + /** + * Interrupt driven config hook allowing us to defer + * attaching children until interrupts (and thus communication + * with the XenStore service) are available. + */ + struct intr_config_hook xs_attachcb; +}; + +/*-------------------------------- Global Data ------------------------------*/ +static struct xs_softc xs; + +/*------------------------- Private Utility Functions -----------------------*/ + +/** + * Count and optionally record pointers to a number of NUL terminated + * strings in a buffer. + * + * \param strings A pointer to a contiguous buffer of NUL terminated strings. + * \param dest An array to store pointers to each string found in strings. + * \param len The length of the buffer pointed to by strings. + * + * \return A count of the number of strings found. + */ +static u_int +extract_strings(const char *strings, const char **dest, u_int len) +{ + u_int num; + const char *p; + + for (p = strings, num = 0; p < strings + len; p += strlen(p) + 1) { + if (dest != NULL) + *dest++ = p; + num++; + } + + return (num); +} + +/** + * Convert a contiguous buffer containing a series of NUL terminated + * strings into an array of pointers to strings. + * + * The returned pointer references the array of string pointers which + * is followed by the storage for the string data. It is the client's + * responsibility to free this storage. + * + * The storage addressed by strings is free'd prior to split returning. + * + * \param strings A pointer to a contiguous buffer of NUL terminated strings. + * \param len The length of the buffer pointed to by strings. + * \param num The number of strings found and returned in the strings + * array. + * + * \return An array of pointers to the strings found in the input buffer. + */ +static const char ** +split(char *strings, u_int len, u_int *num) +{ + const char **ret; + + /* Protect against unterminated buffers. */ + if (len > 0) + strings[len - 1] = '\0'; + + /* Count the strings. */ + *num = extract_strings(strings, /*dest*/NULL, len); + + /* Transfer to one big alloc for easy freeing by the caller. */ + ret = malloc(*num * sizeof(char *) + len, M_XENSTORE, M_WAITOK); + memcpy(&ret[*num], strings, len); + free(strings, M_XENSTORE); + + /* Extract pointers to newly allocated array. */ + strings = (char *)&ret[*num]; + (void)extract_strings(strings, /*dest*/ret, len); + + return (ret); +} + +/*------------------------- Public Utility Functions -------------------------*/ +/*------- API comments for these methods can be found in xenstorevar.h -------*/ +struct sbuf * +xs_join(const char *dir, const char *name) +{ + struct sbuf *sb; + + sb = sbuf_new_auto(); + sbuf_cat(sb, dir); + if (name[0] != '\0') { + sbuf_putc(sb, '/'); + sbuf_cat(sb, name); + } + sbuf_finish(sb); + + return (sb); +} + +/*-------------------- Low Level Communication Management --------------------*/ +/** + * Interrupt handler for the XenStore event channel. + * + * XenStore reads and writes block on "xen_store" for buffer + * space. Wakeup any blocking operations when the XenStore + * service has modified the queues. + */ +static void +xs_intr(void * arg __unused /*__attribute__((unused))*/) +{ + + /* + * Hold ring lock across wakeup so that clients + * cannot miss a wakeup. + */ + mtx_lock(&xs.ring_lock); + wakeup(xen_store); + mtx_unlock(&xs.ring_lock); +} + +/** + * Verify that the indexes for a ring are valid. + * + * The difference between the producer and consumer cannot + * exceed the size of the ring. + * + * \param cons The consumer index for the ring to test. + * \param prod The producer index for the ring to test. + * + * \retval 1 If indexes are in range. + * \retval 0 If the indexes are out of range. + */ +static int +xs_check_indexes(XENSTORE_RING_IDX cons, XENSTORE_RING_IDX prod) +{ + + return ((prod - cons) <= XENSTORE_RING_SIZE); +} + +/** + * Return a pointer to, and the length of, the contiguous + * free region available for output in a ring buffer. + * + * \param cons The consumer index for the ring. + * \param prod The producer index for the ring. + * \param buf The base address of the ring's storage. + * \param len The amount of contiguous storage available. + * + * \return A pointer to the start location of the free region. + */ +static void * +xs_get_output_chunk(XENSTORE_RING_IDX cons, XENSTORE_RING_IDX prod, + char *buf, uint32_t *len) +{ + + *len = XENSTORE_RING_SIZE - MASK_XENSTORE_IDX(prod); + if ((XENSTORE_RING_SIZE - (prod - cons)) < *len) + *len = XENSTORE_RING_SIZE - (prod - cons); + return (buf + MASK_XENSTORE_IDX(prod)); +} + +/** + * Return a pointer to, and the length of, the contiguous + * data available to read from a ring buffer. + * + * \param cons The consumer index for the ring. + * \param prod The producer index for the ring. + * \param buf The base address of the ring's storage. + * \param len The amount of contiguous data available to read. + * + * \return A pointer to the start location of the available data. + */ +static const void * +xs_get_input_chunk(XENSTORE_RING_IDX cons, XENSTORE_RING_IDX prod, + const char *buf, uint32_t *len) +{ + + *len = XENSTORE_RING_SIZE - MASK_XENSTORE_IDX(cons); + if ((prod - cons) < *len) + *len = prod - cons; + return (buf + MASK_XENSTORE_IDX(cons)); +} + +/** + * Transmit data to the XenStore service. + * + * \param tdata A pointer to the contiguous data to send. + * \param len The amount of data to send. + * + * \return On success 0, otherwise an errno value indicating the + * cause of failure. + * + * \invariant Called from thread context. + * \invariant The buffer pointed to by tdata is at least len bytes + * in length. + * \invariant xs.request_mutex exclusively locked. + */ +static int +xs_write_store(const void *tdata, unsigned len) +{ + XENSTORE_RING_IDX cons, prod; + const char *data = (const char *)tdata; + int error; + + sx_assert(&xs.request_mutex, SX_XLOCKED); + while (len != 0) { + void *dst; + u_int avail; + + /* Hold lock so we can't miss wakeups should we block. */ + mtx_lock(&xs.ring_lock); + cons = xen_store->req_cons; + prod = xen_store->req_prod; + if ((prod - cons) == XENSTORE_RING_SIZE) { + /* + * Output ring is full. Wait for a ring event. + * + * Note that the events from both queues + * are combined, so being woken does not + * guarantee that data exist in the read + * ring. + * + * To simplify error recovery and the retry, + * we specify PDROP so our lock is *not* held + * when msleep returns. + */ + error = msleep(xen_store, &xs.ring_lock, PCATCH|PDROP, + "xbwrite", /*timeout*/0); + if (error && error != EWOULDBLOCK) + return (error); + + /* Try again. */ + continue; + } + mtx_unlock(&xs.ring_lock); + + /* Verify queue sanity. */ + if (!xs_check_indexes(cons, prod)) { + xen_store->req_cons = xen_store->req_prod = 0; + return (EIO); + } + + dst = xs_get_output_chunk(cons, prod, xen_store->req, &avail); + if (avail > len) + avail = len; + + memcpy(dst, data, avail); + data += avail; + len -= avail; + + /* + * The store to the producer index, which indicates + * to the other side that new data has arrived, must + * be visible only after our copy of the data into the + * ring has completed. + */ + wmb(); + xen_store->req_prod += avail; + + /* + * xen_intr_signal() implies mb(). The other side will see + * the change to req_prod at the time of the interrupt. + */ + xen_intr_signal(xs.xen_intr_handle); + } + + return (0); +} + +/** + * Receive data from the XenStore service. + * + * \param tdata A pointer to the contiguous buffer to receive the data. + * \param len The amount of data to receive. + * + * \return On success 0, otherwise an errno value indicating the + * cause of failure. + * + * \invariant Called from thread context. + * \invariant The buffer pointed to by tdata is at least len bytes + * in length. + * + * \note xs_read does not perform any internal locking to guarantee + * serial access to the incoming ring buffer. However, there + * is only one context processing reads: xs_rcv_thread(). + */ +static int +xs_read_store(void *tdata, unsigned len) +{ + XENSTORE_RING_IDX cons, prod; + char *data = (char *)tdata; + int error; + + while (len != 0) { + u_int avail; + const char *src; + + /* Hold lock so we can't miss wakeups should we block. */ + mtx_lock(&xs.ring_lock); + cons = xen_store->rsp_cons; + prod = xen_store->rsp_prod; + if (cons == prod) { + /* + * Nothing to read. Wait for a ring event. + * + * Note that the events from both queues + * are combined, so being woken does not + * guarantee that data exist in the read + * ring. + * + * To simplify error recovery and the retry, + * we specify PDROP so our lock is *not* held + * when msleep returns. + */ + error = msleep(xen_store, &xs.ring_lock, PCATCH|PDROP, + "xbread", /*timeout*/0); + if (error && error != EWOULDBLOCK) + return (error); + continue; + } + mtx_unlock(&xs.ring_lock); + + /* Verify queue sanity. */ + if (!xs_check_indexes(cons, prod)) { + xen_store->rsp_cons = xen_store->rsp_prod = 0; + return (EIO); + } + + src = xs_get_input_chunk(cons, prod, xen_store->rsp, &avail); + if (avail > len) + avail = len; + + /* + * Insure the data we read is related to the indexes + * we read above. + */ + rmb(); + + memcpy(data, src, avail); + data += avail; + len -= avail; + + /* + * Insure that the producer of this ring does not see + * the ring space as free until after we have copied it + * out. + */ + mb(); + xen_store->rsp_cons += avail; + + /* + * xen_intr_signal() implies mb(). The producer will see + * the updated consumer index when the event is delivered. + */ + xen_intr_signal(xs.xen_intr_handle); + } + + return (0); +} + +/*----------------------- Received Message Processing ------------------------*/ +/** + * Block reading the next message from the XenStore service and + * process the result. + * + * \param type The returned type of the XenStore message received. + * + * \return 0 on success. Otherwise an errno value indicating the + * type of failure encountered. + */ +static int +xs_process_msg(enum xsd_sockmsg_type *type) +{ + struct xs_stored_msg *msg; + char *body; + int error; + + msg = malloc(sizeof(*msg), M_XENSTORE, M_WAITOK); + error = xs_read_store(&msg->hdr, sizeof(msg->hdr)); + if (error) { + free(msg, M_XENSTORE); + return (error); + } + + body = malloc(msg->hdr.len + 1, M_XENSTORE, M_WAITOK); + error = xs_read_store(body, msg->hdr.len); + if (error) { + free(body, M_XENSTORE); + free(msg, M_XENSTORE); + return (error); + } + body[msg->hdr.len] = '\0'; + + *type = msg->hdr.type; + if (msg->hdr.type == XS_WATCH_EVENT) { + msg->u.watch.vec = split(body, msg->hdr.len, + &msg->u.watch.vec_size); + + mtx_lock(&xs.registered_watches_lock); + msg->u.watch.handle = find_watch( + msg->u.watch.vec[XS_WATCH_TOKEN]); + if (msg->u.watch.handle != NULL) { + mtx_lock(&xs.watch_events_lock); + TAILQ_INSERT_TAIL(&xs.watch_events, msg, list); + wakeup(&xs.watch_events); + mtx_unlock(&xs.watch_events_lock); + } else { + free(msg->u.watch.vec, M_XENSTORE); + free(msg, M_XENSTORE); + } + mtx_unlock(&xs.registered_watches_lock); + } else { + msg->u.reply.body = body; + mtx_lock(&xs.reply_lock); + TAILQ_INSERT_TAIL(&xs.reply_list, msg, list); + wakeup(&xs.reply_list); + mtx_unlock(&xs.reply_lock); + } + + return (0); +} + +/** + * Thread body of the XenStore receive thread. + * + * This thread blocks waiting for data from the XenStore service + * and processes and received messages. + */ +static void +xs_rcv_thread(void *arg __unused) +{ + int error; + enum xsd_sockmsg_type type; + + for (;;) { + error = xs_process_msg(&type); + if (error) + printf("XENSTORE error %d while reading message\n", + error); + } +} + +/*---------------- XenStore Message Request/Reply Processing -----------------*/ +/** + * Filter invoked before transmitting any message to the XenStore service. + * + * The role of the filter may expand, but currently serves to manage + * the interactions of messages with transaction state. + * + * \param request_msg_type The message type for the request. + */ +static inline void +xs_request_filter(uint32_t request_msg_type) +{ + if (request_msg_type == XS_TRANSACTION_START) + sx_slock(&xs.suspend_mutex); +} + +/** + * Filter invoked after transmitting any message to the XenStore service. + * + * The role of the filter may expand, but currently serves to manage + * the interactions of messages with transaction state. + * + * \param request_msg_type The message type for the original request. + * \param reply_msg_type The message type for any received reply. + * \param request_reply_error The error status from the attempt to send + * the request or retrieve the reply. + */ +static inline void +xs_reply_filter(uint32_t request_msg_type, + uint32_t reply_msg_type, int request_reply_error) +{ + /* + * The count of transactions drops if we attempted + * to end a transaction (even if that attempt fails + * in error), we receive a transaction end acknowledgement, + * or if our attempt to begin a transaction fails. + */ + if (request_msg_type == XS_TRANSACTION_END + || (request_reply_error == 0 && reply_msg_type == XS_TRANSACTION_END) + || (request_msg_type == XS_TRANSACTION_START + && (request_reply_error != 0 || reply_msg_type == XS_ERROR))) + sx_sunlock(&xs.suspend_mutex); + +} + +#define xsd_error_count (sizeof(xsd_errors) / sizeof(xsd_errors[0])) + +/** + * Convert a XenStore error string into an errno number. + * + * \param errorstring The error string to convert. + * + * \return The errno best matching the input string. + * + * \note Unknown error strings are converted to EINVAL. + */ +static int +xs_get_error(const char *errorstring) +{ + u_int i; + + for (i = 0; i < xsd_error_count; i++) { + if (!strcmp(errorstring, xsd_errors[i].errstring)) + return (xsd_errors[i].errnum); + } + log(LOG_WARNING, "XENSTORE xen store gave: unknown error %s", + errorstring); + return (EINVAL); +} + +/** + * Block waiting for a reply to a message request. + * + * \param type The returned type of the reply. + * \param len The returned body length of the reply. + * \param result The returned body of the reply. + * + * \return 0 on success. Otherwise an errno indicating the + * cause of failure. + */ +static int +xs_read_reply(enum xsd_sockmsg_type *type, u_int *len, void **result) +{ + struct xs_stored_msg *msg; + char *body; + int error; + + mtx_lock(&xs.reply_lock); + while (TAILQ_EMPTY(&xs.reply_list)) { + error = mtx_sleep(&xs.reply_list, &xs.reply_lock, + PCATCH, "xswait", hz/10); + if (error && error != EWOULDBLOCK) { + mtx_unlock(&xs.reply_lock); + return (error); + } + } + msg = TAILQ_FIRST(&xs.reply_list); + TAILQ_REMOVE(&xs.reply_list, msg, list); + mtx_unlock(&xs.reply_lock); + + *type = msg->hdr.type; + if (len) + *len = msg->hdr.len; + body = msg->u.reply.body; + + free(msg, M_XENSTORE); + *result = body; + return (0); +} + +/** + * Pass-thru interface for XenStore access by userland processes + * via the XenStore device. + * + * Reply type and length data are returned by overwriting these + * fields in the passed in request message. + * + * \param msg A properly formatted message to transmit to + * the XenStore service. + * \param result The returned body of the reply. + * + * \return 0 on success. Otherwise an errno indicating the cause + * of failure. + * + * \note The returned result is provided in malloced storage and thus + * must be free'd by the caller with 'free(result, M_XENSTORE); + */ +int +xs_dev_request_and_reply(struct xsd_sockmsg *msg, void **result) +{ + uint32_t request_type; + int error; + + request_type = msg->type; + xs_request_filter(request_type); + + sx_xlock(&xs.request_mutex); + if ((error = xs_write_store(msg, sizeof(*msg) + msg->len)) == 0) + error = xs_read_reply(&msg->type, &msg->len, result); + sx_xunlock(&xs.request_mutex); + + xs_reply_filter(request_type, msg->type, error); + + return (error); +} + +/** + * Send a message with an optionally muti-part body to the XenStore service. + * + * \param t The transaction to use for this request. + * \param request_type The type of message to send. + * \param iovec Pointers to the body sections of the request. + * \param num_vecs The number of body sections in the request. + * \param len The returned length of the reply. + * \param result The returned body of the reply. + * + * \return 0 on success. Otherwise an errno indicating + * the cause of failure. + * + * \note The returned result is provided in malloced storage and thus + * must be free'd by the caller with 'free(*result, M_XENSTORE); + */ +static int +xs_talkv(struct xs_transaction t, enum xsd_sockmsg_type request_type, + const struct iovec *iovec, u_int num_vecs, u_int *len, void **result) +{ + struct xsd_sockmsg msg; + void *ret = NULL; + u_int i; + int error; + + msg.tx_id = t.id; + msg.req_id = 0; + msg.type = request_type; + msg.len = 0; + for (i = 0; i < num_vecs; i++) + msg.len += iovec[i].iov_len; + + xs_request_filter(request_type); + + sx_xlock(&xs.request_mutex); + error = xs_write_store(&msg, sizeof(msg)); + if (error) { + printf("xs_talkv failed %d\n", error); + goto error_lock_held; + } + + for (i = 0; i < num_vecs; i++) { + error = xs_write_store(iovec[i].iov_base, iovec[i].iov_len); + if (error) { + printf("xs_talkv failed %d\n", error); + goto error_lock_held; + } + } + + error = xs_read_reply(&msg.type, len, &ret); + +error_lock_held: + sx_xunlock(&xs.request_mutex); + xs_reply_filter(request_type, msg.type, error); + if (error) + return (error); + + if (msg.type == XS_ERROR) { + error = xs_get_error(ret); + free(ret, M_XENSTORE); + return (error); + } + + /* Reply is either error or an echo of our request message type. */ + KASSERT(msg.type == request_type, ("bad xenstore message type")); + + if (result) + *result = ret; + else + free(ret, M_XENSTORE); + + return (0); +} + +/** + * Wrapper for xs_talkv allowing easy transmission of a message with + * a single, contiguous, message body. + * + * \param t The transaction to use for this request. + * \param request_type The type of message to send. + * \param body The body of the request. + * \param len The returned length of the reply. + * \param result The returned body of the reply. + * + * \return 0 on success. Otherwise an errno indicating + * the cause of failure. + * + * \note The returned result is provided in malloced storage and thus + * must be free'd by the caller with 'free(*result, M_XENSTORE); + */ +static int +xs_single(struct xs_transaction t, enum xsd_sockmsg_type request_type, + const char *body, u_int *len, void **result) +{ + struct iovec iovec; + + iovec.iov_base = (void *)(uintptr_t)body; + iovec.iov_len = strlen(body) + 1; + + return (xs_talkv(t, request_type, &iovec, 1, len, result)); +} + +/*------------------------- XenStore Watch Support ---------------------------*/ +/** + * Transmit a watch request to the XenStore service. + * + * \param path The path in the XenStore to watch. + * \param tocken A unique identifier for this watch. + * + * \return 0 on success. Otherwise an errno indicating the + * cause of failure. + */ +static int +xs_watch(const char *path, const char *token) +{ + struct iovec iov[2]; + + iov[0].iov_base = (void *)(uintptr_t) path; + iov[0].iov_len = strlen(path) + 1; + iov[1].iov_base = (void *)(uintptr_t) token; + iov[1].iov_len = strlen(token) + 1; + + return (xs_talkv(XST_NIL, XS_WATCH, iov, 2, NULL, NULL)); +} + +/** + * Transmit an uwatch request to the XenStore service. + * + * \param path The path in the XenStore to watch. + * \param tocken A unique identifier for this watch. + * + * \return 0 on success. Otherwise an errno indicating the + * cause of failure. + */ +static int +xs_unwatch(const char *path, const char *token) +{ + struct iovec iov[2]; + + iov[0].iov_base = (void *)(uintptr_t) path; + iov[0].iov_len = strlen(path) + 1; + iov[1].iov_base = (void *)(uintptr_t) token; + iov[1].iov_len = strlen(token) + 1; + + return (xs_talkv(XST_NIL, XS_UNWATCH, iov, 2, NULL, NULL)); +} *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***