linux 3.10 gro的理解和改进
gro,將同一個flow的一定時間范圍之內的skb進行合并,減少協議棧的消耗,用于收包性能提升。gro網上的資料很多,但是都很少談到gro的改進,剛好身邊有個同事也想改這塊的內容,
所以將最近看的gro內容總結一下,作為記錄。
gro的層次,很少有資料提到,可能是大牛們覺得太簡單,但我還是記錄一下,畢竟我基礎不好。
先看關鍵的數據結構,然后分析流程:
為了在skb中記錄相關的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字節的cb字段,被用完了。
所有的packet 級別的gro的類型,放在一個公共鏈表頭 offload_base 變量中管理,我測試的系統中的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回調,都存儲在inet_offloads 數組中,根據當前加載的模塊,本機器對應支持的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的調用查找過程如下:
從dev層,根據到來的skb,可以根據skb->protocol 作為type的類型,比如type是.type = cpu_to_be16(ETH_P_IP),然后才會進入ip_packet_offload 這個層次,
在offload_base這個鏈表頭找到對應的type,然后獲取對應的callback.gro_receive 函數。
找到了對應的inet_gro_receive,就進入了packet層,那么根據iph->protocol,就在net_offload 數組中,找到對應協議類型的gro結構,比如找到的是tcpv4_offload。
那么針對tcp的gro,其i40e驅動的調用順序就是:
i40e_napi_poll--->|i40e_clean_tx_irq
--->|i40e_clean_rx_irq-->napi_gro_receive-->dev_gro_receive-->inet_gro_receive-->tcp4_gro_receive
對應的堆棧如下:
[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進行比較,看能否合并,當然合并的前提是同一個flow的,
除此之外,除了滿足同一個flow,還有很多要求。
那這個gro_list最大多長呢?
/* Instead of increasing this, you should create a hash table. */ #define MAX_GRO_SKBS 8
才8個,這8個skb跟新進來的skb是flow相同的概率其實真不高,比如在tcp4_gro_receive中,我想跟蹤它接著調用的 skb_gro_receive,無奈由于合并的幾率太低而無法跟到,
畢竟還有一個在gro_list中停留的時間限制,為一個jiffies。后來修改了jiffies并且修改了合并的條件才能抓到。
當然,根據作者的注釋,與其將這8改大,不如改成一個hash表,不同的skb先哈希到一個flow鏈,然后在鏈中比較看能否合并,這樣對于gro流程需要改動為:
1.創建flow的hash表,讓skb中看到flow,然后在flow的沖突鏈中找對應的gro_list,然后看能否合并。
2.percpu模式,不適用napi_struct來管理gro_list.
3.修改合并條件,比如對于tcp的ack來說,目前不帶數據的ack無法合并,因為才54個字節,而以太網發出的時候會填充,導致不滿足如下條件:
flush = (u16)((ntohl(*(__be32 *)iph) ^ skb_gro_len(skb)) | (id & ~IP_DF));
但對于流媒體服務器來說,純ack占入向的比例很高,需要將條件改動,由于ack還涉及到快發流程的進入和退出,所以ack合并還是有一些工作要做的。
4.修改間隔,目前限制死了是一個jiffies,比如服務器8M左右的發送碼率,收到的ack間隔可以釋放放大,不然合并幾率也比較低,當然這個是以tcp的send_buf中的數據占用更多內存為前提的。
所以需要一個導出到/proc文件系統的間隔字段來控制。
5.對于低速發送碼率的服務器來說,可以關閉gro,對于lvs服務器來說,應該關閉gro。
水平有限,如果有錯誤,請幫忙提醒我。如果您覺得本文對您有幫助,可以點擊下面的 推薦 支持一下我。版權所有,需要轉發請帶上本文源地址,博客一直在更新,歡迎 關注 。
總結
以上是生活随笔為你收集整理的linux 3.10 gro的理解和改进的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 发行费用和交易费用的区别 发行费用有哪些
- 下一篇: 买什么保险好 买什么保险比较好