diff --git a/sys/conf/GENERIC b/sys/conf/GENERIC index f9f615912f2..6c73bdf500b 100644 --- a/sys/conf/GENERIC +++ b/sys/conf/GENERIC @@ -40,6 +40,7 @@ option UDF # UDF (DVD) file system option MSDOSFS # MS-DOS file system option FIFO # FIFOs; RECOMMENDED #option TMPFS # efficient memory file system +option GEFS # good enough file system option FUSE # FUSE option SOCKET_SPLICE # Socket Splicing for TCP and UDP diff --git a/sys/conf/files b/sys/conf/files index 389e63cf1bc..447d7f54b03 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -842,6 +842,18 @@ file tmpfs/tmpfs_vfsops.c tmpfs file tmpfs/tmpfs_vnops.c tmpfs file tmpfs/tmpfs_specops.c tmpfs file tmpfs/tmpfs_fifoops.c tmpfs & fifo +file gefs/avl.c gefs +file gefs/blk.c gefs +file gefs/dump.c gefs +file gefs/error.c gefs +file gefs/hash.c gefs +file gefs/load.c gefs +file gefs/pack.c gefs +file gefs/snap.c gefs +file gefs/tree.c gefs +file gefs/util.c gefs +file gefs/vnops.c gefs +file gefs/sweep.c gefs file net/art.c file net/bpf.c bpfilter needs-count file net/bpf_filter.c bpfilter diff --git a/sys/gefs/avl.c b/sys/gefs/avl.c new file mode 100644 index 00000000000..99164f0b812 --- /dev/null +++ b/sys/gefs/avl.c @@ -0,0 +1,352 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "u.h" +#include "avl.h" + +#include "dat.h" +#include "fns.h" + + +/* See Knuth Volume 3, 6.2.3 */ + +Avltree* +avlcreate(int (*cmp)(Avl*, Avl*)) +{ + Avltree *t; + + t = malloc(sizeof(*t), M_TEMP, M_WAITOK|M_ZERO); + if(t == nil) + return nil; + return avlinit(t, cmp); +} + +Avltree* +avlinit(Avltree *t, int (*cmp)(Avl*, Avl*)) +{ + t->cmp = cmp; + t->root = nil; + return t; +} + +Avl* +avllookup(Avltree *t, Avl *k, int d) +{ + Avl *h, *n; + int c; + + n = nil; + h = t->root; + while(h != nil){ + c = (t->cmp)(k, h); + if(c < 0){ + if(d > 0) + n = h; + h = h->c[0]; + continue; + } + if(c > 0){ + if(d < 0) + n = h; + h = h->c[1]; + continue; + } + return h; + } + return n; +} + +static int insert(int (*)(Avl*, Avl*), Avl*, Avl**, Avl*, Avl**); + +Avl* +avlinsert(Avltree *t, Avl *k) +{ + Avl *old; + + old = nil; + insert(t->cmp, nil, &t->root, k, &old); + return old; +} + +static int insertfix(int, Avl**); + +static int +insert(int (*cmp)(Avl*, Avl*), Avl *p, Avl **qp, Avl *k, Avl **oldp) +{ + Avl *q; + int fix, c; + + q = *qp; + if(q == nil) { + k->c[0] = nil; + k->c[1] = nil; + k->balance = 0; + k->p = p; + *qp = k; + return 1; + } + + c = cmp(k, q); + if(c == 0) { + *oldp = q; + *k = *q; + if(q->c[0] != nil) + q->c[0]->p = k; + if(q->c[1] != nil) + q->c[1]->p = k; + *qp = k; + return 0; + } + c = c > 0 ? 1 : -1; + fix = insert(cmp, q, q->c + (c+1)/2, k, oldp); + if(fix) + return insertfix(c, qp); + return 0; +} + +static Avl *singlerot(int, Avl*); +static Avl *doublerot(int, Avl*); + +static int +insertfix(int c, Avl **t) +{ + Avl *s; + + s = *t; + if(s->balance == 0) { + s->balance = c; + return 1; + } + if(s->balance == -c) { + s->balance = 0; + return 0; + } + if(s->c[(c+1)/2]->balance == c) + s = singlerot(c, s); + else + s = doublerot(c, s); + *t = s; + return 0; +} + +static int delete(int (*cmp)(Avl*, Avl*), Avl**, Avl*, Avl**); +static int deletemin(Avl**, Avl**); +static int deletefix(int, Avl**); + +Avl* +avldelete(Avltree *t, Avl *k) +{ + Avl *old; + + if(t->root == nil) + return nil; + old = nil; + delete(t->cmp, &t->root, k, &old); + return old; +} + +static int +delete(int (*cmp)(Avl*, Avl*), Avl **qp, Avl *k, Avl **oldp) +{ + Avl *q, *e; + int c, fix; + + q = *qp; + if(q == nil) + return 0; + + c = cmp(k, q); + c = c > 0 ? 1 : c < 0 ? -1: 0; + if(c == 0) { + *oldp = q; + if(q->c[1] == nil) { + *qp = q->c[0]; + if(*qp != nil) + (*qp)->p = q->p; + return 1; + } + fix = deletemin(q->c+1, &e); + *e = *q; + if(q->c[0] != nil) + q->c[0]->p = e; + if(q->c[1] != nil) + q->c[1]->p = e; + *qp = e; + if(fix) + return deletefix(-1, qp); + return 0; + } + fix = delete(cmp, q->c + (c+1)/2, k, oldp); + if(fix) + return deletefix(-c, qp); + return 0; +} + +static int +deletemin(Avl **qp, Avl **oldp) +{ + Avl *q; + int fix; + + q = *qp; + if(q->c[0] == nil) { + *oldp = q; + *qp = q->c[1]; + if(*qp != nil) + (*qp)->p = q->p; + return 1; + } + fix = deletemin(q->c, oldp); + if(fix) + return deletefix(1, qp); + return 0; +} + +static Avl *rotate(int, Avl*); + +static int +deletefix(int c, Avl **t) +{ + Avl *s; + int a; + + s = *t; + if(s->balance == 0) { + s->balance = c; + return 0; + } + if(s->balance == -c) { + s->balance = 0; + return 1; + } + a = (c+1)/2; + if(s->c[a]->balance == 0) { + s = rotate(c, s); + s->balance = -c; + *t = s; + return 0; + } + if(s->c[a]->balance == c) + s = singlerot(c, s); + else + s = doublerot(c, s); + *t = s; + return 1; +} + +static Avl* +singlerot(int c, Avl *s) +{ + s->balance = 0; + s = rotate(c, s); + s->balance = 0; + return s; +} + +static Avl* +doublerot(int c, Avl *s) +{ + Avl *r, *p; + int a; + + a = (c+1)/2; + r = s->c[a]; + s->c[a] = rotate(-c, s->c[a]); + p = rotate(c, s); + assert(r->p == p); + assert(s->p == p); + + if(p->balance == c) { + s->balance = -c; + r->balance = 0; + } else if(p->balance == -c) { + s->balance = 0; + r->balance = c; + } else + s->balance = r->balance = 0; + p->balance = 0; + return p; +} + +static Avl* +rotate(int c, Avl *s) +{ + Avl *r, *n; + int a; + + a = (c+1)/2; + r = s->c[a]; + s->c[a] = n = r->c[a^1]; + if(n != nil) + n->p = s; + r->c[a^1] = s; + r->p = s->p; + s->p = r; + return r; +} + +static Avl *walk1(int, Avl*); + +Avl* +avlprev(Avl *q) +{ + return walk1(0, q); +} + +Avl* +avlnext(Avl *q) +{ + return walk1(1, q); +} + +static Avl* +walk1(int a, Avl *q) +{ + Avl *p; + + if(q == nil) + return nil; + + if(q->c[a] != nil){ + for(q = q->c[a]; q->c[a^1] != nil; q = q->c[a^1]) + ; + return q; + } + for(p = q->p; p != nil && p->c[a] == q; p = p->p) + q = p; + return p; +} + +static Avl *bottom(Avltree*,int); + +Avl* +avlmin(Avltree *t) +{ + return bottom(t, 0); +} + +Avl* +avlmax(Avltree *t) +{ + return bottom(t, 1); +} + +static Avl* +bottom(Avltree *t, int d) +{ + Avl *n; + + if(t == nil) + return nil; + if(t->root == nil) + return nil; + + for(n = t->root; n->c[d] != nil; n = n->c[d]) + ; + return n; +} diff --git a/sys/gefs/avl.h b/sys/gefs/avl.h new file mode 100644 index 00000000000..348b81b5db6 --- /dev/null +++ b/sys/gefs/avl.h @@ -0,0 +1,23 @@ +typedef struct Avl Avl; +typedef struct Avltree Avltree; + +struct Avl { + Avl *c[2]; + Avl *p; + schar balance; +}; + +struct Avltree { + int (*cmp)(Avl*, Avl*); + Avl *root; +}; + +Avltree *avlinit(Avltree*, int(*)(Avl*, Avl*)); +Avltree *avlcreate(int(*)(Avl*, Avl*)); +Avl *avllookup(Avltree*, Avl*, int); +Avl *avldelete(Avltree*, Avl*); +Avl *avlinsert(Avltree*, Avl*); +Avl *avlmin(Avltree*); +Avl *avlmax(Avltree*); +Avl *avlnext(Avl*); +Avl *avlprev(Avl*); diff --git a/sys/gefs/blk.c b/sys/gefs/blk.c new file mode 100644 index 00000000000..16c9a81a92b --- /dev/null +++ b/sys/gefs/blk.c @@ -0,0 +1,1082 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "u.h" +#include "avl.h" + +#include "dat.h" +#include "fns.h" + +vlong gefs_blkalloc_lk(Arena*, int); +vlong gefs_blkalloc(int, uint, int); +void gefs_blkdealloc_lk(Arena*, vlong); +Blk* gefs_initblk(Blk*, vlong, vlong, int); +int gefs_readblk(Blk*, struct buf*, Bptr, int); + +enum { + Emask, + Ectr, +}; + +Blk* +gefs_cget(uvlong addr) +{ + uint32_t h; + Blk *p; + + h = gefs_ihash(addr) % nelem(fs->bpin); + rw_enter(&fs->bpinlk, RW_READ); + for(p = fs->bpin[h]; p != NULL; p = p->cnext) + if(p->bp.addr == addr){ + atomic_add_int_nv(&p->ref, 1); + break; + } + rw_exit(&fs->bpinlk); + return p; +} + +void +gefs_pin(Blk *b) +{ + uint32_t h; + + if(gefs_checkflag(b, Bpin, 0)) + return; + gefs_setflag(b, Bpin, 0); + h = gefs_ihash(b->bp.addr) % nelem(fs->bpin); + rw_enter(&fs->bpinlk, RW_WRITE); + b->cnext = fs->bpin[h]; + fs->bpin[h] = b; + rw_exit(&fs->bpinlk); +} + +void +gefs_unpin(Blk *b) +{ + uint32_t h; + Blk **pp; + + assert(gefs_checkflag(b, Bpin, 0)); + h = gefs_ihash(b->bp.addr) % nelem(fs->bpin); + rw_enter(&fs->bpinlk, RW_WRITE); + for(pp = &fs->bpin[h]; *pp != NULL; pp = &(*pp)->cnext) + if((*pp)->bp.addr == b->bp.addr) + break; + *pp = (*pp)->cnext; + rw_exit(&fs->bpinlk); +} + +int +gefs_checkflag(Blk *b, int set, int clr) +{ + long v; + + v = atomic_load_int(&b->flag); + return (v & (set|clr)) == set; +} + +void +gefs_setflag(Blk *b, int set, int clr) +{ + long ov, nv; + + while(1){ + ov = atomic_load_int(&b->flag); + nv = (ov & ~clr) | set; + if(acasl(&b->flag, ov, nv)) + break; + } +} + +void +gefs_syncblk(Blk *b) +{ + bassert(b, gefs_checkflag(b, Bfinal, 0)); + bassert(b, b->bp.addr >= 0); + tracex("syncblk", b->bp, b->type, -1); + if(gefs_rawio(b, B_WRITE) != 0) + panic("%s: write @%llx", Eio, b->bp.addr); + gefs_setflag(b, 0, Bdirty); +} + + +int +gefs_readblk(Blk *b, struct buf *buf, Bptr bp, int flg) +{ + vlong xh, ck; + char *p; + + b->cnext = nil; + b->cprev = nil; + b->hnext = nil; + + b->bp.addr = bp.addr; + b->bp.hash = -1; + b->bp.gen = -1; + + b->nval = 0; + b->valsz = 0; + b->nbuf = 0; + b->bufsz = 0; + b->logsz = 0; + + p = b->b->b_data + 2; + b->type = (flg&GBraw) ? Tdat : UNPACK16(b->b->b_data+0); + switch(b->type){ + default: + gefs_broke("invalid block type %d @%llx", b->type, bp.addr); + break; + case Tdat: + case Tsuper: + b->data = b->b->b_data; + break; + case Tarena: + b->data = p; + break; + case Tdlist: + case Tlog: + b->logsz = UNPACK16(p); p += 2; + b->logh = UNPACK64(p); p += 8; + b->logp = unpackbp(p, Ptrsz); p += Ptrsz; + bassert(b, p - b->b->b_data == Loghdsz); + b->data = p; + break; + case Tpivot: + b->nval = UNPACK16(p); p += 2; + b->valsz = UNPACK16(p); p += 2; + b->nbuf = UNPACK16(p); p += 2; + b->bufsz = UNPACK16(p); p += 2; + bassert(b, p - b->b->b_data == Pivhdsz); + b->data = p; + break; + case Tleaf: + b->nval = UNPACK16(p); p += 2; + b->valsz = UNPACK16(p); p += 2; + bassert(b, p - b->b->b_data == Leafhdsz); + b->data = p; + break; + } + if(b->type == Tlog || b->type == Tdlist){ + xh = b->logh; + ck = gefs_bufhash(b->data, b->logsz); + }else{ + xh = bp.hash; + ck = gefs_blkhash(b); + } + if((flg&GBnochk) == 0 && ck != xh){ + printf("invalid block hash: %llx %llx != %llx", bp.addr, xh, ck); + return EIO; + } + return 0; +} + +/* + * the log runs inside the allocator, so it must never + * block allocating anything from the cache. + * HACK HACK HACK + */ +int +gefs_rawio(Blk *b, int rw) +{ + struct buf *bp; + int s; + + bp = b->b; + bp->b_dev = fs->dev->v_rdev; + bp->b_blkno = b->bp.addr / DEV_BSIZE; + bp->b_bcount = Blksz; + bp->b_resid = Blksz; + if(rw == B_WRITE && bp->b_vp != NULL){ + s = splbio(); + bp->b_vp->v_numoutput++; + splx(s); + } + CLR(bp->b_flags, B_READ | B_WRITE | B_DONE | B_ERROR | B_EINTR); + SET(bp->b_flags, B_BUSY | B_RAW | rw); + VOP_STRATEGY(fs->dev, bp); + return biowait(bp); +} + +Arena* +gefs_pickarena(uint ty, uint hint, int tries) +{ + uint n, r; + + r = atomic_add_int_nv(&fs->roundrobin, 1)/2048; + if(ty == Tdat) + n = hint % (fs->narena - 1) + r + 1; + else + n = r; + return &fs->arenas[(n + tries) % fs->narena]; +} + +Arena* +gefs_getarena(vlong b) +{ + int hi, lo, mid; + vlong alo, ahi; + Arena *a; + + lo = 0; + hi = fs->narena; + if(b == fs->sb0.addr) + return &fs->arenas[0]; + if(b == fs->sb1.addr) + return &fs->arenas[hi-1]; + while(1){ + mid = (hi + lo)/2; + a = &fs->arenas[mid]; + alo = a->h0.addr; + ahi = alo + a->size + 2*Blksz; + if(b < alo) + hi = mid-1; + else if(b > ahi) + lo = mid+1; + else + return a; + } +} + + +void +gefs_freerange(Avltree *t, vlong off, vlong len) +{ + Arange *r, *s; + + assert(len % Blksz == 0); + r = malloc(sizeof(Arange), M_TEMP, M_WAITOK | M_ZERO); + r->off = off; + r->len = len; + assert(avllookup(t, &r->avl, 0) == nil); + avlinsert(t, &r->avl); + +Again: + s = (Arange*)avlprev(&r->avl); + if(s != nil){ + if(s->off+s->len == r->off){ + avldelete(t, &r->avl); + s->len = s->len + r->len; + free(r, M_TEMP, sizeof(Arange)); + r = s; + goto Again; + } + assert(s->off+s->len < r->off); + } + s = (Arange*)avlnext(&r->avl); + if(s != nil){ + if(r->off+r->len == s->off){ + avldelete(t, &r->avl); + s->off = r->off; + s->len = s->len + r->len; + free(r, M_TEMP, sizeof(Arange)); + r = s; + goto Again; + } + assert(r->off+r->len < s->off); + } +} + +void +gefs_grabrange(Avltree *t, vlong off, vlong len) +{ + Arange *r, *s, q; + vlong l; + + assert(len % Blksz == 0); + q.off = off; + q.len = len; + r = (Arange*)avllookup(t, &q.avl, -1); + if(r == nil || off + len > r->off + r->len) + panic("bad lookup"); + + if(off == r->off){ + r->off += len; + r->len -= len; + }else if(off + len == r->off + r->len){ + r->len -= len; + }else if(off > r->off && off+len < r->off + r->len){ + s = malloc(sizeof(Arange), M_TEMP, M_WAITOK | M_ZERO); + l = r->len; + s->off = off + len; + r->len = off - r->off; + s->len = l - r->len - len; + avlinsert(t, &s->avl); + }else + panic("bad range"); + + if(r->len == 0){ + avldelete(t, &r->avl); + free(r, M_TEMP, sizeof(Arange)); + } +} + +Blk* +gefs_mklogblk(Arena *a, vlong o) +{ + Blk *lb; + + lb = a->logbuf[0]; + if(lb == a->logtl) + lb = a->logbuf[1]; + bassert(lb, atomic_load_int(&lb->ref) == 1); + atomic_swap_uint(&lb->flag, Bstatic); + gefs_initblk(lb, o, -1, Tlog); + traceb("logblk" , lb->bp); + lb->lasthold0 = lb->lasthold; + lb = gefs_holdblk(lb); + return lb; +} + +/* + * Logs an allocation. Must be called + * with arena lock held. Duplicates some + * of the work in allocblk to prevent + * recursion. + */ +int +gefs_logappend(Arena *a, vlong off, vlong len, int op) +{ + vlong o, start, end; + Blk *lb; + char *p; + + assert((off & 0xff) == 0); + assert(op == LogAlloc || op == LogFree || op == LogSync); + if(op != LogSync){ + start = a->h0.addr; + end = start + a->size + 2*Blksz; + assert(off >= start); + assert(off < end); + } + lb = a->logtl; + bassert(lb, atomic_load_int(&lb->ref) > 0); + bassert(lb, lb->type == Tlog); + bassert(lb, lb->logsz >= 0); + + if(gefs_checkflag(lb, 0, Bdirty)) + gefs_setflag(lb, Bdirty, Bfinal); + + /* + * move to the next block when we have + * too little room in the log: + * We're appending up to 16 bytes as + * part of the operation, followed by + * 16 bytes of new log entry allocation + * and chaining. + */ + if(lb->logsz >= Logspc - Logslop){ + o = gefs_blkalloc_lk(a, 0); + if(o == -1) panic("no space"); + //return ENOSPC; + p = lb->data + lb->logsz; + PACK64(p, o|LogAlloc1); + lb->logsz += 8; + lb->logp = (Bptr){o, -1, -1}; + lb = gefs_mklogblk(a, o); + } + if(len == Blksz){ + if(op == LogAlloc) + op = LogAlloc1; + else if(op == LogFree) + op = LogFree1; + } + off |= op; + p = lb->data + lb->logsz; + PACK64(p, off); + lb->logsz += 8; + if(op >= Log2wide){ + PACK64(p+8, len); + lb->logsz += 8; + } + if(lb != a->logtl){ + gefs_finalize(lb); + gefs_syncblk(lb); + gefs_finalize(a->logtl); + gefs_syncblk(a->logtl); + gefs_dropblk(a->logtl); + a->logtl = lb; + a->nlog++; + } + return 0; +} + +void +gefs_loadlog(Arena *a, Bptr bp) +{ + vlong ent, off, len, gen; + int op, i, n; + char *d; + Blk *b; + + + dprint("loadlog %llx\n", bp.addr); + traceb("loadlog", bp); + b = a->logbuf[0]; + while(1){ + bassert(b, gefs_checkflag(b, Bstatic, Bpin)); + gefs_holdblk(b); + b->bp.addr = bp.addr; + if(gefs_rawio(b, B_READ) != 0) + gefs_broke("%s: log @%llx", Eio, bp.addr); + gefs_readblk(b, nil, bp, 0); + dprint("\tload %llx chain %llx\n", bp.addr, b->logp.addr); + a->nlog++; + for(i = 0; i < b->logsz; i += n){ + d = b->data + i; + ent = UNPACK64(d); + op = ent & 0xff; + off = ent & ~0xff; + n = (op >= Log2wide) ? 16 : 8; + switch(op){ + case LogSync: + gen = ent >> 8; + dprint("\tlog@%x: sync %lld\n", i, gen); + if(gen >= agetv(&fs->qgen)){ + if(a->logtl == nil){ + b->logp = Zb; + b->logsz = i; + a->logtl = b; + gefs_setflag(b, Bdirty, 0); + return; + } + gefs_dropblk(b); + return; + } + break; + + case LogAlloc: + case LogAlloc1: + len = (op >= Log2wide) ? UNPACK64(d+8) : Blksz; + dprint("\tlog@%x alloc: %llx+%llx\n", i, off, len); + gefs_grabrange(a->free, off & ~0xff, len); + a->used += len; + break; + case LogFree: + case LogFree1: + len = (op >= Log2wide) ? UNPACK64(d+8) : Blksz; + dprint("\tlog@%x free: %llx+%llx\n", i, off, len); + gefs_freerange(a->free, off & ~0xff, len); + a->used -= len; + break; + default: + dprint("\tlog@%x: log op %d\n", i, op); + gefs_broke("%s: log op %d", Ecorrupt, op); + break; + } + } + if(b->logp.addr == -1){ + a->logtl = b; + return; + } + bp = b->logp; + gefs_dropblk(b); + } +} + +void +gefs_flushlog(Arena *a) +{ + if(gefs_checkflag(a->logtl, 0, Bdirty|Bstatic)) + return; + gefs_finalize(a->logtl); + gefs_syncblk(a->logtl); +} + +int +gefs_compresslog(Arena *a) +{ + int i, nr, nblks, nlog; + vlong sz, *blks; + Blk *b; + Arange *r; + char *p; + + gefs_flushlog(a); + /* + * Prepare what we're writing back. + * Arenas must be sized so that we can + * keep the merged log in memory for + * a rewrite. + */ + sz = 0; + nr = 0; + nlog = 0; + for(r = (Arange*)avlmin(a->free); r != nil; r = (Arange*)avlnext((Avl*)r)){ + sz += 16; + nr++; + } + + /* + * Make a pessimistic estimate of the number of blocks + * needed to store the ranges, as well as the blocks + * used to store the range allocations. + * + * This does modify the tree, but it's safe because + * we can only be removing entries from the tree, not + * splitting or inserting new ones. + */ + nblks = (sz+Logspc)/(Logspc - Logslop) + 16*nr/(Logspc-Logslop) + 1; + if((blks = malloc(nblks*sizeof(vlong), M_TEMP, M_ZERO)) == nil) + return ENOMEM; +// if(waserror()){ +// free(blks); +// nexterror(); +// } + for(i = 0; i < nblks; i++){ + blks[i] = gefs_blkalloc_lk(a, 1); + if(blks[i] == -1) + error(Efull); + } + + /* fill up the log with the ranges from the tree */ + i = 0; + b = gefs_mklogblk(a, blks[i++]); + for(r = (Arange*)avlmin(a->free); r != nil; r = (Arange*)avlnext((Avl*)r)){ + if(b->logsz >= Logspc - Logslop){ + b->logp = (Bptr){blks[i], -1, -1}; + gefs_finalize(b); + gefs_syncblk(b); + gefs_dropblk(b); + nlog++; + b = gefs_mklogblk(a, blks[i++]); + } + p = b->data + b->logsz; + PACK64(p+0, r->off|LogFree); + PACK64(p+8, r->len); + b->logsz += 16; + } + gefs_finalize(b); + gefs_syncblk(b); + + /* + * now we have a valid freelist, and we can start + * appending stuff to it. Clean up the eagerly + * allocated extra blocks. + * + * Note that we need to drop the reference to the + * old logtl before we free the old blocks, because + * deallocating a block may require another block. + */ + gefs_dropblk(a->logtl); + a->loghd = (Bptr){blks[0], -1, -1}; + a->logtl = b; /* written back by sync() later */ + a->nlog = nlog; + a->lastlogsz = nlog; + + /* May add blocks to new log */ + for(; i < nblks; i++) + gefs_blkdealloc_lk(a, blks[i]); +// poperror(); + free(blks, nblks*sizeof(vlong), M_TEMP); + return 0; +} + +int +gefs_logbarrier(Arena *a, vlong gen) +{ + gefs_logappend(a, gen<<8, 0, LogSync); + return 0; +} + +/* + * Allocate from an arena, with lock + * held. May be called multiple times + * per operation, to alloc space for + * the alloc log. + */ +vlong +gefs_blkalloc_lk(Arena *a, int seq) +{ + Arange *r; + vlong b; + + if(seq) + r = (Arange*)avlmin(a->free); + else + r = (Arange*)avlmax(a->free); + if(!fs->usereserve && a->size - a->used <= a->reserve) + return -1; + if(r == nil) + gefs_broke(Estuffed); + + /* + * A bit of sleight of hand here: + * while we're changing the sorting + * key, but we know it won't change + * the sort order because the tree + * covers disjoint ranges + */ + if(seq){ + b = r->off; + r->len -= Blksz; + r->off += Blksz; + }else{ + r->len -= Blksz; + b = r->off + r->len; + } + if(r->len == 0){ + avldelete(a->free, &r->avl); + free(r, M_TEMP, sizeof(Arange)); + } + a->used += Blksz; + tracex("blkalloc" , Zb, b, getcallerpc(&a)); +// printf("blkalloc %llx seq %d caller %p\n", b, seq, (void*)getcallerpc(&a)); + return b; +} + +void +gefs_blkdealloc_lk(Arena *a, vlong ba) +{ + struct buf *b; + + if(incore(fs->dev, ba/DEV_BSIZE) != NULL){ + b = getblk(fs->dev, ba/DEV_BSIZE, Blksz, 0, INFSLP); + SET(b->b_flags, B_INVAL); + brelse(b); + } + gefs_logappend(a, ba, Blksz, LogFree); + gefs_freerange(a->free, ba, Blksz); + a->used -= Blksz; +} + +vlong +gefs_blkalloc(int ty, uint hint, int seq) +{ + Arena *a; + vlong b; + int tries; + + tries = 0; +Again: + a = gefs_pickarena(ty, hint, tries); + /* + * Loop through the arena up to 2 times. + * The first pass tries to find an arena + * that has space and is not in use, the + * second waits until an arena is free. + */ +// if(tries == 2*fs->narena) +// return -1; +// tries++; +// if(tries < fs->narena){ +// if(rw_enter(&a->lk, RW_WRITE | RW_NOSLEEP) == 0) +// goto Again; +// }else + rw_enter(&a->lk, RW_WRITE); + b = gefs_blkalloc_lk(a, seq); + if(b == -1){ + rw_exit(&a->lk); + goto Again; + } + if(gefs_logappend(a, b, Blksz, LogAlloc) != 0){ + gefs_freerange(a->free, b, Blksz); + a->used -= Blksz; + rw_exit(&a->lk); + return -1; + } + rw_exit(&a->lk); + return b; +} + +Blk* +gefs_initblk(Blk *b, vlong bp, vlong gen, int ty) +{ + char *d; + + d = b->b->b_data; + b->type = ty; + b->bp.addr = bp; + b->bp.hash = -1; + b->bp.gen = gen; + switch(ty){ + case Tdat: + b->data = d; + break; + case Tarena: + b->data = d+2; + break; + case Tdlist: + case Tlog: + b->logsz = 0; + b->logp = (Bptr){-1, -1, -1}; + b->data = d + Loghdsz; + break; + case Tpivot: + b->data = d + Pivhdsz; + break; + case Tleaf: + b->data = d + Leafhdsz; + break; + } + gefs_setflag(b, Bdirty, 0); + b->nval = 0; + b->valsz = 0; + b->nbuf = 0; + b->bufsz = 0; + b->logsz = 0; + b->alloced = getcallerpc(&b); + + return b; +} + +Blk* +gefs_newdblk(Tree *t, vlong hint, int seq) +{ + vlong bp; + Blk *b; + + bp = gefs_blkalloc(Tdat, hint, seq); + b = malloc(sizeof(Blk), M_TEMP, M_WAITOK|M_ZERO); + b->ref = 1; + b->b = getblk(fs->dev, bp/DEV_BSIZE, Blksz, 0, INFSLP); + gefs_initblk(b, bp, t->memgen, Tdat); + gefs_pin(b); + return b; +} + +Blk* +gefs_newblk(Tree *t, int ty) +{ + vlong bp; + Blk *b; + + bp = gefs_blkalloc(ty, 0, 0); + b = malloc(sizeof(Blk), M_TEMP, M_WAITOK|M_ZERO); + b->ref = 1; + b->b = getblk(fs->dev, bp/DEV_BSIZE, Blksz, 0, INFSLP); + gefs_initblk(b, bp, t->memgen, ty); + gefs_pin(b); + return b; +} + +Blk* +gefs_dupblk(Tree *t, Blk *b) +{ + Blk *r; + + if((r = gefs_newblk(t, b->type)) == nil) + return nil; + + tracex("dup" , b->bp, b->type, t->gen); + r->bp.hash = -1; + r->nval = b->nval; + r->valsz = b->valsz; + r->nbuf = b->nbuf; + r->bufsz = b->bufsz; + r->logsz = b->logsz; + r->alloced = getcallerpc(&t); + memcpy(r->b->b_data, b->b->b_data, Blksz); + gefs_pin(r); + return r; +} + +void +gefs_finalize(Blk *b) +{ + char *d; + + d = b->b->b_data; + if(b->type != Tdat) + PACK16(d, b->type); + + switch(b->type){ + default: + panic("finalize: bad block type %d", b->type); + break; + case Tpivot: + PACK16(d+2, b->nval); + PACK16(d+4, b->valsz); + PACK16(d+6, b->nbuf); + PACK16(d+8, b->bufsz); + break; + case Tleaf: + PACK16(d+2, b->nval); + PACK16(d+4, b->valsz); + break; + case Tdlist: + case Tlog: + b->logh = gefs_bufhash(b->data, b->logsz); + PACK16(d+2, b->logsz); + PACK64(d+4, b->logh); + packbp(d+12, Ptrsz, &b->logp); + break; + case Tdat: + case Tarena: + case Tsuper: + break; + } + + b->bp.hash = gefs_blkhash(b); + gefs_setflag(b, Bdirty|Bfinal, 0); +} + +Blk* +gefs_getblk(Bptr bp, int flg) +{ + struct buf *buf; + Blk *b; + int e; + + if((b = gefs_cget(bp.addr)) != NULL) + return b; + /* + * this sucks a bit; we keep the pinned, until we drop it; + * we're exclusive, but we don't need to be. + */ + if((e = bread(fs->dev, bp.addr/DEV_BSIZE, Blksz, &buf)) != 0) + return nil; + b = emalloc(sizeof(Blk), 1); + b->ref = 1; + b->b = buf; + if(gefs_readblk(b, buf, bp, flg) != 0){ + brelse(buf); + free(b, M_TEMP, sizeof(Blk)); + return nil; + } + b->bp.hash = bp.hash; + b->bp.gen = bp.gen; + b->alloced = getcallerpc(&bp); + gefs_pin(b); + return b; +} + + +Blk* +gefs_holdblk(Blk *b) +{ + atomic_add_int_nv(&b->ref, 1); + b->lasthold = getcallerpc(&b); + return b; +} + +void +gefs_dropblk(Blk *b) +{ + if(b == nil) + return; + if(atomic_add_int_nv(&b->ref, -1) != 0) + return; + /* preallocated log buffers are never freed */ + if(gefs_checkflag(b, Bstatic, 0)) + return; + gefs_unpin(b); + if(gefs_checkflag(b, Bdirty, 0)) + bdwrite(b->b); + else + brelse(b->b); + free(b, M_TEMP, sizeof(Blk)); +} + +ushort +gefs_blkfill(Blk *b) +{ + switch(b->type){ + case Tpivot: + return 2*b->nbuf + b->bufsz + 2*b->nval + b->valsz; + case Tleaf: + return 2*b->nval + b->valsz; + default: + panic("invalid block @%lld\n", b->bp.addr); + } +} + +void +gefs_limbo(int op, Limbo *l) +{ + Limbo *p; + ulong ge; + + l->op = op; + while(1){ + ge = atomic_load_int(&fs->epoch) & Emask; + p = agetp(&fs->limbo[ge]); + l->next = p; + if(acasp(&fs->limbo[ge], p, l) == p){ + atomic_add_int_nv(&fs->nlimbo, 1); + break; + } + } +} + +void +gefs_freeblk(Tree *t, Blk *b) +{ + if(t == &fs->snap || (t != nil && b->bp.gen < t->memgen)){ + tracex("killb", b->bp, getcallerpc(&t), -1); + gefs_killblk(t, b->bp); + return; + } + tracex("freeb", b->bp, getcallerpc(&t), -1); + gefs_setflag(b, Blimbo, 0); + gefs_holdblk(b); + bassert(b, atomic_load_int(&b->ref) > 1); + gefs_limbo(DFblk, (Limbo*)b); +} + +void +gefs_freebp(Tree *t, Bptr bp) +{ + Bfree *f; + + if(t == &fs->snap || (t != nil && bp.gen < t->memgen)){ + tracex("killb", bp, getcallerpc(&t), -1); + gefs_killblk(t, bp); + return; + } + tracex("freeb", bp, getcallerpc(&t), -1); + f = malloc(sizeof(Bfree), M_TEMP, M_WAITOK|M_ZERO); + f->bp = bp; + gefs_limbo(DFbp, (Limbo*)f); +} + +void +gefs_epochstart(void) +{ + atomic_add_int_nv(&fs->epoch, Ectr); + membar_enter(); +} + +void +gefs_epochend(void) +{ + ulong e; + + assert((atomic_load_int(&fs->epoch) & ~Emask) != 0); + membar_exit(); + e = atomic_add_int_nv(&fs->epoch, -Ectr); + if((e & ~0x3) == 0) + wakeup(&fs->epoch); +} + +int +gefs_epochclean(void) +{ + ulong c, e, ge; + int delay; + vlong ba; + Limbo *p, *n; + Dlist *dl; + Blk *b; + Bfree *f; + Arena *a; + + if(rw_enter(&fs->epochlk, RW_WRITE|RW_NOSLEEP) != 0) + return 0; + delay = 0; + c = atomic_load_int(&fs->nlimbo); + e = atomic_load_int(&fs->epoch); + ge = e & Emask; +Again: + if((e & ~Emask) != 0){ + if(c < 128 /*fs->cmax/4*/){ + rw_exit(&fs->epochlk); + return 0; + } + if(delay++ == 300) + printf("gefs: stalled epoch %lx\n", e); + tsleep_nsec(&fs->epoch, PWAIT, "gefsepoch", MSEC_TO_NSEC(delay)); + goto Again; + } + /* + * limbo[ge+1] was retired two generations back, and no + * reader from before this instant remains, so nothing can + * reach it. if a reader arrives between the swap and the + * advance, the CAS fails and we skip the advance: the + * swapped list is still stale enough to free, and the next + * clean will advance the generation. + */ + p = atomic_swap_ptr(&fs->limbo[(ge+1)&Emask], nil); + acasl(&fs->epoch, e, (ge+1)&Emask); + rw_exit(&fs->epochlk); + + for(; p != nil; p = n){ + n = p->next; + switch(p->op){ + case DFclose: + gefs_closesnap((Tree*)p); + break; + case DFtree: + free(p, M_TEMP, sizeof(Tree)); + break; + case DFmnt: + free(p, M_TEMP, sizeof(Mount)); + break; + case DFbp: + f = (Bfree*)p; + a = gefs_getarena(f->bp.addr); + rw_enter(&a->lk, RW_WRITE); + gefs_blkdealloc_lk(a, f->bp.addr); + rw_exit(&a->lk); + free(f, M_TEMP, sizeof(Bfree)); + break; + case DFblk: + b = (Blk*)p; + ba = b->bp.addr; + a = gefs_getarena(ba); + gefs_setflag(b, Bfreed, Bdirty|Blimbo); + gefs_dropblk(b); + rw_enter(&a->lk, RW_WRITE); + gefs_blkdealloc_lk(a, ba); + rw_exit(&a->lk); + break; + case DFdlist: + dl = (Dlist*)p; + gefs_freedl(dl); + free(dl, M_TEMP, sizeof(Dlist)); + break; + default: + panic("limbo list"); + } + atomic_add_int_nv(&fs->nlimbo, -1); + } + return 1; +} + +void +gefs_enqueue(Blk *b) +{ + assert(gefs_checkflag(b, Bdirty, Bqueued|Bstatic)); + assert(b->bp.addr >= 0); + assert(b->ref == 1); + gefs_finalize(b); +// bawrite(b->b); +// printf("enqueue %llx type %d gen %lld\n", b->bp.addr, b->type, b->bp.gen); +// gefs_syncblk(b); +#ifdef NOTYET + Arena *a; + Qent qe; + + finalize(b); + if(checkflag(b, 0, Bcached)){ + cacheins(b); + b->cached = getcallerpc(&b); + } + holdblk(b); + + b->enqueued = getcallerpc(&b); + traceb("queueb", b->bp); + a = getarena(b->bp.addr); + qe.op = Qwrite; + qe.bp = b->bp; + qe.b = b; + gefs_qput(a->sync, qe); +#endif +} diff --git a/sys/gefs/dat.h b/sys/gefs/dat.h new file mode 100644 index 00000000000..3588228d55a --- /dev/null +++ b/sys/gefs/dat.h @@ -0,0 +1,800 @@ +typedef struct Amsg Amsg; +typedef struct Arange Arange; +typedef struct Arena Arena; +typedef struct Bfree Bfree; +typedef struct Blk Blk; +typedef struct Bptr Bptr; +typedef struct Bucket Bucket; +typedef struct Chan Chan; +typedef struct Conn Conn; +typedef struct Cron Cron; +typedef struct Dent Dent; +typedef struct Dlist Dlist; +typedef struct Errctx Errctx; +typedef struct Fmsg Fmsg; +typedef struct Gefs Gefs; +typedef struct Key Key; +typedef struct Kvp Kvp; +typedef struct Limbo Limbo; +typedef struct Mount Mount; +typedef struct Msg Msg; +typedef struct Qent Qent; +typedef struct Scan Scan; +typedef struct Scanp Scanp; +typedef struct Syncq Syncq; +typedef struct Trace Trace; +typedef struct Tree Tree; +typedef struct Qid Qid; +typedef struct Val Val; +typedef struct Xdir Xdir; + +enum { + KiB = 1024ULL, + MiB = 1024ULL*KiB, + GiB = 1024ULL*MiB, + TiB = 1024ULL*GiB, + + Lgblk = 14, + Blksz = (1ULL< Kpmax ? Kvmax : Kpmax), + Estacksz = 64, + Maxprocs = 128, +}; + +enum { + /* + * dent: pqid[8] qid[8] -- a directory entry key. + * ptr: off[8] hash[8] gen[8] -- a key for an Dir block. + * dir: serialized Xdir + */ + + /* fs keys */ + Kdat, /* qid[8] off[8] => ptr: pointer to data page */ + Kent, /* pqid[8] name[n] => dir[n]: serialized Dir */ + Kup, /* qid[8] => Kent: parent dir */ + + /* snapshot keys */ + Klabel, /* name[] => snapid[]: snapshot label */ + Ksnap, /* sid[8] => ref[8], tree[52]: snapshot root */ + Kdlist, /* snap[8] gen[8] => hd[ptr],tl[ptr] deadlist */ + + /* configuration */ + Kconf, /* name[] => value[] */ + + Khref, /* qid[8] => refs[8] hardlink refs */ +}; + +enum { + Bdirty = 1 << 0, + Bfinal = 1 << 1, + Bfreed = 1 << 2, + Bpin = 1 << 3, + Bqueued = 1 << 4, + Blimbo = 1 << 5, + Bstatic = 1 << 6, +}; + +enum { + Lmut = 1 << 0, /* can we modify snaps via this label */ + _Lauto = 1 << 1, /* deprecated: was auto snap */ + _Ltsnap = 1 << 2, /* deprecated: was skipping timed snaps */ +}; + +enum { + Qmagic = 1ULL << 63, + Qdump = Qmagic, + Qctl, + Qstatus, + Qdev, +}; + +#define Zb (Bptr){-1, -1, -1} +#define Tmfmt "YYYY.MM.DD[_]hh:mm:ss" + +/* internal errors */ +//#define Efs (abort(), "fs broke") +extern char Efs[]; +extern char Ecorrupt[]; +extern char Efsvers[]; +extern char Eimpl[]; +extern char Ebotch[]; +extern char Eio[]; +extern char Enofid[]; +extern char Efid[]; +extern char Etype[]; +extern char Edscan[]; +extern char Esrch[]; +extern char Eexist[]; +extern char Emode[]; +extern char Efull[]; +extern char Estuffed[]; +extern char Eauth[]; +extern char Elength[]; +extern char Eperm[]; +extern char Einuse[]; +extern char Ebadf[]; +extern char Ename[]; +extern char Enomem[]; +extern char Eattach[]; +extern char Enosnap[]; +extern char Esnap[]; +extern char Esnapr[]; +extern char Edir[]; +extern char Esyntax[]; +extern char Enouser[]; +extern char Enogrp[]; +extern char Efsize[]; +extern char Ebadu[]; +extern char Erdonly[]; +extern char Elocked[]; +extern char Eauthp[]; +extern char Eauthd[]; +extern char Eauthph[]; +extern char Ephase[]; +extern char Enone[]; +extern char Enoauth[]; + +extern char Ewstatb[]; +extern char Ewstatd[]; +extern char Ewstatg[]; +extern char Ewstatl[]; +extern char Ewstatm[]; +extern char Ewstato[]; +extern char Ewstatp[]; +extern char Ewstatq[]; +extern char Ewstatu[]; +extern char Ewstatv[]; +extern char Enempty[]; + +/* + * All metadata blocks share a common header: + * + * type[2] + * + * The None type is reserved for file data blocks + * and refcount blocks. + * + * The superblock has this layout: + * version[8] always "gefsNNNNN" + * blksz[4] block size in bytes + * bufsz[4] portion of leaf nodes + * allocated to buffers, + * in bytes + * height[4] tree height of root node + * rootb[8] address of root in last + * snapshot. + * rooth[8] hash of root node + * narena[4] number of arenas in tree + * flag[8] feature flag + * gen[8] The flush generation + * + * The arena zone blocks have this layout, and + * are overwritten in place: + * + * log[8] The head of the alloc log + * logh[8] The hash of the alloc log + * + * The log blocks have this layout, and are one of + * two types of blocks that get overwritten in place: + * + * hash[8] The hash of the previous log block + * + * The remainder of the block is filled with log + * entries. Each log entry has at least 8 bytes + * of entry. Some are longer. The opcode is or'ed + * into the low order bits of the first vlong. + * These ops take the following form: + * + * Alloc, Free: + * off[8] len[8] + * Alloc1, Free1: + * off[8] + * Ref: + * off[8] + * Flush: + * gen[8] + * + * Pivots have the following layout: + * + * nval[2] + * valsz[2] + * nbuf[2] + * bufsz[2] + * + * Leaves have the following layout: + * + * nval[2] + * valsz[2] + * pad[4]sure, + * + * Within these nodes, pointers have the following + * layout: + * + * off[8] hash[8] fill[2] + */ +enum { + Tdat, + Tpivot, + Tleaf, + Tlog, + Tdlist, + Tarena, + Tsuper = 0x6765, /* 'ge' bigendian */ +}; + +enum { + Vinl, /* Inline value */ + Vref, /* Block pointer */ +}; + +enum { + GBraw = 1<<0, + GBwrite = 1<<1, + GBnochk = 1<<2, +}; + +enum { + Onop, /* nothing */ + Oinsert, /* new kvp */ + Odelete, /* delete kvp */ + Oclearb, /* free block ptr if exists */ + Oclobber, /* remove file if it exists */ + Owstat, /* update kvp dirent */ + Orelink, /* rechain forwards */ + Oreprev, /* rechain backwards */ + Oincref, /* adjust refs on snap */ + Nmsgtype, /* maximum message type */ +}; + +enum { + Magic = 0x979b929e98969c8c, +}; + +/* + * Wstat ops come with associated data, in the order + * of the bit flag. + */ +enum{ + /* wstat flag */ + Owsize = 1<<0, /* [8]fsize: update file size */ + Owmode = 1<<1, /* [4]mode: update file mode */ + Owmtime = 1<<2, /* [8]mtime: update mtime, in nsec */ + Owatime = 1<<3, /* [8]atime: update atime, in nsec */ + Owuid = 1<<4, /* [4]uid: set uid */ + Owgid = 1<<5, /* [4]uid: set gid */ + Owmuid = 1<<6, /* [4]uid: set muid */ +}; + +/* bits in Qid.type */ +#define GEFS_QTDIR 0x80 /* type bit for directories */ +#define GEFS_QTAPPEND 0x40 /* type bit for append only files */ +#define GEFS_QTEXCL 0x20 /* type bit for exclusive use files */ +#define GEFS_QTMOUNT 0x10 /* type bit for mounted channel */ +#define GEFS_QTAUTH 0x08 /* type bit for authentication file */ +#define GEFS_QTTMP 0x04 /* type bit for not-backed-up file */ +#define GEFS_QTFILE 0x00 /* plain file */ + +#define GEFS_DEVBLK 1 +#define GEFS_DEVCHR 2 +#define GEFS_DEVSOCK 3 +#define GEFS_DEVFIFO 4 + +/* bits in Dir.mode */ +#define GEFS_DMDIR 0x80000000 /* mode bit for directories */ +#define GEFS_DMAPPEND 0x40000000 /* mode bit for append only files */ +#define GEFS_DMEXCL 0x20000000 /* mode bit for exclusive use files */ +#define GEFS_DMMOUNT 0x10000000 /* mode bit for mounted channel */ +#define GEFS_DMAUTH 0x08000000 /* mode bit for authentication file */ +#define GEFS_DMTMP 0x04000000 /* mode bit for non-backed-up files */ +#define GEFS_DMREAD 0x4 /* mode bit for read permission */ +#define GEFS_DMWRITE 0x2 /* mode bit for write permission */ +#define GEFS_DMEXEC 0x1 /* mode bit for execute permission */ + +#define GEFS_FSYMLINK 0x1 /* attribute bit to track symlinks */ +#define GEFS_FREFCNT 0x2 /* attribute to indicate refcount */ +#define GEFS_FDEVNOD 0x4 /* attribute to indicate refcount */ + +/* + * Operations for the allocation log. + */ +enum { + LogNop, /* unused */ + /* 1-wide entries */ + LogAlloc1, /* alloc a block */ + LogFree1, /* free a block */ + LogSync, /* sync barrier for replay */ + + /* 2-wide entries */ +#define Log2wide LogAlloc + LogAlloc, /* alloc a range */ + LogFree, /* free a range */ +}; + +enum { + AOnone, + AOsnap, + AOsync, + AOhalt, + AOclear, + AOrclose, + AOsetcfg, + AOclrcfg, +}; + +enum { + DFblk, + DFbp, + DFmnt, + DFtree, + DFclose, + DFdlist, +}; + +struct Qid { + uvlong path; + ulong vers; + uchar type; +}; + +struct Limbo { + Limbo *next; + int op; +}; + +struct Bptr { + vlong addr; + uvlong hash; + vlong gen; +}; + +struct Key { + char *k; + int nk; +}; + +struct Val { + short nv; + char *v; +}; + +struct Kvp { + Key k; + Val v; +}; + +struct Msg { + char op; + Kvp p; +}; + +struct Dlist { + Limbo limbo; + Dlist *cnext; /* cache next entry */ + Dlist *cprev; /* cache prev entry */ + Dlist *chain; /* hash table chain */ + int dirty; /* need tree update */ +// Blk *ins; /* loaded head */ + + vlong gen; /* deadlist gen */ + vlong bgen; /* birth gen */ + Bptr hd; /* deadlist head */ + Bptr tl; /* deadlist tail */ + int logsz; /* used space in hd */ +}; + +struct Errctx { + long tid; + char err[128]; + label_t errlab[Estacksz]; + int nerrlab; +}; + +struct Arange { + Avl avl; + vlong off; + vlong len; +}; + +struct Bucket { + Blk *b; +}; + +struct Amsg { + int op; + int fd; + union { + /* AOsync, AOhalt: no data */ + struct { /* AOsnap */ + char old[128]; + char new[128]; + int flag; + char delete; + Fmsg *m; + }; + struct { /* AOsetcfg AOclrcfg */ + char snap[128]; + char key[32]; + char val[128]; + }; + struct { /* AOclear, AOrclose */ + Mount *mnt; + Dent *dent; + vlong qpath; + vlong off; + vlong end; + }; + }; +}; + +struct Tree { + Limbo limbo; + + /* in-memory */ + struct rwlock lk; + Along memref; /* number of in-memory references to this */ + vlong memgen; /* wip next generation */ + int dirty; + + /* on-disk */ + int nref; /* number of forks */ + int nlbl; /* number of labels */ + int ht; /* height of the tree */ + uint flag; /* flag set */ + Bptr bp; /* block pointer of root */ + vlong gen; /* generation */ + vlong pred; /* previous snapshot */ + vlong succ; /* next snapshot */ + vlong base; /* base snapshot */ +}; + +struct Bfree { + Limbo lhd; + Bptr bp; +}; + +struct User { + int id; + int lead; + int *memb; + int nmemb; + char name[128]; +}; + +enum { + /* in priority order */ + Qnone, + Qfence, + Qwrite, + Qfree, +}; + +struct Qent { + vlong qgen; + Bptr bp; + Blk *b; + int op; +}; + +struct Syncq { + struct rwlock lk; + char fullrz; + char emptyrz; + Qent *heap; + int nheap; + int heapsz; +}; + +struct Trace { + int tid; + int qgen; + char msg[16]; + Bptr bp; + vlong v0; + vlong v1; +}; + +/* + * Overall state of the file sytem. + * Shadows the superblock contents. + */ +struct Gefs { + int blksz; + int bufspc; + Tree snap; + Dlist snapdl; + int narena; + vlong flag; + vlong nextqid; /* protected by mutlk */ + vlong nextgen; /* protected by mutlk */ + Avlong qgen; + Bptr *arenabp; + + /* superblocks */ + Bptr sb0; /* primary */ + Bptr sb1; /* backup */ + + /* arena allocation */ + Arena *arenas; + Along roundrobin; + long syncing; + long nsyncers; + long nreaders; + Along nprocs; + + struct rwlock synclk; + char syncrz; + + struct rwlock quitlk; + char quitrz; + + struct rwlock mountlk; + Aptr mounts; /* Mount* */ + Mount *snapmnt; + Conn *conns; + + Chan *wrchan; + Chan *admchan; + Chan **rdchan; + + struct rwlock mutlk; + + Along epoch; /* reader count << 2 | generation */ + Aptr limbo[3]; /* Limbo* */ + Along nlimbo; + struct rwlock epochlk; + + Syncq syncq[32]; + + struct vnode *dev; + Along rdonly; + int noauth; + int usereserve; + + /* slow block io */ + struct rwlock blklk[32]; + + /* deadlist cache */ + Dlist **dlcache; + Dlist *dlhead; + Dlist *dltail; + int dlcount; + int dlcmax; + + /* dirty is not a lock */ + struct rwlock bpinlk; + Blk *bpin[128]; + + /* preallocated deferred frees */ + struct rwlock bfreelk; + char bfreerz; + Bfree *bfree; + + Trace *trace; + Along traceidx; + long ntrace; +}; + +struct Arena { + struct rwlock lk; + Avltree *free; + Blk **queue; + int nqueue; + Blk *logbuf[2]; /* preallocated log pages */ + Bptr h0; /* arena header */ + Bptr h1; /* arena footer */ + Blk **q; /* write queue */ + vlong nq; + vlong size; + vlong used; + vlong reserve; + /* allocation log */ + vlong lastlogsz; /* size after last compression */ + vlong nlog; /* number of blocks in log */ + Bptr loghd; /* allocation log */ + Blk *logtl; /* end of the log, open for writing */ + Syncq *sync; +}; + +struct Xdir { + /* file data */ + uvlong flag; /* storage flag */ + Qid qid; /* unique id from server */ + ulong mode; /* permissions */ + vlong atime; /* last read time: nsec */ + vlong mtime; /* last write time: nsec */ + uvlong length; /* file length */ + int uid; /* owner name */ + int gid; /* group name */ + int muid; /* last modifier name */ + char *name; /* last element of path */ + int devt; /* device type (if flag set) */ + dev_t rdev; /* device (if flag set) */ +}; + +struct Dent { + struct rwlock lk; + Key k; + Xdir d; + Dent *next; + char truncrz; + vlong up; + Along ref; + char gone; + char trunc; + /* XXX: sucks, should really be on the fd */ + Scan *scan; /* in progres scan */ + + union { + char buf[Maxent]; + void *auth; + }; +}; + +struct Cron { + int i; + int cnt; + vlong div; + vlong last; + char *tag; + char (*lbl)[128]; +}; + +struct Mount { + Limbo lhd; + struct rwlock lk; + Mount *next; + Along ref; + vlong gen; + char name[64]; + Aptr root; /* Tree*, EBR protected */ + int flag; + + /* open directory entries */ + struct rwlock dtablk; + Dent *dtab[Ndtab]; + + /* snapshot scheduling */ + Cron cron[3]; +}; + +struct Conn { + Conn *next; + + struct rwlock wrlk; + + int rfd; + int wfd; + int cfd; + int iounit; + int versioned; + int authok; + int hangup; + + Along ref; + +}; + +enum { + POmod, + POrot, + POsplit, + POmerge, +}; + +struct Scanp { + int bi; + int vi; + Blk *b; +}; + +struct Scan { + vlong offset; /* last read offset */ + char first; + char donescan; + char overflow; + char present; + int ht; + Kvp kv; + Key pfx; + char kvbuf[Kvmax]; + char pfxbuf[Keymax]; + Scanp *path; +}; + +struct Blk { + Limbo lhd; + /* cache entry */ + Blk *cnext; + Blk *cprev; + Blk *hnext; + + /* serialized to disk in header */ + short type; /* @0, for all */ + union { + struct { + short nval; /* @2, for Leaf, Pivot: data[0:2] */ + short valsz; /* @4, for Leaf, Pivot: data[2:4] */ + short nbuf; /* @6, for Pivot */ + short bufsz; /* @8, for Pivot */ + }; + struct { + int logsz; /* @2 for allocation log */ + uvlong logh; /* @4 for log body hash */ + Bptr logp; /* @12 next deadlist chain */ + }; + }; + + /* debug */ + uintptr queued; + uintptr lasthold; + uintptr lasthold0; + uintptr lastdrop; + uintptr enqueued; + uintptr cached; + uintptr uncached; + uintptr alloced; + uintptr freed; + + Bptr bp; + Along ref; + Along flag; + char *data; + struct buf *b; +}; + +struct Chan { + struct rwlock lk; /* to manipulate values */ + char empty; + char full; + + int size; /* size of queue */ + int loc; /* circular pointer */ + int count; /* how many in queue */ + void* args[1]; /* list of saved pointers, [->size] */ +}; diff --git a/sys/gefs/dump.c b/sys/gefs/dump.c new file mode 100644 index 00000000000..89e314f5a52 --- /dev/null +++ b/sys/gefs/dump.c @@ -0,0 +1,385 @@ +#include +#include +#include +#include + +#include "u.h" +#include "avl.h" + +#include "dat.h" +#include "fns.h" + +char spc[128]; + +static void +dumphex(void *buf, int n) +{ + uchar *p = buf; + int i; + + for(i = 0; i < n; i++) + printf("%02x", p[i]); +} + +static void +dumpkey(Key *k) +{ + /* + * dent: pqid[8] qid[8] -- a directory entry key. + * ptr: off[8] hash[8] -- a key for an Dir block. + * dir: fixed statbuf header, user ids + */ + if(k->nk == 0){ + printf("\"\""); + return; + } + switch(k->k[0]){ + case Kdat: /* qid[8] off[8] => ptr[16]: pointer to data page */ + printf("dat qid:%llx off:%llx", + UNPACK64(k->k+1), UNPACK64(k->k+9)); + break; + case Kent: /* pqid[8] name[n] => dir[n]: serialized Dir */ + printf("ent dir:%llx, name:\"%.*s\"", + UNPACK64(k->k+1), k->nk-11, k->k+11); + break; + case Klabel: /* name[n] => tree[24]: snapshot ref */ + printf("label name:\"%.*s\"", k->nk-1, k->k+1); + break; + case Ksnap: /* name[n] => tree[24]: snapshot root */ + printf("snap id:%lld", UNPACK64(k->k+1)); + break; + case Kup: /* qid[8] => pqid[8]: parent dir */ + printf("up dir:%llx", UNPACK64(k->k+1)); + break; + case Kdlist: + printf("dlist gen:%lld, bgen:%lld", + UNPACK64(k->k+1), UNPACK64(k->k+9)); + break; + case Kconf: + printf("Conf %.*s", k->nk-1, k->k+1); + break; + default: + printf("??? "); + dumphex(k->k, k->nk); + break; + } +} + +static void +dumpval(Kvp *kv, int op, int flg) +{ + char *p; + int ws; + Tree t; + Xdir d; + Bptr bp; + + if(flg){ + assert(kv->v.nv == Ptrsz+2); + bp = unpackbp(kv->v.v, kv->v.nv); + printf("("); + gefs_dump_bptr(bp); + printf(",%d)", UNPACK16(kv->v.v+Ptrsz)); + return; + } + if(op == Odelete || op == Oclearb || op == Oclobber){ + printf("delete"); + return; + } + switch(kv->k.k[0]){ + case Kdat: /* qid[8] off[8] => ptr[16]: pointer to data page */ + switch(op){ + case Odelete: + case Oclearb: + break; + case Onop: + case Oinsert: + if(kv->v.nv == Ptrsz){ + printf("ptr:"); + gefs_dump_bptr(unpackbp(kv->v.v, kv->v.nv)); + }else{ + printf("BROKEN ptr "); + dumphex(kv->k.k, kv->k.nk); + } + break; + } + break; + case Kent: /* pqid[8] name[n] => dir[n]: serialized Dir */ + switch(op){ + case Onop: + case Oinsert: + kv2dir(kv, &d); + printf("[qid=(%llx,%lu,%d), p=%lo, f=%llx, t=%lld,%lld, l=%lld, o=%d, g=%d m=%d]", + d.qid.path, d.qid.vers, d.qid.type, d.mode, + d.flag, d.atime, d.mtime, d.length, + d.uid, d.gid, d.muid); + break; + case Odelete: + printf("delete"); + break; + case Owstat: + p = kv->v.v; + ws = *p++; + if(ws & Owsize){ + printf("size:%llx ", UNPACK64(p)); + p += 8; + } + if(ws & Owmode){ + printf("mode:%o ", UNPACK32(p)); + p += 4; + } + if(ws & Owmtime){ + printf("mtime:%llx ", UNPACK64(p)); + p += 8; + } + if(ws & Owatime){ + printf("mtime:%llx ", UNPACK64(p)); + p += 8; + } + if(ws & Owuid){ + printf("uid:%d ", UNPACK32(p)); + p += 4; + } + if(ws & Owgid){ + printf("gid:%d ", UNPACK32(p)); + p += 4; + } + if(ws & Owmuid){ + printf("muid:%d ", UNPACK32(p)); + p += 4; + } + if(p != kv->v.v + kv->v.nv){ + printf("v.nv: %d, sz=%d\n", kv->v.nv, (int)(p - kv->v.v)); + panic("dumpval"); + } + break; + } + break; + case Ksnap: /* name[n] => dent[16] ptr[16]: snapshot root */ + switch(op){ + case Orelink: + case Oreprev: + case Oincref: + printf("gen: %lld, dlbl: %d, dref: %d", + UNPACK64(kv->v.v), kv->v.v[8], kv->v.v[9]); + break; + case Onop: + case Oinsert: + if(unpacktree(&t, kv->v.v, kv->v.nv) == nil) + printf("corrupt tree"); + else{ + printf("", + t.pred, t.succ, t.base, t.nref, t.nlbl); + } + break; + default: + printf("?? unknown op %d", op); + } + break; + case Klabel: + printf("snap id:%lld", UNPACK64(kv->v.v+1)); + break; + case Kup: /* qid[8] => pqid[8]: parent dir */ + printf("super dir:%llx, name:\"%.*s\")", + UNPACK64(kv->v.v+1), kv->v.nv-11, kv->v.v+11); + break; + case Kdlist: + printf("hd:"); + gefs_dump_bptr(unpackbp(kv->v.v, kv->v.nv)); + printf(", tl:"); + gefs_dump_bptr(unpackbp(kv->v.v+Ptrsz, kv->v.nv-Ptrsz)); + break; + case Kconf: + printf("%.*s", kv->v.nv, kv->v.v); + break; + default: + printf("??? "); + dumphex(kv->k.k, kv->k.nk); + break; + } +} + +void +gefs_dump_bptr(Bptr bp) +{ + printf("(%llx,%.16llx,%llx)", bp.addr, bp.hash, bp.gen); +} + +void +gefs_dump_msg(Msg *m, int sharp) +{ + char *opname[Nmsgtype] = { + [Oinsert] = "Oinsert", + [Odelete] = "Odelete", + [Oclearb] = "Oclearb", + [Oclobber] = "Oclobber", + [Owstat] = "Owstat", + [Orelink] = "Orelink", + [Oreprev] = "Oreprev", + [Oincref] = "Oincref", + }; + + if(m == nil){ + printf("Msg{nil}"); + return; + } + printf("Msg(%s, ", opname[(int)m->op]); + dumpkey(&m->p.k); + printf(") => ("); + dumpval(&m->p, m->op, sharp); + printf(")"); +} + +void +gefs_dump_kvp(Kvp *kv, int sharp) +{ + if(kv == nil){ + printf("Kvp{nil}"); + return; + } + printf("Kvp("); + dumpkey(&kv->k); + printf(") => ("); + dumpval(kv, Onop, sharp); + printf(")"); +} + +void +gefs_dump_key(Key *k) +{ + if(k == nil){ + printf("Key{nil}"); + return; + } + printf("Key("); + dumpkey(k); + printf(")"); +} + +void +gefs_dump_arange(Arange *r) +{ + if(r == nil) + printf(""); + else + printf("Arange(%lld+%lld)", r->off, r->len); +} + +void +gefs_dump_qid(Qid q) +{ + printf("(%llx %lu %d)", q.path, q.vers, q.type); +} + +void +gefs_rshowblk(Blk *b, int indent, int recurse) +{ + Blk *c; + int i; + Bptr bp; + Kvp kv; + Msg m; + + if(indent > sizeof(spc)/4) + indent = sizeof(spc)/4; + if(b == nil){ + printf("NIL\n"); + return; + } + printf("%.*s[BLK]|{", 4*indent, spc); + gefs_dump_bptr(b->bp); + printf("}\n"); + switch(b->type){ + case Tpivot: + for(i = 0; i < b->nbuf; i++){ + getmsg(b, i, &m); + printf("%.*s[%03d]|", 4*indent, spc, i); + gefs_dump_msg(&m, 0); + printf("\n"); + } + /* wet floor */ + case Tleaf: + for(i = 0; i < b->nval; i++){ + getval(b, i, &kv); + if(b->type == Tpivot){ + printf("%.*s[%03d]|", 4*indent, spc, i); + gefs_dump_kvp(&kv, 1); + printf("\n"); + bp = unpackbp(kv.v.v, kv.v.nv); + c = gefs_getblk(bp, 0); + if(recurse) + gefs_rshowblk(c, indent + 1, 1); + gefs_dropblk(c); + }else{ + printf("%.*s[%03d]|", 4*indent, spc, i); + gefs_dump_kvp(&kv, 0); + printf("\n"); + } + } + break; + case Tarena: + printf("arena -- "); + goto Show; + case Tlog: + printf("log -- "); + goto Show; + case Tdlist: + printf("dlist -- "); + goto Show; + case Tdat: + printf("dat -- "); + Show: + for(i = 0; i < 32; i++){ + printf("%x", b->data[i] & 0xff); + if(i % 4 == 3) + printf(" "); + } + printf("\n"); + break; + } +} + +void +gefs_dump_blk(Blk *b, char *m, int recurse) +{ + printf("=== %s\n", m); + gefs_rshowblk(b, 0, recurse); +} + +void +gefs_dump_bp(Bptr bp, int recurse) +{ + Blk *b; + + b = gefs_getblk(bp, GBnochk); + gefs_rshowblk(b, 0, recurse); + gefs_dropblk(b); +} + +void +gefs_dump_treeroot(Tree *t) +{ + printf("\tflag\t0x%x\n", t->flag); + printf("\tgen:\t%lld\n", t->gen); + printf("\tbase\t%lld\n", t->base); + printf("\tpred:\t%lld\n", t->pred); + printf("\tsucc:\t%lld\n", t->succ); + printf("\tnref:\t%d\n", t->nref); + printf("\tnlbl:\t%d\n", t->nlbl); + printf("\tht:\t%d\n", t->ht); + printf("\tbp:\t"); + gefs_dump_bptr(t->bp); + printf("\n"); +} + +void +initshow(void) +{ + int i; + + memset(spc, ' ', sizeof(spc)); + for(i = 0; i < sizeof(spc); i += 4) + spc[i] = '|'; +} diff --git a/sys/gefs/error.c b/sys/gefs/error.c new file mode 100644 index 00000000000..c824f6f34ee --- /dev/null +++ b/sys/gefs/error.c @@ -0,0 +1,60 @@ +char Efs[] = "internal error"; +char Enoval[] = "message to missing key"; +char Ecorrupt[] = "block contents corrupted"; +char Efsvers[] = "unknown fs version"; +char Eimpl[] = "not implemented"; +char Ebotch[] = "protocol botch"; +char Eio[] = "i/o error"; +char Enofid[] = "unknown fid"; +char Efid[] = "fid in use"; +char Etype[] = "invalid fid type"; +char Edscan[] = "invalid dir scan offset"; +char Esrch[] = "directory entry not found"; +char Eexist[] = "create/wstat -- file exists"; +char Emode[] = "open/create -- unknown mode"; +char Efull[] = "file system full"; +char Estuffed[] = "emergency blocks exhausted"; +char Eauth[] = "authentication failed"; +char Elength[] = "name too long"; +char Eperm[] = "permission denied"; +char Einuse[] = "resource in use"; +char Ebadf[] = "invalid file"; +char Ename[] = "create/wstat -- bad character in file name"; +char Enomem[] = "out of memory"; +char Eattach[] = "attach required"; +char Enosnap[] = "attach -- bad specifier"; +char Edir[] = "invalid directory"; +char Esyntax[] = "syntax error"; +char Enouser[] = "user does not exist"; +char Enogrp[] = "group does not exist"; +char Efsize[] = "file too big"; +char Ebadu[] = "attach -- unknown user or failed authentication"; +char Erdonly[] = "file system read only"; +char Elocked[] = "open/create -- file is locked"; +char Eauthp[] = "authread -- auth protocol not finished"; +char Eauthd[] = "authread -- not enough data"; +char Eauthph[] = "auth phase error"; +char Enone[] = "auth -- user 'none' requires no authentication"; +char Enoauth[] = "auth -- authentication disabled"; +char Ephase[] = "phase error -- use after remove"; +char Ecdir[] = "create -- in a non-directory"; +char Ebadctl[] = "invalid control message"; +char Enoqid[] = "qids exhausted"; +char Enempty[] = "directory is not empty"; +char Enoadm[] = "missing adm snapshot"; +char Echeck[] = "check -- fs not ok"; +char Eopen[] = "read/write -- on non open fid"; +char Eoffset[] = "read/write -- offset negative"; + +char Esnapu[] = "snap -- is currently mounted"; +char Esnapx[] = "snap -- already exists"; +char Esnapr[] = "snap -- reserved name"; + +char Ewstatb[] = "wstat -- unknown bits in qid.type/mode"; +char Ewstatd[] = "wstat -- attempt to change directory"; +char Ewstatg[] = "wstat -- not in group"; +char Ewstatl[] = "wstat -- attempt to make length negative"; +char Ewstatm[] = "wstat -- attempt to change muid"; +char Ewstato[] = "wstat -- not owner or group leader"; +char Ewstatu[] = "wstat -- not owner"; +char Ewstatq[] = "wstat -- attempt to change qid"; diff --git a/sys/gefs/fns.h b/sys/gefs/fns.h new file mode 100644 index 00000000000..349fe37c2ac --- /dev/null +++ b/sys/gefs/fns.h @@ -0,0 +1,214 @@ +extern Gefs* fs; +extern int debug; +extern int permissive; +extern char* reamuser; +extern Errctx** errctx; +extern Blk* blkbuf; +extern int noneid; +extern int nogroupid; +extern int admid; +extern int fuzzonly; +extern ulong fuzzseed; +extern const struct vops gefs_vops; + +#define UNPACK8(p) (((uchar*)(p))[0]) +#define UNPACK16(p) ((((uchar*)(p))[0]<<8)|(((uchar*)(p))[1])) +#define UNPACK32(p) ((((uchar*)(p))[0]<<24)|(((uchar*)(p))[1]<<16)|\ + (((uchar*)(p))[2]<<8)|(((uchar*)(p))[3])) +#define UNPACK64(p) (((u64int)(((uchar*)(p))[0])<<56)|((u64int)(((uchar*)(p))[1])<<48)|\ + ((u64int)(((uchar*)(p))[2])<<40)|((u64int)(((uchar*)(p))[3])<<32)|\ + ((u64int)(((uchar*)(p))[4])<<24)|((u64int)(((uchar*)(p))[5])<<16)|\ + ((u64int)(((uchar*)(p))[6])<<8)|((u64int)(((uchar*)(p))[7]))) + +#define PACK8(p,v) do{(p)[0]=(v);}while(0) +#define PACK16(p,v) do{(p)[0]=(v)>>8;(p)[1]=(v);}while(0) +#define PACK32(p,v) do{(p)[0]=(v)>>24;(p)[1]=(v)>>16;(p)[2]=(v)>>8;(p)[3]=(v);}while(0) +#define PACK64(p,v) do{(p)[0]=(v)>>56;(p)[1]=(v)>>48;(p)[2]=(v)>>40;(p)[3]=(v)>>32;\ + (p)[4]=(v)>>24;(p)[5]=(v)>>16;(p)[6]=(v)>>8;(p)[7]=(v);}while(0) + +#define emalloc(sz, zero) malloc((sz), M_TEMP, M_WAITOK | ((zero) ? M_ZERO : 0)) + +Blk* gefs_newdblk(Tree*, vlong, int); +Blk* gefs_newblk(Tree*, int); +Blk* gefs_dupblk(Tree*, Blk*); +Blk* gefs_getroot(Tree*, int*); +Blk* gefs_getblk(Bptr, int); +Blk* gefs_holdblk(Blk*); +void gefs_dropblk(Blk*); +int gefs_rawio(Blk*, int); + +void lrutop(Blk*); +void lrubot(Blk*); + +void qinit(Syncq*); +void gefs_qput(Syncq*, Qent); + +Arena* getarena(vlong); +void gefs_syncblk(Blk*); +void gefs_enqueue(Blk*); +void gefs_epochstart(void); +void gefs_epochend(void); +int gefs_epochclean(void); +void gefs_limbo(int op, Limbo*); +void gefs_freeblk(Tree*, Blk*); +void gefs_freebp(Tree*, Bptr); +int gefs_logbarrier(Arena *, vlong); +void gefs_killblk(Tree*, Bptr); +ushort gefs_blkfill(Blk*); +uvlong gefs_blkhash(Blk*); +uvlong gefs_bufhash(void*, size_t); +u32int gefs_ihash(uvlong); +void gefs_finalize(Blk*); +int gefs_sync(struct mount *, int, int, struct ucred *, struct proc *); +Mount* gefs_getmount(char*); +void gefs_clunkmount(Mount*); +Dent* gefs_getdent(Mount*, vlong, Xdir*); +void gefs_clunkdent(Mount*, Dent*); +void gefs_touch(Dent*, Msg*); +int gefs_vget(struct mount*, ino_t, struct vnode**); + +Tree* gefs_updatesnap(Tree*, char*, int); +void gefs_tagsnap(Tree*, char*, int); +int gefs_delsnap(Tree*, vlong, char*); +void gefs_freedl(Dlist*); +Tree* opentree(vlong); +Tree* gefs_opensnap(char*, int*); +void gefs_closesnap(Tree*); +void reamfs(char*); +void growfs(char*); +int gefs_loadarena(Arena*, Bptr); +void loadfs(char*); +void gefs_loadlog(Arena*, Bptr); +void gefs_flushlog(Arena*); +int scandead(Dlist*, int, void(*)(Bptr, void*), void*); +int endfs(void); +int gefs_compresslog(Arena*); +void gefs_dlsync(void); +void setval(Blk*, Kvp*); + +Conn* newconn(int, int, int); +void putconn(Conn*); +int gefs_upsert(Mount*, Msg*, int); +int gefs_btupsert(Tree*, Msg*, int); +int btlookup(Tree*, Key*, Kvp*, char*, int); +void btnewscan(Scan*, char*, int); +int btenter(Tree*, Scan*); +int btnext(Scan*, Kvp*); +void btexit(Scan*); + +int gefs_checkflag(Blk *b, int, int); +void gefs_setflag(Blk *b, int, int); + +char* estrdup(char*); + +int gefs_keycmp(Key *, Key *); +void gefs_cpkey(Key*, Key*, char*, int); +void gefs_cpkvp(Kvp*, Kvp*, char*, int); + +/* for dumping */ +void getval(Blk*, int, Kvp*); +void getmsg(Blk*, int, Msg*); +Bptr getptr(Kvp*, int*); + +void initshow(void); +void gefs_dump_bptr(Bptr); +void gefs_dump_msg(Msg*, int); +void gefs_dump_kvp(Kvp*, int); +void gefs_dump_key(Key*); +void gefs_dump_arange(Arange*); +void gefs_dump_qid(Qid); +void gefs_dump_blk(Blk*, char*, int); +void gefs_dump_bp(Bptr, int); +void gefs_dump_treeroot(Tree*); +int checkfs(int); + + +#define dprint(...) \ +// if(debug) printf(__VA_ARGS__) + +#define fatal(...) \ + do { printf(__VA_ARGS__); panic("gefs fatal"); } while(0) + +#define tracex(msg, bp, v0, v1) do {} while(0) +#define traceb(msg, bp) do {} while(0) +#define tracev(msg, v0) do {} while(0) +#define tracem(msg) do {} while(0) + +#define bassert(b, cond) assert(cond) + +//label_t* _waserror(void); +//_Noreturn void error(char*, ...); +#define error(...) panic("%s", __VA_ARGS__) +_Noreturn void gefs_broke(char*, ...); +void writetrace(void*, vlong); +void _trace(char*, Bptr, vlong, vlong); +char* packstr(char*, char*, char*, int); + +void dir2kv(vlong, Xdir*, Kvp*, char*, int); +void dir2statbuf(Xdir*, struct vattr*); +void dlist2kv(Dlist*, Kvp*, char*, int); +void lbl2kv(char*, vlong, uint, Kvp*, char*, int); +void link2kv(vlong, vlong, Kvp*, char*, int); +char* retag2kv(vlong, vlong, int, int, Kvp*, char*, int); +void tree2kv(Tree*, Kvp*, char*, int); + +void kv2dir(Kvp*, Xdir*); +void kv2dlist(Kvp*, Dlist*); +void kv2link(Kvp*, vlong*, vlong*); +void kv2qid(Kvp*, Qid*); +void kv2statbuf(Kvp*, struct vattr*); +int kv2dirent(Kvp*, char*, int); + +char* packarena(char*, int, Arena*); +char* packbp(char*, int, Bptr*); +char* packdkey(char*, int, vlong, char*, int); +char* packdval(char*, int, Xdir*); +char* packlbl(char*, int, char*); +char* packsnap(char*, int, vlong); +char* packsuper(char*, int, vlong); +char* packtree(char*, int, Tree*); +char* packsb(char*, int, Gefs*); + +char* unpackarena(Arena*, char*, int); +Bptr unpackbp(char*, int); +char* unpackdkey(char*, int, vlong*); +Tree* unpacktree(Tree*, char*, int); +int gefs_unpacksb(Gefs*, char*, int); +char* unpackstr(char*, char*, char**); + +Chan* gefs_mkchan(int); +void* gefs_chrecv(Chan*); +void gefs_chsend(Chan*, void*); +void gefs_freeamsg(Amsg*); +void runfs(int, void*); +void runmutate(int, void*); +void runread(int, void*); +void runcons(int, void*); +void runtasks(int, void*); +void runsync(int, void*); +int gefs_syncfs(void); +void gefs_sweep(void*); +void runsnap(int, void*); + +int gefs_v2d(int); +int gefs_d2v(int); +int gefs_d2m(int); + +/* fuzzing and testing */ +void fzinit(void); +void fzwrite(int, void*); +void fzscan(int, void*); + +/* debug prints */ +void gefs_rshowblk(Blk*, int, int); + +/* vnops */ +int gefs_allocvp(struct mount*, struct vnode**, vlong); + +/* Plan 9 compatibility */ +#define ASSERT_LOCKED(lk) assert(rw_status(lk) != 0) +#define agetv(p) ((vlong)atomic_load_long((void*)p)) +#define agetp(p) (*(p)) +#define acasp(p, o, n) atomic_cas_ptr(p, o, n) +#define acasl(p, o, n) atomic_cas_uint(p, o, n) +#define getcallerpc(a) ((uintptr)__builtin_return_address(0)) diff --git a/sys/gefs/hash.c b/sys/gefs/hash.c new file mode 100644 index 00000000000..edd03a9f876 --- /dev/null +++ b/sys/gefs/hash.c @@ -0,0 +1,141 @@ +// metrohash64.cpp +// +// The MIT License (MIT) +// +// Copyright (c) 2015 J. Andrew Rogers +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +#include +#include +#include +#include +#include +#include +#include + +#include "u.h" +#include "avl.h" + +#include "dat.h" +#include "fns.h" + +#define rotate_right(v, k)\ + (((v) >> (k)) | ((v) << (64 - (k)))) +#define read_u64(ptr) \ + (*(u64int*)ptr) +#define read_u32(ptr) \ + (*(u32int*)ptr) +#define read_u16(ptr) \ + (*(u16int*)ptr) +#define read_u8(ptr) \ + (*(u8int*)ptr) + +uvlong +gefs_metrohash64_1(void * key, u64int len, u32int seed) +{ + enum { + k0 = 0xC83A91E1ULL, + k1 = 0x8648DBDBULL, + k2 = 0x7BDEC03BULL, + k3 = 0x2F5870A5ULL, + }; + + const uchar * ptr = key; + const uchar * const end = ptr + len; + u64int v0, v1, v2, v3, t; + + u64int hash = ((((u64int) seed) + k2) * k0) + len; + + if(len >= 32){ + v0 = hash; + v1 = hash; + v2 = hash; + v3 = hash; + + do{ + v0 += read_u64(ptr) * k0; v0 = rotate_right(v0, 29) + v2; ptr += 8; + v1 += read_u64(ptr) * k1; v1 = rotate_right(v1, 29) + v3; ptr += 8; + v2 += read_u64(ptr) * k2; v2 = rotate_right(v2, 29) + v0; ptr += 8; + v3 += read_u64(ptr) * k3; v3 = rotate_right(v3, 29) + v1; ptr += 8; + }while(ptr <= (end - 32)); + + t = ((v0 + v3) * k0) + v1; v2 ^= rotate_right(t, 33) * k1; + t = ((v1 + v2) * k1) + v0; v3 ^= rotate_right(t, 33) * k0; + t = ((v0 + v2) * k0) + v3; v0 ^= rotate_right(t, 33) * k1; + t = ((v1 + v3) * k1) + v2; v1 ^= rotate_right(t, 33) * k0; + hash += v0 ^ v1; + } + + if((end - ptr) >= 16){ + v0 = hash + (read_u64(ptr) * k0); ptr += 8; v0 = rotate_right(v0, 33) * k1; + v1 = hash + (read_u64(ptr) * k1); ptr += 8; v1 = rotate_right(v1, 33) * k2; + t = v0 * k0; v0 ^= rotate_right(t, 35) + v1; + t = v1 * k3; v1 ^= rotate_right(t, 35) + v0; + hash += v1; + } + + if((end - ptr) >= 8){ + hash += read_u64(ptr) * k3; ptr += 8; + hash ^= rotate_right(hash, 33) * k1; + + } + + if((end - ptr) >= 4){ + hash += read_u32(ptr) * k3; ptr += 4; + hash ^= rotate_right(hash, 15) * k1; + } + + if((end - ptr) >= 2){ + hash += read_u16(ptr) * k3; ptr += 2; + hash ^= rotate_right(hash, 13) * k1; + } + + if((end - ptr) >= 1){ + hash += read_u8 (ptr) * k3; + hash ^= rotate_right(hash, 25) * k1; + } + + hash ^= rotate_right(hash, 33); + hash *= k0; + hash ^= rotate_right(hash, 33); + + return hash; +} + +uvlong +gefs_bufhash(void *src, size_t len) +{ + return gefs_metrohash64_1(src, len, 0x6765); +} + +uvlong +gefs_blkhash(Blk *b) +{ + return gefs_metrohash64_1(b->b->b_data, Blksz, 0x6765); +} + +u32int +gefs_ihash(uvlong x) +{ + x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9ULL; + x = (x ^ (x >> 27)) * 0x94d049bb133111ebULL; + x = x ^ (x >> 31); + return x; +} diff --git a/sys/gefs/load.c b/sys/gefs/load.c new file mode 100644 index 00000000000..134f5a0d40a --- /dev/null +++ b/sys/gefs/load.c @@ -0,0 +1,417 @@ +#include +#include +#include +#include +#include +#include +#include +#include /* XXX */ /* defines v_rdev */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "u.h" +#include "avl.h" + +#include "dat.h" +#include "fns.h" + +Gefs *fs; + +int +gefs_rangecmp(Avl *a, Avl *b) +{ + if(((Arange*)a)->off < ((Arange*)b)->off) + return -1; + if(((Arange*)a)->off > ((Arange*)b)->off) + return 1; + return 0; +} + +int +gefs_sync(struct mount *mp, int wait, int stall, struct ucred *cred, struct proc *p) +{ + return gefs_syncfs(); +} + +int +gefs_loadarena(Arena *a, Bptr hd) +{ + Blk *h0, *h1, *b; + Bptr bp; + + /* try to load block pointers with consistency check */ + bp = hd; + h0 = gefs_getblk(bp, 0); + bp.addr += Blksz; + h1 = gefs_getblk(bp, 0); + + /* if neither head nor tail is consistent, we're hosed */ + b = (h0 != nil) ? h0 : h1; + if(b == nil) + return -1; + + /* otherwise, we could have crashed mid-pass, just load the blocks */ + bp = hd; + if(h0 == nil) + h0 = gefs_getblk(bp, GBnochk); + bp.addr += Blksz; + if(h1 == nil) + h1 = gefs_getblk(bp, GBnochk); + + unpackarena(a, b->data, Arenasz); + rw_init(&a->lk, IPL_NONE); + if((a->free = avlcreate(gefs_rangecmp)) == nil) + return -1; + + /* allocate in-memory log working buffers */ + if((a->logbuf[0] = malloc(sizeof(Blk), M_TEMP, M_WAITOK|M_ZERO)) == NULL) + return -1; + if((a->logbuf[1] = malloc(sizeof(Blk), M_TEMP, M_WAITOK|M_ZERO)) == NULL) + return -1; + a->logbuf[0]->b = geteblk(Blksz); + a->logbuf[1]->b = geteblk(Blksz); + if(a->logbuf[0]->b == nil || a->logbuf[1]->b == nil) + return -1; + a->logbuf[0]->data = a->logbuf[0]->b->b_data; + a->logbuf[1]->data = a->logbuf[1]->b->b_data; + a->logbuf[0]->bp = (Bptr){-1, -1, -1}; + a->logbuf[1]->bp = (Bptr){-1, -1, -1}; + a->logbuf[0]->ref = 1; + a->logbuf[1]->ref = 1; + gefs_setflag(a->logbuf[0], Bstatic, 0); + gefs_setflag(a->logbuf[1], Bstatic, 0); + a->h0 = h0->bp; + a->h1 = h1->bp; + gefs_dropblk(h0); + gefs_dropblk(h1); + a->used = a->size; + return 0; +} + +int +gefs_attach(struct mount *mp, char *aname, struct vnode **vpp) +{ + char dbuf[Kvmax], kvbuf[Kvmax]; + struct vnode *vp; + Mount *mnt; + Dent *de; + Tree *t; + char *p; + int e; + Xdir d; + Kvp kv; + Key dk; + + if((e = getnewvnode(VT_GEFS, mp, &gefs_vops, &vp)) != 0){ + return e; +} + + if((mnt = gefs_getmount(aname)) == nil){ + vput(vp); + return ENOENT; + } +// if(strcmp(aname, "dump") == 0){ +// if(uid == noneid) +// error(Eperm); +// memset(&d, 0, sizeof(d)); +// filldumpdir(&d); +// }else{ + { + if((t = gefs_opensnap(aname, nil)) == nil){ + vput(vp); + return ENOENT; + } + p = packdkey(dbuf, sizeof(dbuf), -1ULL, "", 0); + dk.k = dbuf; + dk.nk = p - dbuf; + if((e = btlookup(t, &dk, &kv, kvbuf, sizeof(kvbuf))) != 0){ + gefs_closesnap(t); + vput(vp); + return e; + } + kv2dir(&kv, &d); + gefs_closesnap(t); + } + + de = gefs_getdent(mnt, -1, &d); + if(de == nil){ + vput(vp); + return ENOMEM; + } +// +// f = malloc(sizeof(*f), M_TEMP, M_WAITOK | M_ZERO); +// f->fid = ~0U; +// f->mnt = mnt; +// f->qpath = d.qid.path; +// f->pqpath = d.qid.path; +// f->mode = -1; +// f->iounit = Blksz; +// f->dent = de; +// f->dir = de; +// f->uid = 0; +// f->duid = d.uid; +// f->dgid = d.gid; +// f->dmode = d.mode; +// rw_init(&f->lk, "gefs"); + + vp->v_type = VDIR; + vp->v_flag |= VROOT; + vp->v_data = de; + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); + mp->mnt_data = mnt; + *vpp = vp; + return 0; +} + +int +gefs_root(struct mount *mp, struct vnode **vpp) +{ + return gefs_attach(mp, "main", vpp); +} + +int +gefs_mount(struct mount *mp, const char *path, void *data, + struct nameidata *ndp, struct proc *p) +{ + struct gefs_args *args = data; + struct vnode *devvp; + char fspec[MNAMELEN]; + char fname[MNAMELEN]; + struct disklabel dl; + Blk *sb0, *sb1; + int ok1, ok2; + Mount *dump; + Arena *a; + int i, e; + vlong eb; + + dump = NULL; + printf("mounting %s\n", path); + if (mp->mnt_flag & MNT_UPDATE) { + printf("no updates allowed\n"); + return ENODEV; + } + if(fs != nil){ + printf("FIXME: only one gefs at a time"); + return ENODEV; + } + + if((fs = emalloc(sizeof(Gefs), 1)) == nil) + return ENOMEM; + + if((e = copyinstr(args->fspec, fspec, sizeof(fspec), NULL)) != 0) + goto error; + printf("gefs_mount: fspec=%s, path=%s\n", fspec, path); + if (disk_map(fspec, fname, sizeof(fname), DM_OPENBLCK) == -1) + bcopy(fspec, fname, sizeof(fname)); + NDINIT(ndp, LOOKUP, FOLLOW, UIO_SYSSPACE, fname, p); + if ((e = namei(ndp)) != 0){ + goto error; + } + devvp = ndp->ni_vp; + if (devvp->v_type != VBLK) { + e = ENOTBLK; + goto error; + } + if (major(devvp->v_rdev) >= nblkdev) { + e = ENXIO; + goto error; + } + if((e = VOP_OPEN(devvp, FREAD|FWRITE, FSCRED, p)) != 0) + goto error; + if((e = VOP_IOCTL(devvp, DIOCGDINFO, (caddr_t)&dl, FREAD, NOCRED, curproc)) != 0){ + goto error1; + } + eb =DL_GETDSIZE(&dl)*dl.d_secsize; + eb = eb - (eb%Blksz) - Blksz; + fs->sb0 = (Bptr){0, -1, -1}; + fs->sb1 = (Bptr){eb, -1, -1}; + + if((dump = malloc(sizeof(Mount), M_TEMP, M_WAITOK|M_ZERO)) == NULL) + return ENOMEM; + rw_init(&dump->lk, IPL_NONE); + rw_init(&dump->dtablk, IPL_NONE); + + fs->dev = devvp; + + rw_init(&fs->bpinlk, IPL_NONE); + rw_init(&fs->synclk, IPL_NONE); + rw_init(&fs->mountlk, IPL_NONE); + rw_init(&fs->mutlk, IPL_NONE); + rw_init(&fs->bfreelk, IPL_NONE); + rw_init(&fs->quitlk, IPL_NONE); + + fs->snapmnt = dump; + fs->narena = 1; + sb0 = gefs_getblk(fs->sb0, GBnochk); + sb1 = gefs_getblk(fs->sb1, GBnochk); + if(sb0 == NULL || sb1 == NULL){ + e = EINVAL; + goto error2; + } + ok1 = gefs_unpacksb(fs, sb0->data, Blksz); + ok2 = gefs_unpacksb(fs, sb1->data, Blksz); + if(ok1 == -1) + printf("unable to load primary superblock\n"); + if(ok2 == -1) + printf("unable to load secondary superblock\n"); + if(ok1 == -1 && ok2 == -1){ + printf("no valid superblock\n"); + e = EINVAL; + goto error2; + } + + snprintf(dump->name, sizeof(dump->name), "dump"); + dump->ref = 1; + dump->gen = -1; + dump->root = &fs->snap; + + if((fs->arenas = emalloc(fs->narena * sizeof(Arena), 1)) == nil){ + e = ENOMEM; + goto error2; + } + for(i = 0; i < fs->narena; i++){ + a = &fs->arenas[i]; + if(gefs_loadarena(a, fs->arenabp[i]) != 0){ + e = EIO; + goto error2; + } + a->reserve = a->size / 1024; + if(a->reserve < 512*KiB) + a->reserve = 512*KiB; + if(a->reserve > 8*MiB) + a->reserve = 8*MiB; + } + for(i = 0; i < fs->narena; i++){ + Arange *fmax; + + Arange *fr; + + a = &fs->arenas[i]; + gefs_loadlog(a, a->loghd); + /* XXX bringup */ + fmax = (Arange*)avlmax(a->free); + printf("\tarena %d: loghd %llx nlog %lld size %lld used %lld freetop %llx snaproot %llx\n", + i, a->loghd.addr, a->nlog, a->size, a->used, + fmax ? fmax->off : -1, fs->snap.bp.addr); + /* XXX bringup: dump the top of the free set so we can see + * whether live metadata blocks (7ff..) are wrongly free */ + for(fr = (Arange*)avlmax(a->free); fr != nil; fr = (Arange*)avlprev(&fr->avl)) + printf("\t\tfree %llx+%llx\n", fr->off, fr->len); + } + fs->dlcmax = 128; + fs->dlcache = malloc(fs->dlcmax*sizeof(Dlist*), M_TEMP, M_WAITOK|M_ZERO); + fs->admchan = gefs_mkchan(32); + kthread_create(gefs_sweep, fs->admchan, NULL, "gefs-sweep"); + + printf("load %s:\n", path); + printf("\tsnaptree:\t%llx\n", fs->snap.bp.addr); + printf("\tnarenas:\t%d\n", fs->narena); + printf("\tfeatures:\t%lld\n", fs->flag); + printf("\tnextqid:\t%lld\n", fs->nextqid); + printf("\tlastqgen:\t%lld\n", agetv(&fs->qgen)); + printf("\tnextgen:\t%lld\n", fs->nextgen); + printf("\tblocksize:\t%lld\n", (vlong)Blksz); + +// mp->mnt_data = (void *)fs; + mp->mnt_flag |= MNT_LOCAL; + mp->mnt_stat.f_iosize = Blksz; + gefs_dropblk(sb0); + gefs_dropblk(sb1); + + return 0; + +error2: + gefs_dropblk(sb0); + gefs_dropblk(sb1); + free(fs->dlcache, M_TEMP, fs->dlcmax*sizeof(Dlist*)); + free(fs->arenas, M_TEMP, fs->narena*sizeof(Arena)); + free(dump, M_TEMP, sizeof(Mount)); + free(fs, M_TEMP, sizeof(Gefs)); +error1: + VOP_CLOSE(devvp, FREAD, NOCRED, curproc); +error: + return e; +} + +int +gefs_unmount(struct mount *mp, int mntflags, struct proc *p) +{ + Amsg *am; + int flags = 0; + int e; + + printf("gefs_unmount\n"); + if (mntflags & MNT_FORCE) + flags |= FORCECLOSE; + + am = malloc(sizeof(Amsg), M_TEMP, M_WAITOK|M_ZERO); + am->op = AOhalt; + rw_enter(&fs->quitlk, RW_WRITE); + fs->quitrz = -1; + gefs_chsend(fs->admchan, am); + while(fs->quitrz == -1) + rwsleep(&fs->quitrz, &fs->quitlk, IPL_NONE, "gefsquit", 0); + rw_exit(&fs->quitlk); + if ((e = fs->quitrz) != 0) + return e; + if ((e = vflush(mp, NULL, flags)) != 0) + return e; + + VOP_CLOSE(fs->dev, FREAD|FWRITE, NOCRED, p); + vrele(fs->dev); + + free(fs->arenas, M_TEMP, fs->narena * sizeof(Arena)); + free(fs, M_TEMP, sizeof(Gefs)); + fs = nil; + mp->mnt_data = NULL; + + return 0; +} + +int +gefs_statfs(struct mount *mp, struct statfs *sbp, struct proc *p) +{ + printf("gefs_statfs\n"); + sbp->f_bsize = Blksz; + sbp->f_iosize = Blksz; + sbp->f_blocks = 0; + sbp->f_bfree = 0; + sbp->f_bavail = 0; + sbp->f_files = 0; + sbp->f_ffree = 0; + sbp->f_favail = 0; + copy_statfs_info(sbp, mp); + return 0; +} + +int +gefs_start(struct mount *mp, int flags, struct proc *p) +{ + return 0; +} + +const struct vfsops gefs_vfsops = { + .vfs_mount = gefs_mount, + .vfs_start = gefs_start, + .vfs_unmount = gefs_unmount, + .vfs_root = gefs_root, + .vfs_quotactl = NULL, + .vfs_statfs = gefs_statfs, + .vfs_sync = gefs_sync, + .vfs_vget = gefs_vget, + .vfs_fhtovp = NULL, + .vfs_vptofh = NULL, + .vfs_init = NULL, + .vfs_sysctl = NULL, + .vfs_checkexp = NULL, +}; diff --git a/sys/gefs/pack.c b/sys/gefs/pack.c new file mode 100644 index 00000000000..9d64de33954 --- /dev/null +++ b/sys/gefs/pack.c @@ -0,0 +1,591 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "u.h" +#include "avl.h" + +#include "dat.h" +#include "fns.h" + +/* Terminated so we can use them directly in C */ +char* +unpackstr(char *p, char *e, char **s) +{ + int n; + + assert(e - p >= 3); + n = UNPACK16(p); + if(e - p < n + 3 || p[n+2] != 0) + panic("broken string"); + *s = p+2; + return p+3+n; +} + +/* Terminated so we can use them directly in C */ +char* +packstr(char *p, char *e, char *s, int n) +{ + assert(e - p >= n+3); + PACK16(p, n); p += 2; + memmove(p, s, n); p += n; + *p = 0; p += 1; + return p; +} + +void +dir2kv(vlong up, Xdir *d, Kvp *kv, char *buf, int nbuf) +{ + char *ek, *ev, *eb; + + ek = packdkey(buf, nbuf, up, d->name, strlen(d->name)); + kv->k.k = buf; + kv->k.nk = ek - buf; + eb = buf + nbuf; + ev = packdval(ek, eb - ek, d); + kv->v.v = ek; + kv->v.nv = ev - ek; +} + +char* +packdkey(char *p, int sz, vlong up, char *name, int nname) +{ + char *ep; + + ep = p + sz; + PACK8(p, Kent); p += 1; + PACK64(p, up); p += 8; + if(name != nil) + p = packstr(p, ep, name, nname); + return p; +} + +char* +unpackdkey(char *p, int sz, vlong *up) +{ + char key, *ep, *name; + + ep = p + sz; + assert(sz > 9); + key = UNPACK8(p); p += 1; + *up = UNPACK64(p); p += 8; + assert(key == Kent); + p = unpackstr(p, ep, &name); + assert(p <= ep); + return name; +} + +char* +packsuper(char *p, int sz, vlong up) +{ + char *ep; + + ep = p+sz; + PACK8(p, Kup); p += 1; + PACK64(p, up); p += 8; + assert(p <= ep); + return p; +} + +char* +packdval(char *p, int sz, Xdir *d) +{ + char *e; + + e = p + sz; + PACK64(p, d->flag); p += 8; + PACK64(p, d->qid.path); p += 8; + PACK32(p, d->qid.vers); p += 4; + PACK8(p, d->qid.type); p += 1; + PACK32(p, d->mode); p += 4; + PACK64(p, d->atime); p += 8; + PACK64(p, d->mtime); p += 8; + PACK64(p, d->length); p += 8; + PACK32(p, d->uid); p += 4; + PACK32(p, d->gid); p += 4; + PACK32(p, d->muid); p += 4; + if (d->flag & GEFS_FDEVNOD) { + PACK32(p, d->devt); p += 4; + PACK32(p, d->rdev); p += 4; + } + assert(p <= e); + return p; +} + +void +kv2dir(Kvp *kv, Xdir *d) +{ + char *k, *ek, *v, *ev; + + memset(d, 0, sizeof(Xdir)); + k = kv->k.k + 9; + ek = kv->k.k + kv->k.nk; + assert(k <= ek); + k = unpackstr(k, ek, &d->name); + + v = kv->v.v; + ev = v + kv->v.nv; + d->flag = UNPACK64(v); v += 8; + d->qid.path = UNPACK64(v); v += 8; + d->qid.vers = UNPACK32(v); v += 4; + d->qid.type = UNPACK8(v); v += 1; + d->mode = UNPACK32(v); v += 4; + d->atime = UNPACK64(v); v += 8; + d->mtime = UNPACK64(v); v += 8; + d->length = UNPACK64(v); v += 8; + d->uid = UNPACK32(v); v += 4; + d->gid = UNPACK32(v); v += 4; + d->muid = UNPACK32(v); v += 4; + if (d->flag & GEFS_FDEVNOD) { + d->devt = UNPACK32(v); v += 4; + d->rdev = UNPACK32(v); v += 4; + }else{ + d->devt = 0; + d->rdev = 0; + } + assert(v <= ev); + if(k != ek) + panic("broken dir"); + if(v != ev) + panic("broken dir"); +} + +void +dir2statbuf(Xdir *d, struct vattr *va) +{ + va->va_rdev = 0; + if (d->flag & GEFS_FSYMLINK){ + va->va_type = VLNK; + va->va_mode = S_IFLNK | (d->mode & 0777); + }else if (d->flag & GEFS_FDEVNOD) { + va->va_type = gefs_d2v(d->devt); + va->va_rdev = d->rdev; + va->va_mode = gefs_d2m(d->devt) | (d->mode & 0777); + }else if (d->qid.type & GEFS_QTDIR){ + va->va_type = VDIR; + va->va_mode = S_IFDIR | (d->mode & 0777); + }else{ + va->va_type = VREG; + va->va_mode = (d->mode & 0777); + } + va->va_uid = d->uid; + va->va_gid = d->gid; + va->va_nlink = 1; + va->va_uid = d->uid; + va->va_gid = d->gid; + va->va_fsid = 42; // TODO + va->va_fileid = d->qid.path; + va->va_size = d->length; + va->va_blocksize = Blksz; + va->va_atime.tv_sec = d->atime/Nsec; + va->va_atime.tv_nsec = d->atime%Nsec; + va->va_mtime.tv_sec = d->mtime/Nsec; + va->va_mtime.tv_nsec = d->mtime%Nsec; + va->va_ctime.tv_sec = d->mtime/Nsec; + va->va_ctime.tv_nsec = d->mtime%Nsec; + va->va_gen = d->qid.vers; + va->va_flags = 0; // TODO + va->va_bytes = d->length; + va->va_filerev = d->qid.vers; + va->va_vaflags = 0; + va->va_spare = 0; +} + +void +kv2statbuf(Kvp *kv, struct vattr *va) +{ + Xdir d; + + kv2dir(kv, &d); + dir2statbuf(&d, va); +} + +void +kv2qid(Kvp *kv, Qid *q) +{ + char *v, *e; + + v = kv->v.v; + e = v + kv->v.nv; + q->path = UNPACK64(v); v += 8; + q->vers = UNPACK64(v); v += 8; + assert(v <= e); +} + +void +kv2dlist(Kvp *kv, Dlist *dl) +{ + char *p, *e; + + p = kv->k.k; + e = p + kv->k.nk; + p++; + dl->gen = UNPACK64(p); p += 8; + dl->bgen = UNPACK64(p); p += 8; + assert(p <= e); + + p = kv->v.v; + e = p + kv->v.nv; + dl->hd = unpackbp(p, e-p); p += Ptrsz; + dl->tl = unpackbp(p, e-p); p += Ptrsz; + assert(p <= e); +} + +void +dlist2kv(Dlist *dl, Kvp *kv, char *buf, int nbuf) +{ + char *p, *e; + + assert(nbuf >= Dlkvpsz); + p = buf; + e = buf+nbuf; + + kv->k.k = p; + *p++ = Kdlist; + PACK64(p, dl->gen); p += 8; + PACK64(p, dl->bgen); p += 8; + kv->k.nk = (p - kv->k.k); + + kv->v.v = p; + p = packbp(p, e-p, &dl->hd); + p = packbp(p, e-p, &dl->tl); + kv->v.nv = (p - kv->v.v); +} + +void +tree2kv(Tree *t, Kvp *kv, char *buf, int nbuf) +{ + char *p, *e; + + p = buf; + e = buf+nbuf; + + kv->k.k = p; + if((p = packsnap(p, e-p, t->gen)) == nil) + panic("packsnap"); + kv->k.nk = p - kv->k.k; + + kv->v.v = p; + if((p = packtree(p, e-p, t)) == nil) + panic("packtree"); + kv->v.nv = p - kv->v.v; +} + +char* +retag2kv(vlong gen, vlong link, int dlbl, int dref, Kvp *kv, char *buf, int nbuf) +{ + char *p; + + assert(gen != -1); + assert(nbuf >= 8+1+1); + kv->k.k = buf; + if((p = packsnap(buf, nbuf, gen)) == nil) + panic("packsnap"); + kv->k.nk = p - buf; + + kv->v.v = p; + PACK64(p, link); p += 8; + *p = dlbl; p += 1; + *p = dref; p += 1; + kv->v.nv = p - kv->v.v; + return p; +} + +void +lbl2kv(char *lbl, vlong gen, uint flg, Kvp *kv, char *buf, int nbuf) +{ + char *p; + int n; + + n = strlen(lbl); + assert(nbuf >= 1+n + 1+8+4); + + p = buf; + kv->k.k = p; + p[0] = Klabel; p += 1; + memcpy(p, lbl, n); p += n; + kv->k.nk = p - kv->k.k; + + kv->v.v = p; + p[0] = Ksnap; p += 1; + PACK64(p, gen); p += 8; + PACK32(p, flg); p += 4; + kv->v.nv = p - kv->v.v; +} + +char* +packlbl(char *p, int sz, char *name) +{ + int n; + + n = strlen(name); + assert(sz >= n+1); + p[0] = Klabel; p += 1; + memcpy(p, name, n); p += n; + return p; +} + +char* +packsnap(char *p, int sz, vlong id) +{ + assert(sz >= Snapsz); + p[0] = Ksnap; p += 1; + PACK64(p, id); p += 8; + return p; +} + +char* +packbp(char *p, int sz, Bptr *bp) +{ + assert(sz >= Ptrsz); + PACK64(p, bp->addr); p += 8; + PACK64(p, bp->hash); p += 8; + PACK64(p, bp->gen); p += 8; + return p; +} + +Bptr +unpackbp(char *p, int sz) +{ + Bptr bp; + + assert(sz >= Ptrsz); + bp.addr = UNPACK64(p); p += 8; + bp.hash = UNPACK64(p); p += 8; + bp.gen = UNPACK64(p); + return bp; +} + +Tree* +unpacktree(Tree *t, char *p, int sz) +{ + assert(sz >= Treesz); + memset(t, 0, sizeof(Tree)); + t->nref = UNPACK32(p); p += 4; + t->nlbl = UNPACK32(p); p += 4; + t->ht = UNPACK32(p); p += 4; + t->flag = UNPACK32(p); p += 4; + t->gen = UNPACK64(p); p += 8; + t->pred = UNPACK64(p); p += 8; + t->succ = UNPACK64(p); p += 8; + t->base = UNPACK64(p); p += 8; + t->bp.addr = UNPACK64(p); p += 8; + t->bp.hash = UNPACK64(p); p += 8; + t->bp.gen = UNPACK64(p); //p += 8; + + return t; +} + +char* +packtree(char *p, int sz, Tree *t) +{ + assert(sz >= Treesz); + PACK32(p, t->nref); p += 4; + PACK32(p, t->nlbl); p += 4; + PACK32(p, t->ht); p += 4; + PACK32(p, t->flag); p += 4; + PACK64(p, t->gen); p += 8; + PACK64(p, t->pred); p += 8; + PACK64(p, t->succ); p += 8; + PACK64(p, t->base); p += 8; + PACK64(p, t->bp.addr); p += 8; + PACK64(p, t->bp.hash); p += 8; + PACK64(p, t->bp.gen); p += 8; + return p; +} + +char* +packarena(char *p, int sz, Arena *a) +{ + char *e; + + assert(sz >= Arenasz); + e = p + Arenasz; + PACK64(p, a->loghd.addr); p += 8; /* freelist addr */ + PACK64(p, a->loghd.hash); p += 8; /* freelist hash */ + PACK64(p, a->size); p += 8; /* arena size */ + PACK64(p, a->used); p += 8; /* arena used */ + assert(p <= e); + return p; +} + +char* +unpackarena(Arena *a, char *p, int sz) +{ + char *e; + + assert(sz >= Arenasz); + memset(a, 0, sizeof(*a)); + + e = p + Arenasz; + a->loghd.addr = UNPACK64(p); p += 8; + a->loghd.hash = UNPACK64(p); p += 8; + a->loghd.gen = -1; p += 0; + a->size = UNPACK64(p); p += 8; + a->used = UNPACK64(p); p += 8; + a->logtl = nil; + + assert(p <= e); + return p; +} + +char* +packsb(char *p0, int sz, Gefs *fi) +{ + vlong nextqid, nextgen, qgen; + uvlong h; + char *p; + int i; + + assert(sz == Blksz); + assert(fi->narena < 512); + p = p0; + nextqid = fi->nextqid; + nextgen = fi->nextgen; + qgen = agetv(&fi->qgen); + memcpy(p, "gefs9.00", 8); p += 8; + PACK32(p, Blksz); p += 4; + PACK32(p, Bufspc); p += 4; + PACK32(p, fi->narena); p += 4; + PACK32(p, fi->snap.ht); p += 4; + PACK64(p, fi->snap.bp.addr); p += 8; + PACK64(p, fi->snap.bp.hash); p += 8; + PACK64(p, fi->snapdl.hd.addr); p += 8; + PACK64(p, fi->snapdl.hd.hash); p += 8; + PACK64(p, fi->snapdl.tl.addr); p += 8; + PACK64(p, fi->snapdl.tl.hash); p += 8; + PACK64(p, fi->flag); p += 8; + PACK64(p, nextqid); p += 8; + PACK64(p, nextgen); p += 8; + PACK64(p, qgen); p += 8; + for(i = 0; i < fi->narena; i++){ + PACK64(p, fi->arenabp[i].addr); p += 8; + PACK64(p, fi->arenabp[i].hash); p += 8; + } + h = gefs_bufhash(p0, p - p0); + PACK64(p, h); p += 8; + return p; +} + +int +gefs_unpacksb(Gefs *fi, char *p0, int sz) +{ + vlong qgen; + uvlong dh, xh; + char *p; + int i; + + assert(sz == Blksz); + p = p0; + if(memcmp(p, "gefs9.00", 8) != 0){ + printf("unknown fs type\n"); + return -1; + } + p += 8; + fi->blksz = UNPACK32(p); p += 4; + fi->bufspc = UNPACK32(p); p += 4; + fi->narena = UNPACK32(p); p += 4; + fi->snap.ht = UNPACK32(p); p += 4; + fi->snap.bp.addr = UNPACK64(p); p += 8; + fi->snap.bp.hash = UNPACK64(p); p += 8; + fi->snap.bp.gen = -1; p += 0; + fi->snapdl.hd.addr = UNPACK64(p); p += 8; + fi->snapdl.hd.hash = UNPACK64(p); p += 8; + fi->snapdl.hd.gen = -1; p += 0; + fi->snapdl.gen = -1; p += 0; + fi->snapdl.tl.addr = UNPACK64(p); p += 8; + fi->snapdl.tl.hash = UNPACK64(p); p += 8; + fi->snapdl.hd.gen = -1; p += 0; + fi->snapdl.gen = -1; p += 0; + fi->flag = UNPACK64(p); p += 8; + fi->nextqid = UNPACK64(p); p += 8; + fi->nextgen = UNPACK64(p); p += 8; + qgen = UNPACK64(p); p += 8; + atomic_swap_64(&fs->qgen, qgen); + fi->arenabp = emalloc(fi->narena * sizeof(Bptr), 0); + for(i = 0; i < fi->narena; i++){ + fi->arenabp[i].addr = UNPACK64(p); p += 8; + fi->arenabp[i].hash = UNPACK64(p); p += 8; + fi->arenabp[i].gen = -1; + } + xh = gefs_bufhash(p0, p - p0); + dh = UNPACK64(p); p += 8; + if(dh != xh){ + printf("bad superblock hash\n"); + return -1; + } + assert(fi->narena < 256); /* should be more than anyone needs */ + return 0; +} + +int +kv2dirent(Kvp *kv, char *buf, int nbuf) +{ + struct dirent *dp = (struct dirent *)buf; + char *name; + uvlong qpath; + ulong mode; + int namlen, reclen; + + unpackstr(kv->k.k + 9, kv->k.k + kv->k.nk, &name); + namlen = strlen(name); + reclen = DIRENT_RECSIZE(namlen); + + if(reclen > nbuf) + return -1; + + qpath = UNPACK64(kv->v.v + 8); /* after flag[8] */ + mode = UNPACK32(kv->v.v + 21); /* after flag[8]+qid.path[8]+vers[4]+type[1] */ + + memset(dp, 0, reclen); + dp->d_fileno = qpath; + dp->d_off = 0; + dp->d_reclen = reclen; + dp->d_type = (mode & GEFS_DMDIR) ? DT_DIR : DT_REG; + dp->d_namlen = namlen; + memcpy(dp->d_name, name, namlen + 1); + + return reclen; +} + +int +gefs_v2d(int v) +{ + switch(v){ + case VBLK: return GEFS_DEVBLK; + case VCHR: return GEFS_DEVCHR; + case VSOCK: return GEFS_DEVSOCK; + case VFIFO: return GEFS_DEVFIFO; + default: panic("bad device"); + }; +} + +int +gefs_d2v(int d) +{ + switch(d){ + case GEFS_DEVBLK: return VBLK; + case GEFS_DEVCHR: return VCHR; + case GEFS_DEVSOCK: return VSOCK; + case GEFS_DEVFIFO: return VFIFO; + default: panic("bad device"); + }; +} + +int +gefs_d2m(int d) +{ + switch(d){ + case GEFS_DEVBLK: return S_IFBLK; + case GEFS_DEVCHR: return S_IFCHR; + case GEFS_DEVSOCK: return S_IFSOCK; + case GEFS_DEVFIFO: return S_IFIFO; + default: panic("bad device"); + }; +} diff --git a/sys/gefs/snap.c b/sys/gefs/snap.c new file mode 100644 index 00000000000..e373bf587cf --- /dev/null +++ b/sys/gefs/snap.c @@ -0,0 +1,685 @@ +#include +#include +#include +#include +#include +#include + +#include "u.h" +#include "avl.h" + +#include "dat.h" +#include "fns.h" + +static void +dlflush(Dlist *dl) +{ + char kvbuf[512]; + Msg m; + + if(!dl->dirty) + return; + traceb("dlflush", dl->ins->bp); + if(dl->tl.addr == dl->hd.addr) + dl->tl = dl->hd; +// gefs_enqueue(dl->ins); +// dropblk(dl->ins); + dl->dirty = 0; + /* special case: the snap dlist has gen -1, skip it */ + if(dl->gen != -1){ + m.op = Oinsert; + dlist2kv(dl, &m.p, kvbuf, sizeof(kvbuf)); + gefs_btupsert(&fs->snap, &m, 1); + } +} + +static void +dlcachedel(Dlist *dl, int hdel) +{ + uint h; + Dlist *d, **p; + + h = gefs_ihash(dl->gen) ^ gefs_ihash(dl->bgen); + if(hdel){ + p = &fs->dlcache[h % fs->dlcmax]; + for(d = *p; d != nil; d = d->chain){ + if(d->gen == dl->gen && d->bgen == dl->bgen) + break; + p = &d->chain; + } + if(d != nil) + *p = d->chain; + } + if(dl == fs->dlhead) + fs->dlhead = dl->cnext; + if(dl == fs->dltail) + fs->dltail = dl->cprev; + if(dl->cnext != nil) + dl->cnext->cprev = dl->cprev; + if(dl->cprev != nil) + dl->cprev->cnext = dl->cnext; + dl->cnext = nil; + dl->cprev = nil; +} + +static Dlist* +dlcacheget(vlong gen, vlong bgen) +{ + Dlist *dl; + uint h; + + h = gefs_ihash(gen) ^ gefs_ihash(bgen); + for(dl = fs->dlcache[h % fs->dlcmax]; dl != nil; dl = dl->chain) + if(dl->gen == gen && dl->bgen == bgen) + break; + if(dl != nil) + dlcachedel(dl, 0); + return dl; +} + + +static Dlist* +getdl(vlong gen, vlong bgen) +{ + char kbuf[Dlksz], kvbuf[Dlkvpsz]; + Dlist *dl, **p; + uint h; + Msg m; + Kvp kv; + Key k; + + if((dl = dlcacheget(gen, bgen)) != nil) + return dl; + dl = emalloc(sizeof(Dlist), 1); + fs->dlcount++; + kbuf[0] = Kdlist; + PACK64(kbuf+1, gen); + PACK64(kbuf+9, bgen); + k.k = kbuf; + k.nk = sizeof(kbuf); + + /* load up existing dlist */ + if(btlookup(&fs->snap, &k, &kv, kvbuf, sizeof(kvbuf)) == 0){ + kv2dlist(&kv, dl); + goto Found; + } + + /* create a new one if it didn't exist */ + dl->gen = gen; + dl->bgen = bgen; + dl->hd = Zb; + dl->tl = Zb; + dl->dirty = 0; +// dl->ins = nil; + + m.op = Oinsert; + dlist2kv(dl, &m.p, kvbuf, sizeof(kvbuf)); + gefs_btupsert(&fs->snap, &m, 1); +Found: +// poperror(); + h = gefs_ihash(gen) ^ gefs_ihash(bgen); + p = &fs->dlcache[h % fs->dlcmax]; + dl->chain = *p; + *p = dl; + return dl; +} + +void +putdl(Dlist *dl) +{ + Dlist *dt; + + if(dl->gen == -1) + return; + dlcachedel(dl, 0); + while(fs->dlcount >= fs->dlcmax && (dt = fs->dltail) != nil){ + fs->dlcount--; + dlcachedel(dt, 1); + dlflush(dt); + assert(!dt->dirty); + free(dt, M_TEMP, sizeof(Dlist)); + } + + dl->cprev = nil; + dl->cnext = fs->dlhead; + if(fs->dltail == nil) + fs->dltail = dl; + if(fs->dlhead != nil) + fs->dlhead->cprev = dl; + fs->dlhead = dl; +} + +void +gefs_freedl(Dlist *dl) +{ +// Arena *a; + Qent qe; + Bptr bp; + Blk *b; + char *p; + int done; + + bp = dl->hd; + done = 0; + while(bp.addr != -1 && !done){ + b = gefs_getblk(bp, 0); + for(p = b->data; p != b->data+b->logsz; p += 8){ + qe.op = Qfree; + qe.bp.addr = UNPACK64(p); + qe.bp.hash = -1; + qe.bp.gen = -1; + qe.b = nil; +// a = getarena(qe.bp.addr); +// qput(a->sync, qe); + traceb("dlclear", qe.bp); + } + done = (bp.addr == dl->tl.addr); + bp = b->logp; + qe.op = Qfree; + qe.bp = b->bp; + qe.b = nil; +// a = getarena(qe.bp.addr); +// qput(a->sync, qe); + traceb("dlfreeb", qe.bp); + gefs_dropblk(b); + } +} + + +static void +gefs_splicedl(Dlist *d, Dlist *m) +{ + Blk *b; + + /* + * If the dest dlist didn't exist, + * just move the merge dlist over + * and be done with it, otherwise + * chain onto the existing dlist + * tail. + */ + assert(d != m); + if(m->hd.addr == -1) + return; + if(d->hd.addr == -1){ + assert(!d->dirty); + d->hd = m->hd; + d->tl = m->tl; + }else{ +// if(m->dirty){ +// gefs_enqueue(m->ins); +// dropblk(m->ins); +// } + b = gefs_getblk(d->tl, 0); + gefs_setflag(b, Bdirty, 0); + b->logp = m->hd; + d->tl = m->tl; + assert(d->hd.addr != m->hd.addr); + /* dlflush writes the tail, so we shouldn't */ +// if(b->bp.addr != dl->hd.addr){ +// gefs_finalize(b); +// gefs_syncblk(b); +// } + gefs_dropblk(b); + } +} + +static void +gefs_mergedl(vlong merge, vlong gen, vlong bgen) +{ + char *buf_; + Dlist *d, *m; + Msg msg[2]; + + buf_ = malloc(2*Kvmax, M_TEMP, M_WAITOK|M_ZERO); + d = getdl(merge, bgen); + m = getdl(gen, bgen); + gefs_splicedl(d, m); + msg[0].op = Odelete; + dlist2kv(m, &msg[0].p, buf_, Kvmax); + msg[1].op = Oinsert; + dlist2kv(d, &msg[1].p, buf_+Kvmax, Kvmax); +// if(waserror()){ +// putdl(m); +// putdl(d); +// nexterror(); +// } + gefs_btupsert(&fs->snap, msg, 2); + free(buf_, M_TEMP, 2*Kvmax); +// poperror(); + putdl(m); + putdl(d); +} + +/* + * Reclaims a deadlist after the next superblock + * commit. + */ +static void +gefs_deferdl(vlong gen, vlong bgen) +{ + char *buf_; + Dlist *d; + Msg m; + + buf_ = malloc(Kvmax, M_TEMP, M_WAITOK|M_ZERO); + d = getdl(gen, bgen); + m.op = Odelete; + dlist2kv(d, &m.p, buf_, Kvmax); + gefs_btupsert(&fs->snap, &m, 1); + free(buf_, M_TEMP, Kvmax); + assert(!d->dirty); + dlcachedel(d, 1); + fs->dlcount--; + gefs_splicedl(&fs->snapdl, d); + free(d, M_TEMP, sizeof(Dlist)); +} + + +static void +reclaimblocks(vlong gen, vlong succ, vlong prev) +{ + char pfx[9]; + Dlist dl; + Scan s; + + pfx[0] = Kdlist; + PACK64(pfx+1, gen); + btnewscan(&s, pfx, sizeof(pfx)); + btenter(&fs->snap, &s); + while(1){ + if(!btnext(&s, &s.kv)) + break; + kv2dlist(&s.kv, &dl); +// if(waserror()){ +// btexit(&s); +// nexterror(); +// } + if(succ != -1 && dl.bgen <= prev) + gefs_mergedl(succ, dl.gen, dl.bgen); + else if(dl.bgen <= prev) + gefs_mergedl(prev, dl.gen, dl.bgen); + else + gefs_deferdl(dl.gen, dl.bgen); +// poperror(); + } + btexit(&s); + if(succ != -1){ + pfx[0] = Kdlist; + PACK64(pfx+1, succ); + btnewscan(&s, pfx, sizeof(pfx)); + btenter(&fs->snap, &s); + while(1){ + if(!btnext(&s, &s.kv)) + break; + kv2dlist(&s.kv, &dl); + if(dl.bgen <= prev) + continue; +// if(waserror()){ +// btexit(&s); +// nexterror(); +// } + gefs_deferdl(dl.gen, dl.bgen); +// poperror(); + } + btexit(&s); + } +} + +/* + * Removes a label from a snapshot, allowing + * it to be reclaimed if it is not a direct + * predecessor of more than one other snapshot. + * + * If it has one successor and no label, then + * it will be merged with that successor. + * Returns 1 if we need to sweep the tree + * + * Must be called with mntlock held for r + */ +int +gefs_delsnap(Tree *t, vlong succ, char *name) +{ + char *p, *e, *buf_; + int nm, del; + Mount *mnt; + Tree *r; + Msg m[4]; + + nm = 0; + del = 0; + buf_ = malloc(4*Kvmax, M_TEMP, M_WAITOK|M_ZERO); + p = buf_; + e = buf_ + 4*Kvmax; + if(name != nil){ + if(strcmp(name, "dump") == 0 + || strcmp(name, "empty") == 0 + || strcmp(name, "adm") == 0) + error(Esnapr); + + m[nm].op = Odelete; + m[nm].p.k.k = p; + p = packlbl(p, e - p, name); + m[nm].p.k.nk = p - m[nm].p.k.k; + m[nm].p.v.v = nil; + m[nm].p.v.nv = 0; + t->nlbl--; + nm++; + } + + if(t->nlbl == 0 && t->nref == 0){ + del = 1; + if(t->pred != -1){ + m[nm].op = Orelink; + p = retag2kv(t->pred, succ, 0, 0, &m[nm].p, p, e - p); + nm++; + } + if(t->succ != -1){ + m[nm].op = Oreprev; + p = retag2kv(t->succ, t->pred, 0, 0, &m[nm].p, p, e - p); + nm++; + } + if(t->pred == -1 && succ == -1){ + m[nm].op = Oincref; + p = retag2kv(t->base, -1, 0, -1, &m[nm].p, p, e - p); + nm++; + } + m[nm].op = Odelete; + m[nm].p.k.k = p; + p = packsnap(p, e - p, t->gen); + m[nm].p.k.nk = p - m[nm].p.k.k; + m[nm].p.v.v = nil; + m[nm].p.v.nv = 0; + nm++; + }else{ + assert(name != nil); + m[nm].op = Orelink; + p = retag2kv(t->gen, t->succ, -1, 0, &m[nm].p, p, e - p); + nm++; + } + assert(nm <= nelem(m)); + gefs_dlsync(); + gefs_btupsert(&fs->snap, m, nm); + free(buf_, M_TEMP, 4*Kvmax); + if(del){ + reclaimblocks(t->gen, succ, t->pred); + for(mnt = agetp(&fs->mounts); mnt != nil; mnt = mnt->next){ + r = agetp(&mnt->root); + if(r->gen == t->succ) + r->pred = t->pred; + if(r->gen == t->pred) + r->succ = succ; + } + } + return del && succ == -1; +} + +/* + * Attaches a label to a tree, incrementing + * its reference count. This labelled snapshot + * will show up in the dump. + */ +void +gefs_tagsnap(Tree *t, char *name, int flg) +{ + char *p, buf[3][Keymax]; // FIXME: size + Tree n, c; + Msg m[3]; + Kvp kv; + Key k; + int i; + + if(strcmp(name, "dump") == 0 + || strcmp(name, "empty") == 0 + || strcmp(name, "adm") == 0) + error(Ename); + + i = 0; + if(flg & Lmut){ + p = packsnap(buf[0], sizeof(buf[0]), t->gen); + k.k = buf[0]; + k.nk = p - buf[0]; + if(btlookup(&fs->snap, &k, &kv, buf[0], sizeof(buf[0])) != 0) + panic("snap missing kvp"); + unpacktree(&c, kv.v.v, kv.v.nv); + + atomic_swap_uint(&n.memref, 1); + n.dirty = 0; + n.nlbl = 1; + n.nref = 0; + n.ht = c.ht; + n.bp = c.bp; + n.succ = -1; + n.pred = -1; + n.base = t->gen; + n.gen = fs->nextgen++; + n.memgen = fs->nextgen++; + + t->nref++; + m[i].op = Oincref; + retag2kv(t->gen, -1, 0, 1, &m[i].p, buf[i], sizeof(buf[i])); + i++; + m[i].op = Oinsert; + lbl2kv(name, n.gen, flg, &m[i].p, buf[i], sizeof(buf[i])); + i++; + m[i].op = Oinsert; + tree2kv(&n, &m[i].p, buf[i], sizeof(buf[i])); + i++; + }else{ + t->nlbl++; + + m[i].op = Orelink; + retag2kv(t->gen, t->succ, 1, 0, &m[i].p, buf[i], sizeof(buf[i])); + i++; + + m[i].op = Oinsert; + lbl2kv(name, t->gen, flg, &m[i].p, buf[i], sizeof(buf[i])); + i++; + } + gefs_btupsert(&fs->snap, m, i); +} + +/* + * Updates a snapshot; keeps the generation the same if possible, + * otherwise moves to a new generation. A snapshot may only stay + * at the same generation as long as it is at the tip of a snapshot + * list; once it's observable by a derived snapshot it must be + * immutable. + */ +Tree* +gefs_updatesnap(Tree *o, char *lbl, int flg) +{ + char buf[4][Keymax]; // FIXME: size + Msg m[4]; + Tree *t; + int i; + + if(!o->dirty) + return o; + + tracex("updatesnap", o->bp, o->memgen, getcallerpc(&o)); + t = malloc(sizeof(Tree), M_TEMP, M_WAITOK|M_ZERO); + atomic_swap_uint(&t->memref, 1); + t->dirty = 0; + + t->nlbl = 1; + t->nref = 0; + t->ht = o->ht; + t->bp = o->bp; + t->succ = -1; + t->base = o->base; + t->gen = o->memgen; + t->memgen = fs->nextgen++; + + i = 0; + o->nlbl--; + if(o->nlbl == 0 && o->nref == 0){ + t->pred = o->pred; + if(t->pred != -1){ + m[i].op = Orelink; + retag2kv(t->pred, t->gen, 0, 0, &m[i].p, buf[i], sizeof(buf[i])); + i++; + } + }else{ + t->pred = o->gen; + m[i].op = Orelink; + retag2kv(t->pred, t->gen, -1, 0, &m[i].p, buf[i], sizeof(buf[i])); + i++; + } + + m[i].op = Oinsert; + tree2kv(t, &m[i].p, buf[i], sizeof(buf[i])); + i++; + m[i].op = Oinsert; + lbl2kv(lbl, t->gen, flg, &m[i].p, buf[i], sizeof(buf[i])); + i++; + gefs_btupsert(&fs->snap, m, i); + + /* only update the dirty status after we sync */ + o->dirty = 0; + + /* this was the last ref to the snap */ + if(o->nlbl == 0 && o->nref == 0) + gefs_delsnap(o, t->gen, nil); + gefs_limbo(DFclose, &o->limbo); +// poperror(); + return t; +} + + +Tree* +opentree(vlong gen) +{ + char *p, buf[Kvmax]; // FIXME: size + Mount *mnt; + Tree *t; + Kvp kv; + Key k; + + for(mnt = agetp(&fs->mounts); mnt != nil; mnt = mnt->next){ + t = agetp(&mnt->root); + if(t->gen == gen){ + atomic_add_int_nv(&t->memref, 1); + return t; + } + } + + t = malloc(sizeof(Tree), M_TEMP, M_WAITOK|M_ZERO); +// if(waserror()){ +// free(t); +// nexterror(); +// } + p = packsnap(buf, sizeof(buf), gen); + k.k = buf; + k.nk = p - buf; + if(btlookup(&fs->snap, &k, &kv, buf, sizeof(buf)) != 0) + panic("missing tree"); + unpacktree(t, kv.v.v, kv.v.nv); + atomic_swap_uint(&t->memref, 1); + t->memgen = fs->nextgen++; +// poperror(); + return t; +} + +/* + * open snapshot by label, returning a tree. + */ +Tree* +gefs_opensnap(char *label, int *flg) +{ + char *p, buf[Kvmax]; // FIXME: size + vlong gen; + Kvp kv; + Key k; + + /* Klabel{"name"} => Ksnap{id} */ + if((p = packlbl(buf, sizeof(buf), label)) == nil) + return nil; + k.k = buf; + k.nk = p - buf; + if(btlookup(&fs->snap, &k, &kv, buf, sizeof(buf)) != 0) + return nil; + assert(kv.v.nv == 1+8+4); + gen = UNPACK64(kv.v.v + 1); + if(flg != nil) + *flg = UNPACK32(kv.v.v + 1+8); + + return opentree(gen); +} + +void +gefs_closesnap(Tree *t) +{ + if(t == nil) + return; + if(atomic_dec_int_nv(&t->memref) != 0) + return; + gefs_limbo(DFtree, &t->limbo); +} + +void +gefs_dlsync(void) +{ + Dlist *dl, *n; + + tracem("dlsync"); + dlflush(&fs->snapdl); + for(dl = fs->dlhead; dl != nil; dl = n){ + n = dl->cnext; + dlflush(dl); + } +} + +/* + * Marks a block as killed by the tree + * t, which means that it will be free + * for use after t is reclaimed. + * + * t must be an active snapshot with + * no successors. + */ +void +gefs_killblk(Tree *t, Bptr bp) +{ + Blk *b; + Dlist *dl; + char *p; + + /* + * When we have a forked snap, blocks allocated before the fork + * are the responsibility of the other chain; in this chain, we + * leak it and let the last reference in the other chain clean up + */ + if(t == &fs->snap) + dl = &fs->snapdl; + else if(bp.gen > t->base) + dl = getdl(t->memgen, bp.gen); + else + return; +// if(waserror()){ +// putdl(dl); +// nexterror(); +// } + if(!dl->dirty || Logspc - dl->logsz < Logslop){ + b = gefs_newblk(&fs->snap, Tdlist); + if(dl->tl.addr == -1) + dl->tl = b->bp; + b->logp = dl->hd; + dl->hd = b->bp; + dl->dirty = 1; + dl->logsz = 0; +// gefs_cacheins(b); + }else{ + b = gefs_getblk(dl->hd, 0); + } + + p = b->data + dl->logsz; + dl->logsz += 8; + gefs_setflag(b, Bdirty, 0); + PACK64(p, bp.addr); +// poperror(); + /* hack: we want to cache it, so finalize and toss it in the queue */ + gefs_finalize(b); + gefs_dropblk(b); + putdl(dl); +} diff --git a/sys/gefs/sweep.c b/sys/gefs/sweep.c new file mode 100644 index 00000000000..5accd301bfb --- /dev/null +++ b/sys/gefs/sweep.c @@ -0,0 +1,663 @@ +#include +#include +#include +#include +#include +#include +#include +#include /* XXX */ /* defines v_rdev */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "u.h" +#include "avl.h" + +#include "dat.h" +#include "fns.h" + +int +gefs_syncfs(void) +{ + Mount *mnt; + Blk **h0, **h1, *sb0, *sb1; + Arena *a; + Dlist *dl; + Tree *r; + int i; + + rw_enter(&fs->synclk, RW_WRITE); +// if(waserror()){ +// fprint(2, "failed to sync: %s\n", errmsg()); +// atomic_add_int_nv(&fs->rdonly, 1); +// rw_exit(&fs->synclk); +// nexterror(); +// } + + /* + * Wait for data that we're syncing to hit disk + */ + tracem("flush1"); +// wrbarrier(); + /* + * pass 0: Update all open snapshots, and + * pack the blocks we want to sync. Snap + * while holding the write lock, and then + * wait until all the blocks they point at + * have hit disk; once they're on disk, we + * can take a consistent snapshot. + */ + dl = emalloc(sizeof(Dlist), 1); + rw_enter(&fs->mutlk, RW_WRITE); +// epochstart(id); +// if(waserror()){ +// free(dl); +// epochend(id); +// atomic_add_int_nv(&fs->rdonly, 1); +// rw_exit(&fs->mutlk); +// nexterror(); +// } + tracem("packb"); + + for(mnt = agetp(&fs->mounts); mnt != nil; mnt = mnt->next){ + r = agetp(&mnt->root); + r = gefs_updatesnap(r, mnt->name, mnt->flag); + atomic_swap_ptr(&mnt->root, r); + } + + /* + * Now that we've updated the snaps, we can sync the + * dlist; the snap tree will not change from here. + */ + gefs_dlsync(); + *dl = fs->snapdl; + fs->snapdl.hd = Zb; + fs->snapdl.tl = Zb; + fs->snapdl.logsz = 0; + fs->snapdl.dirty = 0; + traceb("syncdl.dl", dl->hd); + traceb("syncdl.rb", fs->snap.bp); + h0 = malloc(fs->narena * sizeof(Blk*), M_TEMP, M_WAITOK); + h1 = malloc(fs->narena * sizeof(Blk*), M_TEMP, M_WAITOK); + for(i = 0; i < fs->narena; i++){ + a = &fs->arenas[i]; + rw_enter(&a->lk, RW_WRITE); + /* + * because the log uses preallocated + * blocks, we need to write the log + * block out synchronously, or it may + * get reused. + */ + gefs_logbarrier(a, agetv(&fs->qgen)); + gefs_flushlog(a); + + h0[i] = gefs_getblk(a->h0, 0); + h1[i] = gefs_getblk(a->h1, 0); + packarena(h0[i]->data, Blksz, a); + packarena(h1[i]->data, Blksz, a); + gefs_finalize(h0[i]); + gefs_finalize(h1[i]); + a->h0 = h0[i]->bp; + a->h1 = h1[i]->bp; + fs->arenabp[i] = h0[i]->bp; + rw_exit(&a->lk); + } + assert(fs->snapdl.hd.addr == -1); + traceb("packsb.rb", fs->snap.bp); + sb0 = gefs_getblk(fs->sb0, GBnochk); + sb1 = gefs_getblk(fs->sb1, GBnochk); + packsb(sb0->data, Blksz, fs); + packsb(sb1->data, Blksz, fs); + gefs_finalize(sb0); + gefs_finalize(sb1); + fs->snap.dirty = 0; +// epochend(id); + rw_exit(&fs->mutlk); +// poperror(); + + /* + * pass 1: sync block headers; if we crash here, + * the block footers are consistent, and we can + * use them. + */ + tracem("arenas0"); + for(i = 0; i < fs->narena; i++) + gefs_enqueue(h0[i]); +// wrbarrier(); + + /* + * pass 2: sync superblock; we have a consistent + * set of block headers, so if we crash, we can + * use the loaded block headers; the footers will + * get synced after so that we can use them next + * time around. + */ + tracem("supers"); + gefs_enqueue(sb0); + gefs_enqueue(sb1); +// wrbarrier(); + + /* + * pass 3: sync block footers; if we crash here, + * the block headers are consistent, and we can + * use them. + */ + tracem("arenas1"); + for(i = 0; i < fs->narena; i++) + gefs_enqueue(h1[i]); + + + gefs_dropblk(sb0); + gefs_dropblk(sb1); + for(i = 0; i < fs->narena; i++){ + gefs_dropblk(h0[i]); + gefs_dropblk(h1[i]); + } + free(h0, M_TEMP, fs->narena * sizeof(Blk*)); + free(h1, M_TEMP, fs->narena * sizeof(Blk*)); + + /* + * Pass 4: clean up the old snap tree's deadlist. + * we need to wait for all the new data to hit disk + * before we can free anything, otherwise it gets + * clobbered. + */ + tracem("snapdl"); +// wrwait(); + gefs_limbo(DFdlist, &dl->limbo); + rw_exit(&fs->synclk); + tracem("synced"); +// poperror(); + return 0; +} + +void +gefs_freeamsg(Amsg *a) +{ + if(a == nil) + return; + switch(a->op){ + case AOrclose: + case AOclear: + gefs_clunkdent(a->mnt, a->dent); + gefs_clunkmount(a->mnt); + break; + } + free(a, M_TEMP, sizeof(Amsg)); +} + +/* + * Adds or removes a labelled snapshot; if the tree + * needs to be cleaned up outside of the epoch, it + * is returned, otherwise snapfs returns nil. + */ +Tree* +gefs_snapfs(Amsg *a) +{ + Tree *t, *s, *r; + Mount *mnt; + + t = nil; + r = nil; + for(mnt = agetp(&fs->mounts); mnt != nil; mnt = mnt->next){ + if(strcmp(a->old, mnt->name) != 0) + continue; + t = agetp(&mnt->root); + if(mnt->flag & Lmut){ + t = gefs_updatesnap(t, mnt->name, mnt->flag); + atomic_swap_ptr(&mnt->root, t); + } + atomic_add_int_nv(&t->memref, 1); + break; + } + if(t == nil && (t = gefs_opensnap(a->old, nil)) == nil) + error(Eexist); +// if(waserror()){ +// closesnap(t); +// nexterror(); +// } + if(a->delete){ + if(mnt != nil) + return NULL; + if(gefs_delsnap(t, t->succ, a->old)) + r = t; + else + gefs_closesnap(t); + }else{ + if((s = gefs_opensnap(a->new, nil)) != nil){ + gefs_closesnap(s); + return NULL; + } + gefs_tagsnap(t, a->new, a->flag); + gefs_closesnap(t); + } +// poperror(); + return r; +} + +static void +gefs_freetree(Bptr rb, vlong pred) +{ + Bptr bp; + Blk *b; + Kvp kv; + int i; + + b = gefs_getblk(rb, 0); + if(b->type == Tpivot){ + for(i = 0; i < b->nval; i++){ + getval(b, i, &kv); + bp = unpackbp(kv.v.v, kv.v.nv); + gefs_freetree(bp, pred); /* leak b on error() */ + gefs_epochclean(); + } + } + if(rb.gen > pred) + gefs_freeblk(nil, b); + gefs_dropblk(b); +} + +/* + * Here, we clean epochs frequently, but we run outside of + * an epoch; this is because the caller of this function + * has already waited for an epoch to tick over, there's + * nobody that can be accessing the tree other than us, + * and we just need to keep the limbo list short. + * + * Because this is the last reference to the tree, we don't + * need to hold the mutlk, other than when we free or kill + * blocks via gefs_epochclean. + */ +Tree* +gefs_sweeptree(Tree *t) +{ + char pfx[1]; + vlong gen; + Scan s; + Bptr bp; + + pfx[0] = Kdat; + btnewscan(&s, pfx, 1); + gen = (t->pred != -1) ? t->pred : t->base; + btenter(t, &s); + while(1){ + if(!btnext(&s, &s.kv)) + break; + bp = unpackbp(s.kv.v.v, s.kv.v.nv); + if(bp.gen > gen) + gefs_freebp(nil, bp); + gefs_epochclean(); + } + btexit(&s); + gefs_freetree(t->bp, gen); + + if(gen != -1 && (t = opentree(gen)) != nil){ + if(t->nlbl == 0 && t->nref == 0) + return t; + gefs_closesnap(t); + } + return nil; +} + +void +gefs_setconf(int op, char *snap, char *key, char *val) +{ +#ifdef NOTYET + char kbuf[128], xbuf[Kvmax]; + Mount *mnt; + Tree *t; + Msg m; + Kvp x; + + mnt = nil; + t = &fs->snap; + if(waserror()){ + fprint(2, "error setting config: %s\n", errmsg()); + return; + } + if(snap[0] != 0){ + mnt = getmount(snap); + t = agetp(&mnt->root); + if((mnt->flag & Lmut) == 0) + error(Eperm); + } + kbuf[0] = Kconf; + strecpy(kbuf+1, kbuf+sizeof(kbuf), key); + m.op = op; + m.k = kbuf; + m.nk = strlen(key)+1; + m.v = val; + m.nv = strlen(val); + rw_enter(&fs->mutlk, RW_WRITE); + if(!waserror()){ + if(op == Oinsert || btlookup(t, &m, &x, xbuf, sizeof(xbuf))) + btupsert(t, &m, 1); + else + fprint(2, "%s: no key %s\n", (op == Oinsert) ? "set" : "clr", key); + poperror(); + }else + fprint(2, "error setting config: %s\n", errmsg()); + rw_exit(&fs->mutlk); + sync(); + if(mnt != nil) + clunkmount(mnt); + poperror(); +#endif +} + +void +gefs_sweep(void *p) +{ + char *buf; + Msg mb[Kvmax/Offksz]; + Bptr bp, nb, *oldhd; + Tree *t, *n; + int i, nm, ok; + vlong off; + Arena *a; + Amsg *am; + Blk *b; + + buf = malloc(Kvmax, M_TEMP, M_WAITOK|M_ZERO); + oldhd = malloc(fs->narena*sizeof(Bptr), M_TEMP, M_WAITOK|M_ZERO); + while(1){ + am = gefs_chrecv(fs->admchan); +// if(waserror()){ +// if(am->m != nil) +// rerror(am->m, errmsg()); +// continue; +// } + switch(am->op){ + case AOsetcfg: + gefs_setconf(Oinsert, am->snap, am->key, am->val); + break; + case AOclrcfg: + gefs_setconf(Odelete, am->snap, am->key, ""); + break; + case AOhalt: + if(!atomic_load_int(&fs->rdonly)){ + atomic_add_int_nv(&fs->rdonly, 1); + /* cycle through all epochs to clear them. */ + for(i = 0; i < 3; i++) + while(!gefs_epochclean()) + /* nothing */; +// if(waserror()){ +// fprint(2, "halt failed: %s\n", errmsg()); +// break; +// } + gefs_syncfs(); + } + printf("gefs: ending\n"); + rw_enter(&fs->quitlk, RW_WRITE); + fs->quitrz = 0; + wakeup(&fs->quitrz); + rw_exit(&fs->quitlk); + kthread_exit(0); + break; + case AOsync: + tracem("syncreq"); +// if(waserror()){ +// fprint(2, "sync error: %s\n", errmsg()); +// atomic_add_int_nv(&fs->rdonly, 1); +// nexterror(); +// } + if(!fs->snap.dirty || atomic_load_int(&fs->rdonly)) + goto Syncout; + for(i = 0; i < fs->narena; i++){ + a = &fs->arenas[i]; + oldhd[i].addr = -1; + oldhd[i].hash = -1; + oldhd[i].gen = -1; + rw_enter(&a->lk, RW_WRITE); + /* + * arbitrary heuristic -- try compressing + * when the log doubles in size. + */ + if(a->nlog >= 2*a->lastlogsz){ + oldhd[i] = a->loghd; +// epochstart(id); +// if(waserror()){ +// epochend(id); +// rw_exit(a); +// nexterror(); +// } + gefs_compresslog(a); +// gefs_epochend(id); +// poperror(); + } + rw_exit(&a->lk); + gefs_epochclean(); + } + + gefs_syncfs(); /* oldhd blocks leaked on error() */ + + for(i = 0; i < fs->narena; i++){ + for(bp = oldhd[i]; bp.addr != -1; bp = nb){ + rw_enter(&fs->mutlk, RW_WRITE); +// if(waserror()){ +// rw_exit(&fs->mutlk); +// nexterror(); +// } + b = gefs_getblk(bp, 0); + nb = b->logp; + gefs_freeblk(nil, b); + gefs_dropblk(b); + rw_exit(&fs->mutlk); +// poperror(); + gefs_epochclean(); + } + } +Syncout: + rw_enter(&fs->synclk, RW_WRITE); + fs->syncrz = 0; + wakeup(&fs->syncrz); + rw_exit(&fs->synclk); + +// poperror(); + break; + + case AOsnap: + tracem("snapreq"); + if(atomic_load_int(&fs->rdonly)){ + printf("snap on read only fs"); + goto Next; + } + rw_enter(&fs->mutlk, RW_WRITE); +// if(waserror()){ +// rw_exit(&fs->mutlk); +// nexterror(); +// } + t = gefs_snapfs(am); + rw_exit(&fs->mutlk); +// poperror(); + + while(t != nil){ + gefs_syncfs(); /* t leaked on error() */ + n = gefs_sweeptree(t); + gefs_closesnap(t); + if(n == nil) + break; + t = n; + ok = 0; + if(n->nref == 0 && n->nlbl == 0){ + rw_enter(&fs->mutlk, RW_WRITE); + ok = gefs_delsnap(n, n->succ, nil); + rw_exit(&fs->mutlk); + } + if(!ok){ + gefs_closesnap(n); + break; + } + } + break; + + case AOrclose: + if(atomic_load_int(&fs->rdonly)){ + printf("rclose on read only fs"); + goto Next; + } + nm = 0; + mb[nm].op = Odelete; + mb[nm].p.k.k = am->dent->k.k; + mb[nm].p.k.nk = am->dent->k.nk; + mb[nm].p.v.nv = 0; + nm++; + if(am->dent->d.qid.type & GEFS_QTDIR){ + packsuper(buf, Kvmax, am->qpath); + mb[nm].op = Oclobber; + mb[nm].p.k.k = buf; + mb[nm].p.k.nk = Upksz; + mb[nm].p.v.nv = 0; + nm++; + } + rw_enter(&fs->mutlk, RW_WRITE); +// if(waserror()){ +// rw_exit(&fs->mutlk); +// fprint(2, "%s", errmsg()); +// goto Next; +// } + gefs_upsert(am->mnt, mb, nm); + rw_exit(&fs->mutlk); +// poperror(); + /* fallthrough */ + case AOclear: + if(atomic_load_int(&fs->rdonly)){ + printf("clear on read only fs\n"); + goto Next; + } + tracem("bgclear"); +// if(waserror()){ +// fprint(2, "clear file %llx: %s\n", am->qpath, errmsg()); +// atomic_add_int_nv(&fs->rdonly, 1); +// break; +// } + fs->snap.dirty = 1; + nm = 0; + for(off = am->off; off < am->end; off += Blksz){ + mb[nm].op = Oclearb; + mb[nm].p.k.k = buf + Offksz * nm; + mb[nm].p.k.nk = Offksz; + mb[nm].p.k.k[0] = Kdat; + PACK64(mb[nm].p.k.k+1, am->qpath); + PACK64(mb[nm].p.k.k+9, off); + mb[nm].p.v.v = nil; + mb[nm].p.v.nv = 0; + if(++nm >= nelem(mb) || off + Blksz >= am->end){ + rw_enter(&fs->mutlk, RW_WRITE); +// if(waserror()){ +// rw_exit(&fs->mutlk); +// nexterror(); +// } + gefs_upsert(am->mnt, mb, nm); + rw_exit(&fs->mutlk); +// poperror(); + gefs_epochclean(); + nm = 0; + } + } + if(am->dent != nil){ + rw_enter(&fs->mutlk, RW_WRITE); + am->dent->trunc--; + wakeup(&am->dent->truncrz); + rw_exit(&fs->mutlk); + } +// poperror(); + break; + } +Next: +// poperror(); +// if(am->m != nil){ +// switch(am->m->type){ +// case Twstat: +// r.type = Rwstat; +// break; +// case Twrite: +// r.type = Rwrite; +// r.count = am->m->count; +// break; +// default: +// abort(); +// } +// respond(am->m, &r); +// } +// assert(estacksz() == 0); + gefs_freeamsg(am); + } +} + +void +gefs_snapmsg(char *old, char *new) +{ + Amsg *am; + + am = malloc(sizeof(Amsg), M_TEMP, M_WAITOK|M_ZERO); + am->op = AOsnap; + strlcpy(am->old, old, sizeof(am->old)); + if(new == nil) + am->delete = 1; + else + strlcpy(am->new, new, sizeof(am->new)); + gefs_chsend(fs->admchan, am); +} + +static void +gefs_timedsnap(char *name, Cron *c, struct timespec *now) +{ + char *p, *e; + + if(c->div == 0 || c->cnt == 0) + return; + if(now->tv_sec/c->div == c->last/c->div) + return; + + if(c->cnt > 0 && c->lbl[c->i][0] != 0) + gefs_snapmsg(c->lbl[c->i], nil); + p = c->lbl[c->i]; + e = p + sizeof(c->lbl[c->i]); + c->i = (c->cnt > 0) ? (c->i+1) % c->cnt : 0; + snprintf(c->lbl[c->i], sizeof( c->lbl[c->i]), + "%s@%s.%lld", + name, c->tag, + now->tv_sec); + gefs_snapmsg(name, p); + c->last = now->tv_sec; +} + +void +gefs_runtasks(void *p) +{ + struct timespec now; + Mount *mnt; + Amsg *am; + int i; + + while(1){ + tsleep_nsec(&now, IPL_NONE, "gefs-tasks", SEC_TO_NSEC(5)); + if(atomic_load_int(&fs->rdonly)) + continue; +// if(waserror()){ +// fprint(2, "task error: %s\n", errmsg()); +// continue; +// } + nanotime(&now); + am = malloc(sizeof(Amsg), M_TEMP, M_WAITOK|M_ZERO); + am->op = AOsync; + gefs_chsend(fs->admchan, am); + + for(mnt = agetp(&fs->mounts); mnt != nil; mnt = mnt->next){ + if(!(mnt->flag & Lmut)) + continue; + for(i = 0; i < nelem(mnt->cron); i++) + gefs_timedsnap(mnt->name, &mnt->cron[i], &now); + } +// poperror(); + } +} diff --git a/sys/gefs/tree.c b/sys/gefs/tree.c new file mode 100644 index 00000000000..e856093ae7a --- /dev/null +++ b/sys/gefs/tree.c @@ -0,0 +1,1612 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "u.h" +#include "avl.h" + +#include "dat.h" +#include "fns.h" + +typedef struct Path Path; + +struct Path { + /* Flowing down for flush */ + Msg *ins; /* inserted values, bounded by lo..hi */ + Blk *b; /* to shadow */ + int idx; /* insert at */ + int lo; /* key range */ + int hi; /* key range */ + int sz; /* size of range */ + + /* Flowing up from flush */ + int op; /* change done along path */ + Blk *nl; /* new left */ + Blk *nr; /* new right, if we split or rotated */ + int midx; /* modification index */ + int npull; /* number of messages successfully pulled */ + int pullsz; /* size of pulled messages */ +}; + +#define efreeblk(t, b) do { \ + if(b != nil) \ + freeblk(t, b); \ + } while(0) + +static void +gefs_stablesort(Msg *m, int nm) +{ + int i, j; + Msg t; + + for(i = 1; i < nm; i++){ + for(j = i; j > 0; j--){ + if(gefs_keycmp(&m[j-1].p.k, &m[j].p.k) <= 0) + break; + t = m[j-1]; + m[j-1] = m[j]; + m[j] = t; + } + } +} + +void +gefs_cpkey(Key *dst, Key *src, char *buf, int nbuf) +{ + assert(src->nk <= nbuf); + memmove(buf, src->k, src->nk); + dst->k = buf; + dst->nk = src->nk; +} + +void +gefs_cpkvp(Kvp *dst, Kvp *src, char *buf, int nbuf) +{ + assert(src->k.nk+src->v.nv <= nbuf); + memmove(buf, src->k.k, src->k.nk); + memmove(buf+ src->k.nk, src->v.v, src->v.nv); + dst->k.k = buf; + dst->k.nk = src->k.nk; + dst->v.v = buf+src->k.nk; + dst->v.nv = src->v.nv; +} + +int +gefs_keycmp(Key *a, Key *b) +{ + int c, n; + + n = (a->nk < b->nk) ? a->nk : b->nk; + if((c = memcmp(a->k, b->k, n)) != 0) + return c < 0 ? -1 : 1; + if(a->nk < b->nk) + return -1; + else if(a->nk > b->nk) + return 1; + else + return 0; +} + +static int +msgsz(Msg *m) +{ + /* disp + op + klen + key + vlen + v */ + return 2+1+2+m->p.k.nk +2+ m->p.v.nv; +} + +static int +valsz(Kvp *kv) +{ + return 2 + 2+kv->k.nk + 2+kv->v.nv; +} + +void +getval(Blk *b, int i, Kvp *kv) +{ + char *p; + int o; + + bassert(b, i >= 0 && i < b->nval); + p = b->data + 2*i; + o = UNPACK16(p); p = b->data + o; + kv->k.nk = UNPACK16(p); p += 2; + kv->k.k = p; p += kv->k.nk; + kv->v.nv = UNPACK16(p); p += 2; + kv->v.v = p; +} + +Bptr +getptr(Kvp *kv, int *fill) +{ + assert(kv->v.nv == Ptrsz || kv->v.nv == Ptrsz+2); + *fill = UNPACK16(kv->v.v + Ptrsz); + return unpackbp(kv->v.v, kv->v.nv); +} + +/* Exported for reaming */ +void +setval(Blk *b, Kvp *kv) +{ + int off, spc; + char *p; + + spc = (b->type == Tleaf) ? Leafspc : Pivspc; + b->valsz += 2 + kv->k.nk + 2 + kv->v.nv; + off = spc - b->valsz; + + if(kv->k.k[0] == Kent) assert(kv->v.nv != 0); + bassert(b, 2*(b->nval+1) + b->valsz <= spc); + bassert(b, 2*(b->nval+1) <= off); + + p = b->data + 2*b->nval; + PACK16(p, off); + + p = b->data + off; + PACK16(p, kv->k.nk); p += 2; + memmove(p, kv->k.k, kv->k.nk); p += kv->k.nk; + PACK16(p, kv->v.nv); p += 2; + memmove(p, kv->v.v, kv->v.nv); + + b->nval++; +} + +static void +setptr(Blk *b, Key *k, Bptr bp, int fill) +{ + char *p, buf[Ptrsz+2]; + Kvp kv; + + kv.k.k = k->k; + kv.k.nk = k->nk; + kv.v.v = buf; + kv.v.nv = sizeof(buf); + p = packbp(buf, sizeof(buf), &bp); + PACK16(p, fill); + setval(b, &kv); +} + +static void +setmsg(Blk *b, Msg *m) +{ + char *p; + int o; + + bassert(b, b->type == Tpivot); + b->bufsz += msgsz(m)-2; + assert(2*(b->nbuf+1) + b->bufsz <= Bufspc); + + p = b->data + Pivspc + 2*b->nbuf; + o = Bufspc - b->bufsz; + PACK16(p, o); + + p = b->data + Pivspc + o; + *p = m->op; p += 1; + PACK16(p, m->p.k.nk); p += 2; + memmove(p, m->p.k.k, m->p.k.nk); p += m->p.k.nk; + PACK16(p, m->p.v.nv); p += 2; + memmove(p, m->p.v.v, m->p.v.nv); + + b->nbuf++; +} + +void +getmsg(Blk *b, int i, Msg *m) +{ + char *p; + int o; + + bassert(b, b->type == Tpivot); + bassert(b, i >= 0 && i < b->nbuf); + p = b->data + Pivspc + 2*i; + o = UNPACK16(p); + p = b->data + Pivspc + o; + m->op = *p; p += 1; + m->p.k.nk = UNPACK16(p); p += 2; + m->p.k.k = p; p += m->p.k.nk; + m->p.v.nv = UNPACK16(p); p += 2; + m->p.v.v = p; +} + +static int +bufsearch(Blk *b, Key *k, Msg *m, int *same) +{ + int lo, hi, ri, mid, r; + Msg cmp; + + ri = -1; + lo = 0; + hi = b->nbuf-1; + while(lo <= hi){ + mid = (hi + lo) / 2; + getmsg(b, mid, &cmp); + r = gefs_keycmp(k, &cmp.p.k); + switch(r){ + case -1: + hi = mid-1; + break; + case 0: + ri = mid; + hi = mid-1; + break; + case 1: + lo = mid+1; + break; + } + } + /* + * we can have duplicate messages, and we + * want to point to the first of them: + * scan backwards. + */ + *same = 0; + if(ri == -1) + ri = lo-1; + else + *same = 1; + if(m != nil && ri >= 0) + getmsg(b, ri, m); + return ri; +} + +static int +blksearch(Blk *b, Key *k, Kvp *rp, int *same) +{ + int lo, hi, ri, mid, r; + Kvp cmp; + + ri = -1; + lo = 0; + hi = b->nval-1; + while(lo <= hi){ + mid = (hi + lo) / 2; + getval(b, mid, &cmp); + r = gefs_keycmp(k, &cmp.k); + switch(r){ + case -1: + hi = mid-1; + break; + case 0: + ri = mid; + hi = mid-1; + break; + case 1: + lo = mid+1; + break; + } + } + *same = 0; + if(ri == -1) + ri = lo-1; + else + *same = 1; + if(ri >= 0) + getval(b, ri, rp); + return ri; +} + +static int +buffill(Blk *b) +{ + bassert(b, b->type == Tpivot); + return 2*b->nbuf + b->bufsz; +} + +static int +filledbuf(Blk *b, int nmsg, int needed) +{ + bassert(b, b->type == Tpivot); + return 2*(b->nbuf+nmsg) + b->bufsz + needed > Bufspc; +} + +static int +filledleaf(Blk *b, int needed) +{ + bassert(b, b->type == Tleaf); + return 2*(b->nval+1) + b->valsz + needed > Leafspc; +} + +static int +filledpiv(Blk *b, int reserve) +{ + /* + * We need to guarantee there's room for one message + * at all times, so that splits along the whole path + * have somewhere to go as they propagate up. For + * each key-ptr pair we have 8 bytes of overhead, + * so remember to account for that: 2 bytes of fill, + * 2 bytes of offset table, and 2 bytes of length + * for both key and value. + */ + bassert(b, b->type == Tpivot); + return 2*(b->nval+1) + b->valsz + reserve*(8+Keymax+Ptrsz) > Pivspc; +} + +static void +copyup(Blk *n, Path *pp, int *nbytes) +{ + Kvp kv; + Msg m; + + /* + * It's possible for the previous node to have + * been fully cleared out by a large number of + * delete messages, so we need to check if + * there's anything in it to copy up. + */ + if(pp->nl != nil){ + getval(pp->nl, 0, &kv); + if(pp->nl->nbuf > 0){ + getmsg(pp->nl, 0, &m); + if(gefs_keycmp(&kv.k, &m.p.k) > 0) + kv.k = m.p.k; + } + setptr(n, &kv.k, pp->nl->bp, gefs_blkfill(pp->nl)); + if(nbytes != nil) + *nbytes += valsz(&kv); + } + if(pp->nr != nil){ + getval(pp->nr, 0, &kv); + if(pp->nr->nbuf > 0){ + getmsg(pp->nr, 0, &m); + if(gefs_keycmp(&kv.k, &m.p.k) > 0) + kv.k = m.p.k; + } + setptr(n, &kv.k, pp->nr->bp, gefs_blkfill(pp->nr)); + if(nbytes != nil) + *nbytes += valsz(&kv); + } +} + +static void +statupdate(Kvp *kv, Msg *m) +{ + char *p, *e; + int op; + Xdir d; + + assert(m->p.v.nv >= 1); + p = m->p.v.v; + op = *p++; + kv2dir(kv, &d); + /* bump version */ + d.qid.vers++; + if(op & Owsize){ + d.length = UNPACK64(p); + p += 8; + } + if(op & Owmode){ + d.mode = UNPACK32(p); + d.qid.type = d.mode>>24; + p += 4; + } + if(op & Owmtime){ + d.mtime = UNPACK64(p); + p += 8; + } + if(op & Owatime){ + d.atime = UNPACK64(p); + p += 8; + } + if(op & Owuid){ + d.uid = UNPACK32(p); + p += 4; + } + if(op & Owgid){ + d.gid = UNPACK32(p); + p += 4; + } + if(op & Owmuid){ + d.muid = UNPACK32(p); + p += 4; + } + if(p != m->p.v.v + m->p.v.nv){ + fatal("malformed stat"); + } + e = packdval(kv->v.v, kv->v.nv, &d); + if(e == nil || e - kv->v.v != kv->v.nv) + fatal("repacking dir failed\n"); +} + +static int +apply(Kvp *kv, Msg *m, char *buf, int nbuf) +{ + char *p; + Tree t; + + switch(m->op){ + case Odelete: + assert(gefs_keycmp(&kv->k, &m->p.k) == 0); + return 0; + case Oclearb: + case Oclobber: + return 0; + case Oinsert: + gefs_cpkvp(kv, &m->p, buf, nbuf); + return 1; + case Owstat: + assert(gefs_keycmp(&kv->k, &m->p.k) == 0); + statupdate(kv, m); + return 1; + case Orelink: + case Oreprev: + case Oincref: + unpacktree(&t, kv->v.v, kv->v.nv); + p = m->p.v.v; + if(m->op == Orelink) + t.succ = UNPACK64(p); + else if(m->op == Oreprev) + t.pred = UNPACK64(p); + p += 8; + t.nlbl += *p; p++; + t.nref += *p; p++; + assert(t.nlbl >= 0 && t.nref >= 0); + assert(p == m->p.v.v + m->p.v.nv); + packtree(kv->v.v, kv->v.nv, &t); + return 1; + default: + fatal("invalid op %d\n", m->op); + } +} + +static void +setb(Blk **dst, Tree *t, Blk *b) +{ + if(*dst != nil){ + gefs_freeblk(t, *dst); + gefs_dropblk(*dst); + } + if(b->nval == 0){ + gefs_freeblk(t, b); + gefs_dropblk(b); + *dst = nil; + }else{ + gefs_enqueue(b); + *dst = b; + } +} + + +static int +pullmsg(Path *p, int i, Kvp *v, Msg *m, int *full, int spc) +{ + if(i < 0 || i >= p->hi || *full) + return -1; + + if(p->ins != nil) + *m = p->ins[i]; + else + getmsg(p->b, i, m); + if(msgsz(m) <= spc) + return (v == nil) ? 0 : gefs_keycmp(&v->k, &m->p.k); + *full = 1; + return -1; +} + +/* + * Creates a new block with the contents of the old + * block. When copying the contents, it repacks them + * to minimize the space uses, and applies the changes + * pending from the downpath blocks. + * + * When pidx != -1, + */ +static void +updateleaf(Tree *t, Path *up, Path *p) +{ + char buf[Msgmax]; + int i, j, c, ok, full, spc; + Blk *b, *n; + Bptr bp; + Msg m; + Kvp v; + + i = 0; + j = up->lo; + b = p->b; + /* + * spc is the amount of room we have + * to copy data down from the parent; it's + * necessarily a bit conservative, because + * deletion messages don't take space -- but + * we don't know how what the types of all + * messages are. + */ + full = 0; + spc = Leafspc - gefs_blkfill(b); + n = gefs_newblk(t, b->type); + assert(i >= 0 && j >= 0); + while(i < b->nval || j < up->hi){ + if(i >= b->nval) + c = 1; + else{ + c = -1; + getval(p->b, i, &v); + if(j < up->hi){ + if(up->ins != nil) + m = up->ins[j]; + else + getmsg(up->b, j, &m); + if(msgsz(&m) <= spc) + c = gefs_keycmp(&v.k, &m.p.k); + else + full = 1; + } + } + switch(c){ + /* Value before message: just copy value */ + case -1: + i++; + setval(n, &v); + break; + /* Value merges with message sequence */ + case 0: + i++; + j++; + gefs_cpkvp(&v, &v, buf, sizeof(buf)); + if(v.k.nk > 0 && v.k.k[0] == Kdat) + if(m.op == Oclearb + || m.op == Oinsert + || m.op == Odelete){ + bp = unpackbp(v.v.v, v.v.nv); + gefs_freebp(t, bp); + } + ok = apply(&v, &m, buf, sizeof(buf)); + goto Copyloop; + /* Message before value: Insert message sequence */ + case 1: + j++; + gefs_cpkvp(&v, &m.p, buf, sizeof(buf)); + ok = 0; + if(m.op != Oclearb && m.op != Oclobber){ + /* New keys need to start off with Oinsert */ + assert(m.op == Oinsert); + spc -= valsz(&m.p); + p->pullsz += msgsz(&m); + ok = 1; + } + goto Copyloop; + Copyloop: + while(j < up->hi){ + if(pullmsg(up, j, &v, &m, &full, spc) != 0) + break; + assert(!full); + if(ok && v.k.nk > 0 && v.k.k[0] == Kdat) + if(m.op == Oclearb + || m.op == Oinsert + || m.op == Odelete){ + bp = unpackbp(v.v.v, v.v.nv); + gefs_freebp(t, bp); + } + p->pullsz += msgsz(&m); + ok = apply(&v, &m, buf, sizeof(buf)); + j++; + } + if(ok) + setval(n, &v); + break; + } + } + p->npull = (j - up->lo); + p->op = POmod; + setb(&p->nl, t, n); +} + +/* + * Creates a new block with the contents of the old + * block. When copying the contents, it repacks them + * to minimize the space uses, and applies the changes + * pending from the downpath blocks. + * + * When pidx != -1, + */ +static void +updatepiv(Tree *t, Path *up, Path *p, Path *pp) +{ + char buf[Msgmax]; + int i, j, r, sz, full, spc; + Blk *b, *n; + Msg m, u; + + b = p->b; + n = gefs_newblk(t, b->type); + for(i = 0; i < b->nval; i++){ + if(pp != nil && i == p->midx){ + copyup(n, pp, nil); + if(pp->op == POrot || pp->op == POmerge) + i++; + }else{ + getval(b, i, &m.p); + setval(n, &m.p); + } + } + i = 0; + j = up->lo; + sz = 0; + full = 0; + /* + * Compute free space for pulling messages + * from the parent node. The amount of space + * is the space we currently have, plus the + * amount we pulled down from this node in + * our child. + */ + spc = Bufspc - buffill(b); + if(pp != nil) + spc += pp->pullsz; + while(i < b->nbuf){ + if(i == p->lo) + i += pp->npull; + if(i == b->nbuf) + break; + getmsg(b, i, &m); + /* + * a bit tricky: we're signalling full full not when we're done + * copying, but when we're out of room for new messages. The + * amount of consumed space here is coming from messages that + * we pull down from the parent node. If not, we keep cloning + * messages without pulling. We do not want to break out when + * we report a full buffer here -- it's only too full for new + * data when that happens. + */ + r = pullmsg(up, j, &m.p, &u, &full, spc - sz); + switch(r){ + case -1: + case 0: + setmsg(n, &m); + i++; + break; + case 1: + gefs_cpkvp(&m.p, &u.p, buf, sizeof(buf)); + while(pullmsg(up, j, &m.p, &u, &full, spc) == 0){ + setmsg(n, &u); + sz = msgsz(&u); + p->pullsz += sz; + spc -= sz; + j++; + } + } + } + while(j < up->hi){ + pullmsg(up, j, nil, &u, &full, spc); + if(full) + break; + setmsg(n, &u); + sz = msgsz(&u); + p->pullsz += sz; + spc -= sz; + j++; + } + p->npull = (j - up->lo); + p->op = POmod; + setb(&p->nl, t, n); +} + +/* + * Splits a node, returning the block that msg + * would be inserted into. Split must never + * grow the total height of the tree by more than 1. + */ +static void +splitleaf(Tree *t, Path *up, Path *p) +{ + char buf[Msgmax]; + Blk *b, *d, *l, *r; + int full, copied, spc, ok, halfsz; + int i, j, c; + Bptr bp; + Msg m; + Kvp v; + + /* + * If the block one entry up the + * p is nil, we're at the root, + * so we want to make a new block. + */ + b = p->b; + l = nil; + r = nil; +// if(waserror()){ +// efreeblk(t, l); +// efreeblk(t, r); +// nexterror(); +// } + l = gefs_newblk(t, b->type); + r = gefs_newblk(t, b->type); + + d = l; + i = 0; + j = up->lo; + full = 0; + copied = 0; + halfsz = (2*b->nval + b->valsz + up->sz) / 2; + if(halfsz > Leafspc/2) + halfsz = Leafspc/2; + spc = Leafspc - (halfsz + Msgmax); + bassert(b, b->nval >= 4); + while(i < b->nval){ + /* + * We're trying to balance size, + * but we need at least 2 nodes + * in each half of the split if + * we want a valid tree. + */ + if(d == l) + if((i == b->nval-2) || (i >= 2 && copied >= halfsz)){ + d = r; + spc = Leafspc - (halfsz + Msgmax); + } + getval(b, i, &v); + c = pullmsg(up, j, &v, &m, &full, spc); + switch(c){ + case -1: + i++; + setval(d, &v); + copied += valsz(&v); + break; + case 0: + i++; + j++; + gefs_cpkvp(&v, &v, buf, sizeof(buf)); + copied += valsz(&v); + if(v.k.nk > 0 && v.k.k[0] == Kdat) + if(m.op == Oclearb + || m.op == Oinsert + || m.op == Odelete){ + bp = unpackbp(v.v.v, v.v.nv); + gefs_freebp(t, bp); + } + ok = apply(&v, &m, buf, sizeof(buf)); + goto Copyloop; + case 1: + j++; + gefs_cpkvp(&v, &m.p, buf, sizeof(buf)); + copied += valsz(&v); + ok = 0; + if(m.op != Oclearb && m.op != Oclobber){ + /* New keys need to start off with Oinsert */ + assert(m.op == Oinsert); + spc -= valsz(&m.p); + p->pullsz += msgsz(&m); + ok = 1; + } + goto Copyloop; + Copyloop: + while(j < up->hi){ + if(pullmsg(up, j, &v, &m, &full, spc) != 0) + break; + if(ok && v.k.nk > 0 && v.k.k[0] == Kdat) + if(m.op == Oclearb + || m.op == Oinsert + || m.op == Odelete){ + bp = unpackbp(v.v.v, v.v.nv); + gefs_freebp(t, bp); + } + p->pullsz += msgsz(&m); + ok = apply(&v, &m, buf, sizeof(buf)); + j++; + } + if(ok) + setval(d, &v); + break; + } + } + p->npull = (j - up->lo); + p->op = POsplit; + setb(&p->nl, t, l); + setb(&p->nr, t, r); +// poperror(); +} + +/* + * Splits a node, returning the block that msg + * would be inserted into. Split must never + * grow the total height of the tree by more + * than one. + */ +static void +splitpiv(Tree *t, Path *up, Path *p, Path *pp) +{ + int i, copied, halfsz; + Blk *b, *d, *l, *r; + Kvp tk, mid; + Msg m; + + /* + * If the bp->lock one entry up the + * p is nil, we're at the root, + * so we want to make a new bp->lock. + */ + b = p->b; + l = nil; + r = nil; +// if(waserror()){ +// efreeblk(t, l); +// efreeblk(t, r); +// nexterror(); +// } + l = gefs_newblk(t, b->type); + r = gefs_newblk(t, b->type); + d = l; + copied = 0; + halfsz = (2*b->nval + b->valsz)/2; + bassert(b, b->nval >= 4); + for(i = 0; i < b->nval; i++){ + /* + * Balance size, but ensure we have at least 2 nodes + * in each half of the split so we have a valid tree. + */ + if(d == l && (i == b->nval-2 || (i >= 2 && copied >= halfsz))) + d = r; + if(i == p->idx){ + copyup(d, pp, &copied); + continue; + } + getval(b, i, &tk); + setval(d, &tk); + copied += valsz(&tk); + } + d = l; + getval(r, 0, &mid); + for(i = 0; i < b->nbuf; i++){ + if(i == p->lo) + i += pp->npull; + if(i == b->nbuf) + break; + getmsg(b, i, &m); + if(d == l && gefs_keycmp(&m.p.k, &mid.k) >= 0) + d = r; + setmsg(d, &m); + } + p->op = POsplit; + setb(&p->nl, t, l); + setb(&p->nr, t, r); +// poperror(); +} + +static void +merge(Tree *t, Path *p, Path *pp, int idx, Blk *a, Blk *b) +{ + Blk *d; + Msg m; + int i; + + d = gefs_newblk(t, a->type); + for(i = 0; i < a->nval; i++){ + getval(a, i, &m.p); + setval(d, &m.p); + } + for(i = 0; i < b->nval; i++){ + getval(b, i, &m.p); + setval(d, &m.p); + } + if(a->type == Tpivot){ + for(i = 0; i < a->nbuf; i++){ + getmsg(a, i, &m); + setmsg(d, &m); + } + for(i = 0; i < b->nbuf; i++){ + getmsg(b, i, &m); + setmsg(d, &m); + } + } + p->midx = idx; + pp->op = POmerge; + setb(&pp->nl, t, d); +} + +/* + * Find the first message in buffers of l and r with key >= m. + * Trybalance already guarantees we have space for any split + * that we pick here. + */ +static int +splitidx(Blk *l, Blk *r, Msg *m, int idx) +{ + int i; + Msg n; + + if(l->type == Tleaf) + return idx; + for(i = idx; i < l->nbuf + r->nbuf; i++){ + if(i < l->nbuf) + getmsg(l, i, &n); + else + getmsg(r, i - l->nbuf, &n); + if(gefs_keycmp(&m->p.k, &n.p.k) <= 0) + break; + } + return i; +} + +static void +rotate(Tree *t, Path *p, Path *pp, int midx, Blk *a, Blk *b, int halfpiv) +{ + int i, o, sz, sp; + Blk *d, *l, *r; + Msg m; + + l = nil; + r = nil; +// if(waserror()){ +// efreeblk(t, l); +// efreeblk(t, r); +// nexterror(); +// } + l = gefs_newblk(t, a->type); + r = gefs_newblk(t, a->type); + d = l; + sz = 0; + sp = 0; + for(i = 0; i < a->nval; i++){ + getval(a, i, &m.p); + if(d == l){ + sp = splitidx(a, b, &m, sp); + if(sz >= halfpiv) + d = r; + } + setval(d, &m.p); + sz += valsz(&m.p); + } + for(i = 0; i < b->nval; i++){ + getval(b, i, &m.p); + if(d == l){ + sp = splitidx(a, b, &m, sp); + if(sz >= halfpiv) + d = r; + } + setval(d, &m.p); + sz += valsz(&m.p); + } + if(a->type == Tpivot){ + d = l; + o = 0; + for(i = 0; i < a->nbuf; i++){ + if(o == sp){ + d = r; + o = 0; + } + getmsg(a, i, &m); + setmsg(d, &m); + o++; + } + for(i = 0; i < b->nbuf; i++){ + if(o == sp){ + d = r; + o = 0; + } + getmsg(b, i, &m); + setmsg(d, &m); + o++; + } + } + p->midx = midx; + pp->op = POrot; + setb(&pp->nl, t, l); + setb(&pp->nr, t, r); +// poperror(); +} + +static void +rotmerge(Tree *t, Path *p, Path *pp, int idx, Blk *a, Blk *b) +{ + int na, nb, ma, mb, imbalance; + + bassert(a, a->type == b->type); + + na = 2*a->nval + a->valsz; + nb = 2*b->nval + b->valsz; + if(a->type == Tleaf){ + ma = 0; + mb = 0; + }else{ + ma = 2*a->nbuf + a->bufsz; + mb = 2*b->nbuf + b->bufsz; + } + imbalance = na - nb; + if(imbalance < 0) + imbalance *= -1; + /* works for leaf, because 0 always < Bufspc */ + if(na + nb < (Pivspc - 4*Msgmax) && ma + mb < Bufspc) + merge(t, p, pp, idx, a, b); + else if(imbalance > 4*Msgmax) + rotate(t, p, pp, idx, a, b, (na + nb)/2); +} + +static void +trybalance(Tree *t, Path *p, Path *pp, int idx) +{ + Blk *l, *m, *r; + Kvp kl, kr; + int spc, fill; + Bptr bp; + + if(p->idx == -1 || pp == nil || pp->nl == nil) + return; + if(pp->op != POmod && pp->op != POmerge) + return; + + l = nil; + r = nil; + m = gefs_holdblk(pp->nl); +// if(waserror()){ +// gefs_dropblk(m); +// gefs_dropblk(l); +// gefs_dropblk(r); +// nexterror(); +// } + spc = (m->type == Tleaf) ? Leafspc : Pivspc; + if(idx-1 >= 0){ + getval(p->b, idx-1, &kl); + bp = getptr(&kl, &fill); + if(fill + gefs_blkfill(m) < spc){ + l = gefs_getblk(bp, 0); + rotmerge(t, p, pp, idx-1, l, m); + goto Done; + } + } + if(idx+1 < p->b->nval){ + getval(p->b, idx+1, &kr); + bp = getptr(&kr, &fill); + if(fill + gefs_blkfill(m) < spc){ + r = gefs_getblk(bp, 0); + rotmerge(t, p, pp, idx, m, r); + goto Done; + } + } +Done: + gefs_dropblk(m); + gefs_dropblk(l); + gefs_dropblk(r); +// poperror(); +} + +static Path* +flush(Tree *t, Path *path, int npath) +{ + + Path *up, *p, *pp, *rp; + + /* + * The path must contain at minimum two elements: + * we must have 1 node we're inserting into, and + * an empty element at the top of the path that + * we put the new root into if the root gets split. + */ + assert(npath >= 2); + rp = nil; + pp = nil; + p = &path[npath - 1]; + up = &path[npath - 2]; + if(p->b->type == Tleaf){ + if(!filledleaf(p->b, up->sz)){ + updateleaf(t, p-1, p); + rp = p; + }else{ + splitleaf(t, up, p); + } + p->midx = -1; + pp = p; + up--; + p--; + } + while(p != path){ + /* + * While we will add at most one key to a tree, + * we can mutate the children so that we end up + * replacing small nodes with large ones; as a + * result we need to be a bit more conservative + * here, and split earlier than we may otherwise + * want to do. + */ + if(!filledpiv(p->b, 2)){ + trybalance(t, p, pp, p->idx); + /* + * if we merged the root node, and the + * buffer was empty, break out; if we + * still have data in the buffer, pull + * it down and allow for a degenerate + * root node that we retry in upsert. + * + * if we cleared a degenerate root, + * then we want to drop it from the + * tree. + */ + if(up == path /* at root */ + && pp != nil /* has child */ + && pp->nl != nil /* didn't drop this node */ + && pp->nr == nil /* no siblings */ + && pp->npull == p->b->nbuf){ /* got the whole buffer */ + if((pp->op == POmerge && p->b->nval == 2) + || (pp->op == POmod && p->b->nval == 1)){ + pp->npull = p->npull; + rp = pp; + goto Out; + } + } + updatepiv(t, up, p, pp); + rp = p; + }else{ + splitpiv(t, up, p, pp); + } + pp = p; + up--; + p--; + } + if(pp->nl != nil && pp->nr != nil){ + rp = &path[0]; + rp->nl = gefs_newblk(t, Tpivot); + rp->npull = pp->npull; + rp->pullsz = pp->pullsz; + copyup(rp->nl, pp, nil); + gefs_enqueue(rp->nl); + } +Out: + return rp; +} + +static void +freepath(Tree *t, Path *path, int npath, int ok, size_t pathsz) +{ + Path *p; + + for(p = path; p != path + npath; p++){ + if(ok && p->b != nil) + gefs_freeblk(t, p->b); + gefs_dropblk(p->b); + gefs_dropblk(p->nl); + gefs_dropblk(p->nr); + } + free(path, M_TEMP, pathsz); +} + +/* + * Select child node that with the largest message + * segment in the current node's buffer. + */ +static void +victim(Blk *b, Path *p) +{ + int i, j, lo, maxsz, cursz; + Kvp kv; + Msg m; + + j = 0; + maxsz = 0; + p->b = b; + /* + * Start at the second pivot: all values <= this + * go to the first node. Stop *after* the last entry, + * because entries >= the last entry all go into it. + */ + for(i = 1; i <= b->nval; i++){ + if(i < b->nval) + getval(b, i, &kv); + cursz = 0; + lo = j; + for(; j < b->nbuf; j++){ + getmsg(b, j, &m); + if(i < b->nval && gefs_keycmp(&m.p.k, &kv.k) >= 0) + break; + /* 2 bytes for offset, plus message size in buffer */ + cursz += msgsz(&m); + } + if(cursz > maxsz){ + maxsz = cursz; + p->op = POmod; + p->lo = lo; + p->hi = j; + p->sz = maxsz; + p->idx = i - 1; + p->midx = i - 1; + p->npull = 0; + p->pullsz = 0; + } + } +} + +static void +fastupsert(Tree *t, Blk *b, Msg *msg, int nmsg) +{ + int i, c, o, ri, lo, hi, mid, nbuf; + Msg cmp; + char *p; + Blk *r; + + if((r = gefs_dupblk(t, b)) == nil) + error("dup failed"); + + nbuf = r->nbuf; + for(i = 0; i < nmsg; i++) + setmsg(r, &msg[i]); + + for(i = 0; i < nmsg; i++){ + ri = -1; + lo = 0; + hi = nbuf+i-1; + while(lo <= hi){ + mid = (hi + lo) / 2; + getmsg(r, mid, &cmp); + c = gefs_keycmp(&msg[i].p.k, &cmp.p.k); + switch(c){ + case -1: + hi = mid-1; + break; + case 0: + ri = mid+1; + lo = mid+1; + break; + case 1: + lo = mid+1; + break; + } + } + if(ri == -1) + ri = hi+1; + p = r->data + Pivspc + 2*(nbuf+i); + o = UNPACK16(p); + p = r->data + Pivspc + 2*ri; + memmove(p+2, p, 2*(nbuf+i-ri)); + PACK16(p, o); + } + gefs_enqueue(r); + + rw_enter(&t->lk, RW_WRITE); + t->bp = r->bp; + t->dirty = 1; + rw_exit(&t->lk); + + gefs_freeblk(t, b); + gefs_dropblk(b); + gefs_dropblk(r); +} + + +int +gefs_btupsert(Tree *t, Msg *msg, int nmsg) +{ + int i, npath, npull, dh, sz, height, degen; + size_t pathsz; + Path *path, *rp; + Blk *b, *rb; + Kvp sep; + Bptr bp; + + ASSERT_LOCKED(&fs->mutlk); + if(atomic_load_int(&fs->rdonly)) + error(Erdonly); + sz = 0; + gefs_stablesort(msg, nmsg); + for(i = 0; i < nmsg; i++){ + assert(msg[i].p.k.nk <= Keymax); + assert(msg[i].p.v.nv <= Inlmax); + sz += msgsz(&msg[i]); + } + npull = 0; +Again: + b = gefs_getroot(t, &height); +// if(waserror()){ +// aincl(&fs->rdonly, 1); +// gefs_dropblk(b); +// nexterror(); +// } + if(npull == 0 && b->type == Tpivot && b->nval > 1 && !filledbuf(b, nmsg, sz)){ + fastupsert(t, b, msg, nmsg); +// poperror(); + return 0; + } + /* + * The tree can grow in height by 1 when we + * split, so we allocate room for one extra + * node in the path. + */ + pathsz = (height + 2) * sizeof(Path); + if((path = malloc(pathsz, M_TEMP, M_NOWAIT | M_ZERO)) == nil) + error("path failed"); +// poperror(); +// if(waserror()){ +// freepath(t, path, height+2, 0); /* npath not volatile */ +// nexterror(); +// } + degen = 0; + npath = 0; + path[npath].b = nil; + path[npath].idx = -1; + path[npath].midx = -1; + npath++; + + path[0].sz = sz; + path[0].ins = msg; + path[0].lo = npull; + path[0].hi = nmsg; + while(b->type == Tpivot){ + if(b->nval > 1 && !filledbuf(b, nmsg, path[npath - 1].sz)) + break; + victim(b, &path[npath]); + getval(b, path[npath].idx, &sep); + bp = unpackbp(sep.v.v, sep.v.nv); + b = gefs_getblk(bp, 0); + npath++; + assert(npath < height+2); + } + path[npath].b = b; + path[npath].idx = -1; + path[npath].midx = -1; + path[npath].lo = -1; + path[npath].hi = -1; + path[npath].npull = 0; + path[npath].pullsz = 0; + npath++; + + rp = flush(t, path, npath); + rb = rp->nl; + + if(path[0].nl != nil) + dh = 1; + else if(path[1].nl != nil) + dh = 0; + else if(npath >2 && path[2].nl != nil) + dh = -1; + else + fatal("broken path change"); + + /* + * if we merged the root block, but there + * was still data stuck in the buffer, we + * should try again to clear out the data + * in the degenerate root. + */ + if(rb->type == Tpivot && rb->nval == 1) + degen = 1; + + bassert(rb, rb->bp.addr != 0); + + rw_enter(&t->lk, RW_WRITE); + traceb("setroot", rb->bp); + t->ht += dh; + t->bp = rb->bp; + t->dirty = 1; + rw_exit(&t->lk); + + npull += rp->npull; + freepath(t, path, npath, 1, pathsz); +// poperror(); + + if(npull != nmsg || degen){ + tracem("short pull"); + goto Again; + } + return 0; +} + +Blk* +gefs_getroot(Tree *t, int *h) +{ + Bptr bp; + + rw_enter(&t->lk, RW_WRITE); + bp = t->bp; + if(h != nil) + *h = t->ht; + rw_exit(&t->lk); + + return gefs_getblk(bp, 0); +} + +/* + * btlookup: lookup key in tree + * Returns: 0 on success (found), ENOENT if not found, ENOMEM on error + */ +int +btlookup(Tree *t, Key *k, Kvp *r, char *buf, int nbuf) +{ + int i, j, h, ok, same, err; + Blk *b, **p; + Bptr bp; + Msg m; + + b = gefs_getroot(t, &h); + if((p = malloc(h * sizeof(Blk*), M_TEMP, M_NOWAIT | M_ZERO)) == nil){ + gefs_dropblk(b); + return ENOMEM; + } + ok = 0; + p[0] = gefs_holdblk(b); + for(i = 1; i < h; i++){ + if(blksearch(p[i-1], k, r, &same) == -1) + break; + bp = unpackbp(r->v.v, r->v.nv); + p[i] = gefs_getblk(bp, 0); + } + if(p[h-1] != nil) + blksearch(p[h-1], k, r, &ok); + if(ok) + gefs_cpkvp(r, r, buf, nbuf); + for(i = h-2; i >= 0; i--){ + if(p[i] == nil) + continue; + j = bufsearch(p[i], k, &m, &same); + if(j < 0 || !same) + continue; + if(ok || m.op == Oinsert) + ok = apply(r, &m, buf, nbuf); + else if(m.op != Oclearb && m.op != Oclobber) + fatal("lookup missing insert\n"); + for(j++; j < p[i]->nbuf; j++){ + getmsg(p[i], j, &m); + if(gefs_keycmp(k, &m.p.k) != 0) + break; + ok = apply(r, &m, buf, nbuf); + } + } + for(i = 0; i < h; i++) + gefs_dropblk(p[i]); + gefs_dropblk(b); + free(p, M_TEMP, h * sizeof(Blk*)); + err = ok ? 0 : ENOENT; + return err; +} + +void +btnewscan(Scan *s, char *pfx, int npfx) +{ + memset(s, 0, sizeof(*s)); + s->first = 1; + s->donescan = 0; + s->offset = 0; + s->pfx.k = s->pfxbuf; + s->pfx.nk = npfx; + memmove(s->pfxbuf, pfx, npfx); + + s->kv.v.v = s->kvbuf+npfx; + s->kv.v.nv = 0; + gefs_cpkey(&s->kv.k, &s->pfx, s->kvbuf, sizeof(s->kvbuf)); +} + +int +btenter(Tree *t, Scan *s) +{ + int i, same; + Scanp *p; + Msg m, c; + Bptr bp; + Blk *b; + Kvp v; + + if(s->donescan) + return 0; + b = gefs_getroot(t, &s->ht); + if((s->path = malloc(s->ht * sizeof(Scanp), M_TEMP, M_NOWAIT | M_ZERO)) == nil){ + gefs_dropblk(b); + return ENOMEM; + } + p = s->path; + p[0].b = b; + for(i = 0; i < s->ht; i++){ + p[i].vi = blksearch(b, &s->kv.k, &v, &same); + if(b->type == Tpivot){ + if(p[i].vi == -1) + getval(b, ++p[i].vi, &v); + p[i].bi = bufsearch(b, &s->kv.k, &m, &same); + if(p[i].bi == -1){ + p[i].bi++; + }else if(!same || !s->first){ + /* scan past repeated messages */ + while(p[i].bi < p[i].b->nbuf){ + getmsg(p[i].b, p[i].bi, &c); + if(gefs_keycmp(&m.p.k, &c.p.k) != 0) + break; + p[i].bi++; + } + } + bp = unpackbp(v.v.v, v.v.nv); + if((b = gefs_getblk(bp, 0)) == NULL) + return EIO; + p[i+1].b = b; + }else if(p[i].vi == -1 || !same || !s->first) + p[i].vi++; + } + s->first = 0; + return 0; +} + +int +btnext(Scan *s, Kvp *r) +{ + int i, j, h, ok, start, bufsrc; + Scanp *p; + Msg m, n; + Bptr bp; + Kvp kv; + +Again: + p = s->path; + h = s->ht; + start = h; + bufsrc = -1; + if(p == nil || s->donescan) + return 0; + /* load up the correct blocks for the scan */ + for(i = h-1; i >= 0; i--){ + if(p[i].b != nil + &&(p[i].vi < p[i].b->nval || p[i].bi < p[i].b->nbuf)) + break; + if(i == 0){ + s->donescan = 1; + return 0; + } + if(p[i].b != nil) + gefs_dropblk(p[i].b); + p[i].b = nil; + p[i].vi = 0; + p[i].bi = 0; + p[i-1].vi++; + start = i; + } + + if(p[start-1].vi < p[start-1].b->nval){ + for(i = start; i < h; i++){ + getval(p[i-1].b, p[i-1].vi, &kv); + bp = unpackbp(kv.v.v, kv.v.nv); + if((p[i].b = gefs_getblk(bp, 0)) == nil) + return EIO; + } + + /* find the minimum key along the path up */ + m.op = Oinsert; + getval(p[h-1].b, p[h-1].vi, &m.p); + }else{ + getmsg(p[start-1].b, p[start-1].bi, &m); + assert(m.op == Oinsert); + bufsrc = start-1; + } + + for(i = h-2; i >= 0; i--){ + if(p[i].b == nil || p[i].bi == p[i].b->nbuf) + continue; + getmsg(p[i].b, p[i].bi, &n); + if(gefs_keycmp(&n.p.k, &m.p.k) < 0){ + bufsrc = i; + m = n; + } + } + if(m.p.k.nk < s->pfx.nk || memcmp(m.p.k.k, s->pfx.k, s->pfx.nk) != 0){ + s->donescan = 1; + return 0; + } + + /* scan all messages applying to the message */ + ok = 1; + gefs_cpkvp(r, &m.p, s->kvbuf, sizeof(s->kvbuf)); + if(bufsrc == -1) + p[h-1].vi++; + else + p[bufsrc].bi++; + for(i = h-2; i >= 0; i--){ + for(j = p[i].bi; p[i].b != nil && j < p[i].b->nbuf; j++){ + getmsg(p[i].b, j, &m); + if(gefs_keycmp(&r->k, &m.p.k) != 0) + break; + ok = apply(r, &m, s->kvbuf, sizeof(s->kvbuf)); + p[i].bi++; + } + } + if(!ok) + goto Again; + return 1; +} + +void +btexit(Scan *s) +{ + int i; + + for(i = 0; i < s->ht; i++) + gefs_dropblk(s->path[i].b); + free(s->path, M_TEMP, s->ht * sizeof(Scanp)); + s->path = nil; + s->ht = 0; +} diff --git a/sys/gefs/u.h b/sys/gefs/u.h new file mode 100644 index 00000000000..30bfc379df6 --- /dev/null +++ b/sys/gefs/u.h @@ -0,0 +1,31 @@ +#define nil ((void*)0) +typedef unsigned short ushort; +typedef unsigned char uchar; +typedef unsigned long ulong; +typedef unsigned int uint; +typedef signed char schar; +typedef long long vlong; +typedef unsigned long long uvlong; +typedef long long intptr; +typedef unsigned long long uintptr; +typedef unsigned long long usize; +typedef uint Rune; +typedef union FPdbleword FPdbleword; +#define JMPBUFSP 0 +#define JMPBUFPC 1 +#define JMPBUFDPC 0 +typedef unsigned int mpdigit; /* for /sys/include/mp.h */ +typedef unsigned char u8int; +typedef unsigned short u16int; +typedef unsigned int u32int; +typedef unsigned long long u64int; +typedef signed char s8int; +typedef signed short s16int; +typedef signed int s32int; +typedef signed long long s64int; + +#define Along int +#define Avlong vlong +#define Aptr void* + +#define nelem(a) (sizeof(a)/sizeof((a)[0])) diff --git a/sys/gefs/util.c b/sys/gefs/util.c new file mode 100644 index 00000000000..7e87177e9f2 --- /dev/null +++ b/sys/gefs/util.c @@ -0,0 +1,84 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "u.h" +#include "avl.h" + +#include "dat.h" +#include "fns.h" + +Errctx **errctx; + +Chan* +gefs_mkchan(int size) +{ + Chan *c; + + c = malloc(sizeof(Chan) + (size-1)*sizeof(void*), M_TEMP, M_WAITOK|M_ZERO); + c->size = size; + return c; +} + +void +gefs_chsend(Chan *c, void *a) +{ + int i, n; + + rw_enter(&c->lk, RW_WRITE); + while((n = c->count) >= c->size) + rwsleep(&c->full, &c->lk, IPL_NONE, "send", 0); + i = c->loc + n; + if(i >= c->size) + i -= c->size; + c->args[i] = a; + c->count = n+1; + wakeup(&c->empty); /* no longer empty */ + rw_exit(&c->lk); +} + +void* +gefs_chrecv(Chan *c) +{ + void *a; + int i, n; + + rw_enter(&c->lk, RW_WRITE); + while((n = c->count) <= 0) + rwsleep(&c->empty, &c->lk, IPL_NONE, "recv", 0); + i = c->loc; + a = c->args[i]; + i++; + if(i >= c->size) + i = 0; + c->loc = i; + c->count = n-1; + wakeup(&c->full); /* no longer full */ + rw_exit(&c->lk); + return a; +} + +_Noreturn void +gefs_broke(char *fmt, ...) +{ + char buf[256]; + va_list ap; + + /* for now: a corrupt/failed block is fatal. later: go read-only. */ + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + panic("gefs: %s", buf); +} + +void +gefs_atomic_load_vlong(vlong *v) +{ +} diff --git a/sys/gefs/vnops.c b/sys/gefs/vnops.c new file mode 100644 index 00000000000..b254a84b7c1 --- /dev/null +++ b/sys/gefs/vnops.c @@ -0,0 +1,1752 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "u.h" +#include "avl.h" + +#include "dat.h" +#include "fns.h" + +const char zeroblock[Blksz]; + +const struct vops gefs_vops; +const struct vops gefs_devops; +const struct vops gefs_fifoops; +int gefs_read1(struct vnode *vp, struct uio *uio); + +int +gefs_upsert(Mount *mnt, Msg *m, int nm) +{ + Tree *r; + + if(!(mnt->flag & Lmut)) + error(Erdonly); + r = agetp(&mnt->root); + if(r->nlbl != 1 || r->nref != 0) { + r = gefs_updatesnap(r, mnt->name, mnt->flag); + atomic_swap_ptr(&mnt->root, r); + } + return gefs_btupsert(r, m, nm); +} + +// +//static int +//mode2bits(int req) +//{ +// int m; +// +// m = 0; +// switch(req&0xf){ +// case UIO_READ: m = 04; break; +// case UIO_WRITE: m = 02; break; +// case UIO_RDWR: m = 06; break; +// case UIO_EXEC: m = 05; break; +// } +// if(req&UIO_TRUNC) +// m |= 02; +// return m; +//} +// + +int +groupleader(int u, int g) +{ + return 1; +} +int +ingroup(int u, int g) +{ + return 1; +} + +int +gefs_permok(struct vnode *vn, ulong fmode, int fuid, int fgid, int m) +{ + return 0; +#ifdef NOTYET + /* uid none gets only other permissions */ + if(f->permit) + return 0; + if(f->uid != noneid){ + if(f->uid == fuid) + if((m & (fmode>>6)) == m) + return 0; + if(ingroup(f->uid, fgid)) + if((m & (fmode>>3)) == m) + return 0; + } + if((m & fmode) == m){ + if((fmode & DMDIR) && (m == DMEXEC)) + return 0; + if(!ingroup(f->uid, nogroupid)) + return 0; + } + return -1; +#endif +} + +Dent* +gefs_getdent(Mount *mnt, vlong pqid, Xdir *d) +{ + Dent *de; + char *e; + u32int h; + + h = gefs_ihash(d->qid.path) % Ndtab; + rw_enter(&mnt->dtablk, RW_WRITE); + for(de = mnt->dtab[h]; de != nil; de = de->next){ + if(de->d.qid.path == d->qid.path){ + atomic_inc_int(&de->ref); + goto Out; + } + } + de = malloc(sizeof(Dent), M_TEMP, M_NOWAIT | M_ZERO); + if(de == nil){ + rw_exit(&mnt->dtablk); + return nil; + } + de->d = *d; + de->ref = 1; + de->up = pqid; + e = packdkey(de->buf, sizeof(de->buf), pqid, d->name, strlen(d->name)); + de->k.k = de->buf; + de->k.nk = e - de->buf; + de->d.name = de->buf + 11; + de->next = mnt->dtab[h]; + rw_init(&de->lk, IPL_NONE); + mnt->dtab[h] = de; +Out: + rw_exit(&mnt->dtablk); + return de; +} + +void +gefs_clunkdent(Mount *mnt, Dent *de) +{ + Dent *e, **pe; + u32int h; + + if(de == nil) + return; + h = gefs_ihash(de->d.qid.path) % Ndtab; + rw_enter(&mnt->dtablk, RW_WRITE); + if(atomic_dec_int_nv(&de->ref) != 0){ + rw_exit(&mnt->dtablk); + return; + } + pe = &mnt->dtab[h]; + for(e = mnt->dtab[h]; e != nil; e = e->next){ + if(e == de){ + *pe = e->next; + break; + } + pe = &e->next; + } + rw_exit(&mnt->dtablk); + free(de->scan, M_TEMP, sizeof(Scan)); + free(de, M_TEMP, sizeof(Dent)); +} + +Mount* +gefs_getmount(char *name) +{ + Mount *mnt, *hd; + Tree *t; + int flg; + + if(strcmp(name, "dump") == 0){ + atomic_add_int_nv(&fs->snapmnt->ref, 1); + return fs->snapmnt; + } + + rw_enter(&fs->mountlk, RW_WRITE); + hd = agetp(&fs->mounts); + for(mnt = hd; mnt != nil; mnt = mnt->next){ + if(strcmp(name, mnt->name) == 0){ + atomic_add_int_nv(&mnt->ref, 1); + rw_exit(&fs->mountlk); + return mnt; + } + } + if((mnt = malloc(sizeof(*mnt), M_TEMP, M_WAITOK|M_ZERO)) == NULL){ + rw_exit(&fs->mountlk); + return NULL; + } + rw_init(&mnt->lk, IPL_NONE); + rw_init(&mnt->dtablk, IPL_NONE); + atomic_swap_uint(&mnt->ref, 1); + snprintf(mnt->name, sizeof(mnt->name), "%s", name); + if((t = gefs_opensnap(name, &flg)) == nil){ + rw_exit(&fs->mountlk); + free(mnt, M_TEMP, sizeof(*mnt)); + return NULL; + } + mnt->flag = flg; + atomic_swap_ptr(&mnt->root, t); + mnt->next = hd; +// loadautos(mnt); + + atomic_swap_ptr(&fs->mounts, mnt); + rw_exit(&fs->mountlk); + return mnt; +} + +void +gefs_clunkmount(Mount *mnt) +{ + Mount *p, **pp; + + if(mnt == nil) + return; + rw_enter(&fs->mountlk, RW_WRITE); + if(atomic_add_int_nv(&mnt->ref, -1) == 0){ + pp = nil; + for(p = agetp(&fs->mounts); p != nil; p = p->next){ + if(p == mnt) + break; + pp = &p->next; + } + assert(p != nil); + if(pp == nil) + atomic_swap_ptr(&fs->mounts, p->next); + else + *pp = p->next; + gefs_limbo(DFmnt, (Limbo*)p); + } + rw_exit(&fs->mountlk); +} + +void +dkey(Key *k, vlong up, char *name, int nname, char *buf, int nbuf) +{ + char *p; + + p = packdkey(buf, nbuf, up, name, nname); + k->k = buf; + k->nk = p - buf; +} + +int +gefs_findparent(Tree *t, vlong up, vlong *qpath, char **name, char *buf, int nbuf) +{ + char *p, kbuf[Keymax]; + Kvp kv; + Key k; + + p = packsuper(kbuf, sizeof(kbuf), up); + k.k = kbuf; + k.nk = p - kbuf; + if(btlookup(t, &k, &kv, buf, nbuf) != 0) + error(Esrch); + *name = unpackdkey(kv.v.v, kv.v.nv, qpath); + return 1; +} + +int +gefs_vget(struct mount *mp, ino_t ino, struct vnode **vpp) +{ + int e; + + if ((e = getnewvnode(VT_GEFS, mp, &gefs_vops, vpp)) != 0) + *vpp = NULL; + return e; +} + +void +gefs_devinit(struct vnode **vpp, Dent *de) +{ + struct vnode *vp, *nvp; + + vp = *vpp; + vp->v_type = gefs_d2v(de->d.devt); + switch(vp->v_type){ + case VCHR: + case VBLK: + if ((nvp = checkalias(vp, de->d.rdev, vp->v_mount)) != NULL) { + /* + * Discard unneeded vnode, but save its inode. + * Note that the lock is carried over in the inode + * to the replacement vnode. + */ + nvp->v_data = vp->v_data; + vp->v_data = NULL; + vp->v_op = &spec_vops; +#ifdef VFSLCKDEBUG + vp->v_flag &= ~VLOCKSWORK; +#endif + vrele(vp); + vgone(vp); + /* + * Reinitialize aliased inode. + */ + vp = nvp; + } + vp->v_op = &gefs_devops; + break; + case VFIFO: +#ifdef FIFO + vp->v_op = &gefs_fifoops; + break; +#else + return (EOPNOTSUPP); +#endif + default: + break; + } + *vpp = vp; +} + + +int +gefs_lookup(void *v) +{ + char kbuf[Maxent], kvbuf[Kvmax], *name; + struct vop_lookup_args *ap = v; + struct vnode *dvp = ap->a_dvp; + struct vnode **vpp = ap->a_vpp; + struct componentname *cnp = ap->a_cnp; + int nname; + vlong qpath, up; + Dent *dent; + Mount *mnt; + Tree *t; + Xdir d; + Kvp kv; + Key k; + int e; + + e = ENOENT; + *vpp = NULL; +// o = dvp->v_data; + gefs_epochstart(); +// rw_enter(&o->lk, RW_READ); + mnt = dvp->v_mount->mnt_data; + t = agetp(&mnt->root); + dent = dvp->v_data; + rw_enter(&dent->lk, RW_READ); + up = dent->up; + qpath = dent->d.qid.path; + d = dent->d; + rw_exit(&dent->lk); + name = cnp->cn_nameptr; + nname = cnp->cn_namelen; + if(nname > Maxname){ + e = ENAMETOOLONG; + goto Out; + } + if((e = cache_lookup(dvp, vpp, cnp)) != -1) + goto Out; + +// if(fsaccess(o, d.mode, d.uid, d.gid, DMEXEC) != 0) +// break; + if(d.qid.path == Qdump){ +// if((mnt = gefs_getmount(name)) == NULL) +// goto Out; +// name = ""; +// qpath = -1ULL; +// t = agetp(&mnt->root); + e = EOPNOTSUPP; + goto Out; + }else if(nname == 1 && name[0] == '.'){ + *ap->a_vpp = ap->a_dvp; + vref(ap->a_dvp); + e = 0; + goto Out; + } + if(cnp->cn_flags & ISDOTDOT){ +// if(up == -1 && o->fromdump){ +// mnt = fs->snapmnt; +// filldumpdir(&d); +// prev = -1ULL; +// up = -1ULL; +// r.wqid[i] = d.qid; +// continue; +// } + gefs_findparent(t, up, &qpath, &name, kbuf, sizeof(kbuf)); + dkey(&k, qpath, name, strlen(name), kbuf, sizeof(kbuf)); + }else{ + dkey(&k, qpath, name, nname, kbuf, sizeof(kbuf)); + } + if(btlookup(t, &k, &kv, kvbuf, sizeof(kvbuf)) != 0){ + e = ENOENT; + if((cnp->cn_nameiop == CREATE || cnp->cn_nameiop == RENAME) && + (cnp->cn_flags & ISLASTCN)){ + if((e = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred, curproc)) != 0) + goto Out; + cnp->cn_flags |= SAVENAME; + e = EJUSTRETURN; + } + goto Out; + } + kv2dir(&kv, &d); + if((dent = gefs_getdent(mnt, qpath, &d)) == NULL) + goto Out; +// f = gefs_dupfid(o); +// rw_enter(&f->lk, RW_READ); +// gefs_clunkdent(f->mnt, f->dent); +// gefs_clunkdent(f->mnt, f->dir); + if((e = VFS_VGET(dvp->v_mount, d.qid.path, vpp)) != 0){ + gefs_clunkdent(mnt, dent); + *vpp = nil; + goto Out; + } + (*vpp)->v_data = dent; + if(dent->d.qid.type & GEFS_QTDIR) + (*vpp)->v_type = VDIR; + else if(dent->d.flag & GEFS_FSYMLINK) + (*vpp)->v_type = VLNK; + else if(dent->d.flag & GEFS_FDEVNOD) + gefs_devinit(vpp, dent); + else + (*vpp)->v_type = VREG; + if(!(cnp->cn_flags&LOCKPARENT)||!(cnp->cn_flags&ISLASTCN)) + VOP_UNLOCK(dvp); + e = 0; +Out: + gefs_epochend(); +// rw_exit(&o->lk); + return e; +} + +int +gefs_open(void *v) +{ +#ifdef NOTYET + char *p, *e, buf[Kvmax]; + int mbits; + Tree *t; + Fcall r; + Xdir d; + Kvp kv; + Msg mb; + + mbits = mode2bits(m->mode); + if((f = getfid(m->conn, m->fid)) == nil) + error(Enofid); + if(waserror()){ + putfid(f); + nexterror(); + } + if(m->mode & OTRUNC) + truncwait(f->dent, id); + t = agetp(&f->mnt->root); + if((f->qpath & Qdump) != 0){ + filldumpdir(&d); + }else{ + if(btlookup(t, &f->dent->k, &kv, buf, sizeof(buf)) != 0) + error(Esrch); + kv2dir(&kv, &d); + } + wlock(f->dent); + if(waserror()){ + wunlock(f->dent); + nexterror(); + } + if(f->dent->gone) + error(Ephase); + if((f->dent->qid.type & QTEXCL) && u(&f->dent->ref) != 1) + error(Elocked); + if((f->dent->qid.type & QTDIR) && (mbits & 0222) != 0) + error(Eperm); + if(m->mode & ORCLOSE){ + if(fsaccess(f, f->dmode, f->duid, f->dgid, DMWRITE) == -1) + error(Eperm); + if((e = candelete(f)) != nil) + error(e); + } + if(fsaccess(f, d.mode, d.uid, d.gid, mbits) == -1) + error(Eperm); + f->dent->length = d.length; + wunlock(f->dent); + poperror(); + + wlock(f); + if(waserror()){ + wunlock(f); + nexterror(); + } + if(f->mode != -1) + error(Einuse); + if(m->mode & ORCLOSE){ + f->rclose = emalloc(sizeof(Amsg), 1); + if(waserror()){ + free(f->rclose); + f->rclose = nil; + nexterror(); + } + } + if((m->mode & OTRUNC) && !(f->dent->mode & DMAPPEND)){ + wlock(f->dent); + if(waserror()){ + wunlock(f->dent); + freeamsg(*ao); + *ao = nil; + nexterror(); + } + *ao = emalloc(sizeof(Amsg), 1); + f->dent->trunc = 1; + atomic_add_int_nv(&f->dent->ref, 1); + atomic_add_int_nv(&f->mnt->ref, 1); + (*ao)->op = AOclear; + (*ao)->mnt = f->mnt; + (*ao)->qpath = f->qpath; + (*ao)->off = 0; + (*ao)->end = f->dent->length; + (*ao)->dent = f->dent; + + f->dent->muid = f->uid; + f->dent->qid.vers++; + f->dent->length = 0; + + mb.op = Owstat; + p = buf; + p[0] = Owsize|Owmuid; p += 1; + PACK64(p, 0); p += 8; + PACK32(p, f->uid); p += 4; + mb.k = f->dent->k; + mb.nk = f->dent->nk; + mb.v = buf; + mb.nv = p - buf; + + upsert(f->mnt, &mb, 1); + wunlock(f->dent); + poperror(); + } + f->mode = mbits; + if(m->mode & ORCLOSE) + poperror(); /* free(f->rclose) */ + + r.type = Ropen; + r.qid = d.qid; + r.iounit = f->iounit; + + wunlock(f); + poperror(); + putfid(f); + poperror(); + + respond(m, &r); +#endif + return 0; +} + +void +gefs_touch(Dent *de, Msg *msg) +{ + rw_enter(&de->lk, RW_WRITE); + de->d.qid.vers++; + msg->op = Owstat; + msg->p.k = de->k; + msg->p.v.v = "\0"; + msg->p.v.nv = 1; + rw_exit(&de->lk); +} + +int +gefs_create1(struct vnode *dvp, struct vnode **vpp, + struct componentname *cnp, struct vattr *vap, + int qtype, uvlong flag, vlong len, int devt, dev_t rdev) +{ + char *p, buf[Kvmax], upkbuf[Keymax], upvbuf[Maxent], name[Keymax]; + struct timespec now; + int nm, dgid, perm, e; + Mount *mnt; + Dent *de, *pde; + Msg mb[3]; + Xdir d; + + perm = vap->va_mode & 0777; + if(vap->va_type == VDIR || (qtype & GEFS_QTDIR)) + perm |= GEFS_DMDIR; + + pde = dvp->v_data; + mnt = dvp->v_mount->mnt_data; +// rw_enter(&f->lk, RW_WRITE); +// de = f->dent; + rw_enter(&pde->lk, RW_READ); + if(fs->nextqid >= Qdump){ + e = ENOSPC; + goto error; + } + if(pde->gone){ + e = EINVAL; + goto error; + } + if((pde->d.mode & GEFS_DMDIR) == 0){ + e = ENOTDIR; + goto error; + } + dgid = pde->d.gid; + rw_exit(&pde->lk); + + nm = 0; + d.qid.type = 0; + d.flag = flag; + if(perm & GEFS_DMDIR) + d.qid.type |= GEFS_QTDIR; + if(perm & GEFS_DMAPPEND) + d.qid.type |= GEFS_QTAPPEND; + if(perm & GEFS_DMEXCL) + d.qid.type |= GEFS_QTEXCL; + if(perm & GEFS_DMTMP) + d.qid.type |= GEFS_QTTMP; + nanotime(&now); + d.qid.path = fs->nextqid++; + d.qid.vers = 0; + d.mode = perm; + snprintf(name, sizeof(name), "%.*s", (int)cnp->cn_namelen, cnp->cn_nameptr); + d.name = name; + d.atime = SEC_TO_NSEC(now.tv_sec) + now.tv_nsec; + d.mtime = d.atime; + d.length = len; + d.uid = cnp->cn_cred->cr_uid; + d.gid = dgid; + d.muid = d.uid; + d.devt = devt; + d.rdev = rdev; + + mb[nm].op = Oinsert; + dir2kv(pde->d.qid.path, &d, &mb[nm].p, buf, sizeof(buf)); + nm++; + + if(perm & GEFS_DMDIR){ + mb[nm].op = Oinsert; + p = packsuper(upkbuf, sizeof(upkbuf), d.qid.path); + mb[nm].p.k.k = upkbuf; + mb[nm].p.k.nk = p - upkbuf; + p = packdkey(upvbuf, sizeof(upvbuf), pde->d.qid.path, d.name, cnp->cn_namelen); + mb[nm].p.v.v = upvbuf; + mb[nm].p.v.nv = p - upvbuf; + nm++; + } + gefs_touch(pde, &mb[nm++]); + assert(nm <= nelem(mb)); + gefs_upsert(mnt, mb, nm); + + if((e = VFS_VGET(dvp->v_mount, d.qid.path, vpp)) != 0){ + *vpp = nil; + goto ecreate; + } + de = gefs_getdent(mnt, pde->d.qid.path, &d); +// n = gefs_dupfid(f); +// n->pqpath = f->qpath; +// n->qpath = d.qid.path; +// n->dent = de; +// n->duid = duid; +// n->dgid = dgid; +// n->dmode = dmode; + + if((cnp->cn_flags & SAVESTART) == 0) + pool_put(&namei_pool, cnp->cn_pnbuf); + (*vpp)->v_data = de; + if(de->d.qid.type & GEFS_QTDIR) + (*vpp)->v_type = VDIR; + else if(de->d.flag & GEFS_FSYMLINK) + (*vpp)->v_type = VLNK; + else if(de->d.flag & GEFS_FDEVNOD) + (*vpp)->v_type = gefs_d2v(de->d.devt); + else + (*vpp)->v_type = VREG; +// rw_exit(&f->lk); + return 0; + +error: + rw_exit(&pde->lk); +ecreate: +// rw_exit(&f->lk); + gefs_epochclean(); + return e; +} + +int +gefs_writestr(struct vnode *vp, char *str, size_t len) +{ + char kbuf[Offksz], vbuf[Ptrsz]; + Blk *b; + Tree *r; + Msg m; + Dent *dent; + Mount *mnt; + + mnt = vp->v_mount->mnt_data; + dent = vp->v_data; + if(len+1 >= Blksz) + return EINVAL; + r = agetp(&mnt->root); + b = gefs_newdblk(r, dent->d.qid.path, 0); +// if(waserror()){ +// freeblk(r, b); +// dropblk(b); +// nexterror(); +// } + m.op = Oinsert; + m.p.k.k = kbuf; + m.p.k.nk = Offksz; + m.p.v.v = vbuf; + m.p.v.nv = Ptrsz; + m.p.k.k[0] = Kdat; + PACK64(m.p.k.k+1, dent->d.qid.path); + PACK64(m.p.k.k+9, 0ULL); + memset(b->b->b_data, 0, Blksz); + memcpy(b->b->b_data, str, len+1); + gefs_enqueue(b); +// poperror(); + + packbp(m.p.v.v, m.p.v.nv, &b->bp); + gefs_dropblk(b); + if(gefs_upsert(mnt, &m, 1) != 0) + return EIO; + return 0; +} + +int +gefs_create(void *v) +{ + struct vop_create_args *ap = v; + struct vnode *dvp = ap->a_dvp; + struct vnode **vpp = ap->a_vpp; + struct componentname *cnp = ap->a_cnp; + struct vattr *vap = ap->a_vap; + int err; + + rw_enter(&fs->mutlk, RW_WRITE); + err = gefs_create1(dvp, vpp, cnp, vap, GEFS_QTFILE, 0, 0, 0, VNOVAL); + rw_exit(&fs->mutlk); + gefs_epochclean(); + return err; +} + +int +gefs_mkdir(void *v) +{ + struct vop_mkdir_args *ap = v; + struct vnode *dvp = ap->a_dvp; + struct vnode **vpp = ap->a_vpp; + struct componentname *cnp = ap->a_cnp; + struct vattr *vap = ap->a_vap; + int err; + + rw_enter(&fs->mutlk, RW_WRITE); + err = gefs_create1(dvp, vpp, cnp, vap, GEFS_QTDIR, 0, 0, 0, VNOVAL); + rw_exit(&fs->mutlk); + gefs_epochclean(); + return err; +} + +int +gefs_symlink(void *v) +{ + struct vop_symlink_args *ap = v; + struct vnode *dvp = ap->a_dvp; + struct vnode **vpp = ap->a_vpp; + struct componentname *cnp = ap->a_cnp; + struct vattr *vap = ap->a_vap; + size_t len; + int err; + + len = strlen(ap->a_target); + rw_enter(&fs->mutlk, RW_WRITE); + if((err = gefs_create1(dvp, vpp, cnp, vap, GEFS_QTFILE, GEFS_FSYMLINK, len, 0, VNOVAL)) == 0) + err = gefs_writestr(*vpp, ap->a_target, len); + rw_exit(&fs->mutlk); + gefs_epochclean(); + if(*vpp != NULL) + vput(*vpp); + return err; +} + +int +gefs_mknod(void *v) +{ + struct vop_mknod_args *ap = v; + struct vnode *dvp = ap->a_dvp; + struct vattr *vap = ap->a_vap; + struct vnode **vpp = ap->a_vpp; + struct componentname *cnp = ap->a_cnp; + int err; + + rw_enter(&fs->mutlk, RW_WRITE); + err = gefs_create1(dvp, vpp, cnp, vap, GEFS_QTFILE, GEFS_FDEVNOD, 0, + gefs_v2d(vap->va_type), vap->va_rdev); + rw_exit(&fs->mutlk); + gefs_epochclean(); + if(*vpp != NULL) + vput(*vpp); + return err; +} + +int +gefs_readlink(void *v) +{ + struct vop_readlink_args *ap = v; + struct vnode *vp = ap->a_vp; + struct uio *uio = ap->a_uio; + + return gefs_read1(vp, uio); +} + +int +gefs_remove1(struct vnode *dvp, struct vnode *vp) +{ +// char *e; + char *buf; + int nm, error; + Msg mb[3]; + Mount *mnt; + Amsg *am; + Dent *dent; + Tree *t; + Kvp kv; + +// f = vp->v_data; + dent = vp->v_data; + mnt = vp->v_mount->mnt_data; + t = agetp(&mnt->root); + nm = 0; + am = NULL; + buf = malloc(Kvmax, M_TEMP, M_WAITOK|M_ZERO); + rw_enter(&fs->mutlk, RW_WRITE); +// rw_enter(&f->lk, RW_WRITE); +// gefs_clunkfid(f, ao); + if(atomic_load_int(&fs->rdonly)) + error(Erdonly); + while(dent->trunc) + rwsleep(&dent->truncrz, &fs->mutlk, IPL_NONE, "truncate", 0); + rw_enter(&dent->lk, RW_WRITE); +// if(waserror()){ +// wunlock(f->dent); +// wunlock(f); +// putfid(f); +// freeamsg(*ao); +// *ao = nil; +// nexterror(); +// } + if(dent->d.qid.path & Qmagic){ + error = EPERM; + goto Error; + } + if(dent->gone){ + error = EEXIST; + goto Error; + } + /* + * we need a double check that the file is in the tree + * here, because the walk to the fid is done in a reader + * proc that can look it up in a stale version of the + * tree, while we clunk the dent in the mutator proc. + * + * this means we can theoretically get some deletions + * of files that are already gone. + */ + if((error = btlookup(t, &dent->k, &kv, buf, Kvmax)) != 0) + goto Error; +// if((e = candelete(f)) != nil) +// error(e); +// if(fsaccess(f, f->dmode, f->duid, f->dgid, DMWRITE) == -1) +// error(Eperm); + +// gefs_freeamsg(*ao); + am = nil; + + mb[nm].op = Odelete; + mb[nm].p.k = dent->k; + mb[nm].p.v.v = nil; + mb[nm].p.v.nv = 0; + nm++; + if(dent->d.qid.type & GEFS_QTDIR){ + packsuper(buf, Kvmax, dent->d.qid.path); + mb[nm].op = Oclobber; + mb[nm].p.k.k = buf; + mb[nm].p.k.nk = Upksz; + mb[nm].p.v.v = nil; + mb[nm].p.v.nv = 0; + nm++; + }else{ + am = emalloc(sizeof(Amsg), 1); + atomic_add_int_nv(&mnt->ref, 1); + am->op = AOclear; + am->mnt = mnt; + am->qpath = dent->d.qid.path; + am->off = 0; + am->end = dent->d.length; + am->dent = nil; + } + gefs_touch(dvp->v_data, &mb[nm++]); + assert(nm <= nelem(mb)); + gefs_upsert(mnt, mb, nm); + dent->gone = 1; + rw_exit(&dent->lk); +// rw_exit(&f->lk); + rw_exit(&fs->mutlk); + gefs_epochclean(); + free(buf, M_TEMP, Kvmax); + if(am != nil) + gefs_chsend(fs->admchan, am); + return 0; +Error: + rw_exit(&dent->lk); +// rw_exit(&f->lk); + rw_exit(&fs->mutlk); + gefs_epochclean(); +// putfid(f); + gefs_freeamsg(am); + free(buf, M_TEMP, Kvmax); + return error; +} + +int +gefs_remove(void *v) +{ + struct vop_remove_args *ap = v; + return gefs_remove1(ap->a_dvp, ap->a_vp); +} + +int +gefs_rmdir(void *v) +{ + struct vop_rmdir_args *ap = v; + return gefs_remove1(ap->a_dvp, ap->a_vp); +} + +int +gefs_close(void *v) +{ + struct vop_read_args *ap = v; + struct vnode *vp = ap->a_vp; + + if(vp->v_op == &gefs_devops) + return spec_close(ap); + if(vp->v_op == &gefs_fifoops) + return fifo_close(ap); + return 0; +} + +int +gefs_access(void *v) +{ + return 0; +} + +int +gefs_writeb(Mount *mnt, Dent *dent, Msg *m, Bptr *ret, struct uio *uio, vlong o, vlong n, vlong sz) +{ + char buf[Kvmax]; + vlong fb, fo; + Blk *b, *t; + int seq; + Tree *r; + Bptr bp; + Kvp kv; + + fb = o & ~(Blksz-1); + fo = o & (Blksz-1); + + m->p.k.k[0] = Kdat; + PACK64(m->p.k.k+1, dent->d.qid.path); + PACK64(m->p.k.k+9, fb); + + if(fo+n >= Blksz) + seq = 1; + else + seq = 0; + r = agetp(&mnt->root); + b = gefs_newdblk(r, dent->d.qid.path, seq); +// if(waserror()){ +// freeblk(r, b); +// dropblk(b); +// nexterror(); +// } + t = nil; + if(btlookup(r, &m->p.k, &kv, buf, sizeof(buf)) == 0){ + bp = unpackbp(kv.v.v, kv.v.nv); + if(fb < sz && (fo != 0 || n != Blksz)){ + t = gefs_getblk(bp, GBraw); + if(t == nil){ + gefs_freeblk(r, b); + gefs_dropblk(b); + return -1; + } + memcpy(b->b->b_data, t->b->b_data, Blksz); + gefs_dropblk(t); + } + } + if(fo+n > Blksz) + n = Blksz-fo; + uiomove(b->b->b_data + fo, n, uio); + if(t == nil){ + if(fo > 0) + memset(b->b->b_data, 0, fo); + if(fo+n < Blksz) + memset(b->b->b_data+fo+n, 0, Blksz-fo-n); + } + gefs_enqueue(b); +// poperror(); + + packbp(m->p.v.v, m->p.v.nv, &b->bp); + *ret = b->bp; + gefs_dropblk(b); + return n; +} + +int +gefs_write(void *v) +{ + char *p, *kbuf, *vbuf, sbuf[Wstatmax]; + struct vop_write_args *ap = v; + struct vnode *vp = ap->a_vp; + struct uio *uio = ap->a_uio; + struct timespec now; + Dent *de; + Mount *mnt; + int e; + + vlong n, o, c, w, nkv; + ulong msz; + int i, j; + Bptr *bp; + Msg *kv; + Tree *t; + + if(atomic_load_int(&fs->rdonly)) + return EPERM; + de = vp->v_data; + if(de->d.flag & GEFS_FDEVNOD) + return spec_write(ap); + mnt = vp->v_mount->mnt_data; + nkv = howmany(uio->uio_resid, Blksz) + 2; + msz = nkv*(sizeof(Msg) + sizeof(Bptr) + Offksz + Ptrsz); + kv = emalloc(msz, 0); + bp = (Bptr*)(kv + nkv); + kbuf = (char*)(bp + nkv); + vbuf = kbuf + nkv*Offksz; + rw_enter(&fs->mutlk, RW_WRITE); + while(de->trunc) + rwsleep(&de->truncrz, &fs->mutlk, IPL_NONE, "truncate", 0); + rw_enter(&de->lk, RW_WRITE); + if(de->gone){ + e = EINVAL; + goto error; + } + + w = 0; + o = uio->uio_offset; + c = uio->uio_resid; + if(o < 0 || o >= (1ULL<<63) - c){ + e = ENOSPC; + goto error; + } + if(ap->a_ioflag & IO_APPEND) + o = de->d.length; + t = agetp(&mnt->root); + for(i = 0; c != 0; i++){ + assert(i < nkv); + assert(i == 0 || o%Blksz == 0); + kv[i].op = Oinsert; + kv[i].p.k.k = kbuf + i*Offksz; + kv[i].p.k.nk = Offksz; + kv[i].p.v.v = vbuf + i*Ptrsz; + kv[i].p.v.nv = Ptrsz; + n = gefs_writeb(mnt, de, &kv[i], &bp[i], uio, o, c, de->d.length); + if(n == -1){ + e = EIO; + goto ewrite; + } + w += n; + o += n; + c -= n; + } + + p = sbuf; + kv[i].op = Owstat; + kv[i].p.k = de->k; + *p++ = 0; + if(o > de->d.length){ + sbuf[0] |= Owsize; + PACK64(p, o); + p += 8; + de->d.length = o; + } + nanotime(&now); + sbuf[0] |= Owmtime; + de->d.mtime = SEC_TO_NSEC(now.tv_sec) + now.tv_nsec; + PACK64(p, de->d.mtime); + p += 8; + sbuf[0] |= Owmuid; + PACK32(p, de->d.uid); + p += 4; + + kv[i].p.v.v = sbuf; + kv[i].p.v.nv = p - sbuf; + if(gefs_upsert(mnt, kv, i+1) != 0){ + e = EIO; + goto ewrite; + } + + rw_exit(&de->lk); +// rw_exit(&f->lk); + rw_exit(&fs->mutlk); + gefs_epochclean(); + free(kv, M_TEMP, msz); + return 0; + +ewrite: + if(!fs->rdonly) + for(j = 0; j < i; j++) + gefs_freebp(t, bp[j]); +error: + rw_exit(&de->lk); +// rw_exit(&f->lk); + rw_exit(&fs->mutlk); + gefs_epochclean(); + free(kv, M_TEMP, msz); + return e; +} + +int +gefs_getattr(void *v) +{ + struct vop_getattr_args *ap = v; + struct vnode *vp = ap->a_vp; + struct vattr *vap = ap->a_vap; + Dent *dent = vp->v_data; + + gefs_epochstart(); + rw_enter(&dent->lk, RW_READ); + vattr_null(vap); + dir2statbuf(&dent->d, vap); + rw_exit(&dent->lk); + gefs_epochend(); + return 0; +} + +int +gefs_va2mode(int m) +{ + return m & 0777; +} + +int +gefs_setattr(void *v) +{ + struct vop_setattr_args *ap = v; + struct vnode *vp = ap->a_vp; +// struct denode *dep = VTODE(ap->a_vp); + struct vattr *vap = ap->a_vap; + struct ucred *cred = ap->a_cred; + char *opbuf; + char *p, /* *e,*/ *strs; + int op, error, mode; + Mount *mnt; + Dent *de; + Msg m; + Xdir n; + Tree *t; + +// *ao = nil; + error = 0; + de = vp->v_data; + mnt = vp->v_mount->mnt_data; + opbuf = emalloc(Kvmax, 1); + strs = emalloc(65536, 1); + rw_enter(&fs->mutlk, RW_WRITE); + while(de->trunc) + rwsleep(&de->truncrz, &fs->mutlk, IPL_NONE, "truncate", 0); + rw_enter(&de->lk, RW_WRITE); +// if(waserror()){ +// wunlock(de); +// nexterror(); +// } + if(de->gone){ + error = EBADF; + goto Error; + } + if(de->d.qid.path & Qmagic){ + error = EBADF; + goto Error; + } + if(de->d.qid.path & Qmagic){ + error = EPERM; + goto Error; + } + + t = agetp(&mnt->root); + n = de->d; + n.qid.vers++; + p = opbuf+1; + op = 0; + +// if(d.qid.path != ~0 || d.qid.vers != ~0 || d.qid.type != 0xff || d.type != 0xffff || d.dev != ~0) +// error(Ewstatq); + if(vap->va_size != VNOVAL){ + if(vap->va_size < 0 || (de->d.mode & GEFS_DMDIR) != 0){ + error = EPERM; + goto Error; + } + if(vap->va_size != de->d.length){ + if(vap->va_size < de->d.length){ +// *ao = emalloc(sizeof(Amsg), 1); + de->trunc = 1; +// atomic_add_int_nv(&de->ref, 1); +// atomic_add_int_nv(&f->mnt->ref, 1); +// (*ao)->op = AOclear; +// (*ao)->mnt = f->mnt; +// (*ao)->qpath = f->qpath; +// (*ao)->off = d.length; +// (*ao)->end = f->dent->length; +// (*ao)->dent = de; + } + de->d.length = vap->va_size; + n.length = vap->va_size; + op |= Owsize; + PACK64(p, n.length); + p += 8; + } + } + if(vap->va_mode != VNOVAL){ + mode = gefs_va2mode(vap->va_mode); + if((mode^de->d.mode) & GEFS_DMDIR){ + error = EPERM; + goto Error; + } + if(mode & ~(GEFS_DMDIR|0777)){ + error = EPERM; + goto Error; + } + if(mode != de->d.mode){ + n.mode = mode; + n.qid.type = mode>>24; + op |= Owmode; + PACK32(p, n.mode); + p += 4; + } + } + if((vap->va_vaflags & VA_UTIMES_CHANGE) || + vap->va_atime.tv_nsec != VNOVAL || + vap->va_mtime.tv_nsec != VNOVAL){ + n.mtime = vap->va_atime.tv_sec + vap->va_atime.tv_nsec*Nsec; + if(n.mtime != de->d.mtime){ + op |= Owmtime; + PACK64(p, n.mtime); + p += 8; + } + } +// if(*d.uid != '\0'){ +// if(strlen(d.uid) > Maxuname) +// error(Elength); +// rlock(&fs->userlk); +// u = name2user(d.uid); +// if(u == nil){ +// runlock(&fs->userlk); +// error(Enouser); +// } +// n.uid = u->id; +// runlock(&fs->userlk); +// if(n.uid != de->uid){ +// op |= Owuid; +// PACK32(p, n.uid); +// p += 4; +// } +// } +// if(*d.gid != '\0'){ +// if(strlen(d.gid) > Maxuname) +// error(Elength); +// rlock(&fs->userlk); +// u = name2user(d.gid); +// if(u == nil){ +// runlock(&fs->userlk); +// error(Enogrp); +// } +// n.gid = u->id; +// runlock(&fs->userlk); +// if(n.gid != de->gid){ +// op |= Owgid; +// PACK32(p, n.gid); +// p += 4; +// } +// } +// if(*d.muid != '\0') +// error(Eperm); +// if(nulldir && rename == 0){ +// *ao = emalloc(sizeof(Amsg), 1); +// (*ao)->op = AOsync; +// (*ao)->m = m; +// goto Noupdate; +// } + op |= Owmuid; + n.muid = cred->cr_uid; + PACK32(p, n.muid); + p += 4; + + if(op & Owsize){ + if(gefs_permok(vp, de->d.mode, de->d.uid, de->d.gid, GEFS_DMWRITE) == -1){ + error = EPERM; + goto Error; + } + } + if(op & (Owmode|Owmtime)){ + if(cred->cr_uid != de->d.uid && !groupleader(cred->cr_uid, de->d.gid)){ + error = EPERM; + goto Error; + } + } + if(op & Owgid){ + if(!(cred->cr_uid == de->d.uid && ingroup(cred->cr_uid, n.gid)) + && !(groupleader(cred->cr_uid, de->d.gid) && groupleader(cred->cr_uid, n.gid))){ + error = EPERM; + goto Error; + } + } + opbuf[0] = op; + m.op = Owstat; + m.p.k = de->k; + m.p.v.v = opbuf; + m.p.v.nv = p - opbuf; + gefs_upsert(mnt, &m, 1); + + de->d = n; + +//Noupdate: + error = 0; +Error: +// if(error != 0 && *ao != NULL){ +// freemsg(*ao); +// *ao = NULL; +// } + free(opbuf, M_TEMP, Kvmax); + rw_exit(&de->lk); + rw_exit(&fs->mutlk); + gefs_epochclean(); + return error; +} + +int +gefs_rename(void *v) +{ + struct vop_rename_args *ap = v; + struct vnode *fdvp = ap->a_fdvp; + struct vnode *fvp = ap->a_fvp; + struct vnode *tdvp = ap->a_tdvp; + struct vnode *tvp = ap->a_tvp; +// struct componentname *fn = ap->a_fcnp; + struct componentname *tn = ap->a_tcnp; + char *nbuf, *rnbuf, *upbuf; + int error; + Amsg *am; + Dent *de, *tde, *tpde; + Mount *mnt; + Xdir n; + Msg mb[6]; + Key k; + int nm; + + nm = 0; + am = NULL; + de = fvp->v_data; + tpde = tdvp->v_data; + mnt = fvp->v_mount->mnt_data; + if(tvp != NULL){ + tde = tvp->v_data; + mb[nm].op = Odelete; + mb[nm].p.k = tde->k; + mb[nm].p.v.v = nil; + mb[nm].p.v.nv = 0; + nm++; + + am = emalloc(sizeof(Amsg), 1); + atomic_add_int_nv(&mnt->ref, 1); + atomic_add_int_nv(&tde->ref, 1); + am->op = AOclear; + am->mnt = mnt; + am->qpath = tde->d.qid.path; + am->off = 0; + am->end = tde->d.length; + am->dent = tde; + } + + rw_enter(&fs->mutlk, RW_WRITE); + rw_enter(&de->lk, RW_WRITE); + + nbuf = malloc(tn->cn_namelen+1, M_TEMP, M_WAITOK); + rnbuf = malloc(Kvmax, M_TEMP, M_WAITOK); + upbuf = malloc(Kvmax, M_TEMP, M_WAITOK); + strlcpy(nbuf, tn->cn_nameptr, tn->cn_namelen+1); + n = de->d; + n.name = nbuf; + + mb[nm].op = Oclobber; + mb[nm].p.k = de->k; + mb[nm].p.v.v = nil; + mb[nm].p.v.nv = 0; + nm++; + + mb[nm].op = Oinsert; + dir2kv(tpde->d.qid.path, &n, &mb[nm].p, rnbuf, Kvmax); + k = mb[nm].p.k; + nm++; + + if(de->d.qid.type & GEFS_QTDIR){ + packsuper(upbuf, Kvmax, de->d.qid.path); + mb[nm].op = Oinsert; + mb[nm].p.k.k = upbuf; + mb[nm].p.k.nk = Upksz; + mb[nm].p.v.v = mb[nm-1].p.k.k; + mb[nm].p.v.nv = mb[nm-1].p.k.nk; + nm++; + } +// gefs_touch(f->dir, &mb[nm++]); + assert(nm < nelem(mb)); + error = gefs_upsert(mnt, mb, nm); + if(error == 0){ + gefs_cpkey(&de->k, &k, de->buf, sizeof(de->buf)); + de->d.name = de->buf + 11; + de->up = tpde->d.qid.path; + } + rw_exit(&de->lk); + rw_exit(&fs->mutlk); + gefs_epochclean(); + if(am != NULL){ + if(error == 0) + gefs_chsend(fs->admchan, am); + else + gefs_freeamsg(am); + } + free(nbuf, M_TEMP, tn->cn_namelen+1); + free(rnbuf, M_TEMP, Kvmax); + free(upbuf, M_TEMP, Kvmax); + if(tdvp == tvp) + vrele(tdvp); + else + vput(tdvp); + if(tvp != nil) + vput(tvp); + vrele(fdvp); + vrele(fvp); + return error; +} + +Blk* +gefs_readb(Tree *t, Dent *d, vlong o, vlong *fop) +{ + char buf[Offksz], kvbuf[Offksz+32]; + vlong fb; + Bptr bp; + Key k; + Kvp kv; + + fb = o & ~(Blksz-1); + *fop = o & (Blksz-1); + + k.k = buf; + k.nk = sizeof(buf); + k.k[0] = Kdat; + PACK64(k.k+1, d->d.qid.path); + PACK64(k.k+9, fb); + + if(btlookup(t, &k, &kv, kvbuf, sizeof(kvbuf)) != 0) + return nil; + + bp = unpackbp(kv.v.v, kv.v.nv); + return gefs_getblk(bp, GBraw); +} + +int +gefs_read1(struct vnode *vp, struct uio *uio) +{ + vlong n, c, o, fo; + Mount *mnt; + Dent *de; + Tree *t; + Blk *b; + int err; + + gefs_epochstart(); + de = vp->v_data; + err = 0; + mnt = vp->v_mount->mnt_data; + rw_enter(&de->lk, RW_READ); + if(uio->uio_offset >= de->d.length){ + rw_exit(&de->lk); + gefs_epochend(); + return 0; + } + t = mnt->root; + c = uio->uio_resid; + o = uio->uio_offset; + if(o + c > de->d.length) + c = de->d.length - o; + while(c > 0){ + b = gefs_readb(t, de, o, &fo); + n = Blksz - fo; + if(n > c) + n = c; + if(b == nil) + err = uiomove((char *)zeroblock + fo, n, uio); + else{ + err = uiomove(b->data + fo, n, uio); + gefs_dropblk(b); + } + if(err) + break; + o += n; + c -= n; + } + rw_exit(&de->lk); + gefs_epochend(); + return err; +} + + +int +gefs_read(void *v) +{ + struct vop_read_args *ap = v; + struct vnode *vp = ap->a_vp; + struct uio *uio = ap->a_uio; + + return gefs_read1(vp, uio); +} + +int +gefs_readdir(void *v) +{ + struct vop_readdir_args *ap = v; + struct vnode *vp = ap->a_vp; + struct uio *uio = ap->a_uio; + char pfx[Dpfxsz]; + char buf[512]; + int ns, nd, e = 0; + Mount *mnt; + Dent *dent; + Tree *t; + Scan *s; + + gefs_epochstart(); +// rw_enter(&f->lk, RW_WRITE); + e = 0; + mnt = vp->v_mount->mnt_data; + dent = vp->v_data; + rw_enter(&dent->lk, RW_WRITE); + s = dent->scan; + t = mnt->root; + + if (uio->uio_rw != UIO_READ) + return (EINVAL); + if(s == nil && uio->uio_offset != 0) + e = EINVAL; + if(s != nil && uio->uio_offset != 0 && uio->uio_offset != s->offset) + e = EINVAL; + if(e != 0) + goto Out; + if(uio->uio_offset == 0){ + s = malloc(sizeof(Scan), M_TEMP, M_WAITOK | M_ZERO); + packdkey(pfx, sizeof(pfx), dent->d.qid.path, nil, 0); + btnewscan(s, pfx, sizeof(pfx)); + free(dent->scan, M_TEMP, sizeof(Scan)); + dent->scan = s; + } + + if(s->donescan){ + *ap->a_eofflag = 1; + goto Out; + } + if(s->overflow){ + ns = kv2dirent(&s->kv, buf, sizeof(buf)); + if(ns <= 0 || ns > uio->uio_resid) + goto Out; + e = uiomove(buf, ns, uio); + if(e) + goto Out; + s->overflow = 0; + } + if((e = btenter(t, s)) != 0) + goto Out; + while(uio->uio_resid > 0){ + ns = btnext(s, &s->kv); + if(ns < 0){ + e = ENOMEM; + break; + } + if(ns == 0){ + *ap->a_eofflag = 1; + break; + } + nd = kv2dirent(&s->kv, buf, sizeof(buf)); + if(nd <= 0 || nd > uio->uio_resid){ + s->overflow = 1; + break; + } + e = uiomove(buf, nd, uio); + if(e) + break; + } + btexit(s); + s->offset = uio->uio_offset; +Out: + rw_exit(&dent->lk); + gefs_epochend(); + return e; +} + +int +gefs_fsync(void *v) +{ + Amsg *am; + + am = malloc(sizeof(Amsg), M_TEMP, M_WAITOK|M_ZERO); + am->op = AOsync; + rw_enter(&fs->quitlk, RW_WRITE); + fs->syncrz = -1; + gefs_chsend(fs->admchan, am); + while(fs->syncrz == -1) + rwsleep(&fs->syncrz, &fs->quitlk, IPL_NONE, "gefsquit", 0); + rw_exit(&fs->quitlk); + return fs->syncrz; +} + +int +gefs_inactive(void *v) +{ + struct vop_inactive_args *ap = v; + VOP_UNLOCK(ap->a_vp); + return 0; +} + +int +gefs_reclaim(void *v) +{ + struct vop_reclaim_args *ap = v; + struct vnode *vp = ap->a_vp; + Dent *dent = vp->v_data; + Mount *mnt = vp->v_mount->mnt_data; + + gefs_clunkdent(mnt, dent); + vp->v_data = NULL; + if(vp->v_op == &gefs_fifoops) + return fifo_reclaim(v); + return 0; +} + +const struct vops gefs_vops = { + .vop_lookup = gefs_lookup, + .vop_create = gefs_create, + .vop_mknod = gefs_mknod, + .vop_open = gefs_open, + .vop_close = gefs_close, + .vop_access = gefs_access, + .vop_getattr = gefs_getattr, + .vop_setattr = gefs_setattr, + .vop_read = gefs_read, + .vop_write = gefs_write, + .vop_ioctl = (void *)eopnotsupp, + .vop_kqfilter = (void *)eopnotsupp, + .vop_revoke = vop_generic_revoke, + .vop_fsync = gefs_fsync, + .vop_remove = gefs_remove, + .vop_link = (void *)eopnotsupp, + .vop_rename = gefs_rename, + .vop_mkdir = gefs_mkdir, + .vop_rmdir = gefs_rmdir, + .vop_symlink = gefs_symlink, + .vop_readdir = gefs_readdir, + .vop_readlink = gefs_readlink, + .vop_abortop = vop_generic_abortop, + .vop_inactive = gefs_inactive, + .vop_reclaim = gefs_reclaim, + .vop_lock = nullop, + .vop_unlock = nullop, + .vop_bmap = vop_generic_bmap, + .vop_strategy = (void *)eopnotsupp, + .vop_print = NULL, + .vop_islocked = nullop, + .vop_pathconf = (void *)eopnotsupp, + .vop_advlock = (void *)eopnotsupp, + .vop_bwrite = vop_generic_bwrite, +}; + +const struct vops gefs_devops = { + .vop_close = gefs_close, + .vop_access = gefs_access, + .vop_getattr = gefs_getattr, + .vop_setattr = gefs_setattr, + .vop_fsync = gefs_fsync, + .vop_inactive = gefs_inactive, + .vop_reclaim = gefs_reclaim, + .vop_lock = nullop, + .vop_unlock = nullop, + .vop_print = NULL, + .vop_islocked = nullop, + + /* XXX: Keep in sync with spec_vops */ + .vop_lookup = vop_generic_lookup, + .vop_create = vop_generic_badop, + .vop_mknod = vop_generic_badop, + .vop_open = spec_open, + .vop_ioctl = spec_ioctl, + .vop_read = spec_read, + .vop_write = spec_write, + .vop_kqfilter = spec_kqfilter, + .vop_revoke = vop_generic_revoke, + .vop_remove = vop_generic_badop, + .vop_link = vop_generic_badop, + .vop_rename = vop_generic_badop, + .vop_mkdir = vop_generic_badop, + .vop_rmdir = vop_generic_badop, + .vop_symlink = vop_generic_badop, + .vop_readdir = vop_generic_badop, + .vop_readlink = vop_generic_badop, + .vop_abortop = vop_generic_badop, + .vop_bmap = vop_generic_bmap, + .vop_strategy = spec_strategy, + .vop_pathconf = spec_pathconf, + .vop_advlock = spec_advlock, + .vop_bwrite = vop_generic_bwrite, +}; + +const struct vops gefs_fifoops = { + .vop_close = gefs_close, + .vop_access = gefs_access, + .vop_getattr = gefs_getattr, + .vop_setattr = gefs_setattr, + .vop_fsync = gefs_fsync, + .vop_inactive = gefs_inactive, + .vop_reclaim = gefs_reclaim, + .vop_lock = nullop, + .vop_unlock = nullop, + .vop_print = NULL, + .vop_islocked = nullop, + + /* XXX: Keep in sync with fifo_vops */ + .vop_lookup = vop_generic_lookup, + .vop_create = vop_generic_badop, + .vop_mknod = vop_generic_badop, + .vop_read = fifo_read, + .vop_write = fifo_write, + .vop_open = fifo_open, + .vop_ioctl = fifo_ioctl, + .vop_kqfilter = fifo_kqfilter, + .vop_revoke = vop_generic_revoke, + .vop_remove = vop_generic_badop, + .vop_link = vop_generic_badop, + .vop_rename = vop_generic_badop, + .vop_mkdir = vop_generic_badop, + .vop_rmdir = vop_generic_badop, + .vop_symlink = vop_generic_badop, + .vop_readdir = vop_generic_badop, + .vop_readlink = vop_generic_badop, + .vop_abortop = vop_generic_badop, + .vop_bmap = vop_generic_bmap, + .vop_strategy = vop_generic_badop, + .vop_pathconf = fifo_pathconf, + .vop_advlock = fifo_advlock +}; diff --git a/sys/kern/vfs_init.c b/sys/kern/vfs_init.c index 8d31047ac94..f70d32545a5 100644 --- a/sys/kern/vfs_init.c +++ b/sys/kern/vfs_init.c @@ -76,6 +76,11 @@ static struct vfsconf vfsconflist[] = { sizeof(struct msdosfs_args) }, #endif +#ifdef GEFS + { &gefs_vfsops, MOUNT_GEFS, 4, 0, MNT_LOCAL | MNT_SWAPPABLE, + sizeof(struct gefs_args) }, +#endif + #ifdef NFSCLIENT { &nfs_vfsops, MOUNT_NFS, 2, 0, MNT_SWAPPABLE, sizeof(struct nfs_args) }, diff --git a/sys/sys/mount.h b/sys/sys/mount.h index e765164b704..4ab77c123a1 100644 --- a/sys/sys/mount.h +++ b/sys/sys/mount.h @@ -198,6 +198,14 @@ struct msdosfs_args { #define MSDOSFSMNT_LONGNAME 0x02 /* Force Win'95 long names */ #define MSDOSFSMNT_NOWIN95 0x04 /* Completely ignore Win95 entries */ +/* + * Arguments to mount GEFS filesystems + */ +struct gefs_args { + char *fspec; /* block special device to mount */ + struct export_args export_info;/* network export information */ +}; + /* * Arguments to mount ntfs filesystems */ @@ -273,6 +281,7 @@ union mount_info { struct msdosfs_args msdosfs_args; struct ntfs_args ntfs_args; struct tmpfs_args tmpfs_args; + struct gefs_args gefs_args; char __align[160]; /* 64-bit alignment and room to grow */ }; @@ -317,6 +326,7 @@ struct statfs { #define MOUNT_NFS "nfs" /* Network Filesystem */ #define MOUNT_MFS "mfs" /* Memory Filesystem */ #define MOUNT_MSDOS "msdos" /* MSDOS Filesystem */ +#define MOUNT_GEFS "gefs" /* GEFS Filesystem */ #define MOUNT_AFS "afs" /* Andrew Filesystem */ #define MOUNT_CD9660 "cd9660" /* ISO9660 (aka CDROM) Filesystem */ #define MOUNT_EXT2FS "ext2fs" /* Second Extended Filesystem */ @@ -548,6 +558,7 @@ struct vfsops { extern const struct vfsops ffs_vfsops; extern const struct vfsops mfs_vfsops; extern const struct vfsops msdosfs_vfsops; +extern const struct vfsops gefs_vfsops; extern const struct vfsops nfs_vfsops; extern const struct vfsops cd9660_vfsops; extern const struct vfsops ext2fs_vfsops; diff --git a/sys/sys/vnode.h b/sys/sys/vnode.h index 6f1664ce18e..0aaa9eb6924 100644 --- a/sys/sys/vnode.h +++ b/sys/sys/vnode.h @@ -67,6 +67,7 @@ enum vtagtype { VT_NON, VT_UFS, VT_NFS, VT_MFS, VT_MSDOSFS, VT_PORTAL, VT_PROCFS, VT_AFS, VT_ISOFS, VT_ADOSFS, VT_EXT2FS, VT_VFS, VT_NTFS, VT_UDF, VT_FUSEFS, VT_TMPFS, + VT_GEFS, }; #define VTAG_NAMES \