Browse Source

Change cache replacement algorithm to Least Recenrtly Used

main
PaulStoffregen 9 years ago
parent
commit
d9c6f87cee
4 changed files with 139 additions and 99 deletions
  1. +6
    -7
      SD_t3.h
  2. +108
    -85
      cache_t3.cpp
  3. +2
    -0
      card_t3.cpp
  4. +23
    -7
      file_t3.cpp

+ 6
- 7
SD_t3.h View File

// no longer exists. // no longer exists.
SDCache(void) { item = NULL; } SDCache(void) { item = NULL; }
~SDCache(void) { release(); } ~SDCache(void) { release(); }
typedef struct {
typedef struct cache_struct {
SDClass::sector_t data; SDClass::sector_t data;
uint32_t lba; uint32_t lba;
cache_struct * next;
uint8_t usagecount; uint8_t usagecount;
uint8_t priority;
uint8_t flags; uint8_t flags;
} cache_t; } cache_t;
SDClass::sector_t * read(uint32_t lba, bool is_fat=false); SDClass::sector_t * read(uint32_t lba, bool is_fat=false);
bool read(uint32_t lba, void *buffer); bool read(uint32_t lba, void *buffer);
SDClass::sector_t * alloc(uint32_t lba);
void priority(signed int n);
void priority(uint32_t lba, signed int n);
cache_t * get(uint32_t lba, bool allocate=true);
void dirty(void); void dirty(void);
void flush(void); void flush(void);
void release(void); void release(void);
cache_t * find(uint32_t lba);
cache_t * empty();
cache_t * item; cache_t * item;
static cache_t *cache_list;
static cache_t cache[SD_CACHE_SIZE]; static cache_t cache[SD_CACHE_SIZE];
static void init(void);
static void print_cache(void);
friend class SDClass; friend class SDClass;
friend class File; friend class File;
}; };

+ 108
- 85
cache_t3.cpp View File

#define cache_t SDCache::cache_t #define cache_t SDCache::cache_t
#define sector_t SDClass::sector_t #define sector_t SDClass::sector_t


cache_t *SDCache::cache_list = NULL;
cache_t SDCache::cache[SD_CACHE_SIZE]; cache_t SDCache::cache[SD_CACHE_SIZE];


#define CACHE_FLAG_HAS_DATA 1 #define CACHE_FLAG_HAS_DATA 1
} }
#endif #endif


void SDCache::print_cache(void)
{
#if 0
const cache_t *end=cache+SD_CACHE_SIZE;
for (cache_t *c = cache; c < end; c++) {
Serial.printf(" cache index %u, lba= %u, ucount=%u, flags=%u\n",
c - cache, c->lba, c->usagecount, c->flags);
}
Serial.print(" cache order:");
for (cache_t *c = cache_list; c; c = c->next) {
Serial.printf(" %u ->", c - cache);
}
Serial.println();
#endif
}

// Read a sector into the cache. If the sector is already cached, // Read a sector into the cache. If the sector is already cached,
// of course no actual read occurs. This is the primary function // of course no actual read occurs. This is the primary function
// used to access the SD card. // used to access the SD card.
//Serial.printf("cache read: lba = %d\n", lba); //Serial.printf("cache read: lba = %d\n", lba);
SPI.beginTransaction(SD_SPI_SPEED); SPI.beginTransaction(SD_SPI_SPEED);
// does the cache already have the sector? // does the cache already have the sector?
cache_t *c = find(lba);
cache_t *c = get(lba);
if (c) { if (c) {
ret = &c->data;
} else {
c = empty();
if (c != NULL) {
// TODO: if dirty, write to SD card
if (c->flags & CACHE_FLAG_HAS_DATA) {
//Serial.printf(" cache hit, lba=%u\n", lba);
ret = &c->data;
} else {
if (SDClass::sd_read(lba, &c->data)) { if (SDClass::sd_read(lba, &c->data)) {
item = c;
c->lba = lba;
c->usagecount = 1;
c->flags = CACHE_FLAG_HAS_DATA; c->flags = CACHE_FLAG_HAS_DATA;
if (is_fat) c->flags |= CACHE_FLAG_IS_FAT; if (is_fat) c->flags |= CACHE_FLAG_IS_FAT;
ret = &c->data; ret = &c->data;
#ifdef PRINT_SECTORS
Serial.printf("cache read %u\n", lba);
print_sector(&c->data);
#endif
//Serial.printf(" cache miss, lba=%u\n", lba);
} else {
//Serial.printf(" cache miss: read error, lba=%u\n", lba);
} }
} }
} else {
//Serial.printf(" cache full & all in use\n", lba);
} }
//if (c) slot = c - cache, ucount = c->usagecount;
SPI.endTransaction(); SPI.endTransaction();
//if (ret) {
//Serial.printf("read %u, %u, slot %u\n", lba, ucount, slot);
//} else {
//Serial.printf("read %u, FAIL\n", lba);
//}
//print_cache();
return ret; return ret;
} }


bool ret = true; bool ret = true;


SPI.beginTransaction(SD_SPI_SPEED); SPI.beginTransaction(SD_SPI_SPEED);
cache_t *c = find(lba);
if (!c) ret = SDClass::sd_read(lba, buffer);
cache_t *c = get(lba, false);
if (!c || !(c->flags & CACHE_FLAG_HAS_DATA)) {
ret = SDClass::sd_read(lba, buffer);
}
SPI.endTransaction(); SPI.endTransaction();
if (c) memcpy(buffer, &c->data, 512);
if (c) {
if ((c->flags & CACHE_FLAG_HAS_DATA)) {
memcpy(buffer, &c->data, 512);
release();
return true;
}
release();
}
return ret; return ret;
} }




sector_t * SDCache::alloc(uint32_t lba)
// locate a sector in the cache.
cache_t * SDCache::get(uint32_t lba, bool allocate)
{ {
cache_t *c, *p=NULL, *last=NULL, *plast=NULL;


return NULL;
}

void SDCache::priority(signed int n)
{
if (!item) return;
if (n > 0) {
__disable_irq();
signed int pri = (int)(item->priority) + n;
if (pri > 255) pri = 255;
item->priority = pri;
__enable_irq();
} else {
__disable_irq();
signed int pri = (int)(item->priority) + n;
if (pri < 0) pri = 0;
item->priority = pri;
__enable_irq();
// TODO: move initialization to a function called when the SD card is initialized
if (cache_list == NULL) init();
// have we already acquired a cache entry?
if (item) {
// if it's the desired block, use it
if (item->lba == lba) return item;
// if not, release our hold on it
release();
} }
__disable_irq();
c = cache_list;
do {
if (c->lba == lba) {
if (p) {
p->next = c->next;
c->next = cache_list;
cache_list = c;
}
c->usagecount++;
__enable_irq();
item = c;
return item;
}
if (c->usagecount == 0) {
plast = p;
last = c;
}
p = c;
c = c->next;
} while (c);
if (allocate && last) {
if (plast) {
plast->next = last->next;
last->next = cache_list;
cache_list = last;
}
last->usagecount = 1;
// TODO: flush if dirty
last->lba = lba;
last->flags = 0;
item = last;
}
__enable_irq();
return item;
} }


void SDCache::priority(uint32_t lba, signed int n)

void SDCache::init(void)
{ {
// TODO: if any a specific sector is cached, adjust its priority
cache_t *c = cache;
cache_t *end = c + SD_CACHE_SIZE;
//Serial.println("cache init");
__disable_irq();
do {
c->lba = 0xFFFFFFFF;
c->usagecount = 0;
c->flags = 0;
c->next = c + 1;
c = c + 1;
} while (c < end);
c--;
c->next = NULL;
cache_list = cache;
__enable_irq();
} }



void SDCache::dirty(void) void SDCache::dirty(void)
{ {
__disable_irq(); __disable_irq();
item->usagecount |= CACHE_FLAG_IS_DIRTY;
item->flags |= CACHE_FLAG_IS_DIRTY;
__enable_irq(); __enable_irq();
} }


} }
} }


cache_t * SDCache::find(uint32_t lba)
{
//Serial.printf("SDCache::find, lba=%n\n", lba);
// have we already acquired a cache entry?
if (item) {
//Serial.printf(" item exists, lba=%d\n", item->lba);
// if it's the desired block, use it
if (item->lba == lba) return item;
// if not, release our hold on it
//Serial.printf("cache find release\n");
item->usagecount--;
item = NULL;
}
// does the cache already have the sector we want?
const cache_t *end=cache+SD_CACHE_SIZE;
for (cache_t *c = cache; c < end; c++) {
if ((c->flags) && (c->lba == lba)) {
//Serial.printf(" item found\n");
item = c;
c->usagecount++;
return c;
}
}
//Serial.printf(" item not found\n");
// the desired sector isn't in the cache
return NULL;
}


cache_t * SDCache::empty(void)
{
const cache_t *end=cache+SD_CACHE_SIZE;
cache_t *useme = NULL;
uint32_t lowest_priority = 0xFF;


for (cache_t *c = cache; c < end; c++) {
if (c->usagecount == 0 && c->priority < lowest_priority) {
useme = c;
lowest_priority = c->priority;
if (lowest_priority == 0) break;
}
}
return useme;
}





#endif #endif
#endif #endif

+ 2
- 0
card_t3.cpp View File

uint8_t r1 = recv_r1(); uint8_t r1 = recv_r1();
if (r1 != 0) { if (r1 != 0) {
end_cmd(); end_cmd();
//Serial.println(" sd_read fail r1");
return false; return false;
} }
while (1) { while (1) {
if (token == 0xFE) break; if (token == 0xFE) break;
if (token != 0xFF) { if (token != 0xFF) {
end_cmd(); end_cmd();
//Serial.println(" sd_read fail token");
return false; return false;
} }
// TODO: timeout // TODO: timeout

+ 23
- 7
file_t3.cpp View File

uint8_t *dest = (uint8_t *)buf; uint8_t *dest = (uint8_t *)buf;
uint32_t lba = custer_to_sector(current_cluster); uint32_t lba = custer_to_sector(current_cluster);
uint32_t sindex = cluster_offset(offset); uint32_t sindex = cluster_offset(offset);

//Serial.printf(" read %u at %u (%X)\n", size, offset, offset);

lba += sindex >> 9; lba += sindex >> 9;
sindex &= 511; sindex &= 511;
if (sindex) { if (sindex) {
do { do {
SDCache cache; SDCache cache;
sector_t *sector = cache.read(lba); sector_t *sector = cache.read(lba);
if (!sector) return 0;
if (!sector) {
//Serial.println(" read err1, unable to read");
return 0;
}
uint32_t n = 512 - sindex; uint32_t n = 512 - sindex;
if (size < n) { if (size < n) {
// read does not consume all of the sector // read does not consume all of the sector
memcpy(dest, sector->u8 + sindex, size); memcpy(dest, sector->u8 + sindex, size);
offset += size; offset += size;
cache.priority(+1);
//cache.priority(+1);
return size; return size;
} else { } else {
// read fully consumes this sector // read fully consumes this sector
dest += n; dest += n;
count = n; count = n;
offset += n; offset += n;
cache.priority(-1);
//cache.priority(-1);
} }
} while (0); } while (0);
if (is_new_cluster(++lba)) { if (is_new_cluster(++lba)) {
if (!next_cluster()) return count;
if (!next_cluster()) {
//Serial.print(" read err1, next cluster");
return count;
}
} }
if (count >= size) return count; if (count >= size) return count;
} }
if (n < 512) { if (n < 512) {
// only part of a sector is needed // only part of a sector is needed
sector_t *sector = cache.read(lba); sector_t *sector = cache.read(lba);
if (!sector) return count;
if (!sector) {
//Serial.println(" read err2, unable to read");
return count;
}
memcpy(dest, sector->u8, n); memcpy(dest, sector->u8, n);
offset += n; offset += n;
count += n; count += n;
cache.priority(+1);
//cache.priority(+1);
return count; return count;
} else { } else {
// a full sector is required // a full sector is required
} }
} while (0); } while (0);
if (is_new_cluster(++lba)) { if (is_new_cluster(++lba)) {
if (!next_cluster()) return count;
if (!next_cluster()) {
//Serial.print(" read err2, next cluster");
return count;
}
} }
if (count >= size) return count; if (count >= size) return count;
} }
if (type > FILE_WRITE) return false; if (type > FILE_WRITE) return false;
if (pos > length) return false; if (pos > length) return false;


//Serial.printf(" seek to %u\n", pos);
uint32_t save_cluster = current_cluster; uint32_t save_cluster = current_cluster;
uint32_t count; uint32_t count;
// TODO: if moving to a new lba, lower cache priority // TODO: if moving to a new lba, lower cache priority

Loading…
Cancel
Save