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.