您的位置:首页 > 其它

dm_cache中缓存查询与替换策略分析

2014-03-14 00:02 543 查看
注:dm-cache为兼容linux内核2.6.29的稳定版

cache_lookup函数涉及dm_cache的缓存块映射方式、查找算法及缓存替换策略,详细分析该函数有窥一斑而知全豹的效果。

注:dm-cache缓存块的几种状态

有效块(valid):与原磁盘数据块一致;

保留块(reserved):该缓存块已分配,但尚未写入数据;

脏块(dirty):脏数据块是相对于原数据块而言的,是指被修改过的,与原数据不一致的数据块;

写回块(writeback):该缓存块上的数据正被写回原磁盘;

无效块(invalid):该缓存块上数据已失效;

cache_lookup函数包含3个参数,其中dmc为指向表示dm_cache结构的指针,block为请求在原磁盘上的起始扇区。函数返回1表示命中缓存,cache_block指向请求扇区映射后所对应的缓存块。函数返回0表示不命中但是仍然分配了缓存块,并且当存在空的缓存块时,cache_block指向遇到的第一个空的缓存块。否则,如果存在干净的缓存块,则cache_block指向根据LRU替换策略进行替换后的缓存块。函数返回2表示没有命中缓存并且没有分配缓存块,此时的cache_block指向经过LRU替换策略淘汰的脏缓存块,在这种情况下,函数返回后首先应该将脏数据写回磁盘。函数返回-1表示没有命中缓存并且没有缓存块可供分配,它发生在当请求映射到的集合内所有的块的状态都为RESERVED或WRITEBACK时。

static int cache_lookup(struct cache_c *dmc, sector_t block,
sector_t *cache_block)
{
unsigned long set_number = hash_block(dmc, block);
/* 参数block表示请求的起始扇区编号,利用hash_block函数得到请求起始扇区所属的组(set_number)*/
sector_t index;
int i, res;
unsigned int cache_assoc = dmc->assoc;
/*磁盘块映射方式采用组相连,dmc->assoc表示组相连的路数,也就是说每组所包含的缓存块数*/
struct cacheblock *cache = dmc->cache;
int invalid = -1, oldest = -1, oldest_clean = -1;
unsigned long counter = ULONG_MAX, clean_counter = ULONG_MAX;

index=set_number * cache_assoc;
/*组号乘以路数,求得请求起始扇区所属的缓存块号*/
for (i=0; i<cache_assoc; i++, index++)
{
/*首先从状态为VALID和RESERVED的缓存块中进行查找*/
if (is_state(cache[index].state, VALID) ||
is_state(cache[index].state, RESERVED))
{
if (cache[index].block == block) 	/*判断是否命中?*/
{
*cache_block = index;
/* Reset all counters if the largest one is going to overflow */
if (dmc->counter == ULONG_MAX) cache_reset_counter(dmc);
cache[index].counter = ++dmc->counter;
break;
}
else
{
/* Don't consider blocks that are in the middle of copying */
if (!is_state(cache[index].state, RESERVED) &&
!is_state(cache[index].state, WRITEBACK))
{
if (!is_state(cache[index].state, DIRTY) &&
cache[index].counter < clean_counter) 	/* Alex:For clean blocks */
{
clean_counter = cache[index].counter;	/* Alex:A smart vist time record method */
oldest_clean = i;						/* Alex:Record the block index */
}
if (cache[index].counter < counter)
{
counter = cache[index].counter;
oldest = i;
}
}
}
}
else
{
if (-1 == invalid) invalid = i;
}
}

res = i < cache_assoc ? 1 : 0;
if (!res) { /* Cache miss */
if (invalid != -1) /* 选择遇到的第一个空的缓存块 */
*cache_block = set_number * cache_assoc + invalid;
else if (oldest_clean != -1) /* 使用LRU算法优先选择干净的缓存块 */
*cache_block = set_number * cache_assoc + oldest_clean;
else if (oldest != -1) { /* 使用LRU算法选择脏缓存块 */
res = 2;
*cache_block = set_number * cache_assoc + oldest;
} else {
res = -1;
}
}

/* Alex:What is about the condition that res requal to -1? */
if (-1 == res)
DPRINTK("Cache lookup: Block %llu(%lu):%s",
block, set_number, "NO ROOM");
else
DPRINTK("Cache lookup: Block %llu(%lu):%llu(%s)",
block, set_number, *cache_block,
1 == res ? "HIT" : (0 == res ? "MISS" : "WB NEEDED"));
return res;
}


dm_cache对原始的LRU算法进行了改进,和一条队列的LRU算法不同,你可以认为改进后的LRU算法实际上使用了两条队列,分别管理最近干净块和脏块,这两条队列都是按照LRU替换算法进行管理。优先从干净队列中选择缓存块,如果没有干净块则从脏块中选择,将访问次数(cache.count)最少的缓存块淘汰掉。

在cache_lookup函数中,通过hash_block函数计算请求扇区映射到缓存上所对应的组的编号。

static unsigned long hash_block(struct cache_c *dmc, sector_t block)
{
unsigned long set_number, value;

value = (unsigned long)(block >> (dmc->block_shift +
dmc->consecutive_shift));
set_number = hash_long(value, dmc->bits) / dmc->assoc;

return set_number;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: