總體概覽如下:
假設upcall handler執行緒有兩個,vport有四個,那麼每個vport下都將持有兩個NetLink連線的資訊,這兩個NetLink連線將被用來上送upcall訊息。
每個NetLink連線被對應的upcall handler執行緒接管,多個vport在同一執行緒中的NetLink連線被epoll管理。
1:每個vport下都掛多個NetLink連線,數量等同於upcall處理執行緒的數量
2:執行緒中routine函式為udpif_upcall_handler,偽碼如下:
routine {
while(執行緒不設自殺標記) {
if(從epoll中收到了一些upcall訊息,則進一步處理upcall) {
continue;
} else {
註冊poll:當epoll_fd有騷動時解除當前執行緒的block
}
block當前執行緒
}
}
3:每個NetLink連線被稱為一個upcall channel
1:upcall處理執行緒的建立
upcall處理執行緒隨著vswitchd程式的啟動而被建立,概覽如下:
最終所有執行緒資訊會被記錄在 ofproto_dpif->backer->udpif下
2:vport的建立與channels的建立
在基於本機核心的datapath實現的openflow交換機場景下,使用者態的datapath介面將採用dpif_netlink_***系列函式。vport的建立在使用者態最終將呼叫到dpif_netlink_port_add,這是dpif_netlink_port_add__的包裹函式。
先說一點我對NetLink連線的理解
1:建立socket
2:可選的bind步驟,在這一步,應當在本端地址結構中賦值nl_pid為當前程式ID,並bind之。
3:建立連線connect,這一步對端地址結構的nl_pid值如果為非0值 ,那麼連線的就是一個使用者態程式。如果nl_pid值為0,則連線的是核心。
如果沒有第2步,那麼在connect之後,核心會為這個NetLink連線的本端bind地址結構自動指定一個nl_pid值,以標識該連線。此時通過getsockname取到的本端地址結構中的nl_pid欄位將不是發起連線一方的程式ID。
3:核心cache miss
核心cache miss後,會傳送upcall訊息,
netdev_frame_hook(pskb)
|
\-> netdev_port_receive(skb, NULL/skb_tunnel_info(skb))
|
\-> ovs_vport_receive(ovs_netdev_get_vport(skb->dev), skb, tun_info = NULL/skb_tunnel_info(skb))
|
\-> error = ovs_flow_key_extract(tun_info, skb, &key)
|
\-> ovs_dp_process_packet(skb, &key)
|
\-> flow = ovs_flow_tbl_lookup_stats(&dp->table, key, skb_get_hash(skb), &n_mask_hit)
if(unlikely(!flow)) {
...
upcall.cmd = OVS_PACKET_CMD_MISS;
upcall.portid = ovs_vport_find_upcall_portid(p, skb);
|
\-> 在vport下的upcall_portids裡欽定一個port號,作為NetLink的目的地
...
error = ovs_dp_upcall(dp, skb, key, &upcall, 0);
}
即是從upcall的vport下掛的socksp中選擇一個nl_pid,以這個nl_pid做為upcall訊息傳送的目的地址
由上面的圖能看出來,在vport下選擇不同的socksp中的nl_pid,會導致訊息被髮往不同的epoll
4:dpif_handler[]->epoll_fd與使用者態執行緒間的關聯
那麼在udpif_upcall_handler() -> recv_upcalls() -> dpif_recv() -> dpif_netlink_recv() -> dpif_netlink_recv__()中有以下程式碼:
這裡的引數handler_id來自於執行緒routine函式的入參handler例項下的handler_id。
即對於x號執行緒,它會取dpif_handler[]陣列中的x號元素下的epoll_fd去處理。