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

来源:互联网 发布:淘宝卖家拉黑买家 编辑:程序博客网 时间:2024/06/06 05:17

注: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");elseDPRINTK("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;}


0 0