Я вижу сообщение об ошибке «Нет свободного места в буфере», когда процессы вызывают «подключение» на виртуальной машине Linux. У меня проблемы с поиском причины - надеюсь, кто-нибудь может помочь!
Я проверил следующее:
(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 **
По предложению Флупа я сделал систрасу, когда это произойдет (с помощью 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
...?
Что ж, я не знаю, в чем именно проблема, но постараюсь найти правильное направление для ее решения.
В 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()
В dst_alloc()
выделяет запись в соответствующем кэше SLAB, и может произойти сбой, если кэш заполнен. На самом деле старые записи должны быть удалены, если это произойдет, но, возможно, есть случаи, когда он все еще возвращает ошибку.
Так что я думаю, вы можете двигаться в этом направлении. Размер кэша dst может быть изменен через /proc/sys/net/ipv4/route/max_size
. Во-первых, проверьте, что параметр (или любые другие параметры в sys.net.ipv4.route
) заменяется "случайными настройками TCP, показанными в Google".
В ядрах до 3.6 вы могли столкнуться с ENOBUFS для обычного трафика IPv4 / v6, когда net.ipv4.route.max_size или net.ipv6.route.max_size лимит был исчерпан, соответственно.
Начиная с ядра 3.6, кеш маршрутизации был удален, а net.ipv4.route.max_size потерял влияние на количество записей dst. Так что, как правило, это уже невозможно.
Однако вы все равно можете столкнуться с этой ошибкой, как и я, при использовании IPSec. После определенного количества созданных туннелей IPSec мне не удалось проверить связь с удаленным хостом:
# ping 10.100.0.1
connect: No buffer space available
ping из iputils создает дескриптор тестового файла и использует на нем connect () для привязки dst ip. Когда это происходит, запись кэша dst для данного AF создается ядром, и в моем случае ограничение на количество записей кеша dst 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.