<kernel v5.0>
페이지 테이블용 슬랩 캐시 준비
pgtable_init()
include/linux/mm.h
static inline void pgtable_init(void)
{
ptlock_cache_init();
pgtable_cache_init();
}
페이지 테이블용 슬랩 캐시들을 준비한다.
- page->ptl에 사용되는 슬랩 캐시
- pgd 테이블용 슬랩 캐시
- x86, arm에서는 빈 함수이다.
page->ptl용 슬랩 캐시 준비
DEBUG_SPINLOCK과 DEBUG_LOCK_ALLOC이 활성화된 경우 수 만개 이상의 페이지에 사용되는 page->ptl용 spinlock_t 사이즈가 커진다. 이를 kmalloc 메모리 할당자를 통해 할당하게 되면 2의 배수 크기 중 하나를 사용하게 되므로 사용하지 못하고 낭비되는 메모리 양이 매우 커진다. 이에 따라 디버그 상황에서는 메모리 낭비를 최소화하기 위해 page->ptl용 전용 슬랩 캐시를 만들어 사용하게 하였다.
- 약 x86_64에서 spinlock_t에 약 72 바이트가 사용
- 참고: mm: create a separate slab for page->ptl allocation
ptlock_cache_init()
mm/memory.c
#if USE_SPLIT_PTE_PTLOCKS && ALLOC_SPLIT_PTLOCKS
static struct kmem_cache *page_ptl_cachep;
void __init ptlock_cache_init(void)
{
page_ptl_cachep = kmem_cache_create("page->ptl", sizeof(spinlock_t), 0,
SLAB_PANIC, NULL);
}
#endif
페이지 테이블의 spinlock에 사용할 ptlock 캐시를 생성한다.
include/linux/mm_types.h
#define USE_SPLIT_PTE_PTLOCKS (NR_CPUS >= CONFIG_SPLIT_PTLOCK_CPUS)
include/linux/mm_types.h
#define ALLOC_SPLIT_PTLOCKS (SPINLOCK_SIZE > BITS_PER_LONG/8)
페이지 테이블 lock/unlock
ptlock_alloc()
mm/memory.c
bool ptlock_alloc(struct page *page)
{
spinlock_t *ptl;
ptl = kmem_cache_alloc(page_ptl_cachep, GFP_KERNEL);
if (!ptl)
return false;
page->ptl = ptl;
return true;
}
spinlock에 사용할 slub object를 할당받아 page->ptl에 대입한다.
ptlock_free()
mm/memory.c
void ptlock_free(struct page *page)
{
kmem_cache_free(page_ptl_cachep, page->ptl);
}
page->ptl에서 사용한 spinlock이 사용한 slub object를 해제한다.
CONFIG_SPLIT_PTLOCK_CPUS 커널 옵션
mm/Kconfig
# Heavily threaded applications may benefit from splitting the mm-wide
# page_table_lock, so that faults on different parts of the user address
# space can be handled with less contention: split it at this NR_CPUS.
# Default to 4 for wider testing, though 8 might be more appropriate.
# ARM's adjust_pte (unused if VIPT) depends on mm-wide page_table_lock.
# PA-RISC 7xxx's spinlock_t would enlarge struct page from 32 to 44 bytes.
# DEBUG_SPINLOCK and DEBUG_LOCK_ALLOC spinlock_t also enlarge struct page.
#
config SPLIT_PTLOCK_CPUS
int
default "999999" if !MMU
default "999999" if ARM && !CPU_CACHE_VIPT
default "999999" if PARISC && !PA20
default "4"
pgd 테이블용 슬랩 캐시 준비
pgtable_cache_init() – ARM64
arch/arm64/include/asm/pgtable.h
#define pgtable_cache_init pgd_cache_init
pgd_cache_init() – ARM64
arch/arm64/mm/pgd.c
void __init pgd_cache_init(void)
{
if (PGD_SIZE == PAGE_SIZE)
return;
#ifdef CONFIG_ARM64_PA_BITS_52
/*
* With 52-bit physical addresses, the architecture requires the
* top-level table to be aligned to at least 64 bytes.
*/
BUILD_BUG_ON(PGD_SIZE < 64);
#endif
/*
* Naturally aligned pgds required by the architecture.
*/
pgd_cache = kmem_cache_create("pgd_cache", PGD_SIZE, PGD_SIZE,
SLAB_PANIC, NULL);
}
pgd 테이블용 슬랩 캐시를 준비한다.
pgd 테이블 할당/해제
pgd_alloc()
arch/arm64/mm/pgd.c
pgd_t *pgd_alloc(struct mm_struct *mm)
{
if (PGD_SIZE == PAGE_SIZE)
return (pgd_t *)__get_free_page(PGALLOC_GFP);
else
return kmem_cache_alloc(pgd_cache, PGALLOC_GFP);
}
pgd 테이블 슬랩 캐시를 사용하여 pgd 테이블을 할당한다.
pgd_free()
arch/arm64/mm/pgd.c
void pgd_free(struct mm_struct *mm, pgd_t *pgd)
{
if (PGD_SIZE == PAGE_SIZE)
free_page((unsigned long)pgd);
else
kmem_cache_free(pgd_cache, pgd);
}
pgd 테이블 슬랩 캐시에 pgd 테이블을 할당 해제한다.