Arm Linux Kernel Hacks

rousalome.egloos.com

포토로그 Kernel Crash


통계 위젯 (화이트)

11105
637
415734


vmalloc - overview(1) [Linux][Kernel] MM


vmalloc에 대해서 리뷰를 해보려고 합니다.
vmalloc이라. 이 놈도 리눅스 커널 책에서 엄청나게 많이 소개가 되고 있죠.

너무나도 좋은 글들이 있지만 vmlloc을 쓸 때를 허접하게 요약하면 아래와 같아요.
1> 물리적으로 연속적이지 않은 메모리를 할당하고 싶을 때.
2> 큰 사이즈의 가상 메모리 공간을 확보하고 싶을 때.

흠냐. 이게 뭔 소리지? 사실 전 머리 속에 개념이 잘 안 들어오더라구요.
자, 코드 좀 살펴볼께요.

우선 vmalloc을 관리하는 핵심 변수를 소개하자면 vmap_area_list 이 분들 소개 안 할 수 없네요.
LIST_HEAD(vmap_area_list);

vmap을 관리하는 링크드 리스트가 vmap_area_list인데요, __insert_vmap_area 함수에서 struct vmap_area.list를 
vmap_area_list 링크드 리스트에 추가하네요. 

쉽게 설명하면, vmalloc을 할당할 때 vmap_area_list 리스트에 "나 vmlloac 할당해요!"하며 등록하고, 
vmalloc을 해제할 때, vmap_area_list 리스트에 "나 vmlloac 해제 할 터이니 리스트에서 빼주세요!"라고 보고하죠. 
static void __insert_vmap_area(struct vmap_area *va)
{
struct rb_node **p = &vmap_area_root.rb_node;
struct rb_node *parent = NULL;
struct rb_node *tmp;

//snip
if (tmp) {
struct vmap_area *prev;
prev = rb_entry(tmp, struct vmap_area, rb_node);
list_add_rcu(&va->list, &prev->list);
} else
list_add_rcu(&va->list, &vmap_area_list);  //<<--
}

자 그럼, __insert_vmap_area 함수가 언제 실행될까요?

아래 제 블로그에서 설정한대로 ftrace config를 설정한 다음,
(http://rousalome.egloos.com/9964361)

아래와 같이 __insert_vmap_area 함수에 필터(d/tracing/set_ftrace_filter)를 걸어 봤어요.
adb shell "echo  > d/tracing/set_event"
adb shell "sleep 1"

adb shell "echo 0 > d/tracing/tracing_on"
adb shell "sleep 1"

adb shell " echo 50000 > /d/tracing/buffer_size_kb"

adb shell "echo function > d/tracing/current_tracer"
adb shell "sleep 1"

adb shell "echo 1 > /d/tracing/events/sched/sched_switch/enable"
adb shell "sleep 1"

adb shell "echo __insert_vmap_area  > d/tracing/set_ftrace_filter"
adb shell "sleep 1"

adb shell "echo 1 > d/tracing/options/func_stack_trace"
adb shell "sleep 1"

adb shell "echo 1 > d/tracing/tracing_on"
adb shell "sleep 1"

__insert_vmap_area를 호출하는 콜스택은? 겁나 많네요.

1. 우선 유저 스페이스에서 시스템 콜로 tty driver를 오픈할 때 호출하구요.
 => __insert_vmap_area
 => alloc_vmap_area
 => __get_vm_area_node
 => __vmalloc_node_range
 => __vmalloc_node
 => vmalloc
 => n_tty_open
 => tty_ldisc_open
 => tty_ldisc_hangup
 => __tty_hangup
 => tty_vhangup
 => pty_close
 => tty_release
 => __fput
 => ____fput
 => task_work_run
 => do_work_pending

바인더 mmap 시스템 콜로 호출를 하네요. 
  => __insert_vmap_area
 => alloc_vmap_area
 => __get_vm_area_node
 => get_vm_area
 => binder_alloc_mmap_handler
 => binder_mmap
 => mmap_region
 => do_mmap_pgoff
 => vm_mmap_pgoff
 => SyS_mmap_pgoff
 
2. 커널 공간
아래와 같이 커널 쓰레드에서 호출이 되네요. 
  => __insert_vmap_area
 => alloc_vmap_area
 => __get_vm_area_node
 => __vmalloc_node_range
 => __vmalloc_node
 => vmalloc
 => vos_mem_vmalloc
 => sirConvertBeaconFrame2Struct
 => limProcessBeaconFrameNoSession
 => limProcessMessages
 => limMessageProcessor
 => peProcessMessages
 => VosMCThread
 => kthread
 => ret_from_fork
 
자자, 정리하면 vmalloc이 할당되는 프로세스는 유저공간에서 생성된 프로세스, 커널 쓰레드에서 모두 쓸 수 있다는 거에요.

vmlloc을 할당할 때 속성을 좀 알아볼까요?
아래 해더 파일에 명시되어 있는데요.
[include/linux/vmalloc.h]
#define VM_IOREMAP 0x00000001 /* ioremap() and friends */
#define VM_ALLOC 0x00000002 /* vmalloc() */
#define VM_MAP 0x00000004 /* vmap()ed pages */
#define VM_USERMAP 0x00000008 /* suitable for remap_vmalloc_range */
#define VM_VPAGES 0x00000010 /* buffer for pages was vmalloc'ed */
#define VM_UNINITIALIZED 0x00000020 /* vm_struct is not fully initialized */
#define VM_NO_GUARD 0x00000040      /* don't add guard page */
#define VM_KASAN 0x00000080      /* has allocated kasan shadow memory */
#define VM_LOWMEM 0x00000100 /* Tracking of direct mapped lowmem */

자, 우선 VM_IOREMAP부터 볼께요.
VM_IOREMAP 이 옵션으로 vmalloc 할당하면 비어 있는 vmalloc 메모리를 할당해요.
바인더 mmap 시스템 콜에서 이 옵션을 주로 쓰죠. 
int binder_alloc_mmap_handler(struct binder_alloc *alloc,
      struct vm_area_struct *vma)
{
int ret;
// 생략

area = get_vm_area(vma->vm_end - vma->vm_start, VM_IOREMAP);

이 때 콜 트레이스는 아래와 같네요.
 => __insert_vmap_area
 => alloc_vmap_area
 => __get_vm_area_node
 => get_vm_area
 => binder_alloc_mmap_handler
 => binder_mmap
 => mmap_region
 => do_mmap_pgoff
 => vm_mmap_pgoff
 => SyS_mmap_pgoff
 => ret_fast_syscall

VM_ALLOC
그냥  vmalloc() 함수 호출 시 확인할 수 있어요.

콜 트레이스는 아주 많은데요, 하나만 올릴께요.
 => __insert_vmap_area
 => alloc_vmap_area
 => __get_vm_area_node
 => __vmalloc_node_range
 => __vmalloc_node
 => vmalloc
 => vos_mem_vmalloc
 => limProcessProbeRspFrameNoSession
 => limProcessMessages
 => limMessageProcessor
 => peProcessMessages
 => VosMCThread
 => kthread
 => ret_from_fork

VM_LOWMEM
물리 메모리와 직접 매핑된 vmalloc을 의미해요. 커널이 부팅할 때 map_lowmem() 함수가 호출되는데, 이 때 메모리 뱅크를 돌면서
VM_LOWMEM 필드를 설정하네요.
static void __init map_lowmem(void)
{
struct memblock_region *reg;
unsigned long kernel_x_start = round_down(__pa(_stext), SECTION_SIZE);
// ...생략...
for_each_memblock(memory, reg) {
struct vm_struct *vm;
// ...생략...
vm->addr = (void *)(vaddr & PAGE_MASK);
vm->size = PAGE_ALIGN(length + (vaddr & ~PAGE_MASK));
vm->phys_addr = __pfn_to_phys(pfn);
vm->flags = VM_LOWMEM;  //<<--
vm->flags |= VM_ARM_MTYPE(type);
vm->caller = map_lowmem;
add_static_vm_early(svm++);
mark_vmalloc_reserved_area(vm->addr, vm->size);
}
}


# Reference: For more information on 'Linux Kernel';

디버깅을 통해 배우는 리눅스 커널의 구조와 원리. 1

디버깅을 통해 배우는 리눅스 커널의 구조와 원리. 2




핑백

덧글

댓글 입력 영역