[커널 17차] 32주차

2021.04.11 17:49

ㅇㅇㅇ 조회 수:443

arm64_memblock_init() 진행 중

 

reserve_elfcorehdr()
- DTB에 있는 elf core header 내용을 위한 메모리 영역을 reserve한다
+-- primary kernel core image에 대한 정보를 담고 있는 영역
+-- dump capture kernel이 primary kernel의 system memory에 접근할 때 사용된다
- 우선 elf core header에 대한 DTB 정보를 읽어서 elfcorehdr_addr와 elfcorehdr_size를 구한다
- 해당 영역이 기존 reserve된 memblock 영역과 겹치는 지 검사한다
- elfcorehdr_addr부터 elfcorehdr_size만큼을 reserve한다


early_init_dt_scan_elfcorehdr()
- depth가 1이고 uname이 "chosen"인 노드를 찾는다
- 해당 노드에서 "linux,elfcorehdr"라는 property를 찾는다
- property가 없거나 있어도 property size가 안맞으면 그냥 리턴
- 있으면 전역변수 elfcorehdr_addr, elfcorehdr_size에 property를 parsing해서 기록한다

- 전역변수 high_memory를 가상 주소 최상위 + 1byte로 기록한다

 


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

 

CMA 이론
- http://jake.dothome.co.kr/cma/

 

DTB에 기록된 데이터를 바탕으로 CMA 관련 이미 다음 과정이 수행되었다.

 

1. parse_early_param()에서 다음이 수행됨

 

early_cma() : kernel/dma/contiguous.c, line51
- DTB에서 다음 파라미터를 parsing해서 전역변수에 저장한다
- size_cmdline, base_cmdline, limit_cmdline
- 만약 DTB에서 위 파라미터를 따로 세팅해주지 않았으면 size_cmdline = -1로 compile time에 정해진다
- 이것은 parse_early_param()에 실행된다

 

2. 컴파일 타임에 다음 사항이 정의된다

 

RESERVEDMEM_OF_DECLARE에 의해 아래가 정의되어 있다.
RESERVEDMEM_OF_DECLARE(cma, "shared-dma-pool", rmem_cma_setup);

 

#define RESERVEDMEM_OF_DECLARE(name, compat, init) \
    _OF_DECLARE(reservedmem, name, compat, init, reservedmem_of_init_fn)

 

#define _OF_DECLARE(table, name, compat, fn, fn_type)    \
        static const struct of_device_id __of_table_##name    \
                __used __section(__##table##_of_table)    \
                 = { .compatible = compat,        \
                     .data = (fn == (fn_type)NULL) ? fn : fn  }

 

static const struct of_device_id __of_table_cma = 
{
    .compatible = "shared-dma-pool",
    .data = rmem_cma_setup
};

 

- 위 정의에 의해 __of_table_cma는 __reservedmem_of_table array의 entry로 들어간다.

(__reservedmem_of_table에 대해서는 전 주차 노트 참조)


3. early_init_fdt_scan_reserved_mem()에서 다음이 수행됨

 

- DTB parsing을 통해 (fdt_reserved_mem_save_node() 함수에서) cma에 해당하는 reserved_mem 배열 array에 FDT node, uname, base, size를 기록한다.
- 이후 __reserved_mem_init_node()에서 위 rmem_cma_setup() 함수를 실행하게 된다.
(drivers/of/of_reserved_mem.c line 183)

 

rmem_cma_setup(struct reserved_mem *rmem)
- align을 4MB로 잡는다
- cma fdt node에서 "linux,cma-default" property를 찾는다
- size_cmdline이 디폴트값 -1이 아닌데 default property가 있으면 에러 리턴
- "reusable" property가 없거나 "no-map" property가 있으면 에러 리턴
- base와 size가 align이 안맞으면 에러 리턴
- 이후 cma_init_reserved_mem() 함수를 호출하여 이미 reserved_mem에서 cma data를 꺼내 전역변수 배열 cma_area에 추가한다
+-- 이미 memblock에 해당 영역은 reserved된 상태임
+-- 추가로 전역 변수 배열 reserved_mem에도 cma 관련 entry가 채워진 상태
+-- cma_init_reserved_mem() 함수는 이 cma관련 entry를 꺼내서 cma 정보를 관리하는 배열 cma_area에 넣는 것임
- dma_contiguous_early_fixup() 함수를 호출하여 전역변수 배열 dma_mmu_remap에 base 및 size를 추가한다.
- cma fdt node에 "linux,cma-default" property가 있으면 dma_contiguous_set_default() 함수를 호출해서 전역변수 dma_contiguous_default_area를 현재 cma 값으로 설정한다
- rmem->op을 rmem_cma_ops로 설정한다
+-- reserved_mem_ops 구조체 rmem_cma_ops은 두 개의 함수로 구성되어 있다
+-- rmem_cma_device_init 함수와 rmem_cma_device_release
- rmem->priv를 현재 cma 값으로 설정한다

 

cma_init_reserved_mem()
- struct cma 전역변수 배열인 cma_areas가 이미 꽉찬 상태이면 에러 리턴
- reserve할 size가 0이거나 base부터 size만큼의 영역이 reserved영역과 겹치면 에러 리턴
- alignment를 4MB로 잡는다 (MAX_ORDER 11bit, pageblock_order 10bit 기준)
- alignment가 cma 자료구조에 기록된 1 << order_per_bit에 align되어 있지 않으면 에러 리턴
+-- rmem_cma_setup()에서 호출될 때는 order_per_bit = 0이다.
- base나 size가 4MB align되어 있지 않으면 에러 리턴
- cma_area 배열에 새 entry cma를 기록한다
+-- cma->name에 name을 기록
+-- cma->base에 base를 기록 (page frame number 형태로 기록)
+-- cma->order_per_bit에 order_per_bit을 기록
+-- cma_area_count를 1 증가
+-- totalcma_pages를 size/PAGE_SIZE 만큼 증가
- 추가된 cma entry를 포인터로 리턴하고 종료

 

dma_contiguous_early_fixup()
- arch/arm/mm/dma-mapping.c, line 392
- 인자로 base, size를 받아서 전역 변수 배열 dma_mmu_remap에 새 엔트리를 추가한다
- 엔트리는 dma_contig_early_reserve 구조체이고 멤버변수는 base, size이다
- 이후 엔트리 개수 dma_mmu_remap_num를 1 증가시킨다


dma_contiguous_set_default(cma)
- 전역변수 dma_contiguous_default_area를 인수 cma로 설정

 

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

 

dma_contiguous_reserve()
- contiguous memory handling을 위한 메모리 영역을 reserve한다
- DTB에서 따로 size_cmdline을 지정해주는 경우 selected_size, selected_base을 각각 size_cmdline, base_cmdline으로 지정하고 selected_limit도 limit_cmdline과 argument로 받은 limit 중 minimum으로 지정한다. (0인 값 제외) 이 때 base_cmdline + size_cmdline이 limit_cmdline이랑 똑같으면 fixed = true로 지정한다.
- DTB에서 따로 size_cmdline을 지정해주지 않은 경우 CONFIG 옵션에 따라 달라진다. 이 경우 fixed = false이다.
1) CONFIG_CMA_SIZE_SEL_MBYTES : selected_size를 디폴트값 size_bytes로 지정한다.
2) CONFIG_CMA_SIZE_SEL_PERCENTAGE : CONFIG_CMA_SIZE_PERCENTAGE의 비율대로 selected_size를 지정한다. 이 때 cma_early_percent_memory() 함수로 사이즈를 계산한다.
3) CONFIG_CMA_SIZE_SEL_MIN : 디폴트값 size_bytes와 cma_early_percent_memory() 값 중 작은 것으로 지정한다
4) CONFIG_CMA_SIZE_SEL_MAX : 디폴트값 size_bytes와 cma_early_percent_memory() 값 중 큰 것으로 지정한다
- 이후 selected_size가 0이 아니고 dma_contiguous_default_area가 NULL이 아니면 dma_contiguous_reserve_area() 함수로 메모리 영역을 reserve한다


cma_early_percent_memory()
- memblock loop를 돌면서 총 페이지 개수의 합을 구한다
- CONFIG_CMA_SIZE_PERCENTAGE의 비율에 따라 CMA 크기를 계산하여 리턴한다


dma_contiguous_reserve_area(size, base, limit, res_cma, fixed)
- base부터 size만큼 limit을 최대 상위 주소값 한계로 하여 cma를 위한 메모리 영역을 reserve한다
- res_cma는 DTB에서 parsing한 cma 데이터이다.
- fixed가 true면 정확하게 base부터 size만큼 영역을 reserve하고 아니면 base부터 limit 내의 영역에 reserve한다.
- 함수 cma_declare_contiguous()로 메모리 reserve하고 dma_contiguous_early_fixup()으로 fixup 수행한다

 

cma_declare_contiguous()


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

Queued Spinlock

 

atomic operation vs cache coherence protocol
- CAS나 LL_SC로 store 시도하면 실패해도 다른 cache에 invalidate를 날리는 것으로 보인다.
- http://15418.courses.cs.cmu.edu/spring2013/article/31
- http://jake.dothome.co.kr/exclusive-loads-and-store/ : cache bouncing 문제 참고
- 이를 해결하기 위해 test & test & set을 사용하기도 한다
(먼저 일반 load로 lock 걸렸는지 확인하고 안 걸렸을 때만 atomic operation으로 lock을 잡는 방법)

 

관련 코드
- include/linux/spinlock.h
- include/asm-generic/qspinlock.h
- include/asm-generic/qspinlock_types.h
- kernel/locking/qspinlock.c
- kernel/locking/mcs_spinlock.h

 

qspinlock은 MCS lock과 별도로 존재하며 percpu 레벨에서 normal task/softirq/hardirq/nmi 등의 context 별로 lock을 잡는다

MCS lock은 task별로 lock을 잡는다

 

qspinlock 코드 및 https://0xax.gitbooks.io/linux-insides/content/SyncPrim/linux-sync-2.html 파악 중

XE Login