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