Plan 9 from Bell Labs’s /usr/web/sources/contrib/blstuart/θfs/aoe.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


/*
 * 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]);
		}
	}
}

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to [email protected].