“Nenhum espaço de buffer disponível” na conexão

5

Estou vendo a mensagem de erro "Nenhum espaço de buffer disponível" quando os processos chamam "conectar" em uma máquina virtual Linux. Estou tendo problemas para rastrear a causa - esperamos que alguém possa ajudar!

Eu verifiquei o seguinte:

(1) Identificadores de arquivos:

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

Estou lendo isso como (alocado, não usado, disponível), então isso parece OK.

(2) soquetes ou memória 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

Lendo isso como apenas um total de 579 soquetes em uso, os totais de páginas estão bem abaixo do máximo.

Existem muitos ajustes aleatórios no TCP mostrados no Google - o que estou esperando em uma resposta é (1) o recurso que estou esgotando, (2) como determinar o valor atual e (3) como para ajustar o teto. A maioria das páginas que encontrei está faltando tudo, exceto (3)!

** Atualização nº 1 **

Por sugestão do Flup eu fiz uma systrace quando isso acontece (usando 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)

** Atualização nº 2 **

Eu não sei muito sobre a fonte do kernel do Linux, mas eu tive uma escavação por aí e o único lugar no caminho connect () que eu posso ver o ENOBUFS está aqui: link

Isto parece que está alocando coisas no kernel com kmem_cache_alloc e security_sk_alloc ...?

    
por user611942 22.07.2014 / 12:55

2 respostas

2

Nos kernels anteriores a 3.6, você poderia ter sido atingido pelo ENOBUFS para o tráfego IPv4 / v6 regular, quando o net.ipv4.route.max_size ou net.ipv6.route.max_size limite foi deplorado, de acordo .

Começando com o kernel 3.6, o cache de roteamento foi removido e net.ipv4.route.max_size perdeu sua influência na quantidade de entradas dst. Então, geralmente, isso não seria mais possível.

No entanto, você ainda pode encontrar esse erro, como eu, ao usar o IPSec. Depois de certa quantidade de túneis IPSec criados, não consegui fazer ping no host remoto:

# ping 10.100.0.1
connect: No buffer space available

ping do iputils cria um descritor de arquivo de teste e usa o connect () nele, para ligar o dst ip. Quando isso acontece, a entrada do cache dst para o AF fornecido é criada pelo kernel, e xfrm4 dst cache limite de entradas já foi depleated no meu caso. Este limite é controlado pela configuração 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.

Eu encontrei isso usando o kernel 3.10.59, onde o limite padrão é muito baixo - 1024. A partir do kernel 3.10.83, este limite foi aumentado para 32768, e seria muito mais difícil de acertar .

Então, eu publiquei:

# sysctl net.ipv4.xfrm4_gc_thresh=32768

e isso fez a coisa por mim.

Caminho aproximado no kernel para o meu caso com 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.
    
por 13.03.2018 / 08:00
3

Bem, eu não sei exatamente qual é o problema, mas vou tentar descobrir a direção certa para resolvê-lo.

O código ENOBUFS é retornado quando sk_alloc() ou dst_alloc() falha. Não consigo encontrar nenhuma outra ocorrência de ENOBUFS no código-fonte relacionado aos soquetes.

Além disso, não consigo encontrar nenhum caminho do SYSCALL_DEFINE3(connect) para sk_alloc() , e acho que o soquete não deve ser alocado durante connect() call onde você recebe o erro, então acho que não é provável que o erro sk_alloc() causou o problema.

O dst_alloc() é provavelmente usado para verificar as rotas durante o connect() , não consigo encontrar o caminho exato para ele, ele deve estar em algum lugar dentro dele: SYSCALL_DEFINE3(connect) - > .connect() - > ip4_datagram_connect() - > ip_route_connect()

O dst_alloc() aloca uma entrada em um cache SLAB correspondente e pode realmente falhar se o cache estiver cheio. Na verdade, as entradas antigas devem ser eliminadas se isso acontecer, mas talvez haja casos em que ele ainda retorne um erro.

Então eu acho que você pode se mover nessa direção. O tamanho do cache dst pode ser alterado através de /proc/sys/net/ipv4/route/max_size . Primeiro, verifique se a configuração (ou qualquer outra configuração em sys.net.ipv4.route ) é alterada por "ajustes aleatórios de TCP mostrados no Google".

    
por 23.07.2014 / 00:46

Tags