linux内核源码阅读之facebook硬盘加速flashcache之八
前面我们的分析中重点关注正常的数据流程,这一小节关注如果有异常,那么流程是怎么走完的呢?1)创建新任务时kcached_job申请不到2)读写命中时cache块为忙3)系统关机时处理,系统开机时处理,系统异常掉电后的处理首先来看第1种情况,申请kcached_job是在函数flashcache_lookup中,
294static void295flashcache_do_pending_noerror(struct kcached_job *job)296{297 struct cache_c *dmc = job->dmc;298 int index = job->index;299 unsigned long flags;300 struct pending_job *pending_job;301 int queued;302 struct cacheblock *cacheblk = &dmc->cache[index];303304 spin_lock_irqsave(&dmc->cache_spin_lock, flags);305 if (cacheblk->cache_state & DIRTY) {306 cacheblk->cache_state &= ~(BLOCK_IO_INPROG);307 cacheblk->cache_state |= DISKWRITEINPROG;308 spin_unlock_irqrestore(&dmc->cache_spin_lock, flags);309 flashcache_dirty_writeback(dmc, index);310 goto out;311 }312 DPRINTK("flashcache_do_pending: Index %d %lx",313 index, cacheblk->cache_state);314 VERIFY(cacheblk->cache_state & VALID);315 dmc->cached_blocks--;316 dmc->pending_inval++;317 cacheblk->cache_state &= ~VALID;318 cacheblk->cache_state |= INVALID;319 while (cacheblk->head) {320 VERIFY(!(cacheblk->cache_state & DIRTY));321 pending_job = cacheblk->head;322 cacheblk->head = pending_job->next;323 VERIFY(cacheblk->nr_queued > 0);324 cacheblk->nr_queued--;325 if (pending_job->action == INVALIDATE) {326 DPRINTK("flashcache_do_pending: INVALIDATE %llu",327 next_job->bio->bi_sector);328 VERIFY(pending_job->bio != NULL);329 queued = flashcache_inval_blocks(dmc, pending_job->bio);330 if (queued) {331 if (unlikely(queued < 0)) {332 /*333 * Memory allocation failure inside inval_blocks.334 * Fail this io.335 */336 flashcache_bio_endio(pending_job->bio, -EIO);337 }338 flashcache_free_pending_job(pending_job);339 continue;340 }341 }342 spin_unlock_irqrestore(&dmc->cache_spin_lock, flags);343 DPRINTK("flashcache_do_pending: Sending down IO %llu",344 pending_job->bio->bi_sector);345 /* Start uncached IO */346 flashcache_start_uncached_io(dmc, pending_job->bio);347 flashcache_free_pending_job(pending_job);348 spin_lock_irqsave(&dmc->cache_spin_lock, flags);349 }350 VERIFY(cacheblk->nr_queued == 0);351 cacheblk->cache_state &= ~(BLOCK_IO_INPROG);352 spin_unlock_irqrestore(&dmc->cache_spin_lock, flags);353out:354 flashcache_free_cache_job(job);355 if (atomic_dec_and_test(&dmc->nr_jobs))356 wake_up(&dmc->destroyq);357}
这个函数分为两部分,第一部分是如果cache块为脏,则下发后立即返回,等待第二次调用,第二次调用才真正到处理pending_job,记得我们在上文中插入的pending_job是INVALIDATE的,那么这里也正如前面注释里所说下发了两次IO,一次是写回脏块,后一次是下发uncached IO。319行,取出invalid的pending_job320行,确认非dirty,因为第一次调用的时候已经写回了325行,if语句成立,329行,因为cache块已写回,就不脏不忙了,flashcache_inval_blocks只要设置invalid就可以返回成功346行,下发uncached IO至此uncached IO之旅告一个段落了。接下来讲第2种情况读写命中但cache块忙的情况下是怎么处理的。读写IO在cache块忙的情况下做出的表现是惊人的一致,那就是创建pending_job并挂入cache块队列中。这对我们来说已经是轻车熟路,不过我们这一次要跟踪的是读写IO的情况。经过前面的分析我们知道,pending_job是在flashcache_do_pending_noerror函数中处理的。同样如果为脏块要刷一次脏块,第二次进入到319行循环,由于Action为READCACHE或者WRITECACHE,直接到346行下发uncached IO。第2种情况的处理也就宣告结束了。似乎显得仓促,现实就是这样的,永远别想像电影里那样大起大落,只要你内心够从容,平平淡淡才是真。第3种情况就留给大家自己分析,如果对这几个小节都已经熟悉,那就已经是小case了。至此,flashcache源码的分析也就结束了,我也非常高兴能够坚持写完,因为这确实是一个非常耗时间的过程。如果你阅读之后能有所收获,那将是我最大的欢喜了。