From cffc8bf96cbfedbce18a04ece281023277f0935b Mon Sep 17 00:00:00 2001 From: NIIBE Yutaka Date: Thu, 21 Nov 2019 10:35:14 +0900 Subject: [PATCH] Removal of CHOPSTX_PRIO_INHIBIT_PREEMPTION. Signed-off-by: NIIBE Yutaka --- ChangeLog | 37 +++++++++++ NEWS | 13 ++++ chopstx-cortex-m.c | 157 +++++++++++++++----------------------------- chopstx-gnu-linux.c | 77 +++++++--------------- chopstx.c | 83 ++++++++++++++++++++--- chopstx.h | 2 - entry.c | 20 +++++- 7 files changed, 221 insertions(+), 168 deletions(-) diff --git a/ChangeLog b/ChangeLog index 032d590..2d3a285 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,40 @@ +2019-11-21 NIIBE Yutaka + + * chopstx.h (CHOPSTX_PRIO_INHIBIT_PREEMPTION): Remove. + * chopstx.c (chx_init): Remove support of + CHOPSTX_PRIO_INHIBIT_PREEMPTION. + (chopstx_setpriority): Likewise. + + * chopstx-cortex-m.c (CPU_EXCEPTION_PRIORITY_PENDSV): Change the + value. Now PendSV exception runs at same priority level of + all other exceptions (systick and interrupt exception). + (chx_cpu_sched_lock, chx_cpu_sched_unlock): + Remove support of CHOPSTX_PRIO_INHIBIT_PREEMPTION. + (chx_request_preemption): Change the argument with none. + (chx_timer_handler): New. Use common function of + chx_timer_expired, local one, chx_request_preemption. + (chx_handle_intr): Use chx_recv_irq. + (chx_sched): Remove support of CHOPSTX_PRIO_INHIBIT_PREEMPTION. + (preempt): Have an argument from tail-chaining. + Remove support of CHOPSTX_PRIO_INHIBIT_PREEMPTION. + + * chopstx-gnu-linux.c (chx_idle): Rename. + (chx_init_arch): Follow the rename. + (chx_handle_intr): Use chx_recv_irq, chx_running_preempted, and + chx_preempt_into. + (sigalrm_handler): Use chx_timer_expired, chx_running_preempted, + and chx_preempt_into. + (chx_preempt_into): Rename from chx_request_preemption, changing + argument. Remove the handling of preempted thread, which should + be done by caller using chx_running_preempted, beforehand. + + * entry.c (vector_table): Use chx_timer_handler. + + * chopstx.c (chx_timer_expired): Pop from the ready queue and + return the next thread to switch. + (chx_recv_irq): New. + (chx_running_preempted): New. + 2019-11-21 NIIBE Yutaka * chopstx-cortex-m.c (chx_set_running): New. diff --git a/NEWS b/NEWS index 56469c2..9bf00b2 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,19 @@ NEWS - Noteworthy changes +* Major changes in Chopstx 2.0 + +** Remove support of CHOPSTX_PRIO_INHIBIT_PREEMPTION +We used to have (an experimental) feature of higher priority of thread +(>= CHOPSTX_PRIO_INHIBIT_PREEMPTION). The feature offered: When a +thread has such a priority, Chostx guarantees that it keeps running +and no other threads can preempt the thread until the thread +voluntarily leaves running. I had introduced this (questionable) +feature as an excuse against no support of masking/unmasking interrupt +API. From the experience of Chopstx, it is rather consistent to +remove this feature. + + * Major changes in Chopstx 1.17 Released 2019-11-20 diff --git a/chopstx-cortex-m.c b/chopstx-cortex-m.c index c72ff0a..89315a3 100644 --- a/chopstx-cortex-m.c +++ b/chopstx-cortex-m.c @@ -76,17 +76,15 @@ struct chx_stack_regs { * --------------------- * Prio 0x40: thread temporarily inhibiting schedule for critical region * ... - * Prio 0xb0: systick, external interrupt - * Prio 0xc0: pendsv + * Prio 0xb0: systick, external interrupt, pendsv * ===================================== * * Cortex-M0 * ===================================== * Prio 0x00: thread temporarily inhibiting schedule for critical region * ... - * Prio 0x40: systick, external interrupt - * Prio 0x80: pendsv - * Prio 0x80: svc + * Prio 0x40: systick, external interrupt, pendsv + * Prio 0x80: svc (not used) * ===================================== */ @@ -95,18 +93,18 @@ struct chx_stack_regs { #if defined(__ARM_ARCH_6M__) #define CPU_EXCEPTION_PRIORITY_INHIBIT_SCHED 0x00 /* ... */ -#define CPU_EXCEPTION_PRIORITY_SYSTICK CPU_EXCEPTION_PRIORITY_INTERRUPT -#define CPU_EXCEPTION_PRIORITY_INTERRUPT 0x40 -#define CPU_EXCEPTION_PRIORITY_PENDSV 0x80 +#define CPU_EXCEPTION_PRIORITY_SYSTICK 0x40 +#define CPU_EXCEPTION_PRIORITY_INTERRUPT CPU_EXCEPTION_PRIORITY_SYSTICK +#define CPU_EXCEPTION_PRIORITY_PENDSV CPU_EXCEPTION_PRIORITY_SYSTICK #define CPU_EXCEPTION_PRIORITY_SVC 0x80 /* No use in this arch */ #elif defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7EM__) #define CPU_EXCEPTION_PRIORITY_SVC 0x30 #define CPU_EXCEPTION_PRIORITY_INHIBIT_SCHED 0x40 /* ... */ -#define CPU_EXCEPTION_PRIORITY_SYSTICK CPU_EXCEPTION_PRIORITY_INTERRUPT -#define CPU_EXCEPTION_PRIORITY_INTERRUPT 0xb0 -#define CPU_EXCEPTION_PRIORITY_PENDSV 0xc0 +#define CPU_EXCEPTION_PRIORITY_SYSTICK 0xb0 +#define CPU_EXCEPTION_PRIORITY_INTERRUPT CPU_EXCEPTION_PRIORITY_SYSTICK +#define CPU_EXCEPTION_PRIORITY_PENDSV CPU_EXCEPTION_PRIORITY_SYSTICK #else #error "no support for this arch" #endif @@ -238,55 +236,57 @@ chx_interrupt_controller_init (void) static void chx_cpu_sched_lock (void) { - if (running->prio < CHOPSTX_PRIO_INHIBIT_PREEMPTION) - { #if defined(__ARM_ARCH_6M__) - asm volatile ("cpsid i" : : : "memory"); + asm volatile ("cpsid i" : : : "memory"); #else - register uint32_t tmp = CPU_EXCEPTION_PRIORITY_INHIBIT_SCHED; - asm volatile ("msr BASEPRI, %0" : : "r" (tmp) : "memory"); + register uint32_t tmp = CPU_EXCEPTION_PRIORITY_INHIBIT_SCHED; + asm volatile ("msr BASEPRI, %0" : : "r" (tmp) : "memory"); #endif - } } static void chx_cpu_sched_unlock (void) { - if (running->prio < CHOPSTX_PRIO_INHIBIT_PREEMPTION) - { #if defined(__ARM_ARCH_6M__) - asm volatile ("cpsie i" : : : "memory"); + asm volatile ("cpsie i" : : : "memory"); #else - register uint32_t tmp = CPU_EXCEPTION_PRIORITY_CLEAR; - asm volatile ("msr BASEPRI, %0" : : "r" (tmp) : "memory"); + register uint32_t tmp = CPU_EXCEPTION_PRIORITY_CLEAR; + asm volatile ("msr BASEPRI, %0" : : "r" (tmp) : "memory"); #endif - } } -void +static void +chx_request_preemption (void) +{ + *ICSR = (1 << 28); + asm volatile ("" : : : "memory"); +} + +struct chx_thread * +chx_timer_handler (void) +{ + struct chx_thread *tp_next; + tp_next = chx_timer_expired (); + if (tp_next) + chx_request_preemption (); + return tp_next; +} + +struct chx_thread * chx_handle_intr (void) { - struct chx_pq *p; register uint32_t irq_num; + struct chx_thread *tp_next; asm volatile ("mrs %0, IPSR\n\t" "sub %0, #16" /* Exception # - 16 = interrupt number. */ : "=r" (irq_num) : /* no input */ : "memory"); - 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_request_preemption (px->master->prio); - break; - } - chx_spin_unlock (&q_intr.lock); + tp_next = chx_recv_irq (irq_num); + if (tp_next) + chx_request_preemption (); + return tp_next; } static void @@ -296,16 +296,6 @@ chx_init_arch (struct chx_thread *tp) chx_set_running (tp); } -static void -chx_request_preemption (uint16_t prio) -{ - if (running == NULL || (uint16_t)running->prio < prio) - { - *ICSR = (1 << 28); - asm volatile ("" : : : "memory"); - } -} - /* * chx_sched: switch to another thread. @@ -417,15 +407,9 @@ chx_sched (uint32_t yield) "ldm r0!, {r1, r2}\n\t" "mov r11, r1\n\t" "mov sp, r2\n\t" - "sub r0, #45\n\t" - "ldrb r1, [r0]\n\t" /* ->PRIO field. */ - "cmp r1, #247\n\t" - "bhi 1f\n\t" /* Leave interrupt disabled if >= 248 */ /**/ /* Unmask interrupts. */ - "cpsie i\n" - /**/ - "1:\n\t" + "cpsie i\n\t" /* 0: r0 4: r1 @@ -520,26 +504,18 @@ chopstx_create_arch (uintptr_t stack_addr, size_t stack_size, */ void __attribute__ ((naked)) -preempt (void) +preempt (struct chx_thread * tp_next) { - register struct chx_thread *tp asm ("r0"); - register struct chx_thread *cur asm ("r1"); + register struct chx_thread *tp_current asm ("r1"); - asm volatile ( -#if defined(__ARM_ARCH_6M__) - "cpsid i\n\t" -#else - "msr BASEPRI, r0\n\t" -#endif - "ldr r2, =running\n\t" - "ldr r0, [r2]\n\t" - "mov r1, r0" - : "=r" (tp), "=r" (cur) - : "0" (CPU_EXCEPTION_PRIORITY_INHIBIT_SCHED) + asm ( "ldr r2, =running\n\t" + "ldr r1, [r2]" + : "=r" (tp_current) + : /* no input */ : "r2"); - if (!cur) - /* It's idle thread. It's ok to clobber registers. */ + if (!tp_current) + /* It's idle thread. No need to save registers. */ ; else { @@ -553,38 +529,20 @@ preempt (void) "mov r5, r11\n\t" "mrs r6, PSP\n\t" /* r13(=SP) in user space. */ "stm %0!, {r2, r3, r4, r5, r6}" - : "=r" (cur) - : "0" (cur) - /* + : "=r" (tp_current) + : "0" (tp_current) + /* * Memory clobber constraint here is not accurate, but this * works. R7 keeps its value, but having "r7" here prevents * use of R7 before this asm statement. */ : "r2", "r3", "r4", "r5", "r6", "r7", "memory"); - if (tp) - { - if (tp->flag_sched_rr) - { - if (tp->state == THREAD_RUNNING) - { - chx_timer_dequeue (tp); - chx_ready_enqueue (tp); - } - /* - * It may be THREAD_READY after chx_timer_expired. - * Then, do nothing. - */ - } - else - chx_ready_push (tp); - } + tp_next = chx_running_preempted (tp_next); } /* Registers on stack (PSP): r0, r1, r2, r3, r12, lr, pc, xpsr */ - tp = chx_ready_pop (); - asm volatile ( ".L_CONTEXT_SWITCH:\n\t" /* Now, r0 points to the thread to be switched. */ @@ -617,20 +575,13 @@ preempt (void) "ldr r1, [r0], #4\n\t" "msr PSP, r1\n\t" #endif - "sub r0, #45\n\t" - "ldrb r1, [r0]\n\t" /* ->PRIO field. */ - "mov r0, #0\n\t" - "cmp r1, #247\n\t" - "bhi 0f\n\t" /* Leave interrupt disabled if >= 248 */ - /**/ /* Unmask interrupts. */ #if defined(__ARM_ARCH_6M__) - "cpsie i\n" + "cpsie i\n\t" #else - "msr BASEPRI, r0\n" + "msr BASEPRI, r0\n\t" #endif /**/ - "0:\n\t" "sub r0, #3\n\t" /* EXC_RETURN to a thread with PSP */ "bx r0\n" "1:\n\t" @@ -657,7 +608,7 @@ preempt (void) /**/ "sub r0, #3\n\t" /* EXC_RETURN to a thread with PSP */ "bx r0" - : /* no output */ : "r" (tp) : "memory"); + : /* no output */ : "r" (tp_next) : "memory"); } #if defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7EM__) diff --git a/chopstx-gnu-linux.c b/chopstx-gnu-linux.c index a0fa510..ad200b9 100644 --- a/chopstx-gnu-linux.c +++ b/chopstx-gnu-linux.c @@ -54,6 +54,8 @@ chx_dmb (void) } +static void chx_preempt_into (struct chx_thread *tp_next); + static sigset_t ss_cur; static void @@ -145,7 +147,7 @@ chx_cpu_sched_unlock (void) } static void -idle (void) +chx_idle (void) { for (;;) pause (); @@ -154,22 +156,14 @@ idle (void) void chx_handle_intr (uint32_t irq_num) { - struct chx_pq *p; + struct chx_thread *tp_next; - 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; + tp_next = chx_recv_irq (irq_num); + if (!tp_next) + return; - 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); + tp_next = chx_running_preempted (tp_next); + chx_preempt_into (tp_next); } @@ -193,11 +187,17 @@ chx_sigmask (ucontext_t *uc) static void sigalrm_handler (int sig, siginfo_t *siginfo, void *arg) { - extern void chx_timer_expired (void); + struct chx_thread *tp_next; ucontext_t *uc = arg; (void)sig; (void)siginfo; - chx_timer_expired (); + + tp_next = chx_timer_expired (); + if (tp_next) + { + tp_next = chx_running_preempted (tp_next); + chx_preempt_into (tp_next); + } chx_sigmask (uc); } @@ -217,46 +217,17 @@ chx_init_arch (struct chx_thread *tp) 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); + makecontext (&idle_tc, chx_idle, 0); getcontext (&tp->tc); - chx_set_running (tp); } static void -chx_request_preemption (uint16_t prio) +chx_preempt_into (struct chx_thread *tp_next) { - ucontext_t *tcp; - struct chx_thread *tp_prev; - struct chx_thread *tp = chx_running (); - - if (tp && (uint16_t)tp->prio >= prio) - return; - - /* Change the context to another thread with higher priority. */ - tp_prev = tp; - 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); - } - - tp = chx_ready_pop (); - if (tp) - tcp = &tp->tc; - else - tcp = &idle_tc; - - chx_set_running (tp); + struct chx_thread *tp_prev = chx_running (); + chx_set_running (tp_next); if (tp_prev) { /* @@ -275,11 +246,11 @@ chx_request_preemption (uint16_t prio) * of the thread, and the condition of chx_sched function which * mandates holding cpu_sched_lock. */ - swapcontext (&tp_prev->tc, tcp); + swapcontext (&tp_prev->tc, &tp_next->tc); } - else if (tp) + else { - setcontext (tcp); + setcontext (&tp_next->tc); } } diff --git a/chopstx.c b/chopstx.c index 1cf8187..1b51f47 100644 --- a/chopstx.c +++ b/chopstx.c @@ -101,9 +101,8 @@ static struct chx_queue q_join; static struct chx_queue q_intr; /* Forward declaration(s). */ -static void chx_request_preemption (uint16_t prio); static int chx_wakeup (struct chx_pq *p); -static struct chx_thread * chx_timer_insert (struct chx_thread *tp, uint32_t usec); +static struct chx_thread *chx_timer_insert (struct chx_thread *tp, uint32_t usec); static uint32_t chx_timer_dequeue (struct chx_thread *tp); @@ -297,6 +296,10 @@ chx_ready_enqueue (struct chx_thread *tp) chx_spin_unlock (&q_ready.lock); } +static struct chx_thread *chx_timer_expired (void); +static struct chx_thread *chx_recv_irq (uint32_t irq_num); +static struct chx_thread *chx_running_preempted (struct chx_thread *tp_next); + /* * Here comes architecture specific code. */ @@ -384,7 +387,7 @@ chx_timer_dequeue (struct chx_thread *tp) } -void +static struct chx_thread * chx_timer_expired (void) { struct chx_thread *tp; @@ -430,7 +433,74 @@ chx_timer_expired (void) } chx_spin_unlock (&q_timer.lock); - chx_request_preemption (prio); + + if (running == NULL || (uint16_t)running->prio < prio) + { + tp = chx_ready_pop (); + if (tp != running) + return tp; + else + /* When tp->flag_sched_rr == 1, it's possible. No context switch. */ + return NULL; + } + else + return NULL; +} + + +static struct chx_thread * +chx_recv_irq (uint32_t irq_num) +{ + struct chx_pq *p; + struct chx_thread *r = chx_running (); + + 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. */ + break; + chx_spin_unlock (&q_intr.lock); + + if (p) + { + struct chx_px *px = (struct chx_px *)p; + + ll_dequeue (p); + chx_wakeup (p); + + if (r == NULL || (uint16_t)r->prio < px->master->prio) + return chx_ready_pop (); + } + + return NULL; +} + + +static struct chx_thread * __attribute__ ((noinline)) +chx_running_preempted (struct chx_thread *tp_next) +{ + struct chx_thread *r = chx_running (); + + if (r == NULL) + return tp_next; + + if (r->flag_sched_rr) + { + if (r->state == THREAD_RUNNING) + { + chx_timer_dequeue (r); + chx_ready_enqueue (r); + } + /* + * It may be THREAD_READY after chx_timer_expired. + * Then, do nothing. It's in the ready queue. + */ + } + else + chx_ready_push (r); + + return tp_next; } @@ -482,9 +552,6 @@ chx_init (struct chx_thread *tp) tp->parent = NULL; tp->v = 0; - if (CHX_PRIO_MAIN_INIT >= CHOPSTX_PRIO_INHIBIT_PREEMPTION) - chx_cpu_sched_lock (); - tp->prio = CHX_PRIO_MAIN_INIT; chopstx_main = (chopstx_t)tp; @@ -1544,7 +1611,7 @@ chopstx_setpriority (chopstx_prio_t prio_new) if (tp->prio < prio_cur) chx_sched (CHX_YIELD); - else if (tp->prio < CHOPSTX_PRIO_INHIBIT_PREEMPTION) + else chx_cpu_sched_unlock (); return prio_orig; diff --git a/chopstx.h b/chopstx.h index 4b8880c..45d7320 100644 --- a/chopstx.h +++ b/chopstx.h @@ -45,8 +45,6 @@ chopstx_create (uint32_t flags_and_prio, #define CHOPSTX_DETACHED 0x10000 #define CHOPSTX_SCHED_RR 0x20000 -#define CHOPSTX_PRIO_INHIBIT_PREEMPTION 248 - void chopstx_usec_wait (uint32_t usec); struct chx_spinlock { diff --git a/entry.c b/entry.c index 5260416..2d90b27 100644 --- a/entry.c +++ b/entry.c @@ -64,8 +64,24 @@ extern uint8_t __main_stack_end__; #if defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7EM__) extern void svc (void); #endif + +/* + * In ARMv6-M Architecture Reference Manual and ARM v7-M Architecture + * Reference Manual, you can find a section B1.5.12 for tail-chaining. + * + * B1.5.12 Exceptions on exception return, and tail-chaining exceptions + */ +/* + * Because it is tail-chained, the preempt function has an argument + * with type of (struct chx_thread *), in fact. + */ extern void preempt (void); -extern void chx_timer_expired (void); + +/* + * Following functions return type of (struct chx_thread *) for + * tail-chained function (the preempt function), for its argument. + */ +extern void chx_timer_handler (void); extern void chx_handle_intr (void); static void nmi (void) @@ -193,7 +209,7 @@ handler vector_table[] __attribute__ ((section(".startup.vectors"))) = { none, /* Debug */ none, /* reserved */ preempt, /* PendSV */ - chx_timer_expired, /* SysTick */ + chx_timer_handler, /* SysTick */ /* 0x40 */ chx_handle_intr /* WWDG */, chx_handle_intr /* PVD */, chx_handle_intr /* TAMPER */, chx_handle_intr /* RTC */,