debuggers.hg

view tools/xenstore/xenstored_domain.c @ 22848:6341fe0f4e5a

Added tag 4.1.0-rc2 for changeset 9dca60d88c63
author Keir Fraser <keir@xen.org>
date Tue Jan 25 14:06:55 2011 +0000 (2011-01-25)
parents 60782cefa154
children
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>
25 #include <xenctrl.h>
27 #include "utils.h"
28 #include "talloc.h"
29 #include "xenstored_core.h"
30 #include "xenstored_domain.h"
31 #include "xenstored_transaction.h"
32 #include "xenstored_watch.h"
34 #include <xenctrl.h>
36 static xc_interface **xc_handle;
37 static evtchn_port_t virq_port;
39 xc_evtchn *xce_handle = NULL;
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 static bool check_indexes(XENSTORE_RING_IDX cons, XENSTORE_RING_IDX prod)
81 {
82 return ((prod - cons) <= XENSTORE_RING_SIZE);
83 }
85 static void *get_output_chunk(XENSTORE_RING_IDX cons,
86 XENSTORE_RING_IDX prod,
87 char *buf, uint32_t *len)
88 {
89 *len = XENSTORE_RING_SIZE - MASK_XENSTORE_IDX(prod);
90 if ((XENSTORE_RING_SIZE - (prod - cons)) < *len)
91 *len = XENSTORE_RING_SIZE - (prod - cons);
92 return buf + MASK_XENSTORE_IDX(prod);
93 }
95 static const void *get_input_chunk(XENSTORE_RING_IDX cons,
96 XENSTORE_RING_IDX prod,
97 const char *buf, uint32_t *len)
98 {
99 *len = XENSTORE_RING_SIZE - MASK_XENSTORE_IDX(cons);
100 if ((prod - cons) < *len)
101 *len = prod - cons;
102 return buf + MASK_XENSTORE_IDX(cons);
103 }
105 static int writechn(struct connection *conn,
106 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 xen_mb();
118 if (!check_indexes(cons, prod)) {
119 errno = EIO;
120 return -1;
121 }
123 dest = get_output_chunk(cons, prod, intf->rsp, &avail);
124 if (avail < len)
125 len = avail;
127 memcpy(dest, data, len);
128 xen_mb();
129 intf->rsp_prod += len;
131 xc_evtchn_notify(xce_handle, conn->domain->port);
133 return len;
134 }
136 static int readchn(struct connection *conn, void *data, unsigned int len)
137 {
138 uint32_t avail;
139 const void *src;
140 struct xenstore_domain_interface *intf = conn->domain->interface;
141 XENSTORE_RING_IDX cons, prod;
143 /* Must read indexes once, and before anything else, and verified. */
144 cons = intf->req_cons;
145 prod = intf->req_prod;
146 xen_mb();
148 if (!check_indexes(cons, prod)) {
149 errno = EIO;
150 return -1;
151 }
153 src = get_input_chunk(cons, prod, intf->req, &avail);
154 if (avail < len)
155 len = avail;
157 memcpy(data, src, len);
158 xen_mb();
159 intf->req_cons += len;
161 xc_evtchn_notify(xce_handle, conn->domain->port);
163 return len;
164 }
166 static int destroy_domain(void *_domain)
167 {
168 struct domain *domain = _domain;
170 list_del(&domain->list);
172 if (domain->port) {
173 if (xc_evtchn_unbind(xce_handle, domain->port) == -1)
174 eprintf("> Unbinding port %i failed!\n", domain->port);
175 }
177 if (domain->interface)
178 munmap(domain->interface, getpagesize());
180 fire_watches(NULL, "@releaseDomain", false);
182 return 0;
183 }
185 static void domain_cleanup(void)
186 {
187 xc_dominfo_t dominfo;
188 struct domain *domain, *tmp;
189 int notify = 0;
191 list_for_each_entry_safe(domain, tmp, &domains, list) {
192 if (xc_domain_getinfo(*xc_handle, domain->domid, 1,
193 &dominfo) == 1 &&
194 dominfo.domid == domain->domid) {
195 if ((dominfo.crashed || dominfo.shutdown)
196 && !domain->shutdown) {
197 domain->shutdown = 1;
198 notify = 1;
199 }
200 if (!dominfo.dying)
201 continue;
202 }
203 talloc_free(domain->conn);
204 notify = 0; /* destroy_domain() fires the watch */
205 }
207 if (notify)
208 fire_watches(NULL, "@releaseDomain", false);
209 }
211 /* We scan all domains rather than use the information given here. */
212 void handle_event(void)
213 {
214 evtchn_port_t port;
216 if ((port = xc_evtchn_pending(xce_handle)) == -1)
217 barf_perror("Failed to read from event fd");
219 if (port == virq_port)
220 domain_cleanup();
222 if (xc_evtchn_unmask(xce_handle, port) == -1)
223 barf_perror("Failed to write to event fd");
224 }
226 bool domain_can_read(struct connection *conn)
227 {
228 struct xenstore_domain_interface *intf = conn->domain->interface;
229 return (intf->req_cons != intf->req_prod);
230 }
232 bool domain_is_unprivileged(struct connection *conn)
233 {
234 return (conn && conn->domain && conn->domain->domid != 0);
235 }
237 bool domain_can_write(struct connection *conn)
238 {
239 struct xenstore_domain_interface *intf = conn->domain->interface;
240 return ((intf->rsp_prod - intf->rsp_cons) != XENSTORE_RING_SIZE);
241 }
243 static char *talloc_domain_path(void *context, unsigned int domid)
244 {
245 return talloc_asprintf(context, "/local/domain/%u", domid);
246 }
248 static struct domain *new_domain(void *context, unsigned int domid,
249 int port)
250 {
251 struct domain *domain;
252 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 }
292 static void domain_conn_reset(struct domain *domain)
293 {
294 struct connection *conn = domain->conn;
295 struct buffered_data *out;
297 conn_delete_all_watches(conn);
298 conn_delete_all_transactions(conn);
300 while ((out = list_top(&conn->out_list, struct buffered_data, list))) {
301 list_del(&out->list);
302 talloc_free(out);
303 }
305 talloc_free(conn->in->buffer);
306 memset(conn->in, 0, sizeof(*conn->in));
307 conn->in->inhdr = true;
309 domain->interface->req_cons = domain->interface->req_prod = 0;
310 domain->interface->rsp_cons = domain->interface->rsp_prod = 0;
311 }
313 /* domid, mfn, evtchn, path */
314 void do_introduce(struct connection *conn, struct buffered_data *in)
315 {
316 struct domain *domain;
317 char *vec[3];
318 unsigned int domid;
319 unsigned long mfn;
320 evtchn_port_t port;
321 int rc;
322 struct xenstore_domain_interface *interface;
324 if (get_strings(in, vec, ARRAY_SIZE(vec)) < ARRAY_SIZE(vec)) {
325 send_error(conn, EINVAL);
326 return;
327 }
329 if (conn->id != 0 || !conn->can_write) {
330 send_error(conn, EACCES);
331 return;
332 }
334 domid = atoi(vec[0]);
335 mfn = atol(vec[1]);
336 port = atoi(vec[2]);
338 /* Sanity check args. */
339 if (port <= 0) {
340 send_error(conn, EINVAL);
341 return;
342 }
344 domain = find_domain_by_domid(domid);
346 if (domain == NULL) {
347 interface = xc_map_foreign_range(
348 *xc_handle, domid,
349 getpagesize(), PROT_READ|PROT_WRITE, mfn);
350 if (!interface) {
351 send_error(conn, errno);
352 return;
353 }
354 /* Hang domain off "in" until we're finished. */
355 domain = new_domain(in, domid, port);
356 if (!domain) {
357 munmap(interface, getpagesize());
358 send_error(conn, errno);
359 return;
360 }
361 domain->interface = interface;
362 domain->mfn = mfn;
364 /* Now domain belongs to its connection. */
365 talloc_steal(domain->conn, domain);
367 fire_watches(NULL, "@introduceDomain", false);
368 } else if ((domain->mfn == mfn) && (domain->conn != conn)) {
369 /* Use XS_INTRODUCE for recreating the xenbus event-channel. */
370 if (domain->port)
371 xc_evtchn_unbind(xce_handle, domain->port);
372 rc = xc_evtchn_bind_interdomain(xce_handle, domid, port);
373 domain->port = (rc == -1) ? 0 : rc;
374 domain->remote_port = port;
375 } else {
376 send_error(conn, EINVAL);
377 return;
378 }
380 domain_conn_reset(domain);
382 send_ack(conn, XS_INTRODUCE);
383 }
385 void do_set_target(struct connection *conn, struct buffered_data *in)
386 {
387 char *vec[2];
388 unsigned int domid, tdomid;
389 struct domain *domain, *tdomain;
390 if (get_strings(in, vec, ARRAY_SIZE(vec)) < ARRAY_SIZE(vec)) {
391 send_error(conn, EINVAL);
392 return;
393 }
395 if (conn->id != 0 || !conn->can_write) {
396 send_error(conn, EACCES);
397 return;
398 }
400 domid = atoi(vec[0]);
401 tdomid = atoi(vec[1]);
403 domain = find_domain_by_domid(domid);
404 if (!domain) {
405 send_error(conn, ENOENT);
406 return;
407 }
408 if (!domain->conn) {
409 send_error(conn, EINVAL);
410 return;
411 }
413 tdomain = find_domain_by_domid(tdomid);
414 if (!tdomain) {
415 send_error(conn, ENOENT);
416 return;
417 }
419 if (!tdomain->conn) {
420 send_error(conn, EINVAL);
421 return;
422 }
424 talloc_reference(domain->conn, tdomain->conn);
425 domain->conn->target = tdomain->conn;
427 send_ack(conn, XS_SET_TARGET);
428 }
430 /* domid */
431 void do_release(struct connection *conn, const char *domid_str)
432 {
433 struct domain *domain;
434 unsigned int domid;
436 if (!domid_str) {
437 send_error(conn, EINVAL);
438 return;
439 }
441 domid = atoi(domid_str);
442 if (!domid) {
443 send_error(conn, EINVAL);
444 return;
445 }
447 if (conn->id != 0) {
448 send_error(conn, EACCES);
449 return;
450 }
452 domain = find_domain_by_domid(domid);
453 if (!domain) {
454 send_error(conn, ENOENT);
455 return;
456 }
458 if (!domain->conn) {
459 send_error(conn, EINVAL);
460 return;
461 }
463 talloc_free(domain->conn);
465 send_ack(conn, XS_RELEASE);
466 }
468 void do_resume(struct connection *conn, const char *domid_str)
469 {
470 struct domain *domain;
471 unsigned int domid;
473 if (!domid_str) {
474 send_error(conn, EINVAL);
475 return;
476 }
478 domid = atoi(domid_str);
479 if (!domid) {
480 send_error(conn, EINVAL);
481 return;
482 }
484 if (conn->id != 0) {
485 send_error(conn, EACCES);
486 return;
487 }
489 domain = find_domain_by_domid(domid);
490 if (!domain) {
491 send_error(conn, ENOENT);
492 return;
493 }
495 if (!domain->conn) {
496 send_error(conn, EINVAL);
497 return;
498 }
500 domain->shutdown = 0;
502 send_ack(conn, XS_RESUME);
503 }
505 void do_get_domain_path(struct connection *conn, const char *domid_str)
506 {
507 char *path;
509 if (!domid_str) {
510 send_error(conn, EINVAL);
511 return;
512 }
514 path = talloc_domain_path(conn, atoi(domid_str));
516 send_reply(conn, XS_GET_DOMAIN_PATH, path, strlen(path) + 1);
518 talloc_free(path);
519 }
521 void do_is_domain_introduced(struct connection *conn, const char *domid_str)
522 {
523 int result;
524 unsigned int domid;
526 if (!domid_str) {
527 send_error(conn, EINVAL);
528 return;
529 }
531 domid = atoi(domid_str);
532 if (domid == DOMID_SELF)
533 result = 1;
534 else
535 result = (find_domain_by_domid(domid) != NULL);
537 send_reply(conn, XS_IS_DOMAIN_INTRODUCED, result ? "T" : "F", 2);
538 }
540 static int close_xc_handle(void *_handle)
541 {
542 xc_interface_close(*(xc_interface**)_handle);
543 return 0;
544 }
546 /* Returns the implicit path of a connection (only domains have this) */
547 const char *get_implicit_path(const struct connection *conn)
548 {
549 if (!conn->domain)
550 return "/local/domain/0";
551 return conn->domain->path;
552 }
554 /* Restore existing connections. */
555 void restore_existing_connections(void)
556 {
557 }
559 static int dom0_init(void)
560 {
561 evtchn_port_t port;
562 struct domain *dom0;
564 port = xenbus_evtchn();
565 if (port == -1)
566 return -1;
568 dom0 = new_domain(NULL, 0, port);
569 if (dom0 == NULL)
570 return -1;
572 dom0->interface = xenbus_map();
573 if (dom0->interface == NULL)
574 return -1;
576 talloc_steal(dom0->conn, dom0);
578 xc_evtchn_notify(xce_handle, dom0->port);
580 return 0;
581 }
583 void domain_init(void)
584 {
585 int rc;
587 xc_handle = talloc(talloc_autofree_context(), xc_interface*);
588 if (!xc_handle)
589 barf_perror("Failed to allocate domain handle");
591 *xc_handle = xc_interface_open(0,0,0);
592 if (!*xc_handle)
593 barf_perror("Failed to open connection to hypervisor");
595 talloc_set_destructor(xc_handle, close_xc_handle);
597 xce_handle = xc_evtchn_open(NULL, 0);
599 if (xce_handle == NULL)
600 barf_perror("Failed to open evtchn device");
602 if (dom0_init() != 0)
603 barf_perror("Failed to initialize dom0 state");
605 if ((rc = xc_evtchn_bind_virq(xce_handle, VIRQ_DOM_EXC)) == -1)
606 barf_perror("Failed to bind to domain exception virq port");
607 virq_port = rc;
608 }
610 void domain_entry_inc(struct connection *conn, struct node *node)
611 {
612 struct domain *d;
614 if (!conn)
615 return;
617 if (node->perms && node->perms[0].id != conn->id) {
618 if (conn->transaction) {
619 transaction_entry_inc(conn->transaction,
620 node->perms[0].id);
621 } else {
622 d = find_domain_by_domid(node->perms[0].id);
623 if (d)
624 d->nbentry++;
625 }
626 } else if (conn->domain) {
627 if (conn->transaction) {
628 transaction_entry_inc(conn->transaction,
629 conn->domain->domid);
630 } else {
631 conn->domain->nbentry++;
632 }
633 }
634 }
636 void domain_entry_dec(struct connection *conn, struct node *node)
637 {
638 struct domain *d;
640 if (!conn)
641 return;
643 if (node->perms && node->perms[0].id != conn->id) {
644 if (conn->transaction) {
645 transaction_entry_dec(conn->transaction,
646 node->perms[0].id);
647 } else {
648 d = find_domain_by_domid(node->perms[0].id);
649 if (d && d->nbentry)
650 d->nbentry--;
651 }
652 } else if (conn->domain && conn->domain->nbentry) {
653 if (conn->transaction) {
654 transaction_entry_dec(conn->transaction,
655 conn->domain->domid);
656 } else {
657 conn->domain->nbentry--;
658 }
659 }
660 }
662 void domain_entry_fix(unsigned int domid, int num)
663 {
664 struct domain *d;
666 d = find_domain_by_domid(domid);
667 if (d && ((d->nbentry += num) < 0))
668 d->nbentry = 0;
669 }
671 int domain_entry(struct connection *conn)
672 {
673 return (domain_is_unprivileged(conn))
674 ? conn->domain->nbentry
675 : 0;
676 }
678 void domain_watch_inc(struct connection *conn)
679 {
680 if (!conn || !conn->domain)
681 return;
682 conn->domain->nbwatch++;
683 }
685 void domain_watch_dec(struct connection *conn)
686 {
687 if (!conn || !conn->domain)
688 return;
689 if (conn->domain->nbwatch)
690 conn->domain->nbwatch--;
691 }
693 int domain_watch(struct connection *conn)
694 {
695 return (domain_is_unprivileged(conn))
696 ? conn->domain->nbwatch
697 : 0;
698 }
700 /*
701 * Local variables:
702 * c-file-style: "linux"
703 * indent-tabs-mode: t
704 * c-indent-level: 8
705 * c-basic-offset: 8
706 * tab-width: 8
707 * End:
708 */