// 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); } } }