Subject: Re: sendto() and ENOBUFS question..
To: None <tech-net@netbsd.org>
From: Justin C. Walker <justin@mac.com>
List: tech-net
Date: 05/14/2002 22:04:36
On Tuesday, May 14, 2002, at 07:36 PM, <sudog@sudog.com> wrote:

>
>> Just a thought: do you know that you are getting ENOBUFS from the
>> driver?  Most computers today can generate output way faster than the
>> devices can manage, so it's possible that you are getting the failure
>> from actually taking up all the mbufs in the system.  What does 
>> 'netstat
>> -m' show?  Don't forget that there really isn't any backpressure in 
>> this
>> case, either from UDP or from the driver (AFAIK).
>>
>> Does the socket layer even check with sowriteable() in the case of UDP?
>> Last I looked, data on datagram sockets went straight to the protocol
>> layer, bypassing all (socket) buffering completely.
>>
>> Regards,
>>
>> Justin
>
> I've got a gut feeling that the ENOBUFS is coming from here:
>
> netinet/udp_usrreq.c:882
>
>         M_PREPEND(m, sizeof(struct udpiphdr), M_DONTWAIT);
>         if (m == 0) {
>                 error = ENOBUFS;
>                 goto release;
>         }
>

I'd believe that it's either this code or something else in the path 
where data is copied in from user-mode.

> So, the question is, does select() or poll() make a check to ensure
> that ENOBUFS won't show up when udp_output() is called? Or should it?

Nope; select/poll don't have any way to determine this, since it isn't 
recorded as "state" anywhere.  UDP is "best effort" delivery, which 
means that if the packet can't be delivered for any reason, it gets 
dropped, and very little is done to note that fact.

> If not, how would I, in userland, cleanly wait until I can do another
> udp_output safely without ENOBUFS showing up?

As someone else mentioned, application-layer flow control is your 
answer.  Think about the underlying transport, and what it provides: 
some probability that a datagram you hand to the kernel with "send()" 
will get to the other end.  There's no guarantee about order or even 
eventual delivery.  If you are using UDP, your application is, in 
essence, accepting the fact thatdelivery of data is up to it, not the 
system.  You choose UDP because the terms are acceptable.  If you want 
to guarantee delivery, using UDP, you have to resort to timeouts and 
positive acknowledgements.  There's no other way that I know of.

> Uh oh.. It seems I am doomed to run a guessing heuristic on when I
> think sendto() will succeed or fail.. I just checked in the nfs
> drivers to see how they handle it.. when I saw this:

Yeah; this is what I'm talking about...

> nfs/nfs_socket.c:450: /* Comment validating the above */
[snip]
> Any pointers? I'd hate to have to write something unreliable to patch
> the kernel with.

It's up to your app.  "patching" the kernel would "break" UDP.

Regards,

Justin

--
Justin C. Walker, Curmudgeon-At-Large  *
Institute for General Semantics        | It's not whether you win or 
lose...
                                        |  It's whether *I* win or lose.
*--------------------------------------*-------------------------------*