三、代碼結(jié)構(gòu)(1) 基礎(chǔ)構(gòu)架
成都創(chuàng)新互聯(lián)公司專業(yè)為企業(yè)提供伊吾網(wǎng)站建設(shè)、伊吾做網(wǎng)站、伊吾網(wǎng)站設(shè)計、伊吾網(wǎng)站制作等企業(yè)網(wǎng)站建設(shè)、網(wǎng)頁設(shè)計與制作、伊吾企業(yè)網(wǎng)站模板建站服務(wù),10余年伊吾做網(wǎng)站經(jīng)驗,不只是建網(wǎng)站,更提供有價值的思路和整體網(wǎng)絡(luò)服務(wù)。
邏輯推理地看源碼是學(xué)習(xí)代碼最清晰的方法,這樣對代碼的記憶會提高很多。
能夠從復(fù)雜的代碼結(jié)構(gòu)中找到邏輯關(guān)系也是非常重要的一個技能。
以上是dm dedup的主要代碼邏輯關(guān)系。
因為其主要的設(shè)計已經(jīng)在上一篇有介紹過了,所以我們這里直接分析代碼流程。
四、代碼結(jié)構(gòu)(1) I/O入口 dm_dedup_map
1、dm_dedup_map:這個是從dm.c->dm_dedup.c主要調(diào)用接口
① chunk data的對其切分
首先要解釋的是:圖中chunk bio的過程,是由dm.c中的split_and_process_bio實現(xiàn)的
while (ci.sector_count && !error) {
error = __split_and_process_non_flush(&ci);
if (current->bio_list && ci.sector_count && !error) {
struct bio *b = bio_split(bio, bio_sectors(bio) - ci.sector_count,
GFP_NOIO, &md->queue->bio_split);
ci.io->orig_bio = b;
bio_chain(b, bio);
ret = generic_make_request(bio);
break;
}
}
這段其中比較核心的是split大的BIO變成以某個方式對齊
看明白如何對齊split的,就必須對_split_and_process_non_flush進(jìn)行分析
static int __split_and_process_non_flush(struct clone_info *ci)
{
struct bio *bio = ci->bio;
struct dm_target *ti;
unsigned len;
int r;
ti = dm_table_find_target(ci->map, ci->sector);
if (!dm_target_is_valid(ti))
return -EIO;
if (unlikely(__process_abnormal_io(ci, ti, &r)))
return r;
if (bio_op(bio) == REQ_OP_ZONE_REPORT)
len = ci->sector_count;
else
len = min_t(sector_t, max_io_len(ci->sector, ti),
ci->sector_count);
r = __clone_and_map_data_bio(ci, ti, ci->sector, &len);
if (r < 0)
return r;
ci->sector += len;
ci->sector_count -= len;
return 0;
}
首先I/O對齊split中有比較重要的就是幾個問題:
① 到底是如何切分BIO的?
切分這個讀者應(yīng)該都很容易看懂,就是不斷去ci->sector += len和ci->sector_count -= len;
通過將ci->sector不斷通過len增加,然后ci->sector_count總量不斷減少
制造一個個被split的sub_BIOs。
② 為什么說切分是對齊的?
這就涉及到len的大小,這里我們舉個例子:
bio:【bi_sector:3 size=8】應(yīng)該被切分為什么樣子?
如果按照size=4去切分?那應(yīng)該對其后的結(jié)果是:
3%4 = 3 ,bio_split_1 = [1_bi_sector:3,1_size=1],t_size = 8-1=7;bi_sector:4;
4%4 =0, bio_split_2 = [2_bi_sectoer:4,2_size=4],t_size= 7-4=3;bi_secor:8;
8%4 = 0,bio_split_3 = [3_bi_sectoer:8,3_size=3],t_size= 3-4=-1;bi_secor:11;
其實這里我們演算出來的規(guī)律,正是max_io_len的代碼的邏輯關(guān)系:
static sector_t max_io_len(sector_t sector, struct dm_target *ti)
{
sector_t len = max_io_len_target_boundary(sector, ti);
sector_t offset, max_len;
/*
* Does the target need to split even further?
*/
if (ti->max_io_len) {
offset = dm_target_offset(ti, sector);
if (unlikely(ti->max_io_len & (ti->max_io_len - 1)))
max_len = sector_div(offset, ti->max_io_len);
else
max_len = offset & (ti->max_io_len - 1);
max_len = ti->max_io_len - max_len;
if (len > max_len)
len = max_len;
}
return len;
}
③ 明白了切分的方法,那么還有一個問題就是,max_io_len的n%splt_size的ti>max_io_len是多少呢?
按照多大切分的我們也需要搞明白一下。
這個過程很簡單,大概的過程就是向上推找到這個值的賦值,初始含義和可配置的地方。
最終看到這個值是在dm_dedup_ctr里傳的一個參數(shù)block_size所決定的,也就是塊大小。
這個block_size值得就是hash index的單位,在dm_dedup里它內(nèi)約束在了4k到1M的區(qū)間內(nèi).
#define MIN_DATA_DEV_BLOCK_SIZE (4 1024)
#define MAX_DATA_DEV_BLOCK_SIZE (1024 1024)
OK ,目前我們約定俗成的認(rèn)為它就是page size 4k,那么這樣就很好理解了。
這樣被對齊split后的bio,為什么要對齊split,主要是為了對齊split bio能夠?qū)?yīng)一個pbn,這樣就可以以某個pbn的hash來代表它。
② 多線程處理每個chunk_bio
static int dm_dedup_map(struct dm_target *ti, struct bio *bio)
{
dedup_defer_bio(ti->private, bio);
return DM_MAPIO_SUBMITTED;
}
static void dedup_defer_bio(struct dedup_config *dc, struct bio *bio)
{
struct dedup_work *data;
data = mempool_alloc(dc->dedup_work_pool, GFP_NOIO);
if (!data) {
bio->bi_error = -ENOMEM;
bio_endio(bio);
return;
}
data->bio = bio;
data->config = dc;
INIT_WORK(&(data->worker), do_work);
queue_work(dc->workqueue, &(data->worker));
}
這個代碼原理非常簡單,用mempool申請work,用queue_work去分發(fā)請求到各個cpu。
這里如果想做的更好一點,可以做一個cpu池,在創(chuàng)建設(shè)備的時候可讓配置其cpu親和,單cpu命令隊列深度(最大IO合并的大小)。
static void process_bio(struct dedup_config *dc, struct bio *bio)
{
int r;
if (bio->bi_opf & (REQ_PREFLUSH | REQ_FUA) && !bio_sectors(bio)) {
r = dc->mdops->flush_meta(dc->bmd);
if (r == 0)
dc->writes_after_flush = 0;
do_io_remap_device(dc, bio);
return;
}
switch (bio_data_dir(bio)) {
case READ:
r = handle_read(dc, bio);
break;
case WRITE:
r = handle_write(dc, bio);
}
if (r < 0) {
bio->bi_error = r;
bio_endio(bio);
}
}
最后解析一下bio讀寫的方向然后去給handle_read和handle_write去分發(fā)請求。
如果認(rèn)真看的讀者,應(yīng)該已經(jīng)清楚明白了,map的流程就是:dm_bio(大bio)被以block_size對齊split后帶多cpu處理的一個流程。
這里是dm-dedup的發(fā)動機,很多人可能要問,為什么這里要做成異步處理的形式,為什么不直接就在上層派發(fā)dm_bio的task里就把dedup的工作做完?
我認(rèn)為這里這么做,主要是考慮到了dedup算hash index需要大量的時間,所以高并發(fā)情況下這個程序最終表現(xiàn)出的性能,可能都在多個cpu在計算hash上面。
如果在dm_bio的task里面做hash ,相當(dāng)于沒有流水線并發(fā)能力,單線程在算hash,計算就會是io性能的瓶頸,這里比較好的解決了這個問題,但是這里沒有很好的考慮到I/O合并(如果I/O不能合并,可能會造成巨大的I/O latency),和各個cpu的請求隊列深度均衡問題。
【本文只在51cto博客作者 “底層存儲技術(shù)” https://blog.51cto.com/12580077 個人發(fā)布,公眾號發(fā)布:存儲之谷】,如需轉(zhuǎn)載,請于本人聯(lián)系,謝謝。
當(dāng)前名稱:device-mapper塊級重刪(dmdedup)<3>代碼結(jié)構(gòu)(1)
網(wǎng)站網(wǎng)址:http://muchs.cn/article26/pipdjg.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供企業(yè)建站、網(wǎng)頁設(shè)計公司、域名注冊、做網(wǎng)站、搜索引擎優(yōu)化、電子商務(wù)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)