Support O_NONBLOCK when opening #I chans
authorBarret Rhoden <brho@cs.berkeley.edu>
Sun, 19 Jul 2015 13:41:13 +0000 (09:41 -0400)
committerBarret Rhoden <brho@cs.berkeley.edu>
Fri, 24 Jul 2015 07:05:14 +0000 (03:05 -0400)
commit971232774dcefed89c09833c071cc517bb2c7443
tree406cb3a929cbaddb76c73029dc200eb58970471b
parent43277d8db24d9b913486f166b1ef307659f1a795
Support O_NONBLOCK when opening #I chans

Applications can set O_NONBLOCK when cloning and can getfl() to see
O_NONBLOCK, but they cannot toggle O_NONBLOCK via setfl().  Getting via
getfl() is of dubious value, since only the cloned chan will have the
mark.  To dynamically change, they need to use the ctl message.  I
wasn't interested in having device ops that translate a setfl() call or
something.  That's the purpose of the ctls, after all.

It's up to individual devices if they want to handle O_NONBLOCK when a
chan is opened.  They are free to throw an error() from dev->open.

Note that dial() currently does not support open flags to pass through
to the clone.  If you want to play around with this, you'll need send
the ctl message.

Also note that the non-blocking nature of the chan is part of the conv
(the queues in this case), and not really part of the chan.  If a
process writes a "nonblock" ctl message into the conv, all other chans
will now behave in a O_NONBLOCK-ing manner, since the underlying conv
changed.  This is different than the unix way of dealing with
O_NONBLOCK, where each socket/chan can be treated differently.

It's tempting to just remove the O_NONBLOCK completely and only allow a
ctl message, and fake O_NONBLOCK in userspace (e.g. in open() or the 9ns
networking shims in glibc), but that's at least one extra syscall for
every opened connection, both for clones and (in future commits) for
listen/accept4().

I strongly considered allowing O_NONBLOCK to set "nonblock on" for *any*
 #I file, specifically ctl and data files.  That way, you could just do:

open("/net/tcp/6/data", O_RDWR | O_NONBLOCK);

and then have non-blocking I/O.  It's what a user might expect from
open() with O_NONBLOCK.  The deciding point against this was that I
didn't want data to have any control side-effects, even on open().  This
is only really important if you drop a file in #s, and then someone who
is allowed to read/write to it could suddenly change the nonblocking
status.

Finally, note that these changes support non-blocking qio in #I,
specifically on conversation inbound and outbound queues.  Depending on
your medium, you could still block *below* the IP stack on a write().
For instance, ipoput4() calls the various bwrites, and eventually
etheroq().  It is possible to block there, unless the Ethernet device is
set to nonblocking.  Check out etherwrite() for specifics.
kern/include/ip.h
kern/include/ns.h
kern/src/net/devip.c
kern/src/ns/sysfile.c