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. */ };
struct io_region { struct block_device *bdev; sector_t sector; sector_t count; };
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; };
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);
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);
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);
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结构。