lib/ovs-thread: Ensure that thread stacks are always at least 512 kB.
[cascardo/ovs.git] / lib / ovs-thread.c
index 4161095..b0e10ee 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2014 Nicira, Inc.
+ * Copyright (c) 2013, 2014, 2015 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -24,6 +24,7 @@
 #include <stdlib.h>
 #include <unistd.h>
 #include "compiler.h"
+#include "fatal-signal.h"
 #include "hash.h"
 #include "list.h"
 #include "netdev-dpdk.h"
@@ -118,7 +119,7 @@ TRY_LOCK_FUNCTION(rwlock, trywrlock);
         l->where = WHERE; \
         error = pthread_##TYPE##_##FUN(&l->lock); \
         if (OVS_UNLIKELY(error)) { \
-            ovs_abort(error, "pthread_%s_%sfailed", #TYPE, #FUN); \
+            ovs_abort(error, "pthread_%s_%s failed", #TYPE, #FUN); \
         } \
     }
 UNLOCK_FUNCTION(mutex, unlock, "<unlocked>");
@@ -331,12 +332,33 @@ ovsthread_wrapper(void *aux_)
 
     /* The order of the following calls is important, because
      * ovsrcu_quiesce_end() saves a copy of the thread name. */
-    set_subprogram_name("%s%u", aux.name, id);
+    char *subprogram_name = xasprintf("%s%u", aux.name, id);
+    set_subprogram_name(subprogram_name);
+    free(subprogram_name);
     ovsrcu_quiesce_end();
 
     return aux.start(aux.arg);
 }
 
+static void
+set_min_stack_size(pthread_attr_t *attr, size_t min_stacksize)
+{
+    size_t stacksize;
+    int error;
+
+    error = pthread_attr_getstacksize(attr, &stacksize);
+    if (error) {
+        ovs_abort(error, "pthread_attr_getstacksize failed");
+    }
+
+    if (stacksize < min_stacksize) {
+        error = pthread_attr_setstacksize(attr, min_stacksize);
+        if (error) {
+            ovs_abort(error, "pthread_attr_setstacksize failed");
+        }
+    }
+}
+
 /* Starts a thread that calls 'start(arg)'.  Sets the thread's name to 'name'
  * (suffixed by its ovsthread_id()).  Returns the new thread's pthread_t. */
 pthread_t
@@ -355,10 +377,20 @@ ovs_thread_create(const char *name, void *(*start)(void *), void *arg)
     aux->arg = arg;
     ovs_strlcpy(aux->name, name, sizeof aux->name);
 
+    /* Some small systems use a default stack size as small as 80 kB, but OVS
+     * requires approximately 384 kB according to the following analysis:
+     * http://openvswitch.org/pipermail/dev/2016-January/065049.html
+     *
+     * We use 512 kB to give us some margin of error. */
+    pthread_attr_t attr;
+    pthread_attr_init(&attr);
+    set_min_stack_size(&attr, 512 * 1024);
+
     error = pthread_create(&thread, NULL, ovsthread_wrapper, aux);
     if (error) {
         ovs_abort(error, "pthread_create failed");
     }
+    pthread_attr_destroy(&attr);
     return thread;
 }
 \f
@@ -581,6 +613,15 @@ count_cpu_cores(void)
 
     return n_cores > 0 ? n_cores : 0;
 }
+
+/* Returns 'true' if current thread is PMD thread. */
+bool
+thread_is_pmd(void)
+{
+    const char *name = get_subprogram_name();
+    return !strncmp(name, "pmd", 3);
+}
+
 \f
 /* ovsthread_key. */
 
@@ -662,12 +703,24 @@ ovsthread_key_destruct__(void *slots_)
     n = n_keys;
     ovs_mutex_unlock(&key_mutex);
 
-    for (i = 0; i < n / L2_SIZE; i++) {
+    for (i = 0; i < DIV_ROUND_UP(n, L2_SIZE); i++) {
         free(slots->p1[i]);
     }
     free(slots);
 }
 
+/* Cancels the callback to ovsthread_key_destruct__().
+ *
+ * Cancelling the call to the destructor during the main thread exit
+ * is needed while using pthreads-win32 library in Windows. It has been
+ * observed that in pthreads-win32, a call to the destructor during
+ * main thread exit causes undefined behavior. */
+static void
+ovsthread_cancel_ovsthread_key_destruct__(void *aux OVS_UNUSED)
+{
+    pthread_setspecific(tsd_key, NULL);
+}
+
 /* Initializes '*keyp' as a thread-specific data key.  The data items are
  * initially null in all threads.
  *
@@ -684,6 +737,8 @@ ovsthread_key_create(ovsthread_key_t *keyp, void (*destructor)(void *))
 
     if (ovsthread_once_start(&once)) {
         xpthread_key_create(&tsd_key, ovsthread_key_destruct__);
+        fatal_signal_add_hook(ovsthread_cancel_ovsthread_key_destruct__,
+                              NULL, NULL, true);
         ovsthread_once_done(&once);
     }