This is cache.c in view mode; [Download] [Up]
/*
* Name: cache.c
* Description: This module contains a simple disk cache.
* Author: Christian Starkjohann <cs@hal.kph.tuwien.ac.at>
* Date: 1996-12-16
* Copyright: GNU-GPL
* Tabsize: 4
*/
#include <errno.h>
#include <libc.h>
#include "my_defines.h"
#include "cache.h"
extern int errno;
/* ------------------------------------------------------------------------- */
#define DPRINTF(arg) if(debug_mode & DEBUG_CACHE) dprintf arg
/* ------------------------------------------------------------------------- */
#define BIG_BUFSZ 65536 /* size of one cache block */
#define SMALL_BUFSZ 4096 /* size of one cache block */
#define CACHE_SIZE 10 /* number of cache blocks */
/* ------------------------------------------------------------------------- */
typedef struct cacheline{
struct cacheline *next;
struct cacheline **prevnext; /* doubly linked LRU chain */
char valid; /* all data in buffer is valid */
char any_dirty; /* there is dirty data in the buffer */
unsigned char *dirty; /* bitmap with dirty flags/unit */
int dirty_size; /* number of bytes in dirty buffer */
unsigned long base; /* base of buffer on disk */
char *buffer; /* longword aligned block */
}cacheline_t;
typedef struct list{
cacheline_t *head; /* first element in list */
cacheline_t **tail; /* pointer to 'next' entry of last element */
}list_t;
/* ------------------------------------------------------------------------- */
static int buffer_size = BIG_BUFSZ;
static int base_mask = (~(BIG_BUFSZ - 1));
static cacheline_t cache[CACHE_SIZE];
static list_t cache_lru;
static int unit_size; /* allocation unit */
static int unit_count; /* allocation units */
/* ------------------------------------------------------------------------- */
#define SET_BIT(arr, i) ((arr)[(i)>>3] |= (1 << ((i)&7)))
#define BIT_IS_SET(arr, i) (((arr)[(i)>>3] & (1 << ((i)&7))) != 0)
/* ------------------------------------------------------------------------- */
static inline void list_init(list_t *list)
{
list->head = NULL;
list->tail = &list->head;
}
static inline void list_append(list_t *list, cacheline_t *entry)
{
entry->prevnext = list->tail;
*list->tail = entry;
list->tail = &entry->next;
entry->next = NULL;
}
static inline void list_move_to_end(list_t *list, cacheline_t *entry)
{
if(entry->next == NULL) /* we are already the last! */
return;
*entry->prevnext = entry->next; /* take us out of the list */
entry->next->prevnext = entry->prevnext;
entry->prevnext = list->tail; /* put us at the end */
*list->tail = entry;
list->tail = &entry->next;
entry->next = NULL;
}
/* ------------------------------------------------------------------------- */
static unsigned long file_position = -3; /* force lseek for first time */
static void cacheline_rw(int do_wr, cacheline_t *p, char *buffer)
{
int size;
DPRINTF(("cacheline_rw: %sing from base 0x%08lx\n", do_wr ? "writ":"read",
p->base));
if(do_wr && !wr_enable){
fatal_error("*** Attempt to write on read only filesystem: 0x%lx\n",
p->base);
return;
}
if(file_position != p->base){
if(lseek(my_fd, p->base, 0) == -1){
fatal_error("** cacheline_rw: error in lseek to 0x%08lx: %s\n",
p->base, strerror(errno));
return;
}
file_position = p->base;
}
if(do_wr)
size = write(my_fd, buffer, buffer_size);
else
size = read(my_fd, buffer, buffer_size);
if(size != buffer_size){
fatal_error("** cacheline_rw: error %sing at base 0x%08lx: %s\n",
do_wr ? "writ":"read", p->base, strerror(errno));
}
file_position += buffer_size;
}
/* ------------------------------------------------------------------------- */
static void cache_make_valid(cacheline_t *p)
{
int i, pos;
char merge_buffer[buffer_size];
if(!p->any_dirty){
DPRINTF(("cache_make_valid: full read\n"));
cacheline_rw(0, p, p->buffer);
}else{
for(i=0;i<unit_count;i++){
if(!BIT_IS_SET(p->dirty, i))
break;
}
if(i < unit_count){ /* loop had break: not all blocks dirty */
DPRINTF(("cache_make_valid: merging with dirty\n"));
cacheline_rw(0, p, merge_buffer);
for(;i<unit_count;i++){ /* i is still on first non-dirty */
if(!BIT_IS_SET(p->dirty, i)){
pos = i * unit_size;
memcpy(&p->buffer[pos], &merge_buffer[pos], unit_size);
}
}
}else{
DPRINTF(("cache_make_valid: all dirty\n"));
}
}
p->valid = 1;
}
static void cache_writeback(cacheline_t *p)
{
if(!p->valid){
cache_make_valid(p);
}
cacheline_rw(1, p, p->buffer);
p->any_dirty = 0;
bzero(p->dirty, p->dirty_size);
}
/* ------------------------------------------------------------------------- */
static inline cacheline_t *cache_find(unsigned long base)
{
int i;
cacheline_t *p = NULL;
for(i=0;i<CACHE_SIZE;i++){
if(cache[i].base <= base && (cache[i].base + buffer_size) > base){
DPRINTF(("cache_find: found for base 0x%08lx\n", base));
return &cache[i]; /* found cache line for our region */
}
}
p = cache_lru.head;
DPRINTF(("cache_find: freeing 0x%08lx for 0x%08lx\n", p->base, base));
if(p->any_dirty)
cache_writeback(p);
p->valid = 0;
p->any_dirty = 0;
bzero(p->dirty, p->dirty_size);
p->base = base & base_mask;
return p;
}
/* ------------------------------------------------------------------------- */
void cached_read(unsigned long base, char *dest)
{
cacheline_t *p;
DPRINTF(("cached_read: 0x%08lx\n", base));
p = cache_find(base);
if(!p->valid)
cache_make_valid(p);
list_move_to_end(&cache_lru, p);
memcpy(dest, &p->buffer[base - p->base], unit_size);
}
void cached_write(unsigned long base, char *data)
{
int i;
cacheline_t *p;
DPRINTF(("cached_write: 0x%08lx\n", base));
p = cache_find(base);
i = base - p->base;
memcpy(&p->buffer[i], data, unit_size);
p->any_dirty = 1;
i /= unit_size;
SET_BIT(p->dirty, i);
list_move_to_end(&cache_lru, p);
}
/* ------------------------------------------------------------------------- */
void cache_sync(void)
{
int i;
cacheline_t *p;
DPRINTF(("cache_sync()\n"));
if(debug_mode & DEBUG_CACHE){
for(p=cache_lru.head;p!=NULL;p=p->next){
dprintf("%02d base 0x%08lx valid=%d any_dirty=%d\n",
(int)(p - cache), p->base, p->valid, p->any_dirty);
}
}
for(i=0;i<CACHE_SIZE;i++){
if(cache[i].any_dirty){
cache_writeback(&cache[i]);
}
}
}
/* ------------------------------------------------------------------------- */
void cache_set_bsize(int bsize)
{
DPRINTF(("cache_set_bsize(%d)\n", bsize));
if((bsize & (bsize - 1)) != 0){
eprintf("cache_set_bsize(): %d is not power of 2\n", bsize);
terminate(1);
}
if(bsize > buffer_size){
eprintf("cache_set_bsize(): %d too big\n", bsize);
terminate(1);
}
if(bsize < 512){
eprintf("cache_set_bsize(): %d too small\n", bsize);
terminate(1);
}
cache_sync(); /* make allocation unit size irrelevant */
unit_size = bsize;
unit_count = buffer_size/unit_size;
}
/* ------------------------------------------------------------------------- */
void cache_init_buffers(int use_small_buffer)
{
int i;
DPRINTF(("cache_init_buffers()\n"));
buffer_size = use_small_buffer ? SMALL_BUFSZ : BIG_BUFSZ;
unit_size = 512;
unit_count = buffer_size/unit_size;
base_mask = (~(buffer_size - 1));
list_init(&cache_lru);
for(i=0;i<CACHE_SIZE;i++){
cache[i].valid = 0;
cache[i].any_dirty = 0;
if(cache[i].dirty != NULL)
free(cache[i].dirty);
cache[i].dirty_size = buffer_size >> 12;
cache[i].dirty = malloc(cache[i].dirty_size);
bzero(cache[i].dirty, cache[i].dirty_size);
if(cache[i].buffer != NULL)
free(cache[i].buffer);
cache[i].buffer = malloc(buffer_size);
cache[i].base = i * buffer_size;
list_append(&cache_lru, &cache[i]);
}
}
/* ------------------------------------------------------------------------- */
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.