Raid1原始碼分析--同步流程

來源:互聯網
上載者:User

標籤:

  同步的大流程是先讀,後寫。所以是分兩個階段,sync_request完成第一個階段,sync_request_write完成第二個階段。第一個階段由MD發起(md_do_sync),第二個階段由守護進程發起。

  如果是使用者發起的同步請求。該請求下發到raid1層,首先進入同步讀函數sync_request。在正常的成員盤中,將所有active可用的盤(rdev->flags中有In_sync標記)設定為read盤,而所有停用盤不做設定。對每一個可用盤對應的bios[]都單獨申請頁結構,對所有的read盤下發讀請求,讀成功之後,每塊read盤對應的bios[]的頁結構中都存放有該read盤中的內容。當所有讀請求都完成之時,將r1_bio添加到retry_list隊列,交由守護進程處理。接著,切換到守護進程,守護進程擷取到retry_list中的內容,判斷r1_bio->state為R1BIO_IsSync。然後調用同步寫函數sync_request_write來進行同步寫操作。接著順序遍曆成員盤的bios[],找到第一個讀成功的bio。然後將其與其他所有讀成功的bio做對比,如果有不一致的頁,那麼將不一致的頁拷貝到這些其他的讀成功bio中,並將這些bio也標記為寫請求,並將所有的寫請求下發,寫成功返回;所有頁都是一致的盤,則不用下發寫bio。見圖1。 

圖1 使用者發起的同步流程

 

  如果是非使用者發起的同步請求。該請求下發到raid1層,首先進入同步讀函數sync_request。在正常的成員盤中,將有In_sync標記的盤(標記在rdev->flags上)設定為read盤,將沒有In_sync標記的盤設定為write盤,對所有找不到的或Faulty盤不設定讀寫。所有成員盤對應的bios[]都共用一份頁結構,選取一個read盤下發讀請求,讀成功之後,該塊read盤中的內容讀取到bios[]的頁結構中。當讀請求完成之時,將r1_bio添加到retry_list隊列,交由守護進程處理。接著,切換到守護進程,守護進程擷取到retry_list中的內容,判斷r1_bio->state為R1BIO_IsSync。然後調用同步寫函數sync_request_write來進行同步寫操作。接著該函數將除了執行讀操作的盤之外的所有盤都標記為WRITE盤,並將寫操作下發。由於所有成員盤的bio都共用一份頁結構,所以寫操作下發的均為讀操作讀取到這份頁結構的所有內容。見圖2。

圖2 非使用者發起的同步流程

 

1)同步讀函數sync_request

  函數在MD進行同步操作時調用,作用是對裝置上指定的chunk進行資料同步。入參go_faster指示是否應該更快速進行同步。

  具體代碼流程如下:

  1.  如果同步緩衝的mempool沒有設定,則此時建立。

  2.  如果同步位置不在盤陣內,可能同步出錯了,或者已經完成同步。更新bitmap的相關資訊,並結束同步過程。

  3.  如果沒有指定bitmap(只有置位的bit對應的chunk才需要進行同步),並且盤陣沒有必要進行同步(磁碟正常工作時設定了mddev->recovery_cp = MaxSector),並且沒有設定同步請求位(!test_bit( MD_RECOVERY_REQUESTED, & mddev-> recovery)),並且conf設定為不需要全同步(conf->fullsync == 0,fullsync表示陣列要強制完全同步),則設定該段資料跳過,結束返回。

  4.  如果bitmap(bitmap_start_sync)認為無需同步,並且沒有設定同步請求位(!test_bit( MD_RECOVERY_REQUESTED, & mddev-> recovery)),並且conf設定為不需要全同步(conf->fullsync == 0),則設定該段資料跳過,結束返回。

  5.  如果當前裝置請求隊列忙於處理其他非同步類的訪問,並且沒有設定快速同步(go_fast為0),該同步請求就延時等待一會。如果設定了快速同步則沒有延時等待這一步。

  6.  在盤陣上設定barrier,保證盤陣的同步操作不受其他動作打擾。同樣,這裡也有bitmap的相關設定。

    註:raise_barrier(conf),該障礙在同步結束後end_sync_write中調用put_buf清除。

  7.  申請r1_bio,具體見r1buf_pool_alloc的流程,將其狀態設定為同步狀態。

  8.  遍曆盤陣中所有磁碟,進入迴圈。

    8.1  如果磁碟不存在,或者為Faulty狀態,則設定still_degraded 為1,遍曆下一個磁碟。

    8.2  如果磁碟不為In_sync狀態,則就是同步過程寫入的對象(非active盤)。

    8.3  否則就是同步過程中讀的目標。盡量避免通過WriteMostly的盤來讀,這是針對系統發起的同步而言的。

    註:通過變數wonly和disk來實現。

  9.  保證讀目標只有一個,其餘都是寫目標。

  10. 如果都為讀目標盤,或者都為寫目標盤,那麼則結束返回。

  11. 將max_sector調整為mddev->resync_max,同步不能超過這個值。

  12. 構建r1_bio中每個bio的bi_io_vec,將快取頁面申請並添加到數組中。直到數組滿或者達到一個同步操作要求的資料長度。

  13. 如果是使用者發起的同步(通過sysfs寫入“repair”),則對所有讀對象都發起讀請求;如果不是,則只發起一個可讀的磁碟的讀就可以了。

 

2)同步讀結束函數end_sync_read

  同步讀操作結束後,調用end_sync_read。

  1. 如果讀成功,該函數設定r1_bio的狀態為update態(set_bit(R1BIO_Uptodate, &r1_bio->state))。
  2. 如果r1_bio中所有讀都完成了,則調用reschedule_retry將r1_bio插入到盤陣的retry隊列中(隊列頭為conf的retry_list,隊列元素為r1_bio的retry_list),喚醒守護進程。

 

3)同步寫函數sync_request_write

  守護進程調用sync_request_write,進入同步的寫流程。

  1.  如果同步是使用者發起的。

    test_bit(MD_RECOVERY_REQUESTED, &mddev-> recovery)

    1.1   前面對所有可以讀的對象都發出了讀請求,而且也已經讀完成了。只要其中一個讀成功,則r1_bio的狀態會被設定為R1BIO_Uptodate,如果此時r1_bio仍然不為R1BIO_Uptodate,說明所有的讀都失敗了,此時設定所有讀的盤為故障,並且結束同步過程,結束返回。

    1.2   找到讀成功的第一個盤,遞減rdev的下發IO計數。並且將它與從其他盤讀的資料比較,如果發現有的盤和第一個讀成功的盤資料不同,則將這些盤也列為寫入對象,將讀成功的第一個盤的資料拷貝到這些盤的對應bio中,跳到步驟4。

  2.  如果同步不是使用者發起的。由於是在mempool中分配了RESYNC_PAGES個頁,所有的bio的bvec都指向它們。這就是說,對於非使用者發起的同步操作,讀和寫的緩衝區是同一個,所以不需要單獨的拷貝操作。直接進行後面的工作。

  3.  如果所有讀盤的讀操作全部失敗了,!test_bit(R1BIO_Uptodate, &r1_bio->state),則進行恢複嘗試。

    3.1  嘗試通過同步提供者sync_page_io從所有可能的磁碟讀出資料區塊。

    3.2  如果讀成功,則對這個同步操作的讀失敗的磁碟,嘗試進行一次寫,進行一次讀(都使用同步提供者)。

    3.3  如果都成功,則說明錯誤已經被糾正,否則調用md_error將該盤作廢。

  4.  最後,將所有需要寫的盤上的同步bio進行提交。設定bio結束操作為end_sync_write,返回。

 

4)同步寫結束函數end_sync_write

  最後,同步寫操作完成,調用end_sync_write函數,清除盤陣障礙,釋放r1_bio緩衝區,喚醒障礙上等待的任務。如果寫出錯,設定磁碟錯誤,並且調用bitmap_end_sync將本次同步操作的所有chunk對應的bitmap設定為需要進行同步(needed_mask)操作,下次同步時會嘗試恢複這個寫錯誤。

 

5)一些補充

5.1 讀寫緩衝區分析

  讀的資料要寫入,應該由讀的緩衝區拷貝到寫的緩衝區中,在使用者發起的同步中,是在sync_request_write的1.2中執行的。

  對於非使用者發起的同步而言,這個操作是在r1buf_pool_alloc函數中完成的。在函數sync_request的第一步,r1_bio = mempool_alloc(conf->r1buf_pool, GFP_NOIO); [fp3] 這個語句中,mempool會調用r1buf_pool_alloc進行實際的記憶體配置。

  這個函數在分配r1_bio的緩衝區時,除分配了其中的bio數組外,還分配了bio中的bvec頁,對於使用者發起的同步操作,是每個bio都分配了RESYNC_PAGES個頁;對於非使用者發起的同步操作,則是分配了RESYNC_PAGES個頁,所有的bio的bvec都指向它們。這就是說,對於非使用者發起的同步操作,讀和寫的緩衝區是同一個,所以不需要單獨的拷貝操作。

  r1buf_pool_alloc()函數在sync_request剛開始時調用。具體代碼流程如下:

  1. r1bio_pool_alloc申請一個r1_bio的空間。
  2. 如果r1_bio不存在,則返回NULL。
  3. 分配r1_bio中所有盤的bio。
  4. 如果同步請求是使用者發出的,則針對盤陣中的每一個bio分配RESYNC_PAGES個頁。
  5. 如果同步請求不是使用者發出的,那麼先對一個bio分配RESYNC_PAGES個頁,再將盤陣中的其他所有bio指標都指向第一個bio的page頁面,也就說所有bio的bvec都指向它們。

  關於記憶體池機制(mempool)多說兩句:

  首先利用mempool_create()來預先分配一定記憶體空間,作為一個“備用池”,在raid1中這個機制是用來分配r1_bio結構(包含bio的頁結構)。入參min_nr是預指派至的數目;alloc_fn和free_fn是指向分配和回收函數的指標,在raid1中分別對應的是r1buf_pool_alloc和r1buf_pool_free;pool_data指的是緩衝池對象的一些資訊,raid1中pool_data是*mddev和raid_disk。

  接著在緩衝池機制中,需要分配r1_bio空間的時候,調用mempool_alloc()。該函數首先在“備用池”外尋求分配空間,如果外面的空間不足,那麼就在“備用池”中分配。這樣就保證了重要的結構在分配的時候減少因為記憶體不夠導致空間分配失敗。

  其實就是先在記憶體中圈一片自留地,表明這是我的家底,不到資源緊缺的危急時刻不會動用它;當有結構分配的記憶體需求時,就先在自留地外面去尋找,先佔用公用的地方;如果公用的地方都被占完了,那就只能佔用自留地來滿足需求了。

 

5.2 使用者發起的同步的流程中,無WRITE盤的相關操作

       最初我懷疑是自己漏掉了某些代碼流程,後來分析發現使用者發起的同步中沒有WRITE盤相關操作的代碼是正確的。

sync_request()函數在判斷每個盤的bio是否為READ盤或者為WRITE盤,並指定對應的bio->bi_end_io是end_sync_read還是end_sync_write,是根據rdev->flages的In_sync是否置位來判斷的。但是,在這一步判斷之前,先判斷的是rdev->flages的Faulty標誌是否置位,如果Faulty置位,那麼就不設定READ或者WRITE,也不設定回呼函數,此時的回呼函數指標指向的是NULL。

       而rdev->flages的In_sync標誌表示的是這個盤是否active,置位則為active。設定In_sync為0的時候,除去sys介面和do_md_run中的特殊情況,只有下面兩種情況:

    1、盤陣中的盤出錯,調用error()函數的時候;

    2、Add disk的時候。

  而Add disk時候會由系統發起同步,可能還會在後面將rdev->flages的Insync標誌置為1,反正不會是使用者來進行這種同步操作,沒有具體進到函數中去看。

  在盤陣出錯的時候,會調用到error()函數,而在清除掉In_sync的時候,會降級,並設定Faulty標誌。

  在使用者發起的同步的時候,rdev->flags的In_sync位為1的盤,一定是READ盤;In_sync位為0的盤,Faulty位一定被置1了,所以不會標記為READ或者WRITE盤,並且bio->bi_end_io還是為NULL。這樣一來,使用者發起同步的時候,就不存在標記為WRITE的盤了,所以只確定READ盤的一致即可,不標記的盤只可能是error盤,使用者發起的同步對其不做任何處理。

  而在由於使用者發起的同步請求mddev->recovery也帶有MD_RECOVERY_SYNC標誌位,所以流程會走到write_targets += read_targets-1,於是就不會走到if (write_targets == 0 || read_targets == 0)的判斷條件中,從而避免了流程的終止。

  所以在使用者發起的同步時,流程中找不到WRITE操作的相關代碼是正確的。

 

轉載請註明出處:http://www.cnblogs.com/fangpei/

Raid1原始碼分析--同步流程

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.