• flashcache中内存与磁盘,磁盘与磁盘的io


    flashcache中跟磁盘相关的读写分为以下两类:
    1)磁盘跟内存的交互
    2)磁盘跟磁盘之前的交互
    比如说读不命中时就是直接从磁盘读,属于第1种情况,那读命中呢?也是属于第1种情况,不过这时候是从SSD读。磁盘跟磁盘之间交互是用于写脏数据,将SSD中脏cache块拷贝到磁盘上去。现在介绍下两种情况使用的接口函数,这样后面在看读写流程时看到这两个函数就十分亲切了,并且清楚地知道数据是从哪里流向哪里。
     
    对于情况1,主要是两个函数dm_io_async_bvec和flashcache_dm_io_async_vm。
     
    int dm_io_async_bvec(unsigned int num_regions, 
    #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)
                    struct dm_io_region *where, 
    #else
                    struct io_region *where, 
    #endif
                    int rw, 
                    struct bio_vec *bvec, io_notify_fn fn, 
                    void *context)
    {
        struct dm_io_request iorq;
    
        iorq.bi_rw = rw;
        iorq.mem.type = DM_IO_BVEC;
        iorq.mem.ptr.bvec = bvec;
        iorq.notify.fn = fn;
        iorq.notify.context = context;
        iorq.client = flashcache_io_client;
        return dm_io(&iorq, num_regions, where, NULL);
    }
    #endif
    int 
    flashcache_dm_io_async_vm(struct cache_c *dmc, unsigned int num_regions, 
    #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26)
                  struct io_region *where, 
    #else
                  struct dm_io_region *where, 
    #endif
                  int rw,
                  void *data, io_notify_fn fn, void *context)
    {
        unsigned long error_bits = 0;
        int error;
        struct dm_io_request io_req = {
            .bi_rw = rw,
            .mem.type = DM_IO_VMA,
            .mem.ptr.vma = data,
            .mem.offset = 0,
            .notify.fn = fn,
            .notify.context = context,
            .client = flashcache_io_client,
        };
    
        error = dm_io(&io_req, 1, where, &error_bits);
        if (error)
            return error;
        if (error_bits)
            return error_bits;
        return 0;
    }
    #endif

    上面两个函数都使用struct dm_io_request 来包装了请求,其中的只有两种请求的类型是不一样的,第一个函数对应的是DM_IO_BVEC,第二个函数是DM_IO_VMA。

    其实我开始一直不明白,为什么要使用这两个函数让硬盘与内存打交道,不过后来看了dm_io发现其中的io服务类型有多种不同类型,这两个函数的调用分别对应不同的io类型。下面先看一下dm_io相关的数据结构。

     dm_io

    dm-io为device mapper提供同步或者异步的io服务。

    使用dm-io必须设置dm_io_region结构(2.6.26版本以前叫io_region),该结构定义了io操作的区域,读一般针对一个dm_io_region区,而写可以针对一组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. */
    };
     
    老版本的内核,用户必须设置一个io_region结构来描述预期的I/O所在地。每个io_region说明了一个在区域上的有起始位置和长度的块设备。
     
    struct io_region {
          struct block_device *bdev;
          sector_t sector;
          sector_t count;
       };
     
    Dm-io 可以从一个io_region中读取或者写入到一个或者多个io_region中去。一个io_region结构数组指定了写入到多个区域。

    dm-io一共有四种dm_io_mem_type类型(老一点的内核版本只有前面三种,Flashcache主要使用DM_IO_BVEC):

    enum dm_io_mem_type {
        DM_IO_PAGE_LIST,/* Page list */
        DM_IO_BVEC,     /* Bio vector */
        DM_IO_VMA,      /* Virtual memory area */
        DM_IO_KMEM,     /* Kernel memory */
    };
    
    struct dm_io_memory {
        enum dm_io_mem_type type;
        union {
                struct page_list *pl;
                struct bio_vec *bvec;
                void *vma;
                void *addr;
        } ptr;
    
        unsigned offset;
    };
    Dm-io 提供同步和异步I/O服务。老一点的内核它提供了3种I/O服务,每种服务都有一个同步和一个异步的版本。
     
    DM_IO_PAGE_LIST
    第一个I/O服务类型使用了一串内存页作为缓冲区,伴随着一个首页面的偏移。
     
       struct page_list {
          struct page_list *next;
          struct page *page;
       };
     int dm_io_sync(unsigned int num_regions, struct io_region *where, int rw,
                      struct page_list *pl, unsigned int offset,
                      unsigned long *error_bits);
       int dm_io_async(unsigned int num_regions, struct io_region *where, int rw,
                       struct page_list *pl, unsigned int offset,
                       io_notify_fn fn, void *context);
    DM_IO_BVEC
    第二种I/O服务类型把一组bio载体当着I/O的数据缓冲。如果调用者提前拼装了bio,这个服务可以很顺利地完成。但是需要将不同的bio页指向不同的设备。
     
       int dm_io_sync_bvec(unsigned int num_regions, struct io_region *where,
                           int rw, struct bio_vec *bvec,
                           unsigned long *error_bits);
       int dm_io_async_bvec(unsigned int num_regions, struct io_region *where,
                            int rw, struct bio_vec *bvec,
                            io_notify_fn fn, void *context);
     
    DM_IO_VMA
     
    第三种I/O服务类型把一个指向虚拟动态内存缓冲区的的指针当作I/O的数据缓冲。如果调用者需要在很大的块设备上进行I/O操作又不想分配大量的个人内存页,那么这种服务可以胜任。
     
     int dm_io_sync_vm(unsigned int num_regions, struct io_region *where, int rw,
                         void *data, unsigned long *error_bits);
       int dm_io_async_vm(unsigned int num_regions, struct io_region *where, int rw,
                          void *data, io_notify_fn fn, void *context);
     
    异步I/O服务的调用者必须包含一个完成的回调函数和一个指向一些这个I/O内容数据的指针。
     
    typedef void (*io_notify_fn)(unsigned long error, void *context);
     
    这个"error"参数,就像这个"*error"参数在任何同步版本中一样,在这个回调函数中就象一个位集合(而不是一个简单的错误值)。

    在写I/O到多个目标区域的情况下,这个位集合允许dm-io说明在每个单独的区域上的成功或者失败。
    在使用任何dm-io服务之前,用户必须调用dm_io_get()、同时指定他们想要的页数来执行I/O.
    DM-io将尝试着更改自己的内存池的大小来确认在执行i/o时为了避免不必要的等待而有足够的页面来供给。
    当用户完成了使用I/O服务,他们将调用dm_io_put(),并指定和给dm_io_get()的相同数量的页面。
     

    dm-io通过dm_io_request结构来封装请求的类型,如果设置了dm_io_notify.fn则是异步IO,否则是同步IO。

    struct dm_io_request {
        int bi_rw;                      /* READ|WRITE - not READA */
        struct dm_io_memory mem;        /* Memory to use for io */
        struct dm_io_notify notify;     /* Synchronous if notify.fn is NULL */
        struct dm_io_client *client;    /* Client memory handler */
    };

    使用dm_io服务前前需要通过dm_io_client_create函数(在2.6.22版本前是dm_io_get)先创建dm_io_client结构,为dm-io的执行过程中分配内存池。使用dm-io服务完毕后,则需要调用dm_io_client_destroy函数(在2.6.22版本前是dm_io_put)释放内存池。

    struct dm_io_client {
        mempool_t *pool;
        struct bio_set *bios;
    };

    dm-io函数执行具体的io请求。

    int dm_io(struct dm_io_request *io_req, unsigned num_regions,
          struct dm_io_region *where, unsigned long *sync_error_bits)
    {
        int r;
        struct dpages dp;
    
        r = dp_init(io_req, &dp);
        if (r)
                return r;
    
        if (!io_req->notify.fn)
                return sync_io(io_req->client, num_regions, where,
                               io_req->bi_rw, &dp, sync_error_bits);
    
        return async_io(io_req->client, num_regions, where, io_req->bi_rw,
                        &dp, io_req->notify.fn, io_req->notify.context);
    }

    对于第二种情况,磁盘跟磁盘之前的交互。这种情况只用于将ssd中脏块写入disk中。

    int dm_kcopyd_copy(struct dm_kcopyd_client *kc, struct dm_io_region *from,
                       unsigned int num_dests, struct dm_io_region *dests,
                       unsigned int flags, dm_kcopyd_notify_fn fn, void *context)

    第一个参数dm_kcopyd_client,在使用kcopyd异步拷贝服务时,必须先创建一个对应的client,首先要分配“kcopyd客户端”结构,调用函数如下:

    kcopyd_client_create(FLASHCACHE_COPY_PAGES, &flashcache_kcp_client);

    创建dm_kcopyd_client结构。

    第二个参数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。
  • 相关阅读:
    SQL的各种连接(cross join、inner join、full join)的用法理解
    解决sonarQube 'Unknown': sonar.projectKey
    DOS批处理高级教程
    java自定义注解
    inode备忘
    sed备忘
    awk备忘
    Oracle 删除表中记录 如何释放表及表空间大小
    Oracle 11g 分区拆分与合并
    Maven的settings.xml文件结构之mirrors
  • 原文地址:https://www.cnblogs.com/wuchanming/p/3813491.html
Copyright © 2020-2023  润新知