link もっと前
   2020年 10月 3日 -
      2020年 9月 24日  
link もっと後

link 未来から過去へ表示(*)
link 過去から未来へ表示

2020年 10月 3日

link permalink

link 編集する

Zephyr OS で遊ぼう その 16 - SMP 対応の準備、コンテキストスイッチの実装、後編 1、RISC-V のコンテキストスイッチ

目次: Zephyr を調べる - まとめリンク

前回は、AArch64 の実装を調べました。いよいよ新しい方式のコンテキストスイッチを実装したいところですが、その前にもう一つだけ RISC-V の既存実装を調べます。

RISC-V の 2つのコンテキストスイッチ経路

RISC-V 向け実装において、コンテキストスイッチが行われる条件は 2つあります。1つはスリープしたときなどに呼ばれる明示的なコンテキストスイッチです。do_swap() を経由します。もう 1つは割り込み発生時に行われるプリエンプションです。

明示的コンテキストスイッチ
do_swap() -> arch_switch() ラッパー関数 -> z_riscv_switch() -> ecall -> __irq_wrapper -> is_syscall -> reschedule -> no_reschedule -> mret
プリエンプション
(任意の場所) -> __irq_wrapper -> is_interrupt -> on_irq_stack -> 割り込みハンドラ(isr_timer() など) -> on_thread_stack -> reschedule -> no_reschedule -> mret

明示的コンテキストスイッチについては、以前(2020年 9月 29日の日記参照)実装したラッパー関数がスタート地点となります。コードを変更する前に、従来のコンテキストスイッチがどんな経路を通るか確認します。

arch_switch() ラッパー関数 -> z_riscv_switch() -> ecall -> __irq_wrapper -> is_syscall

// zephyr/arch/riscv/include/kernel_arch_func.h

static inline void arch_switch(void *switch_to, void **switched_from)
{
	z_riscv_switch(switch_to, switched_from);
}


// zephyr/arch/riscv/core/swap.S

/*
 * void z_riscv_switch(void *switch_to, void **switched_from)
 */
SECTION_FUNC(exception.other, z_riscv_switch)

	/* Make a system call to perform context switch */
	ecall    //★例外を発生させる★

	jalr x0, ra


// zephyr/arch/riscv/core/isr.S

/*
 * Handler called upon each exception/interrupt/fault
 * In this architecture, system call (ECALL) is used to perform context
 * switching or IRQ offloading (when enabled).
 */
SECTION_FUNC(exception.entry, __irq_wrapper)
	/* Allocate space on thread stack to save registers */
	addi sp, sp, -__z_arch_esf_t_SIZEOF

...

	/*
	 * Check if exception is the result of an interrupt or not.
	 * (SOC dependent). Following the RISC-V architecture spec, the MSB
	 * of the mcause register is used to indicate whether an exception
	 * is the result of an interrupt or an exception/fault. But for some
	 * SOCs (like pulpino or riscv-qemu), the MSB is never set to indicate
	 * interrupt. Hence, check for interrupt/exception via the __soc_is_irq
	 * function (that needs to be implemented by each SOC). The result is
	 * returned via register a0 (1: interrupt, 0 exception)
	 */
	jal ra, __soc_is_irq

	/* If a0 != 0, jump to is_interrupt */
	addi t1, x0, 0
	bnez a0, is_interrupt    //★割り込みの場合はこちらにジャンプする★

	/*
	 * If the exception is the result of an ECALL, check whether to
	 * perform a context-switch or an IRQ offload. Otherwise call _Fault
	 * to report the exception.
	 */
	csrr t0, mcause
	li t2, SOC_MCAUSE_EXP_MASK
	and t0, t0, t2
	li t1, SOC_MCAUSE_ECALL_EXP

	/*
	 * If mcause == SOC_MCAUSE_ECALL_EXP, handle system call,
	 * otherwise handle fault
	 */
	beq t0, t1, is_syscall    //★ecall の場合はこちらにジャンプする★

	/*
	 * Call _Fault to handle exception.
	 * Stack pointer is pointing to a z_arch_esf_t structure, pass it
	 * to _Fault (via register a0).
	 * If _Fault shall return, set return address to no_reschedule
	 * to restore stack.
	 */
	addi a0, sp, 0
	la ra, no_reschedule
	tail _Fault    //★いずれでもなければ停止させる★

...

Zephyr RISC-V 向け実装では、割り込み・例外ハンドラは 1つだけです。割り込みも例外も全て __irq_wrapper に飛んできますから、最初の方で要因をチェックして仕分けしています。RISC-V の規格としては割り込み要因ごとに別の割り込みハンドラに飛べる形式(ベクタ形式)もありますが、Zephyr は使っていません。

明示的コンテキストスイッチの実行経路: is_syscall -> reschedule -> no_reschedule -> mret


// zephyr/arch/riscv/core/isr.S

is_syscall:
	/*
	 * A syscall is the result of an ecall instruction, in which case the
	 * MEPC will contain the address of the ecall instruction.
	 * Increment saved MEPC by 4 to prevent triggering the same ecall
	 * again upon exiting the ISR.
	 *
	 * It's safe to always increment by 4, even with compressed
	 * instructions, because the ecall instruction is always 4 bytes.
	 */
	RV_OP_LOADREG t0, __z_arch_esf_t_mepc_OFFSET(sp)
	addi t0, t0, 4
	RV_OP_STOREREG t0, __z_arch_esf_t_mepc_OFFSET(sp)

...

	/*
	 * Go to reschedule to handle context-switch
	 */
	j reschedule  //★コンテキストスイッチ★

...

reschedule:

...

	/* Get reference to _kernel */
	la t0, _kernel

	/* Get pointer to _kernel.current */
	RV_OP_LOADREG t1, _kernel_offset_to_current(t0)

	/*
	 * Save callee-saved registers of current thread
	 * prior to handle context-switching
	 */
	RV_OP_STOREREG s0, _thread_offset_to_s0(t1)
	RV_OP_STOREREG s1, _thread_offset_to_s1(t1)
...
	RV_OP_STOREREG s10, _thread_offset_to_s10(t1)
	RV_OP_STOREREG s11, _thread_offset_to_s11(t1)

...

	/*
	 * Save stack pointer of current thread and set the default return value
	 * of z_swap to _k_neg_eagain for the thread.
	 */
	RV_OP_STOREREG sp, _thread_offset_to_sp(t1)
	la t2, _k_neg_eagain
	lw t3, 0x00(t2)
	sw t3, _thread_offset_to_swap_return_value(t1)

	/* Get next thread to schedule. */
	RV_OP_LOADREG t1, _kernel_offset_to_ready_q_cache(t0)

	/*
	 * Set _kernel.current to new thread loaded in t1
	 */
	RV_OP_STOREREG t1, _kernel_offset_to_current(t0)

	/* Switch to new thread stack */
	RV_OP_LOADREG sp, _thread_offset_to_sp(t1)

	/* Restore callee-saved registers of new thread */
	RV_OP_LOADREG s0, _thread_offset_to_s0(t1)
	RV_OP_LOADREG s1, _thread_offset_to_s1(t1)
...
	RV_OP_LOADREG s10, _thread_offset_to_s10(t1)
	RV_OP_LOADREG s11, _thread_offset_to_s11(t1)

...

no_reschedule:

...

	/* Restore MEPC register */
	RV_OP_LOADREG t0, __z_arch_esf_t_mepc_OFFSET(sp)
	csrw mepc, t0

	/* Restore SOC-specific MSTATUS register */
	RV_OP_LOADREG t0, __z_arch_esf_t_mstatus_OFFSET(sp)
	csrw mstatus, t0

...

	/* Restore caller-saved registers from thread stack */
	RV_OP_LOADREG ra, __z_arch_esf_t_ra_OFFSET(sp)
	RV_OP_LOADREG gp, __z_arch_esf_t_gp_OFFSET(sp)
	RV_OP_LOADREG tp, __z_arch_esf_t_tp_OFFSET(sp)
	RV_OP_LOADREG t0, __z_arch_esf_t_t0_OFFSET(sp)
...
	RV_OP_LOADREG a6, __z_arch_esf_t_a6_OFFSET(sp)
	RV_OP_LOADREG a7, __z_arch_esf_t_a7_OFFSET(sp)

	/* Release stack space */
	addi sp, sp, __z_arch_esf_t_SIZEOF

	/* Call SOC_ERET to exit ISR */
	SOC_ERET

コメントが丁寧に書いてあって素晴らしいですね。コンテキストスイッチの手順は AArch64 の実装とほぼ同じですが、AAarch64 は明示的なコンテキストスイッチとプリエンプションが独立して実装されており、RISC-V は reschedule で両者が合流する点が違います。コンテキストスイッチの説明は先日(2020年 10月 1日の日記参照)の紙芝居が参考になるかと思います。

明示的なコンテキストスイッチとプリエンプションの部分が大体仕分けできました。いよいよ実装に挑みます。続きはまた。

[編集者: すずき]
[更新: 2020年 10月 9日 01:13]

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2020年 10月 1日

link permalink

link 編集する

Zephyr OS で遊ぼう その 15 - SMP 対応の準備、コンテキストスイッチの実装、中編 2、既存実装調査

目次: Zephyr を調べる - まとめリンク

前回は、新しいコンテキストスイッチ関数に対応している AArch64 の実装のうち、共通部分からアーキテクチャ依存部分に至るまでを調べました。引き続きアーキテクチャ依存部分を調べます。

アーキ依存部分の処理(AArch64)

コンテキストスイッチのアーキテクチャ依存部分(AArch64 向け arch_switch())の実装はほぼ全てアセンブラで実装されています。

コンテキストスイッチの本体は z_arm64_context_switch です。x0 が new_thread, x1 が old_thread だと思って動きます。コンテキストスイッチの仕事はレジスタの値を切り替え前のスレッド構造体(old_thread)に退避し、切り替え後のスレッド構造体(new_thread)からレジスタの値を復旧させることです。

AArch64 の新しい形式のコンテキストスイッチ arch_switch() 実装

// zephyr/arch/arm/core/aarch64/switch.S

GTEXT(z_arm64_svc)
SECTION_FUNC(TEXT, z_arm64_svc)
	z_arm64_enter_exc x2, x3, x4    /* ★レジスタをスタックに退避(切り替え前のスレッド)★ */

	switch_el x1, 3f, 2f, 1f
3:
	mrs	x0, esr_el3
	b	0f
2:
	mrs	x0, esr_el2
	b	0f
1:
	mrs	x0, esr_el1
0:
	lsr	x1, x0, #26

	cmp	x1, #0x15 /* 0x15 = SVC */
	bne	inv

	/* Demux the SVC call */
	and	x1, x0, #0xff
	cmp	x1, #_SVC_CALL_CONTEXT_SWITCH
	beq	context_switch    /* ★以下参照★ */

...

context_switch:
	/*
	 * Retrieve x0 and x1 from the stack:
	 *  - x0 = new_thread->switch_handle = switch_to thread
	 *  - x1 = x1 = &old_thread->switch_handle = current thread
	 */
	ldp	x0, x1, [sp, #(16 * 10)]    /* ★x1 = 切り替え前、x0 = 切り替え後のスレッド★ */

	/* Get old thread from x1 */
	sub	x1, x1, ___thread_t_switch_handle_OFFSET

	/* Switch thread */
	bl	z_arm64_context_switch    /* ★コンテキストスイッチ本体(スタックが切り替わる)★ */

exit:
	z_arm64_exit_exc x0, x1, x2    /* ★レジスタをスタックから復旧(切り替え後のスレッド)★ */

...

/**
 * @brief Routine to handle context switches
 *
 * This function is directly called either by _isr_wrapper() in case of
 * preemption, or z_arm64_svc() in case of cooperative switching.
 */

GTEXT(z_arm64_context_switch)
SECTION_FUNC(TEXT, z_arm64_context_switch)
	/* addr of callee-saved regs in thread in x2 */
	ldr	x2, =_thread_offset_to_callee_saved
	add	x2, x2, x1

	/* Store rest of process context including x30 */
	stp	x19, x20, [x2], #16
	stp	x21, x22, [x2], #16
	stp	x23, x24, [x2], #16
	stp	x25, x26, [x2], #16
	stp	x27, x28, [x2], #16
	stp	x29, x30, [x2], #16

	/* Save the current SP */
	mov	x1, sp
	str	x1, [x2]

	/* addr of callee-saved regs in thread in x2 */
	ldr	x2, =_thread_offset_to_callee_saved
	add	x2, x2, x0

	/* Restore x19-x29 plus x30 */
	ldp	x19, x20, [x2], #16
	ldp	x21, x22, [x2], #16
	ldp	x23, x24, [x2], #16
	ldp	x25, x26, [x2], #16
	ldp	x27, x28, [x2], #16
	ldp	x29, x30, [x2], #16

	ldr	x1, [x2]
	mov	sp, x1    /* ★ここで切り替え後のスレッドのスタックに変わる★ */

#ifdef CONFIG_TRACING
	stp	xzr, x30, [sp, #-16]!
	bl	sys_trace_thread_switched_in
	ldp	xzr, x30, [sp], #16
#endif

	/* We restored x30 from the process stack. There are three possible
	 * cases:
	 *
	 * - We return to z_arm64_svc() when swapping in a thread that was
	 *   swapped out by z_arm64_svc() before jumping into
	 *   z_arm64_exit_exc()
	 * - We return to _isr_wrapper() when swapping in a thread that was
	 *   swapped out by _isr_wrapper() before jumping into
	 *   z_arm64_exit_exc()
	 * - We return (jump) into z_thread_entry_wrapper() for new threads
	 *   (see thread.c)
	 */
	ret


// zephyr/include/arm/aarch64/thread.h

struct _callee_saved {
	uint64_t x19;
	uint64_t x20;
	uint64_t x21;
	uint64_t x22;
	uint64_t x23;
	uint64_t x24;
	uint64_t x25;
	uint64_t x26;
	uint64_t x27;
	uint64_t x28;
	uint64_t x29; /* FP */
	uint64_t x30; /* LR */
	uint64_t sp;
};

コードを見て一発で理解するのは厳しいので、処理の概要を書いておきます。


Zephyr AArch64 のコンテキストスイッチ概要

コンテキストスイッチの際にスタックが切り替わります。z_arm64_exit_exc x0, x1, x2 はシステムコールを呼んだスレッド(=切り替え前のスレッド)ではなく、コンテキストスイッチ後のスレッドのスタックからレジスタを復旧します。

これも文章だと何だかわからないので、紙芝居を書いておきます。


Zephyr AArch64 のコンテキストスイッチ: 例外ハンドラ先頭


Zephyr AArch64 のコンテキストスイッチ: 例外ハンドラ入り口、スタックへレジスタ退避


Zephyr AArch64 のコンテキストスイッチ: コンテキストスイッチ前半、切り替え前スレッド構造体へレジスタ退避


Zephyr AArch64 のコンテキストスイッチ: コンテキストスイッチ前半、切り替え前スレッド構造体へスタックポインタ退避


Zephyr AArch64 のコンテキストスイッチ: コンテキストスイッチ後半、切り替え後スレッド構造体からレジスタ復旧


Zephyr AArch64 のコンテキストスイッチ: コンテキストスイッチ後半、切り替え後スレッド構造体からスタックポインタ復旧(=スタック切り替え)


Zephyr AArch64 のコンテキストスイッチ: 例外ハンドラ入り口、切り替え後のスタックからレジスタ復旧

図には書きませんでしたが、例外からリターンする命令(eret 命令)が参照するレジスタ(SPSR, ELR レジスタ)もスタックに退避、復旧しています。従って eret が戻る先は切り替え後のスレッドのコードです。

[編集者: すずき]
[更新: 2020年 10月 9日 00:29]

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2020年 9月 30日

link permalink

link 編集する

Zephyr OS で遊ぼう その 14 - SMP 対応の準備、コンテキストスイッチの実装、中編 1、既存実装調査

目次: Zephyr を調べる - まとめリンク

前回は、新しい形式のコンテキストスイッチ関数 arch_switch() のラッパー関数まで実装しました。RISC-V 向け実装をする前に、既に新しいコンテキストスイッチ関数に対応している AArch64 の実装を調べます。

共通部分からアーキ依存部分

コンテキストスイッチ処理の共通部分(do_swap())から、アーキテクチャ依存部分(AArch64 の arch_switch())に至るまでを見ます。

Zephyr の新しい形式のコンテキストスイッチ関数(共通部分)

// zephyr/kernel/include/kswap.h

/* New style context switching.  arch_switch() is a lower level
 * primitive that doesn't know about the scheduler or return value.
 * Needed for SMP, where the scheduler requires spinlocking that we
 * don't want to have to do in per-architecture assembly.
 *
 * Note that is_spinlock is a compile-time construct which will be
 * optimized out when this function is expanded.
 */
static ALWAYS_INLINE unsigned int do_swap(unsigned int key,
					  struct k_spinlock *lock,
					  int is_spinlock)
{

...

	if (new_thread != old_thread) {
#ifdef CONFIG_TIMESLICING
		z_reset_time_slice();
#endif

		old_thread->swap_retval = -EAGAIN;

#ifdef CONFIG_SMP
		_current_cpu->swap_ok = 0;

		new_thread->base.cpu = arch_curr_cpu()->id;

		if (!is_spinlock) {
			z_smp_release_global_lock(new_thread);
		}
#endif
		sys_trace_thread_switched_out();
		_current_cpu->current = new_thread;
		wait_for_switch(new_thread);
		arch_switch(new_thread->switch_handle,
			     &old_thread->switch_handle);    //★コンテキストスイッチ★

	}

...

前々回に紹介した新しいコンテキストスイッチの方式(arch_switch())を使っていることがわかります。

AArch64 の新しい形式のコンテキストスイッチ arch_switch() 実装、入り口

// zephyr/arch/arm/include/aarch64/arch_func.h

static inline void arch_switch(void *switch_to, void **switched_from)
{
	z_arm64_call_svc(switch_to, switched_from);    //★アセンブラ実装へ★

	return;
}


// zephyr/arch/arm/core/aarch64/switch.S

GTEXT(z_arm64_call_svc)
SECTION_FUNC(TEXT, z_arm64_call_svc)
	svc	#_SVC_CALL_CONTEXT_SWITCH    //★スーパーバイザーコール命令★
	ret

アセンブラ側の実装では、いきなりスーパーバイザーコール命令(svc 命令)をぶっ放して、スーパーバイザーコール例外を起こすだけになっています。AArch64 は例外ハンドラ内でコンテキストスイッチを行う仕組みのようです。

arch_switch() の引数

関数 arch_switch() はスレッドを 2つ受け取ります。old_thread は切り替え元のスレッド、new_thread は切り替え先のスレッドです。old_thread -> new_thread にコンテキストスイッチするわけです。ただし直接スレッド構造体を受け取るわけではなく、若干クセのある渡し方をします。

第 1引数 new_thread->switch_handle(void * 型)は切り替え先のスレッド構造体へのポインタ(struct k_thread * 型) new_thread が入っています。このポインタをキャストすると new_thread が求められます。

実はここには罠があり、何も実装せずにいると new_thread->switch_handle には NULL が入ります。すると、あとで CONFIG_SMP を有効にした段階で wait_for_switch() 関数にてハングアップします。こんなのパッと見ではわかりません……。

switch_handle の初期化

// zephyr/kernel/include/kswap.h

/* New style context switching.  arch_switch() is a lower level
 * primitive that doesn't know about the scheduler or return value.
 * Needed for SMP, where the scheduler requires spinlocking that we
 * don't want to have to do in per-architecture assembly.
 *
 * Note that is_spinlock is a compile-time construct which will be
 * optimized out when this function is expanded.
 */
static ALWAYS_INLINE unsigned int do_swap(unsigned int key,
					  struct k_spinlock *lock,
					  int is_spinlock)
{

...

	if (new_thread != old_thread) {

...

		sys_trace_thread_switched_out();
		_current_cpu->current = new_thread;
		wait_for_switch(new_thread);    //★これ★
		arch_switch(new_thread->switch_handle,
			     &old_thread->switch_handle);

	}

	if (is_spinlock) {
		arch_irq_unlock(key);
	} else {
		irq_unlock(key);
	}

	return _current->swap_retval;
}


/* There is an unavoidable SMP race when threads swap -- their thread
 * record is in the queue (and visible to other CPUs) before
 * arch_switch() finishes saving state.  We must spin for the switch
 * handle before entering a new thread.  See docs on arch_switch().
 *
 * Note: future SMP architectures may need a fence/barrier or cache
 * invalidation here.  Current ones don't, and sadly Zephyr doesn't
 * have a framework for that yet.
 */
static inline void wait_for_switch(struct k_thread *thread)
{
#ifdef CONFIG_SMP
	volatile void **shp = (void *)&thread->switch_handle;

	while (*shp == NULL) {    //★CONFIG_SMP 有効で new_thread->switch_handle が NULL だとこのループでハング★
		k_busy_wait(1);
	}
#endif
}

AArch64 向けのスレッド作成する関数を良く見ると、しれっと switch_handle を初期化しています。この処理は RISC-V 向けには存在しないため、追加する必要がありそうです。

switch_handle の初期化

// zephyr/arch/arm/core/aarch64/thread.c

/*
 * An initial context, to be "restored" by z_arm64_context_switch(), is put at
 * the other end of the stack, and thus reusable by the stack when not needed
 * anymore.
 */
void arch_new_thread(struct k_thread *thread, k_thread_stack_t *stack,
		     char *stack_ptr, k_thread_entry_t entry,
		     void *p1, void *p2, void *p3)
{

...

	/*
	 * We are saving:
	 *
	 * - SP: to pop out entry and parameters when going through
	 *   z_thread_entry_wrapper().
	 * - x30: to be used by ret in z_arm64_context_switch() when the new
	 *   task is first scheduled.
	 */

	thread->callee_saved.sp = (uint64_t)pInitCtx;
	thread->callee_saved.x30 = (uint64_t)z_thread_entry_wrapper;

	thread->switch_handle = thread;    //★ここで初期化している★
}

関数 arch_switch() の第 2引数 &old_thread->switch_handle(void ** 型)は切り替え元のスレッド構造体の switch_handle のポインタです。switch_handle の値(たいてい NULL)自体には意味がなくて、このポインタから old_thread が計算できることが大事です。

長くなってきたので続きは次回。

[編集者: すずき]
[更新: 2020年 10月 9日 00:29]

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2020年 9月 29日

link permalink

link 編集する

Zephyr OS で遊ぼう その 13 - SMP 対応の準備、コンテキストスイッチの実装、前編、ラッパー関数まで実装

目次: Zephyr を調べる - まとめリンク

CONFIG_USE_SWITCH を有効にするとビルドエラーが発生しますので、都度対処します。

arch_thread_return_value_set()

最初は arch_thread_return_value_set() が二重定義だと怒られます。

arch_thread_return_value_set() が二重定義
../kernel/include/kernel_internal.h: At top level:
../kernel/include/kernel_internal.h:84:1: error: redefinition of 'arch_thread_return_value_set'
 arch_thread_return_value_set(struct k_thread *thread, unsigned int value)
 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~

USE_SWITCH を有効にした場合、arch_thread_return_value_set() はカーネル側で定義されるので、アーキテクチャ側では実装不要です。

arch_thread_return_value_set() カーネル側の定義

// zephyr/arch/riscv/include/kernel_arch_func.h

static ALWAYS_INLINE void
arch_thread_return_value_set(struct k_thread *thread, unsigned int value)
{
	thread->arch.swap_return_value = value;
}


// zephyr/include/arch/riscv/thread.h

struct _thread_arch {
	uint32_t swap_return_value; /* Return value of z_swap() */
};


// zephyr/arch/riscv/core/offsets/offsets.c

GEN_OFFSET_SYM(_thread_arch_t, swap_return_value);

この辺りは要らないので #ifndef CONFIG_USE_SWITCH で囲って無効化します。

arch_switch

次は arch_switch() が未定義だと言われます。

arch_switch() が未定義
../kernel/include/kernel_arch_interface.h:126:20: warning: 'arch_switch' declared 'static' but never defined [-Wunused-function]
 static inline void arch_switch(void *switch_to, void **switched_from);
                    ^~~~~~~~~~~

この関数が USE_SWITCH の本体です。他のアーキテクチャを見ると、arch_switch() はラッパー関数で、本体はアセンブラで書かれていることが多いようです。他の流儀に習っておきます。

arch_switch() ラッパー

// arch/riscv/include/kernel_arch_func.h

#ifdef CONFIG_USE_SWITCH
extern void z_riscv_switch(void *switch_to, void **switched_from);

static inline void arch_switch(void *switch_to, void **switched_from)
{
	z_riscv_switch(switch_to, switched_from);
}
#else

...

肝心の中身はまた今度実装します。

[編集者: すずき]
[更新: 2020年 10月 9日 00:29]

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2020年 9月 28日

link permalink

link 編集する

Zephyr OS で遊ぼう その 12 - SMP 対応の準備、コンテキストスイッチのコンフィグ

目次: Zephyr を調べる - まとめリンク

以前(2020年 2月 21日の日記参照)ご紹介したとおり、Zephyr の RISC-V 向け実装は SMP に対応していません。Zephyr のマルチコア対応を有効にするコンフィグは CONFIG_SMP ですが、有効にすると大量のエラーが出て、何から直したら良いのかわからなくなります。

コンフィグや実装を見た限りでは、下記の 4つのステップで対応していくと良さそうです。

  • (今ここ)SMP の前提条件、新しいコンテキストスイッチ方式に対応する(CONFIG_USE_SWITCH, CONFIG_USE_SWITCH_SUPPORTED)
  • SMP に対応する(CONFIG_SMP)、ただし CPU コア数は 1
  • 先頭ではないコア(mhartid != 0)で動作させる、ただし CPU コア数は 1
  • CPU コア数を 1以上にする(CONFIG_SMP)

このコンフィグを有効にすその前に対応すべきコンフィグがあります。CONFIG_USE_SWITCH と CONFIG_USE_SWITCH_SUPPORTED です。

CONFIG_USE_SWITCH

// zephyr/kernel/Kconfig

config USE_SWITCH
	bool "Use new-style _arch_switch instead of arch_swap"
	depends on USE_SWITCH_SUPPORTED

...

config SMP
	bool "Enable symmetric multithreading support"
	depends on USE_SWITCH

既存の各アーキテクチャの対応状況を見ると、

CONFIG_USE_SWITCH 対応状況(一例)

// ARM
// Aarch32 は USE_SWITCH は未サポート
// AArch64 は Cortex-A のみ USE_SWITCH をサポート

// zephyr/arch/arm/core/aarch64/Kconfig

config CPU_CORTEX_A
	bool
	select CPU_CORTEX
	select HAS_FLASH_LOAD_OFFSET
	select USE_SWITCH
	select USE_SWITCH_SUPPORTED


// ARC
// ARCV2 は USE_SWITCH をサポート

// zephyr/arch/arc/Kconfig

config CPU_ARCV2
	bool
	select ARCH_HAS_STACK_PROTECTION if ARC_HAS_STACK_CHECKING || ARC_MPU
	select ARCH_HAS_USERSPACE if ARC_MPU
	select USE_SWITCH
	select USE_SWITCH_SUPPORTED
	default y

他は載せませんが AArch64, ARC, x86_64, xtensa が対応しているようです。

Zephyr のコンテキストスイッチ

Zephyr にはコンテキストスイッチが 2種類存在しています。違いは下記の通りです。

  • arch_swap: シングルコア専用のコンテキストスイッチ、RISC-V は対応済み
  • arch_switch: SMP に対応したコンテキストスイッチ、シングルコアだと若干効率が悪い、RISC-V は未対応

CONFIG_USE_SWITCH_SUPPORTED を有効にするには arch_switch を実装する必要がありますが、RISC-V ではシングルコアもマルチコアもあり得ますから、CONFIG_USE_SWITCH を無条件に有効にするのは得策ではないと考えられます。

SoC 側の Kconfig で有効にしても良いですし、ユーザーに menuconfig から選んでもらう手もあります。あまり悩んでも仕方ないので、ユーザーが選べるようにして先に進めます。RISC-V の Kconfig に SMP 対応かどうか?を選べるコンフィグを一つ追加します。

CONFIG_USE_SWITCH を有効にする RISC-V 向けコンフィグを追加

// zephyr/arch/riscv/Kconfig

config RISCV_SMP
	bool "Does SOC has SMP"
	select USE_SWITCH
	select USE_SWITCH_SUPPORTED

有効にするとビルドエラーだらけになります。続きはまた今度。

[編集者: すずき]
[更新: 2020年 10月 14日 20:02]

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



link もっと前
   2020年 10月 3日 -
      2020年 9月 24日  
link もっと後

管理用メニュー

link 記事を新規作成

合計:  counter total
本日:  counter today

link About www.katsuster.net
RDF ファイル RSS 1.0
QR コード QR コード

最終更新: 10/18 04:44

カレンダー

<2020>
<<<10>>>
----123
45678910
11121314151617
18192021222324
25262728293031

最近のコメント 5件

  • link 20年09月10日
    すずき 「追加情報。最新の Debian Test...」
    (更新:10/07 16:48)
  • link 20年09月20日
    hdk 「最近は音楽聞く時やビデオ視聴時はミニコン...」
    (更新:09/24 21:43)
  • link 20年09月20日
    すずき 「ありゃー、同じ壊れ方ですね。\n新たなヘ...」
    (更新:09/24 00:23)
  • link 20年09月20日
    hdk 「うちのATH-AD300もやはり頭にプラ...」
    (更新:09/23 12:26)
  • link 20年07月10日
    すずき 「鳥のゲームは知りませんでした。色々やって...」
    (更新:08/11 18:59)

最近の記事 3件

link もっとみる
  • link 20年02月22日
    すずき 「[Zephyr を調べる - まとめリンク] 日記が増えすぎて、一...」
    (更新:10/18 04:44)
  • link 20年10月18日
    すずき 「[Zephyr OS で遊ぼう その 27 - SMP 対応、] ...」
    (更新:10/18 04:43)
  • link 20年10月17日
    すずき 「[Zephyr OS で遊ぼう その 26 - SMP 対応、] ...」
    (更新:10/18 03:24)

こんてんつ

open/close wiki
open/close Java API

過去の日記

open/close 2002年
open/close 2003年
open/close 2004年
open/close 2005年
open/close 2006年
open/close 2007年
open/close 2008年
open/close 2009年
open/close 2010年
open/close 2011年
open/close 2012年
open/close 2013年
open/close 2014年
open/close 2015年
open/close 2016年
open/close 2017年
open/close 2018年
open/close 2019年
open/close 2020年
open/close 過去日記について

その他の情報

open/close アクセス統計
open/close サーバ一覧
open/close サイトの情報