]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - sys/xen/xenbus/xenbus_dev.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.git] / sys / xen / xenbus / xenbus_dev.c
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$");
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
52 struct xenbus_dev_transaction {
53         LIST_ENTRY(xenbus_dev_transaction) list;
54         struct xenbus_transaction handle;
55 };
56
57 struct 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
74 static int 
75 xenbus_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
98 static void
99 queue_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
112 static int 
113 xenbus_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
178 static int
179 xenbus_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
195 static int
196 xenbus_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
211 static 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
220 static int
221 xenbus_dev_sysinit(void)
222 {
223         make_dev(&xenbus_dev_cdevsw, 0, UID_ROOT, GID_WHEEL, 0400,
224             "xen/xenbus");
225
226         return (0);
227 }
228 SYSINIT(xenbus_dev_sysinit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE,
229     xenbus_dev_sysinit, NULL);