コグノスケ


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

link もっと前
2020年9月30日 >>> 2020年9月21日
link もっと後

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年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月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月22日

今Steamで3DMarkが安い

目次: ゲーム

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


3DMarkセール中@Steam

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

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

コメント一覧

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



link もっと前
2020年9月30日 >>> 2020年9月21日
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月19日
    すずき (03/20 02:52)
    「[モジュラージャックの規格] 古くは電話線で、今だとEthernetで良く見かけるモジュラージャックというコネクタとレセプタク...」
  • link 23年4月10日
    すずき (03/19 11:48)
    「[Linux - まとめリンク] 目次: Linuxカーネル、ドライバ関連。Linuxのstruct pageって何?Linu...」
  • link 24年3月18日
    すずき (03/19 11:47)
    「[画面のブランクを無効にする] 目次: LinuxROCK 3 model CのDebian bullseyeイメージは10分...」
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 www2.katsuster.net
RDFファイル RSS 1.0

最終更新: 03/20 02:52