kmap(): Mapping Arbitrary Pages Into Kernel VM

2.3커널 개발중에서 (제 생각에), "HIGHMEM"에 대한 지원이 추가되었읍니다. 보통, 커널은 단지 (4GB-PAGE_OFFSET)/PAGE_SIZE 만큼의 페이지수만을 address할수가 있읍니다, 왜냐면 모든 물리 페이지가 커널 주소인 PAGE_OFFSET과 4GB사이에 매핑이 되어야하기때문입니다. (그래서 만일 PAGE_OFFSET이 3GB면, 1GB의 물리 RAM만이 사용될수 있읍니다 - 실제로는 고정된 커널 매핑등 때문에 1G도 채 안됩니다) HIGHMEM 패치는 커널이 필요에 따라서 4GB바로 밑의 주소공간으로 추가적인 페이지들을 매핑시킬수 있게 합니다. 그들은 또한 high메모리 페이지들을 사용자 프로세스 주소 공간에 매핑할수도 있게 합니다.

kmapper로의 인터페이스는 kmap()과 kunmap()함수를 통해서 입니다. 이것들의 핵심은 mm/highmem.c에 있는 kmap_high()와 kunmap_high()에 의해서 구현됩니다.

kmapper는 부팅시간에 페이지들을 커널 공간으로 매핑하기 위해서 할당받은 물리적으로 연속된 페이지 테이블을 사용합니다. 연속된 페이지테이블을 가졌다는것은 페이지 디렉토리를 찾지 않고도 쉽게 주위를 찾아다닐수있게 해줍니다. kmap 페이지테이블들은 PKMAP_BASE에서부터 시작하는 커널 가상주소들을 가리킵니다. PKMAP_BASE는 2.4 소스에서 0xFC000000, 즉 4GB의 바로 밑 64MB입니다. kmap의 PTE를 위한 reference count를 추적하기 위해서 pkmap_count 라는 별개의 배열이 사용됩니다.

kmap()은 page struct* argument 를 인자로 해서 호출됩니다. 이것은 커널 공간으로 매핑할 페이지의 page frame입니다. 이것은 normal page나 HIGHMEM page일수 있읍니다; 전자의 경우 kmap()은 단지 직접-매핑된 주소를 반환합니다. HIGHMEM페이지의 경우, 우리는 부팅시간에 할당되었던 kmap 페이지테이블에 쓰이지 않은 entry가 있는지 검색합니다; 이 검색은 간단하게 차례로 pkmap_count의 엔트리를 조사하며 0을 찾음으로써 이루어집니다. 만약 없다면, 우리는 다른 프로세스가 페이지를 unmap하기를 기다리며 sleep합니다. 우리가 사용되지 않은 엔트리를 찾았을때, 우리는 매핑하고자 하는 그 페이지의 물리 주소를 삽입하고, pkmap_count 참조계수를 증가시킵니다. 그리고 호출자에게 가상주소를 반환합니다. 또한 그 page구조체가 매핑된 주소를 의미하도록 page->virtual 를 갱신합니다.

kunmap()은 page struct* 가 unmap할 페이지를 나타낸다고 기대합니다. 그것은 그 페이지의 가상주소를 위한 pkmap_count 엔트리를 찾아서 감소시킵니다. 그게 다입니다; 그 페이지는 kmap pte청소부인 flush_all_zero_pkmaps()가 매핑은 되었지만 참조되지 않은것들 발견하고 page table에서 쫓아낼때까지 매핑된채로 남게됩니다. kmap()은 비어있는 pte를 찾기위한 검색을 하는동안 마지막 pkmap pte가 검사될때마다 flush_all_zero_pkmaps()를 호출합니다. 그것은 매핑은 되었지만 실제로 사용되지 않는 페이지를 찾기위해 전체 pkmap_count배열을 검색하고, pkmap_count 엔트리를 지웁니다(clear) 그리고 모든 그런 페이지들에 대한 pte를 지웁니다(pte).

Usage Notes

사용가능한 kmap slot이 제한적인 숫자만큼만 있기때문에, 페이지를 kmap시키고 그들을 무기한으로 매핑된채로 남겨두는것은 추천할만하지 못합니다. kmap의 대부분의 사용자들은 매핑된 페이지를 즉시 사용하고 다시 매핑을 해제합니다. 예를 들어, HIGHMEM 사용자 페이지에서부터 데이터를 복사하기 위해서 이런 기교들이 사용됩니다.

kunmap()이 실제로 페이지를 unmap하지는 않는다는 사실은 저에게는 어쨌건 위험해 보입니다. 구체적으로는, 그 페이지는 (free_page()를 사용하는) 호출자에 의해서 해제될수도 있읍니다. 하지만 kmap 페이지 테이블에서 매핑된채로 남아있게 됩니다. 만약 __get_free_pages()가 GFP_HIGHMEM의 GFP_MASK와 함께 호출된다면, 그것은 페이지의 kmap된 가상주소를 반환할수 있을것입니다. 그러면, 이어지는, 다른 무관한 코드쪽에서의 free_all_zero_kmaps()로의 호출은 페이지를 unmap할지도 모릅니다. 충돌하게 되죠, 쿵..하고.

HIGHMEM 디자인에서는 __get_free_pages()가 GFP_HIGHMEM과는 절대 함께 호출되어서는 안된다는것은 암묵적인것 같습니다. 하지만 이것은 내가 찾을수 있는 어느 문서에서도 명시적으로 나와있지 않습니다. HIGHMEM 페이지들을 커널내에서 쓰기위해서, 항상 GFP_HIGHMEM와 함께 __alloc_pages()를 사용해야만 합니다. (이것은 빈 페이지 구조체를 반환합니다, 가상 주소가 아닌.) 그리고는 그 페이지에게 가상주소를 주기 위해서 kmap()을 사용합니다.

Kernel VM Allocation

Questions and comments to Joe Knapka

The LXR links in this page were produced by lxrreplace.tcl, which is available for free.

Credits