00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 
00010 
00011 
00012 
00013 
00014 
00015 
00016 
00017 
00018 
00019 
00020 
00021 
00022 
00023 
00024 
00025 
00026 
00027 
00028 
00038 #include <synch/futex.h>
00039 #include <synch/rwlock.h>
00040 #include <synch/spinlock.h>
00041 #include <synch/synch.h>
00042 #include <mm/frame.h>
00043 #include <mm/page.h>
00044 #include <mm/slab.h>
00045 #include <proc/thread.h>
00046 #include <proc/task.h>
00047 #include <genarch/mm/page_pt.h>
00048 #include <genarch/mm/page_ht.h>
00049 #include <adt/hash_table.h>
00050 #include <adt/list.h>
00051 #include <arch.h>
00052 #include <align.h>
00053 #include <panic.h>
00054 #include <errno.h>
00055 #include <print.h>
00056 
00057 #define FUTEX_HT_SIZE   1024    
00058 
00059 static void futex_initialize(futex_t *futex);
00060 
00061 static futex_t *futex_find(__address paddr);
00062 static index_t futex_ht_hash(__native *key);
00063 static bool futex_ht_compare(__native *key, count_t keys, link_t *item);
00064 static void futex_ht_remove_callback(link_t *item);
00065 
00071 static rwlock_t futex_ht_lock;
00072 
00074 static hash_table_t futex_ht;
00075 
00077 static hash_table_operations_t futex_ht_ops = {
00078         .hash = futex_ht_hash,
00079         .compare = futex_ht_compare,
00080         .remove_callback = futex_ht_remove_callback
00081 };
00082 
00084 void futex_init(void)
00085 {
00086         rwlock_initialize(&futex_ht_lock);
00087         hash_table_create(&futex_ht, FUTEX_HT_SIZE, 1, &futex_ht_ops);
00088 }
00089 
00094 void futex_initialize(futex_t *futex)
00095 {
00096         waitq_initialize(&futex->wq);
00097         link_initialize(&futex->ht_link);
00098         futex->paddr = 0;
00099         futex->refcount = 1;
00100 }
00101 
00111 __native sys_futex_sleep_timeout(__address uaddr, __u32 usec, int flags)
00112 {
00113         futex_t *futex;
00114         __address paddr;
00115         pte_t *t;
00116         ipl_t ipl;
00117         
00118         ipl = interrupts_disable();
00119 
00120         
00121 
00122 
00123         page_table_lock(AS, true);
00124         t = page_mapping_find(AS, ALIGN_DOWN(uaddr, PAGE_SIZE));
00125         if (!t || !PTE_VALID(t) || !PTE_PRESENT(t)) {
00126                 page_table_unlock(AS, true);
00127                 interrupts_restore(ipl);
00128                 return (__native) ENOENT;
00129         }
00130         paddr = PTE_GET_FRAME(t) + (uaddr - ALIGN_DOWN(uaddr, PAGE_SIZE));
00131         page_table_unlock(AS, true);
00132         
00133         interrupts_restore(ipl);        
00134 
00135         futex = futex_find(paddr);
00136         
00137         return (__native) waitq_sleep_timeout(&futex->wq, usec, flags | SYNCH_FLAGS_INTERRUPTIBLE);
00138 }
00139 
00146 __native sys_futex_wakeup(__address uaddr)
00147 {
00148         futex_t *futex;
00149         __address paddr;
00150         pte_t *t;
00151         ipl_t ipl;
00152         
00153         ipl = interrupts_disable();
00154         
00155         
00156 
00157 
00158         page_table_lock(AS, true);
00159         t = page_mapping_find(AS, ALIGN_DOWN(uaddr, PAGE_SIZE));
00160         if (!t || !PTE_VALID(t) || !PTE_PRESENT(t)) {
00161                 page_table_unlock(AS, true);
00162                 interrupts_restore(ipl);
00163                 return (__native) ENOENT;
00164         }
00165         paddr = PTE_GET_FRAME(t) + (uaddr - ALIGN_DOWN(uaddr, PAGE_SIZE));
00166         page_table_unlock(AS, true);
00167         
00168         interrupts_restore(ipl);
00169 
00170         futex = futex_find(paddr);
00171                 
00172         waitq_wakeup(&futex->wq, WAKEUP_FIRST);
00173         
00174         return 0;
00175 }
00176 
00185 futex_t *futex_find(__address paddr)
00186 {
00187         link_t *item;
00188         futex_t *futex;
00189         btree_node_t *leaf;
00190         
00191         
00192 
00193 
00194 
00195         rwlock_read_lock(&futex_ht_lock);
00196         item = hash_table_find(&futex_ht, &paddr);
00197         if (item) {
00198                 futex = hash_table_get_instance(item, futex_t, ht_link);
00199 
00200                 
00201 
00202 
00203                 mutex_lock(&TASK->futexes_lock);
00204                 if (!btree_search(&TASK->futexes, paddr, &leaf)) {
00205                         
00206 
00207 
00208 
00209 
00210                         mutex_unlock(&TASK->futexes_lock);
00211                         goto gain_write_access;
00212                 }
00213                 mutex_unlock(&TASK->futexes_lock);
00214 
00215                 rwlock_read_unlock(&futex_ht_lock);
00216         } else {
00217 gain_write_access:
00218                 
00219 
00220 
00221 
00222 
00223                 rwlock_read_unlock(&futex_ht_lock);
00224 
00225                 rwlock_write_lock(&futex_ht_lock);
00226                 
00227 
00228 
00229 
00230                 item = hash_table_find(&futex_ht, &paddr);
00231                 if (item) {
00232                         futex = hash_table_get_instance(item, futex_t, ht_link);
00233                         
00234                         
00235 
00236 
00237                         mutex_lock(&TASK->futexes_lock);
00238                         if (!btree_search(&TASK->futexes, paddr, &leaf)) {
00239                                 
00240 
00241 
00242 
00243 
00244                                 futex->refcount++;
00245                                 btree_insert(&TASK->futexes, paddr, futex, leaf);
00246                         }
00247                         mutex_unlock(&TASK->futexes_lock);
00248         
00249                         rwlock_write_unlock(&futex_ht_lock);
00250                 } else {
00251                         futex = (futex_t *) malloc(sizeof(futex_t), 0);
00252                         futex_initialize(futex);
00253                         futex->paddr = paddr;
00254                         hash_table_insert(&futex_ht, &paddr, &futex->ht_link);
00255                         
00256                         
00257 
00258 
00259 
00260 
00261                         mutex_lock(&TASK->futexes_lock);
00262                         btree_insert(&TASK->futexes, paddr, futex, NULL);
00263                         mutex_unlock(&TASK->futexes_lock);
00264                         
00265                         rwlock_write_unlock(&futex_ht_lock);
00266                 }
00267         }
00268         
00269         return futex;
00270 }
00271 
00278 index_t futex_ht_hash(__native *key)
00279 {
00280         return *key & (FUTEX_HT_SIZE-1);
00281 }
00282 
00289 bool futex_ht_compare(__native *key, count_t keys, link_t *item)
00290 {
00291         futex_t *futex;
00292 
00293         ASSERT(keys == 1);
00294 
00295         futex = hash_table_get_instance(item, futex_t, ht_link);
00296         return *key == futex->paddr;
00297 }
00298 
00303 void futex_ht_remove_callback(link_t *item)
00304 {
00305         futex_t *futex;
00306 
00307         futex = hash_table_get_instance(item, futex_t, ht_link);
00308         free(futex);
00309 }
00310 
00312 void futex_cleanup(void)
00313 {
00314         link_t *cur;
00315         
00316         rwlock_write_lock(&futex_ht_lock);
00317         mutex_lock(&TASK->futexes_lock);
00318 
00319         for (cur = TASK->futexes.leaf_head.next; cur != &TASK->futexes.leaf_head; cur = cur->next) {
00320                 btree_node_t *node;
00321                 int i;
00322                 
00323                 node = list_get_instance(cur, btree_node_t, leaf_link);
00324                 for (i = 0; i < node->keys; i++) {
00325                         futex_t *ftx;
00326                         __address paddr = node->key[i];
00327                         
00328                         ftx = (futex_t *) node->value[i];
00329                         if (--ftx->refcount == 0)
00330                                 hash_table_remove(&futex_ht, &paddr, 1);
00331                 }
00332         }
00333         
00334         mutex_unlock(&TASK->futexes_lock);
00335         rwlock_write_unlock(&futex_ht_lock);
00336 }
00337