iproute uses too small of a receive buffer
Subsystems (3)
I have a very busy system with a bunch of xorp router processes (mis)configured.
This thing is rapidly making route changes for whatever reason.
The 'ip monitor route' command was failing:
[root@i7-dqc-1 ]# ip monitor route
netlink receive error No buffer space available (105) Dump terminated
It is only using a 32k rcv buffer, and it seems the OS was overdriving it.
Please consider making the rcv buffer larger, perhaps something like this (inline is white-space damaged...attachment should apply if deemed useful.):
lib/libnetlink.c +1 -1
@@ -38,7 +38,7 @@ int rtnl_open_byproto(struct rtnl_handle *rth, unsigned subscriptions, {socklen_t addr_len;int sndbuf =32768 ; -int rcvbuf =32768 ; +int rcvbuf =3276800 ;memset (rth,0 ,sizeof (*rth));
Thanks,
Ben
Signature · 2 lines
Ben Greear <[email protected]> Candela Technologies Inc http://www.candelatech.com
lib/libnetlink.c +1 -1
@@ -38,7 +38,7 @@ int rtnl_open_byproto(struct rtnl_handle *rth, unsigned subscriptions, {socklen_t addr_len;int sndbuf =32768 ; -int rcvbuf =32768 ; +int rcvbuf =3276800 ;memset (rth,0 ,sizeof (*rth));
Quoted 34 lines · On Tue, 27 Oct 2009 16:16:52 -0700 Ben Greear <[email protected]> wrote:
I have a very busy system with a bunch of xorp router processes (mis)configured.
This thing is rapidly making route changes for whatever reason.
The 'ip monitor route' command was failing:
[root@i7-dqc-1 ]# ip monitor route
netlink receive error No buffer space available (105) Dump terminatedIt is only using a 32k rcv buffer, and it seems the OS was overdriving it.
Please consider making the rcv buffer larger, perhaps something like this (inline is white-space damaged...attachment should apply if deemed useful.):
Signed-off-by Ben Greear <[email protected]>lib/libnetlink.c +1
-1@@ -38,7 +38,7 @@ int rtnl_open_byproto(struct rtnl_handle *rth, unsigned subscriptions, {socklen_t addr_len;int sndbuf =32768 ; -int rcvbuf =32768 ; +int rcvbuf =3276800 ;memset (rth,0 ,sizeof (*rth));
Quoted 3 lines
Thanks,
Ben
Just having larger buffer isn't guarantee of success. Allocating a huge buffer is not going to work on embedded.
Why not have it continue after one error.
Quoted 37 lines · Stephen Hemminger
Quoted 34 lines · On Tue, 27 Oct 2009 16:16:52 -0700 Ben Greear<[email protected]> wrote:
I have a very busy system with a bunch of xorp router processes (mis)configured.
This thing is rapidly making route changes for whatever reason.
The 'ip monitor route' command was failing:
[root@i7-dqc-1 ]# ip monitor route
netlink receive error No buffer space available (105) Dump terminatedIt is only using a 32k rcv buffer, and it seems the OS was overdriving it.
Please consider making the rcv buffer larger, perhaps something like this (inline is white-space damaged...attachment should apply if deemed useful.):
Signed-off-by Ben Greear<[email protected]>lib/libnetlink.c +1
-1@@ -38,7 +38,7 @@ int rtnl_open_byproto(struct rtnl_handle *rth, unsigned subscriptions, {socklen_t addr_len;int sndbuf =32768 ; -int rcvbuf =32768 ; +int rcvbuf =3276800 ;memset (rth,0 ,sizeof (*rth));
Quoted 8 lines
Quoted 3 lines
Thanks,
BenJust having larger buffer isn't guarantee of success. Allocating a huge buffer is not going to work on embedded.
Why not have it continue after one error.
Probably the right way is to give a cmd-line arg to set the buffer size and also continue if the error is ENOBUFs (but print some error out so users know they have issues). I can make the attempt if that sounds good to you.
Thanks,
Ben
Signature · 2 lines
Ben Greear <[email protected]> Candela Technologies Inc http://www.candelatech.com
Quoted 5 lines · Ben Greear
Probably the right way is to give a cmd-line arg to set the buffer size and also continue if the error is ENOBUFs (but print some error out so users know they have issues). I can make the attempt if that sounds good to you.
Real fix is to realloc buffer at receive time, no need for user setting.
In my testings I saw it reaching 1 Mbyte write(2, "REALLOC buflen 8192\n"..., 20) = 20 write(2, "REALLOC buflen 16384\n"..., 21) = 21 write(2, "REALLOC buflen 32768\n"..., 21) = 21 write(2, "REALLOC buflen 65536\n"..., 21) = 21 write(2, "REALLOC buflen 131072\n"..., 22) = 22 write(2, "REALLOC buflen 262144\n"..., 22) = 22 write(2, "REALLOC buflen 524288\n"..., 22) = 22
[iproute2] realloc buffer in rtnl_listen
# ip monitor route netlink receive error No buffer space available (105) Dump terminated
---
lib/libnetlink.c +16 -2
@@ -392,8 +392,14 @@ int rtnl_listen(struct rtnl_handle *rtnl, .msg_iov = &iov, .msg_iovlen = 1, }; -char buf[8192 ]; +char *buf; +size_t buflen =8192 ; + buf =malloc (buflen); +if (buf ==NULL ) { +fprintf (stderr,"netlink could not alloc %lu bytes\n" , buflen); +return -1 ; + }memset (&nladdr,0 ,sizeof (nladdr)); nladdr.nl_family =AF_NETLINK ; nladdr.nl_pid =0 ;
@@ -401,12 +407,20 @@ int rtnl_listen(struct rtnl_handle *rtnl, iov.iov_base = buf;while (1 ) { - iov.iov_len =sizeof (buf); + iov.iov_len = buflen; status =recvmsg (rtnl->fd, &msg,0 );if (status <0 ) {if (errno ==EINTR || errno ==EAGAIN )continue ; +if (errno ==ENOBUFS ) { + buf =realloc (buf, buflen *2 ); +if (buf) { + buflen *=2 ; + iov.iov_base = buf; +continue ; + } + }fprintf (stderr,"netlink receive error %s (%d)\n" ,strerror (errno), errno);return -1 ;
Quoted 8 lines · Eric Dumazet
Quoted 4 lines · Ben Greear
Probably the right way is to give a cmd-line arg to set the buffer size and also continue if the error is ENOBUFs (but print some error out so users know they have issues). I can make the attempt if that sounds good to you.
Real fix is to realloc buffer at receive time, no need for user setting.
Then, another problem is that some information can be dropped at kernel level when socket rcvbuf is full (ip monitor too slow to read its socket)
Thats hard to fix because you need to tweak /proc/sys/net/core/rmem_max
Quoted 18 lines · Eric Dumazet
Quoted 4 lines · Ben Greear
Probably the right way is to give a cmd-line arg to set the buffer size and also continue if the error is ENOBUFs (but print some error out so users know they have issues). I can make the attempt if that sounds good to you.
Real fix is to realloc buffer at receive time, no need for user setting.
In my testings I saw it reaching 1 Mbyte
write(2, "REALLOC buflen 8192\n"..., 20)= 20write(2, "REALLOC buflen 16384\n"..., 21)= 21write(2, "REALLOC buflen 32768\n"..., 21)= 21write(2, "REALLOC buflen 65536\n"..., 21)= 21write(2, "REALLOC buflen 131072\n"..., 22)= 22write(2, "REALLOC buflen 262144\n"..., 22)= 22write(2, "REALLOC buflen 524288\n"..., 22)= 22
Quoted 8 lines
[iproute2] realloc buffer in
rtnl_listen# ip monitor route netlink receive error No buffer space available (105) Dump terminatedReported-by Ben Greear<[email protected]>Signed-off-by Eric Dumazet <[email protected]>
Oops, this was wrong, Ben was right, sorry...
ENOBUFS errors is a flag to actually report to user that some information was dropped, not that user supplied buffer at recv() time is not big enough.
I was surprised that buffer could reach 1Mbytes, while RCVBUF was 32768 or so.
Quoted 4 lines · Stephen Hemminger
Just having larger buffer isn't guarantee of success. Allocating a huge buffer is not going to work on embedded.
Please note we do not allocate a big buffer, only allow more small skbs to be queued on socket receive queue.
If memory is not available, skb allocation will eventually fail and be reported as well, embedded or not.
I vote for allowing 1024*1024 bytes instead of 32768, and eventually user should be warned that it is capped by /proc/sys/net/core/rmem_max
Quoted 1 line
Why not have it continue after one error.
Yes, but caller of 'ip monitor' just restart it anyway
David Miller
maintainerFrom: Eric Dumazet <[email protected]>
Date: Wed, 28 Oct 2009 08:52:57 +0100
Quoted 9 lines
Quoted 4 lines · Stephen Hemminger
Just having larger buffer isn't guarantee of success. Allocating a huge buffer is not going to work on embedded.
Please note we do not allocate a big buffer, only allow more small skbs to be queued on socket receive queue.
Quoted 6 lines
If memory is not available, skb allocation will eventually fail and be reported as well, embedded or not.
I vote for allowing 1024*1024 bytes instead of 32768, and eventually user should be warned that it is capped by
/proc/sys/net/core/rmem_max
This discussion constantly reminds me of:
/*
* skb should fit one page. This choice is good for headerless malloc.
* But we should limit to 8K so that userspace does not have to
* use enormous buffer sizes on recvmsg() calls just to avoid
* MSG_TRUNC when PAGE_SIZE is very large.
*/
#if PAGE_SIZE < 8192UL
#define NLMSG_GOODSIZE SKB_WITH_OVERHEAD(PAGE_SIZE)
#else
#define NLMSG_GOODSIZE SKB_WITH_OVERHEAD(8192UL)
#endif
#define NLMSG_DEFAULT_SIZE (NLMSG_GOODSIZE - NLMSG_HDRLEN)
Quoted 8 lines · Eric Dumazet
Quoted 3 lines · Stephen Hemminger
Just having larger buffer isn't guarantee of success. Allocating a huge buffer is not going to work on embedded.
Please note we do not allocate a big buffer, only allow more small skbs to be queued on socket receive queue.
Quoted 6 lines
If memory is not available, skb allocation will eventually fail and be reported as well, embedded or not.
I vote for allowing 1024*1024 bytes instead of 32768, and eventually user should be warned that it is capped by
/proc/sys/net/core/rmem_max
How about this? It will double the receive queue limit on ENOBUFS up to 1024 * 1024b, then bail out with the normal error message on further ENOBUFS.
lib/libnetlink.c +8 -1
@@ -25,6 +25,8 @@#include "libnetlink.h" +static int rcvbuf =32768 ; +void rtnl_close (struct rtnl_handle *rth) {if (rth->fd >=0 ) {
@@ -38,7 +40,6 @@ int rtnl_open_byproto(struct rtnl_handle *rth, unsigned subscriptions, {socklen_t addr_len;int sndbuf =32768 ; -int rcvbuf =32768 ;memset (rth,0 ,sizeof (*rth));
@@ -407,6 +409,12 @@ int rtnl_listen(struct rtnl_handle *rtnl,if (status <0 ) {if (errno ==EINTR || errno ==EAGAIN )continue ; +if (errno ==ENOBUFS && rcvbuf <1024 *1024 ) { + rcvbuf *=2 ; +if (setsockopt (rtnl->fd,SOL_SOCKET ,SO_RCVBUF , + &rcvbuf,sizeof (rcvbuf)) ==0 ) +continue ; + }fprintf (stderr,"netlink receive error %s (%d)\n" ,strerror (errno), errno);return -1 ;
Quoted 16 lines · Patrick McHardy
Quoted 8 lines · Eric Dumazet
Quoted 3 lines · Stephen Hemminger
Just having larger buffer isn't guarantee of success. Allocating a huge buffer is not going to work on embedded.
Please note we do not allocate a big buffer, only allow more small skbs to be queued on socket receive queue.
Quoted 6 lines
If memory is not available, skb allocation will eventually fail and be reported as well, embedded or not.
I vote for allowing 1024*1024 bytes instead of 32768, and eventually user should be warned that it is capped by
/proc/sys/net/core/rmem_max
Quoted 5 lines
How about this? It will double the receive queue limit on ENOBUFS up to 1024 * 1024b, then bail out with the normal error message on further ENOBUFS.
Signed-off-by Patrick McHardy<[email protected]>
First: This still pretty much guarantees that messages will be lost when the program starts (when messages are coming in too large of chunks for small buffers) If you are debugging something tricky, having lost messages will be
very annoying!
Second: Why bail on ENOBUFS at all? I don't see how it helps the user since they will probably just have to start it again, and will miss more messages than keeping going would have.
And, even 1MB may not be enough for some scenarios. So, probably best to let users over-ride the initial setting on cmd-line. If not, then use a large value to start with.
Thanks,
Ben
Signature · 2 lines
Ben Greear <[email protected]> Candela Technologies Inc http://www.candelatech.com
Quoted 21 lines · Ben Greear
Quoted 20 lines · Patrick McHardy
Quoted 8 lines · Eric Dumazet
Quoted 3 lines · Stephen Hemminger
Just having larger buffer isn't guarantee of success. Allocating a huge buffer is not going to work on embedded.
Please note we do not allocate a big buffer, only allow more small skbs to be queued on socket receive queue.
Quoted 6 lines
If memory is not available, skb allocation will eventually fail and be reported as well, embedded or not.
I vote for allowing 1024*1024 bytes instead of 32768, and eventually user should be warned that it is capped by
/proc/sys/net/core/rmem_maxHow about this? It will double the receive queue limit on ENOBUFS up to 1024 * 1024b, then bail out with the normal error message on further ENOBUFS.
Quoted 7 lines
Quoted 1 line
Signed-off-by Patrick McHardy<[email protected]>First: This still pretty much guarantees that messages will be lost when the program starts (when messages are coming in too large of chunks for small buffers)
If you are debugging something tricky, having lost messages will be very annoying!
Yeah, on second thought the probing also doesn't make too much sense since the memory is only used when its really needed anyways. And its capped by rmem_max.
Quoted 3 lines
Second: Why bail on ENOBUFS at all? I don't see how it helps the user since they will probably just have to start it again, and will miss more messages than keeping going would have.
Agreed.
Quoted 3 lines
And, even 1MB may not be enough for some scenarios. So, probably best to let users over-ride the initial setting on cmd-line. If not, then use a large value to start with.
How about this? It uses 1MB as receive buf limit by default (without increasing /proc/sys/net/core/rmem_max it will be limited by less however) and allows to specify the size manually using "-rcvbuf X" (-r is already used, so you need to specify at least -rc).
Additionally rtnl_listen() continues on ENOBUFS after printing the error message.
include/libnetlink.h +2 -0
@@ -17,6 +17,8 @@ struct rtnl_handle__u32 dump; }; +extern int rcvbuf; +extern int rtnl_open (struct rtnl_handle *rth,unsigned subscriptions);extern int rtnl_open_byproto (struct rtnl_handle *rth,unsigned subscriptions,int protocol);extern void rtnl_close (struct rtnl_handle *rth);
ip/ip.c +15 -1
@@ -50,7 +50,8 @@ static void usage(void)" tunnel | maddr | mroute | monitor | xfrm }\n" " OPTIONS := { -V[ersion] | -s[tatistics] | -d[etails] | -r[esolve] |\n" " -f[amily] { inet | inet6 | ipx | dnet | link } |\n" -" -o[neline] | -t[imestamp] | -b[atch] [filename] }\n"); +" -o[neline] | -t[imestamp] | -b[atch] [filename] |\n" +" -rc[vbuf] [size]}\n");exit (-1 ); }
@@ -213,6 +214,19 @@ int main(int argc, char **argv)if (argc <=1 )usage (); batch_file = argv[1 ]; + }else if (matches (opt,"-rcvbuf" ) ==0 ) { +unsigned int size; + + argc--; + argv++; +if (argc <=1 ) +usage (); +if (get_unsigned (&size, argv[1 ],0 )) { +fprintf (stderr,"Invalid rcvbuf size '%s'\n" , + argv[1 ]); +exit (-1 ); + } + rcvbuf = size; }else if (matches (opt,"-help" ) ==0 ) {usage (); }else {
lib/libnetlink.c +4 -1
@@ -25,6 +25,8 @@#include "libnetlink.h" +int rcvbuf =1024 *1024 ; +void rtnl_close (struct rtnl_handle *rth) {if (rth->fd >=0 ) {
@@ -38,7 +40,6 @@ int rtnl_open_byproto(struct rtnl_handle *rth, unsigned subscriptions, {socklen_t addr_len;int sndbuf =32768 ; -int rcvbuf =32768 ;memset (rth,0 ,sizeof (*rth));
@@ -409,6 +410,8 @@ int rtnl_listen(struct rtnl_handle *rtnl,continue ;fprintf (stderr,"netlink receive error %s (%d)\n" ,strerror (errno), errno); +if (errno ==ENOBUFS ) +continue ;return -1 ; }if (status ==0 ) {
Quoted 11 lines · Patrick McHardy
Quoted 3 lines
And, even 1MB may not be enough for some scenarios. So, probably best to let users over-ride the initial setting on cmd-line. If not, then use a large value to start with.
How about this? It uses 1MB as receive buf limit by default (without increasing
/proc/sys/net/core/rmem_maxit will be limited by less however) and allows to specify the size manually using "-rcvbuf X" (-r is already used, so you need to specify at least -rc).Additionally
rtnl_listen()continues on ENOBUFS after printing the error message.
Looks good..except:
If rmem_max is smaller than 1M, will that cause setsocktopt to fail and thus fail early out of rtnl_open_byproto?
Maybe we should only print errors but not return in that method when setsockopt fails?
In another project, I ended up trying ever smaller values until one worked in order to get near what the user wanted even if rmem_max was configured smaller. Not sure if that is worth doing here or not.
Thanks,
Ben
Signature · 2 lines
Ben Greear <[email protected]> Candela Technologies Inc http://www.candelatech.com
Quoted 12 lines · Ben Greear
Quoted 10 lines · Patrick McHardy
Quoted 4 lines
And, even 1MB may not be enough for some scenarios. So, probably best to
let users over-ride the initial setting on cmd-line. If not, then use a large value to start with.How about this? It uses 1MB as receive buf limit by default (without increasing
/proc/sys/net/core/rmem_maxit will be limited by less however) and allows to specify the size manually using "-rcvbuf X" (-r is already used, so you need to specify at least -rc).
Quoted 7 lines
Quoted 2 lines
Additionally
rtnl_listen()continues on ENOBUFS after printing the error message.Looks good..except:
If
rmem_maxis smaller than 1M, will that cause setsocktopt to fail and thus fail early out ofrtnl_open_byproto?
No, the kernel takes the value as a hint and only uses the maximum allowable value:
case SO_RCVBUF:
/* Don't error on this BSD doesn't and if you think
about it this is right. Otherwise apps have to play 'guess the biggest size' games. RCVBUF/SNDBUF are treated in BSD as hints */
if (val > sysctl_rmem_max)
val = sysctl_rmem_max;
Quoted 6 lines
Maybe we should only print errors but not return in that method when setsockopt fails?
In another project, I ended up trying ever smaller values until one worked in order to get near what the user wanted even if
rmem_maxwas configured smaller. Not sure if that is worth doing here or not.
I think it should be fine this way.
Quoted 16 lines · Patrick McHardy
Quoted 15 lines · Ben Greear
Quoted 12 lines · Patrick McHardy
Quoted 4 lines
And, even 1MB may not be enough for some scenarios. So, probably best to
let users over-ride the initial setting on cmd-line. If not, then use a large value to start with.How about this? It uses 1MB as receive buf limit by default (without increasing
/proc/sys/net/core/rmem_maxit will be limited by less however) and allows to specify the size manually using "-rcvbuf X" (-r is already used, so you need to specify at least -rc).Additionally
rtnl_listen()continues on ENOBUFS after printing the error message.
Quoted 7 lines
Quoted 4 lines
Looks good..except:
If
rmem_maxis smaller than 1M, will that cause setsocktopt to fail and thus fail early out ofrtnl_open_byproto?No, the kernel takes the value as a hint and only uses the maximum allowable value:
Sweet. No complaints from me then.
Thanks,
Ben
Signature · 2 lines
Ben Greear <[email protected]> Candela Technologies Inc http://www.candelatech.com
Stephen Hemminger
AppliedQuoted 9 lines
How about this? It uses 1MB as receive buf limit by default (without increasing
/proc/sys/net/core/rmem_maxit will be limited by less however) and allows to specify the size manually using "-rcvbuf X" (-r is already used, so you need to specify at least -rc).Additionally
rtnl_listen()continues on ENOBUFS after printing the error message.
Applied, seems like the best workaround
Quoted 8 lines · Ben Greear
Second: Why bail on ENOBUFS at all? I don't see how it helps the user since they will probably just have to start it again, and will miss more messages than keeping going would have.
And, even 1MB may not be enough for some scenarios. So, probably best to let users over-ride the initial setting on cmd-line. If not, then use a large value to start with.
In this case, just dont call setsockopt() at all in "ip" and let system use the standard/default value (/proc/sys/net/core/rmem_default) that an admin can change if he wants to handle one million devices :)
From: Patrick McHardy <[email protected]>
Date: Wed, 28 Oct 2009 20:05:12 +0100
Quoted 5 lines
How about this? It will double the receive queue limit on ENOBUFS up to 1024 * 1024b, then bail out with the normal error message on further ENOBUFS.
Signed-off-by Patrick McHardy <[email protected]>