xenstore_dev.c revision 186557
1/* 2 * xenbus_dev.c 3 * 4 * Driver giving user-space access to the kernel's xenbus connection 5 * to xenstore. 6 * 7 * Copyright (c) 2005, Christian Limpach 8 * Copyright (c) 2005, Rusty Russell, IBM Corporation 9 * 10 * This file may be distributed separately from the Linux kernel, or 11 * incorporated into other software packages, subject to the following license: 12 * 13 * Permission is hereby granted, free of charge, to any person obtaining a copy 14 * of this source file (the "Software"), to deal in the Software without 15 * restriction, including without limitation the rights to use, copy, modify, 16 * merge, publish, distribute, sublicense, and/or sell copies of the Software, 17 * and to permit persons to whom the Software is furnished to do so, subject to 18 * the following conditions: 19 * 20 * The above copyright notice and this permission notice shall be included in 21 * all copies or substantial portions of the Software. 22 * 23 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 28 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 29 * IN THE SOFTWARE. 30 */ 31 32 33#include <sys/cdefs.h> 34__FBSDID("$FreeBSD: head/sys/xen/xenbus/xenbus_dev.c 186557 2008-12-29 06:31:03Z kmacy $"); 35 36#include <sys/types.h> 37#include <sys/cdefs.h> 38#include <sys/errno.h> 39#include <sys/uio.h> 40#include <sys/param.h> 41#include <sys/systm.h> 42#include <sys/proc.h> 43#include <sys/kernel.h> 44#include <sys/malloc.h> 45#include <sys/conf.h> 46 47#include <machine/xen/xen-os.h> 48#include <xen/hypervisor.h> 49#include <xen/xenbus/xenbusvar.h> 50#include <xen/xenbus/xenbus_comms.h> 51 52struct xenbus_dev_transaction { 53 LIST_ENTRY(xenbus_dev_transaction) list; 54 struct xenbus_transaction handle; 55}; 56 57struct xenbus_dev_data { 58 /* In-progress transaction. */ 59 LIST_HEAD(xdd_list_head, xenbus_dev_transaction) transactions; 60 61 /* Partial request. */ 62 unsigned int len; 63 union { 64 struct xsd_sockmsg msg; 65 char buffer[PAGE_SIZE]; 66 } u; 67 68 /* Response queue. */ 69#define MASK_READ_IDX(idx) ((idx)&(PAGE_SIZE-1)) 70 char read_buffer[PAGE_SIZE]; 71 unsigned int read_cons, read_prod; 72}; 73 74static int 75xenbus_dev_read(struct cdev *dev, struct uio *uio, int ioflag) 76{ 77 int error; 78 struct xenbus_dev_data *u = dev->si_drv1; 79 80 while (u->read_prod == u->read_cons) { 81 error = tsleep(u, PCATCH, "xbdread", hz/10); 82 if (error && error != EWOULDBLOCK) 83 return (error); 84 } 85 86 while (uio->uio_resid > 0) { 87 if (u->read_cons == u->read_prod) 88 break; 89 error = uiomove(&u->read_buffer[MASK_READ_IDX(u->read_cons)], 90 1, uio); 91 if (error) 92 return (error); 93 u->read_cons++; 94 } 95 return (0); 96} 97 98static void 99queue_reply(struct xenbus_dev_data *u, char *data, unsigned int len) 100{ 101 int i; 102 103 for (i = 0; i < len; i++, u->read_prod++) 104 u->read_buffer[MASK_READ_IDX(u->read_prod)] = data[i]; 105 106 KASSERT((u->read_prod - u->read_cons) <= sizeof(u->read_buffer), 107 ("xenstore reply too big")); 108 109 wakeup(u); 110} 111 112static int 113xenbus_dev_write(struct cdev *dev, struct uio *uio, int ioflag) 114{ 115 int error; 116 struct xenbus_dev_data *u = dev->si_drv1; 117 struct xenbus_dev_transaction *trans; 118 void *reply; 119 int len = uio->uio_resid; 120 121 if ((len + u->len) > sizeof(u->u.buffer)) 122 return (EINVAL); 123 124 error = uiomove(u->u.buffer + u->len, len, uio); 125 if (error) 126 return (error); 127 128 u->len += len; 129 if (u->len < (sizeof(u->u.msg) + u->u.msg.len)) 130 return (0); 131 132 switch (u->u.msg.type) { 133 case XS_TRANSACTION_START: 134 case XS_TRANSACTION_END: 135 case XS_DIRECTORY: 136 case XS_READ: 137 case XS_GET_PERMS: 138 case XS_RELEASE: 139 case XS_GET_DOMAIN_PATH: 140 case XS_WRITE: 141 case XS_MKDIR: 142 case XS_RM: 143 case XS_SET_PERMS: 144 error = xenbus_dev_request_and_reply(&u->u.msg, &reply); 145 if (!error) { 146 if (u->u.msg.type == XS_TRANSACTION_START) { 147 trans = malloc(sizeof(*trans), M_DEVBUF, 148 M_WAITOK); 149 trans->handle.id = strtoul(reply, NULL, 0); 150 LIST_INSERT_HEAD(&u->transactions, trans, list); 151 } else if (u->u.msg.type == XS_TRANSACTION_END) { 152 LIST_FOREACH(trans, &u->transactions, list) 153 if (trans->handle.id == u->u.msg.tx_id) 154 break; 155#if 0 /* XXX does this mean the list is empty? */ 156 BUG_ON(&trans->list == &u->transactions); 157#endif 158 LIST_REMOVE(trans, list); 159 free(trans, M_DEVBUF); 160 } 161 queue_reply(u, (char *)&u->u.msg, sizeof(u->u.msg)); 162 queue_reply(u, (char *)reply, u->u.msg.len); 163 free(reply, M_DEVBUF); 164 } 165 break; 166 167 default: 168 error = EINVAL; 169 break; 170 } 171 172 if (error == 0) 173 u->len = 0; 174 175 return (error); 176} 177 178static int 179xenbus_dev_open(struct cdev *dev, int oflags, int devtype, struct thread *td) 180{ 181 struct xenbus_dev_data *u; 182 183 if (xen_store_evtchn == 0) 184 return (ENOENT); 185#if 0 /* XXX figure out if equiv needed */ 186 nonseekable_open(inode, filp); 187#endif 188 u = malloc(sizeof(*u), M_DEVBUF, M_WAITOK|M_ZERO); 189 LIST_INIT(&u->transactions); 190 dev->si_drv1 = u; 191 192 return (0); 193} 194 195static int 196xenbus_dev_close(struct cdev *dev, int fflag, int devtype, struct thread *td) 197{ 198 struct xenbus_dev_data *u = dev->si_drv1; 199 struct xenbus_dev_transaction *trans, *tmp; 200 201 LIST_FOREACH_SAFE(trans, &u->transactions, list, tmp) { 202 xenbus_transaction_end(trans->handle, 1); 203 LIST_REMOVE(trans, list); 204 free(trans, M_DEVBUF); 205 } 206 207 free(u, M_DEVBUF); 208 return (0); 209} 210 211static struct cdevsw xenbus_dev_cdevsw = { 212 .d_version = D_VERSION, 213 .d_read = xenbus_dev_read, 214 .d_write = xenbus_dev_write, 215 .d_open = xenbus_dev_open, 216 .d_close = xenbus_dev_close, 217 .d_name = "xenbus_dev", 218}; 219 220static int 221xenbus_dev_sysinit(void) 222{ 223 make_dev(&xenbus_dev_cdevsw, 0, UID_ROOT, GID_WHEEL, 0400, 224 "xen/xenbus"); 225 226 return (0); 227} 228SYSINIT(xenbus_dev_sysinit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, 229 xenbus_dev_sysinit, NULL); 230