linuxのtcp_rmemの変更は何に影響するのか調べたのでメモっておく

大容量なファイル送信にかかる時間を短縮する為に色々調べていたら sysctlで送受信用ウィンドウ・サイズの最大値を変更するようアドバイスするサイトが多かったので 変更によって何が影響するのか調べてみた。

tcp_select_initial_window()のif (wscale_ok)の辺りが関係しそう。

#define TCP_MAX_WSCALE          14U
 191 void tcp_select_initial_window(const struct sock *sk, int __space, __u32 mss,
 192              __u32 *rcv_wnd, __u32 *window_clamp,
 193              int wscale_ok, __u8 *rcv_wscale,
 194              __u32 init_rcv_wnd)
 195 {
 196   unsigned int space = (__space < 0 ? 0 : __space);
 197
 198   /* If no clamp set the clamp to the max possible scaled window */
 199   if (*window_clamp == 0)
 200     (*window_clamp) = (U16_MAX << TCP_MAX_WSCALE);
 201   space = min(*window_clamp, space);
 202
 203   /* Quantize space offering to a multiple of mss if possible. */
 204   if (space > mss)
 205     space = rounddown(space, mss);
 206
 207   /* NOTE: offering an initial window larger than 32767
 208    * will break some buggy TCP stacks. If the admin tells us
 209    * it is likely we could be speaking with such a buggy stack
 210    * we will truncate our initial window offering to 32K-1
 211    * unless the remote has sent us a window scaling option,
 212    * which we interpret as a sign the remote TCP is not
 213    * misinterpreting the window field as a signed quantity.
 214    */
 215   if (sock_net(sk)->ipv4.sysctl_tcp_workaround_signed_windows)
 216     (*rcv_wnd) = min(space, MAX_TCP_WINDOW);
 217   else
 218     (*rcv_wnd) = space;
 219
 220   (*rcv_wscale) = 0;
 221   if (wscale_ok) {
 222     /* Set window scaling on max possible window */
 223     space = max_t(u32, space, sock_net(sk)->ipv4.sysctl_tcp_rmem[2]);
 224     space = max_t(u32, space, sysctl_rmem_max);
 225     space = min_t(u32, space, *window_clamp);
 226     while (space > U16_MAX && (*rcv_wscale) < TCP_MAX_WSCALE) {
 227       space >>= 1;
 228       (*rcv_wscale)++;
 229     }
 230   }
 231
 232   if (mss > (1 << *rcv_wscale)) {
 233     if (!init_rcv_wnd) /* Use default unless specified otherwise */
 234       init_rcv_wnd = tcp_default_init_rwnd(mss);
 235     *rcv_wnd = min(*rcv_wnd, init_rcv_wnd * mss);
 236   }
 237
 238   /* Set the clamp no higher than max representable value */
 239   (*window_clamp) = min_t(__u32, U16_MAX << (*rcv_wscale), *window_clamp);
 240 }
 241 EXPORT_SYMBOL(tcp_select_initial_window);

TCP window scale オプションが有効だった場合、tcp_rmemの設定値に応じて 最大値14のTCP window scale の値が変更になる。

TCP window scale 値は3 Way ハンドシェイク時に通知され、Wireshark等で確認できる。
この画像ではWindow scale: 6
tcp_rmemの値を増やすと、このWindow scaleが最大14まで上昇する。 f:id:nishitki:20180921112833p:plain

ssコマンドなんかでも確認できて、下の画像の wscale:5,6がそれ。
tcp_rmemが3839488だった場合のWindow scale値は6で、通信相手のWindow scale値が5。 f:id:nishitki:20180921123328p:plain

tcp_rmemを増やすことによって、レイテンシーに影響する場合もあるようなので注意が必要ですね。 The story of one latency spike