OpenVSwitch實現淺談(三)
Megaflow的規模越小,對應的hash table數量越少,相應的kernel datapath的轉發效能更好。為了減少megaflow的規模,OpenVSwitch只有在有包上送到ovs-vswitchd之後,才會生成megaflow cache,也就是說,incoming packets驅動了megaflow cache的生成。這與早期的Microflow cache的生成方式一樣。不同於早期的microflow cache的是,megaflow cache是一種模糊匹配,並且應該儘量模糊,因為這樣可以匹配更多不同的網路連線,減少上送ovs-vswitchd的次數。但是又不能太模糊,因為這樣會導致匹配出錯。所以,怎麼確定一條megaflow cache的匹配內容?
例如前面介紹的OpenFlow規則:
priority=200,ip,nw_dst=10.0.0.0/16 actions=output:1
匹配了Ethernet Type(IP)和IP header中目的IP地址的前16bit。對於下面的網路連線對應的網路資料包:
11.0.0.2:5742 -> 10.0.0.10:3306
上送ovs-vswitchd生成的megaflow cache如下,Megaflow會且僅會匹配在經過OpenFlow pipeline時,用到過的field(可以精確到field的某些bit,例如這裡的目的IP前16bit)。
ip,nw_dst=10.0.0.0/16 actions=output:1
這樣,在其他類似網路連線到來時
11.0.0.2:5743 -> 10.0.0.10:3307
11.0.0.2:5744 -> 10.0.0.10:3308
11.0.0.3:5742 -> 10.0.0.10:3309
都不需要上送ovs-vswitchd,直接匹配這條megaflow cache就可以完成轉發。在這個簡單的例子裡面,似乎沒有問題。
如果增加一條OpenFlow規則:
priority=200,ip,nw_dst=10.0.0.0/16 actions=output:1
priority=100,ip,nw_dst=20.0.0.0/16,tcp_dst=22 actions=drop
相應的,在查詢OpenFlow規則時,因為現在有兩種Match,會生成兩個hash table,因為要確保更高優先順序的OpenFlow規則不被遺漏,所以兩個hash table都會被查詢,也就是說有的OpenFlow規則的match部分都會被用到。對於同樣的網路連線:
11.0.0.2:5742 -> 10.0.0.10:3306
上送ovs-vswitchd生成的megaflow cache如下,這裡,Megaflow還是會且僅會匹配在經過OpenFlow pipeline時,用到過的field。
ip,nw_dst=10.0.0.0/16,tcp_dst=3306 actions=output:1
儘管tcp_dst並不重要,但是因為查詢OpenFlow規則時,它最終影響到了查詢結果,所以在Megaflow cache中也會出現。當同樣的其他網路連線到來時:
11.0.0.2:5743 -> 10.0.0.10:3307
11.0.0.2:5744 -> 10.0.0.10:3308
11.0.0.3:5742 -> 10.0.0.10:3309
因為匹配不了megaflow cache,都需要上送ovs-vswitchd。這樣,僅僅因為一條無關的OpenFlow規則,使得megaflow的優勢不復存在了,這明顯不合理。因此OpenVSwitch針對megaflow cache的存在,優化了OpenFlow pipeline的查詢方式。
Tuple Priority Sorting(優先順序排序)
首先是針對OpenFlow的優先順序做了優化。按照之前的介紹,如果查詢一個OpenFlow Table,總是要查詢這個OpenFlow Table對應的所有hash table。即使找到了一個匹配,因為不確定還有沒有更高優的匹配,還需要繼續查詢。針對這點,OpenVSwitch對於一個OpenFlow Table生成的所有hash table,增加了一個屬性:pri_max。它代表當前hash table中所有OpenFlow規則的最高優先順序。在查詢一個OpenFlow Table的所有hash table時,OpenVSwitch會根據pri_max從高到低依次遍歷。這樣,當匹配到某一條OpenFlow規則時,對應的優先順序為priority_F,如果下一個hash table的pri_max < priority_F。意味著,接下來的所有hash table中,都不可能找到更高優先順序的OpenFlow規則,也就沒有必要查詢接下來的hash table。
回到上面的例子,如果當前OpenFlow Table有兩條規則:
priority=200,ip,nw_dst=10.0.0.0/16 actions=output:1
priority=100,ip,nw_dst=20.0.0.0/16,tcp_dst=22 actions=drop
當下面的網路包上送到ovs-vswitchd時,
11.0.0.2:5742 -> 10.0.0.10:3306
因為在第一個Hash table(pri_max=200)就可以完成匹配,並且匹配的OpenFlow規則優先順序為200,這樣沒有必要查詢第二個Hash table(pri_max=100)。所以在查詢OpenFlow規則時,tcp_dst並沒有被用到,生成的megaflow cache如下:
ip,nw_dst=10.0.0.0/16 actions=output:1
這樣,前面提出的問題被解決了,Megaflow cache的匹配又足夠模糊了。為了發揮Tuple Priority Sorting的最大作用:在下發OpenFlow規則時,應當儘量將相同Match的OpenFlow規則配置相同的優先順序,並且一個OpenFlow Table中,優先順序應當儘量少(OpenFlow定義的優先順序是16bit整數)。
Staged Lookup(分段查詢)
如果將之前的OpenFlow規則稍作修改:
priority=200,ip,nw_dst=10.0.0.0/16 actions=output:1
priority=300,ip,nw_dst=20.0.0.0/16,tcp_dst=22 actions=drop
這個時候,Tuple Priority Sorting的優勢就不存在了,因為帶有tcp_dst的OpenFlow規則優先順序更高,總是會被先查詢到。當下面的網路包上送到ovs-vswitchd時,
11.0.0.2:5742 -> 10.0.0.10:3306
相應的生成的megaflow規則中總是要匹配tcp_dst。所以在下發OpenFlow規則時,似乎應當將帶有tcp_dst的OpenFlow規則設定成更低優先順序,但是這又沒有道理。
其實不僅是傳輸層埠號,為了發揮Tuple Priority Sorting 的優勢,匹配了容易變化的field的OpenFlow規則應該低優先順序,而匹配了不容易變化的field的OpenFlow規則應該高優先順序。這樣可以儘量生成只匹配不容易變化的field的megaflow cache,這樣的megaflow cache滿足儘量的模糊,從而在kernel datapath匹配更多的網路連線。但是這增加了OpenFlow邏輯實現的難度,因為相當於在邏輯實現層面需要考慮底層實現。
另一個辦法就是對於具體的OpenFlow規則,先匹配其不容易變化的部分,如果這部分不匹配,就沒有必要再去匹配1111111111111111111111111111111111111111容易變化的部分。但是TSS基於hash table的實現使得區分field變得困難,因為所有相關的field都被hash成一個雜湊值,沒有辦法從這個雜湊值中找出一些field的子集。因此,OpenVSwitch在實現OpenFlow的TSS時,按照field是否容易變化,將原來的一個Hash table分成了四個hash table。第一個hash table只包括metadata,例如ingress port,第二個Hash table包括metadata 和 L2 field,第三個Hash table 包括metadata,L2 field和L3 field,第四個包括metadata,L2 field,L3 field和L4 field,也就是所有的field。最後一個hash table就是之前介紹的Hash table。現在在進行TSS時,原來查詢Hash table的部分,會變成順序的查詢這4個Hash table,如果哪個不匹配,就立刻返回,這樣可以避免查詢不必要的field。這麼劃分的依據是,在TCP/IP中,外層報文的頭部總是比內層報文的頭部變化更多。這就是Staged lookup。
所以對應於上面的OpenFlow規則,如果下面的網路包上送到ovs-vswitchd,
11.0.0.2:5742 -> 10.0.0.10:3306
對於priority=300的OpenFlow規則對應的Hash table,因為不匹配metadata,所以沒有第一個Hash table。在匹配第三個Hash table時,因為目的IP地址不匹配,可以立即終止,此時只用到了Ethernet Type(IP)和IP header中的目的IP地址的前16bit,tcp_dst並未用到,所以生成的megaflow cache自然也不會匹配tcp_dst。Staged Lookup本質是優先匹配更不容易變化的field,可以看做是Tuple Priority Sorting的升級版。
雖然有了Staged Lookup,但是之前查詢一個Hash table變成了要查詢4個hash table,直觀感受是查詢效能要下降。但是實際效能差不多,因為大部分的不匹配在查詢前兩個Hash table就能返回。而前兩個hash table的所需要的hash值,計算起來比計算完整的hash值要更節約時間。並且4個Hash table,後面的包含了前面的所有field,而雜湊值的計算可以是增量計算,就算查詢到了第4個Hash table,計算雜湊值的消耗與拆分前是一樣的。
Staged Lookup要發揮作用,在下發OpenFlow規則時,如果匹配包含了tcp_dst這類易變化的field,應當儘量包含一些metadata,L2 field,或者L3 field。
Prefix Tracking
如果現在OpenFlow規則如下:
priority=200,ip,nw_dst=10.0.0.0/16 actions=output:1
priority=300,ip,nw_dst=20.0.0.2/32,tcp_dst=22 actions=drop
那麼當下面的網路包上送到ovs-vswitchd時:
11.0.0.2:5742 -> 10.0.0.10:3306
雖然有Tuple Priority Sorting和Staged Lookup,但是因為priority=300的OpenFlow規則匹配的是32位的目的IP地址,在遍歷其對應的4個Hash table時,在第3個Hash table不匹配返回,再去查詢別的OpenFlow規則對應的Hash table,最終生成的megaflow cache如下所示:
ip,nw_dst=10.0.0.10/32 actions=output:1
因為在pri_max=300的Hash Table中,最終用到了目的IP地址的整個32bit。當其他的網路連線到來時:
11.0.0.2:5743 -> 10.0.0.11:3307
11.0.0.2:5744 -> 10.0.0.12:3308
11.0.0.3:5742 -> 10.0.0.13:3309
因為目的IP地址不匹配,都需要上送到ovs-vswitchd重新生成megaflow cache,所以這裡也有一些優化空間。OpenVSwitch用了一個Trie 樹來管理一個OpenFlow Table中的所有IP地址匹配,實際實現的時候是按照bit生成樹。這裡為了描述簡單,用byte來生成樹。之前的OpenFlow對應的Trie樹如下所示:
在執行TSS之前,OpenVSwitch會遍歷Trie 樹來執行LPM(Longest Prefix Match)。通過這個遍歷,可以確定(1)生成megaflow cache所需要的IP prefix長度;(2)避免一些不必要的Hash table查詢。
回到剛剛那個網路包:
11.0.0.2:5742 -> 10.0.0.10:3306
通過遍歷Trie樹可以知道,為了匹配目的IP地址(10.0.0.10),可以遍歷到10->0節點,因此只需要匹配前16bit的IP地址就可以在當前OpenFlow Table找到匹配。因此對於這個網路包,生成的megaflow,只需要匹配目的IP地址的前16bit。
另一方面,對於下面的網路包:
11.0.0.2:5742 -> 30.0.0.10:3306
通過遍歷Trie樹可以知道必然不能匹配目的IP地址,所以也沒有必要執行TSS來查詢相應的Hash table。
Prefix Tracking是非常有必要的,因為通過OpenFlow實現路由表時,為了實現LPM,通常帶有更長的prefix的OpenFlow規則會有更高的優先順序,而配合前面的Tuple Priority Sorting,會導致一個OpenFlow Table生成的megaflow cache總是匹配最長的prefix(例如這一小節的例子)。通過Prefix Tracking,可以使得生成的megaflow只匹配足夠的IP地址bit數。
不僅對於IP地址,對於傳輸層埠號,OpenVSwitch也實現了類似的Prefix Tracking,這樣當OpenFlow Table中如果出現了只匹配傳輸層埠號的OpenFlow規則時,生成的megaflow也不用精確匹配傳輸層埠的16bit,只需要匹配相應的bit位就可以。這樣的最終目的還是為了讓megaflow足夠模糊。
OpenVSwitch還有一些其他的優化手段,但是整體而言,都是為了讓生成的megaflow cache儘量模糊,以減少上送ovs-vswitchd的次數,以提升OpenVSwitch的轉發效能。