0,0 → 1,241 |
/* |
* Copyright (c) 2007 Jan Hudecek |
* Copyright (c) 2006 Jakub Jermar |
* All rights reserved. |
* |
* Redistribution and use in source and binary forms, with or without |
* modification, are permitted provided that the following conditions |
* are met: |
* |
* - Redistributions of source code must retain the above copyright |
* notice, this list of conditions and the following disclaimer. |
* - Redistributions in binary form must reproduce the above copyright |
* notice, this list of conditions and the following disclaimer in the |
* documentation and/or other materials provided with the distribution. |
* - The name of the author may not be used to endorse or promote products |
* derived from this software without specific prior written permission. |
* |
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
*/ |
|
/** @addtogroup genericddi |
* @{ |
*/ |
/** @file |
*/ |
|
#include <arch.h> |
#include <config.h> |
#include <arch/types.h> |
#include <arch/asm.h> |
#include <proc/tasklet.h> |
#include <synch/spinlock.h> |
#include <time/delay.h> |
#include <proc/thread.h> |
#include <panic.h> |
#include <print.h> |
#include <synch/waitq.h> |
#include <cpu.h> |
#include <proc/scheduler.h> |
|
/** Timeout length in the tasklet thread */ |
#define TASKLET_THREAD_SLEEPDELAY 10000 |
///** Priority of the tasklet thread */ |
#define TASKLET_THREAD_PRIORITY 1 |
|
|
/** Tasklet state enum */ |
typedef enum { |
TASKLET_STATE_NOTACTIVE = 0, |
TASKLET_STATE_SCHEDULED = 1, |
TASKLET_STATE_RUNNING = 2, |
TASKLET_STATE_DISABLED = 4 |
} tasklet_state_enum; |
|
/** Spinlock protecting list of tasklets */ |
SPINLOCK_INITIALIZE(tasklet_lock); |
|
/** array of tasklet lists for every CPU */ |
tasklet_descriptor_t** tasklet_list; |
|
/** Initializes tasklets - except for the tasklet thread */ |
void tasklet_init(void) |
{ |
int i; |
tasklet_list=malloc(sizeof(tasklet_descriptor_t*)*config.cpu_count,0); |
if (tasklet_list==0) |
panic_printf("tasklet list mem not allocated\n"); |
for(i=0;i<config.cpu_count;i++) { |
tasklet_list[i]=0; |
} |
spinlock_initialize(&tasklet_lock, "tasklet_lock"); |
|
} |
|
/** |
* Creates and runs the tasklet thread |
* |
* @param kernel_task Pointer to the kernel task - for create_thread |
*/ |
void tasklet_run_tasklet_thread(task_t * kernel_task) |
{ |
//create the tasklet_thread, it's wired to the current cpu, we'll migrate it ourselves |
thread_t* t= thread_create(&tasklet_thread, NULL, kernel_task, THREAD_FLAG_WIRED, "tasklet_thread", false); |
if (t==NULL) { |
//wtf? |
panic_printf("tasklet thread not created\n"); |
} else { |
spinlock_lock(&t->lock); |
t->cpu = &cpus[0]; |
t->priority = TASKLET_THREAD_PRIORITY; |
spinlock_unlock(&t->lock); |
thread_ready(t); |
} |
} |
|
/** Thread which keeps executing scheduled enabled tasklets |
* @param data not used |
*/ |
void tasklet_thread(void* data) |
{ |
#ifdef CONFIG_SMP |
int current_cpu, new_cpu ; |
cpu_t *cpu; |
#endif |
waitq_t wq; |
waitq_initialize(&wq); |
|
while (true) { |
tasklet_do(); |
#ifdef CONFIG_SMP |
if (config.cpu_active>1) { |
current_cpu = CPU->id; |
//find the first cpu with nonempty tasklet_list |
for (new_cpu = (current_cpu + 1) % config.cpu_active; new_cpu!=current_cpu && tasklet_list[new_cpu]==0; |
new_cpu=(new_cpu + 1)% config.cpu_active); |
|
if (new_cpu!=current_cpu) { |
//we need to migrate this thread to CPU with id new_cpu |
cpu = &cpus[new_cpu]; |
|
spinlock_lock(&THREAD->lock); |
//put tasklet_thread on the new_cpu |
THREAD->cpu = cpu; |
spinlock_unlock(&THREAD->lock); |
} |
} |
#endif |
waitq_sleep_timeout(&wq, (uint32_t)TASKLET_THREAD_SLEEPDELAY, 0); |
|
|
} |
} |
|
/** Initializes tasklet structure |
* @param func tasklet callback function to be called |
* @param data pointer to be passed to the tasklet function |
*/ |
tasklet_descriptor_t* tasklet_register(void (*func)(void* data), void* data) |
{ |
tasklet_descriptor_t* tasklet=malloc(sizeof(tasklet_descriptor_t),0); |
tasklet->data = data; |
tasklet->func = func; |
tasklet->state = TASKLET_STATE_NOTACTIVE; |
tasklet->next = 0; |
return tasklet; |
} |
|
/** Schedules the tasklet for execution on current CPU |
* @param t tasklet to be scheduled |
*/ |
void tasklet_schedule(tasklet_descriptor_t* t) |
{ |
spinlock_lock(&tasklet_lock); |
//clear notactive, running and scheduled flags |
t->state &= TASKLET_STATE_DISABLED; |
//set the scheduled flag |
t->state |= TASKLET_STATE_SCHEDULED; |
t->next=tasklet_list[CPU->id]; |
tasklet_list[CPU->id]=t; |
spinlock_unlock(&tasklet_lock); |
} |
|
/** Tasklet will not be run, even if scheduled |
* @param t tasklet to be disabled |
*/ |
void tasklet_disable(tasklet_descriptor_t* t) |
{ |
spinlock_lock(&tasklet_lock); |
//set the disabled flag |
t->state |= TASKLET_STATE_DISABLED; |
spinlock_unlock(&tasklet_lock); |
} |
|
/** Tasklet will be run if scheduled |
* @param t tasklet to be enabled |
*/ |
void tasklet_enable(tasklet_descriptor_t* t) |
{ |
spinlock_lock(&tasklet_lock); |
//clear the disabled flag |
t->state &= ~TASKLET_STATE_DISABLED; |
spinlock_unlock(&tasklet_lock); |
} |
|
|
|
|
/** Executes scheduled enabled tasklets on current CPU */ |
void tasklet_do(void) |
{ |
spinlock_lock(&tasklet_lock); |
tasklet_descriptor_t* t = tasklet_list[CPU->id]; |
//printf("."); |
if (t) { |
//empty the tasklet_list |
tasklet_list[CPU->id]=0; |
do { |
if (!(t->state & TASKLET_STATE_DISABLED)) { |
if (t->func) { |
t->state = TASKLET_STATE_RUNNING; |
t->func(t->data); |
t->state = TASKLET_STATE_NOTACTIVE; |
} else |
panic_printf("tasklet func NULL\n"); |
} else { |
//return it back to the queue of scheduled tasklets |
t->next = tasklet_list[CPU->id]; |
tasklet_list[CPU->id] = t; |
} |
t=t->next; |
} |
while (t); |
} |
spinlock_unlock(&tasklet_lock); |
} |
|
/** Frees the tasklet structure when no longer needed. The function doesn't provide |
* any synchronization, the caller must be sure, the tasklet is not scheduled. |
* |
* @param tasklet to be freed |
*/ |
void tasklet_free(tasklet_descriptor_t* t) |
{ |
spinlock_lock(&tasklet_lock); |
if (t->state == TASKLET_STATE_NOTACTIVE) |
free(t); |
else |
panic_printf("attempting to free an active tasklet"); |
spinlock_unlock(&tasklet_lock); |
} |
|
|