new version of creepy. the main change is new code for the console, which accepts multiple users now. most of the diffs are because I relocated code to better places. others are fixes and more checks performed. Due to one of the new checks, the disk format has changed a little bit to include a block type. Reference: /n/patches.lsub.org/patch/creepcons Date: Tue May 15 18:12:24 CES 2012 Signed-off-by: nemo@lsub.org # rm /sys/src/cmd/creepy/cfg.c # rm /sys/src/cmd/creepy/disk # rm /sys/src/cmd/creepy/disk1 # rm /sys/src/cmd/creepy/fcheck --- /sys/src/cmd/creepy/9p.c Mon May 14 11:06:19 2012 +++ /sys/src/cmd/creepy/9p.c Tue May 15 17:14:41 2012 @@ -115,8 +115,8 @@ break; if(r != nil){ r->flushed = 1; - if(r->t.type == Tread && r->fid->consopen) - consprint(""); /* in case it's waiting... */ + if(r->t.type == Tread && r->fid->consrc != nil) + nbsendp(r->fid->consrc, nil); } xqunlock(&rpc->cli->rpclk); xqunlock(&cli->wlk); @@ -640,23 +640,26 @@ errinit(Errstack); *aux = v; /* make it not nil */ } + if(catcherror()) + fatal("uncatched: %r"); nerr = errstacksize(); + fspolicy(); rpc->r.tag = rpc->t.tag; rpc->r.type = rpc->t.type + 1; - xrlock(&fs->quiescence); + xrlock(&fs->dquiescence); if(catcherror()){ - xrunlock(&fs->quiescence); + xrunlock(&fs->dquiescence); rpc->r.type = Rerror; rpc->r.ename = err; rerrstr(err, sizeof err); }else{ - if(fs->halt != 0) + if(fs->halt.ref != 0) error("file system halted"); fcalls[rpc->t.type](rpc); - xrunlock(&fs->quiescence); + xrunlock(&fs->dquiescence); noerror(); } @@ -689,9 +692,10 @@ ncalls[rpc->t.type]++; xqunlock(&cli->wlk); - if(fs->halt == Halted) /* this is for testing */ - threadexitsall(nil); /* halt terminates all processes */ - + if(fs->halt.ref > 0 && decref(&fs->halt) == 0){ + warn("halted"); + threadexitsall(nil); + } if(fahead != nil){ if(rpc->r.type == Rread) rahead(fahead, rpc->t.offset + rpc->r.count); @@ -706,6 +710,7 @@ if(errstacksize() != nerr) fatal("%s: unbalanced error stack", threadgetname()); + noerror(); threadsetname("rpcworker9p"); return nil; } --- /sys/src/cmd/creepy/9pix.c Mon May 14 11:06:19 2012 +++ /sys/src/cmd/creepy/9pix.c Tue May 15 17:14:41 2012 @@ -46,6 +46,7 @@ default: if(ARGC() >= 'A' && ARGC() <= 'Z' || ARGC() == '9'){ dbg[ARGC()] = 1; + dbg['d'] = 1; fatalaborts = 1; }else usage(); @@ -84,8 +85,9 @@ if(addr != nil) listen9pix(addr, noauth, cliworker9p); - consinit(); + consinit("creepy> "); noerror(); + errterm(); threadexits(nil); } --- /sys/src/cmd/creepy/arch.c Fri May 11 13:44:47 2012 +++ /sys/src/cmd/creepy/arch.c Tue May 15 17:14:42 2012 @@ -66,7 +66,7 @@ error(nil); } for(tot = 0;; tot += sl.len){ - sl = dfslice(f, Dblksz, tot, Rd); + sl = dfslice(f, Dblkdatasz, tot, Rd); if(sl.len == 0){ assert(sl.b == nil); break; @@ -132,9 +132,9 @@ return; } f = dbget(DBfile, addr); + noerror(); xrlock(f->mf); dVprint("archdent %N\n", f->mf->name); - noerror(); if(catcherror()){ warn("archdent: %N: %r", f->mf->name); xrunlock(f->mf); @@ -166,6 +166,7 @@ mbput(f); } +/* don't raise errors here */ static void archrm(char *name) { @@ -218,17 +219,17 @@ archrm(d[i].name); }else{ c = dfwalk(b, d[i].name); + noerror(); if(c->type == DBdir && (d[i].mode&DMDIR) == 0) archrm(d[i].name); if(c->type == DBfile && (d[i].mode&DMDIR) != 0) archrm(d[i].name); mbput(c); - noerror(); } } /* archive new or modified files */ for(off = 0;; off += sl.len){ - sl = dfslice(b, Dblksz, off, Rd); + sl = dfslice(b, Dblkdatasz, off, Rd); if(sl.len == 0) break; if(sl.b == nil) @@ -276,7 +277,7 @@ int fd; char c[128]; - if(strlen(last) < strlen(fs->archdir) || strlen(arch) < strlen(fs->archdir)) + if(strlen(last)archdir) || strlen(arch)archdir)) error("bad arch path"); last += strlen(fs->archdir); arch += strlen(fs->archdir); @@ -285,8 +286,10 @@ fd = open(c, OWRITE); if(fd < 0) error("cons: %r"); - if(write(fd, "sync\n", 5) != 5) + if(write(fd, "sync\n", 5) != 5){ + close(fd); error("archive sync: %r"); + } seprint(c, c+sizeof c, "link %s %s\n", last, arch); if(write(fd, c, strlen(c)) != strlen(c)){ close(fd); @@ -337,15 +340,11 @@ daddrt addr; Memblk *b; - xrlock(&fs->dquiescence); - xqlock(&fs->archlk); dVprint("fsarchive...\n"); xqlock(&fs->superlk); addr = fs->super->d.root; xqunlock(&fs->superlk); if(catcherror()){ - xqunlock(&fs->archlk); - xrunlock(&fs->dquiescence); warn("archive: %r"); return; } @@ -354,15 +353,13 @@ b = dbget(DBdir, addr); xrlock(b->mf); - if(b->state != MBclean){ - xrunlock(b->mf); - error("not synced"); - } if(catcherror()){ xrunlock(b->mf); mbput(b); error(nil); } + if(b->state != MBclean) + error("not synced"); archdir(b); @@ -371,6 +368,4 @@ mbput(b); noerror(); dVprint("fsarchive: done\n"); - xqunlock(&fs->archlk); - xrunlock(&fs->dquiescence); } --- /sys/src/cmd/creepy/attr.c Mon May 14 11:06:19 2012 +++ /sys/src/cmd/creepy/attr.c Tue May 15 17:14:43 2012 @@ -95,7 +95,7 @@ static ulong metasize(Memblk *f) { - return strlen(f->mf->name) + 1; + return strlen(f->mf->name) + 1; /* +1 for \0, also stored. */ } /* --- /sys/src/cmd/creepy/cmd.c Mon May 14 11:06:19 2012 +++ /sys/src/cmd/creepy/cmd.c Tue May 15 17:14:43 2012 @@ -423,6 +423,7 @@ if(verb>1) fsdump(0, Mem); noerror(); + errterm(); threadexitsall(nil); } --- /sys/src/cmd/creepy/conf.h Mon May 14 11:06:19 2012 +++ /sys/src/cmd/creepy/conf.h Tue May 15 17:14:43 2012 @@ -29,7 +29,7 @@ Dminfree = 2000, /* # blocks when low on disk blocks */ Dminattrsz = Dblksz/2, /* min size for attributes */ Mmaxdirty = 1000, /* max # of dirty blocks */ - Nahead = 10 * Dblksz, /* # of bytes to read ahead */ + Nahead = 5 * Dblksz, /* # of bytes to read ahead */ /* * Caution: Stack and Errstack also limit the max tree depth, @@ -48,7 +48,7 @@ Nlstats = 1009, /* # of lock profiling entries */ Mmaxfree = 10*Mminfree, /* high on mem blocks */ - Dmaxfree = 2*Dminfree, /* high on disk blocks */ + Dmaxfree = 10*Dminfree, /* high on disk blocks */ Mzerofree = Mminfree/2, /* we think we are out of mem blocks */ Dzerofree = Dminfree/10, /* we think we are out of disk blocks */ @@ -57,6 +57,6 @@ Statsbufsz = 4*KiB, Nscanfree = 16, /* # of times to retry in newdaddr */ - + Nconfids = 16, /* Max # of consoles */ }; --- /sys/src/cmd/creepy/cons.c Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/creepy/cons.c Tue May 15 17:14:44 2012 @@ -0,0 +1,701 @@ +#include "all.h" +/* + * Console and ctl commands. + */ + +static void +csync(int, char**) +{ + fssync(); + consprint("synced\n"); +} + +static void +chalt(int, char**) +{ + fs->halt.ref = 2; /* RPC and sync */ + fssync(); + if(decref(&fs->halt) == 0){ + warn("halted"); + threadexitsall(nil); + } +} + +static void +cusers(int, char *[]) +{ + int i; + Usr *usr; + + xrlock(&fs->Usrs); + for(i = 0; i < fs->uidgen; i++) + if((usr=finduid(i)) != nil) + consprint("%A\n", usr); + xrunlock(&fs->Usrs); +} + +static void +cuname(int, char *argv[]) +{ + Usr *usr; + + xrlock(&fs->Usrs); + usr = finduname(argv[1], Dontmk); + if(usr == nil){ + xrunlock(&fs->Usrs); + error("no such user\n"); + } + consprint("%A\n", usr); + xrunlock(&fs->Usrs); +} + +static void +cstats(int argc, char *argv[]) +{ + int clr, verb; + char *argv0; + + argv0 = argv[0]; + clr = verb = 0; + ARGBEGIN{ + case 'c': + clr = 1; + break; + case 'v': + verb = 1; + break; + default: + error("usage: %s [-cv]\n", argv0); + }ARGEND + if(argc != 0) + error("usage: %s [-cv]\n", argv0); + consprint("%s\n", updatestats(clr, verb)); +} + +static void +cdebug(int argc, char *argv[]) +{ + char *f; + char flags[50]; + int i; + + if(argc != 1 && argc != 2) + error("usage: %s [[+-]FLAGS | on | off]\n", argv[0]); + if(argc == 1){ + f = flags; + for(i = 0; i < 256 && f < flags+sizeof flags -1; i++) + if(dbg[i]) + *f++ = i; + *f = 0; + consprint("debug %s\n", flags); + return; + } + f = argv[1]; + if(strcmp(f, "on") == 0){ + dbg['D'] = 1; + return; + } + if(strcmp(f, "off") == 0){ + memset(dbg, 0, sizeof dbg); + return; + } + if(*f != '+' && *f != '-') + memset(dbg, 0, sizeof dbg); + else + f++; + for(; *f != 0; f++){ + dbg[*f] = 1; + if(*argv[1] == '-') + dbg[*f] = 0; + } + f = flags; + for(i = 0; i < nelem(dbg) && f < flags+nelem(flags)-1; i++) + if(dbg[i]) + *f++ = i; + *f = 0; + consprint("debug %s\n", flags); + +} + +static void +clocks(int, char *argv[]) +{ + if(strcmp(argv[1], "on") == 0) + lockstats(1); + else if(strcmp(argv[1], "off") == 0) + lockstats(0); + else if(strcmp(argv[1], "dump") == 0) + dumplockstats(); + else + error("usage: %s [on|off|dump]\n", argv[0]); +} + +static void +cfids(int, char**) +{ + dumpfids(); +} + +static void +crwerr(int, char *argv[]) +{ + if(*argv[0] == 'r'){ + fs->swreaderr = atoi(argv[1]); + warn("sw read err count = %d", fs->swreaderr); + }else{ + fs->swwriteerr = atoi(argv[1]); + warn("sw write err count = %d", fs->swwriteerr); + } +} + +void +allowuid(int uid) +{ + Usr *usr; + + xwlock(&fs->Usrs); + usr = finduid(uid); + if(usr != nil) + usr->allow = 1; + xwunlock(&fs->Usrs); +} + +static void +callow(int argc, char *argv[]) +{ + Usr *u, *usr; + int i; + + usr = nil; + switch(argc){ + case 1: + if(*argv[0] == 'd') + for(i = 0; i < nelem(fs->uids); i++) + for(u = fs->uids[i]; u != nil; u = u->inext) + u->allow = 0; + break; + case 2: + xwlock(&fs->Usrs); + usr = finduname(argv[1], Dontmk); + if(usr == nil){ + xwunlock(&fs->Usrs); + error("user not found"); + } + usr->allow = (*argv[0] == 'a'); + xwunlock(&fs->Usrs); + break; + default: + error("usage: %s [uid]\n", argv[0]); + } + xrlock(&fs->Usrs); + for(i = 0; i < nelem(fs->uids); i++) + for(u = fs->uids[i]; u != nil; u = u->inext) + if(u->allow) + consprint("user '%s' is allowed\n", u->name); + else if(u == usr) + consprint("user '%s' is not allowed\n", u->name); + xrunlock(&fs->Usrs); +} + +static int +mountrip(void) +{ + int rv, afd; + AuthInfo *ai; + int fd; + + + warn("mounting rip..."); + fd = open("/srv/rip", ORDWR); + if(fd < 0) + return -1; + + afd = fauth(fd, "wormwr"); + if(afd >= 0){ + ai = auth_proxy(afd, amount_getkey, "proto=p9any role=client %s", ""); + if(ai != nil) + auth_freeAI(ai); + else + warn("auth_proxy: %r"); + } + rv = mount(fd, afd, "/n/rip", MREPL|MCREATE, "wormwr"); + if(rv < 0) + close(fd); + if(afd >= 0) + close(afd); + return rv; +} + +static int +canwritearch(char *dir) +{ + char *t; + int fd; + + t = smprint("%s/root/_", dir); + fd = create(t, OWRITE|ORCLOSE, 0664); + if(fd < 0){ + if(strcmp(dir, "/n/rip") == 0){ /* try to mount */ + if(mountrip() < 0){ + free(t); + return -1; + } + fd = create(t, OWRITE|ORCLOSE, 0664); + free(t); + if(fd < 0) + return -1; + close(fd); + return 0; + } + free(t); + return -1; + } + close(fd); + return 0; +} + +static void +carch(int argc, char *argv[]) +{ + int h; + char *path; + + if(fs->mode == Worm) + error("%s allowed only in non-worm mode\n", argv[0]); + path = "/n/rip"; + switch(argc){ + case 1: + if(fs->archdir == nil) + consprint("not archived\n"); + else + consprint("archived at %d:00 %s/root/%s\n", + fs->archhour, fs->archdir, fs->archname); + return; + case 2: + if(strcmp(argv[1], "now") == 0){ + if(fs->archdir == nil){ + consprint("fsys not being archived\n"); + return; + } + consprint("archive scheduled\n"); + fs->archt = time(nil); + return; + }else if(strcmp(argv[1], "none") == 0){ + if(xcanqlock(&fs->archlk) == 0) + error("archive in progress. try again later."); + fs->archt = 0; + fs->archhour = 0; + fs->archdir = nil; /* leaked */ + fs->archname = nil; /* leaked */ + xqunlock(&fs->archlk); + consprint("fsys not being archived\n"); + return; + }else + error("usage: %s [name hour [path] | now | none]", argv[0]); + case 3: + break; + case 4: + path = argv[3]; + break; + default: + error("usage: %s [name hour [path] | now | none]", argv[0]); + } + h = atoi(argv[2]); + if(h < 0 || h >= 24) + error("invalid hour %d\n", h); + if(canwritearch(path) < 0) + error("can't write archive: %r\n"); + if(xcanqlock(&fs->archlk) == 0) + error("archive in progress. try again later."); + fs->archdir = strdup(path); + fs->archname = strdup(argv[1]); + fs->archhour = h; + fs->archt = nextime(time(nil), fs->archhour); + xqunlock(&fs->archlk); + consprint("archived at %d:00 %s/root/%s\n", + fs->archhour, fs->archdir, fs->archname); +} + +static void +cwho(int, char**) +{ + consprintclients(); +} + +static void +clink(int, char *argv[]) +{ + Path *dp, *sp; + Memblk *df, *sf, *nf; + char *nm; + + if(fs->mode != Worm) + error("%s allowed only in worm mode\n", argv[0]); + sp = walkto(argv[1], nil); + if(catcherror()){ + putpath(sp); + error(nil); + } + sf = sp->f[sp->nf-1]; + xrlock(sf->mf); + if(catcherror()){ + xrunlock(sf->mf); + error(nil); + } + xqlock(&sf->slk); + if(sf->state != MBclean){ + xqunlock(&sf->slk); + error("source is not frozen"); + } + xqunlock(&sf->slk); + + dp = walkto(argv[2], &nm); + if(catcherror()){ + putpath(dp); + error(nil); + } + prenew(dp, dp->nf); + df = dp->f[dp->nf-1]; + if(catcherror()){ + xwunlock(df->mf); + error(nil); + } + if(!catcherror()){ + mbput(dfwalk(df, nm)); + noerror(); + error("'%s' already exists", nm); + } + nf = dfcreate(df, nm, sf->d.uid, sf->d.mode); + addelem(&dp, nf); + mbput(nf); + nf->d = sf->d; /* in-place: copy all the pointers and info */ + wname(nf, nm); /* but keep the desired name */ + + noerror(); + xwunlock(df->mf); + + noerror(); + putpath(dp); + + noerror(); + xrunlock(sf->mf); + + noerror(); + putpath(sp); +} + +static void +cmark(int, char*[]) +{ + sendul(fs->sweepc, 1); +} + +static void +ccheck(int, char*[]) +{ + if(fscheck() == 0) + consprint("check passed\n"); + else + consprint("check failed\n"); +} + +static void chelp(int, char**); + +static Cmd cmds[] = +{ + {"allow", callow, 0, "allow [uid]"}, + {"disallow", callow, 0, "disallow [uid]"}, + {"halt", chalt, 1, "halt", 1}, + {"stats", cstats, 0, "stats [-c]"}, + {"sync", csync, 1, "sync", 1}, + {"uname", cuname, 2, "uname uid"}, + {"users", cusers, 1, "users"}, + {"who", cwho, 1, "who"}, + {"arch", carch, 0, "arch [name hour [path] | now | none]", 1}, + {"link", clink, 3, "link file dir", 1}, + /* these will go in the future */ + {"check", ccheck, 1, "check"}, + {"debug", cdebug, 0, "debug [[+-]FLAGS | on | off]"}, + {"fids", cfids, 1, "fids"}, + {"locks", clocks, 2, "locks [on|off|dump]"}, + {"rerr", crwerr, 2, "rerr n"}, + {"werr", crwerr, 2, "werr n"}, + {"mark", cmark, 1, "mark"}, + {"?", chelp, 1, "?"}, +}; + +static void +chelp(int, char**) +{ + int i; + + consprint("commands:\n"); + for(i = 0; i < nelem(cmds); i++) + if(strcmp(cmds[i].name, "?") != 0) + consprint("\t%s\n", cmds[i].usage); +} + +/* + * NB: console commands and ctls do not hold the dquiescence + * rlock, so the fs is not out of quiescence because of them. + * e.g., we should be able to issue a "check" command. + */ +static void +xconsctl(char *p, int raise) +{ + char *args[5]; + int nargs, i; + + nargs = tokenize(p, args, nelem(args)); + if(nargs < 1) + return; + for(i = 0; i < nelem(cmds); i++){ + if(strcmp(args[0], cmds[i].name) != 0) + continue; + xrunlock(&fs->dquiescence); + if(catcherror()){ + xrlock(&fs->dquiescence); + if(raise) + error(nil); + consprint("%r\n"); + break; + } + if(cmds[i].nargs != 0 && cmds[i].nargs != nargs){ + if(raise) + error("usage: %s", cmds[i].usage); + consprint("usage: %s\n", cmds[i].usage); + }else{ + if(raise && cmds[i].isctl == 0) + error("command not available as a ctl"); + cmds[i].f(nargs, args); + } + noerror(); + xrlock(&fs->dquiescence); + break; + } + if(i == nelem(cmds)){ + if(raise) + error("bad ctl"); + consprint("'%s'?\n", args[0]); + } +} + +long +consctl(char *p, long count) +{ + if(count == 0) + return 0; + p[count] = 0; + if(p[count-1] == '\n') + p[count-1] = 0; + xconsctl(p, 1); + return count; +} + +void +consopen(Channel *c) +{ + sendp(fs->consoc, c); +} + +void +consclose(Channel *c) +{ + sendp(fs->conscc, c); +} + +long +consread(Channel *c, char *buf, long count) +{ + char *s; + int nr; + + if(count <= 0) /* shouldn't happen */ + return 0; + xrunlock(&fs->dquiescence); /* fs quiescent while we wait... */ + s = recvp(c); + xrlock(&fs->dquiescence); + if(s == nil) + return 0; + nr = strlen(s); + if(nr > count) + nr = count; + memmove(buf, s, nr); + free(s); + return nr; +} + +long +conswrite(char *ubuf, long count) +{ + char *e; + + ubuf[count] = 0; + dCprint("conswrite [%s]\n", ubuf); + sendp(fs->conswc, strdup(ubuf)); + e = recvp(fs->consec); + if(e != nil){ + werrstr("%s", e); + free(e); + error("%r"); + } + dCprint("conwrite %ld bytes\n", count); + return count; +} + +void +consprint(char *fmt, ...) +{ + va_list arg; + char *s, *x; + + va_start(arg, fmt); + s = vsmprint(fmt, arg); + va_end(arg); + /* consume old messages if the channel is full */ + while(nbsendp(fs->consrc, s) == 0){ + if((x = nbrecvp(fs->consrc)) != nil){ + warn("cons: drop msg: %s", x); + free(x); + } + } +} + +static void +consrproc(void *a) +{ + char *m, *p; + Channel **fidc; + int i, some; + + threadsetname("consrproc"); + errinit(Errstack); + if(catcherror()) + fatal("%s: uncatched: %r", threadgetname()); + fidc = a; + dCprint("consrproc started\n"); + while((m = recvp(fs->consrc)) != nil){ + dCprint("consr: [%s]\n", m); + some = 0; + for(i = 0; i < Nconfids; i++) + if(fidc[i] != nil){ + p = strdup(m); + dCprint("cons: ->%#p\n", fidc[i]); + if(nbsendp(fidc[i], p) == 0) + free(p); + else + some++; + } + if(some == 0) + warn("cons: %s", m); + free(m); + } + dCprint("consrproc exiting\n"); + errterm(); + threadexits(nil); +} + +static void +consproc(void*) +{ + Channel *c; + Channel *fidc[Nconfids]; + char buf[128], *m, *p, *q; + int i, nfidcs; + enum{Oc, Cc, Wc}; + Alt a[] = { + {fs->consoc, &c, CHANRCV}, + {fs->conscc, &c, CHANRCV}, + {fs->conswc, &m, CHANRCV}, + {nil, nil, CHANEND} + }; + + threadsetname("consproc"); + errinit(Errstack); + if(catcherror()) + fatal("%s: uncatched: %r", threadgetname()); + memset(fidc, 0, sizeof fidc); + buf[0] = 0; + nfidcs = 0; + + for(;;){ + c = nil; + m = nil; + switch(alt(a)){ + case Oc: + dCprint("new consc %#p\n", c); + for(i = 0; i < nelem(fidc); i++) + if(fidc[i] == nil) + break; + if(i == nelem(fidc)) + warn("too many consoles"); + else + fidc[i] = c; + if(nfidcs++ == 0) + threadcreate(consrproc, fidc, Stack); + else + consprint("[%d console users]\n", nfidcs); + consprint("%s", fs->prompt); + break; + case Cc: + dCprint("gone consc %#p\n", c); + for(i = 0; i < nelem(fidc); i++) + if(fidc[i] == c) + break; + if(i == nelem(fidc)) + warn("cons read chan not found"); + else + fidc[i] = nil; + if(--nfidcs == 0) + sendp(fs->consrc, nil); + else{ + consprint("[%d console users]\n", nfidcs); + consprint("%s", fs->prompt); + } + break; + case Wc: + dCprint("consw: [%s]\n", m); + consprint("%s", m); /* echo */ + if(catcherror()){ + dCprint("consw: err [%r]\n"); + consprint("%r"); + goto done; + } + if(strlen(buf) + strlen(m) + 1> sizeof buf){ + buf[0] = 0; + error("command too large"); + } + q = buf+strlen(buf); + for(p = m; *p != 0; p++) + if(*p == 0x08){ + if(q > buf) + *--q = 0; + }else + *q++ = *p; + *q = 0; + free(m); + if((p = utfrune(buf, '\n')) == nil) + goto done; + *p = 0; + if((p = utfrune(buf, '#')) != nil) + *p = 0; + dCprint("xconsctl [%s]\n", buf); + xconsctl(buf, 0); + buf[0] = 0; + consprint("%s", fs->prompt); + done: + dCprint("consw: done\n"); + sendp(fs->consec, nil); + break; + } + } +} + +void +consinit(char *prompt) +{ + fs->prompt = strdup(prompt); + consprint("%s", fs->prompt); + proccreate(consproc, nil, Stack); +} + --- /sys/src/cmd/creepy/dbg.c Mon May 14 11:06:19 2012 +++ /sys/src/cmd/creepy/dbg.c Tue May 15 17:14:45 2012 @@ -5,7 +5,73 @@ static char sdbg[256]; static Ref nodbg; -void +char* +mname(int m) +{ + static char *nms[] = { + [Normal] "normal", + [Rdonly] "rdonly", + [Worm] "worm", + }; + + if(m < 0 || m >= nelem(nms)) + return "BADMODE"; + return nms[m]; + +} + +char* +tname(int t) +{ + static char*nms[] = { + [DBfree] "DBfree", + [DBattr] "UNUSED", + [DBsuper] "DBsuper", + [DBtag] "DBtag", + [DBfile] "DBfile", + [DBdata] "DBdata", + [DBptr0] "DBptr0", + [DBptr0+1] "DBptr1", + [DBptr0+2] "DBptr2", + [DBptr0+3] "DBptr3", + [DBptr0+4] "DBptr4", + [DBptr0+5] "DBptr5", + [DBptr0+6] "DBptr6", + [DBdir] "DBdir", + [DBdirdata] "DBdirdata", + [DBdirptr0] "DBdirptr0", + [DBdirptr0+1] "DBdirptr1", + [DBdirptr0+2] "DBdirptr2", + [DBdirptr0+3] "DBdirptr3", + [DBdirptr0+4] "DBdirptr4", + [DBdirptr0+5] "DBdirptr5", + [DBdirptr0+6] "DBdirptr6", + }; + + if(t < 0 || t >= nelem(nms)) + return "BADTYPE"; + return nms[t]; +} + +char* +sname(int s) +{ + static char*nms[] = { + [MBfree] "MBfree", + [MBmem] "MBmem", + [MBout] "MBout", + [MBclean] "MBclean", + [MBin] "MBin", + [MBlru] "MBlru", + [MBerr] "MBerr", + }; + + if(s < 0 || s >= nelem(nms)) + return "BADSTATE"; + return nms[s]; +} + +static void nodebug(void) { incref(&nodbg); @@ -14,32 +80,13 @@ memset(dbg, 0, sizeof dbg); } -void +static void debug(void) { if(decref(&nodbg) == 0) memmove(dbg, sdbg, sizeof dbg); } -int -setdebug(void) -{ - int r; - - r = nodbg.ref; - if(r > 0) - memmove(dbg, sdbg, sizeof dbg); - return r; -} - -void -rlsedebug(int r) -{ - nodbg.ref = r; - if(r > 0) - memset(dbg, 0, sizeof dbg); -} - void fatal(char *fmt, ...) { @@ -125,17 +172,23 @@ daddrt *de; long doff, sz; + if(catcherror()){ + warn("checkblk: %r:\n%H", b); + error(nil); + } + if((b->type&~DBdirflag) != (b->d.type&~DBdirflag)) + error("type mismatch %s != %s", tname(b->type), tname(b->d.type)); type = b->type & ~DBdirflag; switch(type){ case DBfree: case DBattr: - warnerror("%s block on disk", tname(b->type)); + error("%s block on disk", tname(b->type)); break; case DBtag: break; case DBsuper: if(b->d.magic != MAGIC) - warnerror("super: magic"); + error("super: magic"); d1 = &b->d.Dsuperdata; d2 = &b->d.dup; if(b->state == MBin && memcmp(d1, d2, sizeof(Dsuperdata)) != 0){ @@ -144,34 +197,34 @@ b->d.Dsuperdata = b->d.dup; } if(b->d.eaddr > fs->limit || b->d.eaddr < Dblk0addr) - warnerror("super: eaddr out of range"); + error("super: eaddr out of range"); if(b->state != MBmem && !validaddr(b->d.root)) - warnerror("super: root %D out of range", b->d.root); + error("super: root %D out of range", b->d.root); if(b->state == MBmem && !validmaddr(b->d.root)) - warnerror("super: root %D out of range", b->d.root); + error("super: root %D out of range", b->d.root); if(b->d.dblksz != Dblksz) - warnerror("bad Dblksz"); + error("bad Dblksz"); if(b->d.dminattrsz != Dminattrsz) - warnerror("bad Dminattrsz"); + error("bad Dminattrsz"); if(b->d.ndptr != Ndptr) - warnerror("bad ndptr"); + error("bad ndptr"); if(b->d.niptr != Niptr) - warnerror("bad niptr"); + error("bad niptr"); if(b->d.embedsz != Embedsz) - warnerror("bad Embedsz"); + error("bad Embedsz"); if(b->d.dptrperblk != Dptrperblk) - warnerror("bad Dptrperblk"); + error("bad Dptrperblk"); if(b->d.dtagperblk != Dtagperblk) - warnerror("bad Dtagperblk"); + error("bad Dtagperblk"); break; case DBdata: - if(b->type&DBdirflag){ + if(b->d.type&DBdirflag){ de = b->d.ptr; for(i = 0; i < Dptrperblk; i++){ if(b->state != MBmem && !validaddr(de[i])) - warnerror("dir: %D out of range", de[i]); + error("dir: %D out of range", de[i]); if(b->state == MBmem && !validmaddr(de[i])) - warnerror("dir: %D out of range", de[i]); + error("dir: %D out of range", de[i]); } } break; @@ -185,36 +238,36 @@ } for(i = 0; i < nelem(b->d.dptr); i++){ if(b->state != MBmem && !validaddr(b->d.dptr[i])) - warnerror("file: dptr %D out of range", b->d.dptr[i]); + error("file: dptr %D out of range", b->d.dptr[i]); if(b->state == MBmem && !validmaddr(b->d.dptr[i])) - warnerror("file: dptr %D out of range", b->d.dptr[i]); + error("file: dptr %D out of range", b->d.dptr[i]); } for(i = 0; i < nelem(b->d.iptr); i++){ if(b->state != MBmem && !validaddr(b->d.iptr[i])) - warnerror("file: iptr %D out of range", b->d.iptr[i]); + error("file: iptr %D out of range", b->d.iptr[i]); if(b->state == MBmem && !validmaddr(b->d.iptr[i])) - warnerror("file: iptr %D out of range", b->d.iptr[i]); + error("file: iptr %D out of range", b->d.iptr[i]); } /* dbget(DBfile) may load a directory, we don't know by now */ - if((b->type == DBdir) || (b->d.mode&DMDIR) != 0){ + if((b->d.type == DBdir)){ doff = embedattrsz(b); if(doff > Embedsz) - warnerror("file: wrong attr size %ld", doff); + error("file: wrong attr size %ld", doff); sz = Embedsz-doff; de = (daddrt*)(b->d.embed+doff); for(i = 0; i < sz/Daddrsz; i++){ if(b->state != MBmem && !validaddr(de[i])) - warnerror("file: %D out of range", de[i]); + error("file: dent %D out of range", de[i]); if(b->state == MBmem && !validmaddr(de[i])) - warnerror("file: %D out of range", de[i]); + error("file: dent %D out of range", de[i]); } } noerror(); break; default: if(type < DBptr0 || type >= DBptr0 + Niptr) - warnerror("unknown block type %d", b->type); + error("unknown block type %d", b->type); if(catcherror()){ for(i = 0; i < Dptrperblk; i++) b->d.ptr[i] = 0; @@ -222,86 +275,17 @@ } for(i = 0; i < Dptrperblk; i++){ if(b->state != MBmem && !validaddr(b->d.ptr[i])) - warnerror("ptr: %D out of range", b->d.ptr[i]); + error("ptr: %D out of range", b->d.ptr[i]); if(b->state == MBmem && !validmaddr(b->d.ptr[i])) - warnerror("ptr: %D out of range", b->d.ptr[i]); + error("ptr: %D out of range", b->d.ptr[i]); } noerror(); } + noerror(); } -char* -mname(int m) -{ - static char *nms[] = { - [Normal] "normal", - [Rdonly] "rdonly", - [Worm] "worm", - }; - - if(m < 0 || m >= nelem(nms)) - return "BADMODE"; - return nms[m]; - -} - -char* -tname(int t) -{ - static char*nms[] = { - [DBfree] "DBfree", - [DBattr] "UNUSED", - [DBsuper] "DBsuper", - [DBtag] "DBtag", - [DBfile] "DBfile", - [DBdata] "DBdata", - [DBptr0] "DBptr0", - [DBptr0+1] "DBptr1", - [DBptr0+2] "DBptr2", - [DBptr0+3] "DBptr3", - [DBptr0+4] "DBptr4", - [DBptr0+5] "DBptr5", - [DBptr0+6] "DBptr6", - [DBdir] "DBdir", - [DBdirdata] "DBdirdata", - [DBdirptr0] "DBdirptr0", - [DBdirptr0+1] "DBdirptr1", - [DBdirptr0+2] "DBdirptr2", - [DBdirptr0+3] "DBdirptr3", - [DBdirptr0+4] "DBdirptr4", - [DBdirptr0+5] "DBdirptr5", - [DBdirptr0+6] "DBdirptr6", - }; - - if(t < 0 || t >= nelem(nms)) - return "BADTYPE"; - return nms[t]; -} - -char* -sname(int s) -{ - static char*nms[] = { - [MBfree] "MBfree", - [MBmem] "MBmem", - [MBout] "MBout", - [MBclean] "MBclean", - [MBin] "MBin", - [MBlru] "MBlru", - [MBerr] "MBerr", - }; - - if(s < 0 || s >= nelem(nms)) - return "BADSTATE"; - return nms[s]; -} - -int fullfiledumps = 0; - -/* - * These are only for debug. - */ -int mbtab; +static int fullfiledumps; +static int mbtab; static void fmttab(Fmt *fmt, int t, int c) @@ -375,6 +359,21 @@ return fmtprint(fmt, "%s/%s", buf, p); } +/* debug only; no locks */ +static u64int +dbgettag(daddrt addr) +{ + int idx; + Memblk *tb; + u64int e; + + tb = dbget(DBtag, tagaddr(addr, &idx)); + e = tb->d.tag[idx]; + if(decref(tb) == 0) /* mbput would lock tb */ + fatal("dbgettag bug"); + return e; +} + int mbfmt(Fmt *fmt) { @@ -390,14 +389,19 @@ fmttab(fmt, mbtab, type == DBfile); fmtprint(fmt, "%s", tname(b->type)); + if(b->type != b->d.type) + fmtprint(fmt, " != %s", tname(b->d.type)); fmtprint(fmt, " %s", sname(b->state)); if(type == DBfile && b->mf != nil) fmtprint(fmt, " '%s'", b->mf->name); fmtprint(fmt, " %D", b->addr); e = 0; if(fs->mode != Worm && type != DBtag && type != DBsuper && ISDADDR(b->addr)) - e = dbgettag(b->addr); - fmtprint(fmt, " r=%d e=%ulld", b->ref, e); + if(!catcherror()){ + e = dbgettag(b->addr); + noerror(); + } + fmtprint(fmt, " r=%d e=%ulld %D", b->ref, e, MKMADDR(b)); switch(type){ case DBfree: @@ -407,6 +411,8 @@ break; case DBtag: for(i = n = 0; i < Dtagperblk; i++){ + if(b->d.tag[i] == 0) + continue; if(n++%3 == 0){ fmtprint(fmt, "\n"); fmttab(fmt, mbtab, 0); @@ -484,15 +490,17 @@ mbput(b); return; } - if(nind > 0) + if((nind&~DBdirflag) > 0) for(i = 0; i < Dptrperblk; i++) ptrfetch(b->d.ptr[i], nind-1, isdisk); noerror(); mbput(b); } +static void dfdump(Memblk *f, int isdisk, int justchk); + static void -dumpdents(Memblk *d, int isdisk) +dumpdents(Memblk *d, int isdisk, int justchk) { Blksl sl; vlong off; @@ -508,7 +516,7 @@ off = 0; for(;;){ - sl = dfslice(d, Dblksz, off, Rd); + sl = dfslice(d, Dblkdatasz, off, Rd); if(sl.len == 0) break; if(sl.b == nil) @@ -528,7 +536,12 @@ f = dbget(DBfile, de[i]); else f = mbget(de[i]); - dfdump(f, isdisk); + if(catcherror()){ + mbput(f); + error(nil); + } + dfdump(f, isdisk, justchk); + noerror(); noerror(); mbput(f); } @@ -540,8 +553,8 @@ mbtab--; } -void -dfdump(Memblk *f, int isdisk) +static void +dfdump(Memblk *f, int isdisk, int justchk) { int i; @@ -553,9 +566,10 @@ ptrfetch(f->d.dptr[i], f->type&DBdirflag, isdisk); for(i = 0; i < nelem(f->d.iptr); i++) ptrfetch(f->d.iptr[i], (f->type&DBdirflag)+i+1, isdisk); - fprint(2, "%H\n", f); + if(justchk == 0) + fprint(2, "%H\n", f); if(f->type == DBdir) - dumpdents(f, isdisk); + dumpdents(f, isdisk, justchk); } void @@ -563,7 +577,6 @@ { int i, n, x; Memblk *b; - extern int fullfiledumps; daddrt addr, eaddr; x = fullfiledumps; @@ -572,7 +585,7 @@ fprint(2, "\n\nfsys '%s' limit %#ullx super %D root %D:\n", fs->dev, fs->limit, fs->super->addr, fs->root->addr); fprint(2, "%H\n", fs->super); - dfdump(fs->root, disktoo); + dfdump(fs->root, disktoo, 0); if(1){ n = 0; fprint(2, "hash:\t"); @@ -608,13 +621,17 @@ { Memblk *b; long err, i, n; + int x; + + for(n = 0; xcanwlock(&fs->dquiescence) == 0; n++){ + if(n % 60 == 1) + warn("check: waiting for dquiescence..."); + sleep(1000); + } - xwlock(&fs->quiescence); dprint("mem check...\n"); - xwlock(&fs->dquiescence); if(catcherror()){ xwunlock(&fs->dquiescence); - xwunlock(&fs->quiescence); error(nil); } err = 0; @@ -634,8 +651,11 @@ } n = 0; for(i = 0; i < nelem(fs->fhash); i++) - for(b = fs->fhash[i].b; b != nil; b = b->next) + for(b = fs->fhash[i].b; b != nil; b = b->next){ + if(b->ref > 1) + dprint("hash used %H", b); n++; + } if(n != fs->mballoc.nalloc - fs->mballoc.nfree){ err++; warn("%ld hashed != %uld blocks - %uld free", @@ -680,12 +700,33 @@ checkblk(b); noerror(); } - noerror(); - xwunlock(&fs->dquiescence); - xwunlock(&fs->quiescence); if(err != 0) - warn("check: %ld errors", err); + warn("mem check: %ld errors", err); else dprint("mem check: passes\n"); + noerror(); + xwunlock(&fs->dquiescence); + + nbsendul(fs->lruc, 0); /* get some room... */ + sleep(0); + + dprint("disk check...\n"); + for(n = 0; xcanwlock(&fs->dquiescence) == 0; n++){ + if(n % 60 == 1) + warn("check: waiting for dquiescence..."); + sleep(1000); + } + x = fullfiledumps; + if(catcherror()){ + warn("disk check: %r"); + err++; + }else{ + fullfiledumps = 1; + dfdump(fs->root, Disk, 1); /* read the entire tree quietly */ + noerror(); + dprint("disk check: passes\n"); + } + fullfiledumps = x; + xwunlock(&fs->dquiescence); return err; } --- /sys/src/cmd/creepy/dbg.h Mon May 14 11:06:19 2012 +++ /sys/src/cmd/creepy/dbg.h Tue May 15 17:14:45 2012 @@ -1,6 +1,7 @@ /* * '9': 9p * 'A': address + * 'C': cons * 'D': disk * 'E': fids * 'F': slices, indirects, dirnth @@ -18,6 +19,7 @@ */ #define d9print(...) if(!dbg['9']){}else fprint(2, __VA_ARGS__) #define dAprint(...) if(!dbg['A']){}else fprint(2, __VA_ARGS__) +#define dCprint(...) if(!dbg['C']){}else fprint(2, __VA_ARGS__) #define dEprint(...) if(!dbg['E']){}else fprint(2, __VA_ARGS__) #define dFprint(...) if(!dbg['F']){}else fprint(2, __VA_ARGS__) #define dMprint(...) if(!dbg['M']){}else fprint(2, __VA_ARGS__) --- /sys/src/cmd/creepy/dblk.c Mon May 14 11:06:19 2012 +++ /sys/src/cmd/creepy/dblk.c Tue May 15 17:14:45 2012 @@ -4,7 +4,7 @@ * disk blocks. */ -void +static void okdiskaddr(daddrt addr) { if(addr % Dblksz != 0) @@ -13,7 +13,7 @@ fatal("okdiskaddr %D", addr); } -static daddrt +daddrt tagaddr(daddrt addr, int *idx) { daddrt bno; @@ -44,18 +44,24 @@ fatal("implement hosttodisk/disktohost for big endian"); } -u64int -dbgettag(daddrt addr) +/* + * Tags are not protected by file locks. + * this is to make sure a tag block does not leave MBmem after + * being renewed and before being locked. + */ +daddrt +tbrenew(Memblk *b) { - int idx; - Memblk *tb; - u64int e; + daddrt addr; - tb = dbgetlocked(DBtag, tagaddr(addr, &idx)); - e = tb->d.tag[idx]; - xqunlock(&tb->slk); - mbput(tb); - return e; + for(;;){ + addr = mbrenew(b); + xqlock(&b->slk); + if(b->state == MBmem) + return addr; + xqunlock(&b->slk); + sleep(0); + } } u64int @@ -66,7 +72,7 @@ u64int old; tb = dbget(DBtag, tagaddr(addr, &idx)); - mbrenewlocked(tb); + tbrenew(tb); old = tb->d.tag[idx]; tb->d.tag[idx] = e; xqunlock(&tb->slk); @@ -86,7 +92,7 @@ if(b->state != MBin) fatal("dbread: %s", sname(b->state)); - p = b->d.data; + p = (uchar*)&b->d; addr = DADDR(b->addr); for(tot = 0; tot < Dblksz; tot += n){ @@ -115,12 +121,10 @@ okdiskaddr(b->addr); if(b->state != MBout) fatal("dbwrite: bad state %s", sname(b->state)); - if(dbg['d']){ - if(catcherror()) - fatal("dbwrite: %r\n%H", b); - checkblk(b); - noerror(); - } + if(catcherror()) + fatal("dbwrite: %r\n%H", b); + checkblk(b); + noerror(); if(fs->swwriteerr != 0 && ++nw > fs->swwriteerr) warnerror("dbwrite: sw fault"); addr = DADDR(b->addr); @@ -131,39 +135,44 @@ /* * Caller may specify DBfile when it might be DBdir, if only - * the address and not the type is known. + * the address and not the file type is known. * We adjust the type once we know. */ -static Memblk* -xdbget(int type, daddrt addr, int locked) +Memblk* +dbget(int type, daddrt addr) { Memblk *b; int i, n; dMprint("dbget %D\n", addr); - b = mbload(addr, locked); + b = mbload(addr); if(b->state == MBerr){ mbput(b); error("corrupt block"); } - if(b->state != MBin) + if(b->state != MBin){ + if((type&~DBdirflag) != (b->type&~DBdirflag)) + fatal("dbget: type is not %s: %H", tname(type), b); + if((type&~DBdirflag) != (b->d.type&~DBdirflag)) + fatal("dbget: disk type is not %s: %H", tname(type), b); return b; + } if(ISDADDR(addr) == 0) - fatal("xdbget: load: %D", addr); + fatal("dbget: load: %D", addr); /* complete the load for the block, * others wait in b->ldlk. */ if(xcanqlock(&b->ldlk)) - fatal("mbloaded:ldlk bug"); + fatal("dbget:ldlk bug"); b->type = type; if(catcherror()){ - if(b->type == DBsuper) - fatal("can't read super: %r"); + if(type == DBsuper) + fatal("dbget: can't read super: %r"); + xqlock(&b->slk); mbset(b, MBerr); - if(locked) - xqunlock(&b->slk); + xqunlock(&b->slk); xqunlock(&b->ldlk); mbput(b); error(nil); @@ -171,15 +180,16 @@ dbread(b); + assert((b->type&~DBdirflag) == (b->d.type&~DBdirflag)); + b->type = b->d.type; + + if(b->type == DBfile || b->type == DBdir){ assert(b->mf == nil); b->mf = anew(&mfalloc); gmeta(b, b->d.embed, Embedsz); - if(b->d.mode&DMDIR) - b->type = DBdir; /* DBfile -> DBdir once we know */ } - if(locked == 0) - xqlock(&b->slk); + xqlock(&b->slk); if(b->type == DBtag){ n = 0; for(i = 1; i < Dtagperblk; i++) @@ -194,154 +204,10 @@ b->d.tag[0] = n; } mbset(b, MBclean); + xqunlock(&b->slk); if(b->type != DBsuper) mlink(&fs->clean, b); - if(locked == 0) - xqunlock(&b->slk); xqunlock(&b->ldlk); noerror(); return b; } - -Memblk* -dbget(int type, daddrt addr) -{ - return xdbget(type, addr, Unlocked); -} - -Memblk* -dbgetlocked(int type, daddrt addr) -{ - return xdbget(type, addr, Locked); -} - -void -markdentries(void *p, int n, u64int e) -{ - int i; - daddrt *d; - - d = p; - for(i = 0; i < n; i++) - if(d[i] != 0) - dfmark(d[i], e); -} - -/* - * TODO: This is a good place to do self repairs. - * see also fblk.c:/^dfmark - */ -void -dbmark(int type, daddrt addr, u64int e) -{ - Memblk *b; - int i; - u64int old; - - if(ISDADDR(addr)){ - if(catcherror()){ - warn("dbmark %D: %r", addr); - return; - } - old = dbsettag(addr, e); - noerror(); - if(old == e) /* already marked */ - return; - } - if(type == DBdata) - return; - b = mbgetlocked(addr); - if(b == nil){ - if(!ISDADDR(addr)) - fatal("dbmark: unloaded mem %D", addr); - b = dbgetlocked(type, addr); - } - if(catcherror()){ - warn("dbmark: %r"); - goto done; - } - if(type == DBdirdata){ - markdentries(b->d.data, Dptrperblk, e); - noerror(); - goto done; - } - - if((type&~DBdirflag) < DBptr0) - fatal("dbmark: type %s", tname(type)); - for(i = 0; i < Dptrperblk; i++) - if(b->d.ptr[i] != 0) - dbmark(type-1, b->d.ptr[i], e); - noerror(); -done: - xqunlock(&b->slk); - mbput(b); -} - -void -addressdentries(void *p, int n) -{ - int i; - daddrt *d; - - d = p; - for(i = 0; i < n; i++) - if(d[i] != 0) - d[i] = dfaddress(d[i]); -} - -daddrt -address(Memblk *b) -{ - daddrt addr; - - ainc(&fs->naddress); - xqlock(&b->slk); - mbunhash(b, 0); - addr = newdaddr(); - dAprint("address %D: %H", addr, b); - b->addr = addr; - assert(b->state == MBmem); - mbset(b, MBout); - mbhash(b); - mlink(&fs->out, b); - xqunlock(&b->slk); - return b->addr; -} - -daddrt -dbaddress(int type, daddrt addr) -{ - Memblk *b; - int i; - - if(ISDADDR(addr)) - return addr; - - b = mbget(addr); - if(b == nil) - fatal("dbaddress: mem %D not in hash", addr); - if((b->type&~DBdirflag) != (type&~DBdirflag)) - fatal("dbaddress: %D: type %s", addr, tname(b->type)); - if(b->state != MBmem) - fatal("dbaddress: %D: state %s", addr, sname(b->state)); - if(catcherror()){ - mbput(b); - error(nil); - } - - if(type == DBdirdata) - addressdentries(b->d.data, Dptrperblk); - else if(type != DBdata){ - if((type&~DBdirflag) < DBptr0) - fatal("dbaddress: %D: type %s", b->addr, tname(type)); - for(i = 0; i < Dptrperblk; i++) - if(b->d.ptr[i] != 0) - b->d.ptr[i] = dbaddress(type-1, b->d.ptr[i]); - } - - noerror(); - addr = address(b); - mbput(b); - return addr; -} - --- /sys/src/cmd/creepy/dk.h Mon May 14 11:06:20 2012 +++ /sys/src/cmd/creepy/dk.h Tue May 15 17:14:45 2012 @@ -1,6 +1,7 @@ typedef struct Alloc Alloc; typedef struct Blksl Blksl; typedef struct Cmd Cmd; +typedef struct Dblkhdr Dblkhdr; typedef struct Ddatablk Ddatablk; typedef struct Dfileblk Dfileblk; typedef union Diskblk Diskblk; @@ -24,6 +25,7 @@ typedef struct Usr Usr; typedef struct Usrs Usrs; + /* * these are used by several functions that have flags to indicate * read-access/write-access, etc. @@ -43,9 +45,6 @@ Rdonly, Worm, - Halting, - Halted, - Mem = 0, Disk, @@ -53,6 +52,26 @@ NSPERSEC = 1000000000ULL, }; +/* + * Locking (in acquire order): + * fs->dquiescence. debug quiescence. check: w; others: r + * fs->archlk: archproc & cons. arch cmds. + * f->mf: file rw lock. protects also all referenced blocks. + * freeze (set MBout) and melt (set MBmem) of file blocks + * requires also a write lock on the file. + * order is always parent to child file. + * fs->fhash[h]: hash chain + * b->slk: block state change + * clean,out,... block lists + * + * Not expected to block for too long (last in order); shown in random order: + * fs->superlk: changes/consults to fs->super + * any other + * + * Other rules: + * paths locked by fids. + */ + enum { DMUSERS = 0x01000000ULL, @@ -226,17 +245,25 @@ Dsuperdata dup; }; +struct Dblkhdr +{ + u64int type; /* only for safety checks, not really needed */ +}; + /* * disk blocks */ union Diskblk { - union{ - Dtagblk; - Dfileblk; - Dsuperblk; - Ddatablk; - Dptrblk; + struct{ + Dblkhdr; + union{ + Dtagblk; + Dfileblk; + Dsuperblk; + Ddatablk; + Dptrblk; + }; }; uchar diskblockdata[Dblksz]; /* align: ensure Dblksz size */ }; @@ -247,12 +274,12 @@ */ enum { - Embedsz = Dblksz - sizeof(Dfileblk) + Daddrsz, - Dptrperblk = Dblksz / Daddrsz, - Dtagperblk = Dblksz / Dtagsz, + Dblkdatasz = Dblksz - sizeof(Dblkhdr), + Embedsz = Dblkdatasz - sizeof(Dfileblk) + Daddrsz, + Dptrperblk = Dblkdatasz / Daddrsz, + Dtagperblk = Dblkdatasz / Dtagsz, }; - /* * ##### On memory structures. ##### */ @@ -477,7 +504,6 @@ Channel *sweepc; /* wakeup sweepproc */ Channel *sweepec; /* sweepproc done */ - int nlastfree; /* sweepproc <-> newdaddr hint */ QLock superlk; Memblk *super; /* super block */ @@ -488,20 +514,25 @@ Memblk *stats; /* /stats */ Usrs; - Channel *consc; /* of char*; output for /cons */ + char *prompt; + Channel *consoc; /* of Channel*; open for /cons */ + Channel *conscc; /* of Channel*; close for /cons */ + Channel *consrc; /* of char*; read for /cons */ + Channel *conswc; /* of char*; write for /cons */ + Channel *consec; /* of char*; write errors for /cons */ Channel *syncwc; /* used by fssync */ Fsstat; - RWLock quiescence; /* any user activity rlocks() this */ - RWLock dquiescence; /* any fs daemon proc rlocks() this */ + RWLock dquiescence; /* any activity rlocks; check wlocks. */ uvlong atime; /* updated on each request */ int profile; /* measure times for rpcs */ - int halt; /* user wants to halt */ + Ref halt; /* user wants to halt */ int mode; /* Normal, Rdonly, Worm */ + char *werr; /* error during fswrite() */ QLock archlk; /* fsarchive and sweepproc */ char *archdir; /* path to mounted archive */ --- /sys/src/cmd/creepy/fblk.c Mon May 14 11:06:20 2012 +++ /sys/src/cmd/creepy/fblk.c Tue May 15 17:14:45 2012 @@ -9,6 +9,7 @@ * while there's system activity. */ + /* * For dfblk. * Ensure *addrp has a MBmem block we could write. @@ -113,7 +114,6 @@ idx = bno/nblks; } if(*addrp == 0 && !mkit){ - /* hole */ warn("HOLE"); b = nil; }else{ @@ -169,9 +169,9 @@ dFprint("slice m%#p[%#ullx:+%#ulx]%c...\n",f, off, len, iswr?'w':'r'); if(iswr && f->state != MBmem) - fatal("dftruncate: %D: state %s", f->addr, sname(f->state)); + fatal("dfslice: %D: state %s", f->addr, sname(f->state)); if(!iswr && off >= f->d.length) - goto done; + return sl; doff = embedattrsz(f); dlen = Embedsz - doff; @@ -182,26 +182,18 @@ sl.data = f->d.embed + doff + off; sl.len = dlen - off; }else{ - bno = (off-dlen) / Dblksz; - boff = (off-dlen) % Dblksz; + bno = (off-dlen) / Dblkdatasz; + boff = (off-dlen) % Dblkdatasz; sl.b = dfblk(f, bno, iswr); if(sl.b != nil) sl.data = sl.b->d.data + boff; - sl.len = Dblksz - boff; + sl.len = Dblkdatasz - boff; } if(sl.len > len) sl.len = len; if(off + sl.len > f->d.length && iswr == 0) sl.len = f->d.length - off; -done: - if(sl.b == nil) - dFprint("slice m%#p[%#ullx:+%#ulx]%c -> 0[%#ulx]\n", - f, off, len, iswr?'w':'r', sl.len); - else - dFprint("slice m%#p[%#ullx:+%#ulx]%c -> m%#p:%#uld[%#ulx]\n", - f, off, len, iswr?'w':'r', - sl.b, (uchar*)sl.data - sl.b->d.data, sl.len); return sl; } @@ -215,7 +207,7 @@ off = 0; for(;;){ - sl = dfslice(d, Dblksz, off, Rd); + sl = dfslice(d, Dblkdatasz, off, Rd); if(sl.len == 0) break; if(sl.b == nil) @@ -264,112 +256,12 @@ d->d.length = off+Daddrsz; d->d.ndents++; } - if(naddr == 0) + if(naddr == 0){ d->d.ndents--; - mbput(sl.b); -} - -/* - * TODO: This is a good place to do self reparing: - * see also dblk.c:/^dbmark - * We are called also with MBmem blocks, to mark all on-disk blocks - * reachable from the mutable tree. - */ -void -dfmark(daddrt addr, u64int e) -{ - Memblk *b; - int i, type; - ulong doff, sz; - u64int old; - - if(ISDADDR(addr)){ - if(catcherror()){ - warn("dfmark %D: %r", addr); - return; - } - old = dbsettag(addr, e); - noerror(); - if(old == e) /* already marked */ - return; - } - b = mbgetlocked(addr); - if(b == nil){ - if(!ISDADDR(addr)) - fatal("dfmark: unloaded mem %D", addr); - b = dbgetlocked(DBfile, addr); - } - if(b->type != DBfile && b->type != DBdir) - fatal("dfmark: %D: type %s", addr, tname(b->type)); - if(ISDADDR(addr) && b->state != MBclean) - warn("dfmark: %D: state %s", addr, sname(b->state)); /* does it happen? */ - if(catcherror()){ - warn("dfmark: %r"); - goto fail; - } - if(b->type&DBdirflag){ - doff = embedattrsz(b); - sz = Embedsz-doff; - markdentries(b->d.embed+doff, sz/Daddrsz, e); - } - type = DBdata | (b->type&DBdirflag); - for(i = 0; i < nelem(b->d.dptr); i++) - if(b->d.dptr[i] != 0) - dbmark(type, b->d.dptr[i], e); - type = DBptr0 | (b->type&DBdirflag); - for(i = 0; i < nelem(b->d.iptr); i++) - if(b->d.iptr[i] != 0) - dbmark(type+i, b->d.iptr[i], e); - noerror(); -fail: - xqunlock(&b->slk); - mbput(b); -} - -/* - * Give addresses to mutable blocks, to write them out. - */ -daddrt -dfaddress(daddrt addr) -{ - Memblk *b; - int i, type; - ulong doff, sz; - - if(ISDADDR(addr)) - return addr; - - b = mbget(addr); - if(b == nil) - fatal("dfaddress: mem %D not in hash", addr); - if(b->type != DBfile && b->type != DBdir) - fatal("dfaddress: %D: type %s", addr, tname(b->type)); - if(b->state != MBmem) - fatal("dfaddress: %D: state %s", addr, sname(b->state)); - if(catcherror()){ - mbput(b); - error(nil); + if(d->d.ndents == 0 && d->d.length > 0) + d->d.length = 0; } - - if(b->type&DBdirflag){ - doff = embedattrsz(b); - sz = Embedsz-doff; - addressdentries(b->d.embed+doff, sz/Daddrsz); - } - type = DBdata | (b->type&DBdirflag); - for(i = 0; i < nelem(b->d.dptr); i++) - if(b->d.dptr[i] != 0) - b->d.dptr[i] = dbaddress(type, b->d.dptr[i]); - type = DBptr0 | (b->type&DBdirflag); - for(i = 0; i < nelem(b->d.iptr); i++) - if(b->d.iptr[i] != 0) - b->d.iptr[i] = dbaddress(type+i, b->d.iptr[i]); - - noerror(); - addr = address(b); - mbput(b); - nbsendul(fs->syncc, 0); - return addr; + mbput(sl.b); } Memblk* @@ -383,7 +275,7 @@ off = 0; for(;;){ - sl = dfslice(d, Dblksz, off, Rd); + sl = dfslice(d, Dblkdatasz, off, Rd); if(sl.len == 0) break; if(sl.b == nil) @@ -403,11 +295,16 @@ } f = dbget(DBfile, de[i]); noerror(); + if(f->mf == nil) + fatal("no mf: %H", f); + xrlock(f->mf); if(strcmp(f->mf->name, name) == 0){ noerror(); + xrunlock(f->mf); mbput(sl.b); return f; } + xrunlock(f->mf); mbput(f); } off += sl.len; @@ -422,15 +319,15 @@ * May be called with null parent, for the root file. */ Memblk* -dfcreate(Memblk *parent, char *name, int uid, ulong mode) +dfcreate(Memblk *p, char *name, int uid, ulong mode) { Memblk *nf; Mfile *m; - if(parent != nil) - dprint("dfcreate '%s' %M at\n%H\n", name, mode, parent); + if(p != nil) + d9print("dfcreate '%s' %M at\n%H\n", name, mode, p); else - dprint("dfcreate root: '%s' %M\n", name, mode); + d9print("dfcreate root: '%s' %M\n", name, mode); if(mode&DMDIR) nf = mbnew(DBdir); @@ -438,6 +335,8 @@ nf = mbnew(DBfile); if(catcherror()){ mbput(nf); + mbunhash(nf, 0); + mbput(nf); error(nil); } @@ -456,29 +355,27 @@ m->name = name; nf->d.asize = pmeta(nf->d.embed, Embedsz, nf); - if(parent != nil){ - m->gid = parent->mf->gid; - nf->d.gid = parent->d.gid; - dfchdentry(parent, 0, nf->addr); + if(p != nil){ + if(p->state != MBmem) + fatal("dfcreate: %D: state %s", p->addr, sname(p->state)); + m->gid = p->mf->gid; + nf->d.gid = p->d.gid; + dfchdentry(p, 0, nf->addr); } noerror(); - dprint("dfcreate -> %H\n", nf); + d9print("dfcreate -> %H\n", nf); return nf; } /* - * Drops the given ref for f. + * Drops the given ref for f after wunlocking f. */ void dfremove(Memblk *p, Memblk *f) { - if(canrlock(f->mf)) - fatal("dfremove: not wlocked: %H", f); if(f->d.ndents > 0 && fs->mode != Worm) /* Worm can rm a tree */ error("directory not empty"); dfchdentry(p, f->addr, 0); - if(p->d.ndents == 0 && p->d.length > 0) - p->d.length = 0; xwunlock(f->mf); mbput(f); } @@ -520,6 +417,8 @@ char *p; p = a; + if(f->state != MBmem) + fatal("dfblk: %D: state %s", f->addr, sname(f->state)); if(f->d.mode&DMAPPEND) *off = f->d.length; for(tot = 0; tot < count;){ --- /sys/src/cmd/creepy/fid.c Mon May 14 11:06:20 2012 +++ /sys/src/cmd/creepy/fid.c Tue May 15 17:14:46 2012 @@ -35,29 +35,27 @@ fid = va_arg(fmt->args, Fid*); if(fid == nil) return fmtprint(fmt, ""); - fmtprint(fmt, "fid %#p no %d r%d, omode %d", + fmtprint(fmt, "fid %#p no %d r%d, omode %d\n", fid, fid->no, fid->ref, fid->omode); p = fid->p; if(p == nil) return 0; - fmtprint(fmt, " path"); for(i = 0; i < p->nf; i++) - fmtprint(fmt, " d%#ullx", p->f[i]->addr); - return fmtprint(fmt, "\n=>%H", p->f[p->nf-1]); + fmtprint(fmt, " %H", p->f[i]); + return 0; } void dumpfids(void) { Fid *fid; - int n, i; + int i; xrlock(&fidhashlk); fprint(2, "fids:\n"); - n = 0; for(i = 0; i < nelem(fidhash); i++) for(fid = fidhash[i]; fid != nil; fid = fid->next) - fprint(2, "[%d] = %X\n", n++, fid); + fprint(2, "%X\n", fid); xrunlock(&fidhashlk); } @@ -151,6 +149,10 @@ d9print("clunk %X\n", fid); putpath(fid->p); fid->p = nil; + if(fid->consrc != nil){ + chanfree(fid->consrc); + fid->consrc = nil; + } xwlock(&fidhashlk); if(catcherror()){ xwunlock(&fidhashlk); @@ -290,7 +292,6 @@ nfid = newfid(cli, no); nfid->p = clonepath(fid->p); nfid->uid = fid->uid; - nfid->consopen = fid->consopen; nfid->buf = fid->buf; nfid->wormwr = fid->wormwr; noerror(); @@ -348,6 +349,8 @@ void fidcanwrite(Fid *fid) { + if(fs->werr != nil) + error("%s", fs->werr); if(fs->mode == Rdonly && fid->p->f[fid->p->nf-1] != fs->cons) error("read only file system"); if(fs->mode == Worm && fid->wormwr == 0) @@ -443,8 +446,7 @@ } fidaccessok(fid, f, amode); if((fmode&DMEXCL) != 0 && f->mf->open) - if(f != fs->cons || amode != AWRITE) /* ok to write cons cmds */ - error("exclusive use file already open"); + error("exclusive use file already open"); if(mode&ORCLOSE){ if(f->d.mode&DMUSERS) error("can't remove /users"); @@ -467,7 +469,10 @@ fid->omode = mode&3; fid->loff = 0; fid->lidx = 0; - fid->consopen = f == fs->cons; + if(f == fs->cons){ + fid->consrc = chancreate(sizeof(char*), 128); + consopen(fid->consrc); + } noerror(); if(amode != AREAD) xwunlock(f->mf); @@ -562,7 +567,7 @@ n = 0; tot = 0; for(;;){ - sl = dfslice(d, Dblksz, off, Rd); + sl = dfslice(d, Dblkdatasz, off, Rd); if(sl.len == 0) break; if(sl.b == nil) @@ -658,7 +663,7 @@ if(f == fs->cons){ noerror(); xqunlock(fid); - return consread(data, count); + return consread(fid->consrc, data, count); } if(f == fs->stats){ noerror(); @@ -767,7 +772,11 @@ } putpath(fid->p); fid->p = nil; - fid->consopen = 0; + if(fid->consrc != nil){ + consclose(fid->consrc); + chanfree(fid->consrc); + } + fid->consrc = nil; noerror(); xqunlock(fid); } --- /sys/src/cmd/creepy/fmt.c Mon May 14 11:06:20 2012 +++ /sys/src/cmd/creepy/fmt.c Tue May 15 17:14:46 2012 @@ -149,5 +149,6 @@ fscheck(); } noerror(); + errterm(); threadexitsall(nil); } --- /sys/src/cmd/creepy/fns.h Mon May 14 11:06:20 2012 +++ /sys/src/cmd/creepy/fns.h Tue May 15 17:14:46 2012 @@ -1,39 +1,30 @@ extern Path* addelem(Path **pp, Memblk *f); -extern daddrt address(Memblk *b); -extern void addressdentries(void *p, int n); extern void afree(Alloc *a, void *nd); extern int allowed(int uid); extern int allowed(int); extern int allowed(int); extern void allowuid(int uid); extern void* anew(Alloc *a); -extern ulong asize(Alloc *a); extern int builtin(Memblk *f); extern void checkblk(Memblk *b); extern u64int chkusr(char *buf); extern char* cliworker9p(void *v, void**aux); extern Path* clonepath(Path *p); +extern void consclose(Channel *c); extern long consctl(char *p, long count); -extern void consinit(void); +extern void consinit(char *prompt); +extern void consopen(Channel *c); extern void consprint(char *fmt, ...); extern void consprintclients(void); -extern long consread(char *buf, long count); +extern long consread(Channel *c, char *buf, long count); extern long conswrite(char *ubuf, long count); extern int daddrfmt(Fmt *fmt); -extern daddrt dbaddress(int type, daddrt addr); extern Memblk* dbget(int type, daddrt addr); -extern Memblk* dbgetlocked(int type, daddrt addr); -extern u64int dbgettag(daddrt addr); -extern void dbmark(int type, daddrt addr, u64int e); extern u64int dbsettag(daddrt addr, u64int e); extern long dbwrite(Memblk *b); -extern void debug(void); -extern daddrt dfaddress(daddrt addr); extern void dfcattr(Memblk *f, int op, char *name, char *val); extern void dfchdentry(Memblk *d, daddrt addr, daddrt naddr); -extern Memblk* dfcreate(Memblk *parent, char *name, int uid, ulong mode); -extern void dfdump(Memblk *f, int isdisk); -extern void dfmark(daddrt addr, u64int e); +extern Memblk* dfcreate(Memblk *p, char *name, int uid, ulong mode); extern ulong dfpread(Memblk *f, void *a, ulong count, uvlong off); extern ulong dfpwrite(Memblk *f, void *a, ulong count, uvlong *off); extern long dfrattr(Memblk *f, char *name, char *val, long count); @@ -59,6 +50,8 @@ extern void fidremove(Fid *fid); extern void fidwalk(Fid *fid, char *wname); extern long fidwrite(Fid *fid, void *data, ulong count, uvlong *offset); +extern Usr* finduid(int uid); +extern Usr* finduname(char *name, int mkit); extern void freerpc(Rpc *rpc); extern void fsarchive(void); extern long fscheck(void); @@ -68,6 +61,9 @@ extern void fspolicy(void); extern void fssync(void); extern uvlong fstime(uvlong t); +extern void fswrite(Channel *c); +extern void fswritesuper(Channel *c); +extern void fswritetags(void); extern Fid* getfid(Cli* cli, uint no); extern void gmeta(Memblk *f, void *buf, ulong nbuf); extern int ixcallfmt(Fmt *fmt); @@ -80,19 +76,15 @@ extern int leader(int gid, int lead); extern void listen9pix(char *addr, int noauth, char* (*cliworker)(void *arg, void **aux)); extern void lockstats(int on); -extern void markdentries(void *p, int n, u64int e); extern Memblk* mballocz(int zeroit); extern int mbfmt(Fmt *fmt); extern Memblk* mbget(daddrt addr); -extern Memblk* mbgetlocked(daddrt addr); extern void mbhash(Memblk *b); -extern int mbhashed(Memblk *b); -extern Memblk* mbload(daddrt addr, int locked); +extern Memblk* mbload(daddrt addr); extern int mblru(Memblk *b); extern Memblk* mbnew(int type); extern void mbput(Memblk *b); extern daddrt mbrenew(Memblk *b); -extern daddrt mbrenewlocked(Memblk *b); extern void mbset(Memblk *b, int s); extern int mbunhash(Memblk *b, int onlyidle); extern int member(int uid, int member); @@ -112,11 +104,8 @@ extern char* ninestats(char *s, char *e, int clr, int verb); extern char* ninestats(char *s, char*, int, int); extern char* ninestats(char *s, char*, int, int); -extern void nodebug(void); -extern void okdiskaddr(daddrt addr); extern vlong opend(Opstat *o, vlong t0); extern vlong opstart(Opstat *o, vlong t); -extern void ownpath(Path **pp); extern int pathfmt(Fmt *fmt); extern void pchanged(Path *p, int n, int muid); extern ulong pmeta(void *buf, ulong nbuf, Memblk *f); @@ -127,15 +116,17 @@ extern void putpath(Path *p); extern void rahead(Memblk *f, uvlong offset); extern void replied(Rpc *rpc); -extern void rlsedebug(int r); extern int rpcfmt(Fmt *fmt); extern void rwusers(Memblk *uf); extern void rwusers(Memblk*); extern void rwusers(Memblk*); -extern int setdebug(void); extern void setfiduid(Fid *fid, char *uname); extern char* sname(int s); extern void srv9pix(char *srv, int noauth, char* (*cliworker)(void *arg, void **aux)); +extern void sweepproc(void*); +extern void syncproc(void*); +extern daddrt tagaddr(daddrt addr, int *idx); +extern daddrt tbrenew(Memblk *b); extern char* tname(int t); extern char* updatestats(int clr, int verb); extern int usrfmt(Fmt *fmt); @@ -151,7 +142,7 @@ extern int writedenied(int uid); extern void wstatint(Memblk *f, char *name, u64int v); extern int xcanqlock(QLock *q); -extern Memblk* xmbget(daddrt addr, int locked, int load); +extern int xcanwlock(RWLock *rw); extern void xmlinkhd(List *l, Memblk *b); extern void xmunlink(List *l, Memblk *b); extern void xqlock(QLock *q); --- /sys/src/cmd/creepy/fsys.c Mon May 14 11:06:20 2012 +++ /sys/src/cmd/creepy/fsys.c Tue May 15 17:14:47 2012 @@ -30,20 +30,25 @@ xwunlock(f->mf); ainc(&fs->nrenewpath); - for(i = 0; i < nth; i++){ - if(p->f[i]->state == MBmem) - continue; - assert(i > 0); - xwlock(p->f[i-1]->mf); + xwlock(p->f[0]->mf); + for(i = 1; i < nth; i++){ xwlock(p->f[i]->mf); - oaddr = p->f[i]->addr; - naddr = mbrenew(p->f[i]); - if(naddr != 0) - dfchdentry(p->f[i-1], oaddr, naddr); + if(p->f[i]->state != MBmem){ + assert(i > 0); + oaddr = p->f[i]->addr; + naddr = mbrenew(p->f[i]); + if(naddr != 0){ + if(catcherror()){ + xwunlock(p->f[i]->mf); + xwunlock(p->f[i-1]->mf); + error(nil); + } + dfchdentry(p->f[i-1], oaddr, naddr); + noerror(); + } + } xwunlock(p->f[i-1]->mf); - xwunlock(p->f[i]->mf); } - xwlock(f->mf); return f; } @@ -187,137 +192,6 @@ nbsendul(fs->sweepc, 0); } -static void -fswritesuper(Channel *c) -{ - Memblk *b; - Memblk *super; - - super = fs->super; - b = mballocz(1); - xqlock(&fs->superlk); - b->addr = fs->super->addr; - b->type = super->type; - b->addr = super->addr; - xqlock(&b->slk); - mbset(b, MBout); - b->d.Dsuperdata = fs->super->d.Dsuperdata; - b->d.dup = b->d.Dsuperdata; - b->wc = c; - xqunlock(&fs->superlk); - mlink(&fs->out, b); - xqunlock(&b->slk); - nbsendul(fs->syncc, 0); -} - -/* - * The mark & sweep is dumb, slow, and reliable. - * It should happen only when the disk is almost full. - * fsmark and sweep may run concurrently. - */ -static void -fswritetags(void) -{ - Memblk *b; - - while((b = munlink(&fs->dtags, nil)) != nil){ - xqlock(&b->slk); - mbset(b, MBout); - mlink(&fs->out, b); - xqunlock(&b->slk); - } -} - -/* - * Freeze the fs state and schedule it for writing. - * A new super with the new / is written at the end. - */ -static void -fswrite(Channel *c) -{ - vlong t; - daddrt addr, oaddr; - - dZprint("fswrite...\n"); - t = opstart(&fs->opstat[Opwriteq], 0); - xwlock(&fs->quiescence); - t = opend(&fs->opstat[Opwriteq], t); - t = opstart(&fs->opstat[Opwrite], t); - if(catcherror()){ - opend(&fs->opstat[Opwrite], t); - dZprint("fswrite: %r\n"); - xwunlock(&fs->quiescence); - error(nil); - } - - ainc(&fs->nwrite); - oaddr = fs->active->addr; - addr = dfaddress(oaddr); - dfchdentry(fs->root, oaddr, addr); - - xqlock(&fs->superlk); - fs->super->d.root = addr; - xqunlock(&fs->superlk); - fswritetags(); - fswritesuper(c); - - noerror(); - xwunlock(&fs->quiescence); - opend(&fs->opstat[Opwrite], t); - dZprint("fswrite: done\n"); -} - -/* - * Write blocks to disk, including copies for the super. - */ -static void -syncproc(void*) -{ - vlong t; - Memblk *b; - int n; - - threadsetname("syncproc"); - errinit(Errstack); - if(catcherror()) - fatal("%s: uncatched: %r", threadgetname()); - for(;;){ - recvul(fs->syncc); - dZprint("sync...\n"); - t = opstart(&fs->opstat[Opsync], 0); - for(n = 0; (b = munlink(&fs->out, nil)) != nil; n++){ - if(catcherror()){ - warn("write %D: %r", b->addr); - if(b->type == DBsuper){ - if(b->wc != nil) - sendp(b->wc, smprint("%r")); - b->wc = nil; - mbput(b); - }else - mbset(b, MBerr); - continue; - } - - dbwrite(b); - - noerror(); - if(b->type == DBsuper){ - if(b->wc != nil) - sendp(b->wc, nil); - b->wc = nil; - mbput(b); - }else{ - xqlock(&b->slk); - mbset(b, MBclean); - mlink(&fs->clean, b); - xqunlock(&b->slk); - } - } - opend(&fs->opstat[Opsync], t); - dZprint("sync: wrote %d blocks\n", n); - } -} - /* * Move blocks out of memory to release some. */ @@ -334,8 +208,8 @@ fatal("%s: uncatched: %r", threadgetname()); for(;;){ recvul(fs->lruc); - dZprint("lru...\n"); xrlock(&fs->dquiescence); + dZprint("lru...\n"); t = opstart(&fs->opstat[Oplru], 0); xqlock(&fs->clean); n = ign = 0; @@ -360,238 +234,22 @@ } } -/* - * Mark all blocks that could be reached with a new epoch, - * so blocks with lower epochs could be considered free. - */ -static void -fsmark(void) -{ - u64int e; - vlong t; - Channel *c; - - c = chancreate(sizeof(char*), 0); - dZprint("fsmark...\n"); - t = opstart(&fs->opstat[Opmark], 0); - nbsendul(fs->lruc, 0); /* make some room */ - - xqlock(&fs->superlk); - e = ++fs->super->d.epoch; - xqunlock(&fs->superlk); - - dfmark(fs->super->d.root, e); - dfmark(fs->active->addr, e); - - xqlock(&fs->archlk); - xqlock(&fs->superlk); - fs->super->d.fepoch = e-1; - xqunlock(&fs->superlk); - xqunlock(&fs->archlk); - - fswritetags(); - fswritesuper(c); - recvp(c); - chanfree(c); - - opend(&fs->opstat[Opmark], t); - dZprint("fsmark: done\n"); -} - static void -dropold(daddrt addr) +archproc(void*) { - Memblk *b; - - b = mbget(addr); - if(b == nil) - return; - dprint("dropold: reused %H\n", b); - assert(b->ref > 1); /* hash and b */ - mbput(b); /* b */ - xqlock(&fs->clean); - if(mblru(b) < 0) - fatal("dropold: busy %H", b); - xqunlock(&fs->clean); -} - -static daddrt -growworm(void) -{ - daddrt addr; - - xqlock(&fs->superlk); - if(fs->super->d.eaddr + Dblksz > fs->limit){ - xqunlock(&fs->superlk); - warnerror("worm full"); - } - addr = fs->super->d.eaddr; - fs->super->d.eaddr += Dblksz; - fs->ndfree--; - xqunlock(&fs->superlk); - return MKDADDR(addr); -} - -daddrt -newdaddr(void) -{ - Memblk *tb; - daddrt addr0, eaddr, grpsz, a; - dtagt epoch; - int i, ntry; - - if(fs->mode == Worm) - return growworm(); - - grpsz = Dblksz*Dtagperblk; - for(ntry = 0; ntry < Nscanfree; ntry++){ - xqlock(&fs->freelk); - - xqlock(&fs->superlk); - eaddr = fs->super->d.eaddr; - epoch = fs->super->d.epoch; - xqunlock(&fs->superlk); - addr0 = fs->ifree; - do{ - if(catcherror()){ - warn("newdaddr: %r"); - continue; - } - tb = dbgetlocked(DBtag, MKDADDR(fs->ifree)); - for(i = 1; i < Dtagperblk; i++) - if(tb->d.tag[i] == 0) - break; - /* TODO: we can skip this one when tb->d.tag[0] == 0 */ - if(i == Dtagperblk) - goto next; - if(tb->state != MBmem){ - xqunlock(&tb->slk); - mbrenewlocked(tb); - } - assert(tb->state == MBmem); - for(i = 1; i < Dtagperblk; i++){ - if(tb->d.tag[i] == 0){ - a = fs->ifree+i*Dblksz; - assert(a < eaddr); - tb->d.tag[i] = epoch; - tb->d.tag[0]--; - xqlock(&fs->superlk); - if(fs->ndfree == 0) - fatal("newdaddr: ndfree == 0"); - if(fs->ndfree < 30 || (fs->ndfree%50)== 0) - dAprint("newdaddr: %ulld-1\n", fs->ndfree); - fs->ndfree--; - noerror(); - xqunlock(&fs->superlk); - xqunlock(&tb->slk); - mbput(tb); - xqunlock(&fs->freelk); - dropold(MKDADDR(a)); - return MKDADDR(a); - } - } - next: - fs->ifree += grpsz; - if(fs->ifree >= eaddr) - fs->ifree = Dblk0addr; - noerror(); - xqunlock(&tb->slk); - mbput(tb); - }while(fs->ifree != addr0); - xqunlock(&fs->freelk); - - warn("newdaddr: %ulld free; asking sweep for more.", fs->ndfree); - ainc(&fs->newdaddrw); - sendul(fs->sweepc, 0); - recvul(fs->sweepec); - if(fs->nlastfree > 0) /* got some, restart */ - ntry = 0; - } - warnerror("newdaddr: disk full and no luck"); - return Noaddr; -} - -static void -sweepproc(void*) -{ - Memblk *tb; - daddrt addr0, eaddr, grpsz, a; - dtagt fepoch; - int i, n, nb; - vlong t; - - threadsetname("sweepproc"); + threadsetname("archproc"); errinit(Errstack); if(catcherror()) - fatal("%s: uncatched: %r", threadgetname()); - grpsz = Dblksz*Dtagperblk; - for(;;){ - recvul(fs->sweepc); - xrlock(&fs->dquiescence); - dZprint("sweep at %D with %ulld free\n", fs->isweep, fs->ndfree); - again: - t = opstart(&fs->opstat[Opsweep], 0); - xqlock(&fs->superlk); - eaddr = fs->super->d.eaddr; - fepoch = fs->super->d.fepoch; - xqunlock(&fs->superlk); - n = 0; - addr0 = fs->isweep; - do{ - nb = 0; - if(catcherror()){ - warn("sweep: %r"); - continue; - } - tb = dbgetlocked(DBtag, MKDADDR(fs->isweep)); - for(i = 1; i < Dtagperblk; i++) - if(tb->d.tag[i] != 0 && tb->d.tag[i] <= fepoch) - break; - if(i == Dtagperblk) - goto next; - if(tb->state != MBmem){ - xqunlock(&tb->slk); - mbrenewlocked(tb); - } - assert(tb->state == MBmem); - for(i = 1; i < Dtagperblk; i++) - if(tb->d.tag[i] != 0 && tb->d.tag[i] <= fepoch){ - a = fs->isweep+i*Dblksz; - assert(a < eaddr); - dAprint("sweep: free %D\n", MKDADDR(a)); - ainc(&fs->ngc); - nb++; - tb->d.tag[i] = 0; - } - tb->d.tag[0] += nb; - n += nb; - next: - fs->isweep += grpsz; - if(fs->isweep >= eaddr) - fs->isweep = Dblk0addr; - noerror(); - if(nb > 0) - dAprint("sweep: %D: +%d free\n", tb->addr, nb); - xqlock(&fs->superlk); - fs->ndfree += nb; - if(fs->ndfree > fs->ndblk) - fatal("ndfree out of range: %ulld > %ulld", - fs->ndfree, fs->ndblk); - xqunlock(&fs->superlk); - xqunlock(&tb->slk); - mbput(tb); - }while(fs->isweep != addr0 && fs->ndfree < Dmaxfree); - - opend(&fs->opstat[Opsweep], t); - dZprint("sweepproc: %d collected %ulld free\n", n, fs->ndfree); - fs->nlastfree = n; - if(n == 0 && fs->ndfree < Dminfree){ - fsmark(); - goto again; - } - xrunlock(&fs->dquiescence); - nbsendul(fs->sweepec, 0); /* for cmd.c */ - } + fatal("archproc: uncatched: %r"); + xrlock(&fs->dquiescence); + xqlock(&fs->archlk); + fsarchive(); + fs->archt = nextime(time(nil), fs->archhour); + xqunlock(&fs->archlk); + xrunlock(&fs->dquiescence); + noerror(); + errterm(); + threadexits(nil); } static void @@ -600,22 +258,29 @@ uint n; threadsetname("tmrproc"); + errinit(Errstack); + if(catcherror()) + fatal("tmrproc: uncatched: %r"); n = 0; for(;;){ sleep(1000); fstime(nsec()); /* with just one dirty block (/root) nothing has changed */ - if(fs->nmblk > 1) - if(fs->nmblk > Mmaxdirty || (++n % Syncival) == 0){ + if(fs->werr == nil) + if(fs->nmblk > 1 && (fs->nmblk>Mmaxdirty || (++n % Syncival) == 0)){ xrlock(&fs->dquiescence); dprint("tmr: sync %ulld blocks...\n", fs->nmblk); - fswrite(nil); + if(catcherror()) + warn("couldn't sync: %r"); + else{ + fswrite(nil); + noerror(); + } xrunlock(&fs->dquiescence); } - if(fs->mode != Worm) - if(fs->archt != 0 && time(nil) > fs->archt){ - fsarchive(); - fs->archt = nextime(time(nil), fs->archhour); + if(fs->mode != Worm && fs->archt != 0 && time(nil) > fs->archt){ + fs->archt = 0; + proccreate(archproc, nil, Stack); } } } @@ -630,6 +295,7 @@ /* this is an invariant that must hold for directories */ assert(Embedsz % Daddrsz == 0); + assert(Dblksz == sizeof b->d); fs = mallocz(sizeof *fs, 1); fs->dev = strdup(dev); @@ -641,7 +307,11 @@ fs->syncc = chancreate(sizeof(ulong), 0); fs->sweepc = chancreate(sizeof(ulong), 0); fs->sweepec = chancreate(sizeof(ulong), 0); - fs->consc = chancreate(sizeof(char*), 256); + fs->consoc = chancreate(sizeof(Channel*), 0); + fs->conscc = chancreate(sizeof(Channel*), 0); + fs->consrc = chancreate(sizeof(char*), 128); + fs->conswc = chancreate(sizeof(char*), 0); + fs->consec = chancreate(sizeof(char*), 0); fs->isweep = Dblk0addr; fs->ifree = Dblk0addr; nablk = Fsysmem / sizeof(Memblk); @@ -664,7 +334,7 @@ warn("prepaging..."); e = fs->blk + nablk; for(b = fs->blk; b < e; b++){ - for(c = b->d.data + 4096; c < b->d.data+Dblksz; c += 4096) + for(c = b->d.data + 4096; c < b->d.data+Dblkdatasz; c += 4096) *c = 0; b->state = MBfree; afree(&fs->mballoc, b); @@ -691,21 +361,24 @@ static void inittags(void) { - static Diskblk d; + static Memblk m; daddrt addr, n, nt; nt = 0; + m.d.type = DBtag; + m.type = DBtag; + for(addr = Dblk0addr; addr+Dblksz < fs->limit; addr += Dblksz*Dtagperblk){ - d.tag[0] = Dtagperblk - 1; + m.d.tag[0] = Dtagperblk - 1; n = (fs->limit - addr)/Dblksz; if(n < Dtagperblk){ dprint("partial group with %ulld blocks\n", n); - d.tag[0] = n-1; + m.d.tag[0] = n-1; for(; n < Dtagperblk; n++) - d.tag[n] = ~0; + m.d.tag[n] = ~0; } - dWprint("inittags %D\n", addr); - if(pwrite(fs->fd, &d, sizeof d, DADDR(addr)) != Dblksz) + dWprint("inittags %D\n", MKDADDR(addr)); + if(pwrite(fs->fd, &m.d, sizeof m.d, DADDR(addr)) != Dblksz) fatal("inittags: %D: %r", addr); nt++; } @@ -717,6 +390,7 @@ { b->addr = MKDADDR(Dsuperaddr); b->d.magic = MAGIC; + b->d.type = DBsuper; b->d.dblksz = Dblksz; b->d.dminattrsz = Dminattrsz; b->d.ndptr = Ndptr; @@ -779,7 +453,7 @@ uid = usrid("sys"); fs->root = dfcreate(nil, "", uid, DMDIR|0555); - fs->cons = dfcreate(fs->root, "cons", uid, DMEXCL|0660); + fs->cons = dfcreate(fs->root, "cons", uid, 0660); fs->stats = dfcreate(fs->root, "stats", uid, 0664); fs->active = dbget(DBdir, fs->super->d.root); dfchdentry(fs->root, 0, fs->active->addr); --- /sys/src/cmd/creepy/ix.c Mon May 14 11:06:20 2012 +++ /sys/src/cmd/creepy/ix.c Tue May 15 17:14:48 2012 @@ -54,7 +54,7 @@ [IXTwrite] 1, }; -Alloc srpcalloc = +static Alloc srpcalloc = { .elsz = sizeof(Shortrpc), .zeroing = 0, @@ -503,6 +503,10 @@ errinit(Errstack); *aux = v; /* make it not nil */ } + if(catcherror()) + fatal("uncatched: %r"); + + fspolicy(); err[0] = 0; rpc = recvp(c); @@ -515,19 +519,19 @@ nerr = errstacksize(); rpc->xr.type = rpc->xt.type + 1; rpc->rpc0 = rpc0; - xrlock(&fs->quiescence); + xrlock(&fs->dquiescence); if(catcherror()){ - xrunlock(&fs->quiescence); + xrunlock(&fs->dquiescence); rpc->xr.type = Rerror; rpc->xr.ename = err; rerrstr(err, sizeof err); if(rpc0->fid != nil && (rpc0->fid->cflags&OCERR) != 0) rpc0->fid->cflags |= OCEND; }else{ - if(fs->halt != 0) + if(fs->halt.ref != 0) error("file system halted"); ixcalls[rpc->xt.type](rpc); - xrunlock(&fs->quiescence); + xrunlock(&fs->dquiescence); noerror(); } @@ -562,6 +566,7 @@ replied(rpc0); freeixrpc(rpc0); + noerror(); dPprint("%s exiting\n", threadgetname()); threadsetname("rpcworkerix"); return nil; --- /sys/src/cmd/creepy/mblk.c Mon May 14 11:06:20 2012 +++ /sys/src/cmd/creepy/mblk.c Tue May 15 17:14:48 2012 @@ -55,6 +55,7 @@ xqlock(&b->slk); mbset(b, MBmem); b->type = DBfree; + b->d.type = DBfree; b->addr = MKMADDR(b); b->next = nil; xqunlock(&b->slk); @@ -93,12 +94,6 @@ afree(&fs->mballoc, b); } -int -mbhashed(Memblk *b) -{ - return b->next != nil; -} - void mbhash(Memblk *b) { @@ -149,24 +144,26 @@ { if(b->type == DBsuper) return -1; + if(xcanqlock(&fs->clean)) + fatal("mblru: clean not locked"); - if(xcanqlock(&b->slk) == 0){ /* busy; try later... */ + if(xcanqlock(&b->slk) == 0){ /* busy and wrong lock order; try later... */ dprint("mblru: %D lock busy\n", b->addr); return -1; } if(b->state != MBclean) fatal("mblru: %D: not clean: %s", b->addr, sname(b->state)); - if(xcanqlock(&fs->clean)) - fatal("mblru: clean not locked"); if(mbunhash(b, 1) < 0){ xqunlock(&b->slk); - dprint("mblru: %D busy\n", b->addr); + dprint("mblru: %D got busy\n", b->addr); return -1; } xmunlink(&fs->clean, b); mbset(b, MBlru); xqunlock(&b->slk); + dOprint("mblru: %D\n", b->addr); + if(b->type == DBtag){ dprint("lru: %D DBtag out: -%ud ndfree\n", b->addr, b->d.tag[0]); xqlock(&fs->superlk); @@ -174,6 +171,7 @@ fs->ndfree -= b->d.tag[0]; xqunlock(&fs->superlk); } + mbput(b); /* hash and list ref */ return 0; } @@ -221,16 +219,16 @@ ainc(&fs->mbreneww); }else{ if(nwait > 0) - dSprint("mbrenew: waited %d for %D", nwait, b->addr); + dSprint("mbrenew: %d waits for %D", nwait, b->addr); break; } if((++nwait % 1000) == 0) warn("mbrenew: block %D is dirty. waited %d", b->addr, nwait); xqunlock(&b->slk); nbsendul(fs->syncc, 0); /* ask for writing */ - if(nwait < 100) /* but don't wait for it, more blocks */ - sleep(0); /* may be added while we wait and we prefer */ - else if(nwait < 1000) /* to wahead the block we are waiting for */ + if(nwait < 100) /* but don't block: more blocks */ + sleep(0); /* may be added while we wait */ + else if(nwait < 1000) /* and we better insist later */ sleep(60); else sleep(1000); @@ -242,6 +240,8 @@ if(b->state != MBclean) fatal("mbrenew: %D: old %s", b->addr, sname(b->state)); munlink(&fs->clean, b); + mbset(b, MBmem); + xqunlock(&b->slk); if(b->type == DBtag) mlink(&fs->dtags, b); else{ @@ -252,31 +252,10 @@ b->d.prev = oaddr; mbhash(b); } - mbset(b, MBmem); - xqunlock(&b->slk); return b->addr; } /* - * Tags can be frozen (written) even if the fs is quiescent, - * this is to make sure a tag block does not leave MBmem after - * being renewed and before being locked. - */ -daddrt -mbrenewlocked(Memblk *b) -{ - daddrt addr; - - for(;;){ - addr = mbrenew(b); - xqlock(&b->slk); - if(b->state == MBmem) - return addr; - xqunlock(&b->slk); - } -} - -/* * Make a new mem block of the given type. */ Memblk* @@ -288,6 +267,7 @@ fatal("mbnew: DBtag in worm"); b = mballocz(1); b->type = type; + b->d.type = type; incref(b); /* hash and list */ if(b->type == DBfile || b->type == DBdir){ assert(b->mf == nil); @@ -301,16 +281,13 @@ * Return block for the given address. * It might be unloaded, in which case the caller is responsible * for reading it. - * If locked, the block is returned locked so its state - * does not change before used. */ -Memblk* -xmbget(daddrt addr, int locked, int load) +static Memblk* +xmbget(daddrt addr, int load) { - Memblk *b; + Memblk *b, *nb; uint hv; -again: hv = addr%nelem(fs->fhash); xqlock(&fs->fhash[hv]); for(b = fs->fhash[hv].b; b != nil; b = b->next) @@ -323,52 +300,46 @@ xqunlock(&fs->fhash[hv]); return nil; } - b = mballocz(0); + xqunlock(&fs->fhash[hv]); /* we might run out of blocks */ + nb = mballocz(0); /* don't hold the hash */ + xqlock(&fs->fhash[hv]); + for(b = fs->fhash[hv].b; b != nil; b = b->next) + if(b->addr == addr){ + incref(b); /* came during allocation */ + mbput(nb); + goto found; + } + b = nb; b->addr = addr; xqlock(&b->slk); mbset(b, MBin); - incref(b); /* hash (& future list) */ + incref(b); /* hash */ b->next = fs->fhash[hv].b; fs->fhash[hv].b = b; xqlock(&b->ldlk); /* make others wait for it */ xqunlock(&b->slk); xqunlock(&fs->fhash[hv]); }else{ + found: xqunlock(&fs->fhash[hv]); xqlock(&b->ldlk); /* make sure it's loaded */ xqunlock(&b->ldlk); } dMprint("mbload %D -> m%#p\n", addr, b); - if(locked){ - xqlock(&b->slk); - if(b->addr != addr){ - ainc(&fs->mbgetrace); - warn("mbload: %D renewed", b->addr); /* should not happen */ - xqunlock(&b->slk); - mbput(b); - goto again; - } - } return b; } Memblk* -mbload(daddrt addr, int locked) +mbload(daddrt addr) { - return xmbget(addr, locked, 1); + return xmbget(addr, 1); } Memblk* mbget(daddrt addr) { - return xmbget(addr, Unlocked, 0); -} - -Memblk* -mbgetlocked(daddrt addr) -{ - return xmbget(addr, Locked, 0); + return xmbget(addr, 0); } void @@ -377,15 +348,20 @@ if(b == nil) return; dMprint("mbput m%#p d%#010ullx pc=%#p\n", b, b->addr, getcallerpc(&b)); - if(decref(b) == 0) + if(decref(b) == 0){ mbfree(b); - else if(b->type != DBsuper){ - xqlock(&b->slk); - if(b->state == MBclean){ - /* dont "use" DBtag blocks with no free blocks */ - if(b->type != DBtag || b->d.tag[0] > 0) - mused(&fs->clean, b); /* lru */ + return; + } + if(b->type == DBsuper) + return; + xqlock(&b->slk); + if(b->state == MBclean){ + /* dont "use" DBtag blocks with no free blocks */ + if(b->type != DBtag || b->d.tag[0] > 0){ + mused(&fs->clean, b); /* lru */ + xqunlock(&b->slk); + return; } - xqunlock(&b->slk); } + xqunlock(&b->slk); } --- /sys/src/cmd/creepy/mkfile Mon May 14 11:06:20 2012 +++ /sys/src/cmd/creepy/mkfile Tue May 15 17:14:49 2012 @@ -16,12 +16,15 @@ attr.$O\ fsys.$O\ arch.$O\ + sweep.$O\ + write.$O\ IXOFILES=\ ixcall.$O\ ix.$O\ 9p.$O\ - cfg.$O\ + cons.$O\ + usr.$O\ fid.$O\ HFILES=\ --- /sys/src/cmd/creepy/net.h Mon May 14 11:06:20 2012 +++ /sys/src/cmd/creepy/net.h Tue May 15 17:14:49 2012 @@ -28,7 +28,7 @@ int omode; /* -1 if closed */ int rclose; int cflags; /* OCERR|OCEND */ - int consopen; /* for flush. has /cons open? */ + Channel *consrc; /* (of char*) != nil iff cons open for read */ int uid; uvlong loff; /* last offset, for dir reads */ --- /sys/src/cmd/creepy/rip.c Mon May 14 11:06:21 2012 +++ /sys/src/cmd/creepy/rip.c Tue May 15 17:14:49 2012 @@ -41,6 +41,7 @@ default: if(ARGC() >= 'A' && ARGC() <= 'Z' || ARGC() == '9'){ dbg[ARGC()] = 1; + dbg['d'] = 1; fatalaborts = 1; }else usage(); @@ -76,8 +77,9 @@ if(addr != nil) listen9pix(addr, noauth, cliworker9p); - consinit(); + consinit("rip> "); noerror(); + errterm(); threadexits(nil); } --- /sys/src/cmd/creepy/sweep.c Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/creepy/sweep.c Tue May 15 17:14:50 2012 @@ -0,0 +1,356 @@ +#include "all.h" + +/* + * free disk blocks, mark & sweep. + */ + +static void dfmark(daddrt addr, u64int e); + +static void +markdentries(void *p, int n, u64int e) +{ + int i; + daddrt *d; + + d = p; + for(i = 0; i < n; i++) + if(d[i] != 0) + dfmark(d[i], e); +} + +static void +dbmark(int type, daddrt addr, u64int e) +{ + Memblk *b; + int i; + u64int old; + + if(ISDADDR(addr)){ + old = dbsettag(addr, e); + if(old == e) /* already marked */ + return; /* prune */ + } + if(type == DBdata) + return; + b = dbget(type, addr); + if(catcherror()){ + mbput(b); + error(nil); + } + if(type == DBdirdata){ + markdentries(b->d.data, Dptrperblk, e); + goto done; + } + if((type&~DBdirflag) < DBptr0) + fatal("dbmark: type %s", tname(type)); + for(i = 0; i < Dptrperblk; i++) + if(b->d.ptr[i] != 0) + dbmark(type-1, b->d.ptr[i], e); +done: + noerror(); + mbput(b); +} + +static void +dfmark(daddrt addr, u64int e) +{ + Memblk *b; + int i, type; + ulong doff, sz; + u64int old; + + b = dbget(DBfile, addr); + xrlock(b->mf); + if(catcherror()){ + xrunlock(b->mf); + mbput(b); + error(nil); + } + + addr = b->addr; + if(ISDADDR(addr)){ + old = dbsettag(addr, e); + if(old == e) /* already marked */ + goto done; /* prune */ + } + + if(b->type == DBdir){ + doff = embedattrsz(b); + sz = Embedsz-doff; + markdentries(b->d.embed+doff, sz/Daddrsz, e); + } + + type = DBdata | (b->type&DBdirflag); + for(i = 0; i < nelem(b->d.dptr); i++) + if(b->d.dptr[i] != 0) + dbmark(type, b->d.dptr[i], e); + type = DBptr0 | (b->type&DBdirflag); + for(i = 0; i < nelem(b->d.iptr); i++) + if(b->d.iptr[i] != 0) + dbmark(type+i, b->d.iptr[i], e); +done: + noerror(); + xrunlock(b->mf); + mbput(b); +} + +/* + * Mark all blocks that could be reached with a new epoch, + * so blocks with lower epochs could be considered free. + */ +static void +fsmark(void) +{ + u64int e; + vlong t; + Channel *c; + daddrt addr; + + c = chancreate(sizeof(char*), 0); + warn("mark...\n"); + t = opstart(&fs->opstat[Opmark], 0); + nbsendul(fs->lruc, 0); /* make some room */ + + xqlock(&fs->superlk); + e = ++fs->super->d.epoch; + addr = fs->super->d.root; + xqunlock(&fs->superlk); + + if(catcherror()){ + warn("mark failed: %r"); + warn("not advancing fepoch due to errors"); + opend(&fs->opstat[Opmark], t); + return; + } + dfmark(addr, e); /* could use N procs for this */ + noerror(); + + xrlock(fs->active->mf); + if(catcherror()){ + xrunlock(fs->active->mf); + warn("active mark failed: %r"); + warn("not advancing fepoch due to errors"); + opend(&fs->opstat[Opmark], t); + return; + } + dfmark(fs->active->addr, e); + noerror(); + xrunlock(fs->active->mf); + + xqlock(&fs->archlk); + xqlock(&fs->superlk); + fs->super->d.fepoch = e-1; + xqunlock(&fs->superlk); + xqunlock(&fs->archlk); + + fswritetags(); + fswritesuper(c); + recvp(c); + chanfree(c); + + opend(&fs->opstat[Opmark], t); + warn("marked\n"); +} + +static daddrt +growworm(void) +{ + daddrt addr; + + xqlock(&fs->superlk); + if(fs->super->d.eaddr + Dblksz > fs->limit){ + xqunlock(&fs->superlk); + warnerror("worm full"); + } + addr = fs->super->d.eaddr; + fs->super->d.eaddr += Dblksz; + fs->ndfree--; + xqunlock(&fs->superlk); + return MKDADDR(addr); +} + +daddrt +newdaddr(void) +{ + Memblk *tb, *b; + daddrt addr0, eaddr, grpsz, a; + dtagt epoch; + int i; + + if(fs->mode == Worm) + return growworm(); + + grpsz = Dblksz*Dtagperblk; + xqlock(&fs->freelk); + + xqlock(&fs->superlk); + eaddr = fs->super->d.eaddr; + epoch = fs->super->d.epoch; + xqunlock(&fs->superlk); + addr0 = fs->ifree; + do{ + if(catcherror()){ + warn("newdaddr: %r"); + continue; + } + tb = dbget(DBtag, MKDADDR(fs->ifree)); + xqlock(&tb->slk); + for(i = 1; i < Dtagperblk; i++) + if(tb->d.tag[i] == 0) + break; + /* TODO: we can skip this one when tb->d.tag[0] == 0 */ + if(i == Dtagperblk) + goto next; + if(tb->state != MBmem){ + xqunlock(&tb->slk); + tbrenew(tb); + } + assert(tb->state == MBmem); + for(i = 1; i < Dtagperblk; i++){ + if(tb->d.tag[i] == 0){ + a = fs->ifree+i*Dblksz; + assert(a < eaddr); + tb->d.tag[i] = epoch; + tb->d.tag[0]--; + xqlock(&fs->superlk); + if(fs->ndfree == 0) + fatal("newdaddr: ndfree == 0"); + if(fs->ndfree < 30 || (fs->ndfree%50)== 0) + dAprint("newdaddr: %ulld-1\n", fs->ndfree); + fs->ndfree--; + noerror(); + xqunlock(&fs->superlk); + xqunlock(&tb->slk); + mbput(tb); + xqunlock(&fs->freelk); + if(dbg['d'] != 0 && (b=mbget(MKDADDR(a))) != nil) + fatal("newdaddr: free: %H", b); + if(fs->ndfree < Dminfree) + nbsendul(fs->sweepc, 0); /* get more */ + return MKDADDR(a); + } + } + next: + fs->ifree += grpsz; + if(fs->ifree >= eaddr) + fs->ifree = Dblk0addr; + noerror(); + xqunlock(&tb->slk); + mbput(tb); + }while(fs->ifree != addr0); + xqunlock(&fs->freelk); + + dAprint("newdaddr: %ulld free; asking sweep for more.", fs->ndfree); + ainc(&fs->newdaddrw); + nbsendul(fs->sweepc, 0); + error("disk full"); + return Noaddr; +} + +static void +dropold(daddrt addr) +{ + Memblk *b; + + b = mbget(addr); + if(b == nil) + return; + dAprint("dropold: reused: %D\n", addr); + if(b->ref != 2) + warn("dropold: refs: %H", b); + assert(b->ref > 1); /* hash and b */ + mbput(b); /* b */ + xqlock(&fs->clean); + if(mblru(b) < 0) + fatal("dropold: busy %H", b); + xqunlock(&fs->clean); +} + +void +sweepproc(void*) +{ + Memblk *tb; + daddrt addr0, eaddr, grpsz, a; + dtagt fepoch; + int i, n, nb, x; + vlong t; + + threadsetname("sweepproc"); + errinit(Errstack); + if(catcherror()) + fatal("%s: uncatched: %r", threadgetname()); + grpsz = Dblksz*Dtagperblk; + for(;;){ + x = recvul(fs->sweepc); + xrlock(&fs->dquiescence); + dZprint("sweep at %D with %ulld free\n", + MKDADDR(fs->isweep), fs->ndfree); + again: + t = opstart(&fs->opstat[Opsweep], 0); + xqlock(&fs->superlk); + eaddr = fs->super->d.eaddr; + fepoch = fs->super->d.fepoch; + xqunlock(&fs->superlk); + n = 0; + addr0 = fs->isweep; + do{ + nb = 0; + if(catcherror()){ + warn("sweep: %r"); + continue; + } + tb = dbget(DBtag, MKDADDR(fs->isweep)); + xqlock(&tb->slk); + for(i = 1; i < Dtagperblk; i++) + if(tb->d.tag[i] != 0 && tb->d.tag[i] <= fepoch) + break; + if(i == Dtagperblk) + goto next; + if(tb->state != MBmem){ + xqunlock(&tb->slk); + tbrenew(tb); + } + assert(tb->state == MBmem); + for(i = 1; i < Dtagperblk; i++) + if(tb->d.tag[i] != 0 && tb->d.tag[i] <= fepoch){ + a = fs->isweep+i*Dblksz; + assert(a < eaddr); + dAprint("sweep: free %D\n", MKDADDR(a)); + ainc(&fs->ngc); + nb++; + dropold(MKDADDR(a)); + tb->d.tag[i] = 0; + } + tb->d.tag[0] += nb; + n += nb; + next: + fs->isweep += grpsz; + if(fs->isweep >= eaddr) + fs->isweep = Dblk0addr; + noerror(); + if(nb > 0) + dAprint("sweep: %D: +%d free\n", tb->addr, nb); + xqlock(&fs->superlk); + fs->ndfree += nb; + if(fs->ndfree > fs->ndblk) + fatal("ndfree out of range: %ulld > %ulld", + fs->ndfree, fs->ndblk); + xqunlock(&fs->superlk); + xqunlock(&tb->slk); + mbput(tb); + if(fs->ndfree > Dminfree) + x |=nbrecvul(fs->sweepc); /* unlock others while */ + }while(fs->isweep != addr0); /* we scan for more */ + + opend(&fs->opstat[Opsweep], t); + dZprint("sweepproc: %d collected %ulld free\n", n, fs->ndfree); + if(fs->ndfree < Dmaxfree || x != 0){ + x = 0; + fsmark(); + goto again; + } + xrunlock(&fs->dquiescence); + nbsendul(fs->sweepec, 0); /* for cmd.c */ + } +} + --- /sys/src/cmd/creepy/tools.c Mon May 14 11:06:21 2012 +++ /sys/src/cmd/creepy/tools.c Tue May 15 17:14:50 2012 @@ -134,6 +134,22 @@ return canqlock(q); } +int +xcanwlock(RWLock *rw) +{ + Lstat *lst; + + if(lstats != nil){ + lst = getlstat(getcallerpc(&rw), Trwlock); + ainc(&lst->ntimes); + if(canwlock(rw)) + return 1; + ainc(&lst->ncant); + return 0; + } + return canwlock(rw); +} + static void xrwlock(RWLock *rw, int iswr) { @@ -227,17 +243,6 @@ xqunlock(a); } -ulong -asize(Alloc *a) -{ - ulong n; - - xqlock(a); - n = a->nfree; - xqunlock(a); - return n; -} - static void xaddelem(Path *p, Memblk *f) { @@ -261,7 +266,7 @@ return np; } -void +static void ownpath(Path **pp) { Path *p; @@ -403,7 +408,8 @@ void mlink(List *l, Memblk *b) { - assert(b->lnext == nil && b->lprev == nil); + if(b->lnext != nil || b->lprev != nil) + fatal("mlink: %H", b); xqlock(l); xmlinktl(l, b); xqunlock(l); --- /sys/src/cmd/creepy/usr.c Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/creepy/usr.c Tue May 15 17:14:50 2012 @@ -0,0 +1,448 @@ +#include "all.h" +/* + * Users and groups. + * + * Access checks are like those described in Plan 9's stat(5), but for: + * + * - to change gid, the group leader is not required to be a leader + * of the new group; it suffices if he's a member. + * - attempts to change muid are honored if the user is "allowed", + * and ignored otherwise. + * - "allowed" users can also set atime. + * - attributes other than length, name, uid, gid, muid, mode, atime, and mtime + * are ignored (no error raised if they are not void) + * + * + * The user file has the format: + * uid:name:leader:members + * uid is a number. + * + * This program insists on preserving uids already seen. + * That is, after editing /root/users, the server program will notice + * and re-read the file, then clean it up, and rewrite it. + * + * Cleaning ensures that uids for known users are kept as they were, and + * that users not yet seen get unique uids. Numeric uids are only an internal + * concept, the protocol uses names. + */ + +/* + * The uid numbers are irrelevant, they are rewritten. + */ +static char *defaultusers = + "1:none::\n" + "2:adm:adm:sys, elf \n" + "3:sys::glenda,elf\n" + "4:glenda:glenda:\n" + "5:elf:elf:sys\n"; + +static uint +usrhash(char* s) +{ + uchar *p; + uint hash; + + hash = 0; + for(p = (uchar*)s; *p != '\0'; p++) + hash = hash*7 + *p; + + return hash % Uhashsz; +} + +static int +findmember(Usr *u, int member) +{ + Member *m; + + for(m = u->members; m != nil; m = m->next) + if(member == m->u->id) + return 1; + return 0; +} + +Usr* +finduid(int uid) +{ + Usr *u; + + for(u = fs->uids[uid%Uhashsz]; u != nil; u = u->inext) + if(u->id == uid) + return u; + return nil; +} + +Usr* +finduname(char *name, int mkit) +{ + Usr *u; + uint h; + + h = usrhash(name); + for(u = fs->unames[h]; u != nil; u = u->nnext) + if(strcmp(u->name, name) == 0) + return u; + if(mkit){ + /* might be leaked. see freeusr() */ + u = mallocz(sizeof *u, 1); + strecpy(u->name, u->name+sizeof u->name, name); + u->nnext = fs->unames[h]; + fs->unames[h] = u; + } + return u; +} + +char* +usrname(int uid) +{ + Usr *u; + + xrlock(&fs->Usrs); + u = finduid(uid); + if(u == nil){ + xrunlock(&fs->Usrs); /* zero patatero: */ + return "ZP"; /* disgusting, isn't it? */ + } + xrunlock(&fs->Usrs); + return u->name; +} + +int +usrid(char *n) +{ + Usr *u; + + xrlock(&fs->Usrs); + u = finduname(n, Dontmk); + if(u == nil || !u->enabled){ + xrunlock(&fs->Usrs); + return -1; + } + xrunlock(&fs->Usrs); + return u->id; +} + +int +member(int uid, int member) +{ + Usr *u; + int r; + + if(uid == member) + return 1; + xrlock(&fs->Usrs); + u = finduid(uid); + r = u != nil && u->lead != nil && u->lead->id == member; + r |= u != nil && findmember(u, member); + xrunlock(&fs->Usrs); + return r; +} + +int +leader(int gid, int lead) +{ + Usr *u; + int r; + + xrlock(&fs->Usrs); + u = finduid(gid); + r = 0; + if(u != nil) + if(u->lead != nil) + r = u->lead->id == lead; + else + r = findmember(u, lead); + xrunlock(&fs->Usrs); + return r; +} + +static void +clearmembers(Usr *u) +{ + Member *m; + + while(u->members != nil){ + m = u->members; + u->members = m->next; + free(m); + } +} + +static void +addmember(Usr *u, char *n) +{ + Member *m, **ml; + + for(ml = &u->members; (m = *ml) != nil; ml = &m->next) + if(strcmp(m->u->name, n) == 0){ + warn("'%s' is already a member of '%s'", n, u->name); + return; + } + m = mallocz(sizeof *m, 1); + m->u = finduname(n, Mkit); + *ml = m; +} + +static void +checkmembers(Usr *u) +{ + Member *m, **ml; + + for(ml = &u->members; (m = *ml) != nil; ) + if(m->u->id == 0){ + warn("no user '%s' (member of '%s')", m->u->name, u->name); + *ml = m->next; + free(m); + }else + ml = &m->next; +} + +int +usrfmt(Fmt *fmt) +{ + Usr *usr; + Member *m; + + usr = va_arg(fmt->args, Usr*); + + if(usr == nil) + return fmtprint(fmt, "#no user"); + fmtprint(fmt, "%s%d:%s:", usr->enabled?"":"!", + usr->id, usr->name); + fmtprint(fmt, "%s:", usr->lead?usr->lead->name:""); + for(m = usr->members; m != nil; m = m->next){ + fmtprint(fmt, "%s", m->u->name); + if(m->next != nil) + fmtprint(fmt, ","); + } + return 0; +} + +static void +dumpusers(void) +{ + int i; + Usr *usr; + + for(i = 0; i < nelem(fs->uids); i++) + for(usr = fs->uids[i]; usr != nil; usr = usr->inext){ + fprint(2, "%A\n", usr); + } +} + +/* + * Add a user. + * A partial user entry might already exists, as a placeholder + * for the user name (if seen before in the file). + * If the user was known, it's uid is preserved. + * If not, a new unique uid is assigned. + */ +static Usr* +mkusr(char *name) +{ + Usr *u; + uint h; + + u = finduname(name, Mkit); + if(u->id == 0){ + /* first seen! */ + u->id = ++fs->uidgen; + h = u->id%Uhashsz; + u->inext = fs->uids[h]; + fs->uids[h] = u; + } + if(strcmp(name, "write") == 0) + fs->uwrite = u; + return u; +} + +static void +addusr(char *p) +{ + char *c, *nc, *s, *args[5]; + int nargs, on; + Usr *usr; + + on = 1; + if(*p == '!'){ + on = 0; + p++; + } + nargs = getfields(p, args, nelem(args), 0, ":"); + if(nargs != 4) + error("wrong number of fields %s", args[0]); + if(*args[1] == 0) + error("null name"); + usr = mkusr(args[1]); + usr->enabled = on; + usr->lead = finduname(args[2], Mkit); + clearmembers(usr); + for(c = args[3]; c != nil; c = nc){ + while(*c == ' ' || *c == '\t') + c++; + if(*c == 0) + break; + nc = utfrune(c, ','); + if(nc != nil) + *nc++ = 0; + s = utfrune(c, ' '); + if(s != nil) + *s = 0; + s = utfrune(c, '\t'); + if(s != nil) + *s = 0; + if(*c != 0) + addmember(usr, c); + } +} + +/* + * Absorb the new user information as read from u. + * Old users are not removed, but renamed to be disabled. + */ +static void +rwdefaultusers(void) +{ + char *u, *c, *p, *np; + static int once; + + if(once++ > 0) + return; + + u = strdup(defaultusers); + if(catcherror()){ + free(u); + error(nil); + } + p = u; + do{ + np = utfrune(p, '\n'); + if(np != nil) + *np++ = 0; + c = utfrune(p, '#'); + if(c != nil) + *c = 0; + if(*p == 0) + continue; + if(catcherror()){ + warn("users: %r\n"); + consprint("users: %r\n"); + continue; + } + addusr(p); + noerror(); + }while((p = np) != nil); + + if(dbg['d']){ + dprint("users:\n"); + dumpusers(); + dprint("\n"); + } + noerror(); + free(u); + +} + +/* + * This should be called at start time and whenever + * the user updates /active/adm/users, to rewrite it according to our + * in memory data base. + */ +void +rwusers(Memblk *uf) +{ + static char ubuf[512]; + char *p, *nl, *c; + uvlong off; + long tot, nr, nw; + int i; + Usr *usr; + + xwlock(&fs->Usrs); + if(catcherror()){ + warn("users: %r"); + goto update; + } + if(uf == nil){ + rwdefaultusers(); + xwunlock(&fs->Usrs); + return; + } + tot = 0; + p = nil; + for(off = 0; off < uf->d.length; off += nr){ + nr = dfpread(uf, ubuf + tot, sizeof ubuf - tot - 1, off); + tot += nr; + ubuf[tot] = 0; + for(p = ubuf; p != nil && *p != 0 && p - ubuf < tot; p = nl){ + nl = utfrune(p, '\n'); + if(nl == nil){ + tot = strlen(p); + memmove(ubuf, p, tot+1); + break; + } + *nl++ = 0; + c = utfrune(p, '#'); + if(c != nil) + *c = 0; + if(*p != 0) + addusr(p); + } + } + if(p != nil && *p != 0){ + warn("last line in users is not a full line"); + warn("[%s]", p); + } + noerror(); + if(uf->state == MBclean){ /* loaded at boot time */ + xwunlock(&fs->Usrs); + return; + } + +update: + if(catcherror()){ + xwunlock(&fs->Usrs); + warn("users: %r\n"); + return; /* what could we do? */ + } + wstatint(uf, "length", 0); + off = 0; + dprint("users updated:\n"); + for(i = 0; i < fs->uidgen; i++) + if((usr=finduid(i)) != nil){ + dprint("%A\n", usr); + p = seprint(ubuf, ubuf+sizeof ubuf, "%A\n", usr); + nw = dfpwrite(uf, ubuf, p - ubuf, &off); + off += nw; + } + noerror(); + xwunlock(&fs->Usrs); +} + +int +writedenied(int uid) +{ + int r; + + if(fs->uwrite == nil) + return 0; + xrlock(&fs->Usrs); + r = findmember(fs->uwrite, uid) == 0; + xrunlock(&fs->Usrs); + return r; +} + +int +allowed(int uid) +{ + Usr *u; + int r; + + xrlock(&fs->Usrs); + u = finduid(uid); + r = 0; + if(u) + r = u->allow; + xrunlock(&fs->Usrs); + return r; +} + --- /sys/src/cmd/creepy/write.c Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/creepy/write.c Tue May 15 17:14:51 2012 @@ -0,0 +1,291 @@ +#include "all.h" + +/* + * File system write. + */ + +static daddrt dfaddress(daddrt addr); + +static void +addressdentries(void *p, int n) +{ + int i; + daddrt *d; + + d = p; + for(i = 0; i < n; i++) + if(d[i] != 0) + d[i] = dfaddress(d[i]); +} + +static daddrt +address(Memblk *b) +{ + daddrt addr; + + ainc(&fs->naddress); + addr = newdaddr(); + dAprint("address %D: %H", addr, b); + mbunhash(b, 0); + xqlock(&b->slk); + b->addr = addr; + assert(b->state == MBmem); + mbset(b, MBout); + mlink(&fs->out, b); + xqunlock(&b->slk); + mbhash(b); + return b->addr; +} + +static daddrt +dbaddress(int type, daddrt addr) +{ + Memblk *b; + int i; + + if(ISDADDR(addr)) + return addr; + + b = mbget(addr); + if(b == nil) + fatal("dbaddress: mem %D not in hash", addr); + if((b->type&~DBdirflag) != (type&~DBdirflag)) + fatal("dbaddress: %D: type %s", addr, tname(b->type)); + if(b->state != MBmem) + fatal("dbaddress: %D: state %s", addr, sname(b->state)); + if(catcherror()){ + mbput(b); + error(nil); + } + + if(type == DBdirdata) + addressdentries(b->d.data, Dptrperblk); + else if(type != DBdata){ + if((type&~DBdirflag) < DBptr0) + fatal("dbaddress: %D: type %s", b->addr, tname(type)); + for(i = 0; i < Dptrperblk; i++) + if(b->d.ptr[i] != 0) + b->d.ptr[i] = dbaddress(type-1, b->d.ptr[i]); + } + + noerror(); + addr = address(b); + mbput(b); + return addr; +} + +/* + * Give addresses to all mutable blocks reachable from addr. + */ +static daddrt +dfaddress(daddrt addr) +{ + Memblk *b; + int i, type; + ulong doff, sz; + + if(ISDADDR(addr)) + return addr; + + b = dbget(DBfile, addr); + if(b->type != DBfile && b->type != DBdir) + fatal("dfaddress: %D: type %s", addr, tname(b->type)); + xwlock(b->mf); + if(catcherror()){ + xwunlock(b->mf); + mbput(b); + error(nil); + } + if(ISDADDR(b->addr)){ + addr = b->addr; + goto done; + } + if(b->type&DBdirflag){ + doff = embedattrsz(b); + sz = Embedsz-doff; + addressdentries(b->d.embed+doff, sz/Daddrsz); + } + type = DBdata | (b->type&DBdirflag); + for(i = 0; i < nelem(b->d.dptr); i++) + if(b->d.dptr[i] != 0) + b->d.dptr[i] = dbaddress(type, b->d.dptr[i]); + type = DBptr0 | (b->type&DBdirflag); + for(i = 0; i < nelem(b->d.iptr); i++) + if(b->d.iptr[i] != 0) + b->d.iptr[i] = dbaddress(type+i, b->d.iptr[i]); + + addr = address(b); +done: + noerror(); + xwunlock(b->mf); + mbput(b); + nbsendul(fs->syncc, 0); + return addr; +} + +void +fswritesuper(Channel *c) +{ + Memblk *b; + Memblk *super; + + super = fs->super; + b = mballocz(1); + xqlock(&fs->superlk); + b->addr = fs->super->addr; + b->type = super->type; + b->addr = super->addr; + xqlock(&b->slk); + mbset(b, MBout); + b->d.type = DBsuper; + b->d.Dsuperdata = fs->super->d.Dsuperdata; + b->d.dup = b->d.Dsuperdata; + b->wc = c; + xqunlock(&fs->superlk); + mlink(&fs->out, b); + xqunlock(&b->slk); + nbsendul(fs->syncc, 0); +} + +/* + * The mark & sweep is dumb, slow, and reliable. + * It should happen only when the disk is almost full. + * fsmark and sweep may run concurrently. + */ +void +fswritetags(void) +{ + Memblk *b; + + while((b = munlink(&fs->dtags, nil)) != nil){ + xqlock(&b->slk); + mbset(b, MBout); + mlink(&fs->out, b); + xqunlock(&b->slk); + } +} + +/* + * Freeze the fs state and schedule it for writing. + * A new super with the new / is written at the end. + * + * If there's an error we set fs->werr, which disables writing + * for the entire file system. + * TODO: If it's because of a disk-full, we should probably + * ignore werr in fidremove(), and clear werr after a successful remove. + */ +void +fswrite(Channel *c) +{ + vlong t; + daddrt addr, oaddr; + char buf[128]; + int ntry; + + dZprint("fswrite...\n"); + t = opstart(&fs->opstat[Opwriteq], 0); + t = opend(&fs->opstat[Opwriteq], t); + t = opstart(&fs->opstat[Opwrite], t); + xrlock(&fs->dquiescence); + if(catcherror()){ + xrunlock(&fs->dquiescence); + opend(&fs->opstat[Opwrite], t); + dZprint("fswrite: %r\n"); + fs->werr = smprint("%r"); + error(nil); + } + ainc(&fs->nwrite); + for(ntry = 0; ntry < Nscanfree; ntry++){ + xwlock(fs->root->mf); + if(catcherror()){ + xwunlock(fs->root->mf); + nbsendul(fs->syncc, 0); + if(ntry > 1) + sendul(fs->sweepc, 0); + else + nbsendul(fs->sweepc, 0); + rerrstr(buf, sizeof buf); + if(strstr(buf, "disk full") != nil){ + warn("write: waiting for addresses..."); + sleep(1000); + continue; + } + error(nil); + } + oaddr = fs->active->addr; + addr = dfaddress(oaddr); + dfchdentry(fs->root, oaddr, addr); + noerror(); + xwunlock(fs->root->mf); + goto success; + } + addr = Noaddr; + error("disk full"); + +success: + xqlock(&fs->superlk); + fs->super->d.root = addr; + xqunlock(&fs->superlk); + fswritetags(); + fswritesuper(c); + + noerror(); + xrunlock(&fs->dquiescence); + opend(&fs->opstat[Opwrite], t); + dZprint("fswrite: done\n"); +} + +/* + * Write blocks to disk, including copies for the super. + */ +void +syncproc(void*) +{ + vlong t; + Memblk *b; + int n; + + threadsetname("syncproc"); + errinit(Errstack); + if(catcherror()) + fatal("%s: uncatched: %r", threadgetname()); + for(;;){ + recvul(fs->syncc); + dZprint("sync...\n"); + xrlock(&fs->dquiescence); + t = opstart(&fs->opstat[Opsync], 0); + for(n = 0; (b = munlink(&fs->out, nil)) != nil; n++){ + if(catcherror()){ + warn("write %D: %r", b->addr); + if(b->type == DBsuper){ + if(b->wc != nil) + sendp(b->wc, smprint("%r")); + b->wc = nil; + mbput(b); + }else + mbset(b, MBerr); + xrunlock(&fs->dquiescence); + continue; + } + + dbwrite(b); + + noerror(); + if(b->type == DBsuper){ + if(b->wc != nil) + sendp(b->wc, nil); + b->wc = nil; + mbput(b); + }else{ + xqlock(&b->slk); + mbset(b, MBclean); + mlink(&fs->clean, b); + xqunlock(&b->slk); + } + } + opend(&fs->opstat[Opsync], t); + dZprint("sync: wrote %d blocks\n", n); + xrunlock(&fs->dquiescence); + } +} +