OpenBSD/sgi on octane2 - option MULTIPROCESSORでの起動

一気にSMP実装を進めた所、どこにバグが有るのかさっぱり分からなくなって一度すべて元に戻した経緯が有る為、慎重に書き換えを行う事にしている。

まずはコンフィグレーションファイルにて、

option MULTIPROCESSOR

を有効にし、cpuの定義は

cpu0 at mainbus0

と書く事にし、SMP対応カーネルをシングルCPUで動かす所を目指す。

option MULTIPROCESSORを有効にした所でまず出たエラーは、cpu_boot_secondary_processorsなどのimplicit declaration of functionで、これはmips64/cpu.hとmips64/cpu.cに取り合えず関数を宣言しておけばどうにかなる。
(今は中身は空で良い。)

問題は__mp_lock()が無い、の方で、これはmplock.hを作ってcpu.hからincludeしてやる必要があるが、mplock.hの実装をどうすれば良いのか良く分からない。
macppc/mplock.hはMachine independentな実装になっているように見受けられるので、これをそのままコピーしてみた。
コンパイルは通る。見たところではうまく動きそうな気がする。

・・・が、これがうまく動かない。
カーネルが起動してから始めて通過するSCHED_LOCK(s)でロックを確保出来ずに永久ループに入ってしまうらしい。
(SCHED_LOCK()は__mp_lockを呼ぶようにdefineされている)
MP_LOCKDEBUGを有効にすると、db_printf() -> Debugger()の流れに落ちてDDBが上がってくる。

			int got_it;
			do {
				int ticks = __mp_lock_spinout;

				do {
					got_it = __cpu_simple_lock_try(
					    &lock->mpl_lock);
				} while (!got_it && ticks-- > 0);
				if (!got_it) {
 					db_printf(
					    "__mp_lock(0x%x): lock spun out",
					    lock);
					Debugger();
				}
			} while (!got_it)

もう少し詳しく知るために、mplock.h, lock.hにデバッグprintを追加してみた:

Command Monitor.  Type "exit" to return to the menu.
>> bootp()192.168.3.1:bsd
Setting $netaddr to 192.168.3.2 (from server )
Obtaining bsd from server
5946304+492712 entry: 0xa800000020020000
ARCS64 Firmware Version 64.0
Found SGI-IP30, setting up.
Initial setup done, switching console.
cpu_id:0
Copyright (c) 1982, 1986, 1989, 1991, 1993
        The Regents of the University of California.  All rights reserved.
Copyright (c) 1995-2009 OpenBSD. All rights reserved.  http://www.OpenBSD.org

[machine/mplock.h:__mp_lock_init:47] ra:0xa80000002004a850
[mips64/lock.h:__cpu_simple_lock_init:18] l:0xa800000020617b80 *l:0
OpenBSD 4.5-current (OCTANE_RAMDISK) #1: Thu May  7 22:06:45 JST 2009
    asada@ami.s.axe-inc.co.jp:/home/asada/openbsd-octane/src/sys/arch/sgi/compile/OCTANE_RAMDISK
real mem = 1073741824 (1024MB)
rsvd mem = 1064960 (1MB)
avail mem = 1013727232 (966MB)
[machine/mplock.h:__mp_lock_init:47] ra:0xa80000002004ae70
[mips64/lock.h:__cpu_simple_lock_init:18] l:0xa80000002061fcb0 *l:0
mainbus0 at root
cpu0 at mainbus0: MIPS R12000 CPU rev 3.5 400 MHz with R10000 FPU rev 0.0
cpu0: cache L1-I 32KB D 32KB 2 way, L2 2048KB 2 way
clock0 at mainbus0: ticker on int5 using count register
xbow0 at mainbus0: XBow revision 5
xheart0 at xbow0 widget 8: Heart revision 6
onewire0 at xheart0
owserial0 at onewire0 family 0x0b sn 0000004d74fa
owserial0: "PM20400MHZ" p/n 030-1476-001, serial KWL381
"Odyssey" revision 2 at xbow0 widget 11 not configured
xbridge0 at xbow0 widget 15: Bridge revision 4
pci0 at xbridge0 bus 0
isp0 at pci0 dev 0 function 0 "QLogic ISP1020" rev 0x05: irq 0
isp0: invalid NVRAM header
scsibus0 at isp0: 16 targets, initiator 7
[machine/mplock.h:__mp_lock:67] ra:0xa800000020066690
[mips64/lock.h:__cpu_simple_lock_try:47] l:0xa80000002061fcb0 *l:0 old:1
[mips64/lock.h:__cpu_simple_lock_try:47] l:0xa80000002061fcb0 *l:0 old:1
[mips64/lock.h:__cpu_simple_lock_try:47] l:0xa80000002061fcb0 *l:0 old:1
[mips64/lock.h:__cpu_simple_lock_try:47] l:0xa80000002061fcb0 *l:0 old:1
[mips64/lock.h:__cpu_simple_lock_try:47] l:0xa80000002061fcb0 *l:0 old:1
[mips64/lock.h:__cpu_simple_lock_try:47] l:0xa80000002061fcb0 *l:0 old:1
[mips64/lock.h:__cpu_simple_lock_try:47] l:0xa80000002061fcb0 *l:0 old:1
[mips64/lock.h:__cpu_simple_lock_try:47] l:0xa80000002061fcb0 *l:0 old:1
[mips64/lock.h:__cpu_simple_lock_try:47] l:0xa80000002061fcb0 *l:0 old:1
[mips64/lock.h:__cpu_simple_lock_try:47] l:0xa80000002061fcb0 *l:0 old:1
[mips64/lock.h:__cpu_simple_lock_try:47] l:0xa80000002061fcb0 *l:0 old:1
[mips64/lock.h:__cpu_simple_lock_try:47] l:0xa80000002061fcb0 *l:0 old:1
[mips64/lock.h:__cpu_simple_lock_try:47] l:0xa80000002061fcb0 *l:0 old:1
[mips64/lock.h:__cpu_simple_lock_try:47] l:0xa80000002061fcb0 *l:0 old:1
[mips64/lock.h:__cpu_simple_lock_try:47] l:0xa80000002061fcb0 *l:0 old:1
[mips64/lock.h:__cpu_simple_lock_try:47] l:0xa80000002061fcb0 *l:0 old:1
[mips64/lock.h:__cpu_simple_lock_try:47] l:0xa80000002061fcb0 *l:0 old:1
[mips64/lock.h:__cpu_simple_lock_try:47] l:0xa80000002061fcb0 *l:0 old:1
[mips64/lock.h:__cpu_simple_lock_try:47] l:0xa80000002061fcb0 *l:0 old:1
[mips64/lock.h:__cpu_simple_lock_try:47] l:0xa80000002061fcb0 *l:0 old:1
[mips64/lock.h:__cpu_simple_lock_try:47] l:0xa80000002061fcb0 *l:0 old:1
[mips64/lock.h:__cpu_simple_lock_try:47] l:0xa80000002061fcb0 *l:0 old:1

mplock.hでra:%pと表示しているのは__builtin_return_address(0)の事で、これは現在のraの値を取り出すビルトイン関数。
[machine/mplock.h:__mp_lock_init:47] ra:0xa80000002004ae70のraはmain()でsched_init_runqueues()を呼んでいる所。この関数は単に__mp_lock_init(&sched_lock)を実行する。
次の[machine/mplock.h:__mp_lock:67] ra:0xa800000020066690はwakeup_n()で、この関数である事には深い意味はなく、単に一番最初にSCHED_LOCK(s)を呼んだ関数というだけの事。
問題は、今まで誰もロックしておらず、CPUが1つしか無いにも関わらず、__cpu_simple_lock_try(&lock->mpl_lock) == 0と判定されてリトライを繰り返しているように見える事。
何故だ? *l == &lock->mpl_lockはきちんと__mp_lock_init()で__SIMPLELOCK_UNLOCKED == 0にセットしているはず。
というか、現に__cpu_simple_lock_tryでも*lは0だ。
にも関わらず、oldは1だ。ん?なんで?

static __inline__ int
__cpu_simple_lock_try(__cpu_simple_lock_t *l)
{
	__cpu_simple_lock_t old, new = __SIMPLELOCK_LOCKED;

	__asm__ __volatile__
	   ("1:\tll\t%0, %1\n" 
	    "\tsc\t%2, %1\n"
	    "\tbeqz\t%2, 1b\n"
	    "\t nop" : "=r" (old) : "m" (*l), "r" (new));
	extern void printf(const char *,...);
	printf("[%s:%s:%d] l:%p *l:%x old:%x\n", __FILE__, __func__, __LINE__, l, *l, old);
	return (old == __SIMPLELOCK_UNLOCKED);
}

アトミック処理が成功したかどうかはsc命令で判断するので、ll命令は%1のメモリアドレスから32bit幅で値を読み出して%0に代入するだけなはず。
だから、一度も失敗しなかったら
new = 1, *l = 0
ll old, *lでold <- 0
sc new, *lで*l <- 1, new <- 1(成功)
beqz new, 1bでブランチせずに抜ける
となるはず。
どういう状況で*l = 0, old = 1となるのか?
いまいち良く分からない。
なんだか気持ち悪い事になっているような気がする。

しかし、これ、どうしてscで%2を上書きするのにそのままループしてるんだろう。
scがfailしてnew <- 0, beqzで1bへブランチした場合、どうなる?

取り合えず、考えるのめんどいからNetBSD/mipsのコードで置き換えてみる、、、かなぁ・・。