SO_SNDBUF makes select() hang

Oliver Jowett (oliver@sa-search.massey.ac.nz)
Sat, 27 Jul 1996 13:51:51 +1200 (NZST)


Hm, while playing with setting SO_SNDBUF through setsockopt() I noticed
this behavior: if SO_SNDBUF is set low (I was trying 128) then select()
won't ever show the socket as available for writing.

This bit of code demonstrates the problem:

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>

void main(void)
{
int s;
struct sockaddr_in addr;
int v;
fd_set writefds;

s=socket(AF_INET, SOCK_STREAM, 0);

addr.sin_family=AF_INET;
addr.sin_port=htons(9); /* discard */
addr.sin_addr.s_addr=inet_addr("127.0.0.1");

connect(s, (struct sockaddr *)&addr, sizeof(addr));

v=128;
printf("SO_SNDBUF: %d\n",
setsockopt(s, SOL_SOCKET, SO_SNDBUF, &v, sizeof(v)));

printf("write: %d\n",
write(s, &v, sizeof(v))); /* this works */

FD_ZERO(&writefds);
FD_SET(s, &writefds);

printf("select: %d\n",
select(FD_SETSIZE, NULL, &writefds, NULL, NULL)); /* this blocks?! */

close(s);

exit(0);
}

The write succeeds, but the select blocks indefinitely.

I'm not very familiar with the network code, but I think this is due to
this check in tcp_select (net/ipv4/tcp.c):

if (sock_wspace(sk) < sk->mtu+128+sk->prot->max_header)
break;
return 1;

sock_wspace returns sk->sndbuf - sk->wmem_alloc normally, so if sk->sndbuf
is too small then the socket will never become available for writing in
tcp_select.

The SO_SNDBUF code (net/core/sock.c) checks for a buffer size < 256 and
resets it to 256, but it would appear that 256 is always less than
sk->mtu+128+sk->prot->max_header. Should the SO_SNDBUF code check for this
minimum size instead?

[this is with 2.0.0, but I didn't see any changes to this bit of the code in
the .1-.9 patches]

Oliver

--
     "C makes it easy to shoot yourself in the foot.  C++ makes it
      harder, but when you do, it blows away your whole leg."

-- Bjarne Stroustrup on C++