通过hijack了解bio

匿名 (未验证) 提交于 2019-12-02 23:42:01

通过hijack了解bio

背景

bio是block io,它是一个描述硬盘里面的位置与page cache的页对应关系的数据结构,每个bio对应的
硬盘里面一块连续的位置,每一块硬盘里面连续的位置,可能对应着page cache的多页,或者一页,
所以它里面会有一个bio_vec *bi_io_vec的表。
而每个bio_vec都只能描述一个页内数据的连续的数据的偏移和长度:

通过bi_sectors和bi_size来描述硬盘中起始位置和长度。
一个bio的处理:一个bio通常通过submit_bio来提交给设备队列,下面会经过bio聚合,转化成request,磁盘调度队列,块设备驱动,磁盘处理,对于bio来说大部分都是异步,所以处理结束通过bio->bi_end_io来作为callback获取结果,外部的接口使用bio_endio。同步的读写是通过等待callback事件到来从而完成同步读写。

中间贴的只是代码片段,介绍怎么做的,不能直接运行的。获取全部的代码片段,请联系我

目标

进行块设备加密,磁盘落盘的数据是密文,而对于文件系统来说是明文.加密算法因为用过sm4,所以就选用了最简单的sm4(ecb)加解密。

设计

submit_bio是写IO的起手势,所以通过hook submit_bio来进行写加密,保证提交给下面的数据是经过加密的就好了。而对于读IO可以通过在IO完成之后通知给文件系统之前进行解密,可以hook bio_endio来进行数据解密,确保page cache上的数据是经过解密后的明文。

写加密

bio只是管理的数据结构,数据仍然是存放在page cache中的,最终的结果要求是page cache中的所述句仍然是明文,提交给磁盘的数据是密文,所以一块数据是搞不定这件事了,我们需要重新申请一块内存来存放密文数据。

long origin_submit_bio(int rw, struct bio *bio); void hook_submit_bio(int rw, struct bio *bio) {     ret = try_crypt_bio(rw, bio);     if (ret != 0) {         origin_submit_bio(rw, bio);     }     return; } int bfx_bio_alloc_pages(struct bio *bio, gfp_t gfp) {     int i;     struct bio_vec *bv;      bio_for_each_segment(bv, bio, i) {         bv->bv_page = alloc_page(gfp);         if (!bv->bv_page) {             while (bv-- != bio->bi_io_vec + bio->bi_idx)                 __free_page(bv->bv_page);             return -ENOMEM;         }     }      return 0; }  void bfx_crypt_callback(struct bio *bio, int error) {     unsigned int i;     struct bio_vec *bv;     struct bio *origin = (struct bio *)bio->bi_private;      bio_for_each_segment_all(bv, bio, i) {         BUG_ON(!bv->bv_page);         __free_page(bv->bv_page);         bv->bv_page = NULL;     }      bio_endio(origin, error);      bio_put(bio); } struct bio* bfx_bio_clone(struct bio *origin) {     struct bio *new = NULL;     new = bio_clone(origin, GFP_NOIO);     if (!new)         return NULL;      if (bfx_bio_alloc_pages(new, GFP_NOIO)) {         bio_put(new);         return NULL;     }      new->bi_private = origin;     new->bi_end_io = bfx_crypt_callback;     return new; } int try_crypt_bio(int rw, struct bio *bio) {     sm4_key_t key;     int i, j;                                                                                                                                       char buffer[SM4_BLOCK_SIZE * 2 + 1];     struct bio_vec *bv;     struct bio *new;      if (!(rw & WRITE)) {         return -1;     }      new = bfx_bio_clone(bio);     if (!new) {         BUG();         return -ENOMEM;     }     new->bi_rw |= rw;     bio->bi_rw |= rw;      sm4_set_key1(&key, mykey, 16);      bio_for_each_segment(bv, bio, i) {         void *p1 = kmap(bv->bv_page);         void *p2 = kmap(new->bi_io_vec[i].bv_page);         /* Encrypt your bio data */         /*         for (j = bv->bv_offset; j < bv->bv_offset + bv->bv_len; j+= SM4_BLOCK_SIZE) {             sm4_do_crypt(key.rkey_enc, (u32 *)(p2 + j), (u32 *)(p1 + j));         }         */          kunmap(bv->bv_page);         kunmap(new->bi_io_vec[i].bv_page);     }      generic_make_request(new);     return 0; } 

读解密

相对于写操作,读IO就比较简单了,从磁盘中读取到了密文数据到page cache中,我们只需要在文件系统看到这块page cache前解密就好,不需要倒腾内存,只需要原地解密。

遍历bio指向的内存数据:

void origin_bio_endio(struct bio *bio, int error); void hook_bio_endio(struct bio *bio, int error) {     try_decrypt_bio(bio, error);      origin_bio_endio(bio, error);                                                                                                                return; } void try_decrypt_bio(struct bio *bio, int error) {     sm4_key_t key;     int i, j;     char buffer[SM4_BLOCK_SIZE];     char *ptr = NULL;     unsigned long flags;     struct bio_vec *bv;      if (bio_data_dir(bio) == WRITE)         return;      sm4_set_key1(&key, mykey, 16);      bio_for_each_segment(bv, bio, i) {         ptr = bvec_kmap_irq(bv, &flags);         for (j = 0; j < bv->bv_len; j+= SM4_BLOCK_SIZE) {             /* Decrypt ptr pointer buffer */             /*             sm4_do_crypt(key.rkey_dec, (u32 *)buffer, (u32 *)(ptr + j));             memcpy(ptr + j, buffer, SM4_BLOCK_SIZE);             */         }         bvec_kunmap_irq(ptr, &flags);     }    } 

后续改进

因为加解密完全使用cpu来进行,速度肯定比较慢,最终的结果就是IO速率大幅度下降.

一个选择就是使用有硬件加密卡支持的算法,提升速率;

另外一个选择就是启动一个线程,先将bio缓存下来慢慢加解密,为了提升体验,可以提升读IO的优先级,让用户优先看到数据,至于写放在后台慢慢整吧。

参考

https://lwn.net/Articles/26404/
www.eeworld.com.cn/mp/ymc/a52704.jspx
drivers/md/dm-crypt.c

文章来源: https://blog.csdn.net/faxiang1230/article/details/91891496
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!