连接时出现“无可用缓冲区空间”

连接时出现“无可用缓冲区空间”

当进程在 Linux 虚拟机上调用“connect”时,我看到错误消息“无可用缓冲区空间”。我无法找出原因 - 希望有人能帮忙!

我已检查以下内容:

(1)文件句柄:

cat /proc/sys/fs/file-nr
4672 0 810707 

我将其读为(已分配、未使用、可用),因此看起来没问题。

(2)套接字或TCP内存:

cat /proc/sys/net/ipv4/tcp_mem
191889 255854 383778

cat /proc/net/sockstat
sockets: used 579
TCP: inuse 169 orphan 0 tw 245 alloc 187 mem 5
UDP: inuse 31 mem 4
UDPLITE: inuse 0
RAW: inuse 0
FRAG: inuse 0 memory 0

读到这里,发现总共只使用了 579 个插座,页面总数远低于最大值。

Google 上有很多随机 TCP 调整 - 我希望得到的答案是 (1) 我所用资源不足,(2) 如何确定当前值,以及 (3) 如何调整上限。我发现的大多数页面都缺少除 (3) 之外的所有内容!

** 更新 #1 **

根据 Flup 的建议,当它发生时我做了一个系统跟踪(使用 ping):

socket(PF_INET, SOCK_DGRAM, IPPROTO_IP) = 4
connect(4, {sa_family=AF_INET, sin_port=htons(1025), sin_addr=inet_addr("10.140.0.65")}, 16) = -1 ENOBUFS (No buffer space available)

** 更新 #2 **

我不太了解 Linux 内核源代码,但是我仔细研究了一下,在 connect() 路径中我唯一能看到 ENOBUFS 的地方是这里: http://lxr.free-electrons.com/source/net/ipv4/af_inet.c?v=3.11#L353

这看起来像是在内核中分配东西,但是有kmem_cache_alloc…… security_sk_alloc

答案1

在 3.6 之前的内核中,当 net.ipv4.route.max_size 或 net.ipv6.route.max_size因此,限额已耗尽

从内核 3.6 开始,路由缓存已被删除,并且 net.ipv4.route.max_size 不再影响 dst 条目的数量。因此,一般来说,这不再可能。

但是,使用 IPSec 时,您仍然会像我一样遇到此错误。在创建一定数量的 IPSec 隧道后,我无法 ping 远程主机:

# ping 10.100.0.1
connect: No buffer space available

iputils 中的 ping 创建测试文件描述符,并在其上使用 connect() 来绑定目标 IP。发生这种情况时,内核会为给定的 AF 创建目标缓存条目,而 xfrm4 目标缓存条目限制在我的情况下已经耗尽。此限制由 sysctl 设置控制:

xfrm4_gc_thresh - INTEGER
    The threshold at which we will start garbage collecting for IPv4
    destination cache entries.  At twice this value the system will
    refuse new allocations.

我在使用内核 3.10.59 时遇到过这个问题,其中默认限制非常低 - 1024。从内核 3.10.83 开始,此限制增加到 32768,并且将是更难击中

因此,我发布了:

# sysctl net.ipv4.xfrm4_gc_thresh=32768

它帮我实现了这个目标。

对于我使用 IPSec 的情况,内核中的近似路径为:

ip4_datagram_connect() -> ip_route_connect() -> ip_route_output_flow() ->
xfrm_lookup() -> xfrm_resolve_and_create_bundle() ->
... -> xfrm_alloc_dst() -> dst_alloc() with xfrm4_dst_ops, where gc is set.

答案2

好吧,我不知道问题到底是什么,但我会尝试找出解决问题的正确方向。

当或失败ENOBUFS时,将返回代码。我在与套接字相关的源代码中找不到任何其他出现的代码。sk_alloc()dst_alloc()ENOBUFS

另外,我找不到从SYSCALL_DEFINE3(connect)到的任何路径sk_alloc(),并且我认为在出现错误的调用期间不应该分配套接字connect(),因此我认为不太可能sk_alloc()导致问题。

dst_alloc()可能用于在期间检查路线connect(),我找不到它的确切路径,它一定在里面的某个地方:SYSCALL_DEFINE3(connect)->.connect() ->ip4_datagram_connect()->ip_route_connect()

在相应的 SLAB 缓存中分配dst_alloc()一个条目,如果缓存已满,它实际上可能会失败。如果发生这种情况,实际上应该清除旧条目,但可能有些情况下它仍然会返回错误。

所以我认为你可以朝这个方向发展。dst 缓存大小可能通过 更改/proc/sys/net/ipv4/route/max_size。首先,检查设置(或 中的任何其他设置sys.net.ipv4.route)是否被“Google 上显示的随机 TCP 调整”更改。

相关内容