六、代碼結構(4) I/O “小”寫流程
創(chuàng)新互聯(lián)建站服務項目包括普陀網(wǎng)站建設、普陀網(wǎng)站制作、普陀網(wǎng)頁制作以及普陀網(wǎng)絡營銷策劃等。多年來,我們專注于互聯(lián)網(wǎng)行業(yè),利用自身積累的技術優(yōu)勢、行業(yè)經(jīng)驗、深度合作伙伴關系等,向廣大中小型企業(yè)、政府機構等提供互聯(lián)網(wǎng)行業(yè)的解決方案,普陀網(wǎng)站推廣取得了明顯的社會效益與經(jīng)濟效益。目前,我們服務的客戶以成都為中心已經(jīng)輻射到普陀省份的部分城市,未來相信會繼續(xù)擴大服務區(qū)域并繼續(xù)獲得客戶的支持與信任!
上一篇,介紹了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=lbn [x----y] [a--------y] -> x/block_size=lbn [x--y] [a----------z] -> x/block_size=lbn
他們都會得到相同的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。
這里用了內(nèi)核提供的函數(shù)zero_fill_bio。如果我做,我可能不知道這個函數(shù),大家要利用這個函數(shù)。
那么這里很簡單,就是先創(chuàng)建一個null的bio,然后把這個bio的page全部填充成zero。
在和bio的進行合并。這里merge_data是代碼實現(xiàn)的,我們看一下。
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; }
我們做塊級功能研發(fā)的人,要對內(nèi)存和bio sectors操作也要熟悉,
我曾經(jīng)有段時間自己寫代碼就被segments和sectors各種對應繞暈。
這個代碼寫的還是比較鮮明的,先找出bi_sector在block_size內(nèi)的位置
比如
sector [01234567] [01234567] s <data> free<> page <00000000> ---><000data0>
position就是3
這時候,用page_address將[position]的數(shù)據(jù)轉換成ptr
然后將data memmove到3456的<0-0>的ptr上面就可以了。
那么有一點復雜的是< data >他也可能是割裂的比如:
sector [01234567] [01234567] [01234567] s1 <da> free<s1> free<s1> s2 <ta> <ta> free<s2> page <00000000> --><000da000> --><000data0>
這里需要將多個segments的page,s1和s2的內(nèi)容,分別memmove到page里。
一、 lbn without pbn "clone
prepare_bio_with_pbn(dc, bio);"
這里實現(xiàn)的思路,是既然發(fā)現(xiàn)了pbn,那么就把pbn讀出來,然后把新的內(nèi)容覆蓋再寫
這種方式就是標準的讀-改-寫,通常在I/O小于region_size的情況下,需要這么做。
因為需要block_size對齊,所以從pbn讀一整個block_size出來。
static int fetch_whole_block(struct dedup_config *dc, uint64_t pbn, struct page_list *pl) { struct dm_io_request iorq; struct dm_io_region where; unsigned long error_bits; where.bdev = dc->data_dev->bdev; where.sector = pbn; where.count = dc->sectors_per_block; iorq.bi_op = REQ_OP_READ; iorq.bi_op_flags = 0; iorq.mem.type = DM_IO_PAGE_LIST; iorq.mem.ptr.pl = pl; iorq.mem.offset = 0; iorq.notify.fn = NULL; iorq.client = dc->io_client; return dm_io(&iorq, 1, &where, &error_bits); }
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(r); return clone; }
已經(jīng)讀出來的pl->page的pbn的內(nèi)容和bio做merge,這個和上面zero的情況一下。
最后再將merge好的,數(shù)據(jù)拷貝到bio_new中,去做下一輪handle_write處理,也就是上一篇的內(nèi)容。
這一篇的內(nèi)容比較簡單,所以沒有圖示,
--------------未完待續(xù)--------------
【本文只在51cto博客作者 “底層存儲技術” https://blog.51cto.com/12580077 個人發(fā)布,公眾號發(fā)布:存儲之谷】,如需轉載,請于本人聯(lián)系,謝謝。
分享題目:device-mapper塊級重刪(dmdedup)<3>代碼結構(4)
網(wǎng)站路徑:http://m.rwnh.cn/article22/pcddcc.html
成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供虛擬主機、微信公眾號、全網(wǎng)營銷推廣、網(wǎng)站排名、手機網(wǎng)站建設、電子商務
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉載內(nèi)容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉載,或轉載時需注明來源: 創(chuàng)新互聯(lián)