<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 테이블을 할당 해제한다.