2 * Xen block storage device frontend
4 * The present implementation follows the principle of
5 * "what's the simplest thing that could possibly work?".
6 * We can think about performance later.
7 * We can think about dynamically attaching and removing devices later.
11 #include "../port/lib.h"
17 #include "../port/error.h"
19 #include "../port/sd.h"
24 * conversions to machine page numbers, pages and addresses
26 #define MFN(pa) (patomfn[(pa)>>PGSHIFT])
27 #define MFNPG(pa) (MFN(pa)<<PGSHIFT)
28 #define PA2MA(pa) (MFNPG(pa) | PGOFF(pa))
29 #define VA2MA(va) PA2MA(PADDR(va))
30 #define VA2MFN(va) MFN(PADDR(va))
40 extern SDifc sdxenifc;
42 typedef struct Ctlr Ctlr;
51 blkif_front_ring_t ring;
61 ringinit(Ctlr *ctlr, char *a)
65 sr = (blkif_sring_t*)a;
68 FRONT_RING_INIT(&ctlr->ring, sr, BY2PG);
69 ctlr->ringref = shareframe(ctlr->backend, sr, 1);
74 vbdsend(Ctlr *ctlr, int write, int ref, int nb, uvlong bno)
79 ilock(&ctlr->ringlock); // XXX conservative
80 i = ctlr->ring.req_prod_pvt;
81 req = RING_GET_REQUEST(&ctlr->ring, i); // XXX overflow?
83 req->operation = write ? BLKIF_OP_WRITE : BLKIF_OP_READ;
85 req->handle = ctlr->devid;
87 req->sector_number = bno;
88 req->seg[0].gref = ref;
89 req->seg[0].first_sect = 0;
90 req->seg[0].last_sect = nb-1;
92 ctlr->ring.req_prod_pvt = i+1;
93 RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&ctlr->ring, notify);
94 iunlock(&ctlr->ringlock);
99 backendconnect(Ctlr *ctlr)
104 sprint(dir, "device/vbd/%d/", ctlr->devid);
105 xenstore_setd(dir, "ring-ref", ctlr->ringref);
106 xenstore_setd(dir, "event-channel", ctlr->evtchn);
107 xenstore_setd(dir, "state", XenbusStateInitialised);
108 xenstore_gets(dir, "backend", buf, sizeof buf);
109 sprint(dir, "%s/", buf);
111 xenstore_gets(dir, "state", buf, sizeof buf);
112 while (strtol(buf, 0, 0) != XenbusStateConnected) {
113 print("sdxen: waiting for vbd %d to connect\n", ctlr->devid);
114 tsleep(&up->sleep, return0, 0, 50);
115 xenstore_gets(dir, "state", buf, sizeof buf);
117 xenstore_gets(dir, "sector-size", buf, sizeof buf);
118 ctlr->secsize = strtol(buf, 0, 0);
119 xenstore_gets(dir, "sectors", buf, sizeof buf);
120 ctlr->sectors = strtol(buf, 0, 0);
121 print("sdxen: backend %s secsize %ld sectors %ld\n", dir, ctlr->secsize, ctlr->sectors);
122 if (ctlr->secsize > BY2PG)
123 panic("sdxen: sector size bigger than mmu page size");
127 backendactivate(Ctlr *ctlr)
131 sprint(dir, "device/vbd/%d/", ctlr->devid);
132 xenstore_setd(dir, "state", XenbusStateConnected);
139 static char idno[Ndevs] = { '0', 'C', 'D', 'E' };
140 static char nunit[Ndevs] = { 8, 2, 2, 8 };
143 for (i = 0; i < Ndevs; i++) {
144 sdev[i] = mallocz(sizeof(SDev), 1);
145 sdev[i]->ifc = &sdxenifc;
146 sdev[i]->idno = idno[i];
147 sdev[i]->nunit = nunit[i];
148 sdev[i]->ctlr = (Ctlr**)mallocz(sdev[i]->nunit*sizeof(Ctlr*), 1);
150 sdev[i]->next = sdev[i-1];
152 return sdev[Ndevs-1];
156 linuxdev(int idno, int subno)
160 return MajorDevSD + 16*subno;
162 return MajorDevHDA + 64*subno;
164 return MajorDevHDC + 64*subno;
166 return MajorDevXVD + 16*subno;
173 xenverify(SDunit *unit)
182 if (unit->subno > unit->dev->nunit)
184 devid = linuxdev(unit->dev->idno, unit->subno);
185 sprint(dir, "device/vbd/%d/", devid);
186 if (xenstore_gets(dir, "backend-id", buf, sizeof buf) <= 0)
189 ctlr = mallocz(sizeof(Ctlr), 1);
190 ((Ctlr**)unit->dev->ctlr)[unit->subno] = ctlr;
192 ctlr->backend = strtol(buf, 0, 0);
195 p = xspanalloc(npage<<PGSHIFT, BY2PG, 0);
196 p += ringinit(ctlr, p);
198 ctlr->evtchn = xenchanalloc(ctlr->backend);
199 backendconnect(ctlr);
201 unit->inquiry[0] = 0; // XXX how do we know if it's a CD?
202 unit->inquiry[2] = 2;
203 unit->inquiry[3] = 2;
204 unit->inquiry[4] = sizeof(unit->inquiry)-4;
205 strcpy((char*)&unit->inquiry[8], "Xen block device");
213 return ((Ctlr*)a)->iodone != 0;
217 sdxenintr(Ureg *, void *a)
220 blkif_response_t *rsp;
223 ilock(&ctlr->ringlock); // XXX conservative
225 RING_FINAL_CHECK_FOR_RESPONSES(&ctlr->ring, avail);
228 i = ctlr->ring.rsp_cons;
229 rsp = RING_GET_RESPONSE(&ctlr->ring, i);
230 LOG(dprint("sdxen rsp %llud %d %d\n", rsp->id, rsp->operation, rsp->status);)
231 if (rsp->status == BLKIF_RSP_OKAY)
235 ctlr->ring.rsp_cons = ++i;
237 iunlock(&ctlr->ringlock);
239 wakeup(&ctlr->wiodone);
242 static Ctlr *kickctlr;
247 Ctlr *ctlr = kickctlr;
251 s = HYPERVISOR_shared_info;
252 dprint("tick %d %d prod %d cons %d pending %x mask %x\n",
253 m->ticks, ctlr->iodone, ctlr->ring.sring->rsp_prod, ctlr->ring.rsp_cons,
254 s->evtchn_pending[0], s->evtchn_mask[0]);
260 xenonline(SDunit *unit)
264 ctlr = ((Ctlr**)unit->dev->ctlr)[unit->subno];
265 unit->sectors = ctlr->sectors;
266 unit->secsize = ctlr->secsize;
267 if (ctlr->online == 0) {
268 intrenable(ctlr->evtchn, sdxenintr, ctlr, BUSUNKNOWN, "vbd");
270 //addclock0link(kickme, 10000);
271 backendactivate(ctlr);
285 xenbio(SDunit* unit, int lun, int write, void* data, long nb, uvlong bno)
293 USED(lun); // XXX meaningless
294 ctlr = ((Ctlr**)unit->dev->ctlr)[unit->subno];
295 LOG(("xenbio %c %lux %ld %lld\n", write? 'w' : 'r', (ulong)data, nb, bno);)
297 // XXX extra copying & fragmentation could be avoided by
298 // redefining sdmalloc() to get page-aligned buffers
299 if ((ulong)data&(BY2PG-1))
301 bcount = BY2PG/unit->secsize;
302 qlock(&ctlr->iolock);
303 for (n = nb; n > 0; n -= bcount) {
304 ref = shareframe(ctlr->backend, buf, !write);
307 len = bcount*unit->secsize;
308 if (write && buf == ctlr->frame)
309 memmove(buf, data, len);
311 if (vbdsend(ctlr, write, ref, bcount, bno))
312 xenchannotify(ctlr->evtchn);
313 LOG(dprint("sleeping %d prod %d cons %d pending %x mask %x \n", ctlr->iodone, ctlr->ring.sring->rsp_prod, ctlr->ring.rsp_cons,
314 HYPERVISOR_shared_info->evtchn_pending[0], HYPERVISOR_shared_info->evtchn_mask[0]);)
315 sleep(&ctlr->wiodone, wiodone, ctlr);
317 if (ctlr->iodone < 0) {
318 qunlock(&ctlr->iolock);
321 if (buf == ctlr->frame) {
323 memmove(data, buf, len);
324 data = (char*)data + len;
329 qunlock(&ctlr->iolock);
330 return (nb-n)*unit->secsize;
346 xenverify, /* verify */
347 xenonline, /* online */
354 xenclear, /* clear */