前面兩部分主要摘抄於其它的論文和資料,后面部分是對代碼的分析結果。
1 Multipath概述
DM-Multipathing (DM-MPIO) provides I/O failover and load-balancing within Linux for block devices. By utilizing device-mapper, multipathd provides the host-side logic to use multiple paths of a redundant network to provide continuous availability and higher bandwidth connectivity between the host server and the block-level device.
2 Multipath基本原理
2.1 Device Mapper
在內核中它通過一個一個模塊化的 target driver 插件實現對 IO 請求的過濾或者重新定向等工作,當前已經實現的 target driver 插件包括軟 raid、軟加密、邏輯卷條帶、多路徑、鏡像、快照等,圖中 linear、mirror、snapshot、multipath 表示的就是這些 target driver。
Device mapper 進一步體現了在 Linux 內核設計中策略和機制分離的原則,將所有與策略相關的工作放到用戶空間完成,內核中主要提供完成這些策略所需要的機制。Device mapper 用戶空間相關部分主要負責配置具體的策略和控制邏輯,比如邏輯設備和哪些物理設備建立映射,怎么建立這些映射關系等等,而具體過濾和重定向 IO 請求的工作由內核中相關代碼完成。因此整個 device mapper 機制由兩部分組成,內核空間的 device mapper 驅動、用戶空間的device mapper 庫以及它提供的 dmsetup 工具。
2.1.1 Device Mapper內核對象
mapped_device: device mapper生成的邏輯塊設備,map指向dm_table
dm_table: mapped_device和物理設備target的映射,按照B樹組織dm_target,便於IO請求映射的查找
dm_target: 記錄映射的mapped device的邏輯區域的開始地址和范圍,type指向target設備的驅動類型(target_type),private記錄具體設備的數據。
target_type multipath_target: mulitpath類型的target的driver,包括創建、銷毀device的方法。
Device mapper本質功能就是根據映射關系和target driver描述的IO處理規則,將IO請求從邏輯設備mapped device轉發相應的target device上。Device mapper處理所有從內核中塊一級IO子系統的generic_make_request和submit_bio接口中定向到mapped device的所有塊讀寫IO請求。IO請求在device mapper的設備樹中通過請求轉發從上到下地進行處理。當一個bio請求在設備樹中的mapped deivce向下層轉發時,一個或者多個bio的克隆被創建並發送給下層target device。target driver結束某個bio請求后,將表示結束該bio請求的事件上報給它上層的mapped device,該過程在各個層次上進行直到該事件最終上傳到根mapped device的為止,然后device mapper結束根mapped device上原始bio請求,結束整個IO請求過程。
2.1.2 Device Mapper用戶態功能
(1) 發現每個mapped device相關的target device;
(2) 根據配置信息創建映射表;
(3) 將用戶空間構建好的映射表傳入內核,讓內核構建該mapped device對應的dm_table結構;
(4) 保存當前的映射信息,以便未來重新構建。
2.2 Multipath
The Device Mapper multipath module of the Linux kernel:multipath_target
The multipath-tools userspace package:multipath-tools take care of automatic path discovery and grouping as well as automated path retesting, so that a previously failed path is automatically reinstated when it becomes healthy again. This minimizes the need for administrator attention in a production environment. Include Multipathd service and multipath(CLI)
2.2.1 multipath_target
主要提供的方法(每種類型的target都必須提供):
multipath_ctr:構建target device 的方法
multipath_dtr:刪除target device 的方法
multipath_map:Target的映射IO請求的方法,調用map_io
multipath_status:獲取當前target device狀態的訪問
multipath_message: Target 處理用戶消息的方法,根據具體的消息類型會調用fail_path, queue_if_no_path, switch_pg_num等方法
multipath_end_io:Target結束IO請求的方法
3 Multipath實現分析
3.1 對象
Multipath
Pathgroup(優先組)
Path
配置文件中path_grouping_policy項如果設定為failover,則每個path都在不同的pathgroup,如果設置為multibus,則所有的path都在一個pathgroup。
3.2 配置
path_grouping_policy
path_checker:指定用於決定路徑狀態的默認方法,包括:readsector0\directio
failback: 路徑出錯恢復正常后切換策略,包括immediate/manual/數字(代表時間)
no_path_retry:指定所有路徑都失敗后應該再試圖嘗試的次數。Queue/fail/數字(代表次數)
path_selector:指定用來決定下一個 I/O 操作所使用路徑的默認算法。Round-robin/queue-length。
Prio: 指定要獲得路徑優先值所需調用的默認程序及參數。默認是const,即優先值都為1
Hardware_handler(device配置):指定將在切換路徑組群或者處理 I/O 錯誤時用來執行硬件具體動作的模塊。配置值:emc/hp_sw/rdac。默認為”0”
3.3 事件機制
The multipath daemon learns of and reacts to changes in both the current block device configuration and the kernel resident multipathing configuration.
The addition of a new path or the removal of an already existing path to an already managed block device is detected over a netlink socket as a uevent triggered callback which adds or removes the path to or from the set of paths which will be actively tested.
Changes to the kernel resident multipathing state are detected as device-mapper generated event callbacks. Events of this kind invole block io errors, path state change, and changes in the highest priority path group for a mapped device.
3.3.1 Device-mapper event
內核Multipath對象的成員trigger_event,由Dm-mpath.c中的trigger_event函數賦值,
trigger_event調用Dm-table.c的dm_table_event函數,dm_table_event調用dm_table對象的event_fn,event_fn在Dm.c的dm_table_event_callback函數(對於該函數何時被調用還有疑問!!!)里被賦值為event_callback。
event_callback會wake_up mapped_device的eventq,而Dm_ioctl.c中dev_wait函數調用dm_wait_event函數等待eventq的事件中斷。
用戶態每個multipath對象都會啟動一個waiter_thread,線程函數waitevent while(1)循環調用waiteventloop函數,waiteventloop最終調用內核的dev_wait方法。
3.3.2 Uevent
用戶態ueventloop線程監聽Netlink events事件,調用uev_add_map/uev_add_path等方法處理map(未發現內核有此類事件!!!)和path相關的事件。
內核態dm_path_uevent(Dm_uevent.c)向Netlink events中加入path相關事件到mapped_device對象的uevent_list中。dm_send_uevents函數發送事件,該函數被event_callback方法調用。
3.4 路徑發現
3.4.1 自動發現
Multipathd服務啟動時會調用configure方法,該方法依次執行發現路徑、過濾路徑、發現multipath、通過Path生成Multipath、同步用戶態和內核路徑狀態、啟動Multipath的DM事件監聽等操作。
發現路徑:Path_discovery從/sys/block目錄下獲取設備名,即路徑名,通過ioctl等方法獲取設備信息。
過濾路徑:filter_path根據配置文件中blacklist項的配置過濾路徑。
發現multipath: 通過libdevmapper向內核DM發送DM_DEVICE_LIST指令,獲取所有DM設備,保留multipath類型的設備,獲取alias,size,params,status等信息,使用setup_multipath方法,首先從params中解析出multipath的部分配置(pathgroup/path)和狀態,然后從配置文件(或者bulid-in)中獲取mpe、hwe,再根據mpe、hwe的中配置multipath的rr_weight\pgfailback\no_path_retry等配置。
Multipathd第一次啟動從DM獲取multipath,返回應該為空。
通過Path生成Multipath:coalesce_paths遍歷所有路徑,mpp=NULL的生成multipath對象,通過adopt_paths將wwid相同的其它路徑加入到multipath的paths中。verify_paths檢查multipath中所有的path是否都能從sysfs獲取設備信息,如果不行刪除。setup_map通過mpe/hwe/conf設置multipath配置,根據pgpolicy從path生成pathgroup(路徑分組),從配置打包params。select_action確定要對dm要執行的操作(create/rename),domap向內核DM層發送相應指令,DM創建相應的multipath_target。通過dm_message設置no_path_retry。刪除deadmap(所有path都not in sysfs的Multipath)
同步用戶態和內核路徑狀態: 遍歷所有multipath的所有path,根據dmstate和state的差別,調用dm_reinstate_path或dm_fail_path向DM發送對應message,使內核管理的path狀態和用戶態一致。
啟動DM事件監聽:用戶態每個multipath對象啟動一個監聽線程,監聽來自內核DM的報告的事件,可能包括路徑錯誤等事件
3.4.2 手動添加
使用Multipathd –k命令進去CLI管理界面,add path或add map可以添加路徑和multipath
3.5 路徑檢查
3.5.1 路徑狀態
PATH_UNCHECKED: checker方法獲取失敗,或checker_init調用失敗返回。
PATH_WILD: sysfs接口查詢設備及狀態失敗時返回。
PATH_PENDING:從sysfs獲取的設備狀態是”blocked”。
PATH_DOWN:從sysfs獲取的設備狀態是”offline”,或通過checker方法檢查返回。
PATH_UP:從sysfs獲取的設備狀態是”running”,並且通過了checker方法的檢查。
PATH_SHAKY:emc_clariion檢查方法中出現,類似於PATH_DOWN
PATH_GHOST: hp_sw和tur檢查方法中出現,類似於PATH_UP
3.5.2 主動檢查
路徑的初始檢查間隔(checkint)由配置文件polling_interval項配置,默認為5。
配置文件中path_checker項設置路徑檢查方法,譬如readsector0,即通過ioctl讀取設備的第0個扇區(長度根據fstat設備的屬性,測試的iscsi是4K)。
Multipathd服務啟動Checkerloop線程進行路徑檢查,遍歷vecs中所有的path,調用check_path方法進行檢查。
Check_path:
(1) getstate: 首先用path_offline通過sysfs獲取設備狀態(sysfs_get_state),然后針對up狀態的path再調用path_checker具體方法進行檢查。
(2) 如果新舊狀態不一致,作如下(3)-(6)處理:
(3) 恢復path的檢查間隔checkint為配置值(conf->checkint)
(4) 如果新狀態是fail或shark,update_multipath_strings,通過dm_message主動通知DM,並且如果舊狀態是up或ghost,減少該multipath的活躍路徑數nr_active。
處理活躍路徑為0:
如果活躍數等於0 ,判斷no_path_retry是否設置(大於0為重試次數,小於0為其它策略),如果是,設置retry_tick(=mpp->no_path_retry * conf->checkint + 1)。然后退出檢查。
(5) 新狀態是up或ghost,通過dm_message主動通知DM,如果舊狀態不是up或ghost,增加該multipath的活躍路徑數,
處理增加前活躍為0:
如果增加前活躍路徑數為0,並且no_path_retry>0,設置retry_tick=0,並且通過dm_message通知DM “queue_if_no_path”
處理failback:
根據mpp->pgfailback的設置(大於0為等待切換時間,小於0為其它策略),如果大於0,設置failback_tick=pgfailback+1;如果等於--FAILBACK_IMMEDIATE,通過need_switch_pathgroup重新刷新計算path的優先級,如果nextpg<>bestpg,通過dm_message通知DM bestpg(switchgroup)。
默認情況每條路徑的優先級都是1(prio=const),因此need_switch_pathgroup每次都會第一個Path作為最優Path。
(6) 如果新狀態是up,並且所在pathgroup的狀態是disable,通過dm_message通知DM”enable”該pathgroup
(7) 如果新舊狀態一致,並且一直是up或ghost狀態,設置路徑的checkint為原有值的兩倍,最大為配置值的4倍。
(8) 刷新path的優先級,need_switch_pathgroup判斷是否需要切換,如果需要:
如果pgfailback>0並且failback_tick<0,設置failback_tick=pgfailback+1;否則如果pgfailback等於--FAILBACK_IMMEDIATE,通過dm_message通知DM切換到bestpg。
Checkloop線程在檢查完所有path后,會對vecs中所有的multipath做下列兩件事情:
defered_failback_tick:
遍歷所有multipath,對於設置了pgfailback的,failback_tick減1,如果failback_tick等於0並且need_switch_pathgroup刷新路徑返回1,switchgroup。
retry_count_tick:
遍歷所有multipath,對於retry_tick>0,retry_tick減1,如果retry_tick等於0,通過dm_message通知DM “fail_if_no_path”
Switchgroup消息傳遞到內核,會修改內核multipath對象的current_pgpath=NULL和nextpg,failback消息傳遞到內核,會調用fail_path方法修改內核multipath對象的current_pgpath=NULL,之后的讀寫請求到multipath_target的map_io時就會選擇的新的路徑。
3.5.3 讀寫錯誤
內核在對multipath_target進行讀寫結束后會調用do_end_io方法,如果讀寫發生錯誤時,該方法會調用fail_path,通過ioctl事件機制通知用戶態
用戶態multipathd收到事件后,調用update_multipath方法修改path的state為down,並且如果活躍路徑數變為0,采用上述相同方法處理。
3.6 路徑切換
3.6.1 Failover
在3.3.1路徑檢查中,發現路徑故障,會通過dm_message消息通知內核DM,DM修改current_pgpath=NULL,在下次map_io時會選擇新的路徑。
選擇方式為首先在當前優先組(currentpg)中查找,如果找不到,再從其它優先組中查找。
3.6.2 Failback
在3.3.1路徑檢查中,如果pgfailback設置為IMMEDIATE,或者設定為等待時間failback_tick到了,用戶態會通過dm_message消息通知內核DM,DM設置nextpg,修改current_pgpath=NULL, 在下次map_io時會選擇新的路徑。
選擇方式為設定nextpg為當前優先組(currentpg),直接在當前優先組中查找。
3.6.3 負載均衡
內核multipath_target里,每個path有一個屬性repeat_count( round-robin模式下等於1000),每次map_io,repeat_count減1,等於0時,切換路徑
選擇方式為當前currentpg的path列表的頭部取出一個path,然后再插入列表的尾部
3.7 路徑全部失敗
在3.5中當選擇路徑失敗時,即全部路徑都出錯了,這時map_io根據queue_if_no_path的值,決定是將請求排隊(值為1),還是立即返回錯誤。通過測試發現如果請求在排隊時,上層應用進程的狀態是S(可中斷睡眠)。
在3.4.1中用戶態的no_path_retry配置(queue/fail)在向DM發送create multipath指令之后,會傳遞給DM。
在3.3.1路徑檢查中,如果no_path_retry設置為queue/fail,或者設定的等待時間retry_tick到了,用戶態通過dm_message發送“queue_if_no_path”或“fail_if_no_path”消息給DM,DM由此設置queue_if_no_path。
3.8 IO排隊
3.8.1 排隊觸發
用戶態Multipath對象的hw_handle配置會傳遞到內核賦值內核multipath對象的hw_handle_name。
內核mapio在處理路徑切換時,如果發現currentpg和要切換的pg是否相同,如果不相同會調用__switch_pg,__switch_pg方法除了設置currentpg外,會根據hw_handle_name的值設置queue_io和pg_init_required,如果hw_handle_name有值,都設置1。
Mapio處理完路徑切換后,會判斷currentpath!=NULL&&queue_io,或currentpath==NULL&&queue_if_no_path(3.7路徑全部失敗)滿足一個條件后就將請求排隊,將請求插入到queued_ios。
3.8.2 排隊結束
在一些可能要結束排隊的觸發事件發生后會通過queue_work調用process_queued_ios方法:
(1) 該方法首先判斷currentpath是否為空,如果是,調用__choose_pgpath選擇路徑;
(2) 判斷如果curentpath不等於空,queue_io是否等於0,或者等於空,queue_if_no_path是否等於0,這兩個條件如果滿足一個,設置must_queue=0;
(3) 然后判斷pg_init_required是否設置,並且pg_init_in_progress沒有設置,如果是調用__pg_init_all_paths初始化path;
(4) __pg_init_all_paths遍歷每個路徑,調用path的activate_path方法,設置pg_init_in_progress++,activate_path方法最終調用到Scsi_dh.c中的scsi_dh_activate方法(Scsi_device設備驅動需要實現),該方法在設備初始化完成后會調用pg_init_done;
(5) 如果must_queue==0,dispatch_queued_ios調用map_io處理排隊的請求。
可能的觸發條件有:
(1) queue_if_no_path方法在m->queue_if_no_path等於0(fail)時調用
(2) reinstate_path方法在m->nr_valid_paths原為0,情況下調用
(3) pg_init_done在path初始化完后調用
(4) map_io在處理完請求排隊,在pg_init_required被設置(目的是處理path初始化),調用process_queued_ios。
4 主要函數實現說明
4.1 Multipathd
4.1.1 Checker
Multipath采用動態加載的方式加載路徑檢查的方法;
directio主要是用aio的方法讀取一個塊的大小(最大4096)
readsector0是通過ioctl(sg_fd, SG_IO, &io_hdr)向scsi驅動器發送讀指令。
4.1.2 Main(child)
(1) load_config()
(2) init_checker()
(3) init_prio(): 默認為const,每條路徑的優先級都是1
(4) configure()
(5) 創建幾個線程(checkerloop、ueventloop、uxlsnrloop(注冊各種CLI請求(Multipathd -k)處理方法))
4.1.3 Configure
(1)path_discovery
(2)filter_path:根據blacklist過濾路徑
(3)map_discovery:dm_get_maps調用libdevicemap的接口、setup_multipath(update_multipath_strings)
(4)coalesce_paths
(5)coalesce_maps
(6)sync_maps_state: 遍歷所有multipath的所有path,根據dmstate和state的差別,調用dm_reinstate_path或dm_fail_path向DM發送對應message。
(7)遍歷每個multipath,調用start_waiter_thread,啟動一個dm事件監聽線程,update_multipath處理產生的事件,首先查找Multipath,然后setup_multipath,如果查找失敗或setup失敗,退出線程。之后判斷所有path的dmstate如果是failed,修改path的state為down,並且如果path的原有state是up(ghost),調用update_queue_mode_del_path
4.1.4 path_discovery
(1)從“/sys/block”目錄下獲取設備名(char*)
(2)根據設備名在path_vec中查找,不存在添加
(3)path_info獲取路徑相關信息(state\priority\uid\scsi設備的serialno)
discovery.c(.h)里其它方法都是由path_info調用,向各種設備獲取各種設備信息和狀態
4.1.5 dm_get_maps
(1)dm_task_create(DM_DEVICE_LIST)獲取所有DM設備名稱names
(2)遍歷names
(3)dm_type(),通過name判斷是否是multipath設備(通過獲取DM_DEVICE_TABLE)
(4)如果是multipath設備,獲取mpp->alias,mpp->size,mpp->params,mpp->status,mpp->wwid,mpp->dmi(dm_info),插入全局的mpvec
(5)至(2)完成遍歷
4.1.6 setup_multipath
(1)dm_get_info (maybe 重復了,而且可能有內存泄露!!!)
(2)dm_map_present判斷dm_info.exists
(3)set_multipath_wwid (在map_discovery的調用里,不會再執行)
(4)通過wwid,find_mpe賦值mpp->mpe
(5)update_multipath_strings,如果返回1,dm_get_name獲取new_alias,如果成功,說明發生rename,賦值當前mpp->alias,goto out(remove_map(mpp, vecs, PURGE_VEC))
(6)extract_hwe_from_path: 從Multipath中選擇PGSTATE_ACTIVE和PGSTATE_ENABLED的pgp,再選擇非PSTATE_FAILED狀態的path,使用find_hwe,通過path->vendor_id,product_id,rev查詢conf->hwtable,並賦值path->hwe,然后返回hwe
(7)通過mpp->mpe或mpp->hwe或conf,設置Multipath各種屬性(rr_weight\pgfailback\no_path_retry\pg_timeout\flush_on_last_del)
4.1.7 update_multipath_strings
(1)free_multipath_attributes,free_pgvec
(2)update_multipath_table
(3)update_multipath_status
4.1.8 update_multipath_table
(1)dm_get_map:獲取mpp->params
(2)disassemble_map:從params中解析出multipath的配置
4.1.9 disassemble_map
(1)從mpp->params解析出mpp->no_path_retry,mpp->hwhandler
(2)解析出path group的個數
(3)(0,num_pg)循環
(4)如果mpp->selector沒有賦值,先賦值
(5)alloc_pathgroup()
(6)解析出pgp下面的path個數
(7)(0,num_paths)循環
(8)解析出path的設備名,從全局pathvec查找path,沒有則alloc_path(),插入pathvec
(9)將path插入到pgp->paths
(10)如果mpp->wwid沒有賦值,用path->wwid賦值,否則如果path->wwid沒有賦值,用mpp->wwid賦值
(11)解析出mpp->minio(?)
(12)至(7),至(3)
4.1.10 update_multipath_status
(1)dm_get_status,通過dm_get_status獲取設備狀態,賦值mpp->status(char*)
(2)disassemble_status:解析mpp->status
賦值mpp->queueio
循環賦值所以pgp,賦值status(PGSTATE_DISABLED\PGSTATE_ACTIVE\PGSTATE_ENABLED\PGSTATE_UNDEF)
循環賦值pgp下面所有的path的狀態:PSTATE_FAILED、PSTATE_ACTIVE,和failcount
解析出mpp->minio
4.1.11 coalesce_paths
(1)遍歷所有的路徑Path
(2)針對mpp=NULL的Path(pp1),調用add_map_with_path方法,創建multipath(mpp),賦值Path的mpp, adopt_paths
(3)從當前迭代位置K往后遍歷Path,當Path(pp2)的wwid和當前Path的wwid相等時,如果pp2的size不等於mpp的size,或pp2的priority為undef,設置mpp的action為ACT_REJECT
(4)verify_paths: 遍歷Multipath的path,如果path->sysdev=null或者從sysfs_get_dev獲取不到設備信息,從multipath中刪除path,同時從全局的pathvec中刪除path.
(5)setup_map: 通過mpp->mpe或mpp->hwe或conf,設置Multipath各種屬性(pgfailback\pgpolicy\selector等,mpp->pgpolicyfn(mpp):根據pgpolicy從path生成pathgroup),調用assemble_map,將mulitpath相關配置編碼到mp->params.
(6)select_action: 如果mpp->action==undef,通過alias、wwid在全局的mpvec是否存在,設置action為create,rename,reload,nothing等。
(7)domap:根據action具體類別,調用相應dm接口處理
(8)通過dm_message設置no_path_retry和pg_timeout
(9)如果mpp的action不是reject,將mpp加入newmp(與mpvec同類型)
(10)轉向(1)直至遍歷全部path
(11)遍歷newmpp中所有Multipath,如果Multipath是deadmap(所有path->dev=null,not in sysfs),從newmp和全局mpvec中刪除Multipath,調用dm_flush_map通知DM刪除該mpp。
4.1.12 adopt_paths
(1)update_mpp_paths,使用multipath->pg->paths更新multipath->paths
(2)遍歷全局的pathvec, 如果Path的wwid等於multipath的wwid,將Path加入multipath->paths,同時path_info(path).
4.1.13 coalesce_maps
將vecs->mpvec中已經不存在於newmp(coalesce_paths方法生成)的mpp,調用dm_flush_map從DM內核刪除,如果刪除失敗,將mpp插入newmp,setup_multipath(),從oldmp刪除
4.1.14 find_hwe
Hwtable.c記錄了默認支持的硬件驅動器,struct hwentry default_hw[]
conf->hwtable,首先是來自上述的default_hw,其次是由load_config的init_data()從配置文件的devices項讀取.
find_hwe 根據vendor和product,從conf->hwtable中查找hwentry
4.1.15 find_mpe
conf->mptable,由load_config的init_data()從配置文件的multipaths項讀取。
find_mpe從conf->mptable中根據wwid查找mpentry
4.1.16 checkerloop
(1)check_path:get_state獲取Path狀態,根據最新狀態調用update_multipath_strings更新Multipath狀態,或主動更新內核DM的狀態。
(2)defered_failback_tick: Multipath的failback_tick每次減1,當等於0時,根據是否需要(mpp->pgfailback == -FAILBACK_MANUAL)切換調用switchgroup
(3)retry_count_tick: Multipath的retry_tick每次減1,當等於0時,調用dm_queue_if_no_path
4.1.17 get_state
(1)首先用path_offline通過sysfs獲取設備狀態(sysfs_get_state)
(2)如果是up狀態,再調用checker_check使用具體的檢查路徑方法進行check
4.1.18 check_path
(1)newstate=get_state(pp)
(2)如果狀態是PATH_WILD(sysfs相關操作失敗)或PATH_UNCHECKED(獲取或設置checker失敗),pathinfo(pp, conf->hwtable, 0),退出
(3)如果狀態是PATH_PENDING,pp->tick=1
(4)如果新狀態和舊狀態不相等,調用(5)-(8)
(5)調用update_multipath_strings()更新pp->mpp,如果返回大於0,或者新狀態是PATH_DOWN或者PATH_SHAKY,並且oldstate是PATH_UP或PATH_GHOST,調用fail_path(調用dm_fail_path通知DM,update_queue_mode_del_path修改retry_tick),pp->mpp->failback_tick = 0,退出
(6)(新狀態等於PATH_UP或PATH_GHOST),如果oldstate不等於PATH_UP或者不等於PATH_GHOST,reinstate_path(pp,1)否則reinstate_path(pp,0)(dm_reinstate_path通知DM,update_queue_mode_add_path)
(7)如果pp->mpp->pgfailback == FAILBACK_IMMEDIATE並且need_switch_pathgroup(pp>mpp, 1),調用switch_pathgroup(pp->mpp)
(8)如果新狀態是PATH_UP,調用enable_group(dm_enablegroup通知DM)
(9)如果新舊狀態相等,並且newstate是PATH_UP或PATH_GHOST,pp->checkint自增1
(10)pathinfo(DI_PRIO),refreshing path prio
(11)處理need_switch_pathgroup,根據pgfailback可能switch_pathgroup()
4.1.19 update_queue_mode_del_path
(1)mpp活躍路徑數nr_active減1
(2)如果mpp的nr_active等於0並且no_path_retry大於0,stat_queueing_timeouts自增1,設置retry_tick等於mpp->no_path_retry * conf->checkint + 1。
4.1.20 update_queue_mode_add_path
(1)增加活躍路徑數nr_active
(2)如果增加前活躍路徑數為0,並且no_path_retry>0,設置retry_tick=0,通知DM “queue_if_no_path”
4.1.21 need_switch_pathgroup
(1)如果mpp->pgfailback=-FAILBACK_MANUAL,返回0
(2)如果參數refresh設置為1,調用pathinfo刷新path的prio
(3)select_path_group計算mpp的最優pathgroup,先比較平均優先級,再比較enable路徑個數。
(4)如果bestpg不等於nextpg,返回1
(5)返回0
4.2 libdevmapper
libdevmapper提供大部分接口是通過ioctl方法向“/dev/mapper/control”設備(設備號等於/proc/misc中dev-mapper驅動的設備號)通信完成的。
支持的請求類型定義在:_cmd_data_v4
4.3 Multipath_target
multipath_target的主要方法:
multipath_ctr
multipath_dtr
multipath_map:調用map_io
multipath_status
multipath_message: 根據具體的消息類型會調用fail_path, queue_if_no_path, switch_pg_num等方法
multipath_end_io
4.3.1 DM_ioctl.c
處理來自libdevmapper對內核信息的各種請求,主要的方法有:
list_devices
dev_create
table_status (會調用到target的相應方法,即multipath_status)
target_message(最終調用target的相應方法,即multipath_message)
4.3.2 map_io
(1)if (!m->current_pgpath ||
(!m->queue_io && (m->repeat_count && -m>repeat_count == 0)))
__choose_pgpath(m, nr_bytes);
路徑失敗、路徑恢復、負載均衡(repeat_count)等原因,會觸發調用__choose_pgpath
(2)bdev = pgpath->path.dev->bdev;
clone->q = bdev_get_queue(bdev);
clone->rq_disk = bdev->bd_disk;
return DM_MAPIO_REMAPPED;
4.3.3 __choose_pgpath
(1)首先根據m->next_pg選擇,其次在current_pg中尋找,最后再遍歷m->priority_groups,調用__choose_path_in_pg()尋找path.
(2)_choose_path_in_pg():使用path_selector_type中調用的select_path的方法選擇path,然后設置m->current_pgpath,如果m->current_pg和選擇pg不相同,調用_switch_pg。
round-robin:rr_select_path:從valid_paths列表的頭部取出路徑,然后插入到尾部,返回該路徑。用path的repeat_count賦值multipath的repeat_count。
(3)_switch_pg首先設置m->currentpg,然后根據hw_handler_name是否賦值,如果是,設置pg_init_required=1,queue_io=1,否則都設置為0。
4.3.4 fail_path
(1)調用Path所在優先組pg的path_selector_type中定義的fail_path方法
round-robin:將path加入selector的invalid_paths列表中
(2)pgpath->is_active = 0;m->nr_valid_paths-;如果m>current_pgpath等於該path,設置m->current_pgpath=NULL
(3)dm_path_ueventt產生一個fail_path的事件通過netlink events機制通知用戶態
(4)schedule_work(&m->trigger_event)觸發一個event以喚起用戶態的對該Multipath事件的監聽線程
4.3.5 queue_if_no_path
根據傳遞的參數queue_if_no_path設置multipath的queue_if_no_path。
4.3.6 switch_pg_num
對用戶態的switch_pg的指令消息進行處理,修改multipath的next_pg為相應組序號(參數)的優先組,設置m->current_pgpath = NULL;m->current_pg = NULL。
schedule_work(&m->trigger_event)觸發一個event以喚起用戶態的對該Multipath事件的監聽線程
5 遺留問題
(1) 內核devmapper建立過程分為兩個步驟,創建mapped_device和table_load(創建table和target),用戶態觸發內核創建的代碼應該在coalesce_paths中,但是這個方法如何實現觸發這兩個過程的?