Fixmap

특징

  • Fix Maped Linear Address Space
  • himem 페이지를 cpu id 별로 할당된 fixmap 영역에 매핑하여 사용한다.
  • ZONE_HIGHMEM은 커널에서 다음의 매핑 방법을 사용하여 항상 매핑과 언매핑을 반복 사용하며 사용하기 때문에 access 속도가 매핑을 위한 overhead가 발생하여 ZONE_NORMAL에 비해  느린 성능을 보여준다. 물론 kernel과 다르게 user level 에서는 각 task 마다 매우 방대한 user address space(설정에 따라 1G, 2G 또는 3G)의 공간에 매핑하여 사용한다.
    • vmap
      • 장시간 여러 페이지를 매핑하여 사용할 수 있고 꽤 큰 vmalloc address space에 매핑을 한다.
        • arm: 240M 공간
    • kmap
      • 짧은 시간 매핑하여 사용하고 kmap address space에 매핑을 한다. 매핑이 완료되면 스케쥴되어 다른 태스크로 바뀌더라도 매핑을 유지한다.
        • arm: 2M 공간
    • fixmap
      • 아주 극히 짧은 시간 매핑하여 사용할 수 있고 fixmap address space에 매핑을 한다. sleep 되지 않아 interrupt context에서 사용될 수 있다.
      • 스케쥴되어 다른 태스크로 바뀌기 전에 unmap 되어야 한다.
        • arm: 3M 공간
  • stack based kmap_atomic
    • 초기 매핑할 수 있는 index slot은 기존에는 cpu id와 사용 타입에 따라 고정되었는데 현재는 cpu id에 KM_TYPE_NR(ARM=16) 수 만큼 스택과 같은 방법으로 push/pop을 이용하여 운영된다.
      • 즉 cpu 마다 16개의 매핑 슬롯이 배정된다.
    • 참고: mm: stack based kmap_atomic()
  • kmap_atomic() 함수를 사용하여 현재 cpu에 해당하는 fixmap 영역을 사용하여 매핑을 한다. 이미 kmap 영역에 매핑이 된 경우 해당 가상 주소를 리턴한다.

 

Fixmap area

  • fixmap 영역은 아키텍처마다 위치가 다르고 크기도 다르다.
    • ARM:
      • FIXADDR_START(0xffc0_0000) ~ FIXADDR_END(0xfff0_0000) 까지 3M 영역을 사용한다.
      • FIXADDR_TOP 영역은 FIXADDR_END – PAGE_SIZE(4K ) 이다.
        • 인덱스 지정은 FIXADDR_TOP(0xffef_f000)이 0번 인덱스로 아래 방향으로 인덱스 번호가 증가된다.
        • 인덱스 번호는 0부터 최대 0x2ff (767)을 지정할 수 있다.
      • cpu 별 매핑 영역이외에 earlycon을 위한 fixmap 영역이 2015년 8월 kernel v4.3-rc1에 추가되었다.

fixmap-2

 

Fixmap 인덱스 슬롯

  • fixmap 인덱스 슬롯 번호에 따라 가상 주소가 고정되어 있다.
    • 예)
      • index=0 -> vaddr=0xffef_f000 (FIXADDR_TOP)
      • index=1 -> vaddr=0xffef_e000
      • index=767 -> vaddr=0xffc0_0000 (FIXADDR_START)
  • fixmap 인덱스 슬롯 번호는 cpu id 별로 KM_TYPE_NR(ARM=16) 수 만큼 할당할 수 있다.
  • fixmap 인덱스 슬롯은 cpu id별로 할당된 수 만큼의 영역에서 스택처럼 push/pull로 운영하여 사용한다.
    • CONFIG_DEBUG_HIGHMEM 옵션을 사용하는 경우 fixmap 인덱스 슬롯이 초과하는 경우 버그에 대한 메시지를 출력하고 멈춘다.

fixmap-3a

  • fixmap 매핑 예) 물리주소 0x40000000로 시작하는 1페이지를 fixmap 0번 인덱스에 매핑한다.
    • __set_fixmap(0, 0x40000000)
      • fixmap 0번에 해당하는 가상 주소 0xffef_f000에 0x40000000이 매핑된다.

fixmap-1b

 

ARM에서의 fixed_address 값

arch/arm/include/asm/fixmap.h

enum fixed_addresses {
        FIX_KMAP_BEGIN,
        FIX_KMAP_END = FIX_KMAP_BEGIN + (KM_TYPE_NR * NR_CPUS) - 1,

        /* Support writing RO kernel text via kprobes, jump labels, etc. */
        FIX_TEXT_POKE0,
        FIX_TEXT_POKE1,

        __end_of_fixed_addresses
};
  • fixmap 영역을 매핑할 수 있는 슬롯 인덱스 수는 cpu 수(NR_CPUS) x KM_TYPE_NR(ARM=16)으로  제한된다.

 

할당 & 해제

kmap_atomic()

arch/arm/mm/highmem.c

void *kmap_atomic(struct page *page)
{
        unsigned int idx;
        unsigned long vaddr;
        void *kmap;
        int type;

        pagefault_disable();
        if (!PageHighMem(page))
                return page_address(page);

#ifdef CONFIG_DEBUG_HIGHMEM
        /*
         * There is no cache coherency issue when non VIVT, so force the
         * dedicated kmap usage for better debugging purposes in that case.
         */
        if (!cache_is_vivt())
                kmap = NULL;
        else
#endif
                kmap = kmap_high_get(page);
        if (kmap)
                return kmap;

        type = kmap_atomic_idx_push();

        idx = type + KM_TYPE_NR * smp_processor_id();
        vaddr = __fix_to_virt(idx);
#ifdef CONFIG_DEBUG_HIGHMEM
        /*
         * With debugging enabled, kunmap_atomic forces that entry to 0.
         * Make sure it was indeed properly unmapped.
         */
        BUG_ON(!pte_none(get_fixmap_pte(vaddr)));
#endif
        /*
         * When debugging is off, kunmap_atomic leaves the previous mapping
         * in place, so the contained TLB flush ensures the TLB is updated
         * with the new mapping.
         */
        set_fixmap_pte(idx, mk_pte(page, kmap_prot));

        return (void *)vaddr;
}
EXPORT_SYMBOL(kmap_atomic);

page를 atomic하게 fixmap 영역에 매핑한다. 이미 kmap 영역에 매핑된 경우 매핑된 가상 주소를 리턴한다.

  • pagefault_disable()
    • preemption 카운터를 증가시켜 preemption을 disable하고 barrier()를 수행한다.
  • if (!PageHighMem(page))
    • page 주소가 highmem 영역이 아니면
  • return page_address(page);
    • kmap에 이미 매핑되어 있는 경우 page에 해당하는 가상 주소를 리턴한다.
  • kmap = kmap_high_get(page);
    • pkmap 참조 카운터를 증가시키고 highmem page에 해당하는 가상 주소를 리턴한다.
  •  if (kmap)
    • 만일 kmap이 이미 발견된 경우 리턴한다.
  • type = kmap_atomic_idx_push();
    • __kmap_atomic_idx 증가 시키고 이전 값을 알아온다.
  • idx = type + KM_TYPE_NR * smp_processor_id();
    • type 값 + KM_TYPE_NR(16) * cpu id
  • vaddr = __fix_to_virt(idx);
    • fixmap에서 해당 idx 번호로 가상 주소를 알아온다.
    • fixmap은 idx 0번이 FIXADDR_TOP을 가리킨다.
      • FIXADDR_TOP
        • 0xffef_f000= (FIXADDR_END(0xfff00000UL) – PAGE_SIZE)
    • 인덱스 번호는 0부터 최대 0x2ff (767)번 까지 가능하며 매핑 되는 가상 주소는 0xffc0_0000 ~ 0xffef_ffff까지 총 3M이다.
      • 실제 허용 가능한 슬롯 인덱스 번호는 해당 cpu별로 KM_TYPE_NR(ARM=16) 개로 제한된다.
#define __fix_to_virt(x)        (FIXADDR_TOP - ((x) << PAGE_SHIFT))
  • set_fixmap_pte(idx, mk_pte(page, kmap_prot));
    • mk_pte()
      • page 주소와 kmap_prot 속성 값을 합쳐서 pte 엔트리를 만든다.
    • idx 번호에 해당하는 fixmap 영역에 pte 엔트리를 매핑한다.
#define mk_pte(page,prot)       pfn_pte(page_to_pfn(page), prot)

 

set_fixmap_pte()

arch/arm/mm/highmem.c

static inline void set_fixmap_pte(int idx, pte_t pte)
{
        unsigned long vaddr = __fix_to_virt(idx);
        pte_t *ptep = pte_offset_kernel(pmd_off_k(vaddr), vaddr);

        set_pte_ext(ptep, pte, 0); 
        local_flush_tlb_kernel_page(vaddr);
}

idx 번호에 해당하는 fixmap 영역에 pte 엔트리를 매핑한다.

  • unsigned long vaddr = __fix_to_virt(idx);
    • idx 번호에 해당하는 fixmap 영역의 가상 주소를 알아온다.
  • pte_t *ptep = pte_offset_kernel(pmd_off_k(vaddr), vaddr);
    • pmd_off_k()
      • 가상 주소값으로 pmd 엔트리 주소 값을 알아온다.
    • pte_offset_kernel()
      • pmd 엔트리 주소 값과 vaddr 값을 사용하여 pte 엔트리 주소를 알아온다.
  • set_pte_ext(ptep, pte, 0);
    • pte 엔트리 주소에 pte 값을 저장한다.
      • rpi2: cpu_v7_set_pte_ext() 호출
  • local_flush_tlb_kernel_page(vaddr);
    • vaddr에 해당하는 tlb 캐시를 flush한다.

 

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();
        }   
}

 

__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_op()

arch/arm/include/asm/tlbflush.h

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

 

__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)

 

 

kmap_atomic_idx_push()

include/linux/highmem.h

static inline int kmap_atomic_idx_push(void)
{
        int idx = __this_cpu_inc_return(__kmap_atomic_idx) - 1;

#ifdef CONFIG_DEBUG_HIGHMEM
        WARN_ON_ONCE(in_irq() && !irqs_disabled());
        BUG_ON(idx >= KM_TYPE_NR);
#endif
        return idx;
}

해당 cpu에서 사용할 fixmap용 슬롯 인덱스를 리턴하고 그 값은 증가 시킨다.

  • CONFIG_DEBUG_HIGHMEM 옵션을 사용하는 경우 fixmap 인덱스 슬롯이 KM_TYPE_NR(ARM=16)을 초과하는 경우 버그에 대한 메시지를 출력하고 멈춘다.
  • int idx = __this_cpu_inc_return(__kmap_atomic_idx);
    • __kmap_atomic_idx per-cpu 데이터를 1 증가
    • idx 에는 증가전 값을 담아 리턴한다.
#define __this_cpu_inc_return(pcp)      __this_cpu_add_return(pcp, 1)
  • pcp에 1을 더한 값을 리턴
#define __this_cpu_add_return(pcp, val)                                 \
({                                                                      \
        __this_cpu_preempt_check("add_return");                         \
        raw_cpu_add_return(pcp, val);                                   \
})
  • preemption이 disable되어 있지  않거나 irq가 disable되어 있지 않으면 stack dump
  • __kmap_atomic_idx per-cpu 데이터를 1 증가
#define raw_cpu_add_return(pcp, val)    __pcpu_size_call_return2(raw_cpu_add_return_, pcp, val)
  • scalar 데이터 타입 pcp의 사이즈에 따른 최적화된 덧셈 함수를 분류하기 위해 매크로 함수 호출
#define __pcpu_size_call_return2(stem, variable, ...)                   \
({                                                                      \
        typeof(variable) pscr2_ret__;                                   \
        __verify_pcpu_ptr(&(variable));                                 \
        switch(sizeof(variable)) {                                      \
        case 1: pscr2_ret__ = stem##1(variable, __VA_ARGS__); break;    \
        case 2: pscr2_ret__ = stem##2(variable, __VA_ARGS__); break;    \
        case 4: pscr2_ret__ = stem##4(variable, __VA_ARGS__); break;    \
        case 8: pscr2_ret__ = stem##8(variable, __VA_ARGS__); break;    \
        default:                                                        \
                __bad_size_call_parameter(); break;                     \
        }                                                               \
        pscr2_ret__;                                                    \
})
  • variable의 사이즈에 따라 stem(함수명 인수)+1/2/4/8 숫자를 붙여 호출
#define this_cpu_add_return_4(pcp, val) this_cpu_generic_add_return(pcp, val)
  • ARM 아키텍처는 사이즈와 관계 없이 generic 코드를 호출한다.
#define this_cpu_generic_add_return(pcp, val)                           \
({                                                                      \
        typeof(pcp) __ret;                                              \
        unsigned long __flags;                                          \
        raw_local_irq_save(__flags);                                    \
        raw_cpu_add(pcp, val);                                          \
        __ret = raw_cpu_read(pcp);                                      \
        raw_local_irq_restore(__flags);                                 \
        __ret;                                                          \
})
  • per-cpu 변수에 val 값을 더하고 다시 읽어 리턴한다.
#define raw_cpu_add(pcp, val)           __pcpu_size_call(raw_cpu_add_, pcp, val)
  • scalar 데이터 타입 pcp의 사이즈에 따른 최적화된 덧셈 함수를 분류하기 위해 매크로 함수 호출
#define __pcpu_size_call(stem, variable, ...)                           \
do {                                                                    \
        __verify_pcpu_ptr(&(variable));                                 \
        switch(sizeof(variable)) {                                      \
                case 1: stem##1(variable, __VA_ARGS__);break;           \
                case 2: stem##2(variable, __VA_ARGS__);break;           \
                case 4: stem##4(variable, __VA_ARGS__);break;           \
                case 8: stem##8(variable, __VA_ARGS__);break;           \
                default:                                                \
                        __bad_size_call_parameter();break;              \
        }                                                               \
} while (0)
  • variable의 사이즈에 따라 stem(함수명 인수)+1/2/4/8 숫자를 붙여 호출
#define raw_cpu_add_4(pcp, val)         raw_cpu_generic_to_op(pcp, val, +=)
  • ARM 아키텍처는 사이즈와 관계 없이 generic 코드를 호출한다.
#define raw_cpu_generic_to_op(pcp, val, op)                             \
do {                                                                    \
        *raw_cpu_ptr(&(pcp)) op val;                                    \
} while (0)
  • 예) pcp=__kmap_atomic_idx, val=1, op= +=
    • per-cpu int 데이터인 __kmap_atomic_idx += 1

 

kunmap_atomic()

include/linux/highmem.h

/*
 * Prevent people trying to call kunmap_atomic() as if it were kunmap()
 * kunmap_atomic() should get the return value of kmap_atomic, not the page.
 */
#define kunmap_atomic(addr)                                     \
do {                                                            \
        BUILD_BUG_ON(__same_type((addr), struct page *));       \
        __kunmap_atomic(addr);                                  \
} while (0)

kmap_atomic()을 사용하여 fixmap 영역에 매핑되어 있는 highmem 영역을 해제한다.

 

__kunmap_atomic()

arch/arm/mm/highmem.c

void __kunmap_atomic(void *kvaddr)
{
        unsigned long vaddr = (unsigned long) kvaddr & PAGE_MASK;
        int idx, type;

        if (kvaddr >= (void *)FIXADDR_START) {
                type = kmap_atomic_idx();
                idx = type + KM_TYPE_NR * smp_processor_id();

                if (cache_is_vivt())
                        __cpuc_flush_dcache_area((void *)vaddr, PAGE_SIZE);
#ifdef CONFIG_DEBUG_HIGHMEM
                BUG_ON(vaddr != __fix_to_virt(idx));
                set_fixmap_pte(idx, __pte(0));
#else
                (void) idx;  /* to kill a warning */
#endif
                kmap_atomic_idx_pop();
        } else if (vaddr >= PKMAP_ADDR(0) && vaddr < PKMAP_ADDR(LAST_PKMAP)) {
                /* this address was obtained through kmap_high_get() */
                kunmap_high(pte_page(pkmap_page_table[PKMAP_NR(vaddr)]));
        }
        pagefault_enable();
}
EXPORT_SYMBOL(__kunmap_atomic);

kmap_atomic()을 사용하여 fixmap 또는 kmap 영역에 매핑되어 있는 highmem 영역을 해제한다.

  • if (kvaddr >= (void *)FIXADDR_START) {
    • 주소가 fixmap 영역인 경우
  •  type = kmap_atomic_idx();
    • __kmap_atomic_idx per-cpu 데이터값에서 1을 뺀 인덱스 값
  • idx = type + KM_TYPE_NR * smp_processor_id();
    • KM_TYPE_NR=16
  • if (cache_is_vivt())
    • L1 d-cache 타입이 VIVT인 경우
    • rpi2:
      • L1 d-cache는 CACHEID_VIPT_NONALIASING
      • L1 i-cache는 CACHEID_VIPT_I_ALIASING
  • __cpuc_flush_dcache_area((void *)vaddr, PAGE_SIZE);
    • 해당 주소의 1 페이지 영역의 d-cache를 flush 한다.
  • kmap_atomic_idx_pop();
    • __kmap_atomic_idx per-cpu 데이터를 1 감소시킨다.
  • } else if (vaddr >= PKMAP_ADDR(0) && vaddr < PKMAP_ADDR(LAST_PKMAP)) {
    • 가상 주소가 pkmap 매핑 영역인 경우
  • kunmap_high(pte_page(pkmap_page_table[PKMAP_NR(vaddr)]));
    • kmap 영역에 매핑된 highmem page 주소를 매핑 해제한다.
  • pagefault_enable();
    • preemption을 enable 한다.

 

기타 API

set_fixmap()

include/asm-generic/fixmap.h

#define set_fixmap(idx, phys)                           \
        __set_fixmap(idx, phys, FIXMAP_PAGE_NORMAL)
  • fixmap의 요청 인덱스 영역에 물리 주소 1개 페이지를 매핑하는데 속성은 FIXMAP_PAGE_NORMAL로 설정한다.
    • L_PTE_YOUNG | L_PTE_PRESENT | L_PTE_XN | L_PTE_DIRTY | L_PTE_MT_WRITEBACK
    • 2015년 8월 커널 v.4.3-rc1 기준

 

clear_fixmap()

include/asm-generic/fixmap.h

#define clear_fixmap(idx)                       \
        __set_fixmap(idx, 0, FIXMAP_PAGE_CLEAR)
  • fixmap의 요청 인덱스 영역에 물리 주소 1개 페이지를 언매핑하는데 속성은 CLEAR(0)로 설정한다.

 

set_fixmap_nocache()

include/asm-generic/fixmap.h

#define set_fixmap_nocache(idx, phys) \
        __set_fixmap(idx, phys, FIXMAP_PAGE_NOCACHE)
  • fixmap의 요청 인덱스 영역에 물리 주소 1개 페이지를 언매핑하는데 속성은 FIXMAP_PAGE_NOCACHE로 설정한다.
    • L_PTE_YOUNG | L_PTE_PRESENT | L_PTE_XN | L_PTE_DIRTY | L_PTE_MT_DEV_SHARED | L_PTE_SHARE
    • 2015년 8월 커널 v.4.3-rc1 기준

 

set_fixmap_io()

include/asm-generic/fixmap.h

#define set_fixmap_io(idx, phys) \
        __set_fixmap(idx, phys, FIXMAP_PAGE_IO)
  • fixmap의 요청 인덱스 영역에 물리 주소 1개 페이지를 언매핑하는데 속성은 FIXMAP_PAGE_IO로 설정한다.
    • L_PTE_YOUNG | L_PTE_PRESENT | L_PTE_XN | L_PTE_DIRTY | L_PTE_MT_DEV_SHARED | L_PTE_SHARE
    • 2015년 8월 커널 v.4.3-rc1 기준

 

__set_fixmap()

arch/arm/mm/mmu.c

/*
 * To avoid TLB flush broadcasts, this uses local_flush_tlb_kernel_range().
 * As a result, this can only be called with preemption disabled, as under
 * stop_machine().
 */
void __set_fixmap(enum fixed_addresses idx, phys_addr_t phys, pgprot_t prot)
{
        unsigned long vaddr = __fix_to_virt(idx);
        pte_t *pte = pte_offset_kernel(pmd_off_k(vaddr), vaddr);

        /* Make sure fixmap region does not exceed available allocation. */
        BUILD_BUG_ON(FIXADDR_START + (__end_of_fixed_addresses * PAGE_SIZE) >
                     FIXADDR_END);
        BUG_ON(idx >= __end_of_fixed_addresses);

        if (pgprot_val(prot))
                set_pte_at(NULL, vaddr, pte,
                        pfn_pte(phys >> PAGE_SHIFT, prot));
        else
                pte_clear(NULL, vaddr, pte);
        local_flush_tlb_kernel_range(vaddr, vaddr + PAGE_SIZE);
}

물리주소 페이지 1개를 fixmap 인덱스 번호 영역에 인수로 받은 속성값을 pte에 더해 매핑한다.

  • unsigned long vaddr = __fix_to_virt(idx);
    • 인덱스 번호에 매치되는 fixmap 영역의 가상 주소를 알아온다.
  • pte_t *pte = pte_offset_kernel(pmd_off_k(vaddr), vaddr);
    • pmd_off_k()
      • 가상 주소로 pmd 엔트리 주소를 알아온다.
    • pte_offset_kernel()
      • pmd 엔트리 주소와 가상 주소로 pte 엔트리 주소를 알아온다.
  • if (pgprot_val(prot))
    • prot 속성 값이 있는 경우 추가
  • set_pte_at(NULL, vaddr, pte, pfn_pte(phys >> PAGE_SHIFT, prot));
    • pte 엔트리를 매핑한다.
  • pte_clear(NULL, vaddr, pte);
    • pte 엔트리를 언매핑한다.
  • local_flush_tlb_kernel_range(vaddr, vaddr + PAGE_SIZE);
    • pte 엔트리가 수정되었으므로 해당 페이지 1개의 영역에 대해 tlb flush를 수행한다.

 

fix_to_virt()

include/asm-generic/fixmap.h

/*
 * 'index to address' translation. If anyone tries to use the idx
 * directly without translation, we catch the bug with a NULL-deference
 * kernel oops. Illegal ranges of incoming indices are caught too.
 */
static __always_inline unsigned long fix_to_virt(const unsigned int idx)
{
        BUILD_BUG_ON(idx >= __end_of_fixed_addresses);
        return __fix_to_virt(idx);
}

fixmap 영역에 대한 인덱스로 가상 주소를 알아온다.

#define __fix_to_virt(x)        (FIXADDR_TOP - ((x) << PAGE_SHIFT))

 

 

virt_to_fix()

include/asm-generic/fixmap.h

static inline unsigned long virt_to_fix(const unsigned long vaddr)
{
        BUG_ON(vaddr >= FIXADDR_TOP || vaddr < FIXADDR_START);
        return __virt_to_fix(vaddr);
}

fixmap area에 매치되는 fixmap 인덱스를 리턴한다.

#define __virt_to_fix(x)        ((FIXADDR_TOP - ((x)&PAGE_MASK)) >> PAGE_SHIFT)

 

참고

답글 남기기

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다.