Add header file for proper compilation
[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 "spinlock.h"
23 #include "dtls.h"
24 #include "slab.h"
25
26 /* The current dymamic tls implementation uses a locked linked list
27  * to find the key for a given thread. We should probably find a better way to
28  * do this based on a custom lock-free hash table or something. */
29 #include <sys/queue.h>
30 #include "spinlock.h"
31
32 /* The dynamic tls key structure */
33 struct dtls_key {
34   spinlock_t lock;
35   int ref_count;
36   bool valid;
37   void (*dtor)(void*);
38 };
39
40 /* The definition of a dtls_key list and its elements */
41 struct dtls_value {
42   TAILQ_ENTRY(dtls_value) link;
43   struct dtls_key *key;
44   void *dtls;
45 }; 
46 TAILQ_HEAD(dtls_list, dtls_value);
47
48 /* A struct containing all of the per thread (i.e. vcore or uthread) data
49  * associated with dtls */
50 typedef struct dtls_data {
51   /* A per-thread list of dtls regions */
52   struct dtls_list list;
53
54 } dtls_data_t;
55
56 /* A slab of dtls keys (global to all threads) */
57 static struct kmem_cache *__dtls_keys_cache;
58
59 /* A slab of values for use when mapping a dtls_key to its per-thread value */
60 struct kmem_cache *__dtls_values_cache;
61   
62 /* A lock protecting access to the caches above */
63 static spinlock_t __dtls_lock;
64
65 static __thread dtls_data_t __dtls_data;
66 static __thread bool __dtls_initialized = false;
67
68 static dtls_key_t __allocate_dtls_key() 
69 {
70   spinlock_lock(&__dtls_lock);
71   dtls_key_t key = kmem_cache_alloc(__dtls_keys_cache, 0);
72   assert(key);
73   key->ref_count = 1;
74   spinlock_unlock(&__dtls_lock);
75   return key;
76 }
77
78 static void __maybe_free_dtls_key(dtls_key_t key)
79 {
80   if(key->ref_count == 0) {
81     spinlock_lock(&__dtls_lock);
82     kmem_cache_free(__dtls_keys_cache, key);
83     spinlock_unlock(&__dtls_lock);
84   }
85 }
86
87 /* Constructor to get a reference to the main thread's TLS descriptor */
88 int dtls_lib_init()
89 {
90   /* Make sure this only runs once */
91   static bool initialized = false;
92   if (initialized)
93       return 0;
94   initialized = true;
95   
96   /* Initialize the global cache of dtls_keys */
97   __dtls_keys_cache = kmem_cache_create("dtls_keys_cache", 
98     sizeof(struct dtls_key), __alignof__(struct dtls_key), 0, NULL, NULL);
99   
100   __dtls_values_cache = kmem_cache_create("dtls_values_cache", 
101     sizeof(struct dtls_value), __alignof__(struct dtls_value), 0, NULL, NULL);
102   
103   /* Initialize the lock that protects the cache */
104   spinlock_init(&__dtls_lock);
105   return 0;
106 }
107
108 dtls_key_t dtls_key_create(dtls_dtor_t dtor)
109 {
110   dtls_lib_init();
111   dtls_key_t key = __allocate_dtls_key();
112   spinlock_init(&key->lock);
113   key->valid = true;
114   key->dtor = dtor;
115   return key;
116 }
117
118 void dtls_key_delete(dtls_key_t key)
119 {
120   assert(key);
121
122   spinlock_lock(&key->lock);
123   key->valid = false;
124   key->ref_count--;
125   spinlock_unlock(&key->lock);
126   __maybe_free_dtls_key(key);
127 }
128
129 static inline void __set_dtls(dtls_data_t *dtls_data, dtls_key_t key, void *dtls)
130 {
131   assert(key);
132
133   spinlock_lock(&key->lock);
134   key->ref_count++;
135   spinlock_unlock(&key->lock);
136
137   struct dtls_value *v = NULL;
138   TAILQ_FOREACH(v, &dtls_data->list, link)
139     if(v->key == key) break;
140
141   if(!v) {
142     spinlock_lock(&__dtls_lock);
143     v = kmem_cache_alloc(__dtls_values_cache, 0);
144     spinlock_unlock(&__dtls_lock);
145     assert(v);
146     v->key = key;
147     TAILQ_INSERT_HEAD(&dtls_data->list, v, link);
148   }
149   v->dtls = dtls;
150 }
151
152 static inline void *__get_dtls(dtls_data_t *dtls_data, dtls_key_t key)
153 {
154   assert(key);
155
156   struct dtls_value *v = NULL;
157   TAILQ_FOREACH(v, &dtls_data->list, link)
158     if(v->key == key) return v->dtls;
159   return v;
160 }
161
162 static inline void __destroy_dtls(dtls_data_t *dtls_data)
163 {
164  struct dtls_value *v,*n;
165   v = TAILQ_FIRST(&dtls_data->list);
166   while(v != NULL) {
167     dtls_key_t key = v->key;
168     bool run_dtor = false;
169   
170     spinlock_lock(&key->lock);
171     if(key->valid)
172       if(key->dtor)
173         run_dtor = true;
174     spinlock_unlock(&key->lock);
175
176         // MUST run the dtor outside the spinlock if we want it to be able to 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(run_dtor) {
185           void *dtls = v->dtls;
186       v->dtls = NULL;
187       key->dtor(dtls);
188     }
189
190     spinlock_lock(&key->lock);
191     key->ref_count--;
192     spinlock_unlock(&key->lock);
193     __maybe_free_dtls_key(key);
194
195     n = TAILQ_NEXT(v, link);
196     TAILQ_REMOVE(&dtls_data->list, v, link);
197     spinlock_lock(&__dtls_lock);
198     kmem_cache_free(__dtls_values_cache, v);
199     spinlock_unlock(&__dtls_lock);
200     v = n;
201   }
202 }
203
204 void set_dtls(dtls_key_t key, void *dtls)
205 {
206   bool initialized = true;
207   dtls_data_t *dtls_data = NULL;
208   if(!__dtls_initialized) {
209     initialized = false;
210     __dtls_initialized  = true;
211   }
212   dtls_data = &__dtls_data;
213   if(!initialized) {
214     TAILQ_INIT(&dtls_data->list);
215   }
216   __set_dtls(dtls_data, key, dtls);
217 }
218
219 void *get_dtls(dtls_key_t key)
220 {
221   dtls_data_t *dtls_data = NULL;
222   if(!__dtls_initialized)
223     return NULL;
224   dtls_data = &__dtls_data;
225   return __get_dtls(dtls_data, key);
226 }
227
228 void destroy_dtls()
229 {
230   dtls_data_t *dtls_data = NULL;
231   if(!__dtls_initialized)
232     return;
233   dtls_data = &__dtls_data;
234   __destroy_dtls(dtls_data);
235 }
236