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。linux

其實我開始一直不明白,爲何要使用這兩個函數讓硬盤與內存打交道,不事後來看了dm_io發現其中的io服務類型有多種不一樣類型,這兩個函數的調用分別對應不一樣的io類型。下面先看一下dm_io相關的數據結構。ios

 dm_ioc#

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):app

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)釋放內存池。async

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中。this

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。
相關文章
相關標籤/搜索