ia64/xen-unstable

view tools/xenstore/xenstored_domain.c @ 19731:01748ccc4da3

Intel VT-d: fix Stoakley boot issue with iommu=1

Signed-off-by: Weidong Han <Weidong.han@intel.com>
Signed-off-by: Allen Kay <allen.m.kay@intel.com>
author Keir Fraser <keir.fraser@citrix.com>
date Fri Jun 05 09:25:50 2009 +0100 (2009-06-05)
parents 88818d55e95a
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>
26 #include "utils.h"
27 #include "talloc.h"
28 #include "xenstored_core.h"
29 #include "xenstored_domain.h"
30 #include "xenstored_transaction.h"
31 #include "xenstored_watch.h"
33 #include <xenctrl.h>
35 static int *xc_handle;
36 static evtchn_port_t virq_port;
38 int xce_handle = -1;
40 struct domain
41 {
42 struct list_head list;
44 /* The id of this domain */
45 unsigned int domid;
47 /* Event channel port */
48 evtchn_port_t port;
50 /* The remote end of the event channel, used only to validate
51 repeated domain introductions. */
52 evtchn_port_t remote_port;
54 /* The mfn associated with the event channel, used only to validate
55 repeated domain introductions. */
56 unsigned long mfn;
58 /* Domain path in store. */
59 char *path;
61 /* Shared page. */
62 struct xenstore_domain_interface *interface;
64 /* The connection associated with this. */
65 struct connection *conn;
67 /* Have we noticed that this domain is shutdown? */
68 int shutdown;
70 /* number of entry from this domain in the store */
71 int nbentry;
73 /* number of watch for this domain */
74 int nbwatch;
75 };
77 static LIST_HEAD(domains);
79 static bool check_indexes(XENSTORE_RING_IDX cons, XENSTORE_RING_IDX prod)
80 {
81 return ((prod - cons) <= XENSTORE_RING_SIZE);
82 }
84 static void *get_output_chunk(XENSTORE_RING_IDX cons,
85 XENSTORE_RING_IDX prod,
86 char *buf, uint32_t *len)
87 {
88 *len = XENSTORE_RING_SIZE - MASK_XENSTORE_IDX(prod);
89 if ((XENSTORE_RING_SIZE - (prod - cons)) < *len)
90 *len = XENSTORE_RING_SIZE - (prod - cons);
91 return buf + MASK_XENSTORE_IDX(prod);
92 }
94 static const void *get_input_chunk(XENSTORE_RING_IDX cons,
95 XENSTORE_RING_IDX prod,
96 const char *buf, uint32_t *len)
97 {
98 *len = XENSTORE_RING_SIZE - MASK_XENSTORE_IDX(cons);
99 if ((prod - cons) < *len)
100 *len = prod - cons;
101 return buf + MASK_XENSTORE_IDX(cons);
102 }
104 static int writechn(struct connection *conn,
105 const void *data, unsigned int len)
106 {
107 uint32_t avail;
108 void *dest;
109 struct xenstore_domain_interface *intf = conn->domain->interface;
110 XENSTORE_RING_IDX cons, prod;
112 /* Must read indexes once, and before anything else, and verified. */
113 cons = intf->rsp_cons;
114 prod = intf->rsp_prod;
115 xen_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 xen_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 xen_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 xen_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 fire_watches(NULL, "@releaseDomain", false);
181 return 0;
182 }
184 static void domain_cleanup(void)
185 {
186 xc_dominfo_t dominfo;
187 struct domain *domain, *tmp;
188 int notify = 0;
190 list_for_each_entry_safe(domain, tmp, &domains, list) {
191 if (xc_domain_getinfo(*xc_handle, domain->domid, 1,
192 &dominfo) == 1 &&
193 dominfo.domid == domain->domid) {
194 if ((dominfo.crashed || dominfo.shutdown)
195 && !domain->shutdown) {
196 domain->shutdown = 1;
197 notify = 1;
198 }
199 if (!dominfo.dying)
200 continue;
201 }
202 talloc_free(domain->conn);
203 notify = 0; /* destroy_domain() fires the watch */
204 }
206 if (notify)
207 fire_watches(NULL, "@releaseDomain", false);
208 }
210 /* We scan all domains rather than use the information given here. */
211 void handle_event(void)
212 {
213 evtchn_port_t port;
215 if ((port = xc_evtchn_pending(xce_handle)) == -1)
216 barf_perror("Failed to read from event fd");
218 if (port == virq_port)
219 domain_cleanup();
221 if (xc_evtchn_unmask(xce_handle, port) == -1)
222 barf_perror("Failed to write to event fd");
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;
253 domain = talloc(context, struct domain);
254 domain->port = 0;
255 domain->shutdown = 0;
256 domain->domid = domid;
257 domain->path = talloc_domain_path(domain, domid);
259 list_add(&domain->list, &domains);
260 talloc_set_destructor(domain, destroy_domain);
262 /* Tell kernel we're interested in this event. */
263 rc = xc_evtchn_bind_interdomain(xce_handle, domid, port);
264 if (rc == -1)
265 return NULL;
266 domain->port = rc;
268 domain->conn = new_connection(writechn, readchn);
269 domain->conn->domain = domain;
270 domain->conn->id = domid;
272 domain->remote_port = port;
273 domain->nbentry = 0;
274 domain->nbwatch = 0;
276 return domain;
277 }
280 static struct domain *find_domain_by_domid(unsigned int domid)
281 {
282 struct domain *i;
284 list_for_each_entry(i, &domains, list) {
285 if (i->domid == domid)
286 return i;
287 }
288 return NULL;
289 }
291 static void domain_conn_reset(struct domain *domain)
292 {
293 struct connection *conn = domain->conn;
294 struct buffered_data *out;
296 conn_delete_all_watches(conn);
297 conn_delete_all_transactions(conn);
299 while ((out = list_top(&conn->out_list, struct buffered_data, list))) {
300 list_del(&out->list);
301 talloc_free(out);
302 }
304 talloc_free(conn->in->buffer);
305 memset(conn->in, 0, sizeof(*conn->in));
306 conn->in->inhdr = true;
308 domain->interface->req_cons = domain->interface->req_prod = 0;
309 domain->interface->rsp_cons = domain->interface->rsp_prod = 0;
310 }
312 /* domid, mfn, evtchn, path */
313 void do_introduce(struct connection *conn, struct buffered_data *in)
314 {
315 struct domain *domain;
316 char *vec[3];
317 unsigned int domid;
318 unsigned long mfn;
319 evtchn_port_t port;
320 int rc;
321 struct xenstore_domain_interface *interface;
323 if (get_strings(in, vec, ARRAY_SIZE(vec)) < ARRAY_SIZE(vec)) {
324 send_error(conn, EINVAL);
325 return;
326 }
328 if (conn->id != 0 || !conn->can_write) {
329 send_error(conn, EACCES);
330 return;
331 }
333 domid = atoi(vec[0]);
334 mfn = atol(vec[1]);
335 port = atoi(vec[2]);
337 /* Sanity check args. */
338 if (port <= 0) {
339 send_error(conn, EINVAL);
340 return;
341 }
343 domain = find_domain_by_domid(domid);
345 if (domain == NULL) {
346 interface = xc_map_foreign_range(
347 *xc_handle, domid,
348 getpagesize(), PROT_READ|PROT_WRITE, mfn);
349 if (!interface) {
350 send_error(conn, errno);
351 return;
352 }
353 /* Hang domain off "in" until we're finished. */
354 domain = new_domain(in, domid, port);
355 if (!domain) {
356 munmap(interface, getpagesize());
357 send_error(conn, errno);
358 return;
359 }
360 domain->interface = interface;
361 domain->mfn = mfn;
363 /* Now domain belongs to its connection. */
364 talloc_steal(domain->conn, domain);
366 fire_watches(NULL, "@introduceDomain", false);
367 } else if ((domain->mfn == mfn) && (domain->conn != conn)) {
368 /* Use XS_INTRODUCE for recreating the xenbus event-channel. */
369 if (domain->port)
370 xc_evtchn_unbind(xce_handle, domain->port);
371 rc = xc_evtchn_bind_interdomain(xce_handle, domid, port);
372 domain->port = (rc == -1) ? 0 : rc;
373 domain->remote_port = port;
374 } else {
375 send_error(conn, EINVAL);
376 return;
377 }
379 domain_conn_reset(domain);
381 send_ack(conn, XS_INTRODUCE);
382 }
384 void do_set_target(struct connection *conn, struct buffered_data *in)
385 {
386 char *vec[2];
387 unsigned int domid, tdomid;
388 struct domain *domain, *tdomain;
389 if (get_strings(in, vec, ARRAY_SIZE(vec)) < ARRAY_SIZE(vec)) {
390 send_error(conn, EINVAL);
391 return;
392 }
394 if (conn->id != 0 || !conn->can_write) {
395 send_error(conn, EACCES);
396 return;
397 }
399 domid = atoi(vec[0]);
400 tdomid = atoi(vec[1]);
402 domain = find_domain_by_domid(domid);
403 if (!domain) {
404 send_error(conn, ENOENT);
405 return;
406 }
407 if (!domain->conn) {
408 send_error(conn, EINVAL);
409 return;
410 }
412 tdomain = find_domain_by_domid(tdomid);
413 if (!tdomain) {
414 send_error(conn, ENOENT);
415 return;
416 }
418 if (!tdomain->conn) {
419 send_error(conn, EINVAL);
420 return;
421 }
423 talloc_reference(domain->conn, tdomain->conn);
424 domain->conn->target = tdomain->conn;
426 send_ack(conn, XS_SET_TARGET);
427 }
429 /* domid */
430 void do_release(struct connection *conn, const char *domid_str)
431 {
432 struct domain *domain;
433 unsigned int domid;
435 if (!domid_str) {
436 send_error(conn, EINVAL);
437 return;
438 }
440 domid = atoi(domid_str);
441 if (!domid) {
442 send_error(conn, EINVAL);
443 return;
444 }
446 if (conn->id != 0) {
447 send_error(conn, EACCES);
448 return;
449 }
451 domain = find_domain_by_domid(domid);
452 if (!domain) {
453 send_error(conn, ENOENT);
454 return;
455 }
457 if (!domain->conn) {
458 send_error(conn, EINVAL);
459 return;
460 }
462 talloc_free(domain->conn);
464 send_ack(conn, XS_RELEASE);
465 }
467 void do_resume(struct connection *conn, const char *domid_str)
468 {
469 struct domain *domain;
470 unsigned int domid;
472 if (!domid_str) {
473 send_error(conn, EINVAL);
474 return;
475 }
477 domid = atoi(domid_str);
478 if (!domid) {
479 send_error(conn, EINVAL);
480 return;
481 }
483 if (conn->id != 0) {
484 send_error(conn, EACCES);
485 return;
486 }
488 domain = find_domain_by_domid(domid);
489 if (!domain) {
490 send_error(conn, ENOENT);
491 return;
492 }
494 if (!domain->conn) {
495 send_error(conn, EINVAL);
496 return;
497 }
499 domain->shutdown = 0;
501 send_ack(conn, XS_RESUME);
502 }
504 void do_get_domain_path(struct connection *conn, const char *domid_str)
505 {
506 char *path;
508 if (!domid_str) {
509 send_error(conn, EINVAL);
510 return;
511 }
513 path = talloc_domain_path(conn, atoi(domid_str));
515 send_reply(conn, XS_GET_DOMAIN_PATH, path, strlen(path) + 1);
517 talloc_free(path);
518 }
520 void do_is_domain_introduced(struct connection *conn, const char *domid_str)
521 {
522 int result;
523 unsigned int domid;
525 if (!domid_str) {
526 send_error(conn, EINVAL);
527 return;
528 }
530 domid = atoi(domid_str);
531 if (domid == DOMID_SELF)
532 result = 1;
533 else
534 result = (find_domain_by_domid(domid) != NULL);
536 send_reply(conn, XS_IS_DOMAIN_INTRODUCED, result ? "T" : "F", 2);
537 }
539 static int close_xc_handle(void *_handle)
540 {
541 xc_interface_close(*(int *)_handle);
542 return 0;
543 }
545 /* Returns the implicit path of a connection (only domains have this) */
546 const char *get_implicit_path(const struct connection *conn)
547 {
548 if (!conn->domain)
549 return NULL;
550 return conn->domain->path;
551 }
553 /* Restore existing connections. */
554 void restore_existing_connections(void)
555 {
556 }
558 static int dom0_init(void)
559 {
560 evtchn_port_t port;
561 struct domain *dom0;
563 port = xenbus_evtchn();
564 if (port == -1)
565 return -1;
567 dom0 = new_domain(NULL, 0, port);
568 if (dom0 == NULL)
569 return -1;
571 dom0->interface = xenbus_map();
572 if (dom0->interface == NULL)
573 return -1;
575 talloc_steal(dom0->conn, dom0);
577 xc_evtchn_notify(xce_handle, dom0->port);
579 return 0;
580 }
582 /* Returns the event channel handle. */
583 int domain_init(void)
584 {
585 int rc;
587 xc_handle = talloc(talloc_autofree_context(), int);
588 if (!xc_handle)
589 barf_perror("Failed to allocate domain handle");
591 *xc_handle = xc_interface_open();
592 if (*xc_handle < 0)
593 barf_perror("Failed to open connection to hypervisor");
595 talloc_set_destructor(xc_handle, close_xc_handle);
597 xce_handle = xc_evtchn_open();
599 if (xce_handle < 0)
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;
609 return xce_handle;
610 }
612 void domain_entry_inc(struct connection *conn, struct node *node)
613 {
614 struct domain *d;
616 if (!conn)
617 return;
619 if (node->perms && node->perms[0].id != conn->id) {
620 if (conn->transaction) {
621 transaction_entry_inc(conn->transaction,
622 node->perms[0].id);
623 } else {
624 d = find_domain_by_domid(node->perms[0].id);
625 if (d)
626 d->nbentry++;
627 }
628 } else if (conn->domain) {
629 if (conn->transaction) {
630 transaction_entry_inc(conn->transaction,
631 conn->domain->domid);
632 } else {
633 conn->domain->nbentry++;
634 }
635 }
636 }
638 void domain_entry_dec(struct connection *conn, struct node *node)
639 {
640 struct domain *d;
642 if (!conn)
643 return;
645 if (node->perms && node->perms[0].id != conn->id) {
646 if (conn->transaction) {
647 transaction_entry_dec(conn->transaction,
648 node->perms[0].id);
649 } else {
650 d = find_domain_by_domid(node->perms[0].id);
651 if (d && d->nbentry)
652 d->nbentry--;
653 }
654 } else if (conn->domain && conn->domain->nbentry) {
655 if (conn->transaction) {
656 transaction_entry_dec(conn->transaction,
657 conn->domain->domid);
658 } else {
659 conn->domain->nbentry--;
660 }
661 }
662 }
664 void domain_entry_fix(unsigned int domid, int num)
665 {
666 struct domain *d;
668 d = find_domain_by_domid(domid);
669 if (d && ((d->nbentry += num) < 0))
670 d->nbentry = 0;
671 }
673 int domain_entry(struct connection *conn)
674 {
675 return (domain_is_unprivileged(conn))
676 ? conn->domain->nbentry
677 : 0;
678 }
680 void domain_watch_inc(struct connection *conn)
681 {
682 if (!conn || !conn->domain)
683 return;
684 conn->domain->nbwatch++;
685 }
687 void domain_watch_dec(struct connection *conn)
688 {
689 if (!conn || !conn->domain)
690 return;
691 if (conn->domain->nbwatch)
692 conn->domain->nbwatch--;
693 }
695 int domain_watch(struct connection *conn)
696 {
697 return (domain_is_unprivileged(conn))
698 ? conn->domain->nbwatch
699 : 0;
700 }
702 /*
703 * Local variables:
704 * c-file-style: "linux"
705 * indent-tabs-mode: t
706 * c-indent-level: 8
707 * c-basic-offset: 8
708 * tab-width: 8
709 * End:
710 */