Introduction
저번 개발 주기때 간간이 보이길래 공부했었는데, 5.16에서 folio 패치셋이 드디어 머지되었다. 간단하게 요약해보자면 커널에서 메모리는 페이지 단위로 관리된다. 종종 여러 페이지를 그룹으로 묶어서 하나의 페이지처럼 처리해야할 때가 있는데 이럴때 보통 compound page나 thp (transparent huge pages)를 사용한다. compound page 같은 경우에는 첫 번째의 페이지가 head page이고, 그 외에는 모두 tail page이다
실제로 파일시스템에 compound page를 사용해서 큰 페이지 단위로 처리할 때 성능상의 이점이 있다고 한다. 하지만 compound page를 사용하는 경우에는 이게 head page인 tail page인지 명확하지 않기 때문에 매번 확인해주어야 하고, 어떤 함수는 단일 페이지를 인자로 받지만 어떤 함수는 head page만 인자로 받기 때문에 혼란스럽다. 따라서 compound page를 다루는 모든 함수는 이 페이지가 tail page ㅡ head가 아닌 모든 page들 ㅡ 인지를 확인해야한다. 하지만 folio는 compound_page / thp와는 달리항상 head page만을 가리킨다는 걸 보장해줘서 코드를 간결하게 한다. 5.16에서 folio가 머지된 내용을 보면 파일시스템에서 약 0% ~ 10% 사이의 성능 개선이 있다고 한다.
Clarifying memory management with page folios
아래는 관련된 lwn.net 글의의 간단한 번역이다.
리눅스 커널이 만들어졌을 때부터 메모리는 페이지 단위로 나누어 관리했다. 일반적으로 페이지의 크기는 4KB 또는 8KB이다. 하지만 점점 메모리 양이 늘어나면서 4KB마다 struct page를 관리하는 것이 올바른가에 대한 의문이 생긴다.
커널은 전부터 여러 개의 페이지를 묶어서 하나처럼 사용할 필요가 있었고, "huge page"로 몇몇 아키텍처에서 여러 개의 페이지를 하나로 묶을 수 있었다. 예를 들어 x86은 2MB의 huage page를 사용한다. 또 다른 경우로 DMA 버퍼의 경우에는 반드시 물리적으로 연속된 페이지를 할당해야 하는데 이럴때도 페이지를 하나의 그룹으로 묶는다. 이러한 것을 "compound page"라고 불러왔다.
커널에서 페이지는 struct page라는 구조체로 관리된다. compound page와 같은 경우에는 여러 페이지 중 첫 번째 페이지의 구조체에 (head page) '이 페이지는 compound page이다'라는 것을 알 수 있게 표시해놓는다. head page이외에 모든 페이지 ("tail pages")는 head page를 가리키게 된다. 관련된 글은 An introduction to compound pages를 참고하자.
근데 compound page는 특성상 모호함이 존재한다. 어떤 함수에 tail page를 파라미터로 넘기면, compound page 전체 중에서 특정 tail page를 가리키는 것인가, 아니면 compound page의 처음을 가리키는 것인가?
따라서 Matthew Wilcox는 "page folio"라는 개념을 제안한다. folio를 인자로 받았을 때는 항상 tail page가 아니라 compound page를 가리키게 된다.
원래 같은 경우에는 tail page를 compound page로 바꾸어주는 함수를 호출해야하지만, folio는 항상 tail page가 아님이 보장되기 때문에 아래 함수를 호출할 필요가 없다.
struct page *compound_head(struct page *page);
folio는 page에 대한 wrapper 타입이다.
struct folio {
struct page page;
};
하지만 일부 환경에서 struct page대신 struct folio로 처리를 하면서, 관련된 함수도 늘어날것처럼 보인다.
근데 여기서 의견 자체는 매우 갈린다. Andrew Morton이나 Hugh Dickins는 folio를 사용함으로써 얻는 이득이 별로 크지 않다고 생각하는 것 같다.
Geeze it's a lot of noise.
More things to remember and we'll forever have a mismash of `page' and `folio' and code everywhere converting from one to the other.
Ongoing addition of folio accessors/manipulators to overlay the existing page accessors/manipulators, etc.
It's unclear to me that it's all really worth it. - Andrew Morton
반면 filesystem 관련 개발자들은 이러한 형태의 추상화가 반드시 필요하다고 이야기한다.
And this is where the abstraction that the "folio" introduces is required - filesystem people don't want to have to deal with all the complexity and subtlety of compound pages when large page support is added to the page cache. As Willy says, this will be a never-ending source of data corruption bugs....
Hence from the filesystem side of things, I think this abstraction is absolutely necessary. Especially because handling buffered IO in 4kB pages really, really sucks from a performance persepctive and the sooner we have native high-order page support in the page cache the better. These days buffered IO often can't even max out a cheap NVMe SSD because of the CPU cost of per-page state management in the page cache. So the sooner we have large page support to mitigate the worst of the overhead for streaming buffered IO, the better.
- Dave Chinner
Dynamically allocating memory descriptor
이번에 struct slab을 struct page와 별도의 구조체로 분리하는 패치 시리즈를 보면서 느낀 거지만 struct page가 진짜 복잡하다. 슬랩, 파일시스템 등등 작업을 페이지 단위로 처리하는 서브시스템이 많다보니까 struct page가 상황에 따라서 다양한 용도로 사용될 수 있고, 아주 복잡한 union으로 되어있는데, 각각 필드가 어떤 오프셋에 존재하는지도 신경을 써야한다. 또 페이지를 반환할 때 코드에서 직접적으로 사용하지 않는 필드라고 하더라도 union으로 인해 같은 오프셋에 존재하면 (page->_mapcount, page->mapping 등등) 페이지를 반환하기 전에 초기화를 해줘야 페이지 할당자가 bad page라고 불평하지 않는다. 그리고 struct page는 4KiB마다 하나씩 할당하는데, 높은 order로 할당할 때는 불필요하게 디스크립터가 중복된다.
이 문제를 해결하려면 몇 번의 개발 주기를 더 거쳐야겠지만, Matthew는 struct page를 최대한 간소화하고 (8 바이트 정도로 줄이고), 필요에 따라 다른 디스크립터(struct slab, struct folio 등등)를 동적으로 할당하는 방향으로 해결하려는 것 같다. 디스크립터를 물리적으로 분리하면 하면 디스크립터의 불필요한 중복을 피하고, 페이지를 반환할 때 page->_mapcount, page->mapping 등등의 값을 신경쓰지 않아도 된다. 대신 디스크립터를 동적으로 할당해야 한다는 게 챌린징한 부분인데, 이 부분은 메일들을 읽으면서 궁금증을 해결해봐야겠다.
지금은 슬랩 서브시스템에서는 struct slab을 struct page로부터 분리하는 패치 시리즈가 다음 개발 주기를 위해서 작업 중에 있다. 물론 이 시리즈에서는 아직 물리적으로 할당된 공간을 분리하는 건 아니고, struct slab는 struct page가 할당된 메모리 공간을 똑같이 사용한다.
참고 링크
'Kernel > Memory Management' 카테고리의 다른 글
Virtual Memory: Transparent Huge Pages (0) | 2022.03.23 |
---|---|
Virtual Memory: Memory Compaction (0) | 2022.01.10 |
Virtual Memory: Zone의 종류 (0) | 2022.01.03 |
Virtual Memory: Grouping pages by mobility (0) | 2022.01.02 |
Virtual Memory: Node and Zone (5) | 2022.01.02 |
댓글