310 lines
8.3 KiB
C
310 lines
8.3 KiB
C
|
// 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);
|
||
|
}
|
||
|
}
|
||
|
}
|