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 '' | ||