diff options
author | NIIBE Yutaka <gniibe@fsij.org> | 2017-06-23 09:05:11 +0900 |
---|---|---|
committer | NIIBE Yutaka <gniibe@fsij.org> | 2017-06-23 09:05:11 +0900 |
commit | 78c825afe83ff20e43fb680e16d5b86ccbe04fe1 (patch) | |
tree | eadfb251e612c00cc309a3335082fe8714ba69a5 | |
parent | 1bd14d8d40b7132fb2648df8cccb36f73532b17c (diff) |
Add GNU/Linux support (not yet makefile).
-rw-r--r-- | ChangeLog | 2 | ||||
-rw-r--r-- | chopstx-gnu-linux.c | 342 | ||||
-rw-r--r-- | chopstx-gnu-linux.h | 9 |
3 files changed, 353 insertions, 0 deletions
@@ -1,5 +1,7 @@ 2017-06-23 NIIBE Yutaka <gniibe@fsij.org> + * chopstx-gnu-linux.c, chopstx-gnu-linux.h: New. + * chopstx.c (chx_init): Use chx_init_arch. (chopstx_create): Use chopstx_create_arch. (chx_systick_reset, chx_systick_reload, chx_systick_get) diff --git a/chopstx-gnu-linux.c b/chopstx-gnu-linux.c new file mode 100644 index 0000000..b7b6767 --- /dev/null +++ b/chopstx-gnu-linux.c @@ -0,0 +1,342 @@ +/* + * chopstx-gnu-linux.c - Threads and only threads: Arch specific code + * for GNU/Linux emulation + * + * Copyright (C) 2017 Flying Stone Technology + * Author: NIIBE Yutaka <gniibe@fsij.org> + * + * This file is a part of Chopstx, a thread library for embedded. + * + * Chopstx is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Chopstx is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * As additional permission under GNU GPL version 3 section 7, you may + * distribute non-source form of the Program without the copy of the + * GNU GPL normally required by section 4, provided you inform the + * receipents of GNU GPL by a written offer. + * + */ + +#include <unistd.h> +#include <ucontext.h> +#include <signal.h> +#include <sys/time.h> + +static sigset_t ss_cur; + +static void +chx_systick_reset (void) +{ + const struct itimerval it = { {0, 0}, {0, 0} }; + + setitimer (ITIMER_REAL, &it, 0); +} + +static void +chx_systick_reload (uint32_t ticks) +{ + struct itimerval it; + + it.it_value.tv_sec = 0; + it.it_value.tv_usec = (ticks / MHZ); + it.it_interval.tv_sec = 0; + it.it_interval.tv_usec = 0; + + setitimer (ITIMER_REAL, &it, 0); +} + +static uint32_t +chx_systick_get (void) +{ + struct itimerval it; + getitimer (ITIMER_REAL, &it); + return it.it_value.tv_usec * 72; +} + +static uint32_t +usec_to_ticks (uint32_t usec) +{ + return usec * MHZ; +} + + +static void +chx_enable_intr (uint8_t irq_num) +{ + sigdelset (&ss_cur, irq_num); +} + +static void +chx_clr_intr (uint8_t irq_num) +{ /* Clear pending interrupt. */ + (void)irq_num; +} + +static void +chx_disable_intr (uint8_t irq_num) +{ + sigaddset (&ss_cur, irq_num); +} + +static void +chx_set_intr_prio (uint8_t n) +{ + (void)n; +} + +static void +chx_prio_init (void) +{ +} + +static void +chx_cpu_sched_lock (void) +{ + sigset_t ss; + + sigfillset (&ss); + pthread_sigmask (SIG_BLOCK, &ss, &ss_cur); +} + +static void +chx_cpu_sched_unlock (void) +{ + pthread_sigmask (SIG_SETMASK, &ss_cur, NULL); +} + +static void +idle (void) +{ + for (;;) + pause (); +} + +void +chx_handle_intr (uint32_t irq_num) +{ + struct chx_pq *p; + + chx_disable_intr (irq_num); + chx_spin_lock (&q_intr.lock); + for (p = q_intr.q.next; p != (struct chx_pq *)&q_intr.q; p = p->next) + if (p->v == irq_num) + { /* should be one at most. */ + struct chx_px *px = (struct chx_px *)p; + + ll_dequeue (p); + chx_wakeup (p); + chx_spin_unlock (&q_intr.lock); + chx_request_preemption (px->master->prio); + return; + } + chx_spin_unlock (&q_intr.lock); +} + + +static ucontext_t idle_tc; +static char idle_stack[4096]; + +static struct chx_thread main_thread; + +void +chx_sigmask (ucontext_t *uc) +{ + /* Modify oldmask to SS_CUR, so that the signal mask will + * be set to SS_CUR. + * + * In user-level, sigset_t is big, but only the first word + * is used by the kernel. + */ + memcpy (&uc->uc_sigmask, &ss_cur, sizeof (uint64_t)); +} + +static void +sigalrm_handler (int sig, siginfo_t *siginfo, void *arg) +{ + ucontext_t *uc = arg; + (void)sig; + (void)siginfo; + chx_timer_expired (); + chx_sigmask (uc); +} + +static void +chx_init_arch (struct chx_thread *tp) +{ + struct sigaction sa; + + sigemptyset (&ss_cur); + + sa.sa_sigaction = sigalrm_handler; + sigfillset (&sa.sa_mask); + sa.sa_flags = SA_SIGINFO|SA_RESTART; + sigaction (SIGALRM, &sa, NULL); + + getcontext (&idle_tc); + idle_tc.uc_stack.ss_sp = idle_stack; + idle_tc.uc_stack.ss_size = sizeof (idle_stack); + idle_tc.uc_link = NULL; + makecontext (&idle_tc, idle, 0); + + getcontext (&tp->tc); +} + +static void +chx_request_preemption (uint16_t prio) +{ + struct chx_thread *tp, *tp_prev; + ucontext_t *tcp; + + if (running && (uint16_t)running->prio >= prio) + return; + + /* Change the context to another thread with higher priority. */ + tp = tp_prev = running; + if (tp) + { + if (tp->flag_sched_rr) + { + if (tp->state == THREAD_RUNNING) + { + chx_timer_dequeue (tp); + chx_ready_enqueue (tp); + } + } + else + chx_ready_push (tp); + running = NULL; + } + + tp = running = chx_ready_pop (); + if (tp) + { + tcp = &tp->tc; + if (tp->flag_sched_rr) + { + chx_spin_lock (&q_timer.lock); + tp = chx_timer_insert (tp, PREEMPTION_USEC); + chx_spin_unlock (&q_timer.lock); + } + } + else + tcp = &idle_tc; + + if (tp_prev) + { + /* + * The swapcontext implementation may reset sigmask in the + * middle of its execution, unfortunately. It is best if + * sigmask restore is done at the end of the routine, but we + * can't assume that. + * + * Thus, there might be a race condition with regards to the + * user context TCP, if signal mask is cleared and signal comes + * in. To avoid this situation, we block signals. + * + * We don't need to fill the mask here. It keeps the condition + * of blocking signals before&after swapcontext call. It is + * done by the signal mask for sigaction, the initial creation + * of the thread, and the condition of chx_sched function which + * mandates holding cpu_sched_lock. + */ + swapcontext (&tp_prev->tc, tcp); + } + else if (tp) + { + setcontext (tcp); + } +} + +/* + * chx_sched: switch to another thread. + * + * There are two cases: + * YIELD=0 (SLEEP): Current RUNNING thread is already connected to + * something (mutex, cond, intr, etc.) + * YIELD=1 (YIELD): Current RUNNING thread is active, + * it is needed to be enqueued to READY queue. + * + * Returns: + * 1 on wakeup by others. + * 0 on normal wakeup. + * -1 on cancellation. + */ +static uintptr_t +chx_sched (uint32_t yield) +{ + struct chx_thread *tp, *tp_prev; + uintptr_t v; + ucontext_t *tcp; + + tp = tp_prev = running; + if (yield) + { + if (tp->flag_sched_rr) + chx_timer_dequeue (tp); + chx_ready_enqueue (tp); + } + + running = tp = chx_ready_pop (); + if (tp) + { + v = tp->v; + if (tp->flag_sched_rr) + { + chx_spin_lock (&q_timer.lock); + tp = chx_timer_insert (tp, PREEMPTION_USEC); + chx_spin_unlock (&q_timer.lock); + } + tcp = &tp->tc; + } + else + { + v = 0; + tcp = &idle_tc; + } + + swapcontext (&tp_prev->tc, tcp); + chx_cpu_sched_unlock (); + return v; +} + +static void __attribute__((__noreturn__)) +chx_thread_start (voidfunc thread_entry, void *arg) +{ + chx_cpu_sched_unlock (); + thread_entry (arg); + chopstx_exit (0); +} + +static struct chx_thread * +chopstx_create_arch (uintptr_t stack_addr, size_t stack_size, + voidfunc thread_entry, void *arg) +{ + tp = malloc (sizeof (struct chx_thread)); + if (!tp) + chx_fatal (CHOPSTX_ERR_THREAD_CREATE); + + /* + * Calling getcontext with sched_lock held, the context is with + * signal blocked. The sigmask will be cleared in chx_thread_start. + */ + chx_cpu_sched_lock (); + getcontext (&tp->tc); + tp->tc.uc_stack.ss_sp = (void *)stack_addr; + tp->tc.uc_stack.ss_size = stack_size; + tp->tc.uc_link = NULL; + + makecontext (&tp->tc, (void (*)(void))chx_thread_start, + 4, thread_entry, arg); + chx_cpu_sched_unlock (); + return tp; +} diff --git a/chopstx-gnu-linux.h b/chopstx-gnu-linux.h new file mode 100644 index 0000000..cce34f1 --- /dev/null +++ b/chopstx-gnu-linux.h @@ -0,0 +1,9 @@ +/* + * The thread context: specific to GNU/Linux. + * + * We use the type ucontext_t, which includes all registers; + * Note that signal mask is also included in ucontext_t. + * + */ + +typedef ucontext_t tcontext_t; |