TLB Cache (API)

 

local_flush_tlb_kernel_page()

arch/arm/include/asm/tlbflush.h

static inline void local_flush_tlb_kernel_page(unsigned long kaddr)
{
        const unsigned int __tlb_flag = __cpu_tlb_flags;

        kaddr &= PAGE_MASK;

        if (tlb_flag(TLB_WB))
                dsb(nshst);

        __local_flush_tlb_kernel_page(kaddr);
        tlb_op(TLB_V7_UIS_PAGE, "c8, c7, 1", kaddr);

        if (tlb_flag(TLB_BARRIER)) {
                dsb(nsh);
                isb();
        }
}
  • __cpu_tlb_flags
    • cpu_tlb_fns 구조체로 만들어진 전역 cpu_tlb.tlb_flags로 연결된 define 문
    • rpi2:
      • TLB_WB | TLB_BARRIER | TLB_V7_UIS_FULL | TLB_V7_UIS_PAGE |
        TLB_V7_UIS_ASID | TLB_V7_UIS_BP
  • kaddr &= PAGE_MASK;
    • kaddr에 대해 페이지 단위로 round down 한다.
  • if (tlb_flag(TLB_WB)) dsb(nshst);
    • 아키텍처가 TLB_WB 플래그를 지원하면 dsb(nshst) 명령을 수행한다.
      •  TLB_WB
        • (1 << 31)
    • dsb(option)
      • __asm__ __volatile__ (“dsb ” #option : : : “memory”)
      • 모든 cache operation이 끝날 때까지 기다린다.
  • __local_flush_tlb_kernel_page(kaddr);
    • 요청 페이지에 대한 TLB 캐시를 flush 한다.
  • tlb_op(TLB_V7_UIS_PAGE, “c8, c7, 1”, kaddr);
    • 아키텍처가 TLB_V7_UIS_PAGE를 지원하면 TLBIMVA(unified TLB Invalidate by MVA and ASID) 명령을 수행
    • TLB_V7_UIS_PAGE
      • (1 << 20)
    • rpi2:
      • TLBIMVA가 수행된다.
  • if (tlb_flag(TLB_BARRIER)) { dsb(nsh); isb(); }
    • 아키텍처가 TLB_BARRIER를 지원하면 dsb, isb를 수행한다.

 

tlb_flag()

arch/arm/include/asm/tlbflush.h

#define tlb_flag(f)     ((always_tlb_flags & (f)) || (__tlb_flag & possible_tlb_flags & (f)))
  • f(플래그)가 always_tlb_flags에 있거나 __tlb_flag가 possible_tlb_flags에 있는 경우 true

 

tlb_op()

arch/arm/include/asm/tlbflush.h

#define tlb_op(f, regs, arg)    __tlb_op(f, “p15, 0, %0, ” regs, arg)

  • f(플래그)가 always_tlb_flag에 있는 경우 TLB 레지스터에 대한 어셈블리 명령을 수행한다.
  • 그렇지 않은 경우 f가 possible_tlb_flags와 __tlb_flag 둘 다에 있는 경우 어셈블리 명령을 수행한다.

 

__tlb_op()

arch/arm/include/asm/tlbflush.h

#define __tlb_op(f, insnarg, arg)                                       \
        do {                                                            \
                if (always_tlb_flags & (f))                             \
                        asm("mcr " insnarg                              \
                            : : "r" (arg) : "cc");                      \
                else if (possible_tlb_flags & (f))                      \
                        asm("tst %1, %2\n\t"                            \
                            "mcrne " insnarg                            \
                            : : "r" (arg), "r" (__tlb_flag), "Ir" (f)   \
                            : "cc");                                    \
        } while (0)

instruction을 실행시키되 다음의 조건을 만족해야 한다.

  • arm의 모든 아키텍처가 지원하는 명령인지 비교하여 일치하는 경우
  • 현재 아키텍처가 지원하는 명령인지 비교하여 일치하는 경우에는 cpu가 지원하는 명령인지도 추가적으로 비교한다.
  • if (always_tlb_flags & (f)) asm(“mcr ” insnarg : : “r” (arg) : “cc”);
    • 요청 flag가 alway_tlb_flags에 있는 경우 insnarg 어셈블리 명령을 수행한다.
  • else if (possible_tlb_flags & (f)) asm(“tst %1, %2\n\t” “mcrne ” insnarg : : “r” (arg), “r” (__tlb_flag), “Ir” (f) : “cc”);
    • 그렇지 않고 요청 flag가 possible_tlb_flags에 있을 때
  • asm(“tst %1, %2\n\t” “mcrne ” insnarg : : “r” (arg), “r” (__tlb_flag), “Ir” (f) : “cc”);
    • flag가 __tlb_flag에 있는 경우 insarg 어셈블리 명령을 수행한다.

TLB 플래그들

always_tlb_flags
#define always_tlb_flags        (v4_always_flags & \
                                 v4wbi_always_flags & \
                                 fr_always_flags & \
                                 v4wb_always_flags & \
                                 fa_always_flags & \
                                 v6wbi_always_flags & \
                                 v7wbi_always_flags)

arm의 모든 아키텍처가 지원하는 명령어

  • TLB_WB | TLB_BARRIER

 

possible_tlb_flags
#define possible_tlb_flags      (v4_possible_flags | \
                                 v4wbi_possible_flags | \
                                 fr_possible_flags | \
                                 v4wb_possible_flags | \
                                 fa_possible_flags | \
                                 v6wbi_possible_flags | \
                                 v7wbi_possible_flags)

arm 아키텍처 중 현재 아키텍처가 지원하는 명령어

  • ARMv7
    • TLB_WB | TLB_BARRIER | TLB_V7_UIS_FULL | TLB_V7_UIS_PAGE |
      TLB_V7_UIS_ASID | TLB_V7_UIS_BP | TLB_DCLEAN | TLB_V6_U_FULL | TLB_V6_U_PAGE | TLB_V6_U_ASID | TLB_V6_BP

 

__tlb_flag

현재 cpu가 지원하는 명령어 플래그

  • rpi2:
    • TLB_WB | TLB_BARRIER | TLB_V7_UIS_FULL | TLB_V7_UIS_PAGE | TLB_V7_UIS_ASID | TLB_V7_UIS_BP

 

__local_flush_tlb_kernel_page()

arch/arm/include/asm/tlbflush.h

static inline void __local_flush_tlb_kernel_page(unsigned long kaddr)
{
        const int zero = 0;
        const unsigned int __tlb_flag = __cpu_tlb_flags;

        tlb_op(TLB_V4_U_PAGE, "c8, c7, 1", kaddr);
        tlb_op(TLB_V4_D_PAGE, "c8, c6, 1", kaddr);
        tlb_op(TLB_V4_I_PAGE, "c8, c5, 1", kaddr);
        if (!tlb_flag(TLB_V4_I_PAGE) && tlb_flag(TLB_V4_I_FULL))
                asm("mcr p15, 0, %0, c8, c5, 0" : : "r" (zero) : "cc");

        tlb_op(TLB_V6_U_PAGE, "c8, c7, 1", kaddr);
        tlb_op(TLB_V6_D_PAGE, "c8, c6, 1", kaddr);
        tlb_op(TLB_V6_I_PAGE, "c8, c5, 1", kaddr);
}
  • 아키텍처에 맞는 TLB operation 코드가 선택 수행된다.
    • TLBIMVA(unified TLB Invalidate by MVA and ASID)
    • DTLBIMVA(Data TLB Invalidate by MVA and ASID)
    • ITLBIMVA(Instruction TLB Invalidate by MVA and ASID)
    • ITLBIALL(Instruction TLB Invalidate all)
    • rpi2:
      • 해당되는 플래그가 없어서 아무것도 수행하지 않는다.

 

clean_pmd_entry()

arch/arm/include/asm/tlbflush.h

static inline void clean_pmd_entry(void *pmd)
{
        const unsigned int __tlb_flag = __cpu_tlb_flags;

        tlb_op(TLB_DCLEAN, "c7, c10, 1  @ flush_pmd", pmd);
        tlb_l2_op(TLB_L2CLEAN_FR, "c15, c9, 1  @ L2 flush_pmd", pmd);
}

TLB 엔트리 하나를 clean 한다.

  • tlb_op(TLB_DCLEAN, “c7, c10, 1  @ flush_pmd”, pmd);
    • 아키텍처가 TLB_DCLEAN을 지원하는 경우 tlb operation “mcr c7, c10, 1″을  실행한다.
      • Clean data or unified cache line by MVA to PoC
  • tlb_l2_op(TLB_L2CLEAN_FR, “c15, c9, 1  @ L2 flush_pmd”, pmd);
    • 아키텍처가 TLB_L2CLEAN_FR을 지원하는 경우 tlb operation “mcr c15, c9, 1″을  실행한다.

 

구조체

cpu_tlb_fns 구조체

struct cpu_tlb_fns {
        void (*flush_user_range)(unsigned long, unsigned long, struct vm_area_struct *);
        void (*flush_kern_range)(unsigned long, unsigned long);
        unsigned long tlb_flags;
};
  • flush_user_range
    • rpi2: v7wbi_flush_user_tlb_range() 함수를 가리킨다.
  • flush_kern_range
    • rpi2: v7wbi_flush_kern_tlb_range() 함수를 가리킨다.
  • tlb_flags
    • 현재 cpu가 지원하는 TLB 캐시 관련 플래그
    • rpi2:
      • TLB_WB | TLB_BARRIER | TLB_V7_UIS_FULL | TLB_V7_UIS_PAGE | TLB_V7_UIS_ASID | TLB_V7_UIS_BP

참고

댓글 남기기