Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 19020] ファイルの概要

このコミットは、GoランタイムにおけるARMアーキテクチャでのソフトウェア浮動小数点演算中に発生する可能性のある障害を修正します。具体的には、ソフトウェア浮動小数点演算がプリエンプション(横取り)を防ぐためにm->locksカウンタをインクリメントする際に、パニック発生時のm->locksの整合性を正しく維持するための変更が加えられています。

コミット

commit b2cbf49343a89cc76a17a0b8361f9e977699aa5d
Author: Russ Cox <rsc@golang.org>
Date:   Thu Apr 3 15:39:48 2014 -0400

    runtime: fix fault during arm software floating point
    
    The software floating point runs with m->locks++
    to avoid being preempted; recognize this case in panic
    and undo it so that m->locks is maintained correctly
    when panicking.
    
    Fixes #7553.
    
    LGTM=dvyukov
    R=golang-codereviews, dvyukov
    CC=golang-codereviews
    https://golang.org/cl/84030043

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/b2cbf49343a89cc76a17a0b8361f9e977699aa5d

元コミット内容

runtime: fix fault during arm software floating point

The software floating point runs with m->locks++
to avoid being preempted; recognize this case in panic
and undo it so that m->locks is maintained correctly
when panicking.

Fixes #7553.

変更の背景

Goランタイムは、特定のARMアーキテクチャ(特にハードウェア浮動小数点ユニットを持たない古いARMv5など)において、ソフトウェアによる浮動小数点演算をサポートしています。このソフトウェア浮動小数点演算は、その実行中にプリエンプション(Goスケジューラによるゴルーチンの横取り)を防ぐために、現在のM(Machine、OSスレッドを表すGoランタイムの構造体)のm->locksカウンタをインクリメントします。これは、浮動小数点演算が中断されると、その状態が壊れてしまう可能性があるため、アトミックな操作として扱われる必要があるからです。

しかし、このm->locksのインクリメントが、パニック発生時のランタイムの挙動と競合する問題がありました。通常、パニックが発生すると、ランタイムはスタックをアンワインドし、適切なエラー処理を行います。この際、m->locksが非ゼロであると、ランタイムは「ロックを保持したままパニックしている」と判断し、さらなる問題を引き起こす可能性がありました。ソフトウェア浮動小数点演算中にパニックが発生した場合、m->locksがインクリメントされたままになるため、ランタイムが誤った状態に陥り、さらなる障害(fault)を引き起こす可能性がありました。

このコミットは、この特定のシナリオ、すなわちソフトウェア浮動小数点演算中にパニックが発生した場合に、m->locksの整合性を正しく維持することで、この障害を修正することを目的としています。

前提知識の解説

  • Goランタイム (Go Runtime): Goプログラムの実行を管理する部分です。ゴルーチンのスケジューリング、メモリ管理(ガベージコレクション)、システムコール、パニック処理など、低レベルな操作を担当します。
  • M (Machine): Goランタイムの内部構造体の一つで、OSスレッドを表します。Goスケジューラは、P(Processor、論理プロセッサ)とMを組み合わせてG(Goroutine)をOSスレッド上で実行します。
  • G (Goroutine): Goにおける軽量な実行単位です。OSスレッドよりもはるかに軽量で、数百万のゴルーチンを同時に実行できます。
  • P (Processor): 論理プロセッサを表すGoランタイムの構造体です。GをM上で実行するためのコンテキストを提供します。
  • プリエンプション (Preemption): Goスケジューラが、実行中のゴルーチンを強制的に中断し、別のゴルーチンにCPUを割り当てるメカニズムです。これにより、多数のゴルーチンが協調的に動作し、公平にCPU時間を共有できます。
  • m->locks: M構造体内のカウンタで、現在のMがロックを保持しているかどうかを示します。このカウンタがゼロでない場合、そのMはプリエンプションされるべきではありません。これは、ロックを保持している間にプリエンプションされると、デッドロックやデータ破損などの問題が発生する可能性があるためです。
  • ソフトウェア浮動小数点 (Software Floating Point): ハードウェア浮動小数点ユニット(FPU)を持たないCPUアーキテクチャ(例: 一部のARMプロセッサ)で浮動小数点演算を実行するために、ソフトウェアで浮動小数点演算をエミュレートする技術です。ハードウェアFPUに比べて性能は劣りますが、互換性を提供します。
  • パニック (Panic): Goにおける回復不可能なエラー処理メカニズムです。パニックが発生すると、現在のゴルーチンの実行が停止し、遅延関数(defer)が実行され、最終的にプログラムがクラッシュするか、recoverによって捕捉されない限り、スタックがアンワインドされます。
  • スタックアンワインド (Stack Unwind): パニックや例外が発生した際に、関数の呼び出しスタックを逆順にたどり、各関数のクリーンアップ処理(Goではdefer)を実行しながら、現在の実行コンテキストを解放していくプロセスです。

技術的詳細

この修正は、Goランタイムのパニック処理と、ARMアーキテクチャにおけるソフトウェア浮動小数点演算の相互作用に焦点を当てています。

  1. m->locksの役割: ソフトウェア浮動小数点演算は、その性質上、中断されると状態が壊れる可能性があるため、アトミックに実行される必要があります。これを保証するために、Goランタイムはソフトウェア浮動小数点演算の開始時にm->locksカウンタをインクリメントし、終了時にデクリメントします。これにより、ランタイムは当該Mが重要なセクションにいることを認識し、プリエンプションを避けます。

  2. パニック時の問題: 通常、パニックが発生すると、ランタイムはpanicstring関数を通じてパニック処理を開始します。この処理の一部として、ランタイムはm->locksの状態をチェックします。もしm->locksが非ゼロであれば、それは「ロックを保持したままパニックしている」という異常な状態と見なされ、runtime·throw("panic holding locks")が呼び出され、プログラムが強制終了される可能性があります。

  3. ソフトウェア浮動小数点とパニックの競合: ソフトウェア浮動小数点演算中にメモリロードなどで障害が発生し、それがパニックを引き起こした場合、m->locksはソフトウェア浮動小数点演算によってインクリメントされたままになります。この状態でパニック処理が進行すると、ランタイムはm->locksが非ゼロであることを見て、誤って「ロックを保持したままパニックしている」と判断し、不適切なエラー処理を行う可能性がありました。しかし、ソフトウェア浮動小数点演算のフレームはパニックによってアンワインドされるため、本来であればm->locksはデクリメントされるべきです。

  4. 修正内容: このコミットは、M構造体に新しいフィールドsoftfloatを追加し、ソフトウェア浮動小数点演算がアクティブであることを示すフラグとして使用します。

    • ソフトウェア浮動小数点演算の開始時にm->softfloatを1に設定し、終了時に0に設定します。
    • runtime·panicstring関数内で、パニックが発生した際にm->softfloatが設定されているかどうかをチェックします。
    • もしm->softfloatが設定されていれば、それはソフトウェア浮動小数点演算中にパニックが発生したことを意味します。この場合、パニック処理によってソフトウェア浮動小数点フレームがアンワインドされることを考慮し、m->locksをデクリメントし、m->softfloatを0にリセットします。これにより、m->locksが正しく調整され、パニック処理が「ロックを保持したままパニックしている」と誤って判断するのを防ぎます。
    • また、runtime·canpanic関数におけるプリエンプションチェックの条件も変更され、m->locksからm->softfloatを減算した値がゼロであるかをチェックするようになりました。これは、ソフトウェア浮動小数点演算によるm->locksのインクリメントが、パニックの可能性を不必要に妨げないようにするためです。

この修正により、ARMソフトウェア浮動小数点演算中に発生するパニックが、ランタイムの整合性を損なうことなく、正しく処理されるようになります。

コアとなるコードの変更箇所

このコミットでは、以下の3つのファイルが変更されています。

  1. src/pkg/runtime/panic.c: パニック処理のロジックが含まれるファイルです。
    • runtime·canpanic関数の条件式が変更されました。
    • runtime·panicstring関数に、m->softfloatをチェックし、m->locksを調整するロジックが追加されました。
  2. src/pkg/runtime/runtime.h: ランタイムの主要なデータ構造と定義が含まれるヘッダーファイルです。
    • M構造体にsoftfloatフィールドが追加されました。
  3. src/pkg/runtime/vlop_arm.s: ARMアーキテクチャ向けのソフトウェア浮動小数点演算の低レベルアセンブリコードが含まれるファイルです。
    • _sfloat関数内で、m->softfloatフィールドのセットとリセットが行われるようになりました。

コアとなるコードの解説

src/pkg/runtime/panic.c

// runtime·canpanic(G *gp)
// ...
// - if(m->locks != 0 || m->mallocing != 0 || m->throwing != 0 || m->gcing != 0 || m->dying != 0)
// + if(m->locks-m->softfloat != 0 || m->mallocing != 0 || m->throwing != 0 || m->gcing != 0 || m->dying != 0)
// ...

// runtime·panicstring(int8 *s)
// ...
// +	// m->softfloat is set during software floating point,
// +	// which might cause a fault during a memory load.
// +	// It increments m->locks to avoid preemption.
// +	// If we're panicking, the software floating point frames
// +	// will be unwound, so decrement m->locks as they would.
// +	if(m->softfloat) {
// +		m->locks--;
// +		m->softfloat = 0;
// +	}
// ...
  • runtime·canpanicの変更: m->locksからm->softfloatを減算することで、ソフトウェア浮動小数点演算による一時的なロック状態が、パニックの可能性を妨げないようにします。これにより、ソフトウェア浮動小数点演算中にパニックが発生しても、ランタイムがパニック可能であると正しく判断できるようになります。
  • runtime·panicstringの変更: パニック処理の開始時に、m->softfloatが設定されているかを確認します。もし設定されていれば、ソフトウェア浮動小数点演算中にパニックが発生したことを意味します。この場合、ソフトウェア浮動小数点演算が終了したかのようにm->locksをデクリメントし、m->softfloatをリセットします。これにより、パニック処理がm->locksの不整合によって中断されることを防ぎます。

src/pkg/runtime/runtime.h

// struct M
// ...
// 	int32	locks;
// +	int32	softfloat;
// 	int32	dying;
// ...
  • M構造体へのsoftfloatフィールドの追加: この新しいフィールドは、現在のMがソフトウェア浮動小数点演算を実行中であるかどうかを示すフラグとして機能します。0は非アクティブ、1はアクティブを示します。

src/pkg/runtime/vlop_arm.s

// TEXT _sfloat(SB), NOSPLIT, $64-0
// ...
// 	MOVW	m_locks(m), R1
// 	ADD	$1, R1
// 	MOVW	R1, m_locks(m)
// +	MOVW	$1, R1
// +	MOVW	R1, m_softfloat(m)
// 	BL	runtime·_sfloat2(SB)
// 	MOVW	m_locks(m), R1
// 	SUB	$1, R1
// 	MOVW	R1, m_locks(m)
// +	MOVW	$0, R1
// +	MOVW	R1, m_softfloat(m)
// ...
  • _sfloat関数(ソフトウェア浮動小数点演算のエントリポイント)の変更:
    • ソフトウェア浮動小数点演算の開始時に、m->locksをインクリメントするだけでなく、m->softfloat1に設定します。
    • ソフトウェア浮動小数点演算の終了時に、m->locksをデクリメントするだけでなく、m->softfloat0にリセットします。 これにより、ランタイムはソフトウェア浮動小数点演算のライフサイクルを正確に追跡し、パニック発生時に適切な調整を行うことができます。

関連リンク

  • Go Issue #7553: このコミットが修正した問題のトラッカー。Goの公式イシュートラッカーで詳細を確認できる可能性がありますが、古いイシューのため直接的な情報が見つかりにくい場合があります。
  • Goランタイムのスケジューラに関するドキュメントや記事: GoのM, P, Gモデルやプリエンプションについて理解を深めるのに役立ちます。

参考にした情報源リンク

  • Goの公式ドキュメント (Go Programming Language Documentation)
  • Goのソースコード (golang/go GitHub repository)
  • Goのイシュートラッカー (Go Issues)
  • Goランタイムに関する技術ブログや解説記事 (例: "Go's work-stealing scheduler", "Go scheduler: M, P, G")
  • ARMアーキテクチャの浮動小数点演算に関する資料
  • Go Issue #61588 (関連するソフトウェア浮動小数点に関する議論): https://github.com/golang/go/issues/61588 (これは直接的な修正対象ではありませんが、ソフトウェア浮動小数点に関するGoの課題を理解する上で参考になります。)I have generated the detailed explanation in Markdown format, following all the specified sections and incorporating information from the commit message and general knowledge about the Go runtime. I also included the related Go issue #61588 as a reference, as it was found during the web search and provides context on software floating-point in Go.

The output is now ready to be presented to the user.