about summary refs log tree commit homepage
diff options
context:
space:
mode:
authorEric Wong <e@yhbt.net>2013-07-31 20:26:25 +0000
committerEric Wong <normalperson@yhbt.net>2013-08-22 19:28:12 +0000
commit1199492dd1adb394cf4cc0d599e7f77c52ccbdbf (patch)
tree4288bc3f99854c0ca8839d4c2433cd6dda410aaf
parent317b979e29774a77fb933c4f42514ff007669b39 (diff)
downloadcmogstored-1199492dd1adb394cf4cc0d599e7f77c52ccbdbf.tar.gz
While we're fortunate enough to not have encountered a case
where send/writev returns zero with a non-zero-length buffer,
it's not inconceivable that it could strike us one day.  In that
case, error out the connection instead of infinite looping.

Dropping a connection is safer than letting a thread run in
an infinite loop.
-rw-r--r--trywrite.c34
1 files changed, 23 insertions, 11 deletions
diff --git a/trywrite.c b/trywrite.c
index 7078748..1325e8c 100644
--- a/trywrite.c
+++ b/trywrite.c
@@ -35,6 +35,11 @@ static void * wbuf_new(void *buf, size_t len)
         return wbuf_newv(len, &iov, 1);
 }
 
+MOG_NOINLINE static void sysbug(const char *fn, ssize_t bytes)
+{
+        syslog(LOG_ERR, "%s returned %zd bytes written but: %m", fn, bytes);
+}
+
 enum mog_write_state mog_tryflush(int fd, struct mog_wbuf **x)
 {
         struct mog_wbuf *wbuf = *x;
@@ -90,12 +95,13 @@ retry:
 
         if (w == len) {
                 return NULL;
-        } else if (w < 0) {
+        } else if (w <= 0) {
                 switch (errno) {
                 case_EAGAIN:
                         TRACE(CMOGSTORED_WRITE_BUFFERED());
                         return wbuf_newv(len, iov, iovcnt);
                 case EINTR: goto retry;
+                case 0: sysbug("writev", w);
                 }
                 return MOG_WR_ERROR;
         } else {
@@ -146,19 +152,25 @@ void * mog_trysend(int fd, void *buf, size_t len, off_t more)
 
                         if (w == (ssize_t)len)
                                 return NULL; /* all done */
-
-                        if (w < 0) {
-                                switch (errno) {
-                                case_EAGAIN:
-                                        TRACE(CMOGSTORED_WRITE_BUFFERED());
-                                        return wbuf_new(buf, len);
-                                case EINTR: continue;
-                                }
-                                return MOG_WR_ERROR;
-                        } else {
+                        if (w > 0) {
                                 buf = (char *)buf + w;
                                 len -= w;
+                                continue;
+                        }
+
+                        /*
+                         * we bail on w == 0, too.  send should normally
+                         * return zero, but in case there's a kernel bug
+                         * we should not infinite loop
+                         */
+                        switch (errno) {
+                        case_EAGAIN:
+                                TRACE(CMOGSTORED_WRITE_BUFFERED());
+                                return wbuf_new(buf, len);
+                        case EINTR: continue;
+                        case 0: sysbug("send", w);
                         }
+                        return MOG_WR_ERROR;
                 }
 
                 return NULL;