No, nevím, v čem přesně je problém, ale pokusím se najít správný směr jeho řešení.
ENOBUFS
kód je vrácen, když je buď sk_alloc()
nebo dst_alloc()
selhal. Nemohu najít žádné další výskyty ENOBUFS
ve zdrojovém kódu souvisejícím se sokety.
Také nemohu najít žádné cesty z SYSCALL_DEFINE3(connect)
až sk_alloc()
, a myslím, že soket by neměl být alokován během connect()
zavolejte, kde se zobrazí chyba, takže si myslím, že není pravděpodobné, že sk_alloc()
způsobil problém.
dst_alloc()
se pravděpodobně používá pro kontrolu tras během connect()
, nemohu k němu najít přesnou cestu, musí to být někde uvnitř:SYSCALL_DEFINE3(connect)
-> .connect()
-> ip4_datagram_connect()
-> ip_route_connect()
dst_alloc()
přiděluje položku v odpovídající mezipaměti SLAB a může ve skutečnosti selhat, pokud je mezipaměť plná. Pokud k tomu dojde, staré záznamy by měly být vyčištěny, ale možná existují případy, kdy stále vrací chybu.
Takže si myslím, že můžete přejít tímto směrem. Velikost mezipaměti dst se může změnit až do /proc/sys/net/ipv4/route/max_size
. Nejprve zkontrolujte, zda nastavení (nebo jakékoli jiné nastavení v sys.net.ipv4.route
) je změněno „náhodnými vylepšeními TCP zobrazenými na Googlu“.
V jádrech před 3.6 jste mohli být zasaženi ENOBUFS pro běžný provoz IPv4/v6, když byl odpovídajícím způsobem vyčerpán limit net.ipv4.route.max_size nebo net.ipv6.route.max_size.
Počínaje jádrem 3.6 byla směrovací mezipaměť odstraněna a net.ipv4.route.max_size ztratil svůj vliv na množství položek dst. Takže obecně by to již nebylo možné.
Stále však můžete narazit na tuto chybu jako já při používání IPSec. Po určitém počtu vytvořených IPSec tunelů se mi nepodařilo pingnout vzdáleného hostitele:
# ping 10.100.0.1
connect: No buffer space available
ping z iputils vytvoří deskriptor testovacího souboru a použije na něj connect() k navázání dst ip. Když se to stane, dst cache entry pro daný AF je vytvořen jádrem a limit dst cache entries xfrm4 byl v mém případě již vyčerpán. Tento limit je řízen nastavením 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.
Narazil jsem na to pomocí jádra 3.10.59, kde je výchozí limit velmi nízký - 1024. Počínaje jádrem 3.10.83 byl tento limit zvýšen na 32768 a bylo by mnohem těžší ho dosáhnout.
Takže jsem vydal:
# sysctl net.ipv4.xfrm4_gc_thresh=32768
a udělal to pro mě.
Přibližná cesta v jádře pro můj případ s 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.