Назад | Перейти на главную страницу

«Нет свободного места в буфере» при подключении

Я вижу сообщение об ошибке «Нет свободного места в буфере», когда процессы вызывают «подключение» на виртуальной машине 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.