コグノスケ


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

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

2023年11月21日

musl libcのアトミックCAS実装

目次: C言語とlibc

OSSのlibc実装の一つであるmusl libcのアトミックCAS(Compare And Swap)の実装を調べたメモです。CASはa_cas(p, t, s)関数に実装されていて、ポインタpの指す先の値がtなら、アトミックにsと入れ替える関数です。調べるのはRISC-V用の実装です。

コンパイラが提供するアトミック関数実装(stdatomic.h)は使わないみたいです。musl libc独自の実装となっています。

musl libcのアトミックCAS実装

// musl/arch/riscv64/atomic_arch.h

#define a_cas a_cas
static inline int a_cas(volatile int *p, int t, int s)
{
	int old, tmp;
	__asm__ __volatile__ (
		"\n1:   lr.w.aqrl %0, (%2)\n"
		"       bne %0, %3, 1f\n"
		"       sc.w.aqrl %1, %4, (%2)\n"
		"       bnez %1, 1b\n"
		"1:"
		: "=&r"(old), "=&r"(tmp)
		: "r"(p), "r"((long)t), "r"((long)s)
		: "memory");
	return old;
}

RISC-V向けの実装はLR/SC(Load and Reserve, Store Conditional)という仕組みを使い実装されています。RISC-V以外のアーキテクチャでも良く見かける機能で、見たことある方も多いと思います。LRはLL(Load Link)という名前のこともあるようです。

コードはたった4行です、1行ずついきましょう。

  • 1行目: pの指す先を予約付きロードします。ロードした値は変数oldに格納されます。
  • 2行目: ロードした値がtでなければ終了します。
  • 3行目: ロードした値がtならばpの指す先にsを条件付きストアします。
  • 4行目: 条件付きストアが成功したら終了、失敗したら1行目に戻ります。

条件付きストアは、予約付きロードから条件付きストア間にpの指す先を誰も変更していない場合のみストアが成功します。pの指す先の値がsに変化します(tmpには0が返されます)。

もし予約付きロードから条件付きストアの間に誰かがpの指す先を変更していた場合はストアが失敗します。pの指す先の値は変化しません(tmpには0以外の値が返されます)。

編集者:すずき(2023/11/25 03:33)

コメント一覧

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



2023年11月13日

ソフトバンクの料金

目次: プロバイダ

ドコモからソフトバンクに乗り換えて2か月ほど経ちました。2人暮らしで携帯2台+光回線1回線の総支払額は8,916円です。

以前は15,000円近かったから、だいぶお安くなったと思います。今まで高かった理由が「奥さんの携帯をiPhone初期のクソ高プランのまま放置してたから」なので、最近のお得系プランであればソフトバンクでもauでもなんでも良かったのかも。

ざっくり計算だとahamo x2台+ドコモ光Aプラン(2,970円 x2 + 4,300円 = 10,240円かな?)からの移行だとしても、それなりに嬉しいお値段になっているようです。まあその辺は当然考えられていますよね。

携帯電話回線

Y!mobileは安いです(光とのセット割で1,900円+税)1人2,100円くらいですね。初期のiPhoneのデータ通信価格(月額5,000円以上だったはず)と比べると格段に安いです。ahamoと比較しても数百円安くてありがたいですね。

通信の品質は値段相応の品質です。一番気になる点というと、大規模な駅など人口密集地に移動したときデータ通信が急激に遅くなったり止まったりすることです。不都合というほどではないけれど、露骨に遅くなるor止まるので気になります。

光回線

ソフトバンク光の料金はドコモ光と比べて400円くらい上がって(月額3,800+500円+税 = 4,730円、ドコモ光は4,300円)います。

単体で見ると損ですが、携帯回線とのセット割が1,000円くらいなので取り返せる仕組みです。通信会社ってのはなんでこう複雑で理解しにくい制度を考えるんでしょうね。昔からの悪癖ですね……。

通信の品質は特に変わった印象はありません。たぶん。

編集者:すずき(2023/11/15 16:36)

コメント一覧

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



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月21日 >>> 2023年11月8日
link もっと後

管理用メニュー

link 記事を新規作成

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

最近のコメント5件

  • link 21年9月20日
    すずきさん (11/19 01:04)
    「It was my pleasure.」
  • link 21年9月20日
    whtさん (11/17 23:41)
    「This blog solves my ...」
  • link 24年10月1日
    すずきさん (10/06 03:41)
    「xrdpで十分動作しているので、Wayl...」
  • link 24年10月1日
    hdkさん (10/03 19:05)
    「GNOMEをお使いでしたら今はWayla...」
  • link 24年10月1日
    すずきさん (10/03 10:12)
    「私は逆にVNCサーバーに繋ぐ使い方をした...」

最近の記事3件

  • link 24年11月18日
    すずき (12/04 01:49)
    「[nvJPEGとNVJPGとJetson APIその1 - nvJPEG decoupled API] 目次: Linux半年...」
  • link 23年4月10日
    すずき (12/04 01:40)
    「[Linux - まとめリンク] 目次: Linux関係の深いまとめリンク。目次: RISC-V目次: ROCK64/ROCK...」
  • link 24年11月28日
    すずき (12/01 00:53)
    「[BIOS/UEFI画面に入る方法] PCは起動時にあるキーを押すとBIOS/UEFIの設定画面に遷移します。良く見るパターン...」
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

最終更新: 12/04 01:49