Subversion Repositories HelenOS

Rev

Rev 2330 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2296 hudecek 1
/*
2
 * Copyright (c) 2007 Jan Hudecek
3
 * Copyright (c) 2006 Jakub Jermar
4
 * All rights reserved.
5
 *
6
 * Redistribution and use in source and binary forms, with or without
7
 * modification, are permitted provided that the following conditions
8
 * are met:
9
 *
10
 * - Redistributions of source code must retain the above copyright
11
 *   notice, this list of conditions and the following disclaimer.
12
 * - Redistributions in binary form must reproduce the above copyright
13
 *   notice, this list of conditions and the following disclaimer in the
14
 *   documentation and/or other materials provided with the distribution.
15
 * - The name of the author may not be used to endorse or promote products
16
 *   derived from this software without specific prior written permission.
17
 *
18
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
 */
29
 
30
/** @addtogroup genericddi
31
 * @{
32
 */
2430 hudecek 33
/** @file tasklet.c
34
 *  @brief Tasklet implementation
2296 hudecek 35
 */
36
 
37
#include <arch.h>
38
#include <config.h>
39
#include <arch/types.h>
40
#include <arch/asm.h>
41
#include <proc/tasklet.h>
42
#include <synch/spinlock.h>
43
#include <time/delay.h>
44
#include <proc/thread.h>
45
#include <panic.h>
46
#include <print.h>
47
#include <synch/waitq.h>
48
#include <cpu.h>
49
#include <proc/scheduler.h>
50
 
51
/** Timeout length in the tasklet thread */
52
#define TASKLET_THREAD_SLEEPDELAY 10000
53
///** Priority of the tasklet thread */
54
#define TASKLET_THREAD_PRIORITY 1
55
 
56
 
57
/** Tasklet state enum */
58
typedef enum  {
59
TASKLET_STATE_NOTACTIVE = 0,
60
TASKLET_STATE_SCHEDULED = 1,
61
TASKLET_STATE_RUNNING = 2,
62
TASKLET_STATE_DISABLED = 4
63
} tasklet_state_enum;
64
 
65
/** Spinlock protecting list of tasklets */
66
SPINLOCK_INITIALIZE(tasklet_lock);
67
 
68
/** array of tasklet lists for every CPU */
69
tasklet_descriptor_t** tasklet_list;
70
 
71
/** Initializes tasklets - except for the tasklet thread */
72
void tasklet_init(void)
73
{
74
    int i;
75
    tasklet_list=malloc(sizeof(tasklet_descriptor_t*)*config.cpu_count,0);
76
    if (tasklet_list==0)
77
        panic_printf("tasklet list mem not allocated\n");
78
    for(i=0;i<config.cpu_count;i++) {
79
        tasklet_list[i]=0;
80
    }
81
    spinlock_initialize(&tasklet_lock, "tasklet_lock");
82
 
83
}
84
 
85
/**
86
* Creates and runs the tasklet thread
87
*
88
*  @param kernel_task Pointer to the kernel task - for create_thread
89
*/
90
void tasklet_run_tasklet_thread(task_t * kernel_task)
91
{
92
    //create the tasklet_thread, it's wired to the current cpu, we'll migrate it ourselves
93
    thread_t* t= thread_create(&tasklet_thread, NULL, kernel_task, THREAD_FLAG_WIRED, "tasklet_thread", false);
94
    if (t==NULL) {
95
        panic_printf("tasklet thread not created\n");
96
    } else {
2430 hudecek 97
        spinlock_lock(&t->lock);   
98
        //we'll default on the first CPU
2296 hudecek 99
        t->cpu = &cpus[0];
100
        t->priority = TASKLET_THREAD_PRIORITY;
101
        spinlock_unlock(&t->lock);
102
        thread_ready(t);
103
    }
104
}
105
 
106
/** Thread which keeps executing scheduled enabled tasklets
107
* @param data not used
108
*/
109
void tasklet_thread(void* data)
110
{
111
#ifdef CONFIG_SMP
112
    int current_cpu, new_cpu ;
113
    cpu_t *cpu;
114
#endif
115
    waitq_t  wq;
116
    waitq_initialize(&wq);
117
 
2430 hudecek 118
    //the infinite loop
2296 hudecek 119
    while (true) {
2430 hudecek 120
        //execute any scheduled tasklets
2296 hudecek 121
        tasklet_do();
122
#ifdef CONFIG_SMP
2430 hudecek 123
        //check whether other CPUs have tasklets to execute
2296 hudecek 124
        if (config.cpu_active>1) {
125
            current_cpu = CPU->id;
126
            //find the first cpu with nonempty tasklet_list 
2430 hudecek 127
            for (new_cpu = (current_cpu + 1) % config.cpu_count; new_cpu!=current_cpu && tasklet_list[new_cpu]==0 ;
128
                    new_cpu=(new_cpu + 1)% config.cpu_count);
2296 hudecek 129
 
2430 hudecek 130
            //if we found a CPU with unsatisfied tasklet schedule to run there. It must be active!
131
            if (new_cpu!=current_cpu && cpus[new_cpu].active) {
2296 hudecek 132
                //we need to migrate this thread to CPU with id new_cpu
133
                cpu = &cpus[new_cpu];
134
 
135
                spinlock_lock(&THREAD->lock);
2430 hudecek 136
                //move tasklet_thread on the new_cpu 
2296 hudecek 137
                THREAD->cpu = cpu;
138
                spinlock_unlock(&THREAD->lock);
139
            }
140
        }
141
#endif
142
        waitq_sleep_timeout(&wq, (uint32_t)TASKLET_THREAD_SLEEPDELAY, 0);
143
 
144
 
145
    }
146
}
147
 
148
/** Initializes tasklet structure
149
* @param func tasklet callback function to be called
150
* @param data pointer to be passed to the tasklet function
151
*/
152
tasklet_descriptor_t* tasklet_register(void (*func)(void* data), void* data)
153
{
154
    tasklet_descriptor_t* tasklet=malloc(sizeof(tasklet_descriptor_t),0);
155
    tasklet->data = data;
156
    tasklet->func = func;
157
    tasklet->state = TASKLET_STATE_NOTACTIVE;
158
    tasklet->next = 0;
159
    return tasklet;
160
}
161
 
162
/** Schedules the tasklet for execution on current CPU
163
* @param t tasklet to be scheduled
164
*/
165
void tasklet_schedule(tasklet_descriptor_t* t)
166
{
2309 hudecek 167
    tasklet_schedule_SMP(t, CPU->id);
168
}
169
 
170
/** Schedules the tasklet for execution on id CPU
171
* @param t tasklet to be scheduled
172
* @param id CPU id on which the tasklet will be scheduled
173
*/
174
void tasklet_schedule_SMP(tasklet_descriptor_t* t, uint32_t id)
175
{
2296 hudecek 176
    spinlock_lock(&tasklet_lock);
177
    //clear notactive, running and scheduled flags
178
    t->state &= TASKLET_STATE_DISABLED;
179
    //set the scheduled flag
180
    t->state |= TASKLET_STATE_SCHEDULED;
2309 hudecek 181
    t->next=tasklet_list[id];
182
    tasklet_list[id]=t;
2296 hudecek 183
    spinlock_unlock(&tasklet_lock);
184
}
185
 
186
/** Tasklet will not be run, even if scheduled
187
* @param t tasklet to be disabled
188
*/
189
void tasklet_disable(tasklet_descriptor_t* t)
190
{
191
    spinlock_lock(&tasklet_lock);
192
    //set the disabled flag
193
    t->state |= TASKLET_STATE_DISABLED;
194
    spinlock_unlock(&tasklet_lock);
195
}
196
 
197
/** Tasklet will be run if scheduled
198
* @param t tasklet to be enabled
199
*/
200
void tasklet_enable(tasklet_descriptor_t* t)
201
{
202
    spinlock_lock(&tasklet_lock);
203
    //clear the disabled flag
204
    t->state &= ~TASKLET_STATE_DISABLED;
205
    spinlock_unlock(&tasklet_lock);
206
}
207
 
208
 
209
 
210
 
2430 hudecek 211
/** Executes scheduled enabled tasklets on current CPU
212
* this function could be called from other parts of kernel */
2296 hudecek 213
void tasklet_do(void)
214
{
215
    spinlock_lock(&tasklet_lock);
216
    tasklet_descriptor_t* t = tasklet_list[CPU->id];
217
    if (t) {
218
        //empty the tasklet_list
219
        tasklet_list[CPU->id]=0;
2309 hudecek 220
        spinlock_unlock(&tasklet_lock);
2296 hudecek 221
        do {
222
            if (!(t->state & TASKLET_STATE_DISABLED)) {
223
                if (t->func) {
224
                    t->state = TASKLET_STATE_RUNNING;
225
                    t->func(t->data);
2430 hudecek 226
                    //clear running flag, set not active 
2309 hudecek 227
                    t->state &= ~TASKLET_STATE_RUNNING;
228
                    t->state |= TASKLET_STATE_NOTACTIVE;
2296 hudecek 229
                } else
230
                    panic_printf("tasklet func NULL\n");
231
            } else {  
232
                //return it back to the queue of scheduled tasklets
2309 hudecek 233
                spinlock_lock(&tasklet_lock);  
2296 hudecek 234
                t->next = tasklet_list[CPU->id];
235
                tasklet_list[CPU->id] = t;
2309 hudecek 236
                spinlock_unlock(&tasklet_lock);
2296 hudecek 237
            }
238
            t=t->next;
239
        }
240
        while (t);
241
    }
2309 hudecek 242
    else
243
        spinlock_unlock(&tasklet_lock);
2296 hudecek 244
}
245
 
246
/** Frees the tasklet structure when no longer needed. The function doesn't provide
2430 hudecek 247
*   any synchronization, the caller must be sure, that the tasklet is not scheduled.
2296 hudecek 248
*
249
* @param tasklet to be freed
250
*/
251
void tasklet_free(tasklet_descriptor_t* t)
252
{
253
    spinlock_lock(&tasklet_lock);
254
    if (t->state == TASKLET_STATE_NOTACTIVE)
255
        free(t);
256
    else
257
        panic_printf("attempting to free an active tasklet");
258
    spinlock_unlock(&tasklet_lock);
259
}
260
 
261