worker: Don't have worker abort when parent dies.
[sliver-openvswitch.git] / lib / worker.c
index bc44885..b0f1a82 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012 Nicira, Inc.
+/* Copyright (c) 2012, 2013 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 
 VLOG_DEFINE_THIS_MODULE(worker);
 
+/* ovs_assert() logs the assertion message and logging sometimes goes through a
+ * worker, so using ovs_assert() in this source file could cause recursion. */
+#undef ovs_assert
+#define ovs_assert use_assert_instead_of_ovs_assert_in_this_module
+
 /* Header for an RPC request. */
 struct worker_request {
     size_t request_len;              /* Length of the payload in bytes. */
@@ -198,6 +203,7 @@ worker_send_iovec(const struct iovec iovs[], size_t n_iovs,
     size_t sent = 0;
 
     for (;;) {
+        struct pollfd pfd;
         int error;
 
         /* Try to send the rest of the request. */
@@ -210,8 +216,21 @@ worker_send_iovec(const struct iovec iovs[], size_t n_iovs,
         /* Process replies to avoid deadlock. */
         worker_run();
 
-        poll_fd_wait(client_sock, POLLIN | POLLOUT);
-        poll_block();
+        /* Wait for 'client_sock' to become ready before trying again.  We
+         * can't use poll_block() because it sometimes calls into vlog, which
+         * calls indirectly into worker_send_iovec().  To be usable here,
+         * poll_block() would therefore need to be reentrant, but it isn't
+         * (calling it recursively causes memory corruption and an eventual
+         * crash). */
+        pfd.fd = client_sock;
+        pfd.events = POLLIN | POLLOUT;
+        do {
+            error = poll(&pfd, 1, -1) < 0 ? errno : 0;
+        } while (error == EINTR);
+        if (error) {
+            worker_broke();
+            VLOG_ABORT("poll failed (%s)", strerror(error));
+        }
     }
 }
 
@@ -223,11 +242,14 @@ worker_request_iovec(const struct iovec iovs[], size_t n_iovs,
                      worker_request_func *request_cb,
                      worker_reply_func *reply_cb, void *aux)
 {
+    static bool recursing = false;
     struct worker_request rq;
     struct iovec *all_iovs;
     int error;
 
     assert(worker_is_running());
+    assert(!recursing);
+    recursing = true;
 
     rq.request_len = iovec_len(iovs, n_iovs);
     rq.request_cb = request_cb;
@@ -241,6 +263,8 @@ worker_request_iovec(const struct iovec iovs[], size_t n_iovs,
         VLOG_ABORT("send failed (%s)", strerror(error));
     }
     free(all_iovs);
+
+    recursing = false;
 }
 
 /* Closes the client socket, if any, so that worker_is_running() will return
@@ -321,7 +345,7 @@ worker_reply_iovec(const struct iovec *iovs, size_t n_iovs,
          * to avoid missing log messages. */
         VLOG_INFO("send failed (%s)", strerror(error));
     } else if (error) {
-        VLOG_ABORT("send failed (%s)", strerror(error));
+        VLOG_FATAL("send failed (%s)", strerror(error));
     }
 
     free(all_iovs);
@@ -335,8 +359,7 @@ worker_main(int fd)
     server_sock = fd;
 
     subprogram_name = "worker";
-    proctitle_set("%s: worker process for pid %lu",
-                  program_name, (unsigned long int) getppid());
+    proctitle_set("worker process for pid %lu", (unsigned long int) getppid());
     VLOG_INFO("worker process started");
 
     rxbuf_init(&rx);
@@ -356,7 +379,7 @@ worker_main(int fd)
             /* Main process closed the IPC socket.  Exit cleanly. */
             break;
         } else if (error != EAGAIN) {
-            VLOG_ABORT("RPC receive failed (%s)", strerror(error));
+            VLOG_FATAL("RPC receive failed (%s)", strerror(error));
         }
 
         poll_fd_wait(server_sock, POLLIN);
@@ -429,8 +452,6 @@ rxbuf_run(struct rxbuf *rx, int sock, size_t header_len)
             }
         }
     }
-
-    return EAGAIN;
 }
 
 static struct iovec *