direct-io.hg

view tools/console/daemon/io.c @ 7480:a90d670c98b9

Change the semantics of GetDomainPath so that it always succeeds, regardless of
whether a domain has been introduced to the store. Added a separate message
XS_IS_DOMAIN_INTRODUCED and API for that (xs_is_domain_introduced) to determine
whether the domain has really been introduced. This change means that the
tools can determine the correct domain path earlier in the domain creation
process, which is particularly a factor with live migration, as it allows us
to create the devices earlier in the process, and unpause the new domain before
performing the introduce. Until recently we already had these features, but
the simplification of the interface between xend and xenstored caused breakage.

No longer clear out the domain path when a domain is introduced -- this was a
hack to work around the recent problematic semantics of GetDomainPath.

Do not write the contents of the info block to the store. All the configuration
info is written to the /vm path, and anything else in the info block is either
dealt with explicitly or is ephemeral and has no place in the store.

Signed-off-by: Ewan Mellor <ewan@xensource.com>
author emellor@leeni.uk.xensource.com
date Sun Oct 23 22:45:15 2005 +0100 (2005-10-23)
parents 40b4860f554a
children e6591119fda0
line source
1 /*
2 * Copyright (C) International Business Machines Corp., 2005
3 * Author(s): Anthony Liguori <aliguori@us.ibm.com>
4 *
5 * Xen Console Daemon
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; under version 2 of the License.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
21 #define _GNU_SOURCE
23 #include "utils.h"
24 #include "io.h"
25 #include <xenctrl.h>
26 #include <xs.h>
27 #include <xen/linux/evtchn.h>
28 #include <xen/io/console.h>
30 #include <malloc.h>
31 #include <stdlib.h>
32 #include <errno.h>
33 #include <string.h>
34 #include <sys/select.h>
35 #include <fcntl.h>
36 #include <unistd.h>
37 #include <termios.h>
38 #include <stdarg.h>
39 #include <sys/ioctl.h>
40 #include <sys/mman.h>
42 #define MAX(a, b) (((a) > (b)) ? (a) : (b))
43 #define MIN(a, b) (((a) < (b)) ? (a) : (b))
45 /* Each 10 bits takes ~ 3 digits, plus one, plus one for nul terminator. */
46 #define MAX_STRLEN(x) ((sizeof(x) * CHAR_BIT + CHAR_BIT-1) / 10 * 3 + 2)
48 struct buffer
49 {
50 char *data;
51 size_t size;
52 size_t capacity;
53 size_t max_capacity;
54 };
56 struct domain
57 {
58 int domid;
59 int tty_fd;
60 bool is_dead;
61 struct buffer buffer;
62 struct domain *next;
63 char *conspath;
64 int ring_ref;
65 int local_port;
66 int evtchn_fd;
67 struct xencons_interface *interface;
68 };
70 static struct domain *dom_head;
72 static void evtchn_notify(struct domain *dom)
73 {
74 struct ioctl_evtchn_notify notify;
75 notify.port = dom->local_port;
76 (void)ioctl(dom->evtchn_fd, IOCTL_EVTCHN_NOTIFY, &notify);
77 }
79 static void buffer_append(struct domain *dom)
80 {
81 struct buffer *buffer = &dom->buffer;
82 XENCONS_RING_IDX cons, prod, size;
83 struct xencons_interface *intf = dom->interface;
85 cons = intf->out_cons;
86 prod = intf->out_prod;
87 mb();
89 size = prod - cons;
90 if ((size == 0) || (size > sizeof(intf->out)))
91 return;
93 if ((buffer->capacity - buffer->size) < size) {
94 buffer->capacity += (size + 1024);
95 buffer->data = realloc(buffer->data, buffer->capacity);
96 if (buffer->data == NULL) {
97 dolog(LOG_ERR, "Memory allocation failed");
98 exit(ENOMEM);
99 }
100 }
102 while (cons != prod)
103 buffer->data[buffer->size++] = intf->out[
104 MASK_XENCONS_IDX(cons++, intf->out)];
106 mb();
107 intf->out_cons = cons;
108 evtchn_notify(dom);
110 if (buffer->max_capacity &&
111 buffer->size > buffer->max_capacity) {
112 memmove(buffer->data + (buffer->size -
113 buffer->max_capacity),
114 buffer->data, buffer->max_capacity);
115 buffer->data = realloc(buffer->data,
116 buffer->max_capacity);
117 buffer->capacity = buffer->max_capacity;
118 }
119 }
121 static bool buffer_empty(struct buffer *buffer)
122 {
123 return buffer->size == 0;
124 }
126 static void buffer_advance(struct buffer *buffer, size_t size)
127 {
128 size = MIN(size, buffer->size);
129 memmove(buffer->data, buffer + size, buffer->size - size);
130 buffer->size -= size;
131 }
133 static bool domain_is_valid(int domid)
134 {
135 bool ret;
136 xc_dominfo_t info;
138 ret = (xc_domain_getinfo(xc, domid, 1, &info) == 1 &&
139 info.domid == domid);
141 return ret;
142 }
144 static int domain_create_tty(struct domain *dom)
145 {
146 char *path;
147 int master;
148 bool success;
150 if ((master = getpt()) == -1 ||
151 grantpt(master) == -1 || unlockpt(master) == -1) {
152 dolog(LOG_ERR, "Failed to create tty for domain-%d",
153 dom->domid);
154 master = -1;
155 } else {
156 const char *slave = ptsname(master);
157 struct termios term;
158 char *data;
159 unsigned int len;
161 if (tcgetattr(master, &term) != -1) {
162 cfmakeraw(&term);
163 tcsetattr(master, TCSAFLUSH, &term);
164 }
166 success = asprintf(&path, "%s/tty", dom->conspath) != -1;
167 if (!success)
168 goto out;
169 success = xs_write(xs, NULL, path, slave, strlen(slave));
170 free(path);
171 if (!success)
172 goto out;
174 success = asprintf(&path, "%s/limit", dom->conspath) != -1;
175 if (!success)
176 goto out;
177 data = xs_read(xs, NULL, path, &len);
178 if (data) {
179 dom->buffer.max_capacity = strtoul(data, 0, 0);
180 free(data);
181 }
182 free(path);
183 }
185 return master;
186 out:
187 close(master);
188 return -1;
189 }
191 /* Takes tuples of names, scanf-style args, and void **, NULL terminated. */
192 int xs_gather(struct xs_handle *xs, const char *dir, ...)
193 {
194 va_list ap;
195 const char *name;
196 char *path;
197 int ret = 0;
199 va_start(ap, dir);
200 while (ret == 0 && (name = va_arg(ap, char *)) != NULL) {
201 const char *fmt = va_arg(ap, char *);
202 void *result = va_arg(ap, void *);
203 char *p;
205 asprintf(&path, "%s/%s", dir, name);
206 p = xs_read(xs, NULL, path, NULL);
207 free(path);
208 if (p == NULL) {
209 ret = ENOENT;
210 break;
211 }
212 if (fmt) {
213 if (sscanf(p, fmt, result) == 0)
214 ret = EINVAL;
215 free(p);
216 } else
217 *(char **)result = p;
218 }
219 va_end(ap);
220 return ret;
221 }
223 static int domain_create_ring(struct domain *dom)
224 {
225 int err, remote_port, ring_ref, rc;
226 struct ioctl_evtchn_bind_interdomain bind;
228 err = xs_gather(xs, dom->conspath,
229 "ring-ref", "%u", &ring_ref,
230 "port", "%i", &remote_port,
231 NULL);
232 if (err)
233 goto out;
235 if (ring_ref != dom->ring_ref) {
236 if (dom->interface != NULL)
237 munmap(dom->interface, getpagesize());
238 dom->interface = xc_map_foreign_range(
239 xc, dom->domid, getpagesize(),
240 PROT_READ|PROT_WRITE,
241 (unsigned long)ring_ref);
242 if (dom->interface == NULL) {
243 err = EINVAL;
244 goto out;
245 }
246 dom->ring_ref = ring_ref;
247 }
249 dom->local_port = -1;
250 if (dom->evtchn_fd != -1)
251 close(dom->evtchn_fd);
253 /* Opening evtchn independently for each console is a bit
254 * wasteful, but that's how the code is structured... */
255 dom->evtchn_fd = open("/dev/xen/evtchn", O_RDWR);
256 if (dom->evtchn_fd == -1) {
257 err = errno;
258 goto out;
259 }
261 bind.remote_domain = dom->domid;
262 bind.remote_port = remote_port;
263 rc = ioctl(dom->evtchn_fd, IOCTL_EVTCHN_BIND_INTERDOMAIN, &bind);
264 if (rc == -1) {
265 err = errno;
266 close(dom->evtchn_fd);
267 dom->evtchn_fd = -1;
268 goto out;
269 }
270 dom->local_port = rc;
272 out:
273 return err;
274 }
276 static bool watch_domain(struct domain *dom, bool watch)
277 {
278 char domid_str[3 + MAX_STRLEN(dom->domid)];
279 bool success;
281 sprintf(domid_str, "dom%u", dom->domid);
282 if (watch)
283 success = xs_watch(xs, dom->conspath, domid_str);
284 else
285 success = xs_unwatch(xs, dom->conspath, domid_str);
286 if (success)
287 domain_create_ring(dom);
288 return success;
289 }
291 static struct domain *create_domain(int domid)
292 {
293 struct domain *dom;
294 char *s;
296 dom = (struct domain *)malloc(sizeof(struct domain));
297 if (dom == NULL) {
298 dolog(LOG_ERR, "Out of memory %s:%s():L%d",
299 __FILE__, __FUNCTION__, __LINE__);
300 exit(ENOMEM);
301 }
303 dom->domid = domid;
304 dom->conspath = xs_get_domain_path(xs, dom->domid);
305 s = realloc(dom->conspath, strlen(dom->conspath) +
306 strlen("/console") + 1);
307 if (s == NULL)
308 goto out;
309 dom->conspath = s;
310 strcat(dom->conspath, "/console");
312 dom->tty_fd = domain_create_tty(dom);
313 dom->is_dead = false;
314 dom->buffer.data = 0;
315 dom->buffer.size = 0;
316 dom->buffer.capacity = 0;
317 dom->buffer.max_capacity = 0;
318 dom->next = NULL;
320 dom->ring_ref = -1;
321 dom->local_port = -1;
322 dom->interface = NULL;
323 dom->evtchn_fd = -1;
325 if (!watch_domain(dom, true))
326 goto out;
328 dom->next = dom_head;
329 dom_head = dom;
331 dolog(LOG_DEBUG, "New domain %d", domid);
333 return dom;
334 out:
335 if (dom->conspath)
336 free(dom->conspath);
337 free(dom);
338 return NULL;
339 }
341 static struct domain *lookup_domain(int domid)
342 {
343 struct domain *dom;
345 for (dom = dom_head; dom; dom = dom->next)
346 if (dom->domid == domid)
347 return dom;
348 return NULL;
349 }
351 static void remove_domain(struct domain *dom)
352 {
353 struct domain **pp;
355 dolog(LOG_DEBUG, "Removing domain-%d", dom->domid);
357 for (pp = &dom_head; *pp; pp = &(*pp)->next) {
358 if (dom == *pp) {
359 *pp = dom->next;
360 free(dom);
361 break;
362 }
363 }
364 }
366 static void cleanup_domain(struct domain *d)
367 {
368 if (!buffer_empty(&d->buffer))
369 return;
371 if (d->buffer.data)
372 free(d->buffer.data);
373 d->buffer.data = NULL;
374 if (d->tty_fd != -1)
375 close(d->tty_fd);
376 d->tty_fd = -1;
377 remove_domain(d);
378 }
380 static void shutdown_domain(struct domain *d)
381 {
382 d->is_dead = true;
383 watch_domain(d, false);
384 if (d->interface != NULL)
385 munmap(d->interface, getpagesize());
386 d->interface = NULL;
387 if (d->evtchn_fd != -1)
388 close(d->evtchn_fd);
389 d->evtchn_fd = -1;
390 cleanup_domain(d);
391 }
393 void enum_domains(void)
394 {
395 int domid = 1;
396 xc_dominfo_t dominfo;
397 struct domain *dom;
399 while (xc_domain_getinfo(xc, domid, 1, &dominfo) == 1) {
400 dom = lookup_domain(dominfo.domid);
401 if (dominfo.dying) {
402 if (dom)
403 shutdown_domain(dom);
404 } else {
405 if (dom == NULL)
406 create_domain(dominfo.domid);
407 }
408 domid = dominfo.domid + 1;
409 }
410 }
412 static void handle_tty_read(struct domain *dom)
413 {
414 ssize_t len = 0;
415 char msg[80];
416 int i;
417 struct xencons_interface *intf = dom->interface;
418 XENCONS_RING_IDX cons, prod;
420 cons = intf->in_cons;
421 prod = intf->in_prod;
422 mb();
424 if (sizeof(intf->in) > (prod - cons))
425 len = sizeof(intf->in) - (prod - cons);
426 if (len > sizeof(msg))
427 len = sizeof(msg);
429 if (len == 0)
430 return;
432 len = read(dom->tty_fd, msg, len);
433 if (len < 1) {
434 close(dom->tty_fd);
435 dom->tty_fd = -1;
437 if (domain_is_valid(dom->domid)) {
438 dom->tty_fd = domain_create_tty(dom);
439 } else {
440 shutdown_domain(dom);
441 }
442 } else if (domain_is_valid(dom->domid)) {
443 for (i = 0; i < len; i++) {
444 intf->in[MASK_XENCONS_IDX(prod++, intf->in)] =
445 msg[i];
446 }
447 wmb();
448 intf->in_prod = prod;
449 evtchn_notify(dom);
450 } else {
451 close(dom->tty_fd);
452 dom->tty_fd = -1;
453 shutdown_domain(dom);
454 }
455 }
457 static void handle_tty_write(struct domain *dom)
458 {
459 ssize_t len;
461 len = write(dom->tty_fd, dom->buffer.data, dom->buffer.size);
462 if (len < 1) {
463 close(dom->tty_fd);
464 dom->tty_fd = -1;
466 if (domain_is_valid(dom->domid)) {
467 dom->tty_fd = domain_create_tty(dom);
468 } else {
469 shutdown_domain(dom);
470 }
471 } else {
472 buffer_advance(&dom->buffer, len);
473 }
474 }
476 static void handle_ring_read(struct domain *dom)
477 {
478 uint16_t v;
480 if (!read_sync(dom->evtchn_fd, &v, sizeof(v)))
481 return;
483 buffer_append(dom);
485 (void)write_sync(dom->evtchn_fd, &v, sizeof(v));
486 }
488 static void handle_xs(int fd)
489 {
490 char **vec;
491 int domid;
492 struct domain *dom;
493 unsigned int num;
495 vec = xs_read_watch(xs, &num);
496 if (!vec)
497 return;
499 if (!strcmp(vec[XS_WATCH_TOKEN], "domlist"))
500 enum_domains();
501 else if (sscanf(vec[XS_WATCH_TOKEN], "dom%u", &domid) == 1) {
502 dom = lookup_domain(domid);
503 if (dom->is_dead == false)
504 domain_create_ring(dom);
505 }
507 free(vec);
508 }
510 void handle_io(void)
511 {
512 fd_set readfds, writefds;
513 int ret;
515 do {
516 struct domain *d, *n;
517 struct timeval tv = { 100, 0 };
518 int max_fd = -1;
520 FD_ZERO(&readfds);
521 FD_ZERO(&writefds);
523 FD_SET(xs_fileno(xs), &readfds);
524 max_fd = MAX(xs_fileno(xs), max_fd);
526 for (d = dom_head; d; d = d->next) {
527 if (d->evtchn_fd != -1) {
528 FD_SET(d->evtchn_fd, &readfds);
529 max_fd = MAX(d->evtchn_fd, max_fd);
530 }
532 if (d->tty_fd != -1) {
533 if (!d->is_dead)
534 FD_SET(d->tty_fd, &readfds);
536 if (!buffer_empty(&d->buffer))
537 FD_SET(d->tty_fd, &writefds);
538 max_fd = MAX(d->tty_fd, max_fd);
539 }
540 }
542 ret = select(max_fd + 1, &readfds, &writefds, 0, &tv);
544 if (FD_ISSET(xs_fileno(xs), &readfds))
545 handle_xs(xs_fileno(xs));
547 for (d = dom_head; d; d = n) {
548 n = d->next;
549 if (d->evtchn_fd != -1 &&
550 FD_ISSET(d->evtchn_fd, &readfds))
551 handle_ring_read(d);
553 if (d->tty_fd != -1) {
554 if (FD_ISSET(d->tty_fd, &readfds))
555 handle_tty_read(d);
557 if (FD_ISSET(d->tty_fd, &writefds))
558 handle_tty_write(d);
560 if (d->is_dead)
561 cleanup_domain(d);
562 }
563 }
564 } while (ret > -1);
565 }
567 /*
568 * Local variables:
569 * c-file-style: "linux"
570 * indent-tabs-mode: t
571 * c-indent-level: 8
572 * c-basic-offset: 8
573 * tab-width: 8
574 * End:
575 */