Subject: Re: sendto() and ENOBUFS question..
To: Jonathan Stone <jonathan@DSG.Stanford.EDU>
From: sudog <sudog@sudog.com>
List: tech-net
Date: 05/15/2002 12:43:48
First off, I apologize in advance for beating a dead horse over.. and over.. 
and over. :)

On Wednesday 15 May 2002 11:53, Jonathan Stone wrote:
>
> First: suppose the bandwidth between your sending app, A, and your
> receiving app, B, is BW.  Let the round-trip-time (RTT) between A and
> B be RTT. There's little or no point having more than BW*RTT traffic
> in flight between A and B.  To a first approximation (ignoring loss
> and feedback effects), having more data than BW*RTT in flight just
> increases queue depths without increasing actual goodput between
> A and B. 

This I understand. Increasing queue depth is simply a resource hog which 
doesn't send packets any faster than the underlying mechanism can support. 
Reaching this theoretical maximum on a LAN is something I'm aiming to hit 
close to.

> If your sender app can send faster than the pipe to your receiver app
> can keep up, eventually *someone* will run out of buffers and start
> dropping packets.  This is exactly what you're seeing.  (I'll skip
> over some details since we don't know what kind of network you're
> using, or what BW or RTT you expect).

It's just a dinky little 10Mb hub right now. I would hope to see at least 1 
MB/sec in raw throughput, to match a similar TCP stream. This much I expect 
to be able to do. But if the mbufs are chewed up, it'd be nice to block 
somehow instead of regularly polling to check the writable status with a 
sendto(). 

> From prior discussion it looks like you're exceeding the send-queue
> limit on the socketbuffer on your sending host.  (See setsockopt
> SO_SNDBUF). Once your app has queued more than this limit of data on
> the socket, sosend() is going to start discarding data until the local
> socket send queue drains.
> 
> The suggestion I offered before was: monitor for this condition; when
> it occurs, whack the watermark up high, wait (via poll/select) for
> the majority of the queued data to drain before sending more.

So, grab the ENOBUFS, diddle with the watermark, and then send more. The 
problem I saw was that the watermark was by default 2048--larger than the 800 
byte packets I was sending, and neither select() nor poll() were blocking. :) 
I don't suppose you have another suggestion for me do you? =]

> You can increase the size of your socket send queue, but soon you'll
> find that a standing queue builds up on the outbound NIC send
> queue. But NIC drivers check for "queue full condition": a NIC queue
> is "full" at around 50 packets.  If you try to send more than that,
> and your packets get dropped, but in the driver send queue rather
> than from your app's socket send queue.

This is interesting--if the condition is detectable shouldn't the socket send 
queue stop trying to deliver packets? If not, then most of this is irrelevant 
since a working select() would just move the drops down lower and I'd be back 
where I started. I could definitely live with that. I mean if I have to 
interpret ENOBUFS I really don't mind. I just think that select()'ing for 
write on SOCK_DGRAMS feels broken. Purely my opinion of course. :)

> Bottom line is: the Internet drops packets. Your app should be aware
> of that.  If you know upfront what kind of data rates and delay you
> expect, you can precompute BW*RTT and adjust your app's sending rate
> appropriately.

This is precisely the kind of control I want to hand to the logic in 
userland, and (possibly) to the end user.

Thank you for your note,
Marc