/* cached-worm file server */
#include "all.h"
#include "io.h"
#include "9p1.h"
extern int oldcachefmt;
Map *devmap;
Biobuf bin;
void
machinit(void)
{
active.exiting = 0;
}
/*
* Put a string on the console.
*/
void
puts(char *s, int n)
{
print("%.*s", n, s);
}
void
prflush(void)
{
}
/*
* Print a string on the console.
*/
void
putstrn(char *str, int n)
{
puts(str, n);
}
/*
* get a character from the console
*/
int
getc(void)
{
return Bgetrune(&bin);
}
void
panic(char *fmt, ...)
{
int n;
va_list arg;
char buf[PRINTSIZE];
va_start(arg, fmt);
n = vseprint(buf, buf + sizeof buf, fmt, arg) - buf;
va_end(arg);
buf[n] = '\0';
print("panic: %s\n", buf);
exit();
}
int
okay(char *quest)
{
char *ln;
print("okay to %s? ", quest);
if ((ln = Brdline(&bin, '\n')) == nil)
return 0;
ln[Blinelen(&bin)-1] = '\0';
if (isascii(*ln) && isupper(*ln))
*ln = tolower(*ln);
return *ln == 'y';
}
static void
mapinit(char *mapfile)
{
int nf;
char *ln;
char *fields[2];
Biobuf *bp;
Map *map;
if (mapfile == nil)
return;
bp = Bopen(mapfile, OREAD);
if (bp == nil)
sysfatal("can't read %s", mapfile);
devmap = nil;
while ((ln = Brdline(bp, '\n')) != nil) {
ln[Blinelen(bp)-1] = '\0';
if (*ln == '\0' || *ln == '#')
continue;
nf = tokenize(ln, fields, nelem(fields));
if (nf != 2)
continue;
if(testconfig(fields[0]) != 0) {
print("bad `from' device %s in %s\n",
fields[0], mapfile);
continue;
}
map = malloc(sizeof *map);
map->from = strdup(fields[0]);
map->to = strdup(fields[1]);
map->fdev = iconfig(fields[0]);
map->tdev = nil;
if (access(map->to, AEXIST) < 0) {
/*
* map->to isn't an existing file, so it had better be
* a config string for a device.
*/
if(testconfig(fields[1]) == 0)
map->tdev = iconfig(fields[1]);
}
/* else map->to is the replacement file name */
map->next = devmap;
devmap = map;
}
Bterm(bp);
}
static void
confinit(void)
{
conf.nmach = 1;
conf.mem = meminit();
conf.nuid = 1000;
conf.nserve = 15; /* tunable */
conf.nfile = 30000;
conf.nlgmsg = 100;
conf.nsmmsg = 500;
localconfinit();
conf.nwpath = conf.nfile*8;
conf.nauth = conf.nfile/10;
conf.gidspace = conf.nuid*3;
cons.flags = 0;
if (conf.devmap)
mapinit(conf.devmap);
}
/*
* compute BUFSIZE*(NDBLOCK+INDPERBUF+INDPERBUF+INDPERBUF+INDPERBUF⁴)
* while watching for overflow; in that case, return 0.
*/
static uvlong
adduvlongov(uvlong a, uvlong b)
{
uvlong r = a + b;
if (r < a || r < b)
return 0;
return r;
}
static uvlong
muluvlongov(uvlong a, uvlong b)
{
uvlong r = a * b;
if (a != 0 && r/a != b || r < a || r < b)
return 0;
return r;
}
static uvlong
maxsize(void)
{
int i;
uvlong max = NDBLOCK, ind = 1;
for (i = 0; i < NIBLOCK; i++) {
ind = muluvlongov(ind, INDPERBUF); /* power of INDPERBUF */
if (ind == 0)
return 0;
max = adduvlongov(max, ind);
if (max == 0)
return 0;
}
return muluvlongov(max, BUFSIZE);
}
enum {
INDPERBUF = ((uvlong)INDPERBUF *INDPERBUF),
INDPERBUF⁴ = ((uvlong)INDPERBUF*INDPERBUF),
};
static void
printsizes(void)
{
uvlong max = maxsize();
print("\tblock size = %d; ", RBUFSIZE);
if (max == 0)
print("max file size exceeds 2⁶⁴ bytes\n");
else {
uvlong offlim = 1ULL << (sizeof(Off)*8 - 1);
if (max >= offlim)
max = offlim - 1;
print("max file size = %,llud\n", (Wideoff)max);
}
if (INDPERBUF/INDPERBUF != INDPERBUF)
print("overflow computing INDPERBUF\n");
if (INDPERBUF⁴/INDPERBUF != INDPERBUF)
print("overflow computing INDPERBUF⁴\n");
print("\tINDPERBUF = %d, INDPERBUF^4 = %,lld, ", INDPERBUF,
(Wideoff)INDPERBUF⁴);
print("CEPERBK = %d\n", CEPERBK);
print("\tsizeofs: Dentry = %d, Cache = %d\n",
sizeof(Dentry), sizeof(Cache));
}
void
usage(void)
{
fprint(2, "usage: %s [-cf][-a ann-str][-m dev-map] config-dev\n",
argv0);
exits("usage");
}
void
main(int argc, char **argv)
{
int i, nets = 0;
char *ann;
rfork(RFNOTEG);
formatinit();
machinit();
conf.confdev = "n"; /* Devnone */
ARGBEGIN{
case 'a': /* announce on this net */
ann = EARGF(usage());
if (nets >= Maxnets) {
fprint(2, "%s: too many networks to announce: %s\n",
argv0, ann);
exits("too many nets");
}
annstrs[nets++] = ann;
break;
case 'c': /* use new, faster cache layout */
oldcachefmt = 0;
break;
case 'f': /* enter configuration mode first */
conf.configfirst++;
break;
case 'm': /* name device-map file */
conf.devmap = EARGF(usage());
break;
default:
usage();
break;
}ARGEND
if (argc != 1)
usage();
conf.confdev = argv[0]; /* config string for dev holding full config */
Binit(&bin, 0, OREAD);
confinit();
print("\nPlan 9 %d-bit cached-worm file server with %d-deep indir blks\n",
sizeof(Off)*8 - 1, NIBLOCK);
printsizes();
qlock(&reflock);
qunlock(&reflock);
serveq = newqueue(1000, "9P service"); /* tunable */
raheadq = newqueue(1000, "readahead"); /* tunable */
mbinit();
netinit();
scsiinit();
files = malloc(conf.nfile * sizeof *files);
for(i=0; i < conf.nfile; i++) {
qlock(&files[i]);
qunlock(&files[i]);
}
wpaths = malloc(conf.nwpath * sizeof(*wpaths));
uid = malloc(conf.nuid * sizeof(*uid));
gidspace = malloc(conf.gidspace * sizeof(*gidspace));
authinit();
print("iobufinit\n");
iobufinit();
arginit();
boottime = time(nil);
print("sysinit\n");
sysinit();
/*
* Ethernet i/o processes
*/
netstart();
/*
* read ahead processes
*/
newproc(rahead, 0, "rah");
/*
* server processes
*/
for(i=0; i < conf.nserve; i++)
newproc(serve, 0, "srv");
/*
* worm "dump" copy process
*/
newproc(wormcopy, 0, "wcp");
/*
* processes to read the console
*/
consserve();
/*
* "sync" copy process
* this doesn't return.
*/
procsetname("scp");
synccopy();
}
/*
* read ahead processes.
* read message from q and then
* read the device.
*/
int
rbcmp(void *va, void *vb)
{
Rabuf *ra, *rb;
ra = *(Rabuf**)va;
rb = *(Rabuf**)vb;
if(rb == 0)
return 1;
if(ra == 0)
return -1;
if(ra->dev > rb->dev)
return 1;
if(ra->dev < rb->dev)
return -1;
if(ra->addr > rb->addr)
return 1;
if(ra->addr < rb->addr)
return -1;
return 0;
}
void
rahead(void *)
{
Rabuf *rb[50];
Iobuf *p;
int i, n;
for (;;) {
rb[0] = fs_recv(raheadq, 0);
for(n = 1; n < nelem(rb); n++) {
if(raheadq->count <= 0)
break;
rb[n] = fs_recv(raheadq, 0);
}
qsort(rb, n, sizeof rb[0], rbcmp);
for(i = 0; i < n; i++) {
if(rb[i] == 0)
continue;
p = getbuf(rb[i]->dev, rb[i]->addr, Brd);
if(p)
putbuf(p);
lock(&rabuflock);
rb[i]->link = rabuffree;
rabuffree = rb[i];
unlock(&rabuflock);
}
}
}
/*
* main filesystem server loop.
* entered by many processes.
* they wait for message buffers and
* then process them.
*/
void
serve(void *)
{
int i;
Chan *cp;
Msgbuf *mb;
for (;;) {
qlock(&reflock);
/* read 9P request from a network input process */
mb = fs_recv(serveq, 0);
assert(mb->magic == Mbmagic);
/* fs kernel sets chan in /sys/src/fs/ip/il.c:/^getchan */
cp = mb->chan;
if (cp == nil)
panic("serve: nil mb->chan");
rlock(&cp->reflock);
qunlock(&reflock);
rlock(&mainlock);
if (mb->data == nil)
panic("serve: nil mb->data");
/* better sniffing code in /sys/src/cmd/disk/kfs/9p12.c */
if(cp->protocol == nil){
/* do we recognise the protocol in this packet? */
/* better sniffing code: /sys/src/cmd/disk/kfs/9p12.c */
for(i = 0; fsprotocol[i] != nil; i++)
if(fsprotocol[i](mb) != 0) {
cp->protocol = fsprotocol[i];
break;
}
if(cp->protocol == nil){
print("no protocol for message\n");
for(i = 0; i < 12; i++)
print(" %2.2uX", mb->data[i]);
print("\n");
}
} else
/* process the request, generate an answer and reply */
cp->protocol(mb);
mbfree(mb);
runlock(&mainlock);
runlock(&cp->reflock);
}
}
void
exit(void)
{
lock(&active);
active.exiting = 1;
unlock(&active);
print("halted at %T.\n", time(nil));
postnote(PNGROUP, getpid(), "die");
exits(nil);
}
enum {
DUMPTIME = 5, /* 5 am */
WEEKMASK = 0, /* every day (1=sun, 2=mon, 4=tue, etc.) */
};
/*
* calculate the next dump time.
* minimum delay is 100 minutes.
*/
Timet
nextdump(Timet t)
{
Timet nddate = nextime(t+MINUTE(100), DUMPTIME, WEEKMASK);
if(!conf.nodump)
print("next dump at %T\n", nddate);
return nddate;
}
/*
* process to copy dump blocks from
* cache to worm. it runs flat out when
* it gets work, but only looks for
* work every 10 seconds.
*/
void
wormcopy(void *)
{
int f, dorecalc = 1;
Timet dt, t = 0, nddate = 0, ntoytime = 0;
Filsys *fs;
for (;;) {
if (dorecalc) {
dorecalc = 0;
t = time(nil);
nddate = nextdump(t); /* chatters */
ntoytime = time(nil);
}
dt = time(nil) - t;
if(dt < 0 || dt > MINUTE(100)) {
if(dt < 0)
print("time went back\n");
else
print("time jumped ahead\n");
dorecalc = 1;
continue;
}
t += dt;
f = 0;
if(t > ntoytime)
ntoytime = time(nil) + HOUR(1);
else if(t > nddate) {
if(!conf.nodump) {
print("automatic dump %T\n", t);
for(fs=filsys; fs->name; fs++)
if(fs->dev->type == Devcw)
cfsdump(fs);
}
dorecalc = 1;
} else {
rlock(&mainlock);
for(fs=filsys; fs->name; fs++)
if(fs->dev->type == Devcw)
f |= dumpblock(fs->dev);
runlock(&mainlock);
if(!f)
delay(10000);
wormprobe();
}
}
}
/*
* process to synch blocks
* it puts out a block/cache-line every second
* it waits 10 seconds if caught up.
* in both cases, it takes about 10 seconds
* to get up-to-date.
*/
void
synccopy(void)
{
int f;
for (;;) {
rlock(&mainlock);
f = syncblock();
runlock(&mainlock);
if(!f)
delay(10000);
else
delay(1000);
}
}
Devsize
inqsize(char *file)
{
int nf;
char *ln, *end, *data = malloc(strlen(file) + 5 + 1);
char *fields[4];
Devsize rv = -1;
Biobuf *bp;
strcpy(data, file);
end = strstr(data, "/data");
if (end == nil)
strcat(data, "/ctl");
else
strcpy(end, "/ctl");
bp = Bopen(data, OREAD);
if (bp) {
while (rv < 0 && (ln = Brdline(bp, '\n')) != nil) {
ln[Blinelen(bp)-1] = '\0';
nf = tokenize(ln, fields, nelem(fields));
if (nf == 3 && strcmp(fields[0], "geometry") == 0)
rv = atoi(fields[2]);
}
Bterm(bp);
}
free(data);
return rv;
}
|