[커널 17차] 42주차

2021.06.20 00:25

ㅇㅇㅇ 조회 수:296

unflatten_device_tree()
- unittest_unflatten_overlay_base() 실행

 

unittest_unflatten_overlay_base()
- Device tree overlay를 kernel에서 API를 통해 수행할 수 있다 (dtc 및 부트로더에서도 가능한 것으로 보임 --> 본래 dtc로 수행하는게 보통?)
- Device tree 관련 기능에 대해서는 unittest 수행이 가능하다
- 이 중 device tree overlay를 수행하는 기능에 대해서도 unit test가 존재한다
- Overlay 기능에 대해 unittest 수행을 위해서는 unflatten된 overlay device tree가 필요하다
- 따라서 이 함수에서 테스트용 overlay device tree를 미리 unflatten 시킨다
- Unittest를 이 함수에서 수행하지는 않는다

 

Device Tree Overlay 내용
- https://source.android.com/devices/architecture/dto

 

Open Firmware Devicetree Unittest
- https://www.kernel.org/doc/html/latest/devicetree/of_unittest.html
- https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=19fd74879a32fb10357e0cda9c8050f01bb3eeb8

 

Devicetree Overlay
- https://www.kernel.org/doc/html/latest/devicetree/overlay-notes.html#
- https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=81d0848fc8d2058c4cc645d971435c889869433b

 

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

 

nomap 영역
- 커널이 해당 영역의 물리 메모리를 쓸 수 없다 (memblock memory에서 삭제)
- 디바이스 드라이버 등이 매핑해서 써야 한다

 

chosen node
- "bootargs" property에 추가해야 early param로 동작한다

 

head.S에서 BSS 영역은 0으로 초기화 한다
- head.S의 line 447 참조 (clear BSS 주석)
- All the static variables that do not have an explicit initialization or are initialized to zero are stored in the uninitialized data segment( also known as the BSS segment). Compared to this, the static variables that are initialized are stored in the initialized data segment.

 

bootmem_init()
- memblock start와 end에 해당하는 page frame number를 min, max로 가져온다
- early_memtest()로 min, max pfn에 대해 테스트 수행
- max_pfn, max_low_pfn = max
- min_low_pfn = min
- max_low_pfn, min_low_pfn : highmem legacy 관련 내용으로 추정됨
- arm64_numa_init() 수행

 

Page align 관련 commit
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=8f73d4b7011061f46ba6f46006b2848b412ff43f

 

early_memtest()
- parse_memtest()가 수행되지 않았으면 그냥 리턴
- memtest_pattern 횟수만큼 메모리 테스트 수행 - do_one_pass() 함수

 

parse_memtest()
- early_param 함수로 do_early_param()에서 수행
- arg를 받아서 memtest_pattern 전역 변수에 arg를 string to unsigned integer 변환해서 저장
- arg가 NULL이면 patterns 전역변수 배열 길이로 memtest_pattern을 저장

 

do_one_pass()
- free memory 영역에 대해서 memtest() 함수 수행
- memory 영역이고 reserve 영역이 아닌 것만 테스트

 

memtest()
- 정해진 패턴으로 쓰고 읽어서 정상이면 ok
- 아니면 해당 영역은 reserve_bad_mem()으로 bad 영역은 reserve 시킴

 

reserve_bad_mem()
- 그냥 memblock_reserve()랑 같다

 

DTB 예시
- numa-node-id 속성이 있는 DTB가 아래 예시 밖에 없어서 아래 dts를 가지고 분석 진행함
- arch/arm64/boot/dts/hisilicon/hip07.dtsi
- arch/arm64/boot/dts/hisilicon/hip07-d05.dts

 

arm64_numa_init()
- numa_init() 함수를 수행한다
- numa_init() 함수는 인자로 함수 포인터를 받는데, 이 함수 포인터에 따라 초기화가 달라진다
- numa_off가 true이면 함수 포인터로 dummy_numa_init()을 수행한다. NUMA 시스템이 아닌 경우이기 때문에 dummy 초기화를 하고 끝난다
- numa_off가 false이면 acpi가 disable되었는지에 따라 달라진다
- acpi가 disable이 된 경우 of_numa_init()를 수행한다
- acpi가 enable된 경우 arm64_acpi_numa_init()를 수행한다
- 본 스터디에서는 numa_off = false, acpi가 disable이라서 가정하고 분석했음

 

numa_off 전역 변수
- early_param에서 설정
- 기본은 false -> 기본은 numa 설정
- numa_parse_early_param() 함수에서 설정한다

 

numa_init()
- numa 노드를 관리하는 비트맵을 0으로 초기화한다
- 3개의 비트맵을 초기화함
- numa_nodes_parsed, node_possible_map, node_online_map
- 각각 노드가 parsing되었는지, possible 상태인지, online 상태인지를 나타낸다
- numa_alloc_distance() 함수로 노드간 거리를 초기화한다 (동 노드 10, 타 노드간 20)
- 함수 포인터를 실행한다 - init_func() --> 여기까지 진행

 

numa_alloc_distance()
- 노드 간 거리 정보를 초기화
- 노드 간 거리를 나타내는 메모리를 할당받고 reserve한다
- 해당 주소를 numa_distance 전역 변수 포인터로 할당한다
- 노드 개수는 MAX_NUMNODES로 설정한다
- 노드 간 거리를 동 노드 10, 타 노드간 20로 설정한다

 

of_numa_init()
- of_numa_parse_cpu_nodes() 함수로 cpu node를 모두 검색해서 numa_nodes_parsed 비트맵에 업데이트한다
- of_numa_parse_memory_nodes() 함수로 memory node를 검색, 모든 메모리 주소에 대한 node id를 검색하고 memblock에 반영한다. 그리고 numa_nodes_parsed 비트맵에 node id를 set한다
- of_numa_parse_distance_map()으로 노드간 거리 정보를 numa_distance 전역 변수 배열에 저장한다

 

of_numa_parse_cpu_nodes()
- 모든 cpu 노드를 검색해서 "numa-node-id" 속성을 찾는다
- "numa-node-id" 속성에서 node id를 추출해서 해당 id를 numa_nodes_parsed 비트맵에 1로 set한다


of_numa_parse_memory_nodes()
- 모든 "memory" 타입 노드를 검색한다
- 각 메모리 타입 노드에서 "numa-node-id" 속성을 찾아 nid에 저장한다
- 메모리 노드 하나에 reg값이 여러 개 있을 수 있으며, 각 reg에 대해 아래를 루프를 돌면서 수행한다
1) of_address_to_resource() 함수로 메모리 노드의 주소, 사이즈, flag, device full name을 가지는 resource 구조체를 만들어 리턴한다
2) numa_add_memblk() 함수로 메모리 노드의 시작 주소, 끝 주소를 이용해서 memblock을 분할하고 nid를 설정, 병합한다. 이후 비트맵 numa_nodes_parsed에 nid를 1로 세팅한다.

of_address_to_resource()
- of_get_address() 함수로 입력 device node의 물리주소, size, flag를 구한다
- of_property_read_string_index() 함수로 입력 device node의 "reg-names" 속성을 조사하여 name 문자열에 저장한다
- __of_address_to_resource() 함수를 이용해서 resource 구조체에 device node full name, 물리 시작 주소, 물리 주소 마지막, flag를 업데이트하여 반환한다

 

of_get_address()
- 부모 노드를 구해서 NULL이면 리턴한다 (of_get_parent() 함수)
- 부모 노드를 이용해서 bus를 구한다 (of_match_bus() 함수)
- bus에는 "pci", "isa", "default"가 있음
- 예시 dts (hisilicon)에서는 루트 노드에 "pci", "isa" 관련 노드가 없어서 "default"로 진행함
- of_bus_default_count_cells() 함수로 cell count값을 구함
- 예시 dts의 경우 #address-cells = <2>, #size-cells = <2>이므로 na, ns 모두 2로 설정
- default bus의 addresses = "reg"이므로 of_get_property() 함수로 메모리 node에서 "reg" 속성 및 사이즈 읽어서 저장
- dts 파일 기준으로 psize = 4 (32bit cell 4개이므로)
- 그러나 실제로는 UEFI에서 메모리 노드를 업데이트 할 것이므로 4의 배수로 여러 개가 나올 것임
- 루프를 돌면서 각 메모리 주소에 대해 of_read_number() 함수로 사이즈를 구하고 of_bus_default_get_flags() 함수로 flag IORESOURCE_MEM를 구함
- 그리고 "reg" 속성을 리턴

 

of_property_read_string_index()
- of_property_read_string_helper() 함수 호출

 

of_property_read_string_helper()
- 입력 노드에서 "reg-names" 속성을 조사 한뒤, 속성 value 값을 가키리는 문자열 포인터를 리턴한다
- 결국 "reg-names" 값, 즉 메모리 노드 주소의 이름 문자열을 반환하게 됨

 

__of_address_to_resource()
- flag가 IORESOURCE_MEM에 해당하므로 of_translate_address() 함수를 수행한다
- of_translate_address() 함수로 메모리 주소값을 구한다
- 방금 구한 메모리 물리 주소값과 인자로 받은 사이즈, flag, 메모리 노드 full name을 이용하여 resource 구조체에 메모리 물리주소 시작, 끝, flag, 메모리 노드 full name을 저장하고 리턴한다

 

of_translate_address()
- __of_translate_address() 함수를 수행하여 주소값을 반환한다

 

__of_translate_address()
- of_read_number() 함수로 속성 value에서 2개 cell, 즉 64bit 만큼의 값을 읽어서 리턴한다
- 속성이 "reg"이므로 메모리 물리주소를 리턴한 것이다

 

numa_add_memblk()
- memblock_set_node() 함수로 시작 주소부타 끝 주소까지 memblock 영역을 분할 후 nid 값을 부여, 다시 병합한다
- node_set() 함수로 numa_nodes_parsed 비트맵에 nid를 set한다

 

of_numa_parse_distance_map()
- 모든 노드를 검색해서 compatible 속성이 "numa-distance-map-v1" 인 노드를 찾는다 (of_find_compatible_node() 함수)
- 해당 노드에서 노드간 거리 정보를 parsing해서 numa_distance 전역변수 테이블에 반영한다 (of_numa_parse_distance_map_v1() 함수)

 

of_find_compatible_node()
- irq disable, devtree_lock 스핀락을 건다
- 모든 노드를 검색해서 compatible 속성이 인자와 동일한 노드를 찾는다
- irq restore, devtree_lock 스핀락을 푼다
- 찾은 노드를 반환한다

 

__of_device_is_compatible()
- compatible 속성이 인자로 받은 문자열과 동일한 노드를 찾는다
- 한 번에 찾아야 점수가 가장 높다
- type과 name에 가산점?이 있다 (각 +2, +1점)

 

of_numa_parse_distance_map_v1()
- 노드에서 "distance-matrix" 속성을 찾는다
- 속성의 length를 32bit cell 개수로 나눈 값을 구한다
- 3개를 하나의 row로 하여 row 별로 루프를 돌면서 각 노드간 거리를 numa_distance 전역 변수 배열에 저장한다

XE Login