OpenBSD/sgi on octane2 - exception.S・tlbhandler.SにおけるGET_CPU_INFO()とコンテキスト保存、割り込みの話
exception.S・tlbhandler.Sでは、以下のような処理が行われている:
1. Exception発生、ベクタアドレスにジャンプ
2. ベクタアドレスに設置されたハンドラはException要因を調べexceptionテーブルに書いてある関数へジャンプ
3. 割り込み前のコンテキストを保存
4. 要因毎に必要な処理を行う
5. 割り込み前のコンテキストを復帰、eret
ここで注目したいのはコンテキストの保存・復帰なのだが、保存の前や復帰後に保持対象のレジスタに書き込みを行うと当然正しく復帰出来なくなる。
ただ、レジスタ一つも使えないんじゃ保存・復帰も行えないのでカーネル用にリザーブされたレジスタとしてk0, k1が存在している。
このレジスタはException時に壊してしまっていい事になっていて、事実上この辺りの処理専用のレジスタだ。
TLB Miss時に呼ばれるtlb_miss()、xtlb_miss()では高速化の為にexceptionテーブルの参照、コンテキストの保存・復帰の手順を省略してしまっている。
#TLB Missの度に呼ばれるので、一命令分でも速くしたいのだ。
が、省略したという事はレジスタを壊せないという事なので、この関数ではk0, k1レジスタのみを使用してレジスタを使って全処理を行っている:
.globl tlb_miss /* 0xffffffff80000000 */ .set noat .ent tlb_miss, 0 tlb_miss: PTR_L k1, curprocpaddr dmfc0 k0, COP_0_BAD_VADDR bltz k0, _k_miss # kernel address space PTR_SRL k0, k0, SEGSHIFT - LOGREGSZ PTR_L k1, PCB_SEGTAB(k1) andi k0, k0, (PMAP_SEGTABSIZE - 1) << LOGREGSZ PTR_ADDU k1, k1, k0 PTR_L k1, 0(k1) # get pointer to page table dmfc0 k0, COP_0_BAD_VADDR PTR_SRL k0, k0, PGSHIFT - 2 andi k0, k0, ((NPTEPG/2) - 1) << 3 beqz k1, _inv_seg # invalid segment map PTR_ADDU k1, k1, k0 # index into segment map lw k0, 0(k1) # get page PTE tlb_load: lw k1, 4(k1) dsll k0, k0, 34 dsrl k0, k0, 34 dmtc0 k0, COP_0_TLB_LO0 dsll k1, k1, 34 dsrl k1, k1, 34 dmtc0 k1, COP_0_TLB_LO1 nop # RM7000 needs 4 nops nop nop nop tlbwr # update TLB nop nop nop nop eret # RM7000 need 4 for JTLB usage. .end tlb_miss .globl e_tlb_miss e_tlb_miss:
こっから本題
SMP化コードでは、GET_CPU_INFO(ci, tmp)というマクロを書いた。
これはexception.Sでcurprocpaddrへの参照などをcpu_info[cpuid]->ci_curprocpaddrを参照するように変更する為のものだ。
このマクロでは引数に与えたci, tmpをレジスタ名として使用する。 ciがcpu_info[cpuid]でtmpは一時的に使うレジスタだ。
u_intr()などexception.Sの中のハンドラでは、コンテキスト保存先のアドレスとしてcurprocpaddrを参照している。
これをGET_CPU_INFO()を使うように書き換えたのだが、コンテキスト保存前なのでk0, k1を使うようにした:
NNON_LEAF(u_intr, FRAMESZ(CF_SZ), ra) .set noat .mask 0x80000000, (CF_RA_OFFS - FRAMESZ(CF_SZ)) GET_CPU_INFO(k1, k0) PTR_L k0, CI_CURPROCPADDR(k1) SAVE_CPU(k0, 0)
この調子で、exception.Sのそこら中にGET_CPU_INFO(k1, k0)と書いていたのだが、時折この辺りで落ちるバグが発生している:
dsrtc at ioc0 not configured "SGI Rad1" rev 0xc0 at pci0 dev 3 function 0 not configured rd0: fixed, 8192 blocks boot device: sd0 root on rd0a swap on rd0b dump on rd0b WARNING: No TOD clock, believing file system. WARNING: CHECK AND RESET THE DATE! Trap cause = 4 Frame 0xffffffffcffc7e78 Trap PC 0xa800000020156f34 RA 0xa800000020156ef8 fault 0x24d 0xa800000020156f34 ra 0xa800000020156ef8 sp 0xffffffffcffc7fd0 (0xffffffffc9107138,0x8,0x0,0x0) 0xa800000020156ef8 ra 0x0 sp 0xffffffffcffc7fd0 User-level: pid 32099 stopped on non ddb fault Stopped at 0xa800000020156f34: lw v0,588(k0) ddb{0}>
落ちる確立は50%以下、場所は毎回違う。
そこでふと気がついた。
もしかして、割り込みがかかるポイントでk0, k1を使ったコードを書いてるとまずいんちゃうか?
だって、k0, k1は保存・復帰されないんでしょ?
やべ。
ってな訳で、GET_CPU_INFO()を呼んでいる場所で使用レジスタの選択が妥当かチェック中。