コグノスケ


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

link もっと前
2023年11月10日 >>> 2023年11月10日
link もっと後

2023年11月10日

Zephyr RISC-V向けのマルチコアブートを修正

目次: Zephyr

Zephyr RTOSのRISC-V向けマルチコアブート処理がバグっていて修正したので解析したメモを残しておきます。

ZephyrではCONFIG_RV_BOOT_HARTというコンフィグで設定したhartidと、mhartid CSRの値が一致するhartがメインの初期化を担当します。Zephyrのコードではfirst coreと呼び、メイン以外はsecondary coreと呼んでいます。あまり聞いたことがない呼び方ですね。この日記では素直にメインコア/サブコアと書きます。

メインコアはarch_start_cpu()という関数からサブコアを起床します。呼び出しの経路は下記です。

メインコアのブート処理の呼び出し経路
z_thread_entry()
  bg_thread_main()
    z_smp_init()
      arch_start_cpu()

このbg_thread_main()関数はメインスレッド実行前に必要な初期化を実施していて、最終的にmain()を呼び出します。実行タイミングはカーネルの初期化が終わって、メインスレッドにコンテキストスイッチしたあとです。ブートからそれなりに時間が経過しています。

メインコアのブート処理

// zephyr/arch/riscv/core/smp.c

void arch_start_cpu(int cpu_num, k_thread_stack_t *stack, int sz,
		    arch_cpustart_t fn, void *arg)
{
	riscv_cpu_init[cpu_num].fn = fn;
	riscv_cpu_init[cpu_num].arg = arg;

	riscv_cpu_sp = Z_KERNEL_STACK_BUFFER(stack) + sz;
	riscv_cpu_wake_flag = _kernel.cpus[cpu_num].arch.hartid;    //★★1-1: riscv_cpu_wake_flagに起床させるhartidを設定する★★

#ifdef CONFIG_PM_CPU_OPS
	if (pm_cpu_on(cpu_num, (uintptr_t)&__start)) {
		printk("Failed to boot secondary CPU %d\n", cpu_num);
		return;
	}
#endif

	//★★1-2: riscv_cpu_wake_flagが0になるまで待つ★★
	while (riscv_cpu_wake_flag != 0U) {
		;
	}

	//★★1-3: riscv_cpu_wake_flagが0になったら継続★★
}

サブコアは下記のようにブート直後にフラグチェックし、起動の指示があるまで待っています。

サブコアのブート処理

// zephyr/arch/riscv/core/reset.S

SECTION_FUNC(TEXT, __initialize)
	csrr a0, mhartid            //★★a0 <- mhartid★★
	li t0, CONFIG_RV_BOOT_HART
	beq a0, t0, boot_first_core
	j boot_secondary_core

//...

boot_secondary_core:
#if CONFIG_MP_MAX_NUM_CPUS > 1
	//★★2-1: riscv_cpu_wake_flagがmhartidになるまで待つ★★★
	la t0, riscv_cpu_wake_flag
	lr t0, 0(t0)
	bne a0, t0, boot_secondary_core

	//★★2-2: riscv_cpu_wake_flagがmhartidになったら継続する★★★
	/* Set up stack */
	la t0, riscv_cpu_sp
	lr sp, 0(t0)

	la t0, riscv_cpu_wake_flag
	sr zero, 0(t0)               //★★2-3: riscv_cpu_wake_flagに0をセット★★
	j z_riscv_secondary_cpu_init

コードの想定する動作は下記の通りです。例としてhart0がメイン、hart1がサブの2コアのブートとします。

  • hart1: 2-1: riscv_cpu_wake_flag == mhartid (= 1)になるまで待つ
  • hart0: 1-1: riscv_cpu_wake_flagに起床させるhartid = 1を設定する(arch_start_cpu()関数)
  • hart0: 1-2: riscv_cpu_wake_flag == 0になるまで待つ
  • hart1: 2-2: riscv_cpu_wake_flag == mhartid (= 1)になったので継続
  • hart1: 2-3: riscv_cpu_wake_flagを0にセットする
  • hart0: 1-3: riscv_cpu_wake_flag == 0になったので継続

このときriscv_cpu_wake_flagの初期値はいずれのサブコアのmhartidとも一致しないのが期待値です。

問題点

このコードはriscv_cpu_wake_flagの初期値がいずれかのサブコアのmhartidと一致するとハングアップします。riscv_cpu_wake_flagはBSS領域に配置されておりメインコアがいずれ0に初期化しますが、サブコアはメインコアがBSS領域を初期化する前にriscv_cpu_wake_flagを参照するので間に合いません。

例えば起動直後にriscv_cpu_wake_flagが偶然1だったとすると下記のような動きをしてハングアップします。

  • hart1: 2-1: riscv_cpu_wake_flag == mhartid (= 1)になるまで待つ
  • hart1: 2-2: riscv_cpu_wake_flag == mhartid (= 1)なので継続
  • hart1: 2-3: riscv_cpu_wake_flagを0にセットする
  • hart0: 1-1: riscv_cpu_wake_flagに起床させるhartid = 1を設定する(arch_start_cpu()関数)
  • hart0: 1-2: riscv_cpu_wake_flag == 0になるまで待つ
  • hart0: 誰もriscv_cpu_wake_flagを0に戻さないのでハングアップ

この実装をどう変更したら良くなるのか?なぜか?については少々長くなるので、以前の日記(2021年9月28日の日記参照)をご覧ください。

対策

サブコアを起こすフラグriscv_cpu_wake_flag(初期値は-1)とサブコアが起きたことを示すフラグriscv_cpu_boot_flag(初期値は0)に分けます。riscv_cpu_wake_flagはサブコアから-1に初期化してから起動待ちに入るようにして、不定値問題に対処します。

Zephyr RTOSのプロジェクトにPull Requestを送ったところあっさり取り込まれました(Zephyrへのリンク)。1週間以上は掛かるかと思っていましたが、早かったです。今は修正に向いている時期なんでしょうか。

とまあ、ここまで書いていて対策後のコードも間違っているような気がしてきました。

メインコアがhart0じゃない場合、BSS領域の初期化でriscv_cpu_wake_flagが0に変わってしまうので、hart0が間違って起動するのではなかろうか……?それは良くないな、後で確かめようと思います。

編集者:すずき(2023/11/13 11:54)

コメント一覧

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



link もっと前
2023年11月10日 >>> 2023年11月10日
link もっと後

管理用メニュー

link 記事を新規作成

<2023>
<<<11>>>
---1234
567891011
12131415161718
19202122232425
2627282930--

最近のコメント5件

  • link 24年6月17日
    すずきさん (06/23 00:12)
    「ありがとうございます。バルコニーではない...」
  • link 24年6月17日
    hdkさん (06/22 22:08)
    「GPSの最初の同期を取る時は見晴らしのい...」
  • link 24年5月16日
    すずきさん (05/21 11:41)
    「あー、確かにdpkg-reconfigu...」
  • link 24年5月16日
    hdkさん (05/21 08:55)
    「システム全体のlocale設定はDebi...」
  • link 24年5月17日
    すずきさん (05/20 13:16)
    「そうですねえ、普通はStandardなの...」

最近の記事3件

  • link 22年3月18日
    すずき (06/22 17:32)
    「[射的 - まとめリンク] 目次: 射的一覧が欲しくなったので作りました。ガスガン その1ガスガン その2ガスガンが増えました...」
  • link 23年11月25日
    すずき (06/22 17:31)
    「[JTSA Limited大会参加2023] 目次: 射的JTSA Limitedの大会に参加しました。いつも使っているエアガ...」
  • link 24年5月26日
    すずき (06/22 17:16)
    「[JTSA Unlimited大会参加2024] 目次: 射的JTSA Unlimitedの大会に参加しました。去年は選手登録...」
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

最終更新: 06/23 00:12