Subversion Repositories HelenOS

Rev

Rev 2309 | Go to most recent revision | 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
 */
33
/** @file
34
 */
35
 
36
#include <arch.h>
37
#include <config.h>
38
#include <arch/types.h>
39
#include <arch/asm.h>
40
#include <proc/tasklet.h>
41
#include <synch/spinlock.h>
42
#include <time/delay.h>
43
#include <proc/thread.h>
44
#include <panic.h>
45
#include <print.h>
46
#include <synch/waitq.h>
47
#include <cpu.h>
48
#include <proc/scheduler.h>
49
 
50
/** Timeout length in the tasklet thread */
51
#define TASKLET_THREAD_SLEEPDELAY 10000
52
///** Priority of the tasklet thread */
53
#define TASKLET_THREAD_PRIORITY 1
54
 
55
 
56
/** Tasklet state enum */
57
typedef enum  {
58
TASKLET_STATE_NOTACTIVE = 0,
59
TASKLET_STATE_SCHEDULED = 1,
60
TASKLET_STATE_RUNNING = 2,
61
TASKLET_STATE_DISABLED = 4
62
} tasklet_state_enum;
63
 
64
/** Spinlock protecting list of tasklets */
65
SPINLOCK_INITIALIZE(tasklet_lock);
66
 
67
/** array of tasklet lists for every CPU */
68
tasklet_descriptor_t** tasklet_list;
69
 
70
/** Initializes tasklets - except for the tasklet thread */
71
void tasklet_init(void)
72
{
73
    int i;
74
    tasklet_list=malloc(sizeof(tasklet_descriptor_t*)*config.cpu_count,0);
75
    if (tasklet_list==0)
76
        panic_printf("tasklet list mem not allocated\n");
77
    for(i=0;i<config.cpu_count;i++) {
78
        tasklet_list[i]=0;
79
    }
80
    spinlock_initialize(&tasklet_lock, "tasklet_lock");
81
 
82
}
83
 
84
/**
85
* Creates and runs the tasklet thread
86
*
87
*  @param kernel_task Pointer to the kernel task - for create_thread
88
*/
89
void tasklet_run_tasklet_thread(task_t * kernel_task)
90
{
91
    //create the tasklet_thread, it's wired to the current cpu, we'll migrate it ourselves
92
    thread_t* t= thread_create(&tasklet_thread, NULL, kernel_task, THREAD_FLAG_WIRED, "tasklet_thread", false);
93
    if (t==NULL) {
94
        //wtf?
95
        panic_printf("tasklet thread not created\n");
96
    } else {
97
        spinlock_lock(&t->lock);           
98
        t->cpu = &cpus[0];
99
        t->priority = TASKLET_THREAD_PRIORITY;
100
        spinlock_unlock(&t->lock);
101
        thread_ready(t);
102
    }
103
}
104
 
105
/** Thread which keeps executing scheduled enabled tasklets
106
* @param data not used
107
*/
108
void tasklet_thread(void* data)
109
{
110
#ifdef CONFIG_SMP
111
    int current_cpu, new_cpu ;
112
    cpu_t *cpu;
113
#endif
114
    waitq_t  wq;
115
    waitq_initialize(&wq);
116
 
117
    while (true) {
118
        tasklet_do();
119
#ifdef CONFIG_SMP
120
        if (config.cpu_active>1) {
121
            current_cpu = CPU->id;
122
            //find the first cpu with nonempty tasklet_list 
2330 hudecek 123
            for (new_cpu = (current_cpu + 1) % config.cpu_active; new_cpu!=current_cpu && tasklet_list[new_cpu]==0 && cpus[new_cpu].active;
2296 hudecek 124
                    new_cpu=(new_cpu + 1)% config.cpu_active);
125
 
126
            if (new_cpu!=current_cpu) {
127
                //we need to migrate this thread to CPU with id new_cpu
128
                cpu = &cpus[new_cpu];
129
 
130
                spinlock_lock(&THREAD->lock);
131
                //put tasklet_thread on the new_cpu 
132
                THREAD->cpu = cpu;
133
                spinlock_unlock(&THREAD->lock);
134
            }
135
        }
136
#endif
137
        waitq_sleep_timeout(&wq, (uint32_t)TASKLET_THREAD_SLEEPDELAY, 0);
138
 
139
 
140
    }
141
}
142
 
143
/** Initializes tasklet structure
144
* @param func tasklet callback function to be called
145
* @param data pointer to be passed to the tasklet function
146
*/
147
tasklet_descriptor_t* tasklet_register(void (*func)(void* data), void* data)
148
{
149
    tasklet_descriptor_t* tasklet=malloc(sizeof(tasklet_descriptor_t),0);
150
    tasklet->data = data;
151
    tasklet->func = func;
152
    tasklet->state = TASKLET_STATE_NOTACTIVE;
153
    tasklet->next = 0;
154
    return tasklet;
155
}
156
 
157
/** Schedules the tasklet for execution on current CPU
158
* @param t tasklet to be scheduled
159
*/
160
void tasklet_schedule(tasklet_descriptor_t* t)
161
{
2309 hudecek 162
    tasklet_schedule_SMP(t, CPU->id);
163
}
164
 
165
/** Schedules the tasklet for execution on id CPU
166
* @param t tasklet to be scheduled
167
* @param id CPU id on which the tasklet will be scheduled
168
*/
169
void tasklet_schedule_SMP(tasklet_descriptor_t* t, uint32_t id)
170
{
2296 hudecek 171
    spinlock_lock(&tasklet_lock);
172
    //clear notactive, running and scheduled flags
173
    t->state &= TASKLET_STATE_DISABLED;
174
    //set the scheduled flag
175
    t->state |= TASKLET_STATE_SCHEDULED;
2309 hudecek 176
    t->next=tasklet_list[id];
177
    tasklet_list[id]=t;
2296 hudecek 178
    spinlock_unlock(&tasklet_lock);
179
}
180
 
181
/** Tasklet will not be run, even if scheduled
182
* @param t tasklet to be disabled
183
*/
184
void tasklet_disable(tasklet_descriptor_t* t)
185
{
186
    spinlock_lock(&tasklet_lock);
187
    //set the disabled flag
188
    t->state |= TASKLET_STATE_DISABLED;
189
    spinlock_unlock(&tasklet_lock);
190
}
191
 
192
/** Tasklet will be run if scheduled
193
* @param t tasklet to be enabled
194
*/
195
void tasklet_enable(tasklet_descriptor_t* t)
196
{
197
    spinlock_lock(&tasklet_lock);
198
    //clear the disabled flag
199
    t->state &= ~TASKLET_STATE_DISABLED;
200
    spinlock_unlock(&tasklet_lock);
201
}
202
 
203
 
204
 
205
 
206
/** Executes scheduled enabled tasklets on current CPU */
207
void tasklet_do(void)
208
{
209
    spinlock_lock(&tasklet_lock);
210
    tasklet_descriptor_t* t = tasklet_list[CPU->id];
211
    //printf(".");
212
    if (t) {
213
        //empty the tasklet_list
214
        tasklet_list[CPU->id]=0;
2309 hudecek 215
        spinlock_unlock(&tasklet_lock);
2296 hudecek 216
        do {
217
            if (!(t->state & TASKLET_STATE_DISABLED)) {
218
                if (t->func) {
219
                    t->state = TASKLET_STATE_RUNNING;
220
                    t->func(t->data);
2309 hudecek 221
                    //clear running flag, set not active - the tasklet can disable itself
222
                    //thats why we don't just set it as not active
223
                    t->state &= ~TASKLET_STATE_RUNNING;
224
                    t->state |= TASKLET_STATE_NOTACTIVE;
2296 hudecek 225
                } else
226
                    panic_printf("tasklet func NULL\n");
227
            } else {  
228
                //return it back to the queue of scheduled tasklets
2309 hudecek 229
                spinlock_lock(&tasklet_lock);  
2296 hudecek 230
                t->next = tasklet_list[CPU->id];
231
                tasklet_list[CPU->id] = t;
2309 hudecek 232
                spinlock_unlock(&tasklet_lock);
2296 hudecek 233
            }
234
            t=t->next;
235
        }
236
        while (t);
237
    }
2309 hudecek 238
    else
239
        spinlock_unlock(&tasklet_lock);
2296 hudecek 240
}
241
 
242
/** Frees the tasklet structure when no longer needed. The function doesn't provide
243
*   any synchronization, the caller must be sure, the tasklet is not scheduled.
244
*
245
* @param tasklet to be freed
246
*/
247
void tasklet_free(tasklet_descriptor_t* t)
248
{
249
    spinlock_lock(&tasklet_lock);
250
    if (t->state == TASKLET_STATE_NOTACTIVE)
251
        free(t);
252
    else
253
        panic_printf("attempting to free an active tasklet");
254
    spinlock_unlock(&tasklet_lock);
255
}
256
 
257