From 0a9dd47dd817904ec2b4a80b551e6050218ee8a6 Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Tue, 22 Sep 2020 09:30:31 +0700 Subject: [PATCH] net: reflect TCP backlog size update of uint16->uint32 on Linux The sk_max_ack_backlog was increased from uint16 to uint32 in kernel version 4.1 and above, so adopt that change to maxListenerBacklog. See https://github.com/torvalds/linux/commit/becb74f0acca19b5abfcb24dc602530f3deea66a Fixes #41470 Change-Id: I63a142eb28f3ac3acaca57f0903c085c6cb15a6e Reviewed-on: https://go-review.googlesource.com/c/go/+/255898 Run-TryBot: Cuong Manh Le TryBot-Result: Go Bot Trust: Cuong Manh Le Trust: Emmanuel Odeke Reviewed-by: Ian Lance Taylor Reviewed-by: Emmanuel Odeke --- src/net/sock_linux.go | 62 +++++++++++++++++++++++++++++++++++--- src/net/sock_linux_test.go | 22 ++++++++++++++ 2 files changed, 80 insertions(+), 4 deletions(-) create mode 100644 src/net/sock_linux_test.go diff --git a/src/net/sock_linux.go b/src/net/sock_linux.go index 7bca37605e..4d91001937 100644 --- a/src/net/sock_linux.go +++ b/src/net/sock_linux.go @@ -6,6 +6,62 @@ package net import "syscall" +func kernelVersion() (major int, minor int) { + var uname syscall.Utsname + if err := syscall.Uname(&uname); err != nil { + return + } + + rl := uname.Release + var values [2]int + vi := 0 + value := 0 + for _, c := range rl { + if c >= '0' && c <= '9' { + value = (value * 10) + int(c-'0') + } else { + // Note that we're assuming N.N.N here. If we see anything else we are likely to + // mis-parse it. + values[vi] = value + vi++ + if vi >= len(values) { + break + } + } + } + switch vi { + case 0: + return 0, 0 + case 1: + return values[0], 0 + case 2: + return values[0], values[1] + } + return +} + +// Linux stores the backlog as: +// +// - uint16 in kernel version < 4.1, +// - uint32 in kernel version >= 4.1 +// +// Truncate number to avoid wrapping. +// +// See issue 5030 and 41470. +func maxAckBacklog(n int) int { + major, minor := kernelVersion() + size := 16 + if major > 4 || (major == 4 && minor >= 1) { + size = 32 + } + + var max uint = 1< max { + n = int(max) + } + return n +} + func maxListenerBacklog() int { fd, err := open("/proc/sys/net/core/somaxconn") if err != nil { @@ -21,11 +77,9 @@ func maxListenerBacklog() int { if n == 0 || !ok { return syscall.SOMAXCONN } - // Linux stores the backlog in a uint16. - // Truncate number to avoid wrapping. - // See issue 5030. + if n > 1<<16-1 { - n = 1<<16 - 1 + return maxAckBacklog(n) } return n } diff --git a/src/net/sock_linux_test.go b/src/net/sock_linux_test.go new file mode 100644 index 0000000000..5df02935c3 --- /dev/null +++ b/src/net/sock_linux_test.go @@ -0,0 +1,22 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package net + +import ( + "testing" +) + +func TestMaxAckBacklog(t *testing.T) { + n := 196602 + major, minor := kernelVersion() + backlog := maxAckBacklog(n) + expected := 1<<16 - 1 + if major > 4 || (major == 4 && minor >= 1) { + expected = n + } + if backlog != expected { + t.Fatalf(`Kernel version: "%d.%d", sk_max_ack_backlog mismatch, got %d, want %d`, major, minor, backlog, expected) + } +} -- 2.50.0