[커널 17차] 65주차

2021.11.28 00:16

ㅇㅇㅇ 조회 수:182

kernel_param 생성 위치
- __module_param_call() 매크로에서 kernel_param을 생성해서 “__param” 영역에 저장함
- __param() —> module_param() —> __module_param_call() 순서대로 불림
- 위 방식으로 정의되면 level은 기본적으로 -1로 설정된다

 

obs_kernel_param 생성 : setup parameter
- __setup() —> __setup_param() 매크로에서 설정해준다

- parse_one()에서 파라미터가 저장되는 것은 아니고, command line 중 __param 영역에 저장된 커널 모듈 파라미터(kernel_param : __start___param ~ __stop___param) 에 해당되는 것이 있으면 callback 함수를 부르고 (level = -1이므로 걸러지지 않음), unknown boot option의 경우 따로 저장한 뒤 커널로그에 프린트한다
- unknown option check 중에 obsolete check 진행 : early param이거나 callback이 없거나 callback 결과가 error가 아니면 unknown이 아닌 것으로 취급한다 (obs_kernel_param : __setup_start ~ __setup_end) —> early가 아닌 setup param callback을 여기서 처리
- early param과 setup param은 이름이 같을 수 있다. 그래서 early param의 경우 일치하는 게 있어도 루프를 계속 수행한다 (“pci” early param, “pci=“ setup)
- setup_func은 1이 리턴될 때 정상이고 0이면 오류이다
- unknown option check 중에 module option은 unknown이 아닌 것으로 취급한다 (중간에 “.” 있는 경우)
- after_dashes 옵션이나 extra_init_args 옵션은 init 프로세스에게 넘어가는 파라미터이고 이는 따로 argv_init[]에 저장한다
- 전에 unknown option 중 argv_init[]에 들어간 옵션은 유지된다
- xbc cmdline 중에 key가 “kernel”이면 extra command line으로 추가되고, “init”이면 extra_init_args로 추가된다

 

—> 파리미터 처리에 버그 있음… 동일 파라미터에 순서만 바꿔도 다르게 나옴

 

- https://www.kernel.org/doc/html/v4.14/admin-guide/kernel-parameters.html
- https://github.com/torvalds/linux/commit/51e158c12aca3c9ac63988611a97c05109b14dc9

 

- initcall에서 early param이 0이고, 일반 module param은 -1, 나머지는 1부터 올라간다
- early param이 -1이고 일반 module param이 0인게 자연스러운 듯


- https://github.com/torvalds/linux/commit/ae82fdb1406ad41d68f07027fe31f2d35ba22a90
- https://www.collabora.com/news-and-blog/blog/2020/07/14/introduction-to-linux-kernel-initcalls/

 


- LOG 버퍼는 ring buffer를 쓰고 있으며 컴파일 타임에 만들어지는 static ring buffer가 있고 필요한 커널 로그 크기에 따라 런타임에 새로 만드는 dynamic ring buffer가 있음
- Dynamic ring buffer는 static ring buffer로 충분하지 않다고 판단될 때 만들어져서 static ring buffer를 대체하여 사용하게 되며 이 때 static ring buffer는 더 이상 사용되지 않는 것으로 보임
- 사용되지 않는다고 할지라도 static ring buffer는 kernel image 영역이라 제거되지는 않는다 (디폴트 설정 기준 약 128KB)
- 함수 setup_log_buf()에서 dynamic ring buffer 필요 여부를 판단해서 생성하고 판단 기준은 CPU의 개수임
- https://github.com/torvalds/linux/commit/23b2899f7f194f06e09b52a1f46f027a21fae17c


- 최근 패치에서 ring buffer를 lockless 메커니즘으로 새로 구현하여 대체함
- https://github.com/torvalds/linux/commit/896fbe20b4e2333fb55cc9b9b783ebcc49eee7c7

 

- ring buffer는 data ring buffer와 descriptor ring buffer로 나누어져 있음
- ring buffer는 구조체 printk_ringbuffer로 정의되고 아래 두 링 버퍼를 가짐
- 데이터 ring buffer는 구조체 prb_data_ring으로 정의됨
- descriptor ring buffer는 구조체 prb_desc_ring으로 정의됨
- descriptor ring buffer에는 prb_desc descs와 printk_info infos를 가짐

 

setup_log_buf()
- 함수 log_buf_add_cpu()에서 cpu 개수에 따라 dynamic ring buffer 필요 여부를 판단하고 필요할 경우 그 크기를 계산하여 전역 변수 new_log_buf_len에 전달한다
- 만약 dynamic ring buffer가 필요없으면 여기서 그냥 리턴하고 기존의 static ring buffer를 사용한다
- new_log_buf_len 값을 바탕으로 필요한 descriptor 개수를 구한다. Descriptor 1개당 평균 크기가 파라미터 PRB_AVGBITS로 명시되며 디폴트값은 32B이다. 따라서 new_log_buf_len / PRB_AVGBITS로 필요한 descriptor 개수를 계산한다
- new_log_buf_len 크기의 메모리를 memblock으로 할당받아 new_log_buf로 만든다
- Descriptor 개수 x descriptor 크기로 메모리를 memblock으로 할당받고 new_descs로 만든다
- Descriptor 개수 x info 크기로 메모리를 memblock으로 할당받고 new_infos로 만든다
- 함수 prb_rec_init_rd()로 지역 변수 구조체 printk_record r의 값들을 채워준다
- 함수 prb_init()로 dynamic ring buffer가 되는 printk_rb_dynamic를 초기화한다

 

log_buf_add_cpu()
- 디폴트 설정 기준 static ring buffer는 128KB이고, cpu 추가 1개당 더 필요한 용량은 4KB이다
- 현재 cpu 개수를 기준으로 추가 용량을 계산하여 이것이 static ring buffer 크기의 절반보다 크면 기존 static ring buffer size + 추가 필요한 용량을 2의 power로 올려서 업데이트한다 (최대 2GB)
- 예를 들어 cpu가 20개 이면 19 x 4KB = 76KB > 128KB/2 = 64KB 이므로 128 KB + 76KB = 204KB —> 256KB 크기로 전역 변수 new_log_buf_len를 업데이트한다

 

prb_init()
- 인자로 ring buffer rb, textbuf, descs, infos, textbits, descbits를 전달받아 rb를 초기화한다
- setup_log_buf()에서는 memblock으로 할당받은 new_log_buf, new_descs, new_infos로 printk_rb_dynamic를 초기화하게 된다

 

- Memset으로 descs/infos를 모두 0으로 초기화한다

 

- rb의 desc_ring을 descbits, descs, infos로 초기화한다. 이 때 head_id와 tail_id를 다음과 같이 초기화한다
- 매크로 DESC0_ID를 이용해서 head_id/tail_id를 초기화한다
- 예를 들어 descbits = 10 bit인 경우, 64bit인 head_id/tail_id는 다음과 같이 초기화된다
- [63:62] = 00
- [61:11] = 11111…111
- [10] = 0
- [9:0] = 1111…111
- 실제 head_id/tail_id는 하위 10bit가 사용되므로 사실상 마지막 index id값으로 초기화된 것이며, 첫 write가 이루어지면 +1이되어 0번째 index에 write가 수행되고 [63:62] = 00, [61:10] = 1111…11, [9:0] = 000…00이 된다. 그리고 계속해서 +1이 수행되어 1023를 넘어가면 상위 2비트 [63:62]로 값이 넘어가서 overflow가 되는 상태가 된다.
- 상위 2비트는 플래그 비트이다 (00 : reserved, 01 : committed, 10 : finalized, 11 : reusable)

 

- rb의 text_data_ring을 textbits, text_buf를 이용하여 초기화한다
- 매크로 BLK0_LPOS로 head_lpos, tail_lpos를 초기화한다
- textbits = 10인 경우, head_lpos/tail_lpos는 다음과 같이 초기화된다
- [63:10] = 1111…111, [9:0] = 000…00

 

- rb의 fail은 0으로 설정한다

 

- descs의 마지막 element의 state_var를 descbits를 이용하여 설정한다
- 매크로 DESC0SV로 설정하며 descbits = 10인 경우 DESC0_ID와 같으나 상위 2비트 플래그가 reusable로 11로 설정된다
- 즉 [10] 제외한 나머지 비트는 1이다
- descs의 마지막 element의 text_blk_lpos의 begin/next를 모두 FAILED_LPOS로 설정한다

 

- infos의 첫 번째 element의 seq를 다음과 같이 설정한다
- descbits = 10인 경우 -2^10 —> [63:10] = 1111…11이고 나머지 [9:0] = 000…00
- infos의 마지막 element의 seq는 그냥 0으로 설정한다

XE Login