본문 바로가기
Kernel/理解

ZRAM 분석

by 暻煥 2024. 2. 2.

.

zram은 memory swap 시, flash가 아닌 ram을 사용하는 방법이다.

이때, block device의 구조를 가지고 동작하며 메모리 압축기법을 사용한다.

 

자세한 개념은 아래의 링크를 참고하기 바라며, 해당 포스팅 에서는 source code 분석에 대해서 얘기하고자 한다.

 

 

zram - Wikipedia

From Wikipedia, the free encyclopedia Linux module for compressed RAM This article is about the Linux kernel feature. For the memory hardware technology, see Z-RAM. For the Linux kernel compressed swap cache, see zswap. zram, formerly called compcache, is

en.wikipedia.org

 


zram_init

 

zram_init()
	| zram의 갯수만큼 struct zram을 할당받는다
	create_device(&zram_devices[dev_id], dev_id)
		init_rwsem(&zram->init_lock)
			| 읽기 쓰기 동작을 위한 세마포어 초기화
		blk_alloc_queue(GFP_KERNEL)
			| block 입출력을 queue를 할당한다
		blk_queue_make_request(queue, zram_make_request)
			| 입출력 동작시 콜백 함수를 zram_make_request( )로 등록한다
		alloc_disk(1)
			| 파티션의 갯수를 인자로 전달하여 디스크를 등록한다
		| queue, device driver 번호, operation 함수 등을 zram 구조체에 등록한다
		add_disk(zram->disk)

disksize_store

 

disksize_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len)
	dev_to_zram(dev)
		| struct device로 부터 zram을 얻어온다
		memparse(buf, NULL)
			| 인자로 전달된 buf로 부터 disksize를 알아온다
		| disksize를 page align한다
		zram_meta_alloc(zram->disk->first_minor, disksize)
			| disk에 존재하는 page갯수만큼 zram_meta->table을 생성한다
		| zram의 이름을 pool_name에 저장(최대 8자)
		zs_create_pool(pool_name, GFP_NOIO | __GFP_HIGHMEM)
			kzalloc(sizeof(*pool), GFP_KERNEL)
				| zs_pool 구조체를 저장하기 위한 공간 할당
			kcalloc(zs_size_classes, sizeof(struct size_class *), GFP_KERNEL)
				| 하나의 page를 ZS_SIZE_CLASS_DELTA 크기만큼 나눈값이 zs_size_classes가 된다
				| 하나의 page에 대하여 size_class가 zs_size_classes만큼 존재한다
			kstrdup(name, GFP_KERNEL)
				| pool_name 만큼의 공간을 할당하여 pool->name 에 저장해 놓는다
			create_handle_cache(pool)
				kmem_cache_create("zs_handle", ZS_HANDLE_SIZE, 0, 0, NULL)
					| unsigned long의 크기를 가지는 kmem_cache_create 할당
			| zs_size_classes 횟수 만큼 loop
				| 여러개의 page를 묶어서 하나의 zs_page를 만든다
				| 즉, object의 크기가 ZS_SIZE_CLASS_DELTA의 정수배 만큼 늘어나는데,
				| 3/8*PAGE_SIZE의 크기를 가지는 object를 위해서는 3개의 page를 묶어서 zs_page로 만든다
				get_pages_per_zspage(size)
					| class size에 제일 잘 맞는 page갯수를 구해서 pages_per_zspage에 저장한다
				can_merge(prev_class, size, pages_per_zspage
					| 이전 loop의 size_class와 비교하여서 동일한 pages_per_zspage, 동일한 maxobj_per_zspage를 가진다면 merge가 가능하여 true를 리턴한다
				| pool->size_class에 이전 prev_class를 추가하고 loop를 시작점부터 다시 돈다
				| 만약 merge가 불가능 하다면 size_class를 할당받아 추가한다
		zcomp_create(zram->compressor, zram->max_comp_streams)
			| 사용가능한 compress stream를 검색하여 반환 한다
		down_write(&zram->init_lock)
			| device_init을 여러 코어에서 동시에 실행할 경우를 대비한 lock을 잡는다
		init_done(zram)
			| zram->disksize에 값이 존재하는지 여부를 판단하여 init 여부를 확인한다
			| 이미 초기화가 되어 있는 대상에 대해서 초기화를 수행하는 경우 error를 리턴한다
		init_waitqueue_head(&zram->io_done)
			| io queue를 초기화 한다
		set_capacity(zram->disk, zram->disksize >> SECTOR_SHIFT)
			| 디스크의 크기를 sector의 크기로 나누어서 capacity를 설정한다
		up_write(&zram->init_lock)
			| zram meta data에 대한 lock 해제

zram_bvec_write

 

zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, int offset)
	if (is_partial_io(bvec))
		| 만약 partial_io인 경우에는
		uncmem = kmalloc(PAGE_SIZE, GFP_NOIO)
			| 임시로 사용할 page를 하나 할당받아서 uncmem에 저장한다
		zram_decompress_page(zram, uncmem, index)
			bit_spin_lock(ZRAM_ACCESS, &meta->table[index].value)
				| index로 전달된 page에 대해서 lock을 잡는다
			cmem = zs_map_object(meta->mem_pool, handle, ZS_MM_RO)
				BUG_ON(in_interrupt())
					| zram 루틴은 interrupt에서 사용이 불가능 하다
				pin_tag(handle)
					| object가 이동이 불가능 하도록 pin 한다
				obj_to_location(obj, &page, &obj_idx)
					| object 위치를 얻어온다, page위치를 알아온다
				get_zspage_mapping(get_first_page(page), &class_idx, &fg)
					BUG_ON(!is_first_page(page))
						| handle로 부터 얻어오는 page는 항상 zs_page의 첫번째 page이다
						| page->mapping 정보에는 zram의 fullnes 정보가 들어있다 해당정보를 얻어온다
						| 또한 class index 정보도 얻어온다
				obj_idx_to_offset(page, obj_idx, class->size)
					| object의 offset 정보를 가져온다, 여기서 offset은 zs_page의 시작부터의 offset이다
				area = &get_cpu_var(zs_map_area)
					| zs mapping 정보를 가져온다
				if (off + class->size <= PAGE_SIZE)
					| 만약 object가 하나의 page내에 들어가있다면
					area->vm_addr = kmap_atomic(page)
						| kmap_atomic을 통하여 가상주소를 return한다
				| object가 두개의 page에 걸쳐 있다면
				__zs_map_object(area, pages, off, class->size)
					| 두개의 page에 대하여 mapping을 진행한다
			| 매핑된 오브젝트에 대한 주소를 cmem에 저장한다
			ret = zcomp_decompress(zram->comp, cmem, size, mem)
				| 압축을 해제하여 인자로 넘어온 mem에 위치시킨다
			zs_unmap_object(meta->mem_pool, handle)
				| object에 대한 mapping을 해제한다
			bit_spin_unlock(ZRAM_ACCESS, &meta->table[index].value)
				| page에 대한 lock 해제
	| partial io 가 아닌 경우에는 계속해서 함수를 수행한다
	zstrm = zcomp_strm_find(zram->comp)
		| compress stream을 얻어온다
	user_mem = kmap_atomic(page)
		| 현재 page에 대하여 매핑진행
	| user_mem에 존재하는 내용을 uncmem에 복사한다 (partial io인 경우 user_mem을 언매핑 시킴)
	if (page_zero_filled(uncmem))
		| 값이 0인 page의 경우 
		zram_free_page(zram, index)
			| object 및 page를 free 한다
	zcomp_compress(zram->comp, zstrm, uncmem, &clen)
		| uncmem을 압축한여 zstrm->buffer에 저장한다
	if (!is_partial_io(bvec))	
		kunmap_atomic(user_mem)
			| partial io가 아닌 경우에는 user_mem을 언매핑한다
	handle = zs_malloc(meta->mem_pool, clen)
		| 압축된 데이터의 크기만큼 zs_malloc 하여 handle을 얻어온다
	cmem = zs_map_object(meta->mem_pool, handle, ZS_MM_WO)
		| cmem에 handle을 이용하여 object를 매핑한다
	memcpy(cmem, src, clen)
	| 메모리 복사가 완료 됬으면, 관련된 lock과 매핑을 제거한다.

=================================================================================================================
			
handle 구조
[31..page..19][18..object..1][object_pin]

zram_bvec_read

 

zram_bvec_read
	| write에 비해서 동작이 간단하고, write 동작을 이해했다면 무난히 이해가 가능
	uncmem = kmalloc(PAGE_SIZE, GFP_NOIO)
		| 임시로 사용할 uncompact memory를 할당받는다
	user_mem = kmap_atomic(page)
		| bio_vec 내의 page에 대해서 매핑한다
	zram_decompress_page(zram, uncmem, index)
		| 압축을 해제하여 uncmem에 위치시킨다
	memcpy(user_mem + bvec->bv_offset, uncmem + offset, bvec->bv_len)
		| 압축 해제한 data를 리턴하기 위하여 memcpy 한다

참고 및 출처

 

 

Linux内核中zram模块的理解 - L

在第一次接触zram这个名词的时候有点懵,究竟什么是zram呢?其实zram就是linux内核中内存压缩模块,那么问题又来了,什么是内存压缩呢? 内存压缩 术语: Active memory Active memory:活跃的内存,

liujunming.top

 

.

'Kernel > 理解' 카테고리의 다른 글

Device Tree 분석 (Kernel Source)  (0) 2024.02.02
Device Tree 문법  (0) 2024.02.02
ELF 실행 & execve  (0) 2024.02.02
Bootloader 개요  (0) 2024.02.02
[sched] Pressure Stall Information (PSI)  (0) 2024.02.02