]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/xen/sdxen.c
audiosb16: cleanup audioprobe(), cast ISAConf.port to ulong
[plan9front.git] / sys / src / 9 / xen / sdxen.c
1 /*
2  * Xen block storage device frontend
3  *
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.
8  */
9
10 #include "u.h"
11 #include "../port/lib.h"
12 #include "mem.h"
13 #include "dat.h"
14 #include "fns.h"
15 #include "io.h"
16 #include "ureg.h"
17 #include "../port/error.h"
18
19 #include "../port/sd.h"
20
21 #define LOG(a)
22
23 /*
24  * conversions to machine page numbers, pages and addresses
25  */
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))
31
32 enum {
33         Ndevs           = 4,
34         MajorDevSD      = 0x800,
35         MajorDevHDA     = 0x300,
36         MajorDevHDC     = 0x1600,
37         MajorDevXVD     = 0xCA00,
38 };
39
40 extern SDifc sdxenifc;
41
42 typedef struct Ctlr Ctlr;
43
44 struct Ctlr {
45         int     online;
46         ulong   secsize;
47         ulong   sectors;
48         int     backend;
49         int     devid;
50         int     evtchn;
51         blkif_front_ring_t ring;
52         int     ringref;
53         Lock    ringlock;
54         char    *frame;
55         QLock   iolock;
56         int     iodone;
57         Rendez  wiodone;
58 };
59
60 static int
61 ringinit(Ctlr *ctlr, char *a)
62 {
63         blkif_sring_t *sr;
64
65         sr = (blkif_sring_t*)a;
66         memset(sr, 0, BY2PG);
67         SHARED_RING_INIT(sr);
68         FRONT_RING_INIT(&ctlr->ring, sr, BY2PG);
69         ctlr->ringref = shareframe(ctlr->backend, sr, 1);
70         return BY2PG;
71 }
72
73 static int
74 vbdsend(Ctlr *ctlr, int write, int ref, int nb, uvlong bno)
75 {
76         blkif_request_t *req;
77         int i, notify;
78
79         ilock(&ctlr->ringlock);         // XXX conservative
80         i = ctlr->ring.req_prod_pvt;
81         req = RING_GET_REQUEST(&ctlr->ring, i); // XXX overflow?
82
83         req->operation = write ? BLKIF_OP_WRITE : BLKIF_OP_READ;
84         req->nr_segments = 1;
85         req->handle = ctlr->devid;
86         req->id = 1;
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;
91
92         ctlr->ring.req_prod_pvt = i+1;
93         RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&ctlr->ring, notify);
94         iunlock(&ctlr->ringlock);
95         return notify;
96 }
97
98 static void
99 backendconnect(Ctlr *ctlr)
100 {
101         char dir[64];
102         char buf[64];
103
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);
110         HYPERVISOR_yield();
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);
116         }
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");
124 }
125
126 static void
127 backendactivate(Ctlr *ctlr)
128 {
129         char dir[64];
130
131         sprint(dir, "device/vbd/%d/", ctlr->devid);
132         xenstore_setd(dir, "state", XenbusStateConnected);
133 }
134
135 static SDev*
136 xenpnp(void)
137 {
138         SDev *sdev[Ndevs];
139         static char idno[Ndevs] = { '0', 'C', 'D', 'E' };
140         static char nunit[Ndevs] = { 8, 2, 2, 8 };
141         int i;
142
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);
149                 if (i > 0)
150                         sdev[i]->next = sdev[i-1];
151         }
152         return sdev[Ndevs-1];
153 }
154
155 static int
156 linuxdev(int idno, int subno)
157 {
158         switch (idno) {
159         case '0':
160                 return MajorDevSD + 16*subno;
161         case 'C':
162                 return MajorDevHDA + 64*subno;
163         case 'D':
164                 return MajorDevHDC + 64*subno;
165         case 'E':
166                 return MajorDevXVD + 16*subno;
167         default:
168                 return 0;
169         }
170 }
171
172 static int
173 xenverify(SDunit *unit)
174 {
175         Ctlr *ctlr;
176         char dir[64];
177         char buf[64];
178         int devid;
179         int npage;
180         char *p;
181
182         if (unit->subno > unit->dev->nunit)
183                 return 0;
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)
187                 return 0;
188
189         ctlr = mallocz(sizeof(Ctlr), 1);
190         ((Ctlr**)unit->dev->ctlr)[unit->subno] = ctlr;
191         ctlr->devid = devid;
192         ctlr->backend = strtol(buf, 0, 0);
193
194         npage = 2;
195         p = xspanalloc(npage<<PGSHIFT, BY2PG, 0);
196         p += ringinit(ctlr, p);
197         ctlr->frame = p;
198         ctlr->evtchn = xenchanalloc(ctlr->backend);
199         backendconnect(ctlr);
200
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");
206
207         return 1;
208 }
209
210 static int
211 wiodone(void *a)
212 {
213         return ((Ctlr*)a)->iodone != 0;
214 }
215
216 static void
217 sdxenintr(Ureg *, void *a)
218 {
219         Ctlr *ctlr = a;
220         blkif_response_t *rsp;
221         int i, avail;
222
223         ilock(&ctlr->ringlock); // XXX conservative
224         for (;;) {
225                 RING_FINAL_CHECK_FOR_RESPONSES(&ctlr->ring, avail);
226                 if (!avail)
227                         break;
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)
232                         ctlr->iodone = 1;
233                 else
234                         ctlr->iodone = -1;
235                 ctlr->ring.rsp_cons = ++i;
236         }
237         iunlock(&ctlr->ringlock);
238         if (ctlr->iodone)
239                 wakeup(&ctlr->wiodone);
240 }
241
242 static Ctlr *kickctlr;
243
244 static void
245 kickme(void)
246 {
247         Ctlr *ctlr = kickctlr;
248         shared_info_t *s;
249
250         if (ctlr) {
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]);
255                 sdxenintr(0, ctlr);
256         }
257 }
258
259 static int
260 xenonline(SDunit *unit)
261 {
262         Ctlr *ctlr;
263
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");
269                 //kickctlr = ctlr;
270                 //addclock0link(kickme, 10000);
271                 backendactivate(ctlr);
272                 ctlr->online = 1;
273         }
274
275         return 1;
276 }
277
278 static int
279 xenrio(SDreq*)
280 {
281         return -1;
282 }
283
284 static long
285 xenbio(SDunit* unit, int lun, int write, void* data, long nb, uvlong bno)
286 {
287         Ctlr *ctlr;
288         char *buf;
289         long bcount, len;
290         int ref;
291         int n;
292
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);)
296         buf = data;
297         // XXX extra copying & fragmentation could be avoided by
298         // redefining sdmalloc() to get page-aligned buffers
299         if ((ulong)data&(BY2PG-1))
300                 buf = ctlr->frame;
301         bcount = BY2PG/unit->secsize;
302         qlock(&ctlr->iolock);
303         for (n = nb; n > 0; n -= bcount) {
304                 ref = shareframe(ctlr->backend, buf, !write);
305                 if (bcount > n)
306                         bcount = n;
307                 len = bcount*unit->secsize;
308                 if (write && buf == ctlr->frame)
309                         memmove(buf, data, len);
310                 ctlr->iodone = 0;
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);
316                 xengrantend(ref);
317                 if (ctlr->iodone < 0) {
318                         qunlock(&ctlr->iolock);
319                         return -1;
320                 }
321                 if (buf == ctlr->frame) {
322                         if (!write)
323                                 memmove(data, buf, len);
324                         data = (char*)data + len;
325                 } else
326                         buf += len;
327                 bno += bcount;
328         }
329         qunlock(&ctlr->iolock);
330         return (nb-n)*unit->secsize;
331 }
332
333 static void
334 xenclear(SDev *)
335 {
336 }
337
338 SDifc sdxenifc = {
339         "xen",                          /* name */
340
341         xenpnp,                         /* pnp */
342         0,                      /* legacy */
343         0,                      /* enable */
344         0,                      /* disable */
345
346         xenverify,                      /* verify */
347         xenonline,                      /* online */
348         xenrio,                         /* rio */
349         0,                      /* rctl */
350         0,                      /* wctl */
351
352         xenbio,                         /* bio */
353         0,                      /* probe */
354         xenclear,                       /* clear */
355         0,                      /* stat */
356 };