scheduler.c 29.9 KB
Newer Older
1
/*
mysterywolf's avatar
mysterywolf 已提交
2
 * Copyright (c) 2006-2021, RT-Thread Development Team
B
Bernard Xiong 已提交
3
 *
4
 * SPDX-License-Identifier: Apache-2.0
5 6 7 8 9 10
 *
 * Change Logs:
 * Date           Author       Notes
 * 2006-03-17     Bernard      the first version
 * 2006-04-28     Bernard      fix the scheduler algorthm
 * 2006-04-30     Bernard      add SCHEDULER_DEBUG
11 12
 * 2006-05-27     Bernard      fix the scheduler algorthm for same priority
 *                             thread schedule
13 14 15 16 17
 * 2006-06-04     Bernard      rewrite the scheduler algorithm
 * 2006-08-03     Bernard      add hook support
 * 2006-09-05     Bernard      add 32 priority level support
 * 2006-09-24     Bernard      add rt_system_scheduler_start function
 * 2009-09-16     Bernard      fix _rt_scheduler_stack_check
18
 * 2010-04-11     yi.qiu       add module feature
19
 * 2010-07-13     Bernard      fix the maximal number of rt_scheduler_lock_nest
20
 *                             issue found by kuronca
21
 * 2010-12-13     Bernard      add defunct list initialization even if not use heap.
B
bernard.xiong@gmail.com 已提交
22
 * 2011-05-10     Bernard      clean scheduler debug log.
23
 * 2013-12-21     Grissiom     add rt_critical_level
S
shaojinchun 已提交
24 25
 * 2018-11-22     Jesven       remove the current task from ready queue
 *                             add per cpu ready queue
mysterywolf's avatar
mysterywolf 已提交
26
 *                             add _scheduler_get_highest_priority_thread to find highest priority task
S
shaojinchun 已提交
27 28 29 30
 *                             rt_schedule_insert_thread won't insert current task to ready queue
 *                             in smp version, rt_hw_context_switch_interrupt maybe switch to
 *                               new task directly
 *
31 32 33 34 35 36
 */

#include <rtthread.h>
#include <rthw.h>

rt_list_t rt_thread_priority_table[RT_THREAD_PRIORITY_MAX];
S
shaojinchun 已提交
37
rt_uint32_t rt_thread_ready_priority_group;
38
#if RT_THREAD_PRIORITY_MAX > 32
39
/* Maximum priority level, 256 */
40
rt_uint8_t rt_thread_ready_table[32];
41
#endif /* RT_THREAD_PRIORITY_MAX > 32 */
42

S
shaojinchun 已提交
43 44 45
#ifndef RT_USING_SMP
extern volatile rt_uint8_t rt_interrupt_nest;
static rt_int16_t rt_scheduler_lock_nest;
46
struct rt_thread *rt_current_thread = RT_NULL;
S
shaojinchun 已提交
47
rt_uint8_t rt_current_priority;
48
#endif /* RT_USING_SMP */
S
shaojinchun 已提交
49

50
#ifdef RT_USING_HOOK
D
dzzxzz 已提交
51
static void (*rt_scheduler_hook)(struct rt_thread *from, struct rt_thread *to);
F
fenghuijie 已提交
52
static void (*rt_scheduler_switch_hook)(struct rt_thread *tid);
53 54 55 56

/**
 * @addtogroup Hook
 */
D
dzzxzz 已提交
57

D
dogandog 已提交
58
/**@{*/
59 60 61 62 63 64 65

/**
 * This function will set a hook function, which will be invoked when thread
 * switch happens.
 *
 * @param hook the hook function
 */
66 67
void
rt_scheduler_sethook(void (*hook)(struct rt_thread *from, struct rt_thread *to))
68
{
69
    rt_scheduler_hook = hook;
70 71
}

F
fenghuijie 已提交
72 73 74 75 76 77
void
rt_scheduler_switch_sethook(void (*hook)(struct rt_thread *tid))
{
    rt_scheduler_switch_hook = hook;
}

D
dogandog 已提交
78
/**@}*/
79
#endif /* RT_USING_HOOK */
80 81

#ifdef RT_USING_OVERFLOW_CHECK
D
dzzxzz 已提交
82
static void _rt_scheduler_stack_check(struct rt_thread *thread)
83
{
84 85
    RT_ASSERT(thread != RT_NULL);

86
#ifdef ARCH_CPU_STACK_GROWS_UPWARD
87
    if (*((rt_uint8_t *)((rt_ubase_t)thread->stack_addr + thread->stack_size - 1)) != '#' ||
Z
zhaohengbo 已提交
88
#else
A
Aubr.Cool 已提交
89
    if (*((rt_uint8_t *)thread->stack_addr) != '#' ||
90
#endif /* ARCH_CPU_STACK_GROWS_UPWARD */
B
Bernard Xiong 已提交
91 92 93
        (rt_ubase_t)thread->sp <= (rt_ubase_t)thread->stack_addr ||
        (rt_ubase_t)thread->sp >
        (rt_ubase_t)thread->stack_addr + (rt_ubase_t)thread->stack_size)
94
    {
B
Bernard Xiong 已提交
95
        rt_ubase_t level;
96 97

        rt_kprintf("thread:%s stack overflow\n", thread->name);
98

99 100 101
        level = rt_hw_interrupt_disable();
        while (level);
    }
102
#ifdef ARCH_CPU_STACK_GROWS_UPWARD
103 104 105 106 107 108
    else if ((rt_ubase_t)thread->sp > ((rt_ubase_t)thread->stack_addr + thread->stack_size))
    {
        rt_kprintf("warning: %s stack is close to the top of stack address.\n",
                   thread->name);
    }
#else
S
shaojinchun 已提交
109
    else if ((rt_ubase_t)thread->sp <= ((rt_ubase_t)thread->stack_addr + 32))
110
    {
S
shaojinchun 已提交
111
        rt_kprintf("warning: %s stack is close to end of stack address.\n",
112 113
                   thread->name);
    }
114
#endif /* ARCH_CPU_STACK_GROWS_UPWARD */
S
shaojinchun 已提交
115
}
116
#endif /* RT_USING_OVERFLOW_CHECK */
S
shaojinchun 已提交
117 118 119 120 121

/*
 * get the highest priority thread in ready queue
 */
#ifdef RT_USING_SMP
mysterywolf's avatar
mysterywolf 已提交
122
static struct rt_thread* _scheduler_get_highest_priority_thread(rt_ubase_t *highest_prio)
S
shaojinchun 已提交
123 124 125 126 127 128 129 130 131 132 133
{
    register struct rt_thread *highest_priority_thread;
    register rt_ubase_t highest_ready_priority, local_highest_ready_priority;
    struct rt_cpu* pcpu = rt_cpu_self();
#if RT_THREAD_PRIORITY_MAX > 32
    register rt_ubase_t number;

    number = __rt_ffs(rt_thread_ready_priority_group) - 1;
    highest_ready_priority = (number << 3) + __rt_ffs(rt_thread_ready_table[number]) - 1;
    number = __rt_ffs(pcpu->priority_group) - 1;
    local_highest_ready_priority = (number << 3) + __rt_ffs(pcpu->ready_table[number]) - 1;
134
#else
S
shaojinchun 已提交
135 136
    highest_ready_priority = __rt_ffs(rt_thread_ready_priority_group) - 1;
    local_highest_ready_priority = __rt_ffs(pcpu->priority_group) - 1;
137
#endif /* RT_THREAD_PRIORITY_MAX > 32 */
S
shaojinchun 已提交
138 139 140

    /* get highest ready priority thread */
    if (highest_ready_priority < local_highest_ready_priority)
141
    {
S
shaojinchun 已提交
142 143 144 145 146 147 148 149 150 151 152
        *highest_prio = highest_ready_priority;
        highest_priority_thread = rt_list_entry(rt_thread_priority_table[highest_ready_priority].next,
                                  struct rt_thread,
                                  tlist);
    }
    else
    {
        *highest_prio = local_highest_ready_priority;
        highest_priority_thread = rt_list_entry(pcpu->priority_table[local_highest_ready_priority].next,
                                  struct rt_thread,
                                  tlist);
153
    }
S
shaojinchun 已提交
154 155 156 157

    return highest_priority_thread;
}
#else
mysterywolf's avatar
mysterywolf 已提交
158
static struct rt_thread* _scheduler_get_highest_priority_thread(rt_ubase_t *highest_prio)
S
shaojinchun 已提交
159 160 161 162 163 164 165 166 167 168 169
{
    register struct rt_thread *highest_priority_thread;
    register rt_ubase_t highest_ready_priority;

#if RT_THREAD_PRIORITY_MAX > 32
    register rt_ubase_t number;

    number = __rt_ffs(rt_thread_ready_priority_group) - 1;
    highest_ready_priority = (number << 3) + __rt_ffs(rt_thread_ready_table[number]) - 1;
#else
    highest_ready_priority = __rt_ffs(rt_thread_ready_priority_group) - 1;
170
#endif /* RT_THREAD_PRIORITY_MAX > 32 */
S
shaojinchun 已提交
171 172 173 174 175 176 177 178 179

    /* get highest ready priority thread */
    highest_priority_thread = rt_list_entry(rt_thread_priority_table[highest_ready_priority].next,
                              struct rt_thread,
                              tlist);

    *highest_prio = highest_ready_priority;

    return highest_priority_thread;
180
}
181
#endif /* RT_USING_SMP */
182 183 184

/**
 * @ingroup SystemInit
B
bernard.xiong@gmail.com 已提交
185
 * This function will initialize the system scheduler
186 187 188
 */
void rt_system_scheduler_init(void)
{
S
shaojinchun 已提交
189 190
#ifdef RT_USING_SMP
    int cpu;
191
#endif /* RT_USING_SMP */
192
    register rt_base_t offset;
193

S
shaojinchun 已提交
194
#ifndef RT_USING_SMP
195
    rt_scheduler_lock_nest = 0;
196
#endif /* RT_USING_SMP */
197

198
    RT_DEBUG_LOG(RT_DEBUG_SCHEDULER, ("start scheduler: max priority 0x%02x\n",
199
                                      RT_THREAD_PRIORITY_MAX));
B
bernard.xiong@gmail.com 已提交
200

201 202 203 204
    for (offset = 0; offset < RT_THREAD_PRIORITY_MAX; offset ++)
    {
        rt_list_init(&rt_thread_priority_table[offset]);
    }
S
shaojinchun 已提交
205 206 207 208 209 210 211 212
#ifdef RT_USING_SMP
    for (cpu = 0; cpu < RT_CPUS_NR; cpu++)
    {
        struct rt_cpu *pcpu =  rt_cpu_index(cpu);
        for (offset = 0; offset < RT_THREAD_PRIORITY_MAX; offset ++)
        {
            rt_list_init(&pcpu->priority_table[offset]);
        }
213

S
shaojinchun 已提交
214 215 216 217 218 219 220
        pcpu->irq_switch_flag = 0;
        pcpu->current_priority = RT_THREAD_PRIORITY_MAX - 1;
        pcpu->current_thread = RT_NULL;
        pcpu->priority_group = 0;

#if RT_THREAD_PRIORITY_MAX > 32
        rt_memset(pcpu->ready_table, 0, sizeof(pcpu->ready_table));
221
#endif /* RT_THREAD_PRIORITY_MAX > 32 */
S
shaojinchun 已提交
222
    }
223
#endif /* RT_USING_SMP */
224

225 226
    /* initialize ready priority group */
    rt_thread_ready_priority_group = 0;
227 228

#if RT_THREAD_PRIORITY_MAX > 32
229 230
    /* initialize ready table */
    rt_memset(rt_thread_ready_table, 0, sizeof(rt_thread_ready_table));
231
#endif /* RT_THREAD_PRIORITY_MAX > 32 */
232 233 234
}

/**
235
 * @ingroup SystemInit
236 237 238
 * This function will startup scheduler. It will select one thread
 * with the highest priority level, then switch to it.
 */
D
dzzxzz 已提交
239
void rt_system_scheduler_start(void)
240
{
241
    register struct rt_thread *to_thread;
S
shaojinchun 已提交
242
    rt_ubase_t highest_ready_priority;
243

mysterywolf's avatar
mysterywolf 已提交
244
    to_thread = _scheduler_get_highest_priority_thread(&highest_ready_priority);
245

S
shaojinchun 已提交
246 247
#ifdef RT_USING_SMP
    to_thread->oncpu = rt_hw_cpu_id();
248
#else
249
    rt_current_thread = to_thread;
250
#endif /* RT_USING_SMP */
S
shaojinchun 已提交
251 252

    rt_schedule_remove_thread(to_thread);
253
    to_thread->stat = RT_THREAD_RUNNING;
254

255
    /* switch to new thread */
S
shaojinchun 已提交
256
#ifdef RT_USING_SMP
257
    rt_hw_context_switch_to((rt_ubase_t)&to_thread->sp, to_thread);
S
shaojinchun 已提交
258
#else
259
    rt_hw_context_switch_to((rt_ubase_t)&to_thread->sp);
260
#endif /* RT_USING_SMP */
261

262
    /* never come back */
263 264 265 266 267
}

/**
 * @addtogroup Thread
 */
D
dzzxzz 已提交
268

D
dogandog 已提交
269
/**@{*/
270

S
shaojinchun 已提交
271 272 273 274

#ifdef RT_USING_SMP
/**
 * This function will handle IPI interrupt and do a scheduling in system;
mysterywolf's avatar
mysterywolf 已提交
275
 *
S
shaojinchun 已提交
276 277
 * @param vector, the number of IPI interrupt for system scheduling
 * @param param, use RT_NULL
mysterywolf's avatar
mysterywolf 已提交
278
 *
S
shaojinchun 已提交
279 280 281 282 283 284 285 286 287
 * NOTE: this function should be invoke or register as ISR in BSP.
 */
void rt_scheduler_ipi_handler(int vector, void *param)
{
    rt_schedule();
}

/**
 * This function will perform one scheduling. It will select one thread
mysterywolf's avatar
mysterywolf 已提交
288
 * with the highest priority level in global ready queue or local ready queue,
S
shaojinchun 已提交
289 290 291 292 293 294 295 296 297 298 299
 * then switch to it.
 */
void rt_schedule(void)
{
    rt_base_t level;
    struct rt_thread *to_thread;
    struct rt_thread *current_thread;
    struct rt_cpu    *pcpu;
    int cpu_id;

    /* disable interrupt */
300
    level  = rt_hw_interrupt_disable();
S
shaojinchun 已提交
301 302 303 304 305 306 307 308 309

    cpu_id = rt_hw_cpu_id();
    pcpu   = rt_cpu_index(cpu_id);
    current_thread = pcpu->current_thread;

    /* whether do switch in interrupt */
    if (pcpu->irq_nest)
    {
        pcpu->irq_switch_flag = 1;
S
shaojinchun 已提交
310 311 312 313 314 315 316 317 318 319 320 321 322
        rt_hw_interrupt_enable(level);
        goto __exit;
    }

#ifdef RT_USING_SIGNALS
    if ((current_thread->stat & RT_THREAD_STAT_MASK) == RT_THREAD_SUSPEND)
    {
        /* if current_thread signal is in pending */

        if ((current_thread->stat & RT_THREAD_STAT_SIGNAL_MASK) & RT_THREAD_STAT_SIGNAL_PENDING)
        {
            rt_thread_resume(current_thread);
        }
S
shaojinchun 已提交
323
    }
324
#endif /* RT_USING_SIGNALS */
S
shaojinchun 已提交
325 326

    if (current_thread->scheduler_lock_nest == 1) /* whether lock scheduler */
S
shaojinchun 已提交
327 328 329 330 331
    {
        rt_ubase_t highest_ready_priority;

        if (rt_thread_ready_priority_group != 0 || pcpu->priority_group != 0)
        {
mysterywolf's avatar
mysterywolf 已提交
332
            to_thread = _scheduler_get_highest_priority_thread(&highest_ready_priority);
S
shaojinchun 已提交
333
            current_thread->oncpu = RT_CPU_DETACHED;
334
            if ((current_thread->stat & RT_THREAD_STAT_MASK) == RT_THREAD_RUNNING)
S
shaojinchun 已提交
335 336 337 338 339
            {
                if (current_thread->current_priority < highest_ready_priority)
                {
                    to_thread = current_thread;
                }
340
                else if (current_thread->current_priority == highest_ready_priority && (current_thread->stat & RT_THREAD_STAT_YIELD_MASK) == 0)
341 342 343
                {
                    to_thread = current_thread;
                }
S
shaojinchun 已提交
344 345 346 347
                else
                {
                    rt_schedule_insert_thread(current_thread);
                }
348
                current_thread->stat &= ~RT_THREAD_STAT_YIELD_MASK;
S
shaojinchun 已提交
349 350 351 352 353 354 355 356 357 358
            }
            to_thread->oncpu = cpu_id;
            if (to_thread != current_thread)
            {
                /* if the destination thread is not the same as current thread */
                pcpu->current_priority = (rt_uint8_t)highest_ready_priority;

                RT_OBJECT_HOOK_CALL(rt_scheduler_hook, (current_thread, to_thread));

                rt_schedule_remove_thread(to_thread);
359
                to_thread->stat = RT_THREAD_RUNNING | (to_thread->stat & ~RT_THREAD_STAT_MASK);
S
shaojinchun 已提交
360 361 362 363 364 365 366 367 368 369 370 371

                /* switch to new thread */
                RT_DEBUG_LOG(RT_DEBUG_SCHEDULER,
                        ("[%d]switch to priority#%d "
                         "thread:%.*s(sp:0x%08x), "
                         "from thread:%.*s(sp: 0x%08x)\n",
                         pcpu->irq_nest, highest_ready_priority,
                         RT_NAME_MAX, to_thread->name, to_thread->sp,
                         RT_NAME_MAX, current_thread->name, current_thread->sp));

#ifdef RT_USING_OVERFLOW_CHECK
                _rt_scheduler_stack_check(to_thread);
372
#endif /* RT_USING_OVERFLOW_CHECK */
S
shaojinchun 已提交
373

F
fenghuijie 已提交
374 375
                RT_OBJECT_HOOK_CALL(rt_scheduler_switch_hook, (current_thread));

S
shaojinchun 已提交
376 377
                rt_hw_context_switch((rt_ubase_t)&current_thread->sp,
                        (rt_ubase_t)&to_thread->sp, to_thread);
S
shaojinchun 已提交
378 379 380 381
            }
        }
    }

382 383 384
    /* enable interrupt */
    rt_hw_interrupt_enable(level);

S
shaojinchun 已提交
385
#ifdef RT_USING_SIGNALS
386 387
    /* check stat of thread for signal */
    level = rt_hw_interrupt_disable();
S
shaojinchun 已提交
388 389 390 391 392 393 394 395 396 397 398 399 400 401 402
    if (current_thread->stat & RT_THREAD_STAT_SIGNAL_PENDING)
    {
        extern void rt_thread_handle_sig(rt_bool_t clean_state);

        current_thread->stat &= ~RT_THREAD_STAT_SIGNAL_PENDING;

        rt_hw_interrupt_enable(level);

        /* check signal status */
        rt_thread_handle_sig(RT_TRUE);
    }
    else
    {
        rt_hw_interrupt_enable(level);
    }
403
#endif /* RT_USING_SIGNALS */
S
shaojinchun 已提交
404 405 406 407 408

__exit:
    return ;
}
#else
409 410
/**
 * This function will perform one schedule. It will select one thread
411
 * with the highest priority level, and switch to it immediately.
412
 */
D
dzzxzz 已提交
413
void rt_schedule(void)
414
{
415 416 417
    rt_base_t level;
    struct rt_thread *to_thread;
    struct rt_thread *from_thread;
418

419 420
    /* disable interrupt */
    level = rt_hw_interrupt_disable();
421

422 423 424
    /* check the scheduler is enabled or not */
    if (rt_scheduler_lock_nest == 0)
    {
S
shaojinchun 已提交
425
        rt_ubase_t highest_ready_priority;
426

S
shaojinchun 已提交
427 428
        if (rt_thread_ready_priority_group != 0)
        {
429
            /* need_insert_from_thread: need to insert from_thread to ready queue */
S
shaojinchun 已提交
430
            int need_insert_from_thread = 0;
431

mysterywolf's avatar
mysterywolf 已提交
432
            to_thread = _scheduler_get_highest_priority_thread(&highest_ready_priority);
433

434
            if ((rt_current_thread->stat & RT_THREAD_STAT_MASK) == RT_THREAD_RUNNING)
S
shaojinchun 已提交
435 436 437 438 439
            {
                if (rt_current_thread->current_priority < highest_ready_priority)
                {
                    to_thread = rt_current_thread;
                }
440
                else if (rt_current_thread->current_priority == highest_ready_priority && (rt_current_thread->stat & RT_THREAD_STAT_YIELD_MASK) == 0)
441
                {
442
                    to_thread = rt_current_thread;
443
                }
S
shaojinchun 已提交
444 445 446 447
                else
                {
                    need_insert_from_thread = 1;
                }
448
                rt_current_thread->stat &= ~RT_THREAD_STAT_YIELD_MASK;
S
shaojinchun 已提交
449
            }
450

S
shaojinchun 已提交
451 452 453 454 455 456 457 458
            if (to_thread != rt_current_thread)
            {
                /* if the destination thread is not the same as current thread */
                rt_current_priority = (rt_uint8_t)highest_ready_priority;
                from_thread         = rt_current_thread;
                rt_current_thread   = to_thread;

                RT_OBJECT_HOOK_CALL(rt_scheduler_hook, (from_thread, to_thread));
459

S
shaojinchun 已提交
460 461 462 463
                if (need_insert_from_thread)
                {
                    rt_schedule_insert_thread(from_thread);
                }
464

S
shaojinchun 已提交
465
                rt_schedule_remove_thread(to_thread);
466
                to_thread->stat = RT_THREAD_RUNNING | (to_thread->stat & ~RT_THREAD_STAT_MASK);
S
shaojinchun 已提交
467 468 469 470

                /* switch to new thread */
                RT_DEBUG_LOG(RT_DEBUG_SCHEDULER,
                        ("[%d]switch to priority#%d "
B
Bernard Xiong 已提交
471 472
                         "thread:%.*s(sp:0x%08x), "
                         "from thread:%.*s(sp: 0x%08x)\n",
S
shaojinchun 已提交
473 474 475
                         rt_interrupt_nest, highest_ready_priority,
                         RT_NAME_MAX, to_thread->name, to_thread->sp,
                         RT_NAME_MAX, from_thread->name, from_thread->sp));
476

477
#ifdef RT_USING_OVERFLOW_CHECK
S
shaojinchun 已提交
478
                _rt_scheduler_stack_check(to_thread);
479
#endif /* RT_USING_OVERFLOW_CHECK */
480

S
shaojinchun 已提交
481 482 483
                if (rt_interrupt_nest == 0)
                {
                    extern void rt_thread_handle_sig(rt_bool_t clean_state);
B
bernard 已提交
484

F
fenghuijie 已提交
485 486
                    RT_OBJECT_HOOK_CALL(rt_scheduler_switch_hook, (from_thread));

S
shaojinchun 已提交
487 488
                    rt_hw_context_switch((rt_ubase_t)&from_thread->sp,
                            (rt_ubase_t)&to_thread->sp);
489 490 491 492

                    /* enable interrupt */
                    rt_hw_interrupt_enable(level);

S
shaojinchun 已提交
493
#ifdef RT_USING_SIGNALS
494 495
                    /* check stat of thread for signal */
                    level = rt_hw_interrupt_disable();
S
shaojinchun 已提交
496 497 498 499 500
                    if (rt_current_thread->stat & RT_THREAD_STAT_SIGNAL_PENDING)
                    {
                        extern void rt_thread_handle_sig(rt_bool_t clean_state);

                        rt_current_thread->stat &= ~RT_THREAD_STAT_SIGNAL_PENDING;
B
bernard 已提交
501

S
shaojinchun 已提交
502 503 504 505 506 507 508 509 510
                        rt_hw_interrupt_enable(level);

                        /* check signal status */
                        rt_thread_handle_sig(RT_TRUE);
                    }
                    else
                    {
                        rt_hw_interrupt_enable(level);
                    }
511
#endif /* RT_USING_SIGNALS */
S
shaojinchun 已提交
512 513 514 515 516 517 518 519 520
                    goto __exit;
                }
                else
                {
                    RT_DEBUG_LOG(RT_DEBUG_SCHEDULER, ("switch in interrupt\n"));

                    rt_hw_context_switch_interrupt((rt_ubase_t)&from_thread->sp,
                            (rt_ubase_t)&to_thread->sp);
                }
521 522 523
            }
            else
            {
S
shaojinchun 已提交
524
                rt_schedule_remove_thread(rt_current_thread);
525
                rt_current_thread->stat = RT_THREAD_RUNNING | (rt_current_thread->stat & ~RT_THREAD_STAT_MASK);
526 527 528
            }
        }
    }
S
shaojinchun 已提交
529 530 531 532 533 534 535

    /* enable interrupt */
    rt_hw_interrupt_enable(level);

__exit:
    return;
}
536
#endif /* RT_USING_SMP */
S
shaojinchun 已提交
537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557

/**
 * This function checks if a scheduling is needed after IRQ context. If yes,
 * it will select one thread with the highest priority level, and then switch
 * to it.
 */
#ifdef RT_USING_SMP
void rt_scheduler_do_irq_switch(void *context)
{
    int cpu_id;
    rt_base_t level;
    struct rt_cpu* pcpu;
    struct rt_thread *to_thread;
    struct rt_thread *current_thread;

    level = rt_hw_interrupt_disable();

    cpu_id = rt_hw_cpu_id();
    pcpu   = rt_cpu_index(cpu_id);
    current_thread = pcpu->current_thread;

S
shaojinchun 已提交
558 559 560 561 562 563 564 565 566 567
#ifdef RT_USING_SIGNALS
    if ((current_thread->stat & RT_THREAD_STAT_MASK) == RT_THREAD_SUSPEND)
    {
        /* if current_thread signal is in pending */

        if ((current_thread->stat & RT_THREAD_STAT_SIGNAL_MASK) & RT_THREAD_STAT_SIGNAL_PENDING)
        {
            rt_thread_resume(current_thread);
        }
    }
568
#endif /* RT_USING_SIGNALS */
S
shaojinchun 已提交
569

S
shaojinchun 已提交
570
    if (pcpu->irq_switch_flag == 0)
armink_ztl's avatar
armink_ztl 已提交
571 572
    {
        rt_hw_interrupt_enable(level);
S
shaojinchun 已提交
573 574 575 576 577 578 579 580 581 582 583 584
        return;
    }

    if (current_thread->scheduler_lock_nest == 1 && pcpu->irq_nest == 0)
    {
        rt_ubase_t highest_ready_priority;

        /* clear irq switch flag */
        pcpu->irq_switch_flag = 0;

        if (rt_thread_ready_priority_group != 0 || pcpu->priority_group != 0)
        {
mysterywolf's avatar
mysterywolf 已提交
585
            to_thread = _scheduler_get_highest_priority_thread(&highest_ready_priority);
S
shaojinchun 已提交
586
            current_thread->oncpu = RT_CPU_DETACHED;
587
            if ((current_thread->stat & RT_THREAD_STAT_MASK) == RT_THREAD_RUNNING)
S
shaojinchun 已提交
588 589 590 591 592
            {
                if (current_thread->current_priority < highest_ready_priority)
                {
                    to_thread = current_thread;
                }
593
                else if (current_thread->current_priority == highest_ready_priority && (current_thread->stat & RT_THREAD_STAT_YIELD_MASK) == 0)
594 595 596
                {
                    to_thread = current_thread;
                }
S
shaojinchun 已提交
597 598 599 600
                else
                {
                    rt_schedule_insert_thread(current_thread);
                }
601
                current_thread->stat &= ~RT_THREAD_STAT_YIELD_MASK;
S
shaojinchun 已提交
602 603 604 605 606 607 608 609 610 611 612
            }
            to_thread->oncpu = cpu_id;
            if (to_thread != current_thread)
            {
                /* if the destination thread is not the same as current thread */

                pcpu->current_priority = (rt_uint8_t)highest_ready_priority;

                RT_OBJECT_HOOK_CALL(rt_scheduler_hook, (current_thread, to_thread));

                rt_schedule_remove_thread(to_thread);
613
                to_thread->stat = RT_THREAD_RUNNING | (to_thread->stat & ~RT_THREAD_STAT_MASK);
S
shaojinchun 已提交
614 615 616

#ifdef RT_USING_OVERFLOW_CHECK
                _rt_scheduler_stack_check(to_thread);
617
#endif /* RT_USING_OVERFLOW_CHECK */
S
shaojinchun 已提交
618 619 620 621 622
                RT_DEBUG_LOG(RT_DEBUG_SCHEDULER, ("switch in interrupt\n"));

                current_thread->cpus_lock_nest--;
                current_thread->scheduler_lock_nest--;

F
fenghuijie 已提交
623 624
                RT_OBJECT_HOOK_CALL(rt_scheduler_switch_hook, (current_thread));

S
shaojinchun 已提交
625 626 627 628
                rt_hw_context_switch_interrupt(context, (rt_ubase_t)&current_thread->sp,
                        (rt_ubase_t)&to_thread->sp, to_thread);
            }
        }
armink_ztl's avatar
armink_ztl 已提交
629
    }
S
shaojinchun 已提交
630
    rt_hw_interrupt_enable(level);
631
}
632
#endif /* RT_USING_SMP */
633

B
bernard.xiong@gmail.com 已提交
634
/*
635 636 637 638 639 640
 * This function will insert a thread to system ready queue. The state of
 * thread will be set as READY and remove from suspend queue.
 *
 * @param thread the thread to be inserted
 * @note Please do not invoke this function in user application.
 */
S
shaojinchun 已提交
641 642 643 644 645 646 647 648 649 650 651 652 653
#ifdef RT_USING_SMP
void rt_schedule_insert_thread(struct rt_thread *thread)
{
    int cpu_id;
    int bind_cpu;
    rt_uint32_t cpu_mask;
    register rt_base_t level;

    RT_ASSERT(thread != RT_NULL);

    /* disable interrupt */
    level = rt_hw_interrupt_disable();

654
    /* it should be RUNNING thread */
S
shaojinchun 已提交
655 656
    if (thread->oncpu != RT_CPU_DETACHED)
    {
657
        thread->stat = RT_THREAD_RUNNING | (thread->stat & ~RT_THREAD_STAT_MASK);
S
shaojinchun 已提交
658 659 660
        goto __exit;
    }

661 662 663
    /* READY thread, insert to ready queue */
    thread->stat = RT_THREAD_READY | (thread->stat & ~RT_THREAD_STAT_MASK);

S
shaojinchun 已提交
664 665 666 667 668 669 670 671
    cpu_id   = rt_hw_cpu_id();
    bind_cpu = thread->bind_cpu ;

    /* insert thread to ready list */
    if (bind_cpu == RT_CPUS_NR)
    {
#if RT_THREAD_PRIORITY_MAX > 32
        rt_thread_ready_table[thread->number] |= thread->high_mask;
672
#endif /* RT_THREAD_PRIORITY_MAX > 32 */
S
shaojinchun 已提交
673 674 675 676 677
        rt_thread_ready_priority_group |= thread->number_mask;

        rt_list_insert_before(&(rt_thread_priority_table[thread->current_priority]),
                              &(thread->tlist));
        cpu_mask = RT_CPU_MASK ^ (1 << cpu_id);
S
shaojinchun 已提交
678
        rt_hw_ipi_send(RT_SCHEDULE_IPI, cpu_mask);
S
shaojinchun 已提交
679 680 681 682 683 684 685
    }
    else
    {
        struct rt_cpu *pcpu = rt_cpu_index(bind_cpu);

#if RT_THREAD_PRIORITY_MAX > 32
        pcpu->ready_table[thread->number] |= thread->high_mask;
686
#endif /* RT_THREAD_PRIORITY_MAX > 32 */
S
shaojinchun 已提交
687 688 689 690 691 692 693 694
        pcpu->priority_group |= thread->number_mask;

        rt_list_insert_before(&(rt_cpu_index(bind_cpu)->priority_table[thread->current_priority]),
                              &(thread->tlist));

        if (cpu_id != bind_cpu)
        {
            cpu_mask = 1 << bind_cpu;
S
shaojinchun 已提交
695
            rt_hw_ipi_send(RT_SCHEDULE_IPI, cpu_mask);
S
shaojinchun 已提交
696 697 698 699 700 701 702 703 704 705 706
        }
    }

    RT_DEBUG_LOG(RT_DEBUG_SCHEDULER, ("insert thread[%.*s], the priority: %d\n",
                                      RT_NAME_MAX, thread->name, thread->current_priority));

__exit:
    /* enable interrupt */
    rt_hw_interrupt_enable(level);
}
#else
D
dzzxzz 已提交
707
void rt_schedule_insert_thread(struct rt_thread *thread)
708
{
709
    register rt_base_t temp;
710

711
    RT_ASSERT(thread != RT_NULL);
712

713 714
    /* disable interrupt */
    temp = rt_hw_interrupt_disable();
715

716
    /* it's current thread, it should be RUNNING thread */
S
shaojinchun 已提交
717 718
    if (thread == rt_current_thread)
    {
719
        thread->stat = RT_THREAD_RUNNING | (thread->stat & ~RT_THREAD_STAT_MASK);
S
shaojinchun 已提交
720 721 722
        goto __exit;
    }

723 724
    /* READY thread, insert to ready queue */
    thread->stat = RT_THREAD_READY | (thread->stat & ~RT_THREAD_STAT_MASK);
725 726 727
    /* insert thread to ready list */
    rt_list_insert_before(&(rt_thread_priority_table[thread->current_priority]),
                          &(thread->tlist));
728

G
Grissiom 已提交
729 730
    RT_DEBUG_LOG(RT_DEBUG_SCHEDULER, ("insert thread[%.*s], the priority: %d\n",
                                      RT_NAME_MAX, thread->name, thread->current_priority));
731

S
shaojinchun 已提交
732
    /* set priority mask */
733
#if RT_THREAD_PRIORITY_MAX > 32
734
    rt_thread_ready_table[thread->number] |= thread->high_mask;
735
#endif /* RT_THREAD_PRIORITY_MAX > 32 */
736
    rt_thread_ready_priority_group |= thread->number_mask;
737

S
shaojinchun 已提交
738
__exit:
739 740
    /* enable interrupt */
    rt_hw_interrupt_enable(temp);
741
}
742
#endif /* RT_USING_SMP */
743

B
bernard.xiong@gmail.com 已提交
744
/*
745 746 747 748 749 750
 * This function will remove a thread from system ready queue.
 *
 * @param thread the thread to be removed
 *
 * @note Please do not invoke this function in user application.
 */
S
shaojinchun 已提交
751
#ifdef RT_USING_SMP
D
dzzxzz 已提交
752
void rt_schedule_remove_thread(struct rt_thread *thread)
753
{
S
shaojinchun 已提交
754
    register rt_base_t level;
755

756
    RT_ASSERT(thread != RT_NULL);
757

758
    /* disable interrupt */
S
shaojinchun 已提交
759
    level = rt_hw_interrupt_disable();
760

G
Grissiom 已提交
761 762 763
    RT_DEBUG_LOG(RT_DEBUG_SCHEDULER, ("remove thread[%.*s], the priority: %d\n",
                                      RT_NAME_MAX, thread->name,
                                      thread->current_priority));
S
shaojinchun 已提交
764 765 766 767 768 769 770 771 772 773 774 775 776 777 778

    /* remove thread from ready list */
    rt_list_remove(&(thread->tlist));
    if (thread->bind_cpu == RT_CPUS_NR)
    {
        if (rt_list_isempty(&(rt_thread_priority_table[thread->current_priority])))
        {
#if RT_THREAD_PRIORITY_MAX > 32
            rt_thread_ready_table[thread->number] &= ~thread->high_mask;
            if (rt_thread_ready_table[thread->number] == 0)
            {
                rt_thread_ready_priority_group &= ~thread->number_mask;
            }
#else
            rt_thread_ready_priority_group &= ~thread->number_mask;
779
#endif /* RT_THREAD_PRIORITY_MAX > 32 */
S
shaojinchun 已提交
780 781 782 783 784 785 786 787 788 789
        }
    }
    else
    {
        struct rt_cpu *pcpu = rt_cpu_index(thread->bind_cpu);

        if (rt_list_isempty(&(pcpu->priority_table[thread->current_priority])))
        {
#if RT_THREAD_PRIORITY_MAX > 32
            pcpu->ready_table[thread->number] &= ~thread->high_mask;
790
            if (pcpu->ready_table[thread->number] == 0)
S
shaojinchun 已提交
791 792 793
            {
                pcpu->priority_group &= ~thread->number_mask;
            }
794
#else
S
shaojinchun 已提交
795
            pcpu->priority_group &= ~thread->number_mask;
796
#endif /* RT_THREAD_PRIORITY_MAX > 32 */
S
shaojinchun 已提交
797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815
        }
    }

    /* enable interrupt */
    rt_hw_interrupt_enable(level);
}
#else
void rt_schedule_remove_thread(struct rt_thread *thread)
{
    register rt_base_t level;

    RT_ASSERT(thread != RT_NULL);

    /* disable interrupt */
    level = rt_hw_interrupt_disable();

    RT_DEBUG_LOG(RT_DEBUG_SCHEDULER, ("remove thread[%.*s], the priority: %d\n",
                                      RT_NAME_MAX, thread->name,
                                      thread->current_priority));
816

817 818 819 820
    /* remove thread from ready list */
    rt_list_remove(&(thread->tlist));
    if (rt_list_isempty(&(rt_thread_priority_table[thread->current_priority])))
    {
821
#if RT_THREAD_PRIORITY_MAX > 32
822 823 824 825 826
        rt_thread_ready_table[thread->number] &= ~thread->high_mask;
        if (rt_thread_ready_table[thread->number] == 0)
        {
            rt_thread_ready_priority_group &= ~thread->number_mask;
        }
827
#else
828
        rt_thread_ready_priority_group &= ~thread->number_mask;
829
#endif /* RT_THREAD_PRIORITY_MAX > 32 */
830
    }
831

832
    /* enable interrupt */
S
shaojinchun 已提交
833
    rt_hw_interrupt_enable(level);
834
}
835
#endif /* RT_USING_SMP */
836 837 838 839

/**
 * This function will lock the thread scheduler.
 */
S
shaojinchun 已提交
840 841 842 843 844 845 846 847 848 849
#ifdef RT_USING_SMP
void rt_enter_critical(void)
{
    register rt_base_t level;
    struct rt_thread *current_thread;

    /* disable interrupt */
    level = rt_hw_local_irq_disable();

    current_thread = rt_cpu_self()->current_thread;
850 851 852
    if (!current_thread)
    {
        rt_hw_local_irq_enable(level);
853
        return;
854 855
    }

S
shaojinchun 已提交
856 857 858 859 860 861
    /*
     * the maximal number of nest is RT_UINT16_MAX, which is big
     * enough and does not check here
     */

    {
862 863 864 865 866 867 868
        register rt_uint16_t lock_nest = current_thread->cpus_lock_nest;
        current_thread->cpus_lock_nest++;
        if (lock_nest == 0)
        {
            current_thread->scheduler_lock_nest ++;
            rt_hw_spin_lock(&_cpus_lock);
        }
S
shaojinchun 已提交
869
    }
S
shaojinchun 已提交
870 871 872
    /* critical for local cpu */
    current_thread->critical_lock_nest ++;

S
shaojinchun 已提交
873 874 875 876 877 878 879
    /* lock scheduler for local cpu */
    current_thread->scheduler_lock_nest ++;

    /* enable interrupt */
    rt_hw_local_irq_enable(level);
}
#else
D
dzzxzz 已提交
880
void rt_enter_critical(void)
881
{
882
    register rt_base_t level;
883

884 885
    /* disable interrupt */
    level = rt_hw_interrupt_disable();
886

887 888 889 890 891
    /*
     * the maximal number of nest is RT_UINT16_MAX, which is big
     * enough and does not check here
     */
    rt_scheduler_lock_nest ++;
892

893 894
    /* enable interrupt */
    rt_hw_interrupt_enable(level);
895
}
896
#endif /* RT_USING_SMP */
B
Bernard Xiong 已提交
897
RTM_EXPORT(rt_enter_critical);
898 899 900 901

/**
 * This function will unlock the thread scheduler.
 */
S
shaojinchun 已提交
902 903 904 905 906 907 908 909 910 911
#ifdef RT_USING_SMP
void rt_exit_critical(void)
{
    register rt_base_t level;
    struct rt_thread *current_thread;

    /* disable interrupt */
    level = rt_hw_local_irq_disable();

    current_thread = rt_cpu_self()->current_thread;
912 913 914
    if (!current_thread)
    {
        rt_hw_local_irq_enable(level);
915
        return;
916
    }
S
shaojinchun 已提交
917 918 919

    current_thread->scheduler_lock_nest --;

S
shaojinchun 已提交
920 921
    current_thread->critical_lock_nest --;

922 923
    current_thread->cpus_lock_nest--;
    if (current_thread->cpus_lock_nest == 0)
S
shaojinchun 已提交
924
    {
925 926
        current_thread->scheduler_lock_nest --;
        rt_hw_spin_unlock(&_cpus_lock);
S
shaojinchun 已提交
927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943
    }

    if (current_thread->scheduler_lock_nest <= 0)
    {
        current_thread->scheduler_lock_nest = 0;
        /* enable interrupt */
        rt_hw_local_irq_enable(level);

        rt_schedule();
    }
    else
    {
        /* enable interrupt */
        rt_hw_local_irq_enable(level);
    }
}
#else
D
dzzxzz 已提交
944
void rt_exit_critical(void)
945
{
946 947 948 949 950 951 952 953 954 955 956 957
    register rt_base_t level;

    /* disable interrupt */
    level = rt_hw_interrupt_disable();

    rt_scheduler_lock_nest --;
    if (rt_scheduler_lock_nest <= 0)
    {
        rt_scheduler_lock_nest = 0;
        /* enable interrupt */
        rt_hw_interrupt_enable(level);

958 959 960 961 962
        if (rt_current_thread)
        {
            /* if scheduler is started, do a schedule */
            rt_schedule();
        }
963 964 965 966 967 968
    }
    else
    {
        /* enable interrupt */
        rt_hw_interrupt_enable(level);
    }
969
}
970
#endif /* RT_USING_SMP */
B
Bernard Xiong 已提交
971
RTM_EXPORT(rt_exit_critical);
972

973 974 975 976 977 978 979
/**
 * Get the scheduler lock level
 *
 * @return the level of the scheduler lock. 0 means unlocked.
 */
rt_uint16_t rt_critical_level(void)
{
S
shaojinchun 已提交
980 981 982
#ifdef RT_USING_SMP
    struct rt_thread *current_thread = rt_cpu_self()->current_thread;

S
shaojinchun 已提交
983
    return current_thread->critical_lock_nest;
S
shaojinchun 已提交
984
#else
985
    return rt_scheduler_lock_nest;
986
#endif /* RT_USING_SMP */
987
}
B
Bernard Xiong 已提交
988
RTM_EXPORT(rt_critical_level);
989

S
shaojinchun 已提交
990
/**@}*/