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

注: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; icounter == 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;
}


你可能感兴趣的:(存储技术原理)