zoukankan      html  css  js  c++  java
  • linux内核源码阅读之facebook硬盘加速flashcache之三


    上一节讲到在刷缓存的时候会调用new_kcahed_job创建kcached_job,由此我们也可以看到cache数据块与磁盘数据的对应关系。上一篇:http://blog.csdn.net/liumangxiong/article/details/11726651
    现在继续从new_kcached_job函数中挖掘有用的信息。那就是cache块跟磁盘上扇区是怎么对应起来的?即329行的为什么要写的disk.sector是后面这个值呢?
              job->disk.sector = dmc->cache[index].dbn;
    最这里是时候揭开变量dmc也就是结构体struct cache_c的真面目了。dmc可以理解成device mapper context或者device mapper cache。先看struct cache_c
    134struct cache_c {
    135	struct dm_target	*tgt;
    136	
    137	struct dm_dev 		*disk_dev;   /* Source device */
    138	struct dm_dev 		*cache_dev; /* Cache device */
    139
    140#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27)
    141	struct kcopyd_client *kcp_client; /* Kcopyd client for writing back data */
    142#else
    143	struct dm_kcopyd_client *kcp_client; /* Kcopyd client for writing back data */
    144	struct dm_io_client *io_client; /* Client memory pool*/
    145#endif
    146
    147	spinlock_t		cache_spin_lock;
    148
    149	struct cacheblock	*cache;	/* Hash table for cache blocks */
    150	struct cache_set	*cache_sets;
    151	struct cache_md_sector_head *md_sectors_buf;
    152	
    153	sector_t size;			/* Cache size */
    154	unsigned int assoc;		/* Cache associativity */
    155	unsigned int block_size;	/* Cache block size */
    156	unsigned int block_shift;	/* Cache block size in bits */
    157	unsigned int block_mask;	/* Cache block mask */
    158	unsigned int consecutive_shift;	/* Consecutive blocks size in bits */
    159
    160	wait_queue_head_t destroyq;	/* Wait queue for I/O completion */
    161	/* XXX - Updates of nr_jobs should happen inside the lock. But doing it outside
    162	   is OK since the filesystem is unmounted at this point */
    163	atomic_t nr_jobs;		/* Number of I/O jobs */
    164	atomic_t fast_remove_in_prog;
    165
    166	int	dirty_thresh_set;	/* Per set dirty threshold to start cleaning */
    167	int	max_clean_ios_set;	/* Max cleaning IOs per set */
    168	int	max_clean_ios_total;	/* Total max cleaning IOs */
    169	int	clean_inprog;
    170	int	sync_index;
    171	int	nr_dirty;
    172
    173	int	md_sectors;		/* Numbers of metadata sectors, including header */
    174
    175	/* Stats */
    176	unsigned long reads;		/* Number of reads */
    177	unsigned long writes;		/* Number of writes */
    178	unsigned long read_hits;	/* Number of cache hits */
    179	unsigned long write_hits;	/* Number of write hits (includes dirty write hits) */
    180	unsigned long dirty_write_hits;	/* Number of "dirty" write hits */
    181	unsigned long replace;		/* Number of cache replacements */
    182	unsigned long wr_replace;
    183	unsigned long wr_invalidates;	/* Number of write invalidations */
    184	unsigned long rd_invalidates;	/* Number of read invalidations */
    185	unsigned long pending_inval;	/* Invalidations due to concurrent ios on same block */
    186	unsigned long cached_blocks;	/* Number of cached blocks */
    187#ifdef FLASHCACHE_DO_CHECKSUMS
    188	unsigned long checksum_store;
    189	unsigned long checksum_valid;
    190	unsigned long checksum_invalid;
    191#endif
    192	unsigned long enqueues;		/* enqueues on pending queue */
    193	unsigned long cleanings;
    194	unsigned long noroom;		/* No room in set */
    195	unsigned long md_write_dirty;	/* Metadata sector writes dirtying block */
    196	unsigned long md_write_clean;	/* Metadata sector writes cleaning block */
    197	unsigned long pid_drops;
    198	unsigned long pid_adds;
    199	unsigned long pid_dels;
    200	unsigned long expiry;
    201	unsigned long front_merge, back_merge;	/* Write Merging */
    202	unsigned long uncached_reads, uncached_writes;
    203	unsigned long disk_reads, disk_writes;
    204	unsigned long ssd_reads, ssd_writes;
    205	unsigned long ssd_readfills, ssd_readfill_unplugs;
    206
    207	unsigned long clean_set_calls;
    208	unsigned long clean_set_less_dirty;
    209	unsigned long clean_set_fails;
    210	unsigned long clean_set_ios;
    211	unsigned long set_limit_reached;
    212	unsigned long total_limit_reached;
    213
    214	/* Errors */
    215	int	disk_read_errors;
    216	int	disk_write_errors;
    217	int	ssd_read_errors;
    218	int	ssd_write_errors;
    219	int	memory_alloc_errors;
    220
    221#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
    222	struct work_struct delayed_clean;
    223#else
    224	struct delayed_work delayed_clean;
    225#endif
    226
    227	/* State for doing readfills (batch writes to ssd) */
    228	int readfill_in_prog;
    229	struct kcached_job *readfill_queue;
    230	struct work_struct readfill_wq;
    231
    232	unsigned long pid_expire_check;
    233
    234	struct flashcache_cachectl_pid *blacklist_head, *blacklist_tail;
    235	struct flashcache_cachectl_pid *whitelist_head, *whitelist_tail;
    236	int num_blacklist_pids, num_whitelist_pids;
    237	unsigned long blacklist_expire_check, whitelist_expire_check;
    238	
    239	struct cache_c	*next_cache;
    240
    241	char cache_devname[DEV_PATHLEN];
    242	char disk_devname[DEV_PATHLEN];
    243};
    这个多field,如果一个挨一个看一遍,估计我都要睡着了。就像看书一样,如果从第一章看到最后一章,看过之后脑子里总是一片空白。如果是先看目录,带着疑问找自己感兴趣的地方,不时回味一下为什么是这样子,不失为一种愉快并且有效的阅读方式。
    那么在看这个数据结构之前,头脑风暴一下产生了以下的疑问:
    1)源设备和目的设备分别是什么?映射后数据流是怎么样的?
    2)缓存大小是多少?块大小多少?块是怎么组织的
    3)脏数据刷新机制是什么样的?水位线是多少
    第137,138行表示的磁盘和SSD盘,即目的盘和缓存盘。这里必须十分清楚缓存的概念,一般情况下讲到缓存都是在内存中的,但flashcache中提到缓存块的时候要记住是写在SSD盘上的。数据流的变化就是多了一层flashcache device,在命中情况下直接返回,不到磁盘层。
    缓存大小由153行size表示,但要注意的是,这里的size既不是以字节为单位,而不是以sector为单位,而是以cache数据块为单位的。每个cache数据块为block_size大小。cache块的组织是以集合为单位,每个集合有assoc个块,简单地理解为二维数组,第一维得到的是一个集合,第二维得到集合内块数据。为了理解这个结构,来看函数flashcache_lookup,
    543/* 
    544 * dbn is the starting sector, io_size is the number of sectors.
    545 */
    546static int 
    547flashcache_lookup(struct cache_c *dmc, struct bio *bio, int *index)
    548{
    549	sector_t dbn = bio->bi_sector;
    550#if DMC_DEBUG
    551	int io_size = to_sector(bio->bi_size);
    552#endif
    553	unsigned long set_number = hash_block(dmc, dbn);
    554	int invalid, oldest_clean = -1;
    555	int start_index;
    556
    557	start_index = dmc->assoc * set_number;
    558	DPRINTK("Cache lookup : dbn %llu(%lu), set = %d",
    559		dbn, io_size, set_number);
    560	find_valid_dbn(dmc, dbn, start_index, index);
    561	if (*index > 0) {
    562		DPRINTK("Cache lookup HIT: Block %llu(%lu): VALID index %d",
    563			     dbn, io_size, *index);
    564		/* We found the exact range of blocks we are looking for */
    565		return VALID;
    566	}
    567	invalid = find_invalid_dbn(dmc, start_index);
    568	if (invalid == -1) {
    569		/* We didn't find an invalid entry, search for oldest valid entry */
    570		find_reclaim_dbn(dmc, start_index, &oldest_clean);
    571	}
    572	/* 
    573	 * Cache miss :
    574	 * We can't choose an entry marked INPROG, but choose the oldest
    575	 * INVALID or the oldest VALID entry.
    576	 */
    577	*index = start_index + dmc->assoc;
    578	if (invalid != -1) {
    579		DPRINTK("Cache lookup MISS (INVALID): dbn %llu(%lu), set = %d, index = %d, start_index = %d",
    580			     dbn, io_size, set_number, invalid, start_index);
    581		*index = invalid;
    582	} else if (oldest_clean != -1) {
    583		DPRINTK("Cache lookup MISS (VALID): dbn %llu(%lu), set = %d, index = %d, start_index = %d",
    584			     dbn, io_size, set_number, oldest_clean, start_index);
    585		*index = oldest_clean;
    586	} else {
    587		DPRINTK_LITE("Cache read lookup MISS (NOROOM): dbn %llu(%lu), set = %d",
    588			dbn, io_size, set_number);
    589	}
    590	if (*index < (start_index + dmc->assoc))
    591		return INVALID;
    592	else {
    593		dmc->noroom++;
    594		return -1;
    595	}
    596}
    第549行dbn是bio的起始扇区,第553行set_number就是这个扇区映射到的缓存集合,简单地看一下hash_block:
    444/*
    445 * Map a block from the source device to a block in the cache device.
    446 */
    447static unsigned long 
    448hash_block(struct cache_c *dmc, sector_t dbn)
    449{
    450	unsigned long set_number, value;
    451
    452	value = (unsigned long)
    453		(dbn >> (dmc->block_shift + dmc->consecutive_shift));
    454	set_number = value % (dmc->size >> dmc->consecutive_shift);
    455	DPRINTK("Hash: %llu(%lu)->%lu", dbn, value, set_number);
    456	return set_number;
    457}
    看注释,源设备块映射到cache设备块。看452行,1<<dmc->block_shift就是块大小,1<<dmc->consecutive_shift就是每个集合大小,所以得到的value就是这个扇区在哪个集合上。但直接返回这个值还不行,一般情况下源设备比缓存大得多,所以源设备上多处位置会映射到缓存的一个集合上。所以有了454行,源设备的多个集合映射到缓存的同一个集合上,(dmc->size >> dmc->consecutive_shift)就表示集合的个数。
    继续flashcache_lookup第557行,start_index就是这个集合第一个cache块的下标,560行find_valid_db就是查找缓存是否命中,如果命中的话,由index返回,如果不命中,返回-1。第561行就是判断缓存命中,如果命中就直接返回;不命中的话就继续567行查找可用的缓存块。第578行是找到可用缓存块,582就是找到干净的回收缓存块,586就是没有找到可用的缓存块。
    回到cache_c结构中来,接着讲刷新。刷新是由第224行工作队列控制的struct delayed_work delayed_clean;这个队列为什么是delayed_work,搜下这个队列的调用,在函数flashcache_clean_set中,
              if (do_delayed_clean)
                   schedule_delayed_work(&dmc->delayed_clean, 1*HZ);
    那为什么是延迟1秒调用,看do_delayed_clean
              if (dmc->cache_sets[set].nr_dirty > dmc->dirty_thresh_set)
                   do_delayed_clean = 1;
    这里的意思就是超过阈值的时候延迟1秒再检查一遍,为什么不立即做而要延迟呢?这个函数再往回看就知道了,原来下发的请求已经超过某一个阈值,这个时候就不再下发。
    除了这个队列之外,还需要有一些阈值来控制。从166行到171行就是这些相关的设置。
    nr_dirty是当前集合里脏cache块数
    dirty_thresh_set 是超过这个界面就要开始将脏数据写回磁盘 
    max_clean_ios_set 是单个集合下发写数据块的请求个数
    max_clean_ios_total 是整个缓存下发写数据块的请求个数
    clean_inprog 是已经下发的写数据块的请求个数
    到这里再回去扫描一下cache_c结构,还有一些IO统计和错误统计的field。
    每一场好戏都有精彩好戏在后头,cache_c也不例外,接着请三巨头隆重上场:
         struct cacheblock     *cache;     /* Hash table for cache blocks */
         struct cache_set     *cache_sets;
         struct cache_md_sector_head *md_sectors_buf;
    第一个结构是cache块在内存中的表示,对应SSD上的是flash_cacheblock。第二个cache_set就是之前一直提到的集合。第三个用于flash_cacheblock刷新,即管理结构从内存cacheblock写到SSD的flash_cacheblock。下面逐一来看这三个结构体:
    111/* Cache block metadata structure */
    112struct cacheblock {
    113	u_int16_t	cache_state;
    114	int16_t 	nr_queued;	/* jobs in pending queue */
    115	u_int16_t	lru_prev, lru_next;
    116	sector_t 	dbn;	/* Sector number of the cached block */
    117#ifdef FLASHCACHE_DO_CHECKSUMS
    118	u_int64_t 	checksum;
    119#endif
    120	struct pending_job *head;
    121};
    

    cache_state; cache块的状态
     nr_queued;     /* jobs in pending queue */ 等待工作个数
     lru_prev, lru_next;  按LRU排序,指向前一个和后一个,注意这里是下标
     dbn;     /* Sector number of the cached block */  对应磁盘的扇区
     checksum;   校验
     struct pending_job *head;  等待工作
    第二个数据结构:
    123struct cache_set {
    124	u_int32_t		set_fifo_next;
    125	u_int32_t		set_clean_next;
    126	u_int32_t		clean_inprog;
    127	u_int32_t		nr_dirty;
    128	u_int16_t		lru_head, lru_tail;
    129};
    

    第三个数据结构:
    344/* 
    345 * We have one of these for *every* cache metadata sector, to keep track
    346 * of metadata ios in progress for blocks covered in this sector. Only
    347 * one metadata IO per sector can be in progress at any given point in 
    348 * time
    349 */
    350struct cache_md_sector_head {
    351	u_int32_t		nr_in_prog;
    352	struct kcached_job	*pending_jobs, *md_io_inprog;
    353};
    看注释,每一个cache metadata扇区对应一个struct cache_md_sector_head结构,用以追踪这个扇区上的IO,这个扇区的IO来自该扇区对应的每一个cache块的状态变化。每一次只允许一个IO在下发。在初始化时,nr_in_prog为0,两个队列也都为零。其中的一个cache块发生变化并且状态要更新到SSD中,这时创建一个job并挂入到pending_jobs,下发时将nr_in_prog置为1,并将job从pending_jobs移到md_io_inprog,如果job下发过程中又有其他job下发,就挂到pending_jobs,等md_io_inprog处理完成再继续下一次下发过程。
    到这里,我们把flashcache重要的数据结构都过了一遍。下一节开始介绍flashcache的数据流。
  • 相关阅读:
    ASP.NET MVC案例——————拦截器
    Windows Azure Virtual Network (10) 使用Azure Access Control List(ACL)设置客户端访问权限
    Windows Azure Storage (20) 使用Azure File实现共享文件夹
    Windows Azure HandBook (5) Azure混合云解决方案
    Windows Azure Service Bus (6) 中继(Relay On) 使用VS2013开发Service Bus Relay On
    Azure PowerShell (9) 使用PowerShell导出订阅下所有的Azure VM的Public IP和Private IP
    Windows Azure Service Bus (5) 主题(Topic) 使用VS2013开发Service Bus Topic
    Azure China (9) 在Azure China配置CDN服务
    Windows Azure Storage (19) 再谈Azure Block Blob和Page Blob
    Windows Azure HandBook (4) 分析Windows Azure如何处理Session
  • 原文地址:https://www.cnblogs.com/riskyer/p/3325072.html
Copyright © 2011-2022 走看看