这一小节介绍一下flashcache读写入口和读写的基础实现。
首先,不管是模块还是程序,必须先找到入口,用户态代码会经常去先看main函数,内核看module_init,同样看IO流时候也要找到入口。flashcache作为一个dm_target,入口就是struct target_type 的map函数,对应的是flashcache_map函数:
1581/*
1582 * Decide the mapping and perform necessary cache operations for a bio request.
1583 */
1584int
1585flashcache_map(struct dm_target *ti, struct bio *bio,
1586 union map_info *map_context)
1587{
1588 struct cache_c *dmc = (struct cache_c *) ti->private;
1589 int sectors = to_sector(bio->bi_size);
1590 int queued;
1591
1592 if (sectors <= 32)
1593 size_hist[sectors]++;
1594
1595 if (bio_barrier(bio))
1596 return -EOPNOTSUPP;
1597
1598 VERIFY(to_sector(bio->bi_size) <= dmc->block_size);
1599
1600 if (bio_data_dir(bio) == READ)
1601 dmc->reads++;
1602 else
1603 dmc->writes++;
1604
1605 spin_lock_irq(&dmc->cache_spin_lock);
1606 if (unlikely(sysctl_pid_do_expiry &&
1607 (dmc->whitelist_head || dmc->blacklist_head)))
1608 flashcache_pid_expiry_all_locked(dmc);
1609 if ((to_sector(bio->bi_size) != dmc->block_size) ||
1610 (bio_data_dir(bio) == WRITE && flashcache_uncacheable(dmc))) {
1611 queued = flashcache_inval_blocks(dmc, bio);
1612 spin_unlock_irq(&dmc->cache_spin_lock);
1613 if (queued) {
1614 if (unlikely(queued < 0))
1615 flashcache_bio_endio(bio, -EIO);
1616 } else {
1617 /* Start uncached IO */
1618 flashcache_start_uncached_io(dmc, bio);
1619 }
1620 } else {
1621 spin_unlock_irq(&dmc->cache_spin_lock);
1622 if (bio_data_dir(bio) == READ)
1623 flashcache_read(dmc, bio);
1624 else
1625 flashcache_write(dmc, bio);
1626 }
1627 return DM_MAPIO_SUBMITTED;
1628}

第1588行,dmc = ti->private,是什么时候保持的这个指针呢?看构造函数flashcache_ctr
1350     ti->split_io = dmc->block_size;
1351 ti->private = dmc;

这里对private赋值,这里还有一个额外的收获,就是1350行,这是告诉dm层将IO分发为指定大小下发到dm_target设备。所以就有了flashcache_map函数1609行判断bio->bi_size是否为block_size大小。1606行和1610行是关于黑名单管理的,用于管理哪些进程或组不使用flashcache的,这里暂且不管,有兴趣可以查看flashcache_ioctl。
为什么大小不为block_size就直接下发到磁盘呢?因为flashcache只处理block_size大小的数据,由于设置了ti->spilit_io为block_size,所以flashcache_map接收到的数据都不会超过block_size,取大的bio在dm层被拆分成最大block_size的bio下发。那么处理小块数据对flashcache来讲有什么不好呢?因为flashcache为了提高效率都在按block_size下发到磁盘,这时有小的数据块缓存,那么必须要凑齐block_size才能下发,那怎么凑齐呢,就要去磁盘里读。所以flashcache对于缓存的数据是有选择性的,那么也决定了上层了流量模型不能是小块数据,这样的话flashcache就会直接下发到磁盘,就没起到缓存的作用了。
如果是小数据块的情况,第1611行调用flashcache_inval_block将与该bio有交集的cache块全部设置为INVALID,因为不再是最新的了。然后很不幸的是,设置cache块为invalid也会失败,按直观的想法就是设置一个脏标志位不就行了吗?根据墨菲定律,我们总是会过于乐观的判断一件事情。这里先不讲这些异常处理,因为如果还没有理解正常流程是什么样的,讲异常就失去了意义。
这样我们就很快找了正常流程的读写入口,第1623行是读入口,第1625行是写入口。
这里不急于去看读写实现,先来说说flashcache采用的读写磁盘的方法。
flashcache中跟磁盘相关的读写分为以下两类:
1)磁盘跟内存的交互
2)磁盘跟磁盘之前的交互
比如说读不命中时就是直接从磁盘读,属于第1种情况,那读命中呢?也是属于第1种情况,不过这时候是从SSD读。磁盘跟磁盘之间交互是用于写脏数据,将SSD中脏cache块拷贝到磁盘上去。现在介绍下两种情况使用的接口函数,这样后面在看读写流程时看到这两个函数就十分亲切了,并且清楚地知道数据是从哪里流向哪里。
首先看第一种情况是通过flashcache_dm_io_sync_vm函数实现的:
571int
572flashcache_dm_io_sync_vm(struct cache_c *dmc, struct dm_io_region *where, int rw, void *data)
573{
574 unsigned long error_bits = 0;
575 int error;
576 struct dm_io_request io_req = {
577 .bi_rw = rw,
578 .mem.type = DM_IO_VMA,
579 .mem.ptr.vma = data,
580 .mem.offset = 0,
581 .notify.fn = NULL,
582 .client = dmc->io_client,
583 };
584
585 error = dm_io(&io_req, 1, where, &error_bits);
586 if (error)
587 return error;
588 if (error_bits)
589 return error_bits;
590 return 0;
591}

这里我们只关心dm_io的使用,并不关心其实现,因为这已经涉及到dm层的代码了。
dmc 就是flashcache在内存中的管理结构
where是读写的目标设备
rw 读写
data 对应的内存地址
我们就以flashcache_md_create中读flash_superblock为例
720	header = (struct flash_superblock *)vmalloc(512);
721 if (!header) {
722 DMERR("flashcache_md_create: Unable to allocate sector");
723 return 1;
724 }
725 where.bdev = dmc->cache_dev->bdev;
726 where.sector = 0;
727 where.count = 1;
728#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27)
729 error = flashcache_dm_io_sync_vm(&where, READ, header);
730#else
731 error = flashcache_dm_io_sync_vm(dmc, &where, READ, header);
732#endif

第一个参数dmc,第二个参数设置设备为SSD,即cache_dev->bdev,扇区0开始,1个扇区大小,读,目的地址是header。由于flashcache_dm_io_sync_vm中第581行设置fn=NULL,所以该函数是同步的。
现在看第二类磁盘和磁盘之间交互。看函数原型:
int dm_kcopyd_copy(struct dm_kcopyd_client *kc, struct dm_io_region *from,

             unsigned num_dests, struct dm_io_region *dests,

             unsigned flags, dm_kcopyd_notify_fn fn, void *context);

第一个参数dm_kcopyd_client,在使用kcopyd异步拷贝服务时,必须先创建一个对应的client,创建在flashcache_ctr函数中
1208     r = dm_kcopyd_client_create(FLASHCACHE_COPY_PAGES, &dmc->kcp_client);
1209 if (r) {
1210 ti->error = "Failed to initialize kcopyd client\n";
1211 dm_io_client_destroy(dmc->io_client);
1212 goto bad3;
1213 }

第二个参数dm_io_region是源地址,第四个参数是目的地址,定义如下
struct dm_io_region {

     struct block_device *bdev;

     sector_t sector;

     sector_t count;          /* If this is zero the region is ignored. */

};

dm_kcopyd_notify_fn fn是kcopyd处理完请求的回调函数
context 是回调函数参数,在flashcache都设置对应的kcached_job
小结一下,以上两类函数其实本质是一样的,调用者填写好源地址和目的地址,地址可以是内存中的也可以是设备的,填好之后就调用函数,再接着就等回调通知。就好比我们在网上购物,帐号(dm_client)登录,我们只负责填好订单(dm_io_region),具体的生产制造物流过程我不关心,我只关心门铃响(dm_kcopyd_notify_fn)的时候我要的物品都已经送上门来了。

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

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

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

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

    正常流程到flashcache_map的1623行或1625行,按顺序先看读流程: 1221static void 1222flashcache_read(struct cache_c *dmc, s ...

  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. 在VHDL中,“传输延迟”和“惯性延迟”

    传输延迟就是最容易理解的从输入变化到输出变化之间的延迟.对应语法是transport例如 b <= transport a after 20ns 惯性延迟考虑了电容效应,即如果输入是(相对)窄的 ...

  2. Red Hat Enterprise Linux Release Dates

    Red Hat Enterprise Linux Release Dates UpdatedMay 10 2016 at 10:57 PM - English The tables below lis ...

  3. android LinearLayout android:layout_weight 作用,固定比例

    android 中的 LinearLayout  是线性布局有水平布局horizontal  垂直布局vertical .本文针对 水平布局horizontal 布局的weight属性做一个标记,以免 ...

  4. iPhone/iTouch免99美刀真机调试

    本文经本人验证,攻略来源于网上,由于多次转载原始出处不可靠,故无法对原作者进行链接引用,抱歉. 本文仅为记录流程,以备日后查询.本文版权所无,欢迎转载和拍砖. 测试环境: XCode 4.0.2 + ...

  5. [虚拟化/云] kvm的架构分析

    预备知识 1. 客户机物理页框到宿主机虚拟地址转换 http://blog.csdn.net/zhuriyuxiao/article/details/8968781 http://www.tuicoo ...

  6. openNebula libvirt-virsh attach disk device for kvm

    1,新建文件硬盘 qemu-img create -f qcow2 testdisk.img 2G

  7. 修改spinner选中以后显示的字体颜色

    原来spinner选中以后显示的view  还是textview  ,这样就是可以设置它的字体颜色了 sp.setOnItemSelectedListener(new OnItemSelectedLi ...

  8. 基于Qt的图像采集系统

    硬件 Point Gray Camera 型号:FL3-U3-13S2C-CS 参数 Sony IMX035 CMOS, 1/3", 3.63 µm Rolling Shutter 1328 ...

  9. Tiny Spring 分析一

    近期一直想看spring的源代码,可是奈何水平太低,庞杂的源代码令我一阵阵的头晕. 非常有幸,在网上看到了黄亿华大神的<<1000行代码读懂Spring(一)- 实现一个主要的IoC容器& ...

  10. DOM4J读取XML文件

    最近在做DRP的项目,其中涉及到了读取配置文件,用到了DOM4J,由于是刚开始接触这种读取xml文件的技术,好奇心是难免的,于是在网上又找了一些资料,这里就结合找到的资料来谈一下读取xml文件的4中方 ...