diff options
author | mischief <mischief@offblast.org> | 2014-06-24 18:02:25 -0700 |
---|---|---|
committer | mischief <mischief@offblast.org> | 2014-06-24 18:02:25 -0700 |
commit | 5ba95fdb07ddc2c32111a1b2f57f17aa27fcbbf5 (patch) | |
tree | c1ec54cb9ecff85b0b820a26d26a10a32a118d0c /sys/src/9/xen/etherxen.c | |
parent | fa03455b5057675b18d1c87aef2d1071b2088de0 (diff) |
import xen 32 bit paravirtual kernel from /n/sources/xen.
Diffstat (limited to 'sys/src/9/xen/etherxen.c')
-rw-r--r-- | sys/src/9/xen/etherxen.c | 494 |
1 files changed, 494 insertions, 0 deletions
diff --git a/sys/src/9/xen/etherxen.c b/sys/src/9/xen/etherxen.c new file mode 100644 index 000000000..143f254dc --- /dev/null +++ b/sys/src/9/xen/etherxen.c @@ -0,0 +1,494 @@ +/* + * Xen virtual network interface frontend + */ + +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" +#include "../port/netif.h" +#include "etherif.h" + +#define LOG(a) + +enum { + Nvif = 4, + Ntb = 16, + Nrb = 32, +}; + +typedef struct Ctlr Ctlr; +typedef union Txframe Txframe; +typedef union Rxframe Rxframe; + +struct Ctlr { + int attached; + int backend; + int vifno; + int evtchn; + int rxcopy; + Txframe *txframes; + Txframe *freetxframe; + Rxframe *rxframes; + netif_tx_front_ring_t txring; + netif_rx_front_ring_t rxring; + int *txrefs; + int *rxrefs; + int txringref; + int rxringref; + Lock txlock; + QLock attachlock; + Rendez wtxframe; + Rendez wtxblock; + + ulong interrupts; + ulong transmits; + ulong receives; + ulong txerrors; + ulong rxerrors; + ulong rxoverflows; +}; + +union Txframe { + struct { + Txframe *next; + char data[2]; + } tf; + uchar page[BY2PG]; +}; + +union Rxframe { + uchar page[BY2PG]; +}; + +static int nvif; + +/* + * 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)) + +static int +puttxrequest(Ctlr *ctlr, netif_tx_request_t *tr) +{ + netif_tx_request_t *req; + int i, notify; + + LOG(dprint("puttxrequest id %d ref %d size %d\n", tr->id, tr->gref, tr->size);) + i = ctlr->txring.req_prod_pvt; + req = RING_GET_REQUEST(&ctlr->txring, i); + memmove(req, tr, sizeof(*req)); + ctlr->txring.req_prod_pvt = i+1; + RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&ctlr->txring, notify); + return notify; +} + +static int +putrxrequest(Ctlr *ctlr, netif_rx_request_t *rr) +{ + netif_rx_request_t *req; + int i; + int notify; + + LOG(dprint("putrxrequest %d %d\n", rr->id, rr->gref);) + i = ctlr->rxring.req_prod_pvt; + req = RING_GET_REQUEST(&ctlr->rxring, i); + memmove(req, rr, sizeof(*req)); + ctlr->rxring.req_prod_pvt = i+1; + RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&ctlr->rxring, notify); + return notify; +} + +static int +gettxresponse(Ctlr *ctlr, netif_tx_response_t *tr) +{ + int i, avail; + netif_tx_response_t *rx; + + RING_FINAL_CHECK_FOR_RESPONSES(&ctlr->txring, avail); + if (!avail) + return 0; + i = ctlr->txring.rsp_cons; + rx = RING_GET_RESPONSE(&ctlr->txring, i); + LOG(dprint("gettxresponse id %d status %d\n", rx->id, rx->status);) + if(rx->status) + ctlr->txerrors++; + *tr = *rx; + ctlr->txring.rsp_cons = ++i; + return 1; +} + +static int +getrxresponse(Ctlr *ctlr, netif_rx_response_t* rr) +{ + int i, avail; + netif_rx_response_t *rx; + + RING_FINAL_CHECK_FOR_RESPONSES(&ctlr->rxring, avail); + if (!avail) + return 0; + i = ctlr->rxring.rsp_cons; + rx = RING_GET_RESPONSE(&ctlr->rxring, i); + LOG(dprint("getrxresponse id %d offset %d flags %ux status %d\n", rx->id, rx->offset, rx->flags, rx->status);) + *rr = *rx; + ctlr->rxring.rsp_cons = ++i; + return 1; +} + +static int +ringinit(Ctlr *ctlr, char *a) +{ + netif_tx_sring_t *txr; + netif_rx_sring_t *rxr; + + txr = (netif_tx_sring_t*)a; + memset(txr, 0, BY2PG); + SHARED_RING_INIT(txr); + FRONT_RING_INIT(&ctlr->txring, txr, BY2PG); + ctlr->txringref = shareframe(ctlr->backend, txr, 1); + + rxr = (netif_rx_sring_t*)(a+BY2PG); + SHARED_RING_INIT(rxr); + FRONT_RING_INIT(&ctlr->rxring, rxr, BY2PG); + ctlr->rxringref = shareframe(ctlr->backend, rxr, 1); + + return 2*BY2PG; +} + +static int +vifsend(Ctlr *ctlr, Block *bp) +{ + netif_tx_request_t tr; + Txframe *tx; + int id; + + ilock(&ctlr->txlock); + tx = ctlr->freetxframe; + ctlr->freetxframe = tx->tf.next; + iunlock(&ctlr->txlock); + id = tx - ctlr->txframes; + tr.gref = ctlr->txrefs[id]; + tr.offset = tx->tf.data - (char*)tx; + tr.flags = 0; // XXX checksum? + tr.id = id; + tr.size = BLEN(bp); + memmove(tx->tf.data, bp->rp, tr.size); + return puttxrequest(ctlr, &tr); +} + +static int +vifsenddone(Ctlr *ctlr, netif_tx_response_t *tr) +{ + Txframe *tx; + + tx = &ctlr->txframes[tr->id]; // XXX check validity of id + ilock(&ctlr->txlock); + tx->tf.next = ctlr->freetxframe; + ctlr->freetxframe = tx; + iunlock(&ctlr->txlock); + return 1; +} + +static int +vifrecv(Ctlr *ctlr, Rxframe *rx) +{ + netif_rx_request_t rr; + int id; + int ref; + + id = rx - ctlr->rxframes; + if (ctlr->rxcopy) + ref = ctlr->rxrefs[id]; + else { + ref = donateframe(ctlr->backend, rx); + ctlr->rxrefs[id] = ref; + } + rr.id = id; + rr.gref = ref; + return putrxrequest(ctlr, &rr); +} + +static int +vifrecvdone(Ether *ether, netif_rx_response_t *rr) +{ + Ctlr *ctlr; + Rxframe *rx; + Block *bp; + int len; + + ctlr = ether->ctlr; + rx = &ctlr->rxframes[rr->id]; // XXX check validity of id + if (!ctlr->rxcopy) + acceptframe(ctlr->rxrefs[rr->id], rx); + if ((len = rr->status) <= 0) { + ctlr->rxerrors++; + vifrecv(ctlr, rx); + return 1; + } + if(len > sizeof(Etherpkt) || (bp = iallocb(sizeof(Etherpkt))) == nil) { + ctlr->rxoverflows++; + vifrecv(ctlr, rx); + return 1; + } + + ctlr->receives++; + memmove(bp->base, rx->page + rr->offset, len); + vifrecv(ctlr, rx); + + bp->rp = bp->base; + bp->wp = bp->rp + len; + bp->free = 0; + bp->next = 0; + bp->list = 0; + if (rr->flags & NETRXF_data_validated) + bp->flag |= Btcpck|Budpck; + etheriq(ether, bp, 1); + return 0; +} + +static int +wtxframe(void *a) +{ + return ((struct Ctlr*)a)->freetxframe != 0; +} + +static int +wtxblock(void *a) +{ + return qcanread(((struct Ether*)a)->oq); +} + +static void +etherxenproc(void *a) +{ + Ether *ether = a; + Ctlr *ctlr = ether->ctlr; + Block *bp; + int notify; + + for (;;) { + while (ctlr->freetxframe == 0) + sleep(&ctlr->wtxframe, wtxframe, ctlr); + while ((bp = qget(ether->oq)) == 0) + sleep(&ctlr->wtxblock, wtxblock, ether); + notify = vifsend(ctlr, bp); + freeb(bp); + if (notify) + xenchannotify(ctlr->evtchn); + } +} + +static void +etherxentransmit(Ether *ether) +{ + Ctlr *ctlr; + + ctlr = ether->ctlr; + ctlr->transmits++; + wakeup(&ctlr->wtxblock); +} + +static void +etherxenintr(Ureg*, void *a) +{ + Ether *ether = a; + Ctlr *ctlr = ether->ctlr; + int txnotify; + netif_tx_response_t tr; + netif_rx_response_t rr; + + ctlr->interrupts++; + txnotify = 0; + while (getrxresponse(ctlr, &rr)) + vifrecvdone(ether, &rr); + while (gettxresponse(ctlr, &tr)) { + if (vifsenddone(ctlr, &tr)) + txnotify = 1; + } + if (txnotify) + wakeup(&ctlr->wtxframe); +} + +static long +etherxenctl(Ether *ether, void *buf, long n) +{ + uchar ea[Eaddrlen]; + Cmdbuf *cb; + + cb = parsecmd(buf, n); + if(cb->nf >= 2 + && strcmp(cb->f[0], "ea")==0 + && parseether(ea, cb->f[1]) == 0){ + free(cb); + memmove(ether->ea, ea, Eaddrlen); + memmove(ether->addr, ether->ea, Eaddrlen); + return 0; + } + free(cb); + error(Ebadctl); + return -1; /* not reached */ +} + +static void +backendconnect(Ctlr *ctlr) +{ + char dir[64]; + char buf[64]; + + sprint(dir, "device/vif/%d/", ctlr->vifno); + xenstore_setd(dir, "state", XenbusStateInitialising); + xenstore_setd(dir, "tx-ring-ref", ctlr->txringref); + xenstore_setd(dir, "rx-ring-ref", ctlr->rxringref); + xenstore_setd(dir, "event-channel", ctlr->evtchn); + print("etherxen: request-rx-copy=%d\n", ctlr->rxcopy); + if (ctlr->rxcopy) + xenstore_setd(dir, "request-rx-copy", 1); + xenstore_setd(dir, "state", XenbusStateConnected); + 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("etherxen: waiting for vif %d to connect\n", ctlr->vifno); + tsleep(&up->sleep, return0, 0, 1000); + xenstore_gets(dir, "state", buf, sizeof buf); + } +} + +static void +etherxenattach(Ether *ether) +{ + Ctlr *ctlr; + char *p; + Txframe *tx; + int npage, i; + + LOG(dprint("etherxenattach\n");) + ctlr = ether->ctlr; + qlock(&ctlr->attachlock); + if (ctlr->attached) { + qunlock(&ctlr->attachlock); + return; + } + + npage = 2 + Ntb + Nrb; + p = (char*)xspanalloc(npage<<PGSHIFT, BY2PG, 0); + p += ringinit(ctlr, p); + ctlr->txrefs = malloc(Ntb*sizeof(int)); + ctlr->rxrefs = malloc(Nrb*sizeof(int)); + ctlr->txframes = (Txframe*)p; + for (i = 0; i < Ntb; i++, p += BY2PG) { + tx = (Txframe*)p; + if (i != Ntb-1) + tx->tf.next = tx + 1; + else + tx->tf.next = 0; + ctlr->txrefs[i] = shareframe(ctlr->backend, tx, 0); + } + ctlr->freetxframe = ctlr->txframes; + ctlr->rxframes = (Rxframe*)p; + for (i = 0; i < Nrb; i++, p += BY2PG) { + if (ctlr->rxcopy) + ctlr->rxrefs[i] = shareframe(ctlr->backend, (Rxframe*)p, 1); + vifrecv(ctlr, (Rxframe*)p); + } + + ctlr->evtchn = xenchanalloc(ctlr->backend); + intrenable(ctlr->evtchn, etherxenintr, ether, BUSUNKNOWN, "vif"); + + kproc("vif", etherxenproc, ether); + backendconnect(ctlr); + ctlr->attached = 1; + qunlock(&ctlr->attachlock); +} + +static void +etherxenmulticast(void* arg, uchar* addr, int on) +{ + USED(arg, addr, on); +} + +static long +ifstat(Ether* ether, void* a, long n, ulong offset) +{ + Ctlr *ctlr; + char *buf, *p; + int l, len; + + ctlr = ether->ctlr; + if(n == 0) + return 0; + if((p = malloc(READSTR)) == nil) + error(Enomem); + l = snprint(p, READSTR, "intr: %lud\n", ctlr->interrupts); + l += snprint(p+l, READSTR-l, "transmits: %lud\n", ctlr->transmits); + l += snprint(p+l, READSTR-l, "receives: %lud\n", ctlr->receives); + l += snprint(p+l, READSTR-l, "txerrors: %lud\n", ctlr->txerrors); + l += snprint(p+l, READSTR-l, "rxerrors: %lud\n", ctlr->rxerrors); + snprint(p+l, READSTR-l, "rxoverflows: %lud\n", ctlr->rxoverflows); + + buf = a; + len = readstr(offset, buf, n, p); + free(p); + + return len; +} + +static int +pnp(Ether* ether) +{ + uchar ea[Eaddrlen]; + char dir[64]; + char buf[64]; + Ctlr *ctlr; + int domid, rxcopy; + + if (nvif > Nvif) + return -1; + sprint(dir, "device/vif/%d/", nvif); + if (xenstore_gets(dir, "backend-id", buf, sizeof buf) <= 0) + return -1; + domid = strtol(buf, 0, 0); + if (xenstore_gets(dir, "mac", buf, sizeof buf) <= 0) + return -1; + if (parseether(ea, buf) < 0) + return -1; + if (xenstore_gets(dir, "backend", buf, sizeof buf) <= 0) + return 1; + sprint(dir, "%s/", buf); + rxcopy = 0; + if (xenstore_gets(dir, "feature-rx-copy", buf, sizeof buf) >= 0) + rxcopy = strtol(buf, 0, 0); + ether->ctlr = ctlr = malloc(sizeof(Ctlr)); + memset(ctlr, 0, sizeof(Ctlr)); + ctlr->backend = domid; + ctlr->vifno = nvif++; + ctlr->rxcopy = rxcopy; + + memmove(ether->ea, ea, sizeof ether->ea); + ether->mbps = 100; // XXX what speed? + ether->attach = etherxenattach; + ether->detach = nil; + ether->transmit = etherxentransmit; + ether->irq = -1; + ether->tbdf = BUSUNKNOWN; + ether->interrupt = etherxenintr; + ether->ifstat = ifstat; + ether->ctl = etherxenctl; + ether->promiscuous = nil; + ether->multicast = etherxenmulticast; + ether->arg = ether; + return 0; +} + +void +etherxenlink(void) +{ + addethercard("xen", pnp); +} |