source: trunk/packages/xen-3.1/xen-3.1/tools/xenstore/xenstored_domain.c @ 34

Last change on this file since 34 was 34, checked in by hartmans, 18 years ago

Add xen and xen-common

File size: 13.3 KB
Line 
1/*
2    Domain communications for Xen Store Daemon.
3    Copyright (C) 2005 Rusty Russell IBM Corporation
4
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.
9
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.
14
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*/
19
20#include <stdio.h>
21#include <sys/mman.h>
22#include <unistd.h>
23#include <stdlib.h>
24#include <stdarg.h>
25
26//#define DEBUG
27#include "utils.h"
28#include "talloc.h"
29#include "xenstored_core.h"
30#include "xenstored_domain.h"
31#include "xenstored_transaction.h"
32#include "xenstored_watch.h"
33#include "xenstored_test.h"
34
35#include <xenctrl.h>
36
37static int *xc_handle;
38static evtchn_port_t virq_port;
39
40int xce_handle = -1; 
41
42struct domain
43{
44        struct list_head list;
45
46        /* The id of this domain */
47        unsigned int domid;
48
49        /* Event channel port */
50        evtchn_port_t port;
51
52        /* The remote end of the event channel, used only to validate
53           repeated domain introductions. */
54        evtchn_port_t remote_port;
55
56        /* The mfn associated with the event channel, used only to validate
57           repeated domain introductions. */
58        unsigned long mfn;
59
60        /* Domain path in store. */
61        char *path;
62
63        /* Shared page. */
64        struct xenstore_domain_interface *interface;
65
66        /* The connection associated with this. */
67        struct connection *conn;
68
69        /* Have we noticed that this domain is shutdown? */
70        int shutdown;
71
72        /* number of entry from this domain in the store */
73        int nbentry;
74
75        /* number of watch for this domain */
76        int nbwatch;
77};
78
79static LIST_HEAD(domains);
80
81/* FIXME: Mark connection as broken (close it?) when this happens. */
82static bool check_indexes(XENSTORE_RING_IDX cons, XENSTORE_RING_IDX prod)
83{
84        return ((prod - cons) <= XENSTORE_RING_SIZE);
85}
86
87static void *get_output_chunk(XENSTORE_RING_IDX cons,
88                              XENSTORE_RING_IDX prod,
89                              char *buf, uint32_t *len)
90{
91        *len = XENSTORE_RING_SIZE - MASK_XENSTORE_IDX(prod);
92        if ((XENSTORE_RING_SIZE - (prod - cons)) < *len)
93                *len = XENSTORE_RING_SIZE - (prod - cons);
94        return buf + MASK_XENSTORE_IDX(prod);
95}
96
97static const void *get_input_chunk(XENSTORE_RING_IDX cons,
98                                   XENSTORE_RING_IDX prod,
99                                   const char *buf, uint32_t *len)
100{
101        *len = XENSTORE_RING_SIZE - MASK_XENSTORE_IDX(cons);
102        if ((prod - cons) < *len)
103                *len = prod - cons;
104        return buf + MASK_XENSTORE_IDX(cons);
105}
106
107static int writechn(struct connection *conn, const void *data, unsigned int len)
108{
109        uint32_t avail;
110        void *dest;
111        struct xenstore_domain_interface *intf = conn->domain->interface;
112        XENSTORE_RING_IDX cons, prod;
113
114        /* Must read indexes once, and before anything else, and verified. */
115        cons = intf->rsp_cons;
116        prod = intf->rsp_prod;
117        mb();
118        if (!check_indexes(cons, prod)) {
119                errno = EIO;
120                return -1;
121        }
122
123        dest = get_output_chunk(cons, prod, intf->rsp, &avail);
124        if (avail < len)
125                len = avail;
126
127        memcpy(dest, data, len);
128        mb();
129        intf->rsp_prod += len;
130
131        xc_evtchn_notify(xce_handle, conn->domain->port);
132
133        return len;
134}
135
136static int readchn(struct connection *conn, void *data, unsigned int len)
137{
138        uint32_t avail;
139        const void *src;
140        struct xenstore_domain_interface *intf = conn->domain->interface;
141        XENSTORE_RING_IDX cons, prod;
142
143        /* Must read indexes once, and before anything else, and verified. */
144        cons = intf->req_cons;
145        prod = intf->req_prod;
146        mb();
147
148        if (!check_indexes(cons, prod)) {
149                errno = EIO;
150                return -1;
151        }
152
153        src = get_input_chunk(cons, prod, intf->req, &avail);
154        if (avail < len)
155                len = avail;
156
157        memcpy(data, src, len);
158        mb();
159        intf->req_cons += len;
160
161        xc_evtchn_notify(xce_handle, conn->domain->port);
162
163        return len;
164}
165
166static int destroy_domain(void *_domain)
167{
168        struct domain *domain = _domain;
169
170        list_del(&domain->list);
171
172        if (domain->port) {
173                if (xc_evtchn_unbind(xce_handle, domain->port) == -1)
174                        eprintf("> Unbinding port %i failed!\n", domain->port);
175        }
176
177        if (domain->interface)
178                munmap(domain->interface, getpagesize());
179
180        return 0;
181}
182
183static void domain_cleanup(void)
184{
185        xc_dominfo_t dominfo;
186        struct domain *domain, *tmp;
187        int notify = 0;
188
189        list_for_each_entry_safe(domain, tmp, &domains, list) {
190                if (xc_domain_getinfo(*xc_handle, domain->domid, 1,
191                                      &dominfo) == 1 &&
192                    dominfo.domid == domain->domid) {
193                        if ((dominfo.crashed || dominfo.shutdown)
194                            && !domain->shutdown) {
195                                domain->shutdown = 1;
196                                notify = 1;
197                        }
198                        if (!dominfo.dying)
199                                continue;
200                }
201                talloc_free(domain->conn);
202                notify = 1;
203        }
204
205        if (notify)
206                fire_watches(NULL, "@releaseDomain", false);
207}
208
209/* We scan all domains rather than use the information given here. */
210void handle_event(void)
211{
212        evtchn_port_t port;
213
214        if ((port = xc_evtchn_pending(xce_handle)) == -1)
215                barf_perror("Failed to read from event fd");
216
217        if (port == virq_port)
218                domain_cleanup();
219
220#ifndef TESTING
221        if (xc_evtchn_unmask(xce_handle, port) == -1)
222                barf_perror("Failed to write to event fd");
223#endif
224}
225
226bool domain_can_read(struct connection *conn)
227{
228        struct xenstore_domain_interface *intf = conn->domain->interface;
229        return (intf->req_cons != intf->req_prod);
230}
231
232bool domain_is_unprivileged(struct connection *conn)
233{
234        return (conn && conn->domain && conn->domain->domid != 0);
235}
236
237bool domain_can_write(struct connection *conn)
238{
239        struct xenstore_domain_interface *intf = conn->domain->interface;
240        return ((intf->rsp_prod - intf->rsp_cons) != XENSTORE_RING_SIZE);
241}
242
243static char *talloc_domain_path(void *context, unsigned int domid)
244{
245        return talloc_asprintf(context, "/local/domain/%u", domid);
246}
247
248static struct domain *new_domain(void *context, unsigned int domid,
249                                 int port)
250{
251        struct domain *domain;
252        int rc;
253
254
255        domain = talloc(context, struct domain);
256        domain->port = 0;
257        domain->shutdown = 0;
258        domain->domid = domid;
259        domain->path = talloc_domain_path(domain, domid);
260
261        list_add(&domain->list, &domains);
262        talloc_set_destructor(domain, destroy_domain);
263
264        /* Tell kernel we're interested in this event. */
265        rc = xc_evtchn_bind_interdomain(xce_handle, domid, port);
266        if (rc == -1)
267            return NULL;
268        domain->port = rc;
269
270        domain->conn = new_connection(writechn, readchn);
271        domain->conn->domain = domain;
272        domain->conn->id = domid;
273
274        domain->remote_port = port;
275        domain->nbentry = 0;
276        domain->nbwatch = 0;
277
278        return domain;
279}
280
281
282static struct domain *find_domain_by_domid(unsigned int domid)
283{
284        struct domain *i;
285
286        list_for_each_entry(i, &domains, list) {
287                if (i->domid == domid)
288                        return i;
289        }
290        return NULL;
291}
292
293static void domain_conn_reset(struct domain *domain)
294{
295        struct connection *conn = domain->conn;
296        struct buffered_data *out;
297
298        conn_delete_all_watches(conn);
299        conn_delete_all_transactions(conn);
300
301        while ((out = list_top(&conn->out_list, struct buffered_data, list))) {
302                list_del(&out->list);
303                talloc_free(out);
304        }
305
306        talloc_free(conn->in->buffer);
307        memset(conn->in, 0, sizeof(*conn->in));
308        conn->in->inhdr = true;
309
310        domain->interface->req_cons = domain->interface->req_prod = 0;
311        domain->interface->rsp_cons = domain->interface->rsp_prod = 0;
312}
313
314/* domid, mfn, evtchn, path */
315void do_introduce(struct connection *conn, struct buffered_data *in)
316{
317        struct domain *domain;
318        char *vec[3];
319        unsigned int domid;
320        unsigned long mfn;
321        evtchn_port_t port;
322        int rc;
323        struct xenstore_domain_interface *interface;
324
325        if (get_strings(in, vec, ARRAY_SIZE(vec)) < ARRAY_SIZE(vec)) {
326                send_error(conn, EINVAL);
327                return;
328        }
329
330        if (conn->id != 0 || !conn->can_write) {
331                send_error(conn, EACCES);
332                return;
333        }
334
335        domid = atoi(vec[0]);
336        mfn = atol(vec[1]);
337        port = atoi(vec[2]);
338
339        /* Sanity check args. */
340        if (port <= 0) { 
341                send_error(conn, EINVAL);
342                return;
343        }
344
345        domain = find_domain_by_domid(domid);
346
347        if (domain == NULL) {
348                interface = xc_map_foreign_range(
349                        *xc_handle, domid,
350                        getpagesize(), PROT_READ|PROT_WRITE, mfn);
351                if (!interface) {
352                        send_error(conn, errno);
353                        return;
354                }
355                /* Hang domain off "in" until we're finished. */
356                domain = new_domain(in, domid, port);
357                if (!domain) {
358                        munmap(interface, getpagesize());
359                        send_error(conn, errno);
360                        return;
361                }
362                domain->interface = interface;
363                domain->mfn = mfn;
364
365                /* Now domain belongs to its connection. */
366                talloc_steal(domain->conn, domain);
367
368                fire_watches(conn, "@introduceDomain", false);
369        } else if ((domain->mfn == mfn) && (domain->conn != conn)) {
370                /* Use XS_INTRODUCE for recreating the xenbus event-channel. */
371                if (domain->port)
372                        xc_evtchn_unbind(xce_handle, domain->port);
373                rc = xc_evtchn_bind_interdomain(xce_handle, domid, port);
374                domain->port = (rc == -1) ? 0 : rc;
375                domain->remote_port = port;
376        } else {
377                send_error(conn, EINVAL);
378                return;
379        }
380
381        domain_conn_reset(domain);
382
383        send_ack(conn, XS_INTRODUCE);
384}
385
386/* domid */
387void do_release(struct connection *conn, const char *domid_str)
388{
389        struct domain *domain;
390        unsigned int domid;
391
392        if (!domid_str) {
393                send_error(conn, EINVAL);
394                return;
395        }
396
397        domid = atoi(domid_str);
398        if (!domid) {
399                send_error(conn, EINVAL);
400                return;
401        }
402
403        if (conn->id != 0) {
404                send_error(conn, EACCES);
405                return;
406        }
407
408        domain = find_domain_by_domid(domid);
409        if (!domain) {
410                send_error(conn, ENOENT);
411                return;
412        }
413
414        if (!domain->conn) {
415                send_error(conn, EINVAL);
416                return;
417        }
418
419        talloc_free(domain->conn);
420
421        fire_watches(conn, "@releaseDomain", false);
422
423        send_ack(conn, XS_RELEASE);
424}
425
426void do_resume(struct connection *conn, const char *domid_str)
427{
428        struct domain *domain;
429        unsigned int domid;
430
431        if (!domid_str) {
432                send_error(conn, EINVAL);
433                return;
434        }
435
436        domid = atoi(domid_str);
437        if (!domid) {
438                send_error(conn, EINVAL);
439                return;
440        }
441
442        if (conn->id != 0) {
443                send_error(conn, EACCES);
444                return;
445        }
446
447        domain = find_domain_by_domid(domid);
448        if (!domain) {
449                send_error(conn, ENOENT);
450                return;
451        }
452
453        if (!domain->conn) {
454                send_error(conn, EINVAL);
455                return;
456        }
457
458        domain->shutdown = 0;
459       
460        send_ack(conn, XS_RESUME);
461}
462
463void do_get_domain_path(struct connection *conn, const char *domid_str)
464{
465        char *path;
466
467        if (!domid_str) {
468                send_error(conn, EINVAL);
469                return;
470        }
471
472        path = talloc_domain_path(conn, atoi(domid_str));
473
474        send_reply(conn, XS_GET_DOMAIN_PATH, path, strlen(path) + 1);
475
476        talloc_free(path);
477}
478
479void do_is_domain_introduced(struct connection *conn, const char *domid_str)
480{
481        int result;
482        unsigned int domid;
483
484        if (!domid_str) {
485                send_error(conn, EINVAL);
486                return;
487        }
488
489        domid = atoi(domid_str);
490        if (domid == DOMID_SELF)
491                result = 1;
492        else
493                result = (find_domain_by_domid(domid) != NULL);
494
495        send_reply(conn, XS_IS_DOMAIN_INTRODUCED, result ? "T" : "F", 2);
496}
497
498static int close_xc_handle(void *_handle)
499{
500        xc_interface_close(*(int *)_handle);
501        return 0;
502}
503
504/* Returns the implicit path of a connection (only domains have this) */
505const char *get_implicit_path(const struct connection *conn)
506{
507        if (!conn->domain)
508                return NULL;
509        return conn->domain->path;
510}
511
512/* Restore existing connections. */
513void restore_existing_connections(void)
514{
515}
516
517static int dom0_init(void) 
518{ 
519        evtchn_port_t port;
520        struct domain *dom0;
521
522        port = xenbus_evtchn();
523        if (port == -1)
524                return -1;
525
526        dom0 = new_domain(NULL, 0, port); 
527        if (dom0 == NULL)
528                return -1;
529
530        dom0->interface = xenbus_map();
531        if (dom0->interface == NULL)
532                return -1;
533
534        talloc_steal(dom0->conn, dom0); 
535
536        xc_evtchn_notify(xce_handle, dom0->port); 
537
538        return 0; 
539}
540
541/* Returns the event channel handle. */
542int domain_init(void)
543{
544        int rc;
545
546        xc_handle = talloc(talloc_autofree_context(), int);
547        if (!xc_handle)
548                barf_perror("Failed to allocate domain handle");
549
550        *xc_handle = xc_interface_open();
551        if (*xc_handle < 0)
552                barf_perror("Failed to open connection to hypervisor");
553
554        talloc_set_destructor(xc_handle, close_xc_handle);
555
556        xce_handle = xc_evtchn_open();
557
558        if (xce_handle < 0)
559                barf_perror("Failed to open evtchn device");
560
561        if (dom0_init() != 0) 
562                barf_perror("Failed to initialize dom0 state"); 
563
564        if ((rc = xc_evtchn_bind_virq(xce_handle, VIRQ_DOM_EXC)) == -1)
565                barf_perror("Failed to bind to domain exception virq port");
566        virq_port = rc;
567
568        return xce_handle;
569}
570
571void domain_entry_inc(struct connection *conn, struct node *node)
572{
573        struct domain *d;
574
575        if (!conn)
576                return;
577
578        if (node->perms && node->perms[0].id != conn->id) {
579                d = find_domain_by_domid(node->perms[0].id);
580                if (d)
581                        d->nbentry++;
582        }
583        else if (conn->domain) {
584                conn->domain->nbentry++;
585        }
586}
587
588void domain_entry_dec(struct connection *conn, struct node *node)
589{
590        struct domain *d;
591
592        if (!conn)
593                return;
594
595        if (node->perms && node->perms[0].id != conn->id) {
596                d = find_domain_by_domid(node->perms[0].id);
597                if (d && d->nbentry)
598                        d->nbentry--;
599        } else if (conn->domain && conn->domain->nbentry)
600                conn->domain->nbentry--;
601}
602
603int domain_entry(struct connection *conn)
604{
605        return (domain_is_unprivileged(conn))
606                ? conn->domain->nbentry
607                : 0;
608}
609
610void domain_watch_inc(struct connection *conn)
611{
612        if (!conn || !conn->domain)
613                return;
614        conn->domain->nbwatch++;
615}
616
617void domain_watch_dec(struct connection *conn)
618{
619        if (!conn || !conn->domain)
620                return;
621        if (conn->domain->nbwatch)
622                conn->domain->nbwatch--;
623}
624
625int domain_watch(struct connection *conn)
626{
627        return (domain_is_unprivileged(conn))
628                ? conn->domain->nbwatch
629                : 0;
630}
631
632/*
633 * Local variables:
634 *  c-file-style: "linux"
635 *  indent-tabs-mode: t
636 *  c-indent-level: 8
637 *  c-basic-offset: 8
638 *  tab-width: 8
639 * End:
640 */
Note: See TracBrowser for help on using the repository browser.