#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>
#include <string.h>
#include "fsck_hfs.h"
#include "cache.h"
#define true 1
#define false 0
#define CACHE_DEBUG 0
void *CacheAllocBlock (Cache_t *cache);
static int
CacheFreeBlock( Cache_t *cache, Tag_t *tag );
int CacheLookup (Cache_t *cache, uint64_t off, Tag_t **tag);
int CacheRawRead (Cache_t *cache, uint64_t off, uint32_t len, void *buf);
int CacheRawWrite (Cache_t *cache, uint64_t off, uint32_t len, void *buf);
static int
CacheFlushRange( Cache_t *cache, uint64_t start, uint64_t len, int remove);
static int LRUInit (LRU_t *lru);
static int LRUDestroy (LRU_t *lru);
static int LRUHit (LRU_t *lru, LRUNode_t *node, int age);
static int LRUEvict (LRU_t *lru, LRUNode_t *node);
void CalculateCacheSizes(uint64_t cacheSize, uint32_t *calcBlockSize, uint32_t *calcTotalBlocks, char cache_debug)
{
uint32_t blockSize = DefaultCacheBlockSize;
const size_t max_size_t = ~0;
if (!cacheSize) {
*calcBlockSize = DefaultCacheBlockSize;
*calcTotalBlocks = DefaultCacheBlocks;
goto out;
}
if (cacheSize < MinCacheSize) {
cacheSize = MinCacheSize;
}
if (cacheSize > max_size_t ||
cacheSize > MaxCacheSize) {
if (cache_debug) {
printf ("\tCache size should be greater than %uM and less than %luM\n", MinCacheSize/(1024*1024), max_size_t/(1024*1024));
}
cacheSize = MaxCacheSize;
}
if (cacheSize % blockSize) {
if (cache_debug) {
printf ("\tCache size should be multiple of cache block size (currently %uK)\n", blockSize/1024);
}
cacheSize = (cacheSize / blockSize) * blockSize;
}
*calcBlockSize = blockSize;
*calcTotalBlocks = (uint32_t)(cacheSize / blockSize);
out:
return;
}
int CacheInit (Cache_t *cache, int fdRead, int fdWrite, uint32_t devBlockSize,
uint32_t cacheBlockSize, uint32_t cacheTotalBlocks, uint32_t hashSize, int preTouch)
{
void ** temp;
uint32_t i;
Buf_t * buf;
memset (cache, 0x00, sizeof (Cache_t));
cache->FD_R = fdRead;
cache->FD_W = fdWrite;
cache->DevBlockSize = devBlockSize;
cache->Hash = (Tag_t **) calloc( 1, (sizeof (Tag_t *) * hashSize) );
cache->HashSize = hashSize;
cache->BlockSize = cacheBlockSize;
while (1) {
cache->FreeHead = mmap (NULL,
cacheTotalBlocks * cacheBlockSize,
PROT_READ | PROT_WRITE,
MAP_ANON | MAP_PRIVATE,
-1,
0);
if (cache->FreeHead == (void *)-1) {
if ((cacheTotalBlocks * cacheBlockSize) <= MinCacheSize) {
if (debug)
printf("\tTried to allocate %dK, minimum is %dK\n",
(cacheTotalBlocks * cacheBlockSize) / 1024,
MinCacheSize / 1024);
break;
}
if (debug)
printf("\tFailed to allocate %uK for cache; trying %uK\n",
(cacheTotalBlocks * cacheBlockSize) / 1024,
(cacheTotalBlocks * cacheBlockSize / 2) / 1024);
CalculateCacheSizes((cacheTotalBlocks * cacheBlockSize) / 2, &cacheBlockSize, &cacheTotalBlocks, debug);
continue;
} else {
if (debug) {
printf ("\tUsing cacheBlockSize=%uK cacheTotalBlock=%u cacheSize=%uK.\n", cacheBlockSize/1024, cacheTotalBlocks, (cacheBlockSize/1024) * cacheTotalBlocks);
}
break;
}
}
if (cache->FreeHead == (void*)-1) {
#if CACHE_DEBUG
printf("%s(%d): FreeHead = -1\n", __FUNCTION__, __LINE__);
#endif
return (ENOMEM);
}
if (preTouch) {
size_t pageSize = getpagesize();
unsigned char *ptr = (unsigned char *)cache->FreeHead;
unsigned char *end = ptr + (cacheTotalBlocks * cacheBlockSize);
while (ptr < end) {
*ptr = 0;
ptr += pageSize;
}
}
temp = cache->FreeHead;
for (i = 0; i < cacheTotalBlocks - 1; i++) {
*temp = ((char *)temp + cacheBlockSize);
temp = (void **)((char *)temp + cacheBlockSize);
}
*temp = NULL;
cache->FreeSize = cacheTotalBlocks;
buf = (Buf_t *)malloc(sizeof(Buf_t) * MAXBUFS);
if (buf == NULL) {
#if CACHE_DEBUG
printf("%s(%d): malloc(%zu) failed\n", __FUNCTION__, __LINE__, sizeof(Buf_t) * MAXBUFS);
#endif
return (ENOMEM);
}
memset (&buf[0], 0x00, sizeof (Buf_t) * MAXBUFS);
for (i = 1 ; i < MAXBUFS ; i++) {
(&buf[i-1])->Next = &buf[i];
}
cache->FreeBufs = &buf[0];
#if CACHE_DEBUG
printf( "%s - cacheTotalBlocks %d cacheBlockSize %d hashSize %d \n",
__FUNCTION__, cacheTotalBlocks, cacheBlockSize, hashSize );
printf( "%s - cache memory %d \n", __FUNCTION__, (cacheTotalBlocks * cacheBlockSize) );
#endif
return (LRUInit (&cache->LRU));
}
int CacheDestroy (Cache_t *cache)
{
CacheFlush( cache );
#if CACHE_DEBUG
printf ("Cache Report:\n");
printf ("\tRead Requests: %d\n", cache->ReqRead);
printf ("\tWrite Requests: %d\n", cache->ReqWrite);
printf ("\tDisk Reads: %d\n", cache->DiskRead);
printf ("\tDisk Writes: %d\n", cache->DiskWrite);
printf ("\tSpans: %d\n", cache->Span);
#endif
LRUDestroy (&cache->LRU);
return (EOK);
}
int CacheRead (Cache_t *cache, uint64_t off, uint32_t len, Buf_t **bufp)
{
Tag_t * tag;
Buf_t * searchBuf;
Buf_t * buf;
uint32_t coff = (off % cache->BlockSize);
uint64_t cblk = (off - coff);
int error;
searchBuf = cache->ActiveBufs;
while (searchBuf != NULL) {
if ((searchBuf->Offset >= off) && (searchBuf->Offset < off + len)) {
#if CACHE_DEBUG
printf ("ERROR: CacheRead: Deadlock (searchBuff = <%llu, %u>, off = %llu, off+len = %llu)\n", searchBuf->Offset, searchBuf->Length, off, off+len);
#endif
return (EDEADLK);
}
searchBuf = searchBuf->Next;
}
if ((buf = cache->FreeBufs) == NULL) {
#if CACHE_DEBUG
printf ("ERROR: CacheRead: no more bufs!\n");
#endif
return (ENOBUFS);
}
cache->FreeBufs = buf->Next;
*bufp = buf;
buf->Next = NULL;
buf->Prev = NULL;
buf->Flags = 0;
buf->Offset = off;
buf->Length = len;
buf->Buffer = NULL;
if ((cblk / cache->BlockSize) != ((off + len - 1) / cache->BlockSize)) {
buf->Flags |= BUF_SPAN;
}
#if CACHE_DEBUG
printf("%s(%d): Looking up cache block %llu for offset %llu, cache blockSize %u\n", __FUNCTION__, __LINE__, cblk, off, cache->BlockSize);
#endif
error = CacheLookup (cache, cblk, &tag);
if (error != EOK) {
#if CACHE_DEBUG
printf ("ERROR: CacheRead: CacheLookup error %d\n", error);
#endif
return (error);
}
if (!(buf->Flags & BUF_SPAN)) {
buf->Buffer = tag->Buffer + coff;
tag->Refs++;
LRUHit (&cache->LRU, (LRUNode_t *)tag, 0);
} else {
uint32_t boff;
uint32_t blen;
uint32_t temp;
buf->Buffer = (void *)malloc (len);
if (buf->Buffer == NULL) {
#if CACHE_DEBUG
printf ("ERROR: CacheRead: No Memory\n");
#endif
return (ENOMEM);
}
boff = cache->BlockSize - coff;
blen = len - boff;
#if CACHE_DEBUG
printf("INFO: memcpy(%p, %p + %u, %u)\n", buf->Buffer, tag->Buffer, coff, boff);
#endif
memcpy (buf->Buffer, tag->Buffer + coff, boff);
tag->Refs++;
LRUHit (&cache->LRU, (LRUNode_t *)tag, 0);
cblk += cache->BlockSize;
while (blen) {
error = CacheLookup (cache, cblk, &tag);
if (error != EOK) {
free (buf->Buffer);
buf->Buffer = NULL;
cblk -= cache->BlockSize;
while (!boff) {
if (CacheLookup (cache, cblk, &tag) != EOK) {
fprintf (stderr, "CacheRead: Unrecoverable error\n");
exit (-1);
}
tag->Refs--;
LRUHit (&cache->LRU, (LRUNode_t *)tag, 0);
}
return (error);
}
temp = ((blen > cache->BlockSize) ? cache->BlockSize : blen);
#if CACHE_DEBUG
printf ("INFO: memcpy(%p + %u, %p, %u)\n", buf->Buffer, boff, tag->Buffer, temp);
#endif
memcpy (buf->Buffer + boff,
tag->Buffer,
temp);
boff += temp;
blen -= temp;
tag->Refs++;
cblk += cache->BlockSize;
LRUHit (&cache->LRU, (LRUNode_t *)tag, 0);
}
cache->Span++;
}
if (cache->ActiveBufs != NULL) {
buf->Next = cache->ActiveBufs;
buf->Prev = NULL;
cache->ActiveBufs->Prev = buf;
} else {
cache->ActiveBufs = buf;
}
cache->ReqRead++;
return (EOK);
}
int CacheWrite ( Cache_t *cache, Buf_t *buf, int age, uint32_t writeOptions )
{
Tag_t * tag;
uint32_t coff = (buf->Offset % cache->BlockSize);
uint64_t cblk = (buf->Offset - coff);
int error;
error = CacheLookup (cache, cblk, &tag);
if (error != EOK) return (error);
if (!(buf->Flags & BUF_SPAN)) {
if ( (writeOptions & (kLazyWrite | kLockWrite)) != 0 )
{
tag->Flags |= (writeOptions & (kLazyWrite | kLockWrite));
}
else
{
error = CacheRawWrite (cache,
tag->Offset,
cache->BlockSize,
tag->Buffer);
if (error != EOK) return (error);
}
if ((writeOptions & kLockWrite) == 0)
tag->Refs--;
LRUHit (&cache->LRU, (LRUNode_t *)tag, age);
} else {
uint32_t boff;
uint32_t blen;
uint32_t temp;
boff = cache->BlockSize - coff;
blen = buf->Length - boff;
memcpy (tag->Buffer + coff, buf->Buffer, boff);
if ( (writeOptions & (kLazyWrite | kLockWrite)) != 0 )
{
tag->Flags |= (writeOptions & (kLazyWrite | kLockWrite));
}
else
{
error = CacheRawWrite (cache,
tag->Offset,
cache->BlockSize,
tag->Buffer);
if (error != EOK) return (error);
}
if ((writeOptions & kLockWrite) == 0)
tag->Refs--;
LRUHit (&cache->LRU, (LRUNode_t *)tag, age);
cblk += cache->BlockSize;
while (blen) {
error = CacheLookup (cache, cblk, &tag);
temp = ((blen > cache->BlockSize) ? cache->BlockSize : blen);
memcpy (tag->Buffer,
buf->Buffer + boff,
temp);
if ( (writeOptions & (kLazyWrite | kLockWrite)) != 0 )
{
tag->Flags |= (writeOptions & (kLazyWrite | kLockWrite));
}
else
{
error = CacheRawWrite (cache,
tag->Offset,
cache->BlockSize,
tag->Buffer);
if (error != EOK) return (error);
}
boff += temp;
blen -= temp;
if ((writeOptions & kLockWrite) == 0)
tag->Refs--;
LRUHit (&cache->LRU, (LRUNode_t *)tag, age);
cblk += cache->BlockSize;
}
free (buf->Buffer);
}
if (buf->Next != NULL)
buf->Next->Prev = buf->Prev;
if (buf->Prev != NULL)
buf->Prev->Next = buf->Next;
if (cache->ActiveBufs == buf)
cache->ActiveBufs = buf->Next;
memset (buf, 0x00, sizeof (Buf_t));
buf->Next = cache->FreeBufs;
cache->FreeBufs = buf;
cache->ReqWrite++;
return (EOK);
}
int CacheRelease (Cache_t *cache, Buf_t *buf, int age)
{
Tag_t * tag;
uint32_t coff = (buf->Offset % cache->BlockSize);
uint64_t cblk = (buf->Offset - coff);
int error;
error = CacheLookup (cache, cblk, &tag);
if (error != EOK) {
#if CACHE_DEBUG
printf ("ERROR: CacheRelease: CacheLookup error\n");
#endif
return (error);
}
if (!(buf->Flags & BUF_SPAN)) {
if ((tag->Flags & kLockWrite) == 0) {
tag->Refs--;
}
LRUHit (&cache->LRU, (LRUNode_t *)tag, age);
} else {
uint32_t blen;
blen = buf->Length - cache->BlockSize + coff;
if ((tag->Flags & kLockWrite) == 0) {
tag->Refs--;
}
LRUHit (&cache->LRU, (LRUNode_t *)tag, age);
cblk += cache->BlockSize;
while (blen) {
error = CacheLookup (cache, cblk, &tag);
blen -= ((blen > cache->BlockSize) ? cache->BlockSize : blen);
if ((tag->Flags & kLockWrite) == 0)
tag->Refs--;
LRUHit (&cache->LRU, (LRUNode_t *)tag, age);
cblk += cache->BlockSize;
}
free (buf->Buffer);
}
if (buf->Next != NULL)
buf->Next->Prev = buf->Prev;
if (buf->Prev != NULL)
buf->Prev->Next = buf->Next;
if (cache->ActiveBufs == buf)
cache->ActiveBufs = buf->Next;
memset (buf, 0x00, sizeof (Buf_t));
buf->Next = cache->FreeBufs;
cache->FreeBufs = buf;
return (EOK);
}
int CacheRemove (Cache_t *cache, Tag_t *tag)
{
int error;
if (tag->Refs) return (EBUSY);
if (tag->Next != NULL)
tag->Next->Prev = tag->Prev;
if (tag->Prev != NULL)
tag->Prev->Next = tag->Next;
else
cache->Hash[tag->Offset % cache->HashSize] = tag->Next;
if ((cache->Hash[tag->Offset % cache->HashSize] != NULL) &&
(cache->Hash[tag->Offset % cache->HashSize]->Prev != NULL)) {
#if CACHE_DEBUG
printf ("ERROR: CacheRemove: Corrupt hash chain\n");
#endif
}
if (tag->Buffer != NULL)
{
error = CacheFreeBlock (cache, tag);
if ( EOK != error )
return( error );
}
memset (tag, 0x00, sizeof (Tag_t));
free (tag);
return (EOK);
}
int CacheEvict (Cache_t *cache, Tag_t *tag)
{
int error;
if (tag->Refs) return (EBUSY);
if (tag->Buffer != NULL)
{
error = CacheFreeBlock (cache, tag);
if ( EOK != error )
return( error );
}
tag->Buffer = NULL;
return (EOK);
}
void *CacheAllocBlock (Cache_t *cache)
{
void * temp;
if (cache->FreeHead == NULL)
return (NULL);
if (cache->FreeSize == 0)
return (NULL);
temp = cache->FreeHead;
cache->FreeHead = *((void **)cache->FreeHead);
cache->FreeSize--;
return (temp);
}
static int
CacheFreeBlock( Cache_t *cache, Tag_t *tag )
{
int error;
if ( (tag->Flags & kLazyWrite) != 0 )
{
error = CacheRawWrite( cache,
tag->Offset,
cache->BlockSize,
tag->Buffer );
if ( EOK != error )
{
#if CACHE_DEBUG
printf( "%s - CacheRawWrite failed with error %d \n", __FUNCTION__, error );
#endif
return ( error );
}
tag->Flags &= ~kLazyWrite;
}
if ((tag->Flags & kLockWrite) == 0)
{
*((void **)tag->Buffer) = cache->FreeHead;
cache->FreeHead = (void **)tag->Buffer;
cache->FreeSize++;
}
return( EOK );
}
int
CacheFlush( Cache_t *cache )
{
int error;
int i;
Tag_t * myTagPtr;
for ( i = 0; i < cache->HashSize; i++ )
{
myTagPtr = cache->Hash[ i ];
while ( NULL != myTagPtr )
{
if ( (myTagPtr->Flags & kLazyWrite) != 0 )
{
error = CacheRawWrite( cache,
myTagPtr->Offset,
cache->BlockSize,
myTagPtr->Buffer );
if ( EOK != error )
{
#if CACHE_DEBUG
printf( "%s - CacheRawWrite failed with error %d \n", __FUNCTION__, error );
#endif
return( error );
}
myTagPtr->Flags &= ~kLazyWrite;
}
myTagPtr = myTagPtr->Next;
}
}
return( EOK );
}
static int
RangeIntersect(uint64_t start1, uint64_t len1, uint64_t start2, uint64_t len2)
{
uint64_t end1 = start1 + len1 - 1;
uint64_t end2 = start2 + len2 - 1;
if (end1 < start2 || start1 > end2)
return 0;
else
return 1;
}
static int
CacheFlushRange( Cache_t *cache, uint64_t start, uint64_t len, int remove)
{
int error;
int i;
Tag_t *currentTag, *nextTag;
for ( i = 0; i < cache->HashSize; i++ )
{
currentTag = cache->Hash[ i ];
while ( NULL != currentTag )
{
nextTag = currentTag->Next;
if ( currentTag->Flags & kLazyWrite &&
RangeIntersect(currentTag->Offset, cache->BlockSize, start, len))
{
error = CacheRawWrite( cache,
currentTag->Offset,
cache->BlockSize,
currentTag->Buffer );
if ( EOK != error )
{
#if CACHE_DEBUG
printf( "%s - CacheRawWrite failed with error %d \n", __FUNCTION__, error );
#endif
return error;
}
currentTag->Flags &= ~kLazyWrite;
if ( remove && ((currentTag->Flags & kLockWrite) == 0))
CacheRemove( cache, currentTag );
}
currentTag = nextTag;
}
}
return EOK;
}
int CacheCopyDiskBlocks (Cache_t *cache, uint64_t from_offset, uint64_t to_offset, uint32_t len)
{
int i;
int error;
char *tmpBuffer = NULL;
uint32_t ioReqCount;
uint32_t numberOfBuffersToWrite;
if ((len % cache->DevBlockSize) ||
(from_offset % cache->DevBlockSize) ||
(to_offset % cache->DevBlockSize)) {
error = EINVAL;
goto out;
}
error = CacheFlushRange(cache, from_offset, len, 1);
if (error != EOK) goto out;
error = CacheFlushRange(cache, to_offset, len, 1);
if (error != EOK) goto out;
tmpBuffer = malloc(cache->BlockSize);
if (!tmpBuffer) {
#if CACHE_DEBUG
printf("%s(%d): malloc(%zd) failed\n", __FUNCTION__, __LINE__, (size_t)cache->BlockSize);
#endif
error = ENOMEM;
goto out;
}
ioReqCount = cache->BlockSize;
numberOfBuffersToWrite = (len + ioReqCount - 1) / ioReqCount;
for (i=0; i<numberOfBuffersToWrite; i++) {
if (i == (numberOfBuffersToWrite - 1)) {
ioReqCount = len - (i * cache->BlockSize);
}
error = CacheRawRead (cache, from_offset, ioReqCount, tmpBuffer);
if (error != EOK) goto out;
error = CacheRawWrite (cache, to_offset, ioReqCount, tmpBuffer);
if (error != EOK) goto out;
#if 0
printf ("%s: Copying %d bytes from %qd to %qd\n", __FUNCTION__, ioReqCount, from_offset, to_offset);
#endif
from_offset += ioReqCount;
to_offset += ioReqCount;
}
out:
if (tmpBuffer) {
free (tmpBuffer);
}
return error;
}
int CacheWriteBufferToDisk (Cache_t *cache, uint64_t offset, uint32_t write_len, u_char *buffer, uint32_t buf_len)
{
int error;
u_char *write_buffer = NULL;
uint32_t io_count;
uint32_t buf_offset;
uint32_t bytes_remain;
uint8_t zero_fill = false;
if (buffer == NULL) {
buf_len = 0;
}
if ((write_len % cache->DevBlockSize) ||
(offset % cache->DevBlockSize) ||
(write_len < buf_len)) {
error = EINVAL;
goto out;
}
error = CacheFlushRange(cache, offset, write_len, 1);
if (error != EOK) {
goto out;
}
io_count = (write_len < cache->BlockSize) ? write_len : cache->BlockSize;
write_buffer = malloc (io_count);
if (!write_buffer) {
#if CACHE_DEBUG
printf("%s(%d): malloc(%zd) failed\n", __FUNCTION__, __LINE__, (size_t)cache->BlockSize);
#endif
error = ENOMEM;
goto out;
}
buf_offset = 0;
while (write_len) {
if (write_len < io_count) {
io_count = write_len;
}
if (buf_offset < buf_len) {
bytes_remain = buf_len - buf_offset;
if (bytes_remain >= io_count) {
bytes_remain = io_count;
memcpy (write_buffer, buffer, bytes_remain);
} else {
memcpy (write_buffer, buffer, bytes_remain);
memset (write_buffer + bytes_remain, 0, io_count - bytes_remain);
}
buf_offset += bytes_remain;
buffer += bytes_remain;
} else {
if (zero_fill == false) {
memset (write_buffer, 0, io_count);
zero_fill = true;
}
}
error = CacheRawWrite (cache, offset, io_count, write_buffer);
if (error != EOK) goto out;
offset += io_count;
write_len -= io_count;
}
out:
if (write_buffer != NULL) {
free (write_buffer);
}
return error;
}
int CacheLookup (Cache_t *cache, uint64_t off, Tag_t **tag)
{
Tag_t * temp;
uint32_t hash = off % cache->HashSize;
int error;
*tag = NULL;
error = 0;
temp = cache->Hash[hash];
while (temp != NULL) {
if (temp->Offset == off) break;
temp = temp->Next;
}
if (temp != NULL) {
if (cache->Hash[hash] != temp) {
if (temp->Next != NULL)
temp->Next->Prev = temp->Prev;
temp->Prev->Next = temp->Next;
}
} else {
temp = (Tag_t *)calloc (sizeof (Tag_t), 1);
temp->Offset = off;
}
if (cache->Hash[hash] != temp) {
temp->Prev = NULL;
temp->Next = cache->Hash[hash];
if (temp->Next != NULL)
temp->Next->Prev = temp;
cache->Hash[hash] = temp;
}
if (temp->Buffer == NULL) {
temp->Buffer = CacheAllocBlock (cache);
if (temp->Buffer == NULL) {
error = LRUEvict (&cache->LRU, (LRUNode_t *)temp);
if (error != EOK) return (error);
temp->Buffer = CacheAllocBlock (cache);
if (temp->Buffer == NULL) {
#if CACHE_DEBUG
printf("%s(%d): CacheAllocBlock failed (FreeHead = %p, FreeSize = %u)\n", __FUNCTION__, __LINE__, cache->FreeHead, cache->FreeSize);
#endif
return (ENOMEM);
}
}
error = CacheRawRead (cache, off, cache->BlockSize, temp->Buffer);
if (error != EOK) return (error);
}
#if 0
if (temp && temp->Flags & kLockWrite) {
fprintf(stderr, "CacheLookup(%p, %llu, %p): Found cache-locked block\n", cache, off, tag);
}
#endif
*tag = temp;
return (EOK);
}
int CacheRawRead (Cache_t *cache, uint64_t off, uint32_t len, void *buf)
{
uint64_t result;
ssize_t nread;
if (off % cache->DevBlockSize) return (EINVAL);
if (len % cache->DevBlockSize) return (EINVAL);
errno = 0;
result = lseek (cache->FD_R, off, SEEK_SET);
if (result == (off_t)-1 && errno != 0)
return errno;
if (result != off) return (ENXIO);
#if CACHE_DEBUG
printf("%s: offset %llu, len %u\n", __FUNCTION__, off, len);
#endif
nread = read (cache->FD_R, buf, len);
if (nread == -1) return (errno);
if (nread == 0) return (ENXIO);
cache->DiskRead++;
return (EOK);
}
int CacheRawWrite (Cache_t *cache, uint64_t off, uint32_t len, void *buf)
{
uint64_t result;
ssize_t nwritten;
if (off % cache->DevBlockSize) return (EINVAL);
if (len % cache->DevBlockSize) return (EINVAL);
errno = 0;
result = lseek (cache->FD_W, off, SEEK_SET);
if (result == (off_t)-1 && errno != 0) return (errno);
if (result != off) return (ENXIO);
nwritten = write (cache->FD_W, buf, len);
if (nwritten == -1) return (errno);
if (nwritten == 0) return (ENXIO);
cache->DiskWrite++;
return (EOK);
}
static int LRUInit (LRU_t *lru)
{
lru->Head.Next = &lru->Head;
lru->Head.Prev = &lru->Head;
lru->Busy.Next = &lru->Busy;
lru->Busy.Prev = &lru->Busy;
return (EOK);
}
static int LRUDestroy (LRU_t *lru)
{
return (EOK);
}
static int LRUHit (LRU_t *lru, LRUNode_t *node, int age)
{
if ((node->Next != NULL) && (node->Prev != NULL)) {
node->Next->Prev = node->Prev;
node->Prev->Next = node->Next;
}
if (((Tag_t *)node)->Refs) {
node->Next = lru->Busy.Next;
node->Prev = &lru->Busy;
} else if (age) {
node->Next = &lru->Head;
node->Prev = lru->Head.Prev;
} else {
node->Next = lru->Head.Next;
node->Prev = &lru->Head;
}
node->Next->Prev = node;
node->Prev->Next = node;
return (EOK);
}
static int LRUEvict (LRU_t *lru, LRUNode_t *node)
{
LRUNode_t * temp;
while (1) {
temp = lru->Head.Prev;
if (temp == &lru->Head) {
#if CACHE_DEBUG
printf("%s(%d): empty?\n", __FUNCTION__, __LINE__);
#endif
return (ENOMEM);
}
temp->Next->Prev = temp->Prev;
temp->Prev->Next = temp->Next;
if (!((Tag_t *)temp)->Refs) break;
temp->Next = lru->Busy.Next;
temp->Prev = &lru->Busy;
temp->Next->Prev = temp;
temp->Prev->Next = temp;
}
CacheRemove ((Cache_t *)lru, (Tag_t *)temp);
return (EOK);
}
void
dumpCache(Cache_t *cache)
{
int i;
int numEntries = 0;
printf("Cache:\n");
printf("\tDevBlockSize = %u\n", cache->DevBlockSize);
printf("\tCache Block Size = %u\n", cache->BlockSize);
printf("\tHash Size = %u\n", cache->HashSize);
printf("\tHash Table:\n");
for (i = 0; i < cache->HashSize; i++) {
Tag_t *tag;
for (tag = cache->Hash[i]; tag; tag = tag->Next) {
numEntries++;
printf("\t\tOffset %llu, refs %u, Flags %#x (%skLazyWrite, %skLockWrite)\n",
tag->Offset, tag->Refs, tag->Flags,
(tag->Flags & kLazyWrite) ? "" : "no ",
(tag->Flags & kLockWrite) ? "" : "no ");
}
}
printf("\tNumber of entries: %u\n", numEntries);
return;
}