1. 程式人生 > >device-mapper 塊級福運來原始碼下載重刪(dm dedup)

device-mapper 塊級福運來原始碼下載重刪(dm dedup)

如果福運來原始碼下載,需要請搜尋【大神原始碼論壇】dsluntan.com 客服企娥3393756370 V信17061863513,我們接收到的對齊bio但是它的size < block_size,那麼這時候是不能直接進行hash的。

需要將它的缺少的部分讀出來,填充成一個完整的block_size才能計算hash。

接下來我們就介紹這一部分的程式碼流程。

static int handle_write(struct dedup_config *dc, struct bio bio) { u64 lbn; u8 hash[MAX_DIGEST_SIZE]; struct hash_pbn_value hashpbn_value; u32 vsize; struct bio new_bio = NULL; int r; /

If there is a data corruption make the device read-only / if (dc->corrupted_blocks > dc->fec_fixed) return -EIO; dc->writes++; / Read-on-write handling / if (bio->bi_iter.bi_size < dc->block_size) { dc->reads_on_writes++; new_bio = prepare_bio_on_write(dc, bio); if (!new_bio || IS_ERR(new_bio)) return -ENOMEM; bio = new_bio; } /
/ } 對於“小”寫這種操作,也被叫做reads_on_writes,或者read_motify_write

一起看看這個new_bio是如何被構造出來的。

struct bio *prepare_bio_on_write(struct dedup_config *dc, struct bio *bio) { int r; sector_t lbn; uint32_t vsize; struct lbn_pbn_value lbnpbn_value; struct bio clone; //DMINFO("\nEntered prepare bio on write"); lbn = compute_sector(bio, dc); (void) sector_div(lbn, dc->sectors_per_block); /

check for old or new lbn and fetch the appropriate pbn */ r = dc->kvs_lbn_pbn->kvs_lookup(dc->kvs_lbn_pbn, (void *)&lbn, sizeof(lbn), (void *)&lbnpbn_value, &vsize); if (r == -ENODATA) clone = prepare_bio_without_pbn(dc, bio); else if (r == 0) clone = prepare_bio_with_pbn(dc, bio, lbnpbn_value.pbn * dc->sectors_per_block); else return ERR_PTR®; //DMINFO("\nExiting prpare_bio_on_write"); return clone; } 我們注意:這裡compute_sector算出來的,是bio sector的lbn

①:它有以下幾種情況:

[x—y]

[x--------z] -> x/block_size

   [x----y]

[a--------y] -> x/block_size

   [x--y]

[a----------z] -> x/block_size

他們都會得到相同的lbn=x/block_size

    ② dc->kvs_lbn_pbn->kvs_lookup

這裡需要將剛才算出來的lbn來求出是否存在,不存在填充zero。

接下啦就是兩種情況了,這個lbn是否存在pbn。

一、 lbn without pbn  "clone 

     prepare_bio_without_pbn(dc, bio);"

static struct bio *prepare_bio_without_pbn(struct dedup_config *dc, struct bio *bio) { int r = 0; struct bio *clone = NULL; clone = create_bio(dc, bio); if (!clone) goto out; zero_fill_bio(clone); r = merge_data(dc, bio_page(clone), bio); if (r < 0) return ERR_PTR®; out: return clone; } 這個很容易理解,既然不存在lbn_pbn的對應關係,那麼這裡就是直接填充zero。

這裡用了核心提供的函式zero_fill_bio。如果我做,我可能不知道這個函式,大家要利用這個函式。

那麼這裡很簡單,就是先建立一個null的bio,然後把這個bio的page全部填充成zero。

在和bio的進行合併。這裡merge_data是程式碼實現的,我們看一下。

static int merge_data(struct dedup_config *dc, struct page *page, struct bio *bio) { sector_t bi_sector = bio->bi_iter.bi_sector; void *src_page_vaddr, dest_page_vaddr; int position, err = 0; struct bvec_iter iter; struct bio_vec bvec; / Relative offset in terms of sector size / position = sector_div(bi_sector, dc->sectors_per_block); if (!page || !bio_page(bio)) { err = -EINVAL; goto out; } / Locating the right sector to merge / dest_page_vaddr = page_address(page) + to_bytes(position); bio_for_each_segment(bvec, bio, iter) { src_page_vaddr = page_address(bio_iter_page(bio, iter)) + bio_iter_offset(bio, iter); / Merging Data / memmove(dest_page_vaddr, src_page_vaddr, bio_iter_len(bio, iter)); / Updating destinaion address */ dest_page_vaddr += bio_iter_len(bio, iter); } out: return err; } 我們做塊級功能研發的人,要對記憶體和bio sectors操作也要熟悉,

我曾經有段時間自己寫程式碼就被segments和sectors各種對應繞暈。

這個程式碼寫的還是比較鮮明的,先找出bi_sector在block_size內的位置

比如

   [01234567]                              [01234567]

 <       data   >                         free<>  

 < 00000000  >         ------------><000data0>                           

                           position就是3

    這時候,用page_address將[position]的資料轉換成ptr

    然後將data memmove到3456的<0-0>的ptr上面就可以了。

    那麼有一點複雜的是<  data >他也可能是割裂的比如:

sector [01234567] [01234567] [01234567]

  s1  <       da>                       free<s1>                       free<s1> 

 s2              <ta   >                             <ta  >                free<s2> 

           < 00000000  >        ----><000da000>        --><000data0>

   這裡需要將多個segments的page,s1和s2的內容,分別memmove到page裡。

一、 lbn without pbn "clone

     prepare_bio_without_pbn(dc, bio);"

static struct bio *prepare_bio_with_pbn(struct dedup_config *dc, struct bio *bio, uint64_t pbn) { int r = 0; struct page_list *pl; struct bio *clone = NULL; pl = kmalloc(sizeof(pl), GFP_NOIO); if (!pl) goto out; / * Since target I/O size is 4KB currently, we need only one page to * store the data. However, if the target I/O size increases, we need * to allocate more pages and set this linked list correctly. */ pl->page = alloc_pages(GFP_NOIO, 0); if (!pl->page) goto out_allocfail; pl->next = NULL; r = fetch_whole_block(dc, pbn, pl); if (r < 0) goto out_fail;

r = merge_data(dc, pl->page, bio);
if (r < 0)
    goto out_fail;

clone = create_bio(dc, bio);
if (!clone)
    goto out_fail;

copy_pages(pl->page, clone);

out_fail: free_pages((unsigned long) page_address(pl->page), 0); out_allocfail: kfree(pl); out: if (r < 0) return ERR_PTR®; return clone; }