direct-io.hg

view tools/console/daemon/io.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 7c994d80049c
children c61b8296b39a
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/io/console.h>
28 #include <xenctrl.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/mman.h>
41 #define MAX(a, b) (((a) > (b)) ? (a) : (b))
42 #define MIN(a, b) (((a) < (b)) ? (a) : (b))
44 /* Each 10 bits takes ~ 3 digits, plus one, plus one for nul terminator. */
45 #define MAX_STRLEN(x) ((sizeof(x) * CHAR_BIT + CHAR_BIT-1) / 10 * 3 + 2)
47 struct buffer
48 {
49 char *data;
50 size_t consumed;
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 evtchn_port_t local_port;
66 int xce_handle;
67 struct xencons_interface *interface;
68 };
70 static struct domain *dom_head;
72 static void buffer_append(struct domain *dom)
73 {
74 struct buffer *buffer = &dom->buffer;
75 XENCONS_RING_IDX cons, prod, size;
76 struct xencons_interface *intf = dom->interface;
78 cons = intf->out_cons;
79 prod = intf->out_prod;
80 mb();
82 size = prod - cons;
83 if ((size == 0) || (size > sizeof(intf->out)))
84 return;
86 if ((buffer->capacity - buffer->size) < size) {
87 buffer->capacity += (size + 1024);
88 buffer->data = realloc(buffer->data, buffer->capacity);
89 if (buffer->data == NULL) {
90 dolog(LOG_ERR, "Memory allocation failed");
91 exit(ENOMEM);
92 }
93 }
95 while (cons != prod)
96 buffer->data[buffer->size++] = intf->out[
97 MASK_XENCONS_IDX(cons++, intf->out)];
99 mb();
100 intf->out_cons = cons;
101 xc_evtchn_notify(dom->xce_handle, dom->local_port);
103 if (buffer->max_capacity &&
104 buffer->size > buffer->max_capacity) {
105 /* Discard the middle of the data. */
107 size_t over = buffer->size - buffer->max_capacity;
108 char *maxpos = buffer->data + buffer->max_capacity;
110 memmove(maxpos - over, maxpos, over);
111 buffer->data = realloc(buffer->data, buffer->max_capacity);
112 buffer->size = buffer->capacity = buffer->max_capacity;
114 if (buffer->consumed > buffer->max_capacity - over)
115 buffer->consumed = buffer->max_capacity - over;
116 }
117 }
119 static bool buffer_empty(struct buffer *buffer)
120 {
121 return buffer->size == 0;
122 }
124 static void buffer_advance(struct buffer *buffer, size_t len)
125 {
126 buffer->consumed += len;
127 if (buffer->consumed == buffer->size) {
128 buffer->consumed = 0;
129 buffer->size = 0;
130 }
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/limit", dom->conspath) != -1;
167 if (!success)
168 goto out;
169 data = xs_read(xs, XBT_NULL, path, &len);
170 if (data) {
171 dom->buffer.max_capacity = strtoul(data, 0, 0);
172 free(data);
173 }
174 free(path);
176 success = asprintf(&path, "%s/tty", dom->conspath) != -1;
177 if (!success)
178 goto out;
179 success = xs_write(xs, XBT_NULL, path, slave, strlen(slave));
180 free(path);
181 if (!success)
182 goto out;
184 if (fcntl(master, F_SETFL, O_NONBLOCK) == -1)
185 goto out;
186 }
188 return master;
189 out:
190 close(master);
191 return -1;
192 }
194 /* Takes tuples of names, scanf-style args, and void **, NULL terminated. */
195 int xs_gather(struct xs_handle *xs, const char *dir, ...)
196 {
197 va_list ap;
198 const char *name;
199 char *path;
200 int ret = 0;
202 va_start(ap, dir);
203 while (ret == 0 && (name = va_arg(ap, char *)) != NULL) {
204 const char *fmt = va_arg(ap, char *);
205 void *result = va_arg(ap, void *);
206 char *p;
208 asprintf(&path, "%s/%s", dir, name);
209 p = xs_read(xs, XBT_NULL, path, NULL);
210 free(path);
211 if (p == NULL) {
212 ret = ENOENT;
213 break;
214 }
215 if (fmt) {
216 if (sscanf(p, fmt, result) == 0)
217 ret = EINVAL;
218 free(p);
219 } else
220 *(char **)result = p;
221 }
222 va_end(ap);
223 return ret;
224 }
226 static int domain_create_ring(struct domain *dom)
227 {
228 int err, remote_port, ring_ref, rc;
230 err = xs_gather(xs, dom->conspath,
231 "ring-ref", "%u", &ring_ref,
232 "port", "%i", &remote_port,
233 NULL);
234 if (err)
235 goto out;
237 if (ring_ref != dom->ring_ref) {
238 if (dom->interface != NULL)
239 munmap(dom->interface, getpagesize());
240 dom->interface = xc_map_foreign_range(
241 xc, dom->domid, getpagesize(),
242 PROT_READ|PROT_WRITE,
243 (unsigned long)ring_ref);
244 if (dom->interface == NULL) {
245 err = EINVAL;
246 goto out;
247 }
248 dom->ring_ref = ring_ref;
249 }
251 dom->local_port = -1;
252 if (dom->xce_handle != -1)
253 xc_evtchn_close(dom->xce_handle);
255 /* Opening evtchn independently for each console is a bit
256 * wasteful, but that's how the code is structured... */
257 dom->xce_handle = xc_evtchn_open();
258 if (dom->xce_handle == -1) {
259 err = errno;
260 goto out;
261 }
263 rc = xc_evtchn_bind_interdomain(dom->xce_handle,
264 dom->domid, remote_port);
266 if (rc == -1) {
267 err = errno;
268 xc_evtchn_close(dom->xce_handle);
269 dom->xce_handle = -1;
270 goto out;
271 }
272 dom->local_port = rc;
274 if (dom->tty_fd == -1) {
275 dom->tty_fd = domain_create_tty(dom);
277 if (dom->tty_fd == -1) {
278 err = errno;
279 xc_evtchn_close(dom->xce_handle);
280 dom->xce_handle = -1;
281 dom->local_port = -1;
282 goto out;
283 }
284 }
286 out:
287 return err;
288 }
290 static bool watch_domain(struct domain *dom, bool watch)
291 {
292 char domid_str[3 + MAX_STRLEN(dom->domid)];
293 bool success;
295 sprintf(domid_str, "dom%u", dom->domid);
296 if (watch)
297 success = xs_watch(xs, dom->conspath, domid_str);
298 else
299 success = xs_unwatch(xs, dom->conspath, domid_str);
300 if (success)
301 domain_create_ring(dom);
302 return success;
303 }
305 static struct domain *create_domain(int domid)
306 {
307 struct domain *dom;
308 char *s;
310 dom = (struct domain *)malloc(sizeof(struct domain));
311 if (dom == NULL) {
312 dolog(LOG_ERR, "Out of memory %s:%s():L%d",
313 __FILE__, __FUNCTION__, __LINE__);
314 exit(ENOMEM);
315 }
317 dom->domid = domid;
318 dom->conspath = xs_get_domain_path(xs, dom->domid);
319 s = realloc(dom->conspath, strlen(dom->conspath) +
320 strlen("/console") + 1);
321 if (s == NULL)
322 goto out;
323 dom->conspath = s;
324 strcat(dom->conspath, "/console");
326 dom->tty_fd = -1;
327 dom->is_dead = false;
328 dom->buffer.data = 0;
329 dom->buffer.consumed = 0;
330 dom->buffer.size = 0;
331 dom->buffer.capacity = 0;
332 dom->buffer.max_capacity = 0;
333 dom->next = NULL;
335 dom->ring_ref = -1;
336 dom->local_port = -1;
337 dom->interface = NULL;
338 dom->xce_handle = -1;
340 if (!watch_domain(dom, true))
341 goto out;
343 dom->next = dom_head;
344 dom_head = dom;
346 dolog(LOG_DEBUG, "New domain %d", domid);
348 return dom;
349 out:
350 free(dom->conspath);
351 free(dom);
352 return NULL;
353 }
355 static struct domain *lookup_domain(int domid)
356 {
357 struct domain *dom;
359 for (dom = dom_head; dom; dom = dom->next)
360 if (dom->domid == domid)
361 return dom;
362 return NULL;
363 }
365 static void remove_domain(struct domain *dom)
366 {
367 struct domain **pp;
369 dolog(LOG_DEBUG, "Removing domain-%d", dom->domid);
371 for (pp = &dom_head; *pp; pp = &(*pp)->next) {
372 if (dom == *pp) {
373 *pp = dom->next;
374 free(dom);
375 break;
376 }
377 }
378 }
380 static void cleanup_domain(struct domain *d)
381 {
382 if (d->tty_fd != -1) {
383 close(d->tty_fd);
384 d->tty_fd = -1;
385 }
387 free(d->buffer.data);
388 d->buffer.data = NULL;
390 free(d->conspath);
391 d->conspath = NULL;
393 remove_domain(d);
394 }
396 static void shutdown_domain(struct domain *d)
397 {
398 d->is_dead = true;
399 watch_domain(d, false);
400 if (d->interface != NULL)
401 munmap(d->interface, getpagesize());
402 d->interface = NULL;
403 if (d->xce_handle != -1)
404 xc_evtchn_close(d->xce_handle);
405 d->xce_handle = -1;
406 cleanup_domain(d);
407 }
409 void enum_domains(void)
410 {
411 int domid = 1;
412 xc_dominfo_t dominfo;
413 struct domain *dom;
415 while (xc_domain_getinfo(xc, domid, 1, &dominfo) == 1) {
416 dom = lookup_domain(dominfo.domid);
417 if (dominfo.dying) {
418 if (dom)
419 shutdown_domain(dom);
420 } else {
421 if (dom == NULL)
422 create_domain(dominfo.domid);
423 }
424 domid = dominfo.domid + 1;
425 }
426 }
428 static int ring_free_bytes(struct domain *dom)
429 {
430 struct xencons_interface *intf = dom->interface;
431 XENCONS_RING_IDX cons, prod, space;
433 cons = intf->in_cons;
434 prod = intf->in_prod;
435 mb();
437 space = prod - cons;
438 if (space > sizeof(intf->in))
439 return 0; /* ring is screwed: ignore it */
441 return (sizeof(intf->in) - space);
442 }
444 static void handle_tty_read(struct domain *dom)
445 {
446 ssize_t len = 0;
447 char msg[80];
448 int i;
449 struct xencons_interface *intf = dom->interface;
450 XENCONS_RING_IDX prod;
452 len = ring_free_bytes(dom);
453 if (len == 0)
454 return;
456 if (len > sizeof(msg))
457 len = sizeof(msg);
459 len = read(dom->tty_fd, msg, len);
460 if (len < 1) {
461 close(dom->tty_fd);
462 dom->tty_fd = -1;
464 if (domain_is_valid(dom->domid)) {
465 dom->tty_fd = domain_create_tty(dom);
466 } else {
467 shutdown_domain(dom);
468 }
469 } else if (domain_is_valid(dom->domid)) {
470 prod = intf->in_prod;
471 for (i = 0; i < len; i++) {
472 intf->in[MASK_XENCONS_IDX(prod++, intf->in)] =
473 msg[i];
474 }
475 wmb();
476 intf->in_prod = prod;
477 xc_evtchn_notify(dom->xce_handle, dom->local_port);
478 } else {
479 close(dom->tty_fd);
480 dom->tty_fd = -1;
481 shutdown_domain(dom);
482 }
483 }
485 static void handle_tty_write(struct domain *dom)
486 {
487 ssize_t len;
489 len = write(dom->tty_fd, dom->buffer.data + dom->buffer.consumed,
490 dom->buffer.size - dom->buffer.consumed);
491 if (len < 1) {
492 dolog(LOG_DEBUG, "Write failed on domain %d: %zd, %d\n",
493 dom->domid, len, errno);
495 close(dom->tty_fd);
496 dom->tty_fd = -1;
498 if (domain_is_valid(dom->domid)) {
499 dom->tty_fd = domain_create_tty(dom);
500 } else {
501 shutdown_domain(dom);
502 }
503 } else {
504 buffer_advance(&dom->buffer, len);
505 }
506 }
508 static void handle_ring_read(struct domain *dom)
509 {
510 evtchn_port_t port;
512 if ((port = xc_evtchn_pending(dom->xce_handle)) == -1)
513 return;
515 buffer_append(dom);
517 (void)xc_evtchn_unmask(dom->xce_handle, port);
518 }
520 static void handle_xs(void)
521 {
522 char **vec;
523 int domid;
524 struct domain *dom;
525 unsigned int num;
527 vec = xs_read_watch(xs, &num);
528 if (!vec)
529 return;
531 if (!strcmp(vec[XS_WATCH_TOKEN], "domlist"))
532 enum_domains();
533 else if (sscanf(vec[XS_WATCH_TOKEN], "dom%u", &domid) == 1) {
534 dom = lookup_domain(domid);
535 /* We may get watches firing for domains that have recently
536 been removed, so dom may be NULL here. */
537 if (dom && dom->is_dead == false)
538 domain_create_ring(dom);
539 }
541 free(vec);
542 }
544 void handle_io(void)
545 {
546 fd_set readfds, writefds;
547 int ret;
549 do {
550 struct domain *d, *n;
551 int max_fd = -1;
553 FD_ZERO(&readfds);
554 FD_ZERO(&writefds);
556 FD_SET(xs_fileno(xs), &readfds);
557 max_fd = MAX(xs_fileno(xs), max_fd);
559 for (d = dom_head; d; d = d->next) {
560 if (d->xce_handle != -1) {
561 int evtchn_fd = xc_evtchn_fd(d->xce_handle);
562 FD_SET(evtchn_fd, &readfds);
563 max_fd = MAX(evtchn_fd, max_fd);
564 }
566 if (d->tty_fd != -1) {
567 if (!d->is_dead && ring_free_bytes(d))
568 FD_SET(d->tty_fd, &readfds);
570 if (!buffer_empty(&d->buffer))
571 FD_SET(d->tty_fd, &writefds);
572 max_fd = MAX(d->tty_fd, max_fd);
573 }
574 }
576 ret = select(max_fd + 1, &readfds, &writefds, 0, NULL);
578 if (FD_ISSET(xs_fileno(xs), &readfds))
579 handle_xs();
581 for (d = dom_head; d; d = n) {
582 n = d->next;
583 if (d->xce_handle != -1 &&
584 FD_ISSET(xc_evtchn_fd(d->xce_handle), &readfds))
585 handle_ring_read(d);
587 if (d->tty_fd != -1) {
588 if (FD_ISSET(d->tty_fd, &readfds))
589 handle_tty_read(d);
591 if (FD_ISSET(d->tty_fd, &writefds))
592 handle_tty_write(d);
594 if (d->is_dead)
595 cleanup_domain(d);
596 }
597 }
598 } while (ret > -1);
599 }
601 /*
602 * Local variables:
603 * c-file-style: "linux"
604 * indent-tabs-mode: t
605 * c-indent-level: 8
606 * c-basic-offset: 8
607 * tab-width: 8
608 * End:
609 */