43,77 → 43,53 |
#include <panic.h> |
#include <print.h> |
|
SPINLOCK_INITIALIZE(rcu_global_lock); |
typedef struct { |
uint32_t current_batch; |
uint32_t completed_batch; |
bool next_batch_waiting; |
} rcu_global_t; |
|
typedef struct rcu_callback_list { |
struct rcu_callback_list* next; |
void (*func)(void*); |
void* data; |
bool* cpu_mask; |
} rcu_callback_list_t; |
|
typedef struct { |
#ifdef CONFIG_SMP |
bool* cpu_mask; |
#endif |
|
typedef struct { |
uint32_t current_batch_number; |
uint32_t QS_passed; |
bool QS_pending; |
rcu_callback_list_t* next_batch, *current_batch, *done_batch; |
} rcu_global_t; |
} rcu_percpu_t; |
|
rcu_global_t _rcu_global; |
rcu_percpu_t* _rcu_cpu_lists; |
|
rcu_global_t* _rcu_global; |
tasklet_descriptor_t* rcu_tasklet_desc; |
|
void rcu_init(void) |
{ |
#ifdef CONFIG_SMP |
int i; |
#endif |
|
_rcu_global = malloc(sizeof(rcu_global_t),0); |
_rcu_global->done_batch = NULL; |
_rcu_global->current_batch = NULL; |
_rcu_global->next_batch = NULL; |
spinlock_initialize(&rcu_global_lock, "rcu_global_lock"); |
|
rcu_tasklet_desc = tasklet_register(&rcu_tasklet, NULL); |
tasklet_disable(rcu_tasklet_desc); |
|
#ifdef CONFIG_SMP |
_rcu_global->cpu_mask = malloc (sizeof(bool)*config.cpu_count,0); |
for (i=0;i<config.cpu_count;i++) { |
_rcu_global->cpu_mask[i]=false; |
} |
#else |
tasklet_schedule(rcu_tasklet_desc); |
|
#endif |
tasklet_enable(rcu_tasklet_desc); |
_rcu_cpu_lists = malloc(sizeof(rcu_percpu_t)*config.cpu_count,0); |
_rcu_global.completed_batch = -1; |
_rcu_global.current_batch = -1; |
_rcu_global.next_batch_waiting = -1; |
} |
|
void rcu_synchronize(void) |
{ |
#ifdef CONFIG_SMP |
waitq_t *wq = malloc(sizeof(waitq_t),0); |
waitq_initialize(wq); |
rcu_sync_callback(&rcu_synchronize_callback_function, wq); |
printf("going to sleep\n"); |
waitq_sleep(wq); |
printf("woken up\n"); |
#endif |
waitq_t wq; |
waitq_initialize(&wq); |
rcu_sync_callback(rcu_synchronize_callback_function, &wq); |
waitq_sleep(&wq); |
} |
|
#ifdef CONFIG_SMP |
void rcu_synchronize_callback_function(void* waitq) |
{ |
printf("waking up\n"); |
waitq_wakeup(((waitq_t*)waitq), WAKEUP_ALL_INC_MISSED); |
waitq_wakeup(((waitq_t*)waitq), true); |
} |
#endif |
|
void rcu_sync_callback(void (*func)(void* data), void* data) |
{ |
#ifndef CONFIG_SMP |
func(data); |
#else |
int i; |
rcu_callback_list_t *rd; |
rd = malloc(sizeof(rcu_callback_list_t), 0); |
120,95 → 96,20 |
rd->func = func; |
rd->data = data; |
rd->next = NULL; |
printf("synccallback locking \n"); |
spinlock_lock(&rcu_global_lock); |
rd->next = _rcu_global->next_batch; |
_rcu_global->next_batch = rd; |
|
if (_rcu_global->current_batch == NULL) { |
_rcu_global->current_batch = _rcu_global->next_batch; |
_rcu_global->next_batch = NULL; |
printf("setting callback %x as current\n",&rd->func); |
for (i=0;i<config.cpu_count;i++) |
_rcu_global->cpu_mask[i]=false; |
rd->cpu_mask = malloc (sizeof(bool)*config.cpu_count,0); |
for (i=0;i<config.cpu_count;i++) |
rd->cpu_mask[i]=false; |
|
i = ++(_rcu_global.current_batch); |
_rcu_global.next_batch_waiting = true; |
|
//we've surely passed the quiescent point just by running this method |
rcu_passQS(); |
} |
rd->next = _rcu_cpu_lists[0].next_batch; |
for (i=0;i<config.cpu_count;i++) { |
tasklet_schedule_SMP(rcu_tasklet_desc, i); |
_rcu_cpu_lists[i].next_batch = rd; |
_rcu_cpu_lists[i].QS_pending = true; |
} |
spinlock_unlock(&rcu_global_lock); |
printf ("sync callback called,unlocking, state:%x \n",rcu_tasklet_desc->state); |
#endif |
} |
|
//TODO: polishing, comments |
|
void rcu_tasklet(void* data) |
{ |
rcu_callback_list_t* rd; |
bool passed_all_QS; |
#ifdef CONFIG_SMP |
int i; |
#endif |
rcu_passQS(); |
passed_all_QS = true; |
printf("tasklet locking \n"); |
spinlock_lock(&rcu_global_lock); |
#ifdef CONFIG_SMP |
|
for (i = 0; i < config.cpu_active; i++) |
passed_all_QS &= _rcu_global->cpu_mask[i]; |
#endif |
if (passed_all_QS) { |
if (_rcu_global->done_batch) { |
rd = _rcu_global->done_batch; |
while (rd->next) rd = rd->next; |
|
//append the current list to done list |
rd->next = _rcu_global->current_batch; |
} else |
_rcu_global->done_batch = _rcu_global->current_batch; |
printf("setting callback %x as done\n",&_rcu_global->current_batch->func); |
_rcu_global->current_batch = NULL; |
} |
|
_rcu_global->current_batch = _rcu_global->next_batch; |
_rcu_global->next_batch = NULL; |
|
if (_rcu_global->current_batch == NULL) { |
//there are no rcu callbacks registered, there is no need to monitor QS |
printf("tasklet idle disabling \n"); |
// tasklet_disable(rcu_tasklet_desc); |
spinlock_unlock(&rcu_global_lock); |
} else |
spinlock_unlock(&rcu_global_lock); |
printf("tasklet unlocking \n"); |
//TODO:tasklet, after_thread_ran, polishing |
} |
|
inline void rcu_passQS(void) |
{ |
#ifdef CONFIG_SMP |
_rcu_global->cpu_mask[CPU->id] = true; |
#endif |
} |
|
void rcu_run_callbacks(void) |
{ |
rcu_callback_list_t* rd; |
rcu_passQS(); |
if (_rcu_global->done_batch) { |
printf("."); |
spinlock_lock(&rcu_global_lock); |
for (rd = _rcu_global->done_batch; rd; rd=rd->next) { |
printf("calling %x \n",&rd->func); |
rd->func(&rd->data); |
} |
_rcu_global->done_batch = NULL; |
spinlock_unlock(&rcu_global_lock); |
printf(":"); |
} |
} |
|
|