home *** CD-ROM | disk | FTP | other *** search
- /*
- * 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]);
- }
- }
-
- /* ------------------------------------------------------------------------- */
-