slkern/diskio.c

310 lines
8.3 KiB
C
Raw Permalink Normal View History

// NEW CODE replacing old buffer management code with a more flexible system
#include "diskio.h"
#include "types.h"
#include "param.h"
#include "memlayout.h"
#include "riscv.h"
#include "defs.h"
#include "drives.h"
#include "fsinstance.h"
#include "vmrd.h"
#include "kprintf.h"
int strcmp(const char*, const char*);
void* memcpy(void*, void*, long);
diskio_buffer_t* diskio_buffer_alloc(diskio_cache_t* owner, unsigned long long blocksize) {
if (blocksize > 4096ULL) { // TODO: Standardise page/block limits
panic("diskio_buffer_alloc: Bad block size!");
}
diskio_buffer_t* result = kalloc();
if (result) {
if (blocksize == 0ULL) {
result->data = NULL;
} else {
result->data = kalloc();
if (!result->data) {
kfree(result);
return NULL;
}
}
result->owner = owner;
result->referencecount = 0;
result->padding = 0; // unused
result->isvalid = 0;
result->isdisk = 0;
result->device = 0;
result->blocknumber = 0;
result->previous = NULL;
result->next = NULL;
initsleeplock(&result->lock, "diskio_buffer_t");
}
return result;
}
void diskio_buffer_free(diskio_buffer_t* buffer) {
if (buffer) {
if (buffer->data) {
kfree(buffer->data);
}
kfree(buffer->data);
}
}
void diskio_buffer_reference(diskio_buffer_t* buffer) {
acquire(&buffer->owner->spin);
buffer->referencecount = buffer->referencecount + 1;
release(&buffer->owner->spin);
}
void diskio_buffer_dereference(diskio_buffer_t* buffer) {
acquire(&buffer->owner->spin);
buffer->referencecount = buffer->referencecount - 1;
release(&buffer->owner->spin);
}
// Checks that the buffer is already locked and writes it to the disk.
void diskio_buffer_write(diskio_buffer_t* buffer) {
if (!holdingsleep(&buffer->lock)) {
panic("diskio_buffer_write: Calling code is not holding the appropriate lock!");
}
diskio_performwrite(buffer);
}
// Performs a logical read of a buffer, returning it in locked form with the given block.
diskio_buffer_t* diskio_buffer_read(diskio_cache_t* cache, unsigned int device, unsigned int blocknumber) {
diskio_buffer_t* buffer = diskio_buffer_get_noread(cache, device, blocknumber);
if (!buffer->isvalid) {
diskio_performread(buffer);
buffer->isvalid = 1;
}
return buffer;
}
diskio_buffer_t* diskio_buffer_get_noread(diskio_cache_t* cache, unsigned int device, unsigned int blocknumber) {
acquire(&cache->spin);
diskio_buffer_t* list = cache->leastrecentlist;
// Look for a matching cached entry if one exists and lock it.
for (diskio_buffer_t* buffer = list->next; buffer != list; buffer = buffer->next) {
if (buffer->device == device && buffer->blocknumber == blocknumber) {
buffer->referencecount = buffer->referencecount + 1;
release(&cache->spin);
acquiresleep(&buffer->lock);
return buffer;
}
}
// Otherwise find an empty buffer to re-allocate.
for (diskio_buffer_t* buffer = list->previous; buffer != list; buffer = buffer->previous) {
if (buffer->referencecount == 0) {
buffer->referencecount = 1;
buffer->isvalid = 0;
buffer->device = device;
buffer->blocknumber = blocknumber;
//printf("Set device to 0x%x (0x%x)\n", buffer->device, device);
release(&cache->spin);
acquiresleep(&buffer->lock);
return buffer;
}
}
panic("diskio_buffer_get_noread: TODO: Better handling when running out of buffers!");
return NULL;
}
void diskio_buffer_release(diskio_buffer_t* buffer) {
diskio_cache_t* cache = buffer->owner;
if (!holdingsleep(&buffer->lock)) {
panic("diskio_buffer_release: Calling code is not holding the appropriate lock!");
}
releasesleep(&buffer->lock);
acquire(&cache->spin);
diskio_buffer_t* list = cache->leastrecentlist;
buffer->referencecount = buffer->referencecount - 1;
if (buffer->referencecount == 0) {
buffer->next->previous = buffer->previous;
buffer->previous->next = buffer->next;
buffer->next = list->next;
buffer->previous = list;
list->next->previous = buffer;
list->next = buffer;
}
release(&cache->spin);
}
diskio_cache_t* diskio_cache_alloc(unsigned long long buffercount, unsigned long long blocksize) {
if (buffercount > DISKIO_CACHE_MAXBUFFERS) {
panic("diskio_cache_alloc: buffercount is out of range!");
}
diskio_cache_t* result = kalloc();
if (result) {
initlock(&result->spin, "diskio_cache_t");
result->buffercount = buffercount;
diskio_buffer_t* listhead = diskio_buffer_alloc(result, 0);
if (listhead == NULL) {
kfree(result);
return NULL;
}
listhead->previous = listhead;
listhead->next = listhead;
result->leastrecentlist = listhead;
for (int bufnum = 0; bufnum < buffercount; bufnum++) {
diskio_buffer_t* buffer = diskio_buffer_alloc(result, blocksize);
if (buffer == NULL) {
while (--bufnum > 0) {
diskio_buffer_free(result->buffers[bufnum]);
diskio_buffer_free(listhead);
kfree(result);
}
return NULL;
}
buffer->next = listhead->next;
buffer->previous = listhead;
listhead->next->previous = buffer;
listhead->next = buffer;
result->buffers[bufnum] = buffer;
}
result->blocksize = blocksize;
}
return result;
}
void diskio_cache_free(diskio_cache_t* cache) {
if (cache) {
// TODO: Free block cache structure
}
}
void diskio_performread(diskio_buffer_t* buffer) {
if ((buffer->device & 0xFF00) == 0x0900) {
diskio_ramdisk_rw(buffer, 0);
} else {
if (vmrd_present()) {
vmrd_rw(buffer, 0);
} else {
virtio_disk_rw(buffer, 0);
}
}
}
void diskio_performwrite(diskio_buffer_t* buffer) {
if ((buffer->device & 0xFF00) == 0x0900) {
diskio_ramdisk_rw(buffer, 1);
} else {
if (vmrd_present()) {
vmrd_rw(buffer, 0);
} else {
virtio_disk_rw(buffer, 1);
}
}
}
#define RESOURCES_MAX 256
typedef struct resource resource_t;
struct resource {
const char* type;
const char* name;
unsigned long size;
unsigned char* data;
};
resource_t resources[RESOURCES_MAX];
int nresources = -1;
void resources_checkinit() {
if (nresources == -1) {
printf("Initialising resources...\n");
nresources = 0;
}
}
void resources_register(const char* type, const char* name, unsigned char* data, unsigned long size) {
resources_checkinit();
if (strcmp(type, "RAMDISK") == 0) {
resources[nresources].type = type;
resources[nresources].name = name;
resources[nresources].data = data;
resources[nresources].size = size;
nresources++;
} else {
printf("TODO: resources_register(\"%s\", \"%s\", %p, %d);\n", type, name, data, (int) size);
}
}
void diskio_ramdisk_rw(diskio_buffer_t* buffer, int wr) {
resource_t* ramdisk = &resources[buffer->device & 0xFF];
if ((buffer->blocknumber + 1)*DISKIO_BLOCK_SIZE > ramdisk->size) {
panic("diskio_ramdisk_rw: block number out of range");
}
unsigned long offset = buffer->blocknumber * DISKIO_BLOCK_SIZE;
unsigned char* ramdiskptr = ramdisk->data + offset;
if (wr) {
memcpy(ramdiskptr, buffer->data, DISKIO_BLOCK_SIZE);
} else {
memcpy(buffer->data, ramdiskptr, DISKIO_BLOCK_SIZE);
}
}
void diskio_mountramdisk(struct drives* drives, const char* mountname, unsigned int device) {
printf("TODO: mount ramdisk '%s' on device %x\n", mountname, device);
printf(" diskio_cache_alloc()...\n");
diskio_cache_t* cache = diskio_cache_alloc(NBUF, DISKIO_BLOCK_SIZE);
printf(" diskio_cache_alloc() returned %p\n", cache);
printf(" fsinstance_alloc()...\n");
fsinstance_t* instance = fsinstance_alloc();
printf(" fsinstance_alloc() returned %p\n", instance);
instance->cache = cache;
printf(" drives_setup()...\n");
int dn = drives_setup(drives, DRIVES_HANDLER_FS, instance, mountname);
printf(" drives_setup() returned %d\n", dn);
printf(" fsinstance_init()...\n");
void * fsp = fsinstance_init(instance, device);
printf(" fsinstance_init() returned %p\n", fsp);
printf("device=%x\n", (int)((long)instance->fslog_device));
}
void diskio_mountallramdisks(struct drives* drives) {
for (int i = 0; i < nresources; i++) {
resource_t* r = &resources[i];
if (!strcmp(r->type, "RAMDISK")) {
diskio_mountramdisk(drives, r->name, 0x0900 + i);
}
}
}