direct-io.hg

view tools/xenstore/xenstored_domain.c @ 10355:7fba181c8531

[TOOLS] Introduce xc_evtchn_*() interface for interacting with /dev/xen/evtchn.
No longer open the device as non-blocking: all reads immediately follow
a select() on the device indicating it's ready to read.

Signed-off-by: John Levon <john.levon@sun.com>
author kaf24@firebug.cl.cam.ac.uk
date Thu Jun 15 13:11:31 2006 +0100 (2006-06-15)
parents 041127f2c687
children 38c16b375298
line source
1 /*
2 Domain communications for Xen Store Daemon.
3 Copyright (C) 2005 Rusty Russell IBM Corporation
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
20 #include <stdio.h>
21 #include <sys/mman.h>
22 #include <unistd.h>
23 #include <stdlib.h>
24 #include <stdarg.h>
26 //#define DEBUG
27 #include "utils.h"
28 #include "talloc.h"
29 #include "xenstored_core.h"
30 #include "xenstored_domain.h"
31 #include "xenstored_watch.h"
32 #include "xenstored_test.h"
34 #include <xenctrl.h>
36 static int *xc_handle;
37 static evtchn_port_t virq_port;
39 int xce_handle = -1;
41 struct domain
42 {
43 struct list_head list;
45 /* The id of this domain */
46 unsigned int domid;
48 /* Event channel port */
49 evtchn_port_t port;
51 /* The remote end of the event channel, used only to validate
52 repeated domain introductions. */
53 evtchn_port_t remote_port;
55 /* The mfn associated with the event channel, used only to validate
56 repeated domain introductions. */
57 unsigned long mfn;
59 /* Domain path in store. */
60 char *path;
62 /* Shared page. */
63 struct xenstore_domain_interface *interface;
65 /* The connection associated with this. */
66 struct connection *conn;
68 /* Have we noticed that this domain is shutdown? */
69 int shutdown;
71 /* number of entry from this domain in the store */
72 int nbentry;
74 /* number of watch for this domain */
75 int nbwatch;
76 };
78 static LIST_HEAD(domains);
80 /* FIXME: Mark connection as broken (close it?) when this happens. */
81 static bool check_indexes(XENSTORE_RING_IDX cons, XENSTORE_RING_IDX prod)
82 {
83 return ((prod - cons) <= XENSTORE_RING_SIZE);
84 }
86 static void *get_output_chunk(XENSTORE_RING_IDX cons,
87 XENSTORE_RING_IDX prod,
88 char *buf, uint32_t *len)
89 {
90 *len = XENSTORE_RING_SIZE - MASK_XENSTORE_IDX(prod);
91 if ((XENSTORE_RING_SIZE - (prod - cons)) < *len)
92 *len = XENSTORE_RING_SIZE - (prod - cons);
93 return buf + MASK_XENSTORE_IDX(prod);
94 }
96 static const void *get_input_chunk(XENSTORE_RING_IDX cons,
97 XENSTORE_RING_IDX prod,
98 const char *buf, uint32_t *len)
99 {
100 *len = XENSTORE_RING_SIZE - MASK_XENSTORE_IDX(cons);
101 if ((prod - cons) < *len)
102 *len = prod - cons;
103 return buf + MASK_XENSTORE_IDX(cons);
104 }
106 static int writechn(struct connection *conn, const void *data, unsigned int len)
107 {
108 uint32_t avail;
109 void *dest;
110 struct xenstore_domain_interface *intf = conn->domain->interface;
111 XENSTORE_RING_IDX cons, prod;
113 /* Must read indexes once, and before anything else, and verified. */
114 cons = intf->rsp_cons;
115 prod = intf->rsp_prod;
116 mb();
117 if (!check_indexes(cons, prod)) {
118 errno = EIO;
119 return -1;
120 }
122 dest = get_output_chunk(cons, prod, intf->rsp, &avail);
123 if (avail < len)
124 len = avail;
126 memcpy(dest, data, len);
127 mb();
128 intf->rsp_prod += len;
130 xc_evtchn_notify(xce_handle, conn->domain->port);
132 return len;
133 }
135 static int readchn(struct connection *conn, void *data, unsigned int len)
136 {
137 uint32_t avail;
138 const void *src;
139 struct xenstore_domain_interface *intf = conn->domain->interface;
140 XENSTORE_RING_IDX cons, prod;
142 /* Must read indexes once, and before anything else, and verified. */
143 cons = intf->req_cons;
144 prod = intf->req_prod;
145 mb();
147 if (!check_indexes(cons, prod)) {
148 errno = EIO;
149 return -1;
150 }
152 src = get_input_chunk(cons, prod, intf->req, &avail);
153 if (avail < len)
154 len = avail;
156 memcpy(data, src, len);
157 mb();
158 intf->req_cons += len;
160 xc_evtchn_notify(xce_handle, conn->domain->port);
162 return len;
163 }
165 static int destroy_domain(void *_domain)
166 {
167 struct domain *domain = _domain;
169 list_del(&domain->list);
171 if (domain->port) {
172 if (xc_evtchn_unbind(xce_handle, domain->port) == -1)
173 eprintf("> Unbinding port %i failed!\n", domain->port);
174 }
176 if (domain->interface)
177 munmap(domain->interface, getpagesize());
179 return 0;
180 }
182 static void domain_cleanup(void)
183 {
184 xc_dominfo_t dominfo;
185 struct domain *domain, *tmp;
186 int notify = 0;
188 list_for_each_entry_safe(domain, tmp, &domains, list) {
189 if (xc_domain_getinfo(*xc_handle, domain->domid, 1,
190 &dominfo) == 1 &&
191 dominfo.domid == domain->domid) {
192 if ((dominfo.crashed || dominfo.shutdown)
193 && !domain->shutdown) {
194 domain->shutdown = 1;
195 notify = 1;
196 }
197 if (!dominfo.dying)
198 continue;
199 }
200 talloc_free(domain->conn);
201 notify = 1;
202 }
204 if (notify)
205 fire_watches(NULL, "@releaseDomain", false);
206 }
208 /* We scan all domains rather than use the information given here. */
209 void handle_event(void)
210 {
211 evtchn_port_t port;
213 if ((port = xc_evtchn_pending(xce_handle)) == -1)
214 barf_perror("Failed to read from event fd");
216 if (port == virq_port)
217 domain_cleanup();
219 #ifndef TESTING
220 if (xc_evtchn_unmask(xce_handle, port) == -1)
221 barf_perror("Failed to write to event fd");
222 #endif
223 }
225 bool domain_can_read(struct connection *conn)
226 {
227 struct xenstore_domain_interface *intf = conn->domain->interface;
228 return (intf->req_cons != intf->req_prod);
229 }
231 bool domain_is_unprivileged(struct connection *conn)
232 {
233 return (conn && conn->domain && conn->domain->domid != 0);
234 }
236 bool domain_can_write(struct connection *conn)
237 {
238 struct xenstore_domain_interface *intf = conn->domain->interface;
239 return ((intf->rsp_prod - intf->rsp_cons) != XENSTORE_RING_SIZE);
240 }
242 static char *talloc_domain_path(void *context, unsigned int domid)
243 {
244 return talloc_asprintf(context, "/local/domain/%u", domid);
245 }
247 static struct domain *new_domain(void *context, unsigned int domid,
248 int port)
249 {
250 struct domain *domain;
251 int rc;
254 domain = talloc(context, struct domain);
255 domain->port = 0;
256 domain->shutdown = 0;
257 domain->domid = domid;
258 domain->path = talloc_domain_path(domain, domid);
260 list_add(&domain->list, &domains);
261 talloc_set_destructor(domain, destroy_domain);
263 /* Tell kernel we're interested in this event. */
264 rc = xc_evtchn_bind_interdomain(xce_handle, domid, port);
265 if (rc == -1)
266 return NULL;
267 domain->port = rc;
269 domain->conn = new_connection(writechn, readchn);
270 domain->conn->domain = domain;
271 domain->conn->id = domid;
273 domain->remote_port = port;
274 domain->nbentry = 0;
275 domain->nbwatch = 0;
277 return domain;
278 }
281 static struct domain *find_domain_by_domid(unsigned int domid)
282 {
283 struct domain *i;
285 list_for_each_entry(i, &domains, list) {
286 if (i->domid == domid)
287 return i;
288 }
289 return NULL;
290 }
293 /* domid, mfn, evtchn, path */
294 void do_introduce(struct connection *conn, struct buffered_data *in)
295 {
296 struct domain *domain;
297 char *vec[3];
298 unsigned int domid;
299 unsigned long mfn;
300 evtchn_port_t port;
302 if (get_strings(in, vec, ARRAY_SIZE(vec)) < ARRAY_SIZE(vec)) {
303 send_error(conn, EINVAL);
304 return;
305 }
307 if (conn->id != 0 || !conn->can_write) {
308 send_error(conn, EACCES);
309 return;
310 }
312 domid = atoi(vec[0]);
313 mfn = atol(vec[1]);
314 port = atoi(vec[2]);
316 /* Sanity check args. */
317 if (port <= 0) {
318 send_error(conn, EINVAL);
319 return;
320 }
322 domain = find_domain_by_domid(domid);
324 if (domain == NULL) {
325 /* Hang domain off "in" until we're finished. */
326 domain = new_domain(in, domid, port);
327 if (!domain) {
328 send_error(conn, errno);
329 return;
330 }
331 domain->interface = xc_map_foreign_range(
332 *xc_handle, domid,
333 getpagesize(), PROT_READ|PROT_WRITE, mfn);
334 if (!domain->interface) {
335 send_error(conn, errno);
336 return;
337 }
338 domain->mfn = mfn;
340 /* Now domain belongs to its connection. */
341 talloc_steal(domain->conn, domain);
343 fire_watches(conn, "@introduceDomain", false);
344 }
345 else {
346 /* Check that the given details match the ones we have
347 previously recorded. */
348 if (port != domain->remote_port ||
349 mfn != domain->mfn) {
350 send_error(conn, EINVAL);
351 return;
352 }
353 }
355 send_ack(conn, XS_INTRODUCE);
356 }
358 /* domid */
359 void do_release(struct connection *conn, const char *domid_str)
360 {
361 struct domain *domain;
362 unsigned int domid;
364 if (!domid_str) {
365 send_error(conn, EINVAL);
366 return;
367 }
369 domid = atoi(domid_str);
370 if (!domid) {
371 send_error(conn, EINVAL);
372 return;
373 }
375 if (conn->id != 0) {
376 send_error(conn, EACCES);
377 return;
378 }
380 domain = find_domain_by_domid(domid);
381 if (!domain) {
382 send_error(conn, ENOENT);
383 return;
384 }
386 if (!domain->conn) {
387 send_error(conn, EINVAL);
388 return;
389 }
391 talloc_free(domain->conn);
393 fire_watches(conn, "@releaseDomain", false);
395 send_ack(conn, XS_RELEASE);
396 }
398 void do_get_domain_path(struct connection *conn, const char *domid_str)
399 {
400 char *path;
402 if (!domid_str) {
403 send_error(conn, EINVAL);
404 return;
405 }
407 path = talloc_domain_path(conn, atoi(domid_str));
409 send_reply(conn, XS_GET_DOMAIN_PATH, path, strlen(path) + 1);
411 talloc_free(path);
412 }
414 void do_is_domain_introduced(struct connection *conn, const char *domid_str)
415 {
416 int result;
417 unsigned int domid;
419 if (!domid_str) {
420 send_error(conn, EINVAL);
421 return;
422 }
424 domid = atoi(domid_str);
425 if (domid == DOMID_SELF)
426 result = 1;
427 else
428 result = (find_domain_by_domid(domid) != NULL);
430 send_reply(conn, XS_IS_DOMAIN_INTRODUCED, result ? "T" : "F", 2);
431 }
433 static int close_xc_handle(void *_handle)
434 {
435 xc_interface_close(*(int *)_handle);
436 return 0;
437 }
439 /* Returns the implicit path of a connection (only domains have this) */
440 const char *get_implicit_path(const struct connection *conn)
441 {
442 if (!conn->domain)
443 return NULL;
444 return conn->domain->path;
445 }
447 /* Restore existing connections. */
448 void restore_existing_connections(void)
449 {
450 }
452 static int dom0_init(void)
453 {
454 evtchn_port_t port;
455 struct domain *dom0;
457 port = xenbus_evtchn();
458 if (port == -1)
459 return -1;
461 dom0 = new_domain(NULL, 0, port);
463 dom0->interface = xenbus_map();
464 if (dom0->interface == NULL)
465 return -1;
467 talloc_steal(dom0->conn, dom0);
469 xc_evtchn_notify(xce_handle, dom0->port);
471 return 0;
472 }
474 /* Returns the event channel handle. */
475 int domain_init(void)
476 {
477 int rc;
479 xc_handle = talloc(talloc_autofree_context(), int);
480 if (!xc_handle)
481 barf_perror("Failed to allocate domain handle");
483 *xc_handle = xc_interface_open();
484 if (*xc_handle < 0)
485 barf_perror("Failed to open connection to hypervisor");
487 talloc_set_destructor(xc_handle, close_xc_handle);
489 xce_handle = xc_evtchn_open();
491 if (xce_handle < 0)
492 barf_perror("Failed to open evtchn device");
494 if (dom0_init() != 0)
495 barf_perror("Failed to initialize dom0 state");
497 if ((rc = xc_evtchn_bind_virq(xce_handle, VIRQ_DOM_EXC)) == -1)
498 barf_perror("Failed to bind to domain exception virq port");
499 virq_port = rc;
501 return xce_handle;
502 }
504 void domain_entry_inc(struct connection *conn)
505 {
506 if (!conn || !conn->domain)
507 return;
508 conn->domain->nbentry++;
509 }
511 void domain_entry_dec(struct connection *conn)
512 {
513 if (!conn || !conn->domain)
514 return;
515 if (conn->domain->nbentry)
516 conn->domain->nbentry--;
517 }
519 int domain_entry(struct connection *conn)
520 {
521 return (domain_is_unprivileged(conn))
522 ? conn->domain->nbentry
523 : 0;
524 }
526 void domain_watch_inc(struct connection *conn)
527 {
528 if (!conn || !conn->domain)
529 return;
530 conn->domain->nbwatch++;
531 }
533 void domain_watch_dec(struct connection *conn)
534 {
535 if (!conn || !conn->domain)
536 return;
537 if (conn->domain->nbwatch)
538 conn->domain->nbwatch--;
539 }
541 int domain_watch(struct connection *conn)
542 {
543 return (domain_is_unprivileged(conn))
544 ? conn->domain->nbwatch
545 : 0;
546 }
548 /*
549 * Local variables:
550 * c-file-style: "linux"
551 * indent-tabs-mode: t
552 * c-indent-level: 8
553 * c-basic-offset: 8
554 * tab-width: 8
555 * End:
556 */