home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Crawly Crypt Collection 2
/
crawlyvol2.bin
/
alt_os
/
mint
/
mfs6011
/
source
/
minixfs
/
cache.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-12-30
|
9KB
|
364 lines
/* This file is part of 'minixfs' Copyright 1991,1992,1993 S.N. Henson */
#include "minixfs.h"
#include "global.h"
#include "proto.h"
/* For this kind of file system a cache is absolutely essential,
without it your hard-disk will sound like a buzz-saw .... the
idea is fairly simple , for every block requested if it is not in
the cache load it in at the current position .Then return a
pointer to the block . Additionally all writes go to the cache if
an entry exists for the specified block . This means that when a
cache entry is overwritten we must write out the entry if it is
'dirty' , the function l_sync() writes out the entire cache along
with the zone and inode bitmaps. However for an increase in performance,
three caches are used , one for inodes one for directories and indirection
blocks , one for files , these are kmalloc()'ed at the same time so that
one follows the other in memory , this simplifies cache-flushing for example.
*/
/* Initialise cache */
int init_cache()
{
cache *ctemp;
long i;
/* Start with system cache */
ctemp=Kmalloc((icache_size+scache_size+ucache_size)*(SIZEOF(cache)+BLOCK_SIZE));
if(!ctemp)
{
CCONWS("Can't Allocate Cache: Using defaults");
ctemp=Kmalloc((ICACHE_SIZE+SCACHE_SIZE+UCACHE_SIZE)*(SIZEOF(cache)+BLOCK_SIZE));
if(!ctemp)
{
CCONWS("Can't Allocate Default Cache: Minixfs not installed");
return 1;
}
icache_size=ICACHE_SIZE;
ucache_size=UCACHE_SIZE;
scache_size=SCACHE_SIZE;
}
syscache.start=ctemp;
syscache.end=&ctemp[scache_size];
syscache.pos=syscache.start;
icache.start=syscache.end;
icache.end=&icache.start[icache_size];
icache.pos=icache.start;
usrcache.start=icache.end;
usrcache.pos=usrcache.start;
usrcache.end=&usrcache.start[ucache_size];
/* Invalidate all entries , initialise buffer pointers */
for(ctemp=syscache.start,i=0;ctemp!=usrcache.end;ctemp++,i++)
{
ctemp->buffer= ((bufr *)(usrcache.end))+i;
ctemp->status=0;
}
/* Initialise dummy guess */
dummy.iguess=icache.start;
dummy.zguess=usrcache.start;
dummy.izguess=syscache.start;
dummy.dizguess=syscache.start;
return 0;
}
void
l_sync()
{
cache *p;
int i;
/* Write out dirty cache entries */
for(p=syscache.start;p!=usrcache.end;p+=writeout(&usrcache,p)) continue;
/* Now inode and zone bitmaps */
for(i=0;i<NUM_DRIVES;i++)
{
if(super_ptr[i]) {
super_info *psblk;
psblk=super_ptr[i];
if(psblk==DFS) continue;
if(psblk->idirty && psblk->zdirty)
crwabs(3,psblk->ibitmap,psblk->sblk.s_imap_blks+
psblk->sblk.s_zmap_blks,2,i);
else
{
if(psblk->idirty)
crwabs(3,psblk->ibitmap,psblk->sblk.s_imap_blks,2,i);
if(psblk->zdirty)
crwabs(3,psblk->zbitmap,psblk->sblk.s_zmap_blks,
psblk->sblk.s_imap_blks+2,i);
}
psblk->idirty=0;
psblk->zdirty=0;
}
}
}
/* Return cache entry for numr,drive if it exists or NULL, this uses a 'guess'
* pointer for the caches: start at the point an entry was last found, this
* should be reduce cache searching a bit.
*/
cache *in_cache(numr,drive,control,guess)
long numr;
int drive;
cache_control *control;
cache **guess;
{
cache *p;
if(guess)
{
for(p=*guess;p!=control->end;p++)
if((p->block==numr) && (p->drive==drive) && p->status)
return (*guess=p);
for(p=control->start;p!=*guess;p++)
if((p->block==numr) && (p->drive==drive) && p->status)
return (*guess=p);
}
else
{
for(p=control->start;p!=control->end;p++)
if((p->block==numr) && (p->drive==drive) && p->status)
return p;
}
return NULL;
}
/* Return a pointer to block numr,drive loading and updating cache if needed */
bufr *cget_block(numr,drive,control)
long numr;
int drive;
cache_control * control;
{
return(cache_get(numr,drive,control,NOGUESS)->buffer);
}
#if 1
/* super-simple `least recently used' strategy... after a cache hit
* advance control->pos if this buffer otherwise would be reused soon.
*/
#define pos_up(pp, control) do { \
long size = ((char *)control->end)-((char *)control->start), \
d = ((char *)pp)-((char *)control->pos); \
if (d < 0) d += size; \
if (d < size/4) control->pos = pp+1; \
} while (0)
#else
#define pos_up(pp, control)
#endif
/* Return a pointer to a cache entry buffer for numr,drive with the dirty
* flag set, if not in cache create a new entry but do *not* read in data
* from the disk, this is useful for example when an entire disk block will
* be written in one go.
*/
cache *cache_put(numr,drive,control)
long numr;
int drive;
cache_control *control;
{
cache *p;
if(!(p=in_cache(numr,drive,control,NOGUESS)))
{
if(control!=&icache) chk_zone(numr,1,drive);
if(control->pos==control->end) control->pos=control->start;
p=control->pos++;
writeout(control,p);
p->drive=drive;
p->block=numr;
} else
pos_up (p, control);
p->status=2;
return (p);
}
cache *
cache_get(numr,drive,control,guess)
long numr;
int drive;
cache_control *control;
cache **guess;
{
cache *p;
if( (p=in_cache(numr,drive,control,guess)) ) {
pos_up (p, control);
return(p);
}
/* Read block in */
if(control->pos==control->end)
control->pos=control->start;
/* Write out dirty entries before they are overwritten */
if(control->pos->status >1 ) writeout(control,control->pos);
/* This is the only check done on I/O into cache. If invalid
* blocks can never enter the cache then all should be OK
* later on. Unless the memory gets trashed. Interesting hack:
* if iblock is in range and all inode blocks are used (as will
* usually be the case) then the inode number producing it should
* be in range too, so no checking on inode numbers need be done.
*/
if(control==&icache) chk_iblock(numr,super_ptr[drive]);
else chk_zone(numr,1,drive);
crwabs(2,control->pos->buffer,1,numr,drive);
/* Update Cache */
control->pos->drive=drive;
control->pos->block=numr;
control->pos->status=1;
if(guess) *guess=control->pos;
return ( control->pos++ );
}
/* Write out block, search cache and if 'hit' update and mark as dirty
* after copying the data across, otherwise flush current entry.
*/
int
cput_block(numr,drive,buf,control)
long numr;
int drive;
void *buf;
cache_control *control;
{
cache *p;
if( (p=in_cache(numr,drive,control,NOGUESS)) ){
if(buf!=p->buffer) bcopy(buf,p->buffer,BLOCK_SIZE);
p->status=2;
pos_up (p, control);
return 0;
}
if(control->pos==control->end) control->pos=control->start;
writeout(control,control->pos);
if(control!=&icache) chk_zone(numr,1,drive);
bcopy(buf,control->pos->buffer,BLOCK_SIZE);
/* Update Cache */
control->pos->drive=drive;
control->pos->block=numr;
control->pos->status=2;
control->pos++;
return 0;
}
/* From the cache-pointer 'p' write out dirty entries that are consecutive
* all in one go, this should cut down on I/O quite a lot.
*/
#define DEBUG_CACHE
long writeout(control,p)
cache_control *control;
cache *p;
{
cache *q;
long i;
if(p->status<2) return 1;
/* Determined how many blocks are consecutive */
for(q=p,i=0;q!=control->end;q++,i++)
{
#ifdef DEBUG_CACHE
/* Check block boundaries */
if(q->status)
{
if( (q >= icache.start) && (q < icache.end))
chk_iblock(q->block,super_ptr[q->drive]);
else chk_zone(q->block,1,q->drive);
}
#endif
if( (q->drive!=p->drive) || (q->block!=p->block+i) ||
(q->status < 2) ) break;
q->status=1;
}
crwabs(3,p->buffer,i,p->block,p->drive);
return i;
}
/* From the current cache position, read up to the num zones pointed to by
* zone_list in. Flush the cache and read in as much as possible in one go.
* Stop at first non-consecutive zone.
*/
long readin(zone_list,num,drive,control)
long *zone_list;
int num;
int drive;
cache_control *control;
{
cache *p;
int i,j;
if(!*zone_list) return 0;
for(i=1;i<num;i++)if(zone_list[i]!=zone_list[0]+i) break;
if(control->pos==control->end) control->pos=control->start;
i=min(control->end-control->pos,i);
/* If any entries already in cache, forget it */
for(p=control->start;p!=control->end;p++) if( (p->drive==drive)
&& (p->block >= zone_list[0]) && (p->block < zone_list[0]+i) )
return 0;
/* Write out at least 'i' entries */
for(p=control->pos; p < control->pos+i ; )
{
if(p->status < 2) p++;
else p+=writeout(control,p);
}
chk_zone(zone_list[0],i,drive);
crwabs(2,control->pos->buffer,i,zone_list[0],drive);
for(p=control->pos,j=0;j<i;j++,p++)
{
p->block=zone_list[j];
p->drive=drive;
p->status=1;
}
control->pos+=i;
return i;
}
/* Invalidate all cache entries for a given drive */
void m_invalidate(drv)
int drv;
{
int warned=0;
cache *p;
super_info *psblk;
for(p=syscache.start;p!=usrcache.end;p++)
if(p->drive==drv && p->status)
{
if(p->status==2 && !warned++)
ALERT("Cache entry not written out when drive %c invalidated",drv+'A');
p->status=0;
}
psblk=super_ptr[drv];
if(psblk==DFS) return;
if(psblk->idirty)
ALERT("Inode bitmap not written out when drive %c invalidated",drv+'A');
if(psblk->zdirty)
ALERT("Zone bitmap not written out when drive %c invalidated",drv+'A');
psblk->idirty=0;
psblk->zdirty=0;
}