NetBSD-5.0BETA/i386のSMP実装#1
NetBSD-5.0BETA/i386のSMP実装がどうなっているのかについて調べてみる。
SMPサポート時のアナウンス
http://kerneltrap.org/node/443を読むと、コンフィグレーションが以下のように書き換わると書いてある:
Multiprocessor kernels need: cpu* at mainbus? ioapic* at mainbus? apid ? options MULTIPROCESSOR options COM_MPLOCK ..and if you want debugging and/or lots of output: options MPDEBUG options MPVERBOSE
なので、まずはここからみていく。
デバイスコンフィグレーション
sys/arch/i386/conf/std.i386をみると、以下のような項目がある:
options MULTIPROCESSOR # multiprocessor supprot options MPBIOS # configure CPUs and APICs using MPBIOS mainbus0 at root cpu* at mainbus? ioapic* at mainbus?
これが上述のコンフィグレーションと思われる。
ここからコードを辿っていく。おそらく、
などのコードを探していけばよい。
sys/arch/i386/i386/mainbus.c
mainbus_attach()の中でcpuの検出を行う関数を呼び出している。
mpacpiを使う場合とmpbiosを使う場合があるようだ。
ここではmpbios(少々古いハードウェアの場合)の方を見ていこうと思う。
#ifdef MPBIOS mpbios_present = mpbios_probe(self); #endif
最初にmpbios_probe()を呼び出し、
if (!mpacpi_active) { #ifdef MPBIOS if (mpbios_present) mpbios_scan(self, &numcpus, &numioapics); else #endif
次にmpbios_scan()を呼び、
#if defined(MPBIOS) && defined(MPBIOS_SCANPCI) if (mpbios_scanned != 0) mpbios_scan_pci(self, &mba.mba_pba, pcibusprint); else #endif config_found_ia(self, "pcibus", &mba.mba_pba, pcibusprint);
最後にmpbios_scan_pci()を呼び出している。
sys/arch/x86/x86/mpbios.c
mpbios_probe()は、mp floating pointer structureとmp configuration table headerを読み出して適切な値が読めているかチェックしている。
scan_loc = 0; if (ebda && ebda < IOM_BEGIN ) { mp_fps = mpbios_search(self, ebda, 1024, &mp_fp_map); if (mp_fps != NULL) goto found; } scan_loc = 1; if (memtop && memtop <= IOM_BEGIN ) { mp_fps = mpbios_search(self, memtop - 1024, 1024, &mp_fp_map); if (mp_fps != NULL) goto found; } scan_loc = 2; mp_fps = mpbios_search(self, BIOS_BASE, BIOS_COUNT, &mp_fp_map); if (mp_fps != NULL) goto found; /* nothing found */ return 0;
mp floating pointer structureの取得
cthpa = mp_fps->pap;
mp_cth = mpbios_map (cthpa, sizeof (*mp_cth), &mp_cfg_table_map);
cthlen = mp_cth->base_len;
mpbios_unmap(&mp_cfg_table_map);
mp_cth = mpbios_map (cthpa, cthlen, &mp_cfg_table_map);
mp configuration table headerの取得
mpbios_scan()では、mpbios_probe()で取得したfloating pointer structureとconfiguration table headerに設定されている値を元にデバイス(CPU以外にもバスや割り込み、APICなどの初期化があるらしい)の初期化を行っている。
CPUが一つ検出される毎にmpbios_cpu()が呼ばれる。
while ((count--) && (position < end)) { switch (type = *position) { case MPS_MCT_CPU: #if NACPI > 0 /* ACPI has done this for us */ if (mpacpi_ncpu) break; #endif mpbios_cpu(position, self); break;
CPU毎にmpbios_cpu()の呼び出しを行っているところ
mpbios_cpu()ではstruct cpu_attach_argsにパラメータを入れてcpnfig_found_sm_loc()を呼び出しているので、恐らくこれでcpuデバイスとして検出されるはず。
という事は、次に呼ばれるのは恐らくcpu_attach()になる。
static void mpbios_cpu(const uint8_t *ent, struct device *self) { const struct mpbios_proc *entry = (const struct mpbios_proc *)ent; struct cpu_attach_args caa; int locs[CPUBUSCF_NLOCS]; /* XXX move this into the CPU attachment goo. */ /* check for usability */ if (!(entry->cpu_flags & PROCENTRY_FLAG_EN)) return; mpbios_ncpu++; /* check for BSP flag */ if (entry->cpu_flags & PROCENTRY_FLAG_BP) caa.cpu_role = CPU_ROLE_BP; else caa.cpu_role = CPU_ROLE_AP; caa.cpu_number = entry->apic_id; caa.cpu_func = &mp_cpu_funcs; locs[CPUBUSCF_APID] = caa.cpu_number; config_found_sm_loc(self, "cpubus", locs, &caa, mp_cpuprint, config_stdsubmatch); }
sys/arch/x86/x86/cpu.c
cpu_attach()では、struct cpu_infoを初期化、cpu_vm_init()でuvm_page_recolor()を実行(?)、mi_cpu_attach()を呼んでスケジューラにCPUを認識させidlelwpをcpu_infoに割り当て、TSSの設定、割り込みの初期化、ページングの設定を行ってcpu_start_secondary()を呼んでいる。
この関数はプライマリCPUと共通なので、プライマリCPUとセカンダリ以降は区別されるようになっている。
sys/kern/kern_cpu.c
mi_cpu_attach()では、前述した通りスケジューラにCPUを認識させidlelwpをcpu_infoに割り当てている。
sched_cpuattach(ci);
sys/kern/kern_runq.cに存在
run queueをアロケート・初期化している。
error = create_idle_lwp(ci); if (error != 0) { /* XXX revert sched_cpuattach */ return error; } if (ci == curcpu()) ci->ci_data.cpu_onproc = curlwp; else ci->ci_data.cpu_onproc = ci->ci_data.cpu_idlelwp;
CPUがプライマリでなかったら実行中プロセスとしてidle_lwpを登録
percpu_init_cpu(ci);
謎
softint_init(ci);
softintの初期化
callout_init_cpu(ci);
calloutの初期化
xc_init_cpu(ci); pool_cache_cpu_init(ci); selsysinit(ci);
謎
cache_cpu_init(ci);
多分キャッシュまわり?
TAILQ_INIT(&ci->ci_data.cpu_biodone); ncpu++; ncpuonline++; return 0; }
キューを初期化して、CPUの数をカウントして終わり。