Initialize all vcores in SCP mode
[akaros.git] / user / parlib / dtls.c
1 /*
2  * Copyright (c) 2012 The Regents of the University of California
3  * Kevin Klues <klueska@cs.berkeley.edu>
4  *
5  * This file is part of Parlib.
6  *
7  * Parlib is free software: you can redistribute it and/or modify
8  * it under the terms of the Lesser GNU General Public License as published by
9  * the Free Software Foundation, either version 3 of the License, or
10  * (at your option) any later version.
11  * 
12  * Parlib is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * Lesser GNU General Public License for more details.
16  * 
17  * See COPYING.LESSER for details on the GNU Lesser General Public License.
18  * See COPYING for details on the GNU General Public License.
19  */
20
21 #include <stddef.h>
22 #include <parlib/assert.h>
23 #include <parlib/spinlock.h>
24 #include <parlib/dtls.h>
25 #include <parlib/slab.h>
26
27 /* The current dymamic tls implementation uses a locked linked list
28  * to find the key for a given thread. We should probably find a better way to
29  * do this based on a custom lock-free hash table or something. */
30 #include <sys/queue.h>
31 #include <parlib/spinlock.h>
32
33 /* Define some number of static keys, for which the memory containing the keys
34  * and the per-thread memory for the values associated with those keys is
35  * allocated statically. This is adapted from glibc's notion of the
36  * "specific_1stblock" field embedded directly into its pthread structure for
37  * pthread_get/specific() calls. */
38 #define NUM_STATIC_KEYS 32
39
40 /* The dynamic tls key structure */
41 struct dtls_key {
42   int id;
43   int ref_count;
44   bool valid;
45   void (*dtor)(void*);
46 };
47
48 /* The definition of a dtls_key list and its elements */
49 struct dtls_value {
50   TAILQ_ENTRY(dtls_value) link;
51   struct dtls_key *key;
52   void *dtls;
53 }; 
54 TAILQ_HEAD(dtls_list, dtls_value);
55
56 /* A struct containing all of the per thread (i.e. vcore or uthread) data
57  * associated with dtls */
58 typedef struct dtls_data {
59   /* A per-thread list of dtls regions */
60   struct dtls_list list;
61   /* Memory to hold dtls values for the first NUM_STATIC_KEYS keys */
62   struct dtls_value early_values[NUM_STATIC_KEYS];
63 } dtls_data_t;
64
65 /* A slab of dtls keys (global to all threads) */
66 static struct kmem_cache *__dtls_keys_cache;
67
68 /* A slab of values for use when mapping a dtls_key to its per-thread value */
69 struct kmem_cache *__dtls_values_cache;
70   
71 static __thread dtls_data_t __dtls_data;
72 static __thread bool __dtls_initialized = false;
73 static struct dtls_key static_dtls_keys[NUM_STATIC_KEYS];
74 static int num_dtls_keys = 0;
75
76 /* Initialize the slab caches for allocating dtls keys and values. */
77 int dtls_cache_init()
78 {
79   /* Make sure this only runs once */
80   static bool initialized = false;
81   if (initialized)
82       return 0;
83   initialized = true;
84
85   /* Initialize the global cache of dtls_keys */
86   __dtls_keys_cache = kmem_cache_create("dtls_keys_cache",
87     sizeof(struct dtls_key), __alignof__(struct dtls_key), 0, NULL, NULL);
88
89   /* Initialize the global cache of dtls_values */
90   __dtls_values_cache = kmem_cache_create("dtls_values_cache",
91     sizeof(struct dtls_value), __alignof__(struct dtls_value), 0, NULL, NULL);
92
93   return 0;
94 }
95
96 static dtls_key_t __allocate_dtls_key() 
97 {
98   dtls_key_t key;
99   int keyid = __sync_fetch_and_add(&num_dtls_keys, 1);
100   if (keyid < NUM_STATIC_KEYS) {
101     key = &static_dtls_keys[keyid];
102   } else {
103     dtls_cache_init();
104     key = kmem_cache_alloc(__dtls_keys_cache, 0);
105   }
106   assert(key);
107   key->id = keyid;
108   key->ref_count = 1;
109   return key;
110 }
111
112 static void __maybe_free_dtls_key(dtls_key_t key)
113 {
114   int ref_count = __sync_add_and_fetch(&key->ref_count, -1);
115   if (ref_count == 0 && key->id >= NUM_STATIC_KEYS)
116     kmem_cache_free(__dtls_keys_cache, key);
117 }
118
119 static struct dtls_value *__allocate_dtls_value(struct dtls_data *dtls_data,
120                                                 struct dtls_key *key)
121 {
122   struct dtls_value *v;
123   if (key->id < NUM_STATIC_KEYS) {
124     v = &dtls_data->early_values[key->id];
125   } else {
126     v = kmem_cache_alloc(__dtls_values_cache, 0);
127   }
128   assert(v);
129   return v;
130 }
131
132 static void __free_dtls_value(struct dtls_value *v)
133 {
134   if (v->key->id >= NUM_STATIC_KEYS)
135     kmem_cache_free(__dtls_values_cache, v);
136 }
137
138 dtls_key_t dtls_key_create(dtls_dtor_t dtor)
139 {
140   dtls_key_t key = __allocate_dtls_key();
141   key->valid = true;
142   key->dtor = dtor;
143   return key;
144 }
145
146 void dtls_key_delete(dtls_key_t key)
147 {
148   assert(key);
149   key->valid = false;
150   __maybe_free_dtls_key(key);
151 }
152
153 static inline void *__get_dtls(dtls_data_t *dtls_data, dtls_key_t key)
154 {
155   assert(key);
156
157   struct dtls_value *v;
158   if (key->id < NUM_STATIC_KEYS) {
159     v = &dtls_data->early_values[key->id];
160     if (v->key != NULL)
161       return v->dtls;
162   } else {
163     TAILQ_FOREACH(v, &dtls_data->list, link)
164       if (v->key == key)
165         return v->dtls;
166   }
167   return NULL;
168 }
169
170 static inline void __set_dtls(dtls_data_t *dtls_data, dtls_key_t key, void *dtls)
171 {
172   assert(key);
173   __sync_fetch_and_add(&key->ref_count, 1);
174
175   struct dtls_value *v = __get_dtls(dtls_data, key);
176   if (!v) {
177     v = __allocate_dtls_value(dtls_data, key);
178     v->key = key;
179     TAILQ_INSERT_HEAD(&dtls_data->list, v, link);
180   }
181   v->dtls = dtls;
182 }
183
184 static inline void __destroy_dtls(dtls_data_t *dtls_data)
185 {
186  struct dtls_value *v,*n;
187   v = TAILQ_FIRST(&dtls_data->list);
188   while(v != NULL) {
189     dtls_key_t key = v->key;
190   
191         // The dtor must be called outside of a spinlock so that it can call
192         // code that may deschedule it for a while (i.e. a mutex). Probably a
193         // good idea anyway since it can be arbitrarily long and is written by the
194         // user. Note, there is a small race here on the valid field, whereby we
195         // may run a destructor on an invalid key. At least the keys memory wont
196         // be deleted though, as protected by the ref count. Any reasonable usage
197         // of this interface should safeguard that a key is never destroyed before
198         // all of the threads that use it have exited anyway.
199     if (key->valid && key->dtor) {
200           void *dtls = v->dtls;
201       v->dtls = NULL;
202       key->dtor(dtls);
203     }
204     __maybe_free_dtls_key(key);
205
206     n = TAILQ_NEXT(v, link);
207     TAILQ_REMOVE(&dtls_data->list, v, link);
208     __free_dtls_value(v);
209     v = n;
210   }
211 }
212
213 void set_dtls(dtls_key_t key, void *dtls)
214 {
215   bool initialized = true;
216   dtls_data_t *dtls_data = NULL;
217   if(!__dtls_initialized) {
218     initialized = false;
219     __dtls_initialized  = true;
220   }
221   dtls_data = &__dtls_data;
222   if(!initialized) {
223     TAILQ_INIT(&dtls_data->list);
224   }
225   __set_dtls(dtls_data, key, dtls);
226 }
227
228 void *get_dtls(dtls_key_t key)
229 {
230   dtls_data_t *dtls_data = NULL;
231   if(!__dtls_initialized)
232     return NULL;
233   dtls_data = &__dtls_data;
234   return __get_dtls(dtls_data, key);
235 }
236
237 void destroy_dtls()
238 {
239   dtls_data_t *dtls_data = NULL;
240   if(!__dtls_initialized)
241     return;
242   dtls_data = &__dtls_data;
243   __destroy_dtls(dtls_data);
244 }
245