]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/xen/etherxen.c
devether: mux bridges, portable netconsole
[plan9front.git] / sys / src / 9 / xen / etherxen.c
1 /*
2  * Xen virtual network interface frontend
3  */
4
5 #include "u.h"
6 #include "../port/lib.h"
7 #include "mem.h"
8 #include "dat.h"
9 #include "fns.h"
10 #include "io.h"
11 #include "../port/error.h"
12 #include "../port/netif.h"
13 #include "../port/etherif.h"
14
15 #define LOG(a)
16
17 enum {
18         Nvif    = 4,
19         Ntb             = 16,
20         Nrb             = 32,
21 };
22
23 typedef struct Ctlr Ctlr;
24 typedef union Txframe Txframe;
25 typedef union Rxframe Rxframe;
26
27 struct Ctlr {
28         int     attached;
29         int     backend;
30         int     vifno;
31         int     evtchn;
32         int rxcopy;
33         Txframe *txframes;
34         Txframe *freetxframe;
35         Rxframe *rxframes;
36         netif_tx_front_ring_t txring;
37         netif_rx_front_ring_t rxring;
38         int     *txrefs;
39         int     *rxrefs;
40         int     txringref;
41         int     rxringref;
42         Lock    txlock;
43         QLock   attachlock;
44         Rendez  wtxframe;
45         Rendez  wtxblock;
46
47         ulong interrupts;
48         ulong transmits;
49         ulong receives;
50         ulong txerrors;
51         ulong rxerrors;
52         ulong rxoverflows;
53 };
54
55 union Txframe {
56         struct {
57                 Txframe *next;
58                 char data[2];
59         } tf;
60         uchar page[BY2PG];
61 };
62
63 union Rxframe {
64         uchar page[BY2PG];
65 };
66
67 static int nvif;
68
69 /*
70  * conversions to machine page numbers, pages and addresses
71  */
72 #define MFN(pa)         (patomfn[(pa)>>PGSHIFT])
73 #define MFNPG(pa)               (MFN(pa)<<PGSHIFT)
74 #define PA2MA(pa)               (MFNPG(pa) | PGOFF(pa))
75 #define VA2MA(va)               PA2MA(PADDR(va))
76
77 static int
78 puttxrequest(Ctlr *ctlr, netif_tx_request_t *tr)
79 {
80         netif_tx_request_t *req;
81         int i, notify;
82
83         LOG(dprint("puttxrequest id %d ref %d size %d\n", tr->id, tr->gref, tr->size);)
84         i = ctlr->txring.req_prod_pvt;
85         req = RING_GET_REQUEST(&ctlr->txring, i);
86         memmove(req, tr, sizeof(*req));
87         ctlr->txring.req_prod_pvt = i+1;
88         RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&ctlr->txring, notify);
89         return notify;
90 }
91
92 static int
93 putrxrequest(Ctlr *ctlr, netif_rx_request_t *rr)
94 {
95         netif_rx_request_t *req;
96         int i;
97         int notify;
98
99         LOG(dprint("putrxrequest %d %d\n", rr->id, rr->gref);)
100         i = ctlr->rxring.req_prod_pvt;
101         req = RING_GET_REQUEST(&ctlr->rxring, i);
102         memmove(req, rr, sizeof(*req));
103         ctlr->rxring.req_prod_pvt = i+1;
104         RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&ctlr->rxring, notify);
105         return notify;
106 }
107
108 static int
109 gettxresponse(Ctlr *ctlr, netif_tx_response_t *tr)
110 {
111         int i, avail;
112         netif_tx_response_t *rx;
113
114         RING_FINAL_CHECK_FOR_RESPONSES(&ctlr->txring, avail);
115         if (!avail)
116                 return 0;
117         i = ctlr->txring.rsp_cons;
118         rx = RING_GET_RESPONSE(&ctlr->txring, i);
119         LOG(dprint("gettxresponse id %d status %d\n", rx->id, rx->status);)
120         if(rx->status)
121                 ctlr->txerrors++;
122         *tr = *rx;
123         ctlr->txring.rsp_cons = ++i;
124         return 1;
125 }
126
127 static int
128 getrxresponse(Ctlr *ctlr, netif_rx_response_t* rr)
129 {
130         int i, avail;
131         netif_rx_response_t *rx;
132
133         RING_FINAL_CHECK_FOR_RESPONSES(&ctlr->rxring, avail);
134         if (!avail)
135                 return 0;
136         i = ctlr->rxring.rsp_cons;
137         rx = RING_GET_RESPONSE(&ctlr->rxring, i);
138         LOG(dprint("getrxresponse id %d offset %d flags %ux status %d\n", rx->id, rx->offset, rx->flags, rx->status);)
139         *rr = *rx;
140         ctlr->rxring.rsp_cons = ++i;
141         return 1;
142 }
143
144 static int
145 ringinit(Ctlr *ctlr, char *a)
146 {
147         netif_tx_sring_t *txr;
148         netif_rx_sring_t *rxr;
149
150         txr = (netif_tx_sring_t*)a;
151         memset(txr, 0, BY2PG);
152         SHARED_RING_INIT(txr);
153         FRONT_RING_INIT(&ctlr->txring, txr, BY2PG);
154         ctlr->txringref = shareframe(ctlr->backend, txr, 1);
155
156         rxr = (netif_rx_sring_t*)(a+BY2PG);
157         SHARED_RING_INIT(rxr);
158         FRONT_RING_INIT(&ctlr->rxring, rxr, BY2PG);
159         ctlr->rxringref = shareframe(ctlr->backend, rxr, 1);
160
161         return 2*BY2PG;
162 }
163
164 static int
165 vifsend(Ctlr *ctlr, Block *bp)
166 {
167         netif_tx_request_t tr;
168         Txframe *tx;
169         int id;
170
171         ilock(&ctlr->txlock);
172         tx = ctlr->freetxframe;
173         ctlr->freetxframe = tx->tf.next;
174         iunlock(&ctlr->txlock);
175         id = tx - ctlr->txframes;
176         tr.gref = ctlr->txrefs[id];
177         tr.offset = tx->tf.data - (char*)tx;
178         tr.flags = 0;   // XXX checksum?
179         tr.id = id;
180         tr.size = BLEN(bp);
181         memmove(tx->tf.data, bp->rp, tr.size);
182         return puttxrequest(ctlr, &tr);
183 }
184
185 static int
186 vifsenddone(Ctlr *ctlr, netif_tx_response_t *tr)
187 {
188         Txframe *tx;
189
190         tx = &ctlr->txframes[tr->id];   // XXX check validity of id
191         ilock(&ctlr->txlock);
192         tx->tf.next = ctlr->freetxframe;
193         ctlr->freetxframe = tx;
194         iunlock(&ctlr->txlock);
195         return 1;
196 }
197
198 static int
199 vifrecv(Ctlr *ctlr, Rxframe *rx)
200 {
201         netif_rx_request_t rr;
202         int id;
203         int ref;
204
205         id = rx - ctlr->rxframes;
206         if (ctlr->rxcopy)
207                 ref = ctlr->rxrefs[id];
208         else {
209                 ref = donateframe(ctlr->backend, rx);
210                 ctlr->rxrefs[id] = ref;
211         }
212         rr.id = id;
213         rr.gref = ref;
214         return putrxrequest(ctlr, &rr);
215 }
216
217 static int
218 vifrecvdone(Ether *ether, netif_rx_response_t *rr)
219 {
220         Ctlr *ctlr;
221         Rxframe *rx;
222         Block *bp;
223         int len;
224
225         ctlr = ether->ctlr;
226         rx = &ctlr->rxframes[rr->id];   // XXX check validity of id
227         if (!ctlr->rxcopy)
228                 acceptframe(ctlr->rxrefs[rr->id], rx);
229         if ((len = rr->status) <= 0) {
230                 ctlr->rxerrors++;
231                 vifrecv(ctlr, rx);
232                 return 1;
233         }
234         if(len > sizeof(Etherpkt) || (bp = iallocb(sizeof(Etherpkt))) == nil) {
235                 ctlr->rxoverflows++;
236                 vifrecv(ctlr, rx);
237                 return 1;
238         }
239
240         ctlr->receives++;
241         memmove(bp->base, rx->page + rr->offset, len);
242         vifrecv(ctlr, rx);
243
244         bp->rp = bp->base;
245         bp->wp = bp->rp + len;
246         bp->free = 0;
247         bp->next = 0;
248         bp->list = 0;
249         if (rr->flags & NETRXF_data_validated)
250                 bp->flag |= Btcpck|Budpck;
251         etheriq(ether, bp);
252         return 0;
253 }
254
255 static int
256 wtxframe(void *a)
257 {
258         return ((struct Ctlr*)a)->freetxframe != 0;
259 }
260
261 static int
262 wtxblock(void *a)
263 {
264         return qcanread(((struct Ether*)a)->oq);
265 }
266
267 static void
268 etherxenproc(void *a)
269 {
270         Ether *ether = a;
271         Ctlr *ctlr = ether->ctlr;
272         Block *bp;
273         int notify;
274
275         for (;;) {
276                 while (ctlr->freetxframe == 0)
277                         sleep(&ctlr->wtxframe, wtxframe, ctlr);
278                 while ((bp = qget(ether->oq)) == 0)
279                         sleep(&ctlr->wtxblock, wtxblock, ether);
280                 notify = vifsend(ctlr, bp);
281                 freeb(bp);
282                 if (notify)
283                         xenchannotify(ctlr->evtchn);
284         }
285 }
286
287 static void
288 etherxentransmit(Ether *ether)
289 {
290         Ctlr *ctlr;
291         
292         ctlr = ether->ctlr;
293         ctlr->transmits++;
294         wakeup(&ctlr->wtxblock);
295 }
296
297 static void
298 etherxenintr(Ureg*, void *a)
299 {
300         Ether *ether = a;
301         Ctlr *ctlr = ether->ctlr;
302         int txnotify;
303         netif_tx_response_t tr;
304         netif_rx_response_t rr;
305
306         ctlr->interrupts++;
307         txnotify = 0;
308         while (getrxresponse(ctlr, &rr))
309                 vifrecvdone(ether, &rr);
310         while (gettxresponse(ctlr, &tr)) {
311                 if (vifsenddone(ctlr, &tr))
312                         txnotify = 1;
313         }
314         if (txnotify)
315                 wakeup(&ctlr->wtxframe);
316 }
317
318 static long
319 etherxenctl(Ether *ether, void *buf, long n)
320 {
321         uchar ea[Eaddrlen];
322         Cmdbuf *cb;
323
324         cb = parsecmd(buf, n);
325         if(cb->nf >= 2
326         && strcmp(cb->f[0], "ea")==0
327         && parseether(ea, cb->f[1]) == 0){
328                 free(cb);
329                 memmove(ether->ea, ea, Eaddrlen);
330                 memmove(ether->addr, ether->ea, Eaddrlen);
331                 return 0;
332         }
333         free(cb);
334         error(Ebadctl);
335         return -1;      /* not reached */
336 }
337
338 static void
339 backendconnect(Ctlr *ctlr)
340 {
341         char dir[64];
342         char buf[64];
343
344         sprint(dir, "device/vif/%d/", ctlr->vifno);
345         xenstore_setd(dir, "state", XenbusStateInitialising);
346         xenstore_setd(dir, "tx-ring-ref", ctlr->txringref);
347         xenstore_setd(dir, "rx-ring-ref", ctlr->rxringref);
348         xenstore_setd(dir, "event-channel", ctlr->evtchn);
349         print("etherxen: request-rx-copy=%d\n", ctlr->rxcopy);
350         if (ctlr->rxcopy)
351                 xenstore_setd(dir, "request-rx-copy", 1);
352         xenstore_setd(dir, "state", XenbusStateConnected);
353         xenstore_gets(dir, "backend", buf, sizeof buf);
354         sprint(dir, "%s/", buf);
355         HYPERVISOR_yield();
356         xenstore_gets(dir, "state", buf, sizeof buf);
357         while (strtol(buf, 0, 0) != XenbusStateConnected) {
358                 print("etherxen: waiting for vif %d to connect\n", ctlr->vifno);
359                 tsleep(&up->sleep, return0, 0, 50);
360                 xenstore_gets(dir, "state", buf, sizeof buf);
361         }
362 }
363
364 static void
365 etherxenattach(Ether *ether)
366 {
367         Ctlr *ctlr;
368         char *p;
369         Txframe *tx;
370         int npage, i;
371
372         LOG(dprint("etherxenattach\n");)
373         ctlr = ether->ctlr;
374         qlock(&ctlr->attachlock);
375         if (ctlr->attached) {
376                 qunlock(&ctlr->attachlock);
377                 return;
378         }
379
380         npage = 2 + Ntb + Nrb;
381         p = (char*)xspanalloc(npage<<PGSHIFT, BY2PG, 0);
382         p += ringinit(ctlr, p);
383         ctlr->txrefs = malloc(Ntb*sizeof(int));
384         ctlr->rxrefs = malloc(Nrb*sizeof(int));
385         ctlr->txframes = (Txframe*)p;
386         for (i = 0; i < Ntb; i++, p += BY2PG) {
387                 tx = (Txframe*)p;
388                 if (i != Ntb-1)
389                         tx->tf.next = tx + 1;
390                 else
391                         tx->tf.next = 0;
392                 ctlr->txrefs[i] = shareframe(ctlr->backend, tx, 0);
393         }
394         ctlr->freetxframe = ctlr->txframes;
395         ctlr->rxframes = (Rxframe*)p;
396         for (i = 0; i < Nrb; i++, p += BY2PG) {
397                 if (ctlr->rxcopy)
398                         ctlr->rxrefs[i] = shareframe(ctlr->backend, (Rxframe*)p, 1);
399                 vifrecv(ctlr, (Rxframe*)p);
400         }
401         
402         ctlr->evtchn = xenchanalloc(ctlr->backend);
403         intrenable(ctlr->evtchn, etherxenintr, ether, BUSUNKNOWN, "vif");
404
405         kproc("vif", etherxenproc, ether);
406         backendconnect(ctlr);
407         ctlr->attached = 1;
408         qunlock(&ctlr->attachlock);
409 }
410
411 static void
412 etherxenmulticast(void* arg, uchar* addr, int on)
413 {
414         USED(arg, addr, on);
415 }
416
417 static long
418 ifstat(Ether* ether, void* a, long n, ulong offset)
419 {
420         Ctlr *ctlr;
421         char *buf, *p;
422         int l, len;
423
424         ctlr = ether->ctlr;
425         if(n == 0)
426                 return 0;
427         if((p = malloc(READSTR)) == nil)
428                 error(Enomem);
429         l = snprint(p, READSTR, "intr: %lud\n", ctlr->interrupts);
430         l += snprint(p+l, READSTR-l, "transmits: %lud\n", ctlr->transmits);
431         l += snprint(p+l, READSTR-l, "receives: %lud\n", ctlr->receives);
432         l += snprint(p+l, READSTR-l, "txerrors: %lud\n", ctlr->txerrors);
433         l += snprint(p+l, READSTR-l, "rxerrors: %lud\n", ctlr->rxerrors);
434         snprint(p+l, READSTR-l, "rxoverflows: %lud\n", ctlr->rxoverflows);
435
436         buf = a;
437         len = readstr(offset, buf, n, p);
438         free(p);
439
440         return len;
441 }
442
443 static int
444 pnp(Ether* ether)
445 {
446         uchar ea[Eaddrlen];
447         char dir[64];
448         char buf[64];
449         Ctlr *ctlr;
450         int domid, rxcopy;
451
452         if (nvif > Nvif)
453                 return -1;
454         sprint(dir, "device/vif/%d/", nvif);
455         if (xenstore_gets(dir, "backend-id", buf, sizeof buf) <= 0)
456                 return -1;
457         domid = strtol(buf, 0, 0);
458         if (xenstore_gets(dir, "mac", buf, sizeof buf) <= 0)
459                 return -1;
460         if (parseether(ea, buf) < 0)
461                 return -1;
462         if (xenstore_gets(dir, "backend", buf, sizeof buf) <= 0)
463                 return 1;
464         sprint(dir, "%s/", buf);
465         rxcopy = 0;
466         if (xenstore_gets(dir, "feature-rx-copy", buf, sizeof buf) >= 0)
467                 rxcopy = strtol(buf, 0, 0);
468         ether->ctlr = ctlr = malloc(sizeof(Ctlr));
469         memset(ctlr, 0, sizeof(Ctlr));
470         ctlr->backend = domid;
471         ctlr->vifno = nvif++;
472         ctlr->rxcopy = rxcopy;
473
474         memmove(ether->ea, ea, sizeof ether->ea);
475         ether->mbps = 100;      // XXX what speed?
476         ether->attach = etherxenattach;
477         ether->detach = nil;
478         ether->transmit = etherxentransmit;
479         ether->irq = -1;
480         ether->tbdf = BUSUNKNOWN;
481         ether->ifstat = ifstat;
482         ether->ctl = etherxenctl;
483         ether->promiscuous = nil;
484         ether->multicast = etherxenmulticast;
485         ether->arg = ether;
486
487         intrenable(ether->irq, etherxenintr, ether, ether->tbdf, ether->name);
488
489         return 0;
490 }
491
492 void
493 etherxenlink(void)
494 {
495         addethercard("xen", pnp);
496 }