UDP GSO原理及應用

2019年10月31日

一、需求

  1. 內核如何支持udp gso;
  2. 測試(包括性能);
  3. 用戶如何使用udp gso;

 

二、背景

       針對上述問題,本小節展開一定的論述,基礎知識就不再贅述了。

       Linux支持udp下的generic segmentation offload特性有兩種實現方式,一種是在protocal stack做,一種是在NIC driver做。協議棧中做的udp gso我們亦可稱爲uso,基於協議棧支持的前提下,若硬件支持我們可稱爲udp gso offload或uso offload,顧名思義,後者基於前者再做offload。[注:udp gso容易和幾年前開發的ufo混淆,ufo特性是做在NIC網卡driver上,而ufo問題頻發導致很多NIC不再做支持。]

       2018年Willem de Bruijn公佈了draft論文Optimizing UDP for Content Delivery with GSO, Pacing and Zerocopy ;2018年4月26日,社區合併了第一份uso的patchset(總共10個patch);  2018年11月14日舉辦了Linux Plumbers Conference論文的presentation。2018年12月3日youtube公開了對應的演講視頻

       2018年6月社區合併了第一個網卡driver(mlx5-core)的特性支持;2019年10月intel提供了自己的三塊網卡(igb/ixgbe/i40e)的支持,但是暫未合併到主線。netdevconf的session的公開資料

       這裏列出一些uso的基本原理:

       1. 下圖的表示了ufo和uso切割packet時候的處理方式不同,uso是每一塊都需要帶上udp header頭部(ipv4,8字節),而ufo不需要。

        2. 下圖展示了三種情況,從左到右,1)正常upd發包流程,在proto協議棧層ip層分片,一路小包到NIC再到接收端大量小包;2)開啓gso後,proto協議棧層不再做分片,一路大包到NIC網卡,在網卡發送前做分片;3)開啓gso+gro(gro是接收端),在發送端同(2)一樣,只是接收端在接收到大量小包後合包成大包;理論上加上測試數據來看,從左到右是性能上依次的升級。

        3. 下圖展示了三種NIC前處理包情況,1)開啓udp gso,如果packet正好是gso切割包大小的整數倍,則正常處理每一塊並做好checksum,2)開啓udp gso,如果packet不是gso的整數倍,則需要對最後一塊做額外處理,3)開啓udp gso,且支持gso partial(請注意和driver的gso partial不是一個),則可以將前面的若干包合併只是用一個header,最後一塊作爲non-gso塊處理即可。做更細的理解,此gso partial的代碼["mss *= skb_shinfo(segs)->gso_segs;"]中將MSS的值擴大成了gso_segs倍,也就是如前所說若干的小包合併爲一個。

        注:driver層如mlx5-core的gso partial支持,只是爲了在網卡特性開啓(ethtool -K [網卡] xxx on/off)時能夠區分開正常gso和udp gso offload功能而已,可以讓用戶能夠支持gso的情況下開啓或關閉udp gso offload,原始設計時候二者是混爲一談,用同一開關來控制。綜上,此gso partial非上文提及的gso partial。

三、測試

       測試過程,請按如下準備:

1)兩臺機器client和server。可選在同一網段,並保證OS是linux4.18.0+系列。

2)兩個內核代碼。在兩臺機器分別下載對應版本的內核代碼,並進入到tools/testing/selftests/net的目錄下,make編譯即可。

3)server端輸入命令 ./udpgso_bench_rx 即可作爲udp監聽程序,如果加參數"-t"表示監聽tcp程序。

4)client端輸入命令 sudo perf stat -a -C 5 -e cycles ./udpgso_bench_tx -l 4 -4 -D {server ip addr} -S 即可開啓uso功能,如果不加參數"-s"表示正常udp發包。

5)如想開啓硬件支持uso offload,需查看網卡是否爲mlx5-core,並且查看網卡特性是否在gso-partial下有udp-segmentation-offload開關。

6)記錄第(4)步驟結果,即可。

       測試結果,請閱讀以下細節:

  1. TH-PUT即THROUGHPUT單位MB/s;
  2. CALLS單位calls/s;越小越好,帶來cpu開銷就越小;
  3. MESSAGES單位msg/s;
  4. 測試命令前可加入time命令,可測量SYS/USER的開銷。
  5. perf測試儘可能不要使用多核,否則由於cpu調度會帶來實驗結果的不一致性,綁核一個cpu即可。

       如下是一組實測數據對比:

MACHINE

TYPE

TH-PUT

CALLS

MESSAGES

USER

SYS

client

udp

839

663894

14245

0m0.343s

0m3.385s

udp gso

1139

19333

19333

0m0.038s

0m2.018s

server

udp

932

663896

 

udp gso

1135

808258

       如下是作者公開數據:

Types

calls/s

Mcycles/s

Speed-up (%)

TCP

19040

618

487

UDP

812000

2801

100

UDP GSO

18248

1726

174

      上述的測試過程、方法及結果已找udp gso作者Willem de Bruijn確認。

  1. 開啓gso後,client的calls次數相對於server降低40倍;而未開啓的兩端calls幾乎一樣。
  2. 開啓gso後,USER開銷方面,udp gso相對於gso提升了8倍。
  3. 開啓gso後,SYS開銷方面,udp gso相對於gso提升了0.67倍。

 

四、應用

  1. 開啓內核支持

如下是最初需要的socket設置:

static void set_pmtu_discover(int fd, bool is_ipv4)

{

  int level, name, val;

 

  if (is_ipv4) {

    level = SOL_IP;

    name  = IP_MTU_DISCOVER;

    val = IP_PMTUDISC_DO;

  } else {

    level = SOL_IPV6;

    name  = IPV6_MTU_DISCOVER;

    val = IPV6_PMTUDISC_DO;

  }

 

  if (setsockopt(fd, level, name, &val, sizeof(val)))

    error(1, errno, "setsockopt path mtu");

}

 

如下是udp發包的過程:

static int send_udp_segment(int fd, char *data)

{

  char control[CMSG_SPACE(sizeof(cfg_mss))] = {0};

  struct msghdr msg = {0};

  struct iovec iov = {0};

  int ret;

 

  iov.iov_base = data;

  iov.iov_len = cfg_payload_len;

 

  msg.msg_iov = &iov;

  msg.msg_iovlen = 1;

 

  msg.msg_control = control;

  msg.msg_controllen = sizeof(control);

  send_udp_segment_cmsg(CMSG_FIRSTHDR(&msg));

 

  msg.msg_name = (void *)&cfg_dst_addr;

  msg.msg_namelen = cfg_alen;

 

  ret = sendmsg(fd, &msg, cfg_zerocopy ? MSG_ZEROCOPY : 0);

  if (ret == -1)

    error(1, errno, "sendmsg");

  if (ret != iov.iov_len)

    error(1, 0, "sendmsg: %u != %lu\n", ret, iov.iov_len);

 

  return 1;

}

確定測試或業務方使用的代碼包括上面的socket設置部分和發包方式即可。

        僅作了解:

        開啓gso後,大概流程如此:發包流程將延遲分片,本在ip層做分片(ipv4/output.c文件下ip_fragment()函數),而現在將分片推遲到NIC發送前一刻。

 

五、未來展望

目前在4.18.0 redhat8或centos8版本內核有如下問題:

  1. 在應用層代碼中使用sendmsg(),而sendmmsg()是無法和uso一起工作。
  2. udp gso不支持MGS_ZEROCOPY。
  3. 使用udp gso offload代替udp gso是否會帶來真正性能上的提升。
  4. 目前udp gso offload只有mlx5-core支持,而intel近一個月才提交patch還未進入upstream。
  5. udp gso設置packet size是否有最優值。

如上只是一些brainstorm,未來可做事情依然很多,但是技術方案並不成熟,最大的問題是兼容性。雖然合併到了upstream,但是不能代表能再生產環境穩定運行。

 

附錄

  1. LPC presentation PPT
  2. NETCONF2017 PPT
  3. mlx5-core UDP GSO support
  4. intel系列網卡support
  5. LPC presentation video
  6. 世民談雲計算blog

 

 

 

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