direct-io.hg

view tools/console/daemon/io.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 69df979de1a7
children 95584b819b72
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 evtchn_port_t 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->size = 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/limit", dom->conspath) != -1;
167 if (!success)
168 goto out;
169 data = xs_read(xs, 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, NULL, path, slave, strlen(slave));
180 free(path);
181 if (!success)
182 goto out;
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 if (dom->tty_fd == -1) {
273 dom->tty_fd = domain_create_tty(dom);
275 if (dom->tty_fd == -1) {
276 err = errno;
277 close(dom->evtchn_fd);
278 dom->evtchn_fd = -1;
279 dom->local_port = -1;
280 goto out;
281 }
282 }
284 out:
285 return err;
286 }
288 static bool watch_domain(struct domain *dom, bool watch)
289 {
290 char domid_str[3 + MAX_STRLEN(dom->domid)];
291 bool success;
293 sprintf(domid_str, "dom%u", dom->domid);
294 if (watch)
295 success = xs_watch(xs, dom->conspath, domid_str);
296 else
297 success = xs_unwatch(xs, dom->conspath, domid_str);
298 if (success)
299 domain_create_ring(dom);
300 return success;
301 }
303 static struct domain *create_domain(int domid)
304 {
305 struct domain *dom;
306 char *s;
308 dom = (struct domain *)malloc(sizeof(struct domain));
309 if (dom == NULL) {
310 dolog(LOG_ERR, "Out of memory %s:%s():L%d",
311 __FILE__, __FUNCTION__, __LINE__);
312 exit(ENOMEM);
313 }
315 dom->domid = domid;
316 dom->conspath = xs_get_domain_path(xs, dom->domid);
317 s = realloc(dom->conspath, strlen(dom->conspath) +
318 strlen("/console") + 1);
319 if (s == NULL)
320 goto out;
321 dom->conspath = s;
322 strcat(dom->conspath, "/console");
324 dom->tty_fd = -1;
325 dom->is_dead = false;
326 dom->buffer.data = 0;
327 dom->buffer.size = 0;
328 dom->buffer.capacity = 0;
329 dom->buffer.max_capacity = 0;
330 dom->next = NULL;
332 dom->ring_ref = -1;
333 dom->local_port = -1;
334 dom->interface = NULL;
335 dom->evtchn_fd = -1;
337 if (!watch_domain(dom, true))
338 goto out;
340 dom->next = dom_head;
341 dom_head = dom;
343 dolog(LOG_DEBUG, "New domain %d", domid);
345 return dom;
346 out:
347 free(dom->conspath);
348 free(dom);
349 return NULL;
350 }
352 static struct domain *lookup_domain(int domid)
353 {
354 struct domain *dom;
356 for (dom = dom_head; dom; dom = dom->next)
357 if (dom->domid == domid)
358 return dom;
359 return NULL;
360 }
362 static void remove_domain(struct domain *dom)
363 {
364 struct domain **pp;
366 dolog(LOG_DEBUG, "Removing domain-%d", dom->domid);
368 for (pp = &dom_head; *pp; pp = &(*pp)->next) {
369 if (dom == *pp) {
370 *pp = dom->next;
371 free(dom);
372 break;
373 }
374 }
375 }
377 static void cleanup_domain(struct domain *d)
378 {
379 if (d->tty_fd != -1) {
380 close(d->tty_fd);
381 d->tty_fd = -1;
382 }
384 free(d->buffer.data);
385 d->buffer.data = NULL;
387 free(d->conspath);
388 d->conspath = NULL;
390 remove_domain(d);
391 }
393 static void shutdown_domain(struct domain *d)
394 {
395 d->is_dead = true;
396 watch_domain(d, false);
397 if (d->interface != NULL)
398 munmap(d->interface, getpagesize());
399 d->interface = NULL;
400 if (d->evtchn_fd != -1)
401 close(d->evtchn_fd);
402 d->evtchn_fd = -1;
403 cleanup_domain(d);
404 }
406 void enum_domains(void)
407 {
408 int domid = 1;
409 xc_dominfo_t dominfo;
410 struct domain *dom;
412 while (xc_domain_getinfo(xc, domid, 1, &dominfo) == 1) {
413 dom = lookup_domain(dominfo.domid);
414 if (dominfo.dying) {
415 if (dom)
416 shutdown_domain(dom);
417 } else {
418 if (dom == NULL)
419 create_domain(dominfo.domid);
420 }
421 domid = dominfo.domid + 1;
422 }
423 }
425 static void handle_tty_read(struct domain *dom)
426 {
427 ssize_t len = 0;
428 char msg[80];
429 int i;
430 struct xencons_interface *intf = dom->interface;
431 XENCONS_RING_IDX cons, prod;
433 cons = intf->in_cons;
434 prod = intf->in_prod;
435 mb();
437 if (sizeof(intf->in) > (prod - cons))
438 len = sizeof(intf->in) - (prod - cons);
439 if (len > sizeof(msg))
440 len = sizeof(msg);
442 if (len == 0)
443 return;
445 len = read(dom->tty_fd, msg, len);
446 if (len < 1) {
447 close(dom->tty_fd);
448 dom->tty_fd = -1;
450 if (domain_is_valid(dom->domid)) {
451 dom->tty_fd = domain_create_tty(dom);
452 } else {
453 shutdown_domain(dom);
454 }
455 } else if (domain_is_valid(dom->domid)) {
456 for (i = 0; i < len; i++) {
457 intf->in[MASK_XENCONS_IDX(prod++, intf->in)] =
458 msg[i];
459 }
460 wmb();
461 intf->in_prod = prod;
462 evtchn_notify(dom);
463 } else {
464 close(dom->tty_fd);
465 dom->tty_fd = -1;
466 shutdown_domain(dom);
467 }
468 }
470 static void handle_tty_write(struct domain *dom)
471 {
472 ssize_t len;
474 len = write(dom->tty_fd, dom->buffer.data, dom->buffer.size);
475 if (len < 1) {
476 close(dom->tty_fd);
477 dom->tty_fd = -1;
479 if (domain_is_valid(dom->domid)) {
480 dom->tty_fd = domain_create_tty(dom);
481 } else {
482 shutdown_domain(dom);
483 }
484 } else {
485 buffer_advance(&dom->buffer, len);
486 }
487 }
489 static void handle_ring_read(struct domain *dom)
490 {
491 evtchn_port_t v;
493 if (!read_sync(dom->evtchn_fd, &v, sizeof(v)))
494 return;
496 buffer_append(dom);
498 (void)write_sync(dom->evtchn_fd, &v, sizeof(v));
499 }
501 static void handle_xs(int fd)
502 {
503 char **vec;
504 int domid;
505 struct domain *dom;
506 unsigned int num;
508 vec = xs_read_watch(xs, &num);
509 if (!vec)
510 return;
512 if (!strcmp(vec[XS_WATCH_TOKEN], "domlist"))
513 enum_domains();
514 else if (sscanf(vec[XS_WATCH_TOKEN], "dom%u", &domid) == 1) {
515 dom = lookup_domain(domid);
516 /* We may get watches firing for domains that have recently
517 been removed, so dom may be NULL here. */
518 if (dom && dom->is_dead == false)
519 domain_create_ring(dom);
520 }
522 free(vec);
523 }
525 void handle_io(void)
526 {
527 fd_set readfds, writefds;
528 int ret;
530 do {
531 struct domain *d, *n;
532 struct timeval tv = { 100, 0 };
533 int max_fd = -1;
535 FD_ZERO(&readfds);
536 FD_ZERO(&writefds);
538 FD_SET(xs_fileno(xs), &readfds);
539 max_fd = MAX(xs_fileno(xs), max_fd);
541 for (d = dom_head; d; d = d->next) {
542 if (d->evtchn_fd != -1) {
543 FD_SET(d->evtchn_fd, &readfds);
544 max_fd = MAX(d->evtchn_fd, max_fd);
545 }
547 if (d->tty_fd != -1) {
548 if (!d->is_dead)
549 FD_SET(d->tty_fd, &readfds);
551 if (!buffer_empty(&d->buffer))
552 FD_SET(d->tty_fd, &writefds);
553 max_fd = MAX(d->tty_fd, max_fd);
554 }
555 }
557 ret = select(max_fd + 1, &readfds, &writefds, 0, &tv);
559 if (FD_ISSET(xs_fileno(xs), &readfds))
560 handle_xs(xs_fileno(xs));
562 for (d = dom_head; d; d = n) {
563 n = d->next;
564 if (d->evtchn_fd != -1 &&
565 FD_ISSET(d->evtchn_fd, &readfds))
566 handle_ring_read(d);
568 if (d->tty_fd != -1) {
569 if (FD_ISSET(d->tty_fd, &readfds))
570 handle_tty_read(d);
572 if (FD_ISSET(d->tty_fd, &writefds))
573 handle_tty_write(d);
575 if (d->is_dead)
576 cleanup_domain(d);
577 }
578 }
579 } while (ret > -1);
580 }
582 /*
583 * Local variables:
584 * c-file-style: "linux"
585 * indent-tabs-mode: t
586 * c-indent-level: 8
587 * c-basic-offset: 8
588 * tab-width: 8
589 * End:
590 */