summaryrefslogtreecommitdiff
path: root/sys/src/9/xen/sdxen.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/sdxen.c
parentfa03455b5057675b18d1c87aef2d1071b2088de0 (diff)
import xen 32 bit paravirtual kernel from /n/sources/xen.
Diffstat (limited to 'sys/src/9/xen/sdxen.c')
-rw-r--r--sys/src/9/xen/sdxen.c356
1 files changed, 356 insertions, 0 deletions
diff --git a/sys/src/9/xen/sdxen.c b/sys/src/9/xen/sdxen.c
new file mode 100644
index 000000000..1973b5f8d
--- /dev/null
+++ b/sys/src/9/xen/sdxen.c
@@ -0,0 +1,356 @@
+/*
+ * Xen block storage device frontend
+ *
+ * The present implementation follows the principle of
+ * "what's the simplest thing that could possibly work?".
+ * We can think about performance later.
+ * We can think about dynamically attaching and removing devices later.
+ */
+
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "ureg.h"
+#include "../port/error.h"
+
+#include "../port/sd.h"
+
+#define LOG(a)
+
+/*
+ * conversions to machine page numbers, pages and addresses
+ */
+#define MFN(pa) (patomfn[(pa)>>PGSHIFT])
+#define MFNPG(pa) (MFN(pa)<<PGSHIFT)
+#define PA2MA(pa) (MFNPG(pa) | PGOFF(pa))
+#define VA2MA(va) PA2MA(PADDR(va))
+#define VA2MFN(va) MFN(PADDR(va))
+
+enum {
+ Ndevs = 4,
+ MajorDevSD = 0x800,
+ MajorDevHDA = 0x300,
+ MajorDevHDC = 0x1600,
+ MajorDevXVD = 0xCA00,
+};
+
+extern SDifc sdxenifc;
+
+typedef struct Ctlr Ctlr;
+
+struct Ctlr {
+ int online;
+ ulong secsize;
+ ulong sectors;
+ int backend;
+ int devid;
+ int evtchn;
+ blkif_front_ring_t ring;
+ int ringref;
+ Lock ringlock;
+ char *frame;
+ QLock iolock;
+ int iodone;
+ Rendez wiodone;
+};
+
+static int
+ringinit(Ctlr *ctlr, char *a)
+{
+ blkif_sring_t *sr;
+
+ sr = (blkif_sring_t*)a;
+ memset(sr, 0, BY2PG);
+ SHARED_RING_INIT(sr);
+ FRONT_RING_INIT(&ctlr->ring, sr, BY2PG);
+ ctlr->ringref = shareframe(ctlr->backend, sr, 1);
+ return BY2PG;
+}
+
+static int
+vbdsend(Ctlr *ctlr, int write, int ref, int nb, uvlong bno)
+{
+ blkif_request_t *req;
+ int i, notify;
+
+ ilock(&ctlr->ringlock); // XXX conservative
+ i = ctlr->ring.req_prod_pvt;
+ req = RING_GET_REQUEST(&ctlr->ring, i); // XXX overflow?
+
+ req->operation = write ? BLKIF_OP_WRITE : BLKIF_OP_READ;
+ req->nr_segments = 1;
+ req->handle = ctlr->devid;
+ req->id = 1;
+ req->sector_number = bno;
+ req->seg[0].gref = ref;
+ req->seg[0].first_sect = 0;
+ req->seg[0].last_sect = nb-1;
+
+ ctlr->ring.req_prod_pvt = i+1;
+ RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&ctlr->ring, notify);
+ iunlock(&ctlr->ringlock);
+ return notify;
+}
+
+static void
+backendconnect(Ctlr *ctlr)
+{
+ char dir[64];
+ char buf[64];
+
+ sprint(dir, "device/vbd/%d/", ctlr->devid);
+ xenstore_setd(dir, "ring-ref", ctlr->ringref);
+ xenstore_setd(dir, "event-channel", ctlr->evtchn);
+ xenstore_setd(dir, "state", XenbusStateInitialised);
+ xenstore_gets(dir, "backend", buf, sizeof buf);
+ sprint(dir, "%s/", buf);
+ HYPERVISOR_yield();
+ xenstore_gets(dir, "state", buf, sizeof buf);
+ while (strtol(buf, 0, 0) != XenbusStateConnected) {
+ print("sdxen: waiting for vbd %d to connect\n", ctlr->devid);
+ tsleep(&up->sleep, return0, 0, 1000);
+ xenstore_gets(dir, "state", buf, sizeof buf);
+ }
+ xenstore_gets(dir, "sector-size", buf, sizeof buf);
+ ctlr->secsize = strtol(buf, 0, 0);
+ xenstore_gets(dir, "sectors", buf, sizeof buf);
+ ctlr->sectors = strtol(buf, 0, 0);
+ print("sdxen: backend %s secsize %ld sectors %ld\n", dir, ctlr->secsize, ctlr->sectors);
+ if (ctlr->secsize > BY2PG)
+ panic("sdxen: sector size bigger than mmu page size");
+}
+
+static void
+backendactivate(Ctlr *ctlr)
+{
+ char dir[64];
+
+ sprint(dir, "device/vbd/%d/", ctlr->devid);
+ xenstore_setd(dir, "state", XenbusStateConnected);
+}
+
+static SDev*
+xenpnp(void)
+{
+ SDev *sdev[Ndevs];
+ static char idno[Ndevs] = { '0', 'C', 'D', 'E' };
+ static char nunit[Ndevs] = { 8, 2, 2, 8 };
+ int i;
+
+ for (i = 0; i < Ndevs; i++) {
+ sdev[i] = mallocz(sizeof(SDev), 1);
+ sdev[i]->ifc = &sdxenifc;
+ sdev[i]->idno = idno[i];
+ sdev[i]->nunit = nunit[i];
+ sdev[i]->ctlr = (Ctlr**)mallocz(sdev[i]->nunit*sizeof(Ctlr*), 1);
+ if (i > 0)
+ sdev[i]->next = sdev[i-1];
+ }
+ return sdev[Ndevs-1];
+}
+
+static int
+linuxdev(int idno, int subno)
+{
+ switch (idno) {
+ case '0':
+ return MajorDevSD + 16*subno;
+ case 'C':
+ return MajorDevHDA + 64*subno;
+ case 'D':
+ return MajorDevHDC + 64*subno;
+ case 'E':
+ return MajorDevXVD + 16*subno;
+ default:
+ return 0;
+ }
+}
+
+static int
+xenverify(SDunit *unit)
+{
+ Ctlr *ctlr;
+ char dir[64];
+ char buf[64];
+ int devid;
+ int npage;
+ char *p;
+
+ if (unit->subno > unit->dev->nunit)
+ return 0;
+ devid = linuxdev(unit->dev->idno, unit->subno);
+ sprint(dir, "device/vbd/%d/", devid);
+ if (xenstore_gets(dir, "backend-id", buf, sizeof buf) <= 0)
+ return 0;
+
+ ctlr = mallocz(sizeof(Ctlr), 1);
+ ((Ctlr**)unit->dev->ctlr)[unit->subno] = ctlr;
+ ctlr->devid = devid;
+ ctlr->backend = strtol(buf, 0, 0);
+
+ npage = 2;
+ p = xspanalloc(npage<<PGSHIFT, BY2PG, 0);
+ p += ringinit(ctlr, p);
+ ctlr->frame = p;
+ ctlr->evtchn = xenchanalloc(ctlr->backend);
+ backendconnect(ctlr);
+
+ unit->inquiry[0] = 0; // XXX how do we know if it's a CD?
+ unit->inquiry[2] = 2;
+ unit->inquiry[3] = 2;
+ unit->inquiry[4] = sizeof(unit->inquiry)-4;
+ strcpy((char*)&unit->inquiry[8], "Xen block device");
+
+ return 1;
+}
+
+static int
+wiodone(void *a)
+{
+ return ((Ctlr*)a)->iodone != 0;
+}
+
+static void
+sdxenintr(Ureg *, void *a)
+{
+ Ctlr *ctlr = a;
+ blkif_response_t *rsp;
+ int i, avail;
+
+ ilock(&ctlr->ringlock); // XXX conservative
+ for (;;) {
+ RING_FINAL_CHECK_FOR_RESPONSES(&ctlr->ring, avail);
+ if (!avail)
+ break;
+ i = ctlr->ring.rsp_cons;
+ rsp = RING_GET_RESPONSE(&ctlr->ring, i);
+ LOG(dprint("sdxen rsp %llud %d %d\n", rsp->id, rsp->operation, rsp->status);)
+ if (rsp->status == BLKIF_RSP_OKAY)
+ ctlr->iodone = 1;
+ else
+ ctlr->iodone = -1;
+ ctlr->ring.rsp_cons = ++i;
+ }
+ iunlock(&ctlr->ringlock);
+ if (ctlr->iodone)
+ wakeup(&ctlr->wiodone);
+}
+
+static Ctlr *kickctlr;
+
+static void
+kickme(void)
+{
+ Ctlr *ctlr = kickctlr;
+ shared_info_t *s;
+
+ if (ctlr) {
+ s = HYPERVISOR_shared_info;
+ dprint("tick %d %d prod %d cons %d pending %x mask %x\n",
+ m->ticks, ctlr->iodone, ctlr->ring.sring->rsp_prod, ctlr->ring.rsp_cons,
+ s->evtchn_pending[0], s->evtchn_mask[0]);
+ sdxenintr(0, ctlr);
+ }
+}
+
+static int
+xenonline(SDunit *unit)
+{
+ Ctlr *ctlr;
+
+ ctlr = ((Ctlr**)unit->dev->ctlr)[unit->subno];
+ unit->sectors = ctlr->sectors;
+ unit->secsize = ctlr->secsize;
+ if (ctlr->online == 0) {
+ intrenable(ctlr->evtchn, sdxenintr, ctlr, BUSUNKNOWN, "vbd");
+ //kickctlr = ctlr;
+ //addclock0link(kickme, 10000);
+ backendactivate(ctlr);
+ ctlr->online = 1;
+ }
+
+ return 1;
+}
+
+static int
+xenrio(SDreq*)
+{
+ return -1;
+}
+
+static long
+xenbio(SDunit* unit, int lun, int write, void* data, long nb, uvlong bno)
+{
+ Ctlr *ctlr;
+ char *buf;
+ long bcount, len;
+ int ref;
+ int n;
+
+ USED(lun); // XXX meaningless
+ ctlr = ((Ctlr**)unit->dev->ctlr)[unit->subno];
+ LOG(("xenbio %c %lux %ld %lld\n", write? 'w' : 'r', (ulong)data, nb, bno);)
+ buf = data;
+ // XXX extra copying & fragmentation could be avoided by
+ // redefining sdmalloc() to get page-aligned buffers
+ if ((ulong)data&(BY2PG-1))
+ buf = ctlr->frame;
+ bcount = BY2PG/unit->secsize;
+ qlock(&ctlr->iolock);
+ for (n = nb; n > 0; n -= bcount) {
+ ref = shareframe(ctlr->backend, buf, !write);
+ if (bcount > n)
+ bcount = n;
+ len = bcount*unit->secsize;
+ if (write && buf == ctlr->frame)
+ memmove(buf, data, len);
+ ctlr->iodone = 0;
+ if (vbdsend(ctlr, write, ref, bcount, bno))
+ xenchannotify(ctlr->evtchn);
+ LOG(dprint("sleeping %d prod %d cons %d pending %x mask %x \n", ctlr->iodone, ctlr->ring.sring->rsp_prod, ctlr->ring.rsp_cons,
+ HYPERVISOR_shared_info->evtchn_pending[0], HYPERVISOR_shared_info->evtchn_mask[0]);)
+ sleep(&ctlr->wiodone, wiodone, ctlr);
+ xengrantend(ref);
+ if (ctlr->iodone < 0) {
+ qunlock(&ctlr->iolock);
+ return -1;
+ }
+ if (buf == ctlr->frame) {
+ if (!write)
+ memmove(data, buf, len);
+ data = (char*)data + len;
+ } else
+ buf += len;
+ bno += bcount;
+ }
+ qunlock(&ctlr->iolock);
+ return (nb-n)*unit->secsize;
+}
+
+static void
+xenclear(SDev *)
+{
+}
+
+SDifc sdxenifc = {
+ "xen", /* name */
+
+ xenpnp, /* pnp */
+ 0, /* legacy */
+ 0, /* enable */
+ 0, /* disable */
+
+ xenverify, /* verify */
+ xenonline, /* online */
+ xenrio, /* rio */
+ 0, /* rctl */
+ 0, /* wctl */
+
+ xenbio, /* bio */
+ 0, /* probe */
+ xenclear, /* clear */
+ 0, /* stat */
+};