qio: Report partial progress for NONBLOCK queues
authorBarret Rhoden <brho@cs.berkeley.edu>
Thu, 25 May 2017 20:10:45 +0000 (16:10 -0400)
committerBarret Rhoden <brho@cs.berkeley.edu>
Fri, 26 May 2017 16:22:40 +0000 (12:22 -0400)
__qbwrite() can throw for many reasons, including EPIPE (closed queue),
EAGAIN (full for now), and EINTR (syscall aborted).  However, large
qwrites, meaning greater than Maxatomic, get broken into several block
writes.  Some could succeed before we catch the error, and we need to
report those successes as a partial write.

To make things a little trickier, __qwrite() is only allowed to throw based
on its qio flags.  Other times waserror() might be dangerous, such as in
irq context.

This came up by writing large blocks to a non-blocking pipe.  The writer
thought that none of the write made it, so it kept writing the same blocks
over and over.  However, each time the first block (at least) was actually
written.  The reader received that first block over and over.

Signed-off-by: Barret Rhoden <brho@cs.berkeley.edu>
kern/src/ns/qio.c

index 5f34c32..f5b5430 100644 (file)
@@ -1617,12 +1617,22 @@ static struct block *build_block(void *from, size_t len, int mem_flags)
 static ssize_t __qwrite(struct queue *q, void *vp, size_t len, int mem_flags,
                         int qio_flags)
 {
-       size_t n, sofar;
+       ERRSTACK(1);
+       size_t n;
+       volatile size_t sofar = 0;      /* volatile for the waserror */
        struct block *b;
        uint8_t *p = vp;
        void *ext_buf;
 
-       sofar = 0;
+       /* Only some callers can throw.  Others might be in a context where waserror
+        * isn't safe. */
+       if ((qio_flags & QIO_CAN_ERR_SLEEP) && waserror()) {
+               /* Any error (EAGAIN for nonblock, syscall aborted, even EPIPE) after
+                * some data has been sent should be treated as a partial write. */
+               if (sofar)
+                       goto out_ok;
+               nexterror();
+       }
        do {
                n = len - sofar;
                /* This is 64K, the max amount per single block.  Still a good value? */
@@ -1635,6 +1645,9 @@ static ssize_t __qwrite(struct queue *q, void *vp, size_t len, int mem_flags,
                        break;
                sofar += n;
        } while ((sofar < len) && (q->state & Qmsg) == 0);
+out_ok:
+       if (qio_flags & QIO_CAN_ERR_SLEEP)
+               poperror();
        return sofar;
 }