page_address_init()

  • HIGHMEM 영역을 사용하기 위해 kmap(pkmap)용 hash table을 초기화한다.
    • rpi2: 라즈베리안 기본 설정에서는 HIGHMEM을 사용하지 않아 이 루틴이 동작하지 않지만 HIGHMEM을 사용하는 경우를 알아본다.
  • 이 hash table은 128개의 hash 엔트리를 사용하여 page 주소로 virtual 주소를 빠르게 변환하여 알아올 수 있다.

 

해쉬 페이지 테이블

page_address_init-1

  • CONFIG_HASHED_PAGE_VIRTUAL 옵션을 사용하면 kmap 매핑 영역을 관리하기 위해 해쉬 페이지 테이블을 사용하여 더 빠르게 검색할 수 있게한다.
    • page 주소로 해쉬 변환을 하여 해쉬 슬롯을 알아낸 후 해당 슬롯에서 page를 검색하므로 더욱 빠르게 처리할 수 있다.
  • 해쉬 페이지 테이블은 128개의 배열 즉 128개의 해쉬 슬롯으로 이루어진다.
  • 각 해쉬 페이지 테이블 엔트리는 page_address_map 엔트리를 여러 개 리스트에 등록할 수 있다.

 

page_address_init()

void __init page_address_init(void)
{
        int i;

        for (i = 0; i < ARRAY_SIZE(page_address_htable); i++) {
                INIT_LIST_HEAD(&page_address_htable[i].lh);
                spin_lock_init(&page_address_htable[i].lock);
        }   
}

 

page_address_slot 구조체

/*
 * Hash table bucket
 */
static struct page_address_slot {
        struct list_head lh;                    /* List of page_address_maps */
        spinlock_t lock;                        /* Protect this bucket's list */
} ____cacheline_aligned_in_smp page_address_htable[1<<PA_HASH_ORDER];
  • PA_HASH_ORDER=7
    • 해쉬 테이블의 크기를 128개로 설정한다.
  • ____cacheline_aligned_in_smp
    • 성능을 위하여 해당 아키텍처의 L1 d-cache line 크기로 정렬하여 사용한다.
    • __attribute__((__aligned__(SMP_CACHE_BYTES)))
      • SMP_CACHE_BYTES=6
        • rpi2: ARMv7  L1 d-cache line = 64 bytes
  • lh
    • page_address_map 구조체를 여러 개 등록 관리하기 위한 리스트 헤더로 사용한다.
  • lock
    • lh 연결 리스트를 보호하기 위한 spin lock 용도이다.

 

page_address_map 구조체

/*
 * Describes one page->virtual association
 */
struct page_address_map {
        struct page *page;
        void *virtual;
        struct list_head list;
};
  • page
    • 해당 page 구조체가 저장된 주소
  • virtual
    • 매핑된 virtual address
  • list
    • 이 페이지가 관리되고 있는 hash table의 위치

 

page_address_maps[] 전역 변수

static struct page_address_map page_address_maps[LAST_PKMAP];
  • 이 테이블은 HIGHMEM 영역에 대한 L2 table로 kmap(pkmap)이 유지 보수 한다.
    • kmap(), kunmap() 함수를 사용하여 매핑 설정 및 해제된다.
  • ARM에서 L2용도로 사용하는 엔트리는 512개이다.
    • 매핑 영역의 크기는 총 2M 크기를 사용할 수 있다.
    • 매핑 영역의 시작 위치는 PAGE_OFFSET – 2M 이다.

 

page_slot()

static struct page_address_slot *page_slot(const struct page *page)
{       
        return &page_address_htable[hash_ptr(page, PA_HASH_ORDER)];
}
  • page 구조체 주소로 hash(slot) 값을 알아와 해쉬 테이블의 인덱스로 사용하여 해당 해쉬 테이블의 구조체 주소를 리턴한다.
  • 예)
    • page = 0x10000000 -> hash slot index = 8

 

hash_ptr()

static inline unsigned long hash_ptr(const void *ptr, unsigned int bits)
{
        return hash_long((unsigned long)ptr, bits);
}
  • 주소 값을 사용하여 hash(slot) 값을 알아온다.
    • bits=7 인 경우 hash(slot) 값이 0~127의 값으로 리턴된다.

 

hash_long()

/* 2^31 + 2^29 - 2^25 + 2^22 - 2^19 - 2^16 + 1 */
#define GOLDEN_RATIO_PRIME_32 0x9e370001UL
/*  2^63 + 2^61 - 2^57 + 2^54 - 2^51 - 2^18 + 1 */
#define GOLDEN_RATIO_PRIME_64 0x9e37fffffffc0001UL

#if BITS_PER_LONG == 32
#define GOLDEN_RATIO_PRIME GOLDEN_RATIO_PRIME_32
#define hash_long(val, bits) hash_32(val, bits)
#elif BITS_PER_LONG == 64
#define hash_long(val, bits) hash_64(val, bits)
#define GOLDEN_RATIO_PRIME GOLDEN_RATIO_PRIME_64
#else
#error Wordsize not 32 or 64
#endif
  • 32비트/64비트 아키텍처에 따라 hash_32() 함수 또는 hash_64() 함수를 호출한다.

 

hash_32()

static inline u32 hash_32(u32 val, unsigned int bits)
{
        /* On some cpus multiply is faster, on others gcc will do shifts */
        u32 hash = val * GOLDEN_RATIO_PRIME_32;

        /* High bits are more random, so use them. */
        return hash >> (32 - bits);
}
  • val * 0x9e370001UL 값을 구한 후 bits 만큼 우측 쉬프트한 값을 리턴한다.
    • page_slot()에서 요청한 bits=7이므로 val * 0x9e370001UL 값을 구한 후 msb 7 bits를 우측으로 쉬프트한 후 리턴한다.
      • 결국 hash(slot) 값은 0~127이 리턴된다.
      • 예)
        • val=0x0 -> hash slot index=0
        • val=0x1 -> hash slot index=79
        • val=0x2 -> hash slot index=30
        • val=0x1000_0000 -> return=8
        • val=0x2000_0000 -> return=16

 

참고

답글 남기기

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