linux的qos機制 – dm-ioband篇 (4)

來源:互聯網
上載者:User

這篇延續上一篇的內容,通過幾個典型情境來分析dm-ioband的工作流程。

第一個情境是 http://sourceforge.net/apps/trac/ioband/wiki/dm-ioband/man/examples 中的example 1,首先調用命令建立兩個ioband device,

# echo "0 $(blockdev --getsize /dev/sda1) ioband /dev/sda1 1 0 0 none weight 0 :80" | dmsetup create ioband1# echo "0 $(blockdev --getsize /dev/sda2) ioband /dev/sda2 1 0 0 none weight 0 :40" | dmsetup create ioband2

dmsetup會調用ioband_ctr來建立ioband裝置,從ioband_ctr的注釋可以看出參數順序

/*
 * Create a new band device:
 *   parameters:  <device> <device-group-id> <io_throttle> <io_limit>
 *     <type> <policy> <policy-param...> <group-id:group-param...>
 */

其中 <device-group-id>ioband_device->g_name,用以唯一標識一個ioband裝置。P.S. 這裡ioband1, ioband2兩個裝置其實對應同一個ioband_device,其ioband-group-id為1 。而傳入的<device> 為底層真實的塊裝置,如 /dev/sda1, /dev/sda2。會調用dm_get_device把這個塊裝置加到dm_table->devices
列表中。下面調用alloc_ioband_device( <device-group-id>, <io_throttle>, <io_limit> ) 來建立ioband_device,由於此時已經有了<device-group-id>為 1 的ioband_device,因此只是簡單的把ioband_device->g_ref++就結束了。核心全域的有一個 ioband_device_list 的list_head,所有的ioband_device都屬於這個list_head,這些ioband_device通過ioband_device->g_list
組織起來。

下面調用policy_init,傳入的參數包括 <policy> <policy-param...> <group-id:group_param...>,首先進行policy 名字的比對,在全域變數 dm_ioband_policy_type 中查詢名字叫weight的policy,之後會調用對應的policy_weight_init 函數。 P.S. ioband_device的g_groups連結了attach在上面的所有ioband_group,這個list_head的變數對應於ioband_group的
c_list 成員,通過container_of宏可以得到ioband_group
。 得到的struct ioband_policy_type 賦值給 ioband_device->g_policy。

調用policy_weight_init的時候,傳入的參數為 <policy-param...> <group-id: group-param...>這些參數。weight policy 第一個參數是<token base>,之後是<group-id:group-param> 的數組,policy_weight_init 不對
<group-id:group-param> 的隊列做處理。

下面會調到 ioband_group_init 首先建立 default ioband group,在這個情境中,會有同一個ioband_device出現了兩個default ioband_group,其中c_id為 IOBAND_ID_ANY。ioband_device->g_root_groups儲存了同一個 <device-group-id> 中所有的根group,ioband_group_init 會通過list_add_tail,把ioband_device->g_root_groups加到ioband_group->c_sibling裡面,可以看出每個ioband_group->c_sibling儲存了同層級的所有ioband_group,而ioband_device->g_root_groups裡所有的ioband_group互相都是sibling。本情境下,兩個ioband_group的c_parent,
c_children都為空白,c_sibling有兩個ioband_group。

對於ioband_group其他的成員: c_children表示這個group之下的所有子group,c_parent表示父group,c_sibling表示兄弟group,由於ioband_group是按照紅/黑樹狀結構的結構組織的,c_group_root表示這顆紅/黑樹狀結構的樹根,c_group_node表示當前group在這顆紅/黑樹狀結構上的節點。

最後調用ioband_group_type_select 選擇 ioband_group_type,這裡的 type為 none

############################

OK,現在我們來看請求執行流,請求進入device mapper之後,最終會落到 ioband_map 函數中,ioband_map執行步驟如下:

  1. 調用ioband_group_get,擷取bio對應的ioband_group。如果 t_getid 函數指標為空白,說明是default group,否則根據bio調用dm_ioband_group_type相應函數擷取group type
  2. 由於<device-group-id>為1的ioband_device只有兩個default group,在group初始化函數 policy_weight_ctr 時,會對其分配token。policy_weight_ctr 會調用 policy_weight_param,最終會調用 set_weight 來設定weight 。
  3. set_weight 先填充 ioband_group->c_weight,計算出c_sibling所有的c_weight之和,取出base_token,base_io_limit 等值(也就是parent 的token, io_limit值,如果parent為空白就是ioband_device的相應值),再調用 __set_weight 。 __set_weight 裡面根據c_weight 占的比重基於base_token分配token,基於 base_io_limit 分配io_limit,如果有必要修改parent裡的相應值。
    至此,ioband_group->c_token, ioband_group->c_token_initial, ioband_group->c_token_bucket 值都被設為被分配的token值。
  4. 處理請求時,判斷 is_token_left 是否有剩餘token可用,如果有就調用 consume_token,這樣bio就被執行;否則會調用delayed_work機制延遲處理
  5. 延遲處理的 ioband_conduct 方法會進行一次判斷,如果 nr_blocked(dp),room_for_bio_sync(dp, BLK_RW_SYNC/ASYNC),issue_list, pushback_list都為空白,這三個條件成立,說明剩下還block的bio都是由於token耗盡,調用dp->g_restart_bios 重新放出一輪token,這個行為由 make_global_epoch 完成
  6. make_global_epoch 是個很有意思的函數,首先它找出ioband_device中擁有token最多的group,如果該group擁有的token過多,同時此時該group的IO負載很高的話(iopriority(gp) * PROCESS_THRESHOLD > IOBAND_IOPRIO_BASE && nr_issued(dp) >= dp->g_io_throttle),此時會優先服務這個group,不會放token出來,否則ioband_device增加一個epoch,對於所有group而言,增加
    ioband_group->c_token_initial 個數個token

對於本情境的一種極端情況,即一個default group一直有IO請求,而另外一個一直沒有,這種機制會保證沒有group會token-starving。

-------------------------------------------------華麗的分割線-----------------------------------------------------

第二個情境是 http://sourceforge.net/apps/trac/ioband/wiki/dm-ioband/man/examples 的 example 3,這種情境下,ioband2上多了個uid=1000,weight=20的group。

首先是__ioband_message把type設定為uid

其次在__ioband_message中調用ioband_group_attach(struct ioband_group* head, 0, 1000, NULL) 時,head為原有default group的指標,只是現在head->c_type變成了dm_ioband_group_type中的uid group type。parent_id為0,group id為1000。之後再調用ioband_group_init(dp, head, NULL, gp, 1000, NULL),這裡gp為新分配出來的一塊記憶體,用於儲存ioband_group,這裡gp->c_id
= id,這時ioband_group的id就不是default group中的IOBAND_ID_ANY了,而是uid 1000。這個新分配的ioband_group被掛在ioband_device的g_groups下,因此這個ioband_device目前就有了3個ioband_group。同時ioband2這個group的紅/黑樹狀結構下面,有兩個node,一個屬於default group,一個屬於uid group。這兩個group的c_dev,c_target都是相同的。

最後調用__ioband_message中的ioband_set_param來設定weight。最後還是調用policy_weight_param(gp, "weight", "20") 。

現在ioband_device下面有3個ioband_group了,那麼如何知道請求到底是屬於哪個group呢?還記得ioband_map中的ioband_group_get 麼,對於目的為ioband2的user=1000的進程發出的請求,傳入ioband_group_get 中第一個參數為 default group 的指標。由於此時default group 的c_type已經變成了uid group type,會調用ioband_group_find 在紅/黑樹狀結構上尋找 c_id =1000 的ioband_group,這個尋找演算法要麼返回
c_id 為1000的 rb_node,要麼返回c_id 為 IOBAND_ID_ANY的default group的rb_node,因此不是uid=1000的進程發出的bio,最後都會落到default group上。而uid=1000的進程發出的bio會落到c_id=1000的ioband_group上。

最後總結下來,這個<device-group-id>包含了g_ref為2的ioband_device,裡面有兩個塊裝置/dev/mapper/ioband1, /dev/mapper/ioband2,同時有3個ioband_group,按照權重來分享token

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.