Sorry again. This should be it. Reference: /n/patches.lsub.org/patch/morecreepcons Date: Mon May 21 11:41:20 CES 2012 Signed-off-by: nemo@lsub.org # rm /sys/src/cmd/creepy/fscmd.c # rm /sys/src/cmd/creepy/fsfmt.c --- /sys/src/cmd/creepy/arch.c Mon May 21 11:20:10 2012 +++ /sys/src/cmd/creepy/arch.c Mon May 21 10:46:50 2012 @@ -4,7 +4,6 @@ * file system archival into a worm tree */ - static Dir* dfind(Dir *d, int nd, char *name) { @@ -114,7 +113,7 @@ int fd; dVprint("archmkdir %N\n", dir); - fd = create(dir, OREAD, 0555|DMDIR); + fd = create(dir, OREAD, 0775|DMDIR); if(fd < 0) error("create %N: %r", dir); close(fd); @@ -261,8 +260,10 @@ error("%s: open: %r", dir); nd = dirreadall(fd, &d); close(fd); - if(nd <= 0) - error("%s: read: %r", dir); + if(nd == 0) + error("%s: empty", dir); + if(nd < 0) + error("%s: dirread: %r", dir); last = 0; for(i = 0; i < nd; i++) if(d[last].mtime < d[i].mtime) @@ -325,16 +326,22 @@ for(n = 0; access(dir, AEXIST) == 0; n++) seprint(s, e, ".%d", n); - seprint(last, e, "%s/root/%s", fs->archdir, fs->archname); - lastdir(last, e); /* yyyy */ - lastdir(last, e); /* mmdd */ - linkarch(last, dir); - if(chdir(dir) < 0) - error("chdir %s: %r", dir); + if(!catcherror()){ + seprint(last, e, "%s/root/%s", fs->archdir, fs->archname); + lastdir(last, e); /* yyyy */ + lastdir(last, e); /* mmdd */ + linkarch(last, dir); + noerror(); + } + if(chdir(dir) < 0){ + mkdirp(dir); + if(chdir(dir) < 0) + error("chdir %s: %r", dir); + } warn("archiving at %s...", dir); } -void +static void fsarchive(void) { daddrt addr; @@ -344,10 +351,6 @@ xqlock(&fs->superlk); addr = fs->super->d.root; xqunlock(&fs->superlk); - if(catcherror()){ - warn("archive: %r"); - return; - } cdarchdir(); @@ -366,6 +369,67 @@ noerror(); xrunlock(b->mf); mbput(b); - noerror(); dVprint("fsarchive: done\n"); +} + +static void +usage(void) +{ + fprint(2, "usage: %s [-DFLAGS] dir\n", argv0); + threadexits("usage"); +} + +int mainstacksize = Stack; + +void +threadmain(int argc, char *argv[]) +{ + char *dir, *name; + daddrt z; + + threadsetname("arch"); + ARGBEGIN{ + case 'd': + dbg['d'] = 1; + break; + default: + if((ARGC() >= 'A' && ARGC() <= 'Z') || ARGC() == '9'){ + dbg[ARGC()] = 1; + dbg['d'] = 1; + fatalaborts = 1; + }else + usage(); + }ARGEND; + if(argc != 2) + usage(); + dir = argv[0]; + name = argv[1]; + + outofmemoryexits(1); + workerthreadcreate = proccreate; + fmtinstall('H', mbfmt); + fmtinstall('M', dirmodefmt); + fmtinstall('F', fcallfmt); + fmtinstall('G', ixcallfmt); + fmtinstall('X', fidfmt); + fmtinstall('R', rpcfmt); + fmtinstall('A', usrfmt); + fmtinstall('P', pathfmt); + fmtinstall('D', daddrfmt); + fmtinstall('N', namefmt); + + z = 0; + errinit(Errstack); + if(catcherror()) + fatal("%r"); + + rfork(RFNAMEG|RFNOTEG); + fsopen(nil, Archer, 0); + fs->archdir = dir; + fs->archname = name; + fsarchive(); + write(0, &z, sizeof z); + noerror(); + errterm(); + threadexitsall(nil); } --- /sys/src/cmd/creepy/cmd.c Mon May 21 11:20:10 2012 +++ /sys/src/cmd/creepy/cmd.c Mon May 21 10:33:08 2012 @@ -326,7 +326,7 @@ static void fschk(int, char*[]) { - fscheck(); + fscheck(Disk); } static void --- /sys/src/cmd/creepy/conf.h Mon May 21 11:20:11 2012 +++ /sys/src/cmd/creepy/conf.h Mon May 21 10:33:08 2012 @@ -24,6 +24,7 @@ #endif Incr = 16, Wormmem = 256UL*MiB, /* size for worm in-memory block array */ + Rammem = 64*MiB, /* size for archer mem */ Syncival = 5*60, /* desired sync intervals (s) */ Mminfree = 200, /* # blocks when low on mem blocks */ Dminfree = 2000, /* # blocks when low on disk blocks */ @@ -35,8 +36,8 @@ * Caution: Stack and Errstack also limit the max tree depth, * because of recursive routines (in the worst case). */ - Stack = 128*KiB, /* stack size for threads */ - Errstack = 128, /* max # of nested error labels */ + Stack = 256*KiB, /* stack size for threads */ + Errstack = 256, /* max # of nested error labels */ Npathels = 64, /* max depth (only used by walkto) */ Fhashsz = 7919, /* size of file hash (plan9 has 35454 files). */ --- /sys/src/cmd/creepy/cons.c Tue May 15 17:14:44 2012 +++ /sys/src/cmd/creepy/cons.c Mon May 21 10:33:08 2012 @@ -385,15 +385,19 @@ } static void -cmark(int, char*[]) +cmark(int, char *argv[]) { - sendul(fs->sweepc, 1); + if(strcmp(argv[0], "markmem") == 0) + sendul(fs->sweepc, 2); + else + sendul(fs->sweepc, 1); } + static void -ccheck(int, char*[]) +ccheck(int, char *argv[]) { - if(fscheck() == 0) + if(fscheck(strcmp(argv[0], "memcheck") == 0 ? Mem : Disk) == 0) consprint("check passed\n"); else consprint("check failed\n"); @@ -414,6 +418,7 @@ {"arch", carch, 0, "arch [name hour [path] | now | none]", 1}, {"link", clink, 3, "link file dir", 1}, /* these will go in the future */ + {"memcheck", ccheck, 1, "memcheck"}, {"check", ccheck, 1, "check"}, {"debug", cdebug, 0, "debug [[+-]FLAGS | on | off]"}, {"fids", cfids, 1, "fids"}, @@ -421,6 +426,7 @@ {"rerr", crwerr, 2, "rerr n"}, {"werr", crwerr, 2, "werr n"}, {"mark", cmark, 1, "mark"}, + {"markmem", cmark, 1, "markmem"}, {"?", chelp, 1, "?"}, }; @@ -444,7 +450,7 @@ xconsctl(char *p, int raise) { char *args[5]; - int nargs, i; + int nargs, i, nerr; nargs = tokenize(p, args, nelem(args)); if(nargs < 1) @@ -467,7 +473,10 @@ }else{ if(raise && cmds[i].isctl == 0) error("command not available as a ctl"); + nerr = errstacksize(); cmds[i].f(nargs, args); + if(nerr != errstacksize()) + fatal("%s: unbalanced error stack", cmds[i].name); } noerror(); xrlock(&fs->dquiescence); --- /sys/src/cmd/creepy/dbg.c Mon May 21 11:20:12 2012 +++ /sys/src/cmd/creepy/dbg.c Mon May 21 10:33:08 2012 @@ -12,6 +12,7 @@ [Normal] "normal", [Rdonly] "rdonly", [Worm] "worm", + [Archer] "archer", }; if(m < 0 || m >= nelem(nms)) @@ -96,11 +97,11 @@ va_start(arg, fmt); s = vsmprint(fmt, arg); va_end(arg); - if(fs != nil && fs->dev != nil) + if(fs != nil && fs->dev != nil){ + fs->werr = s; fprint(2, "%s: %s: fatal: %s\n", argv0, fs->dev, s); - else + }else fprint(2, "%s: fatal: %s\n", argv0, s); - free(s); if(fatalaborts) abort(); threadexitsall("fatal"); @@ -138,7 +139,7 @@ error(err); } -static int +int validaddr(daddrt addr) { if(addr == 0) @@ -176,6 +177,8 @@ warn("checkblk: %r:\n%H", b); error(nil); } + if(b->type == DBfree) /* archer reads blocks without knowing their type */ + b->type = b->d.type; if((b->type&~DBdirflag) != (b->d.type&~DBdirflag)) error("type mismatch %s != %s", tname(b->type), tname(b->d.type)); type = b->type & ~DBdirflag; @@ -410,6 +413,8 @@ fmtprint(fmt, "\n"); break; case DBtag: + fmtprint(fmt, " %ud free", b->d.tag[0]); + if(0) for(i = n = 0; i < Dtagperblk; i++){ if(b->d.tag[i] == 0) continue; @@ -432,10 +437,13 @@ fmtprint(fmt, " nr%d nw%d prev %D\n", b->mf->readers, b->mf->writer, b->d.prev); fmttab(fmt, mbtab, 0); + if(catcherror()) + break; fmtprint(fmt, " %M %s %s %s len %ulld ndents %ulld\n", (ulong)b->d.mode, usrname(b->d.uid), usrname(b->d.gid), usrname(b->d.muid), b->d.length, b->d.ndents); + noerror(); mbtab++; if(b->d.mode&DMDIR) dumpdirdata(fmt, b); @@ -530,6 +538,7 @@ if(de[i] != 0){ if(catcherror()){ warn("%D: %r", de[i]); + de[i] = 0; /* fix */ continue; } if(isdisk) @@ -542,8 +551,8 @@ } dfdump(f, isdisk, justchk); noerror(); - noerror(); mbput(f); + noerror(); } off += sl.len; noerror(); @@ -597,7 +606,7 @@ } fprint(2, "\n"); } - if(0){ + if(1){ mlistdump("dtags", &fs->dtags); mlistdump("clean", &fs->clean); mlistdump("out", &fs->out); @@ -617,7 +626,7 @@ } long -fscheck(void) +fscheck(int isdisk) { Memblk *b; long err, i, n; @@ -652,40 +661,66 @@ n = 0; for(i = 0; i < nelem(fs->fhash); i++) for(b = fs->fhash[i].b; b != nil; b = b->next){ - if(b->ref > 1) - dprint("hash used %H", b); n++; + if(b->type == DBsuper || b->type == DBtag) + continue; + if(b->ref > 1 && !builtin(b) && b != fs->active) + dprint("hash used %H", b); + if(ISDADDR(b->addr)){ + if(catcherror()) + continue; + if(dbgettag(b->addr) <= fs->super->d.fepoch) + warn("hash free %H", b); + noerror(); + } } if(n != fs->mballoc.nalloc - fs->mballoc.nfree){ err++; warn("%ld hashed != %uld blocks - %uld free", n, fs->mballoc.nalloc, fs->mballoc.nfree); } + n = fs->nmblk + fs->clean.n + fs->out.n + 1; /* +1 for super */ + if(n != fs->mballoc.nalloc - fs->mballoc.nfree){ + err++; + warn("%8ulld blocks = %8ulld dirty + %8uld clean" + "+ %#4uld out\n != %uld allocated", + fs->nmblk + fs->clean.n + 1 + fs->out.n, + fs->nmblk, fs->clean.n + 1, fs->out.n, + fs->mballoc.nalloc - fs->mballoc.nfree); + } for(b = fs->blk; b < fs->blk + fs->mballoc.nalloc; b++){ switch(b->state){ case MBfree: case MBerr: continue; case MBclean: - if(b->type != DBsuper) - if(b->lnext == nil && b->lprev == nil && b != fs->clean.hd){ + if(b->type == DBsuper || b->type == DBtag) + break; + if(!mlinked(&fs->clean, b)){ err++; - warn("clean miss: %H", b); + warn("clean: unlinked: %H", b); } break; case MBout: - if(b->type != DBsuper) - if(b->lnext == nil && b->lprev == nil && b != fs->out.hd){ + if(b->type == DBsuper) + break; + if(!mlinked(&fs->out, b)){ err++; - warn("out miss: %H", b); + warn("out: unlinked: %H", b); } break; case MBmem: - if(b->type != DBsuper && b->type != DBtag) + if(b->type == DBsuper || b->type == DBtag) + break; if(b->addr != (uintptr)b){ err++; warn("bad addr: %H", b); } + if(b->lnext != nil || b->lprev != nil || + b == fs->clean.hd || b == fs->out.hd){ + err++; + warn("mem: linked: %H", b); + } break; case MBin: case MBlru: @@ -700,33 +735,35 @@ checkblk(b); noerror(); } - if(err != 0) + if(err != 0){ warn("mem check: %ld errors", err); - else + fsdump(1, Mem); + }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"); + if(isdisk){ + 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; + fs->checking = 1; + 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; + fs->checking = 0; + xwunlock(&fs->dquiescence); } - fullfiledumps = x; - xwunlock(&fs->dquiescence); return err; } --- /sys/src/cmd/creepy/dblk.c Mon May 21 11:20:12 2012 +++ /sys/src/cmd/creepy/dblk.c Mon May 21 10:33:08 2012 @@ -80,7 +80,41 @@ return old; } -static long +/* + * Read the block by asking through stdin to the actual fs for + * the given block. Used by archer. + */ +long +abread(Memblk *b) +{ + static int nr; + long tot, n; + uchar *p; + daddrt addr; + + if(write(0, &b->addr, sizeof b->addr) != sizeof b->addr) + error("abread: write: %r"); + p = (uchar*)&b->d; + for(tot = 0; tot < Dblksz; tot += n){ + if(fs->swreaderr != 0 && ++nr > fs->swreaderr) + warnerror("dbread: sw fault"); + n = pread(0, p+tot, Dblksz-tot, addr+tot); + if(n == 0) + werrstr("abread: disk truncated"); + if(n <= 0) + warnerror("abread: %D: %r", b->addr); + } + assert(tot == sizeof b->d && tot == Dblksz); + checkblk(b); + dRprint("abread %D %H\n", b->addr, b); + if(b->d.type == DBfree){ + b->state = MBerr; + warnerror("%s", (char*)b->d.data); + } + return tot; +} + +long dbread(Memblk *b) { static int nr; @@ -100,7 +134,7 @@ warnerror("dbread: sw fault"); n = pread(fs->fd, p+tot, Dblksz-tot, addr+tot); if(n == 0) - werrstr("disk truncated"); + werrstr("dbread: disk truncated"); if(n <= 0) warnerror("dbread: %D: %r", b->addr); } @@ -117,6 +151,9 @@ static int nw; daddrt addr; + if(fs->mode == Archer) + fatal("dbwrite: archer bug"); + dWprint("dbwrite %D %H\n", b->addr, b); okdiskaddr(b->addr); if(b->state != MBout) @@ -178,7 +215,10 @@ error(nil); } - dbread(b); + if(fs->mode == Archer) + abread(b); + else + dbread(b); assert((b->type&~DBdirflag) == (b->d.type&~DBdirflag)); b->type = b->d.type; @@ -198,7 +238,7 @@ if(n > 0){ xqlock(&fs->superlk); fs->ndfree += n; - dZprint("dbget: %D: +%d free:\n", b->addr, n); + dZprint("dbget: %D: +%d free\n", b->addr, n); xqunlock(&fs->superlk); } b->d.tag[0] = n; @@ -208,6 +248,7 @@ if(b->type != DBsuper) mlink(&fs->clean, b); xqunlock(&b->ldlk); + noerror(); return b; } --- /sys/src/cmd/creepy/dk.h Mon May 21 11:20:13 2012 +++ /sys/src/cmd/creepy/dk.h Mon May 21 10:33:08 2012 @@ -44,6 +44,7 @@ Normal = 0, Rdonly, Worm, + Archer, Mem = 0, Disk, @@ -498,7 +499,6 @@ uvlong nmblk; /* # of DBmem blocks */ QLock freelk; /* one newdaddr() at a time */ - daddrt isweep; /* next group to sweep for free blocks */ daddrt ifree; /* next group to scan for free blocks */ uvlong ndfree; /* aprox. # of free disk blocks */ @@ -525,6 +525,7 @@ Fsstat; RWLock dquiescence; /* any activity rlocks; check wlocks. */ + int checking; /* !0 if disk fscheck() in progress */ uvlong atime; /* updated on each request */ --- /sys/src/cmd/creepy/fblk.c Mon May 21 11:20:13 2012 +++ /sys/src/cmd/creepy/fblk.c Mon May 21 10:33:08 2012 @@ -136,19 +136,108 @@ return b; } +static void dfdrop(daddrt addr); + +static void +drop(Memblk *b) +{ + assert(b->ref >= 2); /* hash and b */ + mbunhash(b, 0); + mbput(b); /* hash */ + assert(b->ref >= 1); + if(b->ref > 1) + warn("drop: refs: %H", b); + mbput(b); /* b */ +} + +static void +dropdentries(void *p, int n) +{ + int i; + daddrt *d; + + d = p; + for(i = 0; i < n; i++) + if(d[i] != 0){ + dfdrop(d[i]); + d[i] = 0; + } +} + +static void +dbdrop(daddrt addr) +{ + Memblk *b; + int i; + + if(ISDADDR(addr)) + return; + b = mbget(addr); + if(b == nil) + return; + if(b->state != MBmem){ + mbput(b); + return; + } + if(!catcherror()){ + if(b->type == DBdirdata) + dropdentries(b->d.data, Dptrperblk); + else if((b->type&~DBdirflag) >= DBptr0) + for(i = 0; i < Dptrperblk; i++) + if(b->d.ptr[i] != 0){ + dbdrop(b->d.ptr[i]); + b->d.ptr[i] = 0; + } + noerror(); + } + drop(b); +} + +static void +dfdrop(daddrt addr) +{ + Memblk *b; + + if(ISDADDR(addr)) + return; + b = mbget(addr); + if(b == nil) + return; + if(b->state != MBmem){ + mbput(b); + return; + } + if(!catcherror()){ + dftruncate(b); + noerror(); + } + drop(b); +} + void dftruncate(Memblk *f) { - ulong doff; + ulong doff, sz; + int i; if(f->state != MBmem) fatal("dftruncate: %D: state %s", f->addr, sname(f->state)); f->d.length = 0; doff = embedattrsz(f); - if(doff < Embedsz) - memset(f->d.embed+doff, 0, Embedsz-doff); - memset(f->d.dptr, 0, sizeof f->d.dptr); - memset(f->d.iptr, 0, sizeof f->d.iptr); + if(f->type == DBdir && doff < Embedsz){ + sz = Embedsz - doff; + dropdentries(f->d.embed+doff, sz/Daddrsz); + } + for(i = 0; i < nelem(f->d.dptr); i++) + if(f->d.dptr[i] != 0){ + dbdrop(f->d.dptr[i]); + f->d.dptr[i] = 0; + } + for(i = 0; i < nelem(f->d.iptr); i++) + if(f->d.iptr[i] != 0){ + dbdrop(f->d.iptr[i]); + f->d.iptr[i] = 0; + } } /* @@ -373,11 +462,15 @@ void dfremove(Memblk *p, Memblk *f) { + daddrt addr; + if(f->d.ndents > 0 && fs->mode != Worm) /* Worm can rm a tree */ error("directory not empty"); dfchdentry(p, f->addr, 0); + addr = f->addr; xwunlock(f->mf); mbput(f); + dfdrop(addr); } /* --- /sys/src/cmd/creepy/fid.c Mon May 21 11:20:14 2012 +++ /sys/src/cmd/creepy/fid.c Mon May 21 10:33:08 2012 @@ -346,11 +346,9 @@ dfwattr(f, name, buf); } -void -fidcanwrite(Fid *fid) +static void +fidcanrm(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) @@ -359,6 +357,13 @@ error("user can't write"); } +void +fidcanwrite(Fid *fid) +{ + if(fs->werr != nil && fid->p->f[fid->p->nf-1] != fs->cons) + error("%s", fs->werr); + fidcanrm(fid); +} /* * Does not check if the user can't write because of the "write" @@ -786,13 +791,14 @@ { Memblk *f, *fp; Path *p; + char *e; xqlock(fid); if(catcherror()){ xqunlock(fid); error(nil); } - fidcanwrite(fid); + fidcanrm(fid); /* fidcanwrite, ignoring disk full */ p = fid->p; f = p->f[p->nf-1]; if(builtin(f)) @@ -823,6 +829,9 @@ fid->p = nil; noerror(); xqunlock(fid); + e = fs->werr; + if(e != nil && strstr(e, "disk full") != 0) + nbsendul(fs->sweepc, 1); /* force a mark & sweep */ } void --- /sys/src/cmd/creepy/fmt.c Mon May 21 11:20:14 2012 +++ /sys/src/cmd/creepy/fmt.c Mon May 21 10:33:08 2012 @@ -146,7 +146,7 @@ warn("%lld %ldK %s blocks\n", fs->ndblk, Dblksz/1024, mname(fs->mode)); if(dbg['d']){ fsdump(1, Disk); - fscheck(); + fscheck(Mem); } noerror(); errterm(); --- /sys/src/cmd/creepy/fns.h Mon May 21 11:20:15 2012 +++ /sys/src/cmd/creepy/fns.h Mon May 21 10:33:08 2012 @@ -1,3 +1,4 @@ +extern long abread(Memblk *b); extern Path* addelem(Path **pp, Memblk *f); extern void afree(Alloc *a, void *nd); extern int allowed(int uid); @@ -20,6 +21,7 @@ extern long conswrite(char *ubuf, long count); extern int daddrfmt(Fmt *fmt); extern Memblk* dbget(int type, daddrt addr); +extern long dbread(Memblk *b); extern u64int dbsettag(daddrt addr, u64int e); extern long dbwrite(Memblk *b); extern void dfcattr(Memblk *f, int op, char *name, char *val); @@ -53,10 +55,10 @@ 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); +extern long fscheck(int isdisk); extern void fsdump(int full, int disktoo); extern void fsfmt(char *dev, int isworm); +extern void fslru(void); extern void fsopen(char *dev, int mode, usize fsysmem); extern void fspolicy(void); extern void fssync(void); @@ -91,6 +93,7 @@ extern int member(int uid, int member); extern int member(int uid, int member); extern void mlink(List *l, Memblk *b); +extern int mlinked(List *l, Memblk *b); extern void mlistdump(char *tag, List *l); extern char* mname(int m); extern Memblk* munlink(List *l, Memblk *b); @@ -136,6 +139,7 @@ extern char* usrname(int uid); extern char* usrname(int); extern char* usrname(int); +extern int validaddr(daddrt addr); extern Path* walkto(char *a, char **lastp); extern void warn(char *fmt, ...); extern void warnerror(char *fmt, ...); --- /sys/src/cmd/creepy/fsys.c Mon May 21 11:20:15 2012 +++ /sys/src/cmd/creepy/fsys.c Mon May 21 10:33:08 2012 @@ -6,7 +6,6 @@ Fsys *fs; - /* * Caller wants to write the file represented by path[0:nth]. * Make sure it's MBmem and wlocked for writing. @@ -185,23 +184,46 @@ sendul(fs->lruc, 0); else if(n < Mminfree) nbsendul(fs->lruc, 0); - n = fs->ndfree; - if(n < Dzerofree) - sendul(fs->sweepc, 0); - else if(n < Dminfree) + if(fs->ndfree < Dmaxfree) nbsendul(fs->sweepc, 0); } +void +fslru(void) +{ + Memblk *b, *bnext; + int n, ign; + vlong t; + + dZprint("lru...\n"); + t = opstart(&fs->opstat[Oplru], 0); + xqlock(&fs->clean); + n = ign = 0; + for(b = fs->clean.hd; b != nil; b = bnext){ + bnext = b->lnext; + if(b->state != MBclean) + fatal("fslru: %D: %s", b->addr, sname(b->state)); + if(b->ref > 1 || b->type == DBtag){ + ainc(&fs->nlruign); + ign++; + continue; + } + if(mblru(b) != -1) + n++; /* could move it out */ + if(fs->mballoc.nfree > Mmaxfree) + break; + } + xqunlock(&fs->clean); + opend(&fs->opstat[Oplru], t); + dZprint("lru: %uld free %d out %d ign\n", fs->mballoc.nfree, n, ign); +} + /* * Move blocks out of memory to release some. */ static void lruproc(void*) { - vlong t; - int n, ign; - Memblk *b, *bnext; - threadsetname("lruproc"); errinit(Errstack); if(catcherror()) @@ -209,44 +231,175 @@ for(;;){ recvul(fs->lruc); xrlock(&fs->dquiescence); - dZprint("lru...\n"); - t = opstart(&fs->opstat[Oplru], 0); - xqlock(&fs->clean); - n = ign = 0; - for(b = fs->clean.hd; b != nil; b = bnext){ - bnext = b->lnext; - if(b->state != MBclean) - fatal("fslru: %D: %s", b->addr, sname(b->state)); - if(b->ref > 1 || b->type == DBtag){ - ainc(&fs->nlruign); - ign++; - continue; - } - if(mblru(b) != -1) - n++; /* could move it out */ - if(fs->mballoc.nfree > Mmaxfree) - break; - } - xqunlock(&fs->clean); - opend(&fs->opstat[Oplru], t); - dZprint("lru: %uld free %d out %d ign\n", fs->mballoc.nfree, n, ign); + fslru(); xrunlock(&fs->dquiescence); } } +static Channel *twc; +static Ioproc *tio; + +static void +archer(void *a) +{ + int *p; + + p = a; + threadsetname("archer"); + dVprint("archer: creepy/arch %s\n", fs->archdir); + dup(p[0], 0); + close(p[0]); + close(p[1]); + dup(create("/tmp/archlog", OWRITE, 0664), 2); + dup(2, 1); + procexecl(twc, "/bin/creepy/arch", + "creepy/arch", "-DVZ", fs->archdir, fs->archname, nil); + warn("archer: creepy/arch %s: %r\n", fs->archdir); + threadexits("exec failed"); +} + +static void +waitproc(void *x) +{ + Waitmsg *m; + ulong pid; + Alt a[] = { + {x, &m, CHANRCV}, + {twc, &pid, CHANRCV}, + {nil, nil, CHANEND} + }; + + threadsetname("waitproc"); + pid = 0; + m = nil; + for(;;) + switch(alt(a)){ + case 0: + if(m->pid == pid){ + if(m->msg[0] != 0){ + warn("archer failed: %s", m->msg); + if(tio) + iointerrupt(tio); + } + dVprint("waitproc: pid %uld '%s'\n", pid, m->msg); + free(m); + goto done; + } + dprint("waitproc: ??? %uld '%s'\n", pid, m->msg); + free(m); + break; + case 1: + if(pid == ~0){ + dVprint("waitproc: exec failed\n"); + if(tio) + iointerrupt(tio); + goto done; + } + dVprint("waitproc: waiting for %uld\n", pid); + break; + } +done: + dVprint("waitproc exiting\n"); + threadexits(nil); +} + +/* + * fire up archer and serve any blocks it wants through its stdin. + * The protocol is: + * <- daddrt + * -> disk block; or DBfree block with errstr in data (\0 terminated). + * ... + * <- 0 daddrt + */ static void archproc(void*) { + int fd[2]; + daddrt addr; + Memblk *b, *mb; + long n, nblks; + threadsetname("archproc"); errinit(Errstack); if(catcherror()) fatal("archproc: uncatched: %r"); + if(twc == nil) + twc = chancreate(sizeof(ulong), 0); + threadcreate(waitproc, threadwaitchan(), 16*KiB); xrlock(&fs->dquiescence); xqlock(&fs->archlk); - fsarchive(); + if(pipe(fd) < 0) + fatal("pipe: %r"); + procrfork(archer, fd, Stack, RFFDG|RFNOTEG); + tio = ioproc(); + nblks = 0; + for(;;){ + addr = 0; + werrstr("eof"); + n = ioread(tio, fd[1], &addr, sizeof addr); + if(n <= 0){ + warn("archer read: %r"); + close(fd[1]); + break; + } + if(addr == 0){ + warn("archer: done"); + close(fd[1]); + break; + } + if(!validaddr(addr)){ + warn("archer: invalid addr %D", addr); + close(fd[1]); + continue; /* let arch exit; we'll notice */ + } + mb = nil; + b = mbget(addr); + if(b != nil){ + dVprint("archproc: mblock %#ullx\n", addr); + xqlock(&b->slk); + if(b->state != MBclean){ + xqunlock(&b->slk); + mbput(b); + b = nil; + } + } + if(b == nil){ + dVprint("archproc: dblock %#ullx\n", addr); + mb = mballocz(0); + mb->state = MBin; + mb->addr = addr; + if(catcherror()){ + warn("archer: block %D: %r", mb->addr); + mb->d.type = DBfree; + snprint((char*)mb->d.data, Dblkdatasz, "%r"); + write(fd[1], &mb->d, sizeof mb->d); + mb->state = MBmem; + mbput(mb); + continue; + } + dbread(mb); + noerror(); + b = mb; + } + if(iowrite(tio, fd[1], &b->d, sizeof b->d) != sizeof b->d){ + warn("archer: writer: %r"); + close(fd[1]); + break; + } + if(b == mb) + b->state = MBmem; /* so mbput rlses it. */ + else + xqunlock(&b->slk); + mbput(b); + nblks++; + } + closeioproc(tio); + tio = nil; + close(fd[0]); fs->archt = nextime(time(nil), fs->archhour); xqunlock(&fs->archlk); xrunlock(&fs->dquiescence); + dVprint("archproc: done: %uld blocks served\n", nblks); noerror(); errterm(); threadexits(nil); @@ -298,11 +451,15 @@ assert(Dblksz == sizeof b->d); fs = mallocz(sizeof *fs, 1); - fs->dev = strdup(dev); - fs->fd = open(dev, ORDWR); - if(fs->fd < 0) - error("open %s: %r", dev); - + fs->fd = -1; + if(dev == nil) + fs->dev = "ram"; + else{ + fs->dev = strdup(dev); + fs->fd = open(dev, ORDWR); + if(fs->fd < 0) + error("open %s: %r", dev); + } fs->lruc = chancreate(sizeof(ulong), 0); fs->syncc = chancreate(sizeof(ulong), 0); fs->sweepc = chancreate(sizeof(ulong), 0); @@ -312,20 +469,24 @@ 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); if(nblk > 0 && nblk < nablk) nablk = nblk; - fs->limit = disksize(fs->fd); - fs->ndblk = fs->limit/Dblksz; - fs->limit = fs->ndblk*Dblksz; - if(fs->limit < 10*Dblksz) - fatal("buy a larger disk"); - if(nablk > fs->ndblk){ - warn("using %ulld blocks and not %ulld (small disk)", - fs->ndblk, nablk); - nablk = fs->ndblk; + if(fs->fd < 0){ + fs->limit = ~0; + fs->ndblk = ~0; + }else{ + fs->limit = disksize(fs->fd); + fs->ndblk = fs->limit/Dblksz; + fs->limit = fs->ndblk*Dblksz; + if(fs->limit < 10*Dblksz) + fatal("buy a larger disk"); + if(nablk > fs->ndblk){ + warn("using %ulld blocks and not %ulld (small disk)", + fs->ndblk, nablk); + nablk = fs->ndblk; + } } p = malloc(nablk * sizeof fs->blk[0]); fs->blk = p; @@ -347,7 +508,7 @@ { char *e; - if(fs->mode == Rdonly) + if(fs->mode == Rdonly || fs->mode == Archer) return; if(fs->syncwc == nil) @@ -432,6 +593,8 @@ int uid; if(mode == Worm && fsysmem == 0) fsysmem = Wormmem; + if(mode == Archer) + fsysmem = Rammem; fsinit(dev, fsysmem/Dblksz); fs->mode = mode; fs->super = dbget(DBsuper, MKDADDR(Dsuperaddr)); @@ -444,7 +607,7 @@ proccreate(lruproc, nil, Stack); if(fs->mode == Normal) proccreate(sweepproc, nil, Stack); - if(fs->mode != Rdonly){ + if(fs->mode != Rdonly && fs->mode != Archer){ proccreate(syncproc, nil, Stack); proccreate(tmrproc, nil, Stack); } @@ -459,6 +622,7 @@ dfchdentry(fs->root, 0, fs->active->addr); warn("fsys open %s\n", mname(fs->mode)); + /* * Try to load the /active/users file, if any, * but ignore errors. We already have a default table loaded @@ -486,22 +650,27 @@ fsstats(char *s, char *e, int clr, int verb) { int i; + char *err; s = seprint(s, e, "fsys %s epoch %ulld fepoch %ulld %s\n", fs->dev, fs->super->d.epoch, fs->super->d.fepoch, mname(fs->mode)); + err = fs->werr; + if(err != nil) + s = seprint(s, e, "error:\t*** %s ***\n", err); s = seprint(s, e, "mem:\t%8uld blocks %8uld alloc %8uld free\n", fs->mballoc.nalloc, fs->mballoc.nalloc - fs->mballoc.nfree, fs->mballoc.nfree); s = seprint(s, e, "disk:\t%8ulld blocks %8ulld alloc %8ulld free\n", fs->ndblk, fs->ndblk - fs->ndfree, fs->ndfree); + /* +1 in the next one because of the super, which is clean and unlinked */ s = seprint(s, e, "blocks:\t%8ulld blocks %8ulld dirty %8uld clean" " %#4uld out\n", - fs->nmblk + fs->clean.n + fs->out.n, - fs->nmblk, fs->clean.n, fs->out.n); - s = seprint(s, e, "paths:\t%8uld alloc %8uld free\n", - pathalloc.nalloc, pathalloc.nfree); - s = seprint(s, e, "mfs:\t%8uld alloc %8uld free\n", - mfalloc.nalloc, mfalloc.nfree); + fs->nmblk + fs->clean.n + 1 + fs->out.n, + fs->nmblk, fs->clean.n + 1, fs->out.n); + s = seprint(s, e, "paths:\t%8uld alloc %8uld free (%4uld bytes)\n", + pathalloc.nalloc, pathalloc.nfree, pathalloc.elsz); + s = seprint(s, e, "mfs:\t%8uld alloc %8uld free (%4uld bytes)\n", + mfalloc.nalloc, mfalloc.nfree, pathalloc.elsz); if(verb == 0) return s; --- /sys/src/cmd/creepy/guide Wed Apr 11 22:40:09 2012 +++ /sys/src/cmd/creepy/guide Mon May 21 10:33:08 2012 @@ -1,30 +1,14 @@ Rc !dd -if /dev/zero -of disk -bs 1024 -count 1024 -mk all -slay 8.fscmd 8.fsfmt 8.9pix 9pix fcheck|rc ; broke|rc -8.fsfmt -vy >[2=1] -cd /sys/src/cmd/creepy; 8.9pix -DZ9 disk >[2=1] -8.fscmd -cDKNMOZ ldumpall sync >[2=1] -8.fscmd -cZ ldump sync sync rcl sync ldump>[2=1] +mk install +slay 8.cmd 8.fmt 8.9pix 9pix fcheck fmt rip arch |rc ; broke|rc +# +creepy/fmt -y /dev/sdC0/creepy +8.cmd -DZ check put!/bin/ls!/root/lc sync check ldump >[2=1] +creepy/rip -DZ /dev/sdC0/rip >/sys/log/creepy >[2=1] +creepy/9pix -DVZ /dev/sdC0/creepy + >/sys/log/creepy >[2=1] +mount -c /srv/rip /n/rip wormwr +mount -c /srv/9pix /n/9pix main +con /n/9pix/cons tstack 9pix -;; ;; ;; iprev=0; for(i in `{seq 10}){ echo $i... ; cp /bin/ls /n/9pix/active/ls^$i ; echo sync >/n/9pix/cons; rm -f /n/9pix/active/ls^$iprev ; iprev=$i ; echo check>/n/9pix/cons} -fcheck w 30 put!/bin/echo!/active/ls2 sync >[2=1] -cp disk1 disk -echo halt >/n/9pix/cons -creepy/9pix -Z /dev/sdC0/creepy -mount -c /srv/9pix /n/9pix -echo allow nemo >/n/9pix/cons -dircp -v /n/nix /n/9pix/active - -This is after a copying /n/nix to the active tree. - -creepy> stats -mblks: 194890 nblk 194895 nablk 194139 mused 751 mfree -lists: 190929 clean 2574 dirty 632 refs 194135 total -dblks: 4806423 nblk 3975788 nfree (0 list + 3975788 rem) -paths: 6 alloc 3 free ( 24 bytes) -mfs: 57257 alloc 0 free ( 68 bytes) -fids: 6 alloc 3 free ( 96 bytes) -rpcs: 4 alloc 2 free (8804 bytes) -clis: 1 alloc 0 free (8284 bytes) -srpcs: 0 alloc 0 free ( 740 bytes) - +tstack arch --- /sys/src/cmd/creepy/mblk.c Mon May 21 11:20:16 2012 +++ /sys/src/cmd/creepy/mblk.c Mon May 21 10:33:08 2012 @@ -42,9 +42,12 @@ if(b != nil) break; ainc(&fs->nmballocw); - if((nwait++ % 60) == 0) + if((nwait++ % 100) == 0) warn("out of memory blocks. waiting"); - sendul(fs->lruc, 0); + if(fs->checking) /* fscheck() reads everything */ + fslru(); /* with all processes quiescent */ + else + sendul(fs->lruc, 0); } if(zeroit) @@ -147,15 +150,17 @@ if(xcanqlock(&fs->clean)) fatal("mblru: clean not locked"); - if(xcanqlock(&b->slk) == 0){ /* busy and wrong lock order; try later... */ + if(xcanqlock(&b->slk) == 0){ /* busy and 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(b->state != MBclean){ + warn("mblru: %D: not clean: %s", b->addr, sname(b->state)); + return -1; /* could be a fatal */ + } if(mbunhash(b, 1) < 0){ xqunlock(&b->slk); - dprint("mblru: %D got busy\n", b->addr); + warn("mblru: %D got busy\n", b->addr); return -1; } xmunlink(&fs->clean, b); --- /sys/src/cmd/creepy/mkfile Mon May 21 11:20:16 2012 +++ /sys/src/cmd/creepy/mkfile Mon May 21 10:33:08 2012 @@ -6,6 +6,7 @@ fmt\ cmd\ rip\ + arch\ OFILES=\ dbg.$O\ @@ -15,7 +16,6 @@ tools.$O\ attr.$O\ fsys.$O\ - arch.$O\ sweep.$O\ write.$O\ @@ -46,6 +46,8 @@ $LD $LDFLAGS -o $target $prereq $O.rip: rip.$O $OFILES $IXOFILES $LIB $LD $LDFLAGS -o $target $prereq +$O.arch: arch.$O $OFILES $IXOFILES $LIB + $LD $LDFLAGS -o $target $prereq fns:V: c/f2p *.c >fns.h @@ -59,6 +61,6 @@ echo net: wc -l $CFILES ix.h net.h echo cmd: - wc -l cmd.c fmt.c + wc -l cmd.c fmt.c arch.c rip.c echo total: cat *.[ch] | wc -l --- /sys/src/cmd/creepy/sweep.c Tue May 15 17:14:50 2012 +++ /sys/src/cmd/creepy/sweep.c Mon May 21 10:33:08 2012 @@ -4,10 +4,73 @@ * free disk blocks, mark & sweep. */ -static void dfmark(daddrt addr, u64int e); +static char Efull[] = "disk full"; + +static void dfmark(daddrt addr, u64int e, int isdisk); + +/* + * We mark reachable blocks from the main tree on memory, because it may + * end up in the disk; and we mark reachable blocks from the root on-disk. + * Marking the main tree requires holding the rlock on the files used. + * Marking the tree on disk requires using MBclean blocks (as found on disk) + * and holding the state locks (slk) on them so they stay clean while we use + * them. + */ + +Memblk* +dbgetlocked(int type, daddrt addr, int isdisk) +{ + Memblk *b; + int i; + + for(i = 0;; i++){ + b = dbget(type, addr); + if(isdisk == 0) + return b; + xqlock(&b->slk); + if(b->state == MBclean) + return b; + xqunlock(&b->slk); + sleep(0); + if(++i % 100 == 0) + warn("dbgetlocked: long wait for %D", addr); + } +} + +static void +dbputlocked(Memblk *b, int isdisk) +{ + if(isdisk) + xqunlock(&b->slk); + mbput(b); +} + +static Memblk* +dfgetlocked(daddrt addr, int isdisk) +{ + Memblk *b; + + if(isdisk == 0){ + b = dbget(DBfile, addr); + xrlock(b->mf); + return b; + } + return dbgetlocked(DBfile, addr, isdisk); +} static void -markdentries(void *p, int n, u64int e) +dfputlocked(Memblk *b, int isdisk) +{ + if(isdisk == 0) + xrunlock(b->mf); + else + xqunlock(&b->slk); + mbput(b); + +} + +static void +markdentries(void *p, int n, u64int e, int isdisk) { int i; daddrt *d; @@ -15,11 +78,11 @@ d = p; for(i = 0; i < n; i++) if(d[i] != 0) - dfmark(d[i], e); + dfmark(d[i], e, isdisk); } static void -dbmark(int type, daddrt addr, u64int e) +dbmark(int type, daddrt addr, u64int e, int isdisk) { Memblk *b; int i; @@ -32,38 +95,36 @@ } if(type == DBdata) return; - b = dbget(type, addr); + b = dbgetlocked(type, addr, isdisk); if(catcherror()){ - mbput(b); + dbputlocked(b, isdisk); error(nil); } if(type == DBdirdata){ - markdentries(b->d.data, Dptrperblk, e); + markdentries(b->d.data, Dptrperblk, e, isdisk); 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); + dbmark(type-1, b->d.ptr[i], e, isdisk); done: noerror(); - mbput(b); + dbputlocked(b, isdisk); } static void -dfmark(daddrt addr, u64int e) +dfmark(daddrt addr, u64int e, int isdisk) { Memblk *b; int i, type; ulong doff, sz; u64int old; - b = dbget(DBfile, addr); - xrlock(b->mf); + b = dfgetlocked(addr, isdisk); if(catcherror()){ - xrunlock(b->mf); - mbput(b); + dfputlocked(b, isdisk); error(nil); } @@ -77,21 +138,20 @@ if(b->type == DBdir){ doff = embedattrsz(b); sz = Embedsz-doff; - markdentries(b->d.embed+doff, sz/Daddrsz, e); + markdentries(b->d.embed+doff, sz/Daddrsz, e, isdisk); } 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); + dbmark(type, b->d.dptr[i], e, isdisk); 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); + dbmark(type+i, b->d.iptr[i], e, isdisk); done: noerror(); - xrunlock(b->mf); - mbput(b); + dfputlocked(b, isdisk); } /* @@ -99,7 +159,7 @@ * so blocks with lower epochs could be considered free. */ static void -fsmark(void) +fsmark(int isdisk) { u64int e; vlong t; @@ -117,12 +177,16 @@ xqunlock(&fs->superlk); if(catcherror()){ + xqlock(&fs->superlk); + fs->super->d.epoch--; + xqunlock(&fs->superlk); 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 */ + if(isdisk) + dfmark(addr, e, Disk); noerror(); xrlock(fs->active->mf); @@ -133,7 +197,7 @@ opend(&fs->opstat[Opmark], t); return; } - dfmark(fs->active->addr, e); + dfmark(fs->active->addr, e, Mem); noerror(); xrunlock(fs->active->mf); @@ -177,6 +241,9 @@ dtagt epoch; int i; + + if(fs->werr != nil) + error("%s", fs->werr); if(fs->mode == Worm) return growworm(); @@ -223,9 +290,11 @@ 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) + if((b=mbget(MKDADDR(a))) != nil){ + warn("newdaddr: free: %H", b); + continue; + } + if(fs->ndfree < Dmaxfree) nbsendul(fs->sweepc, 0); /* get more */ return MKDADDR(a); } @@ -243,27 +312,48 @@ dAprint("newdaddr: %ulld free; asking sweep for more.", fs->ndfree); ainc(&fs->newdaddrw); nbsendul(fs->sweepc, 0); - error("disk full"); + error(Efull); /* caller will retry */ return Noaddr; } -static void +/* + * Possible BUG here: + * + * This is to remove from the hash a cached block which is now declared + * free (after mark&sweep). + * + * It seems in certain cases we find busy blocks here, which should not + * happen. This seems to happen only under stress testing with no free blocks. + * + * By now, report if it ever happens and don't reuse the address, + * which is safe and permits to continue normal operation. + * + * In the past this, was caused by a leak in the Memblk.Ref + * that made the block look busy and kept it in the hash. This is now fixed. + * But the check is here in case there are more of the same kind. + * + * It might not be a bug at all: if the fids are still referencing + * the blocks but we are doing a mark & sweep and it becomes available, + * there will be a block with > 2 refs here for a free address. + */ +static int dropold(daddrt addr) { Memblk *b; + int r; b = mbget(addr); if(b == nil) - return; + return 0; 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); + r = mblru(b); xqunlock(&fs->clean); + return r; } void @@ -272,7 +362,7 @@ Memblk *tb; daddrt addr0, eaddr, grpsz, a; dtagt fepoch; - int i, n, nb, x; + int i, n, nb, x, attempts; vlong t; threadsetname("sweepproc"); @@ -281,10 +371,12 @@ fatal("%s: uncatched: %r", threadgetname()); grpsz = Dblksz*Dtagperblk; for(;;){ - x = recvul(fs->sweepc); + attempts = 0; + do{ + x = recvul(fs->sweepc); + }while(fs->werr != nil && x == 0); xrlock(&fs->dquiescence); - dZprint("sweep at %D with %ulld free\n", - MKDADDR(fs->isweep), fs->ndfree); + dZprint("sweep with %ulld free\n", fs->ndfree); again: t = opstart(&fs->opstat[Opsweep], 0); xqlock(&fs->superlk); @@ -292,14 +384,13 @@ fepoch = fs->super->d.fepoch; xqunlock(&fs->superlk); n = 0; - addr0 = fs->isweep; - do{ + for(addr0 = Dblk0addr; addr0 < eaddr; addr0 += grpsz){ nb = 0; if(catcherror()){ warn("sweep: %r"); continue; } - tb = dbget(DBtag, MKDADDR(fs->isweep)); + tb = dbget(DBtag, MKDADDR(addr0)); xqlock(&tb->slk); for(i = 1; i < Dtagperblk; i++) if(tb->d.tag[i] != 0 && tb->d.tag[i] <= fepoch) @@ -313,20 +404,20 @@ 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; + a = addr0+i*Dblksz; assert(a < eaddr); dAprint("sweep: free %D\n", MKDADDR(a)); + if(dropold(MKDADDR(a)) < 0){ + tb->d.tag[i] = ~0; /* safety */ + continue; + } 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); @@ -340,14 +431,24 @@ mbput(tb); if(fs->ndfree > Dminfree) x |=nbrecvul(fs->sweepc); /* unlock others while */ - }while(fs->isweep != addr0); /* we scan for more */ + } /* we scan for more */ opend(&fs->opstat[Opsweep], t); dZprint("sweepproc: %d collected %ulld free\n", n, fs->ndfree); + if(fs->ndfree >= Dminfree && + fs->werr != nil && strstr(fs->werr, "disk full") != 0){ + fs->werr = nil; + warn("disk no longer full"); + } if(fs->ndfree < Dmaxfree || x != 0){ + fsmark(x > 1 ? Mem : Disk); x = 0; - fsmark(); - goto again; + if(attempts++ < 2) + goto again; + if(fs->ndfree < Dzerofree){ + fs->werr = Efull; + warn(Efull); + } } xrunlock(&fs->dquiescence); nbsendul(fs->sweepec, 0); /* for cmd.c */ --- /sys/src/cmd/creepy/tools.c Mon May 21 11:20:18 2012 +++ /sys/src/cmd/creepy/tools.c Mon May 21 10:33:08 2012 @@ -415,6 +415,19 @@ xqunlock(l); } +int +mlinked(List *l, Memblk *b) +{ + Memblk *lb; + + xqlock(l); + for(lb = l->hd; lb != nil; lb = lb->lnext) + if(lb == b) + break; + xqunlock(l); + return lb != nil; +} + void mused(List *l, Memblk *b) { --- /sys/src/cmd/creepy/usr.c Tue May 15 17:14:50 2012 +++ /sys/src/cmd/creepy/usr.c Mon May 21 10:33:08 2012 @@ -347,10 +347,10 @@ * the user updates /active/adm/users, to rewrite it according to our * in memory data base. */ +static char ubuf[512]; void rwusers(Memblk *uf) { - static char ubuf[512]; char *p, *nl, *c; uvlong off; long tot, nr, nw; @@ -363,6 +363,7 @@ goto update; } if(uf == nil){ + noerror(); rwdefaultusers(); xwunlock(&fs->Usrs); return; @@ -399,6 +400,10 @@ } update: + if(fs->mode == Archer){ + xwunlock(&fs->Usrs); + return; + } if(catcherror()){ xwunlock(&fs->Usrs); warn("users: %r\n"); --- /sys/src/cmd/creepy/write.c Tue May 15 17:14:51 2012 +++ /sys/src/cmd/creepy/write.c Mon May 21 10:33:08 2012 @@ -68,8 +68,8 @@ b->d.ptr[i] = dbaddress(type-1, b->d.ptr[i]); } - noerror(); addr = address(b); + noerror(); mbput(b); return addr; } @@ -147,11 +147,6 @@ 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) { @@ -169,8 +164,9 @@ * 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. + * If there are no free disk addresses we retry, after asking + * sweepproc for more. If the disk is full, we give up. + * * TODO: If it's because of a disk-full, we should probably * ignore werr in fidremove(), and clear werr after a successful remove. */ @@ -182,6 +178,8 @@ char buf[128]; int ntry; + if(fs->werr != nil) + error("%s", fs->werr); dZprint("fswrite...\n"); t = opstart(&fs->opstat[Opwriteq], 0); t = opend(&fs->opstat[Opwriteq], t); @@ -191,15 +189,17 @@ xrunlock(&fs->dquiescence); opend(&fs->opstat[Opwrite], t); dZprint("fswrite: %r\n"); - fs->werr = smprint("%r"); + if(fs->werr == nil) + fs->werr = smprint("%r"); error(nil); } ainc(&fs->nwrite); for(ntry = 0; ntry < Nscanfree; ntry++){ + if(fs->werr != nil) + error("%s", fs->werr); xwlock(fs->root->mf); if(catcherror()){ xwunlock(fs->root->mf); - nbsendul(fs->syncc, 0); if(ntry > 1) sendul(fs->sweepc, 0); else