コグノスケ


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

link もっと前
2020年9月22日 >>> 2020年10月1日
link もっと後

2020年9月22日

今Steamで3DMarkが安い

目次: ゲーム

Steamのセールで3DMarkが意味不明なほど安くなっていたので、買いました。Steamは突然90%OFFとか平然とやってくるので、定価とは……??という気分になります。


3DMarkセール中@Steam

新しいマシンを買う(もしくはパーツを買う)時に役に立ちそうです。

編集者:すずき(2023/09/24 13:27)

コメント一覧

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



2020年9月28日

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

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

編集者:すずき(2023/09/24 12:08)

コメント一覧

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



2020年9月29日

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

...

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

編集者:すずき(2023/09/24 12:09)

コメント一覧

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



2020年9月30日

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が計算できることが大事です。

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

編集者:すずき(2023/09/24 12:09)

コメント一覧

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



2020年10月1日

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が戻る先は切り替え後のスレッドのコードです。

編集者:すずき(2023/09/24 12:09)

コメント一覧

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



link もっと前
2020年9月22日 >>> 2020年10月1日
link もっと後

管理用メニュー

link 記事を新規作成

<2020>
<<<09>>>
--12345
6789101112
13141516171819
20212223242526
27282930---

最近のコメント5件

  • link 21年3月13日
    すずきさん (03/05 15:13)
    「あー、このプログラムがまずいんですね。ご...」
  • link 21年3月13日
    emkさん (03/05 12:44)
    「キャストでvolatileを外してアクセ...」
  • link 24年1月24日
    すずきさん (02/19 18:37)
    「簡単にできる方法はPowerShellの...」
  • link 24年1月24日
    KKKさん (02/19 02:30)
    「追伸です。\nネットで調べたらマイクロソ...」
  • link 24年1月24日
    KKKさん (02/19 02:25)
    「私もエラーで困ってます\n手動での回復パ...」

最近の記事3件

  • link 24年3月25日
    すずき (03/26 03:20)
    「[Might and Magic Book One TASのその後] 目次: Might and Magicファミコン版以前(...」
  • link 21年10月4日
    すずき (03/26 03:14)
    「[Might and Magicファミコン版 - まとめリンク] 目次: Might and Magicファミコン版TASに挑...」
  • link 24年3月19日
    すずき (03/20 02:52)
    「[モジュラージャックの規格] 古くは電話線で、今だとEthernetで良く見かけるモジュラージャックというコネクタとレセプタク...」
link もっとみる

こんてんつ

open/close wiki
open/close Linux JM
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 2021年
open/close 2022年
open/close 2023年
open/close 2024年
open/close 過去日記について

その他の情報

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

合計:  counter total
本日:  counter today

link About www.katsuster.net
RDFファイル RSS 1.0

最終更新: 03/26 03:20