Add static storage for bounded # of dtls keys/vals
authorKevin Klues <klueska@cs.berkeley.edu>
Tue, 25 Aug 2015 01:18:27 +0000 (18:18 -0700)
committerBarret Rhoden <brho@cs.berkeley.edu>
Wed, 2 Sep 2015 17:50:05 +0000 (13:50 -0400)
Storage for up to NUM_STATIC_KEYS is now statically allocated for dtls
keys. Likewise, per-thread storage for the values associated with these
keys is allocated as part of a thread's static TLS data (in an array
called 'early_values').  By statically allocating this data, we avoid
having to dynamically allocate memory for a small number of DTLS keys
and their associated values.  Moreover, we have a faster lookup time for
these key/value pairs because we know exactly where to look for their
values by indexing into an array instead of searching through a linked
list.

Realistically, dtls keys will likely go unused in most apps today, in
favor of compiler supported TLS via __thread. For this reason, we chose
to only optimize for a small number of keys since it's likely that we
won't every go beyond this value in a real application.  If we do, we
can revisit this implementation and do something more sophisticated
along the lines of what glibc does in its pthread_key_create() and
pthread_get/setspecific() calls (i.e. use a 2-level array structure with
a finite bound on the number of keys).  For now, what we have is
sufficient for the apps we've encountered.

Overall, it may seem weird to store the dtls early_values array inside
of a __thread variable (since the whole point of dtls is to enable
dynamically allocated TLS when compiler supported TLS is unavailable),
but we mostly only provide dtls for backwards compatibility and plan to
always enable compiler supported TLS for apps that require TLS of any
sort.

user/parlib/dtls.c

index 7357605..9edce00 100644 (file)
 #include <sys/queue.h>
 #include <parlib/spinlock.h>
 
+/* Define some number of static keys, for which the memory containing the keys
+ * and the per-thread memory for the values associated with those keys is
+ * allocated statically. This is adapted from glibc's notion of the
+ * "specific_1stblock" field embedded directly into its pthread structure for
+ * pthread_get/specific() calls. */
+#define NUM_STATIC_KEYS 32
+
 /* The dynamic tls key structure */
 struct dtls_key {
+  int id;
   int ref_count;
   bool valid;
   void (*dtor)(void*);
@@ -50,7 +58,8 @@ TAILQ_HEAD(dtls_list, dtls_value);
 typedef struct dtls_data {
   /* A per-thread list of dtls regions */
   struct dtls_list list;
-
+  /* Memory to hold dtls values for the first NUM_STATIC_KEYS keys */
+  struct dtls_value early_values[NUM_STATIC_KEYS];
 } dtls_data_t;
 
 /* A slab of dtls keys (global to all threads) */
@@ -61,6 +70,8 @@ struct kmem_cache *__dtls_values_cache;
   
 static __thread dtls_data_t __dtls_data;
 static __thread bool __dtls_initialized = false;
+static struct dtls_key static_dtls_keys[NUM_STATIC_KEYS];
+static int num_dtls_keys = 0;
 
 /* Initialize the slab caches for allocating dtls keys and values. */
 int dtls_cache_init()
@@ -84,9 +95,16 @@ int dtls_cache_init()
 
 static dtls_key_t __allocate_dtls_key() 
 {
-  dtls_cache_init();
-  dtls_key_t key = kmem_cache_alloc(__dtls_keys_cache, 0);
+  dtls_key_t key;
+  int keyid = __sync_fetch_and_add(&num_dtls_keys, 1);
+  if (keyid < NUM_STATIC_KEYS) {
+    key = &static_dtls_keys[keyid];
+  } else {
+    dtls_cache_init();
+    key = kmem_cache_alloc(__dtls_keys_cache, 0);
+  }
   assert(key);
+  key->id = keyid;
   key->ref_count = 1;
   return key;
 }
@@ -94,21 +112,27 @@ static dtls_key_t __allocate_dtls_key()
 static void __maybe_free_dtls_key(dtls_key_t key)
 {
   int ref_count = __sync_add_and_fetch(&key->ref_count, -1);
-  if (ref_count == 0)
+  if (ref_count == 0 && key->id >= NUM_STATIC_KEYS)
     kmem_cache_free(__dtls_keys_cache, key);
 }
 
-static struct dtls_value *__allocate_dtls_value()
+static struct dtls_value *__allocate_dtls_value(struct dtls_data *dtls_data,
+                                                struct dtls_key *key)
 {
   struct dtls_value *v;
-  v = kmem_cache_alloc(__dtls_values_cache, 0);
+  if (key->id < NUM_STATIC_KEYS) {
+    v = &dtls_data->early_values[key->id];
+  } else {
+    v = kmem_cache_alloc(__dtls_values_cache, 0);
+  }
   assert(v);
   return v;
 }
 
 static void __free_dtls_value(struct dtls_value *v)
 {
-  kmem_cache_free(__dtls_values_cache, v);
+  if (v->key->id >= NUM_STATIC_KEYS)
+    kmem_cache_free(__dtls_values_cache, v);
 }
 
 dtls_key_t dtls_key_create(dtls_dtor_t dtor)
@@ -130,10 +154,17 @@ static inline void *__get_dtls(dtls_data_t *dtls_data, dtls_key_t key)
 {
   assert(key);
 
-  struct dtls_value *v = NULL;
-  TAILQ_FOREACH(v, &dtls_data->list, link)
-    if(v->key == key) return v->dtls;
-  return v;
+  struct dtls_value *v;
+  if (key->id < NUM_STATIC_KEYS) {
+    v = &dtls_data->early_values[key->id];
+    if (v->key != NULL)
+      return v->dtls;
+  } else {
+    TAILQ_FOREACH(v, &dtls_data->list, link)
+      if (v->key == key)
+        return v->dtls;
+  }
+  return NULL;
 }
 
 static inline void __set_dtls(dtls_data_t *dtls_data, dtls_key_t key, void *dtls)
@@ -143,7 +174,7 @@ static inline void __set_dtls(dtls_data_t *dtls_data, dtls_key_t key, void *dtls
 
   struct dtls_value *v = __get_dtls(dtls_data, key);
   if (!v) {
-    v = __allocate_dtls_value();
+    v = __allocate_dtls_value(dtls_data, key);
     v->key = key;
     TAILQ_INSERT_HEAD(&dtls_data->list, v, link);
   }