/* * nubfs, part 3: Extents * * based on "Disk file allocation based on the buddy system", Philip D L Koch, * ACM Transactions on Computer Systems, Vol. 5, No. 4, November 1987, pp 352-370. * (Koch describes the algorithms and performance of the DTSS disk file subsystem.) */ #include "dat.h" #include "fns.h" enum{ Nslice= 32, }; typedef struct Slice Slice; struct Slice { u64int addr; /* relative to Disk.base */ Slice* next; }; typedef struct Disk Disk; struct Disk { int fd; u64int base; uint secsize; uint secshift; Slice* slices[Nslice]; Slice* freeslices; }; /* * log2 */ static uchar log2v[256]; static void loginit(void) { int i; for(i=2; i round up */ if((n>>8) == 0) return log2v[n] + r; if((n>>16) == 0) return 8 + log2v[n>>8] + r; if((n>>24) == 0) return 16 + log2v[n>>16] + r; return 24 + log2v[n>>24] + r; } static void freeslice(Disk*, u64int, u32int); static void freeslices(Disk*, u64int, u32int); Disk* diskinit(int fd, uint secsize, u64int base, u32int length) { Disk *disk; loginit(); disk = emallocz(sizeof(*disk), 1); disk->fd = fd; disk->base = base; disk->secsize = secsize; disk->secshift = log2of(secsize); freeslices(disk, 0, length>>disk->secshift); return disk; } /* * return the smallest block of at least size bytes, * splitting a much larger available block into smaller ones, if required. */ Extent allocdisk(Disk *disk, u32int size) { Slice *s; u64int addr; uint n0, n; size = (size + disk->secsize-1) >> disk->secshift; n0 = log2of(size); for(n = n0; n < Nslice; n++){ size = (u32int)1<slices[n]) != nil){ disk->slices[n] = s->next; addr = s->addr; s->next = disk->freeslices; disk->freeslices = s; for(; n > n0; n--){ size >>= 1; freeslice(disk, addr+size, size); } return (Extent){addr<secshift, size<secshift}; } } return (Extent){0, 0}; } Extent allocdiskat(Disk *disk, u64int reqaddr, u32int size) { Slice *s, **l; u64int addr, avail; uint n0, n; size = (size + disk->secsize-1) >> disk->secshift; reqaddr >>= disk->secshift; n0 = log2of(size); for(n = n0; n < Nslice; n++){ size = (u32int)1<slices[n]; (s = *l) != nil; l = &s->next) if(s->addr <= reqaddr && reqaddr < s->addr+size) break; if(*l != nil){ *l = s->next; addr = s->addr; s->next = disk->freeslices; disk->freeslices = s; if(addr != reqaddr) freeslices(disk, addr, reqaddr-addr); if(n != n0){ avail = reqaddr+(1<secshift, size<secshift}; } } return (Extent){0, 0}; } void freedisk(Disk *disk, Extent ext) { freeslice(disk, ext.base>>disk->secshift, ext.length>>disk->secshift); } static void freeslices(Disk *disk, u64int addr, u32int size) { uint i; u32int m; DBG('d')print("freeslices %#llux %ud\n", addr, size); /* align the address */ for(i=0; i<32; i++){ m = (u32int)1<>= 1){ if((size & m) != 0){ freeslice(disk, addr, m); addr += m; size &= ~m; } } } static void freeslice(Disk *disk, u64int addr, u32int size) { Slice **l, *s; uint n; DBG('d')print("freeslice %llud %ud\n", addr, size); if(size == 0 || (addr & (size-1)) != 0) error("invalid slice free: %llud %ud", addr, size); n = log2of(size); size = (u32int)1<slices[n]; while(n < Nslice && (s = *l) != nil){ DBG('d')print("merge? %#llux %#llux %#ux\n", addr, s->addr, size); if((s->addr^addr) == size){ /* buddy? */ DBG('d')print("merge %#llux %#llux %#ux\n", addr, s->addr, size); if(s->addr < addr) addr = s->addr; *l = s->next; s->next = disk->freeslices; disk->freeslices = s; n++; size <<= 1; l = &disk->slices[n]; }else{ if(addr < s->addr) break; l = &s->next; } } DBG('d')print("free %llud %ud %d\n", addr, size, n); s = disk->freeslices; if(s != nil){ disk->freeslices = s->next; s->next = nil; }else s = emallocz(sizeof(*s), 1); s->addr = addr; s->next = *l; *l = s; } char* diskdump(Disk *disk) { Fmt fmt; Slice *s; int i; fmtstrinit(&fmt); fmtprint(&fmt, "disk %#p slices %d\n", disk, Nslice); for(i = 0; i < Nslice; i++){ if(disk->slices[i] != nil){ fmtprint(&fmt, "\t%2d:", i); for(s = disk->slices[i]; s != nil; s = s->next) fmtprint(&fmt, " %llud", s->addr); /* check for missed buddies (shouldn't happen) */ for(s = disk->slices[i]; s->next != nil; s = s->next) if(s->addr == (s->next->addr ^ (1<addr, s->next->addr); fmtprint(&fmt, " [%ud]\n", (u32int)1<= Nextent) return 0; b = (b + disk->secsize - 1) >> disk->secshift; l = (l + disk->secsize - 1) >> disk->secshift; p = (u32int)1 << i; DBG('d')print("b=%d l=%d p=%d\n", b, l, p); if(l < p) p = l; if(b > p) p = b; return 1<<(log2of(p)+disk->secshift); /* max(b, min(l, 2**i)) */ } uint secsize(Disk *d) { return d->secsize; } uint byte2sec(Disk *d, u32int bytes) { return (bytes + d->secsize - 1)/d->secsize; } void diskread(Disk *disk, uchar *p, usize n, u64int offset) { if(pread(disk->fd, p, n, disk->base+offset) != n) raise(nil); } void diskwrite(Disk *disk, uchar *p, usize n, u64int offset) { if(pwrite(disk->fd, p, n, disk->base+offset) != n) raise(nil); } void diskzero(Disk *disk, u32int count, u64int offset) { usize n, nz; uchar *p; nz = 32*1024; if(nz > count) nz = count; p = emallocz(nz, 1); if(waserror()){ free(p); raise(nil); } while((n = count) != 0){ if(n > nz) n = nz; diskwrite(disk, p, n, offset); count -= n; offset += n; } poperror(); free(p); }