shithub: neoventi

ref: 3971bc6b760a0658b27d0dd0f46693129ff59164
dir: /cache.c/

View raw version
#include <u.h>
#include <libc.h>

#pragma pack on

typedef struct {
	Lock;
	// index into raw cache
	u32int index[7];
	// 24 bits of value index plus 8 bits of fd
	u32int value[7];
} bucket_t;
#pragma pack off

typedef char mainentry_t[8192];
static mainentry_t *data;
static bucket_t *buckets, *bucketend;
static u32int *freelist;
static u32int freelen;
static Lock freelock;

void
cacheinit(void)
{
	usize displacement;
	assert(sizeof(Lock) == 8);
	assert(sizeof(bucket_t) == 64);
	data = sbrk(8192 * 128*1024);
	// 18725 buckets is the minimum for 128K entries; we round up to 32,768. That "wastes" ~1MiB, but spreads entries out further,
	// and gives a nice power of two for fibonacci hashing.
	buckets = sbrk(64 + 64*32768);
	freelist = sbrk(4 * 128*1024);
	displacement = ((usize)buckets & 0b111111);
	if(displacement != 0){
		displacement = 64 - displacement;
		buckets = (bucket_t*)((usize)buckets + displacement);
	}
	// alignment
	assert(((usize)buckets&0b111111) == 0);
	bucketend = buckets + 32768;
	freelen = 128*1024;
	for(u32int i = 0; i < freelen; i += 1){
		freelist[i] = i;
	}
}

// Grabs a free cache entry, evicting if necessary.
u32int
grabfree(void)
{
	u32int result;
	lock(&freelock);
	// For now, block until something is free.
	// This is terrible. FIXME
	while(freelen == 0){
		unlock(&freelock);
		sleep(50);
		lock(&freelock);
	}
	freelen -= 1;
	result = freelist[freelen];
	unlock(&freelock);
	return result;
}

// Called to indicate that a read has been finished.
// See documentation at top of file for more details.
void
cachewritten(int fd, u32int blockindex)
{
	u64int bucketindex;
	bucket_t *bucket;
	u32int key;
	assert(fd < 0xFF);
	key = (blockindex << 8) | fd;
	// We have 32K buckets, so we want 15 bits
	bucketindex = ((u64int)key * 11400714819323198485LL) >> 49;
	bucket = &buckets[bucketindex];
	unlock(bucket);
}

// Looks up a cache entry. If the entry is not present in cache,
// this grabs a free buffer, inserts it into cache, and yields
// its index so that the caller may fill it up.
// Returns whether the data was already in cache.
int
cachelookup(char **buf, int fd, u32int blockindex)
{
	u64int bucketindex;
	u32int cacheindex;
	bucket_t *bucket;
	u32int key;
	assert(fd < 0xFF);
	key = (blockindex << 8) | fd;
	// We have 32K buckets, so we want 15 bits
	bucketindex = ((u64int)key * 11400714819323198485LL) >> 49;
	print("GET bucket index: %llx\n", bucketindex);
	bucket = &buckets[bucketindex];
	while(bucket != bucketend){
		lock(bucket);
		for(int i = 0; i < 7; i += 1){
			if(bucket->index[i] == 0){
				// Key does not exist
				cacheindex = grabfree();
				*buf = (char*)(data + cacheindex);
				assert((cacheindex & 0xFF000000) == 0);
				bucket->index[i] = blockindex;
				bucket->value[i] = (cacheindex << 8) | fd;
				// Leaves bucket locked intentionally!
				return 0;
			}
			if(bucket->index[i] == blockindex && (bucket->value[i] & 0xff) == fd){
				*buf = (char*)(data + (bucket->value[i] >> 8));
				unlock(bucket);
				return 1;
			}
		}
		unlock(bucket);
		bucket++;
	}
	sysfatal("unreachable");
	return 0;
}