正常流程到flashcache_map的1623行或1625行,按顺序先看读流程:
1221static void
1222flashcache_read(struct cache_c *dmc, struct bio *bio)
1223{
1224 int index;
1225 int res;
1226 struct cacheblock *cacheblk;
1227 int queued;
1228
1229 DPRINTK("Got a %s for %llu %u bytes)",
1230 (bio_rw(bio) == READ ? "READ":"READA"),
1231 bio->bi_sector, bio->bi_size);
1232
1233 spin_lock_irq(&dmc->cache_spin_lock);
1234 res = flashcache_lookup(dmc, bio, &index);
1235 /*
1236 * Handle Cache Hit case first.
1237 * We need to handle 2 cases, BUSY and !BUSY. If BUSY, we enqueue the
1238 * bio for later.
1239 */
1240 if (res > 0) {
1241 cacheblk = &dmc->cache[index];
1242 if ((cacheblk->cache_state & VALID) &&
1243 (cacheblk->dbn == bio->bi_sector)) {
1244 flashcache_read_hit(dmc, bio, index);
1245 return;
1246 }
1247 }
1248 /*
1249 * In all cases except for a cache hit (and VALID), test for potential
1250 * invalidations that we need to do.
1251 */
1252 queued = flashcache_inval_blocks(dmc, bio);
1253 if (queued) {
1254 if (unlikely(queued < 0))
1255 flashcache_bio_endio(bio, -EIO);
1256 spin_unlock_irq(&dmc->cache_spin_lock);
1257 return;
1258 }
1259 if (res == -1 || flashcache_uncacheable(dmc)) {
1260 /* No room or non-cacheable */
1261 spin_unlock_irq(&dmc->cache_spin_lock);
1262 DPRINTK("Cache read: Block %llu(%lu):%s",
1263 bio->bi_sector, bio->bi_size, "CACHE MISS & NO ROOM");
1264 if (res == -1)
1265 flashcache_clean_set(dmc, hash_block(dmc, bio->bi_sector));
1266 /* Start uncached IO */
1267 flashcache_start_uncached_io(dmc, bio);
1268 return;
1269 }
1270 /*
1271 * (res == INVALID) Cache Miss
1272 * And we found cache blocks to replace
1273 * Claim the cache blocks before giving up the spinlock
1274 */
1275 if (dmc->cache[index].cache_state & VALID)
1276 dmc->replace++;
1277 else
1278 dmc->cached_blocks++;
1279 dmc->cache[index].cache_state = VALID | DISKREADINPROG;
1280 dmc->cache[index].dbn = bio->bi_sector;
1281 spin_unlock_irq(&dmc->cache_spin_lock);
1282
1283 DPRINTK("Cache read: Block %llu(%lu), index = %d:%s",
1284 bio->bi_sector, bio->bi_size, index, "CACHE MISS & REPLACE");
1285 flashcache_read_miss(dmc, bio, index);
1286}

我非常喜欢flashcache这种小资的感觉,每个函数都比较短,大部分都没有超过100行的。不像neil大哥写的md代码的函数动则几百行,上千行,总是望啊望啊望不到边。当然不是说我不喜欢neil的代码,实际上他的代码是非常非常好的,因为md代码已经有十多年的历史了,大的框架仍然没有太大的改变,仍能保持那么优雅已经是十分难得了。最新版本的md还加了许多新功能,像bad block和replacement机制都是非常实用的。所以对于一名优秀的软件工程师来说,并不在于写了多少行代码,而是编写的软件运行在多少台机器上,为用户创造了多少价值。
第1234行是查找bio是否命中,flashcache_lookup函数我们在之前的文章里已经分析过了。第1244行是命中的情况,我们跟进看看
1119static void
1120flashcache_read_hit(struct cache_c *dmc, struct bio* bio, int index)
1121{
1122 struct cacheblock *cacheblk;
1123 struct pending_job *pjob;
1124
1125 cacheblk = &dmc->cache[index];
1126 if (!(cacheblk->cache_state & BLOCK_IO_INPROG) && (cacheblk->head == NULL)) {
1127 struct kcached_job *job;
1128
1129 cacheblk->cache_state |= CACHEREADINPROG;
1130 dmc->read_hits++;
1131 spin_unlock_irq(&dmc->cache_spin_lock);
1132 DPRINTK("Cache read: Block %llu(%lu), index = %d:%s",
1133 bio->bi_sector, bio->bi_size, index, "CACHE HIT");
1134 job = new_kcached_job(dmc, bio, index);
1135 if (unlikely(sysctl_flashcache_error_inject & READ_HIT_JOB_ALLOC_FAIL)) {
1136 if (job)
1137 flashcache_free_cache_job(job);
1138 job = NULL;
1139 sysctl_flashcache_error_inject &= ~READ_HIT_JOB_ALLOC_FAIL;
1140 }
1141 if (unlikely(job == NULL)) {
1142 /*
1143 * We have a read hit, and can't allocate a job.
1144 * Since we dropped the spinlock, we have to drain any
1145 * pending jobs.
1146 */
1147 DMERR("flashcache: Read (hit) failed ! Can't allocate memory for cache IO, block %lu",
1148 cacheblk->dbn);
1149 flashcache_bio_endio(bio, -EIO);
1150 spin_lock_irq(&dmc->cache_spin_lock);
1151 flashcache_free_pending_jobs(dmc, cacheblk, -EIO);
1152 cacheblk->cache_state &= ~(BLOCK_IO_INPROG);
1153 spin_unlock_irq(&dmc->cache_spin_lock);
1154 } else {
1155 job->action = READCACHE; /* Fetch data from cache */
1156 atomic_inc(&dmc->nr_jobs);
1157 dmc->ssd_reads++;
1158 dm_io_async_bvec(1, &job->cache, READ,
1159 bio->bi_io_vec + bio->bi_idx,
1160 flashcache_io_callback, job);
1161 flashcache_unplug_device(dmc->cache_dev->bdev);
1162 }
1163 } else {
1164 pjob = flashcache_alloc_pending_job(dmc);
1165 if (unlikely(sysctl_flashcache_error_inject & READ_HIT_PENDING_JOB_ALLOC_FAIL)) {
1166 if (pjob) {
1167 flashcache_free_pending_job(pjob);
1168 pjob = NULL;
1169 }
1170 sysctl_flashcache_error_inject &= ~READ_HIT_PENDING_JOB_ALLOC_FAIL;
1171 }
1172 if (pjob == NULL)
1173 flashcache_bio_endio(bio, -EIO);
1174 else
1175 flashcache_enq_pending(dmc, bio, index, READCACHE, pjob);
1176 spin_unlock_irq(&dmc->cache_spin_lock);
1177 }
1178}

首先获取这个cache块管理结构,第1126行判断cache块不忙的情况,进入1129行设置状态为从cache读,第1134行创建一个kcached_job,在1141行申请kcached_job失败时就对bio返回失败。申请成功到1155行将kcached_job设置为READCACHE,再调用dm_io_async_bvec下发请求,当请求回来时就会调用这里设置的回调函数flashcache_io_callback。再继续看读SSD返回是怎么处理的?我们只看该函数是对READCACHE的处理:

151	case READCACHE:
152 DPRINTK("flashcache_io_callback: READCACHE %d",
153 index);
154 spin_lock_irqsave(&dmc->cache_spin_lock, flags);
155 if (unlikely(sysctl_flashcache_error_inject & READCACHE_ERROR)) {
156 job->error = error = -EIO;
157 sysctl_flashcache_error_inject &= ~READCACHE_ERROR;
158 }
159 VERIFY(cacheblk->cache_state & CACHEREADINPROG);
160 spin_unlock_irqrestore(&dmc->cache_spin_lock, flags);
161 if (unlikely(error))
162 dmc->ssd_read_errors++;
163#ifdef FLASHCACHE_DO_CHECKSUMS
164 if (likely(error == 0)) {
165 if (flashcache_validate_checksum(job)) {
166 DMERR("flashcache_io_callback: Checksum mismatch at disk offset %lu",
167 job->disk.sector);
168 error = -EIO;
169 }
170 }
171#endif
172 flashcache_bio_endio(bio, error);
173 break;

实际上真正有意义的就是第172行,将请求done回去了。这样我们就完成的一次读命中的处理。

读命中处理还有一种情况就是第1163行cache块忙的情况,这个时候就申请一个pending_job,挂到cache块上,等cache块上一个请求回来的时候调度。

继续回到flashcache_read,看不命中的情况。
到第1259行flashcache_uncacheable函数是管理命令相关的,加了黑名单之后会跳过cache,直接下发到磁盘。
到1264行res是flashcache_lookup返回的,为-1就表示获取不到可用的cache块,这时就调用flashcache_clean_set清除一下脏块。但获取不到cache块并不是说请求就结束了,还得下发到磁盘,1267行flashcache_start_uncached_io将请求直接下发到磁盘。
第1275行到1278行是统计信息,根据这些信息可以知道flashcache的运行状况,用于flashcache的性能优化。
接着1279行设置cache块的状态,1280行设置cache块对应磁盘上的扇区,最后调用flashcache_read_miss下发请求:
1180static void
1181flashcache_read_miss(struct cache_c *dmc, struct bio* bio,
1182 int index)
1183{
1184 struct kcached_job *job;
1185 struct cacheblock *cacheblk = &dmc->cache[index];
1186
1187 job = new_kcached_job(dmc, bio, index);
1188 if (unlikely(sysctl_flashcache_error_inject & READ_MISS_JOB_ALLOC_FAIL)) {
1189 if (job)
1190 flashcache_free_cache_job(job);
1191 job = NULL;
1192 sysctl_flashcache_error_inject &= ~READ_MISS_JOB_ALLOC_FAIL;
1193 }
1194 if (unlikely(job == NULL)) {
1195 /*
1196 * We have a read miss, and can't allocate a job.
1197 * Since we dropped the spinlock, we have to drain any
1198 * pending jobs.
1199 */
1200 DMERR("flashcache: Read (miss) failed ! Can't allocate memory for cache IO, block %lu",
1201 cacheblk->dbn);
1202 flashcache_bio_endio(bio, -EIO);
1203 spin_lock_irq(&dmc->cache_spin_lock);
1204 dmc->cached_blocks--;
1205 cacheblk->cache_state &= ~VALID;
1206 cacheblk->cache_state |= INVALID;
1207 flashcache_free_pending_jobs(dmc, cacheblk, -EIO);
1208 cacheblk->cache_state &= ~(BLOCK_IO_INPROG);
1209 spin_unlock_irq(&dmc->cache_spin_lock);
1210 } else {
1211 job->action = READDISK; /* Fetch data from the source device */
1212 atomic_inc(&dmc->nr_jobs);
1213 dmc->disk_reads++;
1214 dm_io_async_bvec(1, &job->disk, READ,
1215 bio->bi_io_vec + bio->bi_idx,
1216 flashcache_io_callback, job);
1217 flashcache_clean_set(dmc, index / dmc->assoc);
1218 }
1219}

在第1187行申请了一个kcached_job,申请成功就到1211行,设置job->action=READDISK,调用dm_io_async_bvec直接从磁盘读取数据。接着调用flashcache_clean_set检查一下水位线。再看这里读磁盘的回调函数flashcache_io_callback,按理说读完磁盘就可以直接向上层返回数据,但这里还要把数据缓存起来之后再返回。
113void
114flashcache_io_callback(unsigned long error, void *context)
115{
116 struct kcached_job *job = (struct kcached_job *) context;
117 struct cache_c *dmc = job->dmc;
118 struct bio *bio;
119 unsigned long flags;
120 int index = job->index;
121 struct cacheblock *cacheblk = &dmc->cache[index];
122
123 VERIFY(index != -1);
124 bio = job->bio;
125 VERIFY(bio != NULL);
126 if (error)
127 DMERR("flashcache_io_callback: io error %ld block %lu action %d",
128 error, job->disk.sector, job->action);
129 job->error = error;
130 switch (job->action) {
131 case READDISK:
132 DPRINTK("flashcache_io_callback: READDISK %d",
133 index);
134 spin_lock_irqsave(&dmc->cache_spin_lock, flags);
135 if (unlikely(sysctl_flashcache_error_inject & READDISK_ERROR)) {
136 job->error = error = -EIO;
137 sysctl_flashcache_error_inject &= ~READDISK_ERROR;
138 }
139 VERIFY(cacheblk->cache_state & DISKREADINPROG);
140 spin_unlock_irqrestore(&dmc->cache_spin_lock, flags);
141 if (likely(error == 0)) {
142 /* Kick off the write to the cache */
143 job->action = READFILL;
144 flashcache_enqueue_readfill(dmc, job);
145 return;
146 } else {
147 dmc->disk_read_errors++;
148 flashcache_bio_endio(bio, error);
149 }
150 break; 174 case READFILL:
175 DPRINTK("flashcache_io_callback: READFILL %d",
176 index);
177 spin_lock_irqsave(&dmc->cache_spin_lock, flags);
178 if (unlikely(sysctl_flashcache_error_inject & READFILL_ERROR)) {
179 job->error = error = -EIO;
180 sysctl_flashcache_error_inject &= ~READFILL_ERROR;
181 }
182 if (unlikely(error))
183 dmc->ssd_write_errors++;
184 VERIFY(cacheblk->cache_state & DISKREADINPROG);
185 spin_unlock_irqrestore(&dmc->cache_spin_lock, flags);
186 flashcache_bio_endio(bio, error);
187 break;

归纳一下读不命中的流程:
1)创建一个kcached_job,直接下发到磁盘
2)读磁盘返回到flashcache_io_callback,到131行下发READFILL,将读出来的数据写到缓存中
3)写缓存成功并返回到flashcache_io_callback,到174行将数据返回给上层
到这里已经将读流程简单过了一遍,下一个小节介绍写流程。

linux内核源码阅读之facebook硬盘加速flashcache之五的更多相关文章

  1. linux内核源码阅读之facebook硬盘加速flashcache之八

    前面我们的分析中重点关注正常的数据流程,这一小节关注如果有异常,那么流程是怎么走完的呢? 1)创建新任务时kcached_job申请不到 2)读写命中时cache块为忙 3)系统关机时处理,系统开机时 ...

  2. linux内核源码阅读之facebook硬盘加速flashcache之三

    上一节讲到在刷缓存的时候会调用new_kcahed_job创建kcached_job,由此我们也可以看到cache数据块与磁盘数据的对应关系.上一篇:http://blog.csdn.net/lium ...

  3. linux内核源码阅读之facebook硬盘加速flashcache之四

    这一小节介绍一下flashcache读写入口和读写的基础实现. 首先,不管是模块还是程序,必须先找到入口,用户态代码会经常去先看main函数,内核看module_init,同样看IO流时候也要找到入口 ...

  4. linux内核源码阅读之facebook硬盘加速flashcache之二

    flashcache数据结构都在flashcache.h文件中,但在看数据结构之前,需要先过一遍flashcache是什么,要完成哪些功能?如果是自己设计这样一个系统的话,大概要怎么设计. 前面讲过, ...

  5. linux内核源码阅读之facebook硬盘加速flashcache之六

    其实到目前为止,如果对读流程已经能轻松地看懂了,那么写流程不需要太多脑细胞.我觉得再写下去没有太大的必要了,后面想想为了保持flashcache完整性,还是写出来吧.接着到写流程: 1530stati ...

  6. linux内核源码阅读之facebook硬盘加速利器flashcache

    从来没有写过源码阅读,这种感觉越来越强烈,虽然劣于文笔,但还是下定决心认真写一回. 源代码下载请参见上一篇flashcache之我见 http://blog.csdn.net/liumangxiong ...

  7. ubuntu下linux内核源码阅读工具和调试方法总结

    http://blog.chinaunix.net/uid-20940095-id-66148.html 一 linux内核源码阅读工具 windows下当然首选source insight, 但是l ...

  8. Linux内核源码阅读记录一之分析存储在不同段中的函数调用过程

    在写驱动的过程中,对于入口函数与出口函数我们会用一句话来修饰他们:module_init与module_exit,那会什么经过修饰后,内核就能狗调用我们编写的入口函数与出口函数呢?下面就来分析内核调用 ...

  9. Linux内核源码分析

    Linux源码下载: https://www.kernel.org/ https://git.kernel.org/ Linux内核源码阅读以及工具(转): https://blog.csdn.net ...

随机推荐

  1. Linux查看网卡状态

    观看网卡传送.接收数据包的状态 $ netstat  -i Kernel Interface table Iface MTU Met RX-OK RX-ERR RX-DRP RX-OVR TX-OK ...

  2. Spring 3.x企业实用开发实战(1)

    有关Spring的介绍这里就不赘述了,主要是学习了陈雄华版的<Spring 3.x企业应用开发实战>并做了一点笔记,以助于后期的回顾和复习. 废话不多说,直接进入主题,以下所有代码基于&l ...

  3. 【操作系统】linux创建子进程--fork()方法

    (1)fork()的定义 fork()函数是Unix中派生新进程的唯一方法,声明如下: #include <unistd.h> pid_t fork(void); 我们需要理解的是,调用一 ...

  4. Build A Micro Team

    最近这两周一直在忙,忙到忘记回QQ和微信的程度,甚至有两天下班回来直接躺平,虽说忙碌但是也收获了不少. Start 说起来有趣,最近在忙的这个项目最初是公司设立的用于工程师在业余时间开发的projec ...

  5. Google的兼容包问题【转】

    转自:http://blog.sina.com.cn/s/blog_3e28c8a50101g14g.html 项目之前好好的,今天开Eclipse,,出错. 错误Error retrieving p ...

  6. java的深复制与浅复制

    今天座右铭-----浪费时间就等于慢性自杀 ⑴浅复制(浅克隆) 被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原 来的对象.换言之,浅复制仅仅复制所考虑的对象,而不复 ...

  7. 谷歌page speed 安装使用及页面问题详解

    原文地址:http://wenku.baidu.com/view/b0a61f3ebcd126fff7050b40.html 谷歌page speed 安装使用及页面问题详解 谷歌page speed ...

  8. Java DES 加密和解密源码(转)

    原文地址:http://www.oschina.net/code/snippet_727646_18383 Java密码学结构设计遵循两个原则: 1) 算法的独立性和可靠性. 2) 实现的独立性和相互 ...

  9. hadoop记录topk

    lk@lk-virtual-machine:~$ cd hadoop-1.0.1 lk@lk-virtual-machine:~/hadoop-1.0.1$ ./bin dfs -mkdir inpu ...

  10. deferred initcalls与模块化

    deferred initcalls与模块化 有两个技术可以加快kernel的启动速度: 1.deferred initcalls 2.模块化 它们的思想类似,都是将非必要的模块初始化推迟到内核启动之 ...