読者です 読者をやめる 読者になる 読者になる

dradonflybsdのmdcpuとmycpuマクロ

DragonFlyBSD kernel

φ(・・*)ゞ ウーン 両者の違いをよく忘れるのでorz

mdcpuもmycpuも今動いているcpuが持ってるデータにアクセスするために使うのは良いとして、mdcpuのmdは多分machine dependentとかそんな意味ですよね。ということで、mdcpuはアーキテクチャ依存な情報、mycpuはアーキテクチャ非依存なデータを取得するという使い分けでしょうか。
mdcpuはsys/platform/pc64/include/globaldata.hで定義されています。

#define mdcpu           ((struct mdglobaldata *)_get_mycpu())

mycpuのほうはsys/platform/pc64/include/thread.hで定義されています。

#define mycpu   _get_mycpu()

それでmdcpu、mycpuともに_get_cpu()を呼び出しているのでこれを見てみます。
実体はsys/platform/pc64/include/thread.hにあります。

struct globaldata;

extern int __mycpu__dummy;

static __inline
struct globaldata *
_get_mycpu(void)
{
    struct globaldata *gd;

    __asm ("movq %%gs:globaldata,%0" : "=r" (gd) : "m"(__mycpu__dummy));
    return(gd);
}

内容としては見ての通りで%%gs:globaldataにあるglobaldata構造体のデータが返ります。
mdcpuの場合はstruct mdglobaldata*にキャストしてますがこれはmdglobaldata構造体がこのようになっているのでうまいこと行くわけですね。

struct mdglobaldata {
        struct globaldata mi;
        struct user_segment_descriptor gd_common_tssd;
~略~

さて、変数としてのglobaldataはどこで定義しているかというとsys/platform/pc64/x86_64/global.sで定義されています。

        .globl  globaldata
        .set    globaldata,0

        /*
         * Define layout of the global data.  On SMP this lives in
         * the per-cpu address space, otherwise it's in the data segment.
         */
        .globl  gd_curthread, gd_npxthread, gd_reqflags, gd_common_tss
        .set    gd_curthread,globaldata + GD_CURTHREAD
        .set    gd_npxthread,globaldata + GD_NPXTHREAD
        .set    gd_reqflags,globaldata + GD_REQFLAGS
        .set    gd_common_tss,globaldata + GD_COMMON_TSS

        .globl  gd_common_tssd, gd_tss_gdt
        .set    gd_common_tssd,globaldata + GD_COMMON_TSSD
        .set    gd_tss_gdt,globaldata + GD_TSS_GDT

        .globl  gd_currentldt
        .set    gd_currentldt,globaldata + GD_CURRENTLDT

        .globl  gd_fpu_lock, gd_savefpu
        .set    gd_fpu_lock, globaldata + GD_FPU_LOCK
        .set    gd_savefpu, globaldata + GD_SAVEFPU

        

これでglobaldataがどこで宣言されているかは分かったので次はどこでデータを設定しているかですね。
mycpu、mdcpuともにプロセスが動いているcpuが持っているデータなのでデータを更新するとすればコンテキストスイッチのタイミングですよねーということで探してみるとコンテキストスイッチをしているのはsys/platform/pc64/x86_64/swtch.sでした。
一口にコンテキストスイッチと言ってもいくつか種類があったけど通常ではこれが使われるっぽいです。

ENTRY(cpu_lwkt_switch)
        pushq   %rbp    /* JG note: GDB hacked to locate ebp rel to td_sp */
        pushq   %rbx
        movq    PCPU(curthread),%rbx    /* becomes old thread in restore */
        pushq   %r12
        pushq   %r13
        pushq   %r14
        pushq   %r15
        pushfq
        cli

#if 1
        /*
         * Save the FP state if we have used the FP.  Note that calling
         * npxsave will NULL out PCPU(npxthread).
         *
         * We have to deal with the FP state for LWKT threads in case they
         * happen to get preempted or block while doing an optimized
         * bzero/bcopy/memcpy.
         */
        cmpq    %rbx,PCPU(npxthread)
        jne     1f
        movq    %rdi,%r12               /* save %rdi. %r12 is callee-saved */
        movq    TD_SAVEFPU(%rbx),%rdi
        call    npxsave                 /* do it in a big C function */
        movq    %r12,%rdi               /* restore %rdi */
1:
#endif

        movq    %rdi,%rax               /* switch to this thread */
        pushq   $cpu_lwkt_restore
        movq    %rsp,TD_SP(%rbx)
        /*
         * %rax contains new thread, %rbx contains old thread.
         */
        movq    %rax,PCPU(curthread)
        movq    TD_SP(%rax),%rsp
        ret

このファイルの中でそれっぽいのはこの辺ですね。

        movq    %rdi,%rax               /* switch to this thread */

ここのcpu_lwkt_switchは関数でcのプロトタイプはsys/sys/proc.hでこのようになってます。

thread_t cpu_lwkt_switch (struct thread *);

x86_64のABIだとrdiレジスタに引数がセットされるのでcpu_lwkt_switch()の引数は%rdiにセットされていて、これは次に動かしたいスレッドの構造体へのポインタとなります。そして関数内でデータが%rdiから%raxにコピーされて、カレントスレッドの構造体を示すcurthreadに次に動くスレッドのデータがセットされます。

        movq    %rdi,%rax               /* switch to this thread */
        pushq   $cpu_lwkt_restore
        movq    %rsp,TD_SP(%rbx)
        /*
         * %rax contains new thread, %rbx contains old thread.
         */
        movq    %rax,PCPU(curthread)
        movq    TD_SP(%rax),%rsp

この関数内でPCPUというマクロが出てきていますがこれはcpu/x86_64/include/asmacros.hで定義されています。

/*
 * Access per-CPU data.
 */
#define PCPU(member)            %gs:gd_ ## member
#define PCPU_E8(member,idx)     %gs:gd_ ## member(,idx,8)
#define PCPU_ADDR(member, reg)                                  \\
        movq %gs:PC_PRVSPACE, reg ;                             \\
        addq $PC_ ## member, reg

これは単純なマクロですね

        movq    %rax,PCPU(curthread)

↑だったら↓のように展開されるだけです。

        movq    %rax,%gs:gd_curthread

ここでglobaldataに戻ってこの部分を見てみます。

.set    gd_curthread,globaldata + GD_CURTHREAD

GD_CURTHREADはplatform/pc64/x86_64/genassym.cで以下のように定義されてます。

ASSYM(GD_CURTHREAD, offsetof(struct mdglobaldata, mi.gd_curthread));

ここまで見てみるとなるほどな~ってなりますね。