/*
* Copyright (c) 1996 The University of Utah and
* the Computer Systems Laboratory at the University of Utah (CSL).
*
* This file is part of Flick, the Flexible IDL Compiler Kit.
*
* Flick is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Flick is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Flick; see the file COPYING. If not, write to
* the Free Software Foundation, 59 Temple Place #330, Boston, MA 02111, USA.
*/
#include <stdio.h>
#include <stdlib.h>
#include "file.h"
#include "cache.h"
/*******************************************************************************
*
* Block is our basic cache structure.
*
*******************************************************************************/
typedef struct Block
{
char * data;
int dirty;
int block;
int file;
struct Block * older;
struct Block * newer;
struct Block * nextfile;
struct Block * lastfile;
} Block;
/*******************************************************************************
*
* Few global definitions.
*
*******************************************************************************/
Block cache[512];
typedef Block * BP;
BP map[4096];
BP file[4096];
Block * oldest, * newest;
Block * hit;
/*******************************************************************************
*
* int InitCache ()
*
* This function intializes our 512 block cache. Returns 0 on success.
*******************************************************************************/
int InitCache ()
{
int count;
/* Initialize the map and file */
for (count = 4096; count--;)
{
map[count] = NULL;
file[count] = NULL;
}
/* Initialize all 512 blocks and allocate all necessary memory */
for (count = 512; count--;)
{
cache[count].data = malloc(512);
if (cache[count].data == NULL)
{
return -1;
}
cache[count].block = -1;
cache[count].file = -1;
/* Initialize a sorted linked lists for LRU replacement and lists associated */
/* with each file */
if (count != 0)
cache[count].older = &(cache[count-1]);
else
cache[count].older = NULL;
if (count != 511)
cache[count].newer = &(cache[count+1]);
else
cache[count].newer = NULL;
cache[count].nextfile = NULL;
cache[count].lastfile = NULL;
cache[count].dirty = 0;
}
oldest = &(cache[0]);
newest = &(cache[511]);
return 0;
}
/*******************************************************************************
*
* char * TestHit (int b, int f)
*
* This function tests for a cache hit. If indeed a cache hit, place
* the block at the "newest" end of our queue and return the associated data.
* Otherwise, return NULL.
*******************************************************************************/
char * TestHit (int b, int f)
{
/* Check if block is already in the cache */
if (map[b] != NULL)
{
/* Modify the links; remove oldest from the cache list */
if (map[b]->older != NULL)
map[b]->older->newer = map[b]->newer;
else
oldest = map[b]->newer;
/* Remove the Block structure from the cache linked list */
if (map[b]->newer != NULL)
map[b]->newer->older = map[b]->older;
else
newest = map[b]->older;
/* Move the Block structure to the newest position */
newest->newer = map[b];
map[b]->older = newest;
map[b]->newer = NULL;
newest = map[b];
/* Remove map[b] from the previous file cache entry list */
if ( map[b]->nextfile != NULL )
map[b]->nextfile->lastfile = map[b]->lastfile;
if ( map[b]->lastfile != NULL )
map[b]->lastfile->nextfile = map[b]->nextfile;
else
if ( map[b]->file >= 0 )
file[ map[b]->file ] = map[b]->nextfile;
/* Link map[b] into the file cache entry list */
map[b]->lastfile = NULL;
map[b]->nextfile = file[f];
file[f] = map[b];
if (file[f]->nextfile != NULL)
file[f]->nextfile->lastfile = file[f];
/* Set file, data associated with Block map[b]. Assign hit to map[b]. */
map[b]->file = f;
hit = map[b];
return map[b]->data;
}
else
return NULL;
}
/*******************************************************************************
*
* char * CacheBlock (int b, int f)
*
* This function replaces the Least Recently Used block with the new one
* (with Block b). f specifies the filehandle.
*******************************************************************************/
char * CacheBlock (int b, int f)
{
Block * temp;
/* Do we need to write the old block back to disk? */
if ((oldest->block >= 0) && oldest->dirty)
if (WriteDisk (oldest->block, oldest->data, 1) == -1)
return (char *) -1;
/* Oldest Block no longer in the cache */
if (oldest->block >= 0)
map[oldest->block] = NULL;
/* Remove the oldest Block from the file cache list. */
if (oldest->nextfile != NULL)
oldest->nextfile->lastfile = oldest->lastfile;
if (oldest->lastfile != NULL)
oldest->lastfile->nextfile = oldest->nextfile;
else if (oldest->file >= 0)
file[oldest->file] = oldest->nextfile;
/* Fun with links -- move the Block structure from the "oldest" position */
/* to the "newest" position */
temp = oldest->newer;
oldest->newer->older = NULL;
oldest->older = newest;
oldest->newer = NULL;
newest->newer = oldest;
newest = oldest;
oldest = temp;
/* Place the block into the file cache list. */
newest->lastfile = NULL;
newest->nextfile = file[f];
file[f] = newest;
if (file[f]->nextfile != NULL)
file[f]->nextfile->lastfile = file[f];
/* Assign some variables */
newest->block = b;
newest->dirty = 0;
newest->file = f;
/* Set the hit Block and map */
hit = newest;
map[b] = newest;
return newest->data;
}
/*******************************************************************************
*
* int CacheReadDisk ( int block_num, char *data, int blocks, int f)
*
* This function reads a disk block(s) starting at disk block block_num
* and places them into buffer data. Returns 0 on success.
*******************************************************************************/
int CacheReadDisk ( int block_num, char *data, int blocks, int f)
{
char * buf;
/* Check if it's a hit */
f %= 4096;
buf = TestHit (block_num, f);
/* Yeah! */
/* Not a cache hit therefore cache the block. */
if (buf == NULL)
{
buf = CacheBlock (block_num, f);
if (buf != (char *) -1 && ReadDisk (newest->block, newest->data, 1) == -1)
{
YfsError = EIO;
return -1;
}
}
/* Incorrect -> return error */
if (buf == (char *) -1)
{
hit->block = -1;
YfsError = EIO;
return -1;
}
/* Cp the local buffer into data */
bcopy (buf, data, 512);
return 0;
}
/*******************************************************************************
*
* int CacheWriteDisk ( int block_num, char *data, int blocks, int f)
*
* This function writes disk blocks starting at disk block block_num.
* The data to be written to disk is available in data. Returns 0 on success.
*******************************************************************************/
int CacheWriteDisk ( int block_num, char *data, int blocks, int f)
{
char * buf;
f %= 4096;
/* Check if this is a hit. */
buf = TestHit (block_num, f);
/* If not a cache hit, attempt to cache that block. */
if (buf == NULL)
{
buf = CacheBlock (block_num, f);
}
/* If incorrect buffer, return error. */
if (buf == (char *) -1)
{
hit->block = -1;
YfsError = EIO;
return -1;
}
/* Set dirty bit */
hit->dirty = 1;
/* Copy data */
bcopy (data, buf, 512);
return 0;
}
/*******************************************************************************
*
* int CacheSync (int f)
*
* This function makes sure that info in cache is consistent with memory.
*******************************************************************************/
int CacheSync (int f)
{
Block * temp;
f %= 4096;
if (file[f] == NULL)
return 0;
temp = file[f];
while (temp != NULL)
{
if (temp->dirty)
{
if (WriteDisk (temp->block, temp->data, 1) == -1)
{
YfsError = EIO;
return -1;
}
}
else
temp->dirty = 0;
temp = temp->nextfile;
}
return 0;
}
/*******************************************************************************
*
* void CacheClean (int f)
*
* This function is a helper to delete. For all the blocks belonging to
* deleted file, there is no need to write them back to disk so mark them clean.
*******************************************************************************/
void CacheClean (int f)
{
Block * temp;
f %= 4096;
if (file[f] == NULL)
return;
temp = file[f];
/* Mark all */
while (temp != NULL)
{
temp->dirty = 0;
temp = temp->nextfile;
}
}
syntax highlighted by Code2HTML, v. 0.9.1