context_switch() 手続き
何故か分かりませんがここ起点にもくもくしてみることに。
mm メンバの話
Linux カーネル2.6 解読室の 13.6.4 空間の切り替えという節にこのあたりの話が出てます。基本的な部分は linaro な 2.6.35 でも変更は無い模様。
以下の処理に関する解説になってます。
mm = next->mm; oldmm = prev->active_mm; /* * For paravirt, this is coupled with an exit in switch_to to * combine the page table reload and the switch backend into * one hypercall. */ arch_start_context_switch(prev); if (likely(!mm)) { next->active_mm = oldmm; atomic_inc(&oldmm->mm_count); enter_lazy_tlb(oldmm, next); } else switch_mm(oldmm, mm, next); if (likely(!prev->mm)) { prev->active_mm = NULL; rq->prev_mm = oldmm; }
mm はこれから実行しようとしているプロセス (next) の mm メンバが格納されて、oldmm には今から切り替えられるプロセス (prev) の active_mm メンバが格納されてます。
mm および active_mm な属性の意味も記述されていて、一般プロセスの場合
- mm は自空間
- active_mm は常に mm と等しい
で、カーネルスレッドの場合
- mm は常に NULL
- カーネルスレッドはプロセス空間を持たないため
- active_mm は借用している mm_struct (実行中のみ意味あり)
- 直前に動いていたプロセスの空間を借用
とのこと。
なので上記に引用している手続きの記述だと
- mm が NULL の場合 (カーネルスレッド)
- 今から切り替えられるプロセスの active_mm を借用
- atomic_inc 手続き呼び出しは TLB のフラッシュはしなくてよい、という印とのこと
- mm が NULL でない場合 (一般プロセス)
- switch_mm 呼び出し
witch_mm は確認必要。あと最後の条件分岐はカーネルスレッドの active_mm 属性を NULL に戻す処理なようです。
アーキテクチャ固有の処理
に手を付ける前に細かいソレ達の整理を。以下のソレ達については
static inline void context_switch(struct rq *rq, struct task_struct *prev, struct task_struct *next) { struct mm_struct *mm, *oldmm; prepare_task_switch(rq, prev, next); trace_sched_switch(prev, next);
- prepare_task_switch() 手続きは kernel/sched.c で定義されてます
- context_switch のための準備と理解
- finish_task_switch() 手続きとセットな模様
- trace_sched_switch() 手続きは Tracepoints に関する手続きと理解
- 何故か include/trace/events/sched.h で諸々の定義な記述がある
また、arch_start_context_switch() という手続きですが、find|xargs grep してみたところでは、ARM では何もしない、という理解で良いのだろうか。良いことにしときます。
$ find|xargs grep arch_start_context_switch arch/x86/include/asm/paravirt.h:static inline void arch_start_context_switch(struct task_struct *prev) kernel/sched.c: arch_start_context_switch(prev); include/asm-generic/pgtable.h:#define arch_start_context_switch(prev) do {} while (0) $
で、肝心の switch_mm および switch_to ですが、それぞれ
- switch_mm はプロセス空間の切り替え (一般プロセスのみ)
- switch_to は各種レジスタの切り替え
との記述があります。ちなみに switch_mm については proc-* 毎で実装が異なる模様。例えば以下。
./mm/proc-arm720.S:ENTRY(cpu_arm720_switch_mm) ./mm/proc-arm7tdmi.S:ENTRY(cpu_arm7tdmi_switch_mm) ./mm/proc-arm6_7.S:ENTRY(cpu_arm6_switch_mm) ./mm/proc-arm6_7.S:ENTRY(cpu_arm7_switch_mm)
ヘッダを grep した所によればこんなカンジの出力
./include/asm/system.h: * switch_mm() may do a full cache flush over the context switch, ./include/asm/cpu-multi32.h: void (*switch_mm)(unsigned long pgd_phys, struct mm_struct *mm); ./include/asm/cpu-multi32.h:#define cpu_do_switch_mm(pgd,mm) processor.switch_mm(pgd,mm) ./include/asm/cpu-single.h:#define cpu_do_switch_mm __cpu_fn(CPU_NAME,_switch_mm) ./include/asm/cpu-single.h:extern void cpu_do_switch_mm(unsigned long pgd_phys, struct mm_struct *mm); ./include/asm/mmu_context.h:switch_mm(struct mm_struct *prev, struct mm_struct *next, ./include/asm/mmu_context.h: cpu_switch_mm(next->pgd, next); ./include/asm/mmu_context.h:#define activate_mm(prev,next) switch_mm(prev, next, NULL) ./include/asm/proc-fns.h:#define cpu_switch_mm(pgd,mm) cpu_do_switch_mm(virt_to_phys(pgd),mm)
ええと、switch_mm という手続きは mmy_context.h というヘッダで定義されてて手続きの本体として cpu_switch_mm という手続きを呼び出している模様。この cpu_switch_mm は proc-fns.h という手続き、というかマクロで定義が以下。
#ifdef CONFIG_MMU #define cpu_switch_mm(pgd,mm) cpu_do_switch_mm(virt_to_phys(pgd),mm)
memory management unit な CONFIG が有効になってないと駄目なのかなぁ。とりあえず cpu_do_switch_mm の wrapper になってます。
で、spu_do_switch_mm というソレですが定義が二箇所。
./include/asm/cpu-multi32.h:#define cpu_do_switch_mm(pgd,mm) processor.switch_mm(pgd,mm) ./include/asm/cpu-single.h:#define cpu_do_switch_mm __cpu_fn(CPU_NAME,_switch_mm)
こいつら include しとるのは proc-fns.h らしい。
#ifndef MULTI_CPU #include <asm/cpu-single.h> #else #include <asm/cpu-multi32.h> #endif
single で掘ってみると __cpu_fn というソレは cpu-single.h で定義されているマクロで以下。
cpu-single.h:18:#define __cpu_fn(name,x) __catify_fn(name,x)
おそらく名前を concat してるんだろうな。
./include/asm/cpu-single.h:#define __catify_fn(name,x) name##x
なので名前がアレでも解決できる、というナニ。で、今回のコンパイル対象を確認したところ、mm/proc-arm926.S になる模様。
/* * cpu_arm926_switch_mm(pgd) * * Set the translation base pointer to be as described by pgd. * * pgd: new page tables */ .align 5 ENTRY(cpu_arm926_switch_mm) #ifdef CONFIG_MMU mov ip, #0 #ifdef CONFIG_CPU_DCACHE_WRITETHROUGH mcr p15, 0, ip, c7, c6, 0 @ invalidate D cache #else @ && 'Clean & Invalidate whole DCache' 1: mrc p15, 0, r15, c7, c14, 3 @ test,clean,invalidate bne 1b #endif mcr p15, 0, ip, c7, c5, 0 @ invalidate I cache mcr p15, 0, ip, c7, c10, 4 @ drain WB mcr p15, 0, r0, c2, c0, 0 @ load page table pointer mcr p15, 0, ip, c8, c7, 0 @ invalidate I & D TLBs #endif mov pc, lr
これ、ARM なマニュアル見ながら別途確認します。今日はもう限界ッス。
switch_to
ちなみに __switch_to な手続きは arch/arm/kernel/entry-armv.S にて定義されている模様。