1. 程式人生 > >device-mapper 塊級重刪(dm dedup) <3>程式碼結構(4)

device-mapper 塊級重刪(dm dedup) <3>程式碼結構(4)

、程式碼結構(4) I/O “小”寫流程


上一篇,介紹了dm dedup的寫流程,這一篇,介紹它的一個特殊流程

如果我們接收到的對齊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(r);
    //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(r);
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裡。




--------------未完待續--------------

【本文只在51cto部落格作者 “底層儲存技術” http://blog.51cto.com/12580077 個人釋出,公眾號釋出:儲存之谷】,如需轉載,請於本人聯絡,謝謝。