千家信息网

device-mapper 块级重删(dm dedup) <3>代码结构(2)

发表于:2025-01-31 作者:千家信息网编辑
千家信息网最后更新 2025年01月31日,四、代码结构(2) space manager这一篇和下一篇我们来介绍dm dedup的空间管理的部分和核心流程I/O写流程在此之前,我们先分析一下用到的资源有哪些,和了解dm dedup的space
千家信息网最后更新 2025年01月31日device-mapper 块级重删(dm dedup) <3>代码结构(2)

四、代码结构(2) space manager

这一篇和下一篇我们来介绍dm dedup的空间管理的部分和核心流程I/O写流程

在此之前,我们先分析一下用到的资源有哪些,和了解dm dedup的space manager空间管理器

空间管理器,是一个巨型的数组,以allocptr申请指针为标,对整个space进行扫描一周(回到current allocptr)。
用来找到空闲的块(白色),并把它分配给一个hash request,把它变成一个绿色的块,并把这个信息hash_pbn,放到kvs_hash_pbn的表内。

kvs_lbn_pbn和kvs_hash_pbn的空间是预先分配好的,所以这两个表的index都是有确定的含义,非常利于查找。

dc->kvs_hash_pbn = dc->mdops->kvs_create_sparse(md, crypto_key_size,sizeof(struct hash_pbn_value),dc->pblocks, unformatted);dc->kvs_lbn_pbn = dc->mdops->kvs_create_linear(md, 8,sizeof(struct lbn_pbn_value), dc->lblocks, unformatted);

在创建kvs(key value space)的时候,有两种选择,一种是按照linear就是一一映射的lbn-pbn的方式,还有一种是hash index的方式。

其中都会将ksize和vsize会在两种space类型不同而会以不同的方式保存。

① kvs-lbn-pbn,linear的create space方式inram

static struct kvstore *kvs_create_linear_inram(struct metadata *md,u32 ksize, u32 vsize,u32 kmax, bool unformatted){        struct kvstore_inram *kvs;        u64 kvstore_size, tmp;        kvs = kmalloc(sizeof(*kvs), GFP_NOIO);        if (!kvs)            return ERR_PTR(-ENOMEM);        kvstore_size = (kmax + 1) * vsize;        kvs->store = vmalloc(kvstore_size);        /*确定kvs->store的大小,这里的思想很简单,        就是64 bit的lbn寻址到一个ksize的pbn上面,一般的pbn也是64 bit*/        /*kmax是 逻辑设备的大小,这个map-table的含义就是lbn-pbn的映射关系*/        tmp = kvstore_size;        (void)do_div(tmp, (1024 * 1024));        memset(kvs->store, EMPTY_ENTRY, kvstore_size);        kvs->ckvs.vsize = vsize;        kvs->ckvs.ksize = ksize;        kvs->kmax = kmax;        kvs->ckvs.kvs_insert = kvs_insert_linear_inram;   /*插入api*/        kvs->ckvs.kvs_lookup = kvs_lookup_linear_inram; /*查找api*/        kvs->ckvs.kvs_delete = kvs_delete_linear_inram; /*删除api*/        kvs->ckvs.kvs_iterate = kvs_iterate_linear_inram; /*迭代api*/        md->kvs_linear = kvs;         return &(kvs->ckvs);}

我们简单看一看kvs_insert_linear_inram和kvs_lookup_linear_inram,删除和迭代留在垃圾回收的部分介绍。

static int kvs_insert_linear_inram(struct kvstore *kvs, void *key,s32 ksize, void *value,int32_t vsize){    u64 idx;    char *ptr;    struct kvstore_inram *kvinram = NULL;    kvinram = container_of(kvs, struct kvstore_inram, ckvs);    idx = *((uint64_t *)key);    ptr = kvinram->store + kvs->vsize * idx; /*以lbn为key,pbn为value的map*/    memcpy(ptr, value, kvs->vsize);    return 0;}

插入的代码非常简单,就可以理解成linear是个 u64 kvs[kmax] 这样的数组,lbn是数组下标,而value是数组内容。

static int kvs_lookup_linear_inram(struct kvstore *kvs, void *key,                   s32 ksize, void *value,                   int32_t *vsize){    u64 idx;    char *ptr;    int r = -ENODATA;    struct kvstore_inram *kvinram = NULL;    kvinram = container_of(kvs, struct kvstore_inram, ckvs);    idx = *((uint64_t *)key);    ptr = kvinram->store + kvs->vsize * idx;    if (is_empty(ptr, kvs->vsize))        return r;    memcpy(value, ptr, kvs->vsize);    *vsize = kvs->vsize;    return 0;}

② kvs-hash-pbn,sparse的create space方式inram
sparse 的方式和linear不太一样,它的组织形式会更加复杂一些
他要存 key和value两个,key_size是采用hash算法的大小,如:md5是128 bit,value是pbn是64 bit

static struct kvstore *kvs_create_sparse_inram(struct metadata *md,                           u32 ksize, u32 vsize,                           u32 knummax, bool unformatted){    struct kvstore_inram *kvs;    u64 kvstore_size, tmp;    kvs = kmalloc(sizeof(*kvs), GFP_NOIO); /* knummax key的最大值这里是按照pbn的最大值申请的,物理设备的大小*/    knummax += (knummax * HASHTABLE_OVERPROV) / 100;/*额外申请了十分之一的空间*/    kvstore_size = (knummax * (vsize + ksize));  /*申请单位是vsize(pbn)64bit ksize(hash_size)128 bit*/    kvs->store = vmalloc(kvstore_size);    tmp = kvstore_size;    (void)do_div(tmp, (1024 * 1024));    memset(kvs->store, EMPTY_ENTRY, kvstore_size);    /*将所有的key都预先变成EMPTY_ENTRY= 0xFB(最新的代码4.13是0xFF)*/    kvs->ckvs.vsize = vsize;    kvs->ckvs.ksize = ksize;    kvs->kmax = knummax;    kvs->ckvs.kvs_insert = kvs_insert_sparse_inram; /*插入api*/    kvs->ckvs.kvs_lookup = kvs_lookup_sparse_inram; /*查找api*/    kvs->ckvs.kvs_delete = kvs_delete_sparse_inram; /*删除api*/    kvs->ckvs.kvs_iterate = kvs_iterate_sparse_inram; /*迭代api*/    md->kvs_sparse = kvs;    return &(kvs->ckvs);}

接下来看一下插入和查找函数,这个函数会比linear的难一些,只要一点例子会很好理解。

看到调试信息中有初始化的过程。
其中也包括两个写流程包括lookup和insert,为了帮助快速理解,就按照这个例子来说会比较容易。

static int kvs_insert_sparse_inram(struct kvstore *kvs, void *key,s32 ksize, void *value, s32 vsize){    u64 idxhead = *((uint64_t *)key); /*无论key是128bit还是64bit,我们取64bit出来*/    u32 entry_size, head, tail;    char *ptr;    struct kvstore_inram *kvinram = NULL;    kvinram = container_of(kvs, struct kvstore_inram, ckvs);    entry_size = kvs->vsize + kvs->ksize;  /*确立每个单位entry_size,一般是:64bit + 128 bit*/    head = do_div(idxhead, kvinram->kmax);     /*这一步算出具体key在散列的位置,是把idxhead取余为head,    hash出来的key相同的概率之前算过了是非常低,除以kmax取余,    就是给key在找位置,这个位置head也一部分key的散列属性*/    tail = head;    /*这个循环很逗,虽然我的调试里没有显示出它起了作用,    但是我们前面说head虽然具有一定的散列属性,但它在这里并不具有唯一性,    因为key本身非常大128bit,他能代表一个pbn的内容的唯一性,    取余出的head却是一个小值,他不能唯一代表pbn,    虽然你可以认为head:150702代表idxhead:0x24100420901436,    并且在这个head位置保存它,但它却没有一唯一性,    那么如果产生了取余后的head所在位置的*ptr已经存在了,    就需要给这个key另找一个head来保存它,代码这里的做法是向后取,    找到一个NULL或者被deleted掉的*ptr,在这个位置把key记录下来*/    do {        ptr = kvinram->store + entry_size * head;    if (is_empty(ptr, entry_size) || is_deleted(ptr, entry_size)) {        memcpy(ptr, key, kvs->ksize);        memcpy(ptr + kvs->ksize, value, kvs->vsize);        return 0;    }    head = next_head(head, kvinram->kmax);    } while (head != tail);    return -ENOSPC;}

查找的代码和插入的代码,几乎就是一样的。
就是在do{}while里先从head开始找,找到一样的key值,就把它的value(pbn)拿出来。

static int kvs_lookup_sparse_inram(struct kvstore *kvs, void *key,s32 ksize, void *value, int32_t *vsize){    u64 idxhead = *((uint64_t *)key);    u32 entry_size, head, tail;    char *ptr;    struct kvstore_inram *kvinram = NULL;    int r = -ENODATA;    kvinram = container_of(kvs, struct kvstore_inram, ckvs);    entry_size = kvs->vsize + kvs->ksize;    head = do_div(idxhead, kvinram->kmax);    tail = head;    do {        ptr = kvinram->store + entry_size * head;        if (is_empty(ptr, entry_size))            return r;        if (memcmp(ptr, key, kvs->ksize)) {            head = next_head(head, kvinram->kmax);        } else {            memcpy(value, ptr + kvs->ksize, kvs->vsize);            return 0;        }    } while (head != tail);    return r;}

这里可能大家就会觉得很奇怪,为什么这里是这么简单的搜索方法,为什么不排序等等。

这里我的思考是这样的:首先这个是在inram的搜索,所以本身查找的性能还是很高的,其次在这个key取余的head值,虽然它不具有唯一性,但他具有key的散列属性,也就是说让它产生冲突的概率都不高,所以在pbn没有申请很多的情况下,应该都是直接一步就可以找到的,而且就算产生了冲突,它也是向后找,很可能就是下一个,所以就算碰撞,它保存的位置也具有相关联性,都会在它的后面的几个就可以找到,我认为这个性能应该还不错。

【本文只在51cto博客作者 "底层存储技术" https://blog.51cto.com/12580077 个人发布,公众号发布:存储之谷】,如需转载,请于本人联系,谢谢。

位置 就是 代码 方式 空间 唯一性 大小 数组 两个 代表 属性 流程 管理 迭代 不同 最大 信息 内容 函数 单位 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 软件开发培内容 西政论文查重数据库 互联网时代科技发展的主要原因 全球互联网科技公司市值 云服务器可以用来学习吗 一个服务器多个网站可以备案吗 职业院校网络安全集团 中国做硬件服务器的大公司有哪些 沧州师范学院计算机网络技术 添加数据库显示空值 义乌市搜猴网络技术有限公司 有关网络安全的主题班会记录 长沙银行软件开发社会招聘 海外独立服务器平台 肇庆自主可控软件开发费用是多少 网页形式的数据库 学生网络安全免费ppt 网络安全阅读作业 阿里巴巴服务器在哪个地方 网络安全软考有什么用 切实筑牢网络安全堤坝 网络安全七要点 石油石化行业网络技术的应用 中国服务器的商品是正品吗 江苏电子软件开发代理品牌 服务器可以远程发送么 崇明区大规模软件开发代理品牌 全球互联网医疗科技公司排名 网络技术的发展造福和造孽 海外独立服务器平台
0