summaryrefslogtreecommitdiff
path: root/sys/src/9/xen/devxenstore.c
diff options
context:
space:
mode:
authormischief <mischief@offblast.org>2014-06-24 18:02:25 -0700
committermischief <mischief@offblast.org>2014-06-24 18:02:25 -0700
commit5ba95fdb07ddc2c32111a1b2f57f17aa27fcbbf5 (patch)
treec1ec54cb9ecff85b0b820a26d26a10a32a118d0c /sys/src/9/xen/devxenstore.c
parentfa03455b5057675b18d1c87aef2d1071b2088de0 (diff)
import xen 32 bit paravirtual kernel from /n/sources/xen.
Diffstat (limited to 'sys/src/9/xen/devxenstore.c')
-rw-r--r--sys/src/9/xen/devxenstore.c590
1 files changed, 590 insertions, 0 deletions
diff --git a/sys/src/9/xen/devxenstore.c b/sys/src/9/xen/devxenstore.c
new file mode 100644
index 000000000..2004aecc1
--- /dev/null
+++ b/sys/src/9/xen/devxenstore.c
@@ -0,0 +1,590 @@
+/*
+ * Driver for xenstore - database shared between domains, used by xenbus to
+ * communicate configuration info.
+ */
+
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+#include "../pc/io.h"
+
+#define LOG(a)
+
+typedef struct Aux Aux;
+
+enum {
+ Qtopdir,
+ Qctl,
+ Qwatch,
+ WRITING = 0,
+ READING,
+ WATCHING,
+ MAXIO = 8*1024,
+};
+
+Dirtab xsdir[] = {
+ ".", {Qtopdir, 0, QTDIR}, 0, 0555,
+ "xenstore", {Qctl, 0}, 0, 0660,
+ "xenwatch", {Qwatch, 0}, 0, 0440,
+};
+
+struct {
+ struct xenstore_domain_interface *intf;
+ struct xsd_sockmsg hdr;
+ int hdrvalid;
+ int evtchn;
+ int nextreqid;
+ Aux *rhead;
+ Aux *kernelaux;
+ Queue *evq;
+ Rendez wr;
+ Rendez rr;
+ QLock;
+ Lock rlock;
+} xenstore;
+
+struct Aux {
+ QLock;
+ Rendez qr;
+ Queue *ioq;
+ Aux *next;
+ int state;
+ int reqid;
+};
+
+static char Ephase[] = "phase error";
+static char Eproto[] = "protocol error";
+static char NodeShutdown[] = "control/shutdown";
+
+static void xenbusproc(void*);
+
+static int
+notfull(void*)
+{
+ struct xenstore_domain_interface *xs = xenstore.intf;
+
+ return (xs->req_prod-xs->req_cons) < XENSTORE_RING_SIZE;
+}
+
+static int
+notempty(void*)
+{
+ struct xenstore_domain_interface *xs = xenstore.intf;
+
+ return xs->rsp_prod > xs->rsp_cons;
+}
+
+static int
+ishead(void* a)
+{
+ return xenstore.rhead == a;
+}
+
+static void
+xsintr(Ureg*, void*)
+{
+ LOG(dprint("xsintr\n");)
+ wakeup(&xenstore.rr);
+ wakeup(&xenstore.wr);
+}
+
+static void
+xwrite(Queue *q, char *buf, int len)
+{
+ struct xenstore_domain_interface *xs;
+ int m, n;
+ XENSTORE_RING_IDX idx;
+
+ xs = xenstore.intf;
+ while (len > 0) {
+ n = XENSTORE_RING_SIZE - (xs->req_prod - xs->req_cons);
+ if (n == 0) {
+ xenchannotify(xenstore.evtchn);
+ sleep(&xenstore.wr, notfull, 0);
+ continue;
+ }
+ if (n > len)
+ n = len;
+ idx = MASK_XENSTORE_IDX(xs->req_prod);
+ m = XENSTORE_RING_SIZE - idx;
+ if (m > n)
+ m = n;
+ if (q)
+ qread(q, xs->req+idx, m);
+ else
+ memmove(xs->req+idx, buf, m);
+ if (m < n) {
+ if (q)
+ qread(q, xs->req, n-m);
+ else
+ memmove(xs->req, buf+m, n-m);
+ }
+ coherence();
+ xs->req_prod += n;
+ xenchannotify(xenstore.evtchn);
+ if (buf)
+ buf += n;
+ len -= n;
+ }
+}
+
+static void
+xread(Queue *q, char *buf, int len)
+{
+ struct xenstore_domain_interface *xs = xenstore.intf;
+ int n, m;
+ XENSTORE_RING_IDX idx;
+
+ for (n = len; n > 0; n -= m) {
+ while (xs->rsp_prod == xs->rsp_cons) {
+ xenchannotify(xenstore.evtchn);
+ if (up == 0)
+ HYPERVISOR_yield();
+ else
+ sleep(&xenstore.rr, notempty, 0);
+ }
+ idx = MASK_XENSTORE_IDX(xs->rsp_cons);
+ m = xs->rsp_prod - xs->rsp_cons;
+ if (m > n)
+ m = n;
+ if (m > XENSTORE_RING_SIZE - idx)
+ m = XENSTORE_RING_SIZE - idx;
+ if (q)
+ qwrite(q, xs->rsp+idx, m);
+ else if (buf) {
+ memmove(buf, xs->rsp+idx, m);
+ buf += m;
+ }
+ coherence();
+ xs->rsp_cons += m;
+ }
+ xenchannotify(xenstore.evtchn);
+}
+
+static void
+xsrpc(Aux *aux)
+{
+ Queue *q;
+ Aux *l, *r, **lp;
+ struct xsd_sockmsg hdr;
+ long n;
+
+ q = aux->ioq;
+
+ if (aux->state == WATCHING)
+ aux->reqid = 0;
+ else {
+ /* get the request header and check validity */
+ if (qlen(q) < sizeof hdr)
+ error(Eproto);
+ qread(q, &hdr, sizeof hdr);
+ n = hdr.len;
+ if (qlen(q) != n)
+ error(Eproto);
+ qlock(&xenstore);
+ /* generate a unique request id */
+ aux->reqid = ++xenstore.nextreqid;
+ hdr.req_id = aux->reqid;
+ hdr.tx_id = 0;
+ /* send the request */
+ xwrite(0, (char*)&hdr, sizeof hdr);
+ xwrite(q, 0, n);
+ qunlock(&xenstore);
+ }
+
+ /* join list of requests awaiting response */
+ ilock(&xenstore.rlock);
+ if (xenstore.rhead == 0) {
+ aux->next = 0;
+ xenstore.rhead = aux;
+ } else {
+ aux->next = xenstore.rhead->next;
+ xenstore.rhead->next = aux;
+ }
+ iunlock(&xenstore.rlock);
+
+ /* loop until matching response header has been received */
+ if (waserror()) {
+ ilock(&xenstore.rlock);
+ for (lp = &xenstore.rhead; *lp && *lp != aux; lp = &(*lp)->next)
+ ;
+ if (*lp != 0) {
+ *lp = (*lp)->next;
+ if (lp == &xenstore.rhead && *lp)
+ wakeup(&(*lp)->qr);
+ }
+ iunlock(&xenstore.rlock);
+ nexterror();
+ }
+ for (;;) {
+ /* wait until this request reaches head of queue */
+ if (xenstore.rhead != aux)
+ sleep(&aux->qr, ishead, aux);
+ /* wait until a response header (maybe for another request) has been read */
+ if (!xenstore.hdrvalid) {
+ xread(0, (char*)&xenstore.hdr, sizeof xenstore.hdr);
+ xenstore.hdrvalid = 1;
+ }
+ if (xenstore.hdr.req_id == aux->reqid)
+ break;
+ /* response was for a different request: move matching request to head of queue */
+ ilock(&xenstore.rlock);
+ for (l = xenstore.rhead; r = l->next; l = r)
+ if (xenstore.hdr.req_id == r->reqid) {
+ l->next = r->next;
+ r->next = xenstore.rhead;
+ xenstore.rhead = r;
+ break;
+ }
+ iunlock(&xenstore.rlock);
+ if (r) {
+ /* wake the matching request */
+ wakeup(&r->qr);
+ } else {
+ /* response without a request: should be a watch event */
+ xenstore.hdrvalid = 0;
+ xread(0, 0, xenstore.hdr.len);
+ continue;
+ }
+ }
+
+ /* queue the response header, and data if any, for the caller to read */
+ qwrite(q, &xenstore.hdr, sizeof xenstore.hdr);
+ xenstore.hdrvalid = 0;
+ /* read the data, if any */
+ if (xenstore.hdr.len > 0)
+ xread(q, 0, xenstore.hdr.len);
+
+ /* remove finished request and wake the next request on the queue */
+ ilock(&xenstore.rlock);
+ xenstore.rhead = aux->next;
+ iunlock(&xenstore.rlock);
+ poperror();
+ if (xenstore.rhead != 0)
+ wakeup(&xenstore.rhead->qr);
+}
+
+static void
+xsreset()
+{
+ LOG(dprint("xsreset\n");)
+}
+
+static void
+xsinit()
+{
+ intrenable(xenstore.evtchn, xsintr, 0, BUSUNKNOWN, "Xen store");
+ kproc("xenbus", xenbusproc, 0);
+}
+
+static Chan*
+xsattach(char *spec)
+{
+ return devattach('x', spec);
+}
+
+static Walkqid*
+xswalk(Chan *c, Chan *nc, char **name, int nname)
+{
+ return devwalk(c, nc, name, nname, xsdir, nelem(xsdir), devgen);
+}
+
+static int
+xsstat(Chan *c, uchar *dp, int n)
+{
+ return devstat(c, dp, n, xsdir, nelem(xsdir), devgen);
+}
+
+static Aux*
+auxalloc(int initstate)
+{
+ Aux *aux;
+ Queue *q;
+
+ aux = mallocz(sizeof(Aux), 1);
+ if (aux == 0)
+ return 0;
+ q = qopen(MAXIO, 0, 0, 0);
+ if (q == 0) {
+ free(aux);
+ return 0;
+ }
+ qnoblock(q, 1);
+ aux->state = initstate;
+ aux->ioq = q;
+ return aux;
+}
+
+static Chan*
+xsopen(Chan *c, int omode)
+{
+ Aux *aux;
+ int state;
+
+ c = devopen(c, omode, xsdir, nelem(xsdir), devgen);
+ state = WRITING;
+ switch ((ulong)c->qid.path) {
+ case Qwatch:
+ state = WATCHING;
+ /* fall through */
+ case Qctl:
+ aux = auxalloc(state);
+ if (aux == 0) {
+ c->flag &= ~COPEN;
+ error(Enomem);
+ }
+ c->aux = aux;
+ break;
+ }
+ return c;
+}
+
+static void
+xsclose(Chan* c)
+{
+ Aux *aux;
+
+ if ((c->flag&COPEN) == 0)
+ return;
+
+ switch ((ulong)c->qid.path) {
+ case Qwatch:
+ case Qctl:
+ if ((aux = (Aux*)c->aux) != 0) {
+ qfree(aux->ioq);
+ free(aux);
+ c->aux = 0;
+ }
+ break;
+ }
+}
+
+static long
+xsread(Chan *c, void *a, long n, vlong off)
+{
+ Aux *aux;
+ Queue *q;
+ long nr;
+
+ USED(off);
+ if (c->qid.type == QTDIR)
+ return devdirread(c, a, n, xsdir, nelem(xsdir), devgen);
+
+ aux = (Aux*)c->aux;
+ qlock(aux);
+ if (waserror()) {
+ qunlock(aux);
+ nexterror();
+ }
+ q = aux->ioq;
+ switch (aux->state) {
+ case WRITING:
+ if (qlen(q) == 0)
+ error(Ephase);
+ xsrpc(aux);
+ aux->state = READING;
+ break;
+ case WATCHING:
+ if (qlen(q) == 0)
+ xsrpc(aux);
+ break;
+ }
+ if (!qcanread(q))
+ nr = 0;
+ else
+ nr = qread(q, a, n);
+ qunlock(aux);
+ poperror();
+ return nr;
+}
+
+static long
+xswrite(Chan *c, void *a, long n, vlong off)
+{
+ Aux *aux;
+ Queue *q;
+ long nr;
+
+ if (c->qid.type == QTDIR)
+ error(Eperm);
+ if ((ulong)c->qid.path == Qwatch)
+ error(Ebadusefd);
+
+ aux = (Aux*)c->aux;
+ qlock(aux);
+ if (waserror()) {
+ qunlock(aux);
+ nexterror();
+ }
+ q = aux->ioq;
+ if ((off == 0 || aux->state == READING) && qlen(q) > 0)
+ qflush(q);
+ aux->state = WRITING;
+ nr = qwrite(aux->ioq, a, n);
+ qunlock(aux);
+ poperror();
+ return nr;
+}
+
+Dev xenstoredevtab = {
+ 'x',
+ "xenstore",
+
+ xsreset,
+ xsinit,
+ devshutdown,
+ xsattach,
+ xswalk,
+ xsstat,
+ xsopen,
+ devcreate,
+ xsclose,
+ xsread,
+ devbread,
+ xswrite,
+ devbwrite,
+ devremove,
+ devwstat,
+};
+
+static char*
+xscmd(Aux *aux, char *buf, int cmd, char *s, char *val)
+{
+ struct xsd_sockmsg *msg;
+ char *arg;
+ long n;
+
+ msg = (struct xsd_sockmsg*)buf;
+ arg = buf + sizeof(*msg);
+ msg->type = cmd;
+ msg->len = strlen(s)+1;
+ if (val) {
+ msg->len += strlen(val);
+ if (cmd == XS_WATCH)
+ msg->len++; /* stupid special case */
+ }
+ strcpy(arg, s);
+ if (val)
+ strcpy(arg+strlen(s)+1, val);
+ n = sizeof(*msg)+msg->len;
+ if (up == 0) {
+ msg->req_id = 1;
+ msg->tx_id = 0;
+ xwrite(0, buf, n);
+ xread(0, buf, sizeof(*msg));
+ xread(0, arg, msg->len);
+ } else {
+ qlock(aux);
+ if (qlen(aux->ioq) > 0)
+ qflush(aux->ioq);
+ qwrite(aux->ioq, buf, n);
+ xsrpc(aux);
+ qread(aux->ioq, buf, sizeof(*msg));
+ LOG(dprint("xs: type %d req_id %d len %d\n", msg->type, msg->req_id, msg->len);)
+ // XXX buffer overflow
+ qread(aux->ioq, arg, msg->len);
+ qunlock(aux);
+ }
+ arg[msg->len] = 0;
+ if (msg->type == XS_ERROR) {
+ return 0;
+ }
+ return arg;
+}
+
+static void
+intfinit(void)
+{
+ if (xenstore.intf == 0) {
+ xenstore.intf = (struct xenstore_domain_interface*)mmumapframe(XENBUS, xenstart->store_mfn);
+ xenstore.evtchn = xenstart->store_evtchn;
+ xenstore.kernelaux = auxalloc(WRITING);
+ }
+}
+
+void
+xenstore_write(char *s, char *val)
+{
+ char buf[512];
+
+ intfinit();
+ xscmd(xenstore.kernelaux, buf, XS_WRITE, s, val);
+}
+
+int
+xenstore_read(char *s, char *val, int len)
+{
+ char buf[512];
+ char *p;
+
+ intfinit();
+ p = xscmd(xenstore.kernelaux, buf, XS_READ, s, nil);
+ if (p == 0)
+ return -1;
+ strecpy(val, val+len, p);
+ return 1;
+}
+
+void
+xenstore_setd(char *dir, char *node, int value)
+{
+ int off;
+ char buf[12];
+
+ off = strlen(dir);
+ sprint(dir+off, "%s", node);
+ sprint(buf, "%ud", value);
+ xenstore_write(dir, buf);
+ dir[off] = 0;
+}
+
+int
+xenstore_gets(char *dir, char *node, char *buf, int buflen)
+{
+ int off;
+ int n;
+
+ off = strlen(dir);
+ sprint(dir+off, "%s", node);
+ n = xenstore_read(dir, buf, buflen);
+ dir[off] = 0;
+ return n;
+}
+
+static void
+xenbusproc(void*)
+{
+ Chan *c;
+ Aux *aux;
+ char *p;
+ struct xsd_sockmsg msg;
+ char buf[512];
+ int n, m;
+
+ c = namec("#x/xenstore", Aopen, ORDWR, 0);
+ aux = (Aux*)c->aux;
+ c = namec("#x/xenwatch", Aopen, OREAD, 0);
+ xscmd(aux, buf, XS_WATCH, NodeShutdown, "$");
+ for (;;) {
+ xsread(c, &msg, sizeof(msg), 0);
+ for (n = msg.len; n > 0; n -= m)
+ m = xsread(c, buf, msg.len, sizeof(msg));
+ buf[msg.len] = 0;
+ if (strcmp(buf, NodeShutdown) != 0)
+ continue;
+ p = xscmd(aux, buf, XS_READ, NodeShutdown, nil);
+ if (p == nil)
+ continue;
+ if (strcmp(p, "poweroff") == 0)
+ reboot(nil, nil, 0);
+ else if (strcmp(p, "reboot") == 0)
+ exit(0);
+ else {
+ print("xenbus: %s=%s\n", NodeShutdown, p);
+ xscmd(aux, buf, XS_WRITE, NodeShutdown, "");
+ }
+ }
+}