ここ数週間、Linux/MIPS カーネルの SGI Octane (IP30) へのポートで SMP サポートを再び動作させようと試みてきました。ユニプロセッサ サポートは正常に動作しますが、2 番目の CPU を使用すると多くの問題が発生します。マシンをinit
プロセスで起動できますが、ほとんどの場合、SIGSEGV または SIGBUS で停止します。私は 5 年以上前に書かれたパッチからのサポート コードのほとんどを適切に配置していますが、適切にロックしていないか、予期せず IRQ を再度有効にしていると思われます。
ハードウェアの背景:
MIPS R10000 シリーズ CPU は、次の目的で 8 つの割り込みを実装IP0
していIP7
ます。
IP0
およびIP1
: ソフトウェア割り込みのみで、現在あまり使用されていません。IP2
toIP6
: 通常、処理のために他のハードウェア関数にルーティングされます。IP7
: R10K タイマー/カウンター/コンペア割り込み。R10K は MIPS-IV ISA をサポートし、I キャッシュと D キャッシュの両方を備えています。
- I キャッシュは 32kB、VIPT、2 ウェイ、および 64 バイトのラインサイズです。
- D キャッシュは 32kB、VIPT、2 ウェイ、エイリアスなし、32 バイトのラインサイズです。
- R10K L2 キャッシュは 2MB、双方向、128 バイトのラインサイズです。
- R10K はスーパースカラーであり、投機的実行を採用し、順不同で実行できます。
- Octane はキャッシュ コヒーレントであるため、投機的実行の影響を受けません。
- 具体的には、このシステムには R14000 デュアル モジュールがあります。主に R10K であり、ダイの縮小とより高速なクロック速度を備えていること以外は、あまり知られていません。SGI は、このプロセッサのハードウェア データシートやエラッタ情報を公開したことはありません。
HEART
Octane には、メモリ コントローラーと割り込みコントローラーの両方 と呼ばれる ASIC があります。HEART
最大 4 つのプロセッサをサポートするように設計されており、64 の割り込み (IRQ) が利用可能です。これらの 64 の IRQ は、いくつかの優先度レベルに分割され、上記の R10K CPU IPx IRQ にマップされます。
0
レベル 0 、IRQ15
-> CPUIP2
- レベル 1、IRQ
16
-31
> CPUIP3
- レベル 2、IRQ
31
-49
> CPUIP4
- レベル 3、IRQ
50
-> CPUIP5
51
レベル 4 、IRQ63
-> CPUIP6
これらの優先度レベルについて、いくつかの注意事項があります。
レベル 0 およびレベル 1 の IRQ は、主にシステム内のデバイス (SCSI、イーサネットなど) に割り当てられます。
レベル 2 にはいくつかの用途があります。
- IRQ
32
は40
、システム内のデバイス (特に、より高い優先度が必要なデバイス) で使用することもできます。 - IRQ
41
は、電源ボタンを押すために配線されています。 - IRQは、4 つの可能な CPU
42
へ45
のデバッガー信号用です。 - IRQ
46
は49
、可能な 4 つの CPU の SMP プロセッサ間割り込み (IPI) です。
- IRQ
レベル 3 の IRQ
50
は、それ自体のカウンター/比較タイマー専用ですHEART
。12.5MHz (80ns だと思います) で動作します。シングルカウントレジスタとコンペアレジスタを持っています。Linux のclockevent
観点からは、これはシステム タイマー (52 ビット カウンター、24 ビット コンペア) として使用するのに適した分解能のタイマーだと思います。レベル 4 はエラー IRQ 用です。
- IRQ
51
toは、XIO バス (スター トポロジーに配置され、 ASICによって処理される高速バス) 上58
の 8 つの使用可能なウィジェットのそれぞれに対するエラー IRQ です。Xtalk
XBOW
- IRQ
59
to62
は、可能な 4 つの CPU のバス エラー IRQ です。 - IRQ
63
は、それ自体の例外エラー IRQ ですHEART
。
- IRQ
HEART
割り込みを操作するためのいくつかのレジスタを示します。各レジスタは 64 ビット幅で、割り込みごとに 1 ビットです。
HEART_ISR
- 保留中の割り込みのリストを取得するための読み取り専用レジスタ。HEART_SET_ISR
- 特定の割り込みビットを設定するための書き込み専用レジスタ。HEART_CLR_ISR
- 特定の割り込みビットをクリアするための書き込み専用レジスタHEAR_IMR(x)
- で表される、特定の CPU 上の特定の割り込みの割り込みマスクを設定またはクリアする読み取り/書き込みレジスタx
。
基本的な IRQ ack/mask/unmasking 操作に次のコードを使用します。
u64 *imr; /* Address of the mask register to work on */
static int heart_irq_owner[64]; /* Which CPU owns which IRQ? (global) */
Ack: writeq((1UL << irq), HEART_CLR_ISR);
Mask: imr = HEART_IMR(heart_irq_owner[irq]);
writeq(readq(imr) & (~(1UL << irq)), imr);
Unmask: imr = HEART_IMR(heart_irq_owner[irq]);
writeq(readq(imr) | (1UL << irq), imr);
これらの基本的な操作はstruct irq_chip
、3.1x シリーズの Linux カーネル内のアクセサーを使用して実装され、HEART
レジスターへのアクセスを および を使用spin_lock_irqsave
して保護しますspin_unlock_irqrestore
。これらのアクセサーでこれらのロック機能を使用する必要があるかどうかは、100% 確信が持てません。
すべての割り込みを処理するために、標準の Linux/MIPS プラットフォームのディスパッチ関数は次のアクションを実行します。
IP7
do_IRQ()
-> CPU タイマー IRQ を処理するための呼び出し。IP6
->エラーを syslogip30_do_error_irq()
に報告するための呼び出し。HEART
IP5
->タイマーdo_IRQ()
に割り当てられたクロック イベント IRQ を処理するための呼び出しHEART
。IP4
、IP3
、および-> 0 から 49 までのすべての IRQ を処理するためのIP2
呼び出し。ip30_do_heart_irq()
HEART
これは、現在使用されているコードですip30_do_heart_irq()
:
static noinline void ip30_do_heart_irq(void)
{
int irqnum = 49;
int cpu = smp_processor_id();
u64 heart_isr = readq(HEART_ISR);
u64 heart_imr = readq(HEART_IMR(cpu));
u64 irqs = (heart_isr & 0x0003ffffffffffffULL &
heart_imr);
/* Poll all IRQs in decreasing priority order */
do {
if (irqs & (1UL << irqnum))
do_IRQ(irqnum);
irqnum--;
} while (likely(irqnum >= 0));
}
SMP サポートに関しては、他の Linux/MIPS プラットフォームとは異なり、どのような IPI アクションを実行する必要があるかを格納するメールボックス レジスタのようなものをハードウェアに持っていません。ip30_ipi_mailbox
元のコードは、他のプロセッサに渡す IPI アクションを指定するために、CPUID でインデックス付けされたグローバル int 配列 ( ) を使用します。
さらに、HEART
SGI は最大 4 つのプロセッサをサポートするように設計されていましたが、デュアル CPU モジュールしか製造していませんでした。したがって、IRQ 44
- 45
、48
- 49
、および61
-62
が実際に使用されることはありません。
これらのグローバル変数が与えられた場合:
#define IPI_CPU(x) (46 + (x))
static DEFINE_SPINLOCK(ip30_ipi_lock);
static u32 ip30_ipi_mailbox[4];
これは、IPI を他の CPU に送信するために現在使用されているコードです。
static void ip30_send_ipi_single(int cpu, u32 action)
{
unsigned long flags;
spin_lock_irqsave(&ip30_ipi_lock, flags);
ip30_ipi_mailbox[cpu] |= action;
spin_unlock_irqrestore(&ip30_ipi_lock, flags);
writeq(1UL << IPI_CPU(cpu)), HEART_SET_ISR);
}
IPI に応答するために、各 CPU はrequest_irq
初期化コードを呼び出し、割り込みハンドラを登録します。これは、IPI 割り込みを処理するためにハンドラーで現在使用されているコードです。
static irqreturn_t ip30_ipi_irq(int irq, void *dev_id)
{
u32 action;
int cpu = smp_processor_id();
unsigned long flags;
spin_lock_irqsave(&ip30_ipi_lock, flags);
action = ip30_ipi_mailbox[cpu];
ip30_ipi_mailbox[cpu] = 0;
spin_unlock_irqrestore(&ip30_ipi_lock, flags);
if (action & SMP_RESCHEDULE_YOURSELF)
scheduler_ipi();
if (action & SMP_CALL_FUNCTION)
smp_call_function_interrupt();
return IRQ_HANDLED;
}
そして、それが背景情報です。
私の現在のカーネル構成には、フレームバッファーと「Impact」ビデオドライバーを除くすべてが取り除かれています。PCI なし、ブロック層なし、ネットワークなし、シリアルなし、キーボード/マウスなし。私は〜7年前のinitramfsをロードしています。すべてが機能すれば、bashプロンプトにドロップするはずです。ただし、RAM に読み込まれるため、メモリの破損がすぐに明らかになる可能性があり、その結果、前述の SIGSEGV または SIGBUS エラーが発生します。
リモート GDB または組み込みの KGDB を使用することは、IOC3 PCI デバイスのため、現時点ではオプションではありません。IOC3 は、単一機能デバイスであると主張する多機能 PCI デバイスであり、その背後には、キーボード/マウス、シリアル ポート、リアルタイム クロック、およびイーサネット用のハードウェア ビットがあります。IOC3 を回避してリモート GDB のシリアル ポートに直接アクセスするためのコードはまだ存在しません。また、KGDB は IOC3 上の標準の i8042 キーボード コントローラーと対話する方法も知りません。
標準の PCI シリアル カード (Moschip ベース) を追加しましたが、そのドライバーは明らかにエンディアン セーフではないため、シリアル ポートをプローブするとカーネル パニックが発生します。
次の質問に答えることで、問題のあるコードをより適切に特定し、それを正しく機能させることに集中できるようになり、SMP を機能させるための正しい道に進むことができると思います。
- スピンロックを正しく使用していますか?
- 正しいスピンロックバリアントを使用していますか?
- 同期呼び出しをどこにでも追加する必要がありますか (つまり、
smp_rmb()
、smp_wmb()
など)? - 私の問題は、このコア プラットフォーム サポート コードの外 (ビデオ ドライバーなど) にあるのでしょうか?
- メモリを無作為に破壊する未知のハードウェア エラータを見ている可能性はありますか?
- 上記のコードのいずれかをより適切に実装できますか? (その多くは、Linux 2.6.17 の元のポートから Octane へのコードであり、カーネル内の他の動作とよりインラインになるように更新されたばかりです)
これを理解するための正しい道に私を置くことができる情報は高く評価されます. 私の希望は、SMP を使用可能な状態にすることです (効率は関係ありません。動作するために必要なだけです)。そのため、パッチに分割する作業を開始し、ある時点でそれをメインライン カーネルに含めることを確認できます。SMP を動作させることができない場合は、そのサポートを中止し、代わりにユニプロセッサ コードをアップストリームに送信することに集中します。