ftp.nice.ch/pub/next/unix/disk/vmount.0.6a.s.tar.gz#/vmount.0.6a.s/cache.c

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.