plan9fox/sys/src/9/xen/etherxen.c
cinap_lenrek f0a314605f devether: remove (unimplemented) detach, allow device creation on attach
we allow devether to create ethernet cards on attach. this is useull
for virtual cards like the sink driver, so we can create a sink
by simply: bind -a '#l2:sink ea=112233445566' /net

the detach routine was never called, so remove it from the few drivers
that attempted to implement it.
2018-02-25 03:42:38 +01:00

496 lines
10 KiB
C

/*
* 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 "../port/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);
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, 50);
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->transmit = etherxentransmit;
ether->irq = -1;
ether->tbdf = BUSUNKNOWN;
ether->ifstat = ifstat;
ether->ctl = etherxenctl;
ether->promiscuous = nil;
ether->multicast = etherxenmulticast;
ether->arg = ether;
intrenable(ether->irq, etherxenintr, ether, ether->tbdf, ether->name);
return 0;
}
void
etherxenlink(void)
{
addethercard("xen", pnp);
}