あるコンテキストスイッチの話

41
あるコンテキストスイッチの話 えとみ なるあき OSC名古屋 Kernel/VM探検隊@北陸

Upload: nullnilaki

Post on 08-Sep-2014

583 views

Category:

Technology


3 download

DESCRIPTION

2014年7月5日に行われたOSC名古屋、及び2014年7月12日にカーネル/VM探検隊@北陸 1でのスライドです。 楽しさ求めて、もうちょっとはじけちゃえ(ぴょんぴょんと)

TRANSCRIPT

Page 1: あるコンテキストスイッチの話

あるコンテキストスイッチの話

えとみ なるあきOSC名古屋

Kernel/VM探検隊@北陸

Page 2: あるコンテキストスイッチの話

お約束

※ コンテキストスイッチのやりかたはいくつか方法(setjmp,longjmpなど)が あると思いますが、 setjmp,longjmpなどは今回は扱いません。※ MPカーネルも扱いません...(頭が追いついていないので(´・ω・`))※ wikipediaのコンテキストスイッチの項目をあらかじめ読んでおくと、 よりいっそう楽しめる内容になっております。※ trapb命令は覚えておくと良いキーワードです。※ exception_return()でFPUをON/OFしているというのは重要です。※ ぴょんぴょんも重要キーワードです。※ 予習→実装という流れでお話は進みます!

ソースコードはおまけです!資料のCPUはalphaですがarm、 mips、powerpc全て実装が違います。この資料を読んで概念を身につけたあとに、一人でも自分の好きなCPUの実装を調べた方がいらっしゃったら嬉しいです

Page 3: あるコンテキストスイッチの話

これまでのあらすじ

※ ある日のNetBSD/alpha currentでtopをすると Floating point exceptionでcore dumpする という事件が発生した!

※ PR-48782でgccのバグだと勘違いして主張してみた!(libmのコンパイルオプションが変更されてた)

“ I think that this case is -mieee-with-inexact option bug.”

※ とりあえず変更はrevretされたので自分でも独自調査 “I think that trapb instruction incompatible with fpu_state_load().”と返答し へぼパッチを投げる

“lazy FPU context switch”って何?調べてみました!

※ その後、いろいろやりとりして、topのバグでもなく、gccのバグでもなく カーネルのlazy FPU context switchのコードがバグっていたという結論がでた!

Page 4: あるコンテキストスイッチの話

まずコンテキストスイッチとは何なのか?

コンテキストスイッチ (context switch) とは、複数のプロセスが1つのCPUを共有できるように、CPUの状態(コンテキスト)を保存したり復元したりする過程のことである。

コンテキストスイッチはマルチタスクオペレーティングシステムに不可欠な機能である。通常コンテキストスイッチは多くの計算機処理を必要とするため、オペレーティングシステムの設計においてはコンテキストスイッチを最適化することが重要である。

コンテキストスイッチでは、実行中のプロセスの状態を何らかの方法で保存し、後にそのプロセスを再開する際にその状態を復元して、正常に実行を継続できるようにしなければならない。

プロセスの状態には、そのプロセスが使用し得る全てのレジスタ(特にプログラムカウンタ)や、プロセスの実行に必要となるオペレーティングシステム固有の情報が含まれる。

wikipediaより

Page 5: あるコンテキストスイッチの話

ぴょんぴょん

つまりOSのなかでプロセスがぴょんぴょんしている訳ですね!

mi_switch()

ご注文はどのLWPですか?

Page 6: あるコンテキストスイッチの話

描いてみた

sh

mi_switch syslog

mi_switch mikutter(ruby)

mi_switch mlterm

shmi_switch

コンテキストスイッチ

コンテキストスイッチ

コンテキストスイッチ

コンテキストスイッチ

コンテキストスイッチするとき、実行中のプロセスの状態を「どこ」に保存しているのか?

PCB(Process control block)

Page 7: あるコンテキストスイッチの話

PCB is 何?

Alphaの浮動小数点レジスタは64bit X 32本+ FPUコントロールレジスタ

swpctx命令でコンテキストされる

/src/sys/arch/alpha/include/alpha_cpu.h src/sys/arch/alpha/include/pcb.h

src/sys/arch/alpha/include/reg.h

Page 8: あるコンテキストスイッチの話

予習として描いてみた(ポルナレフもビックリっーか…)

mikutterのプロセスmltermのプロセス

CPU実行中のmikutterから

mltermにスイッチ

不思議な力で次の実行プロセスは

mltermに決定

mikutterプロセスのPCBにCPUのレジスタとか保存

mltermプロセスのPCBの内容をCPUにロード

『The WORLD』 オレだけの時間だぜ

CPU実行中のmltermからmikutterにスイッチ

不思議な力で次の実行プロセスは

mikutterに決定

mltermプロセスのPCBにCPUのレジスタとか保存

mikutterプロセスのPCBの内容をCPUにロード

『時』は動き出す….

Page 9: あるコンテキストスイッチの話

では実装(コンテキストスイッチをするところ)

Page 10: あるコンテキストスイッチの話

mi_switchの実装/* * The machine independent parts of context switch. * * Returns 1 if another LWP was actually run. */intmi_switch(lwp_t *l){

struct cpu_info *ci;struct schedstate_percpu *spc;struct lwp *newl;int retval, oldspl;struct bintime bt;bool returning;

………./* Switch to the new LWP.. */prevlwp = cpu_switchto(l, newl, returning);ci = curcpu();

/* * Switched away - we have new curlwp. * Restore VM context and IPL. */pmap_activate(l);uvm_emap_switch(l);pcu_switchpoint(l);

/sys/kern/kern_synch.c l

newl

lの情報(PC等)がlのPCBに保存される

newlの情報(PC等)がnewlのPCBからロードされる

mi_switchがカーネル内のどこから呼ばれるか?がミソ

Page 11: あるコンテキストスイッチの話

mi_switchの実装/* * The machine independent parts of context switch. * * Returns 1 if another LWP was actually run. */intmi_switch(lwp_t *l){

struct cpu_info *ci;struct schedstate_percpu *spc;struct lwp *newl;int retval, oldspl;struct bintime bt;bool returning;

………./* Switch to the new LWP.. */prevlwp = cpu_switchto(l, newl, returning);ci = curcpu();

/* * Switched away - we have new curlwp. * Restore VM context and IPL. */pmap_activate(l);uvm_emap_switch(l);pcu_switchpoint(l);

別のLWPにコンテキストスイッチする関数

/sys/kern/kern_synch.c

/* * struct lwp *cpu_switchto(struct lwp *current, struct lwp *next) * Switch to the specified next LWP * Arguments: * a0 'struct lwp *' of the LWP to switch from * a1 'struct lwp *' of the LWP to switch to */LEAF(cpu_switchto, 0) LDGP(pv)

beq a0, 1f

/* * do an inline savectx(), to save old context */ ldq a2, L_PCB(a0) /* NOTE: ksp is stored by the swpctx */ stq s0, PCB_CONTEXT+(0 * 8)(a2) /* store s0 - s6 */ stq s1, PCB_CONTEXT+(1 * 8)(a2)

/sys/arch/alpha/alpha/locore.s

Page 12: あるコンテキストスイッチの話

ここまでのおさらい

     ※ OSのなかではプロセスがぴょんぴょんしている

     ※ 違うプロセスに移る時にプロセスの状態を保存する

     ※ 違うプロセスから復帰する時にプロセスの状態を復元する

     ※ 保存する状態はCPUのレジスタとプロセスの情報

     ※ 保存する場所をPCBという

Page 13: あるコンテキストスイッチの話

lazy FPU context switchのFPUとは?

Macintosh IIciよりMC68882

FPU(Floating Point Unit、浮動小数点(演算処理)装置)とは、浮動小数点演算を専門に行う処理装置のこと。コンピュータの周辺機器のようなアーキテクチャのものもあれば、CPUと一体化したコプロセッサのようなアーキテクチャのものもある。

wikipediaより

※ FPUは浮動小数点専用のレジスタを持つ※ でも、どんなプログラムもFPUを使うとは限らない…※ コンテキストスイッチではCPUのレジスタを保存復帰する※ FPUのレジスタはサイズがでかい、かつ数が多いので保存復帰が超重い…

Page 14: あるコンテキストスイッチの話

lazy FPU context switchとは?

\  __  /_ (m) _  ピコーン   |ミ|/ `´  \  ( ゚∀゚) ノヽノ |  < <

FPUを使うときだけFPUの内容をコンテキストスイッチしよう!

つまり、Lazy context switchとはプロセス切替時に FPUのレジスタを復帰させるのではなく、プロセス切替後に FPUが使われた時点で初めて復帰させることでFPUを使わない場合のコンテキストスイッチを軽くする技!なのです!

ΩΩΩ<な、なんだってー!?

Page 15: あるコンテキストスイッチの話

PCUとは?

Per CPU Unit (PCU) is an interface to manage synchronization of any per-CPU context (unit) tied to an LWP context. Typical use of PCU is for”lazy-switch” synchronization of the FPU state.  NetBSD Kernel Developer's Manual PCU(9)

NetBSDでLazy context switchの対象となるCPU※ Alpha FPUは1個※ ARM FPUは1個※ MIPS FPUは2個(FPUとDSP)※ PowerPC FPUは2個(FPUとAltiVec/SPE)

※ FPUを無効化出来るCPU※ 無効化状態でFPUを使おうとすると浮動小数点無効フォルトが発生するCPU

Page 16: あるコンテキストスイッチの話

PCUを理解するためのお約束※ すべてのLWPはFPUが無効の状態で作成される

struct lwp {/* Scheduling and overall state. */

……….#if PCU_UNIT_COUNT > 0

struct cpu_info * volatile l_pcu_cpu[PCU_UNIT_COUNT];uint32_t l_pcu_valid;

#endif………….

/sys/sys/lwp.h

struct cpu_data {/* * The first section is likely to be touched by other CPUs - * it is cache hot. */lwp_t *cpu_biglock_wanted; /* LWP spinning on biglock */

………..struct lwp * volatile cpu_pcu_curlwp[PCU_UNIT_COUNT];

……….

/sys/sys/cpu_data.h

← FPUを使用している(た) LWPのアドレス

← FPUを使用している(た)  CPU構造体のアドレス

Page 17: あるコンテキストスイッチの話

予習として描いてみた

exec

setreg

fpu_state_release

PCBのFPUを保存する領域を初期化

~MDLWP_FPACTIVEでFPU無効化フラグを立てる

exception_return FPUが使えなくなる(*^-゚)vィェィ♪

Page 18: あるコンテキストスイッチの話

では実装(プロセスが作成される所まで)

Page 19: あるコンテキストスイッチの話

execve_runproc/sys/kern/kern_exec.cstatic intexecve_runproc(struct lwp *l, struct execve_data * restrict data,

bool no_local_exec_lock, bool is_spawn){

struct exec_package * const epp = &data->ed_pack;int error = 0;struct proc *p;

………./* * Set initial SP at the top of the stack. * * Note that on machines where stack grows up (e.g. hppa), SP points to * the end of arg/env strings. Userland guesses the address of argc * via ps_strings::ps_argvstr. */

/* Setup new registers and do misc. setup. */(*epp->ep_esch->es_emul->e_setregs)(l, epp, (vaddr_t)newstack);if (epp->ep_esch->es_setregs)

(*epp->ep_esch->es_setregs)(l, epp, (vaddr_t)newstack);……….

/* Discard all PCU state; need to start fresh */pcu_discard_all(l);

まずはPCBの初期化から

Page 20: あるコンテキストスイッチの話

setregsユーザープロセスがプログラムを実行できるようにスタックの設定、レジスタの初期化を行う

/sys/arch/alpha/alpha/machdep.c

/* * Set registers on exec. */voidsetregs(register struct lwp *l, struct exec_package *pack, vaddr_t stack){

struct trapframe *tfp = l->l_md.md_tf;struct pcb *pcb;

……….pcb = lwp_getpcb(l);memset(&pcb->pcb_fp, 0, sizeof(pcb->pcb_fp));alpha_pal_wrusp(stack);tfp->tf_regs[FRAME_PS] = ALPHA_PSL_USERSET;tfp->tf_regs[FRAME_PC] = pack->ep_entry & ~3;

tfp->tf_regs[FRAME_A0] = stack; /* a0 = sp */tfp->tf_regs[FRAME_A1] = 0; /* a1 = rtld cleanup */tfp->tf_regs[FRAME_A2] = 0; /* a2 = rtld object */tfp->tf_regs[FRAME_A3] = l->l_proc->p_psstrp; /* a3 = ps_strings */tfp->tf_regs[FRAME_T12] = tfp->tf_regs[FRAME_PC]; /* a.k.a. PV */

←PCBのFPUを保存する領域 を初期化する

Page 21: あるコンテキストスイッチの話

execve_runprocsys/kern/kern_exec.cstatic intexecve_runproc(struct lwp *l, struct execve_data * restrict data,

bool no_local_exec_lock, bool is_spawn){

struct exec_package * const epp = &data->ed_pack;int error = 0;struct proc *p;

………./* * Set initial SP at the top of the stack. * * Note that on machines where stack grows up (e.g. hppa), SP points to * the end of arg/env strings. Userland guesses the address of argc * via ps_strings::ps_argvstr. */

/* Setup new registers and do misc. setup. */(*epp->ep_esch->es_emul->e_setregs)(l, epp, (vaddr_t)newstack);if (epp->ep_esch->es_setregs)

(*epp->ep_esch->es_setregs)(l, epp, (vaddr_t)newstack);……….

/* Discard all PCU state; need to start fresh */pcu_discard_all(l);

次はFPUの無効化

Page 22: あるコンテキストスイッチの話

/sys/kern/subr_pcu.c

FPUを無効化する

const pcu_ops_t fpu_ops = {.pcu_id = PCU_FPU,.pcu_state_load = fpu_state_load,.pcu_state_save = fpu_state_save,.pcu_state_release = fpu_state_release,

};const pcu_ops_t * const pcu_ops_md_defs[PCU_UNIT_COUNT] = {

[PCU_FPU] = &fpu_ops,};

/* * pcu_discard_all: discard PCU state of the given LWP. * * Used by exec and LWP exit. */voidpcu_discard_all(lwp_t *l){

const uint32_t pcu_valid = l->l_pcu_valid;

if (__predict_true(pcu_valid == 0)) {/* PCUs are not in use. */return;

}for (u_int id = 0; id < PCU_UNIT_COUNT; id++) {

if ((pcu_valid & (1U << id)) == 0) {continue;

}if (__predict_true(l->l_pcu_cpu[id] == NULL)) {

continue;}const pcu_ops_t * const pcu = pcu_ops_md_defs[id];pcu_lwp_op(pcu, l, PCU_CMD_RELEASE);

}l->l_pcu_valid = 0;

}

/src/sys/arch/alpha/alpha/machdep.c

親LWPがFPUを使っていたら、FPUを無効化するためにpcu_lwp_op()内でpcu_ops_t構造体経由でfpu_state_release()を呼びます

Page 23: あるコンテキストスイッチの話

FPUを無効化する

/* * Release the FPU. */voidfpu_state_release(struct lwp *l){

l->l_md.md_flags &= ~MDLWP_FPACTIVE;}

/src/sys/arch/alpha/alpha/fp_complete.c

#define MDLWP_FP_C 0x007ffffe /* Extended FP_C Quadword bits */#define MDLWP_FPACTIVE __BIT(63) /* FPU is active on LWP's PCU CPU */

/src/sys/arch/alpha/include/proc.h

←フラグをセットします

この時点ではまだFPUは無効ではありません!フラグをセットしただけです。

Page 24: あるコンテキストスイッチの話

/* * exception_return: return from trap, exception, or syscall */

IMPORT(ssir, 8)

LEAF(exception_return, 1) /* XXX should be NESTED */br pv, 1f

……….

/* GET_CPUINFO clobbers v0, t0, t8...t11. */3: GET_CPUINFO

/* check for AST */ldq t1, CPU_INFO_CURLWP(v0)ldl t3, L_MD_ASTPENDING(t1) /* AST pending? */bne t3, 7f /* yes *//* no: headed back to user space */

/* Enable the FPU based on whether MDLWP_FPACTIVE is set. */4: ldq t2, L_MD_FLAGS(t1)

cmplt t2, zero, a0call_pal PAL_OSF1_wrfen

FPUを無効化した状態でプロセスの作成を完了する

trap()/systemcall()を終了するときにexception_return()でFPUを有効化したり無効化したりします

/sys/arch/alpha/alpha/locore.s FENに1を立てるとFPUがON

Page 25: あるコンテキストスイッチの話

次にFPUを使ってみる

……….$main..ng: lda $30,-64($30) .cfi_def_cfa_offset 64 stq $26,0($30) stq $15,8($30)………. stl $1,48($15) ldah $1,$LC0($29) !gprelhigh ldt $f10,$LC0($1) !gprellow………. divt/sui $f12,$f11,$f10 trapb stt $f10,32($15)……….

FPUが無効化された状態でLWPが作成されました!次はFPUを使ってみましょう!

Page 26: あるコンテキストスイッチの話

予習として描いてみた

FPUが使えない状態でLWP作成

浮動小数点命令を発効

※ 以前にCPU(FPU)を使用していたLWPのFPUレジスタの内容を そのLWPのPCBにセーブ/リリース※ 自分用のPCBに保存してあるFPUのレジスタの内容をFPUにロード※ exception_return()でFPU有効化

trap()

Page 27: あるコンテキストスイッチの話

では実装(FPUがロードされる所まで)

Page 28: あるコンテキストスイッチの話

浮動小数点命令を実行すると、浮動小数点無効フォルトが発生する

/sys/arch/alpha/alpha/trap.c 浮動小数点命令を実行出来るようにfpu_load(pcu_load)を呼びます/*

* Trap is called from locore to handle most types of processor traps. * System calls are broken out for efficiency and ASTs are broken out * to make the code a bit cleaner and more representative of the * Alpha architecture. *//*ARGSUSED*/voidtrap(const u_long a0, const u_long a1, const u_long a2, const u_long entry, struct trapframe *framep){

struct lwp *l;struct proc *p;struct pcb *pcb;

……….case ALPHA_KENTRY_IF:

/* * These are always fatal in kernel, and should

never * happen. (Debugger entry is handled in XentIF.)

……….case ALPHA_IF_CODE_FEN:

fpu_load();goto out;

static inline voidfpu_load(void){

pcu_load(&fpu_ops);}

static inline voidfpu_save(void){

pcu_save(&fpu_ops);}

static inline voidfpu_discard(bool valid_p){

pcu_discard(&fpu_ops, valid_p);}

Page 29: あるコンテキストスイッチの話

FPUをsave/releaseする

もし、このLWPがFPU(CPU)の実行権限を取得する「以前」に別のLWPがFPUを使っていたらFPUのレジスタをセーブ、リリース

/* * pcu_load: load/initialize the PCU state of current LWP on current CPU. */voidpcu_load(const pcu_ops_t *pcu){

lwp_t *oncpu_lwp, * const l = curlwp;const u_int id = pcu->pcu_id;struct cpu_info *ci, *curci;int s;

……….

/* Save the PCU state on the current CPU, if there is any. */if ((oncpu_lwp = curci->ci_pcu_curlwp[id]) != NULL) {

pcu_do_op(pcu, oncpu_lwp, PCU_CMD_SAVE | PCU_CMD_RELEASE);}

………./* * Finally, load the state for this LWP on this CPU. Indicate to * the load function whether PCU state was valid before this call. */const bool valid = ((1U << id) & l->l_pcu_valid) != 0;pcu->pcu_state_load(l, valid ? PCU_VALID : 0);curci->ci_pcu_curlwp[id] = l;l->l_pcu_cpu[id] = curci;l->l_pcu_valid |= (1U << id);splx(s);

}

/sys/kern/subr_pcu.c

Page 30: あるコンテキストスイッチの話

FPUをsave/releaseする

/* * pcu_do_op: save/release PCU state on the current CPU. * * => Must be called at IPL_PCU or from the interrupt. */static inline voidpcu_do_op(const pcu_ops_t *pcu, lwp_t * const l, const int flags){

struct cpu_info * const ci = curcpu();const u_int id = pcu->pcu_id;

KASSERT(l->l_pcu_cpu[id] == ci);

if (flags & PCU_CMD_SAVE) {pcu->pcu_state_save(l);

}if (flags & PCU_CMD_RELEASE) {

pcu->pcu_state_release(l);ci->ci_pcu_curlwp[id] = NULL;l->l_pcu_cpu[id] = NULL;

}}

/sys/kern/subr_pcu.c

← save/releaseをするから  割り込まれたくない?

    ∧ ∧___   /(*゚ー゚) /\ /|‾∪∪‾|\/   |  FPU |/    ‾‾‾‾

  ∧ ∧ (*゚ー゚) |つ ⊂~O‐つ

FPUを使いたいのにスイッチ以前のLWPの情報が残ってる….FPUレジスタの内容を以前のLWPのPCBに保存しないと…

FPUのレジスタの内容だけスイッチ時にPCBに退避されてないのねん

Page 31: あるコンテキストスイッチの話

FPUをsaveする/* * Save the FPU state. */

voidfpu_state_save(struct lwp *l){

struct pcb * const pcb = lwp_getpcb(l);

alpha_pal_wrfen(1);savefpstate(&pcb->pcb_fp);alpha_pal_wrfen(0);

}

/* * savefpstate: Save a process's floating point state. * * Arguments: * a0 'struct fpstate *' to save into */

LEAF(savefpstate, 1) LDGP(pv) /* save all of the FP registers */ lda t1, FPREG_FPR_REGS(a0) /* get address of FP reg. save area */ stt $f0, (0 * 8)(t1) /* save first register, using hw name */ stt $f1, (1 * 8)(t1) /* etc. */………. /* * Then save the FPCR; note that the necessary 'trapb's are taken * care of on kernel entry and exit. */ mf_fpcr ft0 stt ft0, FPREG_FPR_CR(a0) /* store to FPCR save area */

RET END(savefpstate)

※alpha_pal_wrfenはfpuを有効にする関数(引数1で有効、引数0で無効)一時的にFPUにアクセスするので有効にします

/sys/arch/alpha/alpha/fp_complete.c /sys/arch/alpha/alpha/locore.s

Page 32: あるコンテキストスイッチの話

FPUをloadする

※ 現在のLWPのPCB領域に保存してある FPUレジスタの内容をFPUにロード※ CPU構造体に現在のLWPのアドレス LWP構造体に現在のCPU構造体のアドレス 何個目のFPUを使用しているか をセット

/* * pcu_load: load/initialize the PCU state of current LWP on current CPU. */voidpcu_load(const pcu_ops_t *pcu){

lwp_t *oncpu_lwp, * const l = curlwp;const u_int id = pcu->pcu_id;struct cpu_info *ci, *curci;int s;

……….

/* Save the PCU state on the current CPU, if there is any. */if ((oncpu_lwp = curci->ci_pcu_curlwp[id]) != NULL) {

pcu_do_op(pcu, oncpu_lwp, PCU_CMD_SAVE | PCU_CMD_RELEASE);}

………./* * Finally, load the state for this LWP on this CPU. Indicate to * the load function whether PCU state was valid before this call. */const bool valid = ((1U << id) & l->l_pcu_valid) != 0;pcu->pcu_state_load(l, valid ? PCU_VALID : 0);curci->ci_pcu_curlwp[id] = l;l->l_pcu_cpu[id] = curci;l->l_pcu_valid |= (1U << id);splx(s);

}

/sys/kern/subr_pcu.c

Page 33: あるコンテキストスイッチの話

FPUをloadする/* * Load the float-point context for the current lwp. */voidfpu_state_load(struct lwp *l, u_int flags){

struct pcb * const pcb = lwp_getpcb(l);……….

if ((flags & PCU_VALID) == 0) {atomic_inc_ulong(&fpevent_use.ev_count);

} else {atomic_inc_ulong(&fpevent_reuse.ev_count);

}

alpha_pal_wrfen(1);restorefpstate(&pcb->pcb_fp);alpha_pal_wrfen(0);

l->l_md.md_flags |= MDLWP_FPACTIVE;}

※alpha_pal_wrfenはfpuを有効にする関数(引数1で有効、引数0で無効)一時的にFPUにアクセスするので有効にします

/sys/arch/alpha/alpha/fp_complete.c /sys/arch/alpha/alpha/locore.s/* * restorefpstate: Restore a process's floating point state. * * Arguments: * a0 'struct fpstate *' to restore from */

LEAF(restorefpstate, 1) LDGP(pv) /* * Restore the FPCR; note that the necessary 'trapb's are taken care of * on kernel entry and exit. */ ldt ft0, FPREG_FPR_CR(a0) /* load from FPCR save area */ mt_fpcr ft0

/* Restore all of the FP registers. */ lda t1, FPREG_FPR_REGS(a0)/* get address of FP reg. save area */ ldt $f0, (0 * 8)(t1) /* restore first reg., using hw name */ ldt $f1, (1 * 8)(t1) /* etc. */………. ldt $f28, (28 * 8)(t1) ldt $f29, (29 * 8)(t1) ldt $f30, (30 * 8)(t1)

RET END(restorefpstate)

Page 34: あるコンテキストスイッチの話

ここまでのおさらい

   ※ すべてのLWPはFPUが無効の状態で作成される

   ※ FPUが無効の状態で浮動小数点命令を実行すると 浮動小数点無効フォルトが発生する

   ※ (もしコンテキストスイッチ以前のLWPがFPUを使っていたら) FPUのレジスタをセーブ、リリースする

∧ ∧  。 (*゚ー゚) /  | ⊃⊃~|   |  U~U

では応用編へ

Page 35: あるコンテキストスイッチの話

予習として描いてみた

FPUが使えない状態でLWP作成

浮動小数点命令を発効

※ 以前にCPU(FPU)を使用していたLWPのFPUレジスタの内容を そのLWPのPCBにセーブ/リリース※ 自分用のPCBに保存してあるFPUのレジスタの内容をFPUにロード※ exception_return()でFPU有効化

mi_switch syslog

mi_switch top

浮動小数点命令を発効

浮動小数点命令

普通の命令

trap()

コンテキストスイッチpcu_switchpoint()でFPU無効化マーク

コンテキストスイッチpcu_switchpoint()でFPU無効化マーク

trap()

※ 以前にCPU(FPU)を使用していたLWPのFPUレジスタの内容を そのLWPのPCBにセーブ/リリース

このタイミングでFPUを保存CPUのコンテキストスイッチと

FPUのコンテキストスイッチが一致しない

Page 36: あるコンテキストスイッチの話

では実装(コンテキストスイッチされるところ)

Page 37: あるコンテキストスイッチの話

mi_switchの実装/* * The machine independent parts of context switch. * * Returns 1 if another LWP was actually run. */intmi_switch(lwp_t *l){

struct cpu_info *ci;struct schedstate_percpu *spc;struct lwp *newl;int retval, oldspl;struct bintime bt;bool returning;

………./* Switch to the new LWP.. */prevlwp = cpu_switchto(l, newl, returning);ci = curcpu();

/* * Switched away - we have new curlwp. * Restore VM context and IPL. */pmap_activate(l);uvm_emap_switch(l);pcu_switchpoint(l);

/sys/kern/kern_synch.c l

newl

lの情報(PC等)がlのPCBに保存される

newlの情報(PC等)がnewlのPCBからロードされる

mi_switchがカーネル内のどこから呼ばれるか?がミソ

Page 38: あるコンテキストスイッチの話

pcu_switchpoint/* * pcu_switchpoint: release PCU state if the LWP is being run on another CPU. * This routine is called on each context switch by by mi_switch(). */voidpcu_switchpoint(lwp_t *l){

const uint32_t pcu_valid = l->l_pcu_valid;int s;

KASSERTMSG(l == curlwp, "l %p != curlwp %p", l, curlwp);

if (__predict_true(pcu_valid == 0)) {/* PCUs are not in use. */return;

}s = splpcu();for (u_int id = 0; id < PCU_UNIT_COUNT; id++) {

if ((pcu_valid & (1U << id)) == 0) {continue;

}struct cpu_info * const pcu_ci = l->l_pcu_cpu[id];if (pcu_ci == NULL || pcu_ci == l->l_cpu) {

continue;}const pcu_ops_t * const pcu = pcu_ops_md_defs[id];pcu->pcu_state_release(l);

}splx(s);

}

コンテキストスイッチ以前のLWPがFPUを使っていたらFPU無効化フラグを立てるtrap()/systemcall()(exception_return)が呼ばれてもFPUは有効にならない

/sys/kern/subr_pcu.c

Page 39: あるコンテキストスイッチの話

おしまい

Page 40: あるコンテキストスイッチの話

で、trapb命令は?※ trapb命令を実行すると特殊なtrapが発生するが、 その処理内で無条件にfpu_load()を呼んでいた! ※ つまり、fpu_load()の二度掛けをしていた  → それまでの演算結果の破棄….

※ trapb命令は結構面白い命令なのでどっかで発表したいです…

fpu_load()は容量用法を守って正しくお使いください

Page 41: あるコンテキストスイッチの話

参考文献 & お世話になった方OSの一般的な知識BSDカーネルの設計と実装 -FreeBSD詳解-Solarisインターナル ―カーネル構造のすべてLion’s Commentary on UNIXはじめてのOSコードリーディング UNIX V6で学ぶカーネルのしくみ

AlphaについてAlpha Architecture Reference Manual Fourth EditionAlpha 21264 Microprocessor Hardware Reference Manual

lazy FPU context switchについてNetBSD Documentation: How lazy FPU context switch workshttp://www.netbsd.org/docs/kernel/lazyfpu.htmlNetBSD ドキュメンテーション: どのように lazy FPU コンテキストスイッチは動作するのかhttp://www.jp.netbsd.org/ja/docs/kernel/lazyfpu.html夜でもアッサムhttp://assam-at-night.blogspot.jp/2006/06/lazy-fpu-context-switchnetbsd.html

お世話になった方&ファボって元気づけてくれた方Izumi Tsutsui ‏@tsutsuiiJun Ebihara ‏@ebijun、oshimaya ‏@oshimyja、Kenji Aoyama ‏@ao_kenji、ご注文はOSvですか? @syuu1228NONAKA Kimihiro ‏@nonakap、Hiroshi Tokuda ‏@tokudahiroshi、 伊織ん@へら~ ‏@ioriveur波打際のだよもんさん ‏@daemon1995、まあぼ@cub ‏@marbocub じとめすきー @lycoris_blog

※イカ先生 @impreza_gf8さんにはAlpha Station XP1000を譲っていただきました!※ mltermはarakiken @arakikenさんの高性能ターミナルエミュレータです! ※ mikutterはおさわり大臣@toshi_aさんの高性能ておくれtwitterクライアントです!