Linux內核報文收發-網卡部分

https://zhaozhanxu.com/archives/page/7/ 

Linux版本: 3.10.103
網卡驅動: ixgbe

報文收發簡單流程


網卡驅動默認採用的是NAPI的報文處理方式。即中斷+輪詢的方式,網卡收到一個報文之後會產生接收中斷,並且屏蔽中斷,直到收夠了netdev_max_backlog個報文(默認300)或者收完網卡上的所有報文之後,重新打開中斷。

網卡數據處理


網卡初始化

  • 內核啓動時會調用do_initcalls,從而調用註冊的初始化接口net_dev_initnet_dev_init註冊軟中斷的回調函數,分別爲接收和發送的:NET_RX_SOFTIRQ = net_rx_actionNET_TX_SOFTIRQ = net_tx_action
  • 網卡驅動加載時調用ixgbe_init_module註冊一個PCI驅動。
  • 接着調用probe對應的ixgbe_probe做一些準備工作如下:
    • 創建ixgbe_adapter數據結構,這是網卡的一個實例,其中包含了網卡的所有數據和接口(netdev)。
    • netdev註冊了網卡的所有操作:ixgbe_netdev_ops和網卡的features。
    • ixgbe_init_interrupt_scheme主要是設置網卡的NAPI對應的poll接口,這裏主要是ixgbe_poll。
  • 網卡激活時會調用netdev上的open函數ixgbe_open,完成工作如下:
    • 設置接收和發送隊列,通過DMA講PCI網卡地址和隊列建立映射。
    • 給網卡註冊硬中斷:ixgbe_open-->ixgbe_request_irq-->ixgbe_request_msix_irqs註冊中斷回調ixgbe_msix_clean_rings。(MSIX中斷時)
  • ixgbe_close函數所做的事情:
    • 關閉中斷並且釋放中斷向量號。
    • 釋放申請的相應空間。

網卡收發數據

  • 當網卡接收到數據幀時,DMA將數據搬運到對應的rx_ring,然後產生一個硬中斷,調用接口ixgbe_msix_clean_rings
  • 硬中斷回調函數ixgbe_msix_clean_rings會調用當前CPU對應的NET_RX_SOFTIRQ,即net_rx_action。其中的主要實現NAPI操作如下:
    • 關閉硬中斷。
    • 調用該CPU的poll_list中的所有poll函數,即ixgbe_poll
    • 開啓硬中斷。
  • 輪詢函數ixgbe_poll主要是處理接收發送任務:
    • 循環處理每個ring中的數據,ixgbe_clean_tx_irq,此處發送tx_work_limit個報文(128)或者沒有報文後完成,並且調用netif_wake_subqueue觸發發送軟中斷NET_TX_SOFTIRQ,即net_tx_action
    • 循環處理每個ring中的數據,ixgbe_clean_rx_irq,此處接收weight個報文(64),總共收取netdev_budget(300)個,並且調用ixgbe_rx_skb進行下一步的處理。
  • ixgbe_rx_skb最終會處理過GRO之後調用到netif_receive_skb,此處忽略rps。
  • netif_receive_skb最終調用__netif_receivve_skb_core進入協議棧處理各種協議。
    • 首先根據下面介紹的rx_handler類型調用ovs或者橋處理函數進入L2的處理。
    • 然後根據pttype_base處理相關的L3協議處理。

備註

  • ixgbe擁有64個rx_ring,每個ring默認有512個buffer,最小64,最大4096。
  • 由以上可知網卡默認可以緩存32768個報文,當網卡接收報文的速度過快,接收軟中斷無法處理的時候,系統進入扼流(throttle)狀態,所有後續的報文都被丟棄,直到rx_ring有空間了。
  • 通過DMA地址映射的方式,網卡接收數據相當於零拷貝。

TUN虛擬網卡


  • 網卡驅動加載時調用tun_init註冊一個MISC驅動,生成/dev/net/tun的設備文件。
  • 接着調用open對應的tun_chr_open做主要創建socket。
  • 隨後調用ioctl對應的tun_chr_ioctl,完成一些需要的配置和信息獲取,包括校驗、連接狀態、offload、mac等。
  • 通過調用write發送報文,主要是調用tun_chr_aio_write生成skb格式報文,最終通過調用netif_rx進入協議棧,通過三層轉發到對應的端口發送。
  • 通過調用read接收報文,主要是調用tun_chr_aio_read,從內核對應的sock隊列上拿到報文,然後將數據上傳到用戶層。

注:
因爲網卡接收報文後最終經過四層的時候,會將報文根據協議幾元組找到對應的sock結構,放入隊列。上層調用read或者recv的時候都是從socket對應的sock結構隊列上取數據。

 

tun虛擬網卡流程圖

VETH虛擬網卡


  • 網卡驅動加載時調用veth_init註冊一個netlink驅動。
  • 接着調用newlink對應的veth_newlink,創建兩個網絡設備,兩者互爲對方的peer。
  • 發送報文時會調用ndo_start_xmit對應的veth_xmit,實際操作爲將skb的dev設置爲自己的peer,調用到netif_rx接收到協議棧。

 

veth虛擬網卡示意圖

QEMU虛擬機網絡通信


  • 主機vhost驅動加載時調用vhost_net_init註冊一個MISC驅動,生成/dev/vhost-net的設備文件。
  • 主機qemu-kvm啓動時調用open對應的vhost_net_open做主要創建隊列和收發函數的掛載,接着調用ioctl啓動內核線程vhost,做收發包的處理。
  • 主機qemu通過ioctl配置kvm模塊,主要設置通信方式,因爲主機vhost和virtio只進行報文的傳輸,kvm進行提醒。
  • 虛擬機virtio模塊註冊,生成虛擬機的網絡設備,配置中斷和NAPI。
  • 虛擬機發包流程如下:
    • 直接從應用層走協議棧最後調用發送接口ndo_start_xmit對應的start_xmit,將報文放入發送隊列,vp_notify通知kvm。
    • kvm通過vmx_handle_exit一系列調用到wake_up_process喚醒vhost線程。
    • vhost模塊的線程激活並且拿到報文,在通過之前綁定的發送接口handle_tx_kick進行發送,調用虛擬網卡的tun_sendmsg最終到netif_rx接口進入主機內核協議棧。
  • 虛擬機收包流程如下:
    • tap設備的ndo_start_xmit對應的tun_net_xmit最終調用到wake_up_process激活vhost線程,調用handle_rx_kick,將報文放入接收隊列。
    • 通過一系列的調用到kvm模塊的接口kvm_vcpu_kick,向qemu虛擬機注入中斷。
    • 虛擬機virtio模塊中斷調用接口vp_interrupt,調用virtnet_poll,再調用到netif_receive_skb進入虛擬機的協議棧。

 

qemu虛擬機通信流程圖

Offload技術


發送數據

  • TSO(TCP Segmentation Offload)使得網絡協議棧能夠將大塊buffer推送至網卡,然後網卡執行分片工作,這樣減輕了CPU 的負荷,但TSO需要硬件來實現分片功能,也叫LSO。
  • UFO(UDP Fragmentation Offload)類似。
  • GSO(Generic Segmentation Offload),它比TSO更通用,基本思想就是儘可能的推遲數據分片直至發送到網卡驅動之前,此時會檢查網卡是否支持分片功能(如TSO、UFO),如果支持直接發送到網卡,如果不支持就進行回調分片後再發往網卡。這樣大數據包只需走一次協議棧,而不是被分割成幾個數據包分別走,這就提高了效率。

接收數據

  • LRO(Large Receive Offload)通過將接收到的多個TCP數據聚合成一個大的數據包,然後傳遞給網絡協議棧處理,以減少上層協議棧處理開銷,提高系統接收TCP數據包的能力。
  • GRO(Generic Receive Offloading),LRO使用發送方和目的地IP地址,IP封包ID,L4協議三者來區分段,對於從同一個SNAT域的兩個機器發向同一目的IP的兩個封包可能會存在相同的IP封包ID(因爲不是全局分配的ID),這樣會有所謂的卷繞的bug。GRO採用發送方和目的地IP地址,源/目的端口,L4協議三者來區分作爲改進。所以對於後續的驅動都應該使用GRO的接口,而不是LRO。另外,GRO也支持多協議。

OpenvSwitch ovs-dpdk安裝

Linux內核報文收發-2層報文處理

注意:所有文章非特別說明皆爲原創。爲保證信息與源同步,轉載時請務必註明文章出處!謝謝合作 :-)

原始鏈接:http://zhaozhanxu.com/2016/07/12/Linux/2016-07-12-Linux-Kernel-Pkts_Processing1/

許可協議: "署名-非商用-相同方式共享 4.0" 轉載請保留原文鏈接及作者。

 

另一些文章:

https://blog.csdn.net/meituantech/article/details/80062289  Redis 高負載下的中斷優化

軟中斷部分有幾個地方會有類似if (static_key_false(&rps_needed))這樣的判斷,會進入前文所述有丟包風險的enqueue_to_backlog函數。 這裏的邏輯爲判斷是否啓用了RPS機制,RPS是早期單隊列網卡上將軟中斷負載均衡到多個CPU Core的技術,它對數據流進行hash並分配到對應的CPU Core上,發揮多核的性能。不過現在基本都是多隊列網卡,不會開啓這個機制,因此走不到這裏,static_key_false是針對默認爲false的static key 的優化判斷方式。這段調用的最後,deliver_skb會將接收的數據傳入一個IP層的數據結構中,至此完成二層的全部處理.

關於ixgbe的rx ring,tx ring,msi,legacy對比 https://www.slideshare.net/suselab/ixgbe-internals:

ixgbe 網卡初始化及收發數據概覽 https://www.jianshu.com/p/9a78390df5b9

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章