Mon 14 Oct 23:06:38 CEST 2024
This commit is contained in:
parent
8beb891218
commit
692f1fb91f
224
kernel/bcache.c
Normal file
224
kernel/bcache.c
Normal file
|
@ -0,0 +1,224 @@
|
|||
/*
|
||||
Copyright (C) 2015-2019 The University of Notre Dame
|
||||
This software is distributed under the GNU General Public License.
|
||||
See the file LICENSE for details.
|
||||
*/
|
||||
|
||||
#include "bcache.h"
|
||||
#include "list.h"
|
||||
#include "page.h"
|
||||
#include "kmalloc.h"
|
||||
#include "string.h"
|
||||
#include "kernel/error.h"
|
||||
|
||||
struct bcache_entry {
|
||||
struct list_node node;
|
||||
struct device *device;
|
||||
int block;
|
||||
int dirty;
|
||||
char *data;
|
||||
};
|
||||
|
||||
static struct list cache = LIST_INIT;
|
||||
static struct bcache_stats stats = {0};
|
||||
static int max_cache_size = 100;
|
||||
|
||||
struct bcache_entry * bcache_entry_create( struct device *device, int block )
|
||||
{
|
||||
struct bcache_entry *e = kmalloc(sizeof(*e));
|
||||
if(!e) return 0;
|
||||
|
||||
e->device = device;
|
||||
e->block = block;
|
||||
e->data = page_alloc(1);
|
||||
if(!e->data) {
|
||||
kfree(e);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return e;
|
||||
|
||||
}
|
||||
|
||||
void bcache_entry_delete( struct bcache_entry *e )
|
||||
{
|
||||
if(e) {
|
||||
if(e->data) page_free(e->data);
|
||||
kfree(e);
|
||||
}
|
||||
}
|
||||
|
||||
void bcache_entry_clean( struct bcache_entry *e )
|
||||
{
|
||||
if(e->dirty) {
|
||||
device_write(e->device,e->data,1,e->block);
|
||||
// XXX How to deal with failure here?
|
||||
e->dirty = 0;
|
||||
stats.writebacks++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void bcache_trim()
|
||||
{
|
||||
struct bcache_entry *e;
|
||||
|
||||
while(list_size(&cache)>max_cache_size) {
|
||||
e = (struct bcache_entry *) list_pop_tail(&cache);
|
||||
bcache_entry_clean(e);
|
||||
bcache_entry_delete(e);
|
||||
}
|
||||
}
|
||||
|
||||
struct bcache_entry * bcache_find( struct device *device, int block )
|
||||
{
|
||||
struct list_node *n;
|
||||
struct bcache_entry *e;
|
||||
|
||||
for(n=cache.head;n;n=n->next) {
|
||||
e = (struct bcache_entry *)n;
|
||||
if(e->device==device && e->block==block) {
|
||||
return e;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct bcache_entry * bcache_find_or_create( struct device *device, int block, int *was_a_hit )
|
||||
{
|
||||
struct bcache_entry *e = bcache_find(device,block);
|
||||
if(e) {
|
||||
*was_a_hit = 1;
|
||||
} else {
|
||||
*was_a_hit = 0;
|
||||
e = bcache_entry_create(device,block);
|
||||
if(!e) return 0;
|
||||
list_push_head(&cache,&e->node);
|
||||
}
|
||||
|
||||
bcache_trim();
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
int bcache_read_block( struct device *device, char *data, int block )
|
||||
{
|
||||
int hit=0;
|
||||
int result;
|
||||
|
||||
struct bcache_entry *e = bcache_find_or_create(device,block,&hit);
|
||||
if(!e) return KERROR_OUT_OF_MEMORY;
|
||||
|
||||
if(hit) {
|
||||
stats.read_hits++;
|
||||
result = 1;
|
||||
} else {
|
||||
stats.read_misses++;
|
||||
result = device_read(device,e->data,1,block);
|
||||
}
|
||||
|
||||
if(result>0) {
|
||||
memcpy(data,e->data,device_block_size(device));
|
||||
} else {
|
||||
list_remove(&e->node);
|
||||
bcache_entry_delete(e);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int bcache_read( struct device *device, char *data, int blocks, int offset )
|
||||
{
|
||||
int i,r;
|
||||
int count = 0;
|
||||
int bs = device_block_size(device);
|
||||
|
||||
for(i=0;i<blocks;i++) {
|
||||
r = bcache_read_block(device,&data[i*bs],offset+i);
|
||||
if(r<1) break;
|
||||
count++;
|
||||
}
|
||||
|
||||
if(count>0) {
|
||||
return count;
|
||||
} else {
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int bcache_write_block( struct device *device, const char *data, int block )
|
||||
{
|
||||
int hit;
|
||||
|
||||
struct bcache_entry *e = bcache_find_or_create(device,block,&hit);
|
||||
if(!e) return KERROR_OUT_OF_MEMORY;
|
||||
|
||||
if(hit) {
|
||||
stats.write_hits++;
|
||||
} else {
|
||||
stats.write_misses++;
|
||||
}
|
||||
|
||||
memcpy(e->data,data,device_block_size(device));
|
||||
e->dirty = 1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int bcache_write( struct device *device, const char *data, int blocks, int offset )
|
||||
{
|
||||
int i,r;
|
||||
int count = 0;
|
||||
int bs = device_block_size(device);
|
||||
|
||||
for(i=0;i<blocks;i++) {
|
||||
r = bcache_write_block(device,&data[i*bs],offset+i);
|
||||
if(r<1) break;
|
||||
count++;
|
||||
}
|
||||
|
||||
if(count>0) {
|
||||
return count;
|
||||
} else {
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void bcache_flush_block( struct device *device, int block )
|
||||
{
|
||||
struct bcache_entry *e;
|
||||
e = bcache_find(device,block);
|
||||
if(e) bcache_entry_clean(e);
|
||||
}
|
||||
|
||||
void bcache_flush_device( struct device *device )
|
||||
{
|
||||
struct list_node *n;
|
||||
struct bcache_entry *e;
|
||||
|
||||
for(n=cache.head;n;n=n->next) {
|
||||
e = (struct bcache_entry *) n;
|
||||
if(e->device==device) {
|
||||
bcache_entry_clean(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void bcache_flush_all()
|
||||
{
|
||||
struct list_node *n;
|
||||
struct bcache_entry *e;
|
||||
|
||||
for(n=cache.head;n;n=n->next) {
|
||||
e = (struct bcache_entry *) n;
|
||||
bcache_entry_clean(e);
|
||||
}
|
||||
}
|
||||
|
||||
void bcache_get_stats( struct bcache_stats *s )
|
||||
{
|
||||
memcpy(s,&stats,sizeof(*s));
|
||||
}
|
Loading…
Reference in New Issue
Block a user