Unified · Split
| @@ -0,0 +1,20 @@ | ||
| 1 | +libblockalloc -- block allocation library and ftrace-lite | |
| 2 | + | |
| 3 | +libblockalloc is a library that allows you to basically malloc and | |
| 4 | +free blocks on a disk. It is able to sync data, keep track of the | |
| 5 | +used and free blocks, and naively cache recently used data. This | |
| 6 | +library is incomplete (needs a read, write, arb free, and arb malloc) | |
| 7 | +but still might be useful. | |
| 8 | + | |
| 9 | +ftrace-lite is a quick hack to allow libblockalloc to compile on all | |
| 10 | +plan 9/9front systems. ftrace-lite, when enabled, prints out some of | |
| 11 | +whats happening inside of the library to help with debugging and | |
| 12 | +streamlining. It isn't as good as the full ftrace (ftrace-lite isn't | |
| 13 | +able to follow system calls into the kernel or blurt out mallocs and | |
| 14 | +frees, only covers qlock, etc...) but it does some of the job. | |
| 15 | + | |
| 16 | +I didn't really test this library at all before releasing it. When | |
| 17 | +you find bugs, bitch at me and mail me a patch. | |
| 18 | + | |
| 19 | +Love, | |
| 20 | +Veety | |
| @@ -0,0 +1,73 @@ | ||
| 1 | +#pragma lib "libblockalloc.a" | |
| 2 | + | |
| 3 | +#define BPTRSIZE 9 // this is bytes | |
| 4 | +#define SYNCSLEEP 300 // in seconds | |
| 5 | + | |
| 6 | +enum { | |
| 7 | + DISKCACHE = 1, | |
| 8 | + DISKAUTOSYNC = 2, | |
| 9 | + DISKCONSTSYNC = 4, | |
| 10 | + DISKDEBUG = 8, | |
| 11 | + DISKMAN = 3, | |
| 12 | +}; | |
| 13 | + | |
| 14 | +typedef struct Blockptr Blockptr; | |
| 15 | +typedef struct Metablock Metablock; | |
| 16 | +typedef struct Disk Disk; | |
| 17 | +typedef struct Fn Fn; | |
| 18 | + | |
| 19 | +struct Fn { | |
| 20 | + char *name; | |
| 21 | + QLock l; | |
| 22 | + uint active; | |
| 23 | +}; | |
| 24 | + | |
| 25 | +struct Blockptr { | |
| 26 | + uchar used; // is the block in use? | |
| 27 | + u64int offset; // where is it from the start of the bitmap? | |
| 28 | + void *buffer; // buffer for sync if blocks are cached | |
| 29 | +}; | |
| 30 | + | |
| 31 | +struct Metablock { // information about the managed disk | |
| 32 | + u64int blocksize; // how big is a block in the bitmap | |
| 33 | + u64int ptrmapstart; // where are the pointers? | |
| 34 | + u64int totalblocks; // how many blocks are there? | |
| 35 | + u64int bitmapstart; // where are the blocks? | |
| 36 | +}; | |
| 37 | + | |
| 38 | +struct Disk { | |
| 39 | + int fd; // rw channel | |
| 40 | + Metablock *meta; // the metadata block | |
| 41 | + uint blocks_cached; // are the blocks cached? | |
| 42 | + int syncpid; | |
| 43 | + int debug; | |
| 44 | + QLock syncing; | |
| 45 | + QLock synclock; | |
| 46 | + Blockptr *ptrs; // blockpointers if they're cached | |
| 47 | +}; | |
| 48 | + | |
| 49 | +/* pointers.c */ | |
| 50 | +u64int preadn(int,void*,u64int,u64int); // pread + readn | |
| 51 | +u64int pwriten(int, void*,u64int,u64int); // wrapper around pwrite to make it match preadn | |
| 52 | +int loadbptrs(Disk*); | |
| 53 | +int syncbptrs(Disk*); | |
| 54 | +u64int readbptr(Disk*, Blockptr*, u64int); | |
| 55 | +u64int writebptr(Disk*, Blockptr*); | |
| 56 | +u64int allocblock(Disk*,Blockptr*); | |
| 57 | +int freeblock(Disk*,Blockptr*); | |
| 58 | +u64int readblock(Disk*,Blockptr*,void*); | |
| 59 | +u64int writeblock(Disk*,Blockptr*,void*); | |
| 60 | + | |
| 61 | +/* disks.c */ | |
| 62 | +int opendisk(char*,Disk*,uchar); | |
| 63 | +int closedisk(Disk*); | |
| 64 | +int fd2disk(int,Disk*,uchar); | |
| 65 | +int disksyncer(Disk*); | |
| 66 | + | |
| 67 | +/* instru.c */ | |
| 68 | +void enableinstru(void); | |
| 69 | +Fn* infn(char*); | |
| 70 | +void action(Fn*,char*); | |
| 71 | +void outfn(Fn*); | |
| 72 | +void iqlock(Fn*,QLock*); | |
| 73 | +void iqunlock(Fn*,QLock*); | |
| @@ -0,0 +1,116 @@ | ||
| 1 | +#include <u.h> | |
| 2 | +#include <libc.h> | |
| 3 | +#include <blockalloc.h> | |
| 4 | + | |
| 5 | +void | |
| 6 | +disksyncer1(Disk *d) | |
| 7 | +{ | |
| 8 | + Fn *f = infn("disk syncer"); | |
| 9 | + for(;;){ | |
| 10 | + sleep(SYNCSLEEP*1000); | |
| 11 | + iqlock(f,&d->syncing); | |
| 12 | + if(!syncbptrs(d)) | |
| 13 | + if(d->debug) | |
| 14 | + print("blockalloc: error syncing block pointers\n"); | |
| 15 | + iqunlock(f,&d->syncing); | |
| 16 | + } | |
| 17 | +} | |
| 18 | + | |
| 19 | +int | |
| 20 | +disksyncer(Disk* d) | |
| 21 | +{ | |
| 22 | + int pid; | |
| 23 | + if((pid = rfork(RFPROC|RFMEM|RFNOWAIT)) == 0){ | |
| 24 | + disksyncer1(d); | |
| 25 | + } else if (pid > 0) { | |
| 26 | + d->syncpid = pid; | |
| 27 | + return 0; | |
| 28 | + } else { | |
| 29 | + return -1; | |
| 30 | + } | |
| 31 | + return -1; | |
| 32 | +} | |
| 33 | + | |
| 34 | +int | |
| 35 | +opendisk(char* fname, Disk* d, uchar opts) | |
| 36 | +{ | |
| 37 | + int fd; | |
| 38 | + if((fd = open(fname,OREAD|OWRITE)) < 0){ | |
| 39 | + d = nil; | |
| 40 | + return -1; | |
| 41 | + } | |
| 42 | + return fd2disk(fd,d,opts); | |
| 43 | +} | |
| 44 | + | |
| 45 | +int | |
| 46 | +closedisk(Disk* d) | |
| 47 | +{ | |
| 48 | + if(d->blocks_cached){ | |
| 49 | + qlock(&d->syncing); | |
| 50 | + syncbptrs(d); | |
| 51 | + if(postnote(PNPROC,d->syncpid,"fucking die you cunt") == 0){ | |
| 52 | + free(d->ptrs); | |
| 53 | + free(d->meta); | |
| 54 | + close(d->fd); | |
| 55 | + qunlock(&d->syncing); | |
| 56 | + free(d); | |
| 57 | + return 0; | |
| 58 | + } else { | |
| 59 | + print("blockalloc: unable to kill syncer process %d\n",d->syncpid); | |
| 60 | + free(d->ptrs); | |
| 61 | + free(d->meta); | |
| 62 | + close(d->fd); | |
| 63 | + return -1; | |
| 64 | + } | |
| 65 | + } else { | |
| 66 | + if(d->ptrs != nil) | |
| 67 | + free(d->ptrs); | |
| 68 | + free(d->meta); | |
| 69 | + close(d->fd); | |
| 70 | + free(d); | |
| 71 | + return 0; | |
| 72 | + } | |
| 73 | + return -2; | |
| 74 | +} | |
| 75 | + | |
| 76 | +int | |
| 77 | +fd2disk(int fd, Disk* d, uchar opts) | |
| 78 | +{ | |
| 79 | + Fn *f = infn("fd2disk"); | |
| 80 | + Disk *bf = malloc(sizeof(Disk)); | |
| 81 | + Metablock *m = malloc(sizeof(Metablock)); | |
| 82 | + bf->fd = fd; | |
| 83 | + action(f,"preadn 1"); | |
| 84 | + if((preadn(bf->fd,m,2,sizeof(Metablock))) < sizeof(Metablock)){ | |
| 85 | + action(f,"short read"); | |
| 86 | + free(m); | |
| 87 | + close(bf->fd); | |
| 88 | + free(bf); | |
| 89 | + d = nil; | |
| 90 | + outfn(f); | |
| 91 | + return -2; | |
| 92 | + } | |
| 93 | + bf->meta = m; | |
| 94 | + if(opts == 1 || opts%2 == 1){ | |
| 95 | + action(f,"enabling disk caching"); | |
| 96 | + bf->blocks_cached = 1; | |
| 97 | + if(loadbptrs(bf) == 0){ | |
| 98 | + if(disksyncer(bf) == 0){ | |
| 99 | + d = bf; | |
| 100 | + outfn(f); | |
| 101 | + return 0; | |
| 102 | + } | |
| 103 | + action(f,"syncer did not start (rfork error)"); | |
| 104 | + free(d->ptrs); | |
| 105 | + bf->blocks_cached = 0; | |
| 106 | + d = bf; | |
| 107 | + outfn(f); | |
| 108 | + return -3; | |
| 109 | + } | |
| 110 | + action(f,"failure to enable caching"); | |
| 111 | + } | |
| 112 | + d = bf; | |
| 113 | + outfn(f); | |
| 114 | + return 0; | |
| 115 | +} | |
| 116 | + | |
| @@ -0,0 +1,91 @@ | ||
| 1 | +#include <u.h> | |
| 2 | +#include <libc.h> | |
| 3 | +#include <blockalloc.h> | |
| 4 | + | |
| 5 | +/* | |
| 6 | +simple instrumentation library | |
| 7 | +This is basically the ftrace-lite library. It doesn't have | |
| 8 | +all the cool features or anything like that, but it kinda | |
| 9 | +does the same thing. | |
| 10 | +*/ | |
| 11 | + | |
| 12 | +int enable_instru = 0; | |
| 13 | + | |
| 14 | +void | |
| 15 | +initinstru(void) | |
| 16 | +{ | |
| 17 | + enable_instru = 1; | |
| 18 | +} | |
| 19 | + | |
| 20 | +Fn* | |
| 21 | +infn(char* name) | |
| 22 | +{ | |
| 23 | + Fn *bf = malloc(sizeof(Fn)); | |
| 24 | + if(enable_instru){ | |
| 25 | + bf->active = 1; | |
| 26 | + qlock(&bf->l); | |
| 27 | + bf->name = name; | |
| 28 | + fprint(2,"instru: infn: %s\n",name); | |
| 29 | + return bf; | |
| 30 | + } | |
| 31 | + free(bf); | |
| 32 | + return nil; | |
| 33 | +} | |
| 34 | + | |
| 35 | +void | |
| 36 | +action(Fn* f, char* c) | |
| 37 | +{ | |
| 38 | + if(f == nil || enable_instru == 0){ | |
| 39 | + return; | |
| 40 | + } else if (f->active == 1 && !canqlock(&f->l)){ | |
| 41 | + fprint(2,"instru: %s: %s\n",f->name,c); | |
| 42 | + } else if (canqlock(&f->l)) { | |
| 43 | + fprint(2,"instru: error: action %s done outside of function\n",c); | |
| 44 | + } | |
| 45 | +} | |
| 46 | + | |
| 47 | +void | |
| 48 | +outfn(Fn* f) | |
| 49 | +{ | |
| 50 | + if(f == nil || enable_instru == 0){ | |
| 51 | + return; | |
| 52 | + } else if (f->active == 1 && !canqlock(&f->l)){ | |
| 53 | + fprint(2,"instru: outfn: %s\n",f->name); | |
| 54 | + qunlock(&f->l); | |
| 55 | + free(f->name); | |
| 56 | + free(f); | |
| 57 | + return; | |
| 58 | + } else { | |
| 59 | + if(f != nil){ | |
| 60 | + fprint(2,"instru: error: outfn done outside of function\n"); | |
| 61 | + if(!canqlock(&f->l)) | |
| 62 | + qunlock(&f->l); | |
| 63 | + free(f->name); | |
| 64 | + free(f); | |
| 65 | + return; | |
| 66 | + } | |
| 67 | + fprint(2,"instru: error: something bad happened\n"); | |
| 68 | + } | |
| 69 | +} | |
| 70 | + | |
| 71 | +void | |
| 72 | +iqlock(Fn* f, QLock* l) | |
| 73 | +{ | |
| 74 | + if(f == nil || enable_instru == 0){ | |
| 75 | + qlock(l); | |
| 76 | + } else if (f->active == 1 && !canqlock(&f->l)){ | |
| 77 | + qlock(l); | |
| 78 | + fprint(2,"instru: %s: lock: %p\n",f->name,l); | |
| 79 | + } | |
| 80 | +} | |
| 81 | + | |
| 82 | +void | |
| 83 | +iqunlock(Fn* f,QLock* l) | |
| 84 | +{ | |
| 85 | + if(f == nil || enable_instru == 0){ | |
| 86 | + qlock(l); | |
| 87 | + } else if (f->active == 1 && !canqlock(&f->l)){ | |
| 88 | + qlock(l); | |
| 89 | + fprint(2,"instru: %s: unlock: %p\n",f->name,l); | |
| 90 | + } | |
| 91 | +} | |
| @@ -0,0 +1,26 @@ | ||
| 1 | +</$objtype/mkfile | |
| 2 | + | |
| 3 | +LIB=/$objtype/lib/libblockalloc.a | |
| 4 | + | |
| 5 | +OFILES=\ | |
| 6 | + disk.$O \ | |
| 7 | + pointers.$O \ | |
| 8 | + instru.$O \ | |
| 9 | + | |
| 10 | +CFILES=\ | |
| 11 | + disk.c \ | |
| 12 | + pointers.c \ | |
| 13 | + instru.c \ | |
| 14 | + | |
| 15 | +HFILES=\ | |
| 16 | + blockalloc.h \ | |
| 17 | + | |
| 18 | +UPDATE=\ | |
| 19 | + mkfile \ | |
| 20 | + $LIB \ | |
| 21 | + $CFILES \ | |
| 22 | + $HFILES \ | |
| 23 | + | |
| 24 | +all:V: $LIB | |
| 25 | + | |
| 26 | +</sys/src/cmd/mksyslib | |
| @@ -0,0 +1,209 @@ | ||
| 1 | +#include <u.h> | |
| 2 | +#include <libc.h> | |
| 3 | +#include <blockalloc.h> | |
| 4 | + | |
| 5 | +u64int | |
| 6 | +preadn(int fd, void *buf, u64int offset, u64int len) | |
| 7 | +{ | |
| 8 | + int nfd = dup(fd,-1); | |
| 9 | + seek(nfd, (vlong) offset, 0); | |
| 10 | + u64int retval = (u64int) readn(nfd, buf, (vlong) len); | |
| 11 | + close(nfd); | |
| 12 | + return retval; | |
| 13 | +} | |
| 14 | + | |
| 15 | +u64int | |
| 16 | +pwriten(int fd, void *buf, u64int offset, u64int len) | |
| 17 | +{ | |
| 18 | + return (u64int)pwrite(fd,buf,(long) len, (vlong) offset); | |
| 19 | +} | |
| 20 | + | |
| 21 | +int | |
| 22 | +loadbptrs(Disk *d) | |
| 23 | +{ | |
| 24 | + Fn *f = infn("loadbptrs"); | |
| 25 | + int fd = d->fd; | |
| 26 | + Metablock *m = d->meta; | |
| 27 | + u64int ptrbitmapsize = m->totalblocks*BPTRSIZE; | |
| 28 | + action(f,"preadn 1"); | |
| 29 | + if(preadn(fd, d->ptrs, m->ptrmapstart, ptrbitmapsize) < ptrbitmapsize){ | |
| 30 | + action(f,"failure"); | |
| 31 | + d->blocks_cached = 0; | |
| 32 | + outfn(f); | |
| 33 | + return -1; | |
| 34 | + } | |
| 35 | + d->blocks_cached = 1; | |
| 36 | + outfn(f); | |
| 37 | + return 0; | |
| 38 | +} | |
| 39 | + | |
| 40 | +int | |
| 41 | +syncbptrs(Disk *d) | |
| 42 | +{ | |
| 43 | + Fn *f = infn("syncbptrs"); | |
| 44 | + iqlock(f,&d->synclock); | |
| 45 | + if(d->blocks_cached){ | |
| 46 | + for(u64int i = 0; i < d->meta->totalblocks; i++){ | |
| 47 | + writebptr(d,&d->ptrs[i]); | |
| 48 | + if(&d->ptrs[i].buffer != nil){ | |
| 49 | + writeblock(d,&d->ptrs[i],&d->ptrs[i].buffer); | |
| 50 | + free(&d->ptrs[i].buffer); | |
| 51 | + } | |
| 52 | + } | |
| 53 | + iqunlock(f,&d->synclock); | |
| 54 | + outfn(f); | |
| 55 | + return 0; | |
| 56 | + } | |
| 57 | + iqunlock(f,&d->synclock); | |
| 58 | + outfn(f); | |
| 59 | + return -1; | |
| 60 | +} | |
| 61 | + | |
| 62 | +u64int | |
| 63 | +readbptr(Disk *d, Blockptr *b, u64int block) | |
| 64 | +{ | |
| 65 | + Fn *f = infn("readbptr"); | |
| 66 | + if(d->blocks_cached){ | |
| 67 | + iqlock(f,&d->synclock); | |
| 68 | + b = &d->ptrs[block]; | |
| 69 | + iqunlock(f,&d->synclock); | |
| 70 | + outfn(f); | |
| 71 | + return BPTRSIZE; | |
| 72 | + } else { | |
| 73 | + action(f,"preadn 1"); | |
| 74 | + u64int retval = preadn(d->fd, b, d->meta->ptrmapstart+(BPTRSIZE*block),BPTRSIZE); | |
| 75 | + b->buffer = nil; | |
| 76 | + outfn(f); | |
| 77 | + return retval; | |
| 78 | + } | |
| 79 | +} | |
| 80 | + | |
| 81 | +u64int | |
| 82 | +writebptr(Disk *d, Blockptr *b) | |
| 83 | +{ | |
| 84 | + Fn *f = infn("writebptr"); | |
| 85 | + Metablock *m = d->meta; | |
| 86 | + u64int blocksize = m->blocksize; | |
| 87 | + u64int blockaddr = b->offset/blocksize; | |
| 88 | + action(f,"pwriten 1 (retval)"); | |
| 89 | + outfn(f); | |
| 90 | + return pwriten(d->fd,b,m->ptrmapstart+(BPTRSIZE*blockaddr),BPTRSIZE); | |
| 91 | +} | |
| 92 | + | |
| 93 | +u64int | |
| 94 | +allocblock(Disk *d, Blockptr *b) | |
| 95 | +{ | |
| 96 | + Fn *f = infn("allocblock"); | |
| 97 | + if(d->blocks_cached){ | |
| 98 | + iqlock(f,&d->synclock); | |
| 99 | + for(u64int i = 0; i < d->meta->totalblocks; i++){ | |
| 100 | + if(d->ptrs[i].used == 0){ | |
| 101 | + b = &d->ptrs[i]; | |
| 102 | + iqunlock(f,&d->synclock); | |
| 103 | + outfn(f); | |
| 104 | + return i+1; | |
| 105 | + } | |
| 106 | + } | |
| 107 | + iqunlock(f,&d->synclock); | |
| 108 | + action(f,"retval -1"); | |
| 109 | + outfn(f); | |
| 110 | + return -1; | |
| 111 | + } else { | |
| 112 | + Blockptr *bf = malloc(sizeof(Blockptr)); | |
| 113 | + action(f,smprint("allocated %p",bf)); | |
| 114 | + for(u64int i = 0 ; i < d->meta->totalblocks; i++){ | |
| 115 | + preadn(d->fd,bf,d->meta->ptrmapstart+(i*BPTRSIZE),BPTRSIZE); | |
| 116 | + if(bf->used == 0){ | |
| 117 | + b = bf; | |
| 118 | + outfn(f); | |
| 119 | + return i; | |
| 120 | + } | |
| 121 | + } | |
| 122 | + action(f,smprint("freeing %p",bf)); | |
| 123 | + free(bf); | |
| 124 | + b = nil; | |
| 125 | + outfn(f); | |
| 126 | + return 0; | |
| 127 | + } | |
| 128 | + action(f,"retval -1"); | |
| 129 | + outfn(f); | |
| 130 | + return -1; | |
| 131 | +} | |
| 132 | + | |
| 133 | +int | |
| 134 | +freeblock(Disk *d, Blockptr *b) | |
| 135 | +{ | |
| 136 | + Fn *f = infn("freeblock"); | |
| 137 | + if(d->blocks_cached){ | |
| 138 | + iqlock(f,&d->synclock); | |
| 139 | + b->used = 0; | |
| 140 | + iqunlock(f,&d->synclock); | |
| 141 | + outfn(f); | |
| 142 | + return 0; | |
| 143 | + } else { | |
| 144 | + b->used = 0; | |
| 145 | + for(int i = 0; i < 10 ; i++){ | |
| 146 | + if(writebptr(d,b) >= BPTRSIZE){ | |
| 147 | + outfn(f); | |
| 148 | + return 0; | |
| 149 | + } | |
| 150 | + } | |
| 151 | + } | |
| 152 | + action(f,"retval -1"); | |
| 153 | + outfn(f); | |
| 154 | + return -1; | |
| 155 | +} | |
| 156 | + | |
| 157 | +u64int | |
| 158 | +readblock(Disk *d, Blockptr *b, void *buf) | |
| 159 | +{ | |
| 160 | + Fn *f = infn("readblock"); | |
| 161 | + int fd = d->fd; | |
| 162 | + u64int bmapstart = d->meta->bitmapstart; | |
| 163 | + u64int blocksize = d->meta->blocksize; | |
| 164 | + u64int offset = b->offset+d->meta->blocksize; | |
| 165 | + iqlock(f,&d->synclock); | |
| 166 | + if(d->blocks_cached && b->buffer != nil){ | |
| 167 | + buf = b->buffer; | |
| 168 | + iqunlock(f,&d->synclock); | |
| 169 | + outfn(f); | |
| 170 | + return blocksize; | |
| 171 | + } else if (d->blocks_cached && b->buffer == nil){ | |
| 172 | + u64int retval = preadn(fd,b->buffer,offset,blocksize); | |
| 173 | + buf = b->buffer; | |
| 174 | + iqunlock(f,&d->synclock); | |
| 175 | + outfn(f); | |
| 176 | + return retval; | |
| 177 | + } else { | |
| 178 | + iqunlock(f,&d->synclock); | |
| 179 | + outfn(f); | |
| 180 | + return preadn(fd,buf,offset,blocksize); | |
| 181 | + } | |
| 182 | + outfn(f); | |
| 183 | + return 0; | |
| 184 | +} | |
| 185 | + | |
| 186 | +u64int | |
| 187 | +writeblock(Disk *d, Blockptr *b, void *buf) | |
| 188 | +{ | |
| 189 | + Fn *f = infn("writeblock"); | |
| 190 | + int fd = d->fd; | |
| 191 | + int syncing = 0; | |
| 192 | + if(canqlock(&d->synclock)){ | |
| 193 | + syncing = 1; | |
| 194 | + iqlock(f,&d->synclock); | |
| 195 | + } | |
| 196 | + u64int bmapstart = d->meta->bitmapstart; | |
| 197 | + u64int blocksize = d->meta->blocksize; | |
| 198 | + u64int offset = b->offset+d->meta->blocksize; | |
| 199 | + if(d->blocks_cached && !syncing){ | |
| 200 | + memcpy(buf,b->buffer,blocksize); | |
| 201 | + iqunlock(f,&d->synclock); | |
| 202 | + action(f,"return (not syncing)"); | |
| 203 | + outfn(f); | |
| 204 | + return blocksize; | |
| 205 | + } | |
| 206 | + action(f,"return (syncing)"); | |
| 207 | + outfn(f); | |
| 208 | + return pwriten(fd,buf,offset,blocksize); | |
| 209 | +} | |
| @@ -0,0 +1,4 @@ | ||
| 1 | +#!/bin/rc | |
| 2 | +echo 'cp blockalloc.h /sys/include/blockalloc.h' | |
| 3 | +cp blockalloc.h /sys/include/blockalloc.h | |
| 4 | +exit '' | |