[커널 17차] 90주차

2022.05.22 00:18

ㅇㅇㅇ 조회 수:149

get_page_from_freelist()
- L4155 : 함수 prep_new_page()로 할당 받은 페이지에 대해 준비 작업을 한다
- L4161 : order가 1 이상이고 alloc flags에 ALLOC_HARDER가 있으면 함수 reserve_highatomic_pageblock()로 high atomic으로 현 page의 pageblock을 전환할 수 있는지 체크하고 가능하면 전환한다.
- L4164 : page를 반환한다
- L4166 : CONFIG_DEFERRED_STRUCT_PAGE_INIT = n이므로 빈 코드이다
- L4112 : <— watermark 체크 실패한 경우 진행할 차례

 

Reclaim 관련 자료
- https://lpc.events/event/11/contributions/896/attachments/793/1493/slides-r2.pdf
- https://www.youtube.com/watch?v=0bnWQF7WQP0

 

prep_new_page()
- L2426 : post_alloc_hook() 함수로 poison 체크, tag 설정, init page clear를 수행한다
- L2428 : gfp_flags에 __GFP_COMP가 있으면 함수 prep_compound_page()로 compound 데이터를 설정한다
- L2437 : alloc flags에 ALLOC_NO_WATERMARKS가 있으면 page를 pfmemalloc page로 만든다. 이는 page->lru.next의 1번 비트를 세팅하여 이루어진다. ALLOC_NO_WATERMARKS가 없으면 비트를 클리어한다

 

CONFIG_KASAN_HW_TAGS : MTE HW를 의미한다
CONFIG_KASAN_SW_TAGS : Shadow 영역에 tag를 기록하는 SW kasan을 의미한다
CONFIG_ARM64_MTE : MTE HW가 구현되어 있는지 의미

 

post_alloc_hook()
- L2391 : page->private = 0
- L2392 : page->refcount을 1로 세팅
- L2394 : arch_alloc_page()는 빈 함수
- L2395 : debug_pagealloc_map_pages() 함수로 새로 할당된 page에 대해 가상주소 매핑을 해준다. 현재는 debug_pagealloc 설정이 안되어 있으므로 빈 함수
- L2402 : 함수 kernel_unpoison_pages()로 page 내의 poison pattern이 잘 세팅되어 있는지 체크한다. 차이가 있으면 에러를 출력하고 덤프한다. 이 함수는 config PAGE_POISONING = y이어야 작동하며, 이는 page를 free 직후 poisoning하는 옵션이다.
- L2409 : 함수 kasan_has_integrated_init()으로 CONFIG_KASAN_HW_TAGS = y이고 static key kasan_flag_enabled가 true이면 참이다. 그러면 함수 kasan_alloc_pages()을 수행한다
- L2411 : CONFIG_KASAN_HW_TAGS = n이거나 static key kasan_flag_enabled = false이면 else문으로 이동
- L2414 : 함수 kasan_unpoison_pages()를 수행한다
- L2415 : init = true이면 함수 kernel_init_free_pages()로 init을 수행하고 __GFP_ZEROTAGS가 설정되어 있으면 tag도 클리어한다 (__GFP_ZEROTAGS는 CONFIG_ARM64_MTE = y일 때만 설정됨)
- L2420 : 함수 set_page_owner()는 CONFIG_PAGE_OWNER = n이므로 빈 함수이다

 

- 정리하면
1) CONFIG_ARM64_MTE = y, CONFIG_KASAN_HW_TAGS = y, kasan_flag_enabled = y이면 MTE 태그를 세팅한다. init = true인 경우 페이지를 클리어한다. 만약 __GFP_ZEROTAGS = y였으면 MTE tag도 클리어한다
2) CONFIG_ARM64_MTE = y, CONFIG_KASAN_HW_TAGS = y, kasan_flag_enabled = n이면 init = true인 경우 페이지만 클리어한다. 태그는 안쓴다
3) CONFIG_KASAN_HW_TAGS= n이고 KASAN SW를 쓰는 경우 shadow 영역에 tag를 설정한다. init = true이면 페이지를 클리어한다
4) KASAN을 안쓰면 init = true일 때 페이지 클리어만 한다

 

kernel_unpoison_pages()
- L81 : 함수 kmap_atomic()로 page로부터 addr를 구한다
- L82 : kasan을 off 한다
- L88 : 함수 check_poison_mem()로 page 영역의 poison 값을 체크한다
- L89 : kasan을 on 한다
- L90 : 함수 kunmap_atomic()으로 pagefault_enable(), preempt_enable()을 수행하고 리턴

 

kmap_atomic()
- CONFIG_HIGHMEM = n이다
- CONFIG_PREEMPT_RT = n이므로 preempt_disable() 수행 후 pagefault_disable() 수행
- pagefault_disable()은 current->pagefault_disabled++ 수행 후 barrier()이다
- 이후 page_address()를 수행하고 리턴한다

 

page_address()
- CONFIG_HIGHMEM = n, HASHED_PAGE_VIRTUAL 및 WANT_PAGE_VIRTUAL이 정의되지 않았으므로 lowmem_page_address() 이다
- lowmem_page_address()는 page_to_virt()이고 이는 함수 page_kasan_tag()와 __tag_set()로 해당 주소의 kasan (또는 MTE) tag를 붙여서 리턴한다

 

page_kasan_tag()
- page를 받아서 page->flags의 kasan tag를 발라내서 리턴한다

 

__tag_set()
- addr, tag를 받아서 tag를 addr의 56번째 비트로 올려서 합친 뒤 리턴한다
- 즉 tagged addr로 만들어서 리턴

 

check_poison_mem()
- 함수 memchr_inv()로 page내에 PAGE_POISON 패턴이 아닌 부분을 찾아서 출력하고 메모리를 덤프한다
- 이상이 없으면 그냥 리턴한다

 

pagefault_enable()
- barrier() 수행 후 current->pagefault_disabled-- 수행

 

kasan_alloc_pages()
- L180 : init에 init on free가 아니면서 init on alloc인지 기록한다 (flags로부터)
- L182 : flags에 __GFP_SKIP_KASAN_POISON이 있으면 page flag에 skip kasan poison을 set한다
- L185 : flags에 __GFP_ZEROTAGS가 있으면 각 page에 대해서 함수 tag_clear_highpage()를 수행한다
- L191 : flags에 __GFP_ZEROTAGS가 없으면 함수 kasan_unpoison_pages()를 수행한다

 

tag_clear_highpage()
- 인자로 page를 받는다
- mte_zero_clear_page_tags() 함수로 page의 내용과 태그를 zeroing한다
- 함수 page_kasan_tag_reset()로 page flag의 kasan tag를 클리어한다
- page->flags의 PG_mte_tagged bit를 set한다

 

mte_zero_clear_page_tags()
- DCZID_EL0 register의 하위 4bit로부터 블록 개수를 가져오고
- 그 블록 개수로 부터 한 번의 dc gzva 명령어로 업데이트 될 메모리 주소 바이트를 계산한다 (1 블록 = 4 byte)
- 그리고 clear 할 page 시작 주소의 tag 영역을 clear 하고
- dc gzva 명령으로 타겟 주소와 tag를 clear한다
- 타겟 주소를 업데이트하고
- 1개 page 주소만큼 업데이트 될 때까지 위 작업을 반복하고 리턴한다
- 만약 CONFIG_ARM64_MTE = n이면 그냥 빈 함수이다

 

DCZID_EL0
- https://developer.arm.com/documentation/ddi0595/2021-06/AArch64-Registers/DCZID-EL0--Data-Cache-Zero-ID-register

 

DC GZVA
- https://developer.arm.com/documentation/ddi0595/2021-06/AArch64-Instructions/DC-GZVA--Data-Cache-set-Allocation-Tags-and-Zero-by-VA?lang=en
- 타겟 주소의 페이지를 클리어하면서 해당 주소에 tag값을 세팅한다 (주소의 56번째 4 비트)
- 한 번에 2^(DCZID_EL0) x 4바이트 만큼을 클리어/세팅한다

 

DC GVA
- https://developer.arm.com/documentation/ddi0595/2021-06/AArch64-Instructions/DC-GVA--Data-Cache-set-Allocation-Tag-by-VA?lang=en
- 타겟 주소의 tag값을 세팅한다 (주소의 56번째 4 비트)
- 한 번에 2^(DCZID_EL0) x 4바이트 만큼을 세팅한다

 

page_kasan_tag_reset()
- kasan_enabled() 되어 있는 경우 함수 page_kasan_tag_set()으로 페이지의 page->flags의 kasan tag 비트를 클리어한다
- kasan_enabled()은 HW Kasan의 경우 static key kasan_flag_enabled에 의해 결정되고 SW Kasan의 경우 CONFIG_KASAN = y인지에 의해 결정된다

 

page_kasan_tag_set()
- kasan_enabled() = true이면 인자로 들어온 page의 page->flags의 kasan tag를 인자로 들어온 tag로 세팅한다

 

kasan_unpoison_pages()
- kasan_enabled()이 참이면 함수 __kasan_unpoison_pages()를 수행한다

 

__kasan_unpoison_pages()
- L111 : 함수 kasan_random_tag()으로 random tag를 받는다
- L112 : 각 페이지의 page->flags에 kasan tag를 생성한 tag로 세팅한다 (함수 page_kasan_tag_set() 사용)
- L114 : 함수 kasan_unpoison()를 수행하여 메모리에 태그를 생성한다

 

kasan_random_tag()
- CONFIG_KASAN_HW_TAGS = y이면 함수 hw_get_random_tag()로 랜덤 태그를 생성한다. 이는 결국 IRG 명령어를 이용해서 랜덤 태그를 만든다. 만들어진 랜덤 태그는 0xF0 | (IRG에 의해 생성된 random 4-bit)로 구성된다
- SW KASAN의 경우 함수 kasan_random_tag()를 이용해서 pcpu 변수 prng_state를 이용, linear congruent generator로 만들어진 랜덤값을 생성한다

 

IRG
- https://developer.arm.com/documentation/ddi0596/2020-12/Base-Instructions/IRG--Insert-Random-Tag-
- 랜덤으로 4bit를 생성해서 입력 레지스터의 [59:56]에 삽입해서 출력 레지스터에 리턴한다

 

kasan_unpoison()
- 인자로 addr, size, init을 받는다


1) CONFIG_KASAN_HW_TAGS = y인 경우
- addr에서 tag를 구하고 함수 hw_set_mem_tag_range()로 addr부터 size만큼의 페이지에 대해 tag를 세팅한다. 만약 init = true인 경우에는 tag 세팅과 함께 페이지 데이터를 zeroing을 수행한다. 이 때 DC GZVA 또는 DC GVA 명령 단위보다 align이 작은 앞/뒤 영역은 STZG / STG 명령으로 tag set 및 page clear를 수행하고, 가운데 큰 영역은 DC GZVA 및 DC GVA 명령어로 처리한다


2)  SW KASAN일 경우
- addr에서 tag를 구하고 함수 kasan_poison()으로 addr부터 size만큼의 shadow 영역에 대해 tag값을 설정한다
- 여기서는 init은 수행하지 않는다

 

STZG
- https://developer.arm.com/documentation/ddi0596/2020-12/Base-Instructions/STZG--Store-Allocation-Tag--Zeroing-
- 16 바이트 단위로 주소 영역을 클리어하면서 tag 값을 세팅한다. Tag는 주소의 [59:56] 값이다
- 비슷한 STG 명령은 tag 값 세팅은 동일하나 데이터를 클리어하지 않는다

 

kernel_init_free_pages()
- L1267 : zero_tags == true이면 각 페이지에 대해 함수 tag_clear_highpage()로 페이지와 태그를 클리어하고 리턴한다
- L1274 : kasan disable
- L1275 : 각 페이지에 대해 함수 clear_highpage()로 클리어하고 태그는 보존한다
- L1281 : kasan enable

 

prep_compound_page()
- L736 : page head flag set하여 head page 표시
- L737 : 모든 tail page에 대해 page->mapping에 TAIL_MAPPING을 표시하고 page->compound head에 head page를 가리키게 한다. 이 때 head page 주소의 0번째 비트를 1로 세팅하여 compound임을 표시한다
- L743 : compound page의 destructor id를 설정한다. 이는 page[1]에 설정된다
- L744 : compound order 설정. 이는 page[1]에 설정된다
- L745 : compound mapcount를 -1로 설정. page[1]에 설정됨
- L746 : compound order가 2 이상이면 page[2]에 hpage_pinned_refcount를 0으로 설정한다

 

reserve_highatomic_pageblock()
- high atomic 용으로 1 pageblock 또는 zone의 managed page의 1% 정도를 상한으로 하여 메모리를 확보한다
- 인자로 page, zone, alloc_order를 받는다
- L2778 : reserve cap을 계산한다
- L2779 : 현재 reserve 크기가 cap을 넘어서면 그냥 리턴
- L2782 : zone->lock spinlock을 잡고 irq를 끈다
- L2785 : 다시 한 번 reserve를 체크한다 (lock 때문)
- L2789 : page의 pageblock migrate type이 high atomic / isolate / cma가 아니면 현 page의 pageblock을 high atomic으로 바꾸고, free page들을 high atomic으로 이동시킨다. 그리고 reserve 크기를 pageblock 만큼 증가시킨다
- L2798 : zone->lock을 풀고 irq를 켠다
- 관련 history
- https://lwn.net/Articles/658081/
- https://github.com/torvalds/linux/commit/e010487dbe09d
- https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=56fd56b868f19385c50af8941a4c78df433b2d32
- https://github.com/torvalds/linux/commit/64c5e135bf5a2a7f0ededb3435a31adbe0202f0c
- https://lore.kernel.org/lkml/1442832762-7247-9-git-send-email-mgorman@techsingularity.net/
- https://lore.kernel.org/lkml/1442832762-7247-9-git-send-email-mgorman@techsingularity.net/

XE Login