From 4be92dd91aac061121041900bd5e98bccf9647a0 Mon Sep 17 00:00:00 2001 From: Louie Lu Date: Mon, 10 Oct 2016 18:55:54 +0800 Subject: [PATCH] sched-rr: A round-robin scheduler for normal thread This is a round-robin scheduler implement for normal thread (user-thread) create by pager or pthread lib, and using timeslice, we can prevent user-space thread starvation trigger by busy loop. This scheduler only schedling user thread create by ipc "thread start protocal". Default timeslice is 4096 ticks for now. Other trivial change: - Update defconfing for all board - Add sched-rr into Makefile - Add new config in kernel/Kconfig for setting thread timeslice --- Makefile | 4 ++ board/discoveryf4/defconfig | 1 + board/discoveryf429/defconfig | 1 + board/stm32p103/defconfig | 1 + include/sched-rr/sched_rr.h | 31 ++++++++++ include/thread.h | 7 ++- kernel/Kconfig | 4 ++ kernel/ipc.c | 11 ++-- kernel/ktimer.c | 1 + kernel/sched-rr/build.mk | 6 ++ kernel/sched-rr/sched_rr.c | 103 ++++++++++++++++++++++++++++++++++ kernel/thread.c | 8 ++- 12 files changed, 170 insertions(+), 8 deletions(-) create mode 100644 include/sched-rr/sched_rr.h create mode 100644 kernel/sched-rr/build.mk create mode 100644 kernel/sched-rr/sched_rr.c diff --git a/Makefile b/Makefile index 774889bf..11133630 100644 --- a/Makefile +++ b/Makefile @@ -46,6 +46,7 @@ includes = \ board/$(BOARD) \ include \ include/platform \ + include/sched-rr \ .\ $(eval BOARD_$(BOARD)=y) @@ -64,6 +65,7 @@ include platform/$(CHIP)/build.mk include platform/$(PLATFORM)-common/build.mk include platform/build.mk include kernel/lib/build.mk +include kernel/sched-rr/build.mk include kernel/build.mk include user/build.mk include loader/build.mk @@ -80,6 +82,7 @@ all-y += $(call objs_from_dir,platform/$(PLATFORM)-common,platform-common) all-y += $(call objs_from_dir,board/$(BOARD),board) all-y += $(call objs_from_dir,platform,platform) all-y += $(call objs_from_dir,kernel/lib,kernel-lib) +all-y += $(call objs_from_dir,kernel/sched-rr,kernel-sched-rr) all-y += $(call objs_from_dir,kernel,kernel) all-y += $(call objs_from_dir,user,user) @@ -93,6 +96,7 @@ loader-all-y += $(call objs_from_dir,kernel,loader-kernel) dirs = \ kernel/lib \ + kernel/sched-rr \ kernel \ platform/$(CHIP) \ platform/$(PLATFORM)-common \ diff --git a/board/discoveryf4/defconfig b/board/discoveryf4/defconfig index 61659c01..90a2a585 100644 --- a/board/discoveryf4/defconfig +++ b/board/discoveryf4/defconfig @@ -46,6 +46,7 @@ CONFIG_SMALLEST_FPAGE_SHIFT=8 # Thread tweaks # CONFIG_INTR_THREAD_MAX=256 +CONFIG_THREAD_TIME_SLICE=4096 # # KIP tweaks diff --git a/board/discoveryf429/defconfig b/board/discoveryf429/defconfig index b96ea5c4..ae8de1fb 100644 --- a/board/discoveryf429/defconfig +++ b/board/discoveryf429/defconfig @@ -132,6 +132,7 @@ CONFIG_SMALLEST_FPAGE_SHIFT=8 # Thread tweaks # CONFIG_INTR_THREAD_MAX=256 +CONFIG_THREAD_TIME_SLICE=4096 # # KIP tweaks diff --git a/board/stm32p103/defconfig b/board/stm32p103/defconfig index f6e589cf..abb2d6c5 100644 --- a/board/stm32p103/defconfig +++ b/board/stm32p103/defconfig @@ -94,6 +94,7 @@ CONFIG_SMALLEST_FPAGE_SHIFT=8 # Thread tweaks # CONFIG_INTR_THREAD_MAX=256 +CONFIG_THREAD_TIME_SLICE=4096 # # KIP tweaks diff --git a/include/sched-rr/sched_rr.h b/include/sched-rr/sched_rr.h new file mode 100644 index 00000000..f48acf27 --- /dev/null +++ b/include/sched-rr/sched_rr.h @@ -0,0 +1,31 @@ +/* Copyright (c) 2016 The F9 Microkernel Project. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + + +#ifndef SCHED_RR_H_ +#define SCHED_RR_H_ + +#define MAX_QUEUE (CONFIG_MAX_THREADS + 1) +#define TIME_SLICE CONFIG_THREAD_TIME_SLICE + +typedef struct cqueue { + uint32_t Queue[MAX_QUEUE]; + int front; + int rear; + int count; + + void (*put)(struct cqueue *q, void *data); + void *(*get)(struct cqueue *q); + int (*is_empty)(struct cqueue *q); + int (*is_full)(struct cqueue *q); + int (*length)(struct cqueue *q); +} cqueue_t; + + +tcb_t *rr_select(void); +void rr_init_thread(tcb_t *thr); +uint32_t rr_exhaust_timeslice(void *data); + +#endif diff --git a/include/thread.h b/include/thread.h index 9346ee89..46085a3e 100644 --- a/include/thread.h +++ b/include/thread.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2013 The F9 Microkernel Project. All rights reserved. +/* Copyright (c) 2013, 2016 The F9 Microkernel Project. All rights reserved. * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ @@ -54,7 +54,9 @@ typedef enum { T_RUNNABLE, T_SVC_BLOCKED, T_RECV_BLOCKED, - T_SEND_BLOCKED + T_SEND_BLOCKED, + T_WAIT_SCHEDULE, /* Wait to append to scheduler */ + T_PENDING_SCHED, /* Wait for execute in scheculer */ } thread_state_t; typedef struct { @@ -95,6 +97,7 @@ struct tcb { struct tcb *t_child; uint32_t timeout_event; + uint32_t (*timeslice_event)(void *); }; typedef struct tcb tcb_t; diff --git a/kernel/Kconfig b/kernel/Kconfig index 39f8ffef..55d53a43 100644 --- a/kernel/Kconfig +++ b/kernel/Kconfig @@ -71,6 +71,10 @@ menu "Thread tweaks" config INTR_THREAD_MAX int "Maximum of interrupt threads" default 256 + +config THREAD_TIME_SLICE + int "Maximum timeslice for threads" + default 4096 endmenu menu "KIP tweaks" diff --git a/kernel/ipc.c b/kernel/ipc.c index 3a574aaa..0c2b6e71 100644 --- a/kernel/ipc.c +++ b/kernel/ipc.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013, 2014 The F9 Microkernel Project. All rights reserved. +/* Copyright (c) 2013, 2014, 2016 The F9 Microkernel Project. All rights reserved. * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ @@ -16,6 +16,7 @@ #include #include #include +#include extern tcb_t *caller; @@ -241,7 +242,6 @@ void sys_ipc(uint32_t *param1) /* mr1: thread func, mr2: stack addr, mr3: stack size*/ /* mr4: thread entry, mr5: thread args */ /* thread start protocol */ - memptr_t sp = ipc_read_mr(caller, 2); size_t stack_size = ipc_read_mr(caller, 3); uint32_t regs[4]; /* r0, r1, r2, r3 */ @@ -260,8 +260,11 @@ void sys_ipc(uint32_t *param1) caller->state = T_RUNNABLE; - /* Start thread */ - to_thr->state = T_RUNNABLE; + /* make a timeslice for thread*/ + dbg_printf(DL_IPC, "IPC: making timeslice for: %x @[%x]\n", + to_tid, + to_thr); + rr_init_thread(to_thr); return; } else { diff --git a/kernel/ktimer.c b/kernel/ktimer.c index d7cff1fd..7528c009 100644 --- a/kernel/ktimer.c +++ b/kernel/ktimer.c @@ -328,6 +328,7 @@ void ktimer_enter_tickless() uint32_t tickless_delta; uint32_t reload; + dbg_printf(DL_KTIMER, "KTE: Handle ticklesss event\n"); irq_disable(); if (ktimer_enabled && ktimer_delta <= KTIMER_MAXTICKS) { diff --git a/kernel/sched-rr/build.mk b/kernel/sched-rr/build.mk new file mode 100644 index 00000000..923d647a --- /dev/null +++ b/kernel/sched-rr/build.mk @@ -0,0 +1,6 @@ +# Copyright (c) 2016 The F9 Microkernel Project. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +kernel-sched-rr-y = \ + sched_rr.o diff --git a/kernel/sched-rr/sched_rr.c b/kernel/sched-rr/sched_rr.c new file mode 100644 index 00000000..c727f945 --- /dev/null +++ b/kernel/sched-rr/sched_rr.c @@ -0,0 +1,103 @@ +/* Copyright (c) 2016 The F9 Microkernel Project. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include +#include +#include +#include + +void put(cqueue_t *q, void *data) +{ + if (!q->is_full(q)) { + q->count++; + q->rear = (q->rear + 1) % (MAX_QUEUE); + q->Queue[q->rear] = (uint32_t) data; + } +} + +void *get(cqueue_t *q) +{ + uint32_t ret; + + if (!q->is_empty(q)) { + q->count--; + ret = q->Queue[++q->front % MAX_QUEUE]; + if (q->front % MAX_QUEUE == 0) { + q->front = 0; + } + + return (void *) ret; + } + + return NULL; +} + +int is_empty(cqueue_t *q) +{ + return q->count == 0; +} + +int is_full(cqueue_t *q) +{ + return q->count == MAX_QUEUE - 1; +} + +int length(cqueue_t *q) +{ + return q->count; +} + +cqueue_t schedQueue = { + .Queue = { 0 }, + .front = -1, + .rear = -1, + .count = 0, + .put = put, + .get = get, + .is_empty = is_empty, + .is_full = is_full, + .length = length, +}; + +void rr_init_thread(tcb_t *thr) +{ + thr->timeslice_event = rr_exhaust_timeslice; + thr->state = T_WAIT_SCHEDULE; + schedQueue.put(&schedQueue, (void *) thr); +} + +uint32_t rr_exhaust_timeslice(void *data) +{ + /* Get thread in data */ + tcb_t *thr = (tcb_t *) ((ktimer_event_t *)data)->data; + + /* Check thread state to schedule */ + if (thr->state == T_RUNNABLE) { + thr->state = T_WAIT_SCHEDULE; + schedQueue.put(&schedQueue, (void *) thr); + } + + return 0; +} + +tcb_t *rr_select(void) +{ + tcb_t *thr = NULL; + + while (!schedQueue.is_empty(&schedQueue)) { + thr = (tcb_t *) schedQueue.get(&schedQueue); + + if (thr->state == T_WAIT_SCHEDULE) { + thr->state = T_PENDING_SCHED; + schedQueue.put(&schedQueue, (void *) thr); + } else if (thr->state == T_PENDING_SCHED) { + thr->state = T_RUNNABLE; + ktimer_event_create(TIME_SLICE, thr->timeslice_event, thr); + return thr; + } + } + + return NULL; +} diff --git a/kernel/thread.c b/kernel/thread.c index 21843132..78df2e2e 100644 --- a/kernel/thread.c +++ b/kernel/thread.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013, 2014 The F9 Microkernel Project. All rights reserved. +/* Copyright (c) 2013, 2014, 2016 The F9 Microkernel Project. All rights reserved. * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ @@ -12,6 +12,7 @@ #include #include #include +#include /** * @file thread.c @@ -411,6 +412,9 @@ static tcb_t *thread_select(tcb_t *parent) if (thr == NULL) return NULL; + if ((thr = rr_select()) != NULL) + return thr; + while (1) { if (thread_isrunnable(thr)) return thr; @@ -467,7 +471,7 @@ void kdb_dump_threads(void) tcb_t *thr; int idx; - char *state[] = { "FREE", "RUN", "SVC", "RECV", "SEND" }; + char *state[] = { "FREE", "RUN", "SVC", "RECV", "SEND", "SCHED", "PEND"}; dbg_printf(DL_KDB, "%5s %8s %8s %6s %s\n", "type", "global", "local", "state", "parent");