/*
* Copyright (c) 2013, Coraid, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Coraid nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL CORAID BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Adapted by Brian L. Stuart from:
*
* vblade — virtual aoe target
* copyright © 2007—2013 erik quanstrom
*/
#include <u.h>
#include <libc.h>
#include <thread.h>
#include <ip.h> /* irony */
#include <fis.h>
#include <fcall.h>
#include <9p.h>
enum {
Eaddrlen = 6, /* only defined in kernel */
};
#include "aoe.h"
#include "dat.h"
enum {
Fclone,
Fdata,
Flast,
Nether = 8,
Nvblade = 16,
Nmask = 10,
Nmaxout= 128,
Maxpkt = 10000,
Conflen = 1024,
};
typedef struct Vblade Vblade;
struct Vblade {
vlong maxlba;
uint nmask;
Lock mlk;
uchar *mask;
int shelf;
int slot;
int clen;
char *config;
};
static Vblade vblade[Nvblade];
static int nblade;
static char *ethertab[Nether] = {
"#l0/ether0",
};
static int etheridx = 1;
static int efdtab[Nether*Flast];
static uchar pkttab[Nether][Maxpkt];
static uchar bctab[Nether][Maxpkt];
static int mtutab[Nether];
static Ioproc *ioprocs[Nether];
static int
getmtu(char *p)
{
char buf[50];
int fd, mtu;
snprint(buf, sizeof buf, "%s/mtu", p);
if((fd = open(buf, OREAD)) == -1)
return 2;
if(read(fd, buf, 36) < 0)
return 2;
close(fd);
buf[36] = 0;
mtu = strtoul(buf+12, 0, 0)-Aoehsz-Aoeatasz;
return mtu>>9;
}
static int
aoeopen(Ioproc *io, char *e, int fds[])
{
char buf[128], ctl[13];
int n;
snprint(buf, sizeof buf, "%s/clone", e);
if((fds[Fclone] = ioopen(io, buf, ORDWR)) == -1)
return -1;
memset(ctl, 0, sizeof ctl);
if(ioread(io, fds[Fclone], ctl, sizeof ctl - 1) < 0)
return -1;
n = atoi(ctl);
snprint(buf, sizeof buf, "connect %d", Aoetype);
if(iowrite(io, fds[Fclone], buf, strlen(buf)) != strlen(buf))
return -1;
snprint(buf, sizeof buf, "%s/%d/data", e, n);
fds[Fdata] = ioopen(io, buf, ORDWR);
return fds[Fdata];
}
static void
replyhdr(Aoehdr *h, Vblade *vblade)
{
uchar ea[Eaddrlen];
memmove(ea, h->dst, Eaddrlen);
memmove(h->dst, h->src, Eaddrlen);
memmove(h->src, ea, Eaddrlen);
hnputs(h->major, vblade->shelf);
h->minor = vblade->slot;
h->verflag |= AFrsp;
}
static int
servebad(uchar *pkt, Vblade*, int)
{
Aoehdr *h;
h = (Aoehdr*)pkt;
h->verflag |= AFerr;
h->error = AEcmd;
return Aoehsz;
}
static uchar nilea[Eaddrlen];
/*
static void
savemask(Vblade *vb)
{
uvlong qpath, meta;
qpath = ((uvlong)TLun << 60) | (vb->shelf << 8) | vb->slot;
meta = q2m(-1, qpath, 0);
if(meta == 0)
return;
setmetaint(meta, "nmask", nil, vb->nmask);
if(vb->mask)
setmetablob(meta, "mask", nil, vb->mask, vb->nmask * Eaddrlen, 0);
else
setmetastr(meta, "mask", nil, "", 0);
}
*/
static int
servemask(uchar *pkt, Vblade *vb, int mtu)
{
int i, j, r, e;
uchar mx[Nmask*Eaddrlen], *mtab[Nmask], *p;
Aoem *m;
Aoemd *d;
m = (Aoem*)(pkt + Aoehsz);
if(m->mcnt > (mtu - Aoehsz - Aoemsz)/Aoemdsz)
return -1;
if(!canlock(&vb->mlk))
return -1; /* drop */
switch(m->mcmd){
default:
unlock(&vb->mlk);
return servebad(pkt, vb, mtu);
case Medit:
memcpy(mx, vb->mask, vb->nmask*Eaddrlen);
j = 0;
for(i = 0; i < vb->nmask; i++){
p = mx + i*Eaddrlen;
if(memcmp(p, nilea, Eaddrlen) != 0)
mtab[j++] = p;
}
e = 0;
p = pkt + Aoehsz + Aoemsz;
for(i = 0; i < m->mcnt && e == 0; i++){
d = (Aoemd*)(p + i*Aoemdsz);
switch(d->dcmd){
default:
e = MEunk;
break;
case MDnop:
break;
case MDadd:
for(i = 0; i < j; i++)
if(memcmp(d->ea, mtab[j], Eaddrlen) == 0)
continue;
if(j == Nmask)
e = MEfull;
else
memcpy(mtab[j++], d->ea, Eaddrlen);
break;
case MDdel:
for(i = 0; i < j; i++)
if(memcmp(d->ea, mtab[j], Eaddrlen) == 0)
break;
if(i < j){
for(; i < j; i++)
mtab[i] = mtab[i+1];
j--;
}
break;
}
}
if(e != 0){
m->merr = e;
r = Aoehsz + Aoemsz;
break;
}
p = malloc(j*Eaddrlen);
if(p == nil){
r = -1;
break;
}
for(i = 0; i < j; i++)
memcpy(p+i*Eaddrlen, mtab[i], Eaddrlen);
free(vb->mask);
vb->nmask = j;
vb->mask = p;
case Mread:
m->mcnt = vb->nmask;
m->merr = 0;
p = pkt + Aoehsz + Aoemsz;
for(i = 0; i < m->mcnt; i++){
d = (Aoemd*)(p + i*Aoemdsz);
d->dres = 0;
d->dcmd = MDnop;
memcpy(d->ea, vb->mask + i*Eaddrlen, Eaddrlen);
}
r = Aoehsz + Aoemsz + m->mcnt * Aoemdsz;
break;
}
unlock(&vb->mlk);
return r;
}
static void
saveconfig(Vblade *vb)
{
uvlong qpath, meta;
qpath = ((uvlong)TLun << 60) | (vb->shelf << 8) | vb->slot;
meta = q2m(-1, qpath, 0);
if(meta == 0)
return;
if(vb->config)
setmetastr(meta, "config", nil, vb->config, 0);
else
setmetastr(meta, "config", nil, "", 0);
}
static int
serveconfig(uchar *pkt, Vblade *vb, int mtu)
{
char *cfg;
int cmd, reqlen, len;
Aoehdr *h;
Aoecfg *q;
h = (Aoehdr*)pkt;
q = (Aoecfg*)(pkt + Aoehsz);
if(memcmp(h->src, h->dst, Eaddrlen) == 0)
return -1;
reqlen = nhgets(q->cslen);
len = vb->clen;
cmd = q->verccmd&0xf;
cfg = (char*)(pkt + Aoehsz + Aoecfgsz);
switch(cmd){
case AQCtest:
if(reqlen != len)
return -1;
case AQCprefix:
if(reqlen > len)
return -1;
if(memcmp(vb->config, cfg, reqlen) != 0)
return -1;
case AQCread:
break;
case AQCset:
if(len && len != reqlen || memcmp(vb->config, cfg, reqlen) != 0){
h->verflag |= AFerr;
h->error = AEcfg;
break;
}
case AQCfset:
if(reqlen > Conflen){
h->verflag |= AFerr;
h->error = AEarg;
break;
}
free(vb->config);
vb->config = θmalloc(reqlen + 1);
memmove(vb->config, cfg, reqlen);
vb->clen = len = reqlen;
saveconfig(vb);
break;
default:
h->verflag |= AFerr;
h->error = AEarg;
break;
}
if(vb->config)
memmove(cfg, vb->config, len);
hnputs(q->cslen, len);
hnputs(q->bufcnt, Nmaxout);
q->scnt = mtu;
hnputs(q->fwver, 2323);
q->verccmd = Aoever<<4 | cmd;
return len;
}
static ushort ident[256] = {
[47] 0x8000,
[49] 0x0200,
[50] 0x4000,
[83] 0x5400,
[84] 0x4000,
[86] 0x1400,
[87] 0x4000,
[93] 0x400b,
};
static void
idmoveto(char *a, int idx, int len, char *s)
{
char *p;
p = a+idx*2;
for(; len > 0; len -= 2) {
if(*s == 0)
p[1] = ' ';
else
p[1] = *s++;
if (*s == 0)
p[0] = ' ';
else
p[0] = *s++;
p += 2;
}
}
static void
lbamoveto(char *p, int idx, int n, vlong lba)
{
int i;
p += idx*2;
for(i = 0; i < n; i++)
*p++ = lba>>i*8;
}
enum {
Crd = 0x20,
Crdext = 0x24,
Cwr = 0x30,
Cwrext = 0x34,
Cid = 0xec,
};
static uvlong
getlba(uchar *p)
{
uvlong v;
v = p[0];
v |= p[1]<<8;
v |= p[2]<<16;
v |= p[3]<<24;
v |= (uvlong)p[4]<<32;
v |= (uvlong)p[5]<<40;
return v;
}
static void
putlba(uchar *p, vlong lba)
{
p[0] = lba;
p[1] = lba>>8;
p[2] = lba>>16;
p[3] = lba>>24;
p[4] = lba>>32;
p[5] = lba>>40;
}
static int
serveata(uchar *pkt, Vblade *vb, int mtu)
{
char *buf;
int rbytes, bytes, len;
vlong lba, off, qpath;
Aoehdr *h;
Aoeata *a;
h = (Aoehdr*)pkt;
a = (Aoeata*)(pkt + Aoehsz);
buf = (char*)(pkt + Aoehsz + Aoeatasz);
lba = getlba(a->lba);
len = a->scnt<<9;
off = lba<<9;
if(a->scnt > mtu || a->scnt == 0){
h->verflag |= AFerr;
h->error = AEarg;
a->cmdstat = ASdrdy|ASerr;
return 0;
}
if(a->cmdstat != Cid)
if(lba+a->scnt > vb->maxlba){
a->errfeat = Eidnf;
a->cmdstat = ASdrdy|ASerr;
return 0;
}
if(a->cmdstat&0xf0 == 0x20)
lba &= 0xfffffff;
switch(a->cmdstat){
default:
a->errfeat = Eabrt;
a->cmdstat = ASdrdy|ASerr;
return 0;
case Cid:
memmove(buf, ident, sizeof ident);
idmoveto(buf, 27, 40, "Plan 9 Vblade");
idmoveto(buf, 10, 20, "serial#");
idmoveto(buf, 23, 8, "2");
lbamoveto(buf, 60, 4, vb->maxlba);
lbamoveto(buf, 100, 8, vb->maxlba);
a->cmdstat = ASdrdy;
return 512;
break;
case Crd:
case Crdext:
qpath = ((uvlong)TLun << 60) | (vb->shelf << 8) | vb->slot;
bytes = θpread(-1, qpath, buf, len, off);
rbytes = bytes;
break;
case Cwr:
case Cwrext:
qpath = ((uvlong)TLun << 60) | (vb->shelf << 8) | vb->slot;
bytes = θpwrite(qpath, buf, len, off, 0);
rbytes = 0;
break;
}
if(bytes != len){
a->errfeat = Eabrt;
a->cmdstat = ASdf|ASerr;
putlba(a->lba, lba+(len-bytes)>>9);
return 0;
}
putlba(a->lba, lba+a->scnt);
a->scnt = 0;
a->errfeat = 0;
a->cmdstat = ASdrdy;
return rbytes;
}
static int
myea(Ioproc *io, uchar ea[6], char *p)
{
char buf[50];
int fd;
snprint(buf, sizeof buf, "%s/addr", p);
if((fd = ioopen(io, buf, OREAD)) == -1)
return -1;
if(ioread(io, fd, buf, 12) < 12)
return -1;
ioclose(io, fd);
return parseether(ea, buf);
}
static int
bcastpkt(uchar *pkt, uint shelf, uint slot, int i)
{
Aoehdr *h;
h = (Aoehdr*)pkt;
myea(ioprocs[i], h->dst, ethertab[i]);
memset(h->src, 0xff, Eaddrlen);
hnputs(h->type, Aoetype);
hnputs(h->major, shelf);
h->minor = slot;
h->cmd = ACconfig;
*(u32int*)h->tag = 0;
return Aoehsz + Aoecfgsz;
}
static int
osdgetattr(Aoeosd *o, int len, uvlong pid, uvlong oid)
{
MVal x;
uchar *inbuf, *outbuf, *end;
char *name, *strval;
uvlong meta;
int n, nn, typ, tot;
name = smprint("%016ullx:%016ullx", pid, oid);
meta = q2m(-1, p2q(-1, name, 0), 0);
free(name);
if(meta == 0) {
o->oflag = 0x40;
return 0;
}
len -= 20;
inbuf = θmalloc(len);
memmove(inbuf, o->oaddr, len);
end = inbuf + len;
outbuf = o->opid;
tot = 0;
while(inbuf < end) {
name = (char *)inbuf; /* the compiler's obsession with signed and unsigned is annoying */
nn = strlen(name);
inbuf += nn + 1;
typ = getmeta(-1, meta, name, &x);
switch(typ) {
/*
case MTshort:
if(tot + nn + 4 >= 8192)
goto done;
tot += nn + 4;
strcpy((char *)outbuf, name);
outbuf += nn + 1;
*outbuf++ = 'h';
hnputs(outbuf, *((ushort *)x));
outbuf += 2;
break;
case MTlong:
if(tot + nn + 6 >= 8192)
goto done;
tot += nn + 6;
strcpy((char *)outbuf, name);
outbuf += nn + 1;
*outbuf++ = 'l';
hnputl(outbuf, *((ulong *)x));
outbuf += 4;
break;
*/
case MTint:
if(tot + nn + 10 >= 8192)
goto done;
tot += nn + 10;
strcpy((char *)outbuf, name);
outbuf += nn + 1;
*outbuf++ = 'v';
hnputv(outbuf, x.val);
outbuf += 8;
break;
case MTistring:
n = strlen(x.str) + 1;
if(tot + nn + n + 3 >= 8192)
goto done;
tot += nn + n + 3;
strcpy((char *)outbuf, name);
outbuf += nn + 1;
*outbuf++ = 's';
strcpy((char *)outbuf, x.str);
outbuf += n;
break;
case MTstring:
strval = getblob(-1, x.val, &n);
if(tot + nn + n + 3 >= 8192)
goto done;
tot += nn + n + 3;
strcpy((char *)outbuf, name);
outbuf += nn + 1;
*outbuf++ = 's';
strcpy((char *)outbuf, strval);
free(strval);
outbuf += n;
break;
case MTblob:
strval = getblob(-1, x.val, &n);
if(tot + nn + n + 4 >= 8192)
goto done;
tot += nn + n + 4;
strcpy((char *)outbuf, name);
outbuf += nn + 1;
*outbuf++ = 'b';
hnputs(outbuf, n);
outbuf += 2;
memmove(outbuf, strval, n);
free(strval);
outbuf += n;
break;
}
}
done:
brelease(meta);
return tot;
}
static int
osdsetattr(Aoeosd *o, int len, uvlong pid, uvlong oid)
{
uchar *buf, *end;
char *name;
uvlong meta;
int n;
name = smprint("%016ullx:%016ullx", pid, oid);
meta = q2m(-1, p2q(-1, name, 0), 0);
free(name);
if(meta == 0) {
o->oflag = 0x40;
return 0;
}
buf = o->oaddr;
end = buf + len;
while(buf < end) {
name = (char *)buf; /* the compiler's obsession with signed and unsigned is annoying */
buf += strlen(name) + 1;
switch(*buf) {
case 'h':
setmetaint(meta, name, nil, nhgets(buf + 1));
buf += 3;
break;
case 'l':
setmetaint(meta, name, nil, nhgetl(buf + 1));
buf += 5;
break;
case 'v':
setmetaint(meta, name, nil, nhgetv(buf + 1));
buf += 9;
break;
case 's':
setmetastr(meta, name, nil, (char *)(buf + 1), 0);
buf += strlen((char *)(buf + 1)) + 2;
break;
case 'b':
n = *((ushort *)(buf + 1));
setmetablob(meta, name, nil, buf + 3, n, 0);
buf += n + 1;
break;
}
}
return 0;
}
static int
serveosd(Ioproc *io, uchar *pkt, int fd, int)
{
Qid nqid;
Aoehdr *ah;
Aoeosd *o;
uchar *buf;
char *name;
uvlong x;
uvlong pid, oid, addr, meta, pmeta, dirblk, pqpath;
int n, len, rlen;
ah = (Aoehdr *)pkt;
o = (Aoeosd *)(pkt + Aoehsz);
len = nhgets(o->olen);
/* for some commands, the pid, oid, or addr may be junk */
pid = nhgetv(o->opid);
oid = nhgetv(o->ooid);
addr = nhgetv(o->oaddr);
rlen = 0;
o->oflag = 0;
fprint(2, "OSD request: %016ullx:%016ullx len:%d cmd:%x addr:%ulld\n", pid, oid, len, o->ocmd, addr);
switch(o->ocmd) {
case AOCformat:
name = smprint("0000000000000000:0000000000000000");
nqid.path = p2q(-1, name, 1);
nqid.vers = 0;
nqid.type = QTFILE;
meta = q2m(-1, nqid.path, 1);
setmetastr(meta, "name", nil, name, 0);
setmetaint(meta, "pid", nil, 0);
setmetaint(meta, "oid", nil, 0);
setmetaint(meta, "qpath", nil, nqid.path);
setmetaint(meta, "qvers", nil, nqid.vers);
setmetaint(meta, "qtype", nil, nqid.type);
setmetaint(meta, "child", nil, 0);
setqhash(nqid.path, meta);
free(name);
savesuper();
break;
case AOCcreate:
name = smprint("%016ullx:0000000000000000", pid);
pqpath = p2q(-1, name, 0);
pmeta = q2m(-1, pqpath, 0);
free(name);
if(pmeta == 0) {
o->oflag = 0x40;
break;
}
name = smprint("%016ullx:%016ullx", pid, oid);
nqid.path = p2q(-1, name, 1);
nqid.vers = 0;
nqid.type = QTFILE;
meta = q2m(-1, nqid.path, 1);
if(meta == 0) {
o->oflag = 0x40;
free(name);
break;
}
setmetastr(meta, "name", nil, name, 0);
setmetaint(meta, "pid", nil, pid);
setmetaint(meta, "oid", nil, oid);
setmetaint(meta, "qpath", nil, nqid.path);
setmetaint(meta, "qvers", nil, nqid.vers);
setmetaint(meta, "qtype", nil, nqid.type);
setmetaint(meta, "length", nil, 0);
setmetaint(meta, "parent", nil, pqpath);
getmetaint(-1, pmeta, "child", &x);
setmetaint(meta, "sib", nil, x);
setmetaint(pmeta, "child", nil, nqid.path);
dirblk = allocblock();
if(dirblk != 0) {
cbclean(dirblk);
cbwrite(dirblk);
brelease(dirblk);
}
setmetaint(meta, "index", nil, dirblk);
setqhash(nqid.path, meta);
free(name);
savesuper();
break;
case AOClist:
name = smprint("%016ullx:%016ullx", pid, oid);
pqpath = p2q(-1, name, 0);
meta = q2m(-1, pqpath, 0);
getmetaint(-1, meta, "child", &pqpath);
buf = o->opid;
while(len > 0 && pqpath != 0) {
meta = q2m(-1, pqpath, 0);
if(meta == 0)
break;
if(pid == 0)
getmetaint(-1, meta, "pid", &x);
else
getmetaint(-1, meta, "oid", &x);
hnputv(buf, x);
buf += 8;
len -= 8;
getmetaint(-1, meta, "sib", &pqpath);
}
rlen = len = buf - o->opid;
hnputs(o->olen, len);
break;
case AOCread:
name = smprint("%016ullx:%016ullx", pid, oid);
pqpath = p2q(-1, name, 0);
buf = o->opid;
len = θpread(-1, pqpath, buf, len, addr);
rlen = len;
free(name);
break;
case AOCwrite:
name = smprint("%016ullx:%016ullx", pid, oid);
pqpath = p2q(-1, name, 0);
buf = o->oaddr + 8;
len = θpwrite(pqpath, buf, len, addr, 1);
free(name);
break;
case AOCappend:
name = smprint("%016ullx:%016ullx", pid, oid);
pqpath = p2q(-1, name, 0);
buf = o->oaddr + 8;
len = θpwrite(pqpath, buf, len, 0, 2);
free(name);
break;
case AOCflush:
resetmeta();
csync();
break;
case AOCremove:
name = smprint("%016ullx:%016ullx", pid, oid);
meta = q2m(-1, p2q(-1, name, 0), 0);
if(meta == 0) {
o->oflag = 0x40;
free(name);
break;
}
getmetaint(-1, meta, "qpath", &x);
rmdlist(meta, x);
rmq(x, meta);
rmp(name);
free(name);
break;
case AOCpcreate:
name = smprint("0000000000000000:0000000000000000");
pqpath = p2q(-1, name, 0);
pmeta = q2m(-1, pqpath, 0);
free(name);
if(pmeta == 0) {
o->oflag = 0x40;
break;
}
name = smprint("%016ullx:0000000000000000", pid);
nqid.path = p2q(-1, name, 1);
nqid.vers = 0;
nqid.type = QTFILE;
meta = q2m(-1, nqid.path, 1);
if(meta == 0) {
o->oflag = 0x40;
free(name);
break;
}
setmetastr(meta, "name", nil, name, 0);
setmetaint(meta, "pid", nil, pid);
setmetaint(meta, "oid", nil, 0);
setmetaint(meta, "qpath", nil, nqid.path);
setmetaint(meta, "qvers", nil, nqid.vers);
setmetaint(meta, "qtype", nil, nqid.type);
setmetaint(meta, "parent", nil, pqpath);
setmetaint(meta, "child", nil, 0);
getmetaint(-1, pmeta, "child", &x);
setmetaint(meta, "sib", nil, x);
setmetaint(pmeta, "child", nil, nqid.path);
setqhash(nqid.path, meta);
free(name);
savesuper();
break;
case AOCpremove:
name = smprint("%016ullx:0000000000000000", pid);
meta = q2m(-1, p2q(-1, name, 0), 0);
if(meta == 0) {
o->oflag = 0x40;
free(name);
break;
}
getmetaint(-1, meta, "child", &x);
if(x == 0) {
rmdlist(meta, x);
rmq(x, meta);
freeblock(meta);
rmp(name);
}
else {
o->oflag |= 0x40;
}
free(name);
break;
case AOCgetattr:
rlen = len = osdgetattr(o, len, pid, oid);
break;
case AOCsetattr:
rlen = len = osdsetattr(o, len, pid, oid);
break;
case AOCccreate:
case AOCcremove:
case AOCclist:
default:
o->oflag = 0x40;
break;
}
memmove(ah->dst, ah->src, Eaddrlen);
ah->verflag |= AFrsp;
if(o->oflag & 0x40) {
ah->verflag |= AFerr;
ah->error = AEarg;
}
o->oflag |= 0x80;
hnputs(o->olen, len);
n = rlen + 4 + sizeof(Aoehdr);
if(n < 60)
n = 60;
if(iowrite(io, fd, pkt, n) != n) {
fprint(2, "response write failed: %r\n");
return -1;
}
return 0;
}
static int
bladereply(Vblade *v, int i, int fd, uchar *pkt)
{
int n;
Aoehdr *h;
h = (Aoehdr*)pkt;
switch(h->cmd){
case ACata:
n = serveata(pkt, v, mtutab[i]);
n += Aoehsz+Aoeatasz;
break;
case ACconfig:
n = serveconfig(pkt, v, mtutab[i]);
if(n >= 0)
n += Aoehsz+Aoecfgsz;
break;
case ACmask:
n = servemask(pkt, v, mtutab[i]);
break;
case ACosd:
if(v == vblade)
return serveosd(ioprocs[i], pkt, fd, mtutab[i]);
else
return 0;
break;
default:
n = servebad(pkt, v, mtutab[i]);
break;
}
if(n == -1)
return -1;
replyhdr(h, v);
if(n < 60){
memset(pkt+n, 0, 60-n);
n = 60;
}
if(iowrite(ioprocs[i], fd, h, n) != n){
fprint(2, "vblade: write to %s failed: %r\n", ethertab[i]);
return -1;
}
return 0;
}
static int
filter(Vblade *v, uchar *ea)
{
int i;
uchar *u;
if(v->nmask == 0)
return 0;
u = v->mask;
for(i = 0; i < v->nmask; i++)
if(memcmp(u + i*Eaddrlen, ea, Eaddrlen) == 0)
return 0;
return -1;
}
static void
serve(void *a)
{
int i, j, popcnt, vec, n, s, efd;
uchar *pkt, *bcpkt;
Aoehdr *h;
Vblade *v;
i = (int)(uintptr)a;
efd = efdtab[i*Flast+Fdata];
pkt = pkttab[i];
bcpkt = bctab[i];
n = 60;
h = (Aoehdr*)pkt;
bcastpkt(pkt, 0xffff, 0xff, i);
goto start;
for(;;){
n = ioread(ioprocs[i], efd, pkt, Maxpkt);
start:
if(shutdown)
threadexits(nil);
if(n < 60 || h->verflag & AFrsp)
continue;
s = nhgets(h->major);
popcnt = 0;
vec = 0;
for(j = 0; j < nblade; j++){
v = vblade+j;
if(v->shelf == s || s == 0xffff)
if(v->slot == h->minor || h->minor == 0xff)
if(v->nmask == 0 || filter(v, h->src) == 0){
popcnt++;
vec |= 1<<j;
}
}
if(popcnt == 0)
continue;
for(j = 0;; j++){
if((vec & 1<<j) == 0)
continue;
if(--popcnt>0){
memcpy(bcpkt, pkt, n);
bladereply(vblade + j, i, efd, bcpkt);
}else{
bladereply(vblade + j, i, efd, pkt);
break;
}
}
}
}
static void
aoeannounce(Vblade *vb)
{
uchar *pkt;
int i;
pkt = θmalloc(Maxpkt);
for(i = 0; i < etheridx; ++i) {
bcastpkt(pkt, 0xffff, 0xff, i);
bladereply(vb, i, efdtab[i*Flast+Fdata], pkt);
}
}
void
starttarget(int major, int minor, uvlong nsect)
{
Vblade *vp;
vp = vblade + nblade;
vp->maxlba = nsect;
vp->nmask = 0;
vp->mask = nil;
vp->shelf = major;
vp->slot = minor;
vp->clen = 0;
++nblade;
aoeannounce(vp);
}
void
rmtarget(int major, int minor)
{
int i;
for(i = 0; i < nblade && (vblade[i].shelf != major || vblade[i].slot != minor); ++i) ;
if(i >= nblade)
return;
for(; i < nblade - 1; ++i)
vblade[i] = vblade[i+1];
memset(vblade + i, 0, sizeof (Vblade));
--nblade;
}
static void
scanluns(void)
{
uvlong x;
uvlong qpath, meta;
for(qpath = super.firstlun; qpath; ) {
meta = q2m(-1, qpath, 0);
if(meta == 0) {
fprint(2, "No metadata for %ulld\n", qpath);
break;
}
getmetaint(-1, meta, "length", &x);
vblade[nblade].maxlba = x >> 9;
if(getmetaint(-1, meta, "nmask", &x) == MTnone)
vblade[nblade].nmask = 0;
else
vblade[nblade].nmask = x;
if(vblade[nblade].nmask != 0) {
if(getmeta(-1, meta, "masks", (MVal *)&x) == MTnone)
vblade[nblade].nmask = 0;
else
vblade[nblade].mask = getblob(-1, x, nil);
}
getmetaint(-1, meta, "aoemajor", &x);
vblade[nblade].shelf = x;
getmetaint(-1, meta, "aoeminor", &x);
vblade[nblade].slot = x;
if(vblade[nblade].config = getmetastr(-1, meta, "config"))
vblade[nblade].clen = strlen(vblade[nblade].config);
else
vblade[nblade].clen = 0;
++nblade;
getmetaint(-1, meta, "nextlun", &qpath);
}
}
static void
launch(char *tab[], int fdtab[])
{
int i;
for(i = 0; tab[i]; i++){
ioprocs[i] = ioproc();
if(aoeopen(ioprocs[i], tab[i], fdtab+Flast*i) < 0)
sysfatal("network open: %r");
threadcreate(serve, (void*)(uintptr)i, 32*1024);
}
}
void
initaoe(void)
{
int i;
for(i = 0; i < etheridx; i++)
mtutab[i] = getmtu(ethertab[i]);
scanluns();
launch(ethertab, efdtab);
}
void
haltaoe(void)
{
int i;
for(i = 0; ethertab[i]; ++i) {
if(ioprocs[i]) {
iointerrupt(ioprocs[i]);
closeioproc(ioprocs[i]);
}
}
}
|