[커널 17차] 71주차

2022.01.09 01:24

ㅇㅇㅇ 조회 수:141

mm_init()
- page_ext_init_flatmem() : SPARSEMEM 설정 되어 있으므로 빈 함수
- init_mem_debugging_and_hardening()
- kfence_alloc_pool() : KFENCE off 이므로 빈 함수
- mem_init() <-- 진행 중

 

init_mem_debugging_and_hardening()
- 메모리 디버깅 및 페이지 체크 관련 static key 설정을 수행한다

 

- CONFIG_PAGE_POISONING
1) page free할 때 page에 poison 패턴 = 0xaa = 8’b10101010을 넣고 page alloc할 때 poison 패턴을 검증하는 식으로 체크를 수행한다
- https://stackoverflow.com/questions/22717661/linux-page-poisoning
2) 현재는 CONFIG off인 상태
3) early param으로 _page_poisoning_enabled_early = true이거나, CONFIG_DEBUG_PAGEALLOC = y 이고 easy param으로 _debug_pagealloc_enabled_early = true 이면서 CONFIG_ARCH_SUPPORTS_DEBUG_PAGEALLOC = n 이면 page poisoning 기능을 사용한다 —> static key enable

 

- static_branch_maybe : https://github.com/torvalds/linux/commit/0d66ccc1627013c95f1e7ef10b95b8451cd7834e/

 

- init on alloc / init on free : alloc/free 시에 메모리 영역을 zeroing하는 것
- https://patchwork.kernel.org/project/linux-security-module/patch/20190626121943.131390-2-glider@google.com/#22731857
- 이 기능들도 early param으로 설정한다
- early param이 켜져 있어도 page poisoning을 사용하는 경우엔 끈다
- 이 기능들도 static key로 enable한다
- 현재 config off임

 

- CONFIG_DEBUG_PAGEALLOC : page 해제 되면 table도 해제하는 방식
- CONFIG_ARCH_SUPPORTS_DEBUG_PAGEALLOC = y 여야 쓸 수 있다
- early param으로 _debug_pagealloc_enabled / _debug_guardpage_enabled 을 설정하고 static key enable로 실제 사용하게 한다
- 현재는 config 꺼져 있음

 

kfence_alloc_pool()
- KFENCE : KASAN 보다 약하지만 메모리 체크를 적은 오버헤드로 수행해주는 기능으로, KASAN을 쓸 수 없는 production kernel code에 적용할 수 있는 기능이다
- 현재는 config off 이다

 

report_meminit()
- stack을 초기에 특정 패턴으로 초기화할지, 0으로 초기화할지 등을 설정한다
- 현재 config는 아무것도 설정되어 있지 않으므로 “off”에 해당된다
- heap alloc/free는 init on alloc / init on free에 따라 on/off가 결정된다
- 현재는 heap alloc/free 역시 off로 설정되어 있다
- 여기서 말하는 stack/heap은 커널용 stack/heap을 의미한다

 

stack_depot_init()
- STACKDEPOT config는 현재 꺼져 있어서 빈 함수이다
- STACKDEPOT는 stack trace를 저장하기 위한 hash table을 만들어서 동일한 stack trace가 중복 저장되는 것을 막는다
- https://lore.kernel.org/lkml/307898236fd33191d65b541103da4c5c4a44da16.1455814741.git.glider@google.com/

 

mem_init()
- SWIOTLB 관련 처리 수행
- SWIOTLB는 과거 낮은 물리주소에만 접근할 수 있는 DMA device를 위해 만들어 놓은 bounce buffer이다
- 즉 DMA가 32bit만 접근이 가능한데 시스템은 48bit 물리주소를 가지고 있는 경우 하위 32bit만 DMA가 접근 가능하므로 상위 메모리 주소 접근을 위해서는 OS가 개입해서 처리해 주어야 한다
- 즉 커널 메모리에서 데이터를 카피해서 bounce buffer에 넘겨주고, 이제 DMA에서 bounce buffer에 접근해서 처리한 다음 다시 OS가 bounce buffer에서 데이터를 읽어서 원래 목적 주소에 써 주는 방식으로 생각된다
- 이를 위해서는 bounce buffer는 DMA가 접근가능한 가장 낮은 물리주소를 할당받아야 하므로 mem_init() 가장 처음에 메모리를 할당받는다
- 최근에는 64bit DMA 및 IOMMU 등이 많이 사용되고 있는데 이 경우에는 필요 없다

 

- https://groups.google.com/g/osinside/c/7hTLmB1HEOQ?pli=1
- https://lwn.net/Articles/91870/
- https://lwn.net/Articles/845096/
- https://wiki.gentoo.org/wiki/IOMMU_SWIOTLB
- https://github.com/torvalds/linux/commit/8424ecdde7df99d5426e1a1fd9f0fb36f4183032/

 

- swiotlb_force = SWIOTLB_FORCE 이면 무조건 swiotlb_init() 함수로 bounce buffer를 생성한다
- arm64_dma_phys_limit 값이 시스템 물리 메모리 주소보다 작으면 마찬가지로 swiotlb_init() 함수로 bounce buffer를 생성한다
- 아니면 xen에서 SWIOTLB 쓸 필요가 없으면 (!xen_swiotlb_detect()) swiotlb_force = SWIOTLB_NO_FORCE로 설정한다

- 함수 set_max_mapnr로 전역 변수 max_mapnr을 max_pfn - memstart_addr로 업데이트한다 (NUMA가 아닌 경우)
- memblock_free_all() <-- 진행 중


swiotlb_init()
- 64MB에 해당하는 최하의 물리주소를 가지는 bounce buffer를 할당한다
- 그리고 bounce buffer를 관리하는 자료구조인 io_tlb_default_mem를 초기화한다
- bounce buffer의 엔트리 하나는 2KB이고 총 32K 개의 엔트리로 구성된다
- 각 엔트리는 io_tlb_default_mem->slot 으로 관리되며 이 메타 데이터를 위한 메모리도 할당받는다
- 이후 함수 swiotlb_init_io_tlb_mem()를 이용해서 bounce buffer의 물리 시작주소, 크기, 엔트리 수, 현재 사용된 엔트리 수, SWIOTLB_FORCE 여부, 각 엔트리의 원래 커널 주소, alloc size, list 값들을 초기화한다
- 할당된 bounce buffer 영역과 크기를 커널 로그에 출력한다
- 할당된 bounce buffer 용량에 따라 전역변수 max_segment를 설정한다

 

memblock_free_all()
- free_unused_memmap()
- reset_all_zones_managed_pages()
- free_low_memory_core_early() <— 진행 중

 

free_unused_memmap()
- CONFIG_SPARSEMEM_VMEMMAP = y 이므로 그냥 리턴

 

reset_all_zones_managed_pages()
- 처음엔 reset_managed_pages_done = 0 이다
- 모든 노드의 managed_pages를 모두 0으로 리셋한다
- reset_managed_pages_done = 1로 만들고 리턴한다

 

free_low_memory_core_early()
- memblock_clear_hotplug()로 모든 memblock 영역의 hotplug flag를 제거한다
- memmap_init_reserved_pages()로 reserved 영역과 nomap flag 영역의 page에 __SetPageReserved() 수행 <— 여기 까지 진행

 

memmap_init_reserved_pages()
- 모든 reserved memblock 영역에 대해서 함수 reserve_bootmem_region()로 __SetPageReserved()를 수행한다
- 또한 memblock 영역 중 nomap region에 대해서도 함수 reserve_bootmem_region()로 __SetPageReserved()를 수행한다
- https://github.com/torvalds/linux/commit/a7259df7670240ee03b0cfce8a3e5d3773911e24/

 

reserve_bootmem_region()
- 인자로 받은 시작 주소부터 마지막 주소까지의 page에 대해서 page->lru list head를 초기화하고 해당 page flag의 reserved bit을 set 한다 (PG_reserved, page->flag의 12번째 비트)
- 함수 init_reserved_page()는 CONFIG_DEFERRED_STRUCT_PAGE_INIT = n 이므로 빈 함수이다

 

=================================================

1. 5.10 에 다음 2개 패치 들어감

A. fdt: Properly handle "no-map" field in the memory region
https://github.com/torvalds/linux/commit/86588296acbfb1591e92ba60221e95677ecadb43/

B. of/fdt: Make sure no-map does not remove already reserved regions
https://github.com/torvalds/linux/commit/8a5a75e5e9e55de1cef5d83ca3589cb4899193ef/

 

2. 5.4에 A,B가 backporting되었는데 driver의 allocation failure 가 생긴다고 주장(뭔지 아무도 모름)
추가로 /proc/iomem 에서 no-map reserved가 system memory로 잡히는 것 확인. 동일 원인으로 추정.

 

3. 분석결과 다음 2개 패치가 5.10에 함께 들어가 있었는데 backporting이 같이 안되어서 문제 발생한 것 같다.
5.10에 C,D 빼니깐 nomap reserved가 system memory로 잡히더라. (driver alloc fail은 모른다...)

C. arch, drivers: replace for_each_membock() with for_each_mem_range()
https://github.com/torvalds/linux/commit/b10d6bca87204cdafd0cd7aaa837ad30b4eb8c20/

D. memblock: use separate iterators for memory and reserved regions
https://github.com/torvalds/linux/commit/cc6de1680538633e4ef9540b2313fa2481a7c641/

 

4. backporting 은 revert. 수사종결. (2021.05)

 

5. 5.12에서 A의 보완패치 들어감.(일부만 하던걸 전부 다 하게 함) (2021.06)

E. of: of_reserved_mem: mark nomap memory instead of removing
https://github.com/torvalds/linux/commit/7b25995f5319adc354a430df0eb2ec7f79a1e807/

 

6. 5.14에서 nomap을 reserved 마킹함.

F. memblock: update initialization of reserved pages
https://github.com/torvalds/linux/commit/9092d4f7a1f846bcc72e9aace4ed64ed3fc4aa32/

 

7. 5.15 - nomap에서 더이상 memblock_remove를 안하므로 memblock_find_in_range + memblock_reserve를 memblock_phys_alloc_range로 변경.

G. memblock: make memblock_find_in_range method private
https://github.com/torvalds/linux/commit/a7259df7670240ee03b0cfce8a3e5d3773911e24/


prefetch 명령어
- pfrm pstl1keep
- pst : prefetch for store
- l1 : L1 cache
- keep : keep in cache <—> strm (streaming)
- https://developer.arm.com/documentation/dui0801/h/A64-Data-Transfer-Instructions/PRFM--literal-

XE Login