linux 3.10 gro的理解和改进
gro,將同一個flow的一定時間范圍之內(nèi)的skb進(jìn)行合并,減少協(xié)議棧的消耗,用于收包性能提升。gro網(wǎng)上的資料很多,但是都很少談到gro的改進(jìn),剛好身邊有個同事也想改這塊的內(nèi)容,
所以將最近看的gro內(nèi)容總結(jié)一下,作為記錄。
gro的層次,很少有資料提到,可能是大牛們覺得太簡單,但我還是記錄一下,畢竟我基礎(chǔ)不好。
先看關(guān)鍵的數(shù)據(jù)結(jié)構(gòu),然后分析流程:
為了在skb中記錄相關(guān)的gro信息,使用了skb的cb字段。
crash> napi_gro_cb
struct napi_gro_cb {
void *frag0;
unsigned int frag0_len;
int data_offset;
u16 flush;
u16 flush_id;
u16 count;
u16 gro_remcsum_start;
unsigned long age;
u16 proto;
u8 encap_mark : 1;
u8 csum_valid : 1;
u8 csum_cnt : 3;
u8 is_ipv6 : 1;
u8 free : 2;
u8 same_flow : 1;
u8 recursion_counter : 4;
u8 is_atomic : 1;
__wsum csum;
struct sk_buff *last;
}
SIZE: 48
48字節(jié)的cb字段,被用完了。
所有的packet 級別的gro的類型,放在一個公共鏈表頭 offload_base 變量中管理,我測試的系統(tǒng)中的packet級別的gro類型有:
crash> list packet_offload.list -H offload_base -s packet_offload
ffffffff81b41bc0
struct packet_offload {
type = 8,
priority = 0,
callbacks = {
gso_segment = 0xffffffff816155b0 <inet_gso_segment>,
gro_receive = 0xffffffff816159a0 <inet_gro_receive>,
gro_complete = 0xffffffff816148c0 <inet_gro_complete>
},
list = {
next = 0xffffffff81b43b40 <ipv6_packet_offload+32>,
prev = 0xffffffff81b3f0e0 <offload_base>
}
}
ffffffff81b43b20
struct packet_offload {
type = 56710,
priority = 0,
callbacks = {
gso_segment = 0xffffffff8168c670 <ipv6_gso_segment>,
gro_receive = 0xffffffff8168c300 <ipv6_gro_receive>,
gro_complete = 0xffffffff8168c120 <ipv6_gro_complete>
},
list = {
next = 0xffffffff81b3f7c0 <eth_packet_offload+32>,
prev = 0xffffffff81b41be0 <ip_packet_offload+32>
}
}
ffffffff81b3f7a0
struct packet_offload {
type = 22629,
priority = 10,
callbacks = {
gso_segment = 0x0,
gro_receive = 0xffffffff815bbd60 <eth_gro_receive>,
gro_complete = 0xffffffff815bbbe0 <eth_gro_complete>
},
list = {
next = 0xffffffff81b3f0e0 <offload_base>,
prev = 0xffffffff81b43b40 <ipv6_packet_offload+32>
}
}
所有的inet層的gro回調(diào),都存儲在inet_offloads 數(shù)組中,根據(jù)當(dāng)前加載的模塊,本機(jī)器對應(yīng)支持的gro就有:
p inet_offloads
inet_offloads = $48 =
{0x0, 0x0, 0x0, 0x0, 0xffffffff8176fd80 <ipip_offload>, 0x0, 0xffffffff8176f220 <tcpv4_offload>, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffff8176f560 <udpv4_offload>, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffff81777680 <sit_offload>, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffff81770be0 <gre_offload>, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
。。。。
0x0, 0x0, 0x0}
gro的調(diào)用查找過程如下:
從dev層,根據(jù)到來的skb,可以根據(jù)skb->protocol 作為type的類型,比如type是.type = cpu_to_be16(ETH_P_IP),然后才會進(jìn)入ip_packet_offload 這個層次,
在offload_base這個鏈表頭找到對應(yīng)的type,然后獲取對應(yīng)的callback.gro_receive 函數(shù)。
找到了對應(yīng)的inet_gro_receive,就進(jìn)入了packet層,那么根據(jù)iph->protocol,就在net_offload 數(shù)組中,找到對應(yīng)協(xié)議類型的gro結(jié)構(gòu),比如找到的是tcpv4_offload。
那么針對tcp的gro,其i40e驅(qū)動的調(diào)用順序就是:
i40e_napi_poll--->|i40e_clean_tx_irq
--->|i40e_clean_rx_irq-->napi_gro_receive-->dev_gro_receive-->inet_gro_receive-->tcp4_gro_receive
對應(yīng)的堆棧如下:
[root@localhost caq]# stap -d i40e netif_rx.stp System Call Monitoring Started (10 seconds)... WARNING: DWARF expression stack underflow in CFI 0xffffffff816041a0 : tcp4_gro_receive+0x0/0x1b0 [kernel] 0xffffffff81615be9 : inet_gro_receive+0x249/0x290 [kernel] 0xffffffff815951b0 : dev_gro_receive+0x2b0/0x3e0 [kernel] 0xffffffff815955d8 : napi_gro_receive+0x38/0x130 [kernel]-------------gro處理開始 0xffffffffc01f4bde : i40e_clean_rx_irq+0x3fe/0x990 [i40e] 0xffffffffc01f5440 : i40e_napi_poll+0x2d0/0x710 [i40e] 0xffffffff81594cf3 : net_rx_action+0x173/0x380 [kernel] 0xffffffff8109404d : __do_softirq+0xfd/0x290 [kernel] 0xffffffff816c8afc : call_softirq+0x1c/0x30 [kernel] 0xffffffff8102d435 : do_softirq+0x65/0xa0 [kernel] 0xffffffff81094495 : irq_exit+0x175/0x180 [kernel] 0xffffffff816c9da6 : __irqentry_text_start+0x56/0xf0 [kernel] 0xffffffff816bc362 : ret_from_intr+0x0/0x15 [kernel]
理清楚了大的流程,我們再來看目前的gro小的流程。在收到一個skb的時候,我們把它和napi_struct中的gro_list的skb進(jìn)行比較,看能否合并,當(dāng)然合并的前提是同一個flow的,
除此之外,除了滿足同一個flow,還有很多要求。
那這個gro_list最大多長呢?
/* Instead of increasing this, you should create a hash table. */ #define MAX_GRO_SKBS 8
才8個,這8個skb跟新進(jìn)來的skb是flow相同的概率其實真不高,比如在tcp4_gro_receive中,我想跟蹤它接著調(diào)用的 skb_gro_receive,無奈由于合并的幾率太低而無法跟到,
畢竟還有一個在gro_list中停留的時間限制,為一個jiffies。后來修改了jiffies并且修改了合并的條件才能抓到。
當(dāng)然,根據(jù)作者的注釋,與其將這8改大,不如改成一個hash表,不同的skb先哈希到一個flow鏈,然后在鏈中比較看能否合并,這樣對于gro流程需要改動為:
1.創(chuàng)建flow的hash表,讓skb中看到flow,然后在flow的沖突鏈中找對應(yīng)的gro_list,然后看能否合并。
2.percpu模式,不適用napi_struct來管理gro_list.
3.修改合并條件,比如對于tcp的ack來說,目前不帶數(shù)據(jù)的ack無法合并,因為才54個字節(jié),而以太網(wǎng)發(fā)出的時候會填充,導(dǎo)致不滿足如下條件:
flush = (u16)((ntohl(*(__be32 *)iph) ^ skb_gro_len(skb)) | (id & ~IP_DF));
但對于流媒體服務(wù)器來說,純ack占入向的比例很高,需要將條件改動,由于ack還涉及到快發(fā)流程的進(jìn)入和退出,所以ack合并還是有一些工作要做的。
4.修改間隔,目前限制死了是一個jiffies,比如服務(wù)器8M左右的發(fā)送碼率,收到的ack間隔可以釋放放大,不然合并幾率也比較低,當(dāng)然這個是以tcp的send_buf中的數(shù)據(jù)占用更多內(nèi)存為前提的。
所以需要一個導(dǎo)出到/proc文件系統(tǒng)的間隔字段來控制。
5.對于低速發(fā)送碼率的服務(wù)器來說,可以關(guān)閉gro,對于lvs服務(wù)器來說,應(yīng)該關(guān)閉gro。
水平有限,如果有錯誤,請幫忙提醒我。如果您覺得本文對您有幫助,可以點擊下面的 推薦 支持一下我。版權(quán)所有,需要轉(zhuǎn)發(fā)請帶上本文源地址,博客一直在更新,歡迎 關(guān)注 。
總結(jié)
以上是生活随笔為你收集整理的linux 3.10 gro的理解和改进的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 发行费用和交易费用的区别 发行费用有哪些
- 下一篇: 买什么保险好 买什么保险比较好