direct-io.hg

view tools/xenstore/xenstored_domain.c @ 8151:f5b119533cc8

Define explicit evtchn_port_t type (32 bits) and plumb up
to user space thru /dev/xen/evtchn.

Signed-off-by: Keir Fraser <keir@xensource.com>
author kaf24@firebug.cl.cam.ac.uk
date Thu Dec 01 15:22:22 2005 +0100 (2005-12-01)
parents a90d670c98b9
children 74b7a81e5eed
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 <linux/ioctl.h>
22 #include <sys/ioctl.h>
23 #include <sys/mman.h>
24 #include <unistd.h>
25 #include <stdlib.h>
26 #include <stdarg.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <fcntl.h>
31 //#define DEBUG
32 #include "utils.h"
33 #include "talloc.h"
34 #include "xenstored_core.h"
35 #include "xenstored_domain.h"
36 #include "xenstored_proc.h"
37 #include "xenstored_watch.h"
38 #include "xenstored_test.h"
40 #include <xenctrl.h>
41 #include <xen/linux/evtchn.h>
43 static int *xc_handle;
44 static evtchn_port_t virq_port;
46 int eventchn_fd = -1;
48 struct domain
49 {
50 struct list_head list;
52 /* The id of this domain */
53 unsigned int domid;
55 /* Event channel port */
56 evtchn_port_t port;
58 /* The remote end of the event channel, used only to validate
59 repeated domain introductions. */
60 evtchn_port_t remote_port;
62 /* The mfn associated with the event channel, used only to validate
63 repeated domain introductions. */
64 unsigned long mfn;
66 /* Domain path in store. */
67 char *path;
69 /* Shared page. */
70 struct xenstore_domain_interface *interface;
72 /* The connection associated with this. */
73 struct connection *conn;
75 /* Have we noticed that this domain is shutdown? */
76 int shutdown;
77 };
79 static LIST_HEAD(domains);
81 #ifndef TESTING
82 static void evtchn_notify(int port)
83 {
84 int rc;
86 struct ioctl_evtchn_notify notify;
87 notify.port = port;
88 rc = ioctl(eventchn_fd, IOCTL_EVTCHN_NOTIFY, &notify);
89 }
90 #else
91 extern void evtchn_notify(int port);
92 #endif
94 /* FIXME: Mark connection as broken (close it?) when this happens. */
95 static bool check_indexes(XENSTORE_RING_IDX cons, XENSTORE_RING_IDX prod)
96 {
97 return ((prod - cons) <= XENSTORE_RING_SIZE);
98 }
100 static void *get_output_chunk(XENSTORE_RING_IDX cons,
101 XENSTORE_RING_IDX prod,
102 char *buf, uint32_t *len)
103 {
104 *len = XENSTORE_RING_SIZE - MASK_XENSTORE_IDX(prod);
105 if ((XENSTORE_RING_SIZE - (prod - cons)) < *len)
106 *len = XENSTORE_RING_SIZE - (prod - cons);
107 return buf + MASK_XENSTORE_IDX(prod);
108 }
110 static const void *get_input_chunk(XENSTORE_RING_IDX cons,
111 XENSTORE_RING_IDX prod,
112 const char *buf, uint32_t *len)
113 {
114 *len = XENSTORE_RING_SIZE - MASK_XENSTORE_IDX(cons);
115 if ((prod - cons) < *len)
116 *len = prod - cons;
117 return buf + MASK_XENSTORE_IDX(cons);
118 }
120 static int writechn(struct connection *conn, const void *data, unsigned int len)
121 {
122 uint32_t avail;
123 void *dest;
124 struct xenstore_domain_interface *intf = conn->domain->interface;
125 XENSTORE_RING_IDX cons, prod;
127 /* Must read indexes once, and before anything else, and verified. */
128 cons = intf->rsp_cons;
129 prod = intf->rsp_prod;
130 mb();
131 if (!check_indexes(cons, prod)) {
132 errno = EIO;
133 return -1;
134 }
136 dest = get_output_chunk(cons, prod, intf->rsp, &avail);
137 if (avail < len)
138 len = avail;
140 memcpy(dest, data, len);
141 mb();
142 intf->rsp_prod += len;
144 evtchn_notify(conn->domain->port);
146 return len;
147 }
149 static int readchn(struct connection *conn, void *data, unsigned int len)
150 {
151 uint32_t avail;
152 const void *src;
153 struct xenstore_domain_interface *intf = conn->domain->interface;
154 XENSTORE_RING_IDX cons, prod;
156 /* Must read indexes once, and before anything else, and verified. */
157 cons = intf->req_cons;
158 prod = intf->req_prod;
159 mb();
161 if (!check_indexes(cons, prod)) {
162 errno = EIO;
163 return -1;
164 }
166 src = get_input_chunk(cons, prod, intf->req, &avail);
167 if (avail < len)
168 len = avail;
170 memcpy(data, src, len);
171 mb();
172 intf->req_cons += len;
174 evtchn_notify(conn->domain->port);
176 return len;
177 }
179 static int destroy_domain(void *_domain)
180 {
181 struct domain *domain = _domain;
182 struct ioctl_evtchn_unbind unbind;
184 list_del(&domain->list);
186 if (domain->port) {
187 unbind.port = domain->port;
188 if (ioctl(eventchn_fd, IOCTL_EVTCHN_UNBIND, &unbind) == -1)
189 eprintf("> Unbinding port %i failed!\n", domain->port);
190 }
192 if (domain->interface)
193 munmap(domain->interface, getpagesize());
195 return 0;
196 }
198 static void domain_cleanup(void)
199 {
200 xc_dominfo_t dominfo;
201 struct domain *domain, *tmp;
202 int notify = 0;
204 list_for_each_entry_safe(domain, tmp, &domains, list) {
205 if (xc_domain_getinfo(*xc_handle, domain->domid, 1,
206 &dominfo) == 1 &&
207 dominfo.domid == domain->domid) {
208 if ((dominfo.crashed || dominfo.shutdown)
209 && !domain->shutdown) {
210 domain->shutdown = 1;
211 notify = 1;
212 }
213 if (!dominfo.dying)
214 continue;
215 }
216 talloc_free(domain->conn);
217 notify = 1;
218 }
220 if (notify)
221 fire_watches(NULL, "@releaseDomain", false);
222 }
224 /* We scan all domains rather than use the information given here. */
225 void handle_event(void)
226 {
227 evtchn_port_t port;
229 if (read(eventchn_fd, &port, sizeof(port)) != sizeof(port))
230 barf_perror("Failed to read from event fd");
232 if (port == virq_port)
233 domain_cleanup();
235 #ifndef TESTING
236 if (write(eventchn_fd, &port, sizeof(port)) != sizeof(port))
237 barf_perror("Failed to write to event fd");
238 #endif
239 }
241 bool domain_can_read(struct connection *conn)
242 {
243 struct xenstore_domain_interface *intf = conn->domain->interface;
244 return (intf->req_cons != intf->req_prod);
245 }
247 bool domain_can_write(struct connection *conn)
248 {
249 struct xenstore_domain_interface *intf = conn->domain->interface;
250 return ((intf->rsp_prod - intf->rsp_cons) != XENSTORE_RING_SIZE);
251 }
253 static char *talloc_domain_path(void *context, unsigned int domid)
254 {
255 return talloc_asprintf(context, "/local/domain/%u", domid);
256 }
258 static struct domain *new_domain(void *context, unsigned int domid,
259 unsigned long mfn, int port)
260 {
261 struct domain *domain;
262 struct ioctl_evtchn_bind_interdomain bind;
263 int rc;
266 domain = talloc(context, struct domain);
267 domain->port = 0;
268 domain->shutdown = 0;
269 domain->domid = domid;
270 domain->path = talloc_domain_path(domain, domid);
271 domain->interface = xc_map_foreign_range(
272 *xc_handle, domain->domid,
273 getpagesize(), PROT_READ|PROT_WRITE, mfn);
274 if (!domain->interface)
275 return NULL;
277 list_add(&domain->list, &domains);
278 talloc_set_destructor(domain, destroy_domain);
280 /* Tell kernel we're interested in this event. */
281 bind.remote_domain = domid;
282 bind.remote_port = port;
283 rc = ioctl(eventchn_fd, IOCTL_EVTCHN_BIND_INTERDOMAIN, &bind);
284 if (rc == -1)
285 return NULL;
286 domain->port = rc;
288 domain->conn = new_connection(writechn, readchn);
289 domain->conn->domain = domain;
291 domain->remote_port = port;
292 domain->mfn = mfn;
294 return domain;
295 }
298 static struct domain *find_domain_by_domid(unsigned int domid)
299 {
300 struct domain *i;
302 list_for_each_entry(i, &domains, list) {
303 if (i->domid == domid)
304 return i;
305 }
306 return NULL;
307 }
310 /* domid, mfn, evtchn, path */
311 void do_introduce(struct connection *conn, struct buffered_data *in)
312 {
313 struct domain *domain;
314 char *vec[3];
315 unsigned int domid;
316 unsigned long mfn;
317 evtchn_port_t port;
319 if (get_strings(in, vec, ARRAY_SIZE(vec)) < ARRAY_SIZE(vec)) {
320 send_error(conn, EINVAL);
321 return;
322 }
324 if (conn->id != 0 || !conn->can_write) {
325 send_error(conn, EACCES);
326 return;
327 }
329 domid = atoi(vec[0]);
330 mfn = atol(vec[1]);
331 port = atoi(vec[2]);
333 /* Sanity check args. */
334 if (port <= 0) {
335 send_error(conn, EINVAL);
336 return;
337 }
339 domain = find_domain_by_domid(domid);
341 if (domain == NULL) {
342 /* Hang domain off "in" until we're finished. */
343 domain = new_domain(in, domid, mfn, port);
344 if (!domain) {
345 send_error(conn, errno);
346 return;
347 }
349 /* Now domain belongs to its connection. */
350 talloc_steal(domain->conn, domain);
352 fire_watches(conn, "@introduceDomain", false);
353 }
354 else {
355 /* Check that the given details match the ones we have
356 previously recorded. */
357 if (port != domain->remote_port ||
358 mfn != domain->mfn) {
359 send_error(conn, EINVAL);
360 return;
361 }
362 }
364 send_ack(conn, XS_INTRODUCE);
365 }
367 /* domid */
368 void do_release(struct connection *conn, const char *domid_str)
369 {
370 struct domain *domain;
371 unsigned int domid;
373 if (!domid_str) {
374 send_error(conn, EINVAL);
375 return;
376 }
378 domid = atoi(domid_str);
379 if (!domid) {
380 send_error(conn, EINVAL);
381 return;
382 }
384 if (conn->id != 0) {
385 send_error(conn, EACCES);
386 return;
387 }
389 domain = find_domain_by_domid(domid);
390 if (!domain) {
391 send_error(conn, ENOENT);
392 return;
393 }
395 if (!domain->conn) {
396 send_error(conn, EINVAL);
397 return;
398 }
400 talloc_free(domain->conn);
402 fire_watches(conn, "@releaseDomain", false);
404 send_ack(conn, XS_RELEASE);
405 }
407 void do_get_domain_path(struct connection *conn, const char *domid_str)
408 {
409 char *path;
411 if (!domid_str) {
412 send_error(conn, EINVAL);
413 return;
414 }
416 path = talloc_domain_path(conn, atoi(domid_str));
418 send_reply(conn, XS_GET_DOMAIN_PATH, path, strlen(path) + 1);
420 talloc_free(path);
421 }
423 void do_is_domain_introduced(struct connection *conn, const char *domid_str)
424 {
425 int result;
426 unsigned int domid;
428 if (!domid_str) {
429 send_error(conn, EINVAL);
430 return;
431 }
433 domid = atoi(domid_str);
434 if (domid == DOMID_SELF)
435 result = 1;
436 else
437 result = (find_domain_by_domid(domid) != NULL);
439 send_reply(conn, XS_IS_DOMAIN_INTRODUCED, result ? "T" : "F", 2);
440 }
442 static int close_xc_handle(void *_handle)
443 {
444 xc_interface_close(*(int *)_handle);
445 return 0;
446 }
448 /* Returns the implicit path of a connection (only domains have this) */
449 const char *get_implicit_path(const struct connection *conn)
450 {
451 if (!conn->domain)
452 return NULL;
453 return conn->domain->path;
454 }
456 /* Restore existing connections. */
457 void restore_existing_connections(void)
458 {
459 }
461 static int dom0_init(void)
462 {
463 int rc, fd;
464 evtchn_port_t port;
465 unsigned long mfn;
466 char str[20];
467 struct domain *dom0;
469 fd = open(XENSTORED_PROC_MFN, O_RDONLY);
471 rc = read(fd, str, sizeof(str));
472 str[rc] = '\0';
473 mfn = strtoul(str, NULL, 0);
475 close(fd);
477 fd = open(XENSTORED_PROC_PORT, O_RDONLY);
479 rc = read(fd, str, sizeof(str));
480 str[rc] = '\0';
481 port = strtoul(str, NULL, 0);
483 close(fd);
486 dom0 = new_domain(NULL, 0, mfn, port);
487 talloc_steal(dom0->conn, dom0);
489 evtchn_notify(dom0->port);
491 return 0;
492 }
496 #define EVTCHN_DEV_NAME "/dev/xen/evtchn"
497 #define EVTCHN_DEV_MAJOR 10
498 #define EVTCHN_DEV_MINOR 201
501 /* Returns the event channel handle. */
502 int domain_init(void)
503 {
504 struct stat st;
505 struct ioctl_evtchn_bind_virq bind;
506 int rc;
508 xc_handle = talloc(talloc_autofree_context(), int);
509 if (!xc_handle)
510 barf_perror("Failed to allocate domain handle");
512 *xc_handle = xc_interface_open();
513 if (*xc_handle < 0)
514 barf_perror("Failed to open connection to hypervisor");
516 talloc_set_destructor(xc_handle, close_xc_handle);
518 #ifdef TESTING
519 eventchn_fd = fake_open_eventchn();
520 (void)&st;
521 #else
522 /* Make sure any existing device file links to correct device. */
523 if ((lstat(EVTCHN_DEV_NAME, &st) != 0) || !S_ISCHR(st.st_mode) ||
524 (st.st_rdev != makedev(EVTCHN_DEV_MAJOR, EVTCHN_DEV_MINOR)))
525 (void)unlink(EVTCHN_DEV_NAME);
527 reopen:
528 eventchn_fd = open(EVTCHN_DEV_NAME, O_NONBLOCK|O_RDWR);
529 if (eventchn_fd == -1) {
530 if ((errno == ENOENT) &&
531 ((mkdir("/dev/xen", 0755) == 0) || (errno == EEXIST)) &&
532 (mknod(EVTCHN_DEV_NAME, S_IFCHR|0600,
533 makedev(EVTCHN_DEV_MAJOR, EVTCHN_DEV_MINOR)) == 0))
534 goto reopen;
535 return -errno;
536 }
537 #endif
538 if (eventchn_fd < 0)
539 barf_perror("Failed to open evtchn device");
541 if (dom0_init() != 0)
542 barf_perror("Failed to initialize dom0 state");
544 bind.virq = VIRQ_DOM_EXC;
545 rc = ioctl(eventchn_fd, IOCTL_EVTCHN_BIND_VIRQ, &bind);
546 if (rc == -1)
547 barf_perror("Failed to bind to domain exception virq port");
548 virq_port = rc;
550 return eventchn_fd;
551 }
553 /*
554 * Local variables:
555 * c-file-style: "linux"
556 * indent-tabs-mode: t
557 * c-indent-level: 8
558 * c-basic-offset: 8
559 * tab-width: 8
560 * End:
561 */